PageRenderTime 9ms CodeModel.GetById 191ms app.highlight 125ms RepoModel.GetById 36ms app.codeStats 1ms

/wp-admin/includes/file.php

https://github.com/dedavidd/piratenpartij.nl
PHP | 1217 lines | 686 code | 168 blank | 363 comment | 233 complexity | 93fa252e00f800f010ef4b005b57afb2 MD5 | raw file
   1<?php
   2/**
   3 * Functions for reading, writing, modifying, and deleting files on the file system.
   4 * Includes functionality for theme-specific files as well as operations for uploading,
   5 * archiving, and rendering output when necessary.
   6 *
   7 * @package WordPress
   8 * @subpackage Administration
   9 */
  10
  11/** The descriptions for theme files. */
  12$wp_file_descriptions = array(
  13	'index.php' => __( 'Main Index Template' ),
  14	'style.css' => __( 'Stylesheet' ),
  15	'editor-style.css' => __( 'Visual Editor Stylesheet' ),
  16	'editor-style-rtl.css' => __( 'Visual Editor RTL Stylesheet' ),
  17	'rtl.css' => __( 'RTL Stylesheet' ),
  18	'comments.php' => __( 'Comments' ),
  19	'comments-popup.php' => __( 'Popup Comments' ),
  20	'footer.php' => __( 'Footer' ),
  21	'header.php' => __( 'Header' ),
  22	'sidebar.php' => __( 'Sidebar' ),
  23	'archive.php' => __( 'Archives' ),
  24	'author.php' => __( 'Author Template' ),
  25	'tag.php' => __( 'Tag Template' ),
  26	'category.php' => __( 'Category Template' ),
  27	'page.php' => __( 'Page Template' ),
  28	'search.php' => __( 'Search Results' ),
  29	'searchform.php' => __( 'Search Form' ),
  30	'single.php' => __( 'Single Post' ),
  31	'404.php' => __( '404 Template' ),
  32	'link.php' => __( 'Links Template' ),
  33	'functions.php' => __( 'Theme Functions' ),
  34	'attachment.php' => __( 'Attachment Template' ),
  35	'image.php' => __('Image Attachment Template'),
  36	'video.php' => __('Video Attachment Template'),
  37	'audio.php' => __('Audio Attachment Template'),
  38	'application.php' => __('Application Attachment Template'),
  39	'my-hacks.php' => __( 'my-hacks.php (legacy hacks support)' ),
  40	'.htaccess' => __( '.htaccess (for rewrite rules )' ),
  41	// Deprecated files
  42	'wp-layout.css' => __( 'Stylesheet' ),
  43	'wp-comments.php' => __( 'Comments Template' ),
  44	'wp-comments-popup.php' => __( 'Popup Comments Template' ),
  45);
  46
  47/**
  48 * Get the description for standard WordPress theme files and other various standard
  49 * WordPress files
  50 *
  51 * @since 1.5.0
  52 *
  53 * @uses _cleanup_header_comment
  54 * @uses $wp_file_descriptions
  55 * @param string $file Filesystem path or filename
  56 * @return string Description of file from $wp_file_descriptions or basename of $file if description doesn't exist
  57 */
  58function get_file_description( $file ) {
  59	global $wp_file_descriptions;
  60
  61	if ( isset( $wp_file_descriptions[basename( $file )] ) ) {
  62		return $wp_file_descriptions[basename( $file )];
  63	}
  64	elseif ( file_exists( $file ) && is_file( $file ) ) {
  65		$template_data = implode( '', file( $file ) );
  66		if ( preg_match( '|Template Name:(.*)$|mi', $template_data, $name ))
  67			return sprintf( __( '%s Page Template' ), _cleanup_header_comment($name[1]) );
  68	}
  69
  70	return trim( basename( $file ) );
  71}
  72
  73/**
  74 * Get the absolute filesystem path to the root of the WordPress installation
  75 *
  76 * @since 1.5.0
  77 *
  78 * @uses get_option
  79 * @return string Full filesystem path to the root of the WordPress installation
  80 */
  81function get_home_path() {
  82	$home    = set_url_scheme( get_option( 'home' ), 'http' );
  83	$siteurl = set_url_scheme( get_option( 'siteurl' ), 'http' );
  84	if ( ! empty( $home ) && 0 !== strcasecmp( $home, $siteurl ) ) {
  85		$wp_path_rel_to_home = str_ireplace( $home, '', $siteurl ); /* $siteurl - $home */
  86		$pos = strripos( str_replace( '\\', '/', $_SERVER['SCRIPT_FILENAME'] ), trailingslashit( $wp_path_rel_to_home ) );
  87		$home_path = substr( $_SERVER['SCRIPT_FILENAME'], 0, $pos );
  88		$home_path = trailingslashit( $home_path );
  89	} else {
  90		$home_path = ABSPATH;
  91	}
  92
  93	return str_replace( '\\', '/', $home_path );
  94}
  95
  96/**
  97 * Returns a listing of all files in the specified folder and all subdirectories up to 100 levels deep.
  98 * The depth of the recursiveness can be controlled by the $levels param.
  99 *
 100 * @since 2.6.0
 101 *
 102 * @param string $folder Full path to folder
 103 * @param int $levels (optional) Levels of folders to follow, Default: 100 (PHP Loop limit).
 104 * @return bool|array False on failure, Else array of files
 105 */
 106function list_files( $folder = '', $levels = 100 ) {
 107	if ( empty($folder) )
 108		return false;
 109
 110	if ( ! $levels )
 111		return false;
 112
 113	$files = array();
 114	if ( $dir = @opendir( $folder ) ) {
 115		while (($file = readdir( $dir ) ) !== false ) {
 116			if ( in_array($file, array('.', '..') ) )
 117				continue;
 118			if ( is_dir( $folder . '/' . $file ) ) {
 119				$files2 = list_files( $folder . '/' . $file, $levels - 1);
 120				if ( $files2 )
 121					$files = array_merge($files, $files2 );
 122				else
 123					$files[] = $folder . '/' . $file . '/';
 124			} else {
 125				$files[] = $folder . '/' . $file;
 126			}
 127		}
 128	}
 129	@closedir( $dir );
 130	return $files;
 131}
 132
 133/**
 134 * Returns a filename of a Temporary unique file.
 135 * Please note that the calling function must unlink() this itself.
 136 *
 137 * The filename is based off the passed parameter or defaults to the current unix timestamp,
 138 * while the directory can either be passed as well, or by leaving it blank, default to a writable temporary directory.
 139 *
 140 * @since 2.6.0
 141 *
 142 * @param string $filename (optional) Filename to base the Unique file off
 143 * @param string $dir (optional) Directory to store the file in
 144 * @return string a writable filename
 145 */
 146function wp_tempnam($filename = '', $dir = '') {
 147	if ( empty($dir) )
 148		$dir = get_temp_dir();
 149	$filename = basename($filename);
 150	if ( empty($filename) )
 151		$filename = time();
 152
 153	$filename = preg_replace('|\..*$|', '.tmp', $filename);
 154	$filename = $dir . wp_unique_filename($dir, $filename);
 155	touch($filename);
 156	return $filename;
 157}
 158
 159/**
 160 * Make sure that the file that was requested to edit, is allowed to be edited
 161 *
 162 * Function will die if if you are not allowed to edit the file
 163 *
 164 * @since 1.5.0
 165 *
 166 * @uses wp_die
 167 * @uses validate_file
 168 * @param string $file file the users is attempting to edit
 169 * @param array $allowed_files Array of allowed files to edit, $file must match an entry exactly
 170 * @return null
 171 */
 172function validate_file_to_edit( $file, $allowed_files = '' ) {
 173	$code = validate_file( $file, $allowed_files );
 174
 175	if (!$code )
 176		return $file;
 177
 178	switch ( $code ) {
 179		case 1 :
 180			wp_die( __( 'Sorry, that file cannot be edited.' ) );
 181
 182		//case 2 :
 183		//	wp_die( __('Sorry, can&#8217;t call files with their real path.' ));
 184
 185		case 3 :
 186			wp_die( __( 'Sorry, that file cannot be edited.' ) );
 187	}
 188}
 189
 190/**
 191 * Handle PHP uploads in WordPress, sanitizing file names, checking extensions for mime type,
 192 * and moving the file to the appropriate directory within the uploads directory.
 193 *
 194 * @since 2.0.0
 195 *
 196 * @uses wp_handle_upload_error
 197 * @uses is_multisite
 198 * @uses wp_check_filetype_and_ext
 199 * @uses current_user_can
 200 * @uses wp_upload_dir
 201 * @uses wp_unique_filename
 202 * @uses delete_transient
 203 * @param array $file Reference to a single element of $_FILES. Call the function once for each uploaded file.
 204 * @param array $overrides Optional. An associative array of names=>values to override default variables.
 205 * @param string $time Optional. Time formatted in 'yyyy/mm'.
 206 * @return array On success, returns an associative array of file attributes. On failure, returns $overrides['upload_error_handler'](&$file, $message ) or array( 'error'=>$message ).
 207 */
 208function wp_handle_upload( &$file, $overrides = false, $time = null ) {
 209	// The default error handler.
 210	if ( ! function_exists( 'wp_handle_upload_error' ) ) {
 211		function wp_handle_upload_error( &$file, $message ) {
 212			return array( 'error'=>$message );
 213		}
 214	}
 215
 216	/**
 217	 * Filter data for the current file to upload.
 218	 *
 219	 * @since 2.9.0
 220	 *
 221	 * @param array $file An array of data for a single file.
 222	 */
 223	$file = apply_filters( 'wp_handle_upload_prefilter', $file );
 224
 225	// You may define your own function and pass the name in $overrides['upload_error_handler']
 226	$upload_error_handler = 'wp_handle_upload_error';
 227	if ( isset( $overrides['upload_error_handler'] ) ) {
 228		$upload_error_handler = $overrides['upload_error_handler'];
 229	}
 230
 231	// You may have had one or more 'wp_handle_upload_prefilter' functions error out the file. Handle that gracefully.
 232	if ( isset( $file['error'] ) && !is_numeric( $file['error'] ) && $file['error'] )
 233		return $upload_error_handler( $file, $file['error'] );
 234
 235	// Install user overrides. Did we mention that this voids your warranty?
 236
 237	// You may define your own function and pass the name in $overrides['unique_filename_callback']
 238	$unique_filename_callback = null;
 239	if ( isset( $overrides['unique_filename_callback'] ) ) {
 240		$unique_filename_callback = $overrides['unique_filename_callback'];
 241	}
 242
 243	// $_POST['action'] must be set and its value must equal $overrides['action'] or this:
 244	$action = 'wp_handle_upload';
 245	if ( isset( $overrides['action'] ) ) {
 246		$action = $overrides['action'];
 247	}
 248
 249	// Courtesy of php.net, the strings that describe the error indicated in $_FILES[{form field}]['error'].
 250	$upload_error_strings = array( false,
 251		__( "The uploaded file exceeds the upload_max_filesize directive in php.ini." ),
 252		__( "The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form." ),
 253		__( "The uploaded file was only partially uploaded." ),
 254		__( "No file was uploaded." ),
 255		'',
 256		__( "Missing a temporary folder." ),
 257		__( "Failed to write file to disk." ),
 258		__( "File upload stopped by extension." ));
 259
 260	// this may not have orignially been intended to be overrideable, but historically has been
 261	if ( isset( $overrides['upload_error_strings'] ) ) {
 262		$upload_error_strings = $overrides['upload_error_strings'];
 263	}
 264
 265	// All tests are on by default. Most can be turned off by $overrides[{test_name}] = false;
 266	$test_form = isset( $overrides['test_form'] ) ? $overrides['test_form'] : true;
 267	$test_size = isset( $overrides['test_size'] ) ? $overrides['test_size'] : true;
 268	$test_upload = isset( $overrides['test_upload'] ) ? $overrides['test_upload'] : true;
 269
 270	// If you override this, you must provide $ext and $type!!!!
 271	$test_type = isset( $overrides['test_type'] ) ? $overrides['test_type'] : true;
 272	$mimes = isset( $overrides['mimes'] ) ? $overrides['mimes'] : false;
 273
 274	// A correct form post will pass this test.
 275	if ( $test_form && ( ! isset( $_POST['action'] ) || ($_POST['action'] != $action ) ) ) {
 276		return call_user_func( $upload_error_handler, $file, __( 'Invalid form submission.' ) );
 277	}
 278	// A successful upload will pass this test. It makes no sense to override this one.
 279	if ( isset( $file['error'] ) && $file['error'] > 0 ) {
 280		return call_user_func( $upload_error_handler, $file, $upload_error_strings[ $file['error'] ] );
 281	}
 282
 283	// A non-empty file will pass this test.
 284	if ( $test_size && !($file['size'] > 0 ) ) {
 285		if ( is_multisite() )
 286			$error_msg = __( 'File is empty. Please upload something more substantial.' );
 287		else
 288			$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.' );
 289		return call_user_func($upload_error_handler, $file, $error_msg);
 290	}
 291
 292	// A properly uploaded file will pass this test. There should be no reason to override this one.
 293	if ( $test_upload && ! @ is_uploaded_file( $file['tmp_name'] ) )
 294		return call_user_func($upload_error_handler, $file, __( 'Specified file failed upload test.' ));
 295
 296	// A correct MIME type will pass this test. Override $mimes or use the upload_mimes filter.
 297	if ( $test_type ) {
 298		$wp_filetype = wp_check_filetype_and_ext( $file['tmp_name'], $file['name'], $mimes );
 299		$ext = empty( $wp_filetype['ext'] ) ? '' : $wp_filetype['ext'];
 300		$type = empty( $wp_filetype['type'] ) ? '' : $wp_filetype['type'];
 301		$proper_filename = empty( $wp_filetype['proper_filename'] ) ? '' : $wp_filetype['proper_filename'];
 302
 303		// Check to see if wp_check_filetype_and_ext() determined the filename was incorrect
 304		if ( $proper_filename ) {
 305			$file['name'] = $proper_filename;
 306		}
 307		if ( ( ! $type || !$ext ) && ! current_user_can( 'unfiltered_upload' ) ) {
 308			return call_user_func( $upload_error_handler, $file, __( 'Sorry, this file type is not permitted for security reasons.' ) );
 309		}
 310		if ( ! $type ) {
 311			$type = $file['type'];
 312		}
 313	} else {
 314		$type = '';
 315	}
 316
 317	// A writable uploads dir will pass this test. Again, there's no point overriding this one.
 318	if ( ! ( ( $uploads = wp_upload_dir($time) ) && false === $uploads['error'] ) )
 319		return call_user_func($upload_error_handler, $file, $uploads['error'] );
 320
 321	$filename = wp_unique_filename( $uploads['path'], $file['name'], $unique_filename_callback );
 322
 323	// Move the file to the uploads dir
 324	$new_file = $uploads['path'] . "/$filename";
 325	if ( false === @ move_uploaded_file( $file['tmp_name'], $new_file ) ) {
 326		if ( 0 === strpos( $uploads['basedir'], ABSPATH ) )
 327			$error_path = str_replace( ABSPATH, '', $uploads['basedir'] ) . $uploads['subdir'];
 328		else
 329			$error_path = basename( $uploads['basedir'] ) . $uploads['subdir'];
 330
 331		return $upload_error_handler( $file, sprintf( __('The uploaded file could not be moved to %s.' ), $error_path ) );
 332	}
 333
 334	// Set correct file permissions
 335	$stat = stat( dirname( $new_file ));
 336	$perms = $stat['mode'] & 0000666;
 337	@ chmod( $new_file, $perms );
 338
 339	// Compute the URL
 340	$url = $uploads['url'] . "/$filename";
 341
 342	if ( is_multisite() )
 343		delete_transient( 'dirsize_cache' );
 344
 345	/**
 346	 * Filter the data array for the uploaded file.
 347	 *
 348	 * @since 2.1.0
 349	 *
 350	 * @param array  $upload {
 351	 *     Array of upload data.
 352	 *
 353	 *     @type string $file Filename of the newly-uploaded file.
 354	 *     @type string $url  URL of the uploaded file.
 355	 *     @type string $type File type.
 356	 * }
 357	 * @param string $context The type of upload action. Accepts 'upload' or 'sideload'.
 358	 */
 359	return apply_filters( 'wp_handle_upload', array( 'file' => $new_file, 'url' => $url, 'type' => $type ), 'upload' );
 360}
 361
 362/**
 363 * Handle sideloads, which is the process of retrieving a media item from another server instead of
 364 * a traditional media upload. This process involves sanitizing the filename, checking extensions
 365 * for mime type, and moving the file to the appropriate directory within the uploads directory.
 366 *
 367 * @since 2.6.0
 368 *
 369 * @uses wp_handle_upload_error
 370 * @uses wp_check_filetype_and_ext
 371 * @uses current_user_can
 372 * @uses wp_upload_dir
 373 * @uses wp_unique_filename
 374 * @param array $file an array similar to that of a PHP $_FILES POST array
 375 * @param array $overrides Optional. An associative array of names=>values to override default variables.
 376 * @param string $time Optional. Time formatted in 'yyyy/mm'.
 377 * @return array On success, returns an associative array of file attributes. On failure, returns $overrides['upload_error_handler'](&$file, $message ) or array( 'error'=>$message ).
 378 */
 379function wp_handle_sideload( &$file, $overrides = false, $time = null ) {
 380	// The default error handler.
 381	if (! function_exists( 'wp_handle_upload_error' ) ) {
 382		function wp_handle_upload_error( &$file, $message ) {
 383			return array( 'error'=>$message );
 384		}
 385	}
 386
 387	// Install user overrides. Did we mention that this voids your warranty?
 388
 389	// You may define your own function and pass the name in $overrides['upload_error_handler']
 390	$upload_error_handler = 'wp_handle_upload_error';
 391	if ( isset( $overrides['upload_error_handler'] ) ) {
 392		$upload_error_handler = $overrides['upload_error_handler'];
 393	}
 394
 395	// You may define your own function and pass the name in $overrides['unique_filename_callback']
 396	$unique_filename_callback = null;
 397	if ( isset( $overrides['unique_filename_callback'] ) ) {
 398		$unique_filename_callback = $overrides['unique_filename_callback'];
 399	}
 400
 401	// $_POST['action'] must be set and its value must equal $overrides['action'] or this:
 402	$action = 'wp_handle_sideload';
 403	if ( isset( $overrides['action'] ) ) {
 404		$action = $overrides['action'];
 405	}
 406
 407	// Courtesy of php.net, the strings that describe the error indicated in $_FILES[{form field}]['error'].
 408	$upload_error_strings = array( false,
 409		__( "The uploaded file exceeds the <code>upload_max_filesize</code> directive in <code>php.ini</code>." ),
 410		__( "The uploaded file exceeds the <em>MAX_FILE_SIZE</em> directive that was specified in the HTML form." ),
 411		__( "The uploaded file was only partially uploaded." ),
 412		__( "No file was uploaded." ),
 413		'',
 414		__( "Missing a temporary folder." ),
 415		__( "Failed to write file to disk." ),
 416		__( "File upload stopped by extension." ));
 417
 418	// this may not have orignially been intended to be overrideable, but historically has been
 419	if ( isset( $overrides['upload_error_strings'] ) ) {
 420		$upload_error_strings = $overrides['upload_error_strings'];
 421	}
 422
 423	// All tests are on by default. Most can be turned off by $overrides[{test_name}] = false;
 424	$test_form = isset( $overrides['test_form'] ) ? $overrides['test_form'] : true;
 425	$test_size = isset( $overrides['test_size'] ) ? $overrides['test_size'] : true;
 426
 427	// If you override this, you must provide $ext and $type!!!!
 428	$test_type = isset( $overrides['test_type'] ) ? $overrides['test_type'] : true;
 429	$mimes = isset( $overrides['mimes'] ) ? $overrides['mimes'] : false;
 430
 431	// A correct form post will pass this test.
 432	if ( $test_form && (!isset( $_POST['action'] ) || ($_POST['action'] != $action ) ) )
 433		return $upload_error_handler( $file, __( 'Invalid form submission.' ));
 434
 435	// A successful upload will pass this test. It makes no sense to override this one.
 436	if ( ! empty( $file['error'] ) )
 437		return $upload_error_handler( $file, $upload_error_strings[$file['error']] );
 438
 439	// A non-empty file will pass this test.
 440	if ( $test_size && !(filesize($file['tmp_name']) > 0 ) )
 441		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.' ));
 442
 443	// A properly uploaded file will pass this test. There should be no reason to override this one.
 444	if (! @ is_file( $file['tmp_name'] ) )
 445		return $upload_error_handler( $file, __( 'Specified file does not exist.' ));
 446
 447	// A correct MIME type will pass this test. Override $mimes or use the upload_mimes filter.
 448	if ( $test_type ) {
 449		$wp_filetype = wp_check_filetype_and_ext( $file['tmp_name'], $file['name'], $mimes );
 450		$ext = empty( $wp_filetype['ext'] ) ? '' : $wp_filetype['ext'];
 451		$type = empty( $wp_filetype['type'] ) ? '' : $wp_filetype['type'];
 452		$proper_filename = empty( $wp_filetype['proper_filename'] ) ? '' : $wp_filetype['proper_filename'];
 453
 454		// Check to see if wp_check_filetype_and_ext() determined the filename was incorrect
 455		if ( $proper_filename ) {
 456			$file['name'] = $proper_filename;
 457		}
 458		if ( ( ! $type || ! $ext ) && ! current_user_can( 'unfiltered_upload' ) ) {
 459			return $upload_error_handler( $file, __( 'Sorry, this file type is not permitted for security reasons.' ) );
 460		}
 461		if ( ! $type ) {
 462			$type = $file['type'];
 463		}
 464	} else {
 465		$type = '';
 466	}
 467
 468	// A writable uploads dir will pass this test. Again, there's no point overriding this one.
 469	if ( ! ( ( $uploads = wp_upload_dir( $time ) ) && false === $uploads['error'] ) )
 470		return $upload_error_handler( $file, $uploads['error'] );
 471
 472	$filename = wp_unique_filename( $uploads['path'], $file['name'], $unique_filename_callback );
 473
 474	// Strip the query strings.
 475	$filename = str_replace('?','-', $filename);
 476	$filename = str_replace('&','-', $filename);
 477
 478	// Move the file to the uploads dir
 479	$new_file = $uploads['path'] . "/$filename";
 480	if ( false === @ rename( $file['tmp_name'], $new_file ) ) {
 481		if ( 0 === strpos( $uploads['basedir'], ABSPATH ) )
 482			$error_path = str_replace( ABSPATH, '', $uploads['basedir'] ) . $uploads['subdir'];
 483		else
 484			$error_path = basename( $uploads['basedir'] ) . $uploads['subdir'];
 485		return $upload_error_handler( $file, sprintf( __('The uploaded file could not be moved to %s.' ), $error_path ) );
 486	}
 487
 488	// Set correct file permissions
 489	$stat = stat( dirname( $new_file ));
 490	$perms = $stat['mode'] & 0000666;
 491	@ chmod( $new_file, $perms );
 492
 493	// Compute the URL
 494	$url = $uploads['url'] . "/$filename";
 495
 496	/** This filter is documented in wp-admin/includes/file.php */
 497	$return = apply_filters( 'wp_handle_upload', array( 'file' => $new_file, 'url' => $url, 'type' => $type ), 'sideload' );
 498
 499	return $return;
 500}
 501
 502/**
 503 * Downloads a url to a local temporary file using the WordPress HTTP Class.
 504 * Please note, That the calling function must unlink() the file.
 505 *
 506 * @since 2.5.0
 507 *
 508 * @param string $url the URL of the file to download
 509 * @param int $timeout The timeout for the request to download the file default 300 seconds
 510 * @return mixed WP_Error on failure, string Filename on success.
 511 */
 512function download_url( $url, $timeout = 300 ) {
 513	//WARNING: The file is not automatically deleted, The script must unlink() the file.
 514	if ( ! $url )
 515		return new WP_Error('http_no_url', __('Invalid URL Provided.'));
 516
 517	$tmpfname = wp_tempnam($url);
 518	if ( ! $tmpfname )
 519		return new WP_Error('http_no_file', __('Could not create Temporary file.'));
 520
 521	$response = wp_safe_remote_get( $url, array( 'timeout' => $timeout, 'stream' => true, 'filename' => $tmpfname ) );
 522
 523	if ( is_wp_error( $response ) ) {
 524		unlink( $tmpfname );
 525		return $response;
 526	}
 527
 528	if ( 200 != wp_remote_retrieve_response_code( $response ) ){
 529		unlink( $tmpfname );
 530		return new WP_Error( 'http_404', trim( wp_remote_retrieve_response_message( $response ) ) );
 531	}
 532
 533	$content_md5 = wp_remote_retrieve_header( $response, 'content-md5' );
 534	if ( $content_md5 ) {
 535		$md5_check = verify_file_md5( $tmpfname, $content_md5 );
 536		if ( is_wp_error( $md5_check ) ) {
 537			unlink( $tmpfname );
 538			return $md5_check;
 539		}
 540	}
 541
 542	return $tmpfname;
 543}
 544
 545/**
 546 * Calculates and compares the MD5 of a file to its expected value.
 547 *
 548 * @since 3.7.0
 549 *
 550 * @param string $filename The filename to check the MD5 of.
 551 * @param string $expected_md5 The expected MD5 of the file, either a base64 encoded raw md5, or a hex-encoded md5
 552 * @return bool|object WP_Error on failure, true on success, false when the MD5 format is unknown/unexpected
 553 */
 554function verify_file_md5( $filename, $expected_md5 ) {
 555	if ( 32 == strlen( $expected_md5 ) )
 556		$expected_raw_md5 = pack( 'H*', $expected_md5 );
 557	elseif ( 24 == strlen( $expected_md5 ) )
 558		$expected_raw_md5 = base64_decode( $expected_md5 );
 559	else
 560		return false; // unknown format
 561
 562	$file_md5 = md5_file( $filename, true );
 563
 564	if ( $file_md5 === $expected_raw_md5 )
 565		return true;
 566
 567	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 ) ) );
 568}
 569
 570/**
 571 * Unzips a specified ZIP file to a location on the Filesystem via the WordPress Filesystem Abstraction.
 572 * Assumes that WP_Filesystem() has already been called and set up. Does not extract a root-level __MACOSX directory, if present.
 573 *
 574 * Attempts to increase the PHP Memory limit to 256M before uncompressing,
 575 * However, The most memory required shouldn't be much larger than the Archive itself.
 576 *
 577 * @since 2.5.0
 578 *
 579 * @param string $file Full path and filename of zip archive
 580 * @param string $to Full path on the filesystem to extract archive to
 581 * @return mixed WP_Error on failure, True on success
 582 */
 583function unzip_file($file, $to) {
 584	global $wp_filesystem;
 585
 586	if ( ! $wp_filesystem || !is_object($wp_filesystem) )
 587		return new WP_Error('fs_unavailable', __('Could not access filesystem.'));
 588
 589	// Unzip can use a lot of memory, but not this much hopefully
 590	/** This filter is documented in wp-admin/admin.php */
 591	@ini_set( 'memory_limit', apply_filters( 'admin_memory_limit', WP_MAX_MEMORY_LIMIT ) );
 592
 593	$needed_dirs = array();
 594	$to = trailingslashit($to);
 595
 596	// Determine any parent dir's needed (of the upgrade directory)
 597	if ( ! $wp_filesystem->is_dir($to) ) { //Only do parents if no children exist
 598		$path = preg_split('![/\\\]!', untrailingslashit($to));
 599		for ( $i = count($path); $i >= 0; $i-- ) {
 600			if ( empty($path[$i]) )
 601				continue;
 602
 603			$dir = implode('/', array_slice($path, 0, $i+1) );
 604			if ( preg_match('!^[a-z]:$!i', $dir) ) // Skip it if it looks like a Windows Drive letter.
 605				continue;
 606
 607			if ( ! $wp_filesystem->is_dir($dir) )
 608				$needed_dirs[] = $dir;
 609			else
 610				break; // A folder exists, therefor, we dont need the check the levels below this
 611		}
 612	}
 613
 614	/**
 615	 * Filter whether to use ZipArchive to unzip archives.
 616	 *
 617	 * @since 3.0.0
 618	 *
 619	 * @param bool $ziparchive Whether to use ZipArchive. Default true.
 620	 */
 621	if ( class_exists( 'ZipArchive' ) && apply_filters( 'unzip_file_use_ziparchive', true ) ) {
 622		$result = _unzip_file_ziparchive($file, $to, $needed_dirs);
 623		if ( true === $result ) {
 624			return $result;
 625		} elseif ( is_wp_error($result) ) {
 626			if ( 'incompatible_archive' != $result->get_error_code() )
 627				return $result;
 628		}
 629	}
 630	// Fall through to PclZip if ZipArchive is not available, or encountered an error opening the file.
 631	return _unzip_file_pclzip($file, $to, $needed_dirs);
 632}
 633
 634/**
 635 * This function should not be called directly, use unzip_file instead. Attempts to unzip an archive using the ZipArchive class.
 636 * Assumes that WP_Filesystem() has already been called and set up.
 637 *
 638 * @since 3.0.0
 639 * @see unzip_file
 640 * @access private
 641 *
 642 * @param string $file Full path and filename of zip archive
 643 * @param string $to Full path on the filesystem to extract archive to
 644 * @param array $needed_dirs A partial list of required folders needed to be created.
 645 * @return mixed WP_Error on failure, True on success
 646 */
 647function _unzip_file_ziparchive($file, $to, $needed_dirs = array() ) {
 648	global $wp_filesystem;
 649
 650	$z = new ZipArchive();
 651
 652	$zopen = $z->open( $file, ZIPARCHIVE::CHECKCONS );
 653	if ( true !== $zopen )
 654		return new WP_Error( 'incompatible_archive', __( 'Incompatible Archive.' ), array( 'ziparchive_error' => $zopen ) );
 655
 656	$uncompressed_size = 0;
 657
 658	for ( $i = 0; $i < $z->numFiles; $i++ ) {
 659		if ( ! $info = $z->statIndex($i) )
 660			return new WP_Error( 'stat_failed_ziparchive', __( 'Could not retrieve file from archive.' ) );
 661
 662		if ( '__MACOSX/' === substr($info['name'], 0, 9) ) // Skip the OS X-created __MACOSX directory
 663			continue;
 664
 665		$uncompressed_size += $info['size'];
 666
 667		if ( '/' == substr($info['name'], -1) ) // directory
 668			$needed_dirs[] = $to . untrailingslashit($info['name']);
 669		else
 670			$needed_dirs[] = $to . untrailingslashit(dirname($info['name']));
 671	}
 672
 673	/*
 674	 * disk_free_space() could return false. Assume that any falsey value is an error.
 675	 * A disk that has zero free bytes has bigger problems.
 676	 * Require we have enough space to unzip the file and copy its contents, with a 10% buffer.
 677	 */
 678	if ( defined( 'DOING_CRON' ) && DOING_CRON ) {
 679		$available_space = @disk_free_space( WP_CONTENT_DIR );
 680		if ( $available_space && ( $uncompressed_size * 2.1 ) > $available_space )
 681			return new WP_Error( 'disk_full_unzip_file', __( 'Could not copy files. You may have run out of disk space.' ), compact( 'uncompressed_size', 'available_space' ) );
 682	}
 683
 684	$needed_dirs = array_unique($needed_dirs);
 685	foreach ( $needed_dirs as $dir ) {
 686		// Check the parent folders of the folders all exist within the creation array.
 687		if ( untrailingslashit($to) == $dir ) // Skip over the working directory, We know this exists (or will exist)
 688			continue;
 689		if ( strpos($dir, $to) === false ) // If the directory is not within the working directory, Skip it
 690			continue;
 691
 692		$parent_folder = dirname($dir);
 693		while ( !empty($parent_folder) && untrailingslashit($to) != $parent_folder && !in_array($parent_folder, $needed_dirs) ) {
 694			$needed_dirs[] = $parent_folder;
 695			$parent_folder = dirname($parent_folder);
 696		}
 697	}
 698	asort($needed_dirs);
 699
 700	// Create those directories if need be:
 701	foreach ( $needed_dirs as $_dir ) {
 702		if ( ! $wp_filesystem->mkdir($_dir, FS_CHMOD_DIR) && ! $wp_filesystem->is_dir($_dir) ) // Only check to see if the Dir exists upon creation failure. Less I/O this way.
 703			return new WP_Error( 'mkdir_failed_ziparchive', __( 'Could not create directory.' ), substr( $_dir, strlen( $to ) ) );
 704	}
 705	unset($needed_dirs);
 706
 707	for ( $i = 0; $i < $z->numFiles; $i++ ) {
 708		if ( ! $info = $z->statIndex($i) )
 709			return new WP_Error( 'stat_failed_ziparchive', __( 'Could not retrieve file from archive.' ) );
 710
 711		if ( '/' == substr($info['name'], -1) ) // directory
 712			continue;
 713
 714		if ( '__MACOSX/' === substr($info['name'], 0, 9) ) // Don't extract the OS X-created __MACOSX directory files
 715			continue;
 716
 717		$contents = $z->getFromIndex($i);
 718		if ( false === $contents )
 719			return new WP_Error( 'extract_failed_ziparchive', __( 'Could not extract file from archive.' ), $info['name'] );
 720
 721		if ( ! $wp_filesystem->put_contents( $to . $info['name'], $contents, FS_CHMOD_FILE) )
 722			return new WP_Error( 'copy_failed_ziparchive', __( 'Could not copy file.' ), $info['name'] );
 723	}
 724
 725	$z->close();
 726
 727	return true;
 728}
 729
 730/**
 731 * This function should not be called directly, use unzip_file instead. Attempts to unzip an archive using the PclZip library.
 732 * Assumes that WP_Filesystem() has already been called and set up.
 733 *
 734 * @since 3.0.0
 735 * @see unzip_file
 736 * @access private
 737 *
 738 * @param string $file Full path and filename of zip archive
 739 * @param string $to Full path on the filesystem to extract archive to
 740 * @param array $needed_dirs A partial list of required folders needed to be created.
 741 * @return mixed WP_Error on failure, True on success
 742 */
 743function _unzip_file_pclzip($file, $to, $needed_dirs = array()) {
 744	global $wp_filesystem;
 745
 746	mbstring_binary_safe_encoding();
 747
 748	require_once(ABSPATH . 'wp-admin/includes/class-pclzip.php');
 749
 750	$archive = new PclZip($file);
 751
 752	$archive_files = $archive->extract(PCLZIP_OPT_EXTRACT_AS_STRING);
 753
 754	reset_mbstring_encoding();
 755
 756	// Is the archive valid?
 757	if ( !is_array($archive_files) )
 758		return new WP_Error('incompatible_archive', __('Incompatible Archive.'), $archive->errorInfo(true));
 759
 760	if ( 0 == count($archive_files) )
 761		return new WP_Error( 'empty_archive_pclzip', __( 'Empty archive.' ) );
 762
 763	$uncompressed_size = 0;
 764
 765	// Determine any children directories needed (From within the archive)
 766	foreach ( $archive_files as $file ) {
 767		if ( '__MACOSX/' === substr($file['filename'], 0, 9) ) // Skip the OS X-created __MACOSX directory
 768			continue;
 769
 770		$uncompressed_size += $file['size'];
 771
 772		$needed_dirs[] = $to . untrailingslashit( $file['folder'] ? $file['filename'] : dirname($file['filename']) );
 773	}
 774
 775	/*
 776	 * disk_free_space() could return false. Assume that any falsey value is an error.
 777	 * A disk that has zero free bytes has bigger problems.
 778	 * Require we have enough space to unzip the file and copy its contents, with a 10% buffer.
 779	 */
 780	if ( defined( 'DOING_CRON' ) && DOING_CRON ) {
 781		$available_space = @disk_free_space( WP_CONTENT_DIR );
 782		if ( $available_space && ( $uncompressed_size * 2.1 ) > $available_space )
 783			return new WP_Error( 'disk_full_unzip_file', __( 'Could not copy files. You may have run out of disk space.' ), compact( 'uncompressed_size', 'available_space' ) );
 784	}
 785
 786	$needed_dirs = array_unique($needed_dirs);
 787	foreach ( $needed_dirs as $dir ) {
 788		// Check the parent folders of the folders all exist within the creation array.
 789		if ( untrailingslashit($to) == $dir ) // Skip over the working directory, We know this exists (or will exist)
 790			continue;
 791		if ( strpos($dir, $to) === false ) // If the directory is not within the working directory, Skip it
 792			continue;
 793
 794		$parent_folder = dirname($dir);
 795		while ( !empty($parent_folder) && untrailingslashit($to) != $parent_folder && !in_array($parent_folder, $needed_dirs) ) {
 796			$needed_dirs[] = $parent_folder;
 797			$parent_folder = dirname($parent_folder);
 798		}
 799	}
 800	asort($needed_dirs);
 801
 802	// Create those directories if need be:
 803	foreach ( $needed_dirs as $_dir ) {
 804		// Only check to see if the dir exists upon creation failure. Less I/O this way.
 805		if ( ! $wp_filesystem->mkdir( $_dir, FS_CHMOD_DIR ) && ! $wp_filesystem->is_dir( $_dir ) )
 806			return new WP_Error( 'mkdir_failed_pclzip', __( 'Could not create directory.' ), substr( $_dir, strlen( $to ) ) );
 807	}
 808	unset($needed_dirs);
 809
 810	// Extract the files from the zip
 811	foreach ( $archive_files as $file ) {
 812		if ( $file['folder'] )
 813			continue;
 814
 815		if ( '__MACOSX/' === substr($file['filename'], 0, 9) ) // Don't extract the OS X-created __MACOSX directory files
 816			continue;
 817
 818		if ( ! $wp_filesystem->put_contents( $to . $file['filename'], $file['content'], FS_CHMOD_FILE) )
 819			return new WP_Error( 'copy_failed_pclzip', __( 'Could not copy file.' ), $file['filename'] );
 820	}
 821	return true;
 822}
 823
 824/**
 825 * Copies a directory from one location to another via the WordPress Filesystem Abstraction.
 826 * Assumes that WP_Filesystem() has already been called and setup.
 827 *
 828 * @since 2.5.0
 829 *
 830 * @param string $from source directory
 831 * @param string $to destination directory
 832 * @param array $skip_list a list of files/folders to skip copying
 833 * @return mixed WP_Error on failure, True on success.
 834 */
 835function copy_dir($from, $to, $skip_list = array() ) {
 836	global $wp_filesystem;
 837
 838	$dirlist = $wp_filesystem->dirlist($from);
 839
 840	$from = trailingslashit($from);
 841	$to = trailingslashit($to);
 842
 843	foreach ( (array) $dirlist as $filename => $fileinfo ) {
 844		if ( in_array( $filename, $skip_list ) )
 845			continue;
 846
 847		if ( 'f' == $fileinfo['type'] ) {
 848			if ( ! $wp_filesystem->copy($from . $filename, $to . $filename, true, FS_CHMOD_FILE) ) {
 849				// If copy failed, chmod file to 0644 and try again.
 850				$wp_filesystem->chmod( $to . $filename, FS_CHMOD_FILE );
 851				if ( ! $wp_filesystem->copy($from . $filename, $to . $filename, true, FS_CHMOD_FILE) )
 852					return new WP_Error( 'copy_failed_copy_dir', __( 'Could not copy file.' ), $to . $filename );
 853			}
 854		} elseif ( 'd' == $fileinfo['type'] ) {
 855			if ( !$wp_filesystem->is_dir($to . $filename) ) {
 856				if ( !$wp_filesystem->mkdir($to . $filename, FS_CHMOD_DIR) )
 857					return new WP_Error( 'mkdir_failed_copy_dir', __( 'Could not create directory.' ), $to . $filename );
 858			}
 859
 860			// generate the $sub_skip_list for the subdirectory as a sub-set of the existing $skip_list
 861			$sub_skip_list = array();
 862			foreach ( $skip_list as $skip_item ) {
 863				if ( 0 === strpos( $skip_item, $filename . '/' ) )
 864					$sub_skip_list[] = preg_replace( '!^' . preg_quote( $filename, '!' ) . '/!i', '', $skip_item );
 865			}
 866
 867			$result = copy_dir($from . $filename, $to . $filename, $sub_skip_list);
 868			if ( is_wp_error($result) )
 869				return $result;
 870		}
 871	}
 872	return true;
 873}
 874
 875/**
 876 * Initialises and connects the WordPress Filesystem Abstraction classes.
 877 * This function will include the chosen transport and attempt connecting.
 878 *
 879 * Plugins may add extra transports, And force WordPress to use them by returning the filename via the 'filesystem_method_file' filter.
 880 *
 881 * @since 2.5.0
 882 *
 883 * @param array $args (optional) Connection args, These are passed directly to the WP_Filesystem_*() classes.
 884 * @param string $context (optional) Context for get_filesystem_method(), See function declaration for more information.
 885 * @return boolean false on failure, true on success
 886 */
 887function WP_Filesystem( $args = false, $context = false ) {
 888	global $wp_filesystem;
 889
 890	require_once(ABSPATH . 'wp-admin/includes/class-wp-filesystem-base.php');
 891
 892	$method = get_filesystem_method($args, $context);
 893
 894	if ( ! $method )
 895		return false;
 896
 897	if ( ! class_exists("WP_Filesystem_$method") ) {
 898
 899		/**
 900		 * Filter the path for a specific filesystem method class file.
 901		 *
 902		 * @since 2.6.0
 903		 *
 904		 * @see get_filesystem_method()
 905		 *
 906		 * @param string $path   Path to the specific filesystem method class file.
 907		 * @param string $method The filesystem method to use.
 908		 */
 909		$abstraction_file = apply_filters( 'filesystem_method_file', ABSPATH . 'wp-admin/includes/class-wp-filesystem-' . $method . '.php', $method );
 910
 911		if ( ! file_exists($abstraction_file) )
 912			return;
 913
 914		require_once($abstraction_file);
 915	}
 916	$method = "WP_Filesystem_$method";
 917
 918	$wp_filesystem = new $method($args);
 919
 920	//Define the timeouts for the connections. Only available after the construct is called to allow for per-transport overriding of the default.
 921	if ( ! defined('FS_CONNECT_TIMEOUT') )
 922		define('FS_CONNECT_TIMEOUT', 30);
 923	if ( ! defined('FS_TIMEOUT') )
 924		define('FS_TIMEOUT', 30);
 925
 926	if ( is_wp_error($wp_filesystem->errors) && $wp_filesystem->errors->get_error_code() )
 927		return false;
 928
 929	if ( !$wp_filesystem->connect() )
 930		return false; //There was an error connecting to the server.
 931
 932	// Set the permission constants if not already set.
 933	if ( ! defined('FS_CHMOD_DIR') )
 934		define('FS_CHMOD_DIR', ( fileperms( ABSPATH ) & 0777 | 0755 ) );
 935	if ( ! defined('FS_CHMOD_FILE') )
 936		define('FS_CHMOD_FILE', ( fileperms( ABSPATH . 'index.php' ) & 0777 | 0644 ) );
 937
 938	return true;
 939}
 940
 941/**
 942 * Determines which Filesystem Method to use.
 943 * The priority of the Transports are: Direct, SSH2, FTP PHP Extension, FTP Sockets (Via Sockets class, or fsockopen())
 944 *
 945 * Note that the return value of this function can be overridden in 2 ways
 946 *  - By defining FS_METHOD in your <code>wp-config.php</code> file
 947 *  - By using the filesystem_method filter
 948 * Valid values for these are: 'direct', 'ssh2', 'ftpext' or 'ftpsockets'
 949 * Plugins may also define a custom transport handler, See the WP_Filesystem function for more information.
 950 *
 951 * @since 2.5.0
 952 *
 953 * @param array $args Connection details.
 954 * @param string $context Full path to the directory that is tested for being writable.
 955 * @return string The transport to use, see description for valid return values.
 956 */
 957function get_filesystem_method($args = array(), $context = false) {
 958	$method = defined('FS_METHOD') ? FS_METHOD : false; // Please ensure that this is either 'direct', 'ssh2', 'ftpext' or 'ftpsockets'
 959
 960	if ( ! $method && function_exists('getmyuid') && function_exists('fileowner') ){
 961		if ( !$context )
 962			$context = WP_CONTENT_DIR;
 963
 964		// If the directory doesn't exist (wp-content/languages) then use the parent directory as we'll create it.
 965		if ( WP_LANG_DIR == $context && ! is_dir( $context ) )
 966			$context = dirname( $context );
 967
 968		$context = trailingslashit($context);
 969		$temp_file_name = $context . 'temp-write-test-' . time();
 970		$temp_handle = @fopen($temp_file_name, 'w');
 971		if ( $temp_handle ) {
 972			if ( getmyuid() == @fileowner($temp_file_name) )
 973				$method = 'direct';
 974			@fclose($temp_handle);
 975			@unlink($temp_file_name);
 976		}
 977 	}
 978
 979	if ( ! $method && isset($args['connection_type']) && 'ssh' == $args['connection_type'] && extension_loaded('ssh2') && function_exists('stream_get_contents') ) $method = 'ssh2';
 980	if ( ! $method && extension_loaded('ftp') ) $method = 'ftpext';
 981	if ( ! $method && ( extension_loaded('sockets') || function_exists('fsockopen') ) ) $method = 'ftpsockets'; //Sockets: Socket extension; PHP Mode: FSockopen / fwrite / fread
 982
 983	/**
 984	 * Filter the filesystem method to use.
 985	 *
 986	 * @since 2.6.0
 987	 *
 988	 * @param string $method Filesystem method to return.
 989	 * @param array  $args   An array of connection details for the method.
 990	 */
 991	return apply_filters( 'filesystem_method', $method, $args );
 992}
 993
 994/**
 995 * Displays a form to the user to request for their FTP/SSH details in order to connect to the filesystem.
 996 * All chosen/entered details are saved, Excluding the Password.
 997 *
 998 * Hostnames may be in the form of hostname:portnumber (eg: wordpress.org:2467) to specify an alternate FTP/SSH port.
 999 *
1000 * Plugins may override this form by returning true|false via the <code>request_filesystem_credentials</code> filter.
1001 *
1002 * @since 2.5.0
1003 *
1004 * @param string $form_post the URL to post the form to
1005 * @param string $type the chosen Filesystem method in use
1006 * @param boolean $error if the current request has failed to connect
1007 * @param string $context The directory which is needed access to, The write-test will be performed on this directory by get_filesystem_method()
1008 * @param string $extra_fields Extra POST fields which should be checked for to be included in the post.
1009 * @return boolean False on failure. True on success.
1010 */
1011function request_filesystem_credentials($form_post, $type = '', $error = false, $context = false, $extra_fields = null) {
1012
1013	/**
1014	 * Filter the filesystem credentials form output.
1015	 *
1016	 * Returning anything other than an empty string will effectively short-circuit
1017	 * output of the filesystem credentials form, returning that value instead.
1018	 *
1019	 * @since 2.5.0
1020	 *
1021	 * @param mixed  $output       Form output to return instead. Default empty.
1022	 * @param string $form_post    URL to POST the form to.
1023	 * @param string $type         Chosen type of filesystem.
1024	 * @param bool   $error        Whether the current request has failed to connect.
1025	 *                             Default false.
1026	 * @param string $context      Full path to the directory that is tested for
1027	 *                             being writable.
1028	 * @param array  $extra_fields Extra POST fields.
1029	 */
1030	$req_cred = apply_filters( 'request_filesystem_credentials', '', $form_post, $type, $error, $context, $extra_fields );
1031	if ( '' !== $req_cred )
1032		return $req_cred;
1033
1034	if ( empty($type) )
1035		$type = get_filesystem_method(array(), $context);
1036
1037	if ( 'direct' == $type )
1038		return true;
1039
1040	if ( is_null( $extra_fields ) )
1041		$extra_fields = array( 'version', 'locale' );
1042
1043	$credentials = get_option('ftp_credentials', array( 'hostname' => '', 'username' => ''));
1044
1045	// 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)
1046	$credentials['hostname'] = defined('FTP_HOST') ? FTP_HOST : (!empty($_POST['hostname']) ? wp_unslash( $_POST['hostname'] ) : $credentials['hostname']);
1047	$credentials['username'] = defined('FTP_USER') ? FTP_USER : (!empty($_POST['username']) ? wp_unslash( $_POST['username'] ) : $credentials['username']);
1048	$credentials['password'] = defined('FTP_PASS') ? FTP_PASS : (!empty($_POST['password']) ? wp_unslash( $_POST['password'] ) : '');
1049
1050	// Check to see if we are setting the public/private keys for ssh
1051	$credentials['public_key'] = defined('FTP_PUBKEY') ? FTP_PUBKEY : (!empty($_POST['public_key']) ? wp_unslash( $_POST['public_key'] ) : '');
1052	$credentials['private_key'] = defined('FTP_PRIKEY') ? FTP_PRIKEY : (!empty($_POST['private_key']) ? wp_unslash( $_POST['private_key'] ) : '');
1053
1054	//sanitize the hostname, Some people might pass in odd-data:
1055	$credentials['hostname'] = preg_replace('|\w+://|', '', $credentials['hostname']); //Strip any schemes off
1056
1057	if ( strpos($credentials['hostname'], ':') ) {
1058		list( $credentials['hostname'], $credentials['port'] ) = explode(':', $credentials['hostname'], 2);
1059		if ( ! is_numeric($credentials['port']) )
1060			unset($credentials['port']);
1061	} else {
1062		unset($credentials['port']);
1063	}
1064
1065	if ( ( defined('FTP_SSH') && FTP_SSH ) || ( defined('FS_METHOD') && 'ssh2' == FS_METHOD ) )
1066		$credentials['connection_type'] = 'ssh';
1067	else if ( (defined('FTP_SSL') && FTP_SSL) && 'ftpext' == $type ) //Only the FTP Extension understands SSL
1068		$credentials['connection_type'] = 'ftps';
1069	else if ( !empty($_POST['connection_type']) )
1070		$credentials['connection_type'] = wp_unslash( $_POST['connection_type'] );
1071	else if ( !isset($credentials['connection_type']) ) //All else fails (And it's not defaulted to something else saved), Default to FTP
1072		$credentials['connection_type'] = 'ftp';
1073
1074	if ( ! $error &&
1075			(
1076				( !empty($credentials['password']) && !empty($credentials['username']) && !empty($credentials['hostname']) ) ||
1077				( 'ssh' == $credentials['connection_type'] && !empty($credentials['public_key']) && !empty($credentials['private_key']) )
1078			) ) {
1079		$stored_credentials = $credentials;
1080		if ( !empty($stored_credentials['port']) ) //save port as part of hostname to simplify above code.
1081			$stored_credentials['hostname'] .= ':' . $stored_credentials['port'];
1082
1083		unset($stored_credentials['password'], $stored_credentials['port'], $stored_credentials['private_key'], $stored_credentials['public_key']);
1084		update_option('ftp_credentials', $stored_credentials);
1085		return $credentials;
1086	}
1087	$hostname = isset( $credentials['hostname'] ) ? $credentials['hostname'] : '';
1088	$username = isset( $credentials['username'] ) ? $credentials['username'] : '';
1089	$public_key = isset( $credentials['public_key'] ) ? $credentials['public_key'] : '';
1090	$private_key = isset( $credentials['private_key'] ) ? $credentials['private_key'] : '';
1091	$port = isset( $credentials['port'] ) ? $credentials['port'] : '';
1092	$connection_type = isset( $credentials[''] ) ? $credentials[''] : '';
1093
1094	if ( $error ) {
1095		$error_string = __('<strong>ERROR:</strong> There was an error connecting to the server, Please verify the settings are correct.');
1096		if ( is_wp_error($error) )
1097			$error_string = esc_html( $error->get_error_message() );
1098		echo '<div id="message" class="error"><p>' . $error_string . '</p></div>';
1099	}
1100
1101	$types = array();
1102	if ( extension_loaded('ftp') || extension_loaded('sockets') || function_exists('fsockopen') )
1103		$types[ 'ftp' ] = __('FTP');
1104	if ( extension_loaded('ftp') ) //Only this supports FTPS
1105		$types[ 'ftps' ] = __('FTPS (SSL)');
1106	if ( extension_loaded('ssh2') && function_exists('stream_get_contents') )
1107		$types[ 'ssh' ] = __('SSH2');
1108
1109	/**
1110	 * Filter the connection types to output to the filesystem credentials form.
1111	 *
1112	 * @since 2.9.0
1113	 *
1114	 * @param array  $types       Types of connections.
1115	 * @param array  $credentials Credentials to connect with.
1116	 * @param string $type        Chosen filesystem method.
1117	 * @param object $error       Error object.
1118	 * @param string $context     Full path to the directory that is tested
1119	 *                            for being writable.
1120	 */
1121	$types = apply_filters( 'fs_ftp_connection_types', $types, $credentials, $type, $error, $context );
1122
1123?>
1124<script type="text/javascript">
1125<!--
1126jQuery(function($){
1127	jQuery("#ssh").click(function () {
1128		jQuery("#ssh_keys").show();
1129	});
1130	jQuery("#ftp, #ftps").click(function () {
1131		jQuery("#ssh_keys").hide();
1132	});
1133	jQuery('form input[value=""]:first').focus();
1134});
1135-->
1136</script>
1137<form action="<?php echo esc_url( $form_post ) ?>" method="post">
1138<div>
1139<h3><?php _e('Connection Information') ?></h3>
1140<p><?php
1141	$label_user = __('Username');
1142	$label_pass = __('Password');
1143	_e('To perform the requested action, WordPress needs to access your web server.');
1144	echo ' ';
1145	if ( ( isset( $types['ftp'] ) || isset( $types['ftps'] ) ) ) {
1146		if ( isset( $types['ssh'] ) ) {
1147			_e('Please enter your FTP or SSH credentials to proceed.');
1148			$label_user = __('FTP/SSH Username');
1149			$label_pass = __('FTP/SSH Password');
1150		} else {
1151			_e('Please enter your FTP credentials to proceed.');
1152			$label_user = __('FTP Username');
1153			$label_pass = __('FTP Password');
1154		}
1155		echo ' ';
1156	}
1157	_e('If you do not remember your credentials, you should contact your web host.');
1158?></p>
1159<table class="form-table">
1160<tr>
1161<th scope="row"><label for="hostname"><?php _e('Hostname') ?></label></th>
1162<td><input name="hostname" type="text" id="hostname" value="<?php echo esc_attr($hostname); if ( !empty($port) ) echo ":$port"; ?>"<?php disabled( defined('FTP_HOST') ); ?> size="40" /></td>
1163</tr>
1164
1165<tr>
1166<th scope="row"><label for="username"><?php echo $label_user; ?></label></th>
1167<td><input name="username" type="text" id="username" value="<?php echo esc_attr($username) ?>"<?php disabled( defined('FTP_USER') ); ?> size="40" /></td>
1168</tr>
1169
1170<tr>
1171<th scope="row"><label for="password"><?php echo $label_pass; ?></label></th>
1172<td><div><input name="password" type="password" id="password" value="<?php if ( defined('FTP_PASS') ) echo '*****'; ?>"<?php disabled( defined('FTP_PASS') ); ?> size="40" /></div>
1173<div><em><?php if ( ! defined('FTP_PASS') ) _e( 'This password will not be stored on the server.' ); ?></em></div></td>
1174</tr>
1175
1176<?php if ( isset($types['ssh']) ) : ?>
1177<tr id="ssh_keys" style="<?php if ( 'ssh' != $connection_type ) echo 'display:none' ?>">
1178<th scope="row"><?php _e('Authentication Keys') ?>
1179<div class="key-labels textright">
1180<label for="public_key"><?php _e('Public Key:') ?></label ><br />
1181<label for="private_key"><?php _e('Private Key:') ?></label>
1182</div></th>
1183<td><br /><input name="public_key" type="text" id="public_key" value="<?php echo esc_attr($public_key) ?>"<?php disabled( defined('FTP_PUBKEY') ); ?> size="40" />
1184	<br /><input name="private_key" type="text" id="private_key" value="<?php echo esc_attr($private_key) ?>"<?php disabled( defined('FTP_PRIKEY') ); ?> size="40" />
1185<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>
1186</tr>
1187<?php endif; ?>
1188
1189<tr>
1190<th scope="row"><?php _e('Connection Type') ?></th>
1191<td>
1192<fieldset><legend class="screen-reader-text"><span><?php _e('Connection Type') ?></span></legend>
1193<?php
1194	$disabled = disabled( (defined('FTP_SSL') && FTP_SSL) || (defined('FTP_SSH') && FTP_SSH), true, false );
1195	foreach ( $types as $name => $text ) : ?>
1196	<label for="<?php echo esc_attr($name) ?>">
1197		<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; ?> />
1198		<?php echo $text ?>
1199	</label>
1200	<?php endforeach; ?>
1201</fieldset>
1202</td>
1203</tr>
1204</table>
1205
1206<?php
1207foreach ( (array) $extra_fields as $field ) {
1208	if ( isset( $_POST[ $field ] ) )
1209		echo '<input type="hidden" name="' . esc_attr( $field ) . '" value="' . esc_attr( wp_unslash( $_POST[ $field ] ) ) . '" />';
1210}
1211submit_button( __( 'Proceed' ), 'button', 'upgrade' );
1212?>
1213</div>
1214</form>
1215<?php
1216	return false;
1217}