TYPO3  7.6
ResourceStorage.php
Go to the documentation of this file.
1 <?php
2 namespace TYPO3\CMS\Core\Resource;
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 
18 use TYPO3\CMS\Core\Resource\Exception\InvalidTargetFolderException;
23 
57 {
63  protected $driver;
64 
70  protected $storageRecord;
71 
77  protected $configuration;
78 
83 
92  protected $evaluatePermissions = false;
93 
99  protected $fileMounts = array();
100 
107  protected $userPermissions = array();
108 
115  protected $capabilities;
116 
121 
125  protected $processingFolder;
126 
133 
139  protected $isOnline = null;
140 
144  protected $isDefault = false;
145 
151  protected $fileAndFolderNameFilters = array();
152 
159  public function __construct(Driver\DriverInterface $driver, array $storageRecord)
160  {
161  $this->storageRecord = $storageRecord;
162  $this->configuration = ResourceFactory::getInstance()->convertFlexFormDataToConfigurationArray($storageRecord['configuration']);
163  $this->capabilities =
164  ($this->storageRecord['is_browsable'] ? self::CAPABILITY_BROWSABLE : 0) |
165  ($this->storageRecord['is_public'] ? self::CAPABILITY_PUBLIC : 0) |
166  ($this->storageRecord['is_writable'] ? self::CAPABILITY_WRITABLE : 0);
167 
168  $this->driver = $driver;
169  $this->driver->setStorageUid($storageRecord['uid']);
170  $this->driver->mergeConfigurationCapabilities($this->capabilities);
171  try {
172  $this->driver->processConfiguration();
173  } catch (Exception\InvalidConfigurationException $e) {
174  // configuration error
175  // mark this storage as permanently unusable
176  $this->markAsPermanentlyOffline();
177  }
178  $this->driver->initialize();
179  $this->capabilities = $this->driver->getCapabilities();
180 
181  $this->isDefault = (isset($storageRecord['is_default']) && $storageRecord['is_default'] == 1);
183  }
184 
190  public function getConfiguration()
191  {
192  return $this->configuration;
193  }
194 
200  public function setConfiguration(array $configuration)
201  {
202  $this->configuration = $configuration;
203  }
204 
210  public function getStorageRecord()
211  {
212  return $this->storageRecord;
213  }
214 
221  public function setDriver(Driver\DriverInterface $driver)
222  {
223  $this->driver = $driver;
224  return $this;
225  }
226 
232  protected function getDriver()
233  {
234  return $this->driver;
235  }
236 
242  public function getName()
243  {
244  return $this->storageRecord['name'];
245  }
246 
252  public function getUid()
253  {
254  return (int)$this->storageRecord['uid'];
255  }
256 
262  public function hasChildren()
263  {
264  return true;
265  }
266 
267  /*********************************
268  * Capabilities
269  ********************************/
276  public function getCapabilities()
277  {
278  return (int)$this->capabilities;
279  }
280 
287  protected function hasCapability($capability)
288  {
289  return ($this->capabilities & $capability) == $capability;
290  }
291 
300  public function isPublic()
301  {
302  return $this->hasCapability(self::CAPABILITY_PUBLIC);
303  }
304 
311  public function isWritable()
312  {
313  return $this->hasCapability(self::CAPABILITY_WRITABLE);
314  }
315 
321  public function isBrowsable()
322  {
323  return $this->isOnline() && $this->hasCapability(self::CAPABILITY_BROWSABLE);
324  }
325 
332  {
333  return $this->driver->isCaseSensitiveFileSystem();
334  }
335 
341  public function isOnline()
342  {
343  if ($this->isOnline === null) {
344  if ($this->getUid() === 0) {
345  $this->isOnline = true;
346  }
347  // the storage is not marked as online for a longer time
348  if ($this->storageRecord['is_online'] == 0) {
349  $this->isOnline = false;
350  }
351  if ($this->isOnline !== false) {
352  // all files are ALWAYS available in the frontend
353  if (TYPO3_MODE === 'FE') {
354  $this->isOnline = true;
355  } else {
356  // check if the storage is disabled temporary for now
357  $registryObject = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Registry::class);
358  $offlineUntil = $registryObject->get('core', 'sys_file_storage-' . $this->getUid() . '-offline-until');
359  if ($offlineUntil && $offlineUntil > time()) {
360  $this->isOnline = false;
361  } else {
362  $this->isOnline = true;
363  }
364  }
365  }
366  }
367  return $this->isOnline;
368  }
369 
375  public function autoExtractMetadataEnabled()
376  {
377  return !empty($this->storageRecord['auto_extract_metadata']);
378  }
379 
389  public function markAsPermanentlyOffline()
390  {
391  if ($this->getUid() > 0) {
392  // @todo: move this to the storage repository
393  $GLOBALS['TYPO3_DB']->exec_UPDATEquery('sys_file_storage', 'uid=' . (int)$this->getUid(), array('is_online' => 0));
394  }
395  $this->storageRecord['is_online'] = 0;
396  $this->isOnline = false;
397  }
398 
407  public function markAsTemporaryOffline()
408  {
409  $registryObject = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Registry::class);
410  $registryObject->set('core', 'sys_file_storage-' . $this->getUid() . '-offline-until', time() + 60 * 5);
411  $this->storageRecord['is_online'] = 0;
412  $this->isOnline = false;
413  }
414 
415  /*********************************
416  * User Permissions / File Mounts
417  ********************************/
428  public function addFileMount($folderIdentifier, $additionalData = array())
429  {
430  // check for the folder before we add it as a filemount
431  if ($this->driver->folderExists($folderIdentifier) === false) {
432  // if there is an error, this is important and should be handled
433  // as otherwise the user would see the whole storage without any restrictions for the filemounts
434  throw new Exception\FolderDoesNotExistException('Folder for file mount ' . $folderIdentifier . ' does not exist.', 1334427099);
435  }
436  $data = $this->driver->getFolderInfoByIdentifier($folderIdentifier);
437  $folderObject = ResourceFactory::getInstance()->createFolderObject($this, $data['identifier'], $data['name']);
438  // Use the canonical identifier instead of the user provided one!
439  $folderIdentifier = $folderObject->getIdentifier();
440  if (
441  !empty($this->fileMounts[$folderIdentifier])
442  && empty($this->fileMounts[$folderIdentifier]['read_only'])
443  && !empty($additionalData['read_only'])
444  ) {
445  // Do not overwrite a regular mount with a read only mount
446  return;
447  }
448  if (empty($additionalData)) {
449  $additionalData = array(
450  'path' => $folderIdentifier,
451  'title' => $folderIdentifier,
452  'folder' => $folderObject
453  );
454  } else {
455  $additionalData['folder'] = $folderObject;
456  if (!isset($additionalData['title'])) {
457  $additionalData['title'] = $folderIdentifier;
458  }
459  }
460  $this->fileMounts[$folderIdentifier] = $additionalData;
461  }
462 
468  public function getFileMounts()
469  {
470  return $this->fileMounts;
471  }
472 
481  public function isWithinFileMountBoundaries($subject, $checkWriteAccess = false)
482  {
483  if (!$this->evaluatePermissions) {
484  return true;
485  }
486  $isWithinFileMount = false;
487  if (!$subject) {
488  $subject = $this->getRootLevelFolder();
489  }
490  $identifier = $subject->getIdentifier();
491 
492  // Allow access to processing folder
493  if ($this->isWithinProcessingFolder($identifier)) {
494  $isWithinFileMount = true;
495  } else {
496  // Check if the identifier of the subject is within at
497  // least one of the file mounts
498  $writableFileMountAvailable = false;
499  foreach ($this->fileMounts as $fileMount) {
500  if ($this->driver->isWithin($fileMount['folder']->getIdentifier(), $identifier)) {
501  $isWithinFileMount = true;
502  if (!$checkWriteAccess) {
503  break;
504  } elseif (empty($fileMount['read_only'])) {
505  $writableFileMountAvailable = true;
506  break;
507  }
508  }
509  }
510  $isWithinFileMount = $checkWriteAccess ? $writableFileMountAvailable : $isWithinFileMount;
511  }
512  return $isWithinFileMount;
513  }
514 
522  {
523  $this->evaluatePermissions = (bool)$evaluatePermissions;
524  }
525 
532  public function getEvaluatePermissions()
533  {
535  }
536 
543  public function setUserPermissions(array $userPermissions)
544  {
545  $this->userPermissions = $userPermissions;
546  }
547 
556  public function checkUserActionPermission($action, $type)
557  {
558  if (!$this->evaluatePermissions) {
559  return true;
560  }
561 
562  $allow = false;
563  if (!empty($this->userPermissions[strtolower($action) . ucfirst(strtolower($type))])) {
564  $allow = true;
565  }
566 
567  return $allow;
568  }
569 
583  public function checkFileActionPermission($action, FileInterface $file)
584  {
585  $isProcessedFile = $file instanceof ProcessedFile;
586  // Check 1: Does the user have permission to perform the action? e.g. "readFile"
587  if (!$isProcessedFile && $this->checkUserActionPermission($action, 'File') === false) {
588  return false;
589  }
590  // Check 2: No action allowed on files for denied file extensions
591  if (!$this->checkFileExtensionPermission($file->getName())) {
592  return false;
593  }
594  $isReadCheck = false;
595  if (in_array($action, array('read', 'copy', 'move', 'replace'), true)) {
596  $isReadCheck = true;
597  }
598  $isWriteCheck = false;
599  if (in_array($action, array('add', 'write', 'move', 'rename', 'replace', 'unzip', 'delete'), true)) {
600  $isWriteCheck = true;
601  }
602  // Check 3: Does the user have the right to perform the action?
603  // (= is he within the file mount borders)
604  if (!$isProcessedFile && !$this->isWithinFileMountBoundaries($file, $isWriteCheck)) {
605  return false;
606  }
607 
608  $isMissing = false;
609  if (!$isProcessedFile && $file instanceof File) {
610  $isMissing = $file->isMissing();
611  }
612 
613  if ($this->driver->fileExists($file->getIdentifier()) === false) {
614  $file->setMissing(true);
615  $isMissing = true;
616  }
617 
618  // Check 4: Check the capabilities of the storage (and the driver)
619  if ($isWriteCheck && ($isMissing || !$this->isWritable())) {
620  return false;
621  }
622 
623  // Check 5: "File permissions" of the driver (only when file isn't marked as missing)
624  if (!$isMissing) {
625  $filePermissions = $this->driver->getPermissions($file->getIdentifier());
626  if ($isReadCheck && !$filePermissions['r']) {
627  return false;
628  }
629  if ($isWriteCheck && !$filePermissions['w']) {
630  return false;
631  }
632  }
633  return true;
634  }
635 
646  public function checkFolderActionPermission($action, Folder $folder = null)
647  {
648  // Check 1: Does the user have permission to perform the action? e.g. "writeFolder"
649  if ($this->checkUserActionPermission($action, 'Folder') === false) {
650  return false;
651  }
652 
653  // If we do not have a folder here, we cannot do further checks
654  if ($folder === null) {
655  return true;
656  }
657 
658  $isReadCheck = false;
659  if (in_array($action, array('read', 'copy'), true)) {
660  $isReadCheck = true;
661  }
662  $isWriteCheck = false;
663  if (in_array($action, array('add', 'move', 'write', 'delete', 'rename'), true)) {
664  $isWriteCheck = true;
665  }
666  // Check 2: Does the user has the right to perform the action?
667  // (= is he within the file mount borders)
668  if (!$this->isWithinFileMountBoundaries($folder, $isWriteCheck)) {
669  return false;
670  }
671  // Check 3: Check the capabilities of the storage (and the driver)
672  if ($isReadCheck && !$this->isBrowsable()) {
673  return false;
674  }
675  if ($isWriteCheck && !$this->isWritable()) {
676  return false;
677  }
678 
679  // Check 4: "Folder permissions" of the driver
680  $folderPermissions = $this->driver->getPermissions($folder->getIdentifier());
681  if ($isReadCheck && !$folderPermissions['r']) {
682  return false;
683  }
684  if ($isWriteCheck && !$folderPermissions['w']) {
685  return false;
686  }
687  return true;
688  }
689 
697  protected function checkFileExtensionPermission($fileName)
698  {
699  if (!$this->evaluatePermissions) {
700  return true;
701  }
702  $fileName = $this->driver->sanitizeFileName($fileName);
703  $isAllowed = GeneralUtility::verifyFilenameAgainstDenyPattern($fileName);
704  if ($isAllowed) {
705  $fileExtension = strtolower(PathUtility::pathinfo($fileName, PATHINFO_EXTENSION));
706  // Set up the permissions for the file extension
707  $fileExtensionPermissions = $GLOBALS['TYPO3_CONF_VARS']['BE']['fileExtensions']['webspace'];
708  $fileExtensionPermissions['allow'] = GeneralUtility::uniqueList(strtolower($fileExtensionPermissions['allow']));
709  $fileExtensionPermissions['deny'] = GeneralUtility::uniqueList(strtolower($fileExtensionPermissions['deny']));
710  if ($fileExtension !== '') {
711  // If the extension is found amongst the allowed types, we return TRUE immediately
712  if ($fileExtensionPermissions['allow'] === '*' || GeneralUtility::inList($fileExtensionPermissions['allow'], $fileExtension)) {
713  return true;
714  }
715  // If the extension is found amongst the denied types, we return FALSE immediately
716  if ($fileExtensionPermissions['deny'] === '*' || GeneralUtility::inList($fileExtensionPermissions['deny'], $fileExtension)) {
717  return false;
718  }
719  // If no match we return TRUE
720  return true;
721  } else {
722  if ($fileExtensionPermissions['allow'] === '*') {
723  return true;
724  }
725  if ($fileExtensionPermissions['deny'] === '*') {
726  return false;
727  }
728  return true;
729  }
730  }
731  return false;
732  }
733 
741  protected function assureFolderReadPermission(Folder $folder = null)
742  {
743  if (!$this->checkFolderActionPermission('read', $folder)) {
744  if ($folder === null) {
745  throw new Exception\InsufficientFolderAccessPermissionsException(
746  'You are not allowed to read folders',
747  1430657869
748  );
749  } else {
750  throw new Exception\InsufficientFolderAccessPermissionsException(
751  'You are not allowed to access the given folder: "' . $folder->getName() . '"',
752  1375955684
753  );
754  }
755  }
756  }
757 
768  protected function assureFolderDeletePermission(Folder $folder, $checkDeleteRecursively)
769  {
770  // Check user permissions for recursive deletion if it is requested
771  if ($checkDeleteRecursively && !$this->checkUserActionPermission('recursivedelete', 'Folder')) {
772  throw new Exception\InsufficientUserPermissionsException('You are not allowed to delete folders recursively', 1377779423);
773  }
774  // Check user action permission
775  if (!$this->checkFolderActionPermission('delete', $folder)) {
776  throw new Exception\InsufficientFolderAccessPermissionsException(
777  'You are not allowed to delete the given folder: "' . $folder->getName() . '"',
778  1377779039
779  );
780  }
781  // Check if the user has write permissions to folders
782  // Would be good if we could check for actual write permissions in the containig folder
783  // but we cannot since we have no access to the containing folder of this file.
784  if (!$this->checkUserActionPermission('write', 'Folder')) {
785  throw new Exception\InsufficientFolderWritePermissionsException('Writing to folders is not allowed.', 1377779111);
786  }
787  }
788 
797  protected function assureFileReadPermission(FileInterface $file)
798  {
799  if (!$this->checkFileActionPermission('read', $file)) {
800  throw new Exception\InsufficientFileAccessPermissionsException(
801  'You are not allowed to access that file: "' . $file->getName() . '"',
802  1375955429
803  );
804  }
805  if (!$this->checkFileExtensionPermission($file->getName())) {
806  throw new Exception\IllegalFileExtensionException(
807  'You are not allowed to use that file extension. File: "' . $file->getName() . '"',
808  1375955430
809  );
810  }
811  }
812 
822  protected function assureFileWritePermissions(FileInterface $file)
823  {
824  // Check if user is allowed to write the file and $file is writable
825  if (!$this->checkFileActionPermission('write', $file)) {
826  throw new Exception\InsufficientFileWritePermissionsException('Writing to file "' . $file->getIdentifier() . '" is not allowed.', 1330121088);
827  }
828  if (!$this->checkFileExtensionPermission($file->getName())) {
829  throw new Exception\IllegalFileExtensionException('You are not allowed to edit a file with extension "' . $file->getExtension() . '"', 1366711933);
830  }
831  }
832 
841  protected function assureFileReplacePermissions(FileInterface $file)
842  {
843  // Check if user is allowed to replace the file and $file is writable
844  if (!$this->checkFileActionPermission('replace', $file)) {
845  throw new Exception\InsufficientFileWritePermissionsException('Replacing file "' . $file->getIdentifier() . '" is not allowed.', 1436899571);
846  }
847  // Check if parentFolder is writable for the user
848  if (!$this->checkFolderActionPermission('write', $file->getParentFolder())) {
849  throw new Exception\InsufficientFolderWritePermissionsException('You are not allowed to write to the target folder "' . $file->getIdentifier() . '"', 1436899572);
850  }
851  }
852 
862  protected function assureFileDeletePermissions(FileInterface $file)
863  {
864  // Check for disallowed file extensions
865  if (!$this->checkFileExtensionPermission($file->getName())) {
866  throw new Exception\IllegalFileExtensionException('You are not allowed to delete a file with extension "' . $file->getExtension() . '"', 1377778916);
867  }
868  // Check further permissions if file is not a processed file
869  if (!$file instanceof ProcessedFile) {
870  // Check if user is allowed to delete the file and $file is writable
871  if (!$this->checkFileActionPermission('delete', $file)) {
872  throw new Exception\InsufficientFileWritePermissionsException('You are not allowed to delete the file "' . $file->getIdentifier() . '"', 1319550425);
873  }
874  // Check if the user has write permissions to folders
875  // Would be good if we could check for actual write permissions in the containig folder
876  // but we cannot since we have no access to the containing folder of this file.
877  if (!$this->checkUserActionPermission('write', 'Folder')) {
878  throw new Exception\InsufficientFolderWritePermissionsException('Writing to folders is not allowed.', 1377778702);
879  }
880  }
881  }
882 
895  protected function assureFileAddPermissions($targetFolder, $targetFileName)
896  {
897  // Check for a valid file extension
898  if (!$this->checkFileExtensionPermission($targetFileName)) {
899  throw new Exception\IllegalFileExtensionException('Extension of file name is not allowed in "' . $targetFileName . '"!', 1322120271);
900  }
901  // Makes sure the user is allowed to upload
902  if (!$this->checkUserActionPermission('add', 'File')) {
903  throw new Exception\InsufficientUserPermissionsException('You are not allowed to add files to this storage "' . $this->getUid() . '"', 1376992145);
904  }
905  // Check if targetFolder is writable
906  if (!$this->checkFolderActionPermission('write', $targetFolder)) {
907  throw new Exception\InsufficientFolderWritePermissionsException('You are not allowed to write to the target folder "' . $targetFolder->getIdentifier() . '"', 1322120356);
908  }
909  }
910 
927  protected function assureFileUploadPermissions($localFilePath, $targetFolder, $targetFileName, $uploadedFileSize)
928  {
929  // Makes sure this is an uploaded file
930  if (!is_uploaded_file($localFilePath)) {
931  throw new Exception\UploadException('The upload has failed, no uploaded file found!', 1322110455);
932  }
933  // Max upload size (kb) for files.
934  $maxUploadFileSize = GeneralUtility::getMaxUploadFileSize() * 1024;
935  if ($uploadedFileSize >= $maxUploadFileSize) {
936  unlink($localFilePath);
937  throw new Exception\UploadSizeException('The uploaded file exceeds the size-limit of ' . $maxUploadFileSize . ' bytes', 1322110041);
938  }
939  $this->assureFileAddPermissions($targetFolder, $targetFileName);
940  }
941 
954  protected function assureFileMovePermissions(FileInterface $file, Folder $targetFolder, $targetFileName)
955  {
956  // Check if targetFolder is within this storage
957  if ($this->getUid() !== $targetFolder->getStorage()->getUid()) {
958  throw new \RuntimeException('The target folder is not in the same storage. Target folder given: "' . $targetFolder . '"', 1422553107);
959  }
960  // Check for a valid file extension
961  if (!$this->checkFileExtensionPermission($targetFileName)) {
962  throw new Exception\IllegalFileExtensionException('Extension of file name is not allowed in "' . $targetFileName . '"!', 1378243279);
963  }
964  // Check if user is allowed to move and $file is readable and writable
965  if (!$file->getStorage()->checkFileActionPermission('move', $file)) {
966  throw new Exception\InsufficientUserPermissionsException('You are not allowed to move files to storage "' . $this->getUid() . '"', 1319219349);
967  }
968  // Check if target folder is writable
969  if (!$this->checkFolderActionPermission('write', $targetFolder)) {
970  throw new Exception\InsufficientFolderAccessPermissionsException('You are not allowed to write to the target folder "' . $targetFolder->getIdentifier() . '"', 1319219350);
971  }
972  }
973 
985  protected function assureFileRenamePermissions(FileInterface $file, $targetFileName)
986  {
987  // Check if file extension is allowed
988  if (!$this->checkFileExtensionPermission($targetFileName) || !$this->checkFileExtensionPermission($file->getName())) {
989  throw new Exception\IllegalFileExtensionException('You are not allowed to rename a file with this extension. File given: "' . $file->getName() . '"', 1371466663);
990  }
991  // Check if user is allowed to rename
992  if (!$this->checkFileActionPermission('rename', $file)) {
993  throw new Exception\InsufficientUserPermissionsException('You are not allowed to rename files. File given: "' . $file->getName() . '"', 1319219351);
994  }
995  // Check if the user is allowed to write to folders
996  // Although it would be good to check, we cannot check here if the folder actually is writable
997  // because we do not know in which folder the file resides.
998  // So we rely on the driver to throw an exception in case the renaming failed.
999  if (!$this->checkFolderActionPermission('write')) {
1000  throw new Exception\InsufficientFileWritePermissionsException('You are not allowed to write to folders', 1319219352);
1001  }
1002  }
1003 
1019  protected function assureFileCopyPermissions(FileInterface $file, Folder $targetFolder, $targetFileName)
1020  {
1021  // Check if targetFolder is within this storage, this should never happen
1022  if ($this->getUid() != $targetFolder->getStorage()->getUid()) {
1023  throw new Exception('The operation of the folder cannot be called by this storage "' . $this->getUid() . '"', 1319550405);
1024  }
1025  // Check if user is allowed to copy
1026  if (!$file->getStorage()->checkFileActionPermission('copy', $file)) {
1027  throw new Exception\InsufficientFileReadPermissionsException('You are not allowed to copy the file "' . $file->getIdentifier() . '"', 1319550426);
1028  }
1029  // Check if targetFolder is writable
1030  if (!$this->checkFolderActionPermission('write', $targetFolder)) {
1031  throw new Exception\InsufficientFolderWritePermissionsException('You are not allowed to write to the target folder "' . $targetFolder->getIdentifier() . '"', 1319550435);
1032  }
1033  // Check for a valid file extension
1034  if (!$this->checkFileExtensionPermission($targetFileName) || !$this->checkFileExtensionPermission($file->getName())) {
1035  throw new Exception\IllegalFileExtensionException('You are not allowed to copy a file of that type.', 1319553317);
1036  }
1037  }
1038 
1054  protected function assureFolderCopyPermissions(FolderInterface $folderToCopy, FolderInterface $targetParentFolder)
1055  {
1056  // Check if targetFolder is within this storage, this should never happen
1057  if ($this->getUid() !== $targetParentFolder->getStorage()->getUid()) {
1058  throw new Exception('The operation of the folder cannot be called by this storage "' . $this->getUid() . '"', 1377777624);
1059  }
1060  if (!$folderToCopy instanceof Folder) {
1061  throw new \RuntimeException('The folder "' . $folderToCopy->getIdentifier() . '" to copy is not of type folder.', 1384209020);
1062  }
1063  // Check if user is allowed to copy and the folder is readable
1064  if (!$folderToCopy->getStorage()->checkFolderActionPermission('copy', $folderToCopy)) {
1065  throw new Exception\InsufficientFileReadPermissionsException('You are not allowed to copy the folder "' . $folderToCopy->getIdentifier() . '"', 1377777629);
1066  }
1067  if (!$targetParentFolder instanceof Folder) {
1068  throw new \RuntimeException('The target folder "' . $targetParentFolder->getIdentifier() . '" is not of type folder.', 1384209021);
1069  }
1070  // Check if targetFolder is writable
1071  if (!$this->checkFolderActionPermission('write', $targetParentFolder)) {
1072  throw new Exception\InsufficientFolderWritePermissionsException('You are not allowed to write to the target folder "' . $targetParentFolder->getIdentifier() . '"', 1377777635);
1073  }
1074  }
1075 
1091  protected function assureFolderMovePermissions(FolderInterface $folderToMove, FolderInterface $targetParentFolder)
1092  {
1093  // Check if targetFolder is within this storage, this should never happen
1094  if ($this->getUid() !== $targetParentFolder->getStorage()->getUid()) {
1095  throw new \InvalidArgumentException('Cannot move a folder into a folder that does not belong to this storage.', 1325777289);
1096  }
1097  if (!$folderToMove instanceof Folder) {
1098  throw new \RuntimeException('The folder "' . $folderToMove->getIdentifier() . '" to move is not of type Folder.', 1384209022);
1099  }
1100  // Check if user is allowed to move and the folder is writable
1101  // In fact we would need to check if the parent folder of the folder to move is writable also
1102  // But as of now we cannot extract the parent folder from this folder
1103  if (!$folderToMove->getStorage()->checkFolderActionPermission('move', $folderToMove)) {
1104  throw new Exception\InsufficientFileReadPermissionsException('You are not allowed to copy the folder "' . $folderToMove->getIdentifier() . '"', 1377778045);
1105  }
1106  if (!$targetParentFolder instanceof Folder) {
1107  throw new \RuntimeException('The target folder "' . $targetParentFolder->getIdentifier() . '" is not of type Folder.', 1384209023);
1108  }
1109  // Check if targetFolder is writable
1110  if (!$this->checkFolderActionPermission('write', $targetParentFolder)) {
1111  throw new Exception\InsufficientFolderWritePermissionsException('You are not allowed to write to the target folder "' . $targetParentFolder->getIdentifier() . '"', 1377778049);
1112  }
1113  }
1114 
1125  public function sanitizeFileName($fileName, Folder $targetFolder = null)
1126  {
1127  $targetFolder = $targetFolder ?: $this->getDefaultFolder();
1128  $fileName = $this->driver->sanitizeFileName($fileName);
1129 
1130  // The file name could be changed by an external slot
1131  $fileName = $this->emitSanitizeFileNameSignal($fileName, $targetFolder);
1132 
1133  return $fileName;
1134  }
1135 
1136 
1137  /********************
1138  * FILE ACTIONS
1139  ********************/
1152  public function addFile($localFilePath, Folder $targetFolder, $targetFileName = '', $conflictMode = DuplicationBehavior::RENAME)
1153  {
1154  $localFilePath = PathUtility::getCanonicalPath($localFilePath);
1155  if (!file_exists($localFilePath)) {
1156  throw new \InvalidArgumentException('File "' . $localFilePath . '" does not exist.', 1319552745);
1157  }
1158  $conflictMode = DuplicationBehavior::cast($conflictMode);
1159  $targetFolder = $targetFolder ?: $this->getDefaultFolder();
1160  $targetFileName = $this->sanitizeFileName($targetFileName ?: PathUtility::basename($localFilePath), $targetFolder);
1161 
1162  $targetFileName = $this->emitPreFileAddSignal($targetFileName, $targetFolder, $localFilePath);
1163 
1164  $this->assureFileAddPermissions($targetFolder, $targetFileName);
1165  if ($conflictMode->equals(DuplicationBehavior::CANCEL) && $this->driver->fileExistsInFolder($targetFileName, $targetFolder->getIdentifier())) {
1166  throw new Exception\ExistingTargetFileNameException('File "' . $targetFileName . '" already exists in folder ' . $targetFolder->getIdentifier(), 1322121068);
1167  } elseif ($conflictMode->equals(DuplicationBehavior::RENAME)) {
1168  $targetFileName = $this->getUniqueName($targetFolder, $targetFileName);
1169  }
1170 
1171  $fileIdentifier = $this->driver->addFile($localFilePath, $targetFolder->getIdentifier(), $targetFileName);
1172  $file = ResourceFactory::getInstance()->getFileObjectByStorageAndIdentifier($this->getUid(), $fileIdentifier);
1173 
1174  if ($this->autoExtractMetadataEnabled()) {
1175  $indexer = GeneralUtility::makeInstance(Indexer::class, $this);
1176  $indexer->extractMetaData($file);
1177  }
1178 
1179  $this->emitPostFileAddSignal($file, $targetFolder);
1180 
1181  return $file;
1182  }
1183 
1194  public function updateProcessedFile($localFilePath, ProcessedFile $processedFile, Folder $processingFolder = null)
1195  {
1196  if (!file_exists($localFilePath)) {
1197  throw new \InvalidArgumentException('File "' . $localFilePath . '" does not exist.', 1319552746);
1198  }
1199  if ($processingFolder === null) {
1201  }
1202  $fileIdentifier = $this->driver->addFile($localFilePath, $processingFolder->getIdentifier(), $processedFile->getName());
1203 
1204  // @todo check if we have to update the processed file other then the identifier
1205  $processedFile->setIdentifier($fileIdentifier);
1206  return $processedFile;
1207  }
1208 
1216  public function hashFile(FileInterface $fileObject, $hash)
1217  {
1218  return $this->hashFileByIdentifier($fileObject->getIdentifier(), $hash);
1219  }
1220 
1229  public function hashFileByIdentifier($fileIdentifier, $hash)
1230  {
1231  return $this->driver->hash($fileIdentifier, $hash);
1232  }
1233 
1242  public function hashFileIdentifier($file)
1243  {
1244  if (is_object($file) && $file instanceof FileInterface) {
1246  $file = $file->getIdentifier();
1247  }
1248  return $this->driver->hashIdentifier($file);
1249  }
1250 
1261  public function getPublicUrl(ResourceInterface $resourceObject, $relativeToCurrentScript = false)
1262  {
1263  $publicUrl = null;
1264  if ($this->isOnline()) {
1265  // Pre-process the public URL by an accordant slot
1266  $this->emitPreGeneratePublicUrlSignal($resourceObject, $relativeToCurrentScript, array('publicUrl' => &$publicUrl));
1267 
1268  if (
1269  $publicUrl === null
1270  && $resourceObject instanceof File
1271  && ($helper = OnlineMediaHelperRegistry::getInstance()->getOnlineMediaHelper($resourceObject)) !== false
1272  ) {
1273  $publicUrl = $helper->getPublicUrl($resourceObject, $relativeToCurrentScript);
1274  }
1275 
1276  // If slot did not handle the signal, use the default way to determine public URL
1277  if ($publicUrl === null) {
1278  if ($this->hasCapability(self::CAPABILITY_PUBLIC)) {
1279  $publicUrl = $this->driver->getPublicUrl($resourceObject->getIdentifier());
1280  }
1281 
1282  if ($publicUrl === null && $resourceObject instanceof FileInterface) {
1283  $queryParameterArray = array('eID' => 'dumpFile', 't' => '');
1284  if ($resourceObject instanceof File) {
1285  $queryParameterArray['f'] = $resourceObject->getUid();
1286  $queryParameterArray['t'] = 'f';
1287  } elseif ($resourceObject instanceof ProcessedFile) {
1288  $queryParameterArray['p'] = $resourceObject->getUid();
1289  $queryParameterArray['t'] = 'p';
1290  }
1291 
1292  $queryParameterArray['token'] = GeneralUtility::hmac(implode('|', $queryParameterArray), 'resourceStorageDumpFile');
1293  $publicUrl = 'index.php?' . str_replace('+', '%20', http_build_query($queryParameterArray));
1294  }
1295 
1296  // If requested, make the path relative to the current script in order to make it possible
1297  // to use the relative file
1298  if ($publicUrl !== null && $relativeToCurrentScript && !GeneralUtility::isValidUrl($publicUrl)) {
1299  $absolutePathToContainingFolder = PathUtility::dirname(PATH_site . $publicUrl);
1300  $pathPart = PathUtility::getRelativePathTo($absolutePathToContainingFolder);
1301  $filePart = substr(PATH_site . $publicUrl, strlen($absolutePathToContainingFolder) + 1);
1302  $publicUrl = $pathPart . $filePart;
1303  }
1304  }
1305  }
1306  return $publicUrl;
1307  }
1308 
1319  public function processFile(FileInterface $fileObject, $context, array $configuration)
1320  {
1321  if ($fileObject->getStorage() !== $this) {
1322  throw new \InvalidArgumentException('Cannot process files of foreign storage', 1353401835);
1323  }
1324  $processedFile = $this->getFileProcessingService()->processFile($fileObject, $this, $context, $configuration);
1325 
1326  return $processedFile;
1327  }
1328 
1336  public function getFileForLocalProcessing(FileInterface $fileObject, $writable = true)
1337  {
1338  $filePath = $this->driver->getFileForLocalProcessing($fileObject->getIdentifier(), $writable);
1339  return $filePath;
1340  }
1341 
1348  public function getFile($identifier)
1349  {
1350  $file = $this->getFileFactory()->getFileObjectByStorageAndIdentifier($this->getUid(), $identifier);
1351  if (!$this->driver->fileExists($identifier)) {
1352  $file->setMissing(true);
1353  }
1354  return $file;
1355  }
1356 
1364  public function getFileInfo(FileInterface $fileObject)
1365  {
1366  return $this->getFileInfoByIdentifier($fileObject->getIdentifier());
1367  }
1368 
1377  public function getFileInfoByIdentifier($identifier, array $propertiesToExtract = array())
1378  {
1379  return $this->driver->getFileInfoByIdentifier($identifier, $propertiesToExtract);
1380  }
1381 
1388  {
1389  $this->fileAndFolderNameFilters = array();
1390  }
1391 
1398  {
1399  $this->fileAndFolderNameFilters = $GLOBALS['TYPO3_CONF_VARS']['SYS']['fal']['defaultFilterCallbacks'];
1400  }
1401 
1408  {
1410  }
1411 
1416  public function setFileAndFolderNameFilters(array $filters)
1417  {
1418  $this->fileAndFolderNameFilters = $filters;
1419  return $this;
1420  }
1421 
1425  public function addFileAndFolderNameFilter($filter)
1426  {
1427  $this->fileAndFolderNameFilters[] = $filter;
1428  }
1429 
1435  public function getFolderIdentifierFromFileIdentifier($fileIdentifier)
1436  {
1437  return $this->driver->getParentFolderIdentifierOfIdentifier($fileIdentifier);
1438  }
1439 
1447  public function getFileInFolder($fileName, Folder $folder)
1448  {
1449  $identifier = $this->driver->getFileInFolder($fileName, $folder->getIdentifier());
1450  return $this->getFileFactory()->getFileObjectByStorageAndIdentifier($this->getUid(), $identifier);
1451  }
1452 
1468  public function getFilesInFolder(Folder $folder, $start = 0, $maxNumberOfItems = 0, $useFilters = true, $recursive = false, $sort = '', $sortRev = false)
1469  {
1470  $this->assureFolderReadPermission($folder);
1471 
1472  $rows = $this->getFileIndexRepository()->findByFolder($folder);
1473 
1474  $filters = $useFilters == true ? $this->fileAndFolderNameFilters : array();
1475  $fileIdentifiers = array_values($this->driver->getFilesInFolder($folder->getIdentifier(), $start, $maxNumberOfItems, $recursive, $filters, $sort, $sortRev));
1476 
1477  $items = array();
1478  foreach ($fileIdentifiers as $identifier) {
1479  if (isset($rows[$identifier])) {
1480  $fileObject = $this->getFileFactory()->getFileObject($rows[$identifier]['uid'], $rows[$identifier]);
1481  } else {
1482  $fileObject = $this->getFileFactory()->getFileObjectByStorageAndIdentifier($this->getUid(), $identifier);
1483  }
1484  if ($fileObject instanceof FileInterface) {
1485  $key = $fileObject->getName();
1486  while (isset($items[$key])) {
1487  $key .= 'z';
1488  }
1489  $items[$key] = $fileObject;
1490  }
1491  }
1492 
1493  return $items;
1494  }
1495 
1502  public function getFileIdentifiersInFolder($folderIdentifier, $useFilters = true, $recursive = false)
1503  {
1504  $filters = $useFilters == true ? $this->fileAndFolderNameFilters : array();
1505  return $this->driver->getFilesInFolder($folderIdentifier, 0, 0, $recursive, $filters);
1506  }
1507 
1515  public function countFilesInFolder(Folder $folder, $useFilters = true, $recursive = false)
1516  {
1517  $this->assureFolderReadPermission($folder);
1518  $filters = $useFilters ? $this->fileAndFolderNameFilters : array();
1519  return $this->driver->countFilesInFolder($folder->getIdentifier(), $recursive, $filters);
1520  }
1521 
1528  public function getFolderIdentifiersInFolder($folderIdentifier, $useFilters = true, $recursive = false)
1529  {
1530  $filters = $useFilters == true ? $this->fileAndFolderNameFilters : array();
1531  return $this->driver->getFoldersInFolder($folderIdentifier, 0, 0, $recursive, $filters);
1532  }
1533 
1540  public function hasFile($identifier)
1541  {
1542  // Allow if identifier is in processing folder
1543  if (!$this->isWithinProcessingFolder($identifier)) {
1544  $this->assureFolderReadPermission();
1545  }
1546  return $this->driver->fileExists($identifier);
1547  }
1548 
1554  public function getProcessingFolders()
1555  {
1556  if ($this->processingFolders === null) {
1557  $this->processingFolders = array();
1558  $this->processingFolders[] = $this->getProcessingFolder();
1560  $storageRepository = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Resource\StorageRepository::class);
1561  $allStorages = $storageRepository->findAll();
1562  foreach ($allStorages as $storage) {
1563  // To circumvent the permission check of the folder, we use the factory to create it "manually" instead of directly using $storage->getProcessingFolder()
1564  // See #66695 for details
1565  list($storageUid, $processingFolderIdentifier) = GeneralUtility::trimExplode(':', $storage->getStorageRecord()['processingfolder']);
1566  if (empty($processingFolderIdentifier) || (int)$storageUid !== $this->getUid()) {
1567  continue;
1568  }
1569  $potentialProcessingFolder = ResourceFactory::getInstance()->getInstance()->createFolderObject($this, $processingFolderIdentifier, $processingFolderIdentifier);
1570  if ($potentialProcessingFolder->getStorage() === $this && $potentialProcessingFolder->getIdentifier() !== $this->getProcessingFolder()->getIdentifier()) {
1571  $this->processingFolders[] = $potentialProcessingFolder;
1572  }
1573  }
1574  }
1575 
1576  return $this->processingFolders;
1577  }
1578 
1586  public function isProcessingFolder(Folder $folder)
1587  {
1588  $isProcessingFolder = false;
1589  foreach ($this->getProcessingFolders() as $processingFolder) {
1590  if ($folder->getCombinedIdentifier() === $processingFolder->getCombinedIdentifier()) {
1591  $isProcessingFolder = true;
1592  break;
1593  }
1594  }
1595  return $isProcessingFolder;
1596  }
1597 
1605  public function hasFileInFolder($fileName, Folder $folder)
1606  {
1607  $this->assureFolderReadPermission($folder);
1608  return $this->driver->fileExistsInFolder($fileName, $folder->getIdentifier());
1609  }
1610 
1619  public function getFileContents($file)
1620  {
1621  $this->assureFileReadPermission($file);
1622  return $this->driver->getFileContents($file->getIdentifier());
1623  }
1624 
1635  public function dumpFileContents(FileInterface $file, $asDownload = false, $alternativeFilename = null, $overrideMimeType = null)
1636  {
1637  $downloadName = $alternativeFilename ?: $file->getName();
1638  $contentDisposition = $asDownload ? 'attachment' : 'inline';
1639  header('Content-Disposition: ' . $contentDisposition . '; filename="' . $downloadName . '"');
1640  header('Content-Type: ' . ($overrideMimeType ?: $file->getMimeType()));
1641  header('Content-Length: ' . $file->getSize());
1642 
1643  // Cache-Control header is needed here to solve an issue with browser IE8 and lower
1644  // See for more information: http://support.microsoft.com/kb/323308
1645  header("Cache-Control: ''");
1646  header('Last-Modified: ' .
1647  gmdate('D, d M Y H:i:s', array_pop($this->driver->getFileInfoByIdentifier($file->getIdentifier(), array('mtime')))) . ' GMT',
1648  true,
1649  200
1650  );
1651  ob_clean();
1652  flush();
1653  while (ob_get_level() > 0) {
1654  ob_end_clean();
1655  }
1656  $this->driver->dumpFileContents($file->getIdentifier());
1657  }
1658 
1670  public function setFileContents(AbstractFile $file, $contents)
1671  {
1672  // Check if user is allowed to edit
1673  $this->assureFileWritePermissions($file);
1674  // Call driver method to update the file and update file index entry afterwards
1675  $result = $this->driver->setFileContents($file->getIdentifier(), $contents);
1676  $this->getIndexer()->updateIndexEntry($file);
1677  $this->emitPostFileSetContentsSignal($file, $contents);
1678  return $result;
1679  }
1680 
1693  public function createFile($fileName, Folder $targetFolderObject)
1694  {
1695  $this->assureFileAddPermissions($targetFolderObject, $fileName);
1696  $newFileIdentifier = $this->driver->createFile($fileName, $targetFolderObject->getIdentifier());
1697  $this->emitPostFileCreateSignal($newFileIdentifier, $targetFolderObject);
1698  return ResourceFactory::getInstance()->getFileObjectByStorageAndIdentifier($this->getUid(), $newFileIdentifier);
1699  }
1700 
1709  public function deleteFile($fileObject)
1710  {
1711  $this->assureFileDeletePermissions($fileObject);
1712 
1713  $this->emitPreFileDeleteSignal($fileObject);
1714 
1715  if ($this->driver->fileExists($fileObject->getIdentifier())) {
1716  $result = $this->driver->deleteFile($fileObject->getIdentifier());
1717  if (!$result) {
1718  throw new Exception\FileOperationErrorException('Deleting the file "' . $fileObject->getIdentifier() . '\' failed.', 1329831691);
1719  }
1720  }
1721  // Mark the file object as deleted
1722  if ($fileObject instanceof File) {
1723  $fileObject->setDeleted();
1724  }
1725 
1726  $this->emitPostFileDeleteSignal($fileObject);
1727 
1728  return true;
1729  }
1730 
1745  public function copyFile(FileInterface $file, Folder $targetFolder, $targetFileName = null, $conflictMode = DuplicationBehavior::RENAME)
1746  {
1747  $conflictMode = DuplicationBehavior::cast($conflictMode);
1748  if ($targetFileName === null) {
1749  $targetFileName = $file->getName();
1750  }
1751  $sanitizedTargetFileName = $this->driver->sanitizeFileName($targetFileName);
1752  $this->assureFileCopyPermissions($file, $targetFolder, $sanitizedTargetFileName);
1753  $this->emitPreFileCopySignal($file, $targetFolder);
1754  // File exists and we should abort, let's abort
1755  if ($conflictMode->equals(DuplicationBehavior::CANCEL) && $targetFolder->hasFile($sanitizedTargetFileName)) {
1756  throw new Exception\ExistingTargetFileNameException('The target file already exists.', 1320291064);
1757  }
1758  // File exists and we should find another name, let's find another one
1759  if ($conflictMode->equals(DuplicationBehavior::RENAME) && $targetFolder->hasFile($sanitizedTargetFileName)) {
1760  $sanitizedTargetFileName = $this->getUniqueName($targetFolder, $sanitizedTargetFileName);
1761  }
1762  $sourceStorage = $file->getStorage();
1763  // Call driver method to create a new file from an existing file object,
1764  // and return the new file object
1765  if ($sourceStorage === $this) {
1766  $newFileObjectIdentifier = $this->driver->copyFileWithinStorage($file->getIdentifier(), $targetFolder->getIdentifier(), $sanitizedTargetFileName);
1767  } else {
1768  $tempPath = $file->getForLocalProcessing();
1769  $newFileObjectIdentifier = $this->driver->addFile($tempPath, $targetFolder->getIdentifier(), $sanitizedTargetFileName);
1770  }
1771  $newFileObject = ResourceFactory::getInstance()->getFileObjectByStorageAndIdentifier($this->getUid(), $newFileObjectIdentifier);
1772  $this->emitPostFileCopySignal($file, $targetFolder);
1773  return $newFileObject;
1774  }
1775 
1791  public function moveFile($file, $targetFolder, $targetFileName = null, $conflictMode = DuplicationBehavior::RENAME)
1792  {
1793  $conflictMode = DuplicationBehavior::cast($conflictMode);
1794  if ($targetFileName === null) {
1795  $targetFileName = $file->getName();
1796  }
1797  $originalFolder = $file->getParentFolder();
1798  $sanitizedTargetFileName = $this->driver->sanitizeFileName($targetFileName);
1799  $this->assureFileMovePermissions($file, $targetFolder, $sanitizedTargetFileName);
1800  if ($targetFolder->hasFile($sanitizedTargetFileName)) {
1801  // File exists and we should abort, let's abort
1802  if ($conflictMode->equals(DuplicationBehavior::RENAME)) {
1803  $sanitizedTargetFileName = $this->getUniqueName($targetFolder, $sanitizedTargetFileName);
1804  } elseif ($conflictMode->equals(DuplicationBehavior::CANCEL)) {
1805  throw new Exception\ExistingTargetFileNameException('The target file already exists', 1329850997);
1806  }
1807  }
1808  $this->emitPreFileMoveSignal($file, $targetFolder);
1809  $sourceStorage = $file->getStorage();
1810  // Call driver method to move the file and update the index entry
1811  try {
1812  if ($sourceStorage === $this) {
1813  $newIdentifier = $this->driver->moveFileWithinStorage($file->getIdentifier(), $targetFolder->getIdentifier(), $sanitizedTargetFileName);
1814  if (!$file instanceof AbstractFile) {
1815  throw new \RuntimeException('The given file is not of type AbstractFile.', 1384209025);
1816  }
1817  $file->updateProperties(array('identifier' => $newIdentifier));
1818  } else {
1819  $tempPath = $file->getForLocalProcessing();
1820  $newIdentifier = $this->driver->addFile($tempPath, $targetFolder->getIdentifier(), $sanitizedTargetFileName);
1821  $sourceStorage->driver->deleteFile($file->getIdentifier());
1822  if ($file instanceof File) {
1823  $file->updateProperties(array('storage' => $this->getUid(), 'identifier' => $newIdentifier));
1824  }
1825  }
1826  $this->getIndexer()->updateIndexEntry($file);
1827  } catch (\TYPO3\CMS\Core\Exception $e) {
1828  echo $e->getMessage();
1829  }
1830  $this->emitPostFileMoveSignal($file, $targetFolder, $originalFolder);
1831  return $file;
1832  }
1833 
1845  public function renameFile($file, $targetFileName)
1846  {
1847  // @todo add $conflictMode setting
1848 
1849  // The name should be different from the current.
1850  if ($file->getName() === $targetFileName) {
1851  return $file;
1852  }
1853  $sanitizedTargetFileName = $this->driver->sanitizeFileName($targetFileName);
1854  $this->assureFileRenamePermissions($file, $sanitizedTargetFileName);
1855  $this->emitPreFileRenameSignal($file, $sanitizedTargetFileName);
1856 
1857  // Call driver method to rename the file and update the index entry
1858  try {
1859  $newIdentifier = $this->driver->renameFile($file->getIdentifier(), $sanitizedTargetFileName);
1860  if ($file instanceof File) {
1861  $file->updateProperties(array('identifier' => $newIdentifier));
1862  }
1863  $this->getIndexer()->updateIndexEntry($file);
1864  } catch (\RuntimeException $e) {
1865  }
1866 
1867  $this->emitPostFileRenameSignal($file, $sanitizedTargetFileName);
1868 
1869  return $file;
1870  }
1871 
1883  public function replaceFile(FileInterface $file, $localFilePath)
1884  {
1885  $this->assureFileReplacePermissions($file);
1886  if (!$this->checkFileExtensionPermission($localFilePath)) {
1887  throw new Exception\IllegalFileExtensionException('Source file extension not allowed.', 1378132239);
1888  }
1889  if (!file_exists($localFilePath)) {
1890  throw new \InvalidArgumentException('File "' . $localFilePath . '" does not exist.', 1325842622);
1891  }
1892  $this->emitPreFileReplaceSignal($file, $localFilePath);
1893  $this->driver->replaceFile($file->getIdentifier(), $localFilePath);
1894  if ($file instanceof File) {
1895  $this->getIndexer()->updateIndexEntry($file);
1896  }
1897  if ($this->autoExtractMetadataEnabled()) {
1898  $indexer = GeneralUtility::makeInstance(Indexer::class, $this);
1899  $indexer->extractMetaData($file);
1900  }
1901  $this->emitPostFileReplaceSignal($file, $localFilePath);
1902 
1903  return $file;
1904  }
1905 
1915  public function addUploadedFile(array $uploadedFileData, Folder $targetFolder = null, $targetFileName = null, $conflictMode = DuplicationBehavior::CANCEL)
1916  {
1917  $conflictMode = DuplicationBehavior::cast($conflictMode);
1918  $localFilePath = $uploadedFileData['tmp_name'];
1919  if ($targetFolder === null) {
1920  $targetFolder = $this->getDefaultFolder();
1921  }
1922  if ($targetFileName === null) {
1923  $targetFileName = $uploadedFileData['name'];
1924  }
1925  $targetFileName = $this->driver->sanitizeFileName($targetFileName);
1926 
1927  $this->assureFileUploadPermissions($localFilePath, $targetFolder, $targetFileName, $uploadedFileData['size']);
1928  if ($this->hasFileInFolder($targetFileName, $targetFolder) && $conflictMode->equals(DuplicationBehavior::REPLACE)) {
1929  $file = $this->getFileInFolder($targetFileName, $targetFolder);
1930  $resultObject = $this->replaceFile($file, $localFilePath);
1931  } else {
1932  $resultObject = $this->addFile($localFilePath, $targetFolder, $targetFileName, (string)$conflictMode);
1933  }
1934  return $resultObject;
1935  }
1936 
1937  /********************
1938  * FOLDER ACTIONS
1939  ********************/
1946  protected function getAllFileObjectsInFolder(Folder $folder)
1947  {
1948  $files = array();
1949  $folderQueue = array($folder);
1950  while (!empty($folderQueue)) {
1951  $folder = array_shift($folderQueue);
1952  foreach ($folder->getSubfolders() as $subfolder) {
1953  $folderQueue[] = $subfolder;
1954  }
1955  foreach ($folder->getFiles() as $file) {
1957  $files[$file->getIdentifier()] = $file;
1958  }
1959  }
1960 
1961  return $files;
1962  }
1963 
1978  public function moveFolder(Folder $folderToMove, Folder $targetParentFolder, $newFolderName = null, $conflictMode = DuplicationBehavior::RENAME)
1979  {
1980  // @todo add tests
1981  $originalFolder = $folderToMove->getParentFolder();
1982  $this->assureFolderMovePermissions($folderToMove, $targetParentFolder);
1983  $sourceStorage = $folderToMove->getStorage();
1984  $returnObject = null;
1985  $sanitizedNewFolderName = $this->driver->sanitizeFileName($newFolderName ?: $folderToMove->getName());
1986  // @todo check if folder already exists in $targetParentFolder, handle this conflict then
1987  $this->emitPreFolderMoveSignal($folderToMove, $targetParentFolder, $sanitizedNewFolderName);
1988  // Get all file objects now so we are able to update them after moving the folder
1989  $fileObjects = $this->getAllFileObjectsInFolder($folderToMove);
1990  if ($sourceStorage === $this) {
1991  if ($this->isWithinFolder($folderToMove, $targetParentFolder)) {
1992  throw new InvalidTargetFolderException(
1993  sprintf(
1994  'Cannot move folder "%s" into target folder "%s", because the target folder is already within the folder to be moved!',
1995  $folderToMove->getName(),
1996  $targetParentFolder->getName()
1997  ),
1998  1422723050
1999  );
2000  }
2001  $fileMappings = $this->driver->moveFolderWithinStorage($folderToMove->getIdentifier(), $targetParentFolder->getIdentifier(), $sanitizedNewFolderName);
2002  } else {
2003  $fileMappings = $this->moveFolderBetweenStorages($folderToMove, $targetParentFolder, $sanitizedNewFolderName);
2004  }
2005  // Update the identifier and storage of all file objects
2006  foreach ($fileObjects as $oldIdentifier => $fileObject) {
2007  $newIdentifier = $fileMappings[$oldIdentifier];
2008  $fileObject->updateProperties(array('storage' => $this->getUid(), 'identifier' => $newIdentifier));
2009  $this->getIndexer()->updateIndexEntry($fileObject);
2010  }
2011  $returnObject = $this->getFolder($fileMappings[$folderToMove->getIdentifier()]);
2012  $this->emitPostFolderMoveSignal($folderToMove, $targetParentFolder, $returnObject->getName(), $originalFolder);
2013  return $returnObject;
2014  }
2015 
2026  protected function moveFolderBetweenStorages(Folder $folderToMove, Folder $targetParentFolder, $newFolderName)
2027  {
2028  throw new \RuntimeException('Not yet implemented');
2029  }
2030 
2041  public function copyFolder(FolderInterface $folderToCopy, FolderInterface $targetParentFolder, $newFolderName = null, $conflictMode = DuplicationBehavior::RENAME)
2042  {
2043  // @todo implement the $conflictMode handling
2044  $this->assureFolderCopyPermissions($folderToCopy, $targetParentFolder);
2045  $returnObject = null;
2046  $sanitizedNewFolderName = $this->driver->sanitizeFileName($newFolderName ?: $folderToCopy->getName());
2047  if ($folderToCopy instanceof Folder && $targetParentFolder instanceof Folder) {
2048  $this->emitPreFolderCopySignal($folderToCopy, $targetParentFolder, $sanitizedNewFolderName);
2049  }
2050  $sourceStorage = $folderToCopy->getStorage();
2051  // call driver method to move the file
2052  // that also updates the file object properties
2053  if ($sourceStorage === $this) {
2054  if ($this->isWithinFolder($folderToCopy, $targetParentFolder)) {
2055  throw new InvalidTargetFolderException(
2056  sprintf(
2057  'Cannot copy folder "%s" into target folder "%s", because the target folder is already within the folder to be copied!',
2058  $folderToCopy->getName(),
2059  $targetParentFolder->getName()
2060  ),
2061  1422723059
2062  );
2063  }
2064  $this->driver->copyFolderWithinStorage($folderToCopy->getIdentifier(), $targetParentFolder->getIdentifier(), $sanitizedNewFolderName);
2065  $returnObject = $this->getFolder($targetParentFolder->getSubfolder($sanitizedNewFolderName)->getIdentifier());
2066  } else {
2067  $this->copyFolderBetweenStorages($folderToCopy, $targetParentFolder, $sanitizedNewFolderName);
2068  }
2069  $this->emitPostFolderCopySignal($folderToCopy, $targetParentFolder, $returnObject->getName());
2070  return $returnObject;
2071  }
2072 
2083  protected function copyFolderBetweenStorages(Folder $folderToCopy, Folder $targetParentFolder, $newFolderName)
2084  {
2085  throw new \RuntimeException('Not yet implemented.');
2086  }
2087 
2097  public function renameFolder($folderObject, $newName)
2098  {
2099 
2100  // Renaming the folder should check if the parent folder is writable
2101  // We cannot do this however because we cannot extract the parent folder from a folder currently
2102  if (!$this->checkFolderActionPermission('rename', $folderObject)) {
2103  throw new Exception\InsufficientUserPermissionsException('You are not allowed to rename the folder "' . $folderObject->getIdentifier() . '\'', 1357811441);
2104  }
2105 
2106  $sanitizedNewName = $this->driver->sanitizeFileName($newName);
2107  $returnObject = null;
2108  if ($this->driver->folderExistsInFolder($sanitizedNewName, $folderObject->getIdentifier())) {
2109  throw new \InvalidArgumentException('The folder ' . $sanitizedNewName . ' already exists in folder ' . $folderObject->getIdentifier(), 1325418870);
2110  }
2111 
2112  $this->emitPreFolderRenameSignal($folderObject, $sanitizedNewName);
2113 
2114  $fileObjects = $this->getAllFileObjectsInFolder($folderObject);
2115  $fileMappings = $this->driver->renameFolder($folderObject->getIdentifier(), $sanitizedNewName);
2116  // Update the identifier of all file objects
2117  foreach ($fileObjects as $oldIdentifier => $fileObject) {
2118  $newIdentifier = $fileMappings[$oldIdentifier];
2119  $fileObject->updateProperties(array('identifier' => $newIdentifier));
2120  $this->getIndexer()->updateIndexEntry($fileObject);
2121  }
2122  $returnObject = $this->getFolder($fileMappings[$folderObject->getIdentifier()]);
2123 
2124  $this->emitPostFolderRenameSignal($folderObject, $returnObject->getName());
2125 
2126  return $returnObject;
2127  }
2128 
2141  public function deleteFolder($folderObject, $deleteRecursively = false)
2142  {
2143  $isEmpty = $this->driver->isFolderEmpty($folderObject->getIdentifier());
2144  $this->assureFolderDeletePermission($folderObject, ($deleteRecursively && !$isEmpty));
2145  if (!$isEmpty && !$deleteRecursively) {
2146  throw new \RuntimeException('Could not delete folder "' . $folderObject->getIdentifier() . '" because it is not empty.', 1325952534);
2147  }
2148 
2149  $this->emitPreFolderDeleteSignal($folderObject);
2150 
2151  foreach ($this->getFilesInFolder($folderObject, 0, 0, false, $deleteRecursively) as $file) {
2152  $this->deleteFile($file);
2153  }
2154 
2155  $result = $this->driver->deleteFolder($folderObject->getIdentifier(), $deleteRecursively);
2156 
2157  $this->emitPostFolderDeleteSignal($folderObject);
2158 
2159  return $result;
2160  }
2161 
2172  public function getFolderInFolder($folderName, Folder $parentFolder, $returnInaccessibleFolderObject = false)
2173  {
2174  $folderIdentifier = $this->driver->getFolderInFolder($folderName, $parentFolder->getIdentifier());
2175  return $this->getFolder($folderIdentifier, $returnInaccessibleFolderObject);
2176  }
2177 
2192  public function getFoldersInFolder(Folder $folder, $start = 0, $maxNumberOfItems = 0, $useFilters = true, $recursive = false, $sort = '', $sortRev = false)
2193  {
2194  $filters = $useFilters == true ? $this->fileAndFolderNameFilters : array();
2195 
2196  $folderIdentifiers = $this->driver->getFoldersInFolder($folder->getIdentifier(), $start, $maxNumberOfItems, $recursive, $filters, $sort, $sortRev);
2197 
2198  // Exclude processing folders
2199  foreach ($this->getProcessingFolders() as $processingFolder) {
2200  $processingIdentifier = $processingFolder->getIdentifier();
2201  if (isset($folderIdentifiers[$processingIdentifier])) {
2202  unset($folderIdentifiers[$processingIdentifier]);
2203  }
2204  }
2205  $folders = array();
2206  foreach ($folderIdentifiers as $folderIdentifier) {
2207  $folders[$folderIdentifier] = $this->getFolder($folderIdentifier, true);
2208  }
2209  return $folders;
2210  }
2211 
2219  public function countFoldersInFolder(Folder $folder, $useFilters = true, $recursive = false)
2220  {
2221  $this->assureFolderReadPermission($folder);
2222  $filters = $useFilters ? $this->fileAndFolderNameFilters : array();
2223  return $this->driver->countFoldersInFolder($folder->getIdentifier(), $recursive, $filters);
2224  }
2225 
2232  public function hasFolder($identifier)
2233  {
2234  $this->assureFolderReadPermission();
2235  return $this->driver->folderExists($identifier);
2236  }
2237 
2245  public function hasFolderInFolder($folderName, Folder $folder)
2246  {
2247  $this->assureFolderReadPermission($folder);
2248  return $this->driver->folderExistsInFolder($folderName, $folder->getIdentifier());
2249  }
2250 
2264  public function createFolder($folderName, Folder $parentFolder = null)
2265  {
2266  if ($parentFolder === null) {
2267  $parentFolder = $this->getRootLevelFolder();
2268  } elseif (!$this->driver->folderExists($parentFolder->getIdentifier())) {
2269  throw new \InvalidArgumentException('Parent folder "' . $parentFolder->getIdentifier() . '" does not exist.', 1325689164);
2270  }
2271  if (!$this->checkFolderActionPermission('add', $parentFolder)) {
2272  throw new Exception\InsufficientFolderWritePermissionsException('You are not allowed to create directories in the folder "' . $parentFolder->getIdentifier() . '"', 1323059807);
2273  }
2274  if ($this->driver->folderExistsInFolder($folderName, $parentFolder->getIdentifier())) {
2275  throw new Exception\ExistingTargetFolderException('Folder "' . $folderName . '" already exists.', 1423347324);
2276  }
2277 
2278  $this->emitPreFolderAddSignal($parentFolder, $folderName);
2279 
2280  $newFolder = $this->getDriver()->createFolder($folderName, $parentFolder->getIdentifier(), true);
2281  $newFolder = $this->getFolder($newFolder);
2282 
2283  $this->emitPostFolderAddSignal($newFolder);
2284 
2285  return $newFolder;
2286  }
2287 
2293  public function getDefaultFolder()
2294  {
2295  return $this->getFolder($this->driver->getDefaultFolder());
2296  }
2297 
2306  public function getFolder($identifier, $returnInaccessibleFolderObject = false)
2307  {
2308  $data = $this->driver->getFolderInfoByIdentifier($identifier);
2309  $folder = ResourceFactory::getInstance()->createFolderObject($this, $data['identifier'], $data['name']);
2310 
2311  try {
2312  $this->assureFolderReadPermission($folder);
2313  } catch (Exception\InsufficientFolderAccessPermissionsException $e) {
2314  $folder = null;
2315  if ($returnInaccessibleFolderObject) {
2316  // if parent folder is readable return inaccessible folder object
2317  $parentPermissions = $this->driver->getPermissions($this->driver->getParentFolderIdentifierOfIdentifier($identifier));
2318  if ($parentPermissions['r']) {
2319  $folder = GeneralUtility::makeInstance(
2320  \TYPO3\CMS\Core\Resource\InaccessibleFolder::class, $this, $data['identifier'], $data['name']
2321  );
2322  }
2323  }
2324 
2325  if ($folder === null) {
2326  throw $e;
2327  }
2328  }
2329  return $folder;
2330  }
2331 
2338  public function isWithinProcessingFolder($identifier)
2339  {
2340  $inProcessingFolder = false;
2341  foreach ($this->getProcessingFolders() as $processingFolder) {
2342  if ($this->driver->isWithin($processingFolder->getIdentifier(), $identifier)) {
2343  $inProcessingFolder = true;
2344  break;
2345  }
2346  }
2347  return $inProcessingFolder;
2348  }
2349 
2358  public function isWithinFolder(Folder $folder, ResourceInterface $resource)
2359  {
2360  if ($folder->getStorage() !== $this) {
2361  throw new \InvalidArgumentException('Given folder "' . $folder->getIdentifier() . '" is not part of this storage!', 1422709241);
2362  }
2363  if ($folder->getStorage() !== $resource->getStorage()) {
2364  return false;
2365  }
2366  return $this->driver->isWithin($folder->getIdentifier(), $resource->getIdentifier());
2367  }
2368 
2377  public function getRootLevelFolder($respectFileMounts = true)
2378  {
2379  if ($respectFileMounts && !empty($this->fileMounts)) {
2380  $mount = reset($this->fileMounts);
2381  return $mount['folder'];
2382  } else {
2383  return ResourceFactory::getInstance()->createFolderObject($this, $this->driver->getRootLevelFolder(), '');
2384  }
2385  }
2386 
2394  protected function emitSanitizeFileNameSignal($fileName, Folder $targetFolder)
2395  {
2396  list($fileName) = $this->getSignalSlotDispatcher()->dispatch(ResourceStorage::class, self::SIGNAL_SanitizeFileName, array($fileName, $targetFolder, $this, $this->driver));
2397  return $fileName;
2398  }
2399 
2408  protected function emitPreFileAddSignal($targetFileName, Folder $targetFolder, $sourceFilePath)
2409  {
2410  $this->getSignalSlotDispatcher()->dispatch(\TYPO3\CMS\Core\Resource\ResourceStorage::class, self::SIGNAL_PreFileAdd, array(&$targetFileName, $targetFolder, $sourceFilePath, $this, $this->driver));
2411  return $targetFileName;
2412  }
2413 
2421  protected function emitPostFileAddSignal(FileInterface $file, Folder $targetFolder)
2422  {
2423  $this->getSignalSlotDispatcher()->dispatch(\TYPO3\CMS\Core\Resource\ResourceStorage::class, self::SIGNAL_PostFileAdd, array($file, $targetFolder));
2424  }
2425 
2433  protected function emitPreFileCopySignal(FileInterface $file, Folder $targetFolder)
2434  {
2435  $this->getSignalSlotDispatcher()->dispatch(\TYPO3\CMS\Core\Resource\ResourceStorage::class, self::SIGNAL_PreFileCopy, array($file, $targetFolder));
2436  }
2437 
2445  protected function emitPostFileCopySignal(FileInterface $file, Folder $targetFolder)
2446  {
2447  $this->getSignalSlotDispatcher()->dispatch(\TYPO3\CMS\Core\Resource\ResourceStorage::class, self::SIGNAL_PostFileCopy, array($file, $targetFolder));
2448  }
2449 
2457  protected function emitPreFileMoveSignal(FileInterface $file, Folder $targetFolder)
2458  {
2459  $this->getSignalSlotDispatcher()->dispatch(\TYPO3\CMS\Core\Resource\ResourceStorage::class, self::SIGNAL_PreFileMove, array($file, $targetFolder));
2460  }
2461 
2470  protected function emitPostFileMoveSignal(FileInterface $file, Folder $targetFolder, Folder $originalFolder)
2471  {
2472  $this->getSignalSlotDispatcher()->dispatch(\TYPO3\CMS\Core\Resource\ResourceStorage::class, self::SIGNAL_PostFileMove, array($file, $targetFolder, $originalFolder));
2473  }
2474 
2482  protected function emitPreFileRenameSignal(FileInterface $file, $targetFolder)
2483  {
2484  $this->getSignalSlotDispatcher()->dispatch(\TYPO3\CMS\Core\Resource\ResourceStorage::class, self::SIGNAL_PreFileRename, array($file, $targetFolder));
2485  }
2486 
2494  protected function emitPostFileRenameSignal(FileInterface $file, $targetFolder)
2495  {
2496  $this->getSignalSlotDispatcher()->dispatch(\TYPO3\CMS\Core\Resource\ResourceStorage::class, self::SIGNAL_PostFileRename, array($file, $targetFolder));
2497  }
2498 
2506  protected function emitPreFileReplaceSignal(FileInterface $file, $localFilePath)
2507  {
2508  $this->getSignalSlotDispatcher()->dispatch(\TYPO3\CMS\Core\Resource\ResourceStorage::class, self::SIGNAL_PreFileReplace, array($file, $localFilePath));
2509  }
2510 
2518  protected function emitPostFileReplaceSignal(FileInterface $file, $localFilePath)
2519  {
2520  $this->getSignalSlotDispatcher()->dispatch(\TYPO3\CMS\Core\Resource\ResourceStorage::class, self::SIGNAL_PostFileReplace, array($file, $localFilePath));
2521  }
2522 
2529  protected function emitPostFileCreateSignal($newFileIdentifier, Folder $targetFolder)
2530  {
2531  $this->getSignalSlotDispatcher()->dispatch(\TYPO3\CMS\Core\Resource\ResourceStorage::class, self::SIGNAL_PostFileCreate, array($newFileIdentifier, $targetFolder));
2532  }
2533 
2540  protected function emitPreFileDeleteSignal(FileInterface $file)
2541  {
2542  $this->getSignalSlotDispatcher()->dispatch(\TYPO3\CMS\Core\Resource\ResourceStorage::class, self::SIGNAL_PreFileDelete, array($file));
2543  }
2544 
2551  protected function emitPostFileDeleteSignal(FileInterface $file)
2552  {
2553  $this->getSignalSlotDispatcher()->dispatch(\TYPO3\CMS\Core\Resource\ResourceStorage::class, self::SIGNAL_PostFileDelete, array($file));
2554  }
2555 
2563  protected function emitPostFileSetContentsSignal(FileInterface $file, $content)
2564  {
2565  $this->getSignalSlotDispatcher()->dispatch(\TYPO3\CMS\Core\Resource\ResourceStorage::class, self::SIGNAL_PostFileSetContents, array($file, $content));
2566  }
2567 
2575  protected function emitPreFolderAddSignal(Folder $targetFolder, $name)
2576  {
2577  $this->getSignalSlotDispatcher()->dispatch(\TYPO3\CMS\Core\Resource\ResourceStorage::class, self::SIGNAL_PreFolderAdd, array($targetFolder, $name));
2578  }
2579 
2586  protected function emitPostFolderAddSignal(Folder $folder)
2587  {
2588  $this->getSignalSlotDispatcher()->dispatch(\TYPO3\CMS\Core\Resource\ResourceStorage::class, self::SIGNAL_PostFolderAdd, array($folder));
2589  }
2590 
2599  protected function emitPreFolderCopySignal(Folder $folder, Folder $targetFolder, $newName)
2600  {
2601  $this->getSignalSlotDispatcher()->dispatch(\TYPO3\CMS\Core\Resource\ResourceStorage::class, self::SIGNAL_PreFolderCopy, array($folder, $targetFolder, $newName));
2602  }
2603 
2612  protected function emitPostFolderCopySignal(Folder $folder, Folder $targetFolder, $newName)
2613  {
2614  $this->getSignalSlotDispatcher()->dispatch(\TYPO3\CMS\Core\Resource\ResourceStorage::class, self::SIGNAL_PostFolderCopy, array($folder, $targetFolder, $newName));
2615  }
2616 
2625  protected function emitPreFolderMoveSignal(Folder $folder, Folder $targetFolder, $newName)
2626  {
2627  $this->getSignalSlotDispatcher()->dispatch(\TYPO3\CMS\Core\Resource\ResourceStorage::class, self::SIGNAL_PreFolderMove, array($folder, $targetFolder, $newName));
2628  }
2629 
2639  protected function emitPostFolderMoveSignal(Folder $folder, Folder $targetFolder, $newName, Folder $originalFolder)
2640  {
2641  $this->getSignalSlotDispatcher()->dispatch(\TYPO3\CMS\Core\Resource\ResourceStorage::class, self::SIGNAL_PostFolderMove, array($folder, $targetFolder, $newName, $originalFolder));
2642  }
2643 
2651  protected function emitPreFolderRenameSignal(Folder $folder, $newName)
2652  {
2653  $this->getSignalSlotDispatcher()->dispatch(\TYPO3\CMS\Core\Resource\ResourceStorage::class, self::SIGNAL_PreFolderRename, array($folder, $newName));
2654  }
2655 
2663  protected function emitPostFolderRenameSignal(Folder $folder, $newName)
2664  {
2665  $this->getSignalSlotDispatcher()->dispatch(\TYPO3\CMS\Core\Resource\ResourceStorage::class, self::SIGNAL_PostFolderRename, array($folder, $newName));
2666  }
2667 
2674  protected function emitPreFolderDeleteSignal(Folder $folder)
2675  {
2676  $this->getSignalSlotDispatcher()->dispatch(\TYPO3\CMS\Core\Resource\ResourceStorage::class, self::SIGNAL_PreFolderDelete, array($folder));
2677  }
2678 
2685  protected function emitPostFolderDeleteSignal(Folder $folder)
2686  {
2687  $this->getSignalSlotDispatcher()->dispatch(\TYPO3\CMS\Core\Resource\ResourceStorage::class, self::SIGNAL_PostFolderDelete, array($folder));
2688  }
2689 
2697  protected function emitPreGeneratePublicUrlSignal(ResourceInterface $resourceObject, $relativeToCurrentScript, array $urlData)
2698  {
2699  $this->getSignalSlotDispatcher()->dispatch(\TYPO3\CMS\Core\Resource\ResourceStorage::class, self::SIGNAL_PreGeneratePublicUrl, array($this, $this->driver, $resourceObject, $relativeToCurrentScript, $urlData));
2700  }
2701 
2715  protected function getUniqueName(Folder $folder, $theFile, $dontCheckForUnique = false)
2716  {
2717  static $maxNumber = 99, $uniqueNamePrefix = '';
2718  // Fetches info about path, name, extension of $theFile
2719  $origFileInfo = PathUtility::pathinfo($theFile);
2720  // Adds prefix
2721  if ($uniqueNamePrefix) {
2722  $origFileInfo['basename'] = $uniqueNamePrefix . $origFileInfo['basename'];
2723  $origFileInfo['filename'] = $uniqueNamePrefix . $origFileInfo['filename'];
2724  }
2725  // Check if the file exists and if not - return the fileName...
2726  // The destinations file
2727  $theDestFile = $origFileInfo['basename'];
2728  // If the file does NOT exist we return this fileName
2729  if (!$this->driver->fileExistsInFolder($theDestFile, $folder->getIdentifier()) || $dontCheckForUnique) {
2730  return $theDestFile;
2731  }
2732  // Well the fileName in its pure form existed. Now we try to append
2733  // numbers / unique-strings and see if we can find an available fileName
2734  // This removes _xx if appended to the file
2735  $theTempFileBody = preg_replace('/_[0-9][0-9]$/', '', $origFileInfo['filename']);
2736  $theOrigExt = $origFileInfo['extension'] ? '.' . $origFileInfo['extension'] : '';
2737  for ($a = 1; $a <= $maxNumber + 1; $a++) {
2738  // First we try to append numbers
2739  if ($a <= $maxNumber) {
2740  $insert = '_' . sprintf('%02d', $a);
2741  } else {
2742  $insert = '_' . substr(md5(uniqid('', true)), 0, 6);
2743  }
2744  $theTestFile = $theTempFileBody . $insert . $theOrigExt;
2745  // The destinations file
2746  $theDestFile = $theTestFile;
2747  // If the file does NOT exist we return this fileName
2748  if (!$this->driver->fileExistsInFolder($theDestFile, $folder->getIdentifier())) {
2749  return $theDestFile;
2750  }
2751  }
2752  throw new \RuntimeException('Last possible name "' . $theDestFile . '" is already taken.', 1325194291);
2753  }
2754 
2760  protected function getSignalSlotDispatcher()
2761  {
2762  if (!isset($this->signalSlotDispatcher)) {
2763  $this->signalSlotDispatcher = $this->getObjectManager()->get(\TYPO3\CMS\Extbase\SignalSlot\Dispatcher::class);
2764  }
2766  }
2767 
2773  protected function getObjectManager()
2774  {
2775  return GeneralUtility::makeInstance(\TYPO3\CMS\Extbase\Object\ObjectManager::class);
2776  }
2777 
2781  protected function getFileFactory()
2782  {
2783  return GeneralUtility::makeInstance(\TYPO3\CMS\Core\Resource\ResourceFactory::class);
2784  }
2785 
2789  protected function getFileIndexRepository()
2790  {
2792  }
2793 
2797  protected function getFileProcessingService()
2798  {
2799  if (!$this->fileProcessingService) {
2800  $this->fileProcessingService = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Resource\Service\FileProcessingService::class, $this, $this->driver);
2801  }
2803  }
2804 
2811  public function getRole(FolderInterface $folder)
2812  {
2813  $folderRole = FolderInterface::ROLE_DEFAULT;
2814  $identifier = $folder->getIdentifier();
2815  if (method_exists($this->driver, 'getRole')) {
2816  $folderRole = $this->driver->getRole($folder->getIdentifier());
2817  }
2818  if (isset($this->fileMounts[$identifier])) {
2819  $folderRole = FolderInterface::ROLE_MOUNT;
2820 
2821  if (!empty($this->fileMounts[$identifier]['read_only'])) {
2823  }
2824  if ($this->fileMounts[$identifier]['user_mount']) {
2825  $folderRole = FolderInterface::ROLE_USER_MOUNT;
2826  }
2827  }
2828  if ($folder instanceof Folder && $this->isProcessingFolder($folder)) {
2829  $folderRole = FolderInterface::ROLE_PROCESSING;
2830  }
2831 
2832  return $folderRole;
2833  }
2834 
2841  public function getProcessingFolder()
2842  {
2843  if (!isset($this->processingFolder)) {
2844  $processingFolder = self::DEFAULT_ProcessingFolder;
2845  if (!empty($this->storageRecord['processingfolder'])) {
2846  $processingFolder = $this->storageRecord['processingfolder'];
2847  }
2848  try {
2849  if (strpos($processingFolder, ':') !== false) {
2850  $this->processingFolder = ResourceFactory::getInstance()->getFolderObjectFromCombinedIdentifier($processingFolder);
2851  } else {
2852  if ($this->driver->folderExists($processingFolder) === false) {
2853  $this->processingFolder = $this->createFolder($processingFolder);
2854  } else {
2855  $data = $this->driver->getFolderInfoByIdentifier($processingFolder);
2856  $this->processingFolder = ResourceFactory::getInstance()->createFolderObject($this, $data['identifier'], $data['name']);
2857  }
2858  }
2859  } catch (Exception\InsufficientFolderWritePermissionsException $e) {
2860  $this->processingFolder = GeneralUtility::makeInstance(
2861  InaccessibleFolder::class, $this, $processingFolder, $processingFolder
2862  );
2863  } catch (Exception\ResourcePermissionsUnavailableException $e) {
2864  $this->processingFolder = GeneralUtility::makeInstance(
2865  InaccessibleFolder::class, $this, $processingFolder, $processingFolder
2866  );
2867  }
2868  }
2869  return $this->processingFolder;
2870  }
2871 
2877  public function getDriverType()
2878  {
2879  return $this->storageRecord['driver'];
2880  }
2881 
2887  protected function getIndexer()
2888  {
2889  return GeneralUtility::makeInstance(\TYPO3\CMS\Core\Resource\Index\Indexer::class, $this);
2890  }
2891 
2896  public function setDefault($isDefault)
2897  {
2898  $this->isDefault = (bool)$isDefault;
2899  }
2900 
2904  public function isDefault()
2905  {
2906  return $this->isDefault;
2907  }
2908 }