[TASK] Use hash_equals for timing-safe comparison of hash-values
[Packages/TYPO3.CMS.git] / typo3 / sysext / frontend / Classes / Controller / ShowImageController.php
1 <?php
2 namespace TYPO3\CMS\Frontend\Controller;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use Psr\Http\Message\ResponseInterface;
18 use Psr\Http\Message\ServerRequestInterface;
19 use TYPO3\CMS\Core\Exception;
20 use TYPO3\CMS\Core\Resource\ResourceFactory;
21 use TYPO3\CMS\Core\Utility\GeneralUtility;
22 use TYPO3\CMS\Core\Utility\MathUtility;
23
24 /**
25 * eID-Script "tx_cms_showpic"
26 *
27 * Shows a picture from FAL in enlarged format in a separate window.
28 * Picture file and settings is supplied by GET-parameters:
29 *
30 * - file = fileUid or Combined Identifier
31 * - encoded in an parameter Array (with weird format - see ContentObjectRenderer about ll. 1500)
32 * - width, height = usual width an height, m/c supported
33 * - frame
34 * - bodyTag
35 * - title
36 */
37 class ShowImageController
38 {
39 /**
40 * @var \Psr\Http\Message\ServerRequestInterface
41 */
42 protected $request;
43
44 /**
45 * @var \TYPO3\CMS\Core\Resource\File
46 */
47 protected $file;
48
49 /**
50 * @var int
51 */
52 protected $width;
53
54 /**
55 * @var int
56 */
57 protected $height;
58
59 /**
60 * @var string
61 */
62 protected $crop;
63
64 /**
65 * @var int
66 */
67 protected $frame;
68
69 /**
70 * @var string
71 */
72 protected $bodyTag = '<body>';
73
74 /**
75 * @var string
76 */
77 protected $title = 'Image';
78
79 /**
80 * @var string
81 */
82 protected $content = <<<EOF
83 <!DOCTYPE html>
84 <html>
85 <head>
86 <title>###TITLE###</title>
87 <meta name="robots" content="noindex,follow" />
88 </head>
89 ###BODY###
90 ###IMAGE###
91 </body>
92 </html>
93 EOF;
94
95 /**
96 * @var string
97 */
98 protected $imageTag = '<img src="###publicUrl###" alt="###alt###" title="###title###" width="###width###" height="###height###" />';
99
100 /**
101 * Init function, setting the input vars in the global space.
102 *
103 * @throws \InvalidArgumentException
104 * @throws \TYPO3\CMS\Core\Resource\Exception\FileDoesNotExistException
105 */
106 public function initialize()
107 {
108 $fileUid = isset($this->request->getQueryParams()['file']) ? $this->request->getQueryParams()['file'] : null;
109 $parametersArray = isset($this->request->getQueryParams()['parameters']) ? $this->request->getQueryParams()['parameters'] : null;
110
111 // If no file-param or parameters are given, we must exit
112 if (!$fileUid || !isset($parametersArray) || !is_array($parametersArray)) {
113 throw new \InvalidArgumentException('No valid fileUid given', 1476048455);
114 }
115
116 // rebuild the parameter array and check if the HMAC is correct
117 $parametersEncoded = implode('', $parametersArray);
118
119 /* For backwards compatibility the HMAC is transported within the md5 param */
120 $hmacParameter = isset($this->request->getQueryParams()['md5']) ? $this->request->getQueryParams()['md5'] : null;
121 $hmac = GeneralUtility::hmac(implode('|', [$fileUid, $parametersEncoded]));
122 if (!hash_equals($hmac, $hmacParameter)) {
123 throw new \InvalidArgumentException('hash does not match', 1476048456);
124 }
125
126 // decode the parameters Array
127 $parameters = unserialize(base64_decode($parametersEncoded));
128 foreach ($parameters as $parameterName => $parameterValue) {
129 $this->{$parameterName} = $parameterValue;
130 }
131
132 if (MathUtility::canBeInterpretedAsInteger($fileUid)) {
133 $this->file = ResourceFactory::getInstance()->getFileObject((int)$fileUid);
134 } else {
135 $this->file = ResourceFactory::getInstance()->retrieveFileOrFolderObject($fileUid);
136 }
137 $this->frame = isset($this->request->getQueryParams()['frame']) ? $this->request->getQueryParams()['frame'] : null;
138 }
139
140 /**
141 * Main function which creates the image if needed and outputs the HTML code for the page displaying the image.
142 * Accumulates the content in $this->content
143 */
144 public function main()
145 {
146 $processedImage = $this->processImage();
147 $imageTagMarkers = [
148 '###publicUrl###' => htmlspecialchars($processedImage->getPublicUrl()),
149 '###alt###' => htmlspecialchars($this->file->getProperty('alternative') ?: $this->title),
150 '###title###' => htmlspecialchars($this->file->getProperty('title') ?: $this->title),
151 '###width###' => $processedImage->getProperty('width'),
152 '###height###' => $processedImage->getProperty('height')
153 ];
154 $this->imageTag = str_replace(array_keys($imageTagMarkers), array_values($imageTagMarkers), $this->imageTag);
155 $markerArray = [
156 '###TITLE###' => ($this->file->getProperty('title') ?: $this->title),
157 '###IMAGE###' => $this->imageTag,
158 '###BODY###' => $this->bodyTag
159 ];
160
161 $this->content = str_replace(array_keys($markerArray), array_values($markerArray), $this->content);
162 }
163
164 /**
165 * Does the actual image processing
166 *
167 * @return \TYPO3\CMS\Core\Resource\ProcessedFile
168 */
169 protected function processImage()
170 {
171 if (strstr($this->width . $this->height, 'm')) {
172 $max = 'm';
173 } else {
174 $max = '';
175 }
176 $this->height = MathUtility::forceIntegerInRange($this->height, 0);
177 $this->width = MathUtility::forceIntegerInRange($this->width, 0) . $max;
178
179 $processingConfiguration = [
180 'width' => $this->width,
181 'height' => $this->height,
182 'frame' => $this->frame,
183 'crop' => $this->crop,
184 ];
185 return $this->file->process('Image.CropScaleMask', $processingConfiguration);
186 }
187
188 /**
189 * Fetches the content and builds a content file out of it
190 *
191 * @param ServerRequestInterface $request the current request object
192 * @param ResponseInterface $response the available response
193 * @return ResponseInterface the modified response
194 */
195 public function processRequest(ServerRequestInterface $request, ResponseInterface $response)
196 {
197 $this->request = $request;
198
199 try {
200 $this->initialize();
201 $this->main();
202 $response->getBody()->write($this->content);
203 return $response;
204 } catch (\InvalidArgumentException $e) {
205 // add a 410 "gone" if invalid parameters given
206 return $response->withStatus(410);
207 } catch (Exception $e) {
208 return $response->withStatus(404);
209 }
210 }
211 }