]> scripts.mit.edu Git - autoinstalls/wordpress.git/blob - wp-admin/includes/file.php
Wordpress 2.9
[autoinstalls/wordpress.git] / wp-admin / includes / file.php
1 <?php
2 /**
3  * File contains all the administration image manipulation functions.
4  *
5  * @package WordPress
6  * @subpackage Administration
7  */
8
9 /** The descriptions for theme files. */
10 $wp_file_descriptions = array (
11         'index.php' => __( 'Main Index Template' ),
12         'style.css' => __( 'Stylesheet' ),
13         'rtl.css' => __( 'RTL Stylesheet' ),
14         'comments.php' => __( 'Comments' ),
15         'comments-popup.php' => __( 'Popup Comments' ),
16         'footer.php' => __( 'Footer' ),
17         'header.php' => __( 'Header' ),
18         'sidebar.php' => __( 'Sidebar' ),
19         'archive.php' => __( 'Archives' ),
20         'category.php' => __( 'Category Template' ),
21         'page.php' => __( 'Page Template' ),
22         'search.php' => __( 'Search Results' ),
23         'searchform.php' => __( 'Search Form' ),
24         'single.php' => __( 'Single Post' ),
25         '404.php' => __( '404 Template' ),
26         'link.php' => __( 'Links Template' ),
27         'functions.php' => __( 'Theme Functions' ),
28         'attachment.php' => __( 'Attachment Template' ),
29         'image.php' => __('Image Attachment Template'),
30         'video.php' => __('Video Attachment Template'),
31         'audio.php' => __('Audio Attachment Template'),
32         'application.php' => __('Application Attachment Template'),
33         'my-hacks.php' => __( 'my-hacks.php (legacy hacks support)' ),
34         '.htaccess' => __( '.htaccess (for rewrite rules )' ),
35         // Deprecated files
36         'wp-layout.css' => __( 'Stylesheet' ), 'wp-comments.php' => __( 'Comments Template' ), 'wp-comments-popup.php' => __( 'Popup Comments Template' ));
37
38 /**
39  * {@internal Missing Short Description}}
40  *
41  * @since unknown
42  *
43  * @param unknown_type $file
44  * @return unknown
45  */
46 function get_file_description( $file ) {
47         global $wp_file_descriptions;
48
49         if ( isset( $wp_file_descriptions[basename( $file )] ) ) {
50                 return $wp_file_descriptions[basename( $file )];
51         }
52         elseif ( file_exists( WP_CONTENT_DIR . $file ) && is_file( WP_CONTENT_DIR . $file ) ) {
53                 $template_data = implode( '', file( WP_CONTENT_DIR . $file ) );
54                 if ( preg_match( '|Template Name:(.*)$|mi', $template_data, $name ))
55                         return _cleanup_header_comment($name[1]) . ' Page Template';
56         }
57
58         return basename( $file );
59 }
60
61 /**
62  * {@internal Missing Short Description}}
63  *
64  * @since unknown
65  *
66  * @return unknown
67  */
68 function get_home_path() {
69         $home = get_option( 'home' );
70         $siteurl = get_option( 'siteurl' );
71         if ( $home != '' && $home != $siteurl ) {
72                 $wp_path_rel_to_home = str_replace($home, '', $siteurl); /* $siteurl - $home */
73                 $pos = strpos($_SERVER["SCRIPT_FILENAME"], $wp_path_rel_to_home);
74                 $home_path = substr($_SERVER["SCRIPT_FILENAME"], 0, $pos);
75                 $home_path = trailingslashit( $home_path );
76         } else {
77                 $home_path = ABSPATH;
78         }
79
80         return $home_path;
81 }
82
83 /**
84  * {@internal Missing Short Description}}
85  *
86  * @since unknown
87  *
88  * @param unknown_type $file
89  * @return unknown
90  */
91 function get_real_file_to_edit( $file ) {
92         if ('index.php' == $file || '.htaccess' == $file ) {
93                 $real_file = get_home_path() . $file;
94         } else {
95                 $real_file = WP_CONTENT_DIR . $file;
96         }
97
98         return $real_file;
99 }
100
101 /**
102  * Returns a listing of all files in the specified folder and all subdirectories up to 100 levels deep.
103  * The depth of the recursiveness can be controlled by the $levels param.
104  *
105  * @since 2.6.0
106  *
107  * @param string $folder Full path to folder
108  * @param int $levels (optional) Levels of folders to follow, Default: 100 (PHP Loop limit).
109  * @return bool|array False on failure, Else array of files
110  */
111 function list_files( $folder = '', $levels = 100 ) {
112         if( empty($folder) )
113                 return false;
114
115         if( ! $levels )
116                 return false;
117
118         $files = array();
119         if ( $dir = @opendir( $folder ) ) {
120                 while (($file = readdir( $dir ) ) !== false ) {
121                         if ( in_array($file, array('.', '..') ) )
122                                 continue;
123                         if ( is_dir( $folder . '/' . $file ) ) {
124                                 $files2 = list_files( $folder . '/' . $file, $levels - 1);
125                                 if( $files2 )
126                                         $files = array_merge($files, $files2 );
127                                 else
128                                         $files[] = $folder . '/' . $file . '/';
129                         } else {
130                                 $files[] = $folder . '/' . $file;
131                         }
132                 }
133         }
134         @closedir( $dir );
135         return $files;
136 }
137
138 /**
139  * Determines a writable directory for temporary files.
140  * Function's preference is to WP_CONTENT_DIR followed by the return value of <code>sys_get_temp_dir()</code>, before finally defaulting to /tmp/
141  *
142  * In the event that this function does not find a writable location, It may be overridden by the <code>WP_TEMP_DIR</code> constant in your <code>wp-config.php</code> file.
143  *
144  * @since 2.5.0
145  *
146  * @return string Writable temporary directory
147  */
148 function get_temp_dir() {
149         if ( defined('WP_TEMP_DIR') )
150                 return trailingslashit(WP_TEMP_DIR);
151
152         $temp = WP_CONTENT_DIR . '/';
153         if ( is_dir($temp) && is_writable($temp) )
154                 return $temp;
155
156         if  ( function_exists('sys_get_temp_dir') )
157                 return trailingslashit(sys_get_temp_dir());
158
159         return '/tmp/';
160 }
161
162 /**
163  * Returns a filename of a Temporary unique file.
164  * Please note that the calling function must unlink() this itself.
165  *
166  * The filename is based off the passed parameter or defaults to the current unix timestamp,
167  * while the directory can either be passed as well, or by leaving  it blank, default to a writable temporary directory.
168  *
169  * @since 2.6.0
170  *
171  * @param string $filename (optional) Filename to base the Unique file off
172  * @param string $dir (optional) Directory to store the file in
173  * @return string a writable filename
174  */
175 function wp_tempnam($filename = '', $dir = ''){
176         if ( empty($dir) )
177                 $dir = get_temp_dir();
178         $filename = basename($filename);
179         if ( empty($filename) )
180                 $filename = time();
181
182         $filename = preg_replace('|\..*$|', '.tmp', $filename);
183         $filename = $dir . wp_unique_filename($dir, $filename);
184         touch($filename);
185         return $filename;
186 }
187
188 /**
189  * {@internal Missing Short Description}}
190  *
191  * @since unknown
192  *
193  * @param unknown_type $file
194  * @param unknown_type $allowed_files
195  * @return unknown
196  */
197 function validate_file_to_edit( $file, $allowed_files = '' ) {
198         $code = validate_file( $file, $allowed_files );
199
200         if (!$code )
201                 return $file;
202
203         switch ( $code ) {
204                 case 1 :
205                         wp_die( __('Sorry, can&#8217;t edit files with &#8220;..&#8221; in the name. If you are trying to edit a file in your WordPress home directory, you can just type the name of the file in.' ));
206
207                 //case 2 :
208                 //      wp_die( __('Sorry, can&#8217;t call files with their real path.' ));
209
210                 case 3 :
211                         wp_die( __('Sorry, that file cannot be edited.' ));
212         }
213 }
214
215 /**
216  * {@internal Missing Short Description}}
217  *
218  * @since unknown
219  *
220  * @param array $file Reference to a single element of $_FILES. Call the function once for each uploaded file.
221  * @param array $overrides Optional. An associative array of names=>values to override default variables with extract( $overrides, EXTR_OVERWRITE ).
222  * @return array On success, returns an associative array of file attributes. On failure, returns $overrides['upload_error_handler'](&$file, $message ) or array( 'error'=>$message ).
223  */
224 function wp_handle_upload( &$file, $overrides = false, $time = null ) {
225         // The default error handler.
226         if (! function_exists( 'wp_handle_upload_error' ) ) {
227                 function wp_handle_upload_error( &$file, $message ) {
228                         return array( 'error'=>$message );
229                 }
230         }
231
232         $file = apply_filters( 'wp_handle_upload_prefilter', $file );
233
234         // You may define your own function and pass the name in $overrides['upload_error_handler']
235         $upload_error_handler = 'wp_handle_upload_error';
236
237         // You may have had one or more 'wp_handle_upload_prefilter' functions error out the file.  Handle that gracefully.
238         if ( isset( $file['error'] ) && !ctype_digit( $file['error'] ) && $file['error'] )
239                 return $upload_error_handler( $file, $file['error'] );
240
241         // You may define your own function and pass the name in $overrides['unique_filename_callback']
242         $unique_filename_callback = null;
243
244         // $_POST['action'] must be set and its value must equal $overrides['action'] or this:
245         $action = 'wp_handle_upload';
246
247         // Courtesy of php.net, the strings that describe the error indicated in $_FILES[{form field}]['error'].
248         $upload_error_strings = array( false,
249                 __( "The uploaded file exceeds the <code>upload_max_filesize</code> directive in <code>php.ini</code>." ),
250                 __( "The uploaded file exceeds the <em>MAX_FILE_SIZE</em> directive that was specified in the HTML form." ),
251                 __( "The uploaded file was only partially uploaded." ),
252                 __( "No file was uploaded." ),
253                 '',
254                 __( "Missing a temporary folder." ),
255                 __( "Failed to write file to disk." ),
256                 __( "File upload stopped by extension." ));
257
258         // All tests are on by default. Most can be turned off by $override[{test_name}] = false;
259         $test_form = true;
260         $test_size = true;
261
262         // If you override this, you must provide $ext and $type!!!!
263         $test_type = true;
264         $mimes = false;
265
266         // Install user overrides. Did we mention that this voids your warranty?
267         if ( is_array( $overrides ) )
268                 extract( $overrides, EXTR_OVERWRITE );
269
270         // A correct form post will pass this test.
271         if ( $test_form && (!isset( $_POST['action'] ) || ($_POST['action'] != $action ) ) )
272                 return $upload_error_handler( $file, __( 'Invalid form submission.' ));
273
274         // A successful upload will pass this test. It makes no sense to override this one.
275         if ( $file['error'] > 0 )
276                 return $upload_error_handler( $file, $upload_error_strings[$file['error']] );
277
278         // A non-empty file will pass this test.
279         if ( $test_size && !($file['size'] > 0 ) )
280                 return $upload_error_handler( $file, __( '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.' ));
281
282         // A properly uploaded file will pass this test. There should be no reason to override this one.
283         if (! @ is_uploaded_file( $file['tmp_name'] ) )
284                 return $upload_error_handler( $file, __( 'Specified file failed upload test.' ));
285
286         // A correct MIME type will pass this test. Override $mimes or use the upload_mimes filter.
287         if ( $test_type ) {
288                 $wp_filetype = wp_check_filetype( $file['name'], $mimes );
289
290                 extract( $wp_filetype );
291
292                 if ( ( !$type || !$ext ) && !current_user_can( 'unfiltered_upload' ) )
293                         return $upload_error_handler( $file, __( 'File type does not meet security guidelines. Try another.' ));
294
295                 if ( !$ext )
296                         $ext = ltrim(strrchr($file['name'], '.'), '.');
297
298                 if ( !$type )
299                         $type = $file['type'];
300         } else {
301                 $type = '';
302         }
303
304         // A writable uploads dir will pass this test. Again, there's no point overriding this one.
305         if ( ! ( ( $uploads = wp_upload_dir($time) ) && false === $uploads['error'] ) )
306                 return $upload_error_handler( $file, $uploads['error'] );
307
308         $filename = wp_unique_filename( $uploads['path'], $file['name'], $unique_filename_callback );
309
310         // Move the file to the uploads dir
311         $new_file = $uploads['path'] . "/$filename";
312         if ( false === @ move_uploaded_file( $file['tmp_name'], $new_file ) ) {
313                 return $upload_error_handler( $file, sprintf( __('The uploaded file could not be moved to %s.' ), $uploads['path'] ) );
314         }
315
316         // Set correct file permissions
317         $stat = stat( dirname( $new_file ));
318         $perms = $stat['mode'] & 0000666;
319         @ chmod( $new_file, $perms );
320
321         // Compute the URL
322         $url = $uploads['url'] . "/$filename";
323
324         return apply_filters( 'wp_handle_upload', array( 'file' => $new_file, 'url' => $url, 'type' => $type ) );
325 }
326
327 /**
328  * {@internal Missing Short Description}}
329  *
330  * Pass this function an array similar to that of a $_FILES POST array.
331  *
332  * @since unknown
333  *
334  * @param unknown_type $file
335  * @param unknown_type $overrides
336  * @return unknown
337  */
338 function wp_handle_sideload( &$file, $overrides = false ) {
339         // The default error handler.
340         if (! function_exists( 'wp_handle_upload_error' ) ) {
341                 function wp_handle_upload_error( &$file, $message ) {
342                         return array( 'error'=>$message );
343                 }
344         }
345
346         // You may define your own function and pass the name in $overrides['upload_error_handler']
347         $upload_error_handler = 'wp_handle_upload_error';
348
349         // You may define your own function and pass the name in $overrides['unique_filename_callback']
350         $unique_filename_callback = null;
351
352         // $_POST['action'] must be set and its value must equal $overrides['action'] or this:
353         $action = 'wp_handle_sideload';
354
355         // Courtesy of php.net, the strings that describe the error indicated in $_FILES[{form field}]['error'].
356         $upload_error_strings = array( false,
357                 __( "The uploaded file exceeds the <code>upload_max_filesize</code> directive in <code>php.ini</code>." ),
358                 __( "The uploaded file exceeds the <em>MAX_FILE_SIZE</em> directive that was specified in the HTML form." ),
359                 __( "The uploaded file was only partially uploaded." ),
360                 __( "No file was uploaded." ),
361                 '',
362                 __( "Missing a temporary folder." ),
363                 __( "Failed to write file to disk." ),
364                 __( "File upload stopped by extension." ));
365
366         // All tests are on by default. Most can be turned off by $override[{test_name}] = false;
367         $test_form = true;
368         $test_size = true;
369
370         // If you override this, you must provide $ext and $type!!!!
371         $test_type = true;
372         $mimes = false;
373
374         // Install user overrides. Did we mention that this voids your warranty?
375         if ( is_array( $overrides ) )
376                 extract( $overrides, EXTR_OVERWRITE );
377
378         // A correct form post will pass this test.
379         if ( $test_form && (!isset( $_POST['action'] ) || ($_POST['action'] != $action ) ) )
380                 return $upload_error_handler( $file, __( 'Invalid form submission.' ));
381
382         // A successful upload will pass this test. It makes no sense to override this one.
383         if ( $file['error'] > 0 )
384                 return $upload_error_handler( $file, $upload_error_strings[$file['error']] );
385
386         // A non-empty file will pass this test.
387         if ( $test_size && !(filesize($file['tmp_name']) > 0 ) )
388                 return $upload_error_handler( $file, __( 'File is empty. Please upload something more substantial. This error could also be caused by uploads being disabled in your php.ini.' ));
389
390         // A properly uploaded file will pass this test. There should be no reason to override this one.
391         if (! @ is_file( $file['tmp_name'] ) )
392                 return $upload_error_handler( $file, __( 'Specified file does not exist.' ));
393
394         // A correct MIME type will pass this test. Override $mimes or use the upload_mimes filter.
395         if ( $test_type ) {
396                 $wp_filetype = wp_check_filetype( $file['name'], $mimes );
397
398                 extract( $wp_filetype );
399
400                 if ( ( !$type || !$ext ) && !current_user_can( 'unfiltered_upload' ) )
401                         return $upload_error_handler( $file, __( 'File type does not meet security guidelines. Try another.' ));
402
403                 if ( !$ext )
404                         $ext = ltrim(strrchr($file['name'], '.'), '.');
405
406                 if ( !$type )
407                         $type = $file['type'];
408         }
409
410         // A writable uploads dir will pass this test. Again, there's no point overriding this one.
411         if ( ! ( ( $uploads = wp_upload_dir() ) && false === $uploads['error'] ) )
412                 return $upload_error_handler( $file, $uploads['error'] );
413
414         $filename = wp_unique_filename( $uploads['path'], $file['name'], $unique_filename_callback );
415
416         // Strip the query strings.
417         $filename = str_replace('?','-', $filename);
418         $filename = str_replace('&','-', $filename);
419
420         // Move the file to the uploads dir
421         $new_file = $uploads['path'] . "/$filename";
422         if ( false === @ rename( $file['tmp_name'], $new_file ) ) {
423                 return $upload_error_handler( $file, sprintf( __('The uploaded file could not be moved to %s.' ), $uploads['path'] ) );
424         }
425
426         // Set correct file permissions
427         $stat = stat( dirname( $new_file ));
428         $perms = $stat['mode'] & 0000666;
429         @ chmod( $new_file, $perms );
430
431         // Compute the URL
432         $url = $uploads['url'] . "/$filename";
433
434         $return = apply_filters( 'wp_handle_upload', array( 'file' => $new_file, 'url' => $url, 'type' => $type ) );
435
436         return $return;
437 }
438
439 /**
440  * Downloads a url to a local temporary file using the WordPress HTTP Class.
441  * Please note, That the calling function must unlink() the  file.
442  *
443  * @since 2.5.0
444  *
445  * @param string $url the URL of the file to download
446  * @return mixed WP_Error on failure, string Filename on success.
447  */
448 function download_url( $url ) {
449         //WARNING: The file is not automatically deleted, The script must unlink() the file.
450         if ( ! $url )
451                 return new WP_Error('http_no_url', __('Invalid URL Provided'));
452
453         $tmpfname = wp_tempnam($url);
454         if ( ! $tmpfname )
455                 return new WP_Error('http_no_file', __('Could not create Temporary file'));
456
457         $handle = @fopen($tmpfname, 'wb');
458         if ( ! $handle )
459                 return new WP_Error('http_no_file', __('Could not create Temporary file'));
460
461         $response = wp_remote_get($url, array('timeout' => 60));
462
463         if ( is_wp_error($response) ) {
464                 fclose($handle);
465                 unlink($tmpfname);
466                 return $response;
467         }
468
469         if ( $response['response']['code'] != '200' ){
470                 fclose($handle);
471                 unlink($tmpfname);
472                 return new WP_Error('http_404', trim($response['response']['message']));
473         }
474
475         fwrite($handle, $response['body']);
476         fclose($handle);
477
478         return $tmpfname;
479 }
480
481 /**
482  * Unzip's a specified ZIP file to a location on the Filesystem via the WordPress Filesystem Abstraction.
483  * Assumes that WP_Filesystem() has already been called and set up.
484  *
485  * Attempts to increase the PHP Memory limit to 256M before uncompressing,
486  * However, The most memory required shouldn't be much larger than the Archive itself.
487  *
488  * @since 2.5.0
489  *
490  * @param string $file Full path and filename of zip archive
491  * @param string $to Full path on the filesystem to extract archive to
492  * @return mixed WP_Error on failure, True on success
493  */
494 function unzip_file($file, $to) {
495         global $wp_filesystem;
496
497         if ( ! $wp_filesystem || !is_object($wp_filesystem) )
498                 return new WP_Error('fs_unavailable', __('Could not access filesystem.'));
499
500         // Unzip uses a lot of memory, but not this much hopefully
501         @ini_set('memory_limit', '256M');
502
503         $fs =& $wp_filesystem;
504
505         require_once(ABSPATH . 'wp-admin/includes/class-pclzip.php');
506
507         $archive = new PclZip($file);
508
509         // Is the archive valid?
510         if ( false == ($archive_files = $archive->extract(PCLZIP_OPT_EXTRACT_AS_STRING)) )
511                 return new WP_Error('incompatible_archive', __('Incompatible archive'), $archive->errorInfo(true));
512
513         if ( 0 == count($archive_files) )
514                 return new WP_Error('empty_archive', __('Empty archive'));
515
516         $path = explode('/', untrailingslashit($to));
517         for ( $i = count($path); $i > 0; $i-- ) { //>0 = first element is empty allways for paths starting with '/'
518                 $tmppath = implode('/', array_slice($path, 0, $i) );
519                 if ( $fs->is_dir($tmppath) ) { //Found the highest folder that exists, Create from here(ie +1)
520                         for ( $i = $i + 1; $i <= count($path); $i++ ) {
521                                 $tmppath = implode('/', array_slice($path, 0, $i) );
522                                 if ( ! $fs->mkdir($tmppath, FS_CHMOD_DIR) )
523                                         return new WP_Error('mkdir_failed', __('Could not create directory'), $tmppath);
524                         }
525                         break; //Exit main for loop
526                 }
527         }
528
529         $to = trailingslashit($to);
530         foreach ($archive_files as $file) {
531                 $path = $file['folder'] ? $file['filename'] : dirname($file['filename']);
532                 $path = explode('/', $path);
533                 for ( $i = count($path); $i >= 0; $i-- ) { //>=0 as the first element contains data
534                         if ( empty($path[$i]) )
535                                 continue;
536                         $tmppath = $to . implode('/', array_slice($path, 0, $i) );
537                         if ( $fs->is_dir($tmppath) ) {//Found the highest folder that exists, Create from here
538                                 for ( $i = $i + 1; $i <= count($path); $i++ ) { //< count() no file component please.
539                                         $tmppath = $to . implode('/', array_slice($path, 0, $i) );
540                                         if ( ! $fs->is_dir($tmppath) && ! $fs->mkdir($tmppath, FS_CHMOD_DIR) )
541                                                 return new WP_Error('mkdir_failed', __('Could not create directory'), $tmppath);
542                                 }
543                                 break; //Exit main for loop
544                         }
545                 }
546
547                 // We've made sure the folders are there, so let's extract the file now:
548                 if ( ! $file['folder'] ) {
549                         if ( !$fs->put_contents( $to . $file['filename'], $file['content']) )
550                                 return new WP_Error('copy_failed', __('Could not copy file'), $to . $file['filename']);
551                         $fs->chmod($to . $file['filename'], FS_CHMOD_FILE);
552                 }
553         }
554         return true;
555 }
556
557 /**
558  * Copies a directory from one location to another via the WordPress Filesystem Abstraction.
559  * Assumes that WP_Filesystem() has already been called and setup.
560  *
561  * @since 2.5.0
562  *
563  * @param string $from source directory
564  * @param string $to destination directory
565  * @return mixed WP_Error on failure, True on success.
566  */
567 function copy_dir($from, $to) {
568         global $wp_filesystem;
569
570         $dirlist = $wp_filesystem->dirlist($from);
571
572         $from = trailingslashit($from);
573         $to = trailingslashit($to);
574
575         foreach ( (array) $dirlist as $filename => $fileinfo ) {
576                 if ( 'f' == $fileinfo['type'] ) {
577                         if ( ! $wp_filesystem->copy($from . $filename, $to . $filename, true) ) {
578                                 // If copy failed, chmod file to 0644 and try again.
579                                 $wp_filesystem->chmod($to . $filename, 0644);
580                                 if ( ! $wp_filesystem->copy($from . $filename, $to . $filename, true) )
581                                         return new WP_Error('copy_failed', __('Could not copy file'), $to . $filename);
582                         }
583                         $wp_filesystem->chmod($to . $filename, FS_CHMOD_FILE);
584                 } elseif ( 'd' == $fileinfo['type'] ) {
585                         if ( !$wp_filesystem->is_dir($to . $filename) ) {
586                                 if ( !$wp_filesystem->mkdir($to . $filename, FS_CHMOD_DIR) )
587                                         return new WP_Error('mkdir_failed', __('Could not create directory'), $to . $filename);
588                         }
589                         $result = copy_dir($from . $filename, $to . $filename);
590                         if ( is_wp_error($result) )
591                                 return $result;
592                 }
593         }
594         return true;
595 }
596
597 /**
598  * Initialises and connects the WordPress Filesystem Abstraction classes.
599  * This function will include the chosen transport and attempt connecting.
600  *
601  * Plugins may add extra transports, And force WordPress to use them by returning the filename via the 'filesystem_method_file' filter.
602  *
603  * @since 2.5.0
604  *
605  * @param array $args (optional) Connection args, These are passed directly to the WP_Filesystem_*() classes.
606  * @param string $context (optional) Context for get_filesystem_method(), See function declaration for more information.
607  * @return boolean false on failure, true on success
608  */
609 function WP_Filesystem( $args = false, $context = false ) {
610         global $wp_filesystem;
611
612         require_once(ABSPATH . 'wp-admin/includes/class-wp-filesystem-base.php');
613
614         $method = get_filesystem_method($args, $context);
615
616         if ( ! $method )
617                 return false;
618
619         if ( ! class_exists("WP_Filesystem_$method") ) {
620                 $abstraction_file = apply_filters('filesystem_method_file', ABSPATH . 'wp-admin/includes/class-wp-filesystem-' . $method . '.php', $method);
621                 if( ! file_exists($abstraction_file) )
622                         return;
623
624                 require_once($abstraction_file);
625         }
626         $method = "WP_Filesystem_$method";
627
628         $wp_filesystem = new $method($args);
629
630         //Define the timeouts for the connections. Only available after the construct is called to allow for per-transport overriding of the default.
631         if ( ! defined('FS_CONNECT_TIMEOUT') )
632                 define('FS_CONNECT_TIMEOUT', 30);
633         if ( ! defined('FS_TIMEOUT') )
634                 define('FS_TIMEOUT', 30);
635
636         if ( is_wp_error($wp_filesystem->errors) && $wp_filesystem->errors->get_error_code() )
637                 return false;
638
639         if ( !$wp_filesystem->connect() )
640                 return false; //There was an erorr connecting to the server.
641
642         // Set the permission constants if not already set.
643         if ( ! defined('FS_CHMOD_DIR') )
644                 define('FS_CHMOD_DIR', 0755 );
645         if ( ! defined('FS_CHMOD_FILE') )
646                 define('FS_CHMOD_FILE', 0644 );
647
648         return true;
649 }
650
651 /**
652  * Determines which Filesystem Method to use.
653  * The priority of the Transports are: Direct, SSH2, FTP PHP Extension, FTP Sockets (Via Sockets class, or fsoxkopen())
654  *
655  * Note that the return value of this function can be overridden in 2 ways
656  *  - By defining FS_METHOD in your <code>wp-config.php</code> file
657  *  - By using the filesystem_method filter
658  * Valid values for these are: 'direct', 'ssh', 'ftpext' or 'ftpsockets'
659  * Plugins may also define a custom transport handler, See the WP_Filesystem function for more information.
660  *
661  * @since 2.5.0
662  *
663  * @param array $args Connection details.
664  * @param string $context Full path to the directory that is tested for being writable.
665  * @return string The transport to use, see description for valid return values.
666  */
667 function get_filesystem_method($args = array(), $context = false) {
668         $method = defined('FS_METHOD') ? FS_METHOD : false; //Please ensure that this is either 'direct', 'ssh', 'ftpext' or 'ftpsockets'
669
670         if( ! $method && function_exists('getmyuid') && function_exists('fileowner') ){
671                 if ( !$context )
672                         $context = WP_CONTENT_DIR;
673                 $context = trailingslashit($context);
674                 $temp_file_name = $context . 'temp-write-test-' . time();
675                 $temp_handle = @fopen($temp_file_name, 'w');
676                 if ( $temp_handle ) {
677                         if ( getmyuid() == @fileowner($temp_file_name) )
678                                 $method = 'direct';
679                         @fclose($temp_handle);
680                         @unlink($temp_file_name);
681                 }
682         }
683
684         if ( ! $method && isset($args['connection_type']) && 'ssh' == $args['connection_type'] && extension_loaded('ssh2') && function_exists('stream_get_contents') ) $method = 'ssh2';
685         if ( ! $method && extension_loaded('ftp') ) $method = 'ftpext';
686         if ( ! $method && ( extension_loaded('sockets') || function_exists('fsockopen') ) ) $method = 'ftpsockets'; //Sockets: Socket extension; PHP Mode: FSockopen / fwrite / fread
687         return apply_filters('filesystem_method', $method, $args);
688 }
689
690 /**
691  * Displays a form to the user to request for their FTP/SSH details in order to  connect to the filesystem.
692  * All chosen/entered details are saved, Excluding the Password.
693  *
694  * Hostnames may be in the form of hostname:portnumber (eg: wordpress.org:2467) to specify an alternate FTP/SSH port.
695  *
696  * Plugins may override this form by returning true|false via the <code>request_filesystem_credentials</code> filter.
697  *
698  * @since 2.5.0
699  *
700  * @param string $form_post the URL to post the form to
701  * @param string $type the chosen Filesystem method in use
702  * @param boolean $error if the current request has failed to connect
703  * @param string $context The directory which is needed access to, The write-test will be performed on  this directory by get_filesystem_method()
704  * @return boolean False on failure. True on success.
705  */
706 function request_filesystem_credentials($form_post, $type = '', $error = false, $context = false) {
707         $req_cred = apply_filters('request_filesystem_credentials', '', $form_post, $type, $error, $context);
708         if ( '' !== $req_cred )
709                 return $req_cred;
710
711         if ( empty($type) )
712                 $type = get_filesystem_method(array(), $context);
713
714         if ( 'direct' == $type )
715                 return true;
716
717         $credentials = get_option('ftp_credentials', array( 'hostname' => '', 'username' => ''));
718
719         // 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)
720         $credentials['hostname'] = defined('FTP_HOST') ? FTP_HOST : (!empty($_POST['hostname']) ? stripslashes($_POST['hostname']) : $credentials['hostname']);
721         $credentials['username'] = defined('FTP_USER') ? FTP_USER : (!empty($_POST['username']) ? stripslashes($_POST['username']) : $credentials['username']);
722         $credentials['password'] = defined('FTP_PASS') ? FTP_PASS : (!empty($_POST['password']) ? stripslashes($_POST['password']) : '');
723
724         // Check to see if we are setting the public/private keys for ssh
725         $credentials['public_key'] = defined('FTP_PUBKEY') ? FTP_PUBKEY : (!empty($_POST['public_key']) ? stripslashes($_POST['public_key']) : '');
726         $credentials['private_key'] = defined('FTP_PRIKEY') ? FTP_PRIKEY : (!empty($_POST['private_key']) ? stripslashes($_POST['private_key']) : '');
727
728         //sanitize the hostname, Some people might pass in odd-data:
729         $credentials['hostname'] = preg_replace('|\w+://|', '', $credentials['hostname']); //Strip any schemes off
730
731         if ( strpos($credentials['hostname'], ':') ) {
732                 list( $credentials['hostname'], $credentials['port'] ) = explode(':', $credentials['hostname'], 2);
733                 if ( ! is_numeric($credentials['port']) )
734                         unset($credentials['port']);
735         } else {
736                 unset($credentials['port']);
737         }
738
739         if ( (defined('FTP_SSH') && FTP_SSH) || (defined('FS_METHOD') && 'ssh' == FS_METHOD) )
740                 $credentials['connection_type'] = 'ssh';
741         else if ( (defined('FTP_SSL') && FTP_SSL) && 'ftpext' == $type ) //Only the FTP Extension understands SSL
742                 $credentials['connection_type'] = 'ftps';
743         else if ( !empty($_POST['connection_type']) )
744                 $credentials['connection_type'] = stripslashes($_POST['connection_type']);
745         else if ( !isset($credentials['connection_type']) ) //All else fails (And its not defaulted to something else saved), Default to FTP
746                 $credentials['connection_type'] = 'ftp';
747
748         if ( ! $error &&
749                         (
750                                 ( !empty($credentials['password']) && !empty($credentials['username']) && !empty($credentials['hostname']) ) ||
751                                 ( 'ssh' == $credentials['connection_type'] && !empty($credentials['public_key']) && !empty($credentials['private_key']) )
752                         ) ) {
753                 $stored_credentials = $credentials;
754                 if ( !empty($stored_credentials['port']) ) //save port as part of hostname to simplify above code.
755                         $stored_credentials['hostname'] .= ':' . $stored_credentials['port'];
756
757                 unset($stored_credentials['password'], $stored_credentials['port'], $stored_credentials['private_key'], $stored_credentials['public_key']);
758                 update_option('ftp_credentials', $stored_credentials);
759                 return $credentials;
760         }
761         $hostname = '';
762         $username = '';
763         $password = '';
764         $connection_type = '';
765         if ( !empty($credentials) )
766                 extract($credentials, EXTR_OVERWRITE);
767         if ( $error ) {
768                 $error_string = __('<strong>Error:</strong> There was an error connecting to the server, Please verify the settings are correct.');
769                 if ( is_wp_error($error) )
770                         $error_string = $error->get_error_message();
771                 echo '<div id="message" class="error"><p>' . $error_string . '</p></div>';
772         }
773
774         $types = array();
775         if ( extension_loaded('ftp') || extension_loaded('sockets') || function_exists('fsockopen') )
776                 $types[ 'ftp' ] = __('FTP');
777         if ( extension_loaded('ftp') ) //Only this supports FTPS
778                 $types[ 'ftps' ] = __('FTPS (SSL)');
779         if ( extension_loaded('ssh2') && function_exists('stream_get_contents') )
780                 $types[ 'ssh' ] = __('SSH2');
781
782         $types = apply_filters('fs_ftp_connection_types', $types, $credentials, $type, $error, $context);
783
784 ?>
785 <script type="text/javascript">
786 <!--
787 jQuery(function($){
788         jQuery("#ssh").click(function () {
789                 jQuery("#ssh_keys").show();
790         });
791         jQuery("#ftp, #ftps").click(function () {
792                 jQuery("#ssh_keys").hide();
793         });
794         jQuery('form input[value=""]:first').focus();
795 });
796 -->
797 </script>
798 <form action="<?php echo $form_post ?>" method="post">
799 <div class="wrap">
800 <?php screen_icon(); ?>
801 <h2><?php _e('Connection Information') ?></h2>
802 <p><?php _e('To perform the requested action, connection information is required.') ?></p>
803
804 <table class="form-table">
805 <tr valign="top">
806 <th scope="row"><label for="hostname"><?php _e('Hostname') ?></label></th>
807 <td><input name="hostname" type="text" id="hostname" value="<?php echo esc_attr($hostname); if ( !empty($port) ) echo ":$port"; ?>"<?php if( defined('FTP_HOST') ) echo ' disabled="disabled"' ?> size="40" /></td>
808 </tr>
809
810 <tr valign="top">
811 <th scope="row"><label for="username"><?php _e('Username') ?></label></th>
812 <td><input name="username" type="text" id="username" value="<?php echo esc_attr($username) ?>"<?php if( defined('FTP_USER') ) echo ' disabled="disabled"' ?> size="40" /></td>
813 </tr>
814
815 <tr valign="top">
816 <th scope="row"><label for="password"><?php _e('Password') ?></label></th>
817 <td><input name="password" type="password" id="password" value="<?php if ( defined('FTP_PASS') ) echo '*****'; ?>"<?php if ( defined('FTP_PASS') ) echo ' disabled="disabled"' ?> size="40" /></td>
818 </tr>
819
820 <?php if ( isset($types['ssh']) ) : ?>
821 <tr id="ssh_keys" valign="top" style="<?php if ( 'ssh' != $connection_type ) echo 'display:none' ?>">
822 <th scope="row"><?php _e('Authentication Keys') ?>
823 <div class="key-labels textright">
824 <label for="public_key"><?php _e('Public Key:') ?></label ><br />
825 <label for="private_key"><?php _e('Private Key:') ?></label>
826 </div></th>
827 <td><br /><input name="public_key" type="text" id="public_key" value="<?php echo esc_attr($public_key) ?>"<?php if( defined('FTP_PUBKEY') ) echo ' disabled="disabled"' ?> size="40" /><br /><input name="private_key" type="text" id="private_key" value="<?php echo esc_attr($private_key) ?>"<?php if( defined('FTP_PRIKEY') ) echo ' disabled="disabled"' ?> size="40" />
828 <div><?php _e('Enter the location on the server where the keys are located. If a passphrase is needed, enter that in the password field above.') ?></div></td>
829 </tr>
830 <?php endif; ?>
831
832 <tr valign="top">
833 <th scope="row"><?php _e('Connection Type') ?></th>
834 <td>
835 <fieldset><legend class="screen-reader-text"><span><?php _e('Connection Type') ?></span></legend>
836 <?php
837
838         $disabled = (defined('FTP_SSL') && FTP_SSL) || (defined('FTP_SSH') && FTP_SSH) ? ' disabled="disabled"' : '';
839
840         foreach ( $types as $name => $text ) : ?>
841         <label for="<?php echo esc_attr($name) ?>">
842                 <input type="radio" name="connection_type" id="<?php echo esc_attr($name) ?>" value="<?php echo esc_attr($name) ?>" <?php checked($name, $connection_type); echo $disabled; ?>/>
843                 <?php echo $text ?>
844         </label>
845         <?php endforeach; ?>
846 </fieldset>
847 </td>
848 </tr>
849 </table>
850
851 <?php if ( isset( $_POST['version'] ) ) : ?>
852 <input type="hidden" name="version" value="<?php echo esc_attr(stripslashes($_POST['version'])) ?>" />
853 <?php endif; ?>
854 <?php if ( isset( $_POST['locale'] ) ) : ?>
855 <input type="hidden" name="locale" value="<?php echo esc_attr(stripslashes($_POST['locale'])) ?>" />
856 <?php endif; ?>
857 <p class="submit">
858 <input id="upgrade" name="upgrade" type="submit" class="button" value="<?php esc_attr_e('Proceed'); ?>" />
859 </p>
860 </div>
861 </form>
862 <?php
863         return false;
864 }
865
866 ?>