Completed
Branch master (69e0e2)
by Johannes
12:46
created

FederatedShareProvider::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 23
Code Lines 21

Duplication

Lines 23
Ratio 100 %

Importance

Changes 0
Metric Value
cc 1
eloc 21
nc 1
nop 10
dl 23
loc 23
rs 9.0856
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) 2016, ownCloud, Inc.
4
 *
5
 * @author Bjoern Schiessle <[email protected]>
6
 * @author Björn Schießle <[email protected]>
7
 * @author Robin Appelman <[email protected]>
8
 * @author Roeland Jago Douma <[email protected]>
9
 * @author Thomas Müller <[email protected]>
10
 *
11
 * @license AGPL-3.0
12
 *
13
 * This code is free software: you can redistribute it and/or modify
14
 * it under the terms of the GNU Affero General Public License, version 3,
15
 * as published by the Free Software Foundation.
16
 *
17
 * This program is distributed in the hope that it will be useful,
18
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20
 * GNU Affero General Public License for more details.
21
 *
22
 * You should have received a copy of the GNU Affero General Public License, version 3,
23
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
24
 *
25
 */
26
27
namespace OCA\FederatedFileSharing;
28
29
use OC\Share20\Share;
30
use OCP\Federation\ICloudIdManager;
31
use OCP\DB\QueryBuilder\IQueryBuilder;
32
use OCP\Files\Folder;
33
use OCP\Files\IRootFolder;
34
use OCP\IConfig;
35
use OCP\IL10N;
36
use OCP\ILogger;
37
use OCP\IUserManager;
38
use OCP\Share\IShare;
39
use OCP\Share\IShareProvider;
40
use OC\Share20\Exception\InvalidShare;
41
use OCP\Share\Exceptions\ShareNotFound;
42
use OCP\Files\NotFoundException;
43
use OCP\IDBConnection;
44
use OCP\Files\Node;
45
46
/**
47
 * Class FederatedShareProvider
48
 *
49
 * @package OCA\FederatedFileSharing
50
 */
51
class FederatedShareProvider implements IShareProvider {
52
53
	const SHARE_TYPE_REMOTE = 6;
54
55
	/** @var IDBConnection */
56
	private $dbConnection;
57
58
	/** @var AddressHandler */
59
	private $addressHandler;
60
61
	/** @var Notifications */
62
	private $notifications;
63
64
	/** @var TokenHandler */
65
	private $tokenHandler;
66
67
	/** @var IL10N */
68
	private $l;
69
70
	/** @var ILogger */
71
	private $logger;
72
73
	/** @var IRootFolder */
74
	private $rootFolder;
75
76
	/** @var IConfig */
77
	private $config;
78
79
	/** @var string */
80
	private $externalShareTable = 'share_external';
81
82
	/** @var IUserManager */
83
	private $userManager;
84
85
	/** @var ICloudIdManager */
86
	private $cloudIdManager;
87
88
	/**
89
	 * DefaultShareProvider constructor.
90
	 *
91
	 * @param IDBConnection $connection
92
	 * @param AddressHandler $addressHandler
93
	 * @param Notifications $notifications
94
	 * @param TokenHandler $tokenHandler
95
	 * @param IL10N $l10n
96
	 * @param ILogger $logger
97
	 * @param IRootFolder $rootFolder
98
	 * @param IConfig $config
99
	 * @param IUserManager $userManager
100
	 * @param ICloudIdManager $cloudIdManager
101
	 */
102 View Code Duplication
	public function __construct(
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
103
			IDBConnection $connection,
104
			AddressHandler $addressHandler,
105
			Notifications $notifications,
106
			TokenHandler $tokenHandler,
107
			IL10N $l10n,
108
			ILogger $logger,
109
			IRootFolder $rootFolder,
110
			IConfig $config,
111
			IUserManager $userManager,
112
			ICloudIdManager $cloudIdManager
113
	) {
114
		$this->dbConnection = $connection;
115
		$this->addressHandler = $addressHandler;
116
		$this->notifications = $notifications;
117
		$this->tokenHandler = $tokenHandler;
118
		$this->l = $l10n;
119
		$this->logger = $logger;
120
		$this->rootFolder = $rootFolder;
121
		$this->config = $config;
122
		$this->userManager = $userManager;
123
		$this->cloudIdManager = $cloudIdManager;
124
	}
125
126
	/**
127
	 * Return the identifier of this provider.
128
	 *
129
	 * @return string Containing only [a-zA-Z0-9]
130
	 */
131
	public function identifier() {
132
		return 'ocFederatedSharing';
133
	}
134
135
	/**
136
	 * Share a path
137
	 *
138
	 * @param IShare $share
139
	 * @return IShare The share object
140
	 * @throws ShareNotFound
141
	 * @throws \Exception
142
	 */
143
	public function create(IShare $share) {
144
145
		$shareWith = $share->getSharedWith();
146
		$itemSource = $share->getNodeId();
147
		$itemType = $share->getNodeType();
148
		$permissions = $share->getPermissions();
149
		$sharedBy = $share->getSharedBy();
150
151
		/*
152
		 * Check if file is not already shared with the remote user
153
		 */
154
		$alreadyShared = $this->getSharedWith($shareWith, self::SHARE_TYPE_REMOTE, $share->getNode(), 1, 0);
155 View Code Duplication
		if (!empty($alreadyShared)) {
156
			$message = 'Sharing %s failed, because this item is already shared with %s';
157
			$message_t = $this->l->t('Sharing %s failed, because this item is already shared with %s', array($share->getNode()->getName(), $shareWith));
158
			$this->logger->debug(sprintf($message, $share->getNode()->getName(), $shareWith), ['app' => 'Federated File Sharing']);
159
			throw new \Exception($message_t);
160
		}
161
162
163
		// don't allow federated shares if source and target server are the same
164
		$cloudId = $this->cloudIdManager->resolveCloudId($shareWith);
165
		$currentServer = $this->addressHandler->generateRemoteURL();
166
		$currentUser = $sharedBy;
167
		if ($this->addressHandler->compareAddresses($cloudId->getUser(), $cloudId->getRemote(), $currentUser, $currentServer)) {
168
			$message = 'Not allowed to create a federated share with the same user.';
169
			$message_t = $this->l->t('Not allowed to create a federated share with the same user');
170
			$this->logger->debug($message, ['app' => 'Federated File Sharing']);
171
			throw new \Exception($message_t);
172
		}
173
174
175
		$share->setSharedWith($cloudId->getId());
176
177
		try {
178
			$remoteShare = $this->getShareFromExternalShareTable($share);
179
		} catch (ShareNotFound $e) {
180
			$remoteShare = null;
181
		}
182
183
		if ($remoteShare) {
184
			try {
185
				$ownerCloudId = $this->cloudIdManager->getCloudId($remoteShare['owner'], $remoteShare['remote']);
186
				$shareId = $this->addShareToDB($itemSource, $itemType, $shareWith, $sharedBy, $ownerCloudId->getId(), $permissions, 'tmp_token_' . time());
187
				$share->setId($shareId);
188
				list($token, $remoteId) = $this->askOwnerToReShare($shareWith, $share, $shareId);
189
				// remote share was create successfully if we get a valid token as return
190
				$send = is_string($token) && $token !== '';
191
			} catch (\Exception $e) {
192
				// fall back to old re-share behavior if the remote server
193
				// doesn't support flat re-shares (was introduced with Nextcloud 9.1)
194
				$this->removeShareFromTable($share);
195
				$shareId = $this->createFederatedShare($share);
196
			}
197
			if ($send) {
0 ignored issues
show
Bug introduced by
The variable $send does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
198
				$this->updateSuccessfulReshare($shareId, $token);
0 ignored issues
show
Bug introduced by
The variable $token does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
199
				$this->storeRemoteId($shareId, $remoteId);
0 ignored issues
show
Bug introduced by
The variable $remoteId does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
200
			} else {
201
				$this->removeShareFromTable($share);
202
				$message_t = $this->l->t('File is already shared with %s', [$shareWith]);
203
				throw new \Exception($message_t);
204
			}
205
206
		} else {
207
			$shareId = $this->createFederatedShare($share);
208
		}
209
210
		$data = $this->getRawShare($shareId);
211
		return $this->createShareObject($data);
212
	}
213
214
	/**
215
	 * create federated share and inform the recipient
216
	 *
217
	 * @param IShare $share
218
	 * @return int
219
	 * @throws ShareNotFound
220
	 * @throws \Exception
221
	 */
222
	protected function createFederatedShare(IShare $share) {
223
		$token = $this->tokenHandler->generateToken();
224
		$shareId = $this->addShareToDB(
225
			$share->getNodeId(),
226
			$share->getNodeType(),
227
			$share->getSharedWith(),
228
			$share->getSharedBy(),
229
			$share->getShareOwner(),
230
			$share->getPermissions(),
231
			$token
232
		);
233
234
		$failure = false;
235
236
		try {
237
			$sharedByFederatedId = $share->getSharedBy();
238
			if ($this->userManager->userExists($sharedByFederatedId)) {
239
				$cloudId = $this->cloudIdManager->getCloudId($sharedByFederatedId, $this->addressHandler->generateRemoteURL());
240
				$sharedByFederatedId = $cloudId->getId();
241
			}
242
			$ownerCloudId = $this->cloudIdManager->getCloudId($share->getShareOwner(), $this->addressHandler->generateRemoteURL());
243
			$send = $this->notifications->sendRemoteShare(
244
				$token,
245
				$share->getSharedWith(),
246
				$share->getNode()->getName(),
247
				$shareId,
248
				$share->getShareOwner(),
249
				$ownerCloudId->getId(),
250
				$share->getSharedBy(),
251
				$sharedByFederatedId
252
			);
253
254
			if ($send === false) {
255
				$failure = true;
256
			}
257
		} catch (\Exception $e) {
258
			$this->logger->error('Failed to notify remote server of federated share, removing share (' . $e->getMessage() . ')');
259
			$failure = true;
260
		}
261
262
		if($failure) {
263
			$this->removeShareFromTableById($shareId);
264
			$message_t = $this->l->t('Sharing %s failed, could not find %s, maybe the server is currently unreachable or uses a self-signed certificate.',
265
				[$share->getNode()->getName(), $share->getSharedWith()]);
266
			throw new \Exception($message_t);
267
		}
268
269
		return $shareId;
270
271
	}
272
273
	/**
274
	 * @param string $shareWith
275
	 * @param IShare $share
276
	 * @param string $shareId internal share Id
277
	 * @return array
278
	 * @throws \Exception
279
	 */
280
	protected function askOwnerToReShare($shareWith, IShare $share, $shareId) {
281
282
		$remoteShare = $this->getShareFromExternalShareTable($share);
283
		$token = $remoteShare['share_token'];
284
		$remoteId = $remoteShare['remote_id'];
285
		$remote = $remoteShare['remote'];
286
287
		list($token, $remoteId) = $this->notifications->requestReShare(
288
			$token,
289
			$remoteId,
290
			$shareId,
291
			$remote,
292
			$shareWith,
293
			$share->getPermissions()
294
		);
295
296
		return [$token, $remoteId];
297
	}
298
299
	/**
300
	 * get federated share from the share_external table but exclude mounted link shares
301
	 *
302
	 * @param IShare $share
303
	 * @return array
304
	 * @throws ShareNotFound
305
	 */
306
	protected function getShareFromExternalShareTable(IShare $share) {
307
		$query = $this->dbConnection->getQueryBuilder();
308
		$query->select('*')->from($this->externalShareTable)
309
			->where($query->expr()->eq('user', $query->createNamedParameter($share->getShareOwner())))
310
			->andWhere($query->expr()->eq('mountpoint', $query->createNamedParameter($share->getTarget())));
311
		$result = $query->execute()->fetchAll();
312
313
		if (isset($result[0]) && (int)$result[0]['remote_id'] > 0) {
314
			return $result[0];
315
		}
316
317
		throw new ShareNotFound('share not found in share_external table');
318
	}
319
320
	/**
321
	 * add share to the database and return the ID
322
	 *
323
	 * @param int $itemSource
324
	 * @param string $itemType
325
	 * @param string $shareWith
326
	 * @param string $sharedBy
327
	 * @param string $uidOwner
328
	 * @param int $permissions
329
	 * @param string $token
330
	 * @return int
331
	 */
332 View Code Duplication
	private function addShareToDB($itemSource, $itemType, $shareWith, $sharedBy, $uidOwner, $permissions, $token) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
333
		$qb = $this->dbConnection->getQueryBuilder();
334
		$qb->insert('share')
335
			->setValue('share_type', $qb->createNamedParameter(self::SHARE_TYPE_REMOTE))
336
			->setValue('item_type', $qb->createNamedParameter($itemType))
337
			->setValue('item_source', $qb->createNamedParameter($itemSource))
338
			->setValue('file_source', $qb->createNamedParameter($itemSource))
339
			->setValue('share_with', $qb->createNamedParameter($shareWith))
340
			->setValue('uid_owner', $qb->createNamedParameter($uidOwner))
341
			->setValue('uid_initiator', $qb->createNamedParameter($sharedBy))
342
			->setValue('permissions', $qb->createNamedParameter($permissions))
343
			->setValue('token', $qb->createNamedParameter($token))
344
			->setValue('stime', $qb->createNamedParameter(time()));
345
346
		/*
347
		 * Added to fix https://github.com/owncloud/core/issues/22215
348
		 * Can be removed once we get rid of ajax/share.php
349
		 */
350
		$qb->setValue('file_target', $qb->createNamedParameter(''));
351
352
		$qb->execute();
353
		$id = $qb->getLastInsertId();
354
355
		return (int)$id;
356
	}
357
358
	/**
359
	 * Update a share
360
	 *
361
	 * @param IShare $share
362
	 * @return IShare The share object
363
	 */
364
	public function update(IShare $share) {
365
		/*
366
		 * We allow updating the permissions of federated shares
367
		 */
368
		$qb = $this->dbConnection->getQueryBuilder();
369
			$qb->update('share')
370
				->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())))
371
				->set('permissions', $qb->createNamedParameter($share->getPermissions()))
372
				->set('uid_owner', $qb->createNamedParameter($share->getShareOwner()))
373
				->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()))
374
				->execute();
375
376
		// send the updated permission to the owner/initiator, if they are not the same
377
		if ($share->getShareOwner() !== $share->getSharedBy()) {
378
			$this->sendPermissionUpdate($share);
379
		}
380
381
		return $share;
382
	}
383
384
	/**
385
	 * send the updated permission to the owner/initiator, if they are not the same
386
	 *
387
	 * @param IShare $share
388
	 * @throws ShareNotFound
389
	 * @throws \OC\HintException
390
	 */
391 View Code Duplication
	protected function sendPermissionUpdate(IShare $share) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
392
		$remoteId = $this->getRemoteId($share);
393
		// if the local user is the owner we send the permission change to the initiator
394
		if ($this->userManager->userExists($share->getShareOwner())) {
395
			list(, $remote) = $this->addressHandler->splitUserRemote($share->getSharedBy());
396
		} else { // ... if not we send the permission change to the owner
397
			list(, $remote) = $this->addressHandler->splitUserRemote($share->getShareOwner());
398
		}
399
		$this->notifications->sendPermissionChange($remote, $remoteId, $share->getToken(), $share->getPermissions());
400
	}
401
402
403
	/**
404
	 * update successful reShare with the correct token
405
	 *
406
	 * @param int $shareId
407
	 * @param string $token
408
	 */
409 View Code Duplication
	protected function updateSuccessfulReShare($shareId, $token) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
410
		$query = $this->dbConnection->getQueryBuilder();
411
		$query->update('share')
412
			->where($query->expr()->eq('id', $query->createNamedParameter($shareId)))
413
			->set('token', $query->createNamedParameter($token))
414
			->execute();
415
	}
416
417
	/**
418
	 * store remote ID in federated reShare table
419
	 *
420
	 * @param $shareId
421
	 * @param $remoteId
422
	 */
423
	public function storeRemoteId($shareId, $remoteId) {
424
		$query = $this->dbConnection->getQueryBuilder();
425
		$query->insert('federated_reshares')
426
			->values(
427
				[
428
					'share_id' =>  $query->createNamedParameter($shareId),
429
					'remote_id' => $query->createNamedParameter($remoteId),
430
				]
431
			);
432
		$query->execute();
433
	}
434
435
	/**
436
	 * get share ID on remote server for federated re-shares
437
	 *
438
	 * @param IShare $share
439
	 * @return int
440
	 * @throws ShareNotFound
441
	 */
442
	public function getRemoteId(IShare $share) {
443
		$query = $this->dbConnection->getQueryBuilder();
444
		$query->select('remote_id')->from('federated_reshares')
445
			->where($query->expr()->eq('share_id', $query->createNamedParameter((int)$share->getId())));
446
		$data = $query->execute()->fetch();
447
448
		if (!is_array($data) || !isset($data['remote_id'])) {
449
			throw new ShareNotFound();
450
		}
451
452
		return (int)$data['remote_id'];
453
	}
454
455
	/**
456
	 * @inheritdoc
457
	 */
458
	public function move(IShare $share, $recipient) {
459
		/*
460
		 * This function does nothing yet as it is just for outgoing
461
		 * federated shares.
462
		 */
463
		return $share;
464
	}
465
466
	/**
467
	 * Get all children of this share
468
	 *
469
	 * @param IShare $parent
470
	 * @return IShare[]
471
	 */
472 View Code Duplication
	public function getChildren(IShare $parent) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
473
		$children = [];
474
475
		$qb = $this->dbConnection->getQueryBuilder();
476
		$qb->select('*')
477
			->from('share')
478
			->where($qb->expr()->eq('parent', $qb->createNamedParameter($parent->getId())))
479
			->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_REMOTE)))
480
			->orderBy('id');
481
482
		$cursor = $qb->execute();
483
		while($data = $cursor->fetch()) {
484
			$children[] = $this->createShareObject($data);
485
		}
486
		$cursor->closeCursor();
487
488
		return $children;
489
	}
490
491
	/**
492
	 * Delete a share (owner unShares the file)
493
	 *
494
	 * @param IShare $share
495
	 */
496
	public function delete(IShare $share) {
497
498
		list(, $remote) = $this->addressHandler->splitUserRemote($share->getSharedWith());
499
500
		$isOwner = false;
501
502
		$this->removeShareFromTable($share);
503
504
		// if the local user is the owner we can send the unShare request directly...
505
		if ($this->userManager->userExists($share->getShareOwner())) {
506
			$this->notifications->sendRemoteUnShare($remote, $share->getId(), $share->getToken());
507
			$this->revokeShare($share, true);
508
			$isOwner = true;
509
		} else { // ... if not we need to correct ID for the unShare request
510
			$remoteId = $this->getRemoteId($share);
511
			$this->notifications->sendRemoteUnShare($remote, $remoteId, $share->getToken());
512
			$this->revokeShare($share, false);
513
		}
514
515
		// send revoke notification to the other user, if initiator and owner are not the same user
516
		if ($share->getShareOwner() !== $share->getSharedBy()) {
517
			$remoteId = $this->getRemoteId($share);
518
			if ($isOwner) {
519
				list(, $remote) = $this->addressHandler->splitUserRemote($share->getSharedBy());
520
			} else {
521
				list(, $remote) = $this->addressHandler->splitUserRemote($share->getShareOwner());
522
			}
523
			$this->notifications->sendRevokeShare($remote, $remoteId, $share->getToken());
524
		}
525
	}
526
527
	/**
528
	 * in case of a re-share we need to send the other use (initiator or owner)
529
	 * a message that the file was unshared
530
	 *
531
	 * @param IShare $share
532
	 * @param bool $isOwner the user can either be the owner or the user who re-sahred it
533
	 * @throws ShareNotFound
534
	 * @throws \OC\HintException
535
	 */
536 View Code Duplication
	protected function revokeShare($share, $isOwner) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
537
		// also send a unShare request to the initiator, if this is a different user than the owner
538
		if ($share->getShareOwner() !== $share->getSharedBy()) {
539
			if ($isOwner) {
540
				list(, $remote) = $this->addressHandler->splitUserRemote($share->getSharedBy());
541
			} else {
542
				list(, $remote) = $this->addressHandler->splitUserRemote($share->getShareOwner());
543
			}
544
			$remoteId = $this->getRemoteId($share);
545
			$this->notifications->sendRevokeShare($remote, $remoteId, $share->getToken());
546
		}
547
	}
548
549
	/**
550
	 * remove share from table
551
	 *
552
	 * @param IShare $share
553
	 */
554
	public function removeShareFromTable(IShare $share) {
555
		$this->removeShareFromTableById($share->getId());
556
	}
557
558
	/**
559
	 * remove share from table
560
	 *
561
	 * @param string $shareId
562
	 */
563
	private function removeShareFromTableById($shareId) {
564
		$qb = $this->dbConnection->getQueryBuilder();
565
		$qb->delete('share')
566
			->where($qb->expr()->eq('id', $qb->createNamedParameter($shareId)));
567
		$qb->execute();
568
569
		$qb->delete('federated_reshares')
570
			->where($qb->expr()->eq('share_id', $qb->createNamedParameter($shareId)));
571
		$qb->execute();
572
	}
573
574
	/**
575
	 * @inheritdoc
576
	 */
577
	public function deleteFromSelf(IShare $share, $recipient) {
578
		// nothing to do here. Technically deleteFromSelf in the context of federated
579
		// shares is a umount of a external storage. This is handled here
580
		// apps/files_sharing/lib/external/manager.php
581
		// TODO move this code over to this app
582
		return;
583
	}
584
585
586 View Code Duplication
	public function getSharesInFolder($userId, Folder $node, $reshares) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
587
		$qb = $this->dbConnection->getQueryBuilder();
588
		$qb->select('*')
589
			->from('share', 's')
590
			->andWhere($qb->expr()->orX(
591
				$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
592
				$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
593
			))
594
			->andWhere(
595
				$qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_REMOTE))
596
			);
597
598
		/**
599
		 * Reshares for this user are shares where they are the owner.
600
		 */
601
		if ($reshares === false) {
602
			$qb->andWhere($qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)));
603
		} else {
604
			$qb->andWhere(
605
				$qb->expr()->orX(
606
					$qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)),
607
					$qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId))
608
				)
609
			);
610
		}
611
612
		$qb->innerJoin('s', 'filecache' ,'f', $qb->expr()->eq('s.file_source', 'f.fileid'));
613
		$qb->andWhere($qb->expr()->eq('f.parent', $qb->createNamedParameter($node->getId())));
614
615
		$qb->orderBy('id');
616
617
		$cursor = $qb->execute();
618
		$shares = [];
619
		while ($data = $cursor->fetch()) {
620
			$shares[$data['fileid']][] = $this->createShareObject($data);
621
		}
622
		$cursor->closeCursor();
623
624
		return $shares;
625
	}
626
627
	/**
628
	 * @inheritdoc
629
	 */
630 View Code Duplication
	public function getSharesBy($userId, $shareType, $node, $reshares, $limit, $offset) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
631
		$qb = $this->dbConnection->getQueryBuilder();
632
		$qb->select('*')
633
			->from('share');
634
635
		$qb->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_REMOTE)));
636
637
		/**
638
		 * Reshares for this user are shares where they are the owner.
639
		 */
640
		if ($reshares === false) {
641
			//Special case for old shares created via the web UI
642
			$or1 = $qb->expr()->andX(
643
				$qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)),
644
				$qb->expr()->isNull('uid_initiator')
645
			);
646
647
			$qb->andWhere(
648
				$qb->expr()->orX(
649
					$qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)),
650
					$or1
651
				)
652
			);
653
		} else {
654
			$qb->andWhere(
655
				$qb->expr()->orX(
656
					$qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)),
657
					$qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId))
658
				)
659
			);
660
		}
661
662
		if ($node !== null) {
663
			$qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId())));
664
		}
665
666
		if ($limit !== -1) {
667
			$qb->setMaxResults($limit);
668
		}
669
670
		$qb->setFirstResult($offset);
671
		$qb->orderBy('id');
672
673
		$cursor = $qb->execute();
674
		$shares = [];
675
		while($data = $cursor->fetch()) {
676
			$shares[] = $this->createShareObject($data);
677
		}
678
		$cursor->closeCursor();
679
680
		return $shares;
681
	}
682
683
	/**
684
	 * @inheritdoc
685
	 */
686 View Code Duplication
	public function getShareById($id, $recipientId = null) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
687
		$qb = $this->dbConnection->getQueryBuilder();
688
689
		$qb->select('*')
690
			->from('share')
691
			->where($qb->expr()->eq('id', $qb->createNamedParameter($id)))
692
			->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_REMOTE)));
693
694
		$cursor = $qb->execute();
695
		$data = $cursor->fetch();
696
		$cursor->closeCursor();
697
698
		if ($data === false) {
699
			throw new ShareNotFound();
700
		}
701
702
		try {
703
			$share = $this->createShareObject($data);
704
		} catch (InvalidShare $e) {
705
			throw new ShareNotFound();
706
		}
707
708
		return $share;
709
	}
710
711
	/**
712
	 * Get shares for a given path
713
	 *
714
	 * @param \OCP\Files\Node $path
715
	 * @return IShare[]
716
	 */
717 View Code Duplication
	public function getSharesByPath(Node $path) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
718
		$qb = $this->dbConnection->getQueryBuilder();
719
720
		$cursor = $qb->select('*')
721
			->from('share')
722
			->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($path->getId())))
723
			->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_REMOTE)))
724
			->execute();
725
726
		$shares = [];
727
		while($data = $cursor->fetch()) {
728
			$shares[] = $this->createShareObject($data);
729
		}
730
		$cursor->closeCursor();
731
732
		return $shares;
733
	}
734
735
	/**
736
	 * @inheritdoc
737
	 */
738 View Code Duplication
	public function getSharedWith($userId, $shareType, $node, $limit, $offset) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
739
		/** @var IShare[] $shares */
740
		$shares = [];
741
742
		//Get shares directly with this user
743
		$qb = $this->dbConnection->getQueryBuilder();
744
		$qb->select('*')
745
			->from('share');
746
747
		// Order by id
748
		$qb->orderBy('id');
749
750
		// Set limit and offset
751
		if ($limit !== -1) {
752
			$qb->setMaxResults($limit);
753
		}
754
		$qb->setFirstResult($offset);
755
756
		$qb->where($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_REMOTE)));
757
		$qb->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($userId)));
758
759
		// Filter by node if provided
760
		if ($node !== null) {
761
			$qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId())));
762
		}
763
764
		$cursor = $qb->execute();
765
766
		while($data = $cursor->fetch()) {
767
			$shares[] = $this->createShareObject($data);
768
		}
769
		$cursor->closeCursor();
770
771
772
		return $shares;
773
	}
774
775
	/**
776
	 * Get a share by token
777
	 *
778
	 * @param string $token
779
	 * @return IShare
780
	 * @throws ShareNotFound
781
	 */
782 View Code Duplication
	public function getShareByToken($token) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
783
		$qb = $this->dbConnection->getQueryBuilder();
784
785
		$cursor = $qb->select('*')
786
			->from('share')
787
			->where($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_REMOTE)))
788
			->andWhere($qb->expr()->eq('token', $qb->createNamedParameter($token)))
789
			->execute();
790
791
		$data = $cursor->fetch();
792
793
		if ($data === false) {
794
			throw new ShareNotFound('Share not found', $this->l->t('Could not find share'));
795
		}
796
797
		try {
798
			$share = $this->createShareObject($data);
799
		} catch (InvalidShare $e) {
800
			throw new ShareNotFound('Share not found', $this->l->t('Could not find share'));
801
		}
802
803
		return $share;
804
	}
805
806
	/**
807
	 * get database row of a give share
808
	 *
809
	 * @param $id
810
	 * @return array
811
	 * @throws ShareNotFound
812
	 */
813 View Code Duplication
	private function getRawShare($id) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
814
815
		// Now fetch the inserted share and create a complete share object
816
		$qb = $this->dbConnection->getQueryBuilder();
817
		$qb->select('*')
818
			->from('share')
819
			->where($qb->expr()->eq('id', $qb->createNamedParameter($id)));
820
821
		$cursor = $qb->execute();
822
		$data = $cursor->fetch();
823
		$cursor->closeCursor();
824
825
		if ($data === false) {
826
			throw new ShareNotFound;
827
		}
828
829
		return $data;
830
	}
831
832
	/**
833
	 * Create a share object from an database row
834
	 *
835
	 * @param array $data
836
	 * @return IShare
837
	 * @throws InvalidShare
838
	 * @throws ShareNotFound
839
	 */
840
	private function createShareObject($data) {
841
842
		$share = new Share($this->rootFolder, $this->userManager);
843
		$share->setId((int)$data['id'])
844
			->setShareType((int)$data['share_type'])
845
			->setPermissions((int)$data['permissions'])
846
			->setTarget($data['file_target'])
847
			->setMailSend((bool)$data['mail_send'])
848
			->setToken($data['token']);
849
850
		$shareTime = new \DateTime();
851
		$shareTime->setTimestamp((int)$data['stime']);
852
		$share->setShareTime($shareTime);
853
		$share->setSharedWith($data['share_with']);
854
855
		if ($data['uid_initiator'] !== null) {
856
			$share->setShareOwner($data['uid_owner']);
857
			$share->setSharedBy($data['uid_initiator']);
858
		} else {
859
			//OLD SHARE
860
			$share->setSharedBy($data['uid_owner']);
861
			$path = $this->getNode($share->getSharedBy(), (int)$data['file_source']);
862
863
			$owner = $path->getOwner();
864
			$share->setShareOwner($owner->getUID());
865
		}
866
867
		$share->setNodeId((int)$data['file_source']);
868
		$share->setNodeType($data['item_type']);
869
870
		$share->setProviderId($this->identifier());
871
872
		return $share;
873
	}
874
875
	/**
876
	 * Get the node with file $id for $user
877
	 *
878
	 * @param string $userId
879
	 * @param int $id
880
	 * @return \OCP\Files\File|\OCP\Files\Folder
881
	 * @throws InvalidShare
882
	 */
883 View Code Duplication
	private function getNode($userId, $id) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
884
		try {
885
			$userFolder = $this->rootFolder->getUserFolder($userId);
886
		} catch (NotFoundException $e) {
887
			throw new InvalidShare();
888
		}
889
890
		$nodes = $userFolder->getById($id);
891
892
		if (empty($nodes)) {
893
			throw new InvalidShare();
894
		}
895
896
		return $nodes[0];
897
	}
898
899
	/**
900
	 * A user is deleted from the system
901
	 * So clean up the relevant shares.
902
	 *
903
	 * @param string $uid
904
	 * @param int $shareType
905
	 */
906 View Code Duplication
	public function userDeleted($uid, $shareType) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
907
		//TODO: probabaly a good idea to send unshare info to remote servers
908
909
		$qb = $this->dbConnection->getQueryBuilder();
910
911
		$qb->delete('share')
912
			->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_REMOTE)))
913
			->andWhere($qb->expr()->eq('uid_owner', $qb->createNamedParameter($uid)))
914
			->execute();
915
	}
916
917
	/**
918
	 * This provider does not handle groups
919
	 *
920
	 * @param string $gid
921
	 */
922
	public function groupDeleted($gid) {
923
		// We don't handle groups here
924
		return;
925
	}
926
927
	/**
928
	 * This provider does not handle groups
929
	 *
930
	 * @param string $uid
931
	 * @param string $gid
932
	 */
933
	public function userDeletedFromGroup($uid, $gid) {
934
		// We don't handle groups here
935
		return;
936
	}
937
938
	/**
939
	 * check if users from other Nextcloud instances are allowed to mount public links share by this instance
940
	 *
941
	 * @return bool
942
	 */
943
	public function isOutgoingServer2serverShareEnabled() {
944
		$result = $this->config->getAppValue('files_sharing', 'outgoing_server2server_share_enabled', 'yes');
945
		return ($result === 'yes');
946
	}
947
948
	/**
949
	 * check if users are allowed to mount public links from other Nextclouds
950
	 *
951
	 * @return bool
952
	 */
953
	public function isIncomingServer2serverShareEnabled() {
954
		$result = $this->config->getAppValue('files_sharing', 'incoming_server2server_share_enabled', 'yes');
955
		return ($result === 'yes');
956
	}
957
958
	/**
959
	 * Check if querying sharees on the lookup server is enabled
960
	 *
961
	 * @return bool
962
	 */
963
	public function isLookupServerQueriesEnabled() {
964
		$result = $this->config->getAppValue('files_sharing', 'lookupServerEnabled', 'no');
965
		return ($result === 'yes');
966
	}
967
968
969
	/**
970
	 * Check if it is allowed to publish user specific data to the lookup server
971
	 *
972
	 * @return bool
973
	 */
974
	public function isLookupServerUploadEnabled() {
975
		$result = $this->config->getAppValue('files_sharing', 'lookupServerUploadEnabled', 'yes');
976
		return ($result === 'yes');
977
	}
978
979
	/**
980
	 * @inheritdoc
981
	 */
982
	public function getAccessList($nodes, $currentAccess) {
983
		$ids = [];
984
		foreach ($nodes as $node) {
985
			$ids[] = $node->getId();
986
		}
987
988
		$qb = $this->dbConnection->getQueryBuilder();
989
		$qb->select('share_with', 'token', 'file_source')
990
			->from('share')
991
			->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_REMOTE)))
992
			->andWhere($qb->expr()->in('file_source', $qb->createNamedParameter($ids, IQueryBuilder::PARAM_INT_ARRAY)))
993
			->andWhere($qb->expr()->orX(
994
				$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
995
				$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
996
			));
997
		$cursor = $qb->execute();
998
999
		if ($currentAccess === false) {
1000
			$remote = $cursor->fetch() !== false;
1001
			$cursor->closeCursor();
1002
1003
			return ['remote' => $remote];
1004
		}
1005
1006
		$remote = [];
1007
		while ($row = $cursor->fetch()) {
1008
			$remote[$row['share_with']] = [
1009
				'node_id' => $row['file_source'],
1010
				'token' => $row['token'],
1011
			];
1012
		}
1013
		$cursor->closeCursor();
1014
1015
		return ['remote' => $remote];
1016
	}
1017
}
1018