19 'sidebar.php' => __( 'Sidebar' ), |
19 'sidebar.php' => __( 'Sidebar' ), |
20 'comments.php' => __( 'Comments' ), |
20 'comments.php' => __( 'Comments' ), |
21 'searchform.php' => __( 'Search Form' ), |
21 'searchform.php' => __( 'Search Form' ), |
22 '404.php' => __( '404 Template' ), |
22 '404.php' => __( '404 Template' ), |
23 'link.php' => __( 'Links Template' ), |
23 'link.php' => __( 'Links Template' ), |
|
24 'theme.json' => __( 'Theme Styles & Block Settings' ), |
24 // Archives. |
25 // Archives. |
25 'index.php' => __( 'Main Index Template' ), |
26 'index.php' => __( 'Main Index Template' ), |
26 'archive.php' => __( 'Archives' ), |
27 'archive.php' => __( 'Archives' ), |
27 'author.php' => __( 'Author Template' ), |
28 'author.php' => __( 'Author Template' ), |
28 'taxonomy.php' => __( 'Taxonomy Template' ), |
29 'taxonomy.php' => __( 'Taxonomy Template' ), |
124 * |
125 * |
125 * The depth of the recursiveness can be controlled by the $levels param. |
126 * The depth of the recursiveness can be controlled by the $levels param. |
126 * |
127 * |
127 * @since 2.6.0 |
128 * @since 2.6.0 |
128 * @since 4.9.0 Added the `$exclusions` parameter. |
129 * @since 4.9.0 Added the `$exclusions` parameter. |
129 * |
130 * @since 6.3.0 Added the `$include_hidden` parameter. |
130 * @param string $folder Optional. Full path to folder. Default empty. |
131 * |
131 * @param int $levels Optional. Levels of folders to follow, Default 100 (PHP Loop limit). |
132 * @param string $folder Optional. Full path to folder. Default empty. |
132 * @param string[] $exclusions Optional. List of folders and files to skip. |
133 * @param int $levels Optional. Levels of folders to follow, Default 100 (PHP Loop limit). |
|
134 * @param string[] $exclusions Optional. List of folders and files to skip. |
|
135 * @param bool $include_hidden Optional. Whether to include details of hidden ("." prefixed) files. |
|
136 * Default false. |
133 * @return string[]|false Array of files on success, false on failure. |
137 * @return string[]|false Array of files on success, false on failure. |
134 */ |
138 */ |
135 function list_files( $folder = '', $levels = 100, $exclusions = array() ) { |
139 function list_files( $folder = '', $levels = 100, $exclusions = array(), $include_hidden = false ) { |
136 if ( empty( $folder ) ) { |
140 if ( empty( $folder ) ) { |
137 return false; |
141 return false; |
138 } |
142 } |
139 |
143 |
140 $folder = trailingslashit( $folder ); |
144 $folder = trailingslashit( $folder ); |
153 if ( in_array( $file, array( '.', '..' ), true ) ) { |
157 if ( in_array( $file, array( '.', '..' ), true ) ) { |
154 continue; |
158 continue; |
155 } |
159 } |
156 |
160 |
157 // Skip hidden and excluded files. |
161 // Skip hidden and excluded files. |
158 if ( '.' === $file[0] || in_array( $file, $exclusions, true ) ) { |
162 if ( ( ! $include_hidden && '.' === $file[0] ) || in_array( $file, $exclusions, true ) ) { |
159 continue; |
163 continue; |
160 } |
164 } |
161 |
165 |
162 if ( is_dir( $folder . $file ) ) { |
166 if ( is_dir( $folder . $file ) ) { |
163 $files2 = list_files( $folder . $file, $levels - 1 ); |
167 $files2 = list_files( $folder . $file, $levels - 1, array(), $include_hidden ); |
164 if ( $files2 ) { |
168 if ( $files2 ) { |
165 $files = array_merge( $files, $files2 ); |
169 $files = array_merge( $files, $files2 ); |
166 } else { |
170 } else { |
167 $files[] = $folder . $file . '/'; |
171 $files[] = $folder . $file . '/'; |
168 } |
172 } |
320 <p> |
324 <p> |
321 <?php |
325 <?php |
322 printf( |
326 printf( |
323 /* translators: %s: Documentation URL. */ |
327 /* translators: %s: Documentation URL. */ |
324 __( 'You need to make this file writable before you can save your changes. See <a href="%s">Changing File Permissions</a> for more information.' ), |
328 __( 'You need to make this file writable before you can save your changes. See <a href="%s">Changing File Permissions</a> for more information.' ), |
325 __( 'https://wordpress.org/support/article/changing-file-permissions/' ) |
329 __( 'https://developer.wordpress.org/advanced-administration/server/file-permissions/' ) |
326 ); |
330 ); |
327 ?> |
331 ?> |
328 </p> |
332 </p> |
329 <# } else { #> |
333 <# } else { #> |
330 <p>{{ data.message || data.code }}</p> |
334 <p>{{ data.message || data.code }}</p> |
537 if ( isset( $_SERVER['PHP_AUTH_USER'] ) && isset( $_SERVER['PHP_AUTH_PW'] ) ) { |
546 if ( isset( $_SERVER['PHP_AUTH_USER'] ) && isset( $_SERVER['PHP_AUTH_PW'] ) ) { |
538 $headers['Authorization'] = 'Basic ' . base64_encode( wp_unslash( $_SERVER['PHP_AUTH_USER'] ) . ':' . wp_unslash( $_SERVER['PHP_AUTH_PW'] ) ); |
547 $headers['Authorization'] = 'Basic ' . base64_encode( wp_unslash( $_SERVER['PHP_AUTH_USER'] ) . ':' . wp_unslash( $_SERVER['PHP_AUTH_PW'] ) ); |
539 } |
548 } |
540 |
549 |
541 // Make sure PHP process doesn't die before loopback requests complete. |
550 // Make sure PHP process doesn't die before loopback requests complete. |
542 set_time_limit( 300 ); |
551 if ( function_exists( 'set_time_limit' ) ) { |
|
552 set_time_limit( 5 * MINUTE_IN_SECONDS ); |
|
553 } |
543 |
554 |
544 // Time to wait for loopback requests to finish. |
555 // Time to wait for loopback requests to finish. |
545 $timeout = 100; |
556 $timeout = 100; // 100 seconds. |
546 |
557 |
547 $needle_start = "###### wp_scraping_result_start:$scrape_key ######"; |
558 $needle_start = "###### wp_scraping_result_start:$scrape_key ######"; |
548 $needle_end = "###### wp_scraping_result_end:$scrape_key ######"; |
559 $needle_end = "###### wp_scraping_result_end:$scrape_key ######"; |
549 |
560 |
550 // Attempt loopback request to editor to see if user just whitescreened themselves. |
561 // Attempt loopback request to editor to see if user just whitescreened themselves. |
561 } else { |
572 } else { |
562 $url = admin_url(); |
573 $url = admin_url(); |
563 } |
574 } |
564 |
575 |
565 if ( function_exists( 'session_status' ) && PHP_SESSION_ACTIVE === session_status() ) { |
576 if ( function_exists( 'session_status' ) && PHP_SESSION_ACTIVE === session_status() ) { |
566 // Close any active session to prevent HTTP requests from timing out |
577 /* |
567 // when attempting to connect back to the site. |
578 * Close any active session to prevent HTTP requests from timing out |
|
579 * when attempting to connect back to the site. |
|
580 */ |
568 session_write_close(); |
581 session_write_close(); |
569 } |
582 } |
570 |
583 |
571 $url = add_query_arg( $scrape_params, $url ); |
584 $url = add_query_arg( $scrape_params, $url ); |
572 $r = wp_remote_get( $url, compact( 'cookies', 'headers', 'timeout', 'sslverify' ) ); |
585 $r = wp_remote_get( $url, compact( 'cookies', 'headers', 'timeout', 'sslverify' ) ); |
641 |
654 |
642 |
655 |
643 /** |
656 /** |
644 * Returns a filename of a temporary unique file. |
657 * Returns a filename of a temporary unique file. |
645 * |
658 * |
646 * Please note that the calling function must unlink() this itself. |
659 * Please note that the calling function must delete or move the file. |
647 * |
660 * |
648 * The filename is based off the passed parameter or defaults to the current unix timestamp, |
661 * The filename is based off the passed parameter or defaults to the current unix timestamp, |
649 * while the directory can either be passed as well, or by leaving it blank, default to a writable |
662 * while the directory can either be passed as well, or by leaving it blank, default to a writable |
650 * temporary directory. |
663 * temporary directory. |
651 * |
664 * |
674 } |
687 } |
675 |
688 |
676 // Suffix some random data to avoid filename conflicts. |
689 // Suffix some random data to avoid filename conflicts. |
677 $temp_filename .= '-' . wp_generate_password( 6, false ); |
690 $temp_filename .= '-' . wp_generate_password( 6, false ); |
678 $temp_filename .= '.tmp'; |
691 $temp_filename .= '.tmp'; |
679 $temp_filename = $dir . wp_unique_filename( $dir, $temp_filename ); |
692 $temp_filename = wp_unique_filename( $dir, $temp_filename ); |
|
693 |
|
694 /* |
|
695 * Filesystems typically have a limit of 255 characters for a filename. |
|
696 * |
|
697 * If the generated unique filename exceeds this, truncate the initial |
|
698 * filename and try again. |
|
699 * |
|
700 * As it's possible that the truncated filename may exist, producing a |
|
701 * suffix of "-1" or "-10" which could exceed the limit again, truncate |
|
702 * it to 252 instead. |
|
703 */ |
|
704 $characters_over_limit = strlen( $temp_filename ) - 252; |
|
705 if ( $characters_over_limit > 0 ) { |
|
706 $filename = substr( $filename, 0, -$characters_over_limit ); |
|
707 return wp_tempnam( $filename, $dir ); |
|
708 } |
|
709 |
|
710 $temp_filename = $dir . $temp_filename; |
680 |
711 |
681 $fp = @fopen( $temp_filename, 'x' ); |
712 $fp = @fopen( $temp_filename, 'x' ); |
682 |
713 |
683 if ( ! $fp && is_writable( $dir ) && file_exists( $temp_filename ) ) { |
714 if ( ! $fp && is_writable( $dir ) && file_exists( $temp_filename ) ) { |
684 return wp_tempnam( $filename, $dir ); |
715 return wp_tempnam( $filename, $dir ); |
744 * } |
775 * } |
745 * @param array|false $overrides { |
776 * @param array|false $overrides { |
746 * An array of override parameters for this file, or boolean false if none are provided. |
777 * An array of override parameters for this file, or boolean false if none are provided. |
747 * |
778 * |
748 * @type callable $upload_error_handler Function to call when there is an error during the upload process. |
779 * @type callable $upload_error_handler Function to call when there is an error during the upload process. |
749 * @see wp_handle_upload_error(). |
780 * See {@see wp_handle_upload_error()}. |
750 * @type callable $unique_filename_callback Function to call when determining a unique file name for the file. |
781 * @type callable $unique_filename_callback Function to call when determining a unique file name for the file. |
751 * @see wp_unique_filename(). |
782 * See {@see wp_unique_filename()}. |
752 * @type string[] $upload_error_strings The strings that describe the error indicated in |
783 * @type string[] $upload_error_strings The strings that describe the error indicated in |
753 * `$_FILES[{form field}]['error']`. |
784 * `$_FILES[{form field}]['error']`. |
754 * @type bool $test_form Whether to test that the `$_POST['action']` parameter is as expected. |
785 * @type bool $test_form Whether to test that the `$_POST['action']` parameter is as expected. |
755 * @type bool $test_size Whether to test that the file size is greater than zero bytes. |
786 * @type bool $test_size Whether to test that the file size is greater than zero bytes. |
756 * @type bool $test_type Whether to test that the mime type of the file is as expected. |
787 * @type bool $test_type Whether to test that the mime type of the file is as expected. |
812 * - `wp_handle_upload_overrides` |
843 * - `wp_handle_upload_overrides` |
813 * |
844 * |
814 * @since 5.7.0 |
845 * @since 5.7.0 |
815 * |
846 * |
816 * @param array|false $overrides An array of override parameters for this file. Boolean false if none are |
847 * @param array|false $overrides An array of override parameters for this file. Boolean false if none are |
817 * provided. @see _wp_handle_upload(). |
848 * provided. See {@see _wp_handle_upload()}. |
818 * @param array $file { |
849 * @param array $file { |
819 * Reference to a single element from `$_FILES`. |
850 * Reference to a single element from `$_FILES`. |
820 * |
851 * |
821 * @type string $name The original name of the file on the client machine. |
852 * @type string $name The original name of the file on the client machine. |
822 * @type string $type The mime type of the file, if the browser provided this information. |
853 * @type string $type The mime type of the file, if the browser provided this information. |
880 $test_form = isset( $overrides['test_form'] ) ? $overrides['test_form'] : true; |
911 $test_form = isset( $overrides['test_form'] ) ? $overrides['test_form'] : true; |
881 $test_size = isset( $overrides['test_size'] ) ? $overrides['test_size'] : true; |
912 $test_size = isset( $overrides['test_size'] ) ? $overrides['test_size'] : true; |
882 |
913 |
883 // If you override this, you must provide $ext and $type!! |
914 // If you override this, you must provide $ext and $type!! |
884 $test_type = isset( $overrides['test_type'] ) ? $overrides['test_type'] : true; |
915 $test_type = isset( $overrides['test_type'] ) ? $overrides['test_type'] : true; |
885 $mimes = isset( $overrides['mimes'] ) ? $overrides['mimes'] : false; |
916 $mimes = isset( $overrides['mimes'] ) ? $overrides['mimes'] : null; |
886 |
917 |
887 // A correct form post will pass this test. |
918 // A correct form post will pass this test. |
888 if ( $test_form && ( ! isset( $_POST['action'] ) || $_POST['action'] !== $action ) ) { |
919 if ( $test_form && ( ! isset( $_POST['action'] ) || $_POST['action'] !== $action ) ) { |
889 return call_user_func_array( $upload_error_handler, array( &$file, __( 'Invalid form submission.' ) ) ); |
920 return call_user_func_array( $upload_error_handler, array( &$file, __( 'Invalid form submission.' ) ) ); |
890 } |
921 } |
987 $move_new_file = @copy( $file['tmp_name'], $new_file ); |
1018 $move_new_file = @copy( $file['tmp_name'], $new_file ); |
988 unlink( $file['tmp_name'] ); |
1019 unlink( $file['tmp_name'] ); |
989 } |
1020 } |
990 |
1021 |
991 if ( false === $move_new_file ) { |
1022 if ( false === $move_new_file ) { |
992 if ( 0 === strpos( $uploads['basedir'], ABSPATH ) ) { |
1023 if ( str_starts_with( $uploads['basedir'], ABSPATH ) ) { |
993 $error_path = str_replace( ABSPATH, '', $uploads['basedir'] ) . $uploads['subdir']; |
1024 $error_path = str_replace( ABSPATH, '', $uploads['basedir'] ) . $uploads['subdir']; |
994 } else { |
1025 } else { |
995 $error_path = basename( $uploads['basedir'] ) . $uploads['subdir']; |
1026 $error_path = basename( $uploads['basedir'] ) . $uploads['subdir']; |
996 } |
1027 } |
997 |
1028 |
1056 * Call the function once for each uploaded file. |
1087 * Call the function once for each uploaded file. |
1057 * See _wp_handle_upload() for accepted values. |
1088 * See _wp_handle_upload() for accepted values. |
1058 * @param array|false $overrides Optional. An associative array of names => values |
1089 * @param array|false $overrides Optional. An associative array of names => values |
1059 * to override default variables. Default false. |
1090 * to override default variables. Default false. |
1060 * See _wp_handle_upload() for accepted values. |
1091 * See _wp_handle_upload() for accepted values. |
1061 * @param string $time Optional. Time formatted in 'yyyy/mm'. Default null. |
1092 * @param string|null $time Optional. Time formatted in 'yyyy/mm'. Default null. |
1062 * @return array See _wp_handle_upload() for return value. |
1093 * @return array See _wp_handle_upload() for return value. |
1063 */ |
1094 */ |
1064 function wp_handle_upload( &$file, $overrides = false, $time = null ) { |
1095 function wp_handle_upload( &$file, $overrides = false, $time = null ) { |
1065 /* |
1096 /* |
1066 * $_POST['action'] must be set and its value must equal $overrides['action'] |
1097 * $_POST['action'] must be set and its value must equal $overrides['action'] |
1087 * Call the function once for each uploaded file. |
1118 * Call the function once for each uploaded file. |
1088 * See _wp_handle_upload() for accepted values. |
1119 * See _wp_handle_upload() for accepted values. |
1089 * @param array|false $overrides Optional. An associative array of names => values |
1120 * @param array|false $overrides Optional. An associative array of names => values |
1090 * to override default variables. Default false. |
1121 * to override default variables. Default false. |
1091 * See _wp_handle_upload() for accepted values. |
1122 * See _wp_handle_upload() for accepted values. |
1092 * @param string $time Optional. Time formatted in 'yyyy/mm'. Default null. |
1123 * @param string|null $time Optional. Time formatted in 'yyyy/mm'. Default null. |
1093 * @return array See _wp_handle_upload() for return value. |
1124 * @return array See _wp_handle_upload() for return value. |
1094 */ |
1125 */ |
1095 function wp_handle_sideload( &$file, $overrides = false, $time = null ) { |
1126 function wp_handle_sideload( &$file, $overrides = false, $time = null ) { |
1096 /* |
1127 /* |
1097 * $_POST['action'] must be set and its value must equal $overrides['action'] |
1128 * $_POST['action'] must be set and its value must equal $overrides['action'] |
1120 * @param bool $signature_verification Whether to perform Signature Verification. |
1151 * @param bool $signature_verification Whether to perform Signature Verification. |
1121 * Default false. |
1152 * Default false. |
1122 * @return string|WP_Error Filename on success, WP_Error on failure. |
1153 * @return string|WP_Error Filename on success, WP_Error on failure. |
1123 */ |
1154 */ |
1124 function download_url( $url, $timeout = 300, $signature_verification = false ) { |
1155 function download_url( $url, $timeout = 300, $signature_verification = false ) { |
1125 // WARNING: The file is not automatically deleted, the script must unlink() the file. |
1156 // WARNING: The file is not automatically deleted, the script must delete or move the file. |
1126 if ( ! $url ) { |
1157 if ( ! $url ) { |
1127 return new WP_Error( 'http_no_url', __( 'Invalid URL Provided.' ) ); |
1158 return new WP_Error( 'http_no_url', __( 'No URL Provided.' ) ); |
1128 } |
1159 } |
1129 |
1160 |
1130 $url_path = parse_url( $url, PHP_URL_PATH ); |
1161 $url_path = parse_url( $url, PHP_URL_PATH ); |
1131 $url_filename = ''; |
1162 $url_filename = ''; |
1132 if ( is_string( $url_path ) && '' !== $url_path ) { |
1163 if ( is_string( $url_path ) && '' !== $url_path ) { |
1181 unlink( $tmpfname ); |
1212 unlink( $tmpfname ); |
1182 |
1213 |
1183 return new WP_Error( 'http_404', trim( wp_remote_retrieve_response_message( $response ) ), $data ); |
1214 return new WP_Error( 'http_404', trim( wp_remote_retrieve_response_message( $response ) ), $data ); |
1184 } |
1215 } |
1185 |
1216 |
1186 $content_disposition = wp_remote_retrieve_header( $response, 'content-disposition' ); |
1217 $content_disposition = wp_remote_retrieve_header( $response, 'Content-Disposition' ); |
1187 |
1218 |
1188 if ( $content_disposition ) { |
1219 if ( $content_disposition ) { |
1189 $content_disposition = strtolower( $content_disposition ); |
1220 $content_disposition = strtolower( $content_disposition ); |
1190 |
1221 |
1191 if ( 0 === strpos( $content_disposition, 'attachment; filename=' ) ) { |
1222 if ( str_starts_with( $content_disposition, 'attachment; filename=' ) ) { |
1192 $tmpfname_disposition = sanitize_file_name( substr( $content_disposition, 21 ) ); |
1223 $tmpfname_disposition = sanitize_file_name( substr( $content_disposition, 21 ) ); |
1193 } else { |
1224 } else { |
1194 $tmpfname_disposition = ''; |
1225 $tmpfname_disposition = ''; |
1195 } |
1226 } |
1196 |
1227 |
1208 unlink( $tmpfname_disposition ); |
1239 unlink( $tmpfname_disposition ); |
1209 } |
1240 } |
1210 } |
1241 } |
1211 } |
1242 } |
1212 |
1243 |
1213 $content_md5 = wp_remote_retrieve_header( $response, 'content-md5' ); |
1244 $content_md5 = wp_remote_retrieve_header( $response, 'Content-MD5' ); |
1214 |
1245 |
1215 if ( $content_md5 ) { |
1246 if ( $content_md5 ) { |
1216 $md5_check = verify_file_md5( $tmpfname, $content_md5 ); |
1247 $md5_check = verify_file_md5( $tmpfname, $content_md5 ); |
1217 |
1248 |
1218 if ( is_wp_error( $md5_check ) ) { |
1249 if ( is_wp_error( $md5_check ) ) { |
1233 $signed_hostnames = apply_filters( 'wp_signature_hosts', array( 'wordpress.org', 'downloads.wordpress.org', 's.w.org' ) ); |
1264 $signed_hostnames = apply_filters( 'wp_signature_hosts', array( 'wordpress.org', 'downloads.wordpress.org', 's.w.org' ) ); |
1234 |
1265 |
1235 $signature_verification = in_array( parse_url( $url, PHP_URL_HOST ), $signed_hostnames, true ); |
1266 $signature_verification = in_array( parse_url( $url, PHP_URL_HOST ), $signed_hostnames, true ); |
1236 } |
1267 } |
1237 |
1268 |
1238 // Perform signature valiation if supported. |
1269 // Perform signature validation if supported. |
1239 if ( $signature_verification ) { |
1270 if ( $signature_verification ) { |
1240 $signature = wp_remote_retrieve_header( $response, 'x-content-signature' ); |
1271 $signature = wp_remote_retrieve_header( $response, 'X-Content-Signature' ); |
1241 |
1272 |
1242 if ( ! $signature ) { |
1273 if ( ! $signature ) { |
1243 // Retrieve signatures from a file if the header wasn't included. |
1274 /* |
1244 // WordPress.org stores signatures at $package_url.sig. |
1275 * Retrieve signatures from a file if the header wasn't included. |
|
1276 * WordPress.org stores signatures at $package_url.sig. |
|
1277 */ |
1245 |
1278 |
1246 $signature_url = false; |
1279 $signature_url = false; |
1247 |
1280 |
1248 if ( is_string( $url_path ) && ( '.zip' === substr( $url_path, -4 ) || '.tar.gz' === substr( $url_path, -7 ) ) ) { |
1281 if ( is_string( $url_path ) && ( str_ends_with( $url_path, '.zip' ) || str_ends_with( $url_path, '.tar.gz' ) ) ) { |
1249 $signature_url = str_replace( $url_path, $url_path . '.sig', $url ); |
1282 $signature_url = str_replace( $url_path, $url_path . '.sig', $url ); |
1250 } |
1283 } |
1251 |
1284 |
1252 /** |
1285 /** |
1253 * Filters the URL where the signature for a file is located. |
1286 * Filters the URL where the signature for a file is located. |
1367 ), |
1400 ), |
1368 ( ! function_exists( 'sodium_crypto_sign_verify_detached' ) ? 'sodium_crypto_sign_verify_detached' : 'sha384' ) |
1401 ( ! function_exists( 'sodium_crypto_sign_verify_detached' ) ? 'sodium_crypto_sign_verify_detached' : 'sha384' ) |
1369 ); |
1402 ); |
1370 } |
1403 } |
1371 |
1404 |
1372 // Check for a edge-case affecting PHP Maths abilities. |
1405 // Check for an edge-case affecting PHP Maths abilities. |
1373 if ( |
1406 if ( |
1374 ! extension_loaded( 'sodium' ) && |
1407 ! extension_loaded( 'sodium' ) && |
1375 in_array( PHP_VERSION_ID, array( 70200, 70201, 70202 ), true ) && |
1408 in_array( PHP_VERSION_ID, array( 70200, 70201, 70202 ), true ) && |
1376 extension_loaded( 'opcache' ) |
1409 extension_loaded( 'opcache' ) |
1377 ) { |
1410 ) { |
1378 // 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. |
1411 /* |
1379 // https://bugs.php.net/bug.php?id=75938 |
1412 * 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. |
|
1413 * https://bugs.php.net/bug.php?id=75938 |
|
1414 */ |
1380 return new WP_Error( |
1415 return new WP_Error( |
1381 'signature_verification_unsupported', |
1416 'signature_verification_unsupported', |
1382 sprintf( |
1417 sprintf( |
1383 /* translators: %s: The filename of the package. */ |
1418 /* translators: %s: The filename of the package. */ |
1384 __( 'The authenticity of %s could not be verified as signature verification is unavailable on this system.' ), |
1419 __( 'The authenticity of %s could not be verified as signature verification is unavailable on this system.' ), |
1385 '<span class="code">' . esc_html( $filename_for_errors ) . '</span>' |
1420 '<span class="code">' . esc_html( $filename_for_errors ) . '</span>' |
1386 ), |
1421 ), |
1387 array( |
1422 array( |
1388 'php' => phpversion(), |
1423 'php' => PHP_VERSION, |
1389 'sodium' => defined( 'SODIUM_LIBRARY_VERSION' ) ? SODIUM_LIBRARY_VERSION : ( defined( 'ParagonIE_Sodium_Compat::VERSION_STRING' ) ? ParagonIE_Sodium_Compat::VERSION_STRING : false ), |
1424 'sodium' => defined( 'SODIUM_LIBRARY_VERSION' ) ? SODIUM_LIBRARY_VERSION : ( defined( 'ParagonIE_Sodium_Compat::VERSION_STRING' ) ? ParagonIE_Sodium_Compat::VERSION_STRING : false ), |
1390 ) |
1425 ) |
1391 ); |
1426 ); |
1392 } |
1427 } |
1393 |
1428 |
1407 $sodium_compat_is_fast = ParagonIE_Sodium_Compat::runtime_speed_test( 100, 10 ); |
1442 $sodium_compat_is_fast = ParagonIE_Sodium_Compat::runtime_speed_test( 100, 10 ); |
1408 ParagonIE_Sodium_Compat::$fastMult = $old_fastMult; |
1443 ParagonIE_Sodium_Compat::$fastMult = $old_fastMult; |
1409 // phpcs:enable |
1444 // phpcs:enable |
1410 } |
1445 } |
1411 |
1446 |
1412 // This cannot be performed in a reasonable amount of time. |
1447 /* |
1413 // https://github.com/paragonie/sodium_compat#help-sodium_compat-is-slow-how-can-i-make-it-fast |
1448 * This cannot be performed in a reasonable amount of time. |
|
1449 * https://github.com/paragonie/sodium_compat#help-sodium_compat-is-slow-how-can-i-make-it-fast |
|
1450 */ |
1414 if ( ! $sodium_compat_is_fast ) { |
1451 if ( ! $sodium_compat_is_fast ) { |
1415 return new WP_Error( |
1452 return new WP_Error( |
1416 'signature_verification_unsupported', |
1453 'signature_verification_unsupported', |
1417 sprintf( |
1454 sprintf( |
1418 /* translators: %s: The filename of the package. */ |
1455 /* translators: %s: The filename of the package. */ |
1419 __( 'The authenticity of %s could not be verified as signature verification is unavailable on this system.' ), |
1456 __( 'The authenticity of %s could not be verified as signature verification is unavailable on this system.' ), |
1420 '<span class="code">' . esc_html( $filename_for_errors ) . '</span>' |
1457 '<span class="code">' . esc_html( $filename_for_errors ) . '</span>' |
1421 ), |
1458 ), |
1422 array( |
1459 array( |
1423 'php' => phpversion(), |
1460 'php' => PHP_VERSION, |
1424 'sodium' => defined( 'SODIUM_LIBRARY_VERSION' ) ? SODIUM_LIBRARY_VERSION : ( defined( 'ParagonIE_Sodium_Compat::VERSION_STRING' ) ? ParagonIE_Sodium_Compat::VERSION_STRING : false ), |
1461 'sodium' => defined( 'SODIUM_LIBRARY_VERSION' ) ? SODIUM_LIBRARY_VERSION : ( defined( 'ParagonIE_Sodium_Compat::VERSION_STRING' ) ? ParagonIE_Sodium_Compat::VERSION_STRING : false ), |
1425 'polyfill_is_fast' => false, |
1462 'polyfill_is_fast' => false, |
1426 'max_execution_time' => ini_get( 'max_execution_time' ), |
1463 'max_execution_time' => ini_get( 'max_execution_time' ), |
1427 ) |
1464 ) |
1428 ); |
1465 ); |
1454 foreach ( (array) $signatures as $signature ) { |
1491 foreach ( (array) $signatures as $signature ) { |
1455 $signature_raw = base64_decode( $signature ); |
1492 $signature_raw = base64_decode( $signature ); |
1456 |
1493 |
1457 // Ensure only valid-length signatures are considered. |
1494 // Ensure only valid-length signatures are considered. |
1458 if ( SODIUM_CRYPTO_SIGN_BYTES !== strlen( $signature_raw ) ) { |
1495 if ( SODIUM_CRYPTO_SIGN_BYTES !== strlen( $signature_raw ) ) { |
1459 $skipped_signature++; |
1496 ++$skipped_signature; |
1460 continue; |
1497 continue; |
1461 } |
1498 } |
1462 |
1499 |
1463 foreach ( (array) $trusted_keys as $key ) { |
1500 foreach ( (array) $trusted_keys as $key ) { |
1464 $key_raw = base64_decode( $key ); |
1501 $key_raw = base64_decode( $key ); |
1465 |
1502 |
1466 // Only pass valid public keys through. |
1503 // Only pass valid public keys through. |
1467 if ( SODIUM_CRYPTO_SIGN_PUBLICKEYBYTES !== strlen( $key_raw ) ) { |
1504 if ( SODIUM_CRYPTO_SIGN_PUBLICKEYBYTES !== strlen( $key_raw ) ) { |
1468 $skipped_key++; |
1505 ++$skipped_key; |
1469 continue; |
1506 continue; |
1470 } |
1507 } |
1471 |
1508 |
1472 if ( sodium_crypto_sign_verify_detached( $signature_raw, $file_hash, $key_raw ) ) { |
1509 if ( sodium_crypto_sign_verify_detached( $signature_raw, $file_hash, $key_raw ) ) { |
1473 reset_mbstring_encoding(); |
1510 reset_mbstring_encoding(); |
1491 'keys' => $trusted_keys, |
1528 'keys' => $trusted_keys, |
1492 'signatures' => $signatures, |
1529 'signatures' => $signatures, |
1493 'hash' => bin2hex( $file_hash ), |
1530 'hash' => bin2hex( $file_hash ), |
1494 'skipped_key' => $skipped_key, |
1531 'skipped_key' => $skipped_key, |
1495 'skipped_sig' => $skipped_signature, |
1532 'skipped_sig' => $skipped_signature, |
1496 'php' => phpversion(), |
1533 'php' => PHP_VERSION, |
1497 'sodium' => defined( 'SODIUM_LIBRARY_VERSION' ) ? SODIUM_LIBRARY_VERSION : ( defined( 'ParagonIE_Sodium_Compat::VERSION_STRING' ) ? ParagonIE_Sodium_Compat::VERSION_STRING : false ), |
1534 'sodium' => defined( 'SODIUM_LIBRARY_VERSION' ) ? SODIUM_LIBRARY_VERSION : ( defined( 'ParagonIE_Sodium_Compat::VERSION_STRING' ) ? ParagonIE_Sodium_Compat::VERSION_STRING : false ), |
1498 ) |
1535 ) |
1499 ); |
1536 ); |
1500 } |
1537 } |
1501 |
1538 |
1522 * @since 5.2.0 |
1559 * @since 5.2.0 |
1523 * |
1560 * |
1524 * @param string[] $trusted_keys The trusted keys that may sign packages. |
1561 * @param string[] $trusted_keys The trusted keys that may sign packages. |
1525 */ |
1562 */ |
1526 return apply_filters( 'wp_trusted_keys', $trusted_keys ); |
1563 return apply_filters( 'wp_trusted_keys', $trusted_keys ); |
|
1564 } |
|
1565 |
|
1566 /** |
|
1567 * Determines whether the given file is a valid ZIP file. |
|
1568 * |
|
1569 * This function does not test to ensure that a file exists. Non-existent files |
|
1570 * are not valid ZIPs, so those will also return false. |
|
1571 * |
|
1572 * @since 6.4.4 |
|
1573 * |
|
1574 * @param string $file Full path to the ZIP file. |
|
1575 * @return bool Whether the file is a valid ZIP file. |
|
1576 */ |
|
1577 function wp_zip_file_is_valid( $file ) { |
|
1578 /** This filter is documented in wp-admin/includes/file.php */ |
|
1579 if ( class_exists( 'ZipArchive', false ) && apply_filters( 'unzip_file_use_ziparchive', true ) ) { |
|
1580 $archive = new ZipArchive(); |
|
1581 $archive_is_valid = $archive->open( $file, ZipArchive::CHECKCONS ); |
|
1582 if ( true === $archive_is_valid ) { |
|
1583 $archive->close(); |
|
1584 return true; |
|
1585 } |
|
1586 } |
|
1587 |
|
1588 // Fall through to PclZip if ZipArchive is not available, or encountered an error opening the file. |
|
1589 require_once ABSPATH . 'wp-admin/includes/class-pclzip.php'; |
|
1590 |
|
1591 $archive = new PclZip( $file ); |
|
1592 $archive_is_valid = is_array( $archive->properties() ); |
|
1593 |
|
1594 return $archive_is_valid; |
1527 } |
1595 } |
1528 |
1596 |
1529 /** |
1597 /** |
1530 * Unzips a specified ZIP file to a location on the filesystem via the WordPress |
1598 * Unzips a specified ZIP file to a location on the filesystem via the WordPress |
1531 * Filesystem Abstraction. |
1599 * Filesystem Abstraction. |
1633 |
1701 |
1634 for ( $i = 0; $i < $z->numFiles; $i++ ) { |
1702 for ( $i = 0; $i < $z->numFiles; $i++ ) { |
1635 $info = $z->statIndex( $i ); |
1703 $info = $z->statIndex( $i ); |
1636 |
1704 |
1637 if ( ! $info ) { |
1705 if ( ! $info ) { |
|
1706 $z->close(); |
1638 return new WP_Error( 'stat_failed_ziparchive', __( 'Could not retrieve file from archive.' ) ); |
1707 return new WP_Error( 'stat_failed_ziparchive', __( 'Could not retrieve file from archive.' ) ); |
1639 } |
1708 } |
1640 |
1709 |
1641 if ( '__MACOSX/' === substr( $info['name'], 0, 9 ) ) { // Skip the OS X-created __MACOSX directory. |
1710 if ( str_starts_with( $info['name'], '__MACOSX/' ) ) { // Skip the OS X-created __MACOSX directory. |
1642 continue; |
1711 continue; |
1643 } |
1712 } |
1644 |
1713 |
1645 // Don't extract invalid files: |
1714 // Don't extract invalid files: |
1646 if ( 0 !== validate_file( $info['name'] ) ) { |
1715 if ( 0 !== validate_file( $info['name'] ) ) { |
1649 |
1718 |
1650 $uncompressed_size += $info['size']; |
1719 $uncompressed_size += $info['size']; |
1651 |
1720 |
1652 $dirname = dirname( $info['name'] ); |
1721 $dirname = dirname( $info['name'] ); |
1653 |
1722 |
1654 if ( '/' === substr( $info['name'], -1 ) ) { |
1723 if ( str_ends_with( $info['name'], '/' ) ) { |
1655 // Directory. |
1724 // Directory. |
1656 $needed_dirs[] = $to . untrailingslashit( $info['name'] ); |
1725 $needed_dirs[] = $to . untrailingslashit( $info['name'] ); |
1657 } elseif ( '.' !== $dirname ) { |
1726 } elseif ( '.' !== $dirname ) { |
1658 // Path to a file. |
1727 // Path to a file. |
1659 $needed_dirs[] = $to . untrailingslashit( $dirname ); |
1728 $needed_dirs[] = $to . untrailingslashit( $dirname ); |
1660 } |
1729 } |
1661 } |
1730 } |
|
1731 |
|
1732 // Enough space to unzip the file and copy its contents, with a 10% buffer. |
|
1733 $required_space = $uncompressed_size * 2.1; |
1662 |
1734 |
1663 /* |
1735 /* |
1664 * disk_free_space() could return false. Assume that any falsey value is an error. |
1736 * disk_free_space() could return false. Assume that any falsey value is an error. |
1665 * A disk that has zero free bytes has bigger problems. |
1737 * A disk that has zero free bytes has bigger problems. |
1666 * Require we have enough space to unzip the file and copy its contents, with a 10% buffer. |
1738 * Require we have enough space to unzip the file and copy its contents, with a 10% buffer. |
1667 */ |
1739 */ |
1668 if ( wp_doing_cron() ) { |
1740 if ( wp_doing_cron() ) { |
1669 $available_space = function_exists( 'disk_free_space' ) ? @disk_free_space( WP_CONTENT_DIR ) : false; |
1741 $available_space = function_exists( 'disk_free_space' ) ? @disk_free_space( WP_CONTENT_DIR ) : false; |
1670 |
1742 |
1671 if ( $available_space && ( $uncompressed_size * 2.1 ) > $available_space ) { |
1743 if ( $available_space && ( $required_space > $available_space ) ) { |
|
1744 $z->close(); |
1672 return new WP_Error( |
1745 return new WP_Error( |
1673 'disk_full_unzip_file', |
1746 'disk_full_unzip_file', |
1674 __( 'Could not copy files. You may have run out of disk space.' ), |
1747 __( 'Could not copy files. You may have run out of disk space.' ), |
1675 compact( 'uncompressed_size', 'available_space' ) |
1748 compact( 'uncompressed_size', 'available_space' ) |
1676 ); |
1749 ); |
1683 // Check the parent folders of the folders all exist within the creation array. |
1756 // Check the parent folders of the folders all exist within the creation array. |
1684 if ( untrailingslashit( $to ) === $dir ) { // Skip over the working directory, we know this exists (or will exist). |
1757 if ( untrailingslashit( $to ) === $dir ) { // Skip over the working directory, we know this exists (or will exist). |
1685 continue; |
1758 continue; |
1686 } |
1759 } |
1687 |
1760 |
1688 if ( strpos( $dir, $to ) === false ) { // If the directory is not within the working directory, skip it. |
1761 if ( ! str_contains( $dir, $to ) ) { // If the directory is not within the working directory, skip it. |
1689 continue; |
1762 continue; |
1690 } |
1763 } |
1691 |
1764 |
1692 $parent_folder = dirname( $dir ); |
1765 $parent_folder = dirname( $dir ); |
1693 |
1766 |
1704 |
1777 |
1705 // Create those directories if need be: |
1778 // Create those directories if need be: |
1706 foreach ( $needed_dirs as $_dir ) { |
1779 foreach ( $needed_dirs as $_dir ) { |
1707 // Only check to see if the Dir exists upon creation failure. Less I/O this way. |
1780 // Only check to see if the Dir exists upon creation failure. Less I/O this way. |
1708 if ( ! $wp_filesystem->mkdir( $_dir, FS_CHMOD_DIR ) && ! $wp_filesystem->is_dir( $_dir ) ) { |
1781 if ( ! $wp_filesystem->mkdir( $_dir, FS_CHMOD_DIR ) && ! $wp_filesystem->is_dir( $_dir ) ) { |
1709 return new WP_Error( 'mkdir_failed_ziparchive', __( 'Could not create directory.' ), substr( $_dir, strlen( $to ) ) ); |
1782 $z->close(); |
1710 } |
1783 return new WP_Error( 'mkdir_failed_ziparchive', __( 'Could not create directory.' ), $_dir ); |
1711 } |
1784 } |
1712 unset( $needed_dirs ); |
1785 } |
|
1786 |
|
1787 /** |
|
1788 * Filters archive unzipping to override with a custom process. |
|
1789 * |
|
1790 * @since 6.4.0 |
|
1791 * |
|
1792 * @param null|true|WP_Error $result The result of the override. True on success, otherwise WP Error. Default null. |
|
1793 * @param string $file Full path and filename of ZIP archive. |
|
1794 * @param string $to Full path on the filesystem to extract archive to. |
|
1795 * @param string[] $needed_dirs A full list of required folders that need to be created. |
|
1796 * @param float $required_space The space required to unzip the file and copy its contents, with a 10% buffer. |
|
1797 */ |
|
1798 $pre = apply_filters( 'pre_unzip_file', null, $file, $to, $needed_dirs, $required_space ); |
|
1799 |
|
1800 if ( null !== $pre ) { |
|
1801 // Ensure the ZIP file archive has been closed. |
|
1802 $z->close(); |
|
1803 |
|
1804 return $pre; |
|
1805 } |
1713 |
1806 |
1714 for ( $i = 0; $i < $z->numFiles; $i++ ) { |
1807 for ( $i = 0; $i < $z->numFiles; $i++ ) { |
1715 $info = $z->statIndex( $i ); |
1808 $info = $z->statIndex( $i ); |
1716 |
1809 |
1717 if ( ! $info ) { |
1810 if ( ! $info ) { |
|
1811 $z->close(); |
1718 return new WP_Error( 'stat_failed_ziparchive', __( 'Could not retrieve file from archive.' ) ); |
1812 return new WP_Error( 'stat_failed_ziparchive', __( 'Could not retrieve file from archive.' ) ); |
1719 } |
1813 } |
1720 |
1814 |
1721 if ( '/' === substr( $info['name'], -1 ) ) { // Directory. |
1815 if ( str_ends_with( $info['name'], '/' ) ) { // Directory. |
1722 continue; |
1816 continue; |
1723 } |
1817 } |
1724 |
1818 |
1725 if ( '__MACOSX/' === substr( $info['name'], 0, 9 ) ) { // Don't extract the OS X-created __MACOSX directory files. |
1819 if ( str_starts_with( $info['name'], '__MACOSX/' ) ) { // Don't extract the OS X-created __MACOSX directory files. |
1726 continue; |
1820 continue; |
1727 } |
1821 } |
1728 |
1822 |
1729 // Don't extract invalid files: |
1823 // Don't extract invalid files: |
1730 if ( 0 !== validate_file( $info['name'] ) ) { |
1824 if ( 0 !== validate_file( $info['name'] ) ) { |
1732 } |
1826 } |
1733 |
1827 |
1734 $contents = $z->getFromIndex( $i ); |
1828 $contents = $z->getFromIndex( $i ); |
1735 |
1829 |
1736 if ( false === $contents ) { |
1830 if ( false === $contents ) { |
|
1831 $z->close(); |
1737 return new WP_Error( 'extract_failed_ziparchive', __( 'Could not extract file from archive.' ), $info['name'] ); |
1832 return new WP_Error( 'extract_failed_ziparchive', __( 'Could not extract file from archive.' ), $info['name'] ); |
1738 } |
1833 } |
1739 |
1834 |
1740 if ( ! $wp_filesystem->put_contents( $to . $info['name'], $contents, FS_CHMOD_FILE ) ) { |
1835 if ( ! $wp_filesystem->put_contents( $to . $info['name'], $contents, FS_CHMOD_FILE ) ) { |
|
1836 $z->close(); |
1741 return new WP_Error( 'copy_failed_ziparchive', __( 'Could not copy file.' ), $info['name'] ); |
1837 return new WP_Error( 'copy_failed_ziparchive', __( 'Could not copy file.' ), $info['name'] ); |
1742 } |
1838 } |
1743 } |
1839 } |
1744 |
1840 |
1745 $z->close(); |
1841 $z->close(); |
1746 |
1842 |
1747 return true; |
1843 /** |
|
1844 * Filters the result of unzipping an archive. |
|
1845 * |
|
1846 * @since 6.4.0 |
|
1847 * |
|
1848 * @param true|WP_Error $result The result of unzipping the archive. True on success, otherwise WP_Error. Default true. |
|
1849 * @param string $file Full path and filename of ZIP archive. |
|
1850 * @param string $to Full path on the filesystem the archive was extracted to. |
|
1851 * @param string[] $needed_dirs A full list of required folders that were created. |
|
1852 * @param float $required_space The space required to unzip the file and copy its contents, with a 10% buffer. |
|
1853 */ |
|
1854 $result = apply_filters( 'unzip_file', true, $file, $to, $needed_dirs, $required_space ); |
|
1855 |
|
1856 unset( $needed_dirs ); |
|
1857 |
|
1858 return $result; |
1748 } |
1859 } |
1749 |
1860 |
1750 /** |
1861 /** |
1751 * Attempts to unzip an archive using the PclZip library. |
1862 * Attempts to unzip an archive using the PclZip library. |
1752 * |
1863 * |
1790 |
1901 |
1791 $uncompressed_size = 0; |
1902 $uncompressed_size = 0; |
1792 |
1903 |
1793 // Determine any children directories needed (From within the archive). |
1904 // Determine any children directories needed (From within the archive). |
1794 foreach ( $archive_files as $file ) { |
1905 foreach ( $archive_files as $file ) { |
1795 if ( '__MACOSX/' === substr( $file['filename'], 0, 9 ) ) { // Skip the OS X-created __MACOSX directory. |
1906 if ( str_starts_with( $file['filename'], '__MACOSX/' ) ) { // Skip the OS X-created __MACOSX directory. |
1796 continue; |
1907 continue; |
1797 } |
1908 } |
1798 |
1909 |
1799 $uncompressed_size += $file['size']; |
1910 $uncompressed_size += $file['size']; |
1800 |
1911 |
1801 $needed_dirs[] = $to . untrailingslashit( $file['folder'] ? $file['filename'] : dirname( $file['filename'] ) ); |
1912 $needed_dirs[] = $to . untrailingslashit( $file['folder'] ? $file['filename'] : dirname( $file['filename'] ) ); |
1802 } |
1913 } |
|
1914 |
|
1915 // Enough space to unzip the file and copy its contents, with a 10% buffer. |
|
1916 $required_space = $uncompressed_size * 2.1; |
1803 |
1917 |
1804 /* |
1918 /* |
1805 * disk_free_space() could return false. Assume that any falsey value is an error. |
1919 * disk_free_space() could return false. Assume that any falsey value is an error. |
1806 * A disk that has zero free bytes has bigger problems. |
1920 * A disk that has zero free bytes has bigger problems. |
1807 * Require we have enough space to unzip the file and copy its contents, with a 10% buffer. |
1921 * Require we have enough space to unzip the file and copy its contents, with a 10% buffer. |
1808 */ |
1922 */ |
1809 if ( wp_doing_cron() ) { |
1923 if ( wp_doing_cron() ) { |
1810 $available_space = function_exists( 'disk_free_space' ) ? @disk_free_space( WP_CONTENT_DIR ) : false; |
1924 $available_space = function_exists( 'disk_free_space' ) ? @disk_free_space( WP_CONTENT_DIR ) : false; |
1811 |
1925 |
1812 if ( $available_space && ( $uncompressed_size * 2.1 ) > $available_space ) { |
1926 if ( $available_space && ( $required_space > $available_space ) ) { |
1813 return new WP_Error( |
1927 return new WP_Error( |
1814 'disk_full_unzip_file', |
1928 'disk_full_unzip_file', |
1815 __( 'Could not copy files. You may have run out of disk space.' ), |
1929 __( 'Could not copy files. You may have run out of disk space.' ), |
1816 compact( 'uncompressed_size', 'available_space' ) |
1930 compact( 'uncompressed_size', 'available_space' ) |
1817 ); |
1931 ); |
1824 // Check the parent folders of the folders all exist within the creation array. |
1938 // Check the parent folders of the folders all exist within the creation array. |
1825 if ( untrailingslashit( $to ) === $dir ) { // Skip over the working directory, we know this exists (or will exist). |
1939 if ( untrailingslashit( $to ) === $dir ) { // Skip over the working directory, we know this exists (or will exist). |
1826 continue; |
1940 continue; |
1827 } |
1941 } |
1828 |
1942 |
1829 if ( strpos( $dir, $to ) === false ) { // If the directory is not within the working directory, skip it. |
1943 if ( ! str_contains( $dir, $to ) ) { // If the directory is not within the working directory, skip it. |
1830 continue; |
1944 continue; |
1831 } |
1945 } |
1832 |
1946 |
1833 $parent_folder = dirname( $dir ); |
1947 $parent_folder = dirname( $dir ); |
1834 |
1948 |
1845 |
1959 |
1846 // Create those directories if need be: |
1960 // Create those directories if need be: |
1847 foreach ( $needed_dirs as $_dir ) { |
1961 foreach ( $needed_dirs as $_dir ) { |
1848 // Only check to see if the dir exists upon creation failure. Less I/O this way. |
1962 // Only check to see if the dir exists upon creation failure. Less I/O this way. |
1849 if ( ! $wp_filesystem->mkdir( $_dir, FS_CHMOD_DIR ) && ! $wp_filesystem->is_dir( $_dir ) ) { |
1963 if ( ! $wp_filesystem->mkdir( $_dir, FS_CHMOD_DIR ) && ! $wp_filesystem->is_dir( $_dir ) ) { |
1850 return new WP_Error( 'mkdir_failed_pclzip', __( 'Could not create directory.' ), substr( $_dir, strlen( $to ) ) ); |
1964 return new WP_Error( 'mkdir_failed_pclzip', __( 'Could not create directory.' ), $_dir ); |
1851 } |
1965 } |
1852 } |
1966 } |
1853 unset( $needed_dirs ); |
1967 |
|
1968 /** This filter is documented in src/wp-admin/includes/file.php */ |
|
1969 $pre = apply_filters( 'pre_unzip_file', null, $file, $to, $needed_dirs, $required_space ); |
|
1970 |
|
1971 if ( null !== $pre ) { |
|
1972 return $pre; |
|
1973 } |
1854 |
1974 |
1855 // Extract the files from the zip. |
1975 // Extract the files from the zip. |
1856 foreach ( $archive_files as $file ) { |
1976 foreach ( $archive_files as $file ) { |
1857 if ( $file['folder'] ) { |
1977 if ( $file['folder'] ) { |
1858 continue; |
1978 continue; |
1859 } |
1979 } |
1860 |
1980 |
1861 if ( '__MACOSX/' === substr( $file['filename'], 0, 9 ) ) { // Don't extract the OS X-created __MACOSX directory files. |
1981 if ( str_starts_with( $file['filename'], '__MACOSX/' ) ) { // Don't extract the OS X-created __MACOSX directory files. |
1862 continue; |
1982 continue; |
1863 } |
1983 } |
1864 |
1984 |
1865 // Don't extract invalid files: |
1985 // Don't extract invalid files: |
1866 if ( 0 !== validate_file( $file['filename'] ) ) { |
1986 if ( 0 !== validate_file( $file['filename'] ) ) { |
1870 if ( ! $wp_filesystem->put_contents( $to . $file['filename'], $file['content'], FS_CHMOD_FILE ) ) { |
1990 if ( ! $wp_filesystem->put_contents( $to . $file['filename'], $file['content'], FS_CHMOD_FILE ) ) { |
1871 return new WP_Error( 'copy_failed_pclzip', __( 'Could not copy file.' ), $file['filename'] ); |
1991 return new WP_Error( 'copy_failed_pclzip', __( 'Could not copy file.' ), $file['filename'] ); |
1872 } |
1992 } |
1873 } |
1993 } |
1874 |
1994 |
1875 return true; |
1995 /** This action is documented in src/wp-admin/includes/file.php */ |
|
1996 $result = apply_filters( 'unzip_file', true, $file, $to, $needed_dirs, $required_space ); |
|
1997 |
|
1998 unset( $needed_dirs ); |
|
1999 |
|
2000 return $result; |
1876 } |
2001 } |
1877 |
2002 |
1878 /** |
2003 /** |
1879 * Copies a directory from one location to another via the WordPress Filesystem |
2004 * Copies a directory from one location to another via the WordPress Filesystem |
1880 * Abstraction. |
2005 * Abstraction. |
1894 global $wp_filesystem; |
2019 global $wp_filesystem; |
1895 |
2020 |
1896 $dirlist = $wp_filesystem->dirlist( $from ); |
2021 $dirlist = $wp_filesystem->dirlist( $from ); |
1897 |
2022 |
1898 if ( false === $dirlist ) { |
2023 if ( false === $dirlist ) { |
1899 return new WP_Error( 'dirlist_failed_copy_dir', __( 'Directory listing failed.' ), basename( $to ) ); |
2024 return new WP_Error( 'dirlist_failed_copy_dir', __( 'Directory listing failed.' ), basename( $from ) ); |
1900 } |
2025 } |
1901 |
2026 |
1902 $from = trailingslashit( $from ); |
2027 $from = trailingslashit( $from ); |
1903 $to = trailingslashit( $to ); |
2028 $to = trailingslashit( $to ); |
|
2029 |
|
2030 if ( ! $wp_filesystem->exists( $to ) && ! $wp_filesystem->mkdir( $to ) ) { |
|
2031 return new WP_Error( |
|
2032 'mkdir_destination_failed_copy_dir', |
|
2033 __( 'Could not create the destination directory.' ), |
|
2034 basename( $to ) |
|
2035 ); |
|
2036 } |
1904 |
2037 |
1905 foreach ( (array) $dirlist as $filename => $fileinfo ) { |
2038 foreach ( (array) $dirlist as $filename => $fileinfo ) { |
1906 if ( in_array( $filename, $skip_list, true ) ) { |
2039 if ( in_array( $filename, $skip_list, true ) ) { |
1907 continue; |
2040 continue; |
1908 } |
2041 } |
1927 |
2060 |
1928 // Generate the $sub_skip_list for the subdirectory as a sub-set of the existing $skip_list. |
2061 // Generate the $sub_skip_list for the subdirectory as a sub-set of the existing $skip_list. |
1929 $sub_skip_list = array(); |
2062 $sub_skip_list = array(); |
1930 |
2063 |
1931 foreach ( $skip_list as $skip_item ) { |
2064 foreach ( $skip_list as $skip_item ) { |
1932 if ( 0 === strpos( $skip_item, $filename . '/' ) ) { |
2065 if ( str_starts_with( $skip_item, $filename . '/' ) ) { |
1933 $sub_skip_list[] = preg_replace( '!^' . preg_quote( $filename, '!' ) . '/!i', '', $skip_item ); |
2066 $sub_skip_list[] = preg_replace( '!^' . preg_quote( $filename, '!' ) . '/!i', '', $skip_item ); |
1934 } |
2067 } |
1935 } |
2068 } |
1936 |
2069 |
1937 $result = copy_dir( $from . $filename, $to . $filename, $sub_skip_list ); |
2070 $result = copy_dir( $from . $filename, $to . $filename, $sub_skip_list ); |
1941 } |
2074 } |
1942 } |
2075 } |
1943 } |
2076 } |
1944 |
2077 |
1945 return true; |
2078 return true; |
|
2079 } |
|
2080 |
|
2081 /** |
|
2082 * Moves a directory from one location to another. |
|
2083 * |
|
2084 * Recursively invalidates OPcache on success. |
|
2085 * |
|
2086 * If the renaming failed, falls back to copy_dir(). |
|
2087 * |
|
2088 * Assumes that WP_Filesystem() has already been called and setup. |
|
2089 * |
|
2090 * This function is not designed to merge directories, copy_dir() should be used instead. |
|
2091 * |
|
2092 * @since 6.2.0 |
|
2093 * |
|
2094 * @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass. |
|
2095 * |
|
2096 * @param string $from Source directory. |
|
2097 * @param string $to Destination directory. |
|
2098 * @param bool $overwrite Optional. Whether to overwrite the destination directory if it exists. |
|
2099 * Default false. |
|
2100 * @return true|WP_Error True on success, WP_Error on failure. |
|
2101 */ |
|
2102 function move_dir( $from, $to, $overwrite = false ) { |
|
2103 global $wp_filesystem; |
|
2104 |
|
2105 if ( trailingslashit( strtolower( $from ) ) === trailingslashit( strtolower( $to ) ) ) { |
|
2106 return new WP_Error( 'source_destination_same_move_dir', __( 'The source and destination are the same.' ) ); |
|
2107 } |
|
2108 |
|
2109 if ( $wp_filesystem->exists( $to ) ) { |
|
2110 if ( ! $overwrite ) { |
|
2111 return new WP_Error( 'destination_already_exists_move_dir', __( 'The destination folder already exists.' ), $to ); |
|
2112 } elseif ( ! $wp_filesystem->delete( $to, true ) ) { |
|
2113 // Can't overwrite if the destination couldn't be deleted. |
|
2114 return new WP_Error( 'destination_not_deleted_move_dir', __( 'The destination directory already exists and could not be removed.' ) ); |
|
2115 } |
|
2116 } |
|
2117 |
|
2118 if ( $wp_filesystem->move( $from, $to ) ) { |
|
2119 /* |
|
2120 * When using an environment with shared folders, |
|
2121 * there is a delay in updating the filesystem's cache. |
|
2122 * |
|
2123 * This is a known issue in environments with a VirtualBox provider. |
|
2124 * |
|
2125 * A 200ms delay gives time for the filesystem to update its cache, |
|
2126 * prevents "Operation not permitted", and "No such file or directory" warnings. |
|
2127 * |
|
2128 * This delay is used in other projects, including Composer. |
|
2129 * @link https://github.com/composer/composer/blob/2.5.1/src/Composer/Util/Platform.php#L228-L233 |
|
2130 */ |
|
2131 usleep( 200000 ); |
|
2132 wp_opcache_invalidate_directory( $to ); |
|
2133 |
|
2134 return true; |
|
2135 } |
|
2136 |
|
2137 // Fall back to a recursive copy. |
|
2138 if ( ! $wp_filesystem->is_dir( $to ) ) { |
|
2139 if ( ! $wp_filesystem->mkdir( $to, FS_CHMOD_DIR ) ) { |
|
2140 return new WP_Error( 'mkdir_failed_move_dir', __( 'Could not create directory.' ), $to ); |
|
2141 } |
|
2142 } |
|
2143 |
|
2144 $result = copy_dir( $from, $to, array( basename( $to ) ) ); |
|
2145 |
|
2146 // Clear the source directory. |
|
2147 if ( true === $result ) { |
|
2148 $wp_filesystem->delete( $from, true ); |
|
2149 } |
|
2150 |
|
2151 return $result; |
1946 } |
2152 } |
1947 |
2153 |
1948 /** |
2154 /** |
1949 * Initializes and connects the WordPress Filesystem Abstraction classes. |
2155 * Initializes and connects the WordPress Filesystem Abstraction classes. |
1950 * |
2156 * |
2005 /* |
2211 /* |
2006 * Define the timeouts for the connections. Only available after the constructor is called |
2212 * Define the timeouts for the connections. Only available after the constructor is called |
2007 * to allow for per-transport overriding of the default. |
2213 * to allow for per-transport overriding of the default. |
2008 */ |
2214 */ |
2009 if ( ! defined( 'FS_CONNECT_TIMEOUT' ) ) { |
2215 if ( ! defined( 'FS_CONNECT_TIMEOUT' ) ) { |
2010 define( 'FS_CONNECT_TIMEOUT', 30 ); |
2216 define( 'FS_CONNECT_TIMEOUT', 30 ); // 30 seconds. |
2011 } |
2217 } |
2012 if ( ! defined( 'FS_TIMEOUT' ) ) { |
2218 if ( ! defined( 'FS_TIMEOUT' ) ) { |
2013 define( 'FS_TIMEOUT', 30 ); |
2219 define( 'FS_TIMEOUT', 30 ); // 30 seconds. |
2014 } |
2220 } |
2015 |
2221 |
2016 if ( is_wp_error( $wp_filesystem->errors ) && $wp_filesystem->errors->has_errors() ) { |
2222 if ( is_wp_error( $wp_filesystem->errors ) && $wp_filesystem->errors->has_errors() ) { |
2017 return false; |
2223 return false; |
2018 } |
2224 } |
2041 * 'ftpext' or 'ftpsockets'. |
2247 * 'ftpext' or 'ftpsockets'. |
2042 * |
2248 * |
2043 * The return value can be overridden by defining the `FS_METHOD` constant in `wp-config.php`, |
2249 * The return value can be overridden by defining the `FS_METHOD` constant in `wp-config.php`, |
2044 * or filtering via {@see 'filesystem_method'}. |
2250 * or filtering via {@see 'filesystem_method'}. |
2045 * |
2251 * |
2046 * @link https://wordpress.org/support/article/editing-wp-config-php/#wordpress-upgrade-constants |
2252 * @link https://developer.wordpress.org/advanced-administration/wordpress/wp-config/#wordpress-upgrade-constants |
2047 * |
2253 * |
2048 * Plugins may define a custom transport handler, See WP_Filesystem(). |
2254 * Plugins may define a custom transport handler, See WP_Filesystem(). |
2049 * |
2255 * |
2050 * @since 2.5.0 |
2256 * @since 2.5.0 |
2051 * |
2257 * |
2233 'password' => 'FTP_PASS', |
2439 'password' => 'FTP_PASS', |
2234 'public_key' => 'FTP_PUBKEY', |
2440 'public_key' => 'FTP_PUBKEY', |
2235 'private_key' => 'FTP_PRIKEY', |
2441 'private_key' => 'FTP_PRIKEY', |
2236 ); |
2442 ); |
2237 |
2443 |
2238 // If defined, set it to that. Else, if POST'd, set it to that. If not, set it to an empty string. |
2444 /* |
2239 // Otherwise, keep it as it previously was (saved details in option). |
2445 * If defined, set it to that. Else, if POST'd, set it to that. If not, set it to an empty string. |
|
2446 * Otherwise, keep it as it previously was (saved details in option). |
|
2447 */ |
2240 foreach ( $ftp_constants as $key => $constant ) { |
2448 foreach ( $ftp_constants as $key => $constant ) { |
2241 if ( defined( $constant ) ) { |
2449 if ( defined( $constant ) ) { |
2242 $credentials[ $key ] = constant( $constant ); |
2450 $credentials[ $key ] = constant( $constant ); |
2243 } elseif ( ! empty( $submitted_form[ $key ] ) ) { |
2451 } elseif ( ! empty( $submitted_form[ $key ] ) ) { |
2244 $credentials[ $key ] = $submitted_form[ $key ]; |
2452 $credentials[ $key ] = $submitted_form[ $key ]; |
2300 $private_key = isset( $credentials['private_key'] ) ? $credentials['private_key'] : ''; |
2508 $private_key = isset( $credentials['private_key'] ) ? $credentials['private_key'] : ''; |
2301 $port = isset( $credentials['port'] ) ? $credentials['port'] : ''; |
2509 $port = isset( $credentials['port'] ) ? $credentials['port'] : ''; |
2302 $connection_type = isset( $credentials['connection_type'] ) ? $credentials['connection_type'] : ''; |
2510 $connection_type = isset( $credentials['connection_type'] ) ? $credentials['connection_type'] : ''; |
2303 |
2511 |
2304 if ( $error ) { |
2512 if ( $error ) { |
2305 $error_string = __( '<strong>Error</strong>: Could not connect to the server. Please verify the settings are correct.' ); |
2513 $error_string = __( '<strong>Error:</strong> Could not connect to the server. Please verify the settings are correct.' ); |
2306 if ( is_wp_error( $error ) ) { |
2514 if ( is_wp_error( $error ) ) { |
2307 $error_string = esc_html( $error->get_error_message() ); |
2515 $error_string = esc_html( $error->get_error_message() ); |
2308 } |
2516 } |
2309 echo '<div id="message" class="error"><p>' . $error_string . '</p></div>'; |
2517 wp_admin_notice( |
|
2518 $error_string, |
|
2519 array( |
|
2520 'id' => 'message', |
|
2521 'additional_classes' => array( 'error' ), |
|
2522 ) |
|
2523 ); |
2310 } |
2524 } |
2311 |
2525 |
2312 $types = array(); |
2526 $types = array(); |
2313 if ( extension_loaded( 'ftp' ) || extension_loaded( 'sockets' ) || function_exists( 'fsockopen' ) ) { |
2527 if ( extension_loaded( 'ftp' ) || extension_loaded( 'sockets' ) || function_exists( 'fsockopen' ) ) { |
2314 $types['ftp'] = __( 'FTP' ); |
2528 $types['ftp'] = __( 'FTP' ); |
2387 </label> |
2601 </label> |
2388 </div> |
2602 </div> |
2389 <div class="ftp-password"> |
2603 <div class="ftp-password"> |
2390 <label for="password"> |
2604 <label for="password"> |
2391 <span class="field-title"><?php echo $label_pass; ?></span> |
2605 <span class="field-title"><?php echo $label_pass; ?></span> |
2392 <input name="password" type="password" id="password" value="<?php echo $password_value; ?>"<?php disabled( defined( 'FTP_PASS' ) ); ?> /> |
2606 <input name="password" type="password" id="password" value="<?php echo $password_value; ?>"<?php disabled( defined( 'FTP_PASS' ) ); ?> spellcheck="false" /> |
2393 <?php |
2607 <?php |
2394 if ( ! defined( 'FTP_PASS' ) ) { |
2608 if ( ! defined( 'FTP_PASS' ) ) { |
2395 _e( 'This password will not be stored on the server.' );} |
2609 _e( 'This password will not be stored on the server.' ); |
|
2610 } |
2396 ?> |
2611 ?> |
2397 </label> |
2612 </label> |
2398 </div> |
2613 </div> |
2399 <fieldset> |
2614 <fieldset> |
2400 <legend><?php _e( 'Connection Type' ); ?></legend> |
2615 <legend><?php _e( 'Connection Type' ); ?></legend> |
2436 if ( isset( $submitted_form[ $field ] ) ) { |
2651 if ( isset( $submitted_form[ $field ] ) ) { |
2437 echo '<input type="hidden" name="' . esc_attr( $field ) . '" value="' . esc_attr( $submitted_form[ $field ] ) . '" />'; |
2652 echo '<input type="hidden" name="' . esc_attr( $field ) . '" value="' . esc_attr( $submitted_form[ $field ] ) . '" />'; |
2438 } |
2653 } |
2439 } |
2654 } |
2440 |
2655 |
2441 // Make sure the `submit_button()` function is available during the REST API call |
2656 /* |
2442 // from WP_Site_Health_Auto_Updates::test_check_wp_filesystem_method(). |
2657 * Make sure the `submit_button()` function is available during the REST API call |
|
2658 * from WP_Site_Health_Auto_Updates::test_check_wp_filesystem_method(). |
|
2659 */ |
2443 if ( ! function_exists( 'submit_button' ) ) { |
2660 if ( ! function_exists( 'submit_button' ) ) { |
2444 require_once ABSPATH . '/wp-admin/includes/template.php'; |
2661 require_once ABSPATH . 'wp-admin/includes/template.php'; |
2445 } |
2662 } |
2446 ?> |
2663 ?> |
2447 <p class="request-filesystem-credentials-action-buttons"> |
2664 <p class="request-filesystem-credentials-action-buttons"> |
2448 <?php wp_nonce_field( 'filesystem-credentials', '_fs_nonce', false, true ); ?> |
2665 <?php wp_nonce_field( 'filesystem-credentials', '_fs_nonce', false, true ); ?> |
2449 <button class="button cancel-button" data-js-action="close" type="button"><?php _e( 'Cancel' ); ?></button> |
2666 <button class="button cancel-button" data-js-action="close" type="button"><?php _e( 'Cancel' ); ?></button> |
2450 <?php submit_button( __( 'Proceed' ), '', 'upgrade', false ); ?> |
2667 <?php submit_button( __( 'Proceed' ), 'primary', 'upgrade', false ); ?> |
2451 </p> |
2668 </p> |
2452 </div> |
2669 </div> |
2453 </form> |
2670 </form> |
2454 <?php |
2671 <?php |
2455 return false; |
2672 return false; |
2553 return opcache_invalidate( $filepath, $force ); |
2770 return opcache_invalidate( $filepath, $force ); |
2554 } |
2771 } |
2555 |
2772 |
2556 return false; |
2773 return false; |
2557 } |
2774 } |
|
2775 |
|
2776 /** |
|
2777 * Attempts to clear the opcode cache for a directory of files. |
|
2778 * |
|
2779 * @since 6.2.0 |
|
2780 * |
|
2781 * @see wp_opcache_invalidate() |
|
2782 * @link https://www.php.net/manual/en/function.opcache-invalidate.php |
|
2783 * |
|
2784 * @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass. |
|
2785 * |
|
2786 * @param string $dir The path to the directory for which the opcode cache is to be cleared. |
|
2787 */ |
|
2788 function wp_opcache_invalidate_directory( $dir ) { |
|
2789 global $wp_filesystem; |
|
2790 |
|
2791 if ( ! is_string( $dir ) || '' === trim( $dir ) ) { |
|
2792 if ( WP_DEBUG ) { |
|
2793 $error_message = sprintf( |
|
2794 /* translators: %s: The function name. */ |
|
2795 __( '%s expects a non-empty string.' ), |
|
2796 '<code>wp_opcache_invalidate_directory()</code>' |
|
2797 ); |
|
2798 wp_trigger_error( '', $error_message ); |
|
2799 } |
|
2800 return; |
|
2801 } |
|
2802 |
|
2803 $dirlist = $wp_filesystem->dirlist( $dir, false, true ); |
|
2804 |
|
2805 if ( empty( $dirlist ) ) { |
|
2806 return; |
|
2807 } |
|
2808 |
|
2809 /* |
|
2810 * Recursively invalidate opcache of files in a directory. |
|
2811 * |
|
2812 * WP_Filesystem_*::dirlist() returns an array of file and directory information. |
|
2813 * |
|
2814 * This does not include a path to the file or directory. |
|
2815 * To invalidate files within sub-directories, recursion is needed |
|
2816 * to prepend an absolute path containing the sub-directory's name. |
|
2817 * |
|
2818 * @param array $dirlist Array of file/directory information from WP_Filesystem_Base::dirlist(), |
|
2819 * with sub-directories represented as nested arrays. |
|
2820 * @param string $path Absolute path to the directory. |
|
2821 */ |
|
2822 $invalidate_directory = static function ( $dirlist, $path ) use ( &$invalidate_directory ) { |
|
2823 $path = trailingslashit( $path ); |
|
2824 |
|
2825 foreach ( $dirlist as $name => $details ) { |
|
2826 if ( 'f' === $details['type'] ) { |
|
2827 wp_opcache_invalidate( $path . $name, true ); |
|
2828 } elseif ( is_array( $details['files'] ) && ! empty( $details['files'] ) ) { |
|
2829 $invalidate_directory( $details['files'], $path . $name ); |
|
2830 } |
|
2831 } |
|
2832 }; |
|
2833 |
|
2834 $invalidate_directory( $dirlist, $dir ); |
|
2835 } |