Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save rodrigopedra/a64f27f4124d694e85809b68b93acd36 to your computer and use it in GitHub Desktop.
Save rodrigopedra/a64f27f4124d694e85809b68b93acd36 to your computer and use it in GitHub Desktop.
PHP Uploaded File Mimetype Detection
<?
if (check_file_type() && filesize($_FILES['file']['tmp_name']) < 2000000) {
submit();
} else {
display_form('Invalid file type, please upload files in Microsoft Word, OpenOffice or Adobe PDF format and keep them less than 2MB. If you are having trouble, please upload your document as RTF (Rich Text Format)');
}
function check_file_type() {
$mime_type = file_mime_type($_FILES['file']);
switch ($mime_type) {
case 'application/vnd.openxmlformats-officedocument.wordprocessingml.document': // .docx
case 'application/pdf': // .pdf
case 'application/msword':
case 'application/doc':
case 'application/rtf':
case 'application/x-rtf':
case 'text/richtext':
case 'text/rtf':
case 'application/zip':
case 'application/vnd.oasis.opendocument.text ': // .odt
case 'application/vnd.sun.xml.writer': {
return true;
}
default: {
return false;
}
}
}
/** https://github.com/EllisLab/CodeIgniter/blob/develop/system/libraries/Upload.php#L983
* 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
*/
function file_mime_type($file)
{
// We'll need this to validate the MIME info string (e.g. text/plain; charset=us-ascii)
$regexp = '/^([a-z\-]+\/[a-z0-9\-\.\+]+)(;\s.+)?$/';
/* Fileinfo extension - most reliable method
*
* Unfortunately, prior to PHP 5.3 - it's only available as a PECL extension and the
* more convenient FILEINFO_MIME_TYPE flag doesn't exist.
*/
if (function_exists('finfo_file'))
{
$finfo = finfo_open(FILEINFO_MIME);
if (is_resource($finfo)) // It is possible that a FALSE value is returned, if there is no magic MIME database file found on the system
{
$mime = @finfo_file($finfo, $file['tmp_name']);
finfo_close($finfo);
/* 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 (is_string($mime) && preg_match($regexp, $mime, $matches))
{
$file_type = $matches[1];
return $file_type;
}
}
}
/* 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'], and as it
* was reported in issue #750 (https://github.com/EllisLab/CodeIgniter/issues/750) - it's better
* than mime_content_type() as well, hence the attempts to try calling the command line with
* three different functions.
*
* Notes:
* - the DIRECTORY_SEPARATOR comparison ensures that we're not on a Windows system
* - many system admins would disable the exec(), shell_exec(), popen() and similar functions
* due to security concerns, hence the function_exists() checks
*/
if (DIRECTORY_SEPARATOR !== '\\')
{
$cmd = 'file --brief --mime ' . escapeshellarg($file['tmp_name']) . ' 2>&1';
if (function_exists('exec'))
{
/* This might look confusing, as $mime is being populated with all of the output when set in the second parameter.
* However, we only neeed the last line, which is the actual return value of exec(), and as such - it overwrites
* anything that could already be set for $mime previously. This effectively makes the second parameter a dummy
* value, which is only put to allow us to get the return status code.
*/
$mime = @exec($cmd, $mime, $return_status);
if ($return_status === 0 && is_string($mime) && preg_match($regexp, $mime, $matches))
{
$file_type = $matches[1];
return $file_type;
}
}
if ( (bool) @ini_get('safe_mode') === FALSE && function_exists('shell_exec'))
{
$mime = @shell_exec($cmd);
if (strlen($mime) > 0)
{
$mime = explode("\n", trim($mime));
if (preg_match($regexp, $mime[(count($mime) - 1)], $matches))
{
$file_type = $matches[1];
return $file_type;
}
}
}
if (function_exists('popen'))
{
$proc = @popen($cmd, 'r');
if (is_resource($proc))
{
$mime = @fread($proc, 512);
@pclose($proc);
if ($mime !== FALSE)
{
$mime = explode("\n", trim($mime));
if (preg_match($regexp, $mime[(count($mime) - 1)], $matches))
{
$file_type = $matches[1];
return $file_type;
}
}
}
}
}
// Fall back to the deprecated mime_content_type(), if available (still better than $_FILES[$field]['type'])
if (function_exists('mime_content_type'))
{
$file_type = @mime_content_type($file['tmp_name']);
if (strlen($file_type) > 0) // It's possible that mime_content_type() returns FALSE or an empty string
{
return $file_type;
}
}
return $file['type'];
}
?>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment