<?php
-/** Module defining helper functions for detecting and dealing with mime types.
+/**
+ * Module defining helper functions for detecting and dealing with mime types.
*
+ * @file
*/
- /** Defines a set of well known mime types
+/**
+ * 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.
+ *
+ * This list concatenated with mime.types is used to create a mime <-> ext
+ * map. Each line contains a mime type followed by a space separated list of
+ * extensions. If multiple extensions for a single mime type exist or if
+ * multiple mime types exist for a single extension then in most cases
+ * MediaWiki assumes that the first extension following the mime type is the
+ * canonical extension, and the first time a mime type appears for a certain
+ * extension is considered the canonical mime type.
+ *
+ * (Note that appending $wgMimeTypeFile to the end of MM_WELL_KNOWN_MIME_TYPES
+ * sucks because you can't redefine canonical types. This could be fixed by
+ * appending MM_WELL_KNOWN_MIME_TYPES behind $wgMimeTypeFile, but who knows
+ * what will break? In practice this probably isn't a problem anyway -- Bryan)
*/
define('MM_WELL_KNOWN_MIME_TYPES',<<<END_STRING
-application/ogg ogg ogm ogv
+application/ogg ogx ogg ogm ogv oga spx
application/pdf pdf
application/vnd.oasis.opendocument.chart odc
application/vnd.oasis.opendocument.chart-template otc
audio/mpeg mpga mpa mp2 mp3
audio/x-aiff aif aiff aifc
audio/x-wav wav
-audio/ogg ogg
+audio/ogg oga spx ogg
image/x-bmp bmp
image/gif gif
image/jpeg jpeg jpg jpe
image/png png
-image/svg+xml image/svg svg
+image/svg+xml svg
+image/svg svg
image/tiff tiff tif
-image/vnd.djvu image/x.djvu image/x-djvu djvu
+image/vnd.djvu djvu
+image/x.djvu djvu
+image/x-djvu djvu
image/x-portable-pixmap ppm
image/x-xcf xcf
text/plain txt
text/html html htm
-video/ogg ogm ogg ogv
+video/ogg ogv ogm ogg
video/mpeg mpg mpeg
END_STRING
);
- /** Defines a set of well known mime info entries
+/**
+ * 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.
audio/x-wav [AUDIO]
audio/mp3 audio/mpeg [AUDIO]
application/ogg audio/ogg video/ogg [MULTIMEDIA]
-image/x-bmp image/bmp [BITMAP]
+image/x-bmp image/x-ms-bmp image/bmp [BITMAP]
image/gif [BITMAP]
image/jpeg [BITMAP]
image/png [BITMAP]
global $wgLoadFileinfoExtension;
if ($wgLoadFileinfoExtension) {
- if(!extension_loaded('fileinfo')) dl('fileinfo.' . PHP_SHLIB_SUFFIX);
+ wfDl( 'fileinfo' );
}
/**
* Mapping of media types to arrays of mime types.
* This is used by findMediaType and getMediaType, respectively
*/
- var $mMediaTypes= NULL;
+ var $mMediaTypes= null;
/** Map of mime type aliases
*/
- var $mMimeTypeAliases= NULL;
+ var $mMimeTypeAliases= null;
/** map of mime types to file extensions (as a space seprarated list)
*/
- var $mMimeToExt= NULL;
+ var $mMimeToExt= null;
/** map of file extensions types to mime types (as a space seprarated list)
*/
- var $mExtToMime= NULL;
+ var $mExtToMime= null;
+
+ /** IEContentAnalyzer instance
+ */
+ var $mIEAnalyzer;
/** The singleton instance
*/
*/
function guessTypesForExtension( $ext ) {
$m = $this->getTypesForExtension( $ext );
- if ( is_null( $m ) ) return NULL;
+ if ( is_null( $m ) ) return null;
$m = trim( $m );
$m = preg_replace( '/\s.*$/', '', $m );
$ext = $this->getExtensionsForType( $mime );
if ( !$ext ) {
- return NULL; //unknown
+ return null; //unknown
}
$ext = explode( ' ', $ext );
'xbm',
// Formats we recognize magic numbers for
- 'djvu', 'ogg', 'ogv', 'mid', 'pdf', 'wmf', 'xcf',
+ 'djvu', 'ogx', 'ogg', 'ogv', 'oga', 'spx',
+ 'mid', 'pdf', 'wmf', 'xcf', 'webm', 'mkv', 'mka',
+ 'webp',
// XML formats we sure hope we recognize reliably
'svg',
return in_array( strtolower( $extension ), $types );
}
+ /** improves a mime type using the file extension. Some file formats are very generic,
+ * so their mime type is not very meaningful. A more useful mime type can be derived
+ * by looking at the file extension. Typically, this method would be called on the
+ * result of guessMimeType().
+ *
+ * Currently, this method does the following:
+ *
+ * If $mime is "unknown/unknown" and isRecognizableExtension( $ext ) returns false,
+ * return the result of guessTypesForExtension($ext).
+ *
+ * If $mime is "application/x-opc+zip" and isMatchingExtension( $ext, $mime )
+ * gives true, return the result of guessTypesForExtension($ext).
+ *
+ * @param $mime String: the mime type, typically guessed from a file's content.
+ * @param $ext String: the file extension, as taken from the file name
+ *
+ * @return string the mime type
+ */
+ function improveTypeFromExtension( $mime, $ext ) {
+ if ( $mime === "unknown/unknown" ) {
+ if( $this->isRecognizableExtension( $ext ) ) {
+ wfDebug( __METHOD__. ": refusing to guess mime type for .$ext file, " .
+ "we should have recognized it\n" );
+ } else {
+ /* Not something we can detect, so simply
+ * trust the file extension */
+ $mime = $this->guessTypesForExtension( $ext );
+ }
+ }
+ else if ( $mime === "application/x-opc+zip" ) {
+ if ( $this->isMatchingExtension( $ext, $mime ) ) {
+ /* A known file extension for an OPC file,
+ * find the proper mime type for that file extension */
+ $mime = $this->guessTypesForExtension( $ext );
+ } else {
+ wfDebug( __METHOD__. ": refusing to guess better type for $mime file, " .
+ ".$ext is not a known OPC extension.\n" );
+ $mime = "application/zip";
+ }
+ }
+
+ if ( isset( $this->mMimeTypeAliases[$mime] ) ) {
+ $mime = $this->mMimeTypeAliases[$mime];
+ }
+
+ wfDebug(__METHOD__.": improved mime type for .$ext: $mime\n");
+ return $mime;
+ }
/** mime type detection. This uses detectMimeType to detect the mime 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).
+ * or misinterpreter by the default mime detection (namely XML based formats like XHTML or SVG,
+ * as well as ZIP based formats like OPC/ODF files).
*
- * @param string $file The file to check
- * @param mixed $ext The file extension, or true to extract it from the filename.
- * Set it to false to ignore the extension.
+ * @param $file String: the file to check
+ * @param $ext Mixed: the file extension, or true (default) to extract it from the filename.
+ * Set it to false to ignore the extension. DEPRECATED! Set to false, use
+ * improveTypeFromExtension($mime, $ext) later to improve mime type.
*
* @return string the mime type of $file
*/
function guessMimeType( $file, $ext = true ) {
+ if( $ext ) { # TODO: make $ext default to false. Or better, remove it.
+ wfDebug( __METHOD__.": WARNING: use of the \$ext parameter is deprecated. " .
+ "Use improveTypeFromExtension(\$mime, \$ext) instead.\n" );
+ }
+
$mime = $this->doGuessMimeType( $file, $ext );
if( !$mime ) {
$mime = $this->mMimeTypeAliases[$mime];
}
- wfDebug(__METHOD__.": final mime type of $file: $mime\n");
+ wfDebug(__METHOD__.": guessed mime type of $file: $mime\n");
return $mime;
}
- function doGuessMimeType( $file, $ext = true ) {
+ private function doGuessMimeType( $file, $ext ) { # TODO: remove $ext param
// Read a chunk of the file
wfSuppressWarnings();
$f = fopen( $file, "rt" );
$tail = fread( $f, 65558 ); // 65558 = maximum size of a zip EOCDR
fclose( $f );
+ wfDebug( __METHOD__ . ": analyzing head and tail of $file for magic numbers.\n" );
+
// Hardcode a few magic number checks...
$headers = array(
// Multimedia...
}
}
+ /* Look for WebM and Matroska files */
+ if( strncmp( $head, pack( "C4", 0x1a, 0x45, 0xdf, 0xa3 ), 4 ) == 0 ) {
+ $doctype = strpos( $head, "\x42\x82" );
+ if( $doctype ) {
+ // Next byte is datasize, then data (sizes larger than 1 byte are very stupid muxers)
+ $data = substr($head, $doctype+3, 8);
+ if( strncmp( $data, "matroska", 8 ) == 0 ) {
+ wfDebug( __METHOD__ . ": recognized file as video/x-matroska\n" );
+ return "video/x-matroska";
+ } else if ( strncmp( $data, "webm", 4 ) == 0 ) {
+ wfDebug( __METHOD__ . ": recognized file as video/webm\n" );
+ return "video/webm";
+ }
+ }
+ wfDebug( __METHOD__ . ": unknown EBML file\n" );
+ return "unknown/unknown";
+ }
+
+ /* Look for WebP */
+ if( strncmp( $head, "RIFF", 4 ) == 0 && strncmp( substr( $head, 8, 8), "WEBPVP8 ", 8 ) == 0 ) {
+ wfDebug( __METHOD__ . ": recognized file as image/webp\n" );
+ return "image/webp";
+ }
+
/*
- * look for PHP
- * Check for this before HTML/XML...
- * Warning: this is a heuristic, and won't match a file with a lot of non-PHP before.
- * It will also match text files which could be PHP. :)
+ * Look for PHP. Check for this before HTML/XML... Warning: this is a
+ * heuristic, and won't match a file with a lot of non-PHP before. It
+ * will also match text files which could be PHP. :)
+ *
+ * FIXME: For this reason, the check is probably useless -- an attacker
+ * could almost certainly just pad the file with a lot of nonsense to
+ * circumvent the check in any case where it would be a security
+ * problem. On the other hand, it causes harmful false positives (bug
+ * 16583). The heuristic has been cut down to exclude three-character
+ * strings like "<? ", but should it be axed completely?
*/
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 ) ||
/*
* look for shell scripts
*/
- $script_type = NULL;
+ $script_type = null;
# detect by shebang
if ( substr( $head, 0, 2) == "#!" ) {
}
}
- // Check for ZIP (before getimagesize)
+ // Check for ZIP variants (before getimagesize)
if ( strpos( $tail, "PK\x05\x06" ) !== false ) {
- wfDebug( __METHOD__.": ZIP header present at end of $file\n" );
- return $this->detectZipType( $head );
+ wfDebug( __METHOD__.": ZIP header present in $file\n" );
+ return $this->detectZipType( $head, $tail, $ext );
}
wfSuppressWarnings();
/**
* Detect application-specific file type of a given ZIP file from its
- * header data. Currently works for OpenDocument types...
+ * header data. Currently works for OpenDocument and OpenXML types...
* If can't tell, returns 'application/zip'.
*
- * @param string $header Some reasonably-sized chunk of file header
+ * @param $header String: some reasonably-sized chunk of file header
+ * @param $tail String: the tail of the file
+ * @param $ext Mixed: the file extension, or true to extract it from the filename.
+ * Set it to false (default) to ignore the extension. DEPRECATED! Set to false,
+ * use improveTypeFromExtension($mime, $ext) later to improve mime type.
+ *
* @return string
*/
- function detectZipType( $header ) {
+ function detectZipType( $header, $tail = null, $ext = false ) {
+ if( $ext ) { # TODO: remove $ext param
+ wfDebug( __METHOD__.": WARNING: use of the \$ext parameter is deprecated. " .
+ "Use improveTypeFromExtension(\$mime, \$ext) instead.\n" );
+ }
+
+ $mime = 'application/zip';
$opendocTypes = array(
- 'chart',
'chart-template',
- 'formula',
+ 'chart',
'formula-template',
- 'graphics',
+ 'formula',
'graphics-template',
- 'image',
+ 'graphics',
'image-template',
- 'presentation',
+ 'image',
'presentation-template',
- 'spreadsheet',
+ 'presentation',
'spreadsheet-template',
- 'text',
+ 'spreadsheet',
'text-template',
'text-master',
- 'text-web' );
+ 'text-web',
+ 'text' );
// http://lists.oasis-open.org/archives/office/200505/msg00006.html
$types = '(?:' . implode( '|', $opendocTypes ) . ')';
$opendocRegex = "/^mimetype(application\/vnd\.oasis\.opendocument\.$types)/";
- wfDebug( __METHOD__.": $opendocRegex\n" );
-
+
+ $openxmlRegex = "/^\[Content_Types\].xml/";
+
if( preg_match( $opendocRegex, substr( $header, 30 ), $matches ) ) {
$mime = $matches[1];
wfDebug( __METHOD__.": detected $mime from ZIP archive\n" );
- return $mime;
+ } elseif( preg_match( $openxmlRegex, substr( $header, 30 ) ) ) {
+ $mime = "application/x-opc+zip";
+ # TODO: remove the block below, as soon as improveTypeFromExtension is used everywhere
+ if( $ext !== true && $ext !== false ) {
+ /** This is the mode used by getPropsFromPath
+ * These mime's are stored in the database, where we don't really want
+ * x-opc+zip, because we use it only for internal purposes
+ */
+ if( $this->isMatchingExtension( $ext, $mime) ) {
+ /* A known file extension for an OPC file,
+ * find the proper mime type for that file extension */
+ $mime = $this->guessTypesForExtension( $ext );
+ } else {
+ $mime = "application/zip";
+ }
+ }
+ wfDebug( __METHOD__.": detected an Open Packaging Conventions archive: $mime\n" );
+ } else if( substr( $header, 0, 8 ) == "\xd0\xcf\x11\xe0\xa1\xb1\x1a\xe1" &&
+ ($headerpos = strpos( $tail, "PK\x03\x04" ) ) !== false &&
+ preg_match( $openxmlRegex, substr( $tail, $headerpos + 30 ) ) ) {
+ if( substr( $header, 512, 4) == "\xEC\xA5\xC1\x00" ) {
+ $mime = "application/msword";
+ }
+ switch( substr( $header, 512, 6) ) {
+ case "\xEC\xA5\xC1\x00\x0E\x00":
+ case "\xEC\xA5\xC1\x00\x1C\x00":
+ case "\xEC\xA5\xC1\x00\x43\x00":
+ $mime = "application/vnd.ms-powerpoint";
+ break;
+ case "\xFD\xFF\xFF\xFF\x10\x00":
+ case "\xFD\xFF\xFF\xFF\x1F\x00":
+ case "\xFD\xFF\xFF\xFF\x22\x00":
+ case "\xFD\xFF\xFF\xFF\x23\x00":
+ case "\xFD\xFF\xFF\xFF\x28\x00":
+ case "\xFD\xFF\xFF\xFF\x29\x00":
+ case "\xFD\xFF\xFF\xFF\x10\x02":
+ case "\xFD\xFF\xFF\xFF\x1F\x02":
+ case "\xFD\xFF\xFF\xFF\x22\x02":
+ case "\xFD\xFF\xFF\xFF\x23\x02":
+ case "\xFD\xFF\xFF\xFF\x28\x02":
+ case "\xFD\xFF\xFF\xFF\x29\x02":
+ $mime = "application/vnd.msexcel";
+ break;
+ }
+
+ wfDebug( __METHOD__.": detected a MS Office document with OPC trailer\n");
} else {
wfDebug( __METHOD__.": unable to identify type of ZIP archive\n" );
- return 'application/zip';
}
+ return $mime;
}
/** Internal mime type detection, please use guessMimeType() for application code instead.
* 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 mixed $ext The file extension, or true to extract it from the filename.
- * Set it to false to ignore the extension.
+ * @param $file String: the file to check
+ * @param $ext Mixed: the file extension, or true (default) to extract it from the filename.
+ * Set it to false to ignore the extension. DEPRECATED! Set to false, use
+ * improveTypeFromExtension($mime, $ext) later to improve mime type.
*
* @return string the mime type of $file
* @access private
*/
- function detectMimeType( $file, $ext = true ) {
+ private function detectMimeType( $file, $ext = true ) {
global $wgMimeDetectorCommand;
- $m = NULL;
+ if( $ext ) { # TODO: make $ext default to false. Or better, remove it.
+ wfDebug( __METHOD__.": WARNING: use of the \$ext parameter is deprecated. Use improveTypeFromExtension(\$mime, \$ext) instead.\n" );
+ }
+
+ $m = null;
if ( $wgMimeDetectorCommand ) {
$fn = wfEscapeShellArg( $file );
$m = `$wgMimeDetectorCommand $fn`;
$m = strtolower( $m );
if ( strpos( $m, 'unknown' ) !== false ) {
- $m = NULL;
+ $m = null;
} else {
wfDebug( __METHOD__.": magic mime type of $file: $m\n" );
return $m;
* @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
+ * @param $path String: 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.
+ * @param $mime String: 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 ) {
+ function getMediaType( $path = null, $mime = null ) {
if( !$mime && !$path ) return MEDIATYPE_UNKNOWN;
# If mime type is unknown, guess it
}
# Check for entry for file extension
- $e = NULL;
if ( $path ) {
$i = strrpos( $path, '.' );
$e = strtolower( $i ? substr( $path, $i + 1 ) : '' );
}
/**
- * Get the MIME type from ieMimeFromData(), but convert the result from IE's
- * idiosyncratic private types into something MediaWiki will understand.
+ * Get the MIME types that various versions of Internet Explorer would
+ * detect from a chunk of the content.
*
- * @param string $fileName The file name (unused at present)
- * @param string $chunk The first 256 bytes of the file
- * @param string $proposed The MIME type proposed by the server
+ * @param $fileName String: the file name (unused at present)
+ * @param $chunk String: the first 256 bytes of the file
+ * @param $proposed String: the MIME type proposed by the server
*/
- public function getIEMimeType( $fileName, $chunk, $proposed ) {
- static $table = array(
- 'image/pjpeg' => 'image/jpeg',
- 'image/x-png' => 'image/png',
- 'image/x-wmf' => 'application/x-msmetafile',
- 'image/bmp' => 'image/x-bmp',
- 'application/x-zip-compressed' => 'application/zip',
- 'application/x-compressed' => 'application/x-compress',
- 'application/x-gzip-compressed' => 'application/x-gzip',
- 'audio/mid' => 'audio/midi',
- );
-
- $type = $this->ieMimeFromData( $fileName, $chunk, $proposed );
- if ( isset( $table[$type] ) ) {
- $type = $table[$type];
- }
- return $type;
+ public function getIEMimeTypes( $fileName, $chunk, $proposed ) {
+ $ca = $this->getIEContentAnalyzer();
+ return $ca->getRealMimesFromData( $fileName, $chunk, $proposed );
}
/**
- * Do a MIME type check similar to IE's FindMimeFromData(). This is used to
- * ensure that a file won't be detected as a blacklisted type such as text/html,
- * thus opening up an XSS vulnerability.
- *
- * Based on a disassembly of urlmon.dll from IE 7.
- *
- * @param string $fileName The file name (unused at present)
- * @param string $chunk The first 256 bytes of the file
- * @param string $proposed The MIME type proposed by the server
+ * Get a cached instance of IEContentAnalyzer
*/
- public function ieMimeFromData( $fileName, $chunk, $proposed ) {
- // IE puts a null character at byte 255 (the 256th byte)
- $chunk = substr( $chunk, 0, 255 );
-
- $types = array(
- 'ambiguous' /*1*/ => array(
- 'text/plain',
- 'application/octet-stream',
- 'application/x-netcdf', // [sic]
- 'unknown/unknown', // for MediaWiki
- ),
- 'text' /*3*/ => array(
- 'text/richtext', 'image/x-bitmap', 'application/postscript', 'application/base64',
- 'application/macbinhex40', 'application/x-cdf', 'text/scriptlet', 'text/xml',
- 'application/xml',
-
- ),
- 'binary' /*4*/ => array(
- 'application/pdf', 'audio/x-aiff', 'audio/basic', 'audio/wav', 'image/gif',
- 'image/pjpeg', 'image/jpeg', 'image/tiff', 'image/x-png', 'image/png', 'image/bmp',
- 'image/x-jg', 'image/x-art', 'image/x-emf', 'image/x-wmf', 'video/avi',
- 'video/x-msvideo', 'video/mpeg', 'application/x-compressed',
- 'application/x-zip-compressed', 'application/x-gzip-compressed', 'application/java',
- 'application/x-msdownload'
- ),
- 'html' /*5*/ => array( 'text/html' ),
- );
-
- if ( in_array( $proposed, $types['text'] ) ) {
- return $proposedType;
+ protected function getIEContentAnalyzer() {
+ if ( is_null( $this->mIEAnalyzer ) ) {
+ $this->mIEAnalyzer = new IEContentAnalyzer;
}
- if ( !in_array( $proposed, $types['binary'] )
- && !in_array( $proposed, $types['ambiguous'] )
- && !in_array( $proposed, $types['html'] ) )
- {
- // Unknown
- return $proposed;
- }
-
- // CContentAnalyzer::SampleData
- $found = array();
- $numControl = 0;
- $numHigh = 0;
- $numLow = 0;
- $numLF = 0;
- $numCR = 0;
- $numFF = 0;
- $htmlTags = array(
- 'html',
- 'head',
- 'title',
- 'body',
- 'script',
- 'a href',
- 'pre',
- 'img',
- 'plaintext',
- 'table'
- );
- $rdfUrl = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#';
- $rdfPurl = 'http://purl.org/rss/1.0/';
- $xbmMagic1 = '#define';
- $xbmMagic2 = '_width';
- $xbmMagic3 = '_bits';
- $binhexMagic = 'converted with BinHex';
-
- for ( $offset = 0; $offset < strlen( $chunk ); $offset++ ) {
- $curChar = $chunk[$offset];
- if ( $curChar == "\x0a" ) {
- $numLF++;
- continue;
- } elseif ( $curChar == "\x0d" ) {
- $numCR++;
- continue;
- } elseif ( $curChar == "\x0c" ) {
- $numFF++;
- continue;
- } elseif ( $curChar == "\t" ) {
- $numLow++;
- continue;
- } elseif ( ord( $curChar ) < 32 ) {
- $numControl++;
- continue;
- } elseif ( ord( $curChar ) >= 128 ) {
- $numHigh++;
- continue;
- }
-
- $numLow++;
- if ( $curChar == '<' ) {
- // XML
- $remainder = substr( $chunk, $offset + 1 );
- if ( !strncasecmp( $remainder, '?XML', 4 ) ) {
- $nextChar = substr( $chunk, $offset + 5, 1 );
- if ( $nextChar == ':' || $nextChar == ' ' || $nextChar == "\t" ) {
- $found['xml'] = true;
- }
- }
- // Scriptlet (JSP)
- if ( !strncasecmp( $remainder, 'SCRIPTLET', 9 ) ) {
- $found['scriptlet'] = true;
- break;
- }
- // HTML
- foreach ( $htmlTags as $tag ) {
- if ( !strncasecmp( $remainder, $tag, strlen( $tag ) ) ) {
- $found['html'] = true;
- }
- }
- // Skip broken check for additional tags (HR etc.)
-
- // RSS
- if ( !strncasecmp( $remainder, 'RSS', 3 ) ) {
- $found['rss'] = true;
- break; // return from SampleData
- }
- if ( !strncasecmp( $remainder, 'rdf:RDF', 7 ) ) {
- $found['rdf-tag'] = true;
- // no break
- }
- if ( !strncasecmp( $remainder, 'FEED', 4 ) ) {
- $found['feed'] = true;
- break;
- }
- continue;
- }
- // Skip broken check for -->
-
- // RSS URL checks
- // For some reason both URLs must appear before a break is triggered
- $remainder = substr( $chunk, $offset );
- if ( !strncasecmp( $remainder, $rdfUrl, strlen( $rdfUrl ) ) ) {
- $found['rdf-url'] = true;
- if ( isset( $found['rdf-tag'] )
- && isset( $found['rdf-purl'] ) ) // [sic]
- {
- break;
- }
- continue;
- }
-
- if ( !strncasecmp( $remainder, $rdfPurl, strlen( $rdfPurl ) ) ) {
- if ( isset( $found['rdf-tag'] )
- && isset( $found['rdf-url'] ) ) // [sic]
- {
- break;
- }
- continue;
- }
-
- // XBM checks
- if ( !strncasecmp( $remainder, $xbmMagic1, strlen( $xbmMagic1 ) ) ) {
- $found['xbm1'] = true;
- continue;
- }
- if ( $curChar == '_' ) {
- if ( isset( $found['xbm2'] ) ) {
- if ( !strncasecmp( $remainder, $xbmMagic3, strlen( $xbmMagic3 ) ) ) {
- $found['xbm'] = true;
- break;
- }
- } elseif ( isset( $found['xbm1'] ) ) {
- if ( !strncasecmp( $remainder, $xbmMagic2, strlen( $xbmMagic2 ) ) ) {
- $found['xbm2'] = true;
- }
- }
- }
-
- // BinHex
- if ( !strncasecmp( $remainder, $binhexMagic, strlen( $binhexMagic ) ) ) {
- $found['binhex'] = true;
- }
- } // end main loop
-
- // CContentAnalyzer::FindMimeFromData
-
- // IE does the Check*Headers() calls last, and instead does the following image
- // type checksby directly looking for the magic numbers. What I do here should
- // have the same effect since the magic number checks are identical in both cases.
- $binaryType = $this->ieCheckBinaryHeaders( $chunk );
- $textType = $this->ieCheckTextHeaders( $chunk );
-
- if ( $proposed == 'text/html' && isset( $found['html'] ) ) {
- return 'text/html';
- }
- if ( $proposed == 'image/gif' && $binaryType == 'image/gif' ) {
- return 'image/gif';
- }
- if ( ( $proposed == 'image/pjpeg' || $proposed == 'image/jpeg' )
- && $binaryType == 'image/pjpeg' )
- {
- return $proposed;
- }
- if ( ( $proposed == 'image/x-png' || $proposed == 'image/png' )
- && $binaryType == 'image/x-png' )
- {
- return $proposed;
- }
-
- if ( isset( $found['rss'] ) ) {
- return 'application/rss+xml';
- }
- if ( isset( $found['rdf-tag'] )
- && isset( $found['rdf-url'] )
- && isset( $found['rdf-purl'] ) )
- {
- return 'application/rss+xml';
- }
- // Skip apparently unimplemented atom flag
- if ( isset( $found['xml'] ) ) {
- // Skip check for proposed MIME type
- // IE may continue on here to further checks if a feature is enabled
- // and if the server proposes something other than text/html or text/xml
- return 'text/xml';
- }
- if ( isset( $found['html'] ) ) {
- // Skip complex XML-related check
- return 'text/html';
- }
- if ( isset( $found['xbm'] ) ) {
- return 'image/x-bitmap';
- }
- if ( isset( $found['binhex'] ) ) {
- return 'application/macbinhex40';
- }
- if ( isset( $found['scriptlet'] ) ) {
- // Skip complex HTML-related check
- return 'text/scriptlet';
- }
-
- // Freaky heuristics to determine check order
- // Probably doesn't matter since the checks appear to be orthogonal
- // The heuristic is of course broken for non-ASCII text
- if ( $numControl != 0 && ( $numFF + $numLow ) < ( $numControl + $numHigh ) * 16 ) {
- $kindOfBinary = true;
- $type = $binaryType ? $binaryType : $textType;
- } else {
- $kindOfBinary = false;
- $type = $textType ? $textType : $binaryType;
- }
- if ( $type !== false ) {
- return $type;
- }
-
- // FormatAgreesWithData()
- if ( in_array( $proposed, $types['text'] ) && !$kindOfBinary ) {
- return $proposed;
- }
- if ( in_array( $proposed, $types['binary'] ) && $kindOfBinary ) {
- return $proposed;
- }
- if ( in_array( $proposed, $types['html'] ) ) {
- return $proposed;
- }
-
- // TODO: extension checks
- return $proposed;
- }
-
- private function ieCheckTextHeaders( $chunk ) {
- $chunk2 = substr( $chunk, 0, 2 );
- $chunk4 = substr( $chunk, 0, 4 );
- $chunk5 = substr( $chunk, 0, 5 );
- if ( $chunk4 == '%PDF' ) {
- return 'application/pdf';
- }
- if ( $chunk2 == '%!' ) {
- return 'application/postscript';
- }
- if ( $chunk5 == '{\\rtf' ) {
- return 'text/richtext';
- }
- if ( $chunk5 == 'begin' ) {
- return 'application/base64';
- }
- return false;
- }
-
- private function ieCheckBinaryHeaders( $chunk ) {
- $chunk2 = substr( $chunk, 0, 2 );
- $chunk3 = substr( $chunk, 0, 3 );
- $chunk4 = substr( $chunk, 0, 4 );
- $chunk5 = substr( $chunk, 0, 5 );
- $chunk8 = substr( $chunk, 0, 8 );
- if ( $chunk5 == 'GIF87' || $chunk5 == 'GIF89' ) {
- return 'image/gif';
- }
- if ( $chunk2 == "\xff\xd8" ) {
- return 'image/pjpeg'; // actually plain JPEG but this is what IE returns
- }
- if ( $chunk2 == 'BM'
- && substr( $chunk, 6, 2 ) == "\000\000"
- && substr( $chunk, 8, 2 ) != "\000\000" )
- {
- return 'image/bmp'; // another non-standard MIME
- }
- if ( $chunk4 == 'RIFF'
- && substr( $chunk, 8, 4 ) == 'WAVE' )
- {
- return 'audio/wav';
- }
- // These were integer literals in IE
- // Perhaps the author was not sure what the target endianness was
- if ( $chunk4 == ".sd\000"
- || $chunk4 == ".snd"
- || $chunk4 == "\000ds."
- || $chunk4 == "dns." )
- {
- return 'audio/basic';
- }
- if ( $chunk3 == "MM\000" ) {
- return 'image/tiff';
- }
- if ( $chunk2 == 'MZ' ) {
- return 'application/x-msdownload';
- }
- if ( $chunk8 == "\x89PNG\x0d\x0a\x1a\x0a" ) {
- return 'image/x-png'; // [sic]
- }
- if ( strlen( $chunk ) >= 5 ) {
- $byte2 = ord( $chunk[2] );
- $byte4 = ord( $chunk[4] );
- if ( $byte2 >= 3 && $byte2 <= 31 && $byte4 == 0 && $chunk2 == 'JG' ) {
- return 'image/x-jg';
- }
- }
- // More endian confusion
- if ( $chunk4 == 'MROF' ) {
- return 'audio/x-aiff';
- }
- $chunk4_8 = substr( $chunk, 8, 4 );
- if ( $chunk4 == 'FORM' && ( $chunk4_8 == 'AIFF' || $chunk4_8 == 'AIFC' ) ) {
- return 'audio/x-aiff';
- }
- if ( $chunk4 == 'RIFF' && $chunk4_8 == 'AVI ' ) {
- return 'video/avi';
- }
- if ( $chunk4 == "\x00\x00\x01\xb3" || $chunk4 == "\x00\x00\x01\xba" ) {
- return 'video/mpeg';
- }
- if ( $chunk4 == "\001\000\000\000"
- && substr( $chunk, 40, 4 ) == ' EMF' )
- {
- return 'image/x-emf';
- }
- if ( $chunk4 == "\xd7\xcd\xc6\x9a" ) {
- return 'image/x-wmf';
- }
- if ( $chunk4 == "\xca\xfe\xba\xbe" ) {
- return 'application/java';
- }
- if ( $chunk2 == 'PK' ) {
- return 'application/x-zip-compressed';
- }
- if ( $chunk2 == "\x1f\x9d" ) {
- return 'application/x-compressed';
- }
- if ( $chunk2 == "\x1f\x8b" ) {
- return 'application/x-gzip-compressed';
- }
- // Skip redundant check for ZIP
- if ( $chunk5 == "MThd\000" ) {
- return 'audio/mid';
- }
- if ( $chunk4 == '%PDF' ) {
- return 'application/pdf';
- }
- return false;
+ return $this->mIEAnalyzer;
}
-
}