SharesPlugin.php 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. <?php
  2. /**
  3. * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
  4. * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
  5. * SPDX-License-Identifier: AGPL-3.0-only
  6. */
  7. namespace OCA\DAV\Connector\Sabre;
  8. use OC\Share20\Exception\BackendError;
  9. use OCA\DAV\Connector\Sabre\Node as DavNode;
  10. use OCP\Files\Folder;
  11. use OCP\Files\Node;
  12. use OCP\Files\NotFoundException;
  13. use OCP\IUserSession;
  14. use OCP\Share\IManager;
  15. use OCP\Share\IShare;
  16. use Sabre\DAV\PropFind;
  17. use Sabre\DAV\Server;
  18. use Sabre\DAV\Tree;
  19. /**
  20. * Sabre Plugin to provide share-related properties
  21. */
  22. class SharesPlugin extends \Sabre\DAV\ServerPlugin {
  23. public const NS_OWNCLOUD = 'http://owncloud.org/ns';
  24. public const NS_NEXTCLOUD = 'http://nextcloud.org/ns';
  25. public const SHARETYPES_PROPERTYNAME = '{http://owncloud.org/ns}share-types';
  26. public const SHAREES_PROPERTYNAME = '{http://nextcloud.org/ns}sharees';
  27. /**
  28. * Reference to main server object
  29. *
  30. * @var \Sabre\DAV\Server
  31. */
  32. private $server;
  33. private string $userId;
  34. /** @var IShare[][] */
  35. private array $cachedShares = [];
  36. /** @var string[] */
  37. private array $cachedFolders = [];
  38. public function __construct(
  39. private Tree $tree,
  40. private IUserSession $userSession,
  41. private Folder $userFolder,
  42. private IManager $shareManager,
  43. ) {
  44. $this->userId = $userSession->getUser()->getUID();
  45. }
  46. /**
  47. * This initializes the plugin.
  48. *
  49. * This function is called by \Sabre\DAV\Server, after
  50. * addPlugin is called.
  51. *
  52. * This method should set up the required event subscriptions.
  53. *
  54. * @return void
  55. */
  56. public function initialize(Server $server) {
  57. $server->xml->namespaceMap[self::NS_OWNCLOUD] = 'oc';
  58. $server->xml->elementMap[self::SHARETYPES_PROPERTYNAME] = ShareTypeList::class;
  59. $server->protectedProperties[] = self::SHARETYPES_PROPERTYNAME;
  60. $server->protectedProperties[] = self::SHAREES_PROPERTYNAME;
  61. $this->server = $server;
  62. $this->server->on('propFind', [$this, 'handleGetProperties']);
  63. }
  64. /**
  65. * @param Node $node
  66. * @return IShare[]
  67. */
  68. private function getShare(Node $node): array {
  69. $result = [];
  70. $requestedShareTypes = [
  71. IShare::TYPE_USER,
  72. IShare::TYPE_GROUP,
  73. IShare::TYPE_LINK,
  74. IShare::TYPE_REMOTE,
  75. IShare::TYPE_EMAIL,
  76. IShare::TYPE_ROOM,
  77. IShare::TYPE_CIRCLE,
  78. IShare::TYPE_DECK,
  79. IShare::TYPE_SCIENCEMESH,
  80. ];
  81. foreach ($requestedShareTypes as $requestedShareType) {
  82. $result = array_merge($result, $this->shareManager->getSharesBy(
  83. $this->userId,
  84. $requestedShareType,
  85. $node,
  86. false,
  87. -1
  88. ));
  89. // Also check for shares where the user is the recipient
  90. try {
  91. $result = array_merge($result, $this->shareManager->getSharedWith(
  92. $this->userId,
  93. $requestedShareType,
  94. $node,
  95. -1
  96. ));
  97. } catch (BackendError $e) {
  98. // ignore
  99. }
  100. }
  101. return $result;
  102. }
  103. /**
  104. * @param Folder $node
  105. * @return IShare[][]
  106. */
  107. private function getSharesFolder(Folder $node): array {
  108. return $this->shareManager->getSharesInFolder(
  109. $this->userId,
  110. $node,
  111. true
  112. );
  113. }
  114. /**
  115. * @param DavNode $sabreNode
  116. * @return IShare[]
  117. */
  118. private function getShares(DavNode $sabreNode): array {
  119. if (isset($this->cachedShares[$sabreNode->getId()])) {
  120. return $this->cachedShares[$sabreNode->getId()];
  121. }
  122. [$parentPath,] = \Sabre\Uri\split($sabreNode->getPath());
  123. if ($parentPath === '') {
  124. $parentPath = '/';
  125. }
  126. // if we already cached the folder containing this file
  127. // then we already know there are no shares here.
  128. if (array_search($parentPath, $this->cachedFolders) === false) {
  129. try {
  130. $node = $sabreNode->getNode();
  131. } catch (NotFoundException $e) {
  132. return [];
  133. }
  134. $shares = $this->getShare($node);
  135. $this->cachedShares[$sabreNode->getId()] = $shares;
  136. return $shares;
  137. }
  138. return [];
  139. }
  140. /**
  141. * Adds shares to propfind response
  142. *
  143. * @param PropFind $propFind propfind object
  144. * @param \Sabre\DAV\INode $sabreNode sabre node
  145. */
  146. public function handleGetProperties(
  147. PropFind $propFind,
  148. \Sabre\DAV\INode $sabreNode,
  149. ) {
  150. if (!($sabreNode instanceof DavNode)) {
  151. return;
  152. }
  153. // If the node is a directory and we are requesting share types or sharees
  154. // then we get all the shares in the folder and cache them.
  155. // This is more performant than iterating each files afterwards.
  156. if ($sabreNode instanceof Directory
  157. && $propFind->getDepth() !== 0
  158. && (
  159. !is_null($propFind->getStatus(self::SHARETYPES_PROPERTYNAME)) ||
  160. !is_null($propFind->getStatus(self::SHAREES_PROPERTYNAME))
  161. )
  162. ) {
  163. $folderNode = $sabreNode->getNode();
  164. $this->cachedFolders[] = $sabreNode->getPath();
  165. $childShares = $this->getSharesFolder($folderNode);
  166. foreach ($childShares as $id => $shares) {
  167. $this->cachedShares[$id] = $shares;
  168. }
  169. }
  170. $propFind->handle(self::SHARETYPES_PROPERTYNAME, function () use ($sabreNode): ShareTypeList {
  171. $shares = $this->getShares($sabreNode);
  172. $shareTypes = array_unique(array_map(function (IShare $share) {
  173. return $share->getShareType();
  174. }, $shares));
  175. return new ShareTypeList($shareTypes);
  176. });
  177. $propFind->handle(self::SHAREES_PROPERTYNAME, function () use ($sabreNode): ShareeList {
  178. $shares = $this->getShares($sabreNode);
  179. return new ShareeList($shares);
  180. });
  181. }
  182. }