Completed
Push — master ( 1f48f6...828106 )
by Lukas
10:12
created

FederatedShareProvider::removeShareFromTableById()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 10
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 8
c 1
b 0
f 0
nc 1
nop 1
dl 0
loc 10
rs 9.4285
1
<?php
2
/**
3
 * @author Björn Schießle <[email protected]>
4
 * @author Roeland Jago Douma <[email protected]>
5
 * @author Thomas Müller <[email protected]>
6
 *
7
 * @copyright Copyright (c) 2016, ownCloud, Inc.
8
 * @license AGPL-3.0
9
 *
10
 * This code is free software: you can redistribute it and/or modify
11
 * it under the terms of the GNU Affero General Public License, version 3,
12
 * as published by the Free Software Foundation.
13
 *
14
 * This program is distributed in the hope that it will be useful,
15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
 * GNU Affero General Public License for more details.
18
 *
19
 * You should have received a copy of the GNU Affero General Public License, version 3,
20
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
21
 *
22
 */
23
24
namespace OCA\FederatedFileSharing;
25
26
use OC\Share20\Share;
27
use OCP\Files\IRootFolder;
28
use OCP\IConfig;
29
use OCP\IL10N;
30
use OCP\ILogger;
31
use OCP\IUserManager;
32
use OCP\Share\IShare;
33
use OCP\Share\IShareProvider;
34
use OC\Share20\Exception\InvalidShare;
35
use OCP\Share\Exceptions\ShareNotFound;
36
use OCP\Files\NotFoundException;
37
use OCP\IDBConnection;
38
use OCP\Files\Node;
39
40
/**
41
 * Class FederatedShareProvider
42
 *
43
 * @package OCA\FederatedFileSharing
44
 */
45
class FederatedShareProvider implements IShareProvider {
46
47
	const SHARE_TYPE_REMOTE = 6;
48
49
	/** @var IDBConnection */
50
	private $dbConnection;
51
52
	/** @var AddressHandler */
53
	private $addressHandler;
54
55
	/** @var Notifications */
56
	private $notifications;
57
58
	/** @var TokenHandler */
59
	private $tokenHandler;
60
61
	/** @var IL10N */
62
	private $l;
63
64
	/** @var ILogger */
65
	private $logger;
66
67
	/** @var IRootFolder */
68
	private $rootFolder;
69
70
	/** @var IConfig */
71
	private $config;
72
73
	/** @var string */
74
	private $externalShareTable = 'share_external';
75
76
	/** @var IUserManager */
77
	private $userManager;
78
79
	/**
80
	 * DefaultShareProvider constructor.
81
	 *
82
	 * @param IDBConnection $connection
83
	 * @param AddressHandler $addressHandler
84
	 * @param Notifications $notifications
85
	 * @param TokenHandler $tokenHandler
86
	 * @param IL10N $l10n
87
	 * @param ILogger $logger
88
	 * @param IRootFolder $rootFolder
89
	 * @param IConfig $config
90
	 * @param IUserManager $userManager
91
	 */
92
	public function __construct(
93
			IDBConnection $connection,
94
			AddressHandler $addressHandler,
95
			Notifications $notifications,
96
			TokenHandler $tokenHandler,
97
			IL10N $l10n,
98
			ILogger $logger,
99
			IRootFolder $rootFolder,
100
			IConfig $config,
101
			IUserManager $userManager
102
	) {
103
		$this->dbConnection = $connection;
104
		$this->addressHandler = $addressHandler;
105
		$this->notifications = $notifications;
106
		$this->tokenHandler = $tokenHandler;
107
		$this->l = $l10n;
108
		$this->logger = $logger;
109
		$this->rootFolder = $rootFolder;
110
		$this->config = $config;
111
		$this->userManager = $userManager;
112
	}
113
114
	/**
115
	 * Return the identifier of this provider.
116
	 *
117
	 * @return string Containing only [a-zA-Z0-9]
118
	 */
119
	public function identifier() {
120
		return 'ocFederatedSharing';
121
	}
122
123
	/**
124
	 * Share a path
125
	 *
126
	 * @param IShare $share
127
	 * @return IShare The share object
128
	 * @throws ShareNotFound
129
	 * @throws \Exception
130
	 */
131
	public function create(IShare $share) {
132
133
		$shareWith = $share->getSharedWith();
134
		$itemSource = $share->getNodeId();
135
		$itemType = $share->getNodeType();
136
		$permissions = $share->getPermissions();
137
		$sharedBy = $share->getSharedBy();
138
		
139
		/*
140
		 * Check if file is not already shared with the remote user
141
		 */
142
		$alreadyShared = $this->getSharedWith($shareWith, self::SHARE_TYPE_REMOTE, $share->getNode(), 1, 0);
143
		if (!empty($alreadyShared)) {
144
			$message = 'Sharing %s failed, because this item is already shared with %s';
145
			$message_t = $this->l->t('Sharing %s failed, because this item is already shared with %s', array($share->getNode()->getName(), $shareWith));
146
			$this->logger->debug(sprintf($message, $share->getNode()->getName(), $shareWith), ['app' => 'Federated File Sharing']);
147
			throw new \Exception($message_t);
148
		}
149
150
151
		// don't allow federated shares if source and target server are the same
152
		list($user, $remote) = $this->addressHandler->splitUserRemote($shareWith);
153
		$currentServer = $this->addressHandler->generateRemoteURL();
154
		$currentUser = $sharedBy;
155
		if ($this->addressHandler->compareAddresses($user, $remote, $currentUser, $currentServer)) {
156
			$message = 'Not allowed to create a federated share with the same user.';
157
			$message_t = $this->l->t('Not allowed to create a federated share with the same user');
158
			$this->logger->debug($message, ['app' => 'Federated File Sharing']);
159
			throw new \Exception($message_t);
160
		}
161
162
		$share->setSharedWith($user . '@' . $remote);
163
164
		try {
165
			$remoteShare = $this->getShareFromExternalShareTable($share);
166
		} catch (ShareNotFound $e) {
167
			$remoteShare = null;
168
		}
169
170
		if ($remoteShare) {
171
			try {
172
				$uidOwner = $remoteShare['owner'] . '@' . $remoteShare['remote'];
173
				$shareId = $this->addShareToDB($itemSource, $itemType, $shareWith, $sharedBy, $uidOwner, $permissions, 'tmp_token_' . time());
174
				$share->setId($shareId);
175
				list($token, $remoteId) = $this->askOwnerToReShare($shareWith, $share, $shareId);
176
				// remote share was create successfully if we get a valid token as return
177
				$send = is_string($token) && $token !== '';
178
			} catch (\Exception $e) {
179
				// fall back to old re-share behavior if the remote server
180
				// doesn't support flat re-shares (was introduced with Nextcloud 9.1)
181
				$this->removeShareFromTable($share);
182
				$shareId = $this->createFederatedShare($share);
183
			}
184
			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...
185
				$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...
186
				$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...
187
			} else {
188
				$this->removeShareFromTable($share);
189
				$message_t = $this->l->t('File is already shared with %s', [$shareWith]);
190
				throw new \Exception($message_t);
191
			}
192
193
		} else {
194
			$shareId = $this->createFederatedShare($share);
195
		}
196
197
		$data = $this->getRawShare($shareId);
198
		return $this->createShareObject($data);
199
	}
200
201
	/**
202
	 * create federated share and inform the recipient
203
	 *
204
	 * @param IShare $share
205
	 * @return int
206
	 * @throws ShareNotFound
207
	 * @throws \Exception
208
	 */
209
	protected function createFederatedShare(IShare $share) {
210
		$token = $this->tokenHandler->generateToken();
211
		$shareId = $this->addShareToDB(
212
			$share->getNodeId(),
213
			$share->getNodeType(),
214
			$share->getSharedWith(),
215
			$share->getSharedBy(),
216
			$share->getShareOwner(),
217
			$share->getPermissions(),
218
			$token
219
		);
220
221
		try {
222
			$sharedByFederatedId = $share->getSharedBy();
223
			if ($this->userManager->userExists($sharedByFederatedId)) {
224
				$sharedByFederatedId = $sharedByFederatedId . '@' . $this->addressHandler->generateRemoteURL();
225
			}
226
			$send = $this->notifications->sendRemoteShare(
227
				$token,
228
				$share->getSharedWith(),
229
				$share->getNode()->getName(),
230
				$shareId,
231
				$share->getShareOwner(),
232
				$share->getShareOwner() . '@' . $this->addressHandler->generateRemoteURL(),
233
				$share->getSharedBy(),
234
				$sharedByFederatedId
235
			);
236
237
			if ($send === false) {
238
				$message_t = $this->l->t('Sharing %s failed, could not find %s, maybe the server is currently unreachable.',
239
					[$share->getNode()->getName(), $share->getSharedWith()]);
240
				throw new \Exception($message_t);
241
			}
242
		} catch (\Exception $e) {
243
			$this->logger->error('Failed to notify remote server of federated share, removing share (' . $e->getMessage() . ')');
244
			$this->removeShareFromTableById($shareId);
245
			throw $e;
246
		}
247
248
		return $shareId;
249
	}
250
251
	/**
252
	 * @param string $shareWith
253
	 * @param IShare $share
254
	 * @param string $shareId internal share Id
255
	 * @return array
256
	 * @throws \Exception
257
	 */
258
	protected function askOwnerToReShare($shareWith, IShare $share, $shareId) {
259
260
		$remoteShare = $this->getShareFromExternalShareTable($share);
261
		$token = $remoteShare['share_token'];
262
		$remoteId = $remoteShare['remote_id'];
263
		$remote = $remoteShare['remote'];
264
265
		list($token, $remoteId) = $this->notifications->requestReShare(
266
			$token,
267
			$remoteId,
268
			$shareId,
269
			$remote,
270
			$shareWith,
271
			$share->getPermissions()
272
		);
273
274
		return [$token, $remoteId];
275
	}
276
277
	/**
278
	 * get federated share from the share_external table but exclude mounted link shares
279
	 *
280
	 * @param IShare $share
281
	 * @return array
282
	 * @throws ShareNotFound
283
	 */
284
	protected function getShareFromExternalShareTable(IShare $share) {
285
		$query = $this->dbConnection->getQueryBuilder();
286
		$query->select('*')->from($this->externalShareTable)
287
			->where($query->expr()->eq('user', $query->createNamedParameter($share->getShareOwner())))
288
			->andWhere($query->expr()->eq('mountpoint', $query->createNamedParameter($share->getTarget())));
289
		$result = $query->execute()->fetchAll();
290
291
		if (isset($result[0]) && (int)$result[0]['remote_id'] > 0) {
292
			return $result[0];
293
		}
294
295
		throw new ShareNotFound('share not found in share_external table');
296
	}
297
298
	/**
299
	 * add share to the database and return the ID
300
	 *
301
	 * @param int $itemSource
302
	 * @param string $itemType
303
	 * @param string $shareWith
304
	 * @param string $sharedBy
305
	 * @param string $uidOwner
306
	 * @param int $permissions
307
	 * @param string $token
308
	 * @return int
309
	 */
310
	private function addShareToDB($itemSource, $itemType, $shareWith, $sharedBy, $uidOwner, $permissions, $token) {
311
		$qb = $this->dbConnection->getQueryBuilder();
312
		$qb->insert('share')
313
			->setValue('share_type', $qb->createNamedParameter(self::SHARE_TYPE_REMOTE))
314
			->setValue('item_type', $qb->createNamedParameter($itemType))
315
			->setValue('item_source', $qb->createNamedParameter($itemSource))
316
			->setValue('file_source', $qb->createNamedParameter($itemSource))
317
			->setValue('share_with', $qb->createNamedParameter($shareWith))
318
			->setValue('uid_owner', $qb->createNamedParameter($uidOwner))
319
			->setValue('uid_initiator', $qb->createNamedParameter($sharedBy))
320
			->setValue('permissions', $qb->createNamedParameter($permissions))
321
			->setValue('token', $qb->createNamedParameter($token))
322
			->setValue('stime', $qb->createNamedParameter(time()));
323
324
		/*
325
		 * Added to fix https://github.com/owncloud/core/issues/22215
326
		 * Can be removed once we get rid of ajax/share.php
327
		 */
328
		$qb->setValue('file_target', $qb->createNamedParameter(''));
329
330
		$qb->execute();
331
		$id = $qb->getLastInsertId();
332
333
		return (int)$id;
334
	}
335
336
	/**
337
	 * Update a share
338
	 *
339
	 * @param IShare $share
340
	 * @return IShare The share object
341
	 */
342
	public function update(IShare $share) {
343
		/*
344
		 * We allow updating the permissions of federated shares
345
		 */
346
		$qb = $this->dbConnection->getQueryBuilder();
347
			$qb->update('share')
348
				->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())))
349
				->set('permissions', $qb->createNamedParameter($share->getPermissions()))
350
				->set('uid_owner', $qb->createNamedParameter($share->getShareOwner()))
351
				->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()))
352
				->execute();
353
354
		// send the updated permission to the owner/initiator, if they are not the same
355
		if ($share->getShareOwner() !== $share->getSharedBy()) {
356
			$this->sendPermissionUpdate($share);
357
		}
358
359
		return $share;
360
	}
361
362
	/**
363
	 * send the updated permission to the owner/initiator, if they are not the same
364
	 *
365
	 * @param IShare $share
366
	 * @throws ShareNotFound
367
	 * @throws \OC\HintException
368
	 */
369 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...
370
		$remoteId = $this->getRemoteId($share);
371
		// if the local user is the owner we send the permission change to the initiator
372
		if ($this->userManager->userExists($share->getShareOwner())) {
373
			list(, $remote) = $this->addressHandler->splitUserRemote($share->getSharedBy());
374
		} else { // ... if not we send the permission change to the owner
375
			list(, $remote) = $this->addressHandler->splitUserRemote($share->getShareOwner());
376
		}
377
		$this->notifications->sendPermissionChange($remote, $remoteId, $share->getToken(), $share->getPermissions());
378
	}
379
380
381
	/**
382
	 * update successful reShare with the correct token
383
	 *
384
	 * @param int $shareId
385
	 * @param string $token
386
	 */
387 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...
388
		$query = $this->dbConnection->getQueryBuilder();
389
		$query->update('share')
390
			->where($query->expr()->eq('id', $query->createNamedParameter($shareId)))
391
			->set('token', $query->createNamedParameter($token))
392
			->execute();
393
	}
394
395
	/**
396
	 * store remote ID in federated reShare table
397
	 *
398
	 * @param $shareId
399
	 * @param $remoteId
400
	 */
401
	public function storeRemoteId($shareId, $remoteId) {
402
		$query = $this->dbConnection->getQueryBuilder();
403
		$query->insert('federated_reshares')
404
			->values(
405
				[
406
					'share_id' =>  $query->createNamedParameter($shareId),
407
					'remote_id' => $query->createNamedParameter($remoteId),
408
				]
409
			);
410
		$query->execute();
411
	}
412
413
	/**
414
	 * get share ID on remote server for federated re-shares
415
	 *
416
	 * @param IShare $share
417
	 * @return int
418
	 * @throws ShareNotFound
419
	 */
420
	public function getRemoteId(IShare $share) {
421
		$query = $this->dbConnection->getQueryBuilder();
422
		$query->select('remote_id')->from('federated_reshares')
423
			->where($query->expr()->eq('share_id', $query->createNamedParameter((int)$share->getId())));
424
		$data = $query->execute()->fetch();
425
426
		if (!is_array($data) || !isset($data['remote_id'])) {
427
			throw new ShareNotFound();
428
		}
429
430
		return (int)$data['remote_id'];
431
	}
432
433
	/**
434
	 * @inheritdoc
435
	 */
436
	public function move(IShare $share, $recipient) {
437
		/*
438
		 * This function does nothing yet as it is just for outgoing
439
		 * federated shares.
440
		 */
441
		return $share;
442
	}
443
444
	/**
445
	 * Get all children of this share
446
	 *
447
	 * @param IShare $parent
448
	 * @return IShare[]
449
	 */
450 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...
451
		$children = [];
452
453
		$qb = $this->dbConnection->getQueryBuilder();
454
		$qb->select('*')
455
			->from('share')
456
			->where($qb->expr()->eq('parent', $qb->createNamedParameter($parent->getId())))
457
			->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_REMOTE)))
458
			->orderBy('id');
459
460
		$cursor = $qb->execute();
461
		while($data = $cursor->fetch()) {
462
			$children[] = $this->createShareObject($data);
463
		}
464
		$cursor->closeCursor();
465
466
		return $children;
467
	}
468
469
	/**
470
	 * Delete a share (owner unShares the file)
471
	 *
472
	 * @param IShare $share
473
	 */
474
	public function delete(IShare $share) {
475
476
		list(, $remote) = $this->addressHandler->splitUserRemote($share->getSharedWith());
477
478
		$isOwner = false;
479
480
		$this->removeShareFromTable($share);
481
482
		// if the local user is the owner we can send the unShare request directly...
483
		if ($this->userManager->userExists($share->getShareOwner())) {
484
			$this->notifications->sendRemoteUnShare($remote, $share->getId(), $share->getToken());
485
			$this->revokeShare($share, true);
486
			$isOwner = true;
487
		} else { // ... if not we need to correct ID for the unShare request
488
			$remoteId = $this->getRemoteId($share);
489
			$this->notifications->sendRemoteUnShare($remote, $remoteId, $share->getToken());
490
			$this->revokeShare($share, false);
491
		}
492
493
		// send revoke notification to the other user, if initiator and owner are not the same user
494
		if ($share->getShareOwner() !== $share->getSharedBy()) {
495
			$remoteId = $this->getRemoteId($share);
496
			if ($isOwner) {
497
				list(, $remote) = $this->addressHandler->splitUserRemote($share->getSharedBy());
498
			} else {
499
				list(, $remote) = $this->addressHandler->splitUserRemote($share->getShareOwner());
500
			}
501
			$this->notifications->sendRevokeShare($remote, $remoteId, $share->getToken());
502
		}
503
	}
504
505
	/**
506
	 * in case of a re-share we need to send the other use (initiator or owner)
507
	 * a message that the file was unshared
508
	 *
509
	 * @param IShare $share
510
	 * @param bool $isOwner the user can either be the owner or the user who re-sahred it
511
	 * @throws ShareNotFound
512
	 * @throws \OC\HintException
513
	 */
514 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...
515
		// also send a unShare request to the initiator, if this is a different user than the owner
516
		if ($share->getShareOwner() !== $share->getSharedBy()) {
517
			if ($isOwner) {
518
				list(, $remote) = $this->addressHandler->splitUserRemote($share->getSharedBy());
519
			} else {
520
				list(, $remote) = $this->addressHandler->splitUserRemote($share->getShareOwner());
521
			}
522
			$remoteId = $this->getRemoteId($share);
523
			$this->notifications->sendRevokeShare($remote, $remoteId, $share->getToken());
524
		}
525
	}
526
527
	/**
528
	 * remove share from table
529
	 *
530
	 * @param IShare $share
531
	 */
532
	public function removeShareFromTable(IShare $share) {
533
		$this->removeShareFromTableById($share->getId());
534
	}
535
536
	/**
537
	 * remove share from table
538
	 *
539
	 * @param string $shareId
540
	 */
541
	private function removeShareFromTableById($shareId) {
542
		$qb = $this->dbConnection->getQueryBuilder();
543
		$qb->delete('share')
544
			->where($qb->expr()->eq('id', $qb->createNamedParameter($shareId)));
545
		$qb->execute();
546
547
		$qb->delete('federated_reshares')
548
			->where($qb->expr()->eq('share_id', $qb->createNamedParameter($shareId)));
549
		$qb->execute();
550
	}
551
552
	/**
553
	 * @inheritdoc
554
	 */
555
	public function deleteFromSelf(IShare $share, $recipient) {
556
		// nothing to do here. Technically deleteFromSelf in the context of federated
557
		// shares is a umount of a external storage. This is handled here
558
		// apps/files_sharing/lib/external/manager.php
559
		// TODO move this code over to this app
560
		return;
561
	}
562
563
	/**
564
	 * @inheritdoc
565
	 */
566
	public function getSharesBy($userId, $shareType, $node, $reshares, $limit, $offset) {
567
		$qb = $this->dbConnection->getQueryBuilder();
568
		$qb->select('*')
569
			->from('share');
570
571
		$qb->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_REMOTE)));
572
573
		/**
574
		 * Reshares for this user are shares where they are the owner.
575
		 */
576
		if ($reshares === false) {
577
			//Special case for old shares created via the web UI
578
			$or1 = $qb->expr()->andX(
579
				$qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)),
580
				$qb->expr()->isNull('uid_initiator')
0 ignored issues
show
Unused Code introduced by
The call to IExpressionBuilder::andX() has too many arguments starting with $qb->expr()->isNull('uid_initiator').

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
581
			);
582
583
			$qb->andWhere(
584
				$qb->expr()->orX(
585
					$qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)),
586
					$or1
0 ignored issues
show
Unused Code introduced by
The call to IExpressionBuilder::orX() has too many arguments starting with $or1.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
587
				)
588
			);
589
		} else {
590
			$qb->andWhere(
591
				$qb->expr()->orX(
592
					$qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)),
593
					$qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId))
0 ignored issues
show
Unused Code introduced by
The call to IExpressionBuilder::orX() has too many arguments starting with $qb->expr()->eq('uid_ini...amedParameter($userId)).

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
594
				)
595
			);
596
		}
597
598
		if ($node !== null) {
599
			$qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId())));
600
		}
601
602
		if ($limit !== -1) {
603
			$qb->setMaxResults($limit);
604
		}
605
606
		$qb->setFirstResult($offset);
607
		$qb->orderBy('id');
608
609
		$cursor = $qb->execute();
610
		$shares = [];
611
		while($data = $cursor->fetch()) {
612
			$shares[] = $this->createShareObject($data);
613
		}
614
		$cursor->closeCursor();
615
616
		return $shares;
617
	}
618
619
	/**
620
	 * @inheritdoc
621
	 */
622 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...
623
		$qb = $this->dbConnection->getQueryBuilder();
624
625
		$qb->select('*')
626
			->from('share')
627
			->where($qb->expr()->eq('id', $qb->createNamedParameter($id)))
628
			->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_REMOTE)));
629
		
630
		$cursor = $qb->execute();
631
		$data = $cursor->fetch();
632
		$cursor->closeCursor();
633
634
		if ($data === false) {
635
			throw new ShareNotFound();
636
		}
637
638
		try {
639
			$share = $this->createShareObject($data);
640
		} catch (InvalidShare $e) {
641
			throw new ShareNotFound();
642
		}
643
644
		return $share;
645
	}
646
647
	/**
648
	 * Get shares for a given path
649
	 *
650
	 * @param \OCP\Files\Node $path
651
	 * @return IShare[]
652
	 */
653 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...
654
		$qb = $this->dbConnection->getQueryBuilder();
655
656
		$cursor = $qb->select('*')
657
			->from('share')
658
			->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($path->getId())))
659
			->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_REMOTE)))
660
			->execute();
661
662
		$shares = [];
663
		while($data = $cursor->fetch()) {
664
			$shares[] = $this->createShareObject($data);
665
		}
666
		$cursor->closeCursor();
667
668
		return $shares;
669
	}
670
671
	/**
672
	 * @inheritdoc
673
	 */
674
	public function getSharedWith($userId, $shareType, $node, $limit, $offset) {
675
		/** @var IShare[] $shares */
676
		$shares = [];
677
678
		//Get shares directly with this user
679
		$qb = $this->dbConnection->getQueryBuilder();
680
		$qb->select('*')
681
			->from('share');
682
683
		// Order by id
684
		$qb->orderBy('id');
685
686
		// Set limit and offset
687
		if ($limit !== -1) {
688
			$qb->setMaxResults($limit);
689
		}
690
		$qb->setFirstResult($offset);
691
692
		$qb->where($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_REMOTE)));
693
		$qb->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($userId)));
694
695
		// Filter by node if provided
696
		if ($node !== null) {
697
			$qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId())));
698
		}
699
700
		$cursor = $qb->execute();
701
702
		while($data = $cursor->fetch()) {
703
			$shares[] = $this->createShareObject($data);
704
		}
705
		$cursor->closeCursor();
706
707
708
		return $shares;
709
	}
710
711
	/**
712
	 * Get a share by token
713
	 *
714
	 * @param string $token
715
	 * @return IShare
716
	 * @throws ShareNotFound
717
	 */
718 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...
719
		$qb = $this->dbConnection->getQueryBuilder();
720
721
		$cursor = $qb->select('*')
722
			->from('share')
723
			->where($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_REMOTE)))
724
			->andWhere($qb->expr()->eq('token', $qb->createNamedParameter($token)))
725
			->execute();
726
727
		$data = $cursor->fetch();
728
729
		if ($data === false) {
730
			throw new ShareNotFound();
731
		}
732
733
		try {
734
			$share = $this->createShareObject($data);
735
		} catch (InvalidShare $e) {
736
			throw new ShareNotFound();
737
		}
738
739
		return $share;
740
	}
741
742
	/**
743
	 * get database row of a give share
744
	 *
745
	 * @param $id
746
	 * @return array
747
	 * @throws ShareNotFound
748
	 */
749 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...
750
751
		// Now fetch the inserted share and create a complete share object
752
		$qb = $this->dbConnection->getQueryBuilder();
753
		$qb->select('*')
754
			->from('share')
755
			->where($qb->expr()->eq('id', $qb->createNamedParameter($id)));
756
757
		$cursor = $qb->execute();
758
		$data = $cursor->fetch();
759
		$cursor->closeCursor();
760
761
		if ($data === false) {
762
			throw new ShareNotFound;
763
		}
764
765
		return $data;
766
	}
767
768
	/**
769
	 * Create a share object from an database row
770
	 *
771
	 * @param array $data
772
	 * @return IShare
773
	 * @throws InvalidShare
774
	 * @throws ShareNotFound
775
	 */
776
	private function createShareObject($data) {
777
778
		$share = new Share($this->rootFolder, $this->userManager);
779
		$share->setId((int)$data['id'])
780
			->setShareType((int)$data['share_type'])
781
			->setPermissions((int)$data['permissions'])
782
			->setTarget($data['file_target'])
783
			->setMailSend((bool)$data['mail_send'])
784
			->setToken($data['token']);
785
786
		$shareTime = new \DateTime();
787
		$shareTime->setTimestamp((int)$data['stime']);
788
		$share->setShareTime($shareTime);
789
		$share->setSharedWith($data['share_with']);
790
791
		if ($data['uid_initiator'] !== null) {
792
			$share->setShareOwner($data['uid_owner']);
793
			$share->setSharedBy($data['uid_initiator']);
794
		} else {
795
			//OLD SHARE
796
			$share->setSharedBy($data['uid_owner']);
797
			$path = $this->getNode($share->getSharedBy(), (int)$data['file_source']);
798
799
			$owner = $path->getOwner();
800
			$share->setShareOwner($owner->getUID());
801
		}
802
803
		$share->setNodeId((int)$data['file_source']);
804
		$share->setNodeType($data['item_type']);
805
806
		$share->setProviderId($this->identifier());
807
808
		return $share;
809
	}
810
811
	/**
812
	 * Get the node with file $id for $user
813
	 *
814
	 * @param string $userId
815
	 * @param int $id
816
	 * @return \OCP\Files\File|\OCP\Files\Folder
817
	 * @throws InvalidShare
818
	 */
819
	private function getNode($userId, $id) {
820
		try {
821
			$userFolder = $this->rootFolder->getUserFolder($userId);
822
		} catch (NotFoundException $e) {
823
			throw new InvalidShare();
824
		}
825
826
		$nodes = $userFolder->getById($id);
827
828
		if (empty($nodes)) {
829
			throw new InvalidShare();
830
		}
831
832
		return $nodes[0];
833
	}
834
835
	/**
836
	 * A user is deleted from the system
837
	 * So clean up the relevant shares.
838
	 *
839
	 * @param string $uid
840
	 * @param int $shareType
841
	 */
842 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...
843
		//TODO: probabaly a good idea to send unshare info to remote servers
844
845
		$qb = $this->dbConnection->getQueryBuilder();
846
847
		$qb->delete('share')
848
			->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_REMOTE)))
849
			->andWhere($qb->expr()->eq('uid_owner', $qb->createNamedParameter($uid)))
850
			->execute();
851
	}
852
853
	/**
854
	 * This provider does not handle groups
855
	 *
856
	 * @param string $gid
857
	 */
858
	public function groupDeleted($gid) {
859
		// We don't handle groups here
860
		return;
861
	}
862
863
	/**
864
	 * This provider does not handle groups
865
	 *
866
	 * @param string $uid
867
	 * @param string $gid
868
	 */
869
	public function userDeletedFromGroup($uid, $gid) {
870
		// We don't handle groups here
871
		return;
872
	}
873
874
	/**
875
	 * check if users from other Nextcloud instances are allowed to mount public links share by this instance
876
	 *
877
	 * @return bool
878
	 */
879
	public function isOutgoingServer2serverShareEnabled() {
880
		$result = $this->config->getAppValue('files_sharing', 'outgoing_server2server_share_enabled', 'yes');
881
		return ($result === 'yes') ? true : false;
882
	}
883
884
	/**
885
	 * check if users are allowed to mount public links from other ownClouds
886
	 *
887
	 * @return bool
888
	 */
889
	public function isIncomingServer2serverShareEnabled() {
890
		$result = $this->config->getAppValue('files_sharing', 'incoming_server2server_share_enabled', 'yes');
891
		return ($result === 'yes') ? true : false;
892
	}
893
}
894