Skip to content

Commit 2e8d787

Browse files
authored
Updates
1 parent d648284 commit 2e8d787

File tree

1 file changed

+77
-76
lines changed

1 file changed

+77
-76
lines changed

simple-wp-site-exporter.php

Lines changed: 77 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
<?php
2-
/*
3-
Plugin Name: Simple WP Site Exporter
4-
Description: Exports the site files and database as a zip archive.
5-
Version: 1.8.0
6-
Author: EngineScript
7-
License: GPL v3 or later
8-
Text Domain: Simple-WP-Site-Exporter
9-
*/
2+
/**
3+
* Plugin Name: Simple WP Site Exporter
4+
* Description: Exports the site files and database as a zip archive.
5+
* Version: 1.8.0
6+
* Author: EngineScript
7+
* License: GPL v3 or later
8+
* Text Domain: Simple-WP-Site-Exporter
9+
*/
1010

1111
// Prevent direct access. Note: Using return here instead of exit.
1212
if ( ! defined( 'ABSPATH' ) ) {
@@ -40,6 +40,7 @@ function sse_load_textdomain() {
4040
* or use statements as they are part of WordPress core.
4141
*
4242
* Core classes used:
43+
*
4344
* @see WP_Error - WordPress error handling class
4445
* @see ZipArchive - PHP ZipArchive class
4546
* @see RecursiveIteratorIterator - PHP SPL iterator
@@ -64,15 +65,14 @@ function sse_get_client_ip() {
6465
if ( isset( $_SERVER['REMOTE_ADDR'] ) ) {
6566
$client_ip = sanitize_text_field( wp_unslash( $_SERVER['REMOTE_ADDR'] ) );
6667
}
67-
68+
6869
// Basic IP validation.
6970
if ( filter_var( $client_ip, FILTER_VALIDATE_IP ) !== false ) {
7071
return $client_ip;
7172
}
72-
73-
return 'unknown';
7473

75-
} //end sse_get_client_ip()
74+
return 'unknown';
75+
} // end sse_get_client_ip()
7676

7777

7878
/**
@@ -99,8 +99,7 @@ function sse_store_log_in_database( $message, $level ) {
9999
}
100100

101101
update_option( 'sse_error_logs', $logs );
102-
103-
} //end sse_store_log_in_database()
102+
} // end sse_store_log_in_database()
104103

105104

106105
/**
@@ -114,8 +113,7 @@ function sse_output_log_message( $formatted_message ) {
114113
if ( function_exists( 'wp_debug_log' ) ) {
115114
wp_debug_log( $formatted_message );
116115
}
117-
118-
} //end sse_output_log_message()
116+
} // end sse_output_log_message()
119117

120118

121119
/**
@@ -151,37 +149,35 @@ function sse_log( $message, $level = 'info' ) {
151149
if ( 'error' === $level || 'security' === $level ) {
152150
sse_store_log_in_database( $message, $level );
153151
}
154-
155-
} //end sse_log()
152+
} // end sse_log()
156153

157154
/**
158-
* Safely get the PHP execution time limit
155+
* Safely get the PHP execution time limit.
159156
*
160-
* @return int Current PHP execution time limit in seconds
157+
* @return int Current PHP execution time limit in seconds.
161158
*/
162159
function sse_get_execution_time_limit() {
163-
// Get the current execution time limit
160+
// Get the current execution time limit.
164161
$max_exec_time = ini_get( 'max_execution_time' );
165-
166-
// Handle all possible return types from ini_get()
162+
163+
// Handle all possible return types from ini_get().
167164
if ( false === $max_exec_time ) {
168-
// ini_get failed
165+
// Ini_get failed.
169166
return 30;
170167
}
171-
168+
172169
if ( '' === $max_exec_time ) {
173-
// Empty string returned
170+
// Empty string returned.
174171
return 30;
175172
}
176-
173+
177174
if ( ! is_numeric( $max_exec_time ) ) {
178-
// Non-numeric value returned
175+
// Non-numeric value returned.
179176
return 30;
180177
}
181-
182-
return (int) $max_exec_time;
183178

184-
} //end sse_get_execution_time_limit()
179+
return (int) $max_exec_time;
180+
} // end sse_get_execution_time_limit()
185181

186182
// --- Admin Menu ---
187183
/**
@@ -191,9 +187,9 @@ function sse_get_execution_time_limit() {
191187
*/
192188
function sse_admin_menu() {
193189
add_management_page(
194-
esc_html__( 'Simple WP Site Exporter', 'Simple-WP-Site-Exporter' ), // Escaped title
195-
esc_html__( 'Site Exporter', 'Simple-WP-Site-Exporter' ), // Escaped menu title
196-
'manage_options', // Capability required
190+
esc_html__( 'Simple WP Site Exporter', 'Simple-WP-Site-Exporter' ), // Escaped title.
191+
esc_html__( 'Site Exporter', 'Simple-WP-Site-Exporter' ), // Escaped menu title.
192+
'manage_options', // Capability required.
197193
'simple-wp-site-exporter',
198194
'sse_exporter_page_html'
199195
);
@@ -269,7 +265,7 @@ function sse_handle_export() {
269265
}
270266

271267
sse_prepare_execution_environment();
272-
268+
273269
$export_paths = sse_setup_export_directories();
274270
if ( is_wp_error( $export_paths ) ) {
275271
wp_die( esc_html( $export_paths->get_error_message() ) );
@@ -314,8 +310,7 @@ function sse_validate_export_request() { // phpcs:ignore WordPress.Security.Nonc
314310
}
315311

316312
return true;
317-
318-
} //end sse_validate_export_request()
313+
} // end sse_validate_export_request()
319314

320315

321316
/**
@@ -335,8 +330,7 @@ function sse_prepare_execution_environment() {
335330
}
336331

337332
sse_log( 'Execution time limit appears adequate for export operations', 'info' );
338-
339-
} //end sse_prepare_execution_environment()
333+
} // end sse_prepare_execution_environment()
340334

341335
/**
342336
* Sets up export directories and returns path information.
@@ -352,7 +346,7 @@ function sse_setup_export_directories() {
352346
$export_dir_name = 'simple-wp-site-exporter-exports';
353347
$export_dir = trailingslashit( $upload_dir['basedir'] ) . $export_dir_name;
354348
$export_url = trailingslashit( $upload_dir['baseurl'] ) . $export_dir_name;
355-
349+
356350
wp_mkdir_p( $export_dir );
357351
sse_create_index_file( $export_dir );
358352

@@ -382,7 +376,7 @@ function sse_create_index_file( $export_dir ) {
382376
return;
383377
}
384378
}
385-
379+
386380
if ( $wp_filesystem && $wp_filesystem->is_writable( $export_dir ) ) {
387381
$wp_filesystem->put_contents(
388382
$index_file_path,
@@ -391,7 +385,7 @@ function sse_create_index_file( $export_dir ) {
391385
);
392386
return;
393387
}
394-
388+
395389
sse_log('Failed to write index.php file or directory not writable: ' . $export_dir, 'error');
396390
}
397391

@@ -423,10 +417,10 @@ function sse_export_database( $export_dir ) {
423417
escapeshellarg($db_filepath), // phpcs:ignore WordPressVIPMinimum.Functions.RestrictedFunctions.escapeshellarg_escapeshellarg -- Required for shell command security
424418
escapeshellarg(ABSPATH) // phpcs:ignore WordPressVIPMinimum.Functions.RestrictedFunctions.escapeshellarg_escapeshellarg -- Required for shell command security
425419
);
426-
420+
427421
$output = shell_exec($command . ' 2>&1'); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.system_calls_shell_exec -- Required for WP-CLI database export: all parameters are validated and escaped with escapeshellarg()
428-
429-
422+
423+
430424
if ( ! file_exists( $db_filepath ) || filesize( $db_filepath ) <= 0 ) { // phpcs:ignore WordPressVIPMinimum.Functions.RestrictedFunctions.file_exists_file_exists -- Validating WP-CLI export success
431425
$error_message = ! empty($output) ? trim($output) : 'WP-CLI command failed silently.';
432426
return new WP_Error( 'db_export_failed', $error_message );
@@ -458,12 +452,15 @@ function sse_create_site_archive( $export_paths, $database_file ) {
458452
$zip_filepath = trailingslashit( $export_paths['export_dir'] ) . $zip_filename;
459453

460454
$zip = new ZipArchive();
461-
if ( $zip->open( $zip_filepath, ZipArchive::CREATE | ZipArchive::OVERWRITE ) !== TRUE ) {
462-
return new WP_Error( 'zip_create_failed', sprintf(
463-
/* translators: %s: filename */
464-
__( 'Could not create zip file at %s', 'Simple-WP-Site-Exporter' ),
465-
basename($zip_filepath) // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.system_calls_basename -- Safe usage: $zip_filepath is constructed from controlled inputs (WordPress upload dir + sanitized filename), not user input
466-
) );
455+
if ( $zip->open( $zip_filepath, ZipArchive::CREATE | ZipArchive::OVERWRITE ) !== true ) {
456+
return new WP_Error(
457+
'zip_create_failed',
458+
sprintf(
459+
/* translators: %s: filename */
460+
__( 'Could not create zip file at %s', 'Simple-WP-Site-Exporter' ),
461+
basename( $zip_filepath ) // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.system_calls_basename -- Safe usage: $zip_filepath is constructed from controlled inputs (WordPress upload dir + sanitized filename), not user input.
462+
)
463+
);
467464
}
468465

469466
// Add database dump to zip
@@ -479,7 +476,7 @@ function sse_create_site_archive( $export_paths, $database_file ) {
479476
}
480477

481478
$zip_close_status = $zip->close();
482-
479+
483480
if ( ! $zip_close_status || ! file_exists( $zip_filepath ) ) {
484481
return new WP_Error( 'zip_finalize_failed', __( 'Failed to finalize or save the zip archive after processing files.', 'Simple-WP-Site-Exporter' ) );
485482
}
@@ -515,11 +512,14 @@ function sse_add_wordpress_files_to_zip( $zip, $export_dir ) {
515512
sse_process_file_for_zip( $zip, $file_info, $source_path, $export_dir );
516513
}
517514
} catch ( Exception $e ) {
518-
return new WP_Error( 'file_iteration_failed', sprintf(
519-
/* translators: %s: error message */
520-
__( 'Error during file processing: %s', 'Simple-WP-Site-Exporter' ),
521-
$e->getMessage()
522-
) );
515+
return new WP_Error(
516+
'file_iteration_failed',
517+
sprintf(
518+
/* translators: %s: error message */
519+
__( 'Error during file processing: %s', 'Simple-WP-Site-Exporter' ),
520+
$e->getMessage()
521+
)
522+
);
523523
}
524524

525525
return true;
@@ -581,21 +581,21 @@ function sse_add_file_to_zip( $zip, $file_info, $file, $pathname, $relative_path
581581
}
582582
return true;
583583
}
584-
584+
585585
if ( $file_info->isFile() ) {
586586
// Use real path (getRealPath() must succeed for security)
587587
if ( false === $file ) {
588588
sse_log( "Skipping file with unresolvable real path: " . $pathname, 'warning' );
589589
return true; // Skip this file but continue processing
590590
}
591-
591+
592592
$file_to_add = $file;
593-
593+
594594
if ( ! $zip->addFile( $file_to_add, $relative_path ) ) {
595595
sse_log( "Failed to add file to zip: " . $relative_path . " (Source: " . $file_to_add . ")", 'error' );
596596
}
597597
}
598-
598+
599599
return true;
600600
}
601601

@@ -656,17 +656,17 @@ function sse_show_success_notice( $zip_result ) {
656656
),
657657
admin_url()
658658
);
659-
659+
660660
$delete_url = add_query_arg(
661661
array(
662662
'sse_delete_export' => $zip_result['filename'],
663663
'sse_delete_nonce' => wp_create_nonce('sse_delete_export')
664664
),
665665
admin_url()
666666
);
667-
667+
668668
$display_zip_path = str_replace( ABSPATH, '[wp-root]/', $zip_result['filepath'] );
669-
$display_zip_path = preg_replace('|/+|', '/', $display_zip_path);
669+
$display_zip_path = preg_replace( '|/+|', '/', $display_zip_path );
670670
?>
671671
<div class="notice notice-success is-dismissible">
672672
<p>
@@ -681,11 +681,12 @@ function sse_show_success_notice( $zip_result ) {
681681
<p><small><?php
682682
printf(
683683
/* translators: %s: file path */
684-
esc_html__('File location: %s', 'Simple-WP-Site-Exporter'),
685-
'<code title="' . esc_attr__('Path is relative to WordPress root directory', 'Simple-WP-Site-Exporter') . '">' .
686-
esc_html($display_zip_path) . '</code>'
684+
esc_html__( 'File location: %s', 'Simple-WP-Site-Exporter' ),
685+
'<code title="' . esc_attr__( 'Path is relative to WordPress root directory', 'Simple-WP-Site-Exporter' ) . '">' .
686+
esc_html( $display_zip_path ) . '</code>'
687687
);
688-
?></small></p>
688+
?>
689+
</small></p>
689690
</div>
690691
<?php
691692
});
@@ -722,14 +723,14 @@ function sse_schedule_export_cleanup( $zip_filepath ) {
722723
function sse_delete_export_file_handler( $file ) {
723724
// Validate that this is actually an export file before deletion
724725
$filename = basename( $file );
725-
726+
726727
// Use the same validation as manual deletion for consistency
727728
$validation = sse_validate_export_file_for_deletion( $filename );
728729
if ( is_wp_error( $validation ) ) {
729730
sse_log( 'Scheduled deletion blocked - invalid file: ' . $file . ' - ' . $validation->get_error_message(), 'warning' );
730731
return;
731732
}
732-
733+
733734
if ( file_exists( $file ) ) {
734735
if ( sse_safely_delete_file( $file ) ) {
735736
sse_log( 'Scheduled deletion successful: ' . $file, 'info' );
@@ -748,7 +749,7 @@ function sse_delete_export_file_handler( $file ) {
748749
*/
749750
function sse_safely_delete_file( $filepath ) {
750751
global $wp_filesystem;
751-
752+
752753
// Initialize the WordPress filesystem
753754
if (empty($wp_filesystem)) {
754755
require_once ABSPATH . 'wp-admin/includes/file.php';
@@ -757,18 +758,18 @@ function sse_safely_delete_file( $filepath ) {
757758
return false;
758759
}
759760
}
760-
761+
761762
if ( ! $wp_filesystem ) {
762763
sse_log('WordPress filesystem API not available', 'error');
763764
return false;
764765
}
765-
766+
766767
// Check if the file exists using WP Filesystem
767768
if ($wp_filesystem->exists($filepath)) {
768769
// Delete the file using WordPress Filesystem API
769770
return $wp_filesystem->delete($filepath, false, 'f');
770771
}
771-
772+
772773
return false;
773774
}
774775

@@ -779,8 +780,8 @@ function sse_safely_delete_file( $filepath ) {
779780
* @return bool True if path is safe, false if contains traversal patterns.
780781
*/
781782
function sse_check_path_traversal( $normalized_file_path ) {
782-
// Block obvious directory traversal attempts
783-
if ( strpos( $normalized_file_path, '..' ) !== false ||
783+
// Block obvious directory traversal attempts.
784+
if ( strpos( $normalized_file_path, '..' ) !== false ||
784785
strpos( $normalized_file_path, '/./' ) !== false ||
785786
strpos( $normalized_file_path, '\\' ) !== false ) {
786787
return false;

0 commit comments

Comments
 (0)