, parameters:array}> */ public function availableBackends(): array { return $this->factory()->describeBackends(); } /** * Add a target: connect, create the repository there (or adopt an existing one made with this * installation's key), then save it. * * @param array $options * @return array{target: Target, created: bool} */ public function add(string $name, string $backend, string $auth, array $options, string $basePath): array { if (!preg_match('/^[A-Za-z0-9][A-Za-z0-9 _.-]{0,63}$/', $name)) { throw new \InvalidArgumentException('Name: letters, digits, space, _ . - (max 64)'); } try { $this->mapper->findByName($name); throw new \InvalidArgumentException("A target named '$name' already exists"); } catch (\OCP\AppFramework\Db\DoesNotExistException) { } $keys = $this->keys->keyRing(); $storageBackend = $this->buildBackend($backend, $auth, $options, $basePath); if ($storageBackend->exists('config')) { $repo = Repository::openWithKey($storageBackend, $keys, $this->indexCache, $this->anchor); $created = false; } else { $repo = Repository::initWithKey($storageBackend, $keys, $this->keys->wrappedKey(), $this->anchor); $created = true; } foreach ($this->mapper->findAll() as $existing) { if ($existing->getRepositoryId() === $repo->id()) { throw new \InvalidArgumentException("This repository is already registered as '{$existing->getName()}'"); } } $target = new Target(); $target->setName($name); $target->setBackend($backend); $target->setAuth($auth); $target->setOptions($this->crypto->encrypt(json_encode($options, JSON_THROW_ON_ERROR))); $target->setBasePath(trim($basePath, '/')); $target->setRepositoryId($repo->id()); $target->setCreatedAt(time()); return ['target' => $this->mapper->insert($target), 'created' => $created]; } /** @return list */ public function list(): array { return $this->mapper->findAll(); } public function get(string $nameOrId): Target { return ctype_digit($nameOrId) ? $this->mapper->find((int)$nameOrId) : $this->mapper->findByName($nameOrId); } public function setAppendOnly(Target $target, bool $appendOnly): Target { $target->setAppendOnly($appendOnly); return $this->mapper->update($target); } /** Remove the target from NG Backup. The backups on the target itself are not touched. */ public function remove(Target $target): void { $this->mapper->delete($target); } public function backend(Target $target): IBackend { return $this->buildBackend($target->getBackend(), $target->getAuth(), $this->options($target), $target->getBasePath()); } public function repository(Target $target): Repository { $repo = Repository::openWithKey($this->backend($target), $this->keys->keyRing(), $this->indexCache, $this->anchor); if ($target->getRepositoryId() !== null && $repo->id() !== $target->getRepositoryId()) { throw new \RuntimeException('The target now contains a different repository than when it was added'); } return $repo; } /** @return array */ public function options(Target $target): array { return json_decode($this->crypto->decrypt($target->getOptions()), true, 512, JSON_THROW_ON_ERROR); } private function buildBackend(string $backend, string $auth, array $options, string $basePath): StorageBackend { return new StorageBackend($this->factory()->create($backend, $auth, $options), $basePath); } private ?ExternalStorageFactory $factory = null; /** * files_external's connection code, without changing the app's state: * - enabled: it is booted by Nextcloud; use its own BackendService; * - disabled: do NOT boot it (booting registers its mount provider and listeners, which use * its database tables; on installations where it was never enabled those do not exist). * Only its classes are loaded, and a private BackendService gets its backends and auth * mechanisms from its Application class. */ private function factory(): ExternalStorageFactory { if ($this->factory !== null) { return $this->factory; } if ($this->appManager->isEnabledForAnyone('files_external')) { $this->appManager->loadApp('files_external'); return $this->factory = new ExternalStorageFactory(Server::get(\OCA\Files_External\Service\BackendService::class)); } if (!class_exists(\OCA\Files_External\AppInfo\Application::class)) { $path = $this->appManager->getAppPath('files_external'); require_once $path . '/composer/autoload.php'; } $service = new \OCA\Files_External\Service\BackendService( Server::get(\OCP\IAppConfig::class), Server::get(\Psr\Log\LoggerInterface::class)); $app = new \OCA\Files_External\AppInfo\Application(); $service->registerBackendProvider($app); $service->registerAuthMechanismProvider($app); return $this->factory = new ExternalStorageFactory($service); } }