Completed
Pull Request — master (#32767)
by Victor
11:38
created

Share20OcsController::formatShare()   F

Complexity

Conditions 19
Paths 1176

Size

Total Lines 90

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 19
nc 1176
nop 2
dl 0
loc 90
rs 0.3499
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
	 * @param string $id
229
	 * @return \OC\OCS\Result
230
	 */
231
	public function getShare($id) {
232
		if (!$this->shareManager->shareApiEnabled()) {
233
			return new \OC\OCS\Result(null, 404, $this->l->t('Share API is disabled'));
234
		}
235
236
		try {
237
			$share = $this->getShareById($id);
238
		} catch (ShareNotFound $e) {
239
			return new \OC\OCS\Result(null, 404, $this->l->t('Wrong share ID, share doesn\'t exist'));
240
		}
241
242
		if ($this->canAccessShare($share)) {
243
			try {
244
				$share = $this->formatShare($share);
245
				return new \OC\OCS\Result([$share]);
246
			} catch (NotFoundException $e) {
247
				//Fall trough
248
			}
249
		}
250
251
		return new \OC\OCS\Result(null, 404, $this->l->t('Wrong share ID, share doesn\'t exist'));
252
	}
253
254
	/**
255
	 * Delete a share
256
	 *
257
	 * @param string $id
258
	 * @return \OC\OCS\Result
259
	 */
260
	public function deleteShare($id) {
261
		if (!$this->shareManager->shareApiEnabled()) {
262
			return new \OC\OCS\Result(null, 404, $this->l->t('Share API is disabled'));
263
		}
264
265
		try {
266
			$share = $this->getShareById($id);
267
		} catch (ShareNotFound $e) {
268
			return new \OC\OCS\Result(null, 404, $this->l->t('Wrong share ID, share doesn\'t exist'));
269
		}
270
271
		try {
272
			$share->getNode()->lock(ILockingProvider::LOCK_SHARED);
273
		} catch (LockedException $e) {
274
			return new \OC\OCS\Result(null, 404, 'could not delete share');
275
		}
276
277 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...
278
			$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
279
			return new \OC\OCS\Result(null, 404, $this->l->t('Could not delete share'));
280
		}
281
282
		$this->shareManager->deleteShare($share);
283
284
		$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
285
286
		return new \OC\OCS\Result();
287
	}
288
289
	/**
290
	 * @return \OC\OCS\Result
291
	 */
292
	public function createShare() {
293
		$share = $this->shareManager->newShare();
294
295
		if (!$this->shareManager->shareApiEnabled()) {
296
			return new \OC\OCS\Result(null, 404, $this->l->t('Share API is disabled'));
297
		}
298
299
		$name = $this->request->getParam('name', null);
300
301
		// Verify path
302
		$path = $this->request->getParam('path', null);
303
		if ($path === null) {
304
			return new \OC\OCS\Result(null, 404, $this->l->t('Please specify a file or folder path'));
305
		}
306
307
		$userFolder = $this->rootFolder->getUserFolder($this->currentUser->getUID());
308
309
		try {
310
			$path = $userFolder->get($path);
311
		} catch (NotFoundException $e) {
312
			return new \OC\OCS\Result(null, 404, $this->l->t('Wrong path, file/folder doesn\'t exist'));
313
		}
314
315
		$share->setNode($path);
316
317
		try {
318
			$share->getNode()->lock(ILockingProvider::LOCK_SHARED);
319
		} catch (LockedException $e) {
320
			return new \OC\OCS\Result(null, 404, 'Could not create share');
321
		}
322
323
		$shareType = (int)$this->request->getParam('shareType', '-1');
324
325
		// Parse permissions (if available)
326
		$permissions = $this->request->getParam('permissions', null);
327
		if ($permissions === null) {
328
			if ($shareType !== \OCP\Share::SHARE_TYPE_LINK) {
329
				$permissions = $this->config->getAppValue('core', 'shareapi_default_permissions', \OCP\Constants::PERMISSION_ALL);
330
				$permissions |= \OCP\Constants::PERMISSION_READ;
331
			} else {
332
				$permissions = \OCP\Constants::PERMISSION_ALL;
333
			}
334
		} else {
335
			$permissions = (int)$permissions;
336
		}
337
338
		if ($permissions < 0 || $permissions > \OCP\Constants::PERMISSION_ALL) {
339
			$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
340
			return new \OC\OCS\Result(null, 404, 'invalid permissions');
341
		}
342
343
		if ($permissions === 0) {
344
			return new \OC\OCS\Result(null, 400, $this->l->t('Cannot remove all permissions'));
345
		}
346
347
		// link shares can have create-only without read (anonymous upload)
348
		if ($shareType !== \OCP\Share::SHARE_TYPE_LINK && $permissions !== \OCP\Constants::PERMISSION_CREATE) {
349
			// Shares always require read permissions
350
			$permissions |= \OCP\Constants::PERMISSION_READ;
351
		}
352
353
		if ($path instanceof \OCP\Files\File) {
354
			// Single file shares should never have delete or create permissions
355
			$permissions &= ~\OCP\Constants::PERMISSION_DELETE;
356
			$permissions &= ~\OCP\Constants::PERMISSION_CREATE;
357
		}
358
359
		/*
360
		 * Hack for https://github.com/owncloud/core/issues/22587
361
		 * We check the permissions via webdav. But the permissions of the mount point
362
		 * do not equal the share permissions. Here we fix that for federated mounts.
363
		 */
364
		if ($path->getStorage()->instanceOfStorage('OCA\Files_Sharing\External\Storage')) {
365
			$permissions &= ~($permissions & ~$path->getPermissions());
366
		}
367
368
		$shareWith = $this->request->getParam('shareWith', null);
369
370
		$autoAccept = $this->config->getAppValue('core', 'shareapi_auto_accept_share', 'yes') === 'yes';
371
		if ($shareType === \OCP\Share::SHARE_TYPE_USER) {
372
			// Valid user is required to share
373
			if ($shareWith === null || !$this->userManager->userExists($shareWith)) {
374
				$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
375
				return new \OC\OCS\Result(null, 404, $this->l->t('Please specify a valid user'));
376
			}
377
			$share->setSharedWith($shareWith);
378
			$share->setPermissions($permissions);
379
			if ($autoAccept) {
380
				$share->setState(\OCP\Share::STATE_ACCEPTED);
381
			} else {
382
				$share->setState(\OCP\Share::STATE_PENDING);
383
			}
384
		} elseif ($shareType === \OCP\Share::SHARE_TYPE_GROUP) {
385
			if (!$this->shareManager->allowGroupSharing()) {
386
				$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
387
				return new \OC\OCS\Result(null, 404, $this->l->t('Group sharing is disabled by the administrator'));
388
			}
389
390
			// Valid group is required to share
391
			if ($shareWith === null || !$this->groupManager->groupExists($shareWith)) {
392
				$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
393
				return new \OC\OCS\Result(null, 404, $this->l->t('Please specify a valid group'));
394
			}
395
			if ($this->sharingBlacklist->isGroupBlacklisted($this->groupManager->get($shareWith))) {
396
				return new \OC\OCS\Result(null, 403, $this->l->t('The group is blacklisted for sharing'));
397
			}
398
			$share->setSharedWith($shareWith);
399
			$share->setPermissions($permissions);
400
			if ($autoAccept) {
401
				$share->setState(\OCP\Share::STATE_ACCEPTED);
402
			} else {
403
				$share->setState(\OCP\Share::STATE_PENDING);
404
			}
405
		} elseif ($shareType === \OCP\Share::SHARE_TYPE_LINK) {
406
			//Can we even share links?
407
			if (!$this->shareManager->shareApiAllowLinks()) {
408
				$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
409
				return new \OC\OCS\Result(null, 404, $this->l->t('Public link sharing is disabled by the administrator'));
410
			}
411
412
			// legacy way, expecting that this won't be used together with "create-only" shares
413
			$publicUpload = $this->request->getParam('publicUpload', null);
414
			// a few permission checks
415 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...
416
				// Check if public upload is allowed
417
				if (!$this->shareManager->shareApiLinkAllowPublicUpload()) {
418
					$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
419
					return new \OC\OCS\Result(null, 403, $this->l->t('Public upload disabled by the administrator'));
420
				}
421
422
				// Public upload can only be set for folders
423
				if ($path instanceof \OCP\Files\File) {
424
					$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
425
					return new \OC\OCS\Result(null, 404, $this->l->t('Public upload is only possible for publicly shared folders'));
426
				}
427
			}
428
429
			// convert to permissions
430
			if ($publicUpload === 'true') {
431
				$share->setPermissions(
432
					\OCP\Constants::PERMISSION_READ |
433
					\OCP\Constants::PERMISSION_CREATE |
434
					\OCP\Constants::PERMISSION_UPDATE |
435
					\OCP\Constants::PERMISSION_DELETE
436
				);
437
			} elseif ($permissions === \OCP\Constants::PERMISSION_CREATE ||
438
				$permissions === (\OCP\Constants::PERMISSION_READ | \OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE)) {
439
				$share->setPermissions($permissions);
440
			} else {
441
				// because when "publicUpload" is passed usually no permissions are set,
442
				// which defaults to ALL. But in the case of link shares we default to READ...
443
				$share->setPermissions(\OCP\Constants::PERMISSION_READ);
444
			}
445
446
			// set name only if passed as parameter, empty string is allowed
447
			if ($name !== null) {
448
				$share->setName($name);
449
			}
450
451
			// Set password
452
			$password = $this->request->getParam('password', '');
453
454
			if ($password !== '') {
455
				$share->setPassword($password);
456
			}
457
458
			//Expire date
459
			$expireDate = $this->request->getParam('expireDate', '');
460
461
			if ($expireDate !== '') {
462
				try {
463
					$expireDate = $this->parseDate($expireDate);
464
					$share->setExpirationDate($expireDate);
465
				} catch (\Exception $e) {
466
					$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
467
					return new \OC\OCS\Result(null, 404, $this->l->t('Invalid date, date format must be YYYY-MM-DD'));
468
				}
469
			}
470
		} elseif ($shareType === \OCP\Share::SHARE_TYPE_REMOTE) {
471
			if (!$this->shareManager->outgoingServer2ServerSharesAllowed()) {
472
				$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
473
				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]));
474
			}
475
476
			$share->setSharedWith($shareWith);
477
			$share->setPermissions($permissions);
478 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...
479
			$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
480
			return new \OC\OCS\Result(null, 400, $this->l->t('Unknown share type'));
481
		}
482
483
		$share->setShareType($shareType);
484
		$share->setSharedBy($this->currentUser->getUID());
485
486
		try {
487
			$share = $this->shareManager->createShare($share);
488
		} catch (GenericShareException $e) {
489
			$code = $e->getCode() === 0 ? 403 : $e->getCode();
490
			$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
491
			return new \OC\OCS\Result(null, $code, $e->getHint());
492
		} catch (\Exception $e) {
493
			$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
494
			return new \OC\OCS\Result(null, 403, $e->getMessage());
495
		}
496
497
		$share->getNode()->unlock(\OCP\Lock\ILockingProvider::LOCK_SHARED);
498
499
		$formattedShareAfterCreate = $this->formatShare($share);
500
501
		return new \OC\OCS\Result($formattedShareAfterCreate);
502
	}
503
504
	/**
505
	 * @param \OCP\Files\File|\OCP\Files\Folder $node
506
	 * @param boolean $includeTags include tags in response
507
	 * @param int|null $stateFilter state filter or empty for all, defaults to 0 (accepted)
508
	 * @return \OC\OCS\Result
509
	 */
510
	private function getSharedWithMe($node = null, $includeTags, $stateFilter = 0) {
511
		$userShares = $this->shareManager->getSharedWith($this->currentUser->getUID(), \OCP\Share::SHARE_TYPE_USER, $node, -1, 0);
512
		$groupShares = $this->shareManager->getSharedWith($this->currentUser->getUID(), \OCP\Share::SHARE_TYPE_GROUP, $node, -1, 0);
513
514
		$shares = \array_merge($userShares, $groupShares);
515
516
		$shares = \array_filter($shares, function (IShare $share) {
517
			return $share->getShareOwner() !== $this->currentUser->getUID();
518
		});
519
520
		$formatted = [];
521
		foreach ($shares as $share) {
522
			if (($stateFilter === null || $share->getState() === $stateFilter) &&
523
				$this->canAccessShare($share)) {
524
				try {
525
					$formatted[] = $this->formatShare($share, true);
526
				} catch (NotFoundException $e) {
527
					// Ignore this share
528
				}
529
			}
530
		}
531
532
		if ($includeTags) {
533
			$formatted = \OCA\Files\Helper::populateTags($formatted, 'file_source');
534
		}
535
536
		return new \OC\OCS\Result($formatted);
537
	}
538
539
	/**
540
	 * @param \OCP\Files\Folder $folder
541
	 * @return \OC\OCS\Result
542
	 */
543
	private function getSharesInDir($folder) {
544
		if (!($folder instanceof \OCP\Files\Folder)) {
545
			return new \OC\OCS\Result(null, 400, $this->l->t('Not a directory'));
546
		}
547
548
		$nodes = $folder->getDirectoryListing();
549
		/** @var \OCP\Share\IShare[] $shares */
550
		$shares = [];
551
		foreach ($nodes as $node) {
552
			$shares = \array_merge($shares, $this->shareManager->getSharesBy($this->currentUser->getUID(), \OCP\Share::SHARE_TYPE_USER, $node, false, -1, 0));
553
			$shares = \array_merge($shares, $this->shareManager->getSharesBy($this->currentUser->getUID(), \OCP\Share::SHARE_TYPE_GROUP, $node, false, -1, 0));
554
			$shares = \array_merge($shares, $this->shareManager->getSharesBy($this->currentUser->getUID(), \OCP\Share::SHARE_TYPE_LINK, $node, false, -1, 0));
555
			if ($this->shareManager->outgoingServer2ServerSharesAllowed()) {
556
				$shares = \array_merge($shares, $this->shareManager->getSharesBy($this->currentUser->getUID(), \OCP\Share::SHARE_TYPE_REMOTE, $node, false, -1, 0));
557
			}
558
		}
559
560
		$formatted = [];
561
		foreach ($shares as $share) {
562
			try {
563
				$formatted[] = $this->formatShare($share);
564
			} catch (NotFoundException $e) {
565
				//Ignore this share
566
			}
567
		}
568
569
		return new \OC\OCS\Result($formatted);
570
	}
571
572
	/**
573
	 * The getShares function.
574
	 *
575
	 * - Get shares by the current user
576
	 * - Get shares by the current user and reshares (?reshares=true)
577
	 * - Get shares with the current user (?shared_with_me=true)
578
	 * - Get shares for a specific path (?path=...)
579
	 * - Get all shares in a folder (?subfiles=true&path=..)
580
	 *
581
	 * @return \OC\OCS\Result
582
	 */
583
	public function getShares() {
584
		if (!$this->shareManager->shareApiEnabled()) {
585
			return new \OC\OCS\Result();
586
		}
587
588
		$sharedWithMe = $this->request->getParam('shared_with_me', null);
589
		$reshares = $this->request->getParam('reshares', null);
590
		$subfiles = $this->request->getParam('subfiles');
591
		$path = $this->request->getParam('path', null);
592
593
		$includeTags = $this->request->getParam('include_tags', false);
594
595
		if ($path !== null) {
596
			$userFolder = $this->rootFolder->getUserFolder($this->currentUser->getUID());
597
			try {
598
				$path = $userFolder->get($path);
599
				$path->lock(ILockingProvider::LOCK_SHARED);
600
			} catch (\OCP\Files\NotFoundException $e) {
601
				return new \OC\OCS\Result(null, 404, $this->l->t('Wrong path, file/folder doesn\'t exist'));
602
			} catch (LockedException $e) {
603
				return new \OC\OCS\Result(null, 404, $this->l->t('Could not lock path'));
604
			}
605
		}
606
607
		if ($sharedWithMe === 'true') {
608
			$stateFilter = $this->request->getParam('state', \OCP\Share::STATE_ACCEPTED);
609
			if ($stateFilter === '') {
610
				$stateFilter = \OCP\Share::STATE_ACCEPTED;
611
			} elseif ($stateFilter === 'all') {
612
				$stateFilter = null; // which means all
613
			} else {
614
				$stateFilter = (int)$stateFilter;
615
			}
616
			$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...
617
			if ($path !== null) {
618
				$path->unlock(ILockingProvider::LOCK_SHARED);
619
			}
620
			return $result;
621
		}
622
623
		if ($subfiles === 'true') {
624
			$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...
625
			if ($path !== null) {
626
				$path->unlock(ILockingProvider::LOCK_SHARED);
627
			}
628
			return $result;
629
		}
630
631
		if ($reshares === 'true') {
632
			$reshares = true;
633
		} else {
634
			$reshares = false;
635
		}
636
637
		// Get all shares
638
		$userShares = $this->shareManager->getSharesBy($this->currentUser->getUID(), \OCP\Share::SHARE_TYPE_USER, $path, $reshares, -1, 0);
639
		$groupShares = $this->shareManager->getSharesBy($this->currentUser->getUID(), \OCP\Share::SHARE_TYPE_GROUP, $path, $reshares, -1, 0);
640
		$linkShares = $this->shareManager->getSharesBy($this->currentUser->getUID(), \OCP\Share::SHARE_TYPE_LINK, $path, $reshares, -1, 0);
641
		$shares = \array_merge($userShares, $groupShares, $linkShares);
642
643
		if ($this->shareManager->outgoingServer2ServerSharesAllowed()) {
644
			$federatedShares = $this->shareManager->getSharesBy($this->currentUser->getUID(), \OCP\Share::SHARE_TYPE_REMOTE, $path, $reshares, -1, 0);
645
			$shares = \array_merge($shares, $federatedShares);
646
		}
647
648
		$formatted = [];
649
		foreach ($shares as $share) {
650
			try {
651
				$formatted[] = $this->formatShare($share);
652
			} catch (NotFoundException $e) {
653
				//Ignore share
654
			}
655
		}
656
657
		if ($includeTags) {
658
			$formatted = \OCA\Files\Helper::populateTags($formatted, 'file_source');
659
		}
660
661
		if ($path !== null) {
662
			$path->unlock(ILockingProvider::LOCK_SHARED);
663
		}
664
665
		return new \OC\OCS\Result($formatted);
666
	}
667
668
	/**
669
	 * @param int $id
670
	 * @return \OC\OCS\Result
671
	 */
672
	public function updateShare($id) {
673
		if (!$this->shareManager->shareApiEnabled()) {
674
			return new \OC\OCS\Result(null, 404, $this->l->t('Share API is disabled'));
675
		}
676
677
		try {
678
			$share = $this->getShareById($id);
679
		} catch (ShareNotFound $e) {
680
			return new \OC\OCS\Result(null, 404, $this->l->t('Wrong share ID, share doesn\'t exist'));
681
		}
682
683
		$share->getNode()->lock(\OCP\Lock\ILockingProvider::LOCK_SHARED);
684
685 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...
686
			$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
687
			return new \OC\OCS\Result(null, 404, $this->l->t('Wrong share ID, share doesn\'t exist'));
688
		}
689
690
		$permissions = $this->request->getParam('permissions', null);
691
		$password = $this->request->getParam('password', null);
692
		$publicUpload = $this->request->getParam('publicUpload', null);
693
		$expireDate = $this->request->getParam('expireDate', null);
694
		$name = $this->request->getParam('name', null);
695
696
		/*
697
		 * expirationdate, password and publicUpload only make sense for link shares
698
		 */
699
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) {
700
			if ($permissions === null && $password === null && $publicUpload === null && $expireDate === null && $name === null) {
701
				$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
702
				return new \OC\OCS\Result(null, 400, 'Wrong or no update parameter given');
703
			}
704
705
			$newPermissions = null;
706
			if ($publicUpload === 'true') {
707
				$newPermissions = \OCP\Constants::PERMISSION_READ | \OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE;
708
			} elseif ($publicUpload === 'false') {
709
				$newPermissions = \OCP\Constants::PERMISSION_READ;
710
			}
711
712
			if ($permissions !== null) {
713
				$newPermissions = (int)$permissions;
714
			}
715
716
			if ($newPermissions !== null &&
717
				$newPermissions !== \OCP\Constants::PERMISSION_READ &&
718
				$newPermissions !== \OCP\Constants::PERMISSION_CREATE &&
719
				// legacy
720
				$newPermissions !== (\OCP\Constants::PERMISSION_READ | \OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE) &&
721
				// correct
722
				$newPermissions !== (\OCP\Constants::PERMISSION_READ | \OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE)
723
			) {
724
				$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
725
				return new \OC\OCS\Result(null, 400, $this->l->t('Can\'t change permissions for public share links'));
726
			}
727
728
			if (
729
				// legacy
730
				$newPermissions === (\OCP\Constants::PERMISSION_READ | \OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE) ||
731
				// correct
732
				$newPermissions === (\OCP\Constants::PERMISSION_READ | \OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE)
733
			) {
734
				if (!$this->shareManager->shareApiLinkAllowPublicUpload()) {
735
					$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
736
					return new \OC\OCS\Result(null, 403, $this->l->t('Public upload disabled by the administrator'));
737
				}
738
739
				if (!($share->getNode() instanceof \OCP\Files\Folder)) {
740
					$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
741
					return new \OC\OCS\Result(null, 400, $this->l->t('Public upload is only possible for publicly shared folders'));
742
				}
743
744
				// normalize to correct public upload permissions
745
				$newPermissions = \OCP\Constants::PERMISSION_READ | \OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE;
746
			}
747
748
			// create-only (upload-only)
749 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...
750
				$newPermissions === \OCP\Constants::PERMISSION_CREATE
751
			) {
752
				if (!$this->shareManager->shareApiLinkAllowPublicUpload()) {
753
					$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
754
					return new \OC\OCS\Result(null, 403, $this->l->t('Public upload disabled by the administrator'));
755
				}
756
757
				if (!($share->getNode() instanceof \OCP\Files\Folder)) {
758
					$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
759
					return new \OC\OCS\Result(null, 400, $this->l->t('Public upload is only possible for publicly shared folders'));
760
				}
761
			}
762
763
			// set name only if passed as parameter, empty string is allowed
764
			if ($name !== null) {
765
				$oldname = $share->getName();
766
				$share->setName($name);
767
			}
768
769
			if ($newPermissions !== null) {
770
				$share->setPermissions($newPermissions);
771
				$permissions = $newPermissions;
772
			}
773
774
			if ($expireDate === '') {
775
				$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...
776
			} elseif ($expireDate !== null) {
777
				try {
778
					$expireDate = $this->parseDate($expireDate);
779
				} catch (\Exception $e) {
780
					$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
781
					return new \OC\OCS\Result(null, 400, $e->getMessage());
782
				}
783
				$share->setExpirationDate($expireDate);
784
			}
785
786
			if ($password === '') {
787
				$share->setPassword(null);
788
			} elseif ($password !== null) {
789
				$share->setPassword($password);
790
			}
791
		} else {
792
			// For other shares only permissions is valid.
793
			if ($permissions === null) {
794
				$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
795
				return new \OC\OCS\Result(null, 400, $this->l->t('Wrong or no update parameter given'));
796
			} else {
797
				$permissions = (int)$permissions;
798
				$share->setPermissions($permissions);
799
			}
800
		}
801
802
		if ($permissions !== null && $share->getShareOwner() !== $this->currentUser->getUID()) {
803
			/* Check if this is an incoming share */
804
			$incomingShares = $this->shareManager->getSharedWith($this->currentUser->getUID(), \OCP\Share::SHARE_TYPE_USER, $share->getNode(), -1, 0);
805
			$incomingShares = \array_merge($incomingShares, $this->shareManager->getSharedWith($this->currentUser->getUID(), \OCP\Share::SHARE_TYPE_GROUP, $share->getNode(), -1, 0));
806
807
			if (!empty($incomingShares)) {
808
				$maxPermissions = 0;
809
				foreach ($incomingShares as $incomingShare) {
810
					$maxPermissions |= $incomingShare->getPermissions();
811
				}
812
813
				if ($share->getPermissions() & ~$maxPermissions) {
814
					$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
815
					return new \OC\OCS\Result(null, 404, $this->l->t('Cannot increase permissions'));
816
				}
817
			}
818
		}
819
820
		if ($share->getPermissions() === 0) {
821
			return new \OC\OCS\Result(null, 400, $this->l->t('Cannot remove all permissions'));
822
		}
823
824
		try {
825
			$share = $this->shareManager->updateShare($share);
826
		} catch (\Exception $e) {
827
			$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
828
			return new \OC\OCS\Result(null, 400, $e->getMessage());
829
		}
830
831
		$share->getNode()->unlock(\OCP\Lock\ILockingProvider::LOCK_SHARED);
832
833
		return new \OC\OCS\Result($this->formatShare($share));
834
	}
835
836
	/**
837
	 * @param int $id
838
	 * @return \OC\OCS\Result
839
	 */
840
	public function acceptShare($id) {
841
		return $this->updateShareState($id, \OCP\Share::STATE_ACCEPTED);
842
	}
843
844
	/**
845
	 * @param int $id
846
	 * @return \OC\OCS\Result
847
	 */
848
	public function declineShare($id) {
849
		return $this->updateShareState($id, \OCP\Share::STATE_REJECTED);
850
	}
851
852
	/**
853
	 * @param $id
854
	 * @param $state
855
	 * @return \OC\OCS\Result
856
	 */
857
	private function updateShareState($id, $state) {
858
		$eventName = '';
859
		if ($state === \OCP\Share::STATE_ACCEPTED) {
860
			$eventName = 'accept';
861
		} elseif ($state === \OCP\Share::STATE_REJECTED) {
862
			$eventName = 'reject';
863
		}
864
865
		if (!$this->shareManager->shareApiEnabled()) {
866
			return new \OC\OCS\Result(null, 404, $this->l->t('Share API is disabled'));
867
		}
868
869
		try {
870
			$share = $this->getShareById($id, $this->currentUser->getUID());
871
			$this->eventDispatcher->dispatch('share.before' . $eventName, new GenericEvent(null, ['share' => $share]));
872
		} catch (ShareNotFound $e) {
873
			return new \OC\OCS\Result(null, 404, $this->l->t('Wrong share ID, share doesn\'t exist'));
874
		}
875
876
		$node = $share->getNode();
877
		$node->lock(\OCP\Lock\ILockingProvider::LOCK_SHARED);
878
879
		// this checks that we are either the owner or recipient
880 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...
881
			$node->unlock(ILockingProvider::LOCK_SHARED);
882
			return new \OC\OCS\Result(null, 404, $this->l->t('Wrong share ID, share doesn\'t exist'));
883
		}
884
885
		// only recipient can accept/reject share
886
		if ($share->getShareOwner() === $this->currentUser->getUID() ||
887
			$share->getSharedBy() === $this->currentUser->getUID()) {
888
			$node->unlock(ILockingProvider::LOCK_SHARED);
889
			return new \OC\OCS\Result(null, 403, $this->l->t('Only recipient can change accepted state'));
890
		}
891
892
		if ($share->getState() === $state) {
893 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...
894
				$this->eventDispatcher->dispatch('share.after' . $eventName, new GenericEvent(null, ['share' => $share]));
895
			}
896
			// if there are no changes in the state, just return the share as if the change was successful
897
			$node->unlock(\OCP\Lock\ILockingProvider::LOCK_SHARED);
898
			return new \OC\OCS\Result([$this->formatShare($share, true)]);
899
		}
900
901
		// we actually want to update all shares related to the node in case there are multiple
902
		// incoming shares for the same node (ex: receiving simultaneously through group share and user share)
903
		$allShares = $this->shareManager->getSharedWith($this->currentUser->getUID(), \OCP\Share::SHARE_TYPE_USER, $node, -1, 0);
904
		$allShares = \array_merge($allShares, $this->shareManager->getSharedWith($this->currentUser->getUID(), \OCP\Share::SHARE_TYPE_GROUP, $node, -1, 0));
905
906
		// resolve and deduplicate target if accepting
907
		if ($state === \OCP\Share::STATE_ACCEPTED) {
908
			$share = $this->deduplicateShareTarget($share);
909
		}
910
911
		$share->setState($state);
912
913
		try {
914
			foreach ($allShares as $aShare) {
915
				$aShare->setState($share->getState());
916
				$aShare->setTarget($share->getTarget());
917
				$this->shareManager->updateShareForRecipient($aShare, $this->currentUser->getUID());
918
			}
919
		} catch (\Exception $e) {
920
			$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
921
			return new \OC\OCS\Result(null, 400, $e->getMessage());
922
		}
923
924
		$node->unlock(\OCP\Lock\ILockingProvider::LOCK_SHARED);
925
926
		// FIXME: needs public API!
927
		\OC\Files\Filesystem::tearDown();
928
		// FIXME: trigger mount for user to make sure the new node is mounted already
929
		// before formatShare resolves it
930
		$this->rootFolder->getUserFolder($this->currentUser->getUID());
931
932
		$this->notificationPublisher->discardNotificationForUser($share, $this->currentUser->getUID());
933
934 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...
935
			$this->eventDispatcher->dispatch('share.after' . $eventName, new GenericEvent(null, ['share' => $share]));
936
		}
937
		return new \OC\OCS\Result([$this->formatShare($share, true)]);
938
	}
939
940
	/**
941
	 * Deduplicate the share target in the current user home folder,
942
	 * based on configured share folder
943
	 *
944
	 * @param IShare $share share target to deduplicate
945
	 * @return IShare same share with target updated if necessary
946
	 */
947
	private function deduplicateShareTarget(IShare $share) {
948
		$userFolder = $this->rootFolder->getUserFolder($this->currentUser->getUID());
949
		$mountPoint = \basename($share->getTarget());
950
		$parentDir = \dirname($share->getTarget());
951
		if (!$userFolder->nodeExists($parentDir)) {
952
			$parentDir = Helper::getShareFolder();
953
			$pathAttempt = \OC\Files\Filesystem::normalizePath($parentDir . '/' . $share->getTarget());
954
		} else {
955
			$pathAttempt = \OC\Files\Filesystem::normalizePath($share->getTarget());
956
		}
957
958
		$pathinfo = \pathinfo($pathAttempt);
959
		$ext = (isset($pathinfo['extension'])) ? '.'.$pathinfo['extension'] : '';
960
		$name = $pathinfo['filename'];
961
962
		$i = 2;
963
		while ($userFolder->nodeExists($pathAttempt)) {
964
			$pathAttempt = \OC\Files\Filesystem::normalizePath($parentDir . '/' . $name . ' ('.$i.')' . $ext);
965
			$i++;
966
		}
967
968
		$share->setTarget($pathAttempt);
969
970
		return $share;
971
	}
972
973
	/**
974
	 * @param \OCP\Share\IShare $share
975
	 * @return bool
976
	 */
977
	protected function canAccessShare(\OCP\Share\IShare $share) {
978
		// A file with permissions 0 can't be accessed by us,
979
		// unless it's a rejected sub-group share in which case we want it visible to let the user accept it again
980
		if ($share->getPermissions() === 0
981
			&& !($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP && $share->getState() === \OCP\Share::STATE_REJECTED)) {
982
			return false;
983
		}
984
985
		// Owner of the file and the sharer of the file can always get share
986
		if ($share->getShareOwner() === $this->currentUser->getUID() ||
987
			$share->getSharedBy() === $this->currentUser->getUID()
988
		) {
989
			return true;
990
		}
991
992
		// If the share is shared with you (or a group you are a member of)
993
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER &&
994
			$share->getSharedWith() === $this->currentUser->getUID()) {
995
			return true;
996
		}
997
998
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
999
			$sharedWith = $this->groupManager->get($share->getSharedWith());
1000
			if ($sharedWith !== null && $sharedWith->inGroup($this->currentUser)) {
1001
				return true;
1002
			}
1003
		}
1004
1005
		return false;
1006
	}
1007
1008
	/**
1009
	 * Make sure that the passed date is valid ISO 8601
1010
	 * So YYYY-MM-DD
1011
	 * If not throw an exception
1012
	 *
1013
	 * @param string $expireDate
1014
	 *
1015
	 * @throws \Exception
1016
	 * @return \DateTime
1017
	 */
1018
	private function parseDate($expireDate) {
1019
		try {
1020
			$date = new \DateTime($expireDate);
1021
		} catch (\Exception $e) {
1022
			throw new \Exception('Invalid date. Format must be YYYY-MM-DD');
1023
		}
1024
1025
		if ($date === false) {
1026
			throw new \Exception('Invalid date. Format must be YYYY-MM-DD');
1027
		}
1028
1029
		$date->setTime(0, 0, 0);
1030
1031
		return $date;
1032
	}
1033
1034
	/**
1035
	 * Since we have multiple providers but the OCS Share API v1 does
1036
	 * not support this we need to check all backends.
1037
	 *
1038
	 * @param string $id
1039
	 * @return \OCP\Share\IShare
1040
	 * @throws ShareNotFound
1041
	 */
1042
	private function getShareById($id, $recipient = null) {
1043
		$share = null;
1044
1045
		// First check if it is an internal share.
1046
		try {
1047
			$share = $this->shareManager->getShareById('ocinternal:'.$id, $recipient);
1048
		} catch (ShareNotFound $e) {
1049
			if (!$this->shareManager->outgoingServer2ServerSharesAllowed()) {
1050
				throw new ShareNotFound();
1051
			}
1052
1053
			$share = $this->shareManager->getShareById('ocFederatedSharing:' . $id, $recipient);
1054
		}
1055
1056
		return $share;
1057
	}
1058
}
1059