Completed
Pull Request — master (#32767)
by Victor
13:48 queued 03:33
created

Share20OcsController::updateShareState()   C

Complexity

Conditions 14
Paths 63

Size

Total Lines 82

Duplication

Lines 10
Ratio 12.2 %

Importance

Changes 0
Metric Value
cc 14
nc 63
nop 2
dl 10
loc 82
rs 5.406
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
/**
3
 * @author Viktar Dubiniuk <[email protected]>
4
 *
5
 * @copyright Copyright (c) 2018, ownCloud GmbH
6
 * @license AGPL-3.0
7
 *
8
 * This code is free software: you can redistribute it and/or modify
9
 * it under the terms of the GNU Affero General Public License, version 3,
10
 * as published by the Free Software Foundation.
11
 *
12
 * This program is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
 * GNU Affero General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU Affero General Public License, version 3,
18
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
19
 *
20
 */
21
22
namespace OCA\Files_Sharing\Controller;
23
24
use OC\OCS\Result;
25
use OCP\AppFramework\OCSController;
26
use OCP\Files\IRootFolder;
27
use OCP\Files\NotFoundException;
28
use OCP\IConfig;
29
use OCP\IGroupManager;
30
use OCP\IL10N;
31
use OCP\IRequest;
32
use OCP\IURLGenerator;
33
use OCP\IUser;
34
use OCP\IUserManager;
35
use OCP\Lock\ILockingProvider;
36
use OCP\Lock\LockedException;
37
use OCP\Share\Exceptions\GenericShareException;
38
use OCP\Share\Exceptions\ShareNotFound;
39
use OCP\Share\IManager;
40
use OCP\Share\IShare;
41
use OCA\Files_Sharing\Service\NotificationPublisher;
42
use OCA\Files_Sharing\Helper;
43
use OCA\Files_Sharing\SharingBlacklist;
44
use Symfony\Component\EventDispatcher\EventDispatcher;
45
use Symfony\Component\EventDispatcher\GenericEvent;
46
47
/**
48
 * Class Share20OcsController
49
 *
50
 * @package OCA\Files_Sharing\Controller
51
 */
52
class Share20OcsController extends OCSController {
53
	/** @var IManager */
54
	private $shareManager;
55
	/** @var IGroupManager */
56
	private $groupManager;
57
	/** @var IUserManager */
58
	private $userManager;
59
	/** @var IRootFolder */
60
	private $rootFolder;
61
	/** @var IURLGenerator */
62
	private $urlGenerator;
63
	/** @var IUser */
64
	private $currentUser;
65
	/** @var IL10N */
66
	private $l;
67
	/** @var IConfig */
68
	private $config;
69
	/** @var NotificationPublisher */
70
	private $notificationPublisher;
71
	/** @var EventDispatcher  */
72
	private $eventDispatcher;
73
	/** @var SharingBlacklist */
74
	private $sharingBlacklist;
75
76
	/**
77
	 * @var string
78
	 */
79
	private $additionalInfoField;
80
81
	public function __construct(
82
		$appName,
83
		IRequest $request,
84
		IManager $shareManager,
85
		IGroupManager $groupManager,
86
		IUserManager $userManager,
87
		IRootFolder $rootFolder,
88
		IURLGenerator $urlGenerator,
89
		IUser $currentUser,
90
		IL10N $l10n,
91
		IConfig $config,
92
		NotificationPublisher $notificationPublisher,
93
		EventDispatcher $eventDispatcher,
94
		SharingBlacklist $sharingBlacklist
95
	) {
96
		parent::__construct($appName, $request);
97
		$this->request = $request;
98
		$this->shareManager = $shareManager;
99
		$this->groupManager = $groupManager;
100
		$this->userManager = $userManager;
101
		$this->rootFolder = $rootFolder;
102
		$this->urlGenerator = $urlGenerator;
103
		$this->currentUser = $currentUser;
104
		$this->l = $l10n;
105
		$this->config = $config;
106
		$this->notificationPublisher = $notificationPublisher;
107
		$this->eventDispatcher = $eventDispatcher;
108
		$this->sharingBlacklist = $sharingBlacklist;
109
		$this->additionalInfoField = $this->config->getAppValue('core', 'user_additional_info_field', '');
110
	}
111
112
	/**
113
	 * Returns the additional info to display behind the display name as configured.
114
	 *
115
	 * @param IUser $user user for which to retrieve the additional info
116
	 * @return string|null additional info or null if none to be displayed
117
	 */
118 View Code Duplication
	private function getAdditionalUserInfo(IUser $user) {
119
		if ($this->additionalInfoField === 'email') {
120
			return $user->getEMailAddress();
121
		} elseif ($this->additionalInfoField === 'id') {
122
			return $user->getUID();
123
		}
124
		return null;
125
	}
126
127
	/**
128
	 * Convert an IShare to an array for OCS output
129
	 *
130
	 * @param \OCP\Share\IShare $share
131
	 * @param bool $received whether it's formatting received shares
132
	 * @return array
133
	 * @throws NotFoundException In case the node can't be resolved.
134
	 */
135
	protected function formatShare(\OCP\Share\IShare $share, $received = false) {
136
		$sharedBy = $this->userManager->get($share->getSharedBy());
137
		$shareOwner = $this->userManager->get($share->getShareOwner());
138
139
		$result = [
140
			'id' => $share->getId(),
141
			'share_type' => $share->getShareType(),
142
			'uid_owner' => $share->getSharedBy(),
143
			'displayname_owner' => $sharedBy !== null ? $sharedBy->getDisplayName() : $share->getSharedBy(),
144
			'permissions' => $share->getPermissions(),
145
			'stime' => $share->getShareTime() ? $share->getShareTime()->getTimestamp() : null,
146
			'parent' => null,
147
			'expiration' => null,
148
			'token' => null,
149
			'uid_file_owner' => $share->getShareOwner(),
150
			'displayname_file_owner' => $shareOwner !== null ? $shareOwner->getDisplayName() : $share->getShareOwner()
151
		];
152
153
		if ($received) {
154
			// also add state
155
			$result['state'] = $share->getState();
156
157
			// can only fetch path info if mounted already or if owner
158
			if ($share->getState() === \OCP\Share::STATE_ACCEPTED || $share->getShareOwner() === $this->currentUser->getUID()) {
159
				$userFolder = $this->rootFolder->getUserFolder($this->currentUser->getUID());
160
			} else {
161
				// need to go through owner user for pending shares
162
				$userFolder = $this->rootFolder->getUserFolder($share->getShareOwner());
163
			}
164
		} else {
165
			$userFolder = $this->rootFolder->getUserFolder($this->currentUser->getUID());
166
		}
167
168
		$nodes = $userFolder->getById($share->getNodeId());
169
170
		if (empty($nodes)) {
171
			throw new NotFoundException();
172
		}
173
174
		$node = $nodes[0];
175
176
		$result['path'] = $userFolder->getRelativePath($node->getPath());
177
		if ($node instanceof \OCP\Files\Folder) {
178
			$result['item_type'] = 'folder';
179
		} else {
180
			$result['item_type'] = 'file';
181
		}
182
		$result['mimetype'] = $node->getMimeType();
183
		$result['storage_id'] = $node->getStorage()->getId();
184
		$result['storage'] = $node->getStorage()->getCache()->getNumericStorageId();
185
		$result['item_source'] = \strval($node->getId());
186
		$result['file_source'] = \strval($node->getId());
187
		$result['file_parent'] = \strval($node->getParent()->getId());
188
		$result['file_target'] = $share->getTarget();
189
190
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) {
191
			$sharedWith = $this->userManager->get($share->getSharedWith());
192
			$result['share_with'] = $share->getSharedWith();
193
			$result['share_with_displayname'] = $sharedWith !== null ? $sharedWith->getDisplayName() : $share->getSharedWith();
194
			if ($sharedWith !== null) {
195
				$result['share_with_additional_info'] = $this->getAdditionalUserInfo($sharedWith);
196
			}
197
		} elseif ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
198
			$group = $this->groupManager->get($share->getSharedWith());
199
			$result['share_with'] = $share->getSharedWith();
200
			$result['share_with_displayname'] = $group !== null ? $group->getDisplayName() : $share->getSharedWith();
201
		} elseif ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) {
202
			$result['share_with'] = $share->getPassword();
203
			$result['share_with_displayname'] = $share->getPassword();
204
			$result['name'] = $share->getName();
205
206
			$result['token'] = $share->getToken();
207
			if ($share->getToken() !== null) {
208
				$result['url'] = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.showShare', ['token' => $share->getToken()]);
209
			}
210
211
			$expiration = $share->getExpirationDate();
212
			if ($expiration !== null) {
213
				$result['expiration'] = $expiration->format('Y-m-d 00:00:00');
214
			}
215
		} elseif ($share->getShareType() === \OCP\Share::SHARE_TYPE_REMOTE) {
216
			$result['share_with'] = $share->getSharedWith();
217
			$result['share_with_displayname'] = $share->getSharedWith();
218
			$result['token'] = $share->getToken();
219
		}
220
221
		$result['mail_send'] = $share->getMailSend() ? 1 : 0;
222
223
		return $result;
224
	}
225
226
	/**
227
	 * Get a specific share by id
228
	 *
229
	 * @NoCSRFRequired
230
	 * @NoAdminRequired
231
	 *
232
	 * @param string $id
233
	 * @return Result
234
	 */
235
	public function getShare($id) {
236
		if (!$this->shareManager->shareApiEnabled()) {
237
			return new Result(null, 404, $this->l->t('Share API is disabled'));
238
		}
239
240
		try {
241
			$share = $this->getShareById($id);
242
		} catch (ShareNotFound $e) {
243
			return new Result(null, 404, $this->l->t('Wrong share ID, share doesn\'t exist'));
244
		}
245
246
		if ($this->canAccessShare($share)) {
247
			try {
248
				$share = $this->formatShare($share);
249
				return new Result([$share]);
250
			} catch (NotFoundException $e) {
251
				//Fall trough
252
			}
253
		}
254
255
		return new Result(null, 404, $this->l->t('Wrong share ID, share doesn\'t exist'));
256
	}
257
258
	/**
259
	 * Delete a share
260
	 *
261
	 * @NoCSRFRequired
262
	 * @NoAdminRequired
263
	 *
264
	 * @param string $id
265
	 * @return Result
266
	 */
267
	public function deleteShare($id) {
268
		if (!$this->shareManager->shareApiEnabled()) {
269
			return new Result(null, 404, $this->l->t('Share API is disabled'));
270
		}
271
272
		try {
273
			$share = $this->getShareById($id);
274
		} catch (ShareNotFound $e) {
275
			return new Result(null, 404, $this->l->t('Wrong share ID, share doesn\'t exist'));
276
		}
277
278
		try {
279
			$share->getNode()->lock(ILockingProvider::LOCK_SHARED);
280
		} catch (LockedException $e) {
281
			return new Result(null, 404, 'could not delete share');
282
		}
283
284 View Code Duplication
		if (!$this->canAccessShare($share)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
285
			$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
286
			return new Result(null, 404, $this->l->t('Could not delete share'));
287
		}
288
289
		$this->shareManager->deleteShare($share);
290
291
		$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
292
293
		return new Result();
294
	}
295
296
	/**
297
	 * @NoCSRFRequired
298
	 * @NoAdminRequired
299
	 *
300
	 * @return Result
301
	 */
302
	public function createShare() {
303
		$share = $this->shareManager->newShare();
304
305
		if (!$this->shareManager->shareApiEnabled()) {
306
			return new Result(null, 404, $this->l->t('Share API is disabled'));
307
		}
308
309
		$name = $this->request->getParam('name', null);
310
311
		// Verify path
312
		$path = $this->request->getParam('path', null);
313
		if ($path === null) {
314
			return new Result(null, 404, $this->l->t('Please specify a file or folder path'));
315
		}
316
317
		$userFolder = $this->rootFolder->getUserFolder($this->currentUser->getUID());
318
319
		try {
320
			$path = $userFolder->get($path);
321
		} catch (NotFoundException $e) {
322
			return new Result(null, 404, $this->l->t('Wrong path, file/folder doesn\'t exist'));
323
		}
324
325
		$share->setNode($path);
326
327
		try {
328
			$share->getNode()->lock(ILockingProvider::LOCK_SHARED);
329
		} catch (LockedException $e) {
330
			return new Result(null, 404, 'Could not create share');
331
		}
332
333
		$shareType = (int)$this->request->getParam('shareType', '-1');
334
335
		// Parse permissions (if available)
336
		$permissions = $this->request->getParam('permissions', null);
337
		if ($permissions === null) {
338
			if ($shareType !== \OCP\Share::SHARE_TYPE_LINK) {
339
				$permissions = $this->config->getAppValue('core', 'shareapi_default_permissions', \OCP\Constants::PERMISSION_ALL);
340
				$permissions |= \OCP\Constants::PERMISSION_READ;
341
			} else {
342
				$permissions = \OCP\Constants::PERMISSION_ALL;
343
			}
344
		} else {
345
			$permissions = (int)$permissions;
346
		}
347
348
		if ($permissions < 0 || $permissions > \OCP\Constants::PERMISSION_ALL) {
349
			$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
350
			return new Result(null, 404, 'invalid permissions');
351
		}
352
353
		if ($permissions === 0) {
354
			return new Result(null, 400, $this->l->t('Cannot remove all permissions'));
355
		}
356
357
		// link shares can have create-only without read (anonymous upload)
358
		if ($shareType !== \OCP\Share::SHARE_TYPE_LINK && $permissions !== \OCP\Constants::PERMISSION_CREATE) {
359
			// Shares always require read permissions
360
			$permissions |= \OCP\Constants::PERMISSION_READ;
361
		}
362
363
		if ($path instanceof \OCP\Files\File) {
364
			// Single file shares should never have delete or create permissions
365
			$permissions &= ~\OCP\Constants::PERMISSION_DELETE;
366
			$permissions &= ~\OCP\Constants::PERMISSION_CREATE;
367
		}
368
369
		/*
370
		 * Hack for https://github.com/owncloud/core/issues/22587
371
		 * We check the permissions via webdav. But the permissions of the mount point
372
		 * do not equal the share permissions. Here we fix that for federated mounts.
373
		 */
374
		if ($path->getStorage()->instanceOfStorage('OCA\Files_Sharing\External\Storage')) {
375
			$permissions &= ~($permissions & ~$path->getPermissions());
376
		}
377
378
		$shareWith = $this->request->getParam('shareWith', null);
379
380
		$autoAccept = $this->config->getAppValue('core', 'shareapi_auto_accept_share', 'yes') === 'yes';
381
		if ($shareType === \OCP\Share::SHARE_TYPE_USER) {
382
			// Valid user is required to share
383
			if ($shareWith === null || !$this->userManager->userExists($shareWith)) {
384
				$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
385
				return new Result(null, 404, $this->l->t('Please specify a valid user'));
386
			}
387
			$share->setSharedWith($shareWith);
388
			$share->setPermissions($permissions);
389
			if ($autoAccept) {
390
				$share->setState(\OCP\Share::STATE_ACCEPTED);
391
			} else {
392
				$share->setState(\OCP\Share::STATE_PENDING);
393
			}
394
		} elseif ($shareType === \OCP\Share::SHARE_TYPE_GROUP) {
395
			if (!$this->shareManager->allowGroupSharing()) {
396
				$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
397
				return new Result(null, 404, $this->l->t('Group sharing is disabled by the administrator'));
398
			}
399
400
			// Valid group is required to share
401
			if ($shareWith === null || !$this->groupManager->groupExists($shareWith)) {
402
				$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
403
				return new Result(null, 404, $this->l->t('Please specify a valid group'));
404
			}
405
			if ($this->sharingBlacklist->isGroupBlacklisted($this->groupManager->get($shareWith))) {
406
				return new Result(null, 403, $this->l->t('The group is blacklisted for sharing'));
407
			}
408
			$share->setSharedWith($shareWith);
409
			$share->setPermissions($permissions);
410
			if ($autoAccept) {
411
				$share->setState(\OCP\Share::STATE_ACCEPTED);
412
			} else {
413
				$share->setState(\OCP\Share::STATE_PENDING);
414
			}
415
		} elseif ($shareType === \OCP\Share::SHARE_TYPE_LINK) {
416
			//Can we even share links?
417
			if (!$this->shareManager->shareApiAllowLinks()) {
418
				$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
419
				return new Result(null, 404, $this->l->t('Public link sharing is disabled by the administrator'));
420
			}
421
422
			// legacy way, expecting that this won't be used together with "create-only" shares
423
			$publicUpload = $this->request->getParam('publicUpload', null);
424
			// a few permission checks
425 View Code Duplication
			if ($publicUpload === 'true' || $permissions === \OCP\Constants::PERMISSION_CREATE) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
426
				// Check if public upload is allowed
427
				if (!$this->shareManager->shareApiLinkAllowPublicUpload()) {
428
					$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
429
					return new Result(null, 403, $this->l->t('Public upload disabled by the administrator'));
430
				}
431
432
				// Public upload can only be set for folders
433
				if ($path instanceof \OCP\Files\File) {
434
					$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
435
					return new Result(null, 404, $this->l->t('Public upload is only possible for publicly shared folders'));
436
				}
437
			}
438
439
			// convert to permissions
440
			if ($publicUpload === 'true') {
441
				$share->setPermissions(
442
					\OCP\Constants::PERMISSION_READ |
443
					\OCP\Constants::PERMISSION_CREATE |
444
					\OCP\Constants::PERMISSION_UPDATE |
445
					\OCP\Constants::PERMISSION_DELETE
446
				);
447
			} elseif ($permissions === \OCP\Constants::PERMISSION_CREATE ||
448
				$permissions === (\OCP\Constants::PERMISSION_READ | \OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE)) {
449
				$share->setPermissions($permissions);
450
			} else {
451
				// because when "publicUpload" is passed usually no permissions are set,
452
				// which defaults to ALL. But in the case of link shares we default to READ...
453
				$share->setPermissions(\OCP\Constants::PERMISSION_READ);
454
			}
455
456
			// set name only if passed as parameter, empty string is allowed
457
			if ($name !== null) {
458
				$share->setName($name);
459
			}
460
461
			// Set password
462
			$password = $this->request->getParam('password', '');
463
464
			if ($password !== '') {
465
				$share->setPassword($password);
466
			}
467
468
			//Expire date
469
			$expireDate = $this->request->getParam('expireDate', '');
470
471
			if ($expireDate !== '') {
472
				try {
473
					$expireDate = $this->parseDate($expireDate);
474
					$share->setExpirationDate($expireDate);
475
				} catch (\Exception $e) {
476
					$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
477
					return new Result(null, 404, $this->l->t('Invalid date, date format must be YYYY-MM-DD'));
478
				}
479
			}
480
		} elseif ($shareType === \OCP\Share::SHARE_TYPE_REMOTE) {
481
			if (!$this->shareManager->outgoingServer2ServerSharesAllowed()) {
482
				$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
483
				return new Result(null, 403, $this->l->t('Sharing %s failed because the back end does not allow shares from type %s', [$path->getPath(), $shareType]));
484
			}
485
486
			$share->setSharedWith($shareWith);
487
			$share->setPermissions($permissions);
488 View Code Duplication
		} else {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
489
			$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
490
			return new Result(null, 400, $this->l->t('Unknown share type'));
491
		}
492
493
		$share->setShareType($shareType);
494
		$share->setSharedBy($this->currentUser->getUID());
495
496
		try {
497
			$share = $this->shareManager->createShare($share);
498
		} catch (GenericShareException $e) {
499
			$code = $e->getCode() === 0 ? 403 : $e->getCode();
500
			$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
501
			return new Result(null, $code, $e->getHint());
502
		} catch (\Exception $e) {
503
			$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
504
			return new Result(null, 403, $e->getMessage());
505
		}
506
507
		$share->getNode()->unlock(\OCP\Lock\ILockingProvider::LOCK_SHARED);
508
509
		$formattedShareAfterCreate = $this->formatShare($share);
510
511
		return new Result($formattedShareAfterCreate);
512
	}
513
514
	/**
515
	 * @param \OCP\Files\File|\OCP\Files\Folder $node
516
	 * @param boolean $includeTags include tags in response
517
	 * @param int|null $stateFilter state filter or empty for all, defaults to 0 (accepted)
518
	 * @return Result
519
	 */
520
	private function getSharedWithMe($node = null, $includeTags, $stateFilter = 0) {
521
		$userShares = $this->shareManager->getSharedWith($this->currentUser->getUID(), \OCP\Share::SHARE_TYPE_USER, $node, -1, 0);
522
		$groupShares = $this->shareManager->getSharedWith($this->currentUser->getUID(), \OCP\Share::SHARE_TYPE_GROUP, $node, -1, 0);
523
524
		$shares = \array_merge($userShares, $groupShares);
525
526
		$shares = \array_filter($shares, function (IShare $share) {
527
			return $share->getShareOwner() !== $this->currentUser->getUID();
528
		});
529
530
		$formatted = [];
531
		foreach ($shares as $share) {
532
			if (($stateFilter === null || $share->getState() === $stateFilter) &&
533
				$this->canAccessShare($share)) {
534
				try {
535
					$formatted[] = $this->formatShare($share, true);
536
				} catch (NotFoundException $e) {
537
					// Ignore this share
538
				}
539
			}
540
		}
541
542
		if ($includeTags) {
543
			$formatted = \OCA\Files\Helper::populateTags($formatted, 'file_source');
544
		}
545
546
		return new Result($formatted);
547
	}
548
549
	/**
550
	 * @param \OCP\Files\Folder $folder
551
	 * @return Result
552
	 */
553
	private function getSharesInDir($folder) {
554
		if (!($folder instanceof \OCP\Files\Folder)) {
555
			return new Result(null, 400, $this->l->t('Not a directory'));
556
		}
557
558
		$nodes = $folder->getDirectoryListing();
559
		/** @var \OCP\Share\IShare[] $shares */
560
		$shares = [];
561
		foreach ($nodes as $node) {
562
			$shares = \array_merge($shares, $this->shareManager->getSharesBy($this->currentUser->getUID(), \OCP\Share::SHARE_TYPE_USER, $node, false, -1, 0));
563
			$shares = \array_merge($shares, $this->shareManager->getSharesBy($this->currentUser->getUID(), \OCP\Share::SHARE_TYPE_GROUP, $node, false, -1, 0));
564
			$shares = \array_merge($shares, $this->shareManager->getSharesBy($this->currentUser->getUID(), \OCP\Share::SHARE_TYPE_LINK, $node, false, -1, 0));
565
			if ($this->shareManager->outgoingServer2ServerSharesAllowed()) {
566
				$shares = \array_merge($shares, $this->shareManager->getSharesBy($this->currentUser->getUID(), \OCP\Share::SHARE_TYPE_REMOTE, $node, false, -1, 0));
567
			}
568
		}
569
570
		$formatted = [];
571
		foreach ($shares as $share) {
572
			try {
573
				$formatted[] = $this->formatShare($share);
574
			} catch (NotFoundException $e) {
575
				//Ignore this share
576
			}
577
		}
578
579
		return new Result($formatted);
580
	}
581
582
	/**
583
	 * The getShares function.
584
	 *
585
	 * @NoCSRFRequired
586
	 * @NoAdminRequired
587
	 *
588
	 * - Get shares by the current user
589
	 * - Get shares by the current user and reshares (?reshares=true)
590
	 * - Get shares with the current user (?shared_with_me=true)
591
	 * - Get shares for a specific path (?path=...)
592
	 * - Get all shares in a folder (?subfiles=true&path=..)
593
	 *
594
	 * @return Result
595
	 */
596
	public function getShares() {
597
		if (!$this->shareManager->shareApiEnabled()) {
598
			return new Result();
599
		}
600
601
		$sharedWithMe = $this->request->getParam('shared_with_me', null);
602
		$reshares = $this->request->getParam('reshares', null);
603
		$subfiles = $this->request->getParam('subfiles');
604
		$path = $this->request->getParam('path', null);
605
606
		$includeTags = $this->request->getParam('include_tags', false);
607
608
		if ($path !== null) {
609
			$userFolder = $this->rootFolder->getUserFolder($this->currentUser->getUID());
610
			try {
611
				$path = $userFolder->get($path);
612
				$path->lock(ILockingProvider::LOCK_SHARED);
613
			} catch (\OCP\Files\NotFoundException $e) {
614
				return new Result(null, 404, $this->l->t('Wrong path, file/folder doesn\'t exist'));
615
			} catch (LockedException $e) {
616
				return new Result(null, 404, $this->l->t('Could not lock path'));
617
			}
618
		}
619
620
		if ($sharedWithMe === 'true') {
621
			$stateFilter = $this->request->getParam('state', \OCP\Share::STATE_ACCEPTED);
622
			if ($stateFilter === '') {
623
				$stateFilter = \OCP\Share::STATE_ACCEPTED;
624
			} elseif ($stateFilter === 'all') {
625
				$stateFilter = null; // which means all
626
			} else {
627
				$stateFilter = (int)$stateFilter;
628
			}
629
			$result = $this->getSharedWithMe($path, $includeTags, $stateFilter);
0 ignored issues
show
Bug introduced by
It seems like $path can also be of type object<OCP\Files\Node>; however, OCA\Files_Sharing\Contro...ller::getSharedWithMe() does only seem to accept object<OCP\Files\File>|o...<OCP\Files\Folder>|null, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
630
			if ($path !== null) {
631
				$path->unlock(ILockingProvider::LOCK_SHARED);
632
			}
633
			return $result;
634
		}
635
636
		if ($subfiles === 'true') {
637
			$result = $this->getSharesInDir($path);
0 ignored issues
show
Documentation introduced by
$path is of type object<OCP\Files\Node>|null, but the function expects a object<OCP\Files\Folder>.

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
638
			if ($path !== null) {
639
				$path->unlock(ILockingProvider::LOCK_SHARED);
640
			}
641
			return $result;
642
		}
643
644
		if ($reshares === 'true') {
645
			$reshares = true;
646
		} else {
647
			$reshares = false;
648
		}
649
650
		// Get all shares
651
		$userShares = $this->shareManager->getSharesBy($this->currentUser->getUID(), \OCP\Share::SHARE_TYPE_USER, $path, $reshares, -1, 0);
652
		$groupShares = $this->shareManager->getSharesBy($this->currentUser->getUID(), \OCP\Share::SHARE_TYPE_GROUP, $path, $reshares, -1, 0);
653
		$linkShares = $this->shareManager->getSharesBy($this->currentUser->getUID(), \OCP\Share::SHARE_TYPE_LINK, $path, $reshares, -1, 0);
654
		$shares = \array_merge($userShares, $groupShares, $linkShares);
655
656
		if ($this->shareManager->outgoingServer2ServerSharesAllowed()) {
657
			$federatedShares = $this->shareManager->getSharesBy($this->currentUser->getUID(), \OCP\Share::SHARE_TYPE_REMOTE, $path, $reshares, -1, 0);
658
			$shares = \array_merge($shares, $federatedShares);
659
		}
660
661
		$formatted = [];
662
		foreach ($shares as $share) {
663
			try {
664
				$formatted[] = $this->formatShare($share);
665
			} catch (NotFoundException $e) {
666
				//Ignore share
667
			}
668
		}
669
670
		if ($includeTags) {
671
			$formatted = \OCA\Files\Helper::populateTags($formatted, 'file_source');
672
		}
673
674
		if ($path !== null) {
675
			$path->unlock(ILockingProvider::LOCK_SHARED);
676
		}
677
678
		return new Result($formatted);
679
	}
680
681
	/**
682
	 * @NoCSRFRequired
683
	 * @NoAdminRequired
684
	 *
685
	 * @param int $id
686
	 * @return Result
687
	 */
688
	public function updateShare($id) {
689
		if (!$this->shareManager->shareApiEnabled()) {
690
			return new Result(null, 404, $this->l->t('Share API is disabled'));
691
		}
692
693
		try {
694
			$share = $this->getShareById($id);
695
		} catch (ShareNotFound $e) {
696
			return new Result(null, 404, $this->l->t('Wrong share ID, share doesn\'t exist'));
697
		}
698
699
		$share->getNode()->lock(\OCP\Lock\ILockingProvider::LOCK_SHARED);
700
701 View Code Duplication
		if (!$this->canAccessShare($share)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
702
			$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
703
			return new Result(null, 404, $this->l->t('Wrong share ID, share doesn\'t exist'));
704
		}
705
706
		$permissions = $this->request->getParam('permissions', null);
707
		$password = $this->request->getParam('password', null);
708
		$publicUpload = $this->request->getParam('publicUpload', null);
709
		$expireDate = $this->request->getParam('expireDate', null);
710
		$name = $this->request->getParam('name', null);
711
712
		/*
713
		 * expirationdate, password and publicUpload only make sense for link shares
714
		 */
715
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) {
716
			if ($permissions === null && $password === null && $publicUpload === null && $expireDate === null && $name === null) {
717
				$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
718
				return new Result(null, 400, 'Wrong or no update parameter given');
719
			}
720
721
			$newPermissions = null;
722
			if ($publicUpload === 'true') {
723
				$newPermissions = \OCP\Constants::PERMISSION_READ | \OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE;
724
			} elseif ($publicUpload === 'false') {
725
				$newPermissions = \OCP\Constants::PERMISSION_READ;
726
			}
727
728
			if ($permissions !== null) {
729
				$newPermissions = (int)$permissions;
730
			}
731
732
			if ($newPermissions !== null &&
733
				$newPermissions !== \OCP\Constants::PERMISSION_READ &&
734
				$newPermissions !== \OCP\Constants::PERMISSION_CREATE &&
735
				// legacy
736
				$newPermissions !== (\OCP\Constants::PERMISSION_READ | \OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE) &&
737
				// correct
738
				$newPermissions !== (\OCP\Constants::PERMISSION_READ | \OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE)
739
			) {
740
				$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
741
				return new Result(null, 400, $this->l->t('Can\'t change permissions for public share links'));
742
			}
743
744
			if (
745
				// legacy
746
				$newPermissions === (\OCP\Constants::PERMISSION_READ | \OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE) ||
747
				// correct
748
				$newPermissions === (\OCP\Constants::PERMISSION_READ | \OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE)
749
			) {
750
				if (!$this->shareManager->shareApiLinkAllowPublicUpload()) {
751
					$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
752
					return new Result(null, 403, $this->l->t('Public upload disabled by the administrator'));
753
				}
754
755
				if (!($share->getNode() instanceof \OCP\Files\Folder)) {
756
					$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
757
					return new Result(null, 400, $this->l->t('Public upload is only possible for publicly shared folders'));
758
				}
759
760
				// normalize to correct public upload permissions
761
				$newPermissions = \OCP\Constants::PERMISSION_READ | \OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE;
762
			}
763
764
			// create-only (upload-only)
765 View Code Duplication
			if (
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
766
				$newPermissions === \OCP\Constants::PERMISSION_CREATE
767
			) {
768
				if (!$this->shareManager->shareApiLinkAllowPublicUpload()) {
769
					$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
770
					return new Result(null, 403, $this->l->t('Public upload disabled by the administrator'));
771
				}
772
773
				if (!($share->getNode() instanceof \OCP\Files\Folder)) {
774
					$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
775
					return new Result(null, 400, $this->l->t('Public upload is only possible for publicly shared folders'));
776
				}
777
			}
778
779
			// set name only if passed as parameter, empty string is allowed
780
			if ($name !== null) {
781
				$oldname = $share->getName();
782
				$share->setName($name);
783
			}
784
785
			if ($newPermissions !== null) {
786
				$share->setPermissions($newPermissions);
787
				$permissions = $newPermissions;
788
			}
789
790
			if ($expireDate === '') {
791
				$share->setExpirationDate(null);
0 ignored issues
show
Documentation introduced by
null is of type null, but the function expects a object<DateTime>.

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
792
			} elseif ($expireDate !== null) {
793
				try {
794
					$expireDate = $this->parseDate($expireDate);
795
				} catch (\Exception $e) {
796
					$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
797
					return new Result(null, 400, $e->getMessage());
798
				}
799
				$share->setExpirationDate($expireDate);
800
			}
801
802
			if ($password === '') {
803
				$share->setPassword(null);
804
			} elseif ($password !== null) {
805
				$share->setPassword($password);
806
			}
807
		} else {
808
			// For other shares only permissions is valid.
809
			if ($permissions === null) {
810
				$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
811
				return new Result(null, 400, $this->l->t('Wrong or no update parameter given'));
812
			} else {
813
				$permissions = (int)$permissions;
814
				$share->setPermissions($permissions);
815
			}
816
		}
817
818
		if ($permissions !== null && $share->getShareOwner() !== $this->currentUser->getUID()) {
819
			/* Check if this is an incoming share */
820
			$incomingShares = $this->shareManager->getSharedWith($this->currentUser->getUID(), \OCP\Share::SHARE_TYPE_USER, $share->getNode(), -1, 0);
821
			$incomingShares = \array_merge($incomingShares, $this->shareManager->getSharedWith($this->currentUser->getUID(), \OCP\Share::SHARE_TYPE_GROUP, $share->getNode(), -1, 0));
822
823
			if (!empty($incomingShares)) {
824
				$maxPermissions = 0;
825
				foreach ($incomingShares as $incomingShare) {
826
					$maxPermissions |= $incomingShare->getPermissions();
827
				}
828
829
				if ($share->getPermissions() & ~$maxPermissions) {
830
					$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
831
					return new Result(null, 404, $this->l->t('Cannot increase permissions'));
832
				}
833
			}
834
		}
835
836
		if ($share->getPermissions() === 0) {
837
			return new Result(null, 400, $this->l->t('Cannot remove all permissions'));
838
		}
839
840
		try {
841
			$share = $this->shareManager->updateShare($share);
842
		} catch (\Exception $e) {
843
			$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
844
			return new Result(null, 400, $e->getMessage());
845
		}
846
847
		$share->getNode()->unlock(\OCP\Lock\ILockingProvider::LOCK_SHARED);
848
849
		return new Result($this->formatShare($share));
850
	}
851
852
	/**
853
	 * @NoCSRFRequired
854
	 * @NoAdminRequired
855
	 *
856
	 * @param int $id
857
	 * @return Result
858
	 */
859
	public function acceptShare($id) {
860
		return $this->updateShareState($id, \OCP\Share::STATE_ACCEPTED);
861
	}
862
863
	/**
864
	 * @NoCSRFRequired
865
	 * @NoAdminRequired
866
	 *
867
	 * @param int $id
868
	 * @return Result
869
	 */
870
	public function declineShare($id) {
871
		return $this->updateShareState($id, \OCP\Share::STATE_REJECTED);
872
	}
873
874
	/**
875
	 * Send a notification to share recipient(s)
876
	 *
877
	 * @NoCSRFRequired
878
	 * @NoAdminRequired
879
	 *
880
	 * @param string $itemType
881
	 * @param int $itemSource
882
	 */
883
	public function notifyRecipients($itemType, $itemSource) {
884
		$l = \OC::$server->getL10N('core');
885
		$shareType = (int) $this->request->getParam('shareType');
886
		$recipient = (string)$this->request->getParam('recipient');
887
888
		$recipientList = [];
889
		if ($shareType === \OCP\Share::SHARE_TYPE_USER) {
890
			$recipientList[] = $this->userManager->get($recipient);
891 View Code Duplication
		} elseif ($shareType === \OCP\Share::SHARE_TYPE_GROUP) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
892
			$group = \OC::$server->getGroupManager()->get($recipient);
893
			$recipientList = $group->searchUsers('');
894
		}
895
		// don't send a mail to the user who shared the file
896
		$recipientList = \array_filter($recipientList, function ($user) {
897
			/** @var IUser $user */
898
			return $user->getUID() !== $this->currentUser->getUID();
899
		});
900
901
		$defaults = new \OCP\Defaults();
902
		$mailNotification = new \OC\Share\MailNotifications(
903
			$this->currentUser,
904
			\OC::$server->getL10N('lib'),
905
			\OC::$server->getMailer(),
906
			\OC::$server->getConfig(),
907
			\OC::$server->getLogger(),
908
			$defaults,
909
			\OC::$server->getURLGenerator(),
910
			\OC::$server->getEventDispatcher()
0 ignored issues
show
Compatibility introduced by
\OC::$server->getEventDispatcher() of type object<Symfony\Component...entDispatcherInterface> is not a sub-type of object<Symfony\Component...atcher\EventDispatcher>. It seems like you assume a concrete implementation of the interface Symfony\Component\EventD...ventDispatcherInterface to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
911
		);
912
913
		$result = $mailNotification->sendInternalShareMail($recipientList, $itemSource, $itemType);
914
915
		// if we were able to send to at least one recipient, mark as sent
916
		// allowing the user to resend would spam users who already got a notification
917
		if (\count($result) < \count($recipientList)) {
918
			\OCP\Share::setSendMailStatus($itemType, $itemSource, $shareType, $recipient, true);
919
		}
920
921
		$message = empty($result)
922
			? null
923
			: $l->t(
924
				"Couldn't send mail to following recipient(s): %s ",
925
				\implode(', ', $result)
0 ignored issues
show
Documentation introduced by
\implode(', ', $result) is of type string, but the function expects a array.

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
926
			);
927
		return new Result([], 200, $message);
928
	}
929
930
	/**
931
	 * Just mark a notification to share recipient(s) as sent
932
	 *
933
	 * @NoCSRFRequired
934
	 * @NoAdminRequired
935
	 *
936
	 * @param string $itemType
937
	 * @param int $itemSource
938
	 */
939
	public function notifyRecipientsDisabled($itemType, $itemSource) {
940
		$shareType = (int) $this->request->getParam('shareType');
941
		$recipient = (string)$this->request->getParam('recipient');
942
		\OCP\Share::setSendMailStatus($itemType, $itemSource, $shareType, $recipient, false);
943
		return new Result();
944
	}
945
946
	/**
947
	 * @param $id
948
	 * @param $state
949
	 * @return Result
950
	 */
951
	private function updateShareState($id, $state) {
952
		$eventName = '';
953
		if ($state === \OCP\Share::STATE_ACCEPTED) {
954
			$eventName = 'accept';
955
		} elseif ($state === \OCP\Share::STATE_REJECTED) {
956
			$eventName = 'reject';
957
		}
958
959
		if (!$this->shareManager->shareApiEnabled()) {
960
			return new Result(null, 404, $this->l->t('Share API is disabled'));
961
		}
962
963
		try {
964
			$share = $this->getShareById($id, $this->currentUser->getUID());
965
			$this->eventDispatcher->dispatch('share.before' . $eventName, new GenericEvent(null, ['share' => $share]));
966
		} catch (ShareNotFound $e) {
967
			return new Result(null, 404, $this->l->t('Wrong share ID, share doesn\'t exist'));
968
		}
969
970
		$node = $share->getNode();
971
		$node->lock(\OCP\Lock\ILockingProvider::LOCK_SHARED);
972
973
		// this checks that we are either the owner or recipient
974 View Code Duplication
		if (!$this->canAccessShare($share)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
975
			$node->unlock(ILockingProvider::LOCK_SHARED);
976
			return new Result(null, 404, $this->l->t('Wrong share ID, share doesn\'t exist'));
977
		}
978
979
		// only recipient can accept/reject share
980
		if ($share->getShareOwner() === $this->currentUser->getUID() ||
981
			$share->getSharedBy() === $this->currentUser->getUID()) {
982
			$node->unlock(ILockingProvider::LOCK_SHARED);
983
			return new Result(null, 403, $this->l->t('Only recipient can change accepted state'));
984
		}
985
986
		if ($share->getState() === $state) {
987 View Code Duplication
			if ($eventName !== '') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
988
				$this->eventDispatcher->dispatch('share.after' . $eventName, new GenericEvent(null, ['share' => $share]));
989
			}
990
			// if there are no changes in the state, just return the share as if the change was successful
991
			$node->unlock(\OCP\Lock\ILockingProvider::LOCK_SHARED);
992
			return new Result([$this->formatShare($share, true)]);
993
		}
994
995
		// we actually want to update all shares related to the node in case there are multiple
996
		// incoming shares for the same node (ex: receiving simultaneously through group share and user share)
997
		$allShares = $this->shareManager->getSharedWith($this->currentUser->getUID(), \OCP\Share::SHARE_TYPE_USER, $node, -1, 0);
998
		$allShares = \array_merge($allShares, $this->shareManager->getSharedWith($this->currentUser->getUID(), \OCP\Share::SHARE_TYPE_GROUP, $node, -1, 0));
999
1000
		// resolve and deduplicate target if accepting
1001
		if ($state === \OCP\Share::STATE_ACCEPTED) {
1002
			$share = $this->deduplicateShareTarget($share);
1003
		}
1004
1005
		$share->setState($state);
1006
1007
		try {
1008
			foreach ($allShares as $aShare) {
1009
				$aShare->setState($share->getState());
1010
				$aShare->setTarget($share->getTarget());
1011
				$this->shareManager->updateShareForRecipient($aShare, $this->currentUser->getUID());
1012
			}
1013
		} catch (\Exception $e) {
1014
			$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
1015
			return new Result(null, 400, $e->getMessage());
1016
		}
1017
1018
		$node->unlock(\OCP\Lock\ILockingProvider::LOCK_SHARED);
1019
1020
		// FIXME: needs public API!
1021
		\OC\Files\Filesystem::tearDown();
1022
		// FIXME: trigger mount for user to make sure the new node is mounted already
1023
		// before formatShare resolves it
1024
		$this->rootFolder->getUserFolder($this->currentUser->getUID());
1025
1026
		$this->notificationPublisher->discardNotificationForUser($share, $this->currentUser->getUID());
1027
1028 View Code Duplication
		if ($eventName !== '') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1029
			$this->eventDispatcher->dispatch('share.after' . $eventName, new GenericEvent(null, ['share' => $share]));
1030
		}
1031
		return new Result([$this->formatShare($share, true)]);
1032
	}
1033
1034
	/**
1035
	 * Deduplicate the share target in the current user home folder,
1036
	 * based on configured share folder
1037
	 *
1038
	 * @param IShare $share share target to deduplicate
1039
	 * @return IShare same share with target updated if necessary
1040
	 */
1041
	private function deduplicateShareTarget(IShare $share) {
1042
		$userFolder = $this->rootFolder->getUserFolder($this->currentUser->getUID());
1043
		$mountPoint = \basename($share->getTarget());
1044
		$parentDir = \dirname($share->getTarget());
1045
		if (!$userFolder->nodeExists($parentDir)) {
1046
			$parentDir = Helper::getShareFolder();
1047
			$pathAttempt = \OC\Files\Filesystem::normalizePath($parentDir . '/' . $share->getTarget());
1048
		} else {
1049
			$pathAttempt = \OC\Files\Filesystem::normalizePath($share->getTarget());
1050
		}
1051
1052
		$pathinfo = \pathinfo($pathAttempt);
1053
		$ext = (isset($pathinfo['extension'])) ? '.'.$pathinfo['extension'] : '';
1054
		$name = $pathinfo['filename'];
1055
1056
		$i = 2;
1057
		while ($userFolder->nodeExists($pathAttempt)) {
1058
			$pathAttempt = \OC\Files\Filesystem::normalizePath($parentDir . '/' . $name . ' ('.$i.')' . $ext);
1059
			$i++;
1060
		}
1061
1062
		$share->setTarget($pathAttempt);
1063
1064
		return $share;
1065
	}
1066
1067
	/**
1068
	 * @param \OCP\Share\IShare $share
1069
	 * @return bool
1070
	 */
1071
	protected function canAccessShare(\OCP\Share\IShare $share) {
1072
		// A file with permissions 0 can't be accessed by us,
1073
		// unless it's a rejected sub-group share in which case we want it visible to let the user accept it again
1074
		if ($share->getPermissions() === 0
1075
			&& !($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP && $share->getState() === \OCP\Share::STATE_REJECTED)) {
1076
			return false;
1077
		}
1078
1079
		// Owner of the file and the sharer of the file can always get share
1080
		if ($share->getShareOwner() === $this->currentUser->getUID() ||
1081
			$share->getSharedBy() === $this->currentUser->getUID()
1082
		) {
1083
			return true;
1084
		}
1085
1086
		// If the share is shared with you (or a group you are a member of)
1087
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER &&
1088
			$share->getSharedWith() === $this->currentUser->getUID()) {
1089
			return true;
1090
		}
1091
1092
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
1093
			$sharedWith = $this->groupManager->get($share->getSharedWith());
1094
			if ($sharedWith !== null && $sharedWith->inGroup($this->currentUser)) {
1095
				return true;
1096
			}
1097
		}
1098
1099
		return false;
1100
	}
1101
1102
	/**
1103
	 * Make sure that the passed date is valid ISO 8601
1104
	 * So YYYY-MM-DD
1105
	 * If not throw an exception
1106
	 *
1107
	 * @param string $expireDate
1108
	 *
1109
	 * @throws \Exception
1110
	 * @return \DateTime
1111
	 */
1112
	private function parseDate($expireDate) {
1113
		try {
1114
			$date = new \DateTime($expireDate);
1115
		} catch (\Exception $e) {
1116
			throw new \Exception('Invalid date. Format must be YYYY-MM-DD');
1117
		}
1118
1119
		if ($date === false) {
1120
			throw new \Exception('Invalid date. Format must be YYYY-MM-DD');
1121
		}
1122
1123
		$date->setTime(0, 0, 0);
1124
1125
		return $date;
1126
	}
1127
1128
	/**
1129
	 * Since we have multiple providers but the OCS Share API v1 does
1130
	 * not support this we need to check all backends.
1131
	 *
1132
	 * @param string $id
1133
	 * @return \OCP\Share\IShare
1134
	 * @throws ShareNotFound
1135
	 */
1136
	private function getShareById($id, $recipient = null) {
1137
		$share = null;
1138
1139
		// First check if it is an internal share.
1140
		try {
1141
			$share = $this->shareManager->getShareById('ocinternal:'.$id, $recipient);
1142
		} catch (ShareNotFound $e) {
1143
			if (!$this->shareManager->outgoingServer2ServerSharesAllowed()) {
1144
				throw new ShareNotFound();
1145
			}
1146
1147
			$share = $this->shareManager->getShareById('ocFederatedSharing:' . $id, $recipient);
1148
		}
1149
1150
		return $share;
1151
	}
1152
}
1153