array(), 'onedrive' => array(), 'googledrive' => array(), 'googlecloud' => array()); private $php_versions = array('5.4', '5.5', '5.6', '7.0', '7.1', '7.2', '7.3'); private $wp_versions = array('3.2', '3.3', '3.4', '3.5', '3.6', '3.7', '3.8', '3.9', '4.0', '4.1', '4.2', '4.3', '4.4', '4.5', '4.6', '4.7', '4.8', '4.9'); /** * Constructor */ public function __construct() { $this->admin_init(); } /** * Get the path to the UI templates directory * * @return String - a filesystem directory path */ public function get_templates_dir() { return apply_filters('updraftplus_templates_dir', UpdraftPlus_Manipulation_Functions::wp_normalize_path(UPDRAFTPLUS_DIR.'/templates')); } private function register_template_directories() { $template_directories = array(); $templates_dir = $this->get_templates_dir(); if ($dh = opendir($templates_dir)) { while (($file = readdir($dh)) !== false) { if ('.' == $file || '..' == $file) continue; if (is_dir($templates_dir.'/'.$file)) { $template_directories[$file] = $templates_dir.'/'.$file; } } closedir($dh); } // This is the optimal hook for most extensions to hook into $this->template_directories = apply_filters('updraftplus_template_directories', $template_directories); } /** * Output, or return, the results of running a template (from the 'templates' directory, unless a filter over-rides it). Templates are run with $updraftplus, $updraftplus_admin and $wpdb set. * * @param String $path - path to the template * @param Boolean $return_instead_of_echo - by default, the template is echo-ed; set this to instead return it * @param Array $extract_these - variables to inject into the template's run context * * @return Void|String */ public function include_template($path, $return_instead_of_echo = false, $extract_these = array()) { if ($return_instead_of_echo) ob_start(); if (preg_match('#^([^/]+)/(.*)$#', $path, $matches)) { $prefix = $matches[1]; $suffix = $matches[2]; if (isset($this->template_directories[$prefix])) { $template_file = $this->template_directories[$prefix].'/'.$suffix; } } if (!isset($template_file)) $template_file = UPDRAFTPLUS_DIR.'/templates/'.$path; $template_file = apply_filters('updraftplus_template', $template_file, $path); do_action('updraftplus_before_template', $path, $template_file, $return_instead_of_echo, $extract_these); if (!file_exists($template_file)) { error_log("UpdraftPlus: template not found: $template_file"); echo __('Error:', 'updraftplus').' '.__('template not found', 'updraftplus')." ($path)"; } else { extract($extract_these); global $updraftplus, $wpdb; $updraftplus_admin = $this; include $template_file; } do_action('updraftplus_after_template', $path, $template_file, $return_instead_of_echo, $extract_these); if ($return_instead_of_echo) return ob_get_clean(); } /** * Add actions for any needed dashboard notices for remote storage services * * @param String|Array $services - a list of services, or single service */ private function setup_all_admin_notices_global($services) { global $updraftplus; if ('googledrive' === $services || (is_array($services) && in_array('googledrive', $services))) { $settings = UpdraftPlus_Storage_Methods_Interface::update_remote_storage_options_format('googledrive'); if (is_wp_error($settings)) { if (!isset($this->storage_module_option_errors)) $this->storage_module_option_errors = ''; $this->storage_module_option_errors .= "Google Drive (".$settings->get_error_code()."): ".$settings->get_error_message(); add_action('all_admin_notices', array($this, 'show_admin_warning_multiple_storage_options')); $updraftplus->log_wp_error($settings, true, true); } elseif (!empty($settings['settings'])) { foreach ($settings['settings'] as $instance_id => $storage_options) { if ((defined('UPDRAFTPLUS_CUSTOM_GOOGLEDRIVE_APP') && UPDRAFTPLUS_CUSTOM_GOOGLEDRIVE_APP) || !empty($storage_options['clientid'])) { if (!empty($storage_options['clientid'])) { $clientid = $storage_options['clientid']; $token = empty($storage_options['token']) ? '' : $storage_options['token']; } if (!empty($clientid) && '' == $token) { if (!in_array($instance_id, $this->auth_instance_ids['googledrive'])) $this->auth_instance_ids['googledrive'][] = $instance_id; if (false === has_action('all_admin_notices', array($this, 'show_admin_warning_googledrive'))) add_action('all_admin_notices', array($this, 'show_admin_warning_googledrive')); } unset($clientid); unset($token); } else { if (empty($storage_options['user_id'])) { if (!in_array($instance_id, $this->auth_instance_ids['googledrive'])) $this->auth_instance_ids['googledrive'][] = $instance_id; if (false === has_action('all_admin_notices', array($this, 'show_admin_warning_googledrive'))) add_action('all_admin_notices', array($this, 'show_admin_warning_googledrive')); } } } } } if ('googlecloud' === $services || (is_array($services) && in_array('googlecloud', $services))) { $settings = UpdraftPlus_Storage_Methods_Interface::update_remote_storage_options_format('googlecloud'); if (is_wp_error($settings)) { if (!isset($this->storage_module_option_errors)) $this->storage_module_option_errors = ''; $this->storage_module_option_errors .= "Google Cloud (".$settings->get_error_code()."): ".$settings->get_error_message(); add_action('all_admin_notices', array($this, 'show_admin_warning_multiple_storage_options')); $updraftplus->log_wp_error($settings, true, true); } elseif (!empty($settings['settings'])) { foreach ($settings['settings'] as $instance_id => $storage_options) { $clientid = $storage_options['clientid']; $token = (empty($storage_options['token'])) ? '' : $storage_options['token']; if (!empty($clientid) && empty($token)) { if (!in_array($instance_id, $this->auth_instance_ids['googlecloud'])) $this->auth_instance_ids['googlecloud'][] = $instance_id; if (false === has_action('all_admin_notices', array($this, 'show_admin_warning_googlecloud'))) add_action('all_admin_notices', array($this, 'show_admin_warning_googlecloud')); } } } } if ('dropbox' === $services || (is_array($services) && in_array('dropbox', $services))) { $settings = UpdraftPlus_Storage_Methods_Interface::update_remote_storage_options_format('dropbox'); if (is_wp_error($settings)) { if (!isset($this->storage_module_option_errors)) $this->storage_module_option_errors = ''; $this->storage_module_option_errors .= "Dropbox (".$settings->get_error_code()."): ".$settings->get_error_message(); add_action('all_admin_notices', array($this, 'show_admin_warning_multiple_storage_options')); $updraftplus->log_wp_error($settings, true, true); } elseif (!empty($settings['settings'])) { foreach ($settings['settings'] as $instance_id => $storage_options) { if (empty($storage_options['tk_access_token'])) { if (!in_array($instance_id, $this->auth_instance_ids['dropbox'])) $this->auth_instance_ids['dropbox'][] = $instance_id; if (false === has_action('all_admin_notices', array($this, 'show_admin_warning_dropbox'))) add_action('all_admin_notices', array($this, 'show_admin_warning_dropbox')); } } } } if ('onedrive' === $services || (is_array($services) && in_array('onedrive', $services))) { $settings = UpdraftPlus_Storage_Methods_Interface::update_remote_storage_options_format('onedrive'); if (is_wp_error($settings)) { if (!isset($this->storage_module_option_errors)) $this->storage_module_option_errors = ''; $this->storage_module_option_errors .= "OneDrive (".$settings->get_error_code()."): ".$settings->get_error_message(); add_action('all_admin_notices', array($this, 'show_admin_warning_multiple_storage_options')); $updraftplus->log_wp_error($settings, true, true); } elseif (!empty($settings['settings'])) { foreach ($settings['settings'] as $instance_id => $storage_options) { if ((defined('UPDRAFTPLUS_CUSTOM_ONEDRIVE_APP') && UPDRAFTPLUS_CUSTOM_ONEDRIVE_APP)) { if (!empty($storage_options['clientid']) && !empty($storage_options['secret']) && empty($storage_options['refresh_token'])) { if (!in_array($instance_id, $this->auth_instance_ids['onedrive'])) $this->auth_instance_ids['onedrive'][] = $instance_id; if (false === has_action('all_admin_notices', array($this, 'show_admin_warning_onedrive'))) add_action('all_admin_notices', array($this, 'show_admin_warning_onedrive')); } elseif (empty($storage_options['refresh_token'])) { if (!in_array($instance_id, $this->auth_instance_ids['onedrive'])) $this->auth_instance_ids['onedrive'][] = $instance_id; if (false === has_action('all_admin_notices', array($this, 'show_admin_warning_onedrive'))) add_action('all_admin_notices', array($this, 'show_admin_warning_onedrive')); } } else { if (empty($storage_options['refresh_token'])) { if (!in_array($instance_id, $this->auth_instance_ids['onedrive'])) $this->auth_instance_ids['onedrive'][] = $instance_id; if (false === has_action('all_admin_notices', array($this, 'show_admin_warning_onedrive'))) add_action('all_admin_notices', array($this, 'show_admin_warning_onedrive')); } } } } } if ('updraftvault' === $services || (is_array($services) && in_array('updraftvault', $services))) { $settings = UpdraftPlus_Storage_Methods_Interface::update_remote_storage_options_format('updraftvault'); if (is_wp_error($settings)) { if (!isset($this->storage_module_option_errors)) $this->storage_module_option_errors = ''; $this->storage_module_option_errors .= "UpdraftVault (".$settings->get_error_code()."): ".$settings->get_error_message(); add_action('all_admin_notices', array($this, 'show_admin_warning_multiple_storage_options')); $updraftplus->log_wp_error($settings, true, true); } elseif (!empty($settings['settings'])) { foreach ($settings['settings'] as $instance_id => $storage_options) { if (empty($storage_options['token']) && empty($storage_options['email'])) { add_action('all_admin_notices', array($this, 'show_admin_warning_updraftvault')); } } } } if ($this->disk_space_check(1048576*35) === false) add_action('all_admin_notices', array($this, 'show_admin_warning_diskspace')); } private function setup_all_admin_notices_udonly($service, $override = false) { global $updraftplus; if (UpdraftPlus_Options::user_can_manage() && defined('DISABLE_WP_CRON') && DISABLE_WP_CRON && (!defined('UPDRAFTPLUS_DISABLE_WP_CRON_NOTICE') || !UPDRAFTPLUS_DISABLE_WP_CRON_NOTICE)) { add_action('all_admin_notices', array($this, 'show_admin_warning_disabledcron')); } if (UpdraftPlus_Options::get_updraft_option('updraft_debug_mode')) { @ini_set('display_errors', 1); // @codingStandardsIgnoreLine if (defined('E_DEPRECATED')) { // @codingStandardsIgnoreLine @error_reporting(E_ALL & ~E_NOTICE & ~E_DEPRECATED); } else { @error_reporting(E_ALL & ~E_NOTICE); } add_action('all_admin_notices', array($this, 'show_admin_debug_warning')); } if (null === UpdraftPlus_Options::get_updraft_option('updraft_interval')) { add_action('all_admin_notices', array($this, 'show_admin_nosettings_warning')); $this->no_settings_warning = true; } // Avoid false positives, by attempting to raise the limit (as happens when we actually do a backup) @set_time_limit(UPDRAFTPLUS_SET_TIME_LIMIT); $max_execution_time = (int) @ini_get('max_execution_time'); if ($max_execution_time>0 && $max_execution_time<20) { add_action('all_admin_notices', array($this, 'show_admin_warning_execution_time')); } // LiteSpeed has a generic problem with terminating cron jobs if (isset($_SERVER['SERVER_SOFTWARE']) && strpos($_SERVER['SERVER_SOFTWARE'], 'LiteSpeed') !== false) { if (!is_file(ABSPATH.'.htaccess') || !preg_match('/noabort/i', file_get_contents(ABSPATH.'.htaccess'))) { add_action('all_admin_notices', array($this, 'show_admin_warning_litespeed')); } } if (version_compare($updraftplus->get_wordpress_version(), '3.2', '<')) add_action('all_admin_notices', array($this, 'show_admin_warning_wordpressversion')); // DreamObjects west cluster shutdown warning if ('dreamobjects' === $service || (is_array($service) && in_array('dreamobjects', $service))) { $settings = UpdraftPlus_Storage_Methods_Interface::update_remote_storage_options_format('dreamobjects'); if (is_wp_error($settings)) { if (!isset($this->storage_module_option_errors)) $this->storage_module_option_errors = ''; $this->storage_module_option_errors .= "DreamObjects (".$settings->get_error_code()."): ".$settings->get_error_message(); add_action('all_admin_notices', array($this, 'show_admin_warning_multiple_storage_options')); $updraftplus->log_wp_error($settings, true, true); } elseif (!empty($settings['settings'])) { foreach ($settings['settings'] as $instance_id => $storage_options) { if ('objects-us-west-1.dream.io' == $storage_options['endpoint']) { add_action('all_admin_notices', array($this, 'show_admin_warning_dreamobjects')); } } } } } /** * Used to output the information for the next scheduled backup. * moved to function for the ajax saves */ public function next_scheduled_backups_output() { // UNIX timestamp $next_scheduled_backup = wp_next_scheduled('updraft_backup'); if ($next_scheduled_backup) { // Convert to GMT $next_scheduled_backup_gmt = gmdate('Y-m-d H:i:s', $next_scheduled_backup); // Convert to blog time zone $next_scheduled_backup = get_date_from_gmt($next_scheduled_backup_gmt, 'D, F j, Y H:i'); // $next_scheduled_backup = date_i18n('D, F j, Y H:i', $next_scheduled_backup); } else { $next_scheduled_backup = __('Nothing currently scheduled', 'updraftplus'); $files_not_scheduled = true; } $next_scheduled_backup_database = wp_next_scheduled('updraft_backup_database'); if (UpdraftPlus_Options::get_updraft_option('updraft_interval_database', UpdraftPlus_Options::get_updraft_option('updraft_interval')) == UpdraftPlus_Options::get_updraft_option('updraft_interval')) { if (isset($files_not_scheduled)) { $next_scheduled_backup_database = $next_scheduled_backup; $database_not_scheduled = true; } else { $next_scheduled_backup_database = __("At the same time as the files backup", 'updraftplus'); $next_scheduled_backup_database_same_time = true; } } else { if ($next_scheduled_backup_database) { // Convert to GMT $next_scheduled_backup_database_gmt = gmdate('Y-m-d H:i:s', $next_scheduled_backup_database); // Convert to blog time zone $next_scheduled_backup_database = get_date_from_gmt($next_scheduled_backup_database_gmt, 'D, F j, Y H:i'); // $next_scheduled_backup_database = date_i18n('D, F j, Y H:i', $next_scheduled_backup_database); } else { $next_scheduled_backup_database = __('Nothing currently scheduled', 'updraftplus'); $database_not_scheduled = true; } } if (isset($files_not_scheduled) && isset($database_not_scheduled)) { ?> : : '.$next_scheduled_backup.''; } else { echo ''.$next_scheduled_backup.''; } if ($return_instead_of_echo) return ob_get_clean(); } /** * Used to output the information for the next scheduled database backup. * moved to function for the ajax saves * * @param Boolean $return_instead_of_echo Whether to return or echo the results. N.B. More than just the results to echo will be returned * @return Void|String If $return_instead_of_echo parameter is true, It returns html string */ public function next_scheduled_database_backups_output($return_instead_of_echo = false) { if ($return_instead_of_echo) ob_start(); $next_scheduled_backup_database = wp_next_scheduled('updraft_backup_database'); if ($next_scheduled_backup_database) { // Convert to GMT $next_scheduled_backup_database_gmt = gmdate('Y-m-d H:i:s', $next_scheduled_backup_database); // Convert to blog time zone $next_scheduled_backup_database = get_date_from_gmt($next_scheduled_backup_database_gmt, 'D, F j, Y H:i'); $database_not_scheduled = false; } else { $next_scheduled_backup_database = __('Nothing currently scheduled', 'updraftplus'); $database_not_scheduled = true; } if ($database_not_scheduled) { echo ''.$next_scheduled_backup_database.''; } else { echo ''.$next_scheduled_backup_database.''; } if ($return_instead_of_echo) return ob_get_clean(); } /** * Run upon the WP admin_init action */ private function admin_init() { add_action('core_upgrade_preamble', array($this, 'core_upgrade_preamble')); add_action('admin_action_upgrade-plugin', array($this, 'admin_action_upgrade_pluginortheme')); add_action('admin_action_upgrade-theme', array($this, 'admin_action_upgrade_pluginortheme')); add_action('admin_head', array($this, 'admin_head')); add_filter((is_multisite() ? 'network_admin_' : '').'plugin_action_links', array($this, 'plugin_action_links'), 10, 2); add_action('wp_ajax_updraft_download_backup', array($this, 'updraft_download_backup')); add_action('wp_ajax_updraft_ajax', array($this, 'updraft_ajax_handler')); add_action('wp_ajax_updraft_ajaxrestore', array($this, 'updraft_ajaxrestore')); add_action('wp_ajax_nopriv_updraft_ajaxrestore', array($this, 'updraft_ajaxrestore')); add_action('wp_ajax_plupload_action', array($this, 'plupload_action')); add_action('wp_ajax_plupload_action2', array($this, 'plupload_action2')); add_action('wp_before_admin_bar_render', array($this, 'wp_before_admin_bar_render')); // Add a new Ajax action for saving settings add_action('wp_ajax_updraft_savesettings', array($this, 'updraft_ajax_savesettings')); // Ajax for settings import and export add_action('wp_ajax_updraft_importsettings', array($this, 'updraft_ajax_importsettings')); // UpdraftPlus templates $this->register_template_directories(); global $updraftplus, $pagenow; add_filter('updraftplus_dirlist_others', array($updraftplus, 'backup_others_dirlist')); add_filter('updraftplus_dirlist_uploads', array($updraftplus, 'backup_uploads_dirlist')); // First, the checks that are on all (admin) pages: $service = UpdraftPlus_Options::get_updraft_option('updraft_service'); if (UpdraftPlus_Options::user_can_manage()) { $this->print_restore_in_progress_box_if_needed(); // Main dashboard page advert // Since our nonce is printed, make sure they have sufficient credentials if ('index.php' == $pagenow && current_user_can('update_plugins') && (!file_exists(UPDRAFTPLUS_DIR.'/udaddons') || (defined('UPDRAFTPLUS_FORCE_DASHNOTICE') && UPDRAFTPLUS_FORCE_DASHNOTICE))) { $dismissed_until = UpdraftPlus_Options::get_updraft_option('updraftplus_dismisseddashnotice', 0); $backup_dir = $updraftplus->backups_dir_location(); // N.B. Not an exact proxy for the installed time; they may have tweaked the expert option to move the directory $installed = @filemtime($backup_dir.'/index.html'); $installed_for = time() - $installed; if (($installed && time() > $dismissed_until && $installed_for > 28*86400 && !defined('UPDRAFTPLUS_NOADS_B')) || (defined('UPDRAFTPLUS_FORCE_DASHNOTICE') && UPDRAFTPLUS_FORCE_DASHNOTICE)) { add_action('all_admin_notices', array($this, 'show_admin_notice_upgradead')); } } // Moved out for use with Ajax saving $this->setup_all_admin_notices_global($service); } if (!class_exists('Updraft_Dashboard_News')) include_once(UPDRAFTPLUS_DIR.'/includes/class-updraft-dashboard-news.php'); $news_translations = array( 'product_title' => 'UpdraftPlus', 'item_prefix' => __('UpdraftPlus', 'updraftplus'), 'item_description' => __('UpdraftPlus News', 'updraftplus'), 'dismiss_tooltip' => __('Dismiss all UpdraftPlus news', 'updraftplus'), 'dismiss_confirm' => __('Are you sure you want to dismiss all UpdraftPlus news forever?', 'updraftplus'), ); $updraftplus_dashboard_news = new Updraft_Dashboard_News('https://feeds.feedburner.com/updraftplus/', 'https://updraftplus.com/news/', $news_translations); // New-install admin tour if ((!defined('UPDRAFTPLUS_ENABLE_TOUR') || UPDRAFTPLUS_ENABLE_TOUR) && (!defined('UPDRAFTPLUS_THIS_IS_CLONE') || !UPDRAFTPLUS_THIS_IS_CLONE)) { include_once(UPDRAFTPLUS_DIR.'/includes/updraftplus-tour.php'); } // Next, the actions that only come on the UpdraftPlus page if (UpdraftPlus_Options::admin_page() != $pagenow || empty($_REQUEST['page']) || 'updraftplus' != $_REQUEST['page']) return; $this->setup_all_admin_notices_udonly($service); add_action('admin_enqueue_scripts', array($this, 'admin_enqueue_scripts'), 99999); $udp_saved_version = UpdraftPlus_Options::get_updraft_option('updraftplus_version'); if (!$udp_saved_version || $udp_saved_version != $updraftplus->version) { if (!$udp_saved_version) { // udp was newly installed, or upgraded from an older version do_action('updraftplus_newly_installed', $updraftplus->version); } else { // udp was updated or downgraded do_action('updraftplus_version_changed', UpdraftPlus_Options::get_updraft_option('updraftplus_version'), $updraftplus->version); } UpdraftPlus_Options::update_updraft_option('updraftplus_version', $updraftplus->version); } } /** * Sets up what is needed to allow an in-page backup to be run. Will enqueue scripts and output appropriate HTML (so, should be run when at a suitable place). Not intended for use on the UpdraftPlus settings page. * * @param string $title Text to use for the title of the modal * @param callable $callback Callable function to output the contents of the updraft_inpage_prebackup element - i.e. what shows in the modal before a backup begins. */ public function add_backup_scaffolding($title, $callback) { $this->admin_enqueue_scripts(); ?>
'.__('This file does not appear to be an UpdraftPlus backup archive (such files are .zip or .gz files which have a name like: backup_(time)_(site name)_(code)_(type).(zip|gz)).', 'updraftplus').'
'.apply_filters('updraftplus_if_foreign_then_premium_message', ''), 'makesure' => __('(make sure that you were trying to upload a zip file previously created by UpdraftPlus)', 'updraftplus'), 'uploaderror' => __('Upload error:', 'updraftplus'), 'notdba' => __('This file does not appear to be an UpdraftPlus encrypted database archive (such files are .gz.crypt files which have a name like: backup_(time)_(site name)_(code)_db.crypt.gz).', 'updraftplus'), 'uploaderr' => __('Upload error', 'updraftplus'), 'followlink' => __('Follow this link to attempt decryption and download the database file to your computer.', 'updraftplus'), 'thiskey' => __('This decryption key will be attempted:', 'updraftplus'), 'unknownresp' => __('Unknown server response:', 'updraftplus'), 'ukrespstatus' => __('Unknown server response status:', 'updraftplus'), 'uploaded' => __('The file was uploaded.', 'updraftplus'), // One of the translators has erroneously changed "Backup" into "Back up" (which means, "reverse" !) 'backupnow' => str_ireplace('Back Up', 'Backup', __('Backup Now', 'updraftplus')), 'cancel' => __('Cancel', 'updraftplus'), 'deletebutton' => __('Delete', 'updraftplus'), 'createbutton' => __('Create', 'updraftplus'), 'uploadbutton' => __('Upload', 'updraftplus'), 'youdidnotselectany' => __('You did not select any components to restore. Please select at least one, and then try again.', 'updraftplus'), 'proceedwithupdate' => __('Proceed with update', 'updraftplus'), 'close' => __('Close', 'updraftplus'), 'restore' => __('Restore', 'updraftplus'), 'downloadlogfile' => __('Download log file', 'updraftplus'), 'automaticbackupbeforeupdate' => __('Automatic backup before update', 'updraftplus'), 'unsavedsettings' => __('You have made changes to your settings, and not saved.', 'updraftplus'), 'saving' => __('Saving...', 'updraftplus'), 'connect' => __('Connect', 'updraftplus'), 'connecting' => __('Connecting...', 'updraftplus'), 'disconnect' => __('Disconnect', 'updraftplus'), 'disconnecting' => __('Disconnecting...', 'updraftplus'), 'counting' => __('Counting...', 'updraftplus'), 'updatequotacount' => __('Update quota count', 'updraftplus'), 'addingsite' => __('Adding...', 'updraftplus'), 'addsite' => __('Add site', 'updraftplus'), // 'resetting' => __('Resetting...', 'updraftplus'), 'creating_please_allow' => __('Creating...', 'updraftplus').(function_exists('openssl_encrypt') ? '' : ' ('.__('your PHP install lacks the openssl module; as a result, this can take minutes; if nothing has happened by then, then you should either try a smaller key size, or ask your web hosting company how to enable this PHP module on your setup.', 'updraftplus').')'), 'sendtosite' => __('Send to site:', 'updraftplus'), 'checkrpcsetup' => sprintf(__('You should check that the remote site is online, not firewalled, does not have security modules that may be blocking access, has UpdraftPlus version %s or later active and that the keys have been entered correctly.', 'updraftplus'), '2.10.3'), 'pleasenamekey' => __('Please give this key a name (e.g. indicate the site it is for):', 'updraftplus'), 'key' => __('Key', 'updraftplus'), 'nokeynamegiven' => sprintf(__("Failure: No %s was given.", 'updraftplus'), __('key name', 'updraftplus')), 'deleting' => __('Deleting...', 'updraftplus'), 'enter_mothership_url' => __('Please enter a valid URL', 'updraftplus'), 'delete_response_not_understood' => __("We requested to delete the file, but could not understand the server's response", 'updraftplus'), 'testingconnection' => __('Testing connection...', 'updraftplus'), 'send' => __('Send', 'updraftplus'), 'migratemodalheight' => class_exists('UpdraftPlus_Addons_Migrator') ? 555 : 300, 'migratemodalwidth' => class_exists('UpdraftPlus_Addons_Migrator') ? 770 : 500, 'download' => _x('Download', '(verb)', 'updraftplus'), 'browse_download_link' => apply_filters('updraftplus_browse_download_link', ''.__("With UpdraftPlus Premium, you can directly download individual files from here.", "updraftplus").''), 'unsavedsettingsbackup' => __('You have made changes to your settings, and not saved.', 'updraftplus')."\n".__('You should save your changes to ensure that they are used for making your backup.', 'updraftplus'), 'unsaved_settings_export' => __('You have made changes to your settings, and not saved.', 'updraftplus')."\n".__('Your export file will be of your displayed settings, not your saved ones.', 'updraftplus'), 'dayselector' => $day_selector, 'mdayselector' => $mday_selector, 'day' => __('day', 'updraftplus'), 'inthemonth' => __('in the month', 'updraftplus'), 'days' => __('day(s)', 'updraftplus'), 'hours' => __('hour(s)', 'updraftplus'), 'weeks' => __('week(s)', 'updraftplus'), 'forbackupsolderthan' => __('For backups older than', 'updraftplus'), 'ud_url' => UPDRAFTPLUS_URL, 'processing' => __('Processing...', 'updraftplus'), 'pleasefillinrequired' => __('Please fill in the required information.', 'updraftplus'), 'test_settings' => __('Test %s Settings', 'updraftplus'), 'testing_settings' => __('Testing %s Settings...', 'updraftplus'), 'settings_test_result' => __('%s settings test result:', 'updraftplus'), 'nothing_yet_logged' => __('Nothing yet logged', 'updraftplus'), 'import_select_file' => __('You have not yet selected a file to import.', 'updraftplus'), 'import_invalid_json_file' => __('Error: The chosen file is corrupt. Please choose a valid UpdraftPlus export file.', 'updraftplus'), 'updraft_settings_url' => UpdraftPlus_Options::admin_page_url().'?page=updraftplus', 'network_site_url' => network_site_url(), 'importing' => __('Importing...', 'updraftplus'), 'importing_data_from' => __('This will import data from:', 'updraftplus'), 'exported_on' => __('Which was exported on:', 'updraftplus'), 'continue_import' => __('Do you want to carry out the import?', 'updraftplus'), 'complete' => __('Complete', 'updraftplus'), 'backup_complete' => __('The backup has finished running', 'updraftplus'), 'backup_aborted' => __('The backup was aborted', 'updraftplus'), 'remote_delete_limit' => defined('UPDRAFTPLUS_REMOTE_DELETE_LIMIT') ? UPDRAFTPLUS_REMOTE_DELETE_LIMIT : 15, 'remote_files_deleted' => __('remote files deleted', 'updraftplus'), 'http_code' => __('HTTP code:', 'updraftplus'), 'makesure2' => __('The file failed to upload. Please check the following:', 'updraftplus')."\n\n - ".__('Any settings in your .htaccess or web.config file that affects the maximum upload or post size.', 'updraftplus')."\n - ".__('The available memory on the server.', 'updraftplus')."\n - ".__('That you are attempting to upload a zip file previously created by UpdraftPlus.', 'updraftplus')."\n\n".__('Further information may be found in the browser JavaScript console, and the server PHP error logs.', 'updraftplus'), 'zip_file_contents' => __('Browsing zip file', 'updraftplus'), 'zip_file_contents_info' => __('Select a file to view information about it', 'updraftplus'), 'search' => __('Search', 'updraftplus'), 'download_timeout' => __('Unable to download file. This could be caused by a timeout. It would be best to download the zip to your computer.', 'updraftplus'), 'loading_log_file' => __('Loading log file', 'updraftplus'), 'updraftplus_version' => $updraftplus->version, 'updraftcentral_wizard_empty_url' => __('Please enter the URL where your UpdraftCentral dashboard is hosted.'), 'updraftcentral_wizard_invalid_url' => __('Please enter a valid URL e.g http://example.com', 'updraftplus'), 'export_settings_file_name' => 'updraftplus-settings-'.sanitize_title(get_bloginfo('name')).'.json', // For remote storage handlebarsjs template 'remote_storage_options' => $remote_storage_options_and_templates['options'], 'remote_storage_templates' => $remote_storage_options_and_templates['templates'], 'instance_enabled' => __('Currently enabled', 'updraftplus'), 'instance_disabled' => __('Currently disabled', 'updraftplus'), 'local_upload_started' => __('Local backup upload has started; please check the current status tab to see the upload progress', 'updraftplus'), 'local_upload_error' => __('You must select at least one remote storage destination to upload this backup set to.', 'updrafplus'), 'already_uploaded' => __('(already uploaded)', 'updraftplus'), 'onedrive_folder_url_warning' => __('Please specify the Microsoft OneDrive folder name, not the URL.', 'updraftplus'), 'updraftcentral_cloud' => __('UpdraftCentral Cloud', 'updraftplus'), 'login_successful' => __('Login successful.', 'updraftplus').' '.__('Please follow this link to open %s in a new window.', 'updraftplus'), 'registration_successful' => __('Registration successful.', 'updraftplus').' '.__('Please follow this link to open %s in a new window.', 'updraftplus'), 'username_password_required' => __('Both email and password fields are required.', 'updraftplus'), 'valid_email_required' => __('An email is required and needs to be in a valid format.', 'updraftplus'), 'trouble_connecting' => __('Trouble connecting? Try using an alternative method in the advanced security options.', 'updraftplus'), 'perhaps_login' => __('Perhaps you would want to login instead.', 'updraftplus'), 'generating_key' => __('Please wait while the system generates and registers an encryption key for your website with UpdraftCentral Cloud.', 'updraftplus'), 'updraftcentral_cloud_redirect' => __('Please wait while you are redirected to UpdraftCentral Cloud.', 'updraftplus'), 'data_consent_required' => __('You need to read and accept the UpdraftCentral Cloud data and privacy policies before you can proceed.', 'updraftplus'), 'close_wizard' => __('You can also close this wizard.', 'updraftplus'), 'control_udc_connections' => __('For future control of all your UpdraftCentral connections, go to the "Advanced Tools" tab.', 'updraftplus'), 'main_tabs_keys' => array_keys($main_tabs), 'clone_version_warning' => __('Warning: you have selected a lower version than your currently installed version. This may fail if you have components that are incompatible with earlier versions.', 'updraftplus'), 'clone_backup_complete' => __('The clone has been provisioned, and its data has been sent to it. Once the clone has finished deploying it, you will receive an email.', 'updraftplus'), 'clone_backup_aborted' => __('The preparation of the clone data has been aborted.', 'updraftplus'), 'current_clean_url' => UpdraftPlus::get_current_clean_url(), 'exclude_rule_remove_conformation_msg' => __('Are you sure you want to remove this exclusion rule?', 'updraftplus'), 'exclude_select_file_or_folder_msg' => __('Please select a file/folder which you would like to exclude', 'updraftplus'), 'exclude_type_ext_msg' => __('Please enter a file extension, like zip', 'updraftplus'), 'exclude_ext_error_msg' => __('Please enter a valid file extension', 'updraftplus'), 'exclude_type_prefix_msg' => __('Please enter characters that begin the filename which you would like to exclude', 'updraftplus'), 'exclude_prefix_error_msg' => __('Please enter a valid file name prefix', 'updraftplus'), 'duplicate_exclude_rule_error_msg' => __('The exclusion rule which you are trying to add already exists', 'updraftplus'), 'clone_key_required' => __('UpdraftClone key is required.', 'updraftplus'), )); } /** * Despite the name, this fires irrespective of what capabilities the user has (even none - so be careful) */ public function core_upgrade_preamble() { // They need to be able to perform backups, and to perform updates if (!UpdraftPlus_Options::user_can_manage() || (!current_user_can('update_core') && !current_user_can('update_plugins') && !current_user_can('update_themes'))) return; if (!class_exists('UpdraftPlus_Addon_Autobackup')) { if (defined('UPDRAFTPLUS_NOADS_B')) return; } ?> do_notice('autobackup', 'autobackup', true)); } else { echo ' '; } ?> 'html5,flash,silverlight,html4', 'browse_button' => 'plupload-browse-button', 'container' => 'plupload-upload-ui', 'drop_element' => 'drag-drop-area', 'file_data_name' => 'async-upload', 'multiple_queues' => true, 'max_file_size' => '100Gb', 'chunk_size' => $chunk_size.'b', 'url' => admin_url('admin-ajax.php', 'relative'), 'multipart' => true, 'multi_selection' => true, 'urlstream_upload' => true, // additional post data to send to our ajax hook 'multipart_params' => array( '_ajax_nonce' => wp_create_nonce('updraft-uploader'), 'action' => 'plupload_action' ) ); // WP 3.9 updated to plupload 2.0 - https://core.trac.wordpress.org/ticket/25663 if (is_file(ABSPATH.WPINC.'/js/plupload/Moxie.swf')) { $plupload_init['flash_swf_url'] = includes_url('js/plupload/Moxie.swf'); } else { $plupload_init['flash_swf_url'] = includes_url('js/plupload/plupload.flash.swf'); } if (is_file(ABSPATH.WPINC.'/js/plupload/Moxie.xap')) { $plupload_init['silverlight_xap_url'] = includes_url('js/plupload/Moxie.xap'); } else { $plupload_init['silverlight_xap_url'] = includes_url('js/plupload/plupload.silverlight.swf'); } ?> backups_dir_location(); $disk_free_space = @disk_free_space($updraft_dir); if (false == $disk_free_space) return -1; return ($disk_free_space > $space) ? true : false; } /** * Adds the settings link under the plugin on the plugin screen. * * @param Array $links Set of links for the plugin, before being filtered * @param String $file File name (relative to the plugin directory) * @return Array filtered results */ public function plugin_action_links($links, $file) { if (is_array($links) && 'updraftplus/updraftplus.php' == $file) { $settings_link = ''.__("Settings", "updraftplus").''; array_unshift($links, $settings_link); $settings_link = ''.__("Add-Ons / Pro Support", "updraftplus").''; array_unshift($links, $settings_link); } return $links; } public function admin_action_upgrade_pluginortheme() { if (isset($_GET['action']) && ('upgrade-plugin' == $_GET['action'] || 'upgrade-theme' == $_GET['action']) && !class_exists('UpdraftPlus_Addon_Autobackup') && !defined('UPDRAFTPLUS_NOADS_B')) { if ('upgrade-plugin' == $_GET['action']) { if (!current_user_can('update_plugins')) return; } else { if (!current_user_can('update_themes')) return; } $dismissed_until = UpdraftPlus_Options::get_updraft_option('updraftplus_dismissedautobackup', 0); if ($dismissed_until > time()) return; if ('upgrade-plugin' == $_GET['action']) { $title = __('Update Plugin'); $parent_file = 'plugins.php'; $submenu_file = 'plugins.php'; } else { $title = __('Update Theme'); $parent_file = 'themes.php'; $submenu_file = 'themes.php'; } include_once(ABSPATH.'wp-admin/admin-header.php'); if (!class_exists('UpdraftPlus_Notices')) include_once(UPDRAFTPLUS_DIR.'/includes/updraftplus-notices.php'); global $updraftplus_notices; $updraftplus_notices->do_notice('autobackup', 'autobackup'); } } public function show_admin_warning($message, $class = 'updated') { echo ' "; } public function show_admin_warning_multiple_storage_options() { $this->show_admin_warning('UpdraftPlus: '.__('An error occurred when fetching storage module options: ', 'updraftplus').htmlspecialchars($this->storage_module_option_errors), 'error'); } public function show_admin_warning_unwritable() { // One of the translators has erroneously changed "Backup" into "Back up" (which means, "reverse" !) $unwritable_mess = htmlspecialchars(str_ireplace('Back Up', 'Backup', __("The 'Backup Now' button is disabled as your backup directory is not writable (go to the 'Settings' tab and find the relevant option).", 'updraftplus'))); $this->show_admin_warning($unwritable_mess, "error"); } public function show_admin_nosettings_warning() { $this->show_admin_warning(''.__('Welcome to UpdraftPlus!', 'updraftplus').' '.str_ireplace('Back Up', 'Backup', __('To make a backup, just press the Backup Now button.', 'updraftplus')).' '.__('To change any of the default settings of what is backed up, to configure scheduled backups, to send your backups to remote storage (recommended), and more, go to the settings tab.', 'updraftplus').'', 'updated notice is-dismissible'); } public function show_admin_warning_execution_time() { $this->show_admin_warning(''.__('Warning', 'updraftplus').': '.sprintf(__('The amount of time allowed for WordPress plugins to run is very low (%s seconds) - you should increase it to avoid backup failures due to time-outs (consult your web hosting company for more help - it is the max_execution_time PHP setting; the recommended value is %s seconds or more)', 'updraftplus'), (int) @ini_get('max_execution_time'), 90)); } public function show_admin_warning_disabledcron() { $this->show_admin_warning(''.__('Warning', 'updraftplus').': '.__('The scheduler is disabled in your WordPress install, via the DISABLE_WP_CRON setting. No backups can run (even "Backup Now") unless either you have set up a facility to call the scheduler manually, or until it is enabled.', 'updraftplus').' '.__('Go here for more information.', 'updraftplus').'', 'updated updraftplus-disable-wp-cron-warning'); } public function show_admin_warning_diskspace() { $this->show_admin_warning(''.__('Warning', 'updraftplus').': '.sprintf(__('You have less than %s of free disk space on the disk which UpdraftPlus is configured to use to create backups. UpdraftPlus could well run out of space. Contact your the operator of your server (e.g. your web hosting company) to resolve this issue.', 'updraftplus'), '35 MB')); } public function show_admin_warning_wordpressversion() { $this->show_admin_warning(''.__('Warning', 'updraftplus').': '.sprintf(__('UpdraftPlus does not officially support versions of WordPress before %s. It may work for you, but if it does not, then please be aware that no support is available until you upgrade WordPress.', 'updraftplus'), '3.2')); } public function show_admin_warning_litespeed() { $this->show_admin_warning(''.__('Warning', 'updraftplus').': '.sprintf(__('Your website is hosted using the %s web server.', 'updraftplus'), 'LiteSpeed').' '.__('Please consult this FAQ if you have problems backing up.', 'updraftplus').''); } public function show_admin_debug_warning() { $this->show_admin_warning(''.__('Notice', 'updraftplus').': '.__('UpdraftPlus\'s debug mode is on. You may see debugging notices on this page not just from UpdraftPlus, but from any other plugin installed. Please try to make sure that the notice you are seeing is from UpdraftPlus before you raise a support request.', 'updraftplus').''); } public function show_admin_warning_overdue_crons($howmany) { $ret = ' '; return $ret; } /** * Output authorisation links for any un-authorised Dropbox settings instances */ public function show_admin_warning_dropbox() { $this->get_method_auth_link('dropbox'); } /** * Output authorisation links for any un-authorised OneDrive settings instances */ public function show_admin_warning_onedrive() { $this->get_method_auth_link('onedrive'); } public function show_admin_warning_updraftvault() { $this->show_admin_warning(''.__('UpdraftPlus notice:', 'updraftplus').' '.sprintf(__('%s has been chosen for remote storage, but you are not currently connected.', 'updraftplus'), 'UpdraftPlus Vault').' '.__('Go to the remote storage settings in order to connect.', 'updraftplus'), 'updated'); } /** * Output authorisation links for any un-authorised Google Drive settings instances */ public function show_admin_warning_googledrive() { $this->get_method_auth_link('googledrive'); } /** * Output authorisation links for any un-authorised Google Cloud settings instances */ public function show_admin_warning_googlecloud() { $this->get_method_auth_link('googlecloud'); } /** * Show DreamObjects cluster migration warning */ public function show_admin_warning_dreamobjects() { $this->show_admin_warning(''.__('UpdraftPlus notice:', 'updraftplus').' '.sprintf(__('The %s endpoint is scheduled to shut down on the 1st October 2018. You will need to switch to a different end-point and migrate your data before that date. %sPlease see this article for more information%s'), 'objects-us-west-1.dream.io', '', ''), 'updated'); } /** * This method will setup the storage object and get the authentication link ready to be output with the notice * * @param String $method - the remote storage method */ public function get_method_auth_link($method) { global $updraftplus; $storage_objects_and_ids = UpdraftPlus_Storage_Methods_Interface::get_storage_objects_and_ids(array($method)); $object = $storage_objects_and_ids[$method]['object']; foreach ($this->auth_instance_ids[$method] as $instance_id) { $object->set_instance_id($instance_id); $this->show_admin_warning(''.__('UpdraftPlus notice:', 'updraftplus').' '.$object->get_authentication_link(false, false), 'updated updraft_authenticate_'.$method); } } /** * Start a download of a backup. This method is called via the AJAX action updraft_download_backup. May die instead of returning depending upon the mode in which it is called. */ public function updraft_download_backup() { try { if (empty($_REQUEST['_wpnonce']) || !wp_verify_nonce($_REQUEST['_wpnonce'], 'updraftplus_download')) die; if (empty($_REQUEST['timestamp']) || !is_numeric($_REQUEST['timestamp']) || empty($_REQUEST['type'])) exit; $findexes = empty($_REQUEST['findex']) ? array(0) : $_REQUEST['findex']; $stage = empty($_REQUEST['stage']) ? '' : $_REQUEST['stage']; $file_path = empty($_REQUEST['filepath']) ? '' : $_REQUEST['filepath']; // This call may not actually return, depending upon what mode it is called in $result = $this->do_updraft_download_backup($findexes, $_REQUEST['type'], $_REQUEST['timestamp'], $stage, false, $file_path); // In theory, if a response was already sent, then Connection: close has been issued, and a Content-Length. However, in https://updraftplus.com/forums/topic/pclzip_err_bad_format-10-invalid-archive-structure/ a browser ignores both of these, and then picks up the second output and complains. if (empty($result['already_closed'])) echo json_encode($result); } catch (Exception $e) { $log_message = 'PHP Fatal Exception error ('.get_class($e).') has occurred during download backup. Error Message: '.$e->getMessage().' (Code: '.$e->getCode().', line '.$e->getLine().' in '.$e->getFile().')'; error_log($log_message); echo json_encode(array( 'fatal_error' => true, 'fatal_error_message' => $log_message )); // @codingStandardsIgnoreLine } catch (Error $e) { $log_message = 'PHP Fatal error ('.get_class($e).') has occurred during download backup. Error Message: '.$e->getMessage().' (Code: '.$e->getCode().', line '.$e->getLine().' in '.$e->getFile().')'; error_log($log_message); echo json_encode(array( 'fatal_error' => true, 'fatal_error_message' => $log_message )); } die(); } /** * Ensure that a specified backup is present, downloading if necessary (or delete it, if the parameters so indicate). N.B. This function may die(), depending on the request being made in $stage * * @param Array $findexes - the index number of the backup archive requested * @param String $type - the entity type (e.g. 'plugins') being requested * @param Integer $timestamp - identifier for the backup being requested (UNIX epoch time) * @param Mixed $stage - the stage; valid values include (have not audited for other possibilities) at least 'delete' and 2. * @param Callable|Boolean $close_connection_callable - function used to close the connection to the caller; an array of data to return is passed. If false, then UpdraftPlus::close_browser_connection is called with a JSON version of the data. * @param String $file_path - an over-ride for where to download the file to (basename only) * * @return Array - sumary of the results. May also just die. */ public function do_updraft_download_backup($findexes, $type, $timestamp, $stage, $close_connection_callable = false, $file_path = '') { @set_time_limit(UPDRAFTPLUS_SET_TIME_LIMIT); global $updraftplus; if (!is_array($findexes)) $findexes = array($findexes); $connection_closed = false; // Check that it is a known entity type; if not, die if ('db' != substr($type, 0, 2)) { $backupable_entities = $updraftplus->get_backupable_file_entities(true); foreach ($backupable_entities as $t => $info) { if ($type == $t) $type_match = true; } if (empty($type_match)) return array('result' => 'error', 'code' => 'no_such_type'); } $debug_mode = UpdraftPlus_Options::get_updraft_option('updraft_debug_mode'); // Retrieve the information from our backup history $backup_history = UpdraftPlus_Backup_History::get_history(); foreach ($findexes as $findex) { // This is a bit ugly; these variables get placed back into $_POST (where they may possibly have come from), so that UpdraftPlus::log() can detect exactly where to log the download status. $_POST['findex'] = $findex; $_POST['type'] = $type; $_POST['timestamp'] = $timestamp; // We already know that no possible entities have an MD5 clash (even after 2 characters) // Also, there's nothing enforcing a requirement that nonces are hexadecimal $job_nonce = dechex($timestamp).$findex.substr(md5($type), 0, 3); // You need a nonce before you can set job data. And we certainly don't yet have one. $updraftplus->backup_time_nonce($job_nonce); // Set the job type before logging, as there can be different logging destinations $updraftplus->jobdata_set('job_type', 'download'); $updraftplus->jobdata_set('job_time_ms', $updraftplus->job_time_ms); // Base name $file = $backup_history[$timestamp][$type]; // Deal with multi-archive sets if (is_array($file)) $file = $file[$findex]; if (false !== strpos($file_path, '..')) { error_log("UpdraftPlus_Admin::do_updraft_download_backup : invalid file_path: $file_path"); return array('result' => __('Error: invalid path', 'updraftplus')); } if (!empty($file_path)) $file = $file_path; // Where it should end up being downloaded to $fullpath = $updraftplus->backups_dir_location().'/'.$file; if (!empty($file_path) && strpos(realpath($fullpath), realpath($updraftplus->backups_dir_location())) === false) { error_log("UpdraftPlus_Admin::do_updraft_download_backup : invalid fullpath: $fullpath"); return array('result' => __('Error: invalid path', 'updraftplus')); } if (2 == $stage) { $updraftplus->spool_file($fullpath); // We only want to remove if it was a temp file from the zip browser if (!empty($file_path)) @unlink($fullpath); // Do not return - we do not want the caller to add any output die; } if ('delete' == $stage) { @unlink($fullpath); $updraftplus->log("The file has been deleted ($file)"); return array('result' => 'deleted'); } // TODO: FIXME: Failed downloads may leave log files forever (though they are small) if ($debug_mode) $updraftplus->logfile_open($updraftplus->nonce); set_error_handler(array($updraftplus, 'php_error'), E_ALL & ~E_STRICT); $updraftplus->log("Requested to obtain file: timestamp=$timestamp, type=$type, index=$findex"); $itext = empty($findex) ? '' : $findex; $known_size = isset($backup_history[$timestamp][$type.$itext.'-size']) ? $backup_history[$timestamp][$type.$itext.'-size'] : 0; $services = isset($backup_history[$timestamp]['service']) ? $backup_history[$timestamp]['service'] : false; if (is_string($services)) $services = array($services); $updraftplus->jobdata_set('service', $services); // Fetch it from the cloud, if we have not already got it $needs_downloading = false; if (!file_exists($fullpath)) { // If the file doesn't exist and they're using one of the cloud options, fetch it down from the cloud. $needs_downloading = true; $updraftplus->log('File does not yet exist locally - needs downloading'); } elseif ($known_size > 0 && filesize($fullpath) < $known_size) { $updraftplus->log("The file was found locally (".filesize($fullpath).") but did not match the size in the backup history ($known_size) - will resume downloading"); $needs_downloading = true; } elseif ($known_size > 0 && filesize($fullpath) > $known_size) { $updraftplus->log("The file was found locally (".filesize($fullpath).") but the size is larger than what is recorded in the backup history ($known_size) - will try to continue but if errors are encountered then check that the backup is correct"); } elseif ($known_size > 0) { $updraftplus->log('The file was found locally and matched the recorded size from the backup history ('.round($known_size/1024, 1).' KB)'); } else { $updraftplus->log('No file size was found recorded in the backup history. We will assume the local one is complete.'); $known_size = filesize($fullpath); } // The AJAX responder that updates on progress wants to see this $updraftplus->jobdata_set('dlfile_'.$timestamp.'_'.$type.'_'.$findex, "downloading:$known_size:$fullpath"); if ($needs_downloading) { // Update the "last modified" time to dissuade any other instances from thinking that no downloaders are active @touch($fullpath); $msg = array( 'result' => 'needs_download', 'request' => array( 'type' => $type, 'timestamp' => $timestamp, 'findex' => $findex ) ); if ($close_connection_callable && is_callable($close_connection_callable) && !$connection_closed) { $connection_closed = true; call_user_func($close_connection_callable, $msg); } elseif (!$connection_closed) { $connection_closed = true; $updraftplus->close_browser_connection(json_encode($msg)); } UpdraftPlus_Storage_Methods_Interface::get_remote_file($services, $file, $timestamp); } // Now, be ready to spool the thing to the browser if (is_file($fullpath) && is_readable($fullpath)) { // That message is then picked up by the AJAX listener $updraftplus->jobdata_set('dlfile_'.$timestamp.'_'.$type.'_'.$findex, 'downloaded:'.filesize($fullpath).":$fullpath"); $result = 'downloaded'; } else { $updraftplus->jobdata_set('dlfile_'.$timestamp.'_'.$type.'_'.$findex, 'failed'); $updraftplus->jobdata_set('dlerrors_'.$timestamp.'_'.$type.'_'.$findex, $updraftplus->errors); $updraftplus->log('Remote fetch failed. File '.$fullpath.' did not exist or was unreadable. If you delete local backups then remote retrieval may have failed.'); $result = 'download_failed'; } restore_error_handler(); @fclose($updraftplus->logfile_handle); if (!$debug_mode) @unlink($updraftplus->logfile_name); } // The browser connection was possibly already closed, but not necessarily return array('result' => $result, 'already_closed' => $connection_closed); } /** * This is used as a callback * * @param Mixed $msg The data to be JSON encoded and sent back */ public function _updraftplus_background_operation_started($msg) { global $updraftplus; // The extra spaces are because of a bug seen on one server in handling of non-ASCII characters; see HS#11739 $updraftplus->close_browser_connection(json_encode($msg).' '); } public function updraft_ajax_handler() { global $updraftplus; $nonce = empty($_REQUEST['nonce']) ? '' : $_REQUEST['nonce']; if (!wp_verify_nonce($nonce, 'updraftplus-credentialtest-nonce') || empty($_REQUEST['subaction'])) die('Security check'); $subaction = $_REQUEST['subaction']; // Mitigation in case the nonce leaked to an unauthorised user if ('dismissautobackup' == $subaction) { if (!current_user_can('update_plugins') && !current_user_can('update_themes')) return; } elseif ('dismissexpiry' == $subaction || 'dismissdashnotice' == $subaction) { if (!current_user_can('update_plugins')) return; } else { if (!UpdraftPlus_Options::user_can_manage()) return; } // All others use _POST $data_in_get = array('get_log', 'get_fragment'); // UpdraftPlus_WPAdmin_Commands extends UpdraftPlus_Commands - i.e. all commands are in there if (!class_exists('UpdraftPlus_WPAdmin_Commands')) include_once(UPDRAFTPLUS_DIR.'/includes/class-wpadmin-commands.php'); $commands = new UpdraftPlus_WPAdmin_Commands($this); if (method_exists($commands, $subaction)) { $data = in_array($subaction, $data_in_get) ? $_GET : $_POST; // Undo WP's slashing of GET/POST data $data = UpdraftPlus_Manipulation_Functions::wp_unslash($data); // TODO: Once all commands come through here and through updraft_send_command(), the data should always come from this attribute (once updraft_send_command() is modified appropriately). if (isset($data['action_data'])) $data = $data['action_data']; try { $results = call_user_func(array($commands, $subaction), $data); } catch (Exception $e) { $log_message = 'PHP Fatal Exception error ('.get_class($e).') has occurred during '.$subaction.' subaction. Error Message: '.$e->getMessage().' (Code: '.$e->getCode().', line '.$e->getLine().' in '.$e->getFile().')'; error_log($log_message); echo json_encode(array( 'fatal_error' => true, 'fatal_error_message' => $log_message )); die; // @codingStandardsIgnoreLine } catch (Error $e) { $log_message = 'PHP Fatal error ('.get_class($e).') has occurred during '.$subaction.' subaction. Error Message: '.$e->getMessage().' (Code: '.$e->getCode().', line '.$e->getLine().' in '.$e->getFile().')'; error_log($log_message); echo json_encode(array( 'fatal_error' => true, 'fatal_error_message' => $log_message )); die; } if (is_wp_error($results)) { $results = array( 'result' => false, 'error_code' => $results->get_error_code(), 'error_message' => $results->get_error_message(), 'error_data' => $results->get_error_data(), ); } if (is_string($results)) { // A handful of legacy methods, and some which are directly the source for iframes, for which JSON is not appropriate. echo $results; } else { echo json_encode($results); } die; } // Below are all the commands not ported over into class-commands.php or class-wpadmin-commands.php if ('activejobs_list' == $subaction) { try { // N.B. Also called from autobackup.php // TODO: This should go into UpdraftPlus_Commands, once the add-ons have been ported to use updraft_send_command() echo json_encode($this->get_activejobs_list(UpdraftPlus_Manipulation_Functions::wp_unslash($_GET))); } catch (Exception $e) { $log_message = 'PHP Fatal Exception error ('.get_class($e).') has occurred during get active job list. Error Message: '.$e->getMessage().' (Code: '.$e->getCode().', line '.$e->getLine().' in '.$e->getFile().')'; error_log($log_message); echo json_encode(array( 'fatal_error' => true, 'fatal_error_message' => $log_message )); // @codingStandardsIgnoreLine } catch (Error $e) { $log_message = 'PHP Fatal error ('.get_class($e).') has occurred during get active job list. Error Message: '.$e->getMessage().' (Code: '.$e->getCode().', line '.$e->getLine().' in '.$e->getFile().')'; error_log($log_message); echo json_encode(array( 'fatal_error' => true, 'fatal_error_message' => $log_message )); } } elseif ('httpget' == $subaction) { try { // httpget $curl = empty($_REQUEST['curl']) ? false : true; echo $this->http_get(UpdraftPlus_Manipulation_Functions::wp_unslash($_REQUEST['uri']), $curl); // @codingStandardsIgnoreLine } catch (Error $e) { $log_message = 'PHP Fatal error ('.get_class($e).') has occurred during http get. Error Message: '.$e->getMessage().' (Code: '.$e->getCode().', line '.$e->getLine().' in '.$e->getFile().')'; error_log($log_message); echo json_encode(array( 'fatal_error' => true, 'fatal_error_message' => $log_message )); } catch (Exception $e) { $log_message = 'PHP Fatal Exception error ('.get_class($e).') has occurred during http get. Error Message: '.$e->getMessage().' (Code: '.$e->getCode().', line '.$e->getLine().' in '.$e->getFile().')'; error_log($log_message); echo json_encode(array( 'fatal_error' => true, 'fatal_error_message' => $log_message )); } } elseif ('doaction' == $subaction && !empty($_REQUEST['subsubaction']) && 'updraft_' == substr($_REQUEST['subsubaction'], 0, 8)) { $subsubaction = $_REQUEST['subsubaction']; try { // These generally echo and die - they will need further work to port to one of the command classes. Some may already have equivalents in UpdraftPlus_Commands, if they are used from UpdraftCentral. do_action(UpdraftPlus_Manipulation_Functions::wp_unslash($subsubaction), $_REQUEST); } catch (Exception $e) { $log_message = 'PHP Fatal Exception error ('.get_class($e).') has occurred during doaction subaction with '.$subsubaction.' subsubaction. Error Message: '.$e->getMessage().' (Code: '.$e->getCode().', line '.$e->getLine().' in '.$e->getFile().')'; error_log($log_message); echo json_encode(array( 'fatal_error' => true, 'fatal_error_message' => $log_message )); die; // @codingStandardsIgnoreLine } catch (Error $e) { $log_message = 'PHP Fatal error ('.get_class($e).') has occurred during doaction subaction with '.$subsubaction.' subsubaction. Error Message: '.$e->getMessage().' (Code: '.$e->getCode().', line '.$e->getLine().' in '.$e->getFile().')'; error_log($log_message); echo json_encode(array( 'fatal_error' => true, 'fatal_error_message' => $log_message )); die; } } else { // These can be removed after a few releases include(UPDRAFTPLUS_DIR.'/includes/deprecated-actions.php'); } die; } /** * Run a credentials test for the indicated remote storage module * * @param Array $test_settings The test parameters, including the method itself indicated in the key 'method' * @param Boolean $return_instead_of_echo Whether to return or echo the results. N.B. More than just the results to echo will be returned * @return Array|Void - the results, if they are being returned (rather than echoed). Keys: 'output' (the output), 'data' (other data) */ public function do_credentials_test($test_settings, $return_instead_of_echo = false) { $method = (!empty($test_settings['method']) && preg_match("/^[a-z0-9]+$/", $test_settings['method'])) ? $test_settings['method'] : ""; $objname = "UpdraftPlus_BackupModule_$method"; $this->logged = array(); // TODO: Add action for WP HTTP SSL stuff set_error_handler(array($this, 'get_php_errors'), E_ALL & ~E_STRICT); if (!class_exists($objname)) include_once(UPDRAFTPLUS_DIR."/methods/$method.php"); $ret = ''; $data = null; // TODO: Add action for WP HTTP SSL stuff if (method_exists($objname, "credentials_test")) { $obj = new $objname; if ($return_instead_of_echo) ob_start(); $data = $obj->credentials_test($test_settings); if ($return_instead_of_echo) $ret .= ob_get_clean(); } if (count($this->logged) >0) { $ret .= "\n\n".__('Messages:', 'updraftplus')."\n"; foreach ($this->logged as $err) { $ret .= "* $err\n"; } if (!$return_instead_of_echo) echo $ret; } restore_error_handler(); if ($return_instead_of_echo) return array('output' => $ret, 'data' => $data); } /** * Delete a backup set, whilst respecting limits on how much to delete in one go * * @uses remove_backup_set_cleanup() * @param Array $opts - deletion options; with keys backup_timestamp, delete_remote, [remote_delete_limit] * @return Array - as from remove_backup_set_cleanup() */ public function delete_set($opts) { global $updraftplus; $backups = UpdraftPlus_Backup_History::get_history(); $timestamps = (string) $opts['backup_timestamp']; $remote_delete_limit = (isset($opts['remote_delete_limit']) && $opts['remote_delete_limit'] > 0) ? (int) $opts['remote_delete_limit'] : PHP_INT_MAX; $timestamps = explode(',', $timestamps); $deleted_timestamps = ''; $delete_remote = empty($opts['delete_remote']) ? false : true; // You need a nonce before you can set job data. And we certainly don't yet have one. $updraftplus->backup_time_nonce(); // Set the job type before logging, as there can be different logging destinations $updraftplus->jobdata_set('job_type', 'delete'); $updraftplus->jobdata_set('job_time_ms', $updraftplus->job_time_ms); if (UpdraftPlus_Options::get_updraft_option('updraft_debug_mode')) { $updraftplus->logfile_open($updraftplus->nonce); set_error_handler(array($updraftplus, 'php_error'), E_ALL & ~E_STRICT); } $updraft_dir = $updraftplus->backups_dir_location(); $backupable_entities = $updraftplus->get_backupable_file_entities(true, true); $local_deleted = 0; $remote_deleted = 0; $sets_removed = 0; foreach ($timestamps as $i => $timestamp) { if (!isset($backups[$timestamp])) { return array('result' => 'error', 'message' => __('Backup set not found', 'updraftplus')); } $nonce = isset($backups[$timestamp]['nonce']) ? $backups[$timestamp]['nonce'] : ''; $delete_from_service = array(); if ($delete_remote) { // Locate backup set if (isset($backups[$timestamp]['service'])) { // Convert to an array so that there is no uncertainty about how to process it $services = is_string($backups[$timestamp]['service']) ? array($backups[$timestamp]['service']) : $backups[$timestamp]['service']; if (is_array($services)) { foreach ($services as $service) { if ($service && 'none' != $service && 'email' != $service) $delete_from_service[] = $service; } } } } $files_to_delete = array(); foreach ($backupable_entities as $key => $ent) { if (isset($backups[$timestamp][$key])) { $files_to_delete[$key] = $backups[$timestamp][$key]; } } // Delete DB foreach ($backups[$timestamp] as $key => $value) { if ('db' == strtolower(substr($key, 0, 2)) && '-size' != substr($key, -5, 5)) { $files_to_delete[$key] = $backups[$timestamp][$key]; } } // Also delete the log if ($nonce && !UpdraftPlus_Options::get_updraft_option('updraft_debug_mode')) { $files_to_delete['log'] = "log.$nonce.txt"; } $updraftplus->register_wp_http_option_hooks(); foreach ($files_to_delete as $key => $files) { if (is_string($files)) { $was_string = true; $files = array($files); } else { $was_string = false; } foreach ($files as $file) { if (is_file($updraft_dir.'/'.$file) && @unlink($updraft_dir.'/'.$file)) $local_deleted++; } if ('log' != $key && count($delete_from_service) > 0) { $storage_objects_and_ids = UpdraftPlus_Storage_Methods_Interface::get_storage_objects_and_ids($delete_from_service); foreach ($delete_from_service as $service) { if ('email' == $service || 'none' == $service || !$service) continue; $deleted = -1; $remote_obj = $storage_objects_and_ids[$service]['object']; $instance_settings = $storage_objects_and_ids[$service]['instance_settings']; $this->backups_instance_ids = empty($backups[$timestamp]['service_instance_ids'][$service]) ? array() : $backups[$timestamp]['service_instance_ids'][$service]; if (empty($instance_settings)) continue; uksort($instance_settings, array($this, 'instance_ids_sort')); foreach ($instance_settings as $instance_id => $options) { $remote_obj->set_options($options, false, $instance_id); foreach ($files as $index => $file) { if ($remote_deleted == $remote_delete_limit) { $timestamps_list = implode(',', $timestamps); return $this->remove_backup_set_cleanup(false, $backups, $local_deleted, $remote_deleted, $sets_removed, $timestamps_list, $deleted_timestamps); } $deleted = $remote_obj->delete($file); if (-1 === $deleted) { // echo __('Did not know how to delete from this cloud service.', 'updraftplus'); } elseif (false !== $deleted) { $remote_deleted++; } $itext = $index ? (string) $index : ''; if ($was_string) { unset($backups[$timestamp][$key]); if ('db' == strtolower(substr($key, 0, 2))) unset($backups[$timestamp][$key][$index.'-size']); } else { unset($backups[$timestamp][$key][$index]); unset($backups[$timestamp][$key.$itext.'-size']); if (empty($backups[$timestamp][$key])) unset($backups[$timestamp][$key]); } if (isset($backups[$timestamp]['checksums']) && is_array($backups[$timestamp]['checksums'])) { foreach (array_keys($backups[$timestamp]['checksums']) as $algo) { unset($backups[$timestamp]['checksums'][$algo][$key.$index]); } } // If we don't save the array back, then the above section will fire again for the same files - and the remote storage will be requested to delete already-deleted files, which then means no time is actually saved by the browser-backend loop method. UpdraftPlus_Backup_History::save_history($backups); } } } } } unset($backups[$timestamp]); unset($timestamps[$i]); if ('' != $deleted_timestamps) $deleted_timestamps .= ','; $deleted_timestamps .= $timestamp; UpdraftPlus_Backup_History::save_history($backups); $sets_removed++; } $timestamps_list = implode(',', $timestamps); return $this->remove_backup_set_cleanup(true, $backups, $local_deleted, $remote_deleted, $sets_removed, $timestamps_list, $deleted_timestamps); } /** * This function sorts the array of instance ids currently saved so that any instance id that is in both the saved settings and the backup history move to the top of the array, as these are likely to work. Then values that don't appear in the backup history move to the bottom. * * @param String $a - the first instance id * @param String $b - the second instance id * @return Integer - returns an integer to indicate what position the $b value should be moved in */ public function instance_ids_sort($a, $b) { if (in_array($a, $this->backups_instance_ids)) { if (in_array($b, $this->backups_instance_ids)) return 0; return -1; } return in_array($b, $this->backups_instance_ids) ? 1 : 0; } /** * Called by self::delete_set() to finish up before returning (whether the complete deletion is finished or not) * * @param Boolean $delete_complete - whether the whole set is now gone (i.e. last round) * @param Array $backups - the backup history * @param Integer $local_deleted - how many backup archives were deleted from local storage * @param Integer $remote_deleted - how many backup archives were deleted from remote storage * @param Integer $sets_removed - how many complete sets were removed * @param String $timestamps - a csv of remaining timestamps * @param String $deleted_timestamps - a csv of deleted timestamps * * @return Array - information on the status, suitable for returning to the UI */ public function remove_backup_set_cleanup($delete_complete, $backups, $local_deleted, $remote_deleted, $sets_removed, $timestamps, $deleted_timestamps) { global $updraftplus; $updraftplus->register_wp_http_option_hooks(false); UpdraftPlus_Backup_History::save_history($backups); $updraftplus->log("Local files deleted: $local_deleted. Remote files deleted: $remote_deleted"); if ($delete_complete) { $set_message = __('Backup sets removed:', 'updraftplus'); $local_message = __('Local files deleted:', 'updraftplus'); $remote_message = __('Remote files deleted:', 'updraftplus'); if (UpdraftPlus_Options::get_updraft_option('updraft_debug_mode')) { restore_error_handler(); } return array('result' => 'success', 'set_message' => $set_message, 'local_message' => $local_message, 'remote_message' => $remote_message, 'backup_sets' => $sets_removed, 'backup_local' => $local_deleted, 'backup_remote' => $remote_deleted); } else { return array('result' => 'continue', 'backup_local' => $local_deleted, 'backup_remote' => $remote_deleted, 'backup_sets' => $sets_removed, 'timestamps' => $timestamps, 'deleted_timestamps' => $deleted_timestamps); } } /** * Get the history status HTML and other information * * @param Boolean $rescan - whether to rescan local storage first * @param Boolean $remotescan - whether to rescan remote storage first * @param Boolean $debug - whether to return debugging information also * * @return Array - the information requested */ public function get_history_status($rescan, $remotescan, $debug = false) { global $updraftplus; if ($rescan) $messages = UpdraftPlus_Backup_History::rebuild($remotescan, false, $debug); $backup_history = UpdraftPlus_Backup_History::get_history(); $output = UpdraftPlus_Backup_History::existing_backup_table($backup_history); $data = array(); if (!empty($messages) && is_array($messages)) { $noutput = ''; foreach ($messages as $msg) { if (empty($msg['code']) || 'file-listing' != $msg['code']) { $noutput .= ''; $updraftplus->log_e('Restore successful!'); echo '
'; $updraftplus->log('Restore successful'); $s_val = 1; if (!empty($this->entities_to_restore) && is_array($this->entities_to_restore)) { foreach ($this->entities_to_restore as $k => $v) { if ('db' != $v) $s_val = 2; } } $pval = $updraftplus->have_addons ? 1 : 0; echo ''.__('Actions', 'updraftplus').': '.__('Return to UpdraftPlus Configuration', 'updraftplus').''; return; } elseif (is_wp_error($backup_success)) { echo ''; $updraftplus->log_e('Restore failed...'); echo '
'; $updraftplus->log_wp_error($backup_success); $updraftplus->log('Restore failed'); $updraftplus->list_errors(); echo ''.__('Actions', 'updraftplus').': '.__('Return to UpdraftPlus Configuration', 'updraftplus').''; return; } elseif (false === $backup_success) { // This means, "not yet - but stay on the page because we may be able to do it later, e.g. if the user types in the requested information" echo ''; $updraftplus->log_e('Restore failed...'); echo '
'; $updraftplus->log("Restore failed"); $updraftplus->list_errors(); echo ''.__('Actions', 'updraftplus').': '.__('Return to UpdraftPlus Configuration', 'updraftplus').''; return; } } if (isset($_REQUEST['action']) && 'updraft_delete_old_dirs' == $_REQUEST['action']) { $nonce = empty($_REQUEST['updraft_delete_old_dirs_nonce']) ? '' : $_REQUEST['updraft_delete_old_dirs_nonce']; if (!wp_verify_nonce($nonce, 'updraftplus-credentialtest-nonce')) die('Security check'); $this->delete_old_dirs_go(); return; } if (!empty($_REQUEST['action']) && 'updraftplus_broadcastaction' == $_REQUEST['action'] && !empty($_REQUEST['subaction'])) { $nonce = (empty($_REQUEST['nonce'])) ? "" : $_REQUEST['nonce']; if (!wp_verify_nonce($nonce, 'updraftplus-credentialtest-nonce')) die('Security check'); do_action($_REQUEST['subaction']); return; } if (isset($_GET['error'])) { // This is used by Microsoft OneDrive authorisation failures (May 15). I am not sure what may have been using the 'error' GET parameter otherwise - but it is harmless. if (!empty($_GET['error_description'])) { $this->show_admin_warning(htmlspecialchars($_GET['error_description']).' ('.htmlspecialchars($_GET['error']).')', 'error'); } else { $this->show_admin_warning(htmlspecialchars($_GET['error']), 'error'); } } if (isset($_GET['message'])) $this->show_admin_warning(htmlspecialchars($_GET['message'])); if (isset($_GET['action']) && 'updraft_create_backup_dir' == $_GET['action'] && isset($_GET['nonce']) && wp_verify_nonce($_GET['nonce'], 'create_backup_dir')) { $created = $this->create_backup_dir(); if (is_wp_error($created)) { echo ''.__('Backup directory could not be created', 'updraftplus').'...
';
echo '
'.__('Backup directory successfully created.', 'updraftplus').'
'.__('For even more features and personal support, check out ', 'updraftplus').'UpdraftPlus Premium.
' : ""; echo "'; if ($include_form_container) { $enter_credentials_end .= ''; } else { $enter_credentials_end .= ''; } $enter_credentials_end .= '' . __('Processing', 'updraftplus') . '...
'; $enter_credentials_end .= ''; $enter_credentials_end .= $include_form_container ? '' : ' '."\n";
$ret .= '(...)
'."\n";
$ret .= '
(...)
';
$ret .= '
'; $ret .= apply_filters('updraft_backupnow_modal_afteroptions', '', ''); return $ret; } /** * Also used by the auto-backups add-on * * @param Boolean $wide_format Whether to return data in a wide format * @param Boolean $print_active_jobs Whether to include currently active jobs * @return String - the HTML output */ public function render_active_jobs_and_log_table($wide_format = false, $print_active_jobs = true) { ?>
'.__('Your WordPress install has old directories from its state before you restored/migrated (technical information: these are suffixed with -old). You should press this button to delete them as soon as you have verified that the restoration worked.', 'updraftplus').'
'; } ?> '; } /** * Return cron status information about a specified in-progress job * * @param Boolean|String $job_id - the job to get information about; or, if not specified, all jobs * * @return Array|Boolean - the requested information, or false if it was not found. Format differs depending on whether info on all jobs, or a single job, was requested. */ public function get_cron($job_id = false) { $cron = get_option('cron'); if (!is_array($cron)) $cron = array(); if (false === $job_id) return $cron; foreach ($cron as $time => $job) { if (!isset($job['updraft_backup_resume'])) continue; foreach ($job['updraft_backup_resume'] as $hook => $info) { if (isset($info['args'][1]) && $job_id == $info['args'][1]) { global $updraftplus; $jobdata = $updraftplus->jobdata_getarray($job_id); return is_array($jobdata) ? array($time, $jobdata) : false; } } } } /** * Print active Jobs * * @param boolean $this_job_only A value for $this_job_only also causes something to always be returned (to allow detection of the job having started on the front-end) * @return [type] [description] */ private function print_active_jobs($this_job_only = false) { $cron = $this->get_cron(); $ret = ''; foreach ($cron as $time => $job) { if (isset($job['updraft_backup_resume'])) { foreach ($job['updraft_backup_resume'] as $hook => $info) { if (isset($info['args'][1])) { $job_id = $info['args'][1]; if (false === $this_job_only || $job_id == $this_job_only) { $ret .= $this->print_active_job($job_id, false, $time, $info['args'][0]); } } } } } // A value for $this_job_only implies that output is required if (false !== $this_job_only && !$ret) { $ret = $this->print_active_job($this_job_only); if ('' == $ret) { // The presence of the exact ID matters to the front-end - indicates that the backup job has at least begun $ret = ''.__('Old directories successfully removed.', 'updraftplus').'
',__('Old directory removal failed for some reason. You may want to do this manually.', 'updraftplus').'
'.__('The folder was created, but we had to change its file permissions to 777 (world-writable) to be able to write to it. You should check with your hosting provider that this will not cause any problems', 'updraftplus').'
'; return true; } else { @$wp_filesystem->chmod($default_backup_dir, 0775); $show_dir = (0 === strpos($default_backup_dir, ABSPATH)) ? substr($default_backup_dir, strlen(ABSPATH)) : $default_backup_dir; return new WP_Error('writable_error', __('The folder exists, but your webserver does not have permission to write to it.', 'updraftplus').' '.__('You will need to consult with your web hosting provider to find out how to set permissions for a WordPress plugin to write to the directory.', 'updraftplus').' ('.$show_dir.')'); } } return true; } /** * scans the content dir to see if any -old dirs are present * * @param Boolean $print_as_comment Echo information in an HTML comment * @return Boolean */ private function scan_old_dirs($print_as_comment = false) { global $updraftplus; $dirs = scandir(untrailingslashit(WP_CONTENT_DIR)); if (!is_array($dirs)) $dirs = array(); $dirs_u = @scandir($updraftplus->backups_dir_location()); if (!is_array($dirs_u)) $dirs_u = array(); foreach (array_merge($dirs, $dirs_u) as $dir) { if (preg_match('/-old$/', $dir)) { if ($print_as_comment) echo ''; return true; } } // No need to scan ABSPATH - we don't backup there if (is_dir(untrailingslashit(WP_PLUGIN_DIR).'-old')) { if ($print_as_comment) echo ''; return true; } return false; } /** * Outputs html for a storage method using the parameters passed in, this version should be removed when all remote storages use the multi version * * @param String $method a list of methods to be used when * @param String $header the table header content * @param String $contents the table contents */ public function storagemethod_row($method, $header, $contents) { ?>$text
$text
"; if ($echo) echo $ret; return $ret; } public function optionfilter_split_every($value) { return max(absint($value), UPDRAFTPLUS_SPLIT_MIN); } /** * Check if curl exists; if not, print or return appropriate error messages * * @param String $service the service description (used only for user-visible messages - so, use the description) * @param Boolean $has_fallback set as true if the lack of Curl only affects the ability to connect over SSL * @param String $extraclass an extra CSS class for any resulting message, passed on to show_double_warning() * @param Boolean $echo_instead_of_return whether the result should be echoed or returned * @return String any resulting message, if $echo_instead_of_return was set */ public function curl_check($service, $has_fallback = false, $extraclass = '', $echo_instead_of_return = true) { $ret = ''; // Check requirements if (!function_exists("curl_init") || !function_exists('curl_exec')) { $ret .= $this->show_double_warning(''.__('Warning', 'updraftplus').': '.sprintf(__("Your web server's PHP installation does not included a required (for %s) module (%s). Please contact your web hosting provider's support and ask for them to enable it.", 'updraftplus'), $service, 'Curl').' ', $extraclass, false); } else { $curl_version = curl_version(); $curl_ssl_supported= ($curl_version['features'] & CURL_VERSION_SSL); if (!$curl_ssl_supported) { if ($has_fallback) { $ret .= ''.__('Warning', 'updraftplus').': '.sprintf(__("Your web server's PHP/Curl installation does not support https access. Communications with %s will be unencrypted. Ask your web host to install Curl/SSL in order to gain the ability for encryption (via an add-on).", 'updraftplus'), $service).'
'; } else { $ret .= $this->show_double_warning(''.__('Warning', 'updraftplus').': '.sprintf(__("Your web server's PHP/Curl installation does not support https access. We cannot access %s without this support. Please contact your web hosting provider's support. %s requires Curl+https. Please do not file any support requests; there is no alternative.", 'updraftplus'), $service, $service).'
', $extraclass, false); } } else { $ret .= ''.sprintf(__("Good news: Your site's communications with %s can be encrypted. If you see any errors to do with encryption, then look in the 'Expert Settings' for more help.", 'updraftplus'), $service).'
'; } } if ($echo_instead_of_return) { echo $ret; } else { return $ret; } } /** * Get backup information in HTML format for a specific backup * * @param array $backup_history all backups history * @param string $key backup timestamp * @param string $nonce backup nonce * @return string HTML-formatted backup information */ public function raw_backup_info($backup_history, $key, $nonce) { global $updraftplus; $backup = $backup_history[$key]; $only_remote_sent = (!empty($backup['service']) && (array('remotesend') === $backup['service'] || 'remotesend' === $backup['service'])) ? true : false; $pretty_date = get_date_from_gmt(gmdate('Y-m-d H:i:s', (int) $key), 'M d, Y G:i'); $rawbackup = "'; $backupable_entities = $updraftplus->get_backupable_file_entities(true, true); $checksums = $updraftplus->which_checksums(); foreach ($backupable_entities as $type => $info) { if (!isset($backup[$type])) continue; $rawbackup .= $updraftplus->printfile($info['description'], $backup, $type, $checksums, $jd, true); } $total_size = 0; foreach ($backup as $ekey => $files) { if ('db' == strtolower(substr($ekey, 0, 2)) && '-size' != substr($ekey, -5, 5)) { $rawbackup .= $updraftplus->printfile(__('Database', 'updraftplus'), $backup, $ekey, $checksums, $jd, true); } if (!isset($backupable_entities[$ekey]) && ('db' != substr($ekey, 0, 2) || '-size' == substr($ekey, -5, 5))) continue; if (is_string($files)) $files = array($files); foreach ($files as $findex => $file) { $size_key = (0 == $findex) ? $ekey.'-size' : $ekey.$findex.'-size'; $total_size = (false === $total_size || !isset($backup[$size_key]) || !is_numeric($backup[$size_key])) ? false : $total_size + $backup[$size_key]; } } $services = empty($backup['service']) ? array('none') : $backup['service']; if (!is_array($services)) $services = array('none'); $rawbackup .= ''.__('Uploaded to:', 'updraftplus').' '; $show_services = ''; foreach ($services as $serv) { if ('none' == $serv || '' == $serv) { $add_none = true; } elseif (isset($updraftplus->backup_methods[$serv])) { $show_services .= $show_services ? ', '.$updraftplus->backup_methods[$serv] : $updraftplus->backup_methods[$serv]; } else { $show_services .= $show_services ? ', '.$serv : $serv; } } if ('' == $show_services && $add_none) $show_services .= __('None', 'updraftplus'); $rawbackup .= $show_services; if (false !== $total_size) { $rawbackup .= '
'.__('Total backup size:', 'updraftplus').' '.UpdraftPlus_Manipulation_Functions::convert_numeric_size_to_text($total_size).''; } $rawbackup .= '
'.print_r($backup, true).''; if (!empty($jd) && is_array($jd)) { $rawbackup .= '
'.print_r($jd, true).''; } return esc_attr($rawbackup); } private function download_db_button($bkey, $key, $esc_pretty_date, $backup, $accept = array()) { if (!empty($backup['meta_foreign']) && isset($accept[$backup['meta_foreign']])) { $desc_source = $accept[$backup['meta_foreign']]['desc']; } else { $desc_source = __('unknown source', 'updraftplus'); } $ret = ''; if ('db' == $bkey) { $dbt = empty($backup['meta_foreign']) ? esc_attr(__('Database', 'updraftplus')) : esc_attr(sprintf(__('Database (created by %s)', 'updraftplus'), $desc_source)); } else { $dbt = __('External database', 'updraftplus').' ('.substr($bkey, 2).')'; } $ret .= $this->download_button($bkey, $key, 0, null, '', $dbt, $esc_pretty_date, '0'); return $ret; } /** * Go through each of the file entities * * @param Array $backup An array of meta information * @param Integer $key Backup timestamp (epoch time) * @param Array $accept An array of values to be accepted from vaules within $backup * @param String $entities Entities to be added * @param String $esc_pretty_date Whether the button needs to escape the pretty date format * @return String - the resulting HTML */ public function download_buttons($backup, $key, $accept, &$entities, $esc_pretty_date) { global $updraftplus; $ret = ''; $backupable_entities = $updraftplus->get_backupable_file_entities(true, true); $first_entity = true; foreach ($backupable_entities as $type => $info) { if (!empty($backup['meta_foreign']) && 'wpcore' != $type) continue; $ide = ''; if ('wpcore' == $type) $wpcore_restore_descrip = $info['description']; if (empty($backup['meta_foreign'])) { $sdescrip = preg_replace('/ \(.*\)$/', '', $info['description']); if (strlen($sdescrip) > 20 && isset($info['shortdescription'])) $sdescrip = $info['shortdescription']; } else { $info['description'] = 'WordPress'; if (isset($accept[$backup['meta_foreign']])) { $desc_source = $accept[$backup['meta_foreign']]['desc']; $ide .= sprintf(__('Backup created by: %s.', 'updraftplus'), $accept[$backup['meta_foreign']]['desc']).' '; } else { $desc_source = __('unknown source', 'updraftplus'); $ide .= __('Backup created by unknown source (%s) - cannot be restored.', 'updraftplus').' '; } $sdescrip = (empty($accept[$backup['meta_foreign']]['separatedb'])) ? sprintf(__('Files and database WordPress backup (created by %s)', 'updraftplus'), $desc_source) : sprintf(__('Files backup (created by %s)', 'updraftplus'), $desc_source); if ('wpcore' == $type) $wpcore_restore_descrip = $sdescrip; } if (isset($backup[$type])) { if (!is_array($backup[$type])) $backup[$type] = array($backup[$type]); $howmanyinset = count($backup[$type]); $expected_index = 0; $index_missing = false; $set_contents = ''; $entities .= "/$type="; $whatfiles = $backup[$type]; ksort($whatfiles); foreach ($whatfiles as $findex => $bfile) { $set_contents .= ('' == $set_contents) ? $findex : ",$findex"; if ($findex != $expected_index) $index_missing = true; $expected_index++; } $entities .= $set_contents.'/'; if (!empty($backup['meta_foreign'])) { $entities .= '/plugins=0//themes=0//uploads=0//others=0/'; } $printing_first = true; foreach ($whatfiles as $findex => $bfile) { $pdescrip = ($findex > 0) ? $sdescrip.' ('.($findex+1).')' : $sdescrip; if ($printing_first) { $ide .= __('Press here to download or browse', 'updraftplus').' '.strtolower($info['description']); } else { $ret .= ' '; } else { $printing_first = false; } } } } return $ret; } public function date_label($pretty_date, $key, $backup, $jobdata, $nonce, $simple_format = false) { $pretty_date = $simple_format ? $pretty_date : '
'.__('Why am I seeing this?', 'updraftplus').'
'; foreach ($wp_filesystem->errors->get_error_messages() as $message) show_message($message); exit; } return $restore_options; } /** * Carry out the restore process * * @param Integer $timestamp Identifying the backup to be restored * @param Array|null $continuation_data For continuing a multi-stage restore (code believed to be incomplete) * @return Boolean|WP_Error WP_Error indicates a terminal failure; false indicates not-yet complete (not necessarily terminal); true indicates complete. */ private function restore_backup($timestamp, $continuation_data = null) { global $updraftplus; $backup_set = UpdraftPlus_Backup_History::get_history($timestamp); if (empty($backup_set)) { echo ''.__('This backup does not exist in the backup history - restoration aborted. Timestamp:', 'updraftplus')." $timestamp
'.__('ABORT: Could not find the information on which entities to restore.', 'updraftplus').'
'; echo ''.__('If making a request for support, please include this information:', 'updraftplus').' '.count($_POST).' : '.htmlspecialchars(serialize($_POST)).'
'; return new WP_Error('missing_info', 'Backup information not found'); } // This is used in painting the admin page after a successful restore $this->entities_to_restore = $entities_to_restore; // This will be removed by self::post_restore_clean_up() set_error_handler(array($updraftplus, 'php_error'), E_ALL & ~E_STRICT); // $_POST['updraft_restore'] is typically something like: array('db', 'plugins', 'themes'), etc. // If not options were supplied in $restore_options, then process things in $_POST to get them if (empty($restore_options)) { // Gather the restore options into one place - code after here should read the options, and not the HTTP variables $restore_options = array(); if (!empty($_POST['updraft_restorer_restore_options'])) { parse_str(stripslashes($_POST['updraft_restorer_restore_options']), $restore_options); } $restore_options['updraft_encryptionphrase'] = empty($_POST['updraft_encryptionphrase']) ? '' : (string) stripslashes($_POST['updraft_encryptionphrase']); $restore_options['updraft_restorer_wpcore_includewpconfig'] = empty($_POST['updraft_restorer_wpcore_includewpconfig']) ? false : true; $restore_options['updraft_incremental_restore_point'] = empty($restore_options['updraft_incremental_restore_point']) ? -1 : (int) $restore_options['updraft_incremental_restore_point']; $updraftplus->jobdata_set('restore_options', $restore_options); } // If updraft_incremental_restore_point is equal to -1 then this is either not a incremental restore or we are going to restore up to the latest increment, so there is no need to prune the backup set of any unwanted backup archives. if (isset($restore_options['updraft_incremental_restore_point']) && $restore_options['updraft_incremental_restore_point'] > 0) { $restore_point = $restore_options['updraft_incremental_restore_point']; foreach ($backup_set['incremental_sets'] as $increment_timestamp => $entities) { if ($increment_timestamp > $restore_point) { foreach ($entities as $entity => $backups) { foreach ($backups as $key => $value) { unset($backup_set[$entity][$key]); } } } } } // Restore in the most helpful order uksort($backup_set, array('UpdraftPlus_Manipulation_Functions', 'sort_restoration_entities')); // Now log. We first remove any encryption passphrase from the log data. $copy_restore_options = $restore_options; if (!empty($copy_restore_options['updraft_encryptionphrase'])) $copy_restore_options['updraft_encryptionphrase'] = '***'; $updraftplus->log("Restore job started. Entities to restore: ".implode(', ', array_flip($entities_to_restore)).'. Restore options: '.json_encode($copy_restore_options)); $backup_set['timestamp'] = $timestamp; // We use a single object for each entity, because we want to store information about the backup set if (!class_exists('Updraft_Restorer')) include_once(UPDRAFTPLUS_DIR.'/restorer.php'); echo "'; ob_start(); $history = UpdraftPlus_Backup_History::get_history(); var_dump($history); $response["html"] .= ob_get_clean(); $response['html'] .= ''; $response['html'] .= '
'; $updraft_dir = $updraftplus->backups_dir_location(); $raw_output = array(); $d = dir($updraft_dir); while (false !== ($entry = $d->read())) { $fp = $updraft_dir.'/'.$entry; $mtime = filemtime($fp); if (is_dir($fp)) { $size = ' d'; } elseif (is_link($fp)) { $size = ' l'; } elseif (is_file($fp)) { $size = sprintf("%8.1f", round(filesize($fp)/1024, 1)).' '.gmdate('r', $mtime); } else { $size = ' ?'; } if (preg_match('/^log\.(.*)\.txt$/', $entry, $lmatch)) $entry = ''.$entry.''; $raw_output[$mtime] = empty($raw_output[$mtime]) ? sprintf("%s %s\n", $size, $entry) : $raw_output[$mtime].sprintf("%s %s\n", $size, $entry); } @$d->close(); krsort($raw_output, SORT_NUMERIC); foreach ($raw_output as $line) { $response['html'] .= $line; } $response['html'] .= ''; $response['html'] .= '
'.htmlspecialchars($opt).' | '.htmlspecialchars(print_r(UpdraftPlus_Options::get_updraft_option($opt), true)).' | '; } $response['html'] .= '
', '', $response['html']); $response['html'] = str_replace('', '', $response['html']); } return $response; } /** * This will call any wp_action * * @param Array|Null $data The array of data with the vaules for wpaction * @param Callable|Boolean $close_connection_callable A callable to call to close the browser connection, or true for a default suitable for internal use, or false for none * @return Array - results */ public function call_wp_action($data = null, $close_connection_callable = false) { global $updraftplus; ob_start(); $res = 'Request received: '; if (preg_match('/^([^:]+)+:(.*)$/', $data['wpaction'], $matches)) { $action = $matches[1]; if (null === ($args = json_decode($matches[2], true))) { $res .= "The parameters (should be JSON) could not be decoded"; $action = false; } else { if (is_string($args)) $args = array($args); $res .= "Will despatch action: ".htmlspecialchars($action).", parameters: ".htmlspecialchars(implode(',', $args)); } } else { $action = $data['wpaction']; $res .= "Will despatch action: ".htmlspecialchars($action).", no parameters"; } $ret = ob_get_clean(); // Need to add this as the close browser should only work for UDP if ($close_connection_callable) { if (is_callable($close_connection_callable)) { call_user_func($close_connection_callable, array('r' => $res)); } else { $updraftplus->close_browser_connection(json_encode(array('r' => $res))); } } if (!empty($action)) { if (!empty($args)) { ob_start(); $returned = do_action_ref_array($action, $args); $output = ob_get_clean(); $res .= " - do_action_ref_array Trigger "; } else { ob_start(); do_action($action); $output = ob_get_contents(); ob_end_clean(); $res .= " - do_action Trigger "; } } $response['response'] = $res; $response['log'] = $output; // Check if response is empty if (!empty($returned)) $response['status'] = $returned; return $response; } /** * Enqueue JSTree JavaScript and CSS, taking into account whether it is already enqueued, and current debug settings */ public function enqueue_jstree() { global $updraftplus; static $already_enqueued = false; if ($already_enqueued) return; $already_enqueued = true; $jstree_enqueue_version = $updraftplus->use_unminified_scripts() ? '3.3'.'.'.time() : '3.3'; $min_or_not = $updraftplus->use_unminified_scripts() ? '' : '.min'; wp_enqueue_script('jstree', UPDRAFTPLUS_URL.'/includes/jstree/jstree'.$min_or_not.'.js', array('jquery'), $jstree_enqueue_version); wp_enqueue_style('jstree', UPDRAFTPLUS_URL.'/includes/jstree/themes/default/style'.$min_or_not.'.css', array(), $jstree_enqueue_version); } /** * Detects byte-order mark at the start of common files and change waning message texts * * @return string|boolean BOM warning text or false if not bom characters detected */ public function get_bom_warning_text() { $files_to_check = array( ABSPATH.'wp-config.php', get_template_directory().DIRECTORY_SEPARATOR.'functions.php', ); if (is_child_theme()) { $files_to_check[] = get_stylesheet_directory().DIRECTORY_SEPARATOR.'functions.php'; } $corrupted_files = array(); foreach ($files_to_check as $file) { if (!file_exists($file)) continue; if (false === ($fp = fopen($file, 'r'))) continue; if (false === ($file_data = fread($fp, 8192))); fclose($fp); $substr_file_data = array(); for ($substr_length = 2; $substr_length <= 5; $substr_length++) { $substr_file_data[$substr_length] = substr($file_data, 0, $substr_length); } // Detect UTF-7, UTF-8, UTF-16 (BE), UTF-16 (LE), UTF-32 (BE) & UTF-32 (LE) Byte order marks (BOM) $bom_decimal_representations = array( array(43, 47, 118, 56), // UTF-7 (Hexadecimal: 2B 2F 76 38) array(43, 47, 118, 57), // UTF-7 (Hexadecimal: 2B 2F 76 39) array(43, 47, 118, 43), // UTF-7 (Hexadecimal: 2B 2F 76 2B) array(43, 47, 118, 47), // UTF-7 (Hexadecimal: 2B 2F 76 2F) array(43, 47, 118, 56, 45), // UTF-7 (Hexadecimal: 2B 2F 76 38 2D) array(239, 187, 191), // UTF-8 (Hexadecimal: 2B 2F 76 38 2D) array(254, 255), // UTF-16 (BE) (Hexadecimal: FE FF) array(255, 254), // UTF-16 (LE) (Hexadecimal: FF FE) array(0, 0, 254, 255), // UTF-32 (BE) (Hexadecimal: 00 00 FE FF) array(255, 254, 0, 0), // UTF-32 (LE) (Hexadecimal: FF FE 00 00) ); foreach ($bom_decimal_representations as $bom_decimal_representation) { $no_of_chars = count($bom_decimal_representation); array_unshift($bom_decimal_representation, 'C*'); $binary = call_user_func_array('pack', $bom_decimal_representation); if ($binary == $substr_file_data[$no_of_chars]) { $corrupted_files[] = $file; break; } } } if (empty($corrupted_files)) { return false; } else { $corrupted_files_count = count($corrupted_files); return ''.__('Warning', 'updraftplus').': '.sprintf(_n('The file %s has a "byte order mark" (BOM) at its beginning.', 'The files %s have a "byte order mark" (BOM) at their beginning.', $corrupted_files_count, 'updraftplus'), ''.implode(', ', $corrupted_files).'').' '.__('Follow this link for more information', 'updraftplus').''; } } /** * Gets an instance of the "UpdraftPlus_UpdraftCentral_Cloud" class which will be * used to login or register the user to the UpdraftCentral cloud * * @return object */ public function get_updraftcentral_cloud() { if (!class_exists('UpdraftPlus_UpdraftCentral_Cloud')) include_once(UPDRAFTPLUS_DIR.'/includes/updraftcentral.php'); return new UpdraftPlus_UpdraftCentral_Cloud(); } /** * This function will build and return the UpdraftPlus tempoaray clone version select widget * * @return string - the UpdraftPlus tempoary clone version select widget */ public function updraftplus_clone_versions() { $output = '
'; $output .= ''.sprintf(__('%s version:', 'updraftplus'), 'PHP').' '; $output .= $this->output_select_data($this->php_versions, 'php'); $output .= '
'; $output .= ''; $output .= ' '.sprintf(__('%s version:', 'updraftplus'), 'WordPress').' '; $output .= $this->output_select_data($this->get_wordpress_versions(), 'wp'); $output .= '
'; $output .= ''; $output .= ''; $output .= ''; $output .= '
'; return $output; } /** * This function will output a select input using the passed in values. * * @param array $data - the keys and values for the select * @param string $name - the name of the items in the select input * * @return string - the output of the select input */ public function output_select_data($data, $name) { $name_version = $this->get_current_version($name); $output = ''; return $output; } /** * This function will output the clones network information * * @param string $url - the clone URL * * @return string - the clone network information */ public function updraftplus_clone_info($url) { global $updraftplus; if (!empty($url)) { $content = '' . __('Your clone has started and will be available at the following URLs once it is ready.', 'updraftplus') . '
'; $content .= '' . __('Front page:', 'updraftplus') . ' ' . esc_html($url) . '
'; $content .= '' . __('Dashboard:', 'updraftplus') . ' ' . esc_html(trailingslashit($url)) . 'wp-admin
'; $content .= '' . __('Your clone has started, network information is not yet available but will be displayed here and at your updraftplus.com account once it is ready.', 'updraftplus') . '
'; $content .= ''; } return $content; } /** * This function will build and return an array of major WordPress versions, the array is built by calling the WordPress version API once every 24 hours and adding any new entires to our existing array of versions. * * @return array - an array of WordPress major versions */ public function get_wordpress_versions() { $versions_info = get_site_transient('update_core'); if (isset($versions_info->updates)) { foreach ($versions_info->updates as $key => $info) { if (!isset($info->version)) continue; $parts = explode(".", $info->version); $version = $parts[0] . "." . $parts[1]; if (in_array($version, $this->wp_versions)) continue; $this->wp_versions[] = $version; } } $key = array_search($this->get_current_version('wp'), $this->wp_versions); if ($key) { $this->wp_versions = array_slice($this->wp_versions, $key); } $version_array = $this->wp_versions; return $version_array; } /** * This function will get the current version the server is running for the passed in item e.g WordPress or PHP * * @param string $name - the thing we want to get the version for e.g WordPress or PHP * * @return string - returns the current version of the passed in item */ public function get_current_version($name) { $version = ''; if ('php' == $name) { $parts = explode(".", PHP_VERSION); $version = $parts[0] . "." . $parts[1]; } elseif ('wp' == $name) { global $updraftplus; $wp_version = $updraftplus->get_wordpress_version(); $parts = explode(".", $wp_version); $version = $parts[0] . "." . $parts[1]; } return $version; } }