From 7c8a77efa31876274383aa3c0c54bca89eaff213 Mon Sep 17 00:00:00 2001 From: nacin Date: Sat, 15 May 2010 04:47:03 +0000 Subject: [PATCH] Introduce wp_check_filetype_and_ext() to handle mime/ext image comparisons and corrections for upload and sideload. props Viper007Bond, see #11946. git-svn-id: http://svn.automattic.com/wordpress/trunk@14649 1a063a9b-81f0-0310-95a4-ce76da25c4cd --- wp-admin/includes/file.php | 40 +++++----------------- wp-includes/functions.php | 68 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+), 31 deletions(-) diff --git a/wp-admin/includes/file.php b/wp-admin/includes/file.php index 01b5b54d4..c8156c693 100644 --- a/wp-admin/includes/file.php +++ b/wp-admin/includes/file.php @@ -306,10 +306,14 @@ function wp_handle_upload( &$file, $overrides = false, $time = null ) { // A correct MIME type will pass this test. Override $mimes or use the upload_mimes filter. if ( $test_type ) { - $wp_filetype = wp_check_filetype( $file['name'], $mimes ); + $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 call_user_func($upload_error_handler, $file, __( 'File type does not meet security guidelines. Try another.' )); @@ -416,39 +420,13 @@ function wp_handle_sideload( &$file, $overrides = false ) { // A correct MIME type will pass this test. Override $mimes or use the upload_mimes filter. if ( $test_type ) { - $wp_filetype = wp_check_filetype( $file['name'], $mimes ); + $wp_filetype = wp_check_filetype_and_ext( $file['tmp_name'], $file['name'], $mimes ); extract( $wp_filetype ); - // If the file claims to be an image, validate it's extension - if ( function_exists('getimagesize') && !empty( $type ) && 'image/' == substr( $type, 0, 6 ) && is_uploaded_file( $file['tmp_name'] ) ) { - // Attempt to figure out what type of image it really is - $imgstats = @getimagesize( $file['tmp_name'] ); - - // If getimagesize() knows what kind of image it really is and if the real MIME doesn't match the claimed MIME - if ( !empty($imgstats['mime']) && $imgstats['mime'] != $type ) { - // This is a simplified array of MIMEs that getimagesize() can detect and their extensions - $mime_to_ext = apply_filters( 'getimagesize_mimes_to_exts', array( - 'image/jpeg' => 'jpg', - 'image/png' => 'png', - 'image/gif' => 'gif', - 'image/bmp' => 'bmp', - 'image/tiff' => 'tif', - ) ); - - // Replace whatever's after the last period in the filename with the correct extension - if ( !empty($mime_to_ext[$imgstats['mime']]) ) { - $filename_parts = explode( '.', $file['name'] ); - array_pop( $filename_parts ); - $filename_parts[] = $mime_to_ext[$imgstats['mime']]; - $file['name'] = implode( '.', $filename_parts ); - - // Re-validate the extension / MIME - $wp_filetype = wp_check_filetype( $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, __( 'File type does not meet security guidelines. Try another.' )); diff --git a/wp-includes/functions.php b/wp-includes/functions.php index 3cdf184e0..709cc5c7b 100644 --- a/wp-includes/functions.php +++ b/wp-includes/functions.php @@ -2362,6 +2362,74 @@ function wp_check_filetype( $filename, $mimes = null ) { return compact( 'ext', 'type' ); } +/** + * Attempt to determine the real file type of a file. + * If unable to, the file name extension will be used to determine type. + * + * If it's determined that the extension does not match the file's real type, + * then the "proper_filename" value will be set with a proper filename and extension. + * + * Currently this function only supports validating images known to getimagesize(). + * + * @since 3.0.0 + * + * @param string $file Full path to the image. + * @param string $filename The filename of the image (may differ from $file due to $file being in a tmp directory) + * @param array $mimes Optional. Key is the file extension with value as the mime type. + * @return array Values for the extension, MIME, and either a corrected filename or false if original $filename is valid + */ +function wp_check_filetype_and_ext( $file, $filename, $mimes = null ) { + + $proper_filename = false; + + // Do basic extension validation and MIME mapping + $wp_filetype = wp_check_filetype( $filename, $mimes ); + extract( $wp_filetype ); + + // We can't do any further validation without a file to work with + if ( ! file_exists( $file ) ) + return compact( 'ext', 'type', 'proper_filename' ); + + // We're able to validate images using GD + if ( $type && 0 === strpos( $type, 'image/' ) && function_exists('getimagesize') ) { + + // Attempt to figure out what type of image it actually is + $imgstats = @getimagesize( $file ); + + // If getimagesize() knows what kind of image it really is and if the real MIME doesn't match the claimed MIME + if ( !empty($imgstats['mime']) && $imgstats['mime'] != $type ) { + // This is a simplified array of MIMEs that getimagesize() can detect and their extensions + // You shouldn't need to use this filter, but it's here just in case + $mime_to_ext = apply_filters( 'getimagesize_mimes_to_exts', array( + 'image/jpeg' => 'jpg', + 'image/png' => 'png', + 'image/gif' => 'gif', + 'image/bmp' => 'bmp', + 'image/tiff' => 'tif', + ) ); + + // Replace whatever is after the last period in the filename with the correct extension + if ( ! empty( $mime_to_ext[ $imgstats['mime'] ] ) ) { + $filename_parts = explode( '.', $filename ); + array_pop( $filename_parts ); + $filename_parts[] = $mime_to_ext[ $imgstats['mime'] ]; + $new_filename = implode( '.', $filename_parts ); + + if ( $new_filename != $filename ) + $proper_filename = $new_filename; // Mark that it changed + + // Redefine the extension / MIME + $wp_filetype = wp_check_filetype( $new_filename, $mimes ); + extract( $wp_filetype ); + } + } + } + + // Let plugins try and validate other types of files + // Should return an array in the style of array( 'ext' => $ext, 'type' => $type, 'proper_filename' => $proper_filename ) + return apply_filters( 'wp_check_filetype_and_ext', compact( 'ext', 'type', 'proper_filename' ), $file, $filename, $mimes ); +} + /** * Retrieve list of allowed mime types and file extensions. *