DefaultShareProvider.php 52 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660
  1. <?php
  2. /**
  3. * @copyright Copyright (c) 2016, ownCloud, Inc.
  4. *
  5. * @author Arthur Schiwon <blizzz@arthur-schiwon.de>
  6. * @author Bjoern Schiessle <bjoern@schiessle.org>
  7. * @author Björn Schießle <bjoern@schiessle.org>
  8. * @author Christoph Wurst <christoph@winzerhof-wurst.at>
  9. * @author Daniel Calviño Sánchez <danxuliu@gmail.com>
  10. * @author Jan-Philipp Litza <jplitza@users.noreply.github.com>
  11. * @author Joas Schilling <coding@schilljs.com>
  12. * @author Julius Härtl <jus@bitgrid.net>
  13. * @author Lukas Reschke <lukas@statuscode.ch>
  14. * @author Maxence Lange <maxence@artificial-owl.com>
  15. * @author phisch <git@philippschaffrath.de>
  16. * @author Robin Appelman <robin@icewind.nl>
  17. * @author Roeland Jago Douma <roeland@famdouma.nl>
  18. * @author Vincent Petry <vincent@nextcloud.com>
  19. *
  20. * @license AGPL-3.0
  21. *
  22. * This code is free software: you can redistribute it and/or modify
  23. * it under the terms of the GNU Affero General Public License, version 3,
  24. * as published by the Free Software Foundation.
  25. *
  26. * This program is distributed in the hope that it will be useful,
  27. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  28. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  29. * GNU Affero General Public License for more details.
  30. *
  31. * You should have received a copy of the GNU Affero General Public License, version 3,
  32. * along with this program. If not, see <http://www.gnu.org/licenses/>
  33. *
  34. */
  35. namespace OC\Share20;
  36. use OC\Files\Cache\Cache;
  37. use OC\Share20\Exception\BackendError;
  38. use OC\Share20\Exception\InvalidShare;
  39. use OC\Share20\Exception\ProviderException;
  40. use OCP\AppFramework\Utility\ITimeFactory;
  41. use OCP\DB\QueryBuilder\IQueryBuilder;
  42. use OCP\Defaults;
  43. use OCP\Files\Folder;
  44. use OCP\Files\IRootFolder;
  45. use OCP\Files\Node;
  46. use OCP\IDBConnection;
  47. use OCP\IGroupManager;
  48. use OCP\IURLGenerator;
  49. use OCP\IUser;
  50. use OCP\IUserManager;
  51. use OCP\L10N\IFactory;
  52. use OCP\Mail\IMailer;
  53. use OCP\Share\Exceptions\ShareNotFound;
  54. use OCP\Share\IAttributes;
  55. use OCP\Share\IManager;
  56. use OCP\Share\IShare;
  57. use OCP\Share\IShareProvider;
  58. use function str_starts_with;
  59. /**
  60. * Class DefaultShareProvider
  61. *
  62. * @package OC\Share20
  63. */
  64. class DefaultShareProvider implements IShareProvider {
  65. // Special share type for user modified group shares
  66. public const SHARE_TYPE_USERGROUP = 2;
  67. /** @var IDBConnection */
  68. private $dbConn;
  69. /** @var IUserManager */
  70. private $userManager;
  71. /** @var IGroupManager */
  72. private $groupManager;
  73. /** @var IRootFolder */
  74. private $rootFolder;
  75. /** @var IMailer */
  76. private $mailer;
  77. /** @var Defaults */
  78. private $defaults;
  79. /** @var IFactory */
  80. private $l10nFactory;
  81. /** @var IURLGenerator */
  82. private $urlGenerator;
  83. private ITimeFactory $timeFactory;
  84. public function __construct(
  85. IDBConnection $connection,
  86. IUserManager $userManager,
  87. IGroupManager $groupManager,
  88. IRootFolder $rootFolder,
  89. IMailer $mailer,
  90. Defaults $defaults,
  91. IFactory $l10nFactory,
  92. IURLGenerator $urlGenerator,
  93. ITimeFactory $timeFactory,
  94. private IManager $shareManager,
  95. ) {
  96. $this->dbConn = $connection;
  97. $this->userManager = $userManager;
  98. $this->groupManager = $groupManager;
  99. $this->rootFolder = $rootFolder;
  100. $this->mailer = $mailer;
  101. $this->defaults = $defaults;
  102. $this->l10nFactory = $l10nFactory;
  103. $this->urlGenerator = $urlGenerator;
  104. $this->timeFactory = $timeFactory;
  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. $expirationDate = $share->getExpirationDate();
  127. if ($expirationDate !== null) {
  128. $expirationDate = clone $expirationDate;
  129. $expirationDate->setTimezone(new \DateTimeZone(date_default_timezone_get()));
  130. }
  131. if ($share->getShareType() === IShare::TYPE_USER) {
  132. //Set the UID of the user we share with
  133. $qb->setValue('share_with', $qb->createNamedParameter($share->getSharedWith()));
  134. $qb->setValue('accepted', $qb->createNamedParameter(IShare::STATUS_PENDING));
  135. //If an expiration date is set store it
  136. if ($expirationDate !== null) {
  137. $qb->setValue('expiration', $qb->createNamedParameter($expirationDate, 'datetime'));
  138. }
  139. } elseif ($share->getShareType() === IShare::TYPE_GROUP) {
  140. //Set the GID of the group we share with
  141. $qb->setValue('share_with', $qb->createNamedParameter($share->getSharedWith()));
  142. //If an expiration date is set store it
  143. if ($expirationDate !== null) {
  144. $qb->setValue('expiration', $qb->createNamedParameter($expirationDate, 'datetime'));
  145. }
  146. } elseif ($share->getShareType() === IShare::TYPE_LINK) {
  147. //set label for public link
  148. $qb->setValue('label', $qb->createNamedParameter($share->getLabel()));
  149. //Set the token of the share
  150. $qb->setValue('token', $qb->createNamedParameter($share->getToken()));
  151. //If a password is set store it
  152. if ($share->getPassword() !== null) {
  153. $qb->setValue('password', $qb->createNamedParameter($share->getPassword()));
  154. }
  155. $qb->setValue('password_by_talk', $qb->createNamedParameter($share->getSendPasswordByTalk(), IQueryBuilder::PARAM_BOOL));
  156. //If an expiration date is set store it
  157. if ($expirationDate !== null) {
  158. $qb->setValue('expiration', $qb->createNamedParameter($expirationDate, 'datetime'));
  159. }
  160. if (method_exists($share, 'getParent')) {
  161. $qb->setValue('parent', $qb->createNamedParameter($share->getParent()));
  162. }
  163. $qb->setValue('hide_download', $qb->createNamedParameter($share->getHideDownload() ? 1 : 0, IQueryBuilder::PARAM_INT));
  164. } else {
  165. throw new \Exception('invalid share type!');
  166. }
  167. // Set what is shares
  168. $qb->setValue('item_type', $qb->createParameter('itemType'));
  169. if ($share->getNode() instanceof \OCP\Files\File) {
  170. $qb->setParameter('itemType', 'file');
  171. } else {
  172. $qb->setParameter('itemType', 'folder');
  173. }
  174. // Set the file id
  175. $qb->setValue('item_source', $qb->createNamedParameter($share->getNode()->getId()));
  176. $qb->setValue('file_source', $qb->createNamedParameter($share->getNode()->getId()));
  177. // set the permissions
  178. $qb->setValue('permissions', $qb->createNamedParameter($share->getPermissions()));
  179. // set share attributes
  180. $shareAttributes = $this->formatShareAttributes(
  181. $share->getAttributes()
  182. );
  183. $qb->setValue('attributes', $qb->createNamedParameter($shareAttributes));
  184. // Set who created this share
  185. $qb->setValue('uid_initiator', $qb->createNamedParameter($share->getSharedBy()));
  186. // Set who is the owner of this file/folder (and this the owner of the share)
  187. $qb->setValue('uid_owner', $qb->createNamedParameter($share->getShareOwner()));
  188. // Set the file target
  189. $qb->setValue('file_target', $qb->createNamedParameter($share->getTarget()));
  190. if ($share->getNote() !== '') {
  191. $qb->setValue('note', $qb->createNamedParameter($share->getNote()));
  192. }
  193. // Set the time this share was created
  194. $shareTime = $this->timeFactory->now();
  195. $qb->setValue('stime', $qb->createNamedParameter($shareTime->getTimestamp()));
  196. // insert the data and fetch the id of the share
  197. $qb->executeStatement();
  198. // Update mandatory data
  199. $id = $qb->getLastInsertId();
  200. $share->setId((string)$id);
  201. $share->setProviderId($this->identifier());
  202. $share->setShareTime(\DateTime::createFromImmutable($shareTime));
  203. $mailSendValue = $share->getMailSend();
  204. $share->setMailSend(($mailSendValue === null) ? true : $mailSendValue);
  205. return $share;
  206. }
  207. /**
  208. * Update a share
  209. *
  210. * @param \OCP\Share\IShare $share
  211. * @return \OCP\Share\IShare The share object
  212. * @throws ShareNotFound
  213. * @throws \OCP\Files\InvalidPathException
  214. * @throws \OCP\Files\NotFoundException
  215. */
  216. public function update(\OCP\Share\IShare $share) {
  217. $originalShare = $this->getShareById($share->getId());
  218. $shareAttributes = $this->formatShareAttributes($share->getAttributes());
  219. $expirationDate = $share->getExpirationDate();
  220. if ($expirationDate !== null) {
  221. $expirationDate = clone $expirationDate;
  222. $expirationDate->setTimezone(new \DateTimeZone(date_default_timezone_get()));
  223. }
  224. if ($share->getShareType() === IShare::TYPE_USER) {
  225. /*
  226. * We allow updating the recipient on user shares.
  227. */
  228. $qb = $this->dbConn->getQueryBuilder();
  229. $qb->update('share')
  230. ->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())))
  231. ->set('share_with', $qb->createNamedParameter($share->getSharedWith()))
  232. ->set('uid_owner', $qb->createNamedParameter($share->getShareOwner()))
  233. ->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()))
  234. ->set('permissions', $qb->createNamedParameter($share->getPermissions()))
  235. ->set('attributes', $qb->createNamedParameter($shareAttributes))
  236. ->set('item_source', $qb->createNamedParameter($share->getNode()->getId()))
  237. ->set('file_source', $qb->createNamedParameter($share->getNode()->getId()))
  238. ->set('expiration', $qb->createNamedParameter($expirationDate, IQueryBuilder::PARAM_DATE))
  239. ->set('note', $qb->createNamedParameter($share->getNote()))
  240. ->set('accepted', $qb->createNamedParameter($share->getStatus()))
  241. ->execute();
  242. } elseif ($share->getShareType() === IShare::TYPE_GROUP) {
  243. $qb = $this->dbConn->getQueryBuilder();
  244. $qb->update('share')
  245. ->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())))
  246. ->set('uid_owner', $qb->createNamedParameter($share->getShareOwner()))
  247. ->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()))
  248. ->set('permissions', $qb->createNamedParameter($share->getPermissions()))
  249. ->set('attributes', $qb->createNamedParameter($shareAttributes))
  250. ->set('item_source', $qb->createNamedParameter($share->getNode()->getId()))
  251. ->set('file_source', $qb->createNamedParameter($share->getNode()->getId()))
  252. ->set('expiration', $qb->createNamedParameter($expirationDate, IQueryBuilder::PARAM_DATE))
  253. ->set('note', $qb->createNamedParameter($share->getNote()))
  254. ->execute();
  255. /*
  256. * Update all user defined group shares
  257. */
  258. $qb = $this->dbConn->getQueryBuilder();
  259. $qb->update('share')
  260. ->where($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId())))
  261. ->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USERGROUP)))
  262. ->set('uid_owner', $qb->createNamedParameter($share->getShareOwner()))
  263. ->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()))
  264. ->set('item_source', $qb->createNamedParameter($share->getNode()->getId()))
  265. ->set('file_source', $qb->createNamedParameter($share->getNode()->getId()))
  266. ->set('expiration', $qb->createNamedParameter($expirationDate, IQueryBuilder::PARAM_DATE))
  267. ->set('note', $qb->createNamedParameter($share->getNote()))
  268. ->execute();
  269. /*
  270. * Now update the permissions for all children that have not set it to 0
  271. */
  272. $qb = $this->dbConn->getQueryBuilder();
  273. $qb->update('share')
  274. ->where($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId())))
  275. ->andWhere($qb->expr()->neq('permissions', $qb->createNamedParameter(0)))
  276. ->set('permissions', $qb->createNamedParameter($share->getPermissions()))
  277. ->set('attributes', $qb->createNamedParameter($shareAttributes))
  278. ->execute();
  279. } elseif ($share->getShareType() === IShare::TYPE_LINK) {
  280. $qb = $this->dbConn->getQueryBuilder();
  281. $qb->update('share')
  282. ->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())))
  283. ->set('password', $qb->createNamedParameter($share->getPassword()))
  284. ->set('password_by_talk', $qb->createNamedParameter($share->getSendPasswordByTalk(), IQueryBuilder::PARAM_BOOL))
  285. ->set('uid_owner', $qb->createNamedParameter($share->getShareOwner()))
  286. ->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()))
  287. ->set('permissions', $qb->createNamedParameter($share->getPermissions()))
  288. ->set('attributes', $qb->createNamedParameter($shareAttributes))
  289. ->set('item_source', $qb->createNamedParameter($share->getNode()->getId()))
  290. ->set('file_source', $qb->createNamedParameter($share->getNode()->getId()))
  291. ->set('token', $qb->createNamedParameter($share->getToken()))
  292. ->set('expiration', $qb->createNamedParameter($expirationDate, IQueryBuilder::PARAM_DATE))
  293. ->set('note', $qb->createNamedParameter($share->getNote()))
  294. ->set('label', $qb->createNamedParameter($share->getLabel()))
  295. ->set('hide_download', $qb->createNamedParameter($share->getHideDownload() ? 1 : 0), IQueryBuilder::PARAM_INT)
  296. ->execute();
  297. }
  298. if ($originalShare->getNote() !== $share->getNote() && $share->getNote() !== '') {
  299. $this->propagateNote($share);
  300. }
  301. return $share;
  302. }
  303. /**
  304. * Accept a share.
  305. *
  306. * @param IShare $share
  307. * @param string $recipient
  308. * @return IShare The share object
  309. * @since 9.0.0
  310. */
  311. public function acceptShare(IShare $share, string $recipient): IShare {
  312. if ($share->getShareType() === IShare::TYPE_GROUP) {
  313. $group = $this->groupManager->get($share->getSharedWith());
  314. $user = $this->userManager->get($recipient);
  315. if (is_null($group)) {
  316. throw new ProviderException('Group "' . $share->getSharedWith() . '" does not exist');
  317. }
  318. if (!$group->inGroup($user)) {
  319. throw new ProviderException('Recipient not in receiving group');
  320. }
  321. // Try to fetch user specific share
  322. $qb = $this->dbConn->getQueryBuilder();
  323. $stmt = $qb->select('*')
  324. ->from('share')
  325. ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USERGROUP)))
  326. ->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($recipient)))
  327. ->andWhere($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId())))
  328. ->andWhere($qb->expr()->orX(
  329. $qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
  330. $qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
  331. ))
  332. ->execute();
  333. $data = $stmt->fetch();
  334. $stmt->closeCursor();
  335. /*
  336. * Check if there already is a user specific group share.
  337. * If there is update it (if required).
  338. */
  339. if ($data === false) {
  340. $id = $this->createUserSpecificGroupShare($share, $recipient);
  341. } else {
  342. $id = $data['id'];
  343. }
  344. } elseif ($share->getShareType() === IShare::TYPE_USER) {
  345. if ($share->getSharedWith() !== $recipient) {
  346. throw new ProviderException('Recipient does not match');
  347. }
  348. $id = $share->getId();
  349. } else {
  350. throw new ProviderException('Invalid shareType');
  351. }
  352. $qb = $this->dbConn->getQueryBuilder();
  353. $qb->update('share')
  354. ->set('accepted', $qb->createNamedParameter(IShare::STATUS_ACCEPTED))
  355. ->where($qb->expr()->eq('id', $qb->createNamedParameter($id)))
  356. ->execute();
  357. return $share;
  358. }
  359. /**
  360. * Get all children of this share
  361. * FIXME: remove once https://github.com/owncloud/core/pull/21660 is in
  362. *
  363. * @param \OCP\Share\IShare $parent
  364. * @return \OCP\Share\IShare[]
  365. */
  366. public function getChildren(\OCP\Share\IShare $parent) {
  367. $children = [];
  368. $qb = $this->dbConn->getQueryBuilder();
  369. $qb->select('*')
  370. ->from('share')
  371. ->where($qb->expr()->eq('parent', $qb->createNamedParameter($parent->getId())))
  372. ->andWhere(
  373. $qb->expr()->in(
  374. 'share_type',
  375. $qb->createNamedParameter([
  376. IShare::TYPE_USER,
  377. IShare::TYPE_GROUP,
  378. IShare::TYPE_LINK,
  379. ], IQueryBuilder::PARAM_INT_ARRAY)
  380. )
  381. )
  382. ->andWhere($qb->expr()->orX(
  383. $qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
  384. $qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
  385. ))
  386. ->orderBy('id');
  387. $cursor = $qb->execute();
  388. while ($data = $cursor->fetch()) {
  389. $children[] = $this->createShare($data);
  390. }
  391. $cursor->closeCursor();
  392. return $children;
  393. }
  394. /**
  395. * Delete a share
  396. *
  397. * @param \OCP\Share\IShare $share
  398. */
  399. public function delete(\OCP\Share\IShare $share) {
  400. $qb = $this->dbConn->getQueryBuilder();
  401. $qb->delete('share')
  402. ->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())));
  403. /*
  404. * If the share is a group share delete all possible
  405. * user defined groups shares.
  406. */
  407. if ($share->getShareType() === IShare::TYPE_GROUP) {
  408. $qb->orWhere($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId())));
  409. }
  410. $qb->execute();
  411. }
  412. /**
  413. * Unshare a share from the recipient. If this is a group share
  414. * this means we need a special entry in the share db.
  415. *
  416. * @param IShare $share
  417. * @param string $recipient UserId of recipient
  418. * @throws BackendError
  419. * @throws ProviderException
  420. */
  421. public function deleteFromSelf(IShare $share, $recipient) {
  422. if ($share->getShareType() === IShare::TYPE_GROUP) {
  423. $group = $this->groupManager->get($share->getSharedWith());
  424. $user = $this->userManager->get($recipient);
  425. if (is_null($group)) {
  426. throw new ProviderException('Group "' . $share->getSharedWith() . '" does not exist');
  427. }
  428. if (!$group->inGroup($user)) {
  429. // nothing left to do
  430. return;
  431. }
  432. // Try to fetch user specific share
  433. $qb = $this->dbConn->getQueryBuilder();
  434. $stmt = $qb->select('*')
  435. ->from('share')
  436. ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USERGROUP)))
  437. ->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($recipient)))
  438. ->andWhere($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId())))
  439. ->andWhere($qb->expr()->orX(
  440. $qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
  441. $qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
  442. ))
  443. ->execute();
  444. $data = $stmt->fetch();
  445. /*
  446. * Check if there already is a user specific group share.
  447. * If there is update it (if required).
  448. */
  449. if ($data === false) {
  450. $id = $this->createUserSpecificGroupShare($share, $recipient);
  451. $permissions = $share->getPermissions();
  452. } else {
  453. $permissions = $data['permissions'];
  454. $id = $data['id'];
  455. }
  456. if ($permissions !== 0) {
  457. // Update existing usergroup share
  458. $qb = $this->dbConn->getQueryBuilder();
  459. $qb->update('share')
  460. ->set('permissions', $qb->createNamedParameter(0))
  461. ->where($qb->expr()->eq('id', $qb->createNamedParameter($id)))
  462. ->execute();
  463. }
  464. } elseif ($share->getShareType() === IShare::TYPE_USER) {
  465. if ($share->getSharedWith() !== $recipient) {
  466. throw new ProviderException('Recipient does not match');
  467. }
  468. // We can just delete user and link shares
  469. $this->delete($share);
  470. } else {
  471. throw new ProviderException('Invalid shareType');
  472. }
  473. }
  474. protected function createUserSpecificGroupShare(IShare $share, string $recipient): int {
  475. $type = $share->getNodeType();
  476. $qb = $this->dbConn->getQueryBuilder();
  477. $qb->insert('share')
  478. ->values([
  479. 'share_type' => $qb->createNamedParameter(IShare::TYPE_USERGROUP),
  480. 'share_with' => $qb->createNamedParameter($recipient),
  481. 'uid_owner' => $qb->createNamedParameter($share->getShareOwner()),
  482. 'uid_initiator' => $qb->createNamedParameter($share->getSharedBy()),
  483. 'parent' => $qb->createNamedParameter($share->getId()),
  484. 'item_type' => $qb->createNamedParameter($type),
  485. 'item_source' => $qb->createNamedParameter($share->getNodeId()),
  486. 'file_source' => $qb->createNamedParameter($share->getNodeId()),
  487. 'file_target' => $qb->createNamedParameter($share->getTarget()),
  488. 'permissions' => $qb->createNamedParameter($share->getPermissions()),
  489. 'stime' => $qb->createNamedParameter($share->getShareTime()->getTimestamp()),
  490. ])->execute();
  491. return $qb->getLastInsertId();
  492. }
  493. /**
  494. * @inheritdoc
  495. *
  496. * For now this only works for group shares
  497. * If this gets implemented for normal shares we have to extend it
  498. */
  499. public function restore(IShare $share, string $recipient): IShare {
  500. $qb = $this->dbConn->getQueryBuilder();
  501. $qb->select('permissions')
  502. ->from('share')
  503. ->where(
  504. $qb->expr()->eq('id', $qb->createNamedParameter($share->getId()))
  505. );
  506. $cursor = $qb->execute();
  507. $data = $cursor->fetch();
  508. $cursor->closeCursor();
  509. $originalPermission = $data['permissions'];
  510. $qb = $this->dbConn->getQueryBuilder();
  511. $qb->update('share')
  512. ->set('permissions', $qb->createNamedParameter($originalPermission))
  513. ->where(
  514. $qb->expr()->eq('parent', $qb->createNamedParameter($share->getParent()))
  515. )->andWhere(
  516. $qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USERGROUP))
  517. )->andWhere(
  518. $qb->expr()->eq('share_with', $qb->createNamedParameter($recipient))
  519. );
  520. $qb->execute();
  521. return $this->getShareById($share->getId(), $recipient);
  522. }
  523. /**
  524. * @inheritdoc
  525. */
  526. public function move(\OCP\Share\IShare $share, $recipient) {
  527. if ($share->getShareType() === IShare::TYPE_USER) {
  528. // Just update the target
  529. $qb = $this->dbConn->getQueryBuilder();
  530. $qb->update('share')
  531. ->set('file_target', $qb->createNamedParameter($share->getTarget()))
  532. ->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())))
  533. ->execute();
  534. } elseif ($share->getShareType() === IShare::TYPE_GROUP) {
  535. // Check if there is a usergroup share
  536. $qb = $this->dbConn->getQueryBuilder();
  537. $stmt = $qb->select('id')
  538. ->from('share')
  539. ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USERGROUP)))
  540. ->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($recipient)))
  541. ->andWhere($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId())))
  542. ->andWhere($qb->expr()->orX(
  543. $qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
  544. $qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
  545. ))
  546. ->setMaxResults(1)
  547. ->execute();
  548. $data = $stmt->fetch();
  549. $stmt->closeCursor();
  550. $shareAttributes = $this->formatShareAttributes(
  551. $share->getAttributes()
  552. );
  553. if ($data === false) {
  554. // No usergroup share yet. Create one.
  555. $qb = $this->dbConn->getQueryBuilder();
  556. $qb->insert('share')
  557. ->values([
  558. 'share_type' => $qb->createNamedParameter(IShare::TYPE_USERGROUP),
  559. 'share_with' => $qb->createNamedParameter($recipient),
  560. 'uid_owner' => $qb->createNamedParameter($share->getShareOwner()),
  561. 'uid_initiator' => $qb->createNamedParameter($share->getSharedBy()),
  562. 'parent' => $qb->createNamedParameter($share->getId()),
  563. 'item_type' => $qb->createNamedParameter($share->getNodeType()),
  564. 'item_source' => $qb->createNamedParameter($share->getNodeId()),
  565. 'file_source' => $qb->createNamedParameter($share->getNodeId()),
  566. 'file_target' => $qb->createNamedParameter($share->getTarget()),
  567. 'permissions' => $qb->createNamedParameter($share->getPermissions()),
  568. 'attributes' => $qb->createNamedParameter($shareAttributes),
  569. 'stime' => $qb->createNamedParameter($share->getShareTime()->getTimestamp()),
  570. ])->execute();
  571. } else {
  572. // Already a usergroup share. Update it.
  573. $qb = $this->dbConn->getQueryBuilder();
  574. $qb->update('share')
  575. ->set('file_target', $qb->createNamedParameter($share->getTarget()))
  576. ->where($qb->expr()->eq('id', $qb->createNamedParameter($data['id'])))
  577. ->execute();
  578. }
  579. }
  580. return $share;
  581. }
  582. public function getSharesInFolder($userId, Folder $node, $reshares, $shallow = true) {
  583. $qb = $this->dbConn->getQueryBuilder();
  584. $qb->select('s.*',
  585. 'f.fileid', 'f.path', 'f.permissions AS f_permissions', 'f.storage', 'f.path_hash',
  586. 'f.parent AS f_parent', 'f.name', 'f.mimetype', 'f.mimepart', 'f.size', 'f.mtime', 'f.storage_mtime',
  587. 'f.encrypted', 'f.unencrypted_size', 'f.etag', 'f.checksum')
  588. ->from('share', 's')
  589. ->andWhere($qb->expr()->orX(
  590. $qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
  591. $qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
  592. ));
  593. $qb->andWhere($qb->expr()->orX(
  594. $qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USER)),
  595. $qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_GROUP)),
  596. $qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_LINK))
  597. ));
  598. /**
  599. * Reshares for this user are shares where they are the owner.
  600. */
  601. if ($reshares === false) {
  602. $qb->andWhere($qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)));
  603. } else {
  604. $qb->andWhere(
  605. $qb->expr()->orX(
  606. $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)),
  607. $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId))
  608. )
  609. );
  610. }
  611. // todo? maybe get these from the oc_mounts table
  612. $childMountNodes = array_filter($node->getDirectoryListing(), function (Node $node): bool {
  613. return $node->getInternalPath() === '';
  614. });
  615. $childMountRootIds = array_map(function (Node $node): int {
  616. return $node->getId();
  617. }, $childMountNodes);
  618. $qb->innerJoin('s', 'filecache', 'f', $qb->expr()->eq('s.file_source', 'f.fileid'));
  619. if ($shallow) {
  620. $qb->andWhere(
  621. $qb->expr()->orX(
  622. $qb->expr()->eq('f.parent', $qb->createNamedParameter($node->getId())),
  623. $qb->expr()->in('f.fileid', $qb->createParameter('chunk'))
  624. )
  625. );
  626. } else {
  627. $qb->andWhere(
  628. $qb->expr()->orX(
  629. $qb->expr()->like('f.path', $qb->createNamedParameter($this->dbConn->escapeLikeParameter($node->getInternalPath()) . '/%')),
  630. $qb->expr()->in('f.fileid', $qb->createParameter('chunk'))
  631. )
  632. );
  633. }
  634. $qb->orderBy('id');
  635. $shares = [];
  636. $chunks = array_chunk($childMountRootIds, 1000);
  637. // Force the request to be run when there is 0 mount.
  638. if (count($chunks) === 0) {
  639. $chunks = [[]];
  640. }
  641. foreach ($chunks as $chunk) {
  642. $qb->setParameter('chunk', $chunk, IQueryBuilder::PARAM_INT_ARRAY);
  643. $cursor = $qb->executeQuery();
  644. while ($data = $cursor->fetch()) {
  645. $shares[$data['fileid']][] = $this->createShare($data);
  646. }
  647. $cursor->closeCursor();
  648. }
  649. return $shares;
  650. }
  651. /**
  652. * @inheritdoc
  653. */
  654. public function getSharesBy($userId, $shareType, $node, $reshares, $limit, $offset) {
  655. $qb = $this->dbConn->getQueryBuilder();
  656. $qb->select('*')
  657. ->from('share')
  658. ->andWhere($qb->expr()->orX(
  659. $qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
  660. $qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
  661. ));
  662. $qb->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter($shareType)));
  663. /**
  664. * Reshares for this user are shares where they are the owner.
  665. */
  666. if ($reshares === false) {
  667. $qb->andWhere($qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)));
  668. } else {
  669. if ($node === null) {
  670. $qb->andWhere(
  671. $qb->expr()->orX(
  672. $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)),
  673. $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId))
  674. )
  675. );
  676. }
  677. }
  678. if ($node !== null) {
  679. $qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId())));
  680. }
  681. if ($limit !== -1) {
  682. $qb->setMaxResults($limit);
  683. }
  684. $qb->setFirstResult($offset);
  685. $qb->orderBy('id');
  686. $cursor = $qb->execute();
  687. $shares = [];
  688. while ($data = $cursor->fetch()) {
  689. $shares[] = $this->createShare($data);
  690. }
  691. $cursor->closeCursor();
  692. return $shares;
  693. }
  694. /**
  695. * @inheritdoc
  696. */
  697. public function getShareById($id, $recipientId = null) {
  698. $qb = $this->dbConn->getQueryBuilder();
  699. $qb->select('*')
  700. ->from('share')
  701. ->where($qb->expr()->eq('id', $qb->createNamedParameter($id)))
  702. ->andWhere(
  703. $qb->expr()->in(
  704. 'share_type',
  705. $qb->createNamedParameter([
  706. IShare::TYPE_USER,
  707. IShare::TYPE_GROUP,
  708. IShare::TYPE_LINK,
  709. ], IQueryBuilder::PARAM_INT_ARRAY)
  710. )
  711. )
  712. ->andWhere($qb->expr()->orX(
  713. $qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
  714. $qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
  715. ));
  716. $cursor = $qb->execute();
  717. $data = $cursor->fetch();
  718. $cursor->closeCursor();
  719. if ($data === false) {
  720. throw new ShareNotFound();
  721. }
  722. try {
  723. $share = $this->createShare($data);
  724. } catch (InvalidShare $e) {
  725. throw new ShareNotFound();
  726. }
  727. // If the recipient is set for a group share resolve to that user
  728. if ($recipientId !== null && $share->getShareType() === IShare::TYPE_GROUP) {
  729. $share = $this->resolveGroupShares([$share], $recipientId)[0];
  730. }
  731. return $share;
  732. }
  733. /**
  734. * Get shares for a given path
  735. *
  736. * @param \OCP\Files\Node $path
  737. * @return \OCP\Share\IShare[]
  738. */
  739. public function getSharesByPath(Node $path) {
  740. $qb = $this->dbConn->getQueryBuilder();
  741. $cursor = $qb->select('*')
  742. ->from('share')
  743. ->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($path->getId())))
  744. ->andWhere(
  745. $qb->expr()->orX(
  746. $qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USER)),
  747. $qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_GROUP))
  748. )
  749. )
  750. ->andWhere($qb->expr()->orX(
  751. $qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
  752. $qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
  753. ))
  754. ->execute();
  755. $shares = [];
  756. while ($data = $cursor->fetch()) {
  757. $shares[] = $this->createShare($data);
  758. }
  759. $cursor->closeCursor();
  760. return $shares;
  761. }
  762. /**
  763. * Returns whether the given database result can be interpreted as
  764. * a share with accessible file (not trashed, not deleted)
  765. */
  766. private function isAccessibleResult($data) {
  767. // exclude shares leading to deleted file entries
  768. if ($data['fileid'] === null || $data['path'] === null) {
  769. return false;
  770. }
  771. // exclude shares leading to trashbin on home storages
  772. $pathSections = explode('/', $data['path'], 2);
  773. // FIXME: would not detect rare md5'd home storage case properly
  774. if ($pathSections[0] !== 'files'
  775. && (str_starts_with($data['storage_string_id'], 'home::') || str_starts_with($data['storage_string_id'], 'object::user'))) {
  776. return false;
  777. } elseif ($pathSections[0] === '__groupfolders'
  778. && str_starts_with($pathSections[1], 'trash/')
  779. ) {
  780. // exclude shares leading to trashbin on group folders storages
  781. return false;
  782. }
  783. return true;
  784. }
  785. /**
  786. * @inheritdoc
  787. */
  788. public function getSharedWith($userId, $shareType, $node, $limit, $offset) {
  789. /** @var Share[] $shares */
  790. $shares = [];
  791. if ($shareType === IShare::TYPE_USER) {
  792. //Get shares directly with this user
  793. $qb = $this->dbConn->getQueryBuilder();
  794. $qb->select('s.*',
  795. 'f.fileid', 'f.path', 'f.permissions AS f_permissions', 'f.storage', 'f.path_hash',
  796. 'f.parent AS f_parent', 'f.name', 'f.mimetype', 'f.mimepart', 'f.size', 'f.mtime', 'f.storage_mtime',
  797. 'f.encrypted', 'f.unencrypted_size', 'f.etag', 'f.checksum'
  798. )
  799. ->selectAlias('st.id', 'storage_string_id')
  800. ->from('share', 's')
  801. ->leftJoin('s', 'filecache', 'f', $qb->expr()->eq('s.file_source', 'f.fileid'))
  802. ->leftJoin('f', 'storages', 'st', $qb->expr()->eq('f.storage', 'st.numeric_id'));
  803. // Order by id
  804. $qb->orderBy('s.id');
  805. // Set limit and offset
  806. if ($limit !== -1) {
  807. $qb->setMaxResults($limit);
  808. }
  809. $qb->setFirstResult($offset);
  810. $qb->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USER)))
  811. ->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($userId)))
  812. ->andWhere($qb->expr()->orX(
  813. $qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
  814. $qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
  815. ));
  816. // Filter by node if provided
  817. if ($node !== null) {
  818. $qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId())));
  819. }
  820. $cursor = $qb->execute();
  821. while ($data = $cursor->fetch()) {
  822. if ($data['fileid'] && $data['path'] === null) {
  823. $data['path'] = (string) $data['path'];
  824. $data['name'] = (string) $data['name'];
  825. $data['checksum'] = (string) $data['checksum'];
  826. }
  827. if ($this->isAccessibleResult($data)) {
  828. $shares[] = $this->createShare($data);
  829. }
  830. }
  831. $cursor->closeCursor();
  832. } elseif ($shareType === IShare::TYPE_GROUP) {
  833. $user = $this->userManager->get($userId);
  834. $allGroups = ($user instanceof IUser) ? $this->groupManager->getUserGroupIds($user) : [];
  835. /** @var Share[] $shares2 */
  836. $shares2 = [];
  837. $start = 0;
  838. while (true) {
  839. $groups = array_slice($allGroups, $start, 1000);
  840. $start += 1000;
  841. if ($groups === []) {
  842. break;
  843. }
  844. $qb = $this->dbConn->getQueryBuilder();
  845. $qb->select('s.*',
  846. 'f.fileid', 'f.path', 'f.permissions AS f_permissions', 'f.storage', 'f.path_hash',
  847. 'f.parent AS f_parent', 'f.name', 'f.mimetype', 'f.mimepart', 'f.size', 'f.mtime', 'f.storage_mtime',
  848. 'f.encrypted', 'f.unencrypted_size', 'f.etag', 'f.checksum'
  849. )
  850. ->selectAlias('st.id', 'storage_string_id')
  851. ->from('share', 's')
  852. ->leftJoin('s', 'filecache', 'f', $qb->expr()->eq('s.file_source', 'f.fileid'))
  853. ->leftJoin('f', 'storages', 'st', $qb->expr()->eq('f.storage', 'st.numeric_id'))
  854. ->orderBy('s.id')
  855. ->setFirstResult(0);
  856. if ($limit !== -1) {
  857. $qb->setMaxResults($limit - count($shares));
  858. }
  859. // Filter by node if provided
  860. if ($node !== null) {
  861. $qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId())));
  862. }
  863. $groups = array_filter($groups);
  864. $qb->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_GROUP)))
  865. ->andWhere($qb->expr()->in('share_with', $qb->createNamedParameter(
  866. $groups,
  867. IQueryBuilder::PARAM_STR_ARRAY
  868. )))
  869. ->andWhere($qb->expr()->orX(
  870. $qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
  871. $qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
  872. ));
  873. $cursor = $qb->execute();
  874. while ($data = $cursor->fetch()) {
  875. if ($offset > 0) {
  876. $offset--;
  877. continue;
  878. }
  879. if ($this->isAccessibleResult($data)) {
  880. $shares2[] = $this->createShare($data);
  881. }
  882. }
  883. $cursor->closeCursor();
  884. }
  885. /*
  886. * Resolve all group shares to user specific shares
  887. */
  888. $shares = $this->resolveGroupShares($shares2, $userId);
  889. } else {
  890. throw new BackendError('Invalid backend');
  891. }
  892. return $shares;
  893. }
  894. /**
  895. * Get a share by token
  896. *
  897. * @param string $token
  898. * @return \OCP\Share\IShare
  899. * @throws ShareNotFound
  900. */
  901. public function getShareByToken($token) {
  902. $qb = $this->dbConn->getQueryBuilder();
  903. $cursor = $qb->select('*')
  904. ->from('share')
  905. ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_LINK)))
  906. ->andWhere($qb->expr()->eq('token', $qb->createNamedParameter($token)))
  907. ->andWhere($qb->expr()->orX(
  908. $qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
  909. $qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
  910. ))
  911. ->executeQuery();
  912. $data = $cursor->fetch();
  913. if ($data === false) {
  914. throw new ShareNotFound();
  915. }
  916. try {
  917. $share = $this->createShare($data);
  918. } catch (InvalidShare $e) {
  919. throw new ShareNotFound();
  920. }
  921. return $share;
  922. }
  923. /**
  924. * Create a share object from an database row
  925. *
  926. * @param mixed[] $data
  927. * @return \OCP\Share\IShare
  928. * @throws InvalidShare
  929. */
  930. private function createShare($data) {
  931. $share = new Share($this->rootFolder, $this->userManager);
  932. $share->setId((int)$data['id'])
  933. ->setShareType((int)$data['share_type'])
  934. ->setPermissions((int)$data['permissions'])
  935. ->setTarget($data['file_target'])
  936. ->setNote((string)$data['note'])
  937. ->setMailSend((bool)$data['mail_send'])
  938. ->setStatus((int)$data['accepted'])
  939. ->setLabel($data['label'] ?? '');
  940. $shareTime = new \DateTime();
  941. $shareTime->setTimestamp((int)$data['stime']);
  942. $share->setShareTime($shareTime);
  943. if ($share->getShareType() === IShare::TYPE_USER) {
  944. $share->setSharedWith($data['share_with']);
  945. $user = $this->userManager->get($data['share_with']);
  946. if ($user !== null) {
  947. $share->setSharedWithDisplayName($user->getDisplayName());
  948. }
  949. } elseif ($share->getShareType() === IShare::TYPE_GROUP) {
  950. $share->setSharedWith($data['share_with']);
  951. $group = $this->groupManager->get($data['share_with']);
  952. if ($group !== null) {
  953. $share->setSharedWithDisplayName($group->getDisplayName());
  954. }
  955. } elseif ($share->getShareType() === IShare::TYPE_LINK) {
  956. $share->setPassword($data['password']);
  957. $share->setSendPasswordByTalk((bool)$data['password_by_talk']);
  958. $share->setToken($data['token']);
  959. }
  960. $share = $this->updateShareAttributes($share, $data['attributes']);
  961. $share->setSharedBy($data['uid_initiator']);
  962. $share->setShareOwner($data['uid_owner']);
  963. $share->setNodeId((int)$data['file_source']);
  964. $share->setNodeType($data['item_type']);
  965. if ($data['expiration'] !== null) {
  966. $expiration = \DateTime::createFromFormat('Y-m-d H:i:s', $data['expiration']);
  967. $share->setExpirationDate($expiration);
  968. }
  969. if (isset($data['f_permissions'])) {
  970. $entryData = $data;
  971. $entryData['permissions'] = $entryData['f_permissions'];
  972. $entryData['parent'] = $entryData['f_parent'];
  973. $share->setNodeCacheEntry(Cache::cacheEntryFromData($entryData,
  974. \OC::$server->getMimeTypeLoader()));
  975. }
  976. $share->setProviderId($this->identifier());
  977. $share->setHideDownload((int)$data['hide_download'] === 1);
  978. return $share;
  979. }
  980. /**
  981. * @param Share[] $shares
  982. * @param $userId
  983. * @return Share[] The updates shares if no update is found for a share return the original
  984. */
  985. private function resolveGroupShares($shares, $userId) {
  986. $result = [];
  987. $start = 0;
  988. while (true) {
  989. /** @var Share[] $shareSlice */
  990. $shareSlice = array_slice($shares, $start, 100);
  991. $start += 100;
  992. if ($shareSlice === []) {
  993. break;
  994. }
  995. /** @var int[] $ids */
  996. $ids = [];
  997. /** @var Share[] $shareMap */
  998. $shareMap = [];
  999. foreach ($shareSlice as $share) {
  1000. $ids[] = (int)$share->getId();
  1001. $shareMap[$share->getId()] = $share;
  1002. }
  1003. $qb = $this->dbConn->getQueryBuilder();
  1004. $query = $qb->select('*')
  1005. ->from('share')
  1006. ->where($qb->expr()->in('parent', $qb->createNamedParameter($ids, IQueryBuilder::PARAM_INT_ARRAY)))
  1007. ->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($userId)))
  1008. ->andWhere($qb->expr()->orX(
  1009. $qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
  1010. $qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
  1011. ));
  1012. $stmt = $query->execute();
  1013. while ($data = $stmt->fetch()) {
  1014. $shareMap[$data['parent']]->setPermissions((int)$data['permissions']);
  1015. $shareMap[$data['parent']]->setStatus((int)$data['accepted']);
  1016. $shareMap[$data['parent']]->setTarget($data['file_target']);
  1017. $shareMap[$data['parent']]->setParent($data['parent']);
  1018. }
  1019. $stmt->closeCursor();
  1020. foreach ($shareMap as $share) {
  1021. $result[] = $share;
  1022. }
  1023. }
  1024. return $result;
  1025. }
  1026. /**
  1027. * A user is deleted from the system
  1028. * So clean up the relevant shares.
  1029. *
  1030. * @param string $uid
  1031. * @param int $shareType
  1032. */
  1033. public function userDeleted($uid, $shareType) {
  1034. $qb = $this->dbConn->getQueryBuilder();
  1035. $qb->delete('share');
  1036. if ($shareType === IShare::TYPE_USER) {
  1037. /*
  1038. * Delete all user shares that are owned by this user
  1039. * or that are received by this user
  1040. */
  1041. $qb->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USER)));
  1042. $qb->andWhere(
  1043. $qb->expr()->orX(
  1044. $qb->expr()->eq('uid_owner', $qb->createNamedParameter($uid)),
  1045. $qb->expr()->eq('share_with', $qb->createNamedParameter($uid))
  1046. )
  1047. );
  1048. } elseif ($shareType === IShare::TYPE_GROUP) {
  1049. /*
  1050. * Delete all group shares that are owned by this user
  1051. * Or special user group shares that are received by this user
  1052. */
  1053. $qb->where(
  1054. $qb->expr()->andX(
  1055. $qb->expr()->orX(
  1056. $qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_GROUP)),
  1057. $qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USERGROUP))
  1058. ),
  1059. $qb->expr()->eq('uid_owner', $qb->createNamedParameter($uid))
  1060. )
  1061. );
  1062. $qb->orWhere(
  1063. $qb->expr()->andX(
  1064. $qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USERGROUP)),
  1065. $qb->expr()->eq('share_with', $qb->createNamedParameter($uid))
  1066. )
  1067. );
  1068. } elseif ($shareType === IShare::TYPE_LINK) {
  1069. /*
  1070. * Delete all link shares owned by this user.
  1071. * And all link shares initiated by this user (until #22327 is in)
  1072. */
  1073. $qb->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_LINK)));
  1074. $qb->andWhere(
  1075. $qb->expr()->orX(
  1076. $qb->expr()->eq('uid_owner', $qb->createNamedParameter($uid)),
  1077. $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($uid))
  1078. )
  1079. );
  1080. } else {
  1081. \OC::$server->getLogger()->logException(new \InvalidArgumentException('Default share provider tried to delete all shares for type: ' . $shareType));
  1082. return;
  1083. }
  1084. $qb->execute();
  1085. }
  1086. /**
  1087. * Delete all shares received by this group. As well as any custom group
  1088. * shares for group members.
  1089. *
  1090. * @param string $gid
  1091. */
  1092. public function groupDeleted($gid) {
  1093. /*
  1094. * First delete all custom group shares for group members
  1095. */
  1096. $qb = $this->dbConn->getQueryBuilder();
  1097. $qb->select('id')
  1098. ->from('share')
  1099. ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_GROUP)))
  1100. ->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($gid)));
  1101. $cursor = $qb->execute();
  1102. $ids = [];
  1103. while ($row = $cursor->fetch()) {
  1104. $ids[] = (int)$row['id'];
  1105. }
  1106. $cursor->closeCursor();
  1107. if (!empty($ids)) {
  1108. $chunks = array_chunk($ids, 100);
  1109. foreach ($chunks as $chunk) {
  1110. $qb->delete('share')
  1111. ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USERGROUP)))
  1112. ->andWhere($qb->expr()->in('parent', $qb->createNamedParameter($chunk, IQueryBuilder::PARAM_INT_ARRAY)));
  1113. $qb->execute();
  1114. }
  1115. }
  1116. /*
  1117. * Now delete all the group shares
  1118. */
  1119. $qb = $this->dbConn->getQueryBuilder();
  1120. $qb->delete('share')
  1121. ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_GROUP)))
  1122. ->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($gid)));
  1123. $qb->execute();
  1124. }
  1125. /**
  1126. * Delete custom group shares to this group for this user
  1127. *
  1128. * @param string $uid
  1129. * @param string $gid
  1130. * @return void
  1131. */
  1132. public function userDeletedFromGroup($uid, $gid) {
  1133. /*
  1134. * Get all group shares
  1135. */
  1136. $qb = $this->dbConn->getQueryBuilder();
  1137. $qb->select('id')
  1138. ->from('share')
  1139. ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_GROUP)))
  1140. ->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($gid)));
  1141. $cursor = $qb->executeQuery();
  1142. $ids = [];
  1143. while ($row = $cursor->fetch()) {
  1144. $ids[] = (int)$row['id'];
  1145. }
  1146. $cursor->closeCursor();
  1147. if (!empty($ids)) {
  1148. $chunks = array_chunk($ids, 100);
  1149. foreach ($chunks as $chunk) {
  1150. /*
  1151. * Delete all special shares with this users for the found group shares
  1152. */
  1153. $qb->delete('share')
  1154. ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USERGROUP)))
  1155. ->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($uid)))
  1156. ->andWhere($qb->expr()->in('parent', $qb->createNamedParameter($chunk, IQueryBuilder::PARAM_INT_ARRAY)));
  1157. $qb->executeStatement();
  1158. }
  1159. }
  1160. if ($this->shareManager->shareWithGroupMembersOnly()) {
  1161. $user = $this->userManager->get($uid);
  1162. if ($user === null) {
  1163. return;
  1164. }
  1165. $userGroups = $this->groupManager->getUserGroupIds($user);
  1166. // Delete user shares received by the user from users in the group.
  1167. $userReceivedShares = $this->shareManager->getSharedWith($uid, IShare::TYPE_USER, null, -1);
  1168. foreach ($userReceivedShares as $share) {
  1169. $owner = $this->userManager->get($share->getSharedBy());
  1170. if ($owner === null) {
  1171. continue;
  1172. }
  1173. $ownerGroups = $this->groupManager->getUserGroupIds($owner);
  1174. $mutualGroups = array_intersect($userGroups, $ownerGroups);
  1175. if (count($mutualGroups) === 0) {
  1176. $this->shareManager->deleteShare($share);
  1177. }
  1178. }
  1179. // Delete user shares from the user to users in the group.
  1180. $userEmittedShares = $this->shareManager->getSharesBy($uid, IShare::TYPE_USER, null, true, -1);
  1181. foreach ($userEmittedShares as $share) {
  1182. $recipient = $this->userManager->get($share->getSharedWith());
  1183. if ($recipient === null) {
  1184. continue;
  1185. }
  1186. $recipientGroups = $this->groupManager->getUserGroupIds($recipient);
  1187. $mutualGroups = array_intersect($userGroups, $recipientGroups);
  1188. if (count($mutualGroups) === 0) {
  1189. $this->shareManager->deleteShare($share);
  1190. }
  1191. }
  1192. }
  1193. }
  1194. /**
  1195. * @inheritdoc
  1196. */
  1197. public function getAccessList($nodes, $currentAccess) {
  1198. $ids = [];
  1199. foreach ($nodes as $node) {
  1200. $ids[] = $node->getId();
  1201. }
  1202. $qb = $this->dbConn->getQueryBuilder();
  1203. $or = $qb->expr()->orX(
  1204. $qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USER)),
  1205. $qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_GROUP)),
  1206. $qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_LINK))
  1207. );
  1208. if ($currentAccess) {
  1209. $or->add($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USERGROUP)));
  1210. }
  1211. $qb->select('id', 'parent', 'share_type', 'share_with', 'file_source', 'file_target', 'permissions')
  1212. ->from('share')
  1213. ->where(
  1214. $or
  1215. )
  1216. ->andWhere($qb->expr()->in('file_source', $qb->createNamedParameter($ids, IQueryBuilder::PARAM_INT_ARRAY)))
  1217. ->andWhere($qb->expr()->orX(
  1218. $qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
  1219. $qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
  1220. ));
  1221. $cursor = $qb->execute();
  1222. $users = [];
  1223. $link = false;
  1224. while ($row = $cursor->fetch()) {
  1225. $type = (int)$row['share_type'];
  1226. if ($type === IShare::TYPE_USER) {
  1227. $uid = $row['share_with'];
  1228. $users[$uid] = $users[$uid] ?? [];
  1229. $users[$uid][$row['id']] = $row;
  1230. } elseif ($type === IShare::TYPE_GROUP) {
  1231. $gid = $row['share_with'];
  1232. $group = $this->groupManager->get($gid);
  1233. if ($group === null) {
  1234. continue;
  1235. }
  1236. $userList = $group->getUsers();
  1237. foreach ($userList as $user) {
  1238. $uid = $user->getUID();
  1239. $users[$uid] = $users[$uid] ?? [];
  1240. $users[$uid][$row['id']] = $row;
  1241. }
  1242. } elseif ($type === IShare::TYPE_LINK) {
  1243. $link = true;
  1244. } elseif ($type === IShare::TYPE_USERGROUP && $currentAccess === true) {
  1245. $uid = $row['share_with'];
  1246. $users[$uid] = $users[$uid] ?? [];
  1247. $users[$uid][$row['id']] = $row;
  1248. }
  1249. }
  1250. $cursor->closeCursor();
  1251. if ($currentAccess === true) {
  1252. $users = array_map([$this, 'filterSharesOfUser'], $users);
  1253. $users = array_filter($users);
  1254. } else {
  1255. $users = array_keys($users);
  1256. }
  1257. return ['users' => $users, 'public' => $link];
  1258. }
  1259. /**
  1260. * For each user the path with the fewest slashes is returned
  1261. * @param array $shares
  1262. * @return array
  1263. */
  1264. protected function filterSharesOfUser(array $shares) {
  1265. // Group shares when the user has a share exception
  1266. foreach ($shares as $id => $share) {
  1267. $type = (int) $share['share_type'];
  1268. $permissions = (int) $share['permissions'];
  1269. if ($type === IShare::TYPE_USERGROUP) {
  1270. unset($shares[$share['parent']]);
  1271. if ($permissions === 0) {
  1272. unset($shares[$id]);
  1273. }
  1274. }
  1275. }
  1276. $best = [];
  1277. $bestDepth = 0;
  1278. foreach ($shares as $id => $share) {
  1279. $depth = substr_count(($share['file_target'] ?? ''), '/');
  1280. if (empty($best) || $depth < $bestDepth) {
  1281. $bestDepth = $depth;
  1282. $best = [
  1283. 'node_id' => $share['file_source'],
  1284. 'node_path' => $share['file_target'],
  1285. ];
  1286. }
  1287. }
  1288. return $best;
  1289. }
  1290. /**
  1291. * propagate notes to the recipients
  1292. *
  1293. * @param IShare $share
  1294. * @throws \OCP\Files\NotFoundException
  1295. */
  1296. private function propagateNote(IShare $share) {
  1297. if ($share->getShareType() === IShare::TYPE_USER) {
  1298. $user = $this->userManager->get($share->getSharedWith());
  1299. $this->sendNote([$user], $share);
  1300. } elseif ($share->getShareType() === IShare::TYPE_GROUP) {
  1301. $group = $this->groupManager->get($share->getSharedWith());
  1302. $groupMembers = $group->getUsers();
  1303. $this->sendNote($groupMembers, $share);
  1304. }
  1305. }
  1306. /**
  1307. * send note by mail
  1308. *
  1309. * @param array $recipients
  1310. * @param IShare $share
  1311. * @throws \OCP\Files\NotFoundException
  1312. */
  1313. private function sendNote(array $recipients, IShare $share) {
  1314. $toListByLanguage = [];
  1315. foreach ($recipients as $recipient) {
  1316. /** @var IUser $recipient */
  1317. $email = $recipient->getEMailAddress();
  1318. if ($email) {
  1319. $language = $this->l10nFactory->getUserLanguage($recipient);
  1320. if (!isset($toListByLanguage[$language])) {
  1321. $toListByLanguage[$language] = [];
  1322. }
  1323. $toListByLanguage[$language][$email] = $recipient->getDisplayName();
  1324. }
  1325. }
  1326. if (empty($toListByLanguage)) {
  1327. return;
  1328. }
  1329. foreach ($toListByLanguage as $l10n => $toList) {
  1330. $filename = $share->getNode()->getName();
  1331. $initiator = $share->getSharedBy();
  1332. $note = $share->getNote();
  1333. $l = $this->l10nFactory->get('lib', $l10n);
  1334. $initiatorUser = $this->userManager->get($initiator);
  1335. $initiatorDisplayName = ($initiatorUser instanceof IUser) ? $initiatorUser->getDisplayName() : $initiator;
  1336. $initiatorEmailAddress = ($initiatorUser instanceof IUser) ? $initiatorUser->getEMailAddress() : null;
  1337. $plainHeading = $l->t('%1$s shared »%2$s« with you and wants to add:', [$initiatorDisplayName, $filename]);
  1338. $htmlHeading = $l->t('%1$s shared »%2$s« with you and wants to add', [$initiatorDisplayName, $filename]);
  1339. $message = $this->mailer->createMessage();
  1340. $emailTemplate = $this->mailer->createEMailTemplate('defaultShareProvider.sendNote');
  1341. $emailTemplate->setSubject($l->t('»%s« added a note to a file shared with you', [$initiatorDisplayName]));
  1342. $emailTemplate->addHeader();
  1343. $emailTemplate->addHeading($htmlHeading, $plainHeading);
  1344. $emailTemplate->addBodyText(htmlspecialchars($note), $note);
  1345. $link = $this->urlGenerator->linkToRouteAbsolute('files.viewcontroller.showFile', ['fileid' => $share->getNode()->getId()]);
  1346. $emailTemplate->addBodyButton(
  1347. $l->t('Open »%s«', [$filename]),
  1348. $link
  1349. );
  1350. // The "From" contains the sharers name
  1351. $instanceName = $this->defaults->getName();
  1352. $senderName = $l->t(
  1353. '%1$s via %2$s',
  1354. [
  1355. $initiatorDisplayName,
  1356. $instanceName
  1357. ]
  1358. );
  1359. $message->setFrom([\OCP\Util::getDefaultEmailAddress($instanceName) => $senderName]);
  1360. if ($initiatorEmailAddress !== null) {
  1361. $message->setReplyTo([$initiatorEmailAddress => $initiatorDisplayName]);
  1362. $emailTemplate->addFooter($instanceName . ' - ' . $this->defaults->getSlogan());
  1363. } else {
  1364. $emailTemplate->addFooter();
  1365. }
  1366. if (count($toList) === 1) {
  1367. $message->setTo($toList);
  1368. } else {
  1369. $message->setTo([]);
  1370. $message->setBcc($toList);
  1371. }
  1372. $message->useTemplate($emailTemplate);
  1373. $this->mailer->send($message);
  1374. }
  1375. }
  1376. public function getAllShares(): iterable {
  1377. $qb = $this->dbConn->getQueryBuilder();
  1378. $qb->select('*')
  1379. ->from('share')
  1380. ->where(
  1381. $qb->expr()->orX(
  1382. $qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share\IShare::TYPE_USER)),
  1383. $qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share\IShare::TYPE_GROUP)),
  1384. $qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share\IShare::TYPE_LINK))
  1385. )
  1386. );
  1387. $cursor = $qb->execute();
  1388. while ($data = $cursor->fetch()) {
  1389. try {
  1390. $share = $this->createShare($data);
  1391. } catch (InvalidShare $e) {
  1392. continue;
  1393. }
  1394. yield $share;
  1395. }
  1396. $cursor->closeCursor();
  1397. }
  1398. /**
  1399. * Load from database format (JSON string) to IAttributes
  1400. *
  1401. * @return IShare the modified share
  1402. */
  1403. private function updateShareAttributes(IShare $share, ?string $data): IShare {
  1404. if ($data !== null && $data !== '') {
  1405. $attributes = new ShareAttributes();
  1406. $compressedAttributes = \json_decode($data, true);
  1407. if ($compressedAttributes === false || $compressedAttributes === null) {
  1408. return $share;
  1409. }
  1410. foreach ($compressedAttributes as $compressedAttribute) {
  1411. $attributes->setAttribute(
  1412. $compressedAttribute[0],
  1413. $compressedAttribute[1],
  1414. $compressedAttribute[2]
  1415. );
  1416. }
  1417. $share->setAttributes($attributes);
  1418. }
  1419. return $share;
  1420. }
  1421. /**
  1422. * Format IAttributes to database format (JSON string)
  1423. */
  1424. private function formatShareAttributes(?IAttributes $attributes): ?string {
  1425. if ($attributes === null || empty($attributes->toArray())) {
  1426. return null;
  1427. }
  1428. $compressedAttributes = [];
  1429. foreach ($attributes->toArray() as $attribute) {
  1430. $compressedAttributes[] = [
  1431. 0 => $attribute['scope'],
  1432. 1 => $attribute['key'],
  1433. 2 => $attribute['enabled']
  1434. ];
  1435. }
  1436. return \json_encode($compressedAttributes);
  1437. }
  1438. }