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