Passed
Push — master ( 103c7e...824757 )
by Roeland
11:48 queued 12s
created
apps/files_sharing/lib/Controller/ShareAPIController.php 1 patch
Indentation   +1594 added lines, -1594 removed lines patch added patch discarded remove patch
@@ -77,1601 +77,1601 @@
 block discarded – undo
77 77
  */
78 78
 class ShareAPIController extends OCSController {
79 79
 
80
-	/** @var IManager */
81
-	private $shareManager;
82
-	/** @var IGroupManager */
83
-	private $groupManager;
84
-	/** @var IUserManager */
85
-	private $userManager;
86
-	/** @var IRootFolder */
87
-	private $rootFolder;
88
-	/** @var IURLGenerator */
89
-	private $urlGenerator;
90
-	/** @var string */
91
-	private $currentUser;
92
-	/** @var IL10N */
93
-	private $l;
94
-	/** @var \OCP\Files\Node */
95
-	private $lockedNode;
96
-	/** @var IConfig */
97
-	private $config;
98
-	/** @var IAppManager */
99
-	private $appManager;
100
-	/** @var IServerContainer */
101
-	private $serverContainer;
102
-
103
-	/**
104
-	 * Share20OCS constructor.
105
-	 *
106
-	 * @param string $appName
107
-	 * @param IRequest $request
108
-	 * @param IManager $shareManager
109
-	 * @param IGroupManager $groupManager
110
-	 * @param IUserManager $userManager
111
-	 * @param IRootFolder $rootFolder
112
-	 * @param IURLGenerator $urlGenerator
113
-	 * @param string $userId
114
-	 * @param IL10N $l10n
115
-	 * @param IConfig $config
116
-	 * @param IAppManager $appManager
117
-	 * @param IServerContainer $serverContainer
118
-	 */
119
-	public function __construct(
120
-		string $appName,
121
-		IRequest $request,
122
-		IManager $shareManager,
123
-		IGroupManager $groupManager,
124
-		IUserManager $userManager,
125
-		IRootFolder $rootFolder,
126
-		IURLGenerator $urlGenerator,
127
-		string $userId = null,
128
-		IL10N $l10n,
129
-		IConfig $config,
130
-		IAppManager $appManager,
131
-		IServerContainer $serverContainer
132
-	) {
133
-		parent::__construct($appName, $request);
134
-
135
-		$this->shareManager = $shareManager;
136
-		$this->userManager = $userManager;
137
-		$this->groupManager = $groupManager;
138
-		$this->request = $request;
139
-		$this->rootFolder = $rootFolder;
140
-		$this->urlGenerator = $urlGenerator;
141
-		$this->currentUser = $userId;
142
-		$this->l = $l10n;
143
-		$this->config = $config;
144
-		$this->appManager = $appManager;
145
-		$this->serverContainer = $serverContainer;
146
-	}
147
-
148
-	/**
149
-	 * Convert an IShare to an array for OCS output
150
-	 *
151
-	 * @param \OCP\Share\IShare $share
152
-	 * @param Node|null $recipientNode
153
-	 * @return array
154
-	 * @throws NotFoundException In case the node can't be resolved.
155
-	 *
156
-	 * @suppress PhanUndeclaredClassMethod
157
-	 */
158
-	protected function formatShare(\OCP\Share\IShare $share, Node $recipientNode = null): array {
159
-		$sharedBy = $this->userManager->get($share->getSharedBy());
160
-		$shareOwner = $this->userManager->get($share->getShareOwner());
161
-
162
-		$result = [
163
-			'id' => $share->getId(),
164
-			'share_type' => $share->getShareType(),
165
-			'uid_owner' => $share->getSharedBy(),
166
-			'displayname_owner' => $sharedBy !== null ? $sharedBy->getDisplayName() : $share->getSharedBy(),
167
-			// recipient permissions
168
-			'permissions' => $share->getPermissions(),
169
-			// current user permissions on this share
170
-			'can_edit' => $this->canEditShare($share),
171
-			'can_delete' => $this->canDeleteShare($share),
172
-			'stime' => $share->getShareTime()->getTimestamp(),
173
-			'parent' => null,
174
-			'expiration' => null,
175
-			'token' => null,
176
-			'uid_file_owner' => $share->getShareOwner(),
177
-			'note' => $share->getNote(),
178
-			'label' => $share->getLabel(),
179
-			'displayname_file_owner' => $shareOwner !== null ? $shareOwner->getDisplayName() : $share->getShareOwner(),
180
-		];
181
-
182
-		$userFolder = $this->rootFolder->getUserFolder($this->currentUser);
183
-		if ($recipientNode) {
184
-			$node = $recipientNode;
185
-		} else {
186
-			$nodes = $userFolder->getById($share->getNodeId());
187
-			if (empty($nodes)) {
188
-				// fallback to guessing the path
189
-				$node = $userFolder->get($share->getTarget());
190
-				if ($node === null || $share->getTarget() === '') {
191
-					throw new NotFoundException();
192
-				}
193
-			} else {
194
-				$node = reset($nodes);
195
-			}
196
-		}
197
-
198
-		$result['path'] = $userFolder->getRelativePath($node->getPath());
199
-		if ($node instanceof \OCP\Files\Folder) {
200
-			$result['item_type'] = 'folder';
201
-		} else {
202
-			$result['item_type'] = 'file';
203
-		}
204
-
205
-		$result['mimetype'] = $node->getMimetype();
206
-		$result['storage_id'] = $node->getStorage()->getId();
207
-		$result['storage'] = $node->getStorage()->getCache()->getNumericStorageId();
208
-		$result['item_source'] = $node->getId();
209
-		$result['file_source'] = $node->getId();
210
-		$result['file_parent'] = $node->getParent()->getId();
211
-		$result['file_target'] = $share->getTarget();
212
-
213
-		$expiration = $share->getExpirationDate();
214
-		if ($expiration !== null) {
215
-			$result['expiration'] = $expiration->format('Y-m-d 00:00:00');
216
-		}
217
-
218
-		if ($share->getShareType() === Share::SHARE_TYPE_USER) {
219
-			$sharedWith = $this->userManager->get($share->getSharedWith());
220
-			$result['share_with'] = $share->getSharedWith();
221
-			$result['share_with_displayname'] = $sharedWith !== null ? $sharedWith->getDisplayName() : $share->getSharedWith();
222
-		} elseif ($share->getShareType() === Share::SHARE_TYPE_GROUP) {
223
-			$group = $this->groupManager->get($share->getSharedWith());
224
-			$result['share_with'] = $share->getSharedWith();
225
-			$result['share_with_displayname'] = $group !== null ? $group->getDisplayName() : $share->getSharedWith();
226
-		} elseif ($share->getShareType() === IShare::TYPE_LINK) {
227
-
228
-			// "share_with" and "share_with_displayname" for passwords of link
229
-			// shares was deprecated in Nextcloud 15, use "password" instead.
230
-			$result['share_with'] = $share->getPassword();
231
-			$result['share_with_displayname'] = '(' . $this->l->t('Shared link') . ')';
232
-
233
-			$result['password'] = $share->getPassword();
234
-
235
-			$result['send_password_by_talk'] = $share->getSendPasswordByTalk();
236
-
237
-			$result['token'] = $share->getToken();
238
-			$result['url'] = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.showShare', ['token' => $share->getToken()]);
239
-		} elseif ($share->getShareType() === Share::SHARE_TYPE_REMOTE || $share->getShareType() === Share::SHARE_TYPE_REMOTE_GROUP) {
240
-			$result['share_with'] = $share->getSharedWith();
241
-			$result['share_with_displayname'] = $this->getDisplayNameFromAddressBook($share->getSharedWith(), 'CLOUD');
242
-			$result['token'] = $share->getToken();
243
-		} elseif ($share->getShareType() === Share::SHARE_TYPE_EMAIL) {
244
-			$result['share_with'] = $share->getSharedWith();
245
-			$result['password'] = $share->getPassword();
246
-			$result['send_password_by_talk'] = $share->getSendPasswordByTalk();
247
-			$result['share_with_displayname'] = $this->getDisplayNameFromAddressBook($share->getSharedWith(), 'EMAIL');
248
-			$result['token'] = $share->getToken();
249
-		} elseif ($share->getShareType() === Share::SHARE_TYPE_CIRCLE) {
250
-			// getSharedWith() returns either "name (type, owner)" or
251
-			// "name (type, owner) [id]", depending on the Circles app version.
252
-			$hasCircleId = (substr($share->getSharedWith(), -1) === ']');
253
-
254
-			$result['share_with_displayname'] = $share->getSharedWithDisplayName();
255
-			if (empty($result['share_with_displayname'])) {
256
-				$displayNameLength = ($hasCircleId ? strrpos($share->getSharedWith(), ' ') : strlen($share->getSharedWith()));
257
-				$result['share_with_displayname'] = substr($share->getSharedWith(), 0, $displayNameLength);
258
-			}
259
-
260
-			$result['share_with_avatar'] = $share->getSharedWithAvatar();
261
-
262
-			$shareWithStart = ($hasCircleId ? strrpos($share->getSharedWith(), '[') + 1 : 0);
263
-			$shareWithLength = ($hasCircleId ? -1 : strpos($share->getSharedWith(), ' '));
264
-			if (is_bool($shareWithLength)) {
265
-				$shareWithLength = -1;
266
-			}
267
-			$result['share_with'] = substr($share->getSharedWith(), $shareWithStart, $shareWithLength);
268
-		} elseif ($share->getShareType() === Share::SHARE_TYPE_ROOM) {
269
-			$result['share_with'] = $share->getSharedWith();
270
-			$result['share_with_displayname'] = '';
271
-
272
-			try {
273
-				$result = array_merge($result, $this->getRoomShareHelper()->formatShare($share));
274
-			} catch (QueryException $e) {
275
-			}
276
-		}
277
-
278
-
279
-		$result['mail_send'] = $share->getMailSend() ? 1 : 0;
280
-		$result['hide_download'] = $share->getHideDownload() ? 1 : 0;
281
-
282
-		return $result;
283
-	}
284
-
285
-	/**
286
-	 * Check if one of the users address books knows the exact property, if
287
-	 * yes we return the full name.
288
-	 *
289
-	 * @param string $query
290
-	 * @param string $property
291
-	 * @return string
292
-	 */
293
-	private function getDisplayNameFromAddressBook(string $query, string $property): string {
294
-		// FIXME: If we inject the contacts manager it gets initialized bofore any address books are registered
295
-		$result = \OC::$server->getContactsManager()->search($query, [$property]);
296
-		foreach ($result as $r) {
297
-			foreach ($r[$property] as $value) {
298
-				if ($value === $query) {
299
-					return $r['FN'];
300
-				}
301
-			}
302
-		}
303
-
304
-		return $query;
305
-	}
306
-
307
-	/**
308
-	 * Get a specific share by id
309
-	 *
310
-	 * @NoAdminRequired
311
-	 *
312
-	 * @param string $id
313
-	 * @return DataResponse
314
-	 * @throws OCSNotFoundException
315
-	 */
316
-	public function getShare(string $id): DataResponse {
317
-		try {
318
-			$share = $this->getShareById($id);
319
-		} catch (ShareNotFound $e) {
320
-			throw new OCSNotFoundException($this->l->t('Wrong share ID, share doesn\'t exist'));
321
-		}
322
-
323
-		try {
324
-			if ($this->canAccessShare($share)) {
325
-				$share = $this->formatShare($share);
326
-				return new DataResponse([$share]);
327
-			}
328
-		} catch (NotFoundException $e) {
329
-			// Fall trough
330
-		}
331
-
332
-		throw new OCSNotFoundException($this->l->t('Wrong share ID, share doesn\'t exist'));
333
-	}
334
-
335
-	/**
336
-	 * Delete a share
337
-	 *
338
-	 * @NoAdminRequired
339
-	 *
340
-	 * @param string $id
341
-	 * @return DataResponse
342
-	 * @throws OCSNotFoundException
343
-	 */
344
-	public function deleteShare(string $id): DataResponse {
345
-		try {
346
-			$share = $this->getShareById($id);
347
-		} catch (ShareNotFound $e) {
348
-			throw new OCSNotFoundException($this->l->t('Wrong share ID, share doesn\'t exist'));
349
-		}
350
-
351
-		try {
352
-			$this->lock($share->getNode());
353
-		} catch (LockedException $e) {
354
-			throw new OCSNotFoundException($this->l->t('Could not delete share'));
355
-		}
356
-
357
-		if (!$this->canAccessShare($share)) {
358
-			throw new OCSNotFoundException($this->l->t('Wrong share ID, share doesn\'t exist'));
359
-		}
360
-
361
-		// if it's a group share or a room share
362
-		// we don't delete the share, but only the
363
-		// mount point. Allowing it to be restored
364
-		// from the deleted shares
365
-		if ($this->canDeleteShareFromSelf($share)) {
366
-			$this->shareManager->deleteFromSelf($share, $this->currentUser);
367
-		} else {
368
-			if (!$this->canDeleteShare($share)) {
369
-				throw new OCSForbiddenException($this->l->t('Could not delete share'));
370
-			}
371
-
372
-			$this->shareManager->deleteShare($share);
373
-		}
374
-
375
-		return new DataResponse();
376
-	}
377
-
378
-	/**
379
-	 * @NoAdminRequired
380
-	 *
381
-	 * @param string $path
382
-	 * @param int $permissions
383
-	 * @param int $shareType
384
-	 * @param string $shareWith
385
-	 * @param string $publicUpload
386
-	 * @param string $password
387
-	 * @param string $sendPasswordByTalk
388
-	 * @param string $expireDate
389
-	 * @param string $label
390
-	 *
391
-	 * @return DataResponse
392
-	 * @throws NotFoundException
393
-	 * @throws OCSBadRequestException
394
-	 * @throws OCSException
395
-	 * @throws OCSForbiddenException
396
-	 * @throws OCSNotFoundException
397
-	 * @throws InvalidPathException
398
-	 * @suppress PhanUndeclaredClassMethod
399
-	 */
400
-	public function createShare(
401
-		string $path = null,
402
-		int $permissions = null,
403
-		int $shareType = -1,
404
-		string $shareWith = null,
405
-		string $publicUpload = 'false',
406
-		string $password = '',
407
-		string $sendPasswordByTalk = null,
408
-		string $expireDate = '',
409
-		string $label = ''
410
-	): DataResponse {
411
-		$share = $this->shareManager->newShare();
412
-
413
-		if ($permissions === null) {
414
-			$permissions = $this->config->getAppValue('core', 'shareapi_default_permissions', Constants::PERMISSION_ALL);
415
-		}
416
-
417
-		// Verify path
418
-		if ($path === null) {
419
-			throw new OCSNotFoundException($this->l->t('Please specify a file or folder path'));
420
-		}
421
-
422
-		$userFolder = $this->rootFolder->getUserFolder($this->currentUser);
423
-		try {
424
-			$path = $userFolder->get($path);
425
-		} catch (NotFoundException $e) {
426
-			throw new OCSNotFoundException($this->l->t('Wrong path, file/folder doesn\'t exist'));
427
-		}
428
-
429
-		$share->setNode($path);
430
-
431
-		try {
432
-			$this->lock($share->getNode());
433
-		} catch (LockedException $e) {
434
-			throw new OCSNotFoundException($this->l->t('Could not create share'));
435
-		}
436
-
437
-		if ($permissions < 0 || $permissions > Constants::PERMISSION_ALL) {
438
-			throw new OCSNotFoundException($this->l->t('invalid permissions'));
439
-		}
440
-
441
-		// Shares always require read permissions
442
-		$permissions |= Constants::PERMISSION_READ;
443
-
444
-		if ($path instanceof \OCP\Files\File) {
445
-			// Single file shares should never have delete or create permissions
446
-			$permissions &= ~Constants::PERMISSION_DELETE;
447
-			$permissions &= ~Constants::PERMISSION_CREATE;
448
-		}
449
-
450
-		/**
451
-		 * Hack for https://github.com/owncloud/core/issues/22587
452
-		 * We check the permissions via webdav. But the permissions of the mount point
453
-		 * do not equal the share permissions. Here we fix that for federated mounts.
454
-		 */
455
-		if ($path->getStorage()->instanceOfStorage(Storage::class)) {
456
-			$permissions &= ~($permissions & ~$path->getPermissions());
457
-		}
458
-
459
-		if ($shareType === Share::SHARE_TYPE_USER) {
460
-			// Valid user is required to share
461
-			if ($shareWith === null || !$this->userManager->userExists($shareWith)) {
462
-				throw new OCSNotFoundException($this->l->t('Please specify a valid user'));
463
-			}
464
-			$share->setSharedWith($shareWith);
465
-			$share->setPermissions($permissions);
466
-		} elseif ($shareType === Share::SHARE_TYPE_GROUP) {
467
-			if (!$this->shareManager->allowGroupSharing()) {
468
-				throw new OCSNotFoundException($this->l->t('Group sharing is disabled by the administrator'));
469
-			}
470
-
471
-			// Valid group is required to share
472
-			if ($shareWith === null || !$this->groupManager->groupExists($shareWith)) {
473
-				throw new OCSNotFoundException($this->l->t('Please specify a valid group'));
474
-			}
475
-			$share->setSharedWith($shareWith);
476
-			$share->setPermissions($permissions);
477
-		} elseif ($shareType === Share::SHARE_TYPE_LINK
478
-			|| $shareType === Share::SHARE_TYPE_EMAIL) {
479
-
480
-			// Can we even share links?
481
-			if (!$this->shareManager->shareApiAllowLinks()) {
482
-				throw new OCSNotFoundException($this->l->t('Public link sharing is disabled by the administrator'));
483
-			}
484
-
485
-			if ($publicUpload === 'true') {
486
-				// Check if public upload is allowed
487
-				if (!$this->shareManager->shareApiLinkAllowPublicUpload()) {
488
-					throw new OCSForbiddenException($this->l->t('Public upload disabled by the administrator'));
489
-				}
490
-
491
-				// Public upload can only be set for folders
492
-				if ($path instanceof \OCP\Files\File) {
493
-					throw new OCSNotFoundException($this->l->t('Public upload is only possible for publicly shared folders'));
494
-				}
495
-
496
-				$permissions = Constants::PERMISSION_READ |
497
-					Constants::PERMISSION_CREATE |
498
-					Constants::PERMISSION_UPDATE |
499
-					Constants::PERMISSION_DELETE;
500
-			} else {
501
-				$permissions = Constants::PERMISSION_READ;
502
-			}
503
-			// TODO: It might make sense to have a dedicated setting to allow/deny converting link shares into federated ones
504
-			if ($this->shareManager->outgoingServer2ServerSharesAllowed()) {
505
-				$permissions |= Constants::PERMISSION_SHARE;
506
-			}
507
-			$share->setPermissions($permissions);
508
-
509
-			// Set password
510
-			if ($password !== '') {
511
-				$share->setPassword($password);
512
-			}
513
-
514
-			// Only share by mail have a recipient
515
-			if ($shareType === Share::SHARE_TYPE_EMAIL) {
516
-				$share->setSharedWith($shareWith);
517
-			} else {
518
-				// Only link share have a label
519
-				if (!empty($label)) {
520
-					$share->setLabel($label);
521
-				}
522
-			}
523
-
524
-			if ($sendPasswordByTalk === 'true') {
525
-				if (!$this->appManager->isEnabledForUser('spreed')) {
526
-					throw new OCSForbiddenException($this->l->t('Sharing %s sending the password by Nextcloud Talk failed because Nextcloud Talk is not enabled', [$path->getPath()]));
527
-				}
528
-
529
-				$share->setSendPasswordByTalk(true);
530
-			}
531
-
532
-			//Expire date
533
-			if ($expireDate !== '') {
534
-				try {
535
-					$expireDate = $this->parseDate($expireDate);
536
-					$share->setExpirationDate($expireDate);
537
-				} catch (\Exception $e) {
538
-					throw new OCSNotFoundException($this->l->t('Invalid date, date format must be YYYY-MM-DD'));
539
-				}
540
-			}
541
-		} elseif ($shareType === Share::SHARE_TYPE_REMOTE) {
542
-			if (!$this->shareManager->outgoingServer2ServerSharesAllowed()) {
543
-				throw new OCSForbiddenException($this->l->t('Sharing %1$s failed because the back end does not allow shares from type %2$s', [$path->getPath(), $shareType]));
544
-			}
545
-
546
-			$share->setSharedWith($shareWith);
547
-			$share->setPermissions($permissions);
548
-		} elseif ($shareType === Share::SHARE_TYPE_REMOTE_GROUP) {
549
-			if (!$this->shareManager->outgoingServer2ServerGroupSharesAllowed()) {
550
-				throw new OCSForbiddenException($this->l->t('Sharing %1$s failed because the back end does not allow shares from type %2$s', [$path->getPath(), $shareType]));
551
-			}
552
-
553
-			$share->setSharedWith($shareWith);
554
-			$share->setPermissions($permissions);
555
-		} elseif ($shareType === Share::SHARE_TYPE_CIRCLE) {
556
-			if (!\OC::$server->getAppManager()->isEnabledForUser('circles') || !class_exists('\OCA\Circles\ShareByCircleProvider')) {
557
-				throw new OCSNotFoundException($this->l->t('You cannot share to a Circle if the app is not enabled'));
558
-			}
559
-
560
-			$circle = \OCA\Circles\Api\v1\Circles::detailsCircle($shareWith);
561
-
562
-			// Valid circle is required to share
563
-			if ($circle === null) {
564
-				throw new OCSNotFoundException($this->l->t('Please specify a valid circle'));
565
-			}
566
-			$share->setSharedWith($shareWith);
567
-			$share->setPermissions($permissions);
568
-		} elseif ($shareType === Share::SHARE_TYPE_ROOM) {
569
-			try {
570
-				$this->getRoomShareHelper()->createShare($share, $shareWith, $permissions, $expireDate);
571
-			} catch (QueryException $e) {
572
-				throw new OCSForbiddenException($this->l->t('Sharing %s failed because the back end does not support room shares', [$path->getPath()]));
573
-			}
574
-		} else {
575
-			throw new OCSBadRequestException($this->l->t('Unknown share type'));
576
-		}
577
-
578
-		$share->setShareType($shareType);
579
-		$share->setSharedBy($this->currentUser);
580
-
581
-		try {
582
-			$share = $this->shareManager->createShare($share);
583
-		} catch (GenericShareException $e) {
584
-			$code = $e->getCode() === 0 ? 403 : $e->getCode();
585
-			throw new OCSException($e->getHint(), $code);
586
-		} catch (\Exception $e) {
587
-			throw new OCSForbiddenException($e->getMessage(), $e);
588
-		}
589
-
590
-		$output = $this->formatShare($share);
591
-
592
-		return new DataResponse($output);
593
-	}
594
-
595
-	/**
596
-	 * @param null|Node $node
597
-	 * @param boolean $includeTags
598
-	 *
599
-	 * @return array
600
-	 */
601
-	private function getSharedWithMe($node, bool $includeTags): array {
602
-		$userShares = $this->shareManager->getSharedWith($this->currentUser, Share::SHARE_TYPE_USER, $node, -1, 0);
603
-		$groupShares = $this->shareManager->getSharedWith($this->currentUser, Share::SHARE_TYPE_GROUP, $node, -1, 0);
604
-		$circleShares = $this->shareManager->getSharedWith($this->currentUser, Share::SHARE_TYPE_CIRCLE, $node, -1, 0);
605
-		$roomShares = $this->shareManager->getSharedWith($this->currentUser, Share::SHARE_TYPE_ROOM, $node, -1, 0);
606
-
607
-		$shares = array_merge($userShares, $groupShares, $circleShares, $roomShares);
608
-
609
-		$shares = array_filter($shares, function (IShare $share) {
610
-			return $share->getShareOwner() !== $this->currentUser;
611
-		});
612
-
613
-		$formatted = [];
614
-		foreach ($shares as $share) {
615
-			if ($this->canAccessShare($share)) {
616
-				try {
617
-					$formatted[] = $this->formatShare($share);
618
-				} catch (NotFoundException $e) {
619
-					// Ignore this share
620
-				}
621
-			}
622
-		}
623
-
624
-		if ($includeTags) {
625
-			$formatted = Helper::populateTags($formatted, 'file_source', \OC::$server->getTagManager());
626
-		}
627
-
628
-		return $formatted;
629
-	}
630
-
631
-	/**
632
-	 * @param \OCP\Files\Node $folder
633
-	 *
634
-	 * @return array
635
-	 * @throws OCSBadRequestException
636
-	 * @throws NotFoundException
637
-	 */
638
-	private function getSharesInDir(Node $folder): array {
639
-		if (!($folder instanceof \OCP\Files\Folder)) {
640
-			throw new OCSBadRequestException($this->l->t('Not a directory'));
641
-		}
642
-
643
-		$nodes = $folder->getDirectoryListing();
644
-
645
-		/** @var \OCP\Share\IShare[] $shares */
646
-		$shares = array_reduce($nodes, function ($carry, $node) {
647
-			$carry = array_merge($carry, $this->getAllShares($node, true));
648
-			return $carry;
649
-		}, []);
650
-
651
-		// filter out duplicate shares
652
-		$known = [];
653
-
654
-
655
-		$formatted = $miniFormatted = [];
656
-		$resharingRight = false;
657
-		$known = [];
658
-		foreach ($shares as $share) {
659
-			if (in_array($share->getId(), $known) || $share->getSharedWith() === $this->currentUser) {
660
-				continue;
661
-			}
662
-
663
-			try {
664
-				$format = $this->formatShare($share);
665
-
666
-				$known[] = $share->getId();
667
-				$formatted[] = $format;
668
-				if ($share->getSharedBy() === $this->currentUser) {
669
-					$miniFormatted[] = $format;
670
-				}
671
-				if (!$resharingRight && $this->shareProviderResharingRights($this->currentUser, $share, $folder)) {
672
-					$resharingRight = true;
673
-				}
674
-			} catch (\Exception $e) {
675
-				//Ignore this share
676
-			}
677
-		}
678
-
679
-		if (!$resharingRight) {
680
-			$formatted = $miniFormatted;
681
-		}
682
-
683
-		return $formatted;
684
-	}
685
-
686
-	/**
687
-	 * The getShares function.
688
-	 *
689
-	 * @NoAdminRequired
690
-	 *
691
-	 * @param string $shared_with_me
692
-	 * @param string $reshares
693
-	 * @param string $subfiles
694
-	 * @param string $path
695
-	 *
696
-	 * - Get shares by the current user
697
-	 * - Get shares by the current user and reshares (?reshares=true)
698
-	 * - Get shares with the current user (?shared_with_me=true)
699
-	 * - Get shares for a specific path (?path=...)
700
-	 * - Get all shares in a folder (?subfiles=true&path=..)
701
-	 *
702
-	 * @param string $include_tags
703
-	 *
704
-	 * @return DataResponse
705
-	 * @throws NotFoundException
706
-	 * @throws OCSBadRequestException
707
-	 * @throws OCSNotFoundException
708
-	 */
709
-	public function getShares(
710
-		string $shared_with_me = 'false',
711
-		string $reshares = 'false',
712
-		string $subfiles = 'false',
713
-		string $path = '',
714
-		string $include_tags = 'false'
715
-	): DataResponse {
716
-		$node = null;
717
-		if ($path !== '') {
718
-			$userFolder = $this->rootFolder->getUserFolder($this->currentUser);
719
-			try {
720
-				$node = $userFolder->get($path);
721
-				$this->lock($node);
722
-			} catch (NotFoundException $e) {
723
-				throw new OCSNotFoundException(
724
-					$this->l->t('Wrong path, file/folder doesn\'t exist')
725
-				);
726
-			} catch (LockedException $e) {
727
-				throw new OCSNotFoundException($this->l->t('Could not lock node'));
728
-			}
729
-		}
730
-
731
-		$shares = $this->getFormattedShares(
732
-			$this->currentUser,
733
-			$node,
734
-			($shared_with_me === 'true'),
735
-			($reshares === 'true'),
736
-			($subfiles === 'true'),
737
-			($include_tags === 'true')
738
-		);
739
-
740
-		return new DataResponse($shares);
741
-	}
742
-
743
-
744
-	/**
745
-	 * @param string $viewer
746
-	 * @param Node $node
747
-	 * @param bool $sharedWithMe
748
-	 * @param bool $reShares
749
-	 * @param bool $subFiles
750
-	 * @param bool $includeTags
751
-	 *
752
-	 * @return array
753
-	 * @throws NotFoundException
754
-	 * @throws OCSBadRequestException
755
-	 */
756
-	private function getFormattedShares(
757
-		string $viewer, $node = null, bool $sharedWithMe = false, bool $reShares = false,
758
-		bool $subFiles = false, bool $includeTags = false
759
-	): array {
760
-		if ($sharedWithMe) {
761
-			return $this->getSharedWithMe($node, $includeTags);
762
-		}
763
-
764
-		if ($subFiles) {
765
-			return $this->getSharesInDir($node);
766
-		}
767
-
768
-		$shares = $this->getSharesFromNode($viewer, $node, $reShares);
769
-
770
-		$known = $formatted = $miniFormatted = [];
771
-		$resharingRight = false;
772
-		foreach ($shares as $share) {
773
-			try {
774
-				$share->getNode();
775
-			} catch (NotFoundException $e) {
776
-				/*
80
+    /** @var IManager */
81
+    private $shareManager;
82
+    /** @var IGroupManager */
83
+    private $groupManager;
84
+    /** @var IUserManager */
85
+    private $userManager;
86
+    /** @var IRootFolder */
87
+    private $rootFolder;
88
+    /** @var IURLGenerator */
89
+    private $urlGenerator;
90
+    /** @var string */
91
+    private $currentUser;
92
+    /** @var IL10N */
93
+    private $l;
94
+    /** @var \OCP\Files\Node */
95
+    private $lockedNode;
96
+    /** @var IConfig */
97
+    private $config;
98
+    /** @var IAppManager */
99
+    private $appManager;
100
+    /** @var IServerContainer */
101
+    private $serverContainer;
102
+
103
+    /**
104
+     * Share20OCS constructor.
105
+     *
106
+     * @param string $appName
107
+     * @param IRequest $request
108
+     * @param IManager $shareManager
109
+     * @param IGroupManager $groupManager
110
+     * @param IUserManager $userManager
111
+     * @param IRootFolder $rootFolder
112
+     * @param IURLGenerator $urlGenerator
113
+     * @param string $userId
114
+     * @param IL10N $l10n
115
+     * @param IConfig $config
116
+     * @param IAppManager $appManager
117
+     * @param IServerContainer $serverContainer
118
+     */
119
+    public function __construct(
120
+        string $appName,
121
+        IRequest $request,
122
+        IManager $shareManager,
123
+        IGroupManager $groupManager,
124
+        IUserManager $userManager,
125
+        IRootFolder $rootFolder,
126
+        IURLGenerator $urlGenerator,
127
+        string $userId = null,
128
+        IL10N $l10n,
129
+        IConfig $config,
130
+        IAppManager $appManager,
131
+        IServerContainer $serverContainer
132
+    ) {
133
+        parent::__construct($appName, $request);
134
+
135
+        $this->shareManager = $shareManager;
136
+        $this->userManager = $userManager;
137
+        $this->groupManager = $groupManager;
138
+        $this->request = $request;
139
+        $this->rootFolder = $rootFolder;
140
+        $this->urlGenerator = $urlGenerator;
141
+        $this->currentUser = $userId;
142
+        $this->l = $l10n;
143
+        $this->config = $config;
144
+        $this->appManager = $appManager;
145
+        $this->serverContainer = $serverContainer;
146
+    }
147
+
148
+    /**
149
+     * Convert an IShare to an array for OCS output
150
+     *
151
+     * @param \OCP\Share\IShare $share
152
+     * @param Node|null $recipientNode
153
+     * @return array
154
+     * @throws NotFoundException In case the node can't be resolved.
155
+     *
156
+     * @suppress PhanUndeclaredClassMethod
157
+     */
158
+    protected function formatShare(\OCP\Share\IShare $share, Node $recipientNode = null): array {
159
+        $sharedBy = $this->userManager->get($share->getSharedBy());
160
+        $shareOwner = $this->userManager->get($share->getShareOwner());
161
+
162
+        $result = [
163
+            'id' => $share->getId(),
164
+            'share_type' => $share->getShareType(),
165
+            'uid_owner' => $share->getSharedBy(),
166
+            'displayname_owner' => $sharedBy !== null ? $sharedBy->getDisplayName() : $share->getSharedBy(),
167
+            // recipient permissions
168
+            'permissions' => $share->getPermissions(),
169
+            // current user permissions on this share
170
+            'can_edit' => $this->canEditShare($share),
171
+            'can_delete' => $this->canDeleteShare($share),
172
+            'stime' => $share->getShareTime()->getTimestamp(),
173
+            'parent' => null,
174
+            'expiration' => null,
175
+            'token' => null,
176
+            'uid_file_owner' => $share->getShareOwner(),
177
+            'note' => $share->getNote(),
178
+            'label' => $share->getLabel(),
179
+            'displayname_file_owner' => $shareOwner !== null ? $shareOwner->getDisplayName() : $share->getShareOwner(),
180
+        ];
181
+
182
+        $userFolder = $this->rootFolder->getUserFolder($this->currentUser);
183
+        if ($recipientNode) {
184
+            $node = $recipientNode;
185
+        } else {
186
+            $nodes = $userFolder->getById($share->getNodeId());
187
+            if (empty($nodes)) {
188
+                // fallback to guessing the path
189
+                $node = $userFolder->get($share->getTarget());
190
+                if ($node === null || $share->getTarget() === '') {
191
+                    throw new NotFoundException();
192
+                }
193
+            } else {
194
+                $node = reset($nodes);
195
+            }
196
+        }
197
+
198
+        $result['path'] = $userFolder->getRelativePath($node->getPath());
199
+        if ($node instanceof \OCP\Files\Folder) {
200
+            $result['item_type'] = 'folder';
201
+        } else {
202
+            $result['item_type'] = 'file';
203
+        }
204
+
205
+        $result['mimetype'] = $node->getMimetype();
206
+        $result['storage_id'] = $node->getStorage()->getId();
207
+        $result['storage'] = $node->getStorage()->getCache()->getNumericStorageId();
208
+        $result['item_source'] = $node->getId();
209
+        $result['file_source'] = $node->getId();
210
+        $result['file_parent'] = $node->getParent()->getId();
211
+        $result['file_target'] = $share->getTarget();
212
+
213
+        $expiration = $share->getExpirationDate();
214
+        if ($expiration !== null) {
215
+            $result['expiration'] = $expiration->format('Y-m-d 00:00:00');
216
+        }
217
+
218
+        if ($share->getShareType() === Share::SHARE_TYPE_USER) {
219
+            $sharedWith = $this->userManager->get($share->getSharedWith());
220
+            $result['share_with'] = $share->getSharedWith();
221
+            $result['share_with_displayname'] = $sharedWith !== null ? $sharedWith->getDisplayName() : $share->getSharedWith();
222
+        } elseif ($share->getShareType() === Share::SHARE_TYPE_GROUP) {
223
+            $group = $this->groupManager->get($share->getSharedWith());
224
+            $result['share_with'] = $share->getSharedWith();
225
+            $result['share_with_displayname'] = $group !== null ? $group->getDisplayName() : $share->getSharedWith();
226
+        } elseif ($share->getShareType() === IShare::TYPE_LINK) {
227
+
228
+            // "share_with" and "share_with_displayname" for passwords of link
229
+            // shares was deprecated in Nextcloud 15, use "password" instead.
230
+            $result['share_with'] = $share->getPassword();
231
+            $result['share_with_displayname'] = '(' . $this->l->t('Shared link') . ')';
232
+
233
+            $result['password'] = $share->getPassword();
234
+
235
+            $result['send_password_by_talk'] = $share->getSendPasswordByTalk();
236
+
237
+            $result['token'] = $share->getToken();
238
+            $result['url'] = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.showShare', ['token' => $share->getToken()]);
239
+        } elseif ($share->getShareType() === Share::SHARE_TYPE_REMOTE || $share->getShareType() === Share::SHARE_TYPE_REMOTE_GROUP) {
240
+            $result['share_with'] = $share->getSharedWith();
241
+            $result['share_with_displayname'] = $this->getDisplayNameFromAddressBook($share->getSharedWith(), 'CLOUD');
242
+            $result['token'] = $share->getToken();
243
+        } elseif ($share->getShareType() === Share::SHARE_TYPE_EMAIL) {
244
+            $result['share_with'] = $share->getSharedWith();
245
+            $result['password'] = $share->getPassword();
246
+            $result['send_password_by_talk'] = $share->getSendPasswordByTalk();
247
+            $result['share_with_displayname'] = $this->getDisplayNameFromAddressBook($share->getSharedWith(), 'EMAIL');
248
+            $result['token'] = $share->getToken();
249
+        } elseif ($share->getShareType() === Share::SHARE_TYPE_CIRCLE) {
250
+            // getSharedWith() returns either "name (type, owner)" or
251
+            // "name (type, owner) [id]", depending on the Circles app version.
252
+            $hasCircleId = (substr($share->getSharedWith(), -1) === ']');
253
+
254
+            $result['share_with_displayname'] = $share->getSharedWithDisplayName();
255
+            if (empty($result['share_with_displayname'])) {
256
+                $displayNameLength = ($hasCircleId ? strrpos($share->getSharedWith(), ' ') : strlen($share->getSharedWith()));
257
+                $result['share_with_displayname'] = substr($share->getSharedWith(), 0, $displayNameLength);
258
+            }
259
+
260
+            $result['share_with_avatar'] = $share->getSharedWithAvatar();
261
+
262
+            $shareWithStart = ($hasCircleId ? strrpos($share->getSharedWith(), '[') + 1 : 0);
263
+            $shareWithLength = ($hasCircleId ? -1 : strpos($share->getSharedWith(), ' '));
264
+            if (is_bool($shareWithLength)) {
265
+                $shareWithLength = -1;
266
+            }
267
+            $result['share_with'] = substr($share->getSharedWith(), $shareWithStart, $shareWithLength);
268
+        } elseif ($share->getShareType() === Share::SHARE_TYPE_ROOM) {
269
+            $result['share_with'] = $share->getSharedWith();
270
+            $result['share_with_displayname'] = '';
271
+
272
+            try {
273
+                $result = array_merge($result, $this->getRoomShareHelper()->formatShare($share));
274
+            } catch (QueryException $e) {
275
+            }
276
+        }
277
+
278
+
279
+        $result['mail_send'] = $share->getMailSend() ? 1 : 0;
280
+        $result['hide_download'] = $share->getHideDownload() ? 1 : 0;
281
+
282
+        return $result;
283
+    }
284
+
285
+    /**
286
+     * Check if one of the users address books knows the exact property, if
287
+     * yes we return the full name.
288
+     *
289
+     * @param string $query
290
+     * @param string $property
291
+     * @return string
292
+     */
293
+    private function getDisplayNameFromAddressBook(string $query, string $property): string {
294
+        // FIXME: If we inject the contacts manager it gets initialized bofore any address books are registered
295
+        $result = \OC::$server->getContactsManager()->search($query, [$property]);
296
+        foreach ($result as $r) {
297
+            foreach ($r[$property] as $value) {
298
+                if ($value === $query) {
299
+                    return $r['FN'];
300
+                }
301
+            }
302
+        }
303
+
304
+        return $query;
305
+    }
306
+
307
+    /**
308
+     * Get a specific share by id
309
+     *
310
+     * @NoAdminRequired
311
+     *
312
+     * @param string $id
313
+     * @return DataResponse
314
+     * @throws OCSNotFoundException
315
+     */
316
+    public function getShare(string $id): DataResponse {
317
+        try {
318
+            $share = $this->getShareById($id);
319
+        } catch (ShareNotFound $e) {
320
+            throw new OCSNotFoundException($this->l->t('Wrong share ID, share doesn\'t exist'));
321
+        }
322
+
323
+        try {
324
+            if ($this->canAccessShare($share)) {
325
+                $share = $this->formatShare($share);
326
+                return new DataResponse([$share]);
327
+            }
328
+        } catch (NotFoundException $e) {
329
+            // Fall trough
330
+        }
331
+
332
+        throw new OCSNotFoundException($this->l->t('Wrong share ID, share doesn\'t exist'));
333
+    }
334
+
335
+    /**
336
+     * Delete a share
337
+     *
338
+     * @NoAdminRequired
339
+     *
340
+     * @param string $id
341
+     * @return DataResponse
342
+     * @throws OCSNotFoundException
343
+     */
344
+    public function deleteShare(string $id): DataResponse {
345
+        try {
346
+            $share = $this->getShareById($id);
347
+        } catch (ShareNotFound $e) {
348
+            throw new OCSNotFoundException($this->l->t('Wrong share ID, share doesn\'t exist'));
349
+        }
350
+
351
+        try {
352
+            $this->lock($share->getNode());
353
+        } catch (LockedException $e) {
354
+            throw new OCSNotFoundException($this->l->t('Could not delete share'));
355
+        }
356
+
357
+        if (!$this->canAccessShare($share)) {
358
+            throw new OCSNotFoundException($this->l->t('Wrong share ID, share doesn\'t exist'));
359
+        }
360
+
361
+        // if it's a group share or a room share
362
+        // we don't delete the share, but only the
363
+        // mount point. Allowing it to be restored
364
+        // from the deleted shares
365
+        if ($this->canDeleteShareFromSelf($share)) {
366
+            $this->shareManager->deleteFromSelf($share, $this->currentUser);
367
+        } else {
368
+            if (!$this->canDeleteShare($share)) {
369
+                throw new OCSForbiddenException($this->l->t('Could not delete share'));
370
+            }
371
+
372
+            $this->shareManager->deleteShare($share);
373
+        }
374
+
375
+        return new DataResponse();
376
+    }
377
+
378
+    /**
379
+     * @NoAdminRequired
380
+     *
381
+     * @param string $path
382
+     * @param int $permissions
383
+     * @param int $shareType
384
+     * @param string $shareWith
385
+     * @param string $publicUpload
386
+     * @param string $password
387
+     * @param string $sendPasswordByTalk
388
+     * @param string $expireDate
389
+     * @param string $label
390
+     *
391
+     * @return DataResponse
392
+     * @throws NotFoundException
393
+     * @throws OCSBadRequestException
394
+     * @throws OCSException
395
+     * @throws OCSForbiddenException
396
+     * @throws OCSNotFoundException
397
+     * @throws InvalidPathException
398
+     * @suppress PhanUndeclaredClassMethod
399
+     */
400
+    public function createShare(
401
+        string $path = null,
402
+        int $permissions = null,
403
+        int $shareType = -1,
404
+        string $shareWith = null,
405
+        string $publicUpload = 'false',
406
+        string $password = '',
407
+        string $sendPasswordByTalk = null,
408
+        string $expireDate = '',
409
+        string $label = ''
410
+    ): DataResponse {
411
+        $share = $this->shareManager->newShare();
412
+
413
+        if ($permissions === null) {
414
+            $permissions = $this->config->getAppValue('core', 'shareapi_default_permissions', Constants::PERMISSION_ALL);
415
+        }
416
+
417
+        // Verify path
418
+        if ($path === null) {
419
+            throw new OCSNotFoundException($this->l->t('Please specify a file or folder path'));
420
+        }
421
+
422
+        $userFolder = $this->rootFolder->getUserFolder($this->currentUser);
423
+        try {
424
+            $path = $userFolder->get($path);
425
+        } catch (NotFoundException $e) {
426
+            throw new OCSNotFoundException($this->l->t('Wrong path, file/folder doesn\'t exist'));
427
+        }
428
+
429
+        $share->setNode($path);
430
+
431
+        try {
432
+            $this->lock($share->getNode());
433
+        } catch (LockedException $e) {
434
+            throw new OCSNotFoundException($this->l->t('Could not create share'));
435
+        }
436
+
437
+        if ($permissions < 0 || $permissions > Constants::PERMISSION_ALL) {
438
+            throw new OCSNotFoundException($this->l->t('invalid permissions'));
439
+        }
440
+
441
+        // Shares always require read permissions
442
+        $permissions |= Constants::PERMISSION_READ;
443
+
444
+        if ($path instanceof \OCP\Files\File) {
445
+            // Single file shares should never have delete or create permissions
446
+            $permissions &= ~Constants::PERMISSION_DELETE;
447
+            $permissions &= ~Constants::PERMISSION_CREATE;
448
+        }
449
+
450
+        /**
451
+         * Hack for https://github.com/owncloud/core/issues/22587
452
+         * We check the permissions via webdav. But the permissions of the mount point
453
+         * do not equal the share permissions. Here we fix that for federated mounts.
454
+         */
455
+        if ($path->getStorage()->instanceOfStorage(Storage::class)) {
456
+            $permissions &= ~($permissions & ~$path->getPermissions());
457
+        }
458
+
459
+        if ($shareType === Share::SHARE_TYPE_USER) {
460
+            // Valid user is required to share
461
+            if ($shareWith === null || !$this->userManager->userExists($shareWith)) {
462
+                throw new OCSNotFoundException($this->l->t('Please specify a valid user'));
463
+            }
464
+            $share->setSharedWith($shareWith);
465
+            $share->setPermissions($permissions);
466
+        } elseif ($shareType === Share::SHARE_TYPE_GROUP) {
467
+            if (!$this->shareManager->allowGroupSharing()) {
468
+                throw new OCSNotFoundException($this->l->t('Group sharing is disabled by the administrator'));
469
+            }
470
+
471
+            // Valid group is required to share
472
+            if ($shareWith === null || !$this->groupManager->groupExists($shareWith)) {
473
+                throw new OCSNotFoundException($this->l->t('Please specify a valid group'));
474
+            }
475
+            $share->setSharedWith($shareWith);
476
+            $share->setPermissions($permissions);
477
+        } elseif ($shareType === Share::SHARE_TYPE_LINK
478
+            || $shareType === Share::SHARE_TYPE_EMAIL) {
479
+
480
+            // Can we even share links?
481
+            if (!$this->shareManager->shareApiAllowLinks()) {
482
+                throw new OCSNotFoundException($this->l->t('Public link sharing is disabled by the administrator'));
483
+            }
484
+
485
+            if ($publicUpload === 'true') {
486
+                // Check if public upload is allowed
487
+                if (!$this->shareManager->shareApiLinkAllowPublicUpload()) {
488
+                    throw new OCSForbiddenException($this->l->t('Public upload disabled by the administrator'));
489
+                }
490
+
491
+                // Public upload can only be set for folders
492
+                if ($path instanceof \OCP\Files\File) {
493
+                    throw new OCSNotFoundException($this->l->t('Public upload is only possible for publicly shared folders'));
494
+                }
495
+
496
+                $permissions = Constants::PERMISSION_READ |
497
+                    Constants::PERMISSION_CREATE |
498
+                    Constants::PERMISSION_UPDATE |
499
+                    Constants::PERMISSION_DELETE;
500
+            } else {
501
+                $permissions = Constants::PERMISSION_READ;
502
+            }
503
+            // TODO: It might make sense to have a dedicated setting to allow/deny converting link shares into federated ones
504
+            if ($this->shareManager->outgoingServer2ServerSharesAllowed()) {
505
+                $permissions |= Constants::PERMISSION_SHARE;
506
+            }
507
+            $share->setPermissions($permissions);
508
+
509
+            // Set password
510
+            if ($password !== '') {
511
+                $share->setPassword($password);
512
+            }
513
+
514
+            // Only share by mail have a recipient
515
+            if ($shareType === Share::SHARE_TYPE_EMAIL) {
516
+                $share->setSharedWith($shareWith);
517
+            } else {
518
+                // Only link share have a label
519
+                if (!empty($label)) {
520
+                    $share->setLabel($label);
521
+                }
522
+            }
523
+
524
+            if ($sendPasswordByTalk === 'true') {
525
+                if (!$this->appManager->isEnabledForUser('spreed')) {
526
+                    throw new OCSForbiddenException($this->l->t('Sharing %s sending the password by Nextcloud Talk failed because Nextcloud Talk is not enabled', [$path->getPath()]));
527
+                }
528
+
529
+                $share->setSendPasswordByTalk(true);
530
+            }
531
+
532
+            //Expire date
533
+            if ($expireDate !== '') {
534
+                try {
535
+                    $expireDate = $this->parseDate($expireDate);
536
+                    $share->setExpirationDate($expireDate);
537
+                } catch (\Exception $e) {
538
+                    throw new OCSNotFoundException($this->l->t('Invalid date, date format must be YYYY-MM-DD'));
539
+                }
540
+            }
541
+        } elseif ($shareType === Share::SHARE_TYPE_REMOTE) {
542
+            if (!$this->shareManager->outgoingServer2ServerSharesAllowed()) {
543
+                throw new OCSForbiddenException($this->l->t('Sharing %1$s failed because the back end does not allow shares from type %2$s', [$path->getPath(), $shareType]));
544
+            }
545
+
546
+            $share->setSharedWith($shareWith);
547
+            $share->setPermissions($permissions);
548
+        } elseif ($shareType === Share::SHARE_TYPE_REMOTE_GROUP) {
549
+            if (!$this->shareManager->outgoingServer2ServerGroupSharesAllowed()) {
550
+                throw new OCSForbiddenException($this->l->t('Sharing %1$s failed because the back end does not allow shares from type %2$s', [$path->getPath(), $shareType]));
551
+            }
552
+
553
+            $share->setSharedWith($shareWith);
554
+            $share->setPermissions($permissions);
555
+        } elseif ($shareType === Share::SHARE_TYPE_CIRCLE) {
556
+            if (!\OC::$server->getAppManager()->isEnabledForUser('circles') || !class_exists('\OCA\Circles\ShareByCircleProvider')) {
557
+                throw new OCSNotFoundException($this->l->t('You cannot share to a Circle if the app is not enabled'));
558
+            }
559
+
560
+            $circle = \OCA\Circles\Api\v1\Circles::detailsCircle($shareWith);
561
+
562
+            // Valid circle is required to share
563
+            if ($circle === null) {
564
+                throw new OCSNotFoundException($this->l->t('Please specify a valid circle'));
565
+            }
566
+            $share->setSharedWith($shareWith);
567
+            $share->setPermissions($permissions);
568
+        } elseif ($shareType === Share::SHARE_TYPE_ROOM) {
569
+            try {
570
+                $this->getRoomShareHelper()->createShare($share, $shareWith, $permissions, $expireDate);
571
+            } catch (QueryException $e) {
572
+                throw new OCSForbiddenException($this->l->t('Sharing %s failed because the back end does not support room shares', [$path->getPath()]));
573
+            }
574
+        } else {
575
+            throw new OCSBadRequestException($this->l->t('Unknown share type'));
576
+        }
577
+
578
+        $share->setShareType($shareType);
579
+        $share->setSharedBy($this->currentUser);
580
+
581
+        try {
582
+            $share = $this->shareManager->createShare($share);
583
+        } catch (GenericShareException $e) {
584
+            $code = $e->getCode() === 0 ? 403 : $e->getCode();
585
+            throw new OCSException($e->getHint(), $code);
586
+        } catch (\Exception $e) {
587
+            throw new OCSForbiddenException($e->getMessage(), $e);
588
+        }
589
+
590
+        $output = $this->formatShare($share);
591
+
592
+        return new DataResponse($output);
593
+    }
594
+
595
+    /**
596
+     * @param null|Node $node
597
+     * @param boolean $includeTags
598
+     *
599
+     * @return array
600
+     */
601
+    private function getSharedWithMe($node, bool $includeTags): array {
602
+        $userShares = $this->shareManager->getSharedWith($this->currentUser, Share::SHARE_TYPE_USER, $node, -1, 0);
603
+        $groupShares = $this->shareManager->getSharedWith($this->currentUser, Share::SHARE_TYPE_GROUP, $node, -1, 0);
604
+        $circleShares = $this->shareManager->getSharedWith($this->currentUser, Share::SHARE_TYPE_CIRCLE, $node, -1, 0);
605
+        $roomShares = $this->shareManager->getSharedWith($this->currentUser, Share::SHARE_TYPE_ROOM, $node, -1, 0);
606
+
607
+        $shares = array_merge($userShares, $groupShares, $circleShares, $roomShares);
608
+
609
+        $shares = array_filter($shares, function (IShare $share) {
610
+            return $share->getShareOwner() !== $this->currentUser;
611
+        });
612
+
613
+        $formatted = [];
614
+        foreach ($shares as $share) {
615
+            if ($this->canAccessShare($share)) {
616
+                try {
617
+                    $formatted[] = $this->formatShare($share);
618
+                } catch (NotFoundException $e) {
619
+                    // Ignore this share
620
+                }
621
+            }
622
+        }
623
+
624
+        if ($includeTags) {
625
+            $formatted = Helper::populateTags($formatted, 'file_source', \OC::$server->getTagManager());
626
+        }
627
+
628
+        return $formatted;
629
+    }
630
+
631
+    /**
632
+     * @param \OCP\Files\Node $folder
633
+     *
634
+     * @return array
635
+     * @throws OCSBadRequestException
636
+     * @throws NotFoundException
637
+     */
638
+    private function getSharesInDir(Node $folder): array {
639
+        if (!($folder instanceof \OCP\Files\Folder)) {
640
+            throw new OCSBadRequestException($this->l->t('Not a directory'));
641
+        }
642
+
643
+        $nodes = $folder->getDirectoryListing();
644
+
645
+        /** @var \OCP\Share\IShare[] $shares */
646
+        $shares = array_reduce($nodes, function ($carry, $node) {
647
+            $carry = array_merge($carry, $this->getAllShares($node, true));
648
+            return $carry;
649
+        }, []);
650
+
651
+        // filter out duplicate shares
652
+        $known = [];
653
+
654
+
655
+        $formatted = $miniFormatted = [];
656
+        $resharingRight = false;
657
+        $known = [];
658
+        foreach ($shares as $share) {
659
+            if (in_array($share->getId(), $known) || $share->getSharedWith() === $this->currentUser) {
660
+                continue;
661
+            }
662
+
663
+            try {
664
+                $format = $this->formatShare($share);
665
+
666
+                $known[] = $share->getId();
667
+                $formatted[] = $format;
668
+                if ($share->getSharedBy() === $this->currentUser) {
669
+                    $miniFormatted[] = $format;
670
+                }
671
+                if (!$resharingRight && $this->shareProviderResharingRights($this->currentUser, $share, $folder)) {
672
+                    $resharingRight = true;
673
+                }
674
+            } catch (\Exception $e) {
675
+                //Ignore this share
676
+            }
677
+        }
678
+
679
+        if (!$resharingRight) {
680
+            $formatted = $miniFormatted;
681
+        }
682
+
683
+        return $formatted;
684
+    }
685
+
686
+    /**
687
+     * The getShares function.
688
+     *
689
+     * @NoAdminRequired
690
+     *
691
+     * @param string $shared_with_me
692
+     * @param string $reshares
693
+     * @param string $subfiles
694
+     * @param string $path
695
+     *
696
+     * - Get shares by the current user
697
+     * - Get shares by the current user and reshares (?reshares=true)
698
+     * - Get shares with the current user (?shared_with_me=true)
699
+     * - Get shares for a specific path (?path=...)
700
+     * - Get all shares in a folder (?subfiles=true&path=..)
701
+     *
702
+     * @param string $include_tags
703
+     *
704
+     * @return DataResponse
705
+     * @throws NotFoundException
706
+     * @throws OCSBadRequestException
707
+     * @throws OCSNotFoundException
708
+     */
709
+    public function getShares(
710
+        string $shared_with_me = 'false',
711
+        string $reshares = 'false',
712
+        string $subfiles = 'false',
713
+        string $path = '',
714
+        string $include_tags = 'false'
715
+    ): DataResponse {
716
+        $node = null;
717
+        if ($path !== '') {
718
+            $userFolder = $this->rootFolder->getUserFolder($this->currentUser);
719
+            try {
720
+                $node = $userFolder->get($path);
721
+                $this->lock($node);
722
+            } catch (NotFoundException $e) {
723
+                throw new OCSNotFoundException(
724
+                    $this->l->t('Wrong path, file/folder doesn\'t exist')
725
+                );
726
+            } catch (LockedException $e) {
727
+                throw new OCSNotFoundException($this->l->t('Could not lock node'));
728
+            }
729
+        }
730
+
731
+        $shares = $this->getFormattedShares(
732
+            $this->currentUser,
733
+            $node,
734
+            ($shared_with_me === 'true'),
735
+            ($reshares === 'true'),
736
+            ($subfiles === 'true'),
737
+            ($include_tags === 'true')
738
+        );
739
+
740
+        return new DataResponse($shares);
741
+    }
742
+
743
+
744
+    /**
745
+     * @param string $viewer
746
+     * @param Node $node
747
+     * @param bool $sharedWithMe
748
+     * @param bool $reShares
749
+     * @param bool $subFiles
750
+     * @param bool $includeTags
751
+     *
752
+     * @return array
753
+     * @throws NotFoundException
754
+     * @throws OCSBadRequestException
755
+     */
756
+    private function getFormattedShares(
757
+        string $viewer, $node = null, bool $sharedWithMe = false, bool $reShares = false,
758
+        bool $subFiles = false, bool $includeTags = false
759
+    ): array {
760
+        if ($sharedWithMe) {
761
+            return $this->getSharedWithMe($node, $includeTags);
762
+        }
763
+
764
+        if ($subFiles) {
765
+            return $this->getSharesInDir($node);
766
+        }
767
+
768
+        $shares = $this->getSharesFromNode($viewer, $node, $reShares);
769
+
770
+        $known = $formatted = $miniFormatted = [];
771
+        $resharingRight = false;
772
+        foreach ($shares as $share) {
773
+            try {
774
+                $share->getNode();
775
+            } catch (NotFoundException $e) {
776
+                /*
777 777
 				 * Ignore shares where we can't get the node
778 778
 				 * For example delted shares
779 779
 				 */
780
-				continue;
781
-			}
782
-
783
-			if (in_array($share->getId(), $known) || $share->getSharedWith() === $this->currentUser) {
784
-				continue;
785
-			}
786
-
787
-			$known[] = $share->getId();
788
-			try {
789
-				/** @var IShare $share */
790
-				$format = $this->formatShare($share, $node);
791
-				$formatted[] = $format;
792
-
793
-				// let's also build a list of shares created
794
-				// by the current user only, in case
795
-				// there is no resharing rights
796
-				if ($share->getSharedBy() === $this->currentUser) {
797
-					$miniFormatted[] = $format;
798
-				}
799
-
800
-				// check if one of those share is shared with me
801
-				// and if I have resharing rights on it
802
-				if (!$resharingRight && $this->shareProviderResharingRights($this->currentUser, $share, $node)) {
803
-					$resharingRight = true;
804
-				}
805
-			} catch (InvalidPathException | NotFoundException $e) {
806
-			}
807
-		}
808
-
809
-		if (!$resharingRight) {
810
-			$formatted = $miniFormatted;
811
-		}
812
-
813
-		if ($includeTags) {
814
-			$formatted =
815
-				Helper::populateTags($formatted, 'file_source', \OC::$server->getTagManager());
816
-		}
817
-
818
-		return $formatted;
819
-	}
820
-
821
-
822
-	/**
823
-	 * The getInheritedShares function.
824
-	 * returns all shares relative to a file, including parent folders shares rights.
825
-	 *
826
-	 * @NoAdminRequired
827
-	 *
828
-	 * @param string $path
829
-	 *
830
-	 * - Get shares by the current user
831
-	 * - Get shares by the current user and reshares (?reshares=true)
832
-	 * - Get shares with the current user (?shared_with_me=true)
833
-	 * - Get shares for a specific path (?path=...)
834
-	 * - Get all shares in a folder (?subfiles=true&path=..)
835
-	 *
836
-	 * @return DataResponse
837
-	 * @throws InvalidPathException
838
-	 * @throws NotFoundException
839
-	 * @throws OCSNotFoundException
840
-	 * @throws OCSBadRequestException
841
-	 * @throws SharingRightsException
842
-	 */
843
-	public function getInheritedShares(string $path): DataResponse {
844
-
845
-		// get Node from (string) path.
846
-		$userFolder = $this->rootFolder->getUserFolder($this->currentUser);
847
-		try {
848
-			$node = $userFolder->get($path);
849
-			$this->lock($node);
850
-		} catch (\OCP\Files\NotFoundException $e) {
851
-			throw new OCSNotFoundException($this->l->t('Wrong path, file/folder doesn\'t exist'));
852
-		} catch (LockedException $e) {
853
-			throw new OCSNotFoundException($this->l->t('Could not lock path'));
854
-		}
855
-
856
-		if (!($node->getPermissions() & Constants::PERMISSION_SHARE)) {
857
-			throw new SharingRightsException('no sharing rights on this item');
858
-		}
859
-
860
-		// The current top parent we have access to
861
-		$parent = $node;
862
-
863
-		// initiate real owner.
864
-		$owner = $node->getOwner()
865
-					  ->getUID();
866
-		if (!$this->userManager->userExists($owner)) {
867
-			return new DataResponse([]);
868
-		}
869
-
870
-		// get node based on the owner, fix owner in case of external storage
871
-		$userFolder = $this->rootFolder->getUserFolder($owner);
872
-		if ($node->getId() !== $userFolder->getId() && !$userFolder->isSubNode($node)) {
873
-			$owner = $node->getOwner()
874
-						  ->getUID();
875
-			$userFolder = $this->rootFolder->getUserFolder($owner);
876
-			$nodes = $userFolder->getById($node->getId());
877
-			$node = array_shift($nodes);
878
-		}
879
-		$basePath = $userFolder->getPath();
880
-
881
-		// generate node list for each parent folders
882
-		/** @var Node[] $nodes */
883
-		$nodes = [];
884
-		while ($node->getPath() !== $basePath) {
885
-			$node = $node->getParent();
886
-			$nodes[] = $node;
887
-		}
888
-
889
-		// The user that is requesting this list
890
-		$currentUserFolder = $this->rootFolder->getUserFolder($this->currentUser);
891
-
892
-		// for each nodes, retrieve shares.
893
-		$shares = [];
894
-
895
-		foreach ($nodes as $node) {
896
-			$getShares = $this->getFormattedShares($owner, $node, false, true);
897
-
898
-			$currentUserNodes = $currentUserFolder->getById($node->getId());
899
-			if (!empty($currentUserNodes)) {
900
-				$parent = array_pop($currentUserNodes);
901
-			}
902
-
903
-			$subPath = $currentUserFolder->getRelativePath($parent->getPath());
904
-			foreach ($getShares as &$share) {
905
-				$share['via_fileid'] = $parent->getId();
906
-				$share['via_path'] = $subPath;
907
-			}
908
-			$this->mergeFormattedShares($shares, $getShares);
909
-		}
910
-
911
-		return new DataResponse(array_values($shares));
912
-	}
913
-
914
-
915
-	/**
916
-	 * @NoAdminRequired
917
-	 *
918
-	 * @param string $id
919
-	 * @param int $permissions
920
-	 * @param string $password
921
-	 * @param string $sendPasswordByTalk
922
-	 * @param string $publicUpload
923
-	 * @param string $expireDate
924
-	 * @param string $note
925
-	 * @param string $label
926
-	 * @param string $hideDownload
927
-	 * @return DataResponse
928
-	 * @throws LockedException
929
-	 * @throws NotFoundException
930
-	 * @throws OCSBadRequestException
931
-	 * @throws OCSForbiddenException
932
-	 * @throws OCSNotFoundException
933
-	 */
934
-	public function updateShare(
935
-		string $id,
936
-		int $permissions = null,
937
-		string $password = null,
938
-		string $sendPasswordByTalk = null,
939
-		string $publicUpload = null,
940
-		string $expireDate = null,
941
-		string $note = null,
942
-		string $label = null,
943
-		string $hideDownload = null
944
-	): DataResponse {
945
-		try {
946
-			$share = $this->getShareById($id);
947
-		} catch (ShareNotFound $e) {
948
-			throw new OCSNotFoundException($this->l->t('Wrong share ID, share doesn\'t exist'));
949
-		}
950
-
951
-		$this->lock($share->getNode());
952
-
953
-		if (!$this->canAccessShare($share, false)) {
954
-			throw new OCSNotFoundException($this->l->t('Wrong share ID, share doesn\'t exist'));
955
-		}
956
-
957
-		if (!$this->canEditShare($share)) {
958
-			throw new OCSForbiddenException('You are not allowed to edit incoming shares');
959
-		}
960
-
961
-		if (
962
-			$permissions === null &&
963
-			$password === null &&
964
-			$sendPasswordByTalk === null &&
965
-			$publicUpload === null &&
966
-			$expireDate === null &&
967
-			$note === null &&
968
-			$label === null &&
969
-			$hideDownload === null
970
-		) {
971
-			throw new OCSBadRequestException($this->l->t('Wrong or no update parameter given'));
972
-		}
973
-
974
-		if ($note !== null) {
975
-			$share->setNote($note);
976
-		}
977
-
978
-		/**
979
-		 * expirationdate, password and publicUpload only make sense for link shares
980
-		 */
981
-		if ($share->getShareType() === Share::SHARE_TYPE_LINK
982
-			|| $share->getShareType() === Share::SHARE_TYPE_EMAIL) {
983
-
984
-			/**
985
-			 * We do not allow editing link shares that the current user
986
-			 * doesn't own. This is confusing and lead to errors when
987
-			 * someone else edit a password or expiration date without
988
-			 * the share owner knowing about it.
989
-			 * We only allow deletion
990
-			 */
991
-
992
-			if ($share->getSharedBy() !== $this->currentUser) {
993
-				throw new OCSForbiddenException('You are not allowed to edit link shares that you don\'t own');
994
-			}
995
-
996
-			// Update hide download state
997
-			if ($hideDownload === 'true') {
998
-				$share->setHideDownload(true);
999
-			} elseif ($hideDownload === 'false') {
1000
-				$share->setHideDownload(false);
1001
-			}
1002
-
1003
-			$newPermissions = null;
1004
-			if ($publicUpload === 'true') {
1005
-				$newPermissions = Constants::PERMISSION_READ | Constants::PERMISSION_CREATE | Constants::PERMISSION_UPDATE | Constants::PERMISSION_DELETE;
1006
-			} elseif ($publicUpload === 'false') {
1007
-				$newPermissions = Constants::PERMISSION_READ;
1008
-			}
1009
-
1010
-			if ($permissions !== null) {
1011
-				$newPermissions = (int) $permissions;
1012
-				$newPermissions = $newPermissions & ~Constants::PERMISSION_SHARE;
1013
-			}
1014
-
1015
-			if ($newPermissions !== null &&
1016
-				!in_array($newPermissions, [
1017
-					Constants::PERMISSION_READ,
1018
-					Constants::PERMISSION_READ | Constants::PERMISSION_CREATE | Constants::PERMISSION_UPDATE, // legacy
1019
-					Constants::PERMISSION_READ | Constants::PERMISSION_CREATE | Constants::PERMISSION_UPDATE | Constants::PERMISSION_DELETE, // correct
1020
-					Constants::PERMISSION_CREATE, // hidden file list
1021
-					Constants::PERMISSION_READ | Constants::PERMISSION_UPDATE, // allow to edit single files
1022
-				], true)
1023
-			) {
1024
-				throw new OCSBadRequestException($this->l->t('Can\'t change permissions for public share links'));
1025
-			}
1026
-
1027
-			if (
1028
-				// legacy
1029
-				$newPermissions === (Constants::PERMISSION_READ | Constants::PERMISSION_CREATE | Constants::PERMISSION_UPDATE) ||
1030
-				// correct
1031
-				$newPermissions === (Constants::PERMISSION_READ | Constants::PERMISSION_CREATE | Constants::PERMISSION_UPDATE | Constants::PERMISSION_DELETE)
1032
-			) {
1033
-				if (!$this->shareManager->shareApiLinkAllowPublicUpload()) {
1034
-					throw new OCSForbiddenException($this->l->t('Public upload disabled by the administrator'));
1035
-				}
1036
-
1037
-				if (!($share->getNode() instanceof \OCP\Files\Folder)) {
1038
-					throw new OCSBadRequestException($this->l->t('Public upload is only possible for publicly shared folders'));
1039
-				}
1040
-
1041
-				// normalize to correct public upload permissions
1042
-				$newPermissions = Constants::PERMISSION_READ | Constants::PERMISSION_CREATE | Constants::PERMISSION_UPDATE | Constants::PERMISSION_DELETE;
1043
-			}
1044
-
1045
-			if ($newPermissions !== null) {
1046
-				$share->setPermissions($newPermissions);
1047
-				$permissions = $newPermissions;
1048
-			}
1049
-
1050
-			if ($expireDate === '') {
1051
-				$share->setExpirationDate(null);
1052
-			} elseif ($expireDate !== null) {
1053
-				try {
1054
-					$expireDate = $this->parseDate($expireDate);
1055
-				} catch (\Exception $e) {
1056
-					throw new OCSBadRequestException($e->getMessage(), $e);
1057
-				}
1058
-				$share->setExpirationDate($expireDate);
1059
-			}
1060
-
1061
-			if ($password === '') {
1062
-				$share->setPassword(null);
1063
-			} elseif ($password !== null) {
1064
-				$share->setPassword($password);
1065
-			}
1066
-
1067
-			// only link shares have labels
1068
-			if ($share->getShareType() === Share::SHARE_TYPE_LINK && $label !== null) {
1069
-				$share->setLabel($label);
1070
-			}
1071
-
1072
-			if ($sendPasswordByTalk === 'true') {
1073
-				if (!$this->appManager->isEnabledForUser('spreed')) {
1074
-					throw new OCSForbiddenException($this->l->t('Sharing sending the password by Nextcloud Talk failed because Nextcloud Talk is not enabled'));
1075
-				}
1076
-
1077
-				$share->setSendPasswordByTalk(true);
1078
-			} elseif ($sendPasswordByTalk !== null) {
1079
-				$share->setSendPasswordByTalk(false);
1080
-			}
1081
-		}
1082
-
1083
-		// NOT A LINK SHARE
1084
-		else {
1085
-			if ($permissions !== null) {
1086
-				$permissions = (int) $permissions;
1087
-				$share->setPermissions($permissions);
1088
-			}
1089
-
1090
-			if ($expireDate === '') {
1091
-				$share->setExpirationDate(null);
1092
-			} elseif ($expireDate !== null) {
1093
-				try {
1094
-					$expireDate = $this->parseDate($expireDate);
1095
-				} catch (\Exception $e) {
1096
-					throw new OCSBadRequestException($e->getMessage(), $e);
1097
-				}
1098
-				$share->setExpirationDate($expireDate);
1099
-			}
1100
-		}
1101
-
1102
-		try {
1103
-			$share = $this->shareManager->updateShare($share);
1104
-		} catch (GenericShareException $e) {
1105
-			$code = $e->getCode() === 0 ? 403 : $e->getCode();
1106
-			throw new OCSException($e->getHint(), $code);
1107
-		} catch (\Exception $e) {
1108
-			throw new OCSBadRequestException($e->getMessage(), $e);
1109
-		}
1110
-
1111
-		return new DataResponse($this->formatShare($share));
1112
-	}
1113
-
1114
-	/**
1115
-	 * @NoAdminRequired
1116
-	 */
1117
-	public function pendingShares(): DataResponse {
1118
-		$pendingShares = [];
1119
-
1120
-		$shareTypes = [
1121
-			IShare::TYPE_USER,
1122
-			IShare::TYPE_GROUP
1123
-		];
1124
-
1125
-		foreach ($shareTypes as $shareType) {
1126
-			$shares = $this->shareManager->getSharedWith($this->currentUser, $shareType, null, -1, 0);
1127
-
1128
-			foreach ($shares as $share) {
1129
-				if ($share->getStatus() === IShare::STATUS_PENDING || $share->getStatus() === IShare::STATUS_REJECTED) {
1130
-					$pendingShares[] = $share;
1131
-				}
1132
-			}
1133
-		}
1134
-
1135
-		$result = array_filter(array_map(function (IShare $share) {
1136
-			$userFolder = $this->rootFolder->getUserFolder($share->getSharedBy());
1137
-			$nodes = $userFolder->getById($share->getNodeId());
1138
-			if (empty($nodes)) {
1139
-				// fallback to guessing the path
1140
-				$node = $userFolder->get($share->getTarget());
1141
-				if ($node === null || $share->getTarget() === '') {
1142
-					return null;
1143
-				}
1144
-			} else {
1145
-				$node = $nodes[0];
1146
-			}
1147
-
1148
-			try {
1149
-				$formattedShare = $this->formatShare($share, $node);
1150
-				$formattedShare['status'] = $share->getStatus();
1151
-				$formattedShare['path'] = $share->getNode()->getName();
1152
-				$formattedShare['permissions'] = 0;
1153
-				return $formattedShare;
1154
-			} catch (NotFoundException $e) {
1155
-				return null;
1156
-			}
1157
-		}, $pendingShares), function ($entry) {
1158
-			return $entry !== null;
1159
-		});
1160
-
1161
-		return new DataResponse($result);
1162
-	}
1163
-
1164
-	/**
1165
-	 * @NoAdminRequired
1166
-	 *
1167
-	 * @param string $id
1168
-	 * @return DataResponse
1169
-	 * @throws OCSNotFoundException
1170
-	 * @throws OCSException
1171
-	 * @throws OCSBadRequestException
1172
-	 */
1173
-	public function acceptShare(string $id): DataResponse {
1174
-		try {
1175
-			$share = $this->getShareById($id);
1176
-		} catch (ShareNotFound $e) {
1177
-			throw new OCSNotFoundException($this->l->t('Wrong share ID, share doesn\'t exist'));
1178
-		}
1179
-
1180
-		if (!$this->canAccessShare($share)) {
1181
-			throw new OCSNotFoundException($this->l->t('Wrong share ID, share doesn\'t exist'));
1182
-		}
1183
-
1184
-		try {
1185
-			$this->shareManager->acceptShare($share, $this->currentUser);
1186
-		} catch (GenericShareException $e) {
1187
-			$code = $e->getCode() === 0 ? 403 : $e->getCode();
1188
-			throw new OCSException($e->getHint(), $code);
1189
-		} catch (\Exception $e) {
1190
-			throw new OCSBadRequestException($e->getMessage(), $e);
1191
-		}
1192
-
1193
-		return new DataResponse();
1194
-	}
1195
-
1196
-	/**
1197
-	 * Does the user have read permission on the share
1198
-	 *
1199
-	 * @param \OCP\Share\IShare $share the share to check
1200
-	 * @param boolean $checkGroups check groups as well?
1201
-	 * @return boolean
1202
-	 * @throws NotFoundException
1203
-	 *
1204
-	 * @suppress PhanUndeclaredClassMethod
1205
-	 */
1206
-	protected function canAccessShare(\OCP\Share\IShare $share, bool $checkGroups = true): bool {
1207
-		// A file with permissions 0 can't be accessed by us. So Don't show it
1208
-		if ($share->getPermissions() === 0) {
1209
-			return false;
1210
-		}
1211
-
1212
-		// Owner of the file and the sharer of the file can always get share
1213
-		if ($share->getShareOwner() === $this->currentUser
1214
-			|| $share->getSharedBy() === $this->currentUser) {
1215
-			return true;
1216
-		}
1217
-
1218
-		// If the share is shared with you, you can access it!
1219
-		if ($share->getShareType() === Share::SHARE_TYPE_USER
1220
-			&& $share->getSharedWith() === $this->currentUser) {
1221
-			return true;
1222
-		}
1223
-
1224
-		// Have reshare rights on the shared file/folder ?
1225
-		// Does the currentUser have access to the shared file?
1226
-		$userFolder = $this->rootFolder->getUserFolder($this->currentUser);
1227
-		$files = $userFolder->getById($share->getNodeId());
1228
-		if (!empty($files) && $this->shareProviderResharingRights($this->currentUser, $share, $files[0])) {
1229
-			return true;
1230
-		}
1231
-
1232
-		// If in the recipient group, you can see the share
1233
-		if ($checkGroups && $share->getShareType() === Share::SHARE_TYPE_GROUP) {
1234
-			$sharedWith = $this->groupManager->get($share->getSharedWith());
1235
-			$user = $this->userManager->get($this->currentUser);
1236
-			if ($user !== null && $sharedWith !== null && $sharedWith->inGroup($user)) {
1237
-				return true;
1238
-			}
1239
-		}
1240
-
1241
-		if ($share->getShareType() === Share::SHARE_TYPE_CIRCLE) {
1242
-			// TODO: have a sanity check like above?
1243
-			return true;
1244
-		}
1245
-
1246
-		if ($share->getShareType() === Share::SHARE_TYPE_ROOM) {
1247
-			try {
1248
-				return $this->getRoomShareHelper()->canAccessShare($share, $this->currentUser);
1249
-			} catch (QueryException $e) {
1250
-				return false;
1251
-			}
1252
-		}
1253
-
1254
-		return false;
1255
-	}
1256
-
1257
-	/**
1258
-	 * Does the user have edit permission on the share
1259
-	 *
1260
-	 * @param \OCP\Share\IShare $share the share to check
1261
-	 * @return boolean
1262
-	 */
1263
-	protected function canEditShare(\OCP\Share\IShare $share): bool {
1264
-		// A file with permissions 0 can't be accessed by us. So Don't show it
1265
-		if ($share->getPermissions() === 0) {
1266
-			return false;
1267
-		}
1268
-
1269
-		// The owner of the file and the creator of the share
1270
-		// can always edit the share
1271
-		if ($share->getShareOwner() === $this->currentUser ||
1272
-			$share->getSharedBy() === $this->currentUser
1273
-		) {
1274
-			return true;
1275
-		}
1276
-
1277
-		//! we do NOT support some kind of `admin` in groups.
1278
-		//! You cannot edit shares shared to a group you're
1279
-		//! a member of if you're not the share owner or the file owner!
1280
-
1281
-		return false;
1282
-	}
1283
-
1284
-	/**
1285
-	 * Does the user have delete permission on the share
1286
-	 *
1287
-	 * @param \OCP\Share\IShare $share the share to check
1288
-	 * @return boolean
1289
-	 */
1290
-	protected function canDeleteShare(\OCP\Share\IShare $share): bool {
1291
-		// A file with permissions 0 can't be accessed by us. So Don't show it
1292
-		if ($share->getPermissions() === 0) {
1293
-			return false;
1294
-		}
1295
-
1296
-		// if the user is the recipient, i can unshare
1297
-		// the share with self
1298
-		if ($share->getShareType() === Share::SHARE_TYPE_USER &&
1299
-			$share->getSharedWith() === $this->currentUser
1300
-		) {
1301
-			return true;
1302
-		}
1303
-
1304
-		// The owner of the file and the creator of the share
1305
-		// can always delete the share
1306
-		if ($share->getShareOwner() === $this->currentUser ||
1307
-			$share->getSharedBy() === $this->currentUser
1308
-		) {
1309
-			return true;
1310
-		}
1311
-
1312
-		return false;
1313
-	}
1314
-
1315
-	/**
1316
-	 * Does the user have delete permission on the share
1317
-	 * This differs from the canDeleteShare function as it only
1318
-	 * remove the share for the current user. It does NOT
1319
-	 * completely delete the share but only the mount point.
1320
-	 * It can then be restored from the deleted shares section.
1321
-	 *
1322
-	 * @param \OCP\Share\IShare $share the share to check
1323
-	 * @return boolean
1324
-	 *
1325
-	 * @suppress PhanUndeclaredClassMethod
1326
-	 */
1327
-	protected function canDeleteShareFromSelf(\OCP\Share\IShare $share): bool {
1328
-		if ($share->getShareType() !== IShare::TYPE_GROUP &&
1329
-			$share->getShareType() !== IShare::TYPE_ROOM
1330
-		) {
1331
-			return false;
1332
-		}
1333
-
1334
-		if ($share->getShareOwner() === $this->currentUser ||
1335
-			$share->getSharedBy() === $this->currentUser
1336
-		) {
1337
-			// Delete the whole share, not just for self
1338
-			return false;
1339
-		}
1340
-
1341
-		// If in the recipient group, you can delete the share from self
1342
-		if ($share->getShareType() === Share::SHARE_TYPE_GROUP) {
1343
-			$sharedWith = $this->groupManager->get($share->getSharedWith());
1344
-			$user = $this->userManager->get($this->currentUser);
1345
-			if ($user !== null && $sharedWith !== null && $sharedWith->inGroup($user)) {
1346
-				return true;
1347
-			}
1348
-		}
1349
-
1350
-		if ($share->getShareType() === Share::SHARE_TYPE_ROOM) {
1351
-			try {
1352
-				return $this->getRoomShareHelper()->canAccessShare($share, $this->currentUser);
1353
-			} catch (QueryException $e) {
1354
-				return false;
1355
-			}
1356
-		}
1357
-
1358
-		return false;
1359
-	}
1360
-
1361
-	/**
1362
-	 * Make sure that the passed date is valid ISO 8601
1363
-	 * So YYYY-MM-DD
1364
-	 * If not throw an exception
1365
-	 *
1366
-	 * @param string $expireDate
1367
-	 *
1368
-	 * @throws \Exception
1369
-	 * @return \DateTime
1370
-	 */
1371
-	private function parseDate(string $expireDate): \DateTime {
1372
-		try {
1373
-			$date = new \DateTime($expireDate);
1374
-		} catch (\Exception $e) {
1375
-			throw new \Exception('Invalid date. Format must be YYYY-MM-DD');
1376
-		}
1377
-
1378
-		if ($date === false) {
1379
-			throw new \Exception('Invalid date. Format must be YYYY-MM-DD');
1380
-		}
1381
-
1382
-		$date->setTime(0, 0, 0);
1383
-
1384
-		return $date;
1385
-	}
1386
-
1387
-	/**
1388
-	 * Since we have multiple providers but the OCS Share API v1 does
1389
-	 * not support this we need to check all backends.
1390
-	 *
1391
-	 * @param string $id
1392
-	 * @return \OCP\Share\IShare
1393
-	 * @throws ShareNotFound
1394
-	 */
1395
-	private function getShareById(string $id): IShare {
1396
-		$share = null;
1397
-
1398
-		// First check if it is an internal share.
1399
-		try {
1400
-			$share = $this->shareManager->getShareById('ocinternal:' . $id, $this->currentUser);
1401
-			return $share;
1402
-		} catch (ShareNotFound $e) {
1403
-			// Do nothing, just try the other share type
1404
-		}
1405
-
1406
-
1407
-		try {
1408
-			if ($this->shareManager->shareProviderExists(Share::SHARE_TYPE_CIRCLE)) {
1409
-				$share = $this->shareManager->getShareById('ocCircleShare:' . $id, $this->currentUser);
1410
-				return $share;
1411
-			}
1412
-		} catch (ShareNotFound $e) {
1413
-			// Do nothing, just try the other share type
1414
-		}
1415
-
1416
-		try {
1417
-			if ($this->shareManager->shareProviderExists(Share::SHARE_TYPE_EMAIL)) {
1418
-				$share = $this->shareManager->getShareById('ocMailShare:' . $id, $this->currentUser);
1419
-				return $share;
1420
-			}
1421
-		} catch (ShareNotFound $e) {
1422
-			// Do nothing, just try the other share type
1423
-		}
1424
-
1425
-		try {
1426
-			$share = $this->shareManager->getShareById('ocRoomShare:' . $id, $this->currentUser);
1427
-			return $share;
1428
-		} catch (ShareNotFound $e) {
1429
-			// Do nothing, just try the other share type
1430
-		}
1431
-
1432
-		if (!$this->shareManager->outgoingServer2ServerSharesAllowed()) {
1433
-			throw new ShareNotFound();
1434
-		}
1435
-		$share = $this->shareManager->getShareById('ocFederatedSharing:' . $id, $this->currentUser);
1436
-
1437
-		return $share;
1438
-	}
1439
-
1440
-	/**
1441
-	 * Lock a Node
1442
-	 *
1443
-	 * @param \OCP\Files\Node $node
1444
-	 * @throws LockedException
1445
-	 */
1446
-	private function lock(\OCP\Files\Node $node) {
1447
-		$node->lock(ILockingProvider::LOCK_SHARED);
1448
-		$this->lockedNode = $node;
1449
-	}
1450
-
1451
-	/**
1452
-	 * Cleanup the remaining locks
1453
-	 * @throws @LockedException
1454
-	 */
1455
-	public function cleanup() {
1456
-		if ($this->lockedNode !== null) {
1457
-			$this->lockedNode->unlock(ILockingProvider::LOCK_SHARED);
1458
-		}
1459
-	}
1460
-
1461
-	/**
1462
-	 * Returns the helper of ShareAPIController for room shares.
1463
-	 *
1464
-	 * If the Talk application is not enabled or the helper is not available
1465
-	 * a QueryException is thrown instead.
1466
-	 *
1467
-	 * @return \OCA\Talk\Share\Helper\ShareAPIController
1468
-	 * @throws QueryException
1469
-	 */
1470
-	private function getRoomShareHelper() {
1471
-		if (!$this->appManager->isEnabledForUser('spreed')) {
1472
-			throw new QueryException();
1473
-		}
1474
-
1475
-		return $this->serverContainer->query('\OCA\Talk\Share\Helper\ShareAPIController');
1476
-	}
1477
-
1478
-
1479
-	/**
1480
-	 * @param string $viewer
1481
-	 * @param Node $node
1482
-	 * @param bool $reShares
1483
-	 *
1484
-	 * @return IShare[]
1485
-	 */
1486
-	private function getSharesFromNode(string $viewer, $node, bool $reShares): array {
1487
-		$providers = [
1488
-			Share::SHARE_TYPE_USER,
1489
-			Share::SHARE_TYPE_GROUP,
1490
-			Share::SHARE_TYPE_LINK,
1491
-			Share::SHARE_TYPE_EMAIL,
1492
-			Share::SHARE_TYPE_EMAIL,
1493
-			Share::SHARE_TYPE_CIRCLE,
1494
-			Share::SHARE_TYPE_ROOM
1495
-		];
1496
-
1497
-		// Should we assume that the (currentUser) viewer is the owner of the node !?
1498
-		$shares = [];
1499
-		foreach ($providers as $provider) {
1500
-			if (!$this->shareManager->shareProviderExists($provider)) {
1501
-				continue;
1502
-			}
1503
-
1504
-			$providerShares =
1505
-				$this->shareManager->getSharesBy($viewer, $provider, $node, $reShares, -1, 0);
1506
-			$shares = array_merge($shares, $providerShares);
1507
-		}
1508
-
1509
-		if ($this->shareManager->outgoingServer2ServerSharesAllowed()) {
1510
-			$federatedShares = $this->shareManager->getSharesBy(
1511
-				$this->currentUser, Share::SHARE_TYPE_REMOTE, $node, $reShares, -1, 0
1512
-			);
1513
-			$shares = array_merge($shares, $federatedShares);
1514
-		}
1515
-
1516
-		if ($this->shareManager->outgoingServer2ServerGroupSharesAllowed()) {
1517
-			$federatedShares = $this->shareManager->getSharesBy(
1518
-				$this->currentUser, Share::SHARE_TYPE_REMOTE_GROUP, $node, $reShares, -1, 0
1519
-			);
1520
-			$shares = array_merge($shares, $federatedShares);
1521
-		}
1522
-
1523
-		return $shares;
1524
-	}
1525
-
1526
-
1527
-	/**
1528
-	 * @param Node $node
1529
-	 *
1530
-	 * @throws SharingRightsException
1531
-	 */
1532
-	private function confirmSharingRights(Node $node): void {
1533
-		if (!$this->hasResharingRights($this->currentUser, $node)) {
1534
-			throw new SharingRightsException('no sharing rights on this item');
1535
-		}
1536
-	}
1537
-
1538
-
1539
-	/**
1540
-	 * @param string $viewer
1541
-	 * @param Node $node
1542
-	 *
1543
-	 * @return bool
1544
-	 */
1545
-	private function hasResharingRights($viewer, $node): bool {
1546
-		if ($viewer === $node->getOwner()->getUID()) {
1547
-			return true;
1548
-		}
1549
-
1550
-		foreach ([$node, $node->getParent()] as $node) {
1551
-			$shares = $this->getSharesFromNode($viewer, $node, true);
1552
-			foreach ($shares as $share) {
1553
-				try {
1554
-					if ($this->shareProviderResharingRights($viewer, $share, $node)) {
1555
-						return true;
1556
-					}
1557
-				} catch (InvalidPathException | NotFoundException $e) {
1558
-				}
1559
-			}
1560
-		}
1561
-
1562
-		return false;
1563
-	}
1564
-
1565
-
1566
-	/**
1567
-	 * Returns if we can find resharing rights in an IShare object for a specific user.
1568
-	 *
1569
-	 * @suppress PhanUndeclaredClassMethod
1570
-	 *
1571
-	 * @param string $userId
1572
-	 * @param IShare $share
1573
-	 * @param Node $node
1574
-	 *
1575
-	 * @return bool
1576
-	 * @throws NotFoundException
1577
-	 * @throws InvalidPathException
1578
-	 */
1579
-	private function shareProviderResharingRights(string $userId, IShare $share, $node): bool {
1580
-		if ($share->getShareOwner() === $userId) {
1581
-			return true;
1582
-		}
1583
-
1584
-		// we check that current user have parent resharing rights on the current file
1585
-		if ($node !== null && ($node->getPermissions() & Constants::PERMISSION_SHARE) !== 0) {
1586
-			return true;
1587
-		}
1588
-
1589
-		if ((\OCP\Constants::PERMISSION_SHARE & $share->getPermissions()) === 0) {
1590
-			return false;
1591
-		}
1592
-
1593
-		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER && $share->getSharedWith() === $userId) {
1594
-			return true;
1595
-		}
1596
-
1597
-		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP && $this->groupManager->isInGroup($userId, $share->getSharedWith())) {
1598
-			return true;
1599
-		}
1600
-
1601
-		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_CIRCLE && \OC::$server->getAppManager()->isEnabledForUser('circles')
1602
-			&& class_exists('\OCA\Circles\Api\v1\Circles')) {
1603
-			$hasCircleId = (substr($share->getSharedWith(), -1) === ']');
1604
-			$shareWithStart = ($hasCircleId ? strrpos($share->getSharedWith(), '[') + 1 : 0);
1605
-			$shareWithLength = ($hasCircleId ? -1 : strpos($share->getSharedWith(), ' '));
1606
-			if (is_bool($shareWithLength)) {
1607
-				$shareWithLength = -1;
1608
-			}
1609
-			$sharedWith = substr($share->getSharedWith(), $shareWithStart, $shareWithLength);
1610
-			try {
1611
-				$member = \OCA\Circles\Api\v1\Circles::getMember($sharedWith, $userId, 1);
1612
-				if ($member->getLevel() >= 4) {
1613
-					return true;
1614
-				}
1615
-				return false;
1616
-			} catch (QueryException $e) {
1617
-				return false;
1618
-			}
1619
-		}
1620
-
1621
-		return false;
1622
-	}
1623
-
1624
-	/**
1625
-	 * Get all the shares for the current user
1626
-	 *
1627
-	 * @param Node|null $path
1628
-	 * @param boolean $reshares
1629
-	 * @return void
1630
-	 */
1631
-	private function getAllShares(?Node $path = null, bool $reshares = false) {
1632
-		// Get all shares
1633
-		$userShares = $this->shareManager->getSharesBy($this->currentUser, Share::SHARE_TYPE_USER, $path, $reshares, -1, 0);
1634
-		$groupShares = $this->shareManager->getSharesBy($this->currentUser, Share::SHARE_TYPE_GROUP, $path, $reshares, -1, 0);
1635
-		$linkShares = $this->shareManager->getSharesBy($this->currentUser, Share::SHARE_TYPE_LINK, $path, $reshares, -1, 0);
1636
-
1637
-		// EMAIL SHARES
1638
-		$mailShares = $this->shareManager->getSharesBy($this->currentUser, Share::SHARE_TYPE_EMAIL, $path, $reshares, -1, 0);
1639
-
1640
-		// CIRCLE SHARES
1641
-		$circleShares = $this->shareManager->getSharesBy($this->currentUser, Share::SHARE_TYPE_CIRCLE, $path, $reshares, -1, 0);
1642
-
1643
-		// TALK SHARES
1644
-		$roomShares = $this->shareManager->getSharesBy($this->currentUser, Share::SHARE_TYPE_ROOM, $path, $reshares, -1, 0);
1645
-
1646
-		// FEDERATION
1647
-		if ($this->shareManager->outgoingServer2ServerSharesAllowed()) {
1648
-			$federatedShares = $this->shareManager->getSharesBy($this->currentUser, Share::SHARE_TYPE_REMOTE, $path, $reshares, -1, 0);
1649
-		} else {
1650
-			$federatedShares = [];
1651
-		}
1652
-		if ($this->shareManager->outgoingServer2ServerGroupSharesAllowed()) {
1653
-			$federatedGroupShares = $this->shareManager->getSharesBy($this->currentUser, Share::SHARE_TYPE_REMOTE_GROUP, $path, $reshares, -1, 0);
1654
-		} else {
1655
-			$federatedGroupShares = [];
1656
-		}
1657
-
1658
-		return array_merge($userShares, $groupShares, $linkShares, $mailShares, $circleShares, $roomShares, $federatedShares, $federatedGroupShares);
1659
-	}
1660
-
1661
-
1662
-	/**
1663
-	 * merging already formatted shares.
1664
-	 * We'll make an associative array to easily detect duplicate Ids.
1665
-	 * Keys _needs_ to be removed after all shares are retrieved and merged.
1666
-	 *
1667
-	 * @param array $shares
1668
-	 * @param array $newShares
1669
-	 */
1670
-	private function mergeFormattedShares(array &$shares, array $newShares) {
1671
-		foreach ($newShares as $newShare) {
1672
-			if (!array_key_exists($newShare['id'], $shares)) {
1673
-				$shares[$newShare['id']] = $newShare;
1674
-			}
1675
-		}
1676
-	}
780
+                continue;
781
+            }
782
+
783
+            if (in_array($share->getId(), $known) || $share->getSharedWith() === $this->currentUser) {
784
+                continue;
785
+            }
786
+
787
+            $known[] = $share->getId();
788
+            try {
789
+                /** @var IShare $share */
790
+                $format = $this->formatShare($share, $node);
791
+                $formatted[] = $format;
792
+
793
+                // let's also build a list of shares created
794
+                // by the current user only, in case
795
+                // there is no resharing rights
796
+                if ($share->getSharedBy() === $this->currentUser) {
797
+                    $miniFormatted[] = $format;
798
+                }
799
+
800
+                // check if one of those share is shared with me
801
+                // and if I have resharing rights on it
802
+                if (!$resharingRight && $this->shareProviderResharingRights($this->currentUser, $share, $node)) {
803
+                    $resharingRight = true;
804
+                }
805
+            } catch (InvalidPathException | NotFoundException $e) {
806
+            }
807
+        }
808
+
809
+        if (!$resharingRight) {
810
+            $formatted = $miniFormatted;
811
+        }
812
+
813
+        if ($includeTags) {
814
+            $formatted =
815
+                Helper::populateTags($formatted, 'file_source', \OC::$server->getTagManager());
816
+        }
817
+
818
+        return $formatted;
819
+    }
820
+
821
+
822
+    /**
823
+     * The getInheritedShares function.
824
+     * returns all shares relative to a file, including parent folders shares rights.
825
+     *
826
+     * @NoAdminRequired
827
+     *
828
+     * @param string $path
829
+     *
830
+     * - Get shares by the current user
831
+     * - Get shares by the current user and reshares (?reshares=true)
832
+     * - Get shares with the current user (?shared_with_me=true)
833
+     * - Get shares for a specific path (?path=...)
834
+     * - Get all shares in a folder (?subfiles=true&path=..)
835
+     *
836
+     * @return DataResponse
837
+     * @throws InvalidPathException
838
+     * @throws NotFoundException
839
+     * @throws OCSNotFoundException
840
+     * @throws OCSBadRequestException
841
+     * @throws SharingRightsException
842
+     */
843
+    public function getInheritedShares(string $path): DataResponse {
844
+
845
+        // get Node from (string) path.
846
+        $userFolder = $this->rootFolder->getUserFolder($this->currentUser);
847
+        try {
848
+            $node = $userFolder->get($path);
849
+            $this->lock($node);
850
+        } catch (\OCP\Files\NotFoundException $e) {
851
+            throw new OCSNotFoundException($this->l->t('Wrong path, file/folder doesn\'t exist'));
852
+        } catch (LockedException $e) {
853
+            throw new OCSNotFoundException($this->l->t('Could not lock path'));
854
+        }
855
+
856
+        if (!($node->getPermissions() & Constants::PERMISSION_SHARE)) {
857
+            throw new SharingRightsException('no sharing rights on this item');
858
+        }
859
+
860
+        // The current top parent we have access to
861
+        $parent = $node;
862
+
863
+        // initiate real owner.
864
+        $owner = $node->getOwner()
865
+                        ->getUID();
866
+        if (!$this->userManager->userExists($owner)) {
867
+            return new DataResponse([]);
868
+        }
869
+
870
+        // get node based on the owner, fix owner in case of external storage
871
+        $userFolder = $this->rootFolder->getUserFolder($owner);
872
+        if ($node->getId() !== $userFolder->getId() && !$userFolder->isSubNode($node)) {
873
+            $owner = $node->getOwner()
874
+                            ->getUID();
875
+            $userFolder = $this->rootFolder->getUserFolder($owner);
876
+            $nodes = $userFolder->getById($node->getId());
877
+            $node = array_shift($nodes);
878
+        }
879
+        $basePath = $userFolder->getPath();
880
+
881
+        // generate node list for each parent folders
882
+        /** @var Node[] $nodes */
883
+        $nodes = [];
884
+        while ($node->getPath() !== $basePath) {
885
+            $node = $node->getParent();
886
+            $nodes[] = $node;
887
+        }
888
+
889
+        // The user that is requesting this list
890
+        $currentUserFolder = $this->rootFolder->getUserFolder($this->currentUser);
891
+
892
+        // for each nodes, retrieve shares.
893
+        $shares = [];
894
+
895
+        foreach ($nodes as $node) {
896
+            $getShares = $this->getFormattedShares($owner, $node, false, true);
897
+
898
+            $currentUserNodes = $currentUserFolder->getById($node->getId());
899
+            if (!empty($currentUserNodes)) {
900
+                $parent = array_pop($currentUserNodes);
901
+            }
902
+
903
+            $subPath = $currentUserFolder->getRelativePath($parent->getPath());
904
+            foreach ($getShares as &$share) {
905
+                $share['via_fileid'] = $parent->getId();
906
+                $share['via_path'] = $subPath;
907
+            }
908
+            $this->mergeFormattedShares($shares, $getShares);
909
+        }
910
+
911
+        return new DataResponse(array_values($shares));
912
+    }
913
+
914
+
915
+    /**
916
+     * @NoAdminRequired
917
+     *
918
+     * @param string $id
919
+     * @param int $permissions
920
+     * @param string $password
921
+     * @param string $sendPasswordByTalk
922
+     * @param string $publicUpload
923
+     * @param string $expireDate
924
+     * @param string $note
925
+     * @param string $label
926
+     * @param string $hideDownload
927
+     * @return DataResponse
928
+     * @throws LockedException
929
+     * @throws NotFoundException
930
+     * @throws OCSBadRequestException
931
+     * @throws OCSForbiddenException
932
+     * @throws OCSNotFoundException
933
+     */
934
+    public function updateShare(
935
+        string $id,
936
+        int $permissions = null,
937
+        string $password = null,
938
+        string $sendPasswordByTalk = null,
939
+        string $publicUpload = null,
940
+        string $expireDate = null,
941
+        string $note = null,
942
+        string $label = null,
943
+        string $hideDownload = null
944
+    ): DataResponse {
945
+        try {
946
+            $share = $this->getShareById($id);
947
+        } catch (ShareNotFound $e) {
948
+            throw new OCSNotFoundException($this->l->t('Wrong share ID, share doesn\'t exist'));
949
+        }
950
+
951
+        $this->lock($share->getNode());
952
+
953
+        if (!$this->canAccessShare($share, false)) {
954
+            throw new OCSNotFoundException($this->l->t('Wrong share ID, share doesn\'t exist'));
955
+        }
956
+
957
+        if (!$this->canEditShare($share)) {
958
+            throw new OCSForbiddenException('You are not allowed to edit incoming shares');
959
+        }
960
+
961
+        if (
962
+            $permissions === null &&
963
+            $password === null &&
964
+            $sendPasswordByTalk === null &&
965
+            $publicUpload === null &&
966
+            $expireDate === null &&
967
+            $note === null &&
968
+            $label === null &&
969
+            $hideDownload === null
970
+        ) {
971
+            throw new OCSBadRequestException($this->l->t('Wrong or no update parameter given'));
972
+        }
973
+
974
+        if ($note !== null) {
975
+            $share->setNote($note);
976
+        }
977
+
978
+        /**
979
+         * expirationdate, password and publicUpload only make sense for link shares
980
+         */
981
+        if ($share->getShareType() === Share::SHARE_TYPE_LINK
982
+            || $share->getShareType() === Share::SHARE_TYPE_EMAIL) {
983
+
984
+            /**
985
+             * We do not allow editing link shares that the current user
986
+             * doesn't own. This is confusing and lead to errors when
987
+             * someone else edit a password or expiration date without
988
+             * the share owner knowing about it.
989
+             * We only allow deletion
990
+             */
991
+
992
+            if ($share->getSharedBy() !== $this->currentUser) {
993
+                throw new OCSForbiddenException('You are not allowed to edit link shares that you don\'t own');
994
+            }
995
+
996
+            // Update hide download state
997
+            if ($hideDownload === 'true') {
998
+                $share->setHideDownload(true);
999
+            } elseif ($hideDownload === 'false') {
1000
+                $share->setHideDownload(false);
1001
+            }
1002
+
1003
+            $newPermissions = null;
1004
+            if ($publicUpload === 'true') {
1005
+                $newPermissions = Constants::PERMISSION_READ | Constants::PERMISSION_CREATE | Constants::PERMISSION_UPDATE | Constants::PERMISSION_DELETE;
1006
+            } elseif ($publicUpload === 'false') {
1007
+                $newPermissions = Constants::PERMISSION_READ;
1008
+            }
1009
+
1010
+            if ($permissions !== null) {
1011
+                $newPermissions = (int) $permissions;
1012
+                $newPermissions = $newPermissions & ~Constants::PERMISSION_SHARE;
1013
+            }
1014
+
1015
+            if ($newPermissions !== null &&
1016
+                !in_array($newPermissions, [
1017
+                    Constants::PERMISSION_READ,
1018
+                    Constants::PERMISSION_READ | Constants::PERMISSION_CREATE | Constants::PERMISSION_UPDATE, // legacy
1019
+                    Constants::PERMISSION_READ | Constants::PERMISSION_CREATE | Constants::PERMISSION_UPDATE | Constants::PERMISSION_DELETE, // correct
1020
+                    Constants::PERMISSION_CREATE, // hidden file list
1021
+                    Constants::PERMISSION_READ | Constants::PERMISSION_UPDATE, // allow to edit single files
1022
+                ], true)
1023
+            ) {
1024
+                throw new OCSBadRequestException($this->l->t('Can\'t change permissions for public share links'));
1025
+            }
1026
+
1027
+            if (
1028
+                // legacy
1029
+                $newPermissions === (Constants::PERMISSION_READ | Constants::PERMISSION_CREATE | Constants::PERMISSION_UPDATE) ||
1030
+                // correct
1031
+                $newPermissions === (Constants::PERMISSION_READ | Constants::PERMISSION_CREATE | Constants::PERMISSION_UPDATE | Constants::PERMISSION_DELETE)
1032
+            ) {
1033
+                if (!$this->shareManager->shareApiLinkAllowPublicUpload()) {
1034
+                    throw new OCSForbiddenException($this->l->t('Public upload disabled by the administrator'));
1035
+                }
1036
+
1037
+                if (!($share->getNode() instanceof \OCP\Files\Folder)) {
1038
+                    throw new OCSBadRequestException($this->l->t('Public upload is only possible for publicly shared folders'));
1039
+                }
1040
+
1041
+                // normalize to correct public upload permissions
1042
+                $newPermissions = Constants::PERMISSION_READ | Constants::PERMISSION_CREATE | Constants::PERMISSION_UPDATE | Constants::PERMISSION_DELETE;
1043
+            }
1044
+
1045
+            if ($newPermissions !== null) {
1046
+                $share->setPermissions($newPermissions);
1047
+                $permissions = $newPermissions;
1048
+            }
1049
+
1050
+            if ($expireDate === '') {
1051
+                $share->setExpirationDate(null);
1052
+            } elseif ($expireDate !== null) {
1053
+                try {
1054
+                    $expireDate = $this->parseDate($expireDate);
1055
+                } catch (\Exception $e) {
1056
+                    throw new OCSBadRequestException($e->getMessage(), $e);
1057
+                }
1058
+                $share->setExpirationDate($expireDate);
1059
+            }
1060
+
1061
+            if ($password === '') {
1062
+                $share->setPassword(null);
1063
+            } elseif ($password !== null) {
1064
+                $share->setPassword($password);
1065
+            }
1066
+
1067
+            // only link shares have labels
1068
+            if ($share->getShareType() === Share::SHARE_TYPE_LINK && $label !== null) {
1069
+                $share->setLabel($label);
1070
+            }
1071
+
1072
+            if ($sendPasswordByTalk === 'true') {
1073
+                if (!$this->appManager->isEnabledForUser('spreed')) {
1074
+                    throw new OCSForbiddenException($this->l->t('Sharing sending the password by Nextcloud Talk failed because Nextcloud Talk is not enabled'));
1075
+                }
1076
+
1077
+                $share->setSendPasswordByTalk(true);
1078
+            } elseif ($sendPasswordByTalk !== null) {
1079
+                $share->setSendPasswordByTalk(false);
1080
+            }
1081
+        }
1082
+
1083
+        // NOT A LINK SHARE
1084
+        else {
1085
+            if ($permissions !== null) {
1086
+                $permissions = (int) $permissions;
1087
+                $share->setPermissions($permissions);
1088
+            }
1089
+
1090
+            if ($expireDate === '') {
1091
+                $share->setExpirationDate(null);
1092
+            } elseif ($expireDate !== null) {
1093
+                try {
1094
+                    $expireDate = $this->parseDate($expireDate);
1095
+                } catch (\Exception $e) {
1096
+                    throw new OCSBadRequestException($e->getMessage(), $e);
1097
+                }
1098
+                $share->setExpirationDate($expireDate);
1099
+            }
1100
+        }
1101
+
1102
+        try {
1103
+            $share = $this->shareManager->updateShare($share);
1104
+        } catch (GenericShareException $e) {
1105
+            $code = $e->getCode() === 0 ? 403 : $e->getCode();
1106
+            throw new OCSException($e->getHint(), $code);
1107
+        } catch (\Exception $e) {
1108
+            throw new OCSBadRequestException($e->getMessage(), $e);
1109
+        }
1110
+
1111
+        return new DataResponse($this->formatShare($share));
1112
+    }
1113
+
1114
+    /**
1115
+     * @NoAdminRequired
1116
+     */
1117
+    public function pendingShares(): DataResponse {
1118
+        $pendingShares = [];
1119
+
1120
+        $shareTypes = [
1121
+            IShare::TYPE_USER,
1122
+            IShare::TYPE_GROUP
1123
+        ];
1124
+
1125
+        foreach ($shareTypes as $shareType) {
1126
+            $shares = $this->shareManager->getSharedWith($this->currentUser, $shareType, null, -1, 0);
1127
+
1128
+            foreach ($shares as $share) {
1129
+                if ($share->getStatus() === IShare::STATUS_PENDING || $share->getStatus() === IShare::STATUS_REJECTED) {
1130
+                    $pendingShares[] = $share;
1131
+                }
1132
+            }
1133
+        }
1134
+
1135
+        $result = array_filter(array_map(function (IShare $share) {
1136
+            $userFolder = $this->rootFolder->getUserFolder($share->getSharedBy());
1137
+            $nodes = $userFolder->getById($share->getNodeId());
1138
+            if (empty($nodes)) {
1139
+                // fallback to guessing the path
1140
+                $node = $userFolder->get($share->getTarget());
1141
+                if ($node === null || $share->getTarget() === '') {
1142
+                    return null;
1143
+                }
1144
+            } else {
1145
+                $node = $nodes[0];
1146
+            }
1147
+
1148
+            try {
1149
+                $formattedShare = $this->formatShare($share, $node);
1150
+                $formattedShare['status'] = $share->getStatus();
1151
+                $formattedShare['path'] = $share->getNode()->getName();
1152
+                $formattedShare['permissions'] = 0;
1153
+                return $formattedShare;
1154
+            } catch (NotFoundException $e) {
1155
+                return null;
1156
+            }
1157
+        }, $pendingShares), function ($entry) {
1158
+            return $entry !== null;
1159
+        });
1160
+
1161
+        return new DataResponse($result);
1162
+    }
1163
+
1164
+    /**
1165
+     * @NoAdminRequired
1166
+     *
1167
+     * @param string $id
1168
+     * @return DataResponse
1169
+     * @throws OCSNotFoundException
1170
+     * @throws OCSException
1171
+     * @throws OCSBadRequestException
1172
+     */
1173
+    public function acceptShare(string $id): DataResponse {
1174
+        try {
1175
+            $share = $this->getShareById($id);
1176
+        } catch (ShareNotFound $e) {
1177
+            throw new OCSNotFoundException($this->l->t('Wrong share ID, share doesn\'t exist'));
1178
+        }
1179
+
1180
+        if (!$this->canAccessShare($share)) {
1181
+            throw new OCSNotFoundException($this->l->t('Wrong share ID, share doesn\'t exist'));
1182
+        }
1183
+
1184
+        try {
1185
+            $this->shareManager->acceptShare($share, $this->currentUser);
1186
+        } catch (GenericShareException $e) {
1187
+            $code = $e->getCode() === 0 ? 403 : $e->getCode();
1188
+            throw new OCSException($e->getHint(), $code);
1189
+        } catch (\Exception $e) {
1190
+            throw new OCSBadRequestException($e->getMessage(), $e);
1191
+        }
1192
+
1193
+        return new DataResponse();
1194
+    }
1195
+
1196
+    /**
1197
+     * Does the user have read permission on the share
1198
+     *
1199
+     * @param \OCP\Share\IShare $share the share to check
1200
+     * @param boolean $checkGroups check groups as well?
1201
+     * @return boolean
1202
+     * @throws NotFoundException
1203
+     *
1204
+     * @suppress PhanUndeclaredClassMethod
1205
+     */
1206
+    protected function canAccessShare(\OCP\Share\IShare $share, bool $checkGroups = true): bool {
1207
+        // A file with permissions 0 can't be accessed by us. So Don't show it
1208
+        if ($share->getPermissions() === 0) {
1209
+            return false;
1210
+        }
1211
+
1212
+        // Owner of the file and the sharer of the file can always get share
1213
+        if ($share->getShareOwner() === $this->currentUser
1214
+            || $share->getSharedBy() === $this->currentUser) {
1215
+            return true;
1216
+        }
1217
+
1218
+        // If the share is shared with you, you can access it!
1219
+        if ($share->getShareType() === Share::SHARE_TYPE_USER
1220
+            && $share->getSharedWith() === $this->currentUser) {
1221
+            return true;
1222
+        }
1223
+
1224
+        // Have reshare rights on the shared file/folder ?
1225
+        // Does the currentUser have access to the shared file?
1226
+        $userFolder = $this->rootFolder->getUserFolder($this->currentUser);
1227
+        $files = $userFolder->getById($share->getNodeId());
1228
+        if (!empty($files) && $this->shareProviderResharingRights($this->currentUser, $share, $files[0])) {
1229
+            return true;
1230
+        }
1231
+
1232
+        // If in the recipient group, you can see the share
1233
+        if ($checkGroups && $share->getShareType() === Share::SHARE_TYPE_GROUP) {
1234
+            $sharedWith = $this->groupManager->get($share->getSharedWith());
1235
+            $user = $this->userManager->get($this->currentUser);
1236
+            if ($user !== null && $sharedWith !== null && $sharedWith->inGroup($user)) {
1237
+                return true;
1238
+            }
1239
+        }
1240
+
1241
+        if ($share->getShareType() === Share::SHARE_TYPE_CIRCLE) {
1242
+            // TODO: have a sanity check like above?
1243
+            return true;
1244
+        }
1245
+
1246
+        if ($share->getShareType() === Share::SHARE_TYPE_ROOM) {
1247
+            try {
1248
+                return $this->getRoomShareHelper()->canAccessShare($share, $this->currentUser);
1249
+            } catch (QueryException $e) {
1250
+                return false;
1251
+            }
1252
+        }
1253
+
1254
+        return false;
1255
+    }
1256
+
1257
+    /**
1258
+     * Does the user have edit permission on the share
1259
+     *
1260
+     * @param \OCP\Share\IShare $share the share to check
1261
+     * @return boolean
1262
+     */
1263
+    protected function canEditShare(\OCP\Share\IShare $share): bool {
1264
+        // A file with permissions 0 can't be accessed by us. So Don't show it
1265
+        if ($share->getPermissions() === 0) {
1266
+            return false;
1267
+        }
1268
+
1269
+        // The owner of the file and the creator of the share
1270
+        // can always edit the share
1271
+        if ($share->getShareOwner() === $this->currentUser ||
1272
+            $share->getSharedBy() === $this->currentUser
1273
+        ) {
1274
+            return true;
1275
+        }
1276
+
1277
+        //! we do NOT support some kind of `admin` in groups.
1278
+        //! You cannot edit shares shared to a group you're
1279
+        //! a member of if you're not the share owner or the file owner!
1280
+
1281
+        return false;
1282
+    }
1283
+
1284
+    /**
1285
+     * Does the user have delete permission on the share
1286
+     *
1287
+     * @param \OCP\Share\IShare $share the share to check
1288
+     * @return boolean
1289
+     */
1290
+    protected function canDeleteShare(\OCP\Share\IShare $share): bool {
1291
+        // A file with permissions 0 can't be accessed by us. So Don't show it
1292
+        if ($share->getPermissions() === 0) {
1293
+            return false;
1294
+        }
1295
+
1296
+        // if the user is the recipient, i can unshare
1297
+        // the share with self
1298
+        if ($share->getShareType() === Share::SHARE_TYPE_USER &&
1299
+            $share->getSharedWith() === $this->currentUser
1300
+        ) {
1301
+            return true;
1302
+        }
1303
+
1304
+        // The owner of the file and the creator of the share
1305
+        // can always delete the share
1306
+        if ($share->getShareOwner() === $this->currentUser ||
1307
+            $share->getSharedBy() === $this->currentUser
1308
+        ) {
1309
+            return true;
1310
+        }
1311
+
1312
+        return false;
1313
+    }
1314
+
1315
+    /**
1316
+     * Does the user have delete permission on the share
1317
+     * This differs from the canDeleteShare function as it only
1318
+     * remove the share for the current user. It does NOT
1319
+     * completely delete the share but only the mount point.
1320
+     * It can then be restored from the deleted shares section.
1321
+     *
1322
+     * @param \OCP\Share\IShare $share the share to check
1323
+     * @return boolean
1324
+     *
1325
+     * @suppress PhanUndeclaredClassMethod
1326
+     */
1327
+    protected function canDeleteShareFromSelf(\OCP\Share\IShare $share): bool {
1328
+        if ($share->getShareType() !== IShare::TYPE_GROUP &&
1329
+            $share->getShareType() !== IShare::TYPE_ROOM
1330
+        ) {
1331
+            return false;
1332
+        }
1333
+
1334
+        if ($share->getShareOwner() === $this->currentUser ||
1335
+            $share->getSharedBy() === $this->currentUser
1336
+        ) {
1337
+            // Delete the whole share, not just for self
1338
+            return false;
1339
+        }
1340
+
1341
+        // If in the recipient group, you can delete the share from self
1342
+        if ($share->getShareType() === Share::SHARE_TYPE_GROUP) {
1343
+            $sharedWith = $this->groupManager->get($share->getSharedWith());
1344
+            $user = $this->userManager->get($this->currentUser);
1345
+            if ($user !== null && $sharedWith !== null && $sharedWith->inGroup($user)) {
1346
+                return true;
1347
+            }
1348
+        }
1349
+
1350
+        if ($share->getShareType() === Share::SHARE_TYPE_ROOM) {
1351
+            try {
1352
+                return $this->getRoomShareHelper()->canAccessShare($share, $this->currentUser);
1353
+            } catch (QueryException $e) {
1354
+                return false;
1355
+            }
1356
+        }
1357
+
1358
+        return false;
1359
+    }
1360
+
1361
+    /**
1362
+     * Make sure that the passed date is valid ISO 8601
1363
+     * So YYYY-MM-DD
1364
+     * If not throw an exception
1365
+     *
1366
+     * @param string $expireDate
1367
+     *
1368
+     * @throws \Exception
1369
+     * @return \DateTime
1370
+     */
1371
+    private function parseDate(string $expireDate): \DateTime {
1372
+        try {
1373
+            $date = new \DateTime($expireDate);
1374
+        } catch (\Exception $e) {
1375
+            throw new \Exception('Invalid date. Format must be YYYY-MM-DD');
1376
+        }
1377
+
1378
+        if ($date === false) {
1379
+            throw new \Exception('Invalid date. Format must be YYYY-MM-DD');
1380
+        }
1381
+
1382
+        $date->setTime(0, 0, 0);
1383
+
1384
+        return $date;
1385
+    }
1386
+
1387
+    /**
1388
+     * Since we have multiple providers but the OCS Share API v1 does
1389
+     * not support this we need to check all backends.
1390
+     *
1391
+     * @param string $id
1392
+     * @return \OCP\Share\IShare
1393
+     * @throws ShareNotFound
1394
+     */
1395
+    private function getShareById(string $id): IShare {
1396
+        $share = null;
1397
+
1398
+        // First check if it is an internal share.
1399
+        try {
1400
+            $share = $this->shareManager->getShareById('ocinternal:' . $id, $this->currentUser);
1401
+            return $share;
1402
+        } catch (ShareNotFound $e) {
1403
+            // Do nothing, just try the other share type
1404
+        }
1405
+
1406
+
1407
+        try {
1408
+            if ($this->shareManager->shareProviderExists(Share::SHARE_TYPE_CIRCLE)) {
1409
+                $share = $this->shareManager->getShareById('ocCircleShare:' . $id, $this->currentUser);
1410
+                return $share;
1411
+            }
1412
+        } catch (ShareNotFound $e) {
1413
+            // Do nothing, just try the other share type
1414
+        }
1415
+
1416
+        try {
1417
+            if ($this->shareManager->shareProviderExists(Share::SHARE_TYPE_EMAIL)) {
1418
+                $share = $this->shareManager->getShareById('ocMailShare:' . $id, $this->currentUser);
1419
+                return $share;
1420
+            }
1421
+        } catch (ShareNotFound $e) {
1422
+            // Do nothing, just try the other share type
1423
+        }
1424
+
1425
+        try {
1426
+            $share = $this->shareManager->getShareById('ocRoomShare:' . $id, $this->currentUser);
1427
+            return $share;
1428
+        } catch (ShareNotFound $e) {
1429
+            // Do nothing, just try the other share type
1430
+        }
1431
+
1432
+        if (!$this->shareManager->outgoingServer2ServerSharesAllowed()) {
1433
+            throw new ShareNotFound();
1434
+        }
1435
+        $share = $this->shareManager->getShareById('ocFederatedSharing:' . $id, $this->currentUser);
1436
+
1437
+        return $share;
1438
+    }
1439
+
1440
+    /**
1441
+     * Lock a Node
1442
+     *
1443
+     * @param \OCP\Files\Node $node
1444
+     * @throws LockedException
1445
+     */
1446
+    private function lock(\OCP\Files\Node $node) {
1447
+        $node->lock(ILockingProvider::LOCK_SHARED);
1448
+        $this->lockedNode = $node;
1449
+    }
1450
+
1451
+    /**
1452
+     * Cleanup the remaining locks
1453
+     * @throws @LockedException
1454
+     */
1455
+    public function cleanup() {
1456
+        if ($this->lockedNode !== null) {
1457
+            $this->lockedNode->unlock(ILockingProvider::LOCK_SHARED);
1458
+        }
1459
+    }
1460
+
1461
+    /**
1462
+     * Returns the helper of ShareAPIController for room shares.
1463
+     *
1464
+     * If the Talk application is not enabled or the helper is not available
1465
+     * a QueryException is thrown instead.
1466
+     *
1467
+     * @return \OCA\Talk\Share\Helper\ShareAPIController
1468
+     * @throws QueryException
1469
+     */
1470
+    private function getRoomShareHelper() {
1471
+        if (!$this->appManager->isEnabledForUser('spreed')) {
1472
+            throw new QueryException();
1473
+        }
1474
+
1475
+        return $this->serverContainer->query('\OCA\Talk\Share\Helper\ShareAPIController');
1476
+    }
1477
+
1478
+
1479
+    /**
1480
+     * @param string $viewer
1481
+     * @param Node $node
1482
+     * @param bool $reShares
1483
+     *
1484
+     * @return IShare[]
1485
+     */
1486
+    private function getSharesFromNode(string $viewer, $node, bool $reShares): array {
1487
+        $providers = [
1488
+            Share::SHARE_TYPE_USER,
1489
+            Share::SHARE_TYPE_GROUP,
1490
+            Share::SHARE_TYPE_LINK,
1491
+            Share::SHARE_TYPE_EMAIL,
1492
+            Share::SHARE_TYPE_EMAIL,
1493
+            Share::SHARE_TYPE_CIRCLE,
1494
+            Share::SHARE_TYPE_ROOM
1495
+        ];
1496
+
1497
+        // Should we assume that the (currentUser) viewer is the owner of the node !?
1498
+        $shares = [];
1499
+        foreach ($providers as $provider) {
1500
+            if (!$this->shareManager->shareProviderExists($provider)) {
1501
+                continue;
1502
+            }
1503
+
1504
+            $providerShares =
1505
+                $this->shareManager->getSharesBy($viewer, $provider, $node, $reShares, -1, 0);
1506
+            $shares = array_merge($shares, $providerShares);
1507
+        }
1508
+
1509
+        if ($this->shareManager->outgoingServer2ServerSharesAllowed()) {
1510
+            $federatedShares = $this->shareManager->getSharesBy(
1511
+                $this->currentUser, Share::SHARE_TYPE_REMOTE, $node, $reShares, -1, 0
1512
+            );
1513
+            $shares = array_merge($shares, $federatedShares);
1514
+        }
1515
+
1516
+        if ($this->shareManager->outgoingServer2ServerGroupSharesAllowed()) {
1517
+            $federatedShares = $this->shareManager->getSharesBy(
1518
+                $this->currentUser, Share::SHARE_TYPE_REMOTE_GROUP, $node, $reShares, -1, 0
1519
+            );
1520
+            $shares = array_merge($shares, $federatedShares);
1521
+        }
1522
+
1523
+        return $shares;
1524
+    }
1525
+
1526
+
1527
+    /**
1528
+     * @param Node $node
1529
+     *
1530
+     * @throws SharingRightsException
1531
+     */
1532
+    private function confirmSharingRights(Node $node): void {
1533
+        if (!$this->hasResharingRights($this->currentUser, $node)) {
1534
+            throw new SharingRightsException('no sharing rights on this item');
1535
+        }
1536
+    }
1537
+
1538
+
1539
+    /**
1540
+     * @param string $viewer
1541
+     * @param Node $node
1542
+     *
1543
+     * @return bool
1544
+     */
1545
+    private function hasResharingRights($viewer, $node): bool {
1546
+        if ($viewer === $node->getOwner()->getUID()) {
1547
+            return true;
1548
+        }
1549
+
1550
+        foreach ([$node, $node->getParent()] as $node) {
1551
+            $shares = $this->getSharesFromNode($viewer, $node, true);
1552
+            foreach ($shares as $share) {
1553
+                try {
1554
+                    if ($this->shareProviderResharingRights($viewer, $share, $node)) {
1555
+                        return true;
1556
+                    }
1557
+                } catch (InvalidPathException | NotFoundException $e) {
1558
+                }
1559
+            }
1560
+        }
1561
+
1562
+        return false;
1563
+    }
1564
+
1565
+
1566
+    /**
1567
+     * Returns if we can find resharing rights in an IShare object for a specific user.
1568
+     *
1569
+     * @suppress PhanUndeclaredClassMethod
1570
+     *
1571
+     * @param string $userId
1572
+     * @param IShare $share
1573
+     * @param Node $node
1574
+     *
1575
+     * @return bool
1576
+     * @throws NotFoundException
1577
+     * @throws InvalidPathException
1578
+     */
1579
+    private function shareProviderResharingRights(string $userId, IShare $share, $node): bool {
1580
+        if ($share->getShareOwner() === $userId) {
1581
+            return true;
1582
+        }
1583
+
1584
+        // we check that current user have parent resharing rights on the current file
1585
+        if ($node !== null && ($node->getPermissions() & Constants::PERMISSION_SHARE) !== 0) {
1586
+            return true;
1587
+        }
1588
+
1589
+        if ((\OCP\Constants::PERMISSION_SHARE & $share->getPermissions()) === 0) {
1590
+            return false;
1591
+        }
1592
+
1593
+        if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER && $share->getSharedWith() === $userId) {
1594
+            return true;
1595
+        }
1596
+
1597
+        if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP && $this->groupManager->isInGroup($userId, $share->getSharedWith())) {
1598
+            return true;
1599
+        }
1600
+
1601
+        if ($share->getShareType() === \OCP\Share::SHARE_TYPE_CIRCLE && \OC::$server->getAppManager()->isEnabledForUser('circles')
1602
+            && class_exists('\OCA\Circles\Api\v1\Circles')) {
1603
+            $hasCircleId = (substr($share->getSharedWith(), -1) === ']');
1604
+            $shareWithStart = ($hasCircleId ? strrpos($share->getSharedWith(), '[') + 1 : 0);
1605
+            $shareWithLength = ($hasCircleId ? -1 : strpos($share->getSharedWith(), ' '));
1606
+            if (is_bool($shareWithLength)) {
1607
+                $shareWithLength = -1;
1608
+            }
1609
+            $sharedWith = substr($share->getSharedWith(), $shareWithStart, $shareWithLength);
1610
+            try {
1611
+                $member = \OCA\Circles\Api\v1\Circles::getMember($sharedWith, $userId, 1);
1612
+                if ($member->getLevel() >= 4) {
1613
+                    return true;
1614
+                }
1615
+                return false;
1616
+            } catch (QueryException $e) {
1617
+                return false;
1618
+            }
1619
+        }
1620
+
1621
+        return false;
1622
+    }
1623
+
1624
+    /**
1625
+     * Get all the shares for the current user
1626
+     *
1627
+     * @param Node|null $path
1628
+     * @param boolean $reshares
1629
+     * @return void
1630
+     */
1631
+    private function getAllShares(?Node $path = null, bool $reshares = false) {
1632
+        // Get all shares
1633
+        $userShares = $this->shareManager->getSharesBy($this->currentUser, Share::SHARE_TYPE_USER, $path, $reshares, -1, 0);
1634
+        $groupShares = $this->shareManager->getSharesBy($this->currentUser, Share::SHARE_TYPE_GROUP, $path, $reshares, -1, 0);
1635
+        $linkShares = $this->shareManager->getSharesBy($this->currentUser, Share::SHARE_TYPE_LINK, $path, $reshares, -1, 0);
1636
+
1637
+        // EMAIL SHARES
1638
+        $mailShares = $this->shareManager->getSharesBy($this->currentUser, Share::SHARE_TYPE_EMAIL, $path, $reshares, -1, 0);
1639
+
1640
+        // CIRCLE SHARES
1641
+        $circleShares = $this->shareManager->getSharesBy($this->currentUser, Share::SHARE_TYPE_CIRCLE, $path, $reshares, -1, 0);
1642
+
1643
+        // TALK SHARES
1644
+        $roomShares = $this->shareManager->getSharesBy($this->currentUser, Share::SHARE_TYPE_ROOM, $path, $reshares, -1, 0);
1645
+
1646
+        // FEDERATION
1647
+        if ($this->shareManager->outgoingServer2ServerSharesAllowed()) {
1648
+            $federatedShares = $this->shareManager->getSharesBy($this->currentUser, Share::SHARE_TYPE_REMOTE, $path, $reshares, -1, 0);
1649
+        } else {
1650
+            $federatedShares = [];
1651
+        }
1652
+        if ($this->shareManager->outgoingServer2ServerGroupSharesAllowed()) {
1653
+            $federatedGroupShares = $this->shareManager->getSharesBy($this->currentUser, Share::SHARE_TYPE_REMOTE_GROUP, $path, $reshares, -1, 0);
1654
+        } else {
1655
+            $federatedGroupShares = [];
1656
+        }
1657
+
1658
+        return array_merge($userShares, $groupShares, $linkShares, $mailShares, $circleShares, $roomShares, $federatedShares, $federatedGroupShares);
1659
+    }
1660
+
1661
+
1662
+    /**
1663
+     * merging already formatted shares.
1664
+     * We'll make an associative array to easily detect duplicate Ids.
1665
+     * Keys _needs_ to be removed after all shares are retrieved and merged.
1666
+     *
1667
+     * @param array $shares
1668
+     * @param array $newShares
1669
+     */
1670
+    private function mergeFormattedShares(array &$shares, array $newShares) {
1671
+        foreach ($newShares as $newShare) {
1672
+            if (!array_key_exists($newShare['id'], $shares)) {
1673
+                $shares[$newShare['id']] = $newShare;
1674
+            }
1675
+        }
1676
+    }
1677 1677
 }
Please login to merge, or discard this patch.