PluginProbe
MainWP Dashboard: Self-hosted WordPress Management for Agencies / 4.1
MainWP Dashboard: Self-hosted WordPress Management for Agencies v4.1
6.2 6.1.8 6.1.7 6.1.6 6.1.5 6.1.4 6.1.3 6.1.2 6.1.1 6.1 6.0.12 6.0.11 4.6.0.1 5.0 5.0.1 5.0.2 5.0.3 5.0.3.1 5.0.3.2 5.1 5.1.1 5.2 5.2.1 5.2.2 5.3 All 153 releases
mainwp / class / class-mainwp-backup-handler.php

class-mainwp-backup-handler.php in MainWP Dashboard: Self-hosted WordPress Management for Agencies 4.1, at class/class-mainwp-backup-handler.php

1,143 lines 38.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * MainWP Backup Handler.
4 *
5 * @package MainWP/Dashboard
6 */
7
8 namespace MainWP\Dashboard;
9
10 /**
11 * Class MainWP_Backup_Handler
12 *
13 * @package MainWP\Dashboard
14 */
15 class MainWP_Backup_Handler {
16
17 /**
18 * Method get_class_name()
19 *
20 * Get Class Name.
21 *
22 * @return string __CLASS__
23 */
24 public static function get_class_name() {
25 return __CLASS__;
26 }
27
28 /**
29 * Method backup_site()
30 *
31 * Backup Child Site.
32 *
33 * @param mixed $siteid Child Site ID.
34 * @param mixed $pTask Task to perform.
35 * @param mixed $subfolder Subfolder to place backup.
36 *
37 * @gobal object $wp_filesystem WordPress filesystem instance.
38 *
39 * @throws MainWP_Exception
40 *
41 * @return mixed $backup_result
42 */
43 public static function backup_site( $siteid, $pTask, $subfolder ) {
44 // phpcs: ignore -- Current complexity is the only way to achieve desired results, pull request solutions appreciated.
45 if ( ! get_option( 'mainwp_enableLegacyBackupFeature' ) ) {
46 return false;
47 }
48
49 $userid = $pTask->userid;
50 $type = $pTask->type;
51 $exclude = $pTask->exclude;
52 $taskId = $pTask->id;
53 $excludebackup = $pTask->excludebackup;
54 $excludecache = $pTask->excludecache;
55 $excludenonwp = $pTask->excludenonwp;
56 $excludezip = $pTask->excludezip;
57 $pFilename = $pTask->filename;
58
59 if ( '' === trim( $pFilename ) ) {
60 $pFilename = null;
61 }
62
63 $backup_result = array();
64
65 // Creating a backup.
66 $website = MainWP_DB::instance()->get_website_by_id( $siteid );
67 $subfolder = str_replace( '%sitename%', MainWP_Utility::sanitize( $website->name ), $subfolder );
68 $subfolder = str_replace( '%url%', MainWP_Utility::sanitize( MainWP_Utility::get_nice_url( $website->url ) ), $subfolder );
69 $subfolder = str_replace( '%type%', $type, $subfolder );
70 $subfolder = str_replace( '%date%', MainWP_Utility::date( 'Ymd' ), $subfolder );
71 $subfolder = str_replace( '%task%', '', $subfolder );
72 $subfolder = str_replace( '%', '', $subfolder );
73 $subfolder = MainWP_Utility::remove_preslash_spaces( $subfolder );
74 $subfolder = MainWP_Utility::normalize_filename( $subfolder );
75
76 if ( ! MainWP_System::instance()->is_single_user() && ( $userid != $website->userid ) ) {
77 throw new MainWP_Exception( 'Undefined error.' );
78 }
79
80 $websiteCleanUrl = $website->url;
81 if ( '/' === substr( $websiteCleanUrl, - 1 ) ) {
82 $websiteCleanUrl = substr( $websiteCleanUrl, 0, - 1 );
83 }
84 $websiteCleanUrl = str_replace(
85 array( 'http://', 'https://', '/' ),
86 array( '', '', '-' ),
87 $websiteCleanUrl
88 );
89
90 if ( 'db' === $type ) {
91 $ext = '.sql.' . self::get_current_archive_extension( $website, $pTask );
92 } else {
93 $ext = '.' . self::get_current_archive_extension( $website, $pTask );
94 }
95
96 $file = str_replace(
97 array(
98 '%sitename%',
99 '%url%',
100 '%date%',
101 '%time%',
102 '%type%',
103 ),
104 array(
105 MainWP_Utility::sanitize( $website->name ),
106 $websiteCleanUrl,
107 MainWP_Utility::date( 'm-d-Y' ),
108 MainWP_Utility::date( 'G\hi\ms\s' ),
109 $type,
110 ),
111 $pFilename
112 );
113 $file = str_replace( '%', '', $file );
114 $file = MainWP_Utility::normalize_filename( $file );
115
116 if ( ! empty( $file ) ) {
117 $file .= $ext;
118 }
119
120 if ( 'zip' == $pTask->archiveFormat ) {
121 $loadFilesBeforeZip = $pTask->loadFilesBeforeZip;
122 } elseif ( '' == $pTask->archiveFormat || 'site' == $pTask->archiveFormat ) {
123 $loadFilesBeforeZip = $website->loadFilesBeforeZip;
124 } else {
125 $loadFilesBeforeZip = 1;
126 }
127
128 if ( 1 === $loadFilesBeforeZip ) {
129 $loadFilesBeforeZip = get_option( 'mainwp_options_loadFilesBeforeZip' );
130 $loadFilesBeforeZip = ( 1 === $loadFilesBeforeZip || false === $loadFilesBeforeZip );
131 } else {
132 $loadFilesBeforeZip = ( 2 === $loadFilesBeforeZip );
133 }
134
135 if ( ( 'zip' == $pTask->archiveFormat ) && ( 1 == $pTask->maximumFileDescriptorsOverride ) ) {
136 $maximumFileDescriptorsAuto = ( 1 == $pTask->maximumFileDescriptorsAuto );
137 $maximumFileDescriptors = $pTask->maximumFileDescriptors;
138 } elseif ( ( '' == $pTask->archiveFormat || 'site' == $pTask->archiveFormat ) && ( 1 == $website->maximumFileDescriptorsOverride ) ) {
139 $maximumFileDescriptorsAuto = ( 1 == $website->maximumFileDescriptorsAuto );
140 $maximumFileDescriptors = $website->maximumFileDescriptors;
141 } else {
142 $maximumFileDescriptorsAuto = get_option( 'mainwp_maximumFileDescriptorsAuto' );
143 $maximumFileDescriptors = get_option( 'mainwp_maximumFileDescriptors' );
144 $maximumFileDescriptors = ( false === $maximumFileDescriptors ? 150 : $maximumFileDescriptors );
145 }
146
147 $information = false;
148 $backupTaskProgress = MainWP_DB_Backup::instance()->get_backup_task_progress( $taskId, $website->id );
149 if ( empty( $backupTaskProgress ) || ( $backupTaskProgress->dtsFetched < $pTask->last_run ) ) {
150 $start = microtime( true );
151 try {
152 $pid = time();
153
154 if ( empty( $backupTaskProgress ) ) {
155 MainWP_DB_Backup::instance()->add_backup_task_progress( $taskId, $website->id, array() );
156 }
157
158 MainWP_DB_Backup::instance()->update_backup_task_progress(
159 $taskId,
160 $website->id,
161 array(
162 'dtsFetched' => time(),
163 'fetchResult' => wp_json_encode( array() ),
164 'downloadedDB' => '',
165 'downloadedDBComplete' => 0,
166 'downloadedFULL' => '',
167 'downloadedFULLComplete' => 0,
168 'removedFiles' => 0,
169 'attempts' => 0,
170 'last_error' => '',
171 'pid' => $pid,
172 )
173 );
174
175 $params = array(
176 'type' => $type,
177 'exclude' => $exclude,
178 'excludebackup' => $excludebackup,
179 'excludecache' => $excludecache,
180 'excludenonwp' => $excludenonwp,
181 'excludezip' => $excludezip,
182 'ext' => self::get_current_archive_extension( $website, $pTask ),
183 'file_descriptors_auto' => $maximumFileDescriptorsAuto,
184 'file_descriptors' => $maximumFileDescriptors,
185 'loadFilesBeforeZip' => $loadFilesBeforeZip,
186 'pid' => $pid,
187 'f' => $file,
188 );
189
190 MainWP_Logger::instance()->debug_for_website( $website, 'backup', 'Requesting backup: ' . MainWP_Utility::value_to_string( $params, 1 ) );
191
192 $information = MainWP_Connect::fetch_url_authed( $website, 'backup', $params, false, false, false );
193 } catch ( MainWP_Exception $e ) {
194 MainWP_Logger::instance()->warning_for_website( $website, 'backup', 'ERROR: ' . $e->getMessage() . ' (' . $e->get_message_extra() . ')' );
195 $stop = microtime( true );
196 // Bigger then 30 seconds means a timeout.
197 if ( 30 < ( $stop - $start ) ) {
198 MainWP_DB_Backup::instance()->update_backup_task_progress(
199 $taskId,
200 $website->id,
201 array(
202 'last_error' => wp_json_encode(
203 array(
204 'message' => $e->getMessage(),
205 'extra' => $e->get_message_extra(),
206 )
207 ),
208 )
209 );
210
211 return false;
212 }
213
214 throw $e;
215 }
216
217 if ( isset( $information['error'] ) && stristr( $information['error'], 'Another backup process is running' ) ) {
218 return false;
219 }
220
221 $backupTaskProgress = MainWP_DB_Backup::instance()->update_backup_task_progress( $taskId, $website->id, array( 'fetchResult' => wp_json_encode( $information ) ) );
222 } elseif ( empty( $backupTaskProgress->fetchResult ) ) {
223 try {
224 $temp = MainWP_Connect::fetch_url_authed( $website, 'backup_checkpid', array( 'pid' => $backupTaskProgress->pid ) );
225 } catch ( \Exception $e ) {
226 // ok!
227 }
228
229 if ( ! empty( $temp ) ) {
230 if ( 'stalled' === $temp['status'] ) {
231 if ( 5 > $backupTaskProgress->attempts ) {
232 $backupTaskProgress = MainWP_DB_Backup::instance()->update_backup_task_progress( $taskId, $website->id, array( 'attempts' => $backupTaskProgress->attempts ++ ) );
233
234 try {
235 $information = MainWP_Connect::fetch_url_authed(
236 $website,
237 'backup',
238 array(
239 'type' => $type,
240 'exclude' => $exclude,
241 'excludebackup' => $excludebackup,
242 'excludecache' => $excludecache,
243 'excludenonwp' => $excludenonwp,
244 'excludezip' => $excludezip,
245 'ext' => self::get_current_archive_extension( $website, $pTask ),
246 'file_descriptors_auto' => $maximumFileDescriptorsAuto,
247 'file_descriptors' => $maximumFileDescriptors,
248 'loadFilesBeforeZip' => $loadFilesBeforeZip,
249 'pid' => $backupTaskProgress->pid,
250 'append' => '1',
251 'f' => $temp['file'],
252 ),
253 false,
254 false,
255 false
256 );
257
258 if ( isset( $information['error'] ) && stristr( $information['error'], 'Another backup process is running' ) ) {
259 MainWP_DB_Backup::instance()->update_backup_task_progress( $taskId, $website->id, array( 'attempts' => ( $backupTaskProgress->attempts - 1 ) ) );
260
261 return false;
262 }
263 } catch ( MainWP_Exception $e ) {
264 return false;
265 }
266
267 $backupTaskProgress = MainWP_DB_Backup::instance()->update_backup_task_progress( $taskId, $website->id, array( 'fetchResult' => wp_json_encode( $information ) ) );
268 } else {
269 throw new MainWP_Exception( 'Backup failed after 5 retries.' );
270 }
271 } elseif ( 'invalid' === $temp['status'] ) {
272 $error = json_decode( $backupTaskProgress->last_error );
273
274 if ( ! is_array( $error ) ) {
275 throw new MainWP_Exception( 'Backup failed.' );
276 } else {
277 throw new MainWP_Exception( $error['message'], $error['extra'] );
278 }
279 } elseif ( 'busy' === $temp['status'] ) {
280 return false;
281 } elseif ( 'done' === $temp['status'] ) {
282 if ( 'full' === $type ) {
283 $information['full'] = $temp['file'];
284 $information['db'] = false;
285 } else {
286 $information['full'] = false;
287 $information['db'] = $temp['file'];
288 }
289
290 $information['size'] = $temp['size'];
291
292 $backupTaskProgress = MainWP_DB_Backup::instance()->update_backup_task_progress( $taskId, $website->id, array( 'fetchResult' => wp_json_encode( $information ) ) );
293 }
294 } else {
295 if ( 5 > $backupTaskProgress->attempts ) {
296 $backupTaskProgress = MainWP_DB_Backup::instance()->update_backup_task_progress( $taskId, $website->id, array( 'attempts' => $backupTaskProgress->attempts ++ ) );
297 } else {
298 throw new MainWP_Exception( 'Backup failed after 5 retries.' );
299 }
300 }
301 }
302
303 if ( false === $information ) {
304 $information = $backupTaskProgress->fetchResult;
305 }
306
307 if ( isset( $information['error'] ) ) {
308 throw new MainWP_Exception( $information['error'] );
309 } elseif ( 'db' === $type && ! $information['db'] ) {
310 throw new MainWP_Exception( 'Database backup failed.' );
311 } elseif ( 'full' === $type && ! $information['full'] ) {
312 throw new MainWP_Exception( 'Full backup failed.' );
313 } elseif ( isset( $information['db'] ) ) {
314 $hasWPFileSystem = MainWP_System_Utility::get_wp_file_system();
315
316 /**
317 * WordPress filesystem instance.
318 *
319 * @global object
320 */
321 global $wp_filesystem;
322
323 $dir = MainWP_System_Utility::get_mainwp_specific_dir( $website->id );
324
325 $wp_filesystem->mkdir( $dir, 0777, true );
326
327 if ( ! $wp_filesystem->exists( $dir . 'index.php' ) ) {
328 $wp_filesystem->touch( $dir . 'index.php' );
329 }
330
331 // Clean old backups from our system.
332 $maxBackups = get_option( 'mainwp_backupsOnServer' );
333 if ( false === $maxBackups ) {
334 $maxBackups = 1;
335 }
336
337 if ( 1 !== $backupTaskProgress->removedFiles ) {
338 $dbBackups = array();
339 $fullBackups = array();
340 if ( $wp_filesystem->exists( $dir ) ) {
341 $dh = opendir( $dir );
342 if ( $dh ) {
343 while ( false !== ( $file = readdir( $dh ) ) ) {
344 if ( '.' !== $file && '..' !== $file ) {
345 $theFile = $dir . $file;
346 if ( $information['db'] && self::is_sql_file( $file ) ) {
347 $dbBackups[ filemtime( $theFile ) . $file ] = $theFile;
348 }
349
350 if ( $information['full'] && self::is_archive( $file ) && ! self::is_sql_archive( $file ) ) {
351 $fullBackups[ filemtime( $theFile ) . $file ] = $theFile;
352 }
353 }
354 }
355 closedir( $dh );
356 }
357 }
358 krsort( $dbBackups );
359 krsort( $fullBackups );
360
361 $cnt = 0;
362 foreach ( $dbBackups as $key => $dbBackup ) {
363 $cnt ++;
364 if ( $cnt >= $maxBackups ) {
365 $wp_filesystem->delete( $dbBackup );
366 }
367 }
368
369 $cnt = 0;
370 foreach ( $fullBackups as $key => $fullBackup ) {
371 $cnt ++;
372 if ( $cnt >= $maxBackups ) {
373 $wp_filesystem->delete( $fullBackup );
374 }
375 }
376 $backupTaskProgress = MainWP_DB_Backup::instance()->update_backup_task_progress( $taskId, $website->id, array( 'removedFiles' => 1 ) );
377 }
378
379 $localBackupFile = null;
380
381 $fm_date = MainWP_Utility::sanitize_file_name( MainWP_Utility::date( get_option( 'date_format' ) ) );
382 $fm_time = MainWP_Utility::sanitize_file_name( MainWP_Utility::date( get_option( 'time_format' ) ) );
383
384 $what = null;
385 $regexBackupFile = null;
386
387 if ( $information['db'] ) {
388 $what = 'db';
389 $regexBackupFile = 'db-' . $websiteCleanUrl . '-(.*)-(.*).sql(\.zip|\.tar|\.tar\.gz|\.tar\.bz2)?';
390 if ( '' == $backupTaskProgress->downloadedDB ) {
391 $localBackupFile = $dir . 'db-' . $websiteCleanUrl . '-' . $fm_date . '-' . $fm_time;
392
393 if ( null != $pFilename ) {
394 $filename = str_replace(
395 array(
396 '%sitename%',
397 '%url%',
398 '%date%',
399 '%time%',
400 '%type%',
401 ),
402 array(
403 MainWP_Utility::sanitize( $website->name ),
404 $websiteCleanUrl,
405 $fm_date,
406 $fm_time,
407 $what,
408 ),
409 $pFilename
410 );
411 $filename = str_replace( '%', '', $filename );
412 $localBackupFile = $dir . $filename;
413 }
414 $localBackupFile .= MainWP_Utility::get_real_extension( $information['db'] );
415
416 $localBackupFile = MainWP_Utility::normalize_filename( $localBackupFile );
417
418 $backupTaskProgress = MainWP_DB_Backup::instance()->update_backup_task_progress( $taskId, $website->id, array( 'downloadedDB' => $localBackupFile ) );
419 } else {
420 $localBackupFile = $backupTaskProgress->downloadedDB;
421 }
422
423 if ( 0 == $backupTaskProgress->downloadedDBComplete ) {
424 MainWP_Connect::download_to_file( MainWP_Connect::get_get_data_authed( $website, $information['db'], 'fdl' ), $localBackupFile, $information['size'], $website->http_user, $website->http_pass );
425 $backupTaskProgress = MainWP_DB_Backup::instance()->update_backup_task_progress( $taskId, $website->id, array( 'downloadedDBComplete' => 1 ) );
426 }
427 }
428
429 if ( $information['full'] ) {
430 $realExt = MainWP_Utility::get_real_extension( $information['full'] );
431 $what = 'full';
432 $regexBackupFile = 'full-' . $websiteCleanUrl . '-(.*)-(.*).(zip|tar|tar.gz|tar.bz2)';
433 if ( '' == $backupTaskProgress->downloadedFULL ) {
434 $localBackupFile = $dir . 'full-' . $websiteCleanUrl . '-' . $fm_date . '-' . $fm_time . $realExt;
435
436 if ( null != $pFilename ) {
437 $filename = str_replace(
438 array(
439 '%sitename%',
440 '%url%',
441 '%date%',
442 '%time%',
443 '%type%',
444 ),
445 array(
446 MainWP_Utility::sanitize( $website->name ),
447 $websiteCleanUrl,
448 $fm_date,
449 $fm_time,
450 $what,
451 ),
452 $pFilename
453 );
454 $filename = str_replace( '%', '', $filename );
455 $localBackupFile = $dir . $filename . $realExt;
456 }
457
458 $localBackupFile = MainWP_Utility::normalize_filename( $localBackupFile );
459
460 $backupTaskProgress = MainWP_DB_Backup::instance()->update_backup_task_progress( $taskId, $website->id, array( 'downloadedFULL' => $localBackupFile ) );
461 } else {
462 $localBackupFile = $backupTaskProgress->downloadedFULL;
463 }
464
465 if ( 0 == $backupTaskProgress->downloadedFULLComplete ) {
466 if ( file_exists( $localBackupFile ) ) {
467 $time = filemtime( $localBackupFile );
468
469 $minutes = gmdate( 'i', time() );
470 $seconds = gmdate( 's', time() );
471
472 $file_minutes = gmdate( 'i', $time );
473 $file_seconds = gmdate( 's', $time );
474
475 $minuteDiff = $minutes - $file_minutes;
476 if ( 59 === $minuteDiff ) {
477 $minuteDiff = 1;
478 }
479 $secondsdiff = ( $minuteDiff * 60 ) + $seconds - $file_seconds;
480
481 if ( 60 > $secondsdiff ) {
482 // still downloading.
483 return false;
484 }
485 }
486
487 MainWP_Connect::download_to_file( MainWP_Connect::get_get_data_authed( $website, $information['full'], 'fdl' ), $localBackupFile, $information['size'], $website->http_user, $website->http_pass );
488 MainWP_Connect::fetch_url_authed( $website, 'delete_backup', array( 'del' => $information['full'] ) );
489 $backupTaskProgress = MainWP_DB_Backup::instance()->update_backup_task_progress( $taskId, $website->id, array( 'downloadedFULLComplete' => 1 ) );
490 }
491 }
492
493 $unique = $pTask->last_run;
494
495 do_action( 'mainwp_postprocess_backup_site', $localBackupFile, $what, $subfolder, $regexBackupFile, $website, $taskId, $unique );
496 $extra_result = apply_filters( 'mainwp_postprocess_backup_sites_feedback', array(), $unique );
497 if ( is_array( $extra_result ) ) {
498 foreach ( $extra_result as $key => $value ) {
499 $backup_result[ $key ] = $value;
500 }
501 }
502 } else {
503 throw new MainWP_Exception( 'Database backup failed due to an undefined error.' );
504 }
505
506 return $backup_result;
507 }
508
509 /**
510 * Method backup_download_file()
511 *
512 * Download backup file.
513 *
514 * @param mixed $pSiteId Child Site ID.
515 * @param mixed $pType full|db Type of backup.
516 * @param mixed $pUrl Backup location.
517 * @param mixed $pFile Backup File.
518 *
519 * @return bool true|false
520 */
521 public static function backup_download_file( $pSiteId, $pType, $pUrl, $pFile ) {
522 $hasWPFileSystem = MainWP_System_Utility::get_wp_file_system();
523
524 /**
525 * WordPress filesystem instance.
526 *
527 * @global object
528 */
529 global $wp_filesystem;
530
531 $dir = dirname( $pFile ) . '/';
532 $wp_filesystem->mkdir( $dir, 0777, true );
533 if ( ! $wp_filesystem->exists( $dir . 'index.php' ) ) {
534 $wp_filesystem->touch( $dir . 'index.php' );
535 }
536 // Clean old backups from our system.
537 $maxBackups = get_option( 'mainwp_backupsOnServer' );
538 if ( false === $maxBackups ) {
539 $maxBackups = 1;
540 }
541
542 $dbBackups = array();
543 $fullBackups = array();
544
545 if ( file_exists( $dir ) ) {
546 $dh = opendir( $dir );
547 if ( $dh ) {
548 while ( false !== ( $file = readdir( $dh ) ) ) {
549 if ( '.' !== $file && '..' !== $file ) {
550 $theFile = $dir . $file;
551 if ( 'db' === $pType && self::is_sql_file( $file ) ) {
552 $dbBackups[ filemtime( $theFile ) . $file ] = $theFile;
553 }
554
555 if ( 'full' === $pType && self::is_archive( $file ) && ! self::is_sql_archive( $file ) ) {
556 $fullBackups[ filemtime( $theFile ) . $file ] = $theFile;
557 }
558 }
559 }
560 closedir( $dh );
561 }
562 }
563 krsort( $dbBackups );
564 krsort( $fullBackups );
565
566 $cnt = 0;
567 foreach ( $dbBackups as $key => $dbBackup ) {
568 $cnt ++;
569 if ( $cnt >= $maxBackups ) {
570 $wp_filesystem->delete( $dbBackup );
571 }
572 }
573
574 $cnt = 0;
575 foreach ( $fullBackups as $key => $fullBackup ) {
576 $cnt ++;
577 if ( $cnt >= $maxBackups ) {
578 $wp_filesystem->delete( $fullBackup );
579 }
580 }
581
582 $website = MainWP_DB::instance()->get_website_by_id( $pSiteId );
583 MainWP_Utility::end_session();
584
585 $what = null;
586 if ( 'db' === $pType ) {
587 MainWP_Connect::download_to_file( MainWP_Connect::get_get_data_authed( $website, $pUrl, 'fdl' ), $pFile, false, $website->http_user, $website->http_pass );
588 }
589
590 if ( 'full' === $pType ) {
591 MainWP_Connect::download_to_file( MainWP_Connect::get_get_data_authed( $website, $pUrl, 'fdl' ), $pFile, false, $website->http_user, $website->http_pass );
592 }
593
594 return true;
595 }
596
597 /**
598 * Method backup_delete_file()
599 *
600 * Delete backup file.
601 *
602 * @param mixed $pSiteId Child Site ID
603 * @param mixed $pFile File to delete.
604 *
605 * @return bool true
606 */
607 public static function backup_delete_file( $pSiteId, $pFile ) {
608 $website = MainWP_DB::instance()->get_website_by_id( $pSiteId );
609 MainWP_Connect::fetch_url_authed( $website, 'delete_backup', array( 'del' => $pFile ) );
610
611 return true;
612 }
613
614 /**
615 * Method backup_check_pid()
616 *
617 * Check backup pid.
618 *
619 * @param mixed $pSiteId Child Site id.
620 * @param mixed $pid Backup pid.
621 * @param mixed $type full|db Type of backup.
622 * @param mixed $subfolder Subfolder for backup.
623 * @param mixed $pFilename Backups filename
624 *
625 * @return array $status, $result.
626 */
627 public static function backup_check_pid( $pSiteId, $pid, $type, $subfolder, $pFilename ) {
628 $website = MainWP_DB::instance()->get_website_by_id( $pSiteId );
629
630 MainWP_Utility::end_session();
631 $information = MainWP_Connect::fetch_url_authed( $website, 'backup_checkpid', array( 'pid' => $pid ) );
632
633 $status = $information['status'];
634
635 $result = isset( $information['file'] ) ? array( 'file' => $information['file'] ) : array();
636 if ( 'done' === $status ) {
637 $result['file'] = $information['file'];
638 $result['size'] = $information['size'];
639
640 $subfolder = str_replace( '%sitename%', MainWP_Utility::sanitize( $website->name ), $subfolder );
641 $subfolder = str_replace( '%url%', MainWP_Utility::sanitize( MainWP_Utility::get_nice_url( $website->url ) ), $subfolder );
642 $subfolder = str_replace( '%type%', $type, $subfolder );
643 $subfolder = str_replace( '%date%', MainWP_Utility::date( 'Ymd' ), $subfolder );
644 $subfolder = str_replace( '%task%', '', $subfolder );
645 $subfolder = str_replace( '%', '', $subfolder );
646 $subfolder = MainWP_Utility::remove_preslash_spaces( $subfolder );
647 $subfolder = MainWP_Utility::normalize_filename( $subfolder );
648
649 $result['subfolder'] = $subfolder;
650
651 $websiteCleanUrl = $website->url;
652 if ( '/' === substr( $websiteCleanUrl, - 1 ) ) {
653 $websiteCleanUrl = substr( $websiteCleanUrl, 0, - 1 );
654 }
655 $websiteCleanUrl = str_replace(
656 array( 'http://', 'https://', '/' ),
657 array(
658 '',
659 '',
660 '-',
661 ),
662 $websiteCleanUrl
663 );
664
665 $dir = MainWP_System_Utility::get_mainwp_specific_dir( $pSiteId );
666
667 $fm_date = MainWP_Utility::sanitize_file_name( MainWP_Utility::date( get_option( 'date_format' ) ) );
668 $fm_time = MainWP_Utility::sanitize_file_name( MainWP_Utility::date( get_option( 'time_format' ) ) );
669
670 if ( 'db' === $type ) {
671 $localBackupFile = $dir . 'db-' . $websiteCleanUrl . '-' . $fm_date . '-' . $fm_time . MainWP_Utility::get_real_extension( $information['file'] );
672 $localRegexFile = 'db-' . $websiteCleanUrl . '-(.*)-(.*).sql(\.zip|\.tar|\.tar\.gz|\.tar\.bz2)?';
673 } else {
674 $localBackupFile = $dir . 'full-' . $websiteCleanUrl . '-' . $fm_date . '-' . $fm_time . MainWP_Utility::get_real_extension( $information['file'] );
675 $localRegexFile = 'full-' . $websiteCleanUrl . '-(.*)-(.*).(zip|tar|tar.gz|tar.bz2)';
676 }
677
678 if ( null != $pFilename ) {
679 $filename = str_replace(
680 array(
681 '%sitename%',
682 '%url%',
683 '%date%',
684 '%time%',
685 '%type%',
686 ),
687 array(
688 MainWP_Utility::sanitize( $website->name ),
689 $websiteCleanUrl,
690 $fm_date,
691 $fm_time,
692 $type,
693 ),
694 $pFilename
695 );
696 $filename = str_replace( '%', '', $filename );
697 $localBackupFile = $dir . $filename;
698 $localBackupFile = MainWP_Utility::normalize_filename( $localBackupFile );
699
700 if ( 'db' === $type ) {
701 $localBackupFile .= MainWP_Utility::get_real_extension( $information['file'] );
702 } else {
703 $localBackupFile .= MainWP_Utility::get_real_extension( $information['file'] );
704 }
705 }
706
707 $result['local'] = $localBackupFile;
708 $result['regexfile'] = $localRegexFile;
709 }
710
711 return array(
712 'status' => $status,
713 'result' => $result,
714 );
715 }
716
717 /**
718 * Method backup()
719 *
720 * Backup Child Site.
721 *
722 * @param mixed $pSiteId Child Site ID.
723 * @param mixed $pType full|db Typ of backup.
724 * @param mixed $pSubfolder Subfolder to store backup.
725 * @param mixed $pExclude Exclusion list.
726 * @param mixed $excludebackup Exclued backup files.
727 * @param mixed $excludecache Exclude cache files.
728 * @param mixed $excludenonwp Exclude no WP Files.
729 * @param mixed $excludezip Exclude Zip files
730 * @param null $pFilename Name of backup file.
731 * @param string $pFileNameUID File name unique ID.
732 * @param bool $pArchiveFormat Achive format.
733 * @param bool $pMaximumFileDescriptorsOverride Overide maximum file descriptors.
734 * @param bool $pMaximumFileDescriptorsAuto Auto maximum file descriptors.
735 * @param bool $pMaximumFileDescriptors Maximum file decriptors.
736 * @param bool $pLoadFilesBeforeZip Load files before zip.
737 * @param bool $pid Backup pid.
738 * @param bool $append Append to backup.
739 *
740 * @throw MainWP_Exception
741 *
742 * @return mixed $backup_result
743 */
744 public static function backup(
745 $pSiteId,
746 $pType,
747 $pSubfolder,
748 $pExclude,
749 $excludebackup,
750 $excludecache,
751 $excludenonwp,
752 $excludezip,
753 $pFilename = null,
754 $pFileNameUID = '',
755 $pArchiveFormat = false,
756 $pMaximumFileDescriptorsOverride = false,
757 $pMaximumFileDescriptorsAuto = false,
758 $pMaximumFileDescriptors = false,
759 $pLoadFilesBeforeZip = false,
760 $pid = false,
761 $append = false ) {
762
763 if ( ! get_option( 'mainwp_enableLegacyBackupFeature' ) ) {
764 return false;
765 }
766
767 if ( '' == trim( $pFilename ) ) {
768 $pFilename = null;
769 }
770
771 $backup_result = array();
772
773 $website = MainWP_DB::instance()->get_website_by_id( $pSiteId );
774 $subfolder = str_replace( '%sitename%', MainWP_Utility::sanitize( $website->name ), $pSubfolder );
775 $subfolder = str_replace( '%url%', MainWP_Utility::sanitize( MainWP_Utility::get_nice_url( $website->url ) ), $subfolder );
776 $subfolder = str_replace( '%type%', $pType, $subfolder );
777 $subfolder = str_replace( '%date%', MainWP_Utility::date( 'Ymd' ), $subfolder );
778 $subfolder = str_replace( '%task%', '', $subfolder );
779 $subfolder = str_replace( '%', '', $subfolder );
780 $subfolder = MainWP_Utility::remove_preslash_spaces( $subfolder );
781 $subfolder = MainWP_Utility::normalize_filename( $subfolder );
782
783 if ( ! MainWP_System_Utility::can_edit_website( $website ) ) {
784 throw new MainWP_Exception( 'You are not allowed to backup this site' );
785 }
786
787 $websiteCleanUrl = $website->url;
788 if ( '/' === substr( $websiteCleanUrl, - 1 ) ) {
789 $websiteCleanUrl = substr( $websiteCleanUrl, 0, - 1 );
790 }
791 $websiteCleanUrl = str_replace(
792 array( 'http://', 'https://', '/' ),
793 array( '', '', '-' ),
794 $websiteCleanUrl
795 );
796
797 if ( false === $pMaximumFileDescriptorsOverride ) {
798 if ( 1 == $website->maximumFileDescriptorsOverride ) {
799 $maximumFileDescriptorsAuto = ( 1 == $website->maximumFileDescriptorsAuto );
800 $maximumFileDescriptors = $website->maximumFileDescriptors;
801 } else {
802 $maximumFileDescriptorsAuto = get_option( 'mainwp_maximumFileDescriptorsAuto' );
803 $maximumFileDescriptors = get_option( 'mainwp_maximumFileDescriptors' );
804 $maximumFileDescriptors = ( false === $maximumFileDescriptors ? 150 : $maximumFileDescriptors );
805 }
806 } elseif ( ( 'global' !== $pArchiveFormat ) && ( 1 === $pMaximumFileDescriptorsOverride ) ) {
807 $maximumFileDescriptorsAuto = ( 1 === $pMaximumFileDescriptorsAuto );
808 $maximumFileDescriptors = $pMaximumFileDescriptors;
809 } else {
810 $maximumFileDescriptorsAuto = get_option( 'mainwp_maximumFileDescriptorsAuto' );
811 $maximumFileDescriptors = get_option( 'mainwp_maximumFileDescriptors' );
812 $maximumFileDescriptors = ( false === $maximumFileDescriptors ? 150 : $maximumFileDescriptors );
813 }
814
815 $file = str_replace(
816 array(
817 '%sitename%',
818 '%url%',
819 '%date%',
820 '%time%',
821 '%type%',
822 ),
823 array(
824 MainWP_Utility::sanitize( $website->name ),
825 $websiteCleanUrl,
826 MainWP_Utility::date( 'm-d-Y' ),
827 MainWP_Utility::date( 'G\hi\ms\s' ),
828 $pType,
829 ),
830 $pFilename
831 );
832 $file = str_replace( '%', '', $file );
833 $file = MainWP_Utility::normalize_filename( $file );
834
835 // Normal flow: check site settings & fallback to global.
836 if ( false === $pLoadFilesBeforeZip ) {
837 $loadFilesBeforeZip = $website->loadFilesBeforeZip;
838 if ( 1 === $loadFilesBeforeZip ) {
839 $loadFilesBeforeZip = get_option( 'mainwp_options_loadFilesBeforeZip' );
840 $loadFilesBeforeZip = ( 1 === $loadFilesBeforeZip || false === $loadFilesBeforeZip );
841 } else {
842 $loadFilesBeforeZip = ( 2 === $loadFilesBeforeZip );
843 }
844 } elseif ( 'global' === $pArchiveFormat || 1 === $pLoadFilesBeforeZip ) { // Overriden flow: only fallback to global.
845 $loadFilesBeforeZip = get_option( 'mainwp_options_loadFilesBeforeZip' );
846 $loadFilesBeforeZip = ( 1 === $loadFilesBeforeZip || false === $loadFilesBeforeZip );
847 } else {
848 $loadFilesBeforeZip = ( 2 === $pLoadFilesBeforeZip );
849 }
850
851 // Nomral flow: check site settings & fallback to global.
852 if ( false === $pArchiveFormat ) {
853 $archiveFormat = self::get_current_archive_extension( $website );
854 } elseif ( 'global' === $pArchiveFormat ) {
855 $archiveFormat = self::get_current_archive_extension();
856 } else {
857 $archiveFormat = $pArchiveFormat;
858 }
859
860 MainWP_Utility::end_session();
861
862 $params = array(
863 'type' => $pType,
864 'exclude' => $pExclude,
865 'excludebackup' => $excludebackup,
866 'excludecache' => $excludecache,
867 'excludenonwp' => $excludenonwp,
868 'excludezip' => $excludezip,
869 'ext' => $archiveFormat,
870 'file_descriptors_auto' => $maximumFileDescriptorsAuto,
871 'file_descriptors' => $maximumFileDescriptors,
872 'loadFilesBeforeZip' => $loadFilesBeforeZip,
873 'f' => $file,
874 'fileUID' => $pFileNameUID,
875 'pid' => $pid,
876 'append' => ( $append ? 1 : 0 ),
877 );
878
879 MainWP_Logger::instance()->debug_for_website( $website, 'backup', 'Requesting backup: ' . MainWP_Utility::value_to_string( $params, 1 ) );
880
881 $information = MainWP_Connect::fetch_url_authed( $website, 'backup', $params, false, false, false );
882 do_action( 'mainwp_managesite_backup', $website, array( 'type' => $pType ), $information );
883
884 if ( isset( $information['error'] ) ) {
885 throw new MainWP_Exception( $information['error'] );
886 } elseif ( 'db' === $pType && ! $information['db'] ) {
887 throw new MainWP_Exception( 'Database backup failed.' );
888 } elseif ( 'full' === $pType && ! $information['full'] ) {
889 throw new MainWP_Exception( 'Full backup failed.' );
890 } elseif ( isset( $information['db'] ) ) {
891 if ( false !== $information['db'] ) {
892 $backup_result['url'] = $information['db'];
893 $backup_result['type'] = 'db';
894 } elseif ( false !== $information['full'] ) {
895 $backup_result['url'] = $information['full'];
896 $backup_result['type'] = 'full';
897 }
898
899 if ( isset( $information['size'] ) ) {
900 $backup_result['size'] = $information['size'];
901 }
902 $backup_result['subfolder'] = $subfolder;
903
904 $dir = MainWP_System_Utility::get_mainwp_specific_dir( $pSiteId );
905
906 $fm_date = MainWP_Utility::sanitize_file_name( MainWP_Utility::date( get_option( 'date_format' ) ) );
907 $fm_time = MainWP_Utility::sanitize_file_name( MainWP_Utility::date( get_option( 'time_format' ) ) );
908
909 if ( 'db' === $pType ) {
910 $localBackupFile = $dir . 'db-' . $websiteCleanUrl . '-' . $fm_date . '-' . $fm_time . MainWP_Utility::get_real_extension( $information['db'] );
911 $localRegexFile = 'db-' . $websiteCleanUrl . '-(.*)-(.*).sql(\.zip|\.tar|\.tar\.gz|\.tar\.bz2)?';
912 } else {
913 $localBackupFile = $dir . 'full-' . $websiteCleanUrl . '-' . $fm_date . '-' . $fm_time . MainWP_Utility::get_real_extension( $information['full'] );
914 $localRegexFile = 'full-' . $websiteCleanUrl . '-(.*)-(.*).(zip|tar|tar.gz|tar.bz2)';
915 }
916
917 if ( null != $pFilename ) {
918 $filename = str_replace(
919 array(
920 '%sitename%',
921 '%url%',
922 '%date%',
923 '%time%',
924 '%type%',
925 ),
926 array(
927 MainWP_Utility::sanitize( $website->name ),
928 $websiteCleanUrl,
929 $fm_date,
930 $fm_time,
931 $pType,
932 ),
933 $pFilename
934 );
935 $filename = str_replace( '%', '', $filename );
936 $localBackupFile = $dir . $filename;
937 $localBackupFile = MainWP_Utility::normalize_filename( $localBackupFile );
938
939 if ( 'db' === $pType ) {
940 $localBackupFile .= MainWP_Utility::get_real_extension( $information['db'] );
941 } else {
942 $localBackupFile .= MainWP_Utility::get_real_extension( $information['full'] );
943 }
944 }
945
946 $backup_result['local'] = $localBackupFile;
947 $backup_result['regexfile'] = $localRegexFile;
948
949 return $backup_result;
950 } else {
951 throw new MainWP_Exception( 'Database backup failed due to an undefined error' );
952 }
953 }
954
955 /**
956 * Method is_archive()
957 *
958 * Check if Archive.
959 *
960 * @param mixed $pFileName File to check.
961 * @param string $pPrefix File prefix.
962 * @param string $pSuffix File suffix.
963 *
964 * @return mixed Files that are archives.
965 */
966 public static function is_archive( $pFileName, $pPrefix = '', $pSuffix = '' ) {
967 return preg_match( '/' . $pPrefix . '(.*).(zip|tar|tar.gz|tar.bz2)' . $pSuffix . '$/', $pFileName );
968 }
969
970 /**
971 * Method is_sql_file()
972 *
973 * Check if file is SQL.
974 *
975 * @param mixed $pFileName File to check.
976 *
977 * @return mixed SQL Files.
978 */
979 public static function is_sql_file( $pFileName ) {
980 return preg_match( '/(.*).sql$/', $pFileName ) || self::is_sql_archive( $pFileName );
981 }
982
983 /**
984 * Method is_sql_archive()
985 *
986 * Check if is SQL Archive.
987 *
988 * @param mixed $pFileName File to check.
989 *
990 * @return mixed SQL archive.
991 */
992 public static function is_sql_archive( $pFileName ) {
993 return preg_match( '/(.*).sql.(zip|tar|tar.gz|tar.bz2)$/', $pFileName );
994 }
995
996 /**
997 * Method get_current_archive_extension()
998 *
999 * Get extension of current Archive.
1000 *
1001 * @param bool $website true|false
1002 * @param bool|string $task true|false|global|site
1003 *
1004 * @return mixed $archiveFormat Format of Archive.
1005 */
1006 public static function get_current_archive_extension( $website = false, $task = false ) {
1007 $useSite = true;
1008 if ( false != $task ) {
1009 if ( 'global' === $task->archiveFormat ) {
1010 $useGlobal = true;
1011 $useSite = false;
1012 } elseif ( '' == $task->archiveFormat || 'site' == $task->archiveFormat ) {
1013 $useGlobal = false;
1014 $useSite = true;
1015 } else {
1016 $archiveFormat = $task->archiveFormat;
1017 $useGlobal = false;
1018 $useSite = false;
1019 }
1020 }
1021
1022 if ( $useSite ) {
1023 if ( false === $website ) {
1024 $useGlobal = true;
1025 } else {
1026 $backupSettings = MainWP_DB_Backup::instance()->get_website_backup_settings( $website->id );
1027 $archiveFormat = $backupSettings->archiveFormat;
1028 $useGlobal = ( 'global' === $archiveFormat );
1029 }
1030 }
1031
1032 if ( $useGlobal ) {
1033 $archiveFormat = get_option( 'mainwp_archiveFormat' );
1034 if ( false === $archiveFormat ) {
1035 $archiveFormat = 'tar.gz';
1036 }
1037 }
1038
1039 return $archiveFormat;
1040 }
1041
1042 /**
1043 * Method get_real_extension()
1044 *
1045 * Get full file extension.
1046 *
1047 * @param mixed $path Path to file.
1048 *
1049 * @return mixed $check|pathinfo()
1050 */
1051 public static function get_real_extension( $path ) {
1052 $checks = array( '.sql.zip', '.sql.tar', '.sql.tar.gz', '.sql.tar.bz2', '.tar.gz', '.tar.bz2' );
1053 foreach ( $checks as $check ) {
1054 if ( self::ends_with( $path, $check ) ) {
1055 return $check;
1056 }
1057 }
1058
1059 return '.' . pathinfo( $path, PATHINFO_EXTENSION );
1060 }
1061
1062
1063 /**
1064 * Method handle_settings_post()
1065 *
1066 * Update Child Site Settings.
1067 *
1068 * @return bool true|false
1069 */
1070 public static function handle_settings_post() {
1071 if ( MainWP_System_Utility::is_admin() ) {
1072 if ( isset( $_POST['submit'] ) && isset( $_POST['wp_nonce'] ) && wp_verify_nonce( sanitize_key( $_POST['wp_nonce'] ), 'Settings' ) ) {
1073 if ( isset( $_POST['mainwp_options_backupOnServer'] ) && 0 < $_POST['mainwp_options_backupOnServer'] ) {
1074 MainWP_Utility::update_option( 'mainwp_backupsOnServer', ( isset( $_POST['mainwp_options_backupOnServer'] ) ? intval( $_POST['mainwp_options_backupOnServer'] ) : 1 ) );
1075 }
1076 if ( isset( $_POST['mainwp_options_maximumFileDescriptors'] ) && - 1 < $_POST['mainwp_options_maximumFileDescriptors'] ) {
1077 MainWP_Utility::update_option( 'mainwp_maximumFileDescriptors', ( isset( $_POST['mainwp_options_maximumFileDescriptors'] ) ? intval( $_POST['mainwp_options_maximumFileDescriptors'] ) : 1 ) );
1078 }
1079 MainWP_Utility::update_option( 'mainwp_maximumFileDescriptorsAuto', ( ! isset( $_POST['mainwp_maximumFileDescriptorsAuto'] ) ? 0 : 1 ) );
1080 if ( ! empty( $_POST['mainwp_options_backupOnExternalSources'] ) && 0 <= $_POST['mainwp_options_backupOnExternalSources'] ) {
1081 MainWP_Utility::update_option( 'mainwp_backupOnExternalSources', ( isset( $_POST['mainwp_options_backupOnExternalSources'] ) ? intval( $_POST['mainwp_options_backupOnExternalSources'] ) : 1 ) );
1082 }
1083 MainWP_Utility::update_option( 'mainwp_archiveFormat', ( isset( $_POST['mainwp_archiveFormat'] ) ? sanitize_text_field( wp_unslash( $_POST['mainwp_archiveFormat'] ) ) : '' ) );
1084
1085 $old_primaryBackup = get_option( 'mainwp_primaryBackup' );
1086 $old_enableLegacyBackup = get_option( 'mainwp_enableLegacyBackupFeature' );
1087 $updated_enableLegacyBackup = false;
1088
1089 if ( isset( $_POST['mainwp_primaryBackup'] ) ) {
1090 if ( ! empty( $_POST['mainwp_primaryBackup'] ) ) { // not default backup method.
1091 MainWP_Utility::update_option( 'mainwp_notificationOnBackupFail', 0 );
1092 MainWP_Utility::update_option( 'mainwp_notificationOnBackupStart', 0 );
1093 MainWP_Utility::update_option( 'mainwp_chunkedBackupTasks', 0 );
1094 if ( empty( $old_primaryBackup ) ) {
1095 MainWP_Utility::update_option( 'mainwp_enableLegacyBackupFeature', 0 );
1096 $updated_enableLegacyBackup = true;
1097 }
1098 }
1099 MainWP_Utility::update_option( 'mainwp_primaryBackup', sanitize_text_field( wp_unslash( $_POST['mainwp_primaryBackup'] ) ) );
1100 }
1101
1102 if ( ! isset( $_POST['mainwp_primaryBackup'] ) || empty( $_POST['mainwp_primaryBackup'] ) ) {
1103 MainWP_Utility::update_option( 'mainwp_options_loadFilesBeforeZip', ( ! isset( $_POST['mainwp_options_loadFilesBeforeZip'] ) ? 0 : 1 ) );
1104 MainWP_Utility::update_option( 'mainwp_notificationOnBackupFail', ( ! isset( $_POST['mainwp_options_notificationOnBackupFail'] ) ? 0 : 1 ) );
1105 MainWP_Utility::update_option( 'mainwp_notificationOnBackupStart', ( ! isset( $_POST['mainwp_options_notificationOnBackupStart'] ) ? 0 : 1 ) );
1106 MainWP_Utility::update_option( 'mainwp_chunkedBackupTasks', ( ! isset( $_POST['mainwp_options_chunkedBackupTasks'] ) ? 0 : 1 ) );
1107 }
1108
1109 $enableLegacyBackup = ( isset( $_POST['mainwp_options_enableLegacyBackupFeature'] ) && ! empty( $_POST['mainwp_options_enableLegacyBackupFeature'] ) ) ? 1 : 0;
1110 if ( $enableLegacyBackup && empty( $old_enableLegacyBackup ) ) {
1111 MainWP_Utility::update_option( 'mainwp_primaryBackup', '' );
1112 }
1113
1114 if ( ! $updated_enableLegacyBackup ) {
1115 MainWP_Utility::update_option( 'mainwp_enableLegacyBackupFeature', $enableLegacyBackup );
1116 }
1117 return true;
1118 }
1119 }
1120 return false;
1121 }
1122
1123 /**
1124 * Method backup_get_file_size()
1125 *
1126 * Get file size.
1127 *
1128 * @param mixed $pFile Backup file.
1129 *
1130 * @return false|int
1131 */
1132 public static function backup_get_file_size( $pFile ) {
1133 $dir = MainWP_System_Utility::get_mainwp_specific_dir();
1134
1135 if ( stristr( $pFile, $dir ) && file_exists( $pFile ) ) {
1136 return @filesize( $pFile );
1137 }
1138
1139 return 0;
1140 }
1141
1142 }
1143