Skip to content

Commit 8448fcb

Browse files
committed
Allow app to register a download provider
This allows to provide a file content for a given dav path. Signed-off-by: Louis Chemineau <louis@chmn.me>
1 parent 8655297 commit 8448fcb

File tree

9 files changed

+281
-2
lines changed

9 files changed

+281
-2
lines changed

apps/files/appinfo/routes.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,12 @@
183183
'url' => '/api/v1/openlocaleditor/{token}',
184184
'verb' => 'POST',
185185
],
186+
[
187+
/** @see DownloadController::index() */
188+
'name' => 'Download#index',
189+
'url' => '/api/v1/download',
190+
'verb' => 'GET',
191+
],
186192
],
187193
]
188194
);

apps/files/composer/composer/autoload_classmap.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
'OCA\\Files\\Controller\\ApiController' => $baseDir . '/../lib/Controller/ApiController.php',
3737
'OCA\\Files\\Controller\\DirectEditingController' => $baseDir . '/../lib/Controller/DirectEditingController.php',
3838
'OCA\\Files\\Controller\\DirectEditingViewController' => $baseDir . '/../lib/Controller/DirectEditingViewController.php',
39+
'OCA\\Files\\Controller\\DownloadController' => $baseDir . '/../lib/Controller/DownloadController.php',
3940
'OCA\\Files\\Controller\\OpenLocalEditorController' => $baseDir . '/../lib/Controller/OpenLocalEditorController.php',
4041
'OCA\\Files\\Controller\\TemplateController' => $baseDir . '/../lib/Controller/TemplateController.php',
4142
'OCA\\Files\\Controller\\TransferOwnershipController' => $baseDir . '/../lib/Controller/TransferOwnershipController.php',
@@ -54,6 +55,7 @@
5455
'OCA\\Files\\Migration\\Version11301Date20191205150729' => $baseDir . '/../lib/Migration/Version11301Date20191205150729.php',
5556
'OCA\\Files\\Migration\\Version12101Date20221011153334' => $baseDir . '/../lib/Migration/Version12101Date20221011153334.php',
5657
'OCA\\Files\\Notification\\Notifier' => $baseDir . '/../lib/Notification/Notifier.php',
58+
'OCA\\Files\\Provider\\FileDownloadProvider' => $baseDir . '/../lib/Provider/FileDownloadProvider.php',
5759
'OCA\\Files\\Search\\FilesSearchProvider' => $baseDir . '/../lib/Search/FilesSearchProvider.php',
5860
'OCA\\Files\\Service\\DirectEditingService' => $baseDir . '/../lib/Service/DirectEditingService.php',
5961
'OCA\\Files\\Service\\OwnershipTransferService' => $baseDir . '/../lib/Service/OwnershipTransferService.php',

apps/files/composer/composer/autoload_static.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ class ComposerStaticInitFiles
5151
'OCA\\Files\\Controller\\ApiController' => __DIR__ . '/..' . '/../lib/Controller/ApiController.php',
5252
'OCA\\Files\\Controller\\DirectEditingController' => __DIR__ . '/..' . '/../lib/Controller/DirectEditingController.php',
5353
'OCA\\Files\\Controller\\DirectEditingViewController' => __DIR__ . '/..' . '/../lib/Controller/DirectEditingViewController.php',
54+
'OCA\\Files\\Controller\\DownloadController' => __DIR__ . '/..' . '/../lib/Controller/DownloadController.php',
5455
'OCA\\Files\\Controller\\OpenLocalEditorController' => __DIR__ . '/..' . '/../lib/Controller/OpenLocalEditorController.php',
5556
'OCA\\Files\\Controller\\TemplateController' => __DIR__ . '/..' . '/../lib/Controller/TemplateController.php',
5657
'OCA\\Files\\Controller\\TransferOwnershipController' => __DIR__ . '/..' . '/../lib/Controller/TransferOwnershipController.php',
@@ -69,6 +70,7 @@ class ComposerStaticInitFiles
6970
'OCA\\Files\\Migration\\Version11301Date20191205150729' => __DIR__ . '/..' . '/../lib/Migration/Version11301Date20191205150729.php',
7071
'OCA\\Files\\Migration\\Version12101Date20221011153334' => __DIR__ . '/..' . '/../lib/Migration/Version12101Date20221011153334.php',
7172
'OCA\\Files\\Notification\\Notifier' => __DIR__ . '/..' . '/../lib/Notification/Notifier.php',
73+
'OCA\\Files\\Provider\\FileDownloadProvider' => __DIR__ . '/..' . '/../lib/Provider/FileDownloadProvider.php',
7274
'OCA\\Files\\Search\\FilesSearchProvider' => __DIR__ . '/..' . '/../lib/Search/FilesSearchProvider.php',
7375
'OCA\\Files\\Service\\DirectEditingService' => __DIR__ . '/..' . '/../lib/Service/DirectEditingService.php',
7476
'OCA\\Files\\Service\\OwnershipTransferService' => __DIR__ . '/..' . '/../lib/Service/OwnershipTransferService.php',

apps/files/lib/AppInfo/Application.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
use OCA\Files\Listener\LegacyLoadAdditionalScriptsAdapter;
4646
use OCA\Files\Listener\LoadSidebarListener;
4747
use OCA\Files\Notification\Notifier;
48+
use OCA\Files\Provider\FileDownloadProvider;
4849
use OCA\Files\Search\FilesSearchProvider;
4950
use OCA\Files\Service\TagService;
5051
use OCP\Activity\IManager as IActivityManager;
@@ -120,6 +121,8 @@ public function register(IRegistrationContext $context): void {
120121
$context->registerSearchProvider(FilesSearchProvider::class);
121122

122123
$context->registerNotifierService(Notifier::class);
124+
125+
$context->registerFileDownloadProvider(FileDownloadProvider::class);
123126
}
124127

125128
public function boot(IBootContext $context): void {
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* @copyright Copyright (c) 2022 Louis Chmn <louis@chmn.me>
7+
*
8+
* @author Louis Chmn <louis@chmn.me>
9+
*
10+
* @license GNU AGPL version 3 or any later version
11+
*
12+
* This program is free software: you can redistribute it and/or modify
13+
* it under the terms of the GNU Affero General Public License as
14+
* published by the Free Software Foundation, either version 3 of the
15+
* License, or (at your option) any later version.
16+
*
17+
* This program is distributed in the hope that it will be useful,
18+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
19+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20+
* GNU Affero General Public License for more details.
21+
*
22+
* You should have received a copy of the GNU Affero General Public License
23+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
24+
*
25+
*/
26+
27+
namespace OCA\Files\Controller;
28+
29+
use OC\AppFramework\Bootstrap\Coordinator;
30+
use OCP\AppFramework\Http\ZipResponse;
31+
use OCP\AppFramework\Controller;
32+
use OCP\Files\File;
33+
use OCP\Files\Folder;
34+
use OCP\Files\IFileDownloadProvider;
35+
use OCP\Files\Node;
36+
use OCP\IRequest;
37+
use Psr\Log\LoggerInterface;
38+
39+
class DownloadController extends Controller {
40+
private Coordinator $coordinator;
41+
private LoggerInterface $logger;
42+
43+
public function __construct(
44+
string $appName,
45+
IRequest $request,
46+
Coordinator $coordinator,
47+
LoggerInterface $logger
48+
) {
49+
parent::__construct($appName, $request);
50+
51+
$this->request = $request;
52+
$this->coordinator = $coordinator;
53+
$this->logger = $logger;
54+
}
55+
56+
/**
57+
* @NoAdminRequired
58+
* @NoCSRFRequired
59+
* @PublicPage
60+
*/
61+
public function index(string $files): ZipResponse {
62+
$response = new ZipResponse($this->request, 'download');
63+
64+
/** @var string[] */
65+
$files = json_decode($files);
66+
67+
if (count($files) === 0) {
68+
return $response;
69+
}
70+
71+
$commonPrefix = $files[0];
72+
foreach ($files as $filePath) {
73+
$commonPrefix = $this->getCommonPrefix($filePath, $commonPrefix);
74+
}
75+
76+
$context = $this->coordinator->getRegistrationContext();
77+
if ($context === null) {
78+
throw new \Exception("Can't get download providers");
79+
}
80+
$providerRegistrations = $context->getFileDownloadProviders();
81+
82+
foreach ($files as $filePath) {
83+
$node = null;
84+
85+
foreach ($providerRegistrations as $registration) {
86+
try {
87+
/** @var IFileDownloadProvider */
88+
$provider = \OCP\Server::get($registration->getService());
89+
$node = $provider->getNode($filePath);
90+
if ($node !== null) {
91+
break;
92+
}
93+
} catch (\Throwable $ex) {
94+
$providerClass = $registration->getService();
95+
$this->logger->warning("Error while getting file content from $providerClass", ['exception' => $ex]);
96+
}
97+
}
98+
99+
if ($node === null) {
100+
continue;
101+
}
102+
103+
$this->addNode($response, $node, str_replace($commonPrefix, '', $filePath));
104+
}
105+
106+
return $response;
107+
}
108+
109+
private function getCommonPrefix(string $str1, string $str2): string {
110+
$mbStr1 = mb_str_split($str1);
111+
$mbStr2 = mb_str_split($str2);
112+
113+
for ($i = 0; $i < count($mbStr1); $i++) {
114+
if ($mbStr1[$i] !== $mbStr2[$i]) {
115+
$i--;
116+
break;
117+
}
118+
}
119+
120+
if ($i < 0) {
121+
return '';
122+
} else {
123+
return join(array_slice($mbStr1, 0, $i));
124+
}
125+
}
126+
127+
private function addNode(ZipResponse $response, Node $node, string $path): void {
128+
if ($node instanceof File) {
129+
$response->addResource($node->fopen('r'), $path, $node->getSize());
130+
}
131+
132+
if ($node instanceof Folder) {
133+
foreach ($node->getDirectoryListing() as $subnode) {
134+
$this->addNode($response, $subnode, $path.'/'.$subnode->getName());
135+
}
136+
}
137+
}
138+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* @copyright Copyright (c) 2022 Louis Chmn <louis@chmn.me>
7+
*
8+
* @author Louis Chmn <louis@chmn.me>
9+
*
10+
* @license GNU AGPL version 3 or any later version
11+
*
12+
* This program is free software: you can redistribute it and/or modify
13+
* it under the terms of the GNU Affero General Public License as
14+
* published by the Free Software Foundation, either version 3 of the
15+
* License, or (at your option) any later version.
16+
*
17+
* This program is distributed in the hope that it will be useful,
18+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
19+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20+
* GNU Affero General Public License for more details.
21+
*
22+
* You should have received a copy of the GNU Affero General Public License
23+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
24+
*
25+
*/
26+
namespace OCA\Files\Provider;
27+
28+
use OCP\Files\Folder;
29+
use OCP\Files\Node;
30+
use OCP\Files\IFileDownloadProvider;
31+
32+
class FileDownloadProvider implements IFileDownloadProvider {
33+
private ?Folder $userFolder;
34+
35+
public function __construct(
36+
?Folder $userFolder,
37+
) {
38+
$this->userFolder = $userFolder;
39+
}
40+
41+
public function getNode(string $davPath): ?Node {
42+
if (!str_starts_with($davPath, "files/")) {
43+
return null;
44+
}
45+
46+
if ($this->userFolder === null) {
47+
return null;
48+
}
49+
50+
/** @var ?string */
51+
$userId = explode('/', $davPath)[1];
52+
if (is_null($userId) || $userId !== $this->userFolder->getOwner()->getUID()) {
53+
return null;
54+
}
55+
56+
$filePath = str_replace("files/$userId", '', $davPath);
57+
58+
return $this->userFolder->get($filePath);
59+
}
60+
}

lib/private/AppFramework/Bootstrap/RegistrationContext.php

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
use OCP\Dashboard\IManager;
4848
use OCP\Dashboard\IWidget;
4949
use OCP\EventDispatcher\IEventDispatcher;
50+
use OCP\Files\IFileDownloadProvider;
5051
use OCP\Files\Template\ICustomTemplateProvider;
5152
use OCP\Http\WellKnown\IHandler;
5253
use OCP\Notification\INotifier;
@@ -58,7 +59,6 @@
5859
use Throwable;
5960

6061
class RegistrationContext {
61-
6262
/** @var ServiceRegistration<ICapability>[] */
6363
private $capabilities = [];
6464

@@ -128,6 +128,9 @@ class RegistrationContext {
128128
/** @var ParameterRegistration[] */
129129
private $sensitiveMethods = [];
130130

131+
/** @var ServiceRegistration<IFileDownloadProvider>[] */
132+
private array $fileDownloadProviders = [];
133+
131134
/** @var LoggerInterface */
132135
private $logger;
133136

@@ -326,6 +329,13 @@ public function registerSensitiveMethods(string $class, array $methods): void {
326329
$methods
327330
);
328331
}
332+
333+
public function registerFileDownloadProvider(string $class): void {
334+
$this->context->registerFileDownloadProvider(
335+
$this->appId,
336+
$class
337+
);
338+
}
329339
};
330340
}
331341

@@ -461,6 +471,10 @@ public function registerSensitiveMethods(string $appId, string $class, array $me
461471
$this->sensitiveMethods[] = new ParameterRegistration($appId, $class, $methods);
462472
}
463473

474+
public function registerFileDownloadProvider(string $appId, string $class): void {
475+
$this->fileDownloadProviders[] = new ServiceRegistration($appId, $class);
476+
}
477+
464478
/**
465479
* @param App[] $apps
466480
*/
@@ -757,4 +771,11 @@ public function getUserMigrators(): array {
757771
public function getSensitiveMethods(): array {
758772
return $this->sensitiveMethods;
759773
}
774+
775+
/**
776+
* @return ServiceRegistration<IFileDownloadProvider>[]
777+
*/
778+
public function getFileDownloadProviders(): array {
779+
return $this->fileDownloadProviders;
780+
}
760781
}

lib/public/AppFramework/Bootstrap/IRegistrationContext.php

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@
4747
* @see IBootstrap::register()
4848
*/
4949
interface IRegistrationContext {
50-
5150
/**
5251
* @param string $capability
5352
* @psalm-param class-string<ICapability> $capability
@@ -327,4 +326,14 @@ public function registerUserMigrator(string $migratorClass): void;
327326
* @since 25.0.0
328327
*/
329328
public function registerSensitiveMethods(string $class, array $methods): void;
329+
330+
/**
331+
* Register a backend to provide file based on a dav path.
332+
*
333+
* @param string $class
334+
* @param string[] $methods
335+
* @return void
336+
* @since 25.0.0
337+
*/
338+
public function registerFileDownloadProvider(string $class): void;
330339
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* @copyright Copyright (c) 2022 Louis Chmn <louis@chmn.me>
7+
*
8+
* @author Louis Chmn <louis@chmn.me>
9+
*
10+
* @license GNU AGPL version 3 or any later version
11+
*
12+
* This program is free software: you can redistribute it and/or modify
13+
* it under the terms of the GNU Affero General Public License as
14+
* published by the Free Software Foundation, either version 3 of the
15+
* License, or (at your option) any later version.
16+
*
17+
* This program is distributed in the hope that it will be useful,
18+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
19+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20+
* GNU Affero General Public License for more details.
21+
*
22+
* You should have received a copy of the GNU Affero General Public License
23+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
24+
*
25+
*/
26+
namespace OCP\Files;
27+
28+
/**
29+
* This interface defines how to provide a file given a dav path.
30+
*
31+
* @since 26.0.0
32+
*/
33+
interface IFileDownloadProvider {
34+
/**
35+
* @since 26.0.0
36+
*/
37+
public function getNode(string $davPath): ?Node;
38+
}

0 commit comments

Comments
 (0)