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