MediaWiki  REL1_21
StreamFile.php
Go to the documentation of this file.
00001 <?php
00026 class StreamFile {
00027         const READY_STREAM = 1;
00028         const NOT_MODIFIED = 2;
00029 
00041         public static function stream( $fname, $headers = array(), $sendErrors = true ) {
00042                 wfProfileIn( __METHOD__ );
00043 
00044                 if ( FileBackend::isStoragePath( $fname ) ) { // sanity
00045                         throw new MWException( __FUNCTION__ . " given storage path '$fname'." );
00046                 }
00047 
00048                 wfSuppressWarnings();
00049                 $stat = stat( $fname );
00050                 wfRestoreWarnings();
00051 
00052                 $res = self::prepareForStream( $fname, $stat, $headers, $sendErrors );
00053                 if ( $res == self::NOT_MODIFIED ) {
00054                         $ok = true; // use client cache
00055                 } elseif ( $res == self::READY_STREAM ) {
00056                         wfProfileIn( __METHOD__ . '-send' );
00057                         $ok = readfile( $fname );
00058                         wfProfileOut( __METHOD__ . '-send' );
00059                 } else {
00060                         $ok = false; // failed
00061                 }
00062 
00063                 wfProfileOut( __METHOD__ );
00064                 return $ok;
00065         }
00066 
00080         public static function prepareForStream(
00081                 $path, $info, $headers = array(), $sendErrors = true
00082         ) {
00083                 if ( !is_array( $info ) ) {
00084                         if ( $sendErrors ) {
00085                                 header( 'HTTP/1.0 404 Not Found' );
00086                                 header( 'Cache-Control: no-cache' );
00087                                 header( 'Content-Type: text/html; charset=utf-8' );
00088                                 $encFile = htmlspecialchars( $path );
00089                                 $encScript = htmlspecialchars( $_SERVER['SCRIPT_NAME'] );
00090                                 echo "<html><body>
00091                                         <h1>File not found</h1>
00092                                         <p>Although this PHP script ($encScript) exists, the file requested for output
00093                                         ($encFile) does not.</p>
00094                                         </body></html>
00095                                         ";
00096                         }
00097                         return false;
00098                 }
00099 
00100                 // Sent Last-Modified HTTP header for client-side caching
00101                 header( 'Last-Modified: ' . wfTimestamp( TS_RFC2822, $info['mtime'] ) );
00102 
00103                 // Cancel output buffering and gzipping if set
00104                 wfResetOutputBuffers();
00105 
00106                 $type = self::contentTypeFromPath( $path );
00107                 if ( $type && $type != 'unknown/unknown' ) {
00108                         header( "Content-type: $type" );
00109                 } else {
00110                         // Send a content type which is not known to Internet Explorer, to
00111                         // avoid triggering IE's content type detection. Sending a standard
00112                         // unknown content type here essentially gives IE license to apply
00113                         // whatever content type it likes.
00114                         header( 'Content-type: application/x-wiki' );
00115                 }
00116 
00117                 // Don't stream it out as text/html if there was a PHP error
00118                 if ( headers_sent() ) {
00119                         echo "Headers already sent, terminating.\n";
00120                         return false;
00121                 }
00122 
00123                 // Send additional headers
00124                 foreach ( $headers as $header ) {
00125                         header( $header );
00126                 }
00127 
00128                 // Don't send if client has up to date cache
00129                 if ( !empty( $_SERVER['HTTP_IF_MODIFIED_SINCE'] ) ) {
00130                         $modsince = preg_replace( '/;.*$/', '', $_SERVER['HTTP_IF_MODIFIED_SINCE'] );
00131                         if ( wfTimestamp( TS_UNIX, $info['mtime'] ) <= strtotime( $modsince ) ) {
00132                                 ini_set( 'zlib.output_compression', 0 );
00133                                 header( "HTTP/1.0 304 Not Modified" );
00134                                 return self::NOT_MODIFIED; // ok
00135                         }
00136                 }
00137 
00138                 header( 'Content-Length: ' . $info['size'] );
00139 
00140                 return self::READY_STREAM; // ok
00141         }
00142 
00150         public static function contentTypeFromPath( $filename, $safe = true ) {
00151                 global $wgTrivialMimeDetection;
00152 
00153                 $ext = strrchr( $filename, '.' );
00154                 $ext = $ext === false ? '' : strtolower( substr( $ext, 1 ) );
00155 
00156                 # trivial detection by file extension,
00157                 # used for thumbnails (thumb.php)
00158                 if ( $wgTrivialMimeDetection ) {
00159                         switch ( $ext ) {
00160                                 case 'gif': return 'image/gif';
00161                                 case 'png': return 'image/png';
00162                                 case 'jpg': return 'image/jpeg';
00163                                 case 'jpeg': return 'image/jpeg';
00164                         }
00165 
00166                         return 'unknown/unknown';
00167                 }
00168 
00169                 $magic = MimeMagic::singleton();
00170                 // Use the extension only, rather than magic numbers, to avoid opening
00171                 // up vulnerabilities due to uploads of files with allowed extensions
00172                 // but disallowed types.
00173                 $type = $magic->guessTypesForExtension( $ext );
00174 
00179                 if ( $safe ) {
00180                         global $wgFileBlacklist, $wgCheckFileExtensions, $wgStrictFileExtensions,
00181                                 $wgFileExtensions, $wgVerifyMimeType, $wgMimeTypeBlacklist;
00182                         list( , $extList ) = UploadBase::splitExtensions( $filename );
00183                         if ( UploadBase::checkFileExtensionList( $extList, $wgFileBlacklist ) ) {
00184                                 return 'unknown/unknown';
00185                         }
00186                         if ( $wgCheckFileExtensions && $wgStrictFileExtensions
00187                                 && !UploadBase::checkFileExtensionList( $extList, $wgFileExtensions ) )
00188                         {
00189                                 return 'unknown/unknown';
00190                         }
00191                         if ( $wgVerifyMimeType && in_array( strtolower( $type ), $wgMimeTypeBlacklist ) ) {
00192                                 return 'unknown/unknown';
00193                         }
00194                 }
00195                 return $type;
00196         }
00197 }