Completed
Push — master ( 25f772...35f6b8 )
by Joas
340:56 queued 325:44
created

Manager::createShare()   D

Complexity

Conditions 13
Paths 84

Size

Total Lines 125
Code Lines 79

Duplication

Lines 4
Ratio 3.2 %

Importance

Changes 0
Metric Value
cc 13
eloc 79
nc 84
nop 1
dl 4
loc 125
rs 4.9922
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * @copyright Copyright (c) 2016, ownCloud, Inc.
4
 *
5
 * @author Arthur Schiwon <[email protected]>
6
 * @author Bjoern Schiessle <[email protected]>
7
 * @author Björn Schießle <[email protected]>
8
 * @author Joas Schilling <[email protected]>
9
 * @author Roeland Jago Douma <[email protected]>
10
 * @author Vincent Petry <[email protected]>
11
 *
12
 * @license AGPL-3.0
13
 *
14
 * This code is free software: you can redistribute it and/or modify
15
 * it under the terms of the GNU Affero General Public License, version 3,
16
 * as published by the Free Software Foundation.
17
 *
18
 * This program is distributed in the hope that it will be useful,
19
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21
 * GNU Affero General Public License for more details.
22
 *
23
 * You should have received a copy of the GNU Affero General Public License, version 3,
24
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
25
 *
26
 */
27
28
namespace OC\Share20;
29
30
use OC\Cache\CappedMemoryCache;
31
use OC\Files\Mount\MoveableMount;
32
use OC\HintException;
33
use OC\Share20\Exception\ProviderException;
34
use OCP\Files\File;
35
use OCP\Files\Folder;
36
use OCP\Files\IRootFolder;
37
use OCP\Files\Mount\IMountManager;
38
use OCP\Files\Node;
39
use OCP\Files\NotFoundException;
40
use OCP\IConfig;
41
use OCP\IGroupManager;
42
use OCP\IL10N;
43
use OCP\ILogger;
44
use OCP\IUserManager;
45
use OCP\Security\IHasher;
46
use OCP\Security\ISecureRandom;
47
use OCP\Share\Exceptions\GenericShareException;
48
use OCP\Share\Exceptions\ShareNotFound;
49
use OCP\Share\IManager;
50
use OCP\Share\IProviderFactory;
51
use Symfony\Component\EventDispatcher\EventDispatcher;
52
use Symfony\Component\EventDispatcher\GenericEvent;
53
use OCP\Share\IShareProvider;
54
55
/**
56
 * This class is the communication hub for all sharing related operations.
57
 */
58
class Manager implements IManager {
59
60
	/** @var IProviderFactory */
61
	private $factory;
62
	/** @var ILogger */
63
	private $logger;
64
	/** @var IConfig */
65
	private $config;
66
	/** @var ISecureRandom */
67
	private $secureRandom;
68
	/** @var IHasher */
69
	private $hasher;
70
	/** @var IMountManager */
71
	private $mountManager;
72
	/** @var IGroupManager */
73
	private $groupManager;
74
	/** @var IL10N */
75
	private $l;
76
	/** @var IUserManager */
77
	private $userManager;
78
	/** @var IRootFolder */
79
	private $rootFolder;
80
	/** @var CappedMemoryCache */
81
	private $sharingDisabledForUsersCache;
82
	/** @var EventDispatcher */
83
	private $eventDispatcher;
84
85
86
	/**
87
	 * Manager constructor.
88
	 *
89
	 * @param ILogger $logger
90
	 * @param IConfig $config
91
	 * @param ISecureRandom $secureRandom
92
	 * @param IHasher $hasher
93
	 * @param IMountManager $mountManager
94
	 * @param IGroupManager $groupManager
95
	 * @param IL10N $l
96
	 * @param IProviderFactory $factory
97
	 * @param IUserManager $userManager
98
	 * @param IRootFolder $rootFolder
99
	 * @param EventDispatcher $eventDispatcher
100
	 */
101
	public function __construct(
102
			ILogger $logger,
103
			IConfig $config,
104
			ISecureRandom $secureRandom,
105
			IHasher $hasher,
106
			IMountManager $mountManager,
107
			IGroupManager $groupManager,
108
			IL10N $l,
109
			IProviderFactory $factory,
110
			IUserManager $userManager,
111
			IRootFolder $rootFolder,
112
			EventDispatcher $eventDispatcher
113
	) {
114
		$this->logger = $logger;
115
		$this->config = $config;
116
		$this->secureRandom = $secureRandom;
117
		$this->hasher = $hasher;
118
		$this->mountManager = $mountManager;
119
		$this->groupManager = $groupManager;
120
		$this->l = $l;
121
		$this->factory = $factory;
122
		$this->userManager = $userManager;
123
		$this->rootFolder = $rootFolder;
124
		$this->eventDispatcher = $eventDispatcher;
125
		$this->sharingDisabledForUsersCache = new CappedMemoryCache();
126
	}
127
128
	/**
129
	 * Convert from a full share id to a tuple (providerId, shareId)
130
	 *
131
	 * @param string $id
132
	 * @return string[]
133
	 */
134
	private function splitFullId($id) {
135
		return explode(':', $id, 2);
136
	}
137
138
	/**
139
	 * Verify if a password meets all requirements
140
	 *
141
	 * @param string $password
142
	 * @throws \Exception
143
	 */
144
	protected function verifyPassword($password) {
145
		if ($password === null) {
146
			// No password is set, check if this is allowed.
147
			if ($this->shareApiLinkEnforcePassword()) {
148
				throw new \InvalidArgumentException('Passwords are enforced for link shares');
149
			}
150
151
			return;
152
		}
153
154
		// Let others verify the password
155
		try {
156
			$event = new GenericEvent($password);
157
			$this->eventDispatcher->dispatch('OCP\PasswordPolicy::validate', $event);
158
		} catch (HintException $e) {
159
			throw new \Exception($e->getHint());
160
		}
161
	}
162
163
	/**
164
	 * Check for generic requirements before creating a share
165
	 *
166
	 * @param \OCP\Share\IShare $share
167
	 * @throws \InvalidArgumentException
168
	 * @throws GenericShareException
169
	 */
170
	protected function generalCreateChecks(\OCP\Share\IShare $share) {
171
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) {
172
			// We expect a valid user as sharedWith for user shares
173
			if (!$this->userManager->userExists($share->getSharedWith())) {
174
				throw new \InvalidArgumentException('SharedWith is not a valid user');
175
			}
176
		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
177
			// We expect a valid group as sharedWith for group shares
178
			if (!$this->groupManager->groupExists($share->getSharedWith())) {
179
				throw new \InvalidArgumentException('SharedWith is not a valid group');
180
			}
181
		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) {
182
			if ($share->getSharedWith() !== null) {
183
				throw new \InvalidArgumentException('SharedWith should be empty');
184
			}
185
		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_REMOTE) {
186
			if ($share->getSharedWith() === null) {
187
				throw new \InvalidArgumentException('SharedWith should not be empty');
188
			}
189
		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_EMAIL) {
190
			if ($share->getSharedWith() === null) {
191
				throw new \InvalidArgumentException('SharedWith should not be empty');
192
			}
193
		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_CIRCLE) {
194
			$circle = \OCA\Circles\Api\Circles::detailsCircle($share->getSharedWith());
195
			if ($circle === null) {
196
				throw new \InvalidArgumentException('SharedWith is not a valid circle');
197
			}
198
		} else {
199
			// We can't handle other types yet
200
			throw new \InvalidArgumentException('unknown share type');
201
		}
202
203
		// Verify the initiator of the share is set
204
		if ($share->getSharedBy() === null) {
205
			throw new \InvalidArgumentException('SharedBy should be set');
206
		}
207
208
		// Cannot share with yourself
209 View Code Duplication
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER &&
210
			$share->getSharedWith() === $share->getSharedBy()) {
211
			throw new \InvalidArgumentException('Can\'t share with yourself');
212
		}
213
214
		// The path should be set
215
		if ($share->getNode() === null) {
216
			throw new \InvalidArgumentException('Path should be set');
217
		}
218
219
		// And it should be a file or a folder
220
		if (!($share->getNode() instanceof \OCP\Files\File) &&
221
				!($share->getNode() instanceof \OCP\Files\Folder)) {
222
			throw new \InvalidArgumentException('Path should be either a file or a folder');
223
		}
224
225
		// And you can't share your rootfolder
226
		if ($this->userManager->userExists($share->getSharedBy())) {
227
			$sharedPath = $this->rootFolder->getUserFolder($share->getSharedBy())->getPath();
228
		} else {
229
			$sharedPath = $this->rootFolder->getUserFolder($share->getShareOwner())->getPath();
230
		}
231
		if ($sharedPath === $share->getNode()->getPath()) {
232
			throw new \InvalidArgumentException('You can\'t share your root folder');
233
		}
234
235
		// Check if we actually have share permissions
236 View Code Duplication
		if (!$share->getNode()->isShareable()) {
237
			$message_t = $this->l->t('You are not allowed to share %s', [$share->getNode()->getPath()]);
238
			throw new GenericShareException($message_t, $message_t, 404);
239
		}
240
241
		// Permissions should be set
242
		if ($share->getPermissions() === null) {
243
			throw new \InvalidArgumentException('A share requires permissions');
244
		}
245
246
		/*
247
		 * Quick fix for #23536
248
		 * Non moveable mount points do not have update and delete permissions
249
		 * while we 'most likely' do have that on the storage.
250
		 */
251
		$permissions = $share->getNode()->getPermissions();
252
		$mount = $share->getNode()->getMountPoint();
253
		if (!($mount instanceof MoveableMount)) {
254
			$permissions |= \OCP\Constants::PERMISSION_DELETE | \OCP\Constants::PERMISSION_UPDATE;
255
		}
256
257
		// Check that we do not share with more permissions than we have
258 View Code Duplication
		if ($share->getPermissions() & ~$permissions) {
259
			$message_t = $this->l->t('Cannot increase permissions of %s', [$share->getNode()->getPath()]);
260
			throw new GenericShareException($message_t, $message_t, 404);
261
		}
262
263
264
		// Check that read permissions are always set
265
		// Link shares are allowed to have no read permissions to allow upload to hidden folders
266
		if ($share->getShareType() !== \OCP\Share::SHARE_TYPE_LINK &&
267
			($share->getPermissions() & \OCP\Constants::PERMISSION_READ) === 0) {
268
			throw new \InvalidArgumentException('Shares need at least read permissions');
269
		}
270
271
		if ($share->getNode() instanceof \OCP\Files\File) {
272 View Code Duplication
			if ($share->getPermissions() & \OCP\Constants::PERMISSION_DELETE) {
273
				$message_t = $this->l->t('Files can\'t be shared with delete permissions');
274
				throw new GenericShareException($message_t);
275
			}
276 View Code Duplication
			if ($share->getPermissions() & \OCP\Constants::PERMISSION_CREATE) {
277
				$message_t = $this->l->t('Files can\'t be shared with create permissions');
278
				throw new GenericShareException($message_t);
279
			}
280
		}
281
	}
282
283
	/**
284
	 * Validate if the expiration date fits the system settings
285
	 *
286
	 * @param \OCP\Share\IShare $share The share to validate the expiration date of
287
	 * @return \OCP\Share\IShare The modified share object
288
	 * @throws GenericShareException
289
	 * @throws \InvalidArgumentException
290
	 * @throws \Exception
291
	 */
292
	protected function validateExpirationDate(\OCP\Share\IShare $share) {
293
294
		$expirationDate = $share->getExpirationDate();
295
296
		if ($expirationDate !== null) {
297
			//Make sure the expiration date is a date
298
			$expirationDate->setTime(0, 0, 0);
299
300
			$date = new \DateTime();
301
			$date->setTime(0, 0, 0);
302 View Code Duplication
			if ($date >= $expirationDate) {
303
				$message = $this->l->t('Expiration date is in the past');
304
				throw new GenericShareException($message, $message, 404);
305
			}
306
		}
307
308
		// If expiredate is empty set a default one if there is a default
309
		$fullId = null;
310
		try {
311
			$fullId = $share->getFullId();
312
		} catch (\UnexpectedValueException $e) {
313
			// This is a new share
314
		}
315
316
		if ($fullId === null && $expirationDate === null && $this->shareApiLinkDefaultExpireDate()) {
317
			$expirationDate = new \DateTime();
318
			$expirationDate->setTime(0,0,0);
319
			$expirationDate->add(new \DateInterval('P'.$this->shareApiLinkDefaultExpireDays().'D'));
320
		}
321
322
		// If we enforce the expiration date check that is does not exceed
323
		if ($this->shareApiLinkDefaultExpireDateEnforced()) {
324
			if ($expirationDate === null) {
325
				throw new \InvalidArgumentException('Expiration date is enforced');
326
			}
327
328
			$date = new \DateTime();
329
			$date->setTime(0, 0, 0);
330
			$date->add(new \DateInterval('P' . $this->shareApiLinkDefaultExpireDays() . 'D'));
331 View Code Duplication
			if ($date < $expirationDate) {
332
				$message = $this->l->t('Cannot set expiration date more than %s days in the future', [$this->shareApiLinkDefaultExpireDays()]);
333
				throw new GenericShareException($message, $message, 404);
334
			}
335
		}
336
337
		$accepted = true;
338
		$message = '';
339
		\OCP\Util::emitHook('\OC\Share', 'verifyExpirationDate', [
340
			'expirationDate' => &$expirationDate,
341
			'accepted' => &$accepted,
342
			'message' => &$message,
343
			'passwordSet' => $share->getPassword() !== null,
344
		]);
345
346
		if (!$accepted) {
347
			throw new \Exception($message);
348
		}
349
350
		$share->setExpirationDate($expirationDate);
351
352
		return $share;
353
	}
354
355
	/**
356
	 * Check for pre share requirements for user shares
357
	 *
358
	 * @param \OCP\Share\IShare $share
359
	 * @throws \Exception
360
	 */
361
	protected function userCreateChecks(\OCP\Share\IShare $share) {
362
		// Check if we can share with group members only
363
		if ($this->shareWithGroupMembersOnly()) {
364
			$sharedBy = $this->userManager->get($share->getSharedBy());
365
			$sharedWith = $this->userManager->get($share->getSharedWith());
366
			// Verify we can share with this user
367
			$groups = array_intersect(
368
					$this->groupManager->getUserGroupIds($sharedBy),
0 ignored issues
show
Bug introduced by
It seems like $sharedBy defined by $this->userManager->get($share->getSharedBy()) on line 364 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...
369
					$this->groupManager->getUserGroupIds($sharedWith)
0 ignored issues
show
Bug introduced by
It seems like $sharedWith defined by $this->userManager->get($share->getSharedWith()) on line 365 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...
370
			);
371
			if (empty($groups)) {
372
				throw new \Exception('Only sharing with group members is allowed');
373
			}
374
		}
375
376
		/*
377
		 * TODO: Could be costly, fix
378
		 *
379
		 * Also this is not what we want in the future.. then we want to squash identical shares.
380
		 */
381
		$provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_USER);
382
		$existingShares = $provider->getSharesByPath($share->getNode());
383
		foreach($existingShares as $existingShare) {
384
			// Ignore if it is the same share
385
			try {
386
				if ($existingShare->getFullId() === $share->getFullId()) {
387
					continue;
388
				}
389
			} catch (\UnexpectedValueException $e) {
390
				//Shares are not identical
391
			}
392
393
			// Identical share already existst
394
			if ($existingShare->getSharedWith() === $share->getSharedWith()) {
395
				throw new \Exception('Path already shared with this user');
396
			}
397
398
			// The share is already shared with this user via a group share
399
			if ($existingShare->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
400
				$group = $this->groupManager->get($existingShare->getSharedWith());
401
				if (!is_null($group)) {
402
					$user = $this->userManager->get($share->getSharedWith());
403
404
					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 402 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...
405
						throw new \Exception('Path already shared with this user');
406
					}
407
				}
408
			}
409
		}
410
	}
411
412
	/**
413
	 * Check for pre share requirements for group shares
414
	 *
415
	 * @param \OCP\Share\IShare $share
416
	 * @throws \Exception
417
	 */
418
	protected function groupCreateChecks(\OCP\Share\IShare $share) {
419
		// Verify group shares are allowed
420
		if (!$this->allowGroupSharing()) {
421
			throw new \Exception('Group sharing is now allowed');
422
		}
423
424
		// Verify if the user can share with this group
425
		if ($this->shareWithGroupMembersOnly()) {
426
			$sharedBy = $this->userManager->get($share->getSharedBy());
427
			$sharedWith = $this->groupManager->get($share->getSharedWith());
428
			if (is_null($sharedWith) || !$sharedWith->inGroup($sharedBy)) {
0 ignored issues
show
Bug introduced by
It seems like $sharedBy defined by $this->userManager->get($share->getSharedBy()) on line 426 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...
429
				throw new \Exception('Only sharing within your own groups is allowed');
430
			}
431
		}
432
433
		/*
434
		 * TODO: Could be costly, fix
435
		 *
436
		 * Also this is not what we want in the future.. then we want to squash identical shares.
437
		 */
438
		$provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_GROUP);
439
		$existingShares = $provider->getSharesByPath($share->getNode());
440
		foreach($existingShares as $existingShare) {
441
			try {
442
				if ($existingShare->getFullId() === $share->getFullId()) {
443
					continue;
444
				}
445
			} catch (\UnexpectedValueException $e) {
446
				//It is a new share so just continue
447
			}
448
449
			if ($existingShare->getSharedWith() === $share->getSharedWith()) {
450
				throw new \Exception('Path already shared with this group');
451
			}
452
		}
453
	}
454
455
	/**
456
	 * Check for pre share requirements for link shares
457
	 *
458
	 * @param \OCP\Share\IShare $share
459
	 * @throws \Exception
460
	 */
461
	protected function linkCreateChecks(\OCP\Share\IShare $share) {
462
		// Are link shares allowed?
463
		if (!$this->shareApiAllowLinks()) {
464
			throw new \Exception('Link sharing not allowed');
465
		}
466
467
		// Link shares by definition can't have share permissions
468
		if ($share->getPermissions() & \OCP\Constants::PERMISSION_SHARE) {
469
			throw new \InvalidArgumentException('Link shares can\'t have reshare permissions');
470
		}
471
472
		// Check if public upload is allowed
473
		if (!$this->shareApiLinkAllowPublicUpload() &&
474
			($share->getPermissions() & (\OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE))) {
475
			throw new \InvalidArgumentException('Public upload not allowed');
476
		}
477
	}
478
479
	/**
480
	 * To make sure we don't get invisible link shares we set the parent
481
	 * of a link if it is a reshare. This is a quick word around
482
	 * until we can properly display multiple link shares in the UI
483
	 *
484
	 * See: https://github.com/owncloud/core/issues/22295
485
	 *
486
	 * FIXME: Remove once multiple link shares can be properly displayed
487
	 *
488
	 * @param \OCP\Share\IShare $share
489
	 */
490
	protected function setLinkParent(\OCP\Share\IShare $share) {
491
492
		// No sense in checking if the method is not there.
493
		if (method_exists($share, 'setParent')) {
494
			$storage = $share->getNode()->getStorage();
495
			if ($storage->instanceOfStorage('\OCA\Files_Sharing\ISharedStorage')) {
496
				/** @var \OCA\Files_Sharing\SharedStorage $storage */
497
				$share->setParent($storage->getShareId());
498
			}
499
		};
500
	}
501
502
	/**
503
	 * @param File|Folder $path
504
	 */
505
	protected function pathCreateChecks($path) {
506
		// Make sure that we do not share a path that contains a shared mountpoint
507
		if ($path instanceof \OCP\Files\Folder) {
508
			$mounts = $this->mountManager->findIn($path->getPath());
509
			foreach($mounts as $mount) {
510
				if ($mount->getStorage()->instanceOfStorage('\OCA\Files_Sharing\ISharedStorage')) {
511
					throw new \InvalidArgumentException('Path contains files shared with you');
512
				}
513
			}
514
		}
515
	}
516
517
	/**
518
	 * Check if the user that is sharing can actually share
519
	 *
520
	 * @param \OCP\Share\IShare $share
521
	 * @throws \Exception
522
	 */
523
	protected function canShare(\OCP\Share\IShare $share) {
524
		if (!$this->shareApiEnabled()) {
525
			throw new \Exception('The share API is disabled');
526
		}
527
528
		if ($this->sharingDisabledForUser($share->getSharedBy())) {
529
			throw new \Exception('You are not allowed to share');
530
		}
531
	}
532
533
	/**
534
	 * Share a path
535
	 *
536
	 * @param \OCP\Share\IShare $share
537
	 * @return Share The share object
538
	 * @throws \Exception
539
	 *
540
	 * TODO: handle link share permissions or check them
541
	 */
542
	public function createShare(\OCP\Share\IShare $share) {
543
		$this->canShare($share);
544
545
		$this->generalCreateChecks($share);
546
547
		// Verify if there are any issues with the path
548
		$this->pathCreateChecks($share->getNode());
549
550
		/*
551
		 * On creation of a share the owner is always the owner of the path
552
		 * Except for mounted federated shares.
553
		 */
554
		$storage = $share->getNode()->getStorage();
555
		if ($storage->instanceOfStorage('OCA\Files_Sharing\External\Storage')) {
556
			$parent = $share->getNode()->getParent();
557
			while($parent->getStorage()->instanceOfStorage('OCA\Files_Sharing\External\Storage')) {
558
				$parent = $parent->getParent();
559
			}
560
			$share->setShareOwner($parent->getOwner()->getUID());
561
		} else {
562
			$share->setShareOwner($share->getNode()->getOwner()->getUID());
563
		}
564
565
		//Verify share type
566
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) {
567
			$this->userCreateChecks($share);
568
		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
569
			$this->groupCreateChecks($share);
570
		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) {
571
			$this->linkCreateChecks($share);
572
			$this->setLinkParent($share);
573
574
			/*
575
			 * For now ignore a set token.
576
			 */
577
			$share->setToken(
578
				$this->secureRandom->generate(
579
					\OC\Share\Constants::TOKEN_LENGTH,
580
					\OCP\Security\ISecureRandom::CHAR_LOWER.
581
					\OCP\Security\ISecureRandom::CHAR_UPPER.
582
					\OCP\Security\ISecureRandom::CHAR_DIGITS
583
				)
584
			);
585
586
			//Verify the expiration date
587
			$this->validateExpirationDate($share);
588
589
			//Verify the password
590
			$this->verifyPassword($share->getPassword());
591
592
			// If a password is set. Hash it!
593
			if ($share->getPassword() !== null) {
594
				$share->setPassword($this->hasher->hash($share->getPassword()));
595
			}
596
		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_EMAIL) {
597
			$share->setToken(
598
				$this->secureRandom->generate(
599
					\OC\Share\Constants::TOKEN_LENGTH,
600
					\OCP\Security\ISecureRandom::CHAR_LOWER.
601
					\OCP\Security\ISecureRandom::CHAR_UPPER.
602
					\OCP\Security\ISecureRandom::CHAR_DIGITS
603
				)
604
			);
605
		}
606
607
		// Cannot share with the owner
608 View Code Duplication
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER &&
609
			$share->getSharedWith() === $share->getShareOwner()) {
610
			throw new \InvalidArgumentException('Can\'t share with the share owner');
611
		}
612
613
		// Generate the target
614
		$target = $this->config->getSystemValue('share_folder', '/') .'/'. $share->getNode()->getName();
615
		$target = \OC\Files\Filesystem::normalizePath($target);
616
		$share->setTarget($target);
617
618
		// Pre share hook
619
		$run = true;
620
		$error = '';
621
		$preHookData = [
622
			'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder',
623
			'itemSource' => $share->getNode()->getId(),
624
			'shareType' => $share->getShareType(),
625
			'uidOwner' => $share->getSharedBy(),
626
			'permissions' => $share->getPermissions(),
627
			'fileSource' => $share->getNode()->getId(),
628
			'expiration' => $share->getExpirationDate(),
629
			'token' => $share->getToken(),
630
			'itemTarget' => $share->getTarget(),
631
			'shareWith' => $share->getSharedWith(),
632
			'run' => &$run,
633
			'error' => &$error,
634
		];
635
		\OC_Hook::emit('OCP\Share', 'pre_shared', $preHookData);
636
637
		if ($run === false) {
638
			throw new \Exception($error);
639
		}
640
641
		$oldShare = $share;
642
		$provider = $this->factory->getProviderForType($share->getShareType());
643
		$share = $provider->create($share);
644
		//reuse the node we already have
645
		$share->setNode($oldShare->getNode());
646
647
		// Post share hook
648
		$postHookData = [
649
			'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder',
650
			'itemSource' => $share->getNode()->getId(),
651
			'shareType' => $share->getShareType(),
652
			'uidOwner' => $share->getSharedBy(),
653
			'permissions' => $share->getPermissions(),
654
			'fileSource' => $share->getNode()->getId(),
655
			'expiration' => $share->getExpirationDate(),
656
			'token' => $share->getToken(),
657
			'id' => $share->getId(),
658
			'shareWith' => $share->getSharedWith(),
659
			'itemTarget' => $share->getTarget(),
660
			'fileTarget' => $share->getTarget(),
661
		];
662
663
		\OC_Hook::emit('OCP\Share', 'post_shared', $postHookData);
664
665
		return $share;
666
	}
667
668
	/**
669
	 * Update a share
670
	 *
671
	 * @param \OCP\Share\IShare $share
672
	 * @return \OCP\Share\IShare The share object
673
	 * @throws \InvalidArgumentException
674
	 */
675
	public function updateShare(\OCP\Share\IShare $share) {
676
		$expirationDateUpdated = false;
677
678
		$this->canShare($share);
679
680
		try {
681
			$originalShare = $this->getShareById($share->getFullId());
682
		} catch (\UnexpectedValueException $e) {
683
			throw new \InvalidArgumentException('Share does not have a full id');
684
		}
685
686
		// We can't change the share type!
687
		if ($share->getShareType() !== $originalShare->getShareType()) {
688
			throw new \InvalidArgumentException('Can\'t change share type');
689
		}
690
691
		// We can only change the recipient on user shares
692
		if ($share->getSharedWith() !== $originalShare->getSharedWith() &&
693
		    $share->getShareType() !== \OCP\Share::SHARE_TYPE_USER) {
694
			throw new \InvalidArgumentException('Can only update recipient on user shares');
695
		}
696
697
		// Cannot share with the owner
698 View Code Duplication
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER &&
699
			$share->getSharedWith() === $share->getShareOwner()) {
700
			throw new \InvalidArgumentException('Can\'t share with the share owner');
701
		}
702
703
		$this->generalCreateChecks($share);
704
705
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) {
706
			$this->userCreateChecks($share);
707
		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
708
			$this->groupCreateChecks($share);
709
		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) {
710
			$this->linkCreateChecks($share);
711
712
			// Password updated.
713
			if ($share->getPassword() !== $originalShare->getPassword()) {
714
				//Verify the password
715
				$this->verifyPassword($share->getPassword());
716
717
				// If a password is set. Hash it!
718
				if ($share->getPassword() !== null) {
719
					$share->setPassword($this->hasher->hash($share->getPassword()));
720
				}
721
			}
722
723
			if ($share->getExpirationDate() != $originalShare->getExpirationDate()) {
724
				//Verify the expiration date
725
				$this->validateExpirationDate($share);
726
				$expirationDateUpdated = true;
727
			}
728
		}
729
730
		$this->pathCreateChecks($share->getNode());
731
732
		// Now update the share!
733
		$provider = $this->factory->getProviderForType($share->getShareType());
734
		$share = $provider->update($share);
735
736
		if ($expirationDateUpdated === true) {
737
			\OC_Hook::emit('OCP\Share', 'post_set_expiration_date', [
738
				'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder',
739
				'itemSource' => $share->getNode()->getId(),
740
				'date' => $share->getExpirationDate(),
741
				'uidOwner' => $share->getSharedBy(),
742
			]);
743
		}
744
745
		if ($share->getPassword() !== $originalShare->getPassword()) {
746
			\OC_Hook::emit('OCP\Share', 'post_update_password', [
747
				'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder',
748
				'itemSource' => $share->getNode()->getId(),
749
				'uidOwner' => $share->getSharedBy(),
750
				'token' => $share->getToken(),
751
				'disabled' => is_null($share->getPassword()),
752
			]);
753
		}
754
755
		if ($share->getPermissions() !== $originalShare->getPermissions()) {
756
			if ($this->userManager->userExists($share->getShareOwner())) {
757
				$userFolder = $this->rootFolder->getUserFolder($share->getShareOwner());
758
			} else {
759
				$userFolder = $this->rootFolder->getUserFolder($share->getSharedBy());
760
			}
761
			\OC_Hook::emit('OCP\Share', 'post_update_permissions', array(
762
				'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder',
763
				'itemSource' => $share->getNode()->getId(),
764
				'shareType' => $share->getShareType(),
765
				'shareWith' => $share->getSharedWith(),
766
				'uidOwner' => $share->getSharedBy(),
767
				'permissions' => $share->getPermissions(),
768
				'path' => $userFolder->getRelativePath($share->getNode()->getPath()),
769
			));
770
		}
771
772
		return $share;
773
	}
774
775
	/**
776
	 * Delete all the children of this share
777
	 * FIXME: remove once https://github.com/owncloud/core/pull/21660 is in
778
	 *
779
	 * @param \OCP\Share\IShare $share
780
	 * @return \OCP\Share\IShare[] List of deleted shares
781
	 */
782
	protected function deleteChildren(\OCP\Share\IShare $share) {
783
		$deletedShares = [];
784
785
		$provider = $this->factory->getProviderForType($share->getShareType());
786
787
		foreach ($provider->getChildren($share) as $child) {
788
			$deletedChildren = $this->deleteChildren($child);
789
			$deletedShares = array_merge($deletedShares, $deletedChildren);
790
791
			$provider->delete($child);
792
			$deletedShares[] = $child;
793
		}
794
795
		return $deletedShares;
796
	}
797
798
	/**
799
	 * Delete a share
800
	 *
801
	 * @param \OCP\Share\IShare $share
802
	 * @throws ShareNotFound
803
	 * @throws \InvalidArgumentException
804
	 */
805
	public function deleteShare(\OCP\Share\IShare $share) {
806
807
		try {
808
			$share->getFullId();
809
		} catch (\UnexpectedValueException $e) {
810
			throw new \InvalidArgumentException('Share does not have a full id');
811
		}
812
813
		$formatHookParams = function(\OCP\Share\IShare $share) {
814
			// Prepare hook
815
			$shareType = $share->getShareType();
816
			$sharedWith = '';
817
			if ($shareType === \OCP\Share::SHARE_TYPE_USER) {
818
				$sharedWith = $share->getSharedWith();
819
			} else if ($shareType === \OCP\Share::SHARE_TYPE_GROUP) {
820
				$sharedWith = $share->getSharedWith();
821
			} else if ($shareType === \OCP\Share::SHARE_TYPE_REMOTE) {
822
				$sharedWith = $share->getSharedWith();
823
			}
824
825
			$hookParams = [
826
				'id'         => $share->getId(),
827
				'itemType'   => $share->getNodeType(),
828
				'itemSource' => $share->getNodeId(),
829
				'shareType'  => $shareType,
830
				'shareWith'  => $sharedWith,
831
				'itemparent' => method_exists($share, 'getParent') ? $share->getParent() : '',
832
				'uidOwner'   => $share->getSharedBy(),
833
				'fileSource' => $share->getNodeId(),
834
				'fileTarget' => $share->getTarget()
835
			];
836
			return $hookParams;
837
		};
838
839
		$hookParams = $formatHookParams($share);
840
841
		// Emit pre-hook
842
		\OC_Hook::emit('OCP\Share', 'pre_unshare', $hookParams);
843
844
		// Get all children and delete them as well
845
		$deletedShares = $this->deleteChildren($share);
846
847
		// Do the actual delete
848
		$provider = $this->factory->getProviderForType($share->getShareType());
849
		$provider->delete($share);
850
851
		// All the deleted shares caused by this delete
852
		$deletedShares[] = $share;
853
854
		//Format hook info
855
		$formattedDeletedShares = array_map(function($share) use ($formatHookParams) {
856
			return $formatHookParams($share);
857
		}, $deletedShares);
858
859
		$hookParams['deletedShares'] = $formattedDeletedShares;
860
861
		// Emit post hook
862
		\OC_Hook::emit('OCP\Share', 'post_unshare', $hookParams);
863
	}
864
865
866
	/**
867
	 * Unshare a file as the recipient.
868
	 * This can be different from a regular delete for example when one of
869
	 * the users in a groups deletes that share. But the provider should
870
	 * handle this.
871
	 *
872
	 * @param \OCP\Share\IShare $share
873
	 * @param string $recipientId
874
	 */
875
	public function deleteFromSelf(\OCP\Share\IShare $share, $recipientId) {
876
		list($providerId, ) = $this->splitFullId($share->getFullId());
877
		$provider = $this->factory->getProvider($providerId);
878
879
		$provider->deleteFromSelf($share, $recipientId);
880
	}
881
882
	/**
883
	 * @inheritdoc
884
	 */
885
	public function moveShare(\OCP\Share\IShare $share, $recipientId) {
886
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) {
887
			throw new \InvalidArgumentException('Can\'t change target of link share');
888
		}
889
890
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER && $share->getSharedWith() !== $recipientId) {
891
			throw new \InvalidArgumentException('Invalid recipient');
892
		}
893
894
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
895
			$sharedWith = $this->groupManager->get($share->getSharedWith());
896
			if (is_null($sharedWith)) {
897
				throw new \InvalidArgumentException('Group "' . $share->getSharedWith() . '" does not exist');
898
			}
899
			$recipient = $this->userManager->get($recipientId);
900
			if (!$sharedWith->inGroup($recipient)) {
0 ignored issues
show
Bug introduced by
It seems like $recipient defined by $this->userManager->get($recipientId) on line 899 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...
901
				throw new \InvalidArgumentException('Invalid recipient');
902
			}
903
		}
904
905
		list($providerId, ) = $this->splitFullId($share->getFullId());
906
		$provider = $this->factory->getProvider($providerId);
907
908
		$provider->move($share, $recipientId);
909
	}
910
911
	public function getSharesInFolder($userId, Folder $node, $reshares = false) {
912
		$providers = $this->factory->getAllProviders();
913
914
		return array_reduce($providers, function($shares, IShareProvider $provider) use ($userId, $node, $reshares) {
915
			$newShares = $provider->getSharesInFolder($userId, $node, $reshares);
916
			foreach ($newShares as $fid => $data) {
917
				if (!isset($shares[$fid])) {
918
					$shares[$fid] = [];
919
				}
920
921
				$shares[$fid] = array_merge($shares[$fid], $data);
922
			}
923
			return $shares;
924
		}, []);
925
	}
926
927
	/**
928
	 * @inheritdoc
929
	 */
930
	public function getSharesBy($userId, $shareType, $path = null, $reshares = false, $limit = 50, $offset = 0) {
931
		if ($path !== null &&
932
				!($path instanceof \OCP\Files\File) &&
933
				!($path instanceof \OCP\Files\Folder)) {
934
			throw new \InvalidArgumentException('invalid path');
935
		}
936
937
		try {
938
			$provider = $this->factory->getProviderForType($shareType);
939
		} catch (ProviderException $e) {
940
			return [];
941
		}
942
943
		$shares = $provider->getSharesBy($userId, $shareType, $path, $reshares, $limit, $offset);
944
945
		/*
946
		 * Work around so we don't return expired shares but still follow
947
		 * proper pagination.
948
		 */
949
		if ($shareType === \OCP\Share::SHARE_TYPE_LINK) {
950
			$shares2 = [];
951
			$today = new \DateTime();
952
953
			while(true) {
954
				$added = 0;
955
				foreach ($shares as $share) {
956
					// Check if the share is expired and if so delete it
957
					if ($share->getExpirationDate() !== null &&
958
						$share->getExpirationDate() <= $today
959
					) {
960
						try {
961
							$this->deleteShare($share);
962
						} catch (NotFoundException $e) {
963
							//Ignore since this basically means the share is deleted
964
						}
965
						continue;
966
					}
967
					$added++;
968
					$shares2[] = $share;
969
970
					if (count($shares2) === $limit) {
971
						break;
972
					}
973
				}
974
975
				if (count($shares2) === $limit) {
976
					break;
977
				}
978
979
				// If there was no limit on the select we are done
980
				if ($limit === -1) {
981
					break;
982
				}
983
984
				$offset += $added;
985
986
				// Fetch again $limit shares
987
				$shares = $provider->getSharesBy($userId, $shareType, $path, $reshares, $limit, $offset);
988
989
				// No more shares means we are done
990
				if (empty($shares)) {
991
					break;
992
				}
993
			}
994
995
			$shares = $shares2;
996
		}
997
998
		return $shares;
999
	}
1000
1001
	/**
1002
	 * @inheritdoc
1003
	 */
1004
	public function getSharedWith($userId, $shareType, $node = null, $limit = 50, $offset = 0) {
1005
		try {
1006
			$provider = $this->factory->getProviderForType($shareType);
1007
		} catch (ProviderException $e) {
1008
			return [];
1009
		}
1010
1011
		return $provider->getSharedWith($userId, $shareType, $node, $limit, $offset);
1012
	}
1013
1014
	/**
1015
	 * @inheritdoc
1016
	 */
1017
	public function getShareById($id, $recipient = null) {
1018
		if ($id === null) {
1019
			throw new ShareNotFound();
1020
		}
1021
1022
		list($providerId, $id) = $this->splitFullId($id);
1023
1024
		try {
1025
			$provider = $this->factory->getProvider($providerId);
1026
		} catch (ProviderException $e) {
1027
			throw new ShareNotFound();
1028
		}
1029
1030
		$share = $provider->getShareById($id, $recipient);
1031
1032
		// Validate link shares expiration date
1033
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK &&
1034
			$share->getExpirationDate() !== null &&
1035
			$share->getExpirationDate() <= new \DateTime()) {
1036
			$this->deleteShare($share);
1037
			throw new ShareNotFound();
1038
		}
1039
1040
		return $share;
1041
	}
1042
1043
	/**
1044
	 * Get all the shares for a given path
1045
	 *
1046
	 * @param \OCP\Files\Node $path
1047
	 * @param int $page
1048
	 * @param int $perPage
1049
	 *
1050
	 * @return Share[]
1051
	 */
1052
	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...
1053
		return [];
1054
	}
1055
1056
	/**
1057
	 * Get the share by token possible with password
1058
	 *
1059
	 * @param string $token
1060
	 * @return Share
1061
	 *
1062
	 * @throws ShareNotFound
1063
	 */
1064
	public function getShareByToken($token) {
1065
		$share = null;
1066
		try {
1067
			if($this->shareApiAllowLinks()) {
1068
				$provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_LINK);
1069
				$share = $provider->getShareByToken($token);
1070
			}
1071
		} catch (ProviderException $e) {
1072
		} catch (ShareNotFound $e) {
1073
		}
1074
1075
1076
		// If it is not a link share try to fetch a federated share by token
1077 View Code Duplication
		if ($share === null) {
1078
			try {
1079
				$provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_REMOTE);
1080
				$share = $provider->getShareByToken($token);
1081
			} catch (ProviderException $e) {
1082
			} catch (ShareNotFound $e) {
1083
			}
1084
		}
1085
1086
		// If it is not a link share try to fetch a mail share by token
1087 View Code Duplication
		if ($share === null && $this->shareProviderExists(\OCP\Share::SHARE_TYPE_EMAIL)) {
1088
			try {
1089
				$provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_EMAIL);
1090
				$share = $provider->getShareByToken($token);
1091
			} catch (ProviderException $e) {
1092
			} catch (ShareNotFound $e) {
1093
			}
1094
		}
1095
1096
		if ($share === null) {
1097
			throw new ShareNotFound();
1098
		}
1099
1100
		if ($share->getExpirationDate() !== null &&
1101
			$share->getExpirationDate() <= new \DateTime()) {
1102
			$this->deleteShare($share);
1103
			throw new ShareNotFound();
1104
		}
1105
1106
		/*
1107
		 * Reduce the permissions for link shares if public upload is not enabled
1108
		 */
1109
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK &&
1110
			!$this->shareApiLinkAllowPublicUpload()) {
1111
			$share->setPermissions($share->getPermissions() & ~(\OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE));
1112
		}
1113
1114
		return $share;
1115
	}
1116
1117
	/**
1118
	 * Verify the password of a public share
1119
	 *
1120
	 * @param \OCP\Share\IShare $share
1121
	 * @param string $password
1122
	 * @return bool
1123
	 */
1124
	public function checkPassword(\OCP\Share\IShare $share, $password) {
1125
		if ($share->getShareType() !== \OCP\Share::SHARE_TYPE_LINK) {
1126
			//TODO maybe exception?
1127
			return false;
1128
		}
1129
1130
		if ($password === null || $share->getPassword() === null) {
1131
			return false;
1132
		}
1133
1134
		$newHash = '';
1135
		if (!$this->hasher->verify($password, $share->getPassword(), $newHash)) {
1136
			return false;
1137
		}
1138
1139
		if (!empty($newHash)) {
1140
			$share->setPassword($newHash);
1141
			$provider = $this->factory->getProviderForType($share->getShareType());
1142
			$provider->update($share);
1143
		}
1144
1145
		return true;
1146
	}
1147
1148
	/**
1149
	 * @inheritdoc
1150
	 */
1151
	public function userDeleted($uid) {
1152
		$types = [\OCP\Share::SHARE_TYPE_USER, \OCP\Share::SHARE_TYPE_GROUP, \OCP\Share::SHARE_TYPE_LINK, \OCP\Share::SHARE_TYPE_REMOTE, \OCP\Share::SHARE_TYPE_EMAIL];
1153
1154
		foreach ($types as $type) {
1155
			try {
1156
				$provider = $this->factory->getProviderForType($type);
1157
			} catch (ProviderException $e) {
1158
				continue;
1159
			}
1160
			$provider->userDeleted($uid, $type);
1161
		}
1162
	}
1163
1164
	/**
1165
	 * @inheritdoc
1166
	 */
1167
	public function groupDeleted($gid) {
1168
		$provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_GROUP);
1169
		$provider->groupDeleted($gid);
1170
	}
1171
1172
	/**
1173
	 * @inheritdoc
1174
	 */
1175
	public function userDeletedFromGroup($uid, $gid) {
1176
		$provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_GROUP);
1177
		$provider->userDeletedFromGroup($uid, $gid);
1178
	}
1179
1180
	/**
1181
	 * Get access list to a path. This means
1182
	 * all the users and groups that can access a given path.
1183
	 *
1184
	 * Consider:
1185
	 * -root
1186
	 * |-folder1
1187
	 *  |-folder2
1188
	 *   |-fileA
1189
	 *
1190
	 * fileA is shared with user1
1191
	 * folder2 is shared with group2
1192
	 * folder1 is shared with user2
1193
	 *
1194
	 * Then the access list will to '/folder1/folder2/fileA' is:
1195
	 * [
1196
	 * 	'users' => ['user1', 'user2'],
1197
	 *  'groups' => ['group2']
1198
	 * ]
1199
	 *
1200
	 * This is required for encryption
1201
	 *
1202
	 * @param \OCP\Files\Node $path
1203
	 */
1204
	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...
1205
	}
1206
1207
	/**
1208
	 * Create a new share
1209
	 * @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...
1210
	 */
1211
	public function newShare() {
1212
		return new \OC\Share20\Share($this->rootFolder, $this->userManager);
1213
	}
1214
1215
	/**
1216
	 * Is the share API enabled
1217
	 *
1218
	 * @return bool
1219
	 */
1220
	public function shareApiEnabled() {
1221
		return $this->config->getAppValue('core', 'shareapi_enabled', 'yes') === 'yes';
1222
	}
1223
1224
	/**
1225
	 * Is public link sharing enabled
1226
	 *
1227
	 * @return bool
1228
	 */
1229
	public function shareApiAllowLinks() {
1230
		return $this->config->getAppValue('core', 'shareapi_allow_links', 'yes') === 'yes';
1231
	}
1232
1233
	/**
1234
	 * Is password on public link requires
1235
	 *
1236
	 * @return bool
1237
	 */
1238
	public function shareApiLinkEnforcePassword() {
1239
		return $this->config->getAppValue('core', 'shareapi_enforce_links_password', 'no') === 'yes';
1240
	}
1241
1242
	/**
1243
	 * Is default expire date enabled
1244
	 *
1245
	 * @return bool
1246
	 */
1247
	public function shareApiLinkDefaultExpireDate() {
1248
		return $this->config->getAppValue('core', 'shareapi_default_expire_date', 'no') === 'yes';
1249
	}
1250
1251
	/**
1252
	 * Is default expire date enforced
1253
	 *`
1254
	 * @return bool
1255
	 */
1256
	public function shareApiLinkDefaultExpireDateEnforced() {
1257
		return $this->shareApiLinkDefaultExpireDate() &&
1258
			$this->config->getAppValue('core', 'shareapi_enforce_expire_date', 'no') === 'yes';
1259
	}
1260
1261
	/**
1262
	 * Number of default expire days
1263
	 *shareApiLinkAllowPublicUpload
1264
	 * @return int
1265
	 */
1266
	public function shareApiLinkDefaultExpireDays() {
1267
		return (int)$this->config->getAppValue('core', 'shareapi_expire_after_n_days', '7');
1268
	}
1269
1270
	/**
1271
	 * Allow public upload on link shares
1272
	 *
1273
	 * @return bool
1274
	 */
1275
	public function shareApiLinkAllowPublicUpload() {
1276
		return $this->config->getAppValue('core', 'shareapi_allow_public_upload', 'yes') === 'yes';
1277
	}
1278
1279
	/**
1280
	 * check if user can only share with group members
1281
	 * @return bool
1282
	 */
1283
	public function shareWithGroupMembersOnly() {
1284
		return $this->config->getAppValue('core', 'shareapi_only_share_with_group_members', 'no') === 'yes';
1285
	}
1286
1287
	/**
1288
	 * Check if users can share with groups
1289
	 * @return bool
1290
	 */
1291
	public function allowGroupSharing() {
1292
		return $this->config->getAppValue('core', 'shareapi_allow_group_sharing', 'yes') === 'yes';
1293
	}
1294
1295
	/**
1296
	 * Copied from \OC_Util::isSharingDisabledForUser
1297
	 *
1298
	 * TODO: Deprecate fuction from OC_Util
1299
	 *
1300
	 * @param string $userId
1301
	 * @return bool
1302
	 */
1303
	public function sharingDisabledForUser($userId) {
1304
		if ($userId === null) {
1305
			return false;
1306
		}
1307
1308
		if (isset($this->sharingDisabledForUsersCache[$userId])) {
1309
			return $this->sharingDisabledForUsersCache[$userId];
1310
		}
1311
1312
		if ($this->config->getAppValue('core', 'shareapi_exclude_groups', 'no') === 'yes') {
1313
			$groupsList = $this->config->getAppValue('core', 'shareapi_exclude_groups_list', '');
1314
			$excludedGroups = json_decode($groupsList);
1315 View Code Duplication
			if (is_null($excludedGroups)) {
1316
				$excludedGroups = explode(',', $groupsList);
1317
				$newValue = json_encode($excludedGroups);
1318
				$this->config->setAppValue('core', 'shareapi_exclude_groups_list', $newValue);
1319
			}
1320
			$user = $this->userManager->get($userId);
1321
			$usersGroups = $this->groupManager->getUserGroupIds($user);
0 ignored issues
show
Bug introduced by
It seems like $user defined by $this->userManager->get($userId) on line 1320 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...
1322 View Code Duplication
			if (!empty($usersGroups)) {
1323
				$remainingGroups = array_diff($usersGroups, $excludedGroups);
1324
				// if the user is only in groups which are disabled for sharing then
1325
				// sharing is also disabled for the user
1326
				if (empty($remainingGroups)) {
1327
					$this->sharingDisabledForUsersCache[$userId] = true;
1328
					return true;
1329
				}
1330
			}
1331
		}
1332
1333
		$this->sharingDisabledForUsersCache[$userId] = false;
1334
		return false;
1335
	}
1336
1337
	/**
1338
	 * @inheritdoc
1339
	 */
1340
	public function outgoingServer2ServerSharesAllowed() {
1341
		return $this->config->getAppValue('files_sharing', 'outgoing_server2server_share_enabled', 'yes') === 'yes';
1342
	}
1343
1344
	/**
1345
	 * @inheritdoc
1346
	 */
1347
	public function shareProviderExists($shareType) {
1348
		try {
1349
			$this->factory->getProviderForType($shareType);
1350
		} catch (ProviderException $e) {
1351
			return false;
1352
		}
1353
1354
		return true;
1355
	}
1356
1357
}
1358