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