Skip to content

Commit

Permalink
Allow app to register a download provider
Browse files Browse the repository at this point in the history
This allows to provide a file content for a given dav path.

Signed-off-by: Louis Chemineau <louis@chmn.me>
  • Loading branch information
artonge committed Nov 7, 2022
1 parent 8655297 commit 6cd7733
Show file tree
Hide file tree
Showing 9 changed files with 281 additions and 2 deletions.
6 changes: 6 additions & 0 deletions apps/files/appinfo/routes.php
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,12 @@
'url' => '/api/v1/openlocaleditor/{token}',
'verb' => 'POST',
],
[
/** @see DownloadController::index() */
'name' => 'Download#index',
'url' => '/api/v1/download',
'verb' => 'GET',
],
],
]
);
Expand Down
2 changes: 2 additions & 0 deletions apps/files/composer/composer/autoload_classmap.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
'OCA\\Files\\Controller\\ApiController' => $baseDir . '/../lib/Controller/ApiController.php',
'OCA\\Files\\Controller\\DirectEditingController' => $baseDir . '/../lib/Controller/DirectEditingController.php',
'OCA\\Files\\Controller\\DirectEditingViewController' => $baseDir . '/../lib/Controller/DirectEditingViewController.php',
'OCA\\Files\\Controller\\DownloadController' => $baseDir . '/../lib/Controller/DownloadController.php',
'OCA\\Files\\Controller\\OpenLocalEditorController' => $baseDir . '/../lib/Controller/OpenLocalEditorController.php',
'OCA\\Files\\Controller\\TemplateController' => $baseDir . '/../lib/Controller/TemplateController.php',
'OCA\\Files\\Controller\\TransferOwnershipController' => $baseDir . '/../lib/Controller/TransferOwnershipController.php',
Expand All @@ -54,6 +55,7 @@
'OCA\\Files\\Migration\\Version11301Date20191205150729' => $baseDir . '/../lib/Migration/Version11301Date20191205150729.php',
'OCA\\Files\\Migration\\Version12101Date20221011153334' => $baseDir . '/../lib/Migration/Version12101Date20221011153334.php',
'OCA\\Files\\Notification\\Notifier' => $baseDir . '/../lib/Notification/Notifier.php',
'OCA\\Files\\Provider\\FileDownloadProvider' => $baseDir . '/../lib/Provider/FileDownloadProvider.php',
'OCA\\Files\\Search\\FilesSearchProvider' => $baseDir . '/../lib/Search/FilesSearchProvider.php',
'OCA\\Files\\Service\\DirectEditingService' => $baseDir . '/../lib/Service/DirectEditingService.php',
'OCA\\Files\\Service\\OwnershipTransferService' => $baseDir . '/../lib/Service/OwnershipTransferService.php',
Expand Down
2 changes: 2 additions & 0 deletions apps/files/composer/composer/autoload_static.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ class ComposerStaticInitFiles
'OCA\\Files\\Controller\\ApiController' => __DIR__ . '/..' . '/../lib/Controller/ApiController.php',
'OCA\\Files\\Controller\\DirectEditingController' => __DIR__ . '/..' . '/../lib/Controller/DirectEditingController.php',
'OCA\\Files\\Controller\\DirectEditingViewController' => __DIR__ . '/..' . '/../lib/Controller/DirectEditingViewController.php',
'OCA\\Files\\Controller\\DownloadController' => __DIR__ . '/..' . '/../lib/Controller/DownloadController.php',
'OCA\\Files\\Controller\\OpenLocalEditorController' => __DIR__ . '/..' . '/../lib/Controller/OpenLocalEditorController.php',
'OCA\\Files\\Controller\\TemplateController' => __DIR__ . '/..' . '/../lib/Controller/TemplateController.php',
'OCA\\Files\\Controller\\TransferOwnershipController' => __DIR__ . '/..' . '/../lib/Controller/TransferOwnershipController.php',
Expand All @@ -69,6 +70,7 @@ class ComposerStaticInitFiles
'OCA\\Files\\Migration\\Version11301Date20191205150729' => __DIR__ . '/..' . '/../lib/Migration/Version11301Date20191205150729.php',
'OCA\\Files\\Migration\\Version12101Date20221011153334' => __DIR__ . '/..' . '/../lib/Migration/Version12101Date20221011153334.php',
'OCA\\Files\\Notification\\Notifier' => __DIR__ . '/..' . '/../lib/Notification/Notifier.php',
'OCA\\Files\\Provider\\FileDownloadProvider' => __DIR__ . '/..' . '/../lib/Provider/FileDownloadProvider.php',
'OCA\\Files\\Search\\FilesSearchProvider' => __DIR__ . '/..' . '/../lib/Search/FilesSearchProvider.php',
'OCA\\Files\\Service\\DirectEditingService' => __DIR__ . '/..' . '/../lib/Service/DirectEditingService.php',
'OCA\\Files\\Service\\OwnershipTransferService' => __DIR__ . '/..' . '/../lib/Service/OwnershipTransferService.php',
Expand Down
3 changes: 3 additions & 0 deletions apps/files/lib/AppInfo/Application.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
use OCA\Files\Listener\LegacyLoadAdditionalScriptsAdapter;
use OCA\Files\Listener\LoadSidebarListener;
use OCA\Files\Notification\Notifier;
use OCA\Files\Provider\FileDownloadProvider;
use OCA\Files\Search\FilesSearchProvider;
use OCA\Files\Service\TagService;
use OCP\Activity\IManager as IActivityManager;
Expand Down Expand Up @@ -120,6 +121,8 @@ public function register(IRegistrationContext $context): void {
$context->registerSearchProvider(FilesSearchProvider::class);

$context->registerNotifierService(Notifier::class);

$context->registerFileDownloadProvider(FileDownloadProvider::class);
}

public function boot(IBootContext $context): void {
Expand Down
138 changes: 138 additions & 0 deletions apps/files/lib/Controller/DownloadController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
<?php

declare(strict_types=1);

/**
* @copyright Copyright (c) 2022 Louis Chmn <louis@chmn.me>
*
* @author Louis Chmn <louis@chmn.me>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/

namespace OCA\Files\Controller;

use OC\AppFramework\Bootstrap\Coordinator;
use OCP\AppFramework\Http\ZipResponse;
use OCP\AppFramework\Controller;
use OCP\Files\File;
use OCP\Files\Folder;
use OCP\Files\IFileDownloadProvider;
use OCP\Files\Node;
use OCP\IRequest;
use Psr\Log\LoggerInterface;

class DownloadController extends Controller {
private Coordinator $coordinator;
private LoggerInterface $logger;

public function __construct(
string $appName,
IRequest $request,
Coordinator $coordinator,
LoggerInterface $logger
) {
parent::__construct($appName, $request);

$this->request = $request;
$this->coordinator = $coordinator;
$this->logger = $logger;
}

/**
* @NoAdminRequired
* @NoCSRFRequired
* @PublicPage
*/
public function index(string $files): ZipResponse {
$response = new ZipResponse($this->request, 'download');

/** @var string[] */
$files = json_decode($files);

if (count($files) === 0) {
return $response;
}

$commonPrefix = $files[0];
foreach ($files as $filePath) {
$commonPrefix = $this->getCommonPrefix($filePath, $commonPrefix);
}

$context = $this->coordinator->getRegistrationContext();
if ($context === null) {
throw new \Exception("Can't get download providers");
}
$providerRegistrations = $context->getFileDownloadProviders();

foreach ($files as $filePath) {
$node = null;

foreach ($providerRegistrations as $registration) {
try {
/** @var IFileDownloadProvider */
$provider = \OCP\Server::get($registration->getService());
$node = $provider->getNode($filePath);
if ($node !== null) {
break;
}
} catch (\Throwable $ex) {
$providerClass = $provider::class;
$this->logger->warning("Error while getting file content from $providerClass", ['exception' => $ex]);
}
}

if ($node === null) {
continue;
}

$this->addNode($response, $node, str_replace($commonPrefix, '', $filePath));
}

return $response;
}

private function getCommonPrefix(string $str1, string $str2): string {
$mbStr1 = mb_str_split($str1);
$mbStr2 = mb_str_split($str2);

for ($i = 0; $i < count($mbStr1); $i++) {
if ($mbStr1[$i] !== $mbStr2[$i]) {
$i--;
break;
}
}

if ($i < 0) {
return '';
} else {
return join(array_slice($mbStr1, 0, $i));
}
}

private function addNode(ZipResponse $response, Node $node, string $path) {
if ($node instanceof File) {
$response->addResource($node->fopen('r'), $path.$node->getName(), $node->getSize());
}

if ($node instanceof Folder) {
foreach ($node->getDirectoryListing() as $subnode) {
$this->addNode($response, $subnode, $path.$node->getName());
}
}
}
}
60 changes: 60 additions & 0 deletions apps/files/lib/Provider/FileDownloadProvider.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<?php

declare(strict_types=1);

/**
* @copyright Copyright (c) 2022 Louis Chmn <louis@chmn.me>
*
* @author Louis Chmn <louis@chmn.me>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Files\Provider;

use OCP\Files\Folder;
use OCP\Files\Node;
use OCP\Files\IFileDownloadProvider;

class FileDownloadProvider implements IFileDownloadProvider {
private ?Folder $userFolder;

public function __construct(
?Folder $userFolder,
) {
$this->userFolder = $userFolder;
}

public function getNode(string $davPath): ?Node {
if (!str_starts_with($davPath, "/files/")) {
return null;
}

if ($this->userFolder === null) {
return null;
}

/** @var ?string */
$userId = explode('/', $davPath)[2];
if (is_null($userId) || $userId !== $this->userFolder->getOwner()->getUID()) {
return null;
}

$filePath = str_replace("/files/$userId", '', $davPath);

return $this->userFolder->get($filePath);
}
}
23 changes: 22 additions & 1 deletion lib/private/AppFramework/Bootstrap/RegistrationContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
use OCP\Dashboard\IManager;
use OCP\Dashboard\IWidget;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\Files\IFileDownloadProvider;
use OCP\Files\Template\ICustomTemplateProvider;
use OCP\Http\WellKnown\IHandler;
use OCP\Notification\INotifier;
Expand All @@ -58,7 +59,6 @@
use Throwable;

class RegistrationContext {

/** @var ServiceRegistration<ICapability>[] */
private $capabilities = [];

Expand Down Expand Up @@ -128,6 +128,9 @@ class RegistrationContext {
/** @var ParameterRegistration[] */
private $sensitiveMethods = [];

/** @var ServiceRegistration<IFileDownloadProvider>[] */
private array $fileDownloadProviders = [];

/** @var LoggerInterface */
private $logger;

Expand Down Expand Up @@ -326,6 +329,13 @@ public function registerSensitiveMethods(string $class, array $methods): void {
$methods
);
}

public function registerFileDownloadProvider(string $class): void {
$this->context->registerFileDownloadProvider(
$this->appId,
$class
);
}
};
}

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

public function registerFileDownloadProvider(string $appId, string $class): void {
$this->fileDownloadProviders[] = new ServiceRegistration($appId, $class);
}

/**
* @param App[] $apps
*/
Expand Down Expand Up @@ -757,4 +771,11 @@ public function getUserMigrators(): array {
public function getSensitiveMethods(): array {
return $this->sensitiveMethods;
}

/**
* @return ServiceRegistration<IFileDownloadProvider>[]
*/
public function getFileDownloadProviders(): array {
return $this->fileDownloadProviders;
}
}
11 changes: 10 additions & 1 deletion lib/public/AppFramework/Bootstrap/IRegistrationContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@
* @see IBootstrap::register()
*/
interface IRegistrationContext {

/**
* @param string $capability
* @psalm-param class-string<ICapability> $capability
Expand Down Expand Up @@ -327,4 +326,14 @@ public function registerUserMigrator(string $migratorClass): void;
* @since 25.0.0
*/
public function registerSensitiveMethods(string $class, array $methods): void;

/**
* Register a backend to provide file based on a dav path.
*
* @param string $class
* @param string[] $methods
* @return void
* @since 25.0.0
*/
public function registerFileDownloadProvider(string $class): void;
}
38 changes: 38 additions & 0 deletions lib/public/Files/IFileDownloadProvider.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php

declare(strict_types=1);

/**
* @copyright Copyright (c) 2022 Louis Chmn <louis@chmn.me>
*
* @author Louis Chmn <louis@chmn.me>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCP\Files;

/**
* This interface defines how to provide a file given a dav path.
*
* @since 26.0.0
*/
interface IFileDownloadProvider {
/**
* @since 26.0.0
*/
public function getNode(string $davPath): ?Node;
}

0 comments on commit 6cd7733

Please sign in to comment.