Merge pull request #52784 from nextcloud/backport/52681/stable30

[stable30] feat: add command to check files_external dependencies
This commit is contained in:
Robin Appelman 2025-05-15 14:09:35 +02:00 committed by GitHub
commit cc56c3b603
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 134 additions and 20 deletions

View file

@ -53,6 +53,7 @@ External storage can be configured using the GUI or at the command line. This se
<command>OCA\Files_External\Command\Verify</command>
<command>OCA\Files_External\Command\Notify</command>
<command>OCA\Files_External\Command\Scan</command>
<command>OCA\Files_External\Command\Dependencies</command>
</commands>
<settings>

View file

@ -14,6 +14,7 @@ return array(
'OCA\\Files_External\\Command\\Config' => $baseDir . '/../lib/Command/Config.php',
'OCA\\Files_External\\Command\\Create' => $baseDir . '/../lib/Command/Create.php',
'OCA\\Files_External\\Command\\Delete' => $baseDir . '/../lib/Command/Delete.php',
'OCA\\Files_External\\Command\\Dependencies' => $baseDir . '/../lib/Command/Dependencies.php',
'OCA\\Files_External\\Command\\Export' => $baseDir . '/../lib/Command/Export.php',
'OCA\\Files_External\\Command\\Import' => $baseDir . '/../lib/Command/Import.php',
'OCA\\Files_External\\Command\\ListCommand' => $baseDir . '/../lib/Command/ListCommand.php',
@ -96,6 +97,7 @@ return array(
'OCA\\Files_External\\Lib\\Storage\\SMB' => $baseDir . '/../lib/Lib/Storage/SMB.php',
'OCA\\Files_External\\Lib\\Storage\\StreamWrapper' => $baseDir . '/../lib/Lib/Storage/StreamWrapper.php',
'OCA\\Files_External\\Lib\\Storage\\Swift' => $baseDir . '/../lib/Lib/Storage/Swift.php',
'OCA\\Files_External\\Lib\\Storage\\SystemBridge' => $baseDir . '/../lib/Lib/Storage/SystemBridge.php',
'OCA\\Files_External\\Lib\\VisibilityTrait' => $baseDir . '/../lib/Lib/VisibilityTrait.php',
'OCA\\Files_External\\Listener\\GroupDeletedListener' => $baseDir . '/../lib/Listener/GroupDeletedListener.php',
'OCA\\Files_External\\Listener\\LoadAdditionalListener' => $baseDir . '/../lib/Listener/LoadAdditionalListener.php',

View file

@ -29,6 +29,7 @@ class ComposerStaticInitFiles_External
'OCA\\Files_External\\Command\\Config' => __DIR__ . '/..' . '/../lib/Command/Config.php',
'OCA\\Files_External\\Command\\Create' => __DIR__ . '/..' . '/../lib/Command/Create.php',
'OCA\\Files_External\\Command\\Delete' => __DIR__ . '/..' . '/../lib/Command/Delete.php',
'OCA\\Files_External\\Command\\Dependencies' => __DIR__ . '/..' . '/../lib/Command/Dependencies.php',
'OCA\\Files_External\\Command\\Export' => __DIR__ . '/..' . '/../lib/Command/Export.php',
'OCA\\Files_External\\Command\\Import' => __DIR__ . '/..' . '/../lib/Command/Import.php',
'OCA\\Files_External\\Command\\ListCommand' => __DIR__ . '/..' . '/../lib/Command/ListCommand.php',
@ -111,6 +112,7 @@ class ComposerStaticInitFiles_External
'OCA\\Files_External\\Lib\\Storage\\SMB' => __DIR__ . '/..' . '/../lib/Lib/Storage/SMB.php',
'OCA\\Files_External\\Lib\\Storage\\StreamWrapper' => __DIR__ . '/..' . '/../lib/Lib/Storage/StreamWrapper.php',
'OCA\\Files_External\\Lib\\Storage\\Swift' => __DIR__ . '/..' . '/../lib/Lib/Storage/Swift.php',
'OCA\\Files_External\\Lib\\Storage\\SystemBridge' => __DIR__ . '/..' . '/../lib/Lib/Storage/SystemBridge.php',
'OCA\\Files_External\\Lib\\VisibilityTrait' => __DIR__ . '/..' . '/../lib/Lib/VisibilityTrait.php',
'OCA\\Files_External\\Listener\\GroupDeletedListener' => __DIR__ . '/..' . '/../lib/Listener/GroupDeletedListener.php',
'OCA\\Files_External\\Listener\\LoadAdditionalListener' => __DIR__ . '/..' . '/../lib/Listener/LoadAdditionalListener.php',

View file

@ -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;
}
}

View file

@ -11,19 +11,20 @@ namespace OCA\Files_External\Lib\Backend;
use Icewind\SMB\BasicAuth;
use Icewind\SMB\KerberosApacheAuth;
use Icewind\SMB\KerberosAuth;
use Icewind\SMB\Native\NativeServer;
use Icewind\SMB\Wrapped\Server;
use OCA\Files_External\Lib\Auth\AuthMechanism;
use OCA\Files_External\Lib\Auth\Password\Password;
use OCA\Files_External\Lib\Auth\SMB\KerberosApacheAuth as KerberosApacheAuthMechanism;
use OCA\Files_External\Lib\DefinitionParameter;
use OCA\Files_External\Lib\InsufficientDataForMeaningfulAnswerException;
use OCA\Files_External\Lib\LegacyDependencyCheckPolyfill;
use OCA\Files_External\Lib\MissingDependency;
use OCA\Files_External\Lib\Storage\SystemBridge;
use OCA\Files_External\Lib\StorageConfig;
use OCP\IL10N;
use OCP\IUser;
class SMB extends Backend {
use LegacyDependencyCheckPolyfill;
public function __construct(IL10N $l, Password $legacyAuth) {
$this
->setIdentifier('smb')
@ -123,4 +124,20 @@ class SMB extends Backend {
$storage->setBackendOption('auth', $smbAuth);
}
public function checkDependencies() {
$system = \OCP\Server::get(SystemBridge::class);
if (NativeServer::available($system)) {
return [];
} elseif (Server::available($system)) {
$missing = new MissingDependency('php-smbclient');
$missing->setOptional(true);
$missing->setMessage('The php-smbclient library provides improved compatibility and performance for SMB storages.');
return [$missing];
} else {
$missing = new MissingDependency('php-smbclient');
$missing->setMessage('Either the php-smbclient library (preferred) or the smbclient binary is required for SMB storages.');
return [$missing, new MissingDependency('smbclient')];
}
}
}

View file

@ -11,18 +11,16 @@ namespace OCA\Files_External\Lib;
* External storage backend dependency
*/
class MissingDependency {
/** @var string */
private $dependency;
/** @var string|null Custom message */
private $message = null;
private ?string $message = null;
private bool $optional = false;
/**
* @param string $dependency
*/
public function __construct($dependency) {
$this->dependency = $dependency;
public function __construct(
private string $dependency,
) {
}
public function getDependency(): string {
@ -41,4 +39,12 @@ class MissingDependency {
$this->message = $message;
return $this;
}
public function isOptional(): bool {
return $this->optional;
}
public function setOptional(bool $optional): void {
$this->optional = $optional;
}
}

View file

@ -5,6 +5,7 @@
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
* SPDX-License-Identifier: AGPL-3.0-only
*/
namespace OCA\Files_External\Lib\Storage;
use Icewind\SMB\ACL;
@ -22,7 +23,7 @@ use Icewind\SMB\IFileInfo;
use Icewind\SMB\Native\NativeServer;
use Icewind\SMB\Options;
use Icewind\SMB\ServerFactory;
use Icewind\SMB\System;
use Icewind\SMB\Wrapped\Server;
use Icewind\Streams\CallbackWrapper;
use Icewind\Streams\IteratorDirectory;
use OC\Files\Filesystem;
@ -93,7 +94,7 @@ class SMB extends Common implements INotifyStorage {
}
$this->logger = $params['logger'];
} else {
$this->logger = \OC::$server->get(LoggerInterface::class);
$this->logger = \OCP\Server::get(LoggerInterface::class);
}
$options = new Options();
@ -103,7 +104,8 @@ class SMB extends Common implements INotifyStorage {
$options->setTimeout($timeout);
}
}
$serverFactory = new ServerFactory($options);
$system = \OCP\Server::get(SystemBridge::class);
$serverFactory = new ServerFactory($options, $system);
$this->server = $serverFactory->createServer($params['host'], $auth);
$this->share = $this->server->getShare(trim($params['share'], '/'));
@ -721,11 +723,9 @@ class SMB extends Common implements INotifyStorage {
/**
* check if smbclient is installed
*/
public static function checkDependencies() {
return (
(bool) \OC_Helper::findBinaryPath('smbclient')
|| NativeServer::available(new System())
) ? true : ['smbclient'];
public static function checkDependencies(): array|bool {
$system = \OCP\Server::get(SystemBridge::class);
return Server::available($system) || NativeServer::available($system) ?: ['smbclient'];
}
/**

View file

@ -0,0 +1,27 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2024 Robin Appelman <robin@icewind.nl>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\Files_External\Lib\Storage;
use Icewind\SMB\System;
use OCP\IBinaryFinder;
/**
* Bridge the NC and SMB binary finding logic
*/
class SystemBridge extends System {
public function __construct(
private IBinaryFinder $binaryFinder,
) {
}
protected function getBinaryPath(string $binary): ?string {
$path = $this->binaryFinder->findBinaryPath($binary);
return $path !== false ? $path : null;
}
}

View file

@ -13,6 +13,7 @@ use OCA\Files_External\Lib\Auth\AuthMechanism;
use OCA\Files_External\Lib\Backend\Backend;
use OCA\Files_External\Lib\Config\IAuthMechanismProvider;
use OCA\Files_External\Lib\Config\IBackendProvider;
use OCA\Files_External\Lib\MissingDependency;
use OCP\EventDispatcher\GenericEvent;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\IConfig;
@ -199,7 +200,8 @@ class BackendService {
*/
public function getAvailableBackends() {
return array_filter($this->getBackends(), function ($backend) {
return !$backend->checkDependencies();
$missing = array_filter($backend->checkDependencies(), fn (MissingDependency $dependency) => !$dependency->isOptional());
return count($missing) === 0;
});
}

View file

@ -11,7 +11,7 @@ namespace OCP;
/**
* Service that find the binary path for a program.
*
* This interface should be injected via depency injection and must
* This interface should be injected via dependency injection and must
* not be implemented in applications.
*
* @since 25.0.0