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