Completed
Push — master ( 235563...87bca3 )
by Joas
28:03 queued 13:40
created

isLookupServerUploadEnabled()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 3
nc 1
nop 0
dl 0
loc 4
rs 10
c 0
b 0
f 0
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\Files\Folder;
32
use OCP\Files\IRootFolder;
33
use OCP\IConfig;
34
use OCP\IL10N;
35
use OCP\ILogger;
36
use OCP\IUserManager;
37
use OCP\Share\IShare;
38
use OCP\Share\IShareProvider;
39
use OC\Share20\Exception\InvalidShare;
40
use OCP\Share\Exceptions\ShareNotFound;
41
use OCP\Files\NotFoundException;
42
use OCP\IDBConnection;
43
use OCP\Files\Node;
44
45
/**
46
 * Class FederatedShareProvider
47
 *
48
 * @package OCA\FederatedFileSharing
49
 */
50
class FederatedShareProvider implements IShareProvider {
51
52
	const SHARE_TYPE_REMOTE = 6;
53
54
	/** @var IDBConnection */
55
	private $dbConnection;
56
57
	/** @var AddressHandler */
58
	private $addressHandler;
59
60
	/** @var Notifications */
61
	private $notifications;
62
63
	/** @var TokenHandler */
64
	private $tokenHandler;
65
66
	/** @var IL10N */
67
	private $l;
68
69
	/** @var ILogger */
70
	private $logger;
71
72
	/** @var IRootFolder */
73
	private $rootFolder;
74
75
	/** @var IConfig */
76
	private $config;
77
78
	/** @var string */
79
	private $externalShareTable = 'share_external';
80
81
	/** @var IUserManager */
82
	private $userManager;
83
84
	/** @var ICloudIdManager */
85
	private $cloudIdManager;
86
87
	/**
88
	 * DefaultShareProvider constructor.
89
	 *
90
	 * @param IDBConnection $connection
91
	 * @param AddressHandler $addressHandler
92
	 * @param Notifications $notifications
93
	 * @param TokenHandler $tokenHandler
94
	 * @param IL10N $l10n
95
	 * @param ILogger $logger
96
	 * @param IRootFolder $rootFolder
97
	 * @param IConfig $config
98
	 * @param IUserManager $userManager
99
	 * @param ICloudIdManager $cloudIdManager
100
	 */
101
	public function __construct(
102
			IDBConnection $connection,
103
			AddressHandler $addressHandler,
104
			Notifications $notifications,
105
			TokenHandler $tokenHandler,
106
			IL10N $l10n,
107
			ILogger $logger,
108
			IRootFolder $rootFolder,
109
			IConfig $config,
110
			IUserManager $userManager,
111
			ICloudIdManager $cloudIdManager
112
	) {
113
		$this->dbConnection = $connection;
114
		$this->addressHandler = $addressHandler;
115
		$this->notifications = $notifications;
116
		$this->tokenHandler = $tokenHandler;
117
		$this->l = $l10n;
118
		$this->logger = $logger;
119
		$this->rootFolder = $rootFolder;
120
		$this->config = $config;
121
		$this->userManager = $userManager;
122
		$this->cloudIdManager = $cloudIdManager;
123
	}
124
125
	/**
126
	 * Return the identifier of this provider.
127
	 *
128
	 * @return string Containing only [a-zA-Z0-9]
129
	 */
130
	public function identifier() {
131
		return 'ocFederatedSharing';
132
	}
133
134
	/**
135
	 * Share a path
136
	 *
137
	 * @param IShare $share
138
	 * @return IShare The share object
139
	 * @throws ShareNotFound
140
	 * @throws \Exception
141
	 */
142
	public function create(IShare $share) {
143
144
		$shareWith = $share->getSharedWith();
145
		$itemSource = $share->getNodeId();
146
		$itemType = $share->getNodeType();
147
		$permissions = $share->getPermissions();
148
		$sharedBy = $share->getSharedBy();
149
150
		/*
151
		 * Check if file is not already shared with the remote user
152
		 */
153
		$alreadyShared = $this->getSharedWith($shareWith, self::SHARE_TYPE_REMOTE, $share->getNode(), 1, 0);
154 View Code Duplication
		if (!empty($alreadyShared)) {
155
			$message = 'Sharing %s failed, because this item is already shared with %s';
156
			$message_t = $this->l->t('Sharing %s failed, because this item is already shared with %s', array($share->getNode()->getName(), $shareWith));
157
			$this->logger->debug(sprintf($message, $share->getNode()->getName(), $shareWith), ['app' => 'Federated File Sharing']);
158
			throw new \Exception($message_t);
159
		}
160
161
162
		// don't allow federated shares if source and target server are the same
163
		$cloudId = $this->cloudIdManager->resolveCloudId($shareWith);
164
		$currentServer = $this->addressHandler->generateRemoteURL();
165
		$currentUser = $sharedBy;
166
		if ($this->addressHandler->compareAddresses($cloudId->getUser(), $cloudId->getRemote(), $currentUser, $currentServer)) {
167
			$message = 'Not allowed to create a federated share with the same user.';
168
			$message_t = $this->l->t('Not allowed to create a federated share with the same user');
169
			$this->logger->debug($message, ['app' => 'Federated File Sharing']);
170
			throw new \Exception($message_t);
171
		}
172
173
174
		$share->setSharedWith($cloudId->getId());
175
176
		try {
177
			$remoteShare = $this->getShareFromExternalShareTable($share);
178
		} catch (ShareNotFound $e) {
179
			$remoteShare = null;
180
		}
181
182
		if ($remoteShare) {
183
			try {
184
				$ownerCloudId = $this->cloudIdManager->getCloudId($remoteShare['owner'], $remoteShare['remote']);
185
				$shareId = $this->addShareToDB($itemSource, $itemType, $shareWith, $sharedBy, $ownerCloudId->getId(), $permissions, 'tmp_token_' . time());
186
				$share->setId($shareId);
187
				list($token, $remoteId) = $this->askOwnerToReShare($shareWith, $share, $shareId);
188
				// remote share was create successfully if we get a valid token as return
189
				$send = is_string($token) && $token !== '';
190
			} catch (\Exception $e) {
191
				// fall back to old re-share behavior if the remote server
192
				// doesn't support flat re-shares (was introduced with Nextcloud 9.1)
193
				$this->removeShareFromTable($share);
194
				$shareId = $this->createFederatedShare($share);
195
			}
196
			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...
197
				$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...
198
				$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...
199
			} else {
200
				$this->removeShareFromTable($share);
201
				$message_t = $this->l->t('File is already shared with %s', [$shareWith]);
202
				throw new \Exception($message_t);
203
			}
204
205
		} else {
206
			$shareId = $this->createFederatedShare($share);
207
		}
208
209
		$data = $this->getRawShare($shareId);
210
		return $this->createShareObject($data);
211
	}
212
213
	/**
214
	 * create federated share and inform the recipient
215
	 *
216
	 * @param IShare $share
217
	 * @return int
218
	 * @throws ShareNotFound
219
	 * @throws \Exception
220
	 */
221
	protected function createFederatedShare(IShare $share) {
222
		$token = $this->tokenHandler->generateToken();
223
		$shareId = $this->addShareToDB(
224
			$share->getNodeId(),
225
			$share->getNodeType(),
226
			$share->getSharedWith(),
227
			$share->getSharedBy(),
228
			$share->getShareOwner(),
229
			$share->getPermissions(),
230
			$token
231
		);
232
233
		$failure = false;
234
235
		try {
236
			$sharedByFederatedId = $share->getSharedBy();
237
			if ($this->userManager->userExists($sharedByFederatedId)) {
238
				$cloudId = $this->cloudIdManager->getCloudId($sharedByFederatedId, $this->addressHandler->generateRemoteURL());
239
				$sharedByFederatedId = $cloudId->getId();
240
			}
241
			$ownerCloudId = $this->cloudIdManager->getCloudId($share->getShareOwner(), $this->addressHandler->generateRemoteURL());
242
			$send = $this->notifications->sendRemoteShare(
243
				$token,
244
				$share->getSharedWith(),
245
				$share->getNode()->getName(),
246
				$shareId,
247
				$share->getShareOwner(),
248
				$ownerCloudId->getId(),
249
				$share->getSharedBy(),
250
				$sharedByFederatedId
251
			);
252
253
			if ($send === false) {
254
				$failure = true;
255
			}
256
		} catch (\Exception $e) {
257
			$this->logger->error('Failed to notify remote server of federated share, removing share (' . $e->getMessage() . ')');
258
			$failure = true;
259
		}
260
261
		if($failure) {
262
			$this->removeShareFromTableById($shareId);
263
			$message_t = $this->l->t('Sharing %s failed, could not find %s, maybe the server is currently unreachable or uses a self-signed certificate.',
264
				[$share->getNode()->getName(), $share->getSharedWith()]);
265
			throw new \Exception($message_t);
266
		}
267
268
		return $shareId;
269
270
	}
271
272
	/**
273
	 * @param string $shareWith
274
	 * @param IShare $share
275
	 * @param string $shareId internal share Id
276
	 * @return array
277
	 * @throws \Exception
278
	 */
279
	protected function askOwnerToReShare($shareWith, IShare $share, $shareId) {
280
281
		$remoteShare = $this->getShareFromExternalShareTable($share);
282
		$token = $remoteShare['share_token'];
283
		$remoteId = $remoteShare['remote_id'];
284
		$remote = $remoteShare['remote'];
285
286
		list($token, $remoteId) = $this->notifications->requestReShare(
287
			$token,
288
			$remoteId,
289
			$shareId,
290
			$remote,
291
			$shareWith,
292
			$share->getPermissions()
293
		);
294
295
		return [$token, $remoteId];
296
	}
297
298
	/**
299
	 * get federated share from the share_external table but exclude mounted link shares
300
	 *
301
	 * @param IShare $share
302
	 * @return array
303
	 * @throws ShareNotFound
304
	 */
305
	protected function getShareFromExternalShareTable(IShare $share) {
306
		$query = $this->dbConnection->getQueryBuilder();
307
		$query->select('*')->from($this->externalShareTable)
308
			->where($query->expr()->eq('user', $query->createNamedParameter($share->getShareOwner())))
309
			->andWhere($query->expr()->eq('mountpoint', $query->createNamedParameter($share->getTarget())));
310
		$result = $query->execute()->fetchAll();
311
312
		if (isset($result[0]) && (int)$result[0]['remote_id'] > 0) {
313
			return $result[0];
314
		}
315
316
		throw new ShareNotFound('share not found in share_external table');
317
	}
318
319
	/**
320
	 * add share to the database and return the ID
321
	 *
322
	 * @param int $itemSource
323
	 * @param string $itemType
324
	 * @param string $shareWith
325
	 * @param string $sharedBy
326
	 * @param string $uidOwner
327
	 * @param int $permissions
328
	 * @param string $token
329
	 * @return int
330
	 */
331 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...
332
		$qb = $this->dbConnection->getQueryBuilder();
333
		$qb->insert('share')
334
			->setValue('share_type', $qb->createNamedParameter(self::SHARE_TYPE_REMOTE))
335
			->setValue('item_type', $qb->createNamedParameter($itemType))
336
			->setValue('item_source', $qb->createNamedParameter($itemSource))
337
			->setValue('file_source', $qb->createNamedParameter($itemSource))
338
			->setValue('share_with', $qb->createNamedParameter($shareWith))
339
			->setValue('uid_owner', $qb->createNamedParameter($uidOwner))
340
			->setValue('uid_initiator', $qb->createNamedParameter($sharedBy))
341
			->setValue('permissions', $qb->createNamedParameter($permissions))
342
			->setValue('token', $qb->createNamedParameter($token))
343
			->setValue('stime', $qb->createNamedParameter(time()));
344
345
		/*
346
		 * Added to fix https://github.com/owncloud/core/issues/22215
347
		 * Can be removed once we get rid of ajax/share.php
348
		 */
349
		$qb->setValue('file_target', $qb->createNamedParameter(''));
350
351
		$qb->execute();
352
		$id = $qb->getLastInsertId();
353
354
		return (int)$id;
355
	}
356
357
	/**
358
	 * Update a share
359
	 *
360
	 * @param IShare $share
361
	 * @return IShare The share object
362
	 */
363
	public function update(IShare $share) {
364
		/*
365
		 * We allow updating the permissions of federated shares
366
		 */
367
		$qb = $this->dbConnection->getQueryBuilder();
368
			$qb->update('share')
369
				->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())))
370
				->set('permissions', $qb->createNamedParameter($share->getPermissions()))
371
				->set('uid_owner', $qb->createNamedParameter($share->getShareOwner()))
372
				->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()))
373
				->execute();
374
375
		// send the updated permission to the owner/initiator, if they are not the same
376
		if ($share->getShareOwner() !== $share->getSharedBy()) {
377
			$this->sendPermissionUpdate($share);
378
		}
379
380
		return $share;
381
	}
382
383
	/**
384
	 * send the updated permission to the owner/initiator, if they are not the same
385
	 *
386
	 * @param IShare $share
387
	 * @throws ShareNotFound
388
	 * @throws \OC\HintException
389
	 */
390 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...
391
		$remoteId = $this->getRemoteId($share);
392
		// if the local user is the owner we send the permission change to the initiator
393
		if ($this->userManager->userExists($share->getShareOwner())) {
394
			list(, $remote) = $this->addressHandler->splitUserRemote($share->getSharedBy());
395
		} else { // ... if not we send the permission change to the owner
396
			list(, $remote) = $this->addressHandler->splitUserRemote($share->getShareOwner());
397
		}
398
		$this->notifications->sendPermissionChange($remote, $remoteId, $share->getToken(), $share->getPermissions());
399
	}
400
401
402
	/**
403
	 * update successful reShare with the correct token
404
	 *
405
	 * @param int $shareId
406
	 * @param string $token
407
	 */
408 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...
409
		$query = $this->dbConnection->getQueryBuilder();
410
		$query->update('share')
411
			->where($query->expr()->eq('id', $query->createNamedParameter($shareId)))
412
			->set('token', $query->createNamedParameter($token))
413
			->execute();
414
	}
415
416
	/**
417
	 * store remote ID in federated reShare table
418
	 *
419
	 * @param $shareId
420
	 * @param $remoteId
421
	 */
422
	public function storeRemoteId($shareId, $remoteId) {
423
		$query = $this->dbConnection->getQueryBuilder();
424
		$query->insert('federated_reshares')
425
			->values(
426
				[
427
					'share_id' =>  $query->createNamedParameter($shareId),
428
					'remote_id' => $query->createNamedParameter($remoteId),
429
				]
430
			);
431
		$query->execute();
432
	}
433
434
	/**
435
	 * get share ID on remote server for federated re-shares
436
	 *
437
	 * @param IShare $share
438
	 * @return int
439
	 * @throws ShareNotFound
440
	 */
441
	public function getRemoteId(IShare $share) {
442
		$query = $this->dbConnection->getQueryBuilder();
443
		$query->select('remote_id')->from('federated_reshares')
444
			->where($query->expr()->eq('share_id', $query->createNamedParameter((int)$share->getId())));
445
		$data = $query->execute()->fetch();
446
447
		if (!is_array($data) || !isset($data['remote_id'])) {
448
			throw new ShareNotFound();
449
		}
450
451
		return (int)$data['remote_id'];
452
	}
453
454
	/**
455
	 * @inheritdoc
456
	 */
457
	public function move(IShare $share, $recipient) {
458
		/*
459
		 * This function does nothing yet as it is just for outgoing
460
		 * federated shares.
461
		 */
462
		return $share;
463
	}
464
465
	/**
466
	 * Get all children of this share
467
	 *
468
	 * @param IShare $parent
469
	 * @return IShare[]
470
	 */
471 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...
472
		$children = [];
473
474
		$qb = $this->dbConnection->getQueryBuilder();
475
		$qb->select('*')
476
			->from('share')
477
			->where($qb->expr()->eq('parent', $qb->createNamedParameter($parent->getId())))
478
			->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_REMOTE)))
479
			->orderBy('id');
480
481
		$cursor = $qb->execute();
482
		while($data = $cursor->fetch()) {
483
			$children[] = $this->createShareObject($data);
484
		}
485
		$cursor->closeCursor();
486
487
		return $children;
488
	}
489
490
	/**
491
	 * Delete a share (owner unShares the file)
492
	 *
493
	 * @param IShare $share
494
	 */
495
	public function delete(IShare $share) {
496
497
		list(, $remote) = $this->addressHandler->splitUserRemote($share->getSharedWith());
498
499
		$isOwner = false;
500
501
		$this->removeShareFromTable($share);
502
503
		// if the local user is the owner we can send the unShare request directly...
504
		if ($this->userManager->userExists($share->getShareOwner())) {
505
			$this->notifications->sendRemoteUnShare($remote, $share->getId(), $share->getToken());
506
			$this->revokeShare($share, true);
507
			$isOwner = true;
508
		} else { // ... if not we need to correct ID for the unShare request
509
			$remoteId = $this->getRemoteId($share);
510
			$this->notifications->sendRemoteUnShare($remote, $remoteId, $share->getToken());
511
			$this->revokeShare($share, false);
512
		}
513
514
		// send revoke notification to the other user, if initiator and owner are not the same user
515
		if ($share->getShareOwner() !== $share->getSharedBy()) {
516
			$remoteId = $this->getRemoteId($share);
517
			if ($isOwner) {
518
				list(, $remote) = $this->addressHandler->splitUserRemote($share->getSharedBy());
519
			} else {
520
				list(, $remote) = $this->addressHandler->splitUserRemote($share->getShareOwner());
521
			}
522
			$this->notifications->sendRevokeShare($remote, $remoteId, $share->getToken());
523
		}
524
	}
525
526
	/**
527
	 * in case of a re-share we need to send the other use (initiator or owner)
528
	 * a message that the file was unshared
529
	 *
530
	 * @param IShare $share
531
	 * @param bool $isOwner the user can either be the owner or the user who re-sahred it
532
	 * @throws ShareNotFound
533
	 * @throws \OC\HintException
534
	 */
535 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...
536
		// also send a unShare request to the initiator, if this is a different user than the owner
537
		if ($share->getShareOwner() !== $share->getSharedBy()) {
538
			if ($isOwner) {
539
				list(, $remote) = $this->addressHandler->splitUserRemote($share->getSharedBy());
540
			} else {
541
				list(, $remote) = $this->addressHandler->splitUserRemote($share->getShareOwner());
542
			}
543
			$remoteId = $this->getRemoteId($share);
544
			$this->notifications->sendRevokeShare($remote, $remoteId, $share->getToken());
545
		}
546
	}
547
548
	/**
549
	 * remove share from table
550
	 *
551
	 * @param IShare $share
552
	 */
553
	public function removeShareFromTable(IShare $share) {
554
		$this->removeShareFromTableById($share->getId());
555
	}
556
557
	/**
558
	 * remove share from table
559
	 *
560
	 * @param string $shareId
561
	 */
562
	private function removeShareFromTableById($shareId) {
563
		$qb = $this->dbConnection->getQueryBuilder();
564
		$qb->delete('share')
565
			->where($qb->expr()->eq('id', $qb->createNamedParameter($shareId)));
566
		$qb->execute();
567
568
		$qb->delete('federated_reshares')
569
			->where($qb->expr()->eq('share_id', $qb->createNamedParameter($shareId)));
570
		$qb->execute();
571
	}
572
573
	/**
574
	 * @inheritdoc
575
	 */
576
	public function deleteFromSelf(IShare $share, $recipient) {
577
		// nothing to do here. Technically deleteFromSelf in the context of federated
578
		// shares is a umount of a external storage. This is handled here
579
		// apps/files_sharing/lib/external/manager.php
580
		// TODO move this code over to this app
581
		return;
582
	}
583
584
585 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...
586
		$qb = $this->dbConnection->getQueryBuilder();
587
		$qb->select('*')
588
			->from('share', 's')
589
			->andWhere($qb->expr()->orX(
590
				$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
591
				$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
592
			))
593
			->andWhere(
594
				$qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_REMOTE))
595
			);
596
597
		/**
598
		 * Reshares for this user are shares where they are the owner.
599
		 */
600
		if ($reshares === false) {
601
			$qb->andWhere($qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)));
602
		} else {
603
			$qb->andWhere(
604
				$qb->expr()->orX(
605
					$qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)),
606
					$qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId))
607
				)
608
			);
609
		}
610
611
		$qb->innerJoin('s', 'filecache' ,'f', 's.file_source = f.fileid');
612
		$qb->andWhere($qb->expr()->eq('f.parent', $qb->createNamedParameter($node->getId())));
613
614
		$qb->orderBy('id');
615
616
		$cursor = $qb->execute();
617
		$shares = [];
618
		while ($data = $cursor->fetch()) {
619
			$shares[$data['fileid']][] = $this->createShareObject($data);
620
		}
621
		$cursor->closeCursor();
622
623
		return $shares;
624
	}
625
626
	/**
627
	 * @inheritdoc
628
	 */
629 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...
630
		$qb = $this->dbConnection->getQueryBuilder();
631
		$qb->select('*')
632
			->from('share');
633
634
		$qb->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_REMOTE)));
635
636
		/**
637
		 * Reshares for this user are shares where they are the owner.
638
		 */
639
		if ($reshares === false) {
640
			//Special case for old shares created via the web UI
641
			$or1 = $qb->expr()->andX(
642
				$qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)),
643
				$qb->expr()->isNull('uid_initiator')
644
			);
645
646
			$qb->andWhere(
647
				$qb->expr()->orX(
648
					$qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)),
649
					$or1
650
				)
651
			);
652
		} else {
653
			$qb->andWhere(
654
				$qb->expr()->orX(
655
					$qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)),
656
					$qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId))
657
				)
658
			);
659
		}
660
661
		if ($node !== null) {
662
			$qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId())));
663
		}
664
665
		if ($limit !== -1) {
666
			$qb->setMaxResults($limit);
667
		}
668
669
		$qb->setFirstResult($offset);
670
		$qb->orderBy('id');
671
672
		$cursor = $qb->execute();
673
		$shares = [];
674
		while($data = $cursor->fetch()) {
675
			$shares[] = $this->createShareObject($data);
676
		}
677
		$cursor->closeCursor();
678
679
		return $shares;
680
	}
681
682
	/**
683
	 * @inheritdoc
684
	 */
685 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...
686
		$qb = $this->dbConnection->getQueryBuilder();
687
688
		$qb->select('*')
689
			->from('share')
690
			->where($qb->expr()->eq('id', $qb->createNamedParameter($id)))
691
			->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_REMOTE)));
692
693
		$cursor = $qb->execute();
694
		$data = $cursor->fetch();
695
		$cursor->closeCursor();
696
697
		if ($data === false) {
698
			throw new ShareNotFound();
699
		}
700
701
		try {
702
			$share = $this->createShareObject($data);
703
		} catch (InvalidShare $e) {
704
			throw new ShareNotFound();
705
		}
706
707
		return $share;
708
	}
709
710
	/**
711
	 * Get shares for a given path
712
	 *
713
	 * @param \OCP\Files\Node $path
714
	 * @return IShare[]
715
	 */
716 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...
717
		$qb = $this->dbConnection->getQueryBuilder();
718
719
		$cursor = $qb->select('*')
720
			->from('share')
721
			->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($path->getId())))
722
			->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_REMOTE)))
723
			->execute();
724
725
		$shares = [];
726
		while($data = $cursor->fetch()) {
727
			$shares[] = $this->createShareObject($data);
728
		}
729
		$cursor->closeCursor();
730
731
		return $shares;
732
	}
733
734
	/**
735
	 * @inheritdoc
736
	 */
737 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...
738
		/** @var IShare[] $shares */
739
		$shares = [];
740
741
		//Get shares directly with this user
742
		$qb = $this->dbConnection->getQueryBuilder();
743
		$qb->select('*')
744
			->from('share');
745
746
		// Order by id
747
		$qb->orderBy('id');
748
749
		// Set limit and offset
750
		if ($limit !== -1) {
751
			$qb->setMaxResults($limit);
752
		}
753
		$qb->setFirstResult($offset);
754
755
		$qb->where($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_REMOTE)));
756
		$qb->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($userId)));
757
758
		// Filter by node if provided
759
		if ($node !== null) {
760
			$qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId())));
761
		}
762
763
		$cursor = $qb->execute();
764
765
		while($data = $cursor->fetch()) {
766
			$shares[] = $this->createShareObject($data);
767
		}
768
		$cursor->closeCursor();
769
770
771
		return $shares;
772
	}
773
774
	/**
775
	 * Get a share by token
776
	 *
777
	 * @param string $token
778
	 * @return IShare
779
	 * @throws ShareNotFound
780
	 */
781 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...
782
		$qb = $this->dbConnection->getQueryBuilder();
783
784
		$cursor = $qb->select('*')
785
			->from('share')
786
			->where($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_REMOTE)))
787
			->andWhere($qb->expr()->eq('token', $qb->createNamedParameter($token)))
788
			->execute();
789
790
		$data = $cursor->fetch();
791
792
		if ($data === false) {
793
			throw new ShareNotFound('Share not found', $this->l->t('Could not find share'));
794
		}
795
796
		try {
797
			$share = $this->createShareObject($data);
798
		} catch (InvalidShare $e) {
799
			throw new ShareNotFound('Share not found', $this->l->t('Could not find share'));
800
		}
801
802
		return $share;
803
	}
804
805
	/**
806
	 * get database row of a give share
807
	 *
808
	 * @param $id
809
	 * @return array
810
	 * @throws ShareNotFound
811
	 */
812 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...
813
814
		// Now fetch the inserted share and create a complete share object
815
		$qb = $this->dbConnection->getQueryBuilder();
816
		$qb->select('*')
817
			->from('share')
818
			->where($qb->expr()->eq('id', $qb->createNamedParameter($id)));
819
820
		$cursor = $qb->execute();
821
		$data = $cursor->fetch();
822
		$cursor->closeCursor();
823
824
		if ($data === false) {
825
			throw new ShareNotFound;
826
		}
827
828
		return $data;
829
	}
830
831
	/**
832
	 * Create a share object from an database row
833
	 *
834
	 * @param array $data
835
	 * @return IShare
836
	 * @throws InvalidShare
837
	 * @throws ShareNotFound
838
	 */
839
	private function createShareObject($data) {
840
841
		$share = new Share($this->rootFolder, $this->userManager);
842
		$share->setId((int)$data['id'])
843
			->setShareType((int)$data['share_type'])
844
			->setPermissions((int)$data['permissions'])
845
			->setTarget($data['file_target'])
846
			->setMailSend((bool)$data['mail_send'])
847
			->setToken($data['token']);
848
849
		$shareTime = new \DateTime();
850
		$shareTime->setTimestamp((int)$data['stime']);
851
		$share->setShareTime($shareTime);
852
		$share->setSharedWith($data['share_with']);
853
854
		if ($data['uid_initiator'] !== null) {
855
			$share->setShareOwner($data['uid_owner']);
856
			$share->setSharedBy($data['uid_initiator']);
857
		} else {
858
			//OLD SHARE
859
			$share->setSharedBy($data['uid_owner']);
860
			$path = $this->getNode($share->getSharedBy(), (int)$data['file_source']);
861
862
			$owner = $path->getOwner();
863
			$share->setShareOwner($owner->getUID());
864
		}
865
866
		$share->setNodeId((int)$data['file_source']);
867
		$share->setNodeType($data['item_type']);
868
869
		$share->setProviderId($this->identifier());
870
871
		return $share;
872
	}
873
874
	/**
875
	 * Get the node with file $id for $user
876
	 *
877
	 * @param string $userId
878
	 * @param int $id
879
	 * @return \OCP\Files\File|\OCP\Files\Folder
880
	 * @throws InvalidShare
881
	 */
882 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...
883
		try {
884
			$userFolder = $this->rootFolder->getUserFolder($userId);
885
		} catch (NotFoundException $e) {
886
			throw new InvalidShare();
887
		}
888
889
		$nodes = $userFolder->getById($id);
890
891
		if (empty($nodes)) {
892
			throw new InvalidShare();
893
		}
894
895
		return $nodes[0];
896
	}
897
898
	/**
899
	 * A user is deleted from the system
900
	 * So clean up the relevant shares.
901
	 *
902
	 * @param string $uid
903
	 * @param int $shareType
904
	 */
905 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...
906
		//TODO: probabaly a good idea to send unshare info to remote servers
907
908
		$qb = $this->dbConnection->getQueryBuilder();
909
910
		$qb->delete('share')
911
			->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_REMOTE)))
912
			->andWhere($qb->expr()->eq('uid_owner', $qb->createNamedParameter($uid)))
913
			->execute();
914
	}
915
916
	/**
917
	 * This provider does not handle groups
918
	 *
919
	 * @param string $gid
920
	 */
921
	public function groupDeleted($gid) {
922
		// We don't handle groups here
923
		return;
924
	}
925
926
	/**
927
	 * This provider does not handle groups
928
	 *
929
	 * @param string $uid
930
	 * @param string $gid
931
	 */
932
	public function userDeletedFromGroup($uid, $gid) {
933
		// We don't handle groups here
934
		return;
935
	}
936
937
	/**
938
	 * check if users from other Nextcloud instances are allowed to mount public links share by this instance
939
	 *
940
	 * @return bool
941
	 */
942
	public function isOutgoingServer2serverShareEnabled() {
943
		$result = $this->config->getAppValue('files_sharing', 'outgoing_server2server_share_enabled', 'yes');
944
		return ($result === 'yes');
945
	}
946
947
	/**
948
	 * check if users are allowed to mount public links from other ownClouds
949
	 *
950
	 * @return bool
951
	 */
952
	public function isIncomingServer2serverShareEnabled() {
953
		$result = $this->config->getAppValue('files_sharing', 'incoming_server2server_share_enabled', 'yes');
954
		return ($result === 'yes');
955
	}
956
957
	/**
958
	 * Check if querying sharees on the lookup server is enabled
959
	 *
960
	 * @return bool
961
	 */
962
	public function isLookupServerQueriesEnabled() {
963
		$result = $this->config->getAppValue('files_sharing', 'lookupServerEnabled', 'no');
964
		return ($result === 'yes');
965
	}
966
967
968
	/**
969
	 * Check if it is allowed to publish user specific data to the lookup server
970
	 *
971
	 * @return bool
972
	 */
973
	public function isLookupServerUploadEnabled() {
974
		$result = $this->config->getAppValue('files_sharing', 'lookupServerUploadEnabled', 'yes');
975
		return ($result === 'yes');
976
	}
977
}
978