DefaultShareProvider.php 33 KB

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