MediaWiki  REL1_19
thumb.php
Go to the documentation of this file.
00001 <?php
00002 
00009 define( 'MW_NO_OUTPUT_COMPRESSION', 1 );
00010 if ( isset( $_SERVER['MW_COMPILED'] ) ) {
00011         require( 'phase3/includes/WebStart.php' );
00012 } else {
00013         require( dirname( __FILE__ ) . '/includes/WebStart.php' );
00014 }
00015 
00016 // Don't use fancy mime detection, just check the file extension for jpg/gif/png
00017 $wgTrivialMimeDetection = true;
00018 
00019 if ( defined( 'THUMB_HANDLER' ) ) {
00020         // Called from thumb_handler.php via 404; extract params from the URI...
00021         wfThumbHandle404();
00022 } else {
00023         // Called directly, use $_REQUEST params
00024         wfThumbHandleRequest();
00025 }
00026 wfLogProfilingData();
00027 
00028 //--------------------------------------------------------------------------
00029 
00035 function wfThumbHandleRequest() {
00036         $params = get_magic_quotes_gpc()
00037                 ? array_map( 'stripslashes', $_REQUEST )
00038                 : $_REQUEST;
00039 
00040         wfStreamThumb( $params ); // stream the thumbnail
00041 }
00042 
00048 function wfThumbHandle404() {
00049         # lighttpd puts the original request in REQUEST_URI, while sjs sets
00050         # that to the 404 handler, and puts the original request in REDIRECT_URL.
00051         if ( isset( $_SERVER['REDIRECT_URL'] ) ) {
00052                 # The URL is un-encoded, so put it back how it was
00053                 $uri = str_replace( "%2F", "/", urlencode( $_SERVER['REDIRECT_URL'] ) );
00054                 # Just get the URI path (REDIRECT_URL is either a full URL or a path)
00055                 if ( $uri[0] !== '/' ) {
00056                         $bits = wfParseUrl( $uri );
00057                         if ( $bits && isset( $bits['path'] ) ) {
00058                                 $uri = $bits['path'];
00059                         }
00060                 }
00061         } else {
00062                 $uri = $_SERVER['REQUEST_URI'];
00063         }
00064 
00065         $params = wfExtractThumbParams( $uri ); // basic wiki URL param extracting
00066         if ( $params == null ) {
00067                 wfThumbError( 404, 'The source file for the specified thumbnail does not exist.' );
00068                 return;
00069         }
00070 
00071         wfStreamThumb( $params ); // stream the thumbnail
00072 }
00073 
00080 function wfStreamThumb( array $params ) {
00081         wfProfileIn( __METHOD__ );
00082 
00083         $headers = array(); // HTTP headers to send
00084 
00085         $fileName = isset( $params['f'] ) ? $params['f'] : '';
00086         unset( $params['f'] );
00087 
00088         // Backwards compatibility parameters
00089         if ( isset( $params['w'] ) ) {
00090                 $params['width'] = $params['w'];
00091                 unset( $params['w'] );
00092         }
00093         if ( isset( $params['p'] ) ) {
00094                 $params['page'] = $params['p'];
00095         }
00096         unset( $params['r'] ); // ignore 'r' because we unconditionally pass File::RENDER
00097 
00098         // Is this a thumb of an archived file?
00099         $isOld = ( isset( $params['archived'] ) && $params['archived'] );
00100         unset( $params['archived'] );
00101 
00102         // Some basic input validation
00103         $fileName = strtr( $fileName, '\\/', '__' );
00104 
00105         // Actually fetch the image. Method depends on whether it is archived or not.
00106         if ( $isOld ) {
00107                 // Format is <timestamp>!<name>
00108                 $bits = explode( '!', $fileName, 2 );
00109                 if ( count( $bits ) != 2 ) {
00110                         wfThumbError( 404, wfMsg( 'badtitletext' ) );
00111                         wfProfileOut( __METHOD__ );
00112                         return;
00113                 }
00114                 $title = Title::makeTitleSafe( NS_FILE, $bits[1] );
00115                 if ( !$title ) {
00116                         wfThumbError( 404, wfMsg( 'badtitletext' ) );
00117                         wfProfileOut( __METHOD__ );
00118                         return;
00119                 }
00120                 $img = RepoGroup::singleton()->getLocalRepo()->newFromArchiveName( $title, $fileName );
00121         } else {
00122                 $img = wfLocalFile( $fileName );
00123         }
00124 
00125         // Check permissions if there are read restrictions
00126         if ( !in_array( 'read', User::getGroupPermissions( array( '*' ) ), true ) ) {
00127                 if ( !$img->getTitle()->userCan( 'read' ) ) {
00128                         wfThumbError( 403, 'Access denied. You do not have permission to access ' .
00129                                 'the source file.' );
00130                         wfProfileOut( __METHOD__ );
00131                         return;
00132                 }
00133                 $headers[] = 'Cache-Control: private';
00134                 $headers[] = 'Vary: Cookie';
00135         }
00136 
00137         // Check the source file storage path
00138         if ( !$img ) {
00139                 wfThumbError( 404, wfMsg( 'badtitletext' ) );
00140                 wfProfileOut( __METHOD__ );
00141                 return;
00142         }
00143         if ( !$img->exists() ) {
00144                 wfThumbError( 404, 'The source file for the specified thumbnail does not exist.' );
00145                 wfProfileOut( __METHOD__ );
00146                 return;
00147         }
00148         $sourcePath = $img->getPath();
00149         if ( $sourcePath === false ) {
00150                 wfThumbError( 500, 'The source file is not locally accessible.' );
00151                 wfProfileOut( __METHOD__ );
00152                 return;
00153         }
00154 
00155         // Check IMS against the source file
00156         // This means that clients can keep a cached copy even after it has been deleted on the server
00157         if ( !empty( $_SERVER['HTTP_IF_MODIFIED_SINCE'] ) ) {
00158                 // Fix IE brokenness
00159                 $imsString = preg_replace( '/;.*$/', '', $_SERVER["HTTP_IF_MODIFIED_SINCE"] );
00160                 // Calculate time
00161                 wfSuppressWarnings();
00162                 $imsUnix = strtotime( $imsString );
00163                 wfRestoreWarnings();
00164                 $sourceTsUnix = wfTimestamp( TS_UNIX, $img->getTimestamp() );
00165                 if ( $sourceTsUnix <= $imsUnix ) {
00166                         header( 'HTTP/1.1 304 Not Modified' );
00167                         wfProfileOut( __METHOD__ );
00168                         return;
00169                 }
00170         }
00171 
00172         // Stream the file if it exists already...
00173         try {
00174                 $thumbName = $img->thumbName( $params );
00175                 if ( strlen( $thumbName ) ) { // valid params?
00176                         // For 404 handled thumbnails, we only use the the base name of the URI
00177                         // for the thumb params and the parent directory for the source file name.
00178                         // Check that the zone relative path matches up so squid caches won't pick
00179                         // up thumbs that would not be purged on source file deletion (bug 34231).
00180                         if ( isset( $params['rel404'] ) // thumbnail was handled via 404
00181                                 && urldecode( $params['rel404'] ) !== $img->getThumbRel( $thumbName ) ) 
00182                         {
00183                                 wfThumbError( 404, 'The source file for the specified thumbnail does not exist.' );
00184                                 wfProfileOut( __METHOD__ );
00185                                 return;
00186                         }
00187                         $thumbPath = $img->getThumbPath( $thumbName );
00188                         if ( $img->getRepo()->fileExists( $thumbPath ) ) {
00189                                 $img->getRepo()->streamFile( $thumbPath, $headers );
00190                                 wfProfileOut( __METHOD__ );
00191                                 return;
00192                         }
00193                 }
00194         } catch ( MWException $e ) {
00195                 wfThumbError( 500, $e->getHTML() );
00196                 wfProfileOut( __METHOD__ );
00197                 return;
00198         }
00199 
00200         // Thumbnail isn't already there, so create the new thumbnail...
00201         try {
00202                 $thumb = $img->transform( $params, File::RENDER_NOW );
00203         } catch ( Exception $ex ) {
00204                 // Tried to select a page on a non-paged file?
00205                 $thumb = false;
00206         }
00207 
00208         // Check for thumbnail generation errors...
00209         $errorMsg = false;
00210         if ( !$thumb ) {
00211                 $errorMsg = wfMsgHtml( 'thumbnail_error', 'File::transform() returned false' );
00212         } elseif ( $thumb->isError() ) {
00213                 $errorMsg = $thumb->getHtmlMsg();
00214         } elseif ( !$thumb->hasFile() ) {
00215                 $errorMsg = wfMsgHtml( 'thumbnail_error', 'No path supplied in thumbnail object' );
00216         } elseif ( $thumb->fileIsSource() ) {
00217                 $errorMsg = wfMsgHtml( 'thumbnail_error',
00218                         'Image was not scaled, is the requested width bigger than the source?' );
00219         }
00220 
00221         if ( $errorMsg !== false ) {
00222                 wfThumbError( 500, $errorMsg );
00223         } else {
00224                 // Stream the file if there were no errors
00225                 $thumb->streamFile( $headers );
00226         }
00227 
00228         wfProfileOut( __METHOD__ );
00229 }
00230 
00238 function wfExtractThumbParams( $uri ) {
00239         $repo = RepoGroup::singleton()->getLocalRepo();
00240 
00241         $zoneURI = $repo->getZoneUrl( 'thumb' );
00242         if ( substr( $zoneURI, 0, 1 ) !== '/' ) {
00243                 $bits = wfParseUrl( $zoneURI );
00244                 if ( $bits && isset( $bits['path'] ) ) {
00245                         $zoneURI = $bits['path'];
00246                 } else {
00247                         return null;
00248                 }
00249         }
00250         $zoneUrlRegex = preg_quote( $zoneURI );
00251 
00252         $hashDirRegex = $subdirRegex = '';
00253         for ( $i = 0; $i < $repo->getHashLevels(); $i++ ) {
00254                 $subdirRegex .= '[0-9a-f]';
00255                 $hashDirRegex .= "$subdirRegex/";
00256         }
00257 
00258         $thumbUrlRegex = "!^$zoneUrlRegex/((archive/|temp/)?$hashDirRegex([^/]*)/([^/]*))$!";
00259 
00260         // Check if this is a valid looking thumbnail request...
00261         if ( preg_match( $thumbUrlRegex, $uri, $matches ) ) {
00262                 list( /* all */, $rel, $archOrTemp, $filename, $thumbname ) = $matches;
00263                 $filename = urldecode( $filename );
00264                 $thumbname = urldecode( $thumbname );
00265 
00266                 $params = array( 'f' => $filename, 'rel404' => $rel );
00267                 if ( $archOrTemp == 'archive/' ) {
00268                         $params['archived'] = 1;
00269                 } elseif ( $archOrTemp == 'temp/' ) {
00270                         $params['temp'] = 1;
00271                 }
00272 
00273                 // Check if the parameters can be extracted from the thumbnail name...
00274                 if ( preg_match( '!^(page(\d*)-)*(\d*)px-[^/]*$!', $thumbname, $matches ) ) {
00275                         list( /* all */, $pagefull, $pagenum, $size ) = $matches;
00276                         $params['width'] = $size;
00277                         if ( $pagenum ) {
00278                                 $params['page'] = $pagenum;
00279                         }
00280                         return $params; // valid thumbnail URL
00281                 // Hooks return false if they manage to *resolve* the parameters
00282                 } elseif ( !wfRunHooks( 'ExtractThumbParameters', array( $thumbname, &$params ) ) ) {
00283                         return $params; // valid thumbnail URL (via extension or config)
00284                 }
00285         }
00286 
00287         return null; // not a valid thumbnail URL
00288 }
00289 
00297 function wfThumbError( $status, $msg ) {
00298         global $wgShowHostnames;
00299 
00300         header( 'Cache-Control: no-cache' );
00301         header( 'Content-Type: text/html; charset=utf-8' );
00302         if ( $status == 404 ) {
00303                 header( 'HTTP/1.1 404 Not found' );
00304         } elseif ( $status == 403 ) {
00305                 header( 'HTTP/1.1 403 Forbidden' );
00306                 header( 'Vary: Cookie' );
00307         } else {
00308                 header( 'HTTP/1.1 500 Internal server error' );
00309         }
00310         if ( $wgShowHostnames ) {
00311                 $url = htmlspecialchars( isset( $_SERVER['REQUEST_URI'] ) ? $_SERVER['REQUEST_URI'] : '' );
00312                 $hostname = htmlspecialchars( wfHostname() );
00313                 $debug = "<!-- $url -->\n<!-- $hostname -->\n";
00314         } else {
00315                 $debug = "";
00316         }
00317         echo <<<EOT
00318 <html><head><title>Error generating thumbnail</title></head>
00319 <body>
00320 <h1>Error generating thumbnail</h1>
00321 <p>
00322 $msg
00323 </p>
00324 $debug
00325 </body>
00326 </html>
00327 
00328 EOT;
00329 }