diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php index 99570635..a4395dbb 100644 --- a/lib/AppInfo/Application.php +++ b/lib/AppInfo/Application.php @@ -13,6 +13,7 @@ use OCA\Files\Event\LoadAdditionalScriptsEvent; use OCA\FilesLock\Capability; use OCA\FilesLock\Listeners\LoadAdditionalScripts; +use OCA\FilesLock\Listeners\PropfindPropertiesListener; use OCA\FilesLock\LockProvider; use OCA\FilesLock\Service\FileService; use OCA\FilesLock\Service\LockService; @@ -21,6 +22,7 @@ use OCP\AppFramework\Bootstrap\IBootContext; use OCP\AppFramework\Bootstrap\IBootstrap; use OCP\AppFramework\Bootstrap\IRegistrationContext; +use OCP\Files\Events\BeforeRemotePropfindEvent; use OCP\Files\Lock\ILockManager; use OCP\IUserSession; use OCP\Server; @@ -48,6 +50,10 @@ public function register(IRegistrationContext $context): void { LoadAdditionalScriptsEvent::class, LoadAdditionalScripts::class ); + $context->registerEventListener( + BeforeRemotePropfindEvent::class, + PropfindPropertiesListener::class + ); } public function boot(IBootContext $context): void { diff --git a/lib/Listeners/PropfindPropertiesListener.php b/lib/Listeners/PropfindPropertiesListener.php new file mode 100644 index 00000000..a62f9067 --- /dev/null +++ b/lib/Listeners/PropfindPropertiesListener.php @@ -0,0 +1,37 @@ + + */ +class PropfindPropertiesListener implements IEventListener { + public function handle(Event $event): void { + if (!($event instanceof BeforeRemotePropfindEvent)) { + return; + } + + $event->addProperties([ + Application::DAV_PROPERTY_LOCK, + Application::DAV_PROPERTY_LOCK_OWNER, + Application::DAV_PROPERTY_LOCK_OWNER_DISPLAYNAME, + Application::DAV_PROPERTY_LOCK_OWNER_TYPE, + Application::DAV_PROPERTY_LOCK_EDITOR, + Application::DAV_PROPERTY_LOCK_TIME, + Application::DAV_PROPERTY_LOCK_TIMEOUT, + Application::DAV_PROPERTY_LOCK_TOKEN, + ]); + } +} diff --git a/lib/LockProvider.php b/lib/LockProvider.php index cb617853..fdc90791 100644 --- a/lib/LockProvider.php +++ b/lib/LockProvider.php @@ -20,17 +20,13 @@ public function __construct(LockService $lockService) { } public function getLocks(int $fileId): array { - try { - $lock = $this->lockService->getLockFromFileId($fileId); - $this->lockService->injectMetadata($lock); - } catch (Exceptions\LockNotFoundException $e) { + $lock = $this->lockService->getLockForNodeId($fileId); + if (!$lock) { return []; } - if ($lock) { - return [$lock]; - } - return []; + $this->lockService->injectMetadata($lock); + return [$lock]; } /** diff --git a/lib/Service/LockService.php b/lib/Service/LockService.php index 4730d4c7..74d25665 100644 --- a/lib/Service/LockService.php +++ b/lib/Service/LockService.php @@ -10,6 +10,9 @@ namespace OCA\FilesLock\Service; use Exception; +use OC\Files\Storage\DAV; +use OC\Files\Storage\Wrapper\Wrapper; +use OCA\FilesLock\AppInfo\Application; use OCA\FilesLock\Db\LocksRequest; use OCA\FilesLock\Exceptions\LockNotFoundException; use OCA\FilesLock\Exceptions\UnauthorizedUnlockException; @@ -19,6 +22,7 @@ use OCP\Constants; use OCP\EventDispatcher\IEventDispatcher; use OCP\Files\InvalidPathException; +use OCP\Files\IRootFolder; use OCP\Files\Lock\ILock; use OCP\Files\Lock\LockContext; use OCP\Files\Lock\OwnerLockedException; @@ -65,6 +69,7 @@ public function __construct( IUserSession $userSession, IRequest $request, LoggerInterface $logger, + private IRootFolder $rootFolder, ) { $this->l10n = $l10n; $this->userManager = $userManager; @@ -90,8 +95,9 @@ public function getLockForNodeId(int $nodeId) { try { $this->lockCache[$nodeId] = $this->getLockFromFileId($nodeId); - } catch (LockNotFoundException $e) { - $this->lockCache[$nodeId] = false; + } catch (LockNotFoundException) { + $remoteLock = $this->getRemoteLockFromDav($nodeId); + $this->lockCache[$nodeId] = $remoteLock ?: false; } return $this->lockCache[$nodeId]; @@ -110,7 +116,6 @@ public function getLockForNodeIds(array $nodeIds): array { $locks[$nodeId] = $this->lockCache[$nodeId]; } else { $locksToRequest[] = $nodeId; - $this->lockCache[$nodeId] = false; } } if (count($locksToRequest) === 0) { @@ -394,6 +399,61 @@ function (FileLock $lock) { $this->locksRequest->removeIds($ids); } + public function getRemoteLockFromDav(int $nodeId): ?FileLock { + try { + $user = $this->userSession->getUser(); + if (!$user) { + return null; + } + + $userFolder = $this->rootFolder->getUserFolder($user->getUID()); + $node = $userFolder->getFirstNodeById($nodeId); + if (empty($node)) { + return null; + } + + $storage = $node->getStorage(); + + while ($storage->instanceOfStorage(Wrapper::class)) { + $storage = $storage->getWrapperStorage(); + } + + if (!$storage->instanceOfStorage(DAV::class)) { + return null; + } + + $path = $node->getInternalPath(); + $storage->getMetaData($path); + + if (!method_exists($storage, 'getPropfindPropertyValue')) { + return null; + } + + $isLocked = $storage->getPropfindPropertyValue($path, Application::DAV_PROPERTY_LOCK); + if (!$isLocked) { + return null; + } + + $fileLock = new FileLock(); + $fileLock->import([ + 'fileId' => $nodeId, + 'owner' => (string)($storage->getPropfindPropertyValue($path, Application::DAV_PROPERTY_LOCK_OWNER_DISPLAYNAME) ?? ''), + 'type' => (int)($storage->getPropfindPropertyValue($path, Application::DAV_PROPERTY_LOCK_OWNER_TYPE) ?? 0), + 'creation' => (int)($storage->getPropfindPropertyValue($path, Application::DAV_PROPERTY_LOCK_TIME) ?? 0), + 'ttl' => (int)($storage->getPropfindPropertyValue($path, Application::DAV_PROPERTY_LOCK_TIMEOUT) ?? 0), + 'token' => (string)($storage->getPropfindPropertyValue($path, Application::DAV_PROPERTY_LOCK_TOKEN) ?? ''), + ]); + + $remoteHost = parse_url($storage->getRemote(), PHP_URL_HOST); + $fileLock->setDisplayName($fileLock->getDisplayName() . '@' . $remoteHost); + + return $fileLock; + } catch (\Exception $e) { + $this->logger->error('Failed to get remote lock from DAV: ' . $e->getMessage(), ['exception' => $e]); + return null; + } + } + private function propagateEtag(LockContext $lockContext): void { $node = $lockContext->getNode(); $node->getStorage()->getCache()->update($node->getId(), [ diff --git a/lib/Storage/LockWrapper.php b/lib/Storage/LockWrapper.php index 0aaee14b..33beca1c 100644 --- a/lib/Storage/LockWrapper.php +++ b/lib/Storage/LockWrapper.php @@ -134,7 +134,7 @@ public function rename($path1, $path2): bool { //This is a rename of the transfer file to the original file if (strpos($part, '.ocTransferId') === 0) { return $this->checkPermissions($path2, Constants::PERMISSION_CREATE) - && parent::rename($path1, $path2); + && parent::rename($path1, $path2); } } $permissions @@ -145,9 +145,9 @@ public function rename($path1, $path2): bool { } return $this->checkPermissions($sourceParent, Constants::PERMISSION_DELETE) - && $this->checkPermissions($path1, Constants::PERMISSION_UPDATE & Constants::PERMISSION_READ) - && $this->checkPermissions($path2, $permissions) - && parent::rename($path1, $path2); + && $this->checkPermissions($path1, Constants::PERMISSION_UPDATE & Constants::PERMISSION_READ) + && $this->checkPermissions($path2, $permissions) + && parent::rename($path1, $path2); } public function copy($path1, $path2): bool { @@ -155,10 +155,10 @@ public function copy($path1, $path2): bool { = $this->file_exists($path2) ? Constants::PERMISSION_UPDATE : Constants::PERMISSION_CREATE; return $this->checkPermissions($path2, $permissions) - && $this->checkPermissions( - $path1, Constants::PERMISSION_READ - ) - && parent::copy($path1, $path2); + && $this->checkPermissions( + $path1, Constants::PERMISSION_READ + ) + && parent::copy($path1, $path2); } public function touch($path, $mtime = null): bool { @@ -174,12 +174,12 @@ public function mkdir($path): bool { public function rmdir($path): bool { return $this->checkPermissions($path, Constants::PERMISSION_DELETE) - && parent::rmdir($path); + && parent::rmdir($path); } public function unlink($path): bool { return $this->checkPermissions($path, Constants::PERMISSION_DELETE) - && parent::unlink($path); + && parent::unlink($path); } public function file_put_contents($path, $data): int|float|false {