FederatedShareProvider.php 26 KB

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