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" );
574 private function doGuessMimeType(
$file,
$ext ) {
581 return 'unknown/unknown';
584 $fsize = filesize(
$file );
585 if ( $fsize ===
false ) {
586 return 'unknown/unknown';
589 $head = fread(
$f, 1024 );
590 $tailLength = min( 65558, $fsize );
591 if ( fseek(
$f, -1 * $tailLength, SEEK_END ) === -1 ) {
593 "Seeking $tailLength bytes from EOF failed in " . __METHOD__ );
595 $tail = fread(
$f, $tailLength );
598 wfDebug( __METHOD__ .
": analyzing head and tail of $file for magic numbers.\n" );
603 'MThd' =>
'audio/midi',
604 'OggS' =>
'application/ogg',
608 "\x01\x00\x09\x00" =>
'application/x-msmetafile',
609 "\xd7\xcd\xc6\x9a" =>
'application/x-msmetafile',
610 '%PDF' =>
'application/pdf',
611 'gimp xcf' =>
'image/x-xcf',
614 'MZ' =>
'application/octet-stream',
615 "\xca\xfe\xba\xbe" =>
'application/octet-stream',
616 "\x7fELF" =>
'application/octet-stream',
619 foreach ( $headers
as $magic => $candidate ) {
620 if ( strncmp( $head, $magic, strlen( $magic ) ) == 0 ) {
621 wfDebug( __METHOD__ .
": magic header in $file recognized as $candidate\n" );
627 if ( strncmp( $head, pack(
"C4", 0x1a, 0x45, 0xdf, 0xa3 ), 4 ) == 0 ) {
628 $doctype = strpos( $head,
"\x42\x82" );
631 $data = substr( $head, $doctype + 3, 8 );
632 if ( strncmp( $data,
"matroska", 8 ) == 0 ) {
633 wfDebug( __METHOD__ .
": recognized file as video/x-matroska\n" );
634 return "video/x-matroska";
635 } elseif ( strncmp( $data,
"webm", 4 ) == 0 ) {
636 wfDebug( __METHOD__ .
": recognized file as video/webm\n" );
640 wfDebug( __METHOD__ .
": unknown EBML file\n" );
641 return "unknown/unknown";
645 if ( strncmp( $head,
"RIFF", 4 ) == 0 && strncmp( substr( $head, 8, 8 ),
"WEBPVP8 ", 8 ) == 0 ) {
646 wfDebug( __METHOD__ .
": recognized file as image/webp\n" );
662 if ( ( strpos( $head,
'<?php' ) !==
false ) ||
663 ( strpos( $head,
"<\x00?\x00p\x00h\x00p" ) !==
false ) ||
664 ( strpos( $head,
"<\x00?\x00 " ) !==
false ) ||
665 ( strpos( $head,
"<\x00?\x00\n" ) !==
false ) ||
666 ( strpos( $head,
"<\x00?\x00\t" ) !==
false ) ||
667 ( strpos( $head,
"<\x00?\x00=" ) !==
false ) ) {
669 wfDebug( __METHOD__ .
": recognized $file as application/x-php\n" );
670 return 'application/x-php';
677 if ( $xml->wellFormed ) {
679 if ( isset( $wgXMLMimeTypes[$xml->getRootElement()] ) ) {
680 return $wgXMLMimeTypes[$xml->getRootElement()];
682 return 'application/xml';
692 if ( substr( $head, 0, 2 ) ==
"#!" ) {
693 $script_type =
"ASCII";
694 } elseif ( substr( $head, 0, 5 ) ==
"\xef\xbb\xbf#!" ) {
695 $script_type =
"UTF-8";
696 } elseif ( substr( $head, 0, 7 ) ==
"\xfe\xff\x00#\x00!" ) {
697 $script_type =
"UTF-16BE";
698 } elseif ( substr( $head, 0, 7 ) ==
"\xff\xfe#\x00!" ) {
699 $script_type =
"UTF-16LE";
702 if ( $script_type ) {
703 if ( $script_type !==
"UTF-8" && $script_type !==
"ASCII" ) {
705 $pack =
array(
'UTF-16BE' =>
'n*',
'UTF-16LE' =>
'v*' );
706 $chars = unpack( $pack[$script_type], substr( $head, 2 ) );
708 foreach ( $chars
as $codepoint ) {
709 if ( $codepoint < 128 ) {
710 $head .= chr( $codepoint );
719 if ( preg_match(
'%/?([^\s]+/)(\w+)%', $head, $match ) ) {
720 $mime =
"application/x-{$match[2]}";
721 wfDebug( __METHOD__ .
": shell script recognized as $mime\n" );
727 if ( strpos( $tail,
"PK\x05\x06" ) !==
false ) {
728 wfDebug( __METHOD__ .
": ZIP header present in $file\n" );
729 return $this->detectZipType( $head, $tail,
$ext );
733 $gis = getimagesize(
$file );
736 if ( $gis && isset( $gis[
'mime'] ) ) {
737 $mime = $gis[
'mime'];
738 wfDebug( __METHOD__ .
": getimagesize detected $file as $mime\n" );
743 $deja =
new DjVuImage(
$file );
744 if ( $deja->isValid() ) {
745 wfDebug( __METHOD__ .
": detected $file as image/vnd.djvu\n" );
746 return 'image/vnd.djvu';
765 function detectZipType( $header, $tail =
null,
$ext =
false ) {
766 if (
$ext ) { # TODO:
remove $ext param
767 wfDebug( __METHOD__ .
": WARNING: use of the \$ext parameter is deprecated. " .
768 "Use improveTypeFromExtension(\$mime, \$ext) instead.\n" );
771 $mime =
'application/zip';
772 $opendocTypes =
array(
781 'presentation-template',
783 'spreadsheet-template',
791 $types =
'(?:' . implode(
'|', $opendocTypes ) .
')';
792 $opendocRegex =
"/^mimetype(application\/vnd\.oasis\.opendocument\.$types)/";
794 $openxmlRegex =
"/^\[Content_Types\].xml/";
796 if ( preg_match( $opendocRegex, substr( $header, 30 ),
$matches ) ) {
798 wfDebug( __METHOD__ .
": detected $mime from ZIP archive\n" );
799 } elseif ( preg_match( $openxmlRegex, substr( $header, 30 ) ) ) {
800 $mime =
"application/x-opc+zip";
801 # TODO: remove the block below, as soon as improveTypeFromExtension is used everywhere
802 if (
$ext !==
true &&
$ext !==
false ) {
807 if ( $this->isMatchingExtension(
$ext,
$mime ) ) {
811 $mime = $this->guessTypesForExtension(
$ext );
813 $mime =
"application/zip";
816 wfDebug( __METHOD__ .
": detected an Open Packaging Conventions archive: $mime\n" );
817 } elseif ( substr( $header, 0, 8 ) ==
"\xd0\xcf\x11\xe0\xa1\xb1\x1a\xe1" &&
818 ( $headerpos = strpos( $tail,
"PK\x03\x04" ) ) !==
false &&
819 preg_match( $openxmlRegex, substr( $tail, $headerpos + 30 ) ) ) {
820 if ( substr( $header, 512, 4 ) ==
"\xEC\xA5\xC1\x00" ) {
821 $mime =
"application/msword";
823 switch ( substr( $header, 512, 6 ) ) {
824 case "\xEC\xA5\xC1\x00\x0E\x00":
825 case "\xEC\xA5\xC1\x00\x1C\x00":
826 case "\xEC\xA5\xC1\x00\x43\x00":
827 $mime =
"application/vnd.ms-powerpoint";
829 case "\xFD\xFF\xFF\xFF\x10\x00":
830 case "\xFD\xFF\xFF\xFF\x1F\x00":
831 case "\xFD\xFF\xFF\xFF\x22\x00":
832 case "\xFD\xFF\xFF\xFF\x23\x00":
833 case "\xFD\xFF\xFF\xFF\x28\x00":
834 case "\xFD\xFF\xFF\xFF\x29\x00":
835 case "\xFD\xFF\xFF\xFF\x10\x02":
836 case "\xFD\xFF\xFF\xFF\x1F\x02":
837 case "\xFD\xFF\xFF\xFF\x22\x02":
838 case "\xFD\xFF\xFF\xFF\x23\x02":
839 case "\xFD\xFF\xFF\xFF\x28\x02":
840 case "\xFD\xFF\xFF\xFF\x29\x02":
841 $mime =
"application/vnd.msexcel";
845 wfDebug( __METHOD__ .
": detected a MS Office document with OPC trailer\n" );
847 wfDebug( __METHOD__ .
": unable to identify type of ZIP archive\n" );
870 private function detectMimeType(
$file,
$ext =
true ) {
871 global $wgMimeDetectorCommand;
873 if (
$ext ) { # TODO: make
$ext default to
false. Or better,
remove it.
874 wfDebug( __METHOD__ .
": WARNING: use of the \$ext parameter is deprecated. Use improveTypeFromExtension(\$mime, \$ext) instead.\n" );
878 if ( $wgMimeDetectorCommand ) {
880 $m =
wfShellExec(
"$wgMimeDetectorCommand $args" );
881 } elseif ( function_exists(
"finfo_open" ) && function_exists(
"finfo_file" ) ) {
883 # This required the fileinfo extension by PECL,
884 # see http://pecl.php.net/package/fileinfo
885 # This must be compiled into PHP
887 # finfo is the official replacement for the deprecated
888 # mime_content_type function, see below.
890 # If you may need to load the fileinfo extension at runtime, set
891 # $wgLoadFileinfoExtension in LocalSettings.php
893 $mime_magic_resource = finfo_open( FILEINFO_MIME );
895 if ( $mime_magic_resource ) {
896 $m = finfo_file( $mime_magic_resource,
$file );
897 finfo_close( $mime_magic_resource );
899 wfDebug( __METHOD__ .
": finfo_open failed on " . FILEINFO_MIME .
"!\n" );
901 } elseif ( function_exists(
"mime_content_type" ) ) {
903 # NOTE: this function is available since PHP 4.3.0, but only if
904 # PHP was compiled with --with-mime-magic or, before 4.3.2, with --enable-mime-magic.
906 # On Windows, you must set mime_magic.magicfile in php.ini to point to the mime.magic file bundled with PHP;
907 # sometimes, this may even be needed under linus/unix.
909 # Also note that this has been DEPRECATED in favor of the fileinfo extension by PECL, see above.
910 # see http://www.php.net/manual/en/ref.mime-magic.php for details.
912 $m = mime_content_type(
$file );
914 wfDebug( __METHOD__ .
": no magic mime detector found!\n" );
919 $m = preg_replace(
'![;, ].*$!',
'', $m ); #strip charset,
etc
921 $m = strtolower( $m );
923 if ( strpos( $m,
'unknown' ) !==
false ) {
926 wfDebug( __METHOD__ .
": magic mime type of $file: $m\n" );
932 if (
$ext ===
true ) {
933 $i = strrpos(
$file,
'.' );
934 $ext = strtolower( $i ? substr(
$file, $i + 1 ) :
'' );
937 if ( $this->isRecognizableExtension(
$ext ) ) {
938 wfDebug( __METHOD__ .
": refusing to guess mime type for .$ext file, we should have recognized it\n" );
940 $m = $this->guessTypesForExtension(
$ext );
942 wfDebug( __METHOD__ .
": extension mime type of $file: $m\n" );
949 wfDebug( __METHOD__ .
": failed to guess mime type for $file!\n" );
950 return 'unknown/unknown';
969 function getMediaType(
$path =
null,
$mime =
null ) {
981 if (
$mime ==
'application/ogg' && file_exists(
$path ) ) {
988 $head = fread(
$f, 256 );
991 $head = strtolower( $head );
994 if ( strpos( $head,
'theora' ) !==
false ) {
996 } elseif ( strpos( $head,
'vorbis' ) !==
false ) {
998 } elseif ( strpos( $head,
'flac' ) !==
false ) {
1000 } elseif ( strpos( $head,
'speex' ) !==
false ) {
1017 $i = strrpos(
$path,
'.' );
1018 $e = strtolower( $i ? substr(
$path, $i + 1 ) :
'' );
1021 $type = $this->findMediaType(
'.' .
$e );
1029 $i = strpos(
$mime,
'/' );
1030 if ( $i !==
false ) {
1031 $major = substr(
$mime, 0, $i );
1032 $type = $this->findMediaType( $major );
1055 function findMediaType( $extMime ) {
1056 if ( strpos( $extMime,
'.' ) === 0 ) {
1058 $m = $this->getTypesForExtension( substr( $extMime, 1 ) );
1063 $m = explode(
' ', $m );
1066 if ( isset( $this->mMimeTypeAliases[$extMime] ) ) {
1067 $extMime = $this->mMimeTypeAliases[$extMime];
1070 $m =
array( $extMime );
1074 foreach ( $this->mMediaTypes
as $type => $codes ) {
1075 if ( in_array(
$mime, $codes,
true ) ) {
1093 public function getIEMimeTypes( $fileName, $chunk, $proposed ) {
1094 $ca = $this->getIEContentAnalyzer();
1095 return $ca->getRealMimesFromData( $fileName, $chunk, $proposed );
1103 protected function getIEContentAnalyzer() {
1104 if ( is_null( $this->mIEAnalyzer ) ) {
1107 return $this->mIEAnalyzer;