changeset 16 | a86126ab1dd4 |
parent 9 | 177826044cd9 |
child 18 | be944660c56a |
15:3d4e9c994f10 | 16:a86126ab1dd4 |
---|---|
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 // Archives |
24 // Archives. |
25 'index.php' => __( 'Main Index Template' ), |
25 'index.php' => __( 'Main Index Template' ), |
26 'archive.php' => __( 'Archives' ), |
26 'archive.php' => __( 'Archives' ), |
27 'author.php' => __( 'Author Template' ), |
27 'author.php' => __( 'Author Template' ), |
28 'taxonomy.php' => __( 'Taxonomy Template' ), |
28 'taxonomy.php' => __( 'Taxonomy Template' ), |
29 'category.php' => __( 'Category Template' ), |
29 'category.php' => __( 'Category Template' ), |
30 'tag.php' => __( 'Tag Template' ), |
30 'tag.php' => __( 'Tag Template' ), |
31 'home.php' => __( 'Posts Page' ), |
31 'home.php' => __( 'Posts Page' ), |
32 'search.php' => __( 'Search Results' ), |
32 'search.php' => __( 'Search Results' ), |
33 'date.php' => __( 'Date Template' ), |
33 'date.php' => __( 'Date Template' ), |
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 'privacy-policy.php' => __( 'Privacy Policy Page' ), |
40 // Attachments |
40 // Attachments. |
41 'attachment.php' => __( 'Attachment Template' ), |
41 'attachment.php' => __( 'Attachment Template' ), |
42 'image.php' => __( 'Image Attachment Template' ), |
42 'image.php' => __( 'Image Attachment Template' ), |
43 'video.php' => __( 'Video Attachment Template' ), |
43 'video.php' => __( 'Video Attachment Template' ), |
44 'audio.php' => __( 'Audio Attachment Template' ), |
44 'audio.php' => __( 'Audio Attachment Template' ), |
45 'application.php' => __( 'Application Attachment Template' ), |
45 'application.php' => __( 'Application Attachment Template' ), |
46 // Embeds |
46 // Embeds. |
47 'embed.php' => __( 'Embed Template' ), |
47 'embed.php' => __( 'Embed Template' ), |
48 'embed-404.php' => __( 'Embed 404 Template' ), |
48 'embed-404.php' => __( 'Embed 404 Template' ), |
49 'embed-content.php' => __( 'Embed Content Template' ), |
49 'embed-content.php' => __( 'Embed Content Template' ), |
50 'header-embed.php' => __( 'Embed Header Template' ), |
50 'header-embed.php' => __( 'Embed Header Template' ), |
51 'footer-embed.php' => __( 'Embed Footer Template' ), |
51 'footer-embed.php' => __( 'Embed Footer Template' ), |
52 // Stylesheets |
52 // Stylesheets. |
53 'style.css' => __( 'Stylesheet' ), |
53 'style.css' => __( 'Stylesheet' ), |
54 'editor-style.css' => __( 'Visual Editor Stylesheet' ), |
54 'editor-style.css' => __( 'Visual Editor Stylesheet' ), |
55 'editor-style-rtl.css' => __( 'Visual Editor RTL Stylesheet' ), |
55 'editor-style-rtl.css' => __( 'Visual Editor RTL Stylesheet' ), |
56 'rtl.css' => __( 'RTL Stylesheet' ), |
56 'rtl.css' => __( 'RTL Stylesheet' ), |
57 // Other |
57 // Other. |
58 'my-hacks.php' => __( 'my-hacks.php (legacy hacks support)' ), |
58 'my-hacks.php' => __( 'my-hacks.php (legacy hacks support)' ), |
59 '.htaccess' => __( '.htaccess (for rewrite rules )' ), |
59 '.htaccess' => __( '.htaccess (for rewrite rules )' ), |
60 // Deprecated files |
60 // Deprecated files. |
61 'wp-layout.css' => __( 'Stylesheet' ), |
61 'wp-layout.css' => __( 'Stylesheet' ), |
62 'wp-comments.php' => __( 'Comments Template' ), |
62 'wp-comments.php' => __( 'Comments Template' ), |
63 'wp-comments-popup.php' => __( 'Popup Comments Template' ), |
63 'wp-comments-popup.php' => __( 'Popup Comments Template' ), |
64 'comments-popup.php' => __( 'Popup Comments' ), |
64 'comments-popup.php' => __( 'Popup Comments' ), |
65 ); |
65 ); |
66 |
66 |
67 /** |
67 /** |
68 * Get the description for standard WordPress theme files and other various standard |
68 * Gets the description for standard WordPress theme files. |
69 * WordPress files |
|
70 * |
69 * |
71 * @since 1.5.0 |
70 * @since 1.5.0 |
72 * |
71 * |
73 * @global array $wp_file_descriptions Theme file descriptions. |
72 * @global array $wp_file_descriptions Theme file descriptions. |
74 * @global array $allowed_files List of allowed files. |
73 * @global array $allowed_files List of allowed files. |
75 * @param string $file Filesystem path or filename |
74 * |
75 * @param string $file Filesystem path or filename. |
|
76 * @return string Description of file from $wp_file_descriptions or basename of $file if description doesn't exist. |
76 * @return string Description of file from $wp_file_descriptions or basename of $file if description doesn't exist. |
77 * Appends 'Page Template' to basename of $file if the file is a page template |
77 * Appends 'Page Template' to basename of $file if the file is a page template. |
78 */ |
78 */ |
79 function get_file_description( $file ) { |
79 function get_file_description( $file ) { |
80 global $wp_file_descriptions, $allowed_files; |
80 global $wp_file_descriptions, $allowed_files; |
81 |
81 |
82 $dirname = pathinfo( $file, PATHINFO_DIRNAME ); |
82 $dirname = pathinfo( $file, PATHINFO_DIRNAME ); |
83 |
|
84 $file_path = $allowed_files[ $file ]; |
83 $file_path = $allowed_files[ $file ]; |
84 |
|
85 if ( isset( $wp_file_descriptions[ basename( $file ) ] ) && '.' === $dirname ) { |
85 if ( isset( $wp_file_descriptions[ basename( $file ) ] ) && '.' === $dirname ) { |
86 return $wp_file_descriptions[ basename( $file ) ]; |
86 return $wp_file_descriptions[ basename( $file ) ]; |
87 } elseif ( file_exists( $file_path ) && is_file( $file_path ) ) { |
87 } elseif ( file_exists( $file_path ) && is_file( $file_path ) ) { |
88 $template_data = implode( '', file( $file_path ) ); |
88 $template_data = implode( '', file( $file_path ) ); |
89 |
|
89 if ( preg_match( '|Template Name:(.*)$|mi', $template_data, $name ) ) { |
90 if ( preg_match( '|Template Name:(.*)$|mi', $template_data, $name ) ) { |
91 /* translators: %s: Template name. */ |
|
90 return sprintf( __( '%s Page Template' ), _cleanup_header_comment( $name[1] ) ); |
92 return sprintf( __( '%s Page Template' ), _cleanup_header_comment( $name[1] ) ); |
91 } |
93 } |
92 } |
94 } |
93 |
95 |
94 return trim( basename( $file ) ); |
96 return trim( basename( $file ) ); |
95 } |
97 } |
96 |
98 |
97 /** |
99 /** |
98 * Get the absolute filesystem path to the root of the WordPress installation |
100 * Gets the absolute filesystem path to the root of the WordPress installation. |
99 * |
101 * |
100 * @since 1.5.0 |
102 * @since 1.5.0 |
101 * |
103 * |
102 * @return string Full filesystem path to the root of the WordPress installation |
104 * @return string Full filesystem path to the root of the WordPress installation. |
103 */ |
105 */ |
104 function get_home_path() { |
106 function get_home_path() { |
105 $home = set_url_scheme( get_option( 'home' ), 'http' ); |
107 $home = set_url_scheme( get_option( 'home' ), 'http' ); |
106 $siteurl = set_url_scheme( get_option( 'siteurl' ), 'http' ); |
108 $siteurl = set_url_scheme( get_option( 'siteurl' ), 'http' ); |
109 |
|
107 if ( ! empty( $home ) && 0 !== strcasecmp( $home, $siteurl ) ) { |
110 if ( ! empty( $home ) && 0 !== strcasecmp( $home, $siteurl ) ) { |
108 $wp_path_rel_to_home = str_ireplace( $home, '', $siteurl ); /* $siteurl - $home */ |
111 $wp_path_rel_to_home = str_ireplace( $home, '', $siteurl ); /* $siteurl - $home */ |
109 $pos = strripos( str_replace( '\\', '/', $_SERVER['SCRIPT_FILENAME'] ), trailingslashit( $wp_path_rel_to_home ) ); |
112 $pos = strripos( str_replace( '\\', '/', $_SERVER['SCRIPT_FILENAME'] ), trailingslashit( $wp_path_rel_to_home ) ); |
110 $home_path = substr( $_SERVER['SCRIPT_FILENAME'], 0, $pos ); |
113 $home_path = substr( $_SERVER['SCRIPT_FILENAME'], 0, $pos ); |
111 $home_path = trailingslashit( $home_path ); |
114 $home_path = trailingslashit( $home_path ); |
116 return str_replace( '\\', '/', $home_path ); |
119 return str_replace( '\\', '/', $home_path ); |
117 } |
120 } |
118 |
121 |
119 /** |
122 /** |
120 * Returns a listing of all files in the specified folder and all subdirectories up to 100 levels deep. |
123 * Returns a listing of all files in the specified folder and all subdirectories up to 100 levels deep. |
124 * |
|
121 * The depth of the recursiveness can be controlled by the $levels param. |
125 * The depth of the recursiveness can be controlled by the $levels param. |
122 * |
126 * |
123 * @since 2.6.0 |
127 * @since 2.6.0 |
124 * @since 4.9.0 Added the `$exclusions` parameter. |
128 * @since 4.9.0 Added the `$exclusions` parameter. |
125 * |
129 * |
163 } |
167 } |
164 } else { |
168 } else { |
165 $files[] = $folder . $file; |
169 $files[] = $folder . $file; |
166 } |
170 } |
167 } |
171 } |
168 } |
172 |
169 @closedir( $dir ); |
173 closedir( $dir ); |
174 } |
|
170 |
175 |
171 return $files; |
176 return $files; |
172 } |
177 } |
173 |
178 |
174 /** |
179 /** |
175 * Get list of file extensions that are editable in plugins. |
180 * Gets the list of file extensions that are editable in plugins. |
176 * |
181 * |
177 * @since 4.9.0 |
182 * @since 4.9.0 |
178 * |
183 * |
179 * @param string $plugin Path to the plugin file relative to the plugins directory. |
184 * @param string $plugin Path to the plugin file relative to the plugins directory. |
180 * @return string[] Array of editable file extensions. |
185 * @return string[] Array of editable file extensions. |
181 */ |
186 */ |
182 function wp_get_plugin_file_editable_extensions( $plugin ) { |
187 function wp_get_plugin_file_editable_extensions( $plugin ) { |
183 |
|
184 $editable_extensions = array( |
|
185 'bash', |
|
186 'conf', |
|
187 'css', |
|
188 'diff', |
|
189 'htm', |
|
190 'html', |
|
191 'http', |
|
192 'inc', |
|
193 'include', |
|
194 'js', |
|
195 'json', |
|
196 'jsx', |
|
197 'less', |
|
198 'md', |
|
199 'patch', |
|
200 'php', |
|
201 'php3', |
|
202 'php4', |
|
203 'php5', |
|
204 'php7', |
|
205 'phps', |
|
206 'phtml', |
|
207 'sass', |
|
208 'scss', |
|
209 'sh', |
|
210 'sql', |
|
211 'svg', |
|
212 'text', |
|
213 'txt', |
|
214 'xml', |
|
215 'yaml', |
|
216 'yml', |
|
217 ); |
|
218 |
|
219 /** |
|
220 * Filters file type extensions editable in the plugin editor. |
|
221 * |
|
222 * @since 2.8.0 |
|
223 * @since 4.9.0 Added the `$plugin` parameter. |
|
224 * |
|
225 * @param string[] $editable_extensions An array of editable plugin file extensions. |
|
226 * @param string $plugin Path to the plugin file relative to the plugins directory. |
|
227 */ |
|
228 $editable_extensions = (array) apply_filters( 'editable_extensions', $editable_extensions, $plugin ); |
|
229 |
|
230 return $editable_extensions; |
|
231 } |
|
232 |
|
233 /** |
|
234 * Get list of file extensions that are editable for a given theme. |
|
235 * |
|
236 * @param WP_Theme $theme Theme object. |
|
237 * @return string[] Array of editable file extensions. |
|
238 */ |
|
239 function wp_get_theme_file_editable_extensions( $theme ) { |
|
240 |
188 |
241 $default_types = array( |
189 $default_types = array( |
242 'bash', |
190 'bash', |
243 'conf', |
191 'conf', |
244 'css', |
192 'css', |
272 'yaml', |
220 'yaml', |
273 'yml', |
221 'yml', |
274 ); |
222 ); |
275 |
223 |
276 /** |
224 /** |
277 * Filters the list of file types allowed for editing in the Theme editor. |
225 * Filters the list of file types allowed for editing in the plugin editor. |
226 * |
|
227 * @since 2.8.0 |
|
228 * @since 4.9.0 Added the `$plugin` parameter. |
|
229 * |
|
230 * @param string[] $default_types An array of editable plugin file extensions. |
|
231 * @param string $plugin Path to the plugin file relative to the plugins directory. |
|
232 */ |
|
233 $file_types = (array) apply_filters( 'editable_extensions', $default_types, $plugin ); |
|
234 |
|
235 return $file_types; |
|
236 } |
|
237 |
|
238 /** |
|
239 * Gets the list of file extensions that are editable for a given theme. |
|
240 * |
|
241 * @since 4.9.0 |
|
242 * |
|
243 * @param WP_Theme $theme Theme object. |
|
244 * @return string[] Array of editable file extensions. |
|
245 */ |
|
246 function wp_get_theme_file_editable_extensions( $theme ) { |
|
247 |
|
248 $default_types = array( |
|
249 'bash', |
|
250 'conf', |
|
251 'css', |
|
252 'diff', |
|
253 'htm', |
|
254 'html', |
|
255 'http', |
|
256 'inc', |
|
257 'include', |
|
258 'js', |
|
259 'json', |
|
260 'jsx', |
|
261 'less', |
|
262 'md', |
|
263 'patch', |
|
264 'php', |
|
265 'php3', |
|
266 'php4', |
|
267 'php5', |
|
268 'php7', |
|
269 'phps', |
|
270 'phtml', |
|
271 'sass', |
|
272 'scss', |
|
273 'sh', |
|
274 'sql', |
|
275 'svg', |
|
276 'text', |
|
277 'txt', |
|
278 'xml', |
|
279 'yaml', |
|
280 'yml', |
|
281 ); |
|
282 |
|
283 /** |
|
284 * Filters the list of file types allowed for editing in the theme editor. |
|
278 * |
285 * |
279 * @since 4.4.0 |
286 * @since 4.4.0 |
280 * |
287 * |
281 * @param string[] $default_types List of allowed file types. |
288 * @param string[] $default_types An array of editable theme file extensions. |
282 * @param WP_Theme $theme The current Theme object. |
289 * @param WP_Theme $theme The current theme object. |
283 */ |
290 */ |
284 $file_types = apply_filters( 'wp_theme_editor_filetypes', $default_types, $theme ); |
291 $file_types = apply_filters( 'wp_theme_editor_filetypes', $default_types, $theme ); |
285 |
292 |
286 // Ensure that default types are still there. |
293 // Ensure that default types are still there. |
287 return array_unique( array_merge( $file_types, $default_types ) ); |
294 return array_unique( array_merge( $file_types, $default_types ) ); |
288 } |
295 } |
289 |
296 |
290 /** |
297 /** |
291 * Print file editor templates (for plugins and themes). |
298 * Prints file editor templates (for plugins and themes). |
292 * |
299 * |
293 * @since 4.9.0 |
300 * @since 4.9.0 |
294 */ |
301 */ |
295 function wp_print_file_editor_templates() { |
302 function wp_print_file_editor_templates() { |
296 ?> |
303 ?> |
298 <div class="notice inline notice-{{ data.type || 'info' }} {{ data.alt ? 'notice-alt' : '' }} {{ data.dismissible ? 'is-dismissible' : '' }} {{ data.classes || '' }}"> |
305 <div class="notice inline notice-{{ data.type || 'info' }} {{ data.alt ? 'notice-alt' : '' }} {{ data.dismissible ? 'is-dismissible' : '' }} {{ data.classes || '' }}"> |
299 <# if ( 'php_error' === data.code ) { #> |
306 <# if ( 'php_error' === data.code ) { #> |
300 <p> |
307 <p> |
301 <?php |
308 <?php |
302 printf( |
309 printf( |
303 /* translators: 1: line number, 2: file path */ |
310 /* translators: 1: Line number, 2: File path. */ |
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.' ), |
311 __( '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.' ), |
305 '{{ data.line }}', |
312 '{{ data.line }}', |
306 '{{ data.file }}' |
313 '{{ data.file }}' |
307 ); |
314 ); |
308 ?> |
315 ?> |
309 </p> |
316 </p> |
310 <pre>{{ data.message }}</pre> |
317 <pre>{{ data.message }}</pre> |
311 <# } else if ( 'file_not_writable' === data.code ) { #> |
318 <# } else if ( 'file_not_writable' === data.code ) { #> |
312 <p><?php _e( 'You need to make this file writable before you can save your changes. See <a href="https://codex.wordpress.org/Changing_File_Permissions">the Codex</a> for more information.' ); ?></p> |
319 <p> |
320 <?php |
|
321 printf( |
|
322 /* translators: %s: Documentation URL. */ |
|
323 __( 'You need to make this file writable before you can save your changes. See <a href="%s">Changing File Permissions</a> for more information.' ), |
|
324 __( 'https://wordpress.org/support/article/changing-file-permissions/' ) |
|
325 ); |
|
326 ?> |
|
327 </p> |
|
313 <# } else { #> |
328 <# } else { #> |
314 <p>{{ data.message || data.code }}</p> |
329 <p>{{ data.message || data.code }}</p> |
315 |
330 |
316 <# if ( 'lint_errors' === data.code ) { #> |
331 <# if ( 'lint_errors' === data.code ) { #> |
317 <p> |
332 <p> |
328 </script> |
343 </script> |
329 <?php |
344 <?php |
330 } |
345 } |
331 |
346 |
332 /** |
347 /** |
333 * Attempt to edit a file for a theme or plugin. |
348 * Attempts to edit a file for a theme or plugin. |
334 * |
349 * |
335 * When editing a PHP file, loopback requests will be made to the admin and the homepage |
350 * When editing a PHP file, loopback requests will be made to the admin and the homepage |
336 * to attempt to see if there is a fatal error introduced. If so, the PHP change will be |
351 * to attempt to see if there is a fatal error introduced. If so, the PHP change will be |
337 * reverted. |
352 * reverted. |
338 * |
353 * |
443 $allowed_files = array_merge( $allowed_files, $theme->get_files( $type, -1 ) ); |
458 $allowed_files = array_merge( $allowed_files, $theme->get_files( $type, -1 ) ); |
444 break; |
459 break; |
445 } |
460 } |
446 } |
461 } |
447 |
462 |
448 // Compare based on relative paths |
463 // Compare based on relative paths. |
449 if ( 0 !== validate_file( $file, array_keys( $allowed_files ) ) ) { |
464 if ( 0 !== validate_file( $file, array_keys( $allowed_files ) ) ) { |
450 return new WP_Error( 'disallowed_theme_file', __( 'Sorry, that file cannot be edited.' ) ); |
465 return new WP_Error( 'disallowed_theme_file', __( 'Sorry, that file cannot be edited.' ) ); |
451 } |
466 } |
452 |
467 |
453 $real_file = $theme->get_stylesheet_directory() . '/' . $file; |
468 $real_file = $theme->get_stylesheet_directory() . '/' . $file; |
458 return new WP_Error( 'missing_theme_or_plugin' ); |
473 return new WP_Error( 'missing_theme_or_plugin' ); |
459 } |
474 } |
460 |
475 |
461 // Ensure file is real. |
476 // Ensure file is real. |
462 if ( ! is_file( $real_file ) ) { |
477 if ( ! is_file( $real_file ) ) { |
463 return new WP_Error( 'file_does_not_exist', __( 'No such file exists! Double check the name and try again.' ) ); |
478 return new WP_Error( 'file_does_not_exist', __( 'File does not exist! Please double check the name and try again.' ) ); |
464 } |
479 } |
465 |
480 |
466 // Ensure file extension is allowed. |
481 // Ensure file extension is allowed. |
467 $extension = null; |
482 $extension = null; |
468 if ( preg_match( '/\.([^.]+)$/', $real_file, $matches ) ) { |
483 if ( preg_match( '/\.([^.]+)$/', $real_file, $matches ) ) { |
486 $written = fwrite( $f, $content ); |
501 $written = fwrite( $f, $content ); |
487 fclose( $f ); |
502 fclose( $f ); |
488 if ( false === $written ) { |
503 if ( false === $written ) { |
489 return new WP_Error( 'unable_to_write', __( 'Unable to write to file.' ) ); |
504 return new WP_Error( 'unable_to_write', __( 'Unable to write to file.' ) ); |
490 } |
505 } |
491 if ( 'php' === $extension && function_exists( 'opcache_invalidate' ) ) { |
506 |
492 opcache_invalidate( $real_file, true ); |
507 wp_opcache_invalidate( $real_file, true ); |
493 } |
|
494 |
508 |
495 if ( $is_active && 'php' === $extension ) { |
509 if ( $is_active && 'php' === $extension ) { |
496 |
510 |
497 $scrape_key = md5( rand() ); |
511 $scrape_key = md5( rand() ); |
498 $transient = 'scrape_key_' . $scrape_key; |
512 $transient = 'scrape_key_' . $scrape_key; |
499 $scrape_nonce = strval( rand() ); |
513 $scrape_nonce = strval( rand() ); |
500 set_transient( $transient, $scrape_nonce, 60 ); // It shouldn't take more than 60 seconds to make the two loopback requests. |
514 // It shouldn't take more than 60 seconds to make the two loopback requests. |
515 set_transient( $transient, $scrape_nonce, 60 ); |
|
501 |
516 |
502 $cookies = wp_unslash( $_COOKIE ); |
517 $cookies = wp_unslash( $_COOKIE ); |
503 $scrape_params = array( |
518 $scrape_params = array( |
504 'wp_scrape_key' => $scrape_key, |
519 'wp_scrape_key' => $scrape_key, |
505 'wp_scrape_nonce' => $scrape_nonce, |
520 'wp_scrape_nonce' => $scrape_nonce, |
506 ); |
521 ); |
507 $headers = array( |
522 $headers = array( |
508 'Cache-Control' => 'no-cache', |
523 'Cache-Control' => 'no-cache', |
509 ); |
524 ); |
510 |
525 |
526 /** This filter is documented in wp-includes/class-wp-http-streams.php */ |
|
527 $sslverify = apply_filters( 'https_local_ssl_verify', false ); |
|
528 |
|
511 // Include Basic auth in loopback requests. |
529 // Include Basic auth in loopback requests. |
512 if ( isset( $_SERVER['PHP_AUTH_USER'] ) && isset( $_SERVER['PHP_AUTH_PW'] ) ) { |
530 if ( isset( $_SERVER['PHP_AUTH_USER'] ) && isset( $_SERVER['PHP_AUTH_PW'] ) ) { |
513 $headers['Authorization'] = 'Basic ' . base64_encode( wp_unslash( $_SERVER['PHP_AUTH_USER'] ) . ':' . wp_unslash( $_SERVER['PHP_AUTH_PW'] ) ); |
531 $headers['Authorization'] = 'Basic ' . base64_encode( wp_unslash( $_SERVER['PHP_AUTH_USER'] ) . ':' . wp_unslash( $_SERVER['PHP_AUTH_PW'] ) ); |
514 } |
532 } |
515 |
533 |
516 // Make sure PHP process doesn't die before loopback requests complete. |
534 // Make sure PHP process doesn't die before loopback requests complete. |
517 @set_time_limit( 300 ); |
535 set_time_limit( 300 ); |
518 |
536 |
519 // Time to wait for loopback requests to finish. |
537 // Time to wait for loopback requests to finish. |
520 $timeout = 100; |
538 $timeout = 100; |
521 |
539 |
522 $needle_start = "###### wp_scraping_result_start:$scrape_key ######"; |
540 $needle_start = "###### wp_scraping_result_start:$scrape_key ######"; |
534 admin_url( 'theme-editor.php' ) |
552 admin_url( 'theme-editor.php' ) |
535 ); |
553 ); |
536 } else { |
554 } else { |
537 $url = admin_url(); |
555 $url = admin_url(); |
538 } |
556 } |
557 |
|
558 if ( function_exists( 'session_status' ) && PHP_SESSION_ACTIVE === session_status() ) { |
|
559 // Close any active session to prevent HTTP requests from timing out |
|
560 // when attempting to connect back to the site. |
|
561 session_write_close(); |
|
562 } |
|
563 |
|
539 $url = add_query_arg( $scrape_params, $url ); |
564 $url = add_query_arg( $scrape_params, $url ); |
540 $r = wp_remote_get( $url, compact( 'cookies', 'headers', 'timeout' ) ); |
565 $r = wp_remote_get( $url, compact( 'cookies', 'headers', 'timeout', 'sslverify' ) ); |
541 $body = wp_remote_retrieve_body( $r ); |
566 $body = wp_remote_retrieve_body( $r ); |
542 $scrape_result_position = strpos( $body, $needle_start ); |
567 $scrape_result_position = strpos( $body, $needle_start ); |
543 |
568 |
544 $loopback_request_failure = array( |
569 $loopback_request_failure = array( |
545 'code' => 'loopback_request_failed', |
570 'code' => 'loopback_request_failed', |
563 |
588 |
564 // Try making request to homepage as well to see if visitors have been whitescreened. |
589 // Try making request to homepage as well to see if visitors have been whitescreened. |
565 if ( true === $result ) { |
590 if ( true === $result ) { |
566 $url = home_url( '/' ); |
591 $url = home_url( '/' ); |
567 $url = add_query_arg( $scrape_params, $url ); |
592 $url = add_query_arg( $scrape_params, $url ); |
568 $r = wp_remote_get( $url, compact( 'cookies', 'headers', 'timeout' ) ); |
593 $r = wp_remote_get( $url, compact( 'cookies', 'headers', 'timeout', 'sslverify' ) ); |
569 $body = wp_remote_retrieve_body( $r ); |
594 $body = wp_remote_retrieve_body( $r ); |
570 $scrape_result_position = strpos( $body, $needle_start ); |
595 $scrape_result_position = strpos( $body, $needle_start ); |
571 |
596 |
572 if ( false === $scrape_result_position ) { |
597 if ( false === $scrape_result_position ) { |
573 $result = $loopback_request_failure; |
598 $result = $loopback_request_failure; |
585 |
610 |
586 if ( true !== $result ) { |
611 if ( true !== $result ) { |
587 |
612 |
588 // Roll-back file change. |
613 // Roll-back file change. |
589 file_put_contents( $real_file, $previous_content ); |
614 file_put_contents( $real_file, $previous_content ); |
590 if ( function_exists( 'opcache_invalidate' ) ) { |
615 wp_opcache_invalidate( $real_file, true ); |
591 opcache_invalidate( $real_file, true ); |
|
592 } |
|
593 |
616 |
594 if ( ! isset( $result['message'] ) ) { |
617 if ( ! isset( $result['message'] ) ) { |
595 $message = __( 'Something went wrong.' ); |
618 $message = __( 'Something went wrong.' ); |
596 } else { |
619 } else { |
597 $message = $result['message']; |
620 $message = $result['message']; |
608 return true; |
631 return true; |
609 } |
632 } |
610 |
633 |
611 |
634 |
612 /** |
635 /** |
613 * Returns a filename of a Temporary unique file. |
636 * Returns a filename of a temporary unique file. |
637 * |
|
614 * Please note that the calling function must unlink() this itself. |
638 * Please note that the calling function must unlink() this itself. |
615 * |
639 * |
616 * The filename is based off the passed parameter or defaults to the current unix timestamp, |
640 * The filename is based off the passed parameter or defaults to the current unix timestamp, |
617 * while the directory can either be passed as well, or by leaving it blank, default to a writable temporary directory. |
641 * while the directory can either be passed as well, or by leaving it blank, default to a writable |
642 * temporary directory. |
|
618 * |
643 * |
619 * @since 2.6.0 |
644 * @since 2.6.0 |
620 * |
645 * |
621 * @param string $filename Optional. Filename to base the Unique file off. Default empty. |
646 * @param string $filename Optional. Filename to base the Unique file off. Default empty. |
622 * @param string $dir Optional. Directory to store the file in. Default empty. |
647 * @param string $dir Optional. Directory to store the file in. Default empty. |
623 * @return string a writable filename |
648 * @return string A writable filename. |
624 */ |
649 */ |
625 function wp_tempnam( $filename = '', $dir = '' ) { |
650 function wp_tempnam( $filename = '', $dir = '' ) { |
626 if ( empty( $dir ) ) { |
651 if ( empty( $dir ) ) { |
627 $dir = get_temp_dir(); |
652 $dir = get_temp_dir(); |
628 } |
653 } |
629 |
654 |
630 if ( empty( $filename ) || '.' == $filename || '/' == $filename || '\\' == $filename ) { |
655 if ( empty( $filename ) || in_array( $filename, array( '.', '/', '\\' ), true ) ) { |
631 $filename = uniqid(); |
656 $filename = uniqid(); |
632 } |
657 } |
633 |
658 |
634 // Use the basename of the given file without the extension as the name for the temporary directory |
659 // Use the basename of the given file without the extension as the name for the temporary directory. |
635 $temp_filename = basename( $filename ); |
660 $temp_filename = basename( $filename ); |
636 $temp_filename = preg_replace( '|\.[^.]*$|', '', $temp_filename ); |
661 $temp_filename = preg_replace( '|\.[^.]*$|', '', $temp_filename ); |
637 |
662 |
638 // If the folder is falsey, use its parent directory name instead. |
663 // If the folder is falsey, use its parent directory name instead. |
639 if ( ! $temp_filename ) { |
664 if ( ! $temp_filename ) { |
640 return wp_tempnam( dirname( $filename ), $dir ); |
665 return wp_tempnam( dirname( $filename ), $dir ); |
641 } |
666 } |
642 |
667 |
643 // Suffix some random data to avoid filename conflicts |
668 // Suffix some random data to avoid filename conflicts. |
644 $temp_filename .= '-' . wp_generate_password( 6, false ); |
669 $temp_filename .= '-' . wp_generate_password( 6, false ); |
645 $temp_filename .= '.tmp'; |
670 $temp_filename .= '.tmp'; |
646 $temp_filename = $dir . wp_unique_filename( $dir, $temp_filename ); |
671 $temp_filename = $dir . wp_unique_filename( $dir, $temp_filename ); |
647 |
672 |
648 $fp = @fopen( $temp_filename, 'x' ); |
673 $fp = @fopen( $temp_filename, 'x' ); |
662 * Function will die if you are not allowed to edit the file. |
687 * Function will die if you are not allowed to edit the file. |
663 * |
688 * |
664 * @since 1.5.0 |
689 * @since 1.5.0 |
665 * |
690 * |
666 * @param string $file File the user is attempting to edit. |
691 * @param string $file File the user is attempting to edit. |
667 * @param string[] $allowed_files Optional. Array of allowed files to edit. `$file` must match an entry exactly. |
692 * @param string[] $allowed_files Optional. Array of allowed files to edit. |
693 * `$file` must match an entry exactly. |
|
668 * @return string|void Returns the file name on success, dies on failure. |
694 * @return string|void Returns the file name on success, dies on failure. |
669 */ |
695 */ |
670 function validate_file_to_edit( $file, $allowed_files = array() ) { |
696 function validate_file_to_edit( $file, $allowed_files = array() ) { |
671 $code = validate_file( $file, $allowed_files ); |
697 $code = validate_file( $file, $allowed_files ); |
672 |
698 |
685 wp_die( __( 'Sorry, that file cannot be edited.' ) ); |
711 wp_die( __( 'Sorry, that file cannot be edited.' ) ); |
686 } |
712 } |
687 } |
713 } |
688 |
714 |
689 /** |
715 /** |
690 * Handle PHP uploads in WordPress, sanitizing file names, checking extensions for mime type, |
716 * Handles PHP uploads in WordPress. |
691 * and moving the file to the appropriate directory within the uploads directory. |
717 * |
718 * Sanitizes file names, checks extensions for mime type, and moves the file |
|
719 * to the appropriate directory within the uploads directory. |
|
692 * |
720 * |
693 * @access private |
721 * @access private |
694 * @since 4.0.0 |
722 * @since 4.0.0 |
695 * |
723 * |
696 * @see wp_handle_upload_error |
724 * @see wp_handle_upload_error |
697 * |
725 * |
698 * @param string[] $file Reference to a single element of `$_FILES`. Call the function once for each uploaded file. |
726 * @param string[] $file Reference to a single element of `$_FILES`. |
699 * @param string[]|false $overrides An associative array of names => values to override default variables. Default false. |
727 * Call the function once for each uploaded file. |
728 * @param string[]|false $overrides An associative array of names => values |
|
729 * to override default variables. Default false. |
|
700 * @param string $time Time formatted in 'yyyy/mm'. |
730 * @param string $time Time formatted in 'yyyy/mm'. |
701 * @param string $action Expected value for `$_POST['action']`. |
731 * @param string $action Expected value for `$_POST['action']`. |
702 * @return string[] On success, returns an associative array of file attributes. On failure, returns |
732 * @return string[] On success, returns an associative array of file attributes. |
703 * `$overrides['upload_error_handler'](&$file, $message )` or `array( 'error'=>$message )`. |
733 * On failure, returns `$overrides['upload_error_handler']( &$file, $message )` |
734 * or `array( 'error' => $message )`. |
|
704 */ |
735 */ |
705 function _wp_handle_upload( &$file, $overrides, $time, $action ) { |
736 function _wp_handle_upload( &$file, $overrides, $time, $action ) { |
706 // The default error handler. |
737 // The default error handler. |
707 if ( ! function_exists( 'wp_handle_upload_error' ) ) { |
738 if ( ! function_exists( 'wp_handle_upload_error' ) ) { |
708 function wp_handle_upload_error( &$file, $message ) { |
739 function wp_handle_upload_error( &$file, $message ) { |
720 * |
751 * |
721 * @param string[] $file An array of data for a single file. |
752 * @param string[] $file An array of data for a single file. |
722 */ |
753 */ |
723 $file = apply_filters( "{$action}_prefilter", $file ); |
754 $file = apply_filters( "{$action}_prefilter", $file ); |
724 |
755 |
725 // You may define your own function and pass the name in $overrides['upload_error_handler'] |
756 // You may define your own function and pass the name in $overrides['upload_error_handler']. |
726 $upload_error_handler = 'wp_handle_upload_error'; |
757 $upload_error_handler = 'wp_handle_upload_error'; |
727 if ( isset( $overrides['upload_error_handler'] ) ) { |
758 if ( isset( $overrides['upload_error_handler'] ) ) { |
728 $upload_error_handler = $overrides['upload_error_handler']; |
759 $upload_error_handler = $overrides['upload_error_handler']; |
729 } |
760 } |
730 |
761 |
733 return call_user_func_array( $upload_error_handler, array( &$file, $file['error'] ) ); |
764 return call_user_func_array( $upload_error_handler, array( &$file, $file['error'] ) ); |
734 } |
765 } |
735 |
766 |
736 // Install user overrides. Did we mention that this voids your warranty? |
767 // Install user overrides. Did we mention that this voids your warranty? |
737 |
768 |
738 // You may define your own function and pass the name in $overrides['unique_filename_callback'] |
769 // You may define your own function and pass the name in $overrides['unique_filename_callback']. |
739 $unique_filename_callback = null; |
770 $unique_filename_callback = null; |
740 if ( isset( $overrides['unique_filename_callback'] ) ) { |
771 if ( isset( $overrides['unique_filename_callback'] ) ) { |
741 $unique_filename_callback = $overrides['unique_filename_callback']; |
772 $unique_filename_callback = $overrides['unique_filename_callback']; |
742 } |
773 } |
743 |
774 |
744 /* |
775 /* |
745 * This may not have orignially been intended to be overrideable, |
776 * This may not have originally been intended to be overridable, |
746 * but historically has been. |
777 * but historically has been. |
747 */ |
778 */ |
748 if ( isset( $overrides['upload_error_strings'] ) ) { |
779 if ( isset( $overrides['upload_error_strings'] ) ) { |
749 $upload_error_strings = $overrides['upload_error_strings']; |
780 $upload_error_strings = $overrides['upload_error_strings']; |
750 } else { |
781 } else { |
751 // Courtesy of php.net, the strings that describe the error indicated in $_FILES[{form field}]['error']. |
782 // Courtesy of php.net, the strings that describe the error indicated in $_FILES[{form field}]['error']. |
752 $upload_error_strings = array( |
783 $upload_error_strings = array( |
753 false, |
784 false, |
754 __( 'The uploaded file exceeds the upload_max_filesize directive in php.ini.' ), |
785 sprintf( |
755 __( 'The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form.' ), |
786 /* translators: 1: upload_max_filesize, 2: php.ini */ |
787 __( 'The uploaded file exceeds the %1$s directive in %2$s.' ), |
|
788 'upload_max_filesize', |
|
789 'php.ini' |
|
790 ), |
|
791 sprintf( |
|
792 /* translators: %s: MAX_FILE_SIZE */ |
|
793 __( 'The uploaded file exceeds the %s directive that was specified in the HTML form.' ), |
|
794 'MAX_FILE_SIZE' |
|
795 ), |
|
756 __( 'The uploaded file was only partially uploaded.' ), |
796 __( 'The uploaded file was only partially uploaded.' ), |
757 __( 'No file was uploaded.' ), |
797 __( 'No file was uploaded.' ), |
758 '', |
798 '', |
759 __( 'Missing a temporary folder.' ), |
799 __( 'Missing a temporary folder.' ), |
760 __( 'Failed to write file to disk.' ), |
800 __( 'Failed to write file to disk.' ), |
778 if ( isset( $file['error'] ) && $file['error'] > 0 ) { |
818 if ( isset( $file['error'] ) && $file['error'] > 0 ) { |
779 return call_user_func_array( $upload_error_handler, array( &$file, $upload_error_strings[ $file['error'] ] ) ); |
819 return call_user_func_array( $upload_error_handler, array( &$file, $upload_error_strings[ $file['error'] ] ) ); |
780 } |
820 } |
781 |
821 |
782 // A properly uploaded file will pass this test. There should be no reason to override this one. |
822 // 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'] ); |
823 $test_uploaded_file = 'wp_handle_upload' === $action ? is_uploaded_file( $file['tmp_name'] ) : @is_readable( $file['tmp_name'] ); |
784 if ( ! $test_uploaded_file ) { |
824 if ( ! $test_uploaded_file ) { |
785 return call_user_func_array( $upload_error_handler, array( &$file, __( 'Specified file failed upload test.' ) ) ); |
825 return call_user_func_array( $upload_error_handler, array( &$file, __( 'Specified file failed upload test.' ) ) ); |
786 } |
826 } |
787 |
827 |
788 $test_file_size = 'wp_handle_upload' === $action ? $file['size'] : filesize( $file['tmp_name'] ); |
828 $test_file_size = 'wp_handle_upload' === $action ? $file['size'] : filesize( $file['tmp_name'] ); |
789 // A non-empty file will pass this test. |
829 // A non-empty file will pass this test. |
790 if ( $test_size && ! ( $test_file_size > 0 ) ) { |
830 if ( $test_size && ! ( $test_file_size > 0 ) ) { |
791 if ( is_multisite() ) { |
831 if ( is_multisite() ) { |
792 $error_msg = __( 'File is empty. Please upload something more substantial.' ); |
832 $error_msg = __( 'File is empty. Please upload something more substantial.' ); |
793 } else { |
833 } else { |
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.' ); |
834 $error_msg = sprintf( |
835 /* translators: 1: php.ini, 2: post_max_size, 3: upload_max_filesize */ |
|
836 __( 'File is empty. Please upload something more substantial. This error could also be caused by uploads being disabled in your %1$s file or by %2$s being defined as smaller than %3$s in %1$s.' ), |
|
837 'php.ini', |
|
838 'post_max_size', |
|
839 'upload_max_filesize' |
|
840 ); |
|
795 } |
841 } |
796 return call_user_func_array( $upload_error_handler, array( &$file, $error_msg ) ); |
842 return call_user_func_array( $upload_error_handler, array( &$file, $error_msg ) ); |
797 } |
843 } |
798 |
844 |
799 // A correct MIME type will pass this test. Override $mimes or use the upload_mimes filter. |
845 // A correct MIME type will pass this test. Override $mimes or use the upload_mimes filter. |
801 $wp_filetype = wp_check_filetype_and_ext( $file['tmp_name'], $file['name'], $mimes ); |
847 $wp_filetype = wp_check_filetype_and_ext( $file['tmp_name'], $file['name'], $mimes ); |
802 $ext = empty( $wp_filetype['ext'] ) ? '' : $wp_filetype['ext']; |
848 $ext = empty( $wp_filetype['ext'] ) ? '' : $wp_filetype['ext']; |
803 $type = empty( $wp_filetype['type'] ) ? '' : $wp_filetype['type']; |
849 $type = empty( $wp_filetype['type'] ) ? '' : $wp_filetype['type']; |
804 $proper_filename = empty( $wp_filetype['proper_filename'] ) ? '' : $wp_filetype['proper_filename']; |
850 $proper_filename = empty( $wp_filetype['proper_filename'] ) ? '' : $wp_filetype['proper_filename']; |
805 |
851 |
806 // Check to see if wp_check_filetype_and_ext() determined the filename was incorrect |
852 // Check to see if wp_check_filetype_and_ext() determined the filename was incorrect. |
807 if ( $proper_filename ) { |
853 if ( $proper_filename ) { |
808 $file['name'] = $proper_filename; |
854 $file['name'] = $proper_filename; |
809 } |
855 } |
810 if ( ( ! $type || ! $ext ) && ! current_user_can( 'unfiltered_upload' ) ) { |
856 if ( ( ! $type || ! $ext ) && ! current_user_can( 'unfiltered_upload' ) ) { |
811 return call_user_func_array( $upload_error_handler, array( &$file, __( 'Sorry, this file type is not permitted for security reasons.' ) ) ); |
857 return call_user_func_array( $upload_error_handler, array( &$file, __( 'Sorry, this file type is not permitted for security reasons.' ) ) ); |
819 |
865 |
820 /* |
866 /* |
821 * A writable uploads dir will pass this test. Again, there's no point |
867 * A writable uploads dir will pass this test. Again, there's no point |
822 * overriding this one. |
868 * overriding this one. |
823 */ |
869 */ |
824 if ( ! ( ( $uploads = wp_upload_dir( $time ) ) && false === $uploads['error'] ) ) { |
870 $uploads = wp_upload_dir( $time ); |
871 if ( ! ( $uploads && false === $uploads['error'] ) ) { |
|
825 return call_user_func_array( $upload_error_handler, array( &$file, $uploads['error'] ) ); |
872 return call_user_func_array( $upload_error_handler, array( &$file, $uploads['error'] ) ); |
826 } |
873 } |
827 |
874 |
828 $filename = wp_unique_filename( $uploads['path'], $file['name'], $unique_filename_callback ); |
875 $filename = wp_unique_filename( $uploads['path'], $file['name'], $unique_filename_callback ); |
829 |
876 |
831 $new_file = $uploads['path'] . "/$filename"; |
878 $new_file = $uploads['path'] . "/$filename"; |
832 |
879 |
833 /** |
880 /** |
834 * Filters whether to short-circuit moving the uploaded file after passing all checks. |
881 * Filters whether to short-circuit moving the uploaded file after passing all checks. |
835 * |
882 * |
836 * If a non-null value is passed to the filter, moving the file and any related error |
883 * If a non-null value is returned from the filter, moving the file and any related |
837 * reporting will be completely skipped. |
884 * error reporting will be completely skipped. |
838 * |
885 * |
839 * @since 4.9.0 |
886 * @since 4.9.0 |
840 * |
887 * |
841 * @param string $move_new_file If null (default) move the file after the upload. |
888 * @param mixed $move_new_file If null (default) move the file after the upload. |
842 * @param string $file An array of data for a single file. |
889 * @param string[] $file An array of data for a single file. |
843 * @param string $new_file Filename of the newly-uploaded file. |
890 * @param string $new_file Filename of the newly-uploaded file. |
844 * @param string $type File type. |
891 * @param string $type Mime type of the newly-uploaded file. |
845 */ |
892 */ |
846 $move_new_file = apply_filters( 'pre_move_uploaded_file', null, $file, $new_file, $type ); |
893 $move_new_file = apply_filters( 'pre_move_uploaded_file', null, $file, $new_file, $type ); |
847 |
894 |
848 if ( null === $move_new_file ) { |
895 if ( null === $move_new_file ) { |
849 if ( 'wp_handle_upload' === $action ) { |
896 if ( 'wp_handle_upload' === $action ) { |
850 $move_new_file = @ move_uploaded_file( $file['tmp_name'], $new_file ); |
897 $move_new_file = @move_uploaded_file( $file['tmp_name'], $new_file ); |
851 } else { |
898 } else { |
852 // use copy and unlink because rename breaks streams. |
899 // Use copy and unlink because rename breaks streams. |
853 $move_new_file = @ copy( $file['tmp_name'], $new_file ); |
900 // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged |
901 $move_new_file = @copy( $file['tmp_name'], $new_file ); |
|
854 unlink( $file['tmp_name'] ); |
902 unlink( $file['tmp_name'] ); |
855 } |
903 } |
856 |
904 |
857 if ( false === $move_new_file ) { |
905 if ( false === $move_new_file ) { |
858 if ( 0 === strpos( $uploads['basedir'], ABSPATH ) ) { |
906 if ( 0 === strpos( $uploads['basedir'], ABSPATH ) ) { |
859 $error_path = str_replace( ABSPATH, '', $uploads['basedir'] ) . $uploads['subdir']; |
907 $error_path = str_replace( ABSPATH, '', $uploads['basedir'] ) . $uploads['subdir']; |
860 } else { |
908 } else { |
861 $error_path = basename( $uploads['basedir'] ) . $uploads['subdir']; |
909 $error_path = basename( $uploads['basedir'] ) . $uploads['subdir']; |
862 } |
910 } |
863 return $upload_error_handler( $file, sprintf( __( 'The uploaded file could not be moved to %s.' ), $error_path ) ); |
911 return $upload_error_handler( |
912 $file, |
|
913 sprintf( |
|
914 /* translators: %s: Destination file path. */ |
|
915 __( 'The uploaded file could not be moved to %s.' ), |
|
916 $error_path |
|
917 ) |
|
918 ); |
|
864 } |
919 } |
865 } |
920 } |
866 |
921 |
867 // Set correct file permissions. |
922 // Set correct file permissions. |
868 $stat = stat( dirname( $new_file ) ); |
923 $stat = stat( dirname( $new_file ) ); |
869 $perms = $stat['mode'] & 0000666; |
924 $perms = $stat['mode'] & 0000666; |
870 @ chmod( $new_file, $perms ); |
925 chmod( $new_file, $perms ); |
871 |
926 |
872 // Compute the URL. |
927 // Compute the URL. |
873 $url = $uploads['url'] . "/$filename"; |
928 $url = $uploads['url'] . "/$filename"; |
874 |
929 |
875 if ( is_multisite() ) { |
930 if ( is_multisite() ) { |
883 * |
938 * |
884 * @param array $upload { |
939 * @param array $upload { |
885 * Array of upload data. |
940 * Array of upload data. |
886 * |
941 * |
887 * @type string $file Filename of the newly-uploaded file. |
942 * @type string $file Filename of the newly-uploaded file. |
888 * @type string $url URL of the uploaded file. |
943 * @type string $url URL of the newly-uploaded file. |
889 * @type string $type File type. |
944 * @type string $type Mime type of the newly-uploaded file. |
890 * } |
945 * } |
891 * @param string $context The type of upload action. Values include 'upload' or 'sideload'. |
946 * @param string $context The type of upload action. Values include 'upload' or 'sideload'. |
892 */ |
947 */ |
893 return apply_filters( |
948 return apply_filters( |
894 'wp_handle_upload', |
949 'wp_handle_upload', |
908 * |
963 * |
909 * @since 2.0.0 |
964 * @since 2.0.0 |
910 * |
965 * |
911 * @see _wp_handle_upload() |
966 * @see _wp_handle_upload() |
912 * |
967 * |
913 * @param array $file Reference to a single element of `$_FILES`. Call the function once for |
968 * @param array $file Reference to a single element of `$_FILES`. |
914 * each uploaded file. |
969 * Call the function once for each uploaded file. |
915 * @param array|bool $overrides Optional. An associative array of names=>values to override default |
970 * @param array|bool $overrides Optional. An associative array of names => values |
916 * variables. Default false. |
971 * to override default variables. Default false. |
917 * @param string $time Optional. Time formatted in 'yyyy/mm'. Default null. |
972 * @param string $time Optional. Time formatted in 'yyyy/mm'. Default null. |
918 * @return array On success, returns an associative array of file attributes. On failure, returns |
973 * @return array On success, returns an associative array of file attributes. |
919 * $overrides['upload_error_handler'](&$file, $message ) or array( 'error'=>$message ). |
974 * On failure, returns `$overrides['upload_error_handler']( &$file, $message )` |
975 * or `array( 'error' => $message )`. |
|
920 */ |
976 */ |
921 function wp_handle_upload( &$file, $overrides = false, $time = null ) { |
977 function wp_handle_upload( &$file, $overrides = false, $time = null ) { |
922 /* |
978 /* |
923 * $_POST['action'] must be set and its value must equal $overrides['action'] |
979 * $_POST['action'] must be set and its value must equal $overrides['action'] |
924 * or this: |
980 * or this: |
938 * |
994 * |
939 * @since 2.6.0 |
995 * @since 2.6.0 |
940 * |
996 * |
941 * @see _wp_handle_upload() |
997 * @see _wp_handle_upload() |
942 * |
998 * |
943 * @param array $file An array similar to that of a PHP `$_FILES` POST array |
999 * @param array $file Reference to a single element of `$_FILES`. |
944 * @param array|bool $overrides Optional. An associative array of names=>values to override default |
1000 * Call the function once for each uploaded file. |
945 * variables. Default false. |
1001 * @param array|bool $overrides Optional. An associative array of names => values |
1002 * to override default variables. Default false. |
|
946 * @param string $time Optional. Time formatted in 'yyyy/mm'. Default null. |
1003 * @param string $time Optional. Time formatted in 'yyyy/mm'. Default null. |
947 * @return array On success, returns an associative array of file attributes. On failure, returns |
1004 * @return array On success, returns an associative array of file attributes. |
948 * $overrides['upload_error_handler'](&$file, $message ) or array( 'error'=>$message ). |
1005 * On failure, returns `$overrides['upload_error_handler']( &$file, $message )` |
1006 * or `array( 'error' => $message )`. |
|
949 */ |
1007 */ |
950 function wp_handle_sideload( &$file, $overrides = false, $time = null ) { |
1008 function wp_handle_sideload( &$file, $overrides = false, $time = null ) { |
951 /* |
1009 /* |
952 * $_POST['action'] must be set and its value must equal $overrides['action'] |
1010 * $_POST['action'] must be set and its value must equal $overrides['action'] |
953 * or this: |
1011 * or this: |
954 */ |
1012 */ |
955 $action = 'wp_handle_sideload'; |
1013 $action = 'wp_handle_sideload'; |
956 if ( isset( $overrides['action'] ) ) { |
1014 if ( isset( $overrides['action'] ) ) { |
957 $action = $overrides['action']; |
1015 $action = $overrides['action']; |
958 } |
1016 } |
1017 |
|
959 return _wp_handle_upload( $file, $overrides, $time, $action ); |
1018 return _wp_handle_upload( $file, $overrides, $time, $action ); |
960 } |
1019 } |
961 |
|
962 |
1020 |
963 /** |
1021 /** |
964 * Downloads a URL to a local temporary file using the WordPress HTTP API. |
1022 * Downloads a URL to a local temporary file using the WordPress HTTP API. |
965 * |
1023 * |
966 * Please note that the calling function must unlink() the file. |
1024 * Please note that the calling function must unlink() the file. |
967 * |
1025 * |
968 * @since 2.5.0 |
1026 * @since 2.5.0 |
969 * @since 5.2.0 Signature Verification with SoftFail was added. |
1027 * @since 5.2.0 Signature Verification with SoftFail was added. |
970 * |
1028 * |
971 * @param string $url The URL of the file to download. |
1029 * @param string $url The URL of the file to download. |
972 * @param int $timeout The timeout for the request to download the file. Default 300 seconds. |
1030 * @param int $timeout The timeout for the request to download the file. |
973 * @param bool $signature_verification Whether to perform Signature Verification. Default false. |
1031 * Default 300 seconds. |
1032 * @param bool $signature_verification Whether to perform Signature Verification. |
|
1033 * Default false. |
|
974 * @return string|WP_Error Filename on success, WP_Error on failure. |
1034 * @return string|WP_Error Filename on success, WP_Error on failure. |
975 */ |
1035 */ |
976 function download_url( $url, $timeout = 300, $signature_verification = false ) { |
1036 function download_url( $url, $timeout = 300, $signature_verification = false ) { |
977 //WARNING: The file is not automatically deleted, The script must unlink() the file. |
1037 // WARNING: The file is not automatically deleted, the script must unlink() the file. |
978 if ( ! $url ) { |
1038 if ( ! $url ) { |
979 return new WP_Error( 'http_no_url', __( 'Invalid URL Provided.' ) ); |
1039 return new WP_Error( 'http_no_url', __( 'Invalid URL Provided.' ) ); |
980 } |
1040 } |
981 |
1041 |
982 $url_filename = basename( parse_url( $url, PHP_URL_PATH ) ); |
1042 $url_filename = basename( parse_url( $url, PHP_URL_PATH ) ); |
1038 } |
1098 } |
1039 |
1099 |
1040 // If the caller expects signature verification to occur, check to see if this URL supports it. |
1100 // If the caller expects signature verification to occur, check to see if this URL supports it. |
1041 if ( $signature_verification ) { |
1101 if ( $signature_verification ) { |
1042 /** |
1102 /** |
1043 * Filters the list of hosts which should have Signature Verification attempteds on. |
1103 * Filters the list of hosts which should have Signature Verification attempted on. |
1044 * |
1104 * |
1045 * @since 5.2.0 |
1105 * @since 5.2.0 |
1046 * |
1106 * |
1047 * @param array List of hostnames. |
1107 * @param string[] $hostnames List of hostnames. |
1048 */ |
1108 */ |
1049 $signed_hostnames = apply_filters( 'wp_signature_hosts', array( 'wordpress.org', 'downloads.wordpress.org', 's.w.org' ) ); |
1109 $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 ); |
1110 $signature_verification = in_array( parse_url( $url, PHP_URL_HOST ), $signed_hostnames, true ); |
1051 } |
1111 } |
1052 |
1112 |
1053 // Perform signature valiation if supported. |
1113 // Perform signature valiation if supported. |
1054 if ( $signature_verification ) { |
1114 if ( $signature_verification ) { |
1055 $signature = wp_remote_retrieve_header( $response, 'x-content-signature' ); |
1115 $signature = wp_remote_retrieve_header( $response, 'x-content-signature' ); |
1056 if ( ! $signature ) { |
1116 if ( ! $signature ) { |
1057 // Retrieve signatures from a file if the header wasn't included. |
1117 // Retrieve signatures from a file if the header wasn't included. |
1058 // WordPress.org stores signatures at $package_url.sig |
1118 // WordPress.org stores signatures at $package_url.sig. |
1059 |
1119 |
1060 $signature_url = false; |
1120 $signature_url = false; |
1061 $url_path = parse_url( $url, PHP_URL_PATH ); |
1121 $url_path = parse_url( $url, PHP_URL_PATH ); |
1062 if ( substr( $url_path, -4 ) == '.zip' || substr( $url_path, -7 ) == '.tar.gz' ) { |
1122 |
1123 if ( '.zip' === substr( $url_path, -4 ) || '.tar.gz' === substr( $url_path, -7 ) ) { |
|
1063 $signature_url = str_replace( $url_path, $url_path . '.sig', $url ); |
1124 $signature_url = str_replace( $url_path, $url_path . '.sig', $url ); |
1064 } |
1125 } |
1065 |
1126 |
1066 /** |
1127 /** |
1067 * Filter the URL where the signature for a file is located. |
1128 * Filter the URL where the signature for a file is located. |
1075 |
1136 |
1076 if ( $signature_url ) { |
1137 if ( $signature_url ) { |
1077 $signature_request = wp_safe_remote_get( |
1138 $signature_request = wp_safe_remote_get( |
1078 $signature_url, |
1139 $signature_url, |
1079 array( |
1140 array( |
1080 'limit_response_size' => 10 * 1024, // 10KB should be large enough for quite a few signatures. |
1141 'limit_response_size' => 10 * KB_IN_BYTES, // 10KB should be large enough for quite a few signatures. |
1081 ) |
1142 ) |
1082 ); |
1143 ); |
1083 |
1144 |
1084 if ( ! is_wp_error( $signature_request ) && 200 === wp_remote_retrieve_response_code( $signature_request ) ) { |
1145 if ( ! is_wp_error( $signature_request ) && 200 === wp_remote_retrieve_response_code( $signature_request ) ) { |
1085 $signature = explode( "\n", wp_remote_retrieve_body( $signature_request ) ); |
1146 $signature = explode( "\n", wp_remote_retrieve_body( $signature_request ) ); |
1132 if ( 32 == strlen( $expected_md5 ) ) { |
1193 if ( 32 == strlen( $expected_md5 ) ) { |
1133 $expected_raw_md5 = pack( 'H*', $expected_md5 ); |
1194 $expected_raw_md5 = pack( 'H*', $expected_md5 ); |
1134 } elseif ( 24 == strlen( $expected_md5 ) ) { |
1195 } elseif ( 24 == strlen( $expected_md5 ) ) { |
1135 $expected_raw_md5 = base64_decode( $expected_md5 ); |
1196 $expected_raw_md5 = base64_decode( $expected_md5 ); |
1136 } else { |
1197 } else { |
1137 return false; // unknown format |
1198 return false; // Unknown format. |
1138 } |
1199 } |
1139 |
1200 |
1140 $file_md5 = md5_file( $filename, true ); |
1201 $file_md5 = md5_file( $filename, true ); |
1141 |
1202 |
1142 if ( $file_md5 === $expected_raw_md5 ) { |
1203 if ( $file_md5 === $expected_raw_md5 ) { |
1143 return true; |
1204 return true; |
1144 } |
1205 } |
1145 |
1206 |
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 ) ) ); |
1207 return new WP_Error( |
1208 'md5_mismatch', |
|
1209 sprintf( |
|
1210 /* translators: 1: File checksum, 2: Expected checksum value. */ |
|
1211 __( 'The checksum of the file (%1$s) does not match the expected checksum value (%2$s).' ), |
|
1212 bin2hex( $file_md5 ), |
|
1213 bin2hex( $expected_raw_md5 ) |
|
1214 ) |
|
1215 ); |
|
1147 } |
1216 } |
1148 |
1217 |
1149 /** |
1218 /** |
1150 * Verifies the contents of a file against its ED25519 signature. |
1219 * Verifies the contents of a file against its ED25519 signature. |
1151 * |
1220 * |
1152 * @since 5.2.0 |
1221 * @since 5.2.0 |
1153 * |
1222 * |
1154 * @param string $filename The file to validate. |
1223 * @param string $filename The file to validate. |
1155 * @param string|array $signatures A Signature provided for the file. |
1224 * @param string|array $signatures A Signature provided for the file. |
1156 * @param string $filename_for_errors A friendly filename for errors. Optional. |
1225 * @param string $filename_for_errors A friendly filename for errors. Optional. |
1157 * |
1226 * @return bool|WP_Error True on success, false if verification not attempted, |
1158 * @return bool|WP_Error true on success, false if verificaiton not attempted, or WP_Error describing an error condition. |
1227 * or WP_Error describing an error condition. |
1159 */ |
1228 */ |
1160 function verify_file_signature( $filename, $signatures, $filename_for_errors = false ) { |
1229 function verify_file_signature( $filename, $signatures, $filename_for_errors = false ) { |
1161 if ( ! $filename_for_errors ) { |
1230 if ( ! $filename_for_errors ) { |
1162 $filename_for_errors = wp_basename( $filename ); |
1231 $filename_for_errors = wp_basename( $filename ); |
1163 } |
1232 } |
1164 |
1233 |
1165 // Check we can process signatures. |
1234 // Check we can process signatures. |
1166 if ( ! function_exists( 'sodium_crypto_sign_verify_detached' ) || ! in_array( 'sha384', array_map( 'strtolower', hash_algos() ) ) ) { |
1235 if ( ! function_exists( 'sodium_crypto_sign_verify_detached' ) || ! in_array( 'sha384', array_map( 'strtolower', hash_algos() ), true ) ) { |
1167 return new WP_Error( |
1236 return new WP_Error( |
1168 'signature_verification_unsupported', |
1237 'signature_verification_unsupported', |
1169 sprintf( |
1238 sprintf( |
1170 /* translators: %s: The filename of the package. */ |
1239 /* translators: %s: The filename of the package. */ |
1171 __( 'The authenticity of %s could not be verified as signature verification is unavailable on this system.' ), |
1240 __( 'The authenticity of %s could not be verified as signature verification is unavailable on this system.' ), |
1173 ), |
1242 ), |
1174 ( ! function_exists( 'sodium_crypto_sign_verify_detached' ) ? 'sodium_crypto_sign_verify_detached' : 'sha384' ) |
1243 ( ! function_exists( 'sodium_crypto_sign_verify_detached' ) ? 'sodium_crypto_sign_verify_detached' : 'sha384' ) |
1175 ); |
1244 ); |
1176 } |
1245 } |
1177 |
1246 |
1178 // Check for a edge-case affecting PHP Maths abilities |
1247 // Check for a edge-case affecting PHP Maths abilities. |
1179 if ( |
1248 if ( |
1180 ! extension_loaded( 'sodium' ) && |
1249 ! extension_loaded( 'sodium' ) && |
1181 in_array( PHP_VERSION_ID, [ 70200, 70201, 70202 ], true ) && |
1250 in_array( PHP_VERSION_ID, array( 70200, 70201, 70202 ), true ) && |
1182 extension_loaded( 'opcache' ) |
1251 extension_loaded( 'opcache' ) |
1183 ) { |
1252 ) { |
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. |
1253 // 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 |
1254 // https://bugs.php.net/bug.php?id=75938 |
1186 |
1255 |
1191 __( 'The authenticity of %s could not be verified as signature verification is unavailable on this system.' ), |
1260 __( '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>' |
1261 '<span class="code">' . esc_html( $filename_for_errors ) . '</span>' |
1193 ), |
1262 ), |
1194 array( |
1263 array( |
1195 'php' => phpversion(), |
1264 'php' => phpversion(), |
1265 // phpcs:ignore PHPCompatibility.Constants.NewConstants.sodium_library_versionFound |
|
1196 'sodium' => defined( 'SODIUM_LIBRARY_VERSION' ) ? SODIUM_LIBRARY_VERSION : ( defined( 'ParagonIE_Sodium_Compat::VERSION_STRING' ) ? ParagonIE_Sodium_Compat::VERSION_STRING : false ), |
1266 'sodium' => defined( 'SODIUM_LIBRARY_VERSION' ) ? SODIUM_LIBRARY_VERSION : ( defined( 'ParagonIE_Sodium_Compat::VERSION_STRING' ) ? ParagonIE_Sodium_Compat::VERSION_STRING : false ), |
1197 ) |
1267 ) |
1198 ); |
1268 ); |
1199 |
1269 |
1200 } |
1270 } |
1204 $sodium_compat_is_fast = false; |
1274 $sodium_compat_is_fast = false; |
1205 |
1275 |
1206 // Allow for an old version of Sodium_Compat being loaded before the bundled WordPress one. |
1276 // 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' ) ) { |
1277 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. |
1278 // Run `ParagonIE_Sodium_Compat::runtime_speed_test()` in optimized integer mode, as that's what WordPress utilises during signing verifications. |
1279 // phpcs:disable WordPress.NamingConventions.ValidVariableName |
|
1209 $old_fastMult = ParagonIE_Sodium_Compat::$fastMult; |
1280 $old_fastMult = ParagonIE_Sodium_Compat::$fastMult; |
1210 ParagonIE_Sodium_Compat::$fastMult = true; |
1281 ParagonIE_Sodium_Compat::$fastMult = true; |
1211 $sodium_compat_is_fast = ParagonIE_Sodium_Compat::runtime_speed_test( 100, 10 ); |
1282 $sodium_compat_is_fast = ParagonIE_Sodium_Compat::runtime_speed_test( 100, 10 ); |
1212 ParagonIE_Sodium_Compat::$fastMult = $old_fastMult; |
1283 ParagonIE_Sodium_Compat::$fastMult = $old_fastMult; |
1213 } |
1284 // phpcs:enable |
1214 |
1285 } |
1215 // This cannot be performed in a reasonable amount of time |
1286 |
1287 // 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 |
1288 // https://github.com/paragonie/sodium_compat#help-sodium_compat-is-slow-how-can-i-make-it-fast |
1217 if ( ! $sodium_compat_is_fast ) { |
1289 if ( ! $sodium_compat_is_fast ) { |
1218 return new WP_Error( |
1290 return new WP_Error( |
1219 'signature_verification_unsupported', |
1291 'signature_verification_unsupported', |
1220 sprintf( |
1292 sprintf( |
1222 __( 'The authenticity of %s could not be verified as signature verification is unavailable on this system.' ), |
1294 __( '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>' |
1295 '<span class="code">' . esc_html( $filename_for_errors ) . '</span>' |
1224 ), |
1296 ), |
1225 array( |
1297 array( |
1226 'php' => phpversion(), |
1298 'php' => phpversion(), |
1299 // phpcs:ignore PHPCompatibility.Constants.NewConstants.sodium_library_versionFound |
|
1227 'sodium' => defined( 'SODIUM_LIBRARY_VERSION' ) ? SODIUM_LIBRARY_VERSION : ( defined( 'ParagonIE_Sodium_Compat::VERSION_STRING' ) ? ParagonIE_Sodium_Compat::VERSION_STRING : false ), |
1300 '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, |
1301 'polyfill_is_fast' => false, |
1229 'max_execution_time' => ini_get( 'max_execution_time' ), |
1302 'max_execution_time' => ini_get( 'max_execution_time' ), |
1230 ) |
1303 ) |
1231 ); |
1304 ); |
1249 $trusted_keys = wp_trusted_keys(); |
1322 $trusted_keys = wp_trusted_keys(); |
1250 $file_hash = hash_file( 'sha384', $filename, true ); |
1323 $file_hash = hash_file( 'sha384', $filename, true ); |
1251 |
1324 |
1252 mbstring_binary_safe_encoding(); |
1325 mbstring_binary_safe_encoding(); |
1253 |
1326 |
1254 $skipped_key = $skipped_signature = 0; |
1327 $skipped_key = 0; |
1328 $skipped_signature = 0; |
|
1255 |
1329 |
1256 foreach ( (array) $signatures as $signature ) { |
1330 foreach ( (array) $signatures as $signature ) { |
1257 $signature_raw = base64_decode( $signature ); |
1331 $signature_raw = base64_decode( $signature ); |
1258 |
1332 |
1259 // Ensure only valid-length signatures are considered. |
1333 // Ensure only valid-length signatures are considered. |
1294 'signatures' => $signatures, |
1368 'signatures' => $signatures, |
1295 'hash' => bin2hex( $file_hash ), |
1369 'hash' => bin2hex( $file_hash ), |
1296 'skipped_key' => $skipped_key, |
1370 'skipped_key' => $skipped_key, |
1297 'skipped_sig' => $skipped_signature, |
1371 'skipped_sig' => $skipped_signature, |
1298 'php' => phpversion(), |
1372 'php' => phpversion(), |
1373 // phpcs:ignore PHPCompatibility.Constants.NewConstants.sodium_library_versionFound |
|
1299 'sodium' => defined( 'SODIUM_LIBRARY_VERSION' ) ? SODIUM_LIBRARY_VERSION : ( defined( 'ParagonIE_Sodium_Compat::VERSION_STRING' ) ? ParagonIE_Sodium_Compat::VERSION_STRING : false ), |
1374 'sodium' => defined( 'SODIUM_LIBRARY_VERSION' ) ? SODIUM_LIBRARY_VERSION : ( defined( 'ParagonIE_Sodium_Compat::VERSION_STRING' ) ? ParagonIE_Sodium_Compat::VERSION_STRING : false ), |
1300 ) |
1375 ) |
1301 ); |
1376 ); |
1302 } |
1377 } |
1303 |
1378 |
1304 /** |
1379 /** |
1305 * Retrieve the list of signing keys trusted by WordPress. |
1380 * Retrieves the list of signing keys trusted by WordPress. |
1306 * |
1381 * |
1307 * @since 5.2.0 |
1382 * @since 5.2.0 |
1308 * |
1383 * |
1309 * @return array List of base64-encoded Signing keys. |
1384 * @return string[] Array of base64-encoded signing keys. |
1310 */ |
1385 */ |
1311 function wp_trusted_keys() { |
1386 function wp_trusted_keys() { |
1312 $trusted_keys = array(); |
1387 $trusted_keys = array(); |
1313 |
1388 |
1314 if ( time() < 1617235200 ) { |
1389 if ( time() < 1617235200 ) { |
1317 } |
1392 } |
1318 |
1393 |
1319 // TODO: Add key #2 with longer expiration. |
1394 // TODO: Add key #2 with longer expiration. |
1320 |
1395 |
1321 /** |
1396 /** |
1322 * Filter the valid Signing keys used to verify the contents of files. |
1397 * Filter the valid signing keys used to verify the contents of files. |
1323 * |
1398 * |
1324 * @since 5.2.0 |
1399 * @since 5.2.0 |
1325 * |
1400 * |
1326 * @param array $trusted_keys The trusted keys that may sign packages. |
1401 * @param string[] $trusted_keys The trusted keys that may sign packages. |
1327 */ |
1402 */ |
1328 return apply_filters( 'wp_trusted_keys', $trusted_keys ); |
1403 return apply_filters( 'wp_trusted_keys', $trusted_keys ); |
1329 } |
1404 } |
1330 |
1405 |
1331 /** |
1406 /** |
1390 if ( class_exists( 'ZipArchive', false ) && apply_filters( 'unzip_file_use_ziparchive', true ) ) { |
1465 if ( class_exists( 'ZipArchive', false ) && apply_filters( 'unzip_file_use_ziparchive', true ) ) { |
1391 $result = _unzip_file_ziparchive( $file, $to, $needed_dirs ); |
1466 $result = _unzip_file_ziparchive( $file, $to, $needed_dirs ); |
1392 if ( true === $result ) { |
1467 if ( true === $result ) { |
1393 return $result; |
1468 return $result; |
1394 } elseif ( is_wp_error( $result ) ) { |
1469 } elseif ( is_wp_error( $result ) ) { |
1395 if ( 'incompatible_archive' != $result->get_error_code() ) { |
1470 if ( 'incompatible_archive' !== $result->get_error_code() ) { |
1396 return $result; |
1471 return $result; |
1397 } |
1472 } |
1398 } |
1473 } |
1399 } |
1474 } |
1400 // Fall through to PclZip if ZipArchive is not available, or encountered an error opening the file. |
1475 // Fall through to PclZip if ZipArchive is not available, or encountered an error opening the file. |
1407 * This function should not be called directly, use `unzip_file()` instead. |
1482 * This function should not be called directly, use `unzip_file()` instead. |
1408 * |
1483 * |
1409 * Assumes that WP_Filesystem() has already been called and set up. |
1484 * Assumes that WP_Filesystem() has already been called and set up. |
1410 * |
1485 * |
1411 * @since 3.0.0 |
1486 * @since 3.0.0 |
1487 * @access private |
|
1488 * |
|
1412 * @see unzip_file() |
1489 * @see unzip_file() |
1413 * @access private |
|
1414 * |
1490 * |
1415 * @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass. |
1491 * @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass. |
1416 * |
1492 * |
1417 * @param string $file Full path and filename of ZIP archive. |
1493 * @param string $file Full path and filename of ZIP archive. |
1418 * @param string $to Full path on the filesystem to extract archive to. |
1494 * @param string $to Full path on the filesystem to extract archive to. |
1419 * @param array $needed_dirs A partial list of required folders needed to be created. |
1495 * @param string[] $needed_dirs A partial list of required folders needed to be created. |
1420 * @return true|WP_Error True on success, WP_Error on failure. |
1496 * @return true|WP_Error True on success, WP_Error on failure. |
1421 */ |
1497 */ |
1422 function _unzip_file_ziparchive( $file, $to, $needed_dirs = array() ) { |
1498 function _unzip_file_ziparchive( $file, $to, $needed_dirs = array() ) { |
1423 global $wp_filesystem; |
1499 global $wp_filesystem; |
1424 |
1500 |
1430 } |
1506 } |
1431 |
1507 |
1432 $uncompressed_size = 0; |
1508 $uncompressed_size = 0; |
1433 |
1509 |
1434 for ( $i = 0; $i < $z->numFiles; $i++ ) { |
1510 for ( $i = 0; $i < $z->numFiles; $i++ ) { |
1435 if ( ! $info = $z->statIndex( $i ) ) { |
1511 $info = $z->statIndex( $i ); |
1512 if ( ! $info ) { |
|
1436 return new WP_Error( 'stat_failed_ziparchive', __( 'Could not retrieve file from archive.' ) ); |
1513 return new WP_Error( 'stat_failed_ziparchive', __( 'Could not retrieve file from archive.' ) ); |
1437 } |
1514 } |
1438 |
1515 |
1439 if ( '__MACOSX/' === substr( $info['name'], 0, 9 ) ) { // Skip the OS X-created __MACOSX directory |
1516 if ( '__MACOSX/' === substr( $info['name'], 0, 9 ) ) { // Skip the OS X-created __MACOSX directory. |
1440 continue; |
1517 continue; |
1441 } |
1518 } |
1442 |
1519 |
1443 // Don't extract invalid files: |
1520 // Don't extract invalid files: |
1444 if ( 0 !== validate_file( $info['name'] ) ) { |
1521 if ( 0 !== validate_file( $info['name'] ) ) { |
1445 continue; |
1522 continue; |
1446 } |
1523 } |
1447 |
1524 |
1448 $uncompressed_size += $info['size']; |
1525 $uncompressed_size += $info['size']; |
1449 |
1526 |
1527 $dirname = dirname( $info['name'] ); |
|
1528 |
|
1450 if ( '/' === substr( $info['name'], -1 ) ) { |
1529 if ( '/' === substr( $info['name'], -1 ) ) { |
1451 // Directory. |
1530 // Directory. |
1452 $needed_dirs[] = $to . untrailingslashit( $info['name'] ); |
1531 $needed_dirs[] = $to . untrailingslashit( $info['name'] ); |
1453 } elseif ( '.' !== $dirname = dirname( $info['name'] ) ) { |
1532 } elseif ( '.' !== $dirname ) { |
1454 // Path to a file. |
1533 // Path to a file. |
1455 $needed_dirs[] = $to . untrailingslashit( $dirname ); |
1534 $needed_dirs[] = $to . untrailingslashit( $dirname ); |
1456 } |
1535 } |
1457 } |
1536 } |
1458 |
1537 |
1469 } |
1548 } |
1470 |
1549 |
1471 $needed_dirs = array_unique( $needed_dirs ); |
1550 $needed_dirs = array_unique( $needed_dirs ); |
1472 foreach ( $needed_dirs as $dir ) { |
1551 foreach ( $needed_dirs as $dir ) { |
1473 // Check the parent folders of the folders all exist within the creation array. |
1552 // Check the parent folders of the folders all exist within the creation array. |
1474 if ( untrailingslashit( $to ) == $dir ) { // Skip over the working directory, We know this exists (or will exist) |
1553 if ( untrailingslashit( $to ) == $dir ) { // Skip over the working directory, we know this exists (or will exist). |
1475 continue; |
1554 continue; |
1476 } |
1555 } |
1477 if ( strpos( $dir, $to ) === false ) { // If the directory is not within the working directory, Skip it |
1556 if ( strpos( $dir, $to ) === false ) { // If the directory is not within the working directory, skip it. |
1478 continue; |
1557 continue; |
1479 } |
1558 } |
1480 |
1559 |
1481 $parent_folder = dirname( $dir ); |
1560 $parent_folder = dirname( $dir ); |
1482 while ( ! empty( $parent_folder ) && untrailingslashit( $to ) != $parent_folder && ! in_array( $parent_folder, $needed_dirs ) ) { |
1561 while ( ! empty( $parent_folder ) && untrailingslashit( $to ) != $parent_folder && ! in_array( $parent_folder, $needed_dirs, true ) ) { |
1483 $needed_dirs[] = $parent_folder; |
1562 $needed_dirs[] = $parent_folder; |
1484 $parent_folder = dirname( $parent_folder ); |
1563 $parent_folder = dirname( $parent_folder ); |
1485 } |
1564 } |
1486 } |
1565 } |
1487 asort( $needed_dirs ); |
1566 asort( $needed_dirs ); |
1494 } |
1573 } |
1495 } |
1574 } |
1496 unset( $needed_dirs ); |
1575 unset( $needed_dirs ); |
1497 |
1576 |
1498 for ( $i = 0; $i < $z->numFiles; $i++ ) { |
1577 for ( $i = 0; $i < $z->numFiles; $i++ ) { |
1499 if ( ! $info = $z->statIndex( $i ) ) { |
1578 $info = $z->statIndex( $i ); |
1579 if ( ! $info ) { |
|
1500 return new WP_Error( 'stat_failed_ziparchive', __( 'Could not retrieve file from archive.' ) ); |
1580 return new WP_Error( 'stat_failed_ziparchive', __( 'Could not retrieve file from archive.' ) ); |
1501 } |
1581 } |
1502 |
1582 |
1503 if ( '/' == substr( $info['name'], -1 ) ) { // directory |
1583 if ( '/' === substr( $info['name'], -1 ) ) { // Directory. |
1504 continue; |
1584 continue; |
1505 } |
1585 } |
1506 |
1586 |
1507 if ( '__MACOSX/' === substr( $info['name'], 0, 9 ) ) { // Don't extract the OS X-created __MACOSX directory files |
1587 if ( '__MACOSX/' === substr( $info['name'], 0, 9 ) ) { // Don't extract the OS X-created __MACOSX directory files. |
1508 continue; |
1588 continue; |
1509 } |
1589 } |
1510 |
1590 |
1511 // Don't extract invalid files: |
1591 // Don't extract invalid files: |
1512 if ( 0 !== validate_file( $info['name'] ) ) { |
1592 if ( 0 !== validate_file( $info['name'] ) ) { |
1534 * This function should not be called directly, use `unzip_file()` instead. |
1614 * This function should not be called directly, use `unzip_file()` instead. |
1535 * |
1615 * |
1536 * Assumes that WP_Filesystem() has already been called and set up. |
1616 * Assumes that WP_Filesystem() has already been called and set up. |
1537 * |
1617 * |
1538 * @since 3.0.0 |
1618 * @since 3.0.0 |
1619 * @access private |
|
1620 * |
|
1539 * @see unzip_file() |
1621 * @see unzip_file() |
1540 * @access private |
|
1541 * |
1622 * |
1542 * @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass. |
1623 * @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass. |
1543 * |
1624 * |
1544 * @param string $file Full path and filename of ZIP archive. |
1625 * @param string $file Full path and filename of ZIP archive. |
1545 * @param string $to Full path on the filesystem to extract archive to. |
1626 * @param string $to Full path on the filesystem to extract archive to. |
1546 * @param array $needed_dirs A partial list of required folders needed to be created. |
1627 * @param string[] $needed_dirs A partial list of required folders needed to be created. |
1547 * @return true|WP_Error True on success, WP_Error on failure. |
1628 * @return true|WP_Error True on success, WP_Error on failure. |
1548 */ |
1629 */ |
1549 function _unzip_file_pclzip( $file, $to, $needed_dirs = array() ) { |
1630 function _unzip_file_pclzip( $file, $to, $needed_dirs = array() ) { |
1550 global $wp_filesystem; |
1631 global $wp_filesystem; |
1551 |
1632 |
1552 mbstring_binary_safe_encoding(); |
1633 mbstring_binary_safe_encoding(); |
1553 |
1634 |
1554 require_once( ABSPATH . 'wp-admin/includes/class-pclzip.php' ); |
1635 require_once ABSPATH . 'wp-admin/includes/class-pclzip.php'; |
1555 |
1636 |
1556 $archive = new PclZip( $file ); |
1637 $archive = new PclZip( $file ); |
1557 |
1638 |
1558 $archive_files = $archive->extract( PCLZIP_OPT_EXTRACT_AS_STRING ); |
1639 $archive_files = $archive->extract( PCLZIP_OPT_EXTRACT_AS_STRING ); |
1559 |
1640 |
1562 // Is the archive valid? |
1643 // Is the archive valid? |
1563 if ( ! is_array( $archive_files ) ) { |
1644 if ( ! is_array( $archive_files ) ) { |
1564 return new WP_Error( 'incompatible_archive', __( 'Incompatible Archive.' ), $archive->errorInfo( true ) ); |
1645 return new WP_Error( 'incompatible_archive', __( 'Incompatible Archive.' ), $archive->errorInfo( true ) ); |
1565 } |
1646 } |
1566 |
1647 |
1567 if ( 0 == count( $archive_files ) ) { |
1648 if ( 0 === count( $archive_files ) ) { |
1568 return new WP_Error( 'empty_archive_pclzip', __( 'Empty archive.' ) ); |
1649 return new WP_Error( 'empty_archive_pclzip', __( 'Empty archive.' ) ); |
1569 } |
1650 } |
1570 |
1651 |
1571 $uncompressed_size = 0; |
1652 $uncompressed_size = 0; |
1572 |
1653 |
1573 // Determine any children directories needed (From within the archive) |
1654 // Determine any children directories needed (From within the archive). |
1574 foreach ( $archive_files as $file ) { |
1655 foreach ( $archive_files as $file ) { |
1575 if ( '__MACOSX/' === substr( $file['filename'], 0, 9 ) ) { // Skip the OS X-created __MACOSX directory |
1656 if ( '__MACOSX/' === substr( $file['filename'], 0, 9 ) ) { // Skip the OS X-created __MACOSX directory. |
1576 continue; |
1657 continue; |
1577 } |
1658 } |
1578 |
1659 |
1579 $uncompressed_size += $file['size']; |
1660 $uncompressed_size += $file['size']; |
1580 |
1661 |
1594 } |
1675 } |
1595 |
1676 |
1596 $needed_dirs = array_unique( $needed_dirs ); |
1677 $needed_dirs = array_unique( $needed_dirs ); |
1597 foreach ( $needed_dirs as $dir ) { |
1678 foreach ( $needed_dirs as $dir ) { |
1598 // Check the parent folders of the folders all exist within the creation array. |
1679 // Check the parent folders of the folders all exist within the creation array. |
1599 if ( untrailingslashit( $to ) == $dir ) { // Skip over the working directory, We know this exists (or will exist) |
1680 if ( untrailingslashit( $to ) == $dir ) { // Skip over the working directory, we know this exists (or will exist). |
1600 continue; |
1681 continue; |
1601 } |
1682 } |
1602 if ( strpos( $dir, $to ) === false ) { // If the directory is not within the working directory, Skip it |
1683 if ( strpos( $dir, $to ) === false ) { // If the directory is not within the working directory, skip it. |
1603 continue; |
1684 continue; |
1604 } |
1685 } |
1605 |
1686 |
1606 $parent_folder = dirname( $dir ); |
1687 $parent_folder = dirname( $dir ); |
1607 while ( ! empty( $parent_folder ) && untrailingslashit( $to ) != $parent_folder && ! in_array( $parent_folder, $needed_dirs ) ) { |
1688 while ( ! empty( $parent_folder ) && untrailingslashit( $to ) != $parent_folder && ! in_array( $parent_folder, $needed_dirs, true ) ) { |
1608 $needed_dirs[] = $parent_folder; |
1689 $needed_dirs[] = $parent_folder; |
1609 $parent_folder = dirname( $parent_folder ); |
1690 $parent_folder = dirname( $parent_folder ); |
1610 } |
1691 } |
1611 } |
1692 } |
1612 asort( $needed_dirs ); |
1693 asort( $needed_dirs ); |
1618 return new WP_Error( 'mkdir_failed_pclzip', __( 'Could not create directory.' ), substr( $_dir, strlen( $to ) ) ); |
1699 return new WP_Error( 'mkdir_failed_pclzip', __( 'Could not create directory.' ), substr( $_dir, strlen( $to ) ) ); |
1619 } |
1700 } |
1620 } |
1701 } |
1621 unset( $needed_dirs ); |
1702 unset( $needed_dirs ); |
1622 |
1703 |
1623 // Extract the files from the zip |
1704 // Extract the files from the zip. |
1624 foreach ( $archive_files as $file ) { |
1705 foreach ( $archive_files as $file ) { |
1625 if ( $file['folder'] ) { |
1706 if ( $file['folder'] ) { |
1626 continue; |
1707 continue; |
1627 } |
1708 } |
1628 |
1709 |
1629 if ( '__MACOSX/' === substr( $file['filename'], 0, 9 ) ) { // Don't extract the OS X-created __MACOSX directory files |
1710 if ( '__MACOSX/' === substr( $file['filename'], 0, 9 ) ) { // Don't extract the OS X-created __MACOSX directory files. |
1630 continue; |
1711 continue; |
1631 } |
1712 } |
1632 |
1713 |
1633 // Don't extract invalid files: |
1714 // Don't extract invalid files: |
1634 if ( 0 !== validate_file( $file['filename'] ) ) { |
1715 if ( 0 !== validate_file( $file['filename'] ) ) { |
1650 * |
1731 * |
1651 * @since 2.5.0 |
1732 * @since 2.5.0 |
1652 * |
1733 * |
1653 * @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass. |
1734 * @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass. |
1654 * |
1735 * |
1655 * @param string $from Source directory. |
1736 * @param string $from Source directory. |
1656 * @param string $to Destination directory. |
1737 * @param string $to Destination directory. |
1657 * @param array $skip_list A list of files/folders to skip copying. |
1738 * @param string[] $skip_list An array of files/folders to skip copying. |
1658 * @return true|WP_Error True on success, WP_Error on failure. |
1739 * @return true|WP_Error True on success, WP_Error on failure. |
1659 */ |
1740 */ |
1660 function copy_dir( $from, $to, $skip_list = array() ) { |
1741 function copy_dir( $from, $to, $skip_list = array() ) { |
1661 global $wp_filesystem; |
1742 global $wp_filesystem; |
1662 |
1743 |
1664 |
1745 |
1665 $from = trailingslashit( $from ); |
1746 $from = trailingslashit( $from ); |
1666 $to = trailingslashit( $to ); |
1747 $to = trailingslashit( $to ); |
1667 |
1748 |
1668 foreach ( (array) $dirlist as $filename => $fileinfo ) { |
1749 foreach ( (array) $dirlist as $filename => $fileinfo ) { |
1669 if ( in_array( $filename, $skip_list ) ) { |
1750 if ( in_array( $filename, $skip_list, true ) ) { |
1670 continue; |
1751 continue; |
1671 } |
1752 } |
1672 |
1753 |
1673 if ( 'f' == $fileinfo['type'] ) { |
1754 if ( 'f' === $fileinfo['type'] ) { |
1674 if ( ! $wp_filesystem->copy( $from . $filename, $to . $filename, true, FS_CHMOD_FILE ) ) { |
1755 if ( ! $wp_filesystem->copy( $from . $filename, $to . $filename, true, FS_CHMOD_FILE ) ) { |
1675 // If copy failed, chmod file to 0644 and try again. |
1756 // If copy failed, chmod file to 0644 and try again. |
1676 $wp_filesystem->chmod( $to . $filename, FS_CHMOD_FILE ); |
1757 $wp_filesystem->chmod( $to . $filename, FS_CHMOD_FILE ); |
1677 if ( ! $wp_filesystem->copy( $from . $filename, $to . $filename, true, FS_CHMOD_FILE ) ) { |
1758 if ( ! $wp_filesystem->copy( $from . $filename, $to . $filename, true, FS_CHMOD_FILE ) ) { |
1678 return new WP_Error( 'copy_failed_copy_dir', __( 'Could not copy file.' ), $to . $filename ); |
1759 return new WP_Error( 'copy_failed_copy_dir', __( 'Could not copy file.' ), $to . $filename ); |
1679 } |
1760 } |
1680 } |
1761 } |
1681 } elseif ( 'd' == $fileinfo['type'] ) { |
1762 |
1763 wp_opcache_invalidate( $to . $filename ); |
|
1764 } elseif ( 'd' === $fileinfo['type'] ) { |
|
1682 if ( ! $wp_filesystem->is_dir( $to . $filename ) ) { |
1765 if ( ! $wp_filesystem->is_dir( $to . $filename ) ) { |
1683 if ( ! $wp_filesystem->mkdir( $to . $filename, FS_CHMOD_DIR ) ) { |
1766 if ( ! $wp_filesystem->mkdir( $to . $filename, FS_CHMOD_DIR ) ) { |
1684 return new WP_Error( 'mkdir_failed_copy_dir', __( 'Could not create directory.' ), $to . $filename ); |
1767 return new WP_Error( 'mkdir_failed_copy_dir', __( 'Could not create directory.' ), $to . $filename ); |
1685 } |
1768 } |
1686 } |
1769 } |
1687 |
1770 |
1688 // generate the $sub_skip_list for the subdirectory as a sub-set of the existing $skip_list |
1771 // Generate the $sub_skip_list for the subdirectory as a sub-set of the existing $skip_list. |
1689 $sub_skip_list = array(); |
1772 $sub_skip_list = array(); |
1690 foreach ( $skip_list as $skip_item ) { |
1773 foreach ( $skip_list as $skip_item ) { |
1691 if ( 0 === strpos( $skip_item, $filename . '/' ) ) { |
1774 if ( 0 === strpos( $skip_item, $filename . '/' ) ) { |
1692 $sub_skip_list[] = preg_replace( '!^' . preg_quote( $filename, '!' ) . '/!i', '', $skip_item ); |
1775 $sub_skip_list[] = preg_replace( '!^' . preg_quote( $filename, '!' ) . '/!i', '', $skip_item ); |
1693 } |
1776 } |
1697 if ( is_wp_error( $result ) ) { |
1780 if ( is_wp_error( $result ) ) { |
1698 return $result; |
1781 return $result; |
1699 } |
1782 } |
1700 } |
1783 } |
1701 } |
1784 } |
1785 |
|
1702 return true; |
1786 return true; |
1703 } |
1787 } |
1704 |
1788 |
1705 /** |
1789 /** |
1706 * Initialises and connects the WordPress Filesystem Abstraction classes. |
1790 * Initializes and connects the WordPress Filesystem Abstraction classes. |
1707 * |
1791 * |
1708 * This function will include the chosen transport and attempt connecting. |
1792 * This function will include the chosen transport and attempt connecting. |
1709 * |
1793 * |
1710 * Plugins may add extra transports, And force WordPress to use them by returning |
1794 * Plugins may add extra transports, And force WordPress to use them by returning |
1711 * the filename via the {@see 'filesystem_method_file'} filter. |
1795 * the filename via the {@see 'filesystem_method_file'} filter. |
1712 * |
1796 * |
1713 * @since 2.5.0 |
1797 * @since 2.5.0 |
1714 * |
1798 * |
1715 * @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass. |
1799 * @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass. |
1716 * |
1800 * |
1717 * @param array|false $args Optional. Connection args, These are passed directly to |
1801 * @param array|false $args Optional. Connection args, These are passed |
1718 * the `WP_Filesystem_*()` classes. Default false. |
1802 * directly to the `WP_Filesystem_*()` classes. |
1719 * @param string|false $context Optional. Context for get_filesystem_method(). Default false. |
1803 * Default false. |
1720 * @param bool $allow_relaxed_file_ownership Optional. Whether to allow Group/World writable. Default false. |
1804 * @param string|false $context Optional. Context for get_filesystem_method(). |
1721 * @return bool|null True on success, false on failure, null if the filesystem method class file does not exist. |
1805 * Default false. |
1722 */ |
1806 * @param bool $allow_relaxed_file_ownership Optional. Whether to allow Group/World writable. |
1723 function WP_Filesystem( $args = false, $context = false, $allow_relaxed_file_ownership = false ) { |
1807 * Default false. |
1808 * @return bool|null True on success, false on failure, |
|
1809 * null if the filesystem method class file does not exist. |
|
1810 */ |
|
1811 function WP_Filesystem( $args = false, $context = false, $allow_relaxed_file_ownership = false ) { // phpcs:ignore WordPress.NamingConventions.ValidFunctionName.FunctionNameInvalid |
|
1724 global $wp_filesystem; |
1812 global $wp_filesystem; |
1725 |
1813 |
1726 require_once( ABSPATH . 'wp-admin/includes/class-wp-filesystem-base.php' ); |
1814 require_once ABSPATH . 'wp-admin/includes/class-wp-filesystem-base.php'; |
1727 |
1815 |
1728 $method = get_filesystem_method( $args, $context, $allow_relaxed_file_ownership ); |
1816 $method = get_filesystem_method( $args, $context, $allow_relaxed_file_ownership ); |
1729 |
1817 |
1730 if ( ! $method ) { |
1818 if ( ! $method ) { |
1731 return false; |
1819 return false; |
1747 |
1835 |
1748 if ( ! file_exists( $abstraction_file ) ) { |
1836 if ( ! file_exists( $abstraction_file ) ) { |
1749 return; |
1837 return; |
1750 } |
1838 } |
1751 |
1839 |
1752 require_once( $abstraction_file ); |
1840 require_once $abstraction_file; |
1753 } |
1841 } |
1754 $method = "WP_Filesystem_$method"; |
1842 $method = "WP_Filesystem_$method"; |
1755 |
1843 |
1756 $wp_filesystem = new $method( $args ); |
1844 $wp_filesystem = new $method( $args ); |
1757 |
1845 |
1758 //Define the timeouts for the connections. Only available after the construct is called to allow for per-transport overriding of the default. |
1846 /* |
1847 * Define the timeouts for the connections. Only available after the constructor is called |
|
1848 * to allow for per-transport overriding of the default. |
|
1849 */ |
|
1759 if ( ! defined( 'FS_CONNECT_TIMEOUT' ) ) { |
1850 if ( ! defined( 'FS_CONNECT_TIMEOUT' ) ) { |
1760 define( 'FS_CONNECT_TIMEOUT', 30 ); |
1851 define( 'FS_CONNECT_TIMEOUT', 30 ); |
1761 } |
1852 } |
1762 if ( ! defined( 'FS_TIMEOUT' ) ) { |
1853 if ( ! defined( 'FS_TIMEOUT' ) ) { |
1763 define( 'FS_TIMEOUT', 30 ); |
1854 define( 'FS_TIMEOUT', 30 ); |
1766 if ( is_wp_error( $wp_filesystem->errors ) && $wp_filesystem->errors->has_errors() ) { |
1857 if ( is_wp_error( $wp_filesystem->errors ) && $wp_filesystem->errors->has_errors() ) { |
1767 return false; |
1858 return false; |
1768 } |
1859 } |
1769 |
1860 |
1770 if ( ! $wp_filesystem->connect() ) { |
1861 if ( ! $wp_filesystem->connect() ) { |
1771 return false; //There was an error connecting to the server. |
1862 return false; // There was an error connecting to the server. |
1772 } |
1863 } |
1773 |
1864 |
1774 // Set the permission constants if not already set. |
1865 // Set the permission constants if not already set. |
1775 if ( ! defined( 'FS_CHMOD_DIR' ) ) { |
1866 if ( ! defined( 'FS_CHMOD_DIR' ) ) { |
1776 define( 'FS_CHMOD_DIR', ( fileperms( ABSPATH ) & 0777 | 0755 ) ); |
1867 define( 'FS_CHMOD_DIR', ( fileperms( ABSPATH ) & 0777 | 0755 ) ); |
1791 * 'ftpext' or 'ftpsockets'. |
1882 * 'ftpext' or 'ftpsockets'. |
1792 * |
1883 * |
1793 * The return value can be overridden by defining the `FS_METHOD` constant in `wp-config.php`, |
1884 * The return value can be overridden by defining the `FS_METHOD` constant in `wp-config.php`, |
1794 * or filtering via {@see 'filesystem_method'}. |
1885 * or filtering via {@see 'filesystem_method'}. |
1795 * |
1886 * |
1796 * @link https://codex.wordpress.org/Editing_wp-config.php#WordPress_Upgrade_Constants |
1887 * @link https://wordpress.org/support/article/editing-wp-config-php/#wordpress-upgrade-constants |
1797 * |
1888 * |
1798 * Plugins may define a custom transport handler, See WP_Filesystem(). |
1889 * Plugins may define a custom transport handler, See WP_Filesystem(). |
1799 * |
1890 * |
1800 * @since 2.5.0 |
1891 * @since 2.5.0 |
1801 * |
1892 * |
1807 * @param bool $allow_relaxed_file_ownership Optional. Whether to allow Group/World writable. |
1898 * @param bool $allow_relaxed_file_ownership Optional. Whether to allow Group/World writable. |
1808 * Default false. |
1899 * Default false. |
1809 * @return string The transport to use, see description for valid return values. |
1900 * @return string The transport to use, see description for valid return values. |
1810 */ |
1901 */ |
1811 function get_filesystem_method( $args = array(), $context = '', $allow_relaxed_file_ownership = false ) { |
1902 function get_filesystem_method( $args = array(), $context = '', $allow_relaxed_file_ownership = false ) { |
1812 $method = defined( 'FS_METHOD' ) ? FS_METHOD : false; // Please ensure that this is either 'direct', 'ssh2', 'ftpext' or 'ftpsockets' |
1903 // Please ensure that this is either 'direct', 'ssh2', 'ftpext', or 'ftpsockets'. |
1904 $method = defined( 'FS_METHOD' ) ? FS_METHOD : false; |
|
1813 |
1905 |
1814 if ( ! $context ) { |
1906 if ( ! $context ) { |
1815 $context = WP_CONTENT_DIR; |
1907 $context = WP_CONTENT_DIR; |
1816 } |
1908 } |
1817 |
1909 |
1826 |
1918 |
1827 $temp_file_name = $context . 'temp-write-test-' . str_replace( '.', '-', uniqid( '', true ) ); |
1919 $temp_file_name = $context . 'temp-write-test-' . str_replace( '.', '-', uniqid( '', true ) ); |
1828 $temp_handle = @fopen( $temp_file_name, 'w' ); |
1920 $temp_handle = @fopen( $temp_file_name, 'w' ); |
1829 if ( $temp_handle ) { |
1921 if ( $temp_handle ) { |
1830 |
1922 |
1831 // Attempt to determine the file owner of the WordPress files, and that of newly created files |
1923 // Attempt to determine the file owner of the WordPress files, and that of newly created files. |
1832 $wp_file_owner = $temp_file_owner = false; |
1924 $wp_file_owner = false; |
1925 $temp_file_owner = false; |
|
1833 if ( function_exists( 'fileowner' ) ) { |
1926 if ( function_exists( 'fileowner' ) ) { |
1834 $wp_file_owner = @fileowner( __FILE__ ); |
1927 $wp_file_owner = @fileowner( __FILE__ ); |
1835 $temp_file_owner = @fileowner( $temp_file_name ); |
1928 $temp_file_owner = @fileowner( $temp_file_name ); |
1836 } |
1929 } |
1837 |
1930 |
1838 if ( $wp_file_owner !== false && $wp_file_owner === $temp_file_owner ) { |
1931 if ( false !== $wp_file_owner && $wp_file_owner === $temp_file_owner ) { |
1839 // WordPress is creating files as the same owner as the WordPress files, |
1932 /* |
1840 // this means it's safe to modify & create new files via PHP. |
1933 * WordPress is creating files as the same owner as the WordPress files, |
1934 * this means it's safe to modify & create new files via PHP. |
|
1935 */ |
|
1841 $method = 'direct'; |
1936 $method = 'direct'; |
1842 $GLOBALS['_wp_filesystem_direct_method'] = 'file_owner'; |
1937 $GLOBALS['_wp_filesystem_direct_method'] = 'file_owner'; |
1843 } elseif ( $allow_relaxed_file_ownership ) { |
1938 } elseif ( $allow_relaxed_file_ownership ) { |
1844 // The $context directory is writable, and $allow_relaxed_file_ownership is set, this means we can modify files |
1939 /* |
1845 // safely in this directory. This mode doesn't create new files, only alter existing ones. |
1940 * The $context directory is writable, and $allow_relaxed_file_ownership is set, |
1941 * this means we can modify files safely in this directory. |
|
1942 * This mode doesn't create new files, only alter existing ones. |
|
1943 */ |
|
1846 $method = 'direct'; |
1944 $method = 'direct'; |
1847 $GLOBALS['_wp_filesystem_direct_method'] = 'relaxed_ownership'; |
1945 $GLOBALS['_wp_filesystem_direct_method'] = 'relaxed_ownership'; |
1848 } |
1946 } |
1849 |
1947 |
1850 @fclose( $temp_handle ); |
1948 fclose( $temp_handle ); |
1851 @unlink( $temp_file_name ); |
1949 @unlink( $temp_file_name ); |
1852 } |
1950 } |
1853 } |
1951 } |
1854 |
1952 |
1855 if ( ! $method && isset( $args['connection_type'] ) && 'ssh' == $args['connection_type'] && extension_loaded( 'ssh2' ) && function_exists( 'stream_get_contents' ) ) { |
1953 if ( ! $method && isset( $args['connection_type'] ) && 'ssh' === $args['connection_type'] && extension_loaded( 'ssh2' ) ) { |
1856 $method = 'ssh2'; |
1954 $method = 'ssh2'; |
1857 } |
1955 } |
1858 if ( ! $method && extension_loaded( 'ftp' ) ) { |
1956 if ( ! $method && extension_loaded( 'ftp' ) ) { |
1859 $method = 'ftpext'; |
1957 $method = 'ftpext'; |
1860 } |
1958 } |
1861 if ( ! $method && ( extension_loaded( 'sockets' ) || function_exists( 'fsockopen' ) ) ) { |
1959 if ( ! $method && ( extension_loaded( 'sockets' ) || function_exists( 'fsockopen' ) ) ) { |
1862 $method = 'ftpsockets'; //Sockets: Socket extension; PHP Mode: FSockopen / fwrite / fread |
1960 $method = 'ftpsockets'; // Sockets: Socket extension; PHP Mode: FSockopen / fwrite / fread. |
1863 } |
1961 } |
1864 |
1962 |
1865 /** |
1963 /** |
1866 * Filters the filesystem method to use. |
1964 * Filters the filesystem method to use. |
1867 * |
1965 * |
1868 * @since 2.6.0 |
1966 * @since 2.6.0 |
1869 * |
1967 * |
1870 * @param string $method Filesystem method to return. |
1968 * @param string $method Filesystem method to return. |
1871 * @param array $args An array of connection details for the method. |
1969 * @param array $args An array of connection details for the method. |
1872 * @param string $context Full path to the directory that is tested for being writable. |
1970 * @param string $context Full path to the directory that is tested for being writable. |
1873 * @param bool $allow_relaxed_file_ownership Whether to allow Group/World writable. |
1971 * @param bool $allow_relaxed_file_ownership Whether to allow Group/World writable. |
1874 */ |
1972 */ |
1875 return apply_filters( 'filesystem_method', $method, $args, $context, $allow_relaxed_file_ownership ); |
1973 return apply_filters( 'filesystem_method', $method, $args, $context, $allow_relaxed_file_ownership ); |
1876 } |
1974 } |
1877 |
1975 |
1889 * @since 2.5.0 |
1987 * @since 2.5.0 |
1890 * @since 4.6.0 The `$context` parameter default changed from `false` to an empty string. |
1988 * @since 4.6.0 The `$context` parameter default changed from `false` to an empty string. |
1891 * |
1989 * |
1892 * @global string $pagenow |
1990 * @global string $pagenow |
1893 * |
1991 * |
1894 * @param string $form_post The URL to post the form to. |
1992 * @param string $form_post The URL to post the form to. |
1895 * @param string $type Optional. Chosen type of filesystem. Default empty. |
1993 * @param string $type Optional. Chosen type of filesystem. Default empty. |
1896 * @param bool $error Optional. Whether the current request has failed to connect. |
1994 * @param bool|WP_Error $error Optional. Whether the current request has failed |
1897 * Default false. |
1995 * to connect, or an error object. Default false. |
1898 * @param string $context Optional. Full path to the directory that is tested for being |
1996 * @param string $context Optional. Full path to the directory that is tested |
1899 * writable. Default empty. |
1997 * for being writable. Default empty. |
1900 * @param array $extra_fields Optional. Extra `POST` fields to be checked for inclusion in |
1998 * @param array $extra_fields Optional. Extra `POST` fields to be checked |
1901 * the post. Default null. |
1999 * for inclusion in the post. Default null. |
1902 * @param bool $allow_relaxed_file_ownership Optional. Whether to allow Group/World writable. Default false. |
2000 * @param bool $allow_relaxed_file_ownership Optional. Whether to allow Group/World writable. |
1903 * |
2001 * Default false. |
1904 * @return bool True on success, false on failure. |
2002 * @return bool|array True if no filesystem credentials are required, |
2003 * false if they are required but have not been provided, |
|
2004 * array of credentials if they are required and have been provided. |
|
1905 */ |
2005 */ |
1906 function request_filesystem_credentials( $form_post, $type = '', $error = false, $context = '', $extra_fields = null, $allow_relaxed_file_ownership = false ) { |
2006 function request_filesystem_credentials( $form_post, $type = '', $error = false, $context = '', $extra_fields = null, $allow_relaxed_file_ownership = false ) { |
1907 global $pagenow; |
2007 global $pagenow; |
1908 |
2008 |
1909 /** |
2009 /** |
1910 * Filters the filesystem credentials form output. |
2010 * Filters the filesystem credentials. |
1911 * |
2011 * |
1912 * Returning anything other than an empty string will effectively short-circuit |
2012 * Returning anything other than an empty string will effectively short-circuit |
1913 * output of the filesystem credentials form, returning that value instead. |
2013 * output of the filesystem credentials form, returning that value instead. |
1914 * |
2014 * |
2015 * A filter should return true if no filesystem credentials are required, false if they are required but have not been |
|
2016 * provided, or an array of credentials if they are required and have been provided. |
|
2017 * |
|
1915 * @since 2.5.0 |
2018 * @since 2.5.0 |
1916 * @since 4.6.0 The `$context` parameter default changed from `false` to an empty string. |
2019 * @since 4.6.0 The `$context` parameter default changed from `false` to an empty string. |
1917 * |
2020 * |
1918 * @param mixed $output Form output to return instead. Default empty. |
2021 * @param mixed $credentials Credentials to return instead. Default empty string. |
1919 * @param string $form_post The URL to post the form to. |
2022 * @param string $form_post The URL to post the form to. |
1920 * @param string $type Chosen type of filesystem. |
2023 * @param string $type Chosen type of filesystem. |
1921 * @param bool $error Whether the current request has failed to connect. |
2024 * @param bool|WP_Error $error Whether the current request has failed to connect, |
1922 * Default false. |
2025 * or an error object. |
1923 * @param string $context Full path to the directory that is tested for |
2026 * @param string $context Full path to the directory that is tested for |
1924 * being writable. |
2027 * being writable. |
1925 * @param bool $allow_relaxed_file_ownership Whether to allow Group/World writable. |
2028 * @param array $extra_fields Extra POST fields. |
1926 * Default false. |
2029 * @param bool $allow_relaxed_file_ownership Whether to allow Group/World writable. |
1927 * @param array $extra_fields Extra POST fields. |
|
1928 */ |
2030 */ |
1929 $req_cred = apply_filters( 'request_filesystem_credentials', '', $form_post, $type, $error, $context, $extra_fields, $allow_relaxed_file_ownership ); |
2031 $req_cred = apply_filters( 'request_filesystem_credentials', '', $form_post, $type, $error, $context, $extra_fields, $allow_relaxed_file_ownership ); |
1930 if ( '' !== $req_cred ) { |
2032 if ( '' !== $req_cred ) { |
1931 return $req_cred; |
2033 return $req_cred; |
1932 } |
2034 } |
1933 |
2035 |
1934 if ( empty( $type ) ) { |
2036 if ( empty( $type ) ) { |
1935 $type = get_filesystem_method( array(), $context, $allow_relaxed_file_ownership ); |
2037 $type = get_filesystem_method( array(), $context, $allow_relaxed_file_ownership ); |
1936 } |
2038 } |
1937 |
2039 |
1938 if ( 'direct' == $type ) { |
2040 if ( 'direct' === $type ) { |
1939 return true; |
2041 return true; |
1940 } |
2042 } |
1941 |
2043 |
1942 if ( is_null( $extra_fields ) ) { |
2044 if ( is_null( $extra_fields ) ) { |
1943 $extra_fields = array( 'version', 'locale' ); |
2045 $extra_fields = array( 'version', 'locale' ); |
1951 ) |
2053 ) |
1952 ); |
2054 ); |
1953 |
2055 |
1954 $submitted_form = wp_unslash( $_POST ); |
2056 $submitted_form = wp_unslash( $_POST ); |
1955 |
2057 |
1956 // Verify nonce, or unset submitted form field values on failure |
2058 // Verify nonce, or unset submitted form field values on failure. |
1957 if ( ! isset( $_POST['_fs_nonce'] ) || ! wp_verify_nonce( $_POST['_fs_nonce'], 'filesystem-credentials' ) ) { |
2059 if ( ! isset( $_POST['_fs_nonce'] ) || ! wp_verify_nonce( $_POST['_fs_nonce'], 'filesystem-credentials' ) ) { |
1958 unset( |
2060 unset( |
1959 $submitted_form['hostname'], |
2061 $submitted_form['hostname'], |
1960 $submitted_form['username'], |
2062 $submitted_form['username'], |
1961 $submitted_form['password'], |
2063 $submitted_form['password'], |
1963 $submitted_form['private_key'], |
2065 $submitted_form['private_key'], |
1964 $submitted_form['connection_type'] |
2066 $submitted_form['connection_type'] |
1965 ); |
2067 ); |
1966 } |
2068 } |
1967 |
2069 |
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) |
2070 // 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). |
1969 $credentials['hostname'] = defined( 'FTP_HOST' ) ? FTP_HOST : ( ! empty( $submitted_form['hostname'] ) ? $submitted_form['hostname'] : $credentials['hostname'] ); |
2071 $credentials['hostname'] = defined( 'FTP_HOST' ) ? FTP_HOST : ( ! empty( $submitted_form['hostname'] ) ? $submitted_form['hostname'] : $credentials['hostname'] ); |
1970 $credentials['username'] = defined( 'FTP_USER' ) ? FTP_USER : ( ! empty( $submitted_form['username'] ) ? $submitted_form['username'] : $credentials['username'] ); |
2072 $credentials['username'] = defined( 'FTP_USER' ) ? FTP_USER : ( ! empty( $submitted_form['username'] ) ? $submitted_form['username'] : $credentials['username'] ); |
1971 $credentials['password'] = defined( 'FTP_PASS' ) ? FTP_PASS : ( ! empty( $submitted_form['password'] ) ? $submitted_form['password'] : '' ); |
2073 $credentials['password'] = defined( 'FTP_PASS' ) ? FTP_PASS : ( ! empty( $submitted_form['password'] ) ? $submitted_form['password'] : '' ); |
1972 |
2074 |
1973 // Check to see if we are setting the public/private keys for ssh |
2075 // Check to see if we are setting the public/private keys for ssh. |
1974 $credentials['public_key'] = defined( 'FTP_PUBKEY' ) ? FTP_PUBKEY : ( ! empty( $submitted_form['public_key'] ) ? $submitted_form['public_key'] : '' ); |
2076 $credentials['public_key'] = defined( 'FTP_PUBKEY' ) ? FTP_PUBKEY : ( ! empty( $submitted_form['public_key'] ) ? $submitted_form['public_key'] : '' ); |
1975 $credentials['private_key'] = defined( 'FTP_PRIKEY' ) ? FTP_PRIKEY : ( ! empty( $submitted_form['private_key'] ) ? $submitted_form['private_key'] : '' ); |
2077 $credentials['private_key'] = defined( 'FTP_PRIKEY' ) ? FTP_PRIKEY : ( ! empty( $submitted_form['private_key'] ) ? $submitted_form['private_key'] : '' ); |
1976 |
2078 |
1977 // Sanitize the hostname, Some people might pass in odd-data: |
2079 // Sanitize the hostname, some people might pass in odd data. |
1978 $credentials['hostname'] = preg_replace( '|\w+://|', '', $credentials['hostname'] ); //Strip any schemes off |
2080 $credentials['hostname'] = preg_replace( '|\w+://|', '', $credentials['hostname'] ); // Strip any schemes off. |
1979 |
2081 |
1980 if ( strpos( $credentials['hostname'], ':' ) ) { |
2082 if ( strpos( $credentials['hostname'], ':' ) ) { |
1981 list( $credentials['hostname'], $credentials['port'] ) = explode( ':', $credentials['hostname'], 2 ); |
2083 list( $credentials['hostname'], $credentials['port'] ) = explode( ':', $credentials['hostname'], 2 ); |
1982 if ( ! is_numeric( $credentials['port'] ) ) { |
2084 if ( ! is_numeric( $credentials['port'] ) ) { |
1983 unset( $credentials['port'] ); |
2085 unset( $credentials['port'] ); |
1984 } |
2086 } |
1985 } else { |
2087 } else { |
1986 unset( $credentials['port'] ); |
2088 unset( $credentials['port'] ); |
1987 } |
2089 } |
1988 |
2090 |
1989 if ( ( defined( 'FTP_SSH' ) && FTP_SSH ) || ( defined( 'FS_METHOD' ) && 'ssh2' == FS_METHOD ) ) { |
2091 if ( ( defined( 'FTP_SSH' ) && FTP_SSH ) || ( defined( 'FS_METHOD' ) && 'ssh2' === FS_METHOD ) ) { |
1990 $credentials['connection_type'] = 'ssh'; |
2092 $credentials['connection_type'] = 'ssh'; |
1991 } elseif ( ( defined( 'FTP_SSL' ) && FTP_SSL ) && 'ftpext' == $type ) { //Only the FTP Extension understands SSL |
2093 } elseif ( ( defined( 'FTP_SSL' ) && FTP_SSL ) && 'ftpext' === $type ) { // Only the FTP Extension understands SSL. |
1992 $credentials['connection_type'] = 'ftps'; |
2094 $credentials['connection_type'] = 'ftps'; |
1993 } elseif ( ! empty( $submitted_form['connection_type'] ) ) { |
2095 } elseif ( ! empty( $submitted_form['connection_type'] ) ) { |
1994 $credentials['connection_type'] = $submitted_form['connection_type']; |
2096 $credentials['connection_type'] = $submitted_form['connection_type']; |
1995 } elseif ( ! isset( $credentials['connection_type'] ) ) { //All else fails (And it's not defaulted to something else saved), Default to FTP |
2097 } elseif ( ! isset( $credentials['connection_type'] ) ) { // All else fails (and it's not defaulted to something else saved), default to FTP. |
1996 $credentials['connection_type'] = 'ftp'; |
2098 $credentials['connection_type'] = 'ftp'; |
1997 } |
2099 } |
1998 if ( ! $error && |
2100 if ( ! $error |
1999 ( |
2101 && ( ( ! empty( $credentials['password'] ) && ! empty( $credentials['username'] ) && ! empty( $credentials['hostname'] ) ) |
2000 ( ! empty( $credentials['password'] ) && ! empty( $credentials['username'] ) && ! empty( $credentials['hostname'] ) ) || |
2102 || ( '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'] ) ) |
2103 ) |
2002 ) ) { |
2104 ) { |
2003 $stored_credentials = $credentials; |
2105 $stored_credentials = $credentials; |
2004 if ( ! empty( $stored_credentials['port'] ) ) { //save port as part of hostname to simplify above code. |
2106 |
2107 if ( ! empty( $stored_credentials['port'] ) ) { // Save port as part of hostname to simplify above code. |
|
2005 $stored_credentials['hostname'] .= ':' . $stored_credentials['port']; |
2108 $stored_credentials['hostname'] .= ':' . $stored_credentials['port']; |
2006 } |
2109 } |
2007 |
2110 |
2008 unset( $stored_credentials['password'], $stored_credentials['port'], $stored_credentials['private_key'], $stored_credentials['public_key'] ); |
2111 unset( $stored_credentials['password'], $stored_credentials['port'], $stored_credentials['private_key'], $stored_credentials['public_key'] ); |
2112 |
|
2009 if ( ! wp_installing() ) { |
2113 if ( ! wp_installing() ) { |
2010 update_option( 'ftp_credentials', $stored_credentials ); |
2114 update_option( 'ftp_credentials', $stored_credentials ); |
2011 } |
2115 } |
2116 |
|
2012 return $credentials; |
2117 return $credentials; |
2013 } |
2118 } |
2014 $hostname = isset( $credentials['hostname'] ) ? $credentials['hostname'] : ''; |
2119 $hostname = isset( $credentials['hostname'] ) ? $credentials['hostname'] : ''; |
2015 $username = isset( $credentials['username'] ) ? $credentials['username'] : ''; |
2120 $username = isset( $credentials['username'] ) ? $credentials['username'] : ''; |
2016 $public_key = isset( $credentials['public_key'] ) ? $credentials['public_key'] : ''; |
2121 $public_key = isset( $credentials['public_key'] ) ? $credentials['public_key'] : ''; |
2017 $private_key = isset( $credentials['private_key'] ) ? $credentials['private_key'] : ''; |
2122 $private_key = isset( $credentials['private_key'] ) ? $credentials['private_key'] : ''; |
2018 $port = isset( $credentials['port'] ) ? $credentials['port'] : ''; |
2123 $port = isset( $credentials['port'] ) ? $credentials['port'] : ''; |
2019 $connection_type = isset( $credentials['connection_type'] ) ? $credentials['connection_type'] : ''; |
2124 $connection_type = isset( $credentials['connection_type'] ) ? $credentials['connection_type'] : ''; |
2020 |
2125 |
2021 if ( $error ) { |
2126 if ( $error ) { |
2022 $error_string = __( '<strong>ERROR:</strong> There was an error connecting to the server, Please verify the settings are correct.' ); |
2127 $error_string = __( '<strong>Error</strong>: Could not connect to the server. Please verify the settings are correct.' ); |
2023 if ( is_wp_error( $error ) ) { |
2128 if ( is_wp_error( $error ) ) { |
2024 $error_string = esc_html( $error->get_error_message() ); |
2129 $error_string = esc_html( $error->get_error_message() ); |
2025 } |
2130 } |
2026 echo '<div id="message" class="error"><p>' . $error_string . '</p></div>'; |
2131 echo '<div id="message" class="error"><p>' . $error_string . '</p></div>'; |
2027 } |
2132 } |
2028 |
2133 |
2029 $types = array(); |
2134 $types = array(); |
2030 if ( extension_loaded( 'ftp' ) || extension_loaded( 'sockets' ) || function_exists( 'fsockopen' ) ) { |
2135 if ( extension_loaded( 'ftp' ) || extension_loaded( 'sockets' ) || function_exists( 'fsockopen' ) ) { |
2031 $types['ftp'] = __( 'FTP' ); |
2136 $types['ftp'] = __( 'FTP' ); |
2032 } |
2137 } |
2033 if ( extension_loaded( 'ftp' ) ) { //Only this supports FTPS |
2138 if ( extension_loaded( 'ftp' ) ) { // Only this supports FTPS. |
2034 $types['ftps'] = __( 'FTPS (SSL)' ); |
2139 $types['ftps'] = __( 'FTPS (SSL)' ); |
2035 } |
2140 } |
2036 if ( extension_loaded( 'ssh2' ) && function_exists( 'stream_get_contents' ) ) { |
2141 if ( extension_loaded( 'ssh2' ) ) { |
2037 $types['ssh'] = __( 'SSH2' ); |
2142 $types['ssh'] = __( 'SSH2' ); |
2038 } |
2143 } |
2039 |
2144 |
2040 /** |
2145 /** |
2041 * Filters the connection types to output to the filesystem credentials form. |
2146 * Filters the connection types to output to the filesystem credentials form. |
2042 * |
2147 * |
2043 * @since 2.9.0 |
2148 * @since 2.9.0 |
2044 * @since 4.6.0 The `$context` parameter default changed from `false` to an empty string. |
2149 * @since 4.6.0 The `$context` parameter default changed from `false` to an empty string. |
2045 * |
2150 * |
2046 * @param array $types Types of connections. |
2151 * @param string[] $types Types of connections. |
2047 * @param array $credentials Credentials to connect with. |
2152 * @param array $credentials Credentials to connect with. |
2048 * @param string $type Chosen filesystem method. |
2153 * @param string $type Chosen filesystem method. |
2049 * @param object $error Error object. |
2154 * @param bool|WP_Error $error Whether the current request has failed to connect, |
2050 * @param string $context Full path to the directory that is tested |
2155 * or an error object. |
2051 * for being writable. |
2156 * @param string $context Full path to the directory that is tested for being writable. |
2052 */ |
2157 */ |
2053 $types = apply_filters( 'fs_ftp_connection_types', $types, $credentials, $type, $error, $context ); |
2158 $types = apply_filters( 'fs_ftp_connection_types', $types, $credentials, $type, $error, $context ); |
2054 |
2159 |
2055 ?> |
2160 ?> |
2056 <form action="<?php echo esc_url( $form_post ); ?>" method="post"> |
2161 <form action="<?php echo esc_url( $form_post ); ?>" method="post"> |
2131 ?> |
2236 ?> |
2132 </fieldset> |
2237 </fieldset> |
2133 <?php |
2238 <?php |
2134 if ( isset( $types['ssh'] ) ) { |
2239 if ( isset( $types['ssh'] ) ) { |
2135 $hidden_class = ''; |
2240 $hidden_class = ''; |
2136 if ( 'ssh' != $connection_type || empty( $connection_type ) ) { |
2241 if ( 'ssh' !== $connection_type || empty( $connection_type ) ) { |
2137 $hidden_class = ' class="hidden"'; |
2242 $hidden_class = ' class="hidden"'; |
2138 } |
2243 } |
2139 ?> |
2244 ?> |
2140 <fieldset id="ssh-keys"<?php echo $hidden_class; ?>> |
2245 <fieldset id="ssh-keys"<?php echo $hidden_class; ?>> |
2141 <legend><?php _e( 'Authentication Keys' ); ?></legend> |
2246 <legend><?php _e( 'Authentication Keys' ); ?></legend> |
2168 <?php |
2273 <?php |
2169 return false; |
2274 return false; |
2170 } |
2275 } |
2171 |
2276 |
2172 /** |
2277 /** |
2173 * Print the filesystem credentials modal when needed. |
2278 * Prints the filesystem credentials modal when needed. |
2174 * |
2279 * |
2175 * @since 4.2.0 |
2280 * @since 4.2.0 |
2176 */ |
2281 */ |
2177 function wp_print_request_filesystem_credentials_modal() { |
2282 function wp_print_request_filesystem_credentials_modal() { |
2178 $filesystem_method = get_filesystem_method(); |
2283 $filesystem_method = get_filesystem_method(); |
2284 |
|
2179 ob_start(); |
2285 ob_start(); |
2180 $filesystem_credentials_are_stored = request_filesystem_credentials( self_admin_url() ); |
2286 $filesystem_credentials_are_stored = request_filesystem_credentials( self_admin_url() ); |
2181 ob_end_clean(); |
2287 ob_end_clean(); |
2182 $request_filesystem_credentials = ( $filesystem_method != 'direct' && ! $filesystem_credentials_are_stored ); |
2288 |
2289 $request_filesystem_credentials = ( 'direct' !== $filesystem_method && ! $filesystem_credentials_are_stored ); |
|
2183 if ( ! $request_filesystem_credentials ) { |
2290 if ( ! $request_filesystem_credentials ) { |
2184 return; |
2291 return; |
2185 } |
2292 } |
2186 ?> |
2293 ?> |
2187 <div id="request-filesystem-credentials-dialog" class="notification-dialog-wrap request-filesystem-credentials-dialog"> |
2294 <div id="request-filesystem-credentials-dialog" class="notification-dialog-wrap request-filesystem-credentials-dialog"> |
2194 </div> |
2301 </div> |
2195 <?php |
2302 <?php |
2196 } |
2303 } |
2197 |
2304 |
2198 /** |
2305 /** |
2199 * Generate a single group for the personal data export report. |
2306 * Attempts to clear the opcode cache for an individual PHP file. |
2200 * |
2307 * |
2201 * @since 4.9.6 |
2308 * This function can be called safely without having to check the file extension |
2202 * |
2309 * or availability of the OPcache extension. |
2203 * @param array $group_data { |
2310 * |
2204 * The group data to render. |
2311 * Whether or not invalidation is possible is cached to improve performance. |
2205 * |
2312 * |
2206 * @type string $group_label The user-facing heading for the group, e.g. 'Comments'. |
2313 * @since 5.5.0 |
2207 * @type array $items { |
2314 * |
2208 * An array of group items. |
2315 * @link https://www.php.net/manual/en/function.opcache-invalidate.php |
2209 * |
2316 * |
2210 * @type array $group_item_data { |
2317 * @param string $filepath Path to the file, including extension, for which the opcode cache is to be cleared. |
2211 * An array of name-value pairs for the item. |
2318 * @param bool $force Invalidate even if the modification time is not newer than the file in cache. |
2212 * |
2319 * Default false. |
2213 * @type string $name The user-facing name of an item name-value pair, e.g. 'IP Address'. |
2320 * @return bool True if opcache was invalidated for `$filepath`, or there was nothing to invalidate. |
2214 * @type string $value The user-facing value of an item data pair, e.g. '50.60.70.0'. |
2321 * False if opcache invalidation is not available, or is disabled via filter. |
2215 * } |
2322 */ |
2216 * } |
2323 function wp_opcache_invalidate( $filepath, $force = false ) { |
2217 * } |
2324 static $can_invalidate = null; |
2218 * @return string The HTML for this group and its items. |
|
2219 */ |
|
2220 function wp_privacy_generate_personal_data_export_group_html( $group_data ) { |
|
2221 $group_html = '<h2>' . esc_html( $group_data['group_label'] ) . '</h2>'; |
|
2222 $group_html .= '<div>'; |
|
2223 |
|
2224 foreach ( (array) $group_data['items'] as $group_item_id => $group_item_data ) { |
|
2225 $group_html .= '<table>'; |
|
2226 $group_html .= '<tbody>'; |
|
2227 |
|
2228 foreach ( (array) $group_item_data as $group_item_datum ) { |
|
2229 $value = $group_item_datum['value']; |
|
2230 // If it looks like a link, make it a link. |
|
2231 if ( false === strpos( $value, ' ' ) && ( 0 === strpos( $value, 'http://' ) || 0 === strpos( $value, 'https://' ) ) ) { |
|
2232 $value = '<a href="' . esc_url( $value ) . '">' . esc_html( $value ) . '</a>'; |
|
2233 } |
|
2234 |
|
2235 $group_html .= '<tr>'; |
|
2236 $group_html .= '<th>' . esc_html( $group_item_datum['name'] ) . '</th>'; |
|
2237 $group_html .= '<td>' . wp_kses( $value, 'personal_data_export' ) . '</td>'; |
|
2238 $group_html .= '</tr>'; |
|
2239 } |
|
2240 |
|
2241 $group_html .= '</tbody>'; |
|
2242 $group_html .= '</table>'; |
|
2243 } |
|
2244 |
|
2245 $group_html .= '</div>'; |
|
2246 |
|
2247 return $group_html; |
|
2248 } |
|
2249 |
|
2250 /** |
|
2251 * Generate the personal data export file. |
|
2252 * |
|
2253 * @since 4.9.6 |
|
2254 * |
|
2255 * @param int $request_id The export request ID. |
|
2256 */ |
|
2257 function wp_privacy_generate_personal_data_export_file( $request_id ) { |
|
2258 if ( ! class_exists( 'ZipArchive' ) ) { |
|
2259 wp_send_json_error( __( 'Unable to generate export file. ZipArchive not available.' ) ); |
|
2260 } |
|
2261 |
|
2262 // Get the request data. |
|
2263 $request = wp_get_user_request_data( $request_id ); |
|
2264 |
|
2265 if ( ! $request || 'export_personal_data' !== $request->action_name ) { |
|
2266 wp_send_json_error( __( 'Invalid request ID when generating export file.' ) ); |
|
2267 } |
|
2268 |
|
2269 $email_address = $request->email; |
|
2270 |
|
2271 if ( ! is_email( $email_address ) ) { |
|
2272 wp_send_json_error( __( 'Invalid email address when generating export file.' ) ); |
|
2273 } |
|
2274 |
|
2275 // Create the exports folder if needed. |
|
2276 $exports_dir = wp_privacy_exports_dir(); |
|
2277 $exports_url = wp_privacy_exports_url(); |
|
2278 |
|
2279 if ( ! wp_mkdir_p( $exports_dir ) ) { |
|
2280 wp_send_json_error( __( 'Unable to create export folder.' ) ); |
|
2281 } |
|
2282 |
|
2283 // Protect export folder from browsing. |
|
2284 $index_pathname = $exports_dir . 'index.html'; |
|
2285 if ( ! file_exists( $index_pathname ) ) { |
|
2286 $file = fopen( $index_pathname, 'w' ); |
|
2287 if ( false === $file ) { |
|
2288 wp_send_json_error( __( 'Unable to protect export folder from browsing.' ) ); |
|
2289 } |
|
2290 fwrite( $file, '<!-- Silence is golden. -->' ); |
|
2291 fclose( $file ); |
|
2292 } |
|
2293 |
|
2294 $stripped_email = str_replace( '@', '-at-', $email_address ); |
|
2295 $stripped_email = sanitize_title( $stripped_email ); // slugify the email address |
|
2296 $obscura = wp_generate_password( 32, false, false ); |
|
2297 $file_basename = 'wp-personal-data-file-' . $stripped_email . '-' . $obscura; |
|
2298 $html_report_filename = $file_basename . '.html'; |
|
2299 $html_report_pathname = wp_normalize_path( $exports_dir . $html_report_filename ); |
|
2300 $file = fopen( $html_report_pathname, 'w' ); |
|
2301 if ( false === $file ) { |
|
2302 wp_send_json_error( __( 'Unable to open export file (HTML report) for writing.' ) ); |
|
2303 } |
|
2304 |
|
2305 $title = sprintf( |
|
2306 /* translators: %s: user's email address */ |
|
2307 __( 'Personal Data Export for %s' ), |
|
2308 $email_address |
|
2309 ); |
|
2310 |
|
2311 // Open HTML. |
|
2312 fwrite( $file, "<!DOCTYPE html>\n" ); |
|
2313 fwrite( $file, "<html>\n" ); |
|
2314 |
|
2315 // Head. |
|
2316 fwrite( $file, "<head>\n" ); |
|
2317 fwrite( $file, "<meta http-equiv='Content-Type' content='text/html; charset=UTF-8' />\n" ); |
|
2318 fwrite( $file, "<style type='text/css'>" ); |
|
2319 fwrite( $file, 'body { color: black; font-family: Arial, sans-serif; font-size: 11pt; margin: 15px auto; width: 860px; }' ); |
|
2320 fwrite( $file, 'table { background: #f0f0f0; border: 1px solid #ddd; margin-bottom: 20px; width: 100%; }' ); |
|
2321 fwrite( $file, 'th { padding: 5px; text-align: left; width: 20%; }' ); |
|
2322 fwrite( $file, 'td { padding: 5px; }' ); |
|
2323 fwrite( $file, 'tr:nth-child(odd) { background-color: #fafafa; }' ); |
|
2324 fwrite( $file, '</style>' ); |
|
2325 fwrite( $file, '<title>' ); |
|
2326 fwrite( $file, esc_html( $title ) ); |
|
2327 fwrite( $file, '</title>' ); |
|
2328 fwrite( $file, "</head>\n" ); |
|
2329 |
|
2330 // Body. |
|
2331 fwrite( $file, "<body>\n" ); |
|
2332 |
|
2333 // Heading. |
|
2334 fwrite( $file, '<h1>' . esc_html__( 'Personal Data Export' ) . '</h1>' ); |
|
2335 |
|
2336 // And now, all the Groups. |
|
2337 $groups = get_post_meta( $request_id, '_export_data_grouped', true ); |
|
2338 |
|
2339 // First, build an "About" group on the fly for this report. |
|
2340 $about_group = array( |
|
2341 /* translators: Header for the About section in a personal data export. */ |
|
2342 'group_label' => _x( 'About', 'personal data group label' ), |
|
2343 'items' => array( |
|
2344 'about-1' => array( |
|
2345 array( |
|
2346 'name' => _x( 'Report generated for', 'email address' ), |
|
2347 'value' => $email_address, |
|
2348 ), |
|
2349 array( |
|
2350 'name' => _x( 'For site', 'website name' ), |
|
2351 'value' => get_bloginfo( 'name' ), |
|
2352 ), |
|
2353 array( |
|
2354 'name' => _x( 'At URL', 'website URL' ), |
|
2355 'value' => get_bloginfo( 'url' ), |
|
2356 ), |
|
2357 array( |
|
2358 'name' => _x( 'On', 'date/time' ), |
|
2359 'value' => current_time( 'mysql' ), |
|
2360 ), |
|
2361 ), |
|
2362 ), |
|
2363 ); |
|
2364 |
|
2365 // Merge in the special about group. |
|
2366 $groups = array_merge( array( 'about' => $about_group ), $groups ); |
|
2367 |
|
2368 // Now, iterate over every group in $groups and have the formatter render it in HTML. |
|
2369 foreach ( (array) $groups as $group_id => $group_data ) { |
|
2370 fwrite( $file, wp_privacy_generate_personal_data_export_group_html( $group_data ) ); |
|
2371 } |
|
2372 |
|
2373 fwrite( $file, "</body>\n" ); |
|
2374 |
|
2375 // Close HTML. |
|
2376 fwrite( $file, "</html>\n" ); |
|
2377 fclose( $file ); |
|
2378 |
2325 |
2379 /* |
2326 /* |
2380 * Now, generate the ZIP. |
2327 * Check to see if WordPress is able to run `opcache_invalidate()` or not, and cache the value. |
2381 * |
2328 * |
2382 * If an archive has already been generated, then remove it and reuse the |
2329 * First, check to see if the function is available to call, then if the host has restricted |
2383 * filename, to avoid breaking any URLs that may have been previously sent |
2330 * the ability to run the function to avoid a PHP warning. |
2384 * via email. |
2331 * |
2332 * `opcache.restrict_api` can specify the path for files allowed to call `opcache_invalidate()`. |
|
2333 * |
|
2334 * If the host has this set, check whether the path in `opcache.restrict_api` matches |
|
2335 * the beginning of the path of the origin file. |
|
2336 * |
|
2337 * `$_SERVER['SCRIPT_FILENAME']` approximates the origin file's path, but `realpath()` |
|
2338 * is necessary because `SCRIPT_FILENAME` can be a relative path when run from CLI. |
|
2339 * |
|
2340 * For more details, see: |
|
2341 * - https://www.php.net/manual/en/opcache.configuration.php |
|
2342 * - https://www.php.net/manual/en/reserved.variables.server.php |
|
2343 * - https://core.trac.wordpress.org/ticket/36455 |
|
2385 */ |
2344 */ |
2386 $error = false; |
2345 if ( null === $can_invalidate |
2387 $archive_url = get_post_meta( $request_id, '_export_file_url', true ); |
2346 && function_exists( 'opcache_invalidate' ) |
2388 $archive_pathname = get_post_meta( $request_id, '_export_file_path', true ); |
2347 && ( ! ini_get( 'opcache.restrict_api' ) |
2389 |
2348 || stripos( realpath( $_SERVER['SCRIPT_FILENAME'] ), ini_get( 'opcache.restrict_api' ) ) === 0 ) |
2390 if ( empty( $archive_pathname ) || empty( $archive_url ) ) { |
2349 ) { |
2391 $archive_filename = $file_basename . '.zip'; |
2350 $can_invalidate = true; |
2392 $archive_pathname = $exports_dir . $archive_filename; |
2351 } |
2393 $archive_url = $exports_url . $archive_filename; |
2352 |
2394 |
2353 // If invalidation is not available, return early. |
2395 update_post_meta( $request_id, '_export_file_url', $archive_url ); |
2354 if ( ! $can_invalidate ) { |
2396 update_post_meta( $request_id, '_export_file_path', wp_normalize_path( $archive_pathname ) ); |
2355 return false; |
2397 } |
2356 } |
2398 |
2357 |
2399 if ( ! empty( $archive_pathname ) && file_exists( $archive_pathname ) ) { |
2358 // Verify that file to be invalidated has a PHP extension. |
2400 wp_delete_file( $archive_pathname ); |
2359 if ( '.php' !== strtolower( substr( $filepath, -4 ) ) ) { |
2401 } |
2360 return false; |
2402 |
2361 } |
2403 $zip = new ZipArchive; |
|
2404 if ( true === $zip->open( $archive_pathname, ZipArchive::CREATE ) ) { |
|
2405 if ( ! $zip->addFile( $html_report_pathname, 'index.html' ) ) { |
|
2406 $error = __( 'Unable to add data to export file.' ); |
|
2407 } |
|
2408 |
|
2409 $zip->close(); |
|
2410 |
|
2411 if ( ! $error ) { |
|
2412 /** |
|
2413 * Fires right after all personal data has been written to the export file. |
|
2414 * |
|
2415 * @since 4.9.6 |
|
2416 * |
|
2417 * @param string $archive_pathname The full path to the export file on the filesystem. |
|
2418 * @param string $archive_url The URL of the archive file. |
|
2419 * @param string $html_report_pathname The full path to the personal data report on the filesystem. |
|
2420 * @param int $request_id The export request ID. |
|
2421 */ |
|
2422 do_action( 'wp_privacy_personal_data_export_file_created', $archive_pathname, $archive_url, $html_report_pathname, $request_id ); |
|
2423 } |
|
2424 } else { |
|
2425 $error = __( 'Unable to open export file (archive) for writing.' ); |
|
2426 } |
|
2427 |
|
2428 // And remove the HTML file. |
|
2429 unlink( $html_report_pathname ); |
|
2430 |
|
2431 if ( $error ) { |
|
2432 wp_send_json_error( $error ); |
|
2433 } |
|
2434 } |
|
2435 |
|
2436 /** |
|
2437 * Send an email to the user with a link to the personal data export file |
|
2438 * |
|
2439 * @since 4.9.6 |
|
2440 * |
|
2441 * @param int $request_id The request ID for this personal data export. |
|
2442 * @return true|WP_Error True on success or `WP_Error` on failure. |
|
2443 */ |
|
2444 function wp_privacy_send_personal_data_export_email( $request_id ) { |
|
2445 // Get the request data. |
|
2446 $request = wp_get_user_request_data( $request_id ); |
|
2447 |
|
2448 if ( ! $request || 'export_personal_data' !== $request->action_name ) { |
|
2449 return new WP_Error( 'invalid_request', __( 'Invalid request ID when sending personal data export email.' ) ); |
|
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 ); |
|
2460 |
|
2461 /** This filter is documented in wp-includes/functions.php */ |
|
2462 $expiration = apply_filters( 'wp_privacy_export_expiration', 3 * DAY_IN_SECONDS ); |
|
2463 $expiration_date = date_i18n( get_option( 'date_format' ), time() + $expiration ); |
|
2464 |
|
2465 /* translators: Do not translate EXPIRATION, LINK, SITENAME, SITEURL: those are placeholders. */ |
|
2466 $email_text = __( |
|
2467 'Howdy, |
|
2468 |
|
2469 Your request for an export of personal data has been completed. You may |
|
2470 download your personal data by clicking on the link below. For privacy |
|
2471 and security, we will automatically delete the file on ###EXPIRATION###, |
|
2472 so please download it before then. |
|
2473 |
|
2474 ###LINK### |
|
2475 |
|
2476 Regards, |
|
2477 All at ###SITENAME### |
|
2478 ###SITEURL###' |
|
2479 ); |
|
2480 |
2362 |
2481 /** |
2363 /** |
2482 * Filters the text of the email sent with a personal data export file. |
2364 * Filters whether to invalidate a file from the opcode cache. |
2483 * |
2365 * |
2484 * The following strings have a special meaning and will get replaced dynamically: |
2366 * @since 5.5.0 |
2485 * ###EXPIRATION### The date when the URL will be automatically deleted. |
2367 * |
2486 * ###LINK### URL of the personal data export file for the user. |
2368 * @param bool $will_invalidate Whether WordPress will invalidate `$filepath`. Default true. |
2487 * ###SITENAME### The name of the site. |
2369 * @param string $filepath The path to the PHP file to invalidate. |
2488 * ###SITEURL### The URL to the site. |
|
2489 * |
|
2490 * @since 4.9.6 |
|
2491 * |
|
2492 * @param string $email_text Text in the email. |
|
2493 * @param int $request_id The request ID for this personal data export. |
|
2494 */ |
2370 */ |
2495 $content = apply_filters( 'wp_privacy_personal_data_email_content', $email_text, $request_id ); |
2371 if ( apply_filters( 'wp_opcache_invalidate_file', true, $filepath ) ) { |
2496 |
2372 return opcache_invalidate( $filepath, $force ); |
2497 $email_address = $request->email; |
2373 } |
2498 $export_file_url = get_post_meta( $request_id, '_export_file_url', true ); |
2374 |
2499 $site_name = wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES ); |
2375 return false; |
2500 $site_url = home_url(); |
2376 } |
2501 |
|
2502 $content = str_replace( '###EXPIRATION###', $expiration_date, $content ); |
|
2503 $content = str_replace( '###LINK###', esc_url_raw( $export_file_url ), $content ); |
|
2504 $content = str_replace( '###EMAIL###', $email_address, $content ); |
|
2505 $content = str_replace( '###SITENAME###', $site_name, $content ); |
|
2506 $content = str_replace( '###SITEURL###', esc_url_raw( $site_url ), $content ); |
|
2507 |
|
2508 $mail_success = wp_mail( |
|
2509 $email_address, |
|
2510 sprintf( |
|
2511 /* translators: Personal data export notification email subject. %s: Site title */ |
|
2512 __( '[%s] Personal Data Export' ), |
|
2513 $site_name |
|
2514 ), |
|
2515 $content |
|
2516 ); |
|
2517 |
|
2518 if ( $switched_locale ) { |
|
2519 restore_previous_locale(); |
|
2520 } |
|
2521 |
|
2522 if ( ! $mail_success ) { |
|
2523 return new WP_Error( 'privacy_email_error', __( 'Unable to send personal data export email.' ) ); |
|
2524 } |
|
2525 |
|
2526 return true; |
|
2527 } |
|
2528 |
|
2529 /** |
|
2530 * Intercept personal data exporter page ajax responses in order to assemble the personal data export file. |
|
2531 * @see wp_privacy_personal_data_export_page |
|
2532 * @since 4.9.6 |
|
2533 * |
|
2534 * @param array $response The response from the personal data exporter for the given page. |
|
2535 * @param int $exporter_index The index of the personal data exporter. Begins at 1. |
|
2536 * @param string $email_address The email address of the user whose personal data this is. |
|
2537 * @param int $page The page of personal data for this exporter. Begins at 1. |
|
2538 * @param int $request_id The request ID for this personal data export. |
|
2539 * @param bool $send_as_email Whether the final results of the export should be emailed to the user. |
|
2540 * @param string $exporter_key The slug (key) of the exporter. |
|
2541 * @return array The filtered response. |
|
2542 */ |
|
2543 function wp_privacy_process_personal_data_export_page( $response, $exporter_index, $email_address, $page, $request_id, $send_as_email, $exporter_key ) { |
|
2544 /* Do some simple checks on the shape of the response from the exporter. |
|
2545 * If the exporter response is malformed, don't attempt to consume it - let it |
|
2546 * pass through to generate a warning to the user by default ajax processing. |
|
2547 */ |
|
2548 if ( ! is_array( $response ) ) { |
|
2549 return $response; |
|
2550 } |
|
2551 |
|
2552 if ( ! array_key_exists( 'done', $response ) ) { |
|
2553 return $response; |
|
2554 } |
|
2555 |
|
2556 if ( ! array_key_exists( 'data', $response ) ) { |
|
2557 return $response; |
|
2558 } |
|
2559 |
|
2560 if ( ! is_array( $response['data'] ) ) { |
|
2561 return $response; |
|
2562 } |
|
2563 |
|
2564 // Get the request data. |
|
2565 $request = wp_get_user_request_data( $request_id ); |
|
2566 |
|
2567 if ( ! $request || 'export_personal_data' !== $request->action_name ) { |
|
2568 wp_send_json_error( __( 'Invalid request ID when merging exporter data.' ) ); |
|
2569 } |
|
2570 |
|
2571 $export_data = array(); |
|
2572 |
|
2573 // First exporter, first page? Reset the report data accumulation array. |
|
2574 if ( 1 === $exporter_index && 1 === $page ) { |
|
2575 update_post_meta( $request_id, '_export_data_raw', $export_data ); |
|
2576 } else { |
|
2577 $export_data = get_post_meta( $request_id, '_export_data_raw', true ); |
|
2578 } |
|
2579 |
|
2580 // Now, merge the data from the exporter response into the data we have accumulated already. |
|
2581 $export_data = array_merge( $export_data, $response['data'] ); |
|
2582 update_post_meta( $request_id, '_export_data_raw', $export_data ); |
|
2583 |
|
2584 // If we are not yet on the last page of the last exporter, return now. |
|
2585 /** This filter is documented in wp-admin/includes/ajax-actions.php */ |
|
2586 $exporters = apply_filters( 'wp_privacy_personal_data_exporters', array() ); |
|
2587 $is_last_exporter = $exporter_index === count( $exporters ); |
|
2588 $exporter_done = $response['done']; |
|
2589 if ( ! $is_last_exporter || ! $exporter_done ) { |
|
2590 return $response; |
|
2591 } |
|
2592 |
|
2593 // Last exporter, last page - let's prepare the export file. |
|
2594 |
|
2595 // First we need to re-organize the raw data hierarchically in groups and items. |
|
2596 $groups = array(); |
|
2597 foreach ( (array) $export_data as $export_datum ) { |
|
2598 $group_id = $export_datum['group_id']; |
|
2599 $group_label = $export_datum['group_label']; |
|
2600 if ( ! array_key_exists( $group_id, $groups ) ) { |
|
2601 $groups[ $group_id ] = array( |
|
2602 'group_label' => $group_label, |
|
2603 'items' => array(), |
|
2604 ); |
|
2605 } |
|
2606 |
|
2607 $item_id = $export_datum['item_id']; |
|
2608 if ( ! array_key_exists( $item_id, $groups[ $group_id ]['items'] ) ) { |
|
2609 $groups[ $group_id ]['items'][ $item_id ] = array(); |
|
2610 } |
|
2611 |
|
2612 $old_item_data = $groups[ $group_id ]['items'][ $item_id ]; |
|
2613 $merged_item_data = array_merge( $export_datum['data'], $old_item_data ); |
|
2614 $groups[ $group_id ]['items'][ $item_id ] = $merged_item_data; |
|
2615 } |
|
2616 |
|
2617 // Then save the grouped data into the request. |
|
2618 delete_post_meta( $request_id, '_export_data_raw' ); |
|
2619 update_post_meta( $request_id, '_export_data_grouped', $groups ); |
|
2620 |
|
2621 /** |
|
2622 * Generate the export file from the collected, grouped personal data. |
|
2623 * |
|
2624 * @since 4.9.6 |
|
2625 * |
|
2626 * @param int $request_id The export request ID. |
|
2627 */ |
|
2628 do_action( 'wp_privacy_personal_data_export_file', $request_id ); |
|
2629 |
|
2630 // Clear the grouped data now that it is no longer needed. |
|
2631 delete_post_meta( $request_id, '_export_data_grouped' ); |
|
2632 |
|
2633 // If the destination is email, send it now. |
|
2634 if ( $send_as_email ) { |
|
2635 $mail_success = wp_privacy_send_personal_data_export_email( $request_id ); |
|
2636 if ( is_wp_error( $mail_success ) ) { |
|
2637 wp_send_json_error( $mail_success->get_error_message() ); |
|
2638 } |
|
2639 |
|
2640 // Update the request to completed state when the export email is sent. |
|
2641 _wp_privacy_completed_request( $request_id ); |
|
2642 } else { |
|
2643 // Modify the response to include the URL of the export file so the browser can fetch it. |
|
2644 $export_file_url = get_post_meta( $request_id, '_export_file_url', true ); |
|
2645 if ( ! empty( $export_file_url ) ) { |
|
2646 $response['url'] = $export_file_url; |
|
2647 } |
|
2648 } |
|
2649 |
|
2650 return $response; |
|
2651 } |