DefaultShareProvider.php 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185
  1. <?php
  2. /**
  3. * @copyright Copyright (c) 2016, ownCloud, Inc.
  4. *
  5. * @author Björn Schießle <bjoern@schiessle.org>
  6. * @author Joas Schilling <coding@schilljs.com>
  7. * @author Roeland Jago Douma <roeland@famdouma.nl>
  8. *
  9. * @license AGPL-3.0
  10. *
  11. * This code is free software: you can redistribute it and/or modify
  12. * it under the terms of the GNU Affero General Public License, version 3,
  13. * as published by the Free Software Foundation.
  14. *
  15. * This program is distributed in the hope that it will be useful,
  16. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  18. * GNU Affero General Public License for more details.
  19. *
  20. * You should have received a copy of the GNU Affero General Public License, version 3,
  21. * along with this program. If not, see <http://www.gnu.org/licenses/>
  22. *
  23. */
  24. namespace OC\Share20;
  25. use OC\Files\Cache\Cache;
  26. use OCP\Files\File;
  27. use OCP\Files\Folder;
  28. use OCP\Share\IShareProvider;
  29. use OC\Share20\Exception\InvalidShare;
  30. use OC\Share20\Exception\ProviderException;
  31. use OCP\Share\Exceptions\ShareNotFound;
  32. use OC\Share20\Exception\BackendError;
  33. use OCP\DB\QueryBuilder\IQueryBuilder;
  34. use OCP\IGroup;
  35. use OCP\IGroupManager;
  36. use OCP\IUserManager;
  37. use OCP\Files\IRootFolder;
  38. use OCP\IDBConnection;
  39. use OCP\Files\Node;
  40. /**
  41. * Class DefaultShareProvider
  42. *
  43. * @package OC\Share20
  44. */
  45. class DefaultShareProvider implements IShareProvider {
  46. // Special share type for user modified group shares
  47. const SHARE_TYPE_USERGROUP = 2;
  48. /** @var IDBConnection */
  49. private $dbConn;
  50. /** @var IUserManager */
  51. private $userManager;
  52. /** @var IGroupManager */
  53. private $groupManager;
  54. /** @var IRootFolder */
  55. private $rootFolder;
  56. /**
  57. * DefaultShareProvider constructor.
  58. *
  59. * @param IDBConnection $connection
  60. * @param IUserManager $userManager
  61. * @param IGroupManager $groupManager
  62. * @param IRootFolder $rootFolder
  63. */
  64. public function __construct(
  65. IDBConnection $connection,
  66. IUserManager $userManager,
  67. IGroupManager $groupManager,
  68. IRootFolder $rootFolder) {
  69. $this->dbConn = $connection;
  70. $this->userManager = $userManager;
  71. $this->groupManager = $groupManager;
  72. $this->rootFolder = $rootFolder;
  73. }
  74. /**
  75. * Return the identifier of this provider.
  76. *
  77. * @return string Containing only [a-zA-Z0-9]
  78. */
  79. public function identifier() {
  80. return 'ocinternal';
  81. }
  82. /**
  83. * Share a path
  84. *
  85. * @param \OCP\Share\IShare $share
  86. * @return \OCP\Share\IShare The share object
  87. * @throws ShareNotFound
  88. * @throws \Exception
  89. */
  90. public function create(\OCP\Share\IShare $share) {
  91. $qb = $this->dbConn->getQueryBuilder();
  92. $qb->insert('share');
  93. $qb->setValue('share_type', $qb->createNamedParameter($share->getShareType()));
  94. if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) {
  95. //Set the UID of the user we share with
  96. $qb->setValue('share_with', $qb->createNamedParameter($share->getSharedWith()));
  97. } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
  98. //Set the GID of the group we share with
  99. $qb->setValue('share_with', $qb->createNamedParameter($share->getSharedWith()));
  100. } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) {
  101. //Set the token of the share
  102. $qb->setValue('token', $qb->createNamedParameter($share->getToken()));
  103. //If a password is set store it
  104. if ($share->getPassword() !== null) {
  105. $qb->setValue('password', $qb->createNamedParameter($share->getPassword()));
  106. }
  107. //If an expiration date is set store it
  108. if ($share->getExpirationDate() !== null) {
  109. $qb->setValue('expiration', $qb->createNamedParameter($share->getExpirationDate(), 'datetime'));
  110. }
  111. if (method_exists($share, 'getParent')) {
  112. $qb->setValue('parent', $qb->createNamedParameter($share->getParent()));
  113. }
  114. } else {
  115. throw new \Exception('invalid share type!');
  116. }
  117. // Set what is shares
  118. $qb->setValue('item_type', $qb->createParameter('itemType'));
  119. if ($share->getNode() instanceof \OCP\Files\File) {
  120. $qb->setParameter('itemType', 'file');
  121. } else {
  122. $qb->setParameter('itemType', 'folder');
  123. }
  124. // Set the file id
  125. $qb->setValue('item_source', $qb->createNamedParameter($share->getNode()->getId()));
  126. $qb->setValue('file_source', $qb->createNamedParameter($share->getNode()->getId()));
  127. // set the permissions
  128. $qb->setValue('permissions', $qb->createNamedParameter($share->getPermissions()));
  129. // Set who created this share
  130. $qb->setValue('uid_initiator', $qb->createNamedParameter($share->getSharedBy()));
  131. // Set who is the owner of this file/folder (and this the owner of the share)
  132. $qb->setValue('uid_owner', $qb->createNamedParameter($share->getShareOwner()));
  133. // Set the file target
  134. $qb->setValue('file_target', $qb->createNamedParameter($share->getTarget()));
  135. // Set the time this share was created
  136. $qb->setValue('stime', $qb->createNamedParameter(time()));
  137. // insert the data and fetch the id of the share
  138. $this->dbConn->beginTransaction();
  139. $qb->execute();
  140. $id = $this->dbConn->lastInsertId('*PREFIX*share');
  141. // Now fetch the inserted share and create a complete share object
  142. $qb = $this->dbConn->getQueryBuilder();
  143. $qb->select('*')
  144. ->from('share')
  145. ->where($qb->expr()->eq('id', $qb->createNamedParameter($id)));
  146. $cursor = $qb->execute();
  147. $data = $cursor->fetch();
  148. $this->dbConn->commit();
  149. $cursor->closeCursor();
  150. if ($data === false) {
  151. throw new ShareNotFound();
  152. }
  153. $share = $this->createShare($data);
  154. return $share;
  155. }
  156. /**
  157. * Update a share
  158. *
  159. * @param \OCP\Share\IShare $share
  160. * @return \OCP\Share\IShare The share object
  161. */
  162. public function update(\OCP\Share\IShare $share) {
  163. if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) {
  164. /*
  165. * We allow updating the recipient on user shares.
  166. */
  167. $qb = $this->dbConn->getQueryBuilder();
  168. $qb->update('share')
  169. ->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())))
  170. ->set('share_with', $qb->createNamedParameter($share->getSharedWith()))
  171. ->set('uid_owner', $qb->createNamedParameter($share->getShareOwner()))
  172. ->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()))
  173. ->set('permissions', $qb->createNamedParameter($share->getPermissions()))
  174. ->set('item_source', $qb->createNamedParameter($share->getNode()->getId()))
  175. ->set('file_source', $qb->createNamedParameter($share->getNode()->getId()))
  176. ->set('expiration', $qb->createNamedParameter($share->getExpirationDate(), IQueryBuilder::PARAM_DATE))
  177. ->execute();
  178. } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
  179. $qb = $this->dbConn->getQueryBuilder();
  180. $qb->update('share')
  181. ->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())))
  182. ->set('uid_owner', $qb->createNamedParameter($share->getShareOwner()))
  183. ->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()))
  184. ->set('permissions', $qb->createNamedParameter($share->getPermissions()))
  185. ->set('item_source', $qb->createNamedParameter($share->getNode()->getId()))
  186. ->set('file_source', $qb->createNamedParameter($share->getNode()->getId()))
  187. ->set('expiration', $qb->createNamedParameter($share->getExpirationDate(), IQueryBuilder::PARAM_DATE))
  188. ->execute();
  189. /*
  190. * Update all user defined group shares
  191. */
  192. $qb = $this->dbConn->getQueryBuilder();
  193. $qb->update('share')
  194. ->where($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId())))
  195. ->set('uid_owner', $qb->createNamedParameter($share->getShareOwner()))
  196. ->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()))
  197. ->set('item_source', $qb->createNamedParameter($share->getNode()->getId()))
  198. ->set('file_source', $qb->createNamedParameter($share->getNode()->getId()))
  199. ->set('expiration', $qb->createNamedParameter($share->getExpirationDate(), IQueryBuilder::PARAM_DATE))
  200. ->execute();
  201. /*
  202. * Now update the permissions for all children that have not set it to 0
  203. */
  204. $qb = $this->dbConn->getQueryBuilder();
  205. $qb->update('share')
  206. ->where($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId())))
  207. ->andWhere($qb->expr()->neq('permissions', $qb->createNamedParameter(0)))
  208. ->set('permissions', $qb->createNamedParameter($share->getPermissions()))
  209. ->execute();
  210. } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) {
  211. $qb = $this->dbConn->getQueryBuilder();
  212. $qb->update('share')
  213. ->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())))
  214. ->set('password', $qb->createNamedParameter($share->getPassword()))
  215. ->set('uid_owner', $qb->createNamedParameter($share->getShareOwner()))
  216. ->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()))
  217. ->set('permissions', $qb->createNamedParameter($share->getPermissions()))
  218. ->set('item_source', $qb->createNamedParameter($share->getNode()->getId()))
  219. ->set('file_source', $qb->createNamedParameter($share->getNode()->getId()))
  220. ->set('token', $qb->createNamedParameter($share->getToken()))
  221. ->set('expiration', $qb->createNamedParameter($share->getExpirationDate(), IQueryBuilder::PARAM_DATE))
  222. ->execute();
  223. }
  224. return $share;
  225. }
  226. /**
  227. * Get all children of this share
  228. * FIXME: remove once https://github.com/owncloud/core/pull/21660 is in
  229. *
  230. * @param \OCP\Share\IShare $parent
  231. * @return \OCP\Share\IShare[]
  232. */
  233. public function getChildren(\OCP\Share\IShare $parent) {
  234. $children = [];
  235. $qb = $this->dbConn->getQueryBuilder();
  236. $qb->select('*')
  237. ->from('share')
  238. ->where($qb->expr()->eq('parent', $qb->createNamedParameter($parent->getId())))
  239. ->andWhere(
  240. $qb->expr()->in(
  241. 'share_type',
  242. $qb->createNamedParameter([
  243. \OCP\Share::SHARE_TYPE_USER,
  244. \OCP\Share::SHARE_TYPE_GROUP,
  245. \OCP\Share::SHARE_TYPE_LINK,
  246. ], IQueryBuilder::PARAM_INT_ARRAY)
  247. )
  248. )
  249. ->andWhere($qb->expr()->orX(
  250. $qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
  251. $qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
  252. ))
  253. ->orderBy('id');
  254. $cursor = $qb->execute();
  255. while($data = $cursor->fetch()) {
  256. $children[] = $this->createShare($data);
  257. }
  258. $cursor->closeCursor();
  259. return $children;
  260. }
  261. /**
  262. * Delete a share
  263. *
  264. * @param \OCP\Share\IShare $share
  265. */
  266. public function delete(\OCP\Share\IShare $share) {
  267. $qb = $this->dbConn->getQueryBuilder();
  268. $qb->delete('share')
  269. ->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())));
  270. /*
  271. * If the share is a group share delete all possible
  272. * user defined groups shares.
  273. */
  274. if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
  275. $qb->orWhere($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId())));
  276. }
  277. $qb->execute();
  278. }
  279. /**
  280. * Unshare a share from the recipient. If this is a group share
  281. * this means we need a special entry in the share db.
  282. *
  283. * @param \OCP\Share\IShare $share
  284. * @param string $recipient UserId of recipient
  285. * @throws BackendError
  286. * @throws ProviderException
  287. */
  288. public function deleteFromSelf(\OCP\Share\IShare $share, $recipient) {
  289. if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
  290. $group = $this->groupManager->get($share->getSharedWith());
  291. $user = $this->userManager->get($recipient);
  292. if (is_null($group)) {
  293. throw new ProviderException('Group "' . $share->getSharedWith() . '" does not exist');
  294. }
  295. if (!$group->inGroup($user)) {
  296. throw new ProviderException('Recipient not in receiving group');
  297. }
  298. // Try to fetch user specific share
  299. $qb = $this->dbConn->getQueryBuilder();
  300. $stmt = $qb->select('*')
  301. ->from('share')
  302. ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP)))
  303. ->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($recipient)))
  304. ->andWhere($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId())))
  305. ->andWhere($qb->expr()->orX(
  306. $qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
  307. $qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
  308. ))
  309. ->execute();
  310. $data = $stmt->fetch();
  311. /*
  312. * Check if there already is a user specific group share.
  313. * If there is update it (if required).
  314. */
  315. if ($data === false) {
  316. $qb = $this->dbConn->getQueryBuilder();
  317. $type = $share->getNodeType();
  318. //Insert new share
  319. $qb->insert('share')
  320. ->values([
  321. 'share_type' => $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP),
  322. 'share_with' => $qb->createNamedParameter($recipient),
  323. 'uid_owner' => $qb->createNamedParameter($share->getShareOwner()),
  324. 'uid_initiator' => $qb->createNamedParameter($share->getSharedBy()),
  325. 'parent' => $qb->createNamedParameter($share->getId()),
  326. 'item_type' => $qb->createNamedParameter($type),
  327. 'item_source' => $qb->createNamedParameter($share->getNodeId()),
  328. 'file_source' => $qb->createNamedParameter($share->getNodeId()),
  329. 'file_target' => $qb->createNamedParameter($share->getTarget()),
  330. 'permissions' => $qb->createNamedParameter(0),
  331. 'stime' => $qb->createNamedParameter($share->getShareTime()->getTimestamp()),
  332. ])->execute();
  333. } else if ($data['permissions'] !== 0) {
  334. // Update existing usergroup share
  335. $qb = $this->dbConn->getQueryBuilder();
  336. $qb->update('share')
  337. ->set('permissions', $qb->createNamedParameter(0))
  338. ->where($qb->expr()->eq('id', $qb->createNamedParameter($data['id'])))
  339. ->execute();
  340. }
  341. } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) {
  342. if ($share->getSharedWith() !== $recipient) {
  343. throw new ProviderException('Recipient does not match');
  344. }
  345. // We can just delete user and link shares
  346. $this->delete($share);
  347. } else {
  348. throw new ProviderException('Invalid shareType');
  349. }
  350. }
  351. /**
  352. * @inheritdoc
  353. */
  354. public function move(\OCP\Share\IShare $share, $recipient) {
  355. if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) {
  356. // Just update the target
  357. $qb = $this->dbConn->getQueryBuilder();
  358. $qb->update('share')
  359. ->set('file_target', $qb->createNamedParameter($share->getTarget()))
  360. ->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())))
  361. ->execute();
  362. } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
  363. // Check if there is a usergroup share
  364. $qb = $this->dbConn->getQueryBuilder();
  365. $stmt = $qb->select('id')
  366. ->from('share')
  367. ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP)))
  368. ->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($recipient)))
  369. ->andWhere($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId())))
  370. ->andWhere($qb->expr()->orX(
  371. $qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
  372. $qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
  373. ))
  374. ->setMaxResults(1)
  375. ->execute();
  376. $data = $stmt->fetch();
  377. $stmt->closeCursor();
  378. if ($data === false) {
  379. // No usergroup share yet. Create one.
  380. $qb = $this->dbConn->getQueryBuilder();
  381. $qb->insert('share')
  382. ->values([
  383. 'share_type' => $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP),
  384. 'share_with' => $qb->createNamedParameter($recipient),
  385. 'uid_owner' => $qb->createNamedParameter($share->getShareOwner()),
  386. 'uid_initiator' => $qb->createNamedParameter($share->getSharedBy()),
  387. 'parent' => $qb->createNamedParameter($share->getId()),
  388. 'item_type' => $qb->createNamedParameter($share->getNode() instanceof File ? 'file' : 'folder'),
  389. 'item_source' => $qb->createNamedParameter($share->getNode()->getId()),
  390. 'file_source' => $qb->createNamedParameter($share->getNode()->getId()),
  391. 'file_target' => $qb->createNamedParameter($share->getTarget()),
  392. 'permissions' => $qb->createNamedParameter($share->getPermissions()),
  393. 'stime' => $qb->createNamedParameter($share->getShareTime()->getTimestamp()),
  394. ])->execute();
  395. } else {
  396. // Already a usergroup share. Update it.
  397. $qb = $this->dbConn->getQueryBuilder();
  398. $qb->update('share')
  399. ->set('file_target', $qb->createNamedParameter($share->getTarget()))
  400. ->where($qb->expr()->eq('id', $qb->createNamedParameter($data['id'])))
  401. ->execute();
  402. }
  403. }
  404. return $share;
  405. }
  406. public function getSharesInFolder($userId, Folder $node, $reshares) {
  407. $qb = $this->dbConn->getQueryBuilder();
  408. $qb->select('*')
  409. ->from('share', 's')
  410. ->andWhere($qb->expr()->orX(
  411. $qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
  412. $qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
  413. ));
  414. $qb->andWhere($qb->expr()->orX(
  415. $qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_USER)),
  416. $qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_GROUP)),
  417. $qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_LINK))
  418. ));
  419. /**
  420. * Reshares for this user are shares where they are the owner.
  421. */
  422. if ($reshares === false) {
  423. $qb->andWhere($qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)));
  424. } else {
  425. $qb->andWhere(
  426. $qb->expr()->orX(
  427. $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)),
  428. $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId))
  429. )
  430. );
  431. }
  432. $qb->innerJoin('s', 'filecache' ,'f', $qb->expr()->eq('s.file_source', 'f.fileid'));
  433. $qb->andWhere($qb->expr()->eq('f.parent', $qb->createNamedParameter($node->getId())));
  434. $qb->orderBy('id');
  435. $cursor = $qb->execute();
  436. $shares = [];
  437. while ($data = $cursor->fetch()) {
  438. $shares[$data['fileid']][] = $this->createShare($data);
  439. }
  440. $cursor->closeCursor();
  441. return $shares;
  442. }
  443. /**
  444. * @inheritdoc
  445. */
  446. public function getSharesBy($userId, $shareType, $node, $reshares, $limit, $offset) {
  447. $qb = $this->dbConn->getQueryBuilder();
  448. $qb->select('*')
  449. ->from('share')
  450. ->andWhere($qb->expr()->orX(
  451. $qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
  452. $qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
  453. ));
  454. $qb->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter($shareType)));
  455. /**
  456. * Reshares for this user are shares where they are the owner.
  457. */
  458. if ($reshares === false) {
  459. $qb->andWhere($qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)));
  460. } else {
  461. $qb->andWhere(
  462. $qb->expr()->orX(
  463. $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)),
  464. $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId))
  465. )
  466. );
  467. }
  468. if ($node !== null) {
  469. $qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId())));
  470. }
  471. if ($limit !== -1) {
  472. $qb->setMaxResults($limit);
  473. }
  474. $qb->setFirstResult($offset);
  475. $qb->orderBy('id');
  476. $cursor = $qb->execute();
  477. $shares = [];
  478. while($data = $cursor->fetch()) {
  479. $shares[] = $this->createShare($data);
  480. }
  481. $cursor->closeCursor();
  482. return $shares;
  483. }
  484. /**
  485. * @inheritdoc
  486. */
  487. public function getShareById($id, $recipientId = null) {
  488. $qb = $this->dbConn->getQueryBuilder();
  489. $qb->select('*')
  490. ->from('share')
  491. ->where($qb->expr()->eq('id', $qb->createNamedParameter($id)))
  492. ->andWhere(
  493. $qb->expr()->in(
  494. 'share_type',
  495. $qb->createNamedParameter([
  496. \OCP\Share::SHARE_TYPE_USER,
  497. \OCP\Share::SHARE_TYPE_GROUP,
  498. \OCP\Share::SHARE_TYPE_LINK,
  499. ], IQueryBuilder::PARAM_INT_ARRAY)
  500. )
  501. )
  502. ->andWhere($qb->expr()->orX(
  503. $qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
  504. $qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
  505. ));
  506. $cursor = $qb->execute();
  507. $data = $cursor->fetch();
  508. $cursor->closeCursor();
  509. if ($data === false) {
  510. throw new ShareNotFound();
  511. }
  512. try {
  513. $share = $this->createShare($data);
  514. } catch (InvalidShare $e) {
  515. throw new ShareNotFound();
  516. }
  517. // If the recipient is set for a group share resolve to that user
  518. if ($recipientId !== null && $share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
  519. $share = $this->resolveGroupShares([$share], $recipientId)[0];
  520. }
  521. return $share;
  522. }
  523. /**
  524. * Get shares for a given path
  525. *
  526. * @param \OCP\Files\Node $path
  527. * @return \OCP\Share\IShare[]
  528. */
  529. public function getSharesByPath(Node $path) {
  530. $qb = $this->dbConn->getQueryBuilder();
  531. $cursor = $qb->select('*')
  532. ->from('share')
  533. ->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($path->getId())))
  534. ->andWhere(
  535. $qb->expr()->orX(
  536. $qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_USER)),
  537. $qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_GROUP))
  538. )
  539. )
  540. ->andWhere($qb->expr()->orX(
  541. $qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
  542. $qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
  543. ))
  544. ->execute();
  545. $shares = [];
  546. while($data = $cursor->fetch()) {
  547. $shares[] = $this->createShare($data);
  548. }
  549. $cursor->closeCursor();
  550. return $shares;
  551. }
  552. /**
  553. * Returns whether the given database result can be interpreted as
  554. * a share with accessible file (not trashed, not deleted)
  555. */
  556. private function isAccessibleResult($data) {
  557. // exclude shares leading to deleted file entries
  558. if ($data['fileid'] === null) {
  559. return false;
  560. }
  561. // exclude shares leading to trashbin on home storages
  562. $pathSections = explode('/', $data['path'], 2);
  563. // FIXME: would not detect rare md5'd home storage case properly
  564. if ($pathSections[0] !== 'files'
  565. && in_array(explode(':', $data['storage_string_id'], 2)[0], array('home', 'object'))) {
  566. return false;
  567. }
  568. return true;
  569. }
  570. /**
  571. * @inheritdoc
  572. */
  573. public function getSharedWith($userId, $shareType, $node, $limit, $offset) {
  574. /** @var Share[] $shares */
  575. $shares = [];
  576. if ($shareType === \OCP\Share::SHARE_TYPE_USER) {
  577. //Get shares directly with this user
  578. $qb = $this->dbConn->getQueryBuilder();
  579. $qb->select('s.*',
  580. 'f.fileid', 'f.path', 'f.permissions AS f_permissions', 'f.storage', 'f.path_hash',
  581. 'f.parent AS f_parent', 'f.name', 'f.mimetype', 'f.mimepart', 'f.size', 'f.mtime', 'f.storage_mtime',
  582. 'f.encrypted', 'f.unencrypted_size', 'f.etag', 'f.checksum'
  583. )
  584. ->selectAlias('st.id', 'storage_string_id')
  585. ->from('share', 's')
  586. ->leftJoin('s', 'filecache', 'f', $qb->expr()->eq('s.file_source', 'f.fileid'))
  587. ->leftJoin('f', 'storages', 'st', $qb->expr()->eq('f.storage', 'st.numeric_id'));
  588. // Order by id
  589. $qb->orderBy('s.id');
  590. // Set limit and offset
  591. if ($limit !== -1) {
  592. $qb->setMaxResults($limit);
  593. }
  594. $qb->setFirstResult($offset);
  595. $qb->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_USER)))
  596. ->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($userId)))
  597. ->andWhere($qb->expr()->orX(
  598. $qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
  599. $qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
  600. ));
  601. // Filter by node if provided
  602. if ($node !== null) {
  603. $qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId())));
  604. }
  605. $cursor = $qb->execute();
  606. while($data = $cursor->fetch()) {
  607. if ($this->isAccessibleResult($data)) {
  608. $shares[] = $this->createShare($data);
  609. }
  610. }
  611. $cursor->closeCursor();
  612. } else if ($shareType === \OCP\Share::SHARE_TYPE_GROUP) {
  613. $user = $this->userManager->get($userId);
  614. $allGroups = $this->groupManager->getUserGroups($user);
  615. /** @var Share[] $shares2 */
  616. $shares2 = [];
  617. $start = 0;
  618. while(true) {
  619. $groups = array_slice($allGroups, $start, 100);
  620. $start += 100;
  621. if ($groups === []) {
  622. break;
  623. }
  624. $qb = $this->dbConn->getQueryBuilder();
  625. $qb->select('s.*',
  626. 'f.fileid', 'f.path', 'f.permissions AS f_permissions', 'f.storage', 'f.path_hash',
  627. 'f.parent AS f_parent', 'f.name', 'f.mimetype', 'f.mimepart', 'f.size', 'f.mtime', 'f.storage_mtime',
  628. 'f.encrypted', 'f.unencrypted_size', 'f.etag', 'f.checksum'
  629. )
  630. ->selectAlias('st.id', 'storage_string_id')
  631. ->from('share', 's')
  632. ->leftJoin('s', 'filecache', 'f', $qb->expr()->eq('s.file_source', 'f.fileid'))
  633. ->leftJoin('f', 'storages', 'st', $qb->expr()->eq('f.storage', 'st.numeric_id'))
  634. ->orderBy('s.id')
  635. ->setFirstResult(0);
  636. if ($limit !== -1) {
  637. $qb->setMaxResults($limit - count($shares));
  638. }
  639. // Filter by node if provided
  640. if ($node !== null) {
  641. $qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId())));
  642. }
  643. $groups = array_filter($groups, function($group) { return $group instanceof IGroup; });
  644. $groups = array_map(function(IGroup $group) { return $group->getGID(); }, $groups);
  645. $qb->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_GROUP)))
  646. ->andWhere($qb->expr()->in('share_with', $qb->createNamedParameter(
  647. $groups,
  648. IQueryBuilder::PARAM_STR_ARRAY
  649. )))
  650. ->andWhere($qb->expr()->orX(
  651. $qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
  652. $qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
  653. ));
  654. $cursor = $qb->execute();
  655. while($data = $cursor->fetch()) {
  656. if ($offset > 0) {
  657. $offset--;
  658. continue;
  659. }
  660. if ($this->isAccessibleResult($data)) {
  661. $shares2[] = $this->createShare($data);
  662. }
  663. }
  664. $cursor->closeCursor();
  665. }
  666. /*
  667. * Resolve all group shares to user specific shares
  668. */
  669. $shares = $this->resolveGroupShares($shares2, $userId);
  670. } else {
  671. throw new BackendError('Invalid backend');
  672. }
  673. return $shares;
  674. }
  675. /**
  676. * Get a share by token
  677. *
  678. * @param string $token
  679. * @return \OCP\Share\IShare
  680. * @throws ShareNotFound
  681. */
  682. public function getShareByToken($token) {
  683. $qb = $this->dbConn->getQueryBuilder();
  684. $cursor = $qb->select('*')
  685. ->from('share')
  686. ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_LINK)))
  687. ->andWhere($qb->expr()->eq('token', $qb->createNamedParameter($token)))
  688. ->andWhere($qb->expr()->orX(
  689. $qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
  690. $qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
  691. ))
  692. ->execute();
  693. $data = $cursor->fetch();
  694. if ($data === false) {
  695. throw new ShareNotFound();
  696. }
  697. try {
  698. $share = $this->createShare($data);
  699. } catch (InvalidShare $e) {
  700. throw new ShareNotFound();
  701. }
  702. return $share;
  703. }
  704. /**
  705. * Create a share object from an database row
  706. *
  707. * @param mixed[] $data
  708. * @return \OCP\Share\IShare
  709. * @throws InvalidShare
  710. */
  711. private function createShare($data) {
  712. $share = new Share($this->rootFolder, $this->userManager);
  713. $share->setId((int)$data['id'])
  714. ->setShareType((int)$data['share_type'])
  715. ->setPermissions((int)$data['permissions'])
  716. ->setTarget($data['file_target'])
  717. ->setMailSend((bool)$data['mail_send']);
  718. $shareTime = new \DateTime();
  719. $shareTime->setTimestamp((int)$data['stime']);
  720. $share->setShareTime($shareTime);
  721. if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) {
  722. $share->setSharedWith($data['share_with']);
  723. } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
  724. $share->setSharedWith($data['share_with']);
  725. } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) {
  726. $share->setPassword($data['password']);
  727. $share->setToken($data['token']);
  728. }
  729. $share->setSharedBy($data['uid_initiator']);
  730. $share->setShareOwner($data['uid_owner']);
  731. $share->setNodeId((int)$data['file_source']);
  732. $share->setNodeType($data['item_type']);
  733. if ($data['expiration'] !== null) {
  734. $expiration = \DateTime::createFromFormat('Y-m-d H:i:s', $data['expiration']);
  735. $share->setExpirationDate($expiration);
  736. }
  737. if (isset($data['f_permissions'])) {
  738. $entryData = $data;
  739. $entryData['permissions'] = $entryData['f_permissions'];
  740. $entryData['parent'] = $entryData['f_parent'];;
  741. $share->setNodeCacheEntry(Cache::cacheEntryFromData($entryData,
  742. \OC::$server->getMimeTypeLoader()));
  743. }
  744. $share->setProviderId($this->identifier());
  745. return $share;
  746. }
  747. /**
  748. * @param Share[] $shares
  749. * @param $userId
  750. * @return Share[] The updates shares if no update is found for a share return the original
  751. */
  752. private function resolveGroupShares($shares, $userId) {
  753. $result = [];
  754. $start = 0;
  755. while(true) {
  756. /** @var Share[] $shareSlice */
  757. $shareSlice = array_slice($shares, $start, 100);
  758. $start += 100;
  759. if ($shareSlice === []) {
  760. break;
  761. }
  762. /** @var int[] $ids */
  763. $ids = [];
  764. /** @var Share[] $shareMap */
  765. $shareMap = [];
  766. foreach ($shareSlice as $share) {
  767. $ids[] = (int)$share->getId();
  768. $shareMap[$share->getId()] = $share;
  769. }
  770. $qb = $this->dbConn->getQueryBuilder();
  771. $query = $qb->select('*')
  772. ->from('share')
  773. ->where($qb->expr()->in('parent', $qb->createNamedParameter($ids, IQueryBuilder::PARAM_INT_ARRAY)))
  774. ->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($userId)))
  775. ->andWhere($qb->expr()->orX(
  776. $qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
  777. $qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
  778. ));
  779. $stmt = $query->execute();
  780. while($data = $stmt->fetch()) {
  781. $shareMap[$data['parent']]->setPermissions((int)$data['permissions']);
  782. $shareMap[$data['parent']]->setTarget($data['file_target']);
  783. }
  784. $stmt->closeCursor();
  785. foreach ($shareMap as $share) {
  786. $result[] = $share;
  787. }
  788. }
  789. return $result;
  790. }
  791. /**
  792. * A user is deleted from the system
  793. * So clean up the relevant shares.
  794. *
  795. * @param string $uid
  796. * @param int $shareType
  797. */
  798. public function userDeleted($uid, $shareType) {
  799. $qb = $this->dbConn->getQueryBuilder();
  800. $qb->delete('share');
  801. if ($shareType === \OCP\Share::SHARE_TYPE_USER) {
  802. /*
  803. * Delete all user shares that are owned by this user
  804. * or that are received by this user
  805. */
  806. $qb->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_USER)));
  807. $qb->andWhere(
  808. $qb->expr()->orX(
  809. $qb->expr()->eq('uid_owner', $qb->createNamedParameter($uid)),
  810. $qb->expr()->eq('share_with', $qb->createNamedParameter($uid))
  811. )
  812. );
  813. } else if ($shareType === \OCP\Share::SHARE_TYPE_GROUP) {
  814. /*
  815. * Delete all group shares that are owned by this user
  816. * Or special user group shares that are received by this user
  817. */
  818. $qb->where(
  819. $qb->expr()->andX(
  820. $qb->expr()->orX(
  821. $qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_GROUP)),
  822. $qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP))
  823. ),
  824. $qb->expr()->eq('uid_owner', $qb->createNamedParameter($uid))
  825. )
  826. );
  827. $qb->orWhere(
  828. $qb->expr()->andX(
  829. $qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP)),
  830. $qb->expr()->eq('share_with', $qb->createNamedParameter($uid))
  831. )
  832. );
  833. } else if ($shareType === \OCP\Share::SHARE_TYPE_LINK) {
  834. /*
  835. * Delete all link shares owned by this user.
  836. * And all link shares initiated by this user (until #22327 is in)
  837. */
  838. $qb->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_LINK)));
  839. $qb->andWhere(
  840. $qb->expr()->orX(
  841. $qb->expr()->eq('uid_owner', $qb->createNamedParameter($uid)),
  842. $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($uid))
  843. )
  844. );
  845. }
  846. $qb->execute();
  847. }
  848. /**
  849. * Delete all shares received by this group. As well as any custom group
  850. * shares for group members.
  851. *
  852. * @param string $gid
  853. */
  854. public function groupDeleted($gid) {
  855. /*
  856. * First delete all custom group shares for group members
  857. */
  858. $qb = $this->dbConn->getQueryBuilder();
  859. $qb->select('id')
  860. ->from('share')
  861. ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_GROUP)))
  862. ->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($gid)));
  863. $cursor = $qb->execute();
  864. $ids = [];
  865. while($row = $cursor->fetch()) {
  866. $ids[] = (int)$row['id'];
  867. }
  868. $cursor->closeCursor();
  869. if (!empty($ids)) {
  870. $chunks = array_chunk($ids, 100);
  871. foreach ($chunks as $chunk) {
  872. $qb->delete('share')
  873. ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP)))
  874. ->andWhere($qb->expr()->in('parent', $qb->createNamedParameter($chunk, IQueryBuilder::PARAM_INT_ARRAY)));
  875. $qb->execute();
  876. }
  877. }
  878. /*
  879. * Now delete all the group shares
  880. */
  881. $qb = $this->dbConn->getQueryBuilder();
  882. $qb->delete('share')
  883. ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_GROUP)))
  884. ->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($gid)));
  885. $qb->execute();
  886. }
  887. /**
  888. * Delete custom group shares to this group for this user
  889. *
  890. * @param string $uid
  891. * @param string $gid
  892. */
  893. public function userDeletedFromGroup($uid, $gid) {
  894. /*
  895. * Get all group shares
  896. */
  897. $qb = $this->dbConn->getQueryBuilder();
  898. $qb->select('id')
  899. ->from('share')
  900. ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_GROUP)))
  901. ->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($gid)));
  902. $cursor = $qb->execute();
  903. $ids = [];
  904. while($row = $cursor->fetch()) {
  905. $ids[] = (int)$row['id'];
  906. }
  907. $cursor->closeCursor();
  908. if (!empty($ids)) {
  909. $chunks = array_chunk($ids, 100);
  910. foreach ($chunks as $chunk) {
  911. /*
  912. * Delete all special shares wit this users for the found group shares
  913. */
  914. $qb->delete('share')
  915. ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP)))
  916. ->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($uid)))
  917. ->andWhere($qb->expr()->in('parent', $qb->createNamedParameter($chunk, IQueryBuilder::PARAM_INT_ARRAY)));
  918. $qb->execute();
  919. }
  920. }
  921. }
  922. /**
  923. * @inheritdoc
  924. */
  925. public function getAccessList($nodes, $currentAccess) {
  926. $ids = [];
  927. foreach ($nodes as $node) {
  928. $ids[] = $node->getId();
  929. }
  930. $qb = $this->dbConn->getQueryBuilder();
  931. $or = $qb->expr()->orX(
  932. $qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_USER)),
  933. $qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_GROUP)),
  934. $qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_LINK))
  935. );
  936. if ($currentAccess) {
  937. $or->add($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP)));
  938. }
  939. $qb->select('id', 'parent', 'share_type', 'share_with', 'file_source', 'file_target', 'permissions')
  940. ->from('share')
  941. ->where(
  942. $or
  943. )
  944. ->andWhere($qb->expr()->in('file_source', $qb->createNamedParameter($ids, IQueryBuilder::PARAM_INT_ARRAY)))
  945. ->andWhere($qb->expr()->orX(
  946. $qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
  947. $qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
  948. ));
  949. $cursor = $qb->execute();
  950. $users = [];
  951. $link = false;
  952. while($row = $cursor->fetch()) {
  953. $type = (int)$row['share_type'];
  954. if ($type === \OCP\Share::SHARE_TYPE_USER) {
  955. $uid = $row['share_with'];
  956. $users[$uid] = isset($users[$uid]) ? $users[$uid] : [];
  957. $users[$uid][$row['id']] = $row;
  958. } else if ($type === \OCP\Share::SHARE_TYPE_GROUP) {
  959. $gid = $row['share_with'];
  960. $group = $this->groupManager->get($gid);
  961. if ($group === null) {
  962. continue;
  963. }
  964. $userList = $group->getUsers();
  965. foreach ($userList as $user) {
  966. $uid = $user->getUID();
  967. $users[$uid] = isset($users[$uid]) ? $users[$uid] : [];
  968. $users[$uid][$row['id']] = $row;
  969. }
  970. } else if ($type === \OCP\Share::SHARE_TYPE_LINK) {
  971. $link = true;
  972. } else if ($type === self::SHARE_TYPE_USERGROUP && $currentAccess === true) {
  973. $uid = $row['share_with'];
  974. $users[$uid] = isset($users[$uid]) ? $users[$uid] : [];
  975. $users[$uid][$row['id']] = $row;
  976. }
  977. }
  978. $cursor->closeCursor();
  979. if ($currentAccess === true) {
  980. $users = array_map([$this, 'filterSharesOfUser'], $users);
  981. $users = array_filter($users);
  982. } else {
  983. $users = array_keys($users);
  984. }
  985. return ['users' => $users, 'public' => $link];
  986. }
  987. /**
  988. * For each user the path with the fewest slashes is returned
  989. * @param array $shares
  990. * @return array
  991. */
  992. protected function filterSharesOfUser(array $shares) {
  993. // Group shares when the user has a share exception
  994. foreach ($shares as $id => $share) {
  995. $type = (int) $share['share_type'];
  996. $permissions = (int) $share['permissions'];
  997. if ($type === self::SHARE_TYPE_USERGROUP) {
  998. unset($shares[$share['parent']]);
  999. if ($permissions === 0) {
  1000. unset($shares[$id]);
  1001. }
  1002. }
  1003. }
  1004. $best = [];
  1005. $bestDepth = 0;
  1006. foreach ($shares as $id => $share) {
  1007. $depth = substr_count($share['file_target'], '/');
  1008. if (empty($best) || $depth < $bestDepth) {
  1009. $bestDepth = $depth;
  1010. $best = [
  1011. 'node_id' => $share['file_source'],
  1012. 'node_path' => $share['file_target'],
  1013. ];
  1014. }
  1015. }
  1016. return $best;
  1017. }
  1018. }