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