Share20OcsController::updateShare()   F
last analyzed

Complexity

Conditions 40
Paths 8793

Size

Total Lines 163

Duplication

Lines 17
Ratio 10.43 %

Importance

Changes 0
Metric Value
cc 40
nc 8793
nop 1
dl 17
loc 163
rs 0
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;
38
use OCP\Share\Exceptions\GenericShareException;
39
use OCP\Share\Exceptions\ShareNotFound;
40
use OCP\Share\IManager;
41
use OCP\Share\IShare;
42
use OCA\Files_Sharing\Service\NotificationPublisher;
43
use OCA\Files_Sharing\Helper;
44
use OCA\Files_Sharing\SharingBlacklist;
45
use Symfony\Component\EventDispatcher\EventDispatcher;
46
use Symfony\Component\EventDispatcher\GenericEvent;
47
48
/**
49
 * Class Share20OcsController
50
 *
51
 * @package OCA\Files_Sharing\Controller
52
 */
53
class Share20OcsController extends OCSController {
54
	/** @var IManager */
55
	private $shareManager;
56
	/** @var IGroupManager */
57
	private $groupManager;
58
	/** @var IUserManager */
59
	private $userManager;
60
	/** @var IRootFolder */
61
	private $rootFolder;
62
	/** @var IURLGenerator */
63
	private $urlGenerator;
64
	/** @var IUser */
65
	private $currentUser;
66
	/** @var IL10N */
67
	private $l;
68
	/** @var IConfig */
69
	private $config;
70
	/** @var NotificationPublisher */
71
	private $notificationPublisher;
72
	/** @var EventDispatcher  */
73
	private $eventDispatcher;
74
	/** @var SharingBlacklist */
75
	private $sharingBlacklist;
76
77
	/**
78
	 * @var string
79
	 */
80
	private $additionalInfoField;
81
82 View Code Duplication
	public function __construct(
83
		$appName,
84
		IRequest $request,
85
		IManager $shareManager,
86
		IGroupManager $groupManager,
87
		IUserManager $userManager,
88
		IRootFolder $rootFolder,
89
		IURLGenerator $urlGenerator,
90
		IUser $currentUser,
91
		IL10N $l10n,
92
		IConfig $config,
93
		NotificationPublisher $notificationPublisher,
94
		EventDispatcher $eventDispatcher,
95
		SharingBlacklist $sharingBlacklist
96
	) {
97
		parent::__construct($appName, $request);
98
		$this->request = $request;
99
		$this->shareManager = $shareManager;
100
		$this->groupManager = $groupManager;
101
		$this->userManager = $userManager;
102
		$this->rootFolder = $rootFolder;
103
		$this->urlGenerator = $urlGenerator;
104
		$this->currentUser = $currentUser;
105
		$this->l = $l10n;
106
		$this->config = $config;
107
		$this->notificationPublisher = $notificationPublisher;
108
		$this->eventDispatcher = $eventDispatcher;
109
		$this->sharingBlacklist = $sharingBlacklist;
110
		$this->additionalInfoField = $this->config->getAppValue('core', 'user_additional_info_field', '');
111
	}
112
113
	/**
114
	 * Returns the additional info to display behind the display name as configured.
115
	 *
116
	 * @param IUser $user user for which to retrieve the additional info
117
	 * @return string|null additional info or null if none to be displayed
118
	 */
119 View Code Duplication
	private function getAdditionalUserInfo(IUser $user) {
120
		if ($this->additionalInfoField === 'email') {
121
			return $user->getEMailAddress();
122
		} elseif ($this->additionalInfoField === 'id') {
123
			return $user->getUID();
124
		}
125
		return null;
126
	}
127
128
	/**
129
	 * Convert an IShare to an array for OCS output
130
	 *
131
	 * @param IShare $share
132
	 * @param bool $received whether it's formatting received shares
133
	 * @return array
134
	 * @throws NotFoundException In case the node can't be resolved.
135
	 */
136
	protected function formatShare(IShare $share, $received = false) {
137
		$sharedBy = $this->userManager->get($share->getSharedBy());
138
		$shareOwner = $this->userManager->get($share->getShareOwner());
139
140
		$result = [
141
			'id' => $share->getId(),
142
			'share_type' => $share->getShareType(),
143
			'uid_owner' => $share->getSharedBy(),
144
			'displayname_owner' => $sharedBy !== null ? $sharedBy->getDisplayName() : $share->getSharedBy(),
145
			'permissions' => $share->getPermissions(),
146
			'stime' => $share->getShareTime() ? $share->getShareTime()->getTimestamp() : null,
147
			'parent' => null,
148
			'expiration' => null,
149
			'token' => null,
150
			'uid_file_owner' => $share->getShareOwner(),
151
			'displayname_file_owner' => $shareOwner !== null ? $shareOwner->getDisplayName() : $share->getShareOwner()
152
		];
153
154
		if ($received) {
155
			// also add state
156
			$result['state'] = $share->getState();
157
158
			// can only fetch path info if mounted already or if owner
159
			if ($share->getState() === Share::STATE_ACCEPTED || $share->getShareOwner() === $this->currentUser->getUID()) {
160
				$userFolder = $this->rootFolder->getUserFolder($this->currentUser->getUID());
161
			} else {
162
				// need to go through owner user for pending shares
163
				$userFolder = $this->rootFolder->getUserFolder($share->getShareOwner());
164
			}
165
		} else {
166
			$userFolder = $this->rootFolder->getUserFolder($this->currentUser->getUID());
167
		}
168
169
		$nodes = $userFolder->getById($share->getNodeId());
170
171
		if (empty($nodes)) {
172
			throw new NotFoundException();
173
		}
174
175
		$node = $nodes[0];
176
177
		$result['path'] = $userFolder->getRelativePath($node->getPath());
178
		if ($node instanceof \OCP\Files\Folder) {
179
			$result['item_type'] = 'folder';
180
		} else {
181
			$result['item_type'] = 'file';
182
		}
183
		$result['mimetype'] = $node->getMimeType();
184
		$result['storage_id'] = $node->getStorage()->getId();
185
		$result['storage'] = $node->getStorage()->getCache()->getNumericStorageId();
186
		$result['item_source'] = \strval($node->getId());
187
		$result['file_source'] = \strval($node->getId());
188
		$result['file_parent'] = \strval($node->getParent()->getId());
189
		$result['file_target'] = $share->getTarget();
190
191
		if ($share->getShareType() === Share::SHARE_TYPE_USER) {
192
			$sharedWith = $this->userManager->get($share->getSharedWith());
193
			$result['share_with'] = $share->getSharedWith();
194
			$result['share_with_displayname'] = $sharedWith !== null ? $sharedWith->getDisplayName() : $share->getSharedWith();
195
			if ($sharedWith !== null) {
196
				$result['share_with_additional_info'] = $this->getAdditionalUserInfo($sharedWith);
197
			}
198
		} elseif ($share->getShareType() === Share::SHARE_TYPE_GROUP) {
199
			$group = $this->groupManager->get($share->getSharedWith());
200
			$result['share_with'] = $share->getSharedWith();
201
			$result['share_with_displayname'] = $group !== null ? $group->getDisplayName() : $share->getSharedWith();
202
		} elseif ($share->getShareType() === Share::SHARE_TYPE_LINK) {
203
			$result['share_with'] = $share->getPassword();
204
			$result['share_with_displayname'] = $share->getPassword();
205
			$result['name'] = $share->getName();
206
207
			$result['token'] = $share->getToken();
208
			if ($share->getToken() !== null) {
209
				$result['url'] = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.showShare', ['token' => $share->getToken()]);
210
			}
211
212
			$expiration = $share->getExpirationDate();
213
			if ($expiration !== null) {
214
				$result['expiration'] = $expiration->format('Y-m-d 00:00:00');
215
			}
216
		} elseif ($share->getShareType() === Share::SHARE_TYPE_REMOTE) {
217
			$result['share_with'] = $share->getSharedWith();
218
			$result['share_with_displayname'] = $share->getSharedWith();
219
			$result['token'] = $share->getToken();
220
		}
221
222
		$result['mail_send'] = $share->getMailSend() ? 1 : 0;
223
224
		return $result;
225
	}
226
227
	/**
228
	 * Get a specific share by id
229
	 *
230
	 * @NoCSRFRequired
231
	 * @NoAdminRequired
232
	 *
233
	 * @param string $id
234
	 * @return Result
235
	 */
236
	public function getShare($id) {
237
		if (!$this->shareManager->shareApiEnabled()) {
238
			return new Result(null, 404, $this->l->t('Share API is disabled'));
239
		}
240
241
		try {
242
			$share = $this->getShareById($id);
243
		} catch (ShareNotFound $e) {
244
			return new Result(null, 404, $this->l->t('Wrong share ID, share doesn\'t exist'));
245
		}
246
247
		if ($this->canAccessShare($share)) {
248
			try {
249
				$share = $this->formatShare($share);
250
				return new Result([$share]);
251
			} catch (NotFoundException $e) {
252
				//Fall trough
253
			}
254
		}
255
256
		return new Result(null, 404, $this->l->t('Wrong share ID, share doesn\'t exist'));
257
	}
258
259
	/**
260
	 * Delete a share
261
	 *
262
	 * @NoCSRFRequired
263
	 * @NoAdminRequired
264
	 *
265
	 * @param string $id
266
	 * @return Result
267
	 */
268
	public function deleteShare($id) {
269
		if (!$this->shareManager->shareApiEnabled()) {
270
			return new Result(null, 404, $this->l->t('Share API is disabled'));
271
		}
272
273
		try {
274
			$share = $this->getShareById($id);
275
		} catch (ShareNotFound $e) {
276
			return new Result(null, 404, $this->l->t('Wrong share ID, share doesn\'t exist'));
277
		}
278
279
		try {
280
			$share->getNode()->lock(ILockingProvider::LOCK_SHARED);
281
		} catch (LockedException $e) {
282
			return new Result(null, 404, 'could not delete share');
283
		}
284
285 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...
286
			$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
287
			return new Result(null, 404, $this->l->t('Could not delete share'));
288
		}
289
290
		$this->shareManager->deleteShare($share);
291
292
		$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
293
294
		return new Result();
295
	}
296
297
	/**
298
	 * @NoCSRFRequired
299
	 * @NoAdminRequired
300
	 *
301
	 * @return Result
302
	 */
303
	public function createShare() {
304
		$share = $this->shareManager->newShare();
305
306
		if (!$this->shareManager->shareApiEnabled()) {
307
			return new Result(null, 404, $this->l->t('Share API is disabled'));
308
		}
309
310
		$name = $this->request->getParam('name', null);
311
312
		// Verify path
313
		$path = $this->request->getParam('path', null);
314
		if ($path === null) {
315
			return new Result(null, 404, $this->l->t('Please specify a file or folder path'));
316
		}
317
318
		$userFolder = $this->rootFolder->getUserFolder($this->currentUser->getUID());
319
320
		try {
321
			$path = $userFolder->get($path);
322
		} catch (NotFoundException $e) {
323
			return new Result(null, 404, $this->l->t('Wrong path, file/folder doesn\'t exist'));
324
		}
325
326
		$share->setNode($path);
327
328
		try {
329
			$share->getNode()->lock(ILockingProvider::LOCK_SHARED);
330
		} catch (LockedException $e) {
331
			return new Result(null, 404, 'Could not create share');
332
		}
333
334
		$shareType = (int)$this->request->getParam('shareType', '-1');
335
336
		// Parse permissions (if available)
337
		$permissions = $this->request->getParam('permissions', null);
338
		if ($permissions === null) {
339
			if ($shareType !== Share::SHARE_TYPE_LINK) {
340
				$permissions = $this->config->getAppValue('core', 'shareapi_default_permissions', \OCP\Constants::PERMISSION_ALL);
341
				$permissions |= \OCP\Constants::PERMISSION_READ;
342
			} else {
343
				$permissions = \OCP\Constants::PERMISSION_ALL;
344
			}
345
		} else {
346
			$permissions = (int)$permissions;
347
		}
348
349
		if ($permissions < 0 || $permissions > \OCP\Constants::PERMISSION_ALL) {
350
			$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
351
			return new Result(null, 404, 'invalid permissions');
352
		}
353
354
		if ($permissions === 0) {
355
			return new Result(null, 400, $this->l->t('Cannot remove all permissions'));
356
		}
357
358
		// link shares can have create-only without read (anonymous upload)
359
		if ($shareType !== Share::SHARE_TYPE_LINK && $permissions !== \OCP\Constants::PERMISSION_CREATE) {
360
			// Shares always require read permissions
361
			$permissions |= \OCP\Constants::PERMISSION_READ;
362
		}
363
364
		if ($path instanceof \OCP\Files\File) {
365
			// Single file shares should never have delete or create permissions
366
			$permissions &= ~\OCP\Constants::PERMISSION_DELETE;
367
			$permissions &= ~\OCP\Constants::PERMISSION_CREATE;
368
		}
369
370
		/*
371
		 * Hack for https://github.com/owncloud/core/issues/22587
372
		 * We check the permissions via webdav. But the permissions of the mount point
373
		 * do not equal the share permissions. Here we fix that for federated mounts.
374
		 */
375
		if ($path->getStorage()->instanceOfStorage('OCA\Files_Sharing\External\Storage')) {
376
			$permissions &= ~($permissions & ~$path->getPermissions());
377
		}
378
379
		$shareWith = $this->request->getParam('shareWith', null);
380
381
		$autoAccept = $this->config->getAppValue('core', 'shareapi_auto_accept_share', 'yes') === 'yes';
382
		if ($shareType === Share::SHARE_TYPE_USER) {
383
			// Valid user is required to share
384
			if ($shareWith === null || !$this->userManager->userExists($shareWith)) {
385
				$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
386
				return new Result(null, 404, $this->l->t('Please specify a valid user'));
387
			}
388
			$share->setSharedWith($shareWith);
389
			$share->setPermissions($permissions);
390
			if ($autoAccept) {
391
				$share->setState(Share::STATE_ACCEPTED);
392
			} else {
393
				$share->setState(Share::STATE_PENDING);
394
			}
395
		} elseif ($shareType === Share::SHARE_TYPE_GROUP) {
396
			if (!$this->shareManager->allowGroupSharing()) {
397
				$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
398
				return new Result(null, 404, $this->l->t('Group sharing is disabled by the administrator'));
399
			}
400
401
			// Valid group is required to share
402
			if ($shareWith === null || !$this->groupManager->groupExists($shareWith)) {
403
				$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
404
				return new Result(null, 404, $this->l->t('Please specify a valid group'));
405
			}
406
			if ($this->sharingBlacklist->isGroupBlacklisted($this->groupManager->get($shareWith))) {
407
				return new Result(null, 403, $this->l->t('The group is blacklisted for sharing'));
408
			}
409
			$share->setSharedWith($shareWith);
410
			$share->setPermissions($permissions);
411
			if ($autoAccept) {
412
				$share->setState(Share::STATE_ACCEPTED);
413
			} else {
414
				$share->setState(Share::STATE_PENDING);
415
			}
416
		} elseif ($shareType === Share::SHARE_TYPE_LINK) {
417
			//Can we even share links?
418
			if (!$this->shareManager->shareApiAllowLinks()) {
419
				$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
420
				return new Result(null, 404, $this->l->t('Public link sharing is disabled by the administrator'));
421
			}
422
423
			// legacy way, expecting that this won't be used together with "create-only" shares
424
			$publicUpload = $this->request->getParam('publicUpload', null);
425
			// a few permission checks
426 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...
427
				// Check if public upload is allowed
428
				if (!$this->shareManager->shareApiLinkAllowPublicUpload()) {
429
					$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
430
					return new Result(null, 403, $this->l->t('Public upload disabled by the administrator'));
431
				}
432
433
				// Public upload can only be set for folders
434
				if ($path instanceof \OCP\Files\File) {
435
					$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
436
					return new Result(null, 404, $this->l->t('Public upload is only possible for publicly shared folders'));
437
				}
438
			}
439
440
			// convert to permissions
441
			if ($publicUpload === 'true') {
442
				$share->setPermissions(
443
					\OCP\Constants::PERMISSION_READ |
444
					\OCP\Constants::PERMISSION_CREATE |
445
					\OCP\Constants::PERMISSION_UPDATE |
446
					\OCP\Constants::PERMISSION_DELETE
447
				);
448
			} elseif ($permissions === \OCP\Constants::PERMISSION_CREATE ||
449
				$permissions === (\OCP\Constants::PERMISSION_READ | \OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE)) {
450
				$share->setPermissions($permissions);
451
			} else {
452
				// because when "publicUpload" is passed usually no permissions are set,
453
				// which defaults to ALL. But in the case of link shares we default to READ...
454
				$share->setPermissions(\OCP\Constants::PERMISSION_READ);
455
			}
456
457
			// set name only if passed as parameter, empty string is allowed
458
			if ($name !== null) {
459
				$share->setName($name);
460
			}
461
462
			// Set password
463
			$password = $this->request->getParam('password', '');
464
465
			if ($password !== '') {
466
				$share->setPassword($password);
467
			}
468
469
			//Expire date
470
			$expireDate = $this->request->getParam('expireDate', '');
471
472
			if ($expireDate !== '') {
473
				try {
474
					$expireDate = $this->parseDate($expireDate);
475
					$share->setExpirationDate($expireDate);
476
				} catch (\Exception $e) {
477
					$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
478
					return new Result(null, 404, $this->l->t('Invalid date, date format must be YYYY-MM-DD'));
479
				}
480
			}
481
		} elseif ($shareType === Share::SHARE_TYPE_REMOTE) {
482
			if (!$this->shareManager->outgoingServer2ServerSharesAllowed()) {
483
				$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
484
				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]));
485
			}
486
487
			$share->setSharedWith($shareWith);
488
			$share->setPermissions($permissions);
489 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...
490
			$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
491
			return new Result(null, 400, $this->l->t('Unknown share type'));
492
		}
493
494
		$share->setShareType($shareType);
495
		$share->setSharedBy($this->currentUser->getUID());
496
497
		try {
498
			$share = $this->shareManager->createShare($share);
499
		} catch (GenericShareException $e) {
500
			$code = $e->getCode() === 0 ? 403 : $e->getCode();
501
			$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
502
			return new Result(null, $code, $e->getHint());
503
		} catch (\Exception $e) {
504
			$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
505
			return new Result(null, 403, $e->getMessage());
506
		}
507
508
		$share->getNode()->unlock(\OCP\Lock\ILockingProvider::LOCK_SHARED);
509
510
		$formattedShareAfterCreate = $this->formatShare($share);
511
512
		return new Result($formattedShareAfterCreate);
513
	}
514
515
	/**
516
	 * @param \OCP\Files\File|\OCP\Files\Folder $node
517
	 * @param boolean $includeTags include tags in response
518
	 * @param int|null $stateFilter state filter or empty for all, defaults to 0 (accepted)
519
	 * @return Result
520
	 */
521
	private function getSharedWithMe($node = null, $includeTags, $stateFilter = 0) {
522
		$userShares = $this->shareManager->getSharedWith($this->currentUser->getUID(), Share::SHARE_TYPE_USER, $node, -1, 0);
523
		$groupShares = $this->shareManager->getSharedWith($this->currentUser->getUID(), Share::SHARE_TYPE_GROUP, $node, -1, 0);
524
525
		$shares = \array_merge($userShares, $groupShares);
526
527
		$shares = \array_filter($shares, function (IShare $share) {
528
			return $share->getShareOwner() !== $this->currentUser->getUID();
529
		});
530
531
		$formatted = [];
532
		foreach ($shares as $share) {
533
			if (($stateFilter === null || $share->getState() === $stateFilter) &&
534
				$this->canAccessShare($share)) {
535
				try {
536
					$formatted[] = $this->formatShare($share, true);
537
				} catch (NotFoundException $e) {
538
					// Ignore this share
539
				}
540
			}
541
		}
542
543
		if ($includeTags) {
544
			$formatted = \OCA\Files\Helper::populateTags($formatted, 'file_source');
545
		}
546
547
		return new Result($formatted);
548
	}
549
550
	/**
551
	 * @param \OCP\Files\Folder $folder
552
	 * @return Result
553
	 */
554
	private function getSharesInDir($folder) {
555
		if (!($folder instanceof \OCP\Files\Folder)) {
556
			return new Result(null, 400, $this->l->t('Not a directory'));
557
		}
558
559
		$nodes = $folder->getDirectoryListing();
560
		/** @var IShare[] $shares */
561
		$shares = [];
562
		foreach ($nodes as $node) {
563
			$shares = \array_merge($shares, $this->shareManager->getSharesBy($this->currentUser->getUID(), Share::SHARE_TYPE_USER, $node, false, -1, 0));
564
			$shares = \array_merge($shares, $this->shareManager->getSharesBy($this->currentUser->getUID(), Share::SHARE_TYPE_GROUP, $node, false, -1, 0));
565
			$shares = \array_merge($shares, $this->shareManager->getSharesBy($this->currentUser->getUID(), Share::SHARE_TYPE_LINK, $node, false, -1, 0));
566
			if ($this->shareManager->outgoingServer2ServerSharesAllowed()) {
567
				$shares = \array_merge($shares, $this->shareManager->getSharesBy($this->currentUser->getUID(), Share::SHARE_TYPE_REMOTE, $node, false, -1, 0));
568
			}
569
		}
570
571
		$formatted = [];
572
		foreach ($shares as $share) {
573
			try {
574
				$formatted[] = $this->formatShare($share);
575
			} catch (NotFoundException $e) {
576
				//Ignore this share
577
			}
578
		}
579
580
		return new Result($formatted);
581
	}
582
583
	/**
584
	 * The getShares function.
585
	 *
586
	 * @NoCSRFRequired
587
	 * @NoAdminRequired
588
	 *
589
	 * - Get shares by the current user
590
	 * - Get shares by the current user and reshares (?reshares=true)
591
	 * - Get shares with the current user (?shared_with_me=true)
592
	 * - Get shares for a specific path (?path=...)
593
	 * - Get all shares in a folder (?subfiles=true&path=..)
594
	 *
595
	 * @return Result
596
	 */
597
	public function getShares() {
598
		if (!$this->shareManager->shareApiEnabled()) {
599
			return new Result();
600
		}
601
602
		$sharedWithMe = $this->request->getParam('shared_with_me', null);
603
		$reshares = $this->request->getParam('reshares', null);
604
		$subfiles = $this->request->getParam('subfiles');
605
		$path = $this->request->getParam('path', null);
606
607
		$includeTags = $this->request->getParam('include_tags', false);
608
609
		if ($path !== null) {
610
			$userFolder = $this->rootFolder->getUserFolder($this->currentUser->getUID());
611
			try {
612
				$path = $userFolder->get($path);
613
				$path->lock(ILockingProvider::LOCK_SHARED);
614
			} catch (\OCP\Files\NotFoundException $e) {
615
				return new Result(null, 404, $this->l->t('Wrong path, file/folder doesn\'t exist'));
616
			} catch (LockedException $e) {
617
				return new Result(null, 404, $this->l->t('Could not lock path'));
618
			}
619
		}
620
621
		if ($sharedWithMe === 'true') {
622
			$stateFilter = $this->request->getParam('state', Share::STATE_ACCEPTED);
623
			if ($stateFilter === '') {
624
				$stateFilter = Share::STATE_ACCEPTED;
625
			} elseif ($stateFilter === 'all') {
626
				$stateFilter = null; // which means all
627
			} else {
628
				$stateFilter = (int)$stateFilter;
629
			}
630
			$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...
631
			if ($path !== null) {
632
				$path->unlock(ILockingProvider::LOCK_SHARED);
633
			}
634
			return $result;
635
		}
636
637
		if ($subfiles === 'true') {
638
			$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...
639
			if ($path !== null) {
640
				$path->unlock(ILockingProvider::LOCK_SHARED);
641
			}
642
			return $result;
643
		}
644
645
		if ($reshares === 'true') {
646
			$reshares = true;
647
		} else {
648
			$reshares = false;
649
		}
650
651
		// Get all shares
652
		$userShares = $this->shareManager->getSharesBy($this->currentUser->getUID(), Share::SHARE_TYPE_USER, $path, $reshares, -1, 0);
653
		$groupShares = $this->shareManager->getSharesBy($this->currentUser->getUID(), Share::SHARE_TYPE_GROUP, $path, $reshares, -1, 0);
654
		$linkShares = $this->shareManager->getSharesBy($this->currentUser->getUID(), Share::SHARE_TYPE_LINK, $path, $reshares, -1, 0);
655
		$shares = \array_merge($userShares, $groupShares, $linkShares);
656
657
		if ($this->shareManager->outgoingServer2ServerSharesAllowed()) {
658
			$federatedShares = $this->shareManager->getSharesBy($this->currentUser->getUID(), Share::SHARE_TYPE_REMOTE, $path, $reshares, -1, 0);
659
			$shares = \array_merge($shares, $federatedShares);
660
		}
661
662
		$formatted = [];
663
		foreach ($shares as $share) {
664
			try {
665
				$formatted[] = $this->formatShare($share);
666
			} catch (NotFoundException $e) {
667
				//Ignore share
668
			}
669
		}
670
671
		if ($includeTags) {
672
			$formatted = \OCA\Files\Helper::populateTags($formatted, 'file_source');
673
		}
674
675
		if ($path !== null) {
676
			$path->unlock(ILockingProvider::LOCK_SHARED);
677
		}
678
679
		return new Result($formatted);
680
	}
681
682
	/**
683
	 * @NoCSRFRequired
684
	 * @NoAdminRequired
685
	 *
686
	 * @param int $id
687
	 * @return Result
688
	 */
689
	public function updateShare($id) {
690
		if (!$this->shareManager->shareApiEnabled()) {
691
			return new Result(null, 404, $this->l->t('Share API is disabled'));
692
		}
693
694
		try {
695
			$share = $this->getShareById($id);
696
		} catch (ShareNotFound $e) {
697
			return new Result(null, 404, $this->l->t('Wrong share ID, share doesn\'t exist'));
698
		}
699
700
		$share->getNode()->lock(\OCP\Lock\ILockingProvider::LOCK_SHARED);
701
702 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...
703
			$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
704
			return new Result(null, 404, $this->l->t('Wrong share ID, share doesn\'t exist'));
705
		}
706
707
		$permissions = $this->request->getParam('permissions', null);
708
		$password = $this->request->getParam('password', null);
709
		$publicUpload = $this->request->getParam('publicUpload', null);
710
		$expireDate = $this->request->getParam('expireDate', null);
711
		$name = $this->request->getParam('name', null);
712
713
		/*
714
		 * expirationdate, password and publicUpload only make sense for link shares
715
		 */
716
		if ($share->getShareType() === Share::SHARE_TYPE_LINK) {
717
			if ($permissions === null && $password === null && $publicUpload === null && $expireDate === null && $name === null) {
718
				$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
719
				return new Result(null, 400, 'Wrong or no update parameter given');
720
			}
721
722
			$newPermissions = null;
723
			if ($publicUpload === 'true') {
724
				$newPermissions = \OCP\Constants::PERMISSION_READ | \OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE;
725
			} elseif ($publicUpload === 'false') {
726
				$newPermissions = \OCP\Constants::PERMISSION_READ;
727
			}
728
729
			if ($permissions !== null) {
730
				$newPermissions = (int)$permissions;
731
			}
732
733
			if ($newPermissions !== null &&
734
				$newPermissions !== \OCP\Constants::PERMISSION_READ &&
735
				$newPermissions !== \OCP\Constants::PERMISSION_CREATE &&
736
				// legacy
737
				$newPermissions !== (\OCP\Constants::PERMISSION_READ | \OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE) &&
738
				// correct
739
				$newPermissions !== (\OCP\Constants::PERMISSION_READ | \OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE)
740
			) {
741
				$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
742
				return new Result(null, 400, $this->l->t('Can\'t change permissions for public share links'));
743
			}
744
745
			if (
746
				// legacy
747
				$newPermissions === (\OCP\Constants::PERMISSION_READ | \OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE) ||
748
				// correct
749
				$newPermissions === (\OCP\Constants::PERMISSION_READ | \OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE)
750
			) {
751
				if (!$this->shareManager->shareApiLinkAllowPublicUpload()) {
752
					$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
753
					return new Result(null, 403, $this->l->t('Public upload disabled by the administrator'));
754
				}
755
756
				if (!($share->getNode() instanceof \OCP\Files\Folder)) {
757
					$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
758
					return new Result(null, 400, $this->l->t('Public upload is only possible for publicly shared folders'));
759
				}
760
761
				// normalize to correct public upload permissions
762
				$newPermissions = \OCP\Constants::PERMISSION_READ | \OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE;
763
			}
764
765
			// create-only (upload-only)
766 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...
767
				$newPermissions === \OCP\Constants::PERMISSION_CREATE
768
			) {
769
				if (!$this->shareManager->shareApiLinkAllowPublicUpload()) {
770
					$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
771
					return new Result(null, 403, $this->l->t('Public upload disabled by the administrator'));
772
				}
773
774
				if (!($share->getNode() instanceof \OCP\Files\Folder)) {
775
					$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
776
					return new Result(null, 400, $this->l->t('Public upload is only possible for publicly shared folders'));
777
				}
778
			}
779
780
			// set name only if passed as parameter, empty string is allowed
781
			if ($name !== null) {
782
				$oldname = $share->getName();
783
				$share->setName($name);
784
			}
785
786
			if ($newPermissions !== null) {
787
				$share->setPermissions($newPermissions);
788
				$permissions = $newPermissions;
789
			}
790
791
			if ($expireDate === '') {
792
				$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...
793
			} elseif ($expireDate !== null) {
794
				try {
795
					$expireDate = $this->parseDate($expireDate);
796
				} catch (\Exception $e) {
797
					$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
798
					return new Result(null, 400, $e->getMessage());
799
				}
800
				$share->setExpirationDate($expireDate);
801
			}
802
803
			if ($password === '') {
804
				$share->setPassword(null);
805
			} elseif ($password !== null) {
806
				$share->setPassword($password);
807
			}
808
		} else {
809
			// For other shares only permissions is valid.
810
			if ($permissions === null) {
811
				$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
812
				return new Result(null, 400, $this->l->t('Wrong or no update parameter given'));
813
			} else {
814
				$permissions = (int)$permissions;
815
				$share->setPermissions($permissions);
816
			}
817
		}
818
819
		if ($permissions !== null && $share->getShareOwner() !== $this->currentUser->getUID()) {
820
			/* Check if this is an incoming share */
821
			$incomingShares = $this->shareManager->getSharedWith($this->currentUser->getUID(), Share::SHARE_TYPE_USER, $share->getNode(), -1, 0);
822
			$incomingShares = \array_merge($incomingShares, $this->shareManager->getSharedWith($this->currentUser->getUID(), Share::SHARE_TYPE_GROUP, $share->getNode(), -1, 0));
823
824
			if (!empty($incomingShares)) {
825
				$maxPermissions = 0;
826
				foreach ($incomingShares as $incomingShare) {
827
					$maxPermissions |= $incomingShare->getPermissions();
828
				}
829
830
				if ($share->getPermissions() & ~$maxPermissions) {
831
					$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
832
					return new Result(null, 404, $this->l->t('Cannot increase permissions'));
833
				}
834
			}
835
		}
836
837
		if ($share->getPermissions() === 0) {
838
			return new Result(null, 400, $this->l->t('Cannot remove all permissions'));
839
		}
840
841
		try {
842
			$share = $this->shareManager->updateShare($share);
843
		} catch (\Exception $e) {
844
			$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
845
			return new Result(null, 400, $e->getMessage());
846
		}
847
848
		$share->getNode()->unlock(\OCP\Lock\ILockingProvider::LOCK_SHARED);
849
850
		return new Result($this->formatShare($share));
851
	}
852
853
	/**
854
	 * @NoCSRFRequired
855
	 * @NoAdminRequired
856
	 *
857
	 * @param int $id
858
	 * @return Result
859
	 */
860
	public function acceptShare($id) {
861
		return $this->updateShareState($id, Share::STATE_ACCEPTED);
862
	}
863
864
	/**
865
	 * @NoCSRFRequired
866
	 * @NoAdminRequired
867
	 *
868
	 * @param int $id
869
	 * @return Result
870
	 */
871
	public function declineShare($id) {
872
		return $this->updateShareState($id, Share::STATE_REJECTED);
873
	}
874
875
	/**
876
	 * Send a notification to share recipient(s)
877
	 *
878
	 * @NoCSRFRequired
879
	 * @NoAdminRequired
880
	 *
881
	 * @param int $itemSource
882
	 * @param int $shareType
883
	 * @param string $recipient
884
	 *
885
	 * @return Result
886
	 */
887
	public function notifyRecipients($itemSource, $shareType, $recipient) {
888
		$recipientList = [];
889
		if ($shareType === Share::SHARE_TYPE_USER) {
890
			$recipientList[] = $this->userManager->get($recipient);
891
		} elseif ($shareType === Share::SHARE_TYPE_GROUP) {
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->shareManager,
904
			$this->currentUser,
905
			\OC::$server->getL10N('lib'),
906
			\OC::$server->getMailer(),
907
			$this->config,
908
			\OC::$server->getLogger(),
909
			$defaults,
910
			$this->urlGenerator,
911
			$this->eventDispatcher
912
		);
913
914
		$userFolder = $this->rootFolder->getUserFolder($this->currentUser->getUID());
915
		$nodes = $userFolder->getById($itemSource);
916
		$node = $nodes[0];
917
		$result = $mailNotification->sendInternalShareMail($node, $shareType, $recipientList);
0 ignored issues
show
Documentation introduced by
$node is of type object<OCP\Files\Node>, but the function expects a object<OC\Share\shared>.

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...
918
919
		// if we were able to send to at least one recipient, mark as sent
920
		// allowing the user to resend would spam users who already got a notification
921
		if (\count($result) < \count($recipientList)) {
922
			$items = $this->shareManager->getSharedWith($recipient, $shareType, $node);
923 View Code Duplication
			if (\count($items) > 0) {
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...
924
				$share = $items[0];
925
				$share->setMailSend(true);
926
				$this->shareManager->updateShare($share);
927
			}
928
		}
929
930
		$message = empty($result)
931
			? null
932
			: $this->l->t(
933
				"Couldn't send mail to following recipient(s): %s ",
934
				\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...
935
			);
936
		return new Result([], 200, $message);
937
	}
938
939
	/**
940
	 * Just mark a notification to share recipient(s) as sent
941
	 *
942
	 * @NoCSRFRequired
943
	 * @NoAdminRequired
944
	 *
945
	 * @param int $itemSource
946
	 * @param int $shareType
947
	 * @param string $recipient
948
	 *
949
	 * @return Result
950
	 */
951
	public function notifyRecipientsDisabled($itemSource, $shareType, $recipient) {
952
		$userFolder = $this->rootFolder->getUserFolder($this->currentUser->getUID());
953
		$nodes = $userFolder->getById($itemSource);
954
		$node = $nodes[0];
955
956
		$items = $this->shareManager->getSharedWith($recipient, $shareType, $node);
957 View Code Duplication
		if (\count($items) > 0) {
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...
958
			$share = $items[0];
959
			$share->setMailSend(true);
960
			$this->shareManager->updateShare($share);
961
		}
962
		return new Result();
963
	}
964
965
	/**
966
	 * @param $id
967
	 * @param $state
968
	 * @return Result
969
	 */
970
	private function updateShareState($id, $state) {
971
		$eventName = '';
972
		if ($state === Share::STATE_ACCEPTED) {
973
			$eventName = 'accept';
974
		} elseif ($state === Share::STATE_REJECTED) {
975
			$eventName = 'reject';
976
		}
977
978
		if (!$this->shareManager->shareApiEnabled()) {
979
			return new Result(null, 404, $this->l->t('Share API is disabled'));
980
		}
981
982
		try {
983
			$share = $this->getShareById($id, $this->currentUser->getUID());
984
			$this->eventDispatcher->dispatch('share.before' . $eventName, new GenericEvent(null, ['share' => $share]));
985
		} catch (ShareNotFound $e) {
986
			return new Result(null, 404, $this->l->t('Wrong share ID, share doesn\'t exist'));
987
		}
988
989
		$node = $share->getNode();
990
		$node->lock(\OCP\Lock\ILockingProvider::LOCK_SHARED);
991
992
		// this checks that we are either the owner or recipient
993 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...
994
			$node->unlock(ILockingProvider::LOCK_SHARED);
995
			return new Result(null, 404, $this->l->t('Wrong share ID, share doesn\'t exist'));
996
		}
997
998
		// only recipient can accept/reject share
999
		if ($share->getShareOwner() === $this->currentUser->getUID() ||
1000
			$share->getSharedBy() === $this->currentUser->getUID()) {
1001
			$node->unlock(ILockingProvider::LOCK_SHARED);
1002
			return new Result(null, 403, $this->l->t('Only recipient can change accepted state'));
1003
		}
1004
1005
		if ($share->getState() === $state) {
1006 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...
1007
				$this->eventDispatcher->dispatch('share.after' . $eventName, new GenericEvent(null, ['share' => $share]));
1008
			}
1009
			// if there are no changes in the state, just return the share as if the change was successful
1010
			$node->unlock(\OCP\Lock\ILockingProvider::LOCK_SHARED);
1011
			return new Result([$this->formatShare($share, true)]);
1012
		}
1013
1014
		// we actually want to update all shares related to the node in case there are multiple
1015
		// incoming shares for the same node (ex: receiving simultaneously through group share and user share)
1016
		$allShares = $this->shareManager->getSharedWith($this->currentUser->getUID(), Share::SHARE_TYPE_USER, $node, -1, 0);
1017
		$allShares = \array_merge($allShares, $this->shareManager->getSharedWith($this->currentUser->getUID(), Share::SHARE_TYPE_GROUP, $node, -1, 0));
1018
1019
		// resolve and deduplicate target if accepting
1020
		if ($state === Share::STATE_ACCEPTED) {
1021
			$share = $this->deduplicateShareTarget($share);
1022
		}
1023
1024
		$share->setState($state);
1025
1026
		try {
1027
			foreach ($allShares as $aShare) {
1028
				$aShare->setState($share->getState());
1029
				$aShare->setTarget($share->getTarget());
1030
				$this->shareManager->updateShareForRecipient($aShare, $this->currentUser->getUID());
1031
			}
1032
		} catch (\Exception $e) {
1033
			$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
1034
			return new Result(null, 400, $e->getMessage());
1035
		}
1036
1037
		$node->unlock(\OCP\Lock\ILockingProvider::LOCK_SHARED);
1038
1039
		// FIXME: needs public API!
1040
		\OC\Files\Filesystem::tearDown();
1041
		// FIXME: trigger mount for user to make sure the new node is mounted already
1042
		// before formatShare resolves it
1043
		$this->rootFolder->getUserFolder($this->currentUser->getUID());
1044
1045
		$this->notificationPublisher->discardNotificationForUser($share, $this->currentUser->getUID());
1046
1047 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...
1048
			$this->eventDispatcher->dispatch('share.after' . $eventName, new GenericEvent(null, ['share' => $share]));
1049
		}
1050
		return new Result([$this->formatShare($share, true)]);
1051
	}
1052
1053
	/**
1054
	 * Deduplicate the share target in the current user home folder,
1055
	 * based on configured share folder
1056
	 *
1057
	 * @param IShare $share share target to deduplicate
1058
	 * @return IShare same share with target updated if necessary
1059
	 */
1060
	private function deduplicateShareTarget(IShare $share) {
1061
		$userFolder = $this->rootFolder->getUserFolder($this->currentUser->getUID());
1062
		$mountPoint = \basename($share->getTarget());
1063
		$parentDir = \dirname($share->getTarget());
1064
		if (!$userFolder->nodeExists($parentDir)) {
1065
			$parentDir = Helper::getShareFolder();
1066
			$pathAttempt = \OC\Files\Filesystem::normalizePath($parentDir . '/' . $share->getTarget());
1067
		} else {
1068
			$pathAttempt = \OC\Files\Filesystem::normalizePath($share->getTarget());
1069
		}
1070
1071
		$pathinfo = \pathinfo($pathAttempt);
1072
		$ext = (isset($pathinfo['extension'])) ? '.'.$pathinfo['extension'] : '';
1073
		$name = $pathinfo['filename'];
1074
1075
		$i = 2;
1076
		while ($userFolder->nodeExists($pathAttempt)) {
1077
			$pathAttempt = \OC\Files\Filesystem::normalizePath($parentDir . '/' . $name . ' ('.$i.')' . $ext);
1078
			$i++;
1079
		}
1080
1081
		$share->setTarget($pathAttempt);
1082
1083
		return $share;
1084
	}
1085
1086
	/**
1087
	 * @param IShare $share
1088
	 * @return bool
1089
	 */
1090
	protected function canAccessShare(IShare $share) {
1091
		// A file with permissions 0 can't be accessed by us,
1092
		// unless it's a rejected sub-group share in which case we want it visible to let the user accept it again
1093
		if ($share->getPermissions() === 0
1094
			&& !($share->getShareType() === Share::SHARE_TYPE_GROUP && $share->getState() === Share::STATE_REJECTED)) {
1095
			return false;
1096
		}
1097
1098
		// Owner of the file and the sharer of the file can always get share
1099
		if ($share->getShareOwner() === $this->currentUser->getUID() ||
1100
			$share->getSharedBy() === $this->currentUser->getUID()
1101
		) {
1102
			return true;
1103
		}
1104
1105
		// If the share is shared with you (or a group you are a member of)
1106
		if ($share->getShareType() === Share::SHARE_TYPE_USER &&
1107
			$share->getSharedWith() === $this->currentUser->getUID()) {
1108
			return true;
1109
		}
1110
1111
		if ($share->getShareType() === Share::SHARE_TYPE_GROUP) {
1112
			$sharedWith = $this->groupManager->get($share->getSharedWith());
1113
			if ($sharedWith !== null && $sharedWith->inGroup($this->currentUser)) {
1114
				return true;
1115
			}
1116
		}
1117
1118
		return false;
1119
	}
1120
1121
	/**
1122
	 * Make sure that the passed date is valid ISO 8601
1123
	 * So YYYY-MM-DD
1124
	 * If not throw an exception
1125
	 *
1126
	 * @param string $expireDate
1127
	 *
1128
	 * @throws \Exception
1129
	 * @return \DateTime
1130
	 */
1131
	private function parseDate($expireDate) {
1132
		try {
1133
			$date = new \DateTime($expireDate);
1134
		} catch (\Exception $e) {
1135
			throw new \Exception('Invalid date. Format must be YYYY-MM-DD');
1136
		}
1137
1138
		if ($date === false) {
1139
			throw new \Exception('Invalid date. Format must be YYYY-MM-DD');
1140
		}
1141
1142
		$date->setTime(0, 0, 0);
1143
1144
		return $date;
1145
	}
1146
1147
	/**
1148
	 * Since we have multiple providers but the OCS Share API v1 does
1149
	 * not support this we need to check all backends.
1150
	 *
1151
	 * @param string $id
1152
	 * @return IShare
1153
	 * @throws ShareNotFound
1154
	 */
1155
	private function getShareById($id, $recipient = null) {
1156
		$share = null;
1157
1158
		// First check if it is an internal share.
1159
		try {
1160
			$share = $this->shareManager->getShareById('ocinternal:'.$id, $recipient);
1161
		} catch (ShareNotFound $e) {
1162
			if (!$this->shareManager->outgoingServer2ServerSharesAllowed()) {
1163
				throw new ShareNotFound();
1164
			}
1165
1166
			$share = $this->shareManager->getShareById('ocFederatedSharing:' . $id, $recipient);
1167
		}
1168
1169
		return $share;
1170
	}
1171
}
1172