MediaFile loses dependency on PEAR::MIME

At the same time we remove the "filecommand" setting, since we will
likely not have use of it thanks to PECL fileinfo.

Also the "supported" list for attachment mime types has changed
format, so we can keep track of at least some known file extensions.
This commit is contained in:
Mikael Nordfeldth 2014-03-08 03:03:04 +01:00
parent 0eb38b8f84
commit 6faed0e451
5 changed files with 87 additions and 88 deletions

View File

@ -549,10 +549,6 @@ supported: an array of mime types you accept to store and distribute,
setup your server to properly recognize the types you want to
support.
uploads: false to disable uploading files with notices (true by default).
filecommand: The required MIME_Type library may need to use the 'file'
command. It tries the one in the Web server's path, but if
you're having problems with uploads, try setting this to the
correct value. Note: 'file' must accept '-b' and '-i' options.
For quotas, be sure you've set the upload_max_filesize and post_max_size
in php.ini to be large enough to handle your upload. In httpd.conf
@ -577,9 +573,6 @@ path: URL path, relative to the server, to find files. Defaults to
main path + '/file/'.
ssl: whether to use HTTPS for file URLs. Defaults to null, meaning to
guess based on other SSL settings.
filecommand: command to use for determining the type of a file. May be
skipped if fileinfo extension is installed. Defaults to
'/usr/bin/file'.
sslserver: if specified, this server will be used when creating HTTPS
URLs. Otherwise, the site SSL server will be used, with /file/ path.
sslpath: if this and the sslserver are specified, this path will be used

View File

@ -213,42 +213,42 @@ $default =
'sslserver' => null,
'sslpath' => null,
'ssl' => null,
'supported' => array('image/png',
'image/jpeg',
'image/gif',
'image/svg+xml',
'audio/mpeg',
'audio/x-speex',
'application/ogg',
'application/pdf',
'application/vnd.oasis.opendocument.text',
'application/vnd.oasis.opendocument.text-template',
'application/vnd.oasis.opendocument.graphics',
'application/vnd.oasis.opendocument.graphics-template',
'application/vnd.oasis.opendocument.presentation',
'application/vnd.oasis.opendocument.presentation-template',
'application/vnd.oasis.opendocument.spreadsheet',
'application/vnd.oasis.opendocument.spreadsheet-template',
'application/vnd.oasis.opendocument.chart',
'application/vnd.oasis.opendocument.chart-template',
'application/vnd.oasis.opendocument.image',
'application/vnd.oasis.opendocument.image-template',
'application/vnd.oasis.opendocument.formula',
'application/vnd.oasis.opendocument.formula-template',
'application/vnd.oasis.opendocument.text-master',
'application/vnd.oasis.opendocument.text-web',
'application/x-zip',
'application/zip',
'text/plain',
'video/mpeg',
'video/mp4',
'video/quicktime',
'video/webm'),
'supported' => array(
'application/vnd.oasis.opendocument.chart' => 'odc',
'application/vnd.oasis.opendocument.formula' => 'odf',
'application/vnd.oasis.opendocument.graphics' => 'odg',
'application/vnd.oasis.opendocument.graphics-template' => 'otg',
'application/vnd.oasis.opendocument.image' => 'odi',
'application/vnd.oasis.opendocument.presentation' => 'odp',
'application/vnd.oasis.opendocument.presentation-template' => 'otp',
'application/vnd.oasis.opendocument.spreadsheet' => 'ods',
'application/vnd.oasis.opendocument.spreadsheet-template' => 'ots',
'application/vnd.oasis.opendocument.text' => 'odt',
'application/vnd.oasis.opendocument.text-master' => 'odm',
'application/vnd.oasis.opendocument.text-template' => 'ott',
'application/vnd.oasis.opendocument.text-web' => 'oth',
'application/pdf' => 'pdf',
'application/zip' => 'zip',
'image/png' => 'png',
'image/jpeg' => 'jpg',
'image/gif' => 'gif',
'image/svg+xml' => 'svg',
'image/vnd.microsoft.icon' => 'ico',
'audio/ogg' => 'ogg',
'audio/mpeg' => 'mpg',
'audio/x-speex' => 'spx',
'application/ogg' => 'ogx',
'text/plain' => 'txt',
'video/mpeg' => 'mpeg',
'video/mp4' => 'mp4',
'video/ogg' => 'ogv',
'video/quicktime' => 'mov',
'video/webm' => 'webm',
),
'file_quota' => 5000000,
'user_quota' => 50000000,
'monthly_quota' => 15000000,
'uploads' => true,
'filecommand' => '/usr/bin/file',
'show_thumbs' => true, // show thumbnails in notice lists for uploaded images, and photos and videos linked remotely that provide oEmbed info
'thumb_width' => 100,
'thumb_height' => 75,

View File

@ -228,7 +228,7 @@ class MediaFile
// Throws exception if additional size does not respect quota
File::respectsQuota($scoped, $_FILES[$param]['size']);
$mimetype = MediaFile::getUploadedFileType($_FILES[$param]['tmp_name'],
$mimetype = self::getUploadedMimeType($_FILES[$param]['tmp_name'],
$_FILES[$param]['name']);
$basename = basename($_FILES[$param]['name']);
@ -252,7 +252,7 @@ class MediaFile
File::respectsQuota($scoped, filesize($stream['uri']));
$mimetype = MediaFile::getUploadedFileType($stream['uri']);
$mimetype = self::getUploadedMimeType($stream['uri']);
$filename = File::filename($scoped, "email", $mimetype);
@ -273,77 +273,58 @@ class MediaFile
/**
* Attempt to identify the content type of a given file.
*
* @param mixed $f file handle resource, or filesystem path as string
* @param string $filepath filesystem path as string (file must exist)
* @param string $originalFilename (optional) for extension-based detection
* @return string
*
* @fixme is this an internal or public method? It's called from GetFileAction
* @fixme this seems to tie a front-end error message in, kinda confusing
* @fixme this looks like it could return a PEAR_Error in some cases, if
* type can't be identified and $config['attachments']['supported'] is true
*
* @throws ClientException if type is known, but not supported for local uploads
*/
static function getUploadedFileType($f, $originalFilename=false) {
global $_PEAR;
require_once 'MIME/Type.php';
require_once 'MIME/Type/Extension.php';
// We have to disable auto handling of PEAR errors
$_PEAR->staticPushErrorHandling(PEAR_ERROR_RETURN);
$mte = new MIME_Type_Extension();
$cmd = &$_PEAR->getStaticProperty('MIME_Type', 'fileCmd');
$cmd = common_config('attachments', 'filecommand');
$filetype = null;
// If we couldn't get a clear type from the file extension,
// we'll go ahead and try checking the content. Content checks
// are unambiguous for most image files, but nearly useless
// for office document formats.
static function getUploadedMimeType($filepath, $originalFilename=false) {
// We only accept filenames to existing files
$filetype = MIME_Type::autoDetect($f);
$mimelookup = new finfo(FILEINFO_MIME_TYPE);
$mimetype = $mimelookup->file($filepath);
// The content-based sources for MIME_Type::autoDetect()
// are wildly unreliable for office-type documents. If we've
// gotten an unclear reponse back or just couldn't identify it,
// we'll try detecting a type from its extension...
// Unclear types are such that we can't really tell by the auto
// detect what they are (.bin, .exe etc. are just "octet-stream")
$unclearTypes = array('application/octet-stream',
'application/vnd.ms-office',
'application/zip',
// TODO: for XML we could do better content-based sniffing too
'text/xml');
if ($originalFilename && (!$filetype || in_array($filetype, $unclearTypes))) {
$type = $mte->getMIMEType($originalFilename);
if (is_string($type)) {
$filetype = $type;
$supported = common_config('attachments', 'supported');
// If we didn't match, or it is an unclear match
if ($originalFilename && (!$mimetype || in_array($mimetype, $unclearTypes))) {
$type = common_supported_ext_to_mime($originalFilename);
if (!empty($type)) {
return $type;
}
}
$supported = common_config('attachments', 'supported');
if ($supported === true || in_array($filetype, $supported)) {
// Restore PEAR error handlers for our DB code...
$_PEAR->staticPopErrorHandling();
return $filetype;
// If $config['attachments']['supported'] equals boolean true, accept any mimetype
if ($supported === true || array_key_exists($mimetype, $supported)) {
// FIXME: Don't know if it always has a mimetype here because
// finfo->file CAN return false on error: http://php.net/finfo_file
// so if $supported === true, this may return something unexpected.
return $mimetype;
}
$media = MIME_Type::getMedia($filetype);
// We can conclude that we have failed to get the MIME type
$media = common_get_mime_media($mimetype);
if ('application' !== $media) {
// TRANS: Client exception thrown trying to upload a forbidden MIME type.
// TRANS: %1$s is the file type that was denied, %2$s is the application part of
// TRANS: the MIME type that was denied.
$hint = sprintf(_('"%1$s" is not a supported file type on this server. ' .
'Try using another %2$s format.'), $filetype, $media);
'Try using another %2$s format.'), $mimetype, $media);
} else {
// TRANS: Client exception thrown trying to upload a forbidden MIME type.
// TRANS: %s is the file type that was denied.
$hint = sprintf(_('"%s" is not a supported file type on this server.'), $filetype);
$hint = sprintf(_('"%s" is not a supported file type on this server.'), $mimetype);
}
// Restore PEAR error handlers for our DB code...
$_PEAR->staticPopErrorHandling();
throw new ClientException($hint);
}
}

View File

@ -1797,6 +1797,31 @@ function common_accept_to_prefs($accept, $def = '*/*')
return $prefs;
}
// Match by our supported file extensions
function common_supported_ext_to_mime($fileext)
{
// Accept a filename and take out the extension
if (strpos($fileext, '.') !== false) {
$fileext = substr(strrchr($fileext, '.'), 1);
}
$supported = common_config('attachments', 'supported');
foreach($supported as $type => $ext) {
if ($ext === $fileext) {
return $type;
}
}
return false;
}
// The MIME "media" is the part before the slash (video in video/webm)
function common_get_mime_media($type)
{
$tmp = explode('/', $type);
return strtolower($tmp[0]);
}
function common_mime_type_match($type, $avail)
{
if(array_key_exists($type, $avail)) {

View File

@ -29,13 +29,13 @@ class MediaFileTest extends PHPUnit_Framework_TestCase
* @dataProvider fileTypeCases
*
*/
public function testFileType($filename, $expectedType)
public function testMimeType($filename, $expectedType)
{
if (!file_exists($filename)) {
throw new Exception("WTF? $filename test file missing");
}
$type = MediaFile::getUploadedFileType($filename, basename($filename));
$type = MediaFile::getUploadedMimeType($filename, basename($filename));
$this->assertEquals($expectedType, $type);
}
@ -43,7 +43,7 @@ class MediaFileTest extends PHPUnit_Framework_TestCase
* @dataProvider fileTypeCases
*
*/
public function testUploadedFileType($filename, $expectedType)
public function testUploadedMimeType($filename, $expectedType)
{
if (!file_exists($filename)) {
throw new Exception("WTF? $filename test file missing");
@ -52,7 +52,7 @@ class MediaFileTest extends PHPUnit_Framework_TestCase
fwrite($tmp, file_get_contents($filename));
$tmp_metadata = stream_get_meta_data($tmp);
$type = MediaFile::getUploadedFileType($tmp_metadata['uri'], basename($filename));
$type = MediaFile::getUploadedMimeType($tmp_metadata['uri'], basename($filename));
$this->assertEquals($expectedType, $type);
}