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