‪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;
33 
50 {
51  protected const ‪ALLOWED_PARAMETER_NAMES = ['width', 'height', 'crop', 'bodyTag', 'title'];
52 
56  protected ‪$request;
57 
61  protected ‪$file;
62 
66  protected ‪$width;
67 
71  protected ‪$height;
72 
76  protected ‪$crop;
77 
81  protected ‪$frame;
82 
86  protected ‪$bodyTag = '<body>';
87 
91  protected ‪$title = 'Image';
92 
96  protected ‪$content = <<<EOF
97 <!DOCTYPE html>
98 <html>
99 <head>
100  <title>###TITLE###</title>
101  <meta name="robots" content="noindex,follow" />
102 </head>
103 ###BODY###
104  ###IMAGE###
105 </body>
106 </html>
107 EOF;
108 
112  protected ‪$imageTag = '<img src="###publicUrl###" alt="###alt###" title="###title###" width="###width###" height="###height###" />';
113 
120  public function ‪initialize()
121  {
122  $fileUid = $this->request->getQueryParams()['file'] ?? null;
123  $parametersArray = $this->request->getQueryParams()['parameters'] ?? null;
124 
125  // If no file-param or parameters are given, we must exit
126  if (!$fileUid || !isset($parametersArray) || !is_array($parametersArray)) {
127  throw new \InvalidArgumentException('No valid fileUid given', 1476048455);
128  }
129 
130  // rebuild the parameter array and check if the HMAC is correct
131  $parametersEncoded = implode('', $parametersArray);
132 
133  /* For backwards compatibility the HMAC is transported within the md5 param */
134  $hmacParameter = $this->request->getQueryParams()['md5'] ?? null;
135  $hashService = GeneralUtility::makeInstance(HashService::class);
136  $hmac = $hashService->hmac(implode('|', [$fileUid, $parametersEncoded]), 'tx_cms_showpic');
137  if (!is_string($hmacParameter) || !hash_equals($hmac, $hmacParameter)) {
138  throw new \InvalidArgumentException('hash does not match', 1476048456);
139  }
140 
141  // decode the parameters Array - `bodyTag` contains HTML if set and would lead
142  // to a false-positive XSS-detection, that's why parameters are base64-encoded
143  $parameters = json_decode(base64_decode($parametersEncoded), true) ?? [];
144  foreach ($parameters as $parameterName => $parameterValue) {
145  if (in_array($parameterName, static::ALLOWED_PARAMETER_NAMES, true)) {
146  $this->{$parameterName} = $parameterValue;
147  }
148  }
149 
151  $this->file = GeneralUtility::makeInstance(ResourceFactory::class)->getFileObject((int)$fileUid);
152  } else {
153  $this->file = GeneralUtility::makeInstance(ResourceFactory::class)->retrieveFileOrFolderObject($fileUid);
154  }
155  if (!($this->file instanceof ‪FileInterface && $this->‪isFileValid($this->file))) {
156  throw new ‪Exception('File processing for local storage is denied', 1594043425);
157  }
158 
159  $this->frame = $this->request->getQueryParams()['frame'] ?? null;
160  }
161 
166  public function ‪main()
167  {
168  $processedImage = $this->‪processImage();
169  $imageTagMarkers = [
170  '###publicUrl###' => htmlspecialchars($processedImage->getPublicUrl() ?? ''),
171  '###alt###' => htmlspecialchars($this->file->getProperty('alternative') ?: $this->title),
172  '###title###' => htmlspecialchars($this->file->getProperty('title') ?: $this->title),
173  '###width###' => $processedImage->getProperty('width'),
174  '###height###' => $processedImage->getProperty('height'),
175  ];
176  $this->imageTag = str_replace(array_keys($imageTagMarkers), array_values($imageTagMarkers), $this->imageTag);
177  $markerArray = [
178  '###TITLE###' => $this->file->getProperty('title') ?: ‪$this->title,
179  '###IMAGE###' => ‪$this->imageTag,
180  '###BODY###' => ‪$this->bodyTag,
181  ];
182 
183  $this->content = str_replace(array_keys($markerArray), array_values($markerArray), $this->content);
184  }
185 
191  protected function ‪processImage()
192  {
193  $max = str_contains($this->width . $this->height, 'm') ? 'm' : '';
194  $this->height = ‪MathUtility::forceIntegerInRange($this->height, 0);
195  $this->width = ‪MathUtility::forceIntegerInRange((int)$this->width, 0) . $max;
196 
197  $processingConfiguration = [
198  'width' => ‪$this->width,
199  'height' => ‪$this->height,
200  'frame' => ‪$this->frame,
201  'crop' => ‪$this->crop,
202  ];
203  return $this->file->process(‪ProcessedFile::CONTEXT_IMAGECROPSCALEMASK, $processingConfiguration);
204  }
205 
212  public function ‪processRequest(ServerRequestInterface ‪$request): ResponseInterface
213  {
214  $this->request = ‪$request;
215 
216  try {
217  $this->‪initialize();
218  $this->‪main();
219  $response = new ‪Response();
220  $response->getBody()->write($this->content);
221  return $response;
222  } catch (\InvalidArgumentException $e) {
223  // add a 410 "gone" if invalid parameters given
224  return (new ‪Response())->withStatus(410);
225  } catch (‪Exception $e) {
226  return (new Response())->withStatus(404);
227  }
228  }
229 
230  protected function ‪isFileValid(FileInterface ‪$file): bool
231  {
232  return ‪$file->‪getStorage()->getDriverType() !== 'Local'
233  || GeneralUtility::makeInstance(FileNameValidator::class)
234  ->isValid(basename(‪$file->‪getIdentifier()));
235  }
236 }
‪TYPO3\CMS\Frontend\Controller\ShowImageController\$bodyTag
‪string $bodyTag
Definition: ShowImageController.php:79
‪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:51
‪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:181
‪TYPO3\CMS\Frontend\Controller\ShowImageController\$frame
‪int $frame
Definition: ShowImageController.php:75
‪TYPO3\CMS\Frontend\Controller\ShowImageController\isFileValid
‪isFileValid(FileInterface $file)
Definition: ShowImageController.php:220
‪TYPO3\CMS\Frontend\Controller\ShowImageController\processRequest
‪ResponseInterface processRequest(ServerRequestInterface $request)
Definition: ShowImageController.php:202
‪TYPO3\CMS\Frontend\Controller\ShowImageController\initialize
‪initialize()
Definition: ShowImageController.php:110
‪TYPO3\CMS\Frontend\Controller\ShowImageController\$file
‪File Folder null $file
Definition: ShowImageController.php:59
‪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:71
‪TYPO3\CMS\Core\Http\Response
Definition: Response.php:32
‪TYPO3\CMS\Frontend\Controller\ShowImageController\$title
‪string $title
Definition: ShowImageController.php:83
‪TYPO3\CMS\Frontend\Controller\ShowImageController\$request
‪Psr Http Message ServerRequestInterface $request
Definition: ShowImageController.php:55
‪TYPO3\CMS\Frontend\Exception
Definition: Exception.php:23
‪TYPO3\CMS\Frontend\Controller\ShowImageController\$content
‪string $content
Definition: ShowImageController.php:87
‪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:156
‪TYPO3\CMS\Frontend\Controller\ShowImageController\$height
‪int $height
Definition: ShowImageController.php:67
‪TYPO3\CMS\Frontend\Controller\ShowImageController\$imageTag
‪string $imageTag
Definition: ShowImageController.php:102
‪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:63
‪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\Crypto\HashService
Definition: HashService.php:27
‪TYPO3\CMS\Core\Resource\AbstractFile\getIdentifier
‪getIdentifier()
Definition: AbstractFile.php:144
‪TYPO3\CMS\Frontend\Controller\ShowImageController
Definition: ShowImageController.php:50