Completed
Push — master ( 1ceb08...093983 )
by Joas
41:44 queued 18:22
created

ShareAPIController::__construct()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 24
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 21
nc 1
nop 10
dl 0
loc 24
rs 8.9713
c 0
b 0
f 0

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
declare(strict_types=1);
3
/**
4
 * @copyright Copyright (c) 2016, ownCloud, Inc.
5
 *
6
 * @author Bjoern Schiessle <[email protected]>
7
 * @author Joas Schilling <[email protected]>
8
 * @author Lukas Reschke <[email protected]>
9
 * @author Maxence Lange <[email protected]>
10
 * @author Michael Jobst <[email protected]>
11
 * @author Robin Appelman <[email protected]>
12
 * @author Roeland Jago Douma <[email protected]>
13
 * @author Vincent Petry <[email protected]>
14
 *
15
 * @license AGPL-3.0
16
 *
17
 * This code is free software: you can redistribute it and/or modify
18
 * it under the terms of the GNU Affero General Public License, version 3,
19
 * as published by the Free Software Foundation.
20
 *
21
 * This program is distributed in the hope that it will be useful,
22
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24
 * GNU Affero General Public License for more details.
25
 *
26
 * You should have received a copy of the GNU Affero General Public License, version 3,
27
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
28
 *
29
 */
30
namespace OCA\Files_Sharing\Controller;
31
32
use OCA\Files\Helper;
33
use OCP\AppFramework\Http\DataResponse;
34
use OCP\AppFramework\OCS\OCSBadRequestException;
35
use OCP\AppFramework\OCS\OCSException;
36
use OCP\AppFramework\OCS\OCSForbiddenException;
37
use OCP\AppFramework\OCS\OCSNotFoundException;
38
use OCP\AppFramework\OCSController;
39
use OCP\Constants;
40
use OCP\Files\Folder;
41
use OCP\Files\Node;
42
use OCP\Files\NotFoundException;
43
use OCP\IConfig;
44
use OCP\IGroupManager;
45
use OCP\IL10N;
46
use OCP\IUserManager;
47
use OCP\IRequest;
48
use OCP\IURLGenerator;
49
use OCP\Files\IRootFolder;
50
use OCP\Lock\LockedException;
51
use OCP\Share\IManager;
52
use OCP\Share\Exceptions\ShareNotFound;
53
use OCP\Share\Exceptions\GenericShareException;
54
use OCP\Lock\ILockingProvider;
55
use OCP\Share\IShare;
56
use OCA\Files_Sharing\External\Storage;
57
58
/**
59
 * Class Share20OCS
60
 *
61
 * @package OCA\Files_Sharing\API
62
 */
63
class ShareAPIController extends OCSController {
64
65
	/** @var IManager */
66
	private $shareManager;
67
	/** @var IGroupManager */
68
	private $groupManager;
69
	/** @var IUserManager */
70
	private $userManager;
71
	/** @var IRootFolder */
72
	private $rootFolder;
73
	/** @var IURLGenerator */
74
	private $urlGenerator;
75
	/** @var string */
76
	private $currentUser;
77
	/** @var IL10N */
78
	private $l;
79
	/** @var \OCP\Files\Node */
80
	private $lockedNode;
81
	/** @var IConfig */
82
	private $config;
83
84
	/**
85
	 * Share20OCS constructor.
86
	 *
87
	 * @param string $appName
88
	 * @param IRequest $request
89
	 * @param IManager $shareManager
90
	 * @param IGroupManager $groupManager
91
	 * @param IUserManager $userManager
92
	 * @param IRootFolder $rootFolder
93
	 * @param IURLGenerator $urlGenerator
94
	 * @param string $userId
95
	 * @param IL10N $l10n
96
	 * @param IConfig $config
97
	 */
98
	public function __construct(
99
		string $appName,
100
		IRequest $request,
101
		IManager $shareManager,
102
		IGroupManager $groupManager,
103
		IUserManager $userManager,
104
		IRootFolder $rootFolder,
105
		IURLGenerator $urlGenerator,
106
		string $userId,
107
		IL10N $l10n,
108
		IConfig $config
109
	) {
110
		parent::__construct($appName, $request);
111
112
		$this->shareManager = $shareManager;
113
		$this->userManager = $userManager;
114
		$this->groupManager = $groupManager;
115
		$this->request = $request;
116
		$this->rootFolder = $rootFolder;
117
		$this->urlGenerator = $urlGenerator;
118
		$this->currentUser = $userId;
119
		$this->l = $l10n;
120
		$this->config = $config;
121
	}
122
123
	/**
124
	 * Convert an IShare to an array for OCS output
125
	 *
126
	 * @param \OCP\Share\IShare $share
127
	 * @param Node|null $recipientNode
128
	 * @return array
129
	 * @throws NotFoundException In case the node can't be resolved.
130
	 */
131
	protected function formatShare(\OCP\Share\IShare $share, Node $recipientNode = null): array {
132
		$sharedBy = $this->userManager->get($share->getSharedBy());
133
		$shareOwner = $this->userManager->get($share->getShareOwner());
134
135
		$result = [
136
			'id' => $share->getId(),
137
			'share_type' => $share->getShareType(),
138
			'uid_owner' => $share->getSharedBy(),
139
			'displayname_owner' => $sharedBy !== null ? $sharedBy->getDisplayName() : $share->getSharedBy(),
140
			'permissions' => $share->getPermissions(),
141
			'stime' => $share->getShareTime()->getTimestamp(),
142
			'parent' => null,
143
			'expiration' => null,
144
			'token' => null,
145
			'uid_file_owner' => $share->getShareOwner(),
146
			'displayname_file_owner' => $shareOwner !== null ? $shareOwner->getDisplayName() : $share->getShareOwner(),
147
		];
148
149
		$userFolder = $this->rootFolder->getUserFolder($this->currentUser);
150
		if ($recipientNode) {
151
			$node = $recipientNode;
152
		} else {
153
			$nodes = $userFolder->getById($share->getNodeId());
154
155
			if (empty($nodes)) {
156
				// fallback to guessing the path
157
				$node = $userFolder->get($share->getTarget());
158
				if ($node === null || $share->getTarget() === '') {
159
					throw new NotFoundException();
160
				}
161
			} else {
162
				$node = $nodes[0];
163
			}
164
		}
165
166
		$result['path'] = $userFolder->getRelativePath($node->getPath());
167
		if ($node instanceOf \OCP\Files\Folder) {
168
			$result['item_type'] = 'folder';
169
		} else {
170
			$result['item_type'] = 'file';
171
		}
172
		$result['mimetype'] = $node->getMimetype();
173
		$result['storage_id'] = $node->getStorage()->getId();
174
		$result['storage'] = $node->getStorage()->getCache()->getNumericStorageId();
175
		$result['item_source'] = $node->getId();
176
		$result['file_source'] = $node->getId();
177
		$result['file_parent'] = $node->getParent()->getId();
178
		$result['file_target'] = $share->getTarget();
179
180
		$expiration = $share->getExpirationDate();
181
		if ($expiration !== null) {
182
			$result['expiration'] = $expiration->format('Y-m-d 00:00:00');
183
		}
184
185
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) {
186
			$sharedWith = $this->userManager->get($share->getSharedWith());
187
			$result['share_with'] = $share->getSharedWith();
188
			$result['share_with_displayname'] = $sharedWith !== null ? $sharedWith->getDisplayName() : $share->getSharedWith();
189
		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
190
			$group = $this->groupManager->get($share->getSharedWith());
191
			$result['share_with'] = $share->getSharedWith();
192
			$result['share_with_displayname'] = $group !== null ? $group->getDisplayName() : $share->getSharedWith();
193
		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) {
194
195
			$result['share_with'] = $share->getPassword();
196
			$result['share_with_displayname'] = $share->getPassword();
197
198
			$result['token'] = $share->getToken();
199
			$result['url'] = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.showShare', ['token' => $share->getToken()]);
200
201
		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_REMOTE) {
202
			$result['share_with'] = $share->getSharedWith();
203
			$result['share_with_displayname'] = $this->getDisplayNameFromAddressBook($share->getSharedWith(), 'CLOUD');
204
			$result['token'] = $share->getToken();
205
		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_EMAIL) {
206
			$result['share_with'] = $share->getSharedWith();
207
			$result['password'] = $share->getPassword();
208
			$result['share_with_displayname'] = $this->getDisplayNameFromAddressBook($share->getSharedWith(), 'EMAIL');
209
			$result['token'] = $share->getToken();
210
		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_CIRCLE) {
211
			// getSharedWith() returns either "name (type, owner)" or
212
			// "name (type, owner) [id]", depending on the Circles app version.
213
			$hasCircleId = (substr($share->getSharedWith(), -1) === ']');
214
215
			$displayNameLength = ($hasCircleId? strrpos($share->getSharedWith(), ' '): strlen($share->getSharedWith()));
216
			$result['share_with_displayname'] = substr($share->getSharedWith(), 0, $displayNameLength);
217
218
			$shareWithStart = ($hasCircleId? strrpos($share->getSharedWith(), '[') + 1: 0);
219
			$shareWithLength = ($hasCircleId? -1: strpos($share->getSharedWith(), ' '));
220
			$result['share_with'] = substr($share->getSharedWith(), $shareWithStart, $shareWithLength);
221
		}
222
223
224
		$result['mail_send'] = $share->getMailSend() ? 1 : 0;
225
226
		return $result;
227
	}
228
229
	/**
230
	 * Check if one of the users address books knows the exact property, if
231
	 * yes we return the full name.
232
	 *
233
	 * @param string $query
234
	 * @param string $property
235
	 * @return string
236
	 */
237
	private function getDisplayNameFromAddressBook(string $query, string $property): string {
238
		// FIXME: If we inject the contacts manager it gets initialized bofore any address books are registered
239
		$result = \OC::$server->getContactsManager()->search($query, [$property]);
240
		foreach ($result as $r) {
241
			foreach($r[$property] as $value) {
242
				if ($value === $query) {
243
					return $r['FN'];
244
				}
245
			}
246
		}
247
248
		return $query;
249
	}
250
251
	/**
252
	 * Get a specific share by id
253
	 *
254
	 * @NoAdminRequired
255
	 *
256
	 * @param string $id
257
	 * @return DataResponse
258
	 * @throws OCSNotFoundException
259
	 */
260
	public function getShare(string $id): DataResponse {
261
		try {
262
			$share = $this->getShareById($id);
263
		} catch (ShareNotFound $e) {
264
			throw new OCSNotFoundException($this->l->t('Wrong share ID, share doesn\'t exist'));
265
		}
266
267
		if ($this->canAccessShare($share)) {
268
			try {
269
				$share = $this->formatShare($share);
270
				return new DataResponse([$share]);
271
			} catch (NotFoundException $e) {
272
				//Fall trough
273
			}
274
		}
275
276
		throw new OCSNotFoundException($this->l->t('Wrong share ID, share doesn\'t exist'));
277
	}
278
279
	/**
280
	 * Delete a share
281
	 *
282
	 * @NoAdminRequired
283
	 *
284
	 * @param string $id
285
	 * @return DataResponse
286
	 * @throws OCSNotFoundException
287
	 */
288
	public function deleteShare(string $id): DataResponse {
289
		try {
290
			$share = $this->getShareById($id);
291
		} catch (ShareNotFound $e) {
292
			throw new OCSNotFoundException($this->l->t('Wrong share ID, share doesn\'t exist'));
293
		}
294
295
		try {
296
			$this->lock($share->getNode());
297
		} catch (LockedException $e) {
298
			throw new OCSNotFoundException($this->l->t('could not delete share'));
299
		}
300
301
		if (!$this->canAccessShare($share)) {
302
			throw new OCSNotFoundException($this->l->t('Could not delete share'));
303
		}
304
305
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP &&
306
			$share->getShareOwner() !== $this->currentUser &&
307
			$share->getSharedBy() !== $this->currentUser) {
308
			$this->shareManager->deleteFromSelf($share, $this->currentUser);
309
		} else {
310
			$this->shareManager->deleteShare($share);
311
		}
312
313
		return new DataResponse();
314
	}
315
316
	/**
317
	 * @NoAdminRequired
318
	 *
319
	 * @param string $path
320
	 * @param int $permissions
321
	 * @param int $shareType
322
	 * @param string $shareWith
323
	 * @param string $publicUpload
324
	 * @param string $password
325
	 * @param string $expireDate
326
	 *
327
	 * @return DataResponse
328
	 * @throws OCSNotFoundException
329
	 * @throws OCSForbiddenException
330
	 * @throws OCSBadRequestException
331
	 * @throws OCSException
332
	 *
333
	 * @suppress PhanUndeclaredClassMethod
334
	 */
335
	public function createShare(
336
		string $path = null,
337
		int $permissions = null,
338
		int $shareType = -1,
339
		string $shareWith = null,
340
		string $publicUpload = 'false',
341
		string $password = '',
342
		string $expireDate = ''
343
	): DataResponse {
344
		$share = $this->shareManager->newShare();
345
346
		if ($permissions === null) {
347
			$permissions = $this->config->getAppValue('core', 'shareapi_default_permissions', Constants::PERMISSION_ALL);
348
		}
349
350
		// Verify path
351
		if ($path === null) {
352
			throw new OCSNotFoundException($this->l->t('Please specify a file or folder path'));
353
		}
354
355
		$userFolder = $this->rootFolder->getUserFolder($this->currentUser);
356
		try {
357
			$path = $userFolder->get($path);
358
		} catch (NotFoundException $e) {
359
			throw new OCSNotFoundException($this->l->t('Wrong path, file/folder doesn\'t exist'));
360
		}
361
362
		$share->setNode($path);
363
364
		try {
365
			$this->lock($share->getNode());
366
		} catch (LockedException $e) {
367
			throw new OCSNotFoundException($this->l->t('Could not create share'));
368
		}
369
370
		if ($permissions < 0 || $permissions > Constants::PERMISSION_ALL) {
371
			throw new OCSNotFoundException($this->l->t('invalid permissions'));
372
		}
373
374
		// Shares always require read permissions
375
		$permissions |= Constants::PERMISSION_READ;
376
377
		if ($path instanceof \OCP\Files\File) {
378
			// Single file shares should never have delete or create permissions
379
			$permissions &= ~Constants::PERMISSION_DELETE;
380
			$permissions &= ~Constants::PERMISSION_CREATE;
381
		}
382
383
		/*
384
		 * Hack for https://github.com/owncloud/core/issues/22587
385
		 * We check the permissions via webdav. But the permissions of the mount point
386
		 * do not equal the share permissions. Here we fix that for federated mounts.
387
		 */
388
		if ($path->getStorage()->instanceOfStorage(Storage::class)) {
389
			$permissions &= ~($permissions & ~$path->getPermissions());
390
		}
391
392
		if ($shareType === \OCP\Share::SHARE_TYPE_USER) {
393
			// Valid user is required to share
394
			if ($shareWith === null || !$this->userManager->userExists($shareWith)) {
395
				throw new OCSNotFoundException($this->l->t('Please specify a valid user'));
396
			}
397
			$share->setSharedWith($shareWith);
398
			$share->setPermissions($permissions);
399
		} else if ($shareType === \OCP\Share::SHARE_TYPE_GROUP) {
400
			if (!$this->shareManager->allowGroupSharing()) {
401
				throw new OCSNotFoundException($this->l->t('Group sharing is disabled by the administrator'));
402
			}
403
404
			// Valid group is required to share
405
			if ($shareWith === null || !$this->groupManager->groupExists($shareWith)) {
406
				throw new OCSNotFoundException($this->l->t('Please specify a valid group'));
407
			}
408
			$share->setSharedWith($shareWith);
409
			$share->setPermissions($permissions);
410
		} else if ($shareType === \OCP\Share::SHARE_TYPE_LINK) {
411
			//Can we even share links?
412
			if (!$this->shareManager->shareApiAllowLinks()) {
413
				throw new OCSNotFoundException($this->l->t('Public link sharing is disabled by the administrator'));
414
			}
415
416
			/*
417
			 * For now we only allow 1 link share.
418
			 * Return the existing link share if this is a duplicate
419
			 */
420
			$existingShares = $this->shareManager->getSharesBy($this->currentUser, \OCP\Share::SHARE_TYPE_LINK, $path, false, 1, 0);
421
			if (!empty($existingShares)) {
422
				return new DataResponse($this->formatShare($existingShares[0]));
423
			}
424
425
			if ($publicUpload === 'true') {
426
				// Check if public upload is allowed
427
				if (!$this->shareManager->shareApiLinkAllowPublicUpload()) {
428
					throw new OCSForbiddenException($this->l->t('Public upload disabled by the administrator'));
429
				}
430
431
				// Public upload can only be set for folders
432
				if ($path instanceof \OCP\Files\File) {
433
					throw new OCSNotFoundException($this->l->t('Public upload is only possible for publicly shared folders'));
434
				}
435
436
				$share->setPermissions(
437
					Constants::PERMISSION_READ |
438
					Constants::PERMISSION_CREATE |
439
					Constants::PERMISSION_UPDATE |
440
					Constants::PERMISSION_DELETE
441
				);
442
			} else {
443
				$share->setPermissions(Constants::PERMISSION_READ);
444
			}
445
446
			// Set password
447
			if ($password !== '') {
448
				$share->setPassword($password);
449
			}
450
451
			//Expire date
452
			if ($expireDate !== '') {
453
				try {
454
					$expireDate = $this->parseDate($expireDate);
455
					$share->setExpirationDate($expireDate);
456
				} catch (\Exception $e) {
457
					throw new OCSNotFoundException($this->l->t('Invalid date, date format must be YYYY-MM-DD'));
458
				}
459
			}
460
461
		} else if ($shareType === \OCP\Share::SHARE_TYPE_REMOTE) {
462
			if (!$this->shareManager->outgoingServer2ServerSharesAllowed()) {
463
				throw new OCSForbiddenException($this->l->t('Sharing %s failed because the back end does not allow shares from type %s', [$path->getPath(), $shareType]));
464
			}
465
466
			$share->setSharedWith($shareWith);
467
			$share->setPermissions($permissions);
468
		} else if ($shareType === \OCP\Share::SHARE_TYPE_EMAIL) {
469
			if ($share->getNodeType() === 'file') {
470
				$share->setPermissions(Constants::PERMISSION_READ);
471
			} else {
472
				$share->setPermissions($permissions);
473
			}
474
			$share->setSharedWith($shareWith);
475
		} else if ($shareType === \OCP\Share::SHARE_TYPE_CIRCLE) {
476
			if (!\OC::$server->getAppManager()->isEnabledForUser('circles') || !class_exists('\OCA\Circles\ShareByCircleProvider')) {
477
				throw new OCSNotFoundException($this->l->t('You cannot share to a Circle if the app is not enabled'));
478
			}
479
480
			$circle = \OCA\Circles\Api\v1\Circles::detailsCircle($shareWith);
481
482
			// Valid circle is required to share
483
			if ($circle === null) {
484
				throw new OCSNotFoundException($this->l->t('Please specify a valid circle'));
485
			}
486
			$share->setSharedWith($shareWith);
487
			$share->setPermissions($permissions);
488
		} else {
489
			throw new OCSBadRequestException($this->l->t('Unknown share type'));
490
		}
491
492
		$share->setShareType($shareType);
493
		$share->setSharedBy($this->currentUser);
494
495
		try {
496
			$share = $this->shareManager->createShare($share);
497
		} catch (GenericShareException $e) {
498
			$code = $e->getCode() === 0 ? 403 : $e->getCode();
499
			throw new OCSException($e->getHint(), $code);
500
		} catch (\Exception $e) {
501
			throw new OCSForbiddenException($e->getMessage(), $e);
502
		}
503
504
		$output = $this->formatShare($share);
505
506
		return new DataResponse($output);
507
	}
508
509
	/**
510
	 * @param \OCP\Files\File|\OCP\Files\Folder $node
511
	 * @param boolean $includeTags
512
	 * @return DataResponse
513
	 */
514
	private function getSharedWithMe($node = null, bool $includeTags): DataResponse {
515
516
		$userShares = $this->shareManager->getSharedWith($this->currentUser, \OCP\Share::SHARE_TYPE_USER, $node, -1, 0);
517
		$groupShares = $this->shareManager->getSharedWith($this->currentUser, \OCP\Share::SHARE_TYPE_GROUP, $node, -1, 0);
518
		$circleShares = $this->shareManager->getSharedWith($this->currentUser, \OCP\Share::SHARE_TYPE_CIRCLE, $node, -1, 0);
519
520
		$shares = array_merge($userShares, $groupShares, $circleShares);
521
522
		$shares = array_filter($shares, function (IShare $share) {
523
			return $share->getShareOwner() !== $this->currentUser;
524
		});
525
526
		$formatted = [];
527
		foreach ($shares as $share) {
528
			if ($this->canAccessShare($share)) {
529
				try {
530
					$formatted[] = $this->formatShare($share);
531
				} catch (NotFoundException $e) {
532
					// Ignore this share
533
				}
534
			}
535
		}
536
537
		if ($includeTags) {
538
			$formatted = Helper::populateTags($formatted, 'file_source', \OC::$server->getTagManager());
539
		}
540
541
		return new DataResponse($formatted);
542
	}
543
544
	/**
545
	 * @param \OCP\Files\Folder $folder
546
	 * @return DataResponse
547
	 * @throws OCSBadRequestException
548
	 */
549
	private function getSharesInDir(Node $folder): DataResponse {
550
		if (!($folder instanceof \OCP\Files\Folder)) {
551
			throw new OCSBadRequestException($this->l->t('Not a directory'));
552
		}
553
554
		$nodes = $folder->getDirectoryListing();
555
		/** @var \OCP\Share\IShare[] $shares */
556
		$shares = [];
557
		foreach ($nodes as $node) {
558
			$shares = array_merge($shares, $this->shareManager->getSharesBy($this->currentUser, \OCP\Share::SHARE_TYPE_USER, $node, false, -1, 0));
559
			$shares = array_merge($shares, $this->shareManager->getSharesBy($this->currentUser, \OCP\Share::SHARE_TYPE_GROUP, $node, false, -1, 0));
560
			$shares = array_merge($shares, $this->shareManager->getSharesBy($this->currentUser, \OCP\Share::SHARE_TYPE_LINK, $node, false, -1, 0));
561
			if($this->shareManager->shareProviderExists(\OCP\Share::SHARE_TYPE_EMAIL)) {
562
				$shares = array_merge($shares, $this->shareManager->getSharesBy($this->currentUser, \OCP\Share::SHARE_TYPE_EMAIL, $node, false, -1, 0));
563
			}
564 View Code Duplication
			if ($this->shareManager->outgoingServer2ServerSharesAllowed()) {
565
				$shares = array_merge($shares, $this->shareManager->getSharesBy($this->currentUser, \OCP\Share::SHARE_TYPE_REMOTE, $node, false, -1, 0));
566
			}
567
		}
568
569
		$formatted = [];
570
		foreach ($shares as $share) {
571
			try {
572
				$formatted[] = $this->formatShare($share);
573
			} catch (NotFoundException $e) {
574
				//Ignore this share
575
			}
576
		}
577
578
		return new DataResponse($formatted);
579
	}
580
581
	/**
582
	 * The getShares function.
583
	 *
584
	 * @NoAdminRequired
585
	 *
586
	 * @param string $shared_with_me
587
	 * @param string $reshares
588
	 * @param string $subfiles
589
	 * @param string $path
590
	 *
591
	 * - Get shares by the current user
592
	 * - Get shares by the current user and reshares (?reshares=true)
593
	 * - Get shares with the current user (?shared_with_me=true)
594
	 * - Get shares for a specific path (?path=...)
595
	 * - Get all shares in a folder (?subfiles=true&path=..)
596
	 *
597
	 * @return DataResponse
598
	 * @throws OCSNotFoundException
599
	 */
600
	public function getShares(
601
		string $shared_with_me = 'false',
602
		string $reshares = 'false',
603
		string $subfiles = 'false',
604
		string $path = null,
605
		string $include_tags = 'false'
606
	): DataResponse {
607
608
		if ($path !== null) {
609
			$userFolder = $this->rootFolder->getUserFolder($this->currentUser);
610
			try {
611
				$path = $userFolder->get($path);
612
				$this->lock($path);
613
			} catch (\OCP\Files\NotFoundException $e) {
614
				throw new OCSNotFoundException($this->l->t('Wrong path, file/folder doesn\'t exist'));
615
			} catch (LockedException $e) {
616
				throw new OCSNotFoundException($this->l->t('Could not lock path'));
617
			}
618
		}
619
620
		$include_tags = $include_tags === 'true';
621
622
		if ($shared_with_me === 'true') {
623
			$result = $this->getSharedWithMe($path, $include_tags);
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...
624
			return $result;
625
		}
626
627
		if ($subfiles === 'true') {
628
			$result = $this->getSharesInDir($path);
0 ignored issues
show
Bug introduced by
It seems like $path defined by parameter $path on line 604 can be null; however, OCA\Files_Sharing\Contro...oller::getSharesInDir() does not accept null, maybe add an additional type check?

It seems like you allow that null is being passed for a parameter, however the function which is called does not seem to accept null.

We recommend to add an additional type check (or disallow null for the parameter):

function notNullable(stdClass $x) { }

// Unsafe
function withoutCheck(stdClass $x = null) {
    notNullable($x);
}

// Safe - Alternative 1: Adding Additional Type-Check
function withCheck(stdClass $x = null) {
    if ($x instanceof stdClass) {
        notNullable($x);
    }
}

// Safe - Alternative 2: Changing Parameter
function withNonNullableParam(stdClass $x) {
    notNullable($x);
}
Loading history...
629
			return $result;
630
		}
631
632
		if ($reshares === 'true') {
633
			$reshares = true;
634
		} else {
635
			$reshares = false;
636
		}
637
638
		// Get all shares
639
		$userShares = $this->shareManager->getSharesBy($this->currentUser, \OCP\Share::SHARE_TYPE_USER, $path, $reshares, -1, 0);
640
		$groupShares = $this->shareManager->getSharesBy($this->currentUser, \OCP\Share::SHARE_TYPE_GROUP, $path, $reshares, -1, 0);
641
		$linkShares = $this->shareManager->getSharesBy($this->currentUser, \OCP\Share::SHARE_TYPE_LINK, $path, $reshares, -1, 0);
642 View Code Duplication
		if ($this->shareManager->shareProviderExists(\OCP\Share::SHARE_TYPE_EMAIL)) {
643
			$mailShares = $this->shareManager->getSharesBy($this->currentUser, \OCP\Share::SHARE_TYPE_EMAIL, $path, $reshares, -1, 0);
644
		} else {
645
			$mailShares = [];
646
		}
647 View Code Duplication
		if ($this->shareManager->shareProviderExists(\OCP\Share::SHARE_TYPE_CIRCLE)) {
648
			$circleShares = $this->shareManager->getSharesBy($this->currentUser, \OCP\Share::SHARE_TYPE_CIRCLE, $path, $reshares, -1, 0);
649
		} else {
650
			$circleShares = [];
651
		}
652
653
		$shares = array_merge($userShares, $groupShares, $linkShares, $mailShares, $circleShares);
654
655 View Code Duplication
		if ($this->shareManager->outgoingServer2ServerSharesAllowed()) {
656
			$federatedShares = $this->shareManager->getSharesBy($this->currentUser, \OCP\Share::SHARE_TYPE_REMOTE, $path, $reshares, -1, 0);
657
			$shares = array_merge($shares, $federatedShares);
658
		}
659
660
		$formatted = [];
661
		foreach ($shares as $share) {
662
			try {
663
				$formatted[] = $this->formatShare($share, $path);
664
			} catch (NotFoundException $e) {
665
				//Ignore share
666
			}
667
		}
668
669
		if ($include_tags) {
670
			$formatted = Helper::populateTags($formatted, 'file_source', \OC::$server->getTagManager());
671
		}
672
673
		return new DataResponse($formatted);
674
	}
675
676
	/**
677
	 * @NoAdminRequired
678
	 *
679
	 * @param string $id
680
	 * @param int $permissions
681
	 * @param string $password
682
	 * @param string $publicUpload
683
	 * @param string $expireDate
684
	 * @return DataResponse
685
	 * @throws OCSNotFoundException
686
	 * @throws OCSBadRequestException
687
	 * @throws OCSForbiddenException
688
	 */
689
	public function updateShare(
690
		string $id,
691
		int $permissions = null,
692
		string $password = null,
693
		string $publicUpload = null,
694
		string $expireDate = null
695
	): DataResponse {
696
		try {
697
			$share = $this->getShareById($id);
698
		} catch (ShareNotFound $e) {
699
			throw new OCSNotFoundException($this->l->t('Wrong share ID, share doesn\'t exist'));
700
		}
701
702
		$this->lock($share->getNode());
703
704
		if (!$this->canAccessShare($share, false)) {
705
			throw new OCSNotFoundException($this->l->t('Wrong share ID, share doesn\'t exist'));
706
		}
707
708
		if ($permissions === null && $password === null && $publicUpload === null && $expireDate === null) {
709
			throw new OCSBadRequestException($this->l->t('Wrong or no update parameter given'));
710
		}
711
712
		/*
713
		 * expirationdate, password and publicUpload only make sense for link shares
714
		 */
715
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) {
716
717
			$newPermissions = null;
718
			if ($publicUpload === 'true') {
719
				$newPermissions = Constants::PERMISSION_READ | Constants::PERMISSION_CREATE | Constants::PERMISSION_UPDATE | Constants::PERMISSION_DELETE;
720
			} else if ($publicUpload === 'false') {
721
				$newPermissions = Constants::PERMISSION_READ;
722
			}
723
724
			if ($permissions !== null) {
725
				$newPermissions = (int)$permissions;
726
				$newPermissions = $newPermissions & ~Constants::PERMISSION_SHARE;
727
			}
728
729
			if ($newPermissions !== null &&
730
				!in_array($newPermissions, [
731
					Constants::PERMISSION_READ,
732
					Constants::PERMISSION_READ | Constants::PERMISSION_CREATE | Constants::PERMISSION_UPDATE, // legacy
733
					Constants::PERMISSION_READ | Constants::PERMISSION_CREATE | Constants::PERMISSION_UPDATE | Constants::PERMISSION_DELETE, // correct
734
					Constants::PERMISSION_CREATE, // hidden file list
735
					Constants::PERMISSION_READ | Constants::PERMISSION_UPDATE, // allow to edit single files
736
				], true)
737
			) {
738
				throw new OCSBadRequestException($this->l->t('Can\'t change permissions for public share links'));
739
			}
740
741
			if (
742
				// legacy
743
				$newPermissions === (Constants::PERMISSION_READ | Constants::PERMISSION_CREATE | Constants::PERMISSION_UPDATE) ||
744
				// correct
745
				$newPermissions === (Constants::PERMISSION_READ | Constants::PERMISSION_CREATE | Constants::PERMISSION_UPDATE | Constants::PERMISSION_DELETE)
746
			) {
747
				if (!$this->shareManager->shareApiLinkAllowPublicUpload()) {
748
					throw new OCSForbiddenException($this->l->t('Public upload disabled by the administrator'));
749
				}
750
751
				if (!($share->getNode() instanceof \OCP\Files\Folder)) {
752
					throw new OCSBadRequestException($this->l->t('Public upload is only possible for publicly shared folders'));
753
				}
754
755
				// normalize to correct public upload permissions
756
				$newPermissions = Constants::PERMISSION_READ | Constants::PERMISSION_CREATE | Constants::PERMISSION_UPDATE | Constants::PERMISSION_DELETE;
757
			}
758
759
			if ($newPermissions !== null) {
760
				$share->setPermissions($newPermissions);
761
				$permissions = $newPermissions;
762
			}
763
764 View Code Duplication
			if ($expireDate === '') {
765
				$share->setExpirationDate(null);
766
			} else if ($expireDate !== null) {
767
				try {
768
					$expireDate = $this->parseDate($expireDate);
769
				} catch (\Exception $e) {
770
					throw new OCSBadRequestException($e->getMessage(), $e);
771
				}
772
				$share->setExpirationDate($expireDate);
773
			}
774
775 View Code Duplication
			if ($password === '') {
776
				$share->setPassword(null);
777
			} else if ($password !== null) {
778
				$share->setPassword($password);
779
			}
780
781
		} else {
782
			if ($permissions !== null) {
783
				$permissions = (int)$permissions;
784
				$share->setPermissions($permissions);
785
			}
786
787
			if ($share->getShareType() === \OCP\Share::SHARE_TYPE_EMAIL) {
788 View Code Duplication
				if ($password === '') {
789
					$share->setPassword(null);
790
				} else if ($password !== null) {
791
					$share->setPassword($password);
792
				}
793
			}
794
795 View Code Duplication
			if ($expireDate === '') {
796
				$share->setExpirationDate(null);
797
			} else if ($expireDate !== null) {
798
				try {
799
					$expireDate = $this->parseDate($expireDate);
800
				} catch (\Exception $e) {
801
					throw new OCSBadRequestException($e->getMessage(), $e);
802
				}
803
				$share->setExpirationDate($expireDate);
804
			}
805
806
		}
807
808
		if ($permissions !== null && $share->getShareOwner() !== $this->currentUser) {
809
			/* Check if this is an incomming share */
810
			$incomingShares = $this->shareManager->getSharedWith($this->currentUser, \OCP\Share::SHARE_TYPE_USER, $share->getNode(), -1, 0);
811
			$incomingShares = array_merge($incomingShares, $this->shareManager->getSharedWith($this->currentUser, \OCP\Share::SHARE_TYPE_GROUP, $share->getNode(), -1, 0));
812
813
			/** @var \OCP\Share\IShare[] $incomingShares */
814
			if (!empty($incomingShares)) {
815
				$maxPermissions = 0;
816
				foreach ($incomingShares as $incomingShare) {
817
					$maxPermissions |= $incomingShare->getPermissions();
818
				}
819
820
				if ($share->getPermissions() & ~$maxPermissions) {
821
					throw new OCSNotFoundException($this->l->t('Cannot increase permissions'));
822
				}
823
			}
824
		}
825
826
827
		try {
828
			$share = $this->shareManager->updateShare($share);
829
		} catch (\Exception $e) {
830
			throw new OCSBadRequestException($e->getMessage(), $e);
831
		}
832
833
		return new DataResponse($this->formatShare($share));
834
	}
835
836
	protected function canAccessShare(\OCP\Share\IShare $share, bool $checkGroups = true): bool {
837
		// A file with permissions 0 can't be accessed by us. So Don't show it
838
		if ($share->getPermissions() === 0) {
839
			return false;
840
		}
841
842
		// Owner of the file and the sharer of the file can always get share
843
		if ($share->getShareOwner() === $this->currentUser ||
844
			$share->getSharedBy() === $this->currentUser
845
		) {
846
			return true;
847
		}
848
849
		// If the share is shared with you (or a group you are a member of)
850
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER &&
851
			$share->getSharedWith() === $this->currentUser
852
		) {
853
			return true;
854
		}
855
856
		if ($checkGroups && $share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
857
			$sharedWith = $this->groupManager->get($share->getSharedWith());
858
			$user = $this->userManager->get($this->currentUser);
859
			if ($user !== null && $sharedWith !== null && $sharedWith->inGroup($user)) {
860
				return true;
861
			}
862
		}
863
864
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_CIRCLE) {
865
			// TODO: have a sanity check like above?
866
			return true;
867
		}
868
869
		return false;
870
	}
871
872
	/**
873
	 * Make sure that the passed date is valid ISO 8601
874
	 * So YYYY-MM-DD
875
	 * If not throw an exception
876
	 *
877
	 * @param string $expireDate
878
	 *
879
	 * @throws \Exception
880
	 * @return \DateTime
881
	 */
882
	private function parseDate(string $expireDate): \DateTime {
883
		try {
884
			$date = new \DateTime($expireDate);
885
		} catch (\Exception $e) {
886
			throw new \Exception('Invalid date. Format must be YYYY-MM-DD');
887
		}
888
889
		if ($date === false) {
890
			throw new \Exception('Invalid date. Format must be YYYY-MM-DD');
891
		}
892
893
		$date->setTime(0, 0, 0);
894
895
		return $date;
896
	}
897
898
	/**
899
	 * Since we have multiple providers but the OCS Share API v1 does
900
	 * not support this we need to check all backends.
901
	 *
902
	 * @param string $id
903
	 * @return \OCP\Share\IShare
904
	 * @throws ShareNotFound
905
	 */
906
	private function getShareById(string $id): IShare {
907
		$share = null;
908
909
		// First check if it is an internal share.
910
		try {
911
			$share = $this->shareManager->getShareById('ocinternal:' . $id);
912
			return $share;
913
		} catch (ShareNotFound $e) {
914
			// Do nothing, just try the other share type
915
		}
916
917
918
		try {
919 View Code Duplication
			if ($this->shareManager->shareProviderExists(\OCP\Share::SHARE_TYPE_CIRCLE)) {
920
				$share = $this->shareManager->getShareById('ocCircleShare:' . $id);
921
				return $share;
922
			}
923
		} catch (ShareNotFound $e) {
924
			// Do nothing, just try the other share type
925
		}
926
927
		try {
928 View Code Duplication
			if ($this->shareManager->shareProviderExists(\OCP\Share::SHARE_TYPE_EMAIL)) {
929
				$share = $this->shareManager->getShareById('ocMailShare:' . $id);
930
				return $share;
931
			}
932
		} catch (ShareNotFound $e) {
933
			// Do nothing, just try the other share type
934
		}
935
936
		if (!$this->shareManager->outgoingServer2ServerSharesAllowed()) {
937
			throw new ShareNotFound();
938
		}
939
		$share = $this->shareManager->getShareById('ocFederatedSharing:' . $id);
940
941
		return $share;
942
	}
943
944
	/**
945
	 * Lock a Node
946
	 *
947
	 * @param \OCP\Files\Node $node
948
	 * @throws LockedException
949
	 */
950
	private function lock(\OCP\Files\Node $node) {
951
		$node->lock(ILockingProvider::LOCK_SHARED);
952
		$this->lockedNode = $node;
953
	}
954
955
	/**
956
	 * Cleanup the remaining locks
957
	 * @throws @LockedException
958
	 */
959
	public function cleanup() {
960
		if ($this->lockedNode !== null) {
961
			$this->lockedNode->unlock(ILockingProvider::LOCK_SHARED);
962
		}
963
	}
964
}
965