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