diff --git a/lib/Service/SecureViewService.php b/lib/Service/SecureViewService.php index 894dc2f2f2..ce2c6bd027 100644 --- a/lib/Service/SecureViewService.php +++ b/lib/Service/SecureViewService.php @@ -28,16 +28,40 @@ public function isEnabled(): bool { } /** - * @throws NotFoundException + * Prepares metadata and context needed to decide if SecureView restrictions (such as watermarking or + * download blocking) may apply to a given file or folder. Ensures filecache metadata is available by + * triggering backend cache updates for files if requested, and supports external storages. Delegates + * the actual policy decision to shouldWatermark(). + * + * @param string $path Relative storage path to check. + * @param IStorage $storage Storage backend instance. + * @param bool $tryOpen Whether to attempt opening files to force/cache refresh file metadata. + * @return bool True if SecureView restrictions may apply (per shouldWatermark()), false otherwise. + * @throws NotFoundException If neither the file nor its parent are present in the filecache/remote storage. */ public function shouldSecure(string $path, IStorage $storage, bool $tryOpen = true): bool { - if ($tryOpen) { - // pity… fopen() does not document any possible Exceptions + // First: try to get the cache entry for $path + $cacheEntry = $storage->getCache()->get($path); + + $isDir = false; + // Prefer cache for fast directory check; fall back to (potentially slower) is_dir() if needed + if ($cacheEntry && !empty($cacheEntry['mimetype']) && $cacheEntry['mimetype'] === 'httpd/unix-directory') { + $isDir = true; + } elseif (!$cacheEntry && method_exists($storage, 'is_dir') && $storage->is_dir($path)) { + $isDir = true; + } + + if ($tryOpen && !$isDir) { + // Attempt to open the file to potentially trigger cache entry creation $fp = $storage->fopen($path, 'r'); - fclose($fp); + if ($fp !== false && is_resource($fp)) { + fclose($fp); + } + // Re-fetch the cache entry after the 'poke' + $cacheEntry = $storage->getCache()->get($path); } - $cacheEntry = $storage->getCache()->get($path); + // Fallback to parent *only if cache entry still missing after poke* if (!$cacheEntry) { $parent = dirname($path); if ($parent === '.') { @@ -45,16 +69,25 @@ public function shouldSecure(string $path, IStorage $storage, bool $tryOpen = tr } $cacheEntry = $storage->getCache()->get($parent); if (!$cacheEntry) { - throw new NotFoundException(sprintf('Could not find cache entry for path and parent of %s within storage %s ', $path, $storage->getId())); + throw new NotFoundException(sprintf( + 'Could not find cache entry for path and parent of %s within storage %s', + $path, $storage->getId() + )); } } + // Now carry on with original SecureView logic... $isSharedStorage = $storage->instanceOfStorage(ISharedStorage::class); /** @noinspection PhpPossiblePolymorphicInvocationInspection */ /** @psalm-suppress UndefinedMethod **/ $share = $isSharedStorage ? $storage->getShare() : null; $userId = $this->userSession->getUser()?->getUID(); - return $this->permissionManager->shouldWatermark($cacheEntry, $userId, $share, $storage->getOwner($path) ?: null); + return $this->permissionManager->shouldWatermark( + $cacheEntry, + $userId, + $share, + $storage->getOwner($path) ?: null + ); } }