diff --git a/lib/Files/AbstractLocalNode.php b/lib/Files/AbstractLocalNode.php index fcb477b4..cb4071aa 100644 --- a/lib/Files/AbstractLocalNode.php +++ b/lib/Files/AbstractLocalNode.php @@ -36,30 +36,30 @@ abstract class AbstractLocalNode extends AbstractNode implements NodeInterface /** @var string */ protected $path; - /** @var string */ - protected $basePath; - - /** @var LocalFolder */ + /** @var LocalFolder|null */ protected $parentFolder; - /** @var int */ + /** @var string */ + protected $rootPath; + + /** @var int|null */ protected $permissions; /** * AbstractLocalNode constructor. * - * @param string $path - * @param string $basePath + * @param string $path + * @param string|null $rootPath * * @throws InvalidPathException * @throws NotFoundException */ - public function __construct(string $path, string $basePath) + public function __construct(string $path, string $rootPath = null) { parent::__construct(); $this->path = $this->normalizePath($path); - $this->basePath = realpath($basePath ?: \OC::$SERVERROOT); + $this->rootPath = realpath($rootPath ?? \OC::$SERVERROOT); if (!file_exists($this->getLocalPath())) { throw new NotFoundException(); @@ -73,7 +73,11 @@ public function rename(string $name): NodeInterface { $this->assertValidFileName($name); - $parentNode = $this->getParentNode(); + if (!$this->isDeletable()) { + throw new NotPermittedException(); + } + + $parentNode = $this->getParentFolder(); if ($parentNode->exists($name)) { throw new AlreadyExistsException(); } @@ -114,7 +118,7 @@ public function copy(FolderInterface $targetPath, string $name = null): NodeInte throw new GenericFileException(); } - return new LocalFile($targetPath->getPath() . '/' . $name, $targetPath->basePath); + return new LocalFile($targetPath->getPath() . '/' . $name, $targetPath->getRootPath()); } else { return parent::copy($targetPath, $name); } @@ -132,6 +136,10 @@ public function move(FolderInterface $targetPath, string $name = null): NodeInte $name = $this->getName(); } + if (!$this->isDeletable()) { + throw new NotPermittedException(); + } + if ($targetPath->exists($name)) { throw new AlreadyExistsException(); } @@ -143,26 +151,12 @@ public function move(FolderInterface $targetPath, string $name = null): NodeInte throw new GenericFileException(); } - return new LocalFile($targetPath->getPath() . '/' . $name, $targetPath->getBasePath()); + return new LocalFile($targetPath->getPath() . '/' . $name, $targetPath->getRootPath()); } else { return parent::move($targetPath, $name); } } - /** - * {@inheritDoc} - */ - public function delete(): void - { - if (!$this->isDeletable()) { - throw new NotPermittedException(); - } - - if (!@unlink($this->getLocalPath())) { - throw new GenericFileException(); - } - } - /** * {@inheritDoc} */ @@ -171,20 +165,12 @@ public function getPath(): string return $this->path; } - /** - * @return string - */ - public function getBasePath(): string - { - return $this->basePath; - } - /** * {@inheritDoc} */ public function getLocalPath(): string { - return $this->basePath . (($this->path !== '/') ? $this->path : ''); + return $this->rootPath . (($this->path !== '/') ? $this->path : ''); } /** @@ -198,7 +184,7 @@ public function getName(): string /** * {@inheritDoc} */ - public function getParent(): string + public function getParentPath(): string { if ($this->path === '/') { throw new InvalidPathException(); @@ -210,15 +196,23 @@ public function getParent(): string /** * {@inheritDoc} */ - public function getParentNode(): FolderInterface + public function getParentFolder(): FolderInterface { if ($this->parentFolder === null) { - $this->parentFolder = new LocalFolder($this->getParent(), $this->basePath); + $this->parentFolder = new LocalFolder($this->getParentPath(), $this->rootPath); } return $this->parentFolder; } + /** + * @return string + */ + public function getRootPath(): string + { + return $this->rootPath; + } + /** * {@inheritDoc} */ @@ -265,13 +259,13 @@ public function getPermissions(): int if (is_writable($localPath)) { $this->permissions |= Constants::PERMISSION_UPDATE; - if ($this->isFolder()) { + if ($this->isFolder() && is_executable($localPath)) { $this->permissions |= Constants::PERMISSION_CREATE; } } try { - if (is_writable($this->getParentNode()->getLocalPath())) { + if (is_writable($this->getParentFolder()->getLocalPath())) { $this->permissions |= Constants::PERMISSION_DELETE; } } catch (\Exception $e) { diff --git a/lib/Files/AbstractNode.php b/lib/Files/AbstractNode.php index 8843fa05..d7724787 100644 --- a/lib/Files/AbstractNode.php +++ b/lib/Files/AbstractNode.php @@ -27,6 +27,7 @@ use OCA\CMSPico\Service\MiscService; use OCP\Constants; use OCP\Files\InvalidPathException; +use OCP\Files\NotPermittedException; abstract class AbstractNode implements NodeInterface { @@ -74,6 +75,10 @@ public function move(FolderInterface $targetPath, string $name = null): NodeInte $this->assertValidFileName($name); } + if (!$this->isDeletable()) { + throw new NotPermittedException(); + } + if ($this->isFolder()) { /** @var FolderInterface $this */ $target = $targetPath->newFolder($name ?? $this->getName()); diff --git a/lib/Files/AbstractStorageNode.php b/lib/Files/AbstractStorageNode.php index 1e215bca..ee409fda 100644 --- a/lib/Files/AbstractStorageNode.php +++ b/lib/Files/AbstractStorageNode.php @@ -35,31 +35,31 @@ abstract class AbstractStorageNode extends AbstractNode implements NodeInterface /** @var OCNode */ protected $node; - /** @var string */ + /** @var string|null */ protected $path; - /** @var StorageFolder */ + /** @var StorageFolder|null */ protected $parentFolder; /** * StorageNode constructor. * * @param OCNode $node - * @param string|null $basePath + * @param string|null $parentPath * * @throws InvalidPathException */ - public function __construct(OCNode $node, string $basePath = null) + public function __construct(OCNode $node, string $parentPath = null) { parent::__construct(); $this->node = $node; - if ($basePath !== null) { - $basePath = $this->normalizePath($basePath); + if ($parentPath !== null) { + $parentPath = $this->normalizePath($parentPath); $nodePath = $this->normalizePath($node->getPath()); - $path = (($basePath !== '/') ? $basePath : '') . '/' . basename($nodePath); + $path = (($parentPath !== '/') ? $parentPath : '') . '/' . basename($nodePath); if (substr_compare($nodePath, $path, -strlen($path)) !== 0) { throw new InvalidPathException(); } @@ -73,7 +73,7 @@ public function __construct(OCNode $node, string $basePath = null) */ public function rename(string $name): NodeInterface { - return $this->move($this->getParentNode(), $name); + return $this->move($this->getParentFolder(), $name); } /** @@ -133,7 +133,7 @@ public function getOCNode(): OCNode */ public function getPath(): string { - return $this->path ?: ($this->isFolder() ? '/' : '/' . $this->getName()); + return $this->path ?? ($this->isFolder() ? '/' : '/' . $this->getName()); } /** @@ -171,15 +171,15 @@ public function getName(): string /** * {@inheritDoc} */ - public function getParent(): string + public function getParentPath(): string { - return $this->getParentNode()->getPath(); + return $this->getParentFolder()->getPath(); } /** * {@inheritDoc} */ - public function getParentNode(): FolderInterface + public function getParentFolder(): FolderInterface { if ($this->parentFolder === null) { if ($this->path === null) { @@ -192,9 +192,9 @@ public function getParentNode(): FolderInterface throw new InvalidPathException(); } - $parentBasePath = dirname($this->path); - $parentBasePath = ($parentBasePath !== '/') ? dirname($parentBasePath) : null; - $this->parentFolder = new StorageFolder($ocNode, $parentBasePath); + $grandParentPath = dirname($this->path); + $grandParentPath = ($grandParentPath !== '/') ? dirname($grandParentPath) : null; + $this->parentFolder = new StorageFolder($ocNode, $grandParentPath); } return $this->parentFolder; @@ -242,17 +242,17 @@ public function getPermissions(): int /** * @param OCNode $node - * @param string|null $basePath + * @param string|null $parentPath * * @return AbstractStorageNode * @throws InvalidPathException */ - protected function repackNode(OCNode $node, string $basePath = null): AbstractStorageNode + protected function repackNode(OCNode $node, string $parentPath = null): AbstractStorageNode { if ($node instanceof OCFile) { - return new StorageFile($node, $basePath); + return new StorageFile($node, $parentPath); } elseif ($node instanceof OCFolder) { - return new StorageFolder($node, $basePath); + return new StorageFolder($node, $parentPath); } else { throw new \UnexpectedValueException(); } diff --git a/lib/Files/FolderTrait.php b/lib/Files/FolderTrait.php index 611a734c..5dc645e8 100644 --- a/lib/Files/FolderTrait.php +++ b/lib/Files/FolderTrait.php @@ -24,6 +24,7 @@ namespace OCA\CMSPico\Files; +use OCP\Files\AlreadyExistsException; use OCP\Files\GenericFileException; use OCP\Files\InvalidPathException; use OCP\Files\NotFoundException; @@ -72,6 +73,38 @@ public function getFile(string $path): FileInterface return $file; } + /** + * @param string $fullPath + * + * @return FolderInterface + * @throws AlreadyExistsException + * @throws InvalidPathException + * @throws NotPermittedException + */ + protected function newFolderRecursive(string $fullPath): FolderInterface + { + if ($fullPath !== '/') { + if (!$this->getRootFolder()->exists($fullPath)) { + return $this->getRootFolder()->newFolder($fullPath); + } else { + /** @var FolderInterface $parentFolder */ + $parentFolder = $this->getRootFolder()->get($fullPath); + if (!$parentFolder->isFolder()) { + throw new AlreadyExistsException(); + } + return $parentFolder; + } + } else { + return $this->getRootFolder(); + } + } + + /** + * @return FolderInterface + * @throws InvalidPathException + */ + abstract protected function getRootFolder(): FolderInterface; + /** * @return \Generator * @throws NotPermittedException diff --git a/lib/Files/LocalFile.php b/lib/Files/LocalFile.php index 0885d9af..4dcda03f 100644 --- a/lib/Files/LocalFile.php +++ b/lib/Files/LocalFile.php @@ -34,21 +34,35 @@ class LocalFile extends AbstractLocalNode implements FileInterface /** * LocalFile constructor. * - * @param string $path - * @param string $basePath + * @param string $path + * @param string|null $rootPath * * @throws InvalidPathException * @throws NotFoundException */ - public function __construct(string $path, string $basePath) + public function __construct(string $path, string $rootPath = null) { - parent::__construct($path, $basePath); + parent::__construct($path, $rootPath); if (!is_file($this->getLocalPath())) { throw new InvalidPathException(); } } + /** + * {@inheritDoc} + */ + public function delete(): void + { + if (!$this->isDeletable()) { + throw new NotPermittedException(); + } + + if (!@unlink($this->getLocalPath())) { + throw new GenericFileException(); + } + } + /** * {@inheritDoc} */ diff --git a/lib/Files/LocalFolder.php b/lib/Files/LocalFolder.php index f6f3fb9e..f7ef189d 100644 --- a/lib/Files/LocalFolder.php +++ b/lib/Files/LocalFolder.php @@ -35,21 +35,21 @@ class LocalFolder extends AbstractLocalNode implements FolderInterface { use FolderTrait; - /** @var LocalFolder */ - private $baseFolder; + /** @var LocalFolder|null */ + protected $rootFolder; /** * LocalFolder constructor. * - * @param string $path - * @param string $basePath + * @param string $path + * @param string|null $rootPath * * @throws InvalidPathException * @throws NotFoundException */ - public function __construct(string $path, string $basePath) + public function __construct(string $path, string $rootPath = null) { - parent::__construct($path, $basePath); + parent::__construct($path, $rootPath); if (!is_dir($this->getLocalPath())) { throw new InvalidPathException(); @@ -121,7 +121,7 @@ public function exists(string $path): bool { $path = $this->normalizePath($this->path . '/' . $path); - return file_exists($this->basePath . $path); + return file_exists($this->rootPath . $path); } /** @@ -153,32 +153,17 @@ public function newFolder(string $path): FolderInterface } $path = $this->normalizePath($this->path . '/' . $path); - - $parentPath = dirname($path); - - if ($parentPath !== '/') { - if (!$this->getBaseFolder()->exists($parentPath)) { - $parentFolder = $this->getBaseFolder()->newFolder($parentPath); - } else { - /** @var FolderInterface $parentFolder */ - $parentFolder = $this->getBaseFolder()->get($parentPath); - if (!$parentFolder->isFolder()) { - throw new AlreadyExistsException(); - } - } - } else { - $parentFolder = $this->getBaseFolder(); - } + $parentFolder = $this->newFolderRecursive(dirname($path)); if (!$parentFolder->isCreatable()) { throw new NotPermittedException(); } - if (!@mkdir($this->basePath . '/' . $path)) { + if (!@mkdir($this->rootPath . '/' . $path)) { throw new GenericFileException(); } - return new LocalFolder($path, $this->basePath); + return new LocalFolder($path, $this->rootPath); } /** @@ -191,32 +176,17 @@ public function newFile(string $path): FileInterface } $path = $this->normalizePath($this->path . '/' . $path); - - $parentPath = dirname($path); - - if ($parentPath !== '/') { - if (!$this->getBaseFolder()->exists($parentPath)) { - $parentFolder = $this->getBaseFolder()->newFolder($parentPath); - } else { - /** @var FolderInterface $parentFolder */ - $parentFolder = $this->getBaseFolder()->get($parentPath); - if (!$parentFolder->isFolder()) { - throw new AlreadyExistsException(); - } - } - } else { - $parentFolder = $this->getBaseFolder(); - } + $parentFolder = $this->newFolderRecursive(dirname($path)); if (!$parentFolder->isCreatable()) { throw new NotPermittedException(); } - if (!@touch($this->basePath . '/' . $path)) { + if (!@touch($this->rootPath . '/' . $path)) { throw new GenericFileException(); } - return new LocalFile($path, $this->basePath); + return new LocalFile($path, $this->rootPath); } /** @@ -246,17 +216,17 @@ public function isCreatable(): bool /** * @param string $path * - * @return AbstractLocalNode|null + * @return LocalFolder|LocalFile|null */ - private function createNode(string $path): ?AbstractLocalNode + protected function createNode(string $path): ?AbstractLocalNode { try { if ($path === '/') { - return new LocalFolder('/', $this->basePath); - } elseif (is_file($this->basePath . '/' . $path)) { - return new LocalFile($path, $this->basePath); - } elseif (is_dir($this->basePath . '/' . $path)) { - return new LocalFolder($path, $this->basePath); + return new LocalFolder('/', $this->rootPath); + } elseif (is_file($this->rootPath . '/' . $path)) { + return new LocalFile($path, $this->rootPath); + } elseif (is_dir($this->rootPath . '/' . $path)) { + return new LocalFolder($path, $this->rootPath); } return null; @@ -266,14 +236,14 @@ private function createNode(string $path): ?AbstractLocalNode } /** - * @return LocalFolder + * {@inheritDoc} */ - private function getBaseFolder(): self + protected function getRootFolder(): self { - if ($this->baseFolder === null) { - $this->baseFolder = new LocalFolder('/', $this->basePath); + if ($this->rootFolder === null) { + $this->rootFolder = new LocalFolder('/', $this->rootPath); } - return $this->baseFolder; + return $this->rootFolder; } } diff --git a/lib/Files/NodeInterface.php b/lib/Files/NodeInterface.php index 2f2bdf7f..d43cec02 100644 --- a/lib/Files/NodeInterface.php +++ b/lib/Files/NodeInterface.php @@ -100,13 +100,13 @@ public function getName(): string; * @return string * @throws InvalidPathException */ - public function getParent(): string; + public function getParentPath(): string; /** * @return FolderInterface * @throws InvalidPathException */ - public function getParentNode(): FolderInterface; + public function getParentFolder(): FolderInterface; /** * @return bool diff --git a/lib/Files/StorageFile.php b/lib/Files/StorageFile.php index 893a4021..ef0bbfa1 100644 --- a/lib/Files/StorageFile.php +++ b/lib/Files/StorageFile.php @@ -36,13 +36,13 @@ class StorageFile extends AbstractStorageNode implements FileInterface * StorageFile constructor. * * @param OCFile $file - * @param string|null $basePath + * @param string|null $parentPath * * @throws InvalidPathException */ - public function __construct(OCFile $file, string $basePath = null) + public function __construct(OCFile $file, string $parentPath = null) { - parent::__construct($file, $basePath); + parent::__construct($file, $parentPath); } /** diff --git a/lib/Files/StorageFolder.php b/lib/Files/StorageFolder.php index 0cca9468..739540b2 100644 --- a/lib/Files/StorageFolder.php +++ b/lib/Files/StorageFolder.php @@ -57,22 +57,25 @@ class StorageFolder extends AbstractStorageNode implements FolderInterface /** @var IEventDispatcher */ private $eventDispatcher; + /** @var StorageFolder|null */ + protected $rootFolder; + /** * StorageFolder constructor. * * @param OCFolder $folder - * @param string|null $basePath + * @param string|null $parentPath * * @throws InvalidPathException */ - public function __construct(OCFolder $folder, string $basePath = null) + public function __construct(OCFolder $folder, string $parentPath = null) { $this->tempManager = \OC::$server->getTempManager(); $this->connection = \OC::$server->query(IDBConnection::class); $this->logger = \OC::$server->query(ILogger::class); $this->eventDispatcher = \OC::$server->query(IEventDispatcher::class); - parent::__construct($folder, $basePath); + parent::__construct($folder, $parentPath); } /** @@ -105,9 +108,9 @@ public function listing(): array */ protected function getGenerator(): \Generator { - $basePath = $this->getPath(); + $parentPath = $this->getPath(); foreach ($this->node->getDirectoryListing() as $node) { - yield $this->repackNode($node, $basePath); + yield $this->repackNode($node, $parentPath); } } @@ -116,10 +119,8 @@ protected function getGenerator(): \Generator */ public function exists(string $path): bool { - // check for root path breakouts - $this->getBasePath($path); - - return $this->node->nodeExists($path); + $path = $this->normalizePath($this->getPath() . '/' . $path); + return $this->getRootFolder()->getOCNode()->nodeExists($path); } /** @@ -127,8 +128,9 @@ public function exists(string $path): bool */ public function get(string $path): NodeInterface { - $basePath = $this->getBasePath($path); - return $this->repackNode($this->node->get($path), $basePath); + $path = $this->normalizePath($this->getPath() . '/' . $path); + $parentPath = ($path !== '/') ? dirname($path) : null; + return $this->repackNode($this->getRootFolder()->getOCNode()->get($path), $parentPath); } /** @@ -140,8 +142,22 @@ public function newFolder(string $path): FolderInterface throw new AlreadyExistsException(); } - $basePath = $this->getBasePath($path); - return new StorageFolder($this->node->newFolder($path), $basePath); + $path = $this->normalizePath($this->getPath() . '/' . $path); + + $name = basename($path); + $parentPath = dirname($path); + + /** @var StorageFolder $parentFolder */ + $parentFolder = $this->newFolderRecursive($parentPath); + + if (!$parentFolder->isCreatable()) { + throw new NotPermittedException(); + } + + return new StorageFolder( + $parentFolder->getOCNode()->newFolder($name), + ($path !== '/') ? $parentPath : null + ); } /** @@ -153,8 +169,22 @@ public function newFile(string $path): FileInterface throw new AlreadyExistsException(); } - $basePath = $this->getBasePath($path); - return new StorageFile($this->node->newFile($path), $basePath); + $path = $this->normalizePath($this->getPath() . '/' . $path); + + $name = basename($path); + $parentPath = dirname($path); + + /** @var StorageFolder $parentFolder */ + $parentFolder = $this->newFolderRecursive($parentPath); + + if (!$parentFolder->isCreatable()) { + throw new NotPermittedException(); + } + + return new StorageFile( + $parentFolder->getOCNode()->newFile($name), + ($path !== '/') ? $parentPath : null + ); } /** @@ -188,14 +218,23 @@ public function isCreatable(): bool } /** - * @param string $path - * - * @return string|null - * @throws InvalidPathException + * {@inheritDoc} */ - private function getBasePath(string $path): ?string + protected function getRootFolder(): self { - $path = $this->normalizePath($this->getPath() . '/' . $path); - return ($path !== '/') ? dirname($path) : null; + if ($this->getPath() === '/') { + return $this; + } + + if ($this->rootFolder === null) { + $ocFolder = $this->node; + for ($i = 0; $i < substr_count($this->getPath(), '/'); $i++) { + $ocFolder = $ocFolder->getParent(); + } + + $this->rootFolder = new StorageFolder($ocFolder); + } + + return $this->rootFolder; } } diff --git a/lib/Model/TemplateFile.php b/lib/Model/TemplateFile.php index e224e72f..367da0f9 100644 --- a/lib/Model/TemplateFile.php +++ b/lib/Model/TemplateFile.php @@ -101,15 +101,15 @@ public function getName(): string /** * {@inheritDoc} */ - public function getParent(): string + public function getParentPath(): string { - return $this->file->getParent(); + return $this->file->getParentPath(); } /** * {@inheritDoc} */ - public function getParentNode(): FolderInterface + public function getParentFolder(): FolderInterface { throw new InvalidPathException(); } diff --git a/lib/Service/FileService.php b/lib/Service/FileService.php index 88ff40df..d2f08a5c 100644 --- a/lib/Service/FileService.php +++ b/lib/Service/FileService.php @@ -79,8 +79,8 @@ public function __construct(IRootFolder $rootFolder, ConfigService $configServic public function getPublicFolder(string $folderName = null): FolderInterface { if ($this->publicFolder === null) { - $publicFolderBasePath = Application::getAppPath() . '/' . self::APPDATA_PUBLIC; - $this->publicFolder = new LocalFolder('/', $publicFolderBasePath); + $publicFolderPath = Application::getAppPath() . '/' . self::APPDATA_PUBLIC; + $this->publicFolder = new LocalFolder('/', $publicFolderPath); } if ($folderName) { @@ -104,8 +104,8 @@ public function getPublicFolder(string $folderName = null): FolderInterface public function getSystemFolder(string $folderName = null): FolderInterface { if ($this->systemFolder === null) { - $systemFolderBasePath = Application::getAppPath() . '/' . self::APPDATA_SYSTEM; - $this->systemFolder = new LocalFolder('/', $systemFolderBasePath); + $systemFolderPath = Application::getAppPath() . '/' . self::APPDATA_SYSTEM; + $this->systemFolder = new LocalFolder('/', $systemFolderPath); } if ($folderName) { diff --git a/lib/Service/TemplatesService.php b/lib/Service/TemplatesService.php index 9bddaf05..f3752705 100644 --- a/lib/Service/TemplatesService.php +++ b/lib/Service/TemplatesService.php @@ -270,9 +270,9 @@ public function installTemplate(Website $website, string $templateName): void $templateFile = new TemplateFile($file); try { - $targetFolder = $websiteFolder->getFolder($templateFile->getParent()); + $targetFolder = $websiteFolder->getFolder($templateFile->getParentPath()); } catch (NotFoundException $e) { - $targetFolder = $websiteFolder->newFolder($templateFile->getParent()); + $targetFolder = $websiteFolder->newFolder($templateFile->getParentPath()); } if ($templateFile->getName() === 'empty') {