PluginProbe ʕ •ᴥ•ʔ
Backup Migration / 2.1.7
Backup Migration v2.1.7
2.1.7 2.1.6 2.1.5.2 trunk 1.3.0 1.3.1 1.3.2 1.3.3 1.3.4 1.3.5 1.3.6 1.3.7 1.3.8 1.3.9 1.4.0 1.4.1 1.4.2 1.4.3 1.4.4 1.4.5 1.4.6 1.4.6.1 1.4.7 1.4.8 1.4.9 1.4.9.1 2.0.0 2.1.0 2.1.1 2.1.2 2.1.3 2.1.4 2.1.5 2.1.5.1
backup-backup / includes / ajax.php
backup-backup / includes Last commit date
banner 2 weeks ago bodies 2 weeks ago check 2 weeks ago cli 2 weeks ago cron 2 weeks ago dashboard 2 weeks ago database 2 weeks ago external 2 weeks ago extracter 2 weeks ago htaccess 2 weeks ago notices 2 weeks ago progress 2 weeks ago scanner 2 weeks ago services 2 weeks ago staging 2 weeks ago traits 2 weeks ago uploader 2 weeks ago vendor 2 weeks ago zipper 2 weeks ago .htaccess 2 weeks ago activation.php 2 weeks ago ajax.php 2 weeks ago ajax_offline.php 2 weeks ago analyst.php 2 weeks ago backup-process.php 2 weeks ago class-backup-method-mananger.php 2 weeks ago cli-handler.php 2 weeks ago compatibility.php 2 weeks ago config.php 2 weeks ago config_v2.php 2 weeks ago constants.php 2 weeks ago file-explorer.php 2 weeks ago initializer.php 2 weeks ago logger.php 2 weeks ago offline.php 2 weeks ago
ajax.php
6266 lines
1 <?php
2
3 // Namespace
4 namespace BMI\Plugin;
5
6 // Exit on direct access
7 if (!defined('ABSPATH')) exit;
8
9 // Uses
10 use BMI\Plugin\Backup_Migration_Plugin as BMP;
11 use BMI\Plugin\BMI_Logger as Logger;
12 use BMI\Plugin\Checker\BMI_Checker as Checker;
13 use BMI\Plugin\Checker\System_Info as SI;
14 use BMI\Plugin\CRON\BMI_Crons as Crons;
15 use BMI\Plugin\Dashboard as Dashboard;
16 use BMI\Plugin\Extracter\BMI_Extracter as Extracter;
17 use BMI\Plugin\Progress\BMI_MigrationProgress as MigrationProgress;
18 use BMI\Plugin\Progress\BMI_ZipProgress as Progress;
19 use BMI\Plugin\Progress\BMI_StagingProgress as StagingProgress;
20 use BMI\Plugin\Scanner\BMI_BackupsScanner as Backups;
21 use BMI\Plugin\Scanner\BMI_FileScanner as Scanner;
22 use BMI\Plugin\Zipper\BMI_Zipper as Zipper;
23 use BMI\Plugin\PHPCLI\Checker as PHPCLICheck;
24 use BMI\Plugin\External\BMI_External_Storage as ExternalStorage;
25 use BMI\Plugin\External\BMI_External_Storage_Premium as ExternalStoragePremium;
26 use BMI\Plugin\Staging\BMI_Staging_TasteWP as StagingTasteWP;
27 use BMI\Plugin\Staging\BMI_StagingLocal as StagingLocal;
28 use BMI\Plugin\Heart\BMI_Backup_Heart as Bypasser;
29 use BMI\Plugin\Staging\BMI_Staging as Staging;
30 use BMI\Plugin\Checker\Compatibility as Compatibility;
31 use BMI\Plugin\External\BMI_External_BackupBliss as BackupBliss;
32 use BMI\Plugin\BMI_File_Explorer as File_Explorer;
33 use BMI\Plugin\External\BMI_External_Dropbox as Dropbox;
34 use BMI\Plugin\External\BMI_External_GDrive as GDrive;
35 use BMI\Plugin\External\BMI_External_FTP as FTP;
36 use BMI\Plugin\External\BMI_External_S3 as S3;
37
38 /**
39 * Ajax Handler for BMI
40 */
41 class BMI_Ajax {
42
43 private $gdrive_access_token = false;
44 public $post;
45 public $zip_progress;
46 public $migration_progress;
47 public $lock_cli;
48 public $lastCurlCode;
49
50 public $total_size_for_backup = 0;
51 public $total_size_for_backup_in_mb = 0;
52 public $total_excluded_size_for_backup = 0;
53 public $ignoredDirectoriesSize = 0;
54
55 public function __construct($initializedWithCLI = false) {
56
57 // Initialize CRON if wasn't done earlier
58 $this->shareDomainForAutoCron();
59
60 // Return if it's not post
61 if (empty($_POST)) {
62 $this->post = ['f' => 'unknown_method'];
63 return;
64 }
65
66 // Sanitize User Input
67 $this->post = BMP::sanitize($_POST);
68
69 if (!isset($this->post['f'])) {
70 if (is_object($this->post) || is_array($this->post)) $this->post['f'] = 'unknown_method';
71 else $this->post = ['f' => 'unknown_method'];
72 }
73
74 // Check nonce for non PHP CLI usage (ignore while self requested via previously verified nonce to PHP CLI)
75 if (check_ajax_referer('backup-migration-ajax', 'nonce', false) === false && $initializedWithCLI === false) {
76 return wp_send_json_error(['reason' => 'not authorized request']);
77 }
78
79 // Log Handler Call (Verbose)
80 Logger::debug(__("Running POST Function: ", 'backup-backup') . $this->post['f']);
81
82 // Create backup folder
83 if (!file_exists(BMI_BACKUPS)) {
84 mkdir(BMI_BACKUPS, 0755, true);
85 }
86
87 // Create background logs file
88 $backgroundLogsPath = BMI_CONFIG_DIR . DIRECTORY_SEPARATOR . 'background-errors.' . BMI_LOGS_SUFFIX . '.log';
89 if (!file_exists($backgroundLogsPath)) {
90 @touch($backgroundLogsPath);
91 }
92
93 if (!isset($this->post['f'])) {
94 return;
95 }
96
97 // Handle User Request If Known And Sanitize Response
98 if ($this->post['f'] == 'scan-directory') {
99 BMP::res($this->dirSize());
100 } elseif ($this->post['f'] == 'create-backup') {
101 BMP::res($this->prepareAndMakeBackup());
102 } elseif ($this->post['f'] == 'reset-latest') {
103 BMP::res($this->resetLatestLogs());
104 } elseif ($this->post['f'] == 'get-current-backups') {
105 BMP::res($this->getBackupsList());
106 } elseif ($this->post['f'] == 'restore-backup') {
107 BMP::res($this->restoreBackup());
108 } elseif ($this->post['f'] == 'is-running-backup') {
109 BMP::res($this->isRunningBackup());
110 } elseif ($this->post['f'] == 'stop-backup') {
111 BMP::res($this->stopBackup());
112 } elseif ($this->post['f'] == 'download-backup') {
113 BMP::res($this->handleQuickMigration());
114 } elseif ($this->post['f'] == 'migration-locked') {
115 BMP::res($this->isMigrationLocked());
116 } elseif ($this->post['f'] == 'upload-backup') {
117 BMP::res($this->handleChunkUpload());
118 } elseif ($this->post['f'] == 'delete-backup') {
119 BMP::res($this->removeBackupFile());
120 } elseif ($this->post['f'] == 'save-storage') {
121 BMP::res($this->saveStorageConfig());
122 } elseif ($this->post['f'] == 'save-file-config') {
123 BMP::res($this->saveFilesConfig());
124 } elseif ($this->post['f'] == 'save-other-options') {
125 BMP::res($this->saveOtherOptions());
126 } elseif ($this->post['f'] == 'store-config') {
127 BMP::res($this->saveStorageTypeConfig());
128 } elseif ($this->post['f'] == 'unlock-backup') {
129 BMP::res($this->toggleBackupLock(true));
130 } elseif ($this->post['f'] == 'lock-backup') {
131 BMP::res($this->toggleBackupLock(false));
132 } elseif ($this->post['f'] == 'get-dynamic-names') {
133 BMP::res($this->getDynamicNames());
134 } elseif ($this->post['f'] == 'reset-configuration') {
135 BMP::res($this->resetConfiguration());
136 } elseif ($this->post['f'] == 'get-site-data') {
137 BMP::res($this->getSiteData());
138 } elseif ($this->post['f'] == 'send-test-mail') {
139 BMP::res($this->sendTestMail());
140 } elseif ($this->post['f'] == 'calculate-cron') {
141 BMP::res($this->calculateCron());
142 } elseif ($this->post['f'] == 'dismiss-error-notice') {
143 BMP::res($this->dismissErrorNotice());
144 } elseif ($this->post['f'] == 'fix_uname_issues') {
145 BMP::res($this->fixUnameFunction());
146 } elseif ($this->post['f'] == 'revert_uname_issues') {
147 BMP::res($this->revertUnameProcess());
148 } elseif ($this->post['f'] == 'continue_restore_process') {
149 BMP::res($this->continueRestoreProcess());
150 } elseif ($this->post['f'] == 'htaccess-litespeed') {
151 BMP::res($this->fixLitespeed());
152 } elseif ($this->post['f'] == 'force-backup-to-stop') {
153 BMP::res($this->forceBackupToStop());
154 } elseif ($this->post['f'] == 'force-restore-to-stop') {
155 BMP::res($this->forceRestoreToStop());
156 } elseif ($this->post['f'] == 'staging-local-name') {
157 BMP::res($this->checkStagingLocalName());
158 } elseif ($this->post['f'] == 'staging-start-local-creation') {
159 BMP::res($this->startLocalStagingCreation());
160 } elseif ($this->post['f'] == 'staging-local-creation-process') {
161 BMP::res($this->localStagingCreationProcess());
162 } elseif ($this->post['f'] == 'staging-tastewp-creation-process') {
163 BMP::res($this->tastewpStagingCreation());
164 } elseif ($this->post['f'] == 'staging-rename-display') {
165 BMP::res($this->stagingRename());
166 } elseif ($this->post['f'] == 'staging-prepare-login') {
167 BMP::res($this->stagingPrepareLogin());
168 } elseif ($this->post['f'] == 'staging-delete-permanently') {
169 BMP::res($this->stagingDelete());
170 } elseif ($this->post['f'] == 'staging-get-updated-list') {
171 BMP::res($this->stagingSitesGetList());
172 } elseif ($this->post['f'] == 'send-troubleshooting-logs') {
173 BMP::res($this->sendTroubleshootingDetails());
174 } elseif ($this->post['f'] == 'log-sharing-details') {
175 BMP::res($this->logSharing());
176 } elseif ($this->post['f'] == 'get-latest-backup') {
177 BMP::res($this->getLatestBackupFile());
178 } elseif ($this->post['f'] == 'front-end-ajax-error') {
179 BMP::res($this->frontEndAjaxError());
180 } elseif ($this->post['f'] == 'backup-browser-method') {
181 BMP::res($this->backupBrowserMethodHandler());
182 } elseif ($this->post['f'] == 'debugging') {
183 BMP::res($this->debugging());
184 } elseif ($this->post['f'] == 'check-disk-space') {
185 BMP::res($this->checkDiskSpace());
186 } elseif ($this->post['f'] == 'check-comptability') {
187 BMP::res($this->checkCompatibility());
188 } elseif ($this->post['f'] == 'clean-up-after-error'){
189 BMP::res($this->cleanUpAfterError());
190 } elseif ($this->post['f'] == 'clicked-on-plugin-review') {
191 BMP::res($this->clickedOnPluginReview());
192 } elseif ($this->post['f'] == 'keep-dropbox-connection') {
193 BMP::res($this->keepDropboxToken());
194 } elseif ($this->post['f'] == 'get-dropbox-token') {
195 BMP::res($this->getDropboxToken());
196 } elseif ($this->post['f'] == 'disconnect-dropbox') {
197 BMP::res($this->disconnectDropboxToken());
198 } elseif ($this->post['f'] == 'verify-dropbox-connection') {
199 BMP::res($this->verifyDropboxConnection());
200 } elseif ($this->post['f'] == 'download-dropbox-backup') {
201 BMP::res($this->downloadCloudBackupV2());
202 } elseif ($this->post['f'] == 'dismiss-dropbox-notice') {
203 BMP::res($this->dismissDropboxNotice());
204 } elseif ($this->post['f'] == 'get-gdrive-token') {
205 BMP::res($this->getGDriveToken());
206 } elseif ($this->post['f'] == 'keep-gdrive-connection') {
207 BMP::res($this->keepGDriveToken());
208 } elseif ($this->post['f'] == 'verify-gdrive-connection') {
209 BMP::res($this->verifyGDriveConnection());
210 } elseif ($this->post['f'] == 'disconnect-gdrive') {
211 BMP::res($this->disconnectGDriveToken());
212 } elseif ($this->post['f'] == 'get-ftp-config') {
213 BMP::res($this->connectToConfig());
214 } elseif ($this->post['f'] == 'disconnect-ftp') {
215 BMP::res($this->disconnectFtp());
216 } elseif ($this->post['f'] == 'save-aws-config') {
217 BMP::res($this->saveAWSConfig());
218 } elseif ($this->post['f'] == 'disconnect-aws') {
219 BMP::res($this->disconnectAWS());
220 } elseif ($this->post['f'] == 'verify-aws-connection') {
221 BMP::res($this->verifyAWSConnection());
222 } elseif ($this->post['f'] == 'save-wasabi-config') {
223 BMP::res($this->saveWasabiConfig());
224 } elseif ($this->post['f'] == 'disconnect-wasabi') {
225 BMP::res($this->disconnectWasabi());
226 } elseif ($this->post['f'] == 'verify-wasabi-connection') {
227 BMP::res($this->verifyWasabiConnection());
228 } elseif ($this->post['f'] == 'manually-enqueue-upload') {
229 BMP::res($this->manuallyEnqueueUpload());
230 } elseif ($this->post['f'] == 'validate-backup-password') {
231 BMP::res($this->validateBackupPassword());
232 } elseif ($this->post['f'] == 'resync-with-ping-server') {
233 BMP::res($this->resyncWithPingServer());
234 }
235 elseif (substr($this->post['f'], 0, 3) === "bb-") {
236 require_once BMI_INCLUDES . '/external/backupbliss.php';
237 $backupBliss = new BackupBliss();
238 BMP::res($backupBliss->process(substr($this->post['f'], 3), $this->post));
239 } elseif ($this->post['f'] == 'check-not-uploaded-backups') {
240 do_action('bmi_ajax_offline', $this->post);
241 } elseif($this->post['f'] == 'download-cloud-backup') {
242 if (isset($this->post['storage']) && ($this->post['storage'] == 'backupbliss' ||
243 $this->post['storage'] == 'googledrive' || $this->post['storage'] == 'ftp'))
244 BMP::res($this->downloadCloudBackup());
245 //Forward it to premium plugin for other cloud downloads
246 elseif (has_action('bmi_premium_ajax')) {
247 do_action('bmi_premium_ajax', $this->post);
248 }
249 }
250
251
252 //If none of the action matches it executes premium ajax if it exists
253 elseif (has_action('bmi_premium_ajax')) {
254 do_action('bmi_premium_ajax', $this->post);
255 }
256
257 }
258
259 public function validateBackupPassword() {
260 $backupName = isset($this->post['backupName']) ? $this->post['backupName'] : false;
261 $password = isset($this->post['password']) ? $this->post['password'] : false;
262
263 if (!$backupName || !$password) {
264 return ['status' => 'msg', 'why' => 'Missing backup file or password.', 'level' => 'error'];
265 }
266
267 $backupFile = BMI_BACKUPS . DIRECTORY_SEPARATOR . $backupName;
268
269 if (!file_exists($backupFile)) {
270 return ['status' => 'msg', 'why' => 'Backup file not found.', 'level' => 'error'];
271 }
272
273 try {
274 require_once BMI_INCLUDES . '/zipper/zipping.php';
275 $isValid = Zipper::validateZipPassword($backupFile, $password);
276 } catch (\Exception $e) {
277 return ['status' => 'msg', 'why' => 'Error validating password: ' . $e->getMessage(), 'level' => 'error'];
278 }
279
280
281 if ($isValid) {
282 return ['status' => 'success'];
283 } else {
284 return ['status' => 'msg', 'why' => 'Incorrect password. Please try again.', 'level' => 'error'];
285 }
286 }
287
288 /**
289 * getFtpConfig
290 *
291 * @return string[] Token
292 */
293 private function connectToConfig()
294 {
295 // Safely retrieve POST values
296 $host = isset($this->post['bmip-ftp-host']) ? trim($this->post['bmip-ftp-host']) : false;
297 $dir = isset($this->post['bmip-ftp-backup-dir']) ? trim($this->post['bmip-ftp-backup-dir']) : '';
298 $port = isset($this->post['bmip-ftp-host-port']) && is_numeric($this->post['bmip-ftp-host-port']) ? (int)trim($this->post['bmip-ftp-host-port']) : 21;
299 $password = isset($this->post['bmip-ftp-password']) ? trim($this->post['bmip-ftp-password']) : false;
300 $userName = isset($this->post['bmip-ftp-username']) ? trim($this->post['bmip-ftp-username']) : false;
301
302 if (!$host) {
303 return ['status' => 'error', 'msg' => 'FTP Host is required and cannot be empty', 'errors' => 1];
304 }
305
306 if (!$userName) {
307 return ['status' => 'error', 'msg' => 'FTP Username is required and cannot be empty', 'errors' => 1];
308 }
309
310 if (!$password) {
311 return ['status' => 'error', 'msg' => 'FTP Password is required and cannot be empty', 'errors' => 1];
312 }
313
314 if ($dir[0] !== '/') {
315 $dir = '/' . $dir;
316 }
317
318
319 if (!function_exists('ftp_connect')) {
320 return [
321 'msg' => "FTP functions are not available on your server. Please make sure the FTP extension for PHP is installed and enabled.",
322 'status' => 'error',
323 'errors' => 1
324 ];
325 }
326
327 $ftp = ftp_connect($host, $port, 10);
328 if (!$ftp) {
329 return [
330 'msg' => "Could not connect to FTP server at $host on port $port. Please check the hostname and port.",
331 'status' => 'error',
332 'errors' => 1
333 ];
334 }
335
336 $login_result = @ftp_login($ftp, $userName, $password);
337 if (!$login_result) {
338 ftp_close($ftp);
339 return [
340 'msg' => 'Invalid FTP username or password. Please check your credentials.',
341 'status' => 'error',
342 'errors' => 1
343 ];
344 }
345
346 ftp_pasv($ftp, true);
347
348 // Try to change to the directory or create it if not found
349 if (!@ftp_chdir($ftp, $dir)) {
350 if (!@ftp_mkdir($ftp, $dir)) {
351 ftp_close($ftp);
352 return [
353 'msg' => "The backup directory '$dir' does not exist and could not be created. Please ensure you have the correct permissions to create directories on the FTP server.",
354 'status' => 'error',
355 'errors' => 1
356 ];
357 }
358 }
359
360 // Change into the backup directory
361 if (!@ftp_chdir($ftp, $dir)) {
362 ftp_close($ftp);
363 return [
364 'msg' => "Unable to navigate to the backup directory '$dir'. Please ensure it exists and has the correct permissions.",
365 'status' => 'error',
366 'errors' => 1
367 ];
368 }
369
370 // Permission check: Upload and download a temporary file
371 $testFile = 'bmi_ftp_test_' . uniqid() . '.txt';
372 $localTempPath = BMI_TMP . DIRECTORY_SEPARATOR . $testFile;
373 file_put_contents($localTempPath, "Permission check");
374
375 $upload = @ftp_put($ftp, $testFile, $localTempPath, FTP_ASCII);
376 $download = false;
377
378 if ($upload) {
379 // Try to download back to confirm read access
380 $downloadPath = $localTempPath . '_download';
381 $download = @ftp_get($ftp, $downloadPath, $testFile, FTP_ASCII);
382 }
383
384 // Cleanup
385 $delete = @ftp_delete($ftp, $testFile);
386
387 @unlink($localTempPath);
388 if (isset($downloadPath) && file_exists($downloadPath)) {
389 @unlink($downloadPath);
390 }
391
392 if (!$upload) {
393 ftp_close($ftp);
394 return [
395 'msg' => "Connected successfully, but upload permission check failed. Unable to upload files in '$dir'.",
396 'status' => 'error',
397 'errors' => 1
398 ];
399 }
400
401 if (!$download) {
402 ftp_close($ftp);
403 return [
404 'msg' => "Connected successfully, but download permission check failed. Unable to download files from '$dir'.",
405 'status' => 'error',
406 'errors' => 1
407 ];
408 }
409
410 if (!$delete) {
411 ftp_close($ftp);
412 return [
413 'msg' => "Connected successfully, but delete permission check failed. Unable to delete files in '$dir'.",
414 'status' => 'error',
415 'errors' => 1
416 ];
417 }
418
419 // Store the config and close connection
420 Dashboard\bmi_set_config('STORAGE::EXTERNAL::FTP', "true");
421
422 update_option('bmi_pro_ftp_host', $host);
423 update_option('bmi_pro_ftp_username', $userName);
424 update_option('bmi_pro_ftp_backup_dir', $dir);
425 update_option('bmi_pro_ftp_port', $port);
426 update_option('bmi_pro_ftp_password', $password);
427
428 ftp_close($ftp);
429
430 return [
431 'status' => 'success',
432 'errors' => 0
433 ];
434 }
435
436 private function disconnectFtp()
437 {
438 delete_option('bmi_pro_ftp_host');
439 delete_option('bmi_pro_ftp_backup_dir');
440 delete_option('bmi_pro_ftp_port');
441 delete_option('bmi_pro_ftp_username');
442 delete_option('bmi_pro_ftp_password');
443 Dashboard\bmi_set_config('STORAGE::EXTERNAL::FTP', "false");
444 return ['status' => 'success'];
445 }
446
447 private function getDropboxToken()
448 {
449
450 $bytes = random_bytes(36);
451 $token = bin2hex($bytes);
452
453 update_option('bmip_dropbox', $token);
454 return ['token' => $token];
455 }
456
457 private function keepDropboxToken()
458 {
459
460 $receivedToken = $this->post['receivedToken'];
461 $receivedAuthCode = $this->post['receivedClientID'];
462
463 $currentToken = get_option('bmip_dropbox', false);
464
465 if ($currentToken === $receivedToken) {
466
467 update_option('bmip_dropbox_auth_code', $receivedAuthCode);
468 return ['status' => 'success'];
469 } else {
470
471 return ['status' => 'token_mismatch'];
472 }
473 }
474
475 private function verifyDropboxConnection()
476 {
477
478 require_once BMI_INCLUDES . '/external/dropbox.php';
479
480 $dropbox = new Dropbox();
481 return $dropbox->verifyConnection();
482 }
483
484 private function disconnectDropboxToken()
485 {
486 require_once BMI_INCLUDES . '/external/dropbox.php';
487
488 $dropbox = new Dropbox();
489 $dropbox->disconnect();
490 delete_option($dropbox->dropboxAuthCodeOption);
491 delete_option($dropbox->dropboxId);
492 delete_transient($dropbox->dropboxAccessToken);
493 Dashboard\bmi_set_config('STORAGE::EXTERNAL::DROPBOX', 'false');
494
495 return ['status' => 'success'];
496 }
497
498
499 private function downloadCloudBackupV2()
500 {
501
502 require_once BMI_INCLUDES . '/progress/migration.php';
503
504 $secret = isset($this->post['secret']) ? $this->post['secret'] : false;
505 $startRestoreProcess = isset($this->post['startRestoreProcess']) ? $this->post['startRestoreProcess'] : 'true';
506 $lock = BMI_BACKUPS . '/.migration_lock';
507
508 if (file_exists($lock) && (time() - filemtime($lock)) < 1) {
509 $lockContent = file_get_contents($lock);
510 if ($lockContent !== $secret) {
511 return ['status' => 'msg', 'why' => __('Download process is currently running, please wait till it complete.', 'backup-backup'), 'level' => 'warning'];
512 }
513 }
514
515 $externalStorage = null;
516 switch($this->post['f']){
517 case 'download-dropbox-backup':
518 require_once BMI_INCLUDES . '/external/dropbox.php';
519 $externalStorage = new Dropbox();
520 break;
521 default:
522 return ['status' => 'error'];
523 }
524 $fileId = isset($this->post['fileId']) ? $this->post['fileId'] : false; // Required
525 $md5 = isset($this->post['md5']) ? $this->post['md5'] : false; // Required
526 $step = isset($this->post['step']) ? intval($this->post['step']) : 0; // Required
527 $size = isset($this->post['size']) ? intval($this->post['size']) : false;
528 $fileName = isset($this->post['filename']) ? $this->post['filename'] : false;
529 $writePath = isset($this->post['writepath']) ? $this->post['writepath'] : false;
530 $chunkSize = isset($this->post['chunksize']) && (intval($this->post['chunksize']) != 0) ? intval($this->post['chunksize']) : BMP::getAvailableMemoryInBytes() / 4;
531 $migration = new MigrationProgress(($step === 0) ? false : true);
532
533
534 $migration->start();
535
536 if ($step === 0) {
537 $migration->log((__('Backup & Migration version: ', 'backup-backup') . BMI_VERSION));
538 $migration->log(__('Creating lock file', 'backup-backup'));
539 $secret = $this->randomString();
540 file_put_contents($lock, $secret);
541
542 $migration->log('Download intialized', 'INFO');
543 $migration->log('Getting backup details from cloud...', 'STEP');
544
545 $backupDetails = $externalStorage->getFileMeta($fileId);
546 if (!isset($backupDetails['name'])) $backupDetails['name'] = $fileId;
547
548
549 if ($backupDetails == false) {
550 $migration->log('It seem like I was unable to get backup details from cloud.', 'ERROR');
551 $migration->log(__('Unlocking migration', 'backup-backup'), 'INFO');
552 if (file_exists($lock)) @unlink($lock);
553
554 $migration->log('error_during_downloading_backup', 'verbose');
555 $migration->log('#002', 'END-CODE');
556 $migration->end();
557
558 return ['status' => 'error'];
559 }
560
561 $manifest = BMI_BACKUPS . DIRECTORY_SEPARATOR . $md5 . '.json';
562 $manifestContent = $externalStorage->getFileContent($md5 . '.json');
563 if ($manifestContent == false) {
564 $migration->log('It seem like I was unable to get backup manifest from cloud.', 'ERROR');
565 $migration->log(__('Unlocking migration', 'backup-backup'), 'INFO');
566 if (file_exists($lock)) @unlink($lock);
567
568 $migration->log('error_during_downloading_backup', 'verbose');
569 $migration->log('#002', 'END-CODE');
570 $migration->end();
571
572 return ['status' => 'error'];
573 }
574 file_put_contents($manifest, $manifestContent);
575
576
577 $size = intval($backupDetails['size']);
578 $fileName = $backupDetails['name'];
579
580 $migration->log('Backup details received!', 'SUCCESS');
581 $migration->log('Backup original name: ' . $fileName, 'INFO');
582 $migration->log('Starting download process...', 'STEP');
583
584 $availableMemory = BMP::getAvailableMemoryInBytes();
585 $bytesPerRequest = intval($availableMemory / 4);
586
587
588 $migration->log('Single batch will use up to: ' . $bytesPerRequest . ' bytes (~' . intval($bytesPerRequest / 1024 / 1024 / 2) . ' MBs)', 'INFO');
589
590 $fileIterator = 2;
591 $extension = pathinfo($fileName, PATHINFO_EXTENSION);
592 $fileName = pathinfo($fileName, PATHINFO_FILENAME);
593 if ($extension == 'gz') {
594 $fileName = pathinfo($fileName, PATHINFO_FILENAME);
595 $extension = 'tar.gz';
596 }
597
598 $backupDestinationPath = BMI_BACKUPS . DIRECTORY_SEPARATOR . $fileName . '.' . $extension;
599 $finalName = $fileName . '.' . $extension;
600
601 while (file_exists($backupDestinationPath)) {
602 $backupDestinationPath = BMI_BACKUPS . DIRECTORY_SEPARATOR . $fileName . '-' . $fileIterator . '.' . $extension;
603 $fileIterator++;
604 }
605
606 $originalFilename = $finalName;
607
608 $backupDestinationPath .= '.crdownload';
609 } else {
610 $bytesPerRequest = intval($chunkSize);
611 $backupDestinationPath = $writePath;
612 $originalFilename = $fileName;
613 }
614
615 $totalBatches = ceil($size / (256 * 1024 * 4 * intval($bytesPerRequest / 1024 / 1024 / 2)));
616
617 if ($totalBatches <= $step) {
618 $migration->log('Verifying MD5 checksum of downloaded file...', 'STEP');
619
620 rename($backupDestinationPath, str_replace('.crdownload', '', $backupDestinationPath));
621 $backupDestinationPath = str_replace('.crdownload', '', $backupDestinationPath);
622
623 $isDownloadedFileVerified = BMP::verifyFileMd5($backupDestinationPath, $md5);
624 if ($isDownloadedFileVerified) {
625 $migration->log('File MD5 checksum is correct!', 'SUCCESS');
626 } else {
627
628 $migration->log('File MD5 checksum is NOT correct!', 'ERROR');
629 $migration->log('Downloaded file path: ' . $backupDestinationPath, 'ERROR');
630 $migration->log('File exist?: ' . (file_exists($backupDestinationPath) ? "Yes" : "No?"), 'ERROR');
631 $migration->log('For security reasons, I will remove the file and stop the process...', 'ERROR');
632 $migration->log(__('Unlocking migration', 'backup-backup'), 'INFO');
633 if (file_exists($lock)) @unlink($lock);
634
635 $migration->log('error_during_downloading_backup', 'verbose');
636 $migration->log('#002', 'END-CODE');
637 $migration->end();
638
639 if (file_exists($backupDestinationPath)) @unlink($backupDestinationPath);
640 return ['status' => 'error'];
641 }
642
643 $migration->log(__('Unlocking migration', 'backup-backup'), 'INFO');
644 if (file_exists($lock)) @unlink($lock);
645 if ($startRestoreProcess == 'true') {
646 $migration->log('Download process finished!', 'SUCCESS');
647 $migration->log('Requesting restoration process...', 'STEP');
648
649 $migration->log('#205', 'END-CODE');
650 } else {
651 $migration->log('Download process finished!', 'SUCCESS');
652 $migration->log('#206', 'END-CODE');
653 }
654
655 $migration->progress(100);
656 $migration->end();
657
658 return ['status' => 'success', 'finished' => 'true', 'filename' => $originalFilename];
659 } else {
660
661 $chunkSize = 256 * 1024 * 4 * intval($bytesPerRequest / 1024 / 1024 / 2);
662 $startRange = ($step * $chunkSize);
663 if ($step !== 0) $startRange = $startRange + 1;
664 $endRange = (($step + 1) * $chunkSize);
665 if ($endRange > $size) $endRange = $size;
666 $currentRange = $startRange . '-' . $endRange;
667 $percentage = intval(($endRange / $size) * 100);
668
669 $contents = $externalStorage->getFileContent($fileId, $currentRange);
670
671 if ($contents == false) {
672
673 $migration->log('It seem like I was unable to get backup content from cloud.', 'ERROR');
674 $migration->log('For security reasons, I will remove the file (if exist) and stop the process...', 'ERROR');
675 $migration->log(__('Unlocking migration', 'backup-backup'), 'INFO');
676 if (file_exists($lock)) @unlink($lock);
677
678 $migration->log('error_during_downloading_backup', 'verbose');
679 $migration->log('#002', 'END-CODE');
680 $migration->end();
681
682 if (file_exists($backupDestinationPath)) @unlink($backupDestinationPath);
683 return ['status' => 'error'];
684 }
685
686 if ((is_dir(dirname($backupDestinationPath)) && file_exists($backupDestinationPath)) || $step === 0) {
687
688 $backupFile = fopen($backupDestinationPath, 'ab');
689 fwrite($backupFile, $contents);
690 unset($contents);
691 fclose($backupFile);
692 } else {
693
694 $migration->log('File is not writable or directory does not exist.', 'ERROR');
695 $migration->log('File: ' . basename($backupDestinationPath), 'ERROR');
696 $migration->log('Dirname: ' . dirname($backupDestinationPath), 'ERROR');
697 $migration->log('For security reasons, I will remove the file and stop the process...', 'ERROR');
698 $migration->log(__('Unlocking migration', 'backup-backup'), 'INFO');
699 if (file_exists($lock)) @unlink($lock);
700
701 $migration->log('error_during_downloading_backup', 'verbose');
702 $migration->log('#002', 'END-CODE');
703 $migration->end();
704
705 if (file_exists($backupDestinationPath)) @unlink($backupDestinationPath);
706 return ['status' => 'error'];
707 }
708
709 $migration->log('Download progress (' . ($step + 1) . '/' . $totalBatches . '): ' . $endRange . '/' . $size . ' (' . $percentage . '%)', 'INFO');
710 $migration->progress($percentage);
711 $migration->end();
712
713 return [
714 'status' => 'success',
715 'size' => $size,
716 'md5' => $md5,
717 'finished' => 'false',
718 'originalFilename' => $originalFilename,
719 'writepath' => $backupDestinationPath,
720 'chunksize' => $bytesPerRequest,
721 'secret' => $secret
722 ];
723 }
724 }
725
726 /**
727 * getGDriveToken - Generates client sided auth token
728 *
729 * @return string Token
730 */
731 private function getGDriveToken()
732 {
733
734 $bytes = random_bytes(36);
735 $token = bin2hex($bytes);
736 $backupDirectoryPath = $this->post['backupDirectoryPath'];
737 if (!preg_match("/^[a-zA-Z0-9\_\ \-\.]+$/", $backupDirectoryPath)) {
738 return ['status' => 'msg', 'why' => __('Entered directory name does not match allowed characters (Google Drive).', 'backup-backup'), 'level' => 'warning'];
739 }
740
741 if (strlen(trim($backupDirectoryPath)) < 3) {
742 return ['status' => 'msg', 'why' => __('Entered directory name is too short, min 3 characters (Google Drive).', 'backup-backup'), 'level' => 'warning'];
743 }
744
745 if (strlen(trim($backupDirectoryPath)) > 48) {
746 return ['status' => 'msg', 'why' => __('Entered directory name is too long, max 48 characters (Google Drive).', 'backup-backup'), 'level' => 'warning'];
747 }
748
749 update_option('bmi_pro_gd_token', $token);
750 Dashboard\bmi_set_config('STORAGE::EXTERNAL::GDRIVE::DIRNAME', $backupDirectoryPath);
751
752 return [ 'status' => 'success', 'token' => $token];
753 }
754
755 /**
756 * keepGDriveToken - Saves Client Token for GDrive API - BMI API communication
757 *
758 * @return json status
759 */
760 private function keepGDriveToken()
761 {
762
763 $receivedToken = $this->post['receivedToken'];
764 $receivedClientID = $this->post['receivedClientID'];
765
766 $currentToken = get_option('bmi_pro_gd_token', false);
767
768 if ($currentToken === $receivedToken) {
769
770 update_option('bmi_pro_gd_client_id', $receivedClientID);
771 return ['status' => 'success'];
772 } else {
773
774 return ['status' => 'token_mismatch'];
775 }
776 }
777
778 /**
779 * getGDriveConnectionStatus - Returns Connection Status for PHP
780 *
781 * @return boolean true on connected | false on disconnected
782 */
783 public function getGDriveConnectionStatus()
784 {
785
786 $status = $this->verifyGDriveConnection();
787 if (isset($status['result']) && $status['result'] == 'connected') {
788 return true;
789 } else {
790 return false;
791 }
792 }
793
794 /**
795 * verifyGDriveConnection - Checks if the GDrive is still granted and tokens are not expired
796 *
797 * @return json rtoken
798 */
799 private function verifyGDriveConnection()
800 {
801
802 // Install Drive Keys
803 $tempKeyDriveFile = BMI_TMP . DIRECTORY_SEPARATOR . 'driveKeys.php';
804 if (file_exists($tempKeyDriveFile)) {
805
806 $driveKeys = file_get_contents($tempKeyDriveFile);
807
808 if (strpos($driveKeys, "\n") !== false) {
809
810 $lines = explode("\n", $driveKeys);
811
812 if (sizeof($lines) == 4) {
813 $pro_gd_token = substr($lines[1], 2);
814 $pro_gd_client_id = substr($lines[2], 2);
815
816 if (function_exists('wp_load_alloptions')) {
817 wp_load_alloptions(true);
818 }
819 delete_option('bmi_pro_gd_token');
820 delete_option('bmi_pro_gd_client_id');
821 if (function_exists('wp_load_alloptions')) {
822 wp_load_alloptions(true);
823 }
824 update_option('bmi_pro_gd_token', $pro_gd_token);
825 update_option('bmi_pro_gd_client_id', $pro_gd_client_id);
826 }
827 }
828
829 if (strpos(site_url(), 'tastewp') !== false) {
830 if (function_exists('wp_load_alloptions')) {
831 wp_load_alloptions(true);
832 }
833
834 update_option('__tastewp_redirection_performed', true);
835 update_option('auto_smart_tastewp_redirect_performed', 1);
836 update_option('tastewp_auto_activated', true);
837 update_option('__tastewp_sub_requested', true);
838 }
839
840 unlink($tempKeyDriveFile);
841 }
842
843 $baseurl = home_url();
844 if (substr($baseurl, 0, 4) != 'http') {
845 if (is_ssl()) $baseurl = 'https://' . home_url();
846 else $baseurl = 'http://' . home_url();
847 }
848
849 $client_token = get_option('bmi_pro_gd_client_id', '');
850 $site_token = get_option('bmi_pro_gd_token', '');
851
852 if (strlen($site_token) < 60 || strlen($client_token) < 60) {
853 return ['status' => 'success', 'result' => 'disconnected'];
854 }
855
856 $url = 'https://authentication.backupbliss.com/v1/gdrive/verify';
857 $response = wp_remote_post($url, array(
858 'method' => 'POST',
859 'timeout' => 15,
860 'redirection' => 2,
861 'httpversion' => '1.0',
862 'blocking' => true,
863 'body' => array(
864 'client_id' => get_option('bmi_pro_gd_client_id', ''),
865 'site_token' => get_option('bmi_pro_gd_token', ''),
866 'redirect_uri' => $baseurl,
867 'force_refresh' => get_transient('bmip_gd_issue') === 'auth_error' && get_transient('bmi_pro_access_token') !== false
868 )
869 ));
870
871 $res = 'disconnected';
872 if (is_wp_error($response)) {
873 $error_message = $response->get_error_message();
874 Logger::error('[BMI PRO] Something went wrong during GDrive connection verification:' . $error_message);
875 return ['status' => 'error', 'result' => 'disconnected'];
876 } else {
877 $result = json_decode($response['body']);
878 if (isset($result->status)) {
879 if (isset($result->expiration) && isset($result->access_token)) {
880 $expiresInSeconds = intval($result->expiration) - intval(microtime(true));
881 $accessToken = $result->access_token;
882 set_transient('bmi_pro_access_token', $accessToken, $expiresInSeconds);
883 }
884
885 if ($result->status == 'disconnected') {
886 $res = 'disconnected';
887 if (get_transient('bmip_gd_issue') === 'auth_error' && get_transient('bmi_pro_access_token') !== false) {
888 set_transient('bmip_gd_issue', 'auth_error_disconnected');
889 delete_transient('bmi_pro_access_token');
890 }
891 }
892 if ($result->status == 'connected'){
893 $res = 'connected';
894 if (in_array(get_transient('bmip_gd_issue'), ['auth_error', 'auth_error_disconnected'])) delete_transient('bmip_gd_issue');
895 }
896 if ($result->status == 'error') $res = 'disconnected';
897 }
898 return ['status' => 'success', 'result' => $res];
899 }
900 }
901
902 /**
903 * removeGDriveConnection - Removed GDrive connection from BMI API
904 *
905 * @return json rtoken
906 */
907 private function removeGDriveConnection()
908 {
909
910 $baseurl = home_url();
911 if (substr($baseurl, 0, 4) != 'http') {
912 if (is_ssl()) $baseurl = 'https://' . home_url();
913 else $baseurl = 'http://' . home_url();
914 }
915
916 $client_token = get_option('bmi_pro_gd_client_id', '');
917 $site_token = get_option('bmi_pro_gd_token', '');
918
919 if (strlen($site_token) < 60 || strlen($client_token) < 60) {
920 return ['status' => 'success'];
921 }
922
923 $url = 'https://authentication.backupbliss.com/v1/gdrive/disconnect';
924 $response = wp_remote_post($url, array(
925 'method' => 'POST',
926 'timeout' => 15,
927 'redirection' => 2,
928 'httpversion' => '1.0',
929 'blocking' => true,
930 'body' => array(
931 'client_id' => get_option('bmi_pro_gd_client_id', ''),
932 'site_token' => get_option('bmi_pro_gd_token', ''),
933 'redirect_uri' => $baseurl
934 )
935 ));
936
937 if (is_wp_error($response)) {
938 $error_message = $response->get_error_message();
939 Logger::error('[BMI PRO] Something went wrong during GDrive removal process:' . $error_message);
940 return ['status' => 'error'];
941 }
942 }
943
944 /**
945 * disconnectGDriveToken - Removes connection with GDrive API
946 *
947 * @return json status
948 */
949 private function disconnectGDriveToken()
950 {
951
952 $this->removeGDriveConnection();
953 delete_option('bmi_pro_gd_client_id');
954 delete_option('bmi_pro_gd_token');
955 delete_transient('bmi_pro_access_token');
956 Dashboard\bmi_set_config('STORAGE::EXTERNAL::GDRIVE', 'false');
957
958 return ['status' => 'success'];
959 }
960
961 private function dismissDropboxNotice()
962 {
963 update_option('bmip_dropbox_dismiss_issue', true);
964 return ['status' => 'success'];
965 }
966
967 public function saveAWSConfig()
968 {
969 $accessKey = isset($this->post['access-key']) ? $this->post['access-key'] : '';
970 $secretKey = isset($this->post['secret-key']) ? $this->post['secret-key'] : '';
971 $bucket = isset($this->post['bucket']) ? $this->post['bucket'] : '';
972 $sse = isset($this->post['sse']) ? $this->post['sse'] : '';
973 $storageClass = isset($this->post['storage-class']) ? $this->post['storage-class'] : 'STANDARD';
974 $path = isset($this->post['path']) ? $this->post['path'] : '';
975 $path = trim($path, '/');
976 $region = isset($this->post['region']) ? $this->post['region'] : '';
977
978 // VALIDATE INPUTS
979 if (empty($accessKey) || empty($secretKey) || empty($bucket) || empty($region)) {
980 return ['status' => 'error', 'msg' => __('Please fill all the required fields.', 'backup-backup')];
981 }
982
983 if (!preg_match('/^[a-zA-Z0-9-]*$/', $bucket)) {
984 return ['status' => 'error', 'msg' => __('Bucket name can only contain letters, numbers and hyphens.', 'backup-backup')];
985 }
986
987 if (!in_array($storageClass, ['STANDARD', 'STANDARD_IA', 'REDUCED_REDUNDANCY'])) {
988 return ['status' => 'error', 'msg' => __('Invalid storage class.', 'backup-backup')];
989 }
990
991 if (!in_array($sse, ['AES256', ''])) {
992 return ['status' => 'error', 'msg' => __('Invalid server-side encryption.', 'backup-backup')];
993 }
994
995 if (!in_array($region, [
996 'us-east-1','us-east-2','us-west-1','us-west-2','af-south-1','ap-east-1','ap-south-1','ap-northeast-3','ap-northeast-2','ap-southeast-1','ap-southeast-2','ap-northeast-1','ca-central-1','eu-central-1','eu-west-1','eu-west-2','eu-south-1','eu-west-3','eu-north-1','me-south-1','sa-east-1'
997 ])) {
998 return ['status' => 'error', 'msg' => __('Invalid region.', 'backup-backup')];
999 }
1000
1001 // Test the connection
1002 require_once BMI_INCLUDES . '/external/s3.php';
1003 $s3 = new S3('aws');
1004
1005 $testConnection = $s3->testConnection( $accessKey, $secretKey, $bucket, $region, $path, $storageClass, $sse);
1006
1007 if ($testConnection['status'] == 'error') {
1008 return ['status' => 'error', 'msg' => $testConnection['error']];
1009 }
1010
1011 Dashboard\bmi_set_config('STORAGE::EXTERNAL::AWS', 'true');
1012 update_option('bmip_aws_access_key', $accessKey);
1013 update_option('bmip_aws_secret_key', $secretKey);
1014 update_option('bmip_aws_bucket', $bucket);
1015 update_option('bmip_aws_storage_class', $storageClass);
1016 update_option('bmip_aws_path', $path);
1017 update_option('bmip_aws_sse', $sse);
1018 set_transient('bmip_aws_connection_status', true, HOUR_IN_SECONDS);
1019 update_option('bmip_aws_region', $region);
1020
1021 return ['status' => 'success'];
1022 }
1023
1024 public function disconnectAWS()
1025 {
1026
1027 require_once BMI_INCLUDES . '/external/s3.php';
1028 $s3 = new S3('aws');
1029 $s3->disconnect();
1030
1031 return ['status' => 'success'];
1032 }
1033
1034 /**
1035 * Verify AWS S3 connection
1036 *
1037 * @return array
1038 */
1039 private function verifyAWSConnection()
1040 {
1041 require_once BMI_INCLUDES . '/external/s3.php';
1042 $s3 = new S3('aws');
1043 $status = $s3->verifyConnection();
1044 if ($status['result'] == 'connected') {
1045 return [
1046 'status' => 'success',
1047 'result' => 'connected',
1048 'configs' => $s3->retrieveS3Configs(),
1049 ];
1050 }
1051 return [
1052 'status' => 'success',
1053 'result' => 'disconnected',
1054 ];
1055 }
1056
1057 /**
1058 * Save the selected files, directories and tables for restore process
1059 * required post data:
1060 * - access-key
1061 * - secret-key
1062 * - bucket
1063 * - region
1064 * - path (OPTIONAL)
1065 *
1066 *
1067 * @return array{msg: mixed, status: string|array{msg: string, status: string}|array{status: string}}
1068 */
1069 public function saveWasabiConfig()
1070 {
1071 $accessKey = isset($this->post['access-key']) ? $this->post['access-key'] : '';
1072 $secretKey = isset($this->post['secret-key']) ? $this->post['secret-key'] : '';
1073 $bucket = isset($this->post['bucket']) ? $this->post['bucket'] : '';
1074 $path = isset($this->post['path']) ? $this->post['path'] : '';
1075 $path = trim($path, '/');
1076 $region = isset($this->post['region']) ? $this->post['region'] : '';
1077
1078 // VALIDATE INPUTS
1079 if (empty($accessKey) || empty($secretKey) || empty($bucket) || empty($region)) {
1080 return ['status' => 'error', 'msg' => __('Please fill all the required fields.', 'backup-backup')];
1081 }
1082
1083 // Test the connection
1084 require_once BMI_INCLUDES . '/external/s3.php';
1085 $s3 = new S3('wasabi');
1086
1087 $testConnection = $s3->testConnection($accessKey, $secretKey, $bucket, $region, $path);
1088
1089 if ($testConnection['status'] == 'error') {
1090 return ['status' => 'error', 'msg' => $testConnection['error']];
1091 }
1092
1093 Dashboard\bmi_set_config('STORAGE::EXTERNAL::WASABI', 'true');
1094 update_option('bmip_wasabi_access_key', $accessKey);
1095 update_option('bmip_wasabi_secret_key', $secretKey);
1096 update_option('bmip_wasabi_bucket', $bucket);
1097 update_option('bmip_wasabi_path', $path);
1098 update_option('bmip_wasabi_sse', '');
1099 update_option('bmip_wasabi_storage_class', 'STANDARD');
1100 set_transient('bmip_wasabi_connection_status', true, HOUR_IN_SECONDS);
1101 update_option('bmip_wasabi_region', $region);
1102
1103 return ['status' => 'success'];
1104 }
1105
1106 public function disconnectWasabi()
1107 {
1108 require_once BMI_INCLUDES . '/external/s3.php';
1109 $s3 = new S3('wasabi');
1110 $s3->disconnect();
1111
1112 return ['status' => 'success'];
1113 }
1114
1115 private function verifyWasabiConnection()
1116 {
1117 require_once BMI_INCLUDES . '/external/s3.php';
1118 $s3 = new S3('wasabi');
1119 $status = $s3->verifyConnection();
1120 if ($status['result'] == 'connected') {
1121 return [
1122 'status' => 'success',
1123 'result' => 'connected',
1124 'configs' => $s3->retrieveS3Configs(),
1125 ];
1126 }
1127 return [
1128 'status' => 'success',
1129 'result' => 'disconnected',
1130 ];
1131 }
1132
1133 /**
1134 * shareDomainForAutoCron - Allows our API to keep scheduled backups on time
1135 *
1136 * @return array format:{status: string, msg?: string}
1137 *
1138 */
1139 private function shareDomainForAutoCron($force = false)
1140 {
1141 if (!$force && get_transient('bmi_cron_share_attempted')) {
1142 return ['status' => 'success'];
1143 }
1144
1145 $cron_shared = get_option('bmi_cron_new_domain_done', false);
1146 $last_ping_time = (int) get_option('bmi_cron_last_ping_time', 0);
1147
1148 // 2. Overdue Check: Has it been more than 1 hour since the last ping?
1149 $is_ping_overdue = $last_ping_time === 0 || (current_time('timestamp') - $last_ping_time) > (HOUR_IN_SECONDS);
1150
1151 // 3. Early Exit: If already shared, ping is NOT overdue, and we aren't forcing it.
1152 if ($cron_shared && !$is_ping_overdue && !$force) {
1153 return ['status' => 'success'];
1154 }
1155
1156 // 4. Lock the attempt window immediately for the next hour
1157 set_transient('bmi_cron_share_attempted', true, HOUR_IN_SECONDS);
1158
1159 $baseurl = home_url();
1160 if (substr($baseurl, 0, 4) != 'http') {
1161 if (is_ssl()) $baseurl = 'https://' . home_url();
1162 else $baseurl = 'http://' . home_url();
1163 }
1164
1165 $sk = get_option('bmi_sk_keepalive');
1166 if (empty($sk)) {
1167 $sk = wp_generate_password(32, false);
1168 update_option('bmi_sk_keepalive', $sk);
1169 }
1170
1171 $url = 'https://authentication.backupbliss.com/v2/crons/connect';
1172 $response = wp_remote_post($url, array(
1173 'method' => 'POST',
1174 'timeout' => 15,
1175 'redirection' => 2,
1176 'httpversion' => '1.0',
1177 'blocking' => true,
1178 'body' => array('site' => $baseurl, 'sk' => $sk)
1179 ));
1180
1181 // Handle WP_Error (Network issues, DNS failures, etc.)
1182 if (is_wp_error($response)) {
1183 $error_message = $response->get_error_message();
1184 Logger::error('WP_Error occurred during cron domain sharing: ' . $error_message);
1185 return ['status' => 'error', 'msg' => $error_message];
1186 }
1187
1188 $body = wp_remote_retrieve_body($response);
1189 $decoded_response = json_decode($body, true);
1190
1191 // Ensure we actually got valid JSON back before trying to access array keys
1192 if (json_last_error() === JSON_ERROR_NONE && is_array($decoded_response)) {
1193 if (isset($decoded_response['status']) && $decoded_response['status'] === 'success') {
1194 update_option('bmi_cron_new_domain_done', true);
1195 update_option('bmi_cron_last_ping_time', current_time('timestamp'));
1196 delete_option('bmi_cron_site_local_or_unreachable');
1197 return ['status' => 'success'];
1198 }
1199
1200 if (isset($decoded_response['status']) && $decoded_response['status'] === 'error' && isset($decoded_response['message']) && $decoded_response['message'] === 'Handshake failed - Invalid SK or Site unreachable') {
1201 Logger::error('Site is locally hosted or could not be reached by Backup & Migration API for cron verification. Cron jobs might not work as expected.');
1202 update_option('bmi_cron_site_local_or_unreachable', true);
1203 return ['status' => 'error', 'msg' => 'Site is locally hosted or could not be reached by Backup & Migration API for cron verification. Cron jobs might not work as expected.'];
1204 }
1205 $responseMsg = isset($decoded_response['message']) ? $decoded_response['message'] : 'Unknown error from BackupBliss API.';
1206 Logger::error('Cron API responded with: ' . $responseMsg);
1207 return ['status' => 'error', 'msg' => $responseMsg];
1208 }
1209
1210 // Fallback for malformed or non-JSON responses (e.g., 500 Server Error HTML page)
1211 $responseMsg = wp_remote_retrieve_response_message($response) ?: 'Unknown or malformed response from cron server.';
1212 Logger::error('Malformed response message: ' . $responseMsg);
1213 return ['status' => 'error', 'msg' => $responseMsg];
1214 }
1215
1216 /**
1217 * randomString - Generates "random" string
1218 *
1219 * @return string "random"
1220 */
1221 private function randomString($length = 64)
1222 {
1223
1224 $chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
1225 $str = "";
1226
1227 for ($i = 0; $i < $length; ++$i) {
1228
1229 $str .= $chars[mt_rand(0, strlen($chars) - 1)];
1230 }
1231
1232 return $str;
1233 }
1234
1235 /**
1236 * downloadCloudBackup - Downloads Cloud Backup to Local Storage
1237 *
1238 * @return json status
1239 */
1240 private function downloadCloudBackup()
1241 {
1242
1243 $secret = false;
1244 if (isset($this->post['secret'])) $secret = $this->post['secret'];
1245
1246 $lock = BMI_BACKUPS . '/.migration_lock';
1247 if (file_exists($lock) && (time() - filemtime($lock)) < 1) {
1248 $lockContent = file_get_contents($lock);
1249 if ($lockContent !== $secret) {
1250 return ['status' => 'msg', 'why' => __('Download process is currently running, please wait till it complete.', 'backup-backup'), 'level' => 'warning'];
1251 }
1252 }
1253
1254 require_once BMI_INCLUDES . '/progress/migration.php';
1255
1256 $step = intval($this->post['step']);
1257 $storage = $this->post['storage'];
1258 $startRestoreProcess = isset($this->post['startRestoreProcess']) ? $this->post['startRestoreProcess'] : 'true';
1259
1260 $clearFile = ($step === 0) ? false : true;
1261 $migration = new MigrationProgress($clearFile);
1262 $migration->start();
1263
1264 if ($storage == 'backupbliss') {
1265
1266 require_once BMI_INCLUDES . '/external/backupbliss.php';
1267 $backupbliss = new BackupBliss();
1268
1269 $backupDetails = false;
1270 $fileId = $this->post['fileId'];
1271
1272 if ($step === 0 || (!isset($this->post['size']) || $this->post['size'] == false || !is_numeric($this->post['size']))) {
1273
1274 $migration->log((__('Backup & Migration version: ', 'backup-backup') . BMI_VERSION));
1275 $migration->log(__('Creating lock file', 'backup-backup'));
1276 $secret = $this->randomString();
1277 file_put_contents($lock, $secret);
1278
1279 $migration->log('Download intialized', 'INFO');
1280 $migration->log('Getting backup details from BackupBliss...', 'STEP');
1281 $backupDetails = $backupbliss->getFileDetailByName($fileId);
1282
1283 if (!$backupDetails) {
1284
1285 $migration->log("Couldn't fetch backup details from cloud.", 'ERROR');
1286 $migration->log(__('Unlocking migration', 'backup-backup'), 'INFO');
1287 if (file_exists($lock)) @unlink($lock);
1288
1289 $migration->log('error_during_downloading_backup', 'verbose');
1290 $migration->log('#002', 'END-CODE');
1291 $migration->end();
1292
1293 return ['status' => 'error'];
1294 }
1295
1296 $size = intval($backupDetails['size']);
1297 $originalFilename = $backupDetails['name'];
1298
1299 $migration->log('Backup details received!', 'SUCCESS');
1300 $migration->log('Backup original name: ' . $originalFilename, 'INFO');
1301 $migration->log('Starting download process...', 'STEP');
1302
1303 $availableMemory = BMP::getAvailableMemoryInBytes();
1304 $bytesPerRequest = intval($availableMemory / 4);
1305
1306 $migration->log('Single batch will use up to: ' . $bytesPerRequest . ' bytes (~' . intval($bytesPerRequest / 1024 / 1024 / 2) . ' MBs)', 'INFO');
1307
1308 $fileIterator = 2;
1309 $originalFilenameInfo = pathinfo($originalFilename);
1310 $extension = $originalFilenameInfo['extension'];
1311 $originalFilename = $originalFilenameInfo['filename'];
1312 if ($originalFilenameInfo['extension'] == 'gz') {
1313 $originalFilename = pathinfo($originalFilename, PATHINFO_FILENAME);
1314 $extension = 'tar.gz';
1315 }
1316
1317 $backupDestinationPath = BMI_BACKUPS . DIRECTORY_SEPARATOR . $originalFilename . '.' . $extension;
1318 $finalName = $originalFilename . '.' . $extension;
1319
1320 while (file_exists($backupDestinationPath)) {
1321 $backupDestinationPath = BMI_BACKUPS . DIRECTORY_SEPARATOR . $originalFilename . '-' . $fileIterator . '.' . $extension;
1322 $finalName = $originalFilename . '-' . $fileIterator . '.' . $extension;
1323 $fileIterator++;
1324 }
1325
1326 $originalFilename = $finalName;
1327
1328 $backupDestinationPath .= '.crdownload';
1329
1330 } else {
1331
1332 $size = intval($this->post['size']);
1333 $originalFilename = $this->post['filename'];
1334 $backupDestinationPath = $this->post['writepath'];
1335 $bytesPerRequest = intval($this->post['chunksize']);
1336 }
1337
1338 $md5 = $this->post['md5'];
1339
1340 $totalBatches = ceil($size / (256 * 1024 * 4 * intval($bytesPerRequest / 1024 / 1024 / 2)));
1341
1342 if ($totalBatches <= $step) {
1343
1344 $migration->log('Download process finished!', 'SUCCESS');
1345 $migration->log('Verifying MD5 checksum of downloaded file...', 'STEP');
1346
1347 rename($backupDestinationPath, str_replace('.crdownload', '', $backupDestinationPath));
1348 $backupDestinationPath = str_replace('.crdownload', '', $backupDestinationPath);
1349
1350
1351 $isDownloadedFileVerified = BMP::verifyFileMd5($backupDestinationPath, $md5);
1352 if ($isDownloadedFileVerified) {
1353 $migration->log('File MD5 checksum is correct!', 'SUCCESS');
1354 } else {
1355
1356 $migration->log('File MD5 checksum is NOT correct!', 'ERROR');
1357 $migration->log('Downloaded file path: ' . $backupDestinationPath, 'ERROR');
1358 $migration->log('File exist?: ' . (file_exists($backupDestinationPath) ? "Yes" : "No?"), 'ERROR');
1359 $migration->log('For security reasons, I will remove the file and stop the process...', 'ERROR');
1360 $migration->log(__('Unlocking migration', 'backup-backup'), 'INFO');
1361 if (file_exists($lock)) @unlink($lock);
1362
1363 $migration->log('error_during_downloading_backup', 'verbose');
1364 $migration->log('#002', 'END-CODE');
1365 $migration->end();
1366
1367 if (file_exists($backupDestinationPath)) @unlink($backupDestinationPath);
1368 return ['status' => 'error'];
1369 }
1370
1371 $migration->log(__('Unlocking migration', 'backup-backup'), 'INFO');
1372 if (file_exists($lock)) @unlink($lock);
1373 if ($startRestoreProcess == 'true') {
1374 $migration->log('Requesting restoration process...', 'STEP');
1375
1376 $migration->log('#205', 'END-CODE');
1377 } else {
1378 $migration->log('Download process finished!', 'SUCCESS');
1379 $migration->log('#206', 'END-CODE');
1380 }
1381 $migration->progress(100);
1382 $migration->end();
1383
1384 return ['status' => 'success', 'finished' => 'true', 'filename' => $originalFilename];
1385 } else {
1386
1387 $chunkSize = 256 * 1024 * 4 * intval($bytesPerRequest / 1024 / 1024 / 2);
1388 $startRange = ($step * $chunkSize);
1389 if ($step !== 0) $startRange = $startRange + 1;
1390 $endRange = (($step + 1) * $chunkSize);
1391 if ($endRange > $size) $endRange = $size;
1392 $percentage = intval(($endRange / $size) * 100);
1393
1394 $data = $backupbliss->getFile($fileId, $startRange, $endRange);
1395
1396 if (!$data["file_detail"] || !$data["file_data"]) {
1397
1398 $migration->log("Couldn't fetch backup file from cloud.", 'ERROR');
1399 $migration->log('For security reasons, I will remove the file (if exist) and stop the process...', 'ERROR');
1400 $migration->log(__('Unlocking migration', 'backup-backup'), 'INFO');
1401 if (file_exists($lock)) @unlink($lock);
1402
1403 $migration->log('error_during_downloading_backup', 'verbose');
1404 $migration->log('#002', 'END-CODE');
1405 $migration->end();
1406
1407 if (file_exists($backupDestinationPath)) @unlink($backupDestinationPath);
1408 return ['status' => 'error'];
1409 }
1410
1411 if ((is_dir(dirname($backupDestinationPath)) && file_exists($backupDestinationPath)) || $step === 0) {
1412
1413 $backupFile = fopen($backupDestinationPath, 'ab');
1414 fwrite($backupFile, $data['file_data']);
1415 fclose($backupFile);
1416 } else {
1417
1418 $migration->log('File is not writable or directory does not exist.', 'ERROR');
1419 $migration->log('File: ' . basename($backupDestinationPath), 'ERROR');
1420 $migration->log('Dirname: ' . dirname($backupDestinationPath), 'ERROR');
1421 $migration->log('For security reasons, I will remove the file and stop the process...', 'ERROR');
1422 $migration->log(__('Unlocking migration', 'backup-backup'), 'INFO');
1423 if (file_exists($lock)) @unlink($lock);
1424
1425 $migration->log('error_during_downloading_backup', 'verbose');
1426 $migration->log('#002', 'END-CODE');
1427 $migration->end();
1428
1429 if (file_exists($backupDestinationPath)) @unlink($backupDestinationPath);
1430 return ['status' => 'error'];
1431 }
1432
1433 $migration->log('Download progress (' . ($step + 1) . '/' . $totalBatches . '): ' . $endRange . '/' . $size . ' (' . $percentage . '%)', 'INFO');
1434 $migration->progress($percentage);
1435 $migration->end();
1436
1437 return [
1438 'status' => 'success',
1439 'size' => $size,
1440 'md5' => $md5,
1441 'finished' => 'false',
1442 'originalFilename' => $originalFilename,
1443 'writepath' => $backupDestinationPath,
1444 'chunksize' => $bytesPerRequest,
1445 'secret' => $secret
1446 ];
1447 }
1448 }
1449
1450 if ($storage == 'googledrive') {
1451
1452 require_once BMI_INCLUDES . '/external/google-drive.php';
1453 $gdrive = new GDrive();
1454
1455 $backupDetails = false;
1456 $fileId = $this->post['fileId'];
1457
1458 if ($step === 0 || (!isset($this->post['size']) || $this->post['size'] == false || !is_numeric($this->post['size']))) {
1459
1460 $migration->log((__('Backup & Migration version: ', 'backup-backup') . BMI_VERSION));
1461 $migration->log(__('Creating lock file', 'backup-backup'));
1462 $secret = $this->randomString();
1463 file_put_contents($lock, $secret);
1464
1465 $migration->log('Download intialized', 'INFO');
1466 $migration->log('Getting backup details from Google Drive...', 'STEP');
1467 $backupDetails = $gdrive->getGoogleDriveFileMeta($fileId);
1468
1469 if ($backupDetails == false || !isset($backupDetails['data']) || $backupDetails['data'] == false) {
1470
1471 $migration->log('It seem like I was unable to get backup details from cloud.', 'ERROR');
1472 $migration->log(__('Unlocking migration', 'backup-backup'), 'INFO');
1473 if (file_exists($lock)) @unlink($lock);
1474
1475 $migration->log('error_during_downloading_backup', 'verbose');
1476 $migration->log('#002', 'END-CODE');
1477 $migration->end();
1478
1479 return ['status' => 'error'];
1480 }
1481
1482 $size = intval($backupDetails['data']->size);
1483 $md5 = $backupDetails['data']->md5Checksum;
1484 $originalFilename = $backupDetails['data']->originalFilename;
1485
1486 $migration->log('Backup details received!', 'SUCCESS');
1487 $migration->log('Backup original name: ' . $originalFilename, 'INFO');
1488 $migration->log('Starting download process...', 'STEP');
1489
1490 $availableMemory = BMP::getAvailableMemoryInBytes();
1491 $bytesPerRequest = intval($availableMemory / 4);
1492
1493 $migration->log('Single batch will use up to: ' . $bytesPerRequest . ' bytes (~' . intval($bytesPerRequest / 1024 / 1024 / 2) . ' MBs)', 'INFO');
1494
1495 $fileIterator = 2;
1496 $extension = pathinfo($originalFilename, PATHINFO_EXTENSION);
1497 $originalFilename = pathinfo($originalFilename, PATHINFO_FILENAME);
1498 if ($extension == 'gz') {
1499 $originalFilename = pathinfo($originalFilename, PATHINFO_FILENAME);
1500 $extension = 'tar.gz';
1501 }
1502 $backupDestinationPath = BMI_BACKUPS . DIRECTORY_SEPARATOR . $originalFilename . '.' . $extension;
1503 $finalName = $originalFilename . '.' . $extension;
1504
1505 while (file_exists($backupDestinationPath)) {
1506 $backupDestinationPath = BMI_BACKUPS . DIRECTORY_SEPARATOR . $originalFilename . '-' . $fileIterator . '.' . $extension;
1507 $finalName = $originalFilename . '-' . $fileIterator . '.' . $extension;
1508 $fileIterator++;
1509 }
1510
1511 $originalFilename = $finalName;
1512
1513 $backupDestinationPath .= '.crdownload';
1514
1515 } else {
1516
1517 $size = intval($this->post['size']);
1518 $md5 = $this->post['md5'];
1519 $originalFilename = $this->post['filename'];
1520 $backupDestinationPath = $this->post['writepath'];
1521 $bytesPerRequest = intval($this->post['chunksize']);
1522 }
1523
1524 $totalBatches = ceil($size / (256 * 1024 * 4 * intval($bytesPerRequest / 1024 / 1024 / 2)));
1525
1526 if ($totalBatches <= $step) {
1527
1528 $migration->log('Download process finished!', 'SUCCESS');
1529 $migration->log('Verifying MD5 checksum of downloaded file...', 'STEP');
1530
1531 rename($backupDestinationPath, str_replace('.crdownload', '', $backupDestinationPath));
1532 $backupDestinationPath = str_replace('.crdownload', '', $backupDestinationPath);
1533
1534
1535 $isDownloadedFileVerified = BMP::verifyFileMd5($backupDestinationPath, $md5);
1536 if ($isDownloadedFileVerified) {
1537 $migration->log('File MD5 checksum is correct!', 'SUCCESS');
1538 } else {
1539
1540 $migration->log('File MD5 checksum is NOT correct!', 'ERROR');
1541 $migration->log('Downloaded file path: ' . $backupDestinationPath, 'ERROR');
1542 $migration->log('File exist?: ' . (file_exists($backupDestinationPath) ? "Yes" : "No?"), 'ERROR');
1543 $migration->log('For security reasons, I will remove the file and stop the process...', 'ERROR');
1544 $migration->log(__('Unlocking migration', 'backup-backup'), 'INFO');
1545 if (file_exists($lock)) @unlink($lock);
1546
1547 $migration->log('error_during_downloading_backup', 'verbose');
1548 $migration->log('#002', 'END-CODE');
1549 $migration->end();
1550
1551 if (file_exists($backupDestinationPath)) @unlink($backupDestinationPath);
1552 return ['status' => 'error'];
1553 }
1554
1555 $migration->log(__('Unlocking migration', 'backup-backup'), 'INFO');
1556 if (file_exists($lock)) @unlink($lock);
1557 if ($startRestoreProcess == 'true') {
1558 $migration->log('Requesting restoration process...', 'STEP');
1559
1560 $migration->log('#205', 'END-CODE');
1561 } else {
1562 $migration->log('Download process finished!', 'SUCCESS');
1563 $migration->log('#206', 'END-CODE');
1564 }
1565
1566 $migration->progress(100);
1567 $migration->end();
1568
1569 return ['status' => 'success', 'finished' => 'true', 'filename' => $originalFilename];
1570 } else {
1571
1572 $chunkSize = 256 * 1024 * 4 * intval($bytesPerRequest / 1024 / 1024 / 2);
1573 $startRange = ($step * $chunkSize);
1574 if ($step !== 0) $startRange = $startRange + 1;
1575 $endRange = (($step + 1) * $chunkSize);
1576 if ($endRange > $size) $endRange = $size;
1577 $currentRange = $startRange . '-' . $endRange;
1578 $percentage = intval(($endRange / $size) * 100);
1579
1580 $contents = $gdrive->getGoogleDriveFileContents($fileId, $currentRange);
1581
1582 if ($contents == false || !isset($contents['data']) || $contents['data'] == false) {
1583
1584 $migration->log('It seem like I was unable to get backup content from cloud.', 'ERROR');
1585 $migration->log('For security reasons, I will remove the file (if exist) and stop the process...', 'ERROR');
1586 $migration->log(__('Unlocking migration', 'backup-backup'), 'INFO');
1587 if (file_exists($lock)) @unlink($lock);
1588
1589 $migration->log('error_during_downloading_backup', 'verbose');
1590 $migration->log('#002', 'END-CODE');
1591 $migration->end();
1592
1593 if (file_exists($backupDestinationPath)) @unlink($backupDestinationPath);
1594 return ['status' => 'error'];
1595 }
1596
1597 if ((is_dir(dirname($backupDestinationPath)) && file_exists($backupDestinationPath)) || $step === 0) {
1598
1599 $backupFile = fopen($backupDestinationPath, 'ab');
1600 fwrite($backupFile, $contents['data']);
1601 fclose($backupFile);
1602 } else {
1603
1604 $migration->log('File is not writable or directory does not exist.', 'ERROR');
1605 $migration->log('File: ' . basename($backupDestinationPath), 'ERROR');
1606 $migration->log('Dirname: ' . dirname($backupDestinationPath), 'ERROR');
1607 $migration->log('For security reasons, I will remove the file and stop the process...', 'ERROR');
1608 $migration->log(__('Unlocking migration', 'backup-backup'), 'INFO');
1609 if (file_exists($lock)) @unlink($lock);
1610
1611 $migration->log('error_during_downloading_backup', 'verbose');
1612 $migration->log('#002', 'END-CODE');
1613 $migration->end();
1614
1615 if (file_exists($backupDestinationPath)) @unlink($backupDestinationPath);
1616 return ['status' => 'error'];
1617 }
1618
1619 $migration->log('Download progress (' . ($step + 1) . '/' . $totalBatches . '): ' . $endRange . '/' . $size . ' (' . $percentage . '%)', 'INFO');
1620 $migration->progress($percentage);
1621 $migration->end();
1622
1623 return [
1624 'status' => 'success',
1625 'size' => $size,
1626 'md5' => $md5,
1627 'finished' => 'false',
1628 'originalFilename' => $originalFilename,
1629 'writepath' => $backupDestinationPath,
1630 'chunksize' => $bytesPerRequest,
1631 'secret' => $secret
1632 ];
1633 }
1634 }
1635
1636 if ($storage == 'ftp') {
1637
1638 require_once BMI_INCLUDES . '/external/ftp.php';
1639 $ftp = new FTP();
1640
1641 $backupDetails = false;
1642 $fileId = $this->post['fileId'];
1643
1644 if ($step === 0 || (!isset($this->post['size']) || $this->post['size'] == false || !is_numeric($this->post['size']))) {
1645
1646 $migration->log((__('Backup & Migration version: ', 'backup-backup') . BMI_VERSION));
1647 $migration->log(__('Creating lock file', 'backup-backup'));
1648 $secret = $this->randomString();
1649 file_put_contents($lock, $secret);
1650
1651 $migration->log('Download intialized', 'INFO');
1652 $migration->log('Getting backup details from FTP...', 'STEP');
1653 $backupDetails = $ftp->getFtpDriveFileMeta($fileId);
1654
1655 if ($backupDetails == false || !isset($backupDetails['data']) || $backupDetails['data'] == false) {
1656
1657 $migration->log('It seem like I was unable to get backup details from cloud.', 'ERROR');
1658 $migration->log(__('Unlocking migration', 'backup-backup'), 'INFO');
1659 if (file_exists($lock)) @unlink($lock);
1660
1661 $migration->log('#002', 'END-CODE');
1662 $migration->end();
1663
1664 return ['status' => 'error'];
1665 }
1666
1667 $size = intval($backupDetails['data']['size']);
1668
1669 $originalFilename = $backupDetails['data']['name'];
1670
1671 $migration->log('Backup details received!', 'SUCCESS');
1672 $migration->log('Backup original name: ' . $originalFilename, 'INFO');
1673 $migration->log('Starting download process...', 'STEP');
1674
1675 $availableMemory = BMP::getAvailableMemoryInBytes();
1676 $bytesPerRequest = intval($availableMemory / 4);
1677
1678 $migration->log('Single batch will use up to: ' . $bytesPerRequest . ' bytes (~' . intval($bytesPerRequest / 1024 / 1024 / 2) . ' MBs)', 'INFO');
1679
1680 $fileIterator = 2;
1681 $extension = pathinfo($originalFilename, PATHINFO_EXTENSION);
1682 $originalFilename = pathinfo($originalFilename, PATHINFO_FILENAME);
1683 if ($extension == 'gz') {
1684 $originalFilename = pathinfo($originalFilename, PATHINFO_FILENAME);
1685 $extension = 'tar.gz';
1686 }
1687 $backupDestinationPath = BMI_BACKUPS . DIRECTORY_SEPARATOR . $originalFilename . '.' . $extension;
1688 $finalName = $originalFilename . '.' . $extension;
1689
1690 while (file_exists($backupDestinationPath)) {
1691 $backupDestinationPath = BMI_BACKUPS . DIRECTORY_SEPARATOR . $originalFilename . '-' . $fileIterator . '.' . $extension;
1692 $finalName = $originalFilename . '-' . $fileIterator . '.' . $extension;
1693 $fileIterator++;
1694 }
1695
1696 $originalFilename = $finalName;
1697
1698 $backupDestinationPath .= '.crdownload';
1699
1700 } else {
1701
1702 $size = intval($this->post['size']);
1703 $md5 = $this->post['md5'];
1704 $originalFilename = $this->post['filename'];
1705 $backupDestinationPath = $this->post['writepath'];
1706 $bytesPerRequest = intval($this->post['chunksize']);
1707 }
1708
1709 $totalBatches = ceil($size / (256 * 1024 * 4 * intval($bytesPerRequest / 1024 / 1024 / 2)));
1710 $md5 = $this->post['md5'];
1711
1712 if ($totalBatches <= $step) {
1713 $migration->log('Download process finished!', 'SUCCESS');
1714 $migration->log('Verifying MD5 checksum of downloaded file...', 'STEP');
1715
1716 rename($backupDestinationPath, str_replace('.crdownload', '', $backupDestinationPath));
1717 $backupDestinationPath = str_replace('.crdownload', '', $backupDestinationPath);
1718
1719 $isDownloadedFileVerified = BMP::verifyFileMd5($backupDestinationPath, $md5);
1720 if ($isDownloadedFileVerified) {
1721 $migration->log('File MD5 checksum is correct!', 'SUCCESS');
1722 } else {
1723
1724 $migration->log('File MD5 checksum is NOT correct!', 'ERROR');
1725 $migration->log('Downloaded file path: ' . $backupDestinationPath, 'ERROR');
1726 $migration->log('File exist?: ' . (file_exists($backupDestinationPath) ? "Yes" : "No?"), 'ERROR');
1727 $migration->log('For security reasons, I will remove the file and stop the process...', 'ERROR');
1728 $migration->log(__('Unlocking migration', 'backup-backup'), 'INFO');
1729 if (file_exists($lock)) @unlink($lock);
1730
1731 $migration->log('#002', 'END-CODE');
1732 $migration->end();
1733
1734 if (file_exists($backupDestinationPath)) @unlink($backupDestinationPath);
1735 return ['status' => 'error'];
1736 }
1737
1738 $migration->log(__('Unlocking migration', 'backup-backup'), 'INFO');
1739 if (file_exists($lock)) @unlink($lock);
1740 if ($startRestoreProcess == 'true') {
1741 $migration->log('Requesting restoration process...', 'STEP');
1742
1743 $migration->log('#205', 'END-CODE');
1744 } else {
1745 $migration->log('Download process finished!', 'SUCCESS');
1746 $migration->log('#206', 'END-CODE');
1747 }
1748 $migration->progress(100);
1749 $migration->end();
1750
1751 return ['status' => 'success', 'finished' => 'true', 'filename' => $originalFilename];
1752 } else {
1753 $chunkSize = 256 * 1024 * 4 * intval($bytesPerRequest / 1024 / 1024 / 2);
1754 $startRange = ($step * $chunkSize);
1755 if ($step !== 0) $startRange = $startRange + 1;
1756 $endRange = (($step + 1) * $chunkSize);
1757 if ($endRange > $size) $endRange = $size;
1758 $percentage = intval(($endRange / $size) * 100);
1759
1760 $contents = $ftp->getFtpDriveFileContents($fileId, $startRange, $endRange);
1761 // wp_send_json($contents);
1762 if ($contents == false || !isset($contents['data']) || $contents['data'] == false) {
1763
1764 $migration->log('It seem like I was unable to get backup content from cloud.', 'ERROR');
1765 $migration->log('For security reasons, I will remove the file (if exist) and stop the process...', 'ERROR');
1766 $migration->log(__('Unlocking migration', 'backup-backup'), 'INFO');
1767 if (file_exists($lock)) @unlink($lock);
1768
1769 $migration->log('#002', 'END-CODE');
1770 $migration->end();
1771
1772 if (file_exists($backupDestinationPath)) @unlink($backupDestinationPath);
1773 return ['status' => 'error'];
1774 }
1775
1776 if ((is_dir(dirname($backupDestinationPath)) && file_exists($backupDestinationPath)) || $step === 0) {
1777
1778 $backupFile = fopen($backupDestinationPath, 'ab');
1779 fwrite($backupFile, $contents['data']);
1780 fclose($backupFile);
1781 } else {
1782
1783 $migration->log('File is not writable or directory does not exist.', 'ERROR');
1784 $migration->log('File: ' . basename($backupDestinationPath), 'ERROR');
1785 $migration->log('Dirname: ' . dirname($backupDestinationPath), 'ERROR');
1786 $migration->log('For security reasons, I will remove the file and stop the process...', 'ERROR');
1787 $migration->log(__('Unlocking migration', 'backup-backup'), 'INFO');
1788 if (file_exists($lock)) @unlink($lock);
1789
1790 $migration->log('#002', 'END-CODE');
1791 $migration->end();
1792
1793 if (file_exists($backupDestinationPath)) @unlink($backupDestinationPath);
1794 return ['status' => 'error'];
1795 }
1796
1797 $migration->log('Download progress (' . ($step + 1) . '/' . $totalBatches . '): ' . $endRange . '/' . $size . ' (' . $percentage . '%)', 'INFO');
1798 $migration->progress($percentage);
1799 $migration->end();
1800
1801 return [
1802 'status' => 'success',
1803 'size' => $size,
1804 'md5' => $md5,
1805 'finished' => 'false',
1806 'originalFilename' => $originalFilename,
1807 'writepath' => $backupDestinationPath,
1808 'chunksize' => $bytesPerRequest,
1809 'secret' => $secret
1810 ];
1811 }
1812 }
1813
1814 if ($storage == 's3'){
1815
1816 require_once BMI_INCLUDES . '/external/s3.php';
1817 $provider = $this->post['provider'];
1818 $s3 = new S3($provider);
1819
1820 $backupDetails = false;
1821 $fileId = $this->post['fileId'];
1822 $md5 = $this->post['md5'];
1823
1824 if ($step === 0 || (!isset($this->post['size']) || $this->post['size'] == false || !is_numeric($this->post['size']))) {
1825 $migration->log((__('Backup & Migration version: ', 'backup-backup') . BMI_VERSION));
1826 $migration->log(__('Creating lock file', 'backup-backup'));
1827 $secret = $this->randomString();
1828 file_put_contents($lock, $secret);
1829
1830 $migration->log('Download initialized', 'INFO');
1831 $migration->log('Getting backup details from S3...', 'STEP');
1832 $backupDetails = $s3->getFileMeta($fileId);
1833
1834 if ($backupDetails == false) {
1835 $migration->log('It seems like I was unable to get backup details from cloud.', 'ERROR');
1836 $migration->log(__('Unlocking migration', 'backup-backup'), 'INFO');
1837 if (file_exists($lock)) @unlink($lock);
1838
1839 $migration->log('error_during_downloading_backup', 'verbose');
1840 $migration->log('#002', 'END-CODE');
1841 $migration->end();
1842
1843 return ['status' => 'error'];
1844 }
1845
1846 $size = intval($backupDetails['size']);
1847 $originalFilename = $fileId;
1848
1849 $migration->log('Backup details received!', 'SUCCESS');
1850 $migration->log('Backup original name: ' . $originalFilename, 'INFO');
1851 $migration->log('Starting download process...', 'STEP');
1852
1853 $availableMemory = BMP::getAvailableMemoryInBytes();
1854 $bytesPerRequest = intval($availableMemory / 4);
1855
1856 $migration->log('Single batch will use up to: ' . $bytesPerRequest . ' bytes (~' . intval($bytesPerRequest / 1024 / 1024 / 2) . ' MBs)', 'INFO');
1857
1858 $fileIterator = 2;
1859
1860 $extension = pathinfo($originalFilename, PATHINFO_EXTENSION);
1861 $originalFilename = pathinfo($originalFilename, PATHINFO_FILENAME);
1862 if ($extension == 'gz') {
1863 $originalFilename = pathinfo($originalFilename, PATHINFO_FILENAME);
1864 $extension = 'tar.gz';
1865 }
1866
1867 $backupDestinationPath = BMI_BACKUPS . DIRECTORY_SEPARATOR . $originalFilename . '.' . $extension;
1868 $finalName = $originalFilename . '.' . $extension;
1869
1870 while (file_exists($backupDestinationPath)) {
1871 $backupDestinationPath = BMI_BACKUPS . DIRECTORY_SEPARATOR . $originalFilename . '-' . $fileIterator . '.' . $extension;
1872 $finalName = $originalFilename . '-' . $fileIterator . '.' . $extension;
1873 $fileIterator++;
1874 }
1875
1876 $originalFilename = $finalName;
1877 $backupDestinationPath .= '.crdownload';
1878
1879 } else {
1880 $size = intval($this->post['size']);
1881 $md5 = $this->post['md5'];
1882 $originalFilename = $this->post['filename'];
1883 $backupDestinationPath = $this->post['writepath'];
1884 $bytesPerRequest = intval($this->post['chunksize']);
1885 }
1886
1887 $totalBatches = ceil($size / (256 * 1024 * 4 * intval($bytesPerRequest / 1024 / 1024 / 2)));
1888
1889 if ($totalBatches <= $step) {
1890 $migration->log('Download process finished!', 'SUCCESS');
1891 $migration->log('Verifying MD5 checksum of downloaded file...', 'STEP');
1892
1893 rename($backupDestinationPath, str_replace('.crdownload', '', $backupDestinationPath));
1894 $backupDestinationPath = str_replace('.crdownload', '', $backupDestinationPath);
1895
1896 $isDownloadedFileVerified = BMP::verifyFileMd5($backupDestinationPath, $md5);
1897 if ($isDownloadedFileVerified) {
1898 $migration->log('File MD5 checksum is correct!', 'SUCCESS');
1899 } else {
1900 $migration->log('File MD5 checksum is NOT correct!', 'ERROR');
1901 $migration->log('Downloaded file path: ' . $backupDestinationPath, 'ERROR');
1902 $migration->log('File exist?: ' . (file_exists($backupDestinationPath) ? "Yes" : "No?"), 'ERROR');
1903 $migration->log('For security reasons, I will remove the file and stop the process...', 'ERROR');
1904 $migration->log(__('Unlocking migration', 'backup-backup'), 'INFO');
1905 if (file_exists($lock)) @unlink($lock);
1906
1907 $migration->log('error_during_downloading_backup', 'verbose');
1908 $migration->log('#002', 'END-CODE');
1909 $migration->end();
1910
1911 if (file_exists($backupDestinationPath)) @unlink($backupDestinationPath);
1912 return ['status' => 'error'];
1913 }
1914
1915 $migration->log(__('Unlocking migration', 'backup-backup'), 'INFO');
1916 if (file_exists($lock)) @unlink($lock);
1917 if ($startRestoreProcess == 'true') {
1918 $migration->log('Requesting restoration process...', 'STEP');
1919
1920 $migration->log('#205', 'END-CODE');
1921 } else {
1922 $migration->log('Download process finished!', 'SUCCESS');
1923 $migration->log('#206', 'END-CODE');
1924 }
1925
1926 $migration->progress(100);
1927 $migration->end();
1928
1929 return ['status' => 'success', 'finished' => 'true', 'filename' => $originalFilename];
1930 } else {
1931 $chunkSize = 256 * 1024 * 4 * intval($bytesPerRequest / 1024 / 1024 / 2);
1932 $startRange = ($step * $chunkSize);
1933 if ($step !== 0) $startRange = $startRange + 1;
1934 $endRange = (($step + 1) * $chunkSize);
1935 if ($endRange > $size) $endRange = $size;
1936 $percentage = intval(($endRange / $size) * 100);
1937
1938 $contents = $s3->getFileContent($fileId, strval($startRange) . '-' . strval($endRange));
1939
1940 if ($contents == false) {
1941 $migration->log('It seems like I was unable to get backup content from cloud.', 'ERROR');
1942 $migration->log('For security reasons, I will remove the file (if exist) and stop the process...', 'ERROR');
1943 $migration->log(__('Unlocking migration', 'backup-backup'), 'INFO');
1944 if (file_exists($lock)) @unlink($lock);
1945
1946 $migration->log('error_during_downloading_backup', 'verbose');
1947 $migration->log('#002', 'END-CODE');
1948 $migration->end();
1949
1950 if (file_exists($backupDestinationPath)) @unlink($backupDestinationPath);
1951 return ['status' => 'error'];
1952 }
1953
1954 if ((is_dir(dirname($backupDestinationPath)) && file_exists($backupDestinationPath)) || $step === 0) {
1955 $backupFile = fopen($backupDestinationPath, 'ab');
1956 fwrite($backupFile, $contents);
1957 fclose($backupFile);
1958 } else {
1959 $migration->log('File is not writable or directory does not exist.', 'ERROR');
1960 $migration->log('File: ' . basename($backupDestinationPath), 'ERROR');
1961 $migration->log('Dirname: ' . dirname($backupDestinationPath), 'ERROR');
1962 $migration->log('For security reasons, I will remove the file and stop the process...', 'ERROR');
1963 $migration->log(__('Unlocking migration', 'backup-backup'), 'INFO');
1964 if (file_exists($lock)) @unlink($lock);
1965
1966 $migration->log('error_during_downloading_backup', 'verbose');
1967 $migration->log('#002', 'END-CODE');
1968 $migration->end();
1969
1970 if (file_exists($backupDestinationPath)) @unlink($backupDestinationPath);
1971 return ['status' => 'error'];
1972 }
1973
1974 $migration->log('Download progress (' . ($step + 1) . '/' . $totalBatches . '): ' . $endRange . '/' . $size . ' (' . $percentage . '%)', 'INFO');
1975 $migration->progress($percentage);
1976 $migration->end();
1977
1978 return [
1979 'status' => 'success',
1980 'size' => $size,
1981 'md5' => $md5,
1982 'finished' => 'false',
1983 'originalFilename' => $originalFilename,
1984 'writepath' => $backupDestinationPath,
1985 'chunksize' => $bytesPerRequest,
1986 'secret' => $secret
1987 ];
1988 }
1989 }
1990
1991 if (file_exists($lock)) @unlink($lock);
1992 return ['status' => 'error'];
1993 }
1994
1995 public function siteURL() {
1996 $protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' || $_SERVER['SERVER_PORT'] == 443) ? "https://" : "http://";
1997 $domainName = $_SERVER['HTTP_HOST'];
1998
1999 return $protocol . $domainName;
2000 }
2001
2002 public function checkIfPHPCliExist(&$logger) {
2003
2004 $shouldContinue = apply_filters('bmi_cli_enabled', true);
2005 if ($shouldContinue === false) {
2006 $logger->log(__('PHP CLI is disabled manually, plugin will omit all PHP CLI steps.', 'backup-backup'), 'warn');
2007 return false;
2008 }
2009
2010
2011 if (defined('BMI_CLI_ENABLED')) {
2012 $cliEnabled = apply_filters('bmi_cli_enabled', BMI_CLI_ENABLED);
2013 if ($cliEnabled === false) {
2014 $logger->log(__('PHP CLI is disabled manually, plugin will omit all PHP CLI steps.', 'backup-backup'), 'warn');
2015 return false;
2016 }
2017 }
2018
2019 $logger->log(__('Looking for PHP CLI executable file.', 'backup-backup'), 'step');
2020 require_once BMI_INCLUDES . '/cli/php_cli_finder.php';
2021 $checker = new PHPCLICheck();
2022 $result = $checker->findPHP();
2023
2024 if ($result === false) {
2025
2026 if (!defined('BMI_CLI_ENABLED')) define('BMI_CLI_ENABLED', false);
2027 if (!defined('BMI_CLI_EXECUTABLE')) define('BMI_CLI_EXECUTABLE', false);
2028 if ($checker->ini_disabled === true) {
2029 $logger->log(__('PHP CLI is disabled in your php.ini file, the process may be unstable.', 'backup-backup'), 'warn');
2030 } else {
2031 $logger->log(__('Could not find proper PHP CLI executable, this process may be unstable.', 'backup-backup'), 'warn');
2032 }
2033
2034 return false;
2035
2036 } else {
2037
2038 if (!defined('BMI_CLI_ENABLED')) define('BMI_CLI_ENABLED', apply_filters('bmi_cli_enabled', true));
2039 if (!defined('BMI_CLI_EXECUTABLE')) define('BMI_CLI_EXECUTABLE', $result['executable']);
2040
2041 $logger->log(__('PHP CLI Filename: ', 'backup-backup') . basename($result['executable']), 'info');
2042 $logger->log(__('PHP CLI Version: ', 'backup-backup') . $result['version'] . ' ' . $result['brand'], 'info');
2043 $logger->log(__('PHP CLI Memory limit: ', 'backup-backup') . $result['memory'], 'info');
2044 $logger->log(__('PHP CLI Execution limit: ', 'backup-backup') . $result['max_exec'], 'info');
2045 $logger->log(__('We properly detected PHP CLI executable file.', 'backup-backup'), 'success');
2046
2047 return $result;
2048
2049 }
2050
2051 }
2052
2053 public function getDatabaseSize() {
2054
2055 global $wpdb;
2056 $prefix = $wpdb->prefix;
2057
2058 $result = $wpdb->get_results($wpdb->prepare("SELECT SUM(DATA_LENGTH + INDEX_LENGTH) AS `bytes` FROM information_schema.TABLES WHERE TABLE_SCHEMA = %s;", DB_NAME));
2059
2060 return intval($result[0]->bytes);
2061
2062 }
2063
2064 public function dirSize() {
2065
2066 // Folder
2067 $f = $this->post['folder'];
2068
2069 // Bytes
2070 $bytes = 0;
2071 $excludedBytes = 0;
2072
2073 $emptyVar = [ 'this_is_empty_array' ];
2074 $allowed = [ 'plugins', 'uploads', 'themes', 'contents_others', 'wordpress' ];
2075
2076 if (in_array($f, $allowed)) {
2077
2078 // Get list of staging sites for exclusion rules
2079 require_once BMI_INCLUDES . '/staging/controller.php';
2080 $staging = new Staging('..ajax..');
2081 $stagingSites = $staging->getStagingSites(true);
2082
2083 $files = $this->scanFilesForBackup($emptyVar, $stagingSites, $f);
2084 $files = $this->parseFilesForBackup($files, $emptyVar, false, true);
2085
2086 $bytes = $this->total_size_for_backup;
2087 $excludedBytes = $this->total_excluded_size_for_backup;
2088 set_transient('bmi_latest_size_' . $f, $bytes);
2089 } elseif ($f == 'database') {
2090
2091 $bytes = $this->getDatabaseSize();
2092 set_transient('bmi_latest_size_' . $f, $bytes);
2093 }
2094
2095 return [ 'bytes' => $bytes, 'excluded' => $excludedBytes, 'readable' => BMP::humanSize($bytes) ];
2096
2097 }
2098
2099 public function backupErrorHandler() {
2100 set_error_handler(function ($errno, $errstr, $errfile, $errline) {
2101
2102 if (BMI_DEBUG) {
2103 error_log('BMI DEBUG ENABLED, HERE IS THE COMPLETE REPORT (ERROR HANDLER #1):');
2104 error_log(print_r($errno, true));
2105 error_log(print_r($errstr, true));
2106 error_log(print_r($errfile, true));
2107 error_log(print_r($errline, true));
2108 }
2109
2110 if (in_array($errno, [E_DEPRECATED, E_USER_DEPRECATED, E_NOTICE])) {
2111 return true;
2112 }
2113 if (strpos($errstr, 'php_uname') !== false) return true;
2114
2115 $ignored_warnings = ['unlink(', 'pclzip'];
2116 foreach ($ignored_warnings as $warning) {
2117 if (strpos($errstr, $warning) !== false || strpos($errfile, $warning) !== false) {
2118 Logger::error(sprintf(__("Process continuation: Ignored expected warning. Handler: ajax#0x | ErrNo: %s | Msg: %s", 'backup-backup'), $errno, $errstr));
2119 return true;
2120 }
2121 }
2122
2123 $is_our_plugin = strpos($errfile, 'backup-backup') !== false;
2124 $fatal_levels = [E_USER_ERROR, E_RECOVERABLE_ERROR]; // E_ERROR cannot be caught here
2125 $is_fatal = in_array($errno, $fatal_levels);
2126
2127 if (!$is_fatal) {
2128 if (!$is_our_plugin) {
2129 return false;
2130 }
2131
2132 Logger::error(__('Non-fatal error encountered before request shutdown.', 'backup-backup'));
2133 Logger::error(sprintf(__('Message: %s | File: %s:%s', 'backup-backup'), $errstr, $errfile, $errline));
2134 return true;
2135 }
2136
2137 if (!$is_our_plugin) {
2138 Logger::error(__("Backup process was not aborted because this error originated outside Backup Migration.", 'backup-backup'));
2139 if (isset($this->zip_progress)) {
2140 $this->zip_progress->log(__("Third-party error detected, attempting to continue.", 'backup-backup'), 'warn');
2141 $this->zip_progress->log(__("Message: ", 'backup-backup') . $errstr, 'warn');
2142 }
2143 return false;
2144 }
2145
2146 $this->zip_progress->log(__("There was a critical error during backup:", 'backup-backup'), 'error');
2147 $this->zip_progress->log(__("Message: ", 'backup-backup') . $errstr, 'error');
2148 $this->zip_progress->log(__("File/line: ", 'backup-backup') . $errfile . '|' . $errline, 'error');
2149 $this->zip_progress->log(__('Unfortunately we had to remove the backup (if partly created).', 'backup-backup'), 'error');
2150 $this->zip_progress->log(__("Aborting backup...", 'backup-backup'), 'step');
2151 $this->zip_progress->log(__("#002", 'backup-backup'), 'end-code');
2152 $this->zip_progress->end();
2153
2154 // Cleanup routine
2155 if (!empty($GLOBALS['bmi_current_backup_name'])) {
2156 $backup_path = BMI_BACKUPS . DIRECTORY_SEPARATOR . $GLOBALS['bmi_current_backup_name'];
2157 if (file_exists($backup_path)) @unlink($backup_path);
2158 }
2159
2160 $flags = ['.running', '.abort'];
2161 foreach ($flags as $flag) {
2162 $flag_path = BMI_BACKUPS . DIRECTORY_SEPARATOR . $flag;
2163 if (file_exists($flag_path)) @unlink($flag_path);
2164 }
2165
2166 $GLOBALS['bmi_error_handled'] = true;
2167
2168 if (class_exists('BMP') && method_exists('BMP', 'res')) {
2169 BMP::res(['status' => 'error', 'error' => $errstr]);
2170 }
2171
2172 exit;
2173 }, E_ALL);
2174 }
2175
2176 public function migrationErrorHandler() {
2177 set_error_handler(function ($errno, $errstr, $errfile, $errline) {
2178
2179 if (BMI_DEBUG) {
2180 error_log('BMI DEBUG ENABLED, HERE IS THE COMPLETE REPORT (ERROR HANDLER #2):');
2181 error_log(print_r($errno, true));
2182 error_log(print_r($errstr, true));
2183 error_log(print_r($errfile, true));
2184 error_log(print_r($errline, true));
2185 }
2186
2187 if (in_array($errno, [E_DEPRECATED, E_USER_DEPRECATED, E_NOTICE])) {
2188 return true;
2189 }
2190 if (strpos($errstr, 'php_uname') !== false) return true;
2191
2192
2193 $ignored_warnings = ['unlink(', 'rename('];
2194 foreach ($ignored_warnings as $warning) {
2195 if (strpos($errstr, $warning) !== false || strpos($errfile, $warning) !== false) {
2196 Logger::error(sprintf(__("Process continuation: Ignored expected warning. Handler: ajax#0x | ErrNo: %s | Msg: %s", 'backup-backup'), $errno, $errstr));
2197 return true;
2198 }
2199 }
2200
2201 $is_our_plugin = strpos($errfile, 'backup-backup') !== false;
2202 $fatal_levels = [E_USER_ERROR, E_RECOVERABLE_ERROR]; // E_ERROR cannot be caught here
2203 $is_fatal = in_array($errno, $fatal_levels);
2204
2205 if (!$is_fatal) {
2206 if (!$is_our_plugin) {
2207 return false;
2208 }
2209
2210 Logger::error(__('Non-fatal error encountered before request shutdown.', 'backup-backup'));
2211 Logger::error(sprintf(__('Message: %s | File: %s:%s', 'backup-backup'), $errstr, $errfile, $errline));
2212 return true;
2213 }
2214
2215 if (!$is_our_plugin) {
2216 Logger::error(__("Restore process was not aborted because this error originated outside Backup Migration.", 'backup-backup'));
2217 if (isset($this->migration_progress)) {
2218 $this->migration_progress->log(__("Third-party error detected, attempting to continue.", 'backup-backup'), 'warn');
2219 $this->migration_progress->log(__("Message: ", 'backup-backup') . $errstr, 'warn');
2220 }
2221 return false;
2222 }
2223
2224 $this->migration_progress->log(__("There was a critical error during restore:", 'backup-backup'), 'error');
2225 $this->migration_progress->log(__("Message: ", 'backup-backup') . $errstr, 'error');
2226 $this->migration_progress->log(__("File/line: ", 'backup-backup') . $errfile . '|' . $errline, 'error');
2227 $this->migration_progress->log(__('Unfortunately we had to stop the restore process.', 'backup-backup'), 'error');
2228 $this->migration_progress->log(__("Aborting restore...", 'backup-backup'), 'step');
2229
2230 // Cleanup routine
2231 if (file_exists(BMI_BACKUPS . DIRECTORY_SEPARATOR . '.migration_lock')) @unlink(BMI_BACKUPS . DIRECTORY_SEPARATOR . '.migration_lock');
2232 if (isset($GLOBALS['bmi_current_tmp_restore']) && !empty($GLOBALS['bmi_current_tmp_restore'])) {
2233
2234 $this->migration_progress->log(__("Cleaning up exported files...", 'backup-backup'), 'step');
2235
2236 $tmp_unique = $GLOBALS['bmi_current_tmp_restore_unique'];
2237 $dir = $GLOBALS['bmi_current_tmp_restore'];
2238 $it = new \RecursiveDirectoryIterator($dir, \RecursiveDirectoryIterator::SKIP_DOTS);
2239 $files = new \RecursiveIteratorIterator($it, \RecursiveIteratorIterator::CHILD_FIRST);
2240
2241 $this->migration_progress->log(__('Removing ', 'backup-backup') . iterator_count($files) . __(' files', 'backup-backup'), 'INFO');
2242 foreach ($files as $file) {
2243 if ($file->isDir()) {
2244 @rmdir($file->getRealPath());
2245 } else {
2246 @unlink($file->getRealPath());
2247 }
2248 }
2249
2250 @rmdir($dir);
2251
2252 $config_file = untrailingslashit(ABSPATH) . DIRECTORY_SEPARATOR . 'wp-config.' . $tmp_unique . '.php';
2253 if (file_exists($config_file)) @unlink($config_file);
2254
2255 }
2256
2257 $this->migration_progress->log(__("#002", 'backup-backup'), 'end-code');
2258 $this->migration_progress->end();
2259
2260 $GLOBALS['bmi_error_handled'] = true;
2261 BMP::res(['status' => 'error', 'error' => $errstr]);
2262 exit;
2263
2264 }, E_ALL);
2265 }
2266
2267 public function migrationExceptionHandler() {
2268 set_exception_handler(function ($exception) {
2269 if (BMI_DEBUG) {
2270 error_log('BMI DEBUG ENABLED, HERE IS THE COMPLETE REPORT (EXCEPTION HANDLER #1):');
2271 error_log(print_r($exception, true));
2272 }
2273
2274 $this->migration_progress->log(__("Restore exception: ", 'backup-backup') . $exception->getMessage(), 'warn');
2275 Logger::log(__("Restore exception: ", 'backup-backup') . $exception->getMessage());
2276 });
2277 }
2278 public function backupExceptionHandler() {
2279 set_exception_handler(function ($exception) {
2280 if (BMI_DEBUG) {
2281 error_log('BMI DEBUG ENABLED, HERE IS THE COMPLETE REPORT (EXCEPTION HANDLER #2):');
2282 error_log(print_r($exception, true));
2283 }
2284
2285 $this->zip_progress->log(__("Exception: ", 'backup-backup') . $exception->getMessage(), 'warn');
2286 Logger::log(__("Exception: ", 'backup-backup') . $exception->getMessage());
2287 });
2288 }
2289
2290 public function resetLatestLogs() {
2291
2292 // Restore htaccess
2293 BMP::revertLitespeed();
2294 BMP::fixLitespeed();
2295
2296 // Check time if not bugged
2297 if (file_exists(BMI_BACKUPS . '/.running') && (time() - filemtime(BMI_BACKUPS . '/.running')) > 65) {
2298 if (file_exists(BMI_BACKUPS . '/.running')) @unlink(BMI_BACKUPS . '/.running');
2299 if (file_exists(BMI_BACKUPS . '/.abort')) @unlink(BMI_BACKUPS . '/.abort');
2300 }
2301
2302 // Check if backup is not in progress
2303 if (file_exists(BMI_BACKUPS . '/.running')) {
2304 return ['status' => 'msg', 'why' => __('Backup process already running, please wait till it complete.', 'backup-backup'), 'level' => 'warning'];
2305 }
2306
2307 // Remove too large logs
2308 $completeLogsPath = BMI_CONFIG_DIR . DIRECTORY_SEPARATOR . 'complete_logs.' . BMI_LOGS_SUFFIX . '.log';
2309 if (file_exists($completeLogsPath) && (filesize($completeLogsPath) / 1024 / 1024) >= 3) {
2310 @unlink($completeLogsPath);
2311 }
2312
2313 $backgroundLogsPath = BMI_CONFIG_DIR . DIRECTORY_SEPARATOR . 'background-errors.' . BMI_LOGS_SUFFIX . '.log';
2314 if (file_exists($backgroundLogsPath) && (filesize($backgroundLogsPath) / 1024 / 1024) >= 3) {
2315 @unlink($backgroundLogsPath);
2316 }
2317
2318 @touch($completeLogsPath);
2319 @touch($backgroundLogsPath);
2320
2321 // Require logs
2322 require_once BMI_INCLUDES . '/progress/zip.php';
2323 require_once BMI_INCLUDES . '/progress/migration.php';
2324 require_once BMI_INCLUDES . '/progress/staging.php';
2325
2326 // Write initial
2327 $zip_progress = new Progress('', 0);
2328 $zip_progress->start();
2329 $zip_progress->log(__("Initializing backup...", 'backup-backup'), 'step');
2330 $zip_progress->progress('0/100');
2331 $zip_progress->end();
2332
2333 // Write initial
2334 $migration = new MigrationProgress(false);
2335 $migration->start();
2336 $migration->log(__('Initializing restore process', 'backup-backup'), 'STEP');
2337 $migration->progress('0');
2338 $migration->end();
2339
2340 // Write initial
2341 $staging = new StagingProgress(false);
2342 $staging->start();
2343 $staging->log(__('Preparing creation of staging site...', 'backup-backup'), 'STEP');
2344 $staging->progress('0');
2345 $staging->end();
2346
2347 // Return done
2348 return ['status' => 'success'];
2349 }
2350
2351 public function makeBackupName() {
2352 $name = Dashboard\bmi_get_config('BACKUP:NAME');
2353
2354 $urlparts = parse_url(home_url());
2355 $domain = str_replace('.', '-', sanitize_text_field($urlparts['host']));
2356
2357 $hash = BMP::randomString(16);
2358 $name = str_replace('%domain', $domain, $name);
2359 $name = str_replace('%hash', $hash, $name);
2360 $name = str_replace('%Y', date('Y'), $name);
2361 $name = str_replace('%M', date('M'), $name);
2362 $name = str_replace('%D', date('D'), $name);
2363 $name = str_replace('%d', date('d'), $name);
2364 $name = str_replace('%j', date('j'), $name);
2365 $name = str_replace('%m', date('m'), $name);
2366 $name = str_replace('%n', date('n'), $name);
2367 $name = str_replace('%Y', date('Y'), $name);
2368 $name = str_replace('%y', date('y'), $name);
2369 $name = str_replace('%a', date('a'), $name);
2370 $name = str_replace('%A', date('A'), $name);
2371 $name = str_replace('%B', date('B'), $name);
2372 $name = str_replace('%g', date('g'), $name);
2373 $name = str_replace('%G', date('G'), $name);
2374 $name = str_replace('%h', date('h'), $name);
2375 $name = str_replace('%H', date('H'), $name);
2376 $name = str_replace('%i', date('i'), $name);
2377 $name = str_replace('%s', date('s'), $name);
2378 $name = str_replace('%s', date('s'), $name);
2379
2380 $i = 2;
2381 $tmpname = $name;
2382
2383 while (file_exists($tmpname . '.zip')) {
2384 $tmpname = $name . '_' . $i;
2385 $i++;
2386 }
2387
2388 $name = $tmpname . '.zip';
2389
2390 if (has_filter('bmip_backup_name')) {
2391 $name = apply_filters('bmip_backup_name', $name);
2392 }
2393
2394 $GLOBALS['bmi_current_backup_name'] = $name;
2395 return $name;
2396 }
2397
2398 public function fixUnameFunction() {
2399 $file = trailingslashit(ABSPATH) . 'wp-admin/includes/class-pclzip.php';
2400 $backup = trailingslashit(ABSPATH) . 'wp-admin/includes/class-pclzip-backup.php';
2401
2402 // Make backup
2403 if (!file_exists($backup)) {
2404 @copy($file, $backup);
2405 }
2406
2407 // Replace deprecated php_uname function which is mostly disabled and cause errors
2408 $replace = file_get_contents($file);
2409 $replace = str_replace('php_uname()', '(DIRECTORY_SEPARATOR === "/" ? "linux" : "windows")', $replace);
2410 file_put_contents($file, $replace);
2411 return ['status' => 'success'];
2412 }
2413
2414 public function revertUnameProcess() {
2415 $file = trailingslashit(ABSPATH) . 'wp-admin/includes/class-pclzip.php';
2416 $backup = trailingslashit(ABSPATH) . 'wp-admin/includes/class-pclzip-backup.php';
2417 if (file_exists($backup)) {
2418 if (file_exists($file)) @unlink($file);
2419 @copy($backup, $file);
2420 }
2421 return ['status' => 'success'];
2422 }
2423
2424 public function isFunctionEnabled($func) {
2425 $disabled = explode(',', ini_get('disable_functions'));
2426 $isDisabled = in_array($func, $disabled);
2427 if (!$isDisabled && function_exists($func)) return true;
2428 else return false;
2429 }
2430
2431 public function prepareAndMakeBackup($cron = false) {
2432
2433 global $wp_version;
2434
2435 $triggerLock = BMI_BACKUPS . DIRECTORY_SEPARATOR . '.last_triggered';
2436
2437 if ($this->isFunctionEnabled('ini_set')) {
2438 ini_set('display_errors', 1);
2439 ini_set('error_reporting', E_ALL);
2440 ini_set('log_errors', 1);
2441 ini_set('error_log', BMI_CONFIG_DIR . DIRECTORY_SEPARATOR . 'complete_logs.' . BMI_LOGS_SUFFIX . '.log');
2442 }
2443
2444 // Double check for .space_check file
2445 if (file_exists(BMI_BACKUPS . '/.space_check')) @unlink(BMI_BACKUPS . '/.space_check');
2446
2447 // Require File Scanner
2448 require_once BMI_INCLUDES . '/progress/zip.php';
2449 require_once BMI_INCLUDES . '/check/checker.php';
2450
2451 // CLI Handler
2452 $cliHandler = trailingslashit(sanitize_text_field(BMI_INCLUDES)) . 'cli-handler.php';
2453
2454 // Backup name
2455 if (defined('BMI_CLI_ARGUMENT') && !empty(BMI_CLI_ARGUMENT)) {
2456 $name = BMI_CLI_ARGUMENT;
2457 } else {
2458 $name = $this->makeBackupName();
2459 }
2460
2461 // Progress & Logs
2462 $cliRunning = (defined('BMI_USING_CLI_FUNCTIONALITY') && BMI_USING_CLI_FUNCTIONALITY === true) ? true : false;
2463 $shouldResetLogs = !$cliRunning;
2464 if (defined('BMI_DOING_SCHEDULED_BACKUP_VIA_CLI')) {
2465 $cron = true;
2466 $shouldResetLogs = true;
2467 }
2468
2469 $clearEndCodes = false;
2470 if (isset($this->post['preserveLogs']) && ($this->post['preserveLogs'] == 'true' || $this->post['preserveLogs'] === true)) {
2471 $shouldResetLogs = false;
2472 $clearEndCodes = true;
2473 }
2474
2475 $zip_progress = new Progress($name, 100, 0, $cron, $shouldResetLogs, $clearEndCodes);
2476 $zip_progress->start();
2477
2478 // PHP CLI Check
2479 $isCLI = false;
2480 $cli_lock = BMI_BACKUPS . '/.backup_lock_cli';
2481 $cli_lock_end = BMI_BACKUPS . '/.backup_lock_cli_end';
2482 $cli_failed_lock = BMI_BACKUPS . '/.backup_lock_cli_failed';
2483
2484 if (!defined('BMI_USING_CLI_FUNCTIONALITY') || BMI_USING_CLI_FUNCTIONALITY === false) {
2485
2486 $cli_result = $this->checkIfPHPCliExist($zip_progress);
2487 $functionNormal = apply_filters('bmi_function_normal', BMI_FUNCTION_NORMAL);
2488 if ($cli_result !== false && $functionNormal === true) {
2489
2490 $res = null;
2491 if (defined('BMI_DOING_SCHEDULED_BACKUP')) {
2492 @exec(BMI_CLI_EXECUTABLE . ' -f "' . $cliHandler . '" bmi_backup_cron ' . $name . ' > /dev/null &', $res);
2493 } else {
2494 @exec(BMI_CLI_EXECUTABLE . ' -f "' . $cliHandler . '" bmi_backup ' . $name . ' > /dev/null &', $res);
2495 }
2496 $res = implode("\n", $res);
2497
2498 sleep(3);
2499
2500 if (file_exists($cli_lock_end) && (time() - filemtime($cli_lock_end)) < 10) {
2501
2502 if (file_exists($cli_lock_end)) @unlink($cli_lock_end);
2503 if (file_exists($triggerLock)) @unlink($triggerLock);
2504 return ['status' => 'success', 'filename' => $name];
2505 exit;
2506
2507 }
2508
2509 if (!file_exists($cli_lock) || (time() - filemtime($cli_lock)) > 10) {
2510
2511 if (!file_exists(BMI_BACKUPS . '/.abort') || (time() - filemtime(BMI_BACKUPS . '/.abort')) > 10) {
2512
2513 $zip_progress->log(__("Something went wrong in PHP CLI process, backup will be continued with legacy methods.", 'backup-backup'), 'warn');
2514 if (file_exists($cli_lock)) @unlink($cli_lock);
2515 define('BMI_CLI_FAILED', true);
2516 touch($cli_failed_lock);
2517
2518 } else {
2519
2520 $zip_progress->log(__("Backup will not be continued due to manual abort by user.", 'backup-backup'), 'warn');
2521 if (file_exists($cli_lock)) @unlink($cli_lock);
2522 if (file_exists($triggerLock)) @unlink($triggerLock);
2523 return ['status' => 'msg', 'why' => __('Backup process aborted.', 'backup-backup'), 'level' => 'info'];
2524
2525 }
2526
2527 } else {
2528
2529 return ['status' => 'background', 'filename' => $name];
2530
2531 }
2532
2533 } else {
2534
2535 if ($functionNormal !== true) {
2536 $zip_progress->log(__("PHP CLI will not run due to user settings in plugin other options.", 'backup-backup'), 'warn');
2537 } else {
2538 $zip_progress->log(__("PHP CLI file cannot be executed due to unknown reason.", 'backup-backup'), 'warn');
2539 }
2540
2541 }
2542
2543 } else {
2544
2545 if (defined('BMI_USING_CLI_FUNCTIONALITY') && BMI_USING_CLI_FUNCTIONALITY === true) {
2546
2547 if (file_exists($cli_failed_lock) && (time() - filemtime($cli_failed_lock)) < 10) {
2548 exit;
2549 }
2550
2551 $isCLI = true;
2552 $zip_progress->log(__("Backup via PHP CLI initialized successfully.", 'backup-backup'), 'success');
2553 touch($cli_lock);
2554
2555 }
2556
2557 }
2558
2559 // Just in case (e.g. syntax error, we can close the file correctly)
2560 $GLOBALS['bmi_backup_progress'] = $zip_progress;
2561
2562 // Logs
2563 $zip_progress->log(__("Initializing backup...", 'backup-backup'), 'step');
2564 $zip_progress->log((__("Backup & Migration version: ", 'backup-backup') . BMI_VERSION), 'info');
2565 $zip_progress->log(__("Site which will be backed up: ", 'backup-backup') . site_url(), 'info');
2566 $zip_progress->log(__("PHP Version: ", 'backup-backup') . PHP_VERSION, 'info');
2567 $zip_progress->log(__("WP Version: ", 'backup-backup') . $wp_version, 'info');
2568 $zip_progress->log(__("MySQL Version: ", 'backup-backup') . $GLOBALS['wpdb']->db_version(), 'info');
2569 $maxAllowedPackets = $GLOBALS['wpdb']->get_results("SHOW VARIABLES LIKE 'max_allowed_packet';");
2570 if (sizeof($maxAllowedPackets) > 0) {
2571 $zip_progress->log(__("MySQL Max Length: ", 'backup-backup') . $maxAllowedPackets[0]->Value, 'info');
2572 } else {
2573 $zip_progress->log(__("MySQL Max Length: ", 'backup-backup') . 'Unknown', 'info');
2574 }
2575 if (isset($_SERVER['SERVER_SOFTWARE']) && !empty($_SERVER['SERVER_SOFTWARE'])) {
2576 $zip_progress->log(__("Web server: ", 'backup-backup') . $_SERVER['SERVER_SOFTWARE'], 'info');
2577 } else {
2578 $zip_progress->log(__("Web server: Not available", 'backup-backup'), 'info');
2579 }
2580 $zip_progress->log(__("Max execution time (in seconds): ", 'backup-backup') . @ini_get('max_execution_time'), 'info');
2581
2582 $zip_progress->log(__("Memory limit (server): ", 'backup-backup') . @ini_get('memory_limit'), 'info');
2583 if (defined('WP_MEMORY_LIMIT')) {
2584 $zip_progress->log(__("Memory limit (wp-config): ", 'backup-backup') . WP_MEMORY_LIMIT, 'info');
2585 }
2586 if (defined('WP_MAX_MEMORY_LIMIT')) {
2587 $zip_progress->log(__("Memory limit (wp-config admin): ", 'backup-backup') . WP_MAX_MEMORY_LIMIT, 'info');
2588 }
2589
2590 if (defined('BMI_DB_MAX_ROWS_PER_QUERY')) {
2591 $zip_progress->log(__('Max rows per query (this site): ', 'backup-backup') . BMI_DB_MAX_ROWS_PER_QUERY, 'info');
2592 }
2593
2594 $zip_progress->log(__("Checking if backup dir is writable...", 'backup-backup'), 'info');
2595
2596 if (defined('BMI_DOING_SCHEDULED_BACKUP')) {
2597 $zip_progress->log(__("This process was initialized due to scheduled backup configuration...", 'backup-backup'), 'info');
2598 $zip_progress->log(__("Backup will be unlocked by default as it is not manual backup...", 'backup-backup'), 'info');
2599 $zip_progress->log('This log is triggered by SCHEDULED BACKUP and its part of automatic backup creation', 'verbose');
2600 }
2601
2602 if (defined('BMI_BACKUP_PRO')) {
2603 if (BMI_BACKUP_PRO == 1) {
2604 $zip_progress->log(__("Premium plugin is enabled and activated", 'backup-backup'), 'info');
2605 } else {
2606 $zip_progress->log(__("Premium version is enabled but not active, using free plugin.", 'backup-backup'), 'warn');
2607 }
2608 }
2609
2610 // Error handler
2611 $zip_progress->log(__("Initializing custom error handler", 'backup-backup'), 'info');
2612 $this->zip_progress = &$zip_progress;
2613 $this->backupErrorHandler();
2614 $this->backupExceptionHandler();
2615
2616 // Checker
2617 $checker = new Checker($zip_progress);
2618
2619 if (!is_writable(dirname(BMI_BACKUPS))) {
2620
2621 // Abort backup
2622 $zip_progress->log(__("Backup directory is not writable...", 'backup-backup'), 'error');
2623 $zip_progress->log(__("Path: ", 'backup-backup') . BMI_BACKUPS, 'error');
2624
2625 // Close backup
2626 if (file_exists(BMI_BACKUPS . '/.running')) @unlink(BMI_BACKUPS . '/.running');
2627 if (file_exists(BMI_BACKUPS . '/.abort')) @unlink(BMI_BACKUPS . '/.abort');
2628 if ($isCLI === true && file_exists($cli_lock)) @unlink($cli_lock);
2629
2630 // Log and close log
2631 $zip_progress->log('#002', 'END-CODE');
2632 $zip_progress->end();
2633
2634 if ($isCLI === true) touch($cli_lock_end);
2635 $this->actionsAfterProcess();
2636
2637 // Return error
2638 if (file_exists($triggerLock)) @unlink($triggerLock);
2639 if ($cron == true) return ['status' => 'success'];
2640 else return ['status' => 'error'];
2641 } else {
2642 $zip_progress->log(__("Yup it is writable...", 'backup-backup'), 'success');
2643 }
2644
2645 if (!file_exists(BMI_BACKUPS)) @mkdir(BMI_BACKUPS, true);
2646
2647 // Get list of staging sites for exclusion rules
2648 require_once BMI_INCLUDES . '/staging/controller.php';
2649 $staging = new Staging('..ajax..');
2650 $stagingSites = $staging->getStagingSites(true);
2651
2652 // Get file names (huge list mostly)
2653 if (has_filter('bmip_backup_files')) {
2654 $files = apply_filters('bmip_backup_files', []);
2655 } else if ($fgwp = Dashboard\bmi_get_config('BACKUP:FILES') == 'true') {
2656 $zip_progress->log(__("Scanning files...", 'backup-backup'), 'step');
2657 $files = $this->scanFilesForBackup($zip_progress, $stagingSites);
2658 $files = $this->parseFilesForBackup($files, $zip_progress, $cron);
2659 } else {
2660 $zip_progress->log(__("Omitting files (due to settings)...", 'backup-backup'), 'warn');
2661 $files = [];
2662 }
2663
2664 $zip_progress->log(str_replace('%s', $this->total_excluded_size_for_backup, __("Total size of excluded files: %s bytes", 'backup-backup')), 'info');
2665 $zip_progress->log("Total size of excluded files (bytes): " . $this->total_excluded_size_for_backup, 'verbose');
2666
2667 // Check if there is enough space
2668 if (apply_filters('bmip_streaming_backup_is_enabled', false)) {
2669 $streamingSpaceSucceeded = apply_filters('bmip_streaming_backup_space_check', $this->total_size_for_backup, $zip_progress);
2670 if (!$streamingSpaceSucceeded) {
2671 // Close backup
2672 if (file_exists(BMI_BACKUPS . '/.running')) @unlink(BMI_BACKUPS . '/.running');
2673 if (file_exists(BMI_BACKUPS . '/.abort')) @unlink(BMI_BACKUPS . '/.abort');
2674 if ($isCLI === true && file_exists($cli_lock)) @unlink($cli_lock);
2675
2676 // Log and close log
2677 $zip_progress->log('#002', 'END-CODE');
2678 $zip_progress->end();
2679
2680 if ($isCLI === true) touch($cli_lock_end);
2681 $this->actionsAfterProcess();
2682
2683 // Return error
2684 if (file_exists($triggerLock)) @unlink($triggerLock);
2685 if ($cron == true) return ['status' => 'msg', 'why' => __('There is not enough space for backup, please free up ' . round($this->total_size_for_backup / 1024 / 1024, 2) . ' MB of space.', 'backup-backup')];
2686 else return ['status' => 'error'];
2687 }
2688 } else {
2689 $bytes = intval($this->total_size_for_backup * 1.4);
2690 update_option('bmi_required_space', $bytes);
2691 $zip_progress->log(__("Checking free space, reserving...", 'backup-backup'), 'step');
2692 if ($this->total_size_for_backup_in_mb >= BMI_REV * 1000 && get_option('bmip_last', false) != '1') {
2693
2694 // Abort backup
2695 $zip_progress->log(__("Aborting backup...", 'backup-backup'), 'step');
2696 $zip_progress->log(str_replace('%s', BMI_REV, __("Site weights more than %s GB.", 'backup-backup')), 'error');
2697 if (isset($this->post['f'])) {
2698 $zip_progress->log('Function: ' . print_r($this->post['f'], true), 'verbose');
2699 }
2700
2701 if (isset($_SERVER)) {
2702 $zip_progress->log('REQUEST_URI: ' . $_SERVER['REQUEST_URI'], 'verbose');
2703 $zip_progress->log('REQUEST_METHOD: ' . $_SERVER['REQUEST_METHOD'], 'verbose');
2704 }
2705
2706 if (!empty($this->post)) {
2707 $zip_progress->log(print_r($this->post, true), 'verbose');
2708 }
2709
2710 // Close backup
2711 if (file_exists(BMI_BACKUPS . '/.running')) @unlink(BMI_BACKUPS . '/.running');
2712 if (file_exists(BMI_BACKUPS . '/.abort')) @unlink(BMI_BACKUPS . '/.abort');
2713 if ($isCLI === true && file_exists($cli_lock)) @unlink($cli_lock);
2714
2715 // Log and close log
2716 $zip_progress->log('#100', 'END-CODE');
2717 $zip_progress->end();
2718
2719 if ($isCLI === true) touch($cli_lock_end);
2720 $this->actionsAfterProcess();
2721
2722 // Return error
2723 if (file_exists($triggerLock)) @unlink($triggerLock);
2724 return ['status' => 'error', 'bfs' => true];
2725 }
2726
2727 $isSpaceCheckDisabled = Dashboard\bmi_get_config('OTHER:BACKUP:SPACE:CHECKING');
2728
2729 if ($isSpaceCheckDisabled) {
2730
2731 $zip_progress->log(__("Free space checking is disabled by user in settings...", 'backup-backup'), 'warn');
2732 $zip_progress->log(__("Backup will continue, trusting there is enough space...", 'backup-backup'), 'warn');
2733
2734 } else {
2735
2736 if (!$checker->check_free_space($bytes)) {
2737
2738 // Abort backup
2739 $zip_progress->log(__("Aborting backup...", 'backup-backup'), 'step');
2740 $zip_progress->log(__("There is no space for that backup, checked: ", 'backup-backup') . ($bytes) . __(" bytes", 'backup-backup'), 'error');
2741 $zip_progress->log('not_enough_space', 'verbose');
2742
2743 // Close backup
2744 if (file_exists(BMI_BACKUPS . '/.running')) @unlink(BMI_BACKUPS . '/.running');
2745 if (file_exists(BMI_BACKUPS . '/.abort')) @unlink(BMI_BACKUPS . '/.abort');
2746 if ($isCLI === true && file_exists($cli_lock)) @unlink($cli_lock);
2747
2748 // Log and close log
2749 $zip_progress->log('#002', 'END-CODE');
2750 $zip_progress->end();
2751
2752 if ($isCLI === true) touch($cli_lock_end);
2753 $this->actionsAfterProcess();
2754
2755 // Return error
2756 if (file_exists($triggerLock)) @unlink($triggerLock);
2757 if ($cron == true) return ['status' => 'msg', 'why' => __('There is not enough space for backup, please free up ' . round($bytes / 1024 / 1024, 2) . ' MB of space.', 'backup-backup')];
2758 else return ['status' => 'error'];
2759 } else {
2760 $zip_progress->log(__("Confirmed, there is more than enough space, checked: ", 'backup-backup') . ($bytes) . __(" bytes", 'backup-backup'), 'success');
2761 $zip_progress->bytes = $this->total_size_for_backup;
2762 }
2763
2764 }
2765 }
2766
2767 if (Dashboard\bmi_get_config('BACKUP:DATABASE') != 'true') {
2768
2769 // $zip_progress->log(__("Database won't be backed-up due to user settings, omitting...", 'backup-backup'), 'info');
2770 // Commented as message will be shown in database backup module
2771
2772 }
2773
2774 // Log and set files length
2775 $zip_progress->log(__("Scanning done - found ", 'backup-backup') . sizeof($files) . __(" files...", 'backup-backup'), 'info');
2776 $zip_progress->files = sizeof($files);
2777
2778 // Make Backup
2779 $zip_progress->log(__("Backup initialized...", 'backup-backup'), 'success');
2780 $zip_progress->log(__("Initializing archiving system...", 'backup-backup'), 'step');
2781
2782 $resultCreateBackup = $this->createBackup($files, ABSPATH, $name, $zip_progress, $cron, $isCLI);
2783 return $resultCreateBackup;
2784 }
2785
2786 public function fixLitespeed() {
2787 BMP::fixLitespeed();
2788
2789 return ['status' => 'success'];
2790 }
2791
2792 public function revertLitespeed() {
2793 BMP::revertLitespeed();
2794
2795 return ['status' => 'success'];
2796 }
2797
2798 public function createBackup($files, $base, $name, &$zip_progress, $cron = false, $isCLI = false) {
2799
2800 // Require File Zipper
2801 require_once BMI_INCLUDES . '/zipper/zipping.php';
2802
2803 // CLI locks
2804 $cli_lock = BMI_BACKUPS . '/.backup_lock_cli';
2805 $cli_lock_end = BMI_BACKUPS . '/.backup_lock_cli_end';
2806 $cli_failed_lock = BMI_BACKUPS . '/.backup_lock_cli_failed';
2807
2808 // Backup name
2809 $backup_path = BMI_BACKUPS . '/' . $name;
2810
2811 // Check time if not bugged
2812 if (file_exists(BMI_BACKUPS . '/.running') && (time() - filemtime(BMI_BACKUPS . '/.running')) > 65) {
2813 if (file_exists(BMI_BACKUPS . '/.running')) @unlink(BMI_BACKUPS . '/.running');
2814 if (file_exists(BMI_BACKUPS . '/.abort')) @unlink(BMI_BACKUPS . '/.abort');
2815 if ($isCLI === true && file_exists($cli_lock)) @unlink($cli_lock);
2816 if ($isCLI === true && file_exists($cli_lock_end)) @unlink($cli_lock_end);
2817 }
2818
2819 if ($isCLI === true) {
2820 if (file_exists($cli_failed_lock) && (time() - filemtime($cli_failed_lock)) < 10) {
2821 exit;
2822 }
2823 }
2824
2825 // Mark as in progress
2826 if (!file_exists(BMI_BACKUPS . '/.running')) {
2827 touch(BMI_BACKUPS . '/.running');
2828 file_put_contents(BMI_BACKUPS . '/.running', $name);
2829 if ($isCLI === true) touch($cli_lock);
2830 } else {
2831 return ['status' => 'msg', 'why' => __('Backup process already running, please wait till it complete.', 'backup-backup'), 'level' => 'warning'];
2832 }
2833
2834 // Initialized
2835 $zip_progress->log('backup_initialized', 'verbose');
2836 $zip_progress->log(__("Archive system initialized...", 'backup-backup'), 'success');
2837 do_action('bmip_backup_initialized');
2838
2839 // Make ZIP
2840 $zipper = new Zipper();
2841 $zippy = $zipper->makeZIP($files, $backup_path, $name, $zip_progress, $cron);
2842 if (!$zippy) {
2843
2844 // Make sure it's open
2845 $zip_progress->start();
2846
2847 // Abort backup
2848 $zip_progress->log(__("Aborting backup...", 'backup-backup'), 'step');
2849
2850 // Close backup
2851 if (file_exists(BMI_BACKUPS . '/.running')) @unlink(BMI_BACKUPS . '/.running');
2852 if (file_exists(BMI_BACKUPS . '/.abort')) @unlink(BMI_BACKUPS . '/.abort');
2853 if ($isCLI === true && file_exists($cli_lock)) @unlink($cli_lock);
2854
2855 // Log and close log
2856 $zip_progress->log('#002', 'END-CODE');
2857 $zip_progress->end();
2858
2859 if ($isCLI === true) touch($cli_lock_end);
2860
2861 // Return error
2862 if (file_exists($backup_path)) @unlink($backup_path);
2863
2864 $this->actionsAfterProcess();
2865 return ['status' => 'error'];
2866 }
2867
2868 if (isset($zippy['status']) && $zippy['status'] == 'background') {
2869 return $zippy;
2870 }
2871
2872 // Backup aborted
2873 if (file_exists(BMI_BACKUPS . '/.abort')) {
2874
2875 // Make sure it's open
2876 $zip_progress->start();
2877
2878 if (file_exists($backup_path)) @unlink($backup_path);
2879 if (file_exists(BMI_BACKUPS . '/.running')) @unlink(BMI_BACKUPS . '/.running');
2880 if (file_exists(BMI_BACKUPS . '/.abort')) @unlink(BMI_BACKUPS . '/.abort');
2881 if (file_exists(BMI_BACKUPS . '/.cron')) @unlink(BMI_BACKUPS . '/.cron');
2882 if ($isCLI === true && file_exists($cli_lock)) @unlink($cli_lock);
2883
2884 // Log and close log
2885 $zip_progress->log(__("Backup process aborted.", 'backup-backup'), 'warn');
2886 $zip_progress->log('manually_aborted', 'verbose');
2887 $zip_progress->log('#003', 'END-CODE');
2888 $zip_progress->end();
2889
2890 if ($isCLI === true) touch($cli_lock_end);
2891 Logger::log(__("Backup process aborted.", 'backup-backup'));
2892
2893 $this->actionsAfterProcess();
2894 return ['status' => 'msg', 'why' => __('Backup process aborted.', 'backup-backup'), 'level' => 'info'];
2895 }
2896
2897 if (!file_exists($backup_path) && !$cron) {
2898
2899 // Make sure it's open
2900 $zip_progress->start();
2901
2902 // Abort backup
2903 $zip_progress->log(__("Aborting backup...", 'backup-backup'), 'step');
2904 $zip_progress->log(__("There is no backup file...", 'backup-backup'), 'error');
2905 $zip_progress->log(__("We could not find backup file when it already should be here.", 'backup-backup'), 'error');
2906 $zip_progress->log(__("This error may be related to missing space. (filled during backup)", 'backup-backup'), 'error');
2907 $zip_progress->log(__("Path: ", 'backup-backup') . $backup_path, 'error');
2908
2909 // Close backup
2910 if (file_exists(BMI_BACKUPS . '/.running')) @unlink(BMI_BACKUPS . '/.running');
2911 if (file_exists(BMI_BACKUPS . '/.cron')) @unlink(BMI_BACKUPS . '/.cron');
2912 if (file_exists(BMI_BACKUPS . '/.abort')) @unlink(BMI_BACKUPS . '/.abort');
2913 if ($isCLI === true && file_exists($cli_lock)) @unlink($cli_lock);
2914
2915 // Log and close log
2916 $zip_progress->log('#002', 'END-CODE');
2917 $zip_progress->end();
2918
2919 if ($isCLI === true) touch($cli_lock_end);
2920 $this->actionsAfterProcess();
2921
2922 // Return error
2923 if ($cron == true) return ['status' => 'success'];
2924 else return ['status' => 'error'];
2925 }
2926
2927 // End zip log
2928 $zip_progress->log(__("New backup created and its name is: ", 'backup-backup') . $name, 'success');
2929 $zip_progress->log('#001', 'END-CODE');
2930 $zip_progress->end();
2931
2932 if ($isCLI === true) touch($cli_lock_end);
2933
2934 // Unlink progress
2935 if (file_exists(BMI_BACKUPS . '/.running')) @unlink(BMI_BACKUPS . '/.running');
2936 if (file_exists(BMI_BACKUPS . '/.cron')) @unlink(BMI_BACKUPS . '/.cron');
2937 if (file_exists(BMI_BACKUPS . '/.abort')) @unlink(BMI_BACKUPS . '/.abort');
2938 if ($isCLI === true && file_exists($cli_lock)) @unlink($cli_lock);
2939
2940 // Return
2941 Logger::log(__("New backup created and its name is: ", 'backup-backup') . $name);
2942
2943 $GLOBALS['bmi_error_handled'] = true;
2944
2945 $this->actionsAfterProcess(true, 'backup', $cron);
2946 return ['status' => 'success', 'filename' => $name, 'root' => plugin_dir_url(BMI_ROOT_FILE)];
2947
2948 }
2949
2950 public function continueRestoreProcess() {
2951
2952 // BMI_RESTORE_SECRET
2953
2954 }
2955
2956 public function getBackupsList() {
2957
2958 // Require File Scanner
2959 require_once BMI_INCLUDES . '/scanner/backups.php';
2960
2961 // Get backups
2962 $backups = Backups::getInstance();
2963 $manifests = $backups->getAvailableBackups();
2964
2965 // Get ongoing backups
2966 $manifests['ongoing'] = get_option('bmip_to_be_uploaded', [
2967 'current_upload' => [],
2968 'queue' => [],
2969 'failed' => []
2970 ]);
2971
2972 // Return files
2973 return ['status' => 'success', 'backups' => $manifests];
2974 }
2975
2976 public function sendTestMail() {
2977
2978 $email = Dashboard\bmi_get_config('OTHER:EMAIL') != false ? Dashboard\bmi_get_config('OTHER:EMAIL') : get_bloginfo('admin_email');
2979 $subject = __('Backup Migration – Example email', 'backup-backup');
2980 $message = __('This is a test email sent by the Backup Migration plugin via Troubleshooting options!', 'backup-backup');
2981
2982 try {
2983
2984 if (wp_mail($email, $subject, $message)) return [ 'status' => 'success' ];
2985 else return ['status' => 'error'];
2986
2987 } catch (\Exception $e) {
2988
2989 return ['status' => 'error'];
2990
2991 } catch (\Throwable $e) {
2992
2993 return ['status' => 'error'];
2994
2995 }
2996
2997 }
2998
2999 public function restoreBackup() {
3000
3001 global $wp_version;
3002
3003 if ($this->isFunctionEnabled('ini_set')) {
3004 ini_set('display_errors', 1);
3005 ini_set('error_reporting', E_ALL);
3006 ini_set('log_errors', 1);
3007 ini_set('error_log', BMI_CONFIG_DIR . DIRECTORY_SEPARATOR . 'complete_logs.' . BMI_LOGS_SUFFIX . '.log');
3008 }
3009
3010
3011 // Double check for .space_check file
3012 if (file_exists(BMI_BACKUPS . '/.space_check')) @unlink(BMI_BACKUPS . '/.space_check');
3013
3014 // Require File Scanner
3015 require_once BMI_INCLUDES . '/zipper/zipping.php';
3016 require_once BMI_INCLUDES . '/extracter/extract.php';
3017 require_once BMI_INCLUDES . '/progress/migration.php';
3018 require_once BMI_INCLUDES . '/check/checker.php';
3019
3020
3021 // Progress & lock file
3022 $lock = BMI_BACKUPS . '/.migration_lock';
3023 $lock_cli = BMI_BACKUPS . '/.migration_lock_cli';
3024 $autologin_file = BMI_BACKUPS . '/.autologin';
3025 $lock_cli_end = BMI_BACKUPS . '/.migration_lock_ended';
3026 $progress = BMI_BACKUPS . '/latest_migration_progress.log';
3027 $cli_last_download = BMI_BACKUPS . '/.cli_download_last';
3028
3029 $ignoreRunCheck = ((isset($this->post['ignoreRunning']) && $this->post['ignoreRunning'] == 'true') ? true : false);
3030 $isCLIRunning = (defined('BMI_USING_CLI_FUNCTIONALITY') && BMI_USING_CLI_FUNCTIONALITY === true) ? true : false;
3031 if ($isCLIRunning) $ignoreRunCheck = false;
3032
3033 if (file_exists($lock) && (time() - filemtime($lock)) < 65 && !$ignoreRunCheck) {
3034 return ['status' => 'msg', 'why' => __('The restore process is currently running, please wait till it end or once the lock file expire.', 'backup-backup'), 'level' => 'warning'];
3035 }
3036
3037 // Check if download was via CLI
3038 if ($this->post['file'] == '.cli_download' && file_exists($cli_last_download)) {
3039 $this->post['file'] = file_get_contents($cli_last_download);
3040 if (file_exists($cli_last_download)) @unlink($cli_last_download);
3041 }
3042
3043 // Logs
3044 $migration = new MigrationProgress($this->post['remote']);
3045 $migration->start();
3046
3047 if ($ignoreRunCheck) {
3048
3049 $migration->mute();
3050
3051 }
3052
3053 // Check if password protected and if we have password
3054 require_once BMI_INCLUDES . '/zipper/zipping.php';
3055 $isProtected = false;
3056 $backupFilePath = BMI_BACKUPS . '/' . $this->post['file'];
3057 if (file_exists($backupFilePath)) {
3058 $isProtected = Zipper::isZipProtected($backupFilePath);
3059 }
3060 $hasPassword = isset($this->post['options']['password']) && !empty($this->post['options']['password']);
3061
3062 // Check PHP CLI
3063 if ((!defined('BMI_USING_CLI_FUNCTIONALITY') || BMI_USING_CLI_FUNCTIONALITY === false) && (!defined('BMI_CLI_REQUEST') || BMI_CLI_REQUEST === false) && (!$isProtected || $hasPassword)) {
3064
3065 $cli_result = $this->checkIfPHPCliExist($migration);
3066
3067 if ($cli_result !== false) {
3068
3069 $cliHandler = trailingslashit(sanitize_text_field(BMI_INCLUDES)) . 'cli-handler.php';
3070 $backupName = escapeshellarg($this->post['file']);
3071 if ( ! preg_match( '/^[A-Za-z0-9._-]+\.(zip|tar|tar\.gz)$/', $backupName ) ) {
3072 return ['status' => 'msg', 'why' => __('Invalid backup file name.', 'backup-backup'), 'level' => 'error'];
3073 }
3074 if ( ! file_exists( BMI_BACKUPS . '/' . $backupName ) ) {
3075 return ['status' => 'msg', 'why' => __('Backup not found.', 'backup-backup'), 'level' => 'error'];
3076 }
3077 $remoteType = 'false';
3078 if ($this->post['remote'] == 'true' || $this->post['remote'] === true) $remoteType = 'true';
3079 $cliArg2 = ($isProtected && $hasPassword) ? escapeshellarg($this->post['options']['password']) : $remoteType;
3080 if (file_exists($lock_cli_end)) @unlink($lock_cli_end);
3081
3082 $res = null;
3083 @exec(BMI_CLI_EXECUTABLE . ' -f "' . $cliHandler . '" bmi_restore ' . $backupName . ' ' . $cliArg2 . ' > /dev/null &', $res);
3084 $res = implode("\n", $res);
3085
3086 sleep(3);
3087
3088 if (file_exists($lock_cli_end) && (time() - filemtime($lock_cli_end)) < 10) {
3089
3090 // Put autologin
3091 $autologinToken = $this->generateAutologinData();
3092
3093 return ['status' => 'cli', 'login' => $autologinToken, 'url' => site_url()];
3094 exit;
3095
3096 }
3097
3098 if (!file_exists($lock_cli) || (time() - filemtime($lock_cli)) > 10) {
3099
3100 $progressFile = null;
3101 $migration->log(__('No response from PHP CLI - plugin will try to recover the migration with traditional restore.', 'backup-backup'), 'warn');
3102 if (file_exists($lock_cli)) @unlink($lock_cli);
3103
3104 } else {
3105
3106 $progressFile = null;
3107
3108 // $migration->log(__('PHP CLI responded with correct code - we will continue via PHP CLI.', 'backup-backup'), 'info');
3109 // $migration->end();
3110 $token = $this->generateAutologinData();
3111
3112 return ['status' => 'cli', 'login' => $token, 'url' => site_url()];
3113 exit;
3114
3115 }
3116
3117 } else {
3118
3119 if (file_exists($lock_cli)) @unlink($lock_cli);
3120
3121 }
3122
3123 } else {
3124
3125 if (defined('BMI_USING_CLI_FUNCTIONALITY') && BMI_USING_CLI_FUNCTIONALITY === true) {
3126 $migration->log(__('PHP CLI: Restore process initialized, restoring...', 'backup-backup'), 'success');
3127 touch($lock_cli);
3128 } else {
3129 $migration->log(__('Restore process initialized, restoring (non-cli mode)...', 'backup-backup'), 'success');
3130 }
3131
3132 }
3133
3134 // Just in case (e.g. syntax error, we can close the file correctly)
3135 $GLOBALS['bmi_migration_progress'] = $migration;
3136
3137 // Checker
3138 $checker = new Checker($migration);
3139 $zipper = new Zipper();
3140
3141 // Handle remote
3142 if ($this->post['file']) {
3143 $migration->log(__('Restore process responded', 'backup-backup'), 'SUCCESS');
3144 }
3145
3146 // Make lock file
3147 $migration->log(__('Locking migration process', 'backup-backup'), 'SUCCESS');
3148 touch($lock);
3149
3150 // Initializing
3151 $migration->log(__('Initializing restore process', 'backup-backup'), 'STEP');
3152 $migration->log((__("Backup & Migration version: ", 'backup-backup') . BMI_VERSION), 'info');
3153
3154 // Error handler
3155 $migration->log(__("Initializing custom error handler", 'backup-backup'), 'info');
3156
3157 // Error handler
3158 $this->migration_progress = &$migration;
3159 $this->migrationErrorHandler();
3160 $this->migrationExceptionHandler();
3161
3162 $homeURL = site_url();
3163 if (strlen($homeURL) <= 8) $homeURL = home_url();
3164 if (defined('WP_SITEURL') && strlen(WP_SITEURL) > 8) $homeURL = WP_SITEURL;
3165
3166 $migration->log(__("Site which will be restored: ", 'backup-backup') . $homeURL, 'info');
3167 $migration->log(__("Backup file which will be restored: ", 'backup-backup') . esc_attr($this->post['file']), 'verbose');
3168 $migration->log(__("PHP Version: ", 'backup-backup') . PHP_VERSION, 'info');
3169 $migration->log(__("WP Version: ", 'backup-backup') . $wp_version, 'info');
3170 $migration->log(__("MySQL Version: ", 'backup-backup') . $GLOBALS['wpdb']->db_version(), 'info');
3171 $maxAllowedPackets = $GLOBALS['wpdb']->get_results("SHOW VARIABLES LIKE 'max_allowed_packet';");
3172 if (sizeof($maxAllowedPackets) > 0) {
3173 $migration->log(__("MySQL Max Length: ", 'backup-backup') . $maxAllowedPackets[0]->Value, 'info');
3174 } else {
3175 $migration->log(__("MySQL Max Length: ", 'backup-backup') . 'Unknown', 'info');
3176 }
3177 if (isset($_SERVER['SERVER_SOFTWARE']) && !defined('BMI_USING_CLI_FUNCTIONALITY')) {
3178 $migration->log(__("Web server: ", 'backup-backup') . $_SERVER['SERVER_SOFTWARE'], 'info');
3179 } else {
3180 $migration->log(__("Web server: Not available", 'backup-backup'), 'info');
3181 }
3182 $migration->log(__("Max execution time (in seconds): ", 'backup-backup') . @ini_get('max_execution_time'), 'info');
3183
3184 $migration->log(__("Memory limit (server): ", 'backup-backup') . @ini_get('memory_limit'), 'info');
3185 if (defined('WP_MEMORY_LIMIT')) {
3186 $migration->log(__("Memory limit (wp-config): ", 'backup-backup') . WP_MEMORY_LIMIT, 'info');
3187 }
3188 if (defined('WP_MAX_MEMORY_LIMIT')) {
3189 $migration->log(__("Memory limit (wp-config admin): ", 'backup-backup') . WP_MAX_MEMORY_LIMIT, 'info');
3190 }
3191
3192 if (defined('BMI_BACKUP_PRO')) {
3193 if (BMI_BACKUP_PRO == 1) {
3194 $migration->log(__("Premium plugin is enabled and activated", 'backup-backup'), 'info');
3195 } else {
3196 $migration->log(__("Premium version is enabled but not active, using free plugin.", 'backup-backup'), 'warn');
3197 }
3198 }
3199
3200 $migration->log(__("Checking if backup is valid...", 'backup-backup'), 'STEP');
3201 $zippath = BMP::fixSlashes(BMI_BACKUPS) . DIRECTORY_SEPARATOR . $this->post['file'];
3202 $manifest = $zipper->getZipFileContent($zippath, 'bmi_backup_manifest.json');
3203 if ($manifest) {
3204 $isMultisite = false;
3205 $tablePrefix = $manifest->config->table_prefix;
3206 $multisiteTables = ['blogs', 'blogmeta', 'site', 'sitemeta', 'registration_log', 'signups', 'sitecategories', 'sitecategorymeta'];
3207 foreach ($multisiteTables as $msTable) {
3208 $fileExists = $zipper->isFileExists($zippath, 'db_tables/' . $tablePrefix . $msTable . '.sql');
3209 if ($fileExists) {
3210 $isMultisite = true;
3211 break;
3212 }
3213 }
3214
3215 $notCompatible = false;
3216 if (!$isMultisite && is_multisite()) {
3217 $notCompatible = true;
3218 $migration->log(__("Error: Backup is not multisite but current site is multisite.", 'backup-backup'), 'error');
3219 $migration->log('single_site_backup_on_multisite', 'verbose');
3220 } else if ($isMultisite && !is_multisite()) {
3221 $notCompatible = true;
3222 $migration->log(__("Error: Backup is multisite but current site is not multisite.", 'backup-backup'), 'error');
3223 $migration->log('multisite_backup_on_single_site', 'verbose');
3224 }
3225 if ($notCompatible) {
3226 $migration->log(__("Aborting...", 'backup-backup'), 'error');
3227 $migration->log(__("Unlocking migration", 'backup-backup'), 'INFO');
3228
3229 if (file_exists($lock)) @unlink($lock);
3230 $migration->log('#005', 'END-CODE');
3231 $migration->end();
3232
3233 if ($isCLIRunning == true) touch($lock_cli_end);
3234 $this->actionsAfterProcess(false, 'migration');
3235
3236 return ['status' => 'error'];
3237 }
3238
3239
3240 }
3241
3242 $migration->log(__("Restore process initialized successfully.", 'backup-backup'), 'success');
3243
3244 if (!$ignoreRunCheck) {
3245
3246 $migration->log(__('Free space checking...', 'backup-backup'), 'STEP');
3247 $migration->log(__('Checking if there is enough amount of free space', 'backup-backup'), 'INFO');
3248
3249 $isSpaceCheckDisabled = Dashboard\bmi_get_config('OTHER:BACKUP:SPACE:CHECKING');
3250
3251 if ($isSpaceCheckDisabled) {
3252 $migration->log(__("Free space checking is disabled by user in settings...", 'backup-backup'), 'warn');
3253 $migration->log(__("Restore will continue, trusting there is enough space...", 'backup-backup'), 'warn');
3254 } else {
3255 if ($manifest) {
3256 if (isset($manifest->bytes) && $manifest->bytes) {
3257 $bytes = intval($manifest->bytes * 1.4);
3258 update_option('bmi_required_space', $bytes);
3259 if (file_exists(BMI_TMP . DIRECTORY_SEPARATOR . 'restore_parts.json')) {
3260 $restoreParts = json_decode(file_get_contents(BMI_TMP . DIRECTORY_SEPARATOR . 'restore_parts.json'));
3261 if (isset($restoreParts->size) && $restoreParts->size && $restoreParts->backupName == $this->post['file']) {
3262 $bytes = intval($restoreParts->size * 1.4);
3263 }
3264 }
3265 if (!$checker->check_free_space($bytes)) {
3266 $migration->log(__('Cannot start migration process', 'backup-backup'), 'ERROR');
3267 $migration->log(__('Error: There is not enough space on the server, checked: ' . ($bytes) . ' bytes.', 'backup-backup'), 'ERROR');
3268 $migration->log("not_enough_space", 'verbose');
3269 $migration->log(__('Aborting...', 'backup-backup'), 'ERROR');
3270 $migration->log(__('Unlocking migration', 'backup-backup'), 'INFO');
3271
3272 if (file_exists($lock)) @unlink($lock);
3273 $migration->log('#004', 'END-CODE');
3274 $migration->end();
3275
3276 if ($isCLIRunning == true) touch($lock_cli_end);
3277 $this->actionsAfterProcess(false, 'migration');
3278
3279 return ['status' => 'error'];
3280 } else {
3281 $migration->log(__('Confirmed, there is enough space on the device, checked: ' . ($bytes) . ' bytes.', 'backup-backup'), 'SUCCESS');
3282 }
3283 }
3284 } else {
3285 $migration->log(__('Cannot start migration process', 'backup-backup'), 'ERROR');
3286 $migration->log(__('Error: File may not exist, check file name and if it still exist', 'backup-backup'), 'ERROR');
3287 $migration->log(__('Error: Could not find manifest in backup, file may be broken', 'backup-backup'), 'ERROR');
3288 $migration->log(__('Error: Btw. because of this I also cannot check free space', 'backup-backup'), 'ERROR');
3289 $migration->log(__('Used path: ', 'backup-backup') . $zippath, 'ERROR');
3290 $migration->log(__('Aborting...', 'backup-backup'), 'ERROR');
3291 $migration->log(__('Unlocking migration', 'backup-backup'), 'INFO');
3292
3293 if (file_exists($lock)) @unlink($lock);
3294 $migration->log('#003', 'END-CODE');
3295 $migration->end();
3296
3297 if ($isCLIRunning == true) touch($lock_cli_end);
3298 $this->actionsAfterProcess(false, 'migration');
3299
3300 return ['status' => 'error'];
3301 }
3302 }
3303
3304 }
3305
3306 if ($ignoreRunCheck) {
3307
3308 $migration->unmute();
3309
3310 }
3311
3312 // New extracter
3313 $theTmpName = ((isset($this->post['tmpname'])) ? $this->post['tmpname'] : false);
3314 $options = ((isset($this->post['options'])) ? $this->post['options'] : []);
3315 $extracter = new Extracter($this->post['file'], $migration, $theTmpName, $isCLIRunning, $options);
3316
3317 // Extract
3318 $theSecret = ((isset($this->post['secret'])) ? $this->post['secret'] : null);
3319 $isFine = $extracter->extractTo($theSecret);
3320 if (!$isFine) {
3321 $migration->log(__('Aborting...', 'backup-backup'), 'ERROR');
3322 $migration->log(__('Unlocking migration', 'backup-backup'), 'INFO');
3323
3324 if (file_exists($lock)) @unlink($lock);
3325 $migration->log('#002', 'END-CODE');
3326 $migration->end();
3327
3328 if ($isCLIRunning == true) touch($lock_cli_end);
3329 $this->actionsAfterProcess(false, 'migration');
3330
3331 return ['status' => 'error'];
3332 }
3333
3334 $migration->progress('100');
3335 $migration->log(__('Restore process completed', 'backup-backup'), 'SUCCESS');
3336 $migration->log(__('Finalizing restored files', 'backup-backup'), 'STEP');
3337 $migration->log(__('Unlocking migration', 'backup-backup'), 'INFO');
3338 if (file_exists($lock)) @unlink($lock);
3339
3340 $migration->log('#001', 'END-CODE');
3341 $migration->end();
3342
3343 if ($isCLIRunning == true) touch($lock_cli_end);
3344
3345 // Put autologin
3346 $token = $this->generateAutologinData();
3347
3348 $this->actionsAfterProcess(true, 'migration');
3349 return ['status' => 'success', 'login' => $token, 'url' => site_url()];
3350 }
3351
3352 /**
3353 * Generates autologin data file with the following format: "{hashedToken}_{ip}_{time}"
3354 * @return string Returns token
3355 */
3356 private function generateAutologinData() {
3357 $time = time();
3358 $token = wp_generate_password(32, false);
3359 $ip = '127.0.0.1';
3360 if (isset($_SERVER['REMOTE_ADDR'])) $ip = $_SERVER['REMOTE_ADDR'];
3361
3362 $hashedToken = wp_hash($token, 'bmi_autologin');
3363 $md = "{$hashedToken}_{$ip}_{$time}";
3364 file_put_contents(BMI_BACKUPS . '/.autologin', $md);
3365 touch(BMI_BACKUPS . '/.autologin');
3366 return $token;
3367 }
3368
3369 public function isRunningBackup() {
3370 $this->lock_cli = BMI_BACKUPS . '/.backup_cli_lock';
3371
3372 // Ongoing processes
3373 $ongoing = get_option('bmip_to_be_uploaded', [
3374 'current_upload' => [],
3375 'queue' => [],
3376 'failed' => []
3377 ]);
3378
3379 // Backup CLI running
3380 if (file_exists($this->lock_cli) && (time() - filemtime($this->lock_cli)) <= 3600) {
3381 return ['status' => 'msg', 'why' => __('Backup process already running, please wait till it complete.', 'backup-backup'), 'level' => 'warning', 'ongoing' => $ongoing];
3382 }
3383
3384 if (file_exists(BMI_BACKUPS . '/.running') && (time() - filemtime(BMI_BACKUPS . '/.running')) <= 65) {
3385 return ['status' => 'msg', 'why' => __('Backup process already running, please wait till it complete.', 'backup-backup'), 'level' => 'warning', 'ongoing' => $ongoing];
3386 } else {
3387 return ['status' => 'success', 'ongoing' => $ongoing];
3388 }
3389 }
3390
3391 public function stopBackup() {
3392 if (!file_exists(BMI_BACKUPS . '/.running')) {
3393 return ['status' => 'msg', 'why' => __('Backup process completed or is not running.', 'backup-backup'), 'level' => 'info'];
3394 } else {
3395 if (!file_exists(BMI_BACKUPS . '/.abort')) {
3396 touch(BMI_BACKUPS . '/.abort');
3397 }
3398
3399 return ['status' => 'success'];
3400 }
3401 }
3402
3403 public function isMigrationLocked() {
3404 $lock = BMI_BACKUPS . '/.migration_lock';
3405 $lock_cli = BMI_BACKUPS . '/.migration_lock_cli';
3406 $lock_cli_end = BMI_BACKUPS . '/.migration_lock_ended';
3407
3408 if ((file_exists($lock) && (time() - filemtime($lock)) < 65) || (file_exists($lock_cli) && (time() - filemtime($lock_cli)) < 7200)) {
3409
3410 return ['status' => 'msg', 'why' => __('Restore process is currently running, please wait till it complete.', 'backup-backup'), 'level' => 'warning'];
3411
3412 } else {
3413
3414 require_once BMI_INCLUDES . '/progress/migration.php';
3415 $progress = BMI_BACKUPS . '/latest_migration_progress.log';
3416 $shouldClearLogs = true;
3417
3418 if (isset($this->post['clearLogs']) && $this->post['clearLogs'] == 'false') {
3419 $shouldClearLogs = false;
3420 }
3421
3422 if ($shouldClearLogs === true) {
3423 if (file_exists($lock_cli_end) && (time() - filemtime($lock_cli_end)) > 10) {
3424
3425 $migration = new MigrationProgress();
3426 $migration->start();
3427 $migration->log(__('Initializing restore process...', 'backup-backup'), 'STEP');
3428 $migration->end();
3429
3430 file_put_contents($progress, '0');
3431
3432 }
3433 }
3434
3435 return ['status' => 'success'];
3436
3437 }
3438 }
3439
3440 public function downloadFile($url, $dest, $progress, $lock, &$logger) {
3441 $current_percentage = 0;
3442 $previous_logged = 0;
3443 $fp = fopen($dest, 'w+');
3444
3445 $progressfile = $progress;
3446 $lockfile = $lock;
3447
3448 $ch = curl_init(rawurldecode($url));
3449 curl_setopt($ch, CURLOPT_TIMEOUT, 0);
3450
3451 curl_setopt($ch, CURLOPT_FILE, $fp);
3452 curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
3453 curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
3454 curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
3455
3456 curl_setopt($ch, CURLOPT_NOPROGRESS, false);
3457 curl_setopt($ch, CURLOPT_PROGRESSFUNCTION, function ($resource, $download_size, $downloaded) use (&$current_percentage, &$lockfile, &$progressfile, &$logger, &$previous_logged) {
3458 if ($download_size > 0) {
3459 $new_percentage = intval(($downloaded / $download_size) * 100);
3460
3461 if (intval($current_percentage) != intval($new_percentage)) {
3462 $logger->progress($new_percentage);
3463
3464 if ($current_percentage == 0 || ($new_percentage % 5 == 0) || $new_percentage > 99) {
3465 $logger->log(sprintf(__('Download progress: %s/%s MB (%s%%)', 'backup-backup'), round($downloaded / 1024 / 1024), round($download_size / 1024 / 1024), $new_percentage), 'INFO');
3466 $previous_logged = $new_percentage;
3467 }
3468
3469 $current_percentage = $new_percentage;
3470 }
3471 }
3472 });
3473
3474 curl_exec($ch);
3475 $this->lastCurlCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
3476
3477 $error_msg = false;
3478 if (curl_errno($ch)) {
3479 $error_msg = curl_error($ch);
3480 $curl_errno = curl_errno($ch);
3481 $fileSize = curl_getinfo($ch, CURLINFO_CONTENT_LENGTH_DOWNLOAD);
3482
3483 if ($curl_errno == CURLE_WRITE_ERROR || $curl_errno == CURLE_ABORTED_BY_CALLBACK) {
3484 $requiredSpace = $fileSize * 1.1; // Add 10% buffer
3485 update_option('bmi_required_space', $requiredSpace);
3486 $logger->log('not_enough_space', 'verbose');
3487 }
3488 }
3489
3490 if (is_resource($ch)) {
3491 curl_close($ch);
3492 }
3493 fclose($fp);
3494
3495 if ($error_msg) {
3496 return $error_msg;
3497 } else {
3498 return false;
3499 }
3500 }
3501
3502 public function handleQuickMigration() {
3503 $lock = BMI_BACKUPS . '/.migration_lock';
3504 if (file_exists($lock) && (time() - filemtime($lock)) < 65) {
3505 return ['status' => 'msg', 'why' => __('Download process is currently running, please wait till it complete.', 'backup-backup'), 'level' => 'warning'];
3506 }
3507
3508 require_once BMI_INCLUDES . '/progress/migration.php';
3509 require_once BMI_INCLUDES . '/zipper/zipping.php';
3510
3511 $migration = new MigrationProgress(true);
3512 $migration->start();
3513
3514 $tmp_name = 'backup_' . time() . '.zip.part';
3515
3516 // Missing URL parameter
3517 if (!isset($this->post['url'])) {
3518 wp_send_json_error();
3519 }
3520
3521 if (defined('BMI_USING_CLI_FUNCTIONALITY') && BMI_USING_CLI_FUNCTIONALITY === true && defined('BMI_CLI_ARGUMENT')) {
3522
3523 $url = BMI_CLI_ARGUMENT;
3524
3525 } else {
3526
3527 $url = $this->post['url'];
3528 $startRestoreProcess = isset($this->post['startRestoreProcess']) ? $this->post['startRestoreProcess'] : 'true';
3529
3530 $url = trim(rawurlencode(sanitize_url($url, ['http', 'https']))); // or esc_attr but rawurlencode should be fine
3531
3532 // Just why not {
3533 $url = str_replace(' ', '', $url);
3534 $url = str_replace('$', '%24', $url);
3535 $url = str_replace('`', '%60', $url);
3536 $url = str_replace('"', '%22', $url);
3537 $url = str_replace('\\', '%5C', $url);
3538 $url = str_replace('&amp;', '&', $url);
3539 // }
3540
3541 }
3542
3543 $dest = BMI_BACKUPS . '/' . $tmp_name;
3544 $progress = BMI_BACKUPS . '/latest_migration_progress.log';
3545 $cli_lock = BMI_BACKUPS . '/.cli_download_lock';
3546
3547 if (!defined('BMI_USING_CLI_FUNCTIONALITY') || BMI_USING_CLI_FUNCTIONALITY === false) {
3548
3549 $cli_result = $this->checkIfPHPCliExist($migration);
3550 if ($cli_result !== false) {
3551
3552 $cliHandler = trailingslashit(sanitize_text_field(BMI_INCLUDES)) . 'cli-handler.php';
3553
3554 $res = null;
3555 @exec(BMI_CLI_EXECUTABLE . ' -f "' . $cliHandler . '" bmi_quick_migration ' . escapeshellarg($url) . ' > /dev/null &', $res);
3556 $res = implode("\n", $res);
3557
3558 sleep(2);
3559 if (file_exists($cli_lock) && (time() - filemtime($cli_lock)) < 10) {
3560
3561 if (file_exists($cli_lock)) @unlink($cli_lock);
3562 return [ 'status' => 'cli_download' ];
3563 exit;
3564
3565 }
3566
3567 }
3568
3569 } else {
3570
3571 $migration->log(__('Downloading via PHP CLI', 'backup-backup'));
3572 touch($cli_lock);
3573
3574 }
3575
3576 $migration->log((__("Backup & Migration version: ", 'backup-backup') . BMI_VERSION));
3577 $migration->log(__('Creating lock file', 'backup-backup'));
3578 file_put_contents($lock, '');
3579 $migration->log(__('Initializing download process', 'backup-backup'), 'STEP');
3580 $downstart = microtime(true);
3581 $migration->log(__('Downloading initialized', 'backup-backup'), 'SUCCESS');
3582 $migration->log(__('Downloading remote file...', 'backup-backup'), 'STEP');
3583 $migration->log(__('Used URL: ', 'backup-backup') . rawurldecode($url), 'INFO');
3584 $fileError = $this->downloadFile($url, $dest, $progress, $lock, $migration);
3585 $migration->log(__('Unlocking migration', 'backup-backup'), 'INFO');
3586 if (file_exists($lock)) @unlink($lock);
3587
3588 if ($fileError) {
3589 $migration->log(__('Removing downloaded file', 'backup-backup'), 'INFO');
3590 if (file_exists($dest)) @unlink($dest);
3591 $migration->log(__('Download error', 'backup-backup'), 'ERROR');
3592
3593 if (strpos($fileError, 'Failed writing body') !== false) {
3594 $migration->log(__('Error: There is not enough space on the server', 'backup-backup'), 'ERROR');
3595 $migration->log("not_enough_space", 'verbose');
3596 } else {
3597 $migration->log(__('Error', 'backup-backup') . ': ' . $fileError, 'ERROR');
3598 }
3599
3600 $migration->log('error_during_downloading_backup', 'verbose');
3601 $migration->log('error_during_downloading_backup', 'verbose');
3602 $migration->log('#002', 'END-CODE');
3603 return ['status' => 'error'];
3604 } else {
3605 $migration->log(__('Download completed (took: ', 'backup-backup') . (microtime(true) - $downstart) . 's)', 'SUCCESS');
3606 $migration->log(__('Looking for backup manifest', 'backup-backup'), 'STEP');
3607 $zipper = new Zipper();
3608 $content = $zipper->getZipFileContent($dest, 'bmi_backup_manifest.json');
3609 if ($content) {
3610 try {
3611 $i = 1;
3612 $name = $content->name;
3613 $prepared_name = $name;
3614 $migration->log(__('Manifest found remote name: ', 'backup-backup') . $name, 'SUCCESS');
3615
3616 while (file_exists(BMI_BACKUPS . '/' . $prepared_name)) {
3617 $prepared_name = substr($name, 0, -4) . '_' . $i . '.zip';
3618 $i++;
3619 }
3620
3621 rename($dest, BMI_BACKUPS . '/' . $prepared_name);
3622 $migration->log(__('Requesting restore process', 'backup-backup'), 'STEP');
3623 $migration->progress(0);
3624 file_put_contents(BMI_BACKUPS . '/' . '.cli_download_last', $prepared_name);
3625 if ($startRestoreProcess == 'true'){
3626 $migration->log('#205', 'END-CODE');
3627 } else {
3628 $migration->log('#206', 'END-CODE');
3629 }
3630
3631 if (defined('BMI_USING_CLI_FUNCTIONALITY')) {
3632 $this->post['file'] = '.cli_download';
3633 $this->post['remote'] = true;
3634 return $this->restoreBackup();
3635 } else {
3636 return ['status' => 'success', 'name' => $prepared_name];
3637 }
3638 } catch (\Exception $e) {
3639 $migration->log(__('Error: ', 'backup-backup') . $e, 'ERROR');
3640 $migration->log(__('Removing downloaded file', 'backup-backup'), 'ERROR');
3641 if (file_exists($dest)) @unlink($dest);
3642
3643 $migration->log('error_during_downloading_backup', 'verbose');
3644 $migration->log('error_during_downloading_backup', 'verbose');
3645 $migration->log('#002', 'END-CODE');
3646 return ['status' => 'error'];
3647 } catch (\Throwable $e) {
3648 $migration->log(__('Error: ', 'backup-backup') . $e, 'ERROR');
3649 $migration->log(__('Removing downloaded file', 'backup-backup'), 'ERROR');
3650 if (file_exists($dest)) @unlink($dest);
3651
3652 $migration->log('error_during_downloading_backup', 'verbose');
3653 $migration->log('error_during_downloading_backup', 'verbose');
3654 $migration->log('#002', 'END-CODE');
3655 return ['status' => 'error'];
3656
3657 }
3658
3659 } else {
3660
3661 // $migration->log(__('Error during manifest check: ', 'backup-backup') . print_r($content, true), 'ERROR');
3662 if ($this->lastCurlCode == '403') {
3663 $migration->log(__('Backup is not available to download (Error 403).', 'backup-backup'), 'ERROR');
3664 $migration->log(__('It is restricted by remote server configuration.', 'backup-backup'), 'ERROR');
3665 } elseif ($this->lastCurlCode == '423') {
3666 $migration->log(__('Backup is locked on remote site, please unlock remote downloading.', 'backup-backup'), 'ERROR');
3667 $migration->log(__('You can find the setting in "Where shall the backup(s) be stored?" section.', 'backup-backup'), 'ERROR');
3668 } elseif ($this->lastCurlCode == '200' || $this->lastCurlCode == '404') {
3669 $migration->log(__('Backup does not exist under provided URL.', 'backup-backup'), 'ERROR');
3670 $migration->log(__('Please confirm that you can download the backup file via provided URL.', 'backup-backup'), 'ERROR');
3671 $migration->log(__('...or the manifest file does not exist in the backup.', 'backup-backup'), 'ERROR');
3672 $migration->log(__('Missing manifest means that the backup is probably invalid.', 'backup-backup'), 'ERROR');
3673 } else {
3674 $migration->log(__('Manifest file does not exist', 'backup-backup'), 'ERROR');
3675 $migration->log(__('Downloaded backup may be incomplete (missing manifest)', 'backup-backup'), 'ERROR');
3676 $migration->log(__('...or provided URL is not a direct download of ZIP file.', 'backup-backup'), 'ERROR');
3677 $migration->log(__('Removing downloaded file', 'backup-backup'), 'ERROR');
3678 }
3679
3680 if (file_exists($dest)) @unlink($dest);
3681
3682 $migration->log('error_during_downloading_backup', 'verbose');
3683 $migration->log('error_during_downloading_backup', 'verbose');
3684 $migration->log('#002', 'END-CODE');
3685 return ['status' => 'error'];
3686
3687 }
3688 }
3689 }
3690
3691 public function handleChunkUpload() {
3692 require_once BMI_INCLUDES . '/uploader/chunks.php';
3693 }
3694
3695 public function removeBackupFile() {
3696 $backups = $this->post['backups']; // ['filename' => ['hash' => 'hash', 'isCloud' => true/false]]
3697 $deleteCloud = $this->post['deleteCloud'] === 'yes' ? true : false;
3698
3699 $startTime = time();
3700
3701 require_once BMI_INCLUDES . '/scanner/backups.php';
3702
3703 $backupsScanner = Backups::getInstance();
3704
3705
3706 try {
3707 if (is_array($backups)) {
3708 foreach( $backups as $fileName => $data ) {
3709 $backupsScanner->deleteBackup($fileName, $data, $deleteCloud);
3710
3711 unset($backups[$fileName]);
3712
3713 $elapsedTime = time() - $startTime;
3714 $originalCount = is_array($this->post['backups']) ? count($this->post['backups']) : 0;
3715 $processedCount = $originalCount - count($backups);
3716
3717 if (count($backups) > 0 && ($elapsedTime >= 3 || $processedCount >= 5)) {
3718 return ['status' => 'continue', 'remaining' => $backups];
3719 }
3720
3721 }
3722 }
3723 } catch (\Exception $e) {
3724 return ['status' => 'error', 'e' => $e];
3725 } catch (\Throwable $e) {
3726 return ['status' => 'error', 'e' => $e];
3727 }
3728
3729 return ['status' => 'success'];
3730 }
3731
3732 public function saveStorageConfig() {
3733 $dir_path = $this->post['directory']; // STORAGE::LOCAL::PATH
3734 $accessible = $this->post['access']; // STORAGE::DIRECT::URL
3735 $gdrivedirname = 'BACKUP_MIGRATION_BACKUPS'; // STORAGE::EXTERNAL::GDRIVE::DIRNAME // $this->post['gdrivedirname']
3736 $curr_path = Dashboard\bmi_get_config('STORAGE::LOCAL::PATH');
3737
3738 $errors = 0;
3739 $created = false;
3740
3741 if ($dir_path == WP_CONTENT_DIR . DIRECTORY_SEPARATOR . 'backup-migration') {
3742 return ['status' => 'msg', 'why' => __('For security reasons, please add a random string to the end of the default backup directory path. This will help protect your backup files from unauthorized access.', 'backup-backup'), 'level' => 'warning'];
3743 }
3744
3745 if (!preg_match("/^[a-zA-Z0-9\_\ \-\/\.]+$/", $dir_path)) {
3746 return ['status' => 'msg', 'why' => __('Entered directory/path name does not match allowed characters (Local Storage).', 'backup-backup'), 'level' => 'warning'];
3747 }
3748
3749 if (!is_string($dir_path) || $dir_path === '' ||
3750 !(preg_match('/^[A-Z]:[\/\\\\]/i', $dir_path) || strpos($dir_path, '/') === 0)) {
3751 return ['status' => 'msg', 'why' => __('Please enter full path to the directory (Local Storage).', 'backup-backup'), 'level' => 'warning'];
3752 }
3753 if (!file_exists($dir_path)) {
3754 $created = @mkdir($dir_path, 0755, true);
3755 }
3756
3757
3758 if (isset($this->post['dropbox'])) {
3759 $dropboxenabled = $this->post['dropbox'];
3760 if (!Dashboard\bmi_set_config('STORAGE::EXTERNAL::DROPBOX', $dropboxenabled)) {
3761 $errors++;
3762 }
3763 }
3764
3765 if (isset($this->post['gdrive'])) {
3766 $gdriveenabled = $this->post['gdrive'];
3767 if (!Dashboard\bmi_set_config('STORAGE::EXTERNAL::GDRIVE', $gdriveenabled)) {
3768 $errors++;
3769 }
3770
3771 if (isset($this->post['gdrivedirname'])) {
3772 $gdrivedirname = $this->post['gdrivedirname'];
3773
3774 if (!preg_match("/^[a-zA-Z0-9\_\-\.]+$/", $gdrivedirname)) {
3775 return ['status' => 'msg', 'why' => __('Entered directory name does not match allowed characters (Google Drive).', 'backup-backup'), 'level' => 'warning'];
3776 }
3777
3778 if (strlen(trim($gdrivedirname)) < 3) {
3779 return ['status' => 'msg', 'why' => __('Entered directory name is too short, min 3 characters (Google Drive).', 'backup-backup'), 'level' => 'warning'];
3780 }
3781
3782 if (strlen(trim($gdrivedirname)) > 48) {
3783 return ['status' => 'msg', 'why' => __('Entered directory name is too long, max 48 characters (Google Drive).', 'backup-backup'), 'level' => 'warning'];
3784 }
3785
3786 if (!Dashboard\bmi_set_config('STORAGE::EXTERNAL::GDRIVE::DIRNAME', $gdrivedirname)) {
3787 $errors++;
3788 }
3789 }
3790 }
3791
3792 if (isset($this->post['ftp'])) {
3793 $ftpenabled = $this->post['ftp'];
3794 if (!Dashboard\bmi_set_config('STORAGE::EXTERNAL::FTP', $ftpenabled)) {
3795 $errors++;
3796 }
3797
3798 if ($ftpenabled != "false"){
3799 if (isset($this->post['ftphostip'])) {
3800 $ftpiphost = $this->post['ftphostip'];
3801 update_option('bmi_pro_ftp_host', $ftpiphost);
3802 }
3803
3804 if (isset($this->post['ftphostusername'])) {
3805 $ftpHostUsername = $this->post['ftphostusername'];
3806 update_option('bmi_pro_ftp_username', $ftpHostUsername);
3807 }
3808
3809 if (isset($this->post['ftppassword'])) {
3810 $ftpHostPassword = $this->post['ftppassword'];
3811 if (!empty($ftpHostPassword) && is_string($ftpHostPassword) && strlen(trim($ftpHostPassword)) > 0)
3812 update_option('bmi_pro_ftp_password', $ftpHostPassword);
3813 }
3814
3815 if (isset($this->post['ftpport'])) {
3816 $ftpHostPort = $this->post['ftpport'];
3817 update_option('bmi_pro_ftp_port', $ftpHostPort);
3818 }
3819
3820 if (isset($this->post['ftpdir'])) {
3821 $ftpHostDir = $this->post['ftpdir'];
3822 update_option('bmi_pro_ftp_backup_dir', $ftpHostDir);
3823 }
3824 } else {
3825 delete_option('bmi_pro_ftp_host');
3826 delete_option('bmi_pro_ftp_username');
3827 delete_option('bmi_pro_ftp_password');
3828 }
3829
3830 } else {
3831 delete_option('bmi_pro_ftp_host');
3832 delete_option('bmi_pro_ftp_username');
3833 delete_option('bmi_pro_ftp_password');
3834 }
3835
3836 if (isset($this->post['aws'])) {
3837 $s3enabled = $this->post['aws'];
3838 if (!Dashboard\bmi_set_config('STORAGE::EXTERNAL::AWS', $s3enabled)) {
3839 $errors++;
3840 }
3841 }
3842
3843 if (isset($this->post['wasabi'])) {
3844 $wasabienabled = $this->post['wasabi'];
3845 if (!Dashboard\bmi_set_config('STORAGE::EXTERNAL::WASABI', $wasabienabled)) {
3846 $errors++;
3847 }
3848 }
3849
3850 if (defined('BMI_BACKUP_PRO') && BMI_BACKUP_PRO === 1) {
3851
3852 if (isset($this->post['onedrive'])) {
3853 $onedriveenabled = $this->post['onedrive'];
3854 if (!Dashboard\bmi_set_config('STORAGE::EXTERNAL::ONEDRIVE', $onedriveenabled)) {
3855 $errors++;
3856 }
3857 }
3858
3859 if (isset($this->post['sftp'])) {
3860 $sftpenabled = $this->post['sftp'];
3861 if (!Dashboard\bmi_set_config('STORAGE::EXTERNAL::SFTP', $sftpenabled)) {
3862 $errors++;
3863 }
3864
3865 }
3866
3867 if (isset($this->post['pcloud'])) {
3868 $pcloudenabled = $this->post['pcloud'];
3869 if (!Dashboard\bmi_set_config('STORAGE::EXTERNAL::PCLOUD', $pcloudenabled)) {
3870 $errors++;
3871 }
3872 }
3873
3874
3875 }
3876
3877 if (is_writable($dir_path)) {
3878 if (!Dashboard\bmi_set_config('STORAGE::DIRECT::URL', $accessible)) {
3879 Logger::error('Backup Storage Direct Url Error');
3880 $errors++;
3881 }
3882 if (!Dashboard\bmi_set_config('STORAGE::LOCAL::PATH', esc_attr($dir_path))) {
3883 Logger::error('Backup Storage Local Path Error');
3884 $errors++;
3885 } else {
3886 $cur_dir = BMP::fixSlashes($curr_path);
3887 $new_dir = BMP::fixSlashes($dir_path);
3888
3889 $backups_cur_dir = BMP::fixSlashes($curr_path) . DIRECTORY_SEPARATOR . 'backups';
3890 $backups_new_dir = BMP::fixSlashes($dir_path) . DIRECTORY_SEPARATOR . 'backups';
3891
3892 $staging_cur_dir = BMP::fixSlashes($curr_path) . DIRECTORY_SEPARATOR . 'staging';
3893 $staging_new_dir = BMP::fixSlashes($dir_path) . DIRECTORY_SEPARATOR . 'staging';
3894
3895 $tmp_cur_dir = BMP::fixSlashes($curr_path) . DIRECTORY_SEPARATOR . 'tmp';
3896 $tmp_new_dir = BMP::fixSlashes($dir_path) . DIRECTORY_SEPARATOR . 'tmp';
3897
3898 update_option('BMI::STORAGE::LOCAL::PATH', $new_dir);
3899
3900 if ($cur_dir != $new_dir) {
3901
3902 if (!file_exists($new_dir)) @mkdir($new_dir, 0755, true);
3903 if (!file_exists($backups_new_dir)) @mkdir($backups_new_dir, 0755, true);
3904 if (!file_exists($staging_new_dir)) @mkdir($staging_new_dir, 0755, true);
3905 if (!file_exists($tmp_new_dir)) @mkdir($tmp_new_dir, 0755, true);
3906
3907 $scanned_directory_staging = array_diff(scandir($staging_cur_dir), ['..', '.']);
3908 foreach ($scanned_directory_staging as $i => $file) {
3909 if (file_exists($staging_cur_dir . DIRECTORY_SEPARATOR . $file) && !is_dir($staging_cur_dir . DIRECTORY_SEPARATOR . $file)) {
3910 rename($staging_cur_dir . DIRECTORY_SEPARATOR . $file, $staging_new_dir . DIRECTORY_SEPARATOR . $file);
3911 }
3912 }
3913
3914 $scanned_directory_tmp = array_diff(scandir($tmp_cur_dir), ['..', '.']);
3915 foreach ($scanned_directory_tmp as $i => $file) {
3916 if (file_exists($tmp_cur_dir . DIRECTORY_SEPARATOR . $file) && !is_dir($tmp_cur_dir . DIRECTORY_SEPARATOR . $file)) {
3917 rename($tmp_cur_dir . DIRECTORY_SEPARATOR . $file, $tmp_new_dir . DIRECTORY_SEPARATOR . $file);
3918 }
3919 }
3920
3921 $scanned_directory_backups = array_diff(scandir($backups_cur_dir), ['..', '.']);
3922 foreach ($scanned_directory_backups as $i => $file) {
3923 if (file_exists($backups_cur_dir . DIRECTORY_SEPARATOR . $file) && !is_dir($backups_cur_dir . DIRECTORY_SEPARATOR . $file)) {
3924 rename($backups_cur_dir . DIRECTORY_SEPARATOR . $file, $backups_new_dir . DIRECTORY_SEPARATOR . $file);
3925 }
3926 }
3927
3928 $scanned_directory = array_diff(scandir($cur_dir), ['..', '.']);
3929 foreach ($scanned_directory as $i => $file) {
3930 if (file_exists($cur_dir . DIRECTORY_SEPARATOR . $file) && !is_dir($cur_dir . DIRECTORY_SEPARATOR . $file)) {
3931 rename($cur_dir . DIRECTORY_SEPARATOR . $file, $new_dir . DIRECTORY_SEPARATOR . $file);
3932 }
3933 }
3934
3935 if (file_exists($backups_cur_dir . DIRECTORY_SEPARATOR . '.htaccess')) @unlink($backups_cur_dir . DIRECTORY_SEPARATOR . '.htaccess');
3936 if (file_exists($backups_cur_dir . DIRECTORY_SEPARATOR . 'index.php')) @unlink($backups_cur_dir . DIRECTORY_SEPARATOR . 'index.php');
3937 if (file_exists($backups_cur_dir . DIRECTORY_SEPARATOR . 'index.html')) @unlink($backups_cur_dir . DIRECTORY_SEPARATOR . 'index.html');
3938 if (file_exists($backups_cur_dir)) @rmdir($backups_cur_dir);
3939
3940 if (file_exists($staging_cur_dir . DIRECTORY_SEPARATOR . '.htaccess')) @unlink($staging_cur_dir . DIRECTORY_SEPARATOR . '.htaccess');
3941 if (file_exists($staging_cur_dir . DIRECTORY_SEPARATOR . 'index.php')) @unlink($staging_cur_dir . DIRECTORY_SEPARATOR . 'index.php');
3942 if (file_exists($staging_cur_dir . DIRECTORY_SEPARATOR . 'index.html')) @unlink($staging_cur_dir . DIRECTORY_SEPARATOR . 'index.html');
3943 if (file_exists($staging_cur_dir)) @rmdir($staging_cur_dir);
3944
3945 if (file_exists($tmp_cur_dir . DIRECTORY_SEPARATOR . '.htaccess')) @unlink($tmp_cur_dir . DIRECTORY_SEPARATOR . '.htaccess');
3946 if (file_exists($tmp_cur_dir . DIRECTORY_SEPARATOR . 'index.php')) @unlink($tmp_cur_dir . DIRECTORY_SEPARATOR . 'index.php');
3947 if (file_exists($tmp_cur_dir . DIRECTORY_SEPARATOR . 'index.html')) @unlink($tmp_cur_dir . DIRECTORY_SEPARATOR . 'index.html');
3948 if (file_exists($tmp_cur_dir)) @rmdir($tmp_cur_dir);
3949
3950 if (file_exists($cur_dir . DIRECTORY_SEPARATOR . 'complete_logs.' . BMI_LOGS_SUFFIX . '.log')) @unlink($cur_dir . DIRECTORY_SEPARATOR . 'complete_logs.' . BMI_LOGS_SUFFIX . '.log');
3951 if (file_exists($cur_dir)) @rmdir($cur_dir);
3952
3953 if (is_dir($cur_dir) && file_exists($cur_dir)) {
3954 $left_files = array_diff(scandir($cur_dir), ['..', '.']);
3955 if (sizeof($left_files) == 0) {
3956 if (file_exists($cur_dir)) {
3957 @rmdir($cur_dir);
3958 }
3959 }
3960 }
3961
3962 }
3963 }
3964 } else {
3965 if ($created === true) {
3966 if (file_exists($dir_path)) @unlink($dir_path);
3967 }
3968
3969 return ['status' => 'msg', 'why' => __('Entered path is not writable, cannot be used.', 'backup-backup'), 'level' => 'warning'];
3970 }
3971
3972 return ['status' => 'success', 'errors' => $errors];
3973 }
3974
3975 public function saveOtherOptions() {
3976
3977 // Errors
3978 $invalid_email = __('Provided email addess is not valid.', 'backup-backup');
3979 $title_long = __('Your email title is too long, please change the title (max 64 chars).', 'backup-backup');
3980 $title_short = __('Your email title is too short, please use longer one (at least 3 chars).', 'backup-backup');
3981 $title_empty = __('Title field is required, please fill it.', 'backup-backup');
3982 $email_empty = __('Email field cannot be empty, please fill it.', 'backup-backup');
3983 $cli_no_exist = __('Path to executable that you provided for PHP CLI does not exist.', 'backup-backup');
3984 $db_query_too_low = __('The value for query amount cannot be smaller than 15.', 'backup-backup');
3985 $db_query_too_much = __('The value for query amount cannot be larger than 15000.', 'backup-backup');
3986 $db_sr_max_too_low = __('The value for search replace max page cannot be smaller than 10.', 'backup-backup');
3987 $db_sr_max_too_much = __('The value for search replace max page cannot be larger than 30000.', 'backup-backup');
3988 $fl_ex_max_too_low = __('The value for extraction limit cannot be smaller than 50.', 'backup-backup');
3989 $fl_ex_max_too_much = __('The value for extraction limit cannot be larger than 20000.', 'backup-backup');
3990
3991 $email = sanitize_email(trim($this->post['email'])); // OTHER:EMAIL
3992 $email_title = sanitize_text_field(trim($this->post['email_title'])); // OTHER:EMAIL:TITLE
3993 $schedule_issues = $this->post['schedule_issues'] === 'true' ? true : false; // OTHER:EMAIL:NOTIS
3994 $experiment_timeout = $this->post['experiment_timeout'] === 'true' ? true : false; // OTHER:EXPERIMENT:TIMEOUT
3995 $experiment_timeout_hard = $this->post['experimental_hard_timeout'] === 'true' ? true : false; // OTHER:EXPERIMENT:TIMEOUT:HARD
3996 $php_cli_manual_path = isset($this->post['php_cli_manual_path']) ? trim($this->post['php_cli_manual_path']) : ''; // OTHER:CLI:PATH
3997 $php_cli_disable_others = $this->post['php_cli_disable_others'] === 'true' ? true : false; // OTHER:CLI:DISABLE
3998 $normal_timeout = $this->post['normal_timeout'] === 'true' ? true : false; // OTHER:USE:TIMEOUT:NORMAL
3999 $insecure_download = $this->post['download_technique'] === 'true' ? true : false; // OTHER:DOWNLOAD:DIRECT
4000 $db_query_size = isset($this->post['db_queries_amount']) ? trim($this->post['db_queries_amount']) : '2000'; // OTHER:DB:QUERIES
4001 $db_search_replace_max = isset($this->post['db_search_replace_max']) ? trim($this->post['db_search_replace_max']) : '300'; // OTHER:DB:SEARCHREPLACE:MAX
4002 $file_limit_extraction_max = isset($this->post['file_limit_extraction_max']) ? trim($this->post['file_limit_extraction_max']) : 'auto'; // OTHER:FILE:EXTRACT:MAX
4003 $db_restore_splitting = $this->post['bmi-restore-splitting'] === 'true' ? true : false; // OTHER:RESTORE:SPLITTING
4004 $db_restore_v3_engine = $this->post['bmi-db-v3-restore-engine'] === 'true' ? true : false; // OTHER:RESTORE:DB:V3
4005
4006 $no_assets_b4_restore = $this->post['remove-assets-before-restore'] === 'true' ? true : false; // OTHER:RESTORE:BEFORE:CLEANUP
4007 $single_file_db_force = $this->post['bmi-db-single-file-backup'] === 'true' ? true : false; // OTHER:BACKUP:DB:SINGLE:FILE
4008 $db_batching_backup = $this->post['bmi-db-batching-backup'] === 'true' ? true : false; // OTHER:BACKUP:DB:BATCHING
4009
4010 $bmi_disable_space_check = $this->post['bmi-disable-space-check-function'] === 'true' ? true : false; // OTHER:BACKUP:SPACE:CHECKING
4011
4012 $uninstall_config = $this->post['uninstall_config'] === 'true' ? true : false; // OTHER:UNINSTALL:CONFIGS
4013 $uninstall_backups = $this->post['uninstall_backups'] === 'true' ? true : false; // OTHER:UNINSTALL:BACKUPS
4014 $use_new_search_replace_engine = $this->post['use_new_search_replace_engine'] === 'true' ? true : false; // OTHER::NEW_SEARCH_REPLACE_ENGINE
4015 $use_new_database_export_engine = $this->post['use_new_database_export_engine'] === 'true' ? true : false; // OTHER::NEW_DATABASE_EXPORT_ENGINE
4016
4017 if ($experiment_timeout_hard === true) {
4018 $experiment_timeout = false;
4019 }
4020
4021 if ($normal_timeout === true) {
4022 $experiment_timeout = false;
4023 $experiment_timeout_hard = false;
4024 }
4025
4026 if (!is_numeric($db_query_size) || empty($db_query_size)) {
4027 $db_query_size = "2000";
4028 }
4029
4030 if (!is_numeric($file_limit_extraction_max) || empty($file_limit_extraction_max)) {
4031 $file_limit_extraction_max = "auto";
4032 }
4033
4034 if (!is_numeric($db_search_replace_max) || empty($db_search_replace_max)) {
4035 $db_search_replace_max = "300";
4036 }
4037
4038 if (strlen($email) <= 0) {
4039 return ['status' => 'msg', 'why' => $email_empty, 'level' => 'warning'];
4040 }
4041 if (strlen($email_title) <= 0) {
4042 return ['status' => 'msg', 'why' => $title_empty, 'level' => 'warning'];
4043 }
4044 if (strlen($email_title) > 64) {
4045 return ['status' => 'msg', 'why' => $title_long, 'level' => 'warning'];
4046 }
4047 if (strlen($email_title) < 3) {
4048 return ['status' => 'msg', 'why' => $title_short, 'level' => 'warning'];
4049 }
4050 if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
4051 return ['status' => 'msg', 'why' => $invalid_email, 'level' => 'warning'];
4052 }
4053 if ($php_cli_manual_path != '' && !file_exists($php_cli_manual_path)) {
4054 return ['status' => 'msg', 'why' => $cli_no_exist, 'level' => 'warning'];
4055 }
4056 if (intval($db_query_size) > 15000) {
4057 return ['status' => 'msg', 'why' => $db_query_too_much, 'level' => 'warning'];
4058 }
4059 if (intval($db_query_size) < 15) {
4060 return ['status' => 'msg', 'why' => $db_query_too_low, 'level' => 'warning'];
4061 }
4062 if (intval($db_search_replace_max) > 30000) {
4063 return ['status' => 'msg', 'why' => $db_sr_max_too_much, 'level' => 'warning'];
4064 }
4065 if (intval($db_search_replace_max) < 10) {
4066 return ['status' => 'msg', 'why' => $db_sr_max_too_low, 'level' => 'warning'];
4067 }
4068 if ($file_limit_extraction_max != 'auto' && intval($file_limit_extraction_max) > 20000) {
4069 return ['status' => 'msg', 'why' => $fl_ex_max_too_much, 'level' => 'warning'];
4070 }
4071 if ($file_limit_extraction_max != 'auto' && intval($file_limit_extraction_max) < 50) {
4072 return ['status' => 'msg', 'why' => $fl_ex_max_too_low, 'level' => 'warning'];
4073 }
4074
4075 $error = 0;
4076 if (!Dashboard\bmi_set_config('OTHER:EMAIL', $email)) {
4077 Logger::error('Backup Other Email Error');
4078 $error++;
4079 }
4080 if (!Dashboard\bmi_set_config('OTHER:EMAIL:TITLE', $email_title)) {
4081 Logger::error('Backup Other Email Title Error');
4082 $error++;
4083 }
4084 if (!Dashboard\bmi_set_config('OTHER:EMAIL:NOTIS', $schedule_issues)) {
4085 Logger::error('Backup Other Email Notis Error');
4086 $error++;
4087 }
4088 if (!Dashboard\bmi_set_config('OTHER:CLI:PATH', $php_cli_manual_path)) {
4089 Logger::error('Backup Other CLI Path Error');
4090 $error++;
4091 }
4092 if (!Dashboard\bmi_set_config('OTHER:CLI:DISABLE', $php_cli_disable_others)) {
4093 Logger::error('Backup Other CLI Disable Error');
4094 $error++;
4095 }
4096 if (!Dashboard\bmi_set_config('OTHER:EXPERIMENT:TIMEOUT', $experiment_timeout)) {
4097 Logger::error('Backup Other Experiment Timeout Error');
4098 $error++;
4099 }
4100 if (!Dashboard\bmi_set_config('OTHER:EXPERIMENT:TIMEOUT:HARD', $experiment_timeout_hard)) {
4101 Logger::error('Backup Other Experiment Timeout Hard Error');
4102 $error++;
4103 }
4104 if (!Dashboard\bmi_set_config('OTHER:USE:TIMEOUT:NORMAL', $normal_timeout)) {
4105 Logger::error('Backup Other Experiment Timeout Normal Error');
4106 $error++;
4107 }
4108 if (!Dashboard\bmi_set_config('OTHER:RESTORE:DB:V3', $db_restore_v3_engine)) {
4109 Logger::error('Backup Other Restore DB V3 Error');
4110 $error++;
4111 }
4112 if (!Dashboard\bmi_set_config('OTHER:DB:QUERIES', $db_query_size)) {
4113 Logger::error('Backup Other DB Queries Error');
4114 $error++;
4115 }
4116 if (!Dashboard\bmi_set_config('OTHER:DB:SEARCHREPLACE:MAX', $db_search_replace_max)) {
4117 Logger::error('Backup Other DB Queries Error');
4118 $error++;
4119 }
4120 if (!Dashboard\bmi_set_config('OTHER:FILE:EXTRACT:MAX', $file_limit_extraction_max)) {
4121 Logger::error('Backup Other File Extract Max Error');
4122 $error++;
4123 }
4124 if (!Dashboard\bmi_set_config('OTHER:DOWNLOAD:DIRECT', $insecure_download)) {
4125 Logger::error('Backup Other Download Direct Error');
4126 $error++;
4127 }
4128 if (!Dashboard\bmi_set_config('OTHER:UNINSTALL:CONFIGS', $uninstall_config)) {
4129 Logger::error('Backup Other Uninstall Configs Error');
4130 $error++;
4131 }
4132 if (!Dashboard\bmi_set_config('OTHER:UNINSTALL:BACKUPS', $uninstall_backups)) {
4133 Logger::error('Backup Other Uninstall Backups Error');
4134 $error++;
4135 }
4136 if (!Dashboard\bmi_set_config('OTHER:RESTORE:SPLITTING', $db_restore_splitting)) {
4137 Logger::error('Backup Other Restore Splitting Error');
4138 $error++;
4139 }
4140 if (!Dashboard\bmi_set_config('OTHER:BACKUP:DB:SINGLE:FILE', $single_file_db_force)) {
4141 Logger::error('Backup Other Backup DB Single File Error');
4142 $error++;
4143 }
4144 if (!Dashboard\bmi_set_config('OTHER:BACKUP:DB:BATCHING', $db_batching_backup)) {
4145 Logger::error('Backup Other Backup DB Batching Error');
4146 $error++;
4147 }
4148 if (!Dashboard\bmi_set_config('OTHER:BACKUP:SPACE:CHECKING', $bmi_disable_space_check)) {
4149 Logger::error('Backup Other Backup Space Checking Error');
4150 $error++;
4151 }
4152 if (!Dashboard\bmi_set_config('OTHER:RESTORE:BEFORE:CLEANUP', $no_assets_b4_restore)) {
4153 Logger::error('Backup Other Restore Before Cleanup Error');
4154 $error++;
4155 }
4156 if (!Dashboard\bmi_set_config('OTHER::NEW_SEARCH_REPLACE_ENGINE', $use_new_search_replace_engine)) {
4157 Logger::error('Backup Other DB Search Replace New Engine Error');
4158 $error++;
4159 }
4160 if (!Dashboard\bmi_set_config('OTHER::NEW_DATABASE_EXPORT_ENGINE', $use_new_database_export_engine)) {
4161 Logger::error('Backup Other DB New Export Engine Error');
4162 $error++;
4163 }
4164
4165 if (has_action('bmi_premium_other_options')) {
4166 do_action('bmi_premium_other_options', $this->post);
4167 }
4168
4169 return ['status' => 'success', 'errors' => $error];
4170 }
4171
4172 public function saveStorageTypeConfig() {
4173
4174 // Errors
4175 $name_empty = __('Name is required, please fill the input.', 'backup-backup');
4176 $name_long = __('Your name is too long, please change the name.', 'backup-backup');
4177 $name_short = __('Your name is too short, please create longer one.', 'backup-backup');
4178 $name_space = __('Please, do not use spaces in file name.', 'backup-backup');
4179 $name_forbidden = __('Your name contains character(s) that are not allowed in file names: ', 'backup-backup');
4180
4181 $forbidden_chars = ['/', '\\', '<', '>', ':', '"', "'", '|', '?', '*', '.', ';', '@', '!', '~', '`', ',', '#', '$', '&', '=', '+'];
4182 $name = trim($this->post['name']); // BACKUP:NAME
4183 $extensionType = trim($this->post['extension']); // BACKUP:EXTENSION:TYPE
4184
4185 // Validate % character usage
4186 $allowed_percent_vars = ['%Y', '%M', '%D', '%d', '%j', '%m', '%n', '%y', '%a', '%A', '%B', '%g', '%G', '%h', '%H', '%i', '%s', '%hash', '%domain'];
4187 if (strpos($name, '%') !== false) {
4188 $temp_name = $name;
4189 foreach ($allowed_percent_vars as $var) {
4190 $temp_name = str_replace($var, '', $temp_name);
4191 }
4192 if (strpos($temp_name, '%') !== false) {
4193 return ['status' => 'msg', 'why' => __('Invalid % character used. Only these are allowed: %Y, %M, %D, %d, %j, %m, %n, %y, %a, %A, %B, %g, %G, %h, %H, %i, %s, %hash, %domain', 'backup-backup'), 'level' => 'warning'];
4194 }
4195 }
4196
4197
4198 if (strlen($name) == 0) {
4199 return ['status' => 'msg', 'why' => $name_empty, 'level' => 'warning'];
4200 }
4201 if (strlen($name) > 40) {
4202 return ['status' => 'msg', 'why' => $name_long, 'level' => 'warning'];
4203 }
4204 if (strlen($name) < 3) {
4205 return ['status' => 'msg', 'why' => $name_short, 'level' => 'warning'];
4206 }
4207 if (strpos($name, ' ') !== false) {
4208 return ['status' => 'msg', 'why' => $name_space, 'level' => 'warning'];
4209 }
4210
4211 if (defined('BMI_BACKUP_PRO') && BMI_BACKUP_PRO == 1) {
4212 if (!in_array($extensionType, ['.zip', '.tar.gz', '.tar'])) {
4213 return ['status' => 'msg', 'why' => $name_space, 'level' => 'warning'];
4214 }
4215 }
4216
4217 for ($i = 0; $i < sizeof($forbidden_chars); ++$i) {
4218 $char = $forbidden_chars[$i];
4219 if (strpos($name, $char) !== false) {
4220 return ['status' => 'msg', 'why' => $name_forbidden . $char, 'level' => 'warning'];
4221 }
4222 }
4223
4224 if (!Dashboard\bmi_set_config('BACKUP:NAME', $name)) {
4225 Logger::error('Backup Name Error');
4226 return ['status' => 'msg', 'why' => __('Failed to save backup name.', 'backup-backup'), 'level' => 'error'];
4227 }
4228
4229 if (defined('BMI_BACKUP_PRO') && BMI_BACKUP_PRO == 1) {
4230 if (!Dashboard\bmi_set_config('BACKUP:EXTENSION:TYPE', $extensionType)) {
4231 Logger::error('Backup Extension Type Error');
4232 return ['status' => 'msg', 'why' => __('Failed to save backup extension type.', 'backup-backup'), 'level' => 'error'];
4233 }
4234 }
4235
4236 $storageStrategy = isset($this->post['storage_strategy']) ? $this->post['storage_strategy'] : 'local_and_cloud';
4237
4238 if (!in_array($storageStrategy, ['local_and_cloud', 'cloud_only', 'hybrid'])) {
4239 return ['status' => 'msg', 'why' => __('Invalid storage strategy selected.', 'backup-backup'), 'level' => 'warning'];
4240 }
4241
4242 // Handle local_and_cloud strategy
4243 if ($storageStrategy === 'local_and_cloud') {
4244 if (!Dashboard\bmi_set_config('STORAGE:STRATEGY', 'local_and_cloud')) {
4245 Logger::error('Backup Storage Type Local And Cloud Error');
4246 return ['status' => 'msg', 'why' => __('Failed to save backup storage type.', 'backup-backup'), 'level' => 'error'];
4247 }
4248 }
4249
4250 $response = ['status' => 'success', 'errors' => 0];
4251 if (( $storageStrategy === 'cloud_only' || $storageStrategy === 'hybrid' )) {
4252 // Cloud-only and hybrid strategies require premium
4253 if (!defined('BMI_BACKUP_PRO') || BMI_BACKUP_PRO != 1) {
4254 return ['status' => 'msg', 'why' => __('To use Cloud or Hybrid backup storage type, you need to use our premium extension.', 'backup-backup'), 'level' => 'error'];
4255 }
4256 $response = apply_filters('bmi_premium_store_config', $response, $storageStrategy);
4257 }
4258
4259 if ($response['status'] === 'msg') {
4260 return $response;
4261 }
4262
4263 $direct_cloud_streaming = isset($this->post['direct_cloud_streaming']) && $this->post['direct_cloud_streaming'] === 'true' ? true : false;
4264 $direct_cloud_provider = isset($this->post['direct_cloud_provider']) ? $this->post['direct_cloud_provider'] : '';
4265 $encryptionEnabled = isset($this->post['encryption']) && $this->post['encryption'] === 'true' ? true : false;
4266 $encryptionPassword = isset($this->post['encryption_password']) ? sanitize_text_field($this->post['encryption_password']) : '';
4267
4268 $response = apply_filters('bmi_premium_store_streaming_configuration', $response, $direct_cloud_streaming, $direct_cloud_provider);
4269 if ($response['status'] === 'msg') {
4270 return $response;
4271 }
4272
4273 $response = apply_filters('bmi_premium_store_encryption_configuration', $response, $encryptionEnabled, $encryptionPassword);
4274 if ($response['status'] === 'msg') {
4275 return $response;
4276 }
4277
4278 return $response;
4279
4280 }
4281
4282 public function saveFilesConfig() {
4283 $db_group = $this->post['database_group']; // BACKUP:DATABASE
4284 $files_group = $this->post['files_group']; // BACKUP:FILES
4285
4286 $fgp = $this->post['files-group-plugins']; // BACKUP:FILES::PLUGINS
4287 $fgu = $this->post['files-group-uploads']; // BACKUP:FILES::UPLOADS
4288 $fgt = $this->post['files-group-themes']; // BACKUP:FILES::THEMES
4289 $fgoc = $this->post['files-group-other-contents']; // BACKUP:FILES::OTHERS
4290 $fgwp = $this->post['files-group-wp-install']; // BACKUP:FILES::WP
4291
4292 $file_filters = $this->post['files_by_filters']; // BACKUP:FILES::FILTER
4293 $ffs = $this->post['ex_b_fs']; // BACKUP:FILES::FILTER:SIZE
4294 $ffsizemax = $this->post['BFFSIN']; // BACKUP:FILES::FILTER:SIZE:IN
4295 $ffn = $this->post['ex_b_names']; // BACKUP:FILES::FILTER:NAMES
4296 $ffp = $this->post['ex_b_fpaths']; // BACKUP:FILES::FILTER:FPATHS
4297 $ffd = $this->post['ex_b_dpaths']; // BACKUP:FILES::FILTER:DPATHS
4298
4299 $dbeg = $this->post['db-exclude-tables-group']; // BACKUP:DATABASE:EXCLUDE
4300 $dbet = $this->post['db-excluded-tables']; // BACKUP:DATABASE:EXCLUDE:LIST
4301
4302 $existant = [];
4303 $parsed = [];
4304 $ffnames = $this->post['dynamic-names']; // BACKUP:FILES::FILTER:NAMES:IN
4305 $ffpnames = array_unique($this->post['dynamic-fpaths-names']); // BACKUP:FILES::FILTER:FPATHS:IN
4306 $ffdnames = array_unique($this->post['dynamic-dpaths-names']); // BACKUP:FILES::FILTER:DPATHS:IN
4307
4308 if (is_array($dbet) || is_object($dbet)) {
4309 if (sizeof($dbet) == 1 && $dbet[0] == 'empty') {
4310 $dbet = [];
4311 }
4312 }
4313
4314 if ($dbeg === 'true' || $dbeg === true) $dbeg = true;
4315 else $dbeg = false;
4316
4317 $max = sizeof($ffpnames);
4318 for ($i = 0; $i < $max; ++$i) {
4319 if (!is_string($ffpnames[$i]) || trim(strlen($ffpnames[$i])) <= 1) {
4320 array_splice($ffpnames, $i, 1);
4321 $i--;
4322 $max--;
4323 }
4324 }
4325
4326 $max = sizeof($ffdnames);
4327 for ($i = 0; $i < $max; ++$i) {
4328 if (!is_string($ffdnames[$i]) || trim(strlen($ffdnames[$i])) <= 1) {
4329 array_splice($ffdnames, $i, 1);
4330 $i--;
4331 $max--;
4332 }
4333 }
4334
4335 for ($i = 0; $i < sizeof($ffnames); ++$i) {
4336 $row = $ffnames[$i];
4337 $txt = array_key_exists('txt', $row) ? "" . $row['txt'] . "" : false;
4338 $pos = array_key_exists('pos', $row) ? $row['pos'] : false;
4339 $whr = array_key_exists('whr', $row) ? $row['whr'] : false;
4340
4341 if ($txt === false || $pos === false || $whr === false) {
4342 continue;
4343 }
4344 if (trim(strlen($txt)) <= 0) {
4345 continue;
4346 }
4347 if (!in_array($pos, ["1", "2", "3"])) {
4348 continue;
4349 }
4350 if (!in_array($whr, ["1", "2"])) {
4351 continue;
4352 }
4353 if (in_array($txt . $pos . $whr, $existant)) {
4354 continue;
4355 } else {
4356 $existant[] = $txt . $pos . $whr;
4357 }
4358
4359 $parsed[] = ['txt' => $txt, 'pos' => $pos, 'whr' => $whr];
4360 }
4361
4362 if ($ffs == 'true' && !is_numeric($ffsizemax)) {
4363 return ['status' => 'msg', 'why' => __('Entred file size limit, is not correct number.', 'backup-backup'), 'level' => 'warning'];
4364 }
4365
4366 $error = 0;
4367 if (!Dashboard\bmi_set_config('BACKUP:DATABASE', $db_group)) {
4368 Logger::error('Backup Database Error');
4369 $error++;
4370 }
4371 if (!Dashboard\bmi_set_config('BACKUP:FILES', $files_group)) {
4372 Logger::error('Backup Files Error');
4373 $error++;
4374 }
4375
4376 if (!Dashboard\bmi_set_config('BACKUP:FILES::PLUGINS', $fgp)) {
4377 Logger::error('Backup Files Plugins Error');
4378 $error++;
4379 }
4380 if (!Dashboard\bmi_set_config('BACKUP:FILES::UPLOADS', $fgu)) {
4381 Logger::error('Backup Files Uploads Error');
4382 $error++;
4383 }
4384 if (!Dashboard\bmi_set_config('BACKUP:FILES::THEMES', $fgt)) {
4385 Logger::error('Backup Files Themes Error');
4386 $error++;
4387 }
4388 if (!Dashboard\bmi_set_config('BACKUP:FILES::OTHERS', $fgoc)) {
4389 Logger::error('Backup Files Others Error');
4390 $error++;
4391 }
4392 if (!Dashboard\bmi_set_config('BACKUP:FILES::WP', $fgwp)) {
4393 Logger::error('Backup Files WP Error');
4394 $error++;
4395 }
4396
4397 if (!Dashboard\bmi_set_config('BACKUP:FILES::FILTER', $file_filters)) {
4398 Logger::error('Backup Files Filter Error');
4399 $error++;
4400 }
4401 if (!Dashboard\bmi_set_config('BACKUP:FILES::FILTER:SIZE', $ffs)) {
4402 Logger::error('Backup Files Filter Size Error');
4403 $error++;
4404 }
4405 if (!Dashboard\bmi_set_config('BACKUP:FILES::FILTER:NAMES', $ffn)) {
4406 Logger::error('Backup Files Names Error');
4407 $error++;
4408 }
4409 if (!Dashboard\bmi_set_config('BACKUP:FILES::FILTER:FPATHS', $ffp)) {
4410 Logger::error('Backup Files Fpaths Error');
4411 $error++;
4412 }
4413 if (!Dashboard\bmi_set_config('BACKUP:FILES::FILTER:DPATHS', $ffd)) {
4414 Logger::error('Backup Files Dpaths Error');
4415 $error++;
4416 }
4417
4418 if (!Dashboard\bmi_set_config('BACKUP:FILES::FILTER:SIZE:IN', $ffsizemax)) {
4419 Logger::error('Backup Files Filter Size In Error');
4420 $error++;
4421 }
4422 if (!Dashboard\bmi_set_config('BACKUP:FILES::FILTER:NAMES:IN', $parsed)) {
4423 Logger::error('Backup Files Filter Names In Error');
4424 $error++;
4425 }
4426 if (!Dashboard\bmi_set_config('BACKUP:FILES::FILTER:FPATHS:IN', $ffpnames)) {
4427 Logger::error('Backup Files Filter Fpaths In Error');
4428 $error++;
4429 }
4430 if (!Dashboard\bmi_set_config('BACKUP:FILES::FILTER:DPATHS:IN', $ffdnames)) {
4431 Logger::error('Backup Files Filter Dpaths In Error');
4432 $error++;
4433 }
4434
4435 if (defined('BMI_BACKUP_PRO') && BMI_BACKUP_PRO == 1) {
4436 if (!Dashboard\bmi_set_config('BACKUP:DATABASE:EXCLUDE', $dbeg)) {
4437 Logger::error('Backup Files Filter Database Exclude Error');
4438 $error++;
4439 }
4440 if (!Dashboard\bmi_set_config('BACKUP:DATABASE:EXCLUDE:LIST', $dbet)) {
4441 Logger::error('Backup Files Filter Database Exclude List Error');
4442 $error++;
4443 }
4444 }
4445
4446 if (has_action('bmip_smart_exclusion_options')){
4447 do_action('bmip_smart_exclusion_options', $this->post);
4448 }
4449
4450 // return array('status' => 'msg', 'why' => __('Entred path is not writable or does not exist.', 'backup-backup'), 'level' => 'warning');
4451
4452 return ['status' => 'success', 'errors' => $error];
4453 }
4454
4455 public function scanFilesForBackup(&$progress, $stgSites = [], $fileCalcType = false) {
4456 require_once BMI_INCLUDES . '/scanner/files.php';
4457 require_once BMI_INCLUDES . '/file-explorer.php';
4458 $stagingSites = [];
4459
4460 // Get all directory names of staging sites
4461 foreach ($stgSites as $index => $site) {
4462
4463 // Convert every directory to their location path
4464 $stagingSites[] = '***ABSPATH***/' . $site['name'];
4465
4466 }
4467
4468 // Use filters?
4469 $is = Dashboard\bmi_get_config('BACKUP:FILES::FILTER') === 'true' ? true : false;
4470
4471 // Get settings form config
4472 $fgp = Dashboard\bmi_get_config('BACKUP:FILES::PLUGINS');
4473 $fgt = Dashboard\bmi_get_config('BACKUP:FILES::THEMES');
4474 $fgu = Dashboard\bmi_get_config('BACKUP:FILES::UPLOADS');
4475 $fgoc = Dashboard\bmi_get_config('BACKUP:FILES::OTHERS');
4476 $fgwp = Dashboard\bmi_get_config('BACKUP:FILES::WP');
4477 $dpathsis = Dashboard\bmi_get_config('BACKUP:FILES::FILTER:DPATHS') === 'true' ? true : false;
4478 $dpaths = Dashboard\bmi_get_config('BACKUP:FILES::FILTER:DPATHS:IN');
4479 $dynamesis = Dashboard\bmi_get_config('BACKUP:FILES::FILTER:NAMES') === 'true' ? true : false;
4480 $dynames = Dashboard\bmi_get_config('BACKUP:FILES::FILTER:NAMES:IN');
4481 $dynparsed = [];
4482
4483 $isSmartExclusion =defined("BMI_BACKUP_PRO") && BMI_BACKUP_PRO && Dashboard\bmi_get_config('SMART:EXCLUSION:ENABLED') == 'true' ? true : false;
4484 $isCacheExcluded = $isSmartExclusion && (Dashboard\bmi_get_config('SMART:EXCLUSION:CACHE') == 'true' ? true : false);
4485 $isDeactivePluginsExcluded = $isSmartExclusion && (Dashboard\bmi_get_config('SMART:EXCLUSION:DPLUGINS') == 'true' ? true : false);
4486 $isNotUsedThemesExcluded = $isSmartExclusion && (Dashboard\bmi_get_config('SMART:EXCLUSION:NUTHEMES') == 'true' ? true : false);
4487 $isDebugLogsExcluded = $isSmartExclusion && (Dashboard\bmi_get_config('SMART:EXCLUSION:DLOGS') == 'true' ? true : false);
4488 $isPostRevisionsExcluded = $isSmartExclusion &&(Dashboard\bmi_get_config('SMART:EXCLUSION:PREVISIONS') == 'true' ? true : false);
4489
4490
4491 if ($fileCalcType != false) {
4492 $fgp = ($fileCalcType == 'plugins') ? true : false;
4493 $fgt = ($fileCalcType == 'themes') ? true : false;
4494 $fgu = ($fileCalcType == 'uploads') ? true : false;
4495 $fgoc = ($fileCalcType == 'contents_others') ? true : false;
4496 $fgwp = ($fileCalcType == 'wordpress') ? true : false;
4497 }
4498
4499 // Filter dynames to for smaller size
4500 if ($is && $dynamesis) {
4501 for ($i = 0; $i < sizeof($dynames); ++$i) {
4502 $s = $dynames[$i];
4503 if ($s['whr'] == '2') {
4504 $dynparsed[] = ['s' => $s['txt'], 'w' => $s['pos'], 'z' => strlen($s['txt'])];
4505 }
4506 }
4507 }
4508
4509 // Set exclusion rules
4510 $ignored_folders_default = [];
4511 if ($is && $dynamesis) {
4512 BMP::merge_arrays($ignored_folders_default, $dynparsed);
4513 }
4514 $ignored_folders = $ignored_folders_default;
4515 $ignored_paths_default = [BMI_CONFIG_DIR, BMI_ROOT_DIR];
4516 $ignored_paths_default[] = "***ABSPATH***/wp-content/ai1wm-backups";
4517 $ignored_paths_default[] = "***ABSPATH***/wp-content/ai1wm-backups-old";
4518 $ignored_paths_default[] = "***ABSPATH***/wp-content/mwp-download";
4519 $ignored_paths_default[] = "***ABSPATH***/wp-content/uploads/wp-clone";
4520 $ignored_paths_default[] = "***ABSPATH***/wp-content/updraft";
4521 $ignored_paths_default[] = "***ABSPATH***/wp-content/ebwp-backups";
4522 $ignored_paths_default[] = "***ABSPATH***/wp-content/cache/seraphinite-accelerator";
4523 $ignored_paths_default[] = "***ABSPATH***/wp-content/backups-dup-pro";
4524 $ignored_paths_default[] = "***ABSPATH***/wp-content/wpvividbackups";
4525 $ignored_paths_default[] = "***ABSPATH***/wp-content/backup-guard";
4526 $ignored_paths_default[] = "***ABSPATH***/wp-content/backuply";
4527 $ignored_paths_default[] = "***ABSPATH***/wp-content/backups-dup-lite";
4528 $ignored_paths_default[] = "***ABSPATH***/wp-content/uploads/backupbuddy_backups";
4529 $ignored_paths_default[] = "***ABSPATH***/wp-content/uploads/wp-file-manager-pro";
4530 $ignored_paths_default[] = "***ABSPATH***/wp-content/uploads/wp-file-manager";
4531 $ignored_paths_default[] = "***ABSPATH***/wp-content/plugins/akeebabackupwp";
4532 $ignored_paths_default[] = "***ABSPATH***/wp-content/uploads/jetbackup";
4533 $ignored_paths_default[] = "***ABSPATH***/wp-content/uploads/backup-guard";
4534 $ignored_paths_default[] = "***ABSPATH***/wp-content/uploads/wp-migrate-db";
4535
4536 $ignored_paths_default[] = "***ABSPATH***/wp-content/uploads/wp-staging";
4537
4538 if ($isSmartExclusion && ($fileCalcType == false || $fileCalcType == 'database')) {
4539 if ($isCacheExcluded) {
4540 $ignored_paths_default = apply_filters('bmip_smart_exclusion_cache', $ignored_paths_default);
4541 }
4542 if ($isDeactivePluginsExcluded) {
4543 $ignored_paths_default = apply_filters('bmip_smart_exclusion_deactive_plugins', $ignored_paths_default);
4544 }
4545 if ($isNotUsedThemesExcluded) {
4546 $ignored_paths_default = apply_filters('bmip_smart_exclusion_not_used_themes', $ignored_paths_default);
4547 }
4548 }
4549
4550 // Exclude cache directory permanently as it's just cache
4551 // $ignored_paths_default[] = "***ABSPATH***/wp-content/cache";
4552 // $ignored_paths_default[] = "***ABSPATH***/wp-content/cache_bak";
4553 // $ignored_paths_default[] = "***ABSPATH***/wp-content/uploads/cache";
4554
4555 // Add staging sites to permanent exclusion rules
4556 for ($i = 0; $i < sizeof($stagingSites); ++$i) {
4557 $ignored_paths_default[] = $stagingSites[$i];
4558 }
4559
4560 if (defined('BMI_PRO_ROOT_DIR')) $ignored_paths_default[] = BMI_PRO_ROOT_DIR;
4561 if ($is && $dpathsis) {
4562 foreach($dpaths as $dpath) {
4563 $dpath = str_replace('***ABSPATH***', untrailingslashit(ABSPATH), $dpath);
4564 $dpath = BMP::fixSlashes($dpath);
4565 if (is_dir($dpath)) {
4566 if (!$fileCalcType) $progress->log(__('Removing directory from backup (due to exclude rules): ', 'backup-backup') . $dpath, 'WARN');
4567 $ignored_folders_default[] = $dpath;
4568 }
4569 $ignored_paths_default[] = $dpath;
4570 }
4571 }
4572 $ignored_paths = $ignored_paths_default;
4573
4574 // Fix slashes for current system (directories)
4575 for ($i = 0; $i < sizeof($ignored_paths); ++$i) {
4576 $ignored_paths[$i] = str_replace('***ABSPATH***', untrailingslashit(ABSPATH), $ignored_paths[$i]);
4577 $ignored_paths[$i] = BMP::fixSlashes($ignored_paths[$i]);
4578 }
4579
4580 // WordPress Paths
4581 $plugins_path = BMP::fixSlashes(WP_PLUGIN_DIR);
4582 $themes_path = BMP::fixSlashes(dirname(get_template_directory()));
4583 $uploads_path = BMP::fixSlashes(wp_upload_dir()['basedir']);
4584 $wp_contents = BMP::fixSlashes(WP_CONTENT_DIR);
4585 $wp_install = BMP::fixSlashes(ABSPATH);
4586
4587 // Getting plugins
4588 $sfgp = Scanner::equalFolderByPath($wp_install, $plugins_path, $ignored_folders);
4589 if ($fgp == 'true' && !$sfgp) {
4590 $plugins_path_files = Scanner::scanFilesGetNamesWithIgnoreFBC($plugins_path, $ignored_folders, $ignored_paths);
4591 foreach($ignored_paths as $dpath) {
4592 $isSub = File_Explorer::isSub($dpath, $plugins_path);
4593 if ($isSub != -1) {
4594 $this->ignoredDirectoriesSize += File_Explorer::getDirSize($dpath);
4595 }
4596 }
4597 }
4598
4599 // Getting themes
4600 $sfgt = Scanner::equalFolderByPath($wp_install, $themes_path, $ignored_folders);
4601 if ($fgt == 'true' && !$sfgt) {
4602 $themes_path_files = Scanner::scanFilesGetNamesWithIgnoreFBC($themes_path, $ignored_folders, $ignored_paths);
4603 foreach($ignored_paths as $dpath) {
4604 $isSub = File_Explorer::isSub($dpath, $themes_path);
4605 if ($isSub != -1) {
4606 $this->ignoredDirectoriesSize += File_Explorer::getDirSize($dpath);
4607 }
4608 }
4609 }
4610
4611 // Getting uploads
4612 $sfgu = Scanner::equalFolderByPath($wp_install, $uploads_path, $ignored_folders);
4613 if ($fgu == 'true' && !$sfgu) {
4614 $uploads_path_files = Scanner::scanFilesGetNamesWithIgnoreFBC($uploads_path, $ignored_folders, $ignored_paths);
4615 foreach($ignored_paths as $dpath) {
4616 $isSub = File_Explorer::isSub($dpath, $uploads_path);
4617 if ($isSub != -1) {
4618 $this->ignoredDirectoriesSize += File_Explorer::getDirSize($dpath);
4619 }
4620 }
4621 }
4622
4623 // Ignore above paths
4624 $sfgoc = Scanner::equalFolderByPath($wp_install, $wp_contents, $ignored_folders);
4625 if ($fgoc == 'true' && !$sfgoc) {
4626
4627 // Ignore common folders (already scanned)
4628 $content_folders = [$plugins_path, $themes_path, $uploads_path];
4629 BMP::merge_arrays($content_folders, $ignored_paths);
4630
4631 // Getting other contents
4632 $wp_contents_files = Scanner::scanFilesGetNamesWithIgnoreFBC($wp_contents, $ignored_folders, $content_folders);
4633
4634 foreach($ignored_paths as $dpath) {
4635 $isSub = File_Explorer::isSub($dpath, $wp_contents) != -1 &&
4636 File_Explorer::isSub($dpath, $plugins_path) == -1 &&
4637 File_Explorer::isSub($dpath, $themes_path) == -1 &&
4638 File_Explorer::isSub($dpath, $uploads_path) == -1;
4639 if ($isSub) {
4640 $this->ignoredDirectoriesSize += File_Explorer::getDirSize($dpath);
4641 }
4642 }
4643 }
4644
4645 // Ignore contents path
4646 if ($fgwp == 'true') {
4647
4648 // Ignore contents file
4649 $ignored_paths[] = $wp_contents;
4650
4651 // Getting WP Installation
4652 $wp_install_files = Scanner::scanFilesGetNamesWithIgnoreFBC($wp_install, $ignored_folders, $ignored_paths);
4653
4654 foreach($ignored_paths as $dpath) {
4655 $isSub = File_Explorer::isSub($dpath, $wp_install) != -1 &&
4656 File_Explorer::isSub($dpath, $wp_contents) == -1;
4657 if ($isSub) {
4658 $this->ignoredDirectoriesSize += File_Explorer::getDirSize($dpath);
4659 }
4660 }
4661 }
4662
4663 // Concat all file paths
4664 $all_files = [];
4665 if ($fgp == 'true' && !$sfgp) {
4666 BMP::merge_arrays($all_files, $plugins_path_files);
4667 unset($plugins_path_files);
4668 }
4669
4670 if ($fgt == 'true' && !$sfgt) {
4671 BMP::merge_arrays($all_files, $themes_path_files);
4672 unset($themes_path_files);
4673 }
4674
4675 if ($fgu == 'true' && !$sfgu) {
4676 BMP::merge_arrays($all_files, $uploads_path_files);
4677 unset($uploads_path_files);
4678 }
4679
4680 if ($fgoc == 'true' && !$sfgoc) {
4681 BMP::merge_arrays($all_files, $wp_contents_files);
4682 unset($wp_contents_files);
4683 }
4684
4685 if ($fgwp == 'true') {
4686 BMP::merge_arrays($all_files, $wp_install_files);
4687 unset($wp_install_files);
4688 }
4689
4690 return $all_files;
4691 }
4692
4693 public function parseFilesForBackup(&$files, &$progress, $cron = false, $dirCalc = false) {
4694
4695 $is = Dashboard\bmi_get_config('BACKUP:FILES::FILTER') === 'true' ? true : false;
4696 $acis = (Dashboard\bmi_get_config('BACKUP:FILES::FILTER:FPATHS') === 'true' && $is) ? true : false;
4697 $ac = Dashboard\bmi_get_config('BACKUP:FILES::FILTER:FPATHS:IN');
4698
4699 $abis = (Dashboard\bmi_get_config('BACKUP:FILES::FILTER:NAMES') === 'true' && $is) ? true : false;
4700 $ab = Dashboard\bmi_get_config('BACKUP:FILES::FILTER:NAMES:IN');
4701 $abres = [];
4702 $acres = new \stdClass();
4703
4704 $isSmartExclusion = defined("BMI_BACKUP_PRO") && BMI_BACKUP_PRO && Dashboard\bmi_get_config('SMART:EXCLUSION:ENABLED') == 'true' ? true : false;
4705 $isDebugLogsExcluded = $isSmartExclusion && (Dashboard\bmi_get_config('SMART:EXCLUSION:DLOGS') == 'true' ? true : false);
4706
4707 // Local list of permanently blocked files
4708 if ($acis == false) {
4709 $acis = true;
4710 $ac = [
4711 '***ABSPATH***/wp-content/uploads/wpforms/.htaccess.cpmh3129', // Binary broken file of wpforms
4712 '***ABSPATH***/wp-content/uploads/gravity_forms/.htaccess.cpmh3129', // Binary broken file of wpforms
4713 '***ABSPATH***/.htaccess.cpmh3129', // Binary broken file of wpforms
4714 '***ABSPATH***/logs/traffic.html/.md5sums', // Binary broken file of wpforms
4715 '***ABSPATH***/wp-config.php', // Exclude wp-config.php permanently
4716 '***ABSPATH***/wp-content/mu-plugins/bmi-autologin.php',
4717 '***ABSPATH***/wp-content/backup-migration-config.php' // Exclude BMI CONFIG hardly
4718 ];
4719 } else {
4720 foreach ($ac as $key => $value) {
4721 $value = str_replace('***ABSPATH***', untrailingslashit(ABSPATH), $value);
4722 $value = BMP::fixSlashes($value);
4723 if (file_exists($value)) {
4724 if (!$dirCalc) $progress->log(__('Removing file from backup (due to exclude rules): ', 'backup-backup') . $value, 'WARN');
4725 $ac[$key] = $value;
4726 }
4727 }
4728 $ac[] = '***ABSPATH***/wp-content/uploads/wpforms/.htaccess.cpmh3129'; // Binary broken file of wpforms
4729 $ac[] = '***ABSPATH***/wp-content/uploads/gravity_forms/.htaccess.cpmh3129'; // Binary broken file of wpforms
4730 $ac[] = '***ABSPATH***/.htaccess.cpmh3129'; // Binary broken file of wpforms
4731 $ac[] = '***ABSPATH***/logs/traffic.html/.md5sums'; // Binary broken file of wpforms
4732 $ac[] = '***ABSPATH***/wp-config.php'; // Exclude wp-config.php permanently
4733 $ac[] = '***ABSPATH***/wp-content/backup-migration-config.php'; // Exclude BMI CONFIG hardly
4734 }
4735
4736 if ($isDebugLogsExcluded) {
4737 $ac = apply_filters('bmip_smart_exclusion_debug_logs', $ac);
4738 }
4739
4740 $temp_is = false;
4741 if ($is == false) {
4742 $temp_is = true;
4743 }
4744
4745 if (($is && $acis) || $temp_is) {
4746 foreach ($ac as $key => $value) {
4747 $value = str_replace('***ABSPATH***', untrailingslashit(ABSPATH), $value);
4748 $value = BMP::fixSlashes($value);
4749 $acres->{$value} = 1;
4750 }
4751 }
4752
4753 if ($is && $abis) {
4754 for ($i = 0; $i < sizeof($ab); ++$i) {
4755 $s = $ab[$i];
4756 if ($s['whr'] == '1') {
4757 $abres[] = ['s' => $s['txt'], 'w' => $s['pos'], 'z' => strlen($s['txt'])];
4758 }
4759 }
4760 }
4761
4762 $limitcrl = 64;
4763 $cliEnabled = false;
4764 if (defined('BMI_CLI_ENABLED')) $cliEnabled = apply_filters('bmi_cli_enabled', BMI_CLI_ENABLED);
4765 if ($dirCalc && $cliEnabled && !defined('BMI_CLI_FAILED')) $limitcrl = 128;
4766 $first_big = false;
4767 $sizemax = Dashboard\bmi_get_config('BACKUP:FILES::FILTER:SIZE:IN');
4768 $usesize = (Dashboard\bmi_get_config('BACKUP:FILES::FILTER:SIZE') === 'true' && $is) ? true : false;
4769 if (!is_numeric($sizemax)) {
4770 $usesize = false;
4771 $sizemax = 99999;
4772 } else {
4773 $sizemax = intval($sizemax);
4774 }
4775
4776 // If legacy === false it will use background process to bypass the timeout
4777 if ($dirCalc) {
4778 $legacy = true;
4779 } else {
4780 $legacyVersion = apply_filters('bmi_legacy_version', BMI_LEGACY_VERSION);
4781 $legacyHardVersion = apply_filters('bmi_legacy_hard_version', BMI_LEGACY_HARD_VERSION);
4782 $functionNormal = apply_filters('bmi_function_normal', BMI_FUNCTION_NORMAL);
4783 if (!defined('BMI_LEGACY_VERSION')) $legacy = true;
4784 else $legacy = $legacyVersion;
4785 if ($legacy && defined('BMI_LEGACY_HARD_VERSION') && !$legacyHardVersion) $legacy = $legacyHardVersion;
4786 $cliEnabled = false;
4787 if (defined('BMI_CLI_ENABLED')) $cliEnabled = apply_filters('bmi_cli_enabled', BMI_CLI_ENABLED);
4788 if (defined('BMI_FUNCTION_NORMAL') && $cliEnabled === true && $functionNormal === true && !defined('BMI_CLI_FAILED')) $legacy = false;
4789 }
4790
4791 $total_size = 0;
4792 $excludedBytes = 0;
4793 $max = $sizemax * (1024 * 1024);
4794 $maxfor = sizeof($files);
4795
4796 // Non-legacy variables
4797 if ($legacy === false) {
4798 $Hx = trailingslashit(WP_CONTENT_DIR);
4799 $Hz = trailingslashit(ABSPATH);
4800 $Hxs = strlen($Hx);
4801 $Hzs = strlen($Hz);
4802 }
4803
4804 // Sort it by size
4805 if ($legacy === false) {
4806 usort($files, function ($a, $b) {
4807 $a = explode(',', $a);
4808 $last = sizeof($a) - 1;
4809 $sizea = intval($a[$last]);
4810
4811 $b = explode(',', $b);
4812 $last = sizeof($b) - 1;
4813 $sizeb = intval($b[$last]);
4814
4815 if ($sizea == $sizeb) return 0;
4816 if ($sizea < $sizeb) return -1;
4817 else return 1;
4818 });
4819 }
4820
4821 // Process due to rules
4822 for ($i = 0; $i < $maxfor; ++$i) {
4823
4824 // Remove size from path and get the size
4825 $files[$i] = explode(',', $files[$i]);
4826 $last = sizeof($files[$i]) - 1;
4827 $size = intval($files[$i][$last]);
4828 unset($files[$i][$last]);
4829 $files[$i] = implode(',', $files[$i]);
4830
4831 if ($usesize && Scanner::fileTooLarge($size, $max)) {
4832 if (!$dirCalc) $progress->log(__("Removing file from backup (too large) ", 'backup-backup') . $files[$i] . ' (' . number_format(($size / 1024 / 1024), 2) . ' MB)', 'WARN');
4833 array_splice($files, $i, 1);
4834 $maxfor--;
4835 $i--;
4836
4837 $excludedBytes += $size;
4838 continue;
4839 }
4840
4841 if ($abis && Scanner::equalFolder(basename($files[$i]), $abres)) {
4842 if (!$dirCalc) $progress->log(__("Removing file from backup (due to exclude rules): ", 'backup-backup') . $files[$i], 'WARN');
4843 array_splice($files, $i, 1);
4844 $maxfor--;
4845 $i--;
4846
4847 $excludedBytes += $size;
4848 continue;
4849 }
4850
4851 if ($acis && property_exists($acres, $files[$i])) {
4852 if (!$dirCalc) $progress->log(__("Removing file from backup (due to path rules): ", 'backup-backup') . $files[$i], 'WARN');
4853 array_splice($files, $i, 1);
4854 $maxfor--;
4855 $i--;
4856
4857 $excludedBytes += $size;
4858 continue;
4859 }
4860
4861 // if ($size === 0) {
4862 // array_splice($files, $i, 1);
4863 // $maxfor--;
4864 // $i--;
4865
4866 // $excludedBytes += $size;
4867 // continue;
4868 // }
4869
4870 if (strpos($files[$i], 'bmi-pclzip-') !== false || strpos($files[$i], 'backup-migration') !== false) {
4871 array_splice($files, $i, 1);
4872 $maxfor--;
4873 $i--;
4874
4875 $excludedBytes += $size;
4876 continue;
4877 }
4878
4879 if ($size > ($limitcrl * (1024 * 1024))) {
4880 if ($first_big === false) $first_big = $i;
4881 if (!$dirCalc) $progress->log(__("This file is quite big consider to exclude it, if backup fails: ", 'backup-backup') . $files[$i] . ' (' . BMP::humanSize($size) . ')', 'WARN');
4882 }
4883
4884 $functionNormal = apply_filters('bmi_function_normal', BMI_FUNCTION_NORMAL);
4885 $cliEnabled = apply_filters('bmi_cli_enabled', defined('BMI_CLI_ENABLED') ? BMI_CLI_ENABLED : false);
4886 if (($legacy === false && ($functionNormal === false || ($functionNormal === true && $cliEnabled === true))) && (!defined('BMI_USING_CLI_FUNCTIONALITY') || BMI_USING_CLI_FUNCTIONALITY === false)) {
4887 $fx = strpos($files[$i], $Hx);
4888 $fz = strpos($files[$i], $Hz);
4889
4890 if ($fx !== false) $files[$i] = substr_replace($files[$i], '@1@', $fx, $Hxs);
4891 else if ($fz !== false) $files[$i] = substr_replace($files[$i], '@2@', $fz, $Hzs);
4892
4893 $files[$i] .= ',' . $size;
4894 }
4895 $total_size += $size;
4896 }
4897
4898 if ($legacy === false && (!defined('BMI_USING_CLI_FUNCTIONALITY') || BMI_USING_CLI_FUNCTIONALITY === false)) {
4899 $list_file = BMI_TMP . DIRECTORY_SEPARATOR . 'files_latest.list';
4900 if (file_exists($list_file)) @unlink($list_file);
4901 $files_list = fopen($list_file, 'a');
4902 if ($first_big === false) fwrite($files_list, sizeof($files) . "_-1\r\n");
4903 else fwrite($files_list, sizeof($files) . '_' . $first_big . "\r\n");
4904 for ($i = 0; $i < sizeof($files); ++$i) {
4905 fwrite($files_list, $files[$i] . "\r\n");
4906 }
4907 fclose($files_list);
4908 $this->first_big = $first_big;
4909 }
4910
4911 $this->total_excluded_size_for_backup = $excludedBytes + $this->ignoredDirectoriesSize;
4912 $this->total_size_for_backup = $total_size;
4913 $this->total_size_for_backup_in_mb = ($total_size / 1024 / 1024);
4914
4915 return $files;
4916 }
4917
4918 public function toggleBackupLock($unlock = false) {
4919
4920 // Require lib
4921 require_once BMI_INCLUDES . DIRECTORY_SEPARATOR . 'zipper' . DIRECTORY_SEPARATOR . 'zipping.php';
4922
4923 // Backup name
4924 $filename = $this->post['filename'];
4925
4926 // Init Zipper
4927 $zipper = new Zipper();
4928
4929 // Path to Backup
4930 $path = BMI_BACKUPS . DIRECTORY_SEPARATOR . $filename;
4931 $path_dir = BMP::fixSlashes(dirname($path));
4932
4933 // Check if file exists
4934 if (!file_exists($path)) {
4935 return ['status' => 'fail'];
4936 }
4937
4938 // Check if directory is correct
4939 if ($path_dir != BMP::fixSlashes(BMI_BACKUPS)) {
4940 return ['status' => 'fail'];
4941 }
4942
4943 // Toggle the lock
4944 $status = $zipper->lock_zip($path, $unlock);
4945
4946 // Return the status
4947 return ['status' => ($status ? 'success' : 'fail')];
4948 }
4949
4950 public function getDynamicNames() {
4951 $data = Dashboard\bmi_get_config('BACKUP:FILES::FILTER:NAMES:IN');
4952 $fpdata = Dashboard\bmi_get_config('BACKUP:FILES::FILTER:FPATHS:IN');
4953 $fddata = Dashboard\bmi_get_config('BACKUP:FILES::FILTER:DPATHS:IN');
4954
4955 for ($i = 0; $i < sizeof($fpdata); ++$i) {
4956 $fpdata[$i] = BMP::fixSlashes($fpdata[$i]);
4957 }
4958
4959 for ($i = 0; $i < sizeof($fddata); ++$i) {
4960 $fddata[$i] = BMP::fixSlashes($fddata[$i]);
4961 }
4962
4963 return [
4964 'status' => 'success',
4965 'dynamic-fpaths-names' => $fpdata,
4966 'dynamic-dpaths-names' => $fddata,
4967 'data' => $data
4968 ];
4969 }
4970
4971 public function resetConfiguration() {
4972
4973 global $wpdb;
4974 $free_options = $wpdb->get_results( "SELECT option_name FROM $wpdb->options WHERE option_name LIKE '%bmi_%' OR option_name LIKE '%bmip_%' OR option_name LIKE '%bmi_pro_%'" );
4975 foreach( $free_options as $option ) {
4976 delete_option( $option->option_name );
4977 }
4978 delete_option('BMI::STORAGE::LOCAL::PATH');
4979
4980 return ['status' => 'success'];
4981
4982 }
4983
4984 public function getSiteData() {
4985 require_once BMI_INCLUDES . DIRECTORY_SEPARATOR . 'check' . DIRECTORY_SEPARATOR . 'system_info.php';
4986 $bmi = new SI();
4987 $bmi = $bmi->to_array();
4988
4989 return ['status' => 'success', 'data' => $bmi];
4990 }
4991
4992 public function calculateCron() {
4993 require_once BMI_INCLUDES . DIRECTORY_SEPARATOR . 'cron' . DIRECTORY_SEPARATOR . 'handler.php';
4994
4995 $minutes = [];
4996 $keeps = [];
4997 $days = [];
4998 $weeks = [];
4999 $hours = [];
5000
5001 for ($i = 1; $i <= 28; ++$i) {
5002 $days[] = substr('0' . $i, -2);
5003 }
5004 for ($i = 1; $i <= 7; ++$i) {
5005 $weeks[] = $i . '';
5006 }
5007 for ($i = 0; $i <= 23; ++$i) {
5008 $hours[] = substr('0' . $i, -2);
5009 }
5010 for ($i = 0; $i <= 55; $i += 5) {
5011 $minutes[] = substr('0' . $i, -2);
5012 }
5013 for ($i = 1; $i <= 20; ++$i) {
5014 $keeps[] = $i . '';
5015 }
5016
5017 $errors = 0;
5018 if (in_array($this->post['type'], ['month', 'week', 'day'])) {
5019 if (!Dashboard\bmi_set_config('CRON:TYPE', $this->post['type'])) {
5020 $errors++;
5021 }
5022 }
5023 if (in_array($this->post['day'], $days)) {
5024 if (!Dashboard\bmi_set_config('CRON:DAY', $this->post['day'])) {
5025 $errors++;
5026 }
5027 }
5028 if (in_array($this->post['week'], $weeks)) {
5029 if (!Dashboard\bmi_set_config('CRON:WEEK', $this->post['week'])) {
5030 $errors++;
5031 }
5032 }
5033 if (in_array($this->post['hour'], $hours)) {
5034 if (!Dashboard\bmi_set_config('CRON:HOUR', $this->post['hour'])) {
5035 $errors++;
5036 }
5037 }
5038 if (in_array($this->post['minute'], $minutes)) {
5039 if (!Dashboard\bmi_set_config('CRON:MINUTE', $this->post['minute'])) {
5040 $errors++;
5041 }
5042 }
5043 if (in_array($this->post['keep'], $keeps)) {
5044 if (!Dashboard\bmi_set_config('CRON:KEEP', $this->post['keep'])) {
5045 $errors++;
5046 }
5047 }
5048 do_action('bmip_cron_settings_updated', $this->post);
5049
5050 if ($this->post['enabled'] === 'true') {
5051 $this->post['enabled'] = true;
5052 } else {
5053 $this->post['enabled'] = false;
5054 }
5055
5056 if (!Dashboard\bmi_set_config('CRON:ENABLED', $this->post['enabled'])) {
5057 $errors++;
5058 }
5059
5060 if ($errors === 0) {
5061 $time = Crons::calculate_date([
5062 'type' => $this->post['type'],
5063 'week' => $this->post['week'],
5064 'day' => $this->post['day'],
5065 'hour' => $this->post['hour'],
5066 'minute' => $this->post['minute']
5067 ], time());
5068
5069 $file = BMI_TMP . DIRECTORY_SEPARATOR . '.plan';
5070 if (file_exists($file)) {
5071 $earlier = intval(file_get_contents($file));
5072 } else {
5073 $earlier = 0;
5074 }
5075
5076 if (!wp_next_scheduled('bmi_do_backup_right_now') || $earlier === 0 || (abs($time - $earlier) >= 15)) {
5077 wp_clear_scheduled_hook('bmi_do_backup_right_now');
5078 if ($this->post['enabled'] === true) {
5079 wp_schedule_single_event($time, 'bmi_do_backup_right_now');
5080 file_put_contents($file, $time);
5081 }
5082 }
5083 $siteIsLocalOrUnreachable = false;
5084 $pingIsWorkingWell = true;
5085
5086 if (get_option('bmi_cron_new_domain_done', false)) {
5087 $this->shareDomainForAutoCron(true);
5088 }
5089
5090 if (function_exists('wp_load_alloptions')) {
5091 wp_load_alloptions(true);
5092 }
5093
5094 // If the site is local or unreachable
5095 if (get_option('bmi_cron_site_local_or_unreachable', false)) {
5096 $siteIsLocalOrUnreachable = true;
5097 }
5098
5099 // is local no need to check ping server as it won't work for locals
5100 if (!$siteIsLocalOrUnreachable && get_option('bmi_cron_last_ping_time', false) && (abs(current_time('timestamp') - get_option('bmi_cron_last_ping_time', 0)) > HOUR_IN_SECONDS)) {
5101 $pingIsWorkingWell = false;
5102 }
5103
5104
5105 return [
5106 'status' => 'success',
5107 'data' => date('Y-m-d H:i:s', $time),
5108 'local_site' => $siteIsLocalOrUnreachable,
5109 'ping_working' => $pingIsWorkingWell,
5110 'currdata' => date('Y-m-d H:i:s')
5111 ];
5112 } else {
5113 return ['status' => 'error'];
5114 }
5115 }
5116
5117 public function dismissErrorNotice() {
5118 $optionId = isset($this->post['option_id']) ? $this->post['option_id'] : '';
5119 if (in_array($optionId, ['backupbliss-issues', 'backupbliss-dismiss-upload-issue']))
5120 {
5121 require_once BMI_INCLUDES . '/external/backupbliss.php';
5122 $backupbliss = new BackupBliss();
5123 }
5124
5125 switch ($optionId) {
5126 case 'email-issues':
5127 delete_option('bmi_display_email_issues');
5128 break;
5129 case 'before-update-issues':
5130 delete_option('bmi_display_before_update_backup_issues');
5131 break;
5132 case 'aws-issues':
5133 update_option('bmip_aws_dismiss_issue', true);
5134 break;
5135 case 'wasabi-issues':
5136 update_option('bmip_wasabi_dismiss_issue', true);
5137 break;
5138 case 'sftp-issues':
5139 update_option('bmip_sftp_dismiss_issue', true);
5140 break;
5141 case 'gdrive-issues':
5142 delete_transient('bmip_gd_issue');
5143 break;
5144 case 'pcloud-issues':
5145 update_option('bmip_pcloud_dismiss_issue', true);
5146 break;
5147 case 'backupbliss-issues':
5148 $backupbliss->removeNotice("invalid_key");
5149 $backupbliss->removeNotice("invalid_permission");
5150 if ($backupbliss->getNotice("storage_warn"))
5151 $backupbliss->hideNotice("storage_warn", 60 * 60);
5152 if ($backupbliss->getNotice("upload_issue"))
5153 $backupbliss->hideNotice("upload_issue", 60); //Hide only for a minute
5154 break;
5155 case 'backupbliss-dismiss-upload-issue':
5156 $backupbliss->hideFailureWarnNotice(14 * 24 * 60 * 60); //14 days
5157 break;
5158 case 'security-plugin-warning':
5159 update_option('bmi_security_warning_dismiss', true);
5160 break;
5161 case 'ping-server-issues':
5162 update_option('bmi_ping_server_dismiss_issue', true);
5163 break;
5164 case 'site-local-unreachable-notice':
5165 update_option('bmi_site_local_unreachable_dismiss_issue', true);
5166 break;
5167 case 'cloud-strategy-issue':
5168 update_option('bmi_storage_fallback_dismissed', true);
5169 break;
5170 default:
5171 break;
5172 }
5173 }
5174
5175 // recursive removal
5176 private static function rrmdir($dir) {
5177
5178 if (is_dir($dir)) {
5179
5180 $objects = scandir($dir);
5181 foreach ($objects as $object) {
5182
5183 if ($object != "." && $object != "..") {
5184
5185 if (is_dir($dir . DIRECTORY_SEPARATOR . $object) && !is_link($dir . DIRECTORY_SEPARATOR . $object)) {
5186
5187 self::rrmdir($dir . DIRECTORY_SEPARATOR . $object);
5188
5189 } else {
5190
5191 @unlink($dir . DIRECTORY_SEPARATOR . $object);
5192
5193 }
5194
5195 }
5196
5197 }
5198
5199 @rmdir($dir);
5200
5201 } else {
5202
5203 if (file_exists($dir) && is_file($dir)) {
5204
5205 @unlink($dir);
5206
5207 }
5208
5209 }
5210
5211 }
5212
5213 public static function forceBackupToStop() {
5214
5215 $filesToBeRemoved = [];
5216
5217 $tmp_dir = BMI_ROOT_DIR . DIRECTORY_SEPARATOR . 'tmp';
5218 if (!is_dir($tmp_dir)) @mkdir($tmp_dir, 0755, true);
5219
5220 foreach (scandir($tmp_dir) as $filename) {
5221
5222 if (in_array($filename, ['.', '..'])) continue;
5223 $path = BMI_ROOT_DIR . DIRECTORY_SEPARATOR . 'tmp' . DIRECTORY_SEPARATOR . $filename;
5224 $filesToBeRemoved[] = $path;
5225
5226 }
5227
5228 $allowedFiles = ['wp-config.php', '.htaccess', '.litespeed', '.default.json', 'driveKeys.php', 'dropboxKeys.php', '.autologin.php', '.migrationFinished', 'onedriveKeys.php', 'awsKeys.php', 'wasabiKeys.php', 'backupblissKeys.php', 'sftpKeys.php', 'pcloudKeys.php'];
5229 foreach (glob(BMI_TMP . DIRECTORY_SEPARATOR . '.*') as $filename) {
5230
5231 $basename = basename($filename);
5232
5233 if (in_array($basename, ['.', '..'])) continue;
5234 if (is_file($filename) && !in_array($basename, $allowedFiles)) {
5235 $filesToBeRemoved[] = $filename;
5236 }
5237
5238 }
5239
5240 foreach (glob(BMI_TMP . DIRECTORY_SEPARATOR . 'BMI-*', GLOB_ONLYDIR) as $filename) {
5241
5242 $basename = basename($filename);
5243
5244 if (in_array($basename, ['.', '..'])) continue;
5245 if (is_dir($filename) && !in_array($filename, $allowedFiles)) {
5246 $filesToBeRemoved[] = $filename;
5247 }
5248
5249 }
5250
5251 foreach (glob(BMI_TMP . DIRECTORY_SEPARATOR . 'bg-BMI-*', GLOB_ONLYDIR) as $filename) {
5252
5253 $basename = basename($filename);
5254
5255 if (in_array($basename, ['.', '..'])) continue;
5256 if (is_dir($filename) && !in_array($filename, $allowedFiles)) {
5257 $filesToBeRemoved[] = $filename;
5258 }
5259
5260 }
5261
5262 $filesToBeRemoved[] = BMI_BACKUPS . DIRECTORY_SEPARATOR . '.backup_cli_lock';
5263 $filesToBeRemoved[] = BMI_BACKUPS . DIRECTORY_SEPARATOR . '.backup_cli_lock_ended';
5264 $filesToBeRemoved[] = BMI_BACKUPS . DIRECTORY_SEPARATOR . '.backup_cli_lock_end';
5265 $filesToBeRemoved[] = BMI_BACKUPS . DIRECTORY_SEPARATOR . '.last_triggered';
5266 $filesToBeRemoved[] = BMI_BACKUPS . DIRECTORY_SEPARATOR . '.abort';
5267 $filesToBeRemoved[] = BMI_BACKUPS . DIRECTORY_SEPARATOR . '.running';
5268 $filesToBeRemoved[] = BMI_BACKUPS . DIRECTORY_SEPARATOR . '.cron';
5269 $filesToBeRemoved[] = BMI_BACKUPS . DIRECTORY_SEPARATOR . '.space_check';
5270 $filesToBeRemoved[] = BMI_TMP . DIRECTORY_SEPARATOR . 'db_tables';
5271 $filesToBeRemoved[] = BMI_TMP . DIRECTORY_SEPARATOR . 'bmi_backup_manifest.json';
5272 $filesToBeRemoved[] = BMI_TMP . DIRECTORY_SEPARATOR . '.backup_name';
5273 $filesToBeRemoved[] = BMI_TMP . DIRECTORY_SEPARATOR . 'files_latest.list';
5274 $filesToBeRemoved[] = BMI_TMP . DIRECTORY_SEPARATOR . 'currentBackupConfig.php';
5275
5276 if (file_exists(BMI_BACKUPS . DIRECTORY_SEPARATOR . '.running')) {
5277 $currentlyRunningBackupName = file_get_contents(BMI_BACKUPS . DIRECTORY_SEPARATOR . '.running');
5278 if ($currentlyRunningBackupName) {
5279 foreach (glob(BMI_BACKUPS . DIRECTORY_SEPARATOR . $currentlyRunningBackupName . '*') as $filename) {
5280 $filesToBeRemoved[] = $filename;
5281 }
5282 }
5283 }
5284
5285 if (is_array($filesToBeRemoved) || is_object($filesToBeRemoved)) {
5286 foreach ((array) $filesToBeRemoved as $file) {
5287 self::rrmdir($file);
5288 }
5289 }
5290
5291 return ['status' => 'success'];
5292
5293 }
5294
5295 public static function forceRestoreToStop() {
5296
5297 $filesToBeRemoved = [];
5298
5299 $themedir = get_theme_root();
5300 $tempTheme = $themedir . DIRECTORY_SEPARATOR . 'backup_migration_restoration_in_progress';
5301 $currentTemplate = get_option('template');
5302 $currentStylesheet = get_option('stylesheet');
5303
5304 $templateExists = !empty($currentTemplate) && file_exists($themedir . DIRECTORY_SEPARATOR . $currentTemplate) && is_dir($themedir . DIRECTORY_SEPARATOR . $currentTemplate);
5305 $stylesheetExists = !empty($currentStylesheet) && file_exists($themedir . DIRECTORY_SEPARATOR . $currentStylesheet) && is_dir($themedir . DIRECTORY_SEPARATOR . $currentStylesheet);
5306
5307 // If either the template or stylesheet is still the temporary restoration theme, or if either does not exist on disk,
5308 // both template and stylesheet must be reverted together to maintain parent/child theme compatibility.
5309 $shouldRevertThemeAndPlugins = ($currentTemplate === 'backup_migration_restoration_in_progress' ||
5310 $currentStylesheet === 'backup_migration_restoration_in_progress' ||
5311 !$templateExists ||
5312 !$stylesheetExists);
5313
5314 if ($shouldRevertThemeAndPlugins) {
5315 $prevTemplate = '';
5316 $prevStylesheet = '';
5317
5318 if (file_exists($tempTheme . DIRECTORY_SEPARATOR . '.previous_theme')) {
5319 $prevTemplate = trim(file_get_contents($tempTheme . DIRECTORY_SEPARATOR . '.previous_theme'));
5320 }
5321 if (file_exists($tempTheme . DIRECTORY_SEPARATOR . '.previous_stylesheet')) {
5322 $prevStylesheet = trim(file_get_contents($tempTheme . DIRECTORY_SEPARATOR . '.previous_stylesheet'));
5323 }
5324
5325 $prevTemplateExists = !empty($prevTemplate) && file_exists($themedir . DIRECTORY_SEPARATOR . $prevTemplate) && is_dir($themedir . DIRECTORY_SEPARATOR . $prevTemplate);
5326 $prevStylesheetExists = !empty($prevStylesheet) && file_exists($themedir . DIRECTORY_SEPARATOR . $prevStylesheet) && is_dir($themedir . DIRECTORY_SEPARATOR . $prevStylesheet);
5327
5328 if ($prevTemplateExists && $prevStylesheetExists) {
5329 update_option('template', $prevTemplate);
5330 update_option('stylesheet', $prevStylesheet);
5331 } elseif ($prevTemplateExists) {
5332 update_option('template', $prevTemplate);
5333 update_option('stylesheet', $prevTemplate);
5334 } elseif (!($templateExists && $stylesheetExists && $currentTemplate !== 'backup_migration_restoration_in_progress' && $currentStylesheet !== 'backup_migration_restoration_in_progress')) {
5335 if (function_exists('wp_get_themes')) {
5336 $allThemes = wp_get_themes();
5337 if (is_array($allThemes) && !empty($allThemes)) {
5338 reset($allThemes);
5339 $firstThemeSlug = key($allThemes);
5340 if (!empty($firstThemeSlug) && file_exists($themedir . DIRECTORY_SEPARATOR . $firstThemeSlug) && is_dir($themedir . DIRECTORY_SEPARATOR . $firstThemeSlug)) {
5341 update_option('template', $firstThemeSlug);
5342 update_option('stylesheet', $firstThemeSlug);
5343 }
5344 }
5345 }
5346 }
5347
5348 if (file_exists($tempTheme . DIRECTORY_SEPARATOR . '.earlier_active_plugins')) {
5349 $earlierPlugins = unserialize(file_get_contents($tempTheme . DIRECTORY_SEPARATOR . '.earlier_active_plugins'));
5350 if (is_array($earlierPlugins)) {
5351 $validPlugins = [];
5352 foreach ($earlierPlugins as $plugin) {
5353 if (is_string($plugin) && !empty($plugin) && file_exists(WP_PLUGIN_DIR . DIRECTORY_SEPARATOR . $plugin)) {
5354 $validPlugins[] = $plugin;
5355 }
5356 }
5357 if (!in_array('backup-backup/backup-backup.php', $validPlugins) && file_exists(WP_PLUGIN_DIR . DIRECTORY_SEPARATOR . 'backup-backup/backup-backup.php')) {
5358 $validPlugins[] = 'backup-backup/backup-backup.php';
5359 }
5360 update_option('active_plugins', array_values(array_unique($validPlugins)));
5361 }
5362 }
5363 }
5364
5365 $filesToBeRemoved[] = $tempTheme;
5366
5367 $tmpDirectory = BMI_ROOT_DIR . DIRECTORY_SEPARATOR . 'tmp';
5368 if (!is_dir($tmpDirectory)) @mkdir($tmpDirectory, 0755, true);
5369
5370 foreach (scandir($tmpDirectory) as $filename) {
5371
5372 if (in_array($filename, ['.', '..'])) continue;
5373 $path = BMI_ROOT_DIR . DIRECTORY_SEPARATOR . 'tmp' . DIRECTORY_SEPARATOR . $filename;
5374 $filesToBeRemoved[] = $path;
5375
5376 }
5377
5378 foreach (glob(BMI_TMP . DIRECTORY_SEPARATOR . 'backup-migration_??????????') as $filename) {
5379
5380 $basename = basename($filename);
5381
5382 if (is_dir($filename) && !in_array($basename, ['.', '..'])) {
5383 $filesToBeRemoved[] = $filename;
5384 }
5385
5386 }
5387
5388 $allowedFiles = ['wp-config.php', '.htaccess', '.litespeed', '.default.json', 'driveKeys.php', 'dropboxKeys.php', '.autologin.php', '.migrationFinished', 'onedriveKeys.php','awsKeys.php', 'wasabiKeys.php', 'backupblissKeys.php', 'sftpKeys.php', 'pcloudKeys.php'];
5389 foreach (glob(BMI_TMP . DIRECTORY_SEPARATOR . '.*') as $filename) {
5390
5391 $basename = basename($filename);
5392
5393 if (in_array($basename, ['.', '..'])) continue;
5394 if (is_file($filename) && !in_array($basename, $allowedFiles)) {
5395 $filesToBeRemoved[] = $filename;
5396 }
5397
5398 }
5399
5400 foreach (glob(BMI_TMP . DIRECTORY_SEPARATOR . 'restore_scan_*') as $filename) {
5401
5402 $basename = basename($filename);
5403
5404 if (in_array($basename, ['.', '..'])) continue;
5405 if (is_file($filename) && !in_array($basename, $allowedFiles)) {
5406 $filesToBeRemoved[] = $filename;
5407 }
5408
5409 }
5410
5411 foreach (glob(untrailingslashit(ABSPATH) . DIRECTORY_SEPARATOR . 'wp-config.??????????.php') as $filename) {
5412
5413 $basename = basename($filename);
5414
5415 if (in_array($basename, ['.', '..'])) continue;
5416 if (is_file($filename) && !in_array($filename, $allowedFiles)) {
5417 $filesToBeRemoved[] = $filename;
5418 }
5419
5420 }
5421
5422 $filesToBeRemoved[] = BMI_BACKUPS . DIRECTORY_SEPARATOR . '.migration_lock';
5423 $filesToBeRemoved[] = BMI_BACKUPS . DIRECTORY_SEPARATOR . '.migration_lock_cli';
5424 $filesToBeRemoved[] = BMI_BACKUPS . DIRECTORY_SEPARATOR . '.migration_lock_cli_end';
5425 $filesToBeRemoved[] = BMI_BACKUPS . DIRECTORY_SEPARATOR . '.migration_lock_ended';
5426 $filesToBeRemoved[] = BMI_BACKUPS . DIRECTORY_SEPARATOR . '.cli_download_last';
5427 $filesToBeRemoved[] = BMI_BACKUPS . DIRECTORY_SEPARATOR . '.running';
5428 $filesToBeRemoved[] = BMI_BACKUPS . DIRECTORY_SEPARATOR . '.space_check';
5429 $filesToBeRemoved[] = BMI_TMP . DIRECTORY_SEPARATOR . '.restore_secret';
5430 $filesToBeRemoved[] = BMI_TMP . DIRECTORY_SEPARATOR . '.table_map';
5431
5432 if (is_array($filesToBeRemoved) || is_object($filesToBeRemoved)) {
5433 foreach ((array) $filesToBeRemoved as $file) {
5434 self::rrmdir($file);
5435 }
5436 }
5437
5438 return ['status' => 'success'];
5439
5440 }
5441
5442 public function sendTroubleshootingDetails($send_type = 'manual', $triggeredBy = false, $blocking = true) {
5443
5444 global $table_prefix;
5445
5446 require_once BMI_INCLUDES . DIRECTORY_SEPARATOR . 'check' . DIRECTORY_SEPARATOR . 'system_info.php';
5447 $bmiSiteData = new SI();
5448 $bmiSiteData = $bmiSiteData->to_array();
5449 $bmiSiteData['database_size'] = $this->getDatabaseSize();
5450 $bmiSiteData['database_size_mb'] = BMP::humanSize($bmiSiteData['database_size']);
5451 $bmiSiteData['xhria'] = get_option('z__bmi_xhria', 'none');
5452 $bmiSiteData['current_table_prefix'] = $table_prefix;
5453
5454 $wpconfigPath = ABSPATH . DIRECTORY_SEPARATOR . 'wp-config.php';
5455 if (file_exists($wpconfigPath)) {
5456 $bmiSiteData['is_wp_config_writable'] = is_writable($wpconfigPath) ? "yes" : "no";
5457 } else {
5458 $bmiSiteData['is_wp_config_writable'] = "file_does_not_exist?";
5459 }
5460
5461 $latestBackupLogs = 'does_not_exist';
5462 $latestBackupProgress = 'does_not_exist';
5463 $latestRestorationLogs = 'does_not_exist';
5464 $latestRestorationProgress = 'does_not_exist';
5465 $latestStagingLogs = 'does_not_exist';
5466 $latestStagingProgress = 'does_not_exist';
5467 $currentPluginConfig = 'does_not_exist';
5468 $pluginGlobalLogs = 'does_not_exist';
5469 $backgroundErrors = 'does_not_exist';
5470
5471 if (file_exists(BMI_BACKUPS . '/latest.' . BMI_LOGS_SUFFIX . '.log')) {
5472 $latestBackupLogs = file_get_contents(BMI_BACKUPS . '/latest.' . BMI_LOGS_SUFFIX . '.log');
5473 }
5474
5475 if (file_exists(BMI_BACKUPS . '/latest_progress.' . BMI_LOGS_SUFFIX . '.log')) {
5476 $latestBackupProgress = file_get_contents(BMI_BACKUPS . '/latest_progress.' . BMI_LOGS_SUFFIX . '.log');
5477 }
5478
5479 if (file_exists(BMI_BACKUPS . '/latest_migration.' . BMI_LOGS_SUFFIX . '.log')) {
5480 $latestRestorationLogs = file_get_contents(BMI_BACKUPS . '/latest_migration.' . BMI_LOGS_SUFFIX . '.log');
5481 }
5482
5483 if (file_exists(BMI_BACKUPS . '/latest_migration_progress.' . BMI_LOGS_SUFFIX . '.log')) {
5484 $latestRestorationProgress = file_get_contents(BMI_BACKUPS . '/latest_migration_progress.' . BMI_LOGS_SUFFIX . '.log');
5485 }
5486
5487 if (file_exists(BMI_STAGING . '/latest_staging.' . BMI_LOGS_SUFFIX . '.log')) {
5488 $latestStagingLogs = file_get_contents(BMI_STAGING . '/latest_staging.' . BMI_LOGS_SUFFIX . '.log');
5489 }
5490
5491 if (file_exists(BMI_STAGING . '/latest_staging_progress.' . BMI_LOGS_SUFFIX . '.log')) {
5492 $latestStagingProgress = file_get_contents(BMI_STAGING . '/latest_staging_progress.' . BMI_LOGS_SUFFIX . '.log');
5493 }
5494
5495 $currentPluginConfig = Dashboard\BMI_Config_V2::get_instance()->get_all();
5496 unset($currentPluginConfig['OTHER:EMAIL']);
5497 $currentPluginConfig = json_encode($currentPluginConfig);
5498
5499 $completeLogsPath = BMI_CONFIG_DIR . DIRECTORY_SEPARATOR . 'complete_logs.' . BMI_LOGS_SUFFIX . '.log';
5500 if (file_exists($completeLogsPath)) {
5501 $fileSize = filesize($completeLogsPath);
5502 if ($fileSize <= 65535) {
5503 $pluginGlobalLogs = file_get_contents($completeLogsPath);
5504 } else {
5505 $fp = fopen($completeLogsPath, 'rb');
5506 if ($fp) {
5507 $seekPos = max(0, $fileSize - 65509 ); // Read last 64KB
5508 fseek($fp, $seekPos, SEEK_SET);
5509 $lastBytes = fread($fp, 65509);
5510 fclose($fp);
5511
5512 $pluginGlobalLogs = 'file_too_large, last 64KB:' . "\n" . $lastBytes;
5513
5514 file_put_contents($completeLogsPath, $lastBytes);
5515 } else {
5516 $pluginGlobalLogs = 'could_not_open_file';
5517 }
5518 }
5519 }
5520
5521 $backgroundLogsPath = BMI_CONFIG_DIR . DIRECTORY_SEPARATOR . 'background-errors.' . BMI_LOGS_SUFFIX . '.log';
5522 if (file_exists($backgroundLogsPath)) {
5523 if ((filesize($backgroundLogsPath) / 1024 / 1024) <= 4) {
5524 $backgroundErrors = file_get_contents($backgroundLogsPath);
5525 } else {
5526 @unlink($backgroundLogsPath);
5527 @touch($backgroundLogsPath);
5528 $backgroundErrors = 'file_too_large';
5529 }
5530 }
5531
5532 $ifCLI = false;
5533 if (defined('BMI_USING_CLI_FUNCTIONALITY') && BMI_USING_CLI_FUNCTIONALITY === true) {
5534 $ifCLI = true;
5535 }
5536
5537 $logsSourceFrontEnd = 'manual';
5538 if ($triggeredBy != false) {
5539 $logsSourceFrontEnd = $triggeredBy;
5540 }
5541 if (isset($this->post['source']) && in_array($this->post['source'], ['backup', 'migration', 'staging'])) {
5542 $logsSourceFrontEnd = $this->post['source'];
5543 }
5544
5545 $latestBackupLogs = preg_replace('/\:\ ((.*)\.zip)/', ': *****.zip', $latestBackupLogs);
5546 $latestRestorationLogs = preg_replace('/bmi\-id\=(.*)\.zip/', 'bmi-id=[***redacted***].zip', $latestRestorationLogs);
5547 $latestStagingLogs = preg_replace('/\:\ ((.*)\.zip)/', ': *****.zip', $latestStagingLogs);
5548
5549 $url = 'https://' . BMI_API_BACKUPBLISS_PUSH . '/v1' . '/push';
5550 $data = array(
5551 'method' => 'POST',
5552 'timeout' => 15,
5553 'blocking' => $blocking,
5554 'sslverify' => false,
5555 'send_type' => $send_type,
5556 'body' => array(
5557 'admin_url' => admin_url(),
5558 'home_url' => home_url(),
5559 'site_url' => get_site_url(),
5560 'is_multisite' => is_multisite() ? "yes" : "no",
5561 'is_abspath_writable' => is_writable(ABSPATH) ? "yes" : "no",
5562 'site_information' => $bmiSiteData,
5563 'latest_backup_logs' => $latestBackupLogs,
5564 'latest_backup_progress' => $latestBackupProgress,
5565 'latest_restoration_logs' => $latestRestorationLogs,
5566 'latest_restoration_progress' => $latestRestorationProgress,
5567 'latest_staging_logs' => $latestStagingLogs,
5568 'latest_staging_progress' => $latestStagingProgress,
5569 'current_plugin_config' => $currentPluginConfig,
5570 'plugin_global_logs' => $pluginGlobalLogs,
5571 'background_errors' => $backgroundErrors,
5572 'triggered_by' => $logsSourceFrontEnd,
5573 'is_defined' => defined('BMI_BACKUP_PRO') ? 'yes' : 'no',
5574 'is_cli' => $ifCLI
5575 )
5576 );
5577
5578 $disabled_functions = explode(',', ini_get('disable_functions'));
5579 $vA = !in_array('curl_exec', $disabled_functions);
5580 $vB = !in_array('curl_init', $disabled_functions);
5581 $vC = !in_array('http_build_query', $disabled_functions);
5582 $vD = !in_array('stream_context_create', $disabled_functions);
5583 $vE = !in_array('file_get_contents', $disabled_functions);
5584 $vF = false;
5585 $response = false;
5586
5587 if (function_exists('curl_version') && function_exists('curl_exec') && function_exists('curl_init') && $vA && $vB) {
5588
5589 $response = wp_remote_post($url, $data);
5590
5591 } else {
5592
5593 if (ini_get('allow_url_fopen') == true && $vC && $vD && $vE) {
5594
5595 $vF = true;
5596 $postdata = http_build_query($data['body']);
5597
5598 $opts = [
5599 'ssl' => [ 'verify_peer_name' => false, 'verify_peer' => false ],
5600 'http' => [
5601 'method' => 'POST',
5602 'header' => 'Content-type: application/x-www-form-urlencoded',
5603 'content' => $postdata
5604 ]
5605 ];
5606
5607 $context = stream_context_create($opts);
5608 $result = file_get_contents($url, false, $context);
5609
5610 $response = [ 'body' => $result ];
5611
5612 }
5613
5614 }
5615
5616 if ($response === false || is_wp_error($response)) {
5617 $error_message = $response->get_error_message();
5618 Logger::error($error_message, 'backup-backup');
5619 return ['status' => 'fail'];
5620 } else {
5621 try {
5622 $body = json_decode($response['body']);
5623 if (isset($body->code)) {
5624 return ['status' => 'success', 'code' => sanitize_text_field($body->code)];
5625 } else {
5626 return ['status' => 'fail'];
5627 }
5628 } catch (\Exception $e) {
5629 Logger::error(print_r($e, true), 'backup-backup');
5630 return ['status' => 'fail'];
5631 } catch (\Throwable $t) {
5632 Logger::error(print_r($t, true), 'backup-backup');
5633 return ['status' => 'fail'];
5634 }
5635 }
5636
5637 }
5638
5639 public function actionsAfterProcess($success = false, $triggeredBy = 'backup', $isCron = false) {
5640
5641 $afterMigrationLock = BMI_TMP . DIRECTORY_SEPARATOR . '.migrationFinished';
5642 if ($success) {
5643
5644 file_put_contents($afterMigrationLock, '');
5645 Logger::log("Process (" . $triggeredBy . ") finished successfully via ajax.php");
5646
5647 if ($triggeredBy === 'backup' && !wp_next_scheduled('bmip_keepalive_cron')) {
5648 wp_schedule_single_event(time() + 15, 'bmip_keepalive_cron');
5649 }
5650
5651 } else {
5652
5653 Logger::log("Process (" . $triggeredBy . ") finished with errors via ajax.php");
5654 if (file_exists($afterMigrationLock)) @unlink($afterMigrationLock);
5655
5656 }
5657
5658 if ($triggeredBy === 'backup' && $isCron) BMP::handle_after_cron();
5659
5660 if (file_exists(BMI_TMP . DIRECTORY_SEPARATOR . 'restore_parts.json')) @unlink(BMI_TMP . DIRECTORY_SEPARATOR . 'restore_parts.json');
5661
5662
5663 if (has_action('bmi_premium_after_process') || (defined('BACKUP_TRIGGERED_BY_URL') && BACKUP_TRIGGERED_BY_URL === true)){
5664 do_action('bmi_premium_after_process', $success, $triggeredBy, defined('BACKUP_TRIGGERED_BY_URL') && BACKUP_TRIGGERED_BY_URL === true);
5665 }
5666
5667
5668 return null;
5669
5670 // REMOVED CODE:
5671 // $canShare = BMP::canShareLogsOrShouldAsk();
5672 // if ($canShare === 'allowed') {
5673 //
5674 // $send_type = 'error';
5675 // if ($success) $send_type = 'success';
5676 // $this->sendTroubleshootingDetails($send_type, $triggeredBy, false);
5677 //
5678 // }
5679
5680 }
5681
5682 public function logSharing() {
5683
5684 $type = $this->post['question'];
5685
5686 if ($type == 'set_yes') {
5687
5688 // $isOk = Dashboard\bmi_set_config('LOGS::SHARING', 'yes');
5689 // update_option('BMI_LOGS_SHARING_IS_ALLOWED', 'yes');
5690 return ['status' => 'success'];
5691
5692 } else if ($type == 'set_no') {
5693
5694 // $isOk = Dashboard\bmi_set_config('LOGS::SHARING', 'no');
5695 // update_option('BMI_LOGS_SHARING_IS_ALLOWED', 'no');
5696 return ['status' => 'success'];
5697
5698 } else if ($type == 'is_allowed') {
5699
5700 // $canShare = BMP::canShareLogsOrShouldAsk();
5701 // return ['status' => 'success', 'result' => $canShare];
5702 return ['status' => 'success', 'result' => 'not-allowed'];
5703
5704 } else {
5705
5706 return ['status' => 'fail'];
5707
5708 }
5709
5710 }
5711
5712 public function getLatestBackupFile() {
5713 if (get_transient('bmi_latest_backup_file') !== false) {
5714 return base64_decode(get_transient('bmi_latest_backup_file'));
5715 }
5716 $dir = BMI_BACKUPS;
5717 $backupdir = array_diff(scandir($dir), ['..', '.']);
5718 $backups = [];
5719 foreach ($backupdir as $index => $name) {
5720
5721 $ext = pathinfo($dir . DIRECTORY_SEPARATOR . $name, PATHINFO_EXTENSION);
5722
5723 if (in_array($ext, ['zip', 'tar', 'gz'])) {
5724 $backups[] = [
5725 'cdate' => filemtime($dir . DIRECTORY_SEPARATOR . $name),
5726 'name' => $name
5727 ];
5728 }
5729
5730 }
5731
5732 usort($backups, function ($a, $b) {
5733 if (intval($a['cdate']) < intval($b['cdate'])) return 1;
5734 else return -1;
5735 });
5736
5737 $backups = array_values($backups);
5738
5739 if (sizeof($backups) > 0) {
5740 return $backups[0]['name'];
5741 } else {
5742 return '---';
5743 }
5744
5745 }
5746
5747 /**
5748 * isStagingSiteCreationOngoing - Checks if the process is ongoing or not
5749 *
5750 * @return {bool} true if the process is running false if its not
5751 */
5752 public function isStagingSiteCreationOngoing() {
5753
5754 $staging_lock = BMI_STAGING . '/.staging_lock';
5755 if (file_exists($staging_lock) && (time() - filemtime($staging_lock)) <= 15) {
5756 return true;
5757 } else {
5758 return false;
5759 }
5760
5761 }
5762
5763 /**
5764 * checkStagingLocalName - Verifies name of staging site and checks if it's not currently running
5765 * Can be called for verification pre start or during start on initial request
5766 *
5767 * @param {string} $name = false name of staging site if called without ajax
5768 * @return {array} with status/fail/progress data
5769 */
5770 public function checkStagingLocalName($name = false) {
5771
5772 if ($name == false && isset($this->post['name'])) {
5773 $name = $this->post['name'];
5774 }
5775
5776 $ongoing = __('Staging site creation is already ongoing, please wait and try again.', 'backup-backup');
5777 $empty = __('You have to provide some staging site name before process.', 'backup-backup');
5778 $toolong = __('Staging site name cannot be longer than 24 characters.', 'backup-backup');
5779 $invalid = __('Provided name contains prohibited characters.', 'backup-backup');
5780 $blacklisted = __('This name is not allowed to be used, please pick different one.', 'backup-backup');
5781 $exist = __('Seems like directory or staging site with that name already exist, pick different one.', 'backup-backup');
5782 $dashes = __('Name cannot start or end with dash or underscore.', 'backup-backup');
5783
5784 if ($this->isStagingSiteCreationOngoing()) {
5785 return ['status' => 'fail', 'message' => $ongoing];
5786 }
5787
5788 if (strlen($name) <= 0) {
5789 return ['status' => 'fail', 'message' => $empty];
5790 }
5791
5792 if (!preg_match('/^[a-zA-Z0-9-_]+$/', $name)) {
5793 return ['status' => 'fail', 'message' => $invalid];
5794 }
5795
5796 if (strlen($name) >= 24) {
5797 return ['status' => 'fail', 'message' => $toolong];
5798 }
5799
5800 if (in_array($name[0], ['_', '-']) || in_array($name[strlen($name) - 1], ['_', '-'])) {
5801 return ['status' => 'fail', 'message' => $dashes];
5802 }
5803
5804 $bannedNames = [
5805 'wp-content',
5806 'wp-admin',
5807 'wp-includes',
5808 'content',
5809 'admin',
5810 'includes',
5811 'tmp',
5812 '.well-known',
5813 'download',
5814 'downloads',
5815 'google',
5816 'temporary'
5817 ];
5818
5819 if (strpos($name, '.') !== false) {
5820 return ['status' => 'fail', 'message' => $blacklisted];
5821 }
5822
5823 if (in_array($name, $bannedNames)) {
5824 return ['status' => 'fail', 'message' => $blacklisted];
5825 }
5826
5827 $path = trailingslashit(ABSPATH) . $name;
5828 if (file_exists($path) || is_dir($path)) {
5829 return ['status' => 'fail', 'message' => $exist];
5830 }
5831
5832 return ['status' => 'success'];
5833
5834 }
5835
5836 /**
5837 * startLocalStagingCreation - Initials creation of staging site process
5838 *
5839 * @return {array} with status/fail/progress data
5840 */
5841 public function startLocalStagingCreation() {
5842
5843 // Verification of state
5844 $name = $this->post['name'];
5845 $verification = $this->checkStagingLocalName($name);
5846 $staging_lock = BMI_STAGING . '/.staging_lock';
5847
5848 // Fail in case of wrong data or state
5849 if (isset($verification['status']) && $verification['status'] != 'success') {
5850 return $verification;
5851 }
5852
5853 // Update lock file to prevent double processes
5854 touch($staging_lock);
5855
5856 // Include local staging site controller
5857 require_once BMI_INCLUDES . '/staging/local.php';
5858 $staging = new StagingLocal($name, true);
5859
5860 // Append the return if staging process requires more batches
5861 if ($staging->continue == true) return $staging->continuationData;
5862
5863 return [ 'status' => 'continue', 'data' => [ 'name' => $name ] ];
5864
5865 }
5866
5867 /**
5868 * stagingSitesGetList - Returns staging sites list
5869 *
5870 * @return {array} with staging sites data
5871 */
5872 public function stagingSitesGetList() {
5873 // Force reconstuct local staging sites configuration
5874 require_once BMI_INCLUDES . '/staging/local.php';
5875 $stagingLocal = new StagingLocal('..ajax..');
5876 $stagingLocal->reconstructConfigurations();
5877
5878 // Include local staging site controller
5879 require_once BMI_INCLUDES . '/staging/controller.php';
5880 $staging = new Staging('..ajax..');
5881 $sites = $staging->getStagingSites();
5882
5883 return [ 'status' => 'success', 'sites' => $sites ];
5884
5885 }
5886
5887 /**
5888 * stagingRename - Renames display name
5889 *
5890 * @return {array} status
5891 */
5892 public function stagingRename() {
5893
5894 $name = $this->post['name'];
5895 $newName = $this->post['new'];
5896
5897 // Include local staging site controller
5898 require_once BMI_INCLUDES . '/staging/controller.php';
5899 $staging = new Staging('..ajax..');
5900 return $staging->rename($name, $newName);
5901
5902 }
5903
5904 /**
5905 * stagingPrepareLogin - Prepares login script
5906 *
5907 * @return {array} login credentials
5908 */
5909 public function stagingPrepareLogin() {
5910
5911 $name = $this->post['name'];
5912
5913 // Include local staging site controller
5914 require_once BMI_INCLUDES . '/staging/controller.php';
5915 $staging = new Staging('..ajax..');
5916 return $staging->prepareLogin($name);
5917
5918 }
5919
5920 /**
5921 * Handles secure backup via browser method
5922 */
5923 public function backupBrowserMethodHandler() {
5924 $this->backupErrorHandler();
5925
5926 try {
5927
5928 // Load bypasser
5929 require_once BMI_INCLUDES . '/backup-process.php';
5930 $request = new Bypasser(false, BMI_CONFIG_DIR, trailingslashit(WP_CONTENT_DIR), BMI_BACKUPS, trailingslashit(ABSPATH), plugin_dir_path(BMI_ROOT_FILE));
5931
5932 // Handle request
5933 $request->handle_batch();
5934 $request->shutdown();
5935 return;
5936
5937 } catch (\Exception $e) {
5938
5939 error_log('There was an error with Backup Migration plugin: ' . $e->getMessage());
5940 Logger::error(__('Error handler: ', 'backup-backup') . 'ajax#01' . '|' . $e->getMessage());
5941 error_log(strval($e));
5942
5943 } catch (\Throwable $t) {
5944
5945 error_log('There was an error with Backup Migration plugin: ' . $t->getMessage());
5946 Logger::error(__('Error handler: ', 'backup-backup') . 'ajax#01' . '|' . $t->getMessage());
5947 error_log(strval($t));
5948
5949 }
5950
5951 return [ 'status' => 'error' ];
5952
5953 }
5954
5955 /**
5956 * Handles ajax error on browser side, keep alive timeout etc.
5957 *
5958 * @return array static success
5959 */
5960 public function frontEndAjaxError() {
5961
5962 if ($this->post['call'] == 'create-backup') {
5963 require_once BMI_INCLUDES . '/progress/zip.php';
5964 $logger = new Progress('', 0, 0, false, false);
5965 } else if (in_array($this->post['call'], ['restore-backup', 'download-backup', 'continue_restore_process'])) {
5966 require_once BMI_INCLUDES . '/progress/migration.php';
5967 $logger = new MigrationProgress(true);
5968 } else if (in_array($this->post['call'], ['staging-start-local-creation', 'staging-local-creation-process', 'staging-tastewp-creation-process'])) {
5969 require_once BMI_INCLUDES . '/progress/staging.php';
5970 $logger = new StagingProgress(true);
5971 }
5972
5973 if (isset($this->post['error'])) {
5974
5975 Logger::error('Front End Ajax Error START');
5976 if (isset($logger)) $logger->log('Front End Ajax Error START', 'verbose');
5977
5978 if (is_array($this->post['error'])) {
5979
5980 $errors = $this->post['error'];
5981 foreach ($errors as $k => $val) {
5982 $error = sanitize_text_field(print_r($val, true));
5983 Logger::error($k . ' = ' . $error);
5984 if (isset($logger)) $logger->log($k . ' = ' . $error, 'verbose');
5985 }
5986
5987 } else {
5988
5989 $theError = sanitize_text_field(print_r($this->post->error, true));
5990 Logger::error('Front End Ajax Error: ' . $theError);
5991 if (isset($logger)) $logger->log($theError, 'verbose');
5992
5993 }
5994
5995 Logger::error('Front End Ajax Error END');
5996 if (isset($logger)) $logger->log('Front End Ajax Error END', 'verbose');
5997
5998 } else {
5999
6000 Logger::error('Front End Ajax Error was called, but no error included.');
6001
6002 }
6003
6004 if (isset($logger)) {
6005 $logger->log(__('Browser-side error detected, the process will try to restart with alternative methods, otherwise it will throw error window.', 'backup-backup'), 'error');
6006 $logger->log('Browser-side error detected, the process will try to restart with alternative methods, otherwise it will throw error window.', 'verbose');
6007 }
6008
6009 return [ 'status' => 'success' ];
6010
6011 }
6012
6013 /**
6014 * stagingDelete - Removes the staging site
6015 *
6016 * @return {array} status
6017 */
6018 public function stagingDelete() {
6019
6020 $name = $this->post['name'];
6021
6022 // Include local staging site controller
6023 require_once BMI_INCLUDES . '/staging/controller.php';
6024 $staging = new Staging('..ajax..');
6025 return $staging->delete($name);
6026
6027 }
6028
6029 /**
6030 * localStagingCreationProcess - Method that can continue batching of Staging process
6031 *
6032 * @return {array} data that should be send back to this function as POST
6033 */
6034 public function localStagingCreationProcess() {
6035
6036 // Get $name and declare lock file
6037 $name = $this->post['name'];
6038 $staging_lock = BMI_STAGING . '/.staging_lock';
6039
6040 // Update lock file to prevent double processes
6041 touch($staging_lock);
6042
6043 // Include local staging site controller
6044 require_once BMI_INCLUDES . '/staging/local.php';
6045 $staging = new StagingLocal($name);
6046
6047 // Process handler
6048 if (isset($this->post['delete'])) {
6049 $staging->requestDelete();
6050 } else $staging->continueProcess();
6051
6052 // Append the return if staging process requires more batches
6053 if ($staging->continue == true) return $staging->continuationData;
6054
6055 // Send success if nothing went wrong which finishes the process
6056 if (file_exists($staging_lock)) @unlink($staging_lock);
6057 return ['status' => 'error'];
6058
6059 }
6060
6061 /**
6062 * tastewpStagingCreation - Initializes and declares staging site will
6063 *
6064 * @return {array} batching status
6065 */
6066 public function tastewpStagingCreation() {
6067
6068 // Get $name and declare lock file
6069 $name = $this->post['name'];
6070 $backupName = isset($this->post['backupName']) ? $this->post['backupName'] : false;
6071 $initialize = isset($this->post['initialize']) ? $this->post['initialize'] : false;
6072 $staging_lock = BMI_STAGING . '/.staging_lock';
6073
6074 // Fix var type
6075 if ($initialize === true || $initialize == 'true') $initialize = true;
6076 else $initialize = false;
6077
6078 // Update lock file to prevent double processes
6079 touch($staging_lock);
6080
6081 // Include TasteWP staging site controller
6082 require_once BMI_INCLUDES . '/staging/tastewp.php';
6083
6084 // Process handler
6085 if (isset($this->post['delete'])) {
6086 $delete = true;
6087 } else $delete = false;
6088
6089 // Make first handshake with TasteWP
6090 $staging = new StagingTasteWP($name, $initialize, $backupName, $delete);
6091
6092 // Append the return if staging process requires more batches
6093 if ($staging->continue == true) return $staging->continuationData;
6094
6095 // Send success if nothing went wrong which finishes the process
6096 if (file_exists($staging_lock)) @unlink($staging_lock);
6097 return ['status' => 'error'];
6098
6099 }
6100
6101 public function debugging() {
6102
6103 require_once BMI_INCLUDES . DIRECTORY_SEPARATOR . 'scanner' . DIRECTORY_SEPARATOR . 'backups.php';
6104 $backups = Backups::getInstance();
6105 $availableBackups = $backups->getAvailableBackups();
6106 $list = $availableBackups['local'];
6107
6108 // $cron_list = [];
6109 // $cron_dates = [];
6110 // foreach ($list as $key => $value) {
6111 // if ($list[$key][6] == true) {
6112 // if ($list[$key][5] == 'unlocked') {
6113 // $cron_list[$list[$key][1]] = $list[$key][0];
6114 // $cron_dates[] = $list[$key][1];
6115 // }
6116 // }
6117 // }
6118
6119 // usort($cron_dates, function ($a, $b) {
6120 // return (strtotime($a) < strtotime($b)) ? -1 : 1;
6121 // });
6122
6123 // $cron_dates = array_slice($cron_dates, 0, -(intval(Dashboard\bmi_get_config('CRON:KEEP'))));
6124 // foreach ($cron_dates as $key => $value) {
6125 // $name = $cron_list[$cron_dates[$key]];
6126 // $name = explode('#%&', $name)[1];
6127 // Logger::log(__("Removing backup due to keep rules: ", 'backup-backup') . $name);
6128 // @unlink(BMI_BACKUPS . DIRECTORY_SEPARATOR . $name);
6129 // }
6130
6131 if (isset($availableBackups['external']['gdrive'])) {
6132 $sortedMD5s = [];
6133 $gdrive = $availableBackups['external']['gdrive'];
6134 foreach ($gdrive as $md5 => $data) {
6135 if ($gdrive[$md5][6] == true && $gdrive[$md5][5] == 'unlocked') {
6136 $sortedMD5s[] = [$gdrive[$md5][1], $md5];
6137 }
6138 }
6139
6140 usort($sortedMD5s, function ($a, $b) {
6141 return (strtotime($a[0]) < strtotime($b[0])) ? -1 : 1;
6142 });
6143
6144 $gdrive_md5s = array_slice($sortedMD5s, 0, -(intval(Dashboard\bmi_get_config('CRON:KEEP'))));
6145 foreach ($sortedMD5s as $index => $data) {
6146 $md5 = $data[1];
6147
6148 }
6149 }
6150
6151 return ['availableBackups' => $availableBackups, '$gdrive' => $sortedMD5s];
6152
6153 }
6154
6155 public function checkCompatibility() {
6156
6157 $for = isset($this->post['for']) ? $this->post['for'] : 'backup';
6158
6159 require_once BMI_INCLUDES . DIRECTORY_SEPARATOR . 'check' . DIRECTORY_SEPARATOR . 'compatibility.php';
6160 $compatibility = new Compatibility($for);
6161 $errors = $compatibility->check();
6162 return ['status' => 'success', 'data' => $errors, 'mainReasonFound' => $compatibility->mainReasonFound()];
6163 }
6164
6165 public function clickedOnPluginReview() {
6166 update_option('bmi_review_clicked', time());
6167 }
6168
6169
6170 public function checkDiskSpace(){
6171 $file = BMI_BACKUPS . '/' . '.space_check';
6172
6173 $backupSize = BMP::getRecentSize() * 1.4;
6174
6175 try {
6176 $size = $backupSize;
6177 $fh = fopen($file, 'w');
6178 while($size > 0){
6179 $chunk = 1024;
6180 fputs($fh, str_pad('', min($chunk, $size)));
6181 $size -= $chunk;
6182 }
6183 fclose($fh);
6184
6185 $fs = filesize($file);
6186 @unlink($file);
6187
6188 return ['status' => 'enough-space'];
6189
6190
6191 } catch (\Exception $e) {
6192 if (file_exists($file)){
6193 $fileSize = filesize($file);
6194 unlink($file);
6195
6196 return ['status' => 'not-enough-space', 'data' => ['available' => BMP::humanSize(intval($fileSize)), 'required' => BMP::humanSize(intval($backupSize))]];
6197 }
6198
6199 } catch (\Throwable $e) {
6200 if (file_exists($file)){
6201 $fileSize = filesize($file);
6202 unlink($file);
6203 return ['status' => 'not-enough-space', 'data' => ['available' => BMP::humanSize(intval($fileSize)), 'required' => BMP::humanSize(intval($backupSize))]];
6204 }
6205
6206 }
6207 }
6208
6209 public function cleanUpAfterError() {
6210 $runningFile = BMI_BACKUPS . DIRECTORY_SEPARATOR . '.running';
6211 $spaceCheckFile = BMI_BACKUPS . DIRECTORY_SEPARATOR . '.space_check';
6212
6213 if (file_exists($runningFile)){
6214 $backupName = file_get_contents($runningFile);
6215 if (file_exists(BMI_BACKUPS . DIRECTORY_SEPARATOR . $backupName)){
6216 $partialBackup = glob(BMI_BACKUPS . DIRECTORY_SEPARATOR . $backupName . '.??????');
6217 if (is_array($partialBackup) && !empty($partialBackup)){
6218 foreach ($partialBackup as $file){
6219 @unlink($file);
6220 }
6221 }
6222 @unlink(BMI_BACKUPS . DIRECTORY_SEPARATOR . $backupName);
6223 }
6224 @unlink($runningFile);
6225 }
6226
6227 if (file_exists($spaceCheckFile)){
6228 @unlink($spaceCheckFile);
6229 }
6230 if (file_exists(BMI_BACKUPS . '/.cron')) @unlink(BMI_BACKUPS . '/.cron');
6231 }
6232
6233 function manuallyEnqueueUpload(){
6234 $type = isset($this->post['type']) ? $this->post['type'] : '';
6235 $md5 = isset($this->post['md5']) ? $this->post['md5'] : '';
6236 if (empty($type)) {
6237 return ['status' => 'error', 'msg' => __('Missing backup type.', 'backup-backup')];
6238 }
6239
6240 $uploadedBackupStatus = get_option('bmi_uploaded_backups_status', []);
6241
6242 if (!isset($uploadedBackupStatus[$md5]) || !isset($uploadedBackupStatus[$md5][$type])) {
6243 Logger::error("Failed to enqueue backup for upload. Details: " . print_r([$md5, $type, $uploadedBackupStatus], true));
6244 return [
6245 'status' => 'error',
6246 'msg' => __('Something went wrong', 'backup-backup')
6247 ];
6248 }
6249
6250 unset($uploadedBackupStatus[$md5][$type]);
6251 update_option('bmi_uploaded_backups_status', $uploadedBackupStatus);
6252
6253 return ['status' => 'success', 'msg' => __('Backup will be enqueued for upload shortly.', 'backup-backup'), 'data' => ['type' => $type]];
6254 }
6255
6256 /**
6257 *
6258 * @return mixed Response data from the ping server synchronization.
6259 */
6260 public function resyncWithPingServer() {
6261 $response = $this->shareDomainForAutoCron(true);
6262 return $response;
6263 }
6264
6265 }
6266