PluginProbe
WP Database Backup – Unlimited Database & Files Backup by Backup for WP / 7.13
WP Database Backup – Unlimited Database & Files Backup by Backup for WP v7.13
7.13 7.12 trunk 1.1 2.1.1 5.9 6.0 6.1 6.10 6.11 6.12 6.12.1 6.2 6.3 6.4 6.5 6.5.1 6.6 6.7 6.8 6.9 7.0 7.0.1 7.1 7.10 All 34 releases
wp-database-backup / includes / features.php

features.php in WP Database Backup – Unlimited Database & Files Backup by Backup for WP 7.13, at includes/features.php

1,207 lines 39.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 if ( ! defined( 'ABSPATH' ) ) {
3 exit;
4 }
5
6 /**
7 * Log backup/restore messages when WP_DEBUG_LOG is enabled.
8 *
9 * @param string $message Log message.
10 */
11 function wpdbbkp_log( $message ) {
12 if ( defined( 'WP_DEBUG' ) && WP_DEBUG && defined( 'WP_DEBUG_LOG' ) && WP_DEBUG_LOG ) {
13 // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- Intentional backup operation logging.
14 error_log( $message );
15 }
16 }
17
18 // Anonimization code
19 add_filter('wpdbbkp_process_db_fields', 'bkpforwp_anonimize_database', 10, 3);
20 add_action('wp_ajax_wpdbbkp_check_extract_status', 'wpdbbkp_check_extract_status');
21 function wpdbbkp_check_extract_status(){
22 if ( ! current_user_can( 'manage_options' ) ) {
23 wp_send_json_error( esc_html__( 'Permission denied.', 'wpdbbkp' ) );
24 return;
25 }
26 if ( ! isset( $_POST['wpdbbkp_admin_security_nonce'] ) || ! wp_verify_nonce( wp_unslash( $_POST['wpdbbkp_admin_security_nonce'] ), 'wpdbbkp_ajax_check_nonce' ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Nonce.
27 wp_send_json_success( esc_html__( 'Invalid nonce check.', 'wpdbbkp' ) );
28 return;
29 }
30 $get_progress = get_transient('wpdbbkp_track_progress');
31 if($get_progress==false){
32 wp_send_json_success(['success'=>1,'message'=>esc_html__('Starring Extraction Process', 'wpdbbkp')]);
33 }else{
34 if($get_progress=='Process Completed'){
35 delete_transient('wpdbbkp_track_progress');
36 }
37 wp_send_json_success(['success'=>1,'message'=>$get_progress]);
38 }
39 }
40
41 add_action('wp_ajax_wpdbbkp_upload_site_chunk', 'wpdbbkp_upload_site_chunk');
42 function wpdbbkp_upload_site_chunk() {
43 if ( ! current_user_can( 'manage_options' ) ) {
44 wp_send_json_error( esc_html__( 'Permission denied.', 'wpdbbkp' ) );
45 return;
46 }
47 if ( ! isset( $_POST['wpdbbkp_admin_security_nonce'] ) || ! wp_verify_nonce( wp_unslash( $_POST['wpdbbkp_admin_security_nonce'] ), 'wpdbbkp_ajax_check_nonce' ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Nonce.
48 wp_send_json_success( esc_html__( 'Invalid nonce check.', 'wpdbbkp' ) );
49 return;
50 }
51 if ( ! isset( $_FILES['file'], $_POST['fileName'], $_POST['offset'] ) ) {
52 wp_send_json_error( esc_html__( 'Invalid request.', 'wpdbbkp' ) );
53 }
54
55 $upload_dir = WP_CONTENT_DIR . '/uploads/wpdbbkp/temp';
56 wp_mkdir_p( $upload_dir );
57
58 wpdbbkp_protect_temp_directory( $upload_dir );
59
60 $file_name = sanitize_file_name( wp_unslash( $_POST['fileName'] ) );
61 $file_path = $upload_dir . '/' . $file_name;
62
63 $chunk_tmp = isset( $_FILES['file']['tmp_name'] ) ? $_FILES['file']['tmp_name'] : ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Validated with is_uploaded_file().
64
65 if ( empty( $chunk_tmp ) || ! is_uploaded_file( $chunk_tmp ) ) {
66 wp_send_json_error( esc_html__( 'Invalid upload.', 'wpdbbkp' ) );
67 }
68
69 // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents, WordPress.WP.AlternativeFunctions.file_system_operations_file_put_contents -- Appending validated upload chunk.
70 file_put_contents( $file_path, file_get_contents( $chunk_tmp ), FILE_APPEND );
71
72 wp_send_json_success(esc_html__('Chunk uploaded successfully.', 'wpdbbkp'));
73 }
74 add_action('wp_ajax_wpdbbkp_extract_uploaded_site', 'wpdbbkp_extract_uploaded_site');
75 function wpdbbkp_extract_uploaded_site() {
76
77 try {
78
79 if ( ! current_user_can( 'manage_options' ) ) {
80 wp_send_json_error( esc_html__( 'Permission denied.', 'wpdbbkp' ) );
81 return;
82 }
83
84 if ( ! isset( $_POST['wpdbbkp_admin_security_nonce'] ) || ! wp_verify_nonce( wp_unslash( $_POST['wpdbbkp_admin_security_nonce'] ), 'wpdbbkp_ajax_check_nonce' ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Nonce.
85
86 wp_send_json_success( esc_html__( 'Invalid nonce check.', 'wpdbbkp' ) );
87
88 return;
89
90 }
91
92 // phpcs:ignore Squiz.PHP.DiscouragedFunctions.Discouraged -- Required for large backup extraction.
93 ini_set( 'max_execution_time', '0' );
94
95 // phpcs:ignore Squiz.PHP.DiscouragedFunctions.Discouraged -- Required for large backup extraction.
96 set_time_limit( 0 );
97
98 wpdbbkp_log( 'AJAX extraction started' );
99
100
101
102 if (!isset($_POST['fileName'])) {
103
104 throw new Exception("No file provided.");
105
106 }
107
108
109
110 $upload_dir = WP_CONTENT_DIR . '/uploads/wpdbbkp/temp';
111
112 $backup_file = $upload_dir . '/' . sanitize_file_name( wp_unslash( $_POST['fileName'] ) );
113
114
115
116 if (!file_exists($backup_file)) {
117
118 throw new Exception("Backup file not found.");
119
120 }
121
122
123
124 $extract_path = $upload_dir . '/extracted/';
125
126 $zip = new ZipArchive();
127
128 if ($zip->open($backup_file) !== TRUE) {
129
130 throw new Exception("Failed to open the ZIP archive.");
131
132 }
133
134
135
136 wp_mkdir_p( $extract_path );
137
138
139
140 wpdbbkp_log("ZIP contains " . $zip->numFiles . " files");
141
142
143
144
145
146 $root_folder = '';
147
148 for ($i = 0; $i < $zip->numFiles; $i++) {
149
150 $entry = $zip->getNameIndex($i);
151
152 if (preg_match('#^(.*?)/wp-content/#', $entry, $matches)) {
153
154 $root_folder = $matches[1] . '/';
155
156 break;
157
158 }
159
160 }
161
162 wpdbbkp_log("Detected root folder: $root_folder");
163
164
165
166
167
168 for ($i = 0; $i < $zip->numFiles; $i++) {
169
170 $entry = $zip->getNameIndex($i);
171
172 $normalized_entry = str_replace($root_folder, '', $entry);
173
174
175
176 if (preg_match('#^wp-content/plugins/#', $normalized_entry) ||
177
178 preg_match('#^wp-content/themes/#', $normalized_entry) ||
179
180 (pathinfo($normalized_entry, PATHINFO_EXTENSION) === 'sql' && substr_count($normalized_entry, '/') === 0)
181
182 ) {
183
184 if ($zip->extractTo($extract_path, $entry)) {
185
186 set_transient('wpdbbkp_track_progress', $entry, 3600);
187
188 wpdbbkp_log("Successfully extracted: $entry");
189
190
191
192 } else {
193
194 wpdbbkp_log("Extraction failed: $entry");
195
196 }
197
198 }
199
200 }
201
202
203
204 $zip->close();
205
206 set_transient('wpdbbkp_track_progress', 'Extraction of files completed. Now taking live plugins & themes', 3600);
207
208 $plugins_path = $extract_path . '/wp-content/plugins/';
209
210 $themes_path = $extract_path . '/wp-content/themes/';
211
212
213
214 // Move extracted files to wp-content
215
216 $ignore_plugins = ['wp-database-backup'];
217
218
219
220 if (file_exists($plugins_path)) {
221
222 wpdbbkp_move_extracted_files($plugins_path, WP_CONTENT_DIR . '/plugins/', $ignore_plugins);
223
224 } else {
225
226 wpdbbkp_log("Plugins directory not found in extracted folder.");
227
228 }
229
230
231
232 if (file_exists($themes_path)) {
233
234 wpdbbkp_move_extracted_files($themes_path, WP_CONTENT_DIR . '/themes/');
235
236 } else {
237
238 wpdbbkp_log("Themes directory not found in extracted folder OR extraction failed.");
239
240 }
241
242
243
244 // Process SQL file if found
245
246 $sql_file = wpdbbkp_find_sql_file($extract_path);
247
248 if ($sql_file) {
249
250 set_transient('wpdbbkp_track_progress', 'Migrating Database...', 3600);
251
252 $wp_config_prefix = wpdbbkp_get_wp_config_table_prefix();
253 wpdbbkp_log("Sql File .".$sql_file);
254 wpdbbkp_update_sql_table_prefix($sql_file);
255
256 wpdbbkp_restore_database($sql_file);
257
258 } else {
259
260 wpdbbkp_log("No SQL file found in extracted folder.");
261
262 }
263
264
265
266 wpdbbkp_log("Extraction completed successfully");
267
268 $wpdbbkp_folder = WP_CONTENT_DIR . '/uploads/wpdbbkp';
269 if (file_exists($wpdbbkp_folder)) {
270 wpdbbkp_delete_folder($wpdbbkp_folder);
271 wpdbbkp_log("Deleted wpdbbkp folder after successful migration.");
272 }
273 set_transient('wpdbbkp_track_progress', 'Process Completed', 3600);
274 wp_send_json_success(esc_html__('Plugins, Themes, Database & Table Prefix Updated!', 'wpdbbkp'));
275
276
277
278 } catch (Exception $e) {
279
280 wpdbbkp_log("Error: " . $e->getMessage());
281
282 wp_send_json_error($e->getMessage());
283
284 }
285
286 }
287
288 /**
289 * Initialize WP_Filesystem API.
290 *
291 * @return bool
292 */
293 function wpdbbkp_init_filesystem() {
294 global $wp_filesystem;
295 if ( ! empty( $wp_filesystem ) ) {
296 return true;
297 }
298 if ( ! function_exists( 'WP_Filesystem' ) ) {
299 require_once ABSPATH . 'wp-admin/includes/file.php';
300 }
301 return WP_Filesystem();
302 }
303
304 function wpdbbkp_delete_folder( $folder_path ) {
305 if ( ! is_dir( $folder_path ) ) {
306 return;
307 }
308
309 if ( ! wpdbbkp_init_filesystem() ) {
310 return;
311 }
312
313 global $wp_filesystem;
314
315 $files = array_diff( scandir( $folder_path ), array( '.', '..' ) );
316
317 foreach ( $files as $file ) {
318 $file_path = $folder_path . DIRECTORY_SEPARATOR . $file;
319 if ( is_dir( $file_path ) ) {
320 wpdbbkp_delete_folder( $file_path );
321 } else {
322 set_transient( 'wpdbbkp_track_progress', 'Cleaning : ' . $file_path, 3600 );
323 $wp_filesystem->delete( $file_path );
324 }
325 }
326
327 $wp_filesystem->rmdir( $folder_path );
328 }
329
330
331
332
333 function wpdbbkp_find_sql_file($directory) {
334
335 $files = scandir($directory);
336
337 foreach ($files as $file) {
338
339 if (pathinfo($file, PATHINFO_EXTENSION) === 'sql') {
340
341 return $directory . '/' . $file;
342
343 }
344
345 }
346
347 return false;
348
349 }
350
351
352
353 function wpdbbkp_get_wp_config_table_prefix() {
354
355 $config_file = ABSPATH . 'wp-config.php';
356 wpdbbkp_log("[ERROR] Config Path: $config_file");
357 if (!file_exists($config_file)) {
358
359 return 'wp_';
360
361 }
362
363 $config_contents = file_get_contents($config_file);
364
365 if (preg_match("/\$table_prefix\s*=\s*'([^']+)'/", $config_contents, $matches)) {
366
367 return $matches[1];
368
369 }
370
371 return 'wp_';
372
373 }
374 function wpdbbkp_update_sql_table_prefix($sql_file) {
375 if (!file_exists($sql_file)) {
376 die( esc_html( sprintf(
377 /* translators: %s: path to the SQL file */
378 __( 'ERROR: SQL file not found: %s', 'wpdbbkp' ),
379 $sql_file
380 ) ) );
381 }
382
383 $sql_content = file_get_contents($sql_file);
384
385 // Find wp-config.php
386 $wp_config_path = wpdbbkp_find_wp_config();
387 if (!$wp_config_path) {
388 die(esc_html__("❌ ERROR: wp-config.php not found!\n", 'wpdbbkp'));
389 }
390
391 // Extract table prefix from wp-config.php
392 $config_content = file_get_contents($wp_config_path);
393 if (preg_match("/\\\$table_prefix\s*=\s*['\"]([^'\"]+)['\"]\s*;/", $config_content, $matches)) {
394 $new_prefix = $matches[1];
395 echo esc_html( sprintf(
396 /* translators: %s: table prefix from wp-config.php */
397 __( 'SUCCESS: Extracted new prefix from wp-config.php: %s', 'wpdbbkp' ),
398 $new_prefix
399 ) ) . "\n";
400 } else {
401 die(esc_html__("❌ ERROR: Could not extract table prefix from wp-config.php!\n", 'wpdbbkp'));
402 }
403
404 // Detect old prefix from the SQL file
405 if (preg_match("/(CREATE TABLE|INSERT INTO|ALTER TABLE|UPDATE|DELETE FROM)\s+[`']?([a-zA-Z0-9]+?_)/i", $sql_content, $matches)) {
406 $old_prefix = $matches[2];
407 echo esc_html( sprintf(
408 /* translators: %s: table prefix found in the SQL file */
409 __( 'SUCCESS: Found old prefix in SQL file: %s', 'wpdbbkp' ),
410 $old_prefix
411 ) ) . "\n";
412 } else {
413 die(esc_html__("❌ ERROR: No table prefix found in the SQL file!\n", 'wpdbbkp'));
414 }
415
416 // Prevent replacing if old prefix is same as new prefix
417 if ($old_prefix === $new_prefix) {
418 die( esc_html( sprintf(
419 /* translators: %s: table prefix */
420 __( 'INFO: Old and new prefixes are the same (%s), no changes needed.', 'wpdbbkp' ),
421 $old_prefix
422 ) ) );
423 }
424
425 // Replace all occurrences of the old prefix with the new one
426 $sql_content = preg_replace("/\b" . preg_quote($old_prefix, '/') . "/i", $new_prefix, $sql_content);
427
428 // Write the updated content back to the SQL file
429 file_put_contents($sql_file, $sql_content);
430
431 echo esc_html( sprintf(
432 /* translators: 1: old table prefix, 2: new table prefix, 3: path to SQL file */
433 __( 'SUCCESS: Table prefix updated from %1$s to %2$s in %3$s!', 'wpdbbkp' ),
434 $old_prefix,
435 $new_prefix,
436 $sql_file
437 ) ) . "\n";
438 }
439
440 /**
441 * Finds the root wp-config.php file by checking parent directories.
442 */
443 function wpdbbkp_find_wp_config() {
444 $dir = __DIR__;
445
446 while ($dir !== dirname($dir)) { // Keep going up until the root
447 $config_path = $dir . '/wp-config.php';
448 if (file_exists($config_path)) {
449 return $config_path;
450 }
451 $dir = dirname($dir);
452 }
453
454 return false; // wp-config.php not found
455 }
456
457
458
459
460
461
462
463
464 function wpdbbkp_move_extracted_files( $source, $destination, $ignore = array() ) {
465 if ( ! file_exists( $source ) || ! wpdbbkp_init_filesystem() ) {
466 return;
467 }
468
469 global $wp_filesystem;
470
471 wp_mkdir_p( $destination );
472
473 $files = array_diff( scandir( $source ), array( '.', '..' ) );
474 foreach ( $files as $file ) {
475 $src_file = rtrim( $source, '/' ) . '/' . $file;
476 $dest_file = rtrim( $destination, '/' ) . '/' . $file;
477
478 if ( in_array( $file, $ignore, true ) ) {
479 continue;
480 }
481
482 set_transient( 'wpdbbkp_track_progress', 'Copying: ' . $dest_file, 3600 );
483
484 if ( is_dir( $src_file ) ) {
485 if ( $wp_filesystem->exists( $dest_file ) ) {
486 wpdbbkp_delete_directory( $dest_file );
487 }
488 $wp_filesystem->move( $src_file, $dest_file, true );
489 } else {
490 if ( $wp_filesystem->exists( $dest_file ) ) {
491 $wp_filesystem->delete( $dest_file );
492 }
493 $wp_filesystem->move( $src_file, $dest_file, true );
494 }
495 }
496 }
497
498 function wpdbbkp_delete_directory( $dir ) {
499 if ( ! wpdbbkp_init_filesystem() ) {
500 return false;
501 }
502
503 global $wp_filesystem;
504
505 if ( ! $wp_filesystem->exists( $dir ) ) {
506 return false;
507 }
508
509 if ( ! $wp_filesystem->is_dir( $dir ) ) {
510 return $wp_filesystem->delete( $dir );
511 }
512
513 $files = array_diff( scandir( $dir ), array( '.', '..' ) );
514 foreach ( $files as $file ) {
515 $file_path = $dir . DIRECTORY_SEPARATOR . $file;
516 if ( $wp_filesystem->is_dir( $file_path ) ) {
517 wpdbbkp_delete_directory( $file_path );
518 } else {
519 $wp_filesystem->delete( $file_path );
520 }
521 }
522
523 return $wp_filesystem->rmdir( $dir );
524 }
525
526
527 function wpdbbkp_restore_database($sql_file) {
528
529 global $wpdb;
530
531 $new_site_url = get_site_url();
532
533
534
535 if (!file_exists($sql_file)) {
536
537 wpdbbkp_log("[ERROR] SQL file not found: $sql_file");
538
539 return false;
540
541 }
542
543
544
545 $sql_content = file_get_contents($sql_file);
546
547 if (!$sql_content) {
548
549 wpdbbkp_log("[ERROR] Failed to read SQL file: $sql_file");
550
551 return false;
552
553 }
554
555
556
557 // phpcs:disable WordPress.DB.DirectDatabaseQuery -- Direct $wpdb access required for SQL backup restore.
558 // Fetch old site URL from wp_options before updating
559
560 $old_site_url = $wpdb->get_var("SELECT option_value FROM {$wpdb->options} WHERE option_name = 'siteurl'");
561
562
563
564 if (!$old_site_url) {
565
566 wpdbbkp_log("[ERROR] Failed to fetch old site URL.");
567
568 return false;
569
570 }
571
572
573
574 wpdbbkp_log("[INFO] Old Site URL detected: $old_site_url");
575
576
577
578 // Split SQL statements properly
579
580 $queries = preg_split('/;\s*\n/', $sql_content, -1, PREG_SPLIT_NO_EMPTY);
581
582
583
584 // Tables to exclude (e.g., Users and Usermeta for security reasons)
585
586 $excluded_tables = ['users','usermeta','options'];
587
588
589
590 $wpdb->query('SET foreign_key_checks = 0'); // Disable FK checks
591
592 $wpdb->query('START TRANSACTION'); // Start transaction
593
594
595
596 try {
597
598 foreach ($queries as $query) {
599
600 $query = trim($query);
601
602 if (empty($query)) continue;
603
604 $query = str_replace('options','option_tmp',$query);
605
606 // Check for CREATE TABLE and extract table name
607
608 if (preg_match('/CREATE TABLE `([^`]*)`/', $query, $matches)) {
609
610 $table_name = $matches[1];
611
612
613
614 // Skip excluded tables
615
616 foreach ($excluded_tables as $excluded) {
617
618 if (strpos($table_name, $excluded) !== false) {
619
620 wpdbbkp_log("[SKIPPED] Table excluded: $table_name");
621
622 continue 2;
623
624 }
625
626 }
627
628
629
630 // Drop existing table before restoring
631
632 $wpdb->query( 'DROP TABLE IF EXISTS `' . esc_sql( $table_name ) . '`' );
633
634 wpdbbkp_log("[DROPPED] Table: $table_name");
635
636 }
637
638
639
640 // Skip execution of queries for excluded tables
641
642 foreach ($excluded_tables as $excluded) {
643
644 if (strpos($query, $excluded) !== false) {
645
646 wpdbbkp_log("[SKIPPED] Query contains excluded table reference.");
647
648 continue 2;
649
650 }
651
652 }
653
654
655
656 // Execute the query (dynamic SQL from backup; prepare cannot cover arbitrary statements).
657
658 // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Restoring statements from trusted backup file.
659 $result = $wpdb->query( $query );
660
661 if ($result === false) {
662
663 throw new Exception("[ERROR] Failed to execute query: " . $wpdb->last_error);
664
665 }
666
667 }
668
669
670
671
672
673
674
675 // **Find and replace old URLs in all tables**
676
677 $tables = $wpdb->get_results("SHOW TABLES", ARRAY_N);
678
679 foreach ($tables as $table) {
680
681 $table_name = esc_sql( $table[0] );
682
683
684
685 // Skip excluded tables
686
687 foreach ($excluded_tables as $excluded) {
688
689 if (strpos($table_name, $excluded) !== false) {
690
691 wpdbbkp_log("[SKIPPED] URL Replacement in: $table_name");
692
693 continue 2;
694
695 }
696
697 }
698
699
700
701 // Get all columns for the table
702
703 // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Table name escaped with esc_sql().
704 $columns = $wpdb->get_results( "SHOW COLUMNS FROM `{$table_name}`", ARRAY_A );
705
706 foreach ($columns as $column) {
707
708 $column_name = esc_sql( $column['Field'] );
709
710
711
712 // Update all occurrences of old URL in text-based columns.
713 $wpdb->query(
714 $wpdb->prepare(
715 "UPDATE `{$table_name}` SET `{$column_name}` = REPLACE(`{$column_name}`, %s, %s) WHERE `{$column_name}` LIKE %s", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Table and column identifiers escaped with esc_sql().
716 $old_site_url,
717 $new_site_url,
718 '%' . $wpdb->esc_like( $old_site_url ) . '%'
719 )
720 );
721
722 }
723
724 }
725
726
727
728 $blogname = $wpdb->get_var("SELECT option_value FROM {$wpdb->prefix}option_tmp WHERE option_name = 'blogname'");
729
730 if ($blogname) {
731
732 $wpdb->query(
733
734 $wpdb->prepare(
735
736 "UPDATE {$wpdb->prefix}options SET option_value = %s WHERE option_name = 'blogname'",
737
738 $blogname
739
740 )
741
742 );
743
744 wpdbbkp_log("[SUCCESS] Updated wp_options with blogname from option_tmp");
745
746 }
747 $active_plugins = $wpdb->get_var("SELECT option_value FROM {$wpdb->prefix}option_tmp WHERE option_name = 'active_plugins'");
748
749 if ($active_plugins) {
750
751 $wpdb->query(
752
753 $wpdb->prepare(
754
755 "UPDATE {$wpdb->prefix}options SET option_value = %s WHERE option_name = 'active_plugins'",
756
757 $active_plugins
758
759 )
760
761 );
762
763 wpdbbkp_log("[SUCCESS] Updated wp_options with active_plugins from option_tmp");
764
765 }
766
767 $wpdb->query('COMMIT'); // Commit transaction
768
769 wpdbbkp_log("[SUCCESS] Database restoration and URL replacement completed from: $sql_file");
770
771 return true;
772
773
774
775 } catch (Exception $e) {
776
777 $wpdb->query('ROLLBACK'); // Rollback in case of an error
778
779 wpdbbkp_log($e->getMessage());
780
781 return false;
782
783 } finally {
784
785 $wpdb->query('SET foreign_key_checks = 1'); // Re-enable FK checks
786
787 }
788
789 // phpcs:enable WordPress.DB.DirectDatabaseQuery
790
791 }
792 function bkpforwp_anonimize_database($value, $table, $column)
793 {
794 $enable_anonymization = get_option('bkpforwp_enable_anonymization', false);
795 $anonymization_type = get_option('bkpforwp_anonymization_type', false);
796 $enable_backup_encryption = get_option('bkpforwp_enable_backup_encryption', false);
797 $anonymization_pass = get_option('bkpforwp_anonymization_pass', '');
798
799
800 if (isset($enable_anonymization) && $enable_anonymization == 1) {
801 global $wpdb;
802 $bkpforwp_process_table = array($wpdb->prefix . 'options', $wpdb->prefix . 'users', $wpdb->prefix . 'usermeta', $wpdb->prefix . 'wc_customer_lookup', $wpdb->prefix . 'edd_customers', $wpdb->prefix . 'edd_customermeta');
803 $bkpforwp_process_cols = array('mailserver_pass', 'mailserver_login', 'user_email', 'email', 'user_url', 'nickname', 'name', 'twitter', 'facebook', 'instagram', 'phone', 'mobile', 'address', 'city', 'zip', 'pincode', 'user_login', 'postcode', 'state', 'user_ip', 'ip_address');
804
805 //Masking Logic
806 if (isset($anonymization_type) && $anonymization_type == 'masked_data') {
807 if (in_array($table, $bkpforwp_process_table)) {
808 $check_str = implode(',', $bkpforwp_process_cols);
809 if (stripos($check_str, $column) !== false) {
810 return str_replace($value, str_repeat('*', strlen($value)), $value);
811 }
812 }
813 }
814 //FakeData Logic
815
816 if (isset($anonymization_type) && $anonymization_type == 'fake_data') {
817 if (function_exists('wp_privacy_anonymize_data')) {
818 $bkpforwp_process_email = implode(',', array('email', 'user_email'));
819 $bkpforwp_process_url = implode(',', array('url', 'user_url', 'twitter', 'facebook', 'instagram'));
820 $bkpforwp_process_ip = implode(',', array('user_ip', 'ip_address'));
821 $bkpforwp_process_text = implode(',', array('nickname', 'name', 'address', 'phone', 'mobile', 'city', 'zip', 'pincode', 'user_login', 'postcode', 'state'));
822
823 if (in_array($table, $bkpforwp_process_table)) {
824
825 //For email
826 if (stripos($bkpforwp_process_email, $column) !== false) {
827 return str_replace($value, wp_privacy_anonymize_data('email', $value), $value);
828 }
829
830 if (stripos($bkpforwp_process_url, $column) !== false) {
831 return str_replace($value, wp_privacy_anonymize_data('url', $value), $value);
832 }
833
834 if (stripos($bkpforwp_process_ip, $column) !== false) {
835 return str_replace($value, wp_privacy_anonymize_data('ip', $value), $value);
836 }
837
838 if (stripos($bkpforwp_process_text, $column) !== false) {
839 return str_replace($value, wp_privacy_anonymize_data('text', $value), $value);
840 }
841
842 }
843
844 return $value;
845
846 } else {
847 if (in_array($table, $bkpforwp_process_table)) {
848 $check_str = implode(',', $bkpforwp_process_cols);
849 if (stripos($check_str, $column) !== false) {
850 return str_replace($value, str_repeat('*', strlen($value)), $value);
851 }
852 }
853 }
854
855 }
856
857 if (isset($anonymization_type) && $anonymization_type == 'encrypted_data' && !empty($anonymization_pass)) {
858 require_once 'class-symmetric-encryption.php';
859
860 if (in_array($table, $bkpforwp_process_table)) {
861 $check_str = implode(',', $bkpforwp_process_cols);
862 if (stripos($check_str, $column) !== false) {
863 $enc_pass = $anonymization_pass;
864 $encryption = new WPDBBackupSymmetricEncryption();
865 return str_replace($value, '<==>' . $encryption->encrypt($value, $enc_pass, $enc_pass) . '<==>', $value);
866 }
867
868 }
869
870 }
871
872 }
873 return $value;
874 }
875
876 add_filter('wpdbbkp_sql_query_restore', 'bkpforwp_sql_query_restore', 1);
877 function bkpforwp_sql_query_restore($sql_query)
878 {
879 $anonymization_type = get_option('bkpforwp_anonymization_type', false);
880 $anonymization_pass = get_option('bkpforwp_anonymization_pass', '');
881 if (isset($anonymization_type) && $anonymization_type == 'encrypted_data' && !empty($anonymization_pass)) {
882
883 $pattern = '/<==>(.*?)<==>/i';
884 return preg_replace_callback($pattern, 'bkpforwp_sql_restore_replace', $sql_query);
885 }
886 return $sql_query;
887 }
888
889 function bkpforwp_sql_restore_replace($matches)
890 {
891 $anonymization_pass = get_option('bkpforwp_anonymization_pass', '');
892 $enc_pass = isset($anonymization_pass) ? $anonymization_pass : false;
893 if ($enc_pass) {
894 require_once 'class-symmetric-encryption.php';
895 $encryption = new WPDBBackupSymmetricEncryption();
896 return $encryption->decrypt($matches[0], $enc_pass, $enc_pass);
897 }
898 return $matches[0];
899 }
900
901 add_action('wpdbbkp_database_backup_options', 'bkpforwp_database_backup_options');
902 function bkpforwp_database_backup_options()
903 {
904 $settings = get_option('wp_db_backup_options');
905 $autobackup_days = isset($settings['autobackup_days']) ? implode(',', $settings['autobackup_days']) : ',';
906 $autobackup_time = isset($settings['autobackup_time']) ? $settings['autobackup_time'] : '';
907 $autobackup_date = isset($settings['autobackup_date']) ? $settings['autobackup_date'] : '';
908 ?>
909
910
911 <div class="row form-group autobackup_frequency_pro" style="display:none"><label
912 class="col-sm-12 autobackup_daily_pro">We will automatically backup at 00:00 AM daily. <b><a
913 href="javascript:modify_backup_frequency();">Change Back Frequency Timings</a></b></label></div>
914 <div class="row form-group autobackup_frequency_pro" style="display:none"><label
915 class="col-sm-12 autobackup_weekly_pro">We will automatically backup every Sunday on weekly basis. <b><a
916 href="javascript:modify_backup_frequency();">Change Back Frequency Timings</a></b></label></div>
917 <div class="row form-group autobackup_frequency_pro" style="display:none"><label
918 class="col-sm-12 autobackup_monthly_pro">We will automatically backup on 1st on Monday on monthly basis. <b><a
919 href="javascript:modify_backup_frequency();">Change Back Frequency Timings</a></b></label></div>
920
921
922 <div class="row form-group autobackup_days database_autobackup" style="display:none">
923 <label class="col-sm-3" for="autobackup_days"><?php esc_html_e('Database Backup Days', 'wpdbbkp'); ?></label>
924 <div class="col-sm-9">
925 <select id="autobackup_days" class="form-control bkpforwp_multiselect"
926 name="wp_db_backup_options[autobackup_days][]" multiple>
927 <option value="Mon" <?php if (strpos($autobackup_days, 'Mon') !== false) {
928 echo 'selected';
929 } ?>>
930 <?php esc_html_e('Monday', 'wpdbbkp'); ?></option>
931 <option value="Tue" <?php if (strpos($autobackup_days, 'Tue') !== false) {
932 echo 'selected';
933 } ?>>
934 <?php esc_html_e('Tuesday', 'wpdbbkp'); ?></option>
935 <option value="Wed" <?php if (strpos($autobackup_days, 'Wed') !== false) {
936 echo 'selected';
937 } ?>>
938 <?php esc_html_e('Wednesday', 'wpdbbkp'); ?></option>
939 <option value="Thu" <?php if (strpos($autobackup_days, 'Thu') !== false) {
940 echo 'selected';
941 } ?>>
942 <?php esc_html_e('Thursday', 'wpdbbkp'); ?></option>
943 <option value="Fri" <?php if (strpos($autobackup_days, 'Fri') !== false) {
944 echo 'selected';
945 } ?>>
946 <?php esc_html_e('Friday', 'wpdbbkp'); ?></option>
947 <option value="Sat" <?php if (strpos($autobackup_days, 'Sat') !== false) {
948 echo 'selected';
949 } ?>>
950 <?php esc_html_e('Saturday', 'wpdbbkp'); ?></option>
951 <option value="Sun" <?php if (strpos($autobackup_days, 'Sun') !== false) {
952 echo 'selected';
953 } ?>>
954 <?php esc_html_e('Sunday', 'wpdbbkp'); ?></option>
955 </select>
956 </div>
957 </div>
958 <div class="row form-group autobackup_date database_autobackup" style="display:none">
959 <label class="col-sm-3" for="autobackup_date"><?php esc_html_e('Database Backup Date', 'wpdbbkp'); ?></label>
960 <div class="col-sm-9">
961 <input type="date" id="autobackup_date" value="<?php echo esc_attr($autobackup_date); ?>"
962 class="form-control bkpforwp_multiselect" name="wp_db_backup_options[autobackup_date]">
963 </div>
964 </div>
965 <div class="row form-group autobackup_time database_autobackup" style="display:none">
966 <label class="col-sm-3" for="autobackup_time"><?php esc_html_e('Database Backup Time', 'wpdbbkp'); ?></label>
967 <div class="col-sm-9">
968 <input type="time" id="autobackup_time" value="<?php echo esc_attr($autobackup_time); ?>"
969 class="form-control bkpforwp_multiselect" name="wp_db_backup_options[autobackup_time]">
970 </div>
971 </div>
972
973 <?php
974 }
975
976 add_action('wpdbbkp_full_backup_options', 'bkpforwp_full_backup_options');
977 function bkpforwp_full_backup_options()
978 {
979
980 $settings = get_option('wp_db_backup_options');
981 $autobackup_days = isset($settings['autobackup_full_days']) ? implode(',', $settings['autobackup_full_days']) : ',';
982 $autobackup_time = isset($settings['autobackup_full_time']) ? $settings['autobackup_full_time'] : '';
983 $autobackup_date = isset($settings['autobackup_full_date']) ? $settings['autobackup_full_date'] : '';
984 $autobackup_date = isset($settings['autobackup_full_date']) ? $settings['autobackup_full_date'] : '';
985 $senable_exact_backup_time = get_option('bkpforwp_enable_exact_backup_time', false);
986 if ($senable_exact_backup_time) {
987 ?>
988 <div class="row form-group autobackup_full_days full_autobackup" style="display:none">
989 <label class="col-sm-3" for="autobackup_full_days"><?php esc_html_e('Full Backup Days', 'wpdbbkp'); ?></label>
990 <div class="col-sm-9">
991 <select id="autobackup_full_days" class="form-control bkpforwp_multiselect"
992 name="wp_db_backup_options[autobackup_full_days][]" multiple>
993 <option value="Mon" <?php if (strpos($autobackup_days, 'Mon') !== false) {
994 echo 'selected';
995 } ?>>
996 <?php esc_html_e('Monday', 'wpdbbkp'); ?></option>
997 <option value="Tue" <?php if (strpos($autobackup_days, 'Tue') !== false) {
998 echo 'selected';
999 } ?>>
1000 <?php esc_html_e('Tuesday', 'wpdbbkp'); ?></option>
1001 <option value="Wed" <?php if (strpos($autobackup_days, 'Wed') !== false) {
1002 echo 'selected';
1003 } ?>>
1004 <?php esc_html_e('Wednesday', 'wpdbbkp'); ?></option>
1005 <option value="Thu" <?php if (strpos($autobackup_days, 'Thu') !== false) {
1006 echo 'selected';
1007 } ?>>
1008 <?php esc_html_e('Thursday', 'wpdbbkp'); ?></option>
1009 <option value="Fri" <?php if (strpos($autobackup_days, 'Fri') !== false) {
1010 echo 'selected';
1011 } ?>>
1012 <?php esc_html_e('Friday', 'wpdbbkp'); ?></option>
1013 <option value="Sat" <?php if (strpos($autobackup_days, 'Sat') !== false) {
1014 echo 'selected';
1015 } ?>>
1016 <?php esc_html_e('Saturday', 'wpdbbkp'); ?></option>
1017 <option value="Sun" <?php if (strpos($autobackup_days, 'Sun') !== false) {
1018 echo 'selected';
1019 } ?>>
1020 <?php esc_html_e('Sunday', 'wpdbbkp'); ?></option>
1021 </select>
1022 </div>
1023 </div>
1024 <div class="row form-group autobackup_full_date full_autobackup" style="display:none">
1025 <label class="col-sm-3" for="autobackup_full_date"><?php esc_html_e('Full Backup Date', 'wpdbbkp'); ?></label>
1026 <div class="col-sm-9">
1027 <input type="date" id="autobackup_full_date" value="<?php echo esc_attr($autobackup_date); ?>" class="form-control"
1028 name="wp_db_backup_options[autobackup_full_date]">
1029 </div>
1030 </div>
1031 <div class="row form-group autobackup_full_time full_autobackup" style="display:none">
1032 <label class="col-sm-3" for="autobackup_full_time"><?php esc_html_e('Full Backup Time', 'wpdbbkp'); ?></label>
1033 <div class="col-sm-9">
1034 <input type="time" id="autobackup_full_time" value="<?php echo esc_attr($autobackup_time); ?>" class="form-control"
1035 name="wp_db_backup_options[autobackup_full_time]">
1036 </div>
1037 </div>
1038 <?php
1039 }
1040 }
1041
1042 add_filter('wpdbbkp_fullback_cron_condition', 'bkpforwp_fullback_cron_condition');
1043 function bkpforwp_fullback_cron_condition($value)
1044 {
1045 $options_settings = get_option('wp_db_backup_options', false);
1046
1047 $senable_exact_backup_time = get_option('bkpforwp_enable_exact_backup_time', false);
1048 if (!$senable_exact_backup_time) {
1049 return $value;
1050 }
1051 if (wp_doing_cron() && $options_settings && isset($options_settings['enable_autobackups']) && $options_settings['enable_autobackups'] == 1 && isset($options_settings['full_autobackup_frequency'])) {
1052 if ($options_settings['full_autobackup_frequency'] == 'daily' && isset($options_settings['autobackup_full_time']) && $options_settings['autobackup_full_time']) {
1053 if ($options_settings['autobackup_full_time'] < gmdate("H:i") || $options_settings['autobackup_full_time'] > gmdate("H:i", strtotime('+30 minutes', gmdate("H:i")))) {
1054 $value = false;
1055 }
1056 }
1057 if ($options_settings['full_autobackup_frequency'] == 'weekly' && isset($options_settings['autobackup_full_time']) && $options_settings['autobackup_full_time'] && isset($options_settings['autobackup_full_days'])) {
1058 $current_day = gmdate('M');
1059 $current_time = gmdate('H:i');
1060 $allowed_days = $options_settings['autobackup_full_days'];
1061 if (!in_array($current_day, $allowed_days) || ($options_settings['autobackup_full_time'] < $current_time) || $options_settings['autobackup_full_time'] > gmdate("H:i", strtotime('+30 minutes', $current_time))) {
1062 $value = false;
1063 }
1064 }
1065 if ($options_settings['full_autobackup_frequency'] == 'monthly' && isset($options_settings['autobackup_full_time']) && $options_settings['autobackup_full_time'] && isset($options_settings['autobackup_full_date'])) {
1066 $current_date = gmdate('d');
1067 $current_time = gmdate('H:i');
1068 $allowed_date = gmdate('d', strtotime($options_settings['autobackup_full_date']));
1069 if (($allowed_date != $current_date) || ($options_settings['autobackup_full_time'] < $current_time || $options_settings['autobackup_full_time'] > gmdate("H:i", strtotime('+30 minutes', $current_time)))) {
1070 $value = false;
1071 }
1072 }
1073 }
1074 return $value;
1075 }
1076
1077 add_filter('wpdbbkp_dbback_cron_condition', 'bkpforwp_dbback_cron_condition');
1078 function bkpforwp_dbback_cron_condition($value)
1079 {
1080 $options_settings = get_option('wp_db_backup_options', false);
1081 if (wp_doing_cron() && $options_settings && isset($options_settings['enable_autobackups']) && $options_settings['enable_autobackups'] == 1 && isset($options_settings['autobackup_frequency'])) {
1082 if ($options_settings['autobackup_frequency'] == 'daily' && isset($options_settings['autobackup_time'])) {
1083 if ($options_settings['autobackup_time'] < gmdate("H:i") || $options_settings['autobackup_time'] > gmdate("H:i", strtotime('+30 minutes', gmdate("H:i")))) {
1084 $value = false;
1085 }
1086 }
1087 if ($options_settings['autobackup_frequency'] == 'weekly' && isset($options_settings['autobackup_time']) && isset($options_settings['autobackup_days'])) {
1088 $current_day = gmdate('M');
1089 $current_time = gmdate('H:i');
1090 $allowed_days = $options_settings['autobackup_days'];
1091 if (!in_array($current_day, $allowed_days) || ($options_settings['autobackup_time'] < $current_time || $options_settings['autobackup_time'] > gmdate("H:i", strtotime('+30 minutes', $current_time)))) {
1092 $value = false;
1093 }
1094 }
1095 if ($options_settings['autobackup_frequency'] == 'monthly' && isset($options_settings['autobackup_time']) && isset($options_settings['autobackup_date'])) {
1096 $current_date = gmdate('d');
1097 $current_time = gmdate('H:i');
1098 $allowed_date = gmdate('d', strtotime($options_settings['autobackup_date']));
1099 if (($allowed_date != $current_date) || ($options_settings['autobackup_time'] < $current_time || $options_settings['autobackup_time'] > gmdate("H:i", strtotime('+30 minutes', $current_time)))) {
1100 $value = false;
1101 }
1102 }
1103 }
1104 return $value;
1105 }
1106
1107 add_filter('wpdbbkp_dbback_cron_frequency', 'bkpforwp_dbback_cron_frequency');
1108
1109 function bkpforwp_dbback_cron_frequency($value)
1110 {
1111 if (wp_doing_cron()) {
1112 $options = get_option('wp_db_backup_options');
1113 if (isset($options['autobackup_full_time']) && !empty($options['autobackup_full_time'])) {
1114 $value = 'thirty_minutes';
1115 }
1116 }
1117 return $value;
1118 }
1119
1120 /**
1121 * Protect temp directory from PHP execution.
1122 *
1123 * @param string $temp_dir Path to temp directory.
1124 */
1125 function wpdbbkp_protect_temp_directory( $temp_dir ) {
1126 $htaccess_file = trailingslashit( $temp_dir ) . '.htaccess';
1127
1128 if ( file_exists( $htaccess_file ) ) {
1129 return;
1130 }
1131
1132 $htaccess_content = "# Disable PHP execution and public access\n";
1133 $htaccess_content .= "<IfModule mod_php5.c>\n";
1134 $htaccess_content .= " php_flag engine off\n";
1135 $htaccess_content .= "</IfModule>\n";
1136 $htaccess_content .= "<IfModule mod_php7.c>\n";
1137 $htaccess_content .= " php_flag engine off\n";
1138 $htaccess_content .= "</IfModule>\n";
1139 $htaccess_content .= "<IfModule mod_php8.c>\n";
1140 $htaccess_content .= " php_flag engine off\n";
1141 $htaccess_content .= "</IfModule>\n";
1142 $htaccess_content .= "<FilesMatch \"\\.(?i:php|phtml|php3|php4|php5|php7|phps|cgi|pl|exe)$\">\n";
1143 $htaccess_content .= " <IfModule mod_authz_core.c>\n";
1144 $htaccess_content .= " Require all denied\n";
1145 $htaccess_content .= " </IfModule>\n";
1146 $htaccess_content .= " <IfModule !mod_authz_core.c>\n";
1147 $htaccess_content .= " Order allow,deny\n";
1148 $htaccess_content .= " Deny from all\n";
1149 $htaccess_content .= " </IfModule>\n";
1150 $htaccess_content .= "</FilesMatch>\n";
1151
1152 // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_file_put_contents -- Creating .htaccess for security.
1153 file_put_contents( $htaccess_file, $htaccess_content );
1154 }
1155
1156 /**
1157 * Function to force the new .htaccess file to fix the backup folder protection
1158 */
1159 function wpdbbkp_fix_htaccess_on_update()
1160 {
1161 static $wpdbbkp_htaccess_fix = false;
1162
1163 if (!$wpdbbkp_htaccess_fix && version_compare(WPDB_VERSION, '7.4', '>=')) {
1164 $wpdbbkp_htaccess_fix = true;
1165 $option_name = 'wpdbbkp_htaccess_fix';
1166 if (get_option($option_name, false)) {
1167 return; // Exit if already fixed
1168 }
1169
1170 // Initialize WP Filesystem
1171 global $wp_filesystem;
1172
1173 if (!function_exists('WP_Filesystem')) {
1174 require_once ABSPATH . 'wp-admin/includes/file.php';
1175 }
1176
1177 if (!WP_Filesystem()) {
1178 return;
1179 }
1180 // Define the .htaccess content
1181 $htaccess_content = "
1182 # Disable public access to this folder
1183 <IfModule mod_authz_core.c>
1184 Require all denied
1185 </IfModule>
1186
1187 <IfModule !mod_authz_core.c>
1188 Deny from all
1189 </IfModule>
1190 ";
1191
1192 $path_info = wp_upload_dir();
1193 $backup_folder = $path_info['basedir'] . '/' . WPDB_BACKUPS_DIR . '/';
1194 $htaccess_file = trailingslashit($backup_folder) . '.htaccess';
1195
1196 if ($wp_filesystem->exists($htaccess_file)) {
1197 $wp_filesystem->delete($htaccess_file);
1198 }
1199
1200 if (!$wp_filesystem->put_contents($htaccess_file, $htaccess_content, FS_CHMOD_FILE)) {
1201 return;
1202 }
1203 update_option($option_name, time(), false);
1204 }
1205 }
1206
1207 add_action('admin_init', 'wpdbbkp_fix_htaccess_on_update');