Manager   F
last analyzed

Complexity

Total Complexity 231

Size/Duplication

Total Lines 1490
Duplicated Lines 3.89 %

Coupling/Cohesion

Components 1
Dependencies 30

Importance

Changes 0
Metric Value
dl 58
loc 1490
rs 0.8
c 0
b 0
f 0
wmc 231
lcom 1
cbo 30

50 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 30 1
A shareTypeToProviderMap() 0 12 2
A splitFullId() 0 3 1
A verifyPassword() 0 19 2
B passwordMustBeEnforced() 0 11 8
F generalCreateChecks() 20 95 24
C validateExpirationDate() 8 61 11
B userCreateChecks() 0 50 11
B groupCreateChecks() 0 36 9
A linkCreateChecks() 0 17 5
A setLinkParent() 0 10 3
A pathCreateChecks() 0 11 4
A canShare() 0 9 3
D transferShare() 0 82 19
A deleteChildren() 0 15 2
A formatUnshareHookParams() 0 25 5
A deleteShare() 0 34 2
A deleteFromSelf() 0 22 1
A moveShare() 0 3 1
A updateShareForRecipient() 0 6 1
B getAllSharesBy() 0 33 8
C getSharesBy() 0 66 14
A getSharedWith() 0 5 1
A getAllSharedWith() 0 16 2
A getShareById() 0 20 5
A getSharesByPath() 0 17 3
B getShareByToken() 0 31 7
B checkPassword() 0 23 6
A userDeleted() 0 8 2
A groupDeleted() 0 4 1
A userDeletedFromGroup() 0 4 1
A getAccessList() 0 2 1
A newShare() 0 3 1
A shareApiEnabled() 0 3 1
A shareApiAllowLinks() 0 3 1
A shareApiLinkEnforcePassword() 0 3 1
A shareApiLinkEnforcePasswordReadOnly() 0 3 1
A shareApiLinkEnforcePasswordReadWrite() 0 3 1
A shareApiLinkEnforcePasswordWriteOnly() 0 3 1
A shareApiLinkDefaultExpireDate() 0 3 1
A shareApiLinkDefaultExpireDateEnforced() 0 4 2
A shareApiLinkDefaultExpireDays() 0 3 1
A shareApiLinkAllowPublicUpload() 0 3 1
A shareWithGroupMembersOnly() 0 3 1
A shareWithMembershipGroupOnly() 0 3 1
A allowGroupSharing() 0 3 1
B sharingDisabledForUser() 0 30 6
A outgoingServer2ServerSharesAllowed() 0 3 1
D createShare() 4 124 16
F updateShare() 26 128 27

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Manager often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Manager, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * @author Arthur Schiwon <[email protected]>
4
 * @author Björn Schießle <[email protected]>
5
 * @author Joas Schilling <[email protected]>
6
 * @author Roeland Jago Douma <[email protected]>
7
 * @author Thomas Müller <[email protected]>
8
 * @author Vincent Petry <[email protected]>
9
 *
10
 * @copyright Copyright (c) 2018, ownCloud GmbH
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 OC\Share20;
28
29
use OC\Cache\CappedMemoryCache;
30
use OC\Files\Mount\MoveableMount;
31
use OC\Files\View;
32
use OCP\Files\File;
33
use OCP\Files\Folder;
34
use OCP\Files\IRootFolder;
35
use OCP\Files\Mount\IMountManager;
36
use OCP\Files\NotFoundException;
37
use OCP\IConfig;
38
use OCP\IDBConnection;
39
use OCP\IGroupManager;
40
use OCP\IL10N;
41
use OCP\ILogger;
42
use OCP\IUserManager;
43
use OCP\Security\IHasher;
44
use OCP\Security\ISecureRandom;
45
use OCP\Share\Exceptions\GenericShareException;
46
use OCP\Share\Exceptions\ShareNotFound;
47
use OCP\Share\Exceptions\TransferSharesException;
48
use OCP\Share\IManager;
49
use OCP\Share\IProviderFactory;
50
use OCP\Share\IShare;
51
use Symfony\Component\EventDispatcher\EventDispatcher;
52
use Symfony\Component\EventDispatcher\GenericEvent;
53
54
/**
55
 * This class is the communication hub for all sharing related operations.
56
 */
57
class Manager implements IManager {
58
59
	/** @var IProviderFactory */
60
	private $factory;
61
	/** @var ILogger */
62
	private $logger;
63
	/** @var IConfig */
64
	private $config;
65
	/** @var ISecureRandom */
66
	private $secureRandom;
67
	/** @var IHasher */
68
	private $hasher;
69
	/** @var IMountManager */
70
	private $mountManager;
71
	/** @var IGroupManager */
72
	private $groupManager;
73
	/** @var IL10N */
74
	private $l;
75
	/** @var IUserManager */
76
	private $userManager;
77
	/** @var IRootFolder */
78
	private $rootFolder;
79
	/** @var CappedMemoryCache */
80
	private $sharingDisabledForUsersCache;
81
	/** @var EventDispatcher  */
82
	private $eventDispatcher;
83
84
	/** @var View */
85
	private $view;
86
	/** @var IDBConnection  */
87
	private $connection;
88
89
	/**
90
	 * Manager constructor.
91
	 *
92
	 * @param ILogger $logger
93
	 * @param IConfig $config
94
	 * @param ISecureRandom $secureRandom
95
	 * @param IHasher $hasher
96
	 * @param IMountManager $mountManager
97
	 * @param IGroupManager $groupManager
98
	 * @param IL10N $l
99
	 * @param IProviderFactory $factory
100
	 * @param IUserManager $userManager
101
	 * @param IRootFolder $rootFolder
102
	 */
103
	public function __construct(
104
			ILogger $logger,
105
			IConfig $config,
106
			ISecureRandom $secureRandom,
107
			IHasher $hasher,
108
			IMountManager $mountManager,
109
			IGroupManager $groupManager,
110
			IL10N $l,
111
			IProviderFactory $factory,
112
			IUserManager $userManager,
113
			IRootFolder $rootFolder,
114
			EventDispatcher $eventDispatcher,
115
			View $view,
116
			IDBConnection $connection
117
	) {
118
		$this->logger = $logger;
119
		$this->config = $config;
120
		$this->secureRandom = $secureRandom;
121
		$this->hasher = $hasher;
122
		$this->mountManager = $mountManager;
123
		$this->groupManager = $groupManager;
124
		$this->l = $l;
125
		$this->factory = $factory;
126
		$this->userManager = $userManager;
127
		$this->rootFolder = $rootFolder;
128
		$this->sharingDisabledForUsersCache = new CappedMemoryCache();
129
		$this->eventDispatcher = $eventDispatcher;
130
		$this->view = $view;
131
		$this->connection = $connection;
132
	}
133
134
	/**
135
	 * @param int[] $shareTypes - ref \OC\Share\Constants[]
136
	 * @return int[] $providerIdMap e.g. { "ocinternal" => { 0, 1 }[2] }[1]
137
	 */
138
	private function shareTypeToProviderMap($shareTypes) {
139
		$providerIdMap = [];
140
		foreach ($shareTypes as $shareType) {
141
			// Get provider and its ID, at this point provider is cached at IProviderFactory instance
142
			$provider = $this->factory->getProviderForType($shareType);
143
			$providerId = $provider->identifier();
144
145
			// Create a key -> multi value map
146
			$providerIdMap[$providerId][] = $shareType;
147
		}
148
		return $providerIdMap;
149
	}
150
151
	/**
152
	 * Convert from a full share id to a tuple (providerId, shareId)
153
	 *
154
	 * @param string $id
155
	 * @return string[]
156
	 */
157
	private function splitFullId($id) {
158
		return \explode(':', $id, 2);
159
	}
160
161
	/**
162
	 * Verify if a password meets all requirements
163
	 *
164
	 * @param string $password
165
	 * @throws \Exception
166
	 */
167
	protected function verifyPassword($password) {
168
		// Let others verify the password
169
		$accepted = true;
170
		$message = '';
171
		\OCP\Util::emitHook('\OC\Share', 'verifyPassword', [
172
				'password' => $password,
173
				'accepted' => &$accepted,
174
				'message' => &$message
175
		]);
176
177
		if (!$accepted) {
178
			throw new \Exception($message);
179
		}
180
181
		$this->eventDispatcher->dispatch(
182
			'OCP\Share::validatePassword',
183
			new GenericEvent(null, ['password' => $password])
184
		);
185
	}
186
187
	/**
188
	 * Check if a password must be enforced if the shared has those permissions
189
	 * @param int $permissions \OCP\Constants::PERMISSION_* ("|" can be use for sets of permissions)
190
	 * @return bool true if the password must be enforced, false otherwise
191
	 */
192
	protected function passwordMustBeEnforced($permissions) {
193
		$roEnforcement = $permissions === \OCP\Constants::PERMISSION_READ && $this->shareApiLinkEnforcePasswordReadOnly();
194
		$woEnforcement = $permissions === \OCP\Constants::PERMISSION_CREATE && $this->shareApiLinkEnforcePasswordWriteOnly();
195
		// use read & write enforcement for the rest of the cases
196
		$rwEnforcement = ($permissions !== \OCP\Constants::PERMISSION_READ && $permissions !== \OCP\Constants::PERMISSION_CREATE) && $this->shareApiLinkEnforcePasswordReadWrite();
197
		if ($roEnforcement || $woEnforcement || $rwEnforcement) {
198
			return true;
199
		} else {
200
			return false;
201
		}
202
	}
203
204
	/**
205
	 * Check for generic requirements before creating a share
206
	 *
207
	 * @param \OCP\Share\IShare $share
208
	 * @throws \InvalidArgumentException
209
	 * @throws GenericShareException
210
	 */
211
	protected function generalCreateChecks(\OCP\Share\IShare $share) {
212
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) {
213
			// We expect a valid user as sharedWith for user shares
214
			if (!$this->userManager->userExists($share->getSharedWith())) {
215
				throw new \InvalidArgumentException('SharedWith is not a valid user');
216
			}
217
		} elseif ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
218
			// We expect a valid group as sharedWith for group shares
219
			if (!$this->groupManager->groupExists($share->getSharedWith())) {
220
				throw new \InvalidArgumentException('SharedWith is not a valid group');
221
			}
222
		} elseif ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) {
223
			if ($share->getSharedWith() !== null) {
224
				throw new \InvalidArgumentException('SharedWith should be empty');
225
			}
226
		} elseif ($share->getShareType() === \OCP\Share::SHARE_TYPE_REMOTE) {
227
			if ($share->getSharedWith() === null) {
228
				throw new \InvalidArgumentException('SharedWith should not be empty');
229
			}
230
		} else {
231
			// We can't handle other types yet
232
			throw new \InvalidArgumentException('unkown share type');
233
		}
234
235
		// Verify the initiator of the share is set
236
		if ($share->getSharedBy() === null) {
237
			throw new \InvalidArgumentException('SharedBy should be set');
238
		}
239
240
		// Cannot share with yourself
241 View Code Duplication
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER &&
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
242
			$share->getSharedWith() === $share->getSharedBy()) {
243
			throw new \InvalidArgumentException('Can\'t share with yourself');
244
		}
245
246
		// The path should be set
247
		if ($share->getNode() === null) {
248
			throw new \InvalidArgumentException('Path should be set');
249
		}
250
251
		// And it should be a file or a folder
252
		if (!($share->getNode() instanceof \OCP\Files\File) &&
253
				!($share->getNode() instanceof \OCP\Files\Folder)) {
254
			throw new \InvalidArgumentException('Path should be either a file or a folder');
255
		}
256
257
		// And you can't share your rootfolder
258
		if ($this->userManager->userExists($share->getSharedBy())) {
259
			$sharedPath = $this->rootFolder->getUserFolder($share->getSharedBy())->getPath();
260
		} else {
261
			$sharedPath = $this->rootFolder->getUserFolder($share->getShareOwner())->getPath();
262
		}
263
		if ($sharedPath === $share->getNode()->getPath()) {
264
			throw new \InvalidArgumentException('You can\'t share your root folder');
265
		}
266
267
		// Check if we actually have share permissions
268 View Code Duplication
		if (!$share->getNode()->isShareable()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
269
			$message_t = $this->l->t('You are not allowed to share %s', [$share->getNode()->getPath()]);
270
			throw new GenericShareException($message_t, $message_t, 404);
271
		}
272
273
		// Permissions should be set
274
		if ($share->getPermissions() === null) {
275
			throw new \InvalidArgumentException('A share requires permissions');
276
		}
277
278
		/*
279
		 * Quick fix for #23536
280
		 * Non moveable mount points do not have update and delete permissions
281
		 * while we 'most likely' do have that on the storage.
282
		 */
283
		$permissions = $share->getNode()->getPermissions();
284
		$mount = $share->getNode()->getMountPoint();
285
		if (!($mount instanceof MoveableMount)) {
286
			$permissions |= \OCP\Constants::PERMISSION_DELETE | \OCP\Constants::PERMISSION_UPDATE;
287
		}
288
289
		// Check that we do not share with more permissions than we have
290 View Code Duplication
		if ($share->getPermissions() & ~$permissions) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
291
			$message_t = $this->l->t('Cannot increase permissions of %s', [$share->getNode()->getPath()]);
292
			throw new GenericShareException($message_t, $message_t, 404);
293
		}
294
295
		if ($share->getNode() instanceof \OCP\Files\File) {
296 View Code Duplication
			if ($share->getPermissions() & \OCP\Constants::PERMISSION_DELETE) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
297
				$message_t = $this->l->t('Files can\'t be shared with delete permissions');
298
				throw new GenericShareException($message_t);
299
			}
300 View Code Duplication
			if ($share->getPermissions() & \OCP\Constants::PERMISSION_CREATE) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
301
				$message_t = $this->l->t('Files can\'t be shared with create permissions');
302
				throw new GenericShareException($message_t);
303
			}
304
		}
305
	}
306
307
	/**
308
	 * Validate if the expiration date fits the system settings
309
	 *
310
	 * @param \OCP\Share\IShare $share The share to validate the expiration date of
311
	 * @return \OCP\Share\IShare The modified share object
312
	 * @throws GenericShareException
313
	 * @throws \InvalidArgumentException
314
	 * @throws \Exception
315
	 */
316
	protected function validateExpirationDate(\OCP\Share\IShare $share) {
317
		$expirationDate = $share->getExpirationDate();
318
319
		if ($expirationDate !== null) {
320
			//Make sure the expiration date is a date
321
			$expirationDate->setTime(0, 0, 0);
322
323
			$date = new \DateTime();
324
			$date->setTime(0, 0, 0);
325 View Code Duplication
			if ($date >= $expirationDate) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
326
				$message = $this->l->t('Expiration date is in the past');
327
				throw new GenericShareException($message, $message, 404);
328
			}
329
		}
330
331
		// If expiredate is empty set a default one if there is a default
332
		$fullId = null;
333
		try {
334
			$fullId = $share->getFullId();
335
		} catch (\UnexpectedValueException $e) {
336
			// This is a new share
337
		}
338
339
		if ($fullId === null && $expirationDate === null && $this->shareApiLinkDefaultExpireDate()) {
340
			$expirationDate = new \DateTime();
341
			$expirationDate->setTime(0, 0, 0);
342
			$expirationDate->add(new \DateInterval('P'.$this->shareApiLinkDefaultExpireDays().'D'));
343
		}
344
345
		// If we enforce the expiration date check that is does not exceed
346
		if ($this->shareApiLinkDefaultExpireDateEnforced()) {
347
			if ($expirationDate === null) {
348
				throw new \InvalidArgumentException('Expiration date is enforced');
349
			}
350
351
			$date = new \DateTime();
352
			$date->setTime(0, 0, 0);
353
			$date->add(new \DateInterval('P' . $this->shareApiLinkDefaultExpireDays() . 'D'));
354 View Code Duplication
			if ($date < $expirationDate) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
355
				$message = $this->l->t('Cannot set expiration date more than %s days in the future', [$this->shareApiLinkDefaultExpireDays()]);
356
				throw new GenericShareException($message, $message, 404);
357
			}
358
		}
359
360
		$accepted = true;
361
		$message = '';
362
		\OCP\Util::emitHook('\OC\Share', 'verifyExpirationDate', [
363
			'expirationDate' => &$expirationDate,
364
			'accepted' => &$accepted,
365
			'message' => &$message,
366
			'passwordSet' => $share->getPassword() !== null,
367
		]);
368
369
		if (!$accepted) {
370
			throw new \Exception($message);
371
		}
372
373
		$share->setExpirationDate($expirationDate);
0 ignored issues
show
Bug introduced by
It seems like $expirationDate defined by $share->getExpirationDate() on line 317 can be null; however, OCP\Share\IShare::setExpirationDate() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
374
375
		return $share;
376
	}
377
378
	/**
379
	 * Check for pre share requirements for user shares
380
	 *
381
	 * @param \OCP\Share\IShare $share
382
	 * @throws \Exception
383
	 */
384
	protected function userCreateChecks(\OCP\Share\IShare $share) {
385
		// Check if we can share with group members only
386
		if ($this->shareWithGroupMembersOnly()) {
387
			$sharedBy = $this->userManager->get($share->getSharedBy());
388
			$sharedWith = $this->userManager->get($share->getSharedWith());
389
			// Verify we can share with this user
390
			$groups = \array_intersect(
391
					$this->groupManager->getUserGroupIds($sharedBy),
0 ignored issues
show
Bug introduced by
It seems like $sharedBy defined by $this->userManager->get($share->getSharedBy()) on line 387 can be null; however, OCP\IGroupManager::getUserGroupIds() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
392
					$this->groupManager->getUserGroupIds($sharedWith)
0 ignored issues
show
Bug introduced by
It seems like $sharedWith defined by $this->userManager->get($share->getSharedWith()) on line 388 can be null; however, OCP\IGroupManager::getUserGroupIds() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
393
			);
394
			if (empty($groups)) {
395
				throw new \Exception('Only sharing with group members is allowed');
396
			}
397
		}
398
399
		/*
400
		 * TODO: Could be costly, fix
401
		 *
402
		 * Also this is not what we want in the future.. then we want to squash identical shares.
403
		 */
404
		$provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_USER);
405
		$existingShares = $provider->getSharesByPath($share->getNode());
406
		foreach ($existingShares as $existingShare) {
407
			// Ignore if it is the same share
408
			try {
409
				if ($existingShare->getFullId() === $share->getFullId()) {
410
					continue;
411
				}
412
			} catch (\UnexpectedValueException $e) {
413
				//Shares are not identical
414
			}
415
416
			// Identical share already existst
417
			if ($existingShare->getSharedWith() === $share->getSharedWith()) {
418
				throw new \Exception('Path already shared with this user');
419
			}
420
421
			// The share is already shared with this user via a group share
422
			if ($existingShare->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
423
				$group = $this->groupManager->get($existingShare->getSharedWith());
424
				if ($group !== null) {
425
					$user = $this->userManager->get($share->getSharedWith());
426
427
					if ($group->inGroup($user) && $existingShare->getShareOwner() !== $share->getShareOwner()) {
0 ignored issues
show
Bug introduced by
It seems like $user defined by $this->userManager->get($share->getSharedWith()) on line 425 can be null; however, OCP\IGroup::inGroup() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
428
						throw new \Exception('Path already shared with this user');
429
					}
430
				}
431
			}
432
		}
433
	}
434
435
	/**
436
	 * Check for pre share requirements for group shares
437
	 *
438
	 * @param \OCP\Share\IShare $share
439
	 * @throws \Exception
440
	 */
441
	protected function groupCreateChecks(\OCP\Share\IShare $share) {
442
		// Verify group shares are allowed
443
		if (!$this->allowGroupSharing()) {
444
			throw new \Exception('Group sharing is now allowed');
445
		}
446
447
		// Verify if the user can share with this group
448
		if ($this->shareWithMembershipGroupOnly()) {
449
			$sharedBy = $this->userManager->get($share->getSharedBy());
450
			$sharedWith = $this->groupManager->get($share->getSharedWith());
451
			if ($sharedWith === null || !$sharedWith->inGroup($sharedBy)) {
0 ignored issues
show
Bug introduced by
It seems like $sharedBy defined by $this->userManager->get($share->getSharedBy()) on line 449 can be null; however, OCP\IGroup::inGroup() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
452
				throw new \Exception('Only sharing within your own groups is allowed');
453
			}
454
		}
455
456
		/*
457
		 * TODO: Could be costly, fix
458
		 *
459
		 * Also this is not what we want in the future.. then we want to squash identical shares.
460
		 */
461
		$provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_GROUP);
462
		$existingShares = $provider->getSharesByPath($share->getNode());
463
		foreach ($existingShares as $existingShare) {
464
			try {
465
				if ($existingShare->getFullId() === $share->getFullId()) {
466
					continue;
467
				}
468
			} catch (\UnexpectedValueException $e) {
469
				//It is a new share so just continue
470
			}
471
472
			if ($existingShare->getSharedWith() === $share->getSharedWith()) {
473
				throw new \Exception('Path already shared with this group');
474
			}
475
		}
476
	}
477
478
	/**
479
	 * Check for pre share requirements for link shares
480
	 *
481
	 * @param \OCP\Share\IShare $share
482
	 * @throws \Exception
483
	 */
484
	protected function linkCreateChecks(\OCP\Share\IShare $share) {
485
		// Are link shares allowed?
486
		if (!$this->shareApiAllowLinks()) {
487
			throw new \Exception('Link sharing not allowed');
488
		}
489
490
		// Link shares by definition can't have share permissions
491
		if ($share->getPermissions() & \OCP\Constants::PERMISSION_SHARE) {
492
			throw new \InvalidArgumentException('Link shares can\'t have reshare permissions');
493
		}
494
495
		// Check if public upload is allowed
496
		if (!$this->shareApiLinkAllowPublicUpload() &&
497
			($share->getPermissions() & (\OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE))) {
498
			throw new \InvalidArgumentException('Public upload not allowed');
499
		}
500
	}
501
502
	/**
503
	 * To make sure we don't get invisible link shares we set the parent
504
	 * of a link if it is a reshare. This is a quick word around
505
	 * until we can properly display multiple link shares in the UI
506
	 *
507
	 * See: https://github.com/owncloud/core/issues/22295
508
	 *
509
	 * FIXME: Remove once multiple link shares can be properly displayed
510
	 *
511
	 * @param \OCP\Share\IShare $share
512
	 */
513
	protected function setLinkParent(\OCP\Share\IShare $share) {
514
515
		// No sense in checking if the method is not there.
516
		if (\method_exists($share, 'setParent')) {
517
			$storage = $share->getNode()->getStorage();
518
			if ($storage->instanceOfStorage('\OCA\Files_Sharing\ISharedStorage')) {
519
				$share->setParent($storage->getShareId());
520
			}
521
		};
522
	}
523
524
	/**
525
	 * @param File|Folder $path
526
	 */
527
	protected function pathCreateChecks($path) {
528
		// Make sure that we do not share a path that contains a shared mountpoint
529
		if ($path instanceof \OCP\Files\Folder) {
530
			$mounts = $this->mountManager->findIn($path->getPath());
531
			foreach ($mounts as $mount) {
532
				if ($mount->getStorage()->instanceOfStorage('OCA\Files_Sharing\ISharedStorage')) {
533
					throw new \InvalidArgumentException('Path contains files shared with you');
534
				}
535
			}
536
		}
537
	}
538
539
	/**
540
	 * Check if the user that is sharing can actually share
541
	 *
542
	 * @param \OCP\Share\IShare $share
543
	 * @throws \Exception
544
	 */
545
	protected function canShare(\OCP\Share\IShare $share) {
546
		if (!$this->shareApiEnabled()) {
547
			throw new \Exception('The share API is disabled');
548
		}
549
550
		if ($this->sharingDisabledForUser($share->getSharedBy())) {
551
			throw new \Exception('You are not allowed to share');
552
		}
553
	}
554
555
	/**
556
	 * Share a path
557
	 *
558
	 * @param \OCP\Share\IShare $share
559
	 * @return Share The share object
560
	 * @throws \Exception
561
	 *
562
	 * TODO: handle link share permissions or check them
563
	 */
564
	public function createShare(\OCP\Share\IShare $share) {
565
		$this->canShare($share);
566
567
		$this->generalCreateChecks($share);
568
569
		// Verify if there are any issues with the path
570
		$this->pathCreateChecks($share->getNode());
571
572
		/*
573
		 * On creation of a share the owner is always the owner of the path
574
		 * Except for mounted federated shares.
575
		 */
576
		$storage = $share->getNode()->getStorage();
577
		if ($storage->instanceOfStorage('OCA\Files_Sharing\External\Storage')) {
578
			$parent = $share->getNode()->getParent();
579
			while ($parent->getStorage()->instanceOfStorage('OCA\Files_Sharing\External\Storage')) {
580
				$parent = $parent->getParent();
581
			}
582
			$share->setShareOwner($parent->getOwner()->getUID());
583
		} else {
584
			$share->setShareOwner($share->getNode()->getOwner()->getUID());
585
		}
586
587
		//Verify share type
588
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) {
589
			$this->userCreateChecks($share);
590
		} elseif ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
591
			$this->groupCreateChecks($share);
592
		} elseif ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) {
593
			$this->linkCreateChecks($share);
594
			$this->setLinkParent($share);
595
596
			/*
597
			 * For now ignore a set token.
598
			 */
599
			$share->setToken(
600
				$this->secureRandom->generate(
601
					\OC\Share\Constants::TOKEN_LENGTH,
602
					\OCP\Security\ISecureRandom::CHAR_LOWER.
603
					\OCP\Security\ISecureRandom::CHAR_UPPER.
604
					\OCP\Security\ISecureRandom::CHAR_DIGITS
605
				)
606
			);
607
608
			//Verify the expiration date
609
			$this->validateExpirationDate($share);
610
611
			//Verify the password
612
			if ($this->passwordMustBeEnforced($share->getPermissions()) && $share->getPassword() === null) {
613
				throw new \InvalidArgumentException('Passwords are enforced for link shares');
614
			} else {
615
				$this->verifyPassword($share->getPassword());
616
			}
617
618
			// If a password is set. Hash it!
619
			if (($share->getPassword() !== null) && ($share->getShouldHashPassword() === true)) {
620
				$share->setPassword($this->hasher->hash($share->getPassword()));
621
			}
622
		}
623
624
		// Cannot share with the owner
625 View Code Duplication
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER &&
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
626
			$share->getSharedWith() === $share->getShareOwner()) {
627
			throw new \InvalidArgumentException('Can\'t share with the share owner');
628
		}
629
630
		// Generate the target
631
		$target = $this->config->getSystemValue('share_folder', '/') .'/'. $share->getNode()->getName();
632
		$target = \OC\Files\Filesystem::normalizePath($target);
633
		$share->setTarget($target);
634
635
		// Pre share hook
636
		$run = true;
637
		$error = '';
638
		$preHookData = [
639
			'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder',
640
			'itemSource' => $share->getNode()->getId(),
641
			'shareType' => $share->getShareType(),
642
			'uidOwner' => $share->getSharedBy(),
643
			'permissions' => $share->getPermissions(),
644
			'fileSource' => $share->getNode()->getId(),
645
			'expiration' => $share->getExpirationDate(),
646
			'token' => $share->getToken(),
647
			'itemTarget' => $share->getTarget(),
648
			'shareWith' => $share->getSharedWith(),
649
			'run' => &$run,
650
			'error' => &$error,
651
		];
652
		\OC_Hook::emit('OCP\Share', 'pre_shared', $preHookData);
653
654
		$beforeEvent = new GenericEvent(null, ['shareData' => $preHookData, 'shareObject' => $share]);
655
		$this->eventDispatcher->dispatch('share.beforeCreate', $beforeEvent);
656
657
		if ($run === false) {
658
			throw new \Exception($error);
659
		}
660
661
		$provider = $this->factory->getProviderForType($share->getShareType());
662
		$share = $provider->create($share);
663
664
		// Post share hook
665
		$postHookData = [
666
			'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder',
667
			'itemSource' => $share->getNode()->getId(),
668
			'shareType' => $share->getShareType(),
669
			'uidOwner' => $share->getSharedBy(),
670
			'permissions' => $share->getPermissions(),
671
			'fileSource' => $share->getNode()->getId(),
672
			'expiration' => $share->getExpirationDate(),
673
			'token' => $share->getToken(),
674
			'id' => $share->getId(),
675
			'shareWith' => $share->getSharedWith(),
676
			'itemTarget' => $share->getTarget(),
677
			'fileTarget' => $share->getTarget(),
678
			'passwordEnabled' => ($share->getPassword() !== null and ($share->getPassword() !== '')),
679
		];
680
681
		\OC_Hook::emit('OCP\Share', 'post_shared', $postHookData);
682
683
		$afterEvent = new GenericEvent(null, ['shareData' => $postHookData, 'shareObject' => $share]);
684
		$this->eventDispatcher->dispatch('share.afterCreate', $afterEvent);
685
686
		return $share;
687
	}
688
689
	/**
690
	 * Transfer shares from oldOwner to newOwner. Both old and new owners are uid
691
	 *
692
	 * finalTarget is of the form "user1/files/transferred from admin on 20180509"
693
	 *
694
	 * TransferShareException would be thrown when:
695
	 *  - oldOwner, newOwner does not exist.
696
	 *  - oldOwner and newOwner are same
697
	 * NotFoundException would be thrown when finalTarget does not exist in the file
698
	 * system
699
	 *
700
	 * @param IShare $share
701
	 * @param string $oldOwner
702
	 * @param string $newOwner
703
	 * @param string $finalTarget
704
	 * @param null|bool $isChild
705
	 * @throws TransferSharesException
706
	 * @throws NotFoundException
707
	 */
708
	public function transferShare(IShare $share, $oldOwner, $newOwner, $finalTarget, $isChild = null) {
709
		if ($this->userManager->get($oldOwner) === null) {
710
			throw new TransferSharesException("The current owner of the share $oldOwner doesn't exist");
711
		}
712
		if ($this->userManager->get($newOwner) === null) {
713
			throw new TransferSharesException("The future owner $newOwner, where the share has to be moved doesn't exist");
714
		}
715
716
		if ($oldOwner === $newOwner) {
717
			throw new TransferSharesException("The current owner of the share and the future owner of the share are same");
718
		}
719
720
		//If the destination location, i.e finalTarget is not present, then
721
		//throw an exception
722
		if (!$this->view->file_exists($finalTarget)) {
723
			throw new NotFoundException("The target location $finalTarget doesn't exist");
724
		}
725
726
		if ($isChild === true) {
727
			//Set the parent to null so that we don't lose the shares after transfer
728
			$builder = $this->connection->getQueryBuilder();
729
			$builder->update('share')
730
				->set('parent', 'null')
731
				->where($builder->expr()->eq('id', $builder->createNamedParameter($share->getId())))
732
				->execute();
733
		}
734
		/**
735
		 * If the share was already shared with new owner, then we can delete it
736
		 */
737
		if ($share->getSharedWith() === $newOwner) {
738
			// Unmount the shares before deleting, so we don't try to get the storage later on.
739
			$shareMountPoint = $this->mountManager->find('/' . $newOwner . '/files' . $share->getTarget());
740
			if ($shareMountPoint) {
741
				$this->mountManager->removeMount($shareMountPoint->getMountPoint());
742
			}
743
744
			$provider = $this->factory->getProviderForType($share->getShareType());
745
			//Try to get the children transferred and then delete the parent
746
			foreach ($provider->getChildren($share) as $child) {
747
				$this->transferShare($child, $oldOwner, $newOwner, $finalTarget, true);
748
			}
749
			$this->deleteShare($share);
750
		} else {
751
			$sharedWith = $share->getSharedWith();
752
753
			$targetFile = '/' . \rtrim(\basename($finalTarget), '/') . '/' . \ltrim(\basename($share->getTarget()), '/');
754
			/**
755
			 * Scenario where share is made by old owner to a user different
756
			 * from new owner
757
			 */
758
			if (($sharedWith !== null) && ($sharedWith !== $oldOwner) && ($sharedWith !== $newOwner)) {
759
				$sharedBy = $share->getSharedBy();
760
				$sharedOwner = $share->getShareOwner();
761
				//The origin of the share now has to be the destination user.
762
				if ($sharedBy === $oldOwner) {
763
					$share->setSharedBy($newOwner);
764
				}
765
				if ($sharedOwner === $oldOwner) {
766
					$share->setShareOwner($newOwner);
767
				}
768
				if (($sharedBy === $oldOwner) || ($sharedOwner === $oldOwner)) {
769
					$share->setTarget($targetFile);
770
				}
771
			} else {
772
				if ($share->getShareOwner() === $oldOwner) {
773
					$share->setShareOwner($newOwner);
774
				}
775
				if ($share->getSharedBy() === $oldOwner) {
776
					$share->setSharedBy($newOwner);
777
				}
778
			}
779
780
			/**
781
			 * Here we update the target when the share is link
782
			 */
783
			if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) {
784
				$share->setTarget($targetFile);
785
			}
786
787
			$this->updateShare($share);
788
		}
789
	}
790
791
	/**
792
	 * Update a share
793
	 *
794
	 * @param \OCP\Share\IShare $share
795
	 * @return \OCP\Share\IShare The share object
796
	 * @throws \InvalidArgumentException
797
	 */
798
	public function updateShare(\OCP\Share\IShare $share) {
799
		$expirationDateUpdated = false;
800
801
		$this->canShare($share);
802
803
		try {
804
			$originalShare = $this->getShareById($share->getFullId());
805
		} catch (\UnexpectedValueException $e) {
806
			throw new \InvalidArgumentException('Share does not have a full id');
807
		}
808
809
		// We can't change the share type!
810
		if ($share->getShareType() !== $originalShare->getShareType()) {
811
			throw new \InvalidArgumentException('Can\'t change share type');
812
		}
813
814
		// We can only change the recipient on user shares
815
		if ($share->getSharedWith() !== $originalShare->getSharedWith() &&
816
			$share->getShareType() !== \OCP\Share::SHARE_TYPE_USER) {
817
			throw new \InvalidArgumentException('Can only update recipient on user shares');
818
		}
819
820
		// Cannot share with the owner
821 View Code Duplication
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER &&
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
822
			$share->getSharedWith() === $share->getShareOwner()) {
823
			throw new \InvalidArgumentException('Can\'t share with the share owner');
824
		}
825
826
		$this->generalCreateChecks($share);
827
828
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) {
829
			$this->userCreateChecks($share);
830
		} elseif ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
831
			$this->groupCreateChecks($share);
832
		} elseif ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) {
833
			$this->linkCreateChecks($share);
834
835
			// Password updated.
836
			if ($share->getPassword() !== $originalShare->getPassword() ||
837
					$share->getPermissions() !== $originalShare->getPermissions()) {
838
				//Verify the password. Permissions must be taken into account in case the password must be enforced
839
				if ($this->passwordMustBeEnforced($share->getPermissions()) && $share->getPassword() === null) {
840
					throw new \InvalidArgumentException('Passwords are enforced for link shares');
841
				} else {
842
					$this->verifyPassword($share->getPassword());
843
				}
844
845
				// If a password is set. Hash it! (only if the password has changed)
846
				if (($share->getPassword() !== null) &&
847
						($share->getPassword() !== $originalShare->getPassword()) &&
848
						($share->getShouldHashPassword() === true)) {
849
					$share->setPassword($this->hasher->hash($share->getPassword()));
850
				}
851
			}
852
853
			//Verify the expiration date
854
			$this->validateExpirationDate($share);
855
856
			if ($share->getExpirationDate() != $originalShare->getExpirationDate()) {
857
				$expirationDateUpdated = true;
858
			}
859
		}
860
861
		$this->pathCreateChecks($share->getNode());
862
863
		// Now update the share!
864
		$provider = $this->factory->getProviderForType($share->getShareType());
865
		$share = $provider->update($share);
866
867
		$shareAfterUpdateEvent = new GenericEvent(null);
868
		$shareAfterUpdateEvent->setArgument('shareobject', $share);
869
		$update = false;
870 View Code Duplication
		if ($expirationDateUpdated === true) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
871
			\OC_Hook::emit('OCP\Share', 'post_set_expiration_date', [
872
				'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder',
873
				'itemSource' => $share->getNode()->getId(),
874
				'date' => $share->getExpirationDate(),
875
				'uidOwner' => $share->getSharedBy(),
876
			]);
877
			$shareAfterUpdateEvent->setArgument('expirationdateupdated', true);
878
			$shareAfterUpdateEvent->setArgument('oldexpirationdate', $originalShare->getExpirationDate());
879
			$update = true;
880
		}
881
882 View Code Duplication
		if ($share->getPassword() !== $originalShare->getPassword()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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
			\OC_Hook::emit('OCP\Share', 'post_update_password', [
884
				'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder',
885
				'itemSource' => $share->getNode()->getId(),
886
				'uidOwner' => $share->getSharedBy(),
887
				'token' => $share->getToken(),
888
				'disabled' => $share->getPassword() === null,
889
			]);
890
			$shareAfterUpdateEvent->setArgument('passwordupdate', true);
891
			$update = true;
892
		}
893
894
		if ($share->getPermissions() !== $originalShare->getPermissions()) {
895
			if ($this->userManager->userExists($share->getShareOwner())) {
896
				$userFolder = $this->rootFolder->getUserFolder($share->getShareOwner());
897
			} else {
898
				$userFolder = $this->rootFolder->getUserFolder($share->getSharedBy());
899
			}
900
			\OC_Hook::emit('OCP\Share', 'post_update_permissions', [
901
				'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder',
902
				'itemSource' => $share->getNode()->getId(),
903
				'shareType' => $share->getShareType(),
904
				'shareWith' => $share->getSharedWith(),
905
				'uidOwner' => $share->getSharedBy(),
906
				'permissions' => $share->getPermissions(),
907
				'path' => $userFolder->getRelativePath($share->getNode()->getPath()),
908
			]);
909
			$shareAfterUpdateEvent->setArgument('permissionupdate', true);
910
			$shareAfterUpdateEvent->setArgument('oldpermissions', $originalShare->getPermissions());
911
			$shareAfterUpdateEvent->setArgument('path', $userFolder->getRelativePath($share->getNode()->getPath()));
912
			$update = true;
913
		}
914
915
		if ($share->getName() !== $originalShare->getName()) {
916
			$shareAfterUpdateEvent->setArgument('sharenameupdated', true);
917
			$shareAfterUpdateEvent->setArgument('oldname', $originalShare->getName());
918
			$update = true;
919
		}
920
921
		if ($update === true) {
922
			$this->eventDispatcher->dispatch('share.afterupdate', $shareAfterUpdateEvent);
923
		}
924
		return $share;
925
	}
926
927
	/**
928
	 * Delete all the children of this share
929
	 * FIXME: remove once https://github.com/owncloud/core/pull/21660 is in
930
	 *
931
	 * @param \OCP\Share\IShare $share
932
	 * @return \OCP\Share\IShare[] List of deleted shares
933
	 */
934
	protected function deleteChildren(\OCP\Share\IShare $share) {
935
		$deletedShares = [];
936
937
		$provider = $this->factory->getProviderForType($share->getShareType());
938
939
		foreach ($provider->getChildren($share) as $child) {
940
			$deletedChildren = $this->deleteChildren($child);
941
			$deletedShares = \array_merge($deletedShares, $deletedChildren);
942
943
			$provider->delete($child);
944
			$deletedShares[] = $child;
945
		}
946
947
		return $deletedShares;
948
	}
949
950
	protected static function formatUnshareHookParams(\OCP\Share\IShare $share) {
951
		// Prepare hook
952
		$shareType = $share->getShareType();
953
		$sharedWith = '';
954
		if ($shareType === \OCP\Share::SHARE_TYPE_USER) {
955
			$sharedWith = $share->getSharedWith();
956
		} elseif ($shareType === \OCP\Share::SHARE_TYPE_GROUP) {
957
			$sharedWith = $share->getSharedWith();
958
		} elseif ($shareType === \OCP\Share::SHARE_TYPE_REMOTE) {
959
			$sharedWith = $share->getSharedWith();
960
		}
961
962
		$hookParams = [
963
			'id'         => $share->getId(),
964
			'itemType'   => $share->getNodeType(),
965
			'itemSource' => $share->getNodeId(),
966
			'shareType'  => $shareType,
967
			'shareWith'  => $sharedWith,
968
			'itemparent' => \method_exists($share, 'getParent') ? $share->getParent() : '',
969
			'uidOwner'   => $share->getSharedBy(),
970
			'fileSource' => $share->getNodeId(),
971
			'fileTarget' => $share->getTarget()
972
		];
973
		return $hookParams;
974
	}
975
976
	/**
977
	 * Delete a share
978
	 *
979
	 * @param \OCP\Share\IShare $share
980
	 * @throws ShareNotFound
981
	 * @throws \InvalidArgumentException
982
	 */
983
	public function deleteShare(\OCP\Share\IShare $share) {
984
		try {
985
			$share->getFullId();
986
		} catch (\UnexpectedValueException $e) {
987
			throw new \InvalidArgumentException('Share does not have a full id');
988
		}
989
990
		$hookParams = self::formatUnshareHookParams($share);
991
992
		// Emit pre-hook
993
		\OC_Hook::emit('OCP\Share', 'pre_unshare', $hookParams);
994
995
		$beforeEvent = new GenericEvent(null, ['shareData' => $hookParams, 'shareObject' => $share]);
996
		$this->eventDispatcher->dispatch('share.beforeDelete', $beforeEvent);
997
		// Get all children and delete them as well
998
		$deletedShares = $this->deleteChildren($share);
999
1000
		// Do the actual delete
1001
		$provider = $this->factory->getProviderForType($share->getShareType());
1002
		$provider->delete($share);
1003
1004
		// All the deleted shares caused by this delete
1005
		$deletedShares[] = $share;
1006
1007
		//Format hook info
1008
		$formattedDeletedShares = \array_map('self::formatUnshareHookParams', $deletedShares);
1009
1010
		$hookParams['deletedShares'] = $formattedDeletedShares;
1011
1012
		// Emit post hook
1013
		\OC_Hook::emit('OCP\Share', 'post_unshare', $hookParams);
1014
		$afterEvent = new GenericEvent(null, ['shareData' => $hookParams['deletedShares'], 'shareObject' => $share]);
1015
		$this->eventDispatcher->dispatch('share.afterDelete', $afterEvent);
1016
	}
1017
1018
	/**
1019
	 * Unshare a file as the recipient.
1020
	 * This can be different from a regular delete for example when one of
1021
	 * the users in a groups deletes that share. But the provider should
1022
	 * handle this.
1023
	 *
1024
	 * @param \OCP\Share\IShare $share
1025
	 * @param string $recipientId
1026
	 */
1027
	public function deleteFromSelf(\OCP\Share\IShare $share, $recipientId) {
1028
		list($providerId, ) = $this->splitFullId($share->getFullId());
1029
		$provider = $this->factory->getProvider($providerId);
1030
1031
		$provider->deleteFromSelf($share, $recipientId);
1032
1033
		// Emit post hook. The parameter data structure is slightly different
1034
		// from the post_unshare hook to maintain backward compatibility with
1035
		// Share 1.0: the array contains all the key-value pairs from the old
1036
		// library plus some new ones.
1037
		$hookParams = self::formatUnshareHookParams($share);
1038
		$hookParams['itemTarget'] = $hookParams['fileTarget'];
1039
		$hookParams['unsharedItems'] = [$hookParams];
1040
		\OC_Hook::emit('OCP\Share', 'post_unshareFromSelf', $hookParams);
1041
		$event = new GenericEvent(null, [
1042
			'shareRecipient' => $recipientId,
1043
			'shareOwner' => $share->getSharedBy(),
1044
			'recipientPath' => $share->getTarget(),
1045
			'ownerPath' => $share->getNode()->getPath(),
1046
			'nodeType' => $share->getNodeType()]);
1047
		$this->eventDispatcher->dispatch('fromself.unshare', $event);
1048
	}
1049
1050
	/**
1051
	 * @inheritdoc
1052
	 */
1053
	public function moveShare(\OCP\Share\IShare $share, $recipientId) {
1054
		return $this->updateShareForRecipient($share, $recipientId);
1055
	}
1056
1057
	/**
1058
	 * @inheritdoc
1059
	 */
1060
	public function updateShareForRecipient(\OCP\Share\IShare $share, $recipientId) {
1061
		list($providerId, ) = $this->splitFullId($share->getFullId());
1062
		$provider = $this->factory->getProvider($providerId);
1063
1064
		return $provider->updateForRecipient($share, $recipientId);
1065
	}
1066
1067
	/**
1068
	 * @inheritdoc
1069
	 */
1070
	public function getAllSharesBy($userId, $shareTypes, $nodeIDs, $reshares = false) {
1071
		// This function requires at least 1 node (parent folder)
1072
		if (empty($nodeIDs)) {
1073
			throw new \InvalidArgumentException('Array of nodeIDs empty');
1074
		}
1075
		// This will ensure that if there are multiple share providers for the same share type, we will execute it in batches
1076
		$shares = [];
1077
1078
		$providerIdMap = $this->shareTypeToProviderMap($shareTypes);
1079
1080
		$today = new \DateTime();
1081
		foreach ($providerIdMap as $providerId => $shareTypeArray) {
1082
			// Get provider from cache
1083
			$provider = $this->factory->getProvider($providerId);
1084
1085
			$queriedShares = $provider->getAllSharesBy($userId, $shareTypeArray, $nodeIDs, $reshares);
0 ignored issues
show
Documentation introduced by
$shareTypeArray is of type integer, but the function expects a array<integer,integer>.

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Documentation introduced by
$nodeIDs is of type array<integer,integer>, but the function expects a array<integer,object<OCP\Files\Node>>.

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1086
			foreach ($queriedShares as $queriedShare) {
1087
				if ($queriedShare->getShareType() === \OCP\Share::SHARE_TYPE_LINK && $queriedShare->getExpirationDate() !== null &&
1088
					$queriedShare->getExpirationDate() <= $today
1089
				) {
1090
					try {
1091
						$this->deleteShare($queriedShare);
1092
					} catch (NotFoundException $e) {
1093
						//Ignore since this basically means the share is deleted
1094
					}
1095
					continue;
1096
				}
1097
				\array_push($shares, $queriedShare);
1098
			}
1099
		}
1100
1101
		return $shares;
1102
	}
1103
1104
	/**
1105
	 * @inheritdoc
1106
	 */
1107
	public function getSharesBy($userId, $shareType, $path = null, $reshares = false, $limit = 50, $offset = 0) {
1108
		if ($path !== null &&
1109
				!($path instanceof \OCP\Files\File) &&
1110
				!($path instanceof \OCP\Files\Folder)) {
1111
			throw new \InvalidArgumentException('invalid path');
1112
		}
1113
1114
		$provider = $this->factory->getProviderForType($shareType);
1115
1116
		$shares = $provider->getSharesBy($userId, $shareType, $path, $reshares, $limit, $offset);
1117
1118
		/*
1119
		 * Work around so we don't return expired shares but still follow
1120
		 * proper pagination.
1121
		 */
1122
		if ($shareType === \OCP\Share::SHARE_TYPE_LINK) {
1123
			$shares2 = [];
1124
			$today = new \DateTime();
1125
1126
			while (true) {
1127
				$added = 0;
1128
				foreach ($shares as $share) {
1129
					// Check if the share is expired and if so delete it
1130
					if ($share->getExpirationDate() !== null &&
1131
						$share->getExpirationDate() <= $today
1132
					) {
1133
						try {
1134
							$this->deleteShare($share);
1135
						} catch (NotFoundException $e) {
1136
							//Ignore since this basically means the share is deleted
1137
						}
1138
						continue;
1139
					}
1140
					$added++;
1141
					$shares2[] = $share;
1142
1143
					if (\count($shares2) === $limit) {
1144
						break;
1145
					}
1146
				}
1147
1148
				if (\count($shares2) === $limit) {
1149
					break;
1150
				}
1151
1152
				// If there was no limit on the select we are done
1153
				if ($limit === -1) {
1154
					break;
1155
				}
1156
1157
				$offset += $added;
1158
1159
				// Fetch again $limit shares
1160
				$shares = $provider->getSharesBy($userId, $shareType, $path, $reshares, $limit, $offset);
1161
1162
				// No more shares means we are done
1163
				if (empty($shares)) {
1164
					break;
1165
				}
1166
			}
1167
1168
			$shares = $shares2;
1169
		}
1170
1171
		return $shares;
1172
	}
1173
1174
	/**
1175
	 * @inheritdoc
1176
	 */
1177
	public function getSharedWith($userId, $shareType, $node = null, $limit = 50, $offset = 0) {
1178
		$provider = $this->factory->getProviderForType($shareType);
1179
1180
		return $provider->getSharedWith($userId, $shareType, $node, $limit, $offset);
1181
	}
1182
1183
	/**
1184
	 * @inheritdoc
1185
	 */
1186
	public function getAllSharedWith($userId, $shareTypes, $node = null) {
1187
		$shares = [];
1188
		
1189
		// Aggregate all required $shareTypes by mapping provider to supported shareTypes
1190
		$providerIdMap = $this->shareTypeToProviderMap($shareTypes);
1191
		foreach ($providerIdMap as $providerId => $shareTypeArray) {
1192
			// Get provider from cache
1193
			$provider = $this->factory->getProvider($providerId);
1194
			
1195
			// Obtain all shares for all the supported provider types
1196
			$queriedShares = $provider->getAllSharedWith($userId, $node);
1197
			$shares = \array_merge($shares, $queriedShares);
1198
		}
1199
1200
		return $shares;
1201
	}
1202
	
1203
	/**
1204
	 * @inheritdoc
1205
	 */
1206
	public function getShareById($id, $recipient = null) {
1207
		if ($id === null) {
1208
			throw new ShareNotFound();
1209
		}
1210
1211
		list($providerId, $id) = $this->splitFullId($id);
1212
		$provider = $this->factory->getProvider($providerId);
1213
1214
		$share = $provider->getShareById($id, $recipient);
1215
1216
		// Validate link shares expiration date
1217
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK &&
1218
			$share->getExpirationDate() !== null &&
1219
			$share->getExpirationDate() <= new \DateTime()) {
1220
			$this->deleteShare($share);
1221
			throw new ShareNotFound();
1222
		}
1223
1224
		return $share;
1225
	}
1226
1227
	/**
1228
	 * Get all the shares for a given path
1229
	 *
1230
	 * @param \OCP\Files\Node $path
1231
	 * @param int $page
1232
	 * @param int $perPage
1233
	 *
1234
	 * @return Share[]
1235
	 */
1236
	public function getSharesByPath(\OCP\Files\Node $path, $page=0, $perPage=50) {
0 ignored issues
show
Unused Code introduced by
The parameter $page is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $perPage is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1237
		$types = [\OCP\Share::SHARE_TYPE_USER, \OCP\Share::SHARE_TYPE_GROUP];
1238
		$providers = [];
1239
		$results = [];
1240
1241
		foreach ($types as $type) {
1242
			$provider = $this->factory->getProviderForType($type);
1243
			// store this way to deduplicate entries by id
1244
			$providers[$provider->identifier()] = $provider;
1245
		}
1246
1247
		foreach ($providers as $provider) {
1248
			$results = \array_merge($results, $provider->getSharesByPath($path));
1249
		}
1250
1251
		return $results;
1252
	}
1253
1254
	/**
1255
	 * Get the share by token possible with password
1256
	 *
1257
	 * @param string $token
1258
	 * @return Share
1259
	 *
1260
	 * @throws ShareNotFound
1261
	 */
1262
	public function getShareByToken($token) {
1263
		$provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_LINK);
1264
1265
		try {
1266
			$share = $provider->getShareByToken($token);
1267
		} catch (ShareNotFound $e) {
1268
			$share = null;
1269
		}
1270
1271
		// If it is not a link share try to fetch a federated share by token
1272
		if ($share === null) {
1273
			$provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_REMOTE);
1274
			$share = $provider->getShareByToken($token);
1275
		}
1276
1277
		if ($share->getExpirationDate() !== null &&
1278
			$share->getExpirationDate() <= new \DateTime()) {
1279
			$this->deleteShare($share);
1280
			throw new ShareNotFound();
1281
		}
1282
1283
		/*
1284
		 * Reduce the permissions for link shares if public upload is not enabled
1285
		 */
1286
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK &&
1287
			!$this->shareApiLinkAllowPublicUpload()) {
1288
			$share->setPermissions($share->getPermissions() & ~(\OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE));
1289
		}
1290
1291
		return $share;
1292
	}
1293
1294
	/**
1295
	 * Verify the password of a public share
1296
	 *
1297
	 * @param \OCP\Share\IShare $share
1298
	 * @param string $password
1299
	 * @return bool
1300
	 */
1301
	public function checkPassword(\OCP\Share\IShare $share, $password) {
1302
		if ($share->getShareType() !== \OCP\Share::SHARE_TYPE_LINK) {
1303
			//TODO maybe exception?
1304
			return false;
1305
		}
1306
1307
		if ($password === null || $share->getPassword() === null) {
1308
			return false;
1309
		}
1310
1311
		$newHash = '';
1312
		if (!$this->hasher->verify($password, $share->getPassword(), $newHash)) {
1313
			return false;
1314
		}
1315
1316
		if (!empty($newHash)) {
1317
			$share->setPassword($newHash);
1318
			$provider = $this->factory->getProviderForType($share->getShareType());
1319
			$provider->update($share);
1320
		}
1321
1322
		return true;
1323
	}
1324
1325
	/**
1326
	 * @inheritdoc
1327
	 */
1328
	public function userDeleted($uid) {
1329
		$types = [\OCP\Share::SHARE_TYPE_USER, \OCP\Share::SHARE_TYPE_GROUP, \OCP\Share::SHARE_TYPE_LINK, \OCP\Share::SHARE_TYPE_REMOTE];
1330
1331
		foreach ($types as $type) {
1332
			$provider = $this->factory->getProviderForType($type);
1333
			$provider->userDeleted($uid, $type);
1334
		}
1335
	}
1336
1337
	/**
1338
	 * @inheritdoc
1339
	 */
1340
	public function groupDeleted($gid) {
1341
		$provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_GROUP);
1342
		$provider->groupDeleted($gid);
1343
	}
1344
1345
	/**
1346
	 * @inheritdoc
1347
	 */
1348
	public function userDeletedFromGroup($uid, $gid) {
1349
		$provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_GROUP);
1350
		$provider->userDeletedFromGroup($uid, $gid);
1351
	}
1352
1353
	/**
1354
	 * Get access list to a path. This means
1355
	 * all the users and groups that can access a given path.
1356
	 *
1357
	 * Consider:
1358
	 * -root
1359
	 * |-folder1
1360
	 *  |-folder2
1361
	 *   |-fileA
1362
	 *
1363
	 * fileA is shared with user1
1364
	 * folder2 is shared with group2
1365
	 * folder1 is shared with user2
1366
	 *
1367
	 * Then the access list will to '/folder1/folder2/fileA' is:
1368
	 * [
1369
	 * 	'users' => ['user1', 'user2'],
1370
	 *  'groups' => ['group2']
1371
	 * ]
1372
	 *
1373
	 * This is required for encryption
1374
	 *
1375
	 * @param \OCP\Files\Node $path
1376
	 */
1377
	public function getAccessList(\OCP\Files\Node $path) {
0 ignored issues
show
Unused Code introduced by
The parameter $path is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1378
	}
1379
1380
	/**
1381
	 * Create a new share
1382
	 * @return \OCP\Share\IShare;
0 ignored issues
show
Documentation introduced by
The doc-type \OCP\Share\IShare; could not be parsed: Expected "|" or "end of type", but got ";" at position 17. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
1383
	 */
1384
	public function newShare() {
1385
		return new \OC\Share20\Share($this->rootFolder, $this->userManager);
1386
	}
1387
1388
	/**
1389
	 * Is the share API enabled
1390
	 *
1391
	 * @return bool
1392
	 */
1393
	public function shareApiEnabled() {
1394
		return $this->config->getAppValue('core', 'shareapi_enabled', 'yes') === 'yes';
1395
	}
1396
1397
	/**
1398
	 * Is public link sharing enabled
1399
	 *
1400
	 * @return bool
1401
	 */
1402
	public function shareApiAllowLinks() {
1403
		return $this->config->getAppValue('core', 'shareapi_allow_links', 'yes') === 'yes';
1404
	}
1405
1406
	/**
1407
	 * Is password on public link requires (fallback to shareApiLinkEnforcePasswordReadOnly)
1408
	 *
1409
	 * @return bool
1410
	 */
1411
	public function shareApiLinkEnforcePassword() {
1412
		return $this->shareApiLinkEnforcePasswordReadOnly();
1413
	}
1414
1415
	/**
1416
	 * Is password enforced for read-only shares?
1417
	 *
1418
	 * @return bool
1419
	 */
1420
	public function shareApiLinkEnforcePasswordReadOnly() {
1421
		return $this->config->getAppValue('core', 'shareapi_enforce_links_password_read_only', 'no') === 'yes';
1422
	}
1423
1424
	/**
1425
	 * Is password enforced for read & write shares?
1426
	 *
1427
	 * @return bool
1428
	 */
1429
	public function shareApiLinkEnforcePasswordReadWrite() {
1430
		return $this->config->getAppValue('core', 'shareapi_enforce_links_password_read_write', 'no') === 'yes';
1431
	}
1432
1433
	/**
1434
	 * Is password enforced for write-only shares?
1435
	 *
1436
	 * @return bool
1437
	 */
1438
	public function shareApiLinkEnforcePasswordWriteOnly() {
1439
		return $this->config->getAppValue('core', 'shareapi_enforce_links_password_write_only', 'no') === 'yes';
1440
	}
1441
1442
	/**
1443
	 * Is default expire date enabled
1444
	 *
1445
	 * @return bool
1446
	 */
1447
	public function shareApiLinkDefaultExpireDate() {
1448
		return $this->config->getAppValue('core', 'shareapi_default_expire_date', 'no') === 'yes';
1449
	}
1450
1451
	/**
1452
	 * Is default expire date enforced
1453
	 *`
1454
	 * @return bool
1455
	 */
1456
	public function shareApiLinkDefaultExpireDateEnforced() {
1457
		return $this->shareApiLinkDefaultExpireDate() &&
1458
			$this->config->getAppValue('core', 'shareapi_enforce_expire_date', 'no') === 'yes';
1459
	}
1460
1461
	/**
1462
	 * Number of default expire days
1463
	 *shareApiLinkAllowPublicUpload
1464
	 * @return int
1465
	 */
1466
	public function shareApiLinkDefaultExpireDays() {
1467
		return (int)$this->config->getAppValue('core', 'shareapi_expire_after_n_days', '7');
1468
	}
1469
1470
	/**
1471
	 * Allow public upload on link shares
1472
	 *
1473
	 * @return bool
1474
	 */
1475
	public function shareApiLinkAllowPublicUpload() {
1476
		return $this->config->getAppValue('core', 'shareapi_allow_public_upload', 'yes') === 'yes';
1477
	}
1478
1479
	/**
1480
	 * check if user can only share with group members
1481
	 * @return bool
1482
	 */
1483
	public function shareWithGroupMembersOnly() {
1484
		return $this->config->getAppValue('core', 'shareapi_only_share_with_group_members', 'no') === 'yes';
1485
	}
1486
1487
	/**
1488
	 * check if user can only share with groups he's member of
1489
	 * @return bool
1490
	 */
1491
	public function shareWithMembershipGroupOnly() {
1492
		return $this->config->getAppValue('core', 'shareapi_only_share_with_membership_groups', 'no') === 'yes';
1493
	}
1494
1495
	/**
1496
	 * Check if users can share with groups
1497
	 * @return bool
1498
	 */
1499
	public function allowGroupSharing() {
1500
		return $this->config->getAppValue('core', 'shareapi_allow_group_sharing', 'yes') === 'yes';
1501
	}
1502
1503
	/**
1504
	 * Copied from \OC_Util::isSharingDisabledForUser
1505
	 *
1506
	 * @param string $userId
1507
	 * @return bool
1508
	 */
1509
	public function sharingDisabledForUser($userId) {
1510
		if ($userId === null) {
1511
			return false;
1512
		}
1513
1514
		if (isset($this->sharingDisabledForUsersCache[$userId])) {
1515
			return $this->sharingDisabledForUsersCache[$userId];
1516
		}
1517
1518
		if ($this->config->getAppValue('core', 'shareapi_exclude_groups', 'no') === 'yes') {
1519
			$groupsList = $this->config->getAppValue('core', 'shareapi_exclude_groups_list', '');
1520
			$excludedGroups = \json_decode($groupsList);
1521
			if ($excludedGroups === null) {
1522
				$excludedGroups = \explode(',', $groupsList);
1523
				$newValue = \json_encode($excludedGroups);
1524
				$this->config->setAppValue('core', 'shareapi_exclude_groups_list', $newValue);
1525
			}
1526
			$user = $this->userManager->get($userId);
1527
			$usersGroups = $this->groupManager->getUserGroupIds($user);
0 ignored issues
show
Bug introduced by
It seems like $user defined by $this->userManager->get($userId) on line 1526 can be null; however, OCP\IGroupManager::getUserGroupIds() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
1528
			$matchingGroups = \array_intersect($usersGroups, $excludedGroups);
1529
			if (!empty($matchingGroups)) {
1530
				// If the user is a member of any of the excluded groups they cannot use sharing
1531
				$this->sharingDisabledForUsersCache[$userId] = true;
1532
				return true;
1533
			}
1534
		}
1535
1536
		$this->sharingDisabledForUsersCache[$userId] = false;
1537
		return false;
1538
	}
1539
1540
	/**
1541
	 * @inheritdoc
1542
	 */
1543
	public function outgoingServer2ServerSharesAllowed() {
1544
		return $this->config->getAppValue('files_sharing', 'outgoing_server2server_share_enabled', 'yes') === 'yes';
1545
	}
1546
}
1547