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

DefaultShareProvider.php 43KB

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