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