[ Index ]

PHP Cross Reference of MediaWiki-1.24.0

title

Body

[close]

/ -> img_auth.php (source)

   1  <?php
   2  /**
   3   * Image authorisation script
   4   *
   5   * To use this, see https://www.mediawiki.org/wiki/Manual:Image_Authorization
   6   *
   7   * - Set $wgUploadDirectory to a non-public directory (not web accessible)
   8   * - Set $wgUploadPath to point to this file
   9   *
  10   * Optional Parameters
  11   *
  12   * - Set $wgImgAuthDetails = true if you want the reason the access was denied messages to
  13   *       be displayed instead of just the 403 error (doesn't work on IE anyway),
  14   *       otherwise it will only appear in error logs
  15   *
  16   *  For security reasons, you usually don't want your user to know *why* access was denied,
  17   *  just that it was. If you want to change this, you can set $wgImgAuthDetails to 'true'
  18   *  in localsettings.php and it will give the user the reason why access was denied.
  19   *
  20   * Your server needs to support PATH_INFO; CGI-based configurations usually don't.
  21   *
  22   * This program is free software; you can redistribute it and/or modify
  23   * it under the terms of the GNU General Public License as published by
  24   * the Free Software Foundation; either version 2 of the License, or
  25   * (at your option) any later version.
  26   *
  27   * This program is distributed in the hope that it will be useful,
  28   * but WITHOUT ANY WARRANTY; without even the implied warranty of
  29   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  30   * GNU General Public License for more details.
  31   *
  32   * You should have received a copy of the GNU General Public License along
  33   * with this program; if not, write to the Free Software Foundation, Inc.,
  34   * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  35   * http://www.gnu.org/copyleft/gpl.html
  36   *
  37   * @file
  38   */
  39  
  40  define( 'MW_NO_OUTPUT_COMPRESSION', 1 );
  41  require  __DIR__ . '/includes/WebStart.php';
  42  wfProfileIn( 'img_auth.php' );
  43  
  44  # Set action base paths so that WebRequest::getPathInfo()
  45  # recognizes the "X" as the 'title' in ../img_auth.php/X urls.
  46  $wgArticlePath = false; # Don't let a "/*" article path clober our action path
  47  $wgActionPaths = array( "$wgUploadPath/" );
  48  
  49  wfImageAuthMain();
  50  wfProfileOut( 'img_auth.php' );
  51  wfLogProfilingData();
  52  // Commit and close up!
  53  $factory = wfGetLBFactory();
  54  $factory->commitMasterChanges();
  55  $factory->shutdown();
  56  
  57  function wfImageAuthMain() {
  58      global $wgImgAuthUrlPathMap;
  59  
  60      $request = RequestContext::getMain()->getRequest();
  61      $publicWiki = in_array( 'read', User::getGroupPermissions( array( '*' ) ), true );
  62  
  63      // Get the requested file path (source file or thumbnail)
  64      $matches = WebRequest::getPathInfo();
  65      if ( !isset( $matches['title'] ) ) {
  66          wfForbidden( 'img-auth-accessdenied', 'img-auth-nopathinfo' );
  67          return;
  68      }
  69      $path = $matches['title'];
  70      if ( $path && $path[0] !== '/' ) {
  71          // Make sure $path has a leading /
  72          $path = "/" . $path;
  73      }
  74  
  75      // Check for bug 28235: QUERY_STRING overriding the correct extension
  76      $whitelist = array();
  77      $extension = FileBackend::extensionFromPath( $path, 'rawcase' );
  78      if ( $extension != '' ) {
  79          $whitelist[] = $extension;
  80      }
  81      if ( !$request->checkUrlExtension( $whitelist ) ) {
  82          return;
  83      }
  84  
  85      // Various extensions may have their own backends that need access.
  86      // Check if there is a special backend and storage base path for this file.
  87      foreach ( $wgImgAuthUrlPathMap as $prefix => $storageDir ) {
  88          $prefix = rtrim( $prefix, '/' ) . '/'; // implicit trailing slash
  89          if ( strpos( $path, $prefix ) === 0 ) {
  90              $be = FileBackendGroup::singleton()->backendFromPath( $storageDir );
  91              $filename = $storageDir . substr( $path, strlen( $prefix ) ); // strip prefix
  92              // Check basic user authorization
  93              if ( !RequestContext::getMain()->getUser()->isAllowed( 'read' ) ) {
  94                  wfForbidden( 'img-auth-accessdenied', 'img-auth-noread', $path );
  95                  return;
  96              }
  97              if ( $be->fileExists( array( 'src' => $filename ) ) ) {
  98                  wfDebugLog( 'img_auth', "Streaming `" . $filename . "`." );
  99                  $be->streamFile( array( 'src' => $filename ),
 100                      array( 'Cache-Control: private', 'Vary: Cookie' ) );
 101              } else {
 102                  wfForbidden( 'img-auth-accessdenied', 'img-auth-nofile', $path );
 103              }
 104              return;
 105          }
 106      }
 107  
 108      // Get the local file repository
 109      $repo = RepoGroup::singleton()->getRepo( 'local' );
 110      $zone = strstr( ltrim( $path, '/' ), '/', true );
 111  
 112      // Get the full file storage path and extract the source file name.
 113      // (e.g. 120px-Foo.png => Foo.png or page2-120px-Foo.png => Foo.png).
 114      // This only applies to thumbnails/transcoded, and each of them should
 115      // be under a folder that has the source file name.
 116      if ( $zone === 'thumb' || $zone === 'transcoded' ) {
 117          $name = wfBaseName( dirname( $path ) );
 118          $filename = $repo->getZonePath( $zone ) . substr( $path, strlen( "/" . $zone ) );
 119          // Check to see if the file exists
 120          if ( !$repo->fileExists( $filename ) ) {
 121              wfForbidden( 'img-auth-accessdenied', 'img-auth-nofile', $filename );
 122              return;
 123          }
 124      } else {
 125          $name = wfBaseName( $path ); // file is a source file
 126          $filename = $repo->getZonePath( 'public' ) . $path;
 127          // Check to see if the file exists and is not deleted
 128          $bits = explode( '!', $name, 2 );
 129          if ( substr( $path, 0, 9 ) === '/archive/' && count( $bits ) == 2 ) {
 130              $file = $repo->newFromArchiveName( $bits[1], $name );
 131          } else {
 132              $file = $repo->newFile( $name );
 133          }
 134          if ( !$file->exists() || $file->isDeleted( File::DELETED_FILE ) ) {
 135              wfForbidden( 'img-auth-accessdenied', 'img-auth-nofile', $filename );
 136              return;
 137          }
 138      }
 139  
 140      $headers = array(); // extra HTTP headers to send
 141  
 142      if ( !$publicWiki ) {
 143          // For private wikis, run extra auth checks and set cache control headers
 144          $headers[] = 'Cache-Control: private';
 145          $headers[] = 'Vary: Cookie';
 146  
 147          $title = Title::makeTitleSafe( NS_FILE, $name );
 148          if ( !$title instanceof Title ) { // files have valid titles
 149              wfForbidden( 'img-auth-accessdenied', 'img-auth-badtitle', $name );
 150              return;
 151          }
 152  
 153          // Run hook for extension authorization plugins
 154          /** @var $result array */
 155          $result = null;
 156          if ( !wfRunHooks( 'ImgAuthBeforeStream', array( &$title, &$path, &$name, &$result ) ) ) {
 157              wfForbidden( $result[0], $result[1], array_slice( $result, 2 ) );
 158              return;
 159          }
 160  
 161          // Check user authorization for this title
 162          // Checks Whitelist too
 163          if ( !$title->userCan( 'read' ) ) {
 164              wfForbidden( 'img-auth-accessdenied', 'img-auth-noread', $name );
 165              return;
 166          }
 167      }
 168  
 169      if ( $request->getCheck( 'download' ) ) {
 170          $headers[] = 'Content-Disposition: attachment';
 171      }
 172  
 173      // Stream the requested file
 174      wfDebugLog( 'img_auth', "Streaming `" . $filename . "`." );
 175      $repo->streamFile( $filename, $headers );
 176  }
 177  
 178  /**
 179   * Issue a standard HTTP 403 Forbidden header ($msg1-a message index, not a message) and an
 180   * error message ($msg2, also a message index), (both required) then end the script
 181   * subsequent arguments to $msg2 will be passed as parameters only for replacing in $msg2
 182   * @param string $msg1
 183   * @param string $msg2
 184   */
 185  function wfForbidden( $msg1, $msg2 ) {
 186      global $wgImgAuthDetails;
 187  
 188      $args = func_get_args();
 189      array_shift( $args );
 190      array_shift( $args );
 191      $args = ( isset( $args[0] ) && is_array( $args[0] ) ) ? $args[0] : $args;
 192  
 193      $msgHdr = wfMessage( $msg1 )->escaped();
 194      $detailMsgKey = $wgImgAuthDetails ? $msg2 : 'badaccess-group0';
 195      $detailMsg = wfMessage( $detailMsgKey, $args )->escaped();
 196  
 197      wfDebugLog( 'img_auth',
 198          "wfForbidden Hdr: " . wfMessage( $msg1 )->inLanguage( 'en' )->text() . " Msg: " .
 199              wfMessage( $msg2, $args )->inLanguage( 'en' )->text()
 200      );
 201  
 202      header( 'HTTP/1.0 403 Forbidden' );
 203      header( 'Cache-Control: no-cache' );
 204      header( 'Content-Type: text/html; charset=utf-8' );
 205      echo <<<ENDS
 206  <html>
 207  <body>
 208  <h1>$msgHdr</h1>
 209  <p>$detailMsg</p>
 210  </body>
 211  </html>
 212  ENDS;
 213  }


Generated: Fri Nov 28 14:03:12 2014 Cross-referenced by PHPXref 0.7.1