diff --git a/apps/files_sharing/lib/Controller/ShareController.php b/apps/files_sharing/lib/Controller/ShareController.php
index 3c51a95f1d501..bc078cb0b2bde 100644
--- a/apps/files_sharing/lib/Controller/ShareController.php
+++ b/apps/files_sharing/lib/Controller/ShareController.php
@@ -14,6 +14,7 @@
use OCA\Files_Sharing\Event\ShareLinkAccessedEvent;
use OCP\Accounts\IAccountManager;
use OCP\AppFramework\AuthPublicShareController;
+use OCP\AppFramework\Http;
use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
use OCP\AppFramework\Http\Attribute\OpenAPI;
use OCP\AppFramework\Http\Attribute\PublicPage;
@@ -29,6 +30,7 @@
use OCP\Files\Folder;
use OCP\Files\IRootFolder;
use OCP\Files\NotFoundException;
+use OCP\Files\NotPermittedException;
use OCP\HintException;
use OCP\IConfig;
use OCP\IL10N;
@@ -360,49 +362,75 @@ public function downloadShare($token, $files = null, $path = '') {
$share = $this->shareManager->getShareByToken($token);
if (!($share->getPermissions() & Constants::PERMISSION_READ)) {
- return new DataResponse('Share has no read permission');
+ return new DataResponse('Share has no read permission', Http::STATUS_FORBIDDEN);
}
$attributes = $share->getAttributes();
if ($attributes?->getAttribute('permissions', 'download') === false) {
- return new DataResponse('Share has no download permission');
+ return new DataResponse('Share has no download permission', Http::STATUS_FORBIDDEN);
}
if (!$this->validateShare($share)) {
throw new NotFoundException();
}
+ if ($share->getHideDownload()) {
+ // download API does not work if hidden - use the DAV endpoint for previews
+ throw new NotFoundException();
+ }
+
$node = $share->getNode();
- if ($node instanceof Folder) {
- // Directory share
+ if ($path !== '') {
+ if (!$node instanceof Folder) {
+ return new NotFoundResponse();
+ }
- // Try to get the path
- if ($path !== '') {
+ try {
+ $node = $node->get($path);
+ } catch (NotFoundException|NotPermittedException) {
+ $this->emitAccessShareHook($share, 404, 'Share not found');
+ $this->emitShareAccessEvent($share, self::SHARE_DOWNLOAD, 404, 'Share not found');
+ return new NotFoundResponse();
+ }
+ }
+
+ if ($files !== null) {
+ if (!$node instanceof Folder) {
+ return new NotFoundResponse();
+ }
+
+ $filesParam = json_decode($files, true);
+ if (!is_array($filesParam)) {
try {
- $node = $node->get($path);
- } catch (NotFoundException $e) {
+ // legacy wise this allows also passing the filename
+ $node = $node->get($files);
+ $files = null;
+ } catch (NotFoundException|NotPermittedException) {
$this->emitAccessShareHook($share, 404, 'Share not found');
$this->emitShareAccessEvent($share, self::SHARE_DOWNLOAD, 404, 'Share not found');
return new NotFoundResponse();
}
}
-
- if ($node instanceof Folder) {
- if ($files === null || $files === '') {
- if ($share->getHideDownload()) {
- throw new NotFoundException('Downloading a folder');
- }
- }
- }
}
$this->emitAccessShareHook($share);
$this->emitShareAccessEvent($share, self::SHARE_DOWNLOAD);
- $davUrl = '/public.php/dav/files/' . $token . '/?accept=zip';
+ $davPath = '';
+ if ($node !== $share->getNode()) {
+ $davPath = substr($node->getPath(), strlen($share->getNode()->getPath()));
+ }
+
+ $params = [];
if ($files !== null) {
- $davUrl .= '&files=' . $files;
+ $params['files'] = $files;
}
+ if ($node instanceof Folder) {
+ $params['accept'] = 'zip';
+ }
+
+ $davUrl = '/public.php/dav/files/' . $token . $davPath;
+ $davUrl .= '?' . http_build_query($params);
return new RedirectResponse($this->urlGenerator->getAbsoluteURL($davUrl));
}
}
diff --git a/apps/files_sharing/tests/Controller/ShareControllerTest.php b/apps/files_sharing/tests/Controller/ShareControllerTest.php
index 32fd0f09637b9..f06622d501af3 100644
--- a/apps/files_sharing/tests/Controller/ShareControllerTest.php
+++ b/apps/files_sharing/tests/Controller/ShareControllerTest.php
@@ -18,6 +18,7 @@
use OCP\Accounts\IAccountManager;
use OCP\Accounts\IAccountProperty;
use OCP\Activity\IManager;
+use OCP\AppFramework\Http;
use OCP\AppFramework\Http\ContentSecurityPolicy;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\Http\Template\ExternalShareMenuAction;
@@ -691,7 +692,9 @@ public function testShowShareInvalid(): void {
->with('token')
->willReturn($share);
- $this->userManager->method('get')->with('ownerUID')->willReturn($owner);
+ $this->userManager->method('get')
+ ->with('ownerUID')
+ ->willReturn($owner);
$this->shareController->showShare();
}
@@ -712,7 +715,7 @@ public function testDownloadShareWithCreateOnlyShare(): void {
// Test with a password protected share and no authentication
$response = $this->shareController->downloadShare('validtoken');
- $expectedResponse = new DataResponse('Share has no read permission');
+ $expectedResponse = new DataResponse('Share has no read permission', Http::STATUS_FORBIDDEN);
$this->assertEquals($expectedResponse, $response);
}
@@ -740,7 +743,7 @@ public function testDownloadShareWithoutDownloadPermission(): void {
// Test with a password protected share and no authentication
$response = $this->shareController->downloadShare('validtoken');
- $expectedResponse = new DataResponse('Share has no download permission');
+ $expectedResponse = new DataResponse('Share has no download permission', Http::STATUS_FORBIDDEN);
$this->assertEquals($expectedResponse, $response);
}
diff --git a/build/psalm-baseline.xml b/build/psalm-baseline.xml
index 9b08cc9c28cea..940b950fbecdd 100644
--- a/build/psalm-baseline.xml
+++ b/build/psalm-baseline.xml
@@ -1607,6 +1607,7 @@
+