PluginProbe ʕ •ᴥ•ʔ
Backup Migration / 2.1.6
Backup Migration v2.1.6
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 / initializer.php
backup-backup / includes Last commit date
banner 3 weeks ago bodies 3 weeks ago check 3 weeks ago cli 3 weeks ago cron 3 weeks ago dashboard 3 weeks ago database 3 weeks ago external 3 weeks ago extracter 3 weeks ago htaccess 3 weeks ago notices 3 weeks ago progress 3 weeks ago scanner 3 weeks ago services 3 weeks ago staging 3 weeks ago traits 3 weeks ago uploader 3 weeks ago vendor 3 weeks ago zipper 3 weeks ago .htaccess 3 weeks ago activation.php 3 weeks ago ajax.php 3 weeks ago ajax_offline.php 3 weeks ago analyst.php 3 weeks ago backup-process.php 3 weeks ago class-backup-method-mananger.php 3 weeks ago cli-handler.php 3 weeks ago compatibility.php 3 weeks ago config.php 3 weeks ago constants.php 3 weeks ago file-explorer.php 3 weeks ago initializer.php 3 weeks ago logger.php 3 weeks ago offline.php 3 weeks ago
initializer.php
2059 lines
1 <?php
2
3 // Namespace
4 namespace BMI\Plugin;
5
6 // Exit on direct access
7 if (!defined('ABSPATH')) {
8 exit;
9 }
10
11 // Require classes
12 require_once BMI_INCLUDES . '/logger.php';
13
14 // Alias for classes
15 use BMI\Plugin\BMI_Logger as Logger;
16 use BMI\Plugin\CRON\BMI_Crons as Crons;
17 use BMI\Plugin\Dashboard as Dashboard;
18 use BMI\Plugin\Scanner\BMI_BackupsScanner as Backups;
19 use BMI\Plugin\Heart\BMI_Backup_Heart as Bypasser;
20 use BMI\Plugin\Zipper\BMI_Zipper as Zipper;
21 use BMI\Plugin\Staging\BMI_Staging as Staging;
22 use BMI\Plugin\External\BMI_External_BackupBliss as BackupBliss;
23 use BMI\Plugin\Services\FileHasher;
24
25 /**
26 * Backup Migration Main Class
27 */
28 class Backup_Migration_Plugin {
29 public function initialize() {
30
31 // Determine which BMI version is used
32 add_action('wp_head', function () {
33 echo '<meta name="bmi-version" content="' . esc_attr(BMI_VERSION) . '" />';
34 });
35
36 if (!file_exists(BMI_BACKUPS)) @mkdir(BMI_BACKUPS, 0755, true);
37 if (!file_exists(BMI_STAGING)) @mkdir(BMI_STAGING, 0755, true);
38
39 // Handle PHP CLI functions
40 if (defined('BMI_USING_CLI_FUNCTIONALITY') && BMI_USING_CLI_FUNCTIONALITY === true) {
41
42 // Below all WordPress functions and directives can be accessed
43 if (defined('BMI_CLI_FUNCTION')) {
44
45 if (BMI_CLI_FUNCTION == 'bmi_restore' && defined('BMI_CLI_ARGUMENT')) {
46
47 $_SERVER['HTTP_X_REQUESTED_WITH'] = 'xmlhttprequest';
48 $_POST['f'] = 'restore-backup';
49 if (defined('BMI_CLI_ARGUMENT_2')) {
50 $_POST['remote'] = BMI_CLI_ARGUMENT_2;
51 } else $_POST['remote'] = false;
52 $_POST['file'] = BMI_CLI_ARGUMENT;
53
54 $this->ajax(true);
55
56 } elseif (BMI_CLI_FUNCTION == 'bmi_backup' || BMI_CLI_FUNCTION == 'bmi_backup_cron') {
57
58 if (BMI_CLI_FUNCTION == 'bmi_backup_cron') {
59 define('BMI_DOING_SCHEDULED_BACKUP', true);
60 define('BMI_DOING_SCHEDULED_BACKUP_VIA_CLI', true);
61 }
62
63 $_SERVER['HTTP_X_REQUESTED_WITH'] = 'xmlhttprequest';
64 $_POST['f'] = 'create-backup';
65
66 $this->ajax(true);
67
68 } elseif (BMI_CLI_FUNCTION == 'bmi_quick_migration') {
69
70 $_SERVER['HTTP_X_REQUESTED_WITH'] = 'xmlhttprequest';
71 $_POST['f'] = 'download-backup';
72 $_POST['url'] = BMI_CLI_ARGUMENT;
73
74 $this->ajax(true);
75
76 }
77
78 }
79
80 return;
81
82 }
83
84 if (defined('BMI_RESTORE_SECRET') && defined('BMI_POST_CONTINUE_RESTORE') && constant('BMI_POST_CONTINUE_RESTORE') === true) {
85
86 if (!isset($_POST['bmi_restore_secret'])) exit;
87
88 // Check the secret
89 $bmi_secret_storage = BMI_TMP . DIRECTORY_SEPARATOR . '.restore_secret';
90 if (file_exists($bmi_secret_storage)) {
91 $bmi_saved_secret = file_get_contents($bmi_secret_storage);
92 if ($bmi_saved_secret === $_POST['bmi_restore_secret']) {
93 $bmi_continue_module = true;
94 } else exit;
95 } else exit;
96
97 $_SERVER['HTTP_X_REQUESTED_WITH'] = 'xmlhttprequest';
98 $_POST['f'] = 'continue_restore_process';
99
100 $this->ajax(true);
101
102 return;
103
104 }
105
106 // Hooks
107 register_deactivation_hook(BMI_ROOT_FILE, [&$this, 'deactivation']);
108 require_once BMI_INCLUDES . '/cron/bootstrap.php';
109 \BMI\Plugin\CRON\TaskManager::boot();
110
111 // File downloading
112 add_action('wp_loaded', [&$this, 'handle_downloading']);
113
114 // Additional actions
115 if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'GET') {
116 add_action('wp_loaded', [&$this, 'handle_after_actions'], 1000);
117 }
118 // Handle CRONs
119 add_action('bmi_do_backup_right_now', [&$this, 'handle_cron_backup']);
120 add_action('bmi_handle_cron_check', [&$this, 'handle_cron_check']);
121 add_action('wp_loaded', [&$this, 'handle_crons']);
122 add_action('wp_loaded', [&$this, 'include_offline']);
123 add_action('admin_notices', [&$this, 'incompatibility_notices']);
124 add_action('bmip_fire_action', [&$this, 'fireBMIPAction'], 10, 4);
125 add_action('bmi_backup_upload_completed', [&$this, 'handleUploadComplete']);
126 add_action('bmi_backup_upload_completed', [&$this, 'handle_after_cron']);
127
128 // Return if CRON time
129 if (function_exists('wp_doing_cron') && wp_doing_cron()) return;
130
131 // Check user permissions
132 $user = get_userdata(get_current_user_id());
133 if (!$user || !$user->roles) return;
134 if (!current_user_can('do_backups') && !in_array('administrator', (array) $user->roles)) return;
135
136 if (Dashboard\bmi_get_config('OTHER:PROMOTIONAL:DISPLAY') != 'true') {
137
138 // Include our cool banner
139 include_once BMI_INCLUDES . '/banner/misc.php';
140
141 // Review banner
142 if (!is_dir(WP_PLUGIN_DIR . '/backup-backup-pro')) {
143 if (!(class_exists('\Inisev\Subs\Inisev_Review') || class_exists('Inisev\Subs\Inisev_Review') || class_exists('Inisev_Review'))) {
144 require_once BMI_MODULES_DIR . 'review' . DIRECTORY_SEPARATOR . 'review.php';
145 }
146 $review_banner = new \Inisev\Subs\Inisev_Review(BMI_ROOT_FILE, BMI_ROOT_DIR, 'backup-backup', 'Backup & Migration', 'http://bit.ly/3vdk45L', 'backup-migration');
147 }
148
149 if (!(class_exists('\Inisev\Subs\New_BB_Banner') || class_exists('Inisev\Subs\New_BB_Banner') || class_exists('New_BB_Banner'))) {
150 require_once BMI_MODULES_DIR . 'new-bb-banner' . DIRECTORY_SEPARATOR . 'misc.php';
151 new \Inisev\Subs\New_BB_Banner(BMI_ROOT_FILE, BMI_ROOT_DIR, 'backup-backup', 'Backup & Migration', 'backup-migration');
152 }
153
154
155 // GDrive banner
156 if (!is_dir(WP_PLUGIN_DIR . '/backup-backup-pro')) {
157 if (!(class_exists('\Inisev\Subs\BMI_Banners_GDrive') || class_exists('Inisev\Subs\BMI_Banners_GDrive') || class_exists('BMI_Banners_GDrive'))) {
158 require_once BMI_MODULES_DIR . 'gdrivebanner' . DIRECTORY_SEPARATOR . 'misc.php';
159 }
160 $gdirve_banner = new \Inisev\Subs\BMI_Banners_GDrive('Backup & Migration', 'backup-migration');
161 }
162
163 // Backup banner
164 if (!(class_exists('\Inisev\Subs\BMI_Backup_Banner') || class_exists('Inisev\Subs\BMI_Backup_Banner') || class_exists('BMI_Backup_Banner'))) {
165 require_once BMI_MODULES_DIR . 'backup-banner' . DIRECTORY_SEPARATOR . 'misc.php';
166 new \Inisev\Subs\BMI_Backup_Banner(BMI_ROOT_FILE, BMI_ROOT_DIR, 'backup-backup', 'Backup & Migration', 'backup-migration');
167 }
168
169 }
170
171 // POST Logic
172 if ($_SERVER['REQUEST_METHOD'] === 'POST') {
173
174 // Register AJAX Handler
175 add_action('wp_ajax_backup_migration', [&$this, 'ajax']);
176
177 // Stop GET Registration
178 // return; // Commented because of conflicts with USM Icons
179
180 }
181
182 // Actions
183 add_action('admin_init', [&$this, 'admin_init_hook']);
184 add_action('admin_menu', [&$this, 'submenu']);
185 add_action('admin_notices', [&$this, 'admin_notices']);
186
187
188 // Settings action
189 add_filter('plugin_action_links_' . plugin_basename(BMI_ROOT_FILE), [&$this, 'settings_action']);
190
191 // Whitelist configuration files for Security Ninja
192 add_filter('securityninja_whitelist', [&$this, 'securityninja_whitelist_config_files']);
193
194 // Ignore below actions if those true
195 if (function_exists('wp_doing_ajax') && wp_doing_ajax()) {
196 return;
197 }
198
199 // Styles & scripts
200 add_action('admin_enqueue_scripts', [&$this, 'enqueue_styles']);
201 add_action('admin_enqueue_scripts', [&$this, 'enqueue_scripts']);
202
203 // External storage errors
204 add_action('bmi_external_errors', function() {
205
206 if (file_exists(BMI_INCLUDES . 'notices/dropbox-issues-notice.php'))
207 require_once BMI_INCLUDES . 'notices/dropbox-issues-notice.php';
208
209 if (file_exists(BMI_INCLUDES . '/notices/aws-issues.php'))
210 require_once BMI_INCLUDES . '/notices/aws-issues.php';
211
212 if (file_exists(BMI_INCLUDES . '/notices/google-drive-issues.php'))
213 require_once BMI_INCLUDES . '/notices/google-drive-issues.php';
214
215 if (file_exists(BMI_INCLUDES . '/notices/wasabi-issues.php'))
216 require_once BMI_INCLUDES . '/notices/wasabi-issues.php';
217
218 require_once BMI_INCLUDES . '/notices/backupbliss.php';
219 });
220
221 $upload_issue_notice = $this->backupbliss_space_issues();
222
223 if ($upload_issue_notice) {
224 add_action("admin_notices", function() use ($upload_issue_notice) {
225 $global_warning = true;
226 $error_message = $upload_issue_notice;
227 require_once BMI_INCLUDES . '/external/backupbliss.php';
228 $backupbliss = new BackupBliss();
229 if (!$backupbliss->hasRequiredSpaceBeenFreed()){
230 include BMI_INCLUDES . '/dashboard/modals/bb-warning-notice.php';
231 }
232
233 });
234 }
235
236 }
237
238 public function incompatibility_notices() {
239
240 if (strpos(home_url(), 'instawp') === false && strpos(home_url(), 'playground.wordpress') === false) return;
241
242 $environment = 'sandbox';
243 if (strpos(home_url(), 'instawp') !== false) $environment = 'InstaWP';
244 if (strpos(home_url(), 'playground.wordpress') !== false) $environment = 'WordPress Playground';
245
246 $class = 'notice notice-warning';
247 $message = __('We noticed that you are using %s, our plugin may not work in this environment, please use %s environment instead.', 'backup-backup');
248
249 printf('<div class="%s"><p><b>Backup Migration:</b> %s</p></div>',
250 esc_attr($class),
251 sprintf(
252 esc_html($message),
253 '<i>' . esc_html($environment) . '</i>',
254 '<a href="https://tastewp.com" target="_blank">TasteWP</a>'
255 )
256 );
257 }
258
259 public static function randomString($max = 16) {
260
261 $bank = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
262 $bank .= 'abcdefghijklmnopqrstuvwxyz';
263 $bank .= '0123456789';
264
265 $str = str_shuffle($bank);
266
267 while (is_numeric($str[0])) {
268 $str = str_shuffle($bank);
269 }
270
271 $str = substr($str, 0, $max);
272
273 return $str;
274
275 }
276
277 public static function isFunctionEnabled($func) {
278
279 $disabled = explode(',', ini_get('disable_functions'));
280 $isDisabled = in_array($func, $disabled);
281 if (!$isDisabled && function_exists($func)) return true;
282 else return false;
283
284 }
285
286 /**
287 * hotFixPatches - Function which fixes things for "old" users
288 *
289 * @return void
290 */
291 public function hotfix_patches() {
292
293 if (!is_admin()) return;
294
295 $current_patch = get_option('bmi_hotfixes', array());
296
297 if (!in_array('BMI_D1_M6_26', $current_patch)) {
298 require_once BMI_INCLUDES . '/external/backupbliss.php';
299 $backupbliss = new BackupBliss();
300 $connectionStatus = $backupbliss->verifyConnection();
301 if ($connectionStatus['result'] == 'connected') {
302 Dashboard\bmi_set_config('STORAGE::EXTERNAL::BACKUPBLISS', true);
303 }
304 }
305 if (!in_array('BMI_D17_M1_26', $current_patch)) {
306 $baseurl = home_url();
307 if (substr($baseurl, 0, 4) != 'http') {
308 if (is_ssl()) $baseurl = 'https://' . home_url();
309 else $baseurl = 'http://' . home_url();
310 }
311
312 $sk = get_option('bmi_sk_keepalive');
313 // Generate if missing (First time patch is applied)
314 if (empty($sk)) {
315 $sk = wp_generate_password(32, false);
316 update_option('bmi_sk_keepalive', $sk);
317 }
318
319 $url = 'https://authentication.backupbliss.com/v2/crons/connect';
320 $response = wp_remote_post($url, array(
321 'method' => 'POST',
322 'timeout' => 15,
323 'redirection' => 2,
324 'httpversion' => '1.0',
325 'blocking' => true,
326 'body' => array('site' => $baseurl, 'sk' => $sk)
327 ));
328
329 $current_patch[] = 'BMI_D17_M1_26';
330 }
331
332 if (!in_array('BMI_D13_M12_01', $current_patch)) {
333 Dashboard\bmi_set_config('OTHER::NEW_SEARCH_REPLACE_ENGINE', true);
334 Dashboard\bmi_set_config('OTHER:DB:SEARCHREPLACE:MAX', 5000);
335 $current_patch[] = 'BMI_D13_M12_01';
336 }
337
338 if (!in_array('BMI_D20_M07_01', $current_patch)) {
339
340 $current_directory = Dashboard\bmi_get_config('STORAGE::LOCAL::PATH');
341 if (basename($current_directory) == 'backup-migration') {
342
343 require_once BMI_INCLUDES . '/ajax.php';
344 $fValue = isset($_POST['f']) ? $_POST['f'] : '';
345 unset($_POST['f']);
346 $handler_a = new BMI_Ajax(true);
347 $_POST['f'] = $fValue;
348
349 $handler_a->post['directory'] = dirname($current_directory) . DIRECTORY_SEPARATOR . 'backup-migration-' . $this->randomString(10);
350 $handler_a->post['access'] = Dashboard\bmi_get_config('STORAGE::DIRECT::URL');
351
352 $res_a = $handler_a->saveStorageConfig();
353 if (isset($res_a['status']) && $res_a['status'] == 'success') {
354
355 $current_patch[] = 'BMI_D20_M07_01';
356
357 }
358
359 } else {
360
361 $current_patch[] = 'BMI_D20_M07_01';
362
363 }
364
365 }
366
367 if (!in_array('BMI_D17_M12_Y21_02', $current_patch)) {
368
369 $current_splitting_value = Dashboard\bmi_get_config('OTHER:RESTORE:SPLITTING');
370 $current_query_size = Dashboard\bmi_get_config('OTHER:DB:QUERIES');
371
372 $current_query_size = intval($current_query_size);
373 if ($current_splitting_value == 'true' || $current_splitting_value === true) {
374 $current_splitting_value = true;
375 } else {
376 $current_splitting_value = false;
377 }
378
379 if ($current_splitting_value === false || $current_query_size != 300) {
380
381 $b_db_restore_splitting = true;
382 $b_db_query_size = '2000';
383
384 $error_b = 0;
385 if (!Dashboard\bmi_set_config('OTHER:RESTORE:SPLITTING', $b_db_restore_splitting)) {
386 $error_b++;
387 }
388 if (!Dashboard\bmi_set_config('OTHER:DB:QUERIES', $b_db_query_size)) {
389 $error_b++;
390 }
391
392 if ($error_b <= 0) {
393
394 $current_patch[] = 'BMI_D17_M12_Y21_02';
395
396 }
397
398 } else {
399
400 $current_patch[] = 'BMI_D17_M12_Y21_02';
401
402 }
403
404 }
405
406 if (!in_array('BMI_D13_M08_Y23_01', $current_patch)) {
407
408 $current_direct_download_value = Dashboard\bmi_get_config('OTHER:DOWNLOAD:DIRECT');
409
410 if ($current_direct_download_value === false || $current_direct_download_value == 'false') {
411
412 $error_b = 0;
413 if (!Dashboard\bmi_set_config('OTHER:DOWNLOAD:DIRECT', true)) $error_b++;
414 if ($error_b <= 0) $current_patch[] = 'BMI_D13_M08_Y23_01';
415
416 } else $current_patch[] = 'BMI_D13_M08_Y23_01';
417
418 }
419
420 update_option('bmi_hotfixes', $current_patch);
421
422 }
423
424 public function ajax($cli = false) {
425 if (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) === 'xmlhttprequest') {
426 if ((isset($_POST['token']) && $_POST['token'] == 'bmi' && isset($_POST['f']) && is_admin()) || $cli) {
427 try {
428
429 if (gettype($cli) != 'boolean') $cli = false;
430
431 // Extend execution time
432 if ($this->isFunctionEnabled('headers_sent') && $this->isFunctionEnabled('session_status')) {
433 if (!headers_sent() && session_status() === PHP_SESSION_DISABLED) {
434 if ($this->isFunctionEnabled('ignore_user_abort')) @ignore_user_abort(true);
435 if ($this->isFunctionEnabled('set_time_limit')) @set_time_limit(16000);
436 if ($this->isFunctionEnabled('ini_set')) {
437 @ini_set('max_execution_time', '259200');
438 @ini_set('max_input_time', '259200');
439 }
440 }
441 }
442
443 // May cause issues with auto login
444 // if (strlen(session_id()) > 0) session_write_close();
445
446 if ($this->isFunctionEnabled('register_shutdown_function')) {
447 register_shutdown_function([$this, 'execution_shutdown']);
448 }
449
450 // Require AJAX Handler
451 require_once BMI_INCLUDES . '/ajax.php';
452 $handler = new BMI_Ajax($cli);
453
454 } catch (\Exception $e) {
455
456 Logger::error('POST error:');
457 Logger::error($e);
458 if ($_POST['f'] == 'create-backup') {
459 $progress = &$GLOBALS['bmi_backup_progress'];
460 $this->handleErrorDuringBackup($e->getMessage(), $e->getFile(), $e->getLine(), $progress);
461 }
462 if ($_POST['f'] == 'restore-backup') {
463 $progress = &$GLOBALS['bmi_migration_progress'];
464 $this->handleErrorDuringRestore($e->getMessage(), $e->getFile(), $e->getLine(), $progress);
465 }
466
467 $this->res(['status' => 'error', 'error' => $e]);
468 exit;
469
470 } catch (\Throwable $e) {
471
472 Logger::error('POST error:');
473 Logger::error($e);
474 if ($_POST['f'] == 'create-backup') {
475 $progress = &$GLOBALS['bmi_backup_progress'];
476 $this->handleErrorDuringBackup($e->getMessage(), $e->getFile(), $e->getLine(), $progress);
477 }
478 if ($_POST['f'] == 'restore-backup') {
479 $progress = &$GLOBALS['bmi_migration_progress'];
480 $this->handleErrorDuringRestore($e->getMessage(), $e->getFile(), $e->getLine(), $progress);
481 }
482
483 $this->res(['status' => 'error', 'error' => $e]);
484 exit;
485
486 }
487 }
488 }
489 }
490
491 public function execution_shutdown() {
492 $err = error_get_last();
493
494 if (defined('BMI_USING_CLI_FUNCTIONALITY') && BMI_USING_CLI_FUNCTIONALITY === true) {
495 $lock_cli = BMI_BACKUPS . '/.migration_lock_cli';
496 $lock_cli_end = BMI_BACKUPS . '/.migration_lock_ended';
497 $cli_failed_lock = BMI_BACKUPS . '/.backup_lock_cli_failed';
498 $lock_cli_end_backup = BMI_BACKUPS . '/.backup_lock_cli_end';
499
500 if (file_exists($lock_cli)) @unlink($lock_cli);
501 if (file_exists($lock_cli_end)) @touch($lock_cli_end);
502 if (file_exists($cli_failed_lock)) @touch($cli_failed_lock);
503 if (file_exists($lock_cli_end_backup)) @touch($lock_cli_end_backup);
504 }
505
506 if ($err != null) {
507
508 $msg = $err['message'];
509 $file = $err['file'];
510 $line = $err['line'];
511 $type = $err['type'];
512
513 if ($type != '1' && ($type != E_ERROR && $type != E_CORE_ERROR && $type != E_COMPILE_ERROR && $type != E_USER_ERROR && $type != E_RECOVERABLE_ERROR)) {
514 Logger::error(__('There was an error before request shutdown (but it was not logged to backup/restore log)', 'backup-backup'));
515 Logger::error(__('Error message: ', 'backup-backup') . $msg);
516 Logger::error(__('Error file/line: ', 'backup-backup') . $file . '|' . $line);
517 Logger::error(__('Error handler: ', 'backup-backup') . 'init#01' . '|' . $type);
518 return;
519 }
520
521 if (isset($GLOBALS['bmi_error_handled']) && $GLOBALS['bmi_error_handled']) return;
522 if ($_POST['f'] == 'create-backup') {
523 Logger::error(__('There was an error during backup', 'backup-backup'));
524 Logger::error(__('Error message: ', 'backup-backup') . $msg);
525 Logger::error(__('Error file/line: ', 'backup-backup') . $file . '|' . $line);
526 Logger::error(__('Error handler: ', 'backup-backup') . 'init#02' . '|' . $type);
527 $progress = &$GLOBALS['bmi_backup_progress'];
528 if ($progress) {
529 $progress->log(__('Error message: ', 'backup-backup') . $msg, 'error');
530 $progress->log(__('You can get more pieces of information in troubleshooting log file.', 'backup-backup'), 'error');
531 }
532 $this->handleErrorDuringBackup($msg, $file, $line, $progress);
533
534 $fullPath = BMI_TMP . DIRECTORY_SEPARATOR;
535 array_map('unlink', glob($fullPath . '*.tmp'));
536 array_map('unlink', glob($fullPath . '*.gz'));
537 }
538
539 if ($_POST['f'] == 'restore-backup') {
540 Logger::error(__('There was an error during restore process', 'backup-backup'));
541 Logger::error(__('Error message: ', 'backup-backup') . $msg);
542 Logger::error(__('Error file/line: ', 'backup-backup') . $file . '|' . $line);
543 Logger::error(__('Error handler: ', 'backup-backup') . 'init#03' . '|' . $type);
544 $progress = &$GLOBALS['bmi_migration_progress'];
545 if ($progress) {
546 $progress->log(__('Error message: ', 'backup-backup') . $msg, 'error');
547 $progress->log(__('You can get more pieces of information in troubleshooting log file.', 'backup-backup'), 'error');
548 }
549 $this->handleErrorDuringRestore($msg, $file, $line, $progress);
550 }
551
552 $this->res(['status' => 'error', 'error' => $err]);
553 exit;
554 }
555 }
556
557 public function handleErrorDuringBackup($msg, $file, $line, &$progress) {
558 $backup = $GLOBALS['bmi_current_backup_name'];
559
560 Logger::log('Due to fatal error backup handled correctly (closed and removed).');
561 if ($progress) {
562 $progress->log(__('Something bad happened on PHP side.', 'backup-backup'), 'error');
563 $progress->log(__('Unfortunately we had to remove the backup (if partly created).', 'backup-backup'), 'error');
564 $progress->log(__('Error message: ', 'backup-backup') . $msg, 'error');
565 $progress->log(__('Error file/line: ', 'backup-backup') . $file . '|' . $line, 'error');
566 if (strpos($msg, 'execution time') !== false) {
567 $progress->log(__('Probably we could not increase the execution time, please edit your php.ini manually', 'backup-backup'), 'error');
568 }
569 }
570
571 $backup_path = BMI_BACKUPS . DIRECTORY_SEPARATOR . $backup;
572 $partial_backup_path = glob( BMI_BACKUPS . DIRECTORY_SEPARATOR . $backup . '.??????');
573 if (is_array($partial_backup_path) && count($partial_backup_path) > 0) {
574 foreach ($partial_backup_path as $key => $value) {
575 @unlink($value);
576 }
577 }
578 if (file_exists($backup_path)) @unlink($backup_path);
579 if (file_exists(BMI_BACKUPS . DIRECTORY_SEPARATOR . '.running')) @unlink(BMI_BACKUPS . DIRECTORY_SEPARATOR . '.running');
580 if (file_exists(BMI_BACKUPS . DIRECTORY_SEPARATOR . '.abort')) @unlink(BMI_BACKUPS . DIRECTORY_SEPARATOR . '.abort');
581
582 if ($progress) {
583 $progress->log(__("Aborting backup...", 'backup-backup'), 'step');
584 $progress->log('#002', 'END-CODE');
585 $progress->end();
586 }
587 }
588
589 public function handleErrorDuringRestore($msg, $file, $line, &$progress) {
590 Logger::log('There was fatal error during restore.');
591 if ($progress) {
592 $progress->log(__('Something bad happened on PHP side.', 'backup-backup'), 'error');
593 $progress->log(__('Error message: ', 'backup-backup') . $msg, 'error');
594 $progress->log(__('Error file/line: ', 'backup-backup') . $file . '|' . $line, 'error');
595 }
596 if (file_exists(BMI_BACKUPS . DIRECTORY_SEPARATOR . '.migration_lock')) @unlink(BMI_BACKUPS . DIRECTORY_SEPARATOR . '.migration_lock');
597 if ($progress) {
598 $progress->log(__("Aborting & unlocking restore process...", 'backup-backup'), 'step');
599 $progress->end();
600 }
601
602 $lock = BMI_BACKUPS . '/.migration_lock';
603 if (file_exists($lock)) @unlink($lock);
604 }
605
606 public function submenu() {
607
608 // Menu icon
609 $icon_url = $this->get_asset('images', 'logo-min.png');
610
611 // Main menu slug
612 $parentSlug = 'backup-migration';
613
614 // Content
615 $content = [$this, 'settings_page'];
616
617 // Main menu hook
618 add_menu_page('Backup Migration', '<span id="bmi-menu">Backup Migration</span>', 'read', $parentSlug, $content, $icon_url, 98);
619
620 // Remove default submenu by menu
621 remove_submenu_page($parentSlug, $parentSlug);
622
623 }
624
625 public function settings_action($links) {
626 $text = __('Manage', 'backup-backup');
627 $links['bmi-settings-link'] = '<a href="' . admin_url('/admin.php?page=backup-migration') . '">' . $text . '</a>';
628
629 return $links;
630 }
631
632 /**
633 * Whitelist configuration files for Security Ninja plugin.
634 *
635 * This filter callback prevents Security Ninja from deleting or flagging
636 * Backup Migration configuration files as suspicious. These files are
637 * essential for the plugin's operation and should be preserved.
638 *
639 * @param array $whitelist Existing whitelist array from Security Ninja
640 * @return array Modified whitelist array with our configuration files added
641 */
642 public function securityninja_whitelist_config_files($whitelist = array()) {
643 if (!is_array($whitelist)) {
644 $whitelist = array();
645 }
646
647 // Add all configuration files to the whitelist
648 $config_files = array(
649 BMI_CONFIG_DEFAULT,
650 BMI_STATIC_PHP_CONFIG,
651 BMI_INCLUDES . DIRECTORY_SEPARATOR . 'htaccess' . DIRECTORY_SEPARATOR . '*'
652 );
653
654 // Add config directory if defined
655 if (defined('BMI_CONFIG_DIR') && BMI_CONFIG_DIR) {
656 $config_files[] = BMI_CONFIG_DIR . DIRECTORY_SEPARATOR . '*';
657 }
658
659 // Add config path if defined
660 if (defined('BMI_CONFIG_PATH') && BMI_CONFIG_PATH) {
661 $config_files[] = BMI_CONFIG_PATH;
662 }
663
664 // Merge with existing whitelist and remove duplicates
665 $whitelist = array_unique(array_merge($whitelist, $config_files));
666
667 return $whitelist;
668 }
669
670 public function include_offline() {
671
672
673 // Handle offline tasks
674 if (!class_exists('BMI_Offline')) {
675
676 if (file_exists(BMI_INCLUDES . '/offline.php')) {
677 require_once BMI_INCLUDES . '/offline.php';
678 new BMI_Offline();
679 }
680 }
681
682 }
683
684 public function settings_page() {
685
686 // Set email if does not exist
687 if (!Dashboard\bmi_get_config('OTHER:EMAIL')) {
688 Dashboard\bmi_set_config('OTHER:EMAIL', get_bloginfo('admin_email'));
689 }
690
691 // Require The HTML
692 require_once BMI_INCLUDES . '/dashboard/settings.php';
693 }
694
695 public function backupbliss_space_issues() {
696 require_once BMI_INCLUDES . '/external/backupbliss.php';
697 $backupbliss = new BackupBliss();
698 $upload_issue_notice = false;
699 if ($backupbliss->canShowFailureWarnNotice()) {
700 $upload_issue_notice = $backupbliss->getNotice("upload_issue_space");
701 }
702 return $upload_issue_notice;
703 }
704
705 public function admin_init_hook() {
706 $this->hotfix_patches();
707 if (get_option('_bmi_redirect', false)) {
708 $this->fixLitespeed();
709 delete_option('_bmi_redirect');
710 wp_safe_redirect(admin_url('admin.php?page=backup-migration'));
711 }
712 }
713
714 public function admin_notices() {
715 if (get_current_screen()->id != 'toplevel_page_backup-migration' && get_option('bmi_display_email_issues', false)) {
716 ?>
717 <div class="notice notice-warning">
718 <p>
719 <?php esc_html_e('There was an error during automated backup, please', 'backup-backup'); ?>
720 <?php echo '<a href="' . esc_url(admin_url('/admin.php?page=backup-migration')) . '">' . esc_html(__('check that.', 'backup-backup')) . '</a>'; ?>
721 </p>
722 </div>
723 <?php
724 }
725 }
726
727 public function handle_crons() {
728 if (Dashboard\bmi_get_config('CRON:ENABLED') !== true) return;
729
730 $time = get_option('bmi_backup_check', 0);
731 if ((time() - $time) > 60) {
732 update_option('bmi_backup_check', time());
733
734 do_action('bmi_handle_cron_check');
735 }
736 }
737
738 /**
739 * Retrieves a list of active security plugins detected on the site.
740 *
741 * This function checks the list of currently active plugins and identifies
742 * common security plugins by their slugs. It returns a key-value array
743 * where keys are plugin slugs and values are their human-readable names.
744 *
745 * Supported plugins:
746 * - Wordfence
747 * - Sucuri Security
748 *
749 * @return array<string, string> Associative array of detected security plugins.
750 * Format: [ 'plugin_slug' => 'Plugin Name' ]
751 */
752 public static function get_active_security_plugins() {
753 $active_plugins = [];
754 $plugins = get_option('active_plugins', []);
755 if (is_array($plugins) && count($plugins) > 0) {
756 foreach ($plugins as $plugin) {
757 if (strpos($plugin, 'wordfence') !== false) {
758 $active_plugins['wordfence'] = 'Wordfence';
759 } elseif (strpos($plugin, 'security-ninja') !== false) {
760 $active_plugins['security-ninja'] = 'Security Ninja';
761 }
762 }
763 }
764 return $active_plugins;
765 }
766
767 public static function email_error($msg) {
768 Logger::log('Displaying some issues about email sending...');
769 update_option('bmi_display_email_issues', $msg);
770 }
771
772 public function backup_inproper_time($should_time) {
773 $plan_file = BMI_TMP . DIRECTORY_SEPARATOR . '.plan';
774 if (!file_exists($plan_file) || intval($should_time) < 1234567890) return;
775
776 $currentDate = date('Y-m-d');
777 if (get_option('bmi_last_email_notification', false) == $currentDate) {
778 return;
779 }
780
781 Logger::log('Sending notification about backup being late');
782 $email = Dashboard\bmi_get_config('OTHER:EMAIL') != false ? Dashboard\bmi_get_config('OTHER:EMAIL') : get_bloginfo('admin_email');
783 $subject = Dashboard\bmi_get_config('OTHER:EMAIL:TITLE');
784 $message = __("Automatic backup was not on time because the connection to the ping server was interrupted, and WP Cron was late, as there was no traffic on the site.", 'backup-backup') . "\n";
785 $message .= __("Backup was made on: ", 'backup-backup') . date('Y-m-d H:i:s') . __(', but should be on: ', 'backup-backup') . date('Y-m-d H:i:s', $should_time);
786 $message .= ' ' . __("(server time)", 'backup-backup');
787
788 Logger::debug($message);
789 if (!self::send_notification_mail($email, $subject, $message)) {
790 $issue = __("Couldn't send mail to you, please check server configuration.", 'backup-backup') . '<br>';
791 $issue .= '<b>' . __("Message you missed because of this: ", 'backup-backup') . '</b>' . $message;
792 self::email_error($issue);
793 }
794 }
795
796 public function handle_cron_check() {
797
798 if (Dashboard\bmi_get_config('CRON:ENABLED') !== true) return;
799
800 $now = time();
801 if (file_exists(BMI_TMP . DIRECTORY_SEPARATOR . '.last')) {
802 $last = @file_get_contents(BMI_TMP . DIRECTORY_SEPARATOR . '.last');
803 $last_status = explode('.', $last)[0];
804 $last_time = intval(explode('.', $last)[1]);
805 } else {
806 $last_time = 0;
807 $last_status = 0;
808 }
809
810 if (file_exists(BMI_TMP . DIRECTORY_SEPARATOR . '.plan')) {
811 $plan = intval(@file_get_contents(BMI_TMP . DIRECTORY_SEPARATOR . '.plan'));
812 if ($last_time < $plan && ((time() - $plan) > 7200)) {
813 if ($last_status !== '0') {
814 if (!wp_next_scheduled('bmi_do_backup_right_now')) {
815 wp_schedule_single_event(time(), 'bmi_do_backup_right_now');
816 }
817 }
818 }
819 }
820
821 }
822
823 public function get_next_cron($curr = false) {
824 if ($curr === false) {
825 $curr = time();
826 }
827
828 $time = Crons::calculate_date([
829 'type' => Dashboard\bmi_get_config('CRON:TYPE'),
830 'week' => Dashboard\bmi_get_config('CRON:WEEK'),
831 'day' => Dashboard\bmi_get_config('CRON:DAY'),
832 'hour' => Dashboard\bmi_get_config('CRON:HOUR'),
833 'minute' => Dashboard\bmi_get_config('CRON:MINUTE')
834 ], $curr);
835
836 return $time;
837 }
838
839 public function handle_cron_error($e) {
840 Logger::error(__("Automatic backup failed at time: ", 'backup-backup') . date('Y-m-d, H:i:s'));
841 if (is_object($e) || is_array($e)) {
842 Logger::error('Error: ' . $e->getMessage());
843 } else {
844 Logger::error('Error: ' . $e);
845 }
846
847 $notis = Dashboard\bmi_get_config('OTHER:EMAIL:NOTIS');
848 if (in_array($notis, [true, 'true'])) {
849 $email = Dashboard\bmi_get_config('OTHER:EMAIL') != false ? Dashboard\bmi_get_config('OTHER:EMAIL') : get_bloginfo('admin_email');
850 $subject = Dashboard\bmi_get_config('OTHER:EMAIL:TITLE');
851 $message = __("There was an error during automatic backup, please check the logs.", 'backup-backup');
852 if (is_string($e)) {
853 $message .= "\nError: " . $e;
854 }
855
856 if (!self::send_notification_mail($email, $subject, $message, true)) {
857 $issue = __("Couldn't send mail to you, please check server configuration.", 'backup-backup') . '<br>';
858 $issue .= '<b>' . __("Message you missed because of this: ", 'backup-backup') . '</b>' . $message;
859 self::email_error($issue);
860 }
861 }
862
863 if (file_exists(BMI_BACKUPS . '/.cron')) {
864 @unlink(BMI_BACKUPS . '/.cron');
865 }
866 }
867
868 public static function send_notification_mail($email, $subject, $message, $force = false) {
869
870 $currentDate = date('Y-m-d');
871 if (get_option('bmi_last_email_notification', false) == $currentDate && $force === false) {
872 Logger::log(__("Disallowing to send mail as today we already sent one.", 'backup-backup'));
873 return;
874 }
875
876 update_option('bmi_last_email_notification', $currentDate);
877
878 $email_fail = __("Could not send the email notification about that fail", 'backup-backup');
879
880 try {
881
882 if (wp_mail($email, $subject, $message)) {
883 Logger::log(__("Sent email notification to: ", 'backup-backup') . $email);
884
885 return true;
886 } else {
887 Logger::error($email_fail);
888 self::email_error(__("Couldn't send notification via email, please check the email and your server settings.", 'backup-backup'));
889
890 return false;
891 }
892
893 } catch (\Exception $e) {
894 Logger::error($email_fail);
895 self::email_error(__("Couldn't send notification via email due to error, please check plugin logs for more details.", 'backup-backup'));
896
897 return false;
898 } catch (\Throwable $e) {
899 Logger::error($email_fail);
900 self::email_error(__("Couldn't send notification via email due to error, please check plugin logs for more details.", 'backup-backup'));
901
902 return false;
903 }
904 }
905
906 public static function handle_after_cron() {
907 require_once BMI_INCLUDES . DIRECTORY_SEPARATOR . 'scanner' . DIRECTORY_SEPARATOR . 'backups.php';
908 $backups = new Backups();
909 $availableBackups = $backups->getAvailableBackups();
910 $list = $availableBackups['local'];
911
912 $cron_list = [];
913 $cron_dates = [];
914 $sortedMD5s = [];
915 foreach ($list as $key => $value) {
916 if ($list[$key][6] == true) {
917 if ($list[$key][5] == 'unlocked') {
918 $cron_list[$list[$key][1]] = $list[$key][0];
919 $cron_dates[] = $list[$key][1];
920 $sortedMD5s[] = [$list[$key][1], $list[$key][7]];
921 }
922 }
923 }
924
925 usort($cron_dates, function ($a, $b) {
926 return (strtotime($a) < strtotime($b)) ? -1 : 1;
927 });
928
929 $cron_dates = array_slice($cron_dates, 0, -(intval(Dashboard\bmi_get_config('CRON:KEEP'))));
930 foreach ($cron_dates as $key => $value) {
931 $name = $cron_list[$cron_dates[$key]];
932 $name = explode('#%&', $name)[1];
933 Logger::log(__("Removing backup due to keep rules: ", 'backup-backup') . $name);
934 @unlink(BMI_BACKUPS . DIRECTORY_SEPARATOR . $name);
935 }
936
937 // Auto External Removal
938 $sortedMD5s = [];
939 $externalStorages = ['gdrive', 'dropbox', 'onedrive', 'FTP', 'sftp', 'aws', 'wasabi', 'backupbliss'];
940 $currentDomain = sanitize_text_field(parse_url(home_url(), PHP_URL_HOST));
941 foreach ($externalStorages as $storage) {
942 if (isset($availableBackups['external'][$storage])) {
943 $storageList = $availableBackups['external'][$storage];
944 foreach ($storageList as $md5 => $data) {
945 if ($storageList[$md5][6] == true && $storageList[$md5][5] == 'unlocked' && $storageList[$md5][9] === $currentDomain) {
946 $sortedMD5s[] = [$storageList[$md5][1], $md5, $storageList[$md5][0]];
947 }
948 }
949 }
950 }
951 $sortedMD5s = array_intersect_key($sortedMD5s, array_unique(array_map('serialize', $sortedMD5s)));
952
953 usort($sortedMD5s, function ($a, $b) {
954 return (strtotime($a[0]) < strtotime($b[0])) ? -1 : 1;
955 });
956
957 $sortedMD5s = array_slice($sortedMD5s, 0, -(intval(Dashboard\bmi_get_config('CRON:KEEP'))));
958 foreach ($sortedMD5s as $index => $data) {
959 $md5 = $data[1];
960 $name = $data[2];
961 Logger::log(__("Removing external backup due to keep rules: ", 'backup-backup') . $name);
962 do_action('bmi_premium_remove_backup_file', $md5);
963 do_action('bmi_premium_remove_backup_json_file', $md5 . '.json');
964 }
965 }
966
967 public function set_last_cron($status, $time) {
968 $file = BMI_TMP . DIRECTORY_SEPARATOR . '.last';
969 file_put_contents($file, $status . '.' . $time);
970 }
971
972 public function readFileSensitive($file) {
973 if (!file_exists($file)) {
974 echo '';
975 return;
976 }
977
978 $file = new \SplFileObject($file);
979 $file->seek($file->getSize());
980 $total_lines = $file->key() + 1;
981
982 $current_directory = Dashboard\bmi_get_config('STORAGE::LOCAL::PATH');
983 $backups_path = $this->fixSlashes($current_directory . DIRECTORY_SEPARATOR . 'backups');
984 $scanned_directory_all = array_diff(scandir($backups_path), ['..', '.']);
985 $scanned_directory = array_values(preg_grep('/((.*).zip)/i', $scanned_directory_all));
986
987 for ($i = 0; $i < $total_lines; ++$i) {
988
989 $file->seek($i);
990 $line = $this->escapeSensitive($file->current(), $current_directory, $scanned_directory);
991
992 echo esc_html($line);
993 unset($line);
994
995 }
996
997 }
998
999 public function escapeSensitive($line, $current_directory, $scanned_directory) {
1000
1001 global $table_prefix;
1002
1003 $dir_name = basename($current_directory);
1004
1005 $line = preg_replace('/\:\ ((.*)\.zip)/', ': *****.zip', $line);
1006 $line = preg_replace('/(\"filename\":(.*)\.zip)\"/', '"filename": "*****.zip"', $line);
1007 $line = preg_replace('/\"http(.*)\"/', '"***site_url***"', $line);
1008 $line = preg_replace('/\:\ http(.*)\n/', ": ***site_url***\n", $line);
1009 $line = preg_replace('/\"\d{10}\"/', '"***secret_login***"', $line);
1010 $line = str_replace(ABSPATH, '***ABSPATH***/', $line);
1011 $line = str_replace($dir_name, '***backup_path***', $line);
1012 $line = str_replace($table_prefix, '***_', $line);
1013 $line = preg_replace('/^(.*?&sk=).*$/', '$1***', $line);
1014
1015 for ($i = 0; $i < sizeof($scanned_directory); ++$i) {
1016
1017 $backup_name = $scanned_directory[$i];
1018 $line = str_replace($backup_name, '***some_backup***', $line);
1019
1020 }
1021
1022 return $line;
1023
1024 }
1025
1026 public function handle_cron_backup() {
1027
1028 $plan_file = BMI_TMP . DIRECTORY_SEPARATOR . '.plan';
1029 $last_file = BMI_TMP . DIRECTORY_SEPARATOR . '.last';
1030
1031 // Abort if disabled
1032 if (Dashboard\bmi_get_config('CRON:ENABLED') !== true) {
1033
1034
1035 if (file_exists($plan_file)) @unlink($plan_file);
1036 if (file_exists($last_file)) @unlink($last_file);
1037
1038 return;
1039
1040 }
1041
1042 if (!file_exists($plan_file)) return;
1043
1044 // Planned time
1045 $plan = intval(@file_get_contents(BMI_TMP . DIRECTORY_SEPARATOR . '.plan'));
1046
1047 // Check difference
1048 if ((time() - $plan) > 3600) {
1049 Logger::log('Backup failed to run on proper time, but running now.');
1050 Logger::log('Planned time: ' . date('Y-m-d H:i:s', $plan));
1051 $this->backup_inproper_time($plan);
1052 }
1053
1054 // Now
1055 $now = time();
1056 $this->set_last_cron('0', $now);
1057
1058 // Extend execution time
1059 if ($this->isFunctionEnabled('headers_sent') && $this->isFunctionEnabled('session_status')) {
1060 if (!headers_sent() && session_status() === PHP_SESSION_DISABLED) {
1061 if ($this->isFunctionEnabled('ignore_user_abort')) @ignore_user_abort(true);
1062 if ($this->isFunctionEnabled('set_time_limit')) @set_time_limit(16000);
1063 if ($this->isFunctionEnabled('ini_set')) {
1064 @ini_set('max_execution_time', '259200');
1065 @ini_set('max_input_time', '259200');
1066 }
1067 }
1068 }
1069
1070 if (strlen(session_id()) > 0) session_write_close();
1071
1072 Logger::log(__("Automatic backup called at time: ", 'backup-backup') . date('Y-m-d, H:i:s'));
1073
1074 try {
1075 require_once BMI_INCLUDES . '/ajax.php';
1076 $isBackup = (file_exists(BMI_BACKUPS . '/.running') && (time() - filemtime(BMI_BACKUPS . '/.running')) <= 65) ? true : false;
1077 $isCron = (file_exists(BMI_BACKUPS . '/.cron') && (time() - filemtime(BMI_BACKUPS . '/.cron')) <= 65) ? true : false;
1078 if ($isCron) {
1079 return;
1080 }
1081
1082 if ($isBackup) {
1083 $this->handle_cron_error(__("Could not make the backup: Backup already running, please wait till it complete.", 'backup-backup'));
1084 $this->set_last_cron('2', $now);
1085 } else {
1086 touch(BMI_BACKUPS . '/.cron');
1087
1088 if (!defined('BMI_DOING_SCHEDULED_BACKUP')) {
1089 define('BMI_DOING_SCHEDULED_BACKUP', true);
1090 }
1091
1092 $handler = new BMI_Ajax();
1093 $handler->resetLatestLogs();
1094 $backup = $handler->prepareAndMakeBackup(true);
1095
1096 if ($backup['status'] == 'success') {
1097 if (isset($backup['filename'])) {
1098 Logger::log(__("Automatic backup successed: ", 'backup-backup') . $backup['filename']);
1099 } else {
1100 Logger::log(__("Automatic backup successed", 'backup-backup'));
1101 }
1102 $this->set_last_cron('1', $now);
1103 } elseif ($backup['status'] == 'background') {
1104 Logger::log(__('Scheduled backup is running in background: ', 'backup-backup') . $backup['filename']);
1105 $this->set_last_cron('1', $now);
1106 } elseif ($backup['status'] == 'msg') {
1107 $this->handle_cron_error($backup['why']);
1108 $this->set_last_cron('3', $now);
1109 } else {
1110 $this->handle_cron_error(__("Could not make the backup due to internal server error.", 'backup-backup'));
1111 $this->set_last_cron('4', $now);
1112 }
1113 }
1114 } catch (\Exception $e) {
1115 $this->handle_cron_error($e);
1116 $this->set_last_cron('5', $now);
1117 } catch (\Throwable $e) {
1118 $this->handle_cron_error($e);
1119 $this->set_last_cron('5', $now);
1120 }
1121
1122 $this->handle_after_cron();
1123
1124 if (file_exists(BMI_BACKUPS . '/.cron')) {
1125 @unlink(BMI_BACKUPS . '/.cron');
1126 }
1127 require_once BMI_INCLUDES . '/cron/handler.php';
1128 $time = $this->get_next_cron();
1129
1130 wp_clear_scheduled_hook('bmi_do_backup_right_now');
1131 wp_schedule_single_event($time, 'bmi_do_backup_right_now');
1132
1133 $file = BMI_TMP . DIRECTORY_SEPARATOR . '.plan';
1134 file_put_contents($file, $time);
1135 }
1136
1137 public function enqueue_scripts() {
1138
1139 // Global
1140 if (in_array(get_current_screen()->id, ['toplevel_page_backup-migration', 'plugins'])) { ?>
1141 <script type="text/javascript">
1142 let stars = <?php echo json_encode(plugin_dir_url(BMI_ROOT_FILE)); ?> + 'admin/images/stars.gif';
1143 let css_star = "background:url('" + stars + "')";
1144 document.addEventListener("DOMContentLoaded", function(event) {
1145 jQuery('[data-slug="backup-migration-pro"]').find('strong').html('<span>Backup Migration <b style="color: orange; ' + css_star + '">Pro</b></span>');
1146 jQuery('[data-slug="backup-backup-pro"]').find('strong').html('<span>Backup Migration <b style="color: orange; ' + css_star + '">Pro</b></span>');
1147 });
1148 </script>
1149 <?php }
1150
1151 // Only for BM Settings
1152 if (!in_array(get_current_screen()->id, ['toplevel_page_backup-migration', 'update-core', 'plugins', 'plugin-install', 'themes','customize', 'plugins-network', 'plugin-install-network', 'themes-network']) && $this->backupbliss_space_issues() === false) return;
1153 wp_enqueue_script('backup-migration-script', $this->get_asset('js', 'backup-migration.min.js'), ['jquery'], BMI_VERSION, true);
1154 wp_localize_script('backup-migration-script', 'bmiVariables', [
1155 'nonce' => wp_create_nonce('backup-migration-ajax'),
1156 'stgLoading' => __('Loading, please wait...', 'backup-backup'),
1157 'stgStagingDefaultName' => __('staging', 'backup-backup'),
1158 'urlCopies' => __('URL copied successfully', 'backup-backup'),
1159 'isBeforeUpdateEnabled' => dashboard\bmi_get_config('OTHER:TRIGGER:BEFORE:UPDATES') ? 'true' : 'false',
1160 'maxUploadSize' => $this->getMaxUploadSize()
1161 ]);
1162
1163 }
1164
1165 public function phpSizeToB($phpSize) {
1166
1167 $sSuffix = strtoupper(substr($phpSize, -1));
1168
1169 if (!in_array($sSuffix, array('P','T','G','M','K'))) {
1170 return (int) $phpSize;
1171 }
1172
1173 $iValue = substr($phpSize, 0, -1);
1174 switch ($sSuffix) {
1175 case 'P': $iValue *= 1024;
1176 case 'T': $iValue *= 1024;
1177 case 'G': $iValue *= 1024;
1178 case 'M': $iValue *= 1024;
1179 case 'K': $iValue *= 1024;
1180 break;
1181 }
1182
1183 return (int) $iValue;
1184
1185 }
1186
1187 public function getMaxUploadSize() {
1188 $ten = (10 * 1024 * 1024);
1189 $max = min($this->phpSizeToB(ini_get('post_max_size')), $this->phpSizeToB(ini_get('upload_max_filesize')), $ten);
1190 return intval($max / 1024 / 1024);
1191 }
1192
1193 public function enqueue_styles() {
1194
1195 // Global styles
1196 wp_enqueue_style('backup-migration-style-icon', $this->get_asset('css', 'bmi-plugin-icon.min.css'), [], BMI_VERSION);
1197
1198 // Only for BM Settings and Update Core page and if there's no backupbliss space issues do not include the stylesheet.
1199 if (!in_array(get_current_screen()->id, ['toplevel_page_backup-migration', 'update-core', 'plugins', 'plugin-install', 'themes','customize', 'plugins-network', 'plugin-install-network', 'themes-network']) && $this->backupbliss_space_issues() === false) return;
1200
1201 // Enqueue the style
1202 wp_enqueue_style('backup-migration-style', $this->get_asset('css', 'bmi-plugin.min.css'), [], BMI_VERSION);
1203
1204
1205 // Solve conflict with wp-svg-icons thats breaks the dashboard sections.
1206 wp_dequeue_style('wp-svg-icons');
1207 }
1208
1209 public function handle_after_actions() {
1210
1211 // Handle After Migration actions
1212 $afterMigrationLock = BMI_TMP . DIRECTORY_SEPARATOR . '.migrationFinished';
1213 if (file_exists($afterMigrationLock)) {
1214 if (strpos(site_url(), 'tastewp') !== false) {
1215
1216 if (function_exists('wp_load_alloptions')) {
1217 wp_load_alloptions(true);
1218 }
1219
1220 update_option('__tastewp_redirection_performed', true);
1221 update_option('auto_smart_tastewp_redirect_performed', 1);
1222 update_option('tastewp_auto_activated', true);
1223 update_option('__tastewp_sub_requested', true);
1224
1225 }
1226
1227 unlink($afterMigrationLock);
1228 }
1229
1230 }
1231
1232 public function handle_downloading() {
1233 global $wpdb;
1234 @error_reporting(0);
1235 $autologin_file = BMI_BACKUPS . '/.autologin';
1236 $ip = '127.0.0.1';
1237 if (isset($_SERVER['HTTP_CLIENT_IP'])) {
1238 $ip = $_SERVER['HTTP_CLIENT_IP'];
1239 } else {
1240 if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
1241 $ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
1242 }
1243 if ($ip === false) {
1244 if (isset($_SERVER['REMOTE_ADDR'])) $ip = $_SERVER['REMOTE_ADDR'];
1245 }
1246 }
1247 $allowed = ['BMI_BACKUP', 'BMI_BACKUP_LOGS', 'PROGRESS_LOGS', 'AFTER_RESTORE', 'CURL_BACKUP'];
1248 $get_bmi = !empty($_GET['backup-migration']) ? sanitize_text_field($_GET['backup-migration']) : false;
1249 $get_bid = !empty($_GET['bmi-id']) ? sanitize_text_field($_GET['bmi-id']) : false;
1250 $get_pid = !empty($_GET['progress-id']) ? sanitize_text_field($_GET['progress-id']) : false;
1251 $get_is_uncensored = !empty($_GET['uncensored']) ? sanitize_text_field($_GET['uncensored']) : false;
1252 $crons_enabled = !empty($_GET['crons']) ? sanitize_text_field($_GET['crons']) : false;
1253 $secret_key = !empty($_GET['sk']) ? sanitize_text_field($_GET['sk']) : false;
1254
1255 if (isset($get_bmi) && in_array($get_bmi, $allowed)) {
1256 if (isset($get_bid) && strlen($get_bid) > 0 && isset($secret_key) && $secret_key === Dashboard\bmi_get_config('REQUEST:SECRET')) {
1257 $type = $get_bmi;
1258
1259 if ($type == 'AFTER_RESTORE' && isset($get_pid)) {
1260 if (file_exists($autologin_file)) {
1261 $autoLoginMD = file_get_contents($autologin_file);
1262 $autoLoginMD = explode('_', $autoLoginMD);
1263 $aID = intval($autoLoginMD[0]);
1264 $aID2 = intval($autoLoginMD[0]) - 1;
1265 $aID3 = intval($autoLoginMD[0]) + 1;
1266 $aID4 = intval($autoLoginMD[0]) + 2;
1267 $aID5 = intval($autoLoginMD[0]) + 3;
1268 $aID6 = intval($autoLoginMD[0]) + 4;
1269 $aIP = $autoLoginMD[1];
1270 $aIZ = $autoLoginMD[2];
1271
1272 // Allow 1 second delay
1273 $timeIsProper = false;
1274 if ($aID === intval($get_bid)) $timeIsProper = true;
1275 if ($aID2 === intval($get_bid)) $timeIsProper = true;
1276 if ($aID3 === intval($get_bid)) $timeIsProper = true;
1277 if ($aID4 === intval($get_bid)) $timeIsProper = true;
1278 if ($aID5 === intval($get_bid)) $timeIsProper = true;
1279 if ($aID6 === intval($get_bid)) $timeIsProper = true;
1280
1281 if ($timeIsProper && $aIP === $ip && trim($aIZ) === $get_pid) {
1282 $query = new \WP_User_Query(['role' => 'Administrator', 'count_total' => false, 'fields' => 'all']);
1283 $sqlres = $query->get_results();
1284
1285 if (sizeof($sqlres) > 0 && isset($sqlres[0]->ID) && isset($sqlres[0]->user_login)) {
1286
1287 $user = $sqlres[0];
1288 $adminID = $sqlres[0]->ID;
1289 $adminLogin = $sqlres[0]->user_login;
1290
1291 remove_all_actions('wp_login', -1000);
1292 wp_load_alloptions(true);
1293 clean_user_cache(get_current_user_id());
1294 clean_user_cache($adminID);
1295 wp_clear_auth_cookie();
1296 wp_set_current_user($adminID, $adminLogin);
1297 wp_set_auth_cookie($adminID, 1, is_ssl());
1298 do_action('wp_login', $adminLogin, $user);
1299 update_user_caches($user);
1300
1301 }
1302 $cronsEnabledParam = $crons_enabled ? "&crons=true" : "";
1303
1304 $url = admin_url('admin.php?page=backup-migration' . $cronsEnabledParam);
1305 header('Location: ' . $url);
1306
1307 @unlink($autologin_file);
1308 exit;
1309 }
1310 }
1311
1312 } else if ($type == 'BMI_BACKUP') {
1313 if (Dashboard\bmi_get_config('STORAGE::DIRECT::URL') === 'true' || current_user_can('administrator')) {
1314
1315 $backupname = $get_bid;
1316 $file = $this->fixSlashes(BMI_BACKUPS . DIRECTORY_SEPARATOR . $backupname);
1317
1318 $outsideDir = false;
1319 if (!(file_exists($file) && $this->fixSlashes(dirname($file)) == $this->fixSlashes(BMI_BACKUPS))) {
1320 $outsideDir = true;
1321 }
1322
1323 $isZip = false;
1324 if (function_exists('mime_content_type')) {
1325 $isZip = strpos(strtolower(mime_content_type($file)), 'zip') !== false;
1326 } elseif (function_exists('wp_check_filetype')) {
1327 $filetype = wp_check_filetype($file);
1328 if ($filetype && isset($filetype['ext']) && in_array(strtolower($filetype['ext']), ['zip', 'tar', 'gz', 'tar.gz'])) {
1329 $isZip = true;
1330 }
1331 } else {
1332 // fallback to extension check
1333 $lower = strtolower($file);
1334 if (
1335 substr($lower, -4) === '.zip' ||
1336 substr($lower, -4) === '.tar' ||
1337 substr($lower, -7) === '.tar.gz'
1338 ) {
1339 $isArchive = true;
1340 }
1341 }
1342
1343 if ($outsideDir || !$isZip) {
1344 header('HTTP/1.0 423 Locked');
1345 esc_html_e( "Incorrect usage of the query request.", 'backup-backup' );
1346 exit;
1347 }
1348
1349 if (Dashboard\bmi_get_config('OTHER:DOWNLOAD:DIRECT') == 'true') {
1350 if (file_exists(BMI_BACKUPS . DIRECTORY_SEPARATOR . '.htaccess')) @unlink(BMI_BACKUPS . DIRECTORY_SEPARATOR . '.htaccess');
1351 if (file_exists(dirname(BMI_BACKUPS) . DIRECTORY_SEPARATOR . '.htaccess')) @unlink(dirname(BMI_BACKUPS) . DIRECTORY_SEPARATOR . '.htaccess');
1352 $wpcontent = trailingslashit(WP_CONTENT_DIR);
1353 $wpcs = strlen($wpcontent);
1354 $url = $this->fixSlashes(content_url(substr($file, $wpcs)), '/');
1355 $path = wp_redirect($url);
1356 exit;
1357 }
1358
1359 // Prevent parent directory downloading
1360 if (ob_get_contents()) ob_end_clean();
1361
1362 if ($this->isFunctionEnabled('ignore_user_abort')) @ignore_user_abort(true);
1363 if ($this->isFunctionEnabled('set_time_limit')) @set_time_limit(16000);
1364 if ($this->isFunctionEnabled('headers_sent') && $this->isFunctionEnabled('session_status')) {
1365 if (!headers_sent() && session_status() === PHP_SESSION_DISABLED) {
1366 if ($this->isFunctionEnabled('ini_set')) {
1367 @ini_set('max_execution_time', '259200');
1368 @ini_set('max_input_time', '259200');
1369 @ini_set('memory_limit', '-1');
1370 if (@ini_get('zlib.output_compression')) {
1371 @ini_set('zlib.output_compression', 'Off');
1372 }
1373 }
1374 }
1375 }
1376
1377 if (strlen(session_id()) > 0) session_write_close();
1378
1379 $fp = @fopen($file, 'rb');
1380
1381 // header('X-Sendfile: ' . $file);
1382 // header('X-Sendfile-Type: X-Accel-Redirect');
1383 // header('X-Accel-Redirect: ' . $file);
1384 // header('X-Accel-Buffering: yes');
1385 header('Expires: 0');
1386 header('Pragma: public');
1387 header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
1388 header('Content-Disposition: attachment; filename="' . $backupname . '"');
1389 header('Content-Type: application/octet-stream');
1390 header('Content-Transfer-Encoding: binary');
1391 header('Content-Length: ' . filesize($file));
1392 header('Content-Description: File Transfer');
1393 http_response_code(200);
1394
1395 if (ob_get_level()) ob_end_clean();
1396
1397 fpassthru($fp);
1398 fclose($fp);
1399 exit;
1400
1401 } else {
1402 if (ob_get_contents()) ob_end_clean();
1403 header('HTTP/1.0 423 Locked');
1404 if (ob_get_level()) ob_end_clean();
1405 esc_html_e( "Backup download is restricted (allowed for admins only).", 'backup-backup' );
1406 exit;
1407 }
1408 } else if ($type == 'BMI_BACKUP_LOGS') {
1409
1410 // Only Admin can download backup logs
1411 if (!(current_user_can('administrator') || current_user_can('do_backups'))) return;
1412
1413 if (ob_get_contents()) ob_end_clean();
1414 $backupname = $get_bid;
1415 $file = $this->fixSlashes(BMI_BACKUPS . DIRECTORY_SEPARATOR . $backupname);
1416
1417 // Prevent parent directory downloading
1418 if (file_exists($file) && $this->fixSlashes(dirname($file)) == $this->fixSlashes(BMI_BACKUPS)) {
1419 require_once BMI_INCLUDES . '/zipper/zipping.php';
1420
1421 $zipper = new Zipper();
1422 $logs = $zipper->getZipFileContentPlain($file, 'bmi_logs_this_backup.log');
1423 header('Content-Type: text/plain');
1424
1425 if ($logs) {
1426 header('Content-Disposition: attachment; filename="' . substr($backupname, 0, -4) . '.log"');
1427 http_response_code(200);
1428 if (ob_get_level()) ob_end_clean();
1429
1430 $logs = explode('\n', $logs);
1431 $current_directory = Dashboard\bmi_get_config('STORAGE::LOCAL::PATH');
1432 $backups_path = $this->fixSlashes($current_directory . DIRECTORY_SEPARATOR . 'backups');
1433 $scanned_directory_all = array_diff(scandir($backups_path), ['..', '.']);
1434 $scanned_directory = array_values(preg_grep('/((.*).zip)/i', $scanned_directory_all));
1435
1436 for ($i = 0; $i < sizeof($logs); ++$i) {
1437
1438 $line = $logs[$i];
1439 echo esc_html($this->escapeSensitive($line, $current_directory, $scanned_directory)) . "\n";
1440
1441 }
1442
1443 exit;
1444 } else {
1445 if (ob_get_level()) ob_end_clean();
1446 header('HTTP/1.0 404 Not found');
1447 esc_html_e("There was an error during getting logs, this file is not right log file.", 'backup-backup');
1448 exit;
1449 }
1450 }
1451
1452 } else if ($type == 'PROGRESS_LOGS') {
1453 $allowed_progress = [
1454 'latest_full.log',
1455 'latest.log',
1456 'latest_progress.log',
1457 'latest_migration_full.log',
1458 'latest_migration.log',
1459 'latest_migration_progress.log',
1460 'latest_staging_full.log',
1461 'latest_staging.log',
1462 'latest_staging_progress.log',
1463 'complete_logs.log'
1464 ];
1465 if (isset($get_pid) && in_array($get_pid, $allowed_progress)) {
1466
1467 $restricted_progress = ['complete_logs.log'];
1468 if (in_array($get_pid, $restricted_progress)) {
1469
1470 // Only Admin can download backup logs
1471 if (!(current_user_can('administrator') || current_user_can('do_backups'))) return;
1472
1473 }
1474
1475 header('Content-Type: text/plain');
1476 header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
1477 http_response_code(200);
1478 if (ob_get_contents()) ob_end_clean();
1479 if ($get_pid == 'complete_logs.log') {
1480 $file = BMI_CONFIG_DIR . DIRECTORY_SEPARATOR . 'complete_logs.' . BMI_LOGS_SUFFIX . '.log';
1481 if (ob_get_level()) ob_end_clean();
1482 $this->readFileSensitive($file);
1483 exit;
1484 } else if ($get_pid == 'latest_full.log') {
1485 $progress = dirname(BMI_BACKUPS) . DIRECTORY_SEPARATOR . 'backups' . DIRECTORY_SEPARATOR . 'latest_progress.' . BMI_LOGS_SUFFIX . '.log';
1486 $logs = dirname(BMI_BACKUPS) . DIRECTORY_SEPARATOR . 'backups' . DIRECTORY_SEPARATOR . 'latest.' . BMI_LOGS_SUFFIX . '.log';
1487 if ((file_exists($progress) && file_exists($logs) && ((time() - filemtime($progress)) < (60 * 1))) || current_user_can('administrator')) {
1488 if (ob_get_level()) ob_end_clean();
1489 readfile($progress);
1490 echo "\n";
1491 if (isset($get_is_uncensored) && $get_is_uncensored && current_user_can('administrator')) readfile($logs);
1492 else $this->readFileSensitive($logs);
1493 exit;
1494 } else {
1495 if (file_exists($progress) && !(time() - filemtime($progress)) < (60 * 1)) {
1496 if (ob_get_level()) ob_end_clean();
1497 echo esc_html(__("Due to security reasons access to this file is disabled at this moment.", 'backup-backup')) . "\n";
1498 echo esc_html(__("Human readable: file expired.", 'backup-backup'));
1499 exit;
1500 } else {
1501 if (ob_get_level()) ob_end_clean();
1502 echo '';
1503 exit;
1504 }
1505 }
1506 } else if ($get_pid == 'latest_migration_full.log') {
1507 $progress = dirname(BMI_BACKUPS) . DIRECTORY_SEPARATOR . 'backups' . DIRECTORY_SEPARATOR . 'latest_migration_progress.' . BMI_LOGS_SUFFIX . '.log';
1508 $logs = dirname(BMI_BACKUPS) . DIRECTORY_SEPARATOR . 'backups' . DIRECTORY_SEPARATOR . 'latest_migration.' . BMI_LOGS_SUFFIX . '.log';
1509 if ((file_exists($progress) && file_exists($logs) && ((time() - filemtime($progress)) < (60 * 1))) || current_user_can('administrator')) {
1510 if (ob_get_level()) ob_end_clean();
1511 readfile($progress);
1512 echo "\n";
1513 if (isset($get_is_uncensored) && $get_is_uncensored && current_user_can('administrator')) readfile($logs);
1514 else $this->readFileSensitive($logs);
1515 exit;
1516 } else {
1517 if (file_exists($progress) && !(time() - filemtime($progress)) < (60 * 1)) {
1518 if (ob_get_level()) ob_end_clean();
1519 echo esc_html(__("Due to security reasons access to this file is disabled at this moment.", 'backup-backup')) . "\n";
1520 echo esc_html(__("Human readable: file expired.", 'backup-backup'));
1521 exit;
1522 } else {
1523 if (ob_get_level()) ob_end_clean();
1524 echo '';
1525 exit;
1526 }
1527 }
1528 } else if ($get_pid == 'latest_staging_full.log') {
1529 $progress = BMI_STAGING . DIRECTORY_SEPARATOR . 'latest_staging_progress.' . BMI_LOGS_SUFFIX . '.log';
1530 $logs = BMI_STAGING . DIRECTORY_SEPARATOR . 'latest_staging.' . BMI_LOGS_SUFFIX . '.log';
1531 if ((file_exists($progress) && file_exists($logs) && ((time() - filemtime($progress)) < (60 * 1))) || current_user_can('administrator')) {
1532 if (ob_get_level()) ob_end_clean();
1533 readfile($progress);
1534 echo "\n";
1535 $this->readFileSensitive($logs);
1536 exit;
1537 } else {
1538 if (file_exists($progress) && !(time() - filemtime($progress)) < (60 * 1)) {
1539 if (ob_get_level()) ob_end_clean();
1540 echo esc_html(__("Due to security reasons access to this file is disabled at this moment.", 'backup-backup')) . "\n";
1541 echo esc_html(__("Human readable: file expired.", 'backup-backup'));
1542 exit;
1543 } else {
1544 if (ob_get_level()) ob_end_clean();
1545 echo '';
1546 exit;
1547 }
1548 }
1549 } else {
1550 $filename = substr($get_pid, 0, strrpos($get_pid, '.log')) . '.' . BMI_LOGS_SUFFIX . '.log';
1551 $file = dirname(BMI_BACKUPS) . DIRECTORY_SEPARATOR . 'backups' . DIRECTORY_SEPARATOR . $filename;
1552 if ($get_pid == 'latest_staging.log') $file = BMI_STAGING . DIRECTORY_SEPARATOR . $filename;
1553 if ($get_pid == 'latest_staging_progress.log') $file = BMI_STAGING . DIRECTORY_SEPARATOR . $filename;
1554 if (file_exists($file) && (((time() - filemtime($file)) < (60 * 1)) || current_user_can('administrator'))) {
1555 if (ob_get_level()) ob_end_clean();
1556
1557 if (isset($get_is_uncensored) && $get_is_uncensored && current_user_can('administrator')) readfile($file);
1558 else $this->readFileSensitive($file);
1559
1560 echo "\n";
1561 if ($get_pid == 'latest.log') $file = dirname(BMI_BACKUPS) . DIRECTORY_SEPARATOR . 'backups' . DIRECTORY_SEPARATOR . 'latest_progress.' . BMI_LOGS_SUFFIX . '.log';
1562 if ($get_pid == 'latest_migration.log') $file = dirname(BMI_BACKUPS) . DIRECTORY_SEPARATOR . 'backups' . DIRECTORY_SEPARATOR . 'latest_migration_progress.' . BMI_LOGS_SUFFIX . '.log';
1563 if ($get_pid == 'latest_staging.log') $file = BMI_STAGING . DIRECTORY_SEPARATOR . 'latest_staging_progress.' . BMI_LOGS_SUFFIX . '.log';
1564 echo esc_html(__("[DOWNLOAD GENERATED] File downloaded on (server time): ", 'backup-backup')) . esc_html(date('Y-m-d H:i:s')) . "\n";
1565 echo esc_html(__("[DOWNLOAD GENERATED] Last update (seconds): ", 'backup-backup')) . esc_html(time() - filemtime($file)) . esc_html(__(" seconds ago ", 'backup-backup')) . "\n";
1566 echo esc_html(__("[DOWNLOAD GENERATED] Last update (date): ", 'backup-backup')) . esc_html(date('Y-m-d H:i:s', filemtime($file))) . " \n";
1567 exit;
1568 } else {
1569 if (file_exists($file) && !(time() - filemtime($file)) < (60 * 1)) {
1570 if (ob_get_level()) ob_end_clean();
1571 echo esc_html(__("Due to security reasons access to this file is disabled at this moment.", 'backup-backup')) . "\n";
1572 echo esc_html(__("Human readable: file expired.", 'backup-backup'));
1573 exit;
1574 } else {
1575 if (ob_get_level()) ob_end_clean();
1576 echo '';
1577 exit;
1578 }
1579 }
1580 }
1581 }
1582 } else if ($type == 'CURL_BACKUP') {
1583
1584 // We tried to use nonces here, but turns out that WordPress does not work well with generating nonces for cURL session.
1585 // We also tries to use cookiejar etc. but for researchers, this function is "verified" by process identy.
1586 // It's similarly safe as nonce, but it works in case we need, nonces gets rejected after second request.
1587 //
1588 // At the end, user who indeed would like to abuse this functionality, he can't do anything than helping the site owner.
1589 // Free browser will keep the process ongoing, user won't receive any details other than "success" - as long as the user know the process identy.
1590
1591 try {
1592
1593 // Load bypasser
1594 require_once BMI_INCLUDES . '/backup-process.php';
1595 $request = new Bypasser($get_bid, BMI_CONFIG_DIR, trailingslashit(WP_CONTENT_DIR), BMI_BACKUPS, trailingslashit(ABSPATH), plugin_dir_path(BMI_ROOT_FILE));
1596
1597 if (sizeof($request->remote_settings) === 0) return;
1598
1599 // Handle request
1600 $request->handle_batch();
1601 exit;
1602
1603 } catch (\Exception $e) {
1604
1605 error_log('There was an error with Backup Migration plugin: ' . $e->getMessage());
1606 Logger::error(__('Error handler: ', 'backup-backup') . 'ajax#01' . '|' . $e->getMessage());
1607 error_log(strval($e));
1608
1609 } catch (\Throwable $t) {
1610
1611 error_log('There was an error with Backup Migration plugin: ' . $t->getMessage());
1612 Logger::error(__('Error handler: ', 'backup-backup') . 'ajax#01' . '|' . $t->getMessage());
1613 error_log(strval($t));
1614
1615 }
1616
1617 }
1618 }
1619 }
1620 }
1621
1622 public function deactivation() {
1623 // auto deactivate pro version if exists
1624 if (function_exists('deactivate_plugins')) {
1625 $plugin = 'backup-backup-pro/backup-backup-pro.php';
1626 if (is_plugin_active($plugin)) {
1627 add_action('update_option_active_plugins', function () {
1628 $plugin = 'backup-backup-pro/backup-backup-pro.php';
1629 deactivate_plugins($plugin);
1630 });
1631 }
1632 }
1633 $this->revertLitespeed();
1634 require_once BMI_INCLUDES . '/cron/bootstrap.php';
1635 \BMI\Plugin\CRON\TaskManager::boot();
1636 \BMI\Plugin\CRON\TaskManager::on_deactivation();
1637 Logger::log(__("Plugin has been deactivated", 'backup-backup'));
1638 }
1639
1640 public static function res($array) {
1641 $GLOBALS['BMI::RESPONSE::SENT'] = true;
1642 echo json_encode(Backup_Migration_Plugin::sanitize($array));
1643
1644 if (defined('BMI_USING_CLI_FUNCTIONALITY') && BMI_USING_CLI_FUNCTIONALITY === true) {
1645 Logger::log('CLI response:');
1646 Logger::log(json_encode(Backup_Migration_Plugin::sanitize($array)));
1647 }
1648
1649 exit;
1650 }
1651
1652 public static function getAvailableMemoryInBytes() {
1653
1654 $totalMemory = @ini_get('memory_limit');
1655 if ($totalMemory == -1) {
1656
1657 $totalMemory = 32 * 1024 * 1024;
1658
1659 } else {
1660
1661 if (strpos($totalMemory, 'M') !== false || strpos($totalMemory, 'm') !== false) {
1662 $totalMemory = intval($totalMemory) * 1024 * 1024;
1663 } else if (strpos($totalMemory, 'G') !== false || strpos($totalMemory, 'g') !== false) {
1664 $totalMemory = intval($totalMemory) * 1024 * 1024 * 1024;
1665 } else if (strpos($totalMemory, 'K') !== false || strpos($totalMemory, 'k') !== false) {
1666 $totalMemory = intval($totalMemory) * 1024;
1667 } else {
1668 $totalMemory = intval($totalMemory);
1669 }
1670
1671 }
1672
1673 $availableMemory = $totalMemory - memory_get_usage(true);
1674
1675 return $availableMemory;
1676
1677 }
1678
1679 public static function sanitize($data = []) {
1680 $array = [];
1681
1682 if (is_array($data) || is_object($data)) {
1683 foreach ($data as $key => $value) {
1684 $key = ((is_numeric($key))?intval($key):sanitize_text_field($key));
1685
1686 if (is_array($value) || is_object($value)) {
1687 $array[$key] = Backup_Migration_Plugin::sanitize($value);
1688 } else {
1689 $array[$key] = sanitize_text_field($value);
1690 }
1691 }
1692 } elseif (is_string($data)) {
1693 return sanitize_text_field($data);
1694 } elseif (is_bool($data)) {
1695 return $data;
1696 } elseif (is_null($data)) {
1697 return 'false';
1698 } else {
1699 Logger::log(__("Unknow AJAX Sanitize Type: ", 'backup-backup') . gettype($data));
1700 wp_die();
1701 }
1702
1703 return $array;
1704 }
1705
1706 public static function fixLitespeed() {
1707 $litepath = BMI_INCLUDES . DIRECTORY_SEPARATOR . 'htaccess' . DIRECTORY_SEPARATOR . '.litespeed';
1708 $htpath = ABSPATH . DIRECTORY_SEPARATOR . '.htaccess';
1709 if (!is_writable($htpath)) return ['status' => 'success'];
1710 if (file_exists($htpath)) {
1711 Backup_Migration_Plugin::revertLitespeed();
1712 $litespeed = @file_get_contents($litepath);
1713 $htaccess = @file_get_contents($htpath);
1714 $htaccess = explode("\n", $htaccess);
1715 $litespeed = explode("\n", $litespeed);
1716
1717 $hasAlready = false;
1718 for ($i = 0; $i < sizeof($htaccess); ++$i) {
1719 if (strpos($htaccess[$i], 'Backup Migration') !== false) {
1720 $hasAlready = true;
1721
1722 break;
1723 }
1724 }
1725
1726 if ($hasAlready) {
1727 return ['status' => 'success'];
1728 }
1729 $htaccess[] = '';
1730 for ($i = 0; $i < sizeof($litespeed); ++$i) {
1731 $htaccess[] = $litespeed[$i];
1732 }
1733
1734 file_put_contents($htpath, implode("\n", $htaccess));
1735 } else {
1736 copy($litepath, $htpath);
1737 }
1738
1739 return ['status' => 'success'];
1740 }
1741
1742 public static function revertLitespeed() {
1743 $htpath = ABSPATH . DIRECTORY_SEPARATOR . '.htaccess';
1744 $addline = true;
1745
1746 if (!is_writable($htpath)) return ['status' => 'success'];
1747 $htaccess = @file_get_contents($htpath);
1748 $htaccess = explode("\n", $htaccess);
1749 $htFilter = [];
1750
1751 for ($i = 0; $i < sizeof($htaccess); ++$i) {
1752 if (strpos($htaccess[$i], 'Backup Migration START')) {
1753 $addline = false;
1754
1755 continue;
1756 } elseif (strpos($htaccess[$i], 'Backup Migration END')) {
1757 $addline = true;
1758
1759 continue;
1760 } else {
1761 if ($addline == true) {
1762 $htFilter[] = $htaccess[$i];
1763 }
1764 }
1765 }
1766
1767 file_put_contents($htpath, trim(implode("\n", $htFilter)));
1768
1769 return ['status' => 'success'];
1770 }
1771
1772 public static function humanSize($bytes) {
1773 if (is_int($bytes)) {
1774 $label = ['B', 'KB', 'MB', 'GB', 'TB', 'PB'];
1775 for ($i = 0; $bytes >= 1024 && $i < (count($label) - 1); $bytes /= 1024, $i++);
1776
1777 return (round($bytes, 2) . " " . $label[$i]);
1778 } else return $bytes;
1779 }
1780
1781 public static function fixSlashes($str, $slash = false) {
1782 // Old version
1783 // $str = str_replace('\\\\', DIRECTORY_SEPARATOR, $str);
1784 // $str = str_replace('\\', DIRECTORY_SEPARATOR, $str);
1785 // $str = str_replace('\/', DIRECTORY_SEPARATOR, $str);
1786 // $str = str_replace('/', DIRECTORY_SEPARATOR, $str);
1787
1788 // if ($str[strlen($str) - 1] == DIRECTORY_SEPARATOR) {
1789 // $str = substr($str, 0, -1);
1790 // }
1791
1792 // Since 1.3.2
1793 $protocol = '';
1794 if ($slash == false) $slash = DIRECTORY_SEPARATOR;
1795 if (substr($str, 0, 7) == 'http://') $protocol = 'http://';
1796 else if (substr($str, 0, 8) == 'https://') $protocol = 'https://';
1797
1798 $str = substr($str, strlen($protocol));
1799 $str = preg_replace('/[\\\\\/]+/', $slash, $str);
1800 $str = untrailingslashit($str);
1801
1802 return $protocol . $str;
1803 }
1804
1805 public static function canShareLogsOrShouldAsk() {
1806
1807 return 'not-allowed';
1808
1809 // REMOVED CODE:
1810 // $isAllowed = get_option('BMI_LOGS_SHARING_IS_ALLOWED', 'unknown');
1811 // $isAllowedConfig = Dashboard\bmi_get_config('LOGS::SHARING');
1812 //
1813 // if ($isAllowed == 'unknown' || empty($isAllowedConfig)) return 'ask';
1814 // else if ($isAllowed === 'yes' && $isAllowedConfig === 'yes') {
1815 // return 'allowed';
1816 // } else if ($isAllowed === 'no' && $isAllowedConfig === 'no') {
1817 // return 'not-allowed';
1818 // } else return 'ask';
1819
1820 }
1821
1822 public static function getRecentSize() {
1823 $folderNames = [ 'BACKUP:DATABASE' => 'database',
1824 "BACKUP:FILES::PLUGINS" => 'plugins',
1825 "BACKUP:FILES::UPLOADS" => 'uploads',
1826 "BACKUP:FILES::THEMES" => 'themes',
1827 "BACKUP:FILES::OTHERS" => 'contents_others',
1828 "BACKUP:FILES::WP" => 'wordpress'
1829 ];
1830
1831 $size = 0;
1832 foreach ($folderNames as $setting => $fileName) {
1833 if (Dashboard\bmi_get_config($setting) === 'true') {
1834 $size += get_transient('bmi_latest_size_' . $fileName);
1835 }
1836
1837 }
1838 return $size;
1839 }
1840
1841 public static function merge_arrays(&$array1, &$array2) {
1842 for ($i = 0; $i < sizeof($array2); ++$i) {
1843 $array1[] = $array2[$i];
1844 }
1845 }
1846
1847 public static function getDefaultDisabledPaths() {
1848 require_once BMI_INCLUDES . '/staging/controller.php';
1849 $staging = new Staging('..ajax..');
1850 $stagingSites = $staging->getStagingSites(true);
1851 $stagingSitesPaths = [];
1852 // Get all directory names of staging sites
1853 foreach ($stagingSites as $index => $site) {
1854
1855 // Convert every directory to their location path
1856 $stagingSitesPaths[] = '***ABSPATH***/' . $site['name'];
1857
1858 }
1859
1860 $ignored_paths_default = [
1861 BMI_CONFIG_DIR,
1862 BMI_BACKUPS,
1863 BMI_ROOT_DIR,
1864 constant('BMI_PRO_ROOT_DIR'),
1865 "***ABSPATH***/wp-content/ai1wm-backups",
1866 "***ABSPATH***/wp-content/ai1wm-backups-old",
1867 "***ABSPATH***/wp-content/mwp-download",
1868 "***ABSPATH***/wp-content/uploads/wp-clone",
1869 "***ABSPATH***/wp-content/updraft",
1870 "***ABSPATH***/wp-content/backups-dup-pro",
1871 "***ABSPATH***/wp-content/wpvividbackups",
1872 "***ABSPATH***/wp-content/backup-guard",
1873 "***ABSPATH***/wp-content/backuply",
1874 "***ABSPATH***/wp-content/backups-dup-lite",
1875 "***ABSPATH***/wp-content/uploads/backupbuddy_backups",
1876 "***ABSPATH***/wp-content/uploads/wp-file-manager-pro",
1877 "***ABSPATH***/wp-content/uploads/wp-file-manager",
1878 "***ABSPATH***/wp-content/plugins/akeebabackupwp",
1879 "***ABSPATH***/wp-content/uploads/jetbackup",
1880 "***ABSPATH***/wp-content/uploads/backup-guard",
1881 "***ABSPATH***/wp-content/uploads/wp-migrate-db",
1882 "***ABSPATH***/wp-content/uploads/wpforms/.htaccess.cpmh3129",
1883 "***ABSPATH***/wp-content/uploads/gravity_forms/.htaccess.cpmh3129",
1884 "***ABSPATH***/.htaccess.cpmh3129",
1885 "***ABSPATH***/logs/traffic.html/.md5sums",
1886 "***ABSPATH***/wp-config.php",
1887 "***ABSPATH***/wp-content/backup-migration-config.php",
1888 ];
1889 $ignored_paths = array_merge($ignored_paths_default, $stagingSitesPaths);
1890 array_walk($ignored_paths, function(&$path){
1891 $path = self::fixSlashes(str_replace('***ABSPATH***', ABSPATH, $path));
1892 });
1893 return $ignored_paths;
1894 }
1895
1896 private function get_asset($base = '', $asset = '') {
1897 return BMI_ASSETS . '/' . $base . '/' . $asset;
1898 }
1899
1900 /**
1901 * Extend the execution time for the plugin
1902 *
1903 * @return void
1904 */
1905 public static function extend_execution_time() {
1906 if (self::isFunctionEnabled('headers_sent') && self::isFunctionEnabled('session_status')) {
1907 if (!headers_sent() && session_status() === PHP_SESSION_DISABLED) {
1908 if (self::isFunctionEnabled('ignore_user_abort')) @ignore_user_abort(true);
1909 if (self::isFunctionEnabled('set_time_limit')) @set_time_limit(16000);
1910 if (self::isFunctionEnabled('ini_set')) {
1911 @ini_set('max_execution_time', '259200');
1912 @ini_set('max_input_time', '259200');
1913 }
1914 }
1915 }
1916 }
1917
1918 public static function getRetryAfterIfAvailable($ch, $response)
1919 {
1920 $phpVersion = phpversion();
1921 $curlVersion = curl_version();
1922 // Available as of PHP 8.2.0 and cURL 7.66.0
1923 if (version_compare($phpVersion, '8.2.0', '>=') && version_compare($curlVersion['version'], '7.66.0', '>=')) {
1924 $retryAfter = curl_getinfo($ch, CURLINFO_RETRY_AFTER);
1925 if ($retryAfter !== false) {
1926 return $retryAfter;
1927 }
1928 }else {
1929 $header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
1930 $header = substr($response, 0, $header_size);
1931 $headers = explode("\r\n", $header);
1932 foreach ($headers as $h) {
1933 if (preg_match('/^Retry-After:\s+(\d+)/i', $h, $matches)) {
1934 return intval($matches[1]);
1935 }
1936 }
1937 }
1938 return false;
1939
1940 }
1941
1942 public static function bmiNeedsUpdate($forPro = false) {
1943 if ($forPro) {
1944 if (!defined('BMI_BACKUP_PRO') || BMI_BACKUP_PRO != 1 ) {
1945 return false;
1946 }
1947 $plugin_file = plugin_basename(BMI_PRO_ROOT_FILE);
1948 } else {
1949 $plugin_file = plugin_basename(BMI_ROOT_FILE);
1950 }
1951
1952 $update_cache = get_site_transient('update_plugins');
1953
1954 if (!is_object($update_cache)) {
1955 $update_cache = new \stdClass();
1956 }
1957
1958 // Check if there's an update available in the cache
1959 if (!empty($update_cache->response) && !empty($update_cache->response[$plugin_file])) {
1960 return true;
1961 }
1962
1963 // If no update in cache, check if our version is older than the checked version
1964 if (!empty($update_cache->checked) && !empty($update_cache->checked[$plugin_file])) {
1965 if (version_compare(BMI_VERSION, $update_cache->checked[$plugin_file], '<')) {
1966 return true;
1967 }
1968 }
1969
1970 return false;
1971 }
1972
1973 public static function escapeSQLIDentifier($identifier) {
1974 global $wpdb;
1975
1976 // Use native %i if WordPress 6.2+ is available
1977 if (version_compare($GLOBALS['wp_version'], '6.2', '>=')) {
1978 return $wpdb->prepare('%i', $identifier);
1979 }
1980
1981 // Fallback for older WordPress versions
1982 $identifier = trim($identifier, '`');
1983 if (!preg_match('/^[a-zA-Z0-9_]+$/', $identifier)) {
1984 throw new \InvalidArgumentException("Invalid SQL identifier: " . esc_html($identifier));
1985 }
1986 return '`' . str_replace('`', '``', $identifier) . '`';
1987 }
1988
1989 public function fireBMIPAction($action, $actionParams = [], $executionParams = [], $executionType = 'INITIATOR_URL') {
1990 if (!defined('BMI_PRO_INC') || !file_exists(BMI_PRO_INC . DIRECTORY_SEPARATOR . 'services' . DIRECTORY_SEPARATOR . 'class-bmi-pro-action-initiator.php')) {
1991 return;
1992 }
1993
1994 try {
1995 require_once BMI_PRO_INC . DIRECTORY_SEPARATOR . 'services' . DIRECTORY_SEPARATOR . 'class-bmi-pro-action-initiator.php';
1996 $actionInitiator = new \BMI\Plugin\Services\ActionInitiator($action, $actionParams, Dashboard\bmi_get_config('REQUEST:SECRET'));
1997 $actionInitiator->execute($executionType, $executionParams);
1998 } catch (\Exception $e) {
1999 Logger::error(__('Error firing BMI Pro action: ', 'backup-backup') . $action . '|' . $e->getMessage());
2000 } catch (\Throwable $t) {
2001 Logger::error(__('Error firing BMI Pro action: ', 'backup-backup') . $action . '|' . $t->getMessage());
2002 }
2003 }
2004
2005 public function handleUploadComplete($md5) {
2006
2007 do_action('bmip_fire_action', 'HANDLE_BACKUP_UPLOADED_COMPLETE', ['md5' => $md5], ['timeout' => 0.01 , 'async' => false]);
2008
2009 }
2010
2011 /**
2012 * verifyFileMd5 - Verifies if the file has the same md5 as the one provided, can be used for integrity checks before restore downloaded backup
2013 *
2014 * @param {string} $filePath - Path to the file which md5 should be checked
2015 * @param {string} $expectedMd5 - Expected md5 hash of the file
2016 * @return bool - True if the file's md5 matches the expected md5, false otherwise
2017 */
2018 public static function verifyFileMd5($filePath, $expectedMd5) {
2019 if (!file_exists($filePath)) {
2020 Logger::error("File for MD5 verification does not exist: " . $filePath);
2021 return false;
2022 }
2023 require_once BMI_INCLUDES . '/services/class-file-hasher.php';
2024 $fileMd5 = FileHasher::compute($filePath);
2025
2026 if ($fileMd5 === $expectedMd5) {
2027 return true;
2028 } else {
2029 $manifestPath = BMI_BACKUPS . DIRECTORY_SEPARATOR . $expectedMd5 . '.json';
2030 if (file_exists($manifestPath)) {
2031 $manifestContent = file_get_contents($manifestPath);
2032 if ($manifestContent !== false) {
2033 $manifestData = json_decode($manifestContent, true);
2034 if (json_last_error() === JSON_ERROR_NONE && isset($manifestData['chained_hash_chunk_size'])) {
2035 $chunkSize = $manifestData['chained_hash_chunk_size'];
2036 $chainedHash = FileHasher::compute($filePath, FileHasher::CHAINED, $chunkSize);
2037 if ($chainedHash === $expectedMd5) {
2038 return true;
2039 } else {
2040 Logger::error("MD5 mismatch for file: " . $filePath . " using chained hash with chunk size " . $chunkSize . ".");
2041 return false;
2042 }
2043 } else {
2044 Logger::error("Invalid manifest JSON for MD5 verification: " . $manifestPath);
2045 return false;
2046 }
2047 } else {
2048 Logger::error("Failed to read manifest file for MD5 verification: " . $manifestPath);
2049 return false;
2050 }
2051 } else {
2052 Logger::error("Manifest file for MD5 verification does not exist: " . $manifestPath);
2053 return false;
2054 }
2055 }
2056 }
2057
2058 }
2059