MediaWiki
REL1_20
|
00001 <?php 00024 define( 'MW_NO_OUTPUT_COMPRESSION', 1 ); 00025 if ( isset( $_SERVER['MW_COMPILED'] ) ) { 00026 require( 'core/includes/WebStart.php' ); 00027 } else { 00028 require( __DIR__ . '/includes/WebStart.php' ); 00029 } 00030 00031 // Don't use fancy mime detection, just check the file extension for jpg/gif/png 00032 $wgTrivialMimeDetection = true; 00033 00034 if ( defined( 'THUMB_HANDLER' ) ) { 00035 // Called from thumb_handler.php via 404; extract params from the URI... 00036 wfThumbHandle404(); 00037 } else { 00038 // Called directly, use $_REQUEST params 00039 wfThumbHandleRequest(); 00040 } 00041 wfLogProfilingData(); 00042 00043 //-------------------------------------------------------------------------- 00044 00050 function wfThumbHandleRequest() { 00051 $params = get_magic_quotes_gpc() 00052 ? array_map( 'stripslashes', $_REQUEST ) 00053 : $_REQUEST; 00054 00055 wfStreamThumb( $params ); // stream the thumbnail 00056 } 00057 00063 function wfThumbHandle404() { 00064 # lighttpd puts the original request in REQUEST_URI, while sjs sets 00065 # that to the 404 handler, and puts the original request in REDIRECT_URL. 00066 if ( isset( $_SERVER['REDIRECT_URL'] ) ) { 00067 # The URL is un-encoded, so put it back how it was 00068 $uriPath = str_replace( "%2F", "/", urlencode( $_SERVER['REDIRECT_URL'] ) ); 00069 } else { 00070 $uriPath = $_SERVER['REQUEST_URI']; 00071 } 00072 # Just get the URI path (REDIRECT_URL/REQUEST_URI is either a full URL or a path) 00073 if ( substr( $uriPath, 0, 1 ) !== '/' ) { 00074 $bits = wfParseUrl( $uriPath ); 00075 if ( $bits && isset( $bits['path'] ) ) { 00076 $uriPath = $bits['path']; 00077 } else { 00078 wfThumbError( 404, 'The source file for the specified thumbnail does not exist.' ); 00079 return; 00080 } 00081 } 00082 00083 $params = wfExtractThumbParams( $uriPath ); // basic wiki URL param extracting 00084 if ( $params == null ) { 00085 wfThumbError( 404, 'The source file for the specified thumbnail does not exist.' ); 00086 return; 00087 } 00088 00089 wfStreamThumb( $params ); // stream the thumbnail 00090 } 00091 00098 function wfStreamThumb( array $params ) { 00099 global $wgVaryOnXFP; 00100 wfProfileIn( __METHOD__ ); 00101 00102 $headers = array(); // HTTP headers to send 00103 00104 $fileName = isset( $params['f'] ) ? $params['f'] : ''; 00105 unset( $params['f'] ); 00106 00107 // Backwards compatibility parameters 00108 if ( isset( $params['w'] ) ) { 00109 $params['width'] = $params['w']; 00110 unset( $params['w'] ); 00111 } 00112 if ( isset( $params['p'] ) ) { 00113 $params['page'] = $params['p']; 00114 } 00115 unset( $params['r'] ); // ignore 'r' because we unconditionally pass File::RENDER 00116 00117 // Is this a thumb of an archived file? 00118 $isOld = ( isset( $params['archived'] ) && $params['archived'] ); 00119 unset( $params['archived'] ); // handlers don't care 00120 00121 // Is this a thumb of a temp file? 00122 $isTemp = ( isset( $params['temp'] ) && $params['temp'] ); 00123 unset( $params['temp'] ); // handlers don't care 00124 00125 // Some basic input validation 00126 $fileName = strtr( $fileName, '\\/', '__' ); 00127 00128 // Actually fetch the image. Method depends on whether it is archived or not. 00129 if ( $isTemp ) { 00130 $repo = RepoGroup::singleton()->getLocalRepo()->getTempRepo(); 00131 $img = new UnregisteredLocalFile( null, $repo, 00132 # Temp files are hashed based on the name without the timestamp. 00133 # The thumbnails will be hashed based on the entire name however. 00134 # @TODO: fix this convention to actually be reasonable. 00135 $repo->getZonePath( 'public' ) . '/' . $repo->getTempHashPath( $fileName ) . $fileName 00136 ); 00137 } elseif ( $isOld ) { 00138 // Format is <timestamp>!<name> 00139 $bits = explode( '!', $fileName, 2 ); 00140 if ( count( $bits ) != 2 ) { 00141 wfThumbError( 404, wfMessage( 'badtitletext' )->text() ); 00142 wfProfileOut( __METHOD__ ); 00143 return; 00144 } 00145 $title = Title::makeTitleSafe( NS_FILE, $bits[1] ); 00146 if ( !$title ) { 00147 wfThumbError( 404, wfMessage( 'badtitletext' )->text() ); 00148 wfProfileOut( __METHOD__ ); 00149 return; 00150 } 00151 $img = RepoGroup::singleton()->getLocalRepo()->newFromArchiveName( $title, $fileName ); 00152 } else { 00153 $img = wfLocalFile( $fileName ); 00154 } 00155 00156 // Check permissions if there are read restrictions 00157 $varyHeader = array(); 00158 if ( !in_array( 'read', User::getGroupPermissions( array( '*' ) ), true ) ) { 00159 if ( !$img->getTitle() || !$img->getTitle()->userCan( 'read' ) ) { 00160 wfThumbError( 403, 'Access denied. You do not have permission to access ' . 00161 'the source file.' ); 00162 wfProfileOut( __METHOD__ ); 00163 return; 00164 } 00165 $headers[] = 'Cache-Control: private'; 00166 $varyHeader[] = 'Cookie'; 00167 } 00168 00169 // Check the source file storage path 00170 if ( !$img ) { 00171 wfThumbError( 404, wfMessage( 'badtitletext' )->text() ); 00172 wfProfileOut( __METHOD__ ); 00173 return; 00174 } 00175 if ( !$img->exists() ) { 00176 wfThumbError( 404, 'The source file for the specified thumbnail does not exist.' ); 00177 wfProfileOut( __METHOD__ ); 00178 return; 00179 } 00180 $sourcePath = $img->getPath(); 00181 if ( $sourcePath === false ) { 00182 wfThumbError( 500, 'The source file is not locally accessible.' ); 00183 wfProfileOut( __METHOD__ ); 00184 return; 00185 } 00186 00187 // Check IMS against the source file 00188 // This means that clients can keep a cached copy even after it has been deleted on the server 00189 if ( !empty( $_SERVER['HTTP_IF_MODIFIED_SINCE'] ) ) { 00190 // Fix IE brokenness 00191 $imsString = preg_replace( '/;.*$/', '', $_SERVER["HTTP_IF_MODIFIED_SINCE"] ); 00192 // Calculate time 00193 wfSuppressWarnings(); 00194 $imsUnix = strtotime( $imsString ); 00195 wfRestoreWarnings(); 00196 $sourceTsUnix = wfTimestamp( TS_UNIX, $img->getTimestamp() ); 00197 if ( $sourceTsUnix <= $imsUnix ) { 00198 header( 'HTTP/1.1 304 Not Modified' ); 00199 wfProfileOut( __METHOD__ ); 00200 return; 00201 } 00202 } 00203 00204 $thumbName = $img->thumbName( $params ); 00205 if ( !strlen( $thumbName ) ) { // invalid params? 00206 wfThumbError( 400, 'The specified thumbnail parameters are not valid.' ); 00207 wfProfileOut( __METHOD__ ); 00208 return; 00209 } 00210 00211 $disposition = $img->getThumbDisposition( $thumbName ); 00212 $headers[] = "Content-Disposition: $disposition"; 00213 00214 // Stream the file if it exists already... 00215 try { 00216 $thumbName2 = $img->thumbName( $params, File::THUMB_FULL_NAME ); // b/c; "long" style 00217 // For 404 handled thumbnails, we only use the the base name of the URI 00218 // for the thumb params and the parent directory for the source file name. 00219 // Check that the zone relative path matches up so squid caches won't pick 00220 // up thumbs that would not be purged on source file deletion (bug 34231). 00221 if ( isset( $params['rel404'] ) ) { // thumbnail was handled via 404 00222 if ( urldecode( $params['rel404'] ) === $img->getThumbRel( $thumbName ) ) { 00223 // Request for the canonical thumbnail name 00224 } elseif ( urldecode( $params['rel404'] ) === $img->getThumbRel( $thumbName2 ) ) { 00225 // Request for the "long" thumbnail name; redirect to canonical name 00226 $response = RequestContext::getMain()->getRequest()->response(); 00227 $response->header( "HTTP/1.1 301 " . HttpStatus::getMessage( 301 ) ); 00228 $response->header( 'Location: ' . wfExpandUrl( $img->getThumbUrl( $thumbName ), PROTO_CURRENT ) ); 00229 $response->header( 'Expires: ' . 00230 gmdate( 'D, d M Y H:i:s', time() + 7*86400 ) . ' GMT' ); 00231 if ( $wgVaryOnXFP ) { 00232 $varyHeader[] = 'X-Forwarded-Proto'; 00233 } 00234 $response->header( 'Vary: ' . implode( ', ', $varyHeader ) ); 00235 wfProfileOut( __METHOD__ ); 00236 return; 00237 } else { 00238 wfThumbError( 404, 'The given path of the specified thumbnail is incorrect.' ); 00239 wfProfileOut( __METHOD__ ); 00240 return; 00241 } 00242 } 00243 $thumbPath = $img->getThumbPath( $thumbName ); 00244 if ( $img->getRepo()->fileExists( $thumbPath ) ) { 00245 $headers[] = 'Vary: ' . implode( ', ', $varyHeader ); 00246 $img->getRepo()->streamFile( $thumbPath, $headers ); 00247 wfProfileOut( __METHOD__ ); 00248 return; 00249 } 00250 } catch ( MWException $e ) { 00251 wfThumbError( 500, $e->getHTML() ); 00252 wfProfileOut( __METHOD__ ); 00253 return; 00254 } 00255 $headers[] = 'Vary: ' . implode( ', ', $varyHeader ); 00256 00257 // Thumbnail isn't already there, so create the new thumbnail... 00258 try { 00259 $thumb = $img->transform( $params, File::RENDER_NOW ); 00260 } catch ( Exception $ex ) { 00261 // Tried to select a page on a non-paged file? 00262 $thumb = false; 00263 } 00264 00265 // Check for thumbnail generation errors... 00266 $errorMsg = false; 00267 $msg = wfMessage( 'thumbnail_error' ); 00268 if ( !$thumb ) { 00269 $errorMsg = $msg->rawParams( 'File::transform() returned false' )->escaped(); 00270 } elseif ( $thumb->isError() ) { 00271 $errorMsg = $thumb->getHtmlMsg(); 00272 } elseif ( !$thumb->hasFile() ) { 00273 $errorMsg = $msg->rawParams( 'No path supplied in thumbnail object' )->escaped(); 00274 } elseif ( $thumb->fileIsSource() ) { 00275 $errorMsg = $msg-> 00276 rawParams( 'Image was not scaled, is the requested width bigger than the source?' )->escaped(); 00277 } 00278 00279 if ( $errorMsg !== false ) { 00280 wfThumbError( 500, $errorMsg ); 00281 } else { 00282 // Stream the file if there were no errors 00283 $thumb->streamFile( $headers ); 00284 } 00285 00286 wfProfileOut( __METHOD__ ); 00287 } 00288 00296 function wfExtractThumbParams( $uriPath ) { 00297 $repo = RepoGroup::singleton()->getLocalRepo(); 00298 00299 // Zone URL might be relative ("/images") or protocol-relative ("//lang.site/image") 00300 $zoneUriPath = $repo->getZoneHandlerUrl( 'thumb' ) 00301 ? $repo->getZoneHandlerUrl( 'thumb' ) // custom URL 00302 : $repo->getZoneUrl( 'thumb' ); // default to main URL 00303 $bits = wfParseUrl( wfExpandUrl( $zoneUriPath, PROTO_INTERNAL ) ); 00304 if ( $bits && isset( $bits['path'] ) ) { 00305 $zoneUriPath = $bits['path']; 00306 } else { 00307 return null; // not a valid thumbnail URL 00308 } 00309 00310 $hashDirReg = $subdirReg = ''; 00311 for ( $i = 0; $i < $repo->getHashLevels(); $i++ ) { 00312 $subdirReg .= '[0-9a-f]'; 00313 $hashDirReg .= "$subdirReg/"; 00314 } 00315 $zoneReg = preg_quote( $zoneUriPath ); // regex for thumb zone URI 00316 00317 // Check if this is a thumbnail of an original in the local file repo 00318 if ( preg_match( "!^$zoneReg/((archive/)?$hashDirReg([^/]*)/([^/]*))$!", $uriPath, $m ) ) { 00319 list( /*all*/, $rel, $archOrTemp, $filename, $thumbname ) = $m; 00320 // Check if this is a thumbnail of an temp file in the local file repo 00321 } elseif ( preg_match( "!^$zoneReg/(temp/)($hashDirReg([^/]*)/([^/]*))$!", $uriPath, $m ) ) { 00322 list( /*all*/, $archOrTemp, $rel, $filename, $thumbname ) = $m; 00323 } else { 00324 return null; // not a valid looking thumbnail request 00325 } 00326 00327 $filename = urldecode( $filename ); 00328 $thumbname = urldecode( $thumbname ); 00329 00330 $params = array( 'f' => $filename, 'rel404' => $rel ); 00331 if ( $archOrTemp === 'archive/' ) { 00332 $params['archived'] = 1; 00333 } elseif ( $archOrTemp === 'temp/' ) { 00334 $params['temp'] = 1; 00335 } 00336 00337 // Check if the parameters can be extracted from the thumbnail name... 00338 if ( preg_match( '!^(page(\d*)-)*(\d*)px-[^/]*$!', $thumbname, $matches ) ) { 00339 list( /* all */, $pagefull, $pagenum, $size ) = $matches; 00340 $params['width'] = $size; 00341 if ( $pagenum ) { 00342 $params['page'] = $pagenum; 00343 } 00344 return $params; // valid thumbnail URL 00345 // Hooks return false if they manage to *resolve* the parameters 00346 } elseif ( !wfRunHooks( 'ExtractThumbParameters', array( $thumbname, &$params ) ) ) { 00347 return $params; // valid thumbnail URL (via extension or config) 00348 } 00349 00350 return null; // not a valid thumbnail URL 00351 } 00352 00360 function wfThumbError( $status, $msg ) { 00361 global $wgShowHostnames; 00362 00363 header( 'Cache-Control: no-cache' ); 00364 header( 'Content-Type: text/html; charset=utf-8' ); 00365 if ( $status == 404 ) { 00366 header( 'HTTP/1.1 404 Not found' ); 00367 } elseif ( $status == 403 ) { 00368 header( 'HTTP/1.1 403 Forbidden' ); 00369 header( 'Vary: Cookie' ); 00370 } else { 00371 header( 'HTTP/1.1 500 Internal server error' ); 00372 } 00373 if ( $wgShowHostnames ) { 00374 $url = htmlspecialchars( isset( $_SERVER['REQUEST_URI'] ) ? $_SERVER['REQUEST_URI'] : '' ); 00375 $hostname = htmlspecialchars( wfHostname() ); 00376 $debug = "<!-- $url -->\n<!-- $hostname -->\n"; 00377 } else { 00378 $debug = ""; 00379 } 00380 echo <<<EOT 00381 <html><head><title>Error generating thumbnail</title></head> 00382 <body> 00383 <h1>Error generating thumbnail</h1> 00384 <p> 00385 $msg 00386 </p> 00387 $debug 00388 </body> 00389 </html> 00390 00391 EOT; 00392 }