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