static function filename(Profile $profile, $origname, $mimetype)
{
- $ext = self::guessMimeExtension($mimetype);
+ $ext = self::guessMimeExtension($mimetype, $origname);
// Normalize and make the original filename more URL friendly.
$origname = basename($origname, ".$ext");
return $filename;
}
- static function guessMimeExtension($mimetype)
+ /**
+ * @param $mimetype The mimetype we've discovered for this file.
+ * @param $filename An optional filename which we can use on failure.
+ */
+ static function guessMimeExtension($mimetype, $filename=null)
{
+ $ext = null;
try {
+ // first see if we know the extension for our mimetype
$ext = common_supported_mime_to_ext($mimetype);
- } catch (Exception $e) {
- // We don't support this mimetype, but let's guess the extension
- $matches = array();
- if (!preg_match('/\/([a-z0-9]+)/', mb_strtolower($mimetype), $matches)) {
- throw new Exception('Malformed mimetype: '.$mimetype);
+ // we do, so use it!
+ return $ext;
+ } catch (Exception $e) { // FIXME: Make this exception more specific to "unknown mime=>ext relation"
+ // We don't know the extension for this mimetype, but let's guess.
+
+ // If we are very liberal with uploads ($config['attachments']['supported'] === true)
+ // then we try to do some guessing based on the filename, if it was supplied.
+ if (!is_null($filename) && common_config('attachments', 'supported')===true
+ && preg_match('/^.+\.([A-Za-z0-9]+)$/', $filename, $matches)) {
+ // we matched on a file extension, so let's see if it means something.
+ $ext = mb_strtolower($matches[1]);
+
+ $blacklist = common_config('attachments', 'extblacklist');
+ // If we got an extension from $filename we want to check if it's in a blacklist
+ // so we avoid people uploading .php files etc.
+ if (array_key_exists($ext, $blacklist)) {
+ if (!is_string($blacklist[$ext])) {
+ // we don't have a safe replacement extension
+ throw ClientException(_('Blacklisted file extension.'));
+ }
+ common_debug('Found replaced extension for filename '._ve($filename).': '._ve($ext));
+
+ // return a safe replacement extension ('php' => 'phps' for example)
+ return $blacklist[$ext];
+ }
+ // the attachment extension based on its filename was not blacklisted so it's ok to use it
+ return $ext;
}
- $ext = $matches[1];
}
- return mb_strtolower($ext);
+
+ // If nothing else has given us a result, try to extract it from
+ // the mimetype value (this turns .jpg to .jpeg for example...)
+ $matches = array();
+ if (!preg_match('/\/([a-z0-9]+)/', mb_strtolower($mimetype), $matches)) {
+ throw new Exception('Malformed mimetype: '.$mimetype);
+ }
+ $ext = mb_strtolower($matches[1]);
+ return $ext;
}
/**
'show_html' => false, // show (filtered) text/html attachments (and oEmbed HTML etc.). Doesn't affect AJAX calls.
'show_thumbs' => true, // show thumbnails in notice lists for uploaded images, and photos and videos linked remotely that provide oEmbed info
'process_links' => true, // check linked resources for embeddable photos and videos; this will hit referenced external web sites when processing new messages.
+ 'extblacklist' => [
+ 'php' => 'phps',
+ 'exe' => false, // this would deny any uploads to keep the "exe" file extension
+ ],
),
'thumbnail' =>
array('crop' => false, // overridden to true if thumb height === null
File::respectsQuota($scoped, $_FILES[$param]['size']);
$mimetype = self::getUploadedMimeType($_FILES[$param]['tmp_name'], $_FILES[$param]['name']);
+ $basename = basename($_FILES[$param]['name']);
switch (common_config('attachments', 'filename_base')) {
case 'upload':
- $basename = basename($_FILES[$param]['name']);
$filename = File::filename($scoped, $basename, $mimetype);
break;
case 'hash':
default:
- $filename = strtolower($filehash) . '.' . File::guessMimeExtension($mimetype);
+ $filename = strtolower($filehash) . '.' . File::guessMimeExtension($mimetype, $basename);
}
$filepath = File::path($filename);