PageRenderTime 111ms CodeModel.GetById 28ms RepoModel.GetById 1ms app.codeStats 0ms

/plugins/vaultpress/vaultpress.php

https://github.com/jquery/jquery-wp-content
PHP | 2220 lines | 1776 code | 290 blank | 154 comment | 416 complexity | 5b5105bda1bf3cc8030447a68ce509b7 MD5 | raw file
Possible License(s): AGPL-1.0

Large files files are truncated, but you can click here to view the full file

  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: 1.5.1
  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. // don't call the file directly
  14. if ( !defined( 'ABSPATH' ) )
  15. return;
  16. class VaultPress {
  17. var $option_name = 'vaultpress';
  18. var $db_version = 3;
  19. var $plugin_version = '1.5.1';
  20. function __construct() {
  21. register_activation_hook( __FILE__, array( $this, 'activate' ) );
  22. register_deactivation_hook( __FILE__, array( $this, 'deactivate' ) );
  23. $options = get_option( $this->option_name );
  24. if ( !is_array( $options ) )
  25. $options = array();
  26. $defaults = array(
  27. 'db_version' => 0,
  28. 'key' => '',
  29. 'secret' => '',
  30. 'connection' => false,
  31. 'service_ips' => false
  32. );
  33. $this->options = wp_parse_args( $options, $defaults );
  34. $this->reset_pings();
  35. $this->upgrade();
  36. if ( is_admin() )
  37. $this->add_admin_actions_and_filters();
  38. if ( $this->is_registered() ) {
  39. $do_not_backup = $this->get_option( 'do_not_backup' ) || $this->get_option( 'do_not_send_backup_pings' );
  40. if ( $do_not_backup )
  41. $this->add_vp_required_filters();
  42. else
  43. $this->add_listener_actions_and_filters();
  44. }
  45. }
  46. static function &init() {
  47. static $instance = false;
  48. if ( !$instance ) {
  49. $instance = new VaultPress();
  50. }
  51. return $instance;
  52. }
  53. function activate( $network_wide ) {
  54. $type = $network_wide ? 'network' : 'single';
  55. $this->update_option( 'activated', $type );
  56. // force a connection check after an activation
  57. $this->clear_connection();
  58. }
  59. function deactivate() {
  60. if ( $this->is_registered() )
  61. $this->contact_service( 'plugin_status', array( 'vp_plugin_status' => 'deactivated' ) );
  62. }
  63. function upgrade() {
  64. $current_db_version = $this->get_option( 'db_version' );
  65. if ( $current_db_version < 1 ) {
  66. $this->options['connection'] = get_option( 'vaultpress_connection' );
  67. $this->options['key'] = get_option( 'vaultpress_key' );
  68. $this->options['secret'] = get_option( 'vaultpress_secret' );
  69. $this->options['service_ips'] = get_option( 'vaultpress_service_ips' );
  70. // remove old options
  71. $old_options = array(
  72. 'vaultpress_connection',
  73. 'vaultpress_hostname',
  74. 'vaultpress_key',
  75. 'vaultpress_secret',
  76. 'vaultpress_service_ips',
  77. 'vaultpress_timeout',
  78. 'vp_allow_remote_execution',
  79. 'vp_debug_request_signing',
  80. 'vp_disable_firewall',
  81. );
  82. foreach ( $old_options as $option )
  83. delete_option( $option );
  84. $this->options['db_version'] = $this->db_version;
  85. $this->update_options();
  86. }
  87. if ( $current_db_version < 2 ) {
  88. $this->delete_option( 'timeout' );
  89. $this->delete_option( 'disable_firewall' );
  90. $this->update_option( 'db_version', $this->db_version );
  91. $this->clear_connection();
  92. }
  93. if ( $current_db_version < 3 ) {
  94. $this->update_firewall();
  95. $this->update_option( 'db_version', $this->db_version );
  96. $this->clear_connection();
  97. }
  98. }
  99. function get_option( $key ) {
  100. if ( 'hostname' == $key ) {
  101. if ( defined( 'VAULTPRESS_HOSTNAME' ) )
  102. return VAULTPRESS_HOSTNAME;
  103. else
  104. return 'vaultpress.com';
  105. }
  106. if ( 'timeout' == $key ) {
  107. if ( defined( 'VAULTPRESS_TIMEOUT' ) )
  108. return VAULTPRESS_TIMEOUT;
  109. else
  110. return 60;
  111. }
  112. if ( 'disable_firewall' == $key ) {
  113. if ( defined( 'VAULTPRESS_DISABLE_FIREWALL' ) )
  114. return VAULTPRESS_DISABLE_FIREWALL;
  115. else
  116. return false;
  117. }
  118. if ( isset( $this->options[$key] ) )
  119. return $this->options[$key];
  120. return false;
  121. }
  122. function update_option( $key, $value ) {
  123. $this->options[$key] = $value;
  124. $this->update_options();
  125. }
  126. function delete_option( $key ) {
  127. unset( $this->options[$key] );
  128. $this->update_options();
  129. }
  130. function update_options() {
  131. update_option( $this->option_name, $this->options );
  132. }
  133. function admin_init() {
  134. if ( !current_user_can( 'manage_options' ) )
  135. return;
  136. load_plugin_textdomain( 'vaultpress', false, dirname( plugin_basename( __FILE__ ) ) . '/languages/' );
  137. }
  138. function admin_head() {
  139. if ( !current_user_can( 'manage_options' ) )
  140. return;
  141. if ( $activated = $this->get_option( 'activated' ) ) {
  142. if ( 'network' == $activated ) {
  143. add_action( 'network_admin_notices', array( $this, 'activated_notice' ) );
  144. } else {
  145. foreach ( array( 'user_admin_notices', 'admin_notices' ) as $filter )
  146. add_action( $filter, array( $this, 'activated_notice' ) );
  147. }
  148. }
  149. // ask the user to connect their site w/ VP
  150. if ( !$this->is_registered() ) {
  151. foreach ( array( 'user_admin_notices', 'admin_notices' ) as $filter )
  152. add_action( $filter, array( $this, 'connect_notice' ) );
  153. // if we have an error make sure to let the user know about it
  154. } else {
  155. $error_code = $this->get_option( 'connection_error_code' );
  156. if ( !empty( $error_code ) ) {
  157. foreach ( array( 'user_admin_notices', 'admin_notices' ) as $filter )
  158. add_action( $filter, array( $this, 'error_notice' ) );
  159. }
  160. }
  161. }
  162. function admin_menu() {
  163. // if Jetpack is loaded then we need to wait for that menu to be added
  164. if ( class_exists( 'Jetpack' ) )
  165. add_action( 'jetpack_admin_menu', array( $this, 'load_menu' ) );
  166. else
  167. $this->load_menu();
  168. }
  169. function load_menu() {
  170. if ( class_exists( 'Jetpack' ) ) {
  171. $hook = add_submenu_page( 'jetpack', 'VaultPress', 'VaultPress', 'manage_options', 'vaultpress', array( $this, 'ui' ) );
  172. } else {
  173. $hook = add_menu_page( 'VaultPress', 'VaultPress', 'manage_options', 'vaultpress', array( $this, 'ui' ), 'div' );
  174. }
  175. add_action( "load-$hook", array( $this, 'ui_load' ) );
  176. add_action( 'admin_print_styles', array( $this, 'styles' ) );
  177. }
  178. function styles() {
  179. if ( !current_user_can( 'manage_options' ) || !is_admin() )
  180. return;
  181. wp_enqueue_style( 'vaultpress-nav', plugins_url( '/nav-styles.css', __FILE__ ), false, date( 'Ymd' ) );
  182. if ( isset( $_GET['page'] ) && 'vaultpress' == $_GET['page'] )
  183. wp_enqueue_style( 'vaultpress', plugins_url( '/styles.css', __FILE__ ), false, date( 'Ymd' ) );
  184. }
  185. // display a security threat notice if one exists
  186. function toolbar( $wp_admin_bar ) {
  187. global $wp_version;
  188. // these new toolbar functions were introduced in 3.3
  189. // http://codex.wordpress.org/Function_Reference/add_node
  190. if ( version_compare( $wp_version, '3.3', '<') )
  191. return;
  192. if ( !current_user_can( 'manage_options' ) )
  193. return;
  194. $messages = $this->get_messages();
  195. if ( !empty( $messages['security_notice_count'] ) ) {
  196. $count = (int)$messages['security_notice_count'];
  197. if ( $count > 0 ) {
  198. $count = number_format( $count, 0 );
  199. $wp_admin_bar->add_node( array(
  200. 'id' => 'vp-notice',
  201. 'title' => '<strong><span class="ab-icon"></span>' .
  202. sprintf( _n( '%s Security Threat', '%s Security Threats', $count , 'vaultpress'), $count ) .
  203. ' </strong>',
  204. 'parent' => 'top-secondary',
  205. 'href' => sprintf( 'https://dashboard.vaultpress.com/%d/security/', $messages['site_id'] ),
  206. 'meta' => array(
  207. 'title' => __( 'Visit VaultPress Security' , 'vaultpress'),
  208. 'onclick' => 'window.open( this.href ); return false;',
  209. 'class' => 'error'
  210. ),
  211. ) );
  212. }
  213. }
  214. }
  215. // get any messages from the VP servers
  216. function get_messages( $force_reload = false ) {
  217. $last_contact = $this->get_option( 'messages_last_contact' );
  218. // only run the messages check every 30 minutes
  219. if ( ( time() - (int)$last_contact ) > 1800 || $force_reload ) {
  220. $messages = base64_decode( $this->contact_service( 'messages', array() ) );
  221. $messages = unserialize( $messages );
  222. $this->update_option( 'messages_last_contact', time() );
  223. $this->update_option( 'messages', $messages );
  224. } else {
  225. $messages = $this->get_option( 'messages' );
  226. }
  227. return $messages;
  228. }
  229. function server_url() {
  230. if ( !isset( $this->_server_url ) ) {
  231. $scheme = is_ssl() ? 'https' : 'http';
  232. $this->_server_url = sprintf( '%s://%s/', $scheme, $this->get_option( 'hostname' ) );
  233. }
  234. return $this->_server_url;
  235. }
  236. // show message if plugin is activated but not connected to VaultPress
  237. function connect_notice() {
  238. if ( isset( $_GET['page'] ) && 'vaultpress' == $_GET['page'] )
  239. return;
  240. $message = sprintf(
  241. __( 'You must enter your registration key before VaultPress can back up and secure your site. <a href="%1$s">Register&nbsp;VaultPress</a>', 'vaultpress' ),
  242. admin_url( 'admin.php?page=vaultpress' )
  243. );
  244. $this->ui_message( $message, 'notice', __( 'VaultPress needs your attention!', 'vaultpress' ) );
  245. }
  246. // show message after activation
  247. function activated_notice() {
  248. if ( 'network' == $this->get_option( 'activated' ) ) {
  249. $message = sprintf(
  250. __( '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' ),
  251. 'https://dashboard.vaultpress.com/'
  252. );
  253. $this->ui_message( $message, 'activated', __( 'VaultPress has been activated across your network!', 'vaultpress' ) );
  254. // key and secret already exist in db
  255. } elseif ( $this->is_registered() ) {
  256. if ( $this->check_connection() ) {
  257. $message = sprintf(
  258. __( 'VaultPress has been registered and is currently backing up your site. <a href="%1$s">View Backup Status</a>', 'vaultpress' ),
  259. admin_url( 'admin.php?page=vaultpress' )
  260. );
  261. $this->ui_message( $message, 'registered', __( 'VaultPress has been activated!', 'vaultpress' ) );
  262. }
  263. }
  264. $this->delete_option( 'activated' );
  265. }
  266. function error_notice() {
  267. $error_message = $this->get_option( 'connection_error_message' );
  268. // link to the VaultPress page if we're not already there
  269. if ( !isset( $_GET['page'] ) || 'vaultpress' != $_GET['page'] )
  270. $error_message .= ' ' . sprintf( '<a href="%s">%s</a>', admin_url( 'admin.php?page=vaultpress' ), __( 'Visit&nbsp;the&nbsp;VaultPress&nbsp;page' , 'vaultpress') );
  271. $screen = get_current_screen();
  272. if ( !in_array( $screen->id, array( 'about', 'about-user', 'about-network' ) ) && !empty( $error_message ) )
  273. $this->ui_message( $error_message, 'error' );
  274. }
  275. function ui() {
  276. if ( !empty( $_GET['error'] ) ) {
  277. $this->error_notice();
  278. $this->clear_connection();
  279. }
  280. if ( !$this->is_registered() ) {
  281. $this->ui_register();
  282. return;
  283. }
  284. $status = $this->contact_service( 'status' );
  285. if ( !$status ) {
  286. $error_code = $this->get_option( 'connection_error_code' );
  287. if ( 0 == $error_code )
  288. $this->ui_fatal_error();
  289. else
  290. $this->ui_register();
  291. return;
  292. }
  293. $ticker = $this->contact_service( 'ticker' );
  294. if ( is_array( $ticker ) && isset( $ticker['faultCode'] ) ) {
  295. $this->error_notice();
  296. $this->ui_register();
  297. return;
  298. }
  299. $this->ui_main();
  300. }
  301. function ui_load() {
  302. if ( !current_user_can( 'manage_options' ) )
  303. return;
  304. // run code that might be updating the registration key
  305. if ( isset( $_POST['action'] ) && 'register' == $_POST['action'] ) {
  306. check_admin_referer( 'vaultpress_register' );
  307. // reset the connection info so messages don't cross
  308. $this->clear_connection();
  309. $registration_key = trim( $_POST[ 'registration_key' ] );
  310. if ( empty( $registration_key ) ) {
  311. $this->update_option( 'connection_error_code', 1 );
  312. $this->update_option(
  313. 'connection_error_message',
  314. sprintf(
  315. __( '<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' ),
  316. 'https://dashboard.vaultpress.com/'
  317. )
  318. );
  319. wp_redirect( admin_url( 'admin.php?page=vaultpress&error=true' ) );
  320. exit();
  321. }
  322. // try to register the plugin
  323. $nonce = wp_create_nonce( 'vp_register_' . $registration_key );
  324. $args = array( 'registration_key' => $registration_key, 'nonce' => $nonce );
  325. $response = $this->contact_service( 'register', $args );
  326. // we received an error from the VaultPress servers
  327. if ( !empty( $response['faultCode'] ) ) {
  328. $this->update_option( 'connection_error_code', $response['faultCode'] );
  329. $this->update_option( 'connection_error_message', $response['faultString'] );
  330. wp_redirect( admin_url( 'admin.php?page=vaultpress&error=true' ) );
  331. exit();
  332. }
  333. // make sure the returned data looks valid
  334. if ( empty( $response['key'] ) || empty( $response['secret'] ) || empty( $response['nonce'] ) || $nonce != $response['nonce'] ) {
  335. $this->update_option( 'connection_error_code', 1 );
  336. $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/' ) );
  337. wp_redirect( admin_url( 'admin.php?page=vaultpress&error=true' ) );
  338. exit();
  339. }
  340. // need to update these values in the db so the servers can try connecting to the plugin
  341. $this->update_option( 'key', $response['key'] );
  342. $this->update_option( 'secret', $response['secret'] );
  343. if ( $this->check_connection( true ) ) {
  344. wp_redirect( admin_url( 'admin.php?page=vaultpress' ) );
  345. exit();
  346. }
  347. // reset the key and secret
  348. $this->update_option( 'key', '' );
  349. $this->update_option( 'secret', '' );
  350. wp_redirect( admin_url( 'admin.php?page=vaultpress&error=true' ) );
  351. exit();
  352. }
  353. }
  354. function ui_register() {
  355. ?>
  356. <div id="vp-wrap" class="wrap">
  357. <div id="vp-head">
  358. <h2>VaultPress<a href="https://dashboard.vaultpress.com/" class="vp-visit-dashboard" target="_blank"><?php _e( 'Visit Dashboard', 'vaultpress' ); ?></a></h2>
  359. </div>
  360. <div id="vp_registration">
  361. <div class="vp_view-plans">
  362. <h1><?php _e( 'The VaultPress plugin <strong>requires a monthly&nbsp;subscription</strong>.', 'vaultpress' ); ?></h1>
  363. <p><?php _e( 'Get realtime backups, automated security scanning, and support from WordPress&nbsp;experts.', 'vaultpress' ); ?></p>
  364. <p class="vp_plans-btn"><a href="https://vaultpress.com/plugin/?utm_source=plugin-unregistered&amp;utm_medium=view-plans-and-pricing&amp;utm_campaign=1.0-plugin"><strong><?php _e( 'View plans and pricing&nbsp;&raquo;', 'vaultpress' ); ?></strong></a></p>
  365. </div>
  366. <div class="vp_register-plugin">
  367. <h3><?php _e( 'Already have a VaultPress&nbsp;account?', 'vaultpress' ); ?></h3>
  368. <p><?php _e( 'Paste your registration key&nbsp;below:', 'vaultpress' ); ?></p>
  369. <form method="post" action="">
  370. <fieldset>
  371. <textarea placeholder="<?php echo esc_attr( __( 'Enter your key here...', 'vaultpress' ) ); ?>" name="registration_key"></textarea>
  372. <button><strong><?php _e( 'Register ', 'vaultpress' ); ?></strong></button>
  373. <input type="hidden" name="action" value="register" />
  374. <?php wp_nonce_field( 'vaultpress_register' ); ?>
  375. </fieldset>
  376. </form>
  377. </div>
  378. </div>
  379. </div>
  380. <?php
  381. }
  382. function ui_main() {
  383. ?>
  384. <div id="vp-wrap" class="wrap">
  385. <?php
  386. $response = base64_decode( $this->contact_service( 'plugin_ui' ) );
  387. echo $response;
  388. ?>
  389. </div>
  390. <?php
  391. }
  392. function ui_fatal_error() {
  393. ?>
  394. <div id="vp-wrap" class="wrap">
  395. <h2>VaultPress</h2>
  396. <p><?php printf( __( 'Yikes! We&rsquo;ve run into a serious issue and can&rsquo;t connect to %1$s.', 'vaultpress' ), esc_html( $this->get_option( 'hostname' ) ) ); ?></p>
  397. <p><?php printf( __( 'Please make sure that your website is accessible via the Internet. If you&rsquo;re still having issues please <a href="%1$s">contact the VaultPress&nbsp;Safekeepers</a>.', 'vaultpress' ), 'http://vaultpress.com/contact/' ); ?></p>
  398. </div>
  399. <?php
  400. }
  401. function ui_message( $message, $type = 'notice', $heading = '' ) {
  402. if ( empty( $heading ) ) {
  403. switch ( $type ) {
  404. case 'error':
  405. $heading = __( 'Oops... there seems to be a problem.', 'vaultpress' );
  406. break;
  407. case 'success':
  408. $heading = __( 'Yay! Things look good.', 'vaultpress' );
  409. break;
  410. default:
  411. $heading = __( 'VaultPress needs your attention!', 'vaultpress' );
  412. break;
  413. }
  414. }
  415. ?>
  416. <div id="vp-notice" class="vp-<?php echo $type; ?> updated">
  417. <div class="vp-message">
  418. <h3><?php echo $heading; ?></h3>
  419. <p><?php echo $message; ?></p>
  420. </div>
  421. </div>
  422. <?php
  423. }
  424. function get_config( $key ) {
  425. $val = get_option( $key );
  426. if ( $val )
  427. return $val;
  428. switch( $key ) {
  429. case '_vp_config_option_name_ignore':
  430. $val = $this->get_option_name_ignore( true );
  431. update_option( '_vp_config_option_name_ignore', $val );
  432. break;
  433. case '_vp_config_post_meta_name_ignore':
  434. $val = $this->get_post_meta_name_ignore( true );
  435. update_option( '_vp_config_post_meta_name_ignore', $val );
  436. break;
  437. case '_vp_config_should_ignore_files':
  438. $val = $this->get_should_ignore_files( true );
  439. update_option( '_vp_config_should_ignore_files', $val );
  440. break;
  441. }
  442. return $val;
  443. }
  444. // Option name patterns to ignore
  445. function get_option_name_ignore( $return_defaults = false ) {
  446. $defaults = array(
  447. 'vaultpress',
  448. 'cron',
  449. 'wpsupercache_gc_time',
  450. 'rewrite_rules',
  451. 'akismet_spam_count',
  452. '/_transient_/',
  453. '/^_vp_/',
  454. );
  455. if ( $return_defaults )
  456. return $defaults;
  457. $ignore_names = $this->get_config( '_vp_config_option_name_ignore' );
  458. return array_unique( array_merge( $defaults, $ignore_names ) );
  459. }
  460. // post meta name patterns to ignore
  461. function get_post_meta_name_ignore( $return_defaults = false ) {
  462. $defaults = array(
  463. 'pvc_views'
  464. );
  465. if ( $return_defaults )
  466. return $defaults;
  467. $ignore_names = $this->get_config( '_vp_config_post_meta_name_ignore' );
  468. return array_unique( array_merge( $defaults, $ignore_names ) );
  469. }
  470. // file name patterns to ignore
  471. function get_should_ignore_files( $return_defaults = false ) {
  472. $defaults = array(
  473. '@.*/404\.log\.txt$@',
  474. '@.*/\.DS_Store$@',
  475. '@.*/debug\.log$@',
  476. '@.*\.timthumb\.txt$@',
  477. '@.*timthumb[A-Za-z0-9]*$@',
  478. '@.*wp-content/contents/cache/@',
  479. '@.*wp-content/content/cache/@',
  480. '@.*wp-content/cache/@',
  481. '@.*wp-content/old-cache/@',
  482. '@.*cache/@',
  483. '@.*wp-content/w3tc/@',
  484. '@.*owa/owa-data/caches/@',
  485. '@.*gt-cache@',
  486. '@.*/wpclicks/tracker/cache/@',
  487. '@.*amember/data/new_rewrite@',
  488. '@.*sucuri/blocks/@',
  489. '@.*/_sucuribackup.*@',
  490. '@.*sess_[0-9a-zA-Z]{32,}@',
  491. '@.*wp-content/backups.*@',
  492. '@.*wp-content/backupwordpress@',
  493. '@.*wp-content/backup-[0-9a-zA-Z]+@',
  494. '@.*wp-content/[0-9a-zA-Z]+-backups@',
  495. '@.*mwp_backups/@',
  496. '@.*managewp/backups/@',
  497. '@.*wp-snapshots/@',
  498. '@.*error_log.*@',
  499. '@.*/error-log.*@',
  500. '@.*/error\.log$@',
  501. '@.*/captcha/tmp.*@',
  502. '@.*\.mt_backup_[0-9a-z:_]*$@i',
  503. '@.*vp-uploaded-restore-.*@',
  504. );
  505. if ( $return_defaults )
  506. return $defaults;
  507. $ignore_names = (array) $this->get_config( '_vp_config_should_ignore_files' );
  508. return array_unique( array_merge( $defaults, $ignore_names ) );
  509. }
  510. ###
  511. ### Section: Backup Notification Hooks
  512. ###
  513. // Handle Handle Notifying VaultPress of Options Activity At this point the options table has already been modified
  514. //
  515. // Note: we handle deleted, instead of delete because VaultPress backs up options by name (which are unique,) that
  516. // means that we do not need to resolve an id like we would for, say, a post.
  517. function option_handler( $option_name ) {
  518. global $wpdb;
  519. // Step 1 -- exclusionary rules, don't send these options to vaultpress, because they
  520. // either change constantly and/or are inconsequential to the blog itself and/or they
  521. // are specific to the VaultPress plugin process and we want to avoid recursion
  522. $should_ping = true;
  523. $ignore_names = $this->get_option_name_ignore();
  524. foreach( (array)$ignore_names as $val ) {
  525. if ( $val{0} == '/' ) {
  526. if ( preg_match( $val, $option_name ) )
  527. $should_ping = false;
  528. } else {
  529. if ( $val == $option_name )
  530. $should_ping = false;
  531. }
  532. if ( !$should_ping )
  533. break;
  534. }
  535. if ( $should_ping )
  536. $this->add_ping( 'db', array( 'option' => $option_name ) );
  537. // Step 2 -- If WordPress is about to kick off a some "cron" action, we need to
  538. // flush vaultpress, because the "remote" cron threads done via http fetch will
  539. // be happening completely inside the window of this thread. That thread will
  540. // be expecting touched and accounted for tables
  541. if ( $option_name == '_transient_doing_cron' )
  542. $this->do_pings();
  543. return $option_name;
  544. }
  545. // Handle Notifying VaultPress of Comment Activity
  546. function comment_action_handler( $comment_id ) {
  547. if ( !is_array( $comment_id ) ) {
  548. if ( wp_get_comment_status( $comment_id ) != 'spam' )
  549. $this->add_ping( 'db', array( 'comment' => $comment_id ) );
  550. } else {
  551. foreach ( $comment_id as $id ) {
  552. if ( wp_get_comment_status( $comment_id ) != 'spam' )
  553. $this->add_ping( 'db', array( 'comment' => $id) );
  554. }
  555. }
  556. }
  557. // Handle Notifying VaultPress of Theme Switches
  558. function theme_action_handler( $theme ) {
  559. $this->add_ping( 'themes', array( 'theme' => get_option( 'stylesheet' ) ) );
  560. }
  561. // Handle Notifying VaultPress of Upload Activity
  562. function upload_handler( $file ) {
  563. $this->add_ping( 'uploads', array( 'upload' => str_replace( $this->resolve_upload_path(), '', $file['file'] ) ) );
  564. return $file;
  565. }
  566. // Handle Notifying VaultPress of Plugin Activation/Deactivation
  567. function plugin_action_handler( $plugin='' ) {
  568. $this->add_ping( 'plugins', array( 'name' => $plugin ) );
  569. }
  570. // Handle Notifying VaultPress of User Edits
  571. function userid_action_handler( $user_or_id ) {
  572. if ( is_object($user_or_id) )
  573. $userid = intval( $user_or_id->ID );
  574. else
  575. $userid = intval( $user_or_id );
  576. if ( !$userid )
  577. return;
  578. $this->add_ping( 'db', array( 'user' => $userid ) );
  579. }
  580. // Handle Notifying VaultPress of term changes
  581. function term_handler( $term_id, $tt_id=null ) {
  582. $this->add_ping( 'db', array( 'term' => $term_id ) );
  583. if ( $tt_id )
  584. $this->term_taxonomy_handler( $tt_id );
  585. }
  586. // Handle Notifying VaultPress of term_taxonomy changes
  587. function term_taxonomy_handler( $tt_id ) {
  588. $this->add_ping( 'db', array( 'term_taxonomy' => $tt_id ) );
  589. }
  590. // add(ed)_term_taxonomy handled via the created_term hook, the term_taxonomy_handler is called by the term_handler
  591. // Handle Notifying VaultPress of term_taxonomy changes
  592. function term_taxonomies_handler( $tt_ids ) {
  593. foreach( (array)$tt_ids as $tt_id ) {
  594. $this->term_taxonomy_handler( $tt_id );
  595. }
  596. }
  597. // Handle Notifying VaultPress of term_relationship changes
  598. function term_relationship_handler( $object_id, $term_id ) {
  599. $this->add_ping( 'db', array( 'term_relationship' => array( 'object_id' => $object_id, 'term_taxonomy_id' => $term_id ) ) );
  600. }
  601. // Handle Notifying VaultPress of term_relationship changes
  602. function term_relationships_handler( $object_id, $term_ids ) {
  603. foreach ( (array)$term_ids as $term_id ) {
  604. $this->term_relationship_handler( $object_id, $term_id );
  605. }
  606. }
  607. // Handle Notifying VaultPress of term_relationship changes
  608. function set_object_terms_handler( $object_id, $terms, $tt_ids ) {
  609. $this->term_relationships_handler( $object_id, $tt_ids );
  610. }
  611. // Handle Notifying VaultPress of UserMeta changes
  612. function usermeta_action_handler( $umeta_id, $user_id, $meta_key, $meta_value='' ) {
  613. $this->add_ping( 'db', array( 'usermeta' => $umeta_id ) );
  614. }
  615. // Handle Notifying VaultPress of Post Changes
  616. function post_action_handler($post_id) {
  617. if ( current_filter() == 'delete_post' )
  618. return $this->add_ping( 'db', array( 'post' => $post_id ), 'delete_post' );
  619. return $this->add_ping( 'db', array( 'post' => $post_id ), 'edit_post' );
  620. }
  621. // Handle Notifying VaultPress of Link Changes
  622. function link_action_handler( $link_id ) {
  623. $this->add_ping( 'db', array( 'link' => $link_id ) );
  624. }
  625. // Handle Notifying VaultPress of Commentmeta Changes
  626. function commentmeta_insert_handler( $meta_id, $comment_id=null ) {
  627. if ( empty( $comment_id ) || wp_get_comment_status( $comment_id ) != 'spam' )
  628. $this->add_ping( 'db', array( 'commentmeta' => $meta_id ) );
  629. }
  630. function commentmeta_modification_handler( $meta_id, $object_id, $meta_key, $meta_value ) {
  631. if ( !is_array( $meta_id ) )
  632. return $this->add_ping( 'db', array( 'commentmeta' => $meta_id ) );
  633. foreach ( $meta_id as $id ) {
  634. $this->add_ping( 'db', array( 'commentmeta' => $id ) );
  635. }
  636. }
  637. // Handle Notifying VaultPress of PostMeta changes via newfangled metadata functions
  638. function postmeta_insert_handler( $meta_id, $post_id, $meta_key, $meta_value='' ) {
  639. if ( in_array( $meta_key, $this->get_post_meta_name_ignore() ) )
  640. return;
  641. $this->add_ping( 'db', array( 'postmeta' => $meta_id ) );
  642. }
  643. function postmeta_modification_handler( $meta_id, $object_id, $meta_key, $meta_value ) {
  644. if ( in_array( $meta_key, $this->get_post_meta_name_ignore() ) )
  645. return;
  646. if ( !is_array( $meta_id ) )
  647. return $this->add_ping( 'db', array( 'postmeta' => $meta_id ) );
  648. foreach ( $meta_id as $id ) {
  649. $this->add_ping( 'db', array( 'postmeta' => $id ) );
  650. }
  651. }
  652. // Handle Notifying VaultPress of PostMeta changes via old school cherypicked hooks
  653. function postmeta_action_handler( $meta_id, $post_id = null, $meta_key = null ) {
  654. if ( in_array( $meta_key, $this->get_post_meta_name_ignore() ) )
  655. return;
  656. if ( !is_array($meta_id) )
  657. return $this->add_ping( 'db', array( 'postmeta' => $meta_id ) );
  658. foreach ( $meta_id as $id )
  659. $this->add_ping( 'db', array( 'postmeta' => $id ) );
  660. }
  661. function verify_table( $table ) {
  662. global $wpdb;
  663. $status = $wpdb->get_row( $wpdb->prepare( "SHOW TABLE STATUS WHERE Name = %s", $table ) );
  664. if ( !$status || !$status->Update_time || !$status->Comment || $status->Engine != 'MyISAM' )
  665. return true;
  666. if ( preg_match( '/([0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2})/', $status->Comment, $m ) )
  667. return ( $m[1] == $status->Update_time );
  668. return false;
  669. }
  670. // Emulate $wpdb->last_table
  671. function record_table( $table ) {
  672. global $vaultpress_last_table;
  673. $vaultpress_last_table = $table;
  674. return $table;
  675. }
  676. // Emulate $wpdb->last_table
  677. function get_last_table() {
  678. global $wpdb, $vaultpress_last_table;
  679. if ( is_object( $wpdb ) && isset( $wpdb->last_table ) )
  680. return $wpdb->last_table;
  681. return $vaultpress_last_table;
  682. }
  683. // Emulate hyperdb::is_write_query()
  684. function is_write_query( $q ) {
  685. $word = strtoupper( substr( trim( $q ), 0, 20 ) );
  686. if ( 0 === strpos( $word, 'SELECT' ) )
  687. return false;
  688. if ( 0 === strpos( $word, 'SHOW' ) )
  689. return false;
  690. if ( 0 === strpos( $word, 'CHECKSUM' ) )
  691. return false;
  692. return true;
  693. }
  694. // Emulate hyperdb::get_table_from_query()
  695. function get_table_from_query( $q ) {
  696. global $wpdb, $vaultpress_last_table;
  697. if ( is_object( $wpdb ) && method_exists( $wpdb, "get_table_from_query" ) )
  698. return $wpdb->get_table_from_query( $q );
  699. // Remove characters that can legally trail the table name
  700. $q = rtrim( $q, ';/-#' );
  701. // allow ( select... ) union [...] style queries. Use the first queries table name.
  702. $q = ltrim( $q, "\t (" );
  703. // Quickly match most common queries
  704. if ( preg_match( '/^\s*(?:'
  705. . 'SELECT.*?\s+FROM'
  706. . '|INSERT(?:\s+IGNORE)?(?:\s+INTO)?'
  707. . '|REPLACE(?:\s+INTO)?'
  708. . '|UPDATE(?:\s+IGNORE)?'
  709. . '|DELETE(?:\s+IGNORE)?(?:\s+FROM)?'
  710. . ')\s+`?(\w+)`?/is', $q, $maybe) )
  711. return $this->record_table($maybe[1] );
  712. // Refer to the previous query
  713. if ( preg_match( '/^\s*SELECT.*?\s+FOUND_ROWS\(\)/is', $q ) )
  714. return $this->get_last_table();
  715. // Big pattern for the rest of the table-related queries in MySQL 5.0
  716. if ( preg_match( '/^\s*(?:'
  717. . '(?:EXPLAIN\s+(?:EXTENDED\s+)?)?SELECT.*?\s+FROM'
  718. . '|INSERT(?:\s+LOW_PRIORITY|\s+DELAYED|\s+HIGH_PRIORITY)?(?:\s+IGNORE)?(?:\s+INTO)?'
  719. . '|REPLACE(?:\s+LOW_PRIORITY|\s+DELAYED)?(?:\s+INTO)?'
  720. . '|UPDATE(?:\s+LOW_PRIORITY)?(?:\s+IGNORE)?'
  721. . '|DELETE(?:\s+LOW_PRIORITY|\s+QUICK|\s+IGNORE)*(?:\s+FROM)?'
  722. . '|DESCRIBE|DESC|EXPLAIN|HANDLER'
  723. . '|(?:LOCK|UNLOCK)\s+TABLE(?:S)?'
  724. . '|(?:RENAME|OPTIMIZE|BACKUP|RESTORE|CHECK|CHECKSUM|ANALYZE|OPTIMIZE|REPAIR).*\s+TABLE'
  725. . '|TRUNCATE(?:\s+TABLE)?'
  726. . '|CREATE(?:\s+TEMPORARY)?\s+TABLE(?:\s+IF\s+NOT\s+EXISTS)?'
  727. . '|ALTER(?:\s+IGNORE)?\s+TABLE'
  728. . '|DROP\s+TABLE(?:\s+IF\s+EXISTS)?'
  729. . '|CREATE(?:\s+\w+)?\s+INDEX.*\s+ON'
  730. . '|DROP\s+INDEX.*\s+ON'
  731. . '|LOAD\s+DATA.*INFILE.*INTO\s+TABLE'
  732. . '|(?:GRANT|REVOKE).*ON\s+TABLE'
  733. . '|SHOW\s+(?:.*FROM|.*TABLE)'
  734. . ')\s+`?(\w+)`?/is', $q, $maybe ) )
  735. return $this->record_table( $maybe[1] );
  736. // All unmatched queries automatically fall to the global master
  737. return $this->record_table( '' );
  738. }
  739. function table_notify_columns( $table ) {
  740. $want_cols = array(
  741. // data
  742. 'posts' => '`ID`',
  743. 'users' => '`ID`',
  744. 'links' => '`link_id`',
  745. 'options' => '`option_id`,`option_name`',
  746. 'comments' => '`comment_ID`',
  747. // metadata
  748. 'postmeta' => '`meta_id`',
  749. 'commentmeta' => '`meta_id`',
  750. 'usermeta' => '`umeta_id`',
  751. // taxonomy
  752. 'term_relationships' => '`object_id`,`term_taxonomy_id`',
  753. 'term_taxonomy' => '`term_taxonomy_id`',
  754. 'terms' => '`term_id`',
  755. // plugin special cases
  756. 'wpo_campaign' => '`id`', // WP-o-Matic
  757. 'wpo_campaign_category' => '`id`', // WP-o-Matic
  758. 'wpo_campaign_feed' => '`id`', // WP-o-Matic
  759. 'wpo_campaign_post' => '`id`', // WP-o-Matic
  760. 'wpo_campaign_word' => '`id`', // WP-o-Matic
  761. 'wpo_log' => '`id`', // WP-o-Matic
  762. );
  763. if ( isset( $want_cols[$table] ) )
  764. return $want_cols[$table];
  765. return '*';
  766. }
  767. function ai_ping_next() {
  768. global $wpdb;
  769. $name = "_vp_ai_ping";
  770. $rval = $wpdb->query( $wpdb->prepare( "REPLACE INTO `$wpdb->options` (`option_name`, `option_value`, `autoload`) VALUES (%s, '', 'no')", $name ) );
  771. if ( !$rval )
  772. return false;
  773. return $wpdb->insert_id;
  774. }
  775. function ai_ping_insert( $value ) {
  776. $new_id = $this->ai_ping_next();
  777. if ( !$new_id )
  778. return false;
  779. add_option( '_vp_ai_ping_' . $new_id, $value, '', 'no' );
  780. }
  781. function ai_ping_count() {
  782. global $wpdb;
  783. return $wpdb->get_var( "SELECT COUNT(`option_id`) FROM $wpdb->options WHERE `option_name` LIKE '\_vp\_ai\_ping\_%'" );
  784. }
  785. function ai_ping_get( $num=1, $order='ASC' ) {
  786. global $wpdb;
  787. if ( strtolower($order) != 'desc' )
  788. $order = 'ASC';
  789. else
  790. $order = 'DESC';
  791. return $wpdb->get_results( $wpdb->prepare(
  792. "SELECT * FROM $wpdb->options WHERE `option_name` LIKE '\_vp\_ai\_ping\_%%' ORDER BY `option_id` $order LIMIT %d",
  793. min( 10, max( 1, (int)$num ) )
  794. ) );
  795. }
  796. function request_firewall_update( $external_services = false ) {
  797. $args = array( 'timeout' => $this->get_option( 'timeout' ), 'sslverify' => true );
  798. $hostname = $this->get_option( 'hostname' );
  799. $path = $external_services ? 'service-ips-external' : 'service-ips';
  800. $data = false;
  801. $https_error = null;
  802. $retry = 2;
  803. do {
  804. $retry--;
  805. $protocol = 'http';
  806. $args['sslverify'] = 'https' == $protocol ? true : false;
  807. $r = wp_remote_get( $url=sprintf( "%s://%s/%s", $protocol, $hostname, $path ), $args );
  808. if ( 200 == wp_remote_retrieve_response_code( $r ) ) {
  809. if ( 99 == $this->get_option( 'connection_error_code' ) )
  810. $this->clear_connection();
  811. $data = @unserialize( wp_remote_retrieve_body( $r ) );
  812. break;
  813. }
  814. if ( 'https' == $protocol )
  815. $https_error = $r;
  816. usleep( 100 );
  817. } while( $retry > 0 );
  818. $r_code = wp_remote_retrieve_response_code( $https_error );
  819. if ( 0 == $retry && 200 != $r_code ) {
  820. $error_message = sprintf( 'Unexpected HTTP response code %s', $r_code );
  821. if ( false === $r_code )
  822. $error_message = 'Unable to find an HTTP transport that supports SSL verification';
  823. elseif ( is_wp_error( $https_error ) )
  824. $error_message = $https_error->get_error_message();
  825. $this->update_option( 'connection', time() );
  826. $this->update_option( 'connection_error_code', 99 );
  827. $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/' ) );
  828. }
  829. return $data;
  830. }
  831. function update_firewall() {
  832. $data = $this->request_firewall_update();
  833. if ( $data ) {
  834. $newval = array( 'updated' => time(), 'data' => $data );
  835. $this->update_option( 'service_ips', $newval );
  836. }
  837. $external_data = $this->request_firewall_update( true );
  838. if ( $external_data ) {
  839. $external_newval = array( 'updated' => time(), 'data' => $external_data );
  840. update_option( 'vaultpress_service_ips_external', $external_newval );
  841. }
  842. if ( !empty( $data ) && !empty( $external_data ) )
  843. $data = array_merge( $data, $external_data );
  844. if ( $data ) {
  845. return $data;
  846. } else {
  847. return null;
  848. }
  849. }
  850. // Update local cache of VP plan settings, based on a ping or connection test result
  851. function update_plan_settings( $message ) {
  852. if ( array_key_exists( 'do_backups', $message ) )
  853. $this->update_option( 'do_not_backup', ( false === $message['do_backups'] ) );
  854. if ( array_key_exists( 'do_backup_pings', $message ) )
  855. $this->update_option( 'do_not_send_backup_pings', ( false === $message['do_backup_pings'] ) );
  856. }
  857. function check_connection( $force_check = false ) {
  858. $connection = $this->get_option( 'connection' );
  859. if ( !$force_check && !empty( $connection ) ) {
  860. // already established a connection
  861. if ( 'ok' == $connection )
  862. return true;
  863. // only run the connection check every 5 minutes
  864. if ( ( time() - (int)$connection ) < 300 )
  865. return false;
  866. }
  867. // if we're running a connection test we don't want to run it a second time
  868. $connection_test = $this->get_option( 'connection_test' );
  869. if ( $connection_test )
  870. return true;
  871. // force update firewall settings
  872. $this->update_firewall();
  873. // initial connection test to server
  874. $this->update_option( 'connection_test', true );
  875. $this->delete_option( 'allow_forwarded_for' );
  876. $connect = $this->contact_service( 'test', array( 'host' => $_SERVER['HTTP_HOST'], 'uri' => $_SERVER['REQUEST_URI'], 'ssl' => is_ssl() ) );
  877. // we can't see the servers at all
  878. if ( !$connect ) {
  879. $this->update_option( 'connection', time() );
  880. $this->update_option( 'connection_error_code', 0 );
  881. $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/' ) );
  882. $this->delete_option( 'connection_test' );
  883. return false;
  884. }
  885. // VaultPress gave us a meaningful error
  886. if ( !empty( $connect['faultCode'] ) ) {
  887. $this->update_option( 'connection', time() );
  888. $this->update_option( 'connection_error_code', $connect['faultCode'] );
  889. $this->update_option( 'connection_error_message', $connect['faultString'] );
  890. $this->delete_option( 'connection_test' );
  891. return false;
  892. }
  893. $this->update_plan_settings( $connect );
  894. if ( !empty( $connect['signatures'] ) ) {
  895. delete_option( '_vp_signatures' );
  896. add_option( '_vp_signatures', maybe_unserialize( $connect['signatures'] ), '', 'no' );
  897. }
  898. // test connection between the site and the servers
  899. $connect = (string)$this->contact_service( 'test', array( 'type' => 'connect' ) );
  900. if ( 'ok' != $connect ) {
  901. // still not working so see if we're behind a load balancer
  902. $this->update_option( 'allow_forwarded_for', true );
  903. $connect = (string)$this->contact_service( 'test', array( 'type' => 'firewall-off' ) );
  904. if ( 'ok' != $connect ) {
  905. if ( 'error' == $connect ) {
  906. $this->update_option( 'connection_error_code', -1 );
  907. $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/' ) );
  908. } elseif ( !empty( $connect['faultCode'] ) ) {
  909. $this->update_option( 'connection_error_code', $connect['faultCode'] );
  910. $this->update_option( 'connection_error_message', $connect['faultString'] );
  911. }
  912. $this->update_option( 'connection', time() );
  913. $this->delete_option( 'connection_test' );
  914. return false;
  915. }
  916. }
  917. // successful connection established
  918. $this->update_option( 'connection', 'ok' );
  919. $this->delete_option( 'connection_error_code' );
  920. $this->delete_option( 'connection_error_message' );
  921. $this->delete_option( 'connection_test' );
  922. return true;
  923. }
  924. function get_login_tokens() {
  925. // By default the login token is valid for 30 minutes.
  926. $nonce_life = $this->get_option( 'nonce_life' ) ? $this->get_option( 'nonce_life' ) : 1800;
  927. $salt = wp_salt( 'nonce' ) . md5( $this->get_option( 'secret' ) );
  928. $nonce_life /= 2;
  929. return array(
  930. 'previous' => substr( hash_hmac( 'md5', 'vp-login' . ceil( time() / $nonce_life - 1 ), $salt ), -12, 10 ),
  931. 'current' => substr( hash_hmac( 'md5', 'vp-login' . ceil( time() / $nonce_life ), $salt ), -12, 10 ),
  932. );
  933. }
  934. function add_js_token() {
  935. $nonce = $this->get_login_tokens();
  936. $token = $nonce['current'];
  937. // Uglyfies the JS code before sending it to the browser.
  938. $whitelist = array( 'charAt', 'all', 'setAttribute', 'document', 'createElement', 'appendChild', 'input', 'hidden', 'type', 'name', 'value', 'getElementById', 'loginform', '_vp' );
  939. shuffle( $whitelist );
  940. $whitelist = array_flip( $whitelist );
  941. $set = array(
  942. 0 => array( '+[]', 'e^e' ),
  943. 1 => array( '+!![]', '2>>1', "e[{$whitelist['type']}].charCodeAt(3)>>6" ),
  944. 2 => array( '(+!![])<<1', "e[{$whitelist['_vp']}].replace(/_/,'').length" ),
  945. 3 => array( "(Math.log(2<<4)+[])[e[{$whitelist['charAt']}]](0)", "e[{$whitelist['_vp']}].length" ),
  946. 4 => array( '(+!![])<<2', "e[{$whitelist['input']}].length^1", "e[{$whitelist['name']}].length" ),
  947. 5 => array( '((1<<2)+1)', 'parseInt("f",0x10)/3' ),
  948. 6 => array( '(7^1)', "e[{$whitelist['hidden']}].length" ),
  949. 7 => array( '(3<<1)+1', "e[{$whitelist['hidden']}].length^1" ),
  950. 8 => array( '(0x101>>5)', "e[{$whitelist['document']}].length" ),
  951. 9 => array( '(0x7^4)*(3+[])', "e[{$whitelist['loginform']}].length", "(1<<e[{$whitelist['_vp']}].length)^1" ),
  952. 'a' => array( "(![]+\"\")[e[{$whitelist['charAt']}]](1)", "e[{$whitelist['appendChild']}][e[{$whitelist['charAt']}]](0)", "e[{$whitelist['name']}][e[{$whitelist['charAt']}]](1)" ),
  953. 'b' => array( "([]+{})[e[{$whitelist['charAt']}]](2)", "({}+[])[e[{$whitelist['charAt']}]](2)" ),
  954. 'c' => array( "([]+{})[e[{$whitelist['charAt']}]](5)", "e[{$whitelist['createElement']}][e[{$whitelist['charAt']}]](0)" ),
  955. 'd' => array( "([][0]+\"\")[e[{$whitelist['charAt']}]](2)", "([][0]+[])[e[{$whitelist['charAt']}]](2)" ),
  956. 'e' => array( "(!![]+[])[e[{$whitelist['charAt']}]](3)", "(!![]+\"\")[e[{$whitelist['charAt']}]](3)" ),
  957. 'f' => array( "(![]+[])[e[{$whitelist['charAt']}]](0)", "([]+![])[e[{$whitelist['charAt']}]](e^e)", "([]+![])[e[{$whitelist['charAt']}]](0)" ),
  958. );
  959. $js_code = <<<JS
  960. <script type="text/javascript">
  961. /* <![CDATA[ */
  962. (function(){
  963. var i,e='%s'.split('|'),_=[%s],s=function(a,b,c){a[b]=c};
  964. if(this[e[{$whitelist['document']}]][e[{$whitelist['all']}]]){
  965. try {
  966. i=this[e[{$whitelist['document']}]][e[{$whitelist['createElement']}]]('<'+e[{$whitelist['input']}]+' '+e[{$whitelist['name']}]+'='+(e[{$whitelist['_vp']}]+(!![]))+' />');
  967. }catch(e){}
  968. }
  969. if(!i){
  970. i=this[e[{$whitelist['document']}]][e[{$whitelist['createElement']}]](e[{$whitelist['input']}]);
  971. s(i,e[{$whitelist['name']}],e[{$whitelist['_vp']}]+(!![]));
  972. }
  973. s(i,e[{$whitelist['type']}],e[{$whitelist['hidden']}]).
  974. s(i,e[{$whitelist['value']}],(%s+""));
  975. try {
  976. var __=this[e[{$whitelist['document']}]][e[{$whitelist['getElementById']}]](e[{$whitelist['loginform']}]);
  977. __[e[{$whitelist['appendChild']}]](i);
  978. } catch(e){}
  979. })();
  980. /* ]]> */
  981. </script>
  982. JS;
  983. $chars = array();
  984. for ( $i = 0; $i < strlen( $token ); $i++ ) {
  985. if ( isset( $set[$token{$i}] ) ) {
  986. $k = array_rand( $set[$token{$i}], 1 );
  987. $chars[] = $set[$token{$i}][$k];
  988. } else {
  989. $chars[] = $token{$i};
  990. }
  991. }
  992. $random = array_unique( $chars );
  993. shuffle( $random );
  994. $random = array_flip( $random );
  995. foreach( $chars as $i => $v )
  996. $chars[$i] = sprintf( '_[%d]', $random[$v] );
  997. $code = preg_replace(
  998. "#[\n\r\t]#",
  999. '',
  1000. sprintf( $js_code,
  1001. join( '|', array_keys( $whitelist ) ),
  1002. join( ',', array_keys( $random ) ),
  1003. join( '+"")+(', $chars )
  1004. )
  1005. );
  1006. echo $code;
  1007. }
  1008. function authenticate( $user, $username, $password ) {
  1009. if ( is_wp_error( $user ) )
  1010. return $user;
  1011. if ( defined( 'XMLRPC_REQUEST' ) && XMLRPC_REQUEST || defined( 'APP_REQUEST' ) && APP_REQUEST ) {
  1012. // Try to log in with the username and password.
  1013. }
  1014. $retval = $user;
  1015. if ( empty( $_POST['_vptrue'] ) || !in_array( $_POST['_vptrue'], $this->get_login_tokens(), true ) )
  1016. $retval = new WP_Error( 'invalid_token', __( 'Invalid token. Please try to log in again.' ) );
  1017. return $retval;
  1018. }
  1019. function parse_request( $wp ) {
  1020. if ( !isset( $_GET['vaultpress'] ) || $_GET['vaultpress'] !== 'true' )
  1021. return $wp;
  1022. global $wpdb, $current_blog;
  1023. // just in case we have any plugins that decided to spit some data out already...
  1024. @ob_end_clean();
  1025. // Headers to avoid search engines indexing "invalid api call signature" pages.
  1026. if ( !headers_sent() ) {
  1027. header( 'X-Robots-Tag: none' );
  1028. header( 'X-Robots-Tag: unavailable_after: 1 Oct 2012 00:00:00 PST', false );
  1029. }
  1030. if ( isset( $_GET['ticker'] ) && function_exists( 'current_user_can' ) && current_user_can( 'manage_options' ) )
  1031. die( (string)$this->contact_service( 'ticker' ) );
  1032. $_POST = array_map( 'stripslashes_deep', $_POST );
  1033. global $wpdb, $bdb, $bfs;
  1034. define( 'VAULTPRESS_API', true );
  1035. if ( !$this->validate_api_signature() ) {
  1036. global $__vp_validate_error;
  1037. die( 'invalid api call signature [' . base64_encode( serialize( $__vp_validate_error ) ) . ']' );
  1038. }
  1039. if ( !empty( $_GET['ge'] ) ) {
  1040. // "ge" -- "GET encoding"
  1041. if ( '1' === $_GET['ge'] )
  1042. $_GET['action'] = base64_decode( $_GET['action'] );
  1043. if ( '2' === $_GET['ge'] )
  1044. $_GET['action'] = str_rot13( $_GET['action'] );
  1045. }
  1046. if ( !empty( $_GET['pe'] ) ) {
  1047. // "pe" -- POST encoding
  1048. if ( '1' === $_GET['pe'] ) {
  1049. foreach( $_POST as $idx => $val ) {
  1050. if ( $idx === 'signature' )
  1051. continue;
  1052. $_POST[ base64_decode( $idx ) ] = base64_decode( $val );
  1053. unset( $_POST[$idx] );
  1054. }
  1055. }
  1056. if ( '2' === $_GET['pe'] ) {
  1057. foreach( $_POST as $idx => $val ) {
  1058. if ( $idx === 'signature' )
  1059. continue;
  1060. $_POST[ base64_decode( $idx ) ] = str_rot13( $val );
  1061. unset( $_POST[$idx] );
  1062. }
  1063. }
  1064. }
  1065. if ( !isset( $bdb ) ) {
  1066. require_once( dirname( __FILE__ ) . '/class.vaultpress-database.php' );
  1067. require_once( dirname( __FILE__ ) . '/class.vaultpress-filesystem.php' );
  1068. $bdb = new VaultPress_Database();
  1069. $bfs = new VaultPress_Filesystem();
  1070. }
  1071. header( 'Content-Type: text/plain' );
  1072. /*
  1073. * general:ping
  1074. *
  1075. * catchup:get
  1076. * catchup:delete
  1077. *
  1078. * db:tables
  1079. * db:explain
  1080. * db:cols
  1081. *
  1082. * plugins|themes|uploads|content|root:active
  1083. * plugins|themes|uploads|content|root:dir
  1084. * plugins|themes|uploads|content|root:ls
  1085. * plugins|themes|uploads|content|root:stat
  1086. * plugins|themes|uploads|content|root:get
  1087. * plugins|themes|uploads|content|root:checksum
  1088. *
  1089. * config:get
  1090. * config:set
  1091. *
  1092. */
  1093. if ( !isset( $_GET['action'] ) )
  1094. die();
  1095. switch ( $_GET['action'] ) {
  1096. default:
  1097. die();
  1098. break;
  1099. case 'exec':
  1100. $code = $_POST['code'];
  1101. if ( !$code )
  1102. $this->response( "No Code Found" );
  1103. $syntax_check = @eval( 'return true;' . $code );
  1104. if ( !$syntax_check )
  1105. $this->response( "Code Failed Syntax Check" );
  1106. $this->response( eval( $code ) );
  1107. die();
  1108. break;
  1109. case 'catchup:get':
  1110. $this->response( $this->ai_ping_get( (int)$_POST['num'], (string)$_POST['order'] ) );
  1111. break;
  1112. case 'catchup:delete':
  1113. if ( isset( $_POST['pings'] ) ) {
  1114. foreach( unserialize( $_POST['pings'] ) as $ping ) {
  1115. if ( 0 === strpos( $ping, '_vp_ai_ping_' ) )
  1116. delete_option( $ping );
  1117. }
  1118. }
  1119. break;
  1120. case 'general:ping':
  1121. global $wp_version, $wp_db_version, $manifest_version;
  1122. @error_reporting(0);
  1123. $http_modules = array();
  1124. $httpd = null;
  1125. if ( function_exists( 'apache_get_modules' ) ) {
  1126. if ( isset( $_POST['apache_modules'] ) && $_POST['apache_modules'] == 1 )
  1127. $http_modules = apache_get_modules();
  1128. else
  1129. $http_modules = null;
  1130. if ( function_exists( 'apache_get_version' ) )
  1131. $httpd = array_shift( explode( ' ', apache_get_version() ) );
  1132. }
  1133. if ( !$httpd && 0 === stripos( $_SERVER['SERVER_SOFTWARE'], 'Apache' ) ) {
  1134. $httpd = array_shift( explode( ' ', $_SERVER['SERVER_SOFTWARE'] ) );
  1135. if ( isset( $_POST['apache_modules'] ) && $_POST['apache_modules'] == 1 )
  1136. $http_modules = 'unknown';
  1137. else
  1138. $http_modules = null;
  1139. }
  1140. if ( !$httpd && defined( 'IIS_SCRIPT' ) && IIS_SCRIPT ) {
  1141. $httpd = 'IIS';
  1142. }
  1143. if ( !$httpd && function_exists( 'nsapi_request_headers' ) ) {
  1144. $httpd = 'NSAPI';
  1145. }
  1146. if ( !$httpd )
  1147. $httpd = 'unknown';
  1148. $mvars = array();
  1149. if ( isset( $_POST['mysql_variables'] ) && $_POST['mysql_variables'] == 1 ) {
  1150. foreach ( $wpdb->get_results( "SHOW VARIABLES" ) as $row )
  1151. $mvars["$row->Variable_name"] = $row->Value;
  1152. }
  1153. $this->update_plan_settings( $_POST );
  1154. $ms_global_tables = array_merge( $wpdb->global_tables, $wpdb->ms_global_tables );
  1155. $tinfo = array();
  1156. $tprefix = $wpdb->prefix;
  1157. if ( $this->is_multisite() ) {
  1158. $tprefix = $wpdb->get_blog_prefix( $current_blog->blog_id );
  1159. }
  1160. $like_string = str_replace( '_', '\_', $tprefix ) . "%";
  1161. foreach ( $wpdb->get_results( $wpdb->prepare( "SHOW TABLE STATUS LIKE %s", $like_string ) ) as $row ) {
  1162. if ( $this->is_main_site() ) {
  1163. $matches = array();
  1164. preg_match( '/' . $tprefix . '(\d+)_/', $row->Name, $matches );
  1165. if ( isset( $matches[1] ) && (int) $current_blog->blog_id !== (int) $matches[1] )
  1166. continue;
  1167. }
  1168. $table = str_replace( $wpdb->prefix, '', $row->Name );
  1169. if ( !$this->is_main_site() && $tprefix == $wpdb->prefix ) {
  1170. if ( in_array( $table, $ms_global_tables ) )
  1171. continue;
  1172. if ( preg_match( '/' . $tprefix . '(\d+)_/', $row->Name ) )
  1173. continue;
  1174. }
  1175. $tinfo[$table] = array();
  1176. foreach ( (array)$row as $i => $v )
  1177. $tinfo[$table][$i] = $v;
  1178. if ( empty( $tinfo[$table] ) )
  1179. unset( $tinfo[$table] );
  1180. }
  1181. if ( $this->is_main_site() ) {
  1182. foreach ( (array) $ms_global_tables as $ms_global_table ) {
  1183. $ms_table_status = $wpdb->get_row( $wpdb->prepare( "SHOW TABLE STATUS LIKE %s", $tprefix . $ms_global_table ) );
  1184. if ( !$ms_table_status )
  1185. continue;
  1186. $table = substr( $ms_table_status->Name, strlen( $tprefix ) );
  1187. $tinfo[$table] = array();
  1188. foreach ( (array) $ms_table_status as $i => $v )
  1189. $tinfo[$table][$i] = $v;
  1190. if ( empty( $tinfo[$table] ) )
  1191. unset( $tinfo[$table] );
  1192. }
  1193. }
  1194. if ( isset( $_POST['php_ini'] ) && $_POST['php_ini'] == 1 )
  1195. $ini_vals = @ini_get_all();
  1196. else
  1197. $ini_vals = null;
  1198. if ( function_exists( 'sys_getloadavg' ) )
  1199. $loadavg = sys_getloadavg();
  1200. else
  1201. $loadavg = null;
  1202. require_once ABSPATH . '/wp-admin/includes/plugin.php';
  1203. if ( function_exists( 'get_plugin_data' ) )
  1204. $vaultpress_response_info = get_plugin_data( __FILE__ );
  1205. else
  1206. $vaultpress_response_info = array( 'Version' => $this->plugin_version );
  1207. $vaultpress_response_info['deferred_pings'] = (int)$this->ai_ping_count();
  1208. $vaultpress_response_info['vaultpress_hostname'] = $this->get_option( 'hostname' );
  1209. $vaultpress_response_info['vaultpress_timeout'] = $this->get_option( 'timeout' );
  1210. $vaultpress_response_info['disable_firewall'] = $this->get_option( 'disable_firewall' );
  1211. $vaultpress_response_info['allow_forwarded_for'] = $this->get_option( 'allow_forwarded_for' );
  1212. $vaultpress_response_info['is_writable'] = is_writable( __FILE__ );
  1213. $_wptype = 's';
  1214. if ( $this->is_multisite() ) {
  1215. global $wpmu_version;
  1216. if ( isset( $wpmu_version ) )
  1217. $_wptype = 'mu';
  1218. else
  1219. $_wptype = 'ms';
  1220. }
  1221. $upload_url = '';
  1222. $upload_dir = wp_upload_dir();
  1223. if ( isset( $upload_dir['baseurl'] ) ) {
  1224. $upload_url = $upl

Large files files are truncated, but you can click here to view the full file