‪TYPO3CMS  9.5
UploadedFileReferenceConverter.php
Go to the documentation of this file.
1 <?php
2 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 
22 use ‪TYPO3\CMS\Core\Resource\FileReference as CoreFileReference;
34 
42 {
43 
48 
53 
58 
63 
67  protected ‪$defaultUploadFolder = '1:/user_upload/';
68 
74  protected ‪$defaultConflictMode = 'rename';
75 
79  protected ‪$sourceTypes = ['array'];
80 
84  protected ‪$targetType = PseudoFileReference::class;
85 
91  protected ‪$priority = 12;
92 
96  protected ‪$convertedResources = [];
97 
101  protected ‪$resourceFactory;
102 
106  protected ‪$hashService;
107 
111  protected ‪$persistenceManager;
112 
118  {
119  $this->resourceFactory = ‪$resourceFactory;
120  }
121 
126  public function ‪injectHashService(\‪TYPO3\CMS\‪Extbase\Security\Cryptography\‪HashService ‪$hashService)
127  {
128  $this->hashService = ‪$hashService;
129  }
130 
135  public function ‪injectPersistenceManager(\‪TYPO3\CMS\‪Extbase\Persistence\PersistenceManagerInterface ‪$persistenceManager)
136  {
137  $this->persistenceManager = ‪$persistenceManager;
138  }
139 
151  public function ‪convertFrom($source, ‪$targetType, array $convertedChildProperties = [], PropertyMappingConfigurationInterface $configuration = null)
152  {
153  // slot/listener using `FileDumpController` instead of direct public URL in (later) rendering process
154  $resourcePublicationSlot = GeneralUtility::makeInstance(ResourcePublicationSlot::class);
155  if (!isset($source['error']) || $source['error'] === \UPLOAD_ERR_NO_FILE) {
156  if (isset($source['submittedFile']['resourcePointer'])) {
157  try {
158  // File references use numeric resource pointers, direct
159  // file relations are using "file:" prefix (e.g. "file:5")
160  $resourcePointer = $this->hashService->validateAndStripHmac($source['submittedFile']['resourcePointer']);
161  if (strpos($resourcePointer, 'file:') === 0) {
162  $fileUid = (int)substr($resourcePointer, 5);
163  $resource = $this->‪createFileReferenceFromFalFileObject($this->resourceFactory->getFileObject($fileUid));
164  } else {
166  $this->resourceFactory->getFileReferenceObject($resourcePointer),
167  (int)$resourcePointer
168  );
169  }
170  $resourcePublicationSlot->add($resource->getOriginalResource()->getOriginalFile());
171  return $resource;
172  } catch (\InvalidArgumentException $e) {
173  // Nothing to do. No file is uploaded and resource pointer is invalid. Discard!
174  }
175  }
176  return null;
177  }
178 
179  if ($source['error'] !== \UPLOAD_ERR_OK) {
180  return $this->objectManager->get(Error::class, $this->‪getUploadErrorMessage($source['error']), 1471715915);
181  }
182 
183  if (isset($this->convertedResources[$source['tmp_name']])) {
184  return $this->convertedResources[$source['tmp_name']];
185  }
186 
187  try {
188  $resource = $this->‪importUploadedResource($source, $configuration);
189  $resourcePublicationSlot->add($resource->getOriginalResource()->getOriginalFile());
190  } catch (TypeConverterException $e) {
191  return $e->getError();
192  } catch (\Exception $e) {
193  return $this->objectManager->get(Error::class, $e->getMessage(), $e->getCode());
194  }
195 
196  $this->convertedResources[$source['tmp_name']] = $resource;
197  return $resource;
198  }
199 
207  protected function ‪importUploadedResource(
208  array $uploadInfo,
211  if (!GeneralUtility::verifyFilenameAgainstDenyPattern($uploadInfo['name'])) {
212  throw new ‪TypeConverterException('Uploading files with PHP file extensions is not allowed!', 1471710357);
213  }
214  // `CONFIGURATION_UPLOAD_SEED` is expected to be defined
215  // if it's not given any random seed is generated, instead of throwing an exception
216  $seed = $configuration->‪getConfigurationValue(self::class, self::CONFIGURATION_UPLOAD_SEED)
217  ?: GeneralUtility::makeInstance(Random::class)->generateRandomHexString(40);
218  $uploadFolderId = $configuration->‪getConfigurationValue(self::class, self::CONFIGURATION_UPLOAD_FOLDER) ?: ‪$this->defaultUploadFolder;
219  $conflictMode = $configuration->‪getConfigurationValue(self::class, self::CONFIGURATION_UPLOAD_CONFLICT_MODE) ?: ‪$this->defaultConflictMode;
220  $pseudoFile = GeneralUtility::makeInstance(PseudoFile::class, $uploadInfo);
221 
222  $validators = $configuration->‪getConfigurationValue(self::class, self::CONFIGURATION_FILE_VALIDATORS);
223  if (is_array($validators)) {
224  foreach ($validators as ‪$validator) {
225  if (‪$validator instanceof AbstractValidator) {
226  $validationResult = ‪$validator->validate($pseudoFile);
227  if ($validationResult->hasErrors()) {
228  throw ‪TypeConverterException::fromError($validationResult->getErrors()[0]);
229  }
230  }
231  }
232  }
233 
234  $uploadFolder = $this->‪provideUploadFolder($uploadFolderId);
235  // current folder name, derived from public random seed (`formSession`)
236  $currentName = 'form_' . GeneralUtility::hmac($seed, self::class);
237  $uploadFolder = $this->‪provideTargetFolder($uploadFolder, $currentName);
238  // sub-folder in $uploadFolder with 160 bit of derived entropy (.../form_<40-chars-hash>/actual.file)
239  $uploadedFile = $uploadFolder->addUploadedFile($uploadInfo, $conflictMode);
240 
241  $resourcePointer = isset($uploadInfo['submittedFile']['resourcePointer']) && strpos($uploadInfo['submittedFile']['resourcePointer'], 'file:') === false
242  ? (int)$this->hashService->validateAndStripHmac($uploadInfo['submittedFile']['resourcePointer'])
243  : null;
244 
245  $fileReferenceModel = $this->‪createFileReferenceFromFalFileObject($uploadedFile, $resourcePointer);
246 
247  return $fileReferenceModel;
248  }
249 
255  protected function ‪createFileReferenceFromFalFileObject(
256  File $file,
257  int $resourcePointer = null
259  $fileReference = $this->resourceFactory->createFileReferenceObject(
260  [
261  'uid_local' => $file->getUid(),
262  'uid_foreign' => uniqid('NEW_'),
263  'uid' => uniqid('NEW_'),
264  'crop' => null,
265  ]
266  );
267  return $this->‪createFileReferenceFromFalFileReferenceObject($fileReference, $resourcePointer);
268  }
269 
280  CoreFileReference $falFileReference,
281  int $resourcePointer = null
282  ): PseudoFileReference {
283  if ($resourcePointer === null) {
284  $fileReference = $this->objectManager->get(PseudoFileReference::class);
285  } else {
286  $fileReference = $this->persistenceManager->getObjectByIdentifier($resourcePointer, PseudoFileReference::class, false);
287  }
288 
289  $fileReference->setOriginalResource($falFileReference);
290  return $fileReference;
291  }
292 
300  protected function ‪getUploadErrorMessage(int $errorCode): string
301  {
302  $logger = GeneralUtility::makeInstance(LogManager::class)->getLogger(static::class);
303  switch ($errorCode) {
304  case \UPLOAD_ERR_INI_SIZE:
305  $logger->error('The uploaded file exceeds the upload_max_filesize directive in php.ini.', []);
306  return ‪TranslationService::getInstance()->‪translate('upload.error.150530345', null, 'EXT:form/Resources/Private/Language/locallang.xlf');
307  case \UPLOAD_ERR_FORM_SIZE:
308  $logger->error('The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form.', []);
309  return ‪TranslationService::getInstance()->‪translate('upload.error.150530345', null, 'EXT:form/Resources/Private/Language/locallang.xlf');
310  case \UPLOAD_ERR_PARTIAL:
311  $logger->error('The uploaded file was only partially uploaded.', []);
312  return ‪TranslationService::getInstance()->‪translate('upload.error.150530346', null, 'EXT:form/Resources/Private/Language/locallang.xlf');
313  case \UPLOAD_ERR_NO_FILE:
314  $logger->error('No file was uploaded.', []);
315  return ‪TranslationService::getInstance()->‪translate('upload.error.150530347', null, 'EXT:form/Resources/Private/Language/locallang.xlf');
316  case \UPLOAD_ERR_NO_TMP_DIR:
317  $logger->error('Missing a temporary folder.', []);
318  return ‪TranslationService::getInstance()->‪translate('upload.error.150530348', null, 'EXT:form/Resources/Private/Language/locallang.xlf');
319  case \UPLOAD_ERR_CANT_WRITE:
320  $logger->error('Failed to write file to disk.', []);
321  return ‪TranslationService::getInstance()->‪translate('upload.error.150530348', null, 'EXT:form/Resources/Private/Language/locallang.xlf');
322  case \UPLOAD_ERR_EXTENSION:
323  $logger->error('File upload stopped by extension.', []);
324  return ‪TranslationService::getInstance()->‪translate('upload.error.150530348', null, 'EXT:form/Resources/Private/Language/locallang.xlf');
325  default:
326  $logger->error('Unknown upload error.', []);
327  return ‪TranslationService::getInstance()->‪translate('upload.error.150530348', null, 'EXT:form/Resources/Private/Language/locallang.xlf');
328  }
329  }
330 
337  protected function ‪provideUploadFolder(string $uploadFolderIdentifier): Folder
338  {
339  try {
340  return $this->resourceFactory->getFolderObjectFromCombinedIdentifier($uploadFolderIdentifier);
341  } catch (FolderDoesNotExistException $exception) {
342  [$storageId, $storagePath] = explode(':', $uploadFolderIdentifier, 2);
343  $storage = $this->resourceFactory->getStorageObject($storageId);
344  $folderNames = GeneralUtility::trimExplode('/', $storagePath, true);
345  $uploadFolder = $this->‪provideTargetFolder($storage->getRootLevelFolder(), ...$folderNames);
346  $this->‪provideFolderInitialization($uploadFolder);
347  return $uploadFolder;
348  }
349  }
350 
358  protected function ‪provideTargetFolder(‪Folder $parentFolder, string $folderName): ‪Folder
359  {
360  return $parentFolder->‪hasFolder($folderName)
361  ? $parentFolder->‪getSubfolder($folderName)
362  : $parentFolder->‪createFolder($folderName);
363  }
364 
371  protected function ‪provideFolderInitialization(‪Folder $parentFolder): void
372  {
373  if (!$parentFolder->‪hasFile('index.html')) {
374  $parentFolder->‪createFile('index.html');
375  }
376  }
377 }
‪TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface\getConfigurationValue
‪mixed getConfigurationValue($typeConverterClassName, $key)
‪TYPO3\CMS\Extbase\Validation\Validator\AbstractValidator
Definition: AbstractValidator.php:23
‪TYPO3\CMS\Form\Mvc\Property\TypeConverter\UploadedFileReferenceConverter
Definition: UploadedFileReferenceConverter.php:42
‪TYPO3\CMS\Form\Mvc\Property\Exception\TypeConverterException\getError
‪getError()
Definition: TypeConverterException.php:36
‪TYPO3\CMS\Form\Service\TranslationService\getInstance
‪static TranslationService getInstance()
Definition: TranslationService.php:82
‪TYPO3\CMS\Form\Mvc\Property\TypeConverter\UploadedFileReferenceConverter\$targetType
‪string $targetType
Definition: UploadedFileReferenceConverter.php:80
‪TYPO3\CMS\Form\Mvc\Property\TypeConverter\PseudoFileReference
Definition: PseudoFileReference.php:34
‪TYPO3\CMS\Form\Mvc\Property\Exception\TypeConverterException\fromError
‪static fromError(Error $error)
Definition: TypeConverterException.php:28
‪TYPO3\CMS\Extbase\Annotation
Definition: IgnoreValidation.php:4
‪TYPO3\CMS\Form\Mvc\Property\TypeConverter\UploadedFileReferenceConverter\$priority
‪int $priority
Definition: UploadedFileReferenceConverter.php:86
‪TYPO3\CMS\Form\Service\TranslationService
Definition: TranslationService.php:43
‪TYPO3\CMS\Form\Mvc\Property\TypeConverter\UploadedFileReferenceConverter\injectPersistenceManager
‪injectPersistenceManager(\TYPO3\CMS\Extbase\Persistence\PersistenceManagerInterface $persistenceManager)
Definition: UploadedFileReferenceConverter.php:126
‪TYPO3\CMS\Core\Resource\Folder\hasFile
‪bool hasFile($name)
Definition: Folder.php:398
‪TYPO3\CMS\Form\Mvc\Property\TypeConverter\UploadedFileReferenceConverter\injectResourceFactory
‪injectResourceFactory(ResourceFactory $resourceFactory)
Definition: UploadedFileReferenceConverter.php:108
‪TYPO3
‪TYPO3\CMS\Core\Resource\Folder\createFile
‪File createFile($fileName)
Definition: Folder.php:350
‪TYPO3\CMS\Core\Resource\FileReference
Definition: FileReference.php:31
‪TYPO3\CMS\Form\Slot\ResourcePublicationSlot
Definition: ResourcePublicationSlot.php:35
‪TYPO3\CMS\Form\Mvc\Property\TypeConverter
Definition: FormDefinitionArrayConverter.php:3
‪TYPO3\CMS\Form\Mvc\Property\TypeConverter\UploadedFileReferenceConverter\$convertedResources
‪PseudoFileReference[] $convertedResources
Definition: UploadedFileReferenceConverter.php:90
‪TYPO3\CMS\Extbase\Security\Cryptography\HashService
Definition: HashService.php:24
‪TYPO3\CMS\Form\Mvc\Property\TypeConverter\UploadedFileReferenceConverter\importUploadedResource
‪PseudoFileReference importUploadedResource(array $uploadInfo, PropertyMappingConfigurationInterface $configuration)
Definition: UploadedFileReferenceConverter.php:198
‪TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface
Definition: PropertyMappingConfigurationInterface.php:21
‪TYPO3\CMS\Form\Mvc\Property\TypeConverter\UploadedFileReferenceConverter\CONFIGURATION_UPLOAD_FOLDER
‪const CONFIGURATION_UPLOAD_FOLDER
Definition: UploadedFileReferenceConverter.php:47
‪TYPO3\CMS\Form\Mvc\Property\TypeConverter\UploadedFileReferenceConverter\CONFIGURATION_UPLOAD_CONFLICT_MODE
‪const CONFIGURATION_UPLOAD_CONFLICT_MODE
Definition: UploadedFileReferenceConverter.php:52
‪TYPO3\CMS\Form\Exception
Definition: Exception.php:24
‪TYPO3\CMS\Form\Mvc\Property\TypeConverter\UploadedFileReferenceConverter\$defaultConflictMode
‪string $defaultConflictMode
Definition: UploadedFileReferenceConverter.php:72
‪TYPO3\CMS\Extbase\Domain\Model\AbstractFileFolder
Definition: AbstractFileFolder.php:23
‪TYPO3\CMS\Extbase\Error\Error
Definition: Error.php:22
‪TYPO3\CMS\Form\Mvc\Property\TypeConverter\UploadedFileReferenceConverter\$persistenceManager
‪TYPO3 CMS Extbase Persistence PersistenceManagerInterface $persistenceManager
Definition: UploadedFileReferenceConverter.php:102
‪TYPO3\CMS\Form\Mvc\Property\Exception\TypeConverterException
Definition: TypeConverterException.php:23
‪TYPO3\CMS\Core\Resource\Folder\createFolder
‪Folder createFolder($folderName)
Definition: Folder.php:361
‪$validator
‪if(isset($args['d'])) $validator
Definition: validateRstFiles.php:218
‪TYPO3\CMS\Core\Resource\Folder
Definition: Folder.php:34
‪TYPO3\CMS\Core\Resource\ResourceFactory
Definition: ResourceFactory.php:33
‪TYPO3\CMS\Core\Resource\Exception\FolderDoesNotExistException
Definition: FolderDoesNotExistException.php:21
‪TYPO3\CMS\Form\Mvc\Property\TypeConverter\UploadedFileReferenceConverter\convertFrom
‪AbstractFileFolder Error null convertFrom($source, $targetType, array $convertedChildProperties=[], PropertyMappingConfigurationInterface $configuration=null)
Definition: UploadedFileReferenceConverter.php:142
‪TYPO3\CMS\Form\Mvc\Property\TypeConverter\UploadedFileReferenceConverter\CONFIGURATION_UPLOAD_SEED
‪const CONFIGURATION_UPLOAD_SEED
Definition: UploadedFileReferenceConverter.php:57
‪TYPO3\CMS\Core\Resource\File
Definition: File.php:23
‪TYPO3\CMS\Form\Mvc\Property\TypeConverter\UploadedFileReferenceConverter\createFileReferenceFromFalFileObject
‪PseudoFileReference createFileReferenceFromFalFileObject(File $file, int $resourcePointer=null)
Definition: UploadedFileReferenceConverter.php:246
‪TYPO3\CMS\Form\Mvc\Property\TypeConverter\UploadedFileReferenceConverter\$resourceFactory
‪ResourceFactory $resourceFactory
Definition: UploadedFileReferenceConverter.php:94
‪TYPO3\CMS\Form\Service\TranslationService\translate
‪mixed translate( $key, array $arguments=null, string $locallangPathAndFilename=null, string $language=null, $defaultValue='')
Definition: TranslationService.php:98
‪TYPO3\CMS\Core\Resource\Folder\getSubfolder
‪Folder getSubfolder($name)
Definition: Folder.php:270
‪TYPO3\CMS\Form\Mvc\Property\TypeConverter\UploadedFileReferenceConverter\provideTargetFolder
‪Folder provideTargetFolder(Folder $parentFolder, string $folderName)
Definition: UploadedFileReferenceConverter.php:349
‪TYPO3\CMS\Extbase\Property\TypeConverter\AbstractTypeConverter
Definition: AbstractTypeConverter.php:26
‪TYPO3\CMS\Form\Mvc\Property\TypeConverter\UploadedFileReferenceConverter\provideFolderInitialization
‪provideFolderInitialization(Folder $parentFolder)
Definition: UploadedFileReferenceConverter.php:362
‪TYPO3\CMS\Form\Mvc\Property\TypeConverter\UploadedFileReferenceConverter\$defaultUploadFolder
‪string $defaultUploadFolder
Definition: UploadedFileReferenceConverter.php:66
‪TYPO3\CMS\Form\Mvc\Property\TypeConverter\UploadedFileReferenceConverter\$sourceTypes
‪array $sourceTypes
Definition: UploadedFileReferenceConverter.php:76
‪TYPO3\CMS\Core\Log\LogManager
Definition: LogManager.php:25
‪TYPO3\CMS\Form\Mvc\Property\TypeConverter\UploadedFileReferenceConverter\CONFIGURATION_FILE_VALIDATORS
‪const CONFIGURATION_FILE_VALIDATORS
Definition: UploadedFileReferenceConverter.php:62
‪TYPO3\CMS\Form\Mvc\Property\TypeConverter\UploadedFileReferenceConverter\$hashService
‪TYPO3 CMS Extbase Security Cryptography HashService $hashService
Definition: UploadedFileReferenceConverter.php:98
‪TYPO3\CMS\Form\Mvc\Property\TypeConverter\UploadedFileReferenceConverter\provideUploadFolder
‪Folder provideUploadFolder(string $uploadFolderIdentifier)
Definition: UploadedFileReferenceConverter.php:328
‪TYPO3\CMS\Core\Crypto\Random
Definition: Random.php:22
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:45
‪TYPO3\CMS\Form\Mvc\Property\TypeConverter\UploadedFileReferenceConverter\injectHashService
‪injectHashService(\TYPO3\CMS\Extbase\Security\Cryptography\HashService $hashService)
Definition: UploadedFileReferenceConverter.php:117
‪TYPO3\CMS\Extbase\Domain\Model\FileReference\setOriginalResource
‪setOriginalResource(\TYPO3\CMS\Core\Resource\ResourceInterface $originalResource)
Definition: FileReference.php:48
‪TYPO3\CMS\Form\Mvc\Property\TypeConverter\UploadedFileReferenceConverter\createFileReferenceFromFalFileReferenceObject
‪PseudoFileReference createFileReferenceFromFalFileReferenceObject(CoreFileReference $falFileReference, int $resourcePointer=null)
Definition: UploadedFileReferenceConverter.php:270
‪TYPO3\CMS\Form\Mvc\Property\TypeConverter\UploadedFileReferenceConverter\getUploadErrorMessage
‪string getUploadErrorMessage(int $errorCode)
Definition: UploadedFileReferenceConverter.php:291
‪TYPO3\CMS\Core\Resource\Folder\hasFolder
‪bool hasFolder($name)
Definition: Folder.php:409