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.

930 lines
29 KiB

10 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
  1. <?php
  2. /**
  3. * @copyright Copyright (c) 2016, ownCloud, Inc.
  4. *
  5. * @author Joas Schilling <coding@schilljs.com>
  6. * @author Roeland Jago Douma <roeland@famdouma.nl>
  7. * @author Vincent Petry <pvince81@owncloud.com>
  8. *
  9. * @license AGPL-3.0
  10. *
  11. * This code is free software: you can redistribute it and/or modify
  12. * it under the terms of the GNU Affero General Public License, version 3,
  13. * as published by the Free Software Foundation.
  14. *
  15. * This program is distributed in the hope that it will be useful,
  16. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  18. * GNU Affero General Public License for more details.
  19. *
  20. * You should have received a copy of the GNU Affero General Public License, version 3,
  21. * along with this program. If not, see <http://www.gnu.org/licenses/>
  22. *
  23. */
  24. namespace OCA\Files_Sharing\Controller;
  25. use OCP\AppFramework\Http\DataResponse;
  26. use OCP\AppFramework\OCS\OCSBadRequestException;
  27. use OCP\AppFramework\OCS\OCSException;
  28. use OCP\AppFramework\OCS\OCSForbiddenException;
  29. use OCP\AppFramework\OCS\OCSNotFoundException;
  30. use OCP\AppFramework\OCSController;
  31. use OCP\Files\Node;
  32. use OCP\Files\NotFoundException;
  33. use OCP\IGroupManager;
  34. use OCP\IL10N;
  35. use OCP\IUserManager;
  36. use OCP\IRequest;
  37. use OCP\IURLGenerator;
  38. use OCP\Files\IRootFolder;
  39. use OCP\Lock\LockedException;
  40. use OCP\Share\IManager;
  41. use OCP\Share\Exceptions\ShareNotFound;
  42. use OCP\Share\Exceptions\GenericShareException;
  43. use OCP\Lock\ILockingProvider;
  44. use OCP\Share\IShare;
  45. /**
  46. * Class Share20OCS
  47. *
  48. * @package OCA\Files_Sharing\API
  49. */
  50. class ShareAPIController extends OCSController {
  51. /** @var IManager */
  52. private $shareManager;
  53. /** @var IGroupManager */
  54. private $groupManager;
  55. /** @var IUserManager */
  56. private $userManager;
  57. /** @var IRequest */
  58. protected $request;
  59. /** @var IRootFolder */
  60. private $rootFolder;
  61. /** @var IURLGenerator */
  62. private $urlGenerator;
  63. /** @var string */
  64. private $currentUser;
  65. /** @var IL10N */
  66. private $l;
  67. /** @var \OCP\Files\Node */
  68. private $lockedNode;
  69. /**
  70. * Share20OCS constructor.
  71. *
  72. * @param string $appName
  73. * @param IRequest $request
  74. * @param IManager $shareManager
  75. * @param IGroupManager $groupManager
  76. * @param IUserManager $userManager
  77. * @param IRootFolder $rootFolder
  78. * @param IURLGenerator $urlGenerator
  79. * @param string $userId
  80. * @param IL10N $l10n
  81. */
  82. public function __construct(
  83. $appName,
  84. IRequest $request,
  85. IManager $shareManager,
  86. IGroupManager $groupManager,
  87. IUserManager $userManager,
  88. IRootFolder $rootFolder,
  89. IURLGenerator $urlGenerator,
  90. $userId,
  91. IL10N $l10n
  92. ) {
  93. parent::__construct($appName, $request);
  94. $this->shareManager = $shareManager;
  95. $this->userManager = $userManager;
  96. $this->groupManager = $groupManager;
  97. $this->request = $request;
  98. $this->rootFolder = $rootFolder;
  99. $this->urlGenerator = $urlGenerator;
  100. $this->currentUser = $userId;
  101. $this->l = $l10n;
  102. }
  103. /**
  104. * Convert an IShare to an array for OCS output
  105. *
  106. * @param \OCP\Share\IShare $share
  107. * @param Node|null $recipientNode
  108. * @return array
  109. * @throws NotFoundException In case the node can't be resolved.
  110. */
  111. protected function formatShare(\OCP\Share\IShare $share, Node $recipientNode = null) {
  112. $sharedBy = $this->userManager->get($share->getSharedBy());
  113. $shareOwner = $this->userManager->get($share->getShareOwner());
  114. $result = [
  115. 'id' => $share->getId(),
  116. 'share_type' => $share->getShareType(),
  117. 'uid_owner' => $share->getSharedBy(),
  118. 'displayname_owner' => $sharedBy !== null ? $sharedBy->getDisplayName() : $share->getSharedBy(),
  119. 'permissions' => $share->getPermissions(),
  120. 'stime' => $share->getShareTime()->getTimestamp(),
  121. 'parent' => null,
  122. 'expiration' => null,
  123. 'token' => null,
  124. 'uid_file_owner' => $share->getShareOwner(),
  125. 'displayname_file_owner' => $shareOwner !== null ? $shareOwner->getDisplayName() : $share->getShareOwner(),
  126. ];
  127. $userFolder = $this->rootFolder->getUserFolder($this->currentUser);
  128. if ($recipientNode) {
  129. $node = $recipientNode;
  130. } else {
  131. $nodes = $userFolder->getById($share->getNodeId());
  132. if (empty($nodes)) {
  133. // fallback to guessing the path
  134. $node = $userFolder->get($share->getTarget());
  135. if ($node === null) {
  136. throw new NotFoundException();
  137. }
  138. } else {
  139. $node = $nodes[0];
  140. }
  141. }
  142. $result['path'] = $userFolder->getRelativePath($node->getPath());
  143. if ($node instanceOf \OCP\Files\Folder) {
  144. $result['item_type'] = 'folder';
  145. } else {
  146. $result['item_type'] = 'file';
  147. }
  148. $result['mimetype'] = $node->getMimetype();
  149. $result['storage_id'] = $node->getStorage()->getId();
  150. $result['storage'] = $node->getStorage()->getCache()->getNumericStorageId();
  151. $result['item_source'] = $node->getId();
  152. $result['file_source'] = $node->getId();
  153. $result['file_parent'] = $node->getParent()->getId();
  154. $result['file_target'] = $share->getTarget();
  155. if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) {
  156. $sharedWith = $this->userManager->get($share->getSharedWith());
  157. $result['share_with'] = $share->getSharedWith();
  158. $result['share_with_displayname'] = $sharedWith !== null ? $sharedWith->getDisplayName() : $share->getSharedWith();
  159. } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
  160. $group = $this->groupManager->get($share->getSharedWith());
  161. $result['share_with'] = $share->getSharedWith();
  162. $result['share_with_displayname'] = $group !== null ? $group->getDisplayName() : $share->getSharedWith();
  163. } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) {
  164. $result['share_with'] = $share->getPassword();
  165. $result['share_with_displayname'] = $share->getPassword();
  166. $result['token'] = $share->getToken();
  167. $result['url'] = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.showShare', ['token' => $share->getToken()]);
  168. $expiration = $share->getExpirationDate();
  169. if ($expiration !== null) {
  170. $result['expiration'] = $expiration->format('Y-m-d 00:00:00');
  171. }
  172. } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_REMOTE) {
  173. $result['share_with'] = $share->getSharedWith();
  174. $result['share_with_displayname'] = $this->getDisplayNameFromAddressBook($share->getSharedWith(), 'CLOUD');
  175. $result['token'] = $share->getToken();
  176. } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_EMAIL) {
  177. $result['share_with'] = $share->getSharedWith();
  178. $result['password'] = $share->getPassword();
  179. $result['share_with_displayname'] = $this->getDisplayNameFromAddressBook($share->getSharedWith(), 'EMAIL');
  180. $result['token'] = $share->getToken();
  181. } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_CIRCLE) {
  182. $result['share_with_displayname'] = $share->getSharedWith();
  183. $result['share_with'] = explode(' ', $share->getSharedWith(), 2)[0];
  184. }
  185. $result['mail_send'] = $share->getMailSend() ? 1 : 0;
  186. return $result;
  187. }
  188. /**
  189. * Check if one of the users address books knows the exact property, if
  190. * yes we return the full name.
  191. *
  192. * @param string $query
  193. * @param string $property
  194. * @return string
  195. */
  196. private function getDisplayNameFromAddressBook($query, $property) {
  197. // FIXME: If we inject the contacts manager it gets initialized bofore any address books are registered
  198. $result = \OC::$server->getContactsManager()->search($query, [$property]);
  199. foreach ($result as $r) {
  200. foreach($r[$property] as $value) {
  201. if ($value === $query) {
  202. return $r['FN'];
  203. }
  204. }
  205. }
  206. return $query;
  207. }
  208. /**
  209. * Get a specific share by id
  210. *
  211. * @NoAdminRequired
  212. *
  213. * @param string $id
  214. * @return DataResponse
  215. * @throws OCSNotFoundException
  216. */
  217. public function getShare($id) {
  218. try {
  219. $share = $this->getShareById($id);
  220. } catch (ShareNotFound $e) {
  221. throw new OCSNotFoundException($this->l->t('Wrong share ID, share doesn\'t exist'));
  222. }
  223. if ($this->canAccessShare($share)) {
  224. try {
  225. $share = $this->formatShare($share);
  226. return new DataResponse([$share]);
  227. } catch (NotFoundException $e) {
  228. //Fall trough
  229. }
  230. }
  231. throw new OCSNotFoundException($this->l->t('Wrong share ID, share doesn\'t exist'));
  232. }
  233. /**
  234. * Delete a share
  235. *
  236. * @NoAdminRequired
  237. *
  238. * @param string $id
  239. * @return DataResponse
  240. * @throws OCSNotFoundException
  241. */
  242. public function deleteShare($id) {
  243. try {
  244. $share = $this->getShareById($id);
  245. } catch (ShareNotFound $e) {
  246. throw new OCSNotFoundException($this->l->t('Wrong share ID, share doesn\'t exist'));
  247. }
  248. try {
  249. $this->lock($share->getNode());
  250. } catch (LockedException $e) {
  251. throw new OCSNotFoundException($this->l->t('could not delete share'));
  252. }
  253. if (!$this->canAccessShare($share)) {
  254. throw new OCSNotFoundException($this->l->t('Could not delete share'));
  255. }
  256. if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP &&
  257. $share->getShareOwner() !== $this->currentUser &&
  258. $share->getSharedBy() !== $this->currentUser) {
  259. $this->shareManager->deleteFromSelf($share, $this->currentUser);
  260. } else {
  261. $this->shareManager->deleteShare($share);
  262. }
  263. return new DataResponse();
  264. }
  265. /**
  266. * @NoAdminRequired
  267. *
  268. * @param string $path
  269. * @param int $permissions
  270. * @param int $shareType
  271. * @param string $shareWith
  272. * @param string $publicUpload
  273. * @param string $password
  274. * @param string $expireDate
  275. *
  276. * @return DataResponse
  277. * @throws OCSNotFoundException
  278. * @throws OCSForbiddenException
  279. * @throws OCSBadRequestException
  280. * @throws OCSException
  281. */
  282. public function createShare(
  283. $path = null,
  284. $permissions = \OCP\Constants::PERMISSION_ALL,
  285. $shareType = -1,
  286. $shareWith = null,
  287. $publicUpload = 'false',
  288. $password = '',
  289. $expireDate = ''
  290. ) {
  291. $share = $this->shareManager->newShare();
  292. // Verify path
  293. if ($path === null) {
  294. throw new OCSNotFoundException($this->l->t('Please specify a file or folder path'));
  295. }
  296. $userFolder = $this->rootFolder->getUserFolder($this->currentUser);
  297. try {
  298. $path = $userFolder->get($path);
  299. } catch (NotFoundException $e) {
  300. throw new OCSNotFoundException($this->l->t('Wrong path, file/folder doesn\'t exist'));
  301. }
  302. $share->setNode($path);
  303. try {
  304. $this->lock($share->getNode());
  305. } catch (LockedException $e) {
  306. throw new OCSNotFoundException($this->l->t('Could not create share'));
  307. }
  308. if ($permissions < 0 || $permissions > \OCP\Constants::PERMISSION_ALL) {
  309. throw new OCSNotFoundException($this->l->t('invalid permissions'));
  310. }
  311. // Shares always require read permissions
  312. $permissions |= \OCP\Constants::PERMISSION_READ;
  313. if ($path instanceof \OCP\Files\File) {
  314. // Single file shares should never have delete or create permissions
  315. $permissions &= ~\OCP\Constants::PERMISSION_DELETE;
  316. $permissions &= ~\OCP\Constants::PERMISSION_CREATE;
  317. }
  318. /*
  319. * Hack for https://github.com/owncloud/core/issues/22587
  320. * We check the permissions via webdav. But the permissions of the mount point
  321. * do not equal the share permissions. Here we fix that for federated mounts.
  322. */
  323. if ($path->getStorage()->instanceOfStorage('OCA\Files_Sharing\External\Storage')) {
  324. $permissions &= ~($permissions & ~$path->getPermissions());
  325. }
  326. if ($shareType === \OCP\Share::SHARE_TYPE_USER) {
  327. // Valid user is required to share
  328. if ($shareWith === null || !$this->userManager->userExists($shareWith)) {
  329. throw new OCSNotFoundException($this->l->t('Please specify a valid user'));
  330. }
  331. $share->setSharedWith($shareWith);
  332. $share->setPermissions($permissions);
  333. } else if ($shareType === \OCP\Share::SHARE_TYPE_GROUP) {
  334. if (!$this->shareManager->allowGroupSharing()) {
  335. throw new OCSNotFoundException($this->l->t('Group sharing is disabled by the administrator'));
  336. }
  337. // Valid group is required to share
  338. if ($shareWith === null || !$this->groupManager->groupExists($shareWith)) {
  339. throw new OCSNotFoundException($this->l->t('Please specify a valid group'));
  340. }
  341. $share->setSharedWith($shareWith);
  342. $share->setPermissions($permissions);
  343. } else if ($shareType === \OCP\Share::SHARE_TYPE_LINK) {
  344. //Can we even share links?
  345. if (!$this->shareManager->shareApiAllowLinks()) {
  346. throw new OCSNotFoundException($this->l->t('Public link sharing is disabled by the administrator'));
  347. }
  348. /*
  349. * For now we only allow 1 link share.
  350. * Return the existing link share if this is a duplicate
  351. */
  352. $existingShares = $this->shareManager->getSharesBy($this->currentUser, \OCP\Share::SHARE_TYPE_LINK, $path, false, 1, 0);
  353. if (!empty($existingShares)) {
  354. return new DataResponse($this->formatShare($existingShares[0]));
  355. }
  356. if ($publicUpload === 'true') {
  357. // Check if public upload is allowed
  358. if (!$this->shareManager->shareApiLinkAllowPublicUpload()) {
  359. throw new OCSForbiddenException($this->l->t('Public upload disabled by the administrator'));
  360. }
  361. // Public upload can only be set for folders
  362. if ($path instanceof \OCP\Files\File) {
  363. throw new OCSNotFoundException($this->l->t('Public upload is only possible for publicly shared folders'));
  364. }
  365. $share->setPermissions(
  366. \OCP\Constants::PERMISSION_READ |
  367. \OCP\Constants::PERMISSION_CREATE |
  368. \OCP\Constants::PERMISSION_UPDATE |
  369. \OCP\Constants::PERMISSION_DELETE
  370. );
  371. } else {
  372. $share->setPermissions(\OCP\Constants::PERMISSION_READ);
  373. }
  374. // Set password
  375. if ($password !== '') {
  376. $share->setPassword($password);
  377. }
  378. //Expire date
  379. if ($expireDate !== '') {
  380. try {
  381. $expireDate = $this->parseDate($expireDate);
  382. $share->setExpirationDate($expireDate);
  383. } catch (\Exception $e) {
  384. throw new OCSNotFoundException($this->l->t('Invalid date, date format must be YYYY-MM-DD'));
  385. }
  386. }
  387. } else if ($shareType === \OCP\Share::SHARE_TYPE_REMOTE) {
  388. if (!$this->shareManager->outgoingServer2ServerSharesAllowed()) {
  389. throw new OCSForbiddenException($this->l->t('Sharing %s failed because the back end does not allow shares from type %s', [$path->getPath(), $shareType]));
  390. }
  391. $share->setSharedWith($shareWith);
  392. $share->setPermissions($permissions);
  393. } else if ($shareType === \OCP\Share::SHARE_TYPE_EMAIL) {
  394. if ($share->getNodeType() === 'file') {
  395. $share->setPermissions(\OCP\Constants::PERMISSION_READ);
  396. } else {
  397. $share->setPermissions(
  398. \OCP\Constants::PERMISSION_READ |
  399. \OCP\Constants::PERMISSION_CREATE |
  400. \OCP\Constants::PERMISSION_UPDATE |
  401. \OCP\Constants::PERMISSION_DELETE);
  402. }
  403. $share->setSharedWith($shareWith);
  404. } else if ($shareType === \OCP\Share::SHARE_TYPE_CIRCLE) {
  405. if (!\OCP\App::isEnabled('circles')) {
  406. throw new OCSNotFoundException($this->l->t('You cannot share to a Circle if the app is not enabled'));
  407. }
  408. $circle = \OCA\Circles\Api\Circles::detailsCircle($shareWith);
  409. // Valid circle is required to share
  410. if ($circle === null) {
  411. throw new OCSNotFoundException($this->l->t('Please specify a valid circle'));
  412. }
  413. $share->setSharedWith($shareWith);
  414. $share->setPermissions($permissions);
  415. } else {
  416. throw new OCSBadRequestException($this->l->t('Unknown share type'));
  417. }
  418. $share->setShareType($shareType);
  419. $share->setSharedBy($this->currentUser);
  420. try {
  421. $share = $this->shareManager->createShare($share);
  422. } catch (GenericShareException $e) {
  423. $code = $e->getCode() === 0 ? 403 : $e->getCode();
  424. throw new OCSException($e->getHint(), $code);
  425. } catch (\Exception $e) {
  426. throw new OCSForbiddenException($e->getMessage());
  427. }
  428. $output = $this->formatShare($share);
  429. return new DataResponse($output);
  430. }
  431. /**
  432. * @param \OCP\Files\File|\OCP\Files\Folder $node
  433. * @return DataResponse
  434. */
  435. private function getSharedWithMe($node = null) {
  436. $userShares = $this->shareManager->getSharedWith($this->currentUser, \OCP\Share::SHARE_TYPE_USER, $node, -1, 0);
  437. $groupShares = $this->shareManager->getSharedWith($this->currentUser, \OCP\Share::SHARE_TYPE_GROUP, $node, -1, 0);
  438. $circleShares = $this->shareManager->getSharedWith($this->currentUser, \OCP\Share::SHARE_TYPE_CIRCLE, $node, -1, 0);
  439. $shares = array_merge($userShares, $groupShares, $circleShares);
  440. $shares = array_filter($shares, function (IShare $share) {
  441. return $share->getShareOwner() !== $this->currentUser;
  442. });
  443. $formatted = [];
  444. foreach ($shares as $share) {
  445. if ($this->canAccessShare($share)) {
  446. try {
  447. $formatted[] = $this->formatShare($share);
  448. } catch (NotFoundException $e) {
  449. // Ignore this share
  450. }
  451. }
  452. }
  453. return new DataResponse($formatted);
  454. }
  455. /**
  456. * @param \OCP\Files\Folder $folder
  457. * @return DataResponse
  458. * @throws OCSBadRequestException
  459. */
  460. private function getSharesInDir($folder) {
  461. if (!($folder instanceof \OCP\Files\Folder)) {
  462. throw new OCSBadRequestException($this->l->t('Not a directory'));
  463. }
  464. $nodes = $folder->getDirectoryListing();
  465. /** @var \OCP\Share\IShare[] $shares */
  466. $shares = [];
  467. foreach ($nodes as $node) {
  468. $shares = array_merge($shares, $this->shareManager->getSharesBy($this->currentUser, \OCP\Share::SHARE_TYPE_USER, $node, false, -1, 0));
  469. $shares = array_merge($shares, $this->shareManager->getSharesBy($this->currentUser, \OCP\Share::SHARE_TYPE_GROUP, $node, false, -1, 0));
  470. $shares = array_merge($shares, $this->shareManager->getSharesBy($this->currentUser, \OCP\Share::SHARE_TYPE_LINK, $node, false, -1, 0));
  471. if($this->shareManager->shareProviderExists(\OCP\Share::SHARE_TYPE_EMAIL)) {
  472. $shares = array_merge($shares, $this->shareManager->getSharesBy($this->currentUser, \OCP\Share::SHARE_TYPE_EMAIL, $node, false, -1, 0));
  473. }
  474. if ($this->shareManager->outgoingServer2ServerSharesAllowed()) {
  475. $shares = array_merge($shares, $this->shareManager->getSharesBy($this->currentUser, \OCP\Share::SHARE_TYPE_REMOTE, $node, false, -1, 0));
  476. }
  477. }
  478. $formatted = [];
  479. foreach ($shares as $share) {
  480. try {
  481. $formatted[] = $this->formatShare($share);
  482. } catch (NotFoundException $e) {
  483. //Ignore this share
  484. }
  485. }
  486. return new DataResponse($formatted);
  487. }
  488. /**
  489. * The getShares function.
  490. *
  491. * @NoAdminRequired
  492. *
  493. * @param string $shared_with_me
  494. * @param string $reshares
  495. * @param string $subfiles
  496. * @param string $path
  497. *
  498. * - Get shares by the current user
  499. * - Get shares by the current user and reshares (?reshares=true)
  500. * - Get shares with the current user (?shared_with_me=true)
  501. * - Get shares for a specific path (?path=...)
  502. * - Get all shares in a folder (?subfiles=true&path=..)
  503. *
  504. * @return DataResponse
  505. * @throws OCSNotFoundException
  506. */
  507. public function getShares(
  508. $shared_with_me = 'false',
  509. $reshares = 'false',
  510. $subfiles = 'false',
  511. $path = null
  512. ) {
  513. if ($path !== null) {
  514. $userFolder = $this->rootFolder->getUserFolder($this->currentUser);
  515. try {
  516. $path = $userFolder->get($path);
  517. $this->lock($path);
  518. } catch (\OCP\Files\NotFoundException $e) {
  519. throw new OCSNotFoundException($this->l->t('Wrong path, file/folder doesn\'t exist'));
  520. } catch (LockedException $e) {
  521. throw new OCSNotFoundException($this->l->t('Could not lock path'));
  522. }
  523. }
  524. if ($shared_with_me === 'true') {
  525. $result = $this->getSharedWithMe($path);
  526. return $result;
  527. }
  528. if ($subfiles === 'true') {
  529. $result = $this->getSharesInDir($path);
  530. return $result;
  531. }
  532. if ($reshares === 'true') {
  533. $reshares = true;
  534. } else {
  535. $reshares = false;
  536. }
  537. // Get all shares
  538. $userShares = $this->shareManager->getSharesBy($this->currentUser, \OCP\Share::SHARE_TYPE_USER, $path, $reshares, -1, 0);
  539. $groupShares = $this->shareManager->getSharesBy($this->currentUser, \OCP\Share::SHARE_TYPE_GROUP, $path, $reshares, -1, 0);
  540. $linkShares = $this->shareManager->getSharesBy($this->currentUser, \OCP\Share::SHARE_TYPE_LINK, $path, $reshares, -1, 0);
  541. if ($this->shareManager->shareProviderExists(\OCP\Share::SHARE_TYPE_EMAIL)) {
  542. $mailShares = $this->shareManager->getSharesBy($this->currentUser, \OCP\Share::SHARE_TYPE_EMAIL, $path, $reshares, -1, 0);
  543. } else {
  544. $mailShares = [];
  545. }
  546. if ($this->shareManager->shareProviderExists(\OCP\Share::SHARE_TYPE_CIRCLE)) {
  547. $circleShares = $this->shareManager->getSharesBy($this->currentUser, \OCP\Share::SHARE_TYPE_CIRCLE, $path, $reshares, -1, 0);
  548. } else {
  549. $circleShares = [];
  550. }
  551. $shares = array_merge($userShares, $groupShares, $linkShares, $mailShares, $circleShares);
  552. if ($this->shareManager->outgoingServer2ServerSharesAllowed()) {
  553. $federatedShares = $this->shareManager->getSharesBy($this->currentUser, \OCP\Share::SHARE_TYPE_REMOTE, $path, $reshares, -1, 0);
  554. $shares = array_merge($shares, $federatedShares);
  555. }
  556. $formatted = [];
  557. foreach ($shares as $share) {
  558. try {
  559. $formatted[] = $this->formatShare($share, $path);
  560. } catch (NotFoundException $e) {
  561. //Ignore share
  562. }
  563. }
  564. return new DataResponse($formatted);
  565. }
  566. /**
  567. * @NoAdminRequired
  568. *
  569. * @param int $id
  570. * @param int $permissions
  571. * @param string $password
  572. * @param string $publicUpload
  573. * @param string $expireDate
  574. * @return DataResponse
  575. * @throws OCSNotFoundException
  576. * @throws OCSBadRequestException
  577. * @throws OCSForbiddenException
  578. */
  579. public function updateShare(
  580. $id,
  581. $permissions = null,
  582. $password = null,
  583. $publicUpload = null,
  584. $expireDate = null
  585. ) {
  586. try {
  587. $share = $this->getShareById($id);
  588. } catch (ShareNotFound $e) {
  589. throw new OCSNotFoundException($this->l->t('Wrong share ID, share doesn\'t exist'));
  590. }
  591. $this->lock($share->getNode());
  592. if (!$this->canAccessShare($share, false)) {
  593. throw new OCSNotFoundException($this->l->t('Wrong share ID, share doesn\'t exist'));
  594. }
  595. /*
  596. * expirationdate, password and publicUpload only make sense for link shares
  597. */
  598. if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) {
  599. if ($permissions === null && $password === null && $publicUpload === null && $expireDate === null) {
  600. throw new OCSBadRequestException($this->l->t('Wrong or no update parameter given'));
  601. }
  602. $newPermissions = null;
  603. if ($publicUpload === 'true') {
  604. $newPermissions = \OCP\Constants::PERMISSION_READ | \OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE;
  605. } else if ($publicUpload === 'false') {
  606. $newPermissions = \OCP\Constants::PERMISSION_READ;
  607. }
  608. if ($permissions !== null) {
  609. $newPermissions = (int)$permissions;
  610. }
  611. if ($newPermissions !== null &&
  612. !in_array($newPermissions, [
  613. \OCP\Constants::PERMISSION_READ,
  614. \OCP\Constants::PERMISSION_READ | \OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE, // legacy
  615. \OCP\Constants::PERMISSION_READ | \OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE, // correct
  616. \OCP\Constants::PERMISSION_CREATE, // hidden file list
  617. \OCP\Constants::PERMISSION_READ | \OCP\Constants::PERMISSION_UPDATE, // allow to edit single files
  618. ])
  619. ) {
  620. throw new OCSBadRequestException($this->l->t('Can\'t change permissions for public share links'));
  621. }
  622. if (
  623. // legacy
  624. $newPermissions === (\OCP\Constants::PERMISSION_READ | \OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE) ||
  625. // correct
  626. $newPermissions === (\OCP\Constants::PERMISSION_READ | \OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE)
  627. ) {
  628. if (!$this->shareManager->shareApiLinkAllowPublicUpload()) {
  629. throw new OCSForbiddenException($this->l->t('Public upload disabled by the administrator'));
  630. }
  631. if (!($share->getNode() instanceof \OCP\Files\Folder)) {
  632. throw new OCSBadRequestException($this->l->t('Public upload is only possible for publicly shared folders'));
  633. }
  634. // normalize to correct public upload permissions
  635. $newPermissions = \OCP\Constants::PERMISSION_READ | \OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE;
  636. }
  637. if ($newPermissions !== null) {
  638. $share->setPermissions($newPermissions);
  639. $permissions = $newPermissions;
  640. }
  641. if ($expireDate === '') {
  642. $share->setExpirationDate(null);
  643. } else if ($expireDate !== null) {
  644. try {
  645. $expireDate = $this->parseDate($expireDate);
  646. } catch (\Exception $e) {
  647. throw new OCSBadRequestException($e->getMessage());
  648. }
  649. $share->setExpirationDate($expireDate);
  650. }
  651. if ($password === '') {
  652. $share->setPassword(null);
  653. } else if ($password !== null) {
  654. $share->setPassword($password);
  655. }
  656. } else {
  657. // For other shares only permissions is valid.
  658. if ($share->getShareType() !== \OCP\Share::SHARE_TYPE_EMAIL && $permissions === null) {
  659. throw new OCSBadRequestException($this->l->t('Wrong or no update parameter given'));
  660. } elseif ($permissions !== null) {
  661. $permissions = (int)$permissions;
  662. $share->setPermissions($permissions);
  663. }
  664. if ($share->getShareType() === \OCP\Share::SHARE_TYPE_EMAIL) {
  665. if ($expireDate === '') {
  666. $share->setExpirationDate(null);
  667. } else if ($expireDate !== null) {
  668. try {
  669. $expireDate = $this->parseDate($expireDate);
  670. } catch (\Exception $e) {
  671. throw new OCSBadRequestException($e->getMessage());
  672. }
  673. $share->setExpirationDate($expireDate);
  674. }
  675. if ($password === '') {
  676. $share->setPassword(null);
  677. } else if ($password !== null) {
  678. $share->setPassword($password);
  679. }
  680. }
  681. }
  682. if ($permissions !== null && $share->getShareOwner() !== $this->currentUser) {
  683. /* Check if this is an incomming share */
  684. $incomingShares = $this->shareManager->getSharedWith($this->currentUser, \OCP\Share::SHARE_TYPE_USER, $share->getNode(), -1, 0);
  685. $incomingShares = array_merge($incomingShares, $this->shareManager->getSharedWith($this->currentUser, \OCP\Share::SHARE_TYPE_GROUP, $share->getNode(), -1, 0));
  686. /** @var \OCP\Share\IShare[] $incomingShares */
  687. if (!empty($incomingShares)) {
  688. $maxPermissions = 0;
  689. foreach ($incomingShares as $incomingShare) {
  690. $maxPermissions |= $incomingShare->getPermissions();
  691. }
  692. if ($share->getPermissions() & ~$maxPermissions) {
  693. throw new OCSNotFoundException($this->l->t('Cannot increase permissions'));
  694. }
  695. }
  696. }
  697. try {
  698. $share = $this->shareManager->updateShare($share);
  699. } catch (\Exception $e) {
  700. throw new OCSBadRequestException($e->getMessage());
  701. }
  702. return new DataResponse($this->formatShare($share));
  703. }
  704. /**
  705. * @param \OCP\Share\IShare $share
  706. * @return bool
  707. */
  708. protected function canAccessShare(\OCP\Share\IShare $share, $checkGroups = true) {
  709. // A file with permissions 0 can't be accessed by us. So Don't show it
  710. if ($share->getPermissions() === 0) {
  711. return false;
  712. }
  713. // Owner of the file and the sharer of the file can always get share
  714. if ($share->getShareOwner() === $this->currentUser ||
  715. $share->getSharedBy() === $this->currentUser
  716. ) {
  717. return true;
  718. }
  719. // If the share is shared with you (or a group you are a member of)
  720. if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER &&
  721. $share->getSharedWith() === $this->currentUser
  722. ) {
  723. return true;
  724. }
  725. if ($checkGroups && $share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
  726. $sharedWith = $this->groupManager->get($share->getSharedWith());
  727. $user = $this->userManager->get($this->currentUser);
  728. if ($user !== null && $sharedWith !== null && $sharedWith->inGroup($user)) {
  729. return true;
  730. }
  731. }
  732. if ($share->getShareType() === \OCP\Share::SHARE_TYPE_CIRCLE) {
  733. // TODO: have a sanity check like above?
  734. return true;
  735. }
  736. return false;
  737. }
  738. /**
  739. * Make sure that the passed date is valid ISO 8601
  740. * So YYYY-MM-DD
  741. * If not throw an exception
  742. *
  743. * @param string $expireDate
  744. *
  745. * @throws \Exception
  746. * @return \DateTime
  747. */
  748. private function parseDate($expireDate) {
  749. try {
  750. $date = new \DateTime($expireDate);
  751. } catch (\Exception $e) {
  752. throw new \Exception('Invalid date. Format must be YYYY-MM-DD');
  753. }
  754. if ($date === false) {
  755. throw new \Exception('Invalid date. Format must be YYYY-MM-DD');
  756. }
  757. $date->setTime(0, 0, 0);
  758. return $date;
  759. }
  760. /**
  761. * Since we have multiple providers but the OCS Share API v1 does
  762. * not support this we need to check all backends.
  763. *
  764. * @param string $id
  765. * @return \OCP\Share\IShare
  766. * @throws ShareNotFound
  767. */
  768. private function getShareById($id) {
  769. $share = null;
  770. // First check if it is an internal share.
  771. try {
  772. $share = $this->shareManager->getShareById('ocinternal:' . $id);
  773. return $share;
  774. } catch (ShareNotFound $e) {
  775. // Do nothing, just try the other share type
  776. }
  777. try {
  778. if ($this->shareManager->shareProviderExists(\OCP\Share::SHARE_TYPE_CIRCLE)) {
  779. $share = $this->shareManager->getShareById('ocCircleShare:' . $id);
  780. return $share;
  781. }
  782. } catch (ShareNotFound $e) {
  783. // Do nothing, just try the other share type
  784. }
  785. try {
  786. if ($this->shareManager->shareProviderExists(\OCP\Share::SHARE_TYPE_EMAIL)) {
  787. $share = $this->shareManager->getShareById('ocMailShare:' . $id);
  788. return $share;
  789. }
  790. } catch (ShareNotFound $e) {
  791. // Do nothing, just try the other share type
  792. }
  793. if (!$this->shareManager->outgoingServer2ServerSharesAllowed()) {
  794. throw new ShareNotFound();
  795. }
  796. $share = $this->shareManager->getShareById('ocFederatedSharing:' . $id);
  797. return $share;
  798. }
  799. /**
  800. * Lock a Node
  801. *
  802. * @param \OCP\Files\Node $node
  803. */
  804. private function lock(\OCP\Files\Node $node) {
  805. $node->lock(ILockingProvider::LOCK_SHARED);
  806. $this->lockedNode = $node;
  807. }
  808. /**
  809. * Cleanup the remaining locks
  810. */
  811. public function cleanup() {
  812. if ($this->lockedNode !== null) {
  813. $this->lockedNode->unlock(ILockingProvider::LOCK_SHARED);
  814. }
  815. }
  816. }