aboutsummaryrefslogtreecommitdiffstats
path: root/apps/files_sharing/lib/External
diff options
context:
space:
mode:
Diffstat (limited to 'apps/files_sharing/lib/External')
-rw-r--r--apps/files_sharing/lib/External/Cache.php41
-rw-r--r--apps/files_sharing/lib/External/Manager.php293
-rw-r--r--apps/files_sharing/lib/External/Mount.php42
-rw-r--r--apps/files_sharing/lib/External/MountProvider.php49
-rw-r--r--apps/files_sharing/lib/External/Scanner.php34
-rw-r--r--apps/files_sharing/lib/External/Storage.php157
-rw-r--r--apps/files_sharing/lib/External/Watcher.php22
7 files changed, 266 insertions, 372 deletions
diff --git a/apps/files_sharing/lib/External/Cache.php b/apps/files_sharing/lib/External/Cache.php
index f353022d067..027f682d818 100644
--- a/apps/files_sharing/lib/External/Cache.php
+++ b/apps/files_sharing/lib/External/Cache.php
@@ -1,47 +1,30 @@
<?php
+
/**
- * @copyright Copyright (c) 2016, ownCloud, Inc.
- *
- * @author Robin Appelman <robin@icewind.nl>
- * @author Vincent Petry <vincent@nextcloud.com>
- *
- * @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_Sharing\External;
use OCP\Federation\ICloudId;
class Cache extends \OC\Files\Cache\Cache {
- /** @var ICloudId */
- private $cloudId;
private $remote;
private $remoteUser;
- private $storage;
/**
- * @param \OCA\Files_Sharing\External\Storage $storage
+ * @param Storage $storage
* @param ICloudId $cloudId
*/
- public function __construct($storage, ICloudId $cloudId) {
- $this->cloudId = $cloudId;
- $this->storage = $storage;
- [, $remote] = explode('://', $cloudId->getRemote(), 2);
+ public function __construct(
+ private $storage,
+ private ICloudId $cloudId,
+ ) {
+ [, $remote] = explode('://', $this->cloudId->getRemote(), 2);
$this->remote = $remote;
- $this->remoteUser = $cloudId->getUser();
- parent::__construct($storage);
+ $this->remoteUser = $this->cloudId->getUser();
+ parent::__construct($this->storage);
}
public function get($file) {
diff --git a/apps/files_sharing/lib/External/Manager.php b/apps/files_sharing/lib/External/Manager.php
index f18d8346dc4..ff4781eba0f 100644
--- a/apps/files_sharing/lib/External/Manager.php
+++ b/apps/files_sharing/lib/External/Manager.php
@@ -1,35 +1,9 @@
<?php
+
/**
- * @copyright Copyright (c) 2016, ownCloud, Inc.
- *
- * @author Bjoern Schiessle <bjoern@schiessle.org>
- * @author Björn Schießle <bjoern@schiessle.org>
- * @author Christoph Wurst <christoph@winzerhof-wurst.at>
- * @author Daniel Hansson <daniel@techandme.se>
- * @author Joas Schilling <coding@schilljs.com>
- * @author Jörn Friedrich Dreyer <jfd@butonic.de>
- * @author Julius Härtl <jus@bitgrid.net>
- * @author Lukas Reschke <lukas@statuscode.ch>
- * @author Morris Jobke <hey@morrisjobke.de>
- * @author Robin Appelman <robin@icewind.nl>
- * @author Roeland Jago Douma <roeland@famdouma.nl>
- * @author Stefan Weil <sw@weilnetz.de>
- * @author Vincent Petry <vincent@nextcloud.com>
- *
- * @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_Sharing\External;
@@ -38,11 +12,13 @@ use Doctrine\DBAL\Driver\Exception;
use OC\Files\Filesystem;
use OCA\FederatedFileSharing\Events\FederatedShareAddedEvent;
use OCA\Files_Sharing\Helper;
+use OCA\Files_Sharing\ResponseDefinitions;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\Federation\ICloudFederationFactory;
use OCP\Federation\ICloudFederationProviderManager;
use OCP\Files;
+use OCP\Files\Events\InvalidateMountCacheEvent;
use OCP\Files\NotFoundException;
use OCP\Files\Storage\IStorageFactory;
use OCP\Http\Client\IClientService;
@@ -56,77 +32,32 @@ use OCP\Share;
use OCP\Share\IShare;
use Psr\Log\LoggerInterface;
+/**
+ * @psalm-import-type Files_SharingRemoteShare from ResponseDefinitions
+ */
class Manager {
public const STORAGE = '\OCA\Files_Sharing\External\Storage';
/** @var string|null */
private $uid;
- /** @var IDBConnection */
- private $connection;
-
- /** @var \OC\Files\Mount\Manager */
- private $mountManager;
-
- /** @var IStorageFactory */
- private $storageLoader;
-
- /** @var IClientService */
- private $clientService;
-
- /** @var IManager */
- private $notificationManager;
-
- /** @var IDiscoveryService */
- private $discoveryService;
-
- /** @var ICloudFederationProviderManager */
- private $cloudFederationProviderManager;
-
- /** @var ICloudFederationFactory */
- private $cloudFederationFactory;
-
- /** @var IGroupManager */
- private $groupManager;
-
- /** @var IUserManager */
- private $userManager;
-
- /** @var IEventDispatcher */
- private $eventDispatcher;
-
- /** @var LoggerInterface */
- private $logger;
-
public function __construct(
- IDBConnection $connection,
- \OC\Files\Mount\Manager $mountManager,
- IStorageFactory $storageLoader,
- IClientService $clientService,
- IManager $notificationManager,
- IDiscoveryService $discoveryService,
- ICloudFederationProviderManager $cloudFederationProviderManager,
- ICloudFederationFactory $cloudFederationFactory,
- IGroupManager $groupManager,
- IUserManager $userManager,
- IUserSession $userSession,
- IEventDispatcher $eventDispatcher,
- LoggerInterface $logger
+ private IDBConnection $connection,
+ private \OC\Files\Mount\Manager $mountManager,
+ private IStorageFactory $storageLoader,
+ private IClientService $clientService,
+ private IManager $notificationManager,
+ private IDiscoveryService $discoveryService,
+ private ICloudFederationProviderManager $cloudFederationProviderManager,
+ private ICloudFederationFactory $cloudFederationFactory,
+ private IGroupManager $groupManager,
+ private IUserManager $userManager,
+ IUserSession $userSession,
+ private IEventDispatcher $eventDispatcher,
+ private LoggerInterface $logger,
) {
$user = $userSession->getUser();
- $this->connection = $connection;
- $this->mountManager = $mountManager;
- $this->storageLoader = $storageLoader;
- $this->clientService = $clientService;
$this->uid = $user ? $user->getUID() : null;
- $this->notificationManager = $notificationManager;
- $this->discoveryService = $discoveryService;
- $this->cloudFederationProviderManager = $cloudFederationProviderManager;
- $this->cloudFederationFactory = $cloudFederationFactory;
- $this->groupManager = $groupManager;
- $this->userManager = $userManager;
- $this->eventDispatcher = $eventDispatcher;
- $this->logger = $logger;
}
/**
@@ -146,7 +77,7 @@ class Manager {
* @throws \Doctrine\DBAL\Exception
*/
public function addShare($remote, $token, $password, $name, $owner, $shareType, $accepted = false, $user = null, $remoteId = '', $parent = -1) {
- $user = $user ? $user : $this->uid;
+ $user = $user ?? $this->uid;
$accepted = $accepted ? IShare::STATUS_ACCEPTED : IShare::STATUS_PENDING;
$name = Filesystem::normalizePath('/' . $name);
@@ -195,7 +126,7 @@ class Manager {
'mountpoint' => $mountPoint,
'owner' => $owner
];
- return $this->mountShare($options);
+ return $this->mountShare($options, $user);
}
/**
@@ -226,18 +157,29 @@ class Manager {
$query->execute([$remote, $token, $password, $name, $owner, $user, $mountPoint, $hash, $accepted, $remoteId, $parent, $shareType]);
}
+ private function fetchShare(int $id): array|false {
+ $getShare = $this->connection->prepare('
+ SELECT `id`, `remote`, `remote_id`, `share_token`, `name`, `owner`, `user`, `mountpoint`, `accepted`, `parent`, `share_type`, `password`, `mountpoint_hash`
+ FROM `*PREFIX*share_external`
+ WHERE `id` = ?');
+ $result = $getShare->execute([$id]);
+ $share = $result->fetch();
+ $result->closeCursor();
+ return $share;
+ }
+
/**
- * get share
+ * get share by token
*
- * @param int $id share id
+ * @param string $token
* @return mixed share of false
*/
- private function fetchShare($id) {
+ private function fetchShareByToken($token) {
$getShare = $this->connection->prepare('
SELECT `id`, `remote`, `remote_id`, `share_token`, `name`, `owner`, `user`, `mountpoint`, `accepted`, `parent`, `share_type`, `password`, `mountpoint_hash`
FROM `*PREFIX*share_external`
- WHERE `id` = ?');
- $result = $getShare->execute([$id]);
+ WHERE `share_token` = ?');
+ $result = $getShare->execute([$token]);
$share = $result->fetch();
$result->closeCursor();
return $share;
@@ -257,20 +199,54 @@ class Manager {
return null;
}
- /**
- * get share
- *
- * @param int $id share id
- * @return mixed share of false
- */
- public function getShare($id) {
+ public function getShare(int $id, ?string $user = null): array|false {
+ $user = $user ?? $this->uid;
$share = $this->fetchShare($id);
- $validShare = is_array($share) && isset($share['share_type']) && isset($share['user']);
+ if ($share === false) {
+ return false;
+ }
// check if the user is allowed to access it
- if ($validShare && (int)$share['share_type'] === IShare::TYPE_USER && $share['user'] === $this->uid) {
+ if ($this->canAccessShare($share, $user)) {
return $share;
- } elseif ($validShare && (int)$share['share_type'] === IShare::TYPE_GROUP) {
+ }
+
+ return false;
+ }
+
+ /**
+ * Get share by token
+ *
+ * @param string $token
+ * @return array|false
+ */
+ public function getShareByToken(string $token): array|false {
+ $share = $this->fetchShareByToken($token);
+
+ // We do not check if the user is allowed to access it here,
+ // as this is not used from a user context.
+ if ($share === false) {
+ return false;
+ }
+
+ return $share;
+ }
+
+ private function canAccessShare(array $share, string $user): bool {
+ $validShare = isset($share['share_type']) && isset($share['user']);
+
+ if (!$validShare) {
+ return false;
+ }
+
+ // If the share is a user share, check if the user is the recipient
+ if ((int)$share['share_type'] === IShare::TYPE_USER
+ && $share['user'] === $user) {
+ return true;
+ }
+
+ // If the share is a group share, check if the user is in the group
+ if ((int)$share['share_type'] === IShare::TYPE_GROUP) {
$parentId = (int)$share['parent'];
if ($parentId !== -1) {
// we just retrieved a sub-share, switch to the parent entry for verification
@@ -278,9 +254,10 @@ class Manager {
} else {
$groupShare = $share;
}
- $user = $this->userManager->get($this->uid);
+
+ $user = $this->userManager->get($user);
if ($this->groupManager->get($groupShare['user'])->inGroup($user)) {
- return $share;
+ return true;
}
}
@@ -307,13 +284,22 @@ class Manager {
* @param int $id
* @return bool True if the share could be accepted, false otherwise
*/
- public function acceptShare($id) {
- $share = $this->getShare($id);
+ public function acceptShare(int $id, ?string $user = null) {
+ // If we're auto-accepting a share, we need to know the user id
+ // as there is no session available while processing the share
+ // from the remote server request.
+ $user = $user ?? $this->uid;
+ if ($user === null) {
+ $this->logger->error('No user specified for accepting share');
+ return false;
+ }
+
+ $share = $this->getShare($id, $user);
$result = false;
if ($share) {
- \OC_Util::setupFS($this->uid);
- $shareFolder = Helper::getShareFolder(null, $this->uid);
+ \OC_Util::setupFS($user);
+ $shareFolder = Helper::getShareFolder(null, $user);
$mountPoint = Files::buildNotExistingFileName($shareFolder, $share['name']);
$mountPoint = Filesystem::normalizePath($mountPoint);
$hash = md5($mountPoint);
@@ -326,14 +312,14 @@ class Manager {
`mountpoint` = ?,
`mountpoint_hash` = ?
WHERE `id` = ? AND `user` = ?');
- $userShareAccepted = $acceptShare->execute([1, $mountPoint, $hash, $id, $this->uid]);
+ $userShareAccepted = $acceptShare->execute([1, $mountPoint, $hash, $id, $user]);
} else {
$parentId = (int)$share['parent'];
if ($parentId !== -1) {
// this is the sub-share
$subshare = $share;
} else {
- $subshare = $this->fetchUserShare($id, $this->uid);
+ $subshare = $this->fetchUserShare($id, $user);
}
if ($subshare !== null) {
@@ -344,7 +330,7 @@ class Manager {
`mountpoint` = ?,
`mountpoint_hash` = ?
WHERE `id` = ? AND `user` = ?');
- $acceptShare->execute([1, $mountPoint, $hash, $subshare['id'], $this->uid]);
+ $acceptShare->execute([1, $mountPoint, $hash, $subshare['id'], $user]);
$result = true;
} catch (Exception $e) {
$this->logger->emergency('Could not update share', ['exception' => $e]);
@@ -358,7 +344,7 @@ class Manager {
$share['password'],
$share['name'],
$share['owner'],
- $this->uid,
+ $user,
$mountPoint, $hash, 1,
$share['remote_id'],
$id,
@@ -370,17 +356,18 @@ class Manager {
}
}
}
+
if ($userShareAccepted !== false) {
$this->sendFeedbackToRemote($share['remote'], $share['share_token'], $share['remote_id'], 'accept');
$event = new FederatedShareAddedEvent($share['remote']);
$this->eventDispatcher->dispatchTyped($event);
- $this->eventDispatcher->dispatchTyped(new Files\Events\InvalidateMountCacheEvent($this->userManager->get($this->uid)));
+ $this->eventDispatcher->dispatchTyped(new InvalidateMountCacheEvent($this->userManager->get($user)));
$result = true;
}
}
// Make sure the user has no notification for something that does not exist anymore.
- $this->processNotification($id);
+ $this->processNotification($id, $user);
return $result;
}
@@ -391,17 +378,23 @@ class Manager {
* @param int $id
* @return bool True if the share could be declined, false otherwise
*/
- public function declineShare($id) {
- $share = $this->getShare($id);
+ public function declineShare(int $id, ?string $user = null) {
+ $user = $user ?? $this->uid;
+ if ($user === null) {
+ $this->logger->error('No user specified for declining share');
+ return false;
+ }
+
+ $share = $this->getShare($id, $user);
$result = false;
if ($share && (int)$share['share_type'] === IShare::TYPE_USER) {
$removeShare = $this->connection->prepare('
DELETE FROM `*PREFIX*share_external` WHERE `id` = ? AND `user` = ?');
- $removeShare->execute([$id, $this->uid]);
+ $removeShare->execute([$id, $user]);
$this->sendFeedbackToRemote($share['remote'], $share['share_token'], $share['remote_id'], 'decline');
- $this->processNotification($id);
+ $this->processNotification($id, $user);
$result = true;
} elseif ($share && (int)$share['share_type'] === IShare::TYPE_GROUP) {
$parentId = (int)$share['parent'];
@@ -409,7 +402,7 @@ class Manager {
// this is the sub-share
$subshare = $share;
} else {
- $subshare = $this->fetchUserShare($id, $this->uid);
+ $subshare = $this->fetchUserShare($id, $user);
}
if ($subshare !== null) {
@@ -428,7 +421,7 @@ class Manager {
$share['password'],
$share['name'],
$share['owner'],
- $this->uid,
+ $user,
$share['mountpoint'],
$share['mountpoint_hash'],
0,
@@ -441,16 +434,27 @@ class Manager {
$result = false;
}
}
- $this->processNotification($id);
+ $this->processNotification($id, $user);
}
return $result;
}
- public function processNotification(int $remoteShare): void {
+ public function processNotification(int $remoteShare, ?string $user = null): void {
+ $user = $user ?? $this->uid;
+ if ($user === null) {
+ $this->logger->error('No user specified for processing notification');
+ return;
+ }
+
+ $share = $this->fetchShare($remoteShare);
+ if ($share === false) {
+ return;
+ }
+
$filter = $this->notificationManager->createNotification();
$filter->setApp('files_sharing')
- ->setUser($this->uid)
+ ->setUser($user)
->setObject('remote_share', (string)$remoteShare);
$this->notificationManager->markProcessed($filter);
}
@@ -550,9 +554,10 @@ class Manager {
return rtrim(substr($path, strlen($prefix)), '/');
}
- public function getMount($data) {
+ public function getMount($data, ?string $user = null) {
+ $user = $user ?? $this->uid;
$data['manager'] = $this;
- $mountPoint = '/' . $this->uid . '/files' . $data['mountpoint'];
+ $mountPoint = '/' . $user . '/files' . $data['mountpoint'];
$data['mountpoint'] = $mountPoint;
$data['certificateManager'] = \OC::$server->getCertificateManager();
return new Mount(self::STORAGE, $mountPoint, $data, $this, $this->storageLoader);
@@ -562,8 +567,8 @@ class Manager {
* @param array $data
* @return Mount
*/
- protected function mountShare($data) {
- $mount = $this->getMount($data);
+ protected function mountShare($data, ?string $user = null) {
+ $mount = $this->getMount($data, $user);
$this->mountManager->addMount($mount);
return $mount;
}
@@ -594,7 +599,7 @@ class Manager {
');
$result = (bool)$query->execute([$target, $targetHash, $sourceHash, $this->uid]);
- $this->eventDispatcher->dispatchTyped(new Files\Events\InvalidateMountCacheEvent($this->userManager->get($this->uid)));
+ $this->eventDispatcher->dispatchTyped(new InvalidateMountCacheEvent($this->userManager->get($this->uid)));
return $result;
}
@@ -733,12 +738,12 @@ class Manager {
$qb = $this->connection->getQueryBuilder();
// delete group share entry and matching sub-entries
$qb->delete('share_external')
- ->where(
- $qb->expr()->orX(
- $qb->expr()->eq('id', $qb->createParameter('share_id')),
- $qb->expr()->eq('parent', $qb->createParameter('share_parent_id'))
- )
- );
+ ->where(
+ $qb->expr()->orX(
+ $qb->expr()->eq('id', $qb->createParameter('share_id')),
+ $qb->expr()->eq('parent', $qb->createParameter('share_parent_id'))
+ )
+ );
foreach ($shares as $share) {
$qb->setParameter('share_id', $share['id']);
@@ -756,7 +761,7 @@ class Manager {
/**
* return a list of shares which are not yet accepted by the user
*
- * @return array list of open server-to-server shares
+ * @return list<Files_SharingRemoteShare> list of open server-to-server shares
*/
public function getOpenShares() {
return $this->getShares(false);
@@ -765,7 +770,7 @@ class Manager {
/**
* return a list of shares which are accepted by the user
*
- * @return array list of accepted server-to-server shares
+ * @return list<Files_SharingRemoteShare> list of accepted server-to-server shares
*/
public function getAcceptedShares() {
return $this->getShares(true);
@@ -777,9 +782,11 @@ class Manager {
* @param bool|null $accepted True for accepted only,
* false for not accepted,
* null for all shares of the user
- * @return array list of open server-to-server shares
+ * @return list<Files_SharingRemoteShare> list of open server-to-server shares
*/
private function getShares($accepted) {
+ // Not allowing providing a user here,
+ // as we only want to retrieve shares for the current user.
$user = $this->userManager->get($this->uid);
$groups = $this->groupManager->getUserGroups($user);
$userGroups = [];
diff --git a/apps/files_sharing/lib/External/Mount.php b/apps/files_sharing/lib/External/Mount.php
index 283027ac43a..f50c379f85f 100644
--- a/apps/files_sharing/lib/External/Mount.php
+++ b/apps/files_sharing/lib/External/Mount.php
@@ -1,50 +1,34 @@
<?php
+
/**
- * @copyright Copyright (c) 2016, ownCloud, Inc.
- *
- * @author Bjoern Schiessle <bjoern@schiessle.org>
- * @author Björn Schießle <bjoern@schiessle.org>
- * @author Robin Appelman <robin@icewind.nl>
- * @author szaimen <szaimen@e.mail.de>
- *
- * @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: 2018-2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
+ * SPDX-License-Identifier: AGPL-3.0-only
*/
namespace OCA\Files_Sharing\External;
use OC\Files\Mount\MountPoint;
use OC\Files\Mount\MoveableMount;
+use OC\Files\Storage\Storage;
use OCA\Files_Sharing\ISharedMountPoint;
class Mount extends MountPoint implements MoveableMount, ISharedMountPoint {
/**
- * @var \OCA\Files_Sharing\External\Manager
- */
- protected $manager;
-
- /**
- * @param string|\OC\Files\Storage\Storage $storage
+ * @param string|Storage $storage
* @param string $mountpoint
* @param array $options
* @param \OCA\Files_Sharing\External\Manager $manager
* @param \OC\Files\Storage\StorageFactory $loader
*/
- public function __construct($storage, $mountpoint, $options, $manager, $loader = null) {
+ public function __construct(
+ $storage,
+ $mountpoint,
+ $options,
+ protected $manager,
+ $loader = null,
+ ) {
parent::__construct($storage, $mountpoint, $options, $loader, null, null, MountProvider::class);
- $this->manager = $manager;
}
/**
diff --git a/apps/files_sharing/lib/External/MountProvider.php b/apps/files_sharing/lib/External/MountProvider.php
index 169bf6ed3d7..a5781d5d35a 100644
--- a/apps/files_sharing/lib/External/MountProvider.php
+++ b/apps/files_sharing/lib/External/MountProvider.php
@@ -1,26 +1,9 @@
<?php
+
/**
- * @copyright Copyright (c) 2016, ownCloud, Inc.
- *
- * @author Christoph Wurst <christoph@winzerhof-wurst.at>
- * @author Lukas Reschke <lukas@statuscode.ch>
- * @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: 2016-2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
+ * SPDX-License-Identifier: AGPL-3.0-only
*/
namespace OCA\Files_Sharing\External;
@@ -28,36 +11,30 @@ use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\Federation\ICloudIdManager;
use OCP\Files\Config\IMountProvider;
use OCP\Files\Storage\IStorageFactory;
+use OCP\Http\Client\IClientService;
use OCP\IDBConnection;
use OCP\IUser;
+use OCP\Server;
class MountProvider implements IMountProvider {
public const STORAGE = '\OCA\Files_Sharing\External\Storage';
/**
- * @var \OCP\IDBConnection
- */
- private $connection;
-
- /**
* @var callable
*/
private $managerProvider;
/**
- * @var ICloudIdManager
- */
- private $cloudIdManager;
-
- /**
- * @param \OCP\IDBConnection $connection
+ * @param IDBConnection $connection
* @param callable $managerProvider due to setup order we need a callable that return the manager instead of the manager itself
* @param ICloudIdManager $cloudIdManager
*/
- public function __construct(IDBConnection $connection, callable $managerProvider, ICloudIdManager $cloudIdManager) {
- $this->connection = $connection;
+ public function __construct(
+ private IDBConnection $connection,
+ callable $managerProvider,
+ private ICloudIdManager $cloudIdManager,
+ ) {
$this->managerProvider = $managerProvider;
- $this->cloudIdManager = $cloudIdManager;
}
public function getMount(IUser $user, $data, IStorageFactory $storageFactory) {
@@ -68,7 +45,7 @@ class MountProvider implements IMountProvider {
$data['mountpoint'] = $mountPoint;
$data['cloudId'] = $this->cloudIdManager->getCloudId($data['owner'], $data['remote']);
$data['certificateManager'] = \OC::$server->getCertificateManager();
- $data['HttpClientService'] = \OC::$server->getHTTPClientService();
+ $data['HttpClientService'] = Server::get(IClientService::class);
return new Mount(self::STORAGE, $mountPoint, $data, $manager, $storageFactory);
}
diff --git a/apps/files_sharing/lib/External/Scanner.php b/apps/files_sharing/lib/External/Scanner.php
index e3336c69a05..0d57248595b 100644
--- a/apps/files_sharing/lib/External/Scanner.php
+++ b/apps/files_sharing/lib/External/Scanner.php
@@ -1,37 +1,20 @@
<?php
+
/**
- * @copyright Copyright (c) 2016, ownCloud, Inc.
- *
- * @author Christoph Wurst <christoph@winzerhof-wurst.at>
- * @author Lukas Reschke <lukas@statuscode.ch>
- * @author Olivier Paroz <github@oparoz.com>
- * @author Robin Appelman <robin@icewind.nl>
- * @author Vincent Petry <vincent@nextcloud.com>
- *
- * @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_Sharing\External;
+use OC\Files\Cache\CacheEntry;
use OC\ForbiddenException;
use OCP\Files\NotFoundException;
use OCP\Files\StorageInvalidException;
use OCP\Files\StorageNotAvailableException;
class Scanner extends \OC\Files\Cache\Scanner {
- /** @var \OCA\Files_Sharing\External\Storage */
+ /** @var Storage */
protected $storage;
public function scan($path, $recursive = self::SCAN_RECURSIVE, $reuse = -1, $lock = true) {
@@ -48,9 +31,10 @@ class Scanner extends \OC\Files\Cache\Scanner {
* @param string $file file to scan
* @param int $reuseExisting
* @param int $parentId
- * @param array | null $cacheData existing data in the cache for the file to be scanned
+ * @param CacheEntry|array|null|false $cacheData existing data in the cache for the file to be scanned
* @param bool $lock set to false to disable getting an additional read lock during scanning
- * @return array | null an array of metadata of the scanned file
+ * @param array|null $data the metadata for the file, as returned by the storage
+ * @return array|null an array of metadata of the scanned file
*/
public function scanFile($file, $reuseExisting = 0, $parentId = -1, $cacheData = null, $lock = true, $data = null) {
try {
diff --git a/apps/files_sharing/lib/External/Storage.php b/apps/files_sharing/lib/External/Storage.php
index 7b64690d53e..a9781b91a6c 100644
--- a/apps/files_sharing/lib/External/Storage.php
+++ b/apps/files_sharing/lib/External/Storage.php
@@ -1,37 +1,10 @@
<?php
declare(strict_types=1);
-
/**
- * @copyright Copyright (c) 2016, ownCloud, Inc.
- *
- * @author Bjoern Schiessle <bjoern@schiessle.org>
- * @author Björn Schießle <bjoern@schiessle.org>
- * @author Christoph Wurst <christoph@winzerhof-wurst.at>
- * @author Daniel Kesselberg <mail@danielkesselberg.de>
- * @author Joas Schilling <coding@schilljs.com>
- * @author Lukas Reschke <lukas@statuscode.ch>
- * @author Maxence Lange <maxence@artificial-owl.com>
- * @author Morris Jobke <hey@morrisjobke.de>
- * @author Robin Appelman <robin@icewind.nl>
- * @author Roeland Jago Douma <roeland@famdouma.nl>
- * @author Thomas Müller <thomas.mueller@tmit.eu>
- * @author Vincent Petry <vincent@nextcloud.com>
- *
- * @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_Sharing\External;
@@ -40,14 +13,19 @@ use GuzzleHttp\Exception\ConnectException;
use GuzzleHttp\Exception\RequestException;
use OC\Files\Storage\DAV;
use OC\ForbiddenException;
+use OC\Share\Share;
use OCA\Files_Sharing\External\Manager as ExternalShareManager;
use OCA\Files_Sharing\ISharedStorage;
use OCP\AppFramework\Http;
use OCP\Constants;
use OCP\Federation\ICloudId;
+use OCP\Files\Cache\ICache;
+use OCP\Files\Cache\IScanner;
+use OCP\Files\Cache\IWatcher;
use OCP\Files\NotFoundException;
use OCP\Files\Storage\IDisableEncryptionStorage;
use OCP\Files\Storage\IReliableEtagStorage;
+use OCP\Files\Storage\IStorage;
use OCP\Files\StorageInvalidException;
use OCP\Files\StorageNotAvailableException;
use OCP\Http\Client\IClientService;
@@ -58,6 +36,7 @@ use OCP\OCM\Exceptions\OCMArgumentException;
use OCP\OCM\Exceptions\OCMProviderException;
use OCP\OCM\IOCMDiscoveryService;
use OCP\Server;
+use OCP\Util;
use Psr\Log\LoggerInterface;
class Storage extends DAV implements ISharedStorage, IDisableEncryptionStorage, IReliableEtagStorage {
@@ -74,7 +53,7 @@ class Storage extends DAV implements ISharedStorage, IDisableEncryptionStorage,
* @param array{HttpClientService: IClientService, manager: ExternalShareManager, cloudId: ICloudId, mountpoint: string, token: string, password: ?string}|array $options
*/
public function __construct($options) {
- $this->memcacheFactory = \OC::$server->getMemCacheFactory();
+ $this->memcacheFactory = Server::get(ICacheFactory::class);
$this->httpClient = $options['HttpClientService'];
$this->manager = $options['manager'];
$this->cloudId = $options['cloudId'];
@@ -95,7 +74,7 @@ class Storage extends DAV implements ISharedStorage, IDisableEncryptionStorage,
$host = parse_url($remote, PHP_URL_HOST);
$port = parse_url($remote, PHP_URL_PORT);
- $host .= (null === $port) ? '' : ':' . $port; // we add port if available
+ $host .= ($port === null) ? '' : ':' . $port; // we add port if available
// in case remote NC is on a sub folder and using deprecated ocm provider
$tmpPath = rtrim(parse_url($this->cloudId->getRemote(), PHP_URL_PATH) ?? '', '/');
@@ -109,6 +88,7 @@ class Storage extends DAV implements ISharedStorage, IDisableEncryptionStorage,
parent::__construct(
[
'secure' => ((parse_url($remote, PHP_URL_SCHEME) ?? 'https') === 'https'),
+ 'verify' => !$this->config->getSystemValueBool('sharing.federation.allowSelfSignedCertificates', false),
'host' => $host,
'root' => $webDavEndpoint,
'user' => $options['token'],
@@ -118,7 +98,7 @@ class Storage extends DAV implements ISharedStorage, IDisableEncryptionStorage,
);
}
- public function getWatcher($path = '', $storage = null) {
+ public function getWatcher(string $path = '', ?IStorage $storage = null): IWatcher {
if (!$storage) {
$storage = $this;
}
@@ -149,46 +129,29 @@ class Storage extends DAV implements ISharedStorage, IDisableEncryptionStorage,
return $this->password;
}
- /**
- * Get id of the mount point.
- * @return string
- */
- public function getId() {
+ public function getId(): string {
return 'shared::' . md5($this->token . '@' . $this->getRemote());
}
- public function getCache($path = '', $storage = null) {
+ public function getCache(string $path = '', ?IStorage $storage = null): ICache {
if (is_null($this->cache)) {
$this->cache = new Cache($this, $this->cloudId);
}
return $this->cache;
}
- /**
- * @param string $path
- * @param \OC\Files\Storage\Storage $storage
- * @return \OCA\Files_Sharing\External\Scanner
- */
- public function getScanner($path = '', $storage = null) {
+ public function getScanner(string $path = '', ?IStorage $storage = null): IScanner {
if (!$storage) {
$storage = $this;
}
if (!isset($this->scanner)) {
$this->scanner = new Scanner($storage);
}
+ /** @var Scanner */
return $this->scanner;
}
- /**
- * Check if a file or folder has been updated since $time
- *
- * @param string $path
- * @param int $time
- * @throws \OCP\Files\StorageNotAvailableException
- * @throws \OCP\Files\StorageInvalidException
- * @return bool
- */
- public function hasUpdated($path, $time) {
+ public function hasUpdated(string $path, int $time): bool {
// since for owncloud webdav servers we can rely on etag propagation we only need to check the root of the storage
// because of that we only do one check for the entire storage per request
if ($this->updateChecked) {
@@ -208,7 +171,7 @@ class Storage extends DAV implements ISharedStorage, IDisableEncryptionStorage,
}
}
- public function test() {
+ public function test(): bool {
try {
return parent::test();
} catch (StorageInvalidException $e) {
@@ -226,8 +189,8 @@ class Storage extends DAV implements ISharedStorage, IDisableEncryptionStorage,
* Check whether this storage is permanently or temporarily
* unavailable
*
- * @throws \OCP\Files\StorageNotAvailableException
- * @throws \OCP\Files\StorageInvalidException
+ * @throws StorageNotAvailableException
+ * @throws StorageInvalidException
*/
public function checkStorageAvailability() {
// see if we can find out why the share is unavailable
@@ -241,24 +204,24 @@ class Storage extends DAV implements ISharedStorage, IDisableEncryptionStorage,
// we remove the invalid storage
$this->manager->removeShare($this->mountPoint);
$this->manager->getMountManager()->removeMount($this->mountPoint);
- throw new StorageInvalidException("Remote share not found", 0, $e);
+ throw new StorageInvalidException('Remote share not found', 0, $e);
} else {
// Nextcloud instance is gone, likely to be a temporary server configuration error
- throw new StorageNotAvailableException("No nextcloud instance found at remote", 0, $e);
+ throw new StorageNotAvailableException('No nextcloud instance found at remote', 0, $e);
}
} catch (ForbiddenException $e) {
// auth error, remove share for now (provide a dialog in the future)
$this->manager->removeShare($this->mountPoint);
$this->manager->getMountManager()->removeMount($this->mountPoint);
- throw new StorageInvalidException("Auth error when getting remote share");
+ throw new StorageInvalidException('Auth error when getting remote share');
} catch (\GuzzleHttp\Exception\ConnectException $e) {
- throw new StorageNotAvailableException("Failed to connect to remote instance", 0, $e);
+ throw new StorageNotAvailableException('Failed to connect to remote instance', 0, $e);
} catch (\GuzzleHttp\Exception\RequestException $e) {
- throw new StorageNotAvailableException("Error while sending request to remote instance", 0, $e);
+ throw new StorageNotAvailableException('Error while sending request to remote instance', 0, $e);
}
}
- public function file_exists($path) {
+ public function file_exists(string $path): bool {
if ($path === '') {
return true;
} else {
@@ -290,19 +253,12 @@ class Storage extends DAV implements ISharedStorage, IDisableEncryptionStorage,
$client = $this->httpClient->newClient();
try {
- $result = $client->get($url, [
- 'timeout' => 10,
- 'connect_timeout' => 10,
- 'verify' => !$this->config->getSystemValueBool('sharing.federation.allowSelfSignedCertificates', false),
- ])->getBody();
+ $result = $client->get($url, $this->getDefaultRequestOptions())->getBody();
$data = json_decode($result);
$returnValue = (is_object($data) && !empty($data->version));
- } catch (ConnectException $e) {
- $returnValue = false;
- } catch (ClientException $e) {
- $returnValue = false;
- } catch (RequestException $e) {
+ } catch (ConnectException|ClientException|RequestException $e) {
$returnValue = false;
+ $this->logger->warning('Failed to test remote URL', ['exception' => $e]);
}
$cache->set($url, $returnValue, 60 * 60 * 24);
@@ -348,14 +304,13 @@ class Storage extends DAV implements ISharedStorage, IDisableEncryptionStorage,
$url = rtrim($remote, '/') . '/index.php/apps/files_sharing/shareinfo?t=' . $token;
// TODO: DI
- $client = \OC::$server->getHTTPClientService()->newClient();
+ $client = Server::get(IClientService::class)->newClient();
try {
- $response = $client->post($url, [
+ $response = $client->post($url, array_merge($this->getDefaultRequestOptions(), [
'body' => ['password' => $password, 'depth' => $depth],
- 'timeout' => 10,
- 'connect_timeout' => 10,
- ]);
+ ]));
} catch (\GuzzleHttp\Exception\RequestException $e) {
+ $this->logger->warning('Failed to fetch share info', ['exception' => $e]);
if ($e->getCode() === Http::STATUS_UNAUTHORIZED || $e->getCode() === Http::STATUS_FORBIDDEN) {
throw new ForbiddenException();
}
@@ -371,27 +326,34 @@ class Storage extends DAV implements ISharedStorage, IDisableEncryptionStorage,
return json_decode($response->getBody(), true);
}
- public function getOwner($path) {
+ public function getOwner(string $path): string|false {
return $this->cloudId->getDisplayId();
}
- public function isSharable($path): bool {
- if (\OCP\Util::isSharingDisabledForUser() || !\OC\Share\Share::isResharingAllowed()) {
+ public function isSharable(string $path): bool {
+ if (Util::isSharingDisabledForUser() || !Share::isResharingAllowed()) {
return false;
}
return (bool)($this->getPermissions($path) & Constants::PERMISSION_SHARE);
}
- public function getPermissions($path): int {
+ public function getPermissions(string $path): int {
$response = $this->propfind($path);
+ if ($response === false) {
+ return 0;
+ }
+
+ $ocsPermissions = $response['{http://open-collaboration-services.org/ns}share-permissions'] ?? null;
+ $ocmPermissions = $response['{http://open-cloud-mesh.org/ns}share-permissions'] ?? null;
+ $ocPermissions = $response['{http://owncloud.org/ns}permissions'] ?? null;
// old federated sharing permissions
- if (isset($response['{http://open-collaboration-services.org/ns}share-permissions'])) {
- $permissions = (int)$response['{http://open-collaboration-services.org/ns}share-permissions'];
- } elseif (isset($response['{http://open-cloud-mesh.org/ns}share-permissions'])) {
+ if ($ocsPermissions !== null) {
+ $permissions = (int)$ocsPermissions;
+ } elseif ($ocmPermissions !== null) {
// permissions provided by the OCM API
- $permissions = $this->ocmPermissions2ncPermissions($response['{http://open-collaboration-services.org/ns}share-permissions'], $path);
- } elseif (isset($response['{http://owncloud.org/ns}permissions'])) {
- return $this->parsePermissions($response['{http://owncloud.org/ns}permissions']);
+ $permissions = $this->ocmPermissions2ncPermissions($ocmPermissions, $path);
+ } elseif ($ocPermissions !== null) {
+ return $this->parsePermissions($ocPermissions);
} else {
// use default permission if remote server doesn't provide the share permissions
$permissions = $this->getDefaultPermissions($path);
@@ -400,7 +362,7 @@ class Storage extends DAV implements ISharedStorage, IDisableEncryptionStorage,
return $permissions;
}
- public function needsPartFile() {
+ public function needsPartFile(): bool {
return false;
}
@@ -450,7 +412,18 @@ class Storage extends DAV implements ISharedStorage, IDisableEncryptionStorage,
return $permissions;
}
- public function free_space($path) {
- return parent::free_space("");
+ public function free_space(string $path): int|float|false {
+ return parent::free_space('');
+ }
+
+ private function getDefaultRequestOptions(): array {
+ $options = [
+ 'timeout' => 10,
+ 'connect_timeout' => 10,
+ ];
+ if ($this->config->getSystemValueBool('sharing.federation.allowSelfSignedCertificates')) {
+ $options['verify'] = false;
+ }
+ return $options;
}
}
diff --git a/apps/files_sharing/lib/External/Watcher.php b/apps/files_sharing/lib/External/Watcher.php
index 6afd1c6d538..f3616feabba 100644
--- a/apps/files_sharing/lib/External/Watcher.php
+++ b/apps/files_sharing/lib/External/Watcher.php
@@ -1,23 +1,9 @@
<?php
+
/**
- * @copyright Copyright (c) 2016, ownCloud, Inc.
- *
- * @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_Sharing\External;