Example #1
0
 function restore_backup($timestamp)
 {
     @set_time_limit(900);
     global $wp_filesystem, $updraftplus;
     $backup_history = UpdraftPlus_Options::get_updraft_option('updraft_backup_history');
     if (!is_array($backup_history[$timestamp])) {
         echo '<p>' . __('This backup does not exist in the backup history - restoration aborted. Timestamp:', 'updraftplus') . " {$timestamp}</p><br/>";
         return new WP_Error('does_not_exist', __('Backup does not exist in the backup history', 'updraftplus'));
     }
     // request_filesystem_credentials passes on fields just via hidden name/value pairs.
     // Build array of parameters to be passed via this
     $extra_fields = array();
     if (isset($_POST['updraft_restore']) && is_array($_POST['updraft_restore'])) {
         foreach ($_POST['updraft_restore'] as $entity) {
             $_POST['updraft_restore_' . $entity] = 1;
             $extra_fields[] = 'updraft_restore_' . $entity;
         }
     }
     // Now make sure that updraft_restorer_ option fields get passed along to request_filesystem_credentials
     foreach ($_POST as $key => $value) {
         if (0 === strpos($key, 'updraft_restorer_')) {
             $extra_fields[] = $key;
         }
     }
     $credentials = request_filesystem_credentials(UpdraftPlus_Options::admin_page() . "?page=updraftplus&action=updraft_restore&backup_timestamp={$timestamp}", '', false, false, $extra_fields);
     WP_Filesystem($credentials);
     if ($wp_filesystem->errors->get_error_code()) {
         foreach ($wp_filesystem->errors->get_error_messages() as $message) {
             show_message($message);
         }
         exit;
     }
     # Set up logging
     $updraftplus->backup_time_nonce();
     $updraftplus->jobdata_set('job_type', 'restore');
     $updraftplus->jobdata_set('job_time_ms', $updraftplus->job_time_ms);
     $updraftplus->logfile_open($updraftplus->nonce);
     # Provide download link for the log file
     #echo '<p><a target="_new" href="?action=downloadlog&page=updraftplus&updraftplus_backup_nonce='.htmlspecialchars($updraftplus->nonce).'">'.__('Follow this link to download the log file for this restoration.', 'updraftplus').'</a></p>';
     # TODO: Automatic purging of old log files
     # TODO: Provide option to auto-email the log file
     //if we make it this far then WP_Filesystem has been instantiated and is functional (tested with ftpext, what about suPHP and other situations where direct may work?)
     echo '<h1>' . __('UpdraftPlus Restoration: Progress', 'updraftplus') . '</h1><div id="updraft-restore-progress">';
     $this->show_admin_warning('<a target="_new" href="?action=downloadlog&page=updraftplus&updraftplus_backup_nonce=' . htmlspecialchars($updraftplus->nonce) . '">' . __('Follow this link to download the log file for this restoration (needed for any support requests).', 'updraftplus') . '</a>');
     $updraft_dir = trailingslashit($updraftplus->backups_dir_location());
     $service = isset($backup_history[$timestamp]['service']) ? $backup_history[$timestamp]['service'] : false;
     if (!is_array($service)) {
         $service = array($service);
     }
     // Now, need to turn any updraft_restore_<entity> fields (that came from a potential WP_Filesystem form) back into parts of the _POST array (which we want to use)
     if (empty($_POST['updraft_restore']) || !is_array($_POST['updraft_restore'])) {
         $_POST['updraft_restore'] = array();
     }
     $entities_to_restore = array_flip($_POST['updraft_restore']);
     $entities_log = '';
     foreach ($_POST as $key => $value) {
         if (strpos($key, 'updraft_restore_') === 0) {
             $nkey = substr($key, 16);
             if (!isset($entities_to_restore[$nkey])) {
                 $_POST['updraft_restore'][] = $nkey;
                 $entities_to_restore[$nkey] = 1;
                 $entities_log .= '' == $entities_log ? $nkey : ",{$nkey}";
             }
         }
     }
     $updraftplus->log("Restore job started. Entities to restore: {$entities_log}");
     if (0 == count($_POST['updraft_restore'])) {
         echo '<p>' . __('ABORT: Could not find the information on which entities to restore.', 'updraftplus') . '</p>';
         echo '<p>' . __('If making a request for support, please include this information:', 'updraftplus') . ' ' . count($_POST) . ' : ' . htmlspecialchars(serialize($_POST)) . '</p>';
         return new WP_Error('missing_info', 'Backup information not found');
     }
     set_error_handler(array($updraftplus, 'php_error'), E_ALL & ~E_STRICT);
     /*
     $_POST['updraft_restore'] is typically something like: array( 0=>'db', 1=>'plugins', 2=>'themes'), etc.
     i.e. array ( 'db', 'plugins', themes')
     */
     $backupable_entities = $updraftplus->get_backupable_file_entities(true, true);
     $backup_set = $backup_history[$timestamp];
     uksort($backup_set, array($this, 'sort_restoration_entities'));
     // We use a single object for each entity, because we want to store information about the backup set
     require_once UPDRAFTPLUS_DIR . '/restorer.php';
     global $updraftplus_restorer;
     $updraftplus_restorer = new Updraft_Restorer(new Updraft_Restorer_Skin());
     $second_loop = array();
     echo "<h2>" . __('Final checks', 'updraftplus') . '</h2>';
     // First loop: make sure that files are present + readable; and populate array for second loop
     foreach ($backup_set as $type => $files) {
         // All restorable entities must be given explicitly, as we can store other arbitrary data in the history array
         if (!isset($backupable_entities[$type]) && 'db' != $type) {
             continue;
         }
         if (isset($backupable_entities[$type]['restorable']) && $backupable_entities[$type]['restorable'] == false) {
             continue;
         }
         if (!isset($entities_to_restore[$type])) {
             continue;
         }
         if ($type == 'wpcore' && is_multisite() && 0 === $updraftplus_restorer->ud_backup_is_multisite) {
             echo "<p>{$type}: <strong>";
             echo __('Skipping restoration of WordPress core when importing a single site into a multisite installation. If you had anything necessary in your WordPress directory then you will need to re-add it manually from the zip file.', 'updraftplus');
             #TODO
             #$updraftplus->log_e('Skipping restoration of WordPress core when importing a single site into a multisite installation. If you had anything necessary in your WordPress directory then you will need to re-add it manually from the zip file.');
             echo "</strong></p>";
             continue;
         }
         if (is_string($files)) {
             $files = array($files);
         }
         foreach ($files as $ind => $file) {
             $fullpath = $updraft_dir . $file;
             echo sprintf(__("Looking for %s archive: file name: %s", 'updraftplus'), $type, htmlspecialchars($file)) . "<br>";
             foreach ($service as $serv) {
                 if (!is_readable($fullpath)) {
                     $sd = empty($updraftplus->backup_methods[$serv]) ? $serv : $updraftplus->backup_methods[$serv];
                     echo __("File is not locally present - needs retrieving from remote storage", 'updraftplus') . " ({$sd})";
                     $this->download_file($file, $serv);
                     echo ": ";
                     if (!is_readable($fullpath)) {
                         echo __("Error", 'updraftplus');
                     } else {
                         echo __("OK", 'updraftplus');
                     }
                     echo '<br>';
                 }
             }
             $index = $ind == 0 ? '' : $ind;
             // If a file size is stored in the backup data, then verify correctness of the local file
             if (isset($backup_history[$timestamp][$type . $index . '-size'])) {
                 $fs = $backup_history[$timestamp][$type . $index . '-size'];
                 echo __("Archive is expected to be size:", 'updraftplus') . " " . round($fs / 1024, 1) . " Kb: ";
                 $as = @filesize($fullpath);
                 if ($as == $fs) {
                     echo __('OK', 'updraftplus') . '<br>';
                 } else {
                     echo "<strong>" . __('Error:', 'updraftplus') . "</strong> " . __('file is size:', 'updraftplus') . " " . round($as / 1024) . " ({$fs}, {$as})<br>";
                 }
             } else {
                 echo __("The backup records do not contain information about the proper size of this file.", 'updraftplus') . "<br>";
             }
             if (!is_readable($fullpath)) {
                 echo __('Could not find one of the files for restoration', 'updraftplus') . " ({$file})<br>";
                 $updraftplus->log("{$file}: " . __('Could not find one of the files for restoration', 'updraftplus'), 'error');
                 echo '</div>';
                 restore_error_handler();
                 return false;
             }
         }
         $info = isset($backupable_entities[$type]) ? $backupable_entities[$type] : array();
         $val = $updraftplus_restorer->pre_restore_backup($files, $type, $info);
         if (is_wp_error($val)) {
             $updraftplus->log_wp_error($val);
             foreach ($val->get_error_messages() as $msg) {
                 echo '<strong>' . __('Error:', 'updraftplus') . '</strong> ' . htmlspecialchars($msg) . '<br>';
             }
             foreach ($val->get_error_codes() as $code) {
                 if ('already_exists' == $code) {
                     $this->print_delete_old_dirs_form(false);
                 }
             }
             echo '</div>';
             //close the updraft_restore_progress div even if we error
             restore_error_handler();
             return $val;
         } elseif (false === $val) {
             echo '</div>';
             //close the updraft_restore_progress div even if we error
             restore_error_handler();
             return false;
         }
         $second_loop[$type] = $files;
     }
     $updraftplus_restorer->delete = UpdraftPlus_Options::get_updraft_option('updraft_delete_local') ? true : false;
     if ('none' === $service || 'email' === $service || empty($service) || is_array($service) && 1 == count($service) && (in_array('none', $service) || in_array('', $service) || in_array('email', $service))) {
         if ($updraftplus_restorer->delete) {
             $updraftplus->log_e('Will not delete any archives after unpacking them, because there was no cloud storage for this backup');
         }
         $updraftplus_restorer->delete = false;
     }
     // Second loop: now actually do the restoration
     uksort($second_loop, array($this, 'sort_restoration_entities'));
     foreach ($second_loop as $type => $files) {
         # Types: uploads, themes, plugins, others, db
         $info = isset($backupable_entities[$type]) ? $backupable_entities[$type] : array();
         echo 'db' == $type ? "<h2>" . __('Database', 'updraftplus') . "</h2>" : "<h2>" . $info['description'] . "</h2>";
         $updraftplus->log("Entity: " . $type);
         if (is_string($files)) {
             $files = array($files);
         }
         foreach ($files as $file) {
             $val = $updraftplus_restorer->restore_backup($file, $type, $info);
             if (is_wp_error($val)) {
                 $updraftplus->log_e($val);
                 foreach ($val->get_error_messages() as $msg) {
                     echo '<strong>' . __('Error message', 'updraftplus') . ':</strong> ' . htmlspecialchars($msg) . '<br>';
                 }
                 $codes = $val->get_error_codes();
                 if (is_array($codes)) {
                     foreach ($codes as $code) {
                         $data = $val->get_error_data($code);
                         if (!empty($data)) {
                             $pdata = is_string($data) ? $data : serialize($data);
                             echo '<strong>' . __('Error data:', 'updraftplus') . '</strong> ' . htmlspecialchars($pdata) . '<br>';
                         }
                     }
                 }
                 echo '</div>';
                 //close the updraft_restore_progress div even if we error
                 restore_error_handler();
                 return $val;
             } elseif (false === $val) {
                 echo '</div>';
                 //close the updraft_restore_progress div even if we error
                 restore_error_handler();
                 return false;
             }
         }
     }
     foreach (array('template', 'stylesheet', 'template_root', 'stylesheet_root') as $opt) {
         add_filter('pre_option_' . $opt, array($this, 'option_filter_' . $opt));
     }
     if (!function_exists('validate_current_theme')) {
         require_once ABSPATH . 'wp-includes/themes';
     }
     if (!validate_current_theme()) {
         global $updraftplus;
         echo '<strong>';
         $updraftplus->log_e("The current theme was not found; to prevent this stopping the site from loading, your theme has been reverted to the default theme");
         echo '</strong>';
     }
     #foreach (array('template', 'stylesheet', 'template_root', 'stylesheet_root') as $opt) {
     #	remove_filter('pre_option_'.$opt, array($this, 'option_filter_'.$opt));
     #}
     echo '</div>';
     //close the updraft_restore_progress div
     restore_error_handler();
     return true;
 }
Example #2
0
 private function restore_backup($timestamp, $continuation_data = null)
 {
     @set_time_limit(UPDRAFTPLUS_SET_TIME_LIMIT);
     global $wp_filesystem, $updraftplus;
     $backup_history = UpdraftPlus_Options::get_updraft_option('updraft_backup_history');
     if (!isset($backup_history[$timestamp]) || !is_array($backup_history[$timestamp])) {
         echo '<p>' . __('This backup does not exist in the backup history - restoration aborted. Timestamp:', 'updraftplus') . " {$timestamp}</p><br/>";
         return new WP_Error('does_not_exist', __('Backup does not exist in the backup history', 'updraftplus'));
     }
     // request_filesystem_credentials passes on fields just via hidden name/value pairs.
     // Build array of parameters to be passed via this
     $extra_fields = array();
     if (isset($_POST['updraft_restore']) && is_array($_POST['updraft_restore'])) {
         foreach ($_POST['updraft_restore'] as $entity) {
             $_POST['updraft_restore_' . $entity] = 1;
             $extra_fields[] = 'updraft_restore_' . $entity;
         }
     }
     if (is_array($continuation_data)) {
         foreach ($continuation_data['second_loop_entities'] as $type => $files) {
             $_POST['updraft_restore_' . $type] = 1;
             if (!in_array('updraft_restore_' . $type, $extra_fields)) {
                 $extra_fields[] = 'updraft_restore_' . $type;
             }
         }
         if (!empty($continuation_data['restore_options'])) {
             $restore_options = $continuation_data['restore_options'];
         }
     }
     // Now make sure that updraft_restorer_ option fields get passed along to request_filesystem_credentials
     foreach ($_POST as $key => $value) {
         if (0 === strpos($key, 'updraft_restorer_')) {
             $extra_fields[] = $key;
         }
     }
     $credentials = request_filesystem_credentials(UpdraftPlus_Options::admin_page() . "?page=updraftplus&action=updraft_restore&backup_timestamp={$timestamp}", '', false, false, $extra_fields);
     WP_Filesystem($credentials);
     if ($wp_filesystem->errors->get_error_code()) {
         echo '<p><em><a href="https://updraftplus.com/faqs/asked-ftp-details-upon-restorationmigration-updates/">' . __('Why am I seeing this?', 'updraftplus') . '</a></em></p>';
         foreach ($wp_filesystem->errors->get_error_messages() as $message) {
             show_message($message);
         }
         exit;
     }
     // If we make it this far then WP_Filesystem has been instantiated and is functional
     # Set up logging
     $updraftplus->backup_time_nonce();
     $updraftplus->jobdata_set('job_type', 'restore');
     $updraftplus->jobdata_set('job_time_ms', $updraftplus->job_time_ms);
     $updraftplus->logfile_open($updraftplus->nonce);
     # Provide download link for the log file
     # TODO: Automatic purging of old log files
     # TODO: Provide option to auto-email the log file
     echo '<h1>' . __('UpdraftPlus Restoration: Progress', 'updraftplus') . '</h1><div id="updraft-restore-progress">';
     $this->show_admin_warning('<a target="_blank" href="?action=downloadlog&page=updraftplus&updraftplus_backup_nonce=' . htmlspecialchars($updraftplus->nonce) . '">' . __('Follow this link to download the log file for this restoration (needed for any support requests).', 'updraftplus') . '</a>');
     $updraft_dir = trailingslashit($updraftplus->backups_dir_location());
     $foreign_known = apply_filters('updraftplus_accept_archivename', array());
     $service = isset($backup_history[$timestamp]['service']) ? $backup_history[$timestamp]['service'] : false;
     if (!is_array($service)) {
         $service = array($service);
     }
     // Now, need to turn any updraft_restore_<entity> fields (that came from a potential WP_Filesystem form) back into parts of the _POST array (which we want to use)
     if (empty($_POST['updraft_restore']) || !is_array($_POST['updraft_restore'])) {
         $_POST['updraft_restore'] = array();
     }
     $backup_set = $backup_history[$timestamp];
     $entities_to_restore = array();
     foreach ($_POST['updraft_restore'] as $entity) {
         if (empty($backup_set['meta_foreign'])) {
             $entities_to_restore[$entity] = $entity;
         } else {
             if ('db' == $entity && !empty($foreign_known[$backup_set['meta_foreign']]) && !empty($foreign_known[$backup_set['meta_foreign']]['separatedb'])) {
                 $entities_to_restore[$entity] = 'db';
             } else {
                 $entities_to_restore[$entity] = 'wpcore';
             }
         }
     }
     foreach ($_POST as $key => $value) {
         if (0 === strpos($key, 'updraft_restore_')) {
             $nkey = substr($key, 16);
             if (!isset($entities_to_restore[$nkey])) {
                 $_POST['updraft_restore'][] = $nkey;
                 if (empty($backup_set['meta_foreign'])) {
                     $entities_to_restore[$nkey] = $nkey;
                 } else {
                     if ('db' == $entity && !empty($foreign_known[$backup_set['meta_foreign']]['separatedb'])) {
                         $entities_to_restore[$nkey] = 'db';
                     } else {
                         $entities_to_restore[$nkey] = 'wpcore';
                     }
                 }
             }
         }
     }
     if (0 == count($_POST['updraft_restore'])) {
         echo '<p>' . __('ABORT: Could not find the information on which entities to restore.', 'updraftplus') . '</p>';
         echo '<p>' . __('If making a request for support, please include this information:', 'updraftplus') . ' ' . count($_POST) . ' : ' . htmlspecialchars(serialize($_POST)) . '</p>';
         return new WP_Error('missing_info', 'Backup information not found');
     }
     $this->entities_to_restore = $entities_to_restore;
     set_error_handler(array($updraftplus, 'php_error'), E_ALL & ~E_STRICT);
     /*
     $_POST['updraft_restore'] is typically something like: array( 0=>'db', 1=>'plugins', 2=>'themes'), etc.
     i.e. array ( 'db', 'plugins', themes')
     */
     if (empty($restore_options)) {
         // Gather the restore optons into one place - code after here should read the options, and not the HTTP layer
         $restore_options = array();
         if (!empty($_POST['updraft_restorer_restore_options'])) {
             parse_str($_POST['updraft_restorer_restore_options'], $restore_options);
         }
         $restore_options['updraft_restorer_replacesiteurl'] = empty($_POST['updraft_restorer_replacesiteurl']) ? false : true;
         $restore_options['updraft_encryptionphrase'] = empty($_POST['updraft_encryptionphrase']) ? '' : (string) $_POST['updraft_encryptionphrase'];
         $restore_options['updraft_restorer_wpcore_includewpconfig'] = empty($_POST['updraft_restorer_wpcore_includewpconfig']) ? false : true;
         $updraftplus->jobdata_set('restore_options', $restore_options);
     }
     // Restore in the most helpful order
     uksort($backup_set, array($this, 'sort_restoration_entities'));
     // Now log
     $copy_restore_options = $restore_options;
     if (!empty($copy_restore_options['updraft_encryptionphrase'])) {
         $copy_restore_options['updraft_encryptionphrase'] = '***';
     }
     $updraftplus->log("Restore job started. Entities to restore: " . implode(', ', array_flip($entities_to_restore)) . '. Restore options: ' . json_encode($copy_restore_options));
     $backup_set['timestamp'] = $timestamp;
     $backupable_entities = $updraftplus->get_backupable_file_entities(true, true);
     // Allow add-ons to adjust the restore directory (but only in the case of restore - otherwise, they could just use the filter built into UpdraftPlus::get_backupable_file_entities)
     $backupable_entities = apply_filters('updraft_backupable_file_entities_on_restore', $backupable_entities, $restore_options, $backup_set);
     // We use a single object for each entity, because we want to store information about the backup set
     require_once UPDRAFTPLUS_DIR . '/restorer.php';
     global $updraftplus_restorer;
     $updraftplus_restorer = new Updraft_Restorer(new Updraft_Restorer_Skin(), $backup_set, false, $restore_options);
     $second_loop = array();
     echo "<h2>" . __('Final checks', 'updraftplus') . '</h2>';
     if (empty($backup_set['meta_foreign'])) {
         $entities_to_download = $entities_to_restore;
     } else {
         if (!empty($foreign_known[$backup_set['meta_foreign']]['separatedb'])) {
             $entities_to_download = array();
             if (in_array('db', $entities_to_restore)) {
                 $entities_to_download['db'] = 1;
             }
             if (count($entities_to_restore) > 1 || !in_array('db', $entities_to_restore)) {
                 $entities_to_download['wpcore'] = 1;
             }
         } else {
             $entities_to_download = array('wpcore' => 1);
         }
     }
     // First loop: make sure that files are present + readable; and populate array for second loop
     foreach ($backup_set as $type => $files) {
         // All restorable entities must be given explicitly, as we can store other arbitrary data in the history array
         if (!isset($backupable_entities[$type]) && 'db' != $type) {
             continue;
         }
         if (isset($backupable_entities[$type]['restorable']) && $backupable_entities[$type]['restorable'] == false) {
             continue;
         }
         if (!isset($entities_to_download[$type])) {
             continue;
         }
         if ('wpcore' == $type && is_multisite() && 0 === $updraftplus_restorer->ud_backup_is_multisite) {
             echo "<p>{$type}: <strong>";
             echo __('Skipping restoration of WordPress core when importing a single site into a multisite installation. If you had anything necessary in your WordPress directory then you will need to re-add it manually from the zip file.', 'updraftplus');
             #TODO
             #$updraftplus->log_e('Skipping restoration of WordPress core when importing a single site into a multisite installation. If you had anything necessary in your WordPress directory then you will need to re-add it manually from the zip file.');
             echo "</strong></p>";
             continue;
         }
         if (is_string($files)) {
             $files = array($files);
         }
         foreach ($files as $ind => $file) {
             $fullpath = $updraft_dir . $file;
             echo sprintf(__("Looking for %s archive: file name: %s", 'updraftplus'), $type, htmlspecialchars($file)) . "<br>";
             if (is_array($continuation_data) && isset($continuation_data['second_loop_entities'][$type]) && !in_array($file, $continuation_data['second_loop_entities'][$type])) {
                 echo __('Skipping: this archive was already restored.', 'updraftplus') . "<br>";
                 // Set the marker so that the existing directory isn't moved out of the way
                 $updraftplus_restorer->been_restored[$type] = true;
                 continue;
             }
             add_action('http_request_args', array($updraftplus, 'modify_http_options'));
             foreach ($service as $serv) {
                 if (!is_readable($fullpath)) {
                     $sd = empty($updraftplus->backup_methods[$serv]) ? $serv : $updraftplus->backup_methods[$serv];
                     echo __("File is not locally present - needs retrieving from remote storage", 'updraftplus') . " ({$sd})";
                     $this->download_file($file, $serv);
                     echo ": ";
                     if (!is_readable($fullpath)) {
                         echo __("Error", 'updraftplus');
                     } else {
                         echo __("OK", 'updraftplus');
                     }
                     echo '<br>';
                 }
             }
             remove_action('http_request_args', array($updraftplus, 'modify_http_options'));
             $index = $ind == 0 ? '' : $ind;
             // If a file size is stored in the backup data, then verify correctness of the local file
             if (isset($backup_history[$timestamp][$type . $index . '-size'])) {
                 $fs = $backup_history[$timestamp][$type . $index . '-size'];
                 echo __("Archive is expected to be size:", 'updraftplus') . " " . round($fs / 1024, 1) . " KB: ";
                 $as = @filesize($fullpath);
                 if ($as == $fs) {
                     echo __('OK', 'updraftplus') . '<br>';
                 } else {
                     echo "<strong>" . __('Error:', 'updraftplus') . "</strong> " . __('file is size:', 'updraftplus') . " " . round($as / 1024) . " ({$fs}, {$as})<br>";
                 }
             } else {
                 echo __("The backup records do not contain information about the proper size of this file.", 'updraftplus') . "<br>";
             }
             if (!is_readable($fullpath)) {
                 echo __('Could not find one of the files for restoration', 'updraftplus') . " ({$file})<br>";
                 $updraftplus->log("{$file}: " . __('Could not find one of the files for restoration', 'updraftplus'), 'error');
                 echo '</div>';
                 restore_error_handler();
                 return false;
             }
         }
         if (empty($updraftplus_restorer->ud_foreign)) {
             $types = array($type);
         } else {
             if ('db' != $type || empty($foreign_known[$updraftplus_restorer->ud_foreign]['separatedb'])) {
                 $types = array('wpcore');
             } else {
                 $types = array('db');
             }
         }
         foreach ($types as $check_type) {
             $info = isset($backupable_entities[$check_type]) ? $backupable_entities[$check_type] : array();
             $val = $updraftplus_restorer->pre_restore_backup($files, $check_type, $info, $continuation_data);
             if (is_wp_error($val)) {
                 $updraftplus->log_wp_error($val);
                 foreach ($val->get_error_messages() as $msg) {
                     echo '<strong>' . __('Error:', 'updraftplus') . '</strong> ' . htmlspecialchars($msg) . '<br>';
                 }
                 foreach ($val->get_error_codes() as $code) {
                     if ('already_exists' == $code) {
                         $this->print_delete_old_dirs_form(false);
                     }
                 }
                 echo '</div>';
                 //close the updraft_restore_progress div even if we error
                 restore_error_handler();
                 return $val;
             } elseif (false === $val) {
                 echo '</div>';
                 //close the updraft_restore_progress div even if we error
                 restore_error_handler();
                 return false;
             }
         }
         foreach ($entities_to_restore as $entity => $via) {
             if ($via == $type) {
                 if ('wpcore' == $via && 'db' == $entity && count($files) > 1) {
                     $second_loop[$entity] = apply_filters('updraftplus_select_wpcore_file_with_db', $files, $updraftplus_restorer->ud_foreign);
                 } else {
                     $second_loop[$entity] = $files;
                 }
             }
         }
     }
     $updraftplus_restorer->delete = UpdraftPlus_Options::get_updraft_option('updraft_delete_local') ? true : false;
     if ('none' === $service || 'email' === $service || empty($service) || is_array($service) && 1 == count($service) && (in_array('none', $service) || in_array('', $service) || in_array('email', $service)) || !empty($updraftplus_restorer->ud_foreign)) {
         if ($updraftplus_restorer->delete) {
             $updraftplus->log_e('Will not delete any archives after unpacking them, because there was no cloud storage for this backup');
         }
         $updraftplus_restorer->delete = false;
     }
     if (!empty($updraftplus_restorer->ud_foreign)) {
         $updraftplus->log("Foreign backup; created by: " . $updraftplus_restorer->ud_foreign);
     }
     // Second loop: now actually do the restoration
     uksort($second_loop, array($this, 'sort_restoration_entities'));
     // If continuing, then prune those already done
     if (is_array($continuation_data)) {
         foreach ($second_loop as $type => $files) {
             if (isset($continuation_data['second_loop_entities'][$type])) {
                 $second_loop[$type] = $continuation_data['second_loop_entities'][$type];
             }
         }
     }
     $updraftplus->jobdata_set('second_loop_entities', $second_loop);
     $updraftplus->jobdata_set('backup_timestamp', $timestamp);
     // use a site option, as otherwise on multisite when all the array of options is updated via UpdraftPlus_Options::update_site_option(), it will over-write any restored UD options from the backup
     update_site_option('updraft_restore_in_progress', $updraftplus->nonce);
     foreach ($second_loop as $type => $files) {
         # Types: uploads, themes, plugins, others, db
         $info = isset($backupable_entities[$type]) ? $backupable_entities[$type] : array();
         echo 'db' == $type ? "<h2>" . __('Database', 'updraftplus') . "</h2>" : "<h2>" . $info['description'] . "</h2>";
         $updraftplus->log("Entity: " . $type);
         if (is_string($files)) {
             $files = array($files);
         }
         foreach ($files as $fkey => $file) {
             $last_one = 1 == count($second_loop) && 1 == count($files);
             $val = $updraftplus_restorer->restore_backup($file, $type, $info, $last_one);
             if (is_wp_error($val)) {
                 $codes = $val->get_error_codes();
                 if (is_array($codes) && in_array('not_found', $codes) && !empty($updraftplus_restorer->ud_foreign) && apply_filters('updraftplus_foreign_allow_missing_entity', false, $type, $updraftplus_restorer->ud_foreign)) {
                     $updraftplus->log("Entity to move not found in this zip - but this is possible with this foreign backup type");
                 } else {
                     $updraftplus->log_e($val);
                     foreach ($val->get_error_messages() as $msg) {
                         echo '<strong>' . __('Error message', 'updraftplus') . ':</strong> ' . htmlspecialchars($msg) . '<br>';
                     }
                     $codes = $val->get_error_codes();
                     if (is_array($codes)) {
                         foreach ($codes as $code) {
                             $data = $val->get_error_data($code);
                             if (!empty($data)) {
                                 $pdata = is_string($data) ? $data : serialize($data);
                                 echo '<strong>' . __('Error data:', 'updraftplus') . '</strong> ' . htmlspecialchars($pdata) . '<br>';
                                 if (false !== strpos($pdata, 'PCLZIP_ERR_BAD_FORMAT (-10)')) {
                                     echo '<a href="https://updraftplus.com/faqs/error-message-pclzip_err_bad_format-10-invalid-archive-structure-mean/"><strong>' . __('Please consult this FAQ for help on what to do about it.', 'updraftplus') . '</strong></a><br>';
                                 }
                             }
                         }
                     }
                     echo '</div>';
                     //close the updraft_restore_progress div even if we error
                     restore_error_handler();
                     return $val;
                 }
             } elseif (false === $val) {
                 echo '</div>';
                 //close the updraft_restore_progress div even if we error
                 restore_error_handler();
                 return false;
             }
             unset($files[$fkey]);
             $second_loop[$type] = $files;
             $updraftplus->jobdata_set('second_loop_entities', $second_loop);
             $updraftplus->jobdata_set('backup_timestamp', $timestamp);
             do_action('updraft_restored_archive', $file, $type, $val, $fkey, $timestamp);
         }
         unset($second_loop[$type]);
         update_site_option('updraft_restore_in_progress', $updraftplus->nonce);
         $updraftplus->jobdata_set('second_loop_entities', $second_loop);
         $updraftplus->jobdata_set('backup_timestamp', $timestamp);
     }
     // All done - remove
     delete_site_option('updraft_restore_in_progress');
     foreach (array('template', 'stylesheet', 'template_root', 'stylesheet_root') as $opt) {
         add_filter('pre_option_' . $opt, array($this, 'option_filter_' . $opt));
     }
     # Clear any cached pages after the restore
     $updraftplus_restorer->clear_cache();
     if (!function_exists('validate_current_theme')) {
         require_once ABSPATH . WPINC . '/themes';
     }
     # Have seen a case where the current theme in the DB began with a capital, but not on disk - and this breaks migrating from Windows to a case-sensitive system
     $template = get_option('template');
     if (!empty($template) && $template != WP_DEFAULT_THEME && $template != strtolower($template)) {
         $theme_root = get_theme_root($template);
         $theme_root2 = get_theme_root(strtolower($template));
         if (!file_exists("{$theme_root}/{$template}/style.css") && file_exists("{$theme_root}/" . strtolower($template) . "/style.css")) {
             $updraftplus->log_e("Theme directory (%s) not found, but lower-case version exists; updating database option accordingly", $template);
             update_option('template', strtolower($template));
         }
     }
     if (!validate_current_theme()) {
         echo '<strong>';
         $updraftplus->log_e("The current theme was not found; to prevent this stopping the site from loading, your theme has been reverted to the default theme");
         echo '</strong>';
     }
     echo '</div>';
     //close the updraft_restore_progress div
     restore_error_handler();
     return true;
 }
Example #3
0
 function restore_backup($timestamp)
 {
     @set_time_limit(900);
     global $wp_filesystem, $updraftplus;
     $backup_history = UpdraftPlus_Options::get_updraft_option('updraft_backup_history');
     if (!is_array($backup_history[$timestamp])) {
         echo '<p>' . __('This backup does not exist in the backup history - restoration aborted. Timestamp:', 'updraftplus') . " {$timestamp}</p><br/>";
         return new WP_Error('does_not_exist', 'Backup does not exist in the backup history');
     }
     // request_filesystem_credentials passes on fields just via hidden name/value pairs.
     // Build array of parameters to be passed via this
     $extra_fields = array();
     if (isset($_POST['updraft_restore']) && is_array($_POST['updraft_restore'])) {
         foreach ($_POST['updraft_restore'] as $entity) {
             $_POST['updraft_restore_' . $entity] = 1;
             $extra_fields[] = 'updraft_restore_' . $entity;
         }
     }
     // Now make sure that updraft_restorer_ option fields get passed along to request_filesystem_credentials
     foreach ($_POST as $key => $value) {
         if (strpos($key, 'updraft_restorer_') === 0) {
             $extra_fields[] = $key;
         }
     }
     $credentials = request_filesystem_credentials("options-general.php?page=updraftplus&action=updraft_restore&backup_timestamp={$timestamp}", '', false, false, $extra_fields);
     WP_Filesystem($credentials);
     if ($wp_filesystem->errors->get_error_code()) {
         foreach ($wp_filesystem->errors->get_error_messages() as $message) {
             show_message($message);
         }
         exit;
     }
     //if we make it this far then WP_Filesystem has been instantiated and is functional (tested with ftpext, what about suPHP and other situations where direct may work?)
     echo '<h1>' . __('UpdraftPlus Restoration: Progress', 'updraftplus') . '</h1><div id="updraft-restore-progress">';
     $updraft_dir = $updraftplus->backups_dir_location() . '/';
     $service = isset($backup_history[$timestamp]['service']) ? $backup_history[$timestamp]['service'] : false;
     // Now, need to turn any updraft_restore_<entity> fields (that came from a potential WP_Filesystem form) back into parts of the _POST array (which we want to use)
     if (empty($_POST['updraft_restore']) || !is_array($_POST['updraft_restore'])) {
         $_POST['updraft_restore'] = array();
     }
     $entities_to_restore = array_flip($_POST['updraft_restore']);
     foreach ($_POST as $key => $value) {
         if (strpos($key, 'updraft_restore_') === 0) {
             $nkey = substr($key, 16);
             if (!isset($entities_to_restore[$nkey])) {
                 $_POST['updraft_restore'][] = $nkey;
                 $entities_to_restore[$nkey] = 1;
             }
         }
     }
     if (count($_POST['updraft_restore']) == 0) {
         echo '<p>' . __('ABORT: Could not find the information on which entities to restore.', 'updraftplus') . '</p>';
         echo '<p>' . __('If making a request for support, please include this information:', 'updraftplus') . ' ' . count($_POST) . ' : ' . htmlspecialchars(serialize($_POST)) . '</p>';
         return new WP_Error('missing_info', 'Backup information not found');
     }
     /*
     $_POST['updraft_restore'] is typically something like: array( 0=>'db', 1=>'plugins', 2=>'themes'), etc.
     i.e. array ( 'db', 'plugins', themes')
     */
     $backupable_entities = $updraftplus->get_backupable_file_entities(true, true);
     $backup_set = $backup_history[$timestamp];
     uksort($backup_set, array($this, 'sort_restoration_entities'));
     // We use a single object for each entity, because we want to store information about the backup set
     if (!class_exists('WP_Upgrader')) {
         require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
     }
     require_once UPDRAFTPLUS_DIR . '/restorer.php';
     $restorer = new Updraft_Restorer();
     $second_loop = array();
     echo "<h2>" . __('Final checks', 'updraftplus') . '</h2>';
     // First loop: make sure that files are present + readable; and populate array for second loop
     foreach ($backup_set as $type => $files) {
         // All restorable entities must be given explicitly, as we can store other arbitrary data in the history array
         if (!isset($backupable_entities[$type]) && 'db' != $type) {
             continue;
         }
         if (isset($backupable_entities[$type]['restorable']) && $backupable_entities[$type]['restorable'] == false) {
             continue;
         }
         if (!isset($entities_to_restore[$type])) {
             continue;
         }
         if ($type == 'wpcore' && is_multisite() && 0 === $restorer->ud_backup_is_multisite) {
             echo "<p>{$type}: <strong>" . __('Skipping restoration of WordPress core when importing a single site into a multisite installation. If you had anything necessary in your WordPress directory then you will need to re-add it manually from the zip file.', 'updraftplus') . "</strong></p>";
             continue;
         }
         if (is_string($files)) {
             $files = array($files);
         }
         foreach ($files as $ind => $file) {
             $fullpath = $updraft_dir . $file;
             echo sprintf(__("Looking for %s archive: file name: %s", 'updraftplus'), $type, htmlspecialchars($file)) . "<br>";
             if (!is_readable($fullpath)) {
                 echo __("File is not locally present - needs retrieving from remote storage", 'updraftplus') . "<br>";
                 $this->download_file($file, $service);
             }
             $index = $ind == 0 ? '' : $ind;
             // If a file size is stored in the backup data, then verify correctness of the local file
             if (isset($backup_history[$timestamp][$type . $index . '-size'])) {
                 $fs = $backup_history[$timestamp][$type . $index . '-size'];
                 echo __("Archive is expected to be size:", 'updraftplus') . " " . round($fs / 1024) . " Kb: ";
                 $as = @filesize($fullpath);
                 if ($as == $fs) {
                     echo __('OK', 'updraftplus') . '<br>';
                 } else {
                     echo "<strong>" . __('Error:', 'updraftplus') . "</strong> " . __('file is size:', 'updraftplus') . " " . round($as / 1024) . " ({$fs}, {$as})<br>";
                 }
             } else {
                 echo __("The backup records do not contain information about the proper size of this file.", 'updraftplus') . "<br>";
             }
             if (!is_readable($fullpath)) {
                 echo __('Could not find one of the files for restoration', 'updraftplus') . " ({$file})<br>";
                 $updraftplus->log("{$file}: " . __('Could not find one of the files for restoration', 'updraftplus'), 'error');
                 echo '</div>';
                 return false;
             }
         }
         $info = isset($backupable_entities[$type]) ? $backupable_entities[$type] : array();
         $val = $restorer->pre_restore_backup($files, $type, $info);
         if (is_wp_error($val)) {
             foreach ($val->get_error_messages() as $msg) {
                 echo '<strong>' . __('Error message', 'updraftplus') . ':</strong> ' . htmlspecialchars($msg) . '<br>';
             }
             echo '</div>';
             //close the updraft_restore_progress div even if we error
             return $val;
         } elseif (false === $val) {
             echo '</div>';
             //close the updraft_restore_progress div even if we error
             return false;
         }
         $second_loop[$type] = $files;
     }
     // Second loop: now actually do the restoration
     uksort($second_loop, array($this, 'sort_restoration_entities'));
     foreach ($second_loop as $type => $files) {
         # Types: uploads, themes, plugins, others, db
         $info = isset($backupable_entities[$type]) ? $backupable_entities[$type] : array();
         echo 'db' == $type ? "<h2>" . __('Database', 'updraftplus') . "</h2>" : "<h2>" . $info['description'] . "</h2>";
         $val = $restorer->restore_backup($files, $type, $service, $info);
         if (is_wp_error($val)) {
             foreach ($val->get_error_messages() as $msg) {
                 echo '<strong>' . __('Error message', 'updraftplus') . ':</strong> ' . htmlspecialchars($msg) . '<br>';
             }
             echo '</div>';
             //close the updraft_restore_progress div even if we error
             return $val;
         } elseif (false === $val) {
             echo '</div>';
             //close the updraft_restore_progress div even if we error
             return false;
         }
     }
     echo '</div>';
     //close the updraft_restore_progress div
     return true;
 }