PluginProbe ʕ •ᴥ•ʔ
Backup Migration / 2.1.5.2
Backup Migration v2.1.5.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 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
1970 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 // Solve conflict with wp-svg-icons thats breaks the dashboard sections.
1200 wp_dequeue_style('wp-svg-icons');
1201 }
1202
1203 public function handle_after_actions() {
1204
1205 // Handle After Migration actions
1206 $afterMigrationLock = BMI_TMP . DIRECTORY_SEPARATOR . '.migrationFinished';
1207 if (file_exists($afterMigrationLock)) {
1208 if (strpos(site_url(), 'tastewp') !== false) {
1209
1210 if (function_exists('wp_load_alloptions')) {
1211 wp_load_alloptions(true);
1212 }
1213
1214 update_option('__tastewp_redirection_performed', true);
1215 update_option('auto_smart_tastewp_redirect_performed', 1);
1216 update_option('tastewp_auto_activated', true);
1217 update_option('__tastewp_sub_requested', true);
1218
1219 }
1220
1221 unlink($afterMigrationLock);
1222 }
1223
1224 }
1225
1226 public function handle_downloading() {
1227 global $wpdb;
1228 @error_reporting(0);
1229 $autologin_file = BMI_BACKUPS . '/.autologin';
1230 $ip = '127.0.0.1';
1231 if (isset($_SERVER['HTTP_CLIENT_IP'])) {
1232 $ip = $_SERVER['HTTP_CLIENT_IP'];
1233 } else {
1234 if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
1235 $ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
1236 }
1237 if ($ip === false) {
1238 if (isset($_SERVER['REMOTE_ADDR'])) $ip = $_SERVER['REMOTE_ADDR'];
1239 }
1240 }
1241 $allowed = ['BMI_BACKUP', 'BMI_BACKUP_LOGS', 'PROGRESS_LOGS', 'AFTER_RESTORE', 'CURL_BACKUP'];
1242 $get_bmi = !empty($_GET['backup-migration']) ? sanitize_text_field($_GET['backup-migration']) : false;
1243 $get_bid = !empty($_GET['bmi-id']) ? sanitize_text_field($_GET['bmi-id']) : false;
1244 $get_pid = !empty($_GET['progress-id']) ? sanitize_text_field($_GET['progress-id']) : false;
1245 $get_is_uncensored = !empty($_GET['uncensored']) ? sanitize_text_field($_GET['uncensored']) : false;
1246 $crons_enabled = !empty($_GET['crons']) ? sanitize_text_field($_GET['crons']) : false;
1247 $secret_key = !empty($_GET['sk']) ? sanitize_text_field($_GET['sk']) : false;
1248
1249 if (isset($get_bmi) && in_array($get_bmi, $allowed)) {
1250 if (isset($get_bid) && strlen($get_bid) > 0 && isset($secret_key) && $secret_key === Dashboard\bmi_get_config('REQUEST:SECRET')) {
1251 $type = $get_bmi;
1252
1253 if ($type == 'AFTER_RESTORE' && isset($get_pid)) {
1254 if (file_exists($autologin_file)) {
1255 $autoLoginMD = file_get_contents($autologin_file);
1256 $autoLoginMD = explode('_', $autoLoginMD);
1257 $aID = intval($autoLoginMD[0]);
1258 $aID2 = intval($autoLoginMD[0]) - 1;
1259 $aID3 = intval($autoLoginMD[0]) + 1;
1260 $aID4 = intval($autoLoginMD[0]) + 2;
1261 $aID5 = intval($autoLoginMD[0]) + 3;
1262 $aID6 = intval($autoLoginMD[0]) + 4;
1263 $aIP = $autoLoginMD[1];
1264 $aIZ = $autoLoginMD[2];
1265
1266 // Allow 1 second delay
1267 $timeIsProper = false;
1268 if ($aID === intval($get_bid)) $timeIsProper = true;
1269 if ($aID2 === intval($get_bid)) $timeIsProper = true;
1270 if ($aID3 === intval($get_bid)) $timeIsProper = true;
1271 if ($aID4 === intval($get_bid)) $timeIsProper = true;
1272 if ($aID5 === intval($get_bid)) $timeIsProper = true;
1273 if ($aID6 === intval($get_bid)) $timeIsProper = true;
1274
1275 if ($timeIsProper && $aIP === $ip && trim($aIZ) === $get_pid) {
1276 $query = new \WP_User_Query(['role' => 'Administrator', 'count_total' => false, 'fields' => 'all']);
1277 $sqlres = $query->get_results();
1278
1279 if (sizeof($sqlres) > 0 && isset($sqlres[0]->ID) && isset($sqlres[0]->user_login)) {
1280
1281 $user = $sqlres[0];
1282 $adminID = $sqlres[0]->ID;
1283 $adminLogin = $sqlres[0]->user_login;
1284
1285 remove_all_actions('wp_login', -1000);
1286 wp_load_alloptions(true);
1287 clean_user_cache(get_current_user_id());
1288 clean_user_cache($adminID);
1289 wp_clear_auth_cookie();
1290 wp_set_current_user($adminID, $adminLogin);
1291 wp_set_auth_cookie($adminID, 1, is_ssl());
1292 do_action('wp_login', $adminLogin, $user);
1293 update_user_caches($user);
1294
1295 }
1296 $cronsEnabledParam = $crons_enabled ? "&crons=true" : "";
1297
1298 $url = admin_url('admin.php?page=backup-migration' . $cronsEnabledParam);
1299 header('Location: ' . $url);
1300
1301 @unlink($autologin_file);
1302 exit;
1303 }
1304 }
1305
1306 } else if ($type == 'BMI_BACKUP') {
1307 if (Dashboard\bmi_get_config('STORAGE::DIRECT::URL') === 'true' || current_user_can('administrator')) {
1308
1309 $backupname = $get_bid;
1310 $file = $this->fixSlashes(BMI_BACKUPS . DIRECTORY_SEPARATOR . $backupname);
1311
1312 $outsideDir = false;
1313 if (!(file_exists($file) && $this->fixSlashes(dirname($file)) == $this->fixSlashes(BMI_BACKUPS))) {
1314 $outsideDir = true;
1315 }
1316
1317 $isZip = false;
1318 if (function_exists('mime_content_type')) {
1319 $isZip = strpos(strtolower(mime_content_type($file)), 'zip') !== false;
1320 } elseif (function_exists('wp_check_filetype')) {
1321 $filetype = wp_check_filetype($file);
1322 if ($filetype && isset($filetype['ext']) && in_array(strtolower($filetype['ext']), ['zip', 'tar', 'gz', 'tar.gz'])) {
1323 $isZip = true;
1324 }
1325 } else {
1326 // fallback to extension check
1327 $lower = strtolower($file);
1328 if (
1329 substr($lower, -4) === '.zip' ||
1330 substr($lower, -4) === '.tar' ||
1331 substr($lower, -7) === '.tar.gz'
1332 ) {
1333 $isArchive = true;
1334 }
1335 }
1336
1337 if ($outsideDir || !$isZip) {
1338 header('HTTP/1.0 423 Locked');
1339 esc_html_e( "Incorrect usage of the query request.", 'backup-backup' );
1340 exit;
1341 }
1342
1343 if (Dashboard\bmi_get_config('OTHER:DOWNLOAD:DIRECT') == 'true') {
1344 if (file_exists(BMI_BACKUPS . DIRECTORY_SEPARATOR . '.htaccess')) @unlink(BMI_BACKUPS . DIRECTORY_SEPARATOR . '.htaccess');
1345 if (file_exists(dirname(BMI_BACKUPS) . DIRECTORY_SEPARATOR . '.htaccess')) @unlink(dirname(BMI_BACKUPS) . DIRECTORY_SEPARATOR . '.htaccess');
1346 $wpcontent = trailingslashit(WP_CONTENT_DIR);
1347 $wpcs = strlen($wpcontent);
1348 $url = $this->fixSlashes(content_url(substr($file, $wpcs)), '/');
1349 $path = wp_redirect($url);
1350 exit;
1351 }
1352
1353 // Prevent parent directory downloading
1354 if (ob_get_contents()) ob_end_clean();
1355
1356 if ($this->isFunctionEnabled('ignore_user_abort')) @ignore_user_abort(true);
1357 if ($this->isFunctionEnabled('set_time_limit')) @set_time_limit(16000);
1358 if ($this->isFunctionEnabled('headers_sent') && $this->isFunctionEnabled('session_status')) {
1359 if (!headers_sent() && session_status() === PHP_SESSION_DISABLED) {
1360 if ($this->isFunctionEnabled('ini_set')) {
1361 @ini_set('max_execution_time', '259200');
1362 @ini_set('max_input_time', '259200');
1363 @ini_set('memory_limit', '-1');
1364 if (@ini_get('zlib.output_compression')) {
1365 @ini_set('zlib.output_compression', 'Off');
1366 }
1367 }
1368 }
1369 }
1370
1371 if (strlen(session_id()) > 0) session_write_close();
1372
1373 $fp = @fopen($file, 'rb');
1374
1375 // header('X-Sendfile: ' . $file);
1376 // header('X-Sendfile-Type: X-Accel-Redirect');
1377 // header('X-Accel-Redirect: ' . $file);
1378 // header('X-Accel-Buffering: yes');
1379 header('Expires: 0');
1380 header('Pragma: public');
1381 header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
1382 header('Content-Disposition: attachment; filename="' . $backupname . '"');
1383 header('Content-Type: application/octet-stream');
1384 header('Content-Transfer-Encoding: binary');
1385 header('Content-Length: ' . filesize($file));
1386 header('Content-Description: File Transfer');
1387 http_response_code(200);
1388
1389 if (ob_get_level()) ob_end_clean();
1390
1391 fpassthru($fp);
1392 fclose($fp);
1393 exit;
1394
1395 } else {
1396 if (ob_get_contents()) ob_end_clean();
1397 header('HTTP/1.0 423 Locked');
1398 if (ob_get_level()) ob_end_clean();
1399 esc_html_e( "Backup download is restricted (allowed for admins only).", 'backup-backup' );
1400 exit;
1401 }
1402 } else if ($type == 'BMI_BACKUP_LOGS') {
1403
1404 // Only Admin can download backup logs
1405 if (!(current_user_can('administrator') || current_user_can('do_backups'))) return;
1406
1407 if (ob_get_contents()) ob_end_clean();
1408 $backupname = $get_bid;
1409 $file = $this->fixSlashes(BMI_BACKUPS . DIRECTORY_SEPARATOR . $backupname);
1410
1411 // Prevent parent directory downloading
1412 if (file_exists($file) && $this->fixSlashes(dirname($file)) == $this->fixSlashes(BMI_BACKUPS)) {
1413 require_once BMI_INCLUDES . '/zipper/zipping.php';
1414
1415 $zipper = new Zipper();
1416 $logs = $zipper->getZipFileContentPlain($file, 'bmi_logs_this_backup.log');
1417 header('Content-Type: text/plain');
1418
1419 if ($logs) {
1420 header('Content-Disposition: attachment; filename="' . substr($backupname, 0, -4) . '.log"');
1421 http_response_code(200);
1422 if (ob_get_level()) ob_end_clean();
1423
1424 $logs = explode('\n', $logs);
1425 $current_directory = Dashboard\bmi_get_config('STORAGE::LOCAL::PATH');
1426 $backups_path = $this->fixSlashes($current_directory . DIRECTORY_SEPARATOR . 'backups');
1427 $scanned_directory_all = array_diff(scandir($backups_path), ['..', '.']);
1428 $scanned_directory = array_values(preg_grep('/((.*).zip)/i', $scanned_directory_all));
1429
1430 for ($i = 0; $i < sizeof($logs); ++$i) {
1431
1432 $line = $logs[$i];
1433 echo esc_html($this->escapeSensitive($line, $current_directory, $scanned_directory)) . "\n";
1434
1435 }
1436
1437 exit;
1438 } else {
1439 if (ob_get_level()) ob_end_clean();
1440 header('HTTP/1.0 404 Not found');
1441 esc_html_e("There was an error during getting logs, this file is not right log file.", 'backup-backup');
1442 exit;
1443 }
1444 }
1445
1446 } else if ($type == 'PROGRESS_LOGS') {
1447 $allowed_progress = [
1448 'latest_full.log',
1449 'latest.log',
1450 'latest_progress.log',
1451 'latest_migration_full.log',
1452 'latest_migration.log',
1453 'latest_migration_progress.log',
1454 'latest_staging_full.log',
1455 'latest_staging.log',
1456 'latest_staging_progress.log',
1457 'complete_logs.log'
1458 ];
1459 if (isset($get_pid) && in_array($get_pid, $allowed_progress)) {
1460
1461 $restricted_progress = ['complete_logs.log'];
1462 if (in_array($get_pid, $restricted_progress)) {
1463
1464 // Only Admin can download backup logs
1465 if (!(current_user_can('administrator') || current_user_can('do_backups'))) return;
1466
1467 }
1468
1469 header('Content-Type: text/plain');
1470 header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
1471 http_response_code(200);
1472 if (ob_get_contents()) ob_end_clean();
1473 if ($get_pid == 'complete_logs.log') {
1474 $file = BMI_CONFIG_DIR . DIRECTORY_SEPARATOR . 'complete_logs.' . BMI_LOGS_SUFFIX . '.log';
1475 if (ob_get_level()) ob_end_clean();
1476 $this->readFileSensitive($file);
1477 exit;
1478 } else if ($get_pid == 'latest_full.log') {
1479 $progress = dirname(BMI_BACKUPS) . DIRECTORY_SEPARATOR . 'backups' . DIRECTORY_SEPARATOR . 'latest_progress.' . BMI_LOGS_SUFFIX . '.log';
1480 $logs = dirname(BMI_BACKUPS) . DIRECTORY_SEPARATOR . 'backups' . DIRECTORY_SEPARATOR . 'latest.' . BMI_LOGS_SUFFIX . '.log';
1481 if ((file_exists($progress) && file_exists($logs) && ((time() - filemtime($progress)) < (60 * 1))) || current_user_can('administrator')) {
1482 if (ob_get_level()) ob_end_clean();
1483 readfile($progress);
1484 echo "\n";
1485 if (isset($get_is_uncensored) && $get_is_uncensored && current_user_can('administrator')) readfile($logs);
1486 else $this->readFileSensitive($logs);
1487 exit;
1488 } else {
1489 if (file_exists($progress) && !(time() - filemtime($progress)) < (60 * 1)) {
1490 if (ob_get_level()) ob_end_clean();
1491 echo esc_html(__("Due to security reasons access to this file is disabled at this moment.", 'backup-backup')) . "\n";
1492 echo esc_html(__("Human readable: file expired.", 'backup-backup'));
1493 exit;
1494 } else {
1495 if (ob_get_level()) ob_end_clean();
1496 echo '';
1497 exit;
1498 }
1499 }
1500 } else if ($get_pid == 'latest_migration_full.log') {
1501 $progress = dirname(BMI_BACKUPS) . DIRECTORY_SEPARATOR . 'backups' . DIRECTORY_SEPARATOR . 'latest_migration_progress.' . BMI_LOGS_SUFFIX . '.log';
1502 $logs = dirname(BMI_BACKUPS) . DIRECTORY_SEPARATOR . 'backups' . DIRECTORY_SEPARATOR . 'latest_migration.' . BMI_LOGS_SUFFIX . '.log';
1503 if ((file_exists($progress) && file_exists($logs) && ((time() - filemtime($progress)) < (60 * 1))) || current_user_can('administrator')) {
1504 if (ob_get_level()) ob_end_clean();
1505 readfile($progress);
1506 echo "\n";
1507 if (isset($get_is_uncensored) && $get_is_uncensored && current_user_can('administrator')) readfile($logs);
1508 else $this->readFileSensitive($logs);
1509 exit;
1510 } else {
1511 if (file_exists($progress) && !(time() - filemtime($progress)) < (60 * 1)) {
1512 if (ob_get_level()) ob_end_clean();
1513 echo esc_html(__("Due to security reasons access to this file is disabled at this moment.", 'backup-backup')) . "\n";
1514 echo esc_html(__("Human readable: file expired.", 'backup-backup'));
1515 exit;
1516 } else {
1517 if (ob_get_level()) ob_end_clean();
1518 echo '';
1519 exit;
1520 }
1521 }
1522 } else if ($get_pid == 'latest_staging_full.log') {
1523 $progress = BMI_STAGING . DIRECTORY_SEPARATOR . 'latest_staging_progress.' . BMI_LOGS_SUFFIX . '.log';
1524 $logs = BMI_STAGING . DIRECTORY_SEPARATOR . 'latest_staging.' . BMI_LOGS_SUFFIX . '.log';
1525 if ((file_exists($progress) && file_exists($logs) && ((time() - filemtime($progress)) < (60 * 1))) || current_user_can('administrator')) {
1526 if (ob_get_level()) ob_end_clean();
1527 readfile($progress);
1528 echo "\n";
1529 $this->readFileSensitive($logs);
1530 exit;
1531 } else {
1532 if (file_exists($progress) && !(time() - filemtime($progress)) < (60 * 1)) {
1533 if (ob_get_level()) ob_end_clean();
1534 echo esc_html(__("Due to security reasons access to this file is disabled at this moment.", 'backup-backup')) . "\n";
1535 echo esc_html(__("Human readable: file expired.", 'backup-backup'));
1536 exit;
1537 } else {
1538 if (ob_get_level()) ob_end_clean();
1539 echo '';
1540 exit;
1541 }
1542 }
1543 } else {
1544 $filename = substr($get_pid, 0, strrpos($get_pid, '.log')) . '.' . BMI_LOGS_SUFFIX . '.log';
1545 $file = dirname(BMI_BACKUPS) . DIRECTORY_SEPARATOR . 'backups' . DIRECTORY_SEPARATOR . $filename;
1546 if ($get_pid == 'latest_staging.log') $file = BMI_STAGING . DIRECTORY_SEPARATOR . $filename;
1547 if ($get_pid == 'latest_staging_progress.log') $file = BMI_STAGING . DIRECTORY_SEPARATOR . $filename;
1548 if (file_exists($file) && (((time() - filemtime($file)) < (60 * 1)) || current_user_can('administrator'))) {
1549 if (ob_get_level()) ob_end_clean();
1550
1551 if (isset($get_is_uncensored) && $get_is_uncensored && current_user_can('administrator')) readfile($file);
1552 else $this->readFileSensitive($file);
1553
1554 echo "\n";
1555 if ($get_pid == 'latest.log') $file = dirname(BMI_BACKUPS) . DIRECTORY_SEPARATOR . 'backups' . DIRECTORY_SEPARATOR . 'latest_progress.' . BMI_LOGS_SUFFIX . '.log';
1556 if ($get_pid == 'latest_migration.log') $file = dirname(BMI_BACKUPS) . DIRECTORY_SEPARATOR . 'backups' . DIRECTORY_SEPARATOR . 'latest_migration_progress.' . BMI_LOGS_SUFFIX . '.log';
1557 if ($get_pid == 'latest_staging.log') $file = BMI_STAGING . DIRECTORY_SEPARATOR . 'latest_staging_progress.' . BMI_LOGS_SUFFIX . '.log';
1558 echo esc_html(__("[DOWNLOAD GENERATED] File downloaded on (server time): ", 'backup-backup')) . esc_html(date('Y-m-d H:i:s')) . "\n";
1559 echo esc_html(__("[DOWNLOAD GENERATED] Last update (seconds): ", 'backup-backup')) . esc_html(time() - filemtime($file)) . esc_html(__(" seconds ago ", 'backup-backup')) . "\n";
1560 echo esc_html(__("[DOWNLOAD GENERATED] Last update (date): ", 'backup-backup')) . esc_html(date('Y-m-d H:i:s', filemtime($file))) . " \n";
1561 exit;
1562 } else {
1563 if (file_exists($file) && !(time() - filemtime($file)) < (60 * 1)) {
1564 if (ob_get_level()) ob_end_clean();
1565 echo esc_html(__("Due to security reasons access to this file is disabled at this moment.", 'backup-backup')) . "\n";
1566 echo esc_html(__("Human readable: file expired.", 'backup-backup'));
1567 exit;
1568 } else {
1569 if (ob_get_level()) ob_end_clean();
1570 echo '';
1571 exit;
1572 }
1573 }
1574 }
1575 }
1576 } else if ($type == 'CURL_BACKUP') {
1577
1578 // We tried to use nonces here, but turns out that WordPress does not work well with generating nonces for cURL session.
1579 // We also tries to use cookiejar etc. but for researchers, this function is "verified" by process identy.
1580 // It's similarly safe as nonce, but it works in case we need, nonces gets rejected after second request.
1581 //
1582 // At the end, user who indeed would like to abuse this functionality, he can't do anything than helping the site owner.
1583 // 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.
1584
1585 try {
1586
1587 // Load bypasser
1588 require_once BMI_INCLUDES . '/backup-process.php';
1589 $request = new Bypasser($get_bid, BMI_CONFIG_DIR, trailingslashit(WP_CONTENT_DIR), BMI_BACKUPS, trailingslashit(ABSPATH), plugin_dir_path(BMI_ROOT_FILE));
1590
1591 if (sizeof($request->remote_settings) === 0) return;
1592
1593 // Handle request
1594 $request->handle_batch();
1595 exit;
1596
1597 } catch (\Exception $e) {
1598
1599 error_log('There was an error with Backup Migration plugin: ' . $e->getMessage());
1600 Logger::error(__('Error handler: ', 'backup-backup') . 'ajax#01' . '|' . $e->getMessage());
1601 error_log(strval($e));
1602
1603 } catch (\Throwable $t) {
1604
1605 error_log('There was an error with Backup Migration plugin: ' . $t->getMessage());
1606 Logger::error(__('Error handler: ', 'backup-backup') . 'ajax#01' . '|' . $t->getMessage());
1607 error_log(strval($t));
1608
1609 }
1610
1611 }
1612 }
1613 }
1614 }
1615
1616 public function deactivation() {
1617 Logger::log(__("Plugin has been deactivated", 'backup-backup'));
1618 $this->revertLitespeed();
1619 }
1620
1621 public static function res($array) {
1622 $GLOBALS['BMI::RESPONSE::SENT'] = true;
1623 echo json_encode(Backup_Migration_Plugin::sanitize($array));
1624
1625 if (defined('BMI_USING_CLI_FUNCTIONALITY') && BMI_USING_CLI_FUNCTIONALITY === true) {
1626 Logger::log('CLI response:');
1627 Logger::log(json_encode(Backup_Migration_Plugin::sanitize($array)));
1628 }
1629
1630 exit;
1631 }
1632
1633 public static function getAvailableMemoryInBytes() {
1634
1635 $totalMemory = @ini_get('memory_limit');
1636 if ($totalMemory == -1) {
1637
1638 $totalMemory = 32 * 1024 * 1024;
1639
1640 } else {
1641
1642 if (strpos($totalMemory, 'M') !== false || strpos($totalMemory, 'm') !== false) {
1643 $totalMemory = intval($totalMemory) * 1024 * 1024;
1644 } else if (strpos($totalMemory, 'G') !== false || strpos($totalMemory, 'g') !== false) {
1645 $totalMemory = intval($totalMemory) * 1024 * 1024 * 1024;
1646 } else if (strpos($totalMemory, 'K') !== false || strpos($totalMemory, 'k') !== false) {
1647 $totalMemory = intval($totalMemory) * 1024;
1648 } else {
1649 $totalMemory = intval($totalMemory);
1650 }
1651
1652 }
1653
1654 $availableMemory = $totalMemory - memory_get_usage(true);
1655
1656 return $availableMemory;
1657
1658 }
1659
1660 public static function sanitize($data = []) {
1661 $array = [];
1662
1663 if (is_array($data) || is_object($data)) {
1664 foreach ($data as $key => $value) {
1665 $key = ((is_numeric($key))?intval($key):sanitize_text_field($key));
1666
1667 if (is_array($value) || is_object($value)) {
1668 $array[$key] = Backup_Migration_Plugin::sanitize($value);
1669 } else {
1670 $array[$key] = sanitize_text_field($value);
1671 }
1672 }
1673 } elseif (is_string($data)) {
1674 return sanitize_text_field($data);
1675 } elseif (is_bool($data)) {
1676 return $data;
1677 } elseif (is_null($data)) {
1678 return 'false';
1679 } else {
1680 Logger::log(__("Unknow AJAX Sanitize Type: ", 'backup-backup') . gettype($data));
1681 wp_die();
1682 }
1683
1684 return $array;
1685 }
1686
1687 public static function fixLitespeed() {
1688 $litepath = BMI_INCLUDES . DIRECTORY_SEPARATOR . 'htaccess' . DIRECTORY_SEPARATOR . '.litespeed';
1689 $htpath = ABSPATH . DIRECTORY_SEPARATOR . '.htaccess';
1690 if (!is_writable($htpath)) return ['status' => 'success'];
1691 if (file_exists($htpath)) {
1692 Backup_Migration_Plugin::revertLitespeed();
1693 $litespeed = @file_get_contents($litepath);
1694 $htaccess = @file_get_contents($htpath);
1695 $htaccess = explode("\n", $htaccess);
1696 $litespeed = explode("\n", $litespeed);
1697
1698 $hasAlready = false;
1699 for ($i = 0; $i < sizeof($htaccess); ++$i) {
1700 if (strpos($htaccess[$i], 'Backup Migration') !== false) {
1701 $hasAlready = true;
1702
1703 break;
1704 }
1705 }
1706
1707 if ($hasAlready) {
1708 return ['status' => 'success'];
1709 }
1710 $htaccess[] = '';
1711 for ($i = 0; $i < sizeof($litespeed); ++$i) {
1712 $htaccess[] = $litespeed[$i];
1713 }
1714
1715 file_put_contents($htpath, implode("\n", $htaccess));
1716 } else {
1717 copy($litepath, $htpath);
1718 }
1719
1720 return ['status' => 'success'];
1721 }
1722
1723 public static function revertLitespeed() {
1724 $htpath = ABSPATH . DIRECTORY_SEPARATOR . '.htaccess';
1725 $addline = true;
1726
1727 if (!is_writable($htpath)) return ['status' => 'success'];
1728 $htaccess = @file_get_contents($htpath);
1729 $htaccess = explode("\n", $htaccess);
1730 $htFilter = [];
1731
1732 for ($i = 0; $i < sizeof($htaccess); ++$i) {
1733 if (strpos($htaccess[$i], 'Backup Migration START')) {
1734 $addline = false;
1735
1736 continue;
1737 } elseif (strpos($htaccess[$i], 'Backup Migration END')) {
1738 $addline = true;
1739
1740 continue;
1741 } else {
1742 if ($addline == true) {
1743 $htFilter[] = $htaccess[$i];
1744 }
1745 }
1746 }
1747
1748 file_put_contents($htpath, trim(implode("\n", $htFilter)));
1749
1750 return ['status' => 'success'];
1751 }
1752
1753 public static function humanSize($bytes) {
1754 if (is_int($bytes)) {
1755 $label = ['B', 'KB', 'MB', 'GB', 'TB', 'PB'];
1756 for ($i = 0; $bytes >= 1024 && $i < (count($label) - 1); $bytes /= 1024, $i++);
1757
1758 return (round($bytes, 2) . " " . $label[$i]);
1759 } else return $bytes;
1760 }
1761
1762 public static function fixSlashes($str, $slash = false) {
1763 // Old version
1764 // $str = str_replace('\\\\', DIRECTORY_SEPARATOR, $str);
1765 // $str = str_replace('\\', DIRECTORY_SEPARATOR, $str);
1766 // $str = str_replace('\/', DIRECTORY_SEPARATOR, $str);
1767 // $str = str_replace('/', DIRECTORY_SEPARATOR, $str);
1768
1769 // if ($str[strlen($str) - 1] == DIRECTORY_SEPARATOR) {
1770 // $str = substr($str, 0, -1);
1771 // }
1772
1773 // Since 1.3.2
1774 $protocol = '';
1775 if ($slash == false) $slash = DIRECTORY_SEPARATOR;
1776 if (substr($str, 0, 7) == 'http://') $protocol = 'http://';
1777 else if (substr($str, 0, 8) == 'https://') $protocol = 'https://';
1778
1779 $str = substr($str, strlen($protocol));
1780 $str = preg_replace('/[\\\\\/]+/', $slash, $str);
1781 $str = untrailingslashit($str);
1782
1783 return $protocol . $str;
1784 }
1785
1786 public static function canShareLogsOrShouldAsk() {
1787
1788 return 'not-allowed';
1789
1790 // REMOVED CODE:
1791 // $isAllowed = get_option('BMI_LOGS_SHARING_IS_ALLOWED', 'unknown');
1792 // $isAllowedConfig = Dashboard\bmi_get_config('LOGS::SHARING');
1793 //
1794 // if ($isAllowed == 'unknown' || empty($isAllowedConfig)) return 'ask';
1795 // else if ($isAllowed === 'yes' && $isAllowedConfig === 'yes') {
1796 // return 'allowed';
1797 // } else if ($isAllowed === 'no' && $isAllowedConfig === 'no') {
1798 // return 'not-allowed';
1799 // } else return 'ask';
1800
1801 }
1802
1803 public static function getRecentSize() {
1804 $folderNames = [ 'BACKUP:DATABASE' => 'database',
1805 "BACKUP:FILES::PLUGINS" => 'plugins',
1806 "BACKUP:FILES::UPLOADS" => 'uploads',
1807 "BACKUP:FILES::THEMES" => 'themes',
1808 "BACKUP:FILES::OTHERS" => 'contents_others',
1809 "BACKUP:FILES::WP" => 'wordpress'
1810 ];
1811
1812 $size = 0;
1813 foreach ($folderNames as $setting => $fileName) {
1814 if (Dashboard\bmi_get_config($setting) === 'true') {
1815 $size += get_transient('bmi_latest_size_' . $fileName);
1816 }
1817
1818 }
1819 return $size;
1820 }
1821
1822 public static function merge_arrays(&$array1, &$array2) {
1823 for ($i = 0; $i < sizeof($array2); ++$i) {
1824 $array1[] = $array2[$i];
1825 }
1826 }
1827
1828 public static function getDefaultDisabledPaths() {
1829 require_once BMI_INCLUDES . '/staging/controller.php';
1830 $staging = new Staging('..ajax..');
1831 $stagingSites = $staging->getStagingSites(true);
1832 $stagingSitesPaths = [];
1833 // Get all directory names of staging sites
1834 foreach ($stagingSites as $index => $site) {
1835
1836 // Convert every directory to their location path
1837 $stagingSitesPaths[] = '***ABSPATH***/' . $site['name'];
1838
1839 }
1840
1841 $ignored_paths_default = [
1842 BMI_CONFIG_DIR,
1843 BMI_BACKUPS,
1844 BMI_ROOT_DIR,
1845 constant('BMI_PRO_ROOT_DIR'),
1846 "***ABSPATH***/wp-content/ai1wm-backups",
1847 "***ABSPATH***/wp-content/ai1wm-backups-old",
1848 "***ABSPATH***/wp-content/mwp-download",
1849 "***ABSPATH***/wp-content/uploads/wp-clone",
1850 "***ABSPATH***/wp-content/updraft",
1851 "***ABSPATH***/wp-content/backups-dup-pro",
1852 "***ABSPATH***/wp-content/wpvividbackups",
1853 "***ABSPATH***/wp-content/backup-guard",
1854 "***ABSPATH***/wp-content/backuply",
1855 "***ABSPATH***/wp-content/backups-dup-lite",
1856 "***ABSPATH***/wp-content/uploads/backupbuddy_backups",
1857 "***ABSPATH***/wp-content/uploads/wp-file-manager-pro",
1858 "***ABSPATH***/wp-content/uploads/wp-file-manager",
1859 "***ABSPATH***/wp-content/plugins/akeebabackupwp",
1860 "***ABSPATH***/wp-content/uploads/jetbackup",
1861 "***ABSPATH***/wp-content/uploads/backup-guard",
1862 "***ABSPATH***/wp-content/uploads/wp-migrate-db",
1863 "***ABSPATH***/wp-content/uploads/wpforms/.htaccess.cpmh3129",
1864 "***ABSPATH***/wp-content/uploads/gravity_forms/.htaccess.cpmh3129",
1865 "***ABSPATH***/.htaccess.cpmh3129",
1866 "***ABSPATH***/logs/traffic.html/.md5sums",
1867 "***ABSPATH***/wp-config.php",
1868 "***ABSPATH***/wp-content/backup-migration-config.php",
1869 ];
1870 $ignored_paths = array_merge($ignored_paths_default, $stagingSitesPaths);
1871 array_walk($ignored_paths, function(&$path){
1872 $path = self::fixSlashes(str_replace('***ABSPATH***', ABSPATH, $path));
1873 });
1874 return $ignored_paths;
1875 }
1876
1877 private function get_asset($base = '', $asset = '') {
1878 return BMI_ASSETS . '/' . $base . '/' . $asset;
1879 }
1880
1881 /**
1882 * Extend the execution time for the plugin
1883 *
1884 * @return void
1885 */
1886 public static function extend_execution_time() {
1887 if (self::isFunctionEnabled('headers_sent') && self::isFunctionEnabled('session_status')) {
1888 if (!headers_sent() && session_status() === PHP_SESSION_DISABLED) {
1889 if (self::isFunctionEnabled('ignore_user_abort')) @ignore_user_abort(true);
1890 if (self::isFunctionEnabled('set_time_limit')) @set_time_limit(16000);
1891 if (self::isFunctionEnabled('ini_set')) {
1892 @ini_set('max_execution_time', '259200');
1893 @ini_set('max_input_time', '259200');
1894 }
1895 }
1896 }
1897 }
1898
1899 public static function getRetryAfterIfAvailable($ch, $response)
1900 {
1901 $phpVersion = phpversion();
1902 $curlVersion = curl_version();
1903 // Available as of PHP 8.2.0 and cURL 7.66.0
1904 if (version_compare($phpVersion, '8.2.0', '>=') && version_compare($curlVersion['version'], '7.66.0', '>=')) {
1905 $retryAfter = curl_getinfo($ch, CURLINFO_RETRY_AFTER);
1906 if ($retryAfter !== false) {
1907 return $retryAfter;
1908 }
1909 }else {
1910 $header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
1911 $header = substr($response, 0, $header_size);
1912 $headers = explode("\r\n", $header);
1913 foreach ($headers as $h) {
1914 if (preg_match('/^Retry-After:\s+(\d+)/i', $h, $matches)) {
1915 return intval($matches[1]);
1916 }
1917 }
1918 }
1919 return false;
1920
1921 }
1922
1923 public static function bmiNeedsUpdate($forPro = false) {
1924 if ($forPro) {
1925 if (!defined('BMI_BACKUP_PRO') || BMI_BACKUP_PRO != 1 ) {
1926 return false;
1927 }
1928 $plugin_file = plugin_basename(BMI_PRO_ROOT_FILE);
1929 } else {
1930 $plugin_file = plugin_basename(BMI_ROOT_FILE);
1931 }
1932
1933 $update_cache = get_site_transient('update_plugins');
1934
1935 if (!is_object($update_cache)) {
1936 $update_cache = new \stdClass();
1937 }
1938
1939 // Check if there's an update available in the cache
1940 if (!empty($update_cache->response) && !empty($update_cache->response[$plugin_file])) {
1941 return true;
1942 }
1943
1944 // If no update in cache, check if our version is older than the checked version
1945 if (!empty($update_cache->checked) && !empty($update_cache->checked[$plugin_file])) {
1946 if (version_compare(BMI_VERSION, $update_cache->checked[$plugin_file], '<')) {
1947 return true;
1948 }
1949 }
1950
1951 return false;
1952 }
1953
1954 public static function escapeSQLIDentifier($identifier) {
1955 global $wpdb;
1956
1957 // Use native %i if WordPress 6.2+ is available
1958 if (version_compare($GLOBALS['wp_version'], '6.2', '>=')) {
1959 return $wpdb->prepare('%i', $identifier);
1960 }
1961
1962 // Fallback for older WordPress versions
1963 $identifier = trim($identifier, '`');
1964 if (!preg_match('/^[a-zA-Z0-9_]+$/', $identifier)) {
1965 throw new \InvalidArgumentException("Invalid SQL identifier: " . esc_html($identifier));
1966 }
1967 return '`' . str_replace('`', '``', $identifier) . '`';
1968 }
1969 }
1970