‪TYPO3CMS  ‪main
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;
36 
41 {
43  protected EventDispatcherInterface ‪$eventDispatcher;
44  protected ResponseFactoryInterface ‪$responseFactory;
45 
46  public function ‪__construct(
47  EventDispatcherInterface ‪$eventDispatcher,
49  ResponseFactoryInterface ‪$responseFactory
50  ) {
51  $this->eventDispatcher = ‪$eventDispatcher;
52  $this->resourceFactory = ‪$resourceFactory;
53  $this->responseFactory = ‪$responseFactory;
54  }
55 
63  public function ‪dumpAction(ServerRequestInterface $request): ResponseInterface
64  {
65  $parameters = $this->‪buildParametersFromRequest($request);
66 
67  if (!$this->‪isTokenValid($parameters, $request)) {
68  return $this->responseFactory->createResponse(403);
69  }
70  $file = $this->‪createFileObjectByParameters($parameters);
71  if ($file === null) {
72  return $this->responseFactory->createResponse(404);
73  }
74 
75  // Allow some other process to do some security/access checks.
76  // Event Listeners should return a 403 response if access is rejected
77  $event = new ‪ModifyFileDumpEvent($file, $request);
78  $event = $this->eventDispatcher->dispatch($event);
79  if ($event->isPropagationStopped()) {
80  return $this->‪applyContentSecurityPolicy($event->getFile(), $event->getResponse());
81  }
82  $file = $event->getFile();
83 
84  $processingInstructions = [];
85 
86  // Apply cropping, if possible
87  if (!empty($parameters['cv'])) {
88  $cropVariant = $parameters['cv'];
89  $cropString = $file instanceof ‪FileReference ? $file->‪getProperty('crop') : '';
90  $cropArea = ‪CropVariantCollection::create((string)$cropString)->getCropArea($cropVariant);
91  $processingInstructions = array_merge(
92  $processingInstructions,
93  [
94  'crop' => $cropArea->isEmpty() ? null : $cropArea->makeAbsoluteBasedOnFile($file),
95  ]
96  );
97  }
98 
99  // Apply width/height, if given
100  if (!empty($parameters['s'])) {
101  $size = ‪GeneralUtility::trimExplode(':', $parameters['s']);
102  $processingInstructions = array_merge(
103  $processingInstructions,
104  [
105  'width' => $size[0] ?? null,
106  'height' => $size[1] ?? null,
107  'minWidth' => $size[2] ? (int)$size[2] : null,
108  'minHeight' => $size[3] ? (int)$size[3] : null,
109  'maxWidth' => $size[4] ? (int)$size[4] : null,
110  'maxHeight' => $size[5] ? (int)$size[5] : null,
111  ]
112  );
113  }
114 
115  if (!empty($processingInstructions) && !($file instanceof ‪ProcessedFile)) {
116  if (is_callable([$file, 'getOriginalFile'])) {
117  // Get the original file from the file reference
118  $file = $file->getOriginalFile();
119  }
120  $file = $file->process(‪ProcessedFile::CONTEXT_IMAGECROPSCALEMASK, $processingInstructions);
121  }
122 
123  return $this->‪applyContentSecurityPolicy(
124  $file,
125  $file->getStorage()->streamFile(
126  $file,
127  (bool)($parameters['dl'] ?? false),
128  $parameters['fn'] ?? null
129  )
130  );
131  }
132 
133  protected function ‪buildParametersFromRequest(ServerRequestInterface $request): array
134  {
135  $parameters = ['eID' => 'dumpFile'];
136  $queryParams = $request->getQueryParams();
137  // Identifier of what to process. f, r or p
138  // Only needed while hash_equals
139  $t = (string)($queryParams['t'] ?? '');
140  if ($t) {
141  $parameters['t'] = $t;
142  }
143  // sys_file
144  $f = (string)($queryParams['f'] ?? '');
145  if ($f) {
146  $parameters['f'] = (int)$f;
147  }
148  // sys_file_reference
149  $r = (string)($queryParams['r'] ?? '');
150  if ($r) {
151  $parameters['r'] = (int)$r;
152  }
153  // Processed file
154  $p = (string)($queryParams['p'] ?? '');
155  if ($p) {
156  $parameters['p'] = (int)$p;
157  }
158  // File's width and height in this order: w:h:minW:minH:maxW:maxH
159  $s = (string)($queryParams['s'] ?? '');
160  if ($s) {
161  $parameters['s'] = $s;
162  }
163  // File's crop variant
164  $cv = (string)($queryParams['cv'] ?? '');
165  if ($cv) {
166  $parameters['cv'] = $cv;
167  }
168  // As download
169  $dl = (string)($queryParams['dl'] ?? '');
170  if ($dl) {
171  $parameters['dl'] = (int)$dl;
172  }
173  // Alternative file name
174  $fn = (string)($queryParams['fn'] ?? '');
175  if ($fn) {
176  $parameters['fn'] = $fn;
177  }
178 
179  return $parameters;
180  }
181 
182  protected function ‪isTokenValid(array $parameters, ServerRequestInterface $request): bool
183  {
184  return hash_equals(
185  ‪GeneralUtility::hmac(implode('|', $parameters), 'resourceStorageDumpFile'),
186  $request->getQueryParams()['token'] ?? ''
187  );
188  }
189 
193  protected function ‪createFileObjectByParameters(array $parameters)
194  {
195  $file = null;
196  if (isset($parameters['f'])) {
197  try {
198  $file = $this->resourceFactory->getFileObject($parameters['f']);
199  if ($file->isDeleted() || $file->isMissing() || !$this->isFileValid($file)) {
200  $file = null;
201  }
202  } catch (\‪Exception $e) {
203  $file = null;
204  }
205  } elseif (isset($parameters['r'])) {
206  try {
207  $file = $this->resourceFactory->getFileReferenceObject($parameters['r']);
208  if ($file->isMissing() || !$this->isFileValid($file->getOriginalFile())) {
209  $file = null;
210  }
211  } catch (\‪Exception $e) {
212  $file = null;
213  }
214  } elseif (isset($parameters['p'])) {
215  try {
216  $processedFileRepository = GeneralUtility::makeInstance(ProcessedFileRepository::class);
217  $file = $processedFileRepository->findByUid((int)$parameters['p']);
218  if ($file->isDeleted() || !$this->isFileValid($file->getOriginalFile())) {
219  $file = null;
220  }
221  } catch (\‪Exception) {
222  $file = null;
223  }
224  }
225  return $file;
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 
238  protected function ‪applyContentSecurityPolicy(‪ResourceInterface $file, ResponseInterface $response): ResponseInterface
239  {
240  $extension = ‪PathUtility::pathinfo($file->‪getName(), PATHINFO_EXTENSION);
241  // same as in `typo3/sysext/install/Resources/Private/FolderStructureTemplateFiles/resources-root-htaccess`
242  if ($extension === 'pdf' || $response->getHeaderLine('content-type') === 'application/pdf') {
243  $policy = "default-src 'self' 'unsafe-inline'; script-src 'none'; object-src 'self'; plugin-types application/pdf;";
244  } elseif ($extension === 'svg' || $response->getHeaderLine('content-type') === 'image/svg+xml') {
245  $policy = "default-src 'self'; script-src 'none'; style-src 'unsafe-inline'; object-src 'none';";
246  } else {
247  $policy = "default-src 'self'; script-src 'none'; style-src 'none'; object-src 'none';";
248  }
249  return $response->withAddedHeader('content-security-policy', $policy);
250  }
251 }
‪TYPO3\CMS\Core\Resource\FileReference\getProperty
‪getProperty(string $key)
Definition: FileReference.php:108
‪TYPO3\CMS\Core\Resource\ProcessedFileRepository
Definition: ProcessedFileRepository.php:39
‪TYPO3\CMS\Core\Resource\ResourceInterface\getIdentifier
‪getIdentifier()
‪TYPO3\CMS\Core\Utility\PathUtility
Definition: PathUtility.php:27
‪TYPO3\CMS\Core\Controller\FileDumpController\$eventDispatcher
‪EventDispatcherInterface $eventDispatcher
Definition: FileDumpController.php:43
‪TYPO3\CMS\Core\Controller\FileDumpController\createFileObjectByParameters
‪File FileReference ProcessedFile null createFileObjectByParameters(array $parameters)
Definition: FileDumpController.php:193
‪TYPO3\CMS\Core\Imaging\ImageManipulation\CropVariantCollection\create
‪static create(string $jsonString, array $tcaConfig=[])
Definition: CropVariantCollection.php:37
‪TYPO3\CMS\Core\Resource\FileInterface
Definition: FileInterface.php:26
‪TYPO3\CMS\Core\Resource\ResourceInterface\getName
‪getName()
‪TYPO3\CMS\Core\Controller\FileDumpController\$resourceFactory
‪ResourceFactory $resourceFactory
Definition: FileDumpController.php:42
‪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:228
‪TYPO3\CMS\Core\Controller\FileDumpController\isTokenValid
‪isTokenValid(array $parameters, ServerRequestInterface $request)
Definition: FileDumpController.php:182
‪TYPO3\CMS\Core\Resource\ProcessedFile\CONTEXT_IMAGECROPSCALEMASK
‪const CONTEXT_IMAGECROPSCALEMASK
Definition: ProcessedFile.php:61
‪TYPO3\CMS\Core\Controller\FileDumpController\buildParametersFromRequest
‪buildParametersFromRequest(ServerRequestInterface $request)
Definition: FileDumpController.php:133
‪TYPO3\CMS\Core\Resource\ResourceInterface\getStorage
‪getStorage()
‪TYPO3\CMS\Core\Resource\FileReference
Definition: FileReference.php:37
‪TYPO3\CMS\Core\Controller
Definition: ErrorPageController.php:18
‪TYPO3\CMS\Core\Controller\FileDumpController\dumpAction
‪dumpAction(ServerRequestInterface $request)
Definition: FileDumpController.php:63
‪TYPO3\CMS\Core\Controller\FileDumpController\__construct
‪__construct(EventDispatcherInterface $eventDispatcher, ResourceFactory $resourceFactory, ResponseFactoryInterface $responseFactory)
Definition: FileDumpController.php:46
‪TYPO3\CMS\Core\Controller\FileDumpController
Definition: FileDumpController.php:41
‪TYPO3\CMS\Core\Resource\Event\ModifyFileDumpEvent
Definition: ModifyFileDumpEvent.php:35
‪TYPO3\CMS\Core\Resource\ResourceFactory
Definition: ResourceFactory.php:42
‪TYPO3\CMS\Core\Resource\File
Definition: File.php:26
‪TYPO3\CMS\Core\Utility\GeneralUtility\hmac
‪static string hmac($input, $additionalSecret='')
Definition: GeneralUtility.php:474
‪TYPO3\CMS\Core\Resource\ProcessedFile
Definition: ProcessedFile.php:47
‪TYPO3\CMS\Core\Controller\FileDumpController\applyContentSecurityPolicy
‪applyContentSecurityPolicy(ResourceInterface $file, ResponseInterface $response)
Definition: FileDumpController.php:238
‪TYPO3\CMS\Core\Imaging\ImageManipulation\CropVariantCollection
Definition: CropVariantCollection.php:23
‪TYPO3\CMS\Core\Resource\ResourceInterface
Definition: ResourceInterface.php:21
‪TYPO3\CMS\Core\Controller\FileDumpController\$responseFactory
‪ResponseFactoryInterface $responseFactory
Definition: FileDumpController.php:44
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:52
‪TYPO3\CMS\Core\Utility\PathUtility\pathinfo
‪static string string[] pathinfo(string $path, int $options=PATHINFO_ALL)
Definition: PathUtility.php:270
‪TYPO3\CMS\Core\Utility\GeneralUtility\trimExplode
‪static list< string > trimExplode(string $delim, string $string, bool $removeEmptyValues=false, int $limit=0)
Definition: GeneralUtility.php:817