Completed
Pull Request — master (#32767)
by Victor
10:21
created

Share20OcsController::getShares()   D

Complexity

Conditions 17
Paths 117

Size

Total Lines 84

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 17
nc 117
nop 0
dl 0
loc 84
rs 4.409
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 OCP\AppFramework\OCSController;
25
use OCP\Files\IRootFolder;
26
use OCP\Files\NotFoundException;
27
use OCP\IConfig;
28
use OCP\IGroupManager;
29
use OCP\IL10N;
30
use OCP\IRequest;
31
use OCP\IURLGenerator;
32
use OCP\IUser;
33
use OCP\IUserManager;
34
use OCP\Lock\ILockingProvider;
35
use OCP\Lock\LockedException;
36
use OCP\Share\Exceptions\GenericShareException;
37
use OCP\Share\Exceptions\ShareNotFound;
38
use OCP\Share\IManager;
39
use OCP\Share\IShare;
40
use OCA\Files_Sharing\Service\NotificationPublisher;
41
use OCA\Files_Sharing\Helper;
42
use OCA\Files_Sharing\SharingBlacklist;
43
use Symfony\Component\EventDispatcher\EventDispatcher;
44
use Symfony\Component\EventDispatcher\GenericEvent;
45
46
/**
47
 * Class Share20OcsController
48
 *
49
 * @package OCA\Files_Sharing\Controller
50
 */
51
class Share20OcsController extends OCSController {
52
	/** @var IManager */
53
	private $shareManager;
54
	/** @var IGroupManager */
55
	private $groupManager;
56
	/** @var IUserManager */
57
	private $userManager;
58
	/** @var IRootFolder */
59
	private $rootFolder;
60
	/** @var IURLGenerator */
61
	private $urlGenerator;
62
	/** @var IUser */
63
	private $currentUser;
64
	/** @var IL10N */
65
	private $l;
66
	/** @var IConfig */
67
	private $config;
68
	/** @var NotificationPublisher */
69
	private $notificationPublisher;
70
	/** @var EventDispatcher  */
71
	private $eventDispatcher;
72
	/** @var SharingBlacklist */
73
	private $sharingBlacklist;
74
75
	/**
76
	 * @var string
77
	 */
78
	private $additionalInfoField;
79
80
	public function __construct(
81
		$appName,
82
		IRequest $request,
83
		IManager $shareManager,
84
		IGroupManager $groupManager,
85
		IUserManager $userManager,
86
		IRootFolder $rootFolder,
87
		IURLGenerator $urlGenerator,
88
		IUser $currentUser,
89
		IL10N $l10n,
90
		IConfig $config,
91
		NotificationPublisher $notificationPublisher,
92
		EventDispatcher $eventDispatcher,
93
		SharingBlacklist $sharingBlacklist
94
	) {
95
		parent::__construct($appName, $request);
96
		$this->request = $request;
97
		$this->shareManager = $shareManager;
98
		$this->groupManager = $groupManager;
99
		$this->userManager = $userManager;
100
		$this->rootFolder = $rootFolder;
101
		$this->urlGenerator = $urlGenerator;
102
		$this->currentUser = $currentUser;
103
		$this->l = $l10n;
104
		$this->config = $config;
105
		$this->notificationPublisher = $notificationPublisher;
106
		$this->eventDispatcher = $eventDispatcher;
107
		$this->sharingBlacklist = $sharingBlacklist;
108
		$this->additionalInfoField = $this->config->getAppValue('core', 'user_additional_info_field', '');
109
	}
110
111
	/**
112
	 * Returns the additional info to display behind the display name as configured.
113
	 *
114
	 * @param IUser $user user for which to retrieve the additional info
115
	 * @return string|null additional info or null if none to be displayed
116
	 */
117 View Code Duplication
	private function getAdditionalUserInfo(IUser $user) {
118
		if ($this->additionalInfoField === 'email') {
119
			return $user->getEMailAddress();
120
		} elseif ($this->additionalInfoField === 'id') {
121
			return $user->getUID();
122
		}
123
		return null;
124
	}
125
126
	/**
127
	 * Convert an IShare to an array for OCS output
128
	 *
129
	 * @param \OCP\Share\IShare $share
130
	 * @param bool $received whether it's formatting received shares
131
	 * @return array
132
	 * @throws NotFoundException In case the node can't be resolved.
133
	 */
134
	protected function formatShare(\OCP\Share\IShare $share, $received = false) {
135
		$sharedBy = $this->userManager->get($share->getSharedBy());
136
		$shareOwner = $this->userManager->get($share->getShareOwner());
137
138
		$result = [
139
			'id' => $share->getId(),
140
			'share_type' => $share->getShareType(),
141
			'uid_owner' => $share->getSharedBy(),
142
			'displayname_owner' => $sharedBy !== null ? $sharedBy->getDisplayName() : $share->getSharedBy(),
143
			'permissions' => $share->getPermissions(),
144
			'stime' => $share->getShareTime() ? $share->getShareTime()->getTimestamp() : null,
145
			'parent' => null,
146
			'expiration' => null,
147
			'token' => null,
148
			'uid_file_owner' => $share->getShareOwner(),
149
			'displayname_file_owner' => $shareOwner !== null ? $shareOwner->getDisplayName() : $share->getShareOwner()
150
		];
151
152
		if ($received) {
153
			// also add state
154
			$result['state'] = $share->getState();
155
156
			// can only fetch path info if mounted already or if owner
157
			if ($share->getState() === \OCP\Share::STATE_ACCEPTED || $share->getShareOwner() === $this->currentUser->getUID()) {
158
				$userFolder = $this->rootFolder->getUserFolder($this->currentUser->getUID());
159
			} else {
160
				// need to go through owner user for pending shares
161
				$userFolder = $this->rootFolder->getUserFolder($share->getShareOwner());
162
			}
163
		} else {
164
			$userFolder = $this->rootFolder->getUserFolder($this->currentUser->getUID());
165
		}
166
167
		$nodes = $userFolder->getById($share->getNodeId());
168
169
		if (empty($nodes)) {
170
			throw new NotFoundException();
171
		}
172
173
		$node = $nodes[0];
174
175
		$result['path'] = $userFolder->getRelativePath($node->getPath());
176
		if ($node instanceof \OCP\Files\Folder) {
177
			$result['item_type'] = 'folder';
178
		} else {
179
			$result['item_type'] = 'file';
180
		}
181
		$result['mimetype'] = $node->getMimeType();
182
		$result['storage_id'] = $node->getStorage()->getId();
183
		$result['storage'] = $node->getStorage()->getCache()->getNumericStorageId();
184
		$result['item_source'] = \strval($node->getId());
185
		$result['file_source'] = \strval($node->getId());
186
		$result['file_parent'] = \strval($node->getParent()->getId());
187
		$result['file_target'] = $share->getTarget();
188
189
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) {
190
			$sharedWith = $this->userManager->get($share->getSharedWith());
191
			$result['share_with'] = $share->getSharedWith();
192
			$result['share_with_displayname'] = $sharedWith !== null ? $sharedWith->getDisplayName() : $share->getSharedWith();
193
			if ($sharedWith !== null) {
194
				$result['share_with_additional_info'] = $this->getAdditionalUserInfo($sharedWith);
195
			}
196
		} elseif ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
197
			$group = $this->groupManager->get($share->getSharedWith());
198
			$result['share_with'] = $share->getSharedWith();
199
			$result['share_with_displayname'] = $group !== null ? $group->getDisplayName() : $share->getSharedWith();
200
		} elseif ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) {
201
			$result['share_with'] = $share->getPassword();
202
			$result['share_with_displayname'] = $share->getPassword();
203
			$result['name'] = $share->getName();
204
205
			$result['token'] = $share->getToken();
206
			if ($share->getToken() !== null) {
207
				$result['url'] = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.showShare', ['token' => $share->getToken()]);
208
			}
209
210
			$expiration = $share->getExpirationDate();
211
			if ($expiration !== null) {
212
				$result['expiration'] = $expiration->format('Y-m-d 00:00:00');
213
			}
214
		} elseif ($share->getShareType() === \OCP\Share::SHARE_TYPE_REMOTE) {
215
			$result['share_with'] = $share->getSharedWith();
216
			$result['share_with_displayname'] = $share->getSharedWith();
217
			$result['token'] = $share->getToken();
218
		}
219
220
		$result['mail_send'] = $share->getMailSend() ? 1 : 0;
221
222
		return $result;
223
	}
224
225
	/**
226
	 * Get a specific share by id
227
	 *
228
	 * @NoCSRFRequired
229
	 * @NoAdminRequired
230
	 *
231
	 * @param string $id
232
	 * @return \OC\OCS\Result
233
	 */
234
	public function getShare($id) {
235
		if (!$this->shareManager->shareApiEnabled()) {
236
			return new \OC\OCS\Result(null, 404, $this->l->t('Share API is disabled'));
237
		}
238
239
		try {
240
			$share = $this->getShareById($id);
241
		} catch (ShareNotFound $e) {
242
			return new \OC\OCS\Result(null, 404, $this->l->t('Wrong share ID, share doesn\'t exist'));
243
		}
244
245
		if ($this->canAccessShare($share)) {
246
			try {
247
				$share = $this->formatShare($share);
248
				return new \OC\OCS\Result([$share]);
249
			} catch (NotFoundException $e) {
250
				//Fall trough
251
			}
252
		}
253
254
		return new \OC\OCS\Result(null, 404, $this->l->t('Wrong share ID, share doesn\'t exist'));
255
	}
256
257
	/**
258
	 * Delete a share
259
	 *
260
	 * @NoCSRFRequired
261
	 * @NoAdminRequired
262
	 *
263
	 * @param string $id
264
	 * @return \OC\OCS\Result
265
	 */
266
	public function deleteShare($id) {
267
		if (!$this->shareManager->shareApiEnabled()) {
268
			return new \OC\OCS\Result(null, 404, $this->l->t('Share API is disabled'));
269
		}
270
271
		try {
272
			$share = $this->getShareById($id);
273
		} catch (ShareNotFound $e) {
274
			return new \OC\OCS\Result(null, 404, $this->l->t('Wrong share ID, share doesn\'t exist'));
275
		}
276
277
		try {
278
			$share->getNode()->lock(ILockingProvider::LOCK_SHARED);
279
		} catch (LockedException $e) {
280
			return new \OC\OCS\Result(null, 404, 'could not delete share');
281
		}
282
283 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...
284
			$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
285
			return new \OC\OCS\Result(null, 404, $this->l->t('Could not delete share'));
286
		}
287
288
		$this->shareManager->deleteShare($share);
289
290
		$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
291
292
		return new \OC\OCS\Result();
293
	}
294
295
	/**
296
	 * @NoCSRFRequired
297
	 * @NoAdminRequired
298
	 *
299
	 * @return \OC\OCS\Result
300
	 */
301
	public function createShare() {
302
		$share = $this->shareManager->newShare();
303
304
		if (!$this->shareManager->shareApiEnabled()) {
305
			return new \OC\OCS\Result(null, 404, $this->l->t('Share API is disabled'));
306
		}
307
308
		$name = $this->request->getParam('name', null);
309
310
		// Verify path
311
		$path = $this->request->getParam('path', null);
312
		if ($path === null) {
313
			return new \OC\OCS\Result(null, 404, $this->l->t('Please specify a file or folder path'));
314
		}
315
316
		$userFolder = $this->rootFolder->getUserFolder($this->currentUser->getUID());
317
318
		try {
319
			$path = $userFolder->get($path);
320
		} catch (NotFoundException $e) {
321
			return new \OC\OCS\Result(null, 404, $this->l->t('Wrong path, file/folder doesn\'t exist'));
322
		}
323
324
		$share->setNode($path);
325
326
		try {
327
			$share->getNode()->lock(ILockingProvider::LOCK_SHARED);
328
		} catch (LockedException $e) {
329
			return new \OC\OCS\Result(null, 404, 'Could not create share');
330
		}
331
332
		$shareType = (int)$this->request->getParam('shareType', '-1');
333
334
		// Parse permissions (if available)
335
		$permissions = $this->request->getParam('permissions', null);
336
		if ($permissions === null) {
337
			if ($shareType !== \OCP\Share::SHARE_TYPE_LINK) {
338
				$permissions = $this->config->getAppValue('core', 'shareapi_default_permissions', \OCP\Constants::PERMISSION_ALL);
339
				$permissions |= \OCP\Constants::PERMISSION_READ;
340
			} else {
341
				$permissions = \OCP\Constants::PERMISSION_ALL;
342
			}
343
		} else {
344
			$permissions = (int)$permissions;
345
		}
346
347
		if ($permissions < 0 || $permissions > \OCP\Constants::PERMISSION_ALL) {
348
			$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
349
			return new \OC\OCS\Result(null, 404, 'invalid permissions');
350
		}
351
352
		if ($permissions === 0) {
353
			return new \OC\OCS\Result(null, 400, $this->l->t('Cannot remove all permissions'));
354
		}
355
356
		// link shares can have create-only without read (anonymous upload)
357
		if ($shareType !== \OCP\Share::SHARE_TYPE_LINK && $permissions !== \OCP\Constants::PERMISSION_CREATE) {
358
			// Shares always require read permissions
359
			$permissions |= \OCP\Constants::PERMISSION_READ;
360
		}
361
362
		if ($path instanceof \OCP\Files\File) {
363
			// Single file shares should never have delete or create permissions
364
			$permissions &= ~\OCP\Constants::PERMISSION_DELETE;
365
			$permissions &= ~\OCP\Constants::PERMISSION_CREATE;
366
		}
367
368
		/*
369
		 * Hack for https://github.com/owncloud/core/issues/22587
370
		 * We check the permissions via webdav. But the permissions of the mount point
371
		 * do not equal the share permissions. Here we fix that for federated mounts.
372
		 */
373
		if ($path->getStorage()->instanceOfStorage('OCA\Files_Sharing\External\Storage')) {
374
			$permissions &= ~($permissions & ~$path->getPermissions());
375
		}
376
377
		$shareWith = $this->request->getParam('shareWith', null);
378
379
		$autoAccept = $this->config->getAppValue('core', 'shareapi_auto_accept_share', 'yes') === 'yes';
380
		if ($shareType === \OCP\Share::SHARE_TYPE_USER) {
381
			// Valid user is required to share
382
			if ($shareWith === null || !$this->userManager->userExists($shareWith)) {
383
				$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
384
				return new \OC\OCS\Result(null, 404, $this->l->t('Please specify a valid user'));
385
			}
386
			$share->setSharedWith($shareWith);
387
			$share->setPermissions($permissions);
388
			if ($autoAccept) {
389
				$share->setState(\OCP\Share::STATE_ACCEPTED);
390
			} else {
391
				$share->setState(\OCP\Share::STATE_PENDING);
392
			}
393
		} elseif ($shareType === \OCP\Share::SHARE_TYPE_GROUP) {
394
			if (!$this->shareManager->allowGroupSharing()) {
395
				$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
396
				return new \OC\OCS\Result(null, 404, $this->l->t('Group sharing is disabled by the administrator'));
397
			}
398
399
			// Valid group is required to share
400
			if ($shareWith === null || !$this->groupManager->groupExists($shareWith)) {
401
				$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
402
				return new \OC\OCS\Result(null, 404, $this->l->t('Please specify a valid group'));
403
			}
404
			if ($this->sharingBlacklist->isGroupBlacklisted($this->groupManager->get($shareWith))) {
405
				return new \OC\OCS\Result(null, 403, $this->l->t('The group is blacklisted for sharing'));
406
			}
407
			$share->setSharedWith($shareWith);
408
			$share->setPermissions($permissions);
409
			if ($autoAccept) {
410
				$share->setState(\OCP\Share::STATE_ACCEPTED);
411
			} else {
412
				$share->setState(\OCP\Share::STATE_PENDING);
413
			}
414
		} elseif ($shareType === \OCP\Share::SHARE_TYPE_LINK) {
415
			//Can we even share links?
416
			if (!$this->shareManager->shareApiAllowLinks()) {
417
				$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
418
				return new \OC\OCS\Result(null, 404, $this->l->t('Public link sharing is disabled by the administrator'));
419
			}
420
421
			// legacy way, expecting that this won't be used together with "create-only" shares
422
			$publicUpload = $this->request->getParam('publicUpload', null);
423
			// a few permission checks
424 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...
425
				// Check if public upload is allowed
426
				if (!$this->shareManager->shareApiLinkAllowPublicUpload()) {
427
					$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
428
					return new \OC\OCS\Result(null, 403, $this->l->t('Public upload disabled by the administrator'));
429
				}
430
431
				// Public upload can only be set for folders
432
				if ($path instanceof \OCP\Files\File) {
433
					$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
434
					return new \OC\OCS\Result(null, 404, $this->l->t('Public upload is only possible for publicly shared folders'));
435
				}
436
			}
437
438
			// convert to permissions
439
			if ($publicUpload === 'true') {
440
				$share->setPermissions(
441
					\OCP\Constants::PERMISSION_READ |
442
					\OCP\Constants::PERMISSION_CREATE |
443
					\OCP\Constants::PERMISSION_UPDATE |
444
					\OCP\Constants::PERMISSION_DELETE
445
				);
446
			} elseif ($permissions === \OCP\Constants::PERMISSION_CREATE ||
447
				$permissions === (\OCP\Constants::PERMISSION_READ | \OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE)) {
448
				$share->setPermissions($permissions);
449
			} else {
450
				// because when "publicUpload" is passed usually no permissions are set,
451
				// which defaults to ALL. But in the case of link shares we default to READ...
452
				$share->setPermissions(\OCP\Constants::PERMISSION_READ);
453
			}
454
455
			// set name only if passed as parameter, empty string is allowed
456
			if ($name !== null) {
457
				$share->setName($name);
458
			}
459
460
			// Set password
461
			$password = $this->request->getParam('password', '');
462
463
			if ($password !== '') {
464
				$share->setPassword($password);
465
			}
466
467
			//Expire date
468
			$expireDate = $this->request->getParam('expireDate', '');
469
470
			if ($expireDate !== '') {
471
				try {
472
					$expireDate = $this->parseDate($expireDate);
473
					$share->setExpirationDate($expireDate);
474
				} catch (\Exception $e) {
475
					$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
476
					return new \OC\OCS\Result(null, 404, $this->l->t('Invalid date, date format must be YYYY-MM-DD'));
477
				}
478
			}
479
		} elseif ($shareType === \OCP\Share::SHARE_TYPE_REMOTE) {
480
			if (!$this->shareManager->outgoingServer2ServerSharesAllowed()) {
481
				$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
482
				return new \OC\OCS\Result(null, 403, $this->l->t('Sharing %s failed because the back end does not allow shares from type %s', [$path->getPath(), $shareType]));
483
			}
484
485
			$share->setSharedWith($shareWith);
486
			$share->setPermissions($permissions);
487 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...
488
			$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
489
			return new \OC\OCS\Result(null, 400, $this->l->t('Unknown share type'));
490
		}
491
492
		$share->setShareType($shareType);
493
		$share->setSharedBy($this->currentUser->getUID());
494
495
		try {
496
			$share = $this->shareManager->createShare($share);
497
		} catch (GenericShareException $e) {
498
			$code = $e->getCode() === 0 ? 403 : $e->getCode();
499
			$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
500
			return new \OC\OCS\Result(null, $code, $e->getHint());
501
		} catch (\Exception $e) {
502
			$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
503
			return new \OC\OCS\Result(null, 403, $e->getMessage());
504
		}
505
506
		$share->getNode()->unlock(\OCP\Lock\ILockingProvider::LOCK_SHARED);
507
508
		$formattedShareAfterCreate = $this->formatShare($share);
509
510
		return new \OC\OCS\Result($formattedShareAfterCreate);
511
	}
512
513
	/**
514
	 * @param \OCP\Files\File|\OCP\Files\Folder $node
515
	 * @param boolean $includeTags include tags in response
516
	 * @param int|null $stateFilter state filter or empty for all, defaults to 0 (accepted)
517
	 * @return \OC\OCS\Result
518
	 */
519
	private function getSharedWithMe($node = null, $includeTags, $stateFilter = 0) {
520
		$userShares = $this->shareManager->getSharedWith($this->currentUser->getUID(), \OCP\Share::SHARE_TYPE_USER, $node, -1, 0);
521
		$groupShares = $this->shareManager->getSharedWith($this->currentUser->getUID(), \OCP\Share::SHARE_TYPE_GROUP, $node, -1, 0);
522
523
		$shares = \array_merge($userShares, $groupShares);
524
525
		$shares = \array_filter($shares, function (IShare $share) {
526
			return $share->getShareOwner() !== $this->currentUser->getUID();
527
		});
528
529
		$formatted = [];
530
		foreach ($shares as $share) {
531
			if (($stateFilter === null || $share->getState() === $stateFilter) &&
532
				$this->canAccessShare($share)) {
533
				try {
534
					$formatted[] = $this->formatShare($share, true);
535
				} catch (NotFoundException $e) {
536
					// Ignore this share
537
				}
538
			}
539
		}
540
541
		if ($includeTags) {
542
			$formatted = \OCA\Files\Helper::populateTags($formatted, 'file_source');
543
		}
544
545
		return new \OC\OCS\Result($formatted);
546
	}
547
548
	/**
549
	 * @param \OCP\Files\Folder $folder
550
	 * @return \OC\OCS\Result
551
	 */
552
	private function getSharesInDir($folder) {
553
		if (!($folder instanceof \OCP\Files\Folder)) {
554
			return new \OC\OCS\Result(null, 400, $this->l->t('Not a directory'));
555
		}
556
557
		$nodes = $folder->getDirectoryListing();
558
		/** @var \OCP\Share\IShare[] $shares */
559
		$shares = [];
560
		foreach ($nodes as $node) {
561
			$shares = \array_merge($shares, $this->shareManager->getSharesBy($this->currentUser->getUID(), \OCP\Share::SHARE_TYPE_USER, $node, false, -1, 0));
562
			$shares = \array_merge($shares, $this->shareManager->getSharesBy($this->currentUser->getUID(), \OCP\Share::SHARE_TYPE_GROUP, $node, false, -1, 0));
563
			$shares = \array_merge($shares, $this->shareManager->getSharesBy($this->currentUser->getUID(), \OCP\Share::SHARE_TYPE_LINK, $node, false, -1, 0));
564
			if ($this->shareManager->outgoingServer2ServerSharesAllowed()) {
565
				$shares = \array_merge($shares, $this->shareManager->getSharesBy($this->currentUser->getUID(), \OCP\Share::SHARE_TYPE_REMOTE, $node, false, -1, 0));
566
			}
567
		}
568
569
		$formatted = [];
570
		foreach ($shares as $share) {
571
			try {
572
				$formatted[] = $this->formatShare($share);
573
			} catch (NotFoundException $e) {
574
				//Ignore this share
575
			}
576
		}
577
578
		return new \OC\OCS\Result($formatted);
579
	}
580
581
	/**
582
	 * The getShares function.
583
	 *
584
	 * @NoCSRFRequired
585
	 * @NoAdminRequired
586
	 *
587
	 * - Get shares by the current user
588
	 * - Get shares by the current user and reshares (?reshares=true)
589
	 * - Get shares with the current user (?shared_with_me=true)
590
	 * - Get shares for a specific path (?path=...)
591
	 * - Get all shares in a folder (?subfiles=true&path=..)
592
	 *
593
	 * @return \OC\OCS\Result
594
	 */
595
	public function getShares() {
596
		if (!$this->shareManager->shareApiEnabled()) {
597
			return new \OC\OCS\Result();
598
		}
599
600
		$sharedWithMe = $this->request->getParam('shared_with_me', null);
601
		$reshares = $this->request->getParam('reshares', null);
602
		$subfiles = $this->request->getParam('subfiles');
603
		$path = $this->request->getParam('path', null);
604
605
		$includeTags = $this->request->getParam('include_tags', false);
606
607
		if ($path !== null) {
608
			$userFolder = $this->rootFolder->getUserFolder($this->currentUser->getUID());
609
			try {
610
				$path = $userFolder->get($path);
611
				$path->lock(ILockingProvider::LOCK_SHARED);
612
			} catch (\OCP\Files\NotFoundException $e) {
613
				return new \OC\OCS\Result(null, 404, $this->l->t('Wrong path, file/folder doesn\'t exist'));
614
			} catch (LockedException $e) {
615
				return new \OC\OCS\Result(null, 404, $this->l->t('Could not lock path'));
616
			}
617
		}
618
619
		if ($sharedWithMe === 'true') {
620
			$stateFilter = $this->request->getParam('state', \OCP\Share::STATE_ACCEPTED);
621
			if ($stateFilter === '') {
622
				$stateFilter = \OCP\Share::STATE_ACCEPTED;
623
			} elseif ($stateFilter === 'all') {
624
				$stateFilter = null; // which means all
625
			} else {
626
				$stateFilter = (int)$stateFilter;
627
			}
628
			$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...
629
			if ($path !== null) {
630
				$path->unlock(ILockingProvider::LOCK_SHARED);
631
			}
632
			return $result;
633
		}
634
635
		if ($subfiles === 'true') {
636
			$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...
637
			if ($path !== null) {
638
				$path->unlock(ILockingProvider::LOCK_SHARED);
639
			}
640
			return $result;
641
		}
642
643
		if ($reshares === 'true') {
644
			$reshares = true;
645
		} else {
646
			$reshares = false;
647
		}
648
649
		// Get all shares
650
		$userShares = $this->shareManager->getSharesBy($this->currentUser->getUID(), \OCP\Share::SHARE_TYPE_USER, $path, $reshares, -1, 0);
651
		$groupShares = $this->shareManager->getSharesBy($this->currentUser->getUID(), \OCP\Share::SHARE_TYPE_GROUP, $path, $reshares, -1, 0);
652
		$linkShares = $this->shareManager->getSharesBy($this->currentUser->getUID(), \OCP\Share::SHARE_TYPE_LINK, $path, $reshares, -1, 0);
653
		$shares = \array_merge($userShares, $groupShares, $linkShares);
654
655
		if ($this->shareManager->outgoingServer2ServerSharesAllowed()) {
656
			$federatedShares = $this->shareManager->getSharesBy($this->currentUser->getUID(), \OCP\Share::SHARE_TYPE_REMOTE, $path, $reshares, -1, 0);
657
			$shares = \array_merge($shares, $federatedShares);
658
		}
659
660
		$formatted = [];
661
		foreach ($shares as $share) {
662
			try {
663
				$formatted[] = $this->formatShare($share);
664
			} catch (NotFoundException $e) {
665
				//Ignore share
666
			}
667
		}
668
669
		if ($includeTags) {
670
			$formatted = \OCA\Files\Helper::populateTags($formatted, 'file_source');
671
		}
672
673
		if ($path !== null) {
674
			$path->unlock(ILockingProvider::LOCK_SHARED);
675
		}
676
677
		return new \OC\OCS\Result($formatted);
678
	}
679
680
	/**
681
	 * @NoCSRFRequired
682
	 * @NoAdminRequired
683
	 *
684
	 * @param int $id
685
	 * @return \OC\OCS\Result
686
	 */
687
	public function updateShare($id) {
688
		if (!$this->shareManager->shareApiEnabled()) {
689
			return new \OC\OCS\Result(null, 404, $this->l->t('Share API is disabled'));
690
		}
691
692
		try {
693
			$share = $this->getShareById($id);
694
		} catch (ShareNotFound $e) {
695
			return new \OC\OCS\Result(null, 404, $this->l->t('Wrong share ID, share doesn\'t exist'));
696
		}
697
698
		$share->getNode()->lock(\OCP\Lock\ILockingProvider::LOCK_SHARED);
699
700 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...
701
			$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
702
			return new \OC\OCS\Result(null, 404, $this->l->t('Wrong share ID, share doesn\'t exist'));
703
		}
704
705
		$permissions = $this->request->getParam('permissions', null);
706
		$password = $this->request->getParam('password', null);
707
		$publicUpload = $this->request->getParam('publicUpload', null);
708
		$expireDate = $this->request->getParam('expireDate', null);
709
		$name = $this->request->getParam('name', null);
710
711
		/*
712
		 * expirationdate, password and publicUpload only make sense for link shares
713
		 */
714
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) {
715
			if ($permissions === null && $password === null && $publicUpload === null && $expireDate === null && $name === null) {
716
				$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
717
				return new \OC\OCS\Result(null, 400, 'Wrong or no update parameter given');
718
			}
719
720
			$newPermissions = null;
721
			if ($publicUpload === 'true') {
722
				$newPermissions = \OCP\Constants::PERMISSION_READ | \OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE;
723
			} elseif ($publicUpload === 'false') {
724
				$newPermissions = \OCP\Constants::PERMISSION_READ;
725
			}
726
727
			if ($permissions !== null) {
728
				$newPermissions = (int)$permissions;
729
			}
730
731
			if ($newPermissions !== null &&
732
				$newPermissions !== \OCP\Constants::PERMISSION_READ &&
733
				$newPermissions !== \OCP\Constants::PERMISSION_CREATE &&
734
				// legacy
735
				$newPermissions !== (\OCP\Constants::PERMISSION_READ | \OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE) &&
736
				// correct
737
				$newPermissions !== (\OCP\Constants::PERMISSION_READ | \OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE)
738
			) {
739
				$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
740
				return new \OC\OCS\Result(null, 400, $this->l->t('Can\'t change permissions for public share links'));
741
			}
742
743
			if (
744
				// legacy
745
				$newPermissions === (\OCP\Constants::PERMISSION_READ | \OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE) ||
746
				// correct
747
				$newPermissions === (\OCP\Constants::PERMISSION_READ | \OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE)
748
			) {
749
				if (!$this->shareManager->shareApiLinkAllowPublicUpload()) {
750
					$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
751
					return new \OC\OCS\Result(null, 403, $this->l->t('Public upload disabled by the administrator'));
752
				}
753
754
				if (!($share->getNode() instanceof \OCP\Files\Folder)) {
755
					$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
756
					return new \OC\OCS\Result(null, 400, $this->l->t('Public upload is only possible for publicly shared folders'));
757
				}
758
759
				// normalize to correct public upload permissions
760
				$newPermissions = \OCP\Constants::PERMISSION_READ | \OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE;
761
			}
762
763
			// create-only (upload-only)
764 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...
765
				$newPermissions === \OCP\Constants::PERMISSION_CREATE
766
			) {
767
				if (!$this->shareManager->shareApiLinkAllowPublicUpload()) {
768
					$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
769
					return new \OC\OCS\Result(null, 403, $this->l->t('Public upload disabled by the administrator'));
770
				}
771
772
				if (!($share->getNode() instanceof \OCP\Files\Folder)) {
773
					$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
774
					return new \OC\OCS\Result(null, 400, $this->l->t('Public upload is only possible for publicly shared folders'));
775
				}
776
			}
777
778
			// set name only if passed as parameter, empty string is allowed
779
			if ($name !== null) {
780
				$oldname = $share->getName();
781
				$share->setName($name);
782
			}
783
784
			if ($newPermissions !== null) {
785
				$share->setPermissions($newPermissions);
786
				$permissions = $newPermissions;
787
			}
788
789
			if ($expireDate === '') {
790
				$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...
791
			} elseif ($expireDate !== null) {
792
				try {
793
					$expireDate = $this->parseDate($expireDate);
794
				} catch (\Exception $e) {
795
					$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
796
					return new \OC\OCS\Result(null, 400, $e->getMessage());
797
				}
798
				$share->setExpirationDate($expireDate);
799
			}
800
801
			if ($password === '') {
802
				$share->setPassword(null);
803
			} elseif ($password !== null) {
804
				$share->setPassword($password);
805
			}
806
		} else {
807
			// For other shares only permissions is valid.
808
			if ($permissions === null) {
809
				$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
810
				return new \OC\OCS\Result(null, 400, $this->l->t('Wrong or no update parameter given'));
811
			} else {
812
				$permissions = (int)$permissions;
813
				$share->setPermissions($permissions);
814
			}
815
		}
816
817
		if ($permissions !== null && $share->getShareOwner() !== $this->currentUser->getUID()) {
818
			/* Check if this is an incoming share */
819
			$incomingShares = $this->shareManager->getSharedWith($this->currentUser->getUID(), \OCP\Share::SHARE_TYPE_USER, $share->getNode(), -1, 0);
820
			$incomingShares = \array_merge($incomingShares, $this->shareManager->getSharedWith($this->currentUser->getUID(), \OCP\Share::SHARE_TYPE_GROUP, $share->getNode(), -1, 0));
821
822
			if (!empty($incomingShares)) {
823
				$maxPermissions = 0;
824
				foreach ($incomingShares as $incomingShare) {
825
					$maxPermissions |= $incomingShare->getPermissions();
826
				}
827
828
				if ($share->getPermissions() & ~$maxPermissions) {
829
					$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
830
					return new \OC\OCS\Result(null, 404, $this->l->t('Cannot increase permissions'));
831
				}
832
			}
833
		}
834
835
		if ($share->getPermissions() === 0) {
836
			return new \OC\OCS\Result(null, 400, $this->l->t('Cannot remove all permissions'));
837
		}
838
839
		try {
840
			$share = $this->shareManager->updateShare($share);
841
		} catch (\Exception $e) {
842
			$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
843
			return new \OC\OCS\Result(null, 400, $e->getMessage());
844
		}
845
846
		$share->getNode()->unlock(\OCP\Lock\ILockingProvider::LOCK_SHARED);
847
848
		return new \OC\OCS\Result($this->formatShare($share));
849
	}
850
851
	/**
852
	 * @NoCSRFRequired
853
	 * @NoAdminRequired
854
	 *
855
	 * @param int $id
856
	 * @return \OC\OCS\Result
857
	 */
858
	public function acceptShare($id) {
859
		return $this->updateShareState($id, \OCP\Share::STATE_ACCEPTED);
860
	}
861
862
	/**
863
	 * @NoCSRFRequired
864
	 * @NoAdminRequired
865
	 *
866
	 * @param int $id
867
	 * @return \OC\OCS\Result
868
	 */
869
	public function declineShare($id) {
870
		return $this->updateShareState($id, \OCP\Share::STATE_REJECTED);
871
	}
872
873
	/**
874
	 * Send a notification to share recipient(s)
875
	 *
876
	 * @NoCSRFRequired
877
	 * @NoAdminRequired
878
	 *
879
	 * @param string $itemType
880
	 * @param int $itemSource
881
	 */
882
	public function notifyRecipients($itemType, $itemSource) {
883
		$l = \OC::$server->getL10N('core');
884
		$shareType = (int) $this->request->getParam('shareType');
885
		$recipient = (string)$this->request->getParam('recipient');
886
887
		$recipientList = [];
888
		if ($shareType === \OCP\Share::SHARE_TYPE_USER) {
889
			$recipientList[] = $this->userManager->get($recipient);
890 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...
891
			$group = \OC::$server->getGroupManager()->get($recipient);
892
			$recipientList = $group->searchUsers('');
893
		}
894
		// don't send a mail to the user who shared the file
895
		$recipientList = \array_filter($recipientList, function ($user) {
896
			/** @var IUser $user */
897
			return $user->getUID() !== $this->currentUser->getUID();
898
		});
899
900
		$defaults = new \OCP\Defaults();
901
		$mailNotification = new \OC\Share\MailNotifications(
902
			$this->currentUser,
903
			\OC::$server->getL10N('lib'),
904
			\OC::$server->getMailer(),
905
			\OC::$server->getConfig(),
906
			\OC::$server->getLogger(),
907
			$defaults,
908
			\OC::$server->getURLGenerator(),
909
			\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...
910
		);
911
912
		$result = $mailNotification->sendInternalShareMail($recipientList, $itemSource, $itemType);
913
914
		// if we were able to send to at least one recipient, mark as sent
915
		// allowing the user to resend would spam users who already got a notification
916
		if (\count($result) < \count($recipientList)) {
917
			\OCP\Share::setSendMailStatus($itemType, $itemSource, $shareType, $recipient, true);
918
		}
919
920
		if (empty($result)) {
921
			\OCP\JSON::success();
922
		} else {
923
			\OCP\JSON::error([
924
				'data' => [
925
					'message' => $l->t("Couldn't send mail to following recipient(s): %s ",
926
						\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...
927
					)
928
				]
929
			]);
930
		}
931
		exit();
932
	}
933
934
	/**
935
	 * Just mark a notification to share recipient(s) as sent
936
	 *
937
	 * @NoCSRFRequired
938
	 * @NoAdminRequired
939
	 *
940
	 * @param string $itemType
941
	 * @param int $itemSource
942
	 */
943
	public function notifyRecipientsDisabled($itemType, $itemSource) {
944
		$shareType = (int) $this->request->getParam('shareType');
945
		$recipient = (string)$this->request->getParam('recipient');
946
		\OCP\Share::setSendMailStatus($itemType, $itemSource, $shareType, $recipient, false);
947
		\OCP\JSON::success();
948
		exit();
949
	}
950
951
	/**
952
	 * @param $id
953
	 * @param $state
954
	 * @return \OC\OCS\Result
955
	 */
956
	private function updateShareState($id, $state) {
957
		$eventName = '';
958
		if ($state === \OCP\Share::STATE_ACCEPTED) {
959
			$eventName = 'accept';
960
		} elseif ($state === \OCP\Share::STATE_REJECTED) {
961
			$eventName = 'reject';
962
		}
963
964
		if (!$this->shareManager->shareApiEnabled()) {
965
			return new \OC\OCS\Result(null, 404, $this->l->t('Share API is disabled'));
966
		}
967
968
		try {
969
			$share = $this->getShareById($id, $this->currentUser->getUID());
970
			$this->eventDispatcher->dispatch('share.before' . $eventName, new GenericEvent(null, ['share' => $share]));
971
		} catch (ShareNotFound $e) {
972
			return new \OC\OCS\Result(null, 404, $this->l->t('Wrong share ID, share doesn\'t exist'));
973
		}
974
975
		$node = $share->getNode();
976
		$node->lock(\OCP\Lock\ILockingProvider::LOCK_SHARED);
977
978
		// this checks that we are either the owner or recipient
979 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...
980
			$node->unlock(ILockingProvider::LOCK_SHARED);
981
			return new \OC\OCS\Result(null, 404, $this->l->t('Wrong share ID, share doesn\'t exist'));
982
		}
983
984
		// only recipient can accept/reject share
985
		if ($share->getShareOwner() === $this->currentUser->getUID() ||
986
			$share->getSharedBy() === $this->currentUser->getUID()) {
987
			$node->unlock(ILockingProvider::LOCK_SHARED);
988
			return new \OC\OCS\Result(null, 403, $this->l->t('Only recipient can change accepted state'));
989
		}
990
991
		if ($share->getState() === $state) {
992 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...
993
				$this->eventDispatcher->dispatch('share.after' . $eventName, new GenericEvent(null, ['share' => $share]));
994
			}
995
			// if there are no changes in the state, just return the share as if the change was successful
996
			$node->unlock(\OCP\Lock\ILockingProvider::LOCK_SHARED);
997
			return new \OC\OCS\Result([$this->formatShare($share, true)]);
998
		}
999
1000
		// we actually want to update all shares related to the node in case there are multiple
1001
		// incoming shares for the same node (ex: receiving simultaneously through group share and user share)
1002
		$allShares = $this->shareManager->getSharedWith($this->currentUser->getUID(), \OCP\Share::SHARE_TYPE_USER, $node, -1, 0);
1003
		$allShares = \array_merge($allShares, $this->shareManager->getSharedWith($this->currentUser->getUID(), \OCP\Share::SHARE_TYPE_GROUP, $node, -1, 0));
1004
1005
		// resolve and deduplicate target if accepting
1006
		if ($state === \OCP\Share::STATE_ACCEPTED) {
1007
			$share = $this->deduplicateShareTarget($share);
1008
		}
1009
1010
		$share->setState($state);
1011
1012
		try {
1013
			foreach ($allShares as $aShare) {
1014
				$aShare->setState($share->getState());
1015
				$aShare->setTarget($share->getTarget());
1016
				$this->shareManager->updateShareForRecipient($aShare, $this->currentUser->getUID());
1017
			}
1018
		} catch (\Exception $e) {
1019
			$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
1020
			return new \OC\OCS\Result(null, 400, $e->getMessage());
1021
		}
1022
1023
		$node->unlock(\OCP\Lock\ILockingProvider::LOCK_SHARED);
1024
1025
		// FIXME: needs public API!
1026
		\OC\Files\Filesystem::tearDown();
1027
		// FIXME: trigger mount for user to make sure the new node is mounted already
1028
		// before formatShare resolves it
1029
		$this->rootFolder->getUserFolder($this->currentUser->getUID());
1030
1031
		$this->notificationPublisher->discardNotificationForUser($share, $this->currentUser->getUID());
1032
1033 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...
1034
			$this->eventDispatcher->dispatch('share.after' . $eventName, new GenericEvent(null, ['share' => $share]));
1035
		}
1036
		return new \OC\OCS\Result([$this->formatShare($share, true)]);
1037
	}
1038
1039
	/**
1040
	 * Deduplicate the share target in the current user home folder,
1041
	 * based on configured share folder
1042
	 *
1043
	 * @param IShare $share share target to deduplicate
1044
	 * @return IShare same share with target updated if necessary
1045
	 */
1046
	private function deduplicateShareTarget(IShare $share) {
1047
		$userFolder = $this->rootFolder->getUserFolder($this->currentUser->getUID());
1048
		$mountPoint = \basename($share->getTarget());
1049
		$parentDir = \dirname($share->getTarget());
1050
		if (!$userFolder->nodeExists($parentDir)) {
1051
			$parentDir = Helper::getShareFolder();
1052
			$pathAttempt = \OC\Files\Filesystem::normalizePath($parentDir . '/' . $share->getTarget());
1053
		} else {
1054
			$pathAttempt = \OC\Files\Filesystem::normalizePath($share->getTarget());
1055
		}
1056
1057
		$pathinfo = \pathinfo($pathAttempt);
1058
		$ext = (isset($pathinfo['extension'])) ? '.'.$pathinfo['extension'] : '';
1059
		$name = $pathinfo['filename'];
1060
1061
		$i = 2;
1062
		while ($userFolder->nodeExists($pathAttempt)) {
1063
			$pathAttempt = \OC\Files\Filesystem::normalizePath($parentDir . '/' . $name . ' ('.$i.')' . $ext);
1064
			$i++;
1065
		}
1066
1067
		$share->setTarget($pathAttempt);
1068
1069
		return $share;
1070
	}
1071
1072
	/**
1073
	 * @param \OCP\Share\IShare $share
1074
	 * @return bool
1075
	 */
1076
	protected function canAccessShare(\OCP\Share\IShare $share) {
1077
		// A file with permissions 0 can't be accessed by us,
1078
		// unless it's a rejected sub-group share in which case we want it visible to let the user accept it again
1079
		if ($share->getPermissions() === 0
1080
			&& !($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP && $share->getState() === \OCP\Share::STATE_REJECTED)) {
1081
			return false;
1082
		}
1083
1084
		// Owner of the file and the sharer of the file can always get share
1085
		if ($share->getShareOwner() === $this->currentUser->getUID() ||
1086
			$share->getSharedBy() === $this->currentUser->getUID()
1087
		) {
1088
			return true;
1089
		}
1090
1091
		// If the share is shared with you (or a group you are a member of)
1092
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER &&
1093
			$share->getSharedWith() === $this->currentUser->getUID()) {
1094
			return true;
1095
		}
1096
1097
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
1098
			$sharedWith = $this->groupManager->get($share->getSharedWith());
1099
			if ($sharedWith !== null && $sharedWith->inGroup($this->currentUser)) {
1100
				return true;
1101
			}
1102
		}
1103
1104
		return false;
1105
	}
1106
1107
	/**
1108
	 * Make sure that the passed date is valid ISO 8601
1109
	 * So YYYY-MM-DD
1110
	 * If not throw an exception
1111
	 *
1112
	 * @param string $expireDate
1113
	 *
1114
	 * @throws \Exception
1115
	 * @return \DateTime
1116
	 */
1117
	private function parseDate($expireDate) {
1118
		try {
1119
			$date = new \DateTime($expireDate);
1120
		} catch (\Exception $e) {
1121
			throw new \Exception('Invalid date. Format must be YYYY-MM-DD');
1122
		}
1123
1124
		if ($date === false) {
1125
			throw new \Exception('Invalid date. Format must be YYYY-MM-DD');
1126
		}
1127
1128
		$date->setTime(0, 0, 0);
1129
1130
		return $date;
1131
	}
1132
1133
	/**
1134
	 * Since we have multiple providers but the OCS Share API v1 does
1135
	 * not support this we need to check all backends.
1136
	 *
1137
	 * @param string $id
1138
	 * @return \OCP\Share\IShare
1139
	 * @throws ShareNotFound
1140
	 */
1141
	private function getShareById($id, $recipient = null) {
1142
		$share = null;
1143
1144
		// First check if it is an internal share.
1145
		try {
1146
			$share = $this->shareManager->getShareById('ocinternal:'.$id, $recipient);
1147
		} catch (ShareNotFound $e) {
1148
			if (!$this->shareManager->outgoingServer2ServerSharesAllowed()) {
1149
				throw new ShareNotFound();
1150
			}
1151
1152
			$share = $this->shareManager->getShareById('ocFederatedSharing:' . $id, $recipient);
1153
		}
1154
1155
		return $share;
1156
	}
1157
}
1158