wp_handle_sideload

Definition:
function wp_handle_sideload( &$file, $overrides = false ) {}

Handle sideloads, which is the process of retrieving a media item from another server instead of a traditional media upload. This process involves sanitizing the filename, checking extensions for mime type, and moving the file to the appropriate directory within the uploads directory.

Parameters

  • array $file: an array similar to that of a PHP $_FILES POST array
  • array $overrides: Optional. An associative array of names=>values to override default variables with extract( $overrides, EXTR_OVERWRITE ).
  • &$file

Return values

returns:On success, returns an associative array of file attributes. On failure, returns $overrides[‘upload_error_handler’](&$file, $message ) or array( ‘error’=>$message ).

Defined filters

  • wp_handle_upload
    apply_filters( 'wp_handle_upload', array( 'file' => $new_file, 'url' => $url, 'type' => $type )
  • admin_memory_limit
    apply_filters( 'admin_memory_limit', WP_MAX_MEMORY_LIMIT )
  • unzip_file_use_ziparchive
    apply_filters('unzip_file_use_ziparchive', true )

Source code

function wp_handle_sideload( &$file, $overrides = false ) {

	// The default error handler.

	if (! function_exists( 'wp_handle_upload_error' ) ) {

		function wp_handle_upload_error( &$file, $message ) {

			return array( 'error'=>$message );

		}

	}



	// You may define your own function and pass the name in $overrides['upload_error_handler']

	$upload_error_handler = 'wp_handle_upload_error';



	// You may define your own function and pass the name in $overrides['unique_filename_callback']

	$unique_filename_callback = null;



	// $_POST['action'] must be set and its value must equal $overrides['action'] or this:

	$action = 'wp_handle_sideload';



	// Courtesy of php.net, the strings that describe the error indicated in $_FILES[{form field}]['error'].

	$upload_error_strings = array( false,

		__( "The uploaded file exceeds the <code>upload_max_filesize</code> directive in <code>php.ini</code>." ),

		__( "The uploaded file exceeds the <em>MAX_FILE_SIZE</em> directive that was specified in the HTML form." ),

		__( "The uploaded file was only partially uploaded." ),

		__( "No file was uploaded." ),

		'',

		__( "Missing a temporary folder." ),

		__( "Failed to write file to disk." ),

		__( "File upload stopped by extension." ));



	// All tests are on by default. Most can be turned off by $override[{test_name}] = false;

	$test_form = true;

	$test_size = true;



	// If you override this, you must provide $ext and $type!!!!

	$test_type = true;

	$mimes = false;



	// Install user overrides. Did we mention that this voids your warranty?

	if ( is_array( $overrides ) )

		extract( $overrides, EXTR_OVERWRITE );



	// A correct form post will pass this test.

	if ( $test_form && (!isset( $_POST['action'] ) || ($_POST['action'] != $action ) ) )

		return $upload_error_handler( $file, __( 'Invalid form submission.' ));



	// A successful upload will pass this test. It makes no sense to override this one.

	if ( ! empty( $file['error'] ) )

		return $upload_error_handler( $file, $upload_error_strings[$file['error']] );



	// A non-empty file will pass this test.

	if ( $test_size && !(filesize($file['tmp_name']) > 0 ) )

		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.' ));



	// A properly uploaded file will pass this test. There should be no reason to override this one.

	if (! @ is_file( $file['tmp_name'] ) )

		return $upload_error_handler( $file, __( 'Specified file does not exist.' ));



	// A correct MIME type will pass this test. Override $mimes or use the upload_mimes filter.

	if ( $test_type ) {

		$wp_filetype = wp_check_filetype_and_ext( $file['tmp_name'], $file['name'], $mimes );



		extract( $wp_filetype );



		// Check to see if wp_check_filetype_and_ext() determined the filename was incorrect

		if ( $proper_filename )

			$file['name'] = $proper_filename;



		if ( ( !$type || !$ext ) && !current_user_can( 'unfiltered_upload' ) )

			return $upload_error_handler( $file, __( 'Sorry, this file type is not permitted for security reasons.' ));



		if ( !$ext )

			$ext = ltrim(strrchr($file['name'], '.'), '.');



		if ( !$type )

			$type = $file['type'];

	}



	// A writable uploads dir will pass this test. Again, there's no point overriding this one.

	if ( ! ( ( $uploads = wp_upload_dir() ) && false === $uploads['error'] ) )

		return $upload_error_handler( $file, $uploads['error'] );



	$filename = wp_unique_filename( $uploads['path'], $file['name'], $unique_filename_callback );



	// Strip the query strings.

	$filename = str_replace('?','-', $filename);

	$filename = str_replace('&','-', $filename);



	// Move the file to the uploads dir

	$new_file = $uploads['path'] . "/$filename";

	if ( false === @ rename( $file['tmp_name'], $new_file ) ) {

		return $upload_error_handler( $file, sprintf( __('The uploaded file could not be moved to %s.' ), $uploads['path'] ) );

	}



	// Set correct file permissions

	$stat = stat( dirname( $new_file ));

	$perms = $stat['mode'] & 0000666;

	@ chmod( $new_file, $perms );



	// Compute the URL

	$url = $uploads['url'] . "/$filename";



	$return = apply_filters( 'wp_handle_upload', array( 'file' => $new_file, 'url' => $url, 'type' => $type ), 'sideload' );



	return $return;

}



/**

 * Downloads a url to a local temporary file using the WordPress HTTP Class.

 * Please note, That the calling function must unlink() the  file.

 *

 * @since 2.5.0

 *

 * @param string $url the URL of the file to download

 * @param int $timeout The timeout for the request to download the file default 300 seconds

 * @return mixed WP_Error on failure, string Filename on success.

 */

function download_url( $url, $timeout = 300 ) {

	//WARNING: The file is not automatically deleted, The script must unlink() the file.

	if ( ! $url )

		return new WP_Error('http_no_url', __('Invalid URL Provided.'));



	$tmpfname = wp_tempnam($url);

	if ( ! $tmpfname )

		return new WP_Error('http_no_file', __('Could not create Temporary file.'));



	$response = wp_remote_get( $url, array( 'timeout' => $timeout, 'stream' => true, 'filename' => $tmpfname ) );



	if ( is_wp_error( $response ) ) {

		unlink( $tmpfname );

		return $response;

	}



	if ( 200 != wp_remote_retrieve_response_code( $response ) ){

		unlink( $tmpfname );

		return new WP_Error( 'http_404', trim( wp_remote_retrieve_response_message( $response ) ) );

	}



	return $tmpfname;

}



/**

 * Unzip's a specified ZIP file to a location on the Filesystem via the WordPress Filesystem Abstraction.

 * Assumes that WP_Filesystem() has already been called and set up. Does not extract a root-level __MACOSX directory, if present.

 *

 * Attempts to increase the PHP Memory limit to 256M before uncompressing,

 * However, The most memory required shouldn't be much larger than the Archive itself.

 *

 * @since 2.5.0

 *

 * @param string $file Full path and filename of zip archive

 * @param string $to Full path on the filesystem to extract archive to

 * @return mixed WP_Error on failure, True on success

 */

function unzip_file($file, $to) {

	global $wp_filesystem;



	if ( ! $wp_filesystem || !is_object($wp_filesystem) )

		return new WP_Error('fs_unavailable', __('Could not access filesystem.'));



	// Unzip can use a lot of memory, but not this much hopefully

	@ini_set( 'memory_limit', apply_filters( 'admin_memory_limit', WP_MAX_MEMORY_LIMIT ) );



	$needed_dirs = array();

	$to = trailingslashit($to);



	// Determine any parent dir's needed (of the upgrade directory)

	if ( ! $wp_filesystem->is_dir($to) ) { //Only do parents if no children exist

		$path = preg_split('![/\\\]!', untrailingslashit($to));

		for ( $i = count($path); $i >= 0; $i-- ) {

			if ( empty($path[$i]) )

				continue;



			$dir = implode('/', array_slice($path, 0, $i+1) );

			if ( preg_match('!^[a-z]:$!i', $dir) ) // Skip it if it looks like a Windows Drive letter.

				continue;



			if ( ! $wp_filesystem->is_dir($dir) )

				$needed_dirs[] = $dir;

			else

				break; // A folder exists, therefor, we dont need the check the levels below this

		}

	}



	if ( class_exists('ZipArchive') && apply_filters('unzip_file_use_ziparchive', true ) ) {

		$result = _unzip_file_ziparchive($file, $to, $needed_dirs);

		if ( true === $result ) {

			return $result;

		} elseif ( is_wp_error($result) ) {

			if ( 'incompatible_archive' != $result->get_error_code() )

				return $result;

		}

	}

	// Fall through to PclZip if ZipArchive is not available, or encountered an error opening the file.

	return _unzip_file_pclzip($file, $to, $needed_dirs);

}



/**

 * This function should not be called directly, use unzip_file instead. Attempts to unzip an archive using the ZipArchive class.

 * Assumes that WP_Filesystem() has already been called and set up.

 *

 * @since 3.0.0

 * @see unzip_file

 * @access private

 *

 * @param string $file Full path and filename of zip archive

 * @param string $to Full path on the filesystem to extract archive to

 * @param array $needed_dirs A partial list of required folders needed to be created.

 * @return mixed WP_Error on failure, True on success

 */

function _unzip_file_ziparchive($file, $to, $needed_dirs = array() ) {

	global $wp_filesystem;



	$z = new ZipArchive();



	// PHP4-compat - php4 classes can't contain constants

	$zopen = $z->open($file, /* ZIPARCHIVE::CHECKCONS */ 4);

	if ( true !== $zopen )

		return new WP_Error('incompatible_archive', __('Incompatible Archive.'));



	for ( $i = 0; $i < $z->numFiles; $i++ ) {

		if ( ! $info = $z->statIndex($i) )

			return new WP_Error('stat_failed', __('Could not retrieve file from archive.'));



		if ( '__MACOSX/' === substr($info['name'], 0, 9) ) // Skip the OS X-created __MACOSX directory

			continue;



		if ( '/' == substr($info['name'], -1) ) // directory

			$needed_dirs[] = $to . untrailingslashit($info['name']);

		else

			$needed_dirs[] = $to . untrailingslashit(dirname($info['name']));

	}



	$needed_dirs = array_unique($needed_dirs);

	foreach ( $needed_dirs as $dir ) {

		// Check the parent folders of the folders all exist within the creation array.

		if ( untrailingslashit($to) == $dir ) // Skip over the working directory, We know this exists (or will exist)

			continue;

		if ( strpos($dir, $to) === false ) // If the directory is not within the working directory, Skip it

			continue;



		$parent_folder = dirname($dir);

		while ( !empty($parent_folder) && untrailingslashit($to) != $parent_folder && !in_array($parent_folder, $needed_dirs) ) {

			$needed_dirs[] = $parent_folder;

			$parent_folder = dirname($parent_folder);

		}

	}

	asort($needed_dirs);



	// Create those directories if need be:

	foreach ( $needed_dirs as $_dir ) {

		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.

			return new WP_Error('mkdir_failed', __('Could not create directory.'), $_dir);

	}

	unset($needed_dirs);



	for ( $i = 0; $i < $z->numFiles; $i++ ) {

		if ( ! $info = $z->statIndex($i) )

			return new WP_Error('stat_failed', __('Could not retrieve file from archive.'));



		if ( '/' == substr($info['name'], -1) ) // directory

			continue;



		if ( '__MACOSX/' === substr($info['name'], 0, 9) ) // Don't extract the OS X-created __MACOSX directory files

			continue;



		$contents = $z->getFromIndex($i);

		if ( false === $contents )

			return new WP_Error('extract_failed', __('Could not extract file from archive.'), $info['name']);



		if ( ! $wp_filesystem->put_contents( $to . $info['name'], $contents, FS_CHMOD_FILE) )

			return new WP_Error('copy_failed', __('Could not copy file.'), $to . $info['filename']);

	}



	$z->close();



	return true;

}



/**

 * This function should not be called directly, use unzip_file instead. Attempts to unzip an archive using the PclZip library.

 * Assumes that WP_Filesystem() has already been called and set up.

 *

 * @since 3.0.0

 * @see unzip_file

 * @access private

 *

 * @param string $file Full path and filename of zip archive

 * @param string $to Full path on the filesystem to extract archive to

 * @param array $needed_dirs A partial list of required folders needed to be created.

 * @return mixed WP_Error on failure, True on success

 */

function _unzip_file_pclzip($file, $to, $needed_dirs = array()) {

	global $wp_filesystem;



	// See #15789 - PclZip uses string functions on binary data, If it's overloaded with Multibyte safe functions the results are incorrect.

	if ( ini_get('mbstring.func_overload') && function_exists('mb_internal_encoding') ) {

		$previous_encoding = mb_internal_encoding();

		mb_internal_encoding('ISO-8859-1');

	}



	require_once(ABSPATH . 'wp-admin/includes/class-pclzip.php');



	$archive = new PclZip($file);



	$archive_files = $archive->extract(PCLZIP_OPT_EXTRACT_AS_STRING);



	if ( isset($previous_encoding) )

		mb_internal_encoding($previous_encoding);



	// Is the archive valid?

	if ( !is_array($archive_files) )

		return new WP_Error('incompatible_archive', __('Incompatible Archive.'), $archive->errorInfo(true));



	if ( 0 == count($archive_files) )

		return new WP_Error('empty_archive', __('Empty archive.'));



	// Determine any children directories needed (From within the archive)

	foreach ( $archive_files as $file ) {

		if ( '__MACOSX/' === substr($file['filename'], 0, 9) ) // Skip the OS X-created __MACOSX directory

			continue;



		$needed_dirs[] = $to . untrailingslashit( $file['folder'] ? $file['filename'] : dirname($file['filename']) );

	}



	$needed_dirs = array_unique($needed_dirs);

	foreach ( $needed_dirs as $dir ) {

		// Check the parent folders of the folders all exist within the creation array.

		if ( untrailingslashit($to) == $dir ) // Skip over the working directory, We know this exists (or will exist)

			continue;

		if ( strpos($dir, $to) === false ) // If the directory is not within the working directory, Skip it

			continue;



		$parent_folder = dirname($dir);

		while ( !empty($parent_folder) && untrailingslashit($to) != $parent_folder && !in_array($parent_folder, $needed_dirs) ) {

			$needed_dirs[] = $parent_folder;

			$parent_folder = dirname($parent_folder);

		}

	}

3755

Leave a comment