/** * Constructor * * @param \TYPO3\Flow\Package\PackageManager $packageManager the package manager which knows this package * @param string $packageKey Key of this package * @param string $packagePath Absolute path to the location of the package's composer manifest * @param string $classesPath Path the classes of the package are in, relative to $packagePath. Optional, read from Composer manifest if not set. * @param string $manifestPath Path the composer manifest of the package, relative to $packagePath. Optional, defaults to ''. * @throws \TYPO3\Flow\Package\Exception\InvalidPackageKeyException if an invalid package key was passed * @throws \TYPO3\Flow\Package\Exception\InvalidPackagePathException if an invalid package path was passed * @throws \TYPO3\Flow\Package\Exception\InvalidPackageManifestException if no composer manifest file could be found */ public function __construct(\TYPO3\Flow\Package\PackageManager $packageManager, $packageKey, $packagePath, $classesPath = NULL, $manifestPath = '') { if (!\TYPO3\CMS\Core\Package\PackageManager::isPackageKeyValid($packageKey)) { throw new \TYPO3\Flow\Package\Exception\InvalidPackageKeyException('"' . $packageKey . '" is not a valid package key.', 1217959511); } if (!(@is_dir($packagePath) || \TYPO3\Flow\Utility\Files::is_link($packagePath) && is_dir(\TYPO3\Flow\Utility\Files::getNormalizedPath($packagePath)))) { throw new \TYPO3\Flow\Package\Exception\InvalidPackagePathException(sprintf('Tried to instantiate a package object for package "%s" with a non-existing package path "%s". Either the package does not exist anymore, or the code creating this object contains an error.', $packageKey, $packagePath), 1166631890); } if (substr($packagePath, -1, 1) !== '/') { throw new \TYPO3\Flow\Package\Exception\InvalidPackagePathException(sprintf('The package path "%s" provided for package "%s" has no trailing forward slash.', $packagePath, $packageKey), 1166633722); } if ($classesPath[1] === '/') { throw new \TYPO3\Flow\Package\Exception\InvalidPackagePathException(sprintf('The package classes path provided for package "%s" has a leading forward slash.', $packageKey), 1334841321); } if (!@file_exists($packagePath . $manifestPath . 'ext_emconf.php')) { throw new \TYPO3\Flow\Package\Exception\InvalidPackageManifestException(sprintf('No ext_emconf file found for package "%s". Please create one at "%sext_emconf.php".', $packageKey, $manifestPath), 1360403545); } $this->packageManager = $packageManager; $this->manifestPath = $manifestPath; $this->packageKey = $packageKey; $this->packagePath = \TYPO3\Flow\Utility\Files::getNormalizedPath($packagePath); $this->classesPath = \TYPO3\Flow\Utility\Files::getNormalizedPath(\TYPO3\Flow\Utility\Files::concatenatePaths(array($this->packagePath, self::DIRECTORY_CLASSES))); try { $this->getComposerManifest(); } catch (\TYPO3\Flow\Package\Exception\MissingPackageManifestException $exception) { $this->getExtensionEmconf($packageKey, $this->packagePath); } $this->loadFlagsFromComposerManifest(); if ($this->objectManagementEnabled === NULL) { $this->objectManagementEnabled = FALSE; } }
/** * Because mirrorFile() uses touch() we can't use vfs to mock the file system. * * @test */ public function mirrorFileSymLinksTheGivenFileIfTheSettingSaysSo() { $sourcePathAndFilename = tempnam('FlowFileSystemPublishingTargetTestSource', ''); $targetPathAndFilename = tempnam('FlowFileSystemPublishingTargetTestTarget', ''); file_put_contents($sourcePathAndFilename, 'some data'); touch($sourcePathAndFilename, time() - 5); $settings = array('resource' => array('publishing' => array('fileSystem' => array('mirrorMode' => 'link')))); $publishingTarget = $this->getAccessibleMock('TYPO3\\Flow\\Resource\\Publishing\\FileSystemPublishingTarget', array('dummy')); $publishingTarget->_set('settings', $settings); $publishingTarget->_call('mirrorFile', $sourcePathAndFilename, $targetPathAndFilename, TRUE); $this->assertFileEquals($sourcePathAndFilename, $targetPathAndFilename); $this->assertTrue(Files::is_link($targetPathAndFilename)); unlink($sourcePathAndFilename); unlink($targetPathAndFilename); }
/** * @test */ public function is_linkReturnsFalseForStreamWrapperPaths() { $targetPath = 'vfs://Foo/Bar'; if (!is_dir($targetPath)) { Files::createDirectoryRecursively($targetPath); } $this->assertFalse(Files::is_link($targetPath)); }
/** * Constructor * * @param \TYPO3\Flow\Package\PackageManager $packageManager the package manager which knows this package * @param string $packageKey Key of this package * @param string $packagePath Absolute path to the location of the package's composer manifest * @param string $classesPath Path the classes of the package are in, relative to $packagePath. Optional, PSR-0/PSR-4 mappings of the composer manifest overrule this argument, if present * @param string $manifestPath Path the composer manifest of the package, relative to $packagePath. Optional, defaults to '' * @throws \TYPO3\Flow\Package\Exception\InvalidPackageKeyException if an invalid package key was passed * @throws \TYPO3\Flow\Package\Exception\InvalidPackagePathException if an invalid package path was passed * @throws \TYPO3\Flow\Package\Exception\InvalidPackageManifestException if no composer manifest file could be found */ public function __construct(PackageManager $packageManager, $packageKey, $packagePath, $classesPath = null, $manifestPath = '') { if (preg_match(self::PATTERN_MATCH_PACKAGEKEY, $packageKey) !== 1) { throw new Exception\InvalidPackageKeyException('"' . $packageKey . '" is not a valid package key.', 1217959510); } if (!(is_dir($packagePath) || Files::is_link($packagePath) && is_dir(Files::getNormalizedPath($packagePath)))) { throw new Exception\InvalidPackagePathException(sprintf('Tried to instantiate a package object for package "%s" with a non-existing package path "%s". Either the package does not exist anymore, or the code creating this object contains an error.', $packageKey, $packagePath), 1166631889); } if (substr($packagePath, -1, 1) !== '/') { throw new Exception\InvalidPackagePathException(sprintf('The package path "%s" provided for package "%s" has no trailing forward slash.', $packagePath, $packageKey), 1166633720); } if (substr($classesPath, 0, 1) === '/') { throw new Exception\InvalidPackagePathException(sprintf('The package classes path provided for package "%s" has a leading forward slash.', $packageKey), 1334841320); } if (!file_exists($packagePath . $manifestPath . 'composer.json')) { throw new Exception\InvalidPackageManifestException(sprintf('No composer manifest file found for package "%s". Please create one at "%scomposer.json".', $packageKey, $packagePath . $manifestPath), 1349776393); } $this->packageManager = $packageManager; $this->manifestPath = $manifestPath; $this->packageKey = $packageKey; $this->packagePath = Files::getNormalizedPath($packagePath); $autoloadType = $this->getAutoloadType(); if ($autoloadType === self::AUTOLOADER_TYPE_PSR0 || $autoloadType === self::AUTOLOADER_TYPE_PSR4) { $autoloadPath = $this->getComposerManifest('autoload')->{$autoloadType}->{$this->getNamespace()}; if (is_array($autoloadPath)) { $autoloadPath = $autoloadPath[0]; } $this->classesPath = Files::getNormalizedPath($this->packagePath . $autoloadPath); } else { $this->classesPath = Files::getNormalizedPath($this->packagePath . $classesPath); } }
/** * Depending on the settings of this publishing target copies the specified file * or creates a symbolic link. * * @param string $sourcePathAndFilename * @param string $targetPathAndFilename * @param boolean $createDirectoriesIfNecessary * @return void * @throws \TYPO3\Flow\Resource\Exception */ protected function mirrorFile($sourcePathAndFilename, $targetPathAndFilename, $createDirectoriesIfNecessary = FALSE) { if ($createDirectoriesIfNecessary === TRUE) { \TYPO3\Flow\Utility\Files::createDirectoryRecursively(dirname($targetPathAndFilename)); } switch ($this->settings['resource']['publishing']['fileSystem']['mirrorMode']) { case 'copy': copy($sourcePathAndFilename, $targetPathAndFilename); touch($targetPathAndFilename, filemtime($sourcePathAndFilename)); break; case 'link': if (file_exists($targetPathAndFilename)) { if (\TYPO3\Flow\Utility\Files::is_link($targetPathAndFilename) && $this->realpath($targetPathAndFilename) === $this->realpath($sourcePathAndFilename)) { break; } unlink($targetPathAndFilename); symlink($sourcePathAndFilename, $targetPathAndFilename); } else { symlink($sourcePathAndFilename, $targetPathAndFilename); } break; default: throw new \TYPO3\Flow\Resource\Exception('An invalid mirror mode (' . $this->settings['resource']['publishing']['fileSystem']['mirrorMode'] . ') has been configured.', 1256133400); } if (!file_exists($targetPathAndFilename)) { throw new \TYPO3\Flow\Resource\Exception('The resource "' . $sourcePathAndFilename . '" could not be mirrored.', 1207255453); } }
/** * Publishes the specified directory to this target, with the given relative path. * * @param string $sourcePath Absolute path to the source directory * @param string $relativeTargetPathAndFilename relative path and filename in the target directory * @throws Exception * @return void */ protected function publishDirectory($sourcePath, $relativeTargetPathAndFilename) { $targetPathAndFilename = $this->path . $relativeTargetPathAndFilename; if (@stat($sourcePath) === false) { throw new Exception(sprintf('Could not publish directory "%s" into resource publishing target "%s" because the source is not accessible (file stat failed).', $sourcePath, $this->name), 1416244512); } if (!file_exists(dirname($targetPathAndFilename))) { Files::createDirectoryRecursively(dirname($targetPathAndFilename)); } try { if (Files::is_link($targetPathAndFilename)) { Files::unlink($targetPathAndFilename); } $temporaryTargetPathAndFilename = uniqid($targetPathAndFilename . '.') . '.tmp'; symlink($sourcePath, $temporaryTargetPathAndFilename); $result = rename($temporaryTargetPathAndFilename, $targetPathAndFilename); } catch (\Exception $exception) { $result = false; } if ($result === false) { throw new Exception(sprintf('Could not publish "%s" into resource publishing target "%s" because the source directory could not be symlinked at target location.', $sourcePath, $this->name), 1416244515, isset($exception) ? $exception : null); } $this->systemLogger->log(sprintf('FileSystemSymlinkTarget: Published directory. (target: %s, file: %s)', $this->name, $relativeTargetPathAndFilename), LOG_DEBUG); }
/** * Check write permissions for folders used for writing files * * @return mixed */ protected function checkFilePermissions() { foreach ($this->requiredWritableFolders as $folder) { $folderPath = FLOW_PATH_ROOT . $folder; if (!is_dir($folderPath) && !\TYPO3\Flow\Utility\Files::is_link($folderPath)) { try { \TYPO3\Flow\Utility\Files::createDirectoryRecursively($folderPath); } catch (\TYPO3\Flow\Utility\Exception $exception) { return new Error('Unable to create folder "%s". Check your file permissions (did you use flow:core:setfilepermissions?).', 1330363887, array($folderPath)); } } if (!is_writable($folderPath)) { return new Error('The folder "%s" is not writable. Check your file permissions (did you use flow:core:setfilepermissions?)', 1330372964, array($folderPath)); } } return NULL; }
/** * Returns the publish path and filename to be used to publish the specified persistent resource * * @Flow\Around("method(TYPO3\Flow\Resource\Publishing\FileSystemPublishingTarget->buildPersistentResourcePublishPathAndFilename()) && setting(TYPO3.Flow.security.enable)") * @param \TYPO3\Flow\Aop\JoinPointInterface $joinPoint The current join point * @return mixed Result of the target method */ public function rewritePersistentResourcePublishPathAndFilenameForPrivateResources(\TYPO3\Flow\Aop\JoinPointInterface $joinPoint) { $resource = $joinPoint->getMethodArgument('resource'); /** @var $configuration \TYPO3\Flow\Security\Authorization\Resource\SecurityPublishingConfiguration */ $configuration = $resource->getPublishingConfiguration(); $returnFilename = $joinPoint->getMethodArgument('returnFilename'); if ($configuration === NULL || $configuration instanceof \TYPO3\Flow\Security\Authorization\Resource\SecurityPublishingConfiguration === FALSE) { return $joinPoint->getAdviceChain()->proceed($joinPoint); } $publishingPath = FALSE; $allowedRoles = $configuration->getAllowedRoles(); if (count(array_intersect($allowedRoles, $this->securityContext->getRoles())) > 0) { $publishingPath = \TYPO3\Flow\Utility\Files::concatenatePaths(array($joinPoint->getProxy()->getResourcesPublishingPath(), 'Persistent/', $this->session->getID())) . '/'; $filename = $resource->getResourcePointer()->getHash() . '.' . $resource->getFileExtension(); \TYPO3\Flow\Utility\Files::createDirectoryRecursively($publishingPath); $this->accessRestrictionPublisher->publishAccessRestrictionsForPath($publishingPath); if ($this->settings['resource']['publishing']['fileSystem']['mirrorMode'] === 'link') { foreach ($allowedRoles as $role) { $roleDirectory = \TYPO3\Flow\Utility\Files::concatenatePaths(array($this->environment->getPathToTemporaryDirectory(), 'PrivateResourcePublishing/', $role)); \TYPO3\Flow\Utility\Files::createDirectoryRecursively($roleDirectory); if (file_exists($publishingPath . $role)) { if (\TYPO3\Flow\Utility\Files::is_link(\TYPO3\Flow\Utility\Files::concatenatePaths(array($publishingPath, $role))) && realpath(\TYPO3\Flow\Utility\Files::concatenatePaths(array($publishingPath, $role))) === $roleDirectory) { continue; } unlink($publishingPath . $role); symlink($roleDirectory, \TYPO3\Flow\Utility\Files::concatenatePaths(array($publishingPath, $role))); } else { symlink($roleDirectory, \TYPO3\Flow\Utility\Files::concatenatePaths(array($publishingPath, $role))); } } $publishingPath = \TYPO3\Flow\Utility\Files::concatenatePaths(array($publishingPath, $allowedRoles[0])) . '/'; } if ($returnFilename === TRUE) { $publishingPath = \TYPO3\Flow\Utility\Files::concatenatePaths(array($publishingPath, $filename)); } } return $publishingPath; }