MediaWiki  REL1_19
StreamFile.php
Go to the documentation of this file.
00001 <?php
00007 class StreamFile {
00008         const READY_STREAM = 1;
00009         const NOT_MODIFIED = 2;
00010 
00021         public static function stream( $fname, $headers = array(), $sendErrors = true ) {
00022                 wfSuppressWarnings();
00023                 $stat = stat( $fname );
00024                 wfRestoreWarnings();
00025 
00026                 $res = self::prepareForStream( $fname, $stat, $headers, $sendErrors );
00027                 if ( $res == self::NOT_MODIFIED ) {
00028                         return true; // use client cache
00029                 } elseif ( $res == self::READY_STREAM ) {
00030                         return readfile( $fname );
00031                 } else {
00032                         return false; // failed
00033                 }
00034         }
00035 
00049         public static function prepareForStream(
00050                 $path, $info, $headers = array(), $sendErrors = true
00051         ) {
00052                 global $wgLanguageCode;
00053 
00054                 if ( !is_array( $info ) ) {
00055                         if ( $sendErrors ) {
00056                                 header( 'HTTP/1.0 404 Not Found' );
00057                                 header( 'Cache-Control: no-cache' );
00058                                 header( 'Content-Type: text/html; charset=utf-8' );
00059                                 $encFile = htmlspecialchars( $path );
00060                                 $encScript = htmlspecialchars( $_SERVER['SCRIPT_NAME'] );
00061                                 echo "<html><body>
00062                                         <h1>File not found</h1>
00063                                         <p>Although this PHP script ($encScript) exists, the file requested for output
00064                                         ($encFile) does not.</p>
00065                                         </body></html>
00066                                         ";
00067                         }
00068                         return false;
00069                 }
00070 
00071                 // Sent Last-Modified HTTP header for client-side caching
00072                 header( 'Last-Modified: ' . wfTimestamp( TS_RFC2822, $info['mtime'] ) );
00073 
00074                 // Cancel output buffering and gzipping if set
00075                 wfResetOutputBuffers();
00076 
00077                 $type = self::contentTypeFromPath( $path );
00078                 if ( $type && $type != 'unknown/unknown' ) {
00079                         header( "Content-type: $type" );
00080                 } else {
00081                         // Send a content type which is not known to Internet Explorer, to
00082                         // avoid triggering IE's content type detection. Sending a standard
00083                         // unknown content type here essentially gives IE license to apply
00084                         // whatever content type it likes.
00085                         header( 'Content-type: application/x-wiki' );
00086                 }
00087 
00088                 // Don't stream it out as text/html if there was a PHP error
00089                 if ( headers_sent() ) {
00090                         echo "Headers already sent, terminating.\n";
00091                         return false;
00092                 }
00093 
00094                 header( "Content-Disposition: inline;filename*=utf-8'$wgLanguageCode'" .
00095                         urlencode( basename( $path ) ) );
00096 
00097                 // Send additional headers
00098                 foreach ( $headers as $header ) {
00099                         header( $header );
00100                 }
00101 
00102                 // Don't send if client has up to date cache
00103                 if ( !empty( $_SERVER['HTTP_IF_MODIFIED_SINCE'] ) ) {
00104                         $modsince = preg_replace( '/;.*$/', '', $_SERVER['HTTP_IF_MODIFIED_SINCE'] );
00105                         if ( wfTimestamp( TS_UNIX, $info['mtime'] ) <= strtotime( $modsince ) ) {
00106                                 ini_set( 'zlib.output_compression', 0 );
00107                                 header( "HTTP/1.0 304 Not Modified" );
00108                                 return self::NOT_MODIFIED; // ok
00109                         }
00110                 }
00111 
00112                 header( 'Content-Length: ' . $info['size'] );
00113 
00114                 return self::READY_STREAM; // ok
00115         }
00116 
00124         public static function contentTypeFromPath( $filename, $safe = true ) {
00125                 global $wgTrivialMimeDetection;
00126 
00127                 $ext = strrchr( $filename, '.' );
00128                 $ext = $ext === false ? '' : strtolower( substr( $ext, 1 ) );
00129 
00130                 # trivial detection by file extension,
00131                 # used for thumbnails (thumb.php)
00132                 if ( $wgTrivialMimeDetection ) {
00133                         switch ( $ext ) {
00134                                 case 'gif': return 'image/gif';
00135                                 case 'png': return 'image/png';
00136                                 case 'jpg': return 'image/jpeg';
00137                                 case 'jpeg': return 'image/jpeg';
00138                         }
00139 
00140                         return 'unknown/unknown';
00141                 }
00142 
00143                 $magic = MimeMagic::singleton();
00144                 // Use the extension only, rather than magic numbers, to avoid opening
00145                 // up vulnerabilities due to uploads of files with allowed extensions
00146                 // but disallowed types.
00147                 $type = $magic->guessTypesForExtension( $ext );
00148 
00153                 if ( $safe ) {
00154                         global $wgFileBlacklist, $wgCheckFileExtensions, $wgStrictFileExtensions,
00155                                 $wgFileExtensions, $wgVerifyMimeType, $wgMimeTypeBlacklist;
00156                         list( , $extList ) = UploadBase::splitExtensions( $filename );
00157                         if ( UploadBase::checkFileExtensionList( $extList, $wgFileBlacklist ) ) {
00158                                 return 'unknown/unknown';
00159                         }
00160                         if ( $wgCheckFileExtensions && $wgStrictFileExtensions
00161                                 && !UploadBase::checkFileExtensionList( $extList, $wgFileExtensions ) )
00162                         {
00163                                 return 'unknown/unknown';
00164                         }
00165                         if ( $wgVerifyMimeType && in_array( strtolower( $type ), $wgMimeTypeBlacklist ) ) {
00166                                 return 'unknown/unknown';
00167                         }
00168                 }
00169                 return $type;
00170         }
00171 }