MediaWiki PHP Cross Reference Collaborative Wikis

Source: /includes/StreamFile.php - 198 lines - 6612 bytes - Summary - Text - Print

Description: Functions related to the output of file content. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.

   1  <?php
   2  /**
   3   * Functions related to the output of file content.
   4   *
   5   * This program is free software; you can redistribute it and/or modify
   6   * it under the terms of the GNU General Public License as published by
   7   * the Free Software Foundation; either version 2 of the License, or
   8   * (at your option) any later version.
   9   *
  10   * This program is distributed in the hope that it will be useful,
  11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13   * GNU General Public License for more details.
  14   *
  15   * You should have received a copy of the GNU General Public License along
  16   * with this program; if not, write to the Free Software Foundation, Inc.,
  17   * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  18   * http://www.gnu.org/copyleft/gpl.html
  19   *
  20   * @file
  21   */
  22  
  23  /**
  24   * Functions related to the output of file content
  25   */
  26  class StreamFile {
  27      const READY_STREAM = 1;
  28      const NOT_MODIFIED = 2;
  29  
  30      /**
  31       * Stream a file to the browser, adding all the headings and fun stuff.
  32       * Headers sent include: Content-type, Content-Length, Last-Modified,
  33       * and Content-Disposition.
  34       *
  35       * @param string $fname Full name and path of the file to stream
  36       * @param array $headers Any additional headers to send
  37       * @param bool $sendErrors Send error messages if errors occur (like 404)
  38       * @throws MWException
  39       * @return bool Success
  40       */
  41  	public static function stream( $fname, $headers = array(), $sendErrors = true ) {
  42          wfProfileIn( __METHOD__ );
  43  
  44          if ( FileBackend::isStoragePath( $fname ) ) { // sanity
  45              wfProfileOut( __METHOD__ );
  46              throw new MWException( __FUNCTION__ . " given storage path '$fname'." );
  47          }
  48  
  49          wfSuppressWarnings();
  50          $stat = stat( $fname );
  51          wfRestoreWarnings();
  52  
  53          $res = self::prepareForStream( $fname, $stat, $headers, $sendErrors );
  54          if ( $res == self::NOT_MODIFIED ) {
  55              $ok = true; // use client cache
  56          } elseif ( $res == self::READY_STREAM ) {
  57              wfProfileIn( __METHOD__ . '-send' );
  58              $ok = readfile( $fname );
  59              wfProfileOut( __METHOD__ . '-send' );
  60          } else {
  61              $ok = false; // failed
  62          }
  63  
  64          wfProfileOut( __METHOD__ );
  65          return $ok;
  66      }
  67  
  68      /**
  69       * Call this function used in preparation before streaming a file.
  70       * This function does the following:
  71       * (a) sends Last-Modified, Content-type, and Content-Disposition headers
  72       * (b) cancels any PHP output buffering and automatic gzipping of output
  73       * (c) sends Content-Length header based on HTTP_IF_MODIFIED_SINCE check
  74       *
  75       * @param string $path Storage path or file system path
  76       * @param array|bool $info File stat info with 'mtime' and 'size' fields
  77       * @param array $headers Additional headers to send
  78       * @param bool $sendErrors Send error messages if errors occur (like 404)
  79       * @return int|bool READY_STREAM, NOT_MODIFIED, or false on failure
  80       */
  81  	public static function prepareForStream(
  82          $path, $info, $headers = array(), $sendErrors = true
  83      ) {
  84          if ( !is_array( $info ) ) {
  85              if ( $sendErrors ) {
  86                  header( 'HTTP/1.0 404 Not Found' );
  87                  header( 'Cache-Control: no-cache' );
  88                  header( 'Content-Type: text/html; charset=utf-8' );
  89                  $encFile = htmlspecialchars( $path );
  90                  $encScript = htmlspecialchars( $_SERVER['SCRIPT_NAME'] );
  91                  echo "<html><body>
  92                      <h1>File not found</h1>
  93                      <p>Although this PHP script ($encScript) exists, the file requested for output
  94                      ($encFile) does not.</p>
  95                      </body></html>
  96                      ";
  97              }
  98              return false;
  99          }
 100  
 101          // Sent Last-Modified HTTP header for client-side caching
 102          header( 'Last-Modified: ' . wfTimestamp( TS_RFC2822, $info['mtime'] ) );
 103  
 104          // Cancel output buffering and gzipping if set
 105          wfResetOutputBuffers();
 106  
 107          $type = self::contentTypeFromPath( $path );
 108          if ( $type && $type != 'unknown/unknown' ) {
 109              header( "Content-type: $type" );
 110          } else {
 111              // Send a content type which is not known to Internet Explorer, to
 112              // avoid triggering IE's content type detection. Sending a standard
 113              // unknown content type here essentially gives IE license to apply
 114              // whatever content type it likes.
 115              header( 'Content-type: application/x-wiki' );
 116          }
 117  
 118          // Don't stream it out as text/html if there was a PHP error
 119          if ( headers_sent() ) {
 120              echo "Headers already sent, terminating.\n";
 121              return false;
 122          }
 123  
 124          // Send additional headers
 125          foreach ( $headers as $header ) {
 126              header( $header );
 127          }
 128  
 129          // Don't send if client has up to date cache
 130          if ( !empty( $_SERVER['HTTP_IF_MODIFIED_SINCE'] ) ) {
 131              $modsince = preg_replace( '/;.*$/', '', $_SERVER['HTTP_IF_MODIFIED_SINCE'] );
 132              if ( wfTimestamp( TS_UNIX, $info['mtime'] ) <= strtotime( $modsince ) ) {
 133                  ini_set( 'zlib.output_compression', 0 );
 134                  header( "HTTP/1.0 304 Not Modified" );
 135                  return self::NOT_MODIFIED; // ok
 136              }
 137          }
 138  
 139          header( 'Content-Length: ' . $info['size'] );
 140  
 141          return self::READY_STREAM; // ok
 142      }
 143  
 144      /**
 145       * Determine the file type of a file based on the path
 146       *
 147       * @param string $filename Storage path or file system path
 148       * @param bool $safe Whether to do retroactive upload blacklist checks
 149       * @return null|string
 150       */
 151  	public static function contentTypeFromPath( $filename, $safe = true ) {
 152          global $wgTrivialMimeDetection;
 153  
 154          $ext = strrchr( $filename, '.' );
 155          $ext = $ext === false ? '' : strtolower( substr( $ext, 1 ) );
 156  
 157          # trivial detection by file extension,
 158          # used for thumbnails (thumb.php)
 159          if ( $wgTrivialMimeDetection ) {
 160              switch ( $ext ) {
 161                  case 'gif': return 'image/gif';
 162                  case 'png': return 'image/png';
 163                  case 'jpg': return 'image/jpeg';
 164                  case 'jpeg': return 'image/jpeg';
 165              }
 166  
 167              return 'unknown/unknown';
 168          }
 169  
 170          $magic = MimeMagic::singleton();
 171          // Use the extension only, rather than magic numbers, to avoid opening
 172          // up vulnerabilities due to uploads of files with allowed extensions
 173          // but disallowed types.
 174          $type = $magic->guessTypesForExtension( $ext );
 175  
 176          /**
 177           * Double-check some security settings that were done on upload but might
 178           * have changed since.
 179           */
 180          if ( $safe ) {
 181              global $wgFileBlacklist, $wgCheckFileExtensions, $wgStrictFileExtensions,
 182                  $wgFileExtensions, $wgVerifyMimeType, $wgMimeTypeBlacklist;
 183              list( , $extList ) = UploadBase::splitExtensions( $filename );
 184              if ( UploadBase::checkFileExtensionList( $extList, $wgFileBlacklist ) ) {
 185                  return 'unknown/unknown';
 186              }
 187              if ( $wgCheckFileExtensions && $wgStrictFileExtensions
 188                  && !UploadBase::checkFileExtensionList( $extList, $wgFileExtensions ) )
 189              {
 190                  return 'unknown/unknown';
 191              }
 192              if ( $wgVerifyMimeType && in_array( strtolower( $type ), $wgMimeTypeBlacklist ) ) {
 193                  return 'unknown/unknown';
 194              }
 195          }
 196          return $type;
 197      }
 198  }

title

Description

title

Description

title

Description

title

title

Body