aboutsummaryrefslogtreecommitdiffstats
path: root/apps/files_external/lib/Command
diff options
context:
space:
mode:
Diffstat (limited to 'apps/files_external/lib/Command')
-rw-r--r--apps/files_external/lib/Command/Applicable.php35
-rw-r--r--apps/files_external/lib/Command/Backends.php26
-rw-r--r--apps/files_external/lib/Command/Config.php31
-rw-r--r--apps/files_external/lib/Command/Create.php31
-rw-r--r--apps/files_external/lib/Command/Delete.php31
-rw-r--r--apps/files_external/lib/Command/Dependencies.php57
-rw-r--r--apps/files_external/lib/Command/Export.php23
-rw-r--r--apps/files_external/lib/Command/Import.php37
-rw-r--r--apps/files_external/lib/Command/ListCommand.php34
-rw-r--r--apps/files_external/lib/Command/Notify.php115
-rw-r--r--apps/files_external/lib/Command/Option.php25
-rw-r--r--apps/files_external/lib/Command/Scan.php166
-rw-r--r--apps/files_external/lib/Command/StorageAuthBase.php114
-rw-r--r--apps/files_external/lib/Command/Verify.php33
14 files changed, 422 insertions, 336 deletions
diff --git a/apps/files_external/lib/Command/Applicable.php b/apps/files_external/lib/Command/Applicable.php
index dbedc72825c..4d5e264bfaf 100644
--- a/apps/files_external/lib/Command/Applicable.php
+++ b/apps/files_external/lib/Command/Applicable.php
@@ -1,26 +1,9 @@
<?php
+
/**
- * @copyright Copyright (c) 2016, ownCloud, Inc.
- *
- * @author Christoph Wurst <christoph@winzerhof-wurst.at>
- * @author Joas Schilling <coding@schilljs.com>
- * @author Robin Appelman <robin@icewind.nl>
- * @author Roeland Jago Douma <roeland@famdouma.nl>
- *
- * @license AGPL-3.0
- *
- * This code is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * 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, version 3,
- * along with this program. If not, see <http://www.gnu.org/licenses/>
- *
+ * SPDX-FileCopyrightText: 2017-2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
+ * SPDX-License-Identifier: AGPL-3.0-only
*/
namespace OCA\Files_External\Command;
@@ -28,13 +11,13 @@ use OC\Core\Command\Base;
use OCA\Files_External\Lib\StorageConfig;
use OCA\Files_External\NotFoundException;
use OCA\Files_External\Service\GlobalStoragesService;
+use OCP\AppFramework\Http;
use OCP\IGroupManager;
use OCP\IUserManager;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
-use Symfony\Component\HttpFoundation\Response;
class Applicable extends Base {
public function __construct(
@@ -88,10 +71,10 @@ class Applicable extends Base {
$mount = $this->globalService->getStorage($mountId);
} catch (NotFoundException $e) {
$output->writeln('<error>Mount with id "' . $mountId . ' not found, check "occ files_external:list" to get available mounts</error>');
- return Response::HTTP_NOT_FOUND;
+ return Http::STATUS_NOT_FOUND;
}
- if ($mount->getType() === StorageConfig::MOUNT_TYPE_PERSONAl) {
+ if ($mount->getType() === StorageConfig::MOUNT_TYPE_PERSONAL) {
$output->writeln('<error>Can\'t change applicables on personal mounts</error>');
return self::FAILURE;
}
@@ -108,13 +91,13 @@ class Applicable extends Base {
foreach ($addUsers as $addUser) {
if (!$this->userManager->userExists($addUser)) {
$output->writeln('<error>User "' . $addUser . '" not found</error>');
- return Response::HTTP_NOT_FOUND;
+ return Http::STATUS_NOT_FOUND;
}
}
foreach ($addGroups as $addGroup) {
if (!$this->groupManager->groupExists($addGroup)) {
$output->writeln('<error>Group "' . $addGroup . '" not found</error>');
- return Response::HTTP_NOT_FOUND;
+ return Http::STATUS_NOT_FOUND;
}
}
diff --git a/apps/files_external/lib/Command/Backends.php b/apps/files_external/lib/Command/Backends.php
index 3a0f26a2803..7fab0477adf 100644
--- a/apps/files_external/lib/Command/Backends.php
+++ b/apps/files_external/lib/Command/Backends.php
@@ -1,25 +1,9 @@
<?php
+
/**
- * @copyright Copyright (c) 2016, ownCloud, Inc.
- *
- * @author Christoph Wurst <christoph@winzerhof-wurst.at>
- * @author Joas Schilling <coding@schilljs.com>
- * @author Robin Appelman <robin@icewind.nl>
- *
- * @license AGPL-3.0
- *
- * This code is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * 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, version 3,
- * along with this program. If not, see <http://www.gnu.org/licenses/>
- *
+ * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
+ * SPDX-License-Identifier: AGPL-3.0-only
*/
namespace OCA\Files_External\Command;
@@ -111,7 +95,7 @@ class Backends extends Base {
*/
private function formatConfiguration(array $parameters): array {
$configuration = array_filter($parameters, function (DefinitionParameter $parameter) {
- return $parameter->getType() !== DefinitionParameter::VALUE_HIDDEN;
+ return $parameter->isFlagSet(DefinitionParameter::FLAG_HIDDEN);
});
return array_map(function (DefinitionParameter $parameter) {
return $parameter->getTypeName();
diff --git a/apps/files_external/lib/Command/Config.php b/apps/files_external/lib/Command/Config.php
index 3b905cc74fb..883b4a2f3e7 100644
--- a/apps/files_external/lib/Command/Config.php
+++ b/apps/files_external/lib/Command/Config.php
@@ -1,26 +1,9 @@
<?php
+
/**
- * @copyright Copyright (c) 2016, ownCloud, Inc.
- *
- * @author Ardinis <Ardinis@users.noreply.github.com>
- * @author Christoph Wurst <christoph@winzerhof-wurst.at>
- * @author Joas Schilling <coding@schilljs.com>
- * @author Robin Appelman <robin@icewind.nl>
- *
- * @license AGPL-3.0
- *
- * This code is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * 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, version 3,
- * along with this program. If not, see <http://www.gnu.org/licenses/>
- *
+ * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
+ * SPDX-License-Identifier: AGPL-3.0-only
*/
namespace OCA\Files_External\Command;
@@ -28,10 +11,10 @@ use OC\Core\Command\Base;
use OCA\Files_External\Lib\StorageConfig;
use OCA\Files_External\NotFoundException;
use OCA\Files_External\Service\GlobalStoragesService;
+use OCP\AppFramework\Http;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
-use Symfony\Component\HttpFoundation\Response;
class Config extends Base {
public function __construct(
@@ -67,7 +50,7 @@ class Config extends Base {
$mount = $this->globalService->getStorage($mountId);
} catch (NotFoundException $e) {
$output->writeln('<error>Mount with id "' . $mountId . ' not found, check "occ files_external:list" to get available mounts"</error>');
- return Response::HTTP_NOT_FOUND;
+ return Http::STATUS_NOT_FOUND;
}
$value = $input->getArgument('value');
@@ -91,7 +74,7 @@ class Config extends Base {
if (!is_string($value) && json_decode(json_encode($value)) === $value) { // show bools and objects correctly
$value = json_encode($value);
}
- $output->writeln($value);
+ $output->writeln((string)$value);
}
/**
diff --git a/apps/files_external/lib/Command/Create.php b/apps/files_external/lib/Command/Create.php
index 50f7fb1cb43..3307015518a 100644
--- a/apps/files_external/lib/Command/Create.php
+++ b/apps/files_external/lib/Command/Create.php
@@ -1,26 +1,9 @@
<?php
+
/**
- * @copyright Copyright (c) 2016, ownCloud, Inc.
- *
- * @author Christoph Wurst <christoph@winzerhof-wurst.at>
- * @author Joas Schilling <coding@schilljs.com>
- * @author Robin Appelman <robin@icewind.nl>
- * @author Roeland Jago Douma <roeland@famdouma.nl>
- *
- * @license AGPL-3.0
- *
- * This code is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * 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, version 3,
- * along with this program. If not, see <http://www.gnu.org/licenses/>
- *
+ * SPDX-FileCopyrightText: 2017-2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
+ * SPDX-License-Identifier: AGPL-3.0-only
*/
namespace OCA\Files_External\Command;
@@ -35,6 +18,7 @@ use OCA\Files_External\Service\BackendService;
use OCA\Files_External\Service\GlobalStoragesService;
use OCA\Files_External\Service\StoragesService;
use OCA\Files_External\Service\UserStoragesService;
+use OCP\AppFramework\Http;
use OCP\IUserManager;
use OCP\IUserSession;
use Symfony\Component\Console\Input\ArrayInput;
@@ -42,7 +26,6 @@ use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
-use Symfony\Component\HttpFoundation\Response;
class Create extends Base {
public function __construct(
@@ -111,11 +94,11 @@ class Create extends Base {
}
if (is_null($storageBackend)) {
$output->writeln('<error>Storage backend with identifier "' . $storageIdentifier . '" not found (see `occ files_external:backends` for possible values)</error>');
- return Response::HTTP_NOT_FOUND;
+ return Http::STATUS_NOT_FOUND;
}
if (is_null($authBackend)) {
$output->writeln('<error>Authentication backend with identifier "' . $authIdentifier . '" not found (see `occ files_external:backends` for possible values)</error>');
- return Response::HTTP_NOT_FOUND;
+ return Http::STATUS_NOT_FOUND;
}
$supportedSchemes = array_keys($storageBackend->getAuthSchemes());
if (!in_array($authBackend->getScheme(), $supportedSchemes)) {
diff --git a/apps/files_external/lib/Command/Delete.php b/apps/files_external/lib/Command/Delete.php
index 6b6ae299773..9f121250f7d 100644
--- a/apps/files_external/lib/Command/Delete.php
+++ b/apps/files_external/lib/Command/Delete.php
@@ -1,25 +1,9 @@
<?php
+
/**
- * @copyright Copyright (c) 2016, ownCloud, Inc.
- *
- * @author Christoph Wurst <christoph@winzerhof-wurst.at>
- * @author Joas Schilling <coding@schilljs.com>
- * @author Robin Appelman <robin@icewind.nl>
- *
- * @license AGPL-3.0
- *
- * This code is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * 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, version 3,
- * along with this program. If not, see <http://www.gnu.org/licenses/>
- *
+ * SPDX-FileCopyrightText: 2017-2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
+ * SPDX-License-Identifier: AGPL-3.0-only
*/
namespace OCA\Files_External\Command;
@@ -27,15 +11,16 @@ use OC\Core\Command\Base;
use OCA\Files_External\NotFoundException;
use OCA\Files_External\Service\GlobalStoragesService;
use OCA\Files_External\Service\UserStoragesService;
+use OCP\AppFramework\Http;
use OCP\IUserManager;
use OCP\IUserSession;
+use Symfony\Component\Console\Helper\QuestionHelper;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ConfirmationQuestion;
-use Symfony\Component\HttpFoundation\Response;
class Delete extends Base {
public function __construct(
@@ -43,6 +28,7 @@ class Delete extends Base {
protected UserStoragesService $userService,
protected IUserSession $userSession,
protected IUserManager $userManager,
+ protected QuestionHelper $questionHelper,
) {
parent::__construct();
}
@@ -70,7 +56,7 @@ class Delete extends Base {
$mount = $this->globalService->getStorage($mountId);
} catch (NotFoundException $e) {
$output->writeln('<error>Mount with id "' . $mountId . ' not found, check "occ files_external:list" to get available mounts"</error>');
- return Response::HTTP_NOT_FOUND;
+ return Http::STATUS_NOT_FOUND;
}
$noConfirm = $input->getOption('yes');
@@ -81,6 +67,7 @@ class Delete extends Base {
$listInput->setOption('output', $input->getOption('output'));
$listCommand->listMounts(null, [$mount], $listInput, $output);
+ /** @var QuestionHelper $questionHelper */
$questionHelper = $this->getHelper('question');
$question = new ConfirmationQuestion('Delete this mount? [y/N] ', false);
diff --git a/apps/files_external/lib/Command/Dependencies.php b/apps/files_external/lib/Command/Dependencies.php
new file mode 100644
index 00000000000..d2db8a8c9af
--- /dev/null
+++ b/apps/files_external/lib/Command/Dependencies.php
@@ -0,0 +1,57 @@
+<?php
+
+/**
+ * SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+namespace OCA\Files_External\Command;
+
+use OC\Core\Command\Base;
+use OCA\Files_External\Service\BackendService;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class Dependencies extends Base {
+ public function __construct(
+ private readonly BackendService $backendService,
+ ) {
+ parent::__construct();
+ }
+
+ protected function configure(): void {
+ $this
+ ->setName('files_external:dependencies')
+ ->setDescription('Show information about the backend dependencies');
+ parent::configure();
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output): int {
+ $storageBackends = $this->backendService->getBackends();
+
+ $anyMissing = false;
+
+ foreach ($storageBackends as $backend) {
+ if ($backend->getDeprecateTo() !== null) {
+ continue;
+ }
+ $missingDependencies = $backend->checkDependencies();
+ if ($missingDependencies) {
+ $anyMissing = true;
+ $output->writeln($backend->getText() . ':');
+ foreach ($missingDependencies as $missingDependency) {
+ if ($missingDependency->getMessage()) {
+ $output->writeln(" - <comment>{$missingDependency->getDependency()}</comment>: {$missingDependency->getMessage()}");
+ } else {
+ $output->writeln(" - <comment>{$missingDependency->getDependency()}</comment>");
+ }
+ }
+ }
+ }
+
+ if (!$anyMissing) {
+ $output->writeln('<info>All dependencies are met</info>');
+ }
+
+ return self::SUCCESS;
+ }
+}
diff --git a/apps/files_external/lib/Command/Export.php b/apps/files_external/lib/Command/Export.php
index b5f7b67eafb..59484d0e788 100644
--- a/apps/files_external/lib/Command/Export.php
+++ b/apps/files_external/lib/Command/Export.php
@@ -1,24 +1,9 @@
<?php
+
/**
- * @copyright Copyright (c) 2016, ownCloud, Inc.
- *
- * @author Joas Schilling <coding@schilljs.com>
- * @author Robin Appelman <robin@icewind.nl>
- *
- * @license AGPL-3.0
- *
- * This code is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * 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, version 3,
- * along with this program. If not, see <http://www.gnu.org/licenses/>
- *
+ * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
+ * SPDX-License-Identifier: AGPL-3.0-only
*/
namespace OCA\Files_External\Command;
diff --git a/apps/files_external/lib/Command/Import.php b/apps/files_external/lib/Command/Import.php
index a4976b21cb7..a9ed76fbe40 100644
--- a/apps/files_external/lib/Command/Import.php
+++ b/apps/files_external/lib/Command/Import.php
@@ -1,26 +1,9 @@
<?php
+
/**
- * @copyright Copyright (c) 2016, ownCloud, Inc.
- *
- * @author Christoph Wurst <christoph@winzerhof-wurst.at>
- * @author Joas Schilling <coding@schilljs.com>
- * @author Robin Appelman <robin@icewind.nl>
- * @author Roeland Jago Douma <roeland@famdouma.nl>
- *
- * @license AGPL-3.0
- *
- * This code is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * 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, version 3,
- * along with this program. If not, see <http://www.gnu.org/licenses/>
- *
+ * SPDX-FileCopyrightText: 2017-2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
+ * SPDX-License-Identifier: AGPL-3.0-only
*/
namespace OCA\Files_External\Command;
@@ -77,7 +60,7 @@ class Import extends Base {
}
protected function execute(InputInterface $input, OutputInterface $output): int {
- $user = (string) $input->getOption('user');
+ $user = (string)$input->getOption('user');
$path = $input->getArgument('path');
if ($path === '-') {
$json = file_get_contents('php://stdin');
@@ -130,12 +113,12 @@ class Import extends Base {
foreach ($mounts as $mount) {
foreach ($existingMounts as $existingMount) {
if (
- $existingMount->getMountPoint() === $mount->getMountPoint() &&
- $existingMount->getApplicableGroups() === $mount->getApplicableGroups() &&
- $existingMount->getApplicableUsers() === $mount->getApplicableUsers() &&
- $existingMount->getBackendOptions() === $mount->getBackendOptions()
+ $existingMount->getMountPoint() === $mount->getMountPoint()
+ && $existingMount->getApplicableGroups() === $mount->getApplicableGroups()
+ && $existingMount->getApplicableUsers() === $mount->getApplicableUsers()
+ && $existingMount->getBackendOptions() === $mount->getBackendOptions()
) {
- $output->writeln("<error>Duplicate mount (" . $mount->getMountPoint() . ")</error>");
+ $output->writeln('<error>Duplicate mount (' . $mount->getMountPoint() . ')</error>');
return self::FAILURE;
}
}
diff --git a/apps/files_external/lib/Command/ListCommand.php b/apps/files_external/lib/Command/ListCommand.php
index e4a33a99a92..350916b6c2c 100644
--- a/apps/files_external/lib/Command/ListCommand.php
+++ b/apps/files_external/lib/Command/ListCommand.php
@@ -1,27 +1,9 @@
<?php
+
/**
- * @copyright Copyright (c) 2016, ownCloud, Inc.
- *
- * @author Christoph Wurst <christoph@winzerhof-wurst.at>
- * @author Daniel Kesselberg <mail@danielkesselberg.de>
- * @author Joas Schilling <coding@schilljs.com>
- * @author Robin Appelman <robin@icewind.nl>
- * @author Roeland Jago Douma <roeland@famdouma.nl>
- *
- * @license AGPL-3.0
- *
- * This code is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * 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, version 3,
- * along with this program. If not, see <http://www.gnu.org/licenses/>
- *
+ * SPDX-FileCopyrightText: 2017-2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
+ * SPDX-License-Identifier: AGPL-3.0-only
*/
namespace OCA\Files_External\Command;
@@ -104,11 +86,11 @@ class ListCommand extends Base {
$output->writeln('[]');
} else {
if ($userId === self::ALL) {
- $output->writeln("<info>No mounts configured</info>");
+ $output->writeln('<info>No mounts configured</info>');
} elseif ($userId) {
$output->writeln("<info>No mounts configured by $userId</info>");
} else {
- $output->writeln("<info>No admin mounts configured</info>");
+ $output->writeln('<info>No admin mounts configured</info>');
}
}
return;
@@ -125,12 +107,12 @@ class ListCommand extends Base {
}
if (!$input->getOption('show-password')) {
- $hideKeys = ['password', 'refresh_token', 'token', 'client_secret', 'public_key', 'private_key'];
+ $hideKeys = ['key', 'bucket', 'secret', 'password', 'refresh_token', 'token', 'client_secret', 'public_key', 'private_key'];
foreach ($mounts as $mount) {
$config = $mount->getBackendOptions();
foreach ($config as $key => $value) {
if (in_array($key, $hideKeys)) {
- $mount->setBackendOption($key, '***');
+ $mount->setBackendOption($key, '***REMOVED SENSITIVE VALUE***');
}
}
}
diff --git a/apps/files_external/lib/Command/Notify.php b/apps/files_external/lib/Command/Notify.php
index fd3a66a756e..0982aa5598b 100644
--- a/apps/files_external/lib/Command/Notify.php
+++ b/apps/files_external/lib/Command/Notify.php
@@ -3,36 +3,12 @@
declare(strict_types=1);
/**
- * @copyright Copyright (c) 2016 Robin Appelman <robin@icewind.nl>
- *
- * @author Ari Selseng <ari@selseng.net>
- * @author Christoph Wurst <christoph@winzerhof-wurst.at>
- * @author Joas Schilling <coding@schilljs.com>
- * @author Robin Appelman <robin@icewind.nl>
- * @author Roeland Jago Douma <roeland@famdouma.nl>
- *
- * @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/>.
- *
+ * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\Files_External\Command;
use Doctrine\DBAL\Exception\DriverException;
-use OC\Core\Command\Base;
-use OCA\Files_External\Lib\InsufficientDataForMeaningfulAnswerException;
-use OCA\Files_External\Lib\StorageConfig;
use OCA\Files_External\Service\GlobalStoragesService;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\Files\Notify\IChange;
@@ -40,7 +16,6 @@ use OCP\Files\Notify\INotifyHandler;
use OCP\Files\Notify\IRenameChange;
use OCP\Files\Storage\INotifyStorage;
use OCP\Files\Storage\IStorage;
-use OCP\Files\StorageNotAvailableException;
use OCP\IDBConnection;
use OCP\IUserManager;
use Psr\Log\LoggerInterface;
@@ -49,14 +24,14 @@ use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
-class Notify extends Base {
+class Notify extends StorageAuthBase {
public function __construct(
- private GlobalStoragesService $globalService,
private IDBConnection $connection,
private LoggerInterface $logger,
- private IUserManager $userManager
+ GlobalStoragesService $globalService,
+ IUserManager $userManager,
) {
- parent::__construct();
+ parent::__construct($globalService, $userManager);
}
protected function configure(): void {
@@ -97,71 +72,12 @@ class Notify extends Base {
parent::configure();
}
- private function getUserOption(InputInterface $input): ?string {
- if ($input->getOption('user')) {
- return (string)$input->getOption('user');
- }
-
- return $_ENV['NOTIFY_USER'] ?? $_SERVER['NOTIFY_USER'] ?? null;
- }
-
- private function getPasswordOption(InputInterface $input): ?string {
- if ($input->getOption('password')) {
- return (string)$input->getOption('password');
- }
-
- return $_ENV['NOTIFY_PASSWORD'] ?? $_SERVER['NOTIFY_PASSWORD'] ?? null;
- }
-
protected function execute(InputInterface $input, OutputInterface $output): int {
- $mount = $this->globalService->getStorage($input->getArgument('mount_id'));
- if (is_null($mount)) {
- $output->writeln('<error>Mount not found</error>');
+ [$mount, $storage] = $this->createStorage($input, $output);
+ if ($storage === null) {
return self::FAILURE;
}
- $noAuth = false;
-
- $userOption = $this->getUserOption($input);
- $passwordOption = $this->getPasswordOption($input);
-
- // if only the user is provided, we get the user object to pass along to the auth backend
- // this allows using saved user credentials
- $user = ($userOption && !$passwordOption) ? $this->userManager->get($userOption) : null;
-
- try {
- $authBackend = $mount->getAuthMechanism();
- $authBackend->manipulateStorageConfig($mount, $user);
- } catch (InsufficientDataForMeaningfulAnswerException $e) {
- $noAuth = true;
- } catch (StorageNotAvailableException $e) {
- $noAuth = true;
- }
- if ($userOption) {
- $mount->setBackendOption('user', $userOption);
- }
- if ($passwordOption) {
- $mount->setBackendOption('password', $passwordOption);
- }
-
- try {
- $backend = $mount->getBackend();
- $backend->manipulateStorageConfig($mount, $user);
- } catch (InsufficientDataForMeaningfulAnswerException $e) {
- $noAuth = true;
- } catch (StorageNotAvailableException $e) {
- $noAuth = true;
- }
-
- try {
- $storage = $this->createStorage($mount);
- } catch (\Exception $e) {
- $output->writeln('<error>Error while trying to create storage</error>');
- if ($noAuth) {
- $output->writeln('<error>Username and/or password required</error>');
- }
- return self::FAILURE;
- }
if (!$storage instanceof INotifyStorage) {
$output->writeln('<error>Mount of type "' . $mount->getBackend()->getText() . '" does not support active update notifications</error>');
return self::FAILURE;
@@ -179,7 +95,7 @@ class Notify extends Base {
$this->selfTest($storage, $notifyHandler, $output);
}
- $notifyHandler->listen(function (IChange $change) use ($mount, $output, $dryRun) {
+ $notifyHandler->listen(function (IChange $change) use ($mount, $output, $dryRun): void {
$this->logUpdate($change, $output);
if ($change instanceof IRenameChange) {
$this->markParentAsOutdated($mount->getId(), $change->getTargetPath(), $output, $dryRun);
@@ -189,11 +105,6 @@ class Notify extends Base {
return self::SUCCESS;
}
- private function createStorage(StorageConfig $mount): IStorage {
- $class = $mount->getBackend()->getStorageClass();
- return new $class($mount->getBackendOptions());
- }
-
private function markParentAsOutdated($mountId, $path, OutputInterface $output, bool $dryRun): void {
$parent = ltrim(dirname($path), '/');
if ($parent === '.') {
@@ -225,7 +136,7 @@ class Notify extends Base {
$storageIds = array_values(array_unique($storageIds));
if ($dryRun) {
- $output->writeln(" dry-run: skipping database write");
+ $output->writeln(' dry-run: skipping database write');
} else {
$result = $this->updateParent($storageIds, $parent);
if ($result === 0) {
@@ -257,7 +168,7 @@ class Notify extends Base {
}
private function getStorageIds(int $mountId, string $path): array {
- $pathHash = md5(trim((string)\OC_Util::normalizeUnicode($path), '/'));
+ $pathHash = md5(trim(\OC_Util::normalizeUnicode($path), '/'));
$qb = $this->connection->getQueryBuilder();
return $qb
->select('storage_id', 'user_id')
@@ -270,7 +181,7 @@ class Notify extends Base {
}
private function updateParent(array $storageIds, string $parent): int {
- $pathHash = md5(trim((string)\OC_Util::normalizeUnicode($parent), '/'));
+ $pathHash = md5(trim(\OC_Util::normalizeUnicode($parent), '/'));
$qb = $this->connection->getQueryBuilder();
return $qb
->update('filecache')
@@ -304,7 +215,7 @@ class Notify extends Base {
private function selfTest(IStorage $storage, INotifyHandler $notifyHandler, OutputInterface $output): void {
usleep(100 * 1000); //give time for the notify to start
if (!$storage->file_put_contents('/.nc_test_file.txt', 'test content')) {
- $output->writeln("Failed to create test file for self-test");
+ $output->writeln('Failed to create test file for self-test');
return;
}
$storage->mkdir('/.nc_test_folder');
diff --git a/apps/files_external/lib/Command/Option.php b/apps/files_external/lib/Command/Option.php
index 8ef423e2699..3fda3fcb3cf 100644
--- a/apps/files_external/lib/Command/Option.php
+++ b/apps/files_external/lib/Command/Option.php
@@ -1,24 +1,9 @@
<?php
+
/**
- * @copyright Copyright (c) 2016, ownCloud, Inc.
- *
- * @author Joas Schilling <coding@schilljs.com>
- * @author Robin Appelman <robin@icewind.nl>
- *
- * @license AGPL-3.0
- *
- * This code is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * 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, version 3,
- * along with this program. If not, see <http://www.gnu.org/licenses/>
- *
+ * SPDX-FileCopyrightText: 2019-2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
+ * SPDX-License-Identifier: AGPL-3.0-only
*/
namespace OCA\Files_External\Command;
@@ -54,7 +39,7 @@ class Option extends Config {
if (!is_string($value)) { // show bools and objects correctly
$value = json_encode($value);
}
- $output->writeln($value);
+ $output->writeln((string)$value);
}
/**
diff --git a/apps/files_external/lib/Command/Scan.php b/apps/files_external/lib/Command/Scan.php
new file mode 100644
index 00000000000..75d98878baa
--- /dev/null
+++ b/apps/files_external/lib/Command/Scan.php
@@ -0,0 +1,166 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OCA\Files_External\Command;
+
+use OC\Files\Cache\Scanner;
+use OCA\Files_External\Service\GlobalStoragesService;
+use OCP\IUserManager;
+use OCP\Lock\LockedException;
+use Symfony\Component\Console\Helper\Table;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class Scan extends StorageAuthBase {
+ protected float $execTime = 0;
+ protected int $foldersCounter = 0;
+ protected int $filesCounter = 0;
+
+ public function __construct(
+ GlobalStoragesService $globalService,
+ IUserManager $userManager,
+ ) {
+ parent::__construct($globalService, $userManager);
+ }
+
+ protected function configure(): void {
+ $this
+ ->setName('files_external:scan')
+ ->setDescription('Scan an external storage for changed files')
+ ->addArgument(
+ 'mount_id',
+ InputArgument::REQUIRED,
+ 'the mount id of the mount to scan'
+ )->addOption(
+ 'user',
+ 'u',
+ InputOption::VALUE_REQUIRED,
+ 'The username for the remote mount (required only for some mount configuration that don\'t store credentials)'
+ )->addOption(
+ 'password',
+ 'p',
+ InputOption::VALUE_REQUIRED,
+ 'The password for the remote mount (required only for some mount configuration that don\'t store credentials)'
+ )->addOption(
+ 'path',
+ '',
+ InputOption::VALUE_OPTIONAL,
+ 'The path in the storage to scan',
+ ''
+ )->addOption(
+ 'unscanned',
+ '',
+ InputOption::VALUE_NONE,
+ 'only scan files which are marked as not fully scanned'
+ );
+ parent::configure();
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output): int {
+ [, $storage] = $this->createStorage($input, $output);
+ if ($storage === null) {
+ return 1;
+ }
+
+ $path = $input->getOption('path');
+
+ $this->execTime = -microtime(true);
+
+ /** @var Scanner $scanner */
+ $scanner = $storage->getScanner();
+
+ $scanner->listen('\OC\Files\Cache\Scanner', 'scanFile', function (string $path) use ($output): void {
+ $output->writeln("\tFile\t<info>$path</info>", OutputInterface::VERBOSITY_VERBOSE);
+ ++$this->filesCounter;
+ $this->abortIfInterrupted();
+ });
+
+ $scanner->listen('\OC\Files\Cache\Scanner', 'scanFolder', function (string $path) use ($output): void {
+ $output->writeln("\tFolder\t<info>$path</info>", OutputInterface::VERBOSITY_VERBOSE);
+ ++$this->foldersCounter;
+ $this->abortIfInterrupted();
+ });
+
+ try {
+ if ($input->getOption('unscanned')) {
+ if ($path !== '') {
+ $output->writeln('<error>--unscanned is mutually exclusive with --path</error>');
+ return 1;
+ }
+ $scanner->backgroundScan();
+ } else {
+ $scanner->scan($path);
+ }
+ } catch (LockedException $e) {
+ if (is_string($e->getReadablePath()) && str_starts_with($e->getReadablePath(), 'scanner::')) {
+ if ($e->getReadablePath() === 'scanner::') {
+ $output->writeln('<error>Another process is already scanning this storage</error>');
+ } else {
+ $output->writeln('<error>Another process is already scanning \'' . substr($e->getReadablePath(), strlen('scanner::')) . '\' in this storage</error>');
+ }
+ } else {
+ throw $e;
+ }
+ }
+
+ $this->presentStats($output);
+
+ return 0;
+ }
+
+ /**
+ * @param OutputInterface $output
+ */
+ protected function presentStats(OutputInterface $output): void {
+ // Stop the timer
+ $this->execTime += microtime(true);
+
+ $headers = [
+ 'Folders', 'Files', 'Elapsed time'
+ ];
+
+ $this->showSummary($headers, [], $output);
+ }
+
+ /**
+ * Shows a summary of operations
+ *
+ * @param string[] $headers
+ * @param string[] $rows
+ * @param OutputInterface $output
+ */
+ protected function showSummary(array $headers, array $rows, OutputInterface $output): void {
+ $niceDate = $this->formatExecTime();
+ if (!$rows) {
+ $rows = [
+ $this->foldersCounter,
+ $this->filesCounter,
+ $niceDate,
+ ];
+ }
+ $table = new Table($output);
+ $table
+ ->setHeaders($headers)
+ ->setRows([$rows]);
+ $table->render();
+ }
+
+
+ /**
+ * Formats microtime into a human readable format
+ *
+ * @return string
+ */
+ protected function formatExecTime(): string {
+ $secs = round($this->execTime);
+ # convert seconds into HH:MM:SS form
+ return sprintf('%02d:%02d:%02d', ($secs / 3600), ($secs / 60 % 60), $secs % 60);
+ }
+}
diff --git a/apps/files_external/lib/Command/StorageAuthBase.php b/apps/files_external/lib/Command/StorageAuthBase.php
new file mode 100644
index 00000000000..6f830a08a60
--- /dev/null
+++ b/apps/files_external/lib/Command/StorageAuthBase.php
@@ -0,0 +1,114 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OCA\Files_External\Command;
+
+use OC\Core\Command\Base;
+use OCA\Files_External\Lib\InsufficientDataForMeaningfulAnswerException;
+use OCA\Files_External\Lib\StorageConfig;
+use OCA\Files_External\NotFoundException;
+use OCA\Files_External\Service\GlobalStoragesService;
+use OCP\Files\Storage\IStorage;
+use OCP\Files\StorageNotAvailableException;
+use OCP\IUserManager;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+
+abstract class StorageAuthBase extends Base {
+ public function __construct(
+ protected GlobalStoragesService $globalService,
+ protected IUserManager $userManager,
+ ) {
+ parent::__construct();
+ }
+
+ private function getUserOption(InputInterface $input): ?string {
+ if ($input->getOption('user')) {
+ return (string)$input->getOption('user');
+ }
+
+ return $_ENV['NOTIFY_USER'] ?? $_SERVER['NOTIFY_USER'] ?? null;
+ }
+
+ private function getPasswordOption(InputInterface $input): ?string {
+ if ($input->getOption('password')) {
+ return (string)$input->getOption('password');
+ }
+
+ return $_ENV['NOTIFY_PASSWORD'] ?? $_SERVER['NOTIFY_PASSWORD'] ?? null;
+ }
+
+ /**
+ * @param InputInterface $input
+ * @param OutputInterface $output
+ * @return array
+ * @psalm-return array{0: StorageConfig, 1: IStorage}|array{0: null, 1: null}
+ */
+ protected function createStorage(InputInterface $input, OutputInterface $output): array {
+ try {
+ /** @var StorageConfig|null $mount */
+ $mount = $this->globalService->getStorage((int)$input->getArgument('mount_id'));
+ } catch (NotFoundException $e) {
+ $output->writeln('<error>Mount not found</error>');
+ return [null, null];
+ }
+ if (is_null($mount)) {
+ $output->writeln('<error>Mount not found</error>');
+ return [null, null];
+ }
+ $noAuth = false;
+
+ $userOption = $this->getUserOption($input);
+ $passwordOption = $this->getPasswordOption($input);
+
+ // if only the user is provided, we get the user object to pass along to the auth backend
+ // this allows using saved user credentials
+ $user = ($userOption && !$passwordOption) ? $this->userManager->get($userOption) : null;
+
+ try {
+ $authBackend = $mount->getAuthMechanism();
+ $authBackend->manipulateStorageConfig($mount, $user);
+ } catch (InsufficientDataForMeaningfulAnswerException $e) {
+ $noAuth = true;
+ } catch (StorageNotAvailableException $e) {
+ $noAuth = true;
+ }
+
+ if ($userOption) {
+ $mount->setBackendOption('user', $userOption);
+ }
+ if ($passwordOption) {
+ $mount->setBackendOption('password', $passwordOption);
+ }
+
+ try {
+ $backend = $mount->getBackend();
+ $backend->manipulateStorageConfig($mount, $user);
+ } catch (InsufficientDataForMeaningfulAnswerException $e) {
+ $noAuth = true;
+ } catch (StorageNotAvailableException $e) {
+ $noAuth = true;
+ }
+
+ try {
+ $class = $mount->getBackend()->getStorageClass();
+ /** @var IStorage $storage */
+ $storage = new $class($mount->getBackendOptions());
+ if (!$storage->test()) {
+ throw new \Exception();
+ }
+ return [$mount, $storage];
+ } catch (\Exception $e) {
+ $output->writeln('<error>Error while trying to create storage</error>');
+ if ($noAuth) {
+ $output->writeln('<error>Username and/or password required</error>');
+ }
+ return [null, null];
+ }
+ }
+}
diff --git a/apps/files_external/lib/Command/Verify.php b/apps/files_external/lib/Command/Verify.php
index 590206d2099..ecebbe0f7e6 100644
--- a/apps/files_external/lib/Command/Verify.php
+++ b/apps/files_external/lib/Command/Verify.php
@@ -1,40 +1,24 @@
<?php
+
/**
- * @copyright Copyright (c) 2016, ownCloud, Inc.
- *
- * @author Christoph Wurst <christoph@winzerhof-wurst.at>
- * @author Joas Schilling <coding@schilljs.com>
- * @author Morris Jobke <hey@morrisjobke.de>
- * @author Robin Appelman <robin@icewind.nl>
- *
- * @license AGPL-3.0
- *
- * This code is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * 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, version 3,
- * along with this program. If not, see <http://www.gnu.org/licenses/>
- *
+ * SPDX-FileCopyrightText: 2019-2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
+ * SPDX-License-Identifier: AGPL-3.0-only
*/
namespace OCA\Files_External\Command;
use OC\Core\Command\Base;
use OCA\Files_External\Lib\InsufficientDataForMeaningfulAnswerException;
use OCA\Files_External\Lib\StorageConfig;
+use OCA\Files_External\MountConfig;
use OCA\Files_External\NotFoundException;
use OCA\Files_External\Service\GlobalStoragesService;
+use OCP\AppFramework\Http;
use OCP\Files\StorageNotAvailableException;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
-use Symfony\Component\HttpFoundation\Response;
class Verify extends Base {
public function __construct(
@@ -68,7 +52,7 @@ class Verify extends Base {
$mount = $this->globalService->getStorage($mountId);
} catch (NotFoundException $e) {
$output->writeln('<error>Mount with id "' . $mountId . ' not found, check "occ files_external:list" to get available mounts"</error>');
- return Response::HTTP_NOT_FOUND;
+ return Http::STATUS_NOT_FOUND;
}
$this->updateStorageStatus($mount, $configInput, $output);
@@ -110,10 +94,9 @@ class Verify extends Base {
$backend = $storage->getBackend();
// update status (can be time-consuming)
$storage->setStatus(
- \OCA\Files_External\MountConfig::getBackendStatus(
+ MountConfig::getBackendStatus(
$backend->getStorageClass(),
$storage->getBackendOptions(),
- false
)
);
} catch (InsufficientDataForMeaningfulAnswerException $e) {