Completed
Push — master ( 01f420...7bc3c2 )
by Morris
99:18 queued 80:12
created

ShareAPIController::__construct()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 24
Code Lines 21

Duplication

Lines 24
Ratio 100 %

Importance

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

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

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