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