diff options
author | Pierre Schmitz <pierre@archlinux.de> | 2014-12-27 15:41:37 +0100 |
---|---|---|
committer | Pierre Schmitz <pierre@archlinux.de> | 2014-12-31 11:43:28 +0100 |
commit | c1f9b1f7b1b77776192048005dcc66dcf3df2bfb (patch) | |
tree | 2b38796e738dd74cb42ecd9bfd151803108386bc /thumb.php | |
parent | b88ab0086858470dd1f644e64cb4e4f62bb2be9b (diff) |
Update to MediaWiki 1.24.1
Diffstat (limited to 'thumb.php')
-rw-r--r-- | thumb.php | 229 |
1 files changed, 194 insertions, 35 deletions
@@ -24,7 +24,7 @@ define( 'MW_NO_OUTPUT_COMPRESSION', 1 ); require __DIR__ . '/includes/WebStart.php'; -// Don't use fancy mime detection, just check the file extension for jpg/gif/png +// Don't use fancy MIME detection, just check the file extension for jpg/gif/png $wgTrivialMimeDetection = true; if ( defined( 'THUMB_HANDLER' ) ) { @@ -36,6 +36,10 @@ if ( defined( 'THUMB_HANDLER' ) ) { } wfLogProfilingData(); +// Commit and close up! +$factory = wfGetLBFactory(); +$factory->commitMasterChanges(); +$factory->shutdown(); //-------------------------------------------------------------------------- @@ -88,7 +92,7 @@ function wfThumbHandle404() { /** * Stream a thumbnail specified by parameters * - * @param $params Array List of thumbnailing parameters. In addition to parameters + * @param array $params List of thumbnailing parameters. In addition to parameters * passed to the MediaHandler, this may also includes the keys: * f (for filename), archived (if archived file), temp (if temp file), * w (alias for width), p (alias for page), r (ignored; historical), @@ -107,6 +111,19 @@ function wfStreamThumb( array $params ) { $fileName = isset( $params['f'] ) ? $params['f'] : ''; + // Backwards compatibility parameters + if ( isset( $params['w'] ) ) { + $params['width'] = $params['w']; + unset( $params['w'] ); + } + if ( isset( $params['width'] ) && substr( $params['width'], -2 ) == 'px' ) { + // strip the px (pixel) suffix, if found + $params['width'] = substr( $params['width'], 0, -2 ); + } + if ( isset( $params['p'] ) ) { + $params['page'] = $params['p']; + } + // Is this a thumb of an archived file? $isOld = ( isset( $params['archived'] ) && $params['archived'] ); unset( $params['archived'] ); // handlers don't care @@ -162,6 +179,12 @@ function wfStreamThumb( array $params ) { $varyHeader[] = 'Cookie'; } + // Check if the file is hidden + if ( $img->isDeleted( File::DELETED_FILE ) ) { + wfThumbError( 404, "The source file '$fileName' does not exist." ); + return; + } + // Do rendering parameters extraction from thumbnail name. if ( isset( $params['thumbName'] ) ) { $params = wfExtractThumbParams( $img, $params ); @@ -171,7 +194,6 @@ function wfStreamThumb( array $params ) { return; } - // Check the source file storage path if ( !$img->exists() ) { $redirectedLocation = false; @@ -235,17 +257,10 @@ function wfStreamThumb( array $params ) { } } - // Backwards compatibility parameters - if ( isset( $params['w'] ) ) { - $params['width'] = $params['w']; - unset( $params['w'] ); - } - if ( isset( $params['p'] ) ) { - $params['page'] = $params['p']; - } + $rel404 = isset( $params['rel404'] ) ? $params['rel404'] : null; unset( $params['r'] ); // ignore 'r' because we unconditionally pass File::RENDER unset( $params['f'] ); // We're done with 'f' parameter. - + unset( $params['rel404'] ); // moved to $rel404 // Get the normalized thumbnail name from the parameters... try { @@ -264,10 +279,10 @@ function wfStreamThumb( array $params ) { // for the thumb params and the parent directory for the source file name. // Check that the zone relative path matches up so squid caches won't pick // up thumbs that would not be purged on source file deletion (bug 34231). - if ( isset( $params['rel404'] ) ) { // thumbnail was handled via 404 - if ( rawurldecode( $params['rel404'] ) === $img->getThumbRel( $thumbName ) ) { + if ( $rel404 !== null ) { // thumbnail was handled via 404 + if ( rawurldecode( $rel404 ) === $img->getThumbRel( $thumbName ) ) { // Request for the canonical thumbnail name - } elseif ( rawurldecode( $params['rel404'] ) === $img->getThumbRel( $thumbName2 ) ) { + } elseif ( rawurldecode( $rel404 ) === $img->getThumbRel( $thumbName2 ) ) { // Request for the "long" thumbnail name; redirect to canonical name $response = RequestContext::getMain()->getRequest()->response(); $response->header( "HTTP/1.1 301 " . HttpStatus::getMessage( 301 ) ); @@ -285,13 +300,15 @@ function wfStreamThumb( array $params ) { } else { wfThumbError( 404, "The given path of the specified thumbnail is incorrect; expected '" . $img->getThumbRel( $thumbName ) . "' but got '" . - rawurldecode( $params['rel404'] ) . "'." ); + rawurldecode( $rel404 ) . "'." ); return; } } + $dispositionType = isset( $params['download'] ) ? 'attachment' : 'inline'; + // Suggest a good name for users downloading this thumbnail - $headers[] = "Content-Disposition: {$img->getThumbDisposition( $thumbName )}"; + $headers[] = "Content-Disposition: {$img->getThumbDisposition( $thumbName, $dispositionType )}"; if ( count( $varyHeader ) ) { $headers[] = 'Vary: ' . implode( ', ', $varyHeader ); @@ -305,24 +322,21 @@ function wfStreamThumb( array $params ) { } $user = RequestContext::getMain()->getUser(); - if ( $user->pingLimiter( 'renderfile' ) ) { + if ( !wfThumbIsStandard( $img, $params ) && $user->pingLimiter( 'renderfile-nonstandard' ) ) { + wfThumbError( 500, wfMessage( 'actionthrottledtext' )->parse() ); + return; + } elseif ( $user->pingLimiter( 'renderfile' ) ) { wfThumbError( 500, wfMessage( 'actionthrottledtext' )->parse() ); return; } - // Thumbnail isn't already there, so create the new thumbnail... - try { - $thumb = $img->transform( $params, File::RENDER_NOW ); - } catch ( Exception $ex ) { - // Tried to select a page on a non-paged file? - $thumb = false; - } + // Actually generate a new thumbnail + list( $thumb, $errorMsg ) = wfGenerateThumbnail( $img, $params, $thumbName, $thumbPath ); // Check for thumbnail generation errors... - $errorMsg = false; $msg = wfMessage( 'thumbnail_error' ); if ( !$thumb ) { - $errorMsg = $msg->rawParams( 'File::transform() returned false' )->escaped(); + $errorMsg = $errorMsg ?: $msg->rawParams( 'File::transform() returned false' )->escaped(); } elseif ( $thumb->isError() ) { $errorMsg = $thumb->getHtmlMsg(); } elseif ( !$thumb->hasFile() ) { @@ -341,6 +355,150 @@ function wfStreamThumb( array $params ) { } /** + * Actually try to generate a new thumbnail + * + * @param File $file + * @param array $params + * @param string $thumbName + * @param string $thumbPath + * @return array (MediaTransformOutput|bool, string|bool error message HTML) + */ +function wfGenerateThumbnail( File $file, array $params, $thumbName, $thumbPath ) { + global $wgMemc, $wgAttemptFailureEpoch; + + $key = wfMemcKey( 'attempt-failures', $wgAttemptFailureEpoch, + $file->getRepo()->getName(), $file->getSha1(), md5( $thumbName ) ); + + // Check if this file keeps failing to render + if ( $wgMemc->get( $key ) >= 4 ) { + return array( false, wfMessage( 'thumbnail_image-failure-limit', 4 ) ); + } + + $done = false; + // Record failures on PHP fatals in addition to caching exceptions + register_shutdown_function( function () use ( &$done, $key ) { + if ( !$done ) { // transform() gave a fatal + global $wgMemc; + // Randomize TTL to reduce stampedes + $wgMemc->incrWithInit( $key, 3600 + mt_rand( 0, 300 ) ); + } + } ); + + $thumb = false; + $errorHtml = false; + + // guard thumbnail rendering with PoolCounter to avoid stampedes + // expensive files use a separate PoolCounter config so it is possible + // to set up a global limit on them + if ( $file->isExpensiveToThumbnail() ) { + $poolCounterType = 'FileRenderExpensive'; + } else { + $poolCounterType = 'FileRender'; + } + + // Thumbnail isn't already there, so create the new thumbnail... + try { + $work = new PoolCounterWorkViaCallback( $poolCounterType, sha1( $file->getName() ), + array( + 'doWork' => function () use ( $file, $params ) { + return $file->transform( $params, File::RENDER_NOW ); + }, + 'getCachedWork' => function () use ( $file, $params, $thumbPath ) { + // If the worker that finished made this thumbnail then use it. + // Otherwise, it probably made a different thumbnail for this file. + return $file->getRepo()->fileExists( $thumbPath ) + ? $file->transform( $params, File::RENDER_NOW ) + : false; // retry once more in exclusive mode + }, + 'fallback' => function () { + return wfMessage( 'generic-pool-error' )->parse(); + }, + 'error' => function ( $status ) { + return $status->getHTML(); + } + ) + ); + $result = $work->execute(); + if ( $result instanceof MediaTransformOutput ) { + $thumb = $result; + } elseif ( is_string( $result ) ) { // error + $errorHtml = $result; + } + } catch ( Exception $e ) { + // Tried to select a page on a non-paged file? + } + + $done = true; // no PHP fatal occured + + if ( !$thumb || $thumb->isError() ) { + // Randomize TTL to reduce stampedes + $wgMemc->incrWithInit( $key, 3600 + mt_rand( 0, 300 ) ); + } + + return array( $thumb, $errorHtml ); +} + +/** + * Returns true if this thumbnail is one that MediaWiki generates + * links to on file description pages and possibly parser output. + * + * $params is considered non-standard if they involve a non-standard + * width or any non-default parameters aside from width and page number. + * The number of possible files with standard parameters is far less than + * that of all combinations; rate-limiting for them can thus be more generious. + * + * @param File $file + * @param array $params + * @return bool + */ +function wfThumbIsStandard( File $file, array $params ) { + global $wgThumbLimits, $wgImageLimits; + + $handler = $file->getHandler(); + if ( !$handler || !isset( $params['width'] ) ) { + return false; + } + + $basicParams = array(); + if ( isset( $params['page'] ) ) { + $basicParams['page'] = $params['page']; + } + + // Check if the width matches one of $wgThumbLimits + if ( in_array( $params['width'], $wgThumbLimits ) ) { + $normalParams = $basicParams + array( 'width' => $params['width'] ); + // Append any default values to the map (e.g. "lossy", "lossless", ...) + $handler->normaliseParams( $file, $normalParams ); + } else { + // If not, then check if the width matchs one of $wgImageLimits + $match = false; + foreach ( $wgImageLimits as $pair ) { + $normalParams = $basicParams + array( 'width' => $pair[0], 'height' => $pair[1] ); + // Decide whether the thumbnail should be scaled on width or height. + // Also append any default values to the map (e.g. "lossy", "lossless", ...) + $handler->normaliseParams( $file, $normalParams ); + // Check if this standard thumbnail size maps to the given width + if ( $normalParams['width'] == $params['width'] ) { + $match = true; + break; + } + } + if ( !$match ) { + return false; // not standard for description pages + } + } + + // Check that the given values for non-page, non-width, params are just defaults + foreach ( $params as $key => $value ) { + if ( !isset( $normalParams[$key] ) || $normalParams[$key] != $value ) { + return false; + } + } + + return true; +} + +/** * Convert pathinfo type parameter, into normal request parameters * * So for example, if the request was redirected from @@ -356,14 +514,15 @@ function wfStreamThumb( array $params ) { * * Transform specific parameters are set later via wfExtractThumbParams(). * - * @param $thumbRel String Thumbnail path relative to the thumb zone - * @return Array|null associative params array or null + * @param string $thumbRel Thumbnail path relative to the thumb zone + * @return array|null Associative params array or null */ function wfExtractThumbRequestInfo( $thumbRel ) { $repo = RepoGroup::singleton()->getLocalRepo(); $hashDirReg = $subdirReg = ''; - for ( $i = 0; $i < $repo->getHashLevels(); $i++ ) { + $hashLevels = $repo->getHashLevels(); + for ( $i = 0; $i < $hashLevels; $i++ ) { $subdirReg .= '[0-9a-f]'; $hashDirReg .= "$subdirReg/"; } @@ -393,9 +552,9 @@ function wfExtractThumbRequestInfo( $thumbRel ) { * Convert a thumbnail name (122px-foo.png) to parameters, using * file handler. * - * @param File $file File object for file in question. - * @param $param Array Array of parameters so far. - * @return Array parameters array with more parameters. + * @param File $file File object for file in question + * @param array $params Array of parameters so far + * @return array Parameters array with more parameters */ function wfExtractThumbParams( $file, $params ) { if ( !isset( $params['thumbName'] ) ) { @@ -414,7 +573,7 @@ function wfExtractThumbParams( $file, $params ) { return $params; // valid thumbnail URL (via extension or config) } - // FIXME: Files in the temp zone don't set a mime type, which means + // FIXME: Files in the temp zone don't set a MIME type, which means // they don't have a handler. Which means we can't parse the param // string. However, not a big issue as what good is a param string // if you have no handler to make use of the param string and @@ -451,7 +610,7 @@ function wfExtractThumbParams( $file, $params ) { /** * Output a thumbnail generation error message * - * @param $status integer + * @param int $status * @param string $msg HTML * @return void */ |