99 $permissionManager = MediaWikiServices::getInstance()->getPermissionManager();
103 $fileName = $params[
'f'] ??
'';
106 if ( isset( $params[
'w'] ) ) {
107 $params[
'width'] = $params[
'w'];
108 unset( $params[
'w'] );
110 if ( isset( $params[
'width'] ) && substr( $params[
'width'], -2 ) ==
'px' ) {
112 $params[
'width'] = substr( $params[
'width'], 0, -2 );
114 if ( isset( $params[
'p'] ) ) {
115 $params[
'page'] = $params[
'p'];
119 $isOld = ( isset( $params[
'archived'] ) && $params[
'archived'] );
120 unset( $params[
'archived'] );
123 $isTemp = ( isset( $params[
'temp'] ) && $params[
'temp'] );
124 unset( $params[
'temp'] );
127 $fileName = strtr( $fileName,
'\\/',
'__' );
131 $repo = RepoGroup::singleton()->getLocalRepo()->getTempRepo();
133 # Temp files are hashed based on the name without the timestamp.
134 # The thumbnails will be hashed based on the entire name however.
135 # @todo fix
this convention to actually be reasonable.
136 $repo->getZonePath(
'public' ) .
'/' . $repo->getTempHashPath( $fileName ) . $fileName
138 } elseif ( $isOld ) {
140 $bits = explode(
'!', $fileName, 2 );
141 if ( count( $bits ) != 2 ) {
150 $img = RepoGroup::singleton()->getLocalRepo()->newFromArchiveName(
$title, $fileName );
163 if ( !in_array(
'read', $permissionManager->getGroupPermissions( [
'*' ] ),
true ) ) {
164 $user = RequestContext::getMain()->getUser();
165 $imgTitle = $img->getTitle();
167 if ( !$imgTitle || !$permissionManager->userCan(
'read', $user, $imgTitle ) ) {
168 wfThumbError( 403,
'Access denied. You do not have permission to access ' .
169 'the source file.' );
172 $headers[] =
'Cache-Control: private';
173 $varyHeader[] =
'Cookie';
177 if ( $img->isDeleted( File::DELETED_FILE ) ) {
183 if ( isset( $params[
'thumbName'] ) ) {
186 if ( $params ==
null ) {
187 wfThumbError( 400,
'The specified thumbnail parameters are not recognized.' );
192 if ( !$img->exists() ) {
193 $redirectedLocation =
false;
198 $possRedirFile = RepoGroup::singleton()->getLocalRepo()->findFile( $img->getName() );
199 if ( $possRedirFile && !is_null( $possRedirFile->getRedirected() ) ) {
200 $redirTarget = $possRedirFile->getName();
202 if ( $targetFile->exists() ) {
203 $newThumbName = $targetFile->thumbName( $params );
206 $newThumbUrl = $targetFile->getArchiveThumbUrl(
207 $bits[0] .
'!' . $targetFile->getName(), $newThumbName );
209 $newThumbUrl = $targetFile->getThumbUrl( $newThumbName );
216 if ( $redirectedLocation ) {
218 $response = RequestContext::getMain()->getRequest()->response();
220 $response->header(
'Location: ' . $redirectedLocation );
222 gmdate(
'D, d M Y H:i:s', time() + 12 * 3600 ) .
' GMT' );
224 $varyHeader[] =
'X-Forwarded-Proto';
226 if ( count( $varyHeader ) ) {
227 $response->header(
'Vary: ' . implode(
', ', $varyHeader ) );
229 $response->header(
'Content-Length: 0' );
236 } elseif ( $img->getPath() ===
false ) {
237 wfThumbErrorText( 400,
"The source file '$fileName' is not locally accessible." );
243 if ( !empty( $_SERVER[
'HTTP_IF_MODIFIED_SINCE'] ) ) {
245 $imsString = preg_replace(
'/;.*$/',
'', $_SERVER[
"HTTP_IF_MODIFIED_SINCE"] );
247 Wikimedia\suppressWarnings();
248 $imsUnix = strtotime( $imsString );
249 Wikimedia\restoreWarnings();
250 if (
wfTimestamp( TS_UNIX, $img->getTimestamp() ) <= $imsUnix ) {
251 HttpStatus::header( 304 );
256 $rel404 = $params[
'rel404'] ??
null;
257 unset( $params[
'r'] );
258 unset( $params[
'f'] );
259 unset( $params[
'rel404'] );
263 $thumbName = $img->thumbName( $params );
264 if ( !strlen( $thumbName ) ) {
266 'Empty return from File::thumbName'
269 $thumbName2 = $img->thumbName( $params, File::THUMB_FULL_NAME );
273 'The specified thumbnail parameters are not valid: ' . $e->getMessage()
278 [
'exception' => $e ] );
286 if ( $rel404 !==
null ) {
287 if ( rawurldecode( $rel404 ) === $img->getThumbRel( $thumbName ) ) {
289 } elseif ( rawurldecode( $rel404 ) === $img->getThumbRel( $thumbName2 ) ) {
291 $response = RequestContext::getMain()->getRequest()->response();
296 gmdate(
'D, d M Y H:i:s', time() + 7 * 86400 ) .
' GMT' );
298 $varyHeader[] =
'X-Forwarded-Proto';
300 if ( count( $varyHeader ) ) {
301 $response->header(
'Vary: ' . implode(
', ', $varyHeader ) );
305 wfThumbErrorText( 404,
"The given path of the specified thumbnail is incorrect;
306 expected '" . $img->getThumbRel( $thumbName ) .
"' but got '" .
307 rawurldecode( $rel404 ) .
"'." );
312 $dispositionType = isset( $params[
'download'] ) ?
'attachment' :
'inline';
316 "Content-Disposition: {$img->getThumbDisposition( $thumbName, $dispositionType )}";
318 if ( count( $varyHeader ) ) {
319 $headers[] =
'Vary: ' . implode(
', ', $varyHeader );
323 $thumbPath = $img->getThumbPath( $thumbName );
324 if ( $img->getRepo()->fileExists( $thumbPath ) ) {
326 $status = $img->getRepo()->streamFileWithStatus( $thumbPath, $headers );
329 if ( $status->isOK() ) {
330 MediaWikiServices::getInstance()->getStatsdDataFactory()->timing(
331 'media.thumbnail.stream', $streamtime
334 wfThumbError( 500,
'Could not stream the file',
null, [
'file' => $thumbName,
335 'path' => $thumbPath,
'error' => $status->getWikiText(
false,
false,
'en' ) ] );
340 $user = RequestContext::getMain()->getUser();
341 if ( !
wfThumbIsStandard( $img, $params ) && $user->pingLimiter(
'renderfile-nonstandard' ) ) {
344 } elseif ( $user->pingLimiter(
'renderfile' ) ) {
349 $thumbProxyUrl = $img->getRepo()->getThumbProxyUrl();
351 if ( strlen( $thumbProxyUrl ) ) {
367 $errorMsg = $errorMsg ?: $msg->rawParams(
'File::transform() returned false' )->escaped();
369 $errorMsg->
getKey() ===
'thumbnail_image-failure-limit'
373 } elseif ( $thumb->isError() ) {
374 $errorMsg = $thumb->getHtmlMsg();
375 $errorCode = $thumb->getHttpStatusCode();
376 } elseif ( !$thumb->hasFile() ) {
377 $errorMsg = $msg->rawParams(
'No path supplied in thumbnail object' )->escaped();
378 } elseif ( $thumb->fileIsSource() ) {
380 ->rawParams(
'Image was not scaled, is the requested width bigger than the source?' )
385 if ( $errorMsg !==
false ) {
386 wfThumbError( $errorCode, $errorMsg,
null, [
'file' => $thumbName,
'path' => $thumbPath ] );
389 $status = $thumb->streamFileWithStatus( $headers );
390 if ( !$status->isOK() ) {
392 'file' => $thumbName,
'path' => $thumbPath,
393 'error' => $status->getWikiText(
false,
false,
'en' ) ] );
449 $cache = ObjectCache::getLocalClusterInstance();
453 $file->getRepo()->getName(),
459 if (
$cache->get( $key ) >= 4 ) {
460 return [
false,
wfMessage(
'thumbnail_image-failure-limit', 4 ) ];
465 register_shutdown_function(
function () use (
$cache, &$done, $key ) {
468 $cache->incrWithInit( $key, $cache::TTL_HOUR + mt_rand( 0, 300 ) );
478 if (
$file->isExpensiveToThumbnail() ) {
479 $poolCounterType =
'FileRenderExpensive';
481 $poolCounterType =
'FileRender';
488 'doWork' => function () use (
$file, $params ) {
489 return $file->transform( $params, File::RENDER_NOW );
491 'doCachedWork' =>
function () use (
$file, $params, $thumbPath ) {
494 return $file->getRepo()->fileExists( $thumbPath )
495 ?
$file->transform( $params, File::RENDER_NOW )
498 'error' =>
function (
Status $status ) {
499 return wfMessage(
'generic-pool-error' )->parse() .
'<hr>' . $status->getHTML();
506 } elseif ( is_string( $result ) ) {
507 $errorHtml = $result;
509 }
catch ( Exception $e ) {
516 if ( !$thumb || $thumb->isError() ) {
518 $cache->incrWithInit( $key, $cache::TTL_HOUR + mt_rand( 0, 300 ) );
521 return [ $thumb, $errorHtml ];
544 $repo = RepoGroup::singleton()->getLocalRepo();
546 $hashDirReg = $subdirReg =
'';
547 $hashLevels = $repo->getHashLevels();
548 for ( $i = 0; $i < $hashLevels; $i++ ) {
549 $subdirReg .=
'[0-9a-f]';
550 $hashDirReg .=
"$subdirReg/";
554 if ( preg_match(
"!^((archive/)?$hashDirReg([^/]*)/([^/]*))$!", $thumbRel, $m ) ) {
555 list( , $rel, $archOrTemp, $filename, $thumbname ) = $m;
557 } elseif ( preg_match(
"!^(temp/)($hashDirReg([^/]*)/([^/]*))$!", $thumbRel, $m ) ) {
558 list( , $archOrTemp, $rel, $filename, $thumbname ) = $m;
563 $params = [
'f' => $filename,
'rel404' => $rel ];
564 if ( $archOrTemp ===
'archive/' ) {
565 $params[
'archived'] = 1;
566 } elseif ( $archOrTemp ===
'temp/' ) {
570 $params[
'thumbName'] = $thumbname;
648 MediaWiki\HeaderCallback::warnIfHeadersSent();
650 header(
'Cache-Control: no-cache' );
651 header(
'Content-Type: text/html; charset=utf-8' );
652 if ( $status == 400 || $status == 404 || $status == 429 ) {
653 HttpStatus::header( $status );
654 } elseif ( $status == 403 ) {
655 HttpStatus::header( 403 );
656 header(
'Vary: Cookie' );
658 LoggerFactory::getInstance(
'thumb' )->error( $msgText ?: $msgHtml,
$context );
659 HttpStatus::header( 500 );
662 header(
'X-MW-Thumbnail-Renderer: ' .
wfHostname() );
663 $url = htmlspecialchars(
664 $_SERVER[
'REQUEST_URI'] ??
'',
667 $hostname = htmlspecialchars(
wfHostname(), ENT_NOQUOTES );
668 $debug =
"<!-- $url -->\n<!-- $hostname -->\n";
675<meta charset=
"UTF-8" />
676<title>Error generating thumbnail</title>
679<h1>Error generating thumbnail</h1>
688 header(
'Content-Length: ' . strlen(
$content ) );