* @author Joas Schilling * @author Julius Härtl * @author Morris Jobke * @author Robin Appelman * @author Roeland Jago Douma * @author Tobias Kaminsky * @author Vincent Petry * * @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 * */ namespace OCA\DAV\Connector\Sabre; use OCP\IUserSession; use OCP\Share\IShare; use Sabre\DAV\PropFind; /** * Sabre Plugin to provide share-related properties */ class SharesPlugin extends \Sabre\DAV\ServerPlugin { public const NS_OWNCLOUD = 'http://owncloud.org/ns'; public const NS_NEXTCLOUD = 'http://nextcloud.org/ns'; public const SHARETYPES_PROPERTYNAME = '{http://owncloud.org/ns}share-types'; public const SHAREES_PROPERTYNAME = '{http://nextcloud.org/ns}sharees'; /** * Reference to main server object * * @var \Sabre\DAV\Server */ private $server; /** * @var \OCP\Share\IManager */ private $shareManager; /** * @var \Sabre\DAV\Tree */ private $tree; /** * @var string */ private $userId; /** * @var \OCP\Files\Folder */ private $userFolder; /** @var IShare[] */ private $cachedShares = []; private $cachedFolders = []; /** * @param \Sabre\DAV\Tree $tree tree * @param IUserSession $userSession user session * @param \OCP\Files\Folder $userFolder user home folder * @param \OCP\Share\IManager $shareManager share manager */ public function __construct( \Sabre\DAV\Tree $tree, IUserSession $userSession, \OCP\Files\Folder $userFolder, \OCP\Share\IManager $shareManager ) { $this->tree = $tree; $this->shareManager = $shareManager; $this->userFolder = $userFolder; $this->userId = $userSession->getUser()->getUID(); } /** * This initializes the plugin. * * This function is called by \Sabre\DAV\Server, after * addPlugin is called. * * This method should set up the required event subscriptions. * * @param \Sabre\DAV\Server $server */ public function initialize(\Sabre\DAV\Server $server) { $server->xml->namespacesMap[self::NS_OWNCLOUD] = 'oc'; $server->xml->elementMap[self::SHARETYPES_PROPERTYNAME] = ShareTypeList::class; $server->protectedProperties[] = self::SHARETYPES_PROPERTYNAME; $server->protectedProperties[] = self::SHAREES_PROPERTYNAME; $this->server = $server; $this->server->on('propFind', [$this, 'handleGetProperties']); } private function getShare(\OCP\Files\Node $node): array { $result = []; $requestedShareTypes = [ IShare::TYPE_USER, IShare::TYPE_GROUP, IShare::TYPE_LINK, IShare::TYPE_REMOTE, IShare::TYPE_EMAIL, IShare::TYPE_ROOM, IShare::TYPE_CIRCLE, IShare::TYPE_DECK, ]; foreach ($requestedShareTypes as $requestedShareType) { $shares = $this->shareManager->getSharesBy( $this->userId, $requestedShareType, $node, false, -1 ); foreach ($shares as $share) { $result[] = $share; } } return $result; } private function getSharesFolder(\OCP\Files\Folder $node): array { return $this->shareManager->getSharesInFolder( $this->userId, $node, true ); } private function getShares(\Sabre\DAV\INode $sabreNode): array { if (isset($this->cachedShares[$sabreNode->getId()])) { $shares = $this->cachedShares[$sabreNode->getId()]; } else { list($parentPath,) = \Sabre\Uri\split($sabreNode->getPath()); if ($parentPath === '') { $parentPath = '/'; } // if we already cached the folder this file is in we know there are no shares for this file if (array_search($parentPath, $this->cachedFolders) === false) { $node = $this->userFolder->get($sabreNode->getPath()); $shares = $this->getShare($node); $this->cachedShares[$sabreNode->getId()] = $shares; } else { return []; } } return $shares; } /** * Adds shares to propfind response * * @param PropFind $propFind propfind object * @param \Sabre\DAV\INode $sabreNode sabre node */ public function handleGetProperties( PropFind $propFind, \Sabre\DAV\INode $sabreNode ) { if (!($sabreNode instanceof \OCA\DAV\Connector\Sabre\Node)) { return; } // need prefetch ? if ($sabreNode instanceof \OCA\DAV\Connector\Sabre\Directory && $propFind->getDepth() !== 0 && ( !is_null($propFind->getStatus(self::SHARETYPES_PROPERTYNAME)) || !is_null($propFind->getStatus(self::SHAREES_PROPERTYNAME)) ) ) { $folderNode = $this->userFolder->get($sabreNode->getPath()); $this->cachedFolders[] = $sabreNode->getPath(); $childShares = $this->getSharesFolder($folderNode); foreach ($childShares as $id => $shares) { $this->cachedShares[$id] = $shares; } } $propFind->handle(self::SHARETYPES_PROPERTYNAME, function () use ($sabreNode) { $shares = $this->getShares($sabreNode); $shareTypes = array_unique(array_map(function (IShare $share) { return $share->getShareType(); }, $shares)); return new ShareTypeList($shareTypes); }); $propFind->handle(self::SHAREES_PROPERTYNAME, function () use ($sabreNode) { $shares = $this->getShares($sabreNode); return new ShareeList($shares); }); } }