* (bug 898) Mime type autodetection.

This commit is contained in:
Ævar Arnfjörð Bjarmason 2005-05-21 07:46:17 +00:00
parent 4ded646628
commit 27105c2129
17 changed files with 1752 additions and 350 deletions

View file

@ -166,6 +166,8 @@ Various bugfixes, small features, and a few experimental things:
* ...various...
* (bug 498) The Views heading in MonoBook.php is now localizable
* (bug 898) The wiki can now do advanced sanity check on uploaded files
including virus checks using external programs.
* (bug 2067) Fixed crash on empty quoted HTML attribute
* (bug 2079) Removed links to Special:Maintenance from movepagetext messages
* Fix for reading incorrectly re-gzipped HistoryBlob entries

View file

@ -110,6 +110,112 @@ $wgTmpDirectory = "{$wgUploadDirectory}/tmp";
$wgUploadBaseUrl = "";
/**#@-*/
/** internal name of virus scanner. This servers as a key to the $wgAntivirusSetup array.
* Set this to NULL to disable virus scanning. If not null, every file uploaded will be scanned for viruses.
* @global string $wgAntivirus
*/
$wgAntivirus= NULL;
/** Configuration for different virus scanners. This an associative array of associative arrays:
* it contains on setup array per known scanner type. The entry is selected by $wgAntivirus, i.e.
* valid values for $wgAntivirus are the keys defined in this array.
*
* The configuration array for each scanner contains the following keys: "command", "codemap", "messagepattern";
*
* "command" is the full command to call the virus scanner - %f will be replaced with the name of the
* file to scan. If not present, the filename will be appended to the command. Note that this must be
* overwritten if the scanner is not in the system path; in that case, plase set
* $wgAntivirusSetup[$wgAntivirus]['command'] to the desired command with full path.
*
* "codemap" is a mapping of exit code to return codes of the detectVirus function in SpecialUpload.
* An exit code mapped to AV_SCAN_FAILED causes the function to consider the scan to be failed. This will pass
* the file if $wgAntivirusRequired is not set.
* An exit code mapped to AV_SCAN_ABORTED causes the function to consider the file to have an usupported format,
* which is probably imune to virusses. This causes the file to pass.
* An exit code mapped to AV_NO_VIRUS will cause the file to pass, meaning no virus was found.
* All other codes (like AV_VIRUS_FOUND) will cause the function to report a virus.
* You may use "*" as a key in the array to catch all exit codes not mapped otherwise.
*
* "messagepattern" is a perl regular expression to extract the meaningful part of the scanners
* output. The relevant part should be matched as group one (\1).
* If not defined or the pattern does not match, the full message is shown to the user.
*
* @global array $wgAntivirusSetup
*/
$wgAntivirusSetup= array(
#setup for clamav
'clamav' => array (
'command' => "clamscan --no-summary ",
'codemap'=> array (
"0"=> AV_NO_VIRUS, #no virus
"1"=> AV_VIRUS_FOUND, #virus found
"52"=> AV_SCAN_ABORTED, #unsupported file format (probably imune)
"*"=> AV_SCAN_FAILED, #else scan failed
),
'messagepattern'=> '/.*?:(.*)/sim',
),
#setup for f-prot
'f-prot' => array (
'command' => "f-prot ",
'codemap'=> array (
"0"=> AV_NO_VIRUS, #no virus
"3"=> AV_VIRUS_FOUND, #virus found
"6"=> AV_VIRUS_FOUND, #virus found
"*"=> AV_SCAN_FAILED, #else scan failed
),
'messagepattern'=> '/.*?Infection:(.*)$/m',
),
);
/** Determines if a failed virus scan (AV_SCAN_FAILED) will cause the file to be rejected.
* @global boolean $wgAntivirusRequired
*/
$wgAntivirusRequired= true;
/** Determines if the mime type of uploaded files should be checked
* @global boolean $wgVerifyMimeType
*/
$wgVerifyMimeType= true;
/** Sets the mime type definition file to use by MimeMagic.php.
* @global string $wgMimeTypeFile
*/
#$wgMimeTypeFile= "/etc/mime.types";
$wgMimeTypeFile= "includes/mime.types";
#$wgMimeTypeFile= NULL; #use build in defaults only.
/** Sets the mime type info file to use by MimeMagic.php.
* @global string $wgMimeInfoFile
*/
$wgMimeInfoFile= "includes/mime.info";
#$wgMimeInfoFile= NULL; #use build in defaults only.
/** Switch for loading the FileInfo extension by PECL at runtime.
* This should be used only if fileinfo is installed as a shared object / dynamic libary
* @global string $wgLoadFileinfoExtension
*/
$wgLoadFileinfoExtension= false;
/** Sets an external mime detector program. The command must print only the mime type to standard output.
* the name of the file to process will be appended to the command given here.
* If not set or NULL, mime_content_type will be used if available.
* @global string $wgMimeTypeFile
*/
$wgMimeDetectorCommand= NULL; # use internal mime_content_type function, available since php 4.3.0
#$wgMimeDetectorCommand= "file -bi" #use external mime detector (linux)
/** Switch for trivial mime detection. Used by thumb.php to disable all fance things,
* because only a few types of images are needed and file extensions can be trusted.
*/
$wgTrivialMimeDetection= false;
/**
* Produce hashed HTML article paths. Used internally, do not set.
*/
@ -795,13 +901,25 @@ $wgFileExtensions = array( 'png', 'gif', 'jpg', 'jpeg' );
/** Files with these extensions will never be allowed as uploads. */
$wgFileBlacklist = array(
# HTML may contain cookie-stealing JavaScript and web bugs
'html', 'htm',
'html', 'htm', 'js', 'jsb',
# PHP scripts may execute arbitrary code on the server
'php', 'phtml', 'php3', 'php4', 'phps',
# Other types that may be interpreted by some servers
'shtml', 'jhtml', 'pl', 'py', 'cgi',
# May contain harmful executables for Windows victims
'exe', 'scr', 'dll', 'msi', 'vbs', 'bat', 'com', 'pif', 'cmd', 'vxd', 'cpl' );
/** Files with these mime types will never be allowed as uploads
* if $wgVerifyMimeType is enabled.
*/
$wgMimeTypeBlacklist= array(
# HTML may contain cookie-stealing JavaScript and web bugs
'text/html', 'text/javascript', 'text/x-javascript', 'application/x-shellscript',
# PHP scripts may execute arbitrary code on the server
'application/x-php', 'text/x-php',
# Other types that may be interpreted by some servers
'text/x-python', 'text/x-perl', 'text/x-bash', 'text/x-sh', 'text/x-csh'
);
/** This is a flag to determine whether or not to check file extensions on upload. */
$wgCheckFileExtensions = true;
@ -1314,4 +1432,24 @@ $wgCountCategorizedImagesAsUsed = false;
* CAUTION: Access to database might lead to code execution
*/
$wgExternalStores = false;
/**
* list of trusted media-types and mime types.
* Use the MEDIATYPE_xxx constants to represent media types.
* This list is used by Image::isSafeFile
*
* Types not listed here will have a warning about unsafe content
* displayed on the images description page. It would also be possible
* to use this for further restrictions, like disabling direct
* [[media:...]] links for non-trusted formats.
*/
$wgTrustedMediaFormats= array(
MEDIATYPE_BITMAP, //all bitmap formats
MEDIATYPE_AUDIO, //all audio formats
MEDIATYPE_VIDEO, //all plain video formats
"image/svg", //svg (only needed if inline rendering of svg is not supported)
"application/pdf", //PDF files
#"application/x-shockwafe-flash", //flash/shockwave movie
);
?>

View file

@ -89,4 +89,34 @@ define( 'CACHE_MEMCACHED', 2 ); // MemCached, must specify servers in $wgMemCac
define( 'CACHE_ACCEL', 3 ); // eAccelerator or Turck, whichever is available
/**#@-*/
/**#@+
* Media types.
* This defines constants for the value returned by Image::getMediaType()
*/
define( 'MEDIATYPE_UNKNOWN', 'UNKNOWN' ); // unknown format
define( 'MEDIATYPE_BITMAP', 'BITMAP' ); // some bitmap image or image source (like psd, etc). Can't scale up.
define( 'MEDIATYPE_DRAWING', 'DRAWING' ); // some vector drawing (SVG, WMF, PS, ...) or image source (oo-draw, etc). Can scale up.
define( 'MEDIATYPE_AUDIO', 'AUDIO' ); // simple audio file (ogg, mp3, wav, midi, whatever)
define( 'MEDIATYPE_VIDEO', 'VIDEO' ); // simple video file (ogg, mpg, etc; no not include formats here that may contain executable sections or scripts!)
define( 'MEDIATYPE_MULTIMEDIA', 'MULTIMEDIA' ); // Scriptable Multimedia (flash, advanced video container formats, etc)
define( 'MEDIATYPE_OFFICE', 'OFFICE' ); // Office Documents, Spreadsheets (office formats possibly containing apples, scripts, etc)
define( 'MEDIATYPE_TEXT', 'TEXT' ); // Plain text (possibly containing program code or scripts)
define( 'MEDIATYPE_EXECUTABLE', 'EXECUTABLE' ); // binary executable
define( 'MEDIATYPE_ARCHIVE', 'ARCHIVE' ); // archive file (zip, tar, etc)
/**#@-*/
/**#@+
* Antivirus result codes, for use in $wgAntivirusSetup.
*/
define( 'AV_NO_VIRUS', 0 ); #scan ok, no virus found
define( 'AV_VIRUS_FOUND', 1 ); #virus found!
define( 'AV_SCAN_ABORTED', -1 ); #scan aborted, the file is probably imune
define( 'AV_SCAN_FAILED', false ); #scan failed (scanner not found or error in scanner)
/**#@-*/
?>

View file

@ -1229,6 +1229,37 @@ function wfElementClean( $element, $attribs = array(), $contents = '') {
return wfElement( $element, $attribs, UtfNormal::cleanUp( $contents ) );
}
/** Global singleton instance of MimeMagic. This is initialized on demand,
* please always use the wfGetMimeMagic() function to get the instance.
*
* @private
*/
$wgMimeMagic= NULL;
/** Factory functions for the global MimeMagic object.
* This function always returns the same singleton instance of MimeMagic.
* That objects will be instantiated on the first call to this function.
* If needed, the MimeMagic.php file is automatically included by this function.
* @return MimeMagic the global MimeMagic objects.
*/
function &wfGetMimeMagic() {
global $wgMimeMagic;
if (!is_null($wgMimeMagic)) {
return $wgMimeMagic;
}
if (!class_exists("MimeMagic")) {
#include on demand
require_once("MimeMagic.php");
}
$wgMimeMagic= new MimeMagic();
return $wgMimeMagic;
}
/**
* Tries to get the system directory for temporary files.
* The TMPDIR, TMP, and TEMP environment variables are checked in sequence,

View file

@ -37,8 +37,9 @@ class Image
$width, # \
$height, # |
$bits, # --- returned by getimagesize (loadFromXxx)
$type, # |
$attr, # /
$type, # MEDIATYPE_xxx (bitmap, drawing, audio...)
$mime, # MIME type, determined by MimeMagic::guessMimeType
$size, # Size in bytes (loadFromXxx)
$metadata, # Metadata
$exif, # The Exif class
@ -111,7 +112,7 @@ class Image
// Check if the key existed and belongs to this version of MediaWiki
if (!empty($cachedValues) && is_array($cachedValues) && isset($cachedValues['width'])
&& $cachedValues['fileExists'] && isset( $cachedValues['metadata'] ) )
&& $cachedValues['fileExists'] && isset( $cachedValues['mime'] ) && isset( $cachedValues['metadata'] ) )
{
if ( $wgUseSharedUploads && $cachedValues['fromShared']) {
# if this is shared file, we need to check if image
@ -126,6 +127,7 @@ class Image
$this->height = $commonsCachedValues['height'];
$this->bits = $commonsCachedValues['bits'];
$this->type = $commonsCachedValues['type'];
$this->mime = $commonsCachedValues['mime'];
$this->metadata = $commonsCachedValues['metadata'];
$this->size = $commonsCachedValues['size'];
$this->fromSharedDirectory = true;
@ -142,6 +144,7 @@ class Image
$this->height = $cachedValues['height'];
$this->bits = $cachedValues['bits'];
$this->type = $cachedValues['type'];
$this->mime = $cachedValues['mime'];
$this->metadata = $cachedValues['metadata'];
$this->size = $cachedValues['size'];
$this->fromSharedDirectory = false;
@ -173,6 +176,7 @@ class Image
'height' => $this->height,
'bits' => $this->bits,
'type' => $this->type,
'mime' => $this->mime,
'metadata' => $this->metadata,
'size' => $this->size);
@ -190,7 +194,9 @@ class Image
$this->imagePath = $this->getFullPath();
$this->fileExists = file_exists( $this->imagePath );
$this->fromSharedDirectory = false;
$gis = false;
$gis = array();
if (!$this->fileExists) wfDebug("$fname: ".$this->imagePath." not found locally!\n");
# If the file is not found, and a shared upload directory is used, look for it there.
if (!$this->fileExists && $wgUseSharedUploads && $wgSharedUploadDirectory) {
@ -206,42 +212,67 @@ class Image
}
}
if ( $this->fileExists ) {
$magic=& wfGetMimeMagic();
$this->mime = $magic->guessMimeType($this->imagePath,true);
$this->type = $magic->getMediaType($this->imagePath,$this->mime);
# Get size in bytes
$this->size = filesize( $this->imagePath );
$magic=& wfGetMimeMagic();
# Height and width
# Don't try to get the width and height of sound and video files, that's bad for performance
if ( !Image::isKnownImageExtension( $this->extension ) ) {
$gis = false;
} elseif( $this->extension == 'svg' ) {
if( $this->mime == 'image/svg' ) {
wfSuppressWarnings();
$gis = wfGetSVGsize( $this->imagePath );
wfRestoreWarnings();
} else {
}
elseif ( !$magic->isPHPImageType( $this->mime ) ) {
# Don't try to get the width and height of sound and video files, that's bad for performance
$gis[0]= 0; //width
$gis[1]= 0; //height
$gis[2]= 0; //unknown
$gis[3]= ""; //width height string
}
else {
wfSuppressWarnings();
$gis = getimagesize( $this->imagePath );
wfRestoreWarnings();
}
wfDebug("$fname: ".$this->imagePath." loaded, ".$this->size." bytes, ".$this->mime.".\n");
}
if( $gis === false ) {
$this->width = 0;
$this->height = 0;
$this->bits = 0;
$this->type = 0;
$this->metadata = serialize ( array() ) ;
} else {
$this->width = $gis[0];
$this->height = $gis[1];
$this->type = $gis[2];
$this->metadata = serialize ( $this->retrieveExifData() ) ;
if ( isset( $gis['bits'] ) ) {
$this->bits = $gis['bits'];
} else {
$this->bits = 0;
}
else {
$gis[0]= 0; //width
$gis[1]= 0; //height
$gis[2]= 0; //unknown
$gis[3]= ""; //width height string
$this->mime = NULL;
$this->type = MEDIATYPE_UNKNOWN;
wfDebug("$fname: ".$this->imagePath." NOT FOUND!\n");
}
$this->width = $gis[0];
$this->height = $gis[1];
#NOTE: $gis[2] contains a code for the image type. This is no longer used.
#NOTE: we have to set this flag early to avoid load() to be called
# be some of the functions below. This may lead to recursion or other bad things!
# as ther's only one thread of execution, this should be safe anyway.
$this->dataLoaded = true;
if ($this->fileExists) $this->metadata = serialize ( $this->retrieveExifData() ) ;
else $this->metadata = serialize ( array() ) ;
if ( isset( $gis['bits'] ) ) $this->bits = $gis['bits'];
else $this->bits = 0;
wfProfileOut( $fname );
}
@ -254,8 +285,12 @@ class Image
wfProfileIn( $fname );
$dbr =& wfGetDB( DB_SLAVE );
$this->checkDBSchema($dbr);
$row = $dbr->selectRow( 'image',
array( 'img_size', 'img_width', 'img_height', 'img_bits', 'img_type' , 'img_metadata' ),
array( 'img_size', 'img_width', 'img_height', 'img_bits',
'img_media_type', 'img_major_mime', 'img_minor_mime', 'img_metadata' ),
array( 'img_name' => $this->name ), $fname );
if ( $row ) {
$this->fromSharedDirectory = false;
@ -263,7 +298,7 @@ class Image
$this->loadFromRow( $row );
$this->imagePath = $this->getFullPath();
// Check for rows from a previous schema, quietly upgrade them
if ( $this->type == -1 ) {
if ( is_null($this->type) ) {
$this->upgradeRow();
}
} elseif ( $wgUseSharedUploads && $wgSharedUploadDBname ) {
@ -273,7 +308,7 @@ class Image
$name = $wgLang->ucfirst($this->name);
$row = $dbr->selectRow( "`$wgSharedUploadDBname`.image",
array( 'img_size', 'img_width', 'img_height', 'img_bits', 'img_type' ),
array( 'img_size', 'img_width', 'img_height', 'img_bits', 'img_media_type', 'img_major_mime', 'img_minor_mime' ),
array( 'img_name' => $name ), $fname );
if ( $row ) {
$this->fromSharedDirectory = true;
@ -283,7 +318,7 @@ class Image
$this->loadFromRow( $row );
// Check for rows from a previous schema, quietly upgrade them
if ( $this->type == -1 ) {
if ( is_null($this->type) ) {
$this->upgradeRow();
}
}
@ -312,9 +347,20 @@ class Image
$this->width = $row->img_width;
$this->height = $row->img_height;
$this->bits = $row->img_bits;
$this->type = $row->img_type;
$this->type = $row->img_media_type;
$major= $row->img_major_mime;
$minor= $row->img_minor_mime;
if (!$major) $this->mime = "unknown/unknown";
else {
if (!$minor) $minor= "unknown";
$this->mime = $major.'/'.$minor;
}
$this->metadata = $row->img_metadata;
if ( $this->metadata == "" ) $this->metadata = serialize ( array() ) ;
$this->dataLoaded = true;
}
@ -355,12 +401,27 @@ class Image
// This avoids breaking replication in MySQL
$dbw->selectDB( $wgSharedUploadDBname );
}
$this->checkDBSchema($dbw);
if (strpos($this->mime,'/')!==false) {
list($major,$minor)= explode('/',$this->mime,2);
}
else {
$major= $this->mime;
$minor= "unknown";
}
wfDebug("$fname: upgrading ".$this->name." to 1.5 schema\n");
$dbw->update( 'image',
array(
'img_width' => $this->width,
'img_height' => $this->height,
'img_bits' => $this->bits,
'img_type' => $this->type,
'img_media_type' => $this->type,
'img_major_mime' => $major,
'img_minor_mime' => $minor,
'img_metadata' => $this->metadata,
), array( 'img_name' => $this->name ), $fname
);
@ -402,8 +463,14 @@ class Image
}
function getViewURL() {
if( $this->mustRender() ) {
return $this->createThumb( $this->getWidth() );
if( $this->mustRender()) {
if( $this->canRender() ) {
return $this->createThumb( $this->getWidth() );
}
else {
wfDebug('Image::getViewURL(): supposed to render '.$this->name.' ('.$this->mime."), but can't!\n");
return $this->getURL(); #hm... return NULL?
}
} else {
return $this->getURL();
}
@ -451,19 +518,157 @@ class Image
}
/**
* Return the type of the image
*
* - 1 GIF
* - 2 JPG
* - 3 PNG
* - 15 WBMP
* - 16 XBM
* Returns the mime type of the file.
*/
function getType() {
function getMimeType() {
$this->load();
return $this->mime;
}
/**
* Return the type of the media in the file.
* Use the value returned by this function with the MEDIATYPE_xxx constants.
*/
function getMediaType() {
$this->load();
return $this->type;
}
/**
* Checks if the file can be presented to the browser as a bitmap.
*
* Currently, this checks if the file is an image format
* that can be converted to a format
* supported by all browsers (namely GIF, PNG and JPEG),
* or if it is an SVG image and SVG conversion is enabled.
*
* @todo remember the result of this check.
*/
function canRender() {
global $wgUseImageMagick;
if( $this->getWidth()<=0 || $this->getHeight()<=0 ) return false;
$mime= $this->getMimeType();
if (!$mime || $mime==='unknown' || $mime==='unknown/unknown') return false;
#if it's SVG, check if ther's a converter enabled
if ($mime === 'image/svg') {
global $wgSVGConverters, $wgSVGConverter;
if ($wgSVGConverter && isset( $wgSVGConverters[$wgSVGConverter])) {
return true;
}
}
#image formats available on ALL browsers
if ( $mime === 'image/gif'
|| $mime === 'image/png'
|| $mime === 'image/jpeg' ) return true;
#image formats that can be converted to the above formats
if ($wgUseImageMagick) {
#convertable by ImageMagick (there are more...)
if ( $mime === 'image/vnd.wap.wbmp'
|| $mime === 'image/x-xbitmap'
|| $mime === 'image/x-xpixmap'
#|| $mime === 'image/x-icon' #file may be split into multiple parts
|| $mime === 'image/x-portable-anymap'
|| $mime === 'image/x-portable-bitmap'
|| $mime === 'image/x-portable-graymap'
|| $mime === 'image/x-portable-pixmap'
#|| $mime === 'image/x-photoshop' #this takes a lot of CPU and RAM!
|| $mime === 'image/x-rgb'
|| $mime === 'image/x-bmp'
|| $mime === 'image/tiff' ) return true;
}
else {
#convertable by the PHP GD image lib
if ( $mime === 'image/vnd.wap.wbmp'
|| $mime === 'image/x-xbitmap' ) return true;
}
return false;
}
/**
* Return true if the file is of a type that can't be directly
* rendered by typical browsers and needs to be re-rasterized.
*
* This returns true for everything but the bitmap types
* supported by all browsers, i.e. JPEG; GIF and PNG. It will
* also return true for any non-image formats.
*
* @return bool
*/
function mustRender() {
$mime= $this->getMimeType();
if ( $mime === "image/gif"
|| $mime === "image/png"
|| $mime === "image/jpeg" ) return false;
return true;
}
/**
* Determines if this media file may be shown inline on a page.
*
* This is currently synonymous to canRender(), but this could be
* extended to also allow inline display of other media,
* like flash animations or videos. If you do so, please keep in mind that
* that could be a scurity risc.
*/
function allowInlineDisplay() {
return $this->canRender();
}
/**
* Determines if this media file is in a format that is unlikely to contain viruses
* or malicious content. It uses the global $wgTrustedMediaFormats list to determine
* if the file is safe.
*
* This is used to show a warning on the description page of non-safe files.
* It may also be used to disallow direct [[media:...]] links to such files.
*
* Note that this function will always return ture if allowInlineDisplay()
* or isTrustedFile() is true for this file.
*/
function isSafeFile() {
if ($this->allowInlineDisplay()) return true;
if ($this->isTrustedFile()) return true;
global $wgTrustedMediaFormats;
$type= $this->getMediaType();
$mime= $this->getMimeType();
#wfDebug("Image::isSafeFile: type= $type, mime= $mime\n");
if (!$type || $type===MEDIATYPE_UNKNOWN) return false; #unknown type, not trusted
if ( in_array( $type, $wgTrustedMediaFormats) ) return true;
if ($mime==="unknown/unknown") return false; #unknown type, not trusted
if ( in_array( $mime, $wgTrustedMediaFormats) ) return true;
return false;
}
/** Returns true if the file is flagegd as trusted. Files flagged that way can be
* linked to directly, even if that is not allowed for this type of file normally.
*
* This is a dummy function right now and always returns false. It could be implemented
* to extract a flag from the database. The trusted flag could be set on upload, if the
* user has sufficient privileges, to bypass script- and html-filters. It may even be
* coupeled with cryptographics signatures or such.
*/
function isTrustedFile() {
#this could be implemented to check a flag in the databas,
#look for signatures, etc
return false;
}
/**
* Return the escapeLocalURL of this image
* @access public
@ -537,6 +742,9 @@ class Image
}
if ( $script ) {
$url = $script . '?f=' . urlencode( $this->name ) . '&w=' . urlencode( $width );
if( $this->mustRender() ) {
$url.= '&r=1';
}
} else {
$name = $this->thumbName( $width );
if($this->fromSharedDirectory) {
@ -567,9 +775,17 @@ class Image
*/
function thumbName( $width ) {
$thumb = $width."px-".$this->name;
if( $this->extension == 'svg' ) {
# Rasterize SVG vector images to PNG
$thumb .= '.png';
if( $this->mustRender() ) {
if( $this->canRender() ) {
# Rasterize to PNG (for SVG vector images, etc)
$thumb .= '.png';
}
else {
#should we use iconThumb here to get a symbolic thumbnail?
#or should we fail with an internal error?
return NULL; //can't make bitmap
}
}
return $thumb;
}
@ -611,18 +827,24 @@ class Image
return $this->renderThumb( $width );
}
$this->load();
if ( $width < $this->width ) {
$thumbheight = $this->height * $width / $this->width;
$thumbwidth = $width;
} else {
$thumbheight = $this->height;
$thumbwidth = $this->width;
if ($this->canRender()) {
if ( $width < $this->width ) {
$thumbheight = $this->height * $width / $this->width;
$thumbwidth = $width;
} else {
$thumbheight = $this->height;
$thumbwidth = $this->width;
}
if ( $thumbheight > $height ) {
$thumbwidth = $thumbwidth * $height / $thumbheight;
$thumbheight = $height;
}
$thumb = $this->renderThumb( $thumbwidth );
}
if ( $thumbheight > $height ) {
$thumbwidth = $thumbwidth * $height / $thumbheight;
$thumbheight = $height;
}
$thumb = $this->renderThumb( $thumbwidth );
else $thumb= NULL; #not a bitmap or renderable image, don't try.
if( is_null( $thumb ) ) {
$thumb = $this->iconThumb();
}
@ -672,7 +894,7 @@ class Image
}
# Sanity check $width
if( $width <= 0 ) {
if( $width <= 0 || $this->width <= 0) {
# BZZZT
return null;
}
@ -721,11 +943,13 @@ class Image
}
}
}
return new ThumbnailImage( $url, $width, $height, $thumbPath );
} // END OF function renderThumb
/**
* Really render a thumbnail
* Call this only for images for which canRender() returns true.
*
* @access private
*/
@ -735,7 +959,9 @@ class Image
$this->load();
if( $this->extension == 'svg' ) {
if( $this->mime === "image/svg" ) {
#Right now we have only SVG
global $wgSVGConverters, $wgSVGConverter;
if( isset( $wgSVGConverters[$wgSVGConverter] ) ) {
global $wgSVGConverterPath;
@ -743,8 +969,8 @@ class Image
array( '$path/', '$width', '$input', '$output' ),
array( $wgSVGConverterPath,
$width,
escapeshellarg( $this->imagePath ),
escapeshellarg( $thumbPath ) ),
wfEscapeShellArg( $this->imagePath ),
wfEscapeShellArg( $thumbPath ) ),
$wgSVGConverters[$wgSVGConverter] );
$conv = shell_exec( $cmd );
} else {
@ -756,8 +982,9 @@ class Image
# in Internet Explorer/Windows instead of default black.
$cmd = $wgImageMagickConvertCommand .
" -quality 85 -background white -geometry {$width} ".
escapeshellarg($this->imagePath) . " " .
escapeshellarg($thumbPath);
wfEscapeShellArg($this->imagePath) . " " .
wfEscapeShellArg($thumbPath);
wfDebug("reallyRenderThumb: running ImageMagick: $cmd");
$conv = shell_exec( $cmd );
} else {
# Use PHP's builtin GD library functions.
@ -814,6 +1041,7 @@ class Image
imagedestroy( $dst_image );
imagedestroy( $src_image );
}
#
# Check for zero-sized thumbnails. Those can be generated when
# no disk space is available or some other error occurs
@ -884,6 +1112,21 @@ class Image
wfPurgeSquidServers( $urls );
}
}
function checkDBSchema(&$db) {
# img_name must be unique
if ( !$db->indexUnique( 'image', 'img_name' ) && !$db->indexExists('image','PRIMARY') ) {
wfDebugDieBacktrace( 'Database schema not up to date, please run maintenance/archives/patch-image_name_unique.sql' );
}
#new fields must exist
if ( !$db->fieldExists( 'image', 'img_media_type' )
|| !$db->fieldExists( 'image', 'img_metadata' )
|| !$db->fieldExists( 'image', 'img_width' ) ) {
wfDebugDieBacktrace( 'Database schema not up to date, please run maintenance/updater.php' );
}
}
/**
* Return the image history of this image, line by line.
@ -898,6 +1141,9 @@ class Image
function nextHistoryLine() {
$fname = 'Image::nextHistoryLine()';
$dbr =& wfGetDB( DB_SLAVE );
$this->checkDBSchema($dbr);
if ( $this->historyLine == 0 ) {// called for the first time, return line from cur
$this->historyRes = $dbr->select( 'image',
array( 'img_size','img_description','img_user','img_user_text','img_timestamp', "'' AS oi_archive_name" ),
@ -926,16 +1172,6 @@ class Image
function resetHistory() {
$this->historyLine = 0;
}
/**
* Return true if the file is of a type that can't be directly
* rendered by typical browsers and needs to be re-rasterized.
* @return bool
*/
function mustRender() {
$this->load();
return ( $this->extension == 'svg' );
}
/**
* Return the full filesystem path to the file. Note that this does
@ -977,15 +1213,6 @@ class Image
return $shared ? $wgHashedSharedUploadDirectory : $wgHashedUploadDirectory;
}
/**
* @return bool
* @static
*/
function isKnownImageExtension( $ext ) {
static $extensions = array( 'svg', 'png', 'jpg', 'jpeg', 'gif', 'bmp', 'xbm' );
return in_array( $ext, $extensions );
}
/**
* Record an image upload in the upload log and the image table
*/
@ -996,16 +1223,14 @@ class Image
$fname = 'Image::recordUpload';
$dbw =& wfGetDB( DB_MASTER );
# img_name must be unique
if ( !$dbw->indexUnique( 'image', 'img_name' ) && !$dbw->indexExists('image','PRIMARY') ) {
wfDebugDieBacktrace( 'Database schema not up to date, please run maintenance/archives/patch-image_name_unique.sql' );
}
$this->checkDBSchema($dbw);
// Delete thumbnails and refresh the metadata cache
$this->purgeCache();
// Fail now if the image isn't there
if ( !$this->fileExists || $this->fromSharedDirectory ) {
wfDebug( "Image::recordUpload: File ".$this->imagePath." went missing!\n" );
return false;
}
@ -1019,6 +1244,15 @@ class Image
$now = $dbw->timestamp();
#split mime type
if (strpos($this->mime,'/')!==false) {
list($major,$minor)= explode('/',$this->mime,2);
}
else {
$major= $this->mime;
$minor= "unknown";
}
# Test to see if the row exists using INSERT IGNORE
# This avoids race conditions by locking the row until the commit, and also
# doesn't deadlock. SELECT FOR UPDATE causes a deadlock for every race condition.
@ -1029,7 +1263,9 @@ class Image
'img_width' => $this->width,
'img_height' => $this->height,
'img_bits' => $this->bits,
'img_type' => $this->type,
'img_media_type' => $this->type,
'img_major_mime' => $major,
'img_minor_mime' => $minor,
'img_timestamp' => $now,
'img_description' => $desc,
'img_user' => $wgUser->getID(),
@ -1059,14 +1295,13 @@ class Image
'oi_width' => 'img_width',
'oi_height' => 'img_height',
'oi_bits' => 'img_bits',
'oi_type' => 'img_type',
'oi_timestamp' => 'img_timestamp',
'oi_description' => 'img_description',
'oi_user' => 'img_user',
'oi_user_text' => 'img_user_text',
), array( 'img_name' => $this->name ), $fname
);
# Update the current image row
$dbw->update( 'image',
array( /* SET */
@ -1074,11 +1309,13 @@ class Image
'img_width' => $this->width,
'img_height' => $this->height,
'img_bits' => $this->bits,
'img_type' => $this->type,
'img_media_type' => $this->type,
'img_major_mime' => $major,
'img_minor_mime' => $minor,
'img_timestamp' => $now,
'img_description' => $desc,
'img_user' => $wgUser->getID(),
'img_user_text' => $wgUser->getName(),
'img_description' => $desc,
'img_metadata' => $this->metadata,
), array( /* WHERE */
'img_name' => $this->name
@ -1148,7 +1385,7 @@ class Image
* @return array
*/
function retrieveExifData () {
if ( $this->type !== '2' ) return array ();
if ( $this->getMimeType() !== "image/jpeg" ) return array ();
$exif = exif_read_data( $this->imagePath );
foreach($exif as $k => $v) {
@ -1208,6 +1445,9 @@ class Image
# Update EXIF data in database
$dbw =& wfGetDB( DB_MASTER );
$this->checkDBSchema($dbw);
$dbw->update( 'image',
array( 'img_metadata' => $this->metadata ),
array( 'img_name' => $this->name ),
@ -1450,7 +1690,7 @@ function wfIsBadImage( $name ) {
return array_key_exists( $name, $titleList );
}
/**

View file

@ -136,10 +136,14 @@ class ImagePage extends Article {
$sk = $wgUser->getSkin();
if ( $this->img->exists() ) {
if ( $this->img->getType() ) {
# image
$width = $this->img->getWidth();
$height = $this->img->getHeight();
$showLink = false;
if ( $this->img->allowInlineDisplay() and $width and $height) {
# image
$width = $this->img->getWidth();
$height = $this->img->getHeight();
# "Download high res version" link below the image
$msg = wfMsg('showbigimage', $width, $height, intval( $this->img->getSize()/1024 ) );
if ( $width > $maxWidth ) {
@ -150,7 +154,8 @@ class ImagePage extends Article {
$width = floor( $width * $maxHeight / $height );
$height = $maxHeight;
}
if ( $width != $this->img->getWidth() || $height != $this->img->getHeight() ) {
if ( !$this->img->mustRender()
&& ( $width != $this->img->getWidth() || $height != $this->img->getHeight() ) ) {
if( $wgUseImageResize ) {
$thumbnail = $this->img->getThumbnail( $width );
$url = $thumbnail->getUrl();
@ -163,14 +168,57 @@ class ImagePage extends Article {
$anchorclose = "</a><br />\n$anchoropen{$msg}</a>";
} else {
$url = $full_url;
$showLink = $this->img->mustRender();
}
$s = '<div class="fullImageLink" id="file">' . $anchoropen .
$wgOut->addHTML( '<div class="fullImageLink" id="file">' . $anchoropen .
"<img border=\"0\" src=\"{$url}\" width=\"{$width}\" height=\"{$height}\" alt=\"" .
htmlspecialchars( $wgRequest->getVal( 'image' ) ).'" />' . $anchorclose . '</div>';
htmlspecialchars( $wgRequest->getVal( 'image' ) ).'" />' . $anchorclose . '</div>' );
} else {
$s = "<div class=\"fullMedia\">" . $sk->makeMediaLink( $this->img->getName(),'' ) . '</div>';
#if direct link is allowed but it's not a renderable image, show an icon.
if ($this->img->isSafeFile()) {
$icon= $this->img->iconThumb();
$wgOut->addHTML( '<div class="fullImageLink" id="file"><a href="' . $full_url . '">' .
$icon->toHtml() .
'</a></div>' );
}
$showLink = true;
}
$wgOut->addHTML( $s );
if ($showLink) {
$s= $sk->makeMediaLink( $this->img->getName(), '', '', true );
$info= wfMsg( 'fileinfo', ceil($this->img->getSize()/1024.0), $this->img->getMimeType() );
if (!$this->img->isSafeFile()) {
$wgOut->addHTML("<div class=\"fullMedia\">");
$wgOut->addHTML("<span class=\"dangerousLink\">");
$wgOut->addHTML($s);
$wgOut->addHTML("</span>");
$wgOut->addHTML("<span class=\"fileInfo\"> (");
$wgOut->addWikiText( $info, false );
$wgOut->addHTML(")</span>");
$wgOut->addHTML("</div>");
#this should be formated a little nicer. Is CSS sufficient?
$wgOut->addHTML("<div class=\"mediaWarning\">");
$wgOut->addWikiText( wfMsg( 'mediawarning' ) );
$wgOut->addHTML('</div>');
} else {
$wgOut->addHTML("<div class=\"fullMedia\">");
$wgOut->addHTML($s);
$wgOut->addHTML("<span class=\"fileInfo\"> (");
$wgOut->addWikiText( $info, false );
$wgOut->addHTML(")</span>");
$wgOut->addHTML("</div>");
}
}
if($this->img->fromSharedDirectory) {
$sharedtext="<div class=\"sharedUploadNotice\">" . wfMsg("sharedupload");
if($wgRepositoryBaseUrl) {

657
includes/MimeMagic.php Normal file
View file

@ -0,0 +1,657 @@
<?php
/** Module defining helper functions for detecting and dealing with mime types.
*
* @package MediaWiki
*/
/** Defines a set of well known mime types
* This is used as a fallback to mime.types files.
* An extensive list of well known mime types is provided by
* the file mime.types in the includes directory.
*/
define('MM_WELL_KNOWN_MIME_TYPES',<<<END_STRING
application/ogg ogg ogm
application/pdf pdf
application/x-javascript js
application/x-shockwave-flash swf
audio/midi mid midi kar
audio/mpeg mpga mpa mp2 mp3
audio/x-aiff aif aiff aifc
audio/x-wav wav
audio/ogg ogg
image/x-bmp bmp
image/gif gif
image/jpeg jpeg jpg jpe
image/png png
image/svg+xml svg
image/tiff tiff tif
text/plain txt
text/html html htm
video/ogg ogm ogg
video/mpeg mpg mpeg
END_STRING
);
/** Defines a set of well known mime info entries
* This is used as a fallback to mime.info files.
* An extensive list of well known mime types is provided by
* the file mime.info in the includes directory.
*/
define('MM_WELL_KNOWN_MIME_INFO', <<<END_STRING
application/pdf [OFFICE]
text/javascript application/x-javascript [EXECUTABLE]
application/x-shockwave-flash [MULTIMEDIA]
audio/midi [AUDIO]
audio/x-aiff [AUDIO]
audio/x-wav [AUDIO]
audio/mp3 audio/mpeg [AUDIO]
application/ogg audio/ogg video/ogg [MULTIMEDIA]
image/x-bmp image/bmp [BITMAP]
image/gif [BITMAP]
image/jpeg [BITMAP]
image/png [BITMAP]
image/svg image/svg+xml [DRAWING]
image/tiff [BITMAP]
text/plain [TEXT]
text/html [TEXT]
video/ogg [VIDEO]
video/mpeg [VIDEO]
unknown/unknown application/octet-stream application/x-empty [UNKNOWN]
END_STRING
);
#note: because this file is possibly included by a function,
#we need to access the global scope explicitely!
global $wgLoadFileinfoExtension;
if ($wgLoadFileinfoExtension) {
if(!extension_loaded('fileinfo')) dl('fileinfo.' . PHP_SHLIB_SUFFIX);
}
/** Implements functions related to mime types such as detection and mapping to
* file extension,
*
* Instances of this class are stateles, there only needs to be one global instance
* of MimeMagic. Please use wfGetMimeMagic to get that instance.
*/
class MimeMagic {
/**
* Mapping of media types to arrays of mime types.
* This is used by findMediaType and getMediaType, respectively
*/
var $mMediaTypes= NULL;
/** Map of mime type aliases
*/
var $mMimeTypeAliases= NULL;
/** map of mime types to file extensions (as a space seprarated list)
*/
var $mMimeToExt= NULL;
/** map of file extensions types to mime types (as a space seprarated list)
*/
var $mExtToMime= NULL;
/** Initializes the MimeMagic object. This is called by wfGetMimeMagic when instantiation
* the global MimeMagic singleton object.
*
* This constructor parses the mime.types and mime.info files and build internal mappings.
*/
function MimeMagic() {
/*
* --- load mime.types ---
*/
global $wgMimeTypeFile;
$types= MM_WELL_KNOWN_MIME_TYPES;
if ($wgMimeTypeFile) {
if (is_file($wgMimeTypeFile) and is_readable($wgMimeTypeFile)) {
wfDebug("MimeMagic::MimeMagic: loading mime types from $wgMimeTypeFile\n");
$types.= "\n";
$types.= file_get_contents($wgMimeTypeFile);
}
else wfDebug("MimeMagic::MimeMagic: can't load mime types from $wgMimeTypeFile\n");
}
else wfDebug("MimeMagic::MimeMagic: no mime types file defined, using build-ins only.\n");
$types= str_replace(array("\r\n","\n\r","\n\n","\r\r","\r"),"\n",$types);
$types= str_replace("\t"," ",$types);
$this->mMimeToExt= array();
$this->mToMime= array();
$lines= explode("\n",$types);
foreach ($lines as $s) {
$s= trim($s);
if (empty($s)) continue;
if (strpos($s,'#')===0) continue;
$s= strtolower($s);
$i= strpos($s,' ');
if ($i===false) continue;
#print "processing MIME line $s<br>";
$mime= substr($s,0,$i);
$ext= trim(substr($s,$i+1));
if (empty($ext)) continue;
if (@$this->mMimeToExt[$mime]) $this->mMimeToExt[$mime] .= ' '.$ext;
else $this->mMimeToExt[$mime]= $ext;
$extensions= explode(' ',$ext);
foreach ($extensions as $e) {
$e= trim($e);
if (empty($e)) continue;
if (@$this->mExtToMime[$e]) $this->mExtToMime[$e] .= ' '.$mime;
else $this->mExtToMime[$e]= $mime;
}
}
/*
* --- load mime.info ---
*/
global $wgMimeInfoFile;
$info= MM_WELL_KNOWN_MIME_INFO;
if ($wgMimeInfoFile) {
if (is_file($wgMimeInfoFile) and is_readable($wgMimeInfoFile)) {
wfDebug("MimeMagic::MimeMagic: loading mime info from $wgMimeInfoFile\n");
$info.= "\n";
$info.= file_get_contents($wgMimeInfoFile);
}
else wfDebug("MimeMagic::MimeMagic: can't load mime info from $wgMimeInfoFile\n");
}
else wfDebug("MimeMagic::MimeMagic: no mime info file defined, using build-ins only.\n");
$info= str_replace(array("\r\n","\n\r","\n\n","\r\r","\r"),"\n",$info);
$info= str_replace("\t"," ",$info);
$this->mMimeTypeAliases= array();
$this->mMediaTypes= array();
$lines= explode("\n",$info);
foreach ($lines as $s) {
$s= trim($s);
if (empty($s)) continue;
if (strpos($s,'#')===0) continue;
$s= strtolower($s);
$i= strpos($s,' ');
if ($i===false) continue;
#print "processing MIME INFO line $s<br>";
$match= array();
if (preg_match('!\[\s*(\w+)\s*\]!',$s,$match)) {
$s= preg_replace('!\[\s*(\w+)\s*\]!','',$s);
$mtype= trim(strtoupper($match[1]));
}
else $mtype= MEDIATYPE_UNKNOWN;
$m= explode(' ',$s);
if (!isset($this->mMediaTypes[$mtype])) $this->mMediaTypes[$mtype]= array();
foreach ($m as $mime) {
$mime= trim($mime);
if (empty($mime)) continue;
$this->mMediaTypes[$mtype][]= $mime;
}
if (sizeof($m)>1) {
$main= $m[0];
for ($i=1; $i<sizeof($m); $i+= 1) {
$mime= $m[$i];
$this->mMimeTypeAliases[$mime]= $main;
}
}
}
}
/** returns a list of file extensions for a given mime type
* as a space separated string.
*/
function getExtensionsForType($mime) {
$mime= strtolower($mime);
$r= @$this->mMimeToExt[$mime];
if (@!$r and isset($this->mMimeTypeAliases[$mime])) {
$mime= $this->mMimeTypeAliases[$mime];
$r= @$this->mMimeToExt[$mime];
}
return $r;
}
/** returns a list of mime types for a given file extension
* as a space separated string.
*/
function getTypesForExtension($ext) {
$ext= strtolower($ext);
$r= @$this->mExtToMime[$ext];
return $r;
}
/** returns a single mime type for a given file extension.
* This is always the first type from the list returned by getTypesForExtension($ext).
*/
function guessTypesForExtension($ext) {
$m= $this->getTypesForExtension( $ext );
if( is_null($m) ) return NULL;
$m= trim( $m );
$m= preg_replace('/\s.*$/','',$m);
return $m;
}
/** tests if the extension matches the given mime type.
* returns true if a match was found, NULL if the mime type is unknown,
* and false if the mime type is known but no matches where found.
*/
function isMatchingExtension($extension,$mime) {
$ext= $this->getExtensionsForType($mime);
if (!$ext) {
return NULL; //unknown
}
$ext= explode(' ',$ext);
$extension= strtolower($extension);
if (in_array($extension,$ext)) {
return true;
}
return false;
}
/** returns true if the mime type is known to represent
* an image format supported by the PHP GD library.
*/
function isPHPImageType( $mime ) {
#as defined by imagegetsize and image_type_to_mime
static $types = array(
'image/gif', 'image/jpeg', 'image/png',
'image/x-bmp', 'image/xbm', 'image/tiff',
'image/jp2', 'image/jpeg2000', 'image/iff',
'image/xbm', 'image/x-xbitmap',
'image/vnd.wap.wbmp', 'image/vnd.xiff',
'image/x-photoshop',
'application/x-shockwave-flash',
);
return in_array( $mime, $types );
}
/** mime type detection. This uses detectMimeType to detect the mim type of the file,
* but applies additional checks to determine some well known file formats that may be missed
* or misinterpreter by the default mime detection (namely xml based formats like XHTML or SVG).
*
* @param string $file The file to check
* @param bool $useExt switch for allowing to use the file extension to guess the mime type. true by default.
*
* @return string the mime type of $file
*/
function guessMimeType( $file, $useExt=true ) {
$fname = 'MimeMagic::guessMimeType';
$mime= $this->detectMimeType($file,$useExt);
if (strpos($mime,"text/")===0 ||
$mime==="application/xml") {
// Read a chunk of the file
$f = fopen( $file, "rt" );
if( !$f ) return "unknown/unknown";
$head = fread( $f, 1024 );
fclose( $f );
$xml_type= NULL;
$script_type= NULL;
/*
* look for XML formats (XHTML and SVG)
*/
if ($mime==="text/sgml" ||
$mime==="text/plain" ||
$mime==="text/html" ||
$mime==="text/xml" ||
$mime==="application/xml") {
if (substr($head,0,5)=="<?xml") $xml_type= "ASCII";
elseif (substr($head,0,8)=="\xef\xbb\xbf<?xml") $xml_type= "UTF-8";
elseif (substr($head,0,10)=="\xfe\xff\x00<\x00?\x00x\x00m\x00l") $xml_type= "UTF-16BE";
elseif (substr($head,0,10)=="\xff\xfe<\x00?\x00x\x00m\x00l\x00") $xml_type= "UTF-16LE";
if ($xml_type) {
if ($xml_type!=="UTF-8" && $xml_type!=="ASCII") $head= iconv($xml_type,"ASCII//IGNORE",$head);
$match= array();
$doctype= "";
$tag= "";
if (preg_match('%<!DOCTYPE\s+[\w-]+\s+PUBLIC\s+["'."'".'"](.*?)["'."'".'"].*>%sim',$head,$match)) $doctype= $match[1];
if (preg_match('%<(\w+).*>%sim',$head,$match)) $tag= $match[1];
#print "<br>ANALYSING $file ($mime): doctype= $doctype; tag= $tag<br>";
if (strpos($doctype,"-//W3C//DTD SVG")===0) $mime= "image/svg";
elseif ($tag==="svg") $mime= "image/svg";
elseif (strpos($doctype,"-//W3C//DTD XHTML")===0) $mime= "text/html";
elseif ($tag==="html") $mime= "text/html";
$test_more= false;
}
}
/*
* look for shell scripts
*/
if (!$xml_type) {
$script_type= NULL;
#detect by shebang
if (substr($head,0,2)=="#!") $script_type= "ASCII";
elseif (substr($head,0,5)=="\xef\xbb\xbf#!") $script_type= "UTF-8";
elseif (substr($head,0,7)=="\xfe\xff\x00#\x00!") $script_type= "UTF-16BE";
elseif (substr($head,0,7)=="\xff\xfe#\x00!") $script_type= "UTF-16LE";
if ($script_type) {
if ($script_type!=="UTF-8" && $script_type!=="ASCII") $head= iconv($script_type,"ASCII//IGNORE",$head);
$match= array();
$prog= "";
if (preg_match('%/?([^\s]+/)(w+)%sim',$head,$match)) $script= $match[2];
$mime= "application/x-$prog";
}
}
/*
* look for PHP
*/
if( !$xml_type && !$script_type ) {
if( ( strpos( $head, '<?php' ) !== false ) ||
( strpos( $head, '<? ' ) !== false ) ||
( strpos( $head, "<?\n" ) !== false ) ||
( strpos( $head, "<?\t" ) !== false ) ||
( strpos( $head, "<?=" ) !== false ) ||
( strpos( $head, "<\x00?\x00p\x00h\x00p" ) !== false ) ||
( strpos( $head, "<\x00?\x00 " ) !== false ) ||
( strpos( $head, "<\x00?\x00\n" ) !== false ) ||
( strpos( $head, "<\x00?\x00\t" ) !== false ) ||
( strpos( $head, "<\x00?\x00=" ) !== false ) ) {
$mime= "application/x-php";
}
}
}
if (isset($this->mMimeTypeAliases[$mime])) $mime= $this->mMimeTypeAliases[$mime];
wfDebug("$fname: final mime type of $file: $mime\n");
return $mime;
}
/** Internal mime type detection, please use guessMimeType() for application code instead.
* Detection is done using an external program, if $wgMimeDetectorCommand is set.
* Otherwise, the fileinfo extension and mime_content_type are tried (in this order), if they are available.
* If the dections fails and $useExt is true, the mime type is guessed from the file extension, using guessTypesForExtension.
* If the mime type is still unknown, getimagesize is used to detect the mime type if the file is an image.
* If no mime type can be determined, this function returns "unknown/unknown".
*
* @param string $file The file to check
* @param bool $useExt switch for allowing to use the file extension to guess the mime type. true by default.
*
* @return string the mime type of $file
* @private
*/
function detectMimeType( $file, $useExt=true ) {
$fname = 'MimeMagic::detectMimeType';
global $wgMimeDetectorCommand;
$m= NULL;
if ($wgMimeDetectorCommand) {
$fn= wfEscapeShellArg($file);
$m= `$wgMimeDetectorCommand $fn`;
}
else if (function_exists("finfo_open") && function_exists("finfo_file")) {
# This required the fileinfo extension by PECL,
# see http://pecl.php.net/package/fileinfo
# This must be compiled into PHP
#
# finfo is the official replacement for the deprecated
# mime_content_type function, see below.
#
# If you may need to load the fileinfo extension at runtime, set
# $wgLoadFileinfoExtension in LocalSettings.php
$mime_magic_resource = finfo_open(FILEINFO_MIME); /* return mime type ala mimetype extension */
if ($mime_magic_resource) {
$m= finfo_file($mime_magic_resource, $file);
finfo_close($mime_magic_resource);
}
else wfDebug("$fname: finfo_open failed on ".FILEINFO_MIME."!\n");
}
else if (function_exists("mime_content_type")) {
# NOTE: this function is available since PHP 4.3.0, but only if
# PHP was compiled with --with-mime-magic or, before 4.3.2, with --enable-mime-magic.
#
# On Winodws, you must set mime_magic.magicfile in php.ini to point to the mime.magic file bundeled with PHP;
# sometimes, this may even be needed under linus/unix.
#
# Also note that this has been DEPRECATED in favor of the fileinfo extension by PECL, see above.
# see http://www.php.net/manual/en/ref.mime-magic.php for details.
$m= mime_content_type($file);
}
else wfDebug("$fname: no magic mime detector found!\n");
if ($m) {
#normalize
$m= preg_replace('![;, ].*$!','',$m); #strip charset, etc
$m= trim($m);
$m= strtolower($m);
if (strpos($m,'unknown')!==false) $m= NULL;
else {
wfDebug("$fname: magic mime type of $file: $m\n");
return $m;
}
}
#if still not known, use getimagesize to find out the type of image
#TODO: skip things that do not have a well-known image extension? Would that be safe?
wfSuppressWarnings();
$gis = getimagesize( $file );
wfRestoreWarnings();
$notAnImage= false;
if ($gis && is_array($gis) && $gis[2]) {
switch ($gis[2]) {
case IMAGETYPE_GIF: $m= "image/gif"; break;
case IMAGETYPE_JPEG: $m= "image/jpeg"; break;
case IMAGETYPE_PNG: $m= "image/png"; break;
case IMAGETYPE_SWF: $m= "application/x-shockwave-flash"; break;
case IMAGETYPE_PSD: $m= "application/photoshop"; break;
case IMAGETYPE_BMP: $m= "image/bmp"; break;
case IMAGETYPE_TIFF_II: $m= "image/tiff"; break;
case IMAGETYPE_TIFF_MM: $m= "image/tiff"; break;
case IMAGETYPE_JPC: $m= "image"; break;
case IMAGETYPE_JP2: $m= "image/jpeg2000"; break;
case IMAGETYPE_JPX: $m= "image/jpeg2000"; break;
case IMAGETYPE_JB2: $m= "image"; break;
case IMAGETYPE_SWC: $m= "application/x-shockwave-flash"; break;
case IMAGETYPE_IFF: $m= "image/vnd.xiff"; break;
case IMAGETYPE_WBMP: $m= "image/vnd.wap.wbmp"; break;
case IMAGETYPE_XBM: $m= "image/x-xbitmap"; break;
}
if ($m) {
wfDebug("$fname: image mime type of $file: $m\n");
return $m;
}
else $notAnImage= true;
}
#if desired, look at extension as a fallback.
if ($useExt) {
$i = strrpos( $file, '.' );
$e= strtolower( $i ? substr( $file, $i + 1 ) : '' );
$m= $this->guessTypesForExtension($e);
#TODO: if $notAnImage is set, do not trust the file extension if
# the results is one of the image types that should have been recognized
# by getimagesize
if ($m) {
wfDebug("$fname: extension mime type of $file: $m\n");
return $m;
}
}
#unknown type
wfDebug("$fname: failed to guess mime type for $file!\n");
return "unknown/unknown";
}
/**
* Determine the media type code for a file, using its mime type, name and possibly
* its contents.
*
* This function relies on the findMediaType(), mapping extensions and mime
* types to media types.
*
* @todo analyse file if need be
* @todo look at multiple extension, separately and together.
*
* @param string $path full path to the image file, in case we have to look at the contents
* (if null, only the mime type is used to determine the media type code).
* @param string $mime mime type. If null it will be guessed using guessMimeType.
*
* @return (int?string?) a value to be used with the MEDIATYPE_xxx constants.
*/
function getMediaType($path=NULL,$mime=NULL) {
if( !$mime && !$path ) return MEDIATYPE_UNKNOWN;
#if mime type is unknown, guess it
if( !$mime ) $mime= $this->guessMimeType($path,false);
#special code for ogg - detect if it's video (theora),
#else label it as sound.
if( $mime=="application/ogg" && file_exists($path) ) {
// Read a chunk of the file
$f = fopen( $path, "rt" );
if( !$f ) return MEDIATYPE_UNKNOWN;
$head = fread( $f, 256 );
fclose( $f );
$head= strtolower( $head );
#This is an UGLY HACK, file should be parsed correctly
if( strpos($head,'theora')!==false ) return MEDIATYPE_VIDEO;
elseif( strpos($head,'vorbis')!==false ) return MEDIATYPE_AUDIO;
elseif( strpos($head,'flac')!==false ) return MEDIATYPE_AUDIO;
elseif( strpos($head,'speex')!==false ) return MEDIATYPE_AUDIO;
else return MEDIATYPE_MULTIMEDIA;
}
#check for entry for full mime type
if( $mime ) {
$type= $this->findMediaType($mime);
if( $type!==MEDIATYPE_UNKNOWN ) return $type;
}
#check for entry for file extension
$e= NULL;
if( $path ) {
$i = strrpos( $path, '.' );
$e= strtolower( $i ? substr( $path, $i + 1 ) : '' );
#TODO: look at multi-extension if this fails, parse from full path
$type= $this->findMediaType('.'.$e);
if( $type!==MEDIATYPE_UNKNOWN ) return $type;
}
#check major mime type
if( $mime ) {
$i= strpos($mime,'/');
if( $i !== false ) {
$major= substr($mime,0,$i);
$type= $this->findMediaType($major);
if( $type!==MEDIATYPE_UNKNOWN ) return $type;
}
}
if( !$type ) $type= MEDIATYPE_UNKNOWN;
return $type;
}
/** returns a media code matching the given mime type or file extension.
* File extensions are represented by a string starting with a dot (.) to
* distinguish them from mime types.
*
* This funktion relies on the mapping defined by $this->mMediaTypes
* @private
*/
function findMediaType($extMime) {
if (strpos($extMime,'.')===0) { #if it's an extension, look up the mime types
$m= $this->getTypesForExtension(substr($extMime,1));
if (!$m) return MEDIATYPE_UNKNOWN;
$m= explode(' ',$m);
}
else { #normalize mime type
if (isset($this->mMimeTypeAliases[$extMime])) {
$extMime= $this->mMimeTypeAliases[$extMime];
}
$m= array($extMime);
}
foreach ($m as $mime) {
foreach ($this->mMediaTypes as $type => $codes) {
if (in_array($mime,$codes,true)) return $type;
}
}
return MEDIATYPE_UNKNOWN;
}
}
?>

View file

@ -207,7 +207,7 @@ class UploadForm {
return $this->uploadError( wfMsg( 'protectedpage' ) );
}
/* Don't allow users to override the blacklist */
/* Don't allow users to override the blacklist (check file extension) */
global $wgStrictFileExtensions;
global $wgFileExtensions, $wgFileBlacklist;
if( $this->checkFileExtensionList( $ext, $wgFileBlacklist ) ||
@ -221,8 +221,12 @@ class UploadForm {
* type but it's corrupt or data of the wrong type, we should
* probably not accept it.
*/
if( !$this->mStashed && !$this->verify( $this->mUploadTempName, $finalExt ) ) {
return $this->uploadError( wfMsg( 'uploadcorrupt' ) );
if( !$this->mStashed ) {
$veri= $this->verify($this->mUploadTempName, $finalExt);
if( $veri !== true ) { //it's a wiki error...
return $this->uploadError( $veri->toString() );
}
}
/**
@ -309,6 +313,8 @@ class UploadForm {
function saveUploadedFile( $saveName, $tempName, $useRename = false ) {
global $wgUploadDirectory, $wgOut;
$fname= "SpecialUpload::saveUploadedFile";
$dest = wfImageDir( $saveName );
$archive = wfImageArchiveDir( $saveName );
$this->mSavedFile = "{$dest}/{$saveName}";
@ -324,7 +330,9 @@ class UploadForm {
"${archive}/{$this->mUploadOldVersion}" );
return false;
}
} else {
else wfDebug("$fname: moved file ".$this->mSavedFile." to ${archive}/{$this->mUploadOldVersion}\n");
}
else {
$this->mUploadOldVersion = '';
}
@ -336,6 +344,8 @@ class UploadForm {
if( ! $success ) {
$wgOut->fileCopyError( $tempName, $this->mSavedFile );
return false;
} else {
wfDebug("$fname: wrote tempfile $tempName to ".$this->mSavedFile."\n");
}
} else {
wfSuppressWarnings();
@ -346,7 +356,9 @@ class UploadForm {
$wgOut->fileCopyError( $tempName, $this->mSavedFile );
return false;
}
else wfDebug("$fname: wrote tempfile $tempName to ".$this->mSavedFile."\n");
}
chmod( $this->mSavedFile, 0644 );
return true;
}
@ -640,86 +652,47 @@ class UploadForm {
}
/**
* Returns false if the file is of a known type but can't be recognized,
* indicating a corrupt file.
* Returns true otherwise; unknown file types are not checked if given
* with an unrecognized extension.
* Verifies that it's ok to include the uploaded file
*
* @param string $tmpfile Pathname to the temporary upload file
* @param string $tmpfile the full path opf the temporary file to verify
* @param string $extension The filename extension that the file is to be served with
* @return bool
* @return mixed true of the file is verified, a WikiError object otherwise.
*/
function verify( $tmpfile, $extension ) {
if( $this->triggersIEbug( $tmpfile ) ||
$this->triggersSafariBug( $tmpfile ) ) {
return false;
#magically determine mime type
$magic=& wfGetMimeMagic();
$mime= $magic->guessMimeType($tmpfile,false);
$fname= "SpecialUpload::verify";
#check mime type, if desired
global $wgVerifyMimeType;
if ($wgVerifyMimeType) {
#check mime type against file extension
if( !$this->verifyExtension( $mime, $extension ) ) {
return new WikiErrorMsg( 'uploadcorrupt' );
}
#check mime type blacklist
global $wgMimeTypeBlacklist;
if( isset($wgMimeTypeBlacklist) && !is_null($wgMimeTypeBlacklist)
&& $this->checkFileExtension( $mime, $wgMimeTypeBlacklist ) ) {
return new WikiErrorMsg( 'badfiletype', htmlspecialchars( $mime ) );
}
}
#check for htmlish code and javascript
if( $this->detectScript ( $tmpfile, $mime ) ) {
return new WikiErrorMsg( 'uploadscripted' );
}
$fname = 'SpecialUpload::verify';
$mergeExtensions = array(
'jpg' => 'jpeg',
'tif' => 'tiff' );
$extensionTypes = array(
# See http://www.php.net/getimagesize
1 => 'gif',
2 => 'jpeg',
3 => 'png',
4 => 'swf',
5 => 'psd',
6 => 'bmp',
7 => 'tiff',
8 => 'tiff',
9 => 'jpc',
10 => 'jp2',
11 => 'jpx',
12 => 'jb2',
13 => 'swc',
14 => 'iff',
15 => 'wbmp',
16 => 'xbm' );
$extension = strtolower( $extension );
if( isset( $mergeExtensions[$extension] ) ) {
$extension = $mergeExtensions[$extension];
}
wfDebug( "$fname: Testing file '$tmpfile' with given extension '$extension'\n" );
if( !in_array( $extension, $extensionTypes ) ) {
# Not a recognized image type. We don't know how to verify these.
# They're allowed by policy or they wouldn't get this far, so we'll
# let them slide for now.
wfDebug( "$fname: Unknown extension; passing.\n" );
return true;
}
wfSuppressWarnings();
$data = getimagesize( $tmpfile );
wfRestoreWarnings();
if( false === $data ) {
# Didn't recognize the image type.
# Either the image is corrupt or someone's slipping us some
# bogus data such as HTML+JavaScript trying to take advantage
# of an Internet Explorer security flaw.
wfDebug( "$fname: getimagesize() doesn't recognize the file; rejecting.\n" );
return false;
}
$imageType = $data[2];
if( !isset( $extensionTypes[$imageType] ) ) {
# Now we're kind of confused. Perhaps new image types added
# to PHP's support that we don't know about.
# We'll let these slide for now.
wfDebug( "$fname: getimagesize() knows the file, but we don't recognize the type; passing.\n" );
return true;
}
$ext = strtolower( $extension );
if( $extension != $extensionTypes[$imageType] ) {
# The given filename extension doesn't match the
# file type. Probably just a mistake, but it's a stupid
# one and we shouldn't let it pass. KILL THEM!
wfDebug( "$fname: file extension does not match recognized type; rejecting.\n" );
return false;
/**
* Scan the uploaded file for viruses
*/
$virus= $this->detectVirus($tmpfile);
if ( $virus ) {
return new WikiErrorMsg( 'uploadvirus', htmlspecialchars($virus) );
}
wfDebug( "$fname: all clear; passing.\n" );
@ -727,66 +700,219 @@ class UploadForm {
}
/**
* Internet Explorer for Windows performs some really stupid file type
* autodetection which can cause it to interpret valid image files as HTML
* and potentially execute JavaScript, creating a cross-site scripting
* attack vectors.
* Checks if the mime type of the uploaded file matches the file extension.
*
* Returns true if IE is likely to mistake the given file for HTML.
*
* @param string $filename
* @param string $mime the mime type of the uploaded file
* @param string $extension The filename extension that the file is to be served with
* @return bool
*/
function triggersIEbug( $filename ) {
$file = fopen( $filename, 'rb' );
$chunk = strtolower( fread( $file, 256 ) );
fclose( $file );
function verifyExtension( $mime, $extension ) {
$fname = 'SpecialUpload::verifyExtension';
if (!$mime || $mime=="unknown" || $mime=="unknown/unknown") {
wfDebug( "$fname: passing file with unknown mime type\n" );
return true;
}
$magic=& wfGetMimeMagic();
$match= $magic->isMatchingExtension($extension,$mime);
if ($match===NULL) {
wfDebug( "$fname: no file extension known for mime type $mime, passing file\n" );
return true;
} elseif ($match===true) {
wfDebug( "$fname: mime type $mime matches extension $extension, passing file\n" );
#TODO: if it's a bitmap, make sure PHP or ImageMagic resp. can handle it!
return true;
} else {
wfDebug( "$fname: mime type $mime mismatches file extension $extension, rejecting file\n" );
return false;
}
}
/** Heuristig for detecting files that *could* contain JavaScript instructions or
* things that may look like HTML to a browser and are thus
* potentially harmful. The present implementation will produce false positives in some situations.
*
* @param string $file Pathname to the temporary upload file
* @param string $mime The mime type of the file
* @return bool true if the file contains something looking like embedded scripts
*/
function detectScript($file,$mime) {
#ugly hack: for text files, always look at the entire file.
#For binarie field, just check the first K.
if (strpos($mime,'text/')===0) $chunk = file_get_contents( $file );
else {
$fp = fopen( $file, 'rb' );
$chunk = fread( $fp, 1024 );
fclose( $fp );
}
$chunk= strtolower( $chunk );
if (!$chunk) return false;
#decode from UTF-16 if needed (could be used for obfuscation).
if (substr($chunk,0,2)=="\xfe\xff") $enc= "UTF-16BE";
elseif (substr($chunk,0,2)=="\xff\xfe") $enc= "UTF-16LE";
else $enc= NULL;
if ($enc) $chunk= iconv($enc,"ASCII//IGNORE",$chunk);
$chunk= trim($chunk);
#FIXME: convert from UTF-16 if necessarry!
wfDebug("SpecialUpload::detectScript: checking for embedded scripts and HTML stuff\n");
#check for HTML doctype
if (eregi("<!DOCTYPE *X?HTML",$chunk)) return true;
/**
* Internet Explorer for Windows performs some really stupid file type
* autodetection which can cause it to interpret valid image files as HTML
* and potentially execute JavaScript, creating a cross-site scripting
* attack vectors.
*
* Apple's Safari browser also performs some unsafe file type autodetection
* which can cause legitimate files to be interpreted as HTML if the
* web server is not correctly configured to send the right content-type
* (or if you're really uploading plain text and octet streams!)
*
* Returns true if IE is likely to mistake the given file for HTML.
* Also returns true if Safari would mistake the given file for HTML
* when served with a generic content-type.
*/
$tags = array(
'<body',
'<head',
'<html',
'<html', #also in safari
'<img',
'<pre',
'<script',
'<script', #also in safari
'<table',
'<title' );
'<title' #also in safari
);
foreach( $tags as $tag ) {
if( false !== strpos( $chunk, $tag ) ) {
return true;
}
}
return false;
}
/**
* Apple's Safari browser performs some unsafe file type autodetection
* which can cause legitimate files to be interpreted as HTML if the
* web server is not correctly configured to send the right content-type
* (or if you're really uploading plain text and octet streams!)
*
* Returns true if Safari would mistake the given file for HTML
* when served with a generic content-type.
*
* @param string $filename
* @return bool
*/
function triggersSafariBug( $filename ) {
$file = fopen( $filename, 'rb' );
$chunk = strtolower( fread( $file, 1024 ) );
fclose( $file );
$tags = array(
'<html',
'<script',
'<title' );
foreach( $tags as $tag ) {
if( false !== strpos( $chunk, $tag ) ) {
return true;
}
}
/*
* look for javascript
*/
#resolve entity-refs to look at attributes. may be harsh on big files... cache result?
$chunk= wfMungeToUtf8($chunk); #this should actually use do_html_decode_entites, once this also deals with numeric entities.
#look for script-types
if (preg_match("!type\s*=\s*['\"]?\s*(\w*/)?(ecma|java)!sim",$chunk)) return true;
#look for html-style script-urls
if (preg_match("!(href|src|data)\s*=\s*['\"]?\s*(ecma|java)script:!sim",$chunk)) return true;
#look for css-style script-urls
if (preg_match("!url\s*\(\s*['\"]?\s*(ecma|java)script:!sim",$chunk)) return true;
wfDebug("SpecialUpload::detectScript: no scripts found\n");
return false;
}
/** Generic wrapper function for a virus scanner program.
* This relies on the $wgAntivirus and $wgAntivirusSetup variables.
* $wgAntivirusRequired may be used to deny upload if the scan fails.
*
* @param string $file Pathname to the temporary upload file
* @return mixed false if not virus is found, NULL if the scan fails or is disabled,
* or a string containing feedback from the virus scanner if a virus was found.
* If textual feedback is missing but a virus was found, this function returns true.
*/
function detectVirus($file) {
global $wgAntivirus, $wgAntivirusSetup, $wgAntivirusRequired;
$fname= "SpecialUpload::detectVirus";
if (!$wgAntivirus) { #disabled?
wfDebug("$fname: virus scanner disabled\n");
return NULL;
}
if (!$wgAntivirusSetup[$wgAntivirus]) {
wfDebug("$fname: unknown virus scanner: $wgAntivirus\n");
$wgOut->addHTML( "<div class='error'>Bad configuration: unknown virus scanner: <i>$wgAntivirus</i></div>\n" ); #LOCALIZE
return "unknown antivirus: $wgAntivirus";
}
#look up scanner configuration
$virus_scanner= $wgAntivirusSetup[$wgAntivirus]["command"]; #command pattern
$virus_scanner_codes= $wgAntivirusSetup[$wgAntivirus]["codemap"]; #exit-code map
$msg_pattern= $wgAntivirusSetup[$wgAntivirus]["messagepattern"]; #message pattern
$scanner= $virus_scanner; #copy, so we can resolve the pattern
if (strpos($scanner,"%f")===false) $scanner.= " ".wfEscapeShellArg($file); #simple pattern: append file to scan
else $scanner= str_replace("%f",wfEscapeShellArg($file),$scanner); #complex pattern: replace "%f" with file to scan
wfDebug("$fname: running virus scan: $scanner \n");
#execute virus scanner
$code= false;
#NOTE: there's a 50 line workaround to make stderr redirection work on windows, too.
# that does not seem to be worth the pain.
# Ask me (Duesentrieb) about it if it's ever needed.
if (wfIsWindows()) exec("$scanner",$output,$code);
else exec("$scanner 2>&1",$output,$code);
$exit_code= $code; #remeber for user feedback
if ($virus_scanner_codes) { #map exit code to AV_xxx constants.
if (isset($virus_scanner_codes[$code])) $code= $virus_scanner_codes[$code]; #explicite mapping
else if (isset($virus_scanner_codes["*"])) $code= $virus_scanner_codes["*"]; #fallback mapping
}
if ($code===AV_SCAN_FAILED) { #scan failed (code was mapped to false by $virus_scanner_codes)
wfDebug("$fname: failed to scan $file (code $exit_code).\n");
if ($wgAntivirusRequired) return "scan failed (code $exit_code)";
else return NULL;
}
else if ($code===AV_SCAN_ABORTED) { #scan failed because filetype is unknown (probably imune)
wfDebug("$fname: unsupported file type $file (code $exit_code).\n");
return NULL;
}
else if ($code===AV_NO_VIRUS) {
wfDebug("$fname: file passed virus scan.\n");
return false; #no virus found
}
else {
$output= join("\n",$output);
$output= trim($output);
if (!$output) $output= true; #if ther's no output, return true
else if ($msg_pattern) {
$groups= array();
if (preg_match($msg_pattern,$output,$groups)) {
if ($groups[1]) $output= $groups[1];
}
}
wfDebug("$fname: FOUND VIRUS! scanner feedback: $output");
return $output;
}
}
}
?>

View file

@ -25,141 +25,39 @@ does not.</p>
}
}
header( 'Content-Length: ' . $stat['size'] );
$type = wfGetType( $fname );
if ( $type ) {
if ( $type and $type!="unknown/unknown") {
header("Content-type: $type");
} else {
header('Content-type: application/x-wiki');
}
readfile( $fname );
}
function wfGetType( $filename ) {
# There's probably a better way to do this
$types = <<<END_STRING
application/andrew-inset ez
application/mac-binhex40 hqx
application/mac-compactpro cpt
application/mathml+xml mathml
application/msword doc
application/octet-stream bin dms lha lzh exe class so dll
application/oda oda
application/ogg ogg
application/pdf pdf
application/postscript ai eps ps
application/rdf+xml rdf
application/smil smi smil
application/srgs gram
application/srgs+xml grxml
application/vnd.mif mif
application/vnd.ms-excel xls
application/vnd.ms-powerpoint ppt
application/vnd.wap.wbxml wbxml
application/vnd.wap.wmlc wmlc
application/vnd.wap.wmlscriptc wmlsc
application/voicexml+xml vxml
application/x-bcpio bcpio
application/x-cdlink vcd
application/x-chess-pgn pgn
application/x-cpio cpio
application/x-csh csh
application/x-director dcr dir dxr
application/x-dvi dvi
application/x-futuresplash spl
application/x-gtar gtar
application/x-hdf hdf
application/x-javascript js
application/x-koan skp skd skt skm
application/x-latex latex
application/x-netcdf nc cdf
application/x-sh sh
application/x-shar shar
application/x-shockwave-flash swf
application/x-stuffit sit
application/x-sv4cpio sv4cpio
application/x-sv4crc sv4crc
application/x-tar tar
application/x-tcl tcl
application/x-tex tex
application/x-texinfo texinfo texi
application/x-troff t tr roff
application/x-troff-man man
application/x-troff-me me
application/x-troff-ms ms
application/x-ustar ustar
application/x-wais-source src
application/xhtml+xml xhtml xht
application/xslt+xml xslt
application/xml xml xsl
application/xml-dtd dtd
application/zip zip
audio/basic au snd
audio/midi mid midi kar
audio/mpeg mpga mp2 mp3
audio/x-aiff aif aiff aifc
audio/x-mpegurl m3u
audio/x-pn-realaudio ram rm
audio/x-pn-realaudio-plugin rpm
audio/x-realaudio ra
audio/x-wav wav
chemical/x-pdb pdb
chemical/x-xyz xyz
image/bmp bmp
image/cgm cgm
image/gif gif
image/ief ief
image/jpeg jpeg jpg jpe
image/png png
image/svg+xml svg
image/tiff tiff tif
image/vnd.djvu djvu djv
image/vnd.wap.wbmp wbmp
image/x-cmu-raster ras
image/x-icon ico
image/x-portable-anymap pnm
image/x-portable-bitmap pbm
image/x-portable-graymap pgm
image/x-portable-pixmap ppm
image/x-rgb rgb
image/x-xbitmap xbm
image/x-xpixmap xpm
image/x-xwindowdump xwd
model/iges igs iges
model/mesh msh mesh silo
model/vrml wrl vrml
text/calendar ics ifb
text/css css
text/richtext rtx
text/rtf rtf
text/sgml sgml sgm
text/tab-separated-values tsv
text/vnd.wap.wml wml
text/vnd.wap.wmlscript wmls
text/x-setext etx
video/mpeg mpeg mpg mpe
video/quicktime qt mov
video/vnd.mpegurl mxu
video/x-msvideo avi
video/x-sgi-movie movie
x-conference/x-cooltalk ice
END_STRING;
// Needed for windows servers who use \r\n not \n
$endl = "
";
$types = explode( $endl, $types );
if ( !preg_match( "/\.([^.]*?)$/", $filename, $matches ) ) {
return false;
}
global $wgTrivialMimeDetection;
foreach( $types as $type ) {
$extensions = explode( " ", $type );
for ( $i=1; $i<count( $extensions ); $i++ ) {
if ( $extensions[$i] == $matches[1] ) {
return $extensions[0];
}
# trivial detection by file extension,
# used for thumbnails (thumb.php)
if ($wgTrivialMimeDetection) {
$ext= strtolower(strrchr($filename, '.'));
switch ($ext) {
case '.gif': return "image/gif";
case '.png': return "image/png";
case '.jpg': return "image/jpeg";
case '.jpeg': return "image/jpeg";
}
return "unknown/unknown";
}
else {
$magic=& wfGetMimeMagic();
return $magic->guessMimeType($filename); //full fancy mime detection
}
return false;
}
?>

76
includes/mime.info Normal file
View file

@ -0,0 +1,76 @@
#Mime type info file.
#the first mime type in each line is the "main" mime type,
#the others are aliases for this type
#the media type is given in upper case and square brackets,
#like [BITMAP], and must indicate a media type as defined by
#the MEDIATYPE_xxx constants in Defines.php
image/gif [BITMAP]
image/png [BITMAP]
image/ief [BITMAP]
image/jpeg [BITMAP]
image/xbm [BITMAP]
image/tiff [BITMAP]
image/x-icon [BITMAP]
image/x-rgb [BITMAP]
image/x-portable-pixmap [BITMAP]
image/x-portable-graymap image/x-portable-greymap [BITMAP]
image/x-bmp image/bmp application/x-bmp application/bmp [BITMAP]
image/x-photoshop image/psd image/x-psd image/photoshop [BITMAP]
image/svg image/svg+xml application/svg+xml application/svg [DRAWING]
application/postscript [DRAWING]
application/x-latex [DRAWING]
application/x-tex [DRAWING]
audio/mp3 audio/mpeg3 audio/mpeg [AUDIO]
audio/wav audio/x-wav audio/wave [AUDIO]
audio/mid audio/midi [AUDIO]
audio/basic [AUDIO]
audio/x-aiff [AUDIO]
audio/x-pn-realaudio [AUDIO]
audio/x-realaudio [AUDIO]
video/mpeg application/mpeg [VIDEO]
video/ogg [VIDEO]
video/x-sgi-video [VIDEO]
application/ogg application/x-ogg audio/ogg audio/x-ogg video/ogg video/x-ogg [MULTIMEDIA]
application/x-shockwave-flash [MULTIMEDIA]
audio/x-pn-realaudio-plugin [MULTIMEDIA]
model/iges [MULTIMEDIA]
model/mesh [MULTIMEDIA]
model/vrml [MULTIMEDIA]
video/quicktime [MULTIMEDIA]
video/x-msvideo [MULTIMEDIA]
text/plain [TEXT]
text/html application/xhtml+xml [TEXT]
application/xml text/xml [TEXT]
text [TEXT]
application/zip application/x-zip [ARCHIVE]
application/x-gzip [ARCHIVE]
application/x-bzip [ARCHIVE]
application/x-tar [ARCHIVE]
application/x-stuffit [ARCHIVE]
text/javascript application/x-javascript application/x-ecmascript text/ecmascript [EXECUTABLE]
application/x-bash [EXECUTABLE]
application/x-sh [EXECUTABLE]
application/x-csh [EXECUTABLE]
application/x-tcsh [EXECUTABLE]
application/x-tcl [EXECUTABLE]
application/x-perl [EXECUTABLE]
application/x-python [EXECUTABLE]
application/pdf application/acrobat [OFFICE]
application/msword [OFFICE]
application/vnd.ms-excel [OFFICE]
application/vnd.ms-powerpoint [OFFICE]
application/x-director [OFFICE]
text/rtf [OFFICE]

117
includes/mime.types Normal file
View file

@ -0,0 +1,117 @@
application/andrew-inset ez
application/mac-binhex40 hqx
application/mac-compactpro cpt
application/mathml+xml mathml
application/msword doc
application/octet-stream bin dms lha lzh exe class so dll
application/oda oda
application/ogg ogg ogm
application/pdf pdf
application/postscript ai eps ps
application/rdf+xml rdf
application/smil smi smil
application/srgs gram
application/srgs+xml grxml
application/vnd.mif mif
application/vnd.ms-excel xls
application/vnd.ms-powerpoint ppt
application/vnd.wap.wbxml wbxml
application/vnd.wap.wmlc wmlc
application/vnd.wap.wmlscriptc wmlsc
application/voicexml+xml vxml
application/x-bcpio bcpio
application/x-bzip gz bz2
application/x-cdlink vcd
application/x-chess-pgn pgn
application/x-cpio cpio
application/x-csh csh
application/x-director dcr dir dxr
application/x-dvi dvi
application/x-futuresplash spl
application/x-gtar gtar tar
application/x-gzip gz
application/x-hdf hdf
application/x-jar jar
application/x-javascript js
application/x-koan skp skd skt skm
application/x-latex latex
application/x-netcdf nc cdf
application/x-sh sh
application/x-shar shar
application/x-shockwave-flash swf
application/x-stuffit sit
application/x-sv4cpio sv4cpio
application/x-sv4crc sv4crc
application/x-tar tar
application/x-tcl tcl
application/x-tex tex
application/x-texinfo texinfo texi
application/x-troff t tr roff
application/x-troff-man man
application/x-troff-me me
application/x-troff-ms ms
application/x-ustar ustar
application/x-wais-source src
application/x-xpinstall xpi
application/xhtml+xml xhtml xht
application/xslt+xml xslt
application/xml xml xsl
application/xml-dtd dtd
application/zip zip jar xpi sxc stc sxd std sxi sti sxm stm sxw stw
audio/basic au snd
audio/midi mid midi kar
audio/mpeg mpga mp2 mp3
audio/ogg ogg
audio/x-aiff aif aiff aifc
audio/x-mpegurl m3u
audio/x-ogg ogg
audio/x-pn-realaudio ram rm
audio/x-pn-realaudio-plugin rpm
audio/x-realaudio ra
audio/x-wav wav
chemical/x-pdb pdb
chemical/x-xyz xyz
image/bmp bmp
image/cgm cgm
image/gif gif
image/ief ief
image/jpeg jpeg jpg jpe
image/png png
image/svg+xml svg
image/tiff tiff tif
image/vnd.djvu djvu djv
image/vnd.wap.wbmp wbmp
image/x-cmu-raster ras
image/x-icon ico
image/x-portable-anymap pnm
image/x-portable-bitmap pbm
image/x-portable-graymap pgm
image/x-portable-pixmap ppm
image/x-rgb rgb
image/x-photoshop psd
image/x-xbitmap xbm
image/x-xpixmap xpm
image/x-xwindowdump xwd
model/iges igs iges
model/mesh msh mesh silo
model/vrml wrl vrml
text/calendar ics ifb
text/css css
text/html html htm
text/plain txt
text/richtext rtx
text/rtf rtf
text/sgml sgml sgm
text/tab-separated-values tsv
text/vnd.wap.wml wml
text/vnd.wap.wmlscript wmls
text/xml xml xsl xslt rss rdf
text/x-setext etx
video/mpeg mpeg mpg mpe
video/ogg ogm ogg
video/quicktime qt mov
video/vnd.mpegurl mxu
video/x-msvideo avi
video/x-ogg ogm ogg
video/x-sgi-movie movie
x-conference/x-cooltalk ice

View file

@ -0,0 +1,20 @@
-- media type columns, added for 1.5
-- this alters the scheme for 1.5, img_type is no longer used.
ALTER TABLE /*$wgDBprefix*/image ADD (
-- Media type as defined by the MEDIATYPE_xxx constants
img_media_type ENUM("UNKNOWN", "BITMAP", "DRAWING", "AUDIO", "VIDEO", "MULTIMEDIA", "OFFICE", "TEXT", "EXECUTABLE", "ARCHIVE") default NULL,
-- major part of a MIME media type as defined by IANA
-- see http://www.iana.org/assignments/media-types/
img_major_mime ENUM("unknown", "application", "audio", "image", "text", "video", "message", "model", "multipart") NOT NULL default "unknown",
-- minor part of a MIME media type as defined by IANA
-- the minor parts are not required to adher to any standard
-- but should be consistent throughout the database
-- see http://www.iana.org/assignments/media-types/
img_minor_mime varchar(32) NOT NULL default "unknown"
);
-- img_type is no longer used, delete it
ALTER TABLE /*$wgDBprefix*/image DROP COLUMN img_type;

View file

@ -1,17 +1,20 @@
-- Extra image metadata, added for 1.5
-- NOTE: as by patch-img_media_type.sql, the img_type
-- column is no longer used and has therefore be removed from this patch
ALTER TABLE /*$wgDBprefix*/image ADD (
img_width int(5) NOT NULL default 0,
img_height int(5) NOT NULL default 0,
img_bits int(5) NOT NULL default 0,
img_type int(5) NOT NULL default -1
-- img_type int(5) NOT NULL default -1
);
ALTER TABLE /*$wgDBprefix*/oldimage ADD (
oi_width int(5) NOT NULL default 0,
oi_height int(5) NOT NULL default 0,
oi_bits int(3) NOT NULL default 0,
oi_type int(3) NOT NULL default 0
-- oi_type int(3) NOT NULL default 0
);

View file

@ -423,7 +423,9 @@ class ParserTest {
'img_width' => 1941,
'img_height' => 220,
'img_bits' => 24,
'img_type' => 2, // 2 == JPEG
'img_media_type' => MEDIATYPE_BITMAP,
'img_major_mime' => "image",
'img_minor_mime' => "jpeg",
) );
$setupDB = true;

View file

@ -531,9 +531,18 @@ CREATE TABLE /*$wgDBprefix*/image (
-- For images, bits per pixel if known.
img_bits int(3) NOT NULL default '0',
-- File type key returned by getimagesize().
-- See http://www.php.net/getimagesize for possible values.
img_type int(3) NOT NULL default '0',
-- Media type as defined by the MEDIATYPE_xxx constants
img_media_type ENUM("UNKNOWN", "BITMAP", "DRAWING", "AUDIO", "VIDEO", "MULTIMEDIA", "OFFICE", "TEXT", "EXECUTABLE", "ARCHIVE") default NULL,
-- major part of a MIME media type as defined by IANA
-- see http://www.iana.org/assignments/media-types/
img_major_mime ENUM("unknown", "application", "audio", "image", "text", "video", "message", "model", "multipart") NOT NULL default "unknown",
-- minor part of a MIME media type as defined by IANA
-- the minor parts are not required to adher to any standard
-- but should be consistent throughout the database
-- see http://www.iana.org/assignments/media-types/
img_minor_mime varchar(32) NOT NULL default "unknown",
-- Description field as entered by the uploader.
-- This is displayed in image upload history and logs.
@ -574,7 +583,6 @@ CREATE TABLE /*$wgDBprefix*/oldimage (
oi_width int(5) NOT NULL default 0,
oi_height int(5) NOT NULL default 0,
oi_bits int(3) NOT NULL default 0,
oi_type int(3) NOT NULL default 0,
oi_description tinyblob NOT NULL default '',
oi_user int(5) unsigned NOT NULL default '0',
oi_user_text varchar(255) binary NOT NULL default '',

View file

@ -46,6 +46,7 @@ $wgNewFields = array(
array( 'revision', 'rev_deleted', 'patch-rev_deleted.sql' ),
array( 'image', 'img_width', 'patch-img_width.sql' ),
array( 'image', 'img_metadata', 'patch-img_metadata.sql' ),
array( 'image', 'img_media_type', 'patch-img_media_type.sql' ),
);
function rename_table( $from, $to, $patch ) {

View file

@ -12,6 +12,9 @@ $wgNoOutputBuffer = true;
require_once( './includes/Defines.php' );
require_once( './LocalSettings.php' );
require_once( 'GlobalFunctions.php' );
$wgTrivialMimeDetection = true; //don't use fancy mime detection, just check the file extension for jpg/gif/png.
require_once( 'Image.php' );
require_once( 'StreamFile.php' );
@ -25,6 +28,8 @@ if ( get_magic_quotes_gpc() ) {
$width = $_REQUEST['w'];
}
$pre_render= isset($_REQUEST['r']) && $_REQUEST['r']!="0";
// Some basic input validation
$width = intval( $width );
@ -34,7 +39,7 @@ $fileName = strtr( $fileName, '\\/', '__' );
$imagePath = wfImageDir( $fileName ) . '/' . $fileName;
$thumbName = "{$width}px-$fileName";
if ( preg_match( '/\.svg$/', $fileName ) ) {
if ( $pre_render ) {
$thumbName .= '.png';
}
$thumbPath = wfImageThumbDir( $fileName ) . '/' . $thumbName;