Completed
Push — master ( fc47d0...2398d4 )
by Morris
13:52
created

ShareesAPIController   F

Complexity

Total Complexity 110

Size/Duplication

Total Lines 723
Duplicated Lines 15.77 %

Coupling/Cohesion

Components 1
Dependencies 21

Importance

Changes 0
Metric Value
dl 114
loc 723
rs 1.263
c 0
b 0
f 0
wmc 110
lcom 1
cbo 21

15 Methods

Rating   Name   Duplication   Size   Complexity  
B __construct() 26 26 1
F getUsers() 12 80 17
C getGroups() 12 63 15
A getCircles() 0 11 3
C getRemote() 10 63 13
A splitUserRemote() 8 8 2
A fixRemoteURL() 9 9 2
C search() 0 60 14
A isRemoteSharingAllowed() 0 8 2
D searchSharees() 0 68 13
D getEmail() 27 87 16
B getLookup() 0 35 4
B hasUserInResult() 10 15 5
A getPaginationLink() 0 11 2
A isV2() 0 3 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like ShareesAPIController often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use ShareesAPIController, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * @copyright Copyright (c) 2016, ownCloud, Inc.
4
 *
5
 * @author Björn Schießle <[email protected]>
6
 * @author Joas Schilling <[email protected]>
7
 * @author Roeland Jago Douma <[email protected]>
8
 * @author Thomas Müller <[email protected]>
9
 *
10
 * @license AGPL-3.0
11
 *
12
 * This code is free software: you can redistribute it and/or modify
13
 * it under the terms of the GNU Affero General Public License, version 3,
14
 * as published by the Free Software Foundation.
15
 *
16
 * This program is distributed in the hope that it will be useful,
17
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19
 * GNU Affero General Public License for more details.
20
 *
21
 * You should have received a copy of the GNU Affero General Public License, version 3,
22
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
23
 *
24
 */
25
namespace OCA\Files_Sharing\Controller;
26
27
use OCP\AppFramework\Http\DataResponse;
28
use OCP\AppFramework\OCS\OCSBadRequestException;
29
use OCP\AppFramework\OCSController;
30
use OCP\Contacts\IManager;
31
use OCP\Federation\ICloudIdManager;
32
use OCP\Http\Client\IClientService;
33
use OCP\IGroup;
34
use OCP\IGroupManager;
35
use OCP\ILogger;
36
use OCP\IRequest;
37
use OCP\IUser;
38
use OCP\IUserManager;
39
use OCP\IConfig;
40
use OCP\IUserSession;
41
use OCP\IURLGenerator;
42
use OCP\Share;
43
44
class ShareesAPIController extends OCSController {
45
46
	/** @var IGroupManager */
47
	protected $groupManager;
48
49
	/** @var IUserManager */
50
	protected $userManager;
51
52
	/** @var IManager */
53
	protected $contactsManager;
54
55
	/** @var IConfig */
56
	protected $config;
57
58
	/** @var IUserSession */
59
	protected $userSession;
60
61
	/** @var IURLGenerator */
62
	protected $urlGenerator;
63
64
	/** @var ILogger */
65
	protected $logger;
66
67
	/** @var \OCP\Share\IManager */
68
	protected $shareManager;
69
70
	/** @var IClientService */
71
	protected $clientService;
72
73
	/** @var ICloudIdManager  */
74
	protected $cloudIdManager;
75
76
	/** @var bool */
77
	protected $shareWithGroupOnly = false;
78
79
	/** @var bool */
80
	protected $shareeEnumeration = true;
81
82
	/** @var int */
83
	protected $offset = 0;
84
85
	/** @var int */
86
	protected $limit = 10;
87
88
	/** @var array */
89
	protected $result = [
90
		'exact' => [
91
			'users' => [],
92
			'groups' => [],
93
			'remotes' => [],
94
			'emails' => [],
95
			'circles' => [],
96
		],
97
		'users' => [],
98
		'groups' => [],
99
		'remotes' => [],
100
		'emails' => [],
101
		'lookup' => [],
102
		'circles' => [],
103
	];
104
105
	protected $reachedEndFor = [];
106
107
	/**
108
	 * @param string $appName
109
	 * @param IRequest $request
110
	 * @param IGroupManager $groupManager
111
	 * @param IUserManager $userManager
112
	 * @param IManager $contactsManager
113
	 * @param IConfig $config
114
	 * @param IUserSession $userSession
115
	 * @param IURLGenerator $urlGenerator
116
	 * @param ILogger $logger
117
	 * @param \OCP\Share\IManager $shareManager
118
	 * @param IClientService $clientService
119
	 * @param ICloudIdManager $cloudIdManager
120
	 */
121 View Code Duplication
	public function __construct($appName,
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
122
								IRequest $request,
123
								IGroupManager $groupManager,
124
								IUserManager $userManager,
125
								IManager $contactsManager,
126
								IConfig $config,
127
								IUserSession $userSession,
128
								IURLGenerator $urlGenerator,
129
								ILogger $logger,
130
								\OCP\Share\IManager $shareManager,
131
								IClientService $clientService,
132
								ICloudIdManager $cloudIdManager
133
	) {
134
		parent::__construct($appName, $request);
135
136
		$this->groupManager = $groupManager;
137
		$this->userManager = $userManager;
138
		$this->contactsManager = $contactsManager;
139
		$this->config = $config;
140
		$this->userSession = $userSession;
141
		$this->urlGenerator = $urlGenerator;
142
		$this->logger = $logger;
143
		$this->shareManager = $shareManager;
144
		$this->clientService = $clientService;
145
		$this->cloudIdManager = $cloudIdManager;
146
	}
147
148
	/**
149
	 * @param string $search
150
	 */
151
	protected function getUsers($search) {
152
		$this->result['users'] = $this->result['exact']['users'] = $users = [];
153
154
		$userGroups = [];
155
		if ($this->shareWithGroupOnly) {
156
			// Search in all the groups this user is part of
157
			$userGroups = $this->groupManager->getUserGroupIds($this->userSession->getUser());
0 ignored issues
show
Bug introduced by
It seems like $this->userSession->getUser() can be null; however, getUserGroupIds() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
158
			foreach ($userGroups as $userGroup) {
159
				$usersTmp = $this->groupManager->displayNamesInGroup($userGroup, $search, $this->limit, $this->offset);
160
				foreach ($usersTmp as $uid => $userDisplayName) {
161
					$users[$uid] = $userDisplayName;
162
				}
163
			}
164
		} else {
165
			// Search in all users
166
			$usersTmp = $this->userManager->searchDisplayName($search, $this->limit, $this->offset);
167
168
			foreach ($usersTmp as $user) {
169
				$users[$user->getUID()] = $user->getDisplayName();
170
			}
171
		}
172
173 View Code Duplication
		if (!$this->shareeEnumeration || sizeof($users) < $this->limit) {
174
			$this->reachedEndFor[] = 'users';
175
		}
176
177
		$foundUserById = false;
178
		$lowerSearch = strtolower($search);
179
		foreach ($users as $uid => $userDisplayName) {
180
			if (strtolower($uid) === $lowerSearch || strtolower($userDisplayName) === $lowerSearch) {
181
				if (strtolower($uid) === $lowerSearch) {
182
					$foundUserById = true;
183
				}
184
				$this->result['exact']['users'][] = [
185
					'label' => $userDisplayName,
186
					'value' => [
187
						'shareType' => Share::SHARE_TYPE_USER,
188
						'shareWith' => $uid,
189
					],
190
				];
191 View Code Duplication
			} else {
192
				$this->result['users'][] = [
193
					'label' => $userDisplayName,
194
					'value' => [
195
						'shareType' => Share::SHARE_TYPE_USER,
196
						'shareWith' => $uid,
197
					],
198
				];
199
			}
200
		}
201
202
		if ($this->offset === 0 && !$foundUserById) {
203
			// On page one we try if the search result has a direct hit on the
204
			// user id and if so, we add that to the exact match list
205
			$user = $this->userManager->get($search);
206
			if ($user instanceof IUser) {
207
				$addUser = true;
208
209
				if ($this->shareWithGroupOnly) {
210
					// Only add, if we have a common group
211
					$commonGroups = array_intersect($userGroups, $this->groupManager->getUserGroupIds($user));
212
					$addUser = !empty($commonGroups);
213
				}
214
215
				if ($addUser) {
216
					array_push($this->result['exact']['users'], [
217
						'label' => $user->getDisplayName(),
218
						'value' => [
219
							'shareType' => Share::SHARE_TYPE_USER,
220
							'shareWith' => $user->getUID(),
221
						],
222
					]);
223
				}
224
			}
225
		}
226
227
		if (!$this->shareeEnumeration) {
228
			$this->result['users'] = [];
229
		}
230
	}
231
232
	/**
233
	 * @param string $search
234
	 */
235
	protected function getGroups($search) {
236
		$this->result['groups'] = $this->result['exact']['groups'] = [];
237
238
		$groups = $this->groupManager->search($search, $this->limit, $this->offset);
239
		$groupIds = array_map(function (IGroup $group) { return $group->getGID(); }, $groups);
240
241 View Code Duplication
		if (!$this->shareeEnumeration || sizeof($groups) < $this->limit) {
242
			$this->reachedEndFor[] = 'groups';
243
		}
244
245
		$userGroups =  [];
246
		if (!empty($groups) && $this->shareWithGroupOnly) {
247
			// Intersect all the groups that match with the groups this user is a member of
248
			$userGroups = $this->groupManager->getUserGroups($this->userSession->getUser());
249
			$userGroups = array_map(function (IGroup $group) { return $group->getGID(); }, $userGroups);
250
			$groupIds = array_intersect($groupIds, $userGroups);
251
		}
252
253
		$lowerSearch = strtolower($search);
254
		foreach ($groups as $group) {
255
			// FIXME: use a more efficient approach
256
			$gid = $group->getGID();
257
			if (!in_array($gid, $groupIds)) {
258
				continue;
259
			}
260
			if (strtolower($gid) === $lowerSearch || strtolower($group->getDisplayName()) === $lowerSearch) {
261
				$this->result['exact']['groups'][] = [
262
					'label' => $group->getDisplayName(),
263
					'value' => [
264
						'shareType' => Share::SHARE_TYPE_GROUP,
265
						'shareWith' => $gid,
266
					],
267
				];
268 View Code Duplication
			} else {
269
				$this->result['groups'][] = [
270
					'label' => $group->getDisplayName(),
271
					'value' => [
272
						'shareType' => Share::SHARE_TYPE_GROUP,
273
						'shareWith' => $gid,
274
					],
275
				];
276
			}
277
		}
278
279
		if ($this->offset === 0 && empty($this->result['exact']['groups'])) {
280
			// On page one we try if the search result has a direct hit on the
281
			// user id and if so, we add that to the exact match list
282
			$group = $this->groupManager->get($search);
283
			if ($group instanceof IGroup && (!$this->shareWithGroupOnly || in_array($group->getGID(), $userGroups))) {
284
				array_push($this->result['exact']['groups'], [
285
					'label' => $group->getDisplayName(),
286
					'value' => [
287
						'shareType' => Share::SHARE_TYPE_GROUP,
288
						'shareWith' => $group->getGID(),
289
					],
290
				]);
291
			}
292
		}
293
294
		if (!$this->shareeEnumeration) {
295
			$this->result['groups'] = [];
296
		}
297
	}
298
299
300
	/**
301
	 * @param string $search
302
	 */
303
	protected function getCircles($search) {
304
		$this->result['circles'] = $this->result['exact']['circles'] = [];
305
306
		$result = \OCA\Circles\Api\Sharees::search($search, $this->limit, $this->offset);
307
		if (array_key_exists('circles', $result['exact'])) {
308
			$this->result['exact']['circles'] = $result['exact']['circles'];
309
		}
310
		if (array_key_exists('circles', $result)) {
311
			$this->result['circles'] = $result['circles'];
312
		}
313
	}
314
315
316
	/**
317
	 * @param string $search
318
	 * @return array
319
	 */
320
	protected function getRemote($search) {
321
		$result = ['results' => [], 'exact' => []];
322
323
		// Search in contacts
324
		//@todo Pagination missing
325
		$addressBookContacts = $this->contactsManager->search($search, ['CLOUD', 'FN']);
326
		$result['exactIdMatch'] = false;
327
		foreach ($addressBookContacts as $contact) {
328
			if (isset($contact['isLocalSystemBook'])) {
329
				continue;
330
			}
331
			if (isset($contact['CLOUD'])) {
332
				$cloudIds = $contact['CLOUD'];
333
				if (!is_array($cloudIds)) {
334
					$cloudIds = [$cloudIds];
335
				}
336
				$lowerSearch = strtolower($search);
337
				foreach ($cloudIds as $cloudId) {
338
					list(, $serverUrl) = $this->splitUserRemote($cloudId);
339
					if (strtolower($contact['FN']) === $lowerSearch || strtolower($cloudId) === $lowerSearch) {
340
						if (strtolower($cloudId) === $lowerSearch) {
341
							$result['exactIdMatch'] = true;
342
						}
343
						$result['exact'][] = [
344
							'label' => $contact['FN'] . " ($cloudId)",
345
							'value' => [
346
								'shareType' => Share::SHARE_TYPE_REMOTE,
347
								'shareWith' => $cloudId,
348
								'server' => $serverUrl,
349
							],
350
						];
351 View Code Duplication
					} else {
352
						$result['results'][] = [
353
							'label' => $contact['FN'] . " ($cloudId)",
354
							'value' => [
355
								'shareType' => Share::SHARE_TYPE_REMOTE,
356
								'shareWith' => $cloudId,
357
								'server' => $serverUrl,
358
							],
359
						];
360
					}
361
				}
362
			}
363
		}
364
365
		if (!$this->shareeEnumeration) {
366
			$result['results'] = [];
367
		}
368
369
		if (!$result['exactIdMatch'] && $this->cloudIdManager->isValidCloudId($search) && $this->offset === 0) {
370
			$result['exact'][] = [
371
				'label' => $search,
372
				'value' => [
373
					'shareType' => Share::SHARE_TYPE_REMOTE,
374
					'shareWith' => $search,
375
				],
376
			];
377
		}
378
379
		$this->reachedEndFor[] = 'remotes';
380
381
		return $result;
382
	}
383
384
	/**
385
	 * split user and remote from federated cloud id
386
	 *
387
	 * @param string $address federated share address
388
	 * @return array [user, remoteURL]
389
	 * @throws \Exception
390
	 */
391 View Code Duplication
	public function splitUserRemote($address) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
392
		try {
393
			$cloudId = $this->cloudIdManager->resolveCloudId($address);
394
			return [$cloudId->getUser(), $cloudId->getRemote()];
395
		} catch (\InvalidArgumentException $e) {
396
			throw new \Exception('Invalid Federated Cloud ID', 0, $e);
397
		}
398
	}
399
400
	/**
401
	 * Strips away a potential file names and trailing slashes:
402
	 * - http://localhost
403
	 * - http://localhost/
404
	 * - http://localhost/index.php
405
	 * - http://localhost/index.php/s/{shareToken}
406
	 *
407
	 * all return: http://localhost
408
	 *
409
	 * @param string $remote
410
	 * @return string
411
	 */
412 View Code Duplication
	protected function fixRemoteURL($remote) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
413
		$remote = str_replace('\\', '/', $remote);
414
		if ($fileNamePosition = strpos($remote, '/index.php')) {
415
			$remote = substr($remote, 0, $fileNamePosition);
416
		}
417
		$remote = rtrim($remote, '/');
418
419
		return $remote;
420
	}
421
422
	/**
423
	 * @NoAdminRequired
424
	 *
425
	 * @param string $search
426
	 * @param string $itemType
427
	 * @param int $page
428
	 * @param int $perPage
429
	 * @param int|int[] $shareType
430
	 * @param bool $lookup
431
	 * @return DataResponse
432
	 * @throws OCSBadRequestException
433
	 */
434
	public function search($search = '', $itemType = null, $page = 1, $perPage = 200, $shareType = null, $lookup = true) {
435
436
		// only search for string larger than a given threshold
437
		$threshold = (int)$this->config->getSystemValue('sharing.minSearchStringLength', 0);
438
		if (strlen($search) < $threshold) {
439
			return new DataResponse($this->result);
440
		}
441
442
		// never return more than the max. number of results configured in the config.php
443
		$maxResults = (int)$this->config->getSystemValue('sharing.maxAutocompleteResults', 0);
444
		if ($maxResults > 0) {
445
			$perPage = min($perPage, $maxResults);
446
		}
447
		if ($perPage <= 0) {
448
			throw new OCSBadRequestException('Invalid perPage argument');
449
		}
450
		if ($page <= 0) {
451
			throw new OCSBadRequestException('Invalid page');
452
		}
453
454
		$shareTypes = [
455
			Share::SHARE_TYPE_USER,
456
		];
457
458
		if ($itemType === 'file' || $itemType === 'folder') {
459
			if ($this->shareManager->allowGroupSharing()) {
460
				$shareTypes[] = Share::SHARE_TYPE_GROUP;
461
			}
462
463
			if ($this->isRemoteSharingAllowed($itemType)) {
464
				$shareTypes[] = Share::SHARE_TYPE_REMOTE;
465
			}
466
467
			if ($this->shareManager->shareProviderExists(Share::SHARE_TYPE_EMAIL)) {
468
				$shareTypes[] = Share::SHARE_TYPE_EMAIL;
469
			}
470
		} else {
471
			$shareTypes[] = Share::SHARE_TYPE_GROUP;
472
			$shareTypes[] = Share::SHARE_TYPE_EMAIL;
473
		}
474
475
		if (\OCP\App::isEnabled('circles')) {
476
			$shareTypes[] = Share::SHARE_TYPE_CIRCLE;
477
		}
478
479
		if (isset($_GET['shareType']) && is_array($_GET['shareType'])) {
480
			$shareTypes = array_intersect($shareTypes, $_GET['shareType']);
481
			sort($shareTypes);
482
		} else if (is_numeric($shareType)) {
483
			$shareTypes = array_intersect($shareTypes, [(int) $shareType]);
484
			sort($shareTypes);
485
		}
486
487
		$this->shareWithGroupOnly = $this->config->getAppValue('core', 'shareapi_only_share_with_group_members', 'no') === 'yes';
488
		$this->shareeEnumeration = $this->config->getAppValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes') === 'yes';
489
		$this->limit = (int) $perPage;
490
		$this->offset = $perPage * ($page - 1);
491
492
		return $this->searchSharees($search, $itemType, $shareTypes, $page, $perPage, $lookup);
493
	}
494
495
	/**
496
	 * Method to get out the static call for better testing
497
	 *
498
	 * @param string $itemType
499
	 * @return bool
500
	 */
501
	protected function isRemoteSharingAllowed($itemType) {
502
		try {
503
			$backend = Share::getBackend($itemType);
504
			return $backend->isShareTypeAllowed(Share::SHARE_TYPE_REMOTE);
505
		} catch (\Exception $e) {
506
			return false;
507
		}
508
	}
509
510
	/**
511
	 * Testable search function that does not need globals
512
	 *
513
	 * @param string $search
514
	 * @param string $itemType
515
	 * @param array $shareTypes
516
	 * @param int $page
517
	 * @param int $perPage
518
	 * @param bool $lookup
519
	 * @return DataResponse
520
	 * @throws OCSBadRequestException
521
	 */
522
	protected function searchSharees($search, $itemType, array $shareTypes, $page, $perPage, $lookup) {
523
		// Verify arguments
524
		if ($itemType === null) {
525
			throw new OCSBadRequestException('Missing itemType');
526
		}
527
528
		// Get users
529
		if (in_array(Share::SHARE_TYPE_USER, $shareTypes)) {
530
			$this->getUsers($search);
531
		}
532
533
		// Get groups
534
		if (in_array(Share::SHARE_TYPE_GROUP, $shareTypes)) {
535
			$this->getGroups($search);
536
		}
537
538
		// Get circles
539
		if (in_array(Share::SHARE_TYPE_CIRCLE, $shareTypes)) {
540
			$this->getCircles($search);
541
		}
542
543
544
		// Get remote
545
		$remoteResults = ['results' => [], 'exact' => [], 'exactIdMatch' => false];
546
		if (in_array(Share::SHARE_TYPE_REMOTE, $shareTypes)) {
547
			$remoteResults = $this->getRemote($search);
548
		}
549
550
		// Get emails
551
		$mailResults = ['results' => [], 'exact' => [], 'exactIdMatch' => false];
552
		if (in_array(Share::SHARE_TYPE_EMAIL, $shareTypes)) {
553
			$mailResults = $this->getEmail($search);
554
		}
555
556
		// Get from lookup server
557
		if ($lookup) {
558
			$this->getLookup($search);
559
		}
560
561
		// if we have a exact match, either for the federated cloud id or for the
562
		// email address we only return the exact match. It is highly unlikely
563
		// that the exact same email address and federated cloud id exists
564
		if ($mailResults['exactIdMatch'] && !$remoteResults['exactIdMatch']) {
565
			$this->result['emails'] = $mailResults['results'];
566
			$this->result['exact']['emails'] = $mailResults['exact'];
567
		} else if (!$mailResults['exactIdMatch'] && $remoteResults['exactIdMatch']) {
568
			$this->result['remotes'] = $remoteResults['results'];
569
			$this->result['exact']['remotes'] = $remoteResults['exact'];
570
		} else {
571
			$this->result['remotes'] = $remoteResults['results'];
572
			$this->result['exact']['remotes'] = $remoteResults['exact'];
573
			$this->result['emails'] = $mailResults['results'];
574
			$this->result['exact']['emails'] = $mailResults['exact'];
575
		}
576
577
		$response = new DataResponse($this->result);
578
579
		if (sizeof($this->reachedEndFor) < 3) {
580
			$response->addHeader('Link', $this->getPaginationLink($page, [
581
				'search' => $search,
582
				'itemType' => $itemType,
583
				'shareType' => $shareTypes,
584
				'perPage' => $perPage,
585
			]));
586
		}
587
588
		return $response;
589
	}
590
591
	/**
592
	 * @param string $search
593
	 * @return array
594
	 */
595
	protected function getEmail($search) {
596
		$result = ['results' => [], 'exact' => [], 'exactIdMatch' => false];
597
598
		// Search in contacts
599
		//@todo Pagination missing
600
		$addressBookContacts = $this->contactsManager->search($search, ['EMAIL', 'FN']);
601
		$lowerSearch = strtolower($search);
602
		foreach ($addressBookContacts as $contact) {
603
			if (isset($contact['EMAIL'])) {
604
				$emailAddresses = $contact['EMAIL'];
605
				if (!is_array($emailAddresses)) {
606
					$emailAddresses = [$emailAddresses];
607
				}
608
				foreach ($emailAddresses as $emailAddress) {
609
					$exactEmailMatch = strtolower($emailAddress) === $lowerSearch;
610
611
					if (isset($contact['isLocalSystemBook'])) {
612
						if ($exactEmailMatch) {
613
							$cloud = $this->cloudIdManager->resolveCloudId($contact['CLOUD'][0]);
614 View Code Duplication
							if (!$this->hasUserInResult($cloud->getUser())) {
615
								$this->result['exact']['users'][] = [
616
									'label' => $contact['FN'] . " ($emailAddress)",
617
									'value' => [
618
										'shareType' => Share::SHARE_TYPE_USER,
619
										'shareWith' => $cloud->getUser(),
620
									],
621
								];
622
							}
623
							return ['results' => [], 'exact' => [], 'exactIdMatch' => true];
624
						}
625
						if ($this->shareeEnumeration) {
626
							$cloud = $this->cloudIdManager->resolveCloudId($contact['CLOUD'][0]);
627 View Code Duplication
							if (!$this->hasUserInResult($cloud->getUser())) {
628
								$this->result['users'][] = [
629
									'label' => $contact['FN'] . " ($emailAddress)",
630
									'value' => [
631
										'shareType' => Share::SHARE_TYPE_USER,
632
										'shareWith' => $cloud->getUser(),
633
									],
634
								];
635
							}
636
						}
637
						continue;
638
					}
639
640
					if ($exactEmailMatch || strtolower($contact['FN']) === $lowerSearch) {
641
						if ($exactEmailMatch) {
642
							$result['exactIdMatch'] = true;
643
						}
644
						$result['exact'][] = [
645
							'label' => $contact['FN'] . " ($emailAddress)",
646
							'value' => [
647
								'shareType' => Share::SHARE_TYPE_EMAIL,
648
								'shareWith' => $emailAddress,
649
							],
650
						];
651 View Code Duplication
					} else {
652
						$result['results'][] = [
653
							'label' => $contact['FN'] . " ($emailAddress)",
654
							'value' => [
655
								'shareType' => Share::SHARE_TYPE_EMAIL,
656
								'shareWith' => $emailAddress,
657
							],
658
						];
659
					}
660
				}
661
			}
662
		}
663
664
		if (!$this->shareeEnumeration) {
665
			$result['results'] = [];
666
		}
667
668
		if (!$result['exactIdMatch'] && filter_var($search, FILTER_VALIDATE_EMAIL)) {
669
			$result['exact'][] = [
670
				'label' => $search,
671
				'value' => [
672
					'shareType' => Share::SHARE_TYPE_EMAIL,
673
					'shareWith' => $search,
674
				],
675
			];
676
		}
677
678
		$this->reachedEndFor[] = 'emails';
679
680
		return $result;
681
	}
682
683
	protected function getLookup($search) {
684
		$isEnabled = $this->config->getAppValue('files_sharing', 'lookupServerEnabled', 'no');
685
		$lookupServerUrl = $this->config->getSystemValue('lookup_server', 'https://lookup.nextcloud.com');
686
		$lookupServerUrl = rtrim($lookupServerUrl, '/');
687
		$result = [];
688
689
		if($isEnabled === 'yes') {
690
			try {
691
				$client = $this->clientService->newClient();
692
				$response = $client->get(
693
					$lookupServerUrl . '/users?search=' . urlencode($search),
694
					[
695
						'timeout' => 10,
696
						'connect_timeout' => 3,
697
					]
698
				);
699
700
				$body = json_decode($response->getBody(), true);
701
702
				$result = [];
703
				foreach ($body as $lookup) {
704
					$result[] = [
705
						'label' => $lookup['federationId'],
706
						'value' => [
707
							'shareType' => Share::SHARE_TYPE_REMOTE,
708
							'shareWith' => $lookup['federationId'],
709
						],
710
						'extra' => $lookup,
711
					];
712
				}
713
			} catch (\Exception $e) {}
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
714
		}
715
716
		$this->result['lookup'] = $result;
717
	}
718
719
	/**
720
	 * Check if a given user is already part of the result
721
	 *
722
	 * @param string $userId
723
	 * @return bool
724
	 */
725
	protected function hasUserInResult($userId) {
726 View Code Duplication
		foreach ($this->result['exact']['users'] as $result) {
727
			if ($result['value']['shareWith'] === $userId) {
728
				return true;
729
			}
730
		}
731
732 View Code Duplication
		foreach ($this->result['users'] as $result) {
733
			if ($result['value']['shareWith'] === $userId) {
734
				return true;
735
			}
736
		}
737
738
		return false;
739
	}
740
741
	/**
742
	 * Generates a bunch of pagination links for the current page
743
	 *
744
	 * @param int $page Current page
745
	 * @param array $params Parameters for the URL
746
	 * @return string
747
	 */
748
	protected function getPaginationLink($page, array $params) {
749
		if ($this->isV2()) {
750
			$url = $this->urlGenerator->getAbsoluteURL('/ocs/v2.php/apps/files_sharing/api/v1/sharees') . '?';
751
		} else {
752
			$url = $this->urlGenerator->getAbsoluteURL('/ocs/v1.php/apps/files_sharing/api/v1/sharees') . '?';
753
		}
754
		$params['page'] = $page + 1;
755
		$link = '<' . $url . http_build_query($params) . '>; rel="next"';
756
757
		return $link;
758
	}
759
760
	/**
761
	 * @return bool
762
	 */
763
	protected function isV2() {
764
		return $this->request->getScriptName() === '/ocs/v2.php';
765
	}
766
}
767