| 1 |
<?php |
| 2 |
if (!defined('UPDRAFTPLUS_DIR')) die('No direct access allowed'); |
| 3 |
|
| 4 |
if (!class_exists('WP_Upgrader')) require_once(ABSPATH.'wp-admin/includes/class-wp-upgrader.php'); |
| 5 |
|
| 6 |
class Updraft_Restorer extends WP_Upgrader { |
| 7 |
|
| 8 |
public $ud_backup_is_multisite = -1; |
| 9 |
|
| 10 |
private $is_multisite; |
| 11 |
|
| 12 |
// This is just used so far for detecting whether we're on the second run for an entity or not. |
| 13 |
public $been_restored = array(); |
| 14 |
private $tables_been_dropped = array(); |
| 15 |
|
| 16 |
public $delete = false; |
| 17 |
|
| 18 |
private $created_by_version = false; |
| 19 |
|
| 20 |
private $ud_backup_info; |
| 21 |
public $ud_foreign; |
| 22 |
|
| 23 |
# The default of false means "use the global $wpdb" |
| 24 |
private $wpdb_obj = false; |
| 25 |
|
| 26 |
private $line_last_logged = 0; |
| 27 |
|
| 28 |
public function __construct($skin = null, $info = null, $shortinit = false) { |
| 29 |
|
| 30 |
global $wpdb; |
| 31 |
// Line up a wpdb-like object to use |
| 32 |
$this->use_wpdb = ((!function_exists('mysql_query') && !function_exists('mysqli_query')) || !$wpdb->is_mysql || !$wpdb->ready) ? true : false; |
| 33 |
|
| 34 |
if (false == $this->use_wpdb) { |
| 35 |
// We have our own extension which drops lots of the overhead on the query |
| 36 |
$wpdb_obj = new UpdraftPlus_WPDB(DB_USER, DB_PASSWORD, DB_NAME, DB_HOST); |
| 37 |
// Was that successful? |
| 38 |
if (!$wpdb_obj->is_mysql || !$wpdb_obj->ready) { |
| 39 |
$this->use_wpdb = true; |
| 40 |
} else { |
| 41 |
$this->wpdb_obj = $wpdb_obj; |
| 42 |
$this->mysql_dbh = $wpdb_obj->updraftplus_getdbh(); |
| 43 |
$this->use_mysqli = $wpdb_obj->updraftplus_use_mysqli(); |
| 44 |
} |
| 45 |
} |
| 46 |
|
| 47 |
if ($shortinit) return; |
| 48 |
$this->ud_backup_info = $info; |
| 49 |
$this->ud_foreign = (empty($info['meta_foreign'])) ? false : $info['meta_foreign']; |
| 50 |
parent::__construct($skin); |
| 51 |
$this->init(); |
| 52 |
$this->backup_strings(); |
| 53 |
$this->is_multisite = is_multisite(); |
| 54 |
} |
| 55 |
|
| 56 |
function backup_strings() { |
| 57 |
$this->strings['not_possible'] = __('UpdraftPlus is not able to directly restore this kind of entity. It must be restored manually.','updraftplus'); |
| 58 |
$this->strings['no_package'] = __('Backup file not available.','updraftplus'); |
| 59 |
$this->strings['copy_failed'] = __('Copying this entity failed.','updraftplus'); |
| 60 |
$this->strings['unpack_package'] = __('Unpacking backup...','updraftplus'); |
| 61 |
$this->strings['decrypt_database'] = __('Decrypting database (can take a while)...','updraftplus'); |
| 62 |
$this->strings['decrypted_database'] = __('Database successfully decrypted.','updraftplus'); |
| 63 |
$this->strings['moving_old'] = __('Moving old data out of the way...','updraftplus'); |
| 64 |
$this->strings['moving_backup'] = __('Moving unpacked backup into place...','updraftplus'); |
| 65 |
$this->strings['restore_database'] = __('Restoring the database (on a large site this can take a long time - if it times out (which can happen if your web hosting company has configured your hosting to limit resources) then you should use a different method, such as phpMyAdmin)...','updraftplus'); |
| 66 |
$this->strings['cleaning_up'] = __('Cleaning up rubbish...','updraftplus'); |
| 67 |
$this->strings['old_move_failed'] = __('Could not move old files out of the way.','updraftplus').' '.__('You should check the file permissions in your WordPress installation', 'updraftplus'); |
| 68 |
$this->strings['old_delete_failed'] = __('Could not delete old directory.','updraftplus'); |
| 69 |
$this->strings['new_move_failed'] = __('Could not move new files into place. Check your wp-content/upgrade folder.','updraftplus'); |
| 70 |
$this->strings['move_failed'] = __('Could not move the files into place. Check your file permissions.','updraftplus'); |
| 71 |
$this->strings['delete_failed'] = __('Failed to delete working directory after restoring.','updraftplus'); |
| 72 |
$this->strings['multisite_error'] = __('You are running on WordPress multisite - but your backup is not of a multisite site.', 'updraftplus'); |
| 73 |
$this->strings['unpack_failed'] = __('Failed to unpack the archive', 'updraftplus'); |
| 74 |
} |
| 75 |
|
| 76 |
# This function is copied from class WP_Upgrader (WP 3.8 - no significant changes since 3.2 at least); we only had to fork it because it hard-codes using the basename of the zip file as its unpack directory; which can be long; and then combining that with long pathnames in the zip being unpacked can overflow a 256-character path limit (yes, they apparently still exist - amazing!) |
| 77 |
# Subsequently, we have also added the ability to unpack tarballs |
| 78 |
private function unpack_package_archive($package, $delete_package = true, $type = false) { |
| 79 |
|
| 80 |
if (!empty($this->ud_foreign) && !empty($this->ud_foreign_working_dir)) { |
| 81 |
if (is_dir($this->ud_foreign_working_dir)) { |
| 82 |
return $this->ud_foreign_working_dir; |
| 83 |
} else { |
| 84 |
global $updraftplus; |
| 85 |
$updraftplus->log('Previously unpacked directory seems to have disappeared; will unpack again'); |
| 86 |
} |
| 87 |
} |
| 88 |
|
| 89 |
global $wp_filesystem, $updraftplus; |
| 90 |
|
| 91 |
$packsize = round(filesize($package)/1048576, 1).' Mb'; |
| 92 |
|
| 93 |
$this->skin->feedback($this->strings['unpack_package'].' ('.basename($package).', '.$packsize.')'); |
| 94 |
|
| 95 |
$upgrade_folder = $wp_filesystem->wp_content_dir() . 'upgrade/'; |
| 96 |
|
| 97 |
//Clean up contents of upgrade directory beforehand. |
| 98 |
$upgrade_files = $wp_filesystem->dirlist($upgrade_folder); |
| 99 |
if ( !empty($upgrade_files) ) { |
| 100 |
foreach ( $upgrade_files as $file ) |
| 101 |
$wp_filesystem->delete($upgrade_folder . $file['name'], true); |
| 102 |
} |
| 103 |
|
| 104 |
//We need a working directory |
| 105 |
#This is the only change from the WP core version - minimise path length |
| 106 |
#$working_dir = $upgrade_folder . basename($package, '.zip'); |
| 107 |
$working_dir = $upgrade_folder . substr(md5($package), 0, 8); |
| 108 |
|
| 109 |
// Clean up working directory |
| 110 |
if ( $wp_filesystem->is_dir($working_dir) ) |
| 111 |
$wp_filesystem->delete($working_dir, true); |
| 112 |
|
| 113 |
// Unzip package to working directory |
| 114 |
if ('.zip' == strtolower(substr($package, -4, 4))) { |
| 115 |
$result = unzip_file( $package, $working_dir ); |
| 116 |
} elseif ('.tar' == strtolower(substr($package, -4, 4)) || '.tar.gz' == strtolower(substr($package, -7, 7)) || '.tar.bz2' == strtolower(substr($package, -8, 8))) { |
| 117 |
if (!class_exists('UpdraftPlus_Archive_Tar')) { |
| 118 |
if (false === strpos(get_include_path(), UPDRAFTPLUS_DIR.'/includes/PEAR')) set_include_path(UPDRAFTPLUS_DIR.'/includes/PEAR'.PATH_SEPARATOR.get_include_path()); |
| 119 |
|
| 120 |
require_once(UPDRAFTPLUS_DIR.'/includes/PEAR/Archive/Tar.php'); |
| 121 |
$p_compress = null; |
| 122 |
if ('.tar.gz' == strtolower(substr($package, -7, 7))) { |
| 123 |
$p_compress = 'gz'; |
| 124 |
} elseif ('.tar.bz2' == strtolower(substr($package, -8, 8))) { |
| 125 |
$p_compress = 'bz2'; |
| 126 |
} |
| 127 |
|
| 128 |
# It's not pretty. But it works. |
| 129 |
if (is_a($wp_filesystem, 'WP_Filesystem_Direct')) { |
| 130 |
$extract_dir = $working_dir; |
| 131 |
} else { |
| 132 |
$updraft_dir = $updraftplus->backups_dir_location(); |
| 133 |
if (!$updraftplus->really_is_writable($updraft_dir)) { |
| 134 |
$updraftplus->log_e("Backup directory (%s) is not writable, or does not exist.", $updraft_dir); |
| 135 |
$result = new WP_Error('unpack_failed', $this->strings['unpack_failed'], $tar->extract); |
| 136 |
} else { |
| 137 |
$extract_dir = $updraft_dir.'/'.basename($working_dir).'-old'; |
| 138 |
if (file_exists($extract_dir)) $updraftplus->remove_local_directory($extract_dir); |
| 139 |
$updraftplus->log("Using a temporary folder to extract before moving over WPFS: $extract_dir"); |
| 140 |
} |
| 141 |
} |
| 142 |
# Slightly hackish - rather than re-write Archive_Tar to use wp_filesystem, we instead unpack into the location that we already require to be directly writable for other reasons, and then move from there. |
| 143 |
|
| 144 |
if (empty($result)) { |
| 145 |
|
| 146 |
$this->ud_extract_count = 0; |
| 147 |
$this->ud_working_dir = trailingslashit($working_dir); |
| 148 |
$this->ud_extract_dir = untrailingslashit($extract_dir); |
| 149 |
$this->ud_made_dirs = array(); |
| 150 |
add_filter('updraftplus_tar_wrote', array($this, 'tar_wrote'), 10, 2); |
| 151 |
$tar = new UpdraftPlus_Archive_Tar($package, $p_compress); |
| 152 |
$result = $tar->extract($extract_dir, false); |
| 153 |
if (!is_a($wp_filesystem, 'WP_Filesystem_Direct')) $updraftplus->remove_local_directory($extract_dir); |
| 154 |
if (true != $result) { |
| 155 |
$result = new WP_Error('unpack_failed', $this->strings['unpack_failed'], $result); |
| 156 |
} else { |
| 157 |
if (!is_a($wp_filesystem, 'WP_Filesystem_Direct')) { |
| 158 |
$updraftplus->log('Moved unpacked tarball contents'); |
| 159 |
} |
| 160 |
} |
| 161 |
remove_filter('updraftplus_tar_wrote', array($this, 'tar_wrote'), 10, 2); |
| 162 |
} |
| 163 |
|
| 164 |
} |
| 165 |
} |
| 166 |
|
| 167 |
// Once extracted, delete the package if required. |
| 168 |
if ( $delete_package ) |
| 169 |
unlink($package); |
| 170 |
|
| 171 |
if ( is_wp_error($result) ) { |
| 172 |
$wp_filesystem->delete($working_dir, true); |
| 173 |
if ( 'incompatible_archive' == $result->get_error_code() ) { |
| 174 |
return new WP_Error( 'incompatible_archive', $this->strings['incompatible_archive'], $result->get_error_data() ); |
| 175 |
} |
| 176 |
return $result; |
| 177 |
} |
| 178 |
|
| 179 |
if (!empty($this->ud_foreign)) { |
| 180 |
$this->ud_foreign_working_dir = $working_dir; |
| 181 |
# Zip containing an SQL file. We only know of one pattern. |
| 182 |
if ('db' === $type) { |
| 183 |
$basepack = basename($package, '.zip'); |
| 184 |
if ($wp_filesystem->exists($working_dir.'/'.$basepack.'.sql')) { |
| 185 |
$wp_filesystem->move($working_dir.'/'.$basepack.'.sql', $working_dir . "/backup.db", true); |
| 186 |
$updraftplus->log("Moving database file $basepack.sql to backup.db"); |
| 187 |
} |
| 188 |
} |
| 189 |
} |
| 190 |
|
| 191 |
return $working_dir; |
| 192 |
} |
| 193 |
|
| 194 |
public function tar_wrote($result, $file) { |
| 195 |
if (0 !== strpos($file, $this->ud_extract_dir)) return false; |
| 196 |
global $wp_filesystem, $updraftplus; |
| 197 |
if (!is_a($wp_filesystem, 'WP_Filesystem_Direct')) { |
| 198 |
$modint = 100; |
| 199 |
$leaf = substr($file, strlen($this->ud_extract_dir)); |
| 200 |
$dirname = dirname($leaf); |
| 201 |
$need_dirs = explode('/', $dirname); |
| 202 |
if (empty($this->ud_made_dirs[$dirname])) { |
| 203 |
$cdir = ''; |
| 204 |
foreach ($need_dirs as $ndir) { |
| 205 |
$cdir .= ($cdir) ? '/'.$ndir : $ndir; |
| 206 |
if (empty($this->ud_made_dirs[$cdir])) { |
| 207 |
if ( !$wp_filesystem->mkdir( $this->ud_working_dir.$cdir, FS_CHMOD_DIR) && ! $wp_filesystem->is_dir($this->ud_working_dir.$cdir) ) { |
| 208 |
$updraftplus->log("Failed to create WPFS directory: ".$this->ud_working_dir.$cdir); |
| 209 |
return false; |
| 210 |
} else { |
| 211 |
$this->ud_made_dirs[$cdir] = true; |
| 212 |
} |
| 213 |
} |
| 214 |
} |
| 215 |
} |
| 216 |
$put = $wp_filesystem->put_contents($this->ud_working_dir.$leaf, file_get_contents($file)); |
| 217 |
if (is_wp_error($put)) $updraftplus->log_wp_error($put); |
| 218 |
@unlink($file); |
| 219 |
} else { |
| 220 |
$modint = 500; |
| 221 |
$put = true; |
| 222 |
} |
| 223 |
if ($put) { |
| 224 |
$this->ud_extract_count++; |
| 225 |
if ($this->ud_extract_count % $modint == 0) { |
| 226 |
$updraftplus->log_e("%s files have been extracted", $this->ud_extract_count); |
| 227 |
} |
| 228 |
} |
| 229 |
return ($put == true); |
| 230 |
} |
| 231 |
|
| 232 |
// This returns a wp_filesystem location (and we musn't change that, as we must retain compatibility with the class parent) |
| 233 |
function unpack_package($package, $delete_package = true, $type = false) { |
| 234 |
|
| 235 |
global $wp_filesystem, $updraftplus; |
| 236 |
|
| 237 |
$updraft_dir = $updraftplus->backups_dir_location(); |
| 238 |
|
| 239 |
// If not database, then it is a zip - unpack in the usual way |
| 240 |
#if (!preg_match('/db\.gz(\.crypt)?$/i', $package)) return parent::unpack_package($updraft_dir.'/'.$package, $delete_package); |
| 241 |
if (!preg_match('/db\.gz(\.crypt)?$/i', $package) && !preg_match('/\.sql(\.gz)?$/i', $package)) return $this->unpack_package_archive($updraft_dir.'/'.$package, $delete_package, $type); |
| 242 |
|
| 243 |
$backup_dir = $wp_filesystem->find_folder($updraft_dir); |
| 244 |
|
| 245 |
// Unpack a database. The general shape of the following is copied from class-wp-upgrader.php |
| 246 |
|
| 247 |
@set_time_limit(1800); |
| 248 |
|
| 249 |
$this->skin->feedback('unpack_package'); |
| 250 |
|
| 251 |
$upgrade_folder = $wp_filesystem->wp_content_dir() . 'upgrade/'; |
| 252 |
@$wp_filesystem->mkdir($upgrade_folder, octdec($this->calculate_additive_chmod_oct(FS_CHMOD_DIR, 0775))); |
| 253 |
|
| 254 |
//Clean up contents of upgrade directory beforehand. |
| 255 |
$upgrade_files = $wp_filesystem->dirlist($upgrade_folder); |
| 256 |
if ( !empty($upgrade_files) ) { |
| 257 |
foreach ( $upgrade_files as $file ) |
| 258 |
$wp_filesystem->delete($upgrade_folder.$file['name'], true); |
| 259 |
} |
| 260 |
|
| 261 |
//We need a working directory |
| 262 |
$working_dir = $upgrade_folder . basename($package, '.crypt'); |
| 263 |
# $working_dir_localpath = WP_CONTENT_DIR.'/upgrade/'. basename($package, '.crypt'); |
| 264 |
|
| 265 |
// Clean up working directory |
| 266 |
if ($wp_filesystem->is_dir($working_dir)) $wp_filesystem->delete($working_dir, true); |
| 267 |
|
| 268 |
if (!$wp_filesystem->mkdir($working_dir, octdec($this->calculate_additive_chmod_oct(FS_CHMOD_DIR, 0775)))) return new WP_Error('mkdir_failed', __('Failed to create a temporary directory','updraftplus').' ('.$working_dir.')'); |
| 269 |
|
| 270 |
// Unpack package to working directory |
| 271 |
if ($updraftplus->is_db_encrypted($package)) { |
| 272 |
$this->skin->feedback('decrypt_database'); |
| 273 |
$encryption = UpdraftPlus_Options::get_updraft_option('updraft_encryptionphrase'); |
| 274 |
if (!$encryption) return new WP_Error('no_encryption_key', __('Decryption failed. The database file is encrypted, but you have no encryption key entered.', 'updraftplus')); |
| 275 |
|
| 276 |
$plaintext = $updraftplus->decrypt(false, $encryption, $wp_filesystem->get_contents($backup_dir.$package)); |
| 277 |
|
| 278 |
if ($plaintext) { |
| 279 |
$this->skin->feedback('decrypted_database'); |
| 280 |
if (!$wp_filesystem->put_contents($working_dir.'/backup.db.gz', $plaintext)) { |
| 281 |
return new WP_Error('write_failed', __('Failed to write out the decrypted database to the filesystem','updraftplus')); |
| 282 |
} |
| 283 |
} else { |
| 284 |
return new WP_Error('decryption_failed', __('Decryption failed. The most likely cause is that you used the wrong key.','updraftplus')); |
| 285 |
} |
| 286 |
} else { |
| 287 |
|
| 288 |
if (preg_match('/\.sql$/i', $package)) { |
| 289 |
if (!$wp_filesystem->copy($backup_dir.$package, $working_dir.'/backup.db')) { |
| 290 |
if ( $wp_filesystem->errors->get_error_code() ) { |
| 291 |
foreach ( $wp_filesystem->errors->get_error_messages() as $message ) show_message($message); |
| 292 |
} |
| 293 |
return new WP_Error('copy_failed', $this->strings['copy_failed']); |
| 294 |
} |
| 295 |
} elseif (!$wp_filesystem->copy($backup_dir.$package, $working_dir.'/backup.db.gz')) { |
| 296 |
if ( $wp_filesystem->errors->get_error_code() ) { |
| 297 |
foreach ( $wp_filesystem->errors->get_error_messages() as $message ) show_message($message); |
| 298 |
} |
| 299 |
return new WP_Error('copy_failed', $this->strings['copy_failed']); |
| 300 |
} |
| 301 |
|
| 302 |
} |
| 303 |
|
| 304 |
// Once extracted, delete the package if required (non-recursive, is a file) |
| 305 |
if ($delete_package) $wp_filesystem->delete($backup_dir.$package, false, true); |
| 306 |
|
| 307 |
$updraftplus->log("Database successfully unpacked"); |
| 308 |
|
| 309 |
return $working_dir; |
| 310 |
|
| 311 |
} |
| 312 |
|
| 313 |
// For moving files out of a directory into their new location |
| 314 |
// The purposes of the $type parameter are 1) to detect 'others' and apply a historical bugfix 2) to detect wpcore, and apply the setting for what to do with wp-config.php 3) to work out whether to delete the directory itself |
| 315 |
// Must use only wp_filesystem |
| 316 |
// $dest_dir must already have a trailing slash |
| 317 |
// $preserve_existing: this setting only applies at the top level: 0 = overwrite with no backup; 1 = make backup of existing; 2 = do nothing if there is existing, 3 = do nothing to the top level directory, but do copy-in contents. Thus, on a multi-archive set where you want a backup, you'd do this: first call with $preserve_existing === 1, then on subsequent zips call with 3 |
| 318 |
public function move_backup_in($working_dir, $dest_dir, $preserve_existing = 1, $do_not_overwrite = array('plugins', 'themes', 'uploads', 'upgrade'), $type = 'not-others', $send_actions = false, $force_local = false) { |
| 319 |
|
| 320 |
global $wp_filesystem, $updraftplus; |
| 321 |
$updraft_dir = $updraftplus->backups_dir_location(); |
| 322 |
|
| 323 |
# && !is_a($wp_filesystem, 'WP_Filesystem_Direct') |
| 324 |
if (true == $force_local) { |
| 325 |
$wpfs = new UpdraftPlus_WP_Filesystem_Direct(true); |
| 326 |
} else { |
| 327 |
$wpfs = $wp_filesystem; |
| 328 |
} |
| 329 |
|
| 330 |
# Get the content to be moved in. Include hidden files = true. Recursion is only required if we're likely to copy-in |
| 331 |
$recursive = (3 == $preserve_existing) ? true : false; |
| 332 |
$upgrade_files = $wpfs->dirlist($working_dir, true, $recursive); |
| 333 |
|
| 334 |
if (empty($upgrade_files)) return true; |
| 335 |
|
| 336 |
if (!$wpfs->is_dir($dest_dir)) { |
| 337 |
return new WP_Error('no_such_dir', __('The directory does not exist', 'updraftplus')." ($dest_dir)"); |
| 338 |
// $updraftplus->log_e("The directory does not exist, so will be created (%s).", $dest_dir); |
| 339 |
// # Attempts to create the directory fail, as due to a core bug, $dest_dir will be the wrong value if it did not already exist (at least for themes - the value of it depends on an is_dir() check wrongly used to detect a relative path) |
| 340 |
// if (!$wpfs->mkdir($dest_dir)) { |
| 341 |
// return new WP_Error('create_failed', __('Failed to create directory', 'updraftplus')." ($dest_dir)"); |
| 342 |
// } |
| 343 |
} |
| 344 |
|
| 345 |
$wpcore_config_moved = false; |
| 346 |
|
| 347 |
foreach ( $upgrade_files as $file => $filestruc ) { |
| 348 |
|
| 349 |
if (empty($file)) continue; |
| 350 |
|
| 351 |
if ($dest_dir.$file == $updraft_dir) { |
| 352 |
$updraftplus->log('Skipping attempt to replace updraft_dir whilst processing '.$type); |
| 353 |
continue; |
| 354 |
} |
| 355 |
|
| 356 |
// Correctly restore files in 'others' in no directory that were wrongly backed up in versions 1.4.0 - 1.4.48 |
| 357 |
if (('others' == $type || 'wpcore' == $type) && preg_match('/^([\-_A-Za-z0-9]+\.php)$/i', $file, $matches) && $wpfs->exists($working_dir . "/$file/$file")) { |
| 358 |
if ('others' == $type) { |
| 359 |
echo "Found file: $file/$file: presuming this is a backup with a known fault (backup made with versions 1.4.0 - 1.4.48, and sometimes up to 1.6.55 on some Windows servers); will rename to simply $file<br>"; |
| 360 |
} else { |
| 361 |
echo "Found file: $file/$file: presuming this is a backup with a known fault (backup made with versions before 1.6.55 in certain situations on Windows servers); will rename to simply $file<br>"; |
| 362 |
} |
| 363 |
$updraftplus->log("$file/$file: rename to $file"); |
| 364 |
$file = $matches[1]; |
| 365 |
$tmp_file = rand(0,999999999).'.php'; |
| 366 |
// Rename directory |
| 367 |
$wpfs->move($working_dir . "/$file", $working_dir . "/".$tmp_file, true); |
| 368 |
$wpfs->move($working_dir . "/$tmp_file/$file", $working_dir ."/".$file, true); |
| 369 |
$wpfs->rmdir($working_dir . "/$tmp_file", false); |
| 370 |
} |
| 371 |
|
| 372 |
if ('wp-config.php' == $file && 'wpcore' == $type) { |
| 373 |
if (empty($_POST['updraft_restorer_wpcore_includewpconfig'])) { |
| 374 |
$updraftplus->log_e('wp-config.php from backup: will restore as wp-config-backup.php', 'updraftplus'); |
| 375 |
$wpfs->move($working_dir . "/$file", $working_dir . "/wp-config-backup.php", true); |
| 376 |
$file = "wp-config-backup.php"; |
| 377 |
$wpcore_config_moved = true; |
| 378 |
} else { |
| 379 |
$updraftplus->log_e("wp-config.php from backup: restoring (as per user's request)", 'updraftplus'); |
| 380 |
} |
| 381 |
} elseif ('wpcore' == $type && 'wp-config-backup.php' == $file && $wpcore_config_moved) { |
| 382 |
# The file is already gone; nothing to do |
| 383 |
continue; |
| 384 |
} |
| 385 |
|
| 386 |
# Sanity check (should not be possible as these were excluded at backup time) |
| 387 |
if (in_array($file, $do_not_overwrite)) continue; |
| 388 |
|
| 389 |
if (('object-cache.php' == $file || 'advanced-cache.php' == $file) && 'others' == $type) { |
| 390 |
if (false == apply_filters('updraftplus_restorecachefiles', true, $file)) { |
| 391 |
$nfile = preg_replace('/\.php$/', '-backup.php', $file); |
| 392 |
$wpfs->move($working_dir . "/$file", $working_dir . "/".$nfile, true); |
| 393 |
$file=$nfile; |
| 394 |
} |
| 395 |
} elseif (('object-cache-backup.php' == $file || 'advanced-cache-backup.php' == $file) && 'others' == $type) { |
| 396 |
$wpfs->delete($working_dir."/".$file); |
| 397 |
continue; |
| 398 |
} |
| 399 |
|
| 400 |
# First, move the existing one, if necessary (may not be present) |
| 401 |
if ($wpfs->exists($dest_dir.$file)) { |
| 402 |
if ($preserve_existing == 1) { |
| 403 |
# Move existing to -old |
| 404 |
if ( !$wpfs->move($dest_dir.$file, $dest_dir.$file.'-old', true) ) { |
| 405 |
return new WP_Error('old_move_failed', $this->strings['old_move_failed']." ($dest_dir.$file)"); |
| 406 |
} |
| 407 |
} elseif ($preserve_existing == 0) { |
| 408 |
# Over-write, no backup |
| 409 |
if (!$wpfs->delete($dest_dir.$file, true)) { |
| 410 |
return new WP_Error('old_delete_failed', $this->strings['old_delete_failed']." ($file)"); |
| 411 |
} |
| 412 |
} |
| 413 |
} |
| 414 |
|
| 415 |
# Secondly, move in the new one |
| 416 |
if (2 == $preserve_existing && $wpfs->exists($dest_dir.$file)) { |
| 417 |
# Something exists - no move. Remove it from the temporary directory - so that it will be clean later |
| 418 |
@$wpfs->delete($working_dir.'/'.$file, true); |
| 419 |
} elseif (3 != $preserve_existing || !$wpfs->exists($dest_dir.$file)) { |
| 420 |
$is_dir = $wpfs->is_dir($working_dir."/".$file); |
| 421 |
# This method is broken due to https://core.trac.wordpress.org/ticket/26598 |
| 422 |
#if (empty($chmod)) $chmod = $wpfs->getnumchmodfromh($wpfs->gethchmod($dest_dir)); |
| 423 |
if (empty($chmod)) $chmod = octdec(sprintf("%04d", $this->get_current_chmod($dest_dir, $wpfs))); |
| 424 |
if ($wpfs->move($working_dir."/".$file, $dest_dir.$file, true) ) { |
| 425 |
if ($send_actions) do_action('updraftplus_restored_'.$type.'_one', $file); |
| 426 |
# Make sure permissions are at least as great as those of the parent |
| 427 |
if ($is_dir && !empty($chmod)) $this->chmod_if_needed($dest_dir.$file, $chmod, false, $wpfs); |
| 428 |
} else { |
| 429 |
return new WP_Error('move_failed', $this->strings['move_failed'], $working_dir."/".$file." -> ".$dest_dir.$file); |
| 430 |
} |
| 431 |
} elseif (3 == $preserve_existing && !empty($filestruc['files'])) { |
| 432 |
# The directory ($dest_dir) already exists, and we've been requested to copy-in. We need to perform the recursive copy-in |
| 433 |
# $filestruc['files'] is then a new structure like $upgrade_files |
| 434 |
# First pass: create directory structure |
| 435 |
# Get chmod value for the parent directory, and re-use it (instead of passing false) |
| 436 |
|
| 437 |
# This method is broken due to https://core.trac.wordpress.org/ticket/26598 |
| 438 |
#if (empty($chmod)) $chmod = $wpfs->getnumchmodfromh($wpfs->gethchmod($dest_dir)); |
| 439 |
if (empty($chmod)) $chmod = octdec(sprintf("%04d", $this->get_current_chmod($dest_dir, $wpfs))); |
| 440 |
# Copy in the files. This also needs to make sure the directories exist, in case the zip file lacks entries |
| 441 |
$delete_root = ('others' == $type || 'wpcore' == $type) ? false : true; |
| 442 |
|
| 443 |
$copy_in = $this->copy_files_in($working_dir.'/'.$file, $dest_dir.$file, $filestruc['files'], $chmod, $delete_root); |
| 444 |
if (!empty($chmod)) $this->chmod_if_needed($dest_dir.$file, $chmod, false, $wpfs); |
| 445 |
|
| 446 |
if (is_wp_error($copy_in)) return $copy_in; |
| 447 |
if (!$copy_in) return new WP_Error('move_failed', $this->strings['move_failed'], "(2) ".$working_dir.'/'.$file." -> ".$dest_dir.$file); |
| 448 |
|
| 449 |
$wpfs->rmdir($working_dir.'/'.$file); |
| 450 |
} else { |
| 451 |
$wpfs->rmdir($working_dir.'/'.$file); |
| 452 |
} |
| 453 |
} |
| 454 |
|
| 455 |
return true; |
| 456 |
|
| 457 |
} |
| 458 |
|
| 459 |
# $dest_dir must already exist |
| 460 |
private function copy_files_in($source_dir, $dest_dir, $files, $chmod = false, $deletesource = false) { |
| 461 |
global $wp_filesystem, $updraftplus; |
| 462 |
foreach ($files as $rname => $rfile) { |
| 463 |
if ('d' != $rfile['type']) { |
| 464 |
# Delete it if it already exists (or perhaps WP does it for us) |
| 465 |
if (!$wp_filesystem->move($source_dir.'/'.$rname, $dest_dir.'/'.$rname, true)) { |
| 466 |
$updraftplus->log_e('Failed to move file (check your file permissions and disk quota): %s', $source_dir.'/'.$rname." -> ".$dest_dir.'/'.$rname); |
| 467 |
return false; |
| 468 |
} |
| 469 |
} else { |
| 470 |
# Directory |
| 471 |
if ($wp_filesystem->is_file($dest_dir.'/'.$rname)) @$wp_filesystem->delete($dest_dir.'/'.$rname, false, 'f'); |
| 472 |
# No such directory yet: just move it |
| 473 |
if (!$wp_filesystem->is_dir($dest_dir.'/'.$rname)) { |
| 474 |
if (!$wp_filesystem->move($source_dir.'/'.$rname, $dest_dir.'/'.$rname, false)) { |
| 475 |
$updraftplus->log_e('Failed to move directory (check your file permissions and disk quota): %s', $source_dir.'/'.$rname." -> ".$dest_dir.'/'.$rname); |
| 476 |
return false; |
| 477 |
} |
| 478 |
} elseif (!empty($rfile['files'])) { |
| 479 |
# There is a directory - and we want to to copy in |
| 480 |
$docopy = $this->copy_files_in($source_dir.'/'.$rname, $dest_dir.'/'.$rname, $rfile['files'], $chmod, false); |
| 481 |
if (is_wp_error($docopy)) return $docopy; |
| 482 |
if (false === $docopy) { |
| 483 |
return false; |
| 484 |
} |
| 485 |
} else { |
| 486 |
# There is a directory: but nothing to copy in to it |
| 487 |
@$wp_filesystem->rmdir($source_dir.'/'.$rname); |
| 488 |
} |
| 489 |
} |
| 490 |
} |
| 491 |
# We are meant to leave the working directory empty. Hence, need to rmdir() once a directory is empty. But not the root of it all in case of others/wpcore. |
| 492 |
if ($deletesource || strpos($source_dir, '/') !== false) { |
| 493 |
$wp_filesystem->rmdir($source_dir, false); |
| 494 |
} |
| 495 |
|
| 496 |
return true; |
| 497 |
|
| 498 |
} |
| 499 |
|
| 500 |
// Pre-flight check: chance to complain and abort before anything at all is done |
| 501 |
public function pre_restore_backup($backup_files, $type, $info) { |
| 502 |
|
| 503 |
if (is_string($backup_files)) $backup_files=array($backup_files); |
| 504 |
|
| 505 |
if ('more' == $type) { |
| 506 |
$this->skin->feedback('not_possible'); |
| 507 |
return; |
| 508 |
} |
| 509 |
|
| 510 |
// Ensure access to the indicated directory - and to WP_CONTENT_DIR (in which we use upgrade/) |
| 511 |
$need_these = array(WP_CONTENT_DIR); |
| 512 |
if (!empty($info['path'])) $need_these[] = $info['path']; |
| 513 |
|
| 514 |
$res = $this->fs_connect($need_these); |
| 515 |
if (false === $res || is_wp_error($res)) return $res; |
| 516 |
|
| 517 |
# Check upgrade directory is writable (instead of having non-obvious messages when we try to write) |
| 518 |
# In theory, this is redundant (since we already checked for access to WP_CONTENT_DIR); but in practice, this extra check has been needed |
| 519 |
|
| 520 |
global $wp_filesystem, $updraftplus, $updraftplus_admin, $updraftplus_addons_migrator; |
| 521 |
|
| 522 |
if (empty($this->pre_restore_updatedir_writable)) { |
| 523 |
$upgrade_folder = $wp_filesystem->wp_content_dir() . 'upgrade/'; |
| 524 |
@$wp_filesystem->mkdir($upgrade_folder, octdec($this->calculate_additive_chmod_oct(FS_CHMOD_DIR, 0775))); |
| 525 |
if (!$wp_filesystem->is_dir($upgrade_folder)) { |
| 526 |
return new WP_Error('no_dir', sprintf(__('UpdraftPlus needed to create a %s in your content directory, but failed - please check your file permissions and enable the access (%s)', 'updraftplus'), __('folder', 'updraftplus'), $upgrade_folder)); |
| 527 |
} |
| 528 |
$rand_file = 'testfile_'.rand(0,9999999).md5(microtime(true)).'.txt'; |
| 529 |
if ($wp_filesystem->put_contents($upgrade_folder.$rand_file, 'testing...')) { |
| 530 |
@$wp_filesystem->delete($upgrade_folder.$rand_file); |
| 531 |
$this->pre_restore_updatedir_writable = true; |
| 532 |
} else { |
| 533 |
return new WP_Error('no_file', sprintf(__('UpdraftPlus needed to create a %s in your content directory, but failed - please check your file permissions and enable the access (%s)', 'updraftplus'), __('file', 'updraftplus'), $upgrade_folder.$rand_file)); |
| 534 |
} |
| 535 |
} |
| 536 |
|
| 537 |
# Code below here assumes that we're dealing with file-based entities |
| 538 |
if ('db' == $type) return true; |
| 539 |
|
| 540 |
$wp_filesystem_dir = $this->get_wp_filesystem_dir($info['path']); |
| 541 |
if ($wp_filesystem_dir === false) return false; |
| 542 |
|
| 543 |
// $this->maintenance_mode(true); |
| 544 |
// |
| 545 |
// $updraftplus->log_e('Testing file permissions...'); |
| 546 |
|
| 547 |
$ret_val = true; |
| 548 |
|
| 549 |
$updraft_dir = $updraftplus->backups_dir_location(); |
| 550 |
|
| 551 |
if (('plugins' == $type || 'uploads' == $type || 'themes' == $type) && (!is_multisite() || $this->ud_backup_is_multisite !== 0 || ('uploads' != $type || empty($updraftplus_addons_migrator->new_blogid )))) { |
| 552 |
// if ($wp_filesystem->exists($wp_filesystem_dir.'-old')) { |
| 553 |
if (file_exists($updraft_dir.'/'.basename($wp_filesystem_dir)."-old")) { |
| 554 |
$ret_val = new WP_Error('already_exists', sprintf(__('Existing unremoved folders from a previous restore exist (please use the "Delete Old Directories" button to delete them before trying again): %s', 'updraftplus'), $wp_filesystem_dir.'-old')); |
| 555 |
|
| 556 |
} else { |
| 557 |
// No longer used - since we now do not move the directories themselves |
| 558 |
// # File permissions test; see if we can move the directory back and forth |
| 559 |
// if (!$wp_filesystem->move($wp_filesystem_dir, $wp_filesystem_dir."-old", false)) { |
| 560 |
// $ret_val = new WP_Error('old_move_failed', $this->strings['old_move_failed']); |
| 561 |
// } else { |
| 562 |
// $wp_filesystem->move($wp_filesystem_dir."-old", $wp_filesystem_dir, false); |
| 563 |
// } |
| 564 |
} |
| 565 |
} |
| 566 |
|
| 567 |
// $this->maintenance_mode(false); |
| 568 |
|
| 569 |
if (!empty($this->ud_foreign)) { |
| 570 |
$known_foreigners = apply_filters('updraftplus_accept_archivename', array()); |
| 571 |
if (!is_array($known_foreigners) || empty($known_foreigners[$this->ud_foreign])) { |
| 572 |
return new WP_Error('uk_foreign', __('This version of UpdraftPlus does not know how to handle this type of foreign backup', 'updraftplus').' ('.$this->ud_foreign.')'); |
| 573 |
} |
| 574 |
} |
| 575 |
|
| 576 |
return $ret_val; |
| 577 |
} |
| 578 |
|
| 579 |
private function get_wp_filesystem_dir($path) { |
| 580 |
global $wp_filesystem; |
| 581 |
// Get the wp_filesystem location for the folder on the local install |
| 582 |
switch ($path) { |
| 583 |
case ABSPATH: |
| 584 |
case ''; |
| 585 |
$wp_filesystem_dir = $wp_filesystem->abspath(); |
| 586 |
break; |
| 587 |
case WP_CONTENT_DIR: |
| 588 |
$wp_filesystem_dir = $wp_filesystem->wp_content_dir(); |
| 589 |
break; |
| 590 |
case WP_PLUGIN_DIR: |
| 591 |
$wp_filesystem_dir = $wp_filesystem->wp_plugins_dir(); |
| 592 |
break; |
| 593 |
case WP_CONTENT_DIR . '/themes': |
| 594 |
$wp_filesystem_dir = $wp_filesystem->wp_themes_dir(); |
| 595 |
break; |
| 596 |
default: |
| 597 |
$wp_filesystem_dir = $wp_filesystem->find_folder($path); |
| 598 |
break; |
| 599 |
} |
| 600 |
if ( ! $wp_filesystem_dir ) return false; |
| 601 |
return untrailingslashit($wp_filesystem_dir); |
| 602 |
} |
| 603 |
|
| 604 |
// $backup_file is just the basename, and must be a string; we expect the caller to deal with looping over an array (multi-archive sets). We do, however, record whether we have already unpacked an entity of the same type - so that we know to add (not replace). |
| 605 |
public function restore_backup($backup_file, $type, $info, $last_one = false) { |
| 606 |
|
| 607 |
if ('more' == $type) { |
| 608 |
$this->skin->feedback('not_possible'); |
| 609 |
return; |
| 610 |
} |
| 611 |
|
| 612 |
global $wp_filesystem, $updraftplus_addons_migrator, $updraftplus; |
| 613 |
|
| 614 |
$updraftplus->log("restore_backup(backup_file=$backup_file, type=$type, info=".serialize($info).", last_one=$last_one)"); |
| 615 |
|
| 616 |
$updraft_dir = $updraftplus->backups_dir_location(); |
| 617 |
|
| 618 |
$get_dir = (empty($info['path'])) ? '' : $info['path']; |
| 619 |
$wp_filesystem_dir = $this->get_wp_filesystem_dir($get_dir); |
| 620 |
if ($wp_filesystem_dir === false) return false; |
| 621 |
|
| 622 |
if (empty($this->abspath)) $this->abspath = trailingslashit($wp_filesystem->abspath()); |
| 623 |
|
| 624 |
@set_time_limit(1800); |
| 625 |
|
| 626 |
// This returns the wp_filesystem path |
| 627 |
$working_dir = $this->unpack_package($backup_file, $this->delete, $type); |
| 628 |
|
| 629 |
if (is_wp_error($working_dir)) return $working_dir; |
| 630 |
|
| 631 |
$working_dir_localpath = WP_CONTENT_DIR.'/upgrade/'.basename($working_dir); |
| 632 |
@set_time_limit(1800); |
| 633 |
|
| 634 |
// We copy the variable because we may be importing with a different prefix (e.g. on multisite imports of individual blog data) |
| 635 |
$import_table_prefix = $updraftplus->get_table_prefix(false); |
| 636 |
|
| 637 |
if (is_multisite() && $this->ud_backup_is_multisite === 0 && ( ( 'plugins' == $type || 'themes' == $type ) || ( 'uploads' == $type && !empty($updraftplus_addons_migrator->new_blogid)) )) { |
| 638 |
|
| 639 |
# Migrating a single site into a multisite |
| 640 |
if ('plugins' == $type || 'themes' == $type) { |
| 641 |
|
| 642 |
$move_from = $this->get_first_directory($working_dir, array(basename($info['path']), $type)); |
| 643 |
|
| 644 |
$this->skin->feedback('moving_backup'); |
| 645 |
|
| 646 |
// Only move in entities that are not already there (2) |
| 647 |
$new_move_failed = (false === $move_from) ? true : false; |
| 648 |
if (false === $new_move_failed) { |
| 649 |
$move_in = $this->move_backup_in($move_from, trailingslashit($wp_filesystem_dir), 2, array(), $type, true); |
| 650 |
if (is_wp_error($move_in)) return $move_in; |
| 651 |
if (!$move_in) $new_move_failed = true; |
| 652 |
} |
| 653 |
if ($new_move_failed) return new WP_Error('new_move_failed', $this->strings['new_move_failed']); |
| 654 |
@$wp_filesystem->delete($move_from); |
| 655 |
|
| 656 |
} else { |
| 657 |
// Uploads |
| 658 |
|
| 659 |
$this->skin->feedback('moving_old'); |
| 660 |
|
| 661 |
switch_to_blog($updraftplus_addons_migrator->new_blogid); |
| 662 |
|
| 663 |
$ud = wp_upload_dir(); |
| 664 |
$wpud = $ud['basedir']; |
| 665 |
$fsud = trailingslashit($wp_filesystem->find_folder($wpud)); |
| 666 |
restore_current_blog(); |
| 667 |
|
| 668 |
// 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). |
| 669 |
|
| 670 |
// TODO: the upload dir is not necessarily reachable through wp_filesystem - try ordinary method instead |
| 671 |
if (is_string($fsud)) { |
| 672 |
// This is not expected to exist, since we created a new blog |
| 673 |
|
| 674 |
if ( $wp_filesystem->exists($fsud) && !$wp_filesystem->move($fsud, untrailingslashit($fsud)."-old", true) ) { |
| 675 |
return new WP_Error('old_move_failed', $this->strings['old_move_failed']); |
| 676 |
} |
| 677 |
|
| 678 |
$this->skin->feedback('moving_backup'); |
| 679 |
|
| 680 |
$move_from = $this->get_first_directory($working_dir, array(basename($info['path']), $type)); |
| 681 |
|
| 682 |
if ( !$wp_filesystem->move($move_from, $fsud, true) ) { |
| 683 |
return new WP_Error('new_move_failed', $this->strings['new_move_failed']); |
| 684 |
} |
| 685 |
|
| 686 |
@$wp_filesystem->delete($move_from); |
| 687 |
|
| 688 |
} else { |
| 689 |
return new WP_Error('new_move_failed', $this->strings['new_move_failed']); |
| 690 |
} |
| 691 |
|
| 692 |
} |
| 693 |
} elseif ('db' == $type) { |
| 694 |
|
| 695 |
// $import_table_prefix is received as a reference |
| 696 |
$rdb = $this->restore_backup_db($working_dir, $working_dir_localpath, $import_table_prefix); |
| 697 |
if (false === $rdb || is_wp_error($rdb)) return $rdb; |
| 698 |
|
| 699 |
} elseif ('others' == $type) { |
| 700 |
|
| 701 |
$dirname = basename($info['path']); |
| 702 |
|
| 703 |
# For foreign 'Simple Backup', we need to keep going down until we find wp-content |
| 704 |
if (empty($this->ud_foreign)) { |
| 705 |
$move_from = $working_dir; |
| 706 |
} else { |
| 707 |
$move_from = $this->search_for_folder('wp-content', $working_dir); |
| 708 |
if (!is_string($move_from)) return new WP_Error('not_found', __('The WordPress content folder (wp-content) was not found in this zip file.', 'updraftplus')); |
| 709 |
} |
| 710 |
|
| 711 |
// 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 |
| 712 |
|
| 713 |
# On subsequent archives of a multi-archive set, don't move anything; but do on the first |
| 714 |
$preserve_existing = (isset($this->been_restored['others'])) ? 3 : 1; |
| 715 |
|
| 716 |
$this->move_backup_in($move_from, trailingslashit($wp_filesystem_dir), $preserve_existing, array('plugins', 'themes', 'uploads', 'upgrade'), 'others'); |
| 717 |
|
| 718 |
$this->been_restored['others'] = true; |
| 719 |
|
| 720 |
} else { |
| 721 |
|
| 722 |
// Default action: used for plugins, themes and uploads (and wpcore, via a filter) |
| 723 |
|
| 724 |
// Multi-archive sets: we record what we've already begun on, and on subsequent runs, copy in instead of replacing |
| 725 |
$movedin = apply_filters('updraftplus_restore_movein_'.$type, $working_dir, $this->abspath, $wp_filesystem_dir); |
| 726 |
// A filter, to allow add-ons to perform the install of non-standard entities, or to indicate that it's not possible |
| 727 |
if (false === $movedin) { |
| 728 |
$this->skin->feedback('not_possible'); |
| 729 |
} elseif (is_wp_error($movedin)) { |
| 730 |
return $movedin; |
| 731 |
} elseif (true !== $movedin) { |
| 732 |
|
| 733 |
# On the first time, create the -old directory in updraft_dir |
| 734 |
# (Old style: On the first time, move the existing data to -old) |
| 735 |
if (!isset($this->been_restored[$type])) { |
| 736 |
|
| 737 |
# First, try filesystem-level move |
| 738 |
$old_dir = $updraft_dir.'/'.$type.'-old'; |
| 739 |
if (is_dir($old_dir)) { |
| 740 |
$updraftplus->log_e('%s: This directory already exists, and will be replaced', $old_dir); |
| 741 |
$updraftplus->remove_local_directory($old_dir); |
| 742 |
} |
| 743 |
|
| 744 |
$move_old_destination = 0; |
| 745 |
|
| 746 |
if (@mkdir($old_dir)) { |
| 747 |
$updraftplus->log("Moving old data: filesystem method / updraft_dir is potentially possible"); |
| 748 |
$move_old_destination = 1; |
| 749 |
} |
| 750 |
|
| 751 |
# Try wp_filesystem instead |
| 752 |
if ($wp_filesystem->exists($wp_filesystem_dir."-old")) { |
| 753 |
// Is better to warn and delete the backup than abort mid-restore and leave inconsistent site |
| 754 |
$updraftplus->log_e('%s: This directory already exists, and will be replaced', $wp_filesystem_dir."-old"); |
| 755 |
# In theory, supply true as the 3rd parameter of true achieves this; in practice, not always so (leads to support requests) |
| 756 |
$wp_filesystem->delete($wp_filesystem_dir."-old", true); |
| 757 |
if ($wp_filesystem->exists($wp_filesystem_dir."-old")) { |
| 758 |
$updraftplus->log("Failed to remove existing directory (".$wp_filesystem_dir."-old"); |
| 759 |
$failed_to_remove = true; |
| 760 |
#return new WP_Error('old_move_failed', $this->strings['old_move_failed']); |
| 761 |
} |
| 762 |
} |
| 763 |
|
| 764 |
if (empty($failed_to_remove) && @$wp_filesystem->mkdir($wp_filesystem_dir."-old")) { |
| 765 |
$updraftplus->log("Moving old data: can potentially use wp_filesystem method / -old"); |
| 766 |
$move_old_destination += 2; |
| 767 |
} |
| 768 |
|
| 769 |
if (0 == $move_old_destination) { |
| 770 |
$updraftplus->log_e("File permissions do not allow the old data to be moved and retained; instead, it will be deleted."); |
| 771 |
} |
| 772 |
|
| 773 |
$this->skin->feedback('moving_old'); |
| 774 |
|
| 775 |
# First, try direct filesystem method into updraft_dir |
| 776 |
if (1 == $move_old_destination % 2) { |
| 777 |
# The final 'true' forces direct filesystem access |
| 778 |
$move_old = @$this->move_backup_in($get_dir, $updraft_dir.'/'.$type.'-old/' , 3, array(), $type, false, true); |
| 779 |
if (is_wp_error($move_old)) $updraftplus->log_wp_error($move_old); |
| 780 |
} |
| 781 |
|
| 782 |
# Try wp_filesystem method into -old if that failed |
| 783 |
if (2 >= $move_old_destination && (0 == $move_old_destination % 2 || (!empty($move_old) && is_wp_error($move_old)))) { |
| 784 |
$move_old = @$this->move_backup_in($wp_filesystem_dir, $wp_filesystem_dir."-old/" , 3, array(), $type); |
| 785 |
#if (is_wp_error($move_old)) return $move_old; |
| 786 |
if (is_wp_error($move_old)) $updraftplus->log_wp_error($move_old); |
| 787 |
// if ( !$wp_filesystem->move($wp_filesystem_dir, $wp_filesystem_dir."-old", false) ) { |
| 788 |
// return new WP_Error('old_move_failed', $this->strings['old_move_failed']); |
| 789 |
// } |
| 790 |
} |
| 791 |
|
| 792 |
# Finally, when all else fails, nuke it |
| 793 |
if (0 == $move_old_destination || (!empty($move_old) && is_wp_error($move_old))) { |
| 794 |
$updraftplus->log("$type: $wp_filesystem_dir: deleting contents (as attempts to copy failed)"); |
| 795 |
$del_files = $wp_filesystem->dirlist($wp_filesystem_dir, true, false); |
| 796 |
if (empty($del_files)) $del_files = array(); |
| 797 |
foreach ( $del_files as $file => $filestruc ) { |
| 798 |
if (empty($file)) continue; |
| 799 |
$wp_filesystem->delete($wp_filesystem_dir.'/'.$file, true); |
| 800 |
} |
| 801 |
} |
| 802 |
|
| 803 |
} |
| 804 |
|
| 805 |
# For foreign 'Simple Backup', we need to keep going down until we find wp-content |
| 806 |
if (empty($this->ud_foreign)) { |
| 807 |
$working_dir_use = $working_dir; |
| 808 |
} else { |
| 809 |
$working_dir_use = $this->search_for_folder('wp-content', $working_dir); |
| 810 |
if (!is_string($working_dir_use)) return new WP_Error('not_found', __('The WordPress content folder (wp-content) was not found in this zip file.', 'updraftplus')); |
| 811 |
} |
| 812 |
|
| 813 |
// The backup may not actually have /$type, since that is info from the present site |
| 814 |
$move_from = $this->get_first_directory($working_dir_use, array(basename($info['path']), $type)); |
| 815 |
if (false === $move_from) return new WP_Error('new_move_failed', $this->strings['new_move_failed']); |
| 816 |
|
| 817 |
$this->skin->feedback('moving_backup'); |
| 818 |
|
| 819 |
// Old-style |
| 820 |
// if (!isset($this->been_restored[$type])) { |
| 821 |
// if (!$wp_filesystem->move($move_from, $wp_filesystem_dir, true) ) { |
| 822 |
// return new WP_Error('new_move_failed', $this->strings['new_move_failed']); |
| 823 |
// } |
| 824 |
// } else { |
| 825 |
$move_in = $this->move_backup_in($move_from, trailingslashit($wp_filesystem_dir), 3, array(), $type); |
| 826 |
if (is_wp_error($move_in)) return $move_in; |
| 827 |
if (!$move_in) return new WP_Error('new_move_failed', $this->strings['new_move_failed']); |
| 828 |
$wp_filesystem->rmdir($move_from); |
| 829 |
// } |
| 830 |
|
| 831 |
} |
| 832 |
|
| 833 |
$this->been_restored[$type] = true; |
| 834 |
|
| 835 |
} |
| 836 |
|
| 837 |
$attempt_delete = true; |
| 838 |
if (!empty($this->ud_foreign) && !$last_one) $attempt_delete = false; |
| 839 |
|
| 840 |
// Non-recursive, so the directory needs to be empty |
| 841 |
if ($attempt_delete) $this->skin->feedback('cleaning_up'); |
| 842 |
|
| 843 |
if ($attempt_delete && !$wp_filesystem->delete($working_dir, !empty($this->ud_foreign))) { |
| 844 |
|
| 845 |
# TODO: Can remove this after 1-Jan-2015; or at least, make it so that it requires the version number to be present. |
| 846 |
$fixed_it_now = false; |
| 847 |
# Deal with a corner-case in version 1.8.5 |
| 848 |
if ('uploads' == $type && (empty($this->created_by_version) || (version_compare($this->created_by_version, '1.8.5', '>=') && version_compare($this->created_by_version, '1.8.8', '<')))) { |
| 849 |
$updraftplus->log("Clean-up failed with uploads: will attempt 1.8.5-1.8.7 fix (".$this->created_by_version.")"); |
| 850 |
$move_in = @$this->move_backup_in(dirname($move_from), trailingslashit($wp_filesystem_dir), 3, array(), $type); |
| 851 |
$updraftplus->log("Result: ".serialize($move_in)); |
| 852 |
if ($wp_filesystem->delete($working_dir)) $fixed_it_now = true; |
| 853 |
} |
| 854 |
|
| 855 |
if (!$fixed_it_now) { |
| 856 |
$updraftplus->log_e('Error: %s', $this->strings['delete_failed'].' ('.$working_dir.')'); |
| 857 |
# List contents |
| 858 |
// No need to make this a restoration-aborting error condition - it's not |
| 859 |
#return new WP_Error('delete_failed', $this->strings['delete_failed'].' ('.$working_dir.')'); |
| 860 |
$dirlist = $wp_filesystem->dirlist($working_dir, true, true); |
| 861 |
if (is_array($dirlist)) { |
| 862 |
echo __('Files found:', 'updraftplus').'<br><ul style="list-style: disc inside;">'; |
| 863 |
foreach ($dirlist as $name => $struc) { |
| 864 |
echo "<li>".htmlspecialchars($name)."</li>"; |
| 865 |
} |
| 866 |
echo '</ul>'; |
| 867 |
} else { |
| 868 |
$updraftplus->log_e('Unable to enumerate files in that directory.'); |
| 869 |
} |
| 870 |
} |
| 871 |
} |
| 872 |
|
| 873 |
# Permissions changes (at the top level - i.e. this does not reply if using recursion) are now *additive* - i.e. there's no danger of permissions being removed from what's on-disk |
| 874 |
switch($type) { |
| 875 |
case 'wpcore': |
| 876 |
$this->chmod_if_needed($wp_filesystem_dir, FS_CHMOD_DIR, false, $wp_filesystem); |
| 877 |
// In case we restored a .htaccess which is incorrect for the local setup |
| 878 |
$this->flush_rewrite_rules(); |
| 879 |
break; |
| 880 |
case 'uploads': |
| 881 |
$this->chmod_if_needed($wp_filesystem_dir, FS_CHMOD_DIR, false, $wp_filesystem); |
| 882 |
break; |
| 883 |
case 'db': |
| 884 |
if (function_exists('wp_cache_flush')) wp_cache_flush(); |
| 885 |
do_action('updraftplus_restored_db', array('expected_oldsiteurl' => $this->old_siteurl, 'expected_oldhome' => $this->old_home, 'expected_oldcontent' => $this->old_content), $import_table_prefix); |
| 886 |
$this->flush_rewrite_rules(); |
| 887 |
|
| 888 |
# N.B. flush_rewrite_rules() causes $wp_rewrite to become up to date again |
| 889 |
if (function_exists('apache_get_modules')) { |
| 890 |
global $wp_rewrite; |
| 891 |
$mods = apache_get_modules(); |
| 892 |
if (($wp_rewrite->using_mod_rewrite_permalinks() && in_array('core', $mods) || in_array('http_core', $mods)) && !in_array('mod_rewrite', $mods)) { |
| 893 |
$updraftplus->log("Using Apache, with permalinks (".get_option('permalink_structure').") but no mod_rewrite enabled"); |
| 894 |
$warn_no_rewrite = sprintf(__('You are using the %s webserver, but do not seem to have the %s module loaded.', 'updraftplus'), 'Apache', 'mod_rewrite').' '.sprintf(__('You should enable %s to make your pretty permalinks (e.g. %s) work', 'updraftplus'), 'mod_rewrite', 'http://example.com/my-page/'); |
| 895 |
echo '<p><strong>'.htmlspecialchars($warn_no_rewrite).'</strong></p>'; |
| 896 |
} |
| 897 |
} |
| 898 |
|
| 899 |
break; |
| 900 |
default: |
| 901 |
$this->chmod_if_needed($wp_filesystem_dir, FS_CHMOD_DIR, false, $wp_filesystem); |
| 902 |
} |
| 903 |
# db was already done |
| 904 |
if ('db' != $type) do_action('updraftplus_restored_'.$type); |
| 905 |
|
| 906 |
return true; |
| 907 |
|
| 908 |
} |
| 909 |
|
| 910 |
private function search_for_folder($folder, $startat) { |
| 911 |
# Exists in this folder? |
| 912 |
if (is_dir($startat.'/'.$folder)) return trailingslashit($startat).$folder; |
| 913 |
# Does not |
| 914 |
if($handle = opendir($startat)) { |
| 915 |
while (($file = readdir($handle)) !== false) { |
| 916 |
if ($file != '.' && $file != '..' && is_dir($startat).'/'.$file) { |
| 917 |
$ss = $this->search_for_folder($folder, trailingslashit($startat).$file); |
| 918 |
if (is_string($ss)) return $ss; |
| 919 |
} |
| 920 |
} |
| 921 |
closedir($handle); |
| 922 |
} |
| 923 |
return false; |
| 924 |
} |
| 925 |
|
| 926 |
# Returns an octal string (but not an octal number) |
| 927 |
private function get_current_chmod($file, $wpfs = false) { |
| 928 |
if (false == $wpfs) { |
| 929 |
global $wp_filesystem; |
| 930 |
$wpfs = $wp_filesystem; |
| 931 |
} |
| 932 |
# getchmod() is broken at least as recently as WP3.8 - see: https://core.trac.wordpress.org/ticket/26598 |
| 933 |
return (is_a($wpfs, 'WP_Filesystem_Direct')) ? substr(sprintf("%06d", decoct(@fileperms($file))),3) : $wpfs->getchmod($file); |
| 934 |
} |
| 935 |
|
| 936 |
# Returns a string in octal format |
| 937 |
# $new_chmod should be an octal, i.e. what you'd pass to chmod() |
| 938 |
function calculate_additive_chmod_oct($old_chmod, $new_chmod) { |
| 939 |
# chmod() expects octal form, which means a preceding zero - see http://php.net/chmod |
| 940 |
$old_chmod = sprintf("%04d", $old_chmod); |
| 941 |
$new_chmod = sprintf("%04d", decoct($new_chmod)); |
| 942 |
|
| 943 |
for ($i=1; $i<=3; $i++) { |
| 944 |
$oldbit = substr($old_chmod, $i, 1); |
| 945 |
$newbit = substr($new_chmod, $i, 1); |
| 946 |
for ($j=0; $j<=2; $j++) { |
| 947 |
if (($oldbit & (1<<$j)) && !($newbit & (1<<$j))) { |
| 948 |
$newbit = (string)($newbit | 1<<$j); |
| 949 |
$new_chmod = sprintf("%04d", substr($new_chmod, 0, $i).$newbit.substr($new_chmod, $i+1)); |
| 950 |
} |
| 951 |
} |
| 952 |
} |
| 953 |
|
| 954 |
return $new_chmod; |
| 955 |
} |
| 956 |
|
| 957 |
# "If needed" means, "If the permissions are not already more permissive than this". i.e. This will not tighten permissions from what the user had before (we trust them) |
| 958 |
# $chmod should be an octal - i.e. the same as you'd pass to chmod() |
| 959 |
private function chmod_if_needed($dir, $chmod, $recursive = false, $wpfs = false, $suppress = true) { |
| 960 |
|
| 961 |
# Do nothing on Windows |
| 962 |
if (strtoupper(substr(php_uname('s'), 0, 3)) === 'WIN') return true; |
| 963 |
|
| 964 |
if (false == $wpfs) { |
| 965 |
global $wp_filesystem; |
| 966 |
$wpfs = $wp_filesystem; |
| 967 |
} |
| 968 |
|
| 969 |
$old_chmod = $this->get_current_chmod($dir, $wpfs); |
| 970 |
|
| 971 |
# Sanity fcheck |
| 972 |
if (strlen($old_chmod) < 3) return; |
| 973 |
|
| 974 |
$new_chmod = $this->calculate_additive_chmod_oct($old_chmod, $chmod); |
| 975 |
|
| 976 |
# Don't fix what isn't broken |
| 977 |
if (!$recursive && $new_chmod == $old_chmod) return true; |
| 978 |
|
| 979 |
$new_chmod = octdec($new_chmod); |
| 980 |
|
| 981 |
if ($suppress) { |
| 982 |
return @$wpfs->chmod($dir, $new_chmod, $recursive); |
| 983 |
} else { |
| 984 |
return $wpfs->chmod($dir, $new_chmod, $recursive); |
| 985 |
} |
| 986 |
} |
| 987 |
|
| 988 |
// $dirnames: an array of preferred names |
| 989 |
private function get_first_directory($working_dir, $dirnames) { |
| 990 |
global $wp_filesystem, $updraftplus; |
| 991 |
$fdirnames = array_flip($dirnames); |
| 992 |
$dirlist = $wp_filesystem->dirlist($working_dir, true, false); |
| 993 |
if (is_array($dirlist)) { |
| 994 |
$move_from = false; |
| 995 |
foreach ($dirlist as $name => $struc) { |
| 996 |
if (isset($struc['type']) && 'd' != $struc['type']) continue; |
| 997 |
if (false === $move_from) { |
| 998 |
if (isset($fdirnames[$name])) { |
| 999 |
$move_from = $working_dir . "/".$name; |
| 1000 |
} elseif (preg_match('/^([^\.].*)$/', $name, $fmatch)) { |
| 1001 |
$first_entry = $working_dir."/".$fmatch[1]; |
| 1002 |
} |
| 1003 |
} |
| 1004 |
} |
| 1005 |
if ($move_from === false && isset($first_entry)) { |
| 1006 |
$updraftplus->log_e('Using directory from backup: %s', basename($first_entry)); |
| 1007 |
$move_from = $first_entry; |
| 1008 |
} |
| 1009 |
} else { |
| 1010 |
# That shouldn't happen. Fall back to default |
| 1011 |
$move_from = $working_dir."/".$dirnames[0]; |
| 1012 |
} |
| 1013 |
return $move_from; |
| 1014 |
} |
| 1015 |
|
| 1016 |
private function pre_sql_actions($import_table_prefix) { |
| 1017 |
|
| 1018 |
$import_table_prefix = apply_filters('updraftplus_restore_set_table_prefix', $import_table_prefix, $this->ud_backup_is_multisite); |
| 1019 |
|
| 1020 |
if (!is_string($import_table_prefix)) { |
| 1021 |
if ($import_table_prefix === false) { |
| 1022 |
echo '<p>'.__('Please supply the requested information, and then continue.', 'updraftplus').'</p>'; |
| 1023 |
return false; |
| 1024 |
} else { |
| 1025 |
return new WP_Error('invalid_table_prefix', __('Error:', 'updraftplus').' '.serialize($import_table_prefix)); |
| 1026 |
} |
| 1027 |
} |
| 1028 |
|
| 1029 |
global $updraftplus; |
| 1030 |
echo $updraftplus->log_e('New table prefix: %s', $import_table_prefix); |
| 1031 |
|
| 1032 |
return $import_table_prefix; |
| 1033 |
|
| 1034 |
} |
| 1035 |
|
| 1036 |
public function option_filter_permalink_structure($val) { |
| 1037 |
global $updraftplus; |
| 1038 |
return $updraftplus->option_filter_get('permalink_structure'); |
| 1039 |
} |
| 1040 |
|
| 1041 |
public function option_filter_page_on_front($val) { |
| 1042 |
global $updraftplus; |
| 1043 |
return $updraftplus->option_filter_get('page_on_front'); |
| 1044 |
} |
| 1045 |
|
| 1046 |
public function option_filter_rewrite_rules($val) { |
| 1047 |
global $updraftplus; |
| 1048 |
return $updraftplus->option_filter_get('rewrite_rules'); |
| 1049 |
} |
| 1050 |
|
| 1051 |
// The pass-by-reference on $import_table_prefix is due to historical refactoring |
| 1052 |
private function restore_backup_db($working_dir, $working_dir_localpath, &$import_table_prefix) { |
| 1053 |
|
| 1054 |
do_action('updraftplus_restore_db_pre'); |
| 1055 |
|
| 1056 |
# This is now a legacy option (at least on the front end), so we should not see it much |
| 1057 |
$this->prior_upload_path = get_option('upload_path'); |
| 1058 |
|
| 1059 |
// There is a file backup.db(.gz) inside the working directory |
| 1060 |
|
| 1061 |
# 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 |
| 1062 |
if (@ini_get('safe_mode') && 'off' != strtolower(@ini_get('safe_mode'))) { |
| 1063 |
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/>"; |
| 1064 |
} |
| 1065 |
|
| 1066 |
$db_basename = 'backup.db.gz'; |
| 1067 |
if (!empty($this->ud_foreign)) { |
| 1068 |
|
| 1069 |
$plugins = apply_filters('updraftplus_accept_archivename', array()); |
| 1070 |
|
| 1071 |
if (empty($plugins[$this->ud_foreign])) return new WP_Error('unknown', sprintf(__('Backup created by unknown source (%s) - cannot be restored.', 'updraftplus'), $this->ud_foreign)); |
| 1072 |
|
| 1073 |
if (empty($plugins[$this->ud_foreign]['separatedb'])) { |
| 1074 |
$db_basename = apply_filters('updraftplus_foreign_separatedbname', false, $this->ud_foreign, $this->ud_backup_info, $working_dir_localpath); |
| 1075 |
} elseif (file_exists($working_dir_localpath.'/backup.db')) { |
| 1076 |
$db_basename = 'backup.db'; |
| 1077 |
} |
| 1078 |
} |
| 1079 |
|
| 1080 |
// wp_filesystem has no gzopen method, so we switch to using the local filesystem (which is harmless, since we are performing read-only operations) |
| 1081 |
if (false === $db_basename || !is_readable($working_dir_localpath.'/'.$db_basename)) return new WP_Error('dbopen_failed',__('Failed to find database file','updraftplus')." ($working_dir/".$db_basename.")"); |
| 1082 |
|
| 1083 |
global $wpdb, $updraftplus; |
| 1084 |
|
| 1085 |
$this->skin->feedback('restore_database'); |
| 1086 |
|
| 1087 |
$is_plain = (substr($db_basename, -3, 3) == '.db'); |
| 1088 |
|
| 1089 |
// Read-only access: don't need to go through WP_Filesystem |
| 1090 |
if ($is_plain) { |
| 1091 |
$dbhandle = fopen($working_dir_localpath.'/'.$db_basename, 'r'); |
| 1092 |
} else { |
| 1093 |
$dbhandle = gzopen($working_dir_localpath.'/'.$db_basename, 'r'); |
| 1094 |
} |
| 1095 |
if (!$dbhandle) return new WP_Error('dbopen_failed',__('Failed to open database file','updraftplus')); |
| 1096 |
|
| 1097 |
$this->line = 0; |
| 1098 |
|
| 1099 |
if (true == $this->use_wpdb) { |
| 1100 |
$updraftplus->log_e('Database access: Direct MySQL access is not available, so we are falling back to wpdb (this will be considerably slower)'); |
| 1101 |
} else { |
| 1102 |
$updraftplus->log("Using direct MySQL access; value of use_mysqli is: ".($this->use_mysqli ? '1' : '0')); |
| 1103 |
if ($this->use_mysqli) { |
| 1104 |
@mysqli_query($this->mysql_dbh, 'SET SESSION query_cache_type = OFF;'); |
| 1105 |
} else { |
| 1106 |
@mysql_query('SET SESSION query_cache_type = OFF;', $this->mysql_dbh ); |
| 1107 |
} |
| 1108 |
} |
| 1109 |
|
| 1110 |
// Find the supported engines - in case the dump had something else (case seen: saved from MariaDB with engine Aria; imported into plain MySQL without) |
| 1111 |
$supported_engines = $wpdb->get_results("SHOW ENGINES", OBJECT_K); |
| 1112 |
|
| 1113 |
$this->errors = 0; |
| 1114 |
$this->statements_run = 0; |
| 1115 |
$this->insert_statements_run = 0; |
| 1116 |
$this->tables_created = 0; |
| 1117 |
|
| 1118 |
$sql_line = ""; |
| 1119 |
$sql_type = -1; |
| 1120 |
|
| 1121 |
$this->start_time = microtime(true); |
| 1122 |
|
| 1123 |
$old_wpversion = ''; |
| 1124 |
$this->old_siteurl = ''; |
| 1125 |
$this->old_home = ''; |
| 1126 |
$this->old_content = ''; |
| 1127 |
$old_table_prefix = ''; |
| 1128 |
$old_siteinfo = array(); |
| 1129 |
$gathering_siteinfo = true; |
| 1130 |
|
| 1131 |
$this->create_forbidden = false; |
| 1132 |
$this->drop_forbidden = false; |
| 1133 |
|
| 1134 |
$this->last_error = ''; |
| 1135 |
$random_table_name = 'updraft_tmp_'.rand(0,9999999).md5(microtime(true)); |
| 1136 |
|
| 1137 |
# The only purpose in funnelling queries directly here is to be able to get the error number |
| 1138 |
if ($this->use_wpdb) { |
| 1139 |
$req = $wpdb->query("CREATE TABLE $random_table_name"); |
| 1140 |
if (!$req) $this->last_error = $wpdb->last_error; |
| 1141 |
$this->last_error_no = false; |
| 1142 |
} else { |
| 1143 |
if ($this->use_mysqli) { |
| 1144 |
$req = mysqli_query($this->mysql_dbh, "CREATE TABLE $random_table_name"); |
| 1145 |
} else { |
| 1146 |
$req = mysql_unbuffered_query("CREATE TABLE $random_table_name", $this->mysql_dbh); |
| 1147 |
} |
| 1148 |
if (!$req) { |
| 1149 |
$this->last_error = ($this->use_mysqli) ? mysqli_error($this->mysql_dbh) : mysql_error($this->mysql_dbh); |
| 1150 |
$this->last_error_no = ($this->use_mysqli) ? mysqli_errno($this->mysql_dbh) : mysql_errno($this->mysql_dbh); |
| 1151 |
} |
| 1152 |
} |
| 1153 |
|
| 1154 |
if (!$req && ($this->use_wpdb || 1142 === $this->last_error_no)) { |
| 1155 |
$this->create_forbidden = true; |
| 1156 |
# If we can't create, then there's no point dropping |
| 1157 |
$this->drop_forbidden = true; |
| 1158 |
echo '<strong>'.__('Warning:', 'updraftplus').'</strong> '; |
| 1159 |
$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.')'); |
| 1160 |
} else { |
| 1161 |
if ($this->use_wpdb) { |
| 1162 |
$req = $wpdb->query("DROP TABLE $random_table_name"); |
| 1163 |
if (!$req) $this->last_error = $wpdb->last_error; |
| 1164 |
$this->last_error_no = false; |
| 1165 |
} else { |
| 1166 |
if ($this->use_mysqli) { |
| 1167 |
$req = mysqli_query($this->mysql_dbh, "DROP TABLE $random_table_name"); |
| 1168 |
} else { |
| 1169 |
$req = mysql_unbuffered_query("DROP TABLE $random_table_name", $this->mysql_dbh); |
| 1170 |
} |
| 1171 |
if (!$req) { |
| 1172 |
$this->last_error = ($this->use_mysqli) ? mysqli_error($this->mysql_dbh) : mysql_error($this->mysql_dbh); |
| 1173 |
$this->last_error_no = ($this->use_mysqli) ? mysqli_errno($this->mysql_dbh) : mysql_errno($this->mysql_dbh); |
| 1174 |
} |
| 1175 |
} |
| 1176 |
if (!$req && ($this->use_wpdb || $this->last_error_no === 1142)) { |
| 1177 |
$this->drop_forbidden = true; |
| 1178 |
echo '<strong>'.__('Warning:','updraftplus').'</strong> '; |
| 1179 |
$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.')'); |
| 1180 |
} |
| 1181 |
} |
| 1182 |
|
| 1183 |
$restoring_table = ''; |
| 1184 |
|
| 1185 |
$this->max_allowed_packet = $updraftplus->get_max_packet_size(); |
| 1186 |
|
| 1187 |
while (($is_plain && !feof($dbhandle)) || (!$is_plain && !gzeof($dbhandle))) { |
| 1188 |
// Up to 1Mb |
| 1189 |
$buffer = ($is_plain) ? rtrim(fgets($dbhandle, 1048576)) : rtrim(gzgets($dbhandle, 1048576)); |
| 1190 |
// Discard comments |
| 1191 |
if (empty($buffer) || substr($buffer, 0, 1) == '#' || preg_match('/^--(\s|$)/', substr($buffer, 0, 3))) { |
| 1192 |
if ('' == $this->old_siteurl && preg_match('/^\# Backup of: (http(.*))$/', $buffer, $matches)) { |
| 1193 |
$this->old_siteurl = untrailingslashit($matches[1]); |
| 1194 |
$updraftplus->log_e('<strong>Backup of:</strong> %s', htmlspecialchars($this->old_siteurl)); |
| 1195 |
do_action('updraftplus_restore_db_record_old_siteurl', $this->old_siteurl); |
| 1196 |
} elseif (false === $this->created_by_version && preg_match('/^\# Created by UpdraftPlus version ([\d\.]+)/', $buffer, $matches)) { |
| 1197 |
$this->created_by_version = trim($matches[1]); |
| 1198 |
echo '<strong>'.__('Backup created by:', 'updraftplus').'</strong> '.htmlspecialchars($this->created_by_version).'<br>'; |
| 1199 |
$updraftplus->log('Backup created by: '.$this->created_by_version); |
| 1200 |
} elseif ('' == $this->old_home && preg_match('/^\# Home URL: (http(.*))$/', $buffer, $matches)) { |
| 1201 |
$this->old_home = untrailingslashit($matches[1]); |
| 1202 |
if ($this->old_siteurl && $this->old_home != $this->old_siteurl) { |
| 1203 |
echo '<strong>'.__('Site home:', 'updraftplus').'</strong> '.htmlspecialchars($this->old_home).'<br>'; |
| 1204 |
$updraftplus->log('Site home: '.$this->old_home); |
| 1205 |
} |
| 1206 |
do_action('updraftplus_restore_db_record_old_home', $this->old_home); |
| 1207 |
} elseif ('' == $this->old_content && preg_match('/^\# Content URL: (http(.*))$/', $buffer, $matches)) { |
| 1208 |
$this->old_content = untrailingslashit($matches[1]); |
| 1209 |
echo '<strong>'.__('Content URL:', 'updraftplus').'</strong> '.htmlspecialchars($this->old_content).'<br>'; |
| 1210 |
$updraftplus->log('Content URL: '.$this->old_content); |
| 1211 |
do_action('updraftplus_restore_db_record_old_content', $this->old_content); |
| 1212 |
} elseif ('' == $old_table_prefix && (preg_match('/^\# Table prefix: (\S+)$/', $buffer, $matches) || preg_match('/^-- Table Prefix: (\S+)$/i', $buffer, $matches))) { |
| 1213 |
# We also support backwpup style: |
| 1214 |
# -- Table Prefix: wp_ |
| 1215 |
$old_table_prefix = $matches[1]; |
| 1216 |
echo '<strong>'.__('Old table prefix:', 'updraftplus').'</strong> '.htmlspecialchars($old_table_prefix).'<br>'; |
| 1217 |
$updraftplus->log("Old table prefix: ".$old_table_prefix); |
| 1218 |
} elseif ($gathering_siteinfo && preg_match('/^\# Site info: (\S+)$/', $buffer, $matches)) { |
| 1219 |
if ('end' == $matches[1]) { |
| 1220 |
$gathering_siteinfo = false; |
| 1221 |
// Sanity checks |
| 1222 |
if (isset($old_siteinfo['multisite']) && !$old_siteinfo['multisite'] && is_multisite()) { |
| 1223 |
// Just need to check that you're crazy |
| 1224 |
if (!defined('UPDRAFTPLUS_EXPERIMENTAL_IMPORTINTOMULTISITE') || UPDRAFTPLUS_EXPERIMENTAL_IMPORTINTOMULTISITE != true) { |
| 1225 |
return new WP_Error('multisite_error', $this->strings['multisite_error']); |
| 1226 |
} |
| 1227 |
// Got the needed code? |
| 1228 |
if (!class_exists('UpdraftPlusAddOn_MultiSite') || !class_exists('UpdraftPlus_Addons_Migrator')) { |
| 1229 |
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')); |
| 1230 |
} |
| 1231 |
} |
| 1232 |
} elseif (preg_match('/^([^=]+)=(.*)$/', $matches[1], $kvmatches)) { |
| 1233 |
$key = $kvmatches[1]; |
| 1234 |
$val = $kvmatches[2]; |
| 1235 |
echo '<strong>'.__('Site information:','updraftplus').'</strong>'.' '.htmlspecialchars($key).' = '.htmlspecialchars($val).'<br>'; |
| 1236 |
$updraftplus->log("Site information: ".$key."=".$val); |
| 1237 |
$old_siteinfo[$key]=$val; |
| 1238 |
if ('multisite' == $key) { |
| 1239 |
if ($val) { $this->ud_backup_is_multisite=1; } else { $this->ud_backup_is_multisite = 0;} |
| 1240 |
} |
| 1241 |
} |
| 1242 |
} |
| 1243 |
continue; |
| 1244 |
} |
| 1245 |
|
| 1246 |
// Detect INSERT commands early, so that we can split them if necessary |
| 1247 |
if ($sql_line && preg_match('/^\s*(insert into \`?([^\`]*)\`?\s+(values|\())/i', $sql_line, $matches)) { |
| 1248 |
$sql_type = 3; |
| 1249 |
$insert_prefix = $matches[1]; |
| 1250 |
} |
| 1251 |
|
| 1252 |
# 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) |
| 1253 |
# ALlow a 100-byte margin for error (including searching/replacing table prefix) |
| 1254 |
if (3 == $sql_type && $sql_line && strlen($sql_line.$buffer) > ($this->max_allowed_packet - 100) && preg_match('/,\s*$/', $sql_line) && preg_match('/^\s*\(/', $buffer)) { |
| 1255 |
// Remove the final comma; replace with semi-colon |
| 1256 |
$sql_line = substr(rtrim($sql_line), 0, strlen($sql_line)-1).';'; |
| 1257 |
if ('' != $old_table_prefix && $import_table_prefix != $old_table_prefix) $sql_line = $updraftplus->str_replace_once($old_table_prefix, $import_table_prefix, $sql_line); |
| 1258 |
# Run the SQL command; then set up for the next one. |
| 1259 |
$this->line++; |
| 1260 |
echo __("Split line to avoid exceeding maximum packet size", 'updraftplus')." (".strlen($sql_line)." + ".strlen($buffer)." : ".$this->max_allowed_packet.")<br>"; |
| 1261 |
$updraftplus->log("Split line to avoid exceeding maximum packet size (".strlen($sql_line)." + ".strlen($buffer)." : ".$this->max_allowed_packet.")"); |
| 1262 |
$do_exec = $this->sql_exec($sql_line, $sql_type, $import_table_prefix); |
| 1263 |
if (is_wp_error($do_exec)) return $do_exec; |
| 1264 |
# Reset, then carry on |
| 1265 |
$sql_line = $insert_prefix." "; |
| 1266 |
} |
| 1267 |
|
| 1268 |
$sql_line .= $buffer; |
| 1269 |
# Do we have a complete line yet? We used to just test the final character for ';' here (up to 1.8.12), but that was too unsophisticated |
| 1270 |
if ( |
| 1271 |
(3 == $sql_type && !preg_match('/\)\s*;$/', substr($sql_line, -3, 3))) |
| 1272 |
|| (3 != $sql_type && ';' != substr($sql_line, -1, 1)) |
| 1273 |
) continue; |
| 1274 |
|
| 1275 |
$this->line++; |
| 1276 |
|
| 1277 |
# We now have a complete line - process it |
| 1278 |
|
| 1279 |
if (3 == $sql_type && $sql_line && strlen($sql_line) > $this->max_allowed_packet) { |
| 1280 |
$this->log_oversized_packet($sql_line); |
| 1281 |
# Reset |
| 1282 |
$sql_line = ''; |
| 1283 |
$sql_type = -1; |
| 1284 |
# If this is the very first SQL line of the options table, we need to bail; it's essential |
| 1285 |
if (0 == $this->insert_statements_run && $restoring_table && $restoring_table == $import_table_prefix.'options') { |
| 1286 |
return new WP_Error('initial_db_error', sprintf(__('An error occurred on the first %s command - aborting run','updraftplus'), 'INSERT (options)')); |
| 1287 |
} |
| 1288 |
continue; |
| 1289 |
} |
| 1290 |
|
| 1291 |
# The timed overhead of this is negligible |
| 1292 |
if (preg_match('/^\s*drop table if exists \`?([^\`]*)\`?\s*;/i', $sql_line, $matches)) { |
| 1293 |
|
| 1294 |
$sql_type = 1; |
| 1295 |
|
| 1296 |
if (!isset($printed_new_table_prefix)) { |
| 1297 |
$import_table_prefix = $this->pre_sql_actions($import_table_prefix); |
| 1298 |
if (false===$import_table_prefix || is_wp_error($import_table_prefix)) return $import_table_prefix; |
| 1299 |
$printed_new_table_prefix = true; |
| 1300 |
} |
| 1301 |
|
| 1302 |
$this->table_name = $matches[1]; |
| 1303 |
|
| 1304 |
// Legacy, less reliable - in case it was not caught before |
| 1305 |
if ('' == $old_table_prefix && preg_match('/^([a-z0-9]+)_.*$/i', $this->table_name, $tmatches)) { |
| 1306 |
$old_table_prefix = $tmatches[1].'_'; |
| 1307 |
echo '<strong>'.__('Old table prefix:', 'updraftplus').'</strong> '.htmlspecialchars($old_table_prefix).'<br>'; |
| 1308 |
$updraftplus->log("Old table prefix: $old_table_prefix"); |
| 1309 |
} |
| 1310 |
|
| 1311 |
$this->new_table_name = ($old_table_prefix) ? $updraftplus->str_replace_once($old_table_prefix, $import_table_prefix, $this->table_name) : $this->table_name; |
| 1312 |
|
| 1313 |
if ('' != $old_table_prefix && $import_table_prefix != $old_table_prefix) { |
| 1314 |
$sql_line = $updraftplus->str_replace_once($old_table_prefix, $import_table_prefix, $sql_line); |
| 1315 |
} |
| 1316 |
$this->tables_been_dropped[] = $this->new_table_name; |
| 1317 |
|
| 1318 |
} elseif (preg_match('/^\s*create table \`?([^\`\(]*)\`?\s*\(/i', $sql_line, $matches)) { |
| 1319 |
|
| 1320 |
$sql_type = 2; |
| 1321 |
$this->insert_statements_run = 0; |
| 1322 |
$this->table_name = $matches[1]; |
| 1323 |
|
| 1324 |
// Legacy, less reliable - in case it was not caught before. We added it in here (CREATE) as well as in DROP because of SQL dumps which lack DROP statements. |
| 1325 |
if ('' == $old_table_prefix && preg_match('/^([a-z0-9]+)_.*$/i', $this->table_name, $tmatches)) { |
| 1326 |
$old_table_prefix = $tmatches[1].'_'; |
| 1327 |
echo '<strong>'.__('Old table prefix:', 'updraftplus').'</strong> '.htmlspecialchars($old_table_prefix).'<br>'; |
| 1328 |
$updraftplus->log("Old table prefix: $old_table_prefix"); |
| 1329 |
} |
| 1330 |
|
| 1331 |
// MySQL 4.1 outputs TYPE=, but accepts ENGINE=; 5.1 onwards accept *only* ENGINE= |
| 1332 |
$sql_line = $updraftplus->str_lreplace('TYPE=', 'ENGINE=', $sql_line); |
| 1333 |
|
| 1334 |
if (empty($printed_new_table_prefix)) { |
| 1335 |
$import_table_prefix = $this->pre_sql_actions($import_table_prefix); |
| 1336 |
if (false === $import_table_prefix || is_wp_error($import_table_prefix)) return $import_table_prefix; |
| 1337 |
$printed_new_table_prefix = true; |
| 1338 |
} |
| 1339 |
|
| 1340 |
$this->new_table_name = ($old_table_prefix) ? $updraftplus->str_replace_once($old_table_prefix, $import_table_prefix, $this->table_name) : $this->table_name; |
| 1341 |
|
| 1342 |
// 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) |
| 1343 |
if ($restoring_table) { |
| 1344 |
|
| 1345 |
# Attempt to reconnect if the DB connection dropped (may not succeed, of course - but that will soon become evident) |
| 1346 |
$updraftplus->check_db_connection($this->wpdb_obj); |
| 1347 |
|
| 1348 |
// After restoring the options table, we can set old_siteurl if on legacy (i.e. not already set) |
| 1349 |
if ($restoring_table == $import_table_prefix.'options') { |
| 1350 |
if ('' == $this->old_siteurl || '' == $this->old_home || '' == $this->old_content) { |
| 1351 |
global $updraftplus_addons_migrator; |
| 1352 |
if (isset($updraftplus_addons_migrator->new_blogid)) switch_to_blog($updraftplus_addons_migrator->new_blogid); |
| 1353 |
|
| 1354 |
if ('' == $this->old_siteurl) { |
| 1355 |
$this->old_siteurl = untrailingslashit($wpdb->get_row("SELECT option_value FROM $wpdb->options WHERE option_name='siteurl'")->option_value); |
| 1356 |
do_action('updraftplus_restore_db_record_old_siteurl', $this->old_siteurl); |
| 1357 |
} |
| 1358 |
if ('' == $this->old_home) { |
| 1359 |
$this->old_home = untrailingslashit($wpdb->get_row("SELECT option_value FROM $wpdb->options WHERE option_name='home'")->option_value); |
| 1360 |
do_action('updraftplus_restore_db_record_old_home', $this->old_home); |
| 1361 |
} |
| 1362 |
if ('' == $this->old_content) { |
| 1363 |
$this->old_content = $this->old_siteurl.'/wp-content'; |
| 1364 |
do_action('updraftplus_restore_db_record_old_content', $this->old_content); |
| 1365 |
} |
| 1366 |
if (isset($updraftplus_addons_migrator->new_blogid)) restore_current_blog(); |
| 1367 |
} |
| 1368 |
} |
| 1369 |
|
| 1370 |
$this->restored_table($restoring_table, $import_table_prefix, $old_table_prefix); |
| 1371 |
|
| 1372 |
} |
| 1373 |
|
| 1374 |
$engine = "(?)"; $engine_change_message = ''; |
| 1375 |
if (preg_match('/ENGINE=([^\s;]+)/', $sql_line, $eng_match)) { |
| 1376 |
$engine = $eng_match[1]; |
| 1377 |
if (isset($supported_engines[$engine])) { |
| 1378 |
#echo sprintf(__('Requested table engine (%s) is present.', 'updraftplus'), $engine); |
| 1379 |
if ('myisam' == strtolower($engine)) { |
| 1380 |
$sql_line = preg_replace('/PAGE_CHECKSUM=\d\s?/', '', $sql_line, 1); |
| 1381 |
} |
| 1382 |
} else { |
| 1383 |
$engine_change_message = sprintf(__('Requested table engine (%s) is not present - changing to MyISAM.', 'updraftplus'), $engine)."<br>"; |
| 1384 |
$sql_line = $updraftplus->str_lreplace("ENGINE=$eng_match", "ENGINE=MyISAM", $sql_line); |
| 1385 |
// Remove (M)aria options |
| 1386 |
if ('maria' == strtolower($engine) || 'aria' == strtolower($engine)) { |
| 1387 |
$sql_line = preg_replace('/PAGE_CHECKSUM=\d\s?/', '', $sql_line, 1); |
| 1388 |
$sql_line = preg_replace('/TRANSACTIONAL=\d\s?/', '', $sql_line, 1); |
| 1389 |
} |
| 1390 |
} |
| 1391 |
} |
| 1392 |
|
| 1393 |
$this->table_name = $matches[1]; |
| 1394 |
echo '<strong>'.sprintf(__('Restoring table (%s)','updraftplus'), $engine).":</strong> ".htmlspecialchars($this->table_name); |
| 1395 |
$logline = "Restoring table ($engine): ".$this->table_name; |
| 1396 |
if ('' != $old_table_prefix && $import_table_prefix != $old_table_prefix) { |
| 1397 |
echo ' - '.__('will restore as:', 'updraftplus').' '.htmlspecialchars($this->new_table_name); |
| 1398 |
$logline .= " - will restore as: ".$this->new_table_name; |
| 1399 |
$sql_line = $updraftplus->str_replace_once($old_table_prefix, $import_table_prefix, $sql_line); |
| 1400 |
} |
| 1401 |
$updraftplus->log($logline); |
| 1402 |
$restoring_table = $this->new_table_name; |
| 1403 |
echo '<br>'; |
| 1404 |
if ($engine_change_message) echo $engine_change_message; |
| 1405 |
|
| 1406 |
} elseif (preg_match('/^\s*(insert into \`?([^\`]*)\`?\s+(values|\())/i', $sql_line, $matches)) { |
| 1407 |
$sql_type = 3; |
| 1408 |
if ('' != $old_table_prefix && $import_table_prefix != $old_table_prefix) $sql_line = $updraftplus->str_replace_once($old_table_prefix, $import_table_prefix, $sql_line); |
| 1409 |
} elseif (preg_match('/^\s*(\/\*\!40000 )?(alter|lock) tables? \`?([^\`\(]*)\`?\s+(write|disable|enable)/i', $sql_line, $matches)) { |
| 1410 |
# Only binary mysqldump produces this pattern (LOCK TABLES `table` WRITE, ALTER TABLE `table` (DISABLE|ENABLE) KEYS) |
| 1411 |
$sql_type = 4; |
| 1412 |
if ('' != $old_table_prefix && $import_table_prefix != $old_table_prefix) $sql_line = $updraftplus->str_replace_once($old_table_prefix, $import_table_prefix, $sql_line); |
| 1413 |
} elseif (preg_match('/^(un)?lock tables/i', $sql_line)) { |
| 1414 |
# BackWPup produces these |
| 1415 |
$sql_type = 5; |
| 1416 |
} elseif (preg_match('/^(create|drop) database /i', $sql_line)) { |
| 1417 |
# WPB2D produces these, as do some phpMyAdmin dumps |
| 1418 |
$sql_type = 6; |
| 1419 |
} elseif (preg_match('/^use /i', $sql_line)) { |
| 1420 |
# WPB2D produces these, as do some phpMyAdmin dumps |
| 1421 |
$sql_type = 7; |
| 1422 |
} |
| 1423 |
// if (5 !== $sql_type) { |
| 1424 |
if ($sql_type < 6) { |
| 1425 |
$do_exec = $this->sql_exec($sql_line, $sql_type); |
| 1426 |
if (is_wp_error($do_exec)) return $do_exec; |
| 1427 |
} else { |
| 1428 |
$updraftplus->log("Skipped SQL statement (unwanted type=$sql_type): $sql_line"); |
| 1429 |
} |
| 1430 |
|
| 1431 |
# Reset |
| 1432 |
$sql_line = ''; |
| 1433 |
$sql_type = -1; |
| 1434 |
|
| 1435 |
} |
| 1436 |
|
| 1437 |
if ($restoring_table) $this->restored_table($restoring_table, $import_table_prefix, $old_table_prefix); |
| 1438 |
|
| 1439 |
$time_taken = microtime(true) - $this->start_time; |
| 1440 |
$updraftplus->log_e('Finished: lines processed: %d in %.2f seconds', $this->line, $time_taken); |
| 1441 |
if ($is_plain) { |
| 1442 |
fclose($dbhandle); |
| 1443 |
} else { |
| 1444 |
gzclose($dbhandle); |
| 1445 |
} |
| 1446 |
|
| 1447 |
global $wp_filesystem; |
| 1448 |
|
| 1449 |
$wp_filesystem->delete($working_dir.'/'.$db_basename, false, 'f'); |
| 1450 |
return true; |
| 1451 |
|
| 1452 |
} |
| 1453 |
|
| 1454 |
private function log_oversized_packet($sql_line) { |
| 1455 |
global $updraftplus; |
| 1456 |
$logit = substr($sql_line, 0, 100); |
| 1457 |
$updraftplus->log(sprintf("An SQL line that is larger than the maximum packet size and cannot be split was found: %s", '('.strlen($sql_line).', '.$logit.' ...)')); |
| 1458 |
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).', '.$this->max_allowed_packet.', '.$logit.' ...)')."<br>"; |
| 1459 |
} |
| 1460 |
|
| 1461 |
# UPDATE is sql_type=5 (not used in the function, but used in Migrator and so noted here for reference) |
| 1462 |
# $import_table_prefix is only use in one place in this function (long INSERTs), and otherwise need/should not be supplied |
| 1463 |
public function sql_exec($sql_line, $sql_type, $import_table_prefix = '') { |
| 1464 |
|
| 1465 |
global $wpdb, $updraftplus; |
| 1466 |
$ignore_errors = false; |
| 1467 |
# Type 2 = CREATE TABLE |
| 1468 |
if (2 == $sql_type && $this->create_forbidden) { |
| 1469 |
$updraftplus->log_e('Cannot create new tables, so skipping this command (%s)', htmlspecialchars($sql_line)); |
| 1470 |
$req = true; |
| 1471 |
} else { |
| 1472 |
|
| 1473 |
if (2 == $sql_type && !$this->drop_forbidden) { |
| 1474 |
# We choose, for now, to be very conservative - we only do the apparently-missing drop if we have never seen any drop - i.e. assume that in SQL dumps with missing DROPs, that it's because there are no DROPs at all |
| 1475 |
if (!in_array($this->new_table_name, $this->tables_been_dropped)) { |
| 1476 |
$updraftplus->log_e('Table to be implicitly dropped: %s', $this->new_table_name); |
| 1477 |
# TODO: Actually drop |
| 1478 |
$this->sql_exec('DROP TABLE IF EXISTS '.esc_sql($this->new_table_name), 1); |
| 1479 |
$this->tables_been_dropped[] = $this->new_table_name; |
| 1480 |
} |
| 1481 |
} |
| 1482 |
|
| 1483 |
# Type 1 = DROP TABLE |
| 1484 |
if (1 == $sql_type && $this->drop_forbidden) { |
| 1485 |
$sql_line = "DELETE FROM ".$updraftplus->backquote($this->new_table_name); |
| 1486 |
$updraftplus->log_e('Cannot drop tables, so deleting instead (%s)', $sql_line); |
| 1487 |
$ignore_errors = true; |
| 1488 |
} |
| 1489 |
|
| 1490 |
if (3 == $sql_type && $sql_line && strlen($sql_line) > $this->max_allowed_packet) { |
| 1491 |
$this->log_oversized_packet($sql_line); |
| 1492 |
# If this is the very first SQL line of the options table, we need to bail; it's essential |
| 1493 |
$this->errors++; |
| 1494 |
if (0 == $this->insert_statements_run && $this->new_table_name && $this->new_table_name == $import_table_prefix.'options') { |
| 1495 |
return new WP_Error('initial_db_error', sprintf(__('An error occurred on the first %s command - aborting run','updraftplus'), 'INSERT (options)')); |
| 1496 |
} |
| 1497 |
return false; |
| 1498 |
} |
| 1499 |
|
| 1500 |
if ($this->use_wpdb) { |
| 1501 |
$req = $wpdb->query($sql_line); |
| 1502 |
if (!$req) $this->last_error = $wpdb->last_error; |
| 1503 |
} else { |
| 1504 |
if ($this->use_mysqli) { |
| 1505 |
$req = mysqli_query($this->mysql_dbh, $sql_line); |
| 1506 |
if (!$req) $this->last_error = mysqli_error($this->mysql_dbh); |
| 1507 |
} else { |
| 1508 |
$req = mysql_unbuffered_query($sql_line, $this->mysql_dbh); |
| 1509 |
if (!$req) $this->last_error = mysql_error($this->mysql_dbh); |
| 1510 |
} |
| 1511 |
} |
| 1512 |
if (3 == $sql_type) $this->insert_statements_run++; |
| 1513 |
if (1 == $sql_type) $this->tables_been_dropped[] = $this->new_table_name; |
| 1514 |
$this->statements_run++; |
| 1515 |
} |
| 1516 |
|
| 1517 |
if (!$req) { |
| 1518 |
if (!$ignore_errors) $this->errors++; |
| 1519 |
$print_err = (strlen($sql_line) > 100) ? substr($sql_line, 0, 100).' ...' : $sql_line; |
| 1520 |
echo sprintf(_x('An error (%s) occurred:', 'The user is being told the number of times an error has happened, e.g. An error (27) occurred', 'updraftplus'), $this->errors)." - ".htmlspecialchars($this->last_error)." - ".__('the database query being run was:','updraftplus').' '.htmlspecialchars($print_err).'<br>'; |
| 1521 |
$updraftplus->log("An error (".$this->errors.") occurred: ".$this->last_error." - SQL query was: ".substr($sql_line, 0, 65536)); |
| 1522 |
// First command is expected to be DROP TABLE |
| 1523 |
if (1 == $this->errors && 2 == $sql_type && 0 == $this->tables_created) { |
| 1524 |
return new WP_Error('initial_db_error', sprintf(__('An error occurred on the first %s command - aborting run','updraftplus'), 'CREATE TABLE')); |
| 1525 |
} |
| 1526 |
if ($this->errors>49) { |
| 1527 |
return new WP_Error('too_many_db_errors', __('Too many database errors have occurred - aborting','updraftplus')); |
| 1528 |
} |
| 1529 |
} elseif ($sql_type == 2) { |
| 1530 |
$this->tables_created++; |
| 1531 |
} |
| 1532 |
|
| 1533 |
if ($this->line >0 && ($this->line)%50 == 0) { |
| 1534 |
if ($this->line > $this->line_last_logged && (($this->line)%250 == 0 || $this->line<250)) { |
| 1535 |
$this->line_last_logged = $this->line; |
| 1536 |
$time_taken = microtime(true) - $this->start_time; |
| 1537 |
$updraftplus->log_e('Database queries processed: %d in %.2f seconds',$this->line, $time_taken); |
| 1538 |
} |
| 1539 |
} |
| 1540 |
return $req; |
| 1541 |
} |
| 1542 |
|
| 1543 |
// function option_filter($which) { |
| 1544 |
// if (strpos($which, 'pre_option') !== false) { echo "OPT_FILT: $which<br>\n"; } |
| 1545 |
// return false; |
| 1546 |
// } |
| 1547 |
|
| 1548 |
private function flush_rewrite_rules() { |
| 1549 |
|
| 1550 |
// We have to deal with the fact that the procedures used call get_option, which could be looking at the wrong table prefix, or have the wrong thing cached |
| 1551 |
|
| 1552 |
global $updraftplus_addons_migrator; |
| 1553 |
if (!empty($updraftplus_addons_migrator->new_blogid)) switch_to_blog($updraftplus_addons_migrator->new_blogid); |
| 1554 |
|
| 1555 |
foreach (array('permalink_structure', 'rewrite_rules', 'page_on_front') as $opt) { |
| 1556 |
add_filter('pre_option_'.$opt, array($this, 'option_filter_'.$opt)); |
| 1557 |
} |
| 1558 |
|
| 1559 |
global $wp_rewrite; |
| 1560 |
$wp_rewrite->init(); |
| 1561 |
// Don't do this: it will cause rules created by plugins that weren't active at the start of the restore run to be lost |
| 1562 |
# flush_rewrite_rules(true); |
| 1563 |
|
| 1564 |
if ( function_exists( 'save_mod_rewrite_rules' ) ) |
| 1565 |
save_mod_rewrite_rules(); |
| 1566 |
if ( function_exists( 'iis7_save_url_rewrite_rules' ) ) |
| 1567 |
iis7_save_url_rewrite_rules(); |
| 1568 |
|
| 1569 |
foreach (array('permalink_structure', 'rewrite_rules', 'page_on_front') as $opt) { |
| 1570 |
remove_filter('pre_option_'.$opt, array($this, 'option_filter_'.$opt)); |
| 1571 |
} |
| 1572 |
|
| 1573 |
if (!empty($updraftplus_addons_migrator->new_blogid)) restore_current_blog(); |
| 1574 |
|
| 1575 |
} |
| 1576 |
|
| 1577 |
private function restored_table($table, $import_table_prefix, $old_table_prefix) { |
| 1578 |
|
| 1579 |
global $wpdb, $updraftplus; |
| 1580 |
|
| 1581 |
// WordPress has an option name predicated upon the table prefix. Yuk. |
| 1582 |
// if ($table == $import_table_prefix.'options') { |
| 1583 |
if (preg_match('/^([\d+]_)?options$/', substr($table, strlen($import_table_prefix)), $matches)) { |
| 1584 |
if (($this->is_multisite && !empty($matches[1])) || !$this->is_multisite && $table == $import_table_prefix.'options') { |
| 1585 |
|
| 1586 |
$mprefix = (empty($matches[1])) ? '' : $matches[1]; |
| 1587 |
|
| 1588 |
if ($import_table_prefix != $old_table_prefix) { |
| 1589 |
$updraftplus->log("Table prefix has changed: changing options table field(s) accordingly (".$mprefix."options)"); |
| 1590 |
echo sprintf(__('Table prefix has changed: changing %s table field(s) accordingly:', 'updraftplus'),'option').' '; |
| 1591 |
if (false === $wpdb->query("UPDATE ${import_table_prefix}".$mprefix."options SET option_name='${import_table_prefix}".$mprefix."user_roles' WHERE option_name='${old_table_prefix}".$mprefix."user_roles' LIMIT 1")) { |
| 1592 |
echo __('Error','updraftplus'); |
| 1593 |
$updraftplus->log("Error when changing options table fields"); |
| 1594 |
} else { |
| 1595 |
$updraftplus->log("Options table fields changed OK"); |
| 1596 |
echo __('OK', 'updraftplus'); |
| 1597 |
} |
| 1598 |
echo '<br>'; |
| 1599 |
} |
| 1600 |
|
| 1601 |
// Now deal with the situation where the imported database sets a new over-ride upload_path that is absolute - which may not be wanted |
| 1602 |
$new_upload_path = $wpdb->get_row($wpdb->prepare("SELECT option_value FROM ${import_table_prefix}".$mprefix."options WHERE option_name = %s LIMIT 1", 'upload_path')); |
| 1603 |
$new_upload_path = (is_object($new_upload_path)) ? $new_upload_path->option_value : ''; |
| 1604 |
// The danger situation is absolute and points somewhere that is now perhaps not accessible at all |
| 1605 |
if (!empty($new_upload_path) && $new_upload_path != $this->prior_upload_path && (strpos($new_upload_path, '/') === 0) || preg_match('#^[A-Za-z]:[/\\\]#', $new_upload_path)) { |
| 1606 |
if (!file_exists($new_upload_path)) { |
| 1607 |
$updraftplus->log_e("Uploads path (%s) does not exist - resetting (%s)", $new_upload_path, $this->prior_upload_path); |
| 1608 |
if (false === $wpdb->query("UPDATE ${import_table_prefix}".$mprefix."options SET option_value='".esc_sql($this->prior_upload_path)."' WHERE option_name='upload_path' LIMIT 1")) { |
| 1609 |
echo __('Error','updraftplus'); |
| 1610 |
$updraftplus->log("Failed"); |
| 1611 |
} |
| 1612 |
#update_option('upload_path', $this->prior_upload_path); |
| 1613 |
} |
| 1614 |
} |
| 1615 |
|
| 1616 |
# TODO: Do on all WPMU tables |
| 1617 |
if ($table == $import_table_prefix.'options') { |
| 1618 |
# Bad plugin that hard-codes path references - https://wordpress.org/plugins/custom-content-type-manager/ |
| 1619 |
$cctm_data = $wpdb->get_row($wpdb->prepare("SELECT option_value FROM $wpdb->options WHERE option_name = %s LIMIT 1", 'cctm_data')); |
| 1620 |
if (!empty($cctm_data->option_value)) { |
| 1621 |
$cctm_data = maybe_unserialize($cctm_data->option_value); |
| 1622 |
if (is_array($cctm_data) && !empty($cctm_data['cache']) && is_array($cctm_data['cache'])) { |
| 1623 |
$cctm_data['cache'] = array(); |
| 1624 |
$updraftplus->log_e("Custom content type manager plugin data detected: clearing option cache"); |
| 1625 |
update_option('cctm_data', $cctm_data); |
| 1626 |
} |
| 1627 |
} |
| 1628 |
# Another - http://www.elegantthemes.com/gallery/elegant-builder/ |
| 1629 |
$elegant_data = $wpdb->get_row($wpdb->prepare("SELECT option_value FROM $wpdb->options WHERE option_name = %s LIMIT 1", 'et_images_temp_folder')); |
| 1630 |
if (!empty($elegant_data->option_value)) { |
| 1631 |
$dbase = basename($elegant_data->option_value); |
| 1632 |
$wp_upload_dir = wp_upload_dir(); |
| 1633 |
$edir = $wp_upload_dir['basedir']; |
| 1634 |
if (!is_dir($edir.'/'.$dbase)) @mkdir($edir.'/'.$dbase); |
| 1635 |
$updraftplus->log_e("Elegant themes theme builder plugin data detected: resetting temporary folder"); |
| 1636 |
update_option('et_images_temp_folder', $edir.'/'.$dbase); |
| 1637 |
} |
| 1638 |
# The gantry menu plugin sometimes uses too-long transient names, causing the timeout option to be missing; and hence the transient becomes permanent. |
| 1639 |
# WP 3.4 onwards has $wpdb->delete(). But we support 3.2 onwards. |
| 1640 |
$wpdb->query("DELETE FROM $wpdb->options WHERE option_name LIKE '_transient_gantry-menu%' OR option_name LIKE '_transient_timeout_gantry-menu%'"); |
| 1641 |
} |
| 1642 |
} |
| 1643 |
|
| 1644 |
} elseif ($import_table_prefix != $old_table_prefix && preg_match('/^([\d+]_)?usermeta$/', substr($table, strlen($import_table_prefix)), $matches)) { |
| 1645 |
|
| 1646 |
# This table is not a per-site table, but per-install |
| 1647 |
|
| 1648 |
$updraftplus->log("Table prefix has changed: changing usermeta table field(s) accordingly"); |
| 1649 |
echo sprintf(__('Table prefix has changed: changing %s table field(s) accordingly:', 'updraftplus'),'usermeta').' '; |
| 1650 |
|
| 1651 |
$um_sql = "SELECT umeta_id, meta_key |
| 1652 |
FROM ${import_table_prefix}usermeta |
| 1653 |
WHERE meta_key |
| 1654 |
LIKE '".str_replace('_', '\_', $old_table_prefix)."%'"; |
| 1655 |
|
| 1656 |
$meta_keys = $wpdb->get_results($um_sql); |
| 1657 |
|
| 1658 |
$old_prefix_length = strlen($old_table_prefix); |
| 1659 |
|
| 1660 |
$errors_occurred = false; |
| 1661 |
foreach ($meta_keys as $meta_key ) { |
| 1662 |
//Create new meta key |
| 1663 |
$new_meta_key = $import_table_prefix . substr($meta_key->meta_key, $old_prefix_length); |
| 1664 |
|
| 1665 |
$query = "UPDATE " . $import_table_prefix . "usermeta |
| 1666 |
SET meta_key='".$new_meta_key."' |
| 1667 |
WHERE umeta_id=".$meta_key->umeta_id; |
| 1668 |
|
| 1669 |
if (false === $wpdb->query($query)) $errors_occurred = true; |
| 1670 |
} |
| 1671 |
|
| 1672 |
if ($errors_occurred) { |
| 1673 |
$updraftplus->log("Error when changing usermeta table fields"); |
| 1674 |
echo __('Error', 'updraftplus'); |
| 1675 |
} else { |
| 1676 |
$updraftplus->log("Usermeta table fields changed OK"); |
| 1677 |
echo __('OK', 'updraftplus'); |
| 1678 |
} |
| 1679 |
echo "<br>"; |
| 1680 |
|
| 1681 |
} |
| 1682 |
|
| 1683 |
do_action('updraftplus_restored_db_table', $table, $import_table_prefix); |
| 1684 |
|
| 1685 |
// Re-generate permalinks. Do this last - i.e. make sure everything else is fixed up first. |
| 1686 |
if ($table == $import_table_prefix.'options') $this->flush_rewrite_rules(); |
| 1687 |
|
| 1688 |
} |
| 1689 |
|
| 1690 |
} |
| 1691 |
|
| 1692 |
// The purpose of this is that, in a certain case, we want to forbid the "move" operation from doing a copy/delete if a direct move fails... because we have our own method for retrying (and don't want to risk copying a tonne of data if we can avoid it) |
| 1693 |
if (!class_exists('WP_Filesystem_Direct')) { |
| 1694 |
if (!class_exists('WP_Filesystem_Base')) require_once(ABSPATH.'wp-admin/includes/class-wp-filesystem-base.php'); |
| 1695 |
require_once(ABSPATH.'wp-admin/includes/class-wp-filesystem-direct.php'); |
| 1696 |
} |
| 1697 |
class UpdraftPlus_WP_Filesystem_Direct extends WP_Filesystem_Direct { |
| 1698 |
|
| 1699 |
function move($source, $destination, $overwrite = false) { |
| 1700 |
if ( ! $overwrite && $this->exists($destination) ) |
| 1701 |
return false; |
| 1702 |
|
| 1703 |
// try using rename first. if that fails (for example, source is read only) try copy |
| 1704 |
if ( @rename($source, $destination) ) |
| 1705 |
return true; |
| 1706 |
|
| 1707 |
return false; |
| 1708 |
} |
| 1709 |
|
| 1710 |
} |
| 1711 |
|
| 1712 |
if (!class_exists('WP_Upgrader_Skin')) require_once(ABSPATH.'wp-admin/includes/class-wp-upgrader.php'); |
| 1713 |
class Updraft_Restorer_Skin extends WP_Upgrader_Skin { |
| 1714 |
|
| 1715 |
function header() {} |
| 1716 |
function footer() {} |
| 1717 |
function bulk_header() {} |
| 1718 |
function bulk_footer() {} |
| 1719 |
|
| 1720 |
function error($error) { |
| 1721 |
if (!$error) return; |
| 1722 |
global $updraftplus; |
| 1723 |
if (is_wp_error($error)) { |
| 1724 |
$updraftplus->log_wp_error($error, true); |
| 1725 |
} elseif (is_string($error)) { |
| 1726 |
echo '<strong>'; |
| 1727 |
$updraftplus->log_e($error); |
| 1728 |
echo '</strong>'; |
| 1729 |
} |
| 1730 |
} |
| 1731 |
|
| 1732 |
function feedback($string) { |
| 1733 |
|
| 1734 |
if ( isset( $this->upgrader->strings[$string] ) ) |
| 1735 |
$string = $this->upgrader->strings[$string]; |
| 1736 |
|
| 1737 |
if ( strpos($string, '%') !== false ) { |
| 1738 |
$args = func_get_args(); |
| 1739 |
$args = array_splice($args, 1); |
| 1740 |
if ( $args ) { |
| 1741 |
$args = array_map( 'strip_tags', $args ); |
| 1742 |
$args = array_map( 'esc_html', $args ); |
| 1743 |
$string = vsprintf($string, $args); |
| 1744 |
} |
| 1745 |
} |
| 1746 |
if ( empty($string) ) return; |
| 1747 |
|
| 1748 |
global $updraftplus; |
| 1749 |
$updraftplus->log_e($string); |
| 1750 |
} |
| 1751 |
} |
| 1752 |
|
| 1753 |
// Get a protected property |
| 1754 |
class UpdraftPlus_WPDB extends wpdb { |
| 1755 |
public function updraftplus_getdbh() { |
| 1756 |
return $this->dbh; |
| 1757 |
} |
| 1758 |
public function updraftplus_use_mysqli() { |
| 1759 |
return !empty($this->use_mysqli); |
| 1760 |
} |
| 1761 |
} |
| 1762 |
|