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