Completed
Push — stable9 ( 212ff8...07c437 )
by Morris
26:13 queued 09:36
created

Manager::shareApiEnabled()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * @author Arthur Schiwon <[email protected]>
4
 * @author Joas Schilling <[email protected]>
5
 * @author Roeland Jago Douma <[email protected]>
6
 *
7
 * @copyright Copyright (c) 2016, ownCloud, Inc.
8
 * @license AGPL-3.0
9
 *
10
 * This code is free software: you can redistribute it and/or modify
11
 * it under the terms of the GNU Affero General Public License, version 3,
12
 * as published by the Free Software Foundation.
13
 *
14
 * This program is distributed in the hope that it will be useful,
15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
 * GNU Affero General Public License for more details.
18
 *
19
 * You should have received a copy of the GNU Affero General Public License, version 3,
20
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
21
 *
22
 */
23
24
namespace OC\Share20;
25
26
use OC\Files\Mount\MoveableMount;
27
use OC\HintException;
28
use OCP\Files\IRootFolder;
29
use OCP\Files\NotFoundException;
30
use OCP\IUserManager;
31
use OCP\Share\IManager;
32
use OCP\Share\IProviderFactory;
33
use OC\Share20\Exception\BackendError;
34
use OCP\IConfig;
35
use OCP\IL10N;
36
use OCP\ILogger;
37
use OCP\Security\ISecureRandom;
38
use OCP\Security\IHasher;
39
use OCP\Files\Mount\IMountManager;
40
use OCP\IGroupManager;
41
use OCP\Files\File;
42
use OCP\Files\Folder;
43
44
use OCP\Share\Exceptions\ShareNotFound;
45
use OCP\Share\Exceptions\GenericShareException;
46
use Symfony\Component\EventDispatcher\EventDispatcher;
47
use Symfony\Component\EventDispatcher\GenericEvent;
48
49
/**
50
 * This class is the communication hub for all sharing related operations.
51
 */
52
class Manager implements IManager {
53
54
	/** @var IProviderFactory */
55
	private $factory;
56
	/** @var ILogger */
57
	private $logger;
58
	/** @var IConfig */
59
	private $config;
60
	/** @var ISecureRandom */
61
	private $secureRandom;
62
	/** @var IHasher */
63
	private $hasher;
64
	/** @var IMountManager */
65
	private $mountManager;
66
	/** @var IGroupManager */
67
	private $groupManager;
68
	/** @var IL10N */
69
	private $l;
70
	/** @var IUserManager */
71
	private $userManager;
72
	/** @var IRootFolder */
73
	private $rootFolder;
74
75
	/**
76
	 * Manager constructor.
77
	 *
78
	 * @param ILogger $logger
79
	 * @param IConfig $config
80
	 * @param ISecureRandom $secureRandom
81
	 * @param IHasher $hasher
82
	 * @param IMountManager $mountManager
83
	 * @param IGroupManager $groupManager
84
	 * @param IL10N $l
85
	 * @param IProviderFactory $factory
86
	 * @param IUserManager $userManager
87
	 * @param IRootFolder $rootFolder
88
	 * @param EventDispatcher $eventDispatcher
89
	 */
90
	public function __construct(
91
			ILogger $logger,
92
			IConfig $config,
93
			ISecureRandom $secureRandom,
94
			IHasher $hasher,
95
			IMountManager $mountManager,
96
			IGroupManager $groupManager,
97
			IL10N $l,
98
			IProviderFactory $factory,
99
			IUserManager $userManager,
100
			IRootFolder $rootFolder,
101
			EventDispatcher $eventDispatcher
102
	) {
103
		$this->logger = $logger;
104
		$this->config = $config;
105
		$this->secureRandom = $secureRandom;
106
		$this->hasher = $hasher;
107
		$this->mountManager = $mountManager;
108
		$this->groupManager = $groupManager;
109
		$this->l = $l;
110
		$this->factory = $factory;
111
		$this->userManager = $userManager;
112
		$this->rootFolder = $rootFolder;
113
		$this->eventDispatcher = $eventDispatcher;
0 ignored issues
show
Bug introduced by
The property eventDispatcher does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
114
	}
115
116
	/**
117
	 * Convert from a full share id to a tuple (providerId, shareId)
118
	 *
119
	 * @param string $id
120
	 * @return string[]
121
	 */
122
	private function splitFullId($id) {
123
		return explode(':', $id, 2);
124
	}
125
126
	/**
127
	 * Verify if a password meets all requirements
128
	 *
129
	 * @param string $password
130
	 * @throws \Exception
131
	 */
132
	protected function verifyPassword($password) {
133
		if ($password === null) {
134
			// No password is set, check if this is allowed.
135
			if ($this->shareApiLinkEnforcePassword()) {
136
				throw new \InvalidArgumentException('Passwords are enforced for link shares');
137
			}
138
139
			return;
140
		}
141
142
		// Let others verify the password
143
		try {
144
			$event = new GenericEvent($password);
145
			$this->eventDispatcher->dispatch('OCP\PasswordPolicy::validate', $event);
146
		} catch (HintException $e) {
147
			throw new \Exception($e->getHint());
148
		}
149
	}
150
151
	/**
152
	 * Check for generic requirements before creating a share
153
	 *
154
	 * @param \OCP\Share\IShare $share
155
	 * @throws \InvalidArgumentException
156
	 * @throws GenericShareException
157
	 */
158
	protected function generalCreateChecks(\OCP\Share\IShare $share) {
159
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) {
160
			// We expect a valid user as sharedWith for user shares
161
			if (!$this->userManager->userExists($share->getSharedWith())) {
162
				throw new \InvalidArgumentException('SharedWith is not a valid user');
163
			}
164
		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
165
			// We expect a valid group as sharedWith for group shares
166
			if (!$this->groupManager->groupExists($share->getSharedWith())) {
167
				throw new \InvalidArgumentException('SharedWith is not a valid group');
168
			}
169
		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) {
170
			if ($share->getSharedWith() !== null) {
171
				throw new \InvalidArgumentException('SharedWith should be empty');
172
			}
173
		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_REMOTE) {
174
			if ($share->getSharedWith() === null) {
175
				throw new \InvalidArgumentException('SharedWith should not be empty');
176
			}
177
		} else {
178
			// We can't handle other types yet
179
			throw new \InvalidArgumentException('unkown share type');
180
		}
181
182
		// Verify the initiator of the share is set
183
		if ($share->getSharedBy() === null) {
184
			throw new \InvalidArgumentException('SharedBy should be set');
185
		}
186
187
		// Cannot share with yourself
188 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...
189
			$share->getSharedWith() === $share->getSharedBy()) {
190
			throw new \InvalidArgumentException('Can\'t share with yourself');
191
		}
192
193
		// The path should be set
194
		if ($share->getNode() === null) {
195
			throw new \InvalidArgumentException('Path should be set');
196
		}
197
198
		// And it should be a file or a folder
199
		if (!($share->getNode() instanceof \OCP\Files\File) &&
200
				!($share->getNode() instanceof \OCP\Files\Folder)) {
201
			throw new \InvalidArgumentException('Path should be either a file or a folder');
202
		}
203
204
		// And you can't share your rootfolder
205
		if ($this->rootFolder->getUserFolder($share->getSharedBy())->getPath() === $share->getNode()->getPath()) {
206
			throw new \InvalidArgumentException('You can\'t share your root folder');
207
		}
208
209
		// Check if we actually have share permissions
210 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...
211
			$message_t = $this->l->t('You are not allowed to share %s', [$share->getNode()->getPath()]);
212
			throw new GenericShareException($message_t, $message_t, 404);
213
		}
214
215
		// Permissions should be set
216
		if ($share->getPermissions() === null) {
217
			throw new \InvalidArgumentException('A share requires permissions');
218
		}
219
220
		/*
221
		 * Quick fix for #23536
222
		 * Non moveable mount points do not have update and delete permissions
223
		 * while we 'most likely' do have that on the storage.
224
		 */
225
		$permissions = $share->getNode()->getPermissions();
226
		$mount = $share->getNode()->getMountPoint();
227
		if (!($mount instanceof MoveableMount)) {
228
			$permissions |= \OCP\Constants::PERMISSION_DELETE | \OCP\Constants::PERMISSION_UPDATE;
229
		}
230
231
		// Check that we do not share with more permissions than we have
232 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...
233
			$message_t = $this->l->t('Cannot increase permissions of %s', [$share->getNode()->getPath()]);
234
			throw new GenericShareException($message_t, $message_t, 404);
235
		}
236
237
		// Link shares are allowed to have no read permissions to allow upload to hidden folders
238
		if ($share->getShareType() !== \OCP\Share::SHARE_TYPE_LINK &&
239
			($share->getPermissions() & \OCP\Constants::PERMISSION_READ) === 0) {
240
			throw new \InvalidArgumentException('Shares need at least read permissions');
241
		}
242
	}
243
244
	/**
245
	 * Validate if the expiration date fits the system settings
246
	 *
247
	 * @param \OCP\Share\IShare $share The share to validate the expiration date of
248
	 * @return \OCP\Share\IShare The modified share object
249
	 * @throws GenericShareException
250
	 * @throws \InvalidArgumentException
251
	 * @throws \Exception
252
	 */
253
	protected function validateExpirationDate(\OCP\Share\IShare $share) {
254
255
		$expirationDate = $share->getExpirationDate();
256
257
		if ($expirationDate !== null) {
258
			//Make sure the expiration date is a date
259
			$expirationDate->setTime(0, 0, 0);
260
261
			$date = new \DateTime();
262
			$date->setTime(0, 0, 0);
263 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...
264
				$message = $this->l->t('Expiration date is in the past');
265
				throw new GenericShareException($message, $message, 404);
266
			}
267
		}
268
269
		// If expiredate is empty set a default one if there is a default
270
		$fullId = null;
271
		try {
272
			$fullId = $share->getFullId();
273
		} catch (\UnexpectedValueException $e) {
274
			// This is a new share
275
		}
276
277
		if ($fullId === null && $expirationDate === null && $this->shareApiLinkDefaultExpireDate()) {
278
			$expirationDate = new \DateTime();
279
			$expirationDate->setTime(0,0,0);
280
			$expirationDate->add(new \DateInterval('P'.$this->shareApiLinkDefaultExpireDays().'D'));
281
		}
282
283
		// If we enforce the expiration date check that is does not exceed
284
		if ($this->shareApiLinkDefaultExpireDateEnforced()) {
285
			if ($expirationDate === null) {
286
				throw new \InvalidArgumentException('Expiration date is enforced');
287
			}
288
289
			$date = new \DateTime();
290
			$date->setTime(0, 0, 0);
291
			$date->add(new \DateInterval('P' . $this->shareApiLinkDefaultExpireDays() . 'D'));
292 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...
293
				$message = $this->l->t('Cannot set expiration date more than %s days in the future', [$this->shareApiLinkDefaultExpireDays()]);
294
				throw new GenericShareException($message, $message, 404);
295
			}
296
		}
297
298
		$accepted = true;
299
		$message = '';
300
		\OCP\Util::emitHook('\OC\Share', 'verifyExpirationDate', [
301
			'expirationDate' => &$expirationDate,
302
			'accepted' => &$accepted,
303
			'message' => &$message,
304
			'passwordSet' => $share->getPassword() !== null,
305
		]);
306
307
		if (!$accepted) {
308
			throw new \Exception($message);
309
		}
310
311
		$share->setExpirationDate($expirationDate);
312
313
		return $share;
314
	}
315
316
	/**
317
	 * Check for pre share requirements for user shares
318
	 *
319
	 * @param \OCP\Share\IShare $share
320
	 * @throws \Exception
321
	 */
322
	protected function userCreateChecks(\OCP\Share\IShare $share) {
323
		// Check if we can share with group members only
324
		if ($this->shareWithGroupMembersOnly()) {
325
			$sharedBy = $this->userManager->get($share->getSharedBy());
326
			$sharedWith = $this->userManager->get($share->getSharedWith());
327
			// Verify we can share with this user
328
			$groups = array_intersect(
329
					$this->groupManager->getUserGroupIds($sharedBy),
0 ignored issues
show
Bug introduced by
It seems like $sharedBy defined by $this->userManager->get($share->getSharedBy()) on line 325 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...
330
					$this->groupManager->getUserGroupIds($sharedWith)
0 ignored issues
show
Bug introduced by
It seems like $sharedWith defined by $this->userManager->get($share->getSharedWith()) on line 326 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...
331
			);
332
			if (empty($groups)) {
333
				throw new \Exception('Only sharing with group members is allowed');
334
			}
335
		}
336
337
		/*
338
		 * TODO: Could be costly, fix
339
		 *
340
		 * Also this is not what we want in the future.. then we want to squash identical shares.
341
		 */
342
		$provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_USER);
343
		$existingShares = $provider->getSharesByPath($share->getNode());
344
		foreach($existingShares as $existingShare) {
345
			// Ignore if it is the same share
346
			try {
347
				if ($existingShare->getFullId() === $share->getFullId()) {
348
					continue;
349
				}
350
			} catch (\UnexpectedValueException $e) {
351
				//Shares are not identical
352
			}
353
354
			// Identical share already existst
355
			if ($existingShare->getSharedWith() === $share->getSharedWith()) {
356
				throw new \Exception('Path already shared with this user');
357
			}
358
359
			// The share is already shared with this user via a group share
360
			if ($existingShare->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
361
				$group = $this->groupManager->get($existingShare->getSharedWith());
362
				$user = $this->userManager->get($share->getSharedWith());
363
364
				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 362 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...
365
					throw new \Exception('Path already shared with this user');
366
				}
367
			}
368
		}
369
	}
370
371
	/**
372
	 * Check for pre share requirements for group shares
373
	 *
374
	 * @param \OCP\Share\IShare $share
375
	 * @throws \Exception
376
	 */
377
	protected function groupCreateChecks(\OCP\Share\IShare $share) {
378
		// Verify group shares are allowed
379
		if (!$this->allowGroupSharing()) {
380
			throw new \Exception('Group sharing is now allowed');
381
		}
382
383
		// Verify if the user can share with this group
384
		if ($this->shareWithGroupMembersOnly()) {
385
			$sharedBy = $this->userManager->get($share->getSharedBy());
386
			$sharedWith = $this->groupManager->get($share->getSharedWith());
387
			if (!$sharedWith->inGroup($sharedBy)) {
0 ignored issues
show
Bug introduced by
It seems like $sharedBy defined by $this->userManager->get($share->getSharedBy()) on line 385 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...
388
				throw new \Exception('Only sharing within your own groups is allowed');
389
			}
390
		}
391
392
		/*
393
		 * TODO: Could be costly, fix
394
		 *
395
		 * Also this is not what we want in the future.. then we want to squash identical shares.
396
		 */
397
		$provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_GROUP);
398
		$existingShares = $provider->getSharesByPath($share->getNode());
399
		foreach($existingShares as $existingShare) {
400
			try {
401
				if ($existingShare->getFullId() === $share->getFullId()) {
402
					continue;
403
				}
404
			} catch (\UnexpectedValueException $e) {
405
				//It is a new share so just continue
406
			}
407
408
			if ($existingShare->getSharedWith() === $share->getSharedWith()) {
409
				throw new \Exception('Path already shared with this group');
410
			}
411
		}
412
	}
413
414
	/**
415
	 * Check for pre share requirements for link shares
416
	 *
417
	 * @param \OCP\Share\IShare $share
418
	 * @throws \Exception
419
	 */
420
	protected function linkCreateChecks(\OCP\Share\IShare $share) {
421
		// Are link shares allowed?
422
		if (!$this->shareApiAllowLinks()) {
423
			throw new \Exception('Link sharing not allowed');
424
		}
425
426
		// Link shares by definition can't have share permissions
427
		if ($share->getPermissions() & \OCP\Constants::PERMISSION_SHARE) {
428
			throw new \InvalidArgumentException('Link shares can\'t have reshare permissions');
429
		}
430
431
		// We don't allow deletion on link shares
432
		if ($share->getPermissions() & \OCP\Constants::PERMISSION_DELETE) {
433
			throw new \InvalidArgumentException('Link shares can\'t have delete permissions');
434
		}
435
436
		// Check if public upload is allowed
437
		if (!$this->shareApiLinkAllowPublicUpload() &&
438
			($share->getPermissions() & (\OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE))) {
439
			throw new \InvalidArgumentException('Public upload not allowed');
440
		}
441
	}
442
443
	/**
444
	 * To make sure we don't get invisible link shares we set the parent
445
	 * of a link if it is a reshare. This is a quick word around
446
	 * until we can properly display multiple link shares in the UI
447
	 *
448
	 * See: https://github.com/owncloud/core/issues/22295
449
	 *
450
	 * FIXME: Remove once multiple link shares can be properly displayed
451
	 *
452
	 * @param \OCP\Share\IShare $share
453
	 */
454
	protected function setLinkParent(\OCP\Share\IShare $share) {
455
456
		// No sense in checking if the method is not there.
457
		if (method_exists($share, 'setParent')) {
458
			$storage = $share->getNode()->getStorage();
459
			if ($storage->instanceOfStorage('\OCA\Files_Sharing\ISharedStorage')) {
460
				$share->setParent($storage->getShareId());
461
			}
462
		};
463
	}
464
465
	/**
466
	 * @param File|Folder $path
467
	 */
468
	protected function pathCreateChecks($path) {
469
		// Make sure that we do not share a path that contains a shared mountpoint
470
		if ($path instanceof \OCP\Files\Folder) {
471
			$mounts = $this->mountManager->findIn($path->getPath());
472
			foreach($mounts as $mount) {
473
				if ($mount->getStorage()->instanceOfStorage('\OCA\Files_Sharing\ISharedStorage')) {
474
					throw new \InvalidArgumentException('Path contains files shared with you');
475
				}
476
			}
477
		}
478
	}
479
480
	/**
481
	 * Check if the user that is sharing can actually share
482
	 *
483
	 * @param \OCP\Share\IShare $share
484
	 * @throws \Exception
485
	 */
486
	protected function canShare(\OCP\Share\IShare $share) {
487
		if (!$this->shareApiEnabled()) {
488
			throw new \Exception('The share API is disabled');
489
		}
490
491
		if ($this->sharingDisabledForUser($share->getSharedBy())) {
492
			throw new \Exception('You are not allowed to share');
493
		}
494
	}
495
496
	/**
497
	 * Share a path
498
	 *
499
	 * @param \OCP\Share\IShare $share
500
	 * @return Share The share object
501
	 * @throws \Exception
502
	 *
503
	 * TODO: handle link share permissions or check them
504
	 */
505
	public function createShare(\OCP\Share\IShare $share) {
506
		$this->canShare($share);
507
508
		$this->generalCreateChecks($share);
509
510
		//Verify share type
511
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) {
512
			$this->userCreateChecks($share);
513
		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
514
			$this->groupCreateChecks($share);
515
		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) {
516
			$this->linkCreateChecks($share);
517
			$this->setLinkParent($share);
518
519
			/*
520
			 * For now ignore a set token.
521
			 */
522
			$share->setToken(
523
				$this->secureRandom->generate(
524
					\OC\Share\Constants::TOKEN_LENGTH,
525
					\OCP\Security\ISecureRandom::CHAR_LOWER.
526
					\OCP\Security\ISecureRandom::CHAR_UPPER.
527
					\OCP\Security\ISecureRandom::CHAR_DIGITS
528
				)
529
			);
530
531
			//Verify the expiration date
532
			$this->validateExpirationDate($share);
533
534
			//Verify the password
535
			$this->verifyPassword($share->getPassword());
536
537
			// If a password is set. Hash it!
538
			if ($share->getPassword() !== null) {
539
				$share->setPassword($this->hasher->hash($share->getPassword()));
540
			}
541
		}
542
543
		// Verify if there are any issues with the path
544
		$this->pathCreateChecks($share->getNode());
545
546
		/*
547
		 * On creation of a share the owner is always the owner of the path
548
		 * Except for mounted federated shares.
549
		 */
550
		$storage = $share->getNode()->getStorage();
551
		if ($storage->instanceOfStorage('OCA\Files_Sharing\External\Storage')) {
552
			$parent = $share->getNode()->getParent();
553
			while($parent->getStorage()->instanceOfStorage('OCA\Files_Sharing\External\Storage')) {
554
				$parent = $parent->getParent();
555
			}
556
			$share->setShareOwner($parent->getOwner()->getUID());
557
		} else {
558
			$share->setShareOwner($share->getNode()->getOwner()->getUID());
559
		}
560
561
		// Cannot share with the owner
562 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...
563
			$share->getSharedWith() === $share->getShareOwner()) {
564
			throw new \InvalidArgumentException('Can\'t share with the share owner');
565
		}
566
567
		// Generate the target
568
		$target = $this->config->getSystemValue('share_folder', '/') .'/'. $share->getNode()->getName();
569
		$target = \OC\Files\Filesystem::normalizePath($target);
570
		$share->setTarget($target);
571
572
		// Pre share hook
573
		$run = true;
574
		$error = '';
575
		$preHookData = [
576
			'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder',
577
			'itemSource' => $share->getNode()->getId(),
578
			'shareType' => $share->getShareType(),
579
			'uidOwner' => $share->getSharedBy(),
580
			'permissions' => $share->getPermissions(),
581
			'fileSource' => $share->getNode()->getId(),
582
			'expiration' => $share->getExpirationDate(),
583
			'token' => $share->getToken(),
584
			'itemTarget' => $share->getTarget(),
585
			'shareWith' => $share->getSharedWith(),
586
			'run' => &$run,
587
			'error' => &$error,
588
		];
589
		\OC_Hook::emit('OCP\Share', 'pre_shared', $preHookData);
590
591
		if ($run === false) {
592
			throw new \Exception($error);
593
		}
594
595
		$provider = $this->factory->getProviderForType($share->getShareType());
596
		$share = $provider->create($share);
597
598
		// Post share hook
599
		$postHookData = [
600
			'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder',
601
			'itemSource' => $share->getNode()->getId(),
602
			'shareType' => $share->getShareType(),
603
			'uidOwner' => $share->getSharedBy(),
604
			'permissions' => $share->getPermissions(),
605
			'fileSource' => $share->getNode()->getId(),
606
			'expiration' => $share->getExpirationDate(),
607
			'token' => $share->getToken(),
608
			'id' => $share->getId(),
609
			'shareWith' => $share->getSharedWith(),
610
			'itemTarget' => $share->getTarget(),
611
			'fileTarget' => $share->getTarget(),
612
		];
613
614
		\OC_Hook::emit('OCP\Share', 'post_shared', $postHookData);
615
616
		return $share;
617
	}
618
619
	/**
620
	 * Update a share
621
	 *
622
	 * @param \OCP\Share\IShare $share
623
	 * @return \OCP\Share\IShare The share object
624
	 * @throws \InvalidArgumentException
625
	 */
626
	public function updateShare(\OCP\Share\IShare $share) {
627
		$expirationDateUpdated = false;
628
629
		$this->canShare($share);
630
631
		try {
632
			$originalShare = $this->getShareById($share->getFullId());
633
		} catch (\UnexpectedValueException $e) {
634
			throw new \InvalidArgumentException('Share does not have a full id');
635
		}
636
637
		// We can't change the share type!
638
		if ($share->getShareType() !== $originalShare->getShareType()) {
639
			throw new \InvalidArgumentException('Can\'t change share type');
640
		}
641
642
		// We can only change the recipient on user shares
643
		if ($share->getSharedWith() !== $originalShare->getSharedWith() &&
644
		    $share->getShareType() !== \OCP\Share::SHARE_TYPE_USER) {
645
			throw new \InvalidArgumentException('Can only update recipient on user shares');
646
		}
647
648
		// Cannot share with the owner
649 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...
650
			$share->getSharedWith() === $share->getShareOwner()) {
651
			throw new \InvalidArgumentException('Can\'t share with the share owner');
652
		}
653
654
		$this->generalCreateChecks($share);
655
656
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) {
657
			$this->userCreateChecks($share);
658
		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
659
			$this->groupCreateChecks($share);
660
		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) {
661
			$this->linkCreateChecks($share);
662
663
			// Password updated.
664
			if ($share->getPassword() !== $originalShare->getPassword()) {
665
				//Verify the password
666
				$this->verifyPassword($share->getPassword());
667
668
				// If a password is set. Hash it!
669
				if ($share->getPassword() !== null) {
670
					$share->setPassword($this->hasher->hash($share->getPassword()));
671
				}
672
			}
673
674
			if ($share->getExpirationDate() != $originalShare->getExpirationDate()) {
675
				//Verify the expiration date
676
				$this->validateExpirationDate($share);
677
				$expirationDateUpdated = true;
678
			}
679
		}
680
681
		$this->pathCreateChecks($share->getNode());
682
683
		// Now update the share!
684
		$provider = $this->factory->getProviderForType($share->getShareType());
685
		$share = $provider->update($share);
686
687
		if ($expirationDateUpdated === true) {
688
			\OC_Hook::emit('OCP\Share', 'post_set_expiration_date', [
689
				'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder',
690
				'itemSource' => $share->getNode()->getId(),
691
				'date' => $share->getExpirationDate(),
692
				'uidOwner' => $share->getSharedBy(),
693
			]);
694
		}
695
696
		if ($share->getPassword() !== $originalShare->getPassword()) {
697
			\OC_Hook::emit('OCP\Share', 'post_update_password', [
698
				'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder',
699
				'itemSource' => $share->getNode()->getId(),
700
				'uidOwner' => $share->getSharedBy(),
701
				'token' => $share->getToken(),
702
				'disabled' => is_null($share->getPassword()),
703
			]);
704
		}
705
706
		if ($share->getPermissions() !== $originalShare->getPermissions()) {
707
			$userFolder = $this->rootFolder->getUserFolder($share->getShareOwner());
708
			\OC_Hook::emit('OCP\Share', 'post_update_permissions', array(
709
				'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder',
710
				'itemSource' => $share->getNode()->getId(),
711
				'shareType' => $share->getShareType(),
712
				'shareWith' => $share->getSharedWith(),
713
				'uidOwner' => $share->getSharedBy(),
714
				'permissions' => $share->getPermissions(),
715
				'path' => $userFolder->getRelativePath($share->getNode()->getPath()),
716
			));
717
		}
718
719
		return $share;
720
	}
721
722
	/**
723
	 * Delete all the children of this share
724
	 * FIXME: remove once https://github.com/owncloud/core/pull/21660 is in
725
	 *
726
	 * @param \OCP\Share\IShare $share
727
	 * @return \OCP\Share\IShare[] List of deleted shares
728
	 */
729
	protected function deleteChildren(\OCP\Share\IShare $share) {
730
		$deletedShares = [];
731
732
		$provider = $this->factory->getProviderForType($share->getShareType());
733
734
		foreach ($provider->getChildren($share) as $child) {
735
			$deletedChildren = $this->deleteChildren($child);
736
			$deletedShares = array_merge($deletedShares, $deletedChildren);
737
738
			$provider->delete($child);
739
			$deletedShares[] = $child;
740
		}
741
742
		return $deletedShares;
743
	}
744
745
	/**
746
	 * Delete a share
747
	 *
748
	 * @param \OCP\Share\IShare $share
749
	 * @throws ShareNotFound
750
	 * @throws \InvalidArgumentException
751
	 */
752
	public function deleteShare(\OCP\Share\IShare $share) {
753
754
		try {
755
			$share->getFullId();
756
		} catch (\UnexpectedValueException $e) {
757
			throw new \InvalidArgumentException('Share does not have a full id');
758
		}
759
760
		$formatHookParams = function(\OCP\Share\IShare $share) {
761
			// Prepare hook
762
			$shareType = $share->getShareType();
763
			$sharedWith = '';
764
			if ($shareType === \OCP\Share::SHARE_TYPE_USER) {
765
				$sharedWith = $share->getSharedWith();
766
			} else if ($shareType === \OCP\Share::SHARE_TYPE_GROUP) {
767
				$sharedWith = $share->getSharedWith();
768
			} else if ($shareType === \OCP\Share::SHARE_TYPE_REMOTE) {
769
				$sharedWith = $share->getSharedWith();
770
			}
771
772
			$hookParams = [
773
				'id'         => $share->getId(),
774
				'itemType'   => $share->getNodeType(),
775
				'itemSource' => $share->getNodeId(),
776
				'shareType'  => $shareType,
777
				'shareWith'  => $sharedWith,
778
				'itemparent' => method_exists($share, 'getParent') ? $share->getParent() : '',
779
				'uidOwner'   => $share->getSharedBy(),
780
				'fileSource' => $share->getNodeId(),
781
				'fileTarget' => $share->getTarget()
782
			];
783
			return $hookParams;
784
		};
785
786
		$hookParams = $formatHookParams($share);
787
788
		// Emit pre-hook
789
		\OC_Hook::emit('OCP\Share', 'pre_unshare', $hookParams);
790
791
		// Get all children and delete them as well
792
		$deletedShares = $this->deleteChildren($share);
793
794
		// Do the actual delete
795
		$provider = $this->factory->getProviderForType($share->getShareType());
796
		$provider->delete($share);
797
798
		// All the deleted shares caused by this delete
799
		$deletedShares[] = $share;
800
801
		//Format hook info
802
		$formattedDeletedShares = array_map(function($share) use ($formatHookParams) {
803
			return $formatHookParams($share);
804
		}, $deletedShares);
805
806
		$hookParams['deletedShares'] = $formattedDeletedShares;
807
808
		// Emit post hook
809
		\OC_Hook::emit('OCP\Share', 'post_unshare', $hookParams);
810
	}
811
812
813
	/**
814
	 * Unshare a file as the recipient.
815
	 * This can be different from a regular delete for example when one of
816
	 * the users in a groups deletes that share. But the provider should
817
	 * handle this.
818
	 *
819
	 * @param \OCP\Share\IShare $share
820
	 * @param string $recipientId
821
	 */
822
	public function deleteFromSelf(\OCP\Share\IShare $share, $recipientId) {
823
		list($providerId, ) = $this->splitFullId($share->getId());
824
		$provider = $this->factory->getProvider($providerId);
825
826
		$provider->deleteFromSelf($share, $recipientId);
827
	}
828
829
	/**
830
	 * @inheritdoc
831
	 */
832
	public function moveShare(\OCP\Share\IShare $share, $recipientId) {
833
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) {
834
			throw new \InvalidArgumentException('Can\'t change target of link share');
835
		}
836
837
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER && $share->getSharedWith() !== $recipientId) {
838
			throw new \InvalidArgumentException('Invalid recipient');
839
		}
840
841
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
842
			$sharedWith = $this->groupManager->get($share->getSharedWith());
843
			$recipient = $this->userManager->get($recipientId);
844
			if (!$sharedWith->inGroup($recipient)) {
0 ignored issues
show
Bug introduced by
It seems like $recipient defined by $this->userManager->get($recipientId) on line 843 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...
845
				throw new \InvalidArgumentException('Invalid recipient');
846
			}
847
		}
848
849
		list($providerId, ) = $this->splitFullId($share->getId());
850
		$provider = $this->factory->getProvider($providerId);
851
852
		$provider->move($share, $recipientId);
853
	}
854
855
	/**
856
	 * Get shares shared by (initiated) by the provided user.
857
	 *
858
	 * @param string $userId
859
	 * @param int $shareType
860
	 * @param \OCP\Files\File|\OCP\Files\Folder $path
861
	 * @param bool $reshares
862
	 * @param int $limit The maximum number of returned results, -1 for all results
863
	 * @param int $offset
864
	 * @return \OCP\Share\IShare[]
865
	 */
866
	public function getSharesBy($userId, $shareType, $path = null, $reshares = false, $limit = 50, $offset = 0) {
867
		if ($path !== null &&
868
				!($path instanceof \OCP\Files\File) &&
869
				!($path instanceof \OCP\Files\Folder)) {
870
			throw new \InvalidArgumentException('invalid path');
871
		}
872
873
		$provider = $this->factory->getProviderForType($shareType);
874
875
		$shares = $provider->getSharesBy($userId, $shareType, $path, $reshares, $limit, $offset);
0 ignored issues
show
Bug introduced by
It seems like $path defined by parameter $path on line 866 can also be of type null; however, OCP\Share\IShareProvider::getSharesBy() does only seem to accept object<OCP\Files\File>|object<OCP\Files\Folder>, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
876
877
		/*
878
		 * Work around so we don't return expired shares but still follow
879
		 * proper pagination.
880
		 */
881
		if ($shareType === \OCP\Share::SHARE_TYPE_LINK) {
882
			$shares2 = [];
883
			$today = new \DateTime();
884
885
			while(true) {
886
				$added = 0;
887
				foreach ($shares as $share) {
888
					// Check if the share is expired and if so delete it
889
					if ($share->getExpirationDate() !== null &&
890
						$share->getExpirationDate() <= $today
891
					) {
892
						try {
893
							$this->deleteShare($share);
894
						} catch (NotFoundException $e) {
895
							//Ignore since this basically means the share is deleted
896
						}
897
						continue;
898
					}
899
					$added++;
900
					$shares2[] = $share;
901
902
					if (count($shares2) === $limit) {
903
						break;
904
					}
905
				}
906
907
				if (count($shares2) === $limit) {
908
					break;
909
				}
910
911
				// If there was no limit on the select we are done
912
				if ($limit === -1) {
913
					break;
914
				}
915
916
				$offset += $added;
917
918
				// Fetch again $limit shares
919
				$shares = $provider->getSharesBy($userId, $shareType, $path, $reshares, $limit, $offset);
0 ignored issues
show
Bug introduced by
It seems like $path defined by parameter $path on line 866 can also be of type null; however, OCP\Share\IShareProvider::getSharesBy() does only seem to accept object<OCP\Files\File>|object<OCP\Files\Folder>, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
920
921
				// No more shares means we are done
922
				if (empty($shares)) {
923
					break;
924
				}
925
			}
926
927
			$shares = $shares2;
928
		}
929
930
		return $shares;
931
	}
932
933
	/**
934
	 * @inheritdoc
935
	 */
936
	public function getSharedWith($userId, $shareType, $node = null, $limit = 50, $offset = 0) {
937
		$provider = $this->factory->getProviderForType($shareType);
938
939
		return $provider->getSharedWith($userId, $shareType, $node, $limit, $offset);
940
	}
941
942
	/**
943
	 * @inheritdoc
944
	 */
945
	public function getShareById($id, $recipient = null) {
946
		if ($id === null) {
947
			throw new ShareNotFound();
948
		}
949
950
		list($providerId, $id) = $this->splitFullId($id);
951
		$provider = $this->factory->getProvider($providerId);
952
953
		$share = $provider->getShareById($id, $recipient);
0 ignored issues
show
Bug introduced by
It seems like $recipient defined by parameter $recipient on line 945 can also be of type object<OCP\IUser>; however, OCP\Share\IShareProvider::getShareById() does only seem to accept string|null, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
954
955
		// Validate link shares expiration date
956
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK &&
957
			$share->getExpirationDate() !== null &&
958
			$share->getExpirationDate() <= new \DateTime()) {
959
			$this->deleteShare($share);
960
			throw new ShareNotFound();
961
		}
962
963
		return $share;
964
	}
965
966
	/**
967
	 * Get all the shares for a given path
968
	 *
969
	 * @param \OCP\Files\Node $path
970
	 * @param int $page
971
	 * @param int $perPage
972
	 *
973
	 * @return Share[]
974
	 */
975
	public function getSharesByPath(\OCP\Files\Node $path, $page=0, $perPage=50) {
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...
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...
976
	}
977
978
	/**
979
	 * Get the share by token possible with password
980
	 *
981
	 * @param string $token
982
	 * @return Share
983
	 *
984
	 * @throws ShareNotFound
985
	 */
986
	public function getShareByToken($token) {
987
		$provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_LINK);
988
989
		$share = $provider->getShareByToken($token);
990
991
		if ($share->getExpirationDate() !== null &&
992
			$share->getExpirationDate() <= new \DateTime()) {
993
			$this->deleteShare($share);
994
			throw new ShareNotFound();
995
		}
996
997
		return $share;
998
	}
999
1000
	/**
1001
	 * Verify the password of a public share
1002
	 *
1003
	 * @param \OCP\Share\IShare $share
1004
	 * @param string $password
1005
	 * @return bool
1006
	 */
1007
	public function checkPassword(\OCP\Share\IShare $share, $password) {
1008
		if ($share->getShareType() !== \OCP\Share::SHARE_TYPE_LINK) {
1009
			//TODO maybe exception?
1010
			return false;
1011
		}
1012
1013
		if ($password === null || $share->getPassword() === null) {
1014
			return false;
1015
		}
1016
1017
		$newHash = '';
1018
		if (!$this->hasher->verify($password, $share->getPassword(), $newHash)) {
1019
			return false;
1020
		}
1021
1022
		if (!empty($newHash)) {
1023
			$share->setPassword($newHash);
1024
			$provider = $this->factory->getProviderForType($share->getShareType());
1025
			$provider->update($share);
1026
		}
1027
1028
		return true;
1029
	}
1030
1031
	/**
1032
	 * Get access list to a path. This means
1033
	 * all the users and groups that can access a given path.
1034
	 *
1035
	 * Consider:
1036
	 * -root
1037
	 * |-folder1
1038
	 *  |-folder2
1039
	 *   |-fileA
1040
	 *
1041
	 * fileA is shared with user1
1042
	 * folder2 is shared with group2
1043
	 * folder1 is shared with user2
1044
	 *
1045
	 * Then the access list will to '/folder1/folder2/fileA' is:
1046
	 * [
1047
	 * 	'users' => ['user1', 'user2'],
1048
	 *  'groups' => ['group2']
1049
	 * ]
1050
	 *
1051
	 * This is required for encryption
1052
	 *
1053
	 * @param \OCP\Files\Node $path
1054
	 */
1055
	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...
1056
	}
1057
1058
	/**
1059
	 * Create a new share
1060
	 * @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...
1061
	 */
1062
	public function newShare() {
1063
		return new \OC\Share20\Share($this->rootFolder);
1064
	}
1065
1066
	/**
1067
	 * Is the share API enabled
1068
	 *
1069
	 * @return bool
1070
	 */
1071
	public function shareApiEnabled() {
1072
		return $this->config->getAppValue('core', 'shareapi_enabled', 'yes') === 'yes';
1073
	}
1074
1075
	/**
1076
	 * Is public link sharing enabled
1077
	 *
1078
	 * @return bool
1079
	 */
1080
	public function shareApiAllowLinks() {
1081
		return $this->config->getAppValue('core', 'shareapi_allow_links', 'yes') === 'yes';
1082
	}
1083
1084
	/**
1085
	 * Is password on public link requires
1086
	 *
1087
	 * @return bool
1088
	 */
1089
	public function shareApiLinkEnforcePassword() {
1090
		return $this->config->getAppValue('core', 'shareapi_enforce_links_password', 'no') === 'yes';
1091
	}
1092
1093
	/**
1094
	 * Is default expire date enabled
1095
	 *
1096
	 * @return bool
1097
	 */
1098
	public function shareApiLinkDefaultExpireDate() {
1099
		return $this->config->getAppValue('core', 'shareapi_default_expire_date', 'no') === 'yes';
1100
	}
1101
1102
	/**
1103
	 * Is default expire date enforced
1104
	 *`
1105
	 * @return bool
1106
	 */
1107
	public function shareApiLinkDefaultExpireDateEnforced() {
1108
		return $this->shareApiLinkDefaultExpireDate() &&
1109
			$this->config->getAppValue('core', 'shareapi_enforce_expire_date', 'no') === 'yes';
1110
	}
1111
1112
	/**
1113
	 * Number of default expire days
1114
	 *shareApiLinkAllowPublicUpload
1115
	 * @return int
1116
	 */
1117
	public function shareApiLinkDefaultExpireDays() {
1118
		return (int)$this->config->getAppValue('core', 'shareapi_expire_after_n_days', '7');
1119
	}
1120
1121
	/**
1122
	 * Allow public upload on link shares
1123
	 *
1124
	 * @return bool
1125
	 */
1126
	public function shareApiLinkAllowPublicUpload() {
1127
		return $this->config->getAppValue('core', 'shareapi_allow_public_upload', 'yes') === 'yes';
1128
	}
1129
1130
	/**
1131
	 * check if user can only share with group members
1132
	 * @return bool
1133
	 */
1134
	public function shareWithGroupMembersOnly() {
1135
		return $this->config->getAppValue('core', 'shareapi_only_share_with_group_members', 'no') === 'yes';
1136
	}
1137
1138
	/**
1139
	 * Check if users can share with groups
1140
	 * @return bool
1141
	 */
1142
	public function allowGroupSharing() {
1143
		return $this->config->getAppValue('core', 'shareapi_allow_group_sharing', 'yes') === 'yes';
1144
	}
1145
1146
	/**
1147
	 * Copied from \OC_Util::isSharingDisabledForUser
1148
	 *
1149
	 * TODO: Deprecate fuction from OC_Util
1150
	 *
1151
	 * @param string $userId
1152
	 * @return bool
1153
	 */
1154
	public function sharingDisabledForUser($userId) {
1155
		if ($this->config->getAppValue('core', 'shareapi_exclude_groups', 'no') === 'yes') {
1156
			$groupsList = $this->config->getAppValue('core', 'shareapi_exclude_groups_list', '');
1157
			$excludedGroups = json_decode($groupsList);
1158 View Code Duplication
			if (is_null($excludedGroups)) {
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...
1159
				$excludedGroups = explode(',', $groupsList);
1160
				$newValue = json_encode($excludedGroups);
1161
				$this->config->setAppValue('core', 'shareapi_exclude_groups_list', $newValue);
1162
			}
1163
			$user = $this->userManager->get($userId);
1164
			$usersGroups = $this->groupManager->getUserGroupIds($user);
0 ignored issues
show
Bug introduced by
It seems like $user defined by $this->userManager->get($userId) on line 1163 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...
1165 View Code Duplication
			if (!empty($usersGroups)) {
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...
1166
				$remainingGroups = array_diff($usersGroups, $excludedGroups);
1167
				// if the user is only in groups which are disabled for sharing then
1168
				// sharing is also disabled for the user
1169
				if (empty($remainingGroups)) {
1170
					return true;
1171
				}
1172
			}
1173
		}
1174
		return false;
1175
	}
1176
1177
	/**
1178
	 * @inheritdoc
1179
	 */
1180
	public function outgoingServer2ServerSharesAllowed() {
1181
		return $this->config->getAppValue('files_sharing', 'outgoing_server2server_share_enabled', 'yes') === 'yes';
1182
	}
1183
1184
}
1185