public function __construct($skin = null, $info = null, $shortinit = false) { global $wpdb; // Line up a wpdb-like object to use $this->use_wpdb = !function_exists('mysql_query') && !function_exists('mysqli_query') || !$wpdb->is_mysql || !$wpdb->ready ? true : false; $this->our_siteurl = untrailingslashit(site_url()); if (false == $this->use_wpdb) { // We have our own extension which drops lots of the overhead on the query $wpdb_obj = new UpdraftPlus_WPDB(DB_USER, DB_PASSWORD, DB_NAME, DB_HOST); // Was that successful? if (!$wpdb_obj->is_mysql || !$wpdb_obj->ready) { $this->use_wpdb = true; } else { $this->wpdb_obj = $wpdb_obj; $this->mysql_dbh = $wpdb_obj->updraftplus_getdbh(); $this->use_mysqli = $wpdb_obj->updraftplus_use_mysqli(); } } if ($shortinit) { return; } $this->ud_backup_info = $info; $this->ud_foreign = empty($info['meta_foreign']) ? false : $info['meta_foreign']; parent::__construct($skin); $this->init(); $this->backup_strings(); $this->is_multisite = is_multisite(); }
public function __construct($skin = null, $info = null, $shortinit = false, $restore_options = array()) { global $wpdb; // Line up a wpdb-like object $this->use_wpdb = !function_exists('mysql_query') && !function_exists('mysqli_query') || !$wpdb->is_mysql || !$wpdb->ready ? true : false; $this->our_siteurl = untrailingslashit(site_url()); if (false == $this->use_wpdb) { // We have our own extension which drops lots of the overhead on the query $wpdb_obj = new UpdraftPlus_WPDB(DB_USER, DB_PASSWORD, DB_NAME, DB_HOST); // Was that successful? if (!$wpdb_obj->is_mysql || !$wpdb_obj->ready) { $this->use_wpdb = true; } else { $this->wpdb_obj = $wpdb_obj; $this->mysql_dbh = $wpdb_obj->updraftplus_getdbh(); $this->use_mysqli = $wpdb_obj->updraftplus_use_mysqli(); } } if ($shortinit) { return; } $this->ud_backup_info = $info; do_action('updraftplus_restorer_restore_options', $restore_options); $this->ud_multisite_selective_restore = is_array($restore_options) && !empty($restore_options['updraft_restore_ms_whichsites']) && $restore_options['updraft_restore_ms_whichsites'] > 0 ? $restore_options['updraft_restore_ms_whichsites'] : false; $this->ud_restore_options = $restore_options; $this->ud_foreign = empty($info['meta_foreign']) ? false : $info['meta_foreign']; if (isset($info['is_multisite'])) { $this->ud_backup_is_multisite = $info['is_multisite']; } if (isset($info['created_by_version'])) { $this->created_by_version = $info['created_by_version']; } add_filter('updraftplus_logline', array($this, 'updraftplus_logline'), 10, 5); parent::__construct($skin); $this->init(); $this->backup_strings(); $this->is_multisite = is_multisite(); }
function restore_backup_db($working_dir, $working_dir_localpath, &$import_table_prefix) { do_action('updraftplus_restore_db_pre'); # This is now a legacy option (at least on the front end), so we should not see it much $this->prior_upload_path = get_option('upload_path'); // There is a file backup.db.gz inside the working directory # The 'off' check is for badly configured setups - http://wordpress.org/support/topic/plugin-wp-super-cache-warning-php-safe-mode-enabled-but-safe-mode-is-off if (@ini_get('safe_mode') && 'off' != strtolower(@ini_get('safe_mode'))) { echo "<p>" . __('Warning: PHP safe_mode is active on your server. Timeouts are much more likely. If these happen, then you will need to manually restore the file via phpMyAdmin or another method.', 'updraftplus') . "</p><br/>"; return false; } // wp_filesystem has no gzopen method, so we switch to using the local filesystem (which is harmless, since we are performing read-only operations) if (!is_readable($working_dir_localpath . '/backup.db.gz')) { return new WP_Error('gzopen_failed', __('Failed to find database file', 'updraftplus') . " ({$working_dir}/backup.db.gz)"); } global $wpdb, $updraftplus; $this->skin->feedback('restore_database'); // Read-only access: don't need to go through WP_Filesystem $dbhandle = gzopen($working_dir_localpath . '/backup.db.gz', 'r'); if (!$dbhandle) { return new WP_Error('gzopen_failed', __('Failed to open database file', 'updraftplus')); } $this->line = 0; // Line up a wpdb-like object to use // mysql_query will throw E_DEPRECATED from PHP 5.5, so we expect WordPress to have switched to something else by then // $use_wpdb = (version_compare(phpversion(), '5.5', '>=') || !function_exists('mysql_query') || !$wpdb->is_mysql || !$wpdb->ready) ? true : false; // Seems not - PHP 5.5 is immanent for release $this->use_wpdb = !function_exists('mysql_query') || !$wpdb->is_mysql || !$wpdb->ready ? true : false; if (false == $this->use_wpdb) { // We have our own extension which drops lots of the overhead on the query $wpdb_obj = new UpdraftPlus_WPDB(DB_USER, DB_PASSWORD, DB_NAME, DB_HOST); // Was that successful? if (!$wpdb_obj->is_mysql || !$wpdb_obj->ready) { $this->use_wpdb = true; } else { $this->mysql_dbh = $wpdb_obj->updraftplus_getdbh(); } } if (true == $this->use_wpdb) { $updraftplus->log_e('Database access: Direct MySQL access is not available, so we are falling back to wpdb (this will be considerably slower)'); } else { @mysql_query('SET SESSION query_cache_type = OFF;', $this->mysql_dbh); } // Find the supported engines - in case the dump had something else (case seen: saved from MariaDB with engine Aria; imported into plain MySQL without) $supported_engines = $wpdb->get_results("SHOW ENGINES", OBJECT_K); $this->errors = 0; $this->statements_run = 0; $this->tables_created = 0; $sql_line = ""; $sql_type = -1; $this->start_time = microtime(true); $old_wpversion = ''; $this->old_siteurl = ''; $this->old_home = ''; $this->old_content = ''; $old_table_prefix = ''; $old_siteinfo = array(); $gathering_siteinfo = true; $this->create_forbidden = false; $this->drop_forbidden = false; $this->last_error = ''; $random_table_name = 'updraft_tmp_' . rand(0, 9999999) . md5(microtime(true)); if ($this->use_wpdb) { $req = $wpdb->query("CREATE TABLE {$random_table_name}"); if (!$req) { $this->last_error = $wpdb->last_error; } $this->last_error_no = false; } else { $req = mysql_unbuffered_query("CREATE TABLE {$random_table_name}", $this->mysql_dbh); if (!$req) { $this->last_error = mysql_error($this->mysql_dbh); $this->last_error_no = mysql_errno($this->mysql_dbh); } } if (!$req && ($this->use_wpdb || $this->last_error_no === 1142)) { $this->create_forbidden = true; # If we can't create, then there's no point dropping $this->drop_forbidden = true; echo '<strong>' . __('Warning:', 'updraftplus') . '</strong> '; $updraftplus->log_e('Your database user does not have permission to create tables. We will attempt to restore by simply emptying the tables; this should work as long as a) you are restoring from a WordPress version with the same database structure, and b) Your imported database does not contain any tables which are not already present on the importing site.', ' (' . $this->last_error . ')'); } else { if ($this->use_wpdb) { $req = $wpdb->query("DROP TABLE {$random_table_name}"); if (!$req) { $this->last_error = $wpdb->last_error; } $this->last_error_no = false; } else { $req = mysql_unbuffered_query("DROP TABLE {$random_table_name}", $this->mysql_dbh); if (!$req) { $this->last_error = mysql_error($this->mysql_dbh); $this->last_error_no = mysql_errno($this->mysql_dbh); } } if (!$req && ($this->use_wpdb || $this->last_error_no === 1142)) { $this->drop_forbidden = true; echo '<strong>' . __('Warning:', 'updraftplus') . '</strong> '; $updraftplus->log_e('Your database user does not have permission to drop tables. We will attempt to restore by simply emptying the tables; this should work as long as you are restoring from a WordPress version with the same database structure (%s)', ' (' . $this->last_error . ')'); } } $restoring_table = ''; $max_allowed_packet = $updraftplus->get_max_packet_size(); while (!gzeof($dbhandle)) { // Up to 1Mb $buffer = rtrim(gzgets($dbhandle, 1048576)); // Discard comments if (empty($buffer) || substr($buffer, 0, 1) == '#') { if ('' == $this->old_siteurl && preg_match('/^\\# Backup of: (http(.*))$/', $buffer, $matches)) { $this->old_siteurl = untrailingslashit($matches[1]); $updraftplus->log_e('<strong>Backup of:</strong> %s', htmlspecialchars($this->old_siteurl)); do_action('updraftplus_restore_db_record_old_siteurl', $this->old_siteurl); } elseif (false === $this->created_by_version && preg_match('/^\\# Created by UpdraftPlus version ([\\d\\.]+)/', $buffer, $matches)) { $this->created_by_version = trim($matches[1]); echo '<strong>' . __('Backup created by:', 'updraftplus') . '</strong> ' . htmlspecialchars($this->created_by_version) . '<br>'; $updraftplus->log('Backup created by: ' . $this->created_by_version); } elseif ('' == $this->old_home && preg_match('/^\\# Home URL: (http(.*))$/', $buffer, $matches)) { $this->old_home = untrailingslashit($matches[1]); if ($this->old_siteurl && $this->old_home != $this->old_siteurl) { echo '<strong>' . __('Site home:', 'updraftplus') . '</strong> ' . htmlspecialchars($this->old_home) . '<br>'; $updraftplus->log('Site home: ' . $this->old_home); } do_action('updraftplus_restore_db_record_old_home', $this->old_home); } elseif ('' == $this->old_content && preg_match('/^\\# Content URL: (http(.*))$/', $buffer, $matches)) { $this->old_content = untrailingslashit($matches[1]); echo '<strong>' . __('Content URL:', 'updraftplus') . '</strong> ' . htmlspecialchars($this->old_content) . '<br>'; $updraftplus->log('Content URL: ' . $this->old_content); do_action('updraftplus_restore_db_record_old_content', $this->old_content); } elseif ('' == $old_table_prefix && preg_match('/^\\# Table prefix: (\\S+)$/', $buffer, $matches)) { $old_table_prefix = $matches[1]; echo '<strong>' . __('Old table prefix:', 'updraftplus') . '</strong> ' . htmlspecialchars($old_table_prefix) . '<br>'; $updraftplus->log("Old table prefix: " . $old_table_prefix); } 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') || UPDRAFTPLUS_EXPERIMENTAL_IMPORTINTOMULTISITE != true) { return new WP_Error('multisite_error', $this->strings['multisite_error']); } // Got the needed code? if (!class_exists('UpdraftPlusAddOn_MultiSite') || !class_exists('UpdraftPlus_Addons_Migrator')) { return new WP_Error('missing_addons', __('To import an ordinary WordPress site into a multisite installation requires both the multisite and migrator add-ons.', 'updraftplus')); } } } elseif (preg_match('/^([^=]+)=(.*)$/', $matches[1], $kvmatches)) { $key = $kvmatches[1]; $val = $kvmatches[2]; echo '<strong>' . __('Site information:', 'updraftplus') . '</strong>' . ' ' . htmlspecialchars($key) . ' = ' . htmlspecialchars($val) . '<br>'; $updraftplus->log("Site information: " . $key . "=" . $val); $old_siteinfo[$key] = $val; if ('multisite' == $key) { if ($val) { $this->ud_backup_is_multisite = 1; } else { $this->ud_backup_is_multisite = 0; } } } } continue; } // Detect INSERT commands early, so that we can split them if necessary if ($sql_line && preg_match('/^\\s*(insert into \\`?([^\\`]*)\\`?\\s+values)/i', $sql_line, $matches)) { $sql_type = 3; $insert_prefix = $matches[1]; } # Deal with case where adding this line will take us over the MySQL max_allowed_packet limit - must split, if we can (if it looks like consecutive rows) # ALlow a 100-byte margin for error (including searching/replacing table prefix) if (3 == $sql_type && $sql_line && strlen($sql_line . $buffer) > $max_allowed_packet - 100 && preg_match('/,\\s*$/', $sql_line) && preg_match('/^\\s*\\(/', $buffer)) { // Remove the final comma; replace with semi-colon $sql_line = substr(rtrim($sql_line), 0, strlen($sql_line) - 1) . ';'; if ('' != $old_table_prefix && $import_table_prefix != $old_table_prefix) { $sql_line = $updraftplus->str_replace_once($old_table_prefix, $import_table_prefix, $sql_line); } # Run the SQL command; then set up for the next one. $this->line++; echo __("Split line to avoid exceeding maximum packet size", 'updraftplus') . " (" . strlen($sql_line) . " + " . strlen($buffer) . " : {$max_allowed_packet})<br>"; $updraftplus->log("Split line to avoid exceeding maximum packet size (" . strlen($sql_line) . " + " . strlen($buffer) . " : {$max_allowed_packet})"); $do_exec = $this->sql_exec($sql_line, $sql_type); # Reset, then carry on $sql_line = $insert_prefix . " "; if (is_wp_error($do_exec)) { return $do_exec; } } $sql_line .= $buffer; # Do we have a complete line yet? if (';' != substr($sql_line, -1, 1)) { continue; } $this->line++; # We now have a complete line - process it if (3 == $sql_type && $sql_line && strlen($sql_line . $buffer) > $max_allowed_packet) { $logit = substr($sql_line . $buffer, 0, 100); $updraftplus->log(sprintf("An SQL line that is larger than the maximum packet size and cannot be split was found: %s", '(' . strlen($sql_line) . ', ' . strlen($buffer) . ', ' . $logit . ' ...)')); echo '<strong>' . __('Warning:', 'updraftplus') . '</strong> ' . sprintf(__("An SQL line that is larger than the maximum packet size and cannot be split was found; this line will not be processed, but will be dropped: %s", 'updraftplus'), '(' . strlen($sql_line) . ', ' . strlen($buffer) . ', ' . $logit . ' ...)') . "<br>"; # Reset $sql_line = ''; $sql_type = -1; continue; } # The timed overhead of this is negligible if (preg_match('/^\\s*drop table if exists \\`?([^\\`]*)\\`?\\s*;/i', $sql_line, $matches)) { $sql_type = 1; if (!isset($printed_new_table_prefix)) { $import_table_prefix = $this->pre_sql_actions($import_table_prefix); if (false === $import_table_prefix || is_wp_error($import_table_prefix)) { return $import_table_prefix; } $printed_new_table_prefix = true; } $this->table_name = $matches[1]; // Legacy, less reliable - in case it was not caught before if ('' == $old_table_prefix && preg_match('/^([a-z0-9]+)_.*$/i', $this->table_name, $tmatches)) { $old_table_prefix = $tmatches[1] . '_'; echo '<strong>' . __('Old table prefix:', 'updraftplus') . '</strong> ' . htmlspecialchars($old_table_prefix) . '<br>'; $updraftplus->log("Old table prefix: {$old_table_prefix}"); } $this->new_table_name = $old_table_prefix ? $updraftplus->str_replace_once($old_table_prefix, $import_table_prefix, $this->table_name) : $this->table_name; if ('' != $old_table_prefix && $import_table_prefix != $old_table_prefix) { $sql_line = $updraftplus->str_replace_once($old_table_prefix, $import_table_prefix, $sql_line); } } elseif (preg_match('/^\\s*create table \\`?([^\\`\\(]*)\\`?\\s*\\(/i', $sql_line, $matches)) { $sql_type = 2; // MySQL 4.1 outputs TYPE=, but accepts ENGINE=; 5.1 onwards accept *only* ENGINE= $sql_line = $updraftplus->str_lreplace('TYPE=', 'ENGINE=', $sql_line); if (!isset($printed_new_table_prefix)) { $import_table_prefix = $this->pre_sql_actions($import_table_prefix); if (false === $import_table_prefix || is_wp_error($import_table_prefix)) { return $import_table_prefix; } $printed_new_table_prefix = true; } // This CREATE TABLE command may be the de-facto mark for the end of processing a previous table (which is so if this is not the first table in the SQL dump) if ($restoring_table) { // After restoring the options table, we can set old_siteurl if on legacy (i.e. not already set) if ($restoring_table == $import_table_prefix . 'options') { if ('' == $this->old_siteurl || '' == $this->old_home || '' == $this->old_content) { global $updraftplus_addons_migrator; if (isset($updraftplus_addons_migrator->new_blogid)) { switch_to_blog($updraftplus_addons_migrator->new_blogid); } if ('' == $this->old_siteurl) { $this->old_siteurl = untrailingslashit($wpdb->get_row("SELECT option_value FROM {$wpdb->options} WHERE option_name='siteurl'")->option_value); do_action('updraftplus_restore_db_record_old_siteurl', $this->old_siteurl); } if ('' == $this->old_home) { $this->old_home = untrailingslashit($wpdb->get_row("SELECT option_value FROM {$wpdb->options} WHERE option_name='home'")->option_value); do_action('updraftplus_restore_db_record_old_home', $this->old_home); } if ('' == $this->old_content) { $this->old_content = $this->old_siteurl . '/wp-content'; do_action('updraftplus_restore_db_record_old_content', $this->old_content); } if (isset($updraftplus_addons_migrator->new_blogid)) { restore_current_blog(); } } } $this->restored_table($restoring_table, $import_table_prefix, $old_table_prefix); } $engine = "(?)"; $engine_change_message = ''; if (preg_match('/ENGINE=([^\\s;]+)/', $sql_line, $eng_match)) { $engine = $eng_match[1]; if (isset($supported_engines[$engine])) { #echo sprintf(__('Requested table engine (%s) is present.', 'updraftplus'), $engine); if ('myisam' == strtolower($engine)) { $sql_line = preg_replace('/PAGE_CHECKSUM=\\d\\s?/', '', $sql_line, 1); } } else { $engine_change_message = sprintf(__('Requested table engine (%s) is not present - changing to MyISAM.', 'updraftplus'), $engine) . "<br>"; $sql_line = $updraftplus->str_lreplace("ENGINE={$eng_match}", "ENGINE=MyISAM", $sql_line); // Remove (M)aria options if ('maria' == strtolower($engine) || 'aria' == strtolower($engine)) { $sql_line = preg_replace('/PAGE_CHECKSUM=\\d\\s?/', '', $sql_line, 1); $sql_line = preg_replace('/TRANSACTIONAL=\\d\\s?/', '', $sql_line, 1); } } } $this->table_name = $matches[1]; echo '<strong>' . sprintf(__('Restoring table (%s)', 'updraftplus'), $engine) . ":</strong> " . htmlspecialchars($this->table_name); $logline = "Restoring table ({$engine}): " . $this->table_name; if ('' != $old_table_prefix && $import_table_prefix != $old_table_prefix) { $new_table_name = $updraftplus->str_replace_once($old_table_prefix, $import_table_prefix, $this->table_name); echo ' - ' . __('will restore as:', 'updraftplus') . ' ' . htmlspecialchars($new_table_name); $logline .= " - will restore as: " . $new_table_name; $sql_line = $updraftplus->str_replace_once($old_table_prefix, $import_table_prefix, $sql_line); } else { $new_table_name = $this->table_name; } $updraftplus->log($logline); $restoring_table = $new_table_name; echo '<br>'; if ($engine_change_message) { echo $engine_change_message; } } elseif (preg_match('/^\\s*(insert into \\`?([^\\`]*)\\`?\\s+values)/i', $sql_line, $matches)) { $sql_type = 3; if ('' != $old_table_prefix && $import_table_prefix != $old_table_prefix) { $sql_line = $updraftplus->str_replace_once($old_table_prefix, $import_table_prefix, $sql_line); } } elseif (preg_match('/^\\s*(\\/\\*\\!40000 alter|lock) tables? \\`?([^\\`\\(]*)\\`?\\s+(write|disable|enable)/i', $sql_line, $matches)) { # Only binary mysqldump produces this pattern (LOCK TABLES `table` WRITE, ALTER TABLE `table` (DISABLE|ENABLE) KEYS) $sql_type = 4; if ('' != $old_table_prefix && $import_table_prefix != $old_table_prefix) { $sql_line = $updraftplus->str_replace_once($old_table_prefix, $import_table_prefix, $sql_line); } } $do_exec = $this->sql_exec($sql_line, $sql_type); if (is_wp_error($do_exec)) { return $do_exec; } # Reset $sql_line = ''; $sql_type = -1; } if ($restoring_table) { $this->restored_table($restoring_table, $import_table_prefix, $old_table_prefix); } $time_taken = microtime(true) - $this->start_time; $updraftplus->log_e('Finished: lines processed: %d in %.2f seconds', $this->line, $time_taken); gzclose($dbhandle); global $wp_filesystem; $wp_filesystem->delete($working_dir . '/backup.db.gz', false, true); return true; }
public function updraftplus_restore_db_pre() { global $wpdb, $updraftplus; $this->siteurl = untrailingslashit(site_url()); $this->home = untrailingslashit(home_url()); $this->content = untrailingslashit(content_url()); $this->use_wpdb = !function_exists('mysql_query') && !function_exists('mysqli_query') || !$wpdb->is_mysql || !$wpdb->ready ? true : false; $this->base_prefix = $updraftplus->get_table_prefix(false); $mysql_dbh = false; if (false == $this->use_wpdb) { // We have our own extension which drops lots of the overhead on the query // This class is defined in updraft-restorer.php, which has been included if we get here $wpdb_obj = new UpdraftPlus_WPDB(DB_USER, DB_PASSWORD, DB_NAME, DB_HOST); // Was that successful? if (!$wpdb_obj->is_mysql || !$wpdb_obj->ready) { $this->use_wpdb = true; } else { $this->wpdb_obj = $wpdb_obj; $mysql_dbh = $wpdb_obj->updraftplus_getdbh(); $use_mysqli = $wpdb_obj->updraftplus_use_mysqli(); } } $this->mysql_dbh = $mysql_dbh; $this->use_mysqli = $use_mysqli; if (true == $this->use_wpdb) { $updraftplus->log_e('Database access: Direct MySQL access is not available, so we are falling back to wpdb (this will be considerably slower)'); } if (is_multisite()) { $sites = $wpdb->get_results('SELECT id, domain, path FROM ' . esc_sql($this->base_prefix) . 'site', ARRAY_N); if (is_array($sites)) { $nsites = array(); foreach ($sites as $site) { $nsites[$site[0]] = array($site[1], $site[2]); } $this->original_sites = $nsites; } } $this->report = array('tables' => 0, 'rows' => 0, 'change' => 0, 'updates' => 0, 'timetaken' => 0, 'errors' => array()); }
function restore_backup($backup_files, $type, $service, $info) { if (is_string($backup_files)) { $backup_files = array($backup_files); } // TODO $backup_file = $backup_files[0]; if ($type == 'more') { show_message($this->strings['not_possible']); return; } global $wp_filesystem, $updraftplus_addons_migrator, $updraftplus; $wp_filesystem_dir = $this->get_wp_filesystem_dir($info['path']); if ($wp_filesystem_dir === false) { return false; } $wp_dir = trailingslashit($wp_filesystem->abspath()); $wp_content_dir = trailingslashit($wp_filesystem->wp_content_dir()); @set_time_limit(1800); $delete = UpdraftPlus_Options::get_updraft_option('updraft_delete_local') ? true : false; if ('none' == $service) { if ($delete) { _e('Will not delete the archive after unpacking it, because there was no cloud storage for this backup', 'updraftplus') . '<br>'; } $delete = false; } // This returns the wp_filesystem path $working_dir = $this->unpack_package($backup_file, $delete); if (is_wp_error($working_dir)) { return $working_dir; } $working_dir_filesystem = WP_CONTENT_DIR . '/upgrade/' . basename($working_dir); global $table_prefix; // We copy the variable because we may be importing with a different prefix (e.g. on multisite imports of individual blog data) $import_table_prefix = $table_prefix; @set_time_limit(1800); if ($type == 'others') { $dirname = basename($info['path']); // In this special case, the backup contents are not in a folder, so it is not simply a case of moving the folder around, but rather looping over all that we find $this->move_backup_in($working_dir, trailingslashit($wp_filesystem_dir), true, array('plugins', 'themes', 'uploads', 'upgrade'), 'others'); } elseif (is_multisite() && $this->ud_backup_is_multisite === 0 && ('plugins' == $type || 'themes' == $type || 'uploads' == $type && isset($updraftplus_addons_migrator['new_blogid'])) && $wp_filesystem->is_dir($working_dir . '/' . $type)) { # Migrating a single site into a multisite if ('plugins' == $type || 'themes' == $type) { // Only move in entities that are not already there (2) $this->move_backup_in($working_dir . '/' . $type, trailingslashit($wp_filesystem_dir), 2, array(), $type, true); @$wp_filesystem->delete($working_dir . '/' . $type); } else { // Uploads show_message($this->strings['moving_old']); switch_to_blog($updraftplus_addons_migrator['new_blogid']); $ud = wp_upload_dir(); $wpud = $ud['basedir']; $fsud = trailingslashit($wp_filesystem->find_folder($wpud)); restore_current_blog(); // TODO: What is below will move the entire uploads directory if blog id is 1. Detect this situation. (Can that happen? We created a new blog, so should not be possible). // TODO: the upload dir is not necessarily reachable through wp_filesystem - try ordinary method instead if (is_string($fsud)) { // This is not expected to exist, since we created a new blog if ($wp_filesystem->exists($fsud) && !$wp_filesystem->move($fsud, untrailingslashit($fsud) . "-old", true)) { return new WP_Error('old_move_failed', $this->strings['old_move_failed']); } show_message($this->strings['moving_backup']); if (!$wp_filesystem->move($working_dir . "/" . $type, $fsud, true)) { return new WP_Error('new_move_failed', $this->strings['new_move_failed']); } /* $this->move_backup_in($working_dir.'/'.$type, $wp_content_dir.$type.'/', 1, array(), $type); @$wp_filesystem->delete($working_dir.'/'.$type);*/ } else { return new WP_Error('move_failed', $this->strings['new_move_failed']); } } } elseif ('db' == $type) { do_action('updraftplus_restore_db_pre'); // There is a file backup.db.gz inside the working directory # The 'off' check is for badly configured setups - http://wordpress.org/support/topic/plugin-wp-super-cache-warning-php-safe-mode-enabled-but-safe-mode-is-off if (@ini_get('safe_mode') && strtolower(@ini_get('safe_mode')) != "off") { echo "<p>" . __('Warning: PHP safe_mode is active on your server. Timeouts are much more likely. If these happen, then you will need to manually restore the file via phpMyAdmin or another method.', 'updraftplus') . "</p><br/>"; return false; } // wp_filesystem has no gzopen method, so we switch to using the local filesystem (which is harmless, since we are performing read-only operations) if (!is_readable($working_dir_filesystem . '/backup.db.gz')) { return new WP_Error('gzopen_failed', __('Failed to find database file', 'updraftplus') . " ({$working_dir}/backup.db.gz)"); } $this->skin->feedback('restore_database'); // Read-only access: don't need to go through WP_Filesystem $dbhandle = gzopen($working_dir_filesystem . '/backup.db.gz', 'r'); if (!$dbhandle) { return new WP_Error('gzopen_failed', __('Failed to open database file', 'updraftplus')); } $line = 0; global $wpdb; // Line up a wpdb-like object to use // mysql_query will throw E_DEPRECATED from PHP 5.5, so we expect WordPress to have switched to something else by then // $use_wpdb = (version_compare(phpversion(), '5.5', '>=') || !function_exists('mysql_query') || !$wpdb->is_mysql || !$wpdb->ready) ? true : false; // Seems not - PHP 5.5 is immanent for release $use_wpdb = !function_exists('mysql_query') || !$wpdb->is_mysql || !$wpdb->ready ? true : false; if (false == $use_wpdb) { // We have our own extension which drops lots of the overhead on the query $wpdb_obj = new UpdraftPlus_WPDB(DB_USER, DB_PASSWORD, DB_NAME, DB_HOST); // Was that successful? if (!$wpdb_obj->is_mysql || !$wpdb_obj->ready) { $use_wpdb = true; } else { $mysql_dbh = $wpdb_obj->updraftplus_getdbh(); } } if (true == $use_wpdb) { _e('Database access: Direct MySQL access is not available, so we are falling back to wpdb (this will be considerably slower)', 'updraftplus'); } else { @mysql_query('SET SESSION query_cache_type = OFF;', $mysql_dbh); } // Find the supported engines - in case the dump had something else (case seen: saved from MariaDB with engine Aria; imported into plain MySQL without) $supported_engines = $wpdb->get_results("SHOW ENGINES", OBJECT_K); $errors = 0; $statements_run = 0; $tables_created = 0; $sql_line = ""; $sql_type = -1; $start_time = microtime(true); // TODO: Print a warning if restoring to a different WP version $old_wpversion = ''; $old_siteurl = ''; $old_table_prefix = ''; $old_siteinfo = array(); $gathering_siteinfo = true; $create_forbidden = false; $drop_forbidden = false; $last_error = ''; $random_table_name = 'updraft_tmp_' . rand(0, 9999999) . md5(microtime(true)); if ($use_wpdb) { $req = $wpdb->query("CREATE TABLE {$random_table_name}"); if (!$req) { $last_error = $wpdb->last_error; } $last_error_no = false; } else { $req = mysql_unbuffered_query("CREATE TABLE {$random_table_name}", $mysql_dbh); if (!$req) { $last_error = mysql_error($mysql_dbh); $last_error_no = mysql_errno($mysql_dbh); } } if (!$req && ($use_wpdb || $last_error_no === 1142)) { $create_forbidden = true; # If we can't create, then there's no point dropping $drop_forbidden = true; echo '<strong>' . __('Warning:', 'updraftplus') . '</strong> ' . __('Your database user does not have permission to create tables. We will attempt to restore by simply emptying the tables; this should work as long as a) you are restoring from a WordPress version with the same database structure, and b) Your imported database does not contain any tables which are not already present on the importing site.', 'updraftplus') . ' (' . $last_error . ')' . "<br>"; } else { if ($use_wpdb) { $req = $wpdb->query("DROP TABLE {$random_table_name}"); if (!$req) { $last_error = $wpdb->last_error; } $last_error_no = false; } else { $req = mysql_unbuffered_query("DROP TABLE {$random_table_name}", $mysql_dbh); if (!$req) { $last_error = mysql_error($mysql_dbh); $last_error_no = mysql_errno($mysql_dbh); } } if (!$req && ($use_wpdb || $last_error_no === 1142)) { $drop_forbidden = true; echo '<strong>' . __('Warning:', 'updraftplus') . '</strong> ' . __('Your database user does not have permission to drop tables. We will attempt to restore by simply emptying the tables; this should work as long as you are restoring from a WordPress version with the same database structure', 'updraftplus') . ' (' . $last_error . ')' . "<br>"; } } $restoring_table = ''; while (!gzeof($dbhandle)) { // Up to 1Mb $buffer = rtrim(gzgets($dbhandle, 1048576)); // Discard comments if (empty($buffer) || substr($buffer, 0, 1) == '#') { if ('' == $old_siteurl && preg_match('/^\\# Backup of: (http(.*))$/', $buffer, $matches)) { $old_siteurl = $matches[1]; echo '<strong>' . __('Backup of:', 'updraftplus') . '</strong> ' . htmlspecialchars($old_siteurl) . '<br>'; do_action('updraftplus_restore_db_record_old_siteurl', $old_siteurl); } elseif ('' == $old_table_prefix && preg_match('/^\\# Table prefix: (\\S+)$/', $buffer, $matches)) { $old_table_prefix = $matches[1]; echo '<strong>' . __('Old table prefix:', 'updraftplus') . '</strong> ' . htmlspecialchars($old_table_prefix) . '<br>'; } 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') || UPDRAFTPLUS_EXPERIMENTAL_IMPORTINTOMULTISITE != true) { return new WP_Error('multisite_error', $this->strings['multisite_error']); } // Got the needed code? if (!class_exists('UpdraftPlusAddOn_MultiSite') || !class_exists('UpdraftPlus_Addons_Migrator')) { return new WP_Error('missing_addons', __('To import an ordinary WordPress site into a multisite installation requires both the multisite and migrator add-ons.', 'updraftplus')); } } } elseif (preg_match('/^([^=]+)=(.*)$/', $matches[1], $kvmatches)) { $key = $kvmatches[1]; $val = $kvmatches[2]; echo '<strong>' . __('Site information:', 'updraftplus') . '</strong>' . ' ' . htmlspecialchars($key) . ' = ' . htmlspecialchars($val) . '<br>'; $old_siteinfo[$key] = $val; if ('multisite' == $key) { if ($val) { $this->ud_backup_is_multisite = 1; } else { $this->ud_backup_is_multisite = 0; } } } } continue; } $sql_line .= $buffer; # Do we have a complete line yet? if (';' != substr($sql_line, -1, 1)) { continue; } $line++; # The timed overhead of this is negligible if (preg_match('/^\\s*drop table if exists \\`?([^\\`]*)\\`?\\s*;/i', $sql_line, $matches)) { $sql_type = 1; if (!isset($printed_new_table_prefix)) { $import_table_prefix = $this->pre_sql_actions($import_table_prefix); if (false === $import_table_prefix || is_wp_error($import_table_prefix)) { return $import_table_prefix; } $printed_new_table_prefix = true; } $table_name = $matches[1]; // Legacy, less reliable - in case it was not caught before if ($old_table_prefix == '' && preg_match('/^([a-z0-9]+)_.*$/i', $table_name, $tmatches)) { $old_table_prefix = $tmatches[1] . '_'; echo '<strong>' . __('Old table prefix:', 'updraftplus') . '</strong> ' . htmlspecialchars($old_table_prefix) . '<br>'; } if ('' != $old_table_prefix && $import_table_prefix != $old_table_prefix) { $sql_line = $this->str_replace_once($old_table_prefix, $import_table_prefix, $sql_line); } } elseif (preg_match('/^\\s*create table \\`?([^\\`\\(]*)\\`?\\s*\\(/i', $sql_line, $matches)) { $sql_type = 2; // MySQL 4.1 outputs TYPE=, but accepts ENGINE=; 5.1 onwards accept *only* ENGINE= $sql_line = $updraftplus->str_lreplace('TYPE=', 'ENGINE=', $sql_line); if (!isset($printed_new_table_prefix)) { $import_table_prefix = $this->pre_sql_actions($import_table_prefix); if (false === $import_table_prefix || is_wp_error($import_table_prefix)) { return $import_table_prefix; } $printed_new_table_prefix = true; } // This CREATE TABLE command may be the de-facto mark for the end of processing a previous table (which is so if this is not the first table in the SQL dump) if ($restoring_table) { $this->restored_table($restoring_table, $import_table_prefix, $old_table_prefix); // After restoring the options table, we can set old_siteurl if on legacy (i.e. not already set) if ($restoring_table == $import_table_prefix . 'options') { if ('' == $old_siteurl) { global $updraftplus_addons_migrator; if (isset($updraftplus_addons_migrator['new_blogid'])) { switch_to_blog($updraftplus_addons_migrator['new_blogid']); } $old_siteurl = $wpdb->get_row("SELECT option_value FROM {$wpdb->options} WHERE option_name='siteurl'")->option_value; do_action('updraftplus_restore_db_record_old_siteurl', $old_siteurl); if (isset($updraftplus_addons_migrator['new_blogid'])) { restore_current_blog(); } } } } $engine = "(?)"; $engine_change_message = ''; if (preg_match('/ENGINE=([^\\s;]+)/', $sql_line, $eng_match)) { $engine = $eng_match[1]; if (isset($supported_engines[$engine])) { #echo sprintf(__('Requested table engine (%s) is present.', 'updraftplus'), $engine); if ('myisam' == strtolower($engine)) { $sql_line = preg_replace('/PAGE_CHECKSUM=\\d\\s?/', '', $sql_line, 1); } } else { $engine_change_message = sprintf(__('Requested table engine (%s) is not present - changing to MyISAM.', 'updraftplus'), $engine) . "<br>"; $sql_line = $updraftplus->str_lreplace("ENGINE={$eng_match}", "ENGINE=MyISAM", $sql_line); // Remove (M)aria options if ('maria' == strtolower($engine) || 'aria' == strtolower($engine)) { $sql_line = preg_replace('/PAGE_CHECKSUM=\\d\\s?/', '', $sql_line, 1); $sql_line = preg_replace('/TRANSACTIONAL=\\d\\s?/', '', $sql_line, 1); } } } $table_name = $matches[1]; echo '<strong>' . sprintf(__('Restoring table (%s)', 'updraftplus'), $engine) . ":</strong> " . htmlspecialchars($table_name); if ('' != $old_table_prefix && $import_table_prefix != $old_table_prefix) { $new_table_name = $this->str_replace_once($old_table_prefix, $import_table_prefix, $table_name); echo ' - ' . __('will restore as:', 'updraftplus') . ' ' . htmlspecialchars($new_table_name); $sql_line = $this->str_replace_once($old_table_prefix, $import_table_prefix, $sql_line); } else { $new_table_name = $table_name; } $restoring_table = $new_table_name; echo '<br>'; if ($engine_change_message) { echo $engine_change_message; } } elseif ('' != $old_table_prefix && preg_match('/^\\s*insert into \\`?([^\\`]*)\\`?\\s+values/i', $sql_line, $matches)) { $sql_type = 3; if ($import_table_prefix != $old_table_prefix) { $sql_line = $this->str_replace_once($old_table_prefix, $import_table_prefix, $sql_line); } } if ($sql_type == 2 && $create_forbidden) { echo sprintf(__('Cannot create new tables, so skipping this command (%s)', 'updraftplus'), htmlspecialchars("CREATE TABLE {$table_name}")) . "<br>"; $req = true; } else { if ($sql_type == 1 && $drop_forbidden) { $sql_line = "DELETE FROM " . $updraftplus->backquote($table_name); echo sprintf(__('Cannot drop tables, so deleting instead (%s)', 'updraftplus'), $sql_line) . "<br>"; } if ($use_wpdb) { $req = $wpdb->query($sql_line); if (!$req) { $last_error = $wpdb->last_error; } } else { $req = mysql_unbuffered_query($sql_line, $mysql_dbh); if (!$req) { $last_error = mysql_error($mysql_dbh); } } $statements_run++; } if (!$req) { $errors++; echo sprintf(_x('An error (%s) occured:', 'The user is being told the number of times an error has happened, e.g. An error (27) occurred', 'updraftplus'), $errors) . " - " . htmlspecialchars($last_error) . " - " . __('the database query being run was:', 'updraftplus') . ' ' . htmlspecialchars($sql_line) . '<br>'; // First command is expected to be DROP TABLE if (1 == $errors && 2 == $sql_type && 0 == $tables_created) { return new WP_Error('initial_db_error', __('An error occured on the first CREATE TABLE command - aborting run', 'updraftplus')); } if ($errors > 49) { return new WP_Error('too_many_db_errors', __('Too many database errors have occurred - aborting restoration (you will need to restore manually)', 'updraftplus')); } } elseif ($sql_type == 2) { $tables_created++; } if ($line % 50 == 0) { if ($line % 250 == 0 || $line < 250) { $time_taken = microtime(true) - $start_time; echo sprintf(__('Database lines processed: %d in %.2f seconds', 'updraftplus'), $line, $time_taken) . "<br>"; } } # Reset $sql_line = ''; $sql_type = -1; } if ($restoring_table) { $this->restored_table($restoring_table, $import_table_prefix, $old_table_prefix); } $time_taken = microtime(true) - $start_time; echo sprintf(__('Finished: lines processed: %d in %.2f seconds', 'updraftplus'), $line, $time_taken) . "<br>"; gzclose($dbhandle); $wp_filesystem->delete($working_dir . '/backup.db.gz', false, true); } else { // Default action: used for plugins, themes and uploads (and wpcore, via a filter) $dirname = basename($info['path']); show_message($this->strings['moving_old']); $movedin = apply_filters('updraftplus_restore_movein_' . $type, $working_dir, $wp_dir, $wp_filesystem_dir); // A filter, to allow add-ons to perform the install of non-standard entities, or to indicate that it's not possible if (false === $movedin) { show_message($this->strings['not_possible']); } elseif ($movedin !== true) { if ($wp_filesystem->exists($wp_filesystem_dir . "-old")) { // Is better to warn and delete the backup than abort mid-restore and leave inconsistent site echo $wp_filesystem_dir . "-old: " . __('This directory already exists, and will be replaced', 'updraftplus') . '<br>'; # In theory, supply true as the 3rd parameter of true achieves this; in practice, not always so (leads to support requests) $wp_filesystem->delete($wp_filesystem_dir . "-old", true); } if (!$wp_filesystem->move($wp_filesystem_dir, $wp_filesystem_dir . "-old", false)) { return new WP_Error('old_move_failed', $this->strings['old_move_failed']); } // The backup may not actually have /$dirname, since that is info from the present site $dirlist = $wp_filesystem->dirlist($working_dir, true, true); if (is_array($dirlist)) { $move_from = false; foreach ($dirlist as $name => $struc) { if (false === $move_from) { if ($name == $dirname) { $move_from = $working_dir . "/{$dirname}"; } elseif (preg_match('/^([^\\.].*)$/', $name, $fmatch)) { $first_entry = $working_dir . "/" . $fmatch[1]; } } } if ($move_from === false && isset($first_entry)) { echo sprintf(__('Using directory from backup: %s', 'updraftplus'), basename($first_entry)) . '<br>'; $move_from = $first_entry; } } else { # That shouldn't happen. Fall back to default $move_from = $working_dir . "/{$dirname}"; } show_message($this->strings['moving_backup']); if (isset($move_from) && false === $move_from || !$wp_filesystem->move($move_from, $wp_filesystem_dir, true)) { return new WP_Error('new_move_failed', $this->strings['new_move_failed']); } } } // Non-recursive, so the directory needs to be empty show_message($this->strings['cleaning_up']); if (!$wp_filesystem->delete($working_dir)) { echo sprintf(__('Error: %s', 'updraftplus'), $this->strings['delete_failed'] . ' (' . $working_dir . ')') . "<br>"; # List contents // No need to make this a restoration-aborting error condition - it's not #return new WP_Error('delete_failed', $this->strings['delete_failed'].' ('.$working_dir.')'); $dirlist = $wp_filesystem->dirlist($working_dir, true, true); if (is_array($dirlist)) { echo __('Files found:', 'updraftplus') . '<br><ul style="list-style: disc inside;">'; foreach ($dirlist as $name => $struc) { echo "<li>" . htmlspecialchars($name) . "</li>"; } echo '</ul>'; } else { echo __('Unable to enumerate files in that directory.', 'updraftplus') . '<br>'; } } switch ($type) { case 'wpcore': @$wp_filesystem->chmod($wp_filesystem_dir, FS_CHMOD_DIR); // In case we restored a .htaccess which is incorrect for the local setup $this->flush_rewrite_rules(); break; case 'uploads': @$wp_filesystem->chmod($wp_filesystem_dir, 0775, true); break; case 'db': do_action('updraftplus_restored_db', array('expected_oldsiteurl' => $old_siteurl), $import_table_prefix); $this->flush_rewrite_rules(); break; default: @$wp_filesystem->chmod($wp_filesystem_dir, FS_CHMOD_DIR); } return true; }