You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

FederatedShareProvider.php 24KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880
  1. <?php
  2. /**
  3. * @author Björn Schießle <bjoern@schiessle.org>
  4. * @author Roeland Jago Douma <rullzer@owncloud.com>
  5. * @author Thomas Müller <thomas.mueller@tmit.eu>
  6. *
  7. * @copyright Copyright (c) 2016, ownCloud, Inc.
  8. * @license AGPL-3.0
  9. *
  10. * This code is free software: you can redistribute it and/or modify
  11. * it under the terms of the GNU Affero General Public License, version 3,
  12. * as published by the Free Software Foundation.
  13. *
  14. * This program is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. * GNU Affero General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU Affero General Public License, version 3,
  20. * along with this program. If not, see <http://www.gnu.org/licenses/>
  21. *
  22. */
  23. namespace OCA\FederatedFileSharing;
  24. use OC\Share20\Share;
  25. use OCP\Files\IRootFolder;
  26. use OCP\IConfig;
  27. use OCP\IL10N;
  28. use OCP\ILogger;
  29. use OCP\IUserManager;
  30. use OCP\Share\IShare;
  31. use OCP\Share\IShareProvider;
  32. use OC\Share20\Exception\InvalidShare;
  33. use OCP\Share\Exceptions\ShareNotFound;
  34. use OCP\Files\NotFoundException;
  35. use OCP\IDBConnection;
  36. use OCP\Files\Node;
  37. /**
  38. * Class FederatedShareProvider
  39. *
  40. * @package OCA\FederatedFileSharing
  41. */
  42. class FederatedShareProvider implements IShareProvider {
  43. const SHARE_TYPE_REMOTE = 6;
  44. /** @var IDBConnection */
  45. private $dbConnection;
  46. /** @var AddressHandler */
  47. private $addressHandler;
  48. /** @var Notifications */
  49. private $notifications;
  50. /** @var TokenHandler */
  51. private $tokenHandler;
  52. /** @var IL10N */
  53. private $l;
  54. /** @var ILogger */
  55. private $logger;
  56. /** @var IRootFolder */
  57. private $rootFolder;
  58. /** @var IConfig */
  59. private $config;
  60. /** @var string */
  61. private $externalShareTable = 'share_external';
  62. /** @var IUserManager */
  63. private $userManager;
  64. /**
  65. * DefaultShareProvider constructor.
  66. *
  67. * @param IDBConnection $connection
  68. * @param AddressHandler $addressHandler
  69. * @param Notifications $notifications
  70. * @param TokenHandler $tokenHandler
  71. * @param IL10N $l10n
  72. * @param ILogger $logger
  73. * @param IRootFolder $rootFolder
  74. * @param IConfig $config
  75. * @param IUserManager $userManager
  76. */
  77. public function __construct(
  78. IDBConnection $connection,
  79. AddressHandler $addressHandler,
  80. Notifications $notifications,
  81. TokenHandler $tokenHandler,
  82. IL10N $l10n,
  83. ILogger $logger,
  84. IRootFolder $rootFolder,
  85. IConfig $config,
  86. IUserManager $userManager
  87. ) {
  88. $this->dbConnection = $connection;
  89. $this->addressHandler = $addressHandler;
  90. $this->notifications = $notifications;
  91. $this->tokenHandler = $tokenHandler;
  92. $this->l = $l10n;
  93. $this->logger = $logger;
  94. $this->rootFolder = $rootFolder;
  95. $this->config = $config;
  96. $this->userManager = $userManager;
  97. }
  98. /**
  99. * Return the identifier of this provider.
  100. *
  101. * @return string Containing only [a-zA-Z0-9]
  102. */
  103. public function identifier() {
  104. return 'ocFederatedSharing';
  105. }
  106. /**
  107. * Share a path
  108. *
  109. * @param IShare $share
  110. * @return IShare The share object
  111. * @throws ShareNotFound
  112. * @throws \Exception
  113. */
  114. public function create(IShare $share) {
  115. $shareWith = $share->getSharedWith();
  116. $itemSource = $share->getNodeId();
  117. $itemType = $share->getNodeType();
  118. $permissions = $share->getPermissions();
  119. $sharedBy = $share->getSharedBy();
  120. /*
  121. * Check if file is not already shared with the remote user
  122. */
  123. $alreadyShared = $this->getSharedWith($shareWith, self::SHARE_TYPE_REMOTE, $share->getNode(), 1, 0);
  124. if (!empty($alreadyShared)) {
  125. $message = 'Sharing %s failed, because this item is already shared with %s';
  126. $message_t = $this->l->t('Sharing %s failed, because this item is already shared with %s', array($share->getNode()->getName(), $shareWith));
  127. $this->logger->debug(sprintf($message, $share->getNode()->getName(), $shareWith), ['app' => 'Federated File Sharing']);
  128. throw new \Exception($message_t);
  129. }
  130. // don't allow federated shares if source and target server are the same
  131. list($user, $remote) = $this->addressHandler->splitUserRemote($shareWith);
  132. $currentServer = $this->addressHandler->generateRemoteURL();
  133. $currentUser = $sharedBy;
  134. if ($this->addressHandler->compareAddresses($user, $remote, $currentUser, $currentServer)) {
  135. $message = 'Not allowed to create a federated share with the same user.';
  136. $message_t = $this->l->t('Not allowed to create a federated share with the same user');
  137. $this->logger->debug($message, ['app' => 'Federated File Sharing']);
  138. throw new \Exception($message_t);
  139. }
  140. $share->setSharedWith($user . '@' . $remote);
  141. try {
  142. $remoteShare = $this->getShareFromExternalShareTable($share);
  143. } catch (ShareNotFound $e) {
  144. $remoteShare = null;
  145. }
  146. if ($remoteShare) {
  147. try {
  148. $uidOwner = $remoteShare['owner'] . '@' . $remoteShare['remote'];
  149. $shareId = $this->addShareToDB($itemSource, $itemType, $shareWith, $sharedBy, $uidOwner, $permissions, 'tmp_token_' . time());
  150. $share->setId($shareId);
  151. list($token, $remoteId) = $this->askOwnerToReShare($shareWith, $share, $shareId);
  152. // remote share was create successfully if we get a valid token as return
  153. $send = is_string($token) && $token !== '';
  154. } catch (\Exception $e) {
  155. // fall back to old re-share behavior if the remote server
  156. // doesn't support flat re-shares (was introduced with Nextcloud 9.1)
  157. $this->removeShareFromTable($share);
  158. $shareId = $this->createFederatedShare($share);
  159. }
  160. if ($send) {
  161. $this->updateSuccessfulReshare($shareId, $token);
  162. $this->storeRemoteId($shareId, $remoteId);
  163. } else {
  164. $this->removeShareFromTable($share);
  165. $message_t = $this->l->t('File is already shared with %s', [$shareWith]);
  166. throw new \Exception($message_t);
  167. }
  168. } else {
  169. $shareId = $this->createFederatedShare($share);
  170. }
  171. $data = $this->getRawShare($shareId);
  172. return $this->createShareObject($data);
  173. }
  174. /**
  175. * create federated share and inform the recipient
  176. *
  177. * @param IShare $share
  178. * @return int
  179. * @throws ShareNotFound
  180. * @throws \Exception
  181. */
  182. protected function createFederatedShare(IShare $share) {
  183. $token = $this->tokenHandler->generateToken();
  184. $shareId = $this->addShareToDB(
  185. $share->getNodeId(),
  186. $share->getNodeType(),
  187. $share->getSharedWith(),
  188. $share->getSharedBy(),
  189. $share->getShareOwner(),
  190. $share->getPermissions(),
  191. $token
  192. );
  193. $sharedByFederatedId = $share->getSharedBy();
  194. if ($this->userManager->userExists($sharedByFederatedId)) {
  195. $sharedByFederatedId = $sharedByFederatedId . '@' . $this->addressHandler->generateRemoteURL();
  196. }
  197. $send = $this->notifications->sendRemoteShare(
  198. $token,
  199. $share->getSharedWith(),
  200. $share->getNode()->getName(),
  201. $shareId,
  202. $share->getShareOwner(),
  203. $share->getShareOwner() . '@' . $this->addressHandler->generateRemoteURL(),
  204. $share->getSharedBy(),
  205. $sharedByFederatedId
  206. );
  207. if ($send === false) {
  208. $data = $this->getRawShare($shareId);
  209. $share = $this->createShareObject($data);
  210. $this->removeShareFromTable($share);
  211. $message_t = $this->l->t('Sharing %s failed, could not find %s, maybe the server is currently unreachable.',
  212. [$share->getNode()->getName(), $share->getSharedWith()]);
  213. throw new \Exception($message_t);
  214. }
  215. return $shareId;
  216. }
  217. /**
  218. * @param string $shareWith
  219. * @param IShare $share
  220. * @param string $shareId internal share Id
  221. * @return array
  222. * @throws \Exception
  223. */
  224. protected function askOwnerToReShare($shareWith, IShare $share, $shareId) {
  225. $remoteShare = $this->getShareFromExternalShareTable($share);
  226. $token = $remoteShare['share_token'];
  227. $remoteId = $remoteShare['remote_id'];
  228. $remote = $remoteShare['remote'];
  229. list($token, $remoteId) = $this->notifications->requestReShare(
  230. $token,
  231. $remoteId,
  232. $shareId,
  233. $remote,
  234. $shareWith,
  235. $share->getPermissions()
  236. );
  237. return [$token, $remoteId];
  238. }
  239. /**
  240. * get federated share from the share_external table but exclude mounted link shares
  241. *
  242. * @param IShare $share
  243. * @return array
  244. * @throws ShareNotFound
  245. */
  246. protected function getShareFromExternalShareTable(IShare $share) {
  247. $query = $this->dbConnection->getQueryBuilder();
  248. $query->select('*')->from($this->externalShareTable)
  249. ->where($query->expr()->eq('user', $query->createNamedParameter($share->getShareOwner())))
  250. ->andWhere($query->expr()->eq('mountpoint', $query->createNamedParameter($share->getTarget())));
  251. $result = $query->execute()->fetchAll();
  252. if (isset($result[0]) && (int)$result[0]['remote_id'] > 0) {
  253. return $result[0];
  254. }
  255. throw new ShareNotFound('share not found in share_external table');
  256. }
  257. /**
  258. * add share to the database and return the ID
  259. *
  260. * @param int $itemSource
  261. * @param string $itemType
  262. * @param string $shareWith
  263. * @param string $sharedBy
  264. * @param string $uidOwner
  265. * @param int $permissions
  266. * @param string $token
  267. * @return int
  268. */
  269. private function addShareToDB($itemSource, $itemType, $shareWith, $sharedBy, $uidOwner, $permissions, $token) {
  270. $qb = $this->dbConnection->getQueryBuilder();
  271. $qb->insert('share')
  272. ->setValue('share_type', $qb->createNamedParameter(self::SHARE_TYPE_REMOTE))
  273. ->setValue('item_type', $qb->createNamedParameter($itemType))
  274. ->setValue('item_source', $qb->createNamedParameter($itemSource))
  275. ->setValue('file_source', $qb->createNamedParameter($itemSource))
  276. ->setValue('share_with', $qb->createNamedParameter($shareWith))
  277. ->setValue('uid_owner', $qb->createNamedParameter($uidOwner))
  278. ->setValue('uid_initiator', $qb->createNamedParameter($sharedBy))
  279. ->setValue('permissions', $qb->createNamedParameter($permissions))
  280. ->setValue('token', $qb->createNamedParameter($token))
  281. ->setValue('stime', $qb->createNamedParameter(time()));
  282. /*
  283. * Added to fix https://github.com/owncloud/core/issues/22215
  284. * Can be removed once we get rid of ajax/share.php
  285. */
  286. $qb->setValue('file_target', $qb->createNamedParameter(''));
  287. $qb->execute();
  288. $id = $qb->getLastInsertId();
  289. return (int)$id;
  290. }
  291. /**
  292. * Update a share
  293. *
  294. * @param IShare $share
  295. * @return IShare The share object
  296. */
  297. public function update(IShare $share) {
  298. /*
  299. * We allow updating the permissions of federated shares
  300. */
  301. $qb = $this->dbConnection->getQueryBuilder();
  302. $qb->update('share')
  303. ->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())))
  304. ->set('permissions', $qb->createNamedParameter($share->getPermissions()))
  305. ->set('uid_owner', $qb->createNamedParameter($share->getShareOwner()))
  306. ->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()))
  307. ->execute();
  308. // send the updated permission to the owner/initiator, if they are not the same
  309. if ($share->getShareOwner() !== $share->getSharedBy()) {
  310. $this->sendPermissionUpdate($share);
  311. }
  312. return $share;
  313. }
  314. /**
  315. * send the updated permission to the owner/initiator, if they are not the same
  316. *
  317. * @param IShare $share
  318. * @throws ShareNotFound
  319. * @throws \OC\HintException
  320. */
  321. protected function sendPermissionUpdate(IShare $share) {
  322. $remoteId = $this->getRemoteId($share);
  323. // if the local user is the owner we send the permission change to the initiator
  324. if ($this->userManager->userExists($share->getShareOwner())) {
  325. list(, $remote) = $this->addressHandler->splitUserRemote($share->getSharedBy());
  326. } else { // ... if not we send the permission change to the owner
  327. list(, $remote) = $this->addressHandler->splitUserRemote($share->getShareOwner());
  328. }
  329. $this->notifications->sendPermissionChange($remote, $remoteId, $share->getToken(), $share->getPermissions());
  330. }
  331. /**
  332. * update successful reShare with the correct token
  333. *
  334. * @param int $shareId
  335. * @param string $token
  336. */
  337. protected function updateSuccessfulReShare($shareId, $token) {
  338. $query = $this->dbConnection->getQueryBuilder();
  339. $query->update('share')
  340. ->where($query->expr()->eq('id', $query->createNamedParameter($shareId)))
  341. ->set('token', $query->createNamedParameter($token))
  342. ->execute();
  343. }
  344. /**
  345. * store remote ID in federated reShare table
  346. *
  347. * @param $shareId
  348. * @param $remoteId
  349. */
  350. public function storeRemoteId($shareId, $remoteId) {
  351. $query = $this->dbConnection->getQueryBuilder();
  352. $query->insert('federated_reshares')
  353. ->values(
  354. [
  355. 'share_id' => $query->createNamedParameter($shareId),
  356. 'remote_id' => $query->createNamedParameter($remoteId),
  357. ]
  358. );
  359. $query->execute();
  360. }
  361. /**
  362. * get share ID on remote server for federated re-shares
  363. *
  364. * @param IShare $share
  365. * @return int
  366. * @throws ShareNotFound
  367. */
  368. public function getRemoteId(IShare $share) {
  369. $query = $this->dbConnection->getQueryBuilder();
  370. $query->select('remote_id')->from('federated_reshares')
  371. ->where($query->expr()->eq('share_id', $query->createNamedParameter((int)$share->getId())));
  372. $data = $query->execute()->fetch();
  373. if (!is_array($data) || !isset($data['remote_id'])) {
  374. throw new ShareNotFound();
  375. }
  376. return (int)$data['remote_id'];
  377. }
  378. /**
  379. * @inheritdoc
  380. */
  381. public function move(IShare $share, $recipient) {
  382. /*
  383. * This function does nothing yet as it is just for outgoing
  384. * federated shares.
  385. */
  386. return $share;
  387. }
  388. /**
  389. * Get all children of this share
  390. *
  391. * @param IShare $parent
  392. * @return IShare[]
  393. */
  394. public function getChildren(IShare $parent) {
  395. $children = [];
  396. $qb = $this->dbConnection->getQueryBuilder();
  397. $qb->select('*')
  398. ->from('share')
  399. ->where($qb->expr()->eq('parent', $qb->createNamedParameter($parent->getId())))
  400. ->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_REMOTE)))
  401. ->orderBy('id');
  402. $cursor = $qb->execute();
  403. while($data = $cursor->fetch()) {
  404. $children[] = $this->createShareObject($data);
  405. }
  406. $cursor->closeCursor();
  407. return $children;
  408. }
  409. /**
  410. * Delete a share (owner unShares the file)
  411. *
  412. * @param IShare $share
  413. */
  414. public function delete(IShare $share) {
  415. list(, $remote) = $this->addressHandler->splitUserRemote($share->getSharedWith());
  416. $isOwner = false;
  417. $this->removeShareFromTable($share);
  418. // if the local user is the owner we can send the unShare request directly...
  419. if ($this->userManager->userExists($share->getShareOwner())) {
  420. $this->notifications->sendRemoteUnShare($remote, $share->getId(), $share->getToken());
  421. $this->revokeShare($share, true);
  422. $isOwner = true;
  423. } else { // ... if not we need to correct ID for the unShare request
  424. $remoteId = $this->getRemoteId($share);
  425. $this->notifications->sendRemoteUnShare($remote, $remoteId, $share->getToken());
  426. $this->revokeShare($share, false);
  427. }
  428. // send revoke notification to the other user, if initiator and owner are not the same user
  429. if ($share->getShareOwner() !== $share->getSharedBy()) {
  430. $remoteId = $this->getRemoteId($share);
  431. if ($isOwner) {
  432. list(, $remote) = $this->addressHandler->splitUserRemote($share->getSharedBy());
  433. } else {
  434. list(, $remote) = $this->addressHandler->splitUserRemote($share->getShareOwner());
  435. }
  436. $this->notifications->sendRevokeShare($remote, $remoteId, $share->getToken());
  437. }
  438. }
  439. /**
  440. * in case of a re-share we need to send the other use (initiator or owner)
  441. * a message that the file was unshared
  442. *
  443. * @param IShare $share
  444. * @param bool $isOwner the user can either be the owner or the user who re-sahred it
  445. * @throws ShareNotFound
  446. * @throws \OC\HintException
  447. */
  448. protected function revokeShare($share, $isOwner) {
  449. // also send a unShare request to the initiator, if this is a different user than the owner
  450. if ($share->getShareOwner() !== $share->getSharedBy()) {
  451. if ($isOwner) {
  452. list(, $remote) = $this->addressHandler->splitUserRemote($share->getSharedBy());
  453. } else {
  454. list(, $remote) = $this->addressHandler->splitUserRemote($share->getShareOwner());
  455. }
  456. $remoteId = $this->getRemoteId($share);
  457. $this->notifications->sendRevokeShare($remote, $remoteId, $share->getToken());
  458. }
  459. }
  460. /**
  461. * remove share from table
  462. *
  463. * @param IShare $share
  464. */
  465. public function removeShareFromTable(IShare $share) {
  466. $qb = $this->dbConnection->getQueryBuilder();
  467. $qb->delete('share')
  468. ->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())));
  469. $qb->execute();
  470. $qb->delete('federated_reshares')
  471. ->where($qb->expr()->eq('share_id', $qb->createNamedParameter($share->getId())));
  472. $qb->execute();
  473. }
  474. /**
  475. * @inheritdoc
  476. */
  477. public function deleteFromSelf(IShare $share, $recipient) {
  478. // nothing to do here. Technically deleteFromSelf in the context of federated
  479. // shares is a umount of a external storage. This is handled here
  480. // apps/files_sharing/lib/external/manager.php
  481. // TODO move this code over to this app
  482. return;
  483. }
  484. /**
  485. * @inheritdoc
  486. */
  487. public function getSharesBy($userId, $shareType, $node, $reshares, $limit, $offset) {
  488. $qb = $this->dbConnection->getQueryBuilder();
  489. $qb->select('*')
  490. ->from('share');
  491. $qb->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_REMOTE)));
  492. /**
  493. * Reshares for this user are shares where they are the owner.
  494. */
  495. if ($reshares === false) {
  496. //Special case for old shares created via the web UI
  497. $or1 = $qb->expr()->andX(
  498. $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)),
  499. $qb->expr()->isNull('uid_initiator')
  500. );
  501. $qb->andWhere(
  502. $qb->expr()->orX(
  503. $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)),
  504. $or1
  505. )
  506. );
  507. } else {
  508. $qb->andWhere(
  509. $qb->expr()->orX(
  510. $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)),
  511. $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId))
  512. )
  513. );
  514. }
  515. if ($node !== null) {
  516. $qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId())));
  517. }
  518. if ($limit !== -1) {
  519. $qb->setMaxResults($limit);
  520. }
  521. $qb->setFirstResult($offset);
  522. $qb->orderBy('id');
  523. $cursor = $qb->execute();
  524. $shares = [];
  525. while($data = $cursor->fetch()) {
  526. $shares[] = $this->createShareObject($data);
  527. }
  528. $cursor->closeCursor();
  529. return $shares;
  530. }
  531. /**
  532. * @inheritdoc
  533. */
  534. public function getShareById($id, $recipientId = null) {
  535. $qb = $this->dbConnection->getQueryBuilder();
  536. $qb->select('*')
  537. ->from('share')
  538. ->where($qb->expr()->eq('id', $qb->createNamedParameter($id)))
  539. ->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_REMOTE)));
  540. $cursor = $qb->execute();
  541. $data = $cursor->fetch();
  542. $cursor->closeCursor();
  543. if ($data === false) {
  544. throw new ShareNotFound();
  545. }
  546. try {
  547. $share = $this->createShareObject($data);
  548. } catch (InvalidShare $e) {
  549. throw new ShareNotFound();
  550. }
  551. return $share;
  552. }
  553. /**
  554. * Get shares for a given path
  555. *
  556. * @param \OCP\Files\Node $path
  557. * @return IShare[]
  558. */
  559. public function getSharesByPath(Node $path) {
  560. $qb = $this->dbConnection->getQueryBuilder();
  561. $cursor = $qb->select('*')
  562. ->from('share')
  563. ->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($path->getId())))
  564. ->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_REMOTE)))
  565. ->execute();
  566. $shares = [];
  567. while($data = $cursor->fetch()) {
  568. $shares[] = $this->createShareObject($data);
  569. }
  570. $cursor->closeCursor();
  571. return $shares;
  572. }
  573. /**
  574. * @inheritdoc
  575. */
  576. public function getSharedWith($userId, $shareType, $node, $limit, $offset) {
  577. /** @var IShare[] $shares */
  578. $shares = [];
  579. //Get shares directly with this user
  580. $qb = $this->dbConnection->getQueryBuilder();
  581. $qb->select('*')
  582. ->from('share');
  583. // Order by id
  584. $qb->orderBy('id');
  585. // Set limit and offset
  586. if ($limit !== -1) {
  587. $qb->setMaxResults($limit);
  588. }
  589. $qb->setFirstResult($offset);
  590. $qb->where($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_REMOTE)));
  591. $qb->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($userId)));
  592. // Filter by node if provided
  593. if ($node !== null) {
  594. $qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId())));
  595. }
  596. $cursor = $qb->execute();
  597. while($data = $cursor->fetch()) {
  598. $shares[] = $this->createShareObject($data);
  599. }
  600. $cursor->closeCursor();
  601. return $shares;
  602. }
  603. /**
  604. * Get a share by token
  605. *
  606. * @param string $token
  607. * @return IShare
  608. * @throws ShareNotFound
  609. */
  610. public function getShareByToken($token) {
  611. $qb = $this->dbConnection->getQueryBuilder();
  612. $cursor = $qb->select('*')
  613. ->from('share')
  614. ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_REMOTE)))
  615. ->andWhere($qb->expr()->eq('token', $qb->createNamedParameter($token)))
  616. ->execute();
  617. $data = $cursor->fetch();
  618. if ($data === false) {
  619. throw new ShareNotFound();
  620. }
  621. try {
  622. $share = $this->createShareObject($data);
  623. } catch (InvalidShare $e) {
  624. throw new ShareNotFound();
  625. }
  626. return $share;
  627. }
  628. /**
  629. * get database row of a give share
  630. *
  631. * @param $id
  632. * @return array
  633. * @throws ShareNotFound
  634. */
  635. private function getRawShare($id) {
  636. // Now fetch the inserted share and create a complete share object
  637. $qb = $this->dbConnection->getQueryBuilder();
  638. $qb->select('*')
  639. ->from('share')
  640. ->where($qb->expr()->eq('id', $qb->createNamedParameter($id)));
  641. $cursor = $qb->execute();
  642. $data = $cursor->fetch();
  643. $cursor->closeCursor();
  644. if ($data === false) {
  645. throw new ShareNotFound;
  646. }
  647. return $data;
  648. }
  649. /**
  650. * Create a share object from an database row
  651. *
  652. * @param array $data
  653. * @return IShare
  654. * @throws InvalidShare
  655. * @throws ShareNotFound
  656. */
  657. private function createShareObject($data) {
  658. $share = new Share($this->rootFolder, $this->userManager);
  659. $share->setId((int)$data['id'])
  660. ->setShareType((int)$data['share_type'])
  661. ->setPermissions((int)$data['permissions'])
  662. ->setTarget($data['file_target'])
  663. ->setMailSend((bool)$data['mail_send'])
  664. ->setToken($data['token']);
  665. $shareTime = new \DateTime();
  666. $shareTime->setTimestamp((int)$data['stime']);
  667. $share->setShareTime($shareTime);
  668. $share->setSharedWith($data['share_with']);
  669. if ($data['uid_initiator'] !== null) {
  670. $share->setShareOwner($data['uid_owner']);
  671. $share->setSharedBy($data['uid_initiator']);
  672. } else {
  673. //OLD SHARE
  674. $share->setSharedBy($data['uid_owner']);
  675. $path = $this->getNode($share->getSharedBy(), (int)$data['file_source']);
  676. $owner = $path->getOwner();
  677. $share->setShareOwner($owner->getUID());
  678. }
  679. $share->setNodeId((int)$data['file_source']);
  680. $share->setNodeType($data['item_type']);
  681. $share->setProviderId($this->identifier());
  682. return $share;
  683. }
  684. /**
  685. * Get the node with file $id for $user
  686. *
  687. * @param string $userId
  688. * @param int $id
  689. * @return \OCP\Files\File|\OCP\Files\Folder
  690. * @throws InvalidShare
  691. */
  692. private function getNode($userId, $id) {
  693. try {
  694. $userFolder = $this->rootFolder->getUserFolder($userId);
  695. } catch (NotFoundException $e) {
  696. throw new InvalidShare();
  697. }
  698. $nodes = $userFolder->getById($id);
  699. if (empty($nodes)) {
  700. throw new InvalidShare();
  701. }
  702. return $nodes[0];
  703. }
  704. /**
  705. * A user is deleted from the system
  706. * So clean up the relevant shares.
  707. *
  708. * @param string $uid
  709. * @param int $shareType
  710. */
  711. public function userDeleted($uid, $shareType) {
  712. //TODO: probabaly a good idea to send unshare info to remote servers
  713. $qb = $this->dbConnection->getQueryBuilder();
  714. $qb->delete('share')
  715. ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_REMOTE)))
  716. ->andWhere($qb->expr()->eq('uid_owner', $qb->createNamedParameter($uid)))
  717. ->execute();
  718. }
  719. /**
  720. * This provider does not handle groups
  721. *
  722. * @param string $gid
  723. */
  724. public function groupDeleted($gid) {
  725. // We don't handle groups here
  726. return;
  727. }
  728. /**
  729. * This provider does not handle groups
  730. *
  731. * @param string $uid
  732. * @param string $gid
  733. */
  734. public function userDeletedFromGroup($uid, $gid) {
  735. // We don't handle groups here
  736. return;
  737. }
  738. /**
  739. * check if users from other Nextcloud instances are allowed to mount public links share by this instance
  740. *
  741. * @return bool
  742. */
  743. public function isOutgoingServer2serverShareEnabled() {
  744. $result = $this->config->getAppValue('files_sharing', 'outgoing_server2server_share_enabled', 'yes');
  745. return ($result === 'yes') ? true : false;
  746. }
  747. /**
  748. * check if users are allowed to mount public links from other ownClouds
  749. *
  750. * @return bool
  751. */
  752. public function isIncomingServer2serverShareEnabled() {
  753. $result = $this->config->getAppValue('files_sharing', 'incoming_server2server_share_enabled', 'yes');
  754. return ($result === 'yes') ? true : false;
  755. }
  756. }