‪TYPO3CMS  11.5
FileDumpController.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\EventDispatcher\EventDispatcherInterface;
21 use Psr\Http\Message\ResponseFactoryInterface;
22 use Psr\Http\Message\ResponseInterface;
23 use Psr\Http\Message\ServerRequestInterface;
38 
43 {
45  protected EventDispatcherInterface ‪$eventDispatcher;
46  protected ResponseFactoryInterface ‪$responseFactory;
47 
48  public function ‪__construct(
49  EventDispatcherInterface ‪$eventDispatcher,
51  ResponseFactoryInterface ‪$responseFactory
52  ) {
53  $this->eventDispatcher = ‪$eventDispatcher;
54  $this->resourceFactory = ‪$resourceFactory;
55  $this->responseFactory = ‪$responseFactory;
56  }
57 
68  public function ‪dumpAction(ServerRequestInterface $request): ResponseInterface
69  {
70  $parameters = $this->‪buildParametersFromRequest($request);
71 
72  if (!$this->‪isTokenValid($parameters, $request)) {
73  return $this->responseFactory->createResponse(403);
74  }
75  $file = $this->‪createFileObjectByParameters($parameters);
76  if ($file === null) {
77  return $this->responseFactory->createResponse(404);
78  }
79 
80  if (!empty(‪$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['FileDumpEID.php']['checkFileAccess'])) {
81  trigger_error(
82  'The hook $TYPO3_CONF_VARS[SC_OPTIONS][FileDumpEID.php][checkFileAccess] is deprecated and will stop working in TYPO3 v12.0. Use the ModifyFileDumpEvent instead.',
83  E_USER_DEPRECATED
84  );
85  }
86 
87  // Hook: allow some other process to do some security/access checks. Hook should return 403 response if access is rejected, void otherwise
88  // @deprecated: will be removed in TYPO3 v12.0.
89  foreach (‪$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['FileDumpEID.php']['checkFileAccess'] ?? [] as $className) {
90  $hookObject = GeneralUtility::makeInstance($className);
91  if (!$hookObject instanceof ‪FileDumpEIDHookInterface) {
92  throw new \UnexpectedValueException($className . ' must implement interface ' . FileDumpEIDHookInterface::class, 1394442417);
93  }
94  $response = $hookObject->checkFileAccess($file);
95  if ($response instanceof ResponseInterface) {
96  return $response;
97  }
98  }
99 
100  // Allow some other process to do some security/access checks.
101  // Event Listeners should return a 403 response if access is rejected
102  $event = new ‪ModifyFileDumpEvent($file, $request);
103  $event = $this->eventDispatcher->dispatch($event);
104  if ($event->isPropagationStopped()) {
105  return $this->‪applyContentSecurityPolicy($event->getFile(), $event->getResponse());
106  }
107  $file = $event->getFile();
108 
109  $processingInstructions = [];
110 
111  // Apply cropping, if possible
112  if (!empty($parameters['cv'])) {
113  $cropVariant = $parameters['cv'];
114  $cropString = $file instanceof ‪FileReference ? $file->‪getProperty('crop') : '';
115  $cropArea = ‪CropVariantCollection::create((string)$cropString)->‪getCropArea($cropVariant);
116  $processingInstructions = array_merge(
117  $processingInstructions,
118  [
119  'crop' => $cropArea->isEmpty() ? null : $cropArea->makeAbsoluteBasedOnFile($file),
120  ]
121  );
122  }
123 
124  // Apply width/height, if given
125  if (!empty($parameters['s'])) {
126  $size = ‪GeneralUtility::trimExplode(':', $parameters['s']);
127  $processingInstructions = array_merge(
128  $processingInstructions,
129  [
130  'width' => $size[0] ?? null,
131  'height' => $size[1] ?? null,
132  'minWidth' => $size[2] ? (int)$size[2] : null,
133  'minHeight' => $size[3] ? (int)$size[3] : null,
134  'maxWidth' => $size[4] ? (int)$size[4] : null,
135  'maxHeight' => $size[5] ? (int)$size[5] : null,
136  ]
137  );
138  }
139 
140  if (!empty($processingInstructions) && !($file instanceof ‪ProcessedFile)) {
141  if (is_callable([$file, 'getOriginalFile'])) {
142  // Get the original file from the file reference
143  $file = $file->getOriginalFile();
144  }
145  $file = $file->process(‪ProcessedFile::CONTEXT_IMAGECROPSCALEMASK, $processingInstructions);
146  }
147 
148  return $this->‪applyContentSecurityPolicy(
149  $file,
150  $file->getStorage()->streamFile(
151  $file,
152  (bool)($parameters['dl'] ?? false),
153  $parameters['fn'] ?? null
154  )
155  );
156  }
157 
158  protected function ‪buildParametersFromRequest(ServerRequestInterface $request): array
159  {
160  $parameters = ['eID' => 'dumpFile'];
161  $queryParams = $request->getQueryParams();
162  // Identifier of what to process. f, r or p
163  // Only needed while hash_equals
164  $t = (string)($queryParams['t'] ?? '');
165  if ($t) {
166  $parameters['t'] = $t;
167  }
168  // sys_file
169  $f = (string)($queryParams['f'] ?? '');
170  if ($f) {
171  $parameters['f'] = (int)$f;
172  }
173  // sys_file_reference
174  $r = (string)($queryParams['r'] ?? '');
175  if ($r) {
176  $parameters['r'] = (int)$r;
177  }
178  // Processed file
179  $p = (string)($queryParams['p'] ?? '');
180  if ($p) {
181  $parameters['p'] = (int)$p;
182  }
183  // File's width and height in this order: w:h:minW:minH:maxW:maxH
184  $s = (string)($queryParams['s'] ?? '');
185  if ($s) {
186  $parameters['s'] = $s;
187  }
188  // File's crop variant
189  $cv = (string)($queryParams['cv'] ?? '');
190  if ($cv) {
191  $parameters['cv'] = $cv;
192  }
193  // As download
194  $dl = (string)($queryParams['dl'] ?? '');
195  if ($dl) {
196  $parameters['dl'] = (int)$dl;
197  }
198  // Alternative file name
199  $fn = (string)($queryParams['fn'] ?? '');
200  if ($fn) {
201  $parameters['fn'] = $fn;
202  }
203 
204  return $parameters;
205  }
206 
207  protected function ‪isTokenValid(array $parameters, ServerRequestInterface $request): bool
208  {
209  return hash_equals(
210  GeneralUtility::hmac(implode('|', $parameters), 'resourceStorageDumpFile'),
211  $request->getQueryParams()['token'] ?? ''
212  );
213  }
214 
219  protected function ‪createFileObjectByParameters(array $parameters)
220  {
221  $file = null;
222  if (isset($parameters['f'])) {
223  try {
224  $file = $this->resourceFactory->getFileObject($parameters['f']);
225  if ($file->isDeleted() || $file->isMissing() || !$this->isFileValid($file)) {
226  $file = null;
227  }
228  } catch (\‪Exception $e) {
229  $file = null;
230  }
231  } elseif (isset($parameters['r'])) {
232  try {
233  $file = $this->resourceFactory->getFileReferenceObject($parameters['r']);
234  if ($file->isMissing() || !$this->isFileValid($file->getOriginalFile())) {
235  $file = null;
236  }
237  } catch (\‪Exception $e) {
238  $file = null;
239  }
240  } elseif (isset($parameters['p'])) {
241  try {
242  $processedFileRepository = GeneralUtility::makeInstance(ProcessedFileRepository::class);
244  $file = $processedFileRepository->findByUid($parameters['p']);
245  if (!$file || $file->isDeleted() || !$this->isFileValid($file->getOriginalFile())) {
246  $file = null;
247  }
248  } catch (\‪Exception $e) {
249  $file = null;
250  }
251  }
252  return $file;
253  }
254 
255  protected function ‪isFileValid(‪FileInterface $file): bool
256  {
257  return $file->‪getStorage()->‪getDriverType() !== 'Local'
258  || GeneralUtility::makeInstance(FileNameValidator::class)
259  ->isValid(basename($file->‪getIdentifier()));
260  }
261 
265  protected function ‪applyContentSecurityPolicy(‪ResourceInterface $file, ResponseInterface $response): ResponseInterface
266  {
267  $extension = ‪PathUtility::pathinfo($file->‪getName(), PATHINFO_EXTENSION);
268  // same as in `typo3/sysext/install/Resources/Private/FolderStructureTemplateFiles/resources-root-htaccess`
269  if ($extension === 'pdf' || $response->getHeaderLine('content-type') === 'application/pdf') {
270  $policy = "default-src 'self' 'unsafe-inline'; script-src 'none'; object-src 'self'; plugin-types application/pdf;";
271  } elseif ($extension === 'svg' || $response->getHeaderLine('content-type') === 'image/svg+xml') {
272  $policy = "default-src 'self'; script-src 'none'; style-src 'unsafe-inline'; object-src 'none';";
273  } else {
274  $policy = "default-src 'self'; script-src 'none'; style-src 'none'; object-src 'none';";
275  }
276  return $response->withAddedHeader('content-security-policy', $policy);
277  }
278 }
‪TYPO3\CMS\Core\Utility\GeneralUtility\trimExplode
‪static list< string > trimExplode($delim, $string, $removeEmptyValues=false, $limit=0)
Definition: GeneralUtility.php:999
‪TYPO3\CMS\Core\Resource\ProcessedFileRepository
Definition: ProcessedFileRepository.php:31
‪TYPO3\CMS\Core\Utility\PathUtility
Definition: PathUtility.php:25
‪TYPO3\CMS\Core\Resource\Hook\FileDumpEIDHookInterface
Definition: FileDumpEIDHookInterface.php:27
‪TYPO3\CMS\Core\Controller\FileDumpController\$eventDispatcher
‪EventDispatcherInterface $eventDispatcher
Definition: FileDumpController.php:45
‪TYPO3\CMS\Core\Controller\FileDumpController\createFileObjectByParameters
‪File FileReference ProcessedFile null createFileObjectByParameters(array $parameters)
Definition: FileDumpController.php:219
‪TYPO3\CMS\Core\Resource\FileInterface
Definition: FileInterface.php:22
‪TYPO3\CMS\Core\Resource\ResourceStorage\getDriverType
‪string getDriverType()
Definition: ResourceStorage.php:2904
‪TYPO3\CMS\Core\Controller\FileDumpController\$resourceFactory
‪ResourceFactory $resourceFactory
Definition: FileDumpController.php:44
‪TYPO3\CMS\Core\Exception
Definition: Exception.php:21
‪TYPO3\CMS\Core\Resource\Security\FileNameValidator
Definition: FileNameValidator.php:25
‪TYPO3\CMS\Core\Controller\FileDumpController\isFileValid
‪isFileValid(FileInterface $file)
Definition: FileDumpController.php:255
‪TYPO3\CMS\Core\Controller\FileDumpController\isTokenValid
‪isTokenValid(array $parameters, ServerRequestInterface $request)
Definition: FileDumpController.php:207
‪TYPO3\CMS\Core\Resource\ProcessedFile\CONTEXT_IMAGECROPSCALEMASK
‪const CONTEXT_IMAGECROPSCALEMASK
Definition: ProcessedFile.php:59
‪TYPO3\CMS\Core\Resource\FileReference\getProperty
‪mixed getProperty($key)
Definition: FileReference.php:110
‪TYPO3\CMS\Core\Controller\FileDumpController\buildParametersFromRequest
‪buildParametersFromRequest(ServerRequestInterface $request)
Definition: FileDumpController.php:158
‪TYPO3\CMS\Core\Resource\FileReference
Definition: FileReference.php:33
‪TYPO3\CMS\Core\Controller
Definition: ErrorPageController.php:18
‪TYPO3\CMS\Core\Resource\Exception\FileDoesNotExistException
Definition: FileDoesNotExistException.php:21
‪TYPO3\CMS\Core\Resource\ResourceInterface\getIdentifier
‪string getIdentifier()
‪TYPO3\CMS\Core\Resource\ResourceInterface\getStorage
‪ResourceStorage getStorage()
‪TYPO3\CMS\Core\Controller\FileDumpController\dumpAction
‪ResponseInterface dumpAction(ServerRequestInterface $request)
Definition: FileDumpController.php:68
‪TYPO3\CMS\Core\Controller\FileDumpController\__construct
‪__construct(EventDispatcherInterface $eventDispatcher, ResourceFactory $resourceFactory, ResponseFactoryInterface $responseFactory)
Definition: FileDumpController.php:48
‪TYPO3\CMS\Core\Controller\FileDumpController
Definition: FileDumpController.php:43
‪TYPO3\CMS\Core\Resource\Event\ModifyFileDumpEvent
Definition: ModifyFileDumpEvent.php:35
‪TYPO3\CMS\Core\Resource\ResourceFactory
Definition: ResourceFactory.php:41
‪TYPO3\CMS\Core\Resource\File
Definition: File.php:24
‪TYPO3\CMS\Core\Imaging\ImageManipulation\CropVariantCollection\getCropArea
‪Area getCropArea(string $id='default')
Definition: CropVariantCollection.php:129
‪TYPO3\CMS\Core\Utility\PathUtility\pathinfo
‪static string string[] pathinfo($path, $options=null)
Definition: PathUtility.php:277
‪TYPO3\CMS\Core\Resource\ResourceInterface\getName
‪string getName()
‪TYPO3\CMS\Core\Resource\ProcessedFile
Definition: ProcessedFile.php:45
‪TYPO3\CMS\Core\Controller\FileDumpController\applyContentSecurityPolicy
‪applyContentSecurityPolicy(ResourceInterface $file, ResponseInterface $response)
Definition: FileDumpController.php:265
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:25
‪TYPO3\CMS\Core\Imaging\ImageManipulation\CropVariantCollection
Definition: CropVariantCollection.php:23
‪TYPO3\CMS\Core\Resource\ResourceInterface
Definition: ResourceInterface.php:22
‪TYPO3\CMS\Core\Controller\FileDumpController\$responseFactory
‪ResponseFactoryInterface $responseFactory
Definition: FileDumpController.php:46
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:50
‪TYPO3\CMS\Core\Imaging\ImageManipulation\CropVariantCollection\create
‪static CropVariantCollection create(string $jsonString, array $tcaConfig=[])
Definition: CropVariantCollection.php:42