Merge pull request #486 from narfbg/ci-issue-60

Fix issues #60 (possible MIME-type injection) and #394
diff --git a/system/libraries/Upload.php b/system/libraries/Upload.php
index 8f324de..045283f 100644
--- a/system/libraries/Upload.php
+++ b/system/libraries/Upload.php
@@ -198,7 +198,8 @@
 		// Set the uploaded data as class variables
 		$this->file_temp = $_FILES[$field]['tmp_name'];
 		$this->file_size = $_FILES[$field]['size'];
-		$this->file_type = preg_replace("/^(.+?);.*$/", "\\1", $_FILES[$field]['type']);
+		$this->_file_mime_type($_FILES[$field]);
+		$this->file_type = preg_replace("/^(.+?);.*$/", "\\1", $this->file_type);
 		$this->file_type = strtolower(trim(stripslashes($this->file_type), '"'));
 		$this->file_name = $this->_prep_filename($_FILES[$field]['name']);
 		$this->file_ext	 = $this->get_extension($this->file_name);
@@ -1008,8 +1009,69 @@
 
 	// --------------------------------------------------------------------
 
+	/**
+	 * File MIME type
+	 *
+	 * Detects the (actual) MIME type of the uploaded file, if possible.
+	 * The input array is expected to be $_FILES[$field]
+	 *
+	 * @param	array
+	 * @return	void
+	 */
+	protected function _file_mime_type($file)
+	{
+		// Use if the Fileinfo extension, if available (only versions above 5.3 support the FILEINFO_MIME_TYPE flag)
+		if (is_php('5.3') && function_exists('finfo_file'))
+		{
+			$finfo = new finfo(FILEINFO_MIME_TYPE);
+			if ($finfo !== FALSE) // This is possible, if there is no magic MIME database file found on the system
+			{
+				$file_type = $finfo->file($file['tmp_name']);
+
+				/* According to the comments section of the PHP manual page,
+				 * it is possible that this function returns an empty string
+				 * for some files (e.g. if they don't exist in the magic MIME database)
+				 */
+				if (strlen($file_type) > 1)
+				{
+					$this->file_type = $file_type;
+					return;
+				}
+			}
+		}
+
+		// Fall back to the deprecated mime_content_type(), if available
+		if (function_exists('mime_content_type'))
+		{
+			$this->file_type = @mime_content_type($file['tmp_name']);
+			return;
+		}
+
+		/* This is an ugly hack, but UNIX-type systems provide a native way to detect the file type,
+		 * which is still more secure than depending on the value of $_FILES[$field]['type'].
+		 *
+		 * Notes:
+		 *	- a 'W' in the substr() expression bellow, would mean that we're using Windows
+		 *	- many system admins would disable the exec() function due to security concerns, hence the function_exists() check
+		 */
+		if (DIRECTORY_SEPARATOR !== '\\' && function_exists('exec'))
+		{
+			$output = array();
+			@exec('file --brief --mime-type ' . escapeshellarg($file['tmp_path']), $output, $return_code);
+			if ($return_code === 0 && strlen($output[0]) > 0) // A return status code != 0 would mean failed execution
+			{
+				$this->file_type = rtrim($output[0]);
+				return;
+			}
+		}
+
+		$this->file_type = $file['type'];
+	}
+
+	// --------------------------------------------------------------------
+
 }
 // END Upload Class
 
 /* End of file Upload.php */
-/* Location: ./system/libraries/Upload.php */
\ No newline at end of file
+/* Location: ./system/libraries/Upload.php */
diff --git a/user_guide/changelog.html b/user_guide/changelog.html
index 881d464..5bb7f60 100644
--- a/user_guide/changelog.html
+++ b/user_guide/changelog.html
@@ -79,7 +79,7 @@
 	<li>Helpers
 		<ul>
 			<li>Added <samp>increment_string()</samp> to <a href="helpers/string_helper.html">String Helper</a> to turn "foo" into "foo-1" or "foo-1" into "foo-2".</li>
-			<li>Altered form helper - made action on form_open_multipart helper function call optional.  Fixes (#65)</li>
+			<li>Altered form helper - made action on form_open_multipart helper function call optional. Fixes (#65)</li>
 			<li><samp>url_title()</samp> will now trim extra dashes from beginning and end.</li>
 			<li>Improved speed of <a href="helpers/string_helper.html">String Helper</a>'s <b>random_string()</b> method</li>
 			<li>Added XHTML Basic 1.1 doctype to <a href="helpers/html_helper.html">HTML Helper</a>.</li>
@@ -140,6 +140,7 @@
 	<li>Fixed a bug (#89) - Fix a variable type mismatch in DB <samp>display_error()</samp> where an array is expected, but a string could be set instead.</li>
 	<li>Fixed a bug (#467) - Suppress warnings generated from get_magic_quotes_gpc() (deprecated in PHP 5.4)</li>
 	<li>Fixed a bug (#484) - First time _csrf_set_hash() is called, hash is never set to the cookie (in Security.php).</li>
+	<li>Fixed a bug (#60) - Added _file_mime_type() method to the <a href="libraries/file_uploading.html">File Uploading Library</a> in order to fix a possible MIME-type injection (also fixes bug #394).</li>
 </ul>
 
 <h2>Version 2.0.3</h2>