Passed
Push — master ( 303d68...10df6b )
by Roeland
18:37 queued 03:04
created
apps/federatedfilesharing/lib/OCM/CloudFederationProviderFiles.php 1 patch
Indentation   +759 added lines, -759 removed lines patch added patch discarded remove patch
@@ -63,763 +63,763 @@
 block discarded – undo
63 63
 
64 64
 class CloudFederationProviderFiles implements ICloudFederationProvider {
65 65
 
66
-	/** @var IAppManager */
67
-	private $appManager;
68
-
69
-	/** @var FederatedShareProvider */
70
-	private $federatedShareProvider;
71
-
72
-	/** @var AddressHandler */
73
-	private $addressHandler;
74
-
75
-	/** @var ILogger */
76
-	private $logger;
77
-
78
-	/** @var IUserManager */
79
-	private $userManager;
80
-
81
-	/** @var IManager */
82
-	private $shareManager;
83
-
84
-	/** @var ICloudIdManager */
85
-	private $cloudIdManager;
86
-
87
-	/** @var IActivityManager */
88
-	private $activityManager;
89
-
90
-	/** @var INotificationManager */
91
-	private $notificationManager;
92
-
93
-	/** @var IURLGenerator */
94
-	private $urlGenerator;
95
-
96
-	/** @var ICloudFederationFactory */
97
-	private $cloudFederationFactory;
98
-
99
-	/** @var ICloudFederationProviderManager */
100
-	private $cloudFederationProviderManager;
101
-
102
-	/** @var IDBConnection */
103
-	private $connection;
104
-
105
-	/** @var IGroupManager */
106
-	private $groupManager;
107
-
108
-	/**
109
-	 * CloudFederationProvider constructor.
110
-	 *
111
-	 * @param IAppManager $appManager
112
-	 * @param FederatedShareProvider $federatedShareProvider
113
-	 * @param AddressHandler $addressHandler
114
-	 * @param ILogger $logger
115
-	 * @param IUserManager $userManager
116
-	 * @param IManager $shareManager
117
-	 * @param ICloudIdManager $cloudIdManager
118
-	 * @param IActivityManager $activityManager
119
-	 * @param INotificationManager $notificationManager
120
-	 * @param IURLGenerator $urlGenerator
121
-	 * @param ICloudFederationFactory $cloudFederationFactory
122
-	 * @param ICloudFederationProviderManager $cloudFederationProviderManager
123
-	 * @param IDBConnection $connection
124
-	 * @param IGroupManager $groupManager
125
-	 */
126
-	public function __construct(IAppManager $appManager,
127
-								FederatedShareProvider $federatedShareProvider,
128
-								AddressHandler $addressHandler,
129
-								ILogger $logger,
130
-								IUserManager $userManager,
131
-								IManager $shareManager,
132
-								ICloudIdManager $cloudIdManager,
133
-								IActivityManager $activityManager,
134
-								INotificationManager $notificationManager,
135
-								IURLGenerator $urlGenerator,
136
-								ICloudFederationFactory $cloudFederationFactory,
137
-								ICloudFederationProviderManager $cloudFederationProviderManager,
138
-								IDBConnection $connection,
139
-								IGroupManager $groupManager
140
-	) {
141
-		$this->appManager = $appManager;
142
-		$this->federatedShareProvider = $federatedShareProvider;
143
-		$this->addressHandler = $addressHandler;
144
-		$this->logger = $logger;
145
-		$this->userManager = $userManager;
146
-		$this->shareManager = $shareManager;
147
-		$this->cloudIdManager = $cloudIdManager;
148
-		$this->activityManager = $activityManager;
149
-		$this->notificationManager = $notificationManager;
150
-		$this->urlGenerator = $urlGenerator;
151
-		$this->cloudFederationFactory = $cloudFederationFactory;
152
-		$this->cloudFederationProviderManager = $cloudFederationProviderManager;
153
-		$this->connection = $connection;
154
-		$this->groupManager = $groupManager;
155
-	}
156
-
157
-
158
-
159
-	/**
160
-	 * @return string
161
-	 */
162
-	public function getShareType() {
163
-		return 'file';
164
-	}
165
-
166
-	/**
167
-	 * share received from another server
168
-	 *
169
-	 * @param ICloudFederationShare $share
170
-	 * @return string provider specific unique ID of the share
171
-	 *
172
-	 * @throws ProviderCouldNotAddShareException
173
-	 * @throws \OCP\AppFramework\QueryException
174
-	 * @throws \OC\HintException
175
-	 * @since 14.0.0
176
-	 */
177
-	public function shareReceived(ICloudFederationShare $share) {
178
-		if (!$this->isS2SEnabled(true)) {
179
-			throw new ProviderCouldNotAddShareException('Server does not support federated cloud sharing', '', Http::STATUS_SERVICE_UNAVAILABLE);
180
-		}
181
-
182
-		$protocol = $share->getProtocol();
183
-		if ($protocol['name'] !== 'webdav') {
184
-			throw new ProviderCouldNotAddShareException('Unsupported protocol for data exchange.', '', Http::STATUS_NOT_IMPLEMENTED);
185
-		}
186
-
187
-		[$ownerUid, $remote] = $this->addressHandler->splitUserRemote($share->getOwner());
188
-		// for backward compatibility make sure that the remote url stored in the
189
-		// database ends with a trailing slash
190
-		if (substr($remote, -1) !== '/') {
191
-			$remote = $remote . '/';
192
-		}
193
-
194
-		$token = $share->getShareSecret();
195
-		$name = $share->getResourceName();
196
-		$owner = $share->getOwnerDisplayName();
197
-		$sharedBy = $share->getSharedByDisplayName();
198
-		$shareWith = $share->getShareWith();
199
-		$remoteId = $share->getProviderId();
200
-		$sharedByFederatedId = $share->getSharedBy();
201
-		$ownerFederatedId = $share->getOwner();
202
-		$shareType = $this->mapShareTypeToNextcloud($share->getShareType());
203
-
204
-		// if no explicit information about the person who created the share was send
205
-		// we assume that the share comes from the owner
206
-		if ($sharedByFederatedId === null) {
207
-			$sharedBy = $owner;
208
-			$sharedByFederatedId = $ownerFederatedId;
209
-		}
210
-
211
-		if ($remote && $token && $name && $owner && $remoteId && $shareWith) {
212
-			if (!Util::isValidFileName($name)) {
213
-				throw new ProviderCouldNotAddShareException('The mountpoint name contains invalid characters.', '', Http::STATUS_BAD_REQUEST);
214
-			}
215
-
216
-			// FIXME this should be a method in the user management instead
217
-			if ($shareType === IShare::TYPE_USER) {
218
-				$this->logger->debug('shareWith before, ' . $shareWith, ['app' => 'files_sharing']);
219
-				Util::emitHook(
220
-					'\OCA\Files_Sharing\API\Server2Server',
221
-					'preLoginNameUsedAsUserName',
222
-					['uid' => &$shareWith]
223
-				);
224
-				$this->logger->debug('shareWith after, ' . $shareWith, ['app' => 'files_sharing']);
225
-
226
-				if (!$this->userManager->userExists($shareWith)) {
227
-					throw new ProviderCouldNotAddShareException('User does not exists', '',Http::STATUS_BAD_REQUEST);
228
-				}
229
-
230
-				\OC_Util::setupFS($shareWith);
231
-			}
232
-
233
-			if ($shareType === IShare::TYPE_GROUP && !$this->groupManager->groupExists($shareWith)) {
234
-				throw new ProviderCouldNotAddShareException('Group does not exists', '',Http::STATUS_BAD_REQUEST);
235
-			}
236
-
237
-			$externalManager = new \OCA\Files_Sharing\External\Manager(
238
-				\OC::$server->getDatabaseConnection(),
239
-				Filesystem::getMountManager(),
240
-				Filesystem::getLoader(),
241
-				\OC::$server->getHTTPClientService(),
242
-				\OC::$server->getNotificationManager(),
243
-				\OC::$server->query(\OCP\OCS\IDiscoveryService::class),
244
-				\OC::$server->getCloudFederationProviderManager(),
245
-				\OC::$server->getCloudFederationFactory(),
246
-				\OC::$server->getGroupManager(),
247
-				\OC::$server->getUserManager(),
248
-				$shareWith,
249
-				\OC::$server->query(IEventDispatcher::class)
250
-			);
251
-
252
-			try {
253
-				$externalManager->addShare($remote, $token, '', $name, $owner, $shareType,false, $shareWith, $remoteId);
254
-				$shareId = \OC::$server->getDatabaseConnection()->lastInsertId('*PREFIX*share_external');
255
-
256
-				if ($shareType === IShare::TYPE_USER) {
257
-					$event = $this->activityManager->generateEvent();
258
-					$event->setApp('files_sharing')
259
-						->setType('remote_share')
260
-						->setSubject(RemoteShares::SUBJECT_REMOTE_SHARE_RECEIVED, [$ownerFederatedId, trim($name, '/')])
261
-						->setAffectedUser($shareWith)
262
-						->setObject('remote_share', $shareId, $name);
263
-					\OC::$server->getActivityManager()->publish($event);
264
-					$this->notifyAboutNewShare($shareWith, $shareId, $ownerFederatedId, $sharedByFederatedId, $name);
265
-				} else {
266
-					$groupMembers = $this->groupManager->get($shareWith)->getUsers();
267
-					foreach ($groupMembers as $user) {
268
-						$event = $this->activityManager->generateEvent();
269
-						$event->setApp('files_sharing')
270
-							->setType('remote_share')
271
-							->setSubject(RemoteShares::SUBJECT_REMOTE_SHARE_RECEIVED, [$ownerFederatedId, trim($name, '/')])
272
-							->setAffectedUser($user->getUID())
273
-							->setObject('remote_share', $shareId, $name);
274
-						\OC::$server->getActivityManager()->publish($event);
275
-						$this->notifyAboutNewShare($user->getUID(), $shareId, $ownerFederatedId, $sharedByFederatedId, $name);
276
-					}
277
-				}
278
-				return $shareId;
279
-			} catch (\Exception $e) {
280
-				$this->logger->logException($e, [
281
-					'message' => 'Server can not add remote share.',
282
-					'level' => ILogger::ERROR,
283
-					'app' => 'files_sharing'
284
-				]);
285
-				throw new ProviderCouldNotAddShareException('internal server error, was not able to add share from ' . $remote, '', HTTP::STATUS_INTERNAL_SERVER_ERROR);
286
-			}
287
-		}
288
-
289
-		throw new ProviderCouldNotAddShareException('server can not add remote share, missing parameter', '', HTTP::STATUS_BAD_REQUEST);
290
-	}
291
-
292
-	/**
293
-	 * notification received from another server
294
-	 *
295
-	 * @param string $notificationType (e.g. SHARE_ACCEPTED)
296
-	 * @param string $providerId id of the share
297
-	 * @param array $notification payload of the notification
298
-	 * @return array data send back to the sender
299
-	 *
300
-	 * @throws ActionNotSupportedException
301
-	 * @throws AuthenticationFailedException
302
-	 * @throws BadRequestException
303
-	 * @throws \OC\HintException
304
-	 * @since 14.0.0
305
-	 */
306
-	public function notificationReceived($notificationType, $providerId, array $notification) {
307
-		switch ($notificationType) {
308
-			case 'SHARE_ACCEPTED':
309
-				return $this->shareAccepted($providerId, $notification);
310
-			case 'SHARE_DECLINED':
311
-				return $this->shareDeclined($providerId, $notification);
312
-			case 'SHARE_UNSHARED':
313
-				return $this->unshare($providerId, $notification);
314
-			case 'REQUEST_RESHARE':
315
-				return $this->reshareRequested($providerId, $notification);
316
-			case 'RESHARE_UNDO':
317
-				return $this->undoReshare($providerId, $notification);
318
-			case 'RESHARE_CHANGE_PERMISSION':
319
-				return $this->updateResharePermissions($providerId, $notification);
320
-		}
321
-
322
-
323
-		throw new BadRequestException([$notificationType]);
324
-	}
325
-
326
-	/**
327
-	 * map OCM share type (strings) to Nextcloud internal share types (integer)
328
-	 *
329
-	 * @param string $shareType
330
-	 * @return int
331
-	 */
332
-	private function mapShareTypeToNextcloud($shareType) {
333
-		$result = IShare::TYPE_USER;
334
-		if ($shareType === 'group') {
335
-			$result = IShare::TYPE_GROUP;
336
-		}
337
-
338
-		return $result;
339
-	}
340
-
341
-	private function notifyAboutNewShare($shareWith, $shareId, $ownerFederatedId, $sharedByFederatedId, $name): void {
342
-		$notification = $this->notificationManager->createNotification();
343
-		$notification->setApp('files_sharing')
344
-			->setUser($shareWith)
345
-			->setDateTime(new \DateTime())
346
-			->setObject('remote_share', $shareId)
347
-			->setSubject('remote_share', [$ownerFederatedId, $sharedByFederatedId, trim($name, '/')]);
348
-
349
-		$declineAction = $notification->createAction();
350
-		$declineAction->setLabel('decline')
351
-			->setLink($this->urlGenerator->getAbsoluteURL($this->urlGenerator->linkTo('', 'ocs/v2.php/apps/files_sharing/api/v1/remote_shares/pending/' . $shareId)), 'DELETE');
352
-		$notification->addAction($declineAction);
353
-
354
-		$acceptAction = $notification->createAction();
355
-		$acceptAction->setLabel('accept')
356
-			->setLink($this->urlGenerator->getAbsoluteURL($this->urlGenerator->linkTo('', 'ocs/v2.php/apps/files_sharing/api/v1/remote_shares/pending/' . $shareId)), 'POST');
357
-		$notification->addAction($acceptAction);
358
-
359
-		$this->notificationManager->notify($notification);
360
-	}
361
-
362
-	/**
363
-	 * process notification that the recipient accepted a share
364
-	 *
365
-	 * @param string $id
366
-	 * @param array $notification
367
-	 * @return array
368
-	 * @throws ActionNotSupportedException
369
-	 * @throws AuthenticationFailedException
370
-	 * @throws BadRequestException
371
-	 * @throws \OC\HintException
372
-	 */
373
-	private function shareAccepted($id, array $notification) {
374
-		if (!$this->isS2SEnabled()) {
375
-			throw new ActionNotSupportedException('Server does not support federated cloud sharing');
376
-		}
377
-
378
-		if (!isset($notification['sharedSecret'])) {
379
-			throw new BadRequestException(['sharedSecret']);
380
-		}
381
-
382
-		$token = $notification['sharedSecret'];
383
-
384
-		$share = $this->federatedShareProvider->getShareById($id);
385
-
386
-		$this->verifyShare($share, $token);
387
-		$this->executeAcceptShare($share);
388
-		if ($share->getShareOwner() !== $share->getSharedBy()) {
389
-			[, $remote] = $this->addressHandler->splitUserRemote($share->getSharedBy());
390
-			$remoteId = $this->federatedShareProvider->getRemoteId($share);
391
-			$notification = $this->cloudFederationFactory->getCloudFederationNotification();
392
-			$notification->setMessage(
393
-				'SHARE_ACCEPTED',
394
-				'file',
395
-				$remoteId,
396
-				[
397
-					'sharedSecret' => $token,
398
-					'message' => 'Recipient accepted the re-share'
399
-				]
400
-
401
-			);
402
-			$this->cloudFederationProviderManager->sendNotification($remote, $notification);
403
-		}
404
-
405
-		return [];
406
-	}
407
-
408
-	/**
409
-	 * @param IShare $share
410
-	 * @throws ShareNotFound
411
-	 */
412
-	protected function executeAcceptShare(IShare $share) {
413
-		try {
414
-			$fileId = (int)$share->getNode()->getId();
415
-			[$file, $link] = $this->getFile($this->getCorrectUid($share), $fileId);
416
-		} catch (\Exception $e) {
417
-			throw new ShareNotFound();
418
-		}
419
-
420
-		$event = $this->activityManager->generateEvent();
421
-		$event->setApp('files_sharing')
422
-			->setType('remote_share')
423
-			->setAffectedUser($this->getCorrectUid($share))
424
-			->setSubject(RemoteShares::SUBJECT_REMOTE_SHARE_ACCEPTED, [$share->getSharedWith(), [$fileId => $file]])
425
-			->setObject('files', $fileId, $file)
426
-			->setLink($link);
427
-		$this->activityManager->publish($event);
428
-	}
429
-
430
-	/**
431
-	 * process notification that the recipient declined a share
432
-	 *
433
-	 * @param string $id
434
-	 * @param array $notification
435
-	 * @return array
436
-	 * @throws ActionNotSupportedException
437
-	 * @throws AuthenticationFailedException
438
-	 * @throws BadRequestException
439
-	 * @throws ShareNotFound
440
-	 * @throws \OC\HintException
441
-	 *
442
-	 */
443
-	protected function shareDeclined($id, array $notification) {
444
-		if (!$this->isS2SEnabled()) {
445
-			throw new ActionNotSupportedException('Server does not support federated cloud sharing');
446
-		}
447
-
448
-		if (!isset($notification['sharedSecret'])) {
449
-			throw new BadRequestException(['sharedSecret']);
450
-		}
451
-
452
-		$token = $notification['sharedSecret'];
453
-
454
-		$share = $this->federatedShareProvider->getShareById($id);
455
-
456
-		$this->verifyShare($share, $token);
457
-
458
-		if ($share->getShareOwner() !== $share->getSharedBy()) {
459
-			[, $remote] = $this->addressHandler->splitUserRemote($share->getSharedBy());
460
-			$remoteId = $this->federatedShareProvider->getRemoteId($share);
461
-			$notification = $this->cloudFederationFactory->getCloudFederationNotification();
462
-			$notification->setMessage(
463
-				'SHARE_DECLINED',
464
-				'file',
465
-				$remoteId,
466
-				[
467
-					'sharedSecret' => $token,
468
-					'message' => 'Recipient declined the re-share'
469
-				]
470
-
471
-			);
472
-			$this->cloudFederationProviderManager->sendNotification($remote, $notification);
473
-		}
474
-
475
-		$this->executeDeclineShare($share);
476
-
477
-		return [];
478
-	}
479
-
480
-	/**
481
-	 * delete declined share and create a activity
482
-	 *
483
-	 * @param IShare $share
484
-	 * @throws ShareNotFound
485
-	 */
486
-	protected function executeDeclineShare(IShare $share) {
487
-		$this->federatedShareProvider->removeShareFromTable($share);
488
-
489
-		try {
490
-			$fileId = (int)$share->getNode()->getId();
491
-			[$file, $link] = $this->getFile($this->getCorrectUid($share), $fileId);
492
-		} catch (\Exception $e) {
493
-			throw new ShareNotFound();
494
-		}
495
-
496
-		$event = $this->activityManager->generateEvent();
497
-		$event->setApp('files_sharing')
498
-			->setType('remote_share')
499
-			->setAffectedUser($this->getCorrectUid($share))
500
-			->setSubject(RemoteShares::SUBJECT_REMOTE_SHARE_DECLINED, [$share->getSharedWith(), [$fileId => $file]])
501
-			->setObject('files', $fileId, $file)
502
-			->setLink($link);
503
-		$this->activityManager->publish($event);
504
-	}
505
-
506
-	/**
507
-	 * received the notification that the owner unshared a file from you
508
-	 *
509
-	 * @param string $id
510
-	 * @param array $notification
511
-	 * @return array
512
-	 * @throws AuthenticationFailedException
513
-	 * @throws BadRequestException
514
-	 */
515
-	private function undoReshare($id, array $notification) {
516
-		if (!isset($notification['sharedSecret'])) {
517
-			throw new BadRequestException(['sharedSecret']);
518
-		}
519
-		$token = $notification['sharedSecret'];
520
-
521
-		$share = $this->federatedShareProvider->getShareById($id);
522
-
523
-		$this->verifyShare($share, $token);
524
-		$this->federatedShareProvider->removeShareFromTable($share);
525
-		return [];
526
-	}
527
-
528
-	/**
529
-	 * unshare file from self
530
-	 *
531
-	 * @param string $id
532
-	 * @param array $notification
533
-	 * @return array
534
-	 * @throws ActionNotSupportedException
535
-	 * @throws BadRequestException
536
-	 */
537
-	private function unshare($id, array $notification) {
538
-		if (!$this->isS2SEnabled(true)) {
539
-			throw new ActionNotSupportedException("incoming shares disabled!");
540
-		}
541
-
542
-		if (!isset($notification['sharedSecret'])) {
543
-			throw new BadRequestException(['sharedSecret']);
544
-		}
545
-		$token = $notification['sharedSecret'];
546
-
547
-		$qb = $this->connection->getQueryBuilder();
548
-		$qb->select('*')
549
-			->from('share_external')
550
-			->where(
551
-				$qb->expr()->andX(
552
-					$qb->expr()->eq('remote_id', $qb->createNamedParameter($id)),
553
-					$qb->expr()->eq('share_token', $qb->createNamedParameter($token))
554
-				)
555
-			);
556
-
557
-		$result = $qb->execute();
558
-		$share = $result->fetch();
559
-		$result->closeCursor();
560
-
561
-		if ($token && $id && !empty($share)) {
562
-			$remote = $this->cleanupRemote($share['remote']);
563
-
564
-			$owner = $this->cloudIdManager->getCloudId($share['owner'], $remote);
565
-			$mountpoint = $share['mountpoint'];
566
-			$user = $share['user'];
567
-
568
-			$qb = $this->connection->getQueryBuilder();
569
-			$qb->delete('share_external')
570
-				->where(
571
-					$qb->expr()->andX(
572
-						$qb->expr()->eq('remote_id', $qb->createNamedParameter($id)),
573
-						$qb->expr()->eq('share_token', $qb->createNamedParameter($token))
574
-					)
575
-				);
576
-
577
-			$qb->execute();
578
-
579
-			// delete all child in case of a group share
580
-			$qb = $this->connection->getQueryBuilder();
581
-			$qb->delete('share_external')
582
-				->where($qb->expr()->eq('parent', $qb->createNamedParameter((int)$share['id'])));
583
-			$qb->execute();
584
-
585
-			if ((int)$share['share_type'] === IShare::TYPE_USER) {
586
-				if ($share['accepted']) {
587
-					$path = trim($mountpoint, '/');
588
-				} else {
589
-					$path = trim($share['name'], '/');
590
-				}
591
-				$notification = $this->notificationManager->createNotification();
592
-				$notification->setApp('files_sharing')
593
-					->setUser($share['user'])
594
-					->setObject('remote_share', (int)$share['id']);
595
-				$this->notificationManager->markProcessed($notification);
596
-
597
-				$event = $this->activityManager->generateEvent();
598
-				$event->setApp('files_sharing')
599
-					->setType('remote_share')
600
-					->setSubject(RemoteShares::SUBJECT_REMOTE_SHARE_UNSHARED, [$owner->getId(), $path])
601
-					->setAffectedUser($user)
602
-					->setObject('remote_share', (int)$share['id'], $path);
603
-				\OC::$server->getActivityManager()->publish($event);
604
-			}
605
-		}
606
-
607
-		return [];
608
-	}
609
-
610
-	private function cleanupRemote($remote) {
611
-		$remote = substr($remote, strpos($remote, '://') + 3);
612
-
613
-		return rtrim($remote, '/');
614
-	}
615
-
616
-	/**
617
-	 * recipient of a share request to re-share the file with another user
618
-	 *
619
-	 * @param string $id
620
-	 * @param array $notification
621
-	 * @return array
622
-	 * @throws AuthenticationFailedException
623
-	 * @throws BadRequestException
624
-	 * @throws ProviderCouldNotAddShareException
625
-	 * @throws ShareNotFound
626
-	 */
627
-	protected function reshareRequested($id, array $notification) {
628
-		if (!isset($notification['sharedSecret'])) {
629
-			throw new BadRequestException(['sharedSecret']);
630
-		}
631
-		$token = $notification['sharedSecret'];
632
-
633
-		if (!isset($notification['shareWith'])) {
634
-			throw new BadRequestException(['shareWith']);
635
-		}
636
-		$shareWith = $notification['shareWith'];
637
-
638
-		if (!isset($notification['senderId'])) {
639
-			throw new BadRequestException(['senderId']);
640
-		}
641
-		$senderId = $notification['senderId'];
642
-
643
-		$share = $this->federatedShareProvider->getShareById($id);
644
-		// don't allow to share a file back to the owner
645
-		try {
646
-			[$user, $remote] = $this->addressHandler->splitUserRemote($shareWith);
647
-			$owner = $share->getShareOwner();
648
-			$currentServer = $this->addressHandler->generateRemoteURL();
649
-			if ($this->addressHandler->compareAddresses($user, $remote, $owner, $currentServer)) {
650
-				throw new ProviderCouldNotAddShareException('Resharing back to the owner is not allowed: ' . $id);
651
-			}
652
-		} catch (\Exception $e) {
653
-			throw new ProviderCouldNotAddShareException($e->getMessage());
654
-		}
655
-
656
-		$this->verifyShare($share, $token);
657
-
658
-		// check if re-sharing is allowed
659
-		if ($share->getPermissions() & Constants::PERMISSION_SHARE) {
660
-			// the recipient of the initial share is now the initiator for the re-share
661
-			$share->setSharedBy($share->getSharedWith());
662
-			$share->setSharedWith($shareWith);
663
-			$result = $this->federatedShareProvider->create($share);
664
-			$this->federatedShareProvider->storeRemoteId((int)$result->getId(), $senderId);
665
-			return ['token' => $result->getToken(), 'providerId' => $result->getId()];
666
-		} else {
667
-			throw new ProviderCouldNotAddShareException('resharing not allowed for share: ' . $id);
668
-		}
669
-	}
670
-
671
-	/**
672
-	 * update permission of a re-share so that the share dialog shows the right
673
-	 * permission if the owner or the sender changes the permission
674
-	 *
675
-	 * @param string $id
676
-	 * @param array $notification
677
-	 * @return array
678
-	 * @throws AuthenticationFailedException
679
-	 * @throws BadRequestException
680
-	 */
681
-	protected function updateResharePermissions($id, array $notification) {
682
-		throw new HintException('Updating reshares not allowed');
683
-	}
684
-
685
-	/**
686
-	 * translate OCM Permissions to Nextcloud permissions
687
-	 *
688
-	 * @param array $ocmPermissions
689
-	 * @return int
690
-	 * @throws BadRequestException
691
-	 */
692
-	protected function ocmPermissions2ncPermissions(array $ocmPermissions) {
693
-		$ncPermissions = 0;
694
-		foreach ($ocmPermissions as $permission) {
695
-			switch (strtolower($permission)) {
696
-				case 'read':
697
-					$ncPermissions += Constants::PERMISSION_READ;
698
-					break;
699
-				case 'write':
700
-					$ncPermissions += Constants::PERMISSION_CREATE + Constants::PERMISSION_UPDATE;
701
-					break;
702
-				case 'share':
703
-					$ncPermissions += Constants::PERMISSION_SHARE;
704
-					break;
705
-				default:
706
-					throw new BadRequestException(['permission']);
707
-			}
708
-		}
709
-
710
-		return $ncPermissions;
711
-	}
712
-
713
-	/**
714
-	 * update permissions in database
715
-	 *
716
-	 * @param IShare $share
717
-	 * @param int $permissions
718
-	 */
719
-	protected function updatePermissionsInDatabase(IShare $share, $permissions) {
720
-		$query = $this->connection->getQueryBuilder();
721
-		$query->update('share')
722
-			->where($query->expr()->eq('id', $query->createNamedParameter($share->getId())))
723
-			->set('permissions', $query->createNamedParameter($permissions))
724
-			->execute();
725
-	}
726
-
727
-
728
-	/**
729
-	 * get file
730
-	 *
731
-	 * @param string $user
732
-	 * @param int $fileSource
733
-	 * @return array with internal path of the file and a absolute link to it
734
-	 */
735
-	private function getFile($user, $fileSource) {
736
-		\OC_Util::setupFS($user);
737
-
738
-		try {
739
-			$file = Filesystem::getPath($fileSource);
740
-		} catch (NotFoundException $e) {
741
-			$file = null;
742
-		}
743
-		$args = Filesystem::is_dir($file) ? ['dir' => $file] : ['dir' => dirname($file), 'scrollto' => $file];
744
-		$link = Util::linkToAbsolute('files', 'index.php', $args);
745
-
746
-		return [$file, $link];
747
-	}
748
-
749
-	/**
750
-	 * check if we are the initiator or the owner of a re-share and return the correct UID
751
-	 *
752
-	 * @param IShare $share
753
-	 * @return string
754
-	 */
755
-	protected function getCorrectUid(IShare $share) {
756
-		if ($this->userManager->userExists($share->getShareOwner())) {
757
-			return $share->getShareOwner();
758
-		}
759
-
760
-		return $share->getSharedBy();
761
-	}
762
-
763
-
764
-
765
-	/**
766
-	 * check if we got the right share
767
-	 *
768
-	 * @param IShare $share
769
-	 * @param string $token
770
-	 * @return bool
771
-	 * @throws AuthenticationFailedException
772
-	 */
773
-	protected function verifyShare(IShare $share, $token) {
774
-		if (
775
-			$share->getShareType() === IShare::TYPE_REMOTE &&
776
-			$share->getToken() === $token
777
-		) {
778
-			return true;
779
-		}
780
-
781
-		if ($share->getShareType() === IShare::TYPE_CIRCLE) {
782
-			try {
783
-				$knownShare = $this->shareManager->getShareByToken($token);
784
-				if ($knownShare->getId() === $share->getId()) {
785
-					return true;
786
-				}
787
-			} catch (ShareNotFound $e) {
788
-			}
789
-		}
790
-
791
-		throw new AuthenticationFailedException();
792
-	}
793
-
794
-
795
-
796
-	/**
797
-	 * check if server-to-server sharing is enabled
798
-	 *
799
-	 * @param bool $incoming
800
-	 * @return bool
801
-	 */
802
-	private function isS2SEnabled($incoming = false) {
803
-		$result = $this->appManager->isEnabledForUser('files_sharing');
804
-
805
-		if ($incoming) {
806
-			$result = $result && $this->federatedShareProvider->isIncomingServer2serverShareEnabled();
807
-		} else {
808
-			$result = $result && $this->federatedShareProvider->isOutgoingServer2serverShareEnabled();
809
-		}
810
-
811
-		return $result;
812
-	}
813
-
814
-
815
-	/**
816
-	 * get the supported share types, e.g. "user", "group", etc.
817
-	 *
818
-	 * @return array
819
-	 *
820
-	 * @since 14.0.0
821
-	 */
822
-	public function getSupportedShareTypes() {
823
-		return ['user', 'group'];
824
-	}
66
+    /** @var IAppManager */
67
+    private $appManager;
68
+
69
+    /** @var FederatedShareProvider */
70
+    private $federatedShareProvider;
71
+
72
+    /** @var AddressHandler */
73
+    private $addressHandler;
74
+
75
+    /** @var ILogger */
76
+    private $logger;
77
+
78
+    /** @var IUserManager */
79
+    private $userManager;
80
+
81
+    /** @var IManager */
82
+    private $shareManager;
83
+
84
+    /** @var ICloudIdManager */
85
+    private $cloudIdManager;
86
+
87
+    /** @var IActivityManager */
88
+    private $activityManager;
89
+
90
+    /** @var INotificationManager */
91
+    private $notificationManager;
92
+
93
+    /** @var IURLGenerator */
94
+    private $urlGenerator;
95
+
96
+    /** @var ICloudFederationFactory */
97
+    private $cloudFederationFactory;
98
+
99
+    /** @var ICloudFederationProviderManager */
100
+    private $cloudFederationProviderManager;
101
+
102
+    /** @var IDBConnection */
103
+    private $connection;
104
+
105
+    /** @var IGroupManager */
106
+    private $groupManager;
107
+
108
+    /**
109
+     * CloudFederationProvider constructor.
110
+     *
111
+     * @param IAppManager $appManager
112
+     * @param FederatedShareProvider $federatedShareProvider
113
+     * @param AddressHandler $addressHandler
114
+     * @param ILogger $logger
115
+     * @param IUserManager $userManager
116
+     * @param IManager $shareManager
117
+     * @param ICloudIdManager $cloudIdManager
118
+     * @param IActivityManager $activityManager
119
+     * @param INotificationManager $notificationManager
120
+     * @param IURLGenerator $urlGenerator
121
+     * @param ICloudFederationFactory $cloudFederationFactory
122
+     * @param ICloudFederationProviderManager $cloudFederationProviderManager
123
+     * @param IDBConnection $connection
124
+     * @param IGroupManager $groupManager
125
+     */
126
+    public function __construct(IAppManager $appManager,
127
+                                FederatedShareProvider $federatedShareProvider,
128
+                                AddressHandler $addressHandler,
129
+                                ILogger $logger,
130
+                                IUserManager $userManager,
131
+                                IManager $shareManager,
132
+                                ICloudIdManager $cloudIdManager,
133
+                                IActivityManager $activityManager,
134
+                                INotificationManager $notificationManager,
135
+                                IURLGenerator $urlGenerator,
136
+                                ICloudFederationFactory $cloudFederationFactory,
137
+                                ICloudFederationProviderManager $cloudFederationProviderManager,
138
+                                IDBConnection $connection,
139
+                                IGroupManager $groupManager
140
+    ) {
141
+        $this->appManager = $appManager;
142
+        $this->federatedShareProvider = $federatedShareProvider;
143
+        $this->addressHandler = $addressHandler;
144
+        $this->logger = $logger;
145
+        $this->userManager = $userManager;
146
+        $this->shareManager = $shareManager;
147
+        $this->cloudIdManager = $cloudIdManager;
148
+        $this->activityManager = $activityManager;
149
+        $this->notificationManager = $notificationManager;
150
+        $this->urlGenerator = $urlGenerator;
151
+        $this->cloudFederationFactory = $cloudFederationFactory;
152
+        $this->cloudFederationProviderManager = $cloudFederationProviderManager;
153
+        $this->connection = $connection;
154
+        $this->groupManager = $groupManager;
155
+    }
156
+
157
+
158
+
159
+    /**
160
+     * @return string
161
+     */
162
+    public function getShareType() {
163
+        return 'file';
164
+    }
165
+
166
+    /**
167
+     * share received from another server
168
+     *
169
+     * @param ICloudFederationShare $share
170
+     * @return string provider specific unique ID of the share
171
+     *
172
+     * @throws ProviderCouldNotAddShareException
173
+     * @throws \OCP\AppFramework\QueryException
174
+     * @throws \OC\HintException
175
+     * @since 14.0.0
176
+     */
177
+    public function shareReceived(ICloudFederationShare $share) {
178
+        if (!$this->isS2SEnabled(true)) {
179
+            throw new ProviderCouldNotAddShareException('Server does not support federated cloud sharing', '', Http::STATUS_SERVICE_UNAVAILABLE);
180
+        }
181
+
182
+        $protocol = $share->getProtocol();
183
+        if ($protocol['name'] !== 'webdav') {
184
+            throw new ProviderCouldNotAddShareException('Unsupported protocol for data exchange.', '', Http::STATUS_NOT_IMPLEMENTED);
185
+        }
186
+
187
+        [$ownerUid, $remote] = $this->addressHandler->splitUserRemote($share->getOwner());
188
+        // for backward compatibility make sure that the remote url stored in the
189
+        // database ends with a trailing slash
190
+        if (substr($remote, -1) !== '/') {
191
+            $remote = $remote . '/';
192
+        }
193
+
194
+        $token = $share->getShareSecret();
195
+        $name = $share->getResourceName();
196
+        $owner = $share->getOwnerDisplayName();
197
+        $sharedBy = $share->getSharedByDisplayName();
198
+        $shareWith = $share->getShareWith();
199
+        $remoteId = $share->getProviderId();
200
+        $sharedByFederatedId = $share->getSharedBy();
201
+        $ownerFederatedId = $share->getOwner();
202
+        $shareType = $this->mapShareTypeToNextcloud($share->getShareType());
203
+
204
+        // if no explicit information about the person who created the share was send
205
+        // we assume that the share comes from the owner
206
+        if ($sharedByFederatedId === null) {
207
+            $sharedBy = $owner;
208
+            $sharedByFederatedId = $ownerFederatedId;
209
+        }
210
+
211
+        if ($remote && $token && $name && $owner && $remoteId && $shareWith) {
212
+            if (!Util::isValidFileName($name)) {
213
+                throw new ProviderCouldNotAddShareException('The mountpoint name contains invalid characters.', '', Http::STATUS_BAD_REQUEST);
214
+            }
215
+
216
+            // FIXME this should be a method in the user management instead
217
+            if ($shareType === IShare::TYPE_USER) {
218
+                $this->logger->debug('shareWith before, ' . $shareWith, ['app' => 'files_sharing']);
219
+                Util::emitHook(
220
+                    '\OCA\Files_Sharing\API\Server2Server',
221
+                    'preLoginNameUsedAsUserName',
222
+                    ['uid' => &$shareWith]
223
+                );
224
+                $this->logger->debug('shareWith after, ' . $shareWith, ['app' => 'files_sharing']);
225
+
226
+                if (!$this->userManager->userExists($shareWith)) {
227
+                    throw new ProviderCouldNotAddShareException('User does not exists', '',Http::STATUS_BAD_REQUEST);
228
+                }
229
+
230
+                \OC_Util::setupFS($shareWith);
231
+            }
232
+
233
+            if ($shareType === IShare::TYPE_GROUP && !$this->groupManager->groupExists($shareWith)) {
234
+                throw new ProviderCouldNotAddShareException('Group does not exists', '',Http::STATUS_BAD_REQUEST);
235
+            }
236
+
237
+            $externalManager = new \OCA\Files_Sharing\External\Manager(
238
+                \OC::$server->getDatabaseConnection(),
239
+                Filesystem::getMountManager(),
240
+                Filesystem::getLoader(),
241
+                \OC::$server->getHTTPClientService(),
242
+                \OC::$server->getNotificationManager(),
243
+                \OC::$server->query(\OCP\OCS\IDiscoveryService::class),
244
+                \OC::$server->getCloudFederationProviderManager(),
245
+                \OC::$server->getCloudFederationFactory(),
246
+                \OC::$server->getGroupManager(),
247
+                \OC::$server->getUserManager(),
248
+                $shareWith,
249
+                \OC::$server->query(IEventDispatcher::class)
250
+            );
251
+
252
+            try {
253
+                $externalManager->addShare($remote, $token, '', $name, $owner, $shareType,false, $shareWith, $remoteId);
254
+                $shareId = \OC::$server->getDatabaseConnection()->lastInsertId('*PREFIX*share_external');
255
+
256
+                if ($shareType === IShare::TYPE_USER) {
257
+                    $event = $this->activityManager->generateEvent();
258
+                    $event->setApp('files_sharing')
259
+                        ->setType('remote_share')
260
+                        ->setSubject(RemoteShares::SUBJECT_REMOTE_SHARE_RECEIVED, [$ownerFederatedId, trim($name, '/')])
261
+                        ->setAffectedUser($shareWith)
262
+                        ->setObject('remote_share', $shareId, $name);
263
+                    \OC::$server->getActivityManager()->publish($event);
264
+                    $this->notifyAboutNewShare($shareWith, $shareId, $ownerFederatedId, $sharedByFederatedId, $name);
265
+                } else {
266
+                    $groupMembers = $this->groupManager->get($shareWith)->getUsers();
267
+                    foreach ($groupMembers as $user) {
268
+                        $event = $this->activityManager->generateEvent();
269
+                        $event->setApp('files_sharing')
270
+                            ->setType('remote_share')
271
+                            ->setSubject(RemoteShares::SUBJECT_REMOTE_SHARE_RECEIVED, [$ownerFederatedId, trim($name, '/')])
272
+                            ->setAffectedUser($user->getUID())
273
+                            ->setObject('remote_share', $shareId, $name);
274
+                        \OC::$server->getActivityManager()->publish($event);
275
+                        $this->notifyAboutNewShare($user->getUID(), $shareId, $ownerFederatedId, $sharedByFederatedId, $name);
276
+                    }
277
+                }
278
+                return $shareId;
279
+            } catch (\Exception $e) {
280
+                $this->logger->logException($e, [
281
+                    'message' => 'Server can not add remote share.',
282
+                    'level' => ILogger::ERROR,
283
+                    'app' => 'files_sharing'
284
+                ]);
285
+                throw new ProviderCouldNotAddShareException('internal server error, was not able to add share from ' . $remote, '', HTTP::STATUS_INTERNAL_SERVER_ERROR);
286
+            }
287
+        }
288
+
289
+        throw new ProviderCouldNotAddShareException('server can not add remote share, missing parameter', '', HTTP::STATUS_BAD_REQUEST);
290
+    }
291
+
292
+    /**
293
+     * notification received from another server
294
+     *
295
+     * @param string $notificationType (e.g. SHARE_ACCEPTED)
296
+     * @param string $providerId id of the share
297
+     * @param array $notification payload of the notification
298
+     * @return array data send back to the sender
299
+     *
300
+     * @throws ActionNotSupportedException
301
+     * @throws AuthenticationFailedException
302
+     * @throws BadRequestException
303
+     * @throws \OC\HintException
304
+     * @since 14.0.0
305
+     */
306
+    public function notificationReceived($notificationType, $providerId, array $notification) {
307
+        switch ($notificationType) {
308
+            case 'SHARE_ACCEPTED':
309
+                return $this->shareAccepted($providerId, $notification);
310
+            case 'SHARE_DECLINED':
311
+                return $this->shareDeclined($providerId, $notification);
312
+            case 'SHARE_UNSHARED':
313
+                return $this->unshare($providerId, $notification);
314
+            case 'REQUEST_RESHARE':
315
+                return $this->reshareRequested($providerId, $notification);
316
+            case 'RESHARE_UNDO':
317
+                return $this->undoReshare($providerId, $notification);
318
+            case 'RESHARE_CHANGE_PERMISSION':
319
+                return $this->updateResharePermissions($providerId, $notification);
320
+        }
321
+
322
+
323
+        throw new BadRequestException([$notificationType]);
324
+    }
325
+
326
+    /**
327
+     * map OCM share type (strings) to Nextcloud internal share types (integer)
328
+     *
329
+     * @param string $shareType
330
+     * @return int
331
+     */
332
+    private function mapShareTypeToNextcloud($shareType) {
333
+        $result = IShare::TYPE_USER;
334
+        if ($shareType === 'group') {
335
+            $result = IShare::TYPE_GROUP;
336
+        }
337
+
338
+        return $result;
339
+    }
340
+
341
+    private function notifyAboutNewShare($shareWith, $shareId, $ownerFederatedId, $sharedByFederatedId, $name): void {
342
+        $notification = $this->notificationManager->createNotification();
343
+        $notification->setApp('files_sharing')
344
+            ->setUser($shareWith)
345
+            ->setDateTime(new \DateTime())
346
+            ->setObject('remote_share', $shareId)
347
+            ->setSubject('remote_share', [$ownerFederatedId, $sharedByFederatedId, trim($name, '/')]);
348
+
349
+        $declineAction = $notification->createAction();
350
+        $declineAction->setLabel('decline')
351
+            ->setLink($this->urlGenerator->getAbsoluteURL($this->urlGenerator->linkTo('', 'ocs/v2.php/apps/files_sharing/api/v1/remote_shares/pending/' . $shareId)), 'DELETE');
352
+        $notification->addAction($declineAction);
353
+
354
+        $acceptAction = $notification->createAction();
355
+        $acceptAction->setLabel('accept')
356
+            ->setLink($this->urlGenerator->getAbsoluteURL($this->urlGenerator->linkTo('', 'ocs/v2.php/apps/files_sharing/api/v1/remote_shares/pending/' . $shareId)), 'POST');
357
+        $notification->addAction($acceptAction);
358
+
359
+        $this->notificationManager->notify($notification);
360
+    }
361
+
362
+    /**
363
+     * process notification that the recipient accepted a share
364
+     *
365
+     * @param string $id
366
+     * @param array $notification
367
+     * @return array
368
+     * @throws ActionNotSupportedException
369
+     * @throws AuthenticationFailedException
370
+     * @throws BadRequestException
371
+     * @throws \OC\HintException
372
+     */
373
+    private function shareAccepted($id, array $notification) {
374
+        if (!$this->isS2SEnabled()) {
375
+            throw new ActionNotSupportedException('Server does not support federated cloud sharing');
376
+        }
377
+
378
+        if (!isset($notification['sharedSecret'])) {
379
+            throw new BadRequestException(['sharedSecret']);
380
+        }
381
+
382
+        $token = $notification['sharedSecret'];
383
+
384
+        $share = $this->federatedShareProvider->getShareById($id);
385
+
386
+        $this->verifyShare($share, $token);
387
+        $this->executeAcceptShare($share);
388
+        if ($share->getShareOwner() !== $share->getSharedBy()) {
389
+            [, $remote] = $this->addressHandler->splitUserRemote($share->getSharedBy());
390
+            $remoteId = $this->federatedShareProvider->getRemoteId($share);
391
+            $notification = $this->cloudFederationFactory->getCloudFederationNotification();
392
+            $notification->setMessage(
393
+                'SHARE_ACCEPTED',
394
+                'file',
395
+                $remoteId,
396
+                [
397
+                    'sharedSecret' => $token,
398
+                    'message' => 'Recipient accepted the re-share'
399
+                ]
400
+
401
+            );
402
+            $this->cloudFederationProviderManager->sendNotification($remote, $notification);
403
+        }
404
+
405
+        return [];
406
+    }
407
+
408
+    /**
409
+     * @param IShare $share
410
+     * @throws ShareNotFound
411
+     */
412
+    protected function executeAcceptShare(IShare $share) {
413
+        try {
414
+            $fileId = (int)$share->getNode()->getId();
415
+            [$file, $link] = $this->getFile($this->getCorrectUid($share), $fileId);
416
+        } catch (\Exception $e) {
417
+            throw new ShareNotFound();
418
+        }
419
+
420
+        $event = $this->activityManager->generateEvent();
421
+        $event->setApp('files_sharing')
422
+            ->setType('remote_share')
423
+            ->setAffectedUser($this->getCorrectUid($share))
424
+            ->setSubject(RemoteShares::SUBJECT_REMOTE_SHARE_ACCEPTED, [$share->getSharedWith(), [$fileId => $file]])
425
+            ->setObject('files', $fileId, $file)
426
+            ->setLink($link);
427
+        $this->activityManager->publish($event);
428
+    }
429
+
430
+    /**
431
+     * process notification that the recipient declined a share
432
+     *
433
+     * @param string $id
434
+     * @param array $notification
435
+     * @return array
436
+     * @throws ActionNotSupportedException
437
+     * @throws AuthenticationFailedException
438
+     * @throws BadRequestException
439
+     * @throws ShareNotFound
440
+     * @throws \OC\HintException
441
+     *
442
+     */
443
+    protected function shareDeclined($id, array $notification) {
444
+        if (!$this->isS2SEnabled()) {
445
+            throw new ActionNotSupportedException('Server does not support federated cloud sharing');
446
+        }
447
+
448
+        if (!isset($notification['sharedSecret'])) {
449
+            throw new BadRequestException(['sharedSecret']);
450
+        }
451
+
452
+        $token = $notification['sharedSecret'];
453
+
454
+        $share = $this->federatedShareProvider->getShareById($id);
455
+
456
+        $this->verifyShare($share, $token);
457
+
458
+        if ($share->getShareOwner() !== $share->getSharedBy()) {
459
+            [, $remote] = $this->addressHandler->splitUserRemote($share->getSharedBy());
460
+            $remoteId = $this->federatedShareProvider->getRemoteId($share);
461
+            $notification = $this->cloudFederationFactory->getCloudFederationNotification();
462
+            $notification->setMessage(
463
+                'SHARE_DECLINED',
464
+                'file',
465
+                $remoteId,
466
+                [
467
+                    'sharedSecret' => $token,
468
+                    'message' => 'Recipient declined the re-share'
469
+                ]
470
+
471
+            );
472
+            $this->cloudFederationProviderManager->sendNotification($remote, $notification);
473
+        }
474
+
475
+        $this->executeDeclineShare($share);
476
+
477
+        return [];
478
+    }
479
+
480
+    /**
481
+     * delete declined share and create a activity
482
+     *
483
+     * @param IShare $share
484
+     * @throws ShareNotFound
485
+     */
486
+    protected function executeDeclineShare(IShare $share) {
487
+        $this->federatedShareProvider->removeShareFromTable($share);
488
+
489
+        try {
490
+            $fileId = (int)$share->getNode()->getId();
491
+            [$file, $link] = $this->getFile($this->getCorrectUid($share), $fileId);
492
+        } catch (\Exception $e) {
493
+            throw new ShareNotFound();
494
+        }
495
+
496
+        $event = $this->activityManager->generateEvent();
497
+        $event->setApp('files_sharing')
498
+            ->setType('remote_share')
499
+            ->setAffectedUser($this->getCorrectUid($share))
500
+            ->setSubject(RemoteShares::SUBJECT_REMOTE_SHARE_DECLINED, [$share->getSharedWith(), [$fileId => $file]])
501
+            ->setObject('files', $fileId, $file)
502
+            ->setLink($link);
503
+        $this->activityManager->publish($event);
504
+    }
505
+
506
+    /**
507
+     * received the notification that the owner unshared a file from you
508
+     *
509
+     * @param string $id
510
+     * @param array $notification
511
+     * @return array
512
+     * @throws AuthenticationFailedException
513
+     * @throws BadRequestException
514
+     */
515
+    private function undoReshare($id, array $notification) {
516
+        if (!isset($notification['sharedSecret'])) {
517
+            throw new BadRequestException(['sharedSecret']);
518
+        }
519
+        $token = $notification['sharedSecret'];
520
+
521
+        $share = $this->federatedShareProvider->getShareById($id);
522
+
523
+        $this->verifyShare($share, $token);
524
+        $this->federatedShareProvider->removeShareFromTable($share);
525
+        return [];
526
+    }
527
+
528
+    /**
529
+     * unshare file from self
530
+     *
531
+     * @param string $id
532
+     * @param array $notification
533
+     * @return array
534
+     * @throws ActionNotSupportedException
535
+     * @throws BadRequestException
536
+     */
537
+    private function unshare($id, array $notification) {
538
+        if (!$this->isS2SEnabled(true)) {
539
+            throw new ActionNotSupportedException("incoming shares disabled!");
540
+        }
541
+
542
+        if (!isset($notification['sharedSecret'])) {
543
+            throw new BadRequestException(['sharedSecret']);
544
+        }
545
+        $token = $notification['sharedSecret'];
546
+
547
+        $qb = $this->connection->getQueryBuilder();
548
+        $qb->select('*')
549
+            ->from('share_external')
550
+            ->where(
551
+                $qb->expr()->andX(
552
+                    $qb->expr()->eq('remote_id', $qb->createNamedParameter($id)),
553
+                    $qb->expr()->eq('share_token', $qb->createNamedParameter($token))
554
+                )
555
+            );
556
+
557
+        $result = $qb->execute();
558
+        $share = $result->fetch();
559
+        $result->closeCursor();
560
+
561
+        if ($token && $id && !empty($share)) {
562
+            $remote = $this->cleanupRemote($share['remote']);
563
+
564
+            $owner = $this->cloudIdManager->getCloudId($share['owner'], $remote);
565
+            $mountpoint = $share['mountpoint'];
566
+            $user = $share['user'];
567
+
568
+            $qb = $this->connection->getQueryBuilder();
569
+            $qb->delete('share_external')
570
+                ->where(
571
+                    $qb->expr()->andX(
572
+                        $qb->expr()->eq('remote_id', $qb->createNamedParameter($id)),
573
+                        $qb->expr()->eq('share_token', $qb->createNamedParameter($token))
574
+                    )
575
+                );
576
+
577
+            $qb->execute();
578
+
579
+            // delete all child in case of a group share
580
+            $qb = $this->connection->getQueryBuilder();
581
+            $qb->delete('share_external')
582
+                ->where($qb->expr()->eq('parent', $qb->createNamedParameter((int)$share['id'])));
583
+            $qb->execute();
584
+
585
+            if ((int)$share['share_type'] === IShare::TYPE_USER) {
586
+                if ($share['accepted']) {
587
+                    $path = trim($mountpoint, '/');
588
+                } else {
589
+                    $path = trim($share['name'], '/');
590
+                }
591
+                $notification = $this->notificationManager->createNotification();
592
+                $notification->setApp('files_sharing')
593
+                    ->setUser($share['user'])
594
+                    ->setObject('remote_share', (int)$share['id']);
595
+                $this->notificationManager->markProcessed($notification);
596
+
597
+                $event = $this->activityManager->generateEvent();
598
+                $event->setApp('files_sharing')
599
+                    ->setType('remote_share')
600
+                    ->setSubject(RemoteShares::SUBJECT_REMOTE_SHARE_UNSHARED, [$owner->getId(), $path])
601
+                    ->setAffectedUser($user)
602
+                    ->setObject('remote_share', (int)$share['id'], $path);
603
+                \OC::$server->getActivityManager()->publish($event);
604
+            }
605
+        }
606
+
607
+        return [];
608
+    }
609
+
610
+    private function cleanupRemote($remote) {
611
+        $remote = substr($remote, strpos($remote, '://') + 3);
612
+
613
+        return rtrim($remote, '/');
614
+    }
615
+
616
+    /**
617
+     * recipient of a share request to re-share the file with another user
618
+     *
619
+     * @param string $id
620
+     * @param array $notification
621
+     * @return array
622
+     * @throws AuthenticationFailedException
623
+     * @throws BadRequestException
624
+     * @throws ProviderCouldNotAddShareException
625
+     * @throws ShareNotFound
626
+     */
627
+    protected function reshareRequested($id, array $notification) {
628
+        if (!isset($notification['sharedSecret'])) {
629
+            throw new BadRequestException(['sharedSecret']);
630
+        }
631
+        $token = $notification['sharedSecret'];
632
+
633
+        if (!isset($notification['shareWith'])) {
634
+            throw new BadRequestException(['shareWith']);
635
+        }
636
+        $shareWith = $notification['shareWith'];
637
+
638
+        if (!isset($notification['senderId'])) {
639
+            throw new BadRequestException(['senderId']);
640
+        }
641
+        $senderId = $notification['senderId'];
642
+
643
+        $share = $this->federatedShareProvider->getShareById($id);
644
+        // don't allow to share a file back to the owner
645
+        try {
646
+            [$user, $remote] = $this->addressHandler->splitUserRemote($shareWith);
647
+            $owner = $share->getShareOwner();
648
+            $currentServer = $this->addressHandler->generateRemoteURL();
649
+            if ($this->addressHandler->compareAddresses($user, $remote, $owner, $currentServer)) {
650
+                throw new ProviderCouldNotAddShareException('Resharing back to the owner is not allowed: ' . $id);
651
+            }
652
+        } catch (\Exception $e) {
653
+            throw new ProviderCouldNotAddShareException($e->getMessage());
654
+        }
655
+
656
+        $this->verifyShare($share, $token);
657
+
658
+        // check if re-sharing is allowed
659
+        if ($share->getPermissions() & Constants::PERMISSION_SHARE) {
660
+            // the recipient of the initial share is now the initiator for the re-share
661
+            $share->setSharedBy($share->getSharedWith());
662
+            $share->setSharedWith($shareWith);
663
+            $result = $this->federatedShareProvider->create($share);
664
+            $this->federatedShareProvider->storeRemoteId((int)$result->getId(), $senderId);
665
+            return ['token' => $result->getToken(), 'providerId' => $result->getId()];
666
+        } else {
667
+            throw new ProviderCouldNotAddShareException('resharing not allowed for share: ' . $id);
668
+        }
669
+    }
670
+
671
+    /**
672
+     * update permission of a re-share so that the share dialog shows the right
673
+     * permission if the owner or the sender changes the permission
674
+     *
675
+     * @param string $id
676
+     * @param array $notification
677
+     * @return array
678
+     * @throws AuthenticationFailedException
679
+     * @throws BadRequestException
680
+     */
681
+    protected function updateResharePermissions($id, array $notification) {
682
+        throw new HintException('Updating reshares not allowed');
683
+    }
684
+
685
+    /**
686
+     * translate OCM Permissions to Nextcloud permissions
687
+     *
688
+     * @param array $ocmPermissions
689
+     * @return int
690
+     * @throws BadRequestException
691
+     */
692
+    protected function ocmPermissions2ncPermissions(array $ocmPermissions) {
693
+        $ncPermissions = 0;
694
+        foreach ($ocmPermissions as $permission) {
695
+            switch (strtolower($permission)) {
696
+                case 'read':
697
+                    $ncPermissions += Constants::PERMISSION_READ;
698
+                    break;
699
+                case 'write':
700
+                    $ncPermissions += Constants::PERMISSION_CREATE + Constants::PERMISSION_UPDATE;
701
+                    break;
702
+                case 'share':
703
+                    $ncPermissions += Constants::PERMISSION_SHARE;
704
+                    break;
705
+                default:
706
+                    throw new BadRequestException(['permission']);
707
+            }
708
+        }
709
+
710
+        return $ncPermissions;
711
+    }
712
+
713
+    /**
714
+     * update permissions in database
715
+     *
716
+     * @param IShare $share
717
+     * @param int $permissions
718
+     */
719
+    protected function updatePermissionsInDatabase(IShare $share, $permissions) {
720
+        $query = $this->connection->getQueryBuilder();
721
+        $query->update('share')
722
+            ->where($query->expr()->eq('id', $query->createNamedParameter($share->getId())))
723
+            ->set('permissions', $query->createNamedParameter($permissions))
724
+            ->execute();
725
+    }
726
+
727
+
728
+    /**
729
+     * get file
730
+     *
731
+     * @param string $user
732
+     * @param int $fileSource
733
+     * @return array with internal path of the file and a absolute link to it
734
+     */
735
+    private function getFile($user, $fileSource) {
736
+        \OC_Util::setupFS($user);
737
+
738
+        try {
739
+            $file = Filesystem::getPath($fileSource);
740
+        } catch (NotFoundException $e) {
741
+            $file = null;
742
+        }
743
+        $args = Filesystem::is_dir($file) ? ['dir' => $file] : ['dir' => dirname($file), 'scrollto' => $file];
744
+        $link = Util::linkToAbsolute('files', 'index.php', $args);
745
+
746
+        return [$file, $link];
747
+    }
748
+
749
+    /**
750
+     * check if we are the initiator or the owner of a re-share and return the correct UID
751
+     *
752
+     * @param IShare $share
753
+     * @return string
754
+     */
755
+    protected function getCorrectUid(IShare $share) {
756
+        if ($this->userManager->userExists($share->getShareOwner())) {
757
+            return $share->getShareOwner();
758
+        }
759
+
760
+        return $share->getSharedBy();
761
+    }
762
+
763
+
764
+
765
+    /**
766
+     * check if we got the right share
767
+     *
768
+     * @param IShare $share
769
+     * @param string $token
770
+     * @return bool
771
+     * @throws AuthenticationFailedException
772
+     */
773
+    protected function verifyShare(IShare $share, $token) {
774
+        if (
775
+            $share->getShareType() === IShare::TYPE_REMOTE &&
776
+            $share->getToken() === $token
777
+        ) {
778
+            return true;
779
+        }
780
+
781
+        if ($share->getShareType() === IShare::TYPE_CIRCLE) {
782
+            try {
783
+                $knownShare = $this->shareManager->getShareByToken($token);
784
+                if ($knownShare->getId() === $share->getId()) {
785
+                    return true;
786
+                }
787
+            } catch (ShareNotFound $e) {
788
+            }
789
+        }
790
+
791
+        throw new AuthenticationFailedException();
792
+    }
793
+
794
+
795
+
796
+    /**
797
+     * check if server-to-server sharing is enabled
798
+     *
799
+     * @param bool $incoming
800
+     * @return bool
801
+     */
802
+    private function isS2SEnabled($incoming = false) {
803
+        $result = $this->appManager->isEnabledForUser('files_sharing');
804
+
805
+        if ($incoming) {
806
+            $result = $result && $this->federatedShareProvider->isIncomingServer2serverShareEnabled();
807
+        } else {
808
+            $result = $result && $this->federatedShareProvider->isOutgoingServer2serverShareEnabled();
809
+        }
810
+
811
+        return $result;
812
+    }
813
+
814
+
815
+    /**
816
+     * get the supported share types, e.g. "user", "group", etc.
817
+     *
818
+     * @return array
819
+     *
820
+     * @since 14.0.0
821
+     */
822
+    public function getSupportedShareTypes() {
823
+        return ['user', 'group'];
824
+    }
825 825
 }
Please login to merge, or discard this patch.