Completed
Push — master ( a89f9a...3036b1 )
by Morris
29:19 queued 12:13
created

CloudFederationProviderFiles::shareReceived()   F

Complexity

Conditions 19
Paths 134

Size

Total Lines 116

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 19
c 0
b 0
f 0
nc 134
nop 1
dl 0
loc 116
rs 3.3866

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * @copyright Copyright (c) 2018 Bjoern Schiessle <[email protected]>
4
 *
5
 * @license GNU AGPL version 3 or any later version
6
 *
7
 * This program is free software: you can redistribute it and/or modify
8
 * it under the terms of the GNU Affero General Public License as
9
 * published by the Free Software Foundation, either version 3 of the
10
 * License, or (at your option) any later version.
11
 *
12
 * This program is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 * GNU Affero General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU Affero General Public License
18
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
19
 *
20
 */
21
22
namespace OCA\FederatedFileSharing\OCM;
23
24
use OC\AppFramework\Http;
25
use OC\Files\Filesystem;
26
use OCA\Files_Sharing\Activity\Providers\RemoteShares;
27
use OCA\FederatedFileSharing\AddressHandler;
28
use OCA\FederatedFileSharing\FederatedShareProvider;
29
use OCP\Activity\IManager as IActivityManager;
30
use OCP\App\IAppManager;
31
use OCP\Constants;
32
use OCP\Federation\Exceptions\ActionNotSupportedException;
33
use OCP\Federation\Exceptions\AuthenticationFailedException;
34
use OCP\Federation\Exceptions\BadRequestException;
35
use OCP\Federation\Exceptions\ProviderCouldNotAddShareException;
36
use OCP\Federation\ICloudFederationFactory;
37
use OCP\Federation\ICloudFederationProvider;
38
use OCP\Federation\ICloudFederationProviderManager;
39
use OCP\Federation\ICloudFederationShare;
40
use OCP\Federation\ICloudIdManager;
41
use OCP\Files\NotFoundException;
42
use OCP\IDBConnection;
43
use OCP\IGroupManager;
44
use OCP\ILogger;
45
use OCP\IURLGenerator;
46
use OCP\IUserManager;
47
use OCP\Notification\IManager as INotificationManager;
48
use OCP\Share;
49
use OCP\Share\Exceptions\ShareNotFound;
50
use OCP\Share\IShare;
51
use OCP\Util;
52
53
class CloudFederationProviderFiles implements ICloudFederationProvider {
54
55
	/** @var IAppManager */
56
	private $appManager;
57
58
	/** @var FederatedShareProvider */
59
	private $federatedShareProvider;
60
61
	/** @var AddressHandler */
62
	private $addressHandler;
63
64
	/** @var ILogger */
65
	private $logger;
66
67
	/** @var IUserManager */
68
	private $userManager;
69
70
	/** @var ICloudIdManager */
71
	private $cloudIdManager;
72
73
	/** @var IActivityManager */
74
	private $activityManager;
75
76
	/** @var INotificationManager */
77
	private $notificationManager;
78
79
	/** @var IURLGenerator */
80
	private $urlGenerator;
81
82
	/** @var ICloudFederationFactory */
83
	private $cloudFederationFactory;
84
85
	/** @var ICloudFederationProviderManager */
86
	private $cloudFederationProviderManager;
87
88
	/** @var IDBConnection */
89
	private $connection;
90
91
	/** @var IGroupManager */
92
	private $groupManager;
93
94
	/**
95
	 * CloudFederationProvider constructor.
96
	 *
97
	 * @param IAppManager $appManager
98
	 * @param FederatedShareProvider $federatedShareProvider
99
	 * @param AddressHandler $addressHandler
100
	 * @param ILogger $logger
101
	 * @param IUserManager $userManager
102
	 * @param ICloudIdManager $cloudIdManager
103
	 * @param IActivityManager $activityManager
104
	 * @param INotificationManager $notificationManager
105
	 * @param IURLGenerator $urlGenerator
106
	 * @param ICloudFederationFactory $cloudFederationFactory
107
	 * @param ICloudFederationProviderManager $cloudFederationProviderManager
108
	 * @param IDBConnection $connection
109
	 * @param IGroupManager $groupManager
110
	 */
111 View Code Duplication
	public function __construct(IAppManager $appManager,
112
								FederatedShareProvider $federatedShareProvider,
113
								AddressHandler $addressHandler,
114
								ILogger $logger,
115
								IUserManager $userManager,
116
								ICloudIdManager $cloudIdManager,
117
								IActivityManager $activityManager,
118
								INotificationManager $notificationManager,
119
								IURLGenerator $urlGenerator,
120
								ICloudFederationFactory $cloudFederationFactory,
121
								ICloudFederationProviderManager $cloudFederationProviderManager,
122
								IDBConnection $connection,
123
								IGroupManager $groupManager
124
	) {
125
		$this->appManager = $appManager;
126
		$this->federatedShareProvider = $federatedShareProvider;
127
		$this->addressHandler = $addressHandler;
128
		$this->logger = $logger;
129
		$this->userManager = $userManager;
130
		$this->cloudIdManager = $cloudIdManager;
131
		$this->activityManager = $activityManager;
132
		$this->notificationManager = $notificationManager;
133
		$this->urlGenerator = $urlGenerator;
134
		$this->cloudFederationFactory = $cloudFederationFactory;
135
		$this->cloudFederationProviderManager = $cloudFederationProviderManager;
136
		$this->connection = $connection;
137
		$this->groupManager = $groupManager;
138
	}
139
140
141
142
	/**
143
	 * @return string
144
	 */
145
	public function getShareType() {
146
		return 'file';
147
	}
148
149
	/**
150
	 * share received from another server
151
	 *
152
	 * @param ICloudFederationShare $share
153
	 * @return string provider specific unique ID of the share
154
	 *
155
	 * @throws ProviderCouldNotAddShareException
156
	 * @throws \OCP\AppFramework\QueryException
157
	 * @throws \OC\HintException
158
	 * @since 14.0.0
159
	 */
160
	public function shareReceived(ICloudFederationShare $share) {
161
162
		if (!$this->isS2SEnabled(true)) {
163
			throw new ProviderCouldNotAddShareException('Server does not support federated cloud sharing', '', Http::STATUS_SERVICE_UNAVAILABLE);
164
		}
165
166
		$protocol = $share->getProtocol();
167
		if ($protocol['name'] !== 'webdav') {
168
			throw new ProviderCouldNotAddShareException('Unsupported protocol for data exchange.', '', Http::STATUS_NOT_IMPLEMENTED);
169
		}
170
171
		list($ownerUid, $remote) = $this->addressHandler->splitUserRemote($share->getOwner());
0 ignored issues
show
Unused Code introduced by
The assignment to $ownerUid is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
172
		// for backward compatibility make sure that the remote url stored in the
173
		// database ends with a trailing slash
174
		if (substr($remote, -1) !== '/') {
175
			$remote = $remote . '/';
176
		}
177
178
		$token = $share->getShareSecret();
179
		$name = $share->getResourceName();
180
		$owner = $share->getOwnerDisplayName();
181
		$sharedBy = $share->getSharedByDisplayName();
0 ignored issues
show
Unused Code introduced by
$sharedBy is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
182
		$shareWith = $share->getShareWith();
183
		$remoteId = $share->getProviderId();
184
		$sharedByFederatedId = $share->getSharedBy();
185
		$ownerFederatedId = $share->getOwner();
186
		$shareType = $this->mapShareTypeToNextcloud($share->getShareType());
187
188
		// if no explicit information about the person who created the share was send
189
		// we assume that the share comes from the owner
190
		if ($sharedByFederatedId === null) {
191
			$sharedBy = $owner;
0 ignored issues
show
Unused Code introduced by
$sharedBy is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
192
			$sharedByFederatedId = $ownerFederatedId;
193
		}
194
195
		if ($remote && $token && $name && $owner && $remoteId && $shareWith) {
196
197
			if (!Util::isValidFileName($name)) {
198
				throw new ProviderCouldNotAddShareException('The mountpoint name contains invalid characters.', '', Http::STATUS_BAD_REQUEST);
199
			}
200
201
			// FIXME this should be a method in the user management instead
202
			if ($shareType === Share::SHARE_TYPE_USER) {
203
				$this->logger->debug('shareWith before, ' . $shareWith, ['app' => 'files_sharing']);
204
				Util::emitHook(
205
					'\OCA\Files_Sharing\API\Server2Server',
206
					'preLoginNameUsedAsUserName',
207
					array('uid' => &$shareWith)
208
				);
209
				$this->logger->debug('shareWith after, ' . $shareWith, ['app' => 'files_sharing']);
210
211
				if (!$this->userManager->userExists($shareWith)) {
212
					throw new ProviderCouldNotAddShareException('User does not exists', '',Http::STATUS_BAD_REQUEST);
213
				}
214
215
				\OC_Util::setupFS($shareWith);
216
			}
217
218
			if ($shareType === Share::SHARE_TYPE_GROUP && !$this->groupManager->groupExists($shareWith)) {
219
				throw new ProviderCouldNotAddShareException('Group does not exists', '',Http::STATUS_BAD_REQUEST);
220
			}
221
222
			$externalManager = new \OCA\Files_Sharing\External\Manager(
223
				\OC::$server->getDatabaseConnection(),
224
				Filesystem::getMountManager(),
225
				Filesystem::getLoader(),
226
				\OC::$server->getHTTPClientService(),
227
				\OC::$server->getNotificationManager(),
228
				\OC::$server->query(\OCP\OCS\IDiscoveryService::class),
229
				\OC::$server->getCloudFederationProviderManager(),
230
				\OC::$server->getCloudFederationFactory(),
231
				\OC::$server->getGroupManager(),
232
				\OC::$server->getUserManager(),
233
				$shareWith
234
			);
235
236
			try {
237
				$externalManager->addShare($remote, $token, '', $name, $owner, $shareType,false, $shareWith, $remoteId);
238
				$shareId = \OC::$server->getDatabaseConnection()->lastInsertId('*PREFIX*share_external');
239
240
				if ($shareType === Share::SHARE_TYPE_USER) {
241
					$event = $this->activityManager->generateEvent();
242
					$event->setApp('files_sharing')
243
						->setType('remote_share')
244
						->setSubject(RemoteShares::SUBJECT_REMOTE_SHARE_RECEIVED, [$ownerFederatedId, trim($name, '/')])
245
						->setAffectedUser($shareWith)
246
						->setObject('remote_share', (int)$shareId, $name);
247
					\OC::$server->getActivityManager()->publish($event);
248
					$this->notifyAboutNewShare($shareWith, $shareId, $ownerFederatedId, $sharedByFederatedId, $name);
249
				} else {
250
					$groupMembers = $this->groupManager->get($shareWith)->getUsers();
251
					foreach ($groupMembers as $user) {
252
						$event = $this->activityManager->generateEvent();
253
						$event->setApp('files_sharing')
254
							->setType('remote_share')
255
							->setSubject(RemoteShares::SUBJECT_REMOTE_SHARE_RECEIVED, [$ownerFederatedId, trim($name, '/')])
256
							->setAffectedUser($user->getUID())
257
							->setObject('remote_share', (int)$shareId, $name);
258
						\OC::$server->getActivityManager()->publish($event);
259
						$this->notifyAboutNewShare($user->getUID(), $shareId, $ownerFederatedId, $sharedByFederatedId, $name);
260
					}
261
				}
262
				return $shareId;
263
			} catch (\Exception $e) {
264
				$this->logger->logException($e, [
0 ignored issues
show
Documentation introduced by
$e is of type object<Exception>, but the function expects a object<Throwable>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
265
					'message' => 'Server can not add remote share.',
266
					'level' => ILogger::ERROR,
267
					'app' => 'files_sharing'
268
				]);
269
				throw new ProviderCouldNotAddShareException('internal server error, was not able to add share from ' . $remote, '', HTTP::STATUS_INTERNAL_SERVER_ERROR);
270
			}
271
		}
272
273
		throw new ProviderCouldNotAddShareException('server can not add remote share, missing parameter', '', HTTP::STATUS_BAD_REQUEST);
274
275
	}
276
277
	/**
278
	 * notification received from another server
279
	 *
280
	 * @param string $notificationType (e.g. SHARE_ACCEPTED)
281
	 * @param string $providerId id of the share
282
	 * @param array $notification payload of the notification
283
	 * @return array data send back to the sender
284
	 *
285
	 * @throws ActionNotSupportedException
286
	 * @throws AuthenticationFailedException
287
	 * @throws BadRequestException
288
	 * @throws \OC\HintException
289
	 * @since 14.0.0
290
	 */
291
	public function notificationReceived($notificationType, $providerId, array $notification) {
292
293
		switch ($notificationType) {
294
			case 'SHARE_ACCEPTED':
295
				return $this->shareAccepted($providerId, $notification);
296
			case 'SHARE_DECLINED':
297
				return $this->shareDeclined($providerId, $notification);
298
			case 'SHARE_UNSHARED':
299
				return $this->unshare($providerId, $notification);
300
			case 'REQUEST_RESHARE':
301
				return $this->reshareRequested($providerId, $notification);
302
			case 'RESHARE_UNDO':
303
				return $this->undoReshare($providerId, $notification);
304
			case 'RESHARE_CHANGE_PERMISSION':
305
				return $this->updateResharePermissions($providerId, $notification);
306
		}
307
308
309
		throw new BadRequestException([$notificationType]);
310
	}
311
312
	/**
313
	 * map OCM share type (strings) to Nextcloud internal share types (integer)
314
	 *
315
	 * @param string $shareType
316
	 * @return int
317
	 */
318
	private function mapShareTypeToNextcloud($shareType) {
319
		$result = Share::SHARE_TYPE_USER;
320
		if ($shareType === 'group') {
321
			$result = Share::SHARE_TYPE_GROUP;
322
		}
323
324
		return $result;
325
	}
326
327
	/**
328
	 * notify user about new federated share
329
	 *
330
	 * @param $shareWith
331
	 * @param $shareId
332
	 * @param $ownerFederatedId
333
	 * @param $sharedByFederatedId
334
	 * @param $name
335
	 */
336
	private function notifyAboutNewShare($shareWith, $shareId, $ownerFederatedId, $sharedByFederatedId, $name) {
337
		$notification = $this->notificationManager->createNotification();
338
		$notification->setApp('files_sharing')
339
			->setUser($shareWith)
340
			->setDateTime(new \DateTime())
341
			->setObject('remote_share', $shareId)
342
			->setSubject('remote_share', [$ownerFederatedId, $sharedByFederatedId, trim($name, '/')]);
343
344
		$declineAction = $notification->createAction();
345
		$declineAction->setLabel('decline')
346
			->setLink($this->urlGenerator->getAbsoluteURL($this->urlGenerator->linkTo('', 'ocs/v2.php/apps/files_sharing/api/v1/remote_shares/pending/' . $shareId)), 'DELETE');
347
		$notification->addAction($declineAction);
348
349
		$acceptAction = $notification->createAction();
350
		$acceptAction->setLabel('accept')
351
			->setLink($this->urlGenerator->getAbsoluteURL($this->urlGenerator->linkTo('', 'ocs/v2.php/apps/files_sharing/api/v1/remote_shares/pending/' . $shareId)), 'POST');
352
		$notification->addAction($acceptAction);
353
354
		$this->notificationManager->notify($notification);
355
	}
356
357
	/**
358
	 * process notification that the recipient accepted a share
359
	 *
360
	 * @param string $id
361
	 * @param array $notification
362
	 * @return array
363
	 * @throws ActionNotSupportedException
364
	 * @throws AuthenticationFailedException
365
	 * @throws BadRequestException
366
	 * @throws \OC\HintException
367
	 */
368 View Code Duplication
	private function shareAccepted($id, array $notification) {
369
370
		if (!$this->isS2SEnabled()) {
371
			throw new ActionNotSupportedException('Server does not support federated cloud sharing');
372
		}
373
374
		if (!isset($notification['sharedSecret'])) {
375
			throw new BadRequestException(['sharedSecret']);
376
		}
377
378
		$token = $notification['sharedSecret'];
379
380
		$share = $this->federatedShareProvider->getShareById($id);
381
382
		$this->verifyShare($share, $token);
383
		$this->executeAcceptShare($share);
384
		if ($share->getShareOwner() !== $share->getSharedBy()) {
385
			list(, $remote) = $this->addressHandler->splitUserRemote($share->getSharedBy());
386
			$remoteId = $this->federatedShareProvider->getRemoteId($share);
387
			$notification = $this->cloudFederationFactory->getCloudFederationNotification();
388
			$notification->setMessage(
389
				'SHARE_ACCEPTED',
390
				'file',
391
				$remoteId,
392
				[
393
					'sharedSecret' => $token,
394
					'message' => 'Recipient accepted the re-share'
395
				]
396
397
			);
398
			$this->cloudFederationProviderManager->sendNotification($remote, $notification);
399
400
		}
401
402
		return [];
403
	}
404
405
	/**
406
	 * @param IShare $share
407
	 * @throws ShareNotFound
408
	 */
409 View Code Duplication
	protected function executeAcceptShare(IShare $share) {
410
		try {
411
			$fileId = (int)$share->getNode()->getId();
412
			list($file, $link) = $this->getFile($this->getCorrectUid($share), $fileId);
413
		} catch (\Exception $e) {
414
			throw new ShareNotFound();
415
		}
416
417
		$event = $this->activityManager->generateEvent();
418
		$event->setApp('files_sharing')
419
			->setType('remote_share')
420
			->setAffectedUser($this->getCorrectUid($share))
421
			->setSubject(RemoteShares::SUBJECT_REMOTE_SHARE_ACCEPTED, [$share->getSharedWith(), [$fileId => $file]])
422
			->setObject('files', $fileId, $file)
423
			->setLink($link);
424
		$this->activityManager->publish($event);
425
	}
426
427
	/**
428
	 * process notification that the recipient declined a share
429
	 *
430
	 * @param string $id
431
	 * @param array $notification
432
	 * @return array
433
	 * @throws ActionNotSupportedException
434
	 * @throws AuthenticationFailedException
435
	 * @throws BadRequestException
436
	 * @throws ShareNotFound
437
	 * @throws \OC\HintException
438
	 *
439
	 */
440 View Code Duplication
	protected function shareDeclined($id, array $notification) {
441
442
		if (!$this->isS2SEnabled()) {
443
			throw new ActionNotSupportedException('Server does not support federated cloud sharing');
444
		}
445
446
		if (!isset($notification['sharedSecret'])) {
447
			throw new BadRequestException(['sharedSecret']);
448
		}
449
450
		$token = $notification['sharedSecret'];
451
452
		$share = $this->federatedShareProvider->getShareById($id);
453
454
		$this->verifyShare($share, $token);
455
456
		if ($share->getShareOwner() !== $share->getSharedBy()) {
457
			list(, $remote) = $this->addressHandler->splitUserRemote($share->getSharedBy());
458
			$remoteId = $this->federatedShareProvider->getRemoteId($share);
459
			$notification = $this->cloudFederationFactory->getCloudFederationNotification();
460
			$notification->setMessage(
461
				'SHARE_DECLINED',
462
				'file',
463
				$remoteId,
464
				[
465
					'sharedSecret' => $token,
466
					'message' => 'Recipient declined the re-share'
467
				]
468
469
			);
470
			$this->cloudFederationProviderManager->sendNotification($remote, $notification);
471
		}
472
473
		$this->executeDeclineShare($share);
474
475
		return [];
476
477
	}
478
479
	/**
480
	 * delete declined share and create a activity
481
	 *
482
	 * @param IShare $share
483
	 * @throws ShareNotFound
484
	 */
485 View Code Duplication
	protected function executeDeclineShare(IShare $share) {
486
		$this->federatedShareProvider->removeShareFromTable($share);
487
488
		try {
489
			$fileId = (int)$share->getNode()->getId();
490
			list($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
	/**
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
539
		if (!$this->isS2SEnabled(true)) {
540
			throw new ActionNotSupportedException("incoming shares disabled!");
541
		}
542
543
		if (!isset($notification['sharedSecret'])) {
544
			throw new BadRequestException(['sharedSecret']);
545
		}
546
		$token = $notification['sharedSecret'];
547
548
		$qb = $this->connection->getQueryBuilder();
549
		$qb->select('*')
550
			->from('share_external')
551
			->where(
552
				$qb->expr()->andX(
553
					$qb->expr()->eq('remote_id', $qb->createNamedParameter($id)),
554
					$qb->expr()->eq('share_token', $qb->createNamedParameter($token))
555
				)
556
			);
557
558
		$result = $qb->execute();
559
		$share = $result->fetch();
560
		$result->closeCursor();
561
562
		if ($token && $id && !empty($share)) {
563
564
			$remote = $this->cleanupRemote($share['remote']);
565
566
			$owner = $this->cloudIdManager->getCloudId($share['owner'], $remote);
567
			$mountpoint = $share['mountpoint'];
568
			$user = $share['user'];
569
570
			$qb = $this->connection->getQueryBuilder();
571
			$qb->delete('share_external')
572
				->where(
573
					$qb->expr()->andX(
574
						$qb->expr()->eq('remote_id', $qb->createNamedParameter($id)),
575
						$qb->expr()->eq('share_token', $qb->createNamedParameter($token))
576
					)
577
				);
578
579
			$qb->execute();
580
581
			// delete all child in case of a group share
582
			$qb = $this->connection->getQueryBuilder();
583
			$qb->delete('share_external')
584
				->where($qb->expr()->eq('parent', $qb->createNamedParameter((int)$share['id'])));
585
			$qb->execute();
586
587
			if ((int)$share['share_type'] === Share::SHARE_TYPE_USER) {
588
				if ($share['accepted']) {
589
					$path = trim($mountpoint, '/');
590
				} else {
591
					$path = trim($share['name'], '/');
592
				}
593
				$notification = $this->notificationManager->createNotification();
594
				$notification->setApp('files_sharing')
595
					->setUser($share['user'])
596
					->setObject('remote_share', (int)$share['id']);
597
				$this->notificationManager->markProcessed($notification);
598
599
				$event = $this->activityManager->generateEvent();
600
				$event->setApp('files_sharing')
601
					->setType('remote_share')
602
					->setSubject(RemoteShares::SUBJECT_REMOTE_SHARE_UNSHARED, [$owner->getId(), $path])
603
					->setAffectedUser($user)
604
					->setObject('remote_share', (int)$share['id'], $path);
605
				\OC::$server->getActivityManager()->publish($event);
606
			}
607
		}
608
609
		return [];
610
	}
611
612
	private function cleanupRemote($remote) {
613
		$remote = substr($remote, strpos($remote, '://') + 3);
614
615
		return rtrim($remote, '/');
616
	}
617
618
	/**
619
	 * recipient of a share request to re-share the file with another user
620
	 *
621
	 * @param string $id
622
	 * @param array $notification
623
	 * @return array
624
	 * @throws AuthenticationFailedException
625
	 * @throws BadRequestException
626
	 * @throws ProviderCouldNotAddShareException
627
	 * @throws ShareNotFound
628
	 */
629
	protected function reshareRequested($id, array $notification) {
630
631
		if (!isset($notification['sharedSecret'])) {
632
			throw new BadRequestException(['sharedSecret']);
633
		}
634
		$token = $notification['sharedSecret'];
635
636
		if (!isset($notification['shareWith'])) {
637
			throw new BadRequestException(['shareWith']);
638
		}
639
		$shareWith = $notification['shareWith'];
640
641
		if (!isset($notification['senderId'])) {
642
			throw new BadRequestException(['senderId']);
643
		}
644
		$senderId = $notification['senderId'];
645
646
		$share = $this->federatedShareProvider->getShareById($id);
647
		// don't allow to share a file back to the owner
648
		try {
649
			list($user, $remote) = $this->addressHandler->splitUserRemote($shareWith);
650
			$owner = $share->getShareOwner();
651
			$currentServer = $this->addressHandler->generateRemoteURL();
652
			if ($this->addressHandler->compareAddresses($user, $remote, $owner, $currentServer)) {
653
				throw new ProviderCouldNotAddShareException('Resharing back to the owner is not allowed: ' . $id);
654
			}
655
		} catch (\Exception $e) {
656
			throw new ProviderCouldNotAddShareException($e->getMessage());
657
		}
658
659
		$this->verifyShare($share, $token);
660
661
		// check if re-sharing is allowed
662
		if ($share->getPermissions() & Constants::PERMISSION_SHARE) {
663
			// the recipient of the initial share is now the initiator for the re-share
664
			$share->setSharedBy($share->getSharedWith());
665
			$share->setSharedWith($shareWith);
666
			$result = $this->federatedShareProvider->create($share);
667
			$this->federatedShareProvider->storeRemoteId((int)$result->getId(), $senderId);
668
			return ['token' => $result->getToken(), 'providerId' => $result->getId()];
669
		} else {
670
			throw new ProviderCouldNotAddShareException('resharing not allowed for share: ' . $id);
671
		}
672
673
	}
674
675
	/**
676
	 * update permission of a re-share so that the share dialog shows the right
677
	 * permission if the owner or the sender changes the permission
678
	 *
679
	 * @param string $id
680
	 * @param array $notification
681
	 * @return array
682
	 * @throws AuthenticationFailedException
683
	 * @throws BadRequestException
684
	 */
685
	protected function updateResharePermissions($id, array $notification) {
686
687
		if (!isset($notification['sharedSecret'])) {
688
			throw new BadRequestException(['sharedSecret']);
689
		}
690
		$token = $notification['sharedSecret'];
691
692
		if (!isset($notification['permission'])) {
693
			throw new BadRequestException(['permission']);
694
		}
695
		$ocmPermissions = $notification['permission'];
696
697
		$share = $this->federatedShareProvider->getShareById($id);
698
699
		$ncPermission = $this->ocmPermissions2ncPermissions($ocmPermissions);
700
701
		$this->verifyShare($share, $token);
702
		$this->updatePermissionsInDatabase($share, $ncPermission);
703
704
		return [];
705
	}
706
707
	/**
708
	 * translate OCM Permissions to Nextcloud permissions
709
	 *
710
	 * @param array $ocmPermissions
711
	 * @return int
712
	 * @throws BadRequestException
713
	 */
714
	protected function ocmPermissions2ncPermissions(array $ocmPermissions) {
715
		$ncPermissions = 0;
716 View Code Duplication
		foreach($ocmPermissions as $permission) {
717
			switch (strtolower($permission)) {
718
				case 'read':
719
					$ncPermissions += Constants::PERMISSION_READ;
720
					break;
721
				case 'write':
722
					$ncPermissions += Constants::PERMISSION_CREATE + Constants::PERMISSION_UPDATE;
723
					break;
724
				case 'share':
725
					$ncPermissions += Constants::PERMISSION_SHARE;
726
					break;
727
				default:
728
					throw new BadRequestException(['permission']);
729
			}
730
731
		}
732
733
		return $ncPermissions;
734
	}
735
736
	/**
737
	 * update permissions in database
738
	 *
739
	 * @param IShare $share
740
	 * @param int $permissions
741
	 */
742 View Code Duplication
	protected function updatePermissionsInDatabase(IShare $share, $permissions) {
743
		$query = $this->connection->getQueryBuilder();
744
		$query->update('share')
745
			->where($query->expr()->eq('id', $query->createNamedParameter($share->getId())))
746
			->set('permissions', $query->createNamedParameter($permissions))
747
			->execute();
748
	}
749
750
751
	/**
752
	 * get file
753
	 *
754
	 * @param string $user
755
	 * @param int $fileSource
756
	 * @return array with internal path of the file and a absolute link to it
757
	 */
758
	private function getFile($user, $fileSource) {
759
		\OC_Util::setupFS($user);
760
761
		try {
762
			$file = Filesystem::getPath($fileSource);
763
		} catch (NotFoundException $e) {
764
			$file = null;
765
		}
766
		$args = Filesystem::is_dir($file) ? array('dir' => $file) : array('dir' => dirname($file), 'scrollto' => $file);
767
		$link = Util::linkToAbsolute('files', 'index.php', $args);
768
769
		return [$file, $link];
770
771
	}
772
773
	/**
774
	 * check if we are the initiator or the owner of a re-share and return the correct UID
775
	 *
776
	 * @param IShare $share
777
	 * @return string
778
	 */
779
	protected function getCorrectUid(IShare $share) {
780
		if ($this->userManager->userExists($share->getShareOwner())) {
781
			return $share->getShareOwner();
782
		}
783
784
		return $share->getSharedBy();
785
	}
786
787
788
789
	/**
790
	 * check if we got the right share
791
	 *
792
	 * @param IShare $share
793
	 * @param string $token
794
	 * @return bool
795
	 * @throws AuthenticationFailedException
796
	 */
797
	protected function verifyShare(IShare $share, $token) {
798
		if (
799
			$share->getShareType() === FederatedShareProvider::SHARE_TYPE_REMOTE &&
800
			$share->getToken() === $token
801
		) {
802
			return true;
803
		}
804
805
		throw new AuthenticationFailedException();
806
	}
807
808
809
810
	/**
811
	 * check if server-to-server sharing is enabled
812
	 *
813
	 * @param bool $incoming
814
	 * @return bool
815
	 */
816 View Code Duplication
	private function isS2SEnabled($incoming = false) {
817
818
		$result = $this->appManager->isEnabledForUser('files_sharing');
819
820
		if ($incoming) {
821
			$result = $result && $this->federatedShareProvider->isIncomingServer2serverShareEnabled();
822
		} else {
823
			$result = $result && $this->federatedShareProvider->isOutgoingServer2serverShareEnabled();
824
		}
825
826
		return $result;
827
	}
828
829
830
	/**
831
	 * get the supported share types, e.g. "user", "group", etc.
832
	 *
833
	 * @return array
834
	 *
835
	 * @since 14.0.0
836
	 */
837
	public function getSupportedShareTypes() {
838
		return ['user', 'group'];
839
	}
840
}
841