‪TYPO3CMS  ‪main
ShowImageController.php
Go to the documentation of this file.
1 <?php
2 
3 declare(strict_types=1);
4 
5 /*
6  * This file is part of the TYPO3 CMS project.
7  *
8  * It is free software; you can redistribute it and/or modify it under
9  * the terms of the GNU General Public License, either version 2
10  * of the License, or any later version.
11  *
12  * For the full copyright and license information, please read the
13  * LICENSE.txt file that was distributed with this source code.
14  *
15  * The TYPO3 project - inspiring people to share!
16  */
17 
19 
20 use Psr\Http\Message\ResponseInterface;
21 use Psr\Http\Message\ServerRequestInterface;
32 
49 {
50  protected const ‪ALLOWED_PARAMETER_NAMES = ['width', 'height', 'crop', 'bodyTag', 'title'];
51 
55  protected ‪$request;
56 
60  protected ‪$file;
61 
65  protected ‪$width;
66 
70  protected ‪$height;
71 
75  protected ‪$crop;
76 
80  protected ‪$frame;
81 
85  protected ‪$bodyTag = '<body>';
86 
90  protected ‪$title = 'Image';
91 
95  protected ‪$content = <<<EOF
96 <!DOCTYPE html>
97 <html>
98 <head>
99  <title>###TITLE###</title>
100  <meta name="robots" content="noindex,follow" />
101 </head>
102 ###BODY###
103  ###IMAGE###
104 </body>
105 </html>
106 EOF;
107 
111  protected ‪$imageTag = '<img src="###publicUrl###" alt="###alt###" title="###title###" width="###width###" height="###height###" />';
112 
119  public function ‪initialize()
120  {
121  $fileUid = $this->request->getQueryParams()['file'] ?? null;
122  $parametersArray = $this->request->getQueryParams()['parameters'] ?? null;
123 
124  // If no file-param or parameters are given, we must exit
125  if (!$fileUid || !isset($parametersArray) || !is_array($parametersArray)) {
126  throw new \InvalidArgumentException('No valid fileUid given', 1476048455);
127  }
128 
129  // rebuild the parameter array and check if the HMAC is correct
130  $parametersEncoded = implode('', $parametersArray);
131 
132  /* For backwards compatibility the HMAC is transported within the md5 param */
133  $hmacParameter = $this->request->getQueryParams()['md5'] ?? null;
134  $hmac = ‪GeneralUtility::hmac(implode('|', [$fileUid, $parametersEncoded]));
135  if (!is_string($hmacParameter) || !hash_equals($hmac, $hmacParameter)) {
136  throw new \InvalidArgumentException('hash does not match', 1476048456);
137  }
138 
139  // decode the parameters Array - `bodyTag` contains HTML if set and would lead
140  // to a false-positive XSS-detection, that's why parameters are base64-encoded
141  $parameters = json_decode(base64_decode($parametersEncoded), true) ?? [];
142  foreach ($parameters as $parameterName => $parameterValue) {
143  if (in_array($parameterName, static::ALLOWED_PARAMETER_NAMES, true)) {
144  $this->{$parameterName} = $parameterValue;
145  }
146  }
147 
149  $this->file = GeneralUtility::makeInstance(ResourceFactory::class)->getFileObject((int)$fileUid);
150  } else {
151  $this->file = GeneralUtility::makeInstance(ResourceFactory::class)->retrieveFileOrFolderObject($fileUid);
152  }
153  if (!($this->file instanceof ‪FileInterface && $this->‪isFileValid($this->file))) {
154  throw new ‪Exception('File processing for local storage is denied', 1594043425);
155  }
156 
157  $this->frame = $this->request->getQueryParams()['frame'] ?? null;
158  }
159 
164  public function ‪main()
165  {
166  $processedImage = $this->‪processImage();
167  $imageTagMarkers = [
168  '###publicUrl###' => htmlspecialchars($processedImage->getPublicUrl() ?? ''),
169  '###alt###' => htmlspecialchars($this->file->getProperty('alternative') ?: $this->title),
170  '###title###' => htmlspecialchars($this->file->getProperty('title') ?: $this->title),
171  '###width###' => $processedImage->getProperty('width'),
172  '###height###' => $processedImage->getProperty('height'),
173  ];
174  $this->imageTag = str_replace(array_keys($imageTagMarkers), array_values($imageTagMarkers), $this->imageTag);
175  $markerArray = [
176  '###TITLE###' => $this->file->getProperty('title') ?: ‪$this->title,
177  '###IMAGE###' => ‪$this->imageTag,
178  '###BODY###' => ‪$this->bodyTag,
179  ];
180 
181  $this->content = str_replace(array_keys($markerArray), array_values($markerArray), $this->content);
182  }
183 
189  protected function ‪processImage()
190  {
191  $max = str_contains($this->width . $this->height, 'm') ? 'm' : '';
192  $this->height = ‪MathUtility::forceIntegerInRange($this->height, 0);
193  $this->width = ‪MathUtility::forceIntegerInRange((int)$this->width, 0) . $max;
194 
195  $processingConfiguration = [
196  'width' => ‪$this->width,
197  'height' => ‪$this->height,
198  'frame' => ‪$this->frame,
199  'crop' => ‪$this->crop,
200  ];
201  return $this->file->process(‪ProcessedFile::CONTEXT_IMAGECROPSCALEMASK, $processingConfiguration);
202  }
203 
210  public function ‪processRequest(ServerRequestInterface ‪$request): ResponseInterface
211  {
212  $this->request = ‪$request;
213 
214  try {
215  $this->‪initialize();
216  $this->‪main();
217  $response = new ‪Response();
218  $response->getBody()->write($this->content);
219  return $response;
220  } catch (\InvalidArgumentException $e) {
221  // add a 410 "gone" if invalid parameters given
222  return (new ‪Response())->withStatus(410);
223  } catch (‪Exception $e) {
224  return (new Response())->withStatus(404);
225  }
226  }
227 
228  protected function ‪isFileValid(FileInterface ‪$file): bool
229  {
230  return ‪$file->‪getStorage()->getDriverType() !== 'Local'
231  || GeneralUtility::makeInstance(FileNameValidator::class)
232  ->isValid(basename(‪$file->‪getIdentifier()));
233  }
234 }
‪TYPO3\CMS\Frontend\Controller\ShowImageController\$bodyTag
‪string $bodyTag
Definition: ShowImageController.php:78
‪TYPO3\CMS\Core\Resource\FileInterface
Definition: FileInterface.php:26
‪TYPO3\CMS\Core\Exception
Definition: Exception.php:21
‪TYPO3\CMS\Core\Resource\Security\FileNameValidator
Definition: FileNameValidator.php:25
‪TYPO3\CMS\Frontend\Controller\ShowImageController\ALLOWED_PARAMETER_NAMES
‪const ALLOWED_PARAMETER_NAMES
Definition: ShowImageController.php:50
‪TYPO3\CMS\Core\Resource\ProcessedFile\CONTEXT_IMAGECROPSCALEMASK
‪const CONTEXT_IMAGECROPSCALEMASK
Definition: ProcessedFile.php:61
‪TYPO3\CMS\Core\Exception
‪TYPO3\CMS\Frontend\Controller\ShowImageController\processImage
‪TYPO3 CMS Core Resource ProcessedFile processImage()
Definition: ShowImageController.php:179
‪TYPO3\CMS\Frontend\Controller\ShowImageController\$frame
‪int $frame
Definition: ShowImageController.php:74
‪TYPO3\CMS\Frontend\Controller\ShowImageController\isFileValid
‪isFileValid(FileInterface $file)
Definition: ShowImageController.php:218
‪TYPO3\CMS\Frontend\Controller\ShowImageController\processRequest
‪ResponseInterface processRequest(ServerRequestInterface $request)
Definition: ShowImageController.php:200
‪TYPO3\CMS\Frontend\Controller\ShowImageController\initialize
‪initialize()
Definition: ShowImageController.php:109
‪TYPO3\CMS\Frontend\Controller\ShowImageController\$file
‪File Folder null $file
Definition: ShowImageController.php:58
‪TYPO3\CMS\Core\Utility\MathUtility\canBeInterpretedAsInteger
‪static bool canBeInterpretedAsInteger(mixed $var)
Definition: MathUtility.php:69
‪TYPO3\CMS\Frontend\Controller\ShowImageController\$crop
‪string $crop
Definition: ShowImageController.php:70
‪TYPO3\CMS\Core\Http\Response
Definition: Response.php:32
‪TYPO3\CMS\Frontend\Controller\ShowImageController\$title
‪string $title
Definition: ShowImageController.php:82
‪TYPO3\CMS\Frontend\Controller\ShowImageController\$request
‪Psr Http Message ServerRequestInterface $request
Definition: ShowImageController.php:54
‪TYPO3\CMS\Frontend\Exception
Definition: Exception.php:23
‪TYPO3\CMS\Frontend\Controller\ShowImageController\$content
‪string $content
Definition: ShowImageController.php:86
‪TYPO3\CMS\Core\Resource\AbstractFile\getStorage
‪int< 0, getSize():int { if( $this->deleted) { throw new \RuntimeException( 'File has been deleted.', 1329821480);} if(empty( $this->properties[ 'size'])) { $fileInfo=$this-> getStorage() -> getFileInfoByIdentifier($this->getIdentifier(), ['size'])
‪TYPO3\CMS\Core\Resource\Folder
Definition: Folder.php:38
‪TYPO3\CMS\Core\Resource\ResourceFactory
Definition: ResourceFactory.php:42
‪TYPO3\CMS\Core\Resource\File
Definition: File.php:26
‪TYPO3\CMS\Frontend\Controller\ShowImageController\main
‪main()
Definition: ShowImageController.php:154
‪TYPO3\CMS\Core\Utility\GeneralUtility\hmac
‪static string hmac($input, $additionalSecret='')
Definition: GeneralUtility.php:474
‪TYPO3\CMS\Frontend\Controller\ShowImageController\$height
‪int $height
Definition: ShowImageController.php:66
‪TYPO3\CMS\Frontend\Controller\ShowImageController\$imageTag
‪string $imageTag
Definition: ShowImageController.php:101
‪TYPO3\CMS\Core\Resource\ProcessedFile
Definition: ProcessedFile.php:47
‪TYPO3\CMS\Core\Utility\MathUtility
Definition: MathUtility.php:24
‪TYPO3\CMS\Frontend\Controller\ShowImageController\$width
‪string $width
Definition: ShowImageController.php:62
‪TYPO3\CMS\Frontend\Controller
Definition: ErrorController.php:18
‪TYPO3\CMS\Core\Utility\MathUtility\forceIntegerInRange
‪static int forceIntegerInRange(mixed $theInt, int $min, int $max=2000000000, int $defaultValue=0)
Definition: MathUtility.php:34
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:52
‪TYPO3\CMS\Core\Resource\AbstractFile\getIdentifier
‪getIdentifier()
Definition: AbstractFile.php:144
‪TYPO3\CMS\Frontend\Controller\ShowImageController
Definition: ShowImageController.php:49