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