PluginProbe
Patchstack – WordPress & Plugins Security / 2.2.4
Patchstack – WordPress & Plugins Security v2.2.4
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
← All changes | includes/activation.php +435 -362 2.1.12.2.4 View file →
@@ -1,362 +1,435 @@
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 -}
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', [ $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 ( defined( 'WP_CLI' ) && WP_CLI ) {
34 + return;
35 + }
36 +
37 + if ( $plugin == $this->plugin->basename ) {
38 +
39 + // In case of multisite, we want to redirect the user to a different page.
40 + if ( $network_activation ) {
41 + wp_safe_redirect( network_admin_url( 'admin.php?page=patchstack-multisite-settings&tab=multisite&activated=1' ) );
42 + } else {
43 + wp_safe_redirect( admin_url( 'admin.php?page=' . $this->plugin->name . '&activated=1' ) );
44 + }
45 + exit;
46 + }
47 + }
48 +
49 + /**
50 + * Check if the plugin meets requirements and disable it if they are not present.
51 + *
52 + * @return boolean
53 + */
54 + public function check_requirements() {
55 + if ( $this->meets_requirements() ) {
56 + return true;
57 + }
58 +
59 + // Add a dashboard notice.
60 + add_action( 'all_admin_notices', [ $this, 'requirements_not_met_notice' ] );
61 + return false;
62 + }
63 +
64 + /**
65 + * Check that all plugin requirements are met.
66 + *
67 + * @return boolean
68 + */
69 + public function meets_requirements() {
70 + // Check to see if we can access the API.
71 + $response = wp_remote_request(
72 + $this->plugin->api_url,
73 + [
74 + 'method' => 'GET',
75 + 'timeout' => 10,
76 + 'redirection' => 5,
77 + ]
78 + );
79 +
80 + // Check if we can access the API.
81 + if ( is_wp_error( $response ) ) {
82 + $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();
83 + return false;
84 + }
85 +
86 + // Do checks for required classes / functions or similar.
87 + // Add detailed messages to $this->activation_errors array.
88 + if ( version_compare( phpversion(), '5.3.0', '<' ) ) {
89 + $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.';
90 + return false;
91 + }
92 +
93 + global $wp_version;
94 + if ( version_compare( $wp_version, '4.3.0', '<' ) ) {
95 + $this->activation_errors[] = 'Please upgrade your WordPress site to at least 4.3.0.';
96 + return false;
97 + }
98 +
99 + return true;
100 + }
101 +
102 + /**
103 + * Adds a notice to the dashboard if the plugin requirements are not met.
104 + *
105 + * @return void
106 + */
107 + public function requirements_not_met_notice() {
108 + // Deactivate the plugin.
109 + deactivate_plugins( $this->plugin->basename );
110 +
111 + // Compile default message.
112 + $default_message = __( 'Patchstack could not be activated due to a conflict. See below for information regarding the conflict.<br />', 'patchstack' );
113 +
114 + // Print the errors on the screen.
115 + echo wp_kses_post( $default_message );
116 + echo wp_kses_post( implode( '<br />', $this->activation_errors ) );
117 + }
118 +
119 + /**
120 + * Activate the plugin.
121 + *
122 + * @param Patchstack $core
123 + * @return void
124 + */
125 + public function activate( $core ) {
126 + // Bail early if requirements are not met.
127 + if ( ! $this->check_requirements() ) {
128 + $this->requirements_not_met_notice();
129 + exit;
130 + }
131 +
132 + // Check if the webarx/webarx.php plugin is present, if so, remove it.
133 + if ( is_dir( WP_PLUGIN_DIR . '/webarx' ) ) {
134 +
135 + // Migrate all current options to the new prefix.
136 + global $wpdb;
137 + $exists = $wpdb->get_var( "SELECT COUNT(*) FROM " . $wpdb->prefix . "options WHERE option_name = 'webarx_api_token'" );
138 +
139 + // Move over the options.
140 + if ( !is_null( $exists ) && $exists >= 1 ) {
141 + $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_%'" );
142 + $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_%'" );
143 + }
144 +
145 + // Deactivate the plugin.
146 + include_once ABSPATH . 'wp-admin/includes/plugin.php';
147 + deactivate_plugins( [ 'webarx/webarx.php' ] );
148 + update_option( 'patchstack_license_free', '0' );
149 + }
150 +
151 + // Make sure any rewrite functionality has been loaded.
152 + $this->migrate();
153 + add_option( 'patchstack_first_activated', '1' );
154 +
155 + // Activate the license.
156 + if ( $this->plugin->client_id != 'PATCHSTACK_CLIENT_ID' && $this->plugin->private_key != 'PATCHSTACK_PRIVATE_KEY' ) {
157 + $this->alter_license( $this->plugin->client_id, $this->plugin->private_key, 'activate' );
158 + } elseif ( get_option( 'patchstack_clientid', false ) != false && get_option( 'patchstack_secretkey', false ) != false ) {
159 + $this->alter_license( get_option( 'patchstack_clientid' ), $this->get_secret_key(), 'activate' );
160 + } else {
161 + update_option( 'patchstack_license_free', '1' );
162 + }
163 +
164 + // Update firewall status after activating plugin
165 + $api = new P_Api( $core );
166 + $token = $api->get_access_token();
167 + if ( ! empty( $token ) ) {
168 + $api->update_firewall_status( [ 'status' => 1 ] );
169 + $api->update_url( [ 'plugin_url' => get_option( 'siteurl' ) ] );
170 + }
171 +
172 + // Immediately send software data to our server to set firewall as enabled.
173 + // Also immediately download the whitelist file and the firewall rules.
174 + do_action( 'patchstack_send_software_data' );
175 + if ( get_option( 'patchstack_license_free', 0 ) != 1 ) {
176 + do_action( 'patchstack_post_firewall_rules' );
177 + do_action( 'patchstack_post_dynamic_firewall_rules' );
178 + }
179 +
180 + // One time actions should be placed here.
181 + $this->plugin->hardening->delete_readme();
182 +
183 + // Try to create the mu-plugins folder/file.
184 + // No need to do this if it already exists.
185 + if ( file_exists( WPMU_PLUGIN_DIR . '/patchstack.php' )) {
186 + return;
187 + }
188 +
189 + // The mu-plugin does not exist, try to create it.
190 + @include_once ABSPATH . 'wp-admin/includes/file.php';
191 + $wpfs = WP_Filesystem();
192 +
193 + // Failed to initialize WP_Filesystem.
194 + if ( ! $wpfs ) {
195 + return;
196 + }
197 +
198 + if ( ! is_dir( WPMU_PLUGIN_DIR ) ) {
199 + wp_mkdir_p( WPMU_PLUGIN_DIR );
200 + }
201 +
202 + // Failed to create the mu-plugin folder.
203 + if ( ! is_dir( WPMU_PLUGIN_DIR ) ) {
204 + return;
205 + }
206 +
207 + // Create the mu-plugin file in the folder.
208 + if ( is_writable( WPMU_PLUGIN_DIR ) ) {
209 + $php = @file_get_contents( trailingslashit( plugin_dir_path( __FILE__ ) ) . 'mu-plugin.php' );
210 + @file_put_contents( trailingslashit( WPMU_PLUGIN_DIR ) . 'patchstack.php', $php );
211 + }
212 + }
213 +
214 + /**
215 + * Used to activate an individual license on multisite/network.
216 + *
217 + * @param object $site
218 + * @param array $license
219 + * @return void
220 + */
221 + public function activate_multisite_license( $site, $license ) {
222 + // Build the Patchstack tables on the site.
223 + $this->migrate( null, $site->id );
224 +
225 + // Add the options to given site.
226 + foreach ( $this->plugin->admin_options->options as $name => $value ) {
227 + add_blog_option( $site->id, $name, $value );
228 + }
229 +
230 + // Set the client id and secret key.
231 + update_blog_option( $site->id, 'patchstack_clientid', $license['id'] );
232 + $enc = $this->get_secret_key( $license['secret'] );
233 + update_blog_option( $site->id, 'patchstack_secretkey', $enc['cipher'] );
234 + update_blog_option( $site->id, 'patchstack_secretkey_nonce', $enc['nonce'] );
235 +
236 + $this->plugin->api->blog_id = $site->id;
237 +
238 + // Activate the license and update firewall status after activating the plugin.
239 + $token = $this->plugin->api->get_access_token( $license['id'], $license['secret'], true );
240 + if ( ! empty( $token ) ) {
241 + $this->plugin->api->update_firewall_status( [ 'status' => $this->get_option( 'patchstack_basic_firewall' ) == 1 ] );
242 + $this->plugin->api->update_url( [ 'plugin_url' => get_blog_option( $site->id, 'siteurl' ) ] );
243 +
244 + // If we have an access token, tell our API that the firewall is activated
245 + // and the current URL of the site.
246 + update_blog_option( $site->id, 'patchstack_license_activated', '1' );
247 + $this->plugin->api->update_license_status();
248 +
249 + // This will trigger the software synchronization action.
250 + wp_remote_get( get_site_url( $site->id ), [ 'sslverify' => false ] );
251 + }
252 +
253 + // Make sure to switch back to the current blog id.
254 + $this->plugin->api->blog_id = get_current_blog_id();
255 + }
256 +
257 + /**
258 + * Build the required Patchstack tables.
259 + *
260 + * @param null|string $ver The version to upgrade to.
261 + * @param null|integer $site_id The blog id to perform the upgrades on.
262 + * @return void
263 + */
264 + public function migrate( $ver = null, $site_id = null ) {
265 + global $wpdb;
266 + require_once ABSPATH . 'wp-admin/includes/upgrade.php';
267 + $charset_collate = $wpdb->get_charset_collate();
268 + $prefix = $site_id != null ? $wpdb->get_blog_prefix( $site_id ) : $wpdb->prefix;
269 +
270 + // The following conditions will only execute if Patchstack is installed because of an update
271 + // and if we need to perform migrations.
272 + if ( $ver !== null && file_exists( dirname( __FILE__ ) . '/migrations/v' . str_replace( '.', '', $ver ) . '.php' ) ) {
273 + require_once dirname( __FILE__ ) . '/migrations/v' . str_replace( '.', '', $ver ) . '.php';
274 + return;
275 + }
276 +
277 + // Require the base migration.
278 + require_once dirname( __FILE__ ) . '/migrations/base.php';
279 + }
280 +
281 + /**
282 + * Check if the database version of the plugin is running behind.
283 + * If so, run the migrations up until the latest version.
284 + *
285 + * @return void
286 + */
287 + public function migrate_check() {
288 + // Only perform migrations if we have any to execute.
289 + $versions = ['3.0.0', '3.0.1', '3.0.2', '3.0.3'];
290 + if ( count( $versions ) == 0 ) {
291 + return;
292 + }
293 +
294 + // Get current database version and run the migrations.
295 + $db_version = get_option( 'patchstack_db_version', false );
296 + foreach ( $versions as $version ) {
297 + if ( version_compare( $db_version, $version, '<' ) ) {
298 + $this->migrate( $version );
299 + }
300 + }
301 + }
302 +
303 + /**
304 + * Perform cleanup when the plugin is deactivated.
305 + *
306 + * @return void
307 + */
308 + public function deactivate() {
309 + // Update firewall status after de-activating plugin
310 + try {
311 + $token = $this->plugin->api->get_access_token();
312 + if ( ! empty( $token ) ) {
313 + $this->plugin->api->update_firewall_status( [ 'status' => 0 ] );
314 + }
315 + } catch (\Exception $e) {
316 + //
317 + }
318 +
319 + // Clear all Patchstack scheduled tasks.
320 + $tasks = [ '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' ];
321 + foreach ( $tasks as $task ) {
322 + wp_clear_scheduled_hook( $task );
323 + }
324 +
325 + // Cleanup the .htaccess file.
326 + $this->plugin->htaccess->cleanup_htaccess_file();
327 +
328 + // Remove the mu-plugin file if it exists.
329 + if ( file_exists( WPMU_PLUGIN_DIR . '/patchstack.php' )) {
330 + wp_delete_file( WPMU_PLUGIN_DIR . '/patchstack.php' );
331 + }
332 + }
333 +
334 + /**
335 + * Activate or deactivate a license on the current site.
336 + *
337 + * @param integer $id
338 + * @param string $secret
339 + * @param string $action
340 + * @return array
341 + */
342 + public function alter_license( $id, $secret, $action ) {
343 + // Store current keys in tmp variable so in case it fails, we can set it back.
344 + $tmp_id = get_option( 'patchstack_clientid' );
345 + $tmp_key = $this->get_secret_key();
346 +
347 + // Set the new values.
348 + update_option( 'patchstack_clientid', $id );
349 + $this->set_secret_key( $secret );
350 +
351 + // Activate the license.
352 + if ( $action == 'activate' ) {
353 + $api_result = $this->plugin->api->get_access_token( $id, $secret, true );
354 +
355 + // Valid result?
356 + if ( ! $api_result ) {
357 + update_option( 'patchstack_clientid', $tmp_id );
358 + $this->set_secret_key( $tmp_key );
359 + return [
360 + 'result' => 'error',
361 + 'message' => 'Cannot activate license!',
362 + ];
363 + }
364 +
365 + // If we have an access token, tell our API that the firewall is activated
366 + // and the current URL of the site.
367 + update_option( 'patchstack_license_activated', '1' );
368 + $this->plugin->api->update_license_status();
369 + $token = $this->plugin->api->get_access_token();
370 + if ( ! empty( $token ) ) {
371 + do_action( 'patchstack_send_software_data' );
372 + if ( get_option( 'patchstack_license_free', 0 ) != 1 ) {
373 + do_action( 'patchstack_post_firewall_rules' );
374 + do_action( 'patchstack_post_dynamic_firewall_rules' );
375 + $this->header();
376 + }
377 +
378 + $this->plugin->api->update_firewall_status( [ 'status' => $this->get_option( 'patchstack_basic_firewall' ) == 1 ] );
379 + $this->plugin->api->update_url( [ 'plugin_url' => get_option( 'siteurl' ) ] );
380 + $this->plugin->api->ping();
381 + }
382 + return [
383 + 'result' => 'success',
384 + 'message' => 'License activated!',
385 + ];
386 + }
387 +
388 + // Deactivate the license.
389 + if ( $action == 'deactivate' ) {
390 + update_option( 'patchstack_api_token', '' );
391 + update_option( 'patchstack_license_activated', '0' );
392 + return [
393 + 'result' => 'success',
394 + 'message' => 'License deactivated!',
395 + ];
396 + }
397 + }
398 +
399 + /**
400 + * Send a request to our API for the IP address header.
401 + *
402 + * @return void
403 + */
404 + public function header()
405 + {
406 + $header = get_option( 'patchstack_firewall_ip_header', '' );
407 + $computed = get_option( 'patchstack_ip_header_computed', 0 );
408 +
409 + if ( $header == '' && ! $computed ) {
410 + // Create an OTT token.
411 + $ott = md5( wp_generate_password( 32, true, true ) );
412 + update_option( 'patchstack_ott_action', $ott );
413 +
414 + // Tell our API.
415 + wp_remote_request(
416 + $this->plugin->api_url . '/api/header',
417 + [
418 + 'method' => 'POST',
419 + 'timeout' => 60,
420 + 'redirection' => 5,
421 + 'httpversion' => '1.0',
422 + 'blocking' => true,
423 + 'headers' => [
424 + 'Source-Host' => get_site_url(),
425 + ],
426 + 'body' => [
427 + 'token' => $ott,
428 + 'url' => get_site_url()
429 + ],
430 + 'cookies' => [],
431 + ]
432 + );
433 + }
434 + }
435 +}