Completed
Push — master ( 8851bc...2533f0 )
by Jeremy
32:27 queued 21:03
created

VaultPress::commentmeta_insert_handler()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
nc 2
nop 2
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * Plugin Name: VaultPress
4
 * Plugin URI: http://vaultpress.com/?utm_source=plugin-uri&amp;utm_medium=plugin-description&amp;utm_campaign=1.0
5
 * Description: Protect your content, themes, plugins, and settings with <strong>realtime backup</strong> and <strong>automated security scanning</strong> from <a href="http://vaultpress.com/?utm_source=wp-admin&amp;utm_medium=plugin-description&amp;utm_campaign=1.0" rel="nofollow">VaultPress</a>. Activate, enter your registration key, and never worry again. <a href="http://vaultpress.com/help/?utm_source=wp-admin&amp;utm_medium=plugin-description&amp;utm_campaign=1.0" rel="nofollow">Need some help?</a>
6
 * Version: 2.2.0-alpha
7
 * Author: Automattic
8
 * Author URI: http://vaultpress.com/?utm_source=author-uri&amp;utm_medium=plugin-description&amp;utm_campaign=1.0
9
 * License: GPL2+
10
 * Text Domain: vaultpress
11
 * Domain Path: /languages/
12
 *
13
 * @package automattic/vaultpress
14
 */
15
16
// don't call the file directly.
17
defined( 'ABSPATH' ) || die();
18
19
define( 'VAULTPRESS__MINIMUM_PHP_VERSION', '5.6' );
20
define( 'VAULTPRESS__VERSION', '2.2.0-alpha' );
21
define( 'VAULTPRESS__PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
22
23
/**
24
 * First, we check for our supported version of PHP. If it fails,
25
 * we "pause" VaultPress by ending the loading process and displaying an admin_notice to inform the site owner.
26
 */
27
if ( version_compare( phpversion(), VAULTPRESS__MINIMUM_PHP_VERSION, '<' ) ) {
28
	if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
29
		error_log(
30
			sprintf(
31
				/* translators: Placeholders are numbers, versions of PHP in use on the site, and required by VaultPress. */
32
				esc_html__( 'Your version of PHP (%1$s) is lower than the version required by VaultPress (%2$s). Please update PHP to continue enjoying VaultPress.', 'vaultpress' ),
33
				esc_html( phpversion() ),
34
				VAULTPRESS__MINIMUM_PHP_VERSION
35
			)
36
		);
37
	}
38
39
	/**
40
	 * Outputs an admin notice for folks running an outdated version of PHP.
41
	 *
42
	 * @todo: Remove once WP 5.2 is the minimum version.
43
	 *
44
	 * @since 2.0.0
45
	 */
46
	function vaultpress_admin_unsupported_php_notice() {
47
		$update_php_url = ( function_exists( 'wp_get_update_php_url' ) ? wp_get_update_php_url() : 'https://wordpress.org/support/update-php/' );
48
49
		?>
50
		<div class="notice notice-error is-dismissible">
51
			<p>
52
			<?php
53
				printf(
54
					/* translators: Placeholders are numbers, versions of PHP in use on the site, and required by VaultPress. */
55
					esc_html__( 'Your version of PHP (%1$s) is lower than the version required by VaultPress (%2$s). Please update PHP to continue enjoying VaultPress.', 'vaultpress' ),
56
					esc_html( phpversion() ),
57
					esc_html( VAULTPRESS__MINIMUM_PHP_VERSION )
58
				);
59
			?>
60
			</p>
61
			<p class="button-container">
62
				<?php
63
				printf(
64
					'<a class="button button-primary" href="%1$s" target="_blank" rel="noopener noreferrer">%2$s <span class="screen-reader-text">%3$s</span><span aria-hidden="true" class="dashicons dashicons-external"></span></a>',
65
					esc_url( $update_php_url ),
66
					__( 'Learn more about updating PHP' ),
67
					/* translators: accessibility text */
68
					__( '(opens in a new tab)' )
69
				);
70
				?>
71
			</p>
72
		</div>
73
		<?php
74
	}
75
76
	add_action( 'admin_notices', 'vaultpress_admin_unsupported_php_notice' );
77
	return;
78
}
79
80
/**
81
 * Load all the packages.
82
 *
83
 * We want to fail gracefully if `composer install` has not been executed yet, so we are checking for the autoloader.
84
 * If the autoloader is not present, let's log the failure, pause VaultPress, and display a nice admin notice.
85
 */
86
$loader = VAULTPRESS__PLUGIN_DIR . 'vendor/autoload_packages.php';
87
88
if ( is_readable( $loader ) ) {
89
	require $loader;
90
} else {
91 View Code Duplication
	if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
92
		error_log(
93
			wp_kses(
94
				__( 'Your installation of VaultPress is incomplete. If you installed it from GitHub, please run <code>composer install</code>.', 'vaultpress' ),
95
				array( 'code' => true )
96
			)
97
		);
98
	}
99
	/**
100
	 * Outputs an admin notice for folks running VaultPress without having run `composer install`.
101
	 */
102
	function vaultpress_admin_missing_autoloader() {
103
		?>
104
		<div class="notice notice-error is-dismissible">
105
			<p>
106
				<?php
107
					echo wp_kses(
108
						__( 'Your installation of VaultPress is incomplete. If you installed it from GitHub, please run <code>composer install</code>.', 'vaultpress' ),
109
						array( 'code' => true )
110
					);
111
				?>
112
			</p>
113
		</div>
114
		<?php
115
	}
116
	add_action( 'admin_notices', 'vaultpress_admin_missing_autoloader' );
117
	return;
118
}
119
120
/**
121
 * Main VaultPress class.
122
 */
123
class VaultPress {
124
	var $option_name          = 'vaultpress';
125
	var $auto_register_option = 'vaultpress_auto_register';
126
	var $db_version           = 4;
127
	var $plugin_version       = VAULTPRESS__VERSION;
128
129
	function __construct() {
130
		register_activation_hook( __FILE__, array( $this, 'activate' ) );
131
		register_deactivation_hook( __FILE__, array( $this, 'deactivate' ) );
132
133
		$this->options_blog_id = get_current_blog_id();
0 ignored issues
show
Bug introduced by
The property options_blog_id does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
134
		$options = get_option( $this->option_name );
135
		if ( !is_array( $options ) )
136
			$options = array();
137
138
		$defaults = array(
139
			'db_version'            => 0,
140
			'key'                   => '',
141
			'secret'                => '',
142
			'connection'            => false,
143
			'service_ips_cidr'      => false
144
		);
145
146
		$this->options = wp_parse_args( $options, $defaults );
0 ignored issues
show
Bug introduced by
The property options does not seem to exist. Did you mean options_blog_id?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
Documentation introduced by
$defaults is of type array<string,integer|str...ice_ips_cidr":"false"}>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
147
		$this->reset_pings();
148
149
		$this->upgrade();
150
151
		$this->add_global_actions_and_filters();
152
153
		if ( is_admin() ) {
154
			$this->add_admin_actions_and_filters();
155
		}
156
157
		if ( $this->is_registered() ) {
158
			$do_not_backup = $this->get_option( 'do_not_backup' ) || $this->get_option( 'do_not_send_backup_pings' );
159
			if ( $do_not_backup )
160
				$this->add_vp_required_filters();
161
			else
162
				$this->add_listener_actions_and_filters();
163
		}
164
	}
165
166
	static function &init() {
167
		static $instance = false;
168
169
		if ( !$instance ) {
170
			$instance = new VaultPress();
171
		}
172
173
		return $instance;
174
	}
175
176
	static function register( $registration_key ) {
177
		$vp = self::init();
178
179
		$nonce = wp_create_nonce( 'vp_register_' . $registration_key );
180
		$args = array( 'registration_key' =>  $registration_key, 'nonce' => $nonce );
181
		$response = $vp->contact_service( 'register', $args );
182
183
		// Check for an error
184
		if ( ! empty( $response['faultCode'] ) )
185
			return new WP_Error( $response['faultCode'], $response['faultString'] );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with $response['faultCode'].

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
186
187
		// Validate result
188
		if ( empty( $response['key'] ) || empty( $response['secret'] ) || empty( $response['nonce'] ) || $nonce != $response['nonce'] )
189
			return new WP_Error( 1, __( 'There was a problem trying to register your VaultPress subscription.' ) );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 1.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
190
191
		// Store the result, force a connection test.
192
		$vp->update_option( 'key', $response['key'] );
193
		$vp->update_option( 'secret', $response['secret'] );
194
		$vp->check_connection( true );
195
196
		return true;
197
	}
198
199
	function activate( $network_wide ) {
200
		$type = $network_wide ? 'network' : 'single';
201
		$this->update_option( 'activated', $type );
202
203
		// force a connection check after an activation
204
		$this->clear_connection();
205
206
		if ( get_option( 'vaultpress_auto_connect' ) ) {
207
			$this->register_via_jetpack( true );
208
		}
209
	}
210
211
	function deactivate() {
212
		if ( $this->is_registered() )
213
			$this->contact_service( 'plugin_status', array( 'vp_plugin_status' => 'deactivated' ) );
214
	}
215
216
	function upgrade() {
217
		$current_db_version = $this->get_option( 'db_version' );
218
219
		if ( $current_db_version < 1 ) {
220
			$this->options['connection']  = get_option( 'vaultpress_connection' );
0 ignored issues
show
Bug introduced by
The property options does not seem to exist. Did you mean options_blog_id?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
221
			$this->options['key']         = get_option( 'vaultpress_key' );
0 ignored issues
show
Bug introduced by
The property options does not seem to exist. Did you mean options_blog_id?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
222
			$this->options['secret']      = get_option( 'vaultpress_secret' );
0 ignored issues
show
Bug introduced by
The property options does not seem to exist. Did you mean options_blog_id?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
223
			$this->options['service_ips'] = get_option( 'vaultpress_service_ips' );
0 ignored issues
show
Bug introduced by
The property options does not seem to exist. Did you mean options_blog_id?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
224
225
			// remove old options
226
			$old_options = array(
227
				'vaultpress_connection',
228
				'vaultpress_hostname',
229
				'vaultpress_key',
230
				'vaultpress_secret',
231
				'vaultpress_service_ips',
232
				'vaultpress_timeout',
233
				'vp_allow_remote_execution',
234
				'vp_debug_request_signing',
235
				'vp_disable_firewall',
236
			);
237
238
			foreach ( $old_options as $option )
239
				delete_option( $option );
240
241
			$this->options['db_version'] = $this->db_version;
0 ignored issues
show
Bug introduced by
The property options does not seem to exist. Did you mean options_blog_id?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
242
			$this->update_options();
243
		}
244
245
		if ( $current_db_version < 2 ) {
246
			$this->delete_option( 'timeout' );
247
			$this->delete_option( 'disable_firewall' );
248
			$this->update_option( 'db_version', $this->db_version );
249
			$this->clear_connection();
250
		}
251
252
		if ( $current_db_version < 3 ) {
253
			$this->update_firewall();
254
			$this->update_option( 'db_version', $this->db_version );
255
			$this->clear_connection();
256
		}
257
258
		if ( $current_db_version < 4 ) {
259
			$this->update_firewall();
260
			$this->update_option( 'db_version', $this->db_version );
261
			$this->clear_connection();
262
		}
263
	}
264
265
	function get_option( $key ) {
266
		if ( 'hostname' == $key ) {
267
			if ( defined( 'VAULTPRESS_HOSTNAME' ) )
268
				return VAULTPRESS_HOSTNAME;
269
			else
270
				return 'vaultpress.com';
271
		}
272
273
		if ( 'timeout' == $key ) {
274
			if ( defined( 'VAULTPRESS_TIMEOUT' ) )
275
				return VAULTPRESS_TIMEOUT;
276
			else
277
				return 60;
278
		}
279
280
		if ( 'disable_firewall' == $key ) {
281
			if ( defined( 'VAULTPRESS_DISABLE_FIREWALL' ) )
282
				return VAULTPRESS_DISABLE_FIREWALL;
283
			else
284
				return false;
285
		}
286
287
		if ( ( 'key' == $key || 'secret' == $key ) && empty( $this->options[$key] ) ) {
0 ignored issues
show
Bug introduced by
The property options does not seem to exist. Did you mean options_blog_id?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
288
			return '';
289
		}
290
291
		// allow_forwarded_for can be overrided by config, or stored in or out of the vp option
292
		if ( 'allow_forwarded_for' === $key ) {
293
			if ( defined( 'ALLOW_FORWARDED_FOR' ) ) {
294
				return ALLOW_FORWARDED_FOR;
295
			}
296
297
			$standalone_option = get_option( 'vaultpress_allow_forwarded_for' );
298
			if ( ! empty( $standalone_option ) ) {
299
				return $standalone_option;
300
			}
301
		}
302
303
		if ( isset( $this->options[$key] ) )
0 ignored issues
show
Bug introduced by
The property options does not seem to exist. Did you mean options_blog_id?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
304
			return $this->options[$key];
0 ignored issues
show
Bug introduced by
The property options does not seem to exist. Did you mean options_blog_id?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
305
306
		return false;
307
	}
308
309
	function update_option( $key, $value ) {
310
		if ( 'allow_forwarded_for' === $key ) {
311
			update_option( 'vaultpress_allow_forwarded_for', $value );
312
313
			if ( isset( $this->options[ $key ] ) ) {
0 ignored issues
show
Bug introduced by
The property options does not seem to exist. Did you mean options_blog_id?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
314
				unset( $this->options[ $key ] );
315
				$this->update_options();
316
			}
317
			return;
318
		}
319
320
		$this->options[$key] = $value;
0 ignored issues
show
Bug introduced by
The property options does not seem to exist. Did you mean options_blog_id?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
321
		$this->update_options();
322
	}
323
324
	function delete_option( $key ) {
325
		if ( 'allow_forwarded_for' === $key ) {
326
			delete_option( 'vaultpress_allow_forwarded_for' );
327
		}
328
329
		unset( $this->options[$key] );
330
		$this->update_options();
331
	}
332
333
	function update_options() {
334
		// Avoid overwriting the VaultPress option if current blog_id has changed since reading it
335
		if ( get_current_blog_id() !== $this->options_blog_id ) {
336
			return;
337
		}
338
339
		update_option( $this->option_name, $this->options );
0 ignored issues
show
Bug introduced by
The property options does not seem to exist. Did you mean options_blog_id?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
340
	}
341
342
	function admin_init() {
343
		if ( !current_user_can( 'manage_options' ) )
344
			return;
345
346
		load_plugin_textdomain( 'vaultpress', false, dirname( plugin_basename( __FILE__ ) ) . '/languages/' );
347
	}
348
349
	function admin_head() {
350
		if ( ! current_user_can( 'manage_options' ) ) {
351
			return;
352
		}
353
354
		// Array of hooks where we want to hook our notices.
355
		$notice_hooks = array( 'user_admin_notices' );
356
357
		/*
358
		 * In the VaultPress dashboard, move the notices.
359
		 */
360
		$screen = get_current_screen();
361
		if (
362
			! is_null( $screen )
363
			&& in_array(
364
				$screen->id,
365
				array( 'jetpack_page_vaultpress', 'toplevel_page_vaultpress' ),
366
				true
367
			)
368
		) {
369
			$notice_hooks[] = 'vaultpress_notices';
370
		} else {
371
			$notice_hooks[] = 'admin_notices';
372
		}
373
374
		if ( $activated = $this->get_option( 'activated' ) ) {
375
			if ( 'network' == $activated ) {
376
				add_action( 'network_admin_notices', array( $this, 'activated_notice' ) );
377
			} else {
378
				foreach ( $notice_hooks as $filter ) {
379
					add_action( $filter, array( $this, 'activated_notice' ) );
380
				}
381
			}
382
		}
383
384
		// ask the user to connect their site w/ VP
385
		if ( !$this->is_registered() ) {
386
			foreach ( $notice_hooks as $filter ) {
387
				add_action( $filter, array( $this, 'connect_notice' ) );
388
			}
389
390
		// if we have an error make sure to let the user know about it
391
		} else {
392
			$error_code = $this->get_option( 'connection_error_code' );
393
		 	if ( ! empty( $error_code ) ) {
394
				foreach ( $notice_hooks as $filter ) {
395
					add_action( $filter, array( $this, 'error_notice' ) );
396
				}
397
			}
398
		}
399
	}
400
401
	function admin_menu() {
402
		// if Jetpack is loaded then we need to wait for that menu to be added
403
		if ( class_exists( 'Jetpack' ) )
404
			add_action( 'jetpack_admin_menu', array( $this, 'load_menu' ) );
405
		else
406
			$this->load_menu();
407
	}
408
409
	function load_menu() {
410
		if ( class_exists( 'Jetpack' ) ) {
411
			$hook = add_submenu_page( 'jetpack', 'VaultPress', 'VaultPress', 'manage_options', 'vaultpress', array( $this, 'ui' ) );
412
		} else {
413
			$hook = add_menu_page( 'VaultPress', 'VaultPress', 'manage_options', 'vaultpress', array( $this, 'ui' ), 'div' );
414
		}
415
416
		add_action( "load-$hook", array( $this, 'ui_load' ) );
417
		add_action( 'admin_print_styles', array( $this, 'styles' ) );
418
	}
419
420
	function styles() {
421
		if ( !current_user_can( 'manage_options' ) || !is_admin() )
422
			return;
423
424
		wp_enqueue_style( 'vaultpress-nav', plugins_url( '/nav-styles.css', __FILE__ ), false, date( 'Ymd' ) );
425
426
		if ( isset( $_GET['page'] ) && 'vaultpress' == $_GET['page'] )
427
			wp_enqueue_style( 'vaultpress', plugins_url( '/styles.css', __FILE__ ), false, date( 'Ymd' ) );
428
	}
429
430
	// display a security threat notice if one exists
431
	function toolbar( $wp_admin_bar ) {
432
		global $wp_version;
433
434
		// these new toolbar functions were introduced in 3.3
435
		// http://codex.wordpress.org/Function_Reference/add_node
436
		if ( version_compare( $wp_version, '3.3', '<') )
437
			return;
438
439
		if ( !current_user_can( 'manage_options' ) )
440
			return;
441
442
		$messages = $this->get_messages();
443
		if ( !empty( $messages['security_notice_count'] ) ) {
444
			$count = (int)$messages['security_notice_count'];
445
			if ( $count > 0 ) {
446
				$count = number_format( $count, 0 );
447
				$wp_admin_bar->add_node( array(
448
					'id' => 'vp-notice',
449
					'title' => '<span class="ab-icon"></span>' .
450
						sprintf( _n( '%s Security Threat', '%s Security Threats', $count , 'vaultpress'), $count ),
451
					'parent' => 'top-secondary',
452
					'href' => sprintf( 'https://dashboard.vaultpress.com/%d/security/', $messages['site_id'] ),
453
					'meta'  => array(
454
						'title' => __( 'Visit VaultPress Security' , 'vaultpress'),
455
						'onclick' => 'window.open( this.href ); return false;',
456
						'class' => 'error'
457
					),
458
				) );
459
			}
460
		}
461
	}
462
463
	// get any messages from the VP servers
464
	function get_messages( $force_reload = false ) {
465
		$last_contact = $this->get_option( 'messages_last_contact' );
466
467
		// only run the messages check every 30 minutes
468
		if ( ( time() - (int)$last_contact ) > 1800 || $force_reload ) {
469
			$messages = base64_decode( $this->contact_service( 'messages', array() ) );
470
			$messages = unserialize( $messages );
471
			$this->update_option( 'messages_last_contact', time() );
472
			$this->update_option( 'messages', $messages );
473
		} else {
474
			$messages = $this->get_option( 'messages' );
475
		}
476
477
		return $messages;
478
	}
479
480
	function server_url() {
481
		if ( !isset( $this->_server_url ) ) {
482
			$scheme = is_ssl() ? 'https' : 'http';
483
			$this->_server_url = sprintf( '%s://%s/', $scheme, $this->get_option( 'hostname' ) );
0 ignored issues
show
Bug introduced by
The property _server_url does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
484
		}
485
486
		return $this->_server_url;
487
	}
488
489
	/**
490
	 * Show message if plugin is activated but not connected to VaultPress.
491
	 */
492
	function connect_notice() {
493
		$screen = get_current_screen();
494
495
		/**
496
		 * Do not display any error message if we don't have any info about the page.
497
		 */
498
		if ( is_null( $screen ) ) {
499
			return;
500
		}
501
502
		/**
503
		 * Only display errors on specific pages:
504
		 * - the main dashboard.
505
		 * - the Jetpack dashboard.
506
		 * - the plugins screen.
507
		 */
508
509
		if (
510
		in_array(
511
			$screen->id,
512
			array(
513
				'dashboard',
514
				'toplevel_page_jetpack',
515
				'plugins',
516
			),
517
			true
518
		)
519
		) {
520
			$message = sprintf(
521
				wp_kses(
522
				/* translators: URLs to VaultPress' dashboard page. */
523
					__( 'To back up and secure your site, enter your registration key. <a href="%1$s">Register VaultPress or purchase a plan.</a>', 'vaultpress' ),
524
					array(
525
						'a' => array(
526
							'href' => array(),
527
						),
528
					)
529
				),
530
				admin_url( 'admin.php?page=vaultpress' )
531
			);
532
			$this->ui_message( $message, 'notice', __( 'VaultPress needs your attention!', 'vaultpress' ) );
533
		}
534
	}
535
536
  // show message after activation
537
	function activated_notice() {
538
		if ( 'network' == $this->get_option( 'activated' ) ) {
539
			$message = sprintf(
540
				__( 'Each site will need to be registered with VaultPress separately. You can purchase new keys from your <a href="%1$s">VaultPress&nbsp;Dashboard</a>.', 'vaultpress' ),
541
				'https://dashboard.vaultpress.com/'
542
			);
543
			$this->ui_message( $message, 'activated', __( 'VaultPress has been activated across your network!', 'vaultpress' ) );
544
545
		// key and secret already exist in db
546
		} elseif ( $this->is_registered() ) {
547
			if ( $this->check_connection() ) {
548
			}
549
		}
550
551
		$this->delete_option( 'activated' );
552
	}
553
554
	/**
555
	 * Display an error notice when something is wrong with our VaultPress installation.
556
	 */
557
	public function error_notice() {
558
		$error_message = $this->get_option( 'connection_error_message' );
559
560
		// link to the VaultPress page if we're not already there.
561
		if (
562
			! isset( $_GET['page'] )
563
			|| 'vaultpress' != $_GET['page']
564
		) {
565
			$error_message .= sprintf(
566
				' <a href="%s">%s</a>',
567
				admin_url( 'admin.php?page=vaultpress' ),
568
				esc_html__( 'Visit the VaultPress page', 'vaultpress' )
569
			);
570
		}
571
572
		$screen = get_current_screen();
573
574
		/*
575
		 * Do not display any error message if we don't have a message,
576
		 * or have no info about the page.
577
		 */
578
		if ( is_null( $screen ) || empty( $error_message ) ) {
579
			return;
580
		}
581
582
		/*
583
		 * Only display errors on specific pages:
584
		 * - the main dashboard.
585
		 * - the VaultPress and Jetpack dashboards.
586
		 * - the plugins screen.
587
		 */
588
		if (
589
			in_array(
590
				$screen->id,
591
				array(
592
					'dashboard',
593
					'toplevel_page_jetpack',
594
					'jetpack_page_vaultpress',
595
					'toplevel_page_vaultpress',
596
					'plugins',
597
				),
598
				true
599
			)
600
		) {
601
			$this->ui_message( $error_message, 'error' );
602
		}
603
	}
604
605
	/**
606
	 * Adds the main wrappers and the header, and defers controls to ui_render to decide which view to render.
607
	 */
608
	function ui() {
609
		$ui_state = $this->ui_render();
610
		?>
611
			<div id="jp-plugin-container">
612
				<?php $this->ui_masthead( $ui_state[ 'dashboard_link' ] ); ?>
613
				<div class="vp-wrap">
614
					<?php
615
					/**
616
					 * Allow the display of custom notices.
617
					 *
618
					 * @since 2.0.0
619
					 */
620
					do_action( 'vaultpress_notices' );
621
					?>
622
					<?php echo $ui_state[ 'ui' ]; // This content is sanitized when it's produced. ?>
623
				</div>
624
				<?php $this->ui_footer(); ?>
625
			</div>
626
		<?php
627
	}
628
629
	/**
630
	 * Decides which UI view to render and executes it.
631
	 *
632
	 * @return array $args {
633
	 *     An array of options to render the dashboard.
634
	 *
635
	 *     @type string $ui             Dashboard markup.
636
	 *     @type string $dashboard_link Whether to show the link to the VaultPress dashboard.
637
	 * }
638
	 */
639
	function ui_render() {
640
		ob_start();
641
642
		if ( $this->is_localhost() ) {
643
			$this->update_option( 'connection', time() );
644
			$this->update_option( 'connection_error_code', 'error_localhost' );
645
			$this->update_option(
646
				'connection_error_message',
647
				esc_html__( 'Hostnames such as localhost or 127.0.0.1 can not be reached by vaultpress.com and will not work with the service. Sites must be publicly accessible in order to work with VaultPress.', 'vaultpress' )
648
			);
649
			$this->error_notice();
650
			return array( 'ui' => ob_get_clean(), 'dashboard_link' => false );
651
		}
652
653
		if ( ! empty( $_GET[ 'error' ] ) ) {
654
			$this->error_notice();
655
			$this->clear_connection();
656
		}
657
658
		if ( ! $this->is_registered() ) {
659
			$this->ui_register();
660
			return array( 'ui' => ob_get_clean(), 'dashboard_link' => false );
661
		}
662
663
		$status = $this->contact_service( 'status' );
664
		if ( ! $status ) {
665
			$error_code = $this->get_option( 'connection_error_code' );
666
			if ( 0 == $error_code ) {
667
				$this->ui_fatal_error();
668
			} else {
669
				$this->ui_register();
670
			}
671
			return array( 'ui' => ob_get_clean(), 'dashboard_link' => 0 != $error_code );
672
		}
673
674
		$ticker = $this->contact_service( 'ticker' );
675
		if ( is_array( $ticker ) && isset( $ticker[ 'faultCode' ] ) ) {
676
			$this->error_notice();
677
			$this->ui_register();
678
			return array( 'ui' => ob_get_clean(), 'dashboard_link' => true );
679
		}
680
681
		$this->ui_main();
682
		return array( 'ui' => ob_get_clean(), 'dashboard_link' => true );
683
	}
684
685
	function ui_load() {
686
		if ( ! current_user_can( 'manage_options' ) ) {
687
			return;
688
		}
689
690
		if ( isset( $_POST['action'] ) && 'delete-vp-settings' == $_POST['action'] ) {
691
			check_admin_referer( 'delete_vp_settings' );
692
693
			$ai_ping_queue_size = $this->ai_ping_queue_size();
694
			if ( ! empty( $ai_ping_queue_size->option_count ) && $ai_ping_queue_size->option_count > 1 ) {
695
				$this->ai_ping_queue_delete();
696
			}
697
698
			delete_option( $this->option_name );
699
			delete_option( 'vaultpress_service_ips_external_cidr' );
700
			delete_option( '_vp_signatures' );
701
			delete_option( '_vp_config_option_name_ignore' );
702
			delete_option( '_vp_config_post_meta_name_ignore' );
703
			delete_option( '_vp_config_should_ignore_files' );
704
			delete_option( '_vp_current_scan' );
705
			delete_option( 'vaultpress_auto_register' );
706
707
			wp_redirect( admin_url( 'admin.php?page=vaultpress&delete-vp-settings=1' ) );
708
			exit();
709
		}
710
711
		// run code that might be updating the registration key
712
		if ( isset( $_POST['action'] ) && 'register' == $_POST['action'] ) {
713
			check_admin_referer( 'vaultpress_register' );
714
715
			// reset the connection info so messages don't cross
716
			$this->clear_connection();
717
718
			// if registering via Jetpack, get a key...
719
			if ( isset( $_POST['key_source'] ) && 'jetpack' === $_POST['key_source'] ) {
720
				$registration_key = $this->get_key_via_jetpack();
721
				if ( is_wp_error( $registration_key ) ) {
722
					$this->update_option( 'connection_error_code', -2 );
723
					$this->update_option(
724
						'connection_error_message',
725
						sprintf( __('<strong>Failed to register VaultPress via Jetpack</strong>: %s. If you&rsquo;re still having issues please <a href="%1$s">contact the VaultPress&nbsp;Safekeepers</a>.', 'vaultpress' ),
726
							esc_html( $registration_key->get_error_message() ), 'http://vaultpress.com/contact/' )
727
					);
728
					wp_redirect( admin_url( 'admin.php?page=vaultpress&error=true' ) );
729
					exit();
730
				}
731
			} else {
732
			$registration_key = trim( $_POST[ 'registration_key' ] );
733
			}
734
735
			if ( empty( $registration_key ) ) {
736
				$this->update_option( 'connection_error_code', 1 );
737
				$this->update_option(
738
					'connection_error_message',
739
					sprintf(
740
						__( '<strong>That\'s not a valid registration key.</strong> Head over to the <a href="%1$s" title="Sign in to your VaultPress Dashboard">VaultPress&nbsp;Dashboard</a> to find your key.', 'vaultpress' ),
741
						'https://dashboard.vaultpress.com/'
742
					)
743
				);
744
				wp_redirect( admin_url( 'admin.php?page=vaultpress&error=true' ) );
745
				exit();
746
			}
747
748
			// try to register the plugin
749
			$nonce = wp_create_nonce( 'vp_register_' . $registration_key );
750
			$args = array( 'registration_key' =>  $registration_key, 'nonce' => $nonce );
751
			$response = $this->contact_service( 'register', $args );
752
753
			// we received an error from the VaultPress servers
754 View Code Duplication
			if ( !empty( $response['faultCode'] ) ) {
755
				$this->update_option( 'connection_error_code',    $response['faultCode'] );
756
				$this->update_option( 'connection_error_message', $response['faultString'] );
757
				wp_redirect( admin_url( 'admin.php?page=vaultpress&error=true' ) );
758
				exit();
759
			}
760
761
			// make sure the returned data looks valid
762
			if ( empty( $response['key'] ) || empty( $response['secret'] ) || empty( $response['nonce'] ) || $nonce != $response['nonce'] ) {
763
				$this->update_option( 'connection_error_code', 1 );
764
				$this->update_option( 'connection_error_message', sprintf( __( 'There was a problem trying to register your subscription. Please try again. If you&rsquo;re still having issues please <a href="%1$s">contact the VaultPress&nbsp;Safekeepers</a>.', 'vaultpress' ), 'http://vaultpress.com/contact/' ) );
765
				wp_redirect( admin_url( 'admin.php?page=vaultpress&error=true' ) );
766
				exit();
767
			}
768
769
			// need to update these values in the db so the servers can try connecting to the plugin
770
			$this->update_option( 'key', $response['key'] );
771
			$this->update_option( 'secret', $response['secret'] );
772
			if ( $this->check_connection( true ) ) {
773
				wp_redirect( admin_url( 'admin.php?page=vaultpress' ) );
774
				exit();
775
			}
776
777
			// reset the key and secret
778
			$this->update_option( 'key', '' );
779
			$this->update_option( 'secret', '' );
780
			wp_redirect( admin_url( 'admin.php?page=vaultpress&error=true' ) );
781
			exit();
782
		}
783
	}
784
785
	function ui_register() {
786
		?>
787
			<div class="vp-notice__wide">
788
				<div class="dops-card">
789
					<img src="<?php echo esc_url( plugins_url( 'images/security.svg', __FILE__ ) ); ?>" alt="VaultPress">
790
					<h2><?php _e( 'The VaultPress plugin requires a subscription.', 'vaultpress' ); ?></h2>
791
					<p><?php _e( 'Get realtime backups, automated security scanning, and support from WordPress&nbsp;experts.', 'vaultpress' ); ?></p>
792
					<a class="dops-button is-primary" href="https://vaultpress.com/plans/?utm_source=plugin-unregistered&amp;utm_medium=view-plans-and-pricing&amp;utm_campaign=1.0-plugin" target="_blank" rel="noopener noreferrer"><?php _e( 'View plans and pricing', 'vaultpress' ); ?></a>
793
				</div>
794
			</div>
795
796
			<div class="jp-dash-section-header">
797
				<div class="jp-dash-section-header__label">
798
					<h2 class="jp-dash-section-header__name">
799
						<?php esc_html_e( 'Management', 'vaultpress' ); ?>
800
					</h2>
801
				</div>
802
			</div>
803
804
			<div class="vp-row">
805
				<div class="vp-col">
806
					<div class="dops-card dops-section-header is-compact">
807
						<?php esc_html_e( 'Registration key', 'vaultpress' ) ?>
808
					</div>
809
					<div class="dops-card">
810
						<form method="post" action="">
811
							<fieldset>
812
								<p>
813
									<?php esc_html_e( 'Paste your registration key&nbsp;below:', 'vaultpress' ); ?>
814
								</p>
815
								<p>
816
									<textarea class="dops-textarea" placeholder="<?php esc_attr_e( __( 'Enter your key here...', 'vaultpress' ) ); ?>" name="registration_key"></textarea>
817
								</p>
818
								<button class="dops-button is-compact"><?php _e( 'Register ', 'vaultpress' ); ?></button>
819
								<input type="hidden" name="action" value="register" />
820
								<?php wp_nonce_field( 'vaultpress_register' ); ?>
821
							</fieldset>
822
						</form>
823
					</div>
824
				</div>
825
				<div class="vp-col">
826
					<?php $this->ui_delete_vp_settings_button(); ?>
827
				</div>
828
			</div>
829
		<?php
830
	}
831
832
	/**
833
	 * Renders the top header.
834
	 *
835
	 * @param bool $show_nav Whether to show navigation.
836
	 */
837
	function ui_masthead( $show_nav = true ) {
838
		?>
839
		<div class="jp-masthead">
840
			<div class="jp-masthead__inside-container">
841
				<div class="jp-masthead__logo-container">
842
					<a class="jp-masthead__logo-link" href="https://vaultpress.com">
843
						<img src="<?php echo esc_url( plugins_url( 'images/vaultpress.svg', __FILE__ ) ); ?>" alt="VaultPress">
844
					</a>
845
				</div>
846
				<?php if ( $show_nav ) : ?>
847
					<div class="jp-masthead__nav">
848
						<div class="dops-button-group">
849
							<a href="https://dashboard.vaultpress.com" class="dops-button is-compact" target="_blank" rel="noopener noreferrer">
850
								<?php _e( 'Visit Dashboard', 'vaultpress' ); ?>
851
							</a>
852
						</div>
853
					</div>
854
				<?php endif; ?>
855
			</div>
856
		</div>
857
		<?php
858
	}
859
860
	/**
861
	 * Renders the footer.
862
	 */
863
	function ui_footer() {
864
		?>
865
		<div class="jp-footer">
866
			<div class="jp-footer__a8c-attr-container">
867
				<svg role="img" class="jp-footer__a8c-attr" x="0" y="0" viewBox="0 0 935 38.2" enable-background="new 0 0 935 38.2" aria-labelledby="a8c-svg-title"><title id="a8c-svg-title">An Automattic Airline</title>
868
					<path d="M317.1 38.2c-12.6 0-20.7-9.1-20.7-18.5v-1.2c0-9.6 8.2-18.5 20.7-18.5 12.6 0 20.8 8.9 20.8 18.5v1.2C337.9 29.1 329.7 38.2 317.1 38.2zM331.2 18.6c0-6.9-5-13-14.1-13s-14 6.1-14 13v0.9c0 6.9 5 13.1 14 13.1s14.1-6.2 14.1-13.1V18.6zM175 36.8l-4.7-8.8h-20.9l-4.5 8.8h-7L157 1.3h5.5L182 36.8H175zM159.7 8.2L152 23.1h15.7L159.7 8.2zM212.4 38.2c-12.7 0-18.7-6.9-18.7-16.2V1.3h6.6v20.9c0 6.6 4.3 10.5 12.5 10.5 8.4 0 11.9-3.9 11.9-10.5V1.3h6.7V22C231.4 30.8 225.8 38.2 212.4 38.2zM268.6 6.8v30h-6.7v-30h-15.5V1.3h37.7v5.5H268.6zM397.3 36.8V8.7l-1.8 3.1 -14.9 25h-3.3l-14.7-25 -1.8-3.1v28.1h-6.5V1.3h9.2l14 24.4 1.7 3 1.7-3 13.9-24.4h9.1v35.5H397.3zM454.4 36.8l-4.7-8.8h-20.9l-4.5 8.8h-7l19.2-35.5h5.5l19.5 35.5H454.4zM439.1 8.2l-7.7 14.9h15.7L439.1 8.2zM488.4 6.8v30h-6.7v-30h-15.5V1.3h37.7v5.5H488.4zM537.3 6.8v30h-6.7v-30h-15.5V1.3h37.7v5.5H537.3zM569.3 36.8V4.6c2.7 0 3.7-1.4 3.7-3.4h2.8v35.5L569.3 36.8 569.3 36.8zM628 11.3c-3.2-2.9-7.9-5.7-14.2-5.7 -9.5 0-14.8 6.5-14.8 13.3v0.7c0 6.7 5.4 13 15.3 13 5.9 0 10.8-2.8 13.9-5.7l4 4.2c-3.9 3.8-10.5 7.1-18.3 7.1 -13.4 0-21.6-8.7-21.6-18.3v-1.2c0-9.6 8.9-18.7 21.9-18.7 7.5 0 14.3 3.1 18 7.1L628 11.3zM321.5 12.4c1.2 0.8 1.5 2.4 0.8 3.6l-6.1 9.4c-0.8 1.2-2.4 1.6-3.6 0.8l0 0c-1.2-0.8-1.5-2.4-0.8-3.6l6.1-9.4C318.7 11.9 320.3 11.6 321.5 12.4L321.5 12.4z"></path>
869
					<path d="M37.5 36.7l-4.7-8.9H11.7l-4.6 8.9H0L19.4 0.8H25l19.7 35.9H37.5zM22 7.8l-7.8 15.1h15.9L22 7.8zM82.8 36.7l-23.3-24 -2.3-2.5v26.6h-6.7v-36H57l22.6 24 2.3 2.6V0.8h6.7v35.9H82.8z"></path>
870
					<path d="M719.9 37l-4.8-8.9H694l-4.6 8.9h-7.1l19.5-36h5.6l19.8 36H719.9zM704.4 8l-7.8 15.1h15.9L704.4 8zM733 37V1h6.8v36H733zM781 37c-1.8 0-2.6-2.5-2.9-5.8l-0.2-3.7c-0.2-3.6-1.7-5.1-8.4-5.1h-12.8V37H750V1h19.6c10.8 0 15.7 4.3 15.7 9.9 0 3.9-2 7.7-9 9 7 0.5 8.5 3.7 8.6 7.9l0.1 3c0.1 2.5 0.5 4.3 2.2 6.1V37H781zM778.5 11.8c0-2.6-2.1-5.1-7.9-5.1h-13.8v10.8h14.4c5 0 7.3-2.4 7.3-5.2V11.8zM794.8 37V1h6.8v30.4h28.2V37H794.8zM836.7 37V1h6.8v36H836.7zM886.2 37l-23.4-24.1 -2.3-2.5V37h-6.8V1h6.5l22.7 24.1 2.3 2.6V1h6.8v36H886.2zM902.3 37V1H935v5.6h-26v9.2h20v5.5h-20v10.1h26V37H902.3z"></path>
871
				</svg>
872
			</div>
873
			<ul class="jp-footer__links">
874
				<li class="jp-footer__link-item">
875
					<a href="https://vaultpress.com" class="jp-footer__link" title="<?php esc_attr_e( 'VaultPress version', 'vaultpress' ) ?>" target="_blank" rel="noopener noreferrer">
876
						<?php printf( 'VaultPress %s', $this->plugin_version ); ?>
877
					</a>
878
				</li>
879
				<li class="jp-footer__link-item">
880
					<a href="https://wordpress.com/tos/" class="jp-footer__link" title="<?php esc_attr_e( 'Terms of service', 'vaultpress' ) ?>" target="_blank" rel="noopener noreferrer">
881
						<?php esc_html_e( 'Terms', 'vaultpress' ); ?>
882
					</a>
883
				</li>
884
			</ul>
885
			<div class="jp-power">
886
				<a
887
					href="<?php echo class_exists( 'Jetpack_Admin_Page' ) ? esc_url( admin_url( 'admin.php?page=jetpack' ) ) : 'https://jetpack.com' ?>"
888
					class="jp-power__text-link"
889
					target="_blank"
890
					rel="noopener noreferrer"
891
				>
892
					<span class="jp-power__text"><?php esc_html_e( 'Powered by', 'vaultpress') ?></span> <?php echo $this->ui_logo(); ?>
893
				</a>
894
			</div>
895
		</div>
896
		<?php
897
	}
898
899
	function ui_main() {
900
		$response = base64_decode( $this->contact_service( 'plugin_ui' ) );
901
		echo $response;
902
		$this->ui_delete_vp_settings_button();
903
	}
904
905
	function ui_fatal_error() {
906
		$this->render_notice(
907
			sprintf(
908
				'<strong>' . __( 'We can\'t connect to %1$s.', 'vaultpress' ) . '</strong><br/>' .
909
				__( 'Please make sure that your website is accessible via the Internet. Please contact the VaultPress support if you still have issues.' ),
910
				esc_html( $this->get_option( 'hostname' ) )
911
			),
912
			'is-warning',
913
			array(
914
				'label' => __( 'Contact support' ),
915
				'url' => 'https://vaultpress.com/contact/',
916
			)
917
		);
918
	}
919
920
	function ui_message( $message, $type = 'notice', $heading = '' ) {
921
		$level = 'is-warning';
922
		if ( empty( $heading ) ) {
923
			switch ( $type ) {
924
				case 'error':
925
					$level = 'is-error';
926
					$heading = __( 'Oops... there seems to be a problem.', 'vaultpress' );
927
					break;
928
929
				case 'success':
930
					$level = 'is-success';
931
					$heading = __( 'Yay! Things look good.', 'vaultpress' );
932
					break;
933
934
				default:
935
					$heading = __( 'VaultPress needs your attention!', 'vaultpress' );
936
					break;
937
			}
938
		}
939
940
		$classes = in_array( get_current_screen()->parent_base, array( 'jetpack', 'vaultpress' ), true )
941
			? ''
942
			: "notice notice-$type";
943
944
		$this->render_notice(
945
			"<strong>$heading</strong><br/>$message",
946
			$level,
947
			array(),
948
			$classes
949
		);
950
	}
951
952
	/**
953
	 * Renders a notice. Can have
954
	 *
955
	 * @param string $content Notice main content.
956
	 * @param string $level Can be is-info, is-warning, is-error. By default, it's is-info.
957
	 * @param array  $action  {
958
	 *     Arguments to display a linked action button in the notice.
959
	 *
960
	 *     @type string $label The action button label.
961
	 *     @type string $url   The action button link.
962
	 * }
963
	 * @param string $classes This is added as a CSS class to the root node. Useful to pass WP core classes for notices.
964
	 */
965
	function render_notice( $content, $level = 'is-info', $action = array(), $classes = '' ) {
966
		$allowed_html = array(
967
			'a' => array( 'href' => true, 'target' => 'blank', 'rel' => 'noopener noreferrer' ),
968
			'br' => true,
969
			'strong' => true,
970
		);
971
		?>
972
			<div class="dops-notice vp-notice <?php echo esc_attr( "$level $classes" ) ?>">
973
				<span class="dops-notice__icon-wrapper">
974
					<svg class="gridicon gridicons-info dops-notice__icon" height="24" width="24" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
975
						<path d="M12 2C6.477 2 2 6.477 2 12s4.477 10 10 10 10-4.477 10-10S17.523 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z" />
976
					</svg>
977
				</span>
978
				<span class="dops-notice__content">
979
					<span class="dops-notice__text"><?php echo wp_kses( $content, $allowed_html ) ?></span>
980
				</span>
981
				<?php if ( ! empty( $action ) ) : ?>
982
					<a class="dops-notice__action" href="<?php echo esc_attr( $action['url'] ) ?>" target="_blank" rel="noopener noreferrer">
983
						<span><?php echo esc_html( $action['label'] ) ?></span>
984
					</a>
985
				<?php endif; ?>
986
			</div>
987
		<?php
988
	}
989
990
	function ui_delete_vp_settings_button() {
991
		?>
992
		<div class="dops-card dops-section-header is-compact">
993
			<?php _e( 'Reset settings', 'vaultpress' ); ?>
994
		</div>
995
		<?php
996
		if ( isset( $_GET['delete-vp-settings'] ) && 1 == (int) $_GET['delete-vp-settings'] ) {
997
			?>
998
			<div class="dops-card">
999
				<p><?php esc_html_e( 'All VaultPress settings have been deleted.', 'vaultpress' ); ?></p>
1000
			</div>
1001
			<?php
1002
		} else {
1003
			?>
1004
			<div class="dops-card">
1005
				<p><?php esc_html_e( 'Click this button to reset all VaultPress options in the database. You can try this if VaultPress is reporting a connection error.', 'vaultpress' ); ?></p>
1006
				<p><strong>
1007
				<?php
1008
				printf(
1009
					wp_kses(
1010
						/* translators: URLs to VaultPress dashboard. */
1011
						__( 'Warning: this button will unregister VaultPress and disconnect it from your site. If you intend on registering the plugin again, you can find your registration key <a href="%1$s" target="_blank" rel="noopener noreferrer">here</a>.', 'vaultpress' ),
1012
						array(
1013
							'a' => array(
1014
								'href'   => array(),
1015
								'target' => array(),
1016
								'rel'    => array(),
1017
							),
1018
						)
1019
					),
1020
					'https://dashboard.vaultpress.com/account/'
1021
				);
1022
				?>
1023
				</strong></p>
1024
				<form
1025
					onsubmit="return confirm( '<?php esc_html_e( 'Do you really want to reset all options?', 'vaultpress' ) ?>' );"
1026
					method="post"
1027
					action="">
1028
					<button class="dops-button is-scary is-compact"><?php esc_html_e( 'Delete all settings', 'vaultpress' ); ?></button>
1029
					<input type="hidden" name="action" value="delete-vp-settings"/>
1030
					<?php wp_nonce_field( 'delete_vp_settings' ); ?>
1031
				</form>
1032
			</div>
1033
			<?php
1034
		}
1035
	}
1036
1037
	/**
1038
	 * Render the Jetpack logo
1039
	 */
1040
	function ui_logo() {
1041
		if ( ! class_exists( 'Jetpack_Logo' ) ) {
1042
			require_once VAULTPRESS__PLUGIN_DIR . 'class-jetpack-logo.php';
1043
			$jetpack_logo = new Jetpack_Logo();
1044
		}
1045
1046
		return $jetpack_logo->output();
0 ignored issues
show
Bug introduced by
The variable $jetpack_logo does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
1047
	}
1048
1049
	function get_config( $key ) {
1050
		$val = get_option( $key );
1051
		if ( $val )
1052
			return $val;
1053
		switch( $key ) {
1054
			case '_vp_config_option_name_ignore':
1055
				$val = $this->get_option_name_ignore( true );
1056
				update_option( '_vp_config_option_name_ignore', $val );
1057
				break;
1058
			case '_vp_config_post_meta_name_ignore':
1059
				$val = $this->get_post_meta_name_ignore( true );
1060
				update_option( '_vp_config_post_meta_name_ignore', $val );
1061
				break;
1062
			case '_vp_config_should_ignore_files':
1063
				$val = $this->get_should_ignore_files( true );
1064
				update_option( '_vp_config_should_ignore_files', $val );
1065
				break;
1066
		}
1067
		return $val;
1068
	}
1069
1070
	// Option name patterns to ignore
1071
	function get_option_name_ignore( $return_defaults = false ) {
1072
		$defaults = array(
1073
			'vaultpress',
1074
			'cron',
1075
			'wpsupercache_gc_time',
1076
			'rewrite_rules',
1077
			'akismet_spam_count',
1078
			'/_transient_/',
1079
			'/^_vp_/',
1080
		);
1081
		if ( $return_defaults )
1082
			return $defaults;
1083
		$ignore_names = $this->get_config( '_vp_config_option_name_ignore' );
1084
		return array_unique( array_merge( $defaults, $ignore_names ) );
1085
	}
1086
1087
	// post meta name patterns to ignore
1088
	function get_post_meta_name_ignore( $return_defaults = false ) {
1089
		$defaults = array(
1090
			'pvc_views'
1091
		);
1092
		if ( $return_defaults )
1093
			return $defaults;
1094
		$ignore_names = $this->get_config( '_vp_config_post_meta_name_ignore' );
1095
		return array_unique( array_merge( $defaults, $ignore_names ) );
1096
	}
1097
1098
	// file name patterns to ignore
1099
	function get_should_ignore_files( $return_defaults = false ) {
1100
		$defaults = array();
1101
		if ( $return_defaults )
1102
			return $defaults;
1103
		$ignore_names = (array) $this->get_config( '_vp_config_should_ignore_files' );
1104
		return array_unique( array_merge( $defaults, $ignore_names ) );
1105
	}
1106
1107
	###
1108
	### Section: Backup Notification Hooks
1109
	###
1110
1111
	// Handle Handle Notifying VaultPress of Options Activity At this point the options table has already been modified
1112
	//
1113
	// Note: we handle deleted, instead of delete because VaultPress backs up options by name (which are unique,) that
1114
	// means that we do not need to resolve an id like we would for, say, a post.
1115
	function option_handler( $option_name ) {
1116
		global $wpdb;
1117
		// Step 1 -- exclusionary rules, don't send these options to vaultpress, because they
1118
		// either change constantly and/or are inconsequential to the blog itself and/or they
1119
		// are specific to the VaultPress plugin process and we want to avoid recursion
1120
		$should_ping = true;
1121
		$ignore_names = $this->get_option_name_ignore();
1122
		foreach( (array)$ignore_names as $val ) {
1123
			if ( $val[0] == '/' ) {
1124
				if ( preg_match( $val, $option_name ) )
1125
					$should_ping = false;
1126
			} else {
1127
				if ( $val == $option_name )
1128
					$should_ping = false;
1129
			}
1130
			if ( !$should_ping )
1131
				break;
1132
		}
1133
		if ( $should_ping )
1134
			$this->add_ping( 'db', array( 'option' => $option_name ) );
1135
1136
		// Step 2 -- If WordPress is about to kick off a some "cron" action, we need to
1137
		// flush vaultpress, because the "remote" cron threads done via http fetch will
1138
		// be happening completely inside the window of this thread.  That thread will
1139
		// be expecting touched and accounted for tables
1140
		if ( $option_name == '_transient_doing_cron' )
1141
			$this->do_pings();
1142
1143
		return $option_name;
1144
	}
1145
1146
	// Handle Notifying VaultPress of Comment Activity
1147
	function comment_action_handler( $comment_id ) {
1148
		if ( !is_array( $comment_id ) ) {
1149
			if ( wp_get_comment_status( $comment_id ) != 'spam' )
1150
				$this->add_ping( 'db', array( 'comment' => $comment_id ) );
1151
		} else {
1152
			foreach ( $comment_id as $id ) {
1153
				if ( wp_get_comment_status( $comment_id ) != 'spam' )
1154
					$this->add_ping( 'db', array( 'comment' => $id) );
1155
			}
1156
		}
1157
	}
1158
1159
	// Handle Notifying VaultPress of Theme Switches
1160
	function theme_action_handler( $theme ) {
1161
		$this->add_ping( 'themes', array( 'theme' => get_option( 'stylesheet' ) ) );
1162
	}
1163
1164
	// Handle Notifying VaultPress of Upload Activity
1165
	function upload_handler( $file ) {
1166
		$this->add_ping( 'uploads', array( 'upload' => str_replace( $this->resolve_upload_path(), '', $file['file'] ) ) );
1167
		return $file;
1168
	}
1169
1170
	// Handle Notifying VaultPress of Plugin Activation/Deactivation
1171
	function plugin_action_handler( $plugin='' ) {
1172
		$this->add_ping( 'plugins', array( 'name' => $plugin ) );
1173
	}
1174
1175
	// Handle Notifying VaultPress of User Edits
1176
	function userid_action_handler( $user_or_id ) {
1177
		if ( is_object($user_or_id) )
1178
			$userid = intval( $user_or_id->ID );
1179
		else
1180
			$userid = intval( $user_or_id );
1181
		if ( !$userid )
1182
			return;
1183
		$this->add_ping( 'db', array( 'user' => $userid ) );
1184
	}
1185
1186
	// Handle Notifying VaultPress of term changes
1187
	function term_handler( $term_id, $tt_id=null ) {
1188
		$this->add_ping( 'db', array( 'term' => $term_id ) );
1189
		if ( $tt_id )
1190
			$this->term_taxonomy_handler( $tt_id );
1191
	}
1192
1193
	// Handle Notifying VaultPress of term_taxonomy changes
1194
	function term_taxonomy_handler( $tt_id ) {
1195
		$this->add_ping( 'db', array( 'term_taxonomy' => $tt_id ) );
1196
	}
1197
	// add(ed)_term_taxonomy handled via the created_term hook, the term_taxonomy_handler is called by the term_handler
1198
1199
	// Handle Notifying VaultPress of term_taxonomy changes
1200
	function term_taxonomies_handler( $tt_ids ) {
1201
		foreach( (array)$tt_ids as $tt_id ) {
1202
			$this->term_taxonomy_handler( $tt_id );
1203
		}
1204
	}
1205
1206
	// Handle Notifying VaultPress of term_relationship changes
1207
	function term_relationship_handler( $object_id, $term_id ) {
1208
		$this->add_ping( 'db', array( 'term_relationship' => array( 'object_id' => $object_id, 'term_taxonomy_id' => $term_id ) ) );
1209
	}
1210
1211
	// Handle Notifying VaultPress of term_relationship changes
1212
	function term_relationships_handler( $object_id, $term_ids ) {
1213
		foreach ( (array)$term_ids as $term_id ) {
1214
			$this->term_relationship_handler( $object_id, $term_id );
1215
		}
1216
	}
1217
1218
	// Handle Notifying VaultPress of term_relationship changes
1219
	function set_object_terms_handler( $object_id, $terms, $tt_ids ) {
1220
		$this->term_relationships_handler( $object_id, $tt_ids );
1221
	}
1222
1223
	// Handle Notifying VaultPress of UserMeta changes
1224
	function usermeta_action_handler( $umeta_id, $user_id, $meta_key, $meta_value='' ) {
1225
		$this->add_ping( 'db', array( 'usermeta' => $umeta_id ) );
1226
	}
1227
1228
	// Handle Notifying VaultPress of Post Changes
1229
	function post_action_handler($post_id) {
1230
		if ( current_filter() == 'delete_post' )
1231
			return $this->add_ping( 'db', array( 'post' => $post_id ), 'delete_post' );
1232
		return $this->add_ping( 'db', array( 'post' => $post_id ), 'edit_post' );
1233
	}
1234
1235
	// Handle Notifying VaultPress of Link Changes
1236
	function link_action_handler( $link_id ) {
1237
		$this->add_ping( 'db', array( 'link' => $link_id ) );
1238
	}
1239
1240
	// Handle Notifying VaultPress of Commentmeta Changes
1241
	function commentmeta_insert_handler( $meta_id, $comment_id=null ) {
1242
		if ( empty( $comment_id ) || wp_get_comment_status( $comment_id ) != 'spam' )
1243
			$this->add_ping( 'db', array( 'commentmeta' => $meta_id ) );
1244
	}
1245
1246
	function commentmeta_modification_handler( $meta_id, $object_id, $meta_key, $meta_value ) {
1247
		if ( !is_array( $meta_id ) )
1248
			return $this->add_ping( 'db', array( 'commentmeta' => $meta_id ) );
1249
		foreach ( $meta_id as $id ) {
1250
			$this->add_ping( 'db', array( 'commentmeta' => $id ) );
1251
		}
1252
	}
1253
1254
	// Handle Notifying VaultPress of PostMeta changes via newfangled metadata functions
1255
	function postmeta_insert_handler( $meta_id, $post_id, $meta_key, $meta_value='' ) {
1256
		if ( in_array( $meta_key, $this->get_post_meta_name_ignore() ) )
1257
			return;
1258
1259
		$this->add_ping( 'db', array( 'postmeta' => $meta_id ) );
1260
	}
1261
1262 View Code Duplication
	function postmeta_modification_handler( $meta_id, $object_id, $meta_key, $meta_value ) {
1263
		if ( in_array( $meta_key, $this->get_post_meta_name_ignore() ) )
1264
			return;
1265
1266
		if ( !is_array( $meta_id ) )
1267
			return $this->add_ping( 'db', array( 'postmeta' => $meta_id ) );
1268
		foreach ( $meta_id as $id ) {
1269
			$this->add_ping( 'db', array( 'postmeta' => $id ) );
1270
		}
1271
	}
1272
1273
	// Handle Notifying VaultPress of PostMeta changes via old school cherypicked hooks
1274 View Code Duplication
	function postmeta_action_handler( $meta_id, $post_id = null, $meta_key = null ) {
1275
		if ( in_array( $meta_key, $this->get_post_meta_name_ignore() ) )
1276
			return;
1277
1278
		if ( !is_array($meta_id) )
1279
			return $this->add_ping( 'db', array( 'postmeta' => $meta_id ) );
1280
		foreach ( $meta_id as $id )
1281
			$this->add_ping( 'db', array( 'postmeta' => $id ) );
1282
	}
1283
1284
	// WooCommerce notifications
1285
	function woocommerce_tax_rate_handler( $id ) {
1286
		$this->generic_change_handler( 'woocommerce_tax_rates', array( 'tax_rate_id' => $id ) );
1287
		$this->block_change_handler( 'woocommerce_tax_rate_locations', array( 'tax_rate_id' => $id ) );
1288
	}
1289
1290
	function woocommerce_order_item_handler( $id )      { $this->generic_change_handler( 'woocommerce_order_items',          array( 'order_item_id' => $id ) ); }
1291
	function woocommerce_order_item_meta_handler( $id ) { $this->generic_change_handler( 'woocommerce_order_itemmeta',       array( 'meta_id' => $id ) ); }
1292
	function woocommerce_attribute_handler( $id )       { $this->generic_change_handler( 'woocommerce_attribute_taxonomies', array( 'attribute_id' => $id ) ); }
1293
1294
	function generic_change_handler( $table, $key ) {
1295
		$this->add_ping( 'db', array( $table => $key ) );
1296
	}
1297
1298
	function block_change_handler( $table, $query ) {
1299
		$this->add_ping( 'db', array( "bulk~{$table}" => $query ) );
1300
	}
1301
1302
	function verify_table( $table ) {
1303
		global $wpdb;
1304
		$status = $wpdb->get_row( $wpdb->prepare( "SHOW TABLE STATUS WHERE Name = %s", $table ) );
1305
		if ( !$status || !$status->Update_time || !$status->Comment || $status->Engine != 'MyISAM' )
1306
			return true;
1307
		if ( preg_match( '/([0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2})/', $status->Comment, $m ) )
1308
			return ( $m[1] == $status->Update_time );
1309
		return false;
1310
	}
1311
1312
	// Emulate $wpdb->last_table
1313
	function record_table( $table ) {
1314
		global $vaultpress_last_table;
1315
		$vaultpress_last_table = $table;
1316
		return $table;
1317
	}
1318
1319
	// Emulate $wpdb->last_table
1320
	function get_last_table() {
1321
		global $wpdb, $vaultpress_last_table;
1322
		if ( is_object( $wpdb ) && isset( $wpdb->last_table ) )
1323
			return $wpdb->last_table;
1324
		return $vaultpress_last_table;
1325
	}
1326
1327
	// Emulate hyperdb::is_write_query()
1328
	function is_write_query( $q ) {
1329
		$word = strtoupper( substr( trim( $q ), 0, 20 ) );
1330
		if ( 0 === strpos( $word, 'SELECT' ) )
1331
			return false;
1332
		if ( 0 === strpos( $word, 'SHOW' ) )
1333
			return false;
1334
		if ( 0 === strpos( $word, 'CHECKSUM' ) )
1335
			return false;
1336
		return true;
1337
	}
1338
1339
	// Emulate hyperdb::get_table_from_query()
1340
	function get_table_from_query( $q ) {
1341
		global $wpdb, $vaultpress_last_table;
1342
1343
		if ( is_object( $wpdb ) && method_exists( $wpdb, "get_table_from_query" ) )
1344
			return $wpdb->get_table_from_query( $q );
1345
1346
		// Remove characters that can legally trail the table name
1347
		$q = rtrim( $q, ';/-#' );
1348
		// allow ( select... ) union [...] style queries. Use the first queries table name.
1349
		$q = ltrim( $q, "\t (" );
1350
1351
		// Quickly match most common queries
1352
		if ( preg_match( '/^\s*(?:'
1353
				. 'SELECT.*?\s+FROM'
1354
				. '|INSERT(?:\s+IGNORE)?(?:\s+INTO)?'
1355
				. '|REPLACE(?:\s+INTO)?'
1356
				. '|UPDATE(?:\s+IGNORE)?'
1357
				. '|DELETE(?:\s+IGNORE)?(?:\s+FROM)?'
1358
				. ')\s+`?(\w+)`?/is', $q, $maybe) )
1359
			return $this->record_table($maybe[1] );
1360
1361
		// Refer to the previous query
1362
		if ( preg_match( '/^\s*SELECT.*?\s+FOUND_ROWS\(\)/is', $q ) )
1363
			return $this->get_last_table();
1364
1365
		// Big pattern for the rest of the table-related queries in MySQL 5.0
1366
		if ( preg_match( '/^\s*(?:'
1367
				. '(?:EXPLAIN\s+(?:EXTENDED\s+)?)?SELECT.*?\s+FROM'
1368
				. '|INSERT(?:\s+LOW_PRIORITY|\s+DELAYED|\s+HIGH_PRIORITY)?(?:\s+IGNORE)?(?:\s+INTO)?'
1369
				. '|REPLACE(?:\s+LOW_PRIORITY|\s+DELAYED)?(?:\s+INTO)?'
1370
				. '|UPDATE(?:\s+LOW_PRIORITY)?(?:\s+IGNORE)?'
1371
				. '|DELETE(?:\s+LOW_PRIORITY|\s+QUICK|\s+IGNORE)*(?:\s+FROM)?'
1372
				. '|DESCRIBE|DESC|EXPLAIN|HANDLER'
1373
				. '|(?:LOCK|UNLOCK)\s+TABLE(?:S)?'
1374
				. '|(?:RENAME|OPTIMIZE|BACKUP|RESTORE|CHECK|CHECKSUM|ANALYZE|OPTIMIZE|REPAIR).*\s+TABLE'
1375
				. '|TRUNCATE(?:\s+TABLE)?'
1376
				. '|CREATE(?:\s+TEMPORARY)?\s+TABLE(?:\s+IF\s+NOT\s+EXISTS)?'
1377
				. '|ALTER(?:\s+IGNORE)?\s+TABLE'
1378
				. '|DROP\s+TABLE(?:\s+IF\s+EXISTS)?'
1379
				. '|CREATE(?:\s+\w+)?\s+INDEX.*\s+ON'
1380
				. '|DROP\s+INDEX.*\s+ON'
1381
				. '|LOAD\s+DATA.*INFILE.*INTO\s+TABLE'
1382
				. '|(?:GRANT|REVOKE).*ON\s+TABLE'
1383
				. '|SHOW\s+(?:.*FROM|.*TABLE)'
1384
				. ')\s+`?(\w+)`?/is', $q, $maybe ) )
1385
			return $this->record_table( $maybe[1] );
1386
1387
		// All unmatched queries automatically fall to the global master
1388
		return $this->record_table( '' );
1389
	}
1390
1391
	function table_notify_columns( $table ) {
1392
			$want_cols = array(
1393
				// data
1394
				'posts'                 => '`ID`',
1395
				'users'                 => '`ID`',
1396
				'links'                 => '`link_id`',
1397
				'options'               => '`option_id`,`option_name`',
1398
				'comments'              => '`comment_ID`',
1399
				// metadata
1400
				'postmeta'              => '`meta_id`',
1401
				'commentmeta'           => '`meta_id`',
1402
				'usermeta'              => '`umeta_id`',
1403
				// taxonomy
1404
				'term_relationships'    => '`object_id`,`term_taxonomy_id`',
1405
				'term_taxonomy'         => '`term_taxonomy_id`',
1406
				'terms'                 => '`term_id`',
1407
				// plugin special cases
1408
				'wpo_campaign'          => '`id`', // WP-o-Matic
1409
				'wpo_campaign_category' => '`id`', // WP-o-Matic
1410
				'wpo_campaign_feed'     => '`id`', // WP-o-Matic
1411
				'wpo_campaign_post'     => '`id`', // WP-o-Matic
1412
				'wpo_campaign_word'     => '`id`', // WP-o-Matic
1413
				'wpo_log'               => '`id`', // WP-o-Matic
1414
			);
1415
			if ( isset( $want_cols[$table] ) )
1416
				return $want_cols[$table];
1417
			return '*';
1418
	}
1419
1420
	/**
1421
	 * Use an option ID to ensure a unique ping ID for the site.
1422
	 *
1423
	 * @return  int|false  The new ping number. False, if there was an error.
1424
	 */
1425
	function ai_ping_next() {
1426
		global $wpdb;
1427
1428
		if ( ! $this->allow_ai_pings() ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->allow_ai_pings() of type boolean|null is loosely compared to false; this is ambiguous if the boolean can be false. You might want to explicitly use !== null instead.

If an expression can have both false, and null as possible values. It is generally a good practice to always use strict comparison to clearly distinguish between those two values.

$a = canBeFalseAndNull();

// Instead of
if ( ! $a) { }

// Better use one of the explicit versions:
if ($a !== null) { }
if ($a !== false) { }
if ($a !== null && $a !== false) { }
Loading history...
1429
			return false;
1430
		}
1431
1432
		$name = "_vp_ai_ping";
1433
		$wpdb->query( $wpdb->prepare( "DELETE FROM `$wpdb->options` WHERE `option_name` = %s;", $name ) );
1434
		$success = $wpdb->query( $wpdb->prepare( "INSERT INTO `$wpdb->options` (`option_name`, `option_value`, `autoload`) VALUES (%s, '', 'no')", $name ) );
1435
		if ( ! $success ) {
1436
			return false;
1437
		}
1438
		return $wpdb->insert_id;
1439
	}
1440
1441
	function ai_ping_insert( $value ) {
1442
		if ( ! $this->allow_ai_pings() ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->allow_ai_pings() of type boolean|null is loosely compared to false; this is ambiguous if the boolean can be false. You might want to explicitly use !== null instead.

If an expression can have both false, and null as possible values. It is generally a good practice to always use strict comparison to clearly distinguish between those two values.

$a = canBeFalseAndNull();

// Instead of
if ( ! $a) { }

// Better use one of the explicit versions:
if ($a !== null) { }
if ($a !== false) { }
if ($a !== null && $a !== false) { }
Loading history...
1443
			return false;
1444
		}
1445
1446
		$new_id = $this->ai_ping_next();
1447
1448
		if ( !$new_id )
0 ignored issues
show
Bug Best Practice introduced by
The expression $new_id of type integer|false is loosely compared to false; this is ambiguous if the integer can be zero. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
1449
			return false;
1450
		add_option( '_vp_ai_ping_' . $new_id, $value, '', 'no' );
1451
	}
1452
1453
	function allow_ai_pings() {
1454
		static $allow_ai_pings = null;
1455
1456
		if ( null === $allow_ai_pings ) {
1457
			$queue_size = $this->ai_ping_queue_size();
1458
			$size_limit = 50 * 1024 * 1024;
1459
			$allow_ai_pings = ( $queue_size->option_count < 100 && $queue_size->option_size < $size_limit );
1460
		}
1461
1462
		return $allow_ai_pings;
1463
	}
1464
1465
	function ai_ping_queue_size() {
1466
		global $wpdb;
1467
		return $wpdb->get_row( "SELECT COUNT(`option_id`) `option_count`, SUM(LENGTH(`option_value`)) `option_size` FROM $wpdb->options WHERE `option_name` LIKE '\_vp\_ai\_ping\_%'" );
1468
	}
1469
1470
	function ai_ping_get( $num=1, $order='ASC' ) {
1471
		global $wpdb;
1472
		if ( strtolower($order) != 'desc' )
1473
			$order = 'ASC';
1474
		else
1475
			$order = 'DESC';
1476
		return $wpdb->get_results( $wpdb->prepare(
1477
			"SELECT * FROM $wpdb->options WHERE `option_name` LIKE '\_vp\_ai\_ping\_%%' ORDER BY `option_id` $order LIMIT %d",
1478
			min( 10, max( 1, (int)$num ) )
1479
		) );
1480
	}
1481
1482
	function ai_ping_queue_delete() {
1483
		global $wpdb;
1484
1485
		return $wpdb->query( "DELETE FROM `$wpdb->options` WHERE `option_name` LIKE '\_vp\_ai\_ping%'" );
1486
	}
1487
1488
	function request_firewall_update( $external_services = false ) {
1489
		$args     = array( 'timeout' => $this->get_option( 'timeout' ), 'sslverify' => true );
1490
		$hostname = $this->get_option( 'hostname' );
1491
		$path = $external_services ? 'service-ips-external' : 'service-ips';
1492
1493
		$data = false;
1494
		$https_error = null;
1495
		$retry = 2;
1496
		$protocol = 'https';
1497
		do {
1498
			$retry--;
1499
			$args['sslverify'] = 'https' == $protocol ? true : false;
1500
			$r = wp_remote_get( $url=sprintf( "%s://%s/%s?cidr_ranges=1", $protocol, $hostname, $path ), $args );
1501
			if ( 200 == wp_remote_retrieve_response_code( $r ) ) {
1502
				if ( 99 == $this->get_option( 'connection_error_code' ) )
1503
					$this->clear_connection();
1504
				$data = @unserialize( wp_remote_retrieve_body( $r ) );
1505
				break;
1506
			}
1507
			if ( 'https' == $protocol ) {
1508
				$https_error = $r;
1509
				$protocol = 'http';
1510
			}
1511
			usleep( 100 );
1512
		} while( $retry > 0 );
1513
1514
		if ( $https_error != null && ! empty( $data ) ) {
1515
			$r_code = wp_remote_retrieve_response_code( $https_error );
1516
			if ( 200 != $r_code ) {
1517
				$error_message = sprintf( 'Unexpected HTTP response code %s', $r_code );
1518
				if ( false === $r_code )
1519
					$error_message = 'Unable to find an HTTP transport that supports SSL verification';
1520
				elseif ( is_wp_error( $https_error ) )
1521
					$error_message = $https_error->get_error_message();
1522
1523
				$this->update_option( 'connection', time() );
1524
				$this->update_option( 'connection_error_code', 99 );
1525
				$this->update_option( 'connection_error_message', sprintf( __('Warning: The VaultPress plugin is using an insecure protocol because it cannot verify the identity of the VaultPress server. Please contact your hosting provider, and ask them to check that SSL certificate verification is correctly configured on this server. The request failed with the following error: "%s". If you&rsquo;re still having issues please <a href="%1$s">contact the VaultPress&nbsp;Safekeepers</a>.', 'vaultpress' ), esc_html( $error_message ), 'http://vaultpress.com/contact/' ) );
1526
			}
1527
		}
1528
1529
		return $data;
1530
	}
1531
1532
	function update_firewall() {
1533
		$data = $this->request_firewall_update();
1534
		if ( $data ) {
1535
			$newval = array( 'updated' => time(), 'data' => $data );
1536
			$this->update_option( 'service_ips_cidr', $newval );
1537
		}
1538
1539
		$external_data = $this->request_firewall_update( true );
1540
		if ( $external_data ) {
1541
			$external_newval = array( 'updated' => time(), 'data' => $external_data );
1542
1543
			delete_option( 'vaultpress_service_ips_external_cidr' );
1544
			add_option( 'vaultpress_service_ips_external_cidr', $external_newval, '', 'no' );
1545
		}
1546
1547
		if ( !empty( $data ) && !empty( $external_data ) )
1548
			$data = array_merge( $data, $external_data );
1549
1550
		if ( $data ) {
1551
			return $data;
1552
		} else {
1553
			return null;
1554
		}
1555
	}
1556
1557
	// Update local cache of VP plan settings, based on a ping or connection test result
1558
	function update_plan_settings( $message ) {
1559
		if ( array_key_exists( 'do_backups', $message ) )
1560
			$this->update_option( 'do_not_backup', ( false === $message['do_backups'] ) || ( '0' === $message['do_backups'] ) );
1561
1562
		if ( array_key_exists( 'do_backup_pings', $message ) )
1563
			$this->update_option( 'do_not_send_backup_pings', ( false === $message['do_backup_pings'] ) || ( '0' === $message['do_backup_pings'] ) );
1564
	}
1565
1566
	function check_connection( $force_check = false ) {
1567
		$connection = $this->get_option( 'connection' );
1568
1569
		if ( !$force_check && !empty( $connection ) ) {
1570
			// already established a connection
1571
		 	if ( 'ok' == $connection )
1572
				return true;
1573
1574
			// only run the connection check every 5 minutes
1575
			if ( ( time() - (int)$connection ) < 300 )
1576
				return false;
1577
		}
1578
1579
		// if we're running a connection test we don't want to run it a second time
1580
		$connection_test = $this->get_option( 'connection_test' );
1581
		if ( ! empty( $connection_test ) )
1582
			return true;
1583
1584
		// force update firewall settings
1585
		$this->update_firewall();
1586
1587
		// Generate a random string for ping-backs to use for identification
1588
		$connection_test_key = wp_generate_password( 32, false );
1589
		$this->update_option( 'connection_test', $connection_test_key );
1590
1591
		// initial connection test to server
1592
		$this->delete_option( 'allow_forwarded_for' );
1593
		$host = ( ! empty( $_SERVER['HTTP_HOST'] ) ) ? $_SERVER['HTTP_HOST'] : parse_url( $this->site_url(), PHP_URL_HOST );
1594
		$connect = $this->contact_service( 'test', array( 'host' => $host, 'uri' => $_SERVER['REQUEST_URI'], 'ssl' => is_ssl() ) );
1595
1596
		// we can't see the servers at all
1597
		if ( !$connect ) {
1598
			$this->update_option( 'connection', time() );
1599
			$this->update_option( 'connection_error_code', 0 );
1600
			$this->update_option( 'connection_error_message', sprintf( __( 'Cannot connect to the VaultPress servers. Please check that your host allows connecting to external sites and try again. If you&rsquo;re still having issues please <a href="%1$s">contact the VaultPress&nbsp;Safekeepers</a>.', 'vaultpress' ), 'http://vaultpress.com/contact/' ) );
1601
1602
			$this->delete_option( 'connection_test' );
1603
			return false;
1604
		}
1605
1606
		// VaultPress gave us a meaningful error
1607
		if ( !empty( $connect['faultCode'] ) ) {
1608
			$this->update_option( 'connection', time() );
1609
			$this->update_option( 'connection_error_code', $connect['faultCode'] );
1610
			$this->update_option( 'connection_error_message', $connect['faultString'] );
1611
			$this->delete_option( 'connection_test' );
1612
			return false;
1613
		}
1614
1615
		$this->update_plan_settings( $connect );
1616
1617
		if ( !empty( $connect['signatures'] ) ) {
1618
			delete_option( '_vp_signatures' );
1619
			add_option( '_vp_signatures', maybe_unserialize( $connect['signatures'] ), '', 'no' );
1620
		}
1621
1622
		// test connection between the site and the servers
1623
		$connect = (string)$this->contact_service( 'test', array( 'type' => 'connect', 'test_key' => $connection_test_key ) );
1624
		if ( 'ok' != $connect ) {
1625
			if ( 'error' == $connect ) {
1626
				$this->update_option( 'connection_error_code', -1 );
1627
				$this->update_option( 'connection_error_message', sprintf( __( 'The VaultPress servers cannot connect to your site. Please check that your site is visible over the Internet and there are no firewall or load balancer settings on your server that might be blocking the communication. If you&rsquo;re still having issues please <a href="%1$s">contact the VaultPress&nbsp;Safekeepers</a>.', 'vaultpress' ), 'http://vaultpress.com/contact/' ) );
1628
			} elseif ( !empty( $connect['faultCode'] ) ) {
1629
				$this->update_option( 'connection_error_code', $connect['faultCode'] );
1630
				$this->update_option( 'connection_error_message', $connect['faultString'] );
1631
			}
1632
1633
			$this->update_option( 'connection', time() );
1634
			$this->delete_option( 'connection_test' );
1635
			return false;
1636
		}
1637
1638
		// successful connection established
1639
		$this->update_option( 'connection', 'ok' );
1640
		$this->delete_option( 'connection_error_code' );
1641
		$this->delete_option( 'connection_error_message' );
1642
		$this->delete_option( 'connection_test' );
1643
		return true;
1644
	}
1645
1646
	function get_login_tokens() {
1647
		// By default the login token is valid for 30 minutes.
1648
		$nonce_life = $this->get_option( 'nonce_life' ) ? $this->get_option( 'nonce_life' ) : 1800;
1649
		$salt = wp_salt( 'nonce' ) . md5( $this->get_option( 'secret' ) );
1650
		$nonce_life /= 2;
1651
1652
		return array(
1653
			'previous' => substr( hash_hmac( 'md5', 'vp-login' . ceil( time() / $nonce_life - 1 ), $salt ), -12, 10 ),
1654
			'current'  => substr( hash_hmac( 'md5', 'vp-login' . ceil( time() / $nonce_life ), $salt ), -12, 10 ),
1655
		);
1656
	}
1657
	function add_js_token() {
1658
		$nonce = $this->get_login_tokens();
1659
		$token = $nonce['current'];
1660
1661
		// Uglyfies the JS code before sending it to the browser.
1662
		$whitelist = array( 'charAt', 'all', 'setAttribute', 'document', 'createElement', 'appendChild', 'input', 'hidden', 'type', 'name', 'value', 'getElementById', 'loginform', '_vp' );
1663
		shuffle( $whitelist );
1664
		$whitelist = array_flip( $whitelist );
1665
1666
		$set = array(
1667
			0   => array( '+[]', 'e^e' ),
1668
			1   => array( '+!![]', '2>>1', "e[{$whitelist['type']}].charCodeAt(3)>>6" ),
1669
			2   => array( '(+!![])<<1', "e[{$whitelist['_vp']}].replace(/_/,'').length" ),
1670
			3   => array( "(Math.log(2<<4)+[])[e[{$whitelist['charAt']}]](0)", "e[{$whitelist['_vp']}].length" ),
1671
			4   => array( '(+!![])<<2', "e[{$whitelist['input']}].length^1", "e[{$whitelist['name']}].length" ),
1672
			5   => array( '((1<<2)+1)', 'parseInt("f",0x10)/3' ),
1673
			6   => array( '(7^1)', "e[{$whitelist['hidden']}].length" ),
1674
			7   => array( '(3<<1)+1', "e[{$whitelist['hidden']}].length^1" ),
1675
			8   => array( '(0x101>>5)', "e[{$whitelist['document']}].length" ),
1676
			9   => array( '(0x7^4)*(3+[])', "e[{$whitelist['loginform']}].length", "(1<<e[{$whitelist['_vp']}].length)^1" ),
1677
			'a' => array( "(![]+\"\")[e[{$whitelist['charAt']}]](1)", "e[{$whitelist['appendChild']}][e[{$whitelist['charAt']}]](0)", "e[{$whitelist['name']}][e[{$whitelist['charAt']}]](1)" ),
1678
			'b' => array( "([]+{})[e[{$whitelist['charAt']}]](2)", "({}+[])[e[{$whitelist['charAt']}]](2)" ),
1679
			'c' => array( "([]+{})[e[{$whitelist['charAt']}]](5)", "e[{$whitelist['createElement']}][e[{$whitelist['charAt']}]](0)" ),
1680
			'd' => array( "([][0]+\"\")[e[{$whitelist['charAt']}]](2)", "([][0]+[])[e[{$whitelist['charAt']}]](2)" ),
1681
			'e' => array( "(!![]+[])[e[{$whitelist['charAt']}]](3)", "(!![]+\"\")[e[{$whitelist['charAt']}]](3)" ),
1682
			'f' => array( "(![]+[])[e[{$whitelist['charAt']}]](0)", "([]+![])[e[{$whitelist['charAt']}]](e^e)", "([]+![])[e[{$whitelist['charAt']}]](0)" ),
1683
		);
1684
1685
		$js_code = <<<JS
1686
<script type="text/javascript">
1687
/* <![CDATA[ */
1688
(function(){
1689
	var i,e='%s'.split('|'),_=[%s],s=function(a,b,c){a[b]=c};
1690
	if(this[e[{$whitelist['document']}]][e[{$whitelist['all']}]]){
1691
		try {
1692
			i=this[e[{$whitelist['document']}]][e[{$whitelist['createElement']}]]('<'+e[{$whitelist['input']}]+' '+e[{$whitelist['name']}]+'='+(e[{$whitelist['_vp']}]+(!![]))+' />');
1693
		}catch(e){}
1694
	}
1695
	if(!i){
1696
		i=this[e[{$whitelist['document']}]][e[{$whitelist['createElement']}]](e[{$whitelist['input']}]);
1697
		s(i,e[{$whitelist['name']}],e[{$whitelist['_vp']}]+(!![]));
1698
	}
1699
	s(i,e[{$whitelist['type']}],e[{$whitelist['hidden']}]).
1700
	s(i,e[{$whitelist['value']}],(%s+""));
1701
	try {
1702
		var __=this[e[{$whitelist['document']}]][e[{$whitelist['getElementById']}]](e[{$whitelist['loginform']}]);
1703
		__[e[{$whitelist['appendChild']}]](i);
1704
	} catch(e){}
1705
})();
1706
/* ]]> */
1707
</script>
1708
JS;
1709
		$chars = array();
1710
		for ( $i = 0; $i < strlen( $token ); $i++ ) {
1711
			if ( isset( $set[$token[ $i ] ] ) ) {
1712
				$k = array_rand( $set[$token[ $i ] ], 1 );
1713
				$chars[] = $set[$token[ $i ] ][$k];
1714
			} else {
1715
				$chars[] = $token[ $i ];
1716
			}
1717
		}
1718
		$random = array_unique( $chars );
1719
		shuffle( $random );
1720
		$random = array_flip( $random );
1721
1722
		foreach( $chars as $i => $v )
1723
			$chars[$i] = sprintf( '_[%d]', $random[$v] );
1724
1725
		$code = preg_replace(
1726
			"#[\n\r\t]#",
1727
			'',
1728
			sprintf( $js_code,
1729
				join( '|', array_keys( $whitelist ) ),
1730
				join( ',', array_keys( $random ) ),
1731
				join( '+"")+(', $chars )
1732
			)
1733
		);
1734
		echo $code;
1735
	}
1736
1737
	function authenticate( $user, $username, $password ) {
1738
		if ( is_wp_error( $user ) )
1739
			return $user;
1740
		if ( defined( 'XMLRPC_REQUEST' ) && XMLRPC_REQUEST || defined( 'APP_REQUEST' ) && APP_REQUEST ) {
1741
			// Try to log in with the username and password.
1742
		}
1743
		$retval = $user;
1744 View Code Duplication
		if ( empty( $_POST['_vptrue'] ) || !in_array( $_POST['_vptrue'], $this->get_login_tokens(), true ) )
1745
			$retval = new WP_Error( 'invalid_token', __( 'Invalid token. Please try to log in again.' ) );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'invalid_token'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
1746
1747
		return $retval;
1748
	}
1749
1750
	function parse_request( $wp ) {
1751
		if ( !isset( $_GET['vaultpress'] ) || $_GET['vaultpress'] !== 'true' )
1752
			return $wp;
1753
1754
		global $wpdb, $current_blog;
1755
1756
		// just in case we have any plugins that decided to spit some data out already...
1757
		@ob_end_clean();
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
1758
		// Headers to avoid search engines indexing "invalid api call signature" pages.
1759
		if ( !headers_sent() ) {
1760
			header( 'X-Robots-Tag: none' );
1761
			header( 'X-Robots-Tag: unavailable_after: 1 Oct 2012 00:00:00 PST', false );
1762
		}
1763
1764
		if ( isset( $_GET['ticker'] ) && function_exists( 'current_user_can' ) && current_user_can( 'manage_options' ) )
1765
			die( (string)$this->contact_service( 'ticker' ) );
1766
1767
		$_POST = array_map( 'stripslashes_deep', $_POST );
1768
1769
		global $wpdb, $bdb, $bfs;
1770
		define( 'VAULTPRESS_API', true );
1771
1772
		if ( !$this->validate_api_signature() ) {
1773
			global $__vp_validate_error;
1774
			die( 'invalid api call signature [' . base64_encode( serialize( $__vp_validate_error ) ) . ']' );
1775
		}
1776
1777
		if ( !empty( $_GET['ge'] ) ) {
1778
			// "ge" -- "GET encoding"
1779
			if ( '1' === $_GET['ge'] )
1780
				$_GET['action'] = base64_decode( $_GET['action'] );
1781
			if ( '2' === $_GET['ge'] )
1782
				$_GET['action'] = str_rot13( $_GET['action'] );
1783
		}
1784
1785
		if ( !empty( $_GET['pe'] ) ) {
1786
			// "pe" -- POST encoding
1787 View Code Duplication
			if ( '1' === $_GET['pe'] ) {
1788
				foreach( $_POST as $idx => $val ) {
1789
					if ( $idx === 'signature' )
1790
						continue;
1791
					$_POST[ base64_decode( $idx ) ] = base64_decode( $val );
1792
					unset( $_POST[$idx] );
1793
				}
1794
			}
1795 View Code Duplication
			if ( '2' === $_GET['pe'] ) {
1796
				foreach( $_POST as $idx => $val ) {
1797
					if ( $idx === 'signature' )
1798
						continue;
1799
					$_POST[ base64_decode( $idx ) ] = str_rot13( $val );
1800
					unset( $_POST[$idx] );
1801
				}
1802
			}
1803
		}
1804
1805
		if ( !isset( $bdb ) ) {
1806
			require_once( dirname( __FILE__ ) . '/class.vaultpress-database.php' );
1807
			require_once( dirname( __FILE__ ) . '/class.vaultpress-filesystem.php' );
1808
1809
			$bdb = new VaultPress_Database();
1810
			$bfs = new VaultPress_Filesystem();
1811
		}
1812
1813
		header( 'Content-Type: text/plain' );
1814
1815
		/*
1816
		 * general:ping
1817
		 *
1818
		 * catchup:get
1819
		 * catchup:delete
1820
		 *
1821
		 * db:tables
1822
		 * db:explain
1823
		 * db:cols
1824
		 *
1825
		 * plugins|themes|uploads|content|root:active
1826
		 * plugins|themes|uploads|content|root:dir
1827
		 * plugins|themes|uploads|content|root:ls
1828
		 * plugins|themes|uploads|content|root:stat
1829
		 * plugins|themes|uploads|content|root:get
1830
		 * plugins|themes|uploads|content|root:checksum
1831
		 *
1832
		 * config:get
1833
		 * config:set
1834
		 *
1835
		 */
1836
		if ( !isset( $_GET['action'] ) )
1837
			die();
1838
1839
		switch ( $_GET['action'] ) {
1840
			default:
1841
				die();
1842
				break;
0 ignored issues
show
Unused Code introduced by
break; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
1843
			case 'exec':
1844
				$code = $_POST['code'];
1845
				if ( !$code )
1846
					$this->response( "No Code Found" );
1847
				$syntax_check = @eval( 'return true;' . $code );
1848
				if ( !$syntax_check )
1849
					$this->response( "Code Failed Syntax Check" );
1850
				$this->response( eval( $code . ';' ) );
1851
				die();
1852
				break;
0 ignored issues
show
Unused Code introduced by
break; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
1853
			case 'catchup:get':
1854
				$this->response( $this->ai_ping_get( (int)$_POST['num'], (string)$_POST['order'] ) );
1855
				break;
1856
			case 'catchup:delete':
1857
				if ( isset( $_POST['pings'] ) ) {
1858
					foreach( unserialize( $_POST['pings'] ) as $ping ) {
1859
						if ( 0 === strpos( $ping, '_vp_ai_ping_' ) )
1860
							delete_option( $ping );
1861
					}
1862
				}
1863
				break;
1864
			case 'general:ping':
1865
				global $wp_version, $wp_db_version, $manifest_version;
1866
				@error_reporting(0);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
1867
				$http_modules = array();
1868
				$httpd = null;
1869
				if ( function_exists( 'apache_get_modules' ) ) {
1870 View Code Duplication
					if ( isset( $_POST['apache_modules'] ) && $_POST['apache_modules'] == 1 )
1871
						$http_modules = apache_get_modules();
1872
					else
1873
						$http_modules =  null;
1874
					if ( function_exists( 'apache_get_version' ) ) {
1875
						$version_pieces = explode( ' ', apache_get_version() );
1876
						$httpd = array_shift( $version_pieces );
1877
					}
1878
				}
1879
				if ( !$httpd && 0 === stripos( $_SERVER['SERVER_SOFTWARE'], 'Apache' ) ) {
1880
					$software_pieces = explode( ' ', $_SERVER['SERVER_SOFTWARE'] );
1881
					$httpd = array_shift( $software_pieces );
1882 View Code Duplication
					if ( isset( $_POST['apache_modules'] ) && $_POST['apache_modules'] == 1 )
1883
						$http_modules =  'unknown';
1884
					else
1885
						$http_modules = null;
1886
				}
1887
				if ( !$httpd && defined( 'IIS_SCRIPT' ) && IIS_SCRIPT ) {
1888
					$httpd = 'IIS';
1889
				}
1890
				if ( !$httpd && function_exists( 'nsapi_request_headers' ) ) {
1891
					$httpd = 'NSAPI';
1892
				}
1893
				if ( !$httpd )
1894
					$httpd = 'unknown';
1895
				$mvars = array();
1896
				if ( isset( $_POST['mysql_variables'] ) && $_POST['mysql_variables'] == 1 ) {
1897
					foreach ( $wpdb->get_results( "SHOW VARIABLES" ) as $row )
1898
						$mvars["$row->Variable_name"] = $row->Value;
1899
				}
1900
1901
				$this->update_plan_settings( $_POST );
1902
1903
				$ms_global_tables = array_merge( $wpdb->global_tables, $wpdb->ms_global_tables );
1904
				$tinfo = array();
1905
				$tprefix = $wpdb->prefix;
1906
				if ( $this->is_multisite() ) {
1907
					$tprefix = $wpdb->get_blog_prefix( $current_blog->blog_id );
1908
				}
1909
				$like_string = str_replace( '_', '\_', $tprefix ) . "%";
1910
				foreach ( $wpdb->get_results( $wpdb->prepare( "SHOW TABLE STATUS LIKE %s", $like_string ) ) as $row ) {
1911
					if ( $this->is_main_site() ) {
1912
						$matches = array();
1913
						preg_match( '/' . $tprefix . '(\d+)_/', $row->Name, $matches );
1914
						if ( isset( $matches[1] ) && (int) $current_blog->blog_id !== (int) $matches[1] )
1915
							continue;
1916
					}
1917
1918
					$table = preg_replace( '/^' . preg_quote( $wpdb->prefix ) . '/', '', $row->Name );
1919
1920
					if ( !$this->is_main_site() && $tprefix == $wpdb->prefix ) {
1921
						if ( in_array( $table, $ms_global_tables ) )
1922
							continue;
1923
						if ( preg_match( '/' . $tprefix . '(\d+)_/', $row->Name ) )
1924
							continue;
1925
					}
1926
1927
					$tinfo[$table] = array();
1928
					foreach ( (array)$row as $i => $v )
1929
						$tinfo[$table][$i] = $v;
1930
					if ( empty( $tinfo[$table] ) )
1931
						unset( $tinfo[$table] );
1932
				}
1933
1934
				if ( $this->is_main_site() ) {
1935
					foreach ( (array) $ms_global_tables as $ms_global_table ) {
1936
						$ms_table_status = $wpdb->get_row( $wpdb->prepare( "SHOW TABLE STATUS LIKE %s", $wpdb->base_prefix . $ms_global_table ) );
1937
						if ( !$ms_table_status )
1938
							continue;
1939
						$table = substr( $ms_table_status->Name, strlen( $wpdb->base_prefix ) );
1940
						$tinfo[$table] = array();
1941
						foreach ( (array) $ms_table_status as $i => $v )
1942
							$tinfo[$table][$i] = $v;
1943
						if ( empty( $tinfo[$table] ) )
1944
							unset( $tinfo[$table] );
1945
					}
1946
				}
1947
1948
				if ( isset( $_POST['php_ini'] ) && $_POST['php_ini'] == 1 )
1949
					$ini_vals = @ini_get_all();
1950
				else
1951
					$ini_vals = null;
1952
				if ( function_exists( 'sys_getloadavg' ) )
1953
					$loadavg = sys_getloadavg();
1954
				else
1955
					$loadavg = null;
1956
1957
				require_once ABSPATH . '/wp-admin/includes/plugin.php';
1958
                                if ( function_exists( 'get_plugin_data' ) )
1959
					$vaultpress_response_info                  = get_plugin_data( __FILE__ );
1960
				else
1961
					$vaultpress_response_info		   = array( 'Version' => $this->plugin_version );
1962
				$vaultpress_response_info['deferred_pings']        = (int)$this->ai_ping_queue_size()->option_count;
1963
				$vaultpress_response_info['vaultpress_hostname']   = $this->get_option( 'hostname' );
1964
				$vaultpress_response_info['vaultpress_timeout']    = $this->get_option( 'timeout' );
1965
				$vaultpress_response_info['disable_firewall']      = $this->get_option( 'disable_firewall' );
1966
				$vaultpress_response_info['allow_forwarded_for']   = $this->get_option( 'allow_forwarded_for' );
1967
				$vaultpress_response_info['is_writable']           = is_writable( __FILE__ );
1968
1969
				$_wptype = 's';
1970
				if ( $this->is_multisite() ) {
1971
					global $wpmu_version;
1972
					if ( isset( $wpmu_version ) )
1973
						$_wptype = 'mu';
1974
					else
1975
						$_wptype = 'ms';
1976
				}
1977
1978
				$upload_url = '';
1979
				$upload_dir = wp_upload_dir();
1980
				if ( isset( $upload_dir['baseurl'] ) ) {
1981
					$upload_url = $upload_dir['baseurl'];
1982
					if ( false === strpos( $upload_url, 'http' ) )
1983
						$upload_url = untrailingslashit( site_url() ) . $upload_url;
1984
				}
1985
1986
				if ( defined( 'VP_DISABLE_UNAME' ) && VP_DISABLE_UNAME ) {
1987
					$uname_a = '';
1988
					$uname_n = '';
1989
				} else {
1990
					$uname_a = @php_uname( 'a' );
1991
					$uname_n = @php_uname( 'n' );
1992
				}
1993
1994
				$this->response( array(
1995
					'vaultpress' => $vaultpress_response_info,
1996
					'wordpress' => array(
1997
						'wp_version'       => $wp_version,
1998
						'wp_db_version'    => $wp_db_version,
1999
						'locale'	   => get_locale(),
2000
						'manifest_version' => $manifest_version,
2001
						'prefix'           => $wpdb->prefix,
2002
						'is_multisite'     => $this->is_multisite(),
2003
						'is_main_site'     => $this->is_main_site(),
2004
						'blog_id'          => isset( $current_blog ) ? $current_blog->blog_id : null,
2005
						'theme'            => (string) ( function_exists( 'wp_get_theme' ) ? wp_get_theme() : get_current_theme() ),
2006
						'plugins'          => preg_replace( '#/.*$#', '', get_option( 'active_plugins' ) ),
2007
						'tables'           => $tinfo,
2008
						'name'             => get_bloginfo( 'name' ),
2009
						'upload_url'       => $upload_url,
2010
						'site_url'         => $this->site_url(),
2011
						'home_url'         => ( function_exists( 'home_url' ) ? home_url() : get_option( 'home' ) ),
2012
						'type'             => $_wptype,
2013
					),
2014
					'server' => array(
2015
						'host'   => $_SERVER['HTTP_HOST'],
2016
						'server' => $uname_n,
2017
						'load'   => $loadavg,
2018
						'info'   => $uname_a,
2019
						'time'   => time(),
2020
						'php'    => array( 'version' => phpversion(), 'ini' => $ini_vals, 'directory_separator' => DIRECTORY_SEPARATOR ),
2021
						'httpd'  => array(
2022
							'type'    => $httpd,
2023
							'modules' => $http_modules,
2024
						),
2025
						'mysql'  => $mvars,
2026
					),
2027
				) );
2028
				break;
2029
			case 'db:prefix':
2030
				$this->response( $wpdb->prefix );
2031
				break;
2032
			case 'db:wpdb':
2033
				if ( !$_POST['query'] )
2034
					die( "naughty naughty" );
2035
				$query = @base64_decode( $_POST['query'] );
2036
				if ( !$query )
2037
					die( "naughty naughty" );
2038
				if ( !$_POST['function'] )
2039
					$function = $function;
0 ignored issues
show
Bug introduced by
The variable $function seems only to be defined at a later point. Did you maybe move this code here without moving the variable definition?

This error can happen if you refactor code and forget to move the variable initialization.

Let’s take a look at a simple example:

function someFunction() {
    $x = 5;
    echo $x;
}

The above code is perfectly fine. Now imagine that we re-order the statements:

function someFunction() {
    echo $x;
    $x = 5;
}

In that case, $x would be read before it is initialized. This was a very basic example, however the principle is the same for the found issue.

Loading history...
Bug introduced by
Why assign $function to itself?

This checks looks for cases where a variable has been assigned to itself.

This assignement can be removed without consequences.

Loading history...
2040
				else
2041
					$function = $_POST['function'];
2042
				$this->response( $bdb->wpdb( $query, $function ) );
2043
				break;
2044
			case 'db:diff':
2045
			case 'db:count':
2046
			case 'db:cols':
2047
				if ( isset( $_POST['limit'] ) )
2048
					$limit = $_POST['limit'];
2049
				else
2050
					$limit = null;
2051
2052
				if ( isset( $_POST['offset'] ) )
2053
					$offset = $_POST['offset'];
2054
				else
2055
					$offset = null;
2056
2057
				if ( isset( $_POST['columns'] ) )
2058
					$columns = $_POST['columns'];
2059
				else
2060
					$columns = null;
2061
2062
				if ( isset( $_POST['signatures'] ) )
2063
					$signatures = $_POST['signatures'];
2064
				else
2065
					$signatures = null;
2066
2067
				if ( isset( $_POST['where'] ) )
2068
					$where = $_POST['where'];
2069
				else
2070
					$where = null;
2071
2072
				if ( isset( $_POST['table'] ) ) {
2073
					$parse_create_table = isset( $_POST['use_new_hash'] ) && $_POST['use_new_hash'] ? true : false;
2074
					$bdb->attach( base64_decode( $_POST['table'] ), $parse_create_table );
2075
				}
2076
2077
				$action_pieces = explode( ':', $_GET['action'] );
2078
				switch ( array_pop( $action_pieces ) ) {
2079
					case 'diff':
2080
						if ( !$signatures ) die( 'naughty naughty' );
2081
						// encoded because mod_security sees this as an SQL injection attack
2082
						$this->response( $bdb->diff( unserialize( base64_decode( $signatures ) ) ) );
2083
					case 'count':
2084
						if ( !$columns ) die( 'naughty naughty' );
2085
						$this->response( $bdb->count( unserialize( $columns ) ) );
2086
					case 'cols':
2087
						if ( !$columns ) die( 'naughty naughty' );
2088
						$this->response( $bdb->get_cols( unserialize( $columns ), $limit, $offset, $where ) );
2089
				}
2090
2091
				break;
2092
			case 'db:tables':
2093
			case 'db:explain':
2094
			case 'db:show_create':
2095
				if ( isset( $_POST['filter'] ) )
2096
					$filter = $_POST['filter'];
2097
				else
2098
					$filter = null;
2099
2100
				if ( isset( $_POST['table'] ) )
2101
					$bdb->attach( base64_decode( $_POST['table'] ) );
2102
2103
				$action_pieces = explode( ':', $_GET['action'] );
2104
				switch ( array_pop( $action_pieces ) ) {
2105
					default:
2106
						die( "naughty naughty" );
2107
					case 'tables':
2108
						$this->response( $bdb->get_tables( $filter ) );
2109
					case 'explain':
2110
						$this->response( $bdb->explain() );
2111
					case 'show_create':
2112
						$this->response( $bdb->show_create() );
2113
				}
2114
				break;
2115
			case 'db:restore':
2116
				if ( !empty( $_POST['path'] ) && isset( $_POST['hash'] ) ) {
2117
					$delete = !isset( $_POST['remove'] ) || $_POST['remove'] && 'false' !== $_POST['remove'];
2118
					$this->response( $bdb->restore( $_POST['path'], $_POST['hash'], $delete ) );
2119
				}
2120
				break;
2121
			case 'themes:active':
2122
				$this->response( get_option( 'current_theme' ) );
2123
			case 'plugins:active':
2124
				$this->response( preg_replace( '#/.*$#', '', get_option( 'active_plugins' ) ) );
2125
				break;
2126
			case 'plugins:checksum': case 'uploads:checksum': case 'themes:checksum': case 'content:checksum': case 'root:checksum':
2127
			case 'plugins:ls':       case 'uploads:ls':       case 'themes:ls':       case 'content:ls':       case 'root:ls':
2128
			case 'plugins:dir':      case 'uploads:dir':      case 'themes:dir':      case 'content:dir':      case 'root:dir':
2129
			case 'plugins:stat':     case 'uploads:stat':     case 'themes:stat':     case 'content:stat':     case 'root:stat':
2130
			case 'plugins:get':      case 'uploads:get':      case 'themes:get':      case 'content:get':      case 'root:get':
2131
2132
				$action_pieces = explode( ':', $_GET['action'] );
2133
				$bfs->want( array_shift( $action_pieces ) );
2134
2135
				if ( isset( $_POST['path'] ) )
2136
					$path = $_POST['path'];
2137
				else
2138
					$path = '';
2139
2140
				if ( !$bfs->validate( $path ) )
0 ignored issues
show
Bug Best Practice introduced by
The expression $bfs->validate($path) of type null|boolean is loosely compared to false; this is ambiguous if the boolean can be false. You might want to explicitly use !== null instead.

If an expression can have both false, and null as possible values. It is generally a good practice to always use strict comparison to clearly distinguish between those two values.

$a = canBeFalseAndNull();

// Instead of
if ( ! $a) { }

// Better use one of the explicit versions:
if ($a !== null) { }
if ($a !== false) { }
if ($a !== null && $a !== false) { }
Loading history...
2141
					die( "naughty naughty" );
2142
2143 View Code Duplication
				if ( isset( $_POST['sha1'] ) && $_POST['sha1'] )
2144
					$sha1 = true;
2145
				else
2146
					$sha1 = false;
2147
2148 View Code Duplication
				if ( isset( $_POST['md5'] ) && $_POST['md5'] )
2149
					$md5 = true;
2150
				else
2151
					$md5 = false;
2152
2153 View Code Duplication
				if ( isset( $_POST['limit'] ) && $_POST['limit'] )
2154
					$limit=$_POST['limit'];
2155
				else
2156
					$limit = false;
2157
2158 View Code Duplication
				if ( isset( $_POST['offset'] ) && $_POST['offset'] )
2159
					$offset = $_POST['offset'];
2160
				else
2161
					$offset = false;
2162
2163 View Code Duplication
				if ( isset( $_POST['recursive'] ) )
2164
					$recursive = (bool)$_POST['recursive'];
2165
				else
2166
					$recursive = false;
2167
2168 View Code Duplication
				if ( isset( $_POST['full_list'] ) )
2169
					$full_list = (bool)$_POST['full_list'];
2170
				else
2171
					$full_list = false;
2172
2173
				$action_pieces = explode( ':', $_GET['action'] );
2174
				switch ( array_pop( $action_pieces ) ) {
2175
					default:
2176
						die( "naughty naughty" );
2177
					case 'checksum':
2178
						$list = array();
2179
						$this->response( $bfs->dir_checksum( $path, $list, $recursive ) );
2180
					case 'dir':
2181
						$this->response( $bfs->dir_examine( $path, $recursive ) );
2182
					case 'stat':
2183
						$this->response( $bfs->stat( $bfs->dir.$path ) );
2184
					case 'get':
2185
						$bfs->fdump( $bfs->dir.$path );
2186
					case 'ls':
2187
						$this->response( $bfs->ls( $path, $md5, $sha1, $limit, $offset, $full_list ) );
2188
				}
2189
				break;
2190
			case 'config:get':
2191
				if ( !isset( $_POST['key'] ) || !$_POST['key'] )
2192
					$this->response( false );
2193
				$key = '_vp_config_' . base64_decode( $_POST['key'] );
2194
				$this->response( base64_encode( maybe_serialize( $this->get_config( $key ) ) ) );
2195
				break;
2196
			case 'config:set':
2197 View Code Duplication
				if ( !isset( $_POST['key'] ) || !$_POST['key'] ) {
2198
					$this->response( false );
2199
					break;
2200
				}
2201
				$key = '_vp_config_' . base64_decode( $_POST['key'] );
2202
				if ( !isset( $_POST['val'] ) || !$_POST['val'] ) {
2203 View Code Duplication
					if ( !isset($_POST['delete']) || !$_POST['delete'] ) {
2204
						$this->response( false );
2205
					} else {
2206
						$this->response( delete_option( $key ) );
2207
					}
2208
					break;
2209
				}
2210
				$val = maybe_unserialize( base64_decode( $_POST['val'] ) );
2211
				$this->response( update_option( $key, $val ) );
2212
				break;
2213
		}
2214
		die();
2215
	}
2216
2217
	function _fix_ixr_null_to_string( &$args ) {
2218
		if ( is_array( $args ) )
2219
			foreach ( $args as $k => $v )
2220
				$args[$k] = $this->_fix_ixr_null_to_string( $v );
2221
		else if ( is_object( $args ) )
2222
			foreach ( get_object_vars( $args ) as $k => $v )
2223
			$args->$k = $this->_fix_ixr_null_to_string( $v );
2224
		else
2225
			return null == $args ? '' : $args;
2226
		return $args;
2227
	}
2228
2229
	function is_localhost() {
2230
		$site_url = $this->site_url();
2231
		if ( empty( $site_url ) )
2232
			return false;
2233
		$parts = parse_url( $site_url );
2234
		if ( !empty( $parts['host'] ) && in_array( $parts['host'], array( 'localhost', '127.0.0.1' ) ) )
2235
			return true;
2236
		return false;
2237
	}
2238
2239
	function contact_service( $action, $args = array() ) {
2240
		if ( 'test' != $action && 'register' != $action && !$this->check_connection() )
2241
			return false;
2242
2243
		global $current_user;
2244
		if ( !isset( $args['args'] ) )
2245
			$args['args'] = '';
2246
		$old_timeout = ini_get( 'default_socket_timeout' );
2247
		$timeout = $this->get_option( 'timeout' );
2248
		if ( function_exists( 'ini_set' ) )
2249
			ini_set( 'default_socket_timeout', $timeout );
2250
		$hostname = $this->get_option( 'hostname' );
2251
2252
		if ( !class_exists( 'VaultPress_IXR_SSL_Client' ) )
2253
			require_once( dirname( __FILE__ ) . '/class.vaultpress-ixr-ssl-client.php' );
2254
		$useragent = 'VaultPress/' . $this->plugin_version . '; ' . $this->site_url();
2255
		$client = new VaultPress_IXR_SSL_Client( $hostname, '/xmlrpc.php', 80, $timeout, $useragent );
2256
2257
		if ( 'vaultpress.com' == $hostname )
2258
			$client->ssl();
2259
2260
		// Begin audit trail breadcrumbs
2261
		if ( isset( $current_user ) && is_object( $current_user ) && isset( $current_user->ID ) ) {
2262
			$args['cause_user_id'] = intval( $current_user->ID );
2263
			$args['cause_user_login'] = (string)$current_user->user_login;
2264
		} else {
2265
			$args['cause_user_id'] = -1;
2266
			$args['cause_user_login'] = '';
2267
		}
2268
		$args['cause_ip'] = isset( $_SERVER['REMOTE_ADDR'] ) ? $_SERVER['REMOTE_ADDR'] : null ;
2269
		$args['cause_uri'] = isset( $_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : null;
2270
		$args['cause_method'] = isset( $_SERVER['REQUEST_METHOD'] ) ? $_SERVER['REQUEST_METHOD'] : null;
2271
		// End audit trail breadcrumbs
2272
2273
		$args['version']   = $this->plugin_version;
2274
		$args['locale']    = get_locale();
2275
		$args['site_url']  = $this->site_url();
2276
2277
		$salt              = md5( time() . serialize( $_SERVER ) );
2278
		$args['key']       = $this->get_option( 'key' );
2279
		$this->_fix_ixr_null_to_string( $args );
2280
		$args['signature'] = $this->sign_string( serialize( $args ), $this->get_option( 'secret' ), $salt ).":$salt";
2281
2282
		$client->query( 'vaultpress.'.$action, new IXR_Base64( serialize( $args ) ) );
2283
		$rval = $client->message ? $client->getResponse() : '';
2284
		if ( function_exists( 'ini_set' ) )
2285
			ini_set( 'default_socket_timeout', $old_timeout );
2286
2287
		// we got an error from the servers
2288 View Code Duplication
		if ( is_array( $rval ) && isset( $rval['faultCode'] ) ) {
2289
			$this->update_option( 'connection', time() );
2290
			$this->update_option( 'connection_error_code', $rval['faultCode'] );
2291
			$this->update_option( 'connection_error_message', $rval['faultString'] );
2292
		}
2293
2294
		return $rval;
2295
	}
2296
2297
	function validate_api_signature() {
2298
		global $__vp_validate_error;
2299
		if ( !empty( $_POST['signature'] ) ) {
2300
			if ( is_string( $_POST['signature'] ) ) {
2301
				$sig = $_POST['signature'];
2302
			} else {
2303
				$__vp_validate_error = array( 'error' => 'invalid_signature_format' );
2304
				return false;
2305
			}
2306
		} else {
2307
			$__vp_validate_error = array( 'error' => 'no_signature' );
2308
			return false;
2309
		}
2310
2311
		$secret = $this->get_option( 'secret' );
2312
		if ( !$secret ) {
2313
			$__vp_validate_error = array( 'error' => 'missing_secret' );
2314
			return false;
2315
		}
2316
		if ( !$this->get_option( 'disable_firewall' ) ) {
2317
			if ( ! $this->check_firewall() )
2318
				return false;
2319
		}
2320
		$sig = explode( ':', $sig );
2321
		if ( !is_array( $sig ) || count( $sig ) != 2 || !isset( $sig[0] ) || !isset( $sig[1] ) ) {
2322
			$__vp_validate_error = array( 'error' => 'invalid_signature_format' );
2323
			return false;
2324
		}
2325
2326
		// Pass 1 -- new method
2327
		$uri = preg_replace( '/^[^?]+\?/', '?', $_SERVER['REQUEST_URI'] );
2328
		$post = $_POST;
2329
		unset( $post['signature'] );
2330
		// Work around for dd-formmailer plugin
2331
		if ( isset( $post['_REPEATED'] ) )
2332
			unset( $post['_REPEATED'] );
2333
		ksort( $post );
2334
		$to_sign = serialize( array( 'uri' => $uri, 'post' => $post ) );
2335
2336
		if ( $this->can_use_openssl() ) {
2337
			$sslsig = '';
2338
			if ( isset( $post['sslsig'] ) ) {
2339
				$sslsig = $post['sslsig'];
2340
				unset( $post['sslsig'] );
2341
			}
2342
			if ( 1 === openssl_verify( serialize( array( 'uri' => $uri, 'post' => $post ) ), base64_decode( $sslsig ), $this->get_option( 'public_key' ) ) ) {
2343
				return true;
2344
			} else {
2345
				$__vp_validate_error = array( 'error' => 'invalid_signed_data' );
2346
				return false;
2347
			}
2348
		}
2349
2350
		$signature = $this->sign_string( $to_sign, $secret, $sig[1] );
2351
		if ( hash_equals( $sig[0], $signature ) ) {
2352
			return true;
2353
		}
2354
2355
		$__vp_validate_error = array( 'error' => 'invalid_signed_data' );
2356
		return false;
2357
	}
2358
2359
	function ip_in_cidr( $ip, $cidr ) {
2360
		list ($net, $mask) = explode( '/', $cidr );
2361
		return ( ip2long( $ip ) & ~((1 << (32 - $mask)) - 1) ) == ( ip2long( $net ) & ~((1 << (32 - $mask)) - 1) );
2362
	}
2363
2364
	function ip_in_cidrs( $ip, $cidrs ) {
2365
		foreach ( (array)$cidrs as $cidr ) {
2366
			if ( $this->ip_in_cidr( $ip, $cidr ) ) {
2367
				return $cidr;
2368
			}
2369
		}
2370
2371
		return false;
2372
	}
2373
2374
	function check_firewall() {
2375
		global $__vp_validate_error;
2376
2377
		$stored_cidrs = $this->get_option( 'service_ips_cidr' );
2378
		$stored_ext_cidrs = get_option( 'vaultpress_service_ips_external_cidr' );
2379
2380
		$one_day_ago = time() - 86400;
2381
		if ( empty( $stored_cidrs ) || empty( $stored_ext_cidrs ) || $stored_cidrs['updated'] < $one_day_ago ) {
2382
			$cidrs = $this->update_firewall();
2383
		}
2384
2385
		if ( empty( $cidrs ) ) {
2386
			$cidrs = array_merge( $stored_cidrs['data'], $stored_ext_cidrs['data'] );
2387
		}
2388
2389
		if ( empty( $cidrs ) ) {
2390
			//	No up-to-date info; fall back on the old methods.
2391
			if ( $this->do_c_block_firewall() ) {
2392
				return true;
2393
			} else {
2394
				$__vp_validate_error = array( 'error' => 'empty_vp_ip_cidr_range' );
2395
				return false;
2396
			}
2397
		}
2398
2399
		//	Figure out possible remote IPs
2400
		$remote_ips = array();
2401
		if ( !empty( $_SERVER['REMOTE_ADDR'] ) )
2402
			$remote_ips['REMOTE_ADDR'] = $_SERVER['REMOTE_ADDR'];
2403
2404
		// If this is a pingback during a connection test, search for valid-looking ips among headers
2405
		$connection_test_key = $this->get_option( 'connection_test' );
2406
		$testing_all_headers = ( ! empty( $_POST['test_key'] ) && $_POST['test_key'] === $connection_test_key );
2407
		if ( $testing_all_headers ) {
2408
			$remote_ips = array_filter( $_SERVER, array( $this, 'looks_like_ip_list' ) );
2409
		}
2410
2411
		// If there is a pre-configured forwarding IP header, check that.
2412
		$forward_header = $this->get_option( 'allow_forwarded_for' );
2413
		if ( true === $forward_header || 1 == $forward_header ) {
2414
			$forward_header = 'HTTP_X_FORWARDED_FOR';
2415
		}
2416
		if ( ! empty( $forward_header ) && ! empty( $_SERVER[ $forward_header ] ) ) {
2417
			$remote_ips[ $forward_header ] = $_SERVER[ $forward_header ];
2418
		}
2419
2420 View Code Duplication
		if ( empty( $remote_ips ) ) {
2421
			$__vp_validate_error = array( 'error' => 'no_remote_addr', 'detail' => (int) $this->get_option( 'allow_forwarded_for' ) ); // shouldn't happen
2422
			return false;
2423
		}
2424
2425
		foreach ( $remote_ips as $header_name => $ip_list ) {
2426
			$ips = explode( ',', $ip_list );
2427
			foreach ( $ips as $ip ) {
2428
				$ip = preg_replace( '#^::(ffff:)?#', '', $ip );
2429
				if ( $cidr = $this->ip_in_cidrs( $ip, $cidrs ) ) {
0 ignored issues
show
Unused Code introduced by
$cidr is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
2430
					// Successful match found. If testing all headers, note the successful header.
2431
					if ( $testing_all_headers && 'REMOTE_ADDR' !== $header_name ) {
2432
						$this->update_option( 'allow_forwarded_for', $header_name );
2433
					}
2434
2435
					return true;
2436
				}
2437
			}
2438
		}
2439
2440
		$__vp_validate_error = array( 'error' => 'remote_addr_fail', 'detail' => $remote_ips );
2441
		return false;
2442
	}
2443
2444
	// Returns true if $value looks like a comma-separated list of IPs
2445
	function looks_like_ip_list( $value ) {
2446
		if ( ! is_string( $value ) ) {
2447
			return false;
2448
		}
2449
2450
		$items = explode( ',', $value );
2451
		foreach ( $items as $item ) {
2452
			if ( ip2long( $item ) === false ) {
2453
				return false;
2454
			}
2455
		}
2456
2457
		return true;
2458
	}
2459
2460
	function do_c_block_firewall() {
2461
		//	Perform the firewall check by class-c ip blocks
2462
		$rxs = $this->get_option( 'service_ips' );
2463
		$service_ips_external = get_option( 'vaultpress_service_ips_external' );
2464
2465
		if ( !empty( $rxs['data'] ) && !empty( $service_ips_external['data'] ) )
2466
			$rxs = array_merge( $rxs['data'], $service_ips_external['data'] );
2467
		if ( ! $rxs )
2468
			return false;
2469
		return $this->validate_ip_address( $rxs );
2470
	}
2471
2472
	function validate_ip_address( $rxs ) {
2473
		global $__vp_validate_error;
2474
		if ( empty( $rxs ) ) {
2475
			$__vp_validate_error = array( 'error' => 'empty_vp_ip_range' );
2476
			return false;
2477
		}
2478
2479
		$remote_ips = array();
2480
2481
		if ( $this->get_option( 'allow_forwarded_for') && !empty( $_SERVER['HTTP_X_FORWARDED_FOR'] ) )
2482
			$remote_ips = explode( ',', $_SERVER['HTTP_X_FORWARDED_FOR'] );
2483
2484
		if ( !empty( $_SERVER['REMOTE_ADDR'] ) )
2485
			$remote_ips[] = $_SERVER['REMOTE_ADDR'];
2486
2487 View Code Duplication
		if ( empty( $remote_ips ) ) {
2488
			$__vp_validate_error = array( 'error' => 'no_remote_addr', 'detail' => (int) $this->get_option( 'allow_forwarded_for' ) ); // shouldn't happen
2489
			return false;
2490
		}
2491
2492
		$iprx = '/^([0-9]+\.[0-9]+\.[0-9]+\.)([0-9]+)$/';
2493
2494
		foreach ( $remote_ips as $_remote_ip ) {
2495
			$remote_ip = preg_replace( '#^::(ffff:)?#', '', $_remote_ip );
2496
			if ( !preg_match( $iprx, $remote_ip, $r ) ) {
2497
				$__vp_validate_error = array( 'error' => "remote_addr_fail", 'detail' => $_remote_ip );
2498
				return false;
2499
			}
2500
2501
			foreach ( (array)$rxs as $begin => $end ) {
2502
				if ( !preg_match( $iprx, $begin, $b ) )
2503
					continue;
2504
				if ( !preg_match( $iprx, $end, $e ) )
2505
					continue;
2506
				if ( $r[1] != $b[1] || $r[1] != $e[1] )
2507
					continue;
2508
				$me = $r[2];
2509
				$b = min( (int)$b[2], (int)$e[2] );
2510
				$e = max( (int)$b[2], (int)$e[2] );
2511
				if ( $me >= $b &&  $me <= $e ) {
2512
					return true;
2513
				}
2514
			}
2515
		}
2516
		$__vp_validate_error = array( 'error' => 'remote_addr_fail', 'detail' => $remote_ips );
2517
2518
		return false;
2519
	}
2520
2521
	function sign_string( $string, $secret, $salt ) {
2522
		return hash_hmac( 'sha1', "$string:$salt", $secret );
2523
	}
2524
2525
	function can_use_openssl() {
2526
		if ( !function_exists( 'openssl_verify' ) )
2527
			return false;
2528
		$pk = $this->get_option( 'public_key' );
2529
		if ( empty( $pk ) )
2530
			return false;
2531
		if ( 1 !== (int) $this->get_option( 'use_openssl_signing' ) )
2532
			return false;
2533
		return true;
2534
	}
2535
2536
	function response( $response, $raw = false ) {
2537
		// "re" -- "Response Encoding"
2538
		if ( !empty( $_GET['re'] ) )
2539
			header( sprintf( 'X-VP-Encoded: X%d', abs( intval( $_GET['re'] ) ) ) );
2540
		if ( $raw ) {
2541
			if ( !isset( $_GET['re'] ) )
2542
				die( $response );
2543
			else if ( '1' === $_GET['re'] )
2544
				die( base64_encode( $response ) );
2545
			else if ( '2' === $_GET['re'] )
2546
				die( str_rot13( $response ) );
2547
			else
2548
				die( $response );
2549
		}
2550
		list( $usec, $sec ) = explode( " ", microtime() );
2551
		$r = new stdClass();
2552
		$r->req_vector = floatval( $_GET['vector'] );
2553
		$r->rsp_vector = ( (float)$usec + (float)$sec );
2554
		if ( function_exists( "getrusage" ) )
2555
			$r->rusage = getrusage();
2556
		else
2557
			$r->rusage = false;
2558
		if ( function_exists( "memory_get_peak_usage" ) )
2559
			$r->peak_memory_usage = memory_get_peak_usage( true );
2560
		else
2561
			$r->peak_memory_usage = false;
2562
		if ( function_exists( "memory_get_usage" ) )
2563
			$r->memory_usage = memory_get_usage( true );
2564
		else
2565
			$r->memory_usage = false;
2566
		$r->response = $response;
2567
		if ( !isset( $_GET['re'] ) )
2568
			die( serialize( $r )  );
2569
		else if ( '1' === $_GET['re'] )
2570
			die( base64_encode( serialize( $r )  ) );
2571
		else if ( '2' === $_GET['re'] )
2572
			die( str_rot13( serialize( $r )  ) );
2573
		else
2574
			die( serialize( $r ) );
2575
	}
2576
2577
	function reset_pings() {
2578
		global $vaultpress_pings;
2579
		$vaultpress_pings = array(
2580
			'version'      => 1,
2581
			'count'        => 0,
2582
			'editedtables' => array(),
2583
			'plugins'      => array(),
2584
			'themes'       => array(),
2585
			'uploads'      => array(),
2586
			'db'           => array(),
2587
			'debug'        => array(),
2588
			'security'     => array(),
2589
		);
2590
	}
2591
2592
	function add_ping( $type, $data, $hook=null ) {
2593
		global $vaultpress_pings;
2594
		if ( defined( 'WP_IMPORTING' ) && constant( 'WP_IMPORTING' ) )
2595
			return;
2596
		if ( isset( $_GET ) && isset( $_GET['comment_status'] ) && isset( $_GET['delete_all'] ) && 'spam' == $_GET['comment_status'] )
2597
			return;	// Skip pings from mass spam delete.
2598
		if ( !array_key_exists( $type, $vaultpress_pings ) )
2599
			return;
2600
2601
		switch( $type ) {
2602
			case 'editedtables';
2603
				$vaultpress_pings[$type] = $data;
2604
				return;
2605
			case 'uploads':
2606
			case 'themes':
2607
			case 'plugins':
2608
				if ( !is_array( $data ) ) {
2609
					$data = array( $data );
2610
				}
2611
				foreach ( $data as $val ) {
2612
					if ( in_array( $data, $vaultpress_pings[$type] ) )
2613
						continue;
2614
					$vaultpress_pings['count']++;
2615
					$vaultpress_pings[$type][]=$val;
2616
				}
2617
				return;
2618
			case 'db':
2619
				$_keys = array_keys( $data );
2620
				$subtype = array_shift( $_keys );
2621
				if ( !isset( $vaultpress_pings[$type][$subtype] ) )
2622
					$vaultpress_pings[$type][$subtype] = array();
2623
				if ( in_array( $data, $vaultpress_pings[$type][$subtype] ) )
2624
					return;
2625
				$vaultpress_pings['count']++;
2626
				$vaultpress_pings[$type][$subtype][] = $data;
2627
				return;
2628
			default:
2629
				if ( in_array( $data, $vaultpress_pings[$type] ) )
2630
					return;
2631
				$vaultpress_pings['count']++;
2632
				$vaultpress_pings[$type][] = $data;
2633
				return;
2634
		}
2635
	}
2636
2637
	function do_pings() {
2638
		global $wpdb, $vaultpress_pings, $__vp_recursive_ping_lock;
2639
		if ( defined( 'WP_IMPORTING' ) && constant( 'WP_IMPORTING' ) )
2640
			return;
2641
2642
		if ( !isset( $wpdb ) ) {
2643
			$wpdb = new wpdb( DB_USER, DB_PASSWORD, DB_NAME, DB_HOST );
2644
			$close_wpdb = true;
2645
		} else {
2646
			$close_wpdb = false;
2647
		}
2648
2649
		if ( !$vaultpress_pings['count'] )
2650
			return;
2651
2652
		// Short circuit the contact process if we know that we can't contact the service
2653
		if ( isset( $__vp_recursive_ping_lock ) && $__vp_recursive_ping_lock ) {
2654
			$this->ai_ping_insert( serialize( $vaultpress_pings ) );
2655
			if ( $close_wpdb ) {
2656
				$wpdb->__destruct();
2657
				unset( $wpdb );
2658
			}
2659
			$this->reset_pings();
2660
			return;
2661
		}
2662
2663
		$ping_attempts = 0;
2664
		do {
2665
			$ping_attempts++;
2666
			$rval = $this->contact_service( 'ping', array( 'args' => $vaultpress_pings ) );
2667
			if ( $rval || $ping_attempts >= 3 )
2668
				break;
2669
			if ( !$rval )
2670
				usleep(500000);
2671
		} while ( true );
2672
		if ( !$rval ) {
2673
			if ( $this->get_option( 'connection_error_code' ) !== -8 ) {    // Do not save pings when the subscription is inactive.
2674
				$__vp_recursive_ping_lock = true;
2675
				$this->ai_ping_insert( serialize( $vaultpress_pings ) );
2676
			}
2677
		}
2678
		$this->reset_pings();
2679
		if ( $close_wpdb ) {
2680
			$wpdb->__destruct();
2681
			unset( $wpdb );
2682
		}
2683
		return $rval;
2684
	}
2685
2686
	function resolve_content_dir() {
2687
		// Take the easy way out
2688
		if ( defined( 'WP_CONTENT_DIR' ) ) {
2689
			if ( substr( WP_CONTENT_DIR, -1 ) != DIRECTORY_SEPARATOR )
2690
				return WP_CONTENT_DIR . DIRECTORY_SEPARATOR;
2691
			return WP_CONTENT_DIR;
2692
		}
2693
		// Best guess
2694
		if ( defined( 'ABSPATH' ) ) {
2695
			if ( substr( ABSPATH, -1 ) != DIRECTORY_SEPARATOR )
2696
				return ABSPATH . DIRECTORY_SEPARATOR . 'wp-content' . DIRECTORY_SEPARATOR;
2697
			return ABSPATH . 'wp-content' . DIRECTORY_SEPARATOR;
2698
		}
2699
		// Run with a solid assumption: WP_CONTENT_DIR/vaultpress/vaultpress.php
2700
		return dirname( dirname( __FILE__ ) ) . DIRECTORY_SEPARATOR;
2701
	}
2702
2703
	function resolve_upload_path() {
2704
		$upload_path = false;
2705
		$upload_dir = wp_upload_dir();
2706
2707
		if ( isset( $upload_dir['basedir'] ) )
2708
			$upload_path = $upload_dir['basedir'];
2709
2710
		// Nothing recorded? use a best guess!
2711
		if ( !$upload_path || $upload_path == realpath( ABSPATH ) )
2712
			return $this->resolve_content_dir() . 'uploads' . DIRECTORY_SEPARATOR;
2713
2714
		if ( substr( $upload_path, -1 ) != DIRECTORY_SEPARATOR )
2715
			$upload_path .= DIRECTORY_SEPARATOR;
2716
2717
		return $upload_path;
2718
	}
2719
2720
	function load_first( $value ) {
2721
		$value = array_unique( $value ); // just in case there are duplicates
2722
		return array_merge(
2723
			preg_grep( '/vaultpress\.php$/', $value ),
2724
			preg_grep( '/vaultpress\.php$/', $value, PREG_GREP_INVERT )
2725
		);
2726
	}
2727
2728
	function is_multisite() {
2729
		if ( function_exists( 'is_multisite' ) )
2730
			return is_multisite();
2731
2732
		return false;
2733
	}
2734
2735
	function is_main_site() {
2736
		if ( !function_exists( 'is_main_site' ) || !$this->is_multisite() )
2737
			return true;
2738
2739
		return is_main_site();
2740
	}
2741
2742
	function is_registered() {
2743
		$key    = $this->get_option( 'key' );
2744
		$secret = $this->get_option( 'secret' );
2745
		return !empty( $key ) && !empty( $secret );
2746
	}
2747
2748
	function clear_connection() {
2749
		$this->delete_option( 'connection' );
2750
		$this->delete_option( 'connection_error_code' );
2751
		$this->delete_option( 'connection_error_message' );
2752
		$this->delete_option( 'connection_test' );
2753
	}
2754
2755
	function site_url() {
2756
		$site_url = '';
2757
2758
		// compatibility for WordPress MU Domain Mapping plugin
2759
		if ( defined( 'DOMAIN_MAPPING' ) && DOMAIN_MAPPING && ! function_exists( 'domain_mapping_siteurl' ) ) {
2760
			if ( !function_exists( 'is_plugin_active' ) )
2761
				require_once ABSPATH . '/wp-admin/includes/plugin.php';
2762
2763
			$plugin = 'wordpress-mu-domain-mapping/domain_mapping.php';
2764
			if ( is_plugin_active( $plugin ) )
2765
				include_once( WP_PLUGIN_DIR . '/' . $plugin );
2766
		}
2767
2768
		if ( function_exists( 'domain_mapping_siteurl' ) )
2769
			$site_url = domain_mapping_siteurl( false );
2770
2771
		if ( empty( $site_url ) )
2772
			$site_url = site_url();
2773
2774
		return $site_url;
2775
	}
2776
2777
	/**
2778
	 * Sync the VaultPress options to WordPress.com if the Jetpack plugin is active.
2779
	 */
2780
	function sync_jetpack_options() {
2781
		if ( class_exists( 'Jetpack_Sync' ) && method_exists( 'Jetpack_Sync', 'sync_options' ) && defined( 'JETPACK__VERSION' ) && version_compare( JETPACK__VERSION, '4.1', '<' ) ) {
2782
			Jetpack_Sync::sync_options( __FILE__, $this->auto_register_option, $this->option_name );
0 ignored issues
show
Unused Code introduced by
The call to Jetpack_Sync::sync_options() has too many arguments starting with __FILE__.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
2783
		}
2784
	}
2785
2786
	/**
2787
	 * Add the VaultPress options to the Jetpack options management whitelist.
2788
	 * Allows Jetpack to register VaultPress options automatically.
2789
	 *
2790
	 * @param array $options The list of whitelisted option names.
2791
	 *
2792
	 * @return array The updated whitelist
2793
	 */
2794
	function add_to_jetpack_options_whitelist( $options ) {
2795
		$options[] = $this->option_name;
2796
		$options[] = $this->auto_register_option;
2797
2798
		return $options;
2799
	}
2800
2801
	/**
2802
	 * When the VaultPress auto-register option is updated, run the registration call.
2803
	 *
2804
	 * This should only be run when the option is updated from the Jetpack/WP.com
2805
	 * API call, and only if the new key is different than the old key.
2806
	 *
2807
	 * @param mixed $old_value The old option value, or the option name (if add_option).
2808
	 * @param mixed $value     The new option value.
2809
	 */
2810
	function updated_auto_register_option( $old_value, $value ) {
2811
		// Not an API call or CLI call
2812
		if ( ! class_exists( 'WPCOM_JSON_API_Update_Option_Endpoint' ) && ! ( defined( 'WP_CLI' ) && WP_CLI ) ) {
2813
			return;
2814
		}
2815
2816
		remove_action( "update_option_{$this->auto_register_option}", array( $this, 'updated_auto_register_option' ) );
2817
2818
		$defaults = array(
2819
			'key'    => false,
2820
			'action' => 'register', // or `response`
2821
			'status' => 'working',
2822
			'error'  => false,
2823
		);
2824
2825
		// `wp_parse_args` uses arrays, might as well be explicit about it.
2826
		$registration = (array) json_decode( $value );
2827
		$registration = wp_parse_args( $registration, $defaults );
0 ignored issues
show
Documentation introduced by
$defaults is of type array<string,false|strin...ring","error":"false"}>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
2828
2829
		// If we have a working connection, don't update the key.
2830
		if ( $this->check_connection( true ) ) {
2831
			$registration['action'] = 'response';
2832
			$registration['error'] = 'VaultPress is already registered on this site.';
2833
			update_option( $this->auto_register_option, json_encode( $registration ) );
2834
			return;
2835
		}
2836
2837
		if ( ! $registration['key'] ) {
2838
			return;
2839
		}
2840
2841
		$registration['action'] = 'response';
2842
2843
		$response = $this->register( $registration['key'] );
2844
		if ( is_wp_error( $response ) ) {
2845
			$registration['status'] = 'broken';
2846
			$registration['error'] = $response->get_error_message();
0 ignored issues
show
Bug introduced by
The method get_error_message() does not seem to exist on object<WP_Error>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
2847
		} else if ( $this->get_option( 'connection_error_code' ) ) {
2848
			$registration['status'] = 'broken';
2849
			$registration['error'] = $this->get_option( 'connection_error_message' );
2850
		} else {
2851
			$registration['error'] = false;
2852
		}
2853
2854
		update_option( $this->auto_register_option, json_encode( $registration ) );
2855
	}
2856
2857
	function add_global_actions_and_filters() {
2858
		add_action( 'init',                                        array( $this, 'sync_jetpack_options' ), 0, 99 );
2859
		add_filter( 'jetpack_options_whitelist',                   array( $this, 'add_to_jetpack_options_whitelist' ) );
2860
		add_action( "update_option_{$this->auto_register_option}", array( $this, 'updated_auto_register_option' ), 10, 2 );
2861
		add_action( "add_option_{$this->auto_register_option}",    array( $this, 'updated_auto_register_option' ), 10, 2 );
2862
		add_action( 'admin_enqueue_scripts',                       array( $this, 'styles' ) );
2863
	}
2864
2865
	function add_admin_actions_and_filters() {
2866
		add_action( 'admin_init', array( $this, 'admin_init' ) );
2867
		add_action( 'admin_menu', array( $this, 'admin_menu' ), 5 ); # Priority 5, so it's called before Jetpack's admin_menu.
2868
		add_action( 'admin_head', array( $this, 'admin_head' ) );
2869
	}
2870
2871
	function add_listener_actions_and_filters() {
2872
		add_action( 'admin_bar_menu', array( $this, 'toolbar' ), 999 );
2873
2874
		// Comments
2875
		add_action( 'delete_comment',        array( $this, 'comment_action_handler' ) );
2876
		add_action( 'wp_set_comment_status', array( $this, 'comment_action_handler' ) );
2877
		add_action( 'trashed_comment',       array( $this, 'comment_action_handler' ) );
2878
		add_action( 'untrashed_comment',     array( $this, 'comment_action_handler' ) );
2879
		add_action( 'wp_insert_comment',     array( $this, 'comment_action_handler' ) );
2880
		add_action( 'comment_post',          array( $this, 'comment_action_handler' ) );
2881
		add_action( 'edit_comment',          array( $this, 'comment_action_handler' ) );
2882
2883
		// Commentmeta
2884
		add_action( 'added_comment_meta',   array( $this, 'commentmeta_insert_handler' ), 10, 2 );
2885
		add_action( 'updated_comment_meta', array( $this, 'commentmeta_modification_handler' ), 10, 4 );
2886
		add_action( 'deleted_comment_meta', array( $this, 'commentmeta_modification_handler' ), 10, 4 );
2887
2888
		// Users
2889
		if ( $this->is_main_site() ) {
2890
			add_action( 'user_register',  array( $this, 'userid_action_handler' ) );
2891
			add_action( 'password_reset', array( $this, 'userid_action_handler' ) );
2892
			add_action( 'profile_update', array( $this, 'userid_action_handler' ) );
2893
			add_action( 'user_register',  array( $this, 'userid_action_handler' ) );
2894
			add_action( 'deleted_user',   array( $this, 'userid_action_handler' ) );
2895
		}
2896
2897
		// Usermeta
2898
		if ( $this->is_main_site() ) {
2899
			// Keeping these action hooks for backward compatibility
2900
			add_action( 'added_usermeta',  array( $this, 'usermeta_action_handler' ), 10, 4 );
2901
			add_action( 'update_usermeta', array( $this, 'usermeta_action_handler' ), 10, 4 );
2902
			add_action( 'delete_usermeta', array( $this, 'usermeta_action_handler' ), 10, 4 );
2903
2904
			add_action( 'added_user_meta',  array( $this, 'usermeta_action_handler' ), 10, 4 );
2905
			add_action( 'update_user_meta', array( $this, 'usermeta_action_handler' ), 10, 4 );
2906
			add_action( 'delete_user_meta', array( $this, 'usermeta_action_handler' ), 10, 4 );
2907
		}
2908
2909
		// Posts
2910
		add_action( 'delete_post',              array( $this, 'post_action_handler' ) );
2911
		add_action( 'trash_post',               array( $this, 'post_action_handler' ) );
2912
		add_action( 'untrash_post',             array( $this, 'post_action_handler' ) );
2913
		add_action( 'edit_post',                array( $this, 'post_action_handler' ) );
2914
		add_action( 'save_post',                array( $this, 'post_action_handler' ) );
2915
		add_action( 'wp_insert_post',           array( $this, 'post_action_handler' ) );
2916
		add_action( 'edit_attachment',          array( $this, 'post_action_handler' ) );
2917
		add_action( 'add_attachment',           array( $this, 'post_action_handler' ) );
2918
		add_action( 'delete_attachment',        array( $this, 'post_action_handler' ) );
2919
		add_action( 'private_to_publish',       array( $this, 'post_action_handler' ) );
2920
		add_action( 'wp_restore_post_revision', array( $this, 'post_action_handler' ) );
2921
2922
		// Postmeta
2923
		add_action( 'added_post_meta',   array( $this, 'postmeta_insert_handler' ), 10, 4 );
2924
		add_action( 'update_post_meta',  array( $this, 'postmeta_modification_handler' ), 10, 4 );
2925
		add_action( 'updated_post_meta', array( $this, 'postmeta_modification_handler' ), 10, 4 );
2926
		add_action( 'delete_post_meta',  array( $this, 'postmeta_modification_handler' ), 10, 4 );
2927
		add_action( 'deleted_post_meta', array( $this, 'postmeta_modification_handler' ), 10, 4 );
2928
		add_action( 'added_postmeta',    array( $this, 'postmeta_action_handler' ), 10, 3 );
2929
		add_action( 'update_postmeta',   array( $this, 'postmeta_action_handler' ), 10, 3 );
2930
		add_action( 'delete_postmeta',   array( $this, 'postmeta_action_handler' ), 10, 3 );
2931
2932
		// Links
2933
		add_action( 'edit_link',   array( $this, 'link_action_handler' ) );
2934
		add_action( 'add_link',    array( $this, 'link_action_handler' ) );
2935
		add_action( 'delete_link', array( $this, 'link_action_handler' ) );
2936
2937
		// Taxonomy
2938
		add_action( 'created_term',              array( $this, 'term_handler' ), 2 );
2939
		add_action( 'edited_terms',              array( $this, 'term_handler' ), 2 );
2940
		add_action( 'delete_term',               array( $this, 'term_handler' ), 2 );
2941
		add_action( 'edit_term_taxonomy',        array( $this, 'term_taxonomy_handler' ) );
2942
		add_action( 'delete_term_taxonomy',      array( $this, 'term_taxonomy_handler' ) );
2943
		add_action( 'edit_term_taxonomies',      array( $this, 'term_taxonomies_handler' ) );
2944
		add_action( 'add_term_relationship',     array( $this, 'term_relationship_handler' ), 10, 2 );
2945
		add_action( 'delete_term_relationships', array( $this, 'term_relationships_handler' ), 10, 2 );
2946
		add_action( 'set_object_terms',          array( $this, 'set_object_terms_handler' ), 10, 3 );
2947
2948
		// Files
2949
		if ( $this->is_main_site() ) {
2950
			add_action( 'switch_theme',      array( $this, 'theme_action_handler' ) );
2951
			add_action( 'activate_plugin',   array( $this, 'plugin_action_handler' ) );
2952
			add_action( 'deactivate_plugin', array( $this, 'plugin_action_handler' ) );
2953
		}
2954
		add_action( 'wp_handle_upload',  array( $this, 'upload_handler' ) );
2955
2956
		// Options
2957
		add_action( 'deleted_option', array( $this, 'option_handler' ), 1 );
2958
		add_action( 'updated_option', array( $this, 'option_handler' ), 1 );
2959
		add_action( 'added_option',   array( $this, 'option_handler' ), 1 );
2960
2961
		$this->add_woocommerce_actions();
2962
		$this->add_vp_required_filters();
2963
	}
2964
2965
	function add_woocommerce_actions() {
2966
		add_action( 'woocommerce_tax_rate_deleted', array( $this, 'woocommerce_tax_rate_handler' ), 10, 1 );
2967
		add_action( 'woocommerce_tax_rate_updated', array( $this, 'woocommerce_tax_rate_handler' ), 10, 1 );
2968
		add_action( 'woocommerce_tax_rate_added', array( $this, 'woocommerce_tax_rate_handler' ), 10, 1 );
2969
2970
		add_action( 'woocommerce_new_order_item', array( $this, 'woocommerce_order_item_handler' ), 10, 1 );
2971
		add_action( 'woocommerce_update_order_item', array( $this, 'woocommerce_order_item_handler' ), 10, 1 );
2972
		add_action( 'woocommerce_delete_order_item', array( $this, 'woocommerce_order_item_handler' ), 10, 1 );
2973
2974
		add_action( 'added_order_item_meta', array( $this, 'woocommerce_order_item_meta_handler' ), 10, 1 );
2975
		add_action( 'updated_order_item_meta', array( $this, 'woocommerce_order_item_meta_handler' ), 10, 1 );
2976
		add_action( 'deleted_order_item_meta', array( $this, 'woocommerce_order_item_meta_handler' ), 10, 1 );
2977
2978
		add_action( 'woocommerce_attribute_added', array( $this, 'woocommerce_attribute_handler' ), 10, 1 );
2979
		add_action( 'woocommerce_attribute_updated', array( $this, 'woocommerce_attribute_handler' ), 10, 1 );
2980
		add_action( 'woocommerce_attribute_deleted', array( $this, 'woocommerce_attribute_handler' ), 10, 1 );
2981
	}
2982
2983
	function add_vp_required_filters() {
2984
		// Log ins
2985
		if ( $this->get_option( 'login_lockdown' ) ) {
2986
			add_action( 'login_form', array( $this, 'add_js_token' ) );
2987
			add_filter( 'authenticate', array( $this, 'authenticate' ), 999 );
2988
		}
2989
2990
		// Report back to VaultPress
2991
		add_action( 'shutdown', array( $this, 'do_pings' ) );
2992
2993
		// VaultPress likes being first in line
2994
		add_filter( 'pre_update_option_active_plugins', array( $this, 'load_first' ) );
2995
	}
2996
2997 View Code Duplication
	function get_jetpack_email() {
2998
		if ( ! class_exists( 'Jetpack' ) ) {
2999
			return false;
3000
		}
3001
3002
		// For version of Jetpack prior to 7.7.
3003
		if ( defined( 'JETPACK__VERSION' ) && version_compare( JETPACK__VERSION, '7.7', '<' ) && ! class_exists( 'Jetpack_IXR_Client' ) ) {
3004
			Jetpack::load_xml_rpc_client();
0 ignored issues
show
Deprecated Code introduced by
The method Jetpack::load_xml_rpc_client() has been deprecated with message: since 7.7.0

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
3005
		}
3006
3007
		$xml = new Jetpack_IXR_Client( array( 'user_id' => get_current_user_id() ) );
3008
		$xml->query( 'wpcom.getUserEmail' );
3009
		if ( ! $xml->isError() ) {
3010
			return $xml->getResponse();
3011
		}
3012
3013
		return new WP_Error( $xml->getErrorCode(), $xml->getErrorMessage() );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with $xml->getErrorCode().

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
3014
	}
3015
3016 View Code Duplication
	function get_key_via_jetpack( $already_purchased = false ) {
3017
		if ( ! class_exists( 'Jetpack' ) ) {
3018
			return false;
3019
		}
3020
3021
		// For version of Jetpack prior to 7.7.
3022
		if ( defined( 'JETPACK__VERSION' ) && version_compare( JETPACK__VERSION, '7.7', '<' ) && ! class_exists( 'Jetpack_IXR_Client' ) ) {
3023
			Jetpack::load_xml_rpc_client();
0 ignored issues
show
Deprecated Code introduced by
The method Jetpack::load_xml_rpc_client() has been deprecated with message: since 7.7.0

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
3024
		}
3025
3026
		$xml = new Jetpack_IXR_Client( array( 'user_id' => Jetpack_Options::get_option( 'master_user' ) ) );
3027
		$xml->query( 'vaultpress.registerSite', $already_purchased );
3028
		if ( ! $xml->isError() ) {
3029
			return $xml->getResponse();
3030
		}
3031
3032
		return new WP_Error( $xml->getErrorCode(), $xml->getErrorMessage() );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with $xml->getErrorCode().

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
3033
	}
3034
3035
	function register_via_jetpack( $already_purchased = false ) {
3036
		$registration_key = $this->get_key_via_jetpack( $already_purchased );
3037
		if ( is_wp_error( $registration_key ) ) {
3038
			return $registration_key;
3039
		}
3040
3041
		return self::register( $registration_key );
3042
	}
3043
}
3044
3045
$vaultpress = VaultPress::init();
3046
3047
if ( isset( $_GET['vaultpress'] ) && $_GET['vaultpress'] ) {
3048
	if ( !function_exists( 'wp_magic_quotes' ) ) {
3049
		// Escape with wpdb.
3050
		$_GET    = add_magic_quotes( $_GET    );
3051
		$_POST   = add_magic_quotes( $_POST   );
3052
		$_COOKIE = add_magic_quotes( $_COOKIE );
3053
		$_SERVER = add_magic_quotes( $_SERVER );
3054
3055
		// Force REQUEST to be GET + POST.  If SERVER, COOKIE, or ENV are needed, use those superglobals directly.
3056
		$_REQUEST = array_merge( $_GET, $_POST );
3057
	} else {
3058
		wp_magic_quotes();
3059
	}
3060
3061
	if ( !function_exists( 'wp_get_current_user' ) )
3062
		include ABSPATH . '/wp-includes/pluggable.php';
3063
3064
	// TODO: this prevents some error notices but do we need it? is there a better way to check capabilities/logged in user/etc?
3065
	if ( function_exists( 'wp_cookie_constants' ) && !defined( 'AUTH_COOKIE' ) )
3066
		wp_cookie_constants();
3067
3068
	$vaultpress->parse_request( null );
3069
3070
	die();
3071
}
3072
3073
// only load hotfixes if it's not a VP request
3074
require_once( dirname( __FILE__ ) . '/class.vaultpress-hotfixes.php' );
3075
$hotfixes = new VaultPress_Hotfixes();
3076
3077
// Add a helper method to WP CLI for auto-registerion via Jetpack
3078
if ( defined( 'WP_CLI' ) && WP_CLI ) {
3079
	require_once( dirname( __FILE__ ) . '/class.vaultpress-cli.php' );
3080
}
3081
3082
include_once( dirname( __FILE__ ) . '/cron-tasks.php' );
3083