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