PluginProbe
Patchstack – WordPress & Plugins Security / 2.1.0
Patchstack – WordPress & Plugins Security v2.1.0
2.3.7 trunk 2.1.0 2.1.1 2.1.10 2.1.11 2.1.12 2.1.13 2.1.14 2.1.15 2.1.16 2.1.17 2.1.18 2.1.19 2.1.2 2.1.20 2.1.21 2.1.22 2.1.23 2.1.24 2.1.25 2.1.3 2.1.4 2.1.5 2.1.6 All 49 releases
patchstack / includes / activation.php

activation.php in Patchstack – WordPress & Plugins Security 2.1.0, at includes/activation.php

363 lines 12.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 // Do not allow the file to be called directly.
4 if ( ! defined( 'ABSPATH' ) ) {
5 exit;
6 }
7
8 /**
9 * This class is used to activate and deactivate the plugin.
10 * Additionally, we use it to run migrations.
11 */
12 class P_Activation extends P_Core {
13
14 /**
15 * Add the actions required for the activation.
16 *
17 * @param Patchstack $core
18 * @return void
19 */
20 public function __construct( $core ) {
21 parent::__construct( $core );
22 add_action( 'activated_plugin', array( $this, 'redirect_activation' ), 10, 2 );
23 }
24
25 /**
26 * Redirect the user to our settings page after plugin activation.
27 *
28 * @param string $plugin The plugin that is activated.
29 * @param boolean $network_activation If a network wide activation. (multisite)
30 * @return void
31 */
32 public function redirect_activation( $plugin, $network_activation ) {
33 if ( $plugin == $this->plugin->basename ) {
34
35 // In case of multisite, we want to redirect the user to a different page.
36 if ( $network_activation ) {
37 wp_safe_redirect( network_admin_url( 'admin.php?page=patchstack-multisite-settings&tab=multisite&activated=1' ) );
38 } else {
39 wp_safe_redirect( admin_url( 'admin.php?page=' . $this->plugin->name . '&activated=1' ) );
40 }
41 exit;
42 }
43 }
44
45 /**
46 * Check if the plugin meets requirements and disable it if they are not present.
47 *
48 * @return boolean
49 */
50 public function check_requirements() {
51 if ( $this->meets_requirements() ) {
52 return true;
53 }
54
55 // Add a dashboard notice.
56 add_action( 'all_admin_notices', array( $this, 'requirements_not_met_notice' ) );
57 return false;
58 }
59
60 /**
61 * Check that all plugin requirements are met.
62 *
63 * @return boolean
64 */
65 public function meets_requirements() {
66 // Check to see if we can access the API.
67 $response = wp_remote_request(
68 $this->plugin->api_url,
69 array(
70 'method' => 'GET',
71 'timeout' => 10,
72 'redirection' => 5,
73 )
74 );
75
76 // Check if we can access the API.
77 if ( is_wp_error( $response ) ) {
78 $this->activation_errors[] = 'We were unable to contact our API server. Please contact your host and ask them to make sure that outgoing connections to api.webarxsecurity.com and api.patchstack.com are not blocked.<br />Additional error message to give to your host: ' . $response->get_error_message();
79 return false;
80 }
81
82 // Do checks for required classes / functions or similar.
83 // Add detailed messages to $this->activation_errors array.
84 if ( version_compare( phpversion(), '5.3.0', '<' ) ) {
85 $this->activation_errors[] = 'Please update the PHP version on your host to at least 5.3.0. Ask your host if you do not know what this means.';
86 return false;
87 }
88
89 global $wp_version;
90 if ( version_compare( $wp_version, '4.3.0', '<' ) ) {
91 $this->activation_errors[] = 'Please upgrade your WordPress site to at least 4.3.0.';
92 return false;
93 }
94
95 // Check if the webarx/webarx.php plugin is present, if so, remove it.
96 if ( is_dir( WP_PLUGIN_DIR . '/webarx' ) ) {
97
98 // Migrate all current options to the new prefix.
99 global $wpdb;
100 $wpdb->query( 'INSERT IGNORE INTO ' . $wpdb->prefix . "options (option_name, option_value, autoload) SELECT REPLACE(option_name, 'webarx_', 'patchstack_') as option_name, option_value, autoload FROM " . $wpdb->prefix . "options WHERE option_name like 'webarx_%'" );
101 $wpdb->query( 'UPDATE ' . $wpdb->prefix . 'options AS a SET option_value = (SELECT option_value FROM ' . $wpdb->prefix . "options WHERE option_name = REPLACE(a.option_name, 'patchstack_', 'webarx_')) WHERE option_name LIKE 'patchstack_%'" );
102
103 // Remove the plugin.
104 include_once ABSPATH . 'wp-admin/includes/plugin.php';
105 $delete = delete_plugins( array( 'webarx/webarx.php' ) );
106
107 // Failure?
108 if ( $delete !== true ) {
109 $this->activation_errors[] = 'An older Patchstack plugin is installed and could not be automatically removed. Please manually remove the Patchstack or WebARX plugin older than 2.1.0 first and then install this plugin again.';
110
111 // Null means filesystem related error.
112 if ( is_null( $delete ) ) {
113 $this->activation_errors[] = 'Detailed error: filesystem credentials are required to proceed the automatic deletion of the older Patchstack plugin.';
114 }
115
116 // WP_Error instance means other type of failure.
117 if ( is_wp_error( $delete ) ) {
118 $this->activation_errors[] = 'Detailed error: ' . $delete->get_error_message();
119 }
120
121 return false;
122 }
123
124 update_option( 'patchstack_license_free', '0' );
125 }
126
127 return true;
128 }
129
130 /**
131 * Adds a notice to the dashboard if the plugin requirements are not met.
132 *
133 * @return void
134 */
135 public function requirements_not_met_notice() {
136 // Deactivate the plugin.
137 deactivate_plugins( $this->plugin->basename );
138
139 // Compile default message.
140 $default_message = __( 'Patchstack could not be activated due to a conflict. See below for information regarding the conflict.<br />', 'patchstack' );
141
142 // Print the errors on the screen.
143 echo wp_kses_post( $default_message );
144 echo wp_kses_post( implode( '<br />', $this->activation_errors ) );
145 }
146
147 /**
148 * Activate the plugin.
149 *
150 * @param Patchstack $core
151 * @return void
152 */
153 public function activate( $core ) {
154 // Bail early if requirements are not met.
155 if ( ! $this->check_requirements() ) {
156 $this->requirements_not_met_notice();
157 exit;
158 }
159
160 // Make sure any rewrite functionality has been loaded.
161 $this->migrate();
162 add_option( 'patchstack_first_activated', '1' );
163
164 // Activate the license.
165 if ( $this->plugin->client_id != 'PATCHSTACK_CLIENT_ID' && $this->plugin->private_key != 'PATCHSTACK_PRIVATE_KEY' ) {
166 $this->alter_license( $this->plugin->client_id, $this->plugin->private_key, 'activate' );
167 } elseif ( get_option( 'patchstack_clientid', false ) != false && get_option( 'patchstack_secretkey', false ) != false ) {
168 $this->alter_license( get_option( 'patchstack_clientid' ), get_option( 'patchstack_secretkey' ), 'activate' );
169 } else {
170 update_option( 'patchstack_license_free', '1' );
171 }
172
173 // Update firewall status after activating plugin
174 $api = new P_Api( $core );
175 $token = $api->get_access_token();
176 if ( ! empty( $token ) ) {
177 $api->update_firewall_status( array( 'status' => 1 ) );
178 $api->update_url( array( 'plugin_url' => get_option( 'siteurl' ) ) );
179 }
180
181 // Immediately send software data to our server to set firewall as enabled.
182 // Also immediately download the whitelist file and the firewall rules.
183 do_action( 'patchstack_send_software_data' );
184 if ( get_option( 'patchstack_license_free', 0 ) != 1 ) {
185 do_action( 'patchstack_post_firewall_rules' );
186 do_action( 'patchstack_post_dynamic_firewall_rules' );
187 }
188
189 // One time actions should be placed here.
190 $this->plugin->hardening->delete_readme();
191 }
192
193 /**
194 * Used to activate an individual license on multisite/network.
195 *
196 * @param object $site
197 * @param array $license
198 * @return void
199 */
200 public function activate_multisite_license( $site, $license ) {
201 // Build the Patchstack tables on the site.
202 $this->migrate( null, $site->id );
203
204 // Add the options to given site.
205 foreach ( $this->plugin->admin_options->options as $name => $value ) {
206 add_blog_option( $site->id, $name, $value );
207 }
208
209 // Set the client id and secret key.
210 update_blog_option( $site->id, 'patchstack_clientid', $license['id'] );
211 update_blog_option( $site->id, 'patchstack_secretkey', $license['secret'] );
212 $this->plugin->api->blog_id = $site->id;
213
214 // Activate the license and update firewall status after activating the plugin.
215 $token = $this->plugin->api->get_access_token( $license['id'], $license['secret'], true );
216 if ( ! empty( $token ) ) {
217 $this->plugin->api->update_firewall_status( array( 'status' => $this->get_option( 'patchstack_basic_firewall' ) == 1 ) );
218 $this->plugin->api->update_url( array( 'plugin_url' => get_blog_option( $site->id, 'siteurl' ) ) );
219
220 // If we have an access token, tell our API that the firewall is activated
221 // and the current URL of the site.
222 update_blog_option( $site->id, 'patchstack_license_activated', '1' );
223 $this->plugin->api->update_license_status();
224
225 // This will trigger the software synchronization action.
226 wp_remote_get( get_site_url( $site->id ), array( 'sslverify' => false ) );
227 }
228
229 // Make sure to switch back to the current blog id.
230 $this->plugin->api->blog_id = get_current_blog_id();
231 }
232
233 /**
234 * Build the required Patchstack tables.
235 *
236 * @param null|string $ver The version to upgrade to.
237 * @param null|integer $site_id The blog id to perform the upgrades on.
238 * @return void
239 */
240 public function migrate( $ver = null, $site_id = null ) {
241 global $wpdb;
242 require_once ABSPATH . 'wp-admin/includes/upgrade.php';
243 $charset_collate = $wpdb->get_charset_collate();
244 $prefix = $site_id != null ? $wpdb->get_blog_prefix( $site_id ) : $wpdb->prefix;
245
246 // The following conditions will only execute if Patchstack is installed because of an update
247 // and if we need to perform migrations.
248 if ( $ver !== null && file_exists( dirname( __FILE__ ) . '/migrations/v' . str_replace( '.', '', $ver ) . '.php' ) ) {
249 require_once dirname( __FILE__ ) . '/migrations/v' . str_replace( '.', '', $ver ) . '.php';
250 return;
251 }
252
253 // Require the base migration.
254 require_once dirname( __FILE__ ) . '/migrations/base.php';
255 }
256
257 /**
258 * Check if the database version of the plugin is running behind.
259 * If so, run the migrations up until the latest version.
260 *
261 * @return void
262 */
263 public function migrate_check() {
264 // Only perform migrations if we have any to execute.
265 $versions = array();
266 if ( count( $versions ) == 0 ) {
267 return;
268 }
269
270 // Get current database version and run the migrations.
271 $db_version = get_option( 'patchstack_db_version', false );
272 foreach ( $versions as $version ) {
273 if ( version_compare( $db_version, $version, '<' ) ) {
274 $this->migrate( $version );
275 }
276 }
277 }
278
279 /**
280 * Perform cleanup when the plugin is deactivated.
281 *
282 * @return void
283 */
284 public function deactivate() {
285 // Update firewall status after de-activating plugin
286 $api = new P_Api( $this );
287 $token = $api->get_access_token();
288 if ( ! empty( $token ) ) {
289 $api->update_firewall_status( array( 'status' => 0 ) );
290 }
291
292 // Clear all Patchstack scheduled tasks.
293 $tasks = array( 'patchstack_zip_backup', 'patchstack_send_software_data', 'patchstack_send_hacker_logs', 'patchstack_send_visitor_logs', 'patchstack_send_event_logs', 'patchstack_reset_blocked_attacks', 'patchstack_post_firewall_rules', 'patchstack_post_firewall_htaccess_rules', 'patchstack_post_dynamic_firewall_rules', 'patchstack_update_license_status', 'patchstack_update_plugins', 'patchstack_send_ping', 'puc_cron_check_updates-webarx' );
294 foreach ( $tasks as $task ) {
295 wp_clear_scheduled_hook( $task );
296 }
297
298 // Cleanup the .htaccess file.
299 $this->plugin->htaccess->cleanup_htaccess_file();
300 }
301
302 /**
303 * Activate or deactivate a license on the current site.
304 *
305 * @param integer $id
306 * @param string $secret
307 * @param string $action
308 * @return array
309 */
310 public function alter_license( $id, $secret, $action ) {
311 // Store current keys in tmp variable so in case it fails, we can set it back.
312 $tmp_id = get_option( 'patchstack_clientid' );
313 $tmp_key = get_option( 'patchstack_secretkey' );
314 update_option( 'patchstack_clientid', $id );
315 update_option( 'patchstack_secretkey', $secret );
316
317 // Activate the license.
318 if ( $action == 'activate' ) {
319 $api_result = $this->plugin->api->get_access_token( $id, $secret, true );
320
321 // Valid result?
322 if ( ! $api_result ) {
323 update_option( 'patchstack_clientid', $tmp_id );
324 update_option( 'patchstack_secretkey', $tmp_key );
325 return array(
326 'result' => 'error',
327 'message' => 'Cannot activate license!',
328 );
329 }
330
331 // If we have an access token, tell our API that the firewall is activated
332 // and the current URL of the site.
333 update_option( 'patchstack_license_activated', '1' );
334 $this->plugin->api->update_license_status();
335 $token = $this->plugin->api->get_access_token();
336 if ( ! empty( $token ) ) {
337 do_action( 'patchstack_send_software_data' );
338 if ( get_option( 'patchstack_license_free', 0 ) != 1 ) {
339 do_action( 'patchstack_post_firewall_rules' );
340 do_action( 'patchstack_post_dynamic_firewall_rules' );
341 }
342
343 $this->plugin->api->update_firewall_status( array( 'status' => $this->get_option( 'patchstack_basic_firewall' ) == 1 ) );
344 $this->plugin->api->update_url( array( 'plugin_url' => get_option( 'siteurl' ) ) );
345 }
346 return array(
347 'result' => 'success',
348 'message' => 'License activated!',
349 );
350 }
351
352 // Deactivate the license.
353 if ( $action == 'deactivate' ) {
354 update_option( 'patchstack_api_token', '' );
355 update_option( 'patchstack_license_activated', '0' );
356 return array(
357 'result' => 'success',
358 'message' => 'License deactivated!',
359 );
360 }
361 }
362 }
363