changeset 9 | 177826044cd9 |
parent 7 | cf61fcea0001 |
child 16 | a86126ab1dd4 |
8:c7c34916027a | 9:177826044cd9 |
---|---|
34 // Content |
34 // Content |
35 'singular.php' => __( 'Singular Template' ), |
35 'singular.php' => __( 'Singular Template' ), |
36 'single.php' => __( 'Single Post' ), |
36 'single.php' => __( 'Single Post' ), |
37 'page.php' => __( 'Single Page' ), |
37 'page.php' => __( 'Single Page' ), |
38 'front-page.php' => __( 'Homepage' ), |
38 'front-page.php' => __( 'Homepage' ), |
39 'privacy-policy.php' => __( 'Privacy Policy Page' ), |
|
39 // Attachments |
40 // Attachments |
40 'attachment.php' => __( 'Attachment Template' ), |
41 'attachment.php' => __( 'Attachment Template' ), |
41 'image.php' => __( 'Image Attachment Template' ), |
42 'image.php' => __( 'Image Attachment Template' ), |
42 'video.php' => __( 'Video Attachment Template' ), |
43 'video.php' => __( 'Video Attachment Template' ), |
43 'audio.php' => __( 'Audio Attachment Template' ), |
44 'audio.php' => __( 'Audio Attachment Template' ), |
103 function get_home_path() { |
104 function get_home_path() { |
104 $home = set_url_scheme( get_option( 'home' ), 'http' ); |
105 $home = set_url_scheme( get_option( 'home' ), 'http' ); |
105 $siteurl = set_url_scheme( get_option( 'siteurl' ), 'http' ); |
106 $siteurl = set_url_scheme( get_option( 'siteurl' ), 'http' ); |
106 if ( ! empty( $home ) && 0 !== strcasecmp( $home, $siteurl ) ) { |
107 if ( ! empty( $home ) && 0 !== strcasecmp( $home, $siteurl ) ) { |
107 $wp_path_rel_to_home = str_ireplace( $home, '', $siteurl ); /* $siteurl - $home */ |
108 $wp_path_rel_to_home = str_ireplace( $home, '', $siteurl ); /* $siteurl - $home */ |
108 $pos = strripos( str_replace( '\\', '/', $_SERVER['SCRIPT_FILENAME'] ), trailingslashit( $wp_path_rel_to_home ) ); |
109 $pos = strripos( str_replace( '\\', '/', $_SERVER['SCRIPT_FILENAME'] ), trailingslashit( $wp_path_rel_to_home ) ); |
109 $home_path = substr( $_SERVER['SCRIPT_FILENAME'], 0, $pos ); |
110 $home_path = substr( $_SERVER['SCRIPT_FILENAME'], 0, $pos ); |
110 $home_path = trailingslashit( $home_path ); |
111 $home_path = trailingslashit( $home_path ); |
111 } else { |
112 } else { |
112 $home_path = ABSPATH; |
113 $home_path = ABSPATH; |
113 } |
114 } |
114 |
115 |
115 return str_replace( '\\', '/', $home_path ); |
116 return str_replace( '\\', '/', $home_path ); |
120 * The depth of the recursiveness can be controlled by the $levels param. |
121 * The depth of the recursiveness can be controlled by the $levels param. |
121 * |
122 * |
122 * @since 2.6.0 |
123 * @since 2.6.0 |
123 * @since 4.9.0 Added the `$exclusions` parameter. |
124 * @since 4.9.0 Added the `$exclusions` parameter. |
124 * |
125 * |
125 * @param string $folder Optional. Full path to folder. Default empty. |
126 * @param string $folder Optional. Full path to folder. Default empty. |
126 * @param int $levels Optional. Levels of folders to follow, Default 100 (PHP Loop limit). |
127 * @param int $levels Optional. Levels of folders to follow, Default 100 (PHP Loop limit). |
127 * @param array $exclusions Optional. List of folders and files to skip. |
128 * @param string[] $exclusions Optional. List of folders and files to skip. |
128 * @return bool|array False on failure, Else array of files |
129 * @return bool|string[] False on failure, else array of files. |
129 */ |
130 */ |
130 function list_files( $folder = '', $levels = 100, $exclusions = array() ) { |
131 function list_files( $folder = '', $levels = 100, $exclusions = array() ) { |
131 if ( empty( $folder ) ) { |
132 if ( empty( $folder ) ) { |
132 return false; |
133 return false; |
133 } |
134 } |
154 } |
155 } |
155 |
156 |
156 if ( is_dir( $folder . $file ) ) { |
157 if ( is_dir( $folder . $file ) ) { |
157 $files2 = list_files( $folder . $file, $levels - 1 ); |
158 $files2 = list_files( $folder . $file, $levels - 1 ); |
158 if ( $files2 ) { |
159 if ( $files2 ) { |
159 $files = array_merge($files, $files2 ); |
160 $files = array_merge( $files, $files2 ); |
160 } else { |
161 } else { |
161 $files[] = $folder . $file . '/'; |
162 $files[] = $folder . $file . '/'; |
162 } |
163 } |
163 } else { |
164 } else { |
164 $files[] = $folder . $file; |
165 $files[] = $folder . $file; |
173 /** |
174 /** |
174 * Get list of file extensions that are editable in plugins. |
175 * Get list of file extensions that are editable in plugins. |
175 * |
176 * |
176 * @since 4.9.0 |
177 * @since 4.9.0 |
177 * |
178 * |
178 * @param string $plugin Plugin. |
179 * @param string $plugin Path to the plugin file relative to the plugins directory. |
179 * @return array File extensions. |
180 * @return string[] Array of editable file extensions. |
180 */ |
181 */ |
181 function wp_get_plugin_file_editable_extensions( $plugin ) { |
182 function wp_get_plugin_file_editable_extensions( $plugin ) { |
182 |
183 |
183 $editable_extensions = array( |
184 $editable_extensions = array( |
184 'bash', |
185 'bash', |
217 |
218 |
218 /** |
219 /** |
219 * Filters file type extensions editable in the plugin editor. |
220 * Filters file type extensions editable in the plugin editor. |
220 * |
221 * |
221 * @since 2.8.0 |
222 * @since 2.8.0 |
222 * @since 4.9.0 Adds $plugin param. |
223 * @since 4.9.0 Added the `$plugin` parameter. |
223 * |
224 * |
224 * @param string $plugin Plugin file. |
225 * @param string[] $editable_extensions An array of editable plugin file extensions. |
225 * @param array $editable_extensions An array of editable plugin file extensions. |
226 * @param string $plugin Path to the plugin file relative to the plugins directory. |
226 */ |
227 */ |
227 $editable_extensions = (array) apply_filters( 'editable_extensions', $editable_extensions, $plugin ); |
228 $editable_extensions = (array) apply_filters( 'editable_extensions', $editable_extensions, $plugin ); |
228 |
229 |
229 return $editable_extensions; |
230 return $editable_extensions; |
230 } |
231 } |
231 |
232 |
232 /** |
233 /** |
233 * Get list of file extensions that are editable for a given theme. |
234 * Get list of file extensions that are editable for a given theme. |
234 * |
235 * |
235 * @param WP_Theme $theme Theme. |
236 * @param WP_Theme $theme Theme object. |
236 * @return array File extensions. |
237 * @return string[] Array of editable file extensions. |
237 */ |
238 */ |
238 function wp_get_theme_file_editable_extensions( $theme ) { |
239 function wp_get_theme_file_editable_extensions( $theme ) { |
239 |
240 |
240 $default_types = array( |
241 $default_types = array( |
241 'bash', |
242 'bash', |
275 /** |
276 /** |
276 * Filters the list of file types allowed for editing in the Theme editor. |
277 * Filters the list of file types allowed for editing in the Theme editor. |
277 * |
278 * |
278 * @since 4.4.0 |
279 * @since 4.4.0 |
279 * |
280 * |
280 * @param array $default_types List of file types. Default types include 'php' and 'css'. |
281 * @param string[] $default_types List of allowed file types. |
281 * @param WP_Theme $theme The current Theme object. |
282 * @param WP_Theme $theme The current Theme object. |
282 */ |
283 */ |
283 $file_types = apply_filters( 'wp_theme_editor_filetypes', $default_types, $theme ); |
284 $file_types = apply_filters( 'wp_theme_editor_filetypes', $default_types, $theme ); |
284 |
285 |
285 // Ensure that default types are still there. |
286 // Ensure that default types are still there. |
297 <div class="notice inline notice-{{ data.type || 'info' }} {{ data.alt ? 'notice-alt' : '' }} {{ data.dismissible ? 'is-dismissible' : '' }} {{ data.classes || '' }}"> |
298 <div class="notice inline notice-{{ data.type || 'info' }} {{ data.alt ? 'notice-alt' : '' }} {{ data.dismissible ? 'is-dismissible' : '' }} {{ data.classes || '' }}"> |
298 <# if ( 'php_error' === data.code ) { #> |
299 <# if ( 'php_error' === data.code ) { #> |
299 <p> |
300 <p> |
300 <?php |
301 <?php |
301 printf( |
302 printf( |
302 /* translators: %$1s is line number and %1$s is file path. */ |
303 /* translators: 1: line number, 2: file path */ |
303 __( 'Your PHP code changes were rolled back due to an error on line %1$s of file %2$s. Please fix and try saving again.' ), |
304 __( 'Your PHP code changes were rolled back due to an error on line %1$s of file %2$s. Please fix and try saving again.' ), |
304 '{{ data.line }}', |
305 '{{ data.line }}', |
305 '{{ data.file }}' |
306 '{{ data.file }}' |
306 ); |
307 ); |
307 ?> |
308 ?> |
335 * to attempt to see if there is a fatal error introduced. If so, the PHP change will be |
336 * to attempt to see if there is a fatal error introduced. If so, the PHP change will be |
336 * reverted. |
337 * reverted. |
337 * |
338 * |
338 * @since 4.9.0 |
339 * @since 4.9.0 |
339 * |
340 * |
340 * @param array $args { |
341 * @param string[] $args { |
341 * Args. Note that all of the arg values are already unslashed. They are, however, |
342 * Args. Note that all of the arg values are already unslashed. They are, however, |
342 * coming straight from $_POST and are not validated or sanitized in any way. |
343 * coming straight from `$_POST` and are not validated or sanitized in any way. |
343 * |
344 * |
344 * @type string $file Relative path to file. |
345 * @type string $file Relative path to file. |
345 * @type string $plugin Plugin being edited. |
346 * @type string $plugin Path to the plugin file relative to the plugins directory. |
346 * @type string $theme Theme being edited. |
347 * @type string $theme Theme being edited. |
347 * @type string $newcontent New content for the file. |
348 * @type string $newcontent New content for the file. |
348 * @type string $nonce Nonce. |
349 * @type string $nonce Nonce. |
349 * } |
350 * } |
350 * @return true|WP_Error True on success or `WP_Error` on failure. |
351 * @return true|WP_Error True on success or `WP_Error` on failure. |
365 |
366 |
366 if ( ! isset( $args['nonce'] ) ) { |
367 if ( ! isset( $args['nonce'] ) ) { |
367 return new WP_Error( 'missing_nonce' ); |
368 return new WP_Error( 'missing_nonce' ); |
368 } |
369 } |
369 |
370 |
370 $plugin = null; |
371 $plugin = null; |
371 $theme = null; |
372 $theme = null; |
372 $real_file = null; |
373 $real_file = null; |
373 if ( ! empty( $args['plugin'] ) ) { |
374 if ( ! empty( $args['plugin'] ) ) { |
374 $plugin = $args['plugin']; |
375 $plugin = $args['plugin']; |
375 |
376 |
376 if ( ! current_user_can( 'edit_plugins' ) ) { |
377 if ( ! current_user_can( 'edit_plugins' ) ) { |
412 $theme = wp_get_theme( $stylesheet ); |
413 $theme = wp_get_theme( $stylesheet ); |
413 if ( ! $theme->exists() ) { |
414 if ( ! $theme->exists() ) { |
414 return new WP_Error( 'non_existent_theme', __( 'The requested theme does not exist.' ) ); |
415 return new WP_Error( 'non_existent_theme', __( 'The requested theme does not exist.' ) ); |
415 } |
416 } |
416 |
417 |
417 $real_file = $theme->get_stylesheet_directory() . '/' . $file; |
418 if ( ! wp_verify_nonce( $args['nonce'], 'edit-theme_' . $stylesheet . '_' . $file ) ) { |
418 if ( ! wp_verify_nonce( $args['nonce'], 'edit-theme_' . $real_file . $stylesheet ) ) { |
|
419 return new WP_Error( 'nonce_failure' ); |
419 return new WP_Error( 'nonce_failure' ); |
420 } |
420 } |
421 |
421 |
422 if ( $theme->errors() && 'theme_no_stylesheet' === $theme->errors()->get_error_code() ) { |
422 if ( $theme->errors() && 'theme_no_stylesheet' === $theme->errors()->get_error_code() ) { |
423 return new WP_Error( |
423 return new WP_Error( |
433 switch ( $type ) { |
433 switch ( $type ) { |
434 case 'php': |
434 case 'php': |
435 $allowed_files = array_merge( $allowed_files, $theme->get_files( 'php', -1 ) ); |
435 $allowed_files = array_merge( $allowed_files, $theme->get_files( 'php', -1 ) ); |
436 break; |
436 break; |
437 case 'css': |
437 case 'css': |
438 $style_files = $theme->get_files( 'css', -1 ); |
438 $style_files = $theme->get_files( 'css', -1 ); |
439 $allowed_files['style.css'] = $style_files['style.css']; |
439 $allowed_files['style.css'] = $style_files['style.css']; |
440 $allowed_files = array_merge( $allowed_files, $style_files ); |
440 $allowed_files = array_merge( $allowed_files, $style_files ); |
441 break; |
441 break; |
442 default: |
442 default: |
443 $allowed_files = array_merge( $allowed_files, $theme->get_files( $type, -1 ) ); |
443 $allowed_files = array_merge( $allowed_files, $theme->get_files( $type, -1 ) ); |
444 break; |
444 break; |
445 } |
445 } |
448 // Compare based on relative paths |
448 // Compare based on relative paths |
449 if ( 0 !== validate_file( $file, array_keys( $allowed_files ) ) ) { |
449 if ( 0 !== validate_file( $file, array_keys( $allowed_files ) ) ) { |
450 return new WP_Error( 'disallowed_theme_file', __( 'Sorry, that file cannot be edited.' ) ); |
450 return new WP_Error( 'disallowed_theme_file', __( 'Sorry, that file cannot be edited.' ) ); |
451 } |
451 } |
452 |
452 |
453 $real_file = $theme->get_stylesheet_directory() . '/' . $file; |
|
454 |
|
453 $is_active = ( get_stylesheet() === $stylesheet || get_template() === $stylesheet ); |
455 $is_active = ( get_stylesheet() === $stylesheet || get_template() === $stylesheet ); |
456 |
|
454 } else { |
457 } else { |
455 return new WP_Error( 'missing_theme_or_plugin' ); |
458 return new WP_Error( 'missing_theme_or_plugin' ); |
456 } |
459 } |
457 |
460 |
458 // Ensure file is real. |
461 // Ensure file is real. |
489 opcache_invalidate( $real_file, true ); |
492 opcache_invalidate( $real_file, true ); |
490 } |
493 } |
491 |
494 |
492 if ( $is_active && 'php' === $extension ) { |
495 if ( $is_active && 'php' === $extension ) { |
493 |
496 |
494 $scrape_key = md5( rand() ); |
497 $scrape_key = md5( rand() ); |
495 $transient = 'scrape_key_' . $scrape_key; |
498 $transient = 'scrape_key_' . $scrape_key; |
496 $scrape_nonce = strval( rand() ); |
499 $scrape_nonce = strval( rand() ); |
497 set_transient( $transient, $scrape_nonce, 60 ); // It shouldn't take more than 60 seconds to make the two loopback requests. |
500 set_transient( $transient, $scrape_nonce, 60 ); // It shouldn't take more than 60 seconds to make the two loopback requests. |
498 |
501 |
499 $cookies = wp_unslash( $_COOKIE ); |
502 $cookies = wp_unslash( $_COOKIE ); |
500 $scrape_params = array( |
503 $scrape_params = array( |
501 'wp_scrape_key' => $scrape_key, |
504 'wp_scrape_key' => $scrape_key, |
502 'wp_scrape_nonce' => $scrape_nonce, |
505 'wp_scrape_nonce' => $scrape_nonce, |
503 ); |
506 ); |
504 $headers = array( |
507 $headers = array( |
505 'Cache-Control' => 'no-cache', |
508 'Cache-Control' => 'no-cache', |
506 ); |
509 ); |
507 |
510 |
508 // Include Basic auth in loopback requests. |
511 // Include Basic auth in loopback requests. |
509 if ( isset( $_SERVER['PHP_AUTH_USER'] ) && isset( $_SERVER['PHP_AUTH_PW'] ) ) { |
512 if ( isset( $_SERVER['PHP_AUTH_USER'] ) && isset( $_SERVER['PHP_AUTH_PW'] ) ) { |
515 |
518 |
516 // Time to wait for loopback requests to finish. |
519 // Time to wait for loopback requests to finish. |
517 $timeout = 100; |
520 $timeout = 100; |
518 |
521 |
519 $needle_start = "###### wp_scraping_result_start:$scrape_key ######"; |
522 $needle_start = "###### wp_scraping_result_start:$scrape_key ######"; |
520 $needle_end = "###### wp_scraping_result_end:$scrape_key ######"; |
523 $needle_end = "###### wp_scraping_result_end:$scrape_key ######"; |
521 |
524 |
522 // Attempt loopback request to editor to see if user just whitescreened themselves. |
525 // Attempt loopback request to editor to see if user just whitescreened themselves. |
523 if ( $plugin ) { |
526 if ( $plugin ) { |
524 $url = add_query_arg( compact( 'plugin', 'file' ), admin_url( 'plugin-editor.php' ) ); |
527 $url = add_query_arg( compact( 'plugin', 'file' ), admin_url( 'plugin-editor.php' ) ); |
525 } elseif ( isset( $stylesheet ) ) { |
528 } elseif ( isset( $stylesheet ) ) { |
526 $url = add_query_arg( |
529 $url = add_query_arg( |
527 array( |
530 array( |
528 'theme' => $stylesheet, |
531 'theme' => $stylesheet, |
529 'file' => $file, |
532 'file' => $file, |
530 ), |
533 ), |
531 admin_url( 'theme-editor.php' ) |
534 admin_url( 'theme-editor.php' ) |
532 ); |
535 ); |
533 } else { |
536 } else { |
534 $url = admin_url(); |
537 $url = admin_url(); |
535 } |
538 } |
536 $url = add_query_arg( $scrape_params, $url ); |
539 $url = add_query_arg( $scrape_params, $url ); |
537 $r = wp_remote_get( $url, compact( 'cookies', 'headers', 'timeout' ) ); |
540 $r = wp_remote_get( $url, compact( 'cookies', 'headers', 'timeout' ) ); |
538 $body = wp_remote_retrieve_body( $r ); |
541 $body = wp_remote_retrieve_body( $r ); |
539 $scrape_result_position = strpos( $body, $needle_start ); |
542 $scrape_result_position = strpos( $body, $needle_start ); |
540 |
543 |
541 $loopback_request_failure = array( |
544 $loopback_request_failure = array( |
542 'code' => 'loopback_request_failed', |
545 'code' => 'loopback_request_failed', |
543 'message' => __( 'Unable to communicate back with site to check for fatal errors, so the PHP change was reverted. You will need to upload your PHP file change by some other means, such as by using SFTP.' ), |
546 'message' => __( 'Unable to communicate back with site to check for fatal errors, so the PHP change was reverted. You will need to upload your PHP file change by some other means, such as by using SFTP.' ), |
544 ); |
547 ); |
545 $json_parse_failure = array( |
548 $json_parse_failure = array( |
546 'code' => 'json_parse_error', |
549 'code' => 'json_parse_error', |
547 ); |
550 ); |
548 |
551 |
549 $result = null; |
552 $result = null; |
550 if ( false === $scrape_result_position ) { |
553 if ( false === $scrape_result_position ) { |
551 $result = $loopback_request_failure; |
554 $result = $loopback_request_failure; |
552 } else { |
555 } else { |
553 $error_output = substr( $body, $scrape_result_position + strlen( $needle_start ) ); |
556 $error_output = substr( $body, $scrape_result_position + strlen( $needle_start ) ); |
554 $error_output = substr( $error_output, 0, strpos( $error_output, $needle_end ) ); |
557 $error_output = substr( $error_output, 0, strpos( $error_output, $needle_end ) ); |
555 $result = json_decode( trim( $error_output ), true ); |
558 $result = json_decode( trim( $error_output ), true ); |
556 if ( empty( $result ) ) { |
559 if ( empty( $result ) ) { |
557 $result = $json_parse_failure; |
560 $result = $json_parse_failure; |
558 } |
561 } |
559 } |
562 } |
560 |
563 |
561 // Try making request to homepage as well to see if visitors have been whitescreened. |
564 // Try making request to homepage as well to see if visitors have been whitescreened. |
562 if ( true === $result ) { |
565 if ( true === $result ) { |
563 $url = home_url( '/' ); |
566 $url = home_url( '/' ); |
564 $url = add_query_arg( $scrape_params, $url ); |
567 $url = add_query_arg( $scrape_params, $url ); |
565 $r = wp_remote_get( $url, compact( 'cookies', 'headers', 'timeout' ) ); |
568 $r = wp_remote_get( $url, compact( 'cookies', 'headers', 'timeout' ) ); |
566 $body = wp_remote_retrieve_body( $r ); |
569 $body = wp_remote_retrieve_body( $r ); |
567 $scrape_result_position = strpos( $body, $needle_start ); |
570 $scrape_result_position = strpos( $body, $needle_start ); |
568 |
571 |
569 if ( false === $scrape_result_position ) { |
572 if ( false === $scrape_result_position ) { |
570 $result = $loopback_request_failure; |
573 $result = $loopback_request_failure; |
571 } else { |
574 } else { |
572 $error_output = substr( $body, $scrape_result_position + strlen( $needle_start ) ); |
575 $error_output = substr( $body, $scrape_result_position + strlen( $needle_start ) ); |
573 $error_output = substr( $error_output, 0, strpos( $error_output, $needle_end ) ); |
576 $error_output = substr( $error_output, 0, strpos( $error_output, $needle_end ) ); |
574 $result = json_decode( trim( $error_output ), true ); |
577 $result = json_decode( trim( $error_output ), true ); |
575 if ( empty( $result ) ) { |
578 if ( empty( $result ) ) { |
576 $result = $json_parse_failure; |
579 $result = $json_parse_failure; |
577 } |
580 } |
578 } |
581 } |
579 } |
582 } |
623 if ( empty( $dir ) ) { |
626 if ( empty( $dir ) ) { |
624 $dir = get_temp_dir(); |
627 $dir = get_temp_dir(); |
625 } |
628 } |
626 |
629 |
627 if ( empty( $filename ) || '.' == $filename || '/' == $filename || '\\' == $filename ) { |
630 if ( empty( $filename ) || '.' == $filename || '/' == $filename || '\\' == $filename ) { |
628 $filename = time(); |
631 $filename = uniqid(); |
629 } |
632 } |
630 |
633 |
631 // Use the basename of the given file without the extension as the name for the temporary directory |
634 // Use the basename of the given file without the extension as the name for the temporary directory |
632 $temp_filename = basename( $filename ); |
635 $temp_filename = basename( $filename ); |
633 $temp_filename = preg_replace( '|\.[^.]*$|', '', $temp_filename ); |
636 $temp_filename = preg_replace( '|\.[^.]*$|', '', $temp_filename ); |
638 } |
641 } |
639 |
642 |
640 // Suffix some random data to avoid filename conflicts |
643 // Suffix some random data to avoid filename conflicts |
641 $temp_filename .= '-' . wp_generate_password( 6, false ); |
644 $temp_filename .= '-' . wp_generate_password( 6, false ); |
642 $temp_filename .= '.tmp'; |
645 $temp_filename .= '.tmp'; |
643 $temp_filename = $dir . wp_unique_filename( $dir, $temp_filename ); |
646 $temp_filename = $dir . wp_unique_filename( $dir, $temp_filename ); |
644 |
647 |
645 $fp = @fopen( $temp_filename, 'x' ); |
648 $fp = @fopen( $temp_filename, 'x' ); |
646 if ( ! $fp && is_writable( $dir ) && file_exists( $temp_filename ) ) { |
649 if ( ! $fp && is_writable( $dir ) && file_exists( $temp_filename ) ) { |
647 return wp_tempnam( $filename, $dir ); |
650 return wp_tempnam( $filename, $dir ); |
648 } |
651 } |
658 * |
661 * |
659 * Function will die if you are not allowed to edit the file. |
662 * Function will die if you are not allowed to edit the file. |
660 * |
663 * |
661 * @since 1.5.0 |
664 * @since 1.5.0 |
662 * |
665 * |
663 * @param string $file File the user is attempting to edit. |
666 * @param string $file File the user is attempting to edit. |
664 * @param array $allowed_files Optional. Array of allowed files to edit, $file must match an entry exactly. |
667 * @param string[] $allowed_files Optional. Array of allowed files to edit. `$file` must match an entry exactly. |
665 * @return string|null |
668 * @return string|void Returns the file name on success, dies on failure. |
666 */ |
669 */ |
667 function validate_file_to_edit( $file, $allowed_files = array() ) { |
670 function validate_file_to_edit( $file, $allowed_files = array() ) { |
668 $code = validate_file( $file, $allowed_files ); |
671 $code = validate_file( $file, $allowed_files ); |
669 |
672 |
670 if (!$code ) |
673 if ( ! $code ) { |
671 return $file; |
674 return $file; |
675 } |
|
672 |
676 |
673 switch ( $code ) { |
677 switch ( $code ) { |
674 case 1 : |
678 case 1: |
675 wp_die( __( 'Sorry, that file cannot be edited.' ) ); |
679 wp_die( __( 'Sorry, that file cannot be edited.' ) ); |
676 |
680 |
677 // case 2 : |
681 // case 2 : |
678 // wp_die( __('Sorry, can’t call files with their real path.' )); |
682 // wp_die( __('Sorry, can’t call files with their real path.' )); |
679 |
683 |
680 case 3 : |
684 case 3: |
681 wp_die( __( 'Sorry, that file cannot be edited.' ) ); |
685 wp_die( __( 'Sorry, that file cannot be edited.' ) ); |
682 } |
686 } |
683 } |
687 } |
684 |
688 |
685 /** |
689 /** |
689 * @access private |
693 * @access private |
690 * @since 4.0.0 |
694 * @since 4.0.0 |
691 * |
695 * |
692 * @see wp_handle_upload_error |
696 * @see wp_handle_upload_error |
693 * |
697 * |
694 * @param array $file Reference to a single element of $_FILES. Call the function once for each uploaded file. |
698 * @param string[] $file Reference to a single element of `$_FILES`. Call the function once for each uploaded file. |
695 * @param array|false $overrides An associative array of names => values to override default variables. Default false. |
699 * @param string[]|false $overrides An associative array of names => values to override default variables. Default false. |
696 * @param string $time Time formatted in 'yyyy/mm'. |
700 * @param string $time Time formatted in 'yyyy/mm'. |
697 * @param string $action Expected value for $_POST['action']. |
701 * @param string $action Expected value for `$_POST['action']`. |
698 * @return array On success, returns an associative array of file attributes. On failure, returns |
702 * @return string[] On success, returns an associative array of file attributes. On failure, returns |
699 * $overrides['upload_error_handler'](&$file, $message ) or array( 'error'=>$message ). |
703 * `$overrides['upload_error_handler'](&$file, $message )` or `array( 'error'=>$message )`. |
700 */ |
704 */ |
701 function _wp_handle_upload( &$file, $overrides, $time, $action ) { |
705 function _wp_handle_upload( &$file, $overrides, $time, $action ) { |
702 // The default error handler. |
706 // The default error handler. |
703 if ( ! function_exists( 'wp_handle_upload_error' ) ) { |
707 if ( ! function_exists( 'wp_handle_upload_error' ) ) { |
704 function wp_handle_upload_error( &$file, $message ) { |
708 function wp_handle_upload_error( &$file, $message ) { |
712 * The dynamic portion of the hook name, `$action`, refers to the post action. |
716 * The dynamic portion of the hook name, `$action`, refers to the post action. |
713 * |
717 * |
714 * @since 2.9.0 as 'wp_handle_upload_prefilter'. |
718 * @since 2.9.0 as 'wp_handle_upload_prefilter'. |
715 * @since 4.0.0 Converted to a dynamic hook with `$action`. |
719 * @since 4.0.0 Converted to a dynamic hook with `$action`. |
716 * |
720 * |
717 * @param array $file An array of data for a single file. |
721 * @param string[] $file An array of data for a single file. |
718 */ |
722 */ |
719 $file = apply_filters( "{$action}_prefilter", $file ); |
723 $file = apply_filters( "{$action}_prefilter", $file ); |
720 |
724 |
721 // You may define your own function and pass the name in $overrides['upload_error_handler'] |
725 // You may define your own function and pass the name in $overrides['upload_error_handler'] |
722 $upload_error_handler = 'wp_handle_upload_error'; |
726 $upload_error_handler = 'wp_handle_upload_error'; |
752 __( 'The uploaded file was only partially uploaded.' ), |
756 __( 'The uploaded file was only partially uploaded.' ), |
753 __( 'No file was uploaded.' ), |
757 __( 'No file was uploaded.' ), |
754 '', |
758 '', |
755 __( 'Missing a temporary folder.' ), |
759 __( 'Missing a temporary folder.' ), |
756 __( 'Failed to write file to disk.' ), |
760 __( 'Failed to write file to disk.' ), |
757 __( 'File upload stopped by extension.' ) |
761 __( 'File upload stopped by extension.' ), |
758 ); |
762 ); |
759 } |
763 } |
760 |
764 |
761 // All tests are on by default. Most can be turned off by $overrides[{test_name}] = false; |
765 // All tests are on by default. Most can be turned off by $overrides[{test_name}] = false; |
762 $test_form = isset( $overrides['test_form'] ) ? $overrides['test_form'] : true; |
766 $test_form = isset( $overrides['test_form'] ) ? $overrides['test_form'] : true; |
763 $test_size = isset( $overrides['test_size'] ) ? $overrides['test_size'] : true; |
767 $test_size = isset( $overrides['test_size'] ) ? $overrides['test_size'] : true; |
764 |
768 |
765 // If you override this, you must provide $ext and $type!! |
769 // If you override this, you must provide $ext and $type!! |
766 $test_type = isset( $overrides['test_type'] ) ? $overrides['test_type'] : true; |
770 $test_type = isset( $overrides['test_type'] ) ? $overrides['test_type'] : true; |
767 $mimes = isset( $overrides['mimes'] ) ? $overrides['mimes'] : false; |
771 $mimes = isset( $overrides['mimes'] ) ? $overrides['mimes'] : false; |
768 |
772 |
769 // A correct form post will pass this test. |
773 // A correct form post will pass this test. |
770 if ( $test_form && ( ! isset( $_POST['action'] ) || ( $_POST['action'] != $action ) ) ) { |
774 if ( $test_form && ( ! isset( $_POST['action'] ) || ( $_POST['action'] != $action ) ) ) { |
771 return call_user_func_array( $upload_error_handler, array( &$file, __( 'Invalid form submission.' ) ) ); |
775 return call_user_func_array( $upload_error_handler, array( &$file, __( 'Invalid form submission.' ) ) ); |
772 } |
776 } |
773 // A successful upload will pass this test. It makes no sense to override this one. |
777 // A successful upload will pass this test. It makes no sense to override this one. |
774 if ( isset( $file['error'] ) && $file['error'] > 0 ) { |
778 if ( isset( $file['error'] ) && $file['error'] > 0 ) { |
775 return call_user_func_array( $upload_error_handler, array( &$file, $upload_error_strings[ $file['error'] ] ) ); |
779 return call_user_func_array( $upload_error_handler, array( &$file, $upload_error_strings[ $file['error'] ] ) ); |
780 } |
|
781 |
|
782 // A properly uploaded file will pass this test. There should be no reason to override this one. |
|
783 $test_uploaded_file = 'wp_handle_upload' === $action ? @ is_uploaded_file( $file['tmp_name'] ) : @ is_readable( $file['tmp_name'] ); |
|
784 if ( ! $test_uploaded_file ) { |
|
785 return call_user_func_array( $upload_error_handler, array( &$file, __( 'Specified file failed upload test.' ) ) ); |
|
776 } |
786 } |
777 |
787 |
778 $test_file_size = 'wp_handle_upload' === $action ? $file['size'] : filesize( $file['tmp_name'] ); |
788 $test_file_size = 'wp_handle_upload' === $action ? $file['size'] : filesize( $file['tmp_name'] ); |
779 // A non-empty file will pass this test. |
789 // A non-empty file will pass this test. |
780 if ( $test_size && ! ( $test_file_size > 0 ) ) { |
790 if ( $test_size && ! ( $test_file_size > 0 ) ) { |
784 $error_msg = __( 'File is empty. Please upload something more substantial. This error could also be caused by uploads being disabled in your php.ini or by post_max_size being defined as smaller than upload_max_filesize in php.ini.' ); |
794 $error_msg = __( 'File is empty. Please upload something more substantial. This error could also be caused by uploads being disabled in your php.ini or by post_max_size being defined as smaller than upload_max_filesize in php.ini.' ); |
785 } |
795 } |
786 return call_user_func_array( $upload_error_handler, array( &$file, $error_msg ) ); |
796 return call_user_func_array( $upload_error_handler, array( &$file, $error_msg ) ); |
787 } |
797 } |
788 |
798 |
789 // A properly uploaded file will pass this test. There should be no reason to override this one. |
|
790 $test_uploaded_file = 'wp_handle_upload' === $action ? @ is_uploaded_file( $file['tmp_name'] ) : @ is_file( $file['tmp_name'] ); |
|
791 if ( ! $test_uploaded_file ) { |
|
792 return call_user_func_array( $upload_error_handler, array( &$file, __( 'Specified file failed upload test.' ) ) ); |
|
793 } |
|
794 |
|
795 // A correct MIME type will pass this test. Override $mimes or use the upload_mimes filter. |
799 // A correct MIME type will pass this test. Override $mimes or use the upload_mimes filter. |
796 if ( $test_type ) { |
800 if ( $test_type ) { |
797 $wp_filetype = wp_check_filetype_and_ext( $file['tmp_name'], $file['name'], $mimes ); |
801 $wp_filetype = wp_check_filetype_and_ext( $file['tmp_name'], $file['name'], $mimes ); |
798 $ext = empty( $wp_filetype['ext'] ) ? '' : $wp_filetype['ext']; |
802 $ext = empty( $wp_filetype['ext'] ) ? '' : $wp_filetype['ext']; |
799 $type = empty( $wp_filetype['type'] ) ? '' : $wp_filetype['type']; |
803 $type = empty( $wp_filetype['type'] ) ? '' : $wp_filetype['type']; |
800 $proper_filename = empty( $wp_filetype['proper_filename'] ) ? '' : $wp_filetype['proper_filename']; |
804 $proper_filename = empty( $wp_filetype['proper_filename'] ) ? '' : $wp_filetype['proper_filename']; |
801 |
805 |
802 // Check to see if wp_check_filetype_and_ext() determined the filename was incorrect |
806 // Check to see if wp_check_filetype_and_ext() determined the filename was incorrect |
803 if ( $proper_filename ) { |
807 if ( $proper_filename ) { |
804 $file['name'] = $proper_filename; |
808 $file['name'] = $proper_filename; |
805 } |
809 } |
806 if ( ( ! $type || !$ext ) && ! current_user_can( 'unfiltered_upload' ) ) { |
810 if ( ( ! $type || ! $ext ) && ! current_user_can( 'unfiltered_upload' ) ) { |
807 return call_user_func_array( $upload_error_handler, array( &$file, __( 'Sorry, this file type is not permitted for security reasons.' ) ) ); |
811 return call_user_func_array( $upload_error_handler, array( &$file, __( 'Sorry, this file type is not permitted for security reasons.' ) ) ); |
808 } |
812 } |
809 if ( ! $type ) { |
813 if ( ! $type ) { |
810 $type = $file['type']; |
814 $type = $file['type']; |
811 } |
815 } |
824 $filename = wp_unique_filename( $uploads['path'], $file['name'], $unique_filename_callback ); |
828 $filename = wp_unique_filename( $uploads['path'], $file['name'], $unique_filename_callback ); |
825 |
829 |
826 // Move the file to the uploads dir. |
830 // Move the file to the uploads dir. |
827 $new_file = $uploads['path'] . "/$filename"; |
831 $new_file = $uploads['path'] . "/$filename"; |
828 |
832 |
829 /** |
833 /** |
830 * Filters whether to short-circuit moving the uploaded file after passing all checks. |
834 * Filters whether to short-circuit moving the uploaded file after passing all checks. |
831 * |
835 * |
832 * If a non-null value is passed to the filter, moving the file and any related error |
836 * If a non-null value is passed to the filter, moving the file and any related error |
833 * reporting will be completely skipped. |
837 * reporting will be completely skipped. |
834 * |
838 * |
854 if ( 0 === strpos( $uploads['basedir'], ABSPATH ) ) { |
858 if ( 0 === strpos( $uploads['basedir'], ABSPATH ) ) { |
855 $error_path = str_replace( ABSPATH, '', $uploads['basedir'] ) . $uploads['subdir']; |
859 $error_path = str_replace( ABSPATH, '', $uploads['basedir'] ) . $uploads['subdir']; |
856 } else { |
860 } else { |
857 $error_path = basename( $uploads['basedir'] ) . $uploads['subdir']; |
861 $error_path = basename( $uploads['basedir'] ) . $uploads['subdir']; |
858 } |
862 } |
859 return $upload_error_handler( $file, sprintf( __('The uploaded file could not be moved to %s.' ), $error_path ) ); |
863 return $upload_error_handler( $file, sprintf( __( 'The uploaded file could not be moved to %s.' ), $error_path ) ); |
860 } |
864 } |
861 } |
865 } |
862 |
866 |
863 // Set correct file permissions. |
867 // Set correct file permissions. |
864 $stat = stat( dirname( $new_file )); |
868 $stat = stat( dirname( $new_file ) ); |
865 $perms = $stat['mode'] & 0000666; |
869 $perms = $stat['mode'] & 0000666; |
866 @ chmod( $new_file, $perms ); |
870 @ chmod( $new_file, $perms ); |
867 |
871 |
868 // Compute the URL. |
872 // Compute the URL. |
869 $url = $uploads['url'] . "/$filename"; |
873 $url = $uploads['url'] . "/$filename"; |
884 * @type string $url URL of the uploaded file. |
888 * @type string $url URL of the uploaded file. |
885 * @type string $type File type. |
889 * @type string $type File type. |
886 * } |
890 * } |
887 * @param string $context The type of upload action. Values include 'upload' or 'sideload'. |
891 * @param string $context The type of upload action. Values include 'upload' or 'sideload'. |
888 */ |
892 */ |
889 return apply_filters( 'wp_handle_upload', array( |
893 return apply_filters( |
890 'file' => $new_file, |
894 'wp_handle_upload', |
891 'url' => $url, |
895 array( |
892 'type' => $type |
896 'file' => $new_file, |
893 ), 'wp_handle_sideload' === $action ? 'sideload' : 'upload' ); |
897 'url' => $url, |
898 'type' => $type, |
|
899 ), |
|
900 'wp_handle_sideload' === $action ? 'sideload' : 'upload' |
|
901 ); |
|
894 } |
902 } |
895 |
903 |
896 /** |
904 /** |
897 * Wrapper for _wp_handle_upload(). |
905 * Wrapper for _wp_handle_upload(). |
898 * |
906 * |
951 return _wp_handle_upload( $file, $overrides, $time, $action ); |
959 return _wp_handle_upload( $file, $overrides, $time, $action ); |
952 } |
960 } |
953 |
961 |
954 |
962 |
955 /** |
963 /** |
956 * Downloads a URL to a local temporary file using the WordPress HTTP Class. |
964 * Downloads a URL to a local temporary file using the WordPress HTTP API. |
957 * Please note, That the calling function must unlink() the file. |
965 * |
966 * Please note that the calling function must unlink() the file. |
|
958 * |
967 * |
959 * @since 2.5.0 |
968 * @since 2.5.0 |
960 * |
969 * @since 5.2.0 Signature Verification with SoftFail was added. |
961 * @param string $url the URL of the file to download |
970 * |
962 * @param int $timeout The timeout for the request to download the file default 300 seconds |
971 * @param string $url The URL of the file to download. |
963 * @return mixed WP_Error on failure, string Filename on success. |
972 * @param int $timeout The timeout for the request to download the file. Default 300 seconds. |
964 */ |
973 * @param bool $signature_verification Whether to perform Signature Verification. Default false. |
965 function download_url( $url, $timeout = 300 ) { |
974 * @return string|WP_Error Filename on success, WP_Error on failure. |
975 */ |
|
976 function download_url( $url, $timeout = 300, $signature_verification = false ) { |
|
966 //WARNING: The file is not automatically deleted, The script must unlink() the file. |
977 //WARNING: The file is not automatically deleted, The script must unlink() the file. |
967 if ( ! $url ) |
978 if ( ! $url ) { |
968 return new WP_Error('http_no_url', __('Invalid URL Provided.')); |
979 return new WP_Error( 'http_no_url', __( 'Invalid URL Provided.' ) ); |
980 } |
|
969 |
981 |
970 $url_filename = basename( parse_url( $url, PHP_URL_PATH ) ); |
982 $url_filename = basename( parse_url( $url, PHP_URL_PATH ) ); |
971 |
983 |
972 $tmpfname = wp_tempnam( $url_filename ); |
984 $tmpfname = wp_tempnam( $url_filename ); |
973 if ( ! $tmpfname ) |
985 if ( ! $tmpfname ) { |
974 return new WP_Error('http_no_file', __('Could not create Temporary file.')); |
986 return new WP_Error( 'http_no_file', __( 'Could not create Temporary file.' ) ); |
975 |
987 } |
976 $response = wp_safe_remote_get( $url, array( 'timeout' => $timeout, 'stream' => true, 'filename' => $tmpfname ) ); |
988 |
989 $response = wp_safe_remote_get( |
|
990 $url, |
|
991 array( |
|
992 'timeout' => $timeout, |
|
993 'stream' => true, |
|
994 'filename' => $tmpfname, |
|
995 ) |
|
996 ); |
|
977 |
997 |
978 if ( is_wp_error( $response ) ) { |
998 if ( is_wp_error( $response ) ) { |
979 unlink( $tmpfname ); |
999 unlink( $tmpfname ); |
980 return $response; |
1000 return $response; |
981 } |
1001 } |
982 |
1002 |
983 if ( 200 != wp_remote_retrieve_response_code( $response ) ){ |
1003 $response_code = wp_remote_retrieve_response_code( $response ); |
1004 |
|
1005 if ( 200 != $response_code ) { |
|
1006 $data = array( |
|
1007 'code' => $response_code, |
|
1008 ); |
|
1009 |
|
1010 // Retrieve a sample of the response body for debugging purposes. |
|
1011 $tmpf = fopen( $tmpfname, 'rb' ); |
|
1012 if ( $tmpf ) { |
|
1013 /** |
|
1014 * Filters the maximum error response body size in `download_url()`. |
|
1015 * |
|
1016 * @since 5.1.0 |
|
1017 * |
|
1018 * @see download_url() |
|
1019 * |
|
1020 * @param int $size The maximum error response body size. Default 1 KB. |
|
1021 */ |
|
1022 $response_size = apply_filters( 'download_url_error_max_body_size', KB_IN_BYTES ); |
|
1023 $data['body'] = fread( $tmpf, $response_size ); |
|
1024 fclose( $tmpf ); |
|
1025 } |
|
1026 |
|
984 unlink( $tmpfname ); |
1027 unlink( $tmpfname ); |
985 return new WP_Error( 'http_404', trim( wp_remote_retrieve_response_message( $response ) ) ); |
1028 return new WP_Error( 'http_404', trim( wp_remote_retrieve_response_message( $response ) ), $data ); |
986 } |
1029 } |
987 |
1030 |
988 $content_md5 = wp_remote_retrieve_header( $response, 'content-md5' ); |
1031 $content_md5 = wp_remote_retrieve_header( $response, 'content-md5' ); |
989 if ( $content_md5 ) { |
1032 if ( $content_md5 ) { |
990 $md5_check = verify_file_md5( $tmpfname, $content_md5 ); |
1033 $md5_check = verify_file_md5( $tmpfname, $content_md5 ); |
992 unlink( $tmpfname ); |
1035 unlink( $tmpfname ); |
993 return $md5_check; |
1036 return $md5_check; |
994 } |
1037 } |
995 } |
1038 } |
996 |
1039 |
1040 // If the caller expects signature verification to occur, check to see if this URL supports it. |
|
1041 if ( $signature_verification ) { |
|
1042 /** |
|
1043 * Filters the list of hosts which should have Signature Verification attempteds on. |
|
1044 * |
|
1045 * @since 5.2.0 |
|
1046 * |
|
1047 * @param array List of hostnames. |
|
1048 */ |
|
1049 $signed_hostnames = apply_filters( 'wp_signature_hosts', array( 'wordpress.org', 'downloads.wordpress.org', 's.w.org' ) ); |
|
1050 $signature_verification = in_array( parse_url( $url, PHP_URL_HOST ), $signed_hostnames, true ); |
|
1051 } |
|
1052 |
|
1053 // Perform signature valiation if supported. |
|
1054 if ( $signature_verification ) { |
|
1055 $signature = wp_remote_retrieve_header( $response, 'x-content-signature' ); |
|
1056 if ( ! $signature ) { |
|
1057 // Retrieve signatures from a file if the header wasn't included. |
|
1058 // WordPress.org stores signatures at $package_url.sig |
|
1059 |
|
1060 $signature_url = false; |
|
1061 $url_path = parse_url( $url, PHP_URL_PATH ); |
|
1062 if ( substr( $url_path, -4 ) == '.zip' || substr( $url_path, -7 ) == '.tar.gz' ) { |
|
1063 $signature_url = str_replace( $url_path, $url_path . '.sig', $url ); |
|
1064 } |
|
1065 |
|
1066 /** |
|
1067 * Filter the URL where the signature for a file is located. |
|
1068 * |
|
1069 * @since 5.2.0 |
|
1070 * |
|
1071 * @param false|string $signature_url The URL where signatures can be found for a file, or false if none are known. |
|
1072 * @param string $url The URL being verified. |
|
1073 */ |
|
1074 $signature_url = apply_filters( 'wp_signature_url', $signature_url, $url ); |
|
1075 |
|
1076 if ( $signature_url ) { |
|
1077 $signature_request = wp_safe_remote_get( |
|
1078 $signature_url, |
|
1079 array( |
|
1080 'limit_response_size' => 10 * 1024, // 10KB should be large enough for quite a few signatures. |
|
1081 ) |
|
1082 ); |
|
1083 |
|
1084 if ( ! is_wp_error( $signature_request ) && 200 === wp_remote_retrieve_response_code( $signature_request ) ) { |
|
1085 $signature = explode( "\n", wp_remote_retrieve_body( $signature_request ) ); |
|
1086 } |
|
1087 } |
|
1088 } |
|
1089 |
|
1090 // Perform the checks. |
|
1091 $signature_verification = verify_file_signature( $tmpfname, $signature, basename( parse_url( $url, PHP_URL_PATH ) ) ); |
|
1092 } |
|
1093 |
|
1094 if ( is_wp_error( $signature_verification ) ) { |
|
1095 if ( |
|
1096 /** |
|
1097 * Filters whether Signature Verification failures should be allowed to soft fail. |
|
1098 * |
|
1099 * WARNING: This may be removed from a future release. |
|
1100 * |
|
1101 * @since 5.2.0 |
|
1102 * |
|
1103 * @param bool $signature_softfail If a softfail is allowed. |
|
1104 * @param string $url The url being accessed. |
|
1105 */ |
|
1106 apply_filters( 'wp_signature_softfail', true, $url ) |
|
1107 ) { |
|
1108 $signature_verification->add_data( $tmpfname, 'softfail-filename' ); |
|
1109 } else { |
|
1110 // Hard-fail. |
|
1111 unlink( $tmpfname ); |
|
1112 } |
|
1113 |
|
1114 return $signature_verification; |
|
1115 } |
|
1116 |
|
997 return $tmpfname; |
1117 return $tmpfname; |
998 } |
1118 } |
999 |
1119 |
1000 /** |
1120 /** |
1001 * Calculates and compares the MD5 of a file to its expected value. |
1121 * Calculates and compares the MD5 of a file to its expected value. |
1002 * |
1122 * |
1003 * @since 3.7.0 |
1123 * @since 3.7.0 |
1004 * |
1124 * |
1005 * @param string $filename The filename to check the MD5 of. |
1125 * @param string $filename The filename to check the MD5 of. |
1006 * @param string $expected_md5 The expected MD5 of the file, either a base64 encoded raw md5, or a hex-encoded md5 |
1126 * @param string $expected_md5 The expected MD5 of the file, either a base64-encoded raw md5, |
1007 * @return bool|object WP_Error on failure, true on success, false when the MD5 format is unknown/unexpected |
1127 * or a hex-encoded md5. |
1128 * @return bool|WP_Error True on success, false when the MD5 format is unknown/unexpected, |
|
1129 * WP_Error on failure. |
|
1008 */ |
1130 */ |
1009 function verify_file_md5( $filename, $expected_md5 ) { |
1131 function verify_file_md5( $filename, $expected_md5 ) { |
1010 if ( 32 == strlen( $expected_md5 ) ) |
1132 if ( 32 == strlen( $expected_md5 ) ) { |
1011 $expected_raw_md5 = pack( 'H*', $expected_md5 ); |
1133 $expected_raw_md5 = pack( 'H*', $expected_md5 ); |
1012 elseif ( 24 == strlen( $expected_md5 ) ) |
1134 } elseif ( 24 == strlen( $expected_md5 ) ) { |
1013 $expected_raw_md5 = base64_decode( $expected_md5 ); |
1135 $expected_raw_md5 = base64_decode( $expected_md5 ); |
1014 else |
1136 } else { |
1015 return false; // unknown format |
1137 return false; // unknown format |
1138 } |
|
1016 |
1139 |
1017 $file_md5 = md5_file( $filename, true ); |
1140 $file_md5 = md5_file( $filename, true ); |
1018 |
1141 |
1019 if ( $file_md5 === $expected_raw_md5 ) |
1142 if ( $file_md5 === $expected_raw_md5 ) { |
1020 return true; |
1143 return true; |
1144 } |
|
1021 |
1145 |
1022 return new WP_Error( 'md5_mismatch', sprintf( __( 'The checksum of the file (%1$s) does not match the expected checksum value (%2$s).' ), bin2hex( $file_md5 ), bin2hex( $expected_raw_md5 ) ) ); |
1146 return new WP_Error( 'md5_mismatch', sprintf( __( 'The checksum of the file (%1$s) does not match the expected checksum value (%2$s).' ), bin2hex( $file_md5 ), bin2hex( $expected_raw_md5 ) ) ); |
1023 } |
1147 } |
1024 |
1148 |
1025 /** |
1149 /** |
1026 * Unzips a specified ZIP file to a location on the Filesystem via the WordPress Filesystem Abstraction. |
1150 * Verifies the contents of a file against its ED25519 signature. |
1027 * Assumes that WP_Filesystem() has already been called and set up. Does not extract a root-level __MACOSX directory, if present. |
1151 * |
1028 * |
1152 * @since 5.2.0 |
1029 * Attempts to increase the PHP Memory limit to 256M before uncompressing, |
1153 * |
1030 * However, The most memory required shouldn't be much larger than the Archive itself. |
1154 * @param string $filename The file to validate. |
1155 * @param string|array $signatures A Signature provided for the file. |
|
1156 * @param string $filename_for_errors A friendly filename for errors. Optional. |
|
1157 * |
|
1158 * @return bool|WP_Error true on success, false if verificaiton not attempted, or WP_Error describing an error condition. |
|
1159 */ |
|
1160 function verify_file_signature( $filename, $signatures, $filename_for_errors = false ) { |
|
1161 if ( ! $filename_for_errors ) { |
|
1162 $filename_for_errors = wp_basename( $filename ); |
|
1163 } |
|
1164 |
|
1165 // Check we can process signatures. |
|
1166 if ( ! function_exists( 'sodium_crypto_sign_verify_detached' ) || ! in_array( 'sha384', array_map( 'strtolower', hash_algos() ) ) ) { |
|
1167 return new WP_Error( |
|
1168 'signature_verification_unsupported', |
|
1169 sprintf( |
|
1170 /* translators: %s: The filename of the package. */ |
|
1171 __( 'The authenticity of %s could not be verified as signature verification is unavailable on this system.' ), |
|
1172 '<span class="code">' . esc_html( $filename_for_errors ) . '</span>' |
|
1173 ), |
|
1174 ( ! function_exists( 'sodium_crypto_sign_verify_detached' ) ? 'sodium_crypto_sign_verify_detached' : 'sha384' ) |
|
1175 ); |
|
1176 } |
|
1177 |
|
1178 // Check for a edge-case affecting PHP Maths abilities |
|
1179 if ( |
|
1180 ! extension_loaded( 'sodium' ) && |
|
1181 in_array( PHP_VERSION_ID, [ 70200, 70201, 70202 ], true ) && |
|
1182 extension_loaded( 'opcache' ) |
|
1183 ) { |
|
1184 // Sodium_Compat isn't compatible with PHP 7.2.0~7.2.2 due to a bug in the PHP Opcache extension, bail early as it'll fail. |
|
1185 // https://bugs.php.net/bug.php?id=75938 |
|
1186 |
|
1187 return new WP_Error( |
|
1188 'signature_verification_unsupported', |
|
1189 sprintf( |
|
1190 /* translators: %s: The filename of the package. */ |
|
1191 __( 'The authenticity of %s could not be verified as signature verification is unavailable on this system.' ), |
|
1192 '<span class="code">' . esc_html( $filename_for_errors ) . '</span>' |
|
1193 ), |
|
1194 array( |
|
1195 'php' => phpversion(), |
|
1196 'sodium' => defined( 'SODIUM_LIBRARY_VERSION' ) ? SODIUM_LIBRARY_VERSION : ( defined( 'ParagonIE_Sodium_Compat::VERSION_STRING' ) ? ParagonIE_Sodium_Compat::VERSION_STRING : false ), |
|
1197 ) |
|
1198 ); |
|
1199 |
|
1200 } |
|
1201 |
|
1202 // Verify runtime speed of Sodium_Compat is acceptable. |
|
1203 if ( ! extension_loaded( 'sodium' ) && ! ParagonIE_Sodium_Compat::polyfill_is_fast() ) { |
|
1204 $sodium_compat_is_fast = false; |
|
1205 |
|
1206 // Allow for an old version of Sodium_Compat being loaded before the bundled WordPress one. |
|
1207 if ( method_exists( 'ParagonIE_Sodium_Compat', 'runtime_speed_test' ) ) { |
|
1208 // Run `ParagonIE_Sodium_Compat::runtime_speed_test()` in optimized integer mode, as that's what WordPress utilises during signing verifications. |
|
1209 $old_fastMult = ParagonIE_Sodium_Compat::$fastMult; |
|
1210 ParagonIE_Sodium_Compat::$fastMult = true; |
|
1211 $sodium_compat_is_fast = ParagonIE_Sodium_Compat::runtime_speed_test( 100, 10 ); |
|
1212 ParagonIE_Sodium_Compat::$fastMult = $old_fastMult; |
|
1213 } |
|
1214 |
|
1215 // This cannot be performed in a reasonable amount of time |
|
1216 // https://github.com/paragonie/sodium_compat#help-sodium_compat-is-slow-how-can-i-make-it-fast |
|
1217 if ( ! $sodium_compat_is_fast ) { |
|
1218 return new WP_Error( |
|
1219 'signature_verification_unsupported', |
|
1220 sprintf( |
|
1221 /* translators: %s: The filename of the package. */ |
|
1222 __( 'The authenticity of %s could not be verified as signature verification is unavailable on this system.' ), |
|
1223 '<span class="code">' . esc_html( $filename_for_errors ) . '</span>' |
|
1224 ), |
|
1225 array( |
|
1226 'php' => phpversion(), |
|
1227 'sodium' => defined( 'SODIUM_LIBRARY_VERSION' ) ? SODIUM_LIBRARY_VERSION : ( defined( 'ParagonIE_Sodium_Compat::VERSION_STRING' ) ? ParagonIE_Sodium_Compat::VERSION_STRING : false ), |
|
1228 'polyfill_is_fast' => false, |
|
1229 'max_execution_time' => ini_get( 'max_execution_time' ), |
|
1230 ) |
|
1231 ); |
|
1232 } |
|
1233 } |
|
1234 |
|
1235 if ( ! $signatures ) { |
|
1236 return new WP_Error( |
|
1237 'signature_verification_no_signature', |
|
1238 sprintf( |
|
1239 /* translators: %s: The filename of the package. */ |
|
1240 __( 'The authenticity of %s could not be verified as no signature was found.' ), |
|
1241 '<span class="code">' . esc_html( $filename_for_errors ) . '</span>' |
|
1242 ), |
|
1243 array( |
|
1244 'filename' => $filename_for_errors, |
|
1245 ) |
|
1246 ); |
|
1247 } |
|
1248 |
|
1249 $trusted_keys = wp_trusted_keys(); |
|
1250 $file_hash = hash_file( 'sha384', $filename, true ); |
|
1251 |
|
1252 mbstring_binary_safe_encoding(); |
|
1253 |
|
1254 $skipped_key = $skipped_signature = 0; |
|
1255 |
|
1256 foreach ( (array) $signatures as $signature ) { |
|
1257 $signature_raw = base64_decode( $signature ); |
|
1258 |
|
1259 // Ensure only valid-length signatures are considered. |
|
1260 if ( SODIUM_CRYPTO_SIGN_BYTES !== strlen( $signature_raw ) ) { |
|
1261 $skipped_signature++; |
|
1262 continue; |
|
1263 } |
|
1264 |
|
1265 foreach ( (array) $trusted_keys as $key ) { |
|
1266 $key_raw = base64_decode( $key ); |
|
1267 |
|
1268 // Only pass valid public keys through. |
|
1269 if ( SODIUM_CRYPTO_SIGN_PUBLICKEYBYTES !== strlen( $key_raw ) ) { |
|
1270 $skipped_key++; |
|
1271 continue; |
|
1272 } |
|
1273 |
|
1274 if ( sodium_crypto_sign_verify_detached( $signature_raw, $file_hash, $key_raw ) ) { |
|
1275 reset_mbstring_encoding(); |
|
1276 return true; |
|
1277 } |
|
1278 } |
|
1279 } |
|
1280 |
|
1281 reset_mbstring_encoding(); |
|
1282 |
|
1283 return new WP_Error( |
|
1284 'signature_verification_failed', |
|
1285 sprintf( |
|
1286 /* translators: %s: The filename of the package. */ |
|
1287 __( 'The authenticity of %s could not be verified.' ), |
|
1288 '<span class="code">' . esc_html( $filename_for_errors ) . '</span>' |
|
1289 ), |
|
1290 // Error data helpful for debugging: |
|
1291 array( |
|
1292 'filename' => $filename_for_errors, |
|
1293 'keys' => $trusted_keys, |
|
1294 'signatures' => $signatures, |
|
1295 'hash' => bin2hex( $file_hash ), |
|
1296 'skipped_key' => $skipped_key, |
|
1297 'skipped_sig' => $skipped_signature, |
|
1298 'php' => phpversion(), |
|
1299 'sodium' => defined( 'SODIUM_LIBRARY_VERSION' ) ? SODIUM_LIBRARY_VERSION : ( defined( 'ParagonIE_Sodium_Compat::VERSION_STRING' ) ? ParagonIE_Sodium_Compat::VERSION_STRING : false ), |
|
1300 ) |
|
1301 ); |
|
1302 } |
|
1303 |
|
1304 /** |
|
1305 * Retrieve the list of signing keys trusted by WordPress. |
|
1306 * |
|
1307 * @since 5.2.0 |
|
1308 * |
|
1309 * @return array List of base64-encoded Signing keys. |
|
1310 */ |
|
1311 function wp_trusted_keys() { |
|
1312 $trusted_keys = array(); |
|
1313 |
|
1314 if ( time() < 1617235200 ) { |
|
1315 // WordPress.org Key #1 - This key is only valid before April 1st, 2021. |
|
1316 $trusted_keys[] = 'fRPyrxb/MvVLbdsYi+OOEv4xc+Eqpsj+kkAS6gNOkI0='; |
|
1317 } |
|
1318 |
|
1319 // TODO: Add key #2 with longer expiration. |
|
1320 |
|
1321 /** |
|
1322 * Filter the valid Signing keys used to verify the contents of files. |
|
1323 * |
|
1324 * @since 5.2.0 |
|
1325 * |
|
1326 * @param array $trusted_keys The trusted keys that may sign packages. |
|
1327 */ |
|
1328 return apply_filters( 'wp_trusted_keys', $trusted_keys ); |
|
1329 } |
|
1330 |
|
1331 /** |
|
1332 * Unzips a specified ZIP file to a location on the filesystem via the WordPress |
|
1333 * Filesystem Abstraction. |
|
1334 * |
|
1335 * Assumes that WP_Filesystem() has already been called and set up. Does not extract |
|
1336 * a root-level __MACOSX directory, if present. |
|
1337 * |
|
1338 * Attempts to increase the PHP memory limit to 256M before uncompressing. However, |
|
1339 * the most memory required shouldn't be much larger than the archive itself. |
|
1031 * |
1340 * |
1032 * @since 2.5.0 |
1341 * @since 2.5.0 |
1033 * |
1342 * |
1034 * @global WP_Filesystem_Base $wp_filesystem Subclass |
1343 * @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass. |
1035 * |
1344 * |
1036 * @param string $file Full path and filename of zip archive |
1345 * @param string $file Full path and filename of ZIP archive. |
1037 * @param string $to Full path on the filesystem to extract archive to |
1346 * @param string $to Full path on the filesystem to extract archive to. |
1038 * @return mixed WP_Error on failure, True on success |
1347 * @return true|WP_Error True on success, WP_Error on failure. |
1039 */ |
1348 */ |
1040 function unzip_file($file, $to) { |
1349 function unzip_file( $file, $to ) { |
1041 global $wp_filesystem; |
1350 global $wp_filesystem; |
1042 |
1351 |
1043 if ( ! $wp_filesystem || !is_object($wp_filesystem) ) |
1352 if ( ! $wp_filesystem || ! is_object( $wp_filesystem ) ) { |
1044 return new WP_Error('fs_unavailable', __('Could not access filesystem.')); |
1353 return new WP_Error( 'fs_unavailable', __( 'Could not access filesystem.' ) ); |
1354 } |
|
1045 |
1355 |
1046 // Unzip can use a lot of memory, but not this much hopefully. |
1356 // Unzip can use a lot of memory, but not this much hopefully. |
1047 wp_raise_memory_limit( 'admin' ); |
1357 wp_raise_memory_limit( 'admin' ); |
1048 |
1358 |
1049 $needed_dirs = array(); |
1359 $needed_dirs = array(); |
1050 $to = trailingslashit($to); |
1360 $to = trailingslashit( $to ); |
1051 |
1361 |
1052 // Determine any parent dir's needed (of the upgrade directory) |
1362 // Determine any parent directories needed (of the upgrade directory). |
1053 if ( ! $wp_filesystem->is_dir($to) ) { //Only do parents if no children exist |
1363 if ( ! $wp_filesystem->is_dir( $to ) ) { // Only do parents if no children exist. |
1054 $path = preg_split('![/\\\]!', untrailingslashit($to)); |
1364 $path = preg_split( '![/\\\]!', untrailingslashit( $to ) ); |
1055 for ( $i = count($path); $i >= 0; $i-- ) { |
1365 for ( $i = count( $path ); $i >= 0; $i-- ) { |
1056 if ( empty($path[$i]) ) |
1366 if ( empty( $path[ $i ] ) ) { |
1057 continue; |
1367 continue; |
1058 |
1368 } |
1059 $dir = implode('/', array_slice($path, 0, $i+1) ); |
1369 |
1060 if ( preg_match('!^[a-z]:$!i', $dir) ) // Skip it if it looks like a Windows Drive letter. |
1370 $dir = implode( '/', array_slice( $path, 0, $i + 1 ) ); |
1371 if ( preg_match( '!^[a-z]:$!i', $dir ) ) { // Skip it if it looks like a Windows Drive letter. |
|
1061 continue; |
1372 continue; |
1062 |
1373 } |
1063 if ( ! $wp_filesystem->is_dir($dir) ) |
1374 |
1375 if ( ! $wp_filesystem->is_dir( $dir ) ) { |
|
1064 $needed_dirs[] = $dir; |
1376 $needed_dirs[] = $dir; |
1065 else |
1377 } else { |
1066 break; // A folder exists, therefor, we dont need the check the levels below this |
1378 break; // A folder exists, therefore we don't need to check the levels below this. |
1379 } |
|
1067 } |
1380 } |
1068 } |
1381 } |
1069 |
1382 |
1070 /** |
1383 /** |
1071 * Filters whether to use ZipArchive to unzip archives. |
1384 * Filters whether to use ZipArchive to unzip archives. |
1073 * @since 3.0.0 |
1386 * @since 3.0.0 |
1074 * |
1387 * |
1075 * @param bool $ziparchive Whether to use ZipArchive. Default true. |
1388 * @param bool $ziparchive Whether to use ZipArchive. Default true. |
1076 */ |
1389 */ |
1077 if ( class_exists( 'ZipArchive', false ) && apply_filters( 'unzip_file_use_ziparchive', true ) ) { |
1390 if ( class_exists( 'ZipArchive', false ) && apply_filters( 'unzip_file_use_ziparchive', true ) ) { |
1078 $result = _unzip_file_ziparchive($file, $to, $needed_dirs); |
1391 $result = _unzip_file_ziparchive( $file, $to, $needed_dirs ); |
1079 if ( true === $result ) { |
1392 if ( true === $result ) { |
1080 return $result; |
1393 return $result; |
1081 } elseif ( is_wp_error($result) ) { |
1394 } elseif ( is_wp_error( $result ) ) { |
1082 if ( 'incompatible_archive' != $result->get_error_code() ) |
1395 if ( 'incompatible_archive' != $result->get_error_code() ) { |
1083 return $result; |
1396 return $result; |
1397 } |
|
1084 } |
1398 } |
1085 } |
1399 } |
1086 // Fall through to PclZip if ZipArchive is not available, or encountered an error opening the file. |
1400 // Fall through to PclZip if ZipArchive is not available, or encountered an error opening the file. |
1087 return _unzip_file_pclzip($file, $to, $needed_dirs); |
1401 return _unzip_file_pclzip( $file, $to, $needed_dirs ); |
1088 } |
1402 } |
1089 |
1403 |
1090 /** |
1404 /** |
1091 * This function should not be called directly, use unzip_file instead. Attempts to unzip an archive using the ZipArchive class. |
1405 * Attempts to unzip an archive using the ZipArchive class. |
1406 * |
|
1407 * This function should not be called directly, use `unzip_file()` instead. |
|
1408 * |
|
1092 * Assumes that WP_Filesystem() has already been called and set up. |
1409 * Assumes that WP_Filesystem() has already been called and set up. |
1093 * |
1410 * |
1094 * @since 3.0.0 |
1411 * @since 3.0.0 |
1095 * @see unzip_file |
1412 * @see unzip_file() |
1096 * @access private |
1413 * @access private |
1097 * |
1414 * |
1098 * @global WP_Filesystem_Base $wp_filesystem Subclass |
1415 * @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass. |
1099 * |
1416 * |
1100 * @param string $file Full path and filename of zip archive |
1417 * @param string $file Full path and filename of ZIP archive. |
1101 * @param string $to Full path on the filesystem to extract archive to |
1418 * @param string $to Full path on the filesystem to extract archive to. |
1102 * @param array $needed_dirs A partial list of required folders needed to be created. |
1419 * @param array $needed_dirs A partial list of required folders needed to be created. |
1103 * @return mixed WP_Error on failure, True on success |
1420 * @return true|WP_Error True on success, WP_Error on failure. |
1104 */ |
1421 */ |
1105 function _unzip_file_ziparchive($file, $to, $needed_dirs = array() ) { |
1422 function _unzip_file_ziparchive( $file, $to, $needed_dirs = array() ) { |
1106 global $wp_filesystem; |
1423 global $wp_filesystem; |
1107 |
1424 |
1108 $z = new ZipArchive(); |
1425 $z = new ZipArchive(); |
1109 |
1426 |
1110 $zopen = $z->open( $file, ZIPARCHIVE::CHECKCONS ); |
1427 $zopen = $z->open( $file, ZIPARCHIVE::CHECKCONS ); |
1111 if ( true !== $zopen ) |
1428 if ( true !== $zopen ) { |
1112 return new WP_Error( 'incompatible_archive', __( 'Incompatible Archive.' ), array( 'ziparchive_error' => $zopen ) ); |
1429 return new WP_Error( 'incompatible_archive', __( 'Incompatible Archive.' ), array( 'ziparchive_error' => $zopen ) ); |
1430 } |
|
1113 |
1431 |
1114 $uncompressed_size = 0; |
1432 $uncompressed_size = 0; |
1115 |
1433 |
1116 for ( $i = 0; $i < $z->numFiles; $i++ ) { |
1434 for ( $i = 0; $i < $z->numFiles; $i++ ) { |
1117 if ( ! $info = $z->statIndex($i) ) |
1435 if ( ! $info = $z->statIndex( $i ) ) { |
1118 return new WP_Error( 'stat_failed_ziparchive', __( 'Could not retrieve file from archive.' ) ); |
1436 return new WP_Error( 'stat_failed_ziparchive', __( 'Could not retrieve file from archive.' ) ); |
1119 |
1437 } |
1120 if ( '__MACOSX/' === substr($info['name'], 0, 9) ) // Skip the OS X-created __MACOSX directory |
1438 |
1439 if ( '__MACOSX/' === substr( $info['name'], 0, 9 ) ) { // Skip the OS X-created __MACOSX directory |
|
1121 continue; |
1440 continue; |
1441 } |
|
1122 |
1442 |
1123 // Don't extract invalid files: |
1443 // Don't extract invalid files: |
1124 if ( 0 !== validate_file( $info['name'] ) ) { |
1444 if ( 0 !== validate_file( $info['name'] ) ) { |
1125 continue; |
1445 continue; |
1126 } |
1446 } |
1141 * A disk that has zero free bytes has bigger problems. |
1461 * A disk that has zero free bytes has bigger problems. |
1142 * Require we have enough space to unzip the file and copy its contents, with a 10% buffer. |
1462 * Require we have enough space to unzip the file and copy its contents, with a 10% buffer. |
1143 */ |
1463 */ |
1144 if ( wp_doing_cron() ) { |
1464 if ( wp_doing_cron() ) { |
1145 $available_space = @disk_free_space( WP_CONTENT_DIR ); |
1465 $available_space = @disk_free_space( WP_CONTENT_DIR ); |
1146 if ( $available_space && ( $uncompressed_size * 2.1 ) > $available_space ) |
1466 if ( $available_space && ( $uncompressed_size * 2.1 ) > $available_space ) { |
1147 return new WP_Error( 'disk_full_unzip_file', __( 'Could not copy files. You may have run out of disk space.' ), compact( 'uncompressed_size', 'available_space' ) ); |
1467 return new WP_Error( 'disk_full_unzip_file', __( 'Could not copy files. You may have run out of disk space.' ), compact( 'uncompressed_size', 'available_space' ) ); |
1148 } |
1468 } |
1149 |
1469 } |
1150 $needed_dirs = array_unique($needed_dirs); |
1470 |
1471 $needed_dirs = array_unique( $needed_dirs ); |
|
1151 foreach ( $needed_dirs as $dir ) { |
1472 foreach ( $needed_dirs as $dir ) { |
1152 // Check the parent folders of the folders all exist within the creation array. |
1473 // Check the parent folders of the folders all exist within the creation array. |
1153 if ( untrailingslashit($to) == $dir ) // Skip over the working directory, We know this exists (or will exist) |
1474 if ( untrailingslashit( $to ) == $dir ) { // Skip over the working directory, We know this exists (or will exist) |
1154 continue; |
1475 continue; |
1155 if ( strpos($dir, $to) === false ) // If the directory is not within the working directory, Skip it |
1476 } |
1477 if ( strpos( $dir, $to ) === false ) { // If the directory is not within the working directory, Skip it |
|
1156 continue; |
1478 continue; |
1157 |
1479 } |
1158 $parent_folder = dirname($dir); |
1480 |
1159 while ( !empty($parent_folder) && untrailingslashit($to) != $parent_folder && !in_array($parent_folder, $needed_dirs) ) { |
1481 $parent_folder = dirname( $dir ); |
1482 while ( ! empty( $parent_folder ) && untrailingslashit( $to ) != $parent_folder && ! in_array( $parent_folder, $needed_dirs ) ) { |
|
1160 $needed_dirs[] = $parent_folder; |
1483 $needed_dirs[] = $parent_folder; |
1161 $parent_folder = dirname($parent_folder); |
1484 $parent_folder = dirname( $parent_folder ); |
1162 } |
1485 } |
1163 } |
1486 } |
1164 asort($needed_dirs); |
1487 asort( $needed_dirs ); |
1165 |
1488 |
1166 // Create those directories if need be: |
1489 // Create those directories if need be: |
1167 foreach ( $needed_dirs as $_dir ) { |
1490 foreach ( $needed_dirs as $_dir ) { |
1168 // Only check to see if the Dir exists upon creation failure. Less I/O this way. |
1491 // Only check to see if the Dir exists upon creation failure. Less I/O this way. |
1169 if ( ! $wp_filesystem->mkdir( $_dir, FS_CHMOD_DIR ) && ! $wp_filesystem->is_dir( $_dir ) ) { |
1492 if ( ! $wp_filesystem->mkdir( $_dir, FS_CHMOD_DIR ) && ! $wp_filesystem->is_dir( $_dir ) ) { |
1170 return new WP_Error( 'mkdir_failed_ziparchive', __( 'Could not create directory.' ), substr( $_dir, strlen( $to ) ) ); |
1493 return new WP_Error( 'mkdir_failed_ziparchive', __( 'Could not create directory.' ), substr( $_dir, strlen( $to ) ) ); |
1171 } |
1494 } |
1172 } |
1495 } |
1173 unset($needed_dirs); |
1496 unset( $needed_dirs ); |
1174 |
1497 |
1175 for ( $i = 0; $i < $z->numFiles; $i++ ) { |
1498 for ( $i = 0; $i < $z->numFiles; $i++ ) { |
1176 if ( ! $info = $z->statIndex($i) ) |
1499 if ( ! $info = $z->statIndex( $i ) ) { |
1177 return new WP_Error( 'stat_failed_ziparchive', __( 'Could not retrieve file from archive.' ) ); |
1500 return new WP_Error( 'stat_failed_ziparchive', __( 'Could not retrieve file from archive.' ) ); |
1178 |
1501 } |
1179 if ( '/' == substr($info['name'], -1) ) // directory |
1502 |
1503 if ( '/' == substr( $info['name'], -1 ) ) { // directory |
|
1180 continue; |
1504 continue; |
1181 |
1505 } |
1182 if ( '__MACOSX/' === substr($info['name'], 0, 9) ) // Don't extract the OS X-created __MACOSX directory files |
1506 |
1507 if ( '__MACOSX/' === substr( $info['name'], 0, 9 ) ) { // Don't extract the OS X-created __MACOSX directory files |
|
1183 continue; |
1508 continue; |
1509 } |
|
1184 |
1510 |
1185 // Don't extract invalid files: |
1511 // Don't extract invalid files: |
1186 if ( 0 !== validate_file( $info['name'] ) ) { |
1512 if ( 0 !== validate_file( $info['name'] ) ) { |
1187 continue; |
1513 continue; |
1188 } |
1514 } |
1189 |
1515 |
1190 $contents = $z->getFromIndex($i); |
1516 $contents = $z->getFromIndex( $i ); |
1191 if ( false === $contents ) |
1517 if ( false === $contents ) { |
1192 return new WP_Error( 'extract_failed_ziparchive', __( 'Could not extract file from archive.' ), $info['name'] ); |
1518 return new WP_Error( 'extract_failed_ziparchive', __( 'Could not extract file from archive.' ), $info['name'] ); |
1193 |
1519 } |
1194 if ( ! $wp_filesystem->put_contents( $to . $info['name'], $contents, FS_CHMOD_FILE) ) |
1520 |
1521 if ( ! $wp_filesystem->put_contents( $to . $info['name'], $contents, FS_CHMOD_FILE ) ) { |
|
1195 return new WP_Error( 'copy_failed_ziparchive', __( 'Could not copy file.' ), $info['name'] ); |
1522 return new WP_Error( 'copy_failed_ziparchive', __( 'Could not copy file.' ), $info['name'] ); |
1523 } |
|
1196 } |
1524 } |
1197 |
1525 |
1198 $z->close(); |
1526 $z->close(); |
1199 |
1527 |
1200 return true; |
1528 return true; |
1201 } |
1529 } |
1202 |
1530 |
1203 /** |
1531 /** |
1204 * This function should not be called directly, use unzip_file instead. Attempts to unzip an archive using the PclZip library. |
1532 * Attempts to unzip an archive using the PclZip library. |
1533 * |
|
1534 * This function should not be called directly, use `unzip_file()` instead. |
|
1535 * |
|
1205 * Assumes that WP_Filesystem() has already been called and set up. |
1536 * Assumes that WP_Filesystem() has already been called and set up. |
1206 * |
1537 * |
1207 * @since 3.0.0 |
1538 * @since 3.0.0 |
1208 * @see unzip_file |
1539 * @see unzip_file() |
1209 * @access private |
1540 * @access private |
1210 * |
1541 * |
1211 * @global WP_Filesystem_Base $wp_filesystem Subclass |
1542 * @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass. |
1212 * |
1543 * |
1213 * @param string $file Full path and filename of zip archive |
1544 * @param string $file Full path and filename of ZIP archive. |
1214 * @param string $to Full path on the filesystem to extract archive to |
1545 * @param string $to Full path on the filesystem to extract archive to. |
1215 * @param array $needed_dirs A partial list of required folders needed to be created. |
1546 * @param array $needed_dirs A partial list of required folders needed to be created. |
1216 * @return mixed WP_Error on failure, True on success |
1547 * @return true|WP_Error True on success, WP_Error on failure. |
1217 */ |
1548 */ |
1218 function _unzip_file_pclzip($file, $to, $needed_dirs = array()) { |
1549 function _unzip_file_pclzip( $file, $to, $needed_dirs = array() ) { |
1219 global $wp_filesystem; |
1550 global $wp_filesystem; |
1220 |
1551 |
1221 mbstring_binary_safe_encoding(); |
1552 mbstring_binary_safe_encoding(); |
1222 |
1553 |
1223 require_once(ABSPATH . 'wp-admin/includes/class-pclzip.php'); |
1554 require_once( ABSPATH . 'wp-admin/includes/class-pclzip.php' ); |
1224 |
1555 |
1225 $archive = new PclZip($file); |
1556 $archive = new PclZip( $file ); |
1226 |
1557 |
1227 $archive_files = $archive->extract(PCLZIP_OPT_EXTRACT_AS_STRING); |
1558 $archive_files = $archive->extract( PCLZIP_OPT_EXTRACT_AS_STRING ); |
1228 |
1559 |
1229 reset_mbstring_encoding(); |
1560 reset_mbstring_encoding(); |
1230 |
1561 |
1231 // Is the archive valid? |
1562 // Is the archive valid? |
1232 if ( !is_array($archive_files) ) |
1563 if ( ! is_array( $archive_files ) ) { |
1233 return new WP_Error('incompatible_archive', __('Incompatible Archive.'), $archive->errorInfo(true)); |
1564 return new WP_Error( 'incompatible_archive', __( 'Incompatible Archive.' ), $archive->errorInfo( true ) ); |
1234 |
1565 } |
1235 if ( 0 == count($archive_files) ) |
1566 |
1567 if ( 0 == count( $archive_files ) ) { |
|
1236 return new WP_Error( 'empty_archive_pclzip', __( 'Empty archive.' ) ); |
1568 return new WP_Error( 'empty_archive_pclzip', __( 'Empty archive.' ) ); |
1569 } |
|
1237 |
1570 |
1238 $uncompressed_size = 0; |
1571 $uncompressed_size = 0; |
1239 |
1572 |
1240 // Determine any children directories needed (From within the archive) |
1573 // Determine any children directories needed (From within the archive) |
1241 foreach ( $archive_files as $file ) { |
1574 foreach ( $archive_files as $file ) { |
1242 if ( '__MACOSX/' === substr($file['filename'], 0, 9) ) // Skip the OS X-created __MACOSX directory |
1575 if ( '__MACOSX/' === substr( $file['filename'], 0, 9 ) ) { // Skip the OS X-created __MACOSX directory |
1243 continue; |
1576 continue; |
1577 } |
|
1244 |
1578 |
1245 $uncompressed_size += $file['size']; |
1579 $uncompressed_size += $file['size']; |
1246 |
1580 |
1247 $needed_dirs[] = $to . untrailingslashit( $file['folder'] ? $file['filename'] : dirname($file['filename']) ); |
1581 $needed_dirs[] = $to . untrailingslashit( $file['folder'] ? $file['filename'] : dirname( $file['filename'] ) ); |
1248 } |
1582 } |
1249 |
1583 |
1250 /* |
1584 /* |
1251 * disk_free_space() could return false. Assume that any falsey value is an error. |
1585 * disk_free_space() could return false. Assume that any falsey value is an error. |
1252 * A disk that has zero free bytes has bigger problems. |
1586 * A disk that has zero free bytes has bigger problems. |
1253 * Require we have enough space to unzip the file and copy its contents, with a 10% buffer. |
1587 * Require we have enough space to unzip the file and copy its contents, with a 10% buffer. |
1254 */ |
1588 */ |
1255 if ( wp_doing_cron() ) { |
1589 if ( wp_doing_cron() ) { |
1256 $available_space = @disk_free_space( WP_CONTENT_DIR ); |
1590 $available_space = @disk_free_space( WP_CONTENT_DIR ); |
1257 if ( $available_space && ( $uncompressed_size * 2.1 ) > $available_space ) |
1591 if ( $available_space && ( $uncompressed_size * 2.1 ) > $available_space ) { |
1258 return new WP_Error( 'disk_full_unzip_file', __( 'Could not copy files. You may have run out of disk space.' ), compact( 'uncompressed_size', 'available_space' ) ); |
1592 return new WP_Error( 'disk_full_unzip_file', __( 'Could not copy files. You may have run out of disk space.' ), compact( 'uncompressed_size', 'available_space' ) ); |
1259 } |
1593 } |
1260 |
1594 } |
1261 $needed_dirs = array_unique($needed_dirs); |
1595 |
1596 $needed_dirs = array_unique( $needed_dirs ); |
|
1262 foreach ( $needed_dirs as $dir ) { |
1597 foreach ( $needed_dirs as $dir ) { |
1263 // Check the parent folders of the folders all exist within the creation array. |
1598 // Check the parent folders of the folders all exist within the creation array. |
1264 if ( untrailingslashit($to) == $dir ) // Skip over the working directory, We know this exists (or will exist) |
1599 if ( untrailingslashit( $to ) == $dir ) { // Skip over the working directory, We know this exists (or will exist) |
1265 continue; |
1600 continue; |
1266 if ( strpos($dir, $to) === false ) // If the directory is not within the working directory, Skip it |
1601 } |
1602 if ( strpos( $dir, $to ) === false ) { // If the directory is not within the working directory, Skip it |
|
1267 continue; |
1603 continue; |
1268 |
1604 } |
1269 $parent_folder = dirname($dir); |
1605 |
1270 while ( !empty($parent_folder) && untrailingslashit($to) != $parent_folder && !in_array($parent_folder, $needed_dirs) ) { |
1606 $parent_folder = dirname( $dir ); |
1607 while ( ! empty( $parent_folder ) && untrailingslashit( $to ) != $parent_folder && ! in_array( $parent_folder, $needed_dirs ) ) { |
|
1271 $needed_dirs[] = $parent_folder; |
1608 $needed_dirs[] = $parent_folder; |
1272 $parent_folder = dirname($parent_folder); |
1609 $parent_folder = dirname( $parent_folder ); |
1273 } |
1610 } |
1274 } |
1611 } |
1275 asort($needed_dirs); |
1612 asort( $needed_dirs ); |
1276 |
1613 |
1277 // Create those directories if need be: |
1614 // Create those directories if need be: |
1278 foreach ( $needed_dirs as $_dir ) { |
1615 foreach ( $needed_dirs as $_dir ) { |
1279 // Only check to see if the dir exists upon creation failure. Less I/O this way. |
1616 // Only check to see if the dir exists upon creation failure. Less I/O this way. |
1280 if ( ! $wp_filesystem->mkdir( $_dir, FS_CHMOD_DIR ) && ! $wp_filesystem->is_dir( $_dir ) ) |
1617 if ( ! $wp_filesystem->mkdir( $_dir, FS_CHMOD_DIR ) && ! $wp_filesystem->is_dir( $_dir ) ) { |
1281 return new WP_Error( 'mkdir_failed_pclzip', __( 'Could not create directory.' ), substr( $_dir, strlen( $to ) ) ); |
1618 return new WP_Error( 'mkdir_failed_pclzip', __( 'Could not create directory.' ), substr( $_dir, strlen( $to ) ) ); |
1282 } |
1619 } |
1283 unset($needed_dirs); |
1620 } |
1621 unset( $needed_dirs ); |
|
1284 |
1622 |
1285 // Extract the files from the zip |
1623 // Extract the files from the zip |
1286 foreach ( $archive_files as $file ) { |
1624 foreach ( $archive_files as $file ) { |
1287 if ( $file['folder'] ) |
1625 if ( $file['folder'] ) { |
1288 continue; |
1626 continue; |
1289 |
1627 } |
1290 if ( '__MACOSX/' === substr($file['filename'], 0, 9) ) // Don't extract the OS X-created __MACOSX directory files |
1628 |
1629 if ( '__MACOSX/' === substr( $file['filename'], 0, 9 ) ) { // Don't extract the OS X-created __MACOSX directory files |
|
1291 continue; |
1630 continue; |
1631 } |
|
1292 |
1632 |
1293 // Don't extract invalid files: |
1633 // Don't extract invalid files: |
1294 if ( 0 !== validate_file( $file['filename'] ) ) { |
1634 if ( 0 !== validate_file( $file['filename'] ) ) { |
1295 continue; |
1635 continue; |
1296 } |
1636 } |
1297 |
1637 |
1298 if ( ! $wp_filesystem->put_contents( $to . $file['filename'], $file['content'], FS_CHMOD_FILE) ) |
1638 if ( ! $wp_filesystem->put_contents( $to . $file['filename'], $file['content'], FS_CHMOD_FILE ) ) { |
1299 return new WP_Error( 'copy_failed_pclzip', __( 'Could not copy file.' ), $file['filename'] ); |
1639 return new WP_Error( 'copy_failed_pclzip', __( 'Could not copy file.' ), $file['filename'] ); |
1640 } |
|
1300 } |
1641 } |
1301 return true; |
1642 return true; |
1302 } |
1643 } |
1303 |
1644 |
1304 /** |
1645 /** |
1305 * Copies a directory from one location to another via the WordPress Filesystem Abstraction. |
1646 * Copies a directory from one location to another via the WordPress Filesystem |
1647 * Abstraction. |
|
1648 * |
|
1306 * Assumes that WP_Filesystem() has already been called and setup. |
1649 * Assumes that WP_Filesystem() has already been called and setup. |
1307 * |
1650 * |
1308 * @since 2.5.0 |
1651 * @since 2.5.0 |
1309 * |
1652 * |
1310 * @global WP_Filesystem_Base $wp_filesystem Subclass |
1653 * @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass. |
1311 * |
1654 * |
1312 * @param string $from source directory |
1655 * @param string $from Source directory. |
1313 * @param string $to destination directory |
1656 * @param string $to Destination directory. |
1314 * @param array $skip_list a list of files/folders to skip copying |
1657 * @param array $skip_list A list of files/folders to skip copying. |
1315 * @return mixed WP_Error on failure, True on success. |
1658 * @return true|WP_Error True on success, WP_Error on failure. |
1316 */ |
1659 */ |
1317 function copy_dir($from, $to, $skip_list = array() ) { |
1660 function copy_dir( $from, $to, $skip_list = array() ) { |
1318 global $wp_filesystem; |
1661 global $wp_filesystem; |
1319 |
1662 |
1320 $dirlist = $wp_filesystem->dirlist($from); |
1663 $dirlist = $wp_filesystem->dirlist( $from ); |
1321 |
1664 |
1322 $from = trailingslashit($from); |
1665 $from = trailingslashit( $from ); |
1323 $to = trailingslashit($to); |
1666 $to = trailingslashit( $to ); |
1324 |
1667 |
1325 foreach ( (array) $dirlist as $filename => $fileinfo ) { |
1668 foreach ( (array) $dirlist as $filename => $fileinfo ) { |
1326 if ( in_array( $filename, $skip_list ) ) |
1669 if ( in_array( $filename, $skip_list ) ) { |
1327 continue; |
1670 continue; |
1671 } |
|
1328 |
1672 |
1329 if ( 'f' == $fileinfo['type'] ) { |
1673 if ( 'f' == $fileinfo['type'] ) { |
1330 if ( ! $wp_filesystem->copy($from . $filename, $to . $filename, true, FS_CHMOD_FILE) ) { |
1674 if ( ! $wp_filesystem->copy( $from . $filename, $to . $filename, true, FS_CHMOD_FILE ) ) { |
1331 // If copy failed, chmod file to 0644 and try again. |
1675 // If copy failed, chmod file to 0644 and try again. |
1332 $wp_filesystem->chmod( $to . $filename, FS_CHMOD_FILE ); |
1676 $wp_filesystem->chmod( $to . $filename, FS_CHMOD_FILE ); |
1333 if ( ! $wp_filesystem->copy($from . $filename, $to . $filename, true, FS_CHMOD_FILE) ) |
1677 if ( ! $wp_filesystem->copy( $from . $filename, $to . $filename, true, FS_CHMOD_FILE ) ) { |
1334 return new WP_Error( 'copy_failed_copy_dir', __( 'Could not copy file.' ), $to . $filename ); |
1678 return new WP_Error( 'copy_failed_copy_dir', __( 'Could not copy file.' ), $to . $filename ); |
1679 } |
|
1335 } |
1680 } |
1336 } elseif ( 'd' == $fileinfo['type'] ) { |
1681 } elseif ( 'd' == $fileinfo['type'] ) { |
1337 if ( !$wp_filesystem->is_dir($to . $filename) ) { |
1682 if ( ! $wp_filesystem->is_dir( $to . $filename ) ) { |
1338 if ( !$wp_filesystem->mkdir($to . $filename, FS_CHMOD_DIR) ) |
1683 if ( ! $wp_filesystem->mkdir( $to . $filename, FS_CHMOD_DIR ) ) { |
1339 return new WP_Error( 'mkdir_failed_copy_dir', __( 'Could not create directory.' ), $to . $filename ); |
1684 return new WP_Error( 'mkdir_failed_copy_dir', __( 'Could not create directory.' ), $to . $filename ); |
1685 } |
|
1340 } |
1686 } |
1341 |
1687 |
1342 // generate the $sub_skip_list for the subdirectory as a sub-set of the existing $skip_list |
1688 // generate the $sub_skip_list for the subdirectory as a sub-set of the existing $skip_list |
1343 $sub_skip_list = array(); |
1689 $sub_skip_list = array(); |
1344 foreach ( $skip_list as $skip_item ) { |
1690 foreach ( $skip_list as $skip_item ) { |
1345 if ( 0 === strpos( $skip_item, $filename . '/' ) ) |
1691 if ( 0 === strpos( $skip_item, $filename . '/' ) ) { |
1346 $sub_skip_list[] = preg_replace( '!^' . preg_quote( $filename, '!' ) . '/!i', '', $skip_item ); |
1692 $sub_skip_list[] = preg_replace( '!^' . preg_quote( $filename, '!' ) . '/!i', '', $skip_item ); |
1693 } |
|
1347 } |
1694 } |
1348 |
1695 |
1349 $result = copy_dir($from . $filename, $to . $filename, $sub_skip_list); |
1696 $result = copy_dir( $from . $filename, $to . $filename, $sub_skip_list ); |
1350 if ( is_wp_error($result) ) |
1697 if ( is_wp_error( $result ) ) { |
1351 return $result; |
1698 return $result; |
1699 } |
|
1352 } |
1700 } |
1353 } |
1701 } |
1354 return true; |
1702 return true; |
1355 } |
1703 } |
1356 |
1704 |
1357 /** |
1705 /** |
1358 * Initialises and connects the WordPress Filesystem Abstraction classes. |
1706 * Initialises and connects the WordPress Filesystem Abstraction classes. |
1707 * |
|
1359 * This function will include the chosen transport and attempt connecting. |
1708 * This function will include the chosen transport and attempt connecting. |
1360 * |
1709 * |
1361 * Plugins may add extra transports, And force WordPress to use them by returning |
1710 * Plugins may add extra transports, And force WordPress to use them by returning |
1362 * the filename via the {@see 'filesystem_method_file'} filter. |
1711 * the filename via the {@see 'filesystem_method_file'} filter. |
1363 * |
1712 * |
1364 * @since 2.5.0 |
1713 * @since 2.5.0 |
1365 * |
1714 * |
1366 * @global WP_Filesystem_Base $wp_filesystem Subclass |
1715 * @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass. |
1367 * |
1716 * |
1368 * @param array|false $args Optional. Connection args, These are passed directly to |
1717 * @param array|false $args Optional. Connection args, These are passed directly to |
1369 * the `WP_Filesystem_*()` classes. Default false. |
1718 * the `WP_Filesystem_*()` classes. Default false. |
1370 * @param string|false $context Optional. Context for get_filesystem_method(). Default false. |
1719 * @param string|false $context Optional. Context for get_filesystem_method(). Default false. |
1371 * @param bool $allow_relaxed_file_ownership Optional. Whether to allow Group/World writable. Default false. |
1720 * @param bool $allow_relaxed_file_ownership Optional. Whether to allow Group/World writable. Default false. |
1372 * @return null|bool false on failure, true on success. |
1721 * @return bool|null True on success, false on failure, null if the filesystem method class file does not exist. |
1373 */ |
1722 */ |
1374 function WP_Filesystem( $args = false, $context = false, $allow_relaxed_file_ownership = false ) { |
1723 function WP_Filesystem( $args = false, $context = false, $allow_relaxed_file_ownership = false ) { |
1375 global $wp_filesystem; |
1724 global $wp_filesystem; |
1376 |
1725 |
1377 require_once(ABSPATH . 'wp-admin/includes/class-wp-filesystem-base.php'); |
1726 require_once( ABSPATH . 'wp-admin/includes/class-wp-filesystem-base.php' ); |
1378 |
1727 |
1379 $method = get_filesystem_method( $args, $context, $allow_relaxed_file_ownership ); |
1728 $method = get_filesystem_method( $args, $context, $allow_relaxed_file_ownership ); |
1380 |
1729 |
1381 if ( ! $method ) |
1730 if ( ! $method ) { |
1382 return false; |
1731 return false; |
1732 } |
|
1383 |
1733 |
1384 if ( ! class_exists( "WP_Filesystem_$method" ) ) { |
1734 if ( ! class_exists( "WP_Filesystem_$method" ) ) { |
1385 |
1735 |
1386 /** |
1736 /** |
1387 * Filters the path for a specific filesystem method class file. |
1737 * Filters the path for a specific filesystem method class file. |
1393 * @param string $path Path to the specific filesystem method class file. |
1743 * @param string $path Path to the specific filesystem method class file. |
1394 * @param string $method The filesystem method to use. |
1744 * @param string $method The filesystem method to use. |
1395 */ |
1745 */ |
1396 $abstraction_file = apply_filters( 'filesystem_method_file', ABSPATH . 'wp-admin/includes/class-wp-filesystem-' . $method . '.php', $method ); |
1746 $abstraction_file = apply_filters( 'filesystem_method_file', ABSPATH . 'wp-admin/includes/class-wp-filesystem-' . $method . '.php', $method ); |
1397 |
1747 |
1398 if ( ! file_exists($abstraction_file) ) |
1748 if ( ! file_exists( $abstraction_file ) ) { |
1399 return; |
1749 return; |
1400 |
1750 } |
1401 require_once($abstraction_file); |
1751 |
1752 require_once( $abstraction_file ); |
|
1402 } |
1753 } |
1403 $method = "WP_Filesystem_$method"; |
1754 $method = "WP_Filesystem_$method"; |
1404 |
1755 |
1405 $wp_filesystem = new $method($args); |
1756 $wp_filesystem = new $method( $args ); |
1406 |
1757 |
1407 //Define the timeouts for the connections. Only available after the construct is called to allow for per-transport overriding of the default. |
1758 //Define the timeouts for the connections. Only available after the construct is called to allow for per-transport overriding of the default. |
1408 if ( ! defined('FS_CONNECT_TIMEOUT') ) |
1759 if ( ! defined( 'FS_CONNECT_TIMEOUT' ) ) { |
1409 define('FS_CONNECT_TIMEOUT', 30); |
1760 define( 'FS_CONNECT_TIMEOUT', 30 ); |
1410 if ( ! defined('FS_TIMEOUT') ) |
1761 } |
1411 define('FS_TIMEOUT', 30); |
1762 if ( ! defined( 'FS_TIMEOUT' ) ) { |
1412 |
1763 define( 'FS_TIMEOUT', 30 ); |
1413 if ( is_wp_error($wp_filesystem->errors) && $wp_filesystem->errors->get_error_code() ) |
1764 } |
1765 |
|
1766 if ( is_wp_error( $wp_filesystem->errors ) && $wp_filesystem->errors->has_errors() ) { |
|
1414 return false; |
1767 return false; |
1415 |
1768 } |
1416 if ( !$wp_filesystem->connect() ) |
1769 |
1770 if ( ! $wp_filesystem->connect() ) { |
|
1417 return false; //There was an error connecting to the server. |
1771 return false; //There was an error connecting to the server. |
1772 } |
|
1418 |
1773 |
1419 // Set the permission constants if not already set. |
1774 // Set the permission constants if not already set. |
1420 if ( ! defined('FS_CHMOD_DIR') ) |
1775 if ( ! defined( 'FS_CHMOD_DIR' ) ) { |
1421 define('FS_CHMOD_DIR', ( fileperms( ABSPATH ) & 0777 | 0755 ) ); |
1776 define( 'FS_CHMOD_DIR', ( fileperms( ABSPATH ) & 0777 | 0755 ) ); |
1422 if ( ! defined('FS_CHMOD_FILE') ) |
1777 } |
1423 define('FS_CHMOD_FILE', ( fileperms( ABSPATH . 'index.php' ) & 0777 | 0644 ) ); |
1778 if ( ! defined( 'FS_CHMOD_FILE' ) ) { |
1779 define( 'FS_CHMOD_FILE', ( fileperms( ABSPATH . 'index.php' ) & 0777 | 0644 ) ); |
|
1780 } |
|
1424 |
1781 |
1425 return true; |
1782 return true; |
1426 } |
1783 } |
1427 |
1784 |
1428 /** |
1785 /** |
1450 * @param bool $allow_relaxed_file_ownership Optional. Whether to allow Group/World writable. |
1807 * @param bool $allow_relaxed_file_ownership Optional. Whether to allow Group/World writable. |
1451 * Default false. |
1808 * Default false. |
1452 * @return string The transport to use, see description for valid return values. |
1809 * @return string The transport to use, see description for valid return values. |
1453 */ |
1810 */ |
1454 function get_filesystem_method( $args = array(), $context = '', $allow_relaxed_file_ownership = false ) { |
1811 function get_filesystem_method( $args = array(), $context = '', $allow_relaxed_file_ownership = false ) { |
1455 $method = defined('FS_METHOD') ? FS_METHOD : false; // Please ensure that this is either 'direct', 'ssh2', 'ftpext' or 'ftpsockets' |
1812 $method = defined( 'FS_METHOD' ) ? FS_METHOD : false; // Please ensure that this is either 'direct', 'ssh2', 'ftpext' or 'ftpsockets' |
1456 |
1813 |
1457 if ( ! $context ) { |
1814 if ( ! $context ) { |
1458 $context = WP_CONTENT_DIR; |
1815 $context = WP_CONTENT_DIR; |
1459 } |
1816 } |
1460 |
1817 |
1465 |
1822 |
1466 $context = trailingslashit( $context ); |
1823 $context = trailingslashit( $context ); |
1467 |
1824 |
1468 if ( ! $method ) { |
1825 if ( ! $method ) { |
1469 |
1826 |
1470 $temp_file_name = $context . 'temp-write-test-' . time(); |
1827 $temp_file_name = $context . 'temp-write-test-' . str_replace( '.', '-', uniqid( '', true ) ); |
1471 $temp_handle = @fopen($temp_file_name, 'w'); |
1828 $temp_handle = @fopen( $temp_file_name, 'w' ); |
1472 if ( $temp_handle ) { |
1829 if ( $temp_handle ) { |
1473 |
1830 |
1474 // Attempt to determine the file owner of the WordPress files, and that of newly created files |
1831 // Attempt to determine the file owner of the WordPress files, and that of newly created files |
1475 $wp_file_owner = $temp_file_owner = false; |
1832 $wp_file_owner = $temp_file_owner = false; |
1476 if ( function_exists('fileowner') ) { |
1833 if ( function_exists( 'fileowner' ) ) { |
1477 $wp_file_owner = @fileowner( __FILE__ ); |
1834 $wp_file_owner = @fileowner( __FILE__ ); |
1478 $temp_file_owner = @fileowner( $temp_file_name ); |
1835 $temp_file_owner = @fileowner( $temp_file_name ); |
1479 } |
1836 } |
1480 |
1837 |
1481 if ( $wp_file_owner !== false && $wp_file_owner === $temp_file_owner ) { |
1838 if ( $wp_file_owner !== false && $wp_file_owner === $temp_file_owner ) { |
1482 // WordPress is creating files as the same owner as the WordPress files, |
1839 // WordPress is creating files as the same owner as the WordPress files, |
1483 // this means it's safe to modify & create new files via PHP. |
1840 // this means it's safe to modify & create new files via PHP. |
1484 $method = 'direct'; |
1841 $method = 'direct'; |
1485 $GLOBALS['_wp_filesystem_direct_method'] = 'file_owner'; |
1842 $GLOBALS['_wp_filesystem_direct_method'] = 'file_owner'; |
1486 } elseif ( $allow_relaxed_file_ownership ) { |
1843 } elseif ( $allow_relaxed_file_ownership ) { |
1487 // The $context directory is writable, and $allow_relaxed_file_ownership is set, this means we can modify files |
1844 // The $context directory is writable, and $allow_relaxed_file_ownership is set, this means we can modify files |
1488 // safely in this directory. This mode doesn't create new files, only alter existing ones. |
1845 // safely in this directory. This mode doesn't create new files, only alter existing ones. |
1489 $method = 'direct'; |
1846 $method = 'direct'; |
1490 $GLOBALS['_wp_filesystem_direct_method'] = 'relaxed_ownership'; |
1847 $GLOBALS['_wp_filesystem_direct_method'] = 'relaxed_ownership'; |
1491 } |
1848 } |
1492 |
1849 |
1493 @fclose($temp_handle); |
1850 @fclose( $temp_handle ); |
1494 @unlink($temp_file_name); |
1851 @unlink( $temp_file_name ); |
1495 } |
1852 } |
1496 } |
1853 } |
1497 |
1854 |
1498 if ( ! $method && isset($args['connection_type']) && 'ssh' == $args['connection_type'] && extension_loaded('ssh2') && function_exists('stream_get_contents') ) $method = 'ssh2'; |
1855 if ( ! $method && isset( $args['connection_type'] ) && 'ssh' == $args['connection_type'] && extension_loaded( 'ssh2' ) && function_exists( 'stream_get_contents' ) ) { |
1499 if ( ! $method && extension_loaded('ftp') ) $method = 'ftpext'; |
1856 $method = 'ssh2'; |
1500 if ( ! $method && ( extension_loaded('sockets') || function_exists('fsockopen') ) ) $method = 'ftpsockets'; //Sockets: Socket extension; PHP Mode: FSockopen / fwrite / fread |
1857 } |
1858 if ( ! $method && extension_loaded( 'ftp' ) ) { |
|
1859 $method = 'ftpext'; |
|
1860 } |
|
1861 if ( ! $method && ( extension_loaded( 'sockets' ) || function_exists( 'fsockopen' ) ) ) { |
|
1862 $method = 'ftpsockets'; //Sockets: Socket extension; PHP Mode: FSockopen / fwrite / fread |
|
1863 } |
|
1501 |
1864 |
1502 /** |
1865 /** |
1503 * Filters the filesystem method to use. |
1866 * Filters the filesystem method to use. |
1504 * |
1867 * |
1505 * @since 2.6.0 |
1868 * @since 2.6.0 |
1536 * writable. Default empty. |
1899 * writable. Default empty. |
1537 * @param array $extra_fields Optional. Extra `POST` fields to be checked for inclusion in |
1900 * @param array $extra_fields Optional. Extra `POST` fields to be checked for inclusion in |
1538 * the post. Default null. |
1901 * the post. Default null. |
1539 * @param bool $allow_relaxed_file_ownership Optional. Whether to allow Group/World writable. Default false. |
1902 * @param bool $allow_relaxed_file_ownership Optional. Whether to allow Group/World writable. Default false. |
1540 * |
1903 * |
1541 * @return bool False on failure, true on success. |
1904 * @return bool True on success, false on failure. |
1542 */ |
1905 */ |
1543 function request_filesystem_credentials( $form_post, $type = '', $error = false, $context = '', $extra_fields = null, $allow_relaxed_file_ownership = false ) { |
1906 function request_filesystem_credentials( $form_post, $type = '', $error = false, $context = '', $extra_fields = null, $allow_relaxed_file_ownership = false ) { |
1544 global $pagenow; |
1907 global $pagenow; |
1545 |
1908 |
1546 /** |
1909 /** |
1562 * @param bool $allow_relaxed_file_ownership Whether to allow Group/World writable. |
1925 * @param bool $allow_relaxed_file_ownership Whether to allow Group/World writable. |
1563 * Default false. |
1926 * Default false. |
1564 * @param array $extra_fields Extra POST fields. |
1927 * @param array $extra_fields Extra POST fields. |
1565 */ |
1928 */ |
1566 $req_cred = apply_filters( 'request_filesystem_credentials', '', $form_post, $type, $error, $context, $extra_fields, $allow_relaxed_file_ownership ); |
1929 $req_cred = apply_filters( 'request_filesystem_credentials', '', $form_post, $type, $error, $context, $extra_fields, $allow_relaxed_file_ownership ); |
1567 if ( '' !== $req_cred ) |
1930 if ( '' !== $req_cred ) { |
1568 return $req_cred; |
1931 return $req_cred; |
1569 |
1932 } |
1570 if ( empty($type) ) { |
1933 |
1934 if ( empty( $type ) ) { |
|
1571 $type = get_filesystem_method( array(), $context, $allow_relaxed_file_ownership ); |
1935 $type = get_filesystem_method( array(), $context, $allow_relaxed_file_ownership ); |
1572 } |
1936 } |
1573 |
1937 |
1574 if ( 'direct' == $type ) |
1938 if ( 'direct' == $type ) { |
1575 return true; |
1939 return true; |
1576 |
1940 } |
1577 if ( is_null( $extra_fields ) ) |
1941 |
1942 if ( is_null( $extra_fields ) ) { |
|
1578 $extra_fields = array( 'version', 'locale' ); |
1943 $extra_fields = array( 'version', 'locale' ); |
1579 |
1944 } |
1580 $credentials = get_option('ftp_credentials', array( 'hostname' => '', 'username' => '')); |
1945 |
1946 $credentials = get_option( |
|
1947 'ftp_credentials', |
|
1948 array( |
|
1949 'hostname' => '', |
|
1950 'username' => '', |
|
1951 ) |
|
1952 ); |
|
1581 |
1953 |
1582 $submitted_form = wp_unslash( $_POST ); |
1954 $submitted_form = wp_unslash( $_POST ); |
1583 |
1955 |
1584 // Verify nonce, or unset submitted form field values on failure |
1956 // Verify nonce, or unset submitted form field values on failure |
1585 if ( ! isset( $_POST['_fs_nonce'] ) || ! wp_verify_nonce( $_POST['_fs_nonce'], 'filesystem-credentials' ) ) { |
1957 if ( ! isset( $_POST['_fs_nonce'] ) || ! wp_verify_nonce( $_POST['_fs_nonce'], 'filesystem-credentials' ) ) { |
1592 $submitted_form['connection_type'] |
1964 $submitted_form['connection_type'] |
1593 ); |
1965 ); |
1594 } |
1966 } |
1595 |
1967 |
1596 // If defined, set it to that, Else, If POST'd, set it to that, If not, Set it to whatever it previously was(saved details in option) |
1968 // If defined, set it to that, Else, If POST'd, set it to that, If not, Set it to whatever it previously was(saved details in option) |
1597 $credentials['hostname'] = defined('FTP_HOST') ? FTP_HOST : (!empty($submitted_form['hostname']) ? $submitted_form['hostname'] : $credentials['hostname']); |
1969 $credentials['hostname'] = defined( 'FTP_HOST' ) ? FTP_HOST : ( ! empty( $submitted_form['hostname'] ) ? $submitted_form['hostname'] : $credentials['hostname'] ); |
1598 $credentials['username'] = defined('FTP_USER') ? FTP_USER : (!empty($submitted_form['username']) ? $submitted_form['username'] : $credentials['username']); |
1970 $credentials['username'] = defined( 'FTP_USER' ) ? FTP_USER : ( ! empty( $submitted_form['username'] ) ? $submitted_form['username'] : $credentials['username'] ); |
1599 $credentials['password'] = defined('FTP_PASS') ? FTP_PASS : (!empty($submitted_form['password']) ? $submitted_form['password'] : ''); |
1971 $credentials['password'] = defined( 'FTP_PASS' ) ? FTP_PASS : ( ! empty( $submitted_form['password'] ) ? $submitted_form['password'] : '' ); |
1600 |
1972 |
1601 // Check to see if we are setting the public/private keys for ssh |
1973 // Check to see if we are setting the public/private keys for ssh |
1602 $credentials['public_key'] = defined('FTP_PUBKEY') ? FTP_PUBKEY : (!empty($submitted_form['public_key']) ? $submitted_form['public_key'] : ''); |
1974 $credentials['public_key'] = defined( 'FTP_PUBKEY' ) ? FTP_PUBKEY : ( ! empty( $submitted_form['public_key'] ) ? $submitted_form['public_key'] : '' ); |
1603 $credentials['private_key'] = defined('FTP_PRIKEY') ? FTP_PRIKEY : (!empty($submitted_form['private_key']) ? $submitted_form['private_key'] : ''); |
1975 $credentials['private_key'] = defined( 'FTP_PRIKEY' ) ? FTP_PRIKEY : ( ! empty( $submitted_form['private_key'] ) ? $submitted_form['private_key'] : '' ); |
1604 |
1976 |
1605 // Sanitize the hostname, Some people might pass in odd-data: |
1977 // Sanitize the hostname, Some people might pass in odd-data: |
1606 $credentials['hostname'] = preg_replace('|\w+://|', '', $credentials['hostname']); //Strip any schemes off |
1978 $credentials['hostname'] = preg_replace( '|\w+://|', '', $credentials['hostname'] ); //Strip any schemes off |
1607 |
1979 |
1608 if ( strpos($credentials['hostname'], ':') ) { |
1980 if ( strpos( $credentials['hostname'], ':' ) ) { |
1609 list( $credentials['hostname'], $credentials['port'] ) = explode(':', $credentials['hostname'], 2); |
1981 list( $credentials['hostname'], $credentials['port'] ) = explode( ':', $credentials['hostname'], 2 ); |
1610 if ( ! is_numeric($credentials['port']) ) |
1982 if ( ! is_numeric( $credentials['port'] ) ) { |
1611 unset($credentials['port']); |
1983 unset( $credentials['port'] ); |
1984 } |
|
1612 } else { |
1985 } else { |
1613 unset($credentials['port']); |
1986 unset( $credentials['port'] ); |
1614 } |
1987 } |
1615 |
1988 |
1616 if ( ( defined( 'FTP_SSH' ) && FTP_SSH ) || ( defined( 'FS_METHOD' ) && 'ssh2' == FS_METHOD ) ) { |
1989 if ( ( defined( 'FTP_SSH' ) && FTP_SSH ) || ( defined( 'FS_METHOD' ) && 'ssh2' == FS_METHOD ) ) { |
1617 $credentials['connection_type'] = 'ssh'; |
1990 $credentials['connection_type'] = 'ssh'; |
1618 } elseif ( ( defined( 'FTP_SSL' ) && FTP_SSL ) && 'ftpext' == $type ) { //Only the FTP Extension understands SSL |
1991 } elseif ( ( defined( 'FTP_SSL' ) && FTP_SSL ) && 'ftpext' == $type ) { //Only the FTP Extension understands SSL |
1622 } elseif ( ! isset( $credentials['connection_type'] ) ) { //All else fails (And it's not defaulted to something else saved), Default to FTP |
1995 } elseif ( ! isset( $credentials['connection_type'] ) ) { //All else fails (And it's not defaulted to something else saved), Default to FTP |
1623 $credentials['connection_type'] = 'ftp'; |
1996 $credentials['connection_type'] = 'ftp'; |
1624 } |
1997 } |
1625 if ( ! $error && |
1998 if ( ! $error && |
1626 ( |
1999 ( |
1627 ( !empty($credentials['password']) && !empty($credentials['username']) && !empty($credentials['hostname']) ) || |
2000 ( ! empty( $credentials['password'] ) && ! empty( $credentials['username'] ) && ! empty( $credentials['hostname'] ) ) || |
1628 ( 'ssh' == $credentials['connection_type'] && !empty($credentials['public_key']) && !empty($credentials['private_key']) ) |
2001 ( 'ssh' == $credentials['connection_type'] && ! empty( $credentials['public_key'] ) && ! empty( $credentials['private_key'] ) ) |
1629 ) ) { |
2002 ) ) { |
1630 $stored_credentials = $credentials; |
2003 $stored_credentials = $credentials; |
1631 if ( !empty($stored_credentials['port']) ) //save port as part of hostname to simplify above code. |
2004 if ( ! empty( $stored_credentials['port'] ) ) { //save port as part of hostname to simplify above code. |
1632 $stored_credentials['hostname'] .= ':' . $stored_credentials['port']; |
2005 $stored_credentials['hostname'] .= ':' . $stored_credentials['port']; |
1633 |
2006 } |
1634 unset($stored_credentials['password'], $stored_credentials['port'], $stored_credentials['private_key'], $stored_credentials['public_key']); |
2007 |
2008 unset( $stored_credentials['password'], $stored_credentials['port'], $stored_credentials['private_key'], $stored_credentials['public_key'] ); |
|
1635 if ( ! wp_installing() ) { |
2009 if ( ! wp_installing() ) { |
1636 update_option( 'ftp_credentials', $stored_credentials ); |
2010 update_option( 'ftp_credentials', $stored_credentials ); |
1637 } |
2011 } |
1638 return $credentials; |
2012 return $credentials; |
1639 } |
2013 } |
1640 $hostname = isset( $credentials['hostname'] ) ? $credentials['hostname'] : ''; |
2014 $hostname = isset( $credentials['hostname'] ) ? $credentials['hostname'] : ''; |
1641 $username = isset( $credentials['username'] ) ? $credentials['username'] : ''; |
2015 $username = isset( $credentials['username'] ) ? $credentials['username'] : ''; |
1642 $public_key = isset( $credentials['public_key'] ) ? $credentials['public_key'] : ''; |
2016 $public_key = isset( $credentials['public_key'] ) ? $credentials['public_key'] : ''; |
1643 $private_key = isset( $credentials['private_key'] ) ? $credentials['private_key'] : ''; |
2017 $private_key = isset( $credentials['private_key'] ) ? $credentials['private_key'] : ''; |
1644 $port = isset( $credentials['port'] ) ? $credentials['port'] : ''; |
2018 $port = isset( $credentials['port'] ) ? $credentials['port'] : ''; |
1645 $connection_type = isset( $credentials['connection_type'] ) ? $credentials['connection_type'] : ''; |
2019 $connection_type = isset( $credentials['connection_type'] ) ? $credentials['connection_type'] : ''; |
1646 |
2020 |
1647 if ( $error ) { |
2021 if ( $error ) { |
1648 $error_string = __('<strong>ERROR:</strong> There was an error connecting to the server, Please verify the settings are correct.'); |
2022 $error_string = __( '<strong>ERROR:</strong> There was an error connecting to the server, Please verify the settings are correct.' ); |
1649 if ( is_wp_error($error) ) |
2023 if ( is_wp_error( $error ) ) { |
1650 $error_string = esc_html( $error->get_error_message() ); |
2024 $error_string = esc_html( $error->get_error_message() ); |
2025 } |
|
1651 echo '<div id="message" class="error"><p>' . $error_string . '</p></div>'; |
2026 echo '<div id="message" class="error"><p>' . $error_string . '</p></div>'; |
1652 } |
2027 } |
1653 |
2028 |
1654 $types = array(); |
2029 $types = array(); |
1655 if ( extension_loaded('ftp') || extension_loaded('sockets') || function_exists('fsockopen') ) |
2030 if ( extension_loaded( 'ftp' ) || extension_loaded( 'sockets' ) || function_exists( 'fsockopen' ) ) { |
1656 $types[ 'ftp' ] = __('FTP'); |
2031 $types['ftp'] = __( 'FTP' ); |
1657 if ( extension_loaded('ftp') ) //Only this supports FTPS |
2032 } |
1658 $types[ 'ftps' ] = __('FTPS (SSL)'); |
2033 if ( extension_loaded( 'ftp' ) ) { //Only this supports FTPS |
1659 if ( extension_loaded('ssh2') && function_exists('stream_get_contents') ) |
2034 $types['ftps'] = __( 'FTPS (SSL)' ); |
1660 $types[ 'ssh' ] = __('SSH2'); |
2035 } |
2036 if ( extension_loaded( 'ssh2' ) && function_exists( 'stream_get_contents' ) ) { |
|
2037 $types['ssh'] = __( 'SSH2' ); |
|
2038 } |
|
1661 |
2039 |
1662 /** |
2040 /** |
1663 * Filters the connection types to output to the filesystem credentials form. |
2041 * Filters the connection types to output to the filesystem credentials form. |
1664 * |
2042 * |
1665 * @since 2.9.0 |
2043 * @since 2.9.0 |
1672 * @param string $context Full path to the directory that is tested |
2050 * @param string $context Full path to the directory that is tested |
1673 * for being writable. |
2051 * for being writable. |
1674 */ |
2052 */ |
1675 $types = apply_filters( 'fs_ftp_connection_types', $types, $credentials, $type, $error, $context ); |
2053 $types = apply_filters( 'fs_ftp_connection_types', $types, $credentials, $type, $error, $context ); |
1676 |
2054 |
1677 ?> |
2055 ?> |
1678 <form action="<?php echo esc_url( $form_post ) ?>" method="post"> |
2056 <form action="<?php echo esc_url( $form_post ); ?>" method="post"> |
1679 <div id="request-filesystem-credentials-form" class="request-filesystem-credentials-form"> |
2057 <div id="request-filesystem-credentials-form" class="request-filesystem-credentials-form"> |
1680 <?php |
2058 <?php |
1681 // Print a H1 heading in the FTP credentials modal dialog, default is a H2. |
2059 // Print a H1 heading in the FTP credentials modal dialog, default is a H2. |
1682 $heading_tag = 'h2'; |
2060 $heading_tag = 'h2'; |
1683 if ( 'plugins.php' === $pagenow || 'plugin-install.php' === $pagenow ) { |
2061 if ( 'plugins.php' === $pagenow || 'plugin-install.php' === $pagenow ) { |
1684 $heading_tag = 'h1'; |
2062 $heading_tag = 'h1'; |
1685 } |
2063 } |
1686 echo "<$heading_tag id='request-filesystem-credentials-title'>" . __( 'Connection Information' ) . "</$heading_tag>"; |
2064 echo "<$heading_tag id='request-filesystem-credentials-title'>" . __( 'Connection Information' ) . "</$heading_tag>"; |
1687 ?> |
2065 ?> |
1688 <p id="request-filesystem-credentials-desc"><?php |
2066 <p id="request-filesystem-credentials-desc"> |
1689 $label_user = __('Username'); |
2067 <?php |
1690 $label_pass = __('Password'); |
2068 $label_user = __( 'Username' ); |
1691 _e('To perform the requested action, WordPress needs to access your web server.'); |
2069 $label_pass = __( 'Password' ); |
2070 _e( 'To perform the requested action, WordPress needs to access your web server.' ); |
|
1692 echo ' '; |
2071 echo ' '; |
1693 if ( ( isset( $types['ftp'] ) || isset( $types['ftps'] ) ) ) { |
2072 if ( ( isset( $types['ftp'] ) || isset( $types['ftps'] ) ) ) { |
1694 if ( isset( $types['ssh'] ) ) { |
2073 if ( isset( $types['ssh'] ) ) { |
1695 _e('Please enter your FTP or SSH credentials to proceed.'); |
2074 _e( 'Please enter your FTP or SSH credentials to proceed.' ); |
1696 $label_user = __('FTP/SSH Username'); |
2075 $label_user = __( 'FTP/SSH Username' ); |
1697 $label_pass = __('FTP/SSH Password'); |
2076 $label_pass = __( 'FTP/SSH Password' ); |
1698 } else { |
2077 } else { |
1699 _e('Please enter your FTP credentials to proceed.'); |
2078 _e( 'Please enter your FTP credentials to proceed.' ); |
1700 $label_user = __('FTP Username'); |
2079 $label_user = __( 'FTP Username' ); |
1701 $label_pass = __('FTP Password'); |
2080 $label_pass = __( 'FTP Password' ); |
1702 } |
2081 } |
1703 echo ' '; |
2082 echo ' '; |
1704 } |
2083 } |
1705 _e('If you do not remember your credentials, you should contact your web host.'); |
2084 _e( 'If you do not remember your credentials, you should contact your web host.' ); |
1706 ?></p> |
2085 |
2086 $hostname_value = esc_attr( $hostname ); |
|
2087 if ( ! empty( $port ) ) { |
|
2088 $hostname_value .= ":$port"; |
|
2089 } |
|
2090 |
|
2091 $password_value = ''; |
|
2092 if ( defined( 'FTP_PASS' ) ) { |
|
2093 $password_value = '*****'; |
|
2094 } |
|
2095 ?> |
|
2096 </p> |
|
1707 <label for="hostname"> |
2097 <label for="hostname"> |
1708 <span class="field-title"><?php _e( 'Hostname' ) ?></span> |
2098 <span class="field-title"><?php _e( 'Hostname' ); ?></span> |
1709 <input name="hostname" type="text" id="hostname" aria-describedby="request-filesystem-credentials-desc" class="code" placeholder="<?php esc_attr_e( 'example: www.wordpress.org' ) ?>" value="<?php echo esc_attr($hostname); if ( !empty($port) ) echo ":$port"; ?>"<?php disabled( defined('FTP_HOST') ); ?> /> |
2099 <input name="hostname" type="text" id="hostname" aria-describedby="request-filesystem-credentials-desc" class="code" placeholder="<?php esc_attr_e( 'example: www.wordpress.org' ); ?>" value="<?php echo $hostname_value; ?>"<?php disabled( defined( 'FTP_HOST' ) ); ?> /> |
1710 </label> |
2100 </label> |
1711 <div class="ftp-username"> |
2101 <div class="ftp-username"> |
1712 <label for="username"> |
2102 <label for="username"> |
1713 <span class="field-title"><?php echo $label_user; ?></span> |
2103 <span class="field-title"><?php echo $label_user; ?></span> |
1714 <input name="username" type="text" id="username" value="<?php echo esc_attr($username) ?>"<?php disabled( defined('FTP_USER') ); ?> /> |
2104 <input name="username" type="text" id="username" value="<?php echo esc_attr( $username ); ?>"<?php disabled( defined( 'FTP_USER' ) ); ?> /> |
1715 </label> |
2105 </label> |
1716 </div> |
2106 </div> |
1717 <div class="ftp-password"> |
2107 <div class="ftp-password"> |
1718 <label for="password"> |
2108 <label for="password"> |
1719 <span class="field-title"><?php echo $label_pass; ?></span> |
2109 <span class="field-title"><?php echo $label_pass; ?></span> |
1720 <input name="password" type="password" id="password" value="<?php if ( defined('FTP_PASS') ) echo '*****'; ?>"<?php disabled( defined('FTP_PASS') ); ?> /> |
2110 <input name="password" type="password" id="password" value="<?php echo $password_value; ?>"<?php disabled( defined( 'FTP_PASS' ) ); ?> /> |
1721 <em><?php if ( ! defined('FTP_PASS') ) _e( 'This password will not be stored on the server.' ); ?></em> |
2111 <em> |
2112 <?php |
|
2113 if ( ! defined( 'FTP_PASS' ) ) { |
|
2114 _e( 'This password will not be stored on the server.' );} |
|
2115 ?> |
|
2116 </em> |
|
1722 </label> |
2117 </label> |
1723 </div> |
2118 </div> |
1724 <fieldset> |
2119 <fieldset> |
1725 <legend><?php _e( 'Connection Type' ); ?></legend> |
2120 <legend><?php _e( 'Connection Type' ); ?></legend> |
1726 <?php |
2121 <?php |
1727 $disabled = disabled( ( defined( 'FTP_SSL' ) && FTP_SSL ) || ( defined( 'FTP_SSH' ) && FTP_SSH ), true, false ); |
2122 $disabled = disabled( ( defined( 'FTP_SSL' ) && FTP_SSL ) || ( defined( 'FTP_SSH' ) && FTP_SSH ), true, false ); |
1728 foreach ( $types as $name => $text ) : ?> |
2123 foreach ( $types as $name => $text ) : |
1729 <label for="<?php echo esc_attr( $name ) ?>"> |
2124 ?> |
1730 <input type="radio" name="connection_type" id="<?php echo esc_attr( $name ) ?>" value="<?php echo esc_attr( $name ) ?>"<?php checked( $name, $connection_type ); echo $disabled; ?> /> |
2125 <label for="<?php echo esc_attr( $name ); ?>"> |
2126 <input type="radio" name="connection_type" id="<?php echo esc_attr( $name ); ?>" value="<?php echo esc_attr( $name ); ?>" <?php checked( $name, $connection_type ); ?> <?php echo $disabled; ?> /> |
|
1731 <?php echo $text; ?> |
2127 <?php echo $text; ?> |
1732 </label> |
2128 </label> |
1733 <?php |
2129 <?php |
1734 endforeach; |
2130 endforeach; |
1735 ?> |
2131 ?> |
1736 </fieldset> |
2132 </fieldset> |
1737 <?php |
2133 <?php |
1738 if ( isset( $types['ssh'] ) ) { |
2134 if ( isset( $types['ssh'] ) ) { |
1739 $hidden_class = ''; |
2135 $hidden_class = ''; |
1740 if ( 'ssh' != $connection_type || empty( $connection_type ) ) { |
2136 if ( 'ssh' != $connection_type || empty( $connection_type ) ) { |
1741 $hidden_class = ' class="hidden"'; |
2137 $hidden_class = ' class="hidden"'; |
1742 } |
2138 } |
1743 ?> |
2139 ?> |
1744 <fieldset id="ssh-keys"<?php echo $hidden_class; ?>> |
2140 <fieldset id="ssh-keys"<?php echo $hidden_class; ?>> |
1745 <legend><?php _e( 'Authentication Keys' ); ?></legend> |
2141 <legend><?php _e( 'Authentication Keys' ); ?></legend> |
1746 <label for="public_key"> |
2142 <label for="public_key"> |
1747 <span class="field-title"><?php _e('Public Key:') ?></span> |
2143 <span class="field-title"><?php _e( 'Public Key:' ); ?></span> |
1748 <input name="public_key" type="text" id="public_key" aria-describedby="auth-keys-desc" value="<?php echo esc_attr($public_key) ?>"<?php disabled( defined('FTP_PUBKEY') ); ?> /> |
2144 <input name="public_key" type="text" id="public_key" aria-describedby="auth-keys-desc" value="<?php echo esc_attr( $public_key ); ?>"<?php disabled( defined( 'FTP_PUBKEY' ) ); ?> /> |
1749 </label> |
2145 </label> |
1750 <label for="private_key"> |
2146 <label for="private_key"> |
1751 <span class="field-title"><?php _e('Private Key:') ?></span> |
2147 <span class="field-title"><?php _e( 'Private Key:' ); ?></span> |
1752 <input name="private_key" type="text" id="private_key" value="<?php echo esc_attr($private_key) ?>"<?php disabled( defined('FTP_PRIKEY') ); ?> /> |
2148 <input name="private_key" type="text" id="private_key" value="<?php echo esc_attr( $private_key ); ?>"<?php disabled( defined( 'FTP_PRIKEY' ) ); ?> /> |
1753 </label> |
2149 </label> |
1754 <p id="auth-keys-desc"><?php _e( 'Enter the location on the server where the public and private keys are located. If a passphrase is needed, enter that in the password field above.' ) ?></p> |
2150 <p id="auth-keys-desc"><?php _e( 'Enter the location on the server where the public and private keys are located. If a passphrase is needed, enter that in the password field above.' ); ?></p> |
1755 </fieldset> |
2151 </fieldset> |
1756 <?php |
2152 <?php |
1757 } |
2153 } |
1758 |
2154 |
1759 foreach ( (array) $extra_fields as $field ) { |
2155 foreach ( (array) $extra_fields as $field ) { |
1760 if ( isset( $submitted_form[ $field ] ) ) |
2156 if ( isset( $submitted_form[ $field ] ) ) { |
1761 echo '<input type="hidden" name="' . esc_attr( $field ) . '" value="' . esc_attr( $submitted_form[ $field ] ) . '" />'; |
2157 echo '<input type="hidden" name="' . esc_attr( $field ) . '" value="' . esc_attr( $submitted_form[ $field ] ) . '" />'; |
1762 } |
2158 } |
1763 ?> |
2159 } |
2160 ?> |
|
1764 <p class="request-filesystem-credentials-action-buttons"> |
2161 <p class="request-filesystem-credentials-action-buttons"> |
1765 <?php wp_nonce_field( 'filesystem-credentials', '_fs_nonce', false, true ); ?> |
2162 <?php wp_nonce_field( 'filesystem-credentials', '_fs_nonce', false, true ); ?> |
1766 <button class="button cancel-button" data-js-action="close" type="button"><?php _e( 'Cancel' ); ?></button> |
2163 <button class="button cancel-button" data-js-action="close" type="button"><?php _e( 'Cancel' ); ?></button> |
1767 <?php submit_button( __( 'Proceed' ), '', 'upgrade', false ); ?> |
2164 <?php submit_button( __( 'Proceed' ), '', 'upgrade', false ); ?> |
1768 </p> |
2165 </p> |
1769 </div> |
2166 </div> |
1770 </form> |
2167 </form> |
1771 <?php |
2168 <?php |
1772 return false; |
2169 return false; |
1773 } |
2170 } |
1774 |
2171 |
1775 /** |
2172 /** |
1776 * Print the filesystem credentials modal when needed. |
2173 * Print the filesystem credentials modal when needed. |
1819 * } |
2216 * } |
1820 * } |
2217 * } |
1821 * @return string The HTML for this group and its items. |
2218 * @return string The HTML for this group and its items. |
1822 */ |
2219 */ |
1823 function wp_privacy_generate_personal_data_export_group_html( $group_data ) { |
2220 function wp_privacy_generate_personal_data_export_group_html( $group_data ) { |
1824 $allowed_tags = array( |
2221 $group_html = '<h2>' . esc_html( $group_data['group_label'] ) . '</h2>'; |
1825 'a' => array( |
|
1826 'href' => array(), |
|
1827 'target' => array() |
|
1828 ), |
|
1829 'br' => array() |
|
1830 ); |
|
1831 $allowed_protocols = array( 'http', 'https' ); |
|
1832 $group_html = ''; |
|
1833 |
|
1834 $group_html .= '<h2>' . esc_html( $group_data['group_label'] ) . '</h2>'; |
|
1835 $group_html .= '<div>'; |
2222 $group_html .= '<div>'; |
1836 |
2223 |
1837 foreach ( (array) $group_data['items'] as $group_item_id => $group_item_data ) { |
2224 foreach ( (array) $group_data['items'] as $group_item_id => $group_item_data ) { |
1838 $group_html .= '<table>'; |
2225 $group_html .= '<table>'; |
1839 $group_html .= '<tbody>'; |
2226 $group_html .= '<tbody>'; |
1840 |
2227 |
1841 foreach ( (array) $group_item_data as $group_item_datum ) { |
2228 foreach ( (array) $group_item_data as $group_item_datum ) { |
1842 $value = $group_item_datum['value']; |
2229 $value = $group_item_datum['value']; |
1843 // If it looks like a link, make it a link |
2230 // If it looks like a link, make it a link. |
1844 if ( false === strpos( $value, ' ' ) && ( 0 === strpos( $value, 'http://' ) || 0 === strpos( $value, 'https://' ) ) ) { |
2231 if ( false === strpos( $value, ' ' ) && ( 0 === strpos( $value, 'http://' ) || 0 === strpos( $value, 'https://' ) ) ) { |
1845 $value = '<a href="' . esc_url( $value ) . '">' . esc_html( $value ) . '</a>'; |
2232 $value = '<a href="' . esc_url( $value ) . '">' . esc_html( $value ) . '</a>'; |
1846 } |
2233 } |
1847 |
2234 |
1848 $group_html .= '<tr>'; |
2235 $group_html .= '<tr>'; |
1849 $group_html .= '<th>' . esc_html( $group_item_datum['name'] ) . '</th>'; |
2236 $group_html .= '<th>' . esc_html( $group_item_datum['name'] ) . '</th>'; |
1850 $group_html .= '<td>' . wp_kses( $value, $allowed_tags, $allowed_protocols ) . '</td>'; |
2237 $group_html .= '<td>' . wp_kses( $value, 'personal_data_export' ) . '</td>'; |
1851 $group_html .= '</tr>'; |
2238 $group_html .= '</tr>'; |
1852 } |
2239 } |
1853 |
2240 |
1854 $group_html .= '</tbody>'; |
2241 $group_html .= '</tbody>'; |
1855 $group_html .= '</table>'; |
2242 $group_html .= '</table>'; |
1908 $stripped_email = sanitize_title( $stripped_email ); // slugify the email address |
2295 $stripped_email = sanitize_title( $stripped_email ); // slugify the email address |
1909 $obscura = wp_generate_password( 32, false, false ); |
2296 $obscura = wp_generate_password( 32, false, false ); |
1910 $file_basename = 'wp-personal-data-file-' . $stripped_email . '-' . $obscura; |
2297 $file_basename = 'wp-personal-data-file-' . $stripped_email . '-' . $obscura; |
1911 $html_report_filename = $file_basename . '.html'; |
2298 $html_report_filename = $file_basename . '.html'; |
1912 $html_report_pathname = wp_normalize_path( $exports_dir . $html_report_filename ); |
2299 $html_report_pathname = wp_normalize_path( $exports_dir . $html_report_filename ); |
1913 $file = fopen( $html_report_pathname, 'w' ); |
2300 $file = fopen( $html_report_pathname, 'w' ); |
1914 if ( false === $file ) { |
2301 if ( false === $file ) { |
1915 wp_send_json_error( __( 'Unable to open export file (HTML report) for writing.' ) ); |
2302 wp_send_json_error( __( 'Unable to open export file (HTML report) for writing.' ) ); |
1916 } |
2303 } |
1917 |
2304 |
1918 $title = sprintf( |
2305 $title = sprintf( |
1919 /* translators: %s: user's e-mail address */ |
2306 /* translators: %s: user's email address */ |
1920 __( 'Personal Data Export for %s' ), |
2307 __( 'Personal Data Export for %s' ), |
1921 $email_address |
2308 $email_address |
1922 ); |
2309 ); |
1923 |
2310 |
1924 // Open HTML. |
2311 // Open HTML. |
1927 |
2314 |
1928 // Head. |
2315 // Head. |
1929 fwrite( $file, "<head>\n" ); |
2316 fwrite( $file, "<head>\n" ); |
1930 fwrite( $file, "<meta http-equiv='Content-Type' content='text/html; charset=UTF-8' />\n" ); |
2317 fwrite( $file, "<meta http-equiv='Content-Type' content='text/html; charset=UTF-8' />\n" ); |
1931 fwrite( $file, "<style type='text/css'>" ); |
2318 fwrite( $file, "<style type='text/css'>" ); |
1932 fwrite( $file, "body { color: black; font-family: Arial, sans-serif; font-size: 11pt; margin: 15px auto; width: 860px; }" ); |
2319 fwrite( $file, 'body { color: black; font-family: Arial, sans-serif; font-size: 11pt; margin: 15px auto; width: 860px; }' ); |
1933 fwrite( $file, "table { background: #f0f0f0; border: 1px solid #ddd; margin-bottom: 20px; width: 100%; }" ); |
2320 fwrite( $file, 'table { background: #f0f0f0; border: 1px solid #ddd; margin-bottom: 20px; width: 100%; }' ); |
1934 fwrite( $file, "th { padding: 5px; text-align: left; width: 20%; }" ); |
2321 fwrite( $file, 'th { padding: 5px; text-align: left; width: 20%; }' ); |
1935 fwrite( $file, "td { padding: 5px; }" ); |
2322 fwrite( $file, 'td { padding: 5px; }' ); |
1936 fwrite( $file, "tr:nth-child(odd) { background-color: #fafafa; }" ); |
2323 fwrite( $file, 'tr:nth-child(odd) { background-color: #fafafa; }' ); |
1937 fwrite( $file, "</style>" ); |
2324 fwrite( $file, '</style>' ); |
1938 fwrite( $file, "<title>" ); |
2325 fwrite( $file, '<title>' ); |
1939 fwrite( $file, esc_html( $title ) ); |
2326 fwrite( $file, esc_html( $title ) ); |
1940 fwrite( $file, "</title>" ); |
2327 fwrite( $file, '</title>' ); |
1941 fwrite( $file, "</head>\n" ); |
2328 fwrite( $file, "</head>\n" ); |
1942 |
2329 |
1943 // Body. |
2330 // Body. |
1944 fwrite( $file, "<body>\n" ); |
2331 fwrite( $file, "<body>\n" ); |
1945 |
2332 |
1946 // Heading. |
2333 // Heading. |
1947 fwrite( $file, "<h1>" . esc_html__( 'Personal Data Export' ) . "</h1>" ); |
2334 fwrite( $file, '<h1>' . esc_html__( 'Personal Data Export' ) . '</h1>' ); |
1948 |
2335 |
1949 // And now, all the Groups. |
2336 // And now, all the Groups. |
1950 $groups = get_post_meta( $request_id, '_export_data_grouped', true ); |
2337 $groups = get_post_meta( $request_id, '_export_data_grouped', true ); |
1951 |
2338 |
1952 // First, build an "About" group on the fly for this report. |
2339 // First, build an "About" group on the fly for this report. |
2057 function wp_privacy_send_personal_data_export_email( $request_id ) { |
2444 function wp_privacy_send_personal_data_export_email( $request_id ) { |
2058 // Get the request data. |
2445 // Get the request data. |
2059 $request = wp_get_user_request_data( $request_id ); |
2446 $request = wp_get_user_request_data( $request_id ); |
2060 |
2447 |
2061 if ( ! $request || 'export_personal_data' !== $request->action_name ) { |
2448 if ( ! $request || 'export_personal_data' !== $request->action_name ) { |
2062 return new WP_Error( 'invalid', __( 'Invalid request ID when sending personal data export email.' ) ); |
2449 return new WP_Error( 'invalid_request', __( 'Invalid request ID when sending personal data export email.' ) ); |
2063 } |
2450 } |
2451 |
|
2452 // Localize message content for user; fallback to site default for visitors. |
|
2453 if ( ! empty( $request->user_id ) ) { |
|
2454 $locale = get_user_locale( $request->user_id ); |
|
2455 } else { |
|
2456 $locale = get_locale(); |
|
2457 } |
|
2458 |
|
2459 $switched_locale = switch_to_locale( $locale ); |
|
2064 |
2460 |
2065 /** This filter is documented in wp-includes/functions.php */ |
2461 /** This filter is documented in wp-includes/functions.php */ |
2066 $expiration = apply_filters( 'wp_privacy_export_expiration', 3 * DAY_IN_SECONDS ); |
2462 $expiration = apply_filters( 'wp_privacy_export_expiration', 3 * DAY_IN_SECONDS ); |
2067 $expiration_date = date_i18n( get_option( 'date_format' ), time() + $expiration ); |
2463 $expiration_date = date_i18n( get_option( 'date_format' ), time() + $expiration ); |
2068 |
2464 |
2069 /* translators: Do not translate EXPIRATION, LINK, SITENAME, SITEURL: those are placeholders. */ |
2465 /* translators: Do not translate EXPIRATION, LINK, SITENAME, SITEURL: those are placeholders. */ |
2070 $email_text = __( |
2466 $email_text = __( |
2071 'Howdy, |
2467 'Howdy, |
2072 |
2468 |
2073 Your request for an export of personal data has been completed. You may |
2469 Your request for an export of personal data has been completed. You may |
2074 download your personal data by clicking on the link below. For privacy |
2470 download your personal data by clicking on the link below. For privacy |
2075 and security, we will automatically delete the file on ###EXPIRATION###, |
2471 and security, we will automatically delete the file on ###EXPIRATION###, |
2076 so please download it before then. |
2472 so please download it before then. |
2078 ###LINK### |
2474 ###LINK### |
2079 |
2475 |
2080 Regards, |
2476 Regards, |
2081 All at ###SITENAME### |
2477 All at ###SITENAME### |
2082 ###SITEURL###' |
2478 ###SITEURL###' |
2083 ); |
2479 ); |
2084 |
2480 |
2085 /** |
2481 /** |
2086 * Filters the text of the email sent with a personal data export file. |
2482 * Filters the text of the email sent with a personal data export file. |
2087 * |
2483 * |
2088 * The following strings have a special meaning and will get replaced dynamically: |
2484 * The following strings have a special meaning and will get replaced dynamically: |
2096 * @param string $email_text Text in the email. |
2492 * @param string $email_text Text in the email. |
2097 * @param int $request_id The request ID for this personal data export. |
2493 * @param int $request_id The request ID for this personal data export. |
2098 */ |
2494 */ |
2099 $content = apply_filters( 'wp_privacy_personal_data_email_content', $email_text, $request_id ); |
2495 $content = apply_filters( 'wp_privacy_personal_data_email_content', $email_text, $request_id ); |
2100 |
2496 |
2101 $email_address = $request->email; |
2497 $email_address = $request->email; |
2102 $export_file_url = get_post_meta( $request_id, '_export_file_url', true ); |
2498 $export_file_url = get_post_meta( $request_id, '_export_file_url', true ); |
2103 $site_name = wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES ); |
2499 $site_name = wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES ); |
2104 $site_url = home_url(); |
2500 $site_url = home_url(); |
2105 |
2501 |
2106 $content = str_replace( '###EXPIRATION###', $expiration_date, $content ); |
2502 $content = str_replace( '###EXPIRATION###', $expiration_date, $content ); |
2107 $content = str_replace( '###LINK###', esc_url_raw( $export_file_url ), $content ); |
2503 $content = str_replace( '###LINK###', esc_url_raw( $export_file_url ), $content ); |
2108 $content = str_replace( '###EMAIL###', $email_address, $content ); |
2504 $content = str_replace( '###EMAIL###', $email_address, $content ); |
2109 $content = str_replace( '###SITENAME###', $site_name, $content ); |
2505 $content = str_replace( '###SITENAME###', $site_name, $content ); |
2110 $content = str_replace( '###SITEURL###', esc_url_raw( $site_url ), $content ); |
2506 $content = str_replace( '###SITEURL###', esc_url_raw( $site_url ), $content ); |
2111 |
2507 |
2112 $mail_success = wp_mail( |
2508 $mail_success = wp_mail( |
2113 $email_address, |
2509 $email_address, |
2114 sprintf( |
2510 sprintf( |
2511 /* translators: Personal data export notification email subject. %s: Site title */ |
|
2115 __( '[%s] Personal Data Export' ), |
2512 __( '[%s] Personal Data Export' ), |
2116 $site_name |
2513 $site_name |
2117 ), |
2514 ), |
2118 $content |
2515 $content |
2119 ); |
2516 ); |
2120 |
2517 |
2518 if ( $switched_locale ) { |
|
2519 restore_previous_locale(); |
|
2520 } |
|
2521 |
|
2121 if ( ! $mail_success ) { |
2522 if ( ! $mail_success ) { |
2122 return new WP_Error( 'error', __( 'Unable to send personal data export email.' ) ); |
2523 return new WP_Error( 'privacy_email_error', __( 'Unable to send personal data export email.' ) ); |
2123 } |
2524 } |
2124 |
2525 |
2125 return true; |
2526 return true; |
2126 } |
2527 } |
2127 |
2528 |
2180 $export_data = array_merge( $export_data, $response['data'] ); |
2581 $export_data = array_merge( $export_data, $response['data'] ); |
2181 update_post_meta( $request_id, '_export_data_raw', $export_data ); |
2582 update_post_meta( $request_id, '_export_data_raw', $export_data ); |
2182 |
2583 |
2183 // If we are not yet on the last page of the last exporter, return now. |
2584 // If we are not yet on the last page of the last exporter, return now. |
2184 /** This filter is documented in wp-admin/includes/ajax-actions.php */ |
2585 /** This filter is documented in wp-admin/includes/ajax-actions.php */ |
2185 $exporters = apply_filters( 'wp_privacy_personal_data_exporters', array() ); |
2586 $exporters = apply_filters( 'wp_privacy_personal_data_exporters', array() ); |
2186 $is_last_exporter = $exporter_index === count( $exporters ); |
2587 $is_last_exporter = $exporter_index === count( $exporters ); |
2187 $exporter_done = $response['done']; |
2588 $exporter_done = $response['done']; |
2188 if ( ! $is_last_exporter || ! $exporter_done ) { |
2589 if ( ! $is_last_exporter || ! $exporter_done ) { |
2189 return $response; |
2590 return $response; |
2190 } |
2591 } |
2191 |
2592 |
2192 // Last exporter, last page - let's prepare the export file. |
2593 // Last exporter, last page - let's prepare the export file. |
2206 $item_id = $export_datum['item_id']; |
2607 $item_id = $export_datum['item_id']; |
2207 if ( ! array_key_exists( $item_id, $groups[ $group_id ]['items'] ) ) { |
2608 if ( ! array_key_exists( $item_id, $groups[ $group_id ]['items'] ) ) { |
2208 $groups[ $group_id ]['items'][ $item_id ] = array(); |
2609 $groups[ $group_id ]['items'][ $item_id ] = array(); |
2209 } |
2610 } |
2210 |
2611 |
2211 $old_item_data = $groups[ $group_id ]['items'][ $item_id ]; |
2612 $old_item_data = $groups[ $group_id ]['items'][ $item_id ]; |
2212 $merged_item_data = array_merge( $export_datum['data'], $old_item_data ); |
2613 $merged_item_data = array_merge( $export_datum['data'], $old_item_data ); |
2213 $groups[ $group_id ]['items'][ $item_id ] = $merged_item_data; |
2614 $groups[ $group_id ]['items'][ $item_id ] = $merged_item_data; |
2214 } |
2615 } |
2215 |
2616 |
2216 // Then save the grouped data into the request. |
2617 // Then save the grouped data into the request. |
2217 delete_post_meta( $request_id, '_export_data_raw' ); |
2618 delete_post_meta( $request_id, '_export_data_raw' ); |
2233 if ( $send_as_email ) { |
2634 if ( $send_as_email ) { |
2234 $mail_success = wp_privacy_send_personal_data_export_email( $request_id ); |
2635 $mail_success = wp_privacy_send_personal_data_export_email( $request_id ); |
2235 if ( is_wp_error( $mail_success ) ) { |
2636 if ( is_wp_error( $mail_success ) ) { |
2236 wp_send_json_error( $mail_success->get_error_message() ); |
2637 wp_send_json_error( $mail_success->get_error_message() ); |
2237 } |
2638 } |
2639 |
|
2640 // Update the request to completed state when the export email is sent. |
|
2641 _wp_privacy_completed_request( $request_id ); |
|
2238 } else { |
2642 } else { |
2239 // Modify the response to include the URL of the export file so the browser can fetch it. |
2643 // Modify the response to include the URL of the export file so the browser can fetch it. |
2240 $export_file_url = get_post_meta( $request_id, '_export_file_url', true ); |
2644 $export_file_url = get_post_meta( $request_id, '_export_file_url', true ); |
2241 if ( ! empty( $export_file_url ) ) { |
2645 if ( ! empty( $export_file_url ) ) { |
2242 $response['url'] = $export_file_url; |
2646 $response['url'] = $export_file_url; |
2243 } |
2647 } |
2244 } |
2648 } |
2245 |
2649 |
2246 // Update the request to completed state. |
|
2247 _wp_privacy_completed_request( $request_id ); |
|
2248 |
|
2249 return $response; |
2650 return $response; |
2250 } |
2651 } |