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