diff --git a/class/class-mainwp-child-updraft-plus-backups.php b/class/class-mainwp-child-updraft-plus-backups.php index 4cb97448..2fe4f73f 100644 --- a/class/class-mainwp-child-updraft-plus-backups.php +++ b/class/class-mainwp-child-updraft-plus-backups.php @@ -1,4 +1,5 @@ is_plugin_installed = true; - } - - if ( ! $this->is_plugin_installed ) { - return; - } - - add_filter( 'mainwp_site_sync_others_data', array( $this, 'sync_others_data' ), 10, 2 ); - add_filter( 'updraftplus_save_last_backup', array( __CLASS__, 'hook_updraft_plus_save_last_backup' ) ); + return static::$instance; + } + + /** + * MainWP_Child_Updraft_Plus_Backups constructor. + * + * Run any time class is called. + */ + public function __construct() + { + require_once ABSPATH . 'wp-admin/includes/plugin.php'; // NOSONAR - WP compatible. + if (is_plugin_active($this->updraftplus_file_path) && defined('UPDRAFTPLUS_DIR')) { + $this->is_plugin_installed = true; } - /** - * Hook UpdraftPlus save last backup. - * - * @param array $last_backup Backup array. - * - * @return array $last_backup Return response array. - * - * @uses \MainWP\Child\MainWP_Utility::update_lasttime_backup() - */ - public static function hook_updraft_plus_save_last_backup( $last_backup ) { - if ( ! is_array( $last_backup ) ) { - return $last_backup; - } - - if ( isset( $last_backup['backup_time'] ) ) { - $backup_time = $last_backup['backup_time']; - if ( $last_backup['success'] ) { - MainWP_Utility::update_lasttime_backup( 'updraftplus', $backup_time ); - } - } - return $last_backup; + if (! $this->is_plugin_installed) { + return; } - /** - * Sync other data from $data[] and merge with $information[] - * - * @param array $information Returned response array for MainWP BackWPup Extension actions. - * @param array $data Other data to sync to $information array. - * - * @return array $information Returned information array with both sets of data. - * - * @uses MainWP_Child_Updraft_Plus_Backups::is_plugin_installed() - * @uses MainWP_Child_Updraft_Plus_Backups::is_plugin_installed() - * @uses MainWP_Exception() - */ - public function sync_others_data( $information, $data = array() ) { - try { - if ( isset( $data['syncUpdraftData'] ) ) { - $info = $data['syncUpdraftData']; - if ( $this->is_plugin_installed ) { - $with_hist = true; - if ( version_compare( $info, '1.7', '>=' ) ) { - $with_hist = false; - } - $information['syncUpdraftData'] = $this->get_sync_data( $with_hist ); - } - } - if ( isset( $data['sync_Updraftvault_quota_text'] ) && $data['sync_Updraftvault_quota_text'] && $this->is_plugin_installed ) { - $information['sync_Updraftvault_quota_text'] = $this->connected_html(); - } - } catch ( MainWP_Exception $e ) { - // ok. - } - - return $information; + add_filter('mainwp_site_sync_others_data', array($this, 'sync_others_data'), 10, 2); + add_filter('updraftplus_save_last_backup', array(__CLASS__, 'hook_updraft_plus_save_last_backup')); + } + + /** + * Hook UpdraftPlus save last backup. + * + * @param array $last_backup Backup array. + * + * @return array $last_backup Return response array. + * + * @uses \MainWP\Child\MainWP_Utility::update_lasttime_backup() + */ + public static function hook_updraft_plus_save_last_backup($last_backup) + { + if (! is_array($last_backup)) { + return $last_backup; } - /** - * MainWP UpdraftPlus Extension actions. - * - * @uses \MainWP\Child\MainWP_Child_Updraft_Plus_Backups::is_plugin_installed() - * @uses \MainWP\Child\MainWP_Child_Updraft_Plus_Backups::required_files() - * @uses \MainWP\Child\MainWP_Child_Updraft_Plus_Backups::set_showhide() - * @uses \MainWP\Child\MainWP_Child_Updraft_Plus_Backups::save_settings() - * @uses \MainWP\Child\MainWP_Child_Updraft_Plus_Backups::addons_connect() - * @uses \MainWP\Child\MainWP_Child_Updraft_Plus_Backups::backup_now() - * @uses \MainWP\Child\MainWP_Child_Updraft_Plus_Backups::activejobs_list() - * @uses \MainWP\Child\MainWP_Child_Updraft_Plus_Backups::diskspaceused() - * @uses \MainWP\Child\MainWP_Child_Updraft_Plus_Backups::activejobs_list() - * @uses \MainWP\Child\MainWP_Child_Updraft_Plus_Backups::last_backup_html() - * @uses \MainWP\Child\MainWP_Child_Updraft_Plus_Backups::get_updraft_data() - * @uses \MainWP\Child\MainWP_Child_Updraft_Plus_Backups::next_scheduled_backups() - * @uses \MainWP\Child\MainWP_Child_Updraft_Plus_Backups::force_scheduled_resumption() - * @uses \MainWP\Child\MainWP_Child_Updraft_Plus_Backups::fetch_updraft_log() - * @uses \MainWP\Child\MainWP_Child_Updraft_Plus_Backups::activejobs_delete() - * @uses \MainWP\Child\MainWP_Child_Updraft_Plus_Backups::historystatus() - * @uses \MainWP\Child\MainWP_Child_Updraft_Plus_Backups::deleteset() - * @uses \MainWP\Child\MainWP_Child_Updraft_Plus_Backups::updraft_download_backup() - * @uses \MainWP\Child\MainWP_Child_Updraft_Plus_Backups::restore_alldownloaded() - * @uses \MainWP\Child\MainWP_Child_Updraft_Plus_Backups::extradb_testconnection() - * @uses \MainWP\Child\MainWP_Child_Updraft_Plus_Backups::delete_old_dirs_go() - * @uses \MainWP\Child\MainWP_Child_Updraft_Plus_Backups::do_vault_connect() - * @uses \MainWP\Child\MainWP_Child_Updraft_Plus_Backups::vault_disconnect() - * @uses \MainWP\Child\MainWP_Helper::write() - * @uses UpdraftPlus() - * @uses MainWP_Exception() - */ - public function action() { - $information = array(); - if ( ! $this->is_plugin_installed ) { - $information['error'] = 'NO_UPDRAFTPLUS'; - MainWP_Helper::write( $information ); - } + if (isset($last_backup['backup_time'])) { + $backup_time = $last_backup['backup_time']; + if ($last_backup['success']) { + MainWP_Utility::update_lasttime_backup('updraftplus', $backup_time); + } + } + return $last_backup; + } + + /** + * Sync other data from $data[] and merge with $information[] + * + * @param array $information Returned response array for MainWP BackWPup Extension actions. + * @param array $data Other data to sync to $information array. + * + * @return array $information Returned information array with both sets of data. + * + * @uses MainWP_Child_Updraft_Plus_Backups::is_plugin_installed() + * @uses MainWP_Child_Updraft_Plus_Backups::is_plugin_installed() + * @uses MainWP_Exception() + */ + public function sync_others_data($information, $data = array()) + { + try { + if (isset($data['syncUpdraftData'])) { + $info = $data['syncUpdraftData']; + if ($this->is_plugin_installed) { + $with_hist = true; + if (version_compare($info, '1.7', '>=')) { + $with_hist = false; + } + $information['syncUpdraftData'] = $this->get_sync_data($with_hist); + } + } + if (isset($data['sync_Updraftvault_quota_text']) && $data['sync_Updraftvault_quota_text'] && $this->is_plugin_installed) { + $information['sync_Updraftvault_quota_text'] = $this->connected_html(); + } + } catch (MainWP_Exception $e) { + // ok. + } - $this->required_files(); + return $information; + } + + /** + * MainWP UpdraftPlus Extension actions. + * + * @uses \MainWP\Child\MainWP_Child_Updraft_Plus_Backups::is_plugin_installed() + * @uses \MainWP\Child\MainWP_Child_Updraft_Plus_Backups::required_files() + * @uses \MainWP\Child\MainWP_Child_Updraft_Plus_Backups::set_showhide() + * @uses \MainWP\Child\MainWP_Child_Updraft_Plus_Backups::save_settings() + * @uses \MainWP\Child\MainWP_Child_Updraft_Plus_Backups::addons_connect() + * @uses \MainWP\Child\MainWP_Child_Updraft_Plus_Backups::backup_now() + * @uses \MainWP\Child\MainWP_Child_Updraft_Plus_Backups::activejobs_list() + * @uses \MainWP\Child\MainWP_Child_Updraft_Plus_Backups::diskspaceused() + * @uses \MainWP\Child\MainWP_Child_Updraft_Plus_Backups::activejobs_list() + * @uses \MainWP\Child\MainWP_Child_Updraft_Plus_Backups::last_backup_html() + * @uses \MainWP\Child\MainWP_Child_Updraft_Plus_Backups::get_updraft_data() + * @uses \MainWP\Child\MainWP_Child_Updraft_Plus_Backups::next_scheduled_backups() + * @uses \MainWP\Child\MainWP_Child_Updraft_Plus_Backups::force_scheduled_resumption() + * @uses \MainWP\Child\MainWP_Child_Updraft_Plus_Backups::fetch_updraft_log() + * @uses \MainWP\Child\MainWP_Child_Updraft_Plus_Backups::activejobs_delete() + * @uses \MainWP\Child\MainWP_Child_Updraft_Plus_Backups::historystatus() + * @uses \MainWP\Child\MainWP_Child_Updraft_Plus_Backups::deleteset() + * @uses \MainWP\Child\MainWP_Child_Updraft_Plus_Backups::updraft_download_backup() + * @uses \MainWP\Child\MainWP_Child_Updraft_Plus_Backups::restore_alldownloaded() + * @uses \MainWP\Child\MainWP_Child_Updraft_Plus_Backups::extradb_testconnection() + * @uses \MainWP\Child\MainWP_Child_Updraft_Plus_Backups::delete_old_dirs_go() + * @uses \MainWP\Child\MainWP_Child_Updraft_Plus_Backups::do_vault_connect() + * @uses \MainWP\Child\MainWP_Child_Updraft_Plus_Backups::vault_disconnect() + * @uses \MainWP\Child\MainWP_Helper::write() + * @uses UpdraftPlus() + * @uses MainWP_Exception() + */ + public function action() { + $information = array(); + if (! $this->is_plugin_installed) { + $information['error'] = 'NO_UPDRAFTPLUS'; + MainWP_Helper::write($information); + } - /** @global object $updraftplus UpdraftPlus class instance. */ - global $updraftplus; + $this->required_files(); - if ( empty( $updraftplus ) && class_exists( '\UpdraftPlus' ) ) { - $updraftplus = new \UpdraftPlus(); - } - if ( empty( $updraftplus ) ) { - $information['error'] = 'Error empty updraftplus'; - MainWP_Helper::write( $information ); - } + /** @global object $updraftplus UpdraftPlus class instance. */ + global $updraftplus; - $mwp_action = MainWP_System::instance()->validate_params( 'mwp_action' ); - if ( ! empty( $mwp_action ) ) { - try { - switch ( $mwp_action ) { - case 'set_showhide': - $information = $this->set_showhide(); - break; - case 'save_settings': - $information = $this->save_settings(); - break; - case 'addons_connect': - $information = $this->addons_connect(); - break; - case 'backup_now': - $this->backup_now(); - break; - case 'activejobs_list': - $information = $this->activejobs_list(); - break; - case 'diskspaceused': - $information = $this->diskspaceused(); - break; - case 'last_backup_html': - $information = $this->last_backup_html(); - break; - case 'reload_data': - $information = $this->get_updraft_data(); - break; - case 'next_scheduled_backups': - $information = $this->next_scheduled_backups(); - break; - case 'forcescheduledresumption': - $information = $this->force_scheduled_resumption(); - break; - case 'fetch_updraft_log': - $information = $this->fetch_updraft_log(); - break; - case 'activejobs_delete': - $information = $this->activejobs_delete(); - break; - case 'historystatus': - $information = $this->historystatus(); - break; - case 'deleteset': - $information = $this->deleteset(); - break; - case 'updraft_download_backup': - $information = $this->updraft_download_backup(); - break; - case 'restore_alldownloaded': - $information = $this->restore_alldownloaded(); - break; - case 'extradbtestconnection': - $information = $this->extradb_testconnection(); - break; - case 'delete_old_dirs': - $information = $this->delete_old_dirs_go(); - break; - case 'vault_connect': - $information = $this->do_vault_connect(); - break; - case 'vault_disconnect': - $this->vault_disconnect(); - break; - default: - break; - } - } catch ( MainWP_Exception $e ) { - $information = array( 'error' => $e->getMessage() ); - } - } - MainWP_Helper::write( $information ); + if (empty($updraftplus) && class_exists('\UpdraftPlus')) { + $updraftplus = new \UpdraftPlus(); } - - /** - * Set show or hide UpdraftPlus Plugin from Admin & plugins list. - * - * @return array $information Return results. - * - * @uses \MainWP\Child\MainWP_Helper::update_option() - */ - public function set_showhide() { - $hide = MainWP_System::instance()->validate_params( 'showhide' ); - MainWP_Helper::update_option( 'mainwp_updraftplus_hide_plugin', $hide ); - $information['result'] = 'SUCCESS'; - - return $information; + if (empty($updraftplus)) { + $information['error'] = 'Error empty updraftplus'; + MainWP_Helper::write($information); } - /** - * Get settings keys. - * - * @return array Array of settings keys. - */ - private function get_settings_keys() { - return array( - 'updraft_autobackup_default', - 'updraftplus_dismissedautobackup', - 'updraftplus_dismissedexpiry', - 'updraft_interval', - 'updraft_interval_increments', - 'updraft_interval_database', - 'updraft_retain', - 'updraft_retain_db', - 'updraft_encryptionphrase', - 'updraft_dir', - 'updraft_email', - 'updraft_delete_local', - 'updraft_include_plugins', - 'updraft_include_themes', - 'updraft_include_uploads', - 'updraft_include_others', - 'updraft_include_wpcore', - 'updraft_include_wpcore_exclude', - 'updraft_include_more', - 'updraft_include_blogs', - 'updraft_include_mu-plugins', - 'updraft_include_others_exclude', - 'updraft_include_uploads_exclude', - 'updraft_starttime_files', - 'updraft_starttime_db', - 'updraft_startday_db', - 'updraft_startday_files', - 'updraft_googledrive', - 'updraft_s3', - 'updraft_s3generic', - 'updraft_dreamhost', - 'updraft_disable_ping', - 'updraft_openstack', - 'updraft_bitcasa', - 'updraft_ssl_useservercerts', - 'updraft_ssl_disableverify', - 'updraft_report_warningsonly', - 'updraft_report_wholebackup', - 'updraft_report_dbbackup', - 'updraft_auto_updates', - 'updraft_log_syslog', - 'updraft_extradatabases', - 'updraft_split_every', - 'updraft_ssl_nossl', - 'updraft_backupdb_nonwp', - 'updraft_extradbs', - 'updraft_include_more_path', - 'updraft_dropbox', - 'updraft_ftp', - 'updraft_copycom', - 'updraft_sftp_settings', - 'updraft_webdav_settings', - 'updraft_dreamobjects', - 'updraft_googlecloud', - 'updraft_retain_extrarules', - 'updraft_backblaze', - ); + $mwp_action = MainWP_System::instance()->validate_params('mwp_action'); + if (! empty($mwp_action)) { + try { + switch ($mwp_action) { + case 'set_showhide': + $information = $this->set_showhide(); + break; + case 'save_settings': + $information = $this->save_settings(); + break; + case 'addons_connect': + $information = $this->addons_connect(); + break; + case 'backup_now': + $this->backup_now(); + break; + case 'activejobs_list': + $information = $this->activejobs_list(); + break; + case 'diskspaceused': + $information = $this->diskspaceused(); + break; + case 'last_backup_html': + $information = $this->last_backup_html(); + break; + case 'reload_data': + $information = $this->get_updraft_data(); + break; + case 'next_scheduled_backups': + $information = $this->next_scheduled_backups(); + break; + case 'forcescheduledresumption': + $information = $this->force_scheduled_resumption(); + break; + case 'fetch_updraft_log': + $information = $this->fetch_updraft_log(); + break; + case 'activejobs_delete': + $information = $this->activejobs_delete(); + break; + case 'historystatus': + $information = $this->historystatus(); + break; + case 'deleteset': + $information = $this->deleteset(); + break; + case 'updraft_download_backup': + $information = $this->updraft_download_backup(); + break; + case 'restore_alldownloaded': + $information = $this->restore_alldownloaded(); + break; + case 'extradbtestconnection': + $information = $this->extradb_testconnection(); + break; + case 'delete_old_dirs': + $information = $this->delete_old_dirs_go(); + break; + case 'vault_connect': + $information = $this->do_vault_connect(); + break; + case 'vault_disconnect': + $this->vault_disconnect(); + break; + default: + break; + } + } catch (MainWP_Exception $e) { + $information = array('error' => $e->getMessage()); + } } - - /** - * Connect to UpdraftPlus Vault. - * - * @return array $response Return response. - * @throws MainWP_Exception Error message. - * - * @uses UpdraftPlus_Options::get_updraft_option() - * @uses MainWP_Child_Updraft_Plus_Backups::connected_html() - */ - private function do_vault_connect() { - $vault_settings = \UpdraftPlus_Options::get_updraft_option( 'updraft_updraftvault' ); - if ( is_array( $vault_settings ) && ! empty( $vault_settings['token'] ) && ! empty( $vault_settings['email'] ) ) { - return array( - 'connected' => true, - 'html' => $this->connected_html(), - ); - } - - $connect = $this->vault_connect( $_REQUEST['email'], $_REQUEST['passwd'] ); - if ( true === $connect ) { - $response = array( - 'connected' => true, - 'html' => $this->connected_html(), - ); - } else { - $response = array( - 'e' => esc_html__( 'An unknown error occurred when trying to connect to UpdraftPlus.Com', 'updraftplus' ), - ); - if ( is_wp_error( $connect ) ) { - $response['e'] = $connect->get_error_message(); - $response['code'] = $connect->get_error_code(); - $response['data'] = serialize( $connect->get_error_data() ); //phpcs:ignore -- NOSONAR - complex. - } - } - return $response; + MainWP_Helper::write($information); + } + + /** + * Set show or hide UpdraftPlus Plugin from Admin & plugins list. + * + * @return array $information Return results. + * + * @uses \MainWP\Child\MainWP_Helper::update_option() + */ + public function set_showhide() + { + $hide = MainWP_System::instance()->validate_params('showhide'); + MainWP_Helper::update_option('mainwp_updraftplus_hide_plugin', $hide); + $information['result'] = 'SUCCESS'; + + return $information; + } + + /** + * Get settings keys. + * + * @return array Array of settings keys. + */ + private function get_settings_keys() + { + return array( + 'updraft_autobackup_default', + 'updraftplus_dismissedautobackup', + 'updraftplus_dismissedexpiry', + 'updraft_interval', + 'updraft_interval_increments', + 'updraft_interval_database', + 'updraft_retain', + 'updraft_retain_db', + 'updraft_encryptionphrase', + 'updraft_dir', + 'updraft_email', + 'updraft_delete_local', + 'updraft_include_plugins', + 'updraft_include_themes', + 'updraft_include_uploads', + 'updraft_include_others', + 'updraft_include_wpcore', + 'updraft_include_wpcore_exclude', + 'updraft_include_more', + 'updraft_include_blogs', + 'updraft_include_mu-plugins', + 'updraft_include_others_exclude', + 'updraft_include_uploads_exclude', + 'updraft_starttime_files', + 'updraft_starttime_db', + 'updraft_startday_db', + 'updraft_startday_files', + 'updraft_googledrive', + 'updraft_s3', + 'updraft_s3generic', + 'updraft_dreamhost', + 'updraft_disable_ping', + 'updraft_openstack', + 'updraft_bitcasa', + 'updraft_ssl_useservercerts', + 'updraft_ssl_disableverify', + 'updraft_report_warningsonly', + 'updraft_report_wholebackup', + 'updraft_report_dbbackup', + 'updraft_auto_updates', + 'updraft_log_syslog', + 'updraft_extradatabases', + 'updraft_split_every', + 'updraft_ssl_nossl', + 'updraft_backupdb_nonwp', + 'updraft_extradbs', + 'updraft_include_more_path', + 'updraft_dropbox', + 'updraft_ftp', + 'updraft_copycom', + 'updraft_sftp_settings', + 'updraft_webdav_settings', + 'updraft_dreamobjects', + 'updraft_googlecloud', + 'updraft_retain_extrarules', + 'updraft_backblaze', + 'updraft_pcloud', + 'updraft_debug_mode', + 'updraft_updraftvault' + ); + } + + /** + * Connect to UpdraftPlus Vault. + * + * @return array $response Return response. + * @throws MainWP_Exception Error message. + * + * @uses UpdraftPlus_Options::get_updraft_option() + * @uses MainWP_Child_Updraft_Plus_Backups::connected_html() + */ + private function do_vault_connect() + { + $vault_settings = \UpdraftPlus_Options::get_updraft_option('updraft_updraftvault'); + if (is_array($vault_settings) && ! empty($vault_settings['token']) && ! empty($vault_settings['email'])) { + return array( + 'connected' => true, + 'html' => $this->connected_html(), + ); } + $connect = $this->vault_connect($_REQUEST['email'], $_REQUEST['passwd']); + if (true === $connect) { + $response = array( + 'connected' => true, + 'html' => $this->connected_html(), + ); + } else { + $response = array( + 'e' => esc_html__('An unknown error occurred when trying to connect to UpdraftPlus.Com', 'updraftplus'), + ); + if (is_wp_error($connect)) { + $response['e'] = $connect->get_error_message(); + $response['code'] = $connect->get_error_code(); + $response['data'] = serialize($connect->get_error_data()); //phpcs:ignore -- NOSONAR - complex. + } + } + return $response; + } + + + /** + * UpdraftPlus Vault connection html. + * + * @return string $ret Returns connected to UpdraftPlus Vault message html. + * @throws MainWP_Exception|MainWP_Exception Error message. + * + * @uses \MainWP\Child\MainWP_Helper::instance()->check_classes_exists() + * @uses \MainWP\Child\MainWP_Helper::MainWP_Helper::instance()->check_methods() + * @uses UpdraftPlus_Options::get_updraft_option() + */ + private function connected_html() + { + MainWP_Helper::instance()->check_classes_exists('\UpdraftPlus_Options'); + MainWP_Helper::instance()->check_methods('\UpdraftPlus_Options', 'get_updraft_option'); + + $vault_settings = \UpdraftPlus_Options::get_updraft_option('updraft_updraftvault'); + if (! is_array($vault_settings) || empty($vault_settings['token']) || empty($vault_settings['email'])) { + return ''; + } - /** - * UpdraftPlus Vault connection html. - * - * @return string $ret Returns connected to UpdraftPlus Vault message html. - * @throws MainWP_Exception|MainWP_Exception Error message. - * - * @uses \MainWP\Child\MainWP_Helper::instance()->check_classes_exists() - * @uses \MainWP\Child\MainWP_Helper::MainWP_Helper::instance()->check_methods() - * @uses UpdraftPlus_Options::get_updraft_option() - */ - private function connected_html() { - MainWP_Helper::instance()->check_classes_exists( '\UpdraftPlus_Options' ); - MainWP_Helper::instance()->check_methods( '\UpdraftPlus_Options', 'get_updraft_option' ); + $ret = ''; + $ret .= '

'; + $ret .= esc_html__('This site is connected to UpdraftPlus Vault.', 'updraftplus') . ' ' . esc_html__("Well done - there's nothing more needed to set up.", 'updraftplus') . '

' . esc_html__('Vault owner', 'updraftplus') . ': ' . htmlspecialchars($vault_settings['email']); + + $ret .= '
' . esc_html__('Quota:', 'updraftplus') . ' '; + if (! isset($vault_settings['quota']) || ! is_numeric($vault_settings['quota']) || ($vault_settings['quota'] < 0)) { + $ret .= esc_html__('Unknown', 'updraftplus'); + } else { + $quota_via_transient = get_transient('updraftvault_quota_text'); + if (is_string($quota_via_transient) && $quota_via_transient) { + $ret .= $quota_via_transient; + } + } + $ret .= '

'; + $ret .= '

'; + + return $ret; + } + + /** + * Connect to UpdraftPlus Vault. + * + * @param string $email Vault account user name. + * @param string $password Vault account password. + * + * @return bool|WP_Error $return Returns either true (in which case the Vault token will be stored), or false|\WP_Error. + * + * @uses UpdraftPlus_Options::get_updraft_option() + * @uses UpdraftPlus_Options::update_updraft_option() + * @uses \WP_Error() + */ + private function vault_connect($email, $password) { //phpcs:ignore -- NOSONAR - complex. + + /** @global object $updraftplus UpdraftPlus instance. */ + global $updraftplus; + + $vault_mothership = 'https://vault.updraftplus.com/plugin-info/'; + + // Use SSL to prevent snooping. + $result = wp_remote_post( + $vault_mothership . '/?udm_action=vault_connect', + array( + 'timeout' => 20, + 'body' => array( + 'e' => $email, + 'p' => base64_encode($password), // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions -- base64_encode function is used for http encode compatible.. + 'sid' => $updraftplus->siteid(), + 'su' => base64_encode(home_url()), // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions -- base64_encode function is used for http encode compatible.. + ), + ) + ); + + if (is_wp_error($result) || (false === $result)) { + return $result; + } - $vault_settings = \UpdraftPlus_Options::get_updraft_option( 'updraft_updraftvault' ); - if ( ! is_array( $vault_settings ) || empty( $vault_settings['token'] ) || empty( $vault_settings['email'] ) ) { - return ''; - } + $response = json_decode($result['body'], true); - $ret = ''; - $ret .= '

'; - $ret .= esc_html__( 'This site is connected to UpdraftPlus Vault.', 'updraftplus' ) . ' ' . esc_html__( "Well done - there's nothing more needed to set up.", 'updraftplus' ) . '

' . esc_html__( 'Vault owner', 'updraftplus' ) . ': ' . htmlspecialchars( $vault_settings['email'] ); + if (! is_array($response) || ! isset($response['mothership']) || ! isset($response['loggedin'])) { + if (preg_match('/has banned your IP address \(([\.:0-9a-f]+)\)/', $result['body'], $matches)) { + return new \WP_Error('banned_ip', sprintf(esc_html__("UpdraftPlus.com has responded with 'Access Denied'.", 'updraftplus') . '
' . esc_html__("It appears that your web server's IP Address (%s) is blocked.", 'updraftplus') . ' ' . esc_html__('This most likely means that you share a webserver with a hacked website that has been used in previous attacks.', 'updraftplus') . '
' . esc_html__('To remove the block, please go here.', 'updraftplus') . ' ', $matches[1])); + } else { + return new \WP_Error('unknown_response', sprintf(esc_html__('UpdraftPlus.Com returned a response which we could not understand (data: %s)', 'updraftplus'), $result['body'])); + } + } - $ret .= '
' . esc_html__( 'Quota:', 'updraftplus' ) . ' '; - if ( ! isset( $vault_settings['quota'] ) || ! is_numeric( $vault_settings['quota'] ) || ( $vault_settings['quota'] < 0 ) ) { - $ret .= esc_html__( 'Unknown', 'updraftplus' ); + $return = false; + switch ($response['loggedin']) { + case 'connected': + if (! empty($response['token'])) { + // Store it. + $vault_settings = \UpdraftPlus_Options::get_updraft_option('updraft_updraftvault'); + if (! is_array($vault_settings)) { + $vault_settings = array(); + } + $vault_settings['email'] = $email; + $vault_settings['token'] = (string) $response['token']; + $vault_settings['quota'] = -1; + unset($vault_settings['last_config']); + if (isset($response['quota'])) { + $vault_settings['quota'] = $response['quota']; + } + \UpdraftPlus_Options::update_updraft_option('updraft_updraftvault', $vault_settings); + $return = true; + } elseif (isset($response['quota']) && ! $response['quota']) { + return new \WP_Error('no_quota', esc_html__('You do not currently have any UpdraftPlus Vault quota', 'updraftplus')); } else { - $quota_via_transient = get_transient( 'updraftvault_quota_text' ); - if ( is_string( $quota_via_transient ) && $quota_via_transient ) { - $ret .= $quota_via_transient; - } + return new \WP_Error('unknown_response', esc_html__('UpdraftPlus.Com returned a response, but we could not understand it', 'updraftplus')); + } + break; + case 'authfailed': + if (! empty($response['authproblem'])) { + if ('invalidpassword' === $response['authproblem']) { + return new \WP_Error('authfailed', esc_html__('Your email address was valid, but your password was not recognised by UpdraftPlus.Com.', 'updraftplus') . ' ' . esc_html__('If you have forgotten your password, then go here to change your password on updraftplus.com.', 'updraftplus') . ''); + } elseif ('invaliduser' === $response['authproblem']) { + return new \WP_Error('authfailed', esc_html__('You entered an email address that was not recognised by UpdraftPlus.Com', 'updraftplus')); + } } - $ret .= '

'; - $ret .= '

'; - return $ret; + $return = new \WP_Error('authfailed', esc_html__('Your email address and password were not recognised by UpdraftPlus.Com', 'updraftplus')); + break; + + default: + $return = new \WP_Error('unknown_response', esc_html__('UpdraftPlus.Com returned a response, but we could not understand it', 'updraftplus')); + break; } - /** - * Connect to UpdraftPlus Vault. - * - * @param string $email Vault account user name. - * @param strign $password Vault account password. - * - * @return bool|WP_Error $return Returns either true (in which case the Vault token will be stored), or false|\WP_Error. - * - * @uses UpdraftPlus_Options::get_updraft_option() - * @uses UpdraftPlus_Options::update_updraft_option() - * @uses \WP_Error() - */ - private function vault_connect($email, $password ) { //phpcs:ignore -- NOSONAR - complex. + return $return; + } + + /** + * Disconnect from UpdraftPlus Vault. + * This method also gets called directly, so don't add code that assumes that it's definitely an AJAX situation. + * + * @throws MainWP_Exception Error message. + * + * @uses \UpdraftPlus_Options::get_updraft_option() + * @uses \UpdraftPlus_Options::update_updraft_option() + * @uses \MainWP\Child\MainWP_Utility::close_connection() + */ + public function vault_disconnect() + { + $vault_settings = \UpdraftPlus_Options::get_updraft_option('updraft_updraftvault'); + \UpdraftPlus_Options::update_updraft_option('updraft_updraftvault', array()); + + /** @global object $updraftplus UpdraftPlus instance. */ + global $updraftplus; + + $vault_mothership = 'https://vault.updraftplus.com/plugin-info/'; + + delete_transient('udvault_last_config'); + delete_transient('updraftvault_quota_text'); + + MainWP_Utility::close_connection( + array( + 'disconnected' => 1, + 'html' => $this->connected_html(), + ) + ); + + // If $_POST['reset_hash'] is set, then we were alerted by updraftplus.com - no need to notify back. + if (is_array($vault_settings) && isset($vault_settings['email']) && empty($_POST['reset_hash'])) { + $post_body = array( + 'e' => (string) $vault_settings['email'], + 'sid' => $updraftplus->siteid(), + 'su' => base64_encode(home_url()), // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions -- base64_encode function is used for http encode compatible.. + ); + + if (! empty($vault_settings['token'])) { + $post_body['token'] = (string) $vault_settings['token']; + } + + // Use SSL to prevent snooping. + wp_remote_post( + $vault_mothership . '/?udm_action=vault_disconnect', + array( + 'timeout' => 20, + 'body' => $post_body, + ) + ); + } + } + + /** + * Require needed UpdraftPlus Files. + */ + public function required_files() + { + if (defined('UPDRAFTPLUS_DIR')) { + if (! class_exists('\UpdraftPlus') && file_exists(UPDRAFTPLUS_DIR . '/class-updraftplus.php')) { + require_once UPDRAFTPLUS_DIR . '/class-updraftplus.php'; // NOSONAR - WP compatible. + } + + if (! class_exists('\UpdraftPlus_Options') && file_exists(UPDRAFTPLUS_DIR . '/options.php')) { + require_once UPDRAFTPLUS_DIR . '/options.php'; // NOSONAR - WP compatible. + } + } + } + + /** + * Save UpdraftPlus settings. + * + * @return array $out Return response array. + * + * @uses MainWP_Child_Updraft_Plus_Backups::get_settings_keys() + * @uses MainWP_Child_Updraft_Plus_Backups::replace_tokens() + * @uses UpdraftPlus_Options::get_updraft_option() + * @uses UpdraftPlus_Options::update_updraft_option() + * @uses $updraftplus::schedule_backup() + * @uses $updraftplus::schedule_backup_database() + */ + public function save_settings() { //phpcs:ignore -- NOSONAR - complex. + $settings = isset($_POST['settings']) ? json_decode(base64_decode(wp_unslash($_POST['settings'])), true) : ''; // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions -- base64_encode function is used for http encode compatible.. + + $keys_filter = $this->get_settings_keys(); + + /** @global object $updraftplus UpdraftPlus instance. */ + global $updraftplus; + + $updated = false; + if (is_array($settings) && class_exists('\UpdraftPlus_Options')) { + foreach ($keys_filter as $key) { + if ('updraft_googledrive' === $key || 'updraft_googlecloud' === $key || 'updraft_onedrive' === $key) { + continue; + } + if (isset($settings[$key])) { + $settings_key = null; + if ('updraft_dropbox' === $key && is_array($settings[$key])) { + $opts = \UpdraftPlus_Options::get_updraft_option('updraft_dropbox'); + if (! is_array($opts)) { + $opts = array(); + } + if (is_array($opts) && isset($opts['settings'])) { + $settings_key = key($opts['settings']); + $opts['settings'][$settings_key]['folder'] = $this->replace_tokens($settings[$key]['folder']); + } else { + $opts['folder'] = $this->replace_tokens($settings[$key]['folder']); + } + \UpdraftPlus_Options::update_updraft_option($key, $opts); + } elseif ('updraft_email' === $key) { + $value = $settings[$key]; + if (! is_array($value) && ! empty($value)) { + $value = htmlspecialchars(get_bloginfo('admin_email')); + } + \UpdraftPlus_Options::update_updraft_option($key, $value); + } elseif ('updraft_s3' === $key) { + $opts = \UpdraftPlus_Options::get_updraft_option('updraft_s3'); + $opts = is_array($opts) ? $opts : array(); + + if (is_array($opts) && isset($opts['settings'])) { + $settings_key = key($opts['settings']); + $opts['settings'][$settings_key]['accesskey'] = $settings[$key]['accesskey']; + $opts['settings'][$settings_key]['secretkey'] = $settings[$key]['secretkey']; + $opts['settings'][$settings_key]['secretkey'] = $settings[$key]['secretkey']; + if (! empty($opts['settings'][$settings_key]['path']) && '/' === substr($opts['settings'][$settings_key]['path'], 0, 1)) { + $opts['settings'][$settings_key]['path'] = substr($opts['settings'][$settings_key]['path'], 1); + } + if (isset($settings[$key]['rrs'])) { // premium settings. + $opts['settings'][$settings_key]['rrs'] = $settings[$key]['rrs']; + $opts['settings'][$settings_key]['server_side_encryption'] = $settings[$key]['server_side_encryption']; + } + } else { + $opts['accesskey'] = $settings[$key]['accesskey']; + $opts['secretkey'] = $settings[$key]['secretkey']; + $opts['path'] = $this->replace_tokens($settings[$key]['path']); + if (! empty($opts['path']) && '/' === substr($opts['path'], 0, 1)) { + $opts['path'] = substr($opts['path'], 1); + } + if (isset($settings[$key]['rrs'])) { // premium settings. + $opts['rrs'] = $settings[$key]['rrs']; + $opts['server_side_encryption'] = $settings[$key]['server_side_encryption']; + } + } - /** @global object $updraftplus UpdraftPlus instance. */ - global $updraftplus; + \UpdraftPlus_Options::update_updraft_option($key, $opts); + } elseif ('updraft_s3generic' === $key) { + $opts = \UpdraftPlus_Options::get_updraft_option('updraft_s3generic'); + $opts = is_array($opts) ? $opts : array(); + + if (is_array($opts) && isset($opts['settings'])) { + $settings_key = key($opts['settings']); + $opts['settings'][$settings_key]['endpoint'] = $settings[$key]['endpoint']; + $opts['settings'][$settings_key]['accesskey'] = $settings[$key]['accesskey']; + $opts['settings'][$settings_key]['secretkey'] = $settings[$key]['secretkey']; + $opts['settings'][$settings_key]['path'] = $this->replace_tokens($settings[$key]['path']); + $opts['settings'][$settings_key]['bucket_access_style'] = $settings[$key]['bucket_access_style']; + $opts['settings'][$settings_key]['signature_version'] = $settings[$key]['signature_version']; + } else { + $opts['endpoint'] = $settings[$key]['endpoint']; + $opts['accesskey'] = $settings[$key]['accesskey']; + $opts['secretkey'] = $settings[$key]['secretkey']; + $opts['bucket_access_style'] = $settings[$key]['bucket_access_style']; + $opts['signature_version'] = $settings[$key]['signature_version']; + $opts['path'] = $this->replace_tokens($settings[$key]['path']); + } + \UpdraftPlus_Options::update_updraft_option($key, $opts); + } elseif ('updraft_dreamobjects' === $key) { + $opts = \UpdraftPlus_Options::get_updraft_option('updraft_dreamobjects'); + $opts = is_array($opts) ? $opts : array(); + + if (is_array($opts) && isset($opts['settings'])) { + $settings_key = key($opts['settings']); + $opts['settings'][$settings_key]['path'] = $this->replace_tokens($settings[$key]['path']); + $opts['settings'][$settings_key]['endpoint'] = $settings[$key]['endpoint']; + $opts['settings'][$settings_key]['accesskey'] = $settings[$key]['accesskey']; + $opts['settings'][$settings_key]['secretkey'] = $settings[$key]['secretkey']; + } else { + $opts['path'] = $this->replace_tokens($settings[$key]['path']); + $opts['endpoint'] = $settings[$key]['endpoint']; + $opts['accesskey'] = $settings[$key]['accesskey']; + $opts['secretkey'] = $settings[$key]['secretkey']; + } + \UpdraftPlus_Options::update_updraft_option($key, $opts); + } elseif ('updraft_ftp' === $key) { + $opts = \UpdraftPlus_Options::get_updraft_option('updraft_ftp'); + if (! is_array($opts)) { + $opts = array(); + } + if (is_array($opts) && isset($opts['settings'])) { + $settings_key = key($opts['settings']); + if (isset($settings[$key]['path'])) { + $opts['settings'][$settings_key]['host'] = $settings[$key]['host']; + $opts['settings'][$settings_key]['user'] = $settings[$key]['user']; + $opts['settings'][$settings_key]['pass'] = $settings[$key]['pass']; + $opts['settings'][$settings_key]['path'] = $this->replace_tokens($settings[$key]['path']); + $opts['settings'][$settings_key]['passive'] = isset($settings[$key]['passive']) ? $settings[$key]['passive'] : 0; + } + } else { + if (isset($settings[$key]['path'])) { + $opts['host'] = $settings[$key]['host']; + $opts['user'] = $settings[$key]['user']; + $opts['pass'] = $settings[$key]['pass']; + $opts['path'] = $this->replace_tokens($settings[$key]['path']); + $opts['passive'] = isset($settings[$key]['passive']) ? $settings[$key]['passive'] : 0; + } + } - $vault_mothership = 'https://vault.updraftplus.com/plugin-info/'; + \UpdraftPlus_Options::update_updraft_option($key, $opts); + } elseif ('updraft_sftp_settings' === $key) { + $opts = \UpdraftPlus_Options::get_updraft_option('updraft_sftp'); + if (! is_array($opts)) { + $opts = array(); + } + if (is_array($opts) && isset($opts['settings'])) { + $settings_key = key($opts['settings']); + if (isset($settings[$key]['path'])) { + $opts['settings'][$settings_key]['host'] = $settings[$key]['host']; + $opts['settings'][$settings_key]['port'] = $settings[$key]['port']; + $opts['settings'][$settings_key]['user'] = $settings[$key]['user']; + $opts['settings'][$settings_key]['pass'] = $settings[$key]['pass']; + $opts['settings'][$settings_key]['key'] = $settings[$key]['key']; + $opts['settings'][$settings_key]['fingerprint'] = $settings[$key]['fingerprint']; + $opts['settings'][$settings_key]['path'] = $this->replace_tokens($settings[$key]['path']); + $opts['settings'][$settings_key]['scp'] = isset($settings[$key]['scp']) ? $settings[$key]['scp'] : 0; + } + } else { + if (isset($settings[$key]['path'])) { + $opts['host'] = $settings[$key]['host']; + $opts['port'] = $settings[$key]['port']; + $opts['user'] = $settings[$key]['user']; + $opts['pass'] = $settings[$key]['pass']; + $opts['key'] = $settings[$key]['key']; + $opts['fingerprint'] = $settings[$key]['fingerprint']; + $opts['path'] = $this->replace_tokens($settings[$key]['path']); + $opts['scp'] = isset($settings[$key]['scp']) ? $settings[$key]['scp'] : 0; + } + } + \UpdraftPlus_Options::update_updraft_option('updraft_sftp', $opts); + } elseif ('updraft_webdav_settings' === $key && is_array($settings[$key])) { + $opts = \UpdraftPlus_Options::get_updraft_option('updraft_webdav'); + $opts = is_array($opts) ? $opts : array(); + + if (is_array($opts) && isset($opts['settings'])) { + $settings_key = key($opts['settings']); + $path = str_replace("/", "", $this->replace_tokens($settings[$key]['path'])); + $placeholders = ['%sitename%', '%siteurl%', '%random%']; // phpcs:ignore - NOSONAR + $updatedString = str_replace($placeholders, $path, $settings[$key]['url']); + $opts['settings'][$settings_key]['path'] = $path; + $opts['settings'][$settings_key]['enable_chunk'] = $settings[$key]['enable_chunk']; + $opts['settings'][$settings_key]['url'] = $updatedString; + } - // Use SSL to prevent snooping. - $result = wp_remote_post( - $vault_mothership . '/?udm_action=vault_connect', - array( - 'timeout' => 20, - 'body' => array( - 'e' => $email, - 'p' => base64_encode( $password ), // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions -- base64_encode function is used for http encode compatible.. - 'sid' => $updraftplus->siteid(), - 'su' => base64_encode( home_url() ), // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions -- base64_encode function is used for http encode compatible.. - ), - ) - ); + \UpdraftPlus_Options::update_updraft_option('updraft_webdav', $opts); + } elseif ('updraft_backblaze' === $key) { + $opts = \UpdraftPlus_Options::get_updraft_option('updraft_backblaze'); + $opts = is_array($opts) ? $opts : array(); + + if (is_array($opts) && isset($opts['settings']) && is_array($settings[$key]) && isset($settings[$key]['account_id'])) { + $settings_key = key($opts['settings']); + $opts['settings'][$settings_key]['account_id'] = $settings[$key]['account_id']; + $opts['settings'][$settings_key]['key'] = $settings[$key]['key']; + + if (isset($settings[$key]['single_bucket_key_id']) && ! empty($settings[$key]['single_bucket_key_id'])) { + $single_bucket_key_id = trim($settings[$key]['single_bucket_key_id']); + if ('[empty]' === $single_bucket_key_id) { + $opts['settings'][$settings_key]['single_bucket_key_id'] = ''; + } elseif (! empty($single_bucket_key_id)) { + $opts['settings'][$settings_key]['single_bucket_key_id'] = $single_bucket_key_id; + } + } - if ( is_wp_error( $result ) || ( false === $result ) ) { - return $result; - } + $bname = $this->replace_tokens($settings[$key]['bucket_name']); + $bpath = $this->replace_tokens($settings[$key]['backup_path']); + $bname = str_replace('.', '-', $bname); + $bpath = str_replace('.', '-', $bpath); + $bname = str_replace('_', '', $bname); // to fix uncommon character issues. + $bpath = str_replace('_', '', $bpath); + $opts['settings'][$settings_key]['bucket_name'] = $bname; + $opts['settings'][$settings_key]['backup_path'] = $bpath; - $response = json_decode( $result['body'], true ); + $opts['settings'][$settings_key]['object_lock_duration'] = $settings[$key]['object_lock_duration']; - if ( ! is_array( $response ) || ! isset( $response['mothership'] ) || ! isset( $response['loggedin'] ) ) { - if ( preg_match( '/has banned your IP address \(([\.:0-9a-f]+)\)/', $result['body'], $matches ) ) { - return new \WP_Error( 'banned_ip', sprintf( esc_html__( "UpdraftPlus.com has responded with 'Access Denied'.", 'updraftplus' ) . '
' . esc_html__( "It appears that your web server's IP Address (%s) is blocked.", 'updraftplus' ) . ' ' . esc_html__( 'This most likely means that you share a webserver with a hacked website that has been used in previous attacks.', 'updraftplus' ) . '
' . esc_html__( 'To remove the block, please go here.', 'updraftplus' ) . ' ', $matches[1] ) ); + \UpdraftPlus_Options::update_updraft_option($key, $opts); + } + } elseif ('updraft_pcloud' === $key) { + $opts = \UpdraftPlus_Options::get_updraft_option('updraft_pcloud'); + $opts = ! is_array($opts) ? $opts : []; + if (is_array($opts) && isset($opts['settings'])) { + $opts['settings'][$settings_key]['folder'] = !empty($settings[$key]['folder']) ? $this->replace_tokens($settings[$key]['folder']) : ''; } else { - return new \WP_Error( 'unknown_response', sprintf( esc_html__( 'UpdraftPlus.Com returned a response which we could not understand (data: %s)', 'updraftplus' ), $result['body'] ) ); + $opts['folder'] = !empty($settings[$key]['folder']) ? $this->replace_tokens($settings[$key]['folder']) : ''; } - } - $return = false; - switch ( $response['loggedin'] ) { - case 'connected': - if ( ! empty( $response['token'] ) ) { - // Store it. - $vault_settings = \UpdraftPlus_Options::get_updraft_option( 'updraft_updraftvault' ); - if ( ! is_array( $vault_settings ) ) { - $vault_settings = array(); - } - $vault_settings['email'] = $email; - $vault_settings['token'] = (string) $response['token']; - $vault_settings['quota'] = -1; - unset( $vault_settings['last_config'] ); - if ( isset( $response['quota'] ) ) { - $vault_settings['quota'] = $response['quota']; - } - \UpdraftPlus_Options::update_updraft_option( 'updraft_updraftvault', $vault_settings ); - } elseif ( isset( $response['quota'] ) && ! $response['quota'] ) { - return new \WP_Error( 'no_quota', esc_html__( 'You do not currently have any UpdraftPlus Vault quota', 'updraftplus' ) ); - } else { - return new \WP_Error( 'unknown_response', esc_html__( 'UpdraftPlus.Com returned a response, but we could not understand it', 'updraftplus' ) ); + \UpdraftPlus_Options::update_updraft_option($key, $opts); + } elseif ('updraft_interval_increments' === $key) { + $value = $updraftplus->schedule_backup_increments($settings[$key]); + \UpdraftPlus_Options::update_updraft_option($key, $value); + } else { + \UpdraftPlus_Options::update_updraft_option($key, $settings[$key]); + } + // premium settings save instance_conditional_logic data. + if (!empty($settings[$key]['instance_conditional_logic'])) { + $setting_conditional_logic = $settings[$key]['instance_conditional_logic']; + + if ($key === 'updraft_sftp_settings') { + $key = 'updraft_sftp'; + } elseif ($key === 'updraft_webdav_settings') { + $key = 'updraft_webdav'; + } + $option_data = \UpdraftPlus_Options::get_updraft_option($key); + $opts = is_array($option_data) ? $option_data : []; + + if (is_array($opts) && isset($opts['settings'])) { + $settings_key = key($opts['settings']); + if ($setting_conditional_logic['type'] != 0) { + $opts['settings'][$settings_key]['instance_conditional_logic']['type'] = $setting_conditional_logic['type']; + if (!empty($setting_conditional_logic['rules'])) { + $opts['settings'][$settings_key]['instance_conditional_logic']['rules'] = $setting_conditional_logic['rules']; } - break; - case 'authfailed': - if ( ! empty( $response['authproblem'] ) ) { - if ( 'invalidpassword' === $response['authproblem'] ) { - return new \WP_Error( 'authfailed', esc_html__( 'Your email address was valid, but your password was not recognised by UpdraftPlus.Com.', 'updraftplus' ) . ' ' . esc_html__( 'If you have forgotten your password, then go here to change your password on updraftplus.com.', 'updraftplus' ) . '' ); - } elseif ( 'invaliduser' === $response['authproblem'] ) { - return new \WP_Error( 'authfailed', esc_html__( 'You entered an email address that was not recognised by UpdraftPlus.Com', 'updraftplus' ) ); - } + } elseif (!empty($opts['settings'][$settings_key]['instance_conditional_logic'])) { + unset($opts['settings'][$settings_key]['instance_conditional_logic']); + } + } else { + if (!empty($setting_conditional_logic['type'])) { + $opts['instance_conditional_logic']['type'] = $setting_conditional_logic['type']; + if (!empty($setting_conditional_logic['rules'])) { + $opts['instance_conditional_logic']['rules'] = $setting_conditional_logic['rules']; } + } elseif ($setting_conditional_logic['type'] == 0) { + unset($opts['instance_conditional_logic']); // remove config if main site empty + } + } + \UpdraftPlus_Options::update_updraft_option($key, $opts); + } - $return = new \WP_Error( 'authfailed', esc_html__( 'Your email address and password were not recognised by UpdraftPlus.Com', 'updraftplus' ) ); - break; - - default: - $return = new \WP_Error( 'unknown_response', esc_html__( 'UpdraftPlus.Com returned a response, but we could not understand it', 'updraftplus' ) ); - break; + $updated = true; } + } + if (! isset($settings['do_not_save_remote_settings']) || empty($settings['do_not_save_remote_settings'])) { + \UpdraftPlus_Options::update_updraft_option('updraft_service', $settings['updraft_service']); + } elseif (! empty( $settings['do_not_save_remote_settings'] ) && 1 === $settings['do_not_save_remote_settings'] ){ + // Save empty updraft service with remote setting do not save equal to 1. + \UpdraftPlus_Options::update_updraft_option('updraft_service', ''); + } - return $return; + if (isset($settings['updraft_interval'])) { + // fix for premium version. + $_POST['updraft_interval'] = $settings['updraft_interval']; + $_POST['updraft_startday_files'] = $settings['updraft_startday_files']; + $_POST['updraft_starttime_files'] = $settings['updraft_starttime_files']; + $updraftplus->schedule_backup($settings['updraft_interval']); + } + + if (isset($settings['updraft_interval_database'])) { + // fix for premium version. + $_POST['updraft_interval_database'] = $settings['updraft_interval_database']; + $_POST['updraft_startday_db'] = $settings['updraft_startday_db']; + $_POST['updraft_starttime_db'] = $settings['updraft_starttime_db']; + $updraftplus->schedule_backup_database($settings['updraft_interval_database']); + } } - /** - * Disconnect from UpdraftPlus Vault. - * This method also gets called directly, so don't add code that assumes that it's definitely an AJAX situation. - * - * @throws MainWP_Exception Error message. - * - * @uses \UpdraftPlus_Options::get_updraft_option() - * @uses \UpdraftPlus_Options::update_updraft_option() - * @uses \MainWP\Child\MainWP_Utility::close_connection() - */ - public function vault_disconnect() { - $vault_settings = \UpdraftPlus_Options::get_updraft_option( 'updraft_updraftvault' ); - \UpdraftPlus_Options::update_updraft_option( 'updraft_updraftvault', array() ); + $out = array(); + if ($updated) { + $out['result'] = 'success'; + } else { + $out['result'] = 'noupdate'; + } - /** @global object $updraftplus UpdraftPlus instance. */ - global $updraftplus; + return $out; + } + + /** + * Replace %sitename%, %siteurl% and %random% tokens. + * + * @param string $str String to search & replace. + * @return string|string[] $str Altered string. + */ + public function replace_tokens($str = '') + { + if (stripos($str, '%sitename%') !== false) { + $replace_token = get_bloginfo('name'); + $replace_token = sanitize_file_name($replace_token); + $replace_token = strtolower($replace_token); + $str = str_ireplace('%sitename%', $replace_token, $str); + } - $vault_mothership = 'https://vault.updraftplus.com/plugin-info/'; + if (stripos($str, '%siteurl%') !== false) { + $replace_token = get_bloginfo('url'); + $replace_token = preg_replace('/^https?:\/\//i', '', $replace_token); + $replace_token = sanitize_file_name($replace_token); + $str = str_ireplace('%siteurl%', $replace_token, $str); + } - delete_transient( 'udvault_last_config' ); - delete_transient( 'updraftvault_quota_text' ); + if (stripos($str, '%random%') !== false) { + $replace_token = $this->generate_random_string(20); + $str = str_ireplace('%random%', $replace_token, $str); + } + return $str; + } + + /** + * Connect UpdraftPlus Premium addons. + * + * @return array|string[] $out return response array. Success or nopremium. + * + * @uses MainWP_Child_Updraft_Plus_Backups::update_wpmu_options() + */ + public function addons_connect() + { + if (! defined('UDADDONS2_SLUG')) { + if (is_file(UPDRAFTPLUS_DIR . '/udaddons/updraftplus-addons.php')) { + require_once UPDRAFTPLUS_DIR . '/udaddons/updraftplus-addons.php'; // NOSONAR - WP compatible. + } + if (! defined('UDADDONS2_SLUG')) { + return array('error' => 'NO_PREMIUM'); + } + } - MainWP_Utility::close_connection( - array( - 'disconnected' => 1, - 'html' => $this->connected_html(), - ) - ); - - // If $_POST['reset_hash'] is set, then we were alerted by updraftplus.com - no need to notify back. - if ( is_array( $vault_settings ) && isset( $vault_settings['email'] ) && empty( $_POST['reset_hash'] ) ) { - $post_body = array( - 'e' => (string) $vault_settings['email'], - 'sid' => $updraftplus->siteid(), - 'su' => base64_encode( home_url() ), // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions -- base64_encode function is used for http encode compatible.. - ); + $addons_options = isset($_POST['addons_options']) ? json_decode(base64_decode(wp_unslash($_POST['addons_options'])), true) : array(); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions -- base64_encode function is used for http encode compatible.. + if (! is_array($addons_options)) { + $addons_options = array(); + } - if ( ! empty( $vault_settings['token'] ) ) { - $post_body['token'] = (string) $vault_settings['token']; - } + $updated = $this->update_wpmu_options($addons_options); - // Use SSL to prevent snooping. - wp_remote_post( - $vault_mothership . '/?udm_action=vault_disconnect', - array( - 'timeout' => 20, - 'body' => $post_body, - ) - ); - } + $out = array(); + if ($updated) { + $out['result'] = 'success'; } - /** - * Require needed UpdraftPlus Files. - */ - public function required_files() { - if ( defined( 'UPDRAFTPLUS_DIR' ) ) { - if ( ! class_exists( '\UpdraftPlus' ) && file_exists( UPDRAFTPLUS_DIR . '/class-updraftplus.php' ) ) { - require_once UPDRAFTPLUS_DIR . '/class-updraftplus.php'; // NOSONAR - WP compatible. - } + return $out; + } + + /** + * Update WPMU Options. + * + * @param array $value Values to save. + * @return bool|void Return TRUE on success or VOID on failure. + * + * @uses UpdraftPlus_Options::user_can_manage() + * @uses MainWP_Child_Updraft_Plus_Backups::addons2_get_option() + * @uses MainWP_Child_Updraft_Plus_Backups::options_validate() + * @uses MainWP_Child_Updraft_Plus_Backups::addons2_update_option() + */ + public function update_wpmu_options($value) + { + + if (! \UpdraftPlus_Options::user_can_manage()) { + return false; + } + $options = $this->addons2_get_option(UDADDONS2_SLUG . '_options'); + if (! is_array($options)) { + $options = array(); + } - if ( ! class_exists( '\UpdraftPlus_Options' ) && file_exists( UPDRAFTPLUS_DIR . '/options.php' ) ) { - require_once UPDRAFTPLUS_DIR . '/options.php'; // NOSONAR - WP compatible. - } - } + $options['email'] = isset($value['email']) ? $value['email'] : ''; + $options['password'] = isset($value['password']) ? $value['password'] : ''; + + $options = $this->options_validate($options); + $this->addons2_update_option(UDADDONS2_SLUG . '_options', $options); + + return true; + } + + /** + * Get site option (2) + * + * Funnelling through here, + * a) allows for future flexibility and, + * b) allows us to migrate elegantly from the previous non-MU-friendly setup. + * + * @param string $option Site option to get. + * @return mixed $val Returned site option. + */ + public function addons2_get_option($option) + { + $val = get_site_option($option); + // On multisite, migrate options into the site options. + if (false === $val && is_multisite()) { + $blog_id = get_current_blog_id(); + if ($blog_id > 1) { + $val = get_option($option); + if (false !== $val) { + delete_option($option); + update_site_option($option, $val); + + return $val; + } + } + // $val is still false. + switch_to_blog(1); + $val = get_option($option); + if (false !== $val) { + delete_option($option); + update_site_option($option, $val); + } + restore_current_blog(); } - /** - * Save UpdraftPlus settings. - * - * @return array $out Return response array. - * - * @uses MainWP_Child_Updraft_Plus_Backups::get_settings_keys() - * @uses MainWP_Child_Updraft_Plus_Backups::replace_tokens() - * @uses UpdraftPlus_Options::get_updraft_option() - * @uses UpdraftPlus_Options::update_updraft_option() - * @uses $updraftplus::schedule_backup() - * @uses $updraftplus::schedule_backup_database() - */ - public function save_settings() { //phpcs:ignore -- NOSONAR - complex. - $settings = isset( $_POST['settings'] ) ? json_decode( base64_decode( wp_unslash( $_POST['settings'] ) ), true ) : ''; // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions -- base64_encode function is used for http encode compatible.. + return $val; + } + + /** + * Update site option (2) + * + * @param string $option Site option to update. + * @param string $val Value New option value. + * @return bool False if value was not updated. True if value was updated. + */ + public function addons2_update_option($option, $val) + { + return update_site_option($option, $val); + } + + /** + * When the options are re-saved, clear any previous cache of the connection status. + * + * @param string $input Option to validate. + * @return mixed $input The validated option. + */ + public function options_validate($input) + { + $ehash = substr(md5($input['email']), 0, 23); // NOSONAR - 3rd compatible. + delete_site_transient('udaddons_connect_' . $ehash); + + return $input; + } + + + /** + * Extra DB test connection. + * + * @return array Return response array. + * + * @uses UpdraftPlus_WPDB_OtherDB_Test() + * + * Credits + * Plugin: UpdraftPlus - Backup/Restore + * PluginURI: http://updraftplus.com + * Description: Backup and restore: take backups locally, or backup to Amazon S3, Dropbox, Google Drive, Rackspace, (S)FTP, WebDAV & email, on automatic schedules. + * Author: UpdraftPlus.Com, DavidAnderson + * Version: 1.9.60 + * Donate link: http://david.dw-perspective.org.uk/donate + * License: GPLv3 or later + * Text Domain: updraftplus + * Domain Path: /languages + * Author URI: http://updraftplus.com + */ + public function extradb_testconnection() { //phpcs:ignore -- NOSONAR - complex. + + if (! class_exists('\UpdraftPlus_WPDB_OtherDB_Test') && file_exists(UPDRAFTPLUS_DIR . '/addons/moredatabase.php')) { + require_once UPDRAFTPLUS_DIR . '/addons/moredatabase.php'; // NOSONAR - WP compatible. + } - $keys_filter = $this->get_settings_keys(); + if (! class_exists('\UpdraftPlus_WPDB_OtherDB_Test')) { + return array( + 'r' => isset($_POST['row']) ? wp_unslash($_POST['row']) : '', + 'm' => 'Error: Require premium UpdraftPlus plugin.', + ); + } - /** @global object $updraftplus UpdraftPlus instance. */ - global $updraftplus; + if (empty($_POST['user_db'])) { + return array( + 'r' => isset($_POST['row']) ? wp_unslash($_POST['row']) : '', + 'm' => '

' . sprintf(esc_html__('Failure: No %s was given.', 'updraftplus') . '

', esc_html__('user', 'updraftplus')), + ); + } - $updated = false; - if ( is_array( $settings ) && class_exists( '\UpdraftPlus_Options' ) ) { - foreach ( $keys_filter as $key ) { - if ( 'updraft_googledrive' === $key || 'updraft_googlecloud' === $key || 'updraft_onedrive' === $key ) { - continue; - } - if ( isset( $settings[ $key ] ) ) { - $settings_key = null; - if ( 'updraft_dropbox' === $key && is_array( $settings[ $key ] ) ) { - $opts = \UpdraftPlus_Options::get_updraft_option( 'updraft_dropbox' ); - if ( ! is_array( $opts ) ) { - $opts = array(); - } - if ( is_array( $opts ) && isset( $opts['settings'] ) ) { - $settings_key = key( $opts['settings'] ); - $opts['settings'][ $settings_key ]['folder'] = $this->replace_tokens( $settings[ $key ]['folder'] ); - } else { - $opts['folder'] = $this->replace_tokens( $settings[ $key ]['folder'] ); - } - \UpdraftPlus_Options::update_updraft_option( $key, $opts ); - } elseif ( 'updraft_email' === $key ) { - $value = $settings[ $key ]; - if ( ! is_array( $value ) && ! empty( $value ) ) { - $value = htmlspecialchars( get_bloginfo( 'admin_email' ) ); - } - \UpdraftPlus_Options::update_updraft_option( $key, $value ); - } elseif ( 'updraft_s3' === $key ) { - $opts = \UpdraftPlus_Options::get_updraft_option( 'updraft_s3' ); - if ( ! is_array( $opts ) ) { - $opts = array(); - } - if ( is_array( $opts ) && isset( $opts['settings'] ) ) { - $settings_key = key( $opts['settings'] ); - $opts['settings'][ $settings_key ]['accesskey'] = $settings[ $key ]['accesskey']; - $opts['settings'][ $settings_key ]['secretkey'] = $settings[ $key ]['secretkey']; - $opts['settings'][ $settings_key ]['path'] = $this->replace_tokens( $settings[ $key ]['path'] ); - if ( ! empty( $opts['settings'][ $settings_key ]['path'] ) && '/' === substr( $opts['settings'][ $settings_key ]['path'], 0, 1 ) ) { - $opts['settings'][ $settings_key ]['path'] = substr( $opts['settings'][ $settings_key ]['path'], 1 ); - } - if ( isset( $settings[ $key ]['rrs'] ) ) { // premium settings. - $opts['settings'][ $settings_key ]['rrs'] = $settings[ $key ]['rrs']; - $opts['settings'][ $settings_key ]['server_side_encryption'] = $settings[ $key ]['server_side_encryption']; - } - } else { - $opts['accesskey'] = $settings[ $key ]['accesskey']; - $opts['secretkey'] = $settings[ $key ]['secretkey']; - $opts['path'] = $this->replace_tokens( $settings[ $key ]['path'] ); - if ( ! empty( $opts['path'] ) && '/' === substr( $opts['path'], 0, 1 ) ) { - $opts['path'] = substr( $opts['path'], 1 ); - } - if ( isset( $settings[ $key ]['rrs'] ) ) { // premium settings. - $opts['rrs'] = $settings[ $key ]['rrs']; - $opts['server_side_encryption'] = $settings[ $key ]['server_side_encryption']; - } - } - - \UpdraftPlus_Options::update_updraft_option( $key, $opts ); - } elseif ( 'updraft_s3generic' === $key ) { - $opts = \UpdraftPlus_Options::get_updraft_option( 'updraft_s3generic' ); - if ( ! is_array( $opts ) ) { - $opts = array(); - } - if ( is_array( $opts ) && isset( $opts['settings'] ) ) { - $settings_key = key( $opts['settings'] ); - $opts['settings'][ $settings_key ]['endpoint'] = $settings[ $key ]['endpoint']; - $opts['settings'][ $settings_key ]['accesskey'] = $settings[ $key ]['accesskey']; - $opts['settings'][ $settings_key ]['secretkey'] = $settings[ $key ]['secretkey']; - $opts['settings'][ $settings_key ]['path'] = $this->replace_tokens( $settings[ $key ]['path'] ); - } else { - $opts['endpoint'] = $settings[ $key ]['endpoint']; - $opts['accesskey'] = $settings[ $key ]['accesskey']; - $opts['secretkey'] = $settings[ $key ]['secretkey']; - $opts['path'] = $this->replace_tokens( $settings[ $key ]['path'] ); - } - - \UpdraftPlus_Options::update_updraft_option( $key, $opts ); - } elseif ( 'updraft_dreamobjects' === $key ) { - $opts = \UpdraftPlus_Options::get_updraft_option( 'updraft_dreamobjects' ); - if ( ! is_array( $opts ) ) { - $opts = array(); - } - if ( is_array( $opts ) && isset( $opts['settings'] ) ) { - $settings_key = key( $opts['settings'] ); - $opts['settings'][ $settings_key ]['path'] = $this->replace_tokens( $settings[ $key ]['path'] ); - $opts['settings'][ $settings_key ]['endpoint'] = $settings[ $key ]['endpoint']; - } else { - $opts['path'] = $this->replace_tokens( $settings[ $key ]['path'] ); - $opts['endpoint'] = $settings[ $key ]['endpoint']; - } - \UpdraftPlus_Options::update_updraft_option( $key, $opts ); - } elseif ( 'updraft_ftp' === $key ) { - $opts = \UpdraftPlus_Options::get_updraft_option( 'updraft_ftp' ); - if ( ! is_array( $opts ) ) { - $opts = array(); - } - if ( is_array( $opts ) && isset( $opts['settings'] ) ) { - $settings_key = key( $opts['settings'] ); - if ( isset( $settings[ $key ]['path'] ) ) { - $opts['settings'][ $settings_key ]['host'] = $settings[ $key ]['host']; - $opts['settings'][ $settings_key ]['user'] = $settings[ $key ]['user']; - $opts['settings'][ $settings_key ]['pass'] = $settings[ $key ]['pass']; - $opts['settings'][ $settings_key ]['path'] = $this->replace_tokens( $settings[ $key ]['path'] ); - $opts['settings'][ $settings_key ]['passive'] = isset( $settings[ $key ]['passive'] ) ? $settings[ $key ]['passive'] : 0; - } - } else { - if ( isset( $settings[ $key ]['path'] ) ) { - $opts['host'] = $settings[ $key ]['host']; - $opts['user'] = $settings[ $key ]['user']; - $opts['pass'] = $settings[ $key ]['pass']; - $opts['path'] = $this->replace_tokens( $settings[ $key ]['path'] ); - $opts['passive'] = isset( $settings[ $key ]['passive'] ) ? $settings[ $key ]['passive'] : 0; - } - } - - \UpdraftPlus_Options::update_updraft_option( $key, $opts ); - } elseif ( 'updraft_sftp_settings' === $key ) { - $opts = \UpdraftPlus_Options::get_updraft_option( 'updraft_sftp' ); - if ( ! is_array( $opts ) ) { - $opts = array(); - } - if ( is_array( $opts ) && isset( $opts['settings'] ) ) { - $settings_key = key( $opts['settings'] ); - if ( isset( $settings[ $key ]['path'] ) ) { - $opts['settings'][ $settings_key ]['host'] = $settings[ $key ]['host']; - $opts['settings'][ $settings_key ]['port'] = $settings[ $key ]['port']; - $opts['settings'][ $settings_key ]['user'] = $settings[ $key ]['user']; - $opts['settings'][ $settings_key ]['pass'] = $settings[ $key ]['pass']; - $opts['settings'][ $settings_key ]['key'] = $settings[ $key ]['key']; - $opts['settings'][ $settings_key ]['path'] = $this->replace_tokens( $settings[ $key ]['path'] ); - $opts['settings'][ $settings_key ]['scp'] = isset( $settings[ $key ]['scp'] ) ? $settings[ $key ]['scp'] : 0; - } - } else { - if ( isset( $settings[ $key ]['path'] ) ) { - $opts['host'] = $settings[ $key ]['host']; - $opts['port'] = $settings[ $key ]['port']; - $opts['user'] = $settings[ $key ]['user']; - $opts['pass'] = $settings[ $key ]['pass']; - $opts['key'] = $settings[ $key ]['key']; - $opts['path'] = $this->replace_tokens( $settings[ $key ]['path'] ); - $opts['scp'] = isset( $settings[ $key ]['scp'] ) ? $settings[ $key ]['scp'] : 0; - } - } - \UpdraftPlus_Options::update_updraft_option( 'updraft_sftp', $opts ); - } elseif ( 'updraft_webdav_settings' === $key && is_array( $settings[ $key ] ) ) { - $opts = \UpdraftPlus_Options::get_updraft_option( 'updraft_webdav' ); - if ( ! is_array( $opts ) ) { - $opts = array(); - } - - if ( is_array( $opts ) && isset( $opts['settings'] ) ) { - $settings_key = key( $opts['settings'] ); - $opts['settings'][ $settings_key ]['path'] = $this->replace_tokens( $settings[ $key ]['path'] ); - \UpdraftPlus_Options::update_updraft_option( 'updraft_webdav', $opts ); - } - } elseif ( 'updraft_backblaze' === $key ) { - $opts = \UpdraftPlus_Options::get_updraft_option( 'updraft_backblaze' ); - if ( ! is_array( $opts ) ) { - $opts = array(); - } - if ( is_array( $opts ) && isset( $opts['settings'] ) && is_array( $settings[ $key ] ) && isset( $settings[ $key ]['account_id'] ) ) { - $settings_key = key( $opts['settings'] ); - $opts['settings'][ $settings_key ]['account_id'] = $settings[ $key ]['account_id']; - $opts['settings'][ $settings_key ]['key'] = $settings[ $key ]['key']; - - if ( isset( $settings[ $key ]['single_bucket_key_id'] ) && ! empty( $settings[ $key ]['single_bucket_key_id'] ) ){ - $single_bucket_key_id = trim( $settings[ $key ]['single_bucket_key_id'] ); - if ( '[empty]' === $single_bucket_key_id ) { - $opts['settings'][ $settings_key ]['single_bucket_key_id'] = ''; - } elseif ( ! empty( $single_bucket_key_id ) ) { - $opts['settings'][ $settings_key ]['single_bucket_key_id'] = $single_bucket_key_id; - } - } - - $bname = $this->replace_tokens( $settings[ $key ]['bucket_name'] ); - $bpath = $this->replace_tokens( $settings[ $key ]['backup_path'] ); - $bname = str_replace( '.', '-', $bname ); - $bpath = str_replace( '.', '-', $bpath ); - $bname = str_replace( '_', '', $bname ); // to fix uncommon character issues. - $bpath = str_replace( '_', '', $bpath ); - $opts['settings'][ $settings_key ]['bucket_name'] = $bname; - $opts['settings'][ $settings_key ]['backup_path'] = $bpath; - \UpdraftPlus_Options::update_updraft_option( $key, $opts ); - } - } elseif ( 'updraft_interval_increments' === $key ) { - $value = $updraftplus->schedule_backup_increments( $settings[ $key ] ); - \UpdraftPlus_Options::update_updraft_option( $key, $value ); - } else { - \UpdraftPlus_Options::update_updraft_option( $key, $settings[ $key ] ); - } - $updated = true; - } - } + if (empty($_POST['host'])) { + return array( + 'r' => isset($_POST['row']) ? wp_unslash($_POST['row']) : '', + 'm' => '

' . sprintf(esc_html__('Failure: No %s was given.', 'updraftplus') . '

', esc_html__('host', 'updraftplus')), + ); + } - if ( ! isset( $settings['do_not_save_remote_settings'] ) || empty( $settings['do_not_save_remote_settings'] ) ) { - \UpdraftPlus_Options::update_updraft_option( 'updraft_service', $settings['updraft_service'] ); - } + if (empty($_POST['name'])) { + return array( + 'r' => isset($_POST['row']) ? wp_unslash($_POST['row']) : '', + 'm' => '

' . sprintf(esc_html__('Failure: No %s was given.', 'updraftplus') . '

', esc_html__('database name', 'updraftplus')), + ); + } - if ( isset( $settings['updraft_interval'] ) ) { - // fix for premium version. - $_POST['updraft_interval'] = $settings['updraft_interval']; - $_POST['updraft_startday_files'] = $settings['updraft_startday_files']; - $_POST['updraft_starttime_files'] = $settings['updraft_starttime_files']; - $updraftplus->schedule_backup( $settings['updraft_interval'] ); + /** @global object $updraftplus_admin UpdraftPlus Admin array. */ + global $updraftplus_admin; + + $updraftplus_admin->logged = array(); + + $ret = ''; + $failed = false; + + $wpdb_obj = new \UpdraftPlus_WPDB_OtherDB_Test($_POST['user_db'], $_POST['pass'], $_POST['name'], $_POST['host']); + if (! empty($wpdb_obj->error)) { + $failed = true; + $ret .= '

' . $dbinfo['user'] . '@' . $dbinfo['host'] . '/' . $dbinfo['name'] . ' : ' . esc_html__('database connection attempt failed', 'updraftplus') . '

'; + if (is_wp_error($wpdb_obj->error) || is_string($wpdb_obj->error)) { + $ret .= ''; + } } - /** - * Replace %sitename% and %siteurl% tokens. - * - * @param string $str String to search & replace. - * @return string|string[] $str Altered string. - */ - public function replace_tokens( $str = '' ) { - if ( stripos( $str, '%sitename%' ) !== false ) { - $replace_token = get_bloginfo( 'name' ); - $replace_token = sanitize_file_name( $replace_token ); - $replace_token = strtolower( $replace_token ); - $str = str_ireplace( '%sitename%', $replace_token, $str ); - } - - if ( stripos( $str, '%siteurl%' ) !== false ) { - $replace_token = get_bloginfo( 'url' ); - $replace_token = preg_replace( '/^https?:\/\//i', '', $replace_token ); - $replace_token = sanitize_file_name( $replace_token ); - $str = str_ireplace( '%siteurl%', $replace_token, $str ); - } - return $str; + $ret_info = ''; + if (! $failed) { + $all_tables = $wpdb_obj->get_results('SHOW TABLES', ARRAY_N); + $all_tables = array_map(array($this, 'cb_get_name_base_type'), $all_tables); + + if (empty($_POST['prefix'])) { + $ret_info .= sprintf(esc_html__('%s table(s) found.', 'updraftplus'), count($all_tables)); + } else { + $our_prefix = 0; + foreach ($all_tables as $table) { + if (0 === strpos($table, $_POST['prefix'])) { + $our_prefix++; + } + } + $ret_info .= sprintf(esc_html__('%1$s total table(s) found; %2$s with the indicated prefix.', 'updraftplus'), count($all_tables), $our_prefix); + } } - /** - * Connect UpdraftPlus Premium addons. - * - * @return array|string[] $out return response array. Success or nopremium. - * - * @uses MainWP_Child_Updraft_Plus_Backups::update_wpmu_options() - */ - public function addons_connect() { - if ( ! defined( 'UDADDONS2_SLUG' ) ) { - if ( is_file( UPDRAFTPLUS_DIR . '/udaddons/updraftplus-addons.php' ) ) { - require_once UPDRAFTPLUS_DIR . '/udaddons/updraftplus-addons.php'; // NOSONAR - WP compatible. - } - if ( ! defined( 'UDADDONS2_SLUG' ) ) { - return array( 'error' => 'NO_PREMIUM' ); - } - } - - $addons_options = isset( $_POST['addons_options'] ) ? json_decode( base64_decode( wp_unslash( $_POST['addons_options'] ) ), true ) : array(); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions -- base64_encode function is used for http encode compatible.. - if ( ! is_array( $addons_options ) ) { - $addons_options = array(); - } + $ret_after = ''; - $updated = $this->update_wpmu_options( $addons_options ); + if (count($updraftplus_admin->logged) > 0) { + $ret_after .= '

' . esc_html__('Messages:', 'updraftplus'); + $ret_after .= '

'; } - /** - * Update WPMU Options. - * - * @param array $value Values to save. - * @return bool|void Return TRUE on success or VOID on failure. - * - * @uses UpdraftPlus_Options::user_can_manage() - * @uses MainWP_Child_Updraft_Plus_Backups::addons2_get_option() - * @uses MainWP_Child_Updraft_Plus_Backups::options_validate() - * @uses MainWP_Child_Updraft_Plus_Backups::addons2_update_option() - */ - public function update_wpmu_options( $value ) { + if (! $failed) { + $ret = '

' . esc_html__('Connection succeeded.', 'updraftplus') . ' ' . $ret_info . '

' . $ret; + } else { + $ret = '

' . esc_html__('Connection failed.', 'updraftplus') . '

' . $ret; + } - if ( ! \UpdraftPlus_Options::user_can_manage() ) { - return false; - } - $options = $this->addons2_get_option( UDADDONS2_SLUG . '_options' ); - if ( ! is_array( $options ) ) { - $options = array(); - } + restore_error_handler(); + + return array( + 'r' => isset($_POST['row']) ? wp_unslash($_POST['row']) : '', + 'm' => $ret . $ret_after, + ); + } + + /** + * CB get name base type. + * + * @param $a + * @return mixed + */ + private function cb_get_name_base_type($a) + { + return $a[0]; + } + + /** + * Backup now. + * + * @uses MainWP_Child_Updraft_Plus_Backups::close_browser_connection() + */ + public function backup_now() { + + /** @global object $updraftplus UpdraftPlus object. */ + global $updraftplus; + + $event_nodb = ! empty($_REQUEST['backupnow_nodb']) ? 'updraft_backupnow_backup' : 'updraft_backupnow_backup_all'; + + $backupnow_nocloud = (empty($_REQUEST['backupnow_nocloud'])) ? false : true; + $event = (! empty($_REQUEST['backupnow_nofiles'])) ? 'updraft_backupnow_backup_database' : $event_nodb; + + // The call to backup_time_nonce() allows us to know the nonce in advance, and return it. + $nonce = $updraftplus->backup_time_nonce(); + + $msg = array( + 'nonce' => $nonce, + 'm' => '' . esc_html__('Start backup', 'updraftplus') . ': ' . htmlspecialchars(esc_html__('OK. You should soon see activity in the "Last log message" field below.', 'updraftplus')), + ); + + $this->close_browser_connection($msg); + + $options = array( + 'nocloud' => $backupnow_nocloud, + 'use_nonce' => $nonce, + ); + if (! empty($_REQUEST['onlythisfileentity']) && is_string($_REQUEST['onlythisfileentity'])) { + // Something to see in the 'last log' field when it first appears, before the backup actually starts. + $updraftplus->log(esc_html__('Start backup', 'updraftplus')); + $options['restrict_files_to_override'] = isset($_REQUEST['onlythisfileentity']) ? explode(',', $_REQUEST['onlythisfileentity']) : array(); + } - $options['email'] = isset( $value['email'] ) ? $value['email'] : ''; - $options['password'] = isset( $value['password'] ) ? $value['password'] : ''; + do_action($event, apply_filters('updraft_backupnow_options', $options, array())); + + // Control returns when the backup finished; However, the browser connection should have been closed before. + die; + } + + /** + * Active jobs list. + * + * @return array Return the active jobs list. + */ + public function activejobs_list() { //phpcs:ignore -- NOSONAR - complex. + + /** @global object $updraftplus UpdraftPlus object. */ + global $updraftplus; + + $download_status = array(); + if (! empty($_REQUEST['downloaders'])) { + foreach (explode(':', $_REQUEST['downloaders']) as $downloader) { + // prefix, timestamp, entity, index. + if (preg_match('/^([^,]+),(\d+),([-a-z]+|db\d+),(\d+)$/', $downloader, $matches)) { + $updraftplus->nonce = dechex($matches[2]) . $matches[4] . substr(md5($matches[3]), 0, 3); //phpcs:ignore -- NOSONAR - mmy encryption. + $status = $this->download_status($matches[2], $matches[3], $matches[4]); + if (is_array($status)) { + $status['base'] = $matches[1]; + $status['timestamp'] = $matches[2]; + $status['what'] = $matches[3]; + $status['findex'] = (empty($matches[4])) ? '0' : $matches[4]; + $download_status[] = $status; + } + } + } + } - $options = $this->options_validate( $options ); - $this->addons2_update_option( UDADDONS2_SLUG . '_options', $options ); + if (! empty($_REQUEST['oneshot'])) { + $job_id = get_site_option('updraft_oneshotnonce', false); + $active_jobs = (false === $job_id) ? '' : $this->print_active_job($job_id, true); + } elseif (! empty($_REQUEST['thisjobonly'])) { + $active_jobs = $this->print_active_jobs($_REQUEST['thisjobonly']); + } else { + $active_jobs = $this->print_active_jobs(); + } - return true; + $logupdate_array = array(); + if (! empty($_REQUEST['log_fetch']) && isset($_REQUEST['log_nonce'])) { + $log_nonce = isset($_REQUEST['log_nonce']) ? sanitize_text_field(wp_unslash($_REQUEST['log_nonce'])) : ''; + $log_pointer = isset($_REQUEST['log_pointer']) ? absint($_REQUEST['log_pointer']) : 0; + $logupdate_array = $this->fetch_log($log_nonce, $log_pointer); } - /** - * Get site option (2) - * - * Funnelling through here, - * a) allows for future flexibility and, - * b) allows us to migrate elegantly from the previous non-MU-friendly setup. - * - * @param string $option Site option to get. - * @return mixed $val Returned site option. - */ - public function addons2_get_option( $option ) { - $val = get_site_option( $option ); - // On multisite, migrate options into the site options. - if ( false === $val && is_multisite() ) { - $blog_id = get_current_blog_id(); - if ( $blog_id > 1 ) { - $val = get_option( $option ); - if ( false !== $val ) { - delete_option( $option ); - update_site_option( $option, $val ); - - return $val; - } - } - // $val is still false. - switch_to_blog( 1 ); - $val = get_option( $option ); - if ( false !== $val ) { - delete_option( $option ); - update_site_option( $option, $val ); - } - restore_current_blog(); + return array( + 'l' => htmlspecialchars(\UpdraftPlus_Options::get_updraft_option('updraft_lastmessage', '(' . esc_html__('Nothing yet logged', 'updraftplus') . ')')), + 'j' => $active_jobs, + 'ds' => $download_status, + 'u' => $logupdate_array, + ); + } + + + /** + * Last backup html. + * + * @return array Return last backup log text & backup time. + * + * @uses UpdraftPlus_Options::get_updraft_option() + * @uses $updraftplus::backups_dir_location() + */ + private function last_backup_html() { //phpcs:ignore -- NOSONAR - complex. + + /** @global object $updraftplus UpdraftPlus object. */ + global $updraftplus; + + $updraft_last_backup = \UpdraftPlus_Options::get_updraft_option('updraft_last_backup'); + $backup_time = 0; + if ($updraft_last_backup) { + + // Convert to GMT, then to blog time. + $backup_time = (int) $updraft_last_backup['backup_time']; + $print_time = get_date_from_gmt(gmdate('Y-m-d H:i:s', $backup_time), 'D, F j, Y H:i'); + + if (empty($updraft_last_backup['backup_time_incremental'])) { + $last_backup_text = '' . $print_time . ''; + } else { + $inc_time = get_date_from_gmt(gmdate('Y-m-d H:i:s', $updraft_last_backup['backup_time_incremental']), 'D, F j, Y H:i'); + $last_backup_text = '$inc_time (" . sprintf(esc_html__('incremental backup; base backup: %s', 'updraftplus'), $print_time) . ')'; + } + + $last_backup_text .= '
'; + + // Show errors + warnings. + if (is_array($updraft_last_backup['errors'])) { + foreach ($updraft_last_backup['errors'] as $err) { + $level = (is_array($err)) ? $err['level'] : 'error'; + $message = (is_array($err)) ? $err['message'] : $err; + $last_backup_text .= ('warning' === $level) ? '' : ''; + if ('warning' === $level) { + $message = sprintf(esc_html__('Warning: %s', 'updraftplus'), make_clickable(htmlspecialchars($message))); + } else { + $message = htmlspecialchars($message); + } + $last_backup_text .= $message; + $last_backup_text .= ''; + $last_backup_text .= '
'; + } + } + + // Link log. + if (! empty($updraft_last_backup['backup_nonce'])) { + $updraft_dir = $updraftplus->backups_dir_location(); + + $potential_log_file = $updraft_dir . '/log.' . $updraft_last_backup['backup_nonce'] . '.txt'; + if (is_readable($potential_log_file)) { + $last_backup_text .= "" . esc_html__('Download log file', 'updraftplus') . ''; } + } + } else { + $last_backup_text = '' . esc_html__('No backup has been completed.', 'updraftplus') . ''; + } - return $val; + return array( + 'b' => $last_backup_text, + 'lasttime_gmt' => $backup_time, + ); + } + + /** + * Get UpdraftPlus data. + * + * @param bool $with_hist Whether or not to build history. + * + * @uses UpdraftPlus() + * @uses UpdraftPlus_Options::get_updraft_option() + * @uses UpdraftPlus_Filesystem_Functions::really_is_writable() + * @uses \MainWP\Child\MainWP_Helper::instance()->check_methods() + * @uses \MainWP\Child\MainWP_Helper::MainWP_Helper::instance()->check_methods() + * @uses MainWP_Child_Updraft_Plus_Backups::build_historystatus() + * @uses MainWP_Child_Updraft_Plus_Backups::last_backup_html() + */ + private function get_updraft_data($with_hist = true) { //phpcs:ignore -- NOSONAR - complex. + + /** @global object $updraftplus UpdraftPlus object. */ + global $updraftplus; + + if (empty($updraftplus) && class_exists('\UpdraftPlus')) { + $updraftplus = new \UpdraftPlus(); } - /** - * Update site option (2) - * - * @param string $option Site option to update. - * @param string $val Value New option value. - * @return bool False if value was not updated. True if value was updated. - */ - public function addons2_update_option( $option, $val ) { - return update_site_option( $option, $val ); + if (empty($updraftplus)) { + return false; } - /** - * When the options are re-saved, clear any previous cache of the connection status. - * - * @param string $input Option to validate. - * @return mixed $input The validated option. - */ - public function options_validate( $input ) { - $ehash = substr( md5( $input['email'] ), 0, 23 ); // NOSONAR - 3rd compatible. - delete_site_transient( 'udaddons_connect_' . $ehash ); + // UNIX timestamp. + $next_scheduled_backup = wp_next_scheduled('updraft_backup'); + $next_scheduled_backup_gmt = 0; + $next_scheduled_backup_database_gmt = 0; + 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'); + } else { + $next_scheduled_backup = 'Nothing currently scheduled'; + } - return $input; + MainWP_Helper::instance()->check_classes_exists(array('\UpdraftPlus_Options', '\UpdraftPlus_Filesystem_Functions')); + MainWP_Helper::instance()->check_methods('\UpdraftPlus_Options', 'get_updraft_option'); + MainWP_Helper::instance()->check_methods('\UpdraftPlus_Filesystem_Functions', 'really_is_writable'); + MainWP_Helper::instance()->check_methods($updraftplus, array('backups_dir_location')); + + $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')) { + $next_scheduled_backup_database = ('Nothing currently scheduled' === $next_scheduled_backup) ? $next_scheduled_backup : esc_html__('At the same time as the files backup', 'updraftplus'); + } 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'); + } else { + $next_scheduled_backup_database = esc_html__('Nothing currently scheduled', 'updraftplus'); + } } + $updraft_dir = $updraftplus->backups_dir_location(); + $backup_disabled = (\UpdraftPlus_Filesystem_Functions::really_is_writable($updraft_dir)) ? 0 : 1; - /** - * Extra DB test connection. - * - * @return array Return response array. - * - * @uses UpdraftPlus_WPDB_OtherDB_Test() - * - * Credits - * Plugin: UpdraftPlus - Backup/Restore - * PluginURI: http://updraftplus.com - * Description: Backup and restore: take backups locally, or backup to Amazon S3, Dropbox, Google Drive, Rackspace, (S)FTP, WebDAV & email, on automatic schedules. - * Author: UpdraftPlus.Com, DavidAnderson - * Version: 1.9.60 - * Donate link: http://david.dw-perspective.org.uk/donate - * License: GPLv3 or later - * Text Domain: updraftplus - * Domain Path: /languages - * Author URI: http://updraftplus.com - */ - public function extradb_testconnection() { //phpcs:ignore -- NOSONAR - complex. + $current_timegmt = time(); + $current_time = get_date_from_gmt(gmdate('Y-m-d H:i:s', $current_timegmt), 'D, F j, Y H:i'); - if ( ! class_exists( '\UpdraftPlus_WPDB_OtherDB_Test' ) && file_exists( UPDRAFTPLUS_DIR . '/addons/moredatabase.php' ) ) { - require_once UPDRAFTPLUS_DIR . '/addons/moredatabase.php'; // NOSONAR - WP compatible. - } + $out = array( + 'updraft_backup_disabled' => $backup_disabled, + 'nextsched_files_gmt' => $next_scheduled_backup_gmt, + 'nextsched_database_gmt' => $next_scheduled_backup_gmt, + 'nextsched_current_timegmt' => $current_timegmt, + 'nextsched_current_timezone' => $current_time, + ); - if ( ! class_exists( '\UpdraftPlus_WPDB_OtherDB_Test' ) ) { - return array( - 'r' => isset( $_POST['row'] ) ? wp_unslash( $_POST['row'] ) : '', - 'm' => 'Error: Require premium UpdraftPlus plugin.', - ); - } + if ($next_scheduled_backup_gmt) { + $out['nextsched_files_timezone'] = $next_scheduled_backup; + } - if ( empty( $_POST['user_db'] ) ) { - return array( - 'r' => isset( $_POST['row'] ) ? wp_unslash( $_POST['row'] ) : '', - 'm' => '

' . sprintf( esc_html__( 'Failure: No %s was given.', 'updraftplus' ) . '

', esc_html__( 'user', 'updraftplus' ) ), - ); - } + if ($next_scheduled_backup_gmt) { + $out['nextsched_database_timezone'] = $next_scheduled_backup_database; + } - if ( empty( $_POST['host'] ) ) { - return array( - 'r' => isset( $_POST['row'] ) ? wp_unslash( $_POST['row'] ) : '', - 'm' => '

' . sprintf( esc_html__( 'Failure: No %s was given.', 'updraftplus' ) . '

', esc_html__( 'host', 'updraftplus' ) ), - ); - } + $bh = $this->build_historystatus(); - if ( empty( $_POST['name'] ) ) { - return array( - 'r' => isset( $_POST['row'] ) ? wp_unslash( $_POST['row'] ) : '', - 'm' => '

' . sprintf( esc_html__( 'Failure: No %s was given.', 'updraftplus' ) . '

', esc_html__( 'database name', 'updraftplus' ) ), - ); - } + // Fixed performance issue. + if ($with_hist) { + $out['updraft_historystatus'] = $bh['h']; + } - /** @global object $updraftplus_admin UpdraftPlus Admin array. */ - global $updraftplus_admin; - - $updraftplus_admin->logged = array(); - - $ret = ''; - $failed = false; - - $wpdb_obj = new \UpdraftPlus_WPDB_OtherDB_Test( $_POST['user_db'], $_POST['pass'], $_POST['name'], $_POST['host'] ); - if ( ! empty( $wpdb_obj->error ) ) { - $failed = true; - $ret .= '

' . $dbinfo['user'] . '@' . $dbinfo['host'] . '/' . $dbinfo['name'] . ' : ' . esc_html__( 'database connection attempt failed', 'updraftplus' ) . '

'; - if ( is_wp_error( $wpdb_obj->error ) || is_string( $wpdb_obj->error ) ) { - $ret .= ''; - } - } + $out['updraft_count_backups'] = $bh['c']; + + $last_backup = $this->last_backup_html(); + $out['updraft_lastbackup_html'] = $last_backup['b']; + $out['updraft_lastbackup_gmttime'] = $last_backup['lasttime_gmt']; + + return $out; + } + + /** + * Get next scheduled backup. + * + * @return array $out Return Next scheduled backup data. + * @throws MainWP_Exception|MainWP_Exception Error message. + * + * @uses UpdraftPlus_Options::get_updraft_option() + * @uses \MainWP\Child\MainWP_Helper::instance()->check_classes_exists() + * @uses \MainWP\Child\MainWP_Helper::instance()->check_methods() + */ + private function next_scheduled_backups() + { + + /** @global object $updraftplus UpdraftPlus object. */ + global $updraftplus; + + // UNIX timestamp. + $next_scheduled_backup = wp_next_scheduled('updraft_backup'); + $next_scheduled_backup_gmt = 0; + $next_scheduled_backup_database_gmt = 0; + + 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'); + } else { + $next_scheduled_backup = esc_html__('Nothing currently scheduled', 'updraftplus'); + $files_not_scheduled = true; + } - $ret_info = ''; - if ( ! $failed ) { - $all_tables = $wpdb_obj->get_results( 'SHOW TABLES', ARRAY_N ); - $all_tables = array_map( array( $this, 'cb_get_name_base_type' ), $all_tables ); + $next_scheduled_backup_database = wp_next_scheduled('updraft_backup_database'); + if ((int) \UpdraftPlus_Options::get_updraft_option('updraft_interval_database', \UpdraftPlus_Options::get_updraft_option('updraft_interval')) === (int) \UpdraftPlus_Options::get_updraft_option('updraft_interval')) { + if (isset($files_not_scheduled)) { + $next_scheduled_backup_database = $next_scheduled_backup; + } else { + $next_scheduled_backup_database = esc_html__('At the same time as the files backup', 'updraftplus'); + } + } 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'); + } else { + $next_scheduled_backup_database = esc_html__('Nothing currently scheduled', 'updraftplus'); + } + } - if ( empty( $_POST['prefix'] ) ) { - $ret_info .= sprintf( esc_html__( '%s table(s) found.', 'updraftplus' ), count( $all_tables ) ); - } else { - $our_prefix = 0; - foreach ( $all_tables as $table ) { - if ( 0 === strpos( $table, $_POST['prefix'] ) ) { - $our_prefix ++; - } - } - $ret_info .= sprintf( esc_html__( '%1$s total table(s) found; %2$s with the indicated prefix.', 'updraftplus' ), count( $all_tables ), $our_prefix ); - } - } + $current_timegmt = time(); + $current_time = get_date_from_gmt(gmdate('Y-m-d H:i:s', $current_timegmt), 'D, F j, Y H:i'); - $ret_after = ''; + $html = ' + + + + + + + + + + + + + + +
' . esc_html__('Files', 'mainwp-updraftplus-extension') . '' . esc_html__('Database', 'mainwp-updraftplus-extension') . '' . esc_html__('Time now', 'mainwp-updraftplus-extension') . '
' . $next_scheduled_backup . '' . $next_scheduled_backup_database . '' . $current_time . '
'; - if ( count( $updraftplus_admin->logged ) > 0 ) { - $ret_after .= '

' . esc_html__( 'Messages:', 'updraftplus' ); - $ret_after .= '

'; - } + $updraft_dir = $updraftplus->backups_dir_location(); + $backup_disabled = (\UpdraftPlus_Filesystem_Functions::really_is_writable($updraft_dir)) ? 0 : 1; - if ( ! $failed ) { - $ret = '

' . esc_html__( 'Connection succeeded.', 'updraftplus' ) . ' ' . $ret_info . '

' . $ret; - } else { - $ret = '

' . esc_html__( 'Connection failed.', 'updraftplus' ) . '

' . $ret; - } + $out = array( + 'n' => $html, + 'updraft_backup_disabled' => $backup_disabled, + 'nextsched_files_gmt' => $next_scheduled_backup_gmt, + 'nextsched_database_gmt' => $next_scheduled_backup_database_gmt, + 'nextsched_current_timegmt' => $current_timegmt, + 'nextsched_current_timezone' => $current_time, + ); - restore_error_handler(); + if ($next_scheduled_backup_gmt) { + $out['nextsched_files_timezone'] = $next_scheduled_backup; + } - return array( - 'r' => isset( $_POST['row'] ) ? wp_unslash( $_POST['row'] ) : '', - 'm' => $ret . $ret_after, - ); + if ($next_scheduled_backup_database_gmt) { + $out['nextsched_database_timezone'] = $next_scheduled_backup_database; } - /** - * CB get name base type. - * - * @param $a - * @return mixed - */ - private function cb_get_name_base_type( $a ) { - return $a[0]; + return $out; + } + + /** + * Delete backup set. + * + * @return array Return results array. + * @throws MainWP_Exception + * + * @uses $updraftplus::get_backup_history() + * @uses $updraftplus::backup_time_nonce() + * @uses $updraftplus::jobdata_set() + * @uses $updraftplus::logfile_open() + * @uses $updraftplus::backups_dir_location() + * @uses $updraftplus::get_backupable_file_entities() + * @uses UpdraftPlus_Options::get_updraft_option() + * @uses UpdraftPlus_Options::update_updraft_option() + * @uses UpdraftPlus_Backup_History::get_history() + * @uses MainWP_Child_Updraft_Plus_Backups::build_historystatus() + */ + private function deleteset() { //phpcs:ignore -- NOSONAR - complex. + + /** @global object $updraftplus UpdraftPlus object. */ + global $updraftplus; + + if (method_exists($updraftplus, 'get_backup_history')) { + $backups = $updraftplus->get_backup_history(); + } elseif (class_exists('\UpdraftPlus_Backup_History')) { + $backups = \UpdraftPlus_Backup_History::get_history(); + } + $timestamp = isset($_POST['backup_timestamp']) ? sanitize_text_field(wp_unslash($_POST['backup_timestamp'])) : ''; + if (! isset($backups[$timestamp])) { + $bh = $this->build_historystatus(); + + return array( + 'result' => 'error', + 'message' => esc_html__('Backup set not found', 'updraftplus'), + 'updraft_historystatus' => $bh['h'], + 'updraft_count_backups' => $bh['c'], + ); } - /** - * Backup now. - * - * @uses MainWP_Child_Updraft_Plus_Backups::close_browser_connection() - */ - public function backup_now() { + // 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); // phpcs:ignore -- third party credits. + } + + $updraft_dir = $updraftplus->backups_dir_location(); + $backupable_entities = $updraftplus->get_backupable_file_entities(true, true); - /** @global object $updraftplus UpdraftPlus object. */ - global $updraftplus; + $nonce = isset($backups[$timestamp]['nonce']) ? $backups[$timestamp]['nonce'] : ''; - $event_nodb = ! empty( $_REQUEST['backupnow_nodb'] ) ? 'updraft_backupnow_backup' : 'updraft_backupnow_backup_all'; + $delete_from_service = array(); - $backupnow_nocloud = ( empty( $_REQUEST['backupnow_nocloud'] ) ) ? false : true; - $event = ( ! empty( $_REQUEST['backupnow_nofiles'] ) ) ? 'updraft_backupnow_backup_database' : $event_nodb; + if (isset($_POST['delete_remote']) && 1 === (int) $_POST['delete_remote'] && isset($backups[$timestamp]['service'])) { + $services = is_string($backups[$timestamp]['service']) ? array($backups[$timestamp]['service']) : $backups[$timestamp]['service']; + if (is_array($services)) { + foreach ($services as $service) { + if ('none' !== $service) { + $delete_from_service[] = $service; + } + } + } + } - // The call to backup_time_nonce() allows us to know the nonce in advance, and return it. - $nonce = $updraftplus->backup_time_nonce(); + $files_to_delete = array(); + foreach ($backupable_entities as $key => $ent) { + if (isset($backups[$timestamp][$key])) { + $files_to_delete[$key] = $backups[$timestamp][$key]; + } + } + // Delete DB. + if (isset($backups[$timestamp]['db'])) { + $files_to_delete['db'] = $backups[$timestamp]['db']; + } - $msg = array( - 'nonce' => $nonce, - 'm' => '' . esc_html__( 'Start backup', 'updraftplus' ) . ': ' . htmlspecialchars( esc_html__( 'OK. You should soon see activity in the "Last log message" field below.', 'updraftplus' ) ), - ); + // Also delete the log. + if ($nonce && ! \UpdraftPlus_Options::get_updraft_option('updraft_debug_mode')) { + $files_to_delete['log'] = "log.$nonce.txt"; + } - $this->close_browser_connection( $msg ); + unset($backups[$timestamp]); + \UpdraftPlus_Options::update_updraft_option('updraft_backup_history', $backups); + + $message = ''; + + $local_deleted = 0; + $remote_deleted = 0; + add_action('http_request_args', array($updraftplus, 'modify_http_options')); + foreach ($files_to_delete as $key => $files) { + // Local deletion. + if (is_string($files)) { + $files = array($files); + } + foreach ($files as $file) { + if (is_file($updraft_dir . '/' . $file) && wp_delete_file($updraft_dir . '/' . $file)) { + $local_deleted++; + } + } + if ('log' !== $key && ! empty($delete_from_service)) { + foreach ($delete_from_service as $service) { + if ('email' === $service) { + continue; + } + if (file_exists(UPDRAFTPLUS_DIR . "/methods/$service.php")) { + require_once UPDRAFTPLUS_DIR . "/methods/$service.php"; // NOSONAR - WP compatible. + } + $objname = '\UpdraftPlus_BackupModule_' . $service; + $deleted = -1; + if (class_exists($objname)) { + $remote_obj = new $objname(); + $deleted = $remote_obj->delete($files); + } - $options = array( - 'nocloud' => $backupnow_nocloud, - 'use_nonce' => $nonce, - ); - if ( ! empty( $_REQUEST['onlythisfileentity'] ) && is_string( $_REQUEST['onlythisfileentity'] ) ) { - // Something to see in the 'last log' field when it first appears, before the backup actually starts. - $updraftplus->log( esc_html__( 'Start backup', 'updraftplus' ) ); - $options['restrict_files_to_override'] = isset( $_REQUEST['onlythisfileentity'] ) ? explode( ',', $_REQUEST['onlythisfileentity'] ) : array(); + if (-1 !== $deleted && false !== $deleted) { + $remote_deleted = $remote_deleted + count($files); + } } + } + } + remove_action('http_request_args', array($updraftplus, 'modify_http_options')); + $message .= esc_html__('The backup set has been removed.', 'updraftplus') . "\n"; + $message .= sprintf(esc_html__('Local archives deleted: %d', 'updraftplus'), $local_deleted) . "\n"; + $message .= sprintf(esc_html__('Remote archives deleted: %d', 'updraftplus'), $remote_deleted) . "\n"; - do_action( $event, apply_filters( 'updraft_backupnow_options', $options, array() ) ); + $updraftplus->log('Local archives deleted: ' . $local_deleted); + $updraftplus->log('Remote archives deleted: ' . $remote_deleted); - // Control returns when the backup finished; However, the browser connection should have been closed before. - die; + if (\UpdraftPlus_Options::get_updraft_option('updraft_debug_mode')) { + restore_error_handler(); } - /** - * Active jobs list. - * - * @return array Return the active jobs list. - */ - public function activejobs_list() { //phpcs:ignore -- NOSONAR - complex. - - /** @global object $updraftplus UpdraftPlus object. */ - global $updraftplus; - - $download_status = array(); - if ( ! empty( $_REQUEST['downloaders'] ) ) { - foreach ( explode( ':', $_REQUEST['downloaders'] ) as $downloader ) { - // prefix, timestamp, entity, index. - if ( preg_match( '/^([^,]+),(\d+),([-a-z]+|db\d+),(\d+)$/', $downloader, $matches ) ) { - $updraftplus->nonce = $matches[2]; - $status = $this->download_status( $matches[2], $matches[3], $matches[4] ); - if ( is_array( $status ) ) { - $status['base'] = $matches[1]; - $status['timestamp'] = $matches[2]; - $status['what'] = $matches[3]; - $status['findex'] = ( empty( $matches[4] ) ) ? '0' : $matches[4]; - $download_status[] = $status; - } - } - } - } + $bh = $this->build_historystatus(); + + return array( + 'result' => 'success', + 'message' => $message, + 'updraft_historystatus' => $bh['h'], + 'updraft_count_backups' => $bh['c'], + ); + } + + /** + * Build backup history status. + * + * @return array Return response array. + * @throws MainWP_Exception Error message. + * + * @uses \MainWP\Child\MainWP_Helper::instance()->check_classes_exists() + * @uses \MainWP\Child\MainWP_Helper::instance()->check_methods() + * @uses \MainWP\Child\MainWP_Child_Updraft_Plus_Backups::existing_backup_table() + * @uses \UpdraftPlus_Backup_History::get_history() + */ + public function build_historystatus() + { + MainWP_Helper::instance()->check_classes_exists('\UpdraftPlus_Backup_History'); + MainWP_Helper::instance()->check_methods('\UpdraftPlus_Backup_History', 'get_history'); + $backup_history = \UpdraftPlus_Backup_History::get_history(); + $output = $this->existing_backup_table($backup_history); + return array( + 'h' => $output, + 'c' => count($backup_history), + ); + } + + /** + * Display History Status. + * + * @param null $remotescan Remote Scan $_POST data. Default: null + * @param null $rescan Rescan $_POST data. Default: null + * + * @return array Return history status data. + * @throws MainWP_Exception + * + * @uses MainWP_Child_Updraft_Plus_Backups::rebuild_backup_history() + * @uses MainWP_Child_Updraft_Plus_Backups::existing_backup_table() + * @uses UpdraftPlus_Backup_History::get_history() + */ + public function historystatus($remotescan = null, $rescan = null) + { + + /** @global object $updraftplus UpdraftPlus object. */ + global $updraftplus; + + $remotescan = (null !== $remotescan) ? $remotescan : $_POST['remotescan']; + $rescan = (null !== $rescan) ? $rescan : $_POST['rescan']; + + if ($rescan) { + $messages = $this->rebuild_backup_history($remotescan); + } - if ( ! empty( $_REQUEST['oneshot'] ) ) { - $job_id = get_site_option( 'updraft_oneshotnonce', false ); - $active_jobs = ( false === $job_id ) ? '' : $this->print_active_job( $job_id, true ); - } elseif ( ! empty( $_REQUEST['thisjobonly'] ) ) { - $active_jobs = $this->print_active_jobs( $_REQUEST['thisjobonly'] ); - } else { - $active_jobs = $this->print_active_jobs(); - } + $backup_history = \UpdraftPlus_Backup_History::get_history(); + $output = $this->existing_backup_table($backup_history); - $logupdate_array = array(); - if ( ! empty( $_REQUEST['log_fetch'] ) && isset( $_REQUEST['log_nonce'] ) ) { - $log_nonce = isset( $_REQUEST['log_nonce'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['log_nonce'] ) ) : ''; - $log_pointer = isset( $_REQUEST['log_pointer'] ) ? absint( $_REQUEST['log_pointer'] ) : 0; - $logupdate_array = $this->fetch_log( $log_nonce, $log_pointer ); - } + if (! empty($messages) && is_array($messages)) { + $noutput = '
'; + $output = $noutput . $output; + } - return array( - 'l' => htmlspecialchars( \UpdraftPlus_Options::get_updraft_option( 'updraft_lastmessage', '(' . esc_html__( 'Nothing yet logged', 'updraftplus' ) . ')' ) ), - 'j' => $active_jobs, - 'ds' => $download_status, - 'u' => $logupdate_array, - ); + return array( + 'n' => sprintf(esc_html__('Existing Backups', 'updraftplus') . ' (%d)', count($backup_history)), + 't' => $output, + 'c' => count($backup_history), + 'm' => $updraftplus->detect_safe_mode(), + ); + } + + + /** + * UpdraftPlus Download Backup. + * + * @return array|string|string[] Return response array. + * + * @uses $updraftplus::get_backupable_file_entities() + * @uses $updraftplus::backup_time_nonce() + * @uses $updraftplus::jobdata_set() + * @uses $updraftplus::get_backup_history() + * @uses $updraftplus::backups_dir_location() + * @uses $updraftplus::spool_file() + * @uses $updraftplus::log() + * @uses $updraftplus::logfile_open() + * @uses $updraftplus::logfile_name() + * @uses $updraftplus::logfile_handle() + * @uses UpdraftPlus_Options::get_updraft_option() + * @uses UpdraftPlus_Backup_History::get_history() + * @uses MainWP_Child_Updraft_Plus_Backups::close_browser_connection() + * @uses MainWP_Child_Updraft_Plus_Backups::download_file() + */ + public function updraft_download_backup() { //phpcs:ignore -- NOSONAR - complex. + + set_time_limit(900); + + /** @global object $updraftplus UpdraftPlus object. */ + global $updraftplus; + + if (! isset($_REQUEST['timestamp']) || ! is_numeric($_REQUEST['timestamp']) || ! isset($_REQUEST['type'])) { + exit; } + $findex = (isset($_REQUEST['findex'])) ? $_REQUEST['findex'] : 0; + if (empty($findex)) { + $findex = 0; + } - /** - * Last backup html. - * - * @return array Return last backup log text & backup time. - * - * @uses UpdraftPlus_Options::get_updraft_option() - * @uses $updraftplus::backups_dir_location() - */ - private function last_backup_html() { //phpcs:ignore -- NOSONAR - complex. + $backupable_entities = $updraftplus->get_backupable_file_entities(true); + $type_match = false; + foreach ($backupable_entities as $type => $info) { + if ($_REQUEST['type'] === $type) { + $type_match = true; + } + } - /** @global object $updraftplus UpdraftPlus object. */ - global $updraftplus; + if (! $type_match && 'db' !== substr($_REQUEST['type'], 0, 2)) { + exit; + } - $updraft_last_backup = \UpdraftPlus_Options::get_updraft_option( 'updraft_last_backup' ); - $backup_time = 0; - if ( $updraft_last_backup ) { + // Get the information on what is wanted. + $type = isset($_REQUEST['type']) ? sanitize_text_field(wp_unslash($_REQUEST['type'])) : ''; + $timestamp = isset($_REQUEST['timestamp']) ? sanitize_text_field(wp_unslash($_REQUEST['timestamp'])) : ''; - // Convert to GMT, then to blog time. - $backup_time = (int) $updraft_last_backup['backup_time']; - $print_time = get_date_from_gmt( gmdate( 'Y-m-d H:i:s', $backup_time ), 'D, F j, Y H:i' ); + // You need a nonce before you can set job data. And we certainly don't yet have one. + $updraftplus->backup_time_nonce($timestamp); - if ( empty( $updraft_last_backup['backup_time_incremental'] ) ) { - $last_backup_text = '' . $print_time . ''; - } else { - $inc_time = get_date_from_gmt( gmdate( 'Y-m-d H:i:s', $updraft_last_backup['backup_time_incremental'] ), 'D, F j, Y H:i' ); - $last_backup_text = '$inc_time (" . sprintf( esc_html__( 'incremental backup; base backup: %s', 'updraftplus' ), $print_time ) . ')'; - } + $debug_mode = \UpdraftPlus_Options::get_updraft_option('updraft_debug_mode'); - $last_backup_text .= '
'; - - // Show errors + warnings. - if ( is_array( $updraft_last_backup['errors'] ) ) { - foreach ( $updraft_last_backup['errors'] as $err ) { - $level = ( is_array( $err ) ) ? $err['level'] : 'error'; - $message = ( is_array( $err ) ) ? $err['message'] : $err; - $last_backup_text .= ( 'warning' === $level ) ? '' : ''; - if ( 'warning' === $level ) { - $message = sprintf( esc_html__( 'Warning: %s', 'updraftplus' ), make_clickable( htmlspecialchars( $message ) ) ); - } else { - $message = htmlspecialchars( $message ); - } - $last_backup_text .= $message; - $last_backup_text .= ''; - $last_backup_text .= '
'; - } - } + // 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); - // Link log. - if ( ! empty( $updraft_last_backup['backup_nonce'] ) ) { - $updraft_dir = $updraftplus->backups_dir_location(); + // Retrieve the information from our backup history. + if (method_exists($updraftplus, 'get_backup_history_option')) { + $backup_history = $updraftplus->get_backup_history_option(); + } elseif (class_exists('\UpdraftPlus_Backup_History')) { + $backup_history = \UpdraftPlus_Backup_History::get_history(); + } - $potential_log_file = $updraft_dir . '/log.' . $updraft_last_backup['backup_nonce'] . '.txt'; - if ( is_readable( $potential_log_file ) ) { - $last_backup_text .= "" . esc_html__( 'Download log file', 'updraftplus' ) . ''; - } - } - } else { - $last_backup_text = '' . esc_html__( 'No backup has been completed.', 'updraftplus' ) . ''; - } + // Base name. + $file = $backup_history[$timestamp][$type]; - return array( - 'b' => $last_backup_text, - 'lasttime_gmt' => $backup_time, - ); + // Deal with multi-archive sets. + if (is_array($file)) { + $file = $file[$findex]; } - /** - * Get UpdraftPlus data. - * - * @param bool $with_hist Whether or not to build history. - * - * @uses UpdraftPlus() - * @uses UpdraftPlus_Options::get_updraft_option() - * @uses UpdraftPlus_Filesystem_Functions::really_is_writable() - * @uses \MainWP\Child\MainWP_Helper::instance()->check_methods() - * @uses \MainWP\Child\MainWP_Helper::MainWP_Helper::instance()->check_methods() - * @uses MainWP_Child_Updraft_Plus_Backups::build_historystatus() - * @uses MainWP_Child_Updraft_Plus_Backups::last_backup_html() - */ - private function get_updraft_data( $with_hist = true ) { //phpcs:ignore -- NOSONAR - complex. + // Where it should end up being downloaded to. + $fullpath = $updraftplus->backups_dir_location() . '/' . $file; - /** @global object $updraftplus UpdraftPlus object. */ - global $updraftplus; + if (isset($_POST['stage']) && '2' === $_POST['stage']) { + $updraftplus->spool_file($type, $fullpath); - if ( empty( $updraftplus ) && class_exists( '\UpdraftPlus' ) ) { - $updraftplus = new \UpdraftPlus(); - } + return array(); + } - if ( empty( $updraftplus ) ) { - return false; - } + if (isset($_POST['stage']) && 'delete' === $_POST['stage']) { + wp_delete_file($fullpath); + $updraftplus->log('The file has been deleted'); - // UNIX timestamp. - $next_scheduled_backup = wp_next_scheduled( 'updraft_backup' ); - $next_scheduled_backup_gmt = 0; - $next_scheduled_backup_database_gmt = 0; - 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' ); - } else { - $next_scheduled_backup = 'Nothing currently scheduled'; - } - - MainWP_Helper::instance()->check_classes_exists( array( '\UpdraftPlus_Options', '\UpdraftPlus_Filesystem_Functions' ) ); - MainWP_Helper::instance()->check_methods( '\UpdraftPlus_Options', 'get_updraft_option' ); - MainWP_Helper::instance()->check_methods( '\UpdraftPlus_Filesystem_Functions', 'really_is_writable' ); - MainWP_Helper::instance()->check_methods( $updraftplus, array( 'backups_dir_location' ) ); - - $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' ) ) { - $next_scheduled_backup_database = ( 'Nothing currently scheduled' === $next_scheduled_backup ) ? $next_scheduled_backup : esc_html__( 'At the same time as the files backup', 'updraftplus' ); - } 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' ); - } else { - $next_scheduled_backup_database = esc_html__( 'Nothing currently scheduled', 'updraftplus' ); - } - } - - $updraft_dir = $updraftplus->backups_dir_location(); - $backup_disabled = ( \UpdraftPlus_Filesystem_Functions::really_is_writable( $updraft_dir ) ) ? 0 : 1; - - $current_timegmt = time(); - $current_time = get_date_from_gmt( gmdate( 'Y-m-d H:i:s', $current_timegmt ), 'D, F j, Y H:i' ); - - $out = array( - 'updraft_backup_disabled' => $backup_disabled, - 'nextsched_files_gmt' => $next_scheduled_backup_gmt, - 'nextsched_database_gmt' => $next_scheduled_backup_gmt, - 'nextsched_current_timegmt' => $current_timegmt, - 'nextsched_current_timezone' => $current_time, - ); - - if ( $next_scheduled_backup_gmt ) { - $out['nextsched_files_timezone'] = $next_scheduled_backup; - } - - if ( $next_scheduled_backup_gmt ) { - $out['nextsched_database_timezone'] = $next_scheduled_backup_database; - } + return 'deleted'; + } - $bh = $this->build_historystatus(); + // Note that log() assumes that the data is in _POST, not _GET. + if ($debug_mode) { + $updraftplus->logfile_open($updraftplus->nonce); + } - // Fixed performance issue. - if ( $with_hist ) { - $out['updraft_historystatus'] = $bh['h']; - } + set_error_handler(array($updraftplus, 'php_error'), E_ALL & ~E_STRICT); // phpcs:ignore -- third party credits. - $out['updraft_count_backups'] = $bh['c']; + $updraftplus->log("Requested to obtain file: timestamp=$timestamp, type=$type, index=$findex"); - $last_backup = $this->last_backup_html(); - $out['updraft_lastbackup_html'] = $last_backup['b']; - $out['updraft_lastbackup_gmttime'] = $last_backup['lasttime_gmt']; + $itext = (empty($findex)) ? '' : $findex; + $known_size = isset($backup_history[$timestamp][$type . $itext . '-size']) ? $backup_history[$timestamp][$type . $itext . '-size'] : 0; - return $out; + $services = (isset($backup_history[$timestamp]['service'])) ? $backup_history[$timestamp]['service'] : false; + if (is_string($services)) { + $services = array($services); } - /** - * Get next scheduled backup. - * - * @return array $out Return Next scheduled backup data. - * @throws MainWP_Exception|MainWP_Exception Error message. - * - * @uses UpdraftPlus_Options::get_updraft_option() - * @uses \MainWP\Child\MainWP_Helper::instance()->check_classes_exists() - * @uses \MainWP\Child\MainWP_Helper::instance()->check_methods() - */ - private function next_scheduled_backups() { - - /** @global object $updraftplus UpdraftPlus object. */ - global $updraftplus; - - // UNIX timestamp. - $next_scheduled_backup = wp_next_scheduled( 'updraft_backup' ); - $next_scheduled_backup_gmt = 0; - $next_scheduled_backup_database_gmt = 0; - - 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' ); - } else { - $next_scheduled_backup = esc_html__( 'Nothing currently scheduled', 'updraftplus' ); - $files_not_scheduled = true; - } + $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) { + $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); + } - $next_scheduled_backup_database = wp_next_scheduled( 'updraft_backup_database' ); - if ( (int) \UpdraftPlus_Options::get_updraft_option( 'updraft_interval_database', \UpdraftPlus_Options::get_updraft_option( 'updraft_interval' ) ) === (int) \UpdraftPlus_Options::get_updraft_option( 'updraft_interval' ) ) { - if ( isset( $files_not_scheduled ) ) { - $next_scheduled_backup_database = $next_scheduled_backup; - } else { - $next_scheduled_backup_database = esc_html__( 'At the same time as the files backup', 'updraftplus' ); - } + // 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) { + $this->close_browser_connection(); + $is_downloaded = false; + add_action('http_request_args', array($updraftplus, 'modify_http_options')); + foreach ($services as $service) { + if ($is_downloaded) { + continue; + } + $download = $this->download_file($file, $service); + if (is_readable($fullpath) && false !== $download) { + clearstatcache(); + $updraftplus->log('Remote fetch was successful (file size: ' . round(filesize($fullpath) / 1024, 1) . ' Kb)'); + $is_downloaded = 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' ); - } else { - $next_scheduled_backup_database = esc_html__( 'Nothing currently scheduled', 'updraftplus' ); - } - } - - $current_timegmt = time(); - $current_time = get_date_from_gmt( gmdate( 'Y-m-d H:i:s', $current_timegmt ), 'D, F j, Y H:i' ); - - $html = ' - - - - - - - - - - - - - - -
' . esc_html__( 'Files', 'mainwp-updraftplus-extension' ) . '' . esc_html__( 'Database', 'mainwp-updraftplus-extension' ) . '' . esc_html__( 'Time now', 'mainwp-updraftplus-extension' ) . '
' . $next_scheduled_backup . '' . $next_scheduled_backup_database . '' . $current_time . '
'; - - MainWP_Helper::instance()->check_classes_exists( array( '\UpdraftPlus_Filesystem_Functions' ) ); - MainWP_Helper::instance()->check_methods( '\UpdraftPlus_Filesystem_Functions', 'really_is_writable' ); - - $updraft_dir = $updraftplus->backups_dir_location(); - $backup_disabled = ( \UpdraftPlus_Filesystem_Functions::really_is_writable( $updraft_dir ) ) ? 0 : 1; + clearstatcache(); + if (0 === filesize($fullpath)) { + wp_delete_file($fullpath); + } + $updraftplus->log('Remote fetch failed'); + } + } + remove_action('http_request_args', array($updraftplus, 'modify_http_options')); + } - $out = array( - 'n' => $html, - 'updraft_backup_disabled' => $backup_disabled, - 'nextsched_files_gmt' => $next_scheduled_backup_gmt, - 'nextsched_database_gmt' => $next_scheduled_backup_database_gmt, - 'nextsched_current_timegmt' => $current_timegmt, - 'nextsched_current_timezone' => $current_time, - ); + // Now, spool the thing to the browser. + if (is_file($fullpath) && is_readable($fullpath)) { - if ( $next_scheduled_backup_gmt ) { - $out['nextsched_files_timezone'] = $next_scheduled_backup; - } + // That message is then picked up by the AJAX listener. + $updraftplus->jobdata_set('dlfile_' . $timestamp . '_' . $type . '_' . $findex, 'downloaded:' . filesize($fullpath) . ":$fullpath"); + } 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.'); + } - if ( $next_scheduled_backup_database_gmt ) { - $out['nextsched_database_timezone'] = $next_scheduled_backup_database; - } + restore_error_handler(); - return $out; + fclose($updraftplus->logfile_handle); + if (! $debug_mode) { + wp_delete_file($updraftplus->logfile_name); } - /** - * Delete backup set. - * - * @return array Return results array. - * @throws MainWP_Exception - * - * @uses $updraftplus::get_backup_history() - * @uses $updraftplus::backup_time_nonce() - * @uses $updraftplus::jobdata_set() - * @uses $updraftplus::logfile_open() - * @uses $updraftplus::backups_dir_location() - * @uses $updraftplus::get_backupable_file_entities() - * @uses UpdraftPlus_Options::get_updraft_option() - * @uses UpdraftPlus_Options::update_updraft_option() - * @uses UpdraftPlus_Backup_History::get_history() - * @uses MainWP_Child_Updraft_Plus_Backups::build_historystatus() - */ - private function deleteset() { //phpcs:ignore -- NOSONAR - complex. + return array('result' => 'OK'); + } + + /** + * Usage requirements: Pass only a single service, as a string, into this function. + * + * @param string $file File to download. + * @param string $service UpdraftPlus service method. + * + * @return bool Return file or FALSE on failure. + * + * @uses $updraftplus::log() + * @uses $remote_obj::download() + */ + private function download_file($file, $service) + { + + /** @global object $updraftplus UpdraftPlus object. */ + global $updraftplus; + + set_time_limit(900); + + $updraftplus->log("Requested file from remote service: $service: $file"); + + $method_include = UPDRAFTPLUS_DIR . '/methods/' . $service . '.php'; + if (file_exists($method_include)) { + require_once $method_include; // NOSONAR - WP compatible. + } - /** @global object $updraftplus UpdraftPlus object. */ - global $updraftplus; + $objname = "UpdraftPlus_BackupModule_{$service}"; + if (method_exists($objname, 'download')) { + $remote_obj = new $objname(); - if ( method_exists( $updraftplus, 'get_backup_history' ) ) { - $backups = $updraftplus->get_backup_history(); - } elseif ( class_exists( '\UpdraftPlus_Backup_History' ) ) { - $backups = \UpdraftPlus_Backup_History::get_history(); - } - $timestamp = isset( $_POST['backup_timestamp'] ) ? sanitize_text_field( wp_unslash( $_POST['backup_timestamp'] ) ) : ''; - if ( ! isset( $backups[ $timestamp ] ) ) { - $bh = $this->build_historystatus(); - - return array( - 'result' => 'error', - 'message' => esc_html__( 'Backup set not found', 'updraftplus' ), - 'updraft_historystatus' => $bh['h'], - 'updraft_count_backups' => $bh['c'], - ); - } + return $remote_obj->download($file); + } else { + $updraftplus->log("Automatic backup restoration is not available with the method: $service."); + $updraftplus->log("$file: " . sprintf(esc_html__("The backup archive for this file could not be found. The remote storage method in use (%s) does not allow us to retrieve files. To perform any restoration using UpdraftPlus, you will need to obtain a copy of this file and place it inside UpdraftPlus's working folder", 'updraftplus'), $service) . ' (' . $this->prune_updraft_dir_prefix($updraftplus->backups_dir_location()) . ')', 'error'); - // 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 ); + return false; + } + } + + /** + * Prune Updraft directory prefix + * + * This options filter removes ABSPATH off the front of $updraft_dir, if it is given absolutely and contained within it. + * + * @param $updraft_dir Directory to prune. + * @return false|string Return Pruned $updraft_dir or FALSE on failure. + */ + public function prune_updraft_dir_prefix($updraft_dir) + { + if ('/' === substr($updraft_dir, 0, 1) || '\\' === substr($updraft_dir, 0, 1) || preg_match('/^[a-zA-Z]:/', $updraft_dir)) { + $wcd = trailingslashit(WP_CONTENT_DIR); + if (strpos($updraft_dir, $wcd) === 0) { + $updraft_dir = substr($updraft_dir, strlen($wcd)); + } + } - 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 ); // phpcs:ignore -- third party credits. - } + return $updraft_dir; + } + + /** + * Restore all downloaded backups from history. + * + * @return array Return response array. + * + * @uses UpdraftPlus_Backup_History::get_history() + * @uses $updraftplus::get_backup_history() + * @uses $updraftplus::backups_dir_location() + * @uses $updraftplus::analyse_db_file( + * @uses $updraftplus::get_backupable_file_entities( + * @uses MainWP_Child_Updraft_Plus_Backups::analyse_db_file_old() + */ + public function restore_alldownloaded() { //phpcs:ignore -- NOSONAR - complex. + + /** @global object $updraftplus UpdraftPlus object. */ + global $updraftplus; + + if (method_exists($updraftplus, 'get_backup_history')) { + $backups = $updraftplus->get_backup_history(); + } elseif (class_exists('\UpdraftPlus_Backup_History')) { + $backups = \UpdraftPlus_Backup_History::get_history(); + } - $updraft_dir = $updraftplus->backups_dir_location(); - $backupable_entities = $updraftplus->get_backupable_file_entities( true, true ); + $updraft_dir = $updraftplus->backups_dir_location(); - $nonce = isset( $backups[ $timestamp ]['nonce'] ) ? $backups[ $timestamp ]['nonce'] : ''; + $timestamp = (int) $_POST['timestamp']; + if (! isset($backups[$timestamp])) { + return array( + 'm' => '', + 'w' => '', + 'e' => esc_html__('No such backup set exists', 'updraftplus'), + ); + } - $delete_from_service = array(); + $mess = array(); + parse_str($_POST['restoreopts'], $res); + if (isset($res['updraft_restore'])) { + set_error_handler(array($this, 'get_php_errors'), E_ALL & ~E_STRICT); // phpcs:ignore -- third party credits. - if ( isset( $_POST['delete_remote'] ) && 1 === (int) $_POST['delete_remote'] && isset( $backups[ $timestamp ]['service'] ) ) { - $services = is_string( $backups[ $timestamp ]['service'] ) ? array( $backups[ $timestamp ]['service'] ) : $backups[ $timestamp ]['service']; - if ( is_array( $services ) ) { - foreach ( $services as $service ) { - if ( 'none' !== $service ) { - $delete_from_service[] = $service; - } - } - } - } + $elements = array_flip($res['updraft_restore']); - $files_to_delete = array(); - foreach ( $backupable_entities as $key => $ent ) { - if ( isset( $backups[ $timestamp ][ $key ] ) ) { - $files_to_delete[ $key ] = $backups[ $timestamp ][ $key ]; - } - } - // Delete DB. - if ( isset( $backups[ $timestamp ]['db'] ) ) { - $files_to_delete['db'] = $backups[ $timestamp ]['db']; - } + $warn = array(); + $err = array(); - // Also delete the log. - if ( $nonce && ! \UpdraftPlus_Options::get_updraft_option( 'updraft_debug_mode' ) ) { - $files_to_delete['log'] = "log.$nonce.txt"; - } + set_time_limit(900); + $max_execution_time = (int) ini_get('max_execution_time'); - unset( $backups[ $timestamp ] ); - \UpdraftPlus_Options::update_updraft_option( 'updraft_backup_history', $backups ); + if ($max_execution_time > 0 && $max_execution_time < 61) { + $warn[] = sprintf(esc_html__('The PHP setup on this webserver allows only %s seconds for PHP to run, and does not allow this limit to be raised. If you have a lot of data to import, and if the restore operation times out, then you will need to ask your web hosting company for ways to raise this limit (or attempt the restoration piece-by-piece).', 'updraftplus'), $max_execution_time); + } - $message = ''; + if (isset($backups[$timestamp]['native']) && false === $backups[$timestamp]['native']) { + $warn[] = esc_html__('This backup set was not known by UpdraftPlus to be created by the current WordPress installation, but was found in remote storage.', 'updraftplus') . ' ' . esc_html__('You should make sure that this really is a backup set intended for use on this website, before you restore (rather than a backup set of an unrelated website that was using the same storage location).', 'updraftplus'); + } - $local_deleted = 0; - $remote_deleted = 0; - add_action( 'http_request_args', array( $updraftplus, 'modify_http_options' ) ); - foreach ( $files_to_delete as $key => $files ) { - // Local deletion. - if ( is_string( $files ) ) { - $files = array( $files ); - } - foreach ( $files as $file ) { - if ( is_file( $updraft_dir . '/' . $file ) && wp_delete_file( $updraft_dir . '/' . $file ) ) { - $local_deleted ++; - } - } - if ( 'log' !== $key && ! empty( $delete_from_service ) ) { - foreach ( $delete_from_service as $service ) { - if ( 'email' === $service ) { - continue; - } - if ( file_exists( UPDRAFTPLUS_DIR . "/methods/$service.php" ) ) { - require_once UPDRAFTPLUS_DIR . "/methods/$service.php"; // NOSONAR - WP compatible. - } - $objname = '\UpdraftPlus_BackupModule_' . $service; - $deleted = - 1; - if ( class_exists( $objname ) ) { - $remote_obj = new $objname(); - $deleted = $remote_obj->delete( $files ); - } - - if ( -1 !== $deleted && false !== $deleted ) { - $remote_deleted = $remote_deleted + count( $files ); - } - } + if (isset($elements['db'])) { + // Analyse the header of the database file + display results. + if (class_exists('\UpdraftPlus_Encryption')) { + list($mess2, $warn2, $err2, $info) = $updraftplus->analyse_db_file($timestamp, $res); + } else { + list($mess2, $warn2, $err2, $info) = $this->analyse_db_file_old($timestamp, $res); + } + $mess = array_merge($mess, $mess2); + $warn = array_merge($warn, $warn2); + $err = array_merge($err, $err2); + foreach ($backups[$timestamp] as $bid => $bval) { + if ('db' !== $bid && 'db' === substr($bid, 0, 2) && '-size' !== substr($bid, -5, 5)) { + $warn[] = esc_html__('Only the WordPress database can be restored; you will need to deal with the external database manually.', 'updraftplus'); + break; + } + } + } + + $backupable_entities = $updraftplus->get_backupable_file_entities(true, true); + $backupable_plus_db = $backupable_entities; + $backupable_plus_db['db'] = array( + 'path' => 'path-unused', + 'description' => esc_html__('Database', 'updraftplus'), + ); + + if (! empty($backups[$timestamp]['meta_foreign'])) { + $foreign_known = apply_filters('updraftplus_accept_archivename', array()); + if (! is_array($foreign_known) || empty($foreign_known[$backups[$timestamp]['meta_foreign']])) { + $err[] = sprintf(esc_html__('Backup created by unknown source (%s) - cannot be restored.', 'updraftplus'), $backups[$timestamp]['meta_foreign']); + } else { + // For some reason, on PHP 5.5 passing by reference in a single array stopped working with apply_filters_ref_array (though not with do_action_ref_array). + $backupable_plus_db = apply_filters_ref_array( + 'updraftplus_importforeign_backupable_plus_db', + array( + $backupable_plus_db, + array( + $foreign_known[$backups[$timestamp]['meta_foreign']], + &$mess, + &$warn, + &$err, + ), + ) + ); + } + } + + foreach ($backupable_plus_db as $type => $info) { + if (! isset($elements[$type])) { + continue; + } + $whatwegot = $backups[$timestamp][$type]; + if (is_string($whatwegot)) { + $whatwegot = array($whatwegot); + } + $expected_index = 0; + $missing = ''; + ksort($whatwegot); + $outof = false; + foreach ($whatwegot as $index => $file) { + if (preg_match('/\d+of(\d+)\.zip/', $file, $omatch)) { + $outof = max($matches[1], 1); + } + if ($index !== $expected_index) { + $missing .= ('' === $missing) ? (1 + $expected_index) : ',' . (1 + $expected_index); + } + if (! file_exists($updraft_dir . '/' . $file)) { + $err[] = sprintf(esc_html__('File not found (you need to upload it): %s', 'updraftplus'), $updraft_dir . '/' . $file); + } elseif (0 === filesize($updraft_dir . '/' . $file)) { + $err[] = sprintf(esc_html__('File was found, but is zero-sized (you need to re-upload it): %s', 'updraftplus'), $file); + } else { + $itext = (0 === $index) ? '' : $index; + if (! empty($backups[$timestamp][$type . $itext . '-size']) && filesize($updraft_dir . '/' . $file) !== $backups[$timestamp][$type . $itext . '-size'] && empty($warn['doublecompressfixed'])) { + $warn[] = sprintf(esc_html__('File (%1$s) was found, but has a different size (%2$s) from what was expected (%3$s) - it may be corrupt.', 'updraftplus'), $file, filesize($updraft_dir . '/' . $file), $backups[$timestamp][$type . $itext . '-size']); } - } - remove_action( 'http_request_args', array( $updraftplus, 'modify_http_options' ) ); - $message .= esc_html__( 'The backup set has been removed.', 'updraftplus' ) . "\n"; - $message .= sprintf( esc_html__( 'Local archives deleted: %d', 'updraftplus' ), $local_deleted ) . "\n"; - $message .= sprintf( esc_html__( 'Remote archives deleted: %d', 'updraftplus' ), $remote_deleted ) . "\n"; + do_action_ref_array( + "updraftplus_checkzip_$type", + array( + $updraft_dir . '/' . $file, + &$mess, + &$warn, + &$err, + ) + ); + } + $expected_index++; + } + do_action_ref_array("updraftplus_checkzip_end_$type", array(&$mess, &$warn, &$err)); + // Detect missing archives where they are missing from the end of the set. + if ($outof > 0 && $expected_index < $outof) { + for ($j = $expected_index; $j < $outof; $j++) { + $missing .= ('' === $missing) ? (1 + $j) : ',' . (1 + $j); + } + } + if ('' !== $missing) { + $warn[] = sprintf(esc_html__('This multi-archive backup set appears to have the following archives missing: %s', 'updraftplus'), $missing . ' (' . $info['description'] . ')'); + } + } + + if (empty($err) && empty($warn)) { + $mess_first = esc_html__('The backup archive files have been successfully processed. Now press Restore again to proceed.', 'updraftplus'); + } elseif (empty($err)) { + $mess_first = esc_html__('The backup archive files have been processed, but with some warnings. If all is well, then now press Restore again to proceed. Otherwise, cancel and correct any problems first.', 'updraftplus'); + } else { + $mess_first = esc_html__('The backup archive files have been processed, but with some errors. You will need to cancel and correct any problems before retrying.', 'updraftplus'); + } + + if (count($this->logged) > 0) { + foreach ($this->logged as $lwarn) { + $warn[] = $lwarn; + } + } + restore_error_handler(); + + return array( + 'm' => '

' . $mess_first . '

' . implode('
', $mess), + 'w' => implode('
', $warn), + 'e' => implode('
', $err), + ); + } + } + + /** + * Delete old directories. + * + * @param bool $show_return Whether or not to show removed directories in the output. Defualt: true. + * + * @return array Return response array. + */ + private function delete_old_dirs_go($show_return = true) + { + ob_start(); + + echo ($show_return) ? '

UpdraftPlus - ' . esc_html__('Remove old directories', 'updraftplus') . '

' : '

' . esc_html__('Remove old directories', 'updraftplus') . '

'; + $deleted = 0; + if ($this->delete_old_dirs()) { + echo '

' . esc_html__('Old directories successfully removed.', 'updraftplus') . '

'; + echo '

' . esc_html__('Now press Restore again to proceed.', 'updraftplus') . '


'; + $deleted = 1; + } else { + echo '

', esc_html__('Old directory removal failed for some reason. You may want to do this manually.', 'updraftplus') . '


'; + } - $updraftplus->log( 'Local archives deleted: ' . $local_deleted ); - $updraftplus->log( 'Remote archives deleted: ' . $remote_deleted ); + $output = ob_get_clean(); - if ( \UpdraftPlus_Options::get_updraft_option( 'updraft_debug_mode' ) ) { - restore_error_handler(); - } + return array( + 'o' => $output, + 'd' => $deleted, + ); + } - $bh = $this->build_historystatus(); - - return array( - 'result' => 'success', - 'message' => $message, - 'updraft_historystatus' => $bh['h'], - 'updraft_count_backups' => $bh['c'], - ); - } + /** + * Delete the old directories that are created when a backup is restored. + * + * @return bool Return $ret, $ret3, $ret4. TRUE|FALSE. + */ + private function delete_old_dirs() + { /** - * Build backup history status. - * - * @return array Return response array. - * @throws MainWP_Exception Error message. - * - * @uses \MainWP\Child\MainWP_Helper::instance()->check_classes_exists() - * @uses \MainWP\Child\MainWP_Helper::instance()->check_methods() - * @uses \MainWP\Child\MainWP_Child_Updraft_Plus_Backups::existing_backup_table() - * @uses \UpdraftPlus_Backup_History::get_history() + * @global object $wp_filesystem WordPress filesystem. + * @global object $updraftplus UpdraftPlus object. */ - public function build_historystatus() { - MainWP_Helper::instance()->check_classes_exists( '\UpdraftPlus_Backup_History' ); - MainWP_Helper::instance()->check_methods( '\UpdraftPlus_Backup_History', 'get_history' ); - $backup_history = \UpdraftPlus_Backup_History::get_history(); - $output = $this->existing_backup_table( $backup_history ); - return array( - 'h' => $output, - 'c' => count( $backup_history ), - ); + global $wp_filesystem, $updraftplus; + + $credentials = request_filesystem_credentials(wp_nonce_url(\UpdraftPlus_Options::admin_page_url() . '?page=updraftplus&action=updraft_delete_old_dirs', 'updraftplus-credentialtest-nonce')); + WP_Filesystem($credentials); + if ($wp_filesystem->errors->get_error_code()) { + foreach ($wp_filesystem->errors->get_error_messages() as $message) { + show_message($message); + } + exit; } - /** - * Display History Status. - * - * @param null $remotescan Remote Scan $_POST data. Default: null - * @param null $rescan Rescan $_POST data. Default: null - * - * @return array Return history status data. - * @throws MainWP_Exception - * - * @uses MainWP_Child_Updraft_Plus_Backups::rebuild_backup_history() - * @uses MainWP_Child_Updraft_Plus_Backups::existing_backup_table() - * @uses UpdraftPlus_Backup_History::get_history() - */ - public function historystatus( $remotescan = null, $rescan = null ) { - - /** @global object $updraftplus UpdraftPlus object. */ - global $updraftplus; - - $remotescan = ( null !== $remotescan ) ? $remotescan : $_POST['remotescan']; - $rescan = ( null !== $rescan ) ? $rescan : $_POST['rescan']; + $ret = $this->delete_old_dirs_dir($wp_filesystem->wp_content_dir()); - if ( $rescan ) { - $messages = $this->rebuild_backup_history( $remotescan ); - } + $updraft_dir = $updraftplus->backups_dir_location(); + if ($updraft_dir) { + $ret4 = ($updraft_dir) ? $this->delete_old_dirs_dir($updraft_dir, false) : true; + } else { + $ret4 = true; + } - $backup_history = \UpdraftPlus_Backup_History::get_history(); - $output = $this->existing_backup_table( $backup_history ); + $plugs = untrailingslashit($wp_filesystem->wp_plugins_dir()); + if ($wp_filesystem->is_dir($plugs . '-old')) { + print '' . esc_html__('Delete', 'updraftplus') . ': plugins-old: '; + if (! $wp_filesystem->delete($plugs . '-old', true)) { + $ret3 = false; + print '' . esc_html__('Failed', 'updraftplus') . '
'; + } else { + $ret3 = true; + print '' . esc_html__('OK', 'updraftplus') . '
'; + } + } else { + $ret3 = true; + } - if ( ! empty( $messages ) && is_array( $messages ) ) { - $noutput = '
    '; - foreach ( $messages as $msg ) { - $noutput .= '
  • ' . ( ( $msg['desc'] ) ? $msg['desc'] . ': ' : '' ) . '' . $msg['message'] . '
  • '; - } - $noutput .= '
'; - $output = $noutput . $output; - } + return $ret && $ret3 && $ret4; + } - return array( - 'n' => sprintf( esc_html__( 'Existing Backups', 'updraftplus' ) . ' (%d)', count( $backup_history ) ), - 't' => $output, - 'c' => count( $backup_history ), - 'm' => $updraftplus->detect_safe_mode(), - ); - } + /** + * Delete the directories within a directory. + * + * @param string $dir Directory to scan. + * @param bool $wpfs Whether or not to use Wordpress filesystem to list directories, Default: true. + * @return bool|string $ret Return FALSE & echo 'Failed' on failure or echo 'OK' on success. + */ + private function delete_old_dirs_dir($dir, $wpfs = true) { //phpcs:ignore -- NOSONAR - complex. + $dir = trailingslashit($dir); /** - * UpdraftPlus Download Backup. - * - * @return array|string|string[] Return response array. - * - * @uses $updraftplus::get_backupable_file_entities() - * @uses $updraftplus::backup_time_nonce() - * @uses $updraftplus::jobdata_set() - * @uses $updraftplus::get_backup_history() - * @uses $updraftplus::backups_dir_location() - * @uses $updraftplus::spool_file() - * @uses $updraftplus::log() - * @uses $updraftplus::logfile_open() - * @uses $updraftplus::logfile_name() - * @uses $updraftplus::logfile_handle() - * @uses UpdraftPlus_Options::get_updraft_option() - * @uses UpdraftPlus_Backup_History::get_history() - * @uses MainWP_Child_Updraft_Plus_Backups::close_browser_connection() - * @uses MainWP_Child_Updraft_Plus_Backups::download_file() + * @global object $wp_filesystem WordPress filesystem. + * @global object $updraftplus UpdraftPlus object. */ - public function updraft_download_backup() { //phpcs:ignore -- NOSONAR - complex. - - set_time_limit( 900 ); - - /** @global object $updraftplus UpdraftPlus object. */ - global $updraftplus; - - if ( ! isset( $_REQUEST['timestamp'] ) || ! is_numeric( $_REQUEST['timestamp'] ) || ! isset( $_REQUEST['type'] ) ) { - exit; - } - - $findex = ( isset( $_REQUEST['findex'] ) ) ? $_REQUEST['findex'] : 0; - if ( empty( $findex ) ) { - $findex = 0; - } + global $wp_filesystem, $updraftplus; - $backupable_entities = $updraftplus->get_backupable_file_entities( true ); - $type_match = false; - foreach ( $backupable_entities as $type => $info ) { - if ( $_REQUEST['type'] === $type ) { - $type_match = true; - } - } - - if ( ! $type_match && 'db' !== substr( $_REQUEST['type'], 0, 2 ) ) { - exit; - } - - // Get the information on what is wanted. - $type = isset( $_REQUEST['type'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['type'] ) ) : ''; - $timestamp = isset( $_REQUEST['timestamp'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['timestamp'] ) ) : ''; - - // You need a nonce before you can set job data. And we certainly don't yet have one. - $updraftplus->backup_time_nonce( $timestamp ); - - $debug_mode = \UpdraftPlus_Options::get_updraft_option( 'updraft_debug_mode' ); - - // 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 ); - - // Retrieve the information from our backup history. - if ( method_exists( $updraftplus, 'get_backup_history' ) ) { - $backup_history = $updraftplus->get_backup_history(); - } elseif ( class_exists( '\UpdraftPlus_Backup_History' ) ) { - $backup_history = \UpdraftPlus_Backup_History::get_history(); - } - - // Base name. - $file = $backup_history[ $timestamp ][ $type ]; - - // Deal with multi-archive sets. - if ( is_array( $file ) ) { - $file = $file[ $findex ]; - } - - // Where it should end up being downloaded to. - $fullpath = $updraftplus->backups_dir_location() . '/' . $file; - - if ( isset( $_POST['stage'] ) && '2' === $_POST['stage'] ) { - $updraftplus->spool_file( $type, $fullpath ); - - return array(); - } - - if ( isset( $_POST['stage'] ) && 'delete' === $_POST['stage'] ) { - wp_delete_file( $fullpath ); - $updraftplus->log( 'The file has been deleted' ); - - return 'deleted'; - } - - // Note that log() assumes that the data is in _POST, not _GET. - if ( $debug_mode ) { - $updraftplus->logfile_open( $updraftplus->nonce ); - } - - set_error_handler( array( $updraftplus, 'php_error' ), E_ALL & ~E_STRICT ); // phpcs:ignore -- third party credits. - - $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 ) { - $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 ) { - $this->close_browser_connection(); - $is_downloaded = false; - add_action( 'http_request_args', array( $updraftplus, 'modify_http_options' ) ); - foreach ( $services as $service ) { - if ( $is_downloaded ) { - continue; - } - $download = $this->download_file( $file, $service ); - if ( is_readable( $fullpath ) && false !== $download ) { - clearstatcache(); - $updraftplus->log( 'Remote fetch was successful (file size: ' . round( filesize( $fullpath ) / 1024, 1 ) . ' Kb)' ); - $is_downloaded = true; - } else { - clearstatcache(); - if ( 0 === filesize( $fullpath ) ) { - wp_delete_file( $fullpath ); - } - $updraftplus->log( 'Remote fetch failed' ); - } - } - remove_action( 'http_request_args', array( $updraftplus, 'modify_http_options' ) ); - } - - // Now, 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" ); + if ($wpfs) { + $list = $wp_filesystem->dirlist($dir); + } else { + $list = scandir($dir); + } + if (! is_array($list)) { + return false; + } + $ret = true; + foreach ($list as $item) { + $name = (is_array($item)) ? $item['name'] : $item; + if ('-old' === substr($name, -4, 4)) { + // recursively delete. + print '' . esc_html__('Delete', 'updraftplus') . ': ' . htmlspecialchars($name) . ': '; + + if ($wpfs) { + if (! $wp_filesystem->delete($dir . $name, true)) { + $ret = false; + echo '' . esc_html__('Failed', 'updraftplus') . '
'; + } else { + echo '' . esc_html__('OK', 'updraftplus') . '
'; + } } 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.' ); - } - - restore_error_handler(); - - fclose( $updraftplus->logfile_handle ); - if ( ! $debug_mode ) { - wp_delete_file( $updraftplus->logfile_name ); - } - - return array( 'result' => 'OK' ); + if ($updraftplus->remove_local_directory($dir . $name)) { + echo '' . esc_html__('OK', 'updraftplus') . '
'; + } else { + $ret = false; + echo '' . esc_html__('Failed', 'updraftplus') . '
'; + } + } + } } + return $ret; + } + + + /** + * Show admin warning. + * + * @param string $message Warning message to display. + * @param string $class UpdraftPlus message CSS class. + */ + public function show_admin_warning($message, $class = 'updated') + { + echo '
' . "

$message

"; + } + + /** + * Analyse old database file. + * + * @param string $timestamp File timestamp. + * @param string $res UpdraftPlus response. + * @param bool $db_file Whether or not a DB file was found. Default: false. + * @param bool $header_only Whether or not file only has headers. Default: false. + * + * @return array[] Return array( $mess, $warn, $err, $info ). + * + * @uses $updraftplus::backups_dir_location() + * @uses $updraftplus::get_max_packet_size() + * @uses $updraftplus::get_backup_history() + * @uses $updraftplus::is_db_encrypted() + * @uses $updraftplus::decrypt() + * @uses UpdraftPlus_Options::get_updraft_option() + * @uses UpdraftPlus_Backup_History::get_history() + * @uses MainWP_Child_Updraft_Plus_Backups::gzopen_for_read() + */ + private function analyse_db_file_old($timestamp, $res, $db_file = false, $header_only = false) { //phpcs:ignore -- NOSONAR - complex. + + $mess = array(); + $warn = array(); + $err = array(); + $info = array(); + /** - * Usage requirements: Pass only a single service, as a string, into this function. - * - * @param string $file File to download. - * @param string $service UpdraftPlus service method. - * - * @return bool Return file or FALSE on failure. - * - * @uses $updraftplus::log() - * @uses $remote_obj::download() + * @global object $wp_filesystem WordPress filesystem. + * @global object $updraftplus UpdraftPlus object. */ - private function download_file( $file, $service ) { + global $updraftplus, $wp_version; - /** @global object $updraftplus UpdraftPlus object. */ - global $updraftplus; + include_once ABSPATH . WPINC . '/version.php'; // NOSONAR - WP compatible. - set_time_limit( 900 ); + $updraft_dir = $updraftplus->backups_dir_location(); - $updraftplus->log( "Requested file from remote service: $service: $file" ); + if (false === $db_file) { + // This attempts to raise the maximum packet size. This can't be done within the session, only globally. Therefore, it has to be done before the session starts; in our case, during the pre-analysis. + $updraftplus->get_max_packet_size(); - $method_include = UPDRAFTPLUS_DIR . '/methods/' . $service . '.php'; - if ( file_exists( $method_include ) ) { - require_once $method_include; // NOSONAR - WP compatible. - } + if (method_exists($updraftplus, 'get_backup_history')) { + $backup = $updraftplus->get_backup_history($timestamp); + } elseif (class_exists('\UpdraftPlus_Backup_History')) { + $backup = \UpdraftPlus_Backup_History::get_history($timestamp); + } - $objname = "UpdraftPlus_BackupModule_{$service}"; - if ( method_exists( $objname, 'download' ) ) { - $remote_obj = new $objname(); - - return $remote_obj->download( $file ); - } else { - $updraftplus->log( "Automatic backup restoration is not available with the method: $service." ); - $updraftplus->log( "$file: " . sprintf( esc_html__( "The backup archive for this file could not be found. The remote storage method in use (%s) does not allow us to retrieve files. To perform any restoration using UpdraftPlus, you will need to obtain a copy of this file and place it inside UpdraftPlus's working folder", 'updraftplus' ), $service ) . ' (' . $this->prune_updraft_dir_prefix( $updraftplus->backups_dir_location() ) . ')', 'error' ); + if (! isset($backup['nonce']) || ! isset($backup['db'])) { + return array($mess, $warn, $err, $info); + } - return false; - } + $db_file = (is_string($backup['db'])) ? $updraft_dir . '/' . $backup['db'] : $updraft_dir . '/' . $backup['db'][0]; } - /** - * Prune Updraft directory prefix - * - * This options filter removes ABSPATH off the front of $updraft_dir, if it is given absolutely and contained within it. - * - * @param $updraft_dir Directory to prune. - * @return false|string Return Pruned $updraft_dir or FALSE on failure. - */ - public function prune_updraft_dir_prefix( $updraft_dir ) { - if ( '/' === substr( $updraft_dir, 0, 1 ) || '\\' === substr( $updraft_dir, 0, 1 ) || preg_match( '/^[a-zA-Z]:/', $updraft_dir ) ) { - $wcd = trailingslashit( WP_CONTENT_DIR ); - if ( strpos( $updraft_dir, $wcd ) === 0 ) { - $updraft_dir = substr( $updraft_dir, strlen( $wcd ) ); - } - } - - return $updraft_dir; + if (! is_readable($db_file)) { + return array($mess, $warn, $err, $info); } - /** - * Restore all downloaded backups from history. - * - * @return array Return response array. - * - * @uses UpdraftPlus_Backup_History::get_history() - * @uses $updraftplus::get_backup_history() - * @uses $updraftplus::backups_dir_location() - * @uses $updraftplus::analyse_db_file( - * @uses $updraftplus::get_backupable_file_entities( - * @uses MainWP_Child_Updraft_Plus_Backups::analyse_db_file_old() - */ - public function restore_alldownloaded() { //phpcs:ignore -- NOSONAR - complex. + // Encrypted - decrypt it. + if ($updraftplus->is_db_encrypted($db_file)) { - /** @global object $updraftplus UpdraftPlus object. */ - global $updraftplus; + $encryption = empty($res['updraft_encryptionphrase']) ? \UpdraftPlus_Options::get_updraft_option('updraft_encryptionphrase') : $res['updraft_encryptionphrase']; - if ( method_exists( $updraftplus, 'get_backup_history' ) ) { - $backups = $updraftplus->get_backup_history(); - } elseif ( class_exists( '\UpdraftPlus_Backup_History' ) ) { - $backups = \UpdraftPlus_Backup_History::get_history(); + if (! $encryption) { + if (class_exists('\UpdraftPlus_Addon_MoreDatabase')) { + $err[] = sprintf(esc_html__('Error: %s', 'updraftplus'), esc_html__('Decryption failed. The database file is encrypted, but you have no encryption key entered.', 'updraftplus')); + } else { + $err[] = sprintf(esc_html__('Error: %s', 'updraftplus'), esc_html__('Decryption failed. The database file is encrypted.', 'updraftplus')); } - $updraft_dir = $updraftplus->backups_dir_location(); + return array($mess, $warn, $err, $info); + } - $timestamp = (int) $_POST['timestamp']; - if ( ! isset( $backups[ $timestamp ] ) ) { - return array( - 'm' => '', - 'w' => '', - 'e' => esc_html__( 'No such backup set exists', 'updraftplus' ), - ); + $ciphertext = $updraftplus->decrypt($db_file, $encryption); + + if ($ciphertext) { + $new_db_file = $updraft_dir . '/' . basename($db_file, '.crypt'); + if (! file_put_contents($new_db_file, $ciphertext)) { //phpcs:ignore WordPress.WP.AlternativeFunctions + $err[] = esc_html__('Failed to write out the decrypted database to the filesystem.', 'updraftplus'); + return array($mess, $warn, $err, $info); } + $db_file = $new_db_file; + } else { + $err[] = esc_html__('Decryption failed. The most likely cause is that you used the wrong key.', 'updraftplus'); - $mess = array(); - parse_str( $_POST['restoreopts'], $res ); - if ( isset( $res['updraft_restore'] ) ) { - set_error_handler( array( $this, 'get_php_errors' ), E_ALL & ~E_STRICT ); // phpcs:ignore -- third party credits. + return array($mess, $warn, $err, $info); + } + } - $elements = array_flip( $res['updraft_restore'] ); + // Even the empty schema when gzipped comes to 1565 bytes; a blank WP 3.6 install at 5158. But we go low, in case someone wants to share single tables. + if (filesize($db_file) < 1000) { + $err[] = sprintf(esc_html__('The database is too small to be a valid WordPress database (size: %s Kb).', 'updraftplus'), round(filesize($db_file) / 1024, 1)); - $warn = array(); - $err = array(); + return array($mess, $warn, $err, $info); + } - set_time_limit( 900 ); - $max_execution_time = (int) ini_get( 'max_execution_time' ); + $is_plain = ('.gz' === substr($db_file, -3, 3)) ? false : true; - if ( $max_execution_time > 0 && $max_execution_time < 61 ) { - $warn[] = sprintf( esc_html__( 'The PHP setup on this webserver allows only %s seconds for PHP to run, and does not allow this limit to be raised. If you have a lot of data to import, and if the restore operation times out, then you will need to ask your web hosting company for ways to raise this limit (or attempt the restoration piece-by-piece).', 'updraftplus' ), $max_execution_time ); - } + $dbhandle = ($is_plain) ? fopen($db_file, 'r') : $this->gzopen_for_read($db_file, $warn, $err); + if (! is_resource($dbhandle)) { + $err[] = esc_html__('Failed to open database file.', 'updraftplus'); - if ( isset( $backups[ $timestamp ]['native'] ) && false === $backups[ $timestamp ]['native'] ) { - $warn[] = esc_html__( 'This backup set was not known by UpdraftPlus to be created by the current WordPress installation, but was found in remote storage.', 'updraftplus' ) . ' ' . esc_html__( 'You should make sure that this really is a backup set intended for use on this website, before you restore (rather than a backup set of an unrelated website that was using the same storage location).', 'updraftplus' ); - } + return array($mess, $warn, $err, $info); + } - if ( isset( $elements['db'] ) ) { - // Analyse the header of the database file + display results. - if ( class_exists( '\UpdraftPlus_Encryption' ) ) { - list ( $mess2, $warn2, $err2, $info ) = $updraftplus->analyse_db_file( $timestamp, $res ); - } else { - list ( $mess2, $warn2, $err2, $info ) = $this->analyse_db_file_old( $timestamp, $res ); - } - $mess = array_merge( $mess, $mess2 ); - $warn = array_merge( $warn, $warn2 ); - $err = array_merge( $err, $err2 ); - foreach ( $backups[ $timestamp ] as $bid => $bval ) { - if ( 'db' !== $bid && 'db' === substr( $bid, 0, 2 ) && '-size' !== substr( $bid, - 5, 5 ) ) { - $warn[] = esc_html__( 'Only the WordPress database can be restored; you will need to deal with the external database manually.', 'updraftplus' ); - break; - } - } + // Analyse the file, print the results. + + $line = 0; + $old_siteurl = ''; + $old_home = ''; + $old_table_prefix = ''; + $old_siteinfo = array(); + $gathering_siteinfo = true; + $old_wp_version = ''; + $old_php_version = ''; + + $tables_found = array(); + + $wanted_tables = array( + 'terms', + 'term_taxonomy', + 'term_relationships', + 'commentmeta', + 'comments', + 'links', + 'options', + 'postmeta', + 'posts', + 'users', + 'usermeta', + ); + + $migration_warning = false; + + // Don't set too high - we want a timely response returned to the browser. + set_time_limit(90); + + $count_wanted_tables = count($wanted_tables); + + while ((($is_plain && ! feof($dbhandle)) || (! $is_plain && ! gzeof($dbhandle))) && ($line < 100 || (! $header_only && $count_wanted_tables > 0))) { + $line++; + // Up to 1Mb. + $buffer = ($is_plain) ? rtrim(fgets($dbhandle, 1048576)) : rtrim(gzgets($dbhandle, 1048576)); + // Comments are what we are interested in. + if ('#' === substr($buffer, 0, 1)) { + if ('' === $old_siteurl && preg_match('/^\# Backup of: (http(.*))$/', $buffer, $matches)) { + $old_siteurl = untrailingslashit($matches[1]); + $mess[] = esc_html__('Backup of:', 'updraftplus') . ' ' . htmlspecialchars($old_siteurl) . ((! empty($old_wp_version)) ? ' ' . sprintf(esc_html__('(version: %s)', 'updraftplus'), $old_wp_version) : ''); + // Check for should-be migration. + if (! $migration_warning && untrailingslashit(site_url()) !== $old_siteurl) { + $migration_warning = true; + $powarn = apply_filters('updraftplus_dbscan_urlchange', sprintf(esc_html__('Warning: %s', 'updraftplus'), '' . esc_html__('This backup set is from a different site - this is not a restoration, but a migration. You need the Migrator add-on in order to make this work.', 'updraftplus') . ''), $old_siteurl, $res); + if (! empty($powarn)) { + $warn[] = $powarn; } - - $backupable_entities = $updraftplus->get_backupable_file_entities( true, true ); - $backupable_plus_db = $backupable_entities; - $backupable_plus_db['db'] = array( - 'path' => 'path-unused', - 'description' => esc_html__( 'Database', 'updraftplus' ), - ); - - if ( ! empty( $backups[ $timestamp ]['meta_foreign'] ) ) { - $foreign_known = apply_filters( 'updraftplus_accept_archivename', array() ); - if ( ! is_array( $foreign_known ) || empty( $foreign_known[ $backups[ $timestamp ]['meta_foreign'] ] ) ) { - $err[] = sprintf( esc_html__( 'Backup created by unknown source (%s) - cannot be restored.', 'updraftplus' ), $backups[ $timestamp ]['meta_foreign'] ); - } else { - // For some reason, on PHP 5.5 passing by reference in a single array stopped working with apply_filters_ref_array (though not with do_action_ref_array). - $backupable_plus_db = apply_filters_ref_array( - 'updraftplus_importforeign_backupable_plus_db', - array( - $backupable_plus_db, - array( - $foreign_known[ $backups[ $timestamp ]['meta_foreign'] ], - &$mess, - &$warn, - &$err, - ), - ) - ); - } + } + } elseif ('' === $old_home && preg_match('/^\# Home URL: (http(.*))$/', $buffer, $matches)) { + $old_home = untrailingslashit($matches[1]); + // Check for should-be migration. + if (! $migration_warning && home_url() !== $old_home) { + $migration_warning = true; + $powarn = apply_filters('updraftplus_dbscan_urlchange', sprintf(esc_html__('Warning: %s', 'updraftplus'), '' . esc_html__('This backup set is from a different site - this is not a restoration, but a migration. You need the Migrator add-on in order to make this work.', 'updraftplus') . ''), $old_home, $res); + if (! empty($powarn)) { + $warn[] = $powarn; } - - foreach ( $backupable_plus_db as $type => $info ) { - if ( ! isset( $elements[ $type ] ) ) { - continue; - } - $whatwegot = $backups[ $timestamp ][ $type ]; - if ( is_string( $whatwegot ) ) { - $whatwegot = array( $whatwegot ); - } - $expected_index = 0; - $missing = ''; - ksort( $whatwegot ); - $outof = false; - foreach ( $whatwegot as $index => $file ) { - if ( preg_match( '/\d+of(\d+)\.zip/', $file, $omatch ) ) { - $outof = max( $matches[1], 1 ); - } - if ( $index !== $expected_index ) { - $missing .= ( '' === $missing ) ? ( 1 + $expected_index ) : ',' . ( 1 + $expected_index ); - } - if ( ! file_exists( $updraft_dir . '/' . $file ) ) { - $err[] = sprintf( esc_html__( 'File not found (you need to upload it): %s', 'updraftplus' ), $updraft_dir . '/' . $file ); - } elseif ( 0 === filesize( $updraft_dir . '/' . $file ) ) { - $err[] = sprintf( esc_html__( 'File was found, but is zero-sized (you need to re-upload it): %s', 'updraftplus' ), $file ); - } else { - $itext = ( 0 === $index ) ? '' : $index; - if ( ! empty( $backups[ $timestamp ][ $type . $itext . '-size' ] ) && filesize( $updraft_dir . '/' . $file ) !== $backups[ $timestamp ][ $type . $itext . '-size' ] && empty( $warn['doublecompressfixed'] ) ) { - $warn[] = sprintf( esc_html__( 'File (%1$s) was found, but has a different size (%2$s) from what was expected (%3$s) - it may be corrupt.', 'updraftplus' ), $file, filesize( $updraft_dir . '/' . $file ), $backups[ $timestamp ][ $type . $itext . '-size' ] ); - } - do_action_ref_array( - "updraftplus_checkzip_$type", - array( - $updraft_dir . '/' . $file, - &$mess, - &$warn, - &$err, - ) - ); - } - $expected_index ++; - } - do_action_ref_array( "updraftplus_checkzip_end_$type", array( &$mess, &$warn, &$err ) ); - // Detect missing archives where they are missing from the end of the set. - if ( $outof > 0 && $expected_index < $outof ) { - for ( $j = $expected_index; $j < $outof; $j ++ ) { - $missing .= ( '' === $missing ) ? ( 1 + $j ) : ',' . ( 1 + $j ); - } - } - if ( '' !== $missing ) { - $warn[] = sprintf( esc_html__( 'This multi-archive backup set appears to have the following archives missing: %s', 'updraftplus' ), $missing . ' (' . $info['description'] . ')' ); - } + } + } elseif ('' === $old_wp_version && preg_match('/^\# WordPress Version: (\d+(\.\d+)+)(-[-a-z0-9]+,)?(.*)$/', $buffer, $matches)) { + $old_wp_version = $matches[1]; + if (! empty($matches[3])) { + $old_wp_version .= substr($matches[3], 0, strlen($matches[3]) - 1); + } + if (version_compare($old_wp_version, $wp_version, '>')) { + $warn[] = sprintf(esc_html__('You are importing from a newer version of WordPress (%1$s) into an older one (%2$s). There are no guarantees that WordPress can handle this.', 'updraftplus'), $old_wp_version, $wp_version); + } + if (preg_match('/running on PHP (\d+\.\d+)([\s\.])/', $matches[4], $nmatches) && preg_match('/^(\d+\.\d+)([\s\.])/', PHP_VERSION, $cmatches)) { + $old_php_version = $nmatches[1]; + $current_php_version = $cmatches[1]; + if (version_compare($old_php_version, $current_php_version, '>')) { + $warn[] = sprintf(esc_html__('The site in this backup was running on a webserver with version %1$s of %2$s. ', 'updraftplus'), $old_php_version, 'PHP') . ' ' . sprintf(esc_html__('This is significantly newer than the server which you are now restoring onto (version %s).', 'updraftplus'), PHP_VERSION) . ' ' . sprintf(esc_html__('You should only proceed if you cannot update the current server and are confident (or willing to risk) that your plugins/themes/etc. are compatible with the older %s version.', 'updraftplus'), 'PHP') . ' ' . sprintf(esc_html__('Any support requests to do with %s should be raised with your web hosting company.', 'updraftplus'), 'PHP'); } - - if ( empty( $err ) && empty( $warn ) ) { - $mess_first = esc_html__( 'The backup archive files have been successfully processed. Now press Restore again to proceed.', 'updraftplus' ); - } elseif ( empty( $err ) ) { - $mess_first = esc_html__( 'The backup archive files have been processed, but with some warnings. If all is well, then now press Restore again to proceed. Otherwise, cancel and correct any problems first.', 'updraftplus' ); - } else { - $mess_first = esc_html__( 'The backup archive files have been processed, but with some errors. You will need to cancel and correct any problems before retrying.', 'updraftplus' ); + } + } elseif ('' === $old_table_prefix && (preg_match('/^\# Table prefix: (\S+)$/', $buffer, $matches) || preg_match('/^-- Table prefix: (\S+)$/i', $buffer, $matches))) { + $old_table_prefix = $matches[1]; + } elseif (empty($info['label']) && preg_match('/^\# Label: (.*)$/', $buffer, $matches)) { + $info['label'] = $matches[1]; + $mess[] = esc_html__('Backup label:', 'updraftplus') . ' ' . htmlspecialchars($info['label']); + } elseif ($gathering_siteinfo && preg_match('/^\# Site info: (\S+)$/', $buffer, $matches)) { + if ('end' === $matches[1]) { + $gathering_siteinfo = false; + // Sanity checks. + if (isset($old_siteinfo['multisite']) && ! $old_siteinfo['multisite'] && is_multisite()) { + // Just need to check that you're crazy. + if (! defined('UPDRAFTPLUS_EXPERIMENTAL_IMPORTINTOMULTISITE') || true !== UPDRAFTPLUS_EXPERIMENTAL_IMPORTINTOMULTISITE) { + $err[] = sprintf(esc_html__('Error: %s', 'updraftplus'), esc_html__('You are running on WordPress multisite - but your backup is not of a multisite site.', 'updraftplus')); + + return array($mess, $warn, $err, $info); + } + // Got the needed code? + if (! class_exists('\UpdraftPlusAddOn_MultiSite') || ! class_exists('\UpdraftPlus_Addons_Migrator')) { + $err[] = sprintf(esc_html__('Error: %s', 'updraftplus'), esc_html__('To import an ordinary WordPress site into a multisite installation requires both the multisite and migrator add-ons.', 'updraftplus')); + + return array($mess, $warn, $err, $info); + } + } elseif (isset($old_siteinfo['multisite']) && $old_siteinfo['multisite'] && ! is_multisite()) { + $warn[] = esc_html__('Warning:', 'updraftplus') . ' ' . esc_html__('Your backup is of a WordPress multisite install; but this site is not. Only the first site of the network will be accessible.', 'updraftplus') . ' ' . esc_html__('If you want to restore a multisite backup, you should first set up your WordPress installation as a multisite.', 'updraftplus') . ''; } - - if ( count( $this->logged ) > 0 ) { - foreach ( $this->logged as $lwarn ) { - $warn[] = $lwarn; - } + } elseif (preg_match('/^([^=]+)=(.*)$/', $matches[1], $kvmatches)) { + $key = $kvmatches[1]; + $val = $kvmatches[2]; + if ('multisite' === $key && $val) { + $mess[] = '' . esc_html__('Site information:', 'updraftplus') . ' is a WordPress Network'; } - restore_error_handler(); - - return array( - 'm' => '

' . $mess_first . '

' . implode( '
', $mess ), - 'w' => implode( '
', $warn ), - 'e' => implode( '
', $err ), - ); - } + $old_siteinfo[$key] = $val; + } + } + } elseif (preg_match('/^\s*create table \`?([^\`\(]*)\`?\s*\(/i', $buffer, $matches)) { + $table = $matches[1]; + $tables_found[] = $table; + if ($old_table_prefix) { + // Remove prefix. + $table = $updraftplus->str_replace_once($old_table_prefix, '', $table); + if (in_array($table, $wanted_tables)) { + $wanted_tables = array_diff($wanted_tables, array($table)); + } + } + } } - /** - * Delete old directories. - * - * @param bool $show_return Whether or not to show removed directories in the output. Defualt: true. - * - * @return array Return response array. - */ - private function delete_old_dirs_go( $show_return = true ) { - ob_start(); - - echo ( $show_return ) ? '

UpdraftPlus - ' . esc_html__( 'Remove old directories', 'updraftplus' ) . '

' : '

' . esc_html__( 'Remove old directories', 'updraftplus' ) . '

'; - $deleted = 0; - if ( $this->delete_old_dirs() ) { - echo '

' . esc_html__( 'Old directories successfully removed.', 'updraftplus' ) . '

'; - echo '

' . esc_html__( 'Now press Restore again to proceed.', 'updraftplus' ) . '


'; - $deleted = 1; - } else { - echo '

', esc_html__( 'Old directory removal failed for some reason. You may want to do this manually.', 'updraftplus' ) . '


'; - } - - $output = ob_get_clean(); - - return array( - 'o' => $output, - 'd' => $deleted, - ); + if ($is_plain) { + fclose($dbhandle); + } else { + gzclose($dbhandle); } - /** - * Delete the old directories that are created when a backup is restored. - * - * @return bool Return $ret, $ret3, $ret4. TRUE|FALSE. - */ - private function delete_old_dirs() { - - /** - * @global object $wp_filesystem WordPress filesystem. - * @global object $updraftplus UpdraftPlus object. - */ - global $wp_filesystem, $updraftplus; - - $credentials = request_filesystem_credentials( wp_nonce_url( \UpdraftPlus_Options::admin_page_url() . '?page=updraftplus&action=updraft_delete_old_dirs', 'updraftplus-credentialtest-nonce' ) ); - WP_Filesystem( $credentials ); - if ( $wp_filesystem->errors->get_error_code() ) { - foreach ( $wp_filesystem->errors->get_error_messages() as $message ) { - show_message( $message ); - } - exit; - } - - $ret = $this->delete_old_dirs_dir( $wp_filesystem->wp_content_dir() ); - - $updraft_dir = $updraftplus->backups_dir_location(); - if ( $updraft_dir ) { - $ret4 = ( $updraft_dir ) ? $this->delete_old_dirs_dir( $updraft_dir, false ) : true; - } else { - $ret4 = true; - } - - $plugs = untrailingslashit( $wp_filesystem->wp_plugins_dir() ); - if ( $wp_filesystem->is_dir( $plugs . '-old' ) ) { - print '' . esc_html__( 'Delete', 'updraftplus' ) . ': plugins-old: '; - if ( ! $wp_filesystem->delete( $plugs . '-old', true ) ) { - $ret3 = false; - print '' . esc_html__( 'Failed', 'updraftplus' ) . '
'; - } else { - $ret3 = true; - print '' . esc_html__( 'OK', 'updraftplus' ) . '
'; - } - } else { - $ret3 = true; - } + $missing_tables = array(); + if ($old_table_prefix) { + if (! $header_only) { + foreach ($wanted_tables as $table) { + if (! in_array($old_table_prefix . $table, $tables_found)) { + $missing_tables[] = $table; + } + } + if (! empty($missing_tables)) { + $warn[] = sprintf(esc_html__('This database backup is missing core WordPress tables: %s', 'updraftplus'), implode(', ', $missing_tables)); + } + } + } else { + if (empty($backup['meta_foreign'])) { + $warn[] = esc_html__('UpdraftPlus was unable to find the table prefix when scanning the database backup.', 'updraftplus'); + } + } - return $ret && $ret3 && $ret4; + return array($mess, $warn, $err, $info); + } + + + /** + * Analyse database file. + * + * @param string $timestamp File timestamp. + * @param string $res UpdraftPlus response. + * @param bool $db_file Whether or not a DB file was found. Default: false. + * @param bool $header_only Whether or not file only has headers. Default: false. + * + * @return array[] Return array( $mess, $warn, $err, $info ). + * + * @uses MainWP_Child_Updraft_Plus_Backups::gzopen_for_read() + * @uses $wpdb::db_version() + * @uses $updraftplus::get_wordpress_version() + * @uses $updraftplus::backups_dir_location() + * @uses $updraftplus::get_max_packet_size() + * @uses $updraftplus::decrypt() + * @uses $updraftplus::get_similar_collate_related_to_charset() + * @uses $updraftplus::mod_rewrite_unavailable() + * @uses $updraftplus::get_similar_collate_based_on_ocuurence_count() + * @uses UpdraftPlus_Backup_History::get_history() + * @uses UpdraftPlus_Encryption::is_file_encrypted( + * @uses UpdraftPlus_Options::get_updraft_option() + * @uses UpdraftPlus_Manipulation_Functions::normalise_url() + * @uses UpdraftPlus_Manipulation_Functions::str_replace_once() + * @uses UpdraftPlus_Manipulation_Functions::get_matching_str_from_array_elems() + */ + public function analyse_db_file($timestamp, $res, $db_file = false, $header_only = false) { //phpcs:ignore -- NOSONAR - complex. + global $updraftplus; + + $mess = array(); + $warn = array(); + $err = array(); + $info = array(); + + $wp_version = $updraftplus->get_wordpress_version(); + + /** @global object $wpdb wpdb. */ + global $wpdb; + + $updraft_dir = $updraftplus->backups_dir_location(); + + if (false === $db_file) { + // This attempts to raise the maximum packet size. This can't be done within the session, only globally. Therefore, it has to be done before the session starts; in our case, during the pre-analysis. + $updraftplus->get_max_packet_size(); + + $backup = \UpdraftPlus_Backup_History::get_history($timestamp); + if (! isset($backup['nonce']) || ! isset($backup['db'])) { + return array($mess, $warn, $err, $info); + } + + $db_file = (is_string($backup['db'])) ? $updraft_dir . '/' . $backup['db'] : $updraft_dir . '/' . $backup['db'][0]; } - /** - * Delete the directories within a directory. - * - * @param string $dir Directory to scan. - * @param bool $wpfs Whether or not to use Wordpress filesystem to list directories, Default: true. - * @return bool|string $ret Return FALSE & echo 'Failed' on failure or echo 'OK' on success. - */ - private function delete_old_dirs_dir( $dir, $wpfs = true ) { //phpcs:ignore -- NOSONAR - complex. + if (! is_readable($db_file)) { + return array($mess, $warn, $err, $info); + } - $dir = trailingslashit( $dir ); + // Encrypted - decrypt it. + if (\UpdraftPlus_Encryption::is_file_encrypted($db_file)) { - /** - * @global object $wp_filesystem WordPress filesystem. - * @global object $updraftplus UpdraftPlus object. - */ - global $wp_filesystem, $updraftplus; + $encryption = empty($res['updraft_encryptionphrase']) ? \UpdraftPlus_Options::get_updraft_option('updraft_encryptionphrase') : $res['updraft_encryptionphrase']; - if ( $wpfs ) { - $list = $wp_filesystem->dirlist( $dir ); + if (! $encryption) { + if (class_exists('\UpdraftPlus_Addon_MoreDatabase')) { + $err[] = sprintf(esc_html__('Error: %s', 'updraftplus'), esc_html__('Decryption failed. The database file is encrypted, but you have no encryption key entered.', 'updraftplus')); } else { - $list = scandir( $dir ); - } - if ( ! is_array( $list ) ) { - return false; + $err[] = sprintf(esc_html__('Error: %s', 'updraftplus'), esc_html__('Decryption failed. The database file is encrypted.', 'updraftplus')); } + return array($mess, $warn, $err, $info); + } - $ret = true; - foreach ( $list as $item ) { - $name = ( is_array( $item ) ) ? $item['name'] : $item; - if ( '-old' === substr( $name, - 4, 4 ) ) { - // recursively delete. - print '' . esc_html__( 'Delete', 'updraftplus' ) . ': ' . htmlspecialchars( $name ) . ': '; - - if ( $wpfs ) { - if ( ! $wp_filesystem->delete( $dir . $name, true ) ) { - $ret = false; - echo '' . esc_html__( 'Failed', 'updraftplus' ) . '
'; - } else { - echo '' . esc_html__( 'OK', 'updraftplus' ) . '
'; - } - } else { - if ( $updraftplus->remove_local_directory( $dir . $name ) ) { - echo '' . esc_html__( 'OK', 'updraftplus' ) . '
'; - } else { - $ret = false; - echo '' . esc_html__( 'Failed', 'updraftplus' ) . '
'; - } - } - } - } + $decrypted_file = \UpdraftPlus_Encryption::decrypt($db_file, $encryption); - return $ret; + if (is_array($decrypted_file)) { + $db_file = $decrypted_file['fullpath']; + } else { + $err[] = esc_html__('Decryption failed. The most likely cause is that you used the wrong key.', 'updraftplus'); + return array($mess, $warn, $err, $info); + } } - - /** - * Show admin warning. - * - * @param string $message Warning message to display. - * @param string $class UpdraftPlus message CSS class. - */ - public function show_admin_warning( $message, $class = 'updated' ) { - echo '
' . "

$message

"; + // Even the empty schema when gzipped comes to 1565 bytes; a blank WP 3.6 install at 5158. But we go low, in case someone wants to share single tables. + if (filesize($db_file) < 1000) { + $err[] = sprintf(esc_html__('The database is too small to be a valid WordPress database (size: %s Kb).', 'updraftplus'), round(filesize($db_file) / 1024, 1)); + return array($mess, $warn, $err, $info); } - /** - * Analyse old database file. - * - * @param string $timestamp File timestamp. - * @param string $res UpdraftPlus response. - * @param bool $db_file Whether or not a DB file was found. Default: false. - * @param bool $header_only Whether or not file only has headers. Default: false. - * - * @return array[] Return array( $mess, $warn, $err, $info ). - * - * @uses $updraftplus::backups_dir_location() - * @uses $updraftplus::get_max_packet_size() - * @uses $updraftplus::get_backup_history() - * @uses $updraftplus::is_db_encrypted() - * @uses $updraftplus::decrypt() - * @uses UpdraftPlus_Options::get_updraft_option() - * @uses UpdraftPlus_Backup_History::get_history() - * @uses MainWP_Child_Updraft_Plus_Backups::gzopen_for_read() - */ - private function analyse_db_file_old( $timestamp, $res, $db_file = false, $header_only = false ) { //phpcs:ignore -- NOSONAR - complex. - - $mess = array(); - $warn = array(); - $err = array(); - $info = array(); - - /** - * @global object $wp_filesystem WordPress filesystem. - * @global object $updraftplus UpdraftPlus object. - */ - global $updraftplus, $wp_version; - - include_once ABSPATH . WPINC . '/version.php'; // NOSONAR - WP compatible. - - $updraft_dir = $updraftplus->backups_dir_location(); - - if ( false === $db_file ) { - // This attempts to raise the maximum packet size. This can't be done within the session, only globally. Therefore, it has to be done before the session starts; in our case, during the pre-analysis. - $updraftplus->get_max_packet_size(); - - if ( method_exists( $updraftplus, 'get_backup_history' ) ) { - $backup = $updraftplus->get_backup_history( $timestamp ); - } elseif ( class_exists( '\UpdraftPlus_Backup_History' ) ) { - $backup = \UpdraftPlus_Backup_History::get_history( $timestamp ); - } - - if ( ! isset( $backup['nonce'] ) || ! isset( $backup['db'] ) ) { - return array( $mess, $warn, $err, $info ); - } - - $db_file = ( is_string( $backup['db'] ) ) ? $updraft_dir . '/' . $backup['db'] : $updraft_dir . '/' . $backup['db'][0]; - } + $is_plain = ('.gz' === substr($db_file, -3, 3)) ? false : true; - if ( ! is_readable( $db_file ) ) { - return array( $mess, $warn, $err, $info ); - } - - // Encrypted - decrypt it. - if ( $updraftplus->is_db_encrypted( $db_file ) ) { - - $encryption = empty( $res['updraft_encryptionphrase'] ) ? \UpdraftPlus_Options::get_updraft_option( 'updraft_encryptionphrase' ) : $res['updraft_encryptionphrase']; + $dbhandle = ($is_plain) ? fopen($db_file, 'r') : $this->gzopen_for_read($db_file, $warn, $err); + if (! is_resource($dbhandle)) { + $err[] = esc_html__('Failed to open database file.', 'updraftplus'); + return array($mess, $warn, $err, $info); + } - if ( ! $encryption ) { - if ( class_exists( '\UpdraftPlus_Addon_MoreDatabase' ) ) { - $err[] = sprintf( esc_html__( 'Error: %s', 'updraftplus' ), esc_html__( 'Decryption failed. The database file is encrypted, but you have no encryption key entered.', 'updraftplus' ) ); + $info['timestamp'] = $timestamp; + + // Analyse the file, print the results. + + $line = 0; + $old_siteurl = ''; + $old_home = ''; + $old_table_prefix = ''; + $old_siteinfo = array(); + $gathering_siteinfo = true; + $old_wp_version = ''; + $old_php_version = ''; + + $tables_found = array(); + $db_charsets_found = array(); + + $wanted_tables = array('terms', 'term_taxonomy', 'term_relationships', 'commentmeta', 'comments', 'links', 'options', 'postmeta', 'posts', 'users', 'usermeta'); + + $migration_warning = false; + $processing_create = false; + $db_version = $wpdb->db_version(); + + $default_dbscan_timeout = (filesize($db_file) < 31457280) ? 120 : 240; + $dbscan_timeout = (defined('UPDRAFTPLUS_DBSCAN_TIMEOUT') && is_numeric(UPDRAFTPLUS_DBSCAN_TIMEOUT)) ? UPDRAFTPLUS_DBSCAN_TIMEOUT : $default_dbscan_timeout; + set_time_limit($dbscan_timeout); + + // We limit the time that we spend scanning the file for character sets. + $db_charset_collate_scan_timeout = (defined('UPDRAFTPLUS_DB_CHARSET_COLLATE_SCAN_TIMEOUT') && is_numeric(UPDRAFTPLUS_DB_CHARSET_COLLATE_SCAN_TIMEOUT)) ? UPDRAFTPLUS_DB_CHARSET_COLLATE_SCAN_TIMEOUT : 10; + $charset_scan_start_time = microtime(true); + $db_supported_character_sets_res = $GLOBALS['wpdb']->get_results('SHOW CHARACTER SET', OBJECT_K); + $db_supported_character_sets = (null !== $db_supported_character_sets_res) ? $db_supported_character_sets_res : array(); + $db_charsets_found = array(); + $db_supported_collations_res = $GLOBALS['wpdb']->get_results('SHOW COLLATION', OBJECT_K); + $db_supported_collations = (null !== $db_supported_collations_res) ? $db_supported_collations_res : array(); + $db_charsets_found = array(); + $db_collates_found = array(); + $db_supported_charset_related_to_unsupported_collation = false; + $db_supported_charsets_related_to_unsupported_collations = array(); + $count_wanted_tables = count($wanted_tables); + while ((($is_plain && ! feof($dbhandle)) || (! $is_plain && ! gzeof($dbhandle))) && ($line < 100 || (! $header_only && $count_wanted_tables > 0) || ((microtime(true) - $charset_scan_start_time) < $db_charset_collate_scan_timeout && ! empty($db_supported_character_sets)))) { + $line++; + // Up to 1MB. + $buffer = ($is_plain) ? rtrim(fgets($dbhandle, 1048576)) : rtrim(gzgets($dbhandle, 1048576)); + // Comments are what we are interested in. + if (substr($buffer, 0, 1) === '#') { + $processing_create = false; + if ('' === $old_siteurl && preg_match('/^\# Backup of: (http(.*))$/', $buffer, $matches)) { + $old_siteurl = untrailingslashit($matches[1]); + $mess[] = esc_html__('Backup of:', 'updraftplus') . ' ' . htmlspecialchars($old_siteurl) . ((! empty($old_wp_version)) ? ' ' . sprintf(esc_html__('(version: %s)', 'updraftplus'), $old_wp_version) : ''); + // Check for should-be migration. + if (untrailingslashit(site_url()) !== $old_siteurl) { + if (! $migration_warning) { + $migration_warning = true; + $info['migration'] = true; + if (\UpdraftPlus_Manipulation_Functions::normalise_url($old_siteurl) === \UpdraftPlus_Manipulation_Functions::normalise_url(site_url())) { + // Same site migration with only http/https difference. + $info['same_url'] = false; + $old_siteurl_parsed = wp_parse_url($old_siteurl); + $actual_siteurl_parsed = wp_parse_url(site_url()); + if ((0 === stripos($old_siteurl_parsed['host'], 'www.') && 0 !== stripos($actual_siteurl_parsed['host'], 'www.')) || (stripos($old_siteurl_parsed['host'], 'www.') !== 0 && stripos($actual_siteurl_parsed['host'], 'www.') === 0)) { + $powarn = sprintf(esc_html__('The website address in the backup set (%1$s) is slightly different from that of the site now (%2$s). This is not expected to be a problem for restoring the site, as long as visits to the former address still reach the site.', 'updraftplus'), $old_siteurl, site_url()) . ' '; } else { - $err[] = sprintf( esc_html__( 'Error: %s', 'updraftplus' ), esc_html__( 'Decryption failed. The database file is encrypted.', 'updraftplus' ) ); - } - - return array( $mess, $warn, $err, $info ); - } - - $ciphertext = $updraftplus->decrypt( $db_file, $encryption ); - - if ( $ciphertext ) { - $new_db_file = $updraft_dir . '/' . basename( $db_file, '.crypt' ); - if ( ! file_put_contents( $new_db_file, $ciphertext ) ) { //phpcs:ignore WordPress.WP.AlternativeFunctions - $err[] = esc_html__( 'Failed to write out the decrypted database to the filesystem.', 'updraftplus' ); - - return array( $mess, $warn, $err, $info ); + $powarn = ''; } - $db_file = $new_db_file; - } else { - $err[] = esc_html__( 'Decryption failed. The most likely cause is that you used the wrong key.', 'updraftplus' ); - - return array( $mess, $warn, $err, $info ); - } - } - - // Even the empty schema when gzipped comes to 1565 bytes; a blank WP 3.6 install at 5158. But we go low, in case someone wants to share single tables. - if ( filesize( $db_file ) < 1000 ) { - $err[] = sprintf( esc_html__( 'The database is too small to be a valid WordPress database (size: %s Kb).', 'updraftplus' ), round( filesize( $db_file ) / 1024, 1 ) ); - - return array( $mess, $warn, $err, $info ); - } - - $is_plain = ( '.gz' === substr( $db_file, - 3, 3 ) ) ? false : true; - - $dbhandle = ( $is_plain ) ? fopen( $db_file, 'r' ) : $this->gzopen_for_read( $db_file, $warn, $err ); - if ( ! is_resource( $dbhandle ) ) { - $err[] = esc_html__( 'Failed to open database file.', 'updraftplus' ); - - return array( $mess, $warn, $err, $info ); - } - - // Analyse the file, print the results. - - $line = 0; - $old_siteurl = ''; - $old_home = ''; - $old_table_prefix = ''; - $old_siteinfo = array(); - $gathering_siteinfo = true; - $old_wp_version = ''; - $old_php_version = ''; - - $tables_found = array(); - - $wanted_tables = array( - 'terms', - 'term_taxonomy', - 'term_relationships', - 'commentmeta', - 'comments', - 'links', - 'options', - 'postmeta', - 'posts', - 'users', - 'usermeta', - ); - - $migration_warning = false; - - // Don't set too high - we want a timely response returned to the browser. - set_time_limit( 90 ); - - $count_wanted_tables = count( $wanted_tables ); - - while ( ( ( $is_plain && ! feof( $dbhandle ) ) || ( ! $is_plain && ! gzeof( $dbhandle ) ) ) && ( $line < 100 || ( ! $header_only && $count_wanted_tables > 0 ) ) ) { - $line ++; - // Up to 1Mb. - $buffer = ( $is_plain ) ? rtrim( fgets( $dbhandle, 1048576 ) ) : rtrim( gzgets( $dbhandle, 1048576 ) ); - // Comments are what we are interested in. - if ( '#' === substr( $buffer, 0, 1 ) ) { - if ( '' === $old_siteurl && preg_match( '/^\# Backup of: (http(.*))$/', $buffer, $matches ) ) { - $old_siteurl = untrailingslashit( $matches[1] ); - $mess[] = esc_html__( 'Backup of:', 'updraftplus' ) . ' ' . htmlspecialchars( $old_siteurl ) . ( ( ! empty( $old_wp_version ) ) ? ' ' . sprintf( esc_html__( '(version: %s)', 'updraftplus' ), $old_wp_version ) : '' ); - // Check for should-be migration. - if ( ! $migration_warning && untrailingslashit( site_url() ) !== $old_siteurl ) { - $migration_warning = true; - $powarn = apply_filters( 'updraftplus_dbscan_urlchange', sprintf( esc_html__( 'Warning: %s', 'updraftplus' ), '' . esc_html__( 'This backup set is from a different site - this is not a restoration, but a migration. You need the Migrator add-on in order to make this work.', 'updraftplus' ) . '' ), $old_siteurl, $res ); - if ( ! empty( $powarn ) ) { - $warn[] = $powarn; - } - } - } elseif ( '' === $old_home && preg_match( '/^\# Home URL: (http(.*))$/', $buffer, $matches ) ) { - $old_home = untrailingslashit( $matches[1] ); - // Check for should-be migration. - if ( ! $migration_warning && home_url() !== $old_home ) { - $migration_warning = true; - $powarn = apply_filters( 'updraftplus_dbscan_urlchange', sprintf( esc_html__( 'Warning: %s', 'updraftplus' ), '' . esc_html__( 'This backup set is from a different site - this is not a restoration, but a migration. You need the Migrator add-on in order to make this work.', 'updraftplus' ) . '' ), $old_home, $res ); - if ( ! empty( $powarn ) ) { - $warn[] = $powarn; - } - } - } elseif ( '' === $old_wp_version && preg_match( '/^\# WordPress Version: (\d+(\.\d+)+)(-[-a-z0-9]+,)?(.*)$/', $buffer, $matches ) ) { - $old_wp_version = $matches[1]; - if ( ! empty( $matches[3] ) ) { - $old_wp_version .= substr( $matches[3], 0, strlen( $matches[3] ) - 1 ); - } - if ( version_compare( $old_wp_version, $wp_version, '>' ) ) { - $warn[] = sprintf( esc_html__( 'You are importing from a newer version of WordPress (%1$s) into an older one (%2$s). There are no guarantees that WordPress can handle this.', 'updraftplus' ), $old_wp_version, $wp_version ); - } - if ( preg_match( '/running on PHP (\d+\.\d+)([\s\.])/', $matches[4], $nmatches ) && preg_match( '/^(\d+\.\d+)([\s\.])/', PHP_VERSION, $cmatches ) ) { - $old_php_version = $nmatches[1]; - $current_php_version = $cmatches[1]; - if ( version_compare( $old_php_version, $current_php_version, '>' ) ) { - $warn[] = sprintf( esc_html__( 'The site in this backup was running on a webserver with version %1$s of %2$s. ', 'updraftplus' ), $old_php_version, 'PHP' ) . ' ' . sprintf( esc_html__( 'This is significantly newer than the server which you are now restoring onto (version %s).', 'updraftplus' ), PHP_VERSION ) . ' ' . sprintf( esc_html__( 'You should only proceed if you cannot update the current server and are confident (or willing to risk) that your plugins/themes/etc. are compatible with the older %s version.', 'updraftplus' ), 'PHP' ) . ' ' . sprintf( esc_html__( 'Any support requests to do with %s should be raised with your web hosting company.', 'updraftplus' ), 'PHP' ); - } - } - } elseif ( '' === $old_table_prefix && ( preg_match( '/^\# Table prefix: (\S+)$/', $buffer, $matches ) || preg_match( '/^-- Table prefix: (\S+)$/i', $buffer, $matches ) ) ) { - $old_table_prefix = $matches[1]; - } elseif ( empty( $info['label'] ) && preg_match( '/^\# Label: (.*)$/', $buffer, $matches ) ) { - $info['label'] = $matches[1]; - $mess[] = esc_html__( 'Backup label:', 'updraftplus' ) . ' ' . htmlspecialchars( $info['label'] ); - } elseif ( $gathering_siteinfo && preg_match( '/^\# Site info: (\S+)$/', $buffer, $matches ) ) { - if ( 'end' === $matches[1] ) { - $gathering_siteinfo = false; - // Sanity checks. - if ( isset( $old_siteinfo['multisite'] ) && ! $old_siteinfo['multisite'] && is_multisite() ) { - // Just need to check that you're crazy. - if ( ! defined( 'UPDRAFTPLUS_EXPERIMENTAL_IMPORTINTOMULTISITE' ) || true !== UPDRAFTPLUS_EXPERIMENTAL_IMPORTINTOMULTISITE ) { - $err[] = sprintf( esc_html__( 'Error: %s', 'updraftplus' ), esc_html__( 'You are running on WordPress multisite - but your backup is not of a multisite site.', 'updraftplus' ) ); - - return array( $mess, $warn, $err, $info ); - } - // Got the needed code? - if ( ! class_exists( '\UpdraftPlusAddOn_MultiSite' ) || ! class_exists( '\UpdraftPlus_Addons_Migrator' ) ) { - $err[] = sprintf( esc_html__( 'Error: %s', 'updraftplus' ), esc_html__( 'To import an ordinary WordPress site into a multisite installation requires both the multisite and migrator add-ons.', 'updraftplus' ) ); - - return array( $mess, $warn, $err, $info ); - } - } elseif ( isset( $old_siteinfo['multisite'] ) && $old_siteinfo['multisite'] && ! is_multisite() ) { - $warn[] = esc_html__( 'Warning:', 'updraftplus' ) . ' ' . esc_html__( 'Your backup is of a WordPress multisite install; but this site is not. Only the first site of the network will be accessible.', 'updraftplus' ) . ' ' . esc_html__( 'If you want to restore a multisite backup, you should first set up your WordPress installation as a multisite.', 'updraftplus' ) . ''; - } - } elseif ( preg_match( '/^([^=]+)=(.*)$/', $matches[1], $kvmatches ) ) { - $key = $kvmatches[1]; - $val = $kvmatches[2]; - if ( 'multisite' === $key && $val ) { - $mess[] = '' . esc_html__( 'Site information:', 'updraftplus' ) . ' is a WordPress Network'; - } - $old_siteinfo[ $key ] = $val; - } - } - } elseif ( preg_match( '/^\s*create table \`?([^\`\(]*)\`?\s*\(/i', $buffer, $matches ) ) { - $table = $matches[1]; - $tables_found[] = $table; - if ( $old_table_prefix ) { - // Remove prefix. - $table = $updraftplus->str_replace_once( $old_table_prefix, '', $table ); - if ( in_array( $table, $wanted_tables ) ) { - $wanted_tables = array_diff( $wanted_tables, array( $table ) ); - } + if (('https' === $old_siteurl_parsed['scheme'] && 'http' === $actual_siteurl_parsed['scheme']) || ('http' === $old_siteurl_parsed['scheme'] && 'https' === $actual_siteurl_parsed['scheme'])) { + $powarn .= sprintf(esc_html__('This backup set is of this site, but at the time of the backup you were using %1$s, whereas the site now uses %2$s.', 'updraftplus'), $old_siteurl_parsed['scheme'], $actual_siteurl_parsed['scheme']); + if ('https' === $old_siteurl_parsed['scheme']) { + $powarn .= ' ' . apply_filters('updraftplus_https_to_http_additional_warning', sprintf(esc_html__('This restoration will work if you still have an SSL certificate (i.e. can use https) to access the site. Otherwise, you will want to use %s to search/replace the site address so that the site can be visited without https.', 'updraftplus'), '' . esc_html__('the migrator add-on', 'updraftplus') . '')); + } else { + $powarn .= ' ' . apply_filters('updraftplus_http_to_https_additional_warning', sprintf(esc_html__('As long as your web hosting allows http (i.e. non-SSL access) or will forward requests to https (which is almost always the case), this is no problem. If that is not yet set up, then you should set it up, or use %s so that the non-https links are automatically replaced.', 'updraftplus'), apply_filters('updraftplus_migrator_addon_link', '' . esc_html__('the migrator add-on', 'updraftplus') . ''))); + } + } else { + $powarn .= apply_filters('updraftplus_dbscan_urlchange_www_append_warning', ''); } + $warn[] = $powarn; + } else { + // For completely different site migration. + $info['same_url'] = false; + $warn[] = apply_filters('updraftplus_dbscan_urlchange', '' . esc_html__('This backup set is from a different site - this is not a restoration, but a migration. You need the Migrator add-on in order to make this work.', 'updraftplus') . '', $old_siteurl, $res); + } + if (! class_exists('\UpdraftPlus_Addons_Migrator')) { + $warn[] .= '' . esc_html__('You can search and replace your database (for migrating a website to a new location/URL) with the Migrator add-on - follow this link for more information', 'updraftplus') . ''; + } } - } - - if ( $is_plain ) { - fclose( $dbhandle ); - } else { - gzclose( $dbhandle ); - } - $missing_tables = array(); - if ( $old_table_prefix ) { - if ( ! $header_only ) { - foreach ( $wanted_tables as $table ) { - if ( ! in_array( $old_table_prefix . $table, $tables_found ) ) { - $missing_tables[] = $table; - } - } - if ( ! empty( $missing_tables ) ) { - $warn[] = sprintf( esc_html__( 'This database backup is missing core WordPress tables: %s', 'updraftplus' ), implode( ', ', $missing_tables ) ); - } + if ($updraftplus->mod_rewrite_unavailable(false)) { + $warn[] = sprintf(esc_html__('You are using the %1$s webserver, but do not seem to have the %2$s module loaded.', 'updraftplus'), 'Apache', 'mod_rewrite') . ' ' . sprintf(esc_html__('You should enable %1$s to make any pretty permalinks (e.g. %2$s) work', 'updraftplus'), 'mod_rewrite', 'http://example.com/my-page/'); } - } else { - if ( empty( $backup['meta_foreign'] ) ) { - $warn[] = esc_html__( 'UpdraftPlus was unable to find the table prefix when scanning the database backup.', 'updraftplus' ); + } else { + // For exactly same URL site restoration. + $info['same_url'] = true; + } + } elseif ('' === $old_home && preg_match('/^\# Home URL: (http(.*))$/', $buffer, $matches)) { + $old_home = untrailingslashit($matches[1]); + // Check for should-be migration. + if (! $migration_warning && home_url() !== $old_home) { + $migration_warning = true; + $powarn = apply_filters('updraftplus_dbscan_urlchange', '' . esc_html__('This backup set is from a different site - this is not a restoration, but a migration. You need the Migrator add-on in order to make this work.', 'updraftplus') . '', $old_home, $res); + if (! empty($powarn)) { + $warn[] = $powarn; } - } - - return array( $mess, $warn, $err, $info ); - } - - - /** - * Analyse database file. - * - * @param string $timestamp File timestamp. - * @param string $res UpdraftPlus response. - * @param bool $db_file Whether or not a DB file was found. Default: false. - * @param bool $header_only Whether or not file only has headers. Default: false. - * - * @return array[] Return array( $mess, $warn, $err, $info ). - * - * @uses MainWP_Child_Updraft_Plus_Backups::gzopen_for_read() - * @uses $wpdb::db_version() - * @uses $updraftplus::get_wordpress_version() - * @uses $updraftplus::backups_dir_location() - * @uses $updraftplus::get_max_packet_size() - * @uses $updraftplus::decrypt() - * @uses $updraftplus::get_similar_collate_related_to_charset() - * @uses $updraftplus::mod_rewrite_unavailable() - * @uses $updraftplus::get_similar_collate_based_on_ocuurence_count() - * @uses UpdraftPlus_Backup_History::get_history() - * @uses UpdraftPlus_Encryption::is_file_encrypted( - * @uses UpdraftPlus_Options::get_updraft_option() - * @uses UpdraftPlus_Manipulation_Functions::normalise_url() - * @uses UpdraftPlus_Manipulation_Functions::str_replace_once() - * @uses UpdraftPlus_Manipulation_Functions::get_matching_str_from_array_elems() - */ - public function analyse_db_file($timestamp, $res, $db_file = false, $header_only = false ) { //phpcs:ignore -- NOSONAR - complex. - global $updraftplus; - - $mess = array(); - $warn = array(); - $err = array(); - $info = array(); - - $wp_version = $updraftplus->get_wordpress_version(); - - /** @global object $wpdb wpdb. */ - global $wpdb; - - $updraft_dir = $updraftplus->backups_dir_location(); - - if ( false === $db_file ) { - // This attempts to raise the maximum packet size. This can't be done within the session, only globally. Therefore, it has to be done before the session starts; in our case, during the pre-analysis. - $updraftplus->get_max_packet_size(); - - $backup = \UpdraftPlus_Backup_History::get_history( $timestamp ); - if ( ! isset( $backup['nonce'] ) || ! isset( $backup['db'] ) ) { - return array( $mess, $warn, $err, $info ); + } + } elseif (! isset($info['created_by_version']) && preg_match('/^\# Created by UpdraftPlus version ([\d\.]+)/', $buffer, $matches)) { + $info['created_by_version'] = trim($matches[1]); + } elseif ('' === $old_wp_version && preg_match('/^\# WordPress Version: (\d+(\.\d+)+)(-[-a-z0-9]+,)?(.*)$/', $buffer, $matches)) { + $old_wp_version = $matches[1]; + if (! empty($matches[3])) { + $old_wp_version .= substr($matches[3], 0, strlen($matches[3]) - 1); + } + if (version_compare($old_wp_version, $wp_version, '>')) { + $warn[] = sprintf(esc_html__('You are importing from a newer version of WordPress (%1$s) into an older one (%2$s). There are no guarantees that WordPress can handle this.', 'updraftplus'), $old_wp_version, $wp_version); + } + if (preg_match('/running on PHP (\d+\.\d+)([\s\.])/', $matches[4], $nmatches) && preg_match('/^(\d+\.\d+)([\s\.])/', PHP_VERSION, $cmatches)) { + $old_php_version = $nmatches[1]; + $current_php_version = $cmatches[1]; + if (version_compare($old_php_version, $current_php_version, '>')) { + $warn[] = sprintf(esc_html__('The site in this backup was running on a webserver with version %1$s of %2$s. ', 'updraftplus'), $old_php_version, 'PHP') . ' ' . sprintf(esc_html__('This is significantly newer than the server which you are now restoring onto (version %s).', 'updraftplus'), PHP_VERSION) . ' ' . sprintf(esc_html__('You should only proceed if you cannot update the current server and are confident (or willing to risk) that your plugins/themes/etc. are compatible with the older %s version.', 'updraftplus'), 'PHP') . ' ' . sprintf(esc_html__('Any support requests to do with %s should be raised with your web hosting company.', 'updraftplus'), 'PHP'); } - - $db_file = ( is_string( $backup['db'] ) ) ? $updraft_dir . '/' . $backup['db'] : $updraft_dir . '/' . $backup['db'][0]; - } - - if ( ! is_readable( $db_file ) ) { - return array( $mess, $warn, $err, $info ); - } - - // Encrypted - decrypt it. - if ( \UpdraftPlus_Encryption::is_file_encrypted( $db_file ) ) { - - $encryption = empty( $res['updraft_encryptionphrase'] ) ? \UpdraftPlus_Options::get_updraft_option( 'updraft_encryptionphrase' ) : $res['updraft_encryptionphrase']; - - if ( ! $encryption ) { - if ( class_exists( '\UpdraftPlus_Addon_MoreDatabase' ) ) { - $err[] = sprintf( esc_html__( 'Error: %s', 'updraftplus' ), esc_html__( 'Decryption failed. The database file is encrypted, but you have no encryption key entered.', 'updraftplus' ) ); - } else { - $err[] = sprintf( esc_html__( 'Error: %s', 'updraftplus' ), esc_html__( 'Decryption failed. The database file is encrypted.', 'updraftplus' ) ); - } - return array( $mess, $warn, $err, $info ); + } + } elseif ('' === $old_table_prefix && (preg_match('/^\# Table prefix: (\S+)$/', $buffer, $matches) || preg_match('/^-- Table prefix: (\S+)$/i', $buffer, $matches))) { + $old_table_prefix = $matches[1]; + } elseif (empty($info['label']) && preg_match('/^\# Label: (.*)$/', $buffer, $matches)) { + $info['label'] = $matches[1]; + $mess[] = esc_html__('Backup label:', 'updraftplus') . ' ' . htmlspecialchars($info['label']); + } elseif ($gathering_siteinfo && preg_match('/^\# Site info: (\S+)$/', $buffer, $matches)) { + if ('end' === $matches[1]) { + $gathering_siteinfo = false; + // Sanity checks. + if (isset($old_siteinfo['multisite']) && ! $old_siteinfo['multisite'] && is_multisite()) { + $warn[] = esc_html__('You are running on WordPress multisite - but your backup is not of a multisite site.', 'updraftplus') . ' ' . esc_html__('It will be imported as a new site.', 'updraftplus') . ' ' . esc_html__('Please read this link for important information on this process.', 'updraftplus') . ''; + if (! class_exists('\UpdraftPlusAddOn_MultiSite') || ! class_exists('\UpdraftPlus_Addons_Migrator')) { + $err[] = sprintf(esc_html__('Error: %s', 'updraftplus'), sprintf(esc_html__('To import an ordinary WordPress site into a multisite installation requires %s.', 'updraftplus'), 'UpdraftPlus Premium')); + return array($mess, $warn, $err, $info); + } + } elseif (isset($old_siteinfo['multisite']) && $old_siteinfo['multisite'] && ! is_multisite()) { + $warn[] = esc_html__('Warning:', 'updraftplus') . ' ' . esc_html__('Your backup is of a WordPress multisite install; but this site is not. Only the first site of the network will be accessible.', 'updraftplus') . ' ' . esc_html__('If you want to restore a multisite backup, you should first set up your WordPress installation as a multisite.', 'updraftplus') . ''; } - - $decrypted_file = \UpdraftPlus_Encryption::decrypt( $db_file, $encryption ); - - if ( is_array( $decrypted_file ) ) { - $db_file = $decrypted_file['fullpath']; - } else { - $err[] = esc_html__( 'Decryption failed. The most likely cause is that you used the wrong key.', 'updraftplus' ); - return array( $mess, $warn, $err, $info ); + } elseif (preg_match('/^([^=]+)=(.*)$/', $matches[1], $kvmatches)) { + $key = $kvmatches[1]; + $val = $kvmatches[2]; + if ('multisite' === $key) { + $info['multisite'] = $val ? true : false; + if ($val) { + $mess[] = '' . esc_html__('Site information:', 'updraftplus') . ' backup is of a WordPress Network'; + } } - } - - // Even the empty schema when gzipped comes to 1565 bytes; a blank WP 3.6 install at 5158. But we go low, in case someone wants to share single tables. - if ( filesize( $db_file ) < 1000 ) { - $err[] = sprintf( esc_html__( 'The database is too small to be a valid WordPress database (size: %s Kb).', 'updraftplus' ), round( filesize( $db_file ) / 1024, 1 ) ); - return array( $mess, $warn, $err, $info ); - } - - $is_plain = ( '.gz' === substr( $db_file, -3, 3 ) ) ? false : true; - - $dbhandle = ( $is_plain ) ? fopen( $db_file, 'r' ) : $this->gzopen_for_read( $db_file, $warn, $err ); - if ( ! is_resource( $dbhandle ) ) { - $err[] = esc_html__( 'Failed to open database file.', 'updraftplus' ); - return array( $mess, $warn, $err, $info ); - } - - $info['timestamp'] = $timestamp; - - // Analyse the file, print the results. - - $line = 0; - $old_siteurl = ''; - $old_home = ''; - $old_table_prefix = ''; - $old_siteinfo = array(); - $gathering_siteinfo = true; - $old_wp_version = ''; - $old_php_version = ''; - - $tables_found = array(); - $db_charsets_found = array(); - - $wanted_tables = array( 'terms', 'term_taxonomy', 'term_relationships', 'commentmeta', 'comments', 'links', 'options', 'postmeta', 'posts', 'users', 'usermeta' ); - - $migration_warning = false; - $processing_create = false; - $db_version = $wpdb->db_version(); - - $default_dbscan_timeout = ( filesize( $db_file ) < 31457280 ) ? 120 : 240; - $dbscan_timeout = ( defined( 'UPDRAFTPLUS_DBSCAN_TIMEOUT' ) && is_numeric( UPDRAFTPLUS_DBSCAN_TIMEOUT ) ) ? UPDRAFTPLUS_DBSCAN_TIMEOUT : $default_dbscan_timeout; - set_time_limit( $dbscan_timeout ); - - // We limit the time that we spend scanning the file for character sets. - $db_charset_collate_scan_timeout = ( defined( 'UPDRAFTPLUS_DB_CHARSET_COLLATE_SCAN_TIMEOUT' ) && is_numeric( UPDRAFTPLUS_DB_CHARSET_COLLATE_SCAN_TIMEOUT ) ) ? UPDRAFTPLUS_DB_CHARSET_COLLATE_SCAN_TIMEOUT : 10; - $charset_scan_start_time = microtime( true ); - $db_supported_character_sets_res = $GLOBALS['wpdb']->get_results( 'SHOW CHARACTER SET', OBJECT_K ); - $db_supported_character_sets = ( null !== $db_supported_character_sets_res ) ? $db_supported_character_sets_res : array(); - $db_charsets_found = array(); - $db_supported_collations_res = $GLOBALS['wpdb']->get_results( 'SHOW COLLATION', OBJECT_K ); - $db_supported_collations = ( null !== $db_supported_collations_res ) ? $db_supported_collations_res : array(); - $db_charsets_found = array(); - $db_collates_found = array(); - $db_supported_charset_related_to_unsupported_collation = false; - $db_supported_charsets_related_to_unsupported_collations = array(); - $count_wanted_tables = count( $wanted_tables ); - while ( ( ( $is_plain && ! feof( $dbhandle ) ) || ( ! $is_plain && ! gzeof( $dbhandle ) ) ) && ( $line < 100 || ( ! $header_only && $count_wanted_tables > 0 ) || ( ( microtime( true ) - $charset_scan_start_time ) < $db_charset_collate_scan_timeout && ! empty( $db_supported_character_sets ) ) ) ) { - $line++; - // Up to 1MB. - $buffer = ( $is_plain ) ? rtrim( fgets( $dbhandle, 1048576 ) ) : rtrim( gzgets( $dbhandle, 1048576 ) ); - // Comments are what we are interested in. - if ( substr( $buffer, 0, 1 ) === '#' ) { - $processing_create = false; - if ( '' === $old_siteurl && preg_match( '/^\# Backup of: (http(.*))$/', $buffer, $matches ) ) { - $old_siteurl = untrailingslashit( $matches[1] ); - $mess[] = esc_html__( 'Backup of:', 'updraftplus' ) . ' ' . htmlspecialchars( $old_siteurl ) . ( ( ! empty( $old_wp_version ) ) ? ' ' . sprintf( esc_html__( '(version: %s)', 'updraftplus' ), $old_wp_version ) : '' ); - // Check for should-be migration. - if ( untrailingslashit( site_url() ) !== $old_siteurl ) { - if ( ! $migration_warning ) { - $migration_warning = true; - $info['migration'] = true; - if ( \UpdraftPlus_Manipulation_Functions::normalise_url( $old_siteurl ) === \UpdraftPlus_Manipulation_Functions::normalise_url( site_url() ) ) { - // Same site migration with only http/https difference. - $info['same_url'] = false; - $old_siteurl_parsed = wp_parse_url( $old_siteurl ); - $actual_siteurl_parsed = wp_parse_url( site_url() ); - if ( ( 0 === stripos( $old_siteurl_parsed['host'], 'www.' ) && 0 !== stripos( $actual_siteurl_parsed['host'], 'www.' ) ) || ( stripos( $old_siteurl_parsed['host'], 'www.' ) !== 0 && stripos( $actual_siteurl_parsed['host'], 'www.' ) === 0 ) ) { - $powarn = sprintf( esc_html__( 'The website address in the backup set (%1$s) is slightly different from that of the site now (%2$s). This is not expected to be a problem for restoring the site, as long as visits to the former address still reach the site.', 'updraftplus' ), $old_siteurl, site_url() ) . ' '; - } else { - $powarn = ''; - } - if ( ( 'https' === $old_siteurl_parsed['scheme'] && 'http' === $actual_siteurl_parsed['scheme'] ) || ( 'http' === $old_siteurl_parsed['scheme'] && 'https' === $actual_siteurl_parsed['scheme'] ) ) { - $powarn .= sprintf( esc_html__( 'This backup set is of this site, but at the time of the backup you were using %1$s, whereas the site now uses %2$s.', 'updraftplus' ), $old_siteurl_parsed['scheme'], $actual_siteurl_parsed['scheme'] ); - if ( 'https' === $old_siteurl_parsed['scheme'] ) { - $powarn .= ' ' . apply_filters( 'updraftplus_https_to_http_additional_warning', sprintf( esc_html__( 'This restoration will work if you still have an SSL certificate (i.e. can use https) to access the site. Otherwise, you will want to use %s to search/replace the site address so that the site can be visited without https.', 'updraftplus' ), '' . esc_html__( 'the migrator add-on', 'updraftplus' ) . '' ) ); - } else { - $powarn .= ' ' . apply_filters( 'updraftplus_http_to_https_additional_warning', sprintf( esc_html__( 'As long as your web hosting allows http (i.e. non-SSL access) or will forward requests to https (which is almost always the case), this is no problem. If that is not yet set up, then you should set it up, or use %s so that the non-https links are automatically replaced.', 'updraftplus' ), apply_filters( 'updraftplus_migrator_addon_link', '' . esc_html__( 'the migrator add-on', 'updraftplus' ) . '' ) ) ); - } - } else { - $powarn .= apply_filters( 'updraftplus_dbscan_urlchange_www_append_warning', '' ); - } - $warn[] = $powarn; - } else { - // For completely different site migration. - $info['same_url'] = false; - $warn[] = apply_filters( 'updraftplus_dbscan_urlchange', '' . esc_html__( 'This backup set is from a different site - this is not a restoration, but a migration. You need the Migrator add-on in order to make this work.', 'updraftplus' ) . '', $old_siteurl, $res ); - } - if ( ! class_exists( '\UpdraftPlus_Addons_Migrator' ) ) { - $warn[] .= '' . esc_html__( 'You can search and replace your database (for migrating a website to a new location/URL) with the Migrator add-on - follow this link for more information', 'updraftplus' ) . ''; - } - } - - if ( $updraftplus->mod_rewrite_unavailable( false ) ) { - $warn[] = sprintf( esc_html__( 'You are using the %1$s webserver, but do not seem to have the %2$s module loaded.', 'updraftplus' ), 'Apache', 'mod_rewrite' ) . ' ' . sprintf( esc_html__( 'You should enable %1$s to make any pretty permalinks (e.g. %2$s) work', 'updraftplus' ), 'mod_rewrite', 'http://example.com/my-page/' ); - } - } else { - // For exactly same URL site restoration. - $info['same_url'] = true; - } - } elseif ( '' === $old_home && preg_match( '/^\# Home URL: (http(.*))$/', $buffer, $matches ) ) { - $old_home = untrailingslashit( $matches[1] ); - // Check for should-be migration. - if ( ! $migration_warning && home_url() !== $old_home ) { - $migration_warning = true; - $powarn = apply_filters( 'updraftplus_dbscan_urlchange', '' . esc_html__( 'This backup set is from a different site - this is not a restoration, but a migration. You need the Migrator add-on in order to make this work.', 'updraftplus' ) . '', $old_home, $res ); - if ( ! empty( $powarn ) ) { - $warn[] = $powarn; - } - } - } elseif ( ! isset( $info['created_by_version'] ) && preg_match( '/^\# Created by UpdraftPlus version ([\d\.]+)/', $buffer, $matches ) ) { - $info['created_by_version'] = trim( $matches[1] ); - } elseif ( '' === $old_wp_version && preg_match( '/^\# WordPress Version: (\d+(\.\d+)+)(-[-a-z0-9]+,)?(.*)$/', $buffer, $matches ) ) { - $old_wp_version = $matches[1]; - if ( ! empty( $matches[3] ) ) { - $old_wp_version .= substr( $matches[3], 0, strlen( $matches[3] ) - 1 ); - } - if ( version_compare( $old_wp_version, $wp_version, '>' ) ) { - $warn[] = sprintf( esc_html__( 'You are importing from a newer version of WordPress (%1$s) into an older one (%2$s). There are no guarantees that WordPress can handle this.', 'updraftplus' ), $old_wp_version, $wp_version ); - } - if ( preg_match( '/running on PHP (\d+\.\d+)([\s\.])/', $matches[4], $nmatches ) && preg_match( '/^(\d+\.\d+)([\s\.])/', PHP_VERSION, $cmatches ) ) { - $old_php_version = $nmatches[1]; - $current_php_version = $cmatches[1]; - if ( version_compare( $old_php_version, $current_php_version, '>' ) ) { - $warn[] = sprintf( esc_html__( 'The site in this backup was running on a webserver with version %1$s of %2$s. ', 'updraftplus' ), $old_php_version, 'PHP' ) . ' ' . sprintf( esc_html__( 'This is significantly newer than the server which you are now restoring onto (version %s).', 'updraftplus' ), PHP_VERSION ) . ' ' . sprintf( esc_html__( 'You should only proceed if you cannot update the current server and are confident (or willing to risk) that your plugins/themes/etc. are compatible with the older %s version.', 'updraftplus' ), 'PHP' ) . ' ' . sprintf( esc_html__( 'Any support requests to do with %s should be raised with your web hosting company.', 'updraftplus' ), 'PHP' ); - } - } - } elseif ( '' === $old_table_prefix && ( preg_match( '/^\# Table prefix: (\S+)$/', $buffer, $matches ) || preg_match( '/^-- Table prefix: (\S+)$/i', $buffer, $matches ) ) ) { - $old_table_prefix = $matches[1]; - } elseif ( empty( $info['label'] ) && preg_match( '/^\# Label: (.*)$/', $buffer, $matches ) ) { - $info['label'] = $matches[1]; - $mess[] = esc_html__( 'Backup label:', 'updraftplus' ) . ' ' . htmlspecialchars( $info['label'] ); - } elseif ( $gathering_siteinfo && preg_match( '/^\# Site info: (\S+)$/', $buffer, $matches ) ) { - if ( 'end' === $matches[1] ) { - $gathering_siteinfo = false; - // Sanity checks. - if ( isset( $old_siteinfo['multisite'] ) && ! $old_siteinfo['multisite'] && is_multisite() ) { - $warn[] = esc_html__( 'You are running on WordPress multisite - but your backup is not of a multisite site.', 'updraftplus' ) . ' ' . esc_html__( 'It will be imported as a new site.', 'updraftplus' ) . ' ' . esc_html__( 'Please read this link for important information on this process.', 'updraftplus' ) . ''; - if ( ! class_exists( '\UpdraftPlusAddOn_MultiSite' ) || ! class_exists( '\UpdraftPlus_Addons_Migrator' ) ) { - $err[] = sprintf( esc_html__( 'Error: %s', 'updraftplus' ), sprintf( esc_html__( 'To import an ordinary WordPress site into a multisite installation requires %s.', 'updraftplus' ), 'UpdraftPlus Premium' ) ); - return array( $mess, $warn, $err, $info ); - } - } elseif ( isset( $old_siteinfo['multisite'] ) && $old_siteinfo['multisite'] && ! is_multisite() ) { - $warn[] = esc_html__( 'Warning:', 'updraftplus' ) . ' ' . esc_html__( 'Your backup is of a WordPress multisite install; but this site is not. Only the first site of the network will be accessible.', 'updraftplus' ) . ' ' . esc_html__( 'If you want to restore a multisite backup, you should first set up your WordPress installation as a multisite.', 'updraftplus' ) . ''; - } - } elseif ( preg_match( '/^([^=]+)=(.*)$/', $matches[1], $kvmatches ) ) { - $key = $kvmatches[1]; - $val = $kvmatches[2]; - if ( 'multisite' === $key ) { - $info['multisite'] = $val ? true : false; - if ( $val ) { - $mess[] = '' . esc_html__( 'Site information:', 'updraftplus' ) . ' backup is of a WordPress Network'; - } - } - $old_siteinfo[ $key ] = $val; - } - } elseif ( preg_match( '/^\# Skipped tables: (.*)$/', $buffer, $matches ) ) { - $skipped_tables = explode( ',', $matches[1] ); - } - } elseif ( preg_match( '/^\s*create table \`?([^\`\(]*)\`?\s*\(/i', $buffer, $matches ) ) { - $table = $matches[1]; - $tables_found[] = $table; - if ( $old_table_prefix ) { - // Remove prefix. - $table = \UpdraftPlus_Manipulation_Functions::str_replace_once( $old_table_prefix, '', $table ); - if ( in_array( $table, $wanted_tables ) ) { - $wanted_tables = array_diff( $wanted_tables, array( $table ) ); - } - } - if ( ';' !== substr( $buffer, -1, 1 ) ) { - $processing_create = true; - $db_supported_charset_related_to_unsupported_collation = true; - } - } elseif ( $processing_create ) { - if ( ! empty( $db_supported_collations ) ) { - if ( preg_match( '/ COLLATE=([^\s;]+)/i', $buffer, $collate_match ) ) { - $db_collates_found[] = $collate_match[1]; - if ( ! isset( $db_supported_collations[ $collate_match[1] ] ) ) { - $db_supported_charset_related_to_unsupported_collation = true; - } - } - if ( preg_match( '/ COLLATE ([a-z0-9._\\-]+),/i', $buffer, $collate_match ) ) { - $db_collates_found[] = $collate_match[1]; - if ( ! isset( $db_supported_collations[ $collate_match[1] ] ) ) { - $db_supported_charset_related_to_unsupported_collation = true; - } - } - if ( preg_match( '/ COLLATE ([a-z0-9._\\-]+) /i', $buffer, $collate_match ) ) { - $db_collates_found[] = $collate_match[1]; - if ( ! isset( $db_supported_collations[ $collate_match[1] ] ) ) { - $db_supported_charset_related_to_unsupported_collation = true; - } - } - } - if ( ! empty( $db_supported_character_sets ) && preg_match( '/ CHARSET=([^\s;]+)/i', $buffer, $charset_match ) ) { - $db_charsets_found[] = $charset_match[1]; - if ( $db_supported_charset_related_to_unsupported_collation && ! in_array( $charset_match[1], $db_supported_charsets_related_to_unsupported_collations ) ) { - $db_supported_charsets_related_to_unsupported_collations[] = $charset_match[1]; - } - } - if ( ';' === substr( $buffer, -1, 1 ) ) { - $processing_create = false; - $db_supported_charset_related_to_unsupported_collation = false; - } - static $mysql_version_warned = false; - if ( ! $mysql_version_warned && version_compare( $db_version, '5.2.0', '<' ) && preg_match( '/(CHARSET|COLLATE)[= ]utf8mb4/', $buffer ) ) { - $mysql_version_warned = true; - $err[] = sprintf( esc_html__( 'Error: %s', 'updraftplus' ), sprintf( esc_html__( 'The database backup uses MySQL features not available in the old MySQL version (%s) that this site is running on.', 'updraftplus' ), $db_version ) . ' ' . esc_html__( 'You must upgrade MySQL to be able to use this database.', 'updraftplus' ) ); - } + $old_siteinfo[$key] = $val; + } + } elseif (preg_match('/^\# Skipped tables: (.*)$/', $buffer, $matches)) { + $skipped_tables = explode(',', $matches[1]); + } + } elseif (preg_match('/^\s*create table \`?([^\`\(]*)\`?\s*\(/i', $buffer, $matches)) { + $table = $matches[1]; + $tables_found[] = $table; + if ($old_table_prefix) { + // Remove prefix. + $table = \UpdraftPlus_Manipulation_Functions::str_replace_once($old_table_prefix, '', $table); + if (in_array($table, $wanted_tables)) { + $wanted_tables = array_diff($wanted_tables, array($table)); + } + } + if (';' !== substr($buffer, -1, 1)) { + $processing_create = true; + $db_supported_charset_related_to_unsupported_collation = true; + } + } elseif ($processing_create) { + if (! empty($db_supported_collations)) { + if (preg_match('/ COLLATE=([^\s;]+)/i', $buffer, $collate_match)) { + $db_collates_found[] = $collate_match[1]; + if (! isset($db_supported_collations[$collate_match[1]])) { + $db_supported_charset_related_to_unsupported_collation = true; } - } - if ( $is_plain ) { - fclose( $dbhandle ); - } else { - gzclose( $dbhandle ); - } - if ( ! empty( $db_supported_character_sets ) ) { - $db_charsets_found_unique = array_unique( $db_charsets_found ); - $db_unsupported_charset = array(); - $db_charset_forbidden = false; - foreach ( $db_charsets_found_unique as $db_charset ) { - if ( ! isset( $db_supported_character_sets[ $db_charset ] ) ) { - $db_unsupported_charset[] = $db_charset; - $db_charset_forbidden = true; - } + } + if (preg_match('/ COLLATE ([a-z0-9._\\-]+),/i', $buffer, $collate_match)) { + $db_collates_found[] = $collate_match[1]; + if (! isset($db_supported_collations[$collate_match[1]])) { + $db_supported_charset_related_to_unsupported_collation = true; } - if ( $db_charset_forbidden ) { - $db_unsupported_charset_unique = array_unique( $db_unsupported_charset ); - $warn[] = sprintf( _n( "The database server that this WordPress site is running on doesn't support the character set (%s) which you are trying to import.", "The database server that this WordPress site is running on doesn't support the character sets (%s) which you are trying to import.", count( $db_unsupported_charset_unique ), 'updraftplus' ), implode( ', ', $db_unsupported_charset_unique ) ) . ' ' . esc_html__( 'You can choose another suitable character set instead and continue with the restoration at your own risk.', 'updraftplus' ) . ' ' . esc_html__( 'Go here for more information.', 'updraftplus' ) . ' ' . esc_html__( 'Go here for more information.', 'updraftplus' ) . ''; - $db_supported_character_sets = array_keys( $db_supported_character_sets ); - $similar_type_charset = \UpdraftPlus_Manipulation_Functions::get_matching_str_from_array_elems( $db_unsupported_charset_unique, $db_supported_character_sets, true ); - if ( empty( $similar_type_charset ) ) { - $row = $GLOBALS['wpdb']->get_row( 'show variables like "character_set_database"' ); - $similar_type_charset = ( null !== $row ) ? $row->Value : ''; - } - if ( empty( $similar_type_charset ) && ! empty( $db_supported_character_sets[0] ) ) { - $similar_type_charset = $db_supported_character_sets[0]; - } - $charset_select_html = ' '; - $charset_select_html .= ''; - if ( empty( $info['addui'] ) ) { - $info['addui'] = ''; - } - $info['addui'] .= $charset_select_html; + } + if (preg_match('/ COLLATE ([a-z0-9._\\-]+) /i', $buffer, $collate_match)) { + $db_collates_found[] = $collate_match[1]; + if (! isset($db_supported_collations[$collate_match[1]])) { + $db_supported_charset_related_to_unsupported_collation = true; } + } } - if ( ! empty( $db_supported_collations ) ) { - $db_collates_found_unique = array_unique( $db_collates_found ); - $db_unsupported_collate = array(); - $db_collate_forbidden = false; - foreach ( $db_collates_found_unique as $db_collate ) { - if ( ! isset( $db_supported_collations[ $db_collate ] ) ) { - $db_unsupported_collate[] = $db_collate; - $db_collate_forbidden = true; - } - } - if ( $db_collate_forbidden ) { - $db_unsupported_collate_unique = array_unique( $db_unsupported_collate ); - $warn[] = sprintf( _n( "The database server that this WordPress site is running on doesn't support the collation (%s) used in the database which you are trying to import.", "The database server that this WordPress site is running on doesn't support multiple collations (%s) used in the database which you are trying to import.", count( $db_unsupported_collate_unique ), 'updraftplus' ), implode( ', ', $db_unsupported_collate_unique ) ) . ' ' . esc_html__( 'You can choose another suitable collation instead and continue with the restoration (at your own risk).', 'updraftplus' ); - $similar_type_collate = ''; - if ( $db_charset_forbidden && ! empty( $similar_type_charset ) ) { - $similar_type_collate = $updraftplus->get_similar_collate_related_to_charset( $db_supported_collations, $db_unsupported_collate_unique, $similar_type_charset ); - } - if ( empty( $similar_type_collate ) && ! empty( $db_supported_charsets_related_to_unsupported_collations ) ) { - $db_supported_collations_related_to_charset = array(); - foreach ( $db_supported_collations as $db_supported_collation => $db_supported_collations_info_obj ) { - if ( isset( $db_supported_collations_info_obj->Charset ) && in_array( $db_supported_collations_info_obj->Charset, $db_supported_charsets_related_to_unsupported_collations ) ) { - $db_supported_collations_related_to_charset[] = $db_supported_collation; - } - } - if ( ! empty( $db_supported_collations_related_to_charset ) ) { - $similar_type_collate = \UpdraftPlus_Manipulation_Functions::get_matching_str_from_array_elems( $db_unsupported_collate_unique, $db_supported_collations_related_to_charset, false ); - } - } - if ( empty( $similar_type_collate ) ) { - $similar_type_collate = $updraftplus->get_similar_collate_based_on_ocuurence_count( $db_collates_found, $db_supported_collations, $db_supported_charsets_related_to_unsupported_collations ); - } - if ( empty( $similar_type_collate ) ) { - $similar_type_collate = \UpdraftPlus_Manipulation_Functions::get_matching_str_from_array_elems( $db_unsupported_collate_unique, array_keys( $db_supported_collations ), false ); - } - - $collate_select_html = ''; - $collate_select_html .= ''; - - $info['addui'] = empty( $info['addui'] ) ? $collate_select_html : $info['addui'] . '
' . $collate_select_html; - - if ( $db_charset_forbidden ) { - $collate_change_on_charset_selection_data = array( - 'db_supported_collations' => $db_supported_collations, - 'db_unsupported_collate_unique' => $db_unsupported_collate_unique, - 'db_collates_found' => $db_collates_found, - ); - $info['addui'] .= ''; - } - } + if (! empty($db_supported_character_sets) && preg_match('/ CHARSET=([^\s;]+)/i', $buffer, $charset_match)) { + $db_charsets_found[] = $charset_match[1]; + if ($db_supported_charset_related_to_unsupported_collation && ! in_array($charset_match[1], $db_supported_charsets_related_to_unsupported_collations)) { + $db_supported_charsets_related_to_unsupported_collations[] = $charset_match[1]; + } } - - if ( ! isset( $skipped_tables ) ) { - $skipped_tables = array(); + if (';' === substr($buffer, -1, 1)) { + $processing_create = false; + $db_supported_charset_related_to_unsupported_collation = false; } - $missing_tables = array(); - if ( $old_table_prefix ) { - if ( ! $header_only ) { - foreach ( $wanted_tables as $table ) { - if ( ! in_array( $old_table_prefix . $table, $tables_found ) ) { - $missing_tables[] = $table; - } - } - - foreach ( $missing_tables as $key => $value ) { - if ( in_array( $old_table_prefix . $value, $skipped_tables ) ) { - unset( $missing_tables[ $key ] ); - } - } - - if ( ! empty( $missing_tables ) ) { - $warn[] = sprintf( esc_html__( 'This database backup is missing core WordPress tables: %s', 'updraftplus' ), implode( ', ', $missing_tables ) ); - } - if ( count( $skipped_tables ) > 0 ) { - $warn[] = sprintf( esc_html__( 'This database backup has the following WordPress tables excluded: %s', 'updraftplus' ), implode( ', ', $skipped_tables ) ); - } - } - } else { - if ( empty( $backup['meta_foreign'] ) ) { - $warn[] = esc_html__( 'UpdraftPlus was unable to find the table prefix when scanning the database backup.', 'updraftplus' ); - } + static $mysql_version_warned = false; + if (! $mysql_version_warned && version_compare($db_version, '5.2.0', '<') && preg_match('/(CHARSET|COLLATE)[= ]utf8mb4/', $buffer)) { + $mysql_version_warned = true; + $err[] = sprintf(esc_html__('Error: %s', 'updraftplus'), sprintf(esc_html__('The database backup uses MySQL features not available in the old MySQL version (%s) that this site is running on.', 'updraftplus'), $db_version) . ' ' . esc_html__('You must upgrade MySQL to be able to use this database.', 'updraftplus')); } - - return array( $mess, $warn, $err, $info ); + } } - - /** - * Gzip open. - * - * @param string $file File to open. - * @param string $warn Warning message. - * @param string $err Error message. - * - * @return bool|false|resource Return FALSE on failure or $what_to_return the unziped file. - */ - private function gzopen_for_read( $file, &$warn, &$err ) { //phpcs:ignore -- NOSONAR - complex. - if ( ! function_exists( 'gzopen' ) || ! function_exists( 'gzread' ) ) { - $missing = ''; - if ( ! function_exists( 'gzopen' ) ) { - $missing .= 'gzopen'; + if ($is_plain) { + fclose($dbhandle); + } else { + gzclose($dbhandle); + } + if (! empty($db_supported_character_sets)) { + $db_charsets_found_unique = array_unique($db_charsets_found); + $db_unsupported_charset = array(); + $db_charset_forbidden = false; + foreach ($db_charsets_found_unique as $db_charset) { + if (! isset($db_supported_character_sets[$db_charset])) { + $db_unsupported_charset[] = $db_charset; + $db_charset_forbidden = true; + } + } + if ($db_charset_forbidden) { + $db_unsupported_charset_unique = array_unique($db_unsupported_charset); + $warn[] = sprintf(_n("The database server that this WordPress site is running on doesn't support the character set (%s) which you are trying to import.", "The database server that this WordPress site is running on doesn't support the character sets (%s) which you are trying to import.", count($db_unsupported_charset_unique), 'updraftplus'), implode(', ', $db_unsupported_charset_unique)) . ' ' . esc_html__('You can choose another suitable character set instead and continue with the restoration at your own risk.', 'updraftplus') . ' ' . esc_html__('Go here for more information.', 'updraftplus') . ' ' . esc_html__('Go here for more information.', 'updraftplus') . ''; + $db_supported_character_sets = array_keys($db_supported_character_sets); + $similar_type_charset = \UpdraftPlus_Manipulation_Functions::get_matching_str_from_array_elems($db_unsupported_charset_unique, $db_supported_character_sets, true); + if (empty($similar_type_charset)) { + $row = $GLOBALS['wpdb']->get_row('show variables like "character_set_database"'); + $similar_type_charset = (null !== $row) ? $row->Value : ''; + } + if (empty($similar_type_charset) && ! empty($db_supported_character_sets[0])) { + $similar_type_charset = $db_supported_character_sets[0]; + } + $charset_select_html = ' '; + $charset_select_html .= ''; + if (empty($info['addui'])) { + $info['addui'] = ''; + } + $info['addui'] .= $charset_select_html; + } + } + if (! empty($db_supported_collations)) { + $db_collates_found_unique = array_unique($db_collates_found); + $db_unsupported_collate = array(); + $db_collate_forbidden = false; + foreach ($db_collates_found_unique as $db_collate) { + if (! isset($db_supported_collations[$db_collate])) { + $db_unsupported_collate[] = $db_collate; + $db_collate_forbidden = true; + } + } + if ($db_collate_forbidden) { + $db_unsupported_collate_unique = array_unique($db_unsupported_collate); + $warn[] = sprintf(_n("The database server that this WordPress site is running on doesn't support the collation (%s) used in the database which you are trying to import.", "The database server that this WordPress site is running on doesn't support multiple collations (%s) used in the database which you are trying to import.", count($db_unsupported_collate_unique), 'updraftplus'), implode(', ', $db_unsupported_collate_unique)) . ' ' . esc_html__('You can choose another suitable collation instead and continue with the restoration (at your own risk).', 'updraftplus'); + $similar_type_collate = ''; + if ($db_charset_forbidden && ! empty($similar_type_charset)) { + $similar_type_collate = $updraftplus->get_similar_collate_related_to_charset($db_supported_collations, $db_unsupported_collate_unique, $similar_type_charset); + } + if (empty($similar_type_collate) && ! empty($db_supported_charsets_related_to_unsupported_collations)) { + $db_supported_collations_related_to_charset = array(); + foreach ($db_supported_collations as $db_supported_collation => $db_supported_collations_info_obj) { + if (isset($db_supported_collations_info_obj->Charset) && in_array($db_supported_collations_info_obj->Charset, $db_supported_charsets_related_to_unsupported_collations)) { + $db_supported_collations_related_to_charset[] = $db_supported_collation; } - if ( ! function_exists( 'gzread' ) ) { - $missing .= ( $missing ) ? ', gzread' : 'gzread'; + } + if (! empty($db_supported_collations_related_to_charset)) { + $similar_type_collate = \UpdraftPlus_Manipulation_Functions::get_matching_str_from_array_elems($db_unsupported_collate_unique, $db_supported_collations_related_to_charset, false); + } + } + if (empty($similar_type_collate)) { + $similar_type_collate = $updraftplus->get_similar_collate_based_on_ocuurence_count($db_collates_found, $db_supported_collations, $db_supported_charsets_related_to_unsupported_collations); + } + if (empty($similar_type_collate)) { + $similar_type_collate = \UpdraftPlus_Manipulation_Functions::get_matching_str_from_array_elems($db_unsupported_collate_unique, array_keys($db_supported_collations), false); + } + + $collate_select_html = ''; + $collate_select_html .= ''; - $dbhandle = gzopen( $file, 'r' ); + $info['addui'] = empty($info['addui']) ? $collate_select_html : $info['addui'] . '
' . $collate_select_html; - if ( false === $dbhandle ) { - return false; + if ($db_charset_forbidden) { + $collate_change_on_charset_selection_data = array( + 'db_supported_collations' => $db_supported_collations, + 'db_unsupported_collate_unique' => $db_unsupported_collate_unique, + 'db_collates_found' => $db_collates_found, + ); + $info['addui'] .= ''; } + } + } - if ( ! function_exists( 'gzseek' ) ) { - return $dbhandle; + if (! isset($skipped_tables)) { + $skipped_tables = array(); + } + $missing_tables = array(); + if ($old_table_prefix) { + if (! $header_only) { + foreach ($wanted_tables as $table) { + if (! in_array($old_table_prefix . $table, $tables_found)) { + $missing_tables[] = $table; + } } - $bytes = gzread( $dbhandle, 3 ); - - if ( false === $bytes ) { - return false; + foreach ($missing_tables as $key => $value) { + if (in_array($old_table_prefix . $value, $skipped_tables)) { + unset($missing_tables[$key]); + } } - // Check if double-gzipped. - if ( 'H4sI' !== base64_encode( $bytes ) ) { // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions -- base64_encode function is used for http encode compatible.. - if ( 0 === gzseek( $dbhandle, 0 ) ) { - return $dbhandle; - } else { - gzclose( $dbhandle ); - - return gzopen( $file, 'r' ); - } + if (! empty($missing_tables)) { + $warn[] = sprintf(esc_html__('This database backup is missing core WordPress tables: %s', 'updraftplus'), implode(', ', $missing_tables)); } + if (count($skipped_tables) > 0) { + $warn[] = sprintf(esc_html__('This database backup has the following WordPress tables excluded: %s', 'updraftplus'), implode(', ', $skipped_tables)); + } + } + } else { + if (empty($backup['meta_foreign'])) { + $warn[] = esc_html__('UpdraftPlus was unable to find the table prefix when scanning the database backup.', 'updraftplus'); + } + } - // If double-gzipped. - $what_to_return = false; - $mess = esc_html__( 'The database file appears to have been compressed twice - probably the website you downloaded it from had a mis-configured webserver.', 'updraftplus' ); - $messkey = 'doublecompress'; - $err_msg = ''; - $fnew = fopen( $file . '.tmp', 'w' ); - if ( false === ( $fnew ) || ! is_resource( $fnew ) ) { + return array($mess, $warn, $err, $info); + } + + /** + * Gzip open. + * + * @param string $file File to open. + * @param string $warn Warning message. + * @param string $err Error message. + * + * @return bool|false|resource Return FALSE on failure or $what_to_return the unziped file. + */ + private function gzopen_for_read($file, &$warn, &$err) { //phpcs:ignore -- NOSONAR - complex. + if (! function_exists('gzopen') || ! function_exists('gzread')) { + $missing = ''; + if (! function_exists('gzopen')) { + $missing .= 'gzopen'; + } + if (! function_exists('gzread')) { + $missing .= ($missing) ? ', gzread' : 'gzread'; + } + $err[] = sprintf(esc_html__("Your web server's PHP installation has these functions disabled: %s.", 'updraftplus'), $missing) . ' ' . sprintf(esc_html__('Your hosting company must enable these functions before %s can work.', 'updraftplus'), esc_html__('restoration', 'updraftplus')); + + return false; + } - gzclose( $dbhandle ); - $err_msg = esc_html__( 'The attempt to undo the double-compression failed.', 'updraftplus' ); + $dbhandle = gzopen($file, 'r'); - } else { + if (false === $dbhandle) { + return false; + } - fwrite( $fnew, $bytes ); - $emptimes = 0; - while ( ! gzeof( $dbhandle ) ) { - $bytes = gzread( $dbhandle, 131072 ); - if ( empty( $bytes ) ) { - global $updraftplus; - $emptimes ++; - $updraftplus->log( "Got empty gzread ( $emptimes times )" ); - if ( $emptimes > 2 ) { - break; - } - } else { - fwrite( $fnew, $bytes ); - } - } + if (! function_exists('gzseek')) { + return $dbhandle; + } - gzclose( $dbhandle ); - fclose( $fnew ); - // On some systems (all Windows?) you can't rename a gz file whilst it's gzopened. - if ( ! rename( $file . '.tmp', $file ) ) { - $err_msg = esc_html__( 'The attempt to undo the double-compression failed.', 'updraftplus' ); - } else { - $mess .= ' ' . esc_html__( 'The attempt to undo the double-compression succeeded.', 'updraftplus' ); - $messkey = 'doublecompressfixed'; - $what_to_return = gzopen( $file, 'r' ); - } - } + $bytes = gzread($dbhandle, 3); - $warn[ $messkey ] = $mess; - if ( ! empty( $err_msg ) ) { - $err[] = $err_msg; - } + if (false === $bytes) { + return false; + } + + // Check if double-gzipped. + if ('H4sI' !== base64_encode($bytes)) { // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions -- base64_encode function is used for http encode compatible.. + if (0 === gzseek($dbhandle, 0)) { + return $dbhandle; + } else { + gzclose($dbhandle); - return $what_to_return; + return gzopen($file, 'r'); + } } - /** - * Build existing backups table. - * - * @param bool $backup_history Whether or not there is existing backups. Default: false. - * - * @return string Return $ret backups table html. - * @throws MainWP_Exception Error message. - * - * @uses UpdraftPlus_Backup_History::get_history() - * @uses UpdraftPlus_Encryption::is_file_encrypted() - * @uses $updraftplus::backups_dir_location() - * @uses $updraftplus::get_backupable_file_entities() - * @uses $updraftplus::jobdata_getarray() - * @uses $updraftplus::is_db_encrypted() - * @uses \MainWP\Child\MainWP_Child_Updraft_Plus_Backups::::delete_button() - * @uses \MainWP\Child\MainWP_Child_Updraft_Plus_Backups::date_label() - * @uses \MainWP\Child\MainWP_Child_Updraft_Plus_Backups::download_db_button() - * @uses \MainWP\Child\MainWP_Child_Updraft_Plus_Backups::download_buttons() - * @uses \MainWP\Child\MainWP_Child_Updraft_Plus_Backups::restore_button() - * @uses \MainWP\Child\MainWP_Child_Updraft_Plus_Backups::log_button() - * @uses \MainWP\Child\MainWP_Helper::instance()->check_methods() - */ - private function existing_backup_table( $backup_history = false ) { //phpcs:ignore -- NOSONAR - complex. + // If double-gzipped. + $what_to_return = false; + $mess = esc_html__('The database file appears to have been compressed twice - probably the website you downloaded it from had a mis-configured webserver.', 'updraftplus'); + $messkey = 'doublecompress'; + $err_msg = ''; + $fnew = fopen($file . '.tmp', 'w'); + if (false === ($fnew) || ! is_resource($fnew)) { + + gzclose($dbhandle); + $err_msg = esc_html__('The attempt to undo the double-compression failed.', 'updraftplus'); + } else { + + fwrite($fnew, $bytes); + $emptimes = 0; + while (! gzeof($dbhandle)) { + $bytes = gzread($dbhandle, 131072); + if (empty($bytes)) { + global $updraftplus; + $emptimes++; + $updraftplus->log("Got empty gzread ( $emptimes times )"); + if ($emptimes > 2) { + break; + } + } else { + fwrite($fnew, $bytes); + } + } + + gzclose($dbhandle); + fclose($fnew); + // On some systems (all Windows?) you can't rename a gz file whilst it's gzopened. + if (! rename($file . '.tmp', $file)) { + $err_msg = esc_html__('The attempt to undo the double-compression failed.', 'updraftplus'); + } else { + $mess .= ' ' . esc_html__('The attempt to undo the double-compression succeeded.', 'updraftplus'); + $messkey = 'doublecompressfixed'; + $what_to_return = gzopen($file, 'r'); + } + } - /** @global object $updraftplus UpdraftPlus object. */ - global $updraftplus; + $warn[$messkey] = $mess; + if (! empty($err_msg)) { + $err[] = $err_msg; + } - if ( false === $backup_history ) { - $backup_history = \UpdraftPlus_Backup_History::get_history(); - } + return $what_to_return; + } + + /** + * Build existing backups table. + * + * @param bool $backup_history Whether or not there is existing backups. Default: false. + * + * @return string Return $ret backups table html. + * @throws MainWP_Exception Error message. + * + * @uses UpdraftPlus_Backup_History::get_history() + * @uses UpdraftPlus_Encryption::is_file_encrypted() + * @uses $updraftplus::backups_dir_location() + * @uses $updraftplus::get_backupable_file_entities() + * @uses $updraftplus::jobdata_getarray() + * @uses $updraftplus::is_db_encrypted() + * @uses \MainWP\Child\MainWP_Child_Updraft_Plus_Backups::::delete_button() + * @uses \MainWP\Child\MainWP_Child_Updraft_Plus_Backups::date_label() + * @uses \MainWP\Child\MainWP_Child_Updraft_Plus_Backups::download_db_button() + * @uses \MainWP\Child\MainWP_Child_Updraft_Plus_Backups::download_buttons() + * @uses \MainWP\Child\MainWP_Child_Updraft_Plus_Backups::restore_button() + * @uses \MainWP\Child\MainWP_Child_Updraft_Plus_Backups::log_button() + * @uses \MainWP\Child\MainWP_Helper::instance()->check_methods() + */ + private function existing_backup_table($backup_history = false) { //phpcs:ignore -- NOSONAR - complex. + + /** @global object $updraftplus UpdraftPlus object. */ + global $updraftplus; + + if (false === $backup_history) { + $backup_history = \UpdraftPlus_Backup_History::get_history(); + } - if ( empty( $backup_history ) ) { - return '
' . esc_html__( 'You have not yet made any backups.', 'updraftplus' ) . '
'; - } + if (empty($backup_history)) { + return '
' . esc_html__('You have not yet made any backups.', 'updraftplus') . '
'; //phpcs:ignore -- NOSONAR - complex. + } - MainWP_Helper::instance()->check_methods( $updraftplus, array( 'backups_dir_location', 'get_backupable_file_entities' ) ); + MainWP_Helper::instance()->check_methods($updraftplus, array('backups_dir_location', 'get_backupable_file_entities')); - $accept = apply_filters( 'updraftplus_accept_archivename', array() ); - if ( ! is_array( $accept ) ) { - $accept = array(); - } + $accept = apply_filters('updraftplus_accept_archivename', array()); + if (! is_array($accept)) { + $accept = array(); + } - $ret = ''; - $nonce_field = wp_nonce_field( 'updraftplus_download', '_wpnonce', true, false ); + $ret = '
'; + $nonce_field = wp_nonce_field('updraftplus_download', '_wpnonce', true, false); - $ret .= ' + $ret .= ' - - - + + + '; - krsort( $backup_history ); - foreach ( $backup_history as $key => $backup ) { - - $pretty_date = get_date_from_gmt( gmdate( 'Y-m-d H:i:s', (int) $key ), 'M d, Y G:i' ); + krsort($backup_history); + foreach ($backup_history as $key => $backup) { - $esc_pretty_date = esc_attr( $pretty_date ); - $entities = ''; + $pretty_date = get_date_from_gmt(gmdate('Y-m-d H:i:s', (int) $key), 'M d, Y G:i'); - $non = $backup['nonce']; + $esc_pretty_date = esc_attr($pretty_date); + $entities = ''; - $rawbackup = ''; + $non = $backup['nonce']; - $jobdata = $updraftplus->jobdata_getarray( $non ); - - $delete_button = $this->delete_button( $key, $non, $backup ); - - $date_label = $this->date_label( $pretty_date, $key, $backup, $jobdata, $non ); - - $service_title = ''; - if ( ! isset( $backup['service'] ) ) { - $backup['service'] = array(); - } - if ( ! is_array( $backup['service'] ) ) { - $backup['service'] = array( $backup['service'] ); - } - foreach ( $backup['service'] as $service ) { - $emptyCheck = ( 'none' === $service || '' === $service || ( is_array( $service ) && ( empty( $service ) || array( 'none' ) === $service || array( '' ) === $service ) ) ); - if ( ! empty( $emptyCheck ) ) { - $remote_storage = ( 'remotesend' === $service ) ? esc_html__( 'remote site', 'updraftplus' ) : $updraftplus->backup_methods[ $service ]; - $service_title = '
' . esc_attr( sprintf( esc_html__( 'Remote storage: %s', 'updraftplus' ), $remote_storage ) ); - } - } - - $ret .= << - -ENDHERE; + $rawbackup = ''; - $ret .= ' +ENDHERE; - if ( ! empty( $backup['meta_foreign'] ) && ! empty( $accept[ $backup['meta_foreign'] ] ) && ! empty( $accept[ $backup['meta_foreign'] ]['separatedb'] ) ) { - $entities .= '/meta_foreign=2/'; - } + $ret .= ''; - $ret .= ''; - $ret .= ''; - } + } elseif (method_exists($updraftplus, 'is_db_encrypted') && $updraftplus->is_db_encrypted($db)) { + $entities .= '/dbcrypted=1/'; + } + + $ret .= $this->download_db_button('db', $key, $esc_pretty_date, $nonce_field, $backup, $accept); + } + + // External databases. + foreach ($backup as $bkey => $binfo) { + if ('db' === $bkey || 'db' !== substr($bkey, 0, 2) || '-size' === substr($bkey, -5, 5)) { + continue; + } + $ret .= $this->download_db_button($bkey, $key, $esc_pretty_date, $nonce_field, $backup); + } + } else { + // Foreign without separate db. + $entities = '/db=0/meta_foreign=1/'; + } + + if (! empty($backup['meta_foreign']) && ! empty($accept[$backup['meta_foreign']]) && ! empty($accept[$backup['meta_foreign']]['separatedb'])) { + $entities .= '/meta_foreign=2/'; + } + + $download_buttons = $this->download_buttons($backup, $key, $accept, $entities, $esc_pretty_date, $nonce_field); + + $ret .= $download_buttons; + $ret .= ''; + $ret .= '
' . esc_html__( 'Backup date', 'updraftplus' ) . '' . esc_html__( 'Backup data (click to download)', 'updraftplus' ) . '' . esc_html__( 'Actions', 'updraftplus' ) . '' . esc_html__('Backup date', 'updraftplus') . '' . esc_html__('Backup data (click to download)', 'updraftplus') . '' . esc_html__('Actions', 'updraftplus') . '
- $date_label . $service_title - '; - if ( empty( $backup['meta_foreign'] ) || ! empty( $accept[ $backup['meta_foreign'] ]['separatedb'] ) ) { + $jobdata = $updraftplus->jobdata_getarray($non); - if ( isset( $backup['db'] ) ) { - $entities .= '/db=0/'; + $delete_button = $this->delete_button($key, $non, $backup); - // Set a flag according to whether or not $backup['db'] ends in .crypt, then pick this up in the display of the decrypt field. - $db = is_array( $backup['db'] ) ? $backup['db'][0] : $backup['db']; - if ( class_exists( '\UpdraftPlus_Encryption' ) ) { - if ( method_exists( 'UpdraftPlus_Encryption', 'is_file_encrypted' ) && \UpdraftPlus_Encryption::is_file_encrypted( $db ) ) { - $entities .= '/dbcrypted=1/'; - } - } elseif ( method_exists( $updraftplus, 'is_db_encrypted' ) && $updraftplus->is_db_encrypted( $db ) ) { - $entities .= '/dbcrypted=1/'; - } + $date_label = $this->date_label($pretty_date, $key, $backup, $jobdata, $non); - $ret .= $this->download_db_button( 'db', $key, $esc_pretty_date, $nonce_field, $backup, $accept ); - } + $service_title = ''; + if (! isset($backup['service'])) { + $backup['service'] = array(); + } + if (! is_array($backup['service'])) { + $backup['service'] = array($backup['service']); + } + foreach ($backup['service'] as $service) { + $emptyCheck = ('none' === $service || '' === $service || (is_array($service) && (empty($service) || array('none') === $service || array('') === $service))); + if (! empty($emptyCheck)) { + $remote_storage = ('remotesend' === $service) ? esc_html__('remote site', 'updraftplus') : $updraftplus->backup_methods[$service]; + $service_title = '
' . esc_attr(sprintf(esc_html__('Remote storage: %s', 'updraftplus'), $remote_storage)); + } + } - // External databases. - foreach ( $backup as $bkey => $binfo ) { - if ( 'db' === $bkey || 'db' !== substr( $bkey, 0, 2 ) || '-size' === substr( $bkey, - 5, 5 ) ) { - continue; - } - $ret .= $this->download_db_button( $bkey, $key, $esc_pretty_date, $nonce_field, $backup ); - } - } else { - // Foreign without separate db. - $entities = '/db=0/meta_foreign=1/'; - } + $ret .= << +
+ $date_label . $service_title + '; + if (empty($backup['meta_foreign']) || ! empty($accept[$backup['meta_foreign']]['separatedb'])) { - $download_buttons = $this->download_buttons( $backup, $key, $accept, $entities, $esc_pretty_date, $nonce_field ); + if (isset($backup['db'])) { + $entities .= '/db=0/'; - $ret .= $download_buttons; - $ret .= ''; - $ret .= $this->restore_button( $key, $entities ); - $ret .= $delete_button; - if ( empty( $backup['meta_foreign'] ) ) { - $ret .= $this->log_button( $backup ); + // Set a flag according to whether or not $backup['db'] ends in .crypt, then pick this up in the display of the decrypt field. + $db = is_array($backup['db']) ? $backup['db'][0] : $backup['db']; + if (class_exists('\UpdraftPlus_Encryption')) { + if (method_exists('UpdraftPlus_Encryption', 'is_file_encrypted') && \UpdraftPlus_Encryption::is_file_encrypted($db)) { + $entities .= '/dbcrypted=1/'; } - $ret .= '
ENDHERE; - if ( $entities ) { - $ret .= ''; - } - $ret .= "
\n"; - - return $ret; + if ($entities) { + $ret .= ''; } - - /** - * Delete button. - * - * @param string $key Timestamp. - * @param string $nonce Security nonce. - * @param string $backup Backup to delete. - * - * @return string Delete button html. - */ - private function delete_button( $key, $nonce, $backup ) { - $sval = ( isset( $backup['service'] ) && 'email' !== $backup['service'] && 'none' !== $backup['service'] ) ? '1' : '0'; - - return '' . esc_html__( 'Delete', 'updraftplus' ) . ''; + $ret .= "\n"; + + return $ret; + } + + /** + * Delete button. + * + * @param string $key Timestamp. + * @param string $nonce Security nonce. + * @param string $backup Backup to delete. + * + * @return string Delete button html. + */ + private function delete_button($key, $nonce, $backup) + { + $sval = (isset($backup['service']) && 'email' !== $backup['service'] && 'none' !== $backup['service']) ? '1' : '0'; + + return '' . esc_html__('Delete', 'updraftplus') . ''; + } + + /** + * Date label. + * + * @param string $pretty_date Pretty date. + * @param string $key Timestamp. + * @param string $backup Type of backup. + * @param array $jobdata Job data. + * @param string $nonce Security nonce. + * + * @return string $ret Return date label html. + */ + private function date_label($pretty_date, $key, $backup, $jobdata, $nonce) + { + $ret = apply_filters('updraftplus_showbackup_date', $pretty_date, $backup, $jobdata, (int) $key, false); + if (is_array($jobdata) && ! empty($jobdata['resume_interval']) && (empty($jobdata['jobstatus']) || 'finished' !== $jobdata['jobstatus'])) { + $ret .= apply_filters('updraftplus_msg_unfinishedbackup', '
' . esc_html__('(Not finished)', 'updraftplus') . '', $jobdata, $nonce); } - /** - * Date label. - * - * @param string $pretty_date Pretty date. - * @param string $key Timestamp. - * @param string $backup Type of backup. - * @param array $jobdata Job data. - * @param string $nonce Security nonce. - * - * @return string $ret Return date label html. - */ - private function date_label($pretty_date, $key, $backup, $jobdata, $nonce ) { - $ret = apply_filters( 'updraftplus_showbackup_date', $pretty_date, $backup, $jobdata, (int) $key, false ); - if ( is_array( $jobdata ) && ! empty( $jobdata['resume_interval'] ) && ( empty( $jobdata['jobstatus'] ) || 'finished' !== $jobdata['jobstatus'] ) ) { - $ret .= apply_filters( 'updraftplus_msg_unfinishedbackup', '
' . esc_html__( '(Not finished)', 'updraftplus' ) . '', $jobdata, $nonce ); - } - - return $ret; + return $ret; + } + + /** + * Download database button. + * + * @param string $bkey Backup key. + * @param string $key Timestamp. + * @param string $esc_pretty_date Escaped pretty date. + * @param string $nonce_field Nonce filed. + * @param array $backup Backup data + * @param array $accept Destination array. + * + * @return string $ret Download DB button html. + */ + private function download_db_button($bkey, $key, $esc_pretty_date, $nonce_field, $backup, $accept = array()) + { + + if (! empty($backup['meta_foreign']) && isset($accept[$backup['meta_foreign']])) { + $desc_source = $accept[$backup['meta_foreign']]['desc']; + } else { + $desc_source = esc_html__('unknown source', 'updraftplus'); } - /** - * Download database button. - * - * @param string $bkey Backup key. - * @param string $key Timestamp. - * @param string $esc_pretty_date Escaped pretty date. - * @param string $nonce_field Nonce filed. - * @param array $backup Backup data - * @param array $accept Destination array. - * - * @return string $ret Download DB button html. - */ - private function download_db_button( $bkey, $key, $esc_pretty_date, $nonce_field, $backup, $accept = array() ) { - - if ( ! empty( $backup['meta_foreign'] ) && isset( $accept[ $backup['meta_foreign'] ] ) ) { - $desc_source = $accept[ $backup['meta_foreign'] ]['desc']; - } else { - $desc_source = esc_html__( 'unknown source', 'updraftplus' ); - } + $ret = ''; - $ret = ''; - - if ( 'db' === $bkey ) { - $dbt = empty( $backup['meta_foreign'] ) ? esc_attr( esc_html__( 'Database', 'updraftplus' ) ) : esc_attr( sprintf( esc_html__( 'Database (created by %s)', 'updraftplus' ), $desc_source ) ); - } else { - $dbt = esc_html__( 'External database', 'updraftplus' ) . ' (' . substr( $bkey, 2 ) . ')'; - } + if ('db' === $bkey) { + $dbt = empty($backup['meta_foreign']) ? esc_attr(esc_html__('Database', 'updraftplus')) : esc_attr(sprintf(esc_html__('Database (created by %s)', 'updraftplus'), $desc_source)); + } else { + $dbt = esc_html__('External database', 'updraftplus') . ' (' . substr($bkey, 2) . ')'; + } - $ret .= << + $ret .= <<
$nonce_field @@ -3249,139 +3352,139 @@ private function download_db_button( $bkey, $key, $esc_pretty_date, $nonce_field ENDHERE; - return $ret; - } - - /** - * Download buttons. - * - * @param array $backup Backup data array. - * @param string $key Backup key. - * @param array $accept Destination array. - * @param array $entities File entities. - * @param string $esc_pretty_date Escaped pretty date. - * @param string $nonce_field Nonce field. - * - * @return string $ret Download button html. - * - * @uses $updraftplus::get_backupable_file_entities() - * @uses MainWP_Child_Updraft_Plus_Backups::download_button() - */ - private function download_buttons( $backup, $key, $accept, &$entities, $esc_pretty_date, $nonce_field ) { //phpcs:ignore -- NOSONAR - complex. - - /** @global object $updraftplus UpdraftPlus object. */ - global $updraftplus; - $ret = ''; - - // Go through each of the file entities. - $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 ( 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( esc_html__( 'Backup created by: %s.', 'updraftplus' ), $accept[ $backup['meta_foreign'] ]['desc'] ) . ' '; - } else { - $desc_source = esc_html__( 'unknown source', 'updraftplus' ); - $ide .= esc_html__( 'Backup created by unknown source (%s) - cannot be restored.', 'updraftplus' ) . ' '; - } - - $sdescrip = ( empty( $accept[ $backup['meta_foreign'] ]['separatedb'] ) ) ? sprintf( esc_html__( 'Files and database WordPress backup (created by %s)', 'updraftplus' ), $desc_source ) : sprintf( esc_html__( 'Files backup (created by %s)', 'updraftplus' ), $desc_source ); - } - - $fix_perfomance = 0; - - 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/'; - } - $first_printed = true; - foreach ( $whatfiles as $findex => $bfile ) { - $ide .= esc_html__( 'Press here to download', 'updraftplus' ) . ' ' . strtolower( $info['description'] ); - $pdescrip = ( $findex > 0 ) ? $sdescrip . ' (' . ( $findex + 1 ) . ')' : $sdescrip; - if ( ! $first_printed ) { - $ret .= '
'; - } - if ( count( $backup[ $type ] ) > 0 ) { - $ide .= ' ' . sprintf( esc_html__( '(%d archive(s) in set).', 'updraftplus' ), $howmanyinset ); - } - if ( $index_missing ) { - $ide .= ' ' . esc_html__( 'You appear to be missing one or more archives from this multi-archive set.', 'updraftplus' ); - } - - if ( $first_entity ) { - $first_entity = false; - } - - $ret .= $this->download_button( $type, $key, $findex, $nonce_field, $ide, $pdescrip, $esc_pretty_date, $set_contents ); - - if ( ! $first_printed ) { - $ret .= '
'; - } else { - $first_printed = false; - } - - $fix_perfomance++; - if ( $fix_perfomance > 50 ) { // to fix perfomance issue of response when too much backup files! - break; - } - } - } - } - - return $ret; + return $ret; + } + + /** + * Download buttons. + * + * @param array $backup Backup data array. + * @param string $key Backup key. + * @param array $accept Destination array. + * @param array $entities File entities. + * @param string $esc_pretty_date Escaped pretty date. + * @param string $nonce_field Nonce field. + * + * @return string $ret Download button html. + * + * @uses $updraftplus::get_backupable_file_entities() + * @uses MainWP_Child_Updraft_Plus_Backups::download_button() + */ + private function download_buttons($backup, $key, $accept, &$entities, $esc_pretty_date, $nonce_field) { //phpcs:ignore -- NOSONAR - complex. + + /** @global object $updraftplus UpdraftPlus object. */ + global $updraftplus; + $ret = ''; + + // Go through each of the file entities. + $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 (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(esc_html__('Backup created by: %s.', 'updraftplus'), $accept[$backup['meta_foreign']]['desc']) . ' '; + } else { + $desc_source = esc_html__('unknown source', 'updraftplus'); + $ide .= esc_html__('Backup created by unknown source (%s) - cannot be restored.', 'updraftplus') . ' '; + } + + $sdescrip = (empty($accept[$backup['meta_foreign']]['separatedb'])) ? sprintf(esc_html__('Files and database WordPress backup (created by %s)', 'updraftplus'), $desc_source) : sprintf(esc_html__('Files backup (created by %s)', 'updraftplus'), $desc_source); + } + + $fix_perfomance = 0; + + 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/'; + } + $first_printed = true; + foreach ($whatfiles as $findex => $bfile) { + $ide .= esc_html__('Press here to download', 'updraftplus') . ' ' . strtolower($info['description']); + $pdescrip = ($findex > 0) ? $sdescrip . ' (' . ($findex + 1) . ')' : $sdescrip; + if (! $first_printed) { + $ret .= ''; + } else { + $first_printed = false; + } + + $fix_perfomance++; + if ($fix_perfomance > 50) { // to fix perfomance issue of response when too much backup files! + break; + } + } + } } - - /** - * Single download button. - * - * @param string $type Backup type. - * @param string $key Backup key. - * @param string $findex File index. - * @param string $nonce_field Security nonce field. - * @param string $ide Submit title. - * @param string $pdescrip Description. - * @param string $esc_pretty_date Escaped pretty date. - * @param string $set_contents mainwp_updraft_downloader value. - * - * @return string Download button html. - */ - private function download_button( $type, $key, $findex, $nonce_field, $ide, $pdescrip, $esc_pretty_date, $set_contents ) { // phpcs:ignore -- NOSONAR - compatible. - return << + return $ret; + } + + + /** + * Single download button. + * + * @param string $type Backup type. + * @param string $key Backup key. + * @param string $findex File index. + * @param string $nonce_field Security nonce field. + * @param string $ide Submit title. + * @param string $pdescrip Description. + * @param string $esc_pretty_date Escaped pretty date. + * @param string $set_contents mainwp_updraft_downloader value. + * + * @return string Download button html. + */ + private function download_button($type, $key, $findex, $nonce_field, $ide, $pdescrip, $esc_pretty_date, $set_contents){ // phpcs:ignore -- NOSONAR - compatible. + return << $nonce_field @@ -3392,30 +3495,31 @@ private function download_button( $type, $key, $findex, $nonce_field, $ide, $pde ENDHERE; - } - - /** - * Log button. - * - * @param array $backup Backup data array. - * - * @return string $ret Log button html. - * - * @uses $updraftplus::backups_dir_location() - * @uses UpdraftPlus_Options::admin_page() - */ - private function log_button( $backup ) { - - /** @global object $updraftplus UpdraftPlus object. */ - global $updraftplus; - - $updraft_dir = $updraftplus->backups_dir_location(); - $ret = ''; - if ( isset( $backup['nonce'] ) && preg_match( '/^[0-9a-f]{12}$/', $backup['nonce'] ) && is_readable( $updraft_dir . '/log.' . $backup['nonce'] . '.txt' ) ) { - $nval = $backup['nonce']; - $lt = esc_attr( esc_html__( 'View Log', 'updraftplus' ) ); - $url = \UpdraftPlus_Options::admin_page(); - $ret .= <<backups_dir_location(); + $ret = ''; + if (isset($backup['nonce']) && preg_match('/^[0-9a-f]{12}$/', $backup['nonce']) && is_readable($updraft_dir . '/log.' . $backup['nonce'] . '.txt')) { + $nval = $backup['nonce']; + $lt = esc_attr(esc_html__('View Log', 'updraftplus')); + $url = \UpdraftPlus_Options::admin_page(); + $ret .= <<
@@ -3426,890 +3530,928 @@ private function log_button( $backup ) { ENDHERE; - return $ret; - } + return $ret; } + } + + /** + * Rebuild backup history. + * + * @param bool $remotescan TRUE|FALSE Remote scan. + * + * @return array|null $messages Return backup history or Null. + * + * @uses $updraftplus::rebuild_backup_history() + * @uses $updraftplus_admin::rebuild_backup_history() + */ + private function rebuild_backup_history($remotescan = false) + { /** - * Rebuild backup history. - * - * @param bool $remotescan TRUE|FALSE Remote scan. - * - * @return array|null $messages Return backup history or Null. - * - * @uses $updraftplus::rebuild_backup_history() - * @uses $updraftplus_admin::rebuild_backup_history() + * @global object $updraftplus_admin UpdraftPlus Admin object. + * @global object $updraftplus UpdraftPlus object. */ - private function rebuild_backup_history( $remotescan = false ) { - - /** - * @global object $updraftplus_admin UpdraftPlus Admin object. - * @global object $updraftplus UpdraftPlus object. - */ - global $updraftplus_admin, $updraftplus; - if (empty($updraftplus_admin)) { - include_once UPDRAFTPLUS_DIR.'/admin.php'; // NOSONAR - compatible. - } - - $messages = null; - - if ( method_exists( $updraftplus, 'rebuild_backup_history' ) ) { - $messages = $updraftplus->rebuild_backup_history( $remotescan ); - } elseif ( method_exists( $updraftplus_admin, 'rebuild_backup_history' ) ) { - $messages = $updraftplus_admin->rebuild_backup_history( $remotescan ); - } - - return $messages; + global $updraftplus_admin, $updraftplus; + if (empty($updraftplus_admin)) { + include_once UPDRAFTPLUS_DIR . '/admin.php'; // NOSONAR - compatible. } - /** - * Force a scheduled resumption. - * - * @return array|bool[] Return FALSE & Empty array on failure. - * - * @uses MainWP_Child_Updraft_Plus_Backups::close_browser_connection() - * @uses $updraftplus::jobdata_set_from_array() - * @uses $updraftplus::backup_resume() - */ - private function force_scheduled_resumption() { - - /** @global object $updraftplus UpdraftPlus object. */ - global $updraftplus; - - /** - * Casting $resumption to int is absolutely necessary, as the WP cron system uses a hashed serialisation - * of the parameters for identifying jobs. Different type => different hash => does not match. - */ - $resumption = isset( $_REQUEST['resumption'] ) ? (int) $_REQUEST['resumption'] : 0; - $job_id = isset( $_REQUEST['job_id'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['job_id'] ) ) : 0; - $get_cron = $this->get_cron( $job_id ); - if ( ! is_array( $get_cron ) ) { - return array( 'r' => false ); - } else { - $updraftplus->log( "Forcing resumption: job id=$job_id, resumption=$resumption" ); - wp_clear_scheduled_hook( 'updraft_backup_resume', array( $resumption, $job_id ) ); - $this->close_browser_connection( array( 'r' => true ) ); - $updraftplus->jobdata_set_from_array( $get_cron[1] ); - $updraftplus->backup_resume( $resumption, $job_id ); - } + $messages = null; - return array(); + if (method_exists($updraftplus, 'rebuild_backup_history')) { + $messages = $updraftplus->rebuild_backup_history($remotescan); + } elseif (method_exists($updraftplus_admin, 'rebuild_backup_history')) { + $messages = $updraftplus_admin->rebuild_backup_history($remotescan); } - /** - * Check disk space used. - * - * @return array $out Return Disk Space Used or ERROR - * - * @uses MainWP_Child_Updraft_Plus_Backups::recursive_directory_size() - * @uses $updraftplus::backups_dir_location() - * @uses $updraftplus::get_backupable_file_entities() - * @uses $updraftplus::get_exclude() - */ - public function diskspaceused() { - - /** @global object $updraftplus UpdraftPlus object. */ - global $updraftplus; + return $messages; + } - $out = array(); - if ( 'updraft' === $_POST['entity'] ) { - $out['diskspaceused'] = $this->recursive_directory_size( $updraftplus->backups_dir_location() ); - } else { - $backupable_entities = $updraftplus->get_backupable_file_entities( true, false ); - if ( ! empty( $backupable_entities[ $_POST['entity'] ] ) ) { - $basedir = $backupable_entities[ $_POST['entity'] ]; - $dirs = apply_filters( 'updraftplus_dirlist_' . sanitize_text_field( wp_unslash( $_POST['entity'] ) ), $basedir ); - $out['diskspaceused'] = $this->recursive_directory_size( $dirs, $updraftplus->get_exclude( $_POST['entity'] ), $basedir ); - } else { - $out['error'] = 'Error'; - } - } + /** + * Force a scheduled resumption. + * + * @return array|bool[] Return FALSE & Empty array on failure. + * + * @uses MainWP_Child_Updraft_Plus_Backups::close_browser_connection() + * @uses $updraftplus::jobdata_set_from_array() + * @uses $updraftplus::backup_resume() + */ + private function force_scheduled_resumption() + { - return $out; - } + /** @global object $updraftplus UpdraftPlus object. */ + global $updraftplus; /** - * Recursively get directory sizes. - * If $basedirs is passed as an array, then $directorieses must be too. - * - * @param array $directorieses Directories to size. - * @param array $exclude Directories to exclude. - * @param string $basedirs Base directories. - * - * @return string Return total size. - * - * @uses MainWP_Child_Updraft_Plus_Backups::recursive_directory_size_raw() + * Casting $resumption to int is absolutely necessary, as the WP cron system uses a hashed serialisation + * of the parameters for identifying jobs. Different type => different hash => does not match. */ - private function recursive_directory_size( $directorieses, $exclude = array(), $basedirs = '' ) { //phpcs:ignore -- NOSONAR - complex. - - $size = 0; + $resumption = isset($_REQUEST['resumption']) ? (int) $_REQUEST['resumption'] : 0; + $job_id = isset($_REQUEST['job_id']) ? sanitize_text_field(wp_unslash($_REQUEST['job_id'])) : 0; + $get_cron = $this->get_cron($job_id); + if (! is_array($get_cron)) { + return array('r' => false); + } else { + $updraftplus->log("Forcing resumption: job id=$job_id, resumption=$resumption"); + wp_clear_scheduled_hook('updraft_backup_resume', array($resumption, $job_id)); + $this->close_browser_connection(array('r' => true)); + $updraftplus->jobdata_set_from_array($get_cron[1]); + $updraftplus->backup_resume($resumption, $job_id); + } - if ( is_string( $directorieses ) ) { - $basedirs = $directorieses; - $directorieses = array( $directorieses ); - } + return array(); + } + + /** + * Check disk space used. + * + * @return array $out Return Disk Space Used or ERROR + * + * @uses MainWP_Child_Updraft_Plus_Backups::recursive_directory_size() + * @uses $updraftplus::backups_dir_location() + * @uses $updraftplus::get_backupable_file_entities() + * @uses $updraftplus::get_exclude() + */ + public function diskspaceused() + { + + /** @global object $updraftplus UpdraftPlus object. */ + global $updraftplus; + + $out = array(); + if ('updraft' === $_POST['entity']) { + $out['diskspaceused'] = $this->recursive_directory_size($updraftplus->backups_dir_location()); + } else { + $backupable_entities = $updraftplus->get_backupable_file_entities(true, false); + if (! empty($backupable_entities[$_POST['entity']])) { + $basedir = $backupable_entities[$_POST['entity']]; + $dirs = apply_filters('updraftplus_dirlist_' . sanitize_text_field(wp_unslash($_POST['entity'])), $basedir); + $out['diskspaceused'] = $this->recursive_directory_size($dirs, $updraftplus->get_exclude($_POST['entity']), $basedir); + } else { + $out['error'] = 'Error'; + } + } - if ( is_string( $basedirs ) ) { - $basedirs = array( $basedirs ); - } + return $out; + } + + /** + * Recursively get directory sizes. + * If $basedirs is passed as an array, then $directorieses must be too. + * + * @param array $directorieses Directories to size. + * @param array $exclude Directories to exclude. + * @param string $basedirs Base directories. + * + * @return string Return total size. + * + * @uses MainWP_Child_Updraft_Plus_Backups::recursive_directory_size_raw() + */ + private function recursive_directory_size($directorieses, $exclude = array(), $basedirs = '') { //phpcs:ignore -- NOSONAR - complex. + + $size = 0; + + if (is_string($directorieses)) { + $basedirs = $directorieses; + $directorieses = array($directorieses); + } - foreach ( $directorieses as $ind => $directories ) { - if ( ! is_array( $directories ) ) { - $directories = array( $directories ); - } + if (is_string($basedirs)) { + $basedirs = array($basedirs); + } - $basedir = empty( $basedirs[ $ind ] ) ? $basedirs[0] : $basedirs[ $ind ]; + foreach ($directorieses as $ind => $directories) { + if (! is_array($directories)) { + $directories = array($directories); + } - foreach ( $directories as $dir ) { - if ( is_file( $dir ) ) { - $size += filesize( $dir ); - } else { - $suff = 0 === strpos( $dir, $basedir . '/' ) ? substr( $dir, 1 + strlen( $basedir ) ) : ''; - $suffix = '' !== $basedir ? $suff : ''; - $size += $this->recursive_directory_size_raw( $basedir, $exclude, $suffix ); - } - } - } + $basedir = empty($basedirs[$ind]) ? $basedirs[0] : $basedirs[$ind]; - if ( $size > 1073741824 ) { - return round( $size / 1073741824, 1 ) . ' Gb'; - } elseif ( $size > 1048576 ) { - return round( $size / 1048576, 1 ) . ' Mb'; - } elseif ( $size > 1024 ) { - return round( $size / 1024, 1 ) . ' Kb'; + foreach ($directories as $dir) { + if (is_file($dir)) { + $size += filesize($dir); } else { - return round( $size, 1 ) . ' b'; + $suff = 0 === strpos($dir, $basedir . '/') ? substr($dir, 1 + strlen($basedir)) : ''; + $suffix = '' !== $basedir ? $suff : ''; + $size += $this->recursive_directory_size_raw($basedir, $exclude, $suffix); } + } } - /** - * Recursivly get raw directory sizes. - * - * @param string $prefix_directory Directory prefix. - * @param array $exclude Directories to exclude. - * @param string $suffix_directory Directory suffix. - * - * @return false|int Return $size Raw Directory size or FALSE on failure. - * - * @uses MainWP_Child_Updraft_Plus_Backups::recursive_directory_size_raw() - */ - private function recursive_directory_size_raw( $prefix_directory, &$exclude = array(), $suffix_directory = '' ) { //phpcs:ignore -- NOSONAR - complex. - - $directory = $prefix_directory . ( '' === $suffix_directory ? '' : '/' . $suffix_directory ); - $size = 0; - if ( '/' === substr( $directory, - 1 ) ) { - $directory = substr( $directory, 0, - 1 ); - } + if ($size > 1073741824) { + return round($size / 1073741824, 1) . ' Gb'; + } elseif ($size > 1048576) { + return round($size / 1048576, 1) . ' Mb'; + } elseif ($size > 1024) { + return round($size / 1024, 1) . ' Kb'; + } else { + return round($size, 1) . ' b'; + } + } + + /** + * Recursivly get raw directory sizes. + * + * @param string $prefix_directory Directory prefix. + * @param array $exclude Directories to exclude. + * @param string $suffix_directory Directory suffix. + * + * @return false|int Return $size Raw Directory size or FALSE on failure. + * + * @uses MainWP_Child_Updraft_Plus_Backups::recursive_directory_size_raw() + */ + private function recursive_directory_size_raw($prefix_directory, &$exclude = array(), $suffix_directory = '') { //phpcs:ignore -- NOSONAR - complex. + + $directory = $prefix_directory . ('' === $suffix_directory ? '' : '/' . $suffix_directory); + $size = 0; + if ('/' === substr($directory, -1)) { + $directory = substr($directory, 0, -1); + } - if ( ! file_exists( $directory ) || ! is_dir( $directory ) || ! is_readable( $directory ) ) { - return - 1; - } - if ( file_exists( $directory . '/.donotbackup' ) ) { - return 0; - } + if (! file_exists($directory) || ! is_dir($directory) || ! is_readable($directory)) { + return -1; + } + if (file_exists($directory . '/.donotbackup')) { + return 0; + } - $handle = opendir( $directory ); - if ( $handle ) { - while ( ( $file = readdir( $handle ) ) !== false ) { - if ( '.' !== $file && '..' !== $file ) { - $spath = ( '' === $suffix_directory ) ? $file : $suffix_directory . '/' . $file; - $fkey = array_search( $spath, $exclude ); - if ( false !== $fkey ) { - unset( $exclude[ $fkey ] ); - continue; - } - $path = $directory . '/' . $file; - if ( is_file( $path ) ) { - $size += filesize( $path ); - } elseif ( is_dir( $path ) ) { - $handlesize = $this->recursive_directory_size_raw( $prefix_directory, $exclude, $suffix_directory . ( '' === $suffix_directory ? '' : '/' ) . $file ); - if ( $handlesize >= 0 ) { - $size += $handlesize; - } - } - } + $handle = opendir($directory); + if ($handle) { + while (($file = readdir($handle)) !== false) { + if ('.' !== $file && '..' !== $file) { + $spath = ('' === $suffix_directory) ? $file : $suffix_directory . '/' . $file; + $fkey = array_search($spath, $exclude); + if (false !== $fkey) { + unset($exclude[$fkey]); + continue; + } + $path = $directory . '/' . $file; + if (is_file($path)) { + $size += filesize($path); + } elseif (is_dir($path)) { + $handlesize = $this->recursive_directory_size_raw($prefix_directory, $exclude, $suffix_directory . ('' === $suffix_directory ? '' : '/') . $file); + if ($handlesize >= 0) { + $size += $handlesize; } - closedir( $handle ); + } } - - return $size; + } + closedir($handle); } - /** - * Get cron job. - * - * @param bool $job_id Cron job ID. - * - * @return array|bool Return cron job time and data or FALSE on failure. - * - * @uses $updraftplus::jobdata_getarray() - */ - private function get_cron( $job_id = false ) { //phpcs:ignore -- NOSONAR - complex. - - $cron = get_option( 'cron' ); - if ( ! is_array( $cron ) ) { - $cron = array(); - } - if ( false === $job_id ) { - return $cron; - } + return $size; + } + + /** + * Get cron job. + * + * @param bool $job_id Cron job ID. + * + * @return array|bool Return cron job time and data or FALSE on failure. + * + * @uses $updraftplus::jobdata_getarray() + */ + private function get_cron($job_id = false) { //phpcs:ignore -- NOSONAR - complex. + + $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'] ) ) { - foreach ( $job['updraft_backup_resume'] as $info ) { - if ( isset( $info['args'][1] ) && $job_id === $info['args'][1] ) { + foreach ($cron as $time => $job) { + if (isset($job['updraft_backup_resume'])) { + foreach ($job['updraft_backup_resume'] as $info) { + if (isset($info['args'][1]) && $job_id === $info['args'][1]) { - /** @global object $updraftplus UpdraftPlus object. */ - global $updraftplus; + /** @global object $updraftplus UpdraftPlus object. */ + global $updraftplus; - $jobdata = $updraftplus->jobdata_getarray( $job_id ); + $jobdata = $updraftplus->jobdata_getarray($job_id); - return ( ! is_array( $jobdata ) ) ? false : array( $time, $jobdata ); - } - } - } + return (! is_array($jobdata)) ? false : array($time, $jobdata); + } } + } } - - /** - * Display active jobs. - * 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). - * - * @param bool $this_job_only - * - * @return string $ret Return active jobs html. - * - * @uses MainWP_Child_Updraft_Plus_Backups::get_cron() - * @uses MainWP_Child_Updraft_Plus_Backups::print_active_job() - */ - private function print_active_jobs( $this_job_only = false ) { //phpcs:ignore -- NOSONAR - complex. - $cron = $this->get_cron(); - $ret = ''; - - foreach ( $cron as $time => $job ) { - if ( isset( $job['updraft_backup_resume'] ) ) { - foreach ( $job['updraft_backup_resume'] as $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 = '
' . esc_html__( 'The backup apparently succeeded and is now complete', 'updraftplus' ) . '
'; + } + + /** + * Display active jobs. + * 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). + * + * @param bool $this_job_only + * + * @return string $ret Return active jobs html. + * + * @uses MainWP_Child_Updraft_Plus_Backups::get_cron() + * @uses MainWP_Child_Updraft_Plus_Backups::print_active_job() + */ + private function print_active_jobs($this_job_only = false) { //phpcs:ignore -- NOSONAR - complex. + $cron = $this->get_cron(); + $ret = ''; + + foreach ($cron as $time => $job) { + if (isset($job['updraft_backup_resume'])) { + foreach ($job['updraft_backup_resume'] as $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]); } + } } - - return $ret; + } } - /** - * Display active job. - * - * @param string $job_id Job ID. - * @param bool $is_oneshot Whether or not this is a one time backup. - * @param bool $time Backup time. - * @param bool $next_resumption Next backups resumption. - * - * @return string Active job html. - * - * @uses $updraftplus::jobdata_getarray() - * @uses $updraftplus::get_backupable_file_entities() - */ - private function print_active_job( $job_id, $is_oneshot = false, $time = false, $next_resumption = false ) { //phpcs:ignore -- NOSONAR - complex. - - $ret = ''; - - /** @global object $updraftplus UpdraftPlus object. */ - global $updraftplus; - $jobdata = $updraftplus->jobdata_getarray( $job_id ); - - if ( false === apply_filters( 'updraftplus_print_active_job_continue', true, $is_oneshot, $next_resumption, $jobdata ) ) { - return ''; - } + // 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 = '
' . esc_html__('The backup apparently succeeded and is now complete', 'updraftplus') . '
'; + } + } - if ( ! isset( $jobdata['backup_time'] ) ) { - return ''; - } + return $ret; + } + + /** + * Display active job. + * + * @param string $job_id Job ID. + * @param bool $is_oneshot Whether or not this is a one time backup. + * @param bool $time Backup time. + * @param bool $next_resumption Next backups resumption. + * + * @return string Active job html. + * + * @uses $updraftplus::jobdata_getarray() + * @uses $updraftplus::get_backupable_file_entities() + */ + private function print_active_job($job_id, $is_oneshot = false, $time = false, $next_resumption = false) { //phpcs:ignore -- NOSONAR - complex. + + $ret = ''; + + /** @global object $updraftplus UpdraftPlus object. */ + global $updraftplus; + $jobdata = $updraftplus->jobdata_getarray($job_id); + + if (false === apply_filters('updraftplus_print_active_job_continue', true, $is_oneshot, $next_resumption, $jobdata)) { + return ''; + } - $backupable_entities = $updraftplus->get_backupable_file_entities( true, true ); - - $began_at = ( isset( $jobdata['backup_time'] ) ) ? get_date_from_gmt( gmdate( 'Y-m-d H:i:s', (int) $jobdata['backup_time'] ), 'D, F j, Y H:i' ) : '?'; - - $jobstatus = empty( $jobdata['jobstatus'] ) ? 'unknown' : $jobdata['jobstatus']; - $stage = 0; - switch ( $jobstatus ) { - case 'begun': - $curstage = esc_html__( 'Backup begun', 'updraftplus' ); - break; - case 'filescreating': - $stage = 1; - $curstage = esc_html__( 'Creating file backup zips', 'updraftplus' ); - if ( ! empty( $jobdata['filecreating_substatus'] ) && isset( $backupable_entities[ $jobdata['filecreating_substatus']['e'] ]['description'] ) ) { - $sdescrip = preg_replace( '/ \(.*\)$/', '', $backupable_entities[ $jobdata['filecreating_substatus']['e'] ]['description'] ); - if ( strlen( $sdescrip ) > 20 && isset( $jobdata['filecreating_substatus']['e'] ) && is_array( $jobdata['filecreating_substatus']['e'] ) && isset( $backupable_entities[ $jobdata['filecreating_substatus']['e'] ]['shortdescription'] ) ) { - $sdescrip = $backupable_entities[ $jobdata['filecreating_substatus']['e'] ]['shortdescription']; - } - $curstage .= ' (' . $sdescrip . ')'; - if ( isset( $jobdata['filecreating_substatus']['i'] ) && isset( $jobdata['filecreating_substatus']['t'] ) ) { - $stage = min( 2, 1 + ( $jobdata['filecreating_substatus']['i'] / max( $jobdata['filecreating_substatus']['t'], 1 ) ) ); - } - } - break; - case 'filescreated': - $stage = 2; - $curstage = esc_html__( 'Created file backup zips', 'updraftplus' ); - break; - case 'clouduploading': - $stage = 4; - $curstage = esc_html__( 'Uploading files to remote storage', 'updraftplus' ); - if ( isset( $jobdata['uploading_substatus']['t'] ) && isset( $jobdata['uploading_substatus']['i'] ) ) { - $t = max( (int) $jobdata['uploading_substatus']['t'], 1 ); - $i = min( $jobdata['uploading_substatus']['i'] / $t, 1 ); - $p = min( $jobdata['uploading_substatus']['p'], 1 ); - $pd = $i + $p / $t; - $stage = 4 + $pd; - $curstage .= ' ' . sprintf( esc_html__( '(%1$s%%, file %2$s of %3$s)', 'updraftplus' ), floor( 100 * $pd ), $jobdata['uploading_substatus']['i'] + 1, $t ); - } - break; - case 'pruning': - $stage = 5; - $curstage = esc_html__( 'Pruning old backup sets', 'updraftplus' ); - break; - case 'resumingforerrors': - $stage = - 1; - $curstage = esc_html__( 'Waiting until scheduled time to retry because of errors', 'updraftplus' ); - break; - case 'finished': - $stage = 6; - $curstage = esc_html__( 'Backup finished', 'updraftplus' ); - break; - default: - // Database creation and encryption occupies the space from 2 to 4. Databases are created then encrypted, then the next databae is created/encrypted, etc. - if ( 'dbcreated' === substr( $jobstatus, 0, 9 ) ) { - $jobstatus = 'dbcreated'; - $whichdb = substr( $jobstatus, 9 ); - if ( ! is_numeric( $whichdb ) ) { - $whichdb = 0; - } - $howmanydbs = max( ( empty( $jobdata['backup_database'] ) || ! is_array( $jobdata['backup_database'] ) ) ? 1 : count( $jobdata['backup_database'] ), 1 ); - $perdbspace = 2 / $howmanydbs; - - $stage = min( 4, 2 + ( $whichdb + 2 ) * $perdbspace ); - - $curstage = esc_html__( 'Created database backup', 'updraftplus' ); - - } elseif ( 'dbcreating' === substr( $jobstatus, 0, 10 ) ) { - $whichdb = substr( $jobstatus, 10 ); - if ( ! is_numeric( $whichdb ) ) { - $whichdb = 0; - } - $howmanydbs = ( empty( $jobdata['backup_database'] ) || ! is_array( $jobdata['backup_database'] ) ) ? 1 : count( $jobdata['backup_database'] ); - $perdbspace = 2 / $howmanydbs; - - $stage = min( 4, 2 + $whichdb * $perdbspace ); - - $curstage = esc_html__( 'Creating database backup', 'updraftplus' ); - if ( ! empty( $jobdata['dbcreating_substatus']['t'] ) ) { - $curstage .= ' (' . sprintf( esc_html__( 'table: %s', 'updraftplus' ), $jobdata['dbcreating_substatus']['t'] ) . ')'; - if ( ! empty( $jobdata['dbcreating_substatus']['i'] ) && ! empty( $jobdata['dbcreating_substatus']['a'] ) ) { - $substage = max( 0.001, ( $jobdata['dbcreating_substatus']['i'] / max( $jobdata['dbcreating_substatus']['a'], 1 ) ) ); - $stage += $substage * $perdbspace * 0.5; - } - } - } elseif ( 'dbencrypting' === substr( $jobstatus, 0, 12 ) ) { - $whichdb = substr( $jobstatus, 12 ); - if ( ! is_numeric( $whichdb ) ) { - $whichdb = 0; - } - $howmanydbs = ( empty( $jobdata['backup_database'] ) || ! is_array( $jobdata['backup_database'] ) ) ? 1 : count( $jobdata['backup_database'] ); - $perdbspace = 2 / $howmanydbs; - $stage = min( 4, 2 + $whichdb * $perdbspace + $perdbspace * 0.5 ); - $curstage = esc_html__( 'Encrypting database', 'updraftplus' ); - } elseif ( 'dbencrypted' === substr( $jobstatus, 0, 11 ) ) { - $whichdb = substr( $jobstatus, 11 ); - if ( ! is_numeric( $whichdb ) ) { - $whichdb = 0; - } - $howmanydbs = ( empty( $jobdata['backup_database'] ) || ! is_array( $jobdata['backup_database'] ) ) ? 1 : count( $jobdata['backup_database'] ); - $perdbspace = 2 / $howmanydbs; - $stage = min( 4, 2 + $whichdb * $perdbspace + $perdbspace ); - $curstage = esc_html__( 'Encrypted database', 'updraftplus' ); - } else { - $curstage = esc_html__( 'Unknown', 'updraftplus' ); - } - } + if (! isset($jobdata['backup_time'])) { + return ''; + } - $runs_started = ( empty( $jobdata['runs_started'] ) ) ? array() : $jobdata['runs_started']; - $time_passed = ( empty( $jobdata['run_times'] ) ) ? array() : $jobdata['run_times']; - $last_checkin_ago = - 1; - if ( is_array( $time_passed ) ) { - foreach ( $time_passed as $run => $passed ) { - if ( isset( $runs_started[ $run ] ) ) { - $time_ago = microtime( true ) - ( $runs_started[ $run ] + $time_passed[ $run ] ); - if ( $time_ago < $last_checkin_ago || -1 === $last_checkin_ago ) { - $last_checkin_ago = $time_ago; - } - } + $backupable_entities = $updraftplus->get_backupable_file_entities(true, true); + + $began_at = (isset($jobdata['backup_time'])) ? get_date_from_gmt(gmdate('Y-m-d H:i:s', (int) $jobdata['backup_time']), 'D, F j, Y H:i') : '?'; + + $jobstatus = empty($jobdata['jobstatus']) ? 'unknown' : $jobdata['jobstatus']; + $stage = 0; + $progress_class = ''; + switch ($jobstatus) { + case 'begun': + $curstage = esc_html__('Backup begun', 'updraftplus'); + break; + case 'filescreating': + $stage = 1; + $curstage = esc_html__('Creating file backup zips', 'updraftplus'); + if (! empty($jobdata['filecreating_substatus']) && isset($backupable_entities[$jobdata['filecreating_substatus']['e']]['description'])) { + $sdescrip = preg_replace('/ \(.*\)$/', '', $backupable_entities[$jobdata['filecreating_substatus']['e']]['description']); + if (strlen($sdescrip) > 20 && isset($jobdata['filecreating_substatus']['e']) && is_array($jobdata['filecreating_substatus']['e']) && isset($backupable_entities[$jobdata['filecreating_substatus']['e']]['shortdescription'])) { + $sdescrip = $backupable_entities[$jobdata['filecreating_substatus']['e']]['shortdescription']; + } + $curstage .= ' (' . $sdescrip . ')'; + if (isset($jobdata['filecreating_substatus']['i']) && isset($jobdata['filecreating_substatus']['t'])) { + $stage = min(2, 1 + ($jobdata['filecreating_substatus']['i'] / max($jobdata['filecreating_substatus']['t'], 1))); + } + } + break; + case 'filescreated': + $stage = 2; + $curstage = esc_html__('Created file backup zips', 'updraftplus'); + break; + case 'clouduploading': + $stage = 4; + $curstage = esc_html__('Uploading files to remote storage', 'updraftplus'); + if (isset($jobdata['uploading_substatus']['t']) && isset($jobdata['uploading_substatus']['i'])) { + $t = max((int) $jobdata['uploading_substatus']['t'], 1); + $i = min($jobdata['uploading_substatus']['i'] / $t, 1); + $p = min($jobdata['uploading_substatus']['p'], 1); + $pd = $i + $p / $t; + $stage = 4 + $pd; + $curstage .= ' ' . sprintf(esc_html__('(%1$s%%, file %2$s of %3$s)', 'updraftplus'), floor(100 * $pd), $jobdata['uploading_substatus']['i'] + 1, $t); + } + break; + case 'pruning': + $stage = 5; + $curstage = esc_html__('Pruning old backup sets', 'updraftplus'); + break; + case 'resumingforerrors': + $progress_class = 'error'; + $stage = -1; + $curstage = esc_html__('Waiting until scheduled time to retry because of errors', 'updraftplus'); + break; + case 'finished': + $progress_class = 'success'; + $stage = 6; + $curstage = esc_html__('Backup finished', 'updraftplus'); + break; + default: + // Database creation and encryption occupies the space from 2 to 4. Databases are created then encrypted, then the next databae is created/encrypted, etc. + if ('dbcreated' === substr($jobstatus, 0, 9)) { + $jobstatus = 'dbcreated'; + $whichdb = substr($jobstatus, 9); + if (! is_numeric($whichdb)) { + $whichdb = 0; + } + $howmanydbs = max((empty($jobdata['backup_database']) || ! is_array($jobdata['backup_database'])) ? 1 : count($jobdata['backup_database']), 1); + $perdbspace = 2 / $howmanydbs; + + $stage = min(4, 2 + ($whichdb + 2) * $perdbspace); + + $curstage = esc_html__('Created database backup', 'updraftplus'); + } elseif ('dbcreating' === substr($jobstatus, 0, 10)) { + $whichdb = substr($jobstatus, 10); + if (! is_numeric($whichdb)) { + $whichdb = 0; + } + $howmanydbs = (empty($jobdata['backup_database']) || ! is_array($jobdata['backup_database'])) ? 1 : count($jobdata['backup_database']); + $perdbspace = 2 / $howmanydbs; + + $stage = min(4, 2 + $whichdb * $perdbspace); + + $curstage = esc_html__('Creating database backup', 'updraftplus'); + if (! empty($jobdata['dbcreating_substatus']['t'])) { + $curstage .= ' (' . sprintf(esc_html__('table: %s', 'updraftplus'), $jobdata['dbcreating_substatus']['t']) . ')'; + if (! empty($jobdata['dbcreating_substatus']['i']) && ! empty($jobdata['dbcreating_substatus']['a'])) { + $substage = max(0.001, ($jobdata['dbcreating_substatus']['i'] / max($jobdata['dbcreating_substatus']['a'], 1))); + $stage += $substage * $perdbspace * 0.5; } - } - - $next_res_after = (int) $time - time(); - $next_res_txt = ( $is_oneshot ) ? '' : ' - ' . sprintf( esc_html__( 'next resumption: %1$d (after %2$ss)', 'updraftplus' ), $next_resumption, $next_res_after ) . ' '; - $last_activity_txt = ( $last_checkin_ago >= 0 ) ? ' - ' . sprintf( esc_html__( 'last activity: %ss ago', 'updraftplus' ), floor( $last_checkin_ago ) ) . ' ' : ''; - - if ( ( $last_checkin_ago < 50 && $next_res_after > 30 ) || $is_oneshot ) { - $show_inline_info = $last_activity_txt; - $title_info = $next_res_txt; + } + } elseif ('dbencrypting' === substr($jobstatus, 0, 12)) { + $whichdb = substr($jobstatus, 12); + if (! is_numeric($whichdb)) { + $whichdb = 0; + } + $howmanydbs = (empty($jobdata['backup_database']) || ! is_array($jobdata['backup_database'])) ? 1 : count($jobdata['backup_database']); + $perdbspace = 2 / $howmanydbs; + $stage = min(4, 2 + $whichdb * $perdbspace + $perdbspace * 0.5); + $curstage = esc_html__('Encrypting database', 'updraftplus'); + } elseif ('dbencrypted' === substr($jobstatus, 0, 11)) { + $whichdb = substr($jobstatus, 11); + if (! is_numeric($whichdb)) { + $whichdb = 0; + } + $howmanydbs = (empty($jobdata['backup_database']) || ! is_array($jobdata['backup_database'])) ? 1 : count($jobdata['backup_database']); + $perdbspace = 2 / $howmanydbs; + $stage = min(4, 2 + $whichdb * $perdbspace + $perdbspace); + $curstage = esc_html__('Encrypted database', 'updraftplus'); } else { - $show_inline_info = $next_res_txt; - $title_info = $last_activity_txt; + $curstage = esc_html__('Unknown', 'updraftplus'); } + } - // Existence of the 'updraft-jobid-(id)' id is checked for in other places, so do not modify this. - $ret .= '
' . $began_at . ' '; - - $ret .= $show_inline_info; - $ret .= '- ' . esc_html__( 'show log', 'updraftplus' ) . ''; + $runs_started = (empty($jobdata['runs_started'])) ? array() : $jobdata['runs_started']; + $time_passed = (empty($jobdata['run_times'])) ? array() : $jobdata['run_times']; + $last_checkin_ago = -1; + if (is_array($time_passed)) { + foreach ($time_passed as $run => $passed) { + if (isset($runs_started[$run])) { + $time_ago = microtime(true) - ($runs_started[$run] + $time_passed[$run]); + if ($time_ago < $last_checkin_ago || -1 === $last_checkin_ago) { + $last_checkin_ago = $time_ago; + } + } + } + } - if ( ! $is_oneshot ) { - $ret .= ' - ' . esc_html__( 'delete schedule', 'updraftplus' ) . ''; - } + $next_res_after = (int) $time - time(); + $next_res_txt = ($is_oneshot) ? '' : ' - ' . sprintf(esc_html__('next resumption: %1$d (after %2$ss)', 'updraftplus'), $next_resumption, $next_res_after) . ' '; + $last_activity_txt = ($last_checkin_ago >= 0) ? ' - ' . sprintf(esc_html__('last activity: %ss ago', 'updraftplus'), floor($last_checkin_ago)) . ' ' : ''; - if ( ! empty( $jobdata['warnings'] ) && is_array( $jobdata['warnings'] ) ) { - $ret .= '
    '; - foreach ( $jobdata['warnings'] as $warning ) { - $ret .= '
  • ' . sprintf( esc_html__( 'Warning: %s', 'updraftplus' ), make_clickable( htmlspecialchars( $warning ) ) ) . '
  • '; - } - $ret .= '
'; - } + if (($last_checkin_ago < 50 && $next_res_after > 30) || $is_oneshot) { + $show_inline_info = $last_activity_txt; + $title_info = $next_res_txt; + } else { + $show_inline_info = $next_res_txt; + $title_info = $last_activity_txt; + } + $progress = ( $stage > 0 ) ? ( ceil( ( 100 / 6 ) * $stage ) ) : '0' ; - $ret .= '
'; - $ret .= htmlspecialchars( $curstage ); - $ret .= '
'; - $ret .= '
'; + // Existence of the 'updraft-jobid-(id)' id is checked for in other places, so do not modify this. + $ret .= '
' . $began_at . ' '; - $ret .= '
'; + $ret .= $show_inline_info; + $ret .= '- ' . esc_html__('show log', 'updraftplus') . ''; - return $ret; + if (! $is_oneshot) { + $ret .= ' - ' . esc_html__('delete schedule', 'updraftplus') . '
'; } - - /** - * Fetch Updraft Log. - * - * @return array|string[] Return log content, nonce & pointer. ERROR on failure. - * - * @uses MainWP_Child_Updraft_Plus_Backups::fetch_log() - */ - private function fetch_updraft_log() { - $backup_nonce = isset( $_POST['backup_nonce'] ) ? sanitize_text_field( wp_unslash( $_POST['backup_nonce'] ) ) : ''; - - return $this->fetch_log( $backup_nonce ); + $ret .= '
'; + + if (! empty($jobdata['warnings']) && is_array($jobdata['warnings'])) { + $ret .= '
    '; + foreach ($jobdata['warnings'] as $warning) { + $ret .= '
  • ' . sprintf(esc_html__('Warning: %s', 'updraftplus'), make_clickable(htmlspecialchars($warning))) . '
  • '; + } + $ret .= '
'; } - /** - * Delete active jobs. - * - * @return array|string[] Return Y Job has been deleted or N Job not found. - */ - private function activejobs_delete() { //phpcs:ignore -- NOSONAR - complex. - $jobid = isset( $_POST['jobid'] ) ? sanitize_text_field( wp_unslash( $_POST['jobid'] ) ) : ''; - if ( empty( $jobid ) ) { - return array( 'error' => 'Error: empty job id.' ); - } + $ret .= '
+
+
+
'. esc_attr( $progress) .'%
+
+
'. htmlspecialchars( $curstage ) .'
+
+
'; + + $ret .= '
'; + return $ret; + } + + /** + * Fetch Updraft Log. + * + * @return array|string[] Return log content, nonce & pointer. ERROR on failure. + * + * @uses MainWP_Child_Updraft_Plus_Backups::fetch_log() + */ + private function fetch_updraft_log() + { + $backup_nonce = isset($_POST['backup_nonce']) ? sanitize_text_field(wp_unslash($_POST['backup_nonce'])) : ''; + + return $this->fetch_log($backup_nonce); + } + + /** + * Delete active jobs. + * + * @return array|string[] Return Y Job has been deleted or N Job not found. + */ + private function activejobs_delete() { //phpcs:ignore -- NOSONAR - complex. + $jobid = isset($_POST['jobid']) ? sanitize_text_field(wp_unslash($_POST['jobid'])) : ''; + if (empty($jobid)) { + return array('error' => 'Error: empty job id.'); + } - $cron = get_option( 'cron' ); - $found_it = 0; - foreach ( $cron as $time => $job ) { - if ( isset( $job['updraft_backup_resume'] ) ) { - foreach ( $job['updraft_backup_resume'] as $hook => $info ) { - if ( isset( $info['args'][1] ) && $info['args'][1] === $jobid ) { - $args = $cron[ $time ]['updraft_backup_resume'][ $hook ]['args']; - wp_unschedule_event( $time, 'updraft_backup_resume', $args ); - if ( ! $found_it ) { - return array( - 'ok' => 'Y', - 'm' => esc_html__( 'Job deleted', 'updraftplus' ), - ); - } - $found_it = 1; - } - } + $cron = get_option('cron'); + $found_it = 0; + foreach ($cron as $time => $job) { + if (isset($job['updraft_backup_resume'])) { + foreach ($job['updraft_backup_resume'] as $hook => $info) { + if (isset($info['args'][1]) && $info['args'][1] === $jobid) { + $args = $cron[$time]['updraft_backup_resume'][$hook]['args']; + wp_unschedule_event($time, 'updraft_backup_resume', $args); + if (! $found_it) { + return array( + 'ok' => 'Y', + 'm' => esc_html__('Job deleted', 'updraftplus'), + ); } + $found_it = 1; + } } - - if ( ! $found_it ) { - return array( - 'ok' => 'N', - 'm' => esc_html__( 'Could not find that job - perhaps it has already finished?', 'updraftplus' ), - ); - } - - return array(); + } } - /** - * Fetch log. - * - * @param string $backup_nonce Backup nonce. - * @param int $log_pointer Log pointer. - * - * @return array|string[] Return log content, nonce & pointer. ERROR on failure. - * - * @uses $updraftplus::last_modified_log() - * @uses $updraftplus::backups_dir_location() - */ - public function fetch_log( $backup_nonce, $log_pointer = 0 ) { //phpcs:ignore -- NOSONAR - complex. - - /** @global object $updraftplus UpdraftPlus object. */ - global $updraftplus; - - if ( empty( $backup_nonce ) ) { - list( $mod_time, $log_file, $nonce ) = $updraftplus->last_modified_log(); - unset( $log_file ); - unset( $mod_time ); - } else { - $nonce = $backup_nonce; - } - - if ( ! preg_match( '/^[0-9a-f]+$/', $nonce ) ) { - return array( 'error' => 'Security check' ); - } - - $log_content = ''; - $new_pointer = $log_pointer; + if (! $found_it) { + return array( + 'ok' => 'N', + 'm' => esc_html__('Could not find that job - perhaps it has already finished?', 'updraftplus'), + ); + } - if ( ! empty( $nonce ) ) { - $updraft_dir = $updraftplus->backups_dir_location(); + return array(); + } + + /** + * Fetch log. + * + * @param string $backup_nonce Backup nonce. + * @param int $log_pointer Log pointer. + * + * @return array|string[] Return log content, nonce & pointer. ERROR on failure. + * + * @uses $updraftplus::last_modified_log() + * @uses $updraftplus::backups_dir_location() + */ + public function fetch_log($backup_nonce, $log_pointer = 0) { //phpcs:ignore -- NOSONAR - complex. + + /** @global object $updraftplus UpdraftPlus object. */ + global $updraftplus; + + if (empty($backup_nonce)) { + list($mod_time, $log_file, $nonce) = $updraftplus->last_modified_log(); + unset($log_file); + unset($mod_time); + } else { + $nonce = $backup_nonce; + } - $potential_log_file = $updraft_dir . '/log.' . $nonce . '.txt'; + if (! preg_match('/^[0-9a-f]+$/', $nonce)) { + return array('error' => 'Security check'); + } - if ( is_readable( $potential_log_file ) ) { + $log_content = ''; + $new_pointer = $log_pointer; - $templog_array = array(); - $log_file = fopen( $potential_log_file, 'r' ); - if ( $log_pointer > 0 ) { - fseek( $log_file, $log_pointer ); - } + if (! empty($nonce)) { + $updraft_dir = $updraftplus->backups_dir_location(); - while ( ( $buffer = fgets( $log_file, 4096 ) ) !== false ) { - $templog_array[] = $buffer; - } - if ( ! feof( $log_file ) ) { - $templog_array[] = esc_html__( 'Error: unexpected file read fail', 'updraftplus' ); - } + $potential_log_file = $updraft_dir . '/log.' . $nonce . '.txt'; - $new_pointer = ftell( $log_file ); - $log_content = implode( '', $templog_array ); + if (is_readable($potential_log_file)) { - } else { - $log_content .= esc_html__( 'The log file could not be read.', 'updraftplus' ); - } - } else { - $log_content .= esc_html__( 'The log file could not be read.', 'updraftplus' ); + $templog_array = array(); + $log_file = fopen($potential_log_file, 'r'); + if ($log_pointer > 0) { + fseek($log_file, $log_pointer); } - return array( - 'html' => $log_content, - 'nonce' => $nonce, - 'pointer' => $new_pointer, - ); - } - /** - * Download status. - * - * @param string $timestamp Job timestamp - * @param string $type Job type. - * @param string $findex File Index. - * - * @return string[] Return $response array. - * - * @uses $updraftplus::jobdata_get() - */ - private function download_status( $timestamp, $type, $findex ) { //phpcs:ignore -- NOSONAR - complex. - - /** @global object $updraftplus UpdraftPlus object. */ - global $updraftplus; - - $response = array( 'm' => $updraftplus->jobdata_get( 'dlmessage_' . $timestamp . '_' . $type . '_' . $findex ) . '
' ); - $file = $updraftplus->jobdata_get( 'dlfile_' . $timestamp . '_' . $type . '_' . $findex ); - if ( $file ) { - if ( 'failed' === $file ) { - $response['e'] = esc_html__( 'Download failed', 'updraftplus' ) . '
'; - $errs = $updraftplus->jobdata_get( 'dlerrors_' . $timestamp . '_' . $type . '_' . $findex ); - if ( is_array( $errs ) && ! empty( $errs ) ) { - $response['e'] .= '
    '; - foreach ( $errs as $err ) { - if ( is_array( $err ) ) { - $response['e'] .= '
  • ' . htmlspecialchars( $err['message'] ) . '
  • '; - } else { - $response['e'] .= '
  • ' . htmlspecialchars( $err ) . '
  • '; - } - } - $response['e'] .= '
'; - } - } elseif ( preg_match( '/^downloaded:(\d+):(.*)$/', $file, $matches ) && file_exists( $matches[2] ) ) { - $response['p'] = 100; - $response['f'] = $matches[2]; - $response['s'] = (int) $matches[1]; - $response['t'] = (int) $matches[1]; - $response['m'] = esc_html__( 'File ready.', 'updraftplus' ); - } elseif ( preg_match( '/^downloading:(\d+):(.*)$/', $file, $matches ) && file_exists( $matches[2] ) ) { - // Convert to bytes. - $response['f'] = $matches[2]; - $total_size = (int) max( $matches[1], 1 ); - $cur_size = filesize( $matches[2] ); - $response['s'] = $cur_size; - $file_age = time() - filemtime( $matches[2] ); - if ( $file_age > 20 ) { - $response['a'] = time() - filemtime( $matches[2] ); - } - $response['t'] = $total_size; - $response['m'] .= esc_html__( 'Download in progress', 'updraftplus' ) . ' (' . round( $cur_size / 1024 ) . ' / ' . round( ( $total_size / 1024 ) ) . ' Kb)'; - $response['p'] = round( 100 * $cur_size / $total_size ); - } else { - $response['m'] .= esc_html__( 'No local copy present.', 'updraftplus' ); - $response['p'] = 0; - $response['s'] = 0; - $response['t'] = 1; - } + while (($buffer = fgets($log_file, 4096)) !== false) { + $templog_array[] = $buffer; + } + if (! feof($log_file)) { + $templog_array[] = esc_html__('Error: unexpected file read fail', 'updraftplus'); } - return $response; + $new_pointer = ftell($log_file); + $log_content = implode('', $templog_array); + } else { + $log_content .= esc_html__('The log file could not be read.', 'updraftplus'); + } + } else { + $log_content .= esc_html__('The log file could not be read.', 'updraftplus'); } - - /** - * Close browser connection. - * - * @param string $txt Return Base64 Encoded output. - */ - public function close_browser_connection($txt = '') { - $output = wp_json_encode( $txt ); - $txt = '' . base64_encode( $output ) . ''; // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions -- base64_encode function is used for http encode compatible.. - - // Close browser connection so that it can resume AJAX polling - header('Content-Length: '.(empty($txt) ? '0' : 4+strlen($txt))); - header('Connection: close'); - header('Content-Encoding: none'); - if (session_id()) { - session_write_close(); - } - echo "\r\n\r\n"; - echo $txt; - // These two added - 19-Feb-15 - started being required on local dev machine, for unknown reason (probably some plugin that started an output buffer). - $ob_level = ob_get_level(); - while ($ob_level > 0) { - ob_end_flush(); - $ob_level--; - } - flush(); - if ( function_exists('fastcgi_finish_request') ) { - fastcgi_finish_request(); - } + return array( + 'html' => $log_content, + 'nonce' => $nonce, + 'pointer' => $new_pointer, + ); + } + + /** + * Download status. + * + * @param string $timestamp Job timestamp + * @param string $type Job type. + * @param string $findex File Index. + * + * @return string[] Return $response array. + * + * @uses $updraftplus::jobdata_get() + */ + private function download_status($timestamp, $type, $findex) { //phpcs:ignore -- NOSONAR - complex. + + /** @global object $updraftplus UpdraftPlus object. */ + global $updraftplus, $updraftplus_admin; + if (empty($updraftplus_admin)) { + include_once UPDRAFTPLUS_DIR . '/admin.php'; // NOSONAR - compatible. } - - /** - * Initiate UpdraftPlus. - * - * @uses MainWP_Child_Updraft_Plus_Backups::is_plugin_installed() - */ - public function updraftplus_init() { - if ( ! $this->is_plugin_installed ) { - return; - } - - if ( get_option( 'mainwp_updraftplus_hide_plugin' ) === 'hide' ) { - add_filter( 'all_plugins', array( $this, 'all_plugins' ) ); - add_action( 'admin_menu', array( $this, 'remove_menu' ) ); - add_filter( 'site_transient_update_plugins', array( &$this, 'remove_update_nag' ) ); - add_filter( 'mainwp_child_hide_update_notice', array( &$this, 'hide_update_notice' ) ); - add_action( 'wp_before_admin_bar_render', array( $this, 'wp_before_admin_bar_render' ), 99 ); - add_action( 'admin_init', array( $this, 'remove_notices' ) ); - } + $response = array('m' => $updraftplus->jobdata_get('dlmessage_' . $timestamp . '_' . $type . '_' . $findex) . '
'); + $file = $updraftplus->jobdata_get('dlfile_' . $timestamp . '_' . $type . '_' . $findex); + if (empty($file)) { + $updraftplus_admin->do_updraft_download_backup($findex, $type, $timestamp, '', false, ''); + $file = $updraftplus->jobdata_get('dlfile_' . $timestamp . '_' . $type . '_' . $findex); } - - /** - * Remove admin notices. - * - * @uses MainWP_Child_Updraft_Plus_Backups::remove_filters_for_anonymous_class() - */ - public function remove_notices() { - $remove_hooks['all_admin_notices'] = array( - 'UpdraftPlus' => array( - 'show_admin_warning_unreadablelog' => 10, - 'show_admin_warning_nolog' => 10, - 'show_admin_warning_unreadablefile' => 10, - ), - 'UpdraftPlus_BackupModule_dropbox' => array( - 'show_authed_admin_warning' => 10, - ), - 'UpdraftPlus_BackupModule_googledrive' => array( - 'show_authed_admin_success' => 10, - ), - ); - - foreach ( $remove_hooks as $hook_name => $hooks ) { - foreach ( $hooks as $class_name => $methods ) { - foreach ( $methods as $method => $priority ) { - static::remove_filters_for_anonymous_class( $hook_name, $class_name, $method, $priority ); - } + if ($file) { + + if ('failed' === $file) { + $response['e'] = esc_html__('Download failed', 'updraftplus') . '
'; + $errs = $updraftplus->jobdata_get('dlerrors_' . $timestamp . '_' . $type . '_' . $findex); + if (is_array($errs) && ! empty($errs)) { + $response['e'] .= '
    '; + foreach ($errs as $err) { + if (is_array($err)) { + $response['e'] .= '
  • ' . htmlspecialchars($err['message']) . '
  • '; + } else { + $response['e'] .= '
  • ' . htmlspecialchars($err) . '
  • '; } - } + } + $response['e'] .= '
'; + } + } elseif (preg_match('/^downloaded:(\d+):(.*)$/', $file, $matches) && file_exists($matches[2])) { + $response['p'] = 100; + $response['f'] = $matches[2]; + $response['s'] = (int) $matches[1]; + $response['t'] = (int) $matches[1]; + $response['m'] = esc_html__('File ready.', 'updraftplus'); + } elseif (preg_match('/^downloading:(\d+):(.*)$/', $file, $matches) && file_exists($matches[2])) { + // Convert to bytes. + $response['f'] = $matches[2]; + $total_size = (int) max($matches[1], 1); + $cur_size = filesize($matches[2]); + $response['s'] = $cur_size; + $file_age = time() - filemtime($matches[2]); + if ($file_age > 20) { + $response['a'] = time() - filemtime($matches[2]); + } + $response['t'] = $total_size; + $response['m'] .= esc_html__('Download in progress', 'updraftplus') . ' (' . round($cur_size / 1024) . ' / ' . round(($total_size / 1024)) . ' Kb)'; + $response['p'] = round(100 * $cur_size / $total_size); + } else { + $response['m'] .= esc_html__('No local copy present.', 'updraftplus'); + $response['p'] = 0; + $response['s'] = 0; + $response['t'] = 1; + } } - /** - * Allows removal of method for a hook when the class doesn't have a variable but you know the class name. - * - * @param string $hook_name Hook name. - * @param string $class_name Class name. - * @param string $method_name Method name. - * @param int $priority Priority. - * - * @return bool Return FALSE on failure. - */ - public static function remove_filters_for_anonymous_class( $hook_name = '', $class_name = '', $method_name = '', $priority = 0 ) { - - /** @global object $wp_filter Wordpress filter object. */ - global $wp_filter; - - // Take only filters on right hook name and priority. - if ( ! isset( $wp_filter[ $hook_name ] ) || ! isset( $wp_filter[ $hook_name ][ $priority ] ) || ! is_array( $wp_filter[ $hook_name ][ $priority ] ) ) { - return false; - } + return $response; + } + + /** + * Close browser connection. + * + * @param string $txt Return Base64 Encoded output. + */ + public function close_browser_connection($txt = '') + { + $output = wp_json_encode($txt); + $txt = '' . base64_encode($output) . ''; // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions -- base64_encode function is used for http encode compatible.. + + // Close browser connection so that it can resume AJAX polling + header('Content-Length: ' . (empty($txt) ? '0' : 4 + strlen($txt))); + header('Connection: close'); + header('Content-Encoding: none'); + if (session_id()) { + session_write_close(); + } + echo "\r\n\r\n"; + echo $txt; + // These two added - 19-Feb-15 - started being required on local dev machine, for unknown reason (probably some plugin that started an output buffer). + $ob_level = ob_get_level(); + while ($ob_level > 0) { + ob_end_flush(); + $ob_level--; + } + flush(); + if (function_exists('fastcgi_finish_request')) { + fastcgi_finish_request(); + } + } + + /** + * Initiate UpdraftPlus. + * + * @uses MainWP_Child_Updraft_Plus_Backups::is_plugin_installed() + */ + public function updraftplus_init() + { + if (! $this->is_plugin_installed) { + return; + } - // Loop on filters registered. - foreach ( (array) $wp_filter[ $hook_name ][ $priority ] as $unique_id => $filter_array ) { - // Test if filter is an array ! (always for class/method). - if ( isset( $filter_array['function'] ) && is_array( $filter_array['function'] ) && is_object( $filter_array['function'][0] ) && get_class( $filter_array['function'][0] ) && get_class( $filter_array['function'][0] ) === $class_name && $filter_array['function'][1] === $method_name ) { - unset( $wp_filter[ $hook_name ][ $priority ][ $unique_id ] ); - } - } + if (get_option('mainwp_updraftplus_hide_plugin') === 'hide') { + add_filter('all_plugins', array($this, 'all_plugins')); + add_action('admin_menu', array($this, 'remove_menu')); + add_filter('site_transient_update_plugins', array(&$this, 'remove_update_nag')); + add_filter('mainwp_child_hide_update_notice', array(&$this, 'hide_update_notice')); + add_action('wp_before_admin_bar_render', array($this, 'wp_before_admin_bar_render'), 99); + add_action('admin_init', array($this, 'remove_notices')); + } + } + + /** + * Remove admin notices. + * + * @uses MainWP_Child_Updraft_Plus_Backups::remove_filters_for_anonymous_class() + */ + public function remove_notices() + { + $remove_hooks['all_admin_notices'] = array( + 'UpdraftPlus' => array( + 'show_admin_warning_unreadablelog' => 10, + 'show_admin_warning_nolog' => 10, + 'show_admin_warning_unreadablefile' => 10, + ), + 'UpdraftPlus_BackupModule_dropbox' => array( + 'show_authed_admin_warning' => 10, + ), + 'UpdraftPlus_BackupModule_googledrive' => array( + 'show_authed_admin_success' => 10, + ), + ); + + foreach ($remove_hooks as $hook_name => $hooks) { + foreach ($hooks as $class_name => $methods) { + foreach ($methods as $method => $priority) { + static::remove_filters_for_anonymous_class($hook_name, $class_name, $method, $priority); + } + } + } + } + + /** + * Allows removal of method for a hook when the class doesn't have a variable but you know the class name. + * + * @param string $hook_name Hook name. + * @param string $class_name Class name. + * @param string $method_name Method name. + * @param int $priority Priority. + * + * @return bool Return FALSE on failure. + */ + public static function remove_filters_for_anonymous_class($hook_name = '', $class_name = '', $method_name = '', $priority = 0) + { + + /** @global object $wp_filter Wordpress filter object. */ + global $wp_filter; + + // Take only filters on right hook name and priority. + if (! isset($wp_filter[$hook_name]) || ! isset($wp_filter[$hook_name][$priority]) || ! is_array($wp_filter[$hook_name][$priority])) { + return false; + } - return false; + // Loop on filters registered. + foreach ((array) $wp_filter[$hook_name][$priority] as $unique_id => $filter_array) { + // Test if filter is an array ! (always for class/method). + if (isset($filter_array['function']) && is_array($filter_array['function']) && is_object($filter_array['function'][0]) && get_class($filter_array['function'][0]) && get_class($filter_array['function'][0]) === $class_name && $filter_array['function'][1] === $method_name) { + unset($wp_filter[$hook_name][$priority][$unique_id]); + } } - /** - * Render before admin bar. - * - * @uses $wp_admin_bar::get_nodes() - * @uses $wp_admin_bar::remove_node() - */ - public function wp_before_admin_bar_render() { //phpcs:ignore -- NOSONAR - complex. - - /** @global object $wp_admin_bar WordPress Admin Bar object. */ - global $wp_admin_bar; - - $nodes = $wp_admin_bar->get_nodes(); - if ( is_array( $nodes ) ) { - foreach ( $nodes as $node ) { - if ( is_array( $nodes ) ) { - foreach ( $nodes as $node ) { - if ( 'updraft_admin_node' === $node->parent || ( 'updraft_admin_node' === $node->id ) ) { - $wp_admin_bar->remove_node( $node->id ); - } - } - } + return false; + } + + /** + * Render before admin bar. + * + * @uses $wp_admin_bar::get_nodes() + * @uses $wp_admin_bar::remove_node() + */ + public function wp_before_admin_bar_render() { //phpcs:ignore -- NOSONAR - complex. + + /** @global object $wp_admin_bar WordPress Admin Bar object. */ + global $wp_admin_bar; + + $nodes = $wp_admin_bar->get_nodes(); + if (is_array($nodes)) { + foreach ($nodes as $node) { + if (is_array($nodes)) { + foreach ($nodes as $node) { + if ('updraft_admin_node' === $node->parent || ('updraft_admin_node' === $node->id)) { + $wp_admin_bar->remove_node($node->id); } + } } + } } - - /** - * Hide UpdraftPlus notices. - * - * @param string $slugs Plugin slugs. - * - * @return string $slugs Plugin slugs. - */ - public function hide_update_notice( $slugs ) { - $slugs[] = 'updraftplus/updraftplus.php'; - return $slugs; + } + + /** + * Hide UpdraftPlus notices. + * + * @param string $slugs Plugin slugs. + * + * @return string $slugs Plugin slugs. + */ + public function hide_update_notice($slugs) + { + $slugs[] = $this->updraftplus_file_path; + return $slugs; + } + + /** + * Remove UpdraftPlus update nag. + * + * @param string $value String to remove. + * + * @return mixed $value Return response. + * + * @uses \MainWP\Child\MainWP_Helper::is_updates_screen() + */ + public function remove_update_nag($value) + { + if (MainWP_Helper::is_dashboard_request()) { + return $value; } - - /** - * Remove UpdraftPlus update nag. - * - * @param string $value String to remove. - * - * @return mixed $value Return response. - * - * @uses \MainWP\Child\MainWP_Helper::is_updates_screen() - */ - public function remove_update_nag( $value ) { - if ( MainWP_Helper::is_dashboard_request() ) { - return $value; - } - if ( ! MainWP_Helper::is_updates_screen() ) { - return $value; - } - - if ( isset( $value->response['updraftplus/updraftplus.php'] ) ) { - unset( $value->response['updraftplus/updraftplus.php'] ); - } - - return $value; + if (! MainWP_Helper::is_updates_screen()) { + return $value; } - /** - * Get sync data. - * - * @param bool $with_hist Whether or not to include history. - * - * @uses MainWP_Child_Updraft_Plus_Backups::required_files() - * @uses MainWP_Child_Updraft_Plus_Backups::get_updraft_data() - */ - public function get_sync_data( $with_hist = false ) { - $this->required_files(); - return $this->get_updraft_data( $with_hist ); + if (isset($value->response[$this->updraftplus_file_path])) { + unset($value->response[$this->updraftplus_file_path]); } - /** - * Remove UpdraftPlus from plugins page. - * - * @param array $plugins All plugins array. - * - * @return array $plugins All plugins array with UpdraftPlus removed. - */ - public function all_plugins( $plugins ) { - foreach ( $plugins as $key => $value ) { - $plugin_slug = basename( $key, '.php' ); - if ( 'updraftplus' === $plugin_slug ) { - unset( $plugins[ $key ] ); - } - } - - return $plugins; + return $value; + } + + /** + * Get sync data. + * + * @param bool $with_hist Whether or not to include history. + * + * @uses MainWP_Child_Updraft_Plus_Backups::required_files() + * @uses MainWP_Child_Updraft_Plus_Backups::get_updraft_data() + */ + public function get_sync_data($with_hist = false) + { + $this->required_files(); + return $this->get_updraft_data($with_hist); + } + + /** + * Remove UpdraftPlus from plugins page. + * + * @param array $plugins All plugins array. + * + * @return array $plugins All plugins array with UpdraftPlus removed. + */ + public function all_plugins($plugins) + { + foreach ($plugins as $key => $value) { + $plugin_slug = basename($key, '.php'); + if ('updraftplus' === $plugin_slug) { + unset($plugins[$key]); + } } - /** - * Remove UpdraftPlus from WP Admin menu. - */ - public function remove_menu() { + return $plugins; + } - /** @global object $submenu WordPress submenu object. */ - global $submenu; + /** + * Remove UpdraftPlus from WP Admin menu. + */ + public function remove_menu() { - if ( isset( $submenu['options-general.php'] ) ) { - foreach ( $submenu['options-general.php'] as $index => $item ) { - if ( 'updraftplus' === $item[2] ) { - unset( $submenu['options-general.php'][ $index ] ); - break; - } - } - } + /** @global object $submenu WordPress submenu object. */ + global $submenu; - $pos = isset( $_SERVER['REQUEST_URI'] ) ? stripos( wp_unslash( $_SERVER['REQUEST_URI'] ), 'options-general.php?page=updraftplus' ) : false; - if ( false !== $pos ) { - wp_safe_redirect( get_option( 'siteurl' ) . '/wp-admin/index.php' ); - exit(); + if (isset($submenu['options-general.php'])) { + foreach ($submenu['options-general.php'] as $index => $item) { + if ('updraftplus' === $item[2]) { + unset($submenu['options-general.php'][$index]); + break; } + } + } + + $pos = isset($_SERVER['REQUEST_URI']) ? stripos(wp_unslash($_SERVER['REQUEST_URI']), 'options-general.php?page=updraftplus') : false; + if (false !== $pos) { + wp_safe_redirect(get_option('siteurl') . '/wp-admin/index.php'); + exit(); } + + remove_menu_page( 'options-general.php?page=updraftplus' ); + } + + /** + * Generate random string using random_bytes + * + * @param int $length + * @return string random. + */ + public function generate_random_string($length = 12) + { + return bin2hex(random_bytes($length / 2)); + } }