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