42 define(
'MM_WELL_KNOWN_MIME_TYPES', <<<END_STRING
43 application/ogg ogx ogg ogm ogv oga spx
45 application/vnd.oasis.opendocument.chart odc
46 application/vnd.oasis.opendocument.chart-
template otc
47 application/vnd.oasis.opendocument.database odb
48 application/vnd.oasis.opendocument.formula odf
49 application/vnd.oasis.opendocument.formula-
template otf
50 application/vnd.oasis.opendocument.graphics odg
51 application/vnd.oasis.opendocument.graphics-
template otg
52 application/vnd.oasis.opendocument.image odi
53 application/vnd.oasis.opendocument.image-
template oti
54 application/vnd.oasis.opendocument.presentation odp
55 application/vnd.oasis.opendocument.presentation-
template otp
56 application/vnd.oasis.opendocument.spreadsheet ods
57 application/vnd.oasis.opendocument.spreadsheet-
template ots
58 application/vnd.oasis.opendocument.text odt
59 application/vnd.oasis.opendocument.text-master otm
60 application/vnd.oasis.opendocument.text-
template ott
61 application/vnd.oasis.opendocument.text-web oth
62 application/x-javascript js
63 application/x-shockwave-flash swf
64 audio/midi mid midi kar
65 audio/mpeg mpga mpa mp2 mp3
66 audio/x-aiff aif aiff aifc
71 image/jpeg jpeg jpg jpe
79 image/x-portable-pixmap ppm
94 define(
'MM_WELL_KNOWN_MIME_INFO', <<<END_STRING
95 application/pdf [OFFICE]
96 application/vnd.oasis.opendocument.chart [OFFICE]
97 application/vnd.oasis.opendocument.chart-
template [OFFICE]
98 application/vnd.oasis.opendocument.database [OFFICE]
99 application/vnd.oasis.opendocument.formula [OFFICE]
100 application/vnd.oasis.opendocument.formula-
template [OFFICE]
101 application/vnd.oasis.opendocument.graphics [OFFICE]
102 application/vnd.oasis.opendocument.graphics-
template [OFFICE]
103 application/vnd.oasis.opendocument.image [OFFICE]
104 application/vnd.oasis.opendocument.image-
template [OFFICE]
105 application/vnd.oasis.opendocument.presentation [OFFICE]
106 application/vnd.oasis.opendocument.presentation-
template [OFFICE]
107 application/vnd.oasis.opendocument.spreadsheet [OFFICE]
108 application/vnd.oasis.opendocument.spreadsheet-
template [OFFICE]
109 application/vnd.oasis.opendocument.text [OFFICE]
110 application/vnd.oasis.opendocument.text-
template [OFFICE]
111 application/vnd.oasis.opendocument.text-master [OFFICE]
112 application/vnd.oasis.opendocument.text-web [OFFICE]
113 text/javascript application/x-javascript [EXECUTABLE]
114 application/x-shockwave-flash [MULTIMEDIA]
118 audio/mp3 audio/mpeg [AUDIO]
119 application/ogg audio/ogg video/ogg [MULTIMEDIA]
120 image/x-bmp image/x-ms-bmp image/bmp [BITMAP]
124 image/svg+xml [DRAWING]
126 image/vnd.djvu [BITMAP]
128 image/x-portable-pixmap [BITMAP]
133 unknown/unknown application/octet-stream application/x-empty [UNKNOWN]
150 var $mMediaTypes =
null;
154 var $mMimeTypeAliases =
null;
158 var $mMimeToExt =
null;
162 var $mExtToMime =
null;
170 private static $instance =
null;
176 function __construct() {
185 if ( $wgMimeTypeFile ==
'includes/mime.types' ) {
186 $wgMimeTypeFile =
"$IP/$wgMimeTypeFile";
189 if ( $wgMimeTypeFile ) {
190 if ( is_file( $wgMimeTypeFile ) and is_readable( $wgMimeTypeFile ) ) {
191 wfDebug( __METHOD__ .
": loading mime types from $wgMimeTypeFile\n" );
193 $types .= file_get_contents( $wgMimeTypeFile );
195 wfDebug( __METHOD__ .
": can't load mime types from $wgMimeTypeFile\n" );
198 wfDebug( __METHOD__ .
": no mime types file defined, using build-ins only.\n" );
201 $types = str_replace(
array(
"\r\n",
"\n\r",
"\n\n",
"\r\r",
"\r" ),
"\n", $types );
202 $types = str_replace(
"\t",
" ", $types );
204 $this->mMimeToExt =
array();
205 $this->mToMime =
array();
207 $lines = explode(
"\n", $types );
213 if ( strpos(
$s,
'#' ) === 0 ) {
217 $s = strtolower(
$s );
218 $i = strpos(
$s,
' ' );
220 if ( $i ===
false ) {
225 $ext = trim( substr(
$s, $i + 1 ) );
227 if ( empty(
$ext ) ) {
231 if ( !empty( $this->mMimeToExt[
$mime] ) ) {
245 if ( !empty( $this->mExtToMime[
$e] ) ) {
246 $this->mExtToMime[
$e] .=
' ' .
$mime;
258 if ( $wgMimeInfoFile ==
'includes/mime.info' ) {
259 $wgMimeInfoFile =
"$IP/$wgMimeInfoFile";
264 if ( $wgMimeInfoFile ) {
265 if ( is_file( $wgMimeInfoFile ) and is_readable( $wgMimeInfoFile ) ) {
266 wfDebug( __METHOD__ .
": loading mime info from $wgMimeInfoFile\n" );
268 $info .= file_get_contents( $wgMimeInfoFile );
270 wfDebug( __METHOD__ .
": can't load mime info from $wgMimeInfoFile\n" );
273 wfDebug( __METHOD__ .
": no mime info file defined, using build-ins only.\n" );
276 $info = str_replace(
array(
"\r\n",
"\n\r",
"\n\n",
"\r\r",
"\r" ),
"\n", $info );
277 $info = str_replace(
"\t",
" ", $info );
279 $this->mMimeTypeAliases =
array();
280 $this->mMediaTypes =
array();
282 $lines = explode(
"\n", $info );
288 if ( strpos(
$s,
'#' ) === 0 ) {
292 $s = strtolower(
$s );
293 $i = strpos(
$s,
' ' );
295 if ( $i ===
false ) {
299 #print "processing MIME INFO line $s<br>";
302 if ( preg_match(
'!\[\s*(\w+)\s*\]!',
$s, $match ) ) {
303 $s = preg_replace(
'!\[\s*(\w+)\s*\]!',
'',
$s );
304 $mtype = trim( strtoupper( $match[1] ) );
309 $m = explode(
' ',
$s );
311 if ( !isset( $this->mMediaTypes[$mtype] ) ) {
312 $this->mMediaTypes[$mtype] =
array();
317 if ( empty(
$mime ) ) {
321 $this->mMediaTypes[$mtype][] =
$mime;
324 if ( count( $m ) > 1 ) {
326 for ( $i = 1; $i < count( $m ); $i += 1 ) {
328 $this->mMimeTypeAliases[
$mime] = $main;
339 public static function singleton() {
340 if ( self::$instance ===
null ) {
341 self::$instance =
new MimeMagic;
343 return self::$instance;
354 public function getExtensionsForType(
$mime ) {
358 if ( isset( $this->mMimeToExt[
$mime] ) ) {
359 return $this->mMimeToExt[
$mime];
363 if ( isset( $this->mMimeTypeAliases[
$mime] ) ) {
365 if ( isset( $this->mMimeToExt[
$mime] ) ) {
366 return $this->mMimeToExt[
$mime];
380 public function getTypesForExtension(
$ext ) {
383 $r = isset( $this->mExtToMime[
$ext] ) ? $this->mExtToMime[
$ext] :
null;
394 public function guessTypesForExtension(
$ext ) {
395 $m = $this->getTypesForExtension(
$ext );
396 if ( is_null( $m ) ) {
402 $m = preg_replace(
'/\s.*$/',
'', $m );
416 public function isMatchingExtension( $extension,
$mime ) {
417 $ext = $this->getExtensionsForType(
$mime );
425 $extension = strtolower( $extension );
426 return in_array( $extension,
$ext );
437 public function isPHPImageType(
$mime ) {
439 static $types =
array(
440 'image/gif',
'image/jpeg',
'image/png',
441 'image/x-bmp',
'image/xbm',
'image/tiff',
442 'image/jp2',
'image/jpeg2000',
'image/iff',
443 'image/xbm',
'image/x-xbitmap',
444 'image/vnd.wap.wbmp',
'image/vnd.xiff',
446 'application/x-shockwave-flash',
449 return in_array(
$mime, $types );
463 function isRecognizableExtension( $extension ) {
464 static $types =
array(
466 'gif',
'jpeg',
'jpg',
'png',
'swf',
'psd',
467 'bmp',
'tiff',
'tif',
'jpc',
'jp2',
468 'jpx',
'jb2',
'swc',
'iff',
'wbmp',
472 'djvu',
'ogx',
'ogg',
'ogv',
'oga',
'spx',
473 'mid',
'pdf',
'wmf',
'xcf',
'webm',
'mkv',
'mka',
479 return in_array( strtolower( $extension ), $types );
501 public function improveTypeFromExtension(
$mime,
$ext ) {
502 if (
$mime ===
'unknown/unknown' ) {
503 if ( $this->isRecognizableExtension(
$ext ) ) {
504 wfDebug( __METHOD__ .
': refusing to guess mime type for .' .
505 "$ext file, we should have recognized it\n" );
509 $mime = $this->guessTypesForExtension(
$ext );
511 } elseif (
$mime ===
'application/x-opc+zip' ) {
512 if ( $this->isMatchingExtension(
$ext,
$mime ) ) {
515 $mime = $this->guessTypesForExtension(
$ext );
517 wfDebug( __METHOD__ .
": refusing to guess better type for $mime file, " .
518 ".$ext is not a known OPC extension.\n" );
519 $mime =
'application/zip';
523 if ( isset( $this->mMimeTypeAliases[
$mime] ) ) {
527 wfDebug( __METHOD__ .
": improved mime type for .$ext: $mime\n" );
545 public function guessMimeType(
$file,
$ext =
true ) {
547 wfDebug( __METHOD__ .
": WARNING: use of the \$ext parameter is deprecated. " .
548 "Use improveTypeFromExtension(\$mime, \$ext) instead.\n" );
554 wfDebug( __METHOD__ .
": internal type detection failed for $file (.$ext)...\n" );
558 if ( isset( $this->mMimeTypeAliases[
$mime] ) ) {
562 wfDebug( __METHOD__ .
": guessed mime type of $file: $mime\n" );
573 private function doGuessMimeType(
$file,
$ext ) {
581 return 'unknown/unknown';
583 $head = fread(
$f, 1024 );
584 fseek(
$f, -65558, SEEK_END );
585 $tail = fread(
$f, 65558 );
588 wfDebug( __METHOD__ .
": analyzing head and tail of $file for magic numbers.\n" );
593 'MThd' =>
'audio/midi',
594 'OggS' =>
'application/ogg',
598 "\x01\x00\x09\x00" =>
'application/x-msmetafile',
599 "\xd7\xcd\xc6\x9a" =>
'application/x-msmetafile',
600 '%PDF' =>
'application/pdf',
601 'gimp xcf' =>
'image/x-xcf',
604 'MZ' =>
'application/octet-stream',
605 "\xca\xfe\xba\xbe" =>
'application/octet-stream',
606 "\x7fELF" =>
'application/octet-stream',
609 foreach ( $headers
as $magic => $candidate ) {
610 if ( strncmp( $head, $magic, strlen( $magic ) ) == 0 ) {
611 wfDebug( __METHOD__ .
": magic header in $file recognized as $candidate\n" );
617 if ( strncmp( $head, pack(
"C4", 0x1a, 0x45, 0xdf, 0xa3 ), 4 ) == 0 ) {
618 $doctype = strpos( $head,
"\x42\x82" );
621 $data = substr( $head, $doctype + 3, 8 );
622 if ( strncmp( $data,
"matroska", 8 ) == 0 ) {
623 wfDebug( __METHOD__ .
": recognized file as video/x-matroska\n" );
624 return "video/x-matroska";
625 } elseif ( strncmp( $data,
"webm", 4 ) == 0 ) {
626 wfDebug( __METHOD__ .
": recognized file as video/webm\n" );
630 wfDebug( __METHOD__ .
": unknown EBML file\n" );
631 return "unknown/unknown";
635 if ( strncmp( $head,
"RIFF", 4 ) == 0 && strncmp( substr( $head, 8, 8 ),
"WEBPVP8 ", 8 ) == 0 ) {
636 wfDebug( __METHOD__ .
": recognized file as image/webp\n" );
652 if ( ( strpos( $head,
'<?php' ) !==
false ) ||
653 ( strpos( $head,
"<\x00?\x00p\x00h\x00p" ) !==
false ) ||
654 ( strpos( $head,
"<\x00?\x00 " ) !==
false ) ||
655 ( strpos( $head,
"<\x00?\x00\n" ) !==
false ) ||
656 ( strpos( $head,
"<\x00?\x00\t" ) !==
false ) ||
657 ( strpos( $head,
"<\x00?\x00=" ) !==
false ) ) {
659 wfDebug( __METHOD__ .
": recognized $file as application/x-php\n" );
660 return 'application/x-php';
667 if ( $xml->wellFormed ) {
669 if ( isset( $wgXMLMimeTypes[$xml->getRootElement()] ) ) {
670 return $wgXMLMimeTypes[$xml->getRootElement()];
672 return 'application/xml';
682 if ( substr( $head, 0, 2 ) ==
"#!" ) {
683 $script_type =
"ASCII";
684 } elseif ( substr( $head, 0, 5 ) ==
"\xef\xbb\xbf#!" ) {
685 $script_type =
"UTF-8";
686 } elseif ( substr( $head, 0, 7 ) ==
"\xfe\xff\x00#\x00!" ) {
687 $script_type =
"UTF-16BE";
688 } elseif ( substr( $head, 0, 7 ) ==
"\xff\xfe#\x00!" ) {
689 $script_type =
"UTF-16LE";
692 if ( $script_type ) {
693 if ( $script_type !==
"UTF-8" && $script_type !==
"ASCII" ) {
695 $pack =
array(
'UTF-16BE' =>
'n*',
'UTF-16LE' =>
'v*' );
696 $chars = unpack( $pack[$script_type], substr( $head, 2 ) );
698 foreach ( $chars
as $codepoint ) {
699 if ( $codepoint < 128 ) {
700 $head .= chr( $codepoint );
709 if ( preg_match(
'%/?([^\s]+/)(\w+)%', $head, $match ) ) {
710 $mime =
"application/x-{$match[2]}";
711 wfDebug( __METHOD__ .
": shell script recognized as $mime\n" );
717 if ( strpos( $tail,
"PK\x05\x06" ) !==
false ) {
718 wfDebug( __METHOD__ .
": ZIP header present in $file\n" );
719 return $this->detectZipType( $head, $tail,
$ext );
723 $gis = getimagesize(
$file );
726 if ( $gis && isset( $gis[
'mime'] ) ) {
727 $mime = $gis[
'mime'];
728 wfDebug( __METHOD__ .
": getimagesize detected $file as $mime\n" );
733 $deja =
new DjVuImage(
$file );
734 if ( $deja->isValid() ) {
735 wfDebug( __METHOD__ .
": detected $file as image/vnd.djvu\n" );
736 return 'image/vnd.djvu';
755 function detectZipType( $header, $tail =
null,
$ext =
false ) {
756 if (
$ext ) { # TODO:
remove $ext param
757 wfDebug( __METHOD__ .
": WARNING: use of the \$ext parameter is deprecated. " .
758 "Use improveTypeFromExtension(\$mime, \$ext) instead.\n" );
761 $mime =
'application/zip';
762 $opendocTypes =
array(
771 'presentation-template',
773 'spreadsheet-template',
781 $types =
'(?:' . implode(
'|', $opendocTypes ) .
')';
782 $opendocRegex =
"/^mimetype(application\/vnd\.oasis\.opendocument\.$types)/";
784 $openxmlRegex =
"/^\[Content_Types\].xml/";
786 if ( preg_match( $opendocRegex, substr( $header, 30 ),
$matches ) ) {
788 wfDebug( __METHOD__ .
": detected $mime from ZIP archive\n" );
789 } elseif ( preg_match( $openxmlRegex, substr( $header, 30 ) ) ) {
790 $mime =
"application/x-opc+zip";
791 # TODO: remove the block below, as soon as improveTypeFromExtension is used everywhere
792 if (
$ext !==
true &&
$ext !==
false ) {
797 if ( $this->isMatchingExtension(
$ext,
$mime ) ) {
801 $mime = $this->guessTypesForExtension(
$ext );
803 $mime =
"application/zip";
806 wfDebug( __METHOD__ .
": detected an Open Packaging Conventions archive: $mime\n" );
807 } elseif ( substr( $header, 0, 8 ) ==
"\xd0\xcf\x11\xe0\xa1\xb1\x1a\xe1" &&
808 ( $headerpos = strpos( $tail,
"PK\x03\x04" ) ) !==
false &&
809 preg_match( $openxmlRegex, substr( $tail, $headerpos + 30 ) ) ) {
810 if ( substr( $header, 512, 4 ) ==
"\xEC\xA5\xC1\x00" ) {
811 $mime =
"application/msword";
813 switch ( substr( $header, 512, 6 ) ) {
814 case "\xEC\xA5\xC1\x00\x0E\x00":
815 case "\xEC\xA5\xC1\x00\x1C\x00":
816 case "\xEC\xA5\xC1\x00\x43\x00":
817 $mime =
"application/vnd.ms-powerpoint";
819 case "\xFD\xFF\xFF\xFF\x10\x00":
820 case "\xFD\xFF\xFF\xFF\x1F\x00":
821 case "\xFD\xFF\xFF\xFF\x22\x00":
822 case "\xFD\xFF\xFF\xFF\x23\x00":
823 case "\xFD\xFF\xFF\xFF\x28\x00":
824 case "\xFD\xFF\xFF\xFF\x29\x00":
825 case "\xFD\xFF\xFF\xFF\x10\x02":
826 case "\xFD\xFF\xFF\xFF\x1F\x02":
827 case "\xFD\xFF\xFF\xFF\x22\x02":
828 case "\xFD\xFF\xFF\xFF\x23\x02":
829 case "\xFD\xFF\xFF\xFF\x28\x02":
830 case "\xFD\xFF\xFF\xFF\x29\x02":
831 $mime =
"application/vnd.msexcel";
835 wfDebug( __METHOD__ .
": detected a MS Office document with OPC trailer\n" );
837 wfDebug( __METHOD__ .
": unable to identify type of ZIP archive\n" );
860 private function detectMimeType(
$file,
$ext =
true ) {
861 global $wgMimeDetectorCommand;
863 if (
$ext ) { # TODO: make
$ext default to
false. Or better,
remove it.
864 wfDebug( __METHOD__ .
": WARNING: use of the \$ext parameter is deprecated. Use improveTypeFromExtension(\$mime, \$ext) instead.\n" );
868 if ( $wgMimeDetectorCommand ) {
870 $m =
wfShellExec(
"$wgMimeDetectorCommand $args" );
871 } elseif ( function_exists(
"finfo_open" ) && function_exists(
"finfo_file" ) ) {
873 # This required the fileinfo extension by PECL,
874 # see http://pecl.php.net/package/fileinfo
875 # This must be compiled into PHP
877 # finfo is the official replacement for the deprecated
878 # mime_content_type function, see below.
880 # If you may need to load the fileinfo extension at runtime, set
881 # $wgLoadFileinfoExtension in LocalSettings.php
883 $mime_magic_resource = finfo_open( FILEINFO_MIME );
885 if ( $mime_magic_resource ) {
886 $m = finfo_file( $mime_magic_resource,
$file );
887 finfo_close( $mime_magic_resource );
889 wfDebug( __METHOD__ .
": finfo_open failed on " . FILEINFO_MIME .
"!\n" );
891 } elseif ( function_exists(
"mime_content_type" ) ) {
893 # NOTE: this function is available since PHP 4.3.0, but only if
894 # PHP was compiled with --with-mime-magic or, before 4.3.2, with --enable-mime-magic.
896 # On Windows, you must set mime_magic.magicfile in php.ini to point to the mime.magic file bundled with PHP;
897 # sometimes, this may even be needed under linus/unix.
899 # Also note that this has been DEPRECATED in favor of the fileinfo extension by PECL, see above.
900 # see http://www.php.net/manual/en/ref.mime-magic.php for details.
902 $m = mime_content_type(
$file );
904 wfDebug( __METHOD__ .
": no magic mime detector found!\n" );
909 $m = preg_replace(
'![;, ].*$!',
'', $m ); #strip charset,
etc
911 $m = strtolower( $m );
913 if ( strpos( $m,
'unknown' ) !==
false ) {
916 wfDebug( __METHOD__ .
": magic mime type of $file: $m\n" );
922 if (
$ext ===
true ) {
923 $i = strrpos(
$file,
'.' );
924 $ext = strtolower( $i ? substr(
$file, $i + 1 ) :
'' );
927 if ( $this->isRecognizableExtension(
$ext ) ) {
928 wfDebug( __METHOD__ .
": refusing to guess mime type for .$ext file, we should have recognized it\n" );
930 $m = $this->guessTypesForExtension(
$ext );
932 wfDebug( __METHOD__ .
": extension mime type of $file: $m\n" );
939 wfDebug( __METHOD__ .
": failed to guess mime type for $file!\n" );
940 return 'unknown/unknown';
959 function getMediaType(
$path =
null,
$mime =
null ) {
971 if (
$mime ==
'application/ogg' && file_exists(
$path ) ) {
978 $head = fread(
$f, 256 );
981 $head = strtolower( $head );
984 if ( strpos( $head,
'theora' ) !==
false ) {
986 } elseif ( strpos( $head,
'vorbis' ) !==
false ) {
988 } elseif ( strpos( $head,
'flac' ) !==
false ) {
990 } elseif ( strpos( $head,
'speex' ) !==
false ) {
1007 $i = strrpos(
$path,
'.' );
1008 $e = strtolower( $i ? substr(
$path, $i + 1 ) :
'' );
1011 $type = $this->findMediaType(
'.' .
$e );
1019 $i = strpos(
$mime,
'/' );
1020 if ( $i !==
false ) {
1021 $major = substr(
$mime, 0, $i );
1022 $type = $this->findMediaType( $major );
1045 function findMediaType( $extMime ) {
1046 if ( strpos( $extMime,
'.' ) === 0 ) {
1048 $m = $this->getTypesForExtension( substr( $extMime, 1 ) );
1053 $m = explode(
' ', $m );
1056 if ( isset( $this->mMimeTypeAliases[$extMime] ) ) {
1057 $extMime = $this->mMimeTypeAliases[$extMime];
1060 $m =
array( $extMime );
1064 foreach ( $this->mMediaTypes
as $type => $codes ) {
1065 if ( in_array(
$mime, $codes,
true ) ) {
1083 public function getIEMimeTypes( $fileName, $chunk, $proposed ) {
1084 $ca = $this->getIEContentAnalyzer();
1085 return $ca->getRealMimesFromData( $fileName, $chunk, $proposed );
1093 protected function getIEContentAnalyzer() {
1094 if ( is_null( $this->mIEAnalyzer ) ) {
1097 return $this->mIEAnalyzer;