Passed
Push — master ( efd025...251a4d )
by Roeland
16:39 queued 12s
created

Manager::updateSharePasswordIfNeeded()   B

Complexity

Conditions 10
Paths 51

Size

Total Lines 25
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 10
eloc 13
nc 51
nop 2
dl 0
loc 25
rs 7.6666
c 0
b 0
f 0

How to fix   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 Christoph Wurst <[email protected]>
9
 * @author Daniel Calviño Sánchez <[email protected]>
10
 * @author Jan-Christoph Borchardt <[email protected]>
11
 * @author Joas Schilling <[email protected]>
12
 * @author John Molakvoæ (skjnldsv) <[email protected]>
13
 * @author Julius Härtl <[email protected]>
14
 * @author Lukas Reschke <[email protected]>
15
 * @author Maxence Lange <[email protected]>
16
 * @author Maxence Lange <[email protected]>
17
 * @author Morris Jobke <[email protected]>
18
 * @author Pauli Järvinen <[email protected]>
19
 * @author Robin Appelman <[email protected]>
20
 * @author Roeland Jago Douma <[email protected]>
21
 * @author Thibault Coupin <[email protected]>
22
 * @author Vincent Petry <[email protected]>
23
 *
24
 * @license AGPL-3.0
25
 *
26
 * This code is free software: you can redistribute it and/or modify
27
 * it under the terms of the GNU Affero General Public License, version 3,
28
 * as published by the Free Software Foundation.
29
 *
30
 * This program is distributed in the hope that it will be useful,
31
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
32
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
33
 * GNU Affero General Public License for more details.
34
 *
35
 * You should have received a copy of the GNU Affero General Public License, version 3,
36
 * along with this program. If not, see <http://www.gnu.org/licenses/>
37
 *
38
 */
39
40
namespace OC\Share20;
41
42
use OC\Cache\CappedMemoryCache;
43
use OC\Files\Mount\MoveableMount;
44
use OC\HintException;
45
use OC\Share20\Exception\ProviderException;
46
use OCP\EventDispatcher\IEventDispatcher;
47
use OCP\Files\File;
48
use OCP\Files\Folder;
49
use OCP\Files\IRootFolder;
50
use OCP\Files\Mount\IMountManager;
51
use OCP\Files\Node;
52
use OCP\IConfig;
53
use OCP\IGroupManager;
54
use OCP\IL10N;
55
use OCP\ILogger;
56
use OCP\IURLGenerator;
57
use OCP\IUser;
58
use OCP\IUserManager;
59
use OCP\L10N\IFactory;
60
use OCP\Mail\IMailer;
61
use OCP\Security\Events\ValidatePasswordPolicyEvent;
62
use OCP\Security\IHasher;
63
use OCP\Security\ISecureRandom;
64
use OCP\Share;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, OC\Share20\Share. Consider defining an alias.

Let?s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let?s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
65
use OCP\Share\Exceptions\GenericShareException;
66
use OCP\Share\Exceptions\ShareNotFound;
67
use OCP\Share\IManager;
68
use OCP\Share\IProviderFactory;
69
use OCP\Share\IShare;
70
use OCP\Share\IShareProvider;
71
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
72
use Symfony\Component\EventDispatcher\GenericEvent;
73
74
/**
75
 * This class is the communication hub for all sharing related operations.
76
 */
77
class Manager implements IManager {
78
79
	/** @var IProviderFactory */
80
	private $factory;
81
	/** @var ILogger */
82
	private $logger;
83
	/** @var IConfig */
84
	private $config;
85
	/** @var ISecureRandom */
86
	private $secureRandom;
87
	/** @var IHasher */
88
	private $hasher;
89
	/** @var IMountManager */
90
	private $mountManager;
91
	/** @var IGroupManager */
92
	private $groupManager;
93
	/** @var IL10N */
94
	private $l;
95
	/** @var IFactory */
96
	private $l10nFactory;
97
	/** @var IUserManager */
98
	private $userManager;
99
	/** @var IRootFolder */
100
	private $rootFolder;
101
	/** @var CappedMemoryCache */
102
	private $sharingDisabledForUsersCache;
103
	/** @var EventDispatcherInterface */
104
	private $legacyDispatcher;
105
	/** @var LegacyHooks */
106
	private $legacyHooks;
107
	/** @var IMailer */
108
	private $mailer;
109
	/** @var IURLGenerator */
110
	private $urlGenerator;
111
	/** @var \OC_Defaults */
112
	private $defaults;
113
	/** @var IEventDispatcher */
114
	private $dispatcher;
115
116
117
	/**
118
	 * Manager constructor.
119
	 *
120
	 * @param ILogger $logger
121
	 * @param IConfig $config
122
	 * @param ISecureRandom $secureRandom
123
	 * @param IHasher $hasher
124
	 * @param IMountManager $mountManager
125
	 * @param IGroupManager $groupManager
126
	 * @param IL10N $l
127
	 * @param IFactory $l10nFactory
128
	 * @param IProviderFactory $factory
129
	 * @param IUserManager $userManager
130
	 * @param IRootFolder $rootFolder
131
	 * @param EventDispatcherInterface $eventDispatcher
132
	 * @param IMailer $mailer
133
	 * @param IURLGenerator $urlGenerator
134
	 * @param \OC_Defaults $defaults
135
	 */
136
	public function __construct(
137
			ILogger $logger,
138
			IConfig $config,
139
			ISecureRandom $secureRandom,
140
			IHasher $hasher,
141
			IMountManager $mountManager,
142
			IGroupManager $groupManager,
143
			IL10N $l,
144
			IFactory $l10nFactory,
145
			IProviderFactory $factory,
146
			IUserManager $userManager,
147
			IRootFolder $rootFolder,
148
			EventDispatcherInterface $legacyDispatcher,
149
			IMailer $mailer,
150
			IURLGenerator $urlGenerator,
151
			\OC_Defaults $defaults,
152
			IEventDispatcher $dispatcher
153
	) {
154
		$this->logger = $logger;
155
		$this->config = $config;
156
		$this->secureRandom = $secureRandom;
157
		$this->hasher = $hasher;
158
		$this->mountManager = $mountManager;
159
		$this->groupManager = $groupManager;
160
		$this->l = $l;
161
		$this->l10nFactory = $l10nFactory;
162
		$this->factory = $factory;
163
		$this->userManager = $userManager;
164
		$this->rootFolder = $rootFolder;
165
		$this->legacyDispatcher = $legacyDispatcher;
166
		$this->sharingDisabledForUsersCache = new CappedMemoryCache();
167
		$this->legacyHooks = new LegacyHooks($this->legacyDispatcher);
168
		$this->mailer = $mailer;
169
		$this->urlGenerator = $urlGenerator;
170
		$this->defaults = $defaults;
171
		$this->dispatcher = $dispatcher;
172
	}
173
174
	/**
175
	 * Convert from a full share id to a tuple (providerId, shareId)
176
	 *
177
	 * @param string $id
178
	 * @return string[]
179
	 */
180
	private function splitFullId($id) {
181
		return explode(':', $id, 2);
182
	}
183
184
	/**
185
	 * Verify if a password meets all requirements
186
	 *
187
	 * @param string $password
188
	 * @throws \Exception
189
	 */
190
	protected function verifyPassword($password) {
191
		if ($password === null) {
0 ignored issues
show
introduced by
The condition $password === null is always false.
Loading history...
192
			// No password is set, check if this is allowed.
193
			if ($this->shareApiLinkEnforcePassword()) {
194
				throw new \InvalidArgumentException('Passwords are enforced for link shares');
195
			}
196
197
			return;
198
		}
199
200
		// Let others verify the password
201
		try {
202
			$this->legacyDispatcher->dispatch(new ValidatePasswordPolicyEvent($password));
203
		} catch (HintException $e) {
204
			throw new \Exception($e->getHint());
205
		}
206
	}
207
208
	/**
209
	 * Check for generic requirements before creating a share
210
	 *
211
	 * @param \OCP\Share\IShare $share
212
	 * @throws \InvalidArgumentException
213
	 * @throws GenericShareException
214
	 *
215
	 * @suppress PhanUndeclaredClassMethod
216
	 */
217
	protected function generalCreateChecks(\OCP\Share\IShare $share) {
218
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) {
0 ignored issues
show
Deprecated Code introduced by
The constant OC\Share\Constants::SHARE_TYPE_USER has been deprecated: 17.0.0 - use IShare::TYPE_USER instead ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

218
		if ($share->getShareType() === /** @scrutinizer ignore-deprecated */ \OCP\Share::SHARE_TYPE_USER) {

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
219
			// We expect a valid user as sharedWith for user shares
220
			if (!$this->userManager->userExists($share->getSharedWith())) {
221
				throw new \InvalidArgumentException('SharedWith is not a valid user');
222
			}
223
		} elseif ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
0 ignored issues
show
Deprecated Code introduced by
The constant OC\Share\Constants::SHARE_TYPE_GROUP has been deprecated: 17.0.0 - use IShare::TYPE_GROUP instead ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

223
		} elseif ($share->getShareType() === /** @scrutinizer ignore-deprecated */ \OCP\Share::SHARE_TYPE_GROUP) {

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
224
			// We expect a valid group as sharedWith for group shares
225
			if (!$this->groupManager->groupExists($share->getSharedWith())) {
226
				throw new \InvalidArgumentException('SharedWith is not a valid group');
227
			}
228
		} elseif ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) {
0 ignored issues
show
Deprecated Code introduced by
The constant OC\Share\Constants::SHARE_TYPE_LINK has been deprecated: 17.0.0 - use IShare::TYPE_LINK instead ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

228
		} elseif ($share->getShareType() === /** @scrutinizer ignore-deprecated */ \OCP\Share::SHARE_TYPE_LINK) {

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
229
			if ($share->getSharedWith() !== null) {
0 ignored issues
show
introduced by
The condition $share->getSharedWith() !== null is always true.
Loading history...
230
				throw new \InvalidArgumentException('SharedWith should be empty');
231
			}
232
		} elseif ($share->getShareType() === \OCP\Share::SHARE_TYPE_REMOTE) {
0 ignored issues
show
Deprecated Code introduced by
The constant OC\Share\Constants::SHARE_TYPE_REMOTE has been deprecated: 17.0.0 - use IShare::TYPE_REMOTE instead ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

232
		} elseif ($share->getShareType() === /** @scrutinizer ignore-deprecated */ \OCP\Share::SHARE_TYPE_REMOTE) {

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
233
			if ($share->getSharedWith() === null) {
0 ignored issues
show
introduced by
The condition $share->getSharedWith() === null is always false.
Loading history...
234
				throw new \InvalidArgumentException('SharedWith should not be empty');
235
			}
236
		} elseif ($share->getShareType() === \OCP\Share::SHARE_TYPE_REMOTE_GROUP) {
0 ignored issues
show
Deprecated Code introduced by
The constant OC\Share\Constants::SHARE_TYPE_REMOTE_GROUP has been deprecated: 17.0.0 - use IShare::REMOTE_GROUP instead ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

236
		} elseif ($share->getShareType() === /** @scrutinizer ignore-deprecated */ \OCP\Share::SHARE_TYPE_REMOTE_GROUP) {

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
237
			if ($share->getSharedWith() === null) {
0 ignored issues
show
introduced by
The condition $share->getSharedWith() === null is always false.
Loading history...
238
				throw new \InvalidArgumentException('SharedWith should not be empty');
239
			}
240
		} elseif ($share->getShareType() === \OCP\Share::SHARE_TYPE_EMAIL) {
0 ignored issues
show
Deprecated Code introduced by
The constant OC\Share\Constants::SHARE_TYPE_EMAIL has been deprecated: 17.0.0 - use IShare::TYPE_EMAIL instead ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

240
		} elseif ($share->getShareType() === /** @scrutinizer ignore-deprecated */ \OCP\Share::SHARE_TYPE_EMAIL) {

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
241
			if ($share->getSharedWith() === null) {
0 ignored issues
show
introduced by
The condition $share->getSharedWith() === null is always false.
Loading history...
242
				throw new \InvalidArgumentException('SharedWith should not be empty');
243
			}
244
		} elseif ($share->getShareType() === \OCP\Share::SHARE_TYPE_CIRCLE) {
0 ignored issues
show
Deprecated Code introduced by
The constant OC\Share\Constants::SHARE_TYPE_CIRCLE has been deprecated: 17.0.0 - use IShare::TYPE_CIRCLE instead ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

244
		} elseif ($share->getShareType() === /** @scrutinizer ignore-deprecated */ \OCP\Share::SHARE_TYPE_CIRCLE) {

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
245
			$circle = \OCA\Circles\Api\v1\Circles::detailsCircle($share->getSharedWith());
0 ignored issues
show
Bug introduced by
The type OCA\Circles\Api\v1\Circles was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
246
			if ($circle === null) {
247
				throw new \InvalidArgumentException('SharedWith is not a valid circle');
248
			}
249
		} elseif ($share->getShareType() === \OCP\Share::SHARE_TYPE_ROOM) {
0 ignored issues
show
Deprecated Code introduced by
The constant OC\Share\Constants::SHARE_TYPE_ROOM has been deprecated: 17.0.0 - use IShare::TYPE_ROOM instead ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

249
		} elseif ($share->getShareType() === /** @scrutinizer ignore-deprecated */ \OCP\Share::SHARE_TYPE_ROOM) {

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
250
		} else {
251
			// We can't handle other types yet
252
			throw new \InvalidArgumentException('unknown share type');
253
		}
254
255
		// Verify the initiator of the share is set
256
		if ($share->getSharedBy() === null) {
0 ignored issues
show
introduced by
The condition $share->getSharedBy() === null is always false.
Loading history...
257
			throw new \InvalidArgumentException('SharedBy should be set');
258
		}
259
260
		// Cannot share with yourself
261
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER &&
0 ignored issues
show
Deprecated Code introduced by
The constant OC\Share\Constants::SHARE_TYPE_USER has been deprecated: 17.0.0 - use IShare::TYPE_USER instead ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

261
		if ($share->getShareType() === /** @scrutinizer ignore-deprecated */ \OCP\Share::SHARE_TYPE_USER &&

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
262
			$share->getSharedWith() === $share->getSharedBy()) {
263
			throw new \InvalidArgumentException('Can’t share with yourself');
264
		}
265
266
		// The path should be set
267
		if ($share->getNode() === null) {
268
			throw new \InvalidArgumentException('Path should be set');
269
		}
270
271
		// And it should be a file or a folder
272
		if (!($share->getNode() instanceof \OCP\Files\File) &&
273
				!($share->getNode() instanceof \OCP\Files\Folder)) {
0 ignored issues
show
introduced by
$share->getNode() is always a sub-type of OCP\Files\Folder.
Loading history...
274
			throw new \InvalidArgumentException('Path should be either a file or a folder');
275
		}
276
277
		// And you can't share your rootfolder
278
		if ($this->userManager->userExists($share->getSharedBy())) {
279
			$userFolder = $this->rootFolder->getUserFolder($share->getSharedBy());
280
			$userFolderPath = $userFolder->getPath();
281
		} else {
282
			$userFolder = $this->rootFolder->getUserFolder($share->getShareOwner());
283
			$userFolderPath = $userFolder->getPath();
284
		}
285
		if ($userFolderPath === $share->getNode()->getPath()) {
286
			throw new \InvalidArgumentException('You can’t share your root folder');
287
		}
288
289
		// Check if we actually have share permissions
290
		if (!$share->getNode()->isShareable()) {
291
			$path = $userFolder->getRelativePath($share->getNode()->getPath());
292
			$message_t = $this->l->t('You are not allowed to share %s', [$path]);
293
			throw new GenericShareException($message_t, $message_t, 404);
294
		}
295
296
		// Permissions should be set
297
		if ($share->getPermissions() === null) {
0 ignored issues
show
introduced by
The condition $share->getPermissions() === null is always false.
Loading history...
298
			throw new \InvalidArgumentException('A share requires permissions');
299
		}
300
301
		$isFederatedShare = $share->getNode()->getStorage()->instanceOfStorage('\OCA\Files_Sharing\External\Storage');
302
		$permissions = 0;
303
		$mount = $share->getNode()->getMountPoint();
304
		if (!$isFederatedShare && $share->getNode()->getOwner() && $share->getNode()->getOwner()->getUID() !== $share->getSharedBy()) {
305
			// When it's a reshare use the parent share permissions as maximum
306
			$userMountPointId = $mount->getStorageRootId();
307
			$userMountPoints = $userFolder->getById($userMountPointId);
308
			$userMountPoint = array_shift($userMountPoints);
309
310
			/* Check if this is an incoming share */
311
			$incomingShares = $this->getSharedWith($share->getSharedBy(), Share::SHARE_TYPE_USER, $userMountPoint, -1, 0);
0 ignored issues
show
Deprecated Code introduced by
The constant OC\Share\Constants::SHARE_TYPE_USER has been deprecated: 17.0.0 - use IShare::TYPE_USER instead ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

311
			$incomingShares = $this->getSharedWith($share->getSharedBy(), /** @scrutinizer ignore-deprecated */ Share::SHARE_TYPE_USER, $userMountPoint, -1, 0);

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
312
			$incomingShares = array_merge($incomingShares, $this->getSharedWith($share->getSharedBy(), Share::SHARE_TYPE_GROUP, $userMountPoint, -1, 0));
0 ignored issues
show
Deprecated Code introduced by
The constant OC\Share\Constants::SHARE_TYPE_GROUP has been deprecated: 17.0.0 - use IShare::TYPE_GROUP instead ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

312
			$incomingShares = array_merge($incomingShares, $this->getSharedWith($share->getSharedBy(), /** @scrutinizer ignore-deprecated */ Share::SHARE_TYPE_GROUP, $userMountPoint, -1, 0));

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
313
			$incomingShares = array_merge($incomingShares, $this->getSharedWith($share->getSharedBy(), Share::SHARE_TYPE_CIRCLE, $userMountPoint, -1, 0));
0 ignored issues
show
Deprecated Code introduced by
The constant OC\Share\Constants::SHARE_TYPE_CIRCLE has been deprecated: 17.0.0 - use IShare::TYPE_CIRCLE instead ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

313
			$incomingShares = array_merge($incomingShares, $this->getSharedWith($share->getSharedBy(), /** @scrutinizer ignore-deprecated */ Share::SHARE_TYPE_CIRCLE, $userMountPoint, -1, 0));

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
314
			$incomingShares = array_merge($incomingShares, $this->getSharedWith($share->getSharedBy(), Share::SHARE_TYPE_ROOM, $userMountPoint, -1, 0));
0 ignored issues
show
Deprecated Code introduced by
The constant OC\Share\Constants::SHARE_TYPE_ROOM has been deprecated: 17.0.0 - use IShare::TYPE_ROOM instead ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

314
			$incomingShares = array_merge($incomingShares, $this->getSharedWith($share->getSharedBy(), /** @scrutinizer ignore-deprecated */ Share::SHARE_TYPE_ROOM, $userMountPoint, -1, 0));

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
315
316
			/** @var \OCP\Share\IShare[] $incomingShares */
317
			if (!empty($incomingShares)) {
318
				foreach ($incomingShares as $incomingShare) {
319
					$permissions |= $incomingShare->getPermissions();
320
				}
321
			}
322
		} else {
323
			/*
324
			 * Quick fix for #23536
325
			 * Non moveable mount points do not have update and delete permissions
326
			 * while we 'most likely' do have that on the storage.
327
			 */
328
			$permissions = $share->getNode()->getPermissions();
329
			if (!($mount instanceof MoveableMount)) {
330
				$permissions |= \OCP\Constants::PERMISSION_DELETE | \OCP\Constants::PERMISSION_UPDATE;
331
			}
332
		}
333
334
		// Check that we do not share with more permissions than we have
335
		if ($share->getPermissions() & ~$permissions) {
336
			$path = $userFolder->getRelativePath($share->getNode()->getPath());
337
			$message_t = $this->l->t('Can’t increase permissions of %s', [$path]);
338
			throw new GenericShareException($message_t, $message_t, 404);
339
		}
340
341
342
		// Check that read permissions are always set
343
		// Link shares are allowed to have no read permissions to allow upload to hidden folders
344
		$noReadPermissionRequired = $share->getShareType() === \OCP\Share::SHARE_TYPE_LINK
0 ignored issues
show
Deprecated Code introduced by
The constant OC\Share\Constants::SHARE_TYPE_LINK has been deprecated: 17.0.0 - use IShare::TYPE_LINK instead ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

344
		$noReadPermissionRequired = $share->getShareType() === /** @scrutinizer ignore-deprecated */ \OCP\Share::SHARE_TYPE_LINK

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
345
			|| $share->getShareType() === \OCP\Share::SHARE_TYPE_EMAIL;
0 ignored issues
show
Deprecated Code introduced by
The constant OC\Share\Constants::SHARE_TYPE_EMAIL has been deprecated: 17.0.0 - use IShare::TYPE_EMAIL instead ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

345
			|| $share->getShareType() === /** @scrutinizer ignore-deprecated */ \OCP\Share::SHARE_TYPE_EMAIL;

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
346
		if (!$noReadPermissionRequired &&
347
			($share->getPermissions() & \OCP\Constants::PERMISSION_READ) === 0) {
348
			throw new \InvalidArgumentException('Shares need at least read permissions');
349
		}
350
351
		if ($share->getNode() instanceof \OCP\Files\File) {
352
			if ($share->getPermissions() & \OCP\Constants::PERMISSION_DELETE) {
353
				$message_t = $this->l->t('Files can’t be shared with delete permissions');
354
				throw new GenericShareException($message_t);
355
			}
356
			if ($share->getPermissions() & \OCP\Constants::PERMISSION_CREATE) {
357
				$message_t = $this->l->t('Files can’t be shared with create permissions');
358
				throw new GenericShareException($message_t);
359
			}
360
		}
361
	}
362
363
	/**
364
	 * Validate if the expiration date fits the system settings
365
	 *
366
	 * @param \OCP\Share\IShare $share The share to validate the expiration date of
367
	 * @return \OCP\Share\IShare The modified share object
368
	 * @throws GenericShareException
369
	 * @throws \InvalidArgumentException
370
	 * @throws \Exception
371
	 */
372
	protected function validateExpirationDateInternal(\OCP\Share\IShare $share) {
373
		$expirationDate = $share->getExpirationDate();
374
375
		if ($expirationDate !== null) {
376
			//Make sure the expiration date is a date
377
			$expirationDate->setTime(0, 0, 0);
378
379
			$date = new \DateTime();
380
			$date->setTime(0, 0, 0);
381
			if ($date >= $expirationDate) {
382
				$message = $this->l->t('Expiration date is in the past');
383
				throw new GenericShareException($message, $message, 404);
384
			}
385
		}
386
387
		// If expiredate is empty set a default one if there is a default
388
		$fullId = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $fullId is dead and can be removed.
Loading history...
389
		try {
390
			$fullId = $share->getFullId();
391
		} catch (\UnexpectedValueException $e) {
392
			// This is a new share
393
		}
394
395
		if ($fullId === null && $expirationDate === null && $this->shareApiInternalDefaultExpireDate()) {
0 ignored issues
show
introduced by
The condition $fullId === null is always false.
Loading history...
396
			$expirationDate = new \DateTime();
397
			$expirationDate->setTime(0,0,0);
398
399
			$days = (int)$this->config->getAppValue('core', 'internal_defaultExpDays', $this->shareApiLinkDefaultExpireDays());
400
			if ($days > $this->shareApiLinkDefaultExpireDays()) {
401
				$days = $this->shareApiLinkDefaultExpireDays();
402
			}
403
			$expirationDate->add(new \DateInterval('P'.$days.'D'));
404
		}
405
406
		// If we enforce the expiration date check that is does not exceed
407
		if ($this->shareApiInternalDefaultExpireDateEnforced()) {
408
			if ($expirationDate === null) {
409
				throw new \InvalidArgumentException('Expiration date is enforced');
410
			}
411
412
			$date = new \DateTime();
413
			$date->setTime(0, 0, 0);
414
			$date->add(new \DateInterval('P' . $this->shareApiInternalDefaultExpireDays() . 'D'));
415
			if ($date < $expirationDate) {
416
				$message = $this->l->t('Can’t set expiration date more than %s days in the future', [$this->shareApiInternalDefaultExpireDays()]);
417
				throw new GenericShareException($message, $message, 404);
418
			}
419
		}
420
421
		$accepted = true;
422
		$message = '';
423
		\OCP\Util::emitHook('\OC\Share', 'verifyExpirationDate', [
424
			'expirationDate' => &$expirationDate,
425
			'accepted' => &$accepted,
426
			'message' => &$message,
427
			'passwordSet' => $share->getPassword() !== null,
428
		]);
429
430
		if (!$accepted) {
0 ignored issues
show
introduced by
The condition $accepted is always true.
Loading history...
431
			throw new \Exception($message);
432
		}
433
434
		$share->setExpirationDate($expirationDate);
435
436
		return $share;
437
	}
438
439
	/**
440
	 * Validate if the expiration date fits the system settings
441
	 *
442
	 * @param \OCP\Share\IShare $share The share to validate the expiration date of
443
	 * @return \OCP\Share\IShare The modified share object
444
	 * @throws GenericShareException
445
	 * @throws \InvalidArgumentException
446
	 * @throws \Exception
447
	 */
448
	protected function validateExpirationDate(\OCP\Share\IShare $share) {
449
		$expirationDate = $share->getExpirationDate();
450
451
		if ($expirationDate !== null) {
452
			//Make sure the expiration date is a date
453
			$expirationDate->setTime(0, 0, 0);
454
455
			$date = new \DateTime();
456
			$date->setTime(0, 0, 0);
457
			if ($date >= $expirationDate) {
458
				$message = $this->l->t('Expiration date is in the past');
459
				throw new GenericShareException($message, $message, 404);
460
			}
461
		}
462
463
		// If expiredate is empty set a default one if there is a default
464
		$fullId = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $fullId is dead and can be removed.
Loading history...
465
		try {
466
			$fullId = $share->getFullId();
467
		} catch (\UnexpectedValueException $e) {
468
			// This is a new share
469
		}
470
471
		if ($fullId === null && $expirationDate === null && $this->shareApiLinkDefaultExpireDate()) {
0 ignored issues
show
introduced by
The condition $fullId === null is always false.
Loading history...
472
			$expirationDate = new \DateTime();
473
			$expirationDate->setTime(0,0,0);
474
475
			$days = (int)$this->config->getAppValue('core', 'link_defaultExpDays', $this->shareApiLinkDefaultExpireDays());
476
			if ($days > $this->shareApiLinkDefaultExpireDays()) {
477
				$days = $this->shareApiLinkDefaultExpireDays();
478
			}
479
			$expirationDate->add(new \DateInterval('P'.$days.'D'));
480
		}
481
482
		// If we enforce the expiration date check that is does not exceed
483
		if ($this->shareApiLinkDefaultExpireDateEnforced()) {
484
			if ($expirationDate === null) {
485
				throw new \InvalidArgumentException('Expiration date is enforced');
486
			}
487
488
			$date = new \DateTime();
489
			$date->setTime(0, 0, 0);
490
			$date->add(new \DateInterval('P' . $this->shareApiLinkDefaultExpireDays() . 'D'));
491
			if ($date < $expirationDate) {
492
				$message = $this->l->t('Can’t set expiration date more than %s days in the future', [$this->shareApiLinkDefaultExpireDays()]);
493
				throw new GenericShareException($message, $message, 404);
494
			}
495
		}
496
497
		$accepted = true;
498
		$message = '';
499
		\OCP\Util::emitHook('\OC\Share', 'verifyExpirationDate', [
500
			'expirationDate' => &$expirationDate,
501
			'accepted' => &$accepted,
502
			'message' => &$message,
503
			'passwordSet' => $share->getPassword() !== null,
504
		]);
505
506
		if (!$accepted) {
0 ignored issues
show
introduced by
The condition $accepted is always true.
Loading history...
507
			throw new \Exception($message);
508
		}
509
510
		$share->setExpirationDate($expirationDate);
511
512
		return $share;
513
	}
514
515
	/**
516
	 * Check for pre share requirements for user shares
517
	 *
518
	 * @param \OCP\Share\IShare $share
519
	 * @throws \Exception
520
	 */
521
	protected function userCreateChecks(\OCP\Share\IShare $share) {
522
		// Check if we can share with group members only
523
		if ($this->shareWithGroupMembersOnly()) {
524
			$sharedBy = $this->userManager->get($share->getSharedBy());
525
			$sharedWith = $this->userManager->get($share->getSharedWith());
526
			// Verify we can share with this user
527
			$groups = array_intersect(
528
					$this->groupManager->getUserGroupIds($sharedBy),
529
					$this->groupManager->getUserGroupIds($sharedWith)
530
			);
531
			if (empty($groups)) {
532
				throw new \Exception('Sharing is only allowed with group members');
533
			}
534
		}
535
536
		/*
537
		 * TODO: Could be costly, fix
538
		 *
539
		 * Also this is not what we want in the future.. then we want to squash identical shares.
540
		 */
541
		$provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_USER);
0 ignored issues
show
Deprecated Code introduced by
The constant OC\Share\Constants::SHARE_TYPE_USER has been deprecated: 17.0.0 - use IShare::TYPE_USER instead ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

541
		$provider = $this->factory->getProviderForType(/** @scrutinizer ignore-deprecated */ \OCP\Share::SHARE_TYPE_USER);

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
542
		$existingShares = $provider->getSharesByPath($share->getNode());
543
		foreach ($existingShares as $existingShare) {
544
			// Ignore if it is the same share
545
			try {
546
				if ($existingShare->getFullId() === $share->getFullId()) {
547
					continue;
548
				}
549
			} catch (\UnexpectedValueException $e) {
550
				//Shares are not identical
551
			}
552
553
			// Identical share already existst
554
			if ($existingShare->getSharedWith() === $share->getSharedWith() && $existingShare->getShareType() === $share->getShareType()) {
555
				throw new \Exception('Path is already shared with this user');
556
			}
557
558
			// The share is already shared with this user via a group share
559
			if ($existingShare->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
0 ignored issues
show
Deprecated Code introduced by
The constant OC\Share\Constants::SHARE_TYPE_GROUP has been deprecated: 17.0.0 - use IShare::TYPE_GROUP instead ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

559
			if ($existingShare->getShareType() === /** @scrutinizer ignore-deprecated */ \OCP\Share::SHARE_TYPE_GROUP) {

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
560
				$group = $this->groupManager->get($existingShare->getSharedWith());
561
				if (!is_null($group)) {
562
					$user = $this->userManager->get($share->getSharedWith());
563
564
					if ($group->inGroup($user) && $existingShare->getShareOwner() !== $share->getShareOwner()) {
565
						throw new \Exception('Path is already shared with this user');
566
					}
567
				}
568
			}
569
		}
570
	}
571
572
	/**
573
	 * Check for pre share requirements for group shares
574
	 *
575
	 * @param \OCP\Share\IShare $share
576
	 * @throws \Exception
577
	 */
578
	protected function groupCreateChecks(\OCP\Share\IShare $share) {
579
		// Verify group shares are allowed
580
		if (!$this->allowGroupSharing()) {
581
			throw new \Exception('Group sharing is now allowed');
582
		}
583
584
		// Verify if the user can share with this group
585
		if ($this->shareWithGroupMembersOnly()) {
586
			$sharedBy = $this->userManager->get($share->getSharedBy());
587
			$sharedWith = $this->groupManager->get($share->getSharedWith());
588
			if (is_null($sharedWith) || !$sharedWith->inGroup($sharedBy)) {
589
				throw new \Exception('Sharing is only allowed within your own groups');
590
			}
591
		}
592
593
		/*
594
		 * TODO: Could be costly, fix
595
		 *
596
		 * Also this is not what we want in the future.. then we want to squash identical shares.
597
		 */
598
		$provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_GROUP);
0 ignored issues
show
Deprecated Code introduced by
The constant OC\Share\Constants::SHARE_TYPE_GROUP has been deprecated: 17.0.0 - use IShare::TYPE_GROUP instead ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

598
		$provider = $this->factory->getProviderForType(/** @scrutinizer ignore-deprecated */ \OCP\Share::SHARE_TYPE_GROUP);

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
599
		$existingShares = $provider->getSharesByPath($share->getNode());
600
		foreach ($existingShares as $existingShare) {
601
			try {
602
				if ($existingShare->getFullId() === $share->getFullId()) {
603
					continue;
604
				}
605
			} catch (\UnexpectedValueException $e) {
606
				//It is a new share so just continue
607
			}
608
609
			if ($existingShare->getSharedWith() === $share->getSharedWith() && $existingShare->getShareType() === $share->getShareType()) {
610
				throw new \Exception('Path is already shared with this group');
611
			}
612
		}
613
	}
614
615
	/**
616
	 * Check for pre share requirements for link shares
617
	 *
618
	 * @param \OCP\Share\IShare $share
619
	 * @throws \Exception
620
	 */
621
	protected function linkCreateChecks(\OCP\Share\IShare $share) {
622
		// Are link shares allowed?
623
		if (!$this->shareApiAllowLinks()) {
624
			throw new \Exception('Link sharing is not allowed');
625
		}
626
627
		// Check if public upload is allowed
628
		if (!$this->shareApiLinkAllowPublicUpload() &&
629
			($share->getPermissions() & (\OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE))) {
630
			throw new \InvalidArgumentException('Public upload is not allowed');
631
		}
632
	}
633
634
	/**
635
	 * To make sure we don't get invisible link shares we set the parent
636
	 * of a link if it is a reshare. This is a quick word around
637
	 * until we can properly display multiple link shares in the UI
638
	 *
639
	 * See: https://github.com/owncloud/core/issues/22295
640
	 *
641
	 * FIXME: Remove once multiple link shares can be properly displayed
642
	 *
643
	 * @param \OCP\Share\IShare $share
644
	 */
645
	protected function setLinkParent(\OCP\Share\IShare $share) {
646
647
		// No sense in checking if the method is not there.
648
		if (method_exists($share, 'setParent')) {
649
			$storage = $share->getNode()->getStorage();
650
			if ($storage->instanceOfStorage('\OCA\Files_Sharing\ISharedStorage')) {
651
				/** @var \OCA\Files_Sharing\SharedStorage $storage */
652
				$share->setParent($storage->getShareId());
653
			}
654
		}
655
	}
656
657
	/**
658
	 * @param File|Folder $path
659
	 */
660
	protected function pathCreateChecks($path) {
661
		// Make sure that we do not share a path that contains a shared mountpoint
662
		if ($path instanceof \OCP\Files\Folder) {
663
			$mounts = $this->mountManager->findIn($path->getPath());
664
			foreach ($mounts as $mount) {
665
				if ($mount->getStorage()->instanceOfStorage('\OCA\Files_Sharing\ISharedStorage')) {
666
					throw new \InvalidArgumentException('Path contains files shared with you');
667
				}
668
			}
669
		}
670
	}
671
672
	/**
673
	 * Check if the user that is sharing can actually share
674
	 *
675
	 * @param \OCP\Share\IShare $share
676
	 * @throws \Exception
677
	 */
678
	protected function canShare(\OCP\Share\IShare $share) {
679
		if (!$this->shareApiEnabled()) {
680
			throw new \Exception('Sharing is disabled');
681
		}
682
683
		if ($this->sharingDisabledForUser($share->getSharedBy())) {
684
			throw new \Exception('Sharing is disabled for you');
685
		}
686
	}
687
688
	/**
689
	 * Share a path
690
	 *
691
	 * @param \OCP\Share\IShare $share
692
	 * @return Share The share object
693
	 * @throws \Exception
694
	 *
695
	 * TODO: handle link share permissions or check them
696
	 */
697
	public function createShare(\OCP\Share\IShare $share) {
698
		$this->canShare($share);
699
700
		$this->generalCreateChecks($share);
701
702
		// Verify if there are any issues with the path
703
		$this->pathCreateChecks($share->getNode());
704
705
		/*
706
		 * On creation of a share the owner is always the owner of the path
707
		 * Except for mounted federated shares.
708
		 */
709
		$storage = $share->getNode()->getStorage();
710
		if ($storage->instanceOfStorage('OCA\Files_Sharing\External\Storage')) {
711
			$parent = $share->getNode()->getParent();
712
			while ($parent->getStorage()->instanceOfStorage('OCA\Files_Sharing\External\Storage')) {
713
				$parent = $parent->getParent();
714
			}
715
			$share->setShareOwner($parent->getOwner()->getUID());
716
		} else {
717
			if ($share->getNode()->getOwner()) {
718
				$share->setShareOwner($share->getNode()->getOwner()->getUID());
719
			} else {
720
				$share->setShareOwner($share->getSharedBy());
721
			}
722
		}
723
724
		//Verify share type
725
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) {
0 ignored issues
show
Deprecated Code introduced by
The constant OC\Share\Constants::SHARE_TYPE_USER has been deprecated: 17.0.0 - use IShare::TYPE_USER instead ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

725
		if ($share->getShareType() === /** @scrutinizer ignore-deprecated */ \OCP\Share::SHARE_TYPE_USER) {

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
726
			$this->userCreateChecks($share);
727
728
			//Verify the expiration date
729
			$share = $this->validateExpirationDateInternal($share);
730
		} elseif ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
0 ignored issues
show
Deprecated Code introduced by
The constant OC\Share\Constants::SHARE_TYPE_GROUP has been deprecated: 17.0.0 - use IShare::TYPE_GROUP instead ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

730
		} elseif ($share->getShareType() === /** @scrutinizer ignore-deprecated */ \OCP\Share::SHARE_TYPE_GROUP) {

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
731
			$this->groupCreateChecks($share);
732
733
			//Verify the expiration date
734
			$share = $this->validateExpirationDateInternal($share);
735
		} elseif ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) {
0 ignored issues
show
Deprecated Code introduced by
The constant OC\Share\Constants::SHARE_TYPE_LINK has been deprecated: 17.0.0 - use IShare::TYPE_LINK instead ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

735
		} elseif ($share->getShareType() === /** @scrutinizer ignore-deprecated */ \OCP\Share::SHARE_TYPE_LINK) {

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
736
			$this->linkCreateChecks($share);
737
			$this->setLinkParent($share);
738
739
			/*
740
			 * For now ignore a set token.
741
			 */
742
			$share->setToken(
743
				$this->secureRandom->generate(
744
					\OC\Share\Constants::TOKEN_LENGTH,
745
					\OCP\Security\ISecureRandom::CHAR_HUMAN_READABLE
746
				)
747
			);
748
749
			//Verify the expiration date
750
			$share = $this->validateExpirationDate($share);
751
752
			//Verify the password
753
			$this->verifyPassword($share->getPassword());
754
755
			// If a password is set. Hash it!
756
			if ($share->getPassword() !== null) {
0 ignored issues
show
introduced by
The condition $share->getPassword() !== null is always true.
Loading history...
757
				$share->setPassword($this->hasher->hash($share->getPassword()));
758
			}
759
		} elseif ($share->getShareType() === \OCP\Share::SHARE_TYPE_EMAIL) {
0 ignored issues
show
Deprecated Code introduced by
The constant OC\Share\Constants::SHARE_TYPE_EMAIL has been deprecated: 17.0.0 - use IShare::TYPE_EMAIL instead ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

759
		} elseif ($share->getShareType() === /** @scrutinizer ignore-deprecated */ \OCP\Share::SHARE_TYPE_EMAIL) {

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
760
			$share->setToken(
761
				$this->secureRandom->generate(
762
					\OC\Share\Constants::TOKEN_LENGTH,
763
					\OCP\Security\ISecureRandom::CHAR_HUMAN_READABLE
764
				)
765
			);
766
		}
767
768
		// Cannot share with the owner
769
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER &&
0 ignored issues
show
Deprecated Code introduced by
The constant OC\Share\Constants::SHARE_TYPE_USER has been deprecated: 17.0.0 - use IShare::TYPE_USER instead ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

769
		if ($share->getShareType() === /** @scrutinizer ignore-deprecated */ \OCP\Share::SHARE_TYPE_USER &&

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
770
			$share->getSharedWith() === $share->getShareOwner()) {
771
			throw new \InvalidArgumentException('Can’t share with the share owner');
772
		}
773
774
		// Generate the target
775
		$target = $this->config->getSystemValue('share_folder', '/') .'/'. $share->getNode()->getName();
776
		$target = \OC\Files\Filesystem::normalizePath($target);
777
		$share->setTarget($target);
778
779
		// Pre share event
780
		$event = new GenericEvent($share);
781
		$this->legacyDispatcher->dispatch('OCP\Share::preShare', $event);
0 ignored issues
show
Unused Code introduced by
The call to Symfony\Contracts\EventD...erInterface::dispatch() has too many arguments starting with $event. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

781
		$this->legacyDispatcher->/** @scrutinizer ignore-call */ 
782
                           dispatch('OCP\Share::preShare', $event);

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

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

Loading history...
Bug introduced by
'OCP\Share::preShare' of type string is incompatible with the type object expected by parameter $event of Symfony\Contracts\EventD...erInterface::dispatch(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

781
		$this->legacyDispatcher->dispatch(/** @scrutinizer ignore-type */ 'OCP\Share::preShare', $event);
Loading history...
782
		if ($event->isPropagationStopped() && $event->hasArgument('error')) {
0 ignored issues
show
Deprecated Code introduced by
The function Symfony\Component\EventD...:isPropagationStopped() has been deprecated: since Symfony 4.3, use "Symfony\Contracts\EventDispatcher\Event" instead ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

782
		if (/** @scrutinizer ignore-deprecated */ $event->isPropagationStopped() && $event->hasArgument('error')) {

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
783
			throw new \Exception($event->getArgument('error'));
784
		}
785
786
		$oldShare = $share;
787
		$provider = $this->factory->getProviderForType($share->getShareType());
788
		$share = $provider->create($share);
789
		//reuse the node we already have
790
		$share->setNode($oldShare->getNode());
791
792
		// Reset the target if it is null for the new share
793
		if ($share->getTarget() === '') {
794
			$share->setTarget($target);
795
		}
796
797
		// Post share event
798
		$event = new GenericEvent($share);
799
		$this->legacyDispatcher->dispatch('OCP\Share::postShare', $event);
800
801
		$this->dispatcher->dispatchTyped(new Share\Events\ShareCreatedEvent($share));
802
803
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) {
0 ignored issues
show
Deprecated Code introduced by
The constant OC\Share\Constants::SHARE_TYPE_USER has been deprecated: 17.0.0 - use IShare::TYPE_USER instead ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

803
		if ($share->getShareType() === /** @scrutinizer ignore-deprecated */ \OCP\Share::SHARE_TYPE_USER) {

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
804
			$mailSend = $share->getMailSend();
805
			if ($mailSend === true) {
806
				$user = $this->userManager->get($share->getSharedWith());
807
				if ($user !== null) {
808
					$emailAddress = $user->getEMailAddress();
809
					if ($emailAddress !== null && $emailAddress !== '') {
810
						$userLang = $this->config->getUserValue($share->getSharedWith(), 'core', 'lang', null);
811
						$l = $this->l10nFactory->get('lib', $userLang);
812
						$this->sendMailNotification(
813
							$l,
814
							$share->getNode()->getName(),
815
							$this->urlGenerator->linkToRouteAbsolute('files_sharing.Accept.accept', ['shareId' => $share->getFullId()]),
816
							$share->getSharedBy(),
817
							$emailAddress,
818
							$share->getExpirationDate()
819
						);
820
						$this->logger->debug('Sent share notification to ' . $emailAddress . ' for share with ID ' . $share->getId(), ['app' => 'share']);
821
					} else {
822
						$this->logger->debug('Share notification not sent to ' . $share->getSharedWith() . ' because email address is not set.', ['app' => 'share']);
823
					}
824
				} else {
825
					$this->logger->debug('Share notification not sent to ' . $share->getSharedWith() . ' because user could not be found.', ['app' => 'share']);
826
				}
827
			} else {
828
				$this->logger->debug('Share notification not sent because mailsend is false.', ['app' => 'share']);
829
			}
830
		}
831
832
		return $share;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $share returns the type OCP\Share\IShare which is incompatible with the documented return type OCP\Share.
Loading history...
833
	}
834
835
	/**
836
	 * Send mail notifications
837
	 *
838
	 * This method will catch and log mail transmission errors
839
	 *
840
	 * @param IL10N $l Language of the recipient
841
	 * @param string $filename file/folder name
842
	 * @param string $link link to the file/folder
843
	 * @param string $initiator user ID of share sender
844
	 * @param string $shareWith email address of share receiver
845
	 * @param \DateTime|null $expiration
846
	 */
847
	protected function sendMailNotification(IL10N $l,
848
											$filename,
849
											$link,
850
											$initiator,
851
											$shareWith,
852
											\DateTime $expiration = null) {
853
		$initiatorUser = $this->userManager->get($initiator);
854
		$initiatorDisplayName = ($initiatorUser instanceof IUser) ? $initiatorUser->getDisplayName() : $initiator;
855
856
		$message = $this->mailer->createMessage();
857
858
		$emailTemplate = $this->mailer->createEMailTemplate('files_sharing.RecipientNotification', [
859
			'filename' => $filename,
860
			'link' => $link,
861
			'initiator' => $initiatorDisplayName,
862
			'expiration' => $expiration,
863
			'shareWith' => $shareWith,
864
		]);
865
866
		$emailTemplate->setSubject($l->t('%1$s shared »%2$s« with you', [$initiatorDisplayName, $filename]));
867
		$emailTemplate->addHeader();
868
		$emailTemplate->addHeading($l->t('%1$s shared »%2$s« with you', [$initiatorDisplayName, $filename]), false);
869
		$text = $l->t('%1$s shared »%2$s« with you.', [$initiatorDisplayName, $filename]);
870
871
		$emailTemplate->addBodyText(
872
			htmlspecialchars($text . ' ' . $l->t('Click the button below to open it.')),
873
			$text
874
		);
875
		$emailTemplate->addBodyButton(
876
			$l->t('Open »%s«', [$filename]),
877
			$link
878
		);
879
880
		$message->setTo([$shareWith]);
881
882
		// The "From" contains the sharers name
883
		$instanceName = $this->defaults->getName();
884
		$senderName = $l->t(
885
			'%1$s via %2$s',
886
			[
887
				$initiatorDisplayName,
888
				$instanceName
889
			]
890
		);
891
		$message->setFrom([\OCP\Util::getDefaultEmailAddress($instanceName) => $senderName]);
892
893
		// The "Reply-To" is set to the sharer if an mail address is configured
894
		// also the default footer contains a "Do not reply" which needs to be adjusted.
895
		$initiatorEmail = $initiatorUser->getEMailAddress();
896
		if ($initiatorEmail !== null) {
897
			$message->setReplyTo([$initiatorEmail => $initiatorDisplayName]);
898
			$emailTemplate->addFooter($instanceName . ($this->defaults->getSlogan($l->getLanguageCode()) !== '' ? ' - ' . $this->defaults->getSlogan($l->getLanguageCode()) : ''));
899
		} else {
900
			$emailTemplate->addFooter('', $l->getLanguageCode());
901
		}
902
903
		$message->useTemplate($emailTemplate);
904
		try {
905
			$failedRecipients = $this->mailer->send($message);
906
			if (!empty($failedRecipients)) {
907
				$this->logger->error('Share notification mail could not be sent to: ' . implode(', ', $failedRecipients));
908
				return;
909
			}
910
		} catch (\Exception $e) {
911
			$this->logger->logException($e, ['message' => 'Share notification mail could not be sent']);
912
		}
913
	}
914
915
	/**
916
	 * Update a share
917
	 *
918
	 * @param \OCP\Share\IShare $share
919
	 * @return \OCP\Share\IShare The share object
920
	 * @throws \InvalidArgumentException
921
	 */
922
	public function updateShare(\OCP\Share\IShare $share) {
923
		$expirationDateUpdated = false;
924
925
		$this->canShare($share);
926
927
		try {
928
			$originalShare = $this->getShareById($share->getFullId());
929
		} catch (\UnexpectedValueException $e) {
930
			throw new \InvalidArgumentException('Share does not have a full id');
931
		}
932
933
		// We can't change the share type!
934
		if ($share->getShareType() !== $originalShare->getShareType()) {
935
			throw new \InvalidArgumentException('Can’t change share type');
936
		}
937
938
		// We can only change the recipient on user shares
939
		if ($share->getSharedWith() !== $originalShare->getSharedWith() &&
940
			$share->getShareType() !== \OCP\Share::SHARE_TYPE_USER) {
0 ignored issues
show
Deprecated Code introduced by
The constant OC\Share\Constants::SHARE_TYPE_USER has been deprecated: 17.0.0 - use IShare::TYPE_USER instead ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

940
			$share->getShareType() !== /** @scrutinizer ignore-deprecated */ \OCP\Share::SHARE_TYPE_USER) {

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
941
			throw new \InvalidArgumentException('Can only update recipient on user shares');
942
		}
943
944
		// Cannot share with the owner
945
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER &&
0 ignored issues
show
Deprecated Code introduced by
The constant OC\Share\Constants::SHARE_TYPE_USER has been deprecated: 17.0.0 - use IShare::TYPE_USER instead ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

945
		if ($share->getShareType() === /** @scrutinizer ignore-deprecated */ \OCP\Share::SHARE_TYPE_USER &&

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
946
			$share->getSharedWith() === $share->getShareOwner()) {
947
			throw new \InvalidArgumentException('Can’t share with the share owner');
948
		}
949
950
		$this->generalCreateChecks($share);
951
952
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) {
0 ignored issues
show
Deprecated Code introduced by
The constant OC\Share\Constants::SHARE_TYPE_USER has been deprecated: 17.0.0 - use IShare::TYPE_USER instead ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

952
		if ($share->getShareType() === /** @scrutinizer ignore-deprecated */ \OCP\Share::SHARE_TYPE_USER) {

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
953
			$this->userCreateChecks($share);
954
955
			if ($share->getExpirationDate() != $originalShare->getExpirationDate()) {
956
				//Verify the expiration date
957
				$this->validateExpirationDate($share);
958
				$expirationDateUpdated = true;
959
			}
960
		} elseif ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
0 ignored issues
show
Deprecated Code introduced by
The constant OC\Share\Constants::SHARE_TYPE_GROUP has been deprecated: 17.0.0 - use IShare::TYPE_GROUP instead ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

960
		} elseif ($share->getShareType() === /** @scrutinizer ignore-deprecated */ \OCP\Share::SHARE_TYPE_GROUP) {

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
961
			$this->groupCreateChecks($share);
962
963
			if ($share->getExpirationDate() != $originalShare->getExpirationDate()) {
964
				//Verify the expiration date
965
				$this->validateExpirationDate($share);
966
				$expirationDateUpdated = true;
967
			}
968
		} elseif ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) {
0 ignored issues
show
Deprecated Code introduced by
The constant OC\Share\Constants::SHARE_TYPE_LINK has been deprecated: 17.0.0 - use IShare::TYPE_LINK instead ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

968
		} elseif ($share->getShareType() === /** @scrutinizer ignore-deprecated */ \OCP\Share::SHARE_TYPE_LINK) {

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
969
			$this->linkCreateChecks($share);
970
971
			$plainTextPassword = $share->getPassword();
972
973
			$this->updateSharePasswordIfNeeded($share, $originalShare);
974
975
			if (empty($plainTextPassword) && $share->getSendPasswordByTalk()) {
976
				throw new \InvalidArgumentException('Can’t enable sending the password by Talk with an empty password');
977
			}
978
979
			if ($share->getExpirationDate() != $originalShare->getExpirationDate()) {
980
				//Verify the expiration date
981
				$this->validateExpirationDate($share);
982
				$expirationDateUpdated = true;
983
			}
984
		} elseif ($share->getShareType() === \OCP\Share::SHARE_TYPE_EMAIL) {
0 ignored issues
show
Deprecated Code introduced by
The constant OC\Share\Constants::SHARE_TYPE_EMAIL has been deprecated: 17.0.0 - use IShare::TYPE_EMAIL instead ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

984
		} elseif ($share->getShareType() === /** @scrutinizer ignore-deprecated */ \OCP\Share::SHARE_TYPE_EMAIL) {

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
985
			// The new password is not set again if it is the same as the old
986
			// one.
987
			$plainTextPassword = $share->getPassword();
988
			if (!empty($plainTextPassword) && !$this->updateSharePasswordIfNeeded($share, $originalShare)) {
989
				$plainTextPassword = null;
990
			}
991
			if (empty($plainTextPassword) && !$originalShare->getSendPasswordByTalk() && $share->getSendPasswordByTalk()) {
992
				// If the same password was already sent by mail the recipient
993
				// would already have access to the share without having to call
994
				// the sharer to verify her identity
995
				throw new \InvalidArgumentException('Can’t enable sending the password by Talk without setting a new password');
996
			} elseif (empty($plainTextPassword) && $originalShare->getSendPasswordByTalk() && !$share->getSendPasswordByTalk()) {
997
				throw new \InvalidArgumentException('Can’t disable sending the password by Talk without setting a new password');
998
			}
999
		}
1000
1001
		$this->pathCreateChecks($share->getNode());
1002
1003
		// Now update the share!
1004
		$provider = $this->factory->getProviderForType($share->getShareType());
1005
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_EMAIL) {
0 ignored issues
show
Deprecated Code introduced by
The constant OC\Share\Constants::SHARE_TYPE_EMAIL has been deprecated: 17.0.0 - use IShare::TYPE_EMAIL instead ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

1005
		if ($share->getShareType() === /** @scrutinizer ignore-deprecated */ \OCP\Share::SHARE_TYPE_EMAIL) {

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
1006
			$share = $provider->update($share, $plainTextPassword);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $plainTextPassword does not seem to be defined for all execution paths leading up to this point.
Loading history...
Unused Code introduced by
The call to OCP\Share\IShareProvider::update() has too many arguments starting with $plainTextPassword. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1006
			/** @scrutinizer ignore-call */ 
1007
   $share = $provider->update($share, $plainTextPassword);

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

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

Loading history...
1007
		} else {
1008
			$share = $provider->update($share);
1009
		}
1010
1011
		if ($expirationDateUpdated === true) {
1012
			\OC_Hook::emit(Share::class, 'post_set_expiration_date', [
1013
				'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder',
1014
				'itemSource' => $share->getNode()->getId(),
1015
				'date' => $share->getExpirationDate(),
1016
				'uidOwner' => $share->getSharedBy(),
1017
			]);
1018
		}
1019
1020
		if ($share->getPassword() !== $originalShare->getPassword()) {
1021
			\OC_Hook::emit(Share::class, 'post_update_password', [
1022
				'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder',
1023
				'itemSource' => $share->getNode()->getId(),
1024
				'uidOwner' => $share->getSharedBy(),
1025
				'token' => $share->getToken(),
1026
				'disabled' => is_null($share->getPassword()),
1027
			]);
1028
		}
1029
1030
		if ($share->getPermissions() !== $originalShare->getPermissions()) {
1031
			if ($this->userManager->userExists($share->getShareOwner())) {
1032
				$userFolder = $this->rootFolder->getUserFolder($share->getShareOwner());
1033
			} else {
1034
				$userFolder = $this->rootFolder->getUserFolder($share->getSharedBy());
1035
			}
1036
			\OC_Hook::emit(Share::class, 'post_update_permissions', [
1037
				'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder',
1038
				'itemSource' => $share->getNode()->getId(),
1039
				'shareType' => $share->getShareType(),
1040
				'shareWith' => $share->getSharedWith(),
1041
				'uidOwner' => $share->getSharedBy(),
1042
				'permissions' => $share->getPermissions(),
1043
				'path' => $userFolder->getRelativePath($share->getNode()->getPath()),
1044
			]);
1045
		}
1046
1047
		return $share;
1048
	}
1049
1050
	/**
1051
	 * Accept a share.
1052
	 *
1053
	 * @param IShare $share
1054
	 * @param string $recipientId
1055
	 * @return IShare The share object
1056
	 * @throws \InvalidArgumentException
1057
	 * @since 9.0.0
1058
	 */
1059
	public function acceptShare(IShare $share, string $recipientId): IShare {
1060
		[$providerId, ] = $this->splitFullId($share->getFullId());
1061
		$provider = $this->factory->getProvider($providerId);
1062
1063
		if (!method_exists($provider, 'acceptShare')) {
1064
			// TODO FIX ME
1065
			throw new \InvalidArgumentException('Share provider does not support accepting');
1066
		}
1067
		$provider->acceptShare($share, $recipientId);
1068
		$event = new GenericEvent($share);
1069
		$this->legacyDispatcher->dispatch('OCP\Share::postAcceptShare', $event);
0 ignored issues
show
Unused Code introduced by
The call to Symfony\Contracts\EventD...erInterface::dispatch() has too many arguments starting with $event. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1069
		$this->legacyDispatcher->/** @scrutinizer ignore-call */ 
1070
                           dispatch('OCP\Share::postAcceptShare', $event);

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

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

Loading history...
Bug introduced by
'OCP\Share::postAcceptShare' of type string is incompatible with the type object expected by parameter $event of Symfony\Contracts\EventD...erInterface::dispatch(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1069
		$this->legacyDispatcher->dispatch(/** @scrutinizer ignore-type */ 'OCP\Share::postAcceptShare', $event);
Loading history...
1070
1071
		return $share;
1072
	}
1073
1074
	/**
1075
	 * Updates the password of the given share if it is not the same as the
1076
	 * password of the original share.
1077
	 *
1078
	 * @param \OCP\Share\IShare $share the share to update its password.
1079
	 * @param \OCP\Share\IShare $originalShare the original share to compare its
1080
	 *        password with.
1081
	 * @return boolean whether the password was updated or not.
1082
	 */
1083
	private function updateSharePasswordIfNeeded(\OCP\Share\IShare $share, \OCP\Share\IShare $originalShare) {
1084
		$passwordsAreDifferent = ($share->getPassword() !== $originalShare->getPassword()) &&
1085
									(($share->getPassword() !== null && $originalShare->getPassword() === null) ||
0 ignored issues
show
introduced by
The condition $originalShare->getPassword() === null is always false.
Loading history...
1086
									 ($share->getPassword() === null && $originalShare->getPassword() !== null) ||
0 ignored issues
show
introduced by
The condition $share->getPassword() === null is always false.
Loading history...
1087
									 ($share->getPassword() !== null && $originalShare->getPassword() !== null &&
1088
										!$this->hasher->verify($share->getPassword(), $originalShare->getPassword())));
1089
1090
		// Password updated.
1091
		if ($passwordsAreDifferent) {
1092
			//Verify the password
1093
			$this->verifyPassword($share->getPassword());
1094
1095
			// If a password is set. Hash it!
1096
			if ($share->getPassword() !== null) {
0 ignored issues
show
introduced by
The condition $share->getPassword() !== null is always true.
Loading history...
1097
				$share->setPassword($this->hasher->hash($share->getPassword()));
1098
1099
				return true;
1100
			}
1101
		} else {
1102
			// Reset the password to the original one, as it is either the same
1103
			// as the "new" password or a hashed version of it.
1104
			$share->setPassword($originalShare->getPassword());
1105
		}
1106
1107
		return false;
1108
	}
1109
1110
	/**
1111
	 * Delete all the children of this share
1112
	 * FIXME: remove once https://github.com/owncloud/core/pull/21660 is in
1113
	 *
1114
	 * @param \OCP\Share\IShare $share
1115
	 * @return \OCP\Share\IShare[] List of deleted shares
1116
	 */
1117
	protected function deleteChildren(\OCP\Share\IShare $share) {
1118
		$deletedShares = [];
1119
1120
		$provider = $this->factory->getProviderForType($share->getShareType());
1121
1122
		foreach ($provider->getChildren($share) as $child) {
0 ignored issues
show
Bug introduced by
The method getChildren() does not exist on OCP\Share\IShareProvider. Since it exists in all sub-types, consider adding an abstract or default implementation to OCP\Share\IShareProvider. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1122
		foreach ($provider->/** @scrutinizer ignore-call */ getChildren($share) as $child) {
Loading history...
1123
			$deletedChildren = $this->deleteChildren($child);
1124
			$deletedShares = array_merge($deletedShares, $deletedChildren);
1125
1126
			$provider->delete($child);
1127
			$deletedShares[] = $child;
1128
		}
1129
1130
		return $deletedShares;
1131
	}
1132
1133
	/**
1134
	 * Delete a share
1135
	 *
1136
	 * @param \OCP\Share\IShare $share
1137
	 * @throws ShareNotFound
1138
	 * @throws \InvalidArgumentException
1139
	 */
1140
	public function deleteShare(\OCP\Share\IShare $share) {
1141
		try {
1142
			$share->getFullId();
1143
		} catch (\UnexpectedValueException $e) {
1144
			throw new \InvalidArgumentException('Share does not have a full id');
1145
		}
1146
1147
		$event = new GenericEvent($share);
1148
		$this->legacyDispatcher->dispatch('OCP\Share::preUnshare', $event);
0 ignored issues
show
Bug introduced by
'OCP\Share::preUnshare' of type string is incompatible with the type object expected by parameter $event of Symfony\Contracts\EventD...erInterface::dispatch(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1148
		$this->legacyDispatcher->dispatch(/** @scrutinizer ignore-type */ 'OCP\Share::preUnshare', $event);
Loading history...
Unused Code introduced by
The call to Symfony\Contracts\EventD...erInterface::dispatch() has too many arguments starting with $event. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1148
		$this->legacyDispatcher->/** @scrutinizer ignore-call */ 
1149
                           dispatch('OCP\Share::preUnshare', $event);

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

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

Loading history...
1149
1150
		// Get all children and delete them as well
1151
		$deletedShares = $this->deleteChildren($share);
1152
1153
		// Do the actual delete
1154
		$provider = $this->factory->getProviderForType($share->getShareType());
1155
		$provider->delete($share);
1156
1157
		// All the deleted shares caused by this delete
1158
		$deletedShares[] = $share;
1159
1160
		// Emit post hook
1161
		$event->setArgument('deletedShares', $deletedShares);
1162
		$this->legacyDispatcher->dispatch('OCP\Share::postUnshare', $event);
1163
	}
1164
1165
1166
	/**
1167
	 * Unshare a file as the recipient.
1168
	 * This can be different from a regular delete for example when one of
1169
	 * the users in a groups deletes that share. But the provider should
1170
	 * handle this.
1171
	 *
1172
	 * @param \OCP\Share\IShare $share
1173
	 * @param string $recipientId
1174
	 */
1175
	public function deleteFromSelf(\OCP\Share\IShare $share, $recipientId) {
1176
		list($providerId, ) = $this->splitFullId($share->getFullId());
1177
		$provider = $this->factory->getProvider($providerId);
1178
1179
		$provider->deleteFromSelf($share, $recipientId);
1180
		$event = new GenericEvent($share);
1181
		$this->legacyDispatcher->dispatch('OCP\Share::postUnshareFromSelf', $event);
0 ignored issues
show
Unused Code introduced by
The call to Symfony\Contracts\EventD...erInterface::dispatch() has too many arguments starting with $event. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1181
		$this->legacyDispatcher->/** @scrutinizer ignore-call */ 
1182
                           dispatch('OCP\Share::postUnshareFromSelf', $event);

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

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

Loading history...
Bug introduced by
'OCP\Share::postUnshareFromSelf' of type string is incompatible with the type object expected by parameter $event of Symfony\Contracts\EventD...erInterface::dispatch(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1181
		$this->legacyDispatcher->dispatch(/** @scrutinizer ignore-type */ 'OCP\Share::postUnshareFromSelf', $event);
Loading history...
1182
	}
1183
1184
	public function restoreShare(IShare $share, string $recipientId): IShare {
1185
		list($providerId, ) = $this->splitFullId($share->getFullId());
1186
		$provider = $this->factory->getProvider($providerId);
1187
1188
		return $provider->restore($share, $recipientId);
1189
	}
1190
1191
	/**
1192
	 * @inheritdoc
1193
	 */
1194
	public function moveShare(\OCP\Share\IShare $share, $recipientId) {
1195
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) {
0 ignored issues
show
Deprecated Code introduced by
The constant OC\Share\Constants::SHARE_TYPE_LINK has been deprecated: 17.0.0 - use IShare::TYPE_LINK instead ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

1195
		if ($share->getShareType() === /** @scrutinizer ignore-deprecated */ \OCP\Share::SHARE_TYPE_LINK) {

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
1196
			throw new \InvalidArgumentException('Can’t change target of link share');
1197
		}
1198
1199
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER && $share->getSharedWith() !== $recipientId) {
0 ignored issues
show
Deprecated Code introduced by
The constant OC\Share\Constants::SHARE_TYPE_USER has been deprecated: 17.0.0 - use IShare::TYPE_USER instead ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

1199
		if ($share->getShareType() === /** @scrutinizer ignore-deprecated */ \OCP\Share::SHARE_TYPE_USER && $share->getSharedWith() !== $recipientId) {

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
1200
			throw new \InvalidArgumentException('Invalid recipient');
1201
		}
1202
1203
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
0 ignored issues
show
Deprecated Code introduced by
The constant OC\Share\Constants::SHARE_TYPE_GROUP has been deprecated: 17.0.0 - use IShare::TYPE_GROUP instead ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

1203
		if ($share->getShareType() === /** @scrutinizer ignore-deprecated */ \OCP\Share::SHARE_TYPE_GROUP) {

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
1204
			$sharedWith = $this->groupManager->get($share->getSharedWith());
1205
			if (is_null($sharedWith)) {
1206
				throw new \InvalidArgumentException('Group "' . $share->getSharedWith() . '" does not exist');
1207
			}
1208
			$recipient = $this->userManager->get($recipientId);
1209
			if (!$sharedWith->inGroup($recipient)) {
1210
				throw new \InvalidArgumentException('Invalid recipient');
1211
			}
1212
		}
1213
1214
		list($providerId, ) = $this->splitFullId($share->getFullId());
1215
		$provider = $this->factory->getProvider($providerId);
1216
1217
		$provider->move($share, $recipientId);
1218
	}
1219
1220
	public function getSharesInFolder($userId, Folder $node, $reshares = false) {
1221
		$providers = $this->factory->getAllProviders();
1222
1223
		return array_reduce($providers, function ($shares, IShareProvider $provider) use ($userId, $node, $reshares) {
1224
			$newShares = $provider->getSharesInFolder($userId, $node, $reshares);
1225
			foreach ($newShares as $fid => $data) {
1226
				if (!isset($shares[$fid])) {
1227
					$shares[$fid] = [];
1228
				}
1229
1230
				$shares[$fid] = array_merge($shares[$fid], $data);
0 ignored issues
show
Bug introduced by
$data of type OCP\Share\IShare is incompatible with the type array|null expected by parameter $array2 of array_merge(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1230
				$shares[$fid] = array_merge($shares[$fid], /** @scrutinizer ignore-type */ $data);
Loading history...
1231
			}
1232
			return $shares;
1233
		}, []);
1234
	}
1235
1236
	/**
1237
	 * @inheritdoc
1238
	 */
1239
	public function getSharesBy($userId, $shareType, $path = null, $reshares = false, $limit = 50, $offset = 0) {
1240
		if ($path !== null &&
1241
				!($path instanceof \OCP\Files\File) &&
1242
				!($path instanceof \OCP\Files\Folder)) {
1243
			throw new \InvalidArgumentException('invalid path');
1244
		}
1245
1246
		try {
1247
			$provider = $this->factory->getProviderForType($shareType);
1248
		} catch (ProviderException $e) {
1249
			return [];
1250
		}
1251
1252
		$shares = $provider->getSharesBy($userId, $shareType, $path, $reshares, $limit, $offset);
1253
1254
		/*
1255
		 * Work around so we don't return expired shares but still follow
1256
		 * proper pagination.
1257
		 */
1258
1259
		$shares2 = [];
1260
1261
		while (true) {
1262
			$added = 0;
1263
			foreach ($shares as $share) {
1264
				try {
1265
					$this->checkExpireDate($share);
1266
				} catch (ShareNotFound $e) {
1267
					//Ignore since this basically means the share is deleted
1268
					continue;
1269
				}
1270
1271
				$added++;
1272
				$shares2[] = $share;
1273
1274
				if (count($shares2) === $limit) {
1275
					break;
1276
				}
1277
			}
1278
1279
			// If we did not fetch more shares than the limit then there are no more shares
1280
			if (count($shares) < $limit) {
1281
				break;
1282
			}
1283
1284
			if (count($shares2) === $limit) {
1285
				break;
1286
			}
1287
1288
			// If there was no limit on the select we are done
1289
			if ($limit === -1) {
1290
				break;
1291
			}
1292
1293
			$offset += $added;
1294
1295
			// Fetch again $limit shares
1296
			$shares = $provider->getSharesBy($userId, $shareType, $path, $reshares, $limit, $offset);
1297
1298
			// No more shares means we are done
1299
			if (empty($shares)) {
1300
				break;
1301
			}
1302
		}
1303
1304
		$shares = $shares2;
1305
1306
		return $shares;
1307
	}
1308
1309
	/**
1310
	 * @inheritdoc
1311
	 */
1312
	public function getSharedWith($userId, $shareType, $node = null, $limit = 50, $offset = 0) {
1313
		try {
1314
			$provider = $this->factory->getProviderForType($shareType);
1315
		} catch (ProviderException $e) {
1316
			return [];
1317
		}
1318
1319
		$shares = $provider->getSharedWith($userId, $shareType, $node, $limit, $offset);
1320
1321
		// remove all shares which are already expired
1322
		foreach ($shares as $key => $share) {
1323
			try {
1324
				$this->checkExpireDate($share);
1325
			} catch (ShareNotFound $e) {
1326
				unset($shares[$key]);
1327
			}
1328
		}
1329
1330
		return $shares;
1331
	}
1332
1333
	/**
1334
	 * @inheritdoc
1335
	 */
1336
	public function getDeletedSharedWith($userId, $shareType, $node = null, $limit = 50, $offset = 0) {
1337
		$shares = $this->getSharedWith($userId, $shareType, $node, $limit, $offset);
1338
1339
		// Only get deleted shares
1340
		$shares = array_filter($shares, function (IShare $share) {
1341
			return $share->getPermissions() === 0;
1342
		});
1343
1344
		// Only get shares where the owner still exists
1345
		$shares = array_filter($shares, function (IShare $share) {
1346
			return $this->userManager->userExists($share->getShareOwner());
1347
		});
1348
1349
		return $shares;
1350
	}
1351
1352
	/**
1353
	 * @inheritdoc
1354
	 */
1355
	public function getShareById($id, $recipient = null) {
1356
		if ($id === null) {
1357
			throw new ShareNotFound();
1358
		}
1359
1360
		list($providerId, $id) = $this->splitFullId($id);
1361
1362
		try {
1363
			$provider = $this->factory->getProvider($providerId);
1364
		} catch (ProviderException $e) {
1365
			throw new ShareNotFound();
1366
		}
1367
1368
		$share = $provider->getShareById($id, $recipient);
0 ignored issues
show
Bug introduced by
$id of type string is incompatible with the type integer expected by parameter $id of OCP\Share\IShareProvider::getShareById(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1368
		$share = $provider->getShareById(/** @scrutinizer ignore-type */ $id, $recipient);
Loading history...
1369
1370
		$this->checkExpireDate($share);
1371
1372
		return $share;
1373
	}
1374
1375
	/**
1376
	 * Get all the shares for a given path
1377
	 *
1378
	 * @param \OCP\Files\Node $path
1379
	 * @param int $page
1380
	 * @param int $perPage
1381
	 *
1382
	 * @return Share[]
1383
	 */
1384
	public function getSharesByPath(\OCP\Files\Node $path, $page=0, $perPage=50) {
0 ignored issues
show
Unused Code introduced by
The parameter $page is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

1384
	public function getSharesByPath(\OCP\Files\Node $path, /** @scrutinizer ignore-unused */ $page=0, $perPage=50) {

This check looks for 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. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

1384
	public function getSharesByPath(\OCP\Files\Node $path, $page=0, /** @scrutinizer ignore-unused */ $perPage=50) {

This check looks for 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 $path is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

1384
	public function getSharesByPath(/** @scrutinizer ignore-unused */ \OCP\Files\Node $path, $page=0, $perPage=50) {

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

Loading history...
1385
		return [];
1386
	}
1387
1388
	/**
1389
	 * Get the share by token possible with password
1390
	 *
1391
	 * @param string $token
1392
	 * @return Share
1393
	 *
1394
	 * @throws ShareNotFound
1395
	 */
1396
	public function getShareByToken($token) {
1397
		// tokens can't be valid local user names
1398
		if ($this->userManager->userExists($token)) {
1399
			throw new ShareNotFound();
1400
		}
1401
		$share = null;
1402
		try {
1403
			if ($this->shareApiAllowLinks()) {
1404
				$provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_LINK);
0 ignored issues
show
Deprecated Code introduced by
The constant OC\Share\Constants::SHARE_TYPE_LINK has been deprecated: 17.0.0 - use IShare::TYPE_LINK instead ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

1404
				$provider = $this->factory->getProviderForType(/** @scrutinizer ignore-deprecated */ \OCP\Share::SHARE_TYPE_LINK);

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
1405
				$share = $provider->getShareByToken($token);
1406
			}
1407
		} catch (ProviderException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
1408
		} catch (ShareNotFound $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
1409
		}
1410
1411
1412
		// If it is not a link share try to fetch a federated share by token
1413
		if ($share === null) {
1414
			try {
1415
				$provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_REMOTE);
0 ignored issues
show
Deprecated Code introduced by
The constant OC\Share\Constants::SHARE_TYPE_REMOTE has been deprecated: 17.0.0 - use IShare::TYPE_REMOTE instead ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

1415
				$provider = $this->factory->getProviderForType(/** @scrutinizer ignore-deprecated */ \OCP\Share::SHARE_TYPE_REMOTE);

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
1416
				$share = $provider->getShareByToken($token);
1417
			} catch (ProviderException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
1418
			} catch (ShareNotFound $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
1419
			}
1420
		}
1421
1422
		// If it is not a link share try to fetch a mail share by token
1423
		if ($share === null && $this->shareProviderExists(\OCP\Share::SHARE_TYPE_EMAIL)) {
0 ignored issues
show
Deprecated Code introduced by
The constant OC\Share\Constants::SHARE_TYPE_EMAIL has been deprecated: 17.0.0 - use IShare::TYPE_EMAIL instead ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

1423
		if ($share === null && $this->shareProviderExists(/** @scrutinizer ignore-deprecated */ \OCP\Share::SHARE_TYPE_EMAIL)) {

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
1424
			try {
1425
				$provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_EMAIL);
0 ignored issues
show
Deprecated Code introduced by
The constant OC\Share\Constants::SHARE_TYPE_EMAIL has been deprecated: 17.0.0 - use IShare::TYPE_EMAIL instead ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

1425
				$provider = $this->factory->getProviderForType(/** @scrutinizer ignore-deprecated */ \OCP\Share::SHARE_TYPE_EMAIL);

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
1426
				$share = $provider->getShareByToken($token);
1427
			} catch (ProviderException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
1428
			} catch (ShareNotFound $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
1429
			}
1430
		}
1431
1432
		if ($share === null && $this->shareProviderExists(\OCP\Share::SHARE_TYPE_CIRCLE)) {
0 ignored issues
show
Deprecated Code introduced by
The constant OC\Share\Constants::SHARE_TYPE_CIRCLE has been deprecated: 17.0.0 - use IShare::TYPE_CIRCLE instead ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

1432
		if ($share === null && $this->shareProviderExists(/** @scrutinizer ignore-deprecated */ \OCP\Share::SHARE_TYPE_CIRCLE)) {

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
1433
			try {
1434
				$provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_CIRCLE);
0 ignored issues
show
Deprecated Code introduced by
The constant OC\Share\Constants::SHARE_TYPE_CIRCLE has been deprecated: 17.0.0 - use IShare::TYPE_CIRCLE instead ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

1434
				$provider = $this->factory->getProviderForType(/** @scrutinizer ignore-deprecated */ \OCP\Share::SHARE_TYPE_CIRCLE);

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
1435
				$share = $provider->getShareByToken($token);
1436
			} catch (ProviderException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
1437
			} catch (ShareNotFound $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
1438
			}
1439
		}
1440
1441
		if ($share === null && $this->shareProviderExists(\OCP\Share::SHARE_TYPE_ROOM)) {
0 ignored issues
show
Deprecated Code introduced by
The constant OC\Share\Constants::SHARE_TYPE_ROOM has been deprecated: 17.0.0 - use IShare::TYPE_ROOM instead ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

1441
		if ($share === null && $this->shareProviderExists(/** @scrutinizer ignore-deprecated */ \OCP\Share::SHARE_TYPE_ROOM)) {

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
1442
			try {
1443
				$provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_ROOM);
0 ignored issues
show
Deprecated Code introduced by
The constant OC\Share\Constants::SHARE_TYPE_ROOM has been deprecated: 17.0.0 - use IShare::TYPE_ROOM instead ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

1443
				$provider = $this->factory->getProviderForType(/** @scrutinizer ignore-deprecated */ \OCP\Share::SHARE_TYPE_ROOM);

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
1444
				$share = $provider->getShareByToken($token);
1445
			} catch (ProviderException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
1446
			} catch (ShareNotFound $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
1447
			}
1448
		}
1449
1450
		if ($share === null) {
1451
			throw new ShareNotFound($this->l->t('The requested share does not exist anymore'));
1452
		}
1453
1454
		$this->checkExpireDate($share);
1455
1456
		/*
1457
		 * Reduce the permissions for link shares if public upload is not enabled
1458
		 */
1459
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK &&
0 ignored issues
show
Deprecated Code introduced by
The constant OC\Share\Constants::SHARE_TYPE_LINK has been deprecated: 17.0.0 - use IShare::TYPE_LINK instead ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

1459
		if ($share->getShareType() === /** @scrutinizer ignore-deprecated */ \OCP\Share::SHARE_TYPE_LINK &&

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
1460
			!$this->shareApiLinkAllowPublicUpload()) {
1461
			$share->setPermissions($share->getPermissions() & ~(\OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE));
1462
		}
1463
1464
		return $share;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $share returns the type OCP\Share\IShare which is incompatible with the documented return type OCP\Share.
Loading history...
1465
	}
1466
1467
	protected function checkExpireDate($share) {
1468
		if ($share->isExpired()) {
1469
			$this->deleteShare($share);
1470
			throw new ShareNotFound($this->l->t('The requested share does not exist anymore'));
1471
		}
1472
	}
1473
1474
	/**
1475
	 * Verify the password of a public share
1476
	 *
1477
	 * @param \OCP\Share\IShare $share
1478
	 * @param string $password
1479
	 * @return bool
1480
	 */
1481
	public function checkPassword(\OCP\Share\IShare $share, $password) {
1482
		$passwordProtected = $share->getShareType() !== IShare::TYPE_LINK
1483
							 || $share->getShareType() !== IShare::TYPE_EMAIL
1484
							 || $share->getShareType() !== IShare::TYPE_CIRCLE;
1485
		if (!$passwordProtected) {
1486
			//TODO maybe exception?
1487
			return false;
1488
		}
1489
1490
		if ($password === null || $share->getPassword() === null) {
0 ignored issues
show
introduced by
The condition $share->getPassword() === null is always false.
Loading history...
1491
			return false;
1492
		}
1493
1494
		$newHash = '';
1495
		if (!$this->hasher->verify($password, $share->getPassword(), $newHash)) {
1496
			return false;
1497
		}
1498
1499
		if (!empty($newHash)) {
1500
			$share->setPassword($newHash);
1501
			$provider = $this->factory->getProviderForType($share->getShareType());
1502
			$provider->update($share);
1503
		}
1504
1505
		return true;
1506
	}
1507
1508
	/**
1509
	 * @inheritdoc
1510
	 */
1511
	public function userDeleted($uid) {
1512
		$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];
0 ignored issues
show
Deprecated Code introduced by
The constant OC\Share\Constants::SHARE_TYPE_LINK has been deprecated: 17.0.0 - use IShare::TYPE_LINK instead ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

1512
		$types = [\OCP\Share::SHARE_TYPE_USER, \OCP\Share::SHARE_TYPE_GROUP, /** @scrutinizer ignore-deprecated */ \OCP\Share::SHARE_TYPE_LINK, \OCP\Share::SHARE_TYPE_REMOTE, \OCP\Share::SHARE_TYPE_EMAIL];

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
Deprecated Code introduced by
The constant OC\Share\Constants::SHARE_TYPE_GROUP has been deprecated: 17.0.0 - use IShare::TYPE_GROUP instead ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

1512
		$types = [\OCP\Share::SHARE_TYPE_USER, /** @scrutinizer ignore-deprecated */ \OCP\Share::SHARE_TYPE_GROUP, \OCP\Share::SHARE_TYPE_LINK, \OCP\Share::SHARE_TYPE_REMOTE, \OCP\Share::SHARE_TYPE_EMAIL];

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
Deprecated Code introduced by
The constant OC\Share\Constants::SHARE_TYPE_EMAIL has been deprecated: 17.0.0 - use IShare::TYPE_EMAIL instead ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

1512
		$types = [\OCP\Share::SHARE_TYPE_USER, \OCP\Share::SHARE_TYPE_GROUP, \OCP\Share::SHARE_TYPE_LINK, \OCP\Share::SHARE_TYPE_REMOTE, /** @scrutinizer ignore-deprecated */ \OCP\Share::SHARE_TYPE_EMAIL];

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
Deprecated Code introduced by
The constant OC\Share\Constants::SHARE_TYPE_REMOTE has been deprecated: 17.0.0 - use IShare::TYPE_REMOTE instead ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

1512
		$types = [\OCP\Share::SHARE_TYPE_USER, \OCP\Share::SHARE_TYPE_GROUP, \OCP\Share::SHARE_TYPE_LINK, /** @scrutinizer ignore-deprecated */ \OCP\Share::SHARE_TYPE_REMOTE, \OCP\Share::SHARE_TYPE_EMAIL];

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
Deprecated Code introduced by
The constant OC\Share\Constants::SHARE_TYPE_USER has been deprecated: 17.0.0 - use IShare::TYPE_USER instead ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

1512
		$types = [/** @scrutinizer ignore-deprecated */ \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];

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
1513
1514
		foreach ($types as $type) {
1515
			try {
1516
				$provider = $this->factory->getProviderForType($type);
1517
			} catch (ProviderException $e) {
1518
				continue;
1519
			}
1520
			$provider->userDeleted($uid, $type);
1521
		}
1522
	}
1523
1524
	/**
1525
	 * @inheritdoc
1526
	 */
1527
	public function groupDeleted($gid) {
1528
		$provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_GROUP);
0 ignored issues
show
Deprecated Code introduced by
The constant OC\Share\Constants::SHARE_TYPE_GROUP has been deprecated: 17.0.0 - use IShare::TYPE_GROUP instead ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

1528
		$provider = $this->factory->getProviderForType(/** @scrutinizer ignore-deprecated */ \OCP\Share::SHARE_TYPE_GROUP);

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
1529
		$provider->groupDeleted($gid);
1530
1531
		$excludedGroups = $this->config->getAppValue('core', 'shareapi_exclude_groups_list', '');
1532
		if ($excludedGroups === '') {
1533
			return;
1534
		}
1535
1536
		$excludedGroups = json_decode($excludedGroups, true);
1537
		if (json_last_error() !== JSON_ERROR_NONE) {
1538
			return;
1539
		}
1540
1541
		$excludedGroups = array_diff($excludedGroups, [$gid]);
1542
		$this->config->setAppValue('core', 'shareapi_exclude_groups_list', json_encode($excludedGroups));
1543
	}
1544
1545
	/**
1546
	 * @inheritdoc
1547
	 */
1548
	public function userDeletedFromGroup($uid, $gid) {
1549
		$provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_GROUP);
0 ignored issues
show
Deprecated Code introduced by
The constant OC\Share\Constants::SHARE_TYPE_GROUP has been deprecated: 17.0.0 - use IShare::TYPE_GROUP instead ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

1549
		$provider = $this->factory->getProviderForType(/** @scrutinizer ignore-deprecated */ \OCP\Share::SHARE_TYPE_GROUP);

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
1550
		$provider->userDeletedFromGroup($uid, $gid);
1551
	}
1552
1553
	/**
1554
	 * Get access list to a path. This means
1555
	 * all the users that can access a given path.
1556
	 *
1557
	 * Consider:
1558
	 * -root
1559
	 * |-folder1 (23)
1560
	 *  |-folder2 (32)
1561
	 *   |-fileA (42)
1562
	 *
1563
	 * fileA is shared with user1 and user1@server1
1564
	 * folder2 is shared with group2 (user4 is a member of group2)
1565
	 * folder1 is shared with user2 (renamed to "folder (1)") and user2@server2
1566
	 *
1567
	 * Then the access list to '/folder1/folder2/fileA' with $currentAccess is:
1568
	 * [
1569
	 *  users  => [
1570
	 *      'user1' => ['node_id' => 42, 'node_path' => '/fileA'],
1571
	 *      'user4' => ['node_id' => 32, 'node_path' => '/folder2'],
1572
	 *      'user2' => ['node_id' => 23, 'node_path' => '/folder (1)'],
1573
	 *  ],
1574
	 *  remote => [
1575
	 *      'user1@server1' => ['node_id' => 42, 'token' => 'SeCr3t'],
1576
	 *      'user2@server2' => ['node_id' => 23, 'token' => 'FooBaR'],
1577
	 *  ],
1578
	 *  public => bool
1579
	 *  mail => bool
1580
	 * ]
1581
	 *
1582
	 * The access list to '/folder1/folder2/fileA' **without** $currentAccess is:
1583
	 * [
1584
	 *  users  => ['user1', 'user2', 'user4'],
1585
	 *  remote => bool,
1586
	 *  public => bool
1587
	 *  mail => bool
1588
	 * ]
1589
	 *
1590
	 * This is required for encryption/activity
1591
	 *
1592
	 * @param \OCP\Files\Node $path
1593
	 * @param bool $recursive Should we check all parent folders as well
1594
	 * @param bool $currentAccess Ensure the recipient has access to the file (e.g. did not unshare it)
1595
	 * @return array
1596
	 */
1597
	public function getAccessList(\OCP\Files\Node $path, $recursive = true, $currentAccess = false) {
1598
		$owner = $path->getOwner();
1599
1600
		if ($owner === null) {
1601
			return [];
1602
		}
1603
1604
		$owner = $owner->getUID();
1605
1606
		if ($currentAccess) {
1607
			$al = ['users' => [], 'remote' => [], 'public' => false];
1608
		} else {
1609
			$al = ['users' => [], 'remote' => false, 'public' => false];
1610
		}
1611
		if (!$this->userManager->userExists($owner)) {
1612
			return $al;
1613
		}
1614
1615
		//Get node for the owner and correct the owner in case of external storages
1616
		$userFolder = $this->rootFolder->getUserFolder($owner);
1617
		if ($path->getId() !== $userFolder->getId() && !$userFolder->isSubNode($path)) {
1618
			$nodes = $userFolder->getById($path->getId());
1619
			$path = array_shift($nodes);
1620
			if ($path->getOwner() === null) {
1621
				return [];
1622
			}
1623
			$owner = $path->getOwner()->getUID();
1624
		}
1625
1626
		$providers = $this->factory->getAllProviders();
1627
1628
		/** @var Node[] $nodes */
1629
		$nodes = [];
1630
1631
1632
		if ($currentAccess) {
1633
			$ownerPath = $path->getPath();
1634
			$ownerPath = explode('/', $ownerPath, 4);
1635
			if (count($ownerPath) < 4) {
1636
				$ownerPath = '';
1637
			} else {
1638
				$ownerPath = $ownerPath[3];
1639
			}
1640
			$al['users'][$owner] = [
1641
				'node_id' => $path->getId(),
1642
				'node_path' => '/' . $ownerPath,
1643
			];
1644
		} else {
1645
			$al['users'][] = $owner;
1646
		}
1647
1648
		// Collect all the shares
1649
		while ($path->getPath() !== $userFolder->getPath()) {
1650
			$nodes[] = $path;
1651
			if (!$recursive) {
1652
				break;
1653
			}
1654
			$path = $path->getParent();
1655
		}
1656
1657
		foreach ($providers as $provider) {
1658
			$tmp = $provider->getAccessList($nodes, $currentAccess);
1659
1660
			foreach ($tmp as $k => $v) {
1661
				if (isset($al[$k])) {
1662
					if (is_array($al[$k])) {
1663
						if ($currentAccess) {
1664
							$al[$k] += $v;
1665
						} else {
1666
							$al[$k] = array_merge($al[$k], $v);
1667
							$al[$k] = array_unique($al[$k]);
1668
							$al[$k] = array_values($al[$k]);
1669
						}
1670
					} else {
1671
						$al[$k] = $al[$k] || $v;
1672
					}
1673
				} else {
1674
					$al[$k] = $v;
1675
				}
1676
			}
1677
		}
1678
1679
		return $al;
1680
	}
1681
1682
	/**
1683
	 * Create a new share
1684
	 * @return \OCP\Share\IShare
1685
	 */
1686
	public function newShare() {
1687
		return new \OC\Share20\Share($this->rootFolder, $this->userManager);
1688
	}
1689
1690
	/**
1691
	 * Is the share API enabled
1692
	 *
1693
	 * @return bool
1694
	 */
1695
	public function shareApiEnabled() {
1696
		return $this->config->getAppValue('core', 'shareapi_enabled', 'yes') === 'yes';
1697
	}
1698
1699
	/**
1700
	 * Is public link sharing enabled
1701
	 *
1702
	 * @return bool
1703
	 */
1704
	public function shareApiAllowLinks() {
1705
		return $this->config->getAppValue('core', 'shareapi_allow_links', 'yes') === 'yes';
1706
	}
1707
1708
	/**
1709
	 * Is password on public link requires
1710
	 *
1711
	 * @return bool
1712
	 */
1713
	public function shareApiLinkEnforcePassword() {
1714
		return $this->config->getAppValue('core', 'shareapi_enforce_links_password', 'no') === 'yes';
1715
	}
1716
1717
	/**
1718
	 * Is default link expire date enabled
1719
	 *
1720
	 * @return bool
1721
	 */
1722
	public function shareApiLinkDefaultExpireDate() {
1723
		return $this->config->getAppValue('core', 'shareapi_default_expire_date', 'no') === 'yes';
1724
	}
1725
1726
	/**
1727
	 * Is default link expire date enforced
1728
	 *`
1729
	 * @return bool
1730
	 */
1731
	public function shareApiLinkDefaultExpireDateEnforced() {
1732
		return $this->shareApiLinkDefaultExpireDate() &&
1733
			$this->config->getAppValue('core', 'shareapi_enforce_expire_date', 'no') === 'yes';
1734
	}
1735
1736
1737
	/**
1738
	 * Number of default link expire days
1739
	 * @return int
1740
	 */
1741
	public function shareApiLinkDefaultExpireDays() {
1742
		return (int)$this->config->getAppValue('core', 'shareapi_expire_after_n_days', '7');
1743
	}
1744
1745
	/**
1746
	 * Is default internal expire date enabled
1747
	 *
1748
	 * @return bool
1749
	 */
1750
	public function shareApiInternalDefaultExpireDate(): bool {
1751
		return $this->config->getAppValue('core', 'shareapi_default_internal_expire_date', 'no') === 'yes';
1752
	}
1753
1754
	/**
1755
	 * Is default expire date enforced
1756
	 *`
1757
	 * @return bool
1758
	 */
1759
	public function shareApiInternalDefaultExpireDateEnforced(): bool {
1760
		return $this->shareApiInternalDefaultExpireDate() &&
1761
			$this->config->getAppValue('core', 'shareapi_enforce_internal_expire_date', 'no') === 'yes';
1762
	}
1763
1764
1765
	/**
1766
	 * Number of default expire days
1767
	 * @return int
1768
	 */
1769
	public function shareApiInternalDefaultExpireDays(): int {
1770
		return (int)$this->config->getAppValue('core', 'shareapi_internal_expire_after_n_days', '7');
1771
	}
1772
1773
	/**
1774
	 * Allow public upload on link shares
1775
	 *
1776
	 * @return bool
1777
	 */
1778
	public function shareApiLinkAllowPublicUpload() {
1779
		return $this->config->getAppValue('core', 'shareapi_allow_public_upload', 'yes') === 'yes';
1780
	}
1781
1782
	/**
1783
	 * check if user can only share with group members
1784
	 * @return bool
1785
	 */
1786
	public function shareWithGroupMembersOnly() {
1787
		return $this->config->getAppValue('core', 'shareapi_only_share_with_group_members', 'no') === 'yes';
1788
	}
1789
1790
	/**
1791
	 * Check if users can share with groups
1792
	 * @return bool
1793
	 */
1794
	public function allowGroupSharing() {
1795
		return $this->config->getAppValue('core', 'shareapi_allow_group_sharing', 'yes') === 'yes';
1796
	}
1797
1798
	public function allowEnumeration(): bool {
1799
		return $this->config->getAppValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes') === 'yes';
1800
	}
1801
1802
	public function limitEnumerationToGroups(): bool {
1803
		return $this->allowEnumeration() &&
1804
			$this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_group', 'no') === 'yes';
1805
	}
1806
1807
	/**
1808
	 * Copied from \OC_Util::isSharingDisabledForUser
1809
	 *
1810
	 * TODO: Deprecate fuction from OC_Util
1811
	 *
1812
	 * @param string $userId
1813
	 * @return bool
1814
	 */
1815
	public function sharingDisabledForUser($userId) {
1816
		if ($userId === null) {
0 ignored issues
show
introduced by
The condition $userId === null is always false.
Loading history...
1817
			return false;
1818
		}
1819
1820
		if (isset($this->sharingDisabledForUsersCache[$userId])) {
1821
			return $this->sharingDisabledForUsersCache[$userId];
1822
		}
1823
1824
		if ($this->config->getAppValue('core', 'shareapi_exclude_groups', 'no') === 'yes') {
1825
			$groupsList = $this->config->getAppValue('core', 'shareapi_exclude_groups_list', '');
1826
			$excludedGroups = json_decode($groupsList);
1827
			if (is_null($excludedGroups)) {
1828
				$excludedGroups = explode(',', $groupsList);
1829
				$newValue = json_encode($excludedGroups);
1830
				$this->config->setAppValue('core', 'shareapi_exclude_groups_list', $newValue);
1831
			}
1832
			$user = $this->userManager->get($userId);
1833
			$usersGroups = $this->groupManager->getUserGroupIds($user);
1834
			if (!empty($usersGroups)) {
1835
				$remainingGroups = array_diff($usersGroups, $excludedGroups);
1836
				// if the user is only in groups which are disabled for sharing then
1837
				// sharing is also disabled for the user
1838
				if (empty($remainingGroups)) {
1839
					$this->sharingDisabledForUsersCache[$userId] = true;
1840
					return true;
1841
				}
1842
			}
1843
		}
1844
1845
		$this->sharingDisabledForUsersCache[$userId] = false;
1846
		return false;
1847
	}
1848
1849
	/**
1850
	 * @inheritdoc
1851
	 */
1852
	public function outgoingServer2ServerSharesAllowed() {
1853
		return $this->config->getAppValue('files_sharing', 'outgoing_server2server_share_enabled', 'yes') === 'yes';
1854
	}
1855
1856
	/**
1857
	 * @inheritdoc
1858
	 */
1859
	public function outgoingServer2ServerGroupSharesAllowed() {
1860
		return $this->config->getAppValue('files_sharing', 'outgoing_server2server_group_share_enabled', 'no') === 'yes';
1861
	}
1862
1863
	/**
1864
	 * @inheritdoc
1865
	 */
1866
	public function shareProviderExists($shareType) {
1867
		try {
1868
			$this->factory->getProviderForType($shareType);
1869
		} catch (ProviderException $e) {
1870
			return false;
1871
		}
1872
1873
		return true;
1874
	}
1875
1876
	public function getAllShares(): iterable {
1877
		$providers = $this->factory->getAllProviders();
1878
1879
		foreach ($providers as $provider) {
1880
			yield from $provider->getAllShares();
1881
		}
1882
	}
1883
}
1884