Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 1806
0.00% covered (danger)
0.00%
0 / 119
CRAP
0.00% covered (danger)
0.00%
0 / 1
vaultpress_admin_missing_autoloader
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 1
6
VaultPress
0.00% covered (danger)
0.00%
0 / 1746
0.00% covered (danger)
0.00%
0 / 118
438906
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 24
0.00% covered (danger)
0.00%
0 / 1
42
 init
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 register
0.00% covered (danger)
0.00%
0 / 12
0.00% covered (danger)
0.00%
0 / 1
42
 activate
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
12
 deactivate
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
6
 upgrade
0.00% covered (danger)
0.00%
0 / 34
0.00% covered (danger)
0.00%
0 / 1
42
 get_option
0.00% covered (danger)
0.00%
0 / 23
0.00% covered (danger)
0.00%
0 / 1
210
 update_option
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
12
 delete_option
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 update_options
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 admin_init
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 admin_head
0.00% covered (danger)
0.00%
0 / 24
0.00% covered (danger)
0.00%
0 / 1
132
 admin_menu
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 load_menu
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
6
 styles
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
30
 toolbar
0.00% covered (danger)
0.00%
0 / 19
0.00% covered (danger)
0.00%
0 / 1
20
 get_messages
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
30
 server_url
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
12
 connect_notice
0.00% covered (danger)
0.00%
0 / 24
0.00% covered (danger)
0.00%
0 / 1
12
 activated_notice
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
20
 error_notice
0.00% covered (danger)
0.00%
0 / 23
0.00% covered (danger)
0.00%
0 / 1
42
 ui
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
2
 ui_render
0.00% covered (danger)
0.00%
0 / 30
0.00% covered (danger)
0.00%
0 / 1
72
 ui_load
0.00% covered (danger)
0.00%
0 / 65
0.00% covered (danger)
0.00%
0 / 1
342
 ui_register
0.00% covered (danger)
0.00%
0 / 34
0.00% covered (danger)
0.00%
0 / 1
2
 ui_masthead
0.00% covered (danger)
0.00%
0 / 17
0.00% covered (danger)
0.00%
0 / 1
6
 ui_footer
0.00% covered (danger)
0.00%
0 / 28
0.00% covered (danger)
0.00%
0 / 1
6
 ui_main
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 ui_fatal_error
0.00% covered (danger)
0.00%
0 / 12
0.00% covered (danger)
0.00%
0 / 1
2
 ui_message
0.00% covered (danger)
0.00%
0 / 21
0.00% covered (danger)
0.00%
0 / 1
42
 render_notice
0.00% covered (danger)
0.00%
0 / 12
0.00% covered (danger)
0.00%
0 / 1
6
 ui_delete_vp_settings_button
0.00% covered (danger)
0.00%
0 / 30
0.00% covered (danger)
0.00%
0 / 1
12
 ui_logo
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 get_config
0.00% covered (danger)
0.00%
0 / 16
0.00% covered (danger)
0.00%
0 / 1
30
 get_option_name_ignore
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 1
6
 get_post_meta_name_ignore
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
6
 get_should_ignore_files
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
6
 option_handler
0.00% covered (danger)
0.00%
0 / 16
0.00% covered (danger)
0.00%
0 / 1
72
 comment_action_handler
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
30
 theme_action_handler
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 upload_handler
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 plugin_action_handler
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 userid_action_handler
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
12
 term_handler
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 term_taxonomy_handler
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 term_taxonomies_handler
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
6
 term_relationship_handler
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 term_relationships_handler
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
6
 set_object_terms_handler
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 usermeta_action_handler
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 post_action_handler
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 link_action_handler
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 commentmeta_insert_handler
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
12
 commentmeta_modification_handler
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
12
 postmeta_insert_handler
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 postmeta_modification_handler
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
20
 postmeta_action_handler
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
20
 woocommerce_tax_rate_handler
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 woocommerce_order_item_handler
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 woocommerce_order_item_meta_handler
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 woocommerce_attribute_handler
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 generic_change_handler
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 block_change_handler
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 verify_table
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
42
 record_table
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 get_last_table
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
12
 is_write_query
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
20
 get_table_from_query
0.00% covered (danger)
0.00%
0 / 36
0.00% covered (danger)
0.00%
0 / 1
42
 table_notify_columns
0.00% covered (danger)
0.00%
0 / 22
0.00% covered (danger)
0.00%
0 / 1
6
 ai_ping_next
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
12
 ai_ping_insert
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
12
 allow_ai_pings
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
12
 ai_ping_queue_size
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 ai_ping_get
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
6
 ai_ping_queue_delete
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 request_firewall_update
0.00% covered (danger)
0.00%
0 / 32
0.00% covered (danger)
0.00%
0 / 1
132
 update_firewall
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
42
 update_plan_settings
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
30
 check_connection
0.00% covered (danger)
0.00%
0 / 47
0.00% covered (danger)
0.00%
0 / 1
182
 get_login_tokens
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
6
 add_js_token
0.00% covered (danger)
0.00%
0 / 55
0.00% covered (danger)
0.00%
0 / 1
20
 authenticate
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
72
 parse_request
0.00% covered (danger)
0.00%
0 / 334
0.00% covered (danger)
0.00%
0 / 1
28056
 _fix_ixr_null_to_string
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
42
 is_localhost
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
20
 contact_service
0.00% covered (danger)
0.00%
0 / 40
0.00% covered (danger)
0.00%
0 / 1
342
 validate_api_signature
0.00% covered (danger)
0.00%
0 / 42
0.00% covered (danger)
0.00%
0 / 1
240
 ip_in_cidr
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 ip_in_cidrs
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
12
 check_firewall
0.00% covered (danger)
0.00%
0 / 38
0.00% covered (danger)
0.00%
0 / 1
420
 looks_like_ip_list
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
20
 do_c_block_firewall
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
20
 validate_ip_address
0.00% covered (danger)
0.00%
0 / 32
0.00% covered (danger)
0.00%
0 / 1
240
 sign_string
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 can_use_openssl
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
20
 response
0.00% covered (danger)
0.00%
0 / 31
0.00% covered (danger)
0.00%
0 / 1
156
 reset_pings
0.00% covered (danger)
0.00%
0 / 12
0.00% covered (danger)
0.00%
0 / 1
2
 add_ping
0.00% covered (danger)
0.00%
0 / 36
0.00% covered (danger)
0.00%
0 / 1
420
 do_pings
0.00% covered (danger)
0.00%
0 / 33
0.00% covered (danger)
0.00%
0 / 1
210
 resolve_content_dir
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
30
 resolve_upload_path
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
30
 load_first
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
2
 is_multisite
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 is_main_site
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
12
 is_registered
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 clear_connection
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 site_url
0.00% covered (danger)
0.00%
0 / 12
0.00% covered (danger)
0.00%
0 / 1
72
 sync_jetpack_options
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
30
 add_to_jetpack_options_whitelist
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 updated_auto_register_option
0.00% covered (danger)
0.00%
0 / 28
0.00% covered (danger)
0.00%
0 / 1
72
 add_global_actions_and_filters
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
2
 add_admin_actions_and_filters
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 add_listener_actions_and_filters
0.00% covered (danger)
0.00%
0 / 65
0.00% covered (danger)
0.00%
0 / 1
20
 add_woocommerce_actions
0.00% covered (danger)
0.00%
0 / 12
0.00% covered (danger)
0.00%
0 / 1
2
 add_vp_required_filters
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
6
 get_jetpack_email
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
42
 get_key_via_jetpack
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
42
 register_via_jetpack
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
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: 4.0.7
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.
17defined( 'ABSPATH' ) || die( 0 );
18
19define( 'VAULTPRESS__MINIMUM_PHP_VERSION', '7.2' );
20define( 'VAULTPRESS__VERSION', '4.0.7' );
21define( 'VAULTPRESS__PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
22
23/**
24 * Load all the packages.
25 *
26 * We want to fail gracefully if `composer install` has not been executed yet, so we are checking for the autoloader.
27 * If the autoloader is not present, let's log the failure, pause VaultPress, and display a nice admin notice.
28 */
29$loader = VAULTPRESS__PLUGIN_DIR . 'vendor/autoload_packages.php';
30
31if ( is_readable( $loader ) ) {
32    require $loader;
33} else {
34    if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
35        error_log(
36            wp_kses(
37                __( 'Your installation of VaultPress is incomplete. If you installed it from GitHub, please run <code>composer install</code>.', 'vaultpress' ),
38                array( 'code' => true )
39            )
40        );
41    }
42
43    // Add a red bubble notification to My Jetpack if the installation is bad.
44    add_filter(
45        'my_jetpack_red_bubble_notification_slugs',
46        function ( $slugs ) {
47            $slugs['vaultpress-plugin-bad-installation'] = array(
48                'data' => array(
49                    'plugin' => 'VaultPress',
50                ),
51            );
52
53            return $slugs;
54        }
55    );
56
57    /**
58     * Outputs an admin notice for folks running VaultPress without having run `composer install`.
59     */
60    function vaultpress_admin_missing_autoloader() {
61        if ( get_current_screen()->id !== 'plugins' ) {
62            return;
63        }
64
65        $message = wp_kses(
66            __( 'Your installation of VaultPress is incomplete. If you installed it from GitHub, please run <code>composer install</code>.', 'vaultpress' ),
67            array( 'code' => true )
68        );
69        wp_admin_notice(
70            $message,
71            array(
72                'type'        => 'error',
73                'dismissible' => true,
74            )
75        );
76    }
77    add_action( 'admin_notices', 'vaultpress_admin_missing_autoloader' );
78    return;
79}
80
81/**
82 * Main VaultPress class.
83 */
84class VaultPress {
85    var $option_name          = 'vaultpress';
86    var $auto_register_option = 'vaultpress_auto_register';
87    var $db_version           = 4;
88    var $plugin_version       = VAULTPRESS__VERSION;
89
90    /**
91     * Server URL.
92     *
93     * @var ?string
94     */
95    private $server_url;
96
97    /**
98     * Options.
99     *
100     * @var array
101     */
102    public $options;
103
104    /**
105     * Blog ID.
106     *
107     * @var int
108     */
109    public $options_blog_id;
110
111    function __construct() {
112        register_activation_hook( __FILE__, array( $this, 'activate' ) );
113        register_deactivation_hook( __FILE__, array( $this, 'deactivate' ) );
114
115        $this->options_blog_id = get_current_blog_id();
116        $options = get_option( $this->option_name );
117        if ( !is_array( $options ) )
118            $options = array();
119
120        $defaults = array(
121            'db_version'            => 0,
122            'key'                   => '',
123            'secret'                => '',
124            'connection'            => false,
125            'service_ips_cidr'      => false
126        );
127
128        $this->options = wp_parse_args( $options, $defaults );
129        $this->reset_pings();
130
131        $this->upgrade();
132
133        $this->add_global_actions_and_filters();
134
135        if ( is_admin() ) {
136            $this->add_admin_actions_and_filters();
137        }
138
139        if ( $this->is_registered() ) {
140            $do_not_backup = $this->get_option( 'do_not_backup' ) || $this->get_option( 'do_not_send_backup_pings' );
141            if ( $do_not_backup )
142                $this->add_vp_required_filters();
143            else
144                $this->add_listener_actions_and_filters();
145        }
146    }
147
148    static function &init() {
149        static $instance = false;
150
151        if ( !$instance ) {
152            $instance = new VaultPress();
153        }
154
155        return $instance;
156    }
157
158    static function register( $registration_key ) {
159        $vp = self::init();
160
161        $nonce = wp_create_nonce( 'vp_register_' . $registration_key );
162        $args = array( 'registration_key' =>  $registration_key, 'nonce' => $nonce );
163        $response = $vp->contact_service( 'register', $args );
164
165        // Check for an error
166        if ( ! empty( $response['faultCode'] ) )
167            return new WP_Error( $response['faultCode'], $response['faultString'] );
168
169        // Validate result
170        if ( empty( $response['key'] ) || empty( $response['secret'] ) || empty( $response['nonce'] ) || $nonce != $response['nonce'] )
171            return new WP_Error( 1, __( 'There was a problem trying to register your VaultPress subscription.' ) );
172
173        // Store the result, force a connection test.
174        $vp->update_option( 'key', $response['key'] );
175        $vp->update_option( 'secret', $response['secret'] );
176        $vp->check_connection( true );
177
178        return true;
179    }
180
181    function activate( $network_wide ) {
182        $type = $network_wide ? 'network' : 'single';
183        $this->update_option( 'activated', $type );
184
185        // force a connection check after an activation
186        $this->clear_connection();
187
188        if ( get_option( 'vaultpress_auto_connect' ) ) {
189            $this->register_via_jetpack( true );
190        }
191    }
192
193    function deactivate() {
194        if ( $this->is_registered() )
195            $this->contact_service( 'plugin_status', array( 'vp_plugin_status' => 'deactivated' ) );
196    }
197
198    function upgrade() {
199        $current_db_version = $this->get_option( 'db_version' );
200
201        if ( $current_db_version < 1 ) {
202            $this->options['connection']  = get_option( 'vaultpress_connection' );
203            $this->options['key']         = get_option( 'vaultpress_key' );
204            $this->options['secret']      = get_option( 'vaultpress_secret' );
205            $this->options['service_ips'] = get_option( 'vaultpress_service_ips' );
206
207            // remove old options
208            $old_options = array(
209                'vaultpress_connection',
210                'vaultpress_hostname',
211                'vaultpress_key',
212                'vaultpress_secret',
213                'vaultpress_service_ips',
214                'vaultpress_timeout',
215                'vp_allow_remote_execution',
216                'vp_debug_request_signing',
217                'vp_disable_firewall',
218            );
219
220            foreach ( $old_options as $option )
221                delete_option( $option );
222
223            $this->options['db_version'] = $this->db_version;
224            $this->update_options();
225        }
226
227        if ( $current_db_version < 2 ) {
228            $this->delete_option( 'timeout' );
229            $this->delete_option( 'disable_firewall' );
230            $this->update_option( 'db_version', $this->db_version );
231            $this->clear_connection();
232        }
233
234        if ( $current_db_version < 3 ) {
235            $this->update_firewall();
236            $this->update_option( 'db_version', $this->db_version );
237            $this->clear_connection();
238        }
239
240        if ( $current_db_version < 4 ) {
241            $this->update_firewall();
242            $this->update_option( 'db_version', $this->db_version );
243            $this->clear_connection();
244        }
245    }
246
247    function get_option( $key ) {
248        if ( 'hostname' == $key ) {
249            if ( defined( 'VAULTPRESS_HOSTNAME' ) )
250                return VAULTPRESS_HOSTNAME;
251            else
252                return 'vaultpress.com';
253        }
254
255        if ( 'timeout' == $key ) {
256            if ( defined( 'VAULTPRESS_TIMEOUT' ) )
257                return VAULTPRESS_TIMEOUT;
258            else
259                return 60;
260        }
261
262        if ( 'disable_firewall' == $key ) {
263            if ( defined( 'VAULTPRESS_DISABLE_FIREWALL' ) )
264                return VAULTPRESS_DISABLE_FIREWALL;
265            else
266                return false;
267        }
268
269        if ( ( 'key' == $key || 'secret' == $key ) && empty( $this->options[$key] ) ) {
270            return '';
271        }
272
273        // allow_forwarded_for can be overrided by config, or stored in or out of the vp option
274        if ( 'allow_forwarded_for' === $key ) {
275            if ( defined( 'ALLOW_FORWARDED_FOR' ) ) {
276                return ALLOW_FORWARDED_FOR;
277            }
278
279            $standalone_option = get_option( 'vaultpress_allow_forwarded_for' );
280            if ( ! empty( $standalone_option ) ) {
281                return $standalone_option;
282            }
283        }
284
285        if ( isset( $this->options[$key] ) )
286            return $this->options[$key];
287
288        return false;
289    }
290
291    function update_option( $key, $value ) {
292        if ( 'allow_forwarded_for' === $key ) {
293            update_option( 'vaultpress_allow_forwarded_for', $value );
294
295            if ( isset( $this->options[ $key ] ) ) {
296                unset( $this->options[ $key ] );
297                $this->update_options();
298            }
299            return;
300        }
301
302        $this->options[$key] = $value;
303        $this->update_options();
304    }
305
306    function delete_option( $key ) {
307        if ( 'allow_forwarded_for' === $key ) {
308            delete_option( 'vaultpress_allow_forwarded_for' );
309        }
310
311        unset( $this->options[$key] );
312        $this->update_options();
313    }
314
315    function update_options() {
316        // Avoid overwriting the VaultPress option if current blog_id has changed since reading it
317        if ( get_current_blog_id() !== $this->options_blog_id ) {
318            return;
319        }
320
321        update_option( $this->option_name, $this->options );
322    }
323
324    function admin_init() {
325        if ( !current_user_can( 'manage_options' ) )
326            return;
327
328        load_plugin_textdomain( 'vaultpress', false, dirname( plugin_basename( __FILE__ ) ) . '/languages/' );
329    }
330
331    function admin_head() {
332        if ( ! current_user_can( 'manage_options' ) ) {
333            return;
334        }
335
336        // Array of hooks where we want to hook our notices.
337        $notice_hooks = array( 'user_admin_notices' );
338
339        /*
340         * In the VaultPress dashboard, move the notices.
341         */
342        $screen = get_current_screen();
343        if (
344            ! is_null( $screen )
345            && in_array(
346                $screen->id,
347                array( 'jetpack_page_vaultpress', 'toplevel_page_vaultpress' ),
348                true
349            )
350        ) {
351            $notice_hooks[] = 'vaultpress_notices';
352        } else {
353            $notice_hooks[] = 'admin_notices';
354        }
355
356        if ( $activated = $this->get_option( 'activated' ) ) {
357            if ( 'network' == $activated ) {
358                add_action( 'network_admin_notices', array( $this, 'activated_notice' ) );
359            } else {
360                foreach ( $notice_hooks as $filter ) {
361                    add_action( $filter, array( $this, 'activated_notice' ) );
362                }
363            }
364        }
365
366        // ask the user to connect their site w/ VP
367        if ( !$this->is_registered() ) {
368            foreach ( $notice_hooks as $filter ) {
369                add_action( $filter, array( $this, 'connect_notice' ) );
370            }
371
372        // if we have an error make sure to let the user know about it
373        } else {
374            $error_code = $this->get_option( 'connection_error_code' );
375             if ( ! empty( $error_code ) ) {
376                foreach ( $notice_hooks as $filter ) {
377                    add_action( $filter, array( $this, 'error_notice' ) );
378                }
379            }
380        }
381    }
382
383    function admin_menu() {
384        // if Jetpack is loaded then we need to wait for that menu to be added
385        if ( class_exists( 'Jetpack' ) )
386            add_action( 'jetpack_admin_menu', array( $this, 'load_menu' ) );
387        else
388            $this->load_menu();
389    }
390
391    function load_menu() {
392        if ( class_exists( 'Jetpack' ) ) {
393            $hook = add_submenu_page( 'jetpack', 'VaultPress', 'VaultPress', 'manage_options', 'vaultpress', array( $this, 'ui' ) );
394        } else {
395            $hook = add_menu_page( 'VaultPress', 'VaultPress', 'manage_options', 'vaultpress', array( $this, 'ui' ), 'div' );
396        }
397
398        add_action( "load-$hook", array( $this, 'ui_load' ) );
399        add_action( 'admin_print_styles', array( $this, 'styles' ) );
400    }
401
402    function styles() {
403        if ( !current_user_can( 'manage_options' ) || !is_admin() )
404            return;
405
406        wp_enqueue_style( 'vaultpress-nav', plugins_url( '/nav-styles.css', __FILE__ ), false, date( 'Ymd' ) );
407
408        if ( isset( $_GET['page'] ) && 'vaultpress' == $_GET['page'] )
409            wp_enqueue_style( 'vaultpress', plugins_url( '/styles.css', __FILE__ ), false, date( 'Ymd' ) );
410    }
411
412    // display a security threat notice if one exists
413    function toolbar( $wp_admin_bar ) {
414
415        if ( !current_user_can( 'manage_options' ) )
416            return;
417
418        $messages = $this->get_messages();
419        if ( !empty( $messages['security_notice_count'] ) ) {
420            $count = (int)$messages['security_notice_count'];
421            if ( $count > 0 ) {
422                $count = number_format( $count, 0 );
423                $wp_admin_bar->add_node( array(
424                    'id' => 'vp-notice',
425                    'title' => '<span class="ab-icon"></span>' .
426                        sprintf( _n( '%s Security Threat', '%s Security Threats', $count , 'vaultpress'), $count ),
427                    'parent' => 'top-secondary',
428                    'href' => sprintf( 'https://dashboard.vaultpress.com/%d/security/', $messages['site_id'] ),
429                    'meta'  => array(
430                        'title' => __( 'Visit VaultPress Security' , 'vaultpress'),
431                        'onclick' => 'window.open( this.href ); return false;',
432                        'class' => 'error'
433                    ),
434                ) );
435            }
436        }
437    }
438
439    /**
440     * Get messages from the VP servers
441     *
442     * @param bool $force_reload Whether to force a reload of the messages.
443     * @return array The messages.
444     */
445    function get_messages( $force_reload = false ) {
446        $last_contact = $this->get_option( 'messages_last_contact' );
447
448        // only run the messages check every 30 minutes
449        if ( ( time() - (int) $last_contact ) > 1800 || $force_reload ) {
450            $response = $this->contact_service( 'messages', array() );
451
452            // Only process if we got a valid string response
453            if ( is_string( $response ) && ! empty( $response ) ) {
454                $messages = base64_decode( $response ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode
455                $messages = unserialize( $messages ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_unserialize
456                $this->update_option( 'messages_last_contact', time() );
457                $this->update_option( 'messages', $messages );
458            } else {
459                // If we got an error (array) or false/empty, fall back to cached messages
460                $messages = $this->get_option( 'messages' );
461            }
462        } else {
463            $messages = $this->get_option( 'messages' );
464        }
465
466        return $messages;
467    }
468
469    function server_url() {
470        if ( ! isset( $this->server_url ) ) {
471            $scheme = is_ssl() ? 'https' : 'http';
472            $this->server_url = sprintf( '%s://%s/', $scheme, $this->get_option( 'hostname' ) );
473        }
474
475        return $this->server_url;
476    }
477
478    /**
479     * Show message if plugin is activated but not connected to VaultPress.
480     */
481    function connect_notice() {
482        $screen = get_current_screen();
483
484        /**
485         * Do not display any error message if we don't have any info about the page.
486         */
487        if ( is_null( $screen ) ) {
488            return;
489        }
490
491        /**
492         * Only display errors on specific pages:
493         * - the main dashboard.
494         * - the Jetpack dashboard.
495         * - the plugins screen.
496         */
497
498        if (
499        in_array(
500            $screen->id,
501            array(
502                'dashboard',
503                'toplevel_page_jetpack',
504                'plugins',
505            ),
506            true
507        )
508        ) {
509            $message = sprintf(
510                wp_kses(
511                /* translators: URLs to VaultPress' dashboard page. */
512                    __( 'To back up and secure your site, enter your registration key. <a href="%1$s">Register VaultPress or purchase a plan.</a>', 'vaultpress' ),
513                    array(
514                        'a' => array(
515                            'href' => array(),
516                        ),
517                    )
518                ),
519                admin_url( 'admin.php?page=vaultpress' )
520            );
521            $this->ui_message( $message, 'notice', __( 'VaultPress needs your attention!', 'vaultpress' ) );
522        }
523    }
524
525    // show message after activation
526    function activated_notice() {
527        if ( 'network' == $this->get_option( 'activated' ) ) {
528            $message = sprintf(
529                __( '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' ),
530                'https://dashboard.vaultpress.com/'
531            );
532            $this->ui_message( $message, 'activated', __( 'VaultPress has been activated across your network!', 'vaultpress' ) );
533
534        // key and secret already exist in db
535        } elseif ( $this->is_registered() ) {
536            if ( $this->check_connection() ) {
537            }
538        }
539
540        $this->delete_option( 'activated' );
541    }
542
543    /**
544     * Display an error notice when something is wrong with our VaultPress installation.
545     */
546    public function error_notice() {
547        $error_message = $this->get_option( 'connection_error_message' );
548
549        // link to the VaultPress page if we're not already there.
550        if (
551            ! isset( $_GET['page'] )
552            || 'vaultpress' != $_GET['page']
553        ) {
554            $error_message .= sprintf(
555                ' <a href="%s">%s</a>',
556                admin_url( 'admin.php?page=vaultpress' ),
557                esc_html__( 'Visit the VaultPress page', 'vaultpress' )
558            );
559        }
560
561        $screen = get_current_screen();
562
563        /*
564         * Do not display any error message if we don't have a message,
565         * or have no info about the page.
566         */
567        if ( is_null( $screen ) || empty( $error_message ) ) {
568            return;
569        }
570
571        /*
572         * Only display errors on specific pages:
573         * - the main dashboard.
574         * - the VaultPress and Jetpack dashboards.
575         * - the plugins screen.
576         */
577        if (
578            in_array(
579                $screen->id,
580                array(
581                    'dashboard',
582                    'toplevel_page_jetpack',
583                    'jetpack_page_vaultpress',
584                    'toplevel_page_vaultpress',
585                    'plugins',
586                ),
587                true
588            )
589        ) {
590            $this->ui_message( $error_message, 'error' );
591        }
592    }
593
594    /**
595     * Adds the main wrappers and the header, and defers controls to ui_render to decide which view to render.
596     */
597    function ui() {
598        $ui_state = $this->ui_render();
599        ?>
600            <div id="jp-plugin-container">
601                <?php $this->ui_masthead( $ui_state[ 'dashboard_link' ] ); ?>
602                <div class="vp-wrap">
603                    <?php
604                    /**
605                     * Allow the display of custom notices.
606                     *
607                     * @since 2.0.0
608                     */
609                    do_action( 'vaultpress_notices' );
610                    ?>
611                    <?php echo $ui_state[ 'ui' ]; // This content is sanitized when it's produced. ?>
612                </div>
613                <?php $this->ui_footer(); ?>
614            </div>
615        <?php
616    }
617
618    /**
619     * Decides which UI view to render and executes it.
620     *
621     * @return array $args {
622     *     An array of options to render the dashboard.
623     *
624     *     @type string $ui             Dashboard markup.
625     *     @type string $dashboard_link Whether to show the link to the VaultPress dashboard.
626     * }
627     */
628    function ui_render() {
629        ob_start();
630
631        if ( $this->is_localhost() ) {
632            $this->update_option( 'connection', time() );
633            $this->update_option( 'connection_error_code', 'error_localhost' );
634            $this->update_option(
635                'connection_error_message',
636                esc_html__( 'Hostnames such as localhost or 127.0.0.1 cannot be reached by vaultpress.com and will not work with the service. Sites must be publicly accessible in order to work with VaultPress.', 'vaultpress' )
637            );
638            $this->error_notice();
639            return array( 'ui' => ob_get_clean(), 'dashboard_link' => false );
640        }
641
642        if ( ! empty( $_GET[ 'error' ] ) ) {
643            $this->error_notice();
644            $this->clear_connection();
645        }
646
647        if ( ! $this->is_registered() ) {
648            $this->ui_register();
649            return array( 'ui' => ob_get_clean(), 'dashboard_link' => false );
650        }
651
652        $status = $this->contact_service( 'status' );
653        if ( ! $status ) {
654            $error_code = $this->get_option( 'connection_error_code' );
655            if ( 0 == $error_code ) {
656                $this->ui_fatal_error();
657            } else {
658                $this->ui_register();
659            }
660            return array( 'ui' => ob_get_clean(), 'dashboard_link' => 0 != $error_code );
661        }
662
663        $ticker = $this->contact_service( 'ticker' );
664        if ( is_array( $ticker ) && isset( $ticker[ 'faultCode' ] ) ) {
665            $this->error_notice();
666            $this->ui_register();
667            return array( 'ui' => ob_get_clean(), 'dashboard_link' => true );
668        }
669
670        $this->ui_main();
671        return array( 'ui' => ob_get_clean(), 'dashboard_link' => true );
672    }
673
674    function ui_load() {
675        if ( ! current_user_can( 'manage_options' ) ) {
676            return;
677        }
678
679        if ( isset( $_POST['action'] ) && 'delete-vp-settings' == $_POST['action'] ) {
680            check_admin_referer( 'delete_vp_settings' );
681
682            $ai_ping_queue_size = $this->ai_ping_queue_size();
683            if ( ! empty( $ai_ping_queue_size->option_count ) && $ai_ping_queue_size->option_count > 1 ) {
684                $this->ai_ping_queue_delete();
685            }
686
687            delete_option( $this->option_name );
688            delete_option( 'vaultpress_service_ips_external_cidr' );
689            delete_option( '_vp_signatures' );
690            delete_option( '_vp_config_option_name_ignore' );
691            delete_option( '_vp_config_post_meta_name_ignore' );
692            delete_option( '_vp_config_should_ignore_files' );
693            delete_option( '_vp_current_scan' );
694            delete_option( 'vaultpress_auto_register' );
695
696            wp_redirect( admin_url( 'admin.php?page=vaultpress&delete-vp-settings=1' ) );
697            exit( 0 );
698        }
699
700        // run code that might be updating the registration key
701        if ( isset( $_POST['action'] ) && 'register' == $_POST['action'] ) {
702            check_admin_referer( 'vaultpress_register' );
703
704            // reset the connection info so messages don't cross
705            $this->clear_connection();
706
707            // if registering via Jetpack, get a key...
708            if ( isset( $_POST['key_source'] ) && 'jetpack' === $_POST['key_source'] ) {
709                $registration_key = $this->get_key_via_jetpack();
710                if ( is_wp_error( $registration_key ) ) {
711                    $this->update_option( 'connection_error_code', -2 );
712                    $this->update_option(
713                        'connection_error_message',
714                        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' ),
715                            esc_html( $registration_key->get_error_message() ), 'http://vaultpress.com/contact/' )
716                    );
717                    wp_redirect( admin_url( 'admin.php?page=vaultpress&error=true' ) );
718                    exit( 0 );
719                }
720            } else {
721            $registration_key = trim( $_POST[ 'registration_key' ] );
722            }
723
724            if ( empty( $registration_key ) ) {
725                $this->update_option( 'connection_error_code', 1 );
726                $this->update_option(
727                    'connection_error_message',
728                    sprintf(
729                        __( '<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' ),
730                        'https://dashboard.vaultpress.com/'
731                    )
732                );
733                wp_redirect( admin_url( 'admin.php?page=vaultpress&error=true' ) );
734                exit( 0 );
735            }
736
737            // try to register the plugin
738            $nonce = wp_create_nonce( 'vp_register_' . $registration_key );
739            $args = array( 'registration_key' =>  $registration_key, 'nonce' => $nonce );
740            $response = $this->contact_service( 'register', $args );
741
742            // we received an error from the VaultPress servers
743            if ( !empty( $response['faultCode'] ) ) {
744                $this->update_option( 'connection_error_code',    $response['faultCode'] );
745                $this->update_option( 'connection_error_message', $response['faultString'] );
746                wp_redirect( admin_url( 'admin.php?page=vaultpress&error=true' ) );
747                exit( 0 );
748            }
749
750            // make sure the returned data looks valid
751            if ( empty( $response['key'] ) || empty( $response['secret'] ) || empty( $response['nonce'] ) || $nonce != $response['nonce'] ) {
752                $this->update_option( 'connection_error_code', 1 );
753                $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/' ) );
754                wp_redirect( admin_url( 'admin.php?page=vaultpress&error=true' ) );
755                exit( 0 );
756            }
757
758            // need to update these values in the db so the servers can try connecting to the plugin
759            $this->update_option( 'key', $response['key'] );
760            $this->update_option( 'secret', $response['secret'] );
761            if ( $this->check_connection( true ) ) {
762                wp_redirect( admin_url( 'admin.php?page=vaultpress' ) );
763                exit( 0 );
764            }
765
766            // reset the key and secret
767            $this->update_option( 'key', '' );
768            $this->update_option( 'secret', '' );
769            wp_redirect( admin_url( 'admin.php?page=vaultpress&error=true' ) );
770            exit( 0 );
771        }
772    }
773
774    function ui_register() {
775        ?>
776            <div class="vp-notice__wide">
777                <div class="dops-card">
778                    <img src="<?php echo esc_url( plugins_url( 'images/security.svg', __FILE__ ) ); ?>" alt="VaultPress">
779                    <h2><?php _e( 'The VaultPress plugin requires a subscription.', 'vaultpress' ); ?></h2>
780                    <p><?php _e( 'Get realtime backups, automated security scanning, and support from WordPress&nbsp;experts.', 'vaultpress' ); ?></p>
781                    <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>
782                </div>
783            </div>
784
785            <div class="jp-dash-section-header">
786                <div class="jp-dash-section-header__label">
787                    <h2 class="jp-dash-section-header__name">
788                        <?php esc_html_e( 'Management', 'vaultpress' ); ?>
789                    </h2>
790                </div>
791            </div>
792
793            <div class="vp-row">
794                <div class="vp-col">
795                    <div class="dops-card dops-section-header is-compact">
796                        <?php esc_html_e( 'Registration key', 'vaultpress' ) ?>
797                    </div>
798                    <div class="dops-card">
799                        <form method="post" action="">
800                            <fieldset>
801                                <p>
802                                    <?php esc_html_e( 'Paste your registration key&nbsp;below:', 'vaultpress' ); ?>
803                                </p>
804                                <p>
805                                    <textarea class="dops-textarea" placeholder="<?php esc_attr_e( __( 'Enter your key here...', 'vaultpress' ) ); ?>" name="registration_key"></textarea>
806                                </p>
807                                <button class="dops-button is-compact"><?php _e( 'Register ', 'vaultpress' ); ?></button>
808                                <input type="hidden" name="action" value="register" />
809                                <?php wp_nonce_field( 'vaultpress_register' ); ?>
810                            </fieldset>
811                        </form>
812                    </div>
813                </div>
814                <div class="vp-col">
815                    <?php $this->ui_delete_vp_settings_button(); ?>
816                </div>
817            </div>
818        <?php
819    }
820
821    /**
822     * Renders the top header.
823     *
824     * @param bool $show_nav Whether to show navigation.
825     */
826    function ui_masthead( $show_nav = true ) {
827        ?>
828        <div class="jp-masthead">
829            <div class="jp-masthead__inside-container">
830                <div class="jp-masthead__logo-container">
831                    <a class="jp-masthead__logo-link" href="https://vaultpress.com">
832                        <img src="<?php echo esc_url( plugins_url( 'images/vaultpress.svg', __FILE__ ) ); ?>" alt="VaultPress">
833                    </a>
834                </div>
835                <?php if ( $show_nav ) : ?>
836                    <div class="jp-masthead__nav">
837                        <div class="dops-button-group">
838                            <a href="https://dashboard.vaultpress.com" class="dops-button is-compact" target="_blank" rel="noopener noreferrer">
839                                <?php _e( 'Visit Dashboard', 'vaultpress' ); ?>
840                            </a>
841                        </div>
842                    </div>
843                <?php endif; ?>
844            </div>
845        </div>
846        <?php
847    }
848
849    /**
850     * Renders the footer.
851     */
852    function ui_footer() {
853        ?>
854        <div class="jp-footer">
855            <div class="jp-footer__a8c-attr-container">
856                <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>
857                    <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>
858                    <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>
859                    <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>
860                </svg>
861            </div>
862            <ul class="jp-footer__links">
863                <li class="jp-footer__link-item">
864                    <a href="https://vaultpress.com" class="jp-footer__link" title="<?php esc_attr_e( 'VaultPress version', 'vaultpress' ) ?>" target="_blank" rel="noopener noreferrer">
865                        <?php printf( 'VaultPress %s', $this->plugin_version ); ?>
866                    </a>
867                </li>
868                <li class="jp-footer__link-item">
869                    <a href="https://wordpress.com/tos/" class="jp-footer__link" title="<?php esc_attr_e( 'Terms of service', 'vaultpress' ) ?>" target="_blank" rel="noopener noreferrer">
870                        <?php esc_html_e( 'Terms', 'vaultpress' ); ?>
871                    </a>
872                </li>
873            </ul>
874            <div class="jp-power">
875                <a
876                    href="<?php echo class_exists( 'Jetpack_Admin_Page' ) ? esc_url( admin_url( 'admin.php?page=jetpack' ) ) : 'https://jetpack.com' ?>"
877                    class="jp-power__text-link"
878                    target="_blank"
879                    rel="noopener noreferrer"
880                >
881                    <span class="jp-power__text"><?php esc_html_e( 'Powered by', 'vaultpress') ?></span> <?php echo $this->ui_logo(); ?>
882                </a>
883            </div>
884        </div>
885        <?php
886    }
887
888    function ui_main() {
889        $response = base64_decode( $this->contact_service( 'plugin_ui' ) );
890        echo $response;
891        $this->ui_delete_vp_settings_button();
892    }
893
894    function ui_fatal_error() {
895        $this->render_notice(
896            sprintf(
897                '<strong>' . __( 'We can\'t connect to %1$s.', 'vaultpress' ) . '</strong><br/>' .
898                __( 'Please make sure that your website is accessible via the Internet. Please contact the VaultPress support if you still have issues.' ),
899                esc_html( $this->get_option( 'hostname' ) )
900            ),
901            'is-warning',
902            array(
903                'label' => __( 'Contact support' ),
904                'url' => 'https://vaultpress.com/contact/',
905            )
906        );
907    }
908
909    function ui_message( $message, $type = 'notice', $heading = '' ) {
910        $level = 'is-warning';
911        if ( empty( $heading ) ) {
912            switch ( $type ) {
913                case 'error':
914                    $level = 'is-error';
915                    $heading = __( 'Oops... there seems to be a problem.', 'vaultpress' );
916                    break;
917
918                case 'success':
919                    $level = 'is-success';
920                    $heading = __( 'Yay! Things look good.', 'vaultpress' );
921                    break;
922
923                default:
924                    $heading = __( 'VaultPress needs your attention!', 'vaultpress' );
925                    break;
926            }
927        }
928
929        $classes = in_array( get_current_screen()->parent_base, array( 'jetpack', 'vaultpress' ), true )
930            ? ''
931            : "notice notice-$type";
932
933        $this->render_notice(
934            "<strong>$heading</strong><br/>$message",
935            $level,
936            array(),
937            $classes
938        );
939    }
940
941    /**
942     * Renders a notice. Can have
943     *
944     * @param string $content Notice main content.
945     * @param string $level Can be is-info, is-warning, is-error. By default, it's is-info.
946     * @param array  $action  {
947     *     Arguments to display a linked action button in the notice.
948     *
949     *     @type string $label The action button label.
950     *     @type string $url   The action button link.
951     * }
952     * @param string $classes This is added as a CSS class to the root node. Useful to pass WP core classes for notices.
953     */
954    function render_notice( $content, $level = 'is-info', $action = array(), $classes = '' ) {
955        $allowed_html = array(
956            'a' => array( 'href' => true, 'target' => 'blank', 'rel' => 'noopener noreferrer' ),
957            'br' => true,
958            'strong' => true,
959        );
960        ?>
961            <div class="dops-notice vp-notice <?php echo esc_attr( "$level $classes" ) ?>">
962                <span class="dops-notice__icon-wrapper">
963                    <svg class="gridicon gridicons-info dops-notice__icon" height="24" width="24" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
964                        <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" />
965                    </svg>
966                </span>
967                <span class="dops-notice__content">
968                    <span class="dops-notice__text"><?php echo wp_kses( $content, $allowed_html ) ?></span>
969                </span>
970                <?php if ( ! empty( $action ) ) : ?>
971                    <a class="dops-notice__action" href="<?php echo esc_attr( $action['url'] ) ?>" target="_blank" rel="noopener noreferrer">
972                        <span><?php echo esc_html( $action['label'] ) ?></span>
973                    </a>
974                <?php endif; ?>
975            </div>
976        <?php
977    }
978
979    function ui_delete_vp_settings_button() {
980        ?>
981        <div class="dops-card dops-section-header is-compact">
982            <?php _e( 'Reset settings', 'vaultpress' ); ?>
983        </div>
984        <?php
985        if ( isset( $_GET['delete-vp-settings'] ) && 1 == (int) $_GET['delete-vp-settings'] ) {
986            ?>
987            <div class="dops-card">
988                <p><?php esc_html_e( 'All VaultPress settings have been deleted.', 'vaultpress' ); ?></p>
989            </div>
990            <?php
991        } else {
992            ?>
993            <div class="dops-card">
994                <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>
995                <p><strong>
996                <?php
997                printf(
998                    wp_kses(
999                        /* translators: URLs to VaultPress dashboard. */
1000                        __( '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' ),
1001                        array(
1002                            'a' => array(
1003                                'href'   => array(),
1004                                'target' => array(),
1005                                'rel'    => array(),
1006                            ),
1007                        )
1008                    ),
1009                    'https://dashboard.vaultpress.com/account/'
1010                );
1011                ?>
1012                </strong></p>
1013                <form
1014                    onsubmit="return confirm( '<?php esc_html_e( 'Do you really want to reset all options?', 'vaultpress' ) ?>' );"
1015                    method="post"
1016                    action="">
1017                    <button class="dops-button is-scary is-compact"><?php esc_html_e( 'Delete all settings', 'vaultpress' ); ?></button>
1018                    <input type="hidden" name="action" value="delete-vp-settings"/>
1019                    <?php wp_nonce_field( 'delete_vp_settings' ); ?>
1020                </form>
1021            </div>
1022            <?php
1023        }
1024    }
1025
1026    /**
1027     * Render the Jetpack logo
1028     */
1029    function ui_logo() {
1030        if ( ! class_exists( 'Jetpack_Logo' ) ) {
1031            require_once VAULTPRESS__PLUGIN_DIR . 'class-jetpack-logo.php';
1032        }
1033        $jetpack_logo = new Jetpack_Logo();
1034
1035        return $jetpack_logo->output();
1036    }
1037
1038    function get_config( $key ) {
1039        $val = get_option( $key );
1040        if ( $val )
1041            return $val;
1042        switch( $key ) {
1043            case '_vp_config_option_name_ignore':
1044                $val = $this->get_option_name_ignore( true );
1045                update_option( '_vp_config_option_name_ignore', $val );
1046                break;
1047            case '_vp_config_post_meta_name_ignore':
1048                $val = $this->get_post_meta_name_ignore( true );
1049                update_option( '_vp_config_post_meta_name_ignore', $val );
1050                break;
1051            case '_vp_config_should_ignore_files':
1052                $val = $this->get_should_ignore_files( true );
1053                update_option( '_vp_config_should_ignore_files', $val );
1054                break;
1055        }
1056        return $val;
1057    }
1058
1059    // Option name patterns to ignore
1060    function get_option_name_ignore( $return_defaults = false ) {
1061        $defaults = array(
1062            'vaultpress',
1063            'cron',
1064            'wpsupercache_gc_time',
1065            'rewrite_rules',
1066            'akismet_spam_count',
1067            '/_transient_/',
1068            '/^_vp_/',
1069        );
1070        if ( $return_defaults )
1071            return $defaults;
1072        $ignore_names = $this->get_config( '_vp_config_option_name_ignore' );
1073        return array_unique( array_merge( $defaults, $ignore_names ) );
1074    }
1075
1076    // post meta name patterns to ignore
1077    function get_post_meta_name_ignore( $return_defaults = false ) {
1078        $defaults = array(
1079            'pvc_views'
1080        );
1081        if ( $return_defaults )
1082            return $defaults;
1083        $ignore_names = $this->get_config( '_vp_config_post_meta_name_ignore' );
1084        return array_unique( array_merge( $defaults, $ignore_names ) );
1085    }
1086
1087    // file name patterns to ignore
1088    function get_should_ignore_files( $return_defaults = false ) {
1089        $defaults = array();
1090        if ( $return_defaults )
1091            return $defaults;
1092        $ignore_names = (array) $this->get_config( '_vp_config_should_ignore_files' );
1093        return array_unique( array_merge( $defaults, $ignore_names ) );
1094    }
1095
1096    ###
1097    ### Section: Backup Notification Hooks
1098    ###
1099
1100    // Handle Handle Notifying VaultPress of Options Activity At this point the options table has already been modified
1101    //
1102    // Note: we handle deleted, instead of delete because VaultPress backs up options by name (which are unique,) that
1103    // means that we do not need to resolve an id like we would for, say, a post.
1104    function option_handler( $option_name ) {
1105        global $wpdb;
1106        // Step 1 -- exclusionary rules, don't send these options to vaultpress, because they
1107        // either change constantly and/or are inconsequential to the blog itself and/or they
1108        // are specific to the VaultPress plugin process and we want to avoid recursion
1109        $should_ping = true;
1110        $ignore_names = $this->get_option_name_ignore();
1111        foreach( (array)$ignore_names as $val ) {
1112            if ( $val[0] == '/' ) {
1113                if ( preg_match( $val, $option_name ) )
1114                    $should_ping = false;
1115            } else {
1116                if ( $val == $option_name )
1117                    $should_ping = false;
1118            }
1119            if ( !$should_ping )
1120                break;
1121        }
1122        if ( $should_ping )
1123            $this->add_ping( 'db', array( 'option' => $option_name ) );
1124
1125        // Step 2 -- If WordPress is about to kick off a some "cron" action, we need to
1126        // flush vaultpress, because the "remote" cron threads done via http fetch will
1127        // be happening completely inside the window of this thread.  That thread will
1128        // be expecting touched and accounted for tables
1129        if ( $option_name == '_transient_doing_cron' )
1130            $this->do_pings();
1131
1132        return $option_name;
1133    }
1134
1135    // Handle Notifying VaultPress of Comment Activity
1136    function comment_action_handler( $comment_id ) {
1137        if ( !is_array( $comment_id ) ) {
1138            if ( wp_get_comment_status( $comment_id ) != 'spam' )
1139                $this->add_ping( 'db', array( 'comment' => $comment_id ) );
1140        } else {
1141            foreach ( $comment_id as $id ) {
1142                if ( wp_get_comment_status( $comment_id ) != 'spam' )
1143                    $this->add_ping( 'db', array( 'comment' => $id) );
1144            }
1145        }
1146    }
1147
1148    // Handle Notifying VaultPress of Theme Switches
1149    function theme_action_handler( $theme ) {
1150        $this->add_ping( 'themes', array( 'theme' => get_option( 'stylesheet' ) ) );
1151    }
1152
1153    // Handle Notifying VaultPress of Upload Activity
1154    function upload_handler( $file ) {
1155        $this->add_ping( 'uploads', array( 'upload' => str_replace( $this->resolve_upload_path(), '', $file['file'] ) ) );
1156        return $file;
1157    }
1158
1159    // Handle Notifying VaultPress of Plugin Activation/Deactivation
1160    function plugin_action_handler( $plugin='' ) {
1161        $this->add_ping( 'plugins', array( 'name' => $plugin ) );
1162    }
1163
1164    // Handle Notifying VaultPress of User Edits
1165    function userid_action_handler( $user_or_id ) {
1166        if ( is_object($user_or_id) )
1167            $userid = intval( $user_or_id->ID );
1168        else
1169            $userid = intval( $user_or_id );
1170        if ( !$userid )
1171            return;
1172        $this->add_ping( 'db', array( 'user' => $userid ) );
1173    }
1174
1175    // Handle Notifying VaultPress of term changes
1176    function term_handler( $term_id, $tt_id=null ) {
1177        $this->add_ping( 'db', array( 'term' => $term_id ) );
1178        if ( $tt_id )
1179            $this->term_taxonomy_handler( $tt_id );
1180    }
1181
1182    // Handle Notifying VaultPress of term_taxonomy changes
1183    function term_taxonomy_handler( $tt_id ) {
1184        $this->add_ping( 'db', array( 'term_taxonomy' => $tt_id ) );
1185    }
1186    // add(ed)_term_taxonomy handled via the created_term hook, the term_taxonomy_handler is called by the term_handler
1187
1188    // Handle Notifying VaultPress of term_taxonomy changes
1189    function term_taxonomies_handler( $tt_ids ) {
1190        foreach( (array)$tt_ids as $tt_id ) {
1191            $this->term_taxonomy_handler( $tt_id );
1192        }
1193    }
1194
1195    // Handle Notifying VaultPress of term_relationship changes
1196    function term_relationship_handler( $object_id, $term_id ) {
1197        $this->add_ping( 'db', array( 'term_relationship' => array( 'object_id' => $object_id, 'term_taxonomy_id' => $term_id ) ) );
1198    }
1199
1200    // Handle Notifying VaultPress of term_relationship changes
1201    function term_relationships_handler( $object_id, $term_ids ) {
1202        foreach ( (array)$term_ids as $term_id ) {
1203            $this->term_relationship_handler( $object_id, $term_id );
1204        }
1205    }
1206
1207    // Handle Notifying VaultPress of term_relationship changes
1208    function set_object_terms_handler( $object_id, $terms, $tt_ids ) {
1209        $this->term_relationships_handler( $object_id, $tt_ids );
1210    }
1211
1212    // Handle Notifying VaultPress of UserMeta changes
1213    function usermeta_action_handler( $umeta_id, $user_id, $meta_key, $meta_value='' ) {
1214        $this->add_ping( 'db', array( 'usermeta' => $umeta_id ) );
1215    }
1216
1217    // Handle Notifying VaultPress of Post Changes
1218    function post_action_handler($post_id) {
1219        if ( current_filter() == 'delete_post' )
1220            return $this->add_ping( 'db', array( 'post' => $post_id ), 'delete_post' );
1221        return $this->add_ping( 'db', array( 'post' => $post_id ), 'edit_post' );
1222    }
1223
1224    // Handle Notifying VaultPress of Link Changes
1225    function link_action_handler( $link_id ) {
1226        $this->add_ping( 'db', array( 'link' => $link_id ) );
1227    }
1228
1229    // Handle Notifying VaultPress of Commentmeta Changes
1230    function commentmeta_insert_handler( $meta_id, $comment_id=null ) {
1231        if ( empty( $comment_id ) || wp_get_comment_status( $comment_id ) != 'spam' )
1232            $this->add_ping( 'db', array( 'commentmeta' => $meta_id ) );
1233    }
1234
1235    function commentmeta_modification_handler( $meta_id, $object_id, $meta_key, $meta_value ) {
1236        if ( !is_array( $meta_id ) )
1237            return $this->add_ping( 'db', array( 'commentmeta' => $meta_id ) );
1238        foreach ( $meta_id as $id ) {
1239            $this->add_ping( 'db', array( 'commentmeta' => $id ) );
1240        }
1241    }
1242
1243    // Handle Notifying VaultPress of PostMeta changes via newfangled metadata functions
1244    function postmeta_insert_handler( $meta_id, $post_id, $meta_key, $meta_value='' ) {
1245        if ( in_array( $meta_key, $this->get_post_meta_name_ignore() ) )
1246            return;
1247
1248        $this->add_ping( 'db', array( 'postmeta' => $meta_id ) );
1249    }
1250
1251    function postmeta_modification_handler( $meta_id, $object_id, $meta_key, $meta_value ) {
1252        if ( in_array( $meta_key, $this->get_post_meta_name_ignore() ) )
1253            return;
1254
1255        if ( !is_array( $meta_id ) )
1256            return $this->add_ping( 'db', array( 'postmeta' => $meta_id ) );
1257        foreach ( $meta_id as $id ) {
1258            $this->add_ping( 'db', array( 'postmeta' => $id ) );
1259        }
1260    }
1261
1262    // Handle Notifying VaultPress of PostMeta changes via old school cherypicked hooks
1263    function postmeta_action_handler( $meta_id, $post_id = null, $meta_key = null ) {
1264        if ( in_array( $meta_key, $this->get_post_meta_name_ignore() ) )
1265            return;
1266
1267        if ( !is_array($meta_id) )
1268            return $this->add_ping( 'db', array( 'postmeta' => $meta_id ) );
1269        foreach ( $meta_id as $id )
1270            $this->add_ping( 'db', array( 'postmeta' => $id ) );
1271    }
1272
1273    // WooCommerce notifications
1274    function woocommerce_tax_rate_handler( $id ) {
1275        $this->generic_change_handler( 'woocommerce_tax_rates', array( 'tax_rate_id' => $id ) );
1276        $this->block_change_handler( 'woocommerce_tax_rate_locations', array( 'tax_rate_id' => $id ) );
1277    }
1278
1279    /**
1280     * Monitor for changes to a Woo order (creation, update, or deletion).
1281     *
1282     * @param int $id Item ID.
1283     */
1284    public function woocommerce_order_item_handler( $id ) {
1285        $this->generic_change_handler( 'woocommerce_order_items', array( 'order_item_id' => $id ) );
1286    }
1287
1288    /**
1289     * Monitor for changes to a Woo order meta (creation, update, or deletion).
1290     *
1291     * @param int $id Item ID.
1292     */
1293    public function woocommerce_order_item_meta_handler( $id ) {
1294        $this->generic_change_handler( 'woocommerce_order_itemmeta', array( 'meta_id' => $id ) );
1295    }
1296
1297    /**
1298     * Monitor for changes to a Woo attribute (creation, update, or deletion).
1299     *
1300     * @param int $id Item ID.
1301     */
1302    public function woocommerce_attribute_handler( $id ) {
1303        $this->generic_change_handler( 'woocommerce_attribute_taxonomies', array( 'attribute_id' => $id ) );
1304    }
1305
1306    function generic_change_handler( $table, $key ) {
1307        $this->add_ping( 'db', array( $table => $key ) );
1308    }
1309
1310    function block_change_handler( $table, $query ) {
1311        $this->add_ping( 'db', array( "bulk~{$table}" => $query ) );
1312    }
1313
1314    function verify_table( $table ) {
1315        global $wpdb;
1316        $status = $wpdb->get_row( $wpdb->prepare( "SHOW TABLE STATUS WHERE Name = %s", $table ) );
1317        if ( !$status || !$status->Update_time || !$status->Comment || $status->Engine != 'MyISAM' )
1318            return true;
1319        if ( preg_match( '/([0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2})/', $status->Comment, $m ) )
1320            return ( $m[1] == $status->Update_time );
1321        return false;
1322    }
1323
1324    // Emulate $wpdb->last_table
1325    function record_table( $table ) {
1326        global $vaultpress_last_table;
1327        $vaultpress_last_table = $table;
1328        return $table;
1329    }
1330
1331    // Emulate $wpdb->last_table
1332    function get_last_table() {
1333        global $wpdb, $vaultpress_last_table;
1334        if ( is_object( $wpdb ) && isset( $wpdb->last_table ) )
1335            return $wpdb->last_table;
1336        return $vaultpress_last_table;
1337    }
1338
1339    // Emulate hyperdb::is_write_query()
1340    function is_write_query( $q ) {
1341        $word = strtoupper( substr( trim( $q ), 0, 20 ) );
1342        if ( 0 === strpos( $word, 'SELECT' ) )
1343            return false;
1344        if ( 0 === strpos( $word, 'SHOW' ) )
1345            return false;
1346        if ( 0 === strpos( $word, 'CHECKSUM' ) )
1347            return false;
1348        return true;
1349    }
1350
1351    // Emulate hyperdb::get_table_from_query()
1352    function get_table_from_query( $q ) {
1353        global $wpdb, $vaultpress_last_table;
1354
1355        if ( is_object( $wpdb ) && method_exists( $wpdb, "get_table_from_query" ) )
1356            return $wpdb->get_table_from_query( $q );
1357
1358        // Remove characters that can legally trail the table name
1359        $q = rtrim( $q, ';/-#' );
1360        // allow ( select... ) union [...] style queries. Use the first queries table name.
1361        $q = ltrim( $q, "\t (" );
1362
1363        // Quickly match most common queries
1364        if ( preg_match( '/^\s*(?:'
1365                . 'SELECT.*?\s+FROM'
1366                . '|INSERT(?:\s+IGNORE)?(?:\s+INTO)?'
1367                . '|REPLACE(?:\s+INTO)?'
1368                . '|UPDATE(?:\s+IGNORE)?'
1369                . '|DELETE(?:\s+IGNORE)?(?:\s+FROM)?'
1370                . ')\s+`?(\w+)`?/is', $q, $maybe) )
1371            return $this->record_table($maybe[1] );
1372
1373        // Refer to the previous query
1374        if ( preg_match( '/^\s*SELECT.*?\s+FOUND_ROWS\(\)/is', $q ) )
1375            return $this->get_last_table();
1376
1377        // Big pattern for the rest of the table-related queries in MySQL 5.0
1378        if ( preg_match( '/^\s*(?:'
1379                . '(?:EXPLAIN\s+(?:EXTENDED\s+)?)?SELECT.*?\s+FROM'
1380                . '|INSERT(?:\s+LOW_PRIORITY|\s+DELAYED|\s+HIGH_PRIORITY)?(?:\s+IGNORE)?(?:\s+INTO)?'
1381                . '|REPLACE(?:\s+LOW_PRIORITY|\s+DELAYED)?(?:\s+INTO)?'
1382                . '|UPDATE(?:\s+LOW_PRIORITY)?(?:\s+IGNORE)?'
1383                . '|DELETE(?:\s+LOW_PRIORITY|\s+QUICK|\s+IGNORE)*(?:\s+FROM)?'
1384                . '|DESCRIBE|DESC|EXPLAIN|HANDLER'
1385                . '|(?:LOCK|UNLOCK)\s+TABLE(?:S)?'
1386                . '|(?:RENAME|OPTIMIZE|BACKUP|RESTORE|CHECK|CHECKSUM|ANALYZE|OPTIMIZE|REPAIR).*\s+TABLE'
1387                . '|TRUNCATE(?:\s+TABLE)?'
1388                . '|CREATE(?:\s+TEMPORARY)?\s+TABLE(?:\s+IF\s+NOT\s+EXISTS)?'
1389                . '|ALTER(?:\s+IGNORE)?\s+TABLE'
1390                . '|DROP\s+TABLE(?:\s+IF\s+EXISTS)?'
1391                . '|CREATE(?:\s+\w+)?\s+INDEX.*\s+ON'
1392                . '|DROP\s+INDEX.*\s+ON'
1393                . '|LOAD\s+DATA.*INFILE.*INTO\s+TABLE'
1394                . '|(?:GRANT|REVOKE).*ON\s+TABLE'
1395                . '|SHOW\s+(?:.*FROM|.*TABLE)'
1396                . ')\s+`?(\w+)`?/is', $q, $maybe ) )
1397            return $this->record_table( $maybe[1] );
1398
1399        // All unmatched queries automatically fall to the global master
1400        return $this->record_table( '' );
1401    }
1402
1403    function table_notify_columns( $table ) {
1404            $want_cols = array(
1405                // data
1406                'posts'                 => '`ID`',
1407                'users'                 => '`ID`',
1408                'links'                 => '`link_id`',
1409                'options'               => '`option_id`,`option_name`',
1410                'comments'              => '`comment_ID`',
1411                // metadata
1412                'postmeta'              => '`meta_id`',
1413                'commentmeta'           => '`meta_id`',
1414                'usermeta'              => '`umeta_id`',
1415                // taxonomy
1416                'term_relationships'    => '`object_id`,`term_taxonomy_id`',
1417                'term_taxonomy'         => '`term_taxonomy_id`',
1418                'terms'                 => '`term_id`',
1419                // plugin special cases
1420                'wpo_campaign'          => '`id`', // WP-o-Matic
1421                'wpo_campaign_category' => '`id`', // WP-o-Matic
1422                'wpo_campaign_feed'     => '`id`', // WP-o-Matic
1423                'wpo_campaign_post'     => '`id`', // WP-o-Matic
1424                'wpo_campaign_word'     => '`id`', // WP-o-Matic
1425                'wpo_log'               => '`id`', // WP-o-Matic
1426            );
1427            if ( isset( $want_cols[$table] ) )
1428                return $want_cols[$table];
1429            return '*';
1430    }
1431
1432    /**
1433     * Use an option ID to ensure a unique ping ID for the site.
1434     *
1435     * @return  int|false  The new ping number. False, if there was an error.
1436     */
1437    function ai_ping_next() {
1438        global $wpdb;
1439
1440        if ( ! $this->allow_ai_pings() ) {
1441            return false;
1442        }
1443
1444        $name = "_vp_ai_ping";
1445        $wpdb->query( $wpdb->prepare( "DELETE FROM `$wpdb->options` WHERE `option_name` = %s;", $name ) );
1446        $success = $wpdb->query( $wpdb->prepare( "INSERT INTO `$wpdb->options` (`option_name`, `option_value`, `autoload`) VALUES (%s, '', 'no')", $name ) );
1447        if ( ! $success ) {
1448            return false;
1449        }
1450        return $wpdb->insert_id;
1451    }
1452
1453    function ai_ping_insert( $value ) {
1454        if ( ! $this->allow_ai_pings() ) {
1455            return false;
1456        }
1457
1458        $new_id = $this->ai_ping_next();
1459
1460        if ( !$new_id )
1461            return false;
1462        add_option( '_vp_ai_ping_' . $new_id, $value, '', 'no' );
1463    }
1464
1465    function allow_ai_pings() {
1466        static $allow_ai_pings = null;
1467
1468        if ( null === $allow_ai_pings ) {
1469            $queue_size = $this->ai_ping_queue_size();
1470            $size_limit = 50 * 1024 * 1024;
1471            $allow_ai_pings = ( $queue_size->option_count < 100 && $queue_size->option_size < $size_limit );
1472        }
1473
1474        return $allow_ai_pings;
1475    }
1476
1477    function ai_ping_queue_size() {
1478        global $wpdb;
1479        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\_%'" );
1480    }
1481
1482    function ai_ping_get( $num=1, $order='ASC' ) {
1483        global $wpdb;
1484        if ( strtolower($order) != 'desc' )
1485            $order = 'ASC';
1486        else
1487            $order = 'DESC';
1488        return $wpdb->get_results( $wpdb->prepare(
1489            "SELECT * FROM $wpdb->options WHERE `option_name` LIKE '\_vp\_ai\_ping\_%%' ORDER BY `option_id` $order LIMIT %d",
1490            min( 10, max( 1, (int)$num ) )
1491        ) );
1492    }
1493
1494    function ai_ping_queue_delete() {
1495        global $wpdb;
1496
1497        return $wpdb->query( "DELETE FROM `$wpdb->options` WHERE `option_name` LIKE '\_vp\_ai\_ping%'" );
1498    }
1499
1500    function request_firewall_update( $external_services = false ) {
1501        $args     = array( 'timeout' => $this->get_option( 'timeout' ), 'sslverify' => true );
1502        $hostname = $this->get_option( 'hostname' );
1503        $path = $external_services ? 'service-ips-external' : 'service-ips';
1504
1505        $data = false;
1506        $https_error = null;
1507        $retry = 2;
1508        $protocol = 'https';
1509        do {
1510            --$retry;
1511            $args['sslverify'] = 'https' == $protocol ? true : false;
1512            $r = wp_remote_get( $url=sprintf( "%s://%s/%s?cidr_ranges=1", $protocol, $hostname, $path ), $args );
1513            if ( 200 == wp_remote_retrieve_response_code( $r ) ) {
1514                if ( 99 == $this->get_option( 'connection_error_code' ) )
1515                    $this->clear_connection();
1516                $data = @unserialize( wp_remote_retrieve_body( $r ) );
1517                break;
1518            }
1519            if ( 'https' == $protocol ) {
1520                $https_error = $r;
1521                $protocol = 'http';
1522            }
1523            usleep( 100 );
1524        } while( $retry > 0 );
1525
1526        if ( $https_error != null && ! empty( $data ) ) {
1527            $r_code = wp_remote_retrieve_response_code( $https_error );
1528            if ( 200 != $r_code ) {
1529                $error_message = sprintf( 'Unexpected HTTP response code %s', $r_code );
1530                if ( false === $r_code )
1531                    $error_message = 'Unable to find an HTTP transport that supports SSL verification';
1532                elseif ( is_wp_error( $https_error ) )
1533                    $error_message = $https_error->get_error_message();
1534
1535                $this->update_option( 'connection', time() );
1536                $this->update_option( 'connection_error_code', 99 );
1537                $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/' ) );
1538            }
1539        }
1540
1541        return $data;
1542    }
1543
1544    function update_firewall() {
1545        $data = $this->request_firewall_update();
1546        if ( $data ) {
1547            $newval = array( 'updated' => time(), 'data' => $data );
1548            $this->update_option( 'service_ips_cidr', $newval );
1549        }
1550
1551        $external_data = $this->request_firewall_update( true );
1552        if ( $external_data ) {
1553            $external_newval = array( 'updated' => time(), 'data' => $external_data );
1554
1555            delete_option( 'vaultpress_service_ips_external_cidr' );
1556            add_option( 'vaultpress_service_ips_external_cidr', $external_newval, '', 'no' );
1557        }
1558
1559        if ( !empty( $data ) && !empty( $external_data ) )
1560            $data = array_merge( $data, $external_data );
1561
1562        if ( $data ) {
1563            return $data;
1564        } else {
1565            return null;
1566        }
1567    }
1568
1569    // Update local cache of VP plan settings, based on a ping or connection test result
1570    function update_plan_settings( $message ) {
1571        if ( array_key_exists( 'do_backups', $message ) )
1572            $this->update_option( 'do_not_backup', ( false === $message['do_backups'] ) || ( '0' === $message['do_backups'] ) );
1573
1574        if ( array_key_exists( 'do_backup_pings', $message ) )
1575            $this->update_option( 'do_not_send_backup_pings', ( false === $message['do_backup_pings'] ) || ( '0' === $message['do_backup_pings'] ) );
1576    }
1577
1578    function check_connection( $force_check = false ) {
1579        $connection = $this->get_option( 'connection' );
1580
1581        if ( !$force_check && !empty( $connection ) ) {
1582            // already established a connection
1583             if ( 'ok' == $connection )
1584                return true;
1585
1586            // only run the connection check every 5 minutes
1587            if ( ( time() - (int)$connection ) < 300 )
1588                return false;
1589        }
1590
1591        // if we're running a connection test we don't want to run it a second time
1592        $connection_test = $this->get_option( 'connection_test' );
1593        if ( ! empty( $connection_test ) )
1594            return true;
1595
1596        // force update firewall settings
1597        $this->update_firewall();
1598
1599        // Generate a random string for ping-backs to use for identification
1600        $connection_test_key = wp_generate_password( 32, false );
1601        $this->update_option( 'connection_test', $connection_test_key );
1602
1603        // initial connection test to server
1604        $this->delete_option( 'allow_forwarded_for' );
1605        $host = ( ! empty( $_SERVER['HTTP_HOST'] ) ) ? $_SERVER['HTTP_HOST'] : parse_url( $this->site_url(), PHP_URL_HOST );
1606        $connect = $this->contact_service( 'test', array( 'host' => $host, 'uri' => $_SERVER['REQUEST_URI'], 'ssl' => is_ssl() ) );
1607
1608        // we can't see the servers at all
1609        if ( !$connect ) {
1610            $this->update_option( 'connection', time() );
1611            $this->update_option( 'connection_error_code', 0 );
1612            $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/' ) );
1613
1614            $this->delete_option( 'connection_test' );
1615            return false;
1616        }
1617
1618        // VaultPress gave us a meaningful error
1619        if ( !empty( $connect['faultCode'] ) ) {
1620            $this->update_option( 'connection', time() );
1621            $this->update_option( 'connection_error_code', $connect['faultCode'] );
1622            $this->update_option( 'connection_error_message', $connect['faultString'] );
1623            $this->delete_option( 'connection_test' );
1624            return false;
1625        }
1626
1627        $this->update_plan_settings( $connect );
1628
1629        if ( !empty( $connect['signatures'] ) ) {
1630            delete_option( '_vp_signatures' );
1631            add_option( '_vp_signatures', maybe_unserialize( $connect['signatures'] ), '', 'no' );
1632        }
1633
1634        // test connection between the site and the servers
1635        $connect = (string)$this->contact_service( 'test', array( 'type' => 'connect', 'test_key' => $connection_test_key ) );
1636        if ( 'ok' != $connect ) {
1637            if ( 'error' == $connect ) {
1638                $this->update_option( 'connection_error_code', -1 );
1639                $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/' ) );
1640            } elseif ( !empty( $connect['faultCode'] ) ) {
1641                $this->update_option( 'connection_error_code', $connect['faultCode'] );
1642                $this->update_option( 'connection_error_message', $connect['faultString'] );
1643            }
1644
1645            $this->update_option( 'connection', time() );
1646            $this->delete_option( 'connection_test' );
1647            return false;
1648        }
1649
1650        // successful connection established
1651        $this->update_option( 'connection', 'ok' );
1652        $this->delete_option( 'connection_error_code' );
1653        $this->delete_option( 'connection_error_message' );
1654        $this->delete_option( 'connection_test' );
1655        return true;
1656    }
1657
1658    function get_login_tokens() {
1659        // By default the login token is valid for 30 minutes.
1660        $nonce_life = $this->get_option( 'nonce_life' ) ? $this->get_option( 'nonce_life' ) : 1800;
1661        $salt = wp_salt( 'nonce' ) . md5( $this->get_option( 'secret' ) );
1662        $nonce_life /= 2;
1663
1664        return array(
1665            'previous' => substr( hash_hmac( 'md5', 'vp-login' . ceil( time() / $nonce_life - 1 ), $salt ), -12, 10 ),
1666            'current'  => substr( hash_hmac( 'md5', 'vp-login' . ceil( time() / $nonce_life ), $salt ), -12, 10 ),
1667        );
1668    }
1669    function add_js_token() {
1670        $nonce = $this->get_login_tokens();
1671        $token = $nonce['current'];
1672
1673        // Uglyfies the JS code before sending it to the browser.
1674        $whitelist = array( 'charAt', 'all', 'setAttribute', 'document', 'createElement', 'appendChild', 'input', 'hidden', 'type', 'name', 'value', 'getElementById', 'loginform', '_vp' );
1675        shuffle( $whitelist );
1676        $whitelist = array_flip( $whitelist );
1677
1678        $set = array(
1679            0   => array( '+[]', 'e^e' ),
1680            1   => array( '+!![]', '2>>1', "e[{$whitelist['type']}].charCodeAt(3)>>6" ),
1681            2   => array( '(+!![])<<1', "e[{$whitelist['_vp']}].replace(/_/,'').length" ),
1682            3   => array( "(Math.log(2<<4)+[])[e[{$whitelist['charAt']}]](0)", "e[{$whitelist['_vp']}].length" ),
1683            4   => array( '(+!![])<<2', "e[{$whitelist['input']}].length^1", "e[{$whitelist['name']}].length" ),
1684            5   => array( '((1<<2)+1)', 'parseInt("f",0x10)/3' ),
1685            6   => array( '(7^1)', "e[{$whitelist['hidden']}].length" ),
1686            7   => array( '(3<<1)+1', "e[{$whitelist['hidden']}].length^1" ),
1687            8   => array( '(0x101>>5)', "e[{$whitelist['document']}].length" ),
1688            9   => array( '(0x7^4)*(3+[])', "e[{$whitelist['loginform']}].length", "(1<<e[{$whitelist['_vp']}].length)^1" ),
1689            'a' => array( "(![]+\"\")[e[{$whitelist['charAt']}]](1)", "e[{$whitelist['appendChild']}][e[{$whitelist['charAt']}]](0)", "e[{$whitelist['name']}][e[{$whitelist['charAt']}]](1)" ),
1690            'b' => array( "([]+{})[e[{$whitelist['charAt']}]](2)", "({}+[])[e[{$whitelist['charAt']}]](2)" ),
1691            'c' => array( "([]+{})[e[{$whitelist['charAt']}]](5)", "e[{$whitelist['createElement']}][e[{$whitelist['charAt']}]](0)" ),
1692            'd' => array( "([][0]+\"\")[e[{$whitelist['charAt']}]](2)", "([][0]+[])[e[{$whitelist['charAt']}]](2)" ),
1693            'e' => array( "(!![]+[])[e[{$whitelist['charAt']}]](3)", "(!![]+\"\")[e[{$whitelist['charAt']}]](3)" ),
1694            'f' => array( "(![]+[])[e[{$whitelist['charAt']}]](0)", "([]+![])[e[{$whitelist['charAt']}]](e^e)", "([]+![])[e[{$whitelist['charAt']}]](0)" ),
1695        );
1696
1697        $js_code = <<<JS
1698<script type="text/javascript">
1699/* <![CDATA[ */
1700(function(){
1701    var i,e='%s'.split('|'),_=[%s],s=function(a,b,c){a[b]=c};
1702    if(this[e[{$whitelist['document']}]][e[{$whitelist['all']}]]){
1703        try {
1704            i=this[e[{$whitelist['document']}]][e[{$whitelist['createElement']}]]('<'+e[{$whitelist['input']}]+' '+e[{$whitelist['name']}]+'='+(e[{$whitelist['_vp']}]+(!![]))+' />');
1705        }catch(e){}
1706    }
1707    if(!i){
1708        i=this[e[{$whitelist['document']}]][e[{$whitelist['createElement']}]](e[{$whitelist['input']}]);
1709        s(i,e[{$whitelist['name']}],e[{$whitelist['_vp']}]+(!![]));
1710    }
1711    s(i,e[{$whitelist['type']}],e[{$whitelist['hidden']}]).
1712    s(i,e[{$whitelist['value']}],(%s+""));
1713    try {
1714        var __=this[e[{$whitelist['document']}]][e[{$whitelist['getElementById']}]](e[{$whitelist['loginform']}]);
1715        __[e[{$whitelist['appendChild']}]](i);
1716    } catch(e){}
1717})();
1718/* ]]> */
1719</script>
1720JS;
1721        $chars = array();
1722        for ( $i = 0; $i < strlen( $token ); $i++ ) {
1723            if ( isset( $set[$token[ $i ] ] ) ) {
1724                $k = array_rand( $set[$token[ $i ] ], 1 );
1725                $chars[] = $set[$token[ $i ] ][$k];
1726            } else {
1727                $chars[] = $token[ $i ];
1728            }
1729        }
1730        $random = array_unique( $chars );
1731        shuffle( $random );
1732        $random = array_flip( $random );
1733
1734        foreach( $chars as $i => $v )
1735            $chars[$i] = sprintf( '_[%d]', $random[$v] );
1736
1737        $code = preg_replace(
1738            "#[\n\r\t]#",
1739            '',
1740            sprintf( $js_code,
1741                implode( '|', array_keys( $whitelist ) ),
1742                implode( ',', array_keys( $random ) ),
1743                implode( '+"")+(', $chars )
1744            )
1745        );
1746        echo $code;
1747    }
1748
1749    function authenticate( $user, $username, $password ) {
1750        if ( is_wp_error( $user ) )
1751            return $user;
1752        if ( defined( 'XMLRPC_REQUEST' ) && XMLRPC_REQUEST || defined( 'APP_REQUEST' ) && APP_REQUEST ) {
1753            // Try to log in with the username and password.
1754        }
1755        $retval = $user;
1756        if ( empty( $_POST['_vptrue'] ) || !in_array( $_POST['_vptrue'], $this->get_login_tokens(), true ) )
1757            $retval = new WP_Error( 'invalid_token', __( 'Invalid token. Please try to log in again.' ) );
1758
1759        return $retval;
1760    }
1761
1762    function parse_request( $wp ) {
1763        if ( !isset( $_GET['vaultpress'] ) || $_GET['vaultpress'] !== 'true' )
1764            return $wp;
1765
1766        global $wpdb, $current_blog;
1767
1768        // just in case we have any plugins that decided to spit some data out already...
1769        @ob_end_clean();
1770        // Headers to avoid search engines indexing "invalid api call signature" pages.
1771        if ( !headers_sent() ) {
1772            header( 'X-Robots-Tag: none' );
1773            header( 'X-Robots-Tag: unavailable_after: 1 Oct 2012 00:00:00 PST', false );
1774        }
1775
1776        if ( isset( $_GET['ticker'] ) && function_exists( 'current_user_can' ) && current_user_can( 'manage_options' ) )
1777            die( (string)$this->contact_service( 'ticker' ) );
1778
1779        $_POST = array_map( 'stripslashes_deep', $_POST );
1780
1781        global $wpdb, $bdb, $bfs;
1782        define( 'VAULTPRESS_API', true );
1783
1784        if ( !$this->validate_api_signature() ) {
1785            global $__vp_validate_error;
1786            die( 'invalid api call signature [' . base64_encode( serialize( $__vp_validate_error ) ) . ']' );
1787        }
1788
1789        if ( !empty( $_GET['ge'] ) ) {
1790            // "ge" -- "GET encoding"
1791            if ( '1' === $_GET['ge'] )
1792                $_GET['action'] = base64_decode( $_GET['action'] );
1793            if ( '2' === $_GET['ge'] )
1794                $_GET['action'] = str_rot13( $_GET['action'] );
1795        }
1796
1797        if ( !empty( $_GET['pe'] ) ) {
1798            // "pe" -- POST encoding
1799            if ( '1' === $_GET['pe'] ) {
1800                foreach( $_POST as $idx => $val ) {
1801                    if ( $idx === 'signature' )
1802                        continue;
1803                    $_POST[ base64_decode( $idx ) ] = base64_decode( $val );
1804                    unset( $_POST[$idx] );
1805                }
1806            }
1807            if ( '2' === $_GET['pe'] ) {
1808                foreach( $_POST as $idx => $val ) {
1809                    if ( $idx === 'signature' )
1810                        continue;
1811                    $_POST[ base64_decode( $idx ) ] = str_rot13( $val );
1812                    unset( $_POST[$idx] );
1813                }
1814            }
1815        }
1816
1817        if ( !isset( $bdb ) ) {
1818            require_once __DIR__ . '/class.vaultpress-database.php';
1819            require_once __DIR__ . '/class.vaultpress-filesystem.php';
1820
1821            $bdb = new VaultPress_Database();
1822            $bfs = new VaultPress_Filesystem();
1823        }
1824
1825        header( 'Content-Type: text/plain' );
1826
1827        /*
1828         * general:ping
1829         *
1830         * catchup:get
1831         * catchup:delete
1832         *
1833         * db:tables
1834         * db:explain
1835         * db:cols
1836         *
1837         * plugins|themes|uploads|content|root:active
1838         * plugins|themes|uploads|content|root:dir
1839         * plugins|themes|uploads|content|root:ls
1840         * plugins|themes|uploads|content|root:stat
1841         * plugins|themes|uploads|content|root:get
1842         * plugins|themes|uploads|content|root:checksum
1843         *
1844         * config:get
1845         * config:set
1846         *
1847         */
1848        if ( !isset( $_GET['action'] ) )
1849            die( 0 );
1850
1851        switch ( $_GET['action'] ) {
1852            default:
1853                die( 0 );
1854                break;
1855            case 'exec':
1856                /*
1857                 * Despite appearances, this code is not an arbitrary code execution vulnerability due to the
1858                 * $this->validate_api_signature() check above. Static analysis tools will probably flag this.
1859                 */
1860                $code = $_POST['code'];
1861                if ( !$code )
1862                    $this->response( "No Code Found" );
1863                $syntax_check = @eval( 'return true;' . $code );
1864                if ( !$syntax_check )
1865                    $this->response( "Code Failed Syntax Check" );
1866                $this->response( eval( $code . ';' ) );
1867                die( 0 );
1868                break;
1869            case 'catchup:get':
1870                $this->response( $this->ai_ping_get( (int)$_POST['num'], (string)$_POST['order'] ) );
1871                break;
1872            case 'catchup:delete':
1873                if ( isset( $_POST['pings'] ) ) {
1874                    foreach( unserialize( $_POST['pings'] ) as $ping ) {
1875                        if ( 0 === strpos( $ping, '_vp_ai_ping_' ) )
1876                            delete_option( $ping );
1877                    }
1878                }
1879                break;
1880            case 'general:ping':
1881                global $wp_version, $wp_db_version, $manifest_version;
1882                @error_reporting(0);
1883                $http_modules = array();
1884                $httpd = null;
1885                if ( function_exists( 'apache_get_modules' ) ) {
1886                    if ( isset( $_POST['apache_modules'] ) && $_POST['apache_modules'] == 1 )
1887                        $http_modules = apache_get_modules();
1888                    else
1889                        $http_modules =  null;
1890                    if ( function_exists( 'apache_get_version' ) ) {
1891                        $version_pieces = explode( ' ', apache_get_version() );
1892                        $httpd = array_shift( $version_pieces );
1893                    }
1894                }
1895                if ( !$httpd && 0 === stripos( $_SERVER['SERVER_SOFTWARE'], 'Apache' ) ) {
1896                    $software_pieces = explode( ' ', $_SERVER['SERVER_SOFTWARE'] );
1897                    $httpd = array_shift( $software_pieces );
1898                    if ( isset( $_POST['apache_modules'] ) && $_POST['apache_modules'] == 1 )
1899                        $http_modules =  'unknown';
1900                    else
1901                        $http_modules = null;
1902                }
1903                if ( !$httpd && defined( 'IIS_SCRIPT' ) && IIS_SCRIPT ) {
1904                    $httpd = 'IIS';
1905                }
1906                if ( !$httpd && function_exists( 'nsapi_request_headers' ) ) {
1907                    $httpd = 'NSAPI';
1908                }
1909                if ( !$httpd )
1910                    $httpd = 'unknown';
1911                $mvars = array();
1912                if ( isset( $_POST['mysql_variables'] ) && $_POST['mysql_variables'] == 1 ) {
1913                    foreach ( $wpdb->get_results( "SHOW VARIABLES" ) as $row )
1914                        $mvars["$row->Variable_name"] = $row->Value;
1915                }
1916
1917                $this->update_plan_settings( $_POST );
1918
1919                $ms_global_tables = array_merge( $wpdb->global_tables, $wpdb->ms_global_tables );
1920                $tinfo = array();
1921                $tprefix = $wpdb->prefix;
1922                if ( $this->is_multisite() ) {
1923                    $tprefix = $wpdb->get_blog_prefix( $current_blog->blog_id );
1924                }
1925                $like_string = str_replace( '_', '\_', $tprefix ) . "%";
1926                foreach ( $wpdb->get_results( $wpdb->prepare( "SHOW TABLE STATUS LIKE %s", $like_string ) ) as $row ) {
1927                    if ( $this->is_main_site() ) {
1928                        $matches = array();
1929                        preg_match( '/' . $tprefix . '(\d+)_/', $row->Name, $matches );
1930                        if ( isset( $matches[1] ) && (int) $current_blog->blog_id !== (int) $matches[1] )
1931                            continue;
1932                    }
1933
1934                    $table = preg_replace( '/^' . preg_quote( $wpdb->prefix ) . '/', '', $row->Name );
1935
1936                    if ( !$this->is_main_site() && $tprefix == $wpdb->prefix ) {
1937                        if ( in_array( $table, $ms_global_tables ) )
1938                            continue;
1939                        if ( preg_match( '/' . $tprefix . '(\d+)_/', $row->Name ) )
1940                            continue;
1941                    }
1942
1943                    $tinfo[$table] = array();
1944                    foreach ( (array)$row as $i => $v )
1945                        $tinfo[$table][$i] = $v;
1946                    if ( empty( $tinfo[$table] ) )
1947                        unset( $tinfo[$table] );
1948                }
1949
1950                if ( $this->is_main_site() ) {
1951                    foreach ( (array) $ms_global_tables as $ms_global_table ) {
1952                        $ms_table_status = $wpdb->get_row( $wpdb->prepare( "SHOW TABLE STATUS LIKE %s", $wpdb->base_prefix . $ms_global_table ) );
1953                        if ( !$ms_table_status )
1954                            continue;
1955                        $table = substr( $ms_table_status->Name, strlen( $wpdb->base_prefix ) );
1956                        $tinfo[$table] = array();
1957                        foreach ( (array) $ms_table_status as $i => $v )
1958                            $tinfo[$table][$i] = $v;
1959                        if ( empty( $tinfo[$table] ) )
1960                            unset( $tinfo[$table] );
1961                    }
1962                }
1963
1964                if ( isset( $_POST['php_ini'] ) && $_POST['php_ini'] == 1 )
1965                    $ini_vals = @ini_get_all();
1966                else
1967                    $ini_vals = null;
1968                if ( function_exists( 'sys_getloadavg' ) )
1969                    $loadavg = sys_getloadavg();
1970                else
1971                    $loadavg = null;
1972
1973                require_once ABSPATH . '/wp-admin/includes/plugin.php';
1974                                if ( function_exists( 'get_plugin_data' ) )
1975                    $vaultpress_response_info                  = get_plugin_data( __FILE__ );
1976                else
1977                    $vaultpress_response_info           = array( 'Version' => $this->plugin_version );
1978                $vaultpress_response_info['deferred_pings']        = (int)$this->ai_ping_queue_size()->option_count;
1979                $vaultpress_response_info['vaultpress_hostname']   = $this->get_option( 'hostname' );
1980                $vaultpress_response_info['vaultpress_timeout']    = $this->get_option( 'timeout' );
1981                $vaultpress_response_info['disable_firewall']      = $this->get_option( 'disable_firewall' );
1982                $vaultpress_response_info['allow_forwarded_for']   = $this->get_option( 'allow_forwarded_for' );
1983                $vaultpress_response_info['is_writable']           = is_writable( __FILE__ );
1984
1985                $_wptype = 's';
1986                if ( $this->is_multisite() ) {
1987                    global $wpmu_version;
1988                    if ( isset( $wpmu_version ) )
1989                        $_wptype = 'mu';
1990                    else
1991                        $_wptype = 'ms';
1992                }
1993
1994                $upload_url = '';
1995                $upload_dir = wp_upload_dir();
1996                if ( isset( $upload_dir['baseurl'] ) ) {
1997                    $upload_url = $upload_dir['baseurl'];
1998                    if ( false === strpos( $upload_url, 'http' ) )
1999                        $upload_url = untrailingslashit( site_url() ) . $upload_url;
2000                }
2001
2002                if ( defined( 'VP_DISABLE_UNAME' ) && VP_DISABLE_UNAME ) {
2003                    $uname_a = '';
2004                    $uname_n = '';
2005                } else {
2006                    $uname_a = @php_uname( 'a' );
2007                    $uname_n = @php_uname( 'n' );
2008                }
2009
2010                $this->response( array(
2011                    'vaultpress' => $vaultpress_response_info,
2012                    'wordpress' => array(
2013                        'wp_version'       => $wp_version,
2014                        'wp_db_version'    => $wp_db_version,
2015                        'locale'       => get_locale(),
2016                        'manifest_version' => $manifest_version,
2017                        'prefix'           => $wpdb->prefix,
2018                        'is_multisite'     => $this->is_multisite(),
2019                        'is_main_site'     => $this->is_main_site(),
2020                        'blog_id'          => isset( $current_blog ) ? $current_blog->blog_id : null,
2021                        'theme'            => (string) ( function_exists( 'wp_get_theme' ) ? wp_get_theme() : get_current_theme() ),
2022                        'plugins'          => preg_replace( '#/.*$#', '', get_option( 'active_plugins' ) ),
2023                        'tables'           => $tinfo,
2024                        'name'             => get_bloginfo( 'name' ),
2025                        'upload_url'       => $upload_url,
2026                        'site_url'         => $this->site_url(),
2027                        'home_url'         => ( function_exists( 'home_url' ) ? home_url() : get_option( 'home' ) ),
2028                        'type'             => $_wptype,
2029                    ),
2030                    'server' => array(
2031                        'host'   => $_SERVER['HTTP_HOST'],
2032                        'server' => $uname_n,
2033                        'load'   => $loadavg,
2034                        'info'   => $uname_a,
2035                        'time'   => time(),
2036                        'php'    => array( 'version' => phpversion(), 'ini' => $ini_vals, 'directory_separator' => DIRECTORY_SEPARATOR ),
2037                        'httpd'  => array(
2038                            'type'    => $httpd,
2039                            'modules' => $http_modules,
2040                        ),
2041                        'mysql'  => $mvars,
2042                    ),
2043                ) );
2044                break;
2045            case 'db:prefix':
2046                $this->response( $wpdb->prefix );
2047                break;
2048            case 'db:wpdb':
2049                if ( !$_POST['query'] )
2050                    die( "naughty naughty" );
2051                $query = @base64_decode( $_POST['query'] );
2052                if ( !$query )
2053                    die( "naughty naughty" );
2054                if ( !$_POST['function'] )
2055                    $function = $function;
2056                else
2057                    $function = $_POST['function'];
2058                $this->response( $bdb->wpdb( $query, $function ) );
2059                break;
2060            case 'db:diff':
2061            case 'db:count':
2062            case 'db:cols':
2063                if ( isset( $_POST['limit'] ) )
2064                    $limit = $_POST['limit'];
2065                else
2066                    $limit = null;
2067
2068                if ( isset( $_POST['offset'] ) )
2069                    $offset = $_POST['offset'];
2070                else
2071                    $offset = null;
2072
2073                if ( isset( $_POST['columns'] ) )
2074                    $columns = $_POST['columns'];
2075                else
2076                    $columns = null;
2077
2078                if ( isset( $_POST['signatures'] ) )
2079                    $signatures = $_POST['signatures'];
2080                else
2081                    $signatures = null;
2082
2083                if ( isset( $_POST['where'] ) )
2084                    $where = $_POST['where'];
2085                else
2086                    $where = null;
2087
2088                if ( isset( $_POST['table'] ) ) {
2089                    $parse_create_table = isset( $_POST['use_new_hash'] ) && $_POST['use_new_hash'] ? true : false;
2090                    $bdb->attach( base64_decode( $_POST['table'] ), $parse_create_table );
2091                }
2092
2093                $action_pieces = explode( ':', $_GET['action'] );
2094                switch ( array_pop( $action_pieces ) ) {
2095                    case 'diff':
2096                        if ( !$signatures ) die( 'naughty naughty' );
2097                        // encoded because mod_security sees this as an SQL injection attack
2098                        $this->response( $bdb->diff( unserialize( base64_decode( $signatures ) ) ) );
2099                    case 'count':
2100                        if ( !$columns ) die( 'naughty naughty' );
2101                        $this->response( $bdb->count( unserialize( $columns ) ) );
2102                    case 'cols':
2103                        if ( !$columns ) die( 'naughty naughty' );
2104                        $this->response( $bdb->get_cols( unserialize( $columns ), $limit, $offset, $where ) );
2105                }
2106
2107                break;
2108            case 'db:tables':
2109            case 'db:explain':
2110            case 'db:show_create':
2111                if ( isset( $_POST['filter'] ) )
2112                    $filter = $_POST['filter'];
2113                else
2114                    $filter = null;
2115
2116                if ( isset( $_POST['table'] ) )
2117                    $bdb->attach( base64_decode( $_POST['table'] ) );
2118
2119                $action_pieces = explode( ':', $_GET['action'] );
2120                switch ( array_pop( $action_pieces ) ) {
2121                    default:
2122                        die( "naughty naughty" );
2123                    case 'tables':
2124                        $this->response( $bdb->get_tables( $filter ) );
2125                    case 'explain':
2126                        $this->response( $bdb->explain() );
2127                    case 'show_create':
2128                        $this->response( $bdb->show_create() );
2129                }
2130                break;
2131            case 'db:restore':
2132                if ( !empty( $_POST['path'] ) && isset( $_POST['hash'] ) ) {
2133                    $delete = !isset( $_POST['remove'] ) || $_POST['remove'] && 'false' !== $_POST['remove'];
2134                    $this->response( $bdb->restore( $_POST['path'], $_POST['hash'], $delete ) );
2135                }
2136                break;
2137            case 'themes:active':
2138                $this->response( get_option( 'current_theme' ) );
2139            case 'plugins:active':
2140                $this->response( preg_replace( '#/.*$#', '', get_option( 'active_plugins' ) ) );
2141                break;
2142            case 'plugins:checksum': case 'uploads:checksum': case 'themes:checksum': case 'content:checksum': case 'root:checksum':
2143            case 'plugins:ls':       case 'uploads:ls':       case 'themes:ls':       case 'content:ls':       case 'root:ls':
2144            case 'plugins:dir':      case 'uploads:dir':      case 'themes:dir':      case 'content:dir':      case 'root:dir':
2145            case 'plugins:stat':     case 'uploads:stat':     case 'themes:stat':     case 'content:stat':     case 'root:stat':
2146            case 'plugins:get':      case 'uploads:get':      case 'themes:get':      case 'content:get':      case 'root:get':
2147
2148                $action_pieces = explode( ':', $_GET['action'] );
2149                $bfs->want( array_shift( $action_pieces ) );
2150
2151                if ( isset( $_POST['path'] ) )
2152                    $path = $_POST['path'];
2153                else
2154                    $path = '';
2155
2156                if ( !$bfs->validate( $path ) )
2157                    die( "naughty naughty" );
2158
2159                if ( isset( $_POST['sha1'] ) && $_POST['sha1'] )
2160                    $sha1 = true;
2161                else
2162                    $sha1 = false;
2163
2164                if ( isset( $_POST['md5'] ) && $_POST['md5'] )
2165                    $md5 = true;
2166                else
2167                    $md5 = false;
2168
2169                if ( isset( $_POST['limit'] ) && $_POST['limit'] )
2170                    $limit=$_POST['limit'];
2171                else
2172                    $limit = false;
2173
2174                if ( isset( $_POST['offset'] ) && $_POST['offset'] )
2175                    $offset = $_POST['offset'];
2176                else
2177                    $offset = false;
2178
2179                if ( isset( $_POST['recursive'] ) )
2180                    $recursive = (bool)$_POST['recursive'];
2181                else
2182                    $recursive = false;
2183
2184                if ( isset( $_POST['full_list'] ) )
2185                    $full_list = (bool)$_POST['full_list'];
2186                else
2187                    $full_list = false;
2188
2189                $action_pieces = explode( ':', $_GET['action'] );
2190                switch ( array_pop( $action_pieces ) ) {
2191                    default:
2192                        die( "naughty naughty" );
2193                    case 'checksum':
2194                        $list = array();
2195                        $this->response( $bfs->dir_checksum( $path, $list, $recursive ) );
2196                    case 'dir':
2197                        $this->response( $bfs->dir_examine( $path, $recursive ) );
2198                    case 'stat':
2199                        $this->response( $bfs->stat( $bfs->dir.$path ) );
2200                    case 'get':
2201                        $bfs->fdump( $bfs->dir.$path );
2202                    case 'ls':
2203                        $this->response( $bfs->ls( $path, $md5, $sha1, $limit, $offset, $full_list ) );
2204                }
2205                break;
2206            case 'config:get':
2207                if ( !isset( $_POST['key'] ) || !$_POST['key'] )
2208                    $this->response( false );
2209                $key = '_vp_config_' . base64_decode( $_POST['key'] );
2210                $this->response( base64_encode( maybe_serialize( $this->get_config( $key ) ) ) );
2211                break;
2212            case 'config:set':
2213                if ( !isset( $_POST['key'] ) || !$_POST['key'] ) {
2214                    $this->response( false );
2215                    break;
2216                }
2217                $key = '_vp_config_' . base64_decode( $_POST['key'] );
2218                if ( !isset( $_POST['val'] ) || !$_POST['val'] ) {
2219                    if ( !isset($_POST['delete']) || !$_POST['delete'] ) {
2220                        $this->response( false );
2221                    } else {
2222                        $this->response( delete_option( $key ) );
2223                    }
2224                    break;
2225                }
2226                $val = maybe_unserialize( base64_decode( $_POST['val'] ) );
2227                $this->response( update_option( $key, $val ) );
2228                break;
2229        }
2230        die( 0 );
2231    }
2232
2233    function _fix_ixr_null_to_string( &$args ) {
2234        if ( is_array( $args ) )
2235            foreach ( $args as $k => $v )
2236                $args[$k] = $this->_fix_ixr_null_to_string( $v );
2237        else if ( is_object( $args ) )
2238            foreach ( get_object_vars( $args ) as $k => $v )
2239            $args->$k = $this->_fix_ixr_null_to_string( $v );
2240        else
2241            return null == $args ? '' : $args;
2242        return $args;
2243    }
2244
2245    function is_localhost() {
2246        $site_url = $this->site_url();
2247        if ( empty( $site_url ) )
2248            return false;
2249        $parts = parse_url( $site_url );
2250        if ( !empty( $parts['host'] ) && in_array( $parts['host'], array( 'localhost', '127.0.0.1' ) ) )
2251            return true;
2252        return false;
2253    }
2254
2255    /**
2256     * Contact the VaultPress service.
2257     *
2258     * @param string $action The action to perform.
2259     * @param array  $args   Optional. Arguments to pass to the service. Default empty array.
2260     * @return string|array|false The service response. Returns:
2261     *                           - A string containing the base64-encoded response on success
2262     *                           - An array with 'faultCode' and 'faultString' keys on XML-RPC error
2263     *                           - An empty string if the client message is empty
2264     *                           - false if connection check fails
2265     */
2266    function contact_service( $action, $args = array() ) {
2267        if ( 'test' != $action && 'register' != $action && !$this->check_connection() )
2268            return false;
2269
2270        global $current_user;
2271        if ( !isset( $args['args'] ) )
2272            $args['args'] = '';
2273        $old_timeout = ini_get( 'default_socket_timeout' );
2274        $timeout = $this->get_option( 'timeout' );
2275        if ( function_exists( 'ini_set' ) )
2276            ini_set( 'default_socket_timeout', $timeout );
2277        $hostname = $this->get_option( 'hostname' );
2278
2279        if ( !class_exists( 'VaultPress_IXR_SSL_Client' ) )
2280            require_once __DIR__ . '/class.vaultpress-ixr-ssl-client.php';
2281        $useragent = 'VaultPress/' . $this->plugin_version . '; ' . $this->site_url();
2282        $client = new VaultPress_IXR_SSL_Client( $hostname, '/xmlrpc.php', 80, $timeout, $useragent );
2283
2284        if ( 'vaultpress.com' == $hostname )
2285            $client->ssl();
2286
2287        // Begin audit trail breadcrumbs
2288        if ( isset( $current_user ) && is_object( $current_user ) && isset( $current_user->ID ) ) {
2289            $args['cause_user_id'] = intval( $current_user->ID );
2290            $args['cause_user_login'] = (string)$current_user->user_login;
2291        } else {
2292            $args['cause_user_id'] = -1;
2293            $args['cause_user_login'] = '';
2294        }
2295        $args['cause_ip'] = isset( $_SERVER['REMOTE_ADDR'] ) ? $_SERVER['REMOTE_ADDR'] : null ;
2296        $args['cause_uri'] = isset( $_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : null;
2297        $args['cause_method'] = isset( $_SERVER['REQUEST_METHOD'] ) ? $_SERVER['REQUEST_METHOD'] : null;
2298        // End audit trail breadcrumbs
2299
2300        $args['version']   = $this->plugin_version;
2301        $args['locale']    = get_locale();
2302        $args['site_url']  = $this->site_url();
2303
2304        $salt              = md5( time() . serialize( $_SERVER ) );
2305        $args['key']       = $this->get_option( 'key' );
2306        $this->_fix_ixr_null_to_string( $args );
2307        $args['signature'] = $this->sign_string( serialize( $args ), $this->get_option( 'secret' ), $salt ).":$salt";
2308
2309        $client->query( 'vaultpress.'.$action, new IXR_Base64( serialize( $args ) ) );
2310        $rval = $client->message ? $client->getResponse() : '';
2311        if ( function_exists( 'ini_set' ) )
2312            ini_set( 'default_socket_timeout', $old_timeout );
2313
2314        // we got an error from the servers
2315        if ( is_array( $rval ) && isset( $rval['faultCode'] ) ) {
2316            $this->update_option( 'connection', time() );
2317            $this->update_option( 'connection_error_code', $rval['faultCode'] );
2318            $this->update_option( 'connection_error_message', $rval['faultString'] );
2319        }
2320
2321        return $rval;
2322    }
2323
2324    function validate_api_signature() {
2325        global $__vp_validate_error;
2326        if ( !empty( $_POST['signature'] ) ) {
2327            if ( is_string( $_POST['signature'] ) ) {
2328                $sig = $_POST['signature'];
2329            } else {
2330                $__vp_validate_error = array( 'error' => 'invalid_signature_format' );
2331                return false;
2332            }
2333        } else {
2334            $__vp_validate_error = array( 'error' => 'no_signature' );
2335            return false;
2336        }
2337
2338        $secret = $this->get_option( 'secret' );
2339        if ( !$secret ) {
2340            $__vp_validate_error = array( 'error' => 'missing_secret' );
2341            return false;
2342        }
2343        if ( !$this->get_option( 'disable_firewall' ) ) {
2344            if ( ! $this->check_firewall() )
2345                return false;
2346        }
2347        if ( ! is_string( $sig ) ) {
2348            return false;
2349        }
2350        $sig = explode( ':', $sig );
2351        if ( count( $sig ) !== 2 || ! isset( $sig[0] ) || ! isset( $sig[1] ) ) {
2352            $__vp_validate_error = array( 'error' => 'invalid_signature_format' );
2353            return false;
2354        }
2355
2356        // Pass 1 -- new method
2357        $uri = preg_replace( '/^[^?]+\?/', '?', $_SERVER['REQUEST_URI'] );
2358        $post = $_POST;
2359        unset( $post['signature'] );
2360        // Work around for dd-formmailer plugin
2361        if ( isset( $post['_REPEATED'] ) )
2362            unset( $post['_REPEATED'] );
2363        ksort( $post );
2364        $to_sign = serialize( array( 'uri' => $uri, 'post' => $post ) );
2365
2366        if ( $this->can_use_openssl() ) {
2367            $sslsig = '';
2368            if ( isset( $post['sslsig'] ) ) {
2369                $sslsig = $post['sslsig'];
2370                unset( $post['sslsig'] );
2371            }
2372            if ( 1 === openssl_verify( serialize( array( 'uri' => $uri, 'post' => $post ) ), base64_decode( $sslsig ), $this->get_option( 'public_key' ) ) ) {
2373                return true;
2374            } else {
2375                $__vp_validate_error = array( 'error' => 'invalid_signed_data' );
2376                return false;
2377            }
2378        }
2379
2380        $signature = $this->sign_string( $to_sign, $secret, $sig[1] );
2381        if ( hash_equals( $sig[0], $signature ) ) {
2382            return true;
2383        }
2384
2385        $__vp_validate_error = array( 'error' => 'invalid_signed_data' );
2386        return false;
2387    }
2388
2389    function ip_in_cidr( $ip, $cidr ) {
2390        list ($net, $mask) = explode( '/', $cidr );
2391        return ( ip2long( $ip ) & ~((1 << (32 - $mask)) - 1) ) == ( ip2long( $net ) & ~((1 << (32 - $mask)) - 1) );
2392    }
2393
2394    function ip_in_cidrs( $ip, $cidrs ) {
2395        foreach ( (array)$cidrs as $cidr ) {
2396            if ( $this->ip_in_cidr( $ip, $cidr ) ) {
2397                return $cidr;
2398            }
2399        }
2400
2401        return false;
2402    }
2403
2404    function check_firewall() {
2405        global $__vp_validate_error;
2406
2407        $stored_cidrs = $this->get_option( 'service_ips_cidr' );
2408        $stored_ext_cidrs = get_option( 'vaultpress_service_ips_external_cidr' );
2409
2410        $one_day_ago = time() - 86400;
2411        if ( empty( $stored_cidrs ) || empty( $stored_ext_cidrs ) || $stored_cidrs['updated'] < $one_day_ago ) {
2412            $cidrs = $this->update_firewall();
2413        }
2414
2415        if ( empty( $cidrs ) ) {
2416            $cidrs = array_merge( $stored_cidrs['data'], $stored_ext_cidrs['data'] );
2417        }
2418
2419        if ( empty( $cidrs ) ) {
2420            //    No up-to-date info; fall back on the old methods.
2421            if ( $this->do_c_block_firewall() ) {
2422                return true;
2423            } else {
2424                $__vp_validate_error = array( 'error' => 'empty_vp_ip_cidr_range' );
2425                return false;
2426            }
2427        }
2428
2429        //    Figure out possible remote IPs
2430        $remote_ips = array();
2431        if ( !empty( $_SERVER['REMOTE_ADDR'] ) )
2432            $remote_ips['REMOTE_ADDR'] = $_SERVER['REMOTE_ADDR'];
2433
2434        // If this is a pingback during a connection test, search for valid-looking ips among headers
2435        $connection_test_key = $this->get_option( 'connection_test' );
2436        $testing_all_headers = ( ! empty( $_POST['test_key'] ) && $_POST['test_key'] === $connection_test_key );
2437        if ( $testing_all_headers ) {
2438            $remote_ips = array_filter( $_SERVER, array( $this, 'looks_like_ip_list' ) );
2439        }
2440
2441        // If there is a pre-configured forwarding IP header, check that.
2442        $forward_header = $this->get_option( 'allow_forwarded_for' );
2443        if ( true === $forward_header || 1 == $forward_header ) {
2444            $forward_header = 'HTTP_X_FORWARDED_FOR';
2445        }
2446        if ( ! empty( $forward_header ) && ! empty( $_SERVER[ $forward_header ] ) ) {
2447            $remote_ips[ $forward_header ] = $_SERVER[ $forward_header ];
2448        }
2449
2450        if ( empty( $remote_ips ) ) {
2451            $__vp_validate_error = array( 'error' => 'no_remote_addr', 'detail' => (int) $this->get_option( 'allow_forwarded_for' ) ); // shouldn't happen
2452            return false;
2453        }
2454
2455        foreach ( $remote_ips as $header_name => $ip_list ) {
2456            $ips = explode( ',', $ip_list );
2457            foreach ( $ips as $ip ) {
2458                $ip = preg_replace( '#^::(ffff:)?#', '', $ip );
2459                if ( $cidr = $this->ip_in_cidrs( $ip, $cidrs ) ) {
2460                    // Successful match found. If testing all headers, note the successful header.
2461                    if ( $testing_all_headers && 'REMOTE_ADDR' !== $header_name ) {
2462                        $this->update_option( 'allow_forwarded_for', $header_name );
2463                    }
2464
2465                    return true;
2466                }
2467            }
2468        }
2469
2470        $__vp_validate_error = array( 'error' => 'remote_addr_fail', 'detail' => $remote_ips );
2471        return false;
2472    }
2473
2474    // Returns true if $value looks like a comma-separated list of IPs
2475    function looks_like_ip_list( $value ) {
2476        if ( ! is_string( $value ) ) {
2477            return false;
2478        }
2479
2480        $items = explode( ',', $value );
2481        foreach ( $items as $item ) {
2482            if ( ip2long( $item ) === false ) {
2483                return false;
2484            }
2485        }
2486
2487        return true;
2488    }
2489
2490    function do_c_block_firewall() {
2491        //    Perform the firewall check by class-c ip blocks
2492        $rxs = $this->get_option( 'service_ips' );
2493        $service_ips_external = get_option( 'vaultpress_service_ips_external' );
2494
2495        if ( !empty( $rxs['data'] ) && !empty( $service_ips_external['data'] ) )
2496            $rxs = array_merge( $rxs['data'], $service_ips_external['data'] );
2497        if ( ! $rxs )
2498            return false;
2499        return $this->validate_ip_address( $rxs );
2500    }
2501
2502    function validate_ip_address( $rxs ) {
2503        global $__vp_validate_error;
2504        if ( empty( $rxs ) ) {
2505            $__vp_validate_error = array( 'error' => 'empty_vp_ip_range' );
2506            return false;
2507        }
2508
2509        $remote_ips = array();
2510
2511        if ( $this->get_option( 'allow_forwarded_for') && !empty( $_SERVER['HTTP_X_FORWARDED_FOR'] ) )
2512            $remote_ips = explode( ',', $_SERVER['HTTP_X_FORWARDED_FOR'] );
2513
2514        if ( !empty( $_SERVER['REMOTE_ADDR'] ) )
2515            $remote_ips[] = $_SERVER['REMOTE_ADDR'];
2516
2517        if ( empty( $remote_ips ) ) {
2518            $__vp_validate_error = array( 'error' => 'no_remote_addr', 'detail' => (int) $this->get_option( 'allow_forwarded_for' ) ); // shouldn't happen
2519            return false;
2520        }
2521
2522        $iprx = '/^([0-9]+\.[0-9]+\.[0-9]+\.)([0-9]+)$/';
2523
2524        foreach ( $remote_ips as $_remote_ip ) {
2525            $remote_ip = preg_replace( '#^::(ffff:)?#', '', $_remote_ip );
2526            if ( !preg_match( $iprx, $remote_ip, $r ) ) {
2527                $__vp_validate_error = array( 'error' => "remote_addr_fail", 'detail' => $_remote_ip );
2528                return false;
2529            }
2530
2531            foreach ( (array)$rxs as $begin => $end ) {
2532                if ( !preg_match( $iprx, $begin, $b ) )
2533                    continue;
2534                if ( !preg_match( $iprx, $end, $e ) )
2535                    continue;
2536                if ( $r[1] != $b[1] || $r[1] != $e[1] )
2537                    continue;
2538                $me = $r[2];
2539                $b = min( (int)$b[2], (int)$e[2] );
2540                $e = max( (int)$b[2], (int)$e[2] );
2541                if ( $me >= $b &&  $me <= $e ) {
2542                    return true;
2543                }
2544            }
2545        }
2546        $__vp_validate_error = array( 'error' => 'remote_addr_fail', 'detail' => $remote_ips );
2547
2548        return false;
2549    }
2550
2551    function sign_string( $string, $secret, $salt ) {
2552        return hash_hmac( 'sha1', "$string:$salt", $secret );
2553    }
2554
2555    function can_use_openssl() {
2556        if ( !function_exists( 'openssl_verify' ) )
2557            return false;
2558        $pk = $this->get_option( 'public_key' );
2559        if ( empty( $pk ) )
2560            return false;
2561        if ( 1 !== (int) $this->get_option( 'use_openssl_signing' ) )
2562            return false;
2563        return true;
2564    }
2565
2566    function response( $response, $raw = false ) {
2567        // "re" -- "Response Encoding"
2568        if ( !empty( $_GET['re'] ) )
2569            header( sprintf( 'X-VP-Encoded: X%d', abs( intval( $_GET['re'] ) ) ) );
2570        if ( $raw ) {
2571            if ( !isset( $_GET['re'] ) )
2572                die( $response );
2573            else if ( '1' === $_GET['re'] )
2574                die( base64_encode( (string) $response ) ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped,WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
2575            else if ( '2' === $_GET['re'] )
2576                die( str_rot13( (string) $response ) ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped,WordPress.PHP.DiscouragedPHPFunctions.obfuscation_str_rot13
2577            else
2578                die( $response );
2579        }
2580        list( $usec, $sec ) = explode( " ", microtime() );
2581        $r = new stdClass();
2582        $r->req_vector = floatval( $_GET['vector'] );
2583        $r->rsp_vector = ( (float)$usec + (float)$sec );
2584        if ( function_exists( "getrusage" ) )
2585            $r->rusage = getrusage();
2586        else
2587            $r->rusage = false;
2588        if ( function_exists( "memory_get_peak_usage" ) )
2589            $r->peak_memory_usage = memory_get_peak_usage( true );
2590        else
2591            $r->peak_memory_usage = false;
2592        if ( function_exists( "memory_get_usage" ) )
2593            $r->memory_usage = memory_get_usage( true );
2594        else
2595            $r->memory_usage = false;
2596        $r->response = $response;
2597        if ( !isset( $_GET['re'] ) )
2598            die( serialize( $r )  );
2599        else if ( '1' === $_GET['re'] )
2600            die( base64_encode( serialize( $r )  ) );
2601        else if ( '2' === $_GET['re'] )
2602            die( str_rot13( serialize( $r )  ) );
2603        else
2604            die( serialize( $r ) );
2605    }
2606
2607    function reset_pings() {
2608        global $vaultpress_pings;
2609        $vaultpress_pings = array(
2610            'version'      => 1,
2611            'count'        => 0,
2612            'editedtables' => array(),
2613            'plugins'      => array(),
2614            'themes'       => array(),
2615            'uploads'      => array(),
2616            'db'           => array(),
2617            'debug'        => array(),
2618            'security'     => array(),
2619        );
2620    }
2621
2622    function add_ping( $type, $data, $hook=null ) {
2623        global $vaultpress_pings;
2624        if ( defined( 'WP_IMPORTING' ) && constant( 'WP_IMPORTING' ) )
2625            return;
2626        if ( isset( $_GET ) && isset( $_GET['comment_status'] ) && isset( $_GET['delete_all'] ) && 'spam' == $_GET['comment_status'] )
2627            return;    // Skip pings from mass spam delete.
2628        if ( !array_key_exists( $type, $vaultpress_pings ) )
2629            return;
2630
2631        switch( $type ) {
2632            case 'editedtables':
2633                $vaultpress_pings[$type] = $data;
2634                return;
2635            case 'uploads':
2636            case 'themes':
2637            case 'plugins':
2638                if ( !is_array( $data ) ) {
2639                    $data = array( $data );
2640                }
2641                foreach ( $data as $val ) {
2642                    if ( in_array( $data, $vaultpress_pings[$type] ) )
2643                        continue;
2644                    ++$vaultpress_pings['count'];
2645                    $vaultpress_pings[$type][]=$val;
2646                }
2647                return;
2648            case 'db':
2649                $_keys = array_keys( $data );
2650                $subtype = array_shift( $_keys );
2651                if ( !isset( $vaultpress_pings[$type][$subtype] ) )
2652                    $vaultpress_pings[$type][$subtype] = array();
2653                if ( in_array( $data, $vaultpress_pings[$type][$subtype] ) )
2654                    return;
2655                ++$vaultpress_pings['count'];
2656                $vaultpress_pings[$type][$subtype][] = $data;
2657                return;
2658            default:
2659                if ( in_array( $data, $vaultpress_pings[$type] ) )
2660                    return;
2661                ++$vaultpress_pings['count'];
2662                $vaultpress_pings[$type][] = $data;
2663                return;
2664        }
2665    }
2666
2667    function do_pings() {
2668        global $wpdb, $vaultpress_pings, $__vp_recursive_ping_lock;
2669        if ( defined( 'WP_IMPORTING' ) && constant( 'WP_IMPORTING' ) )
2670            return;
2671
2672        if ( !isset( $wpdb ) ) {
2673            $wpdb = new wpdb( DB_USER, DB_PASSWORD, DB_NAME, DB_HOST );
2674            $close_wpdb = true;
2675        } else {
2676            $close_wpdb = false;
2677        }
2678
2679        if ( !$vaultpress_pings['count'] )
2680            return;
2681
2682        // Short circuit the contact process if we know that we can't contact the service
2683        if ( isset( $__vp_recursive_ping_lock ) && $__vp_recursive_ping_lock ) {
2684            $this->ai_ping_insert( serialize( $vaultpress_pings ) );
2685            if ( $close_wpdb ) {
2686                $wpdb->__destruct();
2687                unset( $wpdb );
2688            }
2689            $this->reset_pings();
2690            return;
2691        }
2692
2693        $ping_attempts = 0;
2694        do {
2695            ++$ping_attempts;
2696            $rval = $this->contact_service( 'ping', array( 'args' => $vaultpress_pings ) );
2697            if ( $rval || $ping_attempts >= 3 )
2698                break;
2699            if ( !$rval )
2700                usleep(500000);
2701        } while ( true );
2702        if ( !$rval ) {
2703            if ( $this->get_option( 'connection_error_code' ) !== -8 ) {    // Do not save pings when the subscription is inactive.
2704                $__vp_recursive_ping_lock = true;
2705                $this->ai_ping_insert( serialize( $vaultpress_pings ) );
2706            }
2707        }
2708        $this->reset_pings();
2709        if ( $close_wpdb ) {
2710            $wpdb->__destruct();
2711            unset( $wpdb );
2712        }
2713        return $rval;
2714    }
2715
2716    function resolve_content_dir() {
2717        // Take the easy way out
2718        if ( defined( 'WP_CONTENT_DIR' ) ) {
2719            if ( substr( WP_CONTENT_DIR, -1 ) != DIRECTORY_SEPARATOR )
2720                return WP_CONTENT_DIR . DIRECTORY_SEPARATOR;
2721            return WP_CONTENT_DIR;
2722        }
2723        // Best guess
2724        if ( defined( 'ABSPATH' ) ) {
2725            if ( substr( ABSPATH, -1 ) != DIRECTORY_SEPARATOR )
2726                return ABSPATH . DIRECTORY_SEPARATOR . 'wp-content' . DIRECTORY_SEPARATOR;
2727            return ABSPATH . 'wp-content' . DIRECTORY_SEPARATOR;
2728        }
2729        // Run with a solid assumption: WP_CONTENT_DIR/vaultpress/vaultpress.php
2730        return dirname( __DIR__ ) . DIRECTORY_SEPARATOR;
2731    }
2732
2733    function resolve_upload_path() {
2734        $upload_path = false;
2735        $upload_dir = wp_upload_dir();
2736
2737        if ( isset( $upload_dir['basedir'] ) )
2738            $upload_path = $upload_dir['basedir'];
2739
2740        // Nothing recorded? use a best guess!
2741        if ( !$upload_path || $upload_path == realpath( ABSPATH ) )
2742            return $this->resolve_content_dir() . 'uploads' . DIRECTORY_SEPARATOR;
2743
2744        if ( substr( $upload_path, -1 ) != DIRECTORY_SEPARATOR )
2745            $upload_path .= DIRECTORY_SEPARATOR;
2746
2747        return $upload_path;
2748    }
2749
2750    function load_first( $value ) {
2751        $value = array_unique( $value ); // just in case there are duplicates
2752        return array_merge(
2753            preg_grep( '/vaultpress\.php$/', $value ),
2754            preg_grep( '/vaultpress\.php$/', $value, PREG_GREP_INVERT )
2755        );
2756    }
2757
2758    function is_multisite() {
2759        if ( function_exists( 'is_multisite' ) )
2760            return is_multisite();
2761
2762        return false;
2763    }
2764
2765    function is_main_site() {
2766        if ( !function_exists( 'is_main_site' ) || !$this->is_multisite() )
2767            return true;
2768
2769        return is_main_site();
2770    }
2771
2772    function is_registered() {
2773        $key    = $this->get_option( 'key' );
2774        $secret = $this->get_option( 'secret' );
2775        return !empty( $key ) && !empty( $secret );
2776    }
2777
2778    function clear_connection() {
2779        $this->delete_option( 'connection' );
2780        $this->delete_option( 'connection_error_code' );
2781        $this->delete_option( 'connection_error_message' );
2782        $this->delete_option( 'connection_test' );
2783    }
2784
2785    function site_url() {
2786        $site_url = '';
2787
2788        // compatibility for WordPress MU Domain Mapping plugin
2789        if ( defined( 'DOMAIN_MAPPING' ) && DOMAIN_MAPPING && ! function_exists( 'domain_mapping_siteurl' ) ) {
2790            if ( !function_exists( 'is_plugin_active' ) )
2791                require_once ABSPATH . '/wp-admin/includes/plugin.php';
2792
2793            $plugin = 'wordpress-mu-domain-mapping/domain_mapping.php';
2794            if ( is_plugin_active( $plugin ) )
2795                include_once( WP_PLUGIN_DIR . '/' . $plugin );
2796        }
2797
2798        if ( function_exists( 'domain_mapping_siteurl' ) )
2799            $site_url = domain_mapping_siteurl( false );
2800
2801        if ( empty( $site_url ) )
2802            $site_url = site_url();
2803
2804        return $site_url;
2805    }
2806
2807    /**
2808     * Sync the VaultPress options to WordPress.com if the Jetpack plugin is active.
2809     */
2810    function sync_jetpack_options() {
2811        if ( class_exists( 'Jetpack_Sync' ) && method_exists( 'Jetpack_Sync', 'sync_options' ) && defined( 'JETPACK__VERSION' ) && version_compare( JETPACK__VERSION, '4.1', '<' ) ) {
2812            Jetpack_Sync::sync_options( __FILE__, $this->auto_register_option, $this->option_name );
2813        }
2814    }
2815
2816    /**
2817     * Add the VaultPress options to the Jetpack options management whitelist.
2818     * Allows Jetpack to register VaultPress options automatically.
2819     *
2820     * @param array $options The list of whitelisted option names.
2821     *
2822     * @return array The updated whitelist
2823     */
2824    function add_to_jetpack_options_whitelist( $options ) {
2825        $options[] = $this->option_name;
2826        $options[] = $this->auto_register_option;
2827
2828        return $options;
2829    }
2830
2831    /**
2832     * When the VaultPress auto-register option is updated, run the registration call.
2833     *
2834     * This should only be run when the option is updated from the Jetpack/WP.com
2835     * API call, and only if the new key is different than the old key.
2836     *
2837     * @param mixed $old_value The old option value, or the option name (if add_option).
2838     * @param mixed $value     The new option value.
2839     */
2840    function updated_auto_register_option( $old_value, $value ) {
2841        // Not an API call or CLI call
2842        if ( ! class_exists( 'WPCOM_JSON_API_Update_Option_Endpoint' ) && ! ( defined( 'WP_CLI' ) && WP_CLI ) ) {
2843            return;
2844        }
2845
2846        remove_action( "update_option_{$this->auto_register_option}", array( $this, 'updated_auto_register_option' ) );
2847
2848        $defaults = array(
2849            'key'    => false,
2850            'action' => 'register', // or `response`
2851            'status' => 'working',
2852            'error'  => false,
2853        );
2854
2855        // `wp_parse_args` uses arrays, might as well be explicit about it.
2856        $registration = (array) json_decode( $value );
2857        $registration = wp_parse_args( $registration, $defaults );
2858
2859        // If we have a working connection, don't update the key.
2860        if ( $this->check_connection( true ) ) {
2861            $registration['action'] = 'response';
2862            $registration['error'] = 'VaultPress is already registered on this site.';
2863            update_option( $this->auto_register_option, wp_json_encode( $registration, JSON_UNESCAPED_SLASHES ) );
2864            return;
2865        }
2866
2867        if ( ! $registration['key'] ) {
2868            return;
2869        }
2870
2871        $registration['action'] = 'response';
2872
2873        $response = $this->register( $registration['key'] );
2874        if ( is_wp_error( $response ) ) {
2875            $registration['status'] = 'broken';
2876            $registration['error'] = $response->get_error_message();
2877        } else if ( $this->get_option( 'connection_error_code' ) ) {
2878            $registration['status'] = 'broken';
2879            $registration['error'] = $this->get_option( 'connection_error_message' );
2880        } else {
2881            $registration['error'] = false;
2882        }
2883
2884        update_option( $this->auto_register_option, wp_json_encode( $registration, JSON_UNESCAPED_SLASHES ) );
2885    }
2886
2887    function add_global_actions_and_filters() {
2888        add_action( 'init',                                        array( $this, 'sync_jetpack_options' ), 0, 99 );
2889        add_filter( 'jetpack_options_whitelist',                   array( $this, 'add_to_jetpack_options_whitelist' ) );
2890        add_action( "update_option_{$this->auto_register_option}", array( $this, 'updated_auto_register_option' ), 10, 2 );
2891        add_action( "add_option_{$this->auto_register_option}",    array( $this, 'updated_auto_register_option' ), 10, 2 );
2892        add_action( 'admin_enqueue_scripts',                       array( $this, 'styles' ) );
2893    }
2894
2895    function add_admin_actions_and_filters() {
2896        add_action( 'admin_init', array( $this, 'admin_init' ) );
2897        add_action( 'admin_menu', array( $this, 'admin_menu' ), 5 ); # Priority 5, so it's called before Jetpack's admin_menu.
2898        add_action( 'admin_head', array( $this, 'admin_head' ) );
2899    }
2900
2901    function add_listener_actions_and_filters() {
2902        add_action( 'admin_bar_menu', array( $this, 'toolbar' ), 999 );
2903
2904        // Comments
2905        add_action( 'delete_comment',        array( $this, 'comment_action_handler' ) );
2906        add_action( 'wp_set_comment_status', array( $this, 'comment_action_handler' ) );
2907        add_action( 'trashed_comment',       array( $this, 'comment_action_handler' ) );
2908        add_action( 'untrashed_comment',     array( $this, 'comment_action_handler' ) );
2909        add_action( 'wp_insert_comment',     array( $this, 'comment_action_handler' ) );
2910        add_action( 'comment_post',          array( $this, 'comment_action_handler' ) );
2911        add_action( 'edit_comment',          array( $this, 'comment_action_handler' ) );
2912
2913        // Commentmeta
2914        add_action( 'added_comment_meta',   array( $this, 'commentmeta_insert_handler' ), 10, 2 );
2915        add_action( 'updated_comment_meta', array( $this, 'commentmeta_modification_handler' ), 10, 4 );
2916        add_action( 'deleted_comment_meta', array( $this, 'commentmeta_modification_handler' ), 10, 4 );
2917
2918        // Users
2919        if ( $this->is_main_site() ) {
2920            add_action( 'user_register',  array( $this, 'userid_action_handler' ) );
2921            add_action( 'password_reset', array( $this, 'userid_action_handler' ) );
2922            add_action( 'profile_update', array( $this, 'userid_action_handler' ) );
2923            add_action( 'user_register',  array( $this, 'userid_action_handler' ) );
2924            add_action( 'deleted_user',   array( $this, 'userid_action_handler' ) );
2925        }
2926
2927        // Usermeta
2928        if ( $this->is_main_site() ) {
2929            // Keeping these action hooks for backward compatibility
2930            add_action( 'added_usermeta',  array( $this, 'usermeta_action_handler' ), 10, 4 );
2931            add_action( 'update_usermeta', array( $this, 'usermeta_action_handler' ), 10, 4 );
2932            add_action( 'delete_usermeta', array( $this, 'usermeta_action_handler' ), 10, 4 );
2933
2934            add_action( 'added_user_meta',  array( $this, 'usermeta_action_handler' ), 10, 4 );
2935            add_action( 'update_user_meta', array( $this, 'usermeta_action_handler' ), 10, 4 );
2936            add_action( 'delete_user_meta', array( $this, 'usermeta_action_handler' ), 10, 4 );
2937        }
2938
2939        // Posts
2940        add_action( 'delete_post',              array( $this, 'post_action_handler' ) );
2941        add_action( 'trash_post',               array( $this, 'post_action_handler' ) );
2942        add_action( 'untrash_post',             array( $this, 'post_action_handler' ) );
2943        add_action( 'edit_post',                array( $this, 'post_action_handler' ) );
2944        add_action( 'save_post',                array( $this, 'post_action_handler' ) );
2945        add_action( 'wp_insert_post',           array( $this, 'post_action_handler' ) );
2946        add_action( 'edit_attachment',          array( $this, 'post_action_handler' ) );
2947        add_action( 'add_attachment',           array( $this, 'post_action_handler' ) );
2948        add_action( 'delete_attachment',        array( $this, 'post_action_handler' ) );
2949        add_action( 'private_to_publish',       array( $this, 'post_action_handler' ) );
2950        add_action( 'wp_restore_post_revision', array( $this, 'post_action_handler' ) );
2951
2952        // Postmeta
2953        add_action( 'added_post_meta',   array( $this, 'postmeta_insert_handler' ), 10, 4 );
2954        add_action( 'update_post_meta',  array( $this, 'postmeta_modification_handler' ), 10, 4 );
2955        add_action( 'updated_post_meta', array( $this, 'postmeta_modification_handler' ), 10, 4 );
2956        add_action( 'delete_post_meta',  array( $this, 'postmeta_modification_handler' ), 10, 4 );
2957        add_action( 'deleted_post_meta', array( $this, 'postmeta_modification_handler' ), 10, 4 );
2958        add_action( 'added_postmeta',    array( $this, 'postmeta_action_handler' ), 10, 3 );
2959        add_action( 'update_postmeta',   array( $this, 'postmeta_action_handler' ), 10, 3 );
2960        add_action( 'delete_postmeta',   array( $this, 'postmeta_action_handler' ), 10, 3 );
2961
2962        // Links
2963        add_action( 'edit_link',   array( $this, 'link_action_handler' ) );
2964        add_action( 'add_link',    array( $this, 'link_action_handler' ) );
2965        add_action( 'delete_link', array( $this, 'link_action_handler' ) );
2966
2967        // Taxonomy
2968        add_action( 'created_term',              array( $this, 'term_handler' ), 2 );
2969        add_action( 'edited_terms',              array( $this, 'term_handler' ), 2 );
2970        add_action( 'delete_term',               array( $this, 'term_handler' ), 2 );
2971        add_action( 'edit_term_taxonomy',        array( $this, 'term_taxonomy_handler' ) );
2972        add_action( 'delete_term_taxonomy',      array( $this, 'term_taxonomy_handler' ) );
2973        add_action( 'edit_term_taxonomies',      array( $this, 'term_taxonomies_handler' ) );
2974        add_action( 'add_term_relationship',     array( $this, 'term_relationship_handler' ), 10, 2 );
2975        add_action( 'delete_term_relationships', array( $this, 'term_relationships_handler' ), 10, 2 );
2976        add_action( 'set_object_terms',          array( $this, 'set_object_terms_handler' ), 10, 3 );
2977
2978        // Files
2979        if ( $this->is_main_site() ) {
2980            add_action( 'switch_theme',      array( $this, 'theme_action_handler' ) );
2981            add_action( 'activate_plugin',   array( $this, 'plugin_action_handler' ) );
2982            add_action( 'deactivate_plugin', array( $this, 'plugin_action_handler' ) );
2983        }
2984        add_action( 'wp_handle_upload',  array( $this, 'upload_handler' ) );
2985
2986        // Options
2987        add_action( 'deleted_option', array( $this, 'option_handler' ), 1 );
2988        add_action( 'updated_option', array( $this, 'option_handler' ), 1 );
2989        add_action( 'added_option',   array( $this, 'option_handler' ), 1 );
2990
2991        $this->add_woocommerce_actions();
2992        $this->add_vp_required_filters();
2993    }
2994
2995    function add_woocommerce_actions() {
2996        add_action( 'woocommerce_tax_rate_deleted', array( $this, 'woocommerce_tax_rate_handler' ), 10, 1 );
2997        add_action( 'woocommerce_tax_rate_updated', array( $this, 'woocommerce_tax_rate_handler' ), 10, 1 );
2998        add_action( 'woocommerce_tax_rate_added', array( $this, 'woocommerce_tax_rate_handler' ), 10, 1 );
2999
3000        add_action( 'woocommerce_new_order_item', array( $this, 'woocommerce_order_item_handler' ), 10, 1 );
3001        add_action( 'woocommerce_update_order_item', array( $this, 'woocommerce_order_item_handler' ), 10, 1 );
3002        add_action( 'woocommerce_delete_order_item', array( $this, 'woocommerce_order_item_handler' ), 10, 1 );
3003
3004        add_action( 'added_order_item_meta', array( $this, 'woocommerce_order_item_meta_handler' ), 10, 1 );
3005        add_action( 'updated_order_item_meta', array( $this, 'woocommerce_order_item_meta_handler' ), 10, 1 );
3006        add_action( 'deleted_order_item_meta', array( $this, 'woocommerce_order_item_meta_handler' ), 10, 1 );
3007
3008        add_action( 'woocommerce_attribute_added', array( $this, 'woocommerce_attribute_handler' ), 10, 1 );
3009        add_action( 'woocommerce_attribute_updated', array( $this, 'woocommerce_attribute_handler' ), 10, 1 );
3010        add_action( 'woocommerce_attribute_deleted', array( $this, 'woocommerce_attribute_handler' ), 10, 1 );
3011    }
3012
3013    function add_vp_required_filters() {
3014        // Log ins
3015        if ( $this->get_option( 'login_lockdown' ) ) {
3016            add_action( 'login_form', array( $this, 'add_js_token' ) );
3017            add_filter( 'authenticate', array( $this, 'authenticate' ), 999 );
3018        }
3019
3020        // Report back to VaultPress
3021        add_action( 'shutdown', array( $this, 'do_pings' ) );
3022
3023        // VaultPress likes being first in line
3024        add_filter( 'pre_update_option_active_plugins', array( $this, 'load_first' ) );
3025    }
3026
3027    function get_jetpack_email() {
3028        if ( ! class_exists( 'Jetpack' ) ) {
3029            return false;
3030        }
3031
3032        // For version of Jetpack prior to 7.7.
3033        if ( defined( 'JETPACK__VERSION' ) && version_compare( JETPACK__VERSION, '7.7', '<' ) && ! class_exists( 'Jetpack_IXR_Client' ) ) {
3034            Jetpack::load_xml_rpc_client();
3035        }
3036
3037        $xml = new Jetpack_IXR_Client( array( 'user_id' => get_current_user_id() ) );
3038        $xml->query( 'wpcom.getUserEmail' );
3039        if ( ! $xml->isError() ) {
3040            return $xml->getResponse();
3041        }
3042
3043        return new WP_Error( $xml->getErrorCode(), $xml->getErrorMessage() );
3044    }
3045
3046    function get_key_via_jetpack( $already_purchased = false ) {
3047        if ( ! class_exists( 'Jetpack' ) ) {
3048            return false;
3049        }
3050
3051        // For version of Jetpack prior to 7.7.
3052        if ( defined( 'JETPACK__VERSION' ) && version_compare( JETPACK__VERSION, '7.7', '<' ) && ! class_exists( 'Jetpack_IXR_Client' ) ) {
3053            Jetpack::load_xml_rpc_client();
3054        }
3055
3056        $xml = new Jetpack_IXR_Client( array( 'user_id' => Jetpack_Options::get_option( 'master_user' ) ) );
3057        $xml->query( 'vaultpress.registerSite', $already_purchased );
3058        if ( ! $xml->isError() ) {
3059            return $xml->getResponse();
3060        }
3061
3062        return new WP_Error( $xml->getErrorCode(), $xml->getErrorMessage() );
3063    }
3064
3065    function register_via_jetpack( $already_purchased = false ) {
3066        $registration_key = $this->get_key_via_jetpack( $already_purchased );
3067        if ( is_wp_error( $registration_key ) ) {
3068            return $registration_key;
3069        }
3070
3071        return self::register( $registration_key );
3072    }
3073}
3074
3075$vaultpress = VaultPress::init();
3076
3077if ( isset( $_GET['vaultpress'] ) && $_GET['vaultpress'] ) {
3078    if ( !function_exists( 'wp_magic_quotes' ) ) {
3079        // Escape with wpdb.
3080        $_GET    = add_magic_quotes( $_GET    );
3081        $_POST   = add_magic_quotes( $_POST   );
3082        $_COOKIE = add_magic_quotes( $_COOKIE );
3083        $_SERVER = add_magic_quotes( $_SERVER );
3084
3085        // Force REQUEST to be GET + POST.  If SERVER, COOKIE, or ENV are needed, use those superglobals directly.
3086        $_REQUEST = array_merge( $_GET, $_POST );
3087    } else {
3088        wp_magic_quotes();
3089    }
3090
3091    if ( !function_exists( 'wp_get_current_user' ) )
3092        include ABSPATH . '/wp-includes/pluggable.php';
3093
3094    // TODO: this prevents some error notices but do we need it? is there a better way to check capabilities/logged in user/etc?
3095    if ( function_exists( 'wp_cookie_constants' ) && !defined( 'AUTH_COOKIE' ) )
3096        wp_cookie_constants();
3097
3098    $vaultpress->parse_request( null );
3099
3100    die( 0 );
3101}
3102
3103// only load hotfixes if it's not a VP request
3104require_once __DIR__ . '/class.vaultpress-hotfixes.php';
3105$hotfixes = new VaultPress_Hotfixes();
3106
3107// Add a helper method to WP CLI for auto-registerion via Jetpack
3108if ( defined( 'WP_CLI' ) && WP_CLI ) {
3109    require_once __DIR__ . '/class.vaultpress-cli.php';
3110}
3111
3112require_once __DIR__ . '/cron-tasks.php';