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

CloudFederationProviderFiles::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 28

Duplication

Lines 28
Ratio 100 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 13
dl 28
loc 28
rs 9.472
c 0
b 0
f 0

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

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