FederatedShareProvider.php 27 KB

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