Completed
Push — master ( fc47d0...2398d4 )
by Morris
13:52
created
apps/files_sharing/lib/Controller/ShareesAPIController.php 2 patches
Indentation   +720 added lines, -720 removed lines patch added patch discarded remove patch
@@ -43,724 +43,724 @@
 block discarded – undo
43 43
 
44 44
 class ShareesAPIController extends OCSController {
45 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
-	public function __construct($appName,
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());
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
-		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
-			} 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
-		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
-			} 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
-					} 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
-	public function splitUserRemote($address) {
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
-	protected function fixRemoteURL($remote) {
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
-							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
-							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
-					} 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) {}
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
-		foreach ($this->result['exact']['users'] as $result) {
727
-			if ($result['value']['shareWith'] === $userId) {
728
-				return true;
729
-			}
730
-		}
731
-
732
-		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
-	}
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
+    public function __construct($appName,
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());
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
+        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
+            } 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
+        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
+            } 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
+                    } 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
+    public function splitUserRemote($address) {
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
+    protected function fixRemoteURL($remote) {
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
+                            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
+                            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
+                    } 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) {}
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
+        foreach ($this->result['exact']['users'] as $result) {
727
+            if ($result['value']['shareWith'] === $userId) {
728
+                return true;
729
+            }
730
+        }
731
+
732
+        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 766
 }
Please login to merge, or discard this patch.
Spacing   +16 added lines, -16 removed lines patch added patch discarded remove patch
@@ -236,17 +236,17 @@  discard block
 block discarded – undo
236 236
 		$this->result['groups'] = $this->result['exact']['groups'] = [];
237 237
 
238 238
 		$groups = $this->groupManager->search($search, $this->limit, $this->offset);
239
-		$groupIds = array_map(function (IGroup $group) { return $group->getGID(); }, $groups);
239
+		$groupIds = array_map(function(IGroup $group) { return $group->getGID(); }, $groups);
240 240
 
241 241
 		if (!$this->shareeEnumeration || sizeof($groups) < $this->limit) {
242 242
 			$this->reachedEndFor[] = 'groups';
243 243
 		}
244 244
 
245
-		$userGroups =  [];
245
+		$userGroups = [];
246 246
 		if (!empty($groups) && $this->shareWithGroupOnly) {
247 247
 			// Intersect all the groups that match with the groups this user is a member of
248 248
 			$userGroups = $this->groupManager->getUserGroups($this->userSession->getUser());
249
-			$userGroups = array_map(function (IGroup $group) { return $group->getGID(); }, $userGroups);
249
+			$userGroups = array_map(function(IGroup $group) { return $group->getGID(); }, $userGroups);
250 250
 			$groupIds = array_intersect($groupIds, $userGroups);
251 251
 		}
252 252
 
@@ -341,7 +341,7 @@  discard block
 block discarded – undo
341 341
 							$result['exactIdMatch'] = true;
342 342
 						}
343 343
 						$result['exact'][] = [
344
-							'label' => $contact['FN'] . " ($cloudId)",
344
+							'label' => $contact['FN']." ($cloudId)",
345 345
 							'value' => [
346 346
 								'shareType' => Share::SHARE_TYPE_REMOTE,
347 347
 								'shareWith' => $cloudId,
@@ -350,7 +350,7 @@  discard block
 block discarded – undo
350 350
 						];
351 351
 					} else {
352 352
 						$result['results'][] = [
353
-							'label' => $contact['FN'] . " ($cloudId)",
353
+							'label' => $contact['FN']." ($cloudId)",
354 354
 							'value' => [
355 355
 								'shareType' => Share::SHARE_TYPE_REMOTE,
356 356
 								'shareWith' => $cloudId,
@@ -434,13 +434,13 @@  discard block
 block discarded – undo
434 434
 	public function search($search = '', $itemType = null, $page = 1, $perPage = 200, $shareType = null, $lookup = true) {
435 435
 
436 436
 		// only search for string larger than a given threshold
437
-		$threshold = (int)$this->config->getSystemValue('sharing.minSearchStringLength', 0);
437
+		$threshold = (int) $this->config->getSystemValue('sharing.minSearchStringLength', 0);
438 438
 		if (strlen($search) < $threshold) {
439 439
 			return new DataResponse($this->result);
440 440
 		}
441 441
 
442 442
 		// never return more than the max. number of results configured in the config.php
443
-		$maxResults = (int)$this->config->getSystemValue('sharing.maxAutocompleteResults', 0);
443
+		$maxResults = (int) $this->config->getSystemValue('sharing.maxAutocompleteResults', 0);
444 444
 		if ($maxResults > 0) {
445 445
 			$perPage = min($perPage, $maxResults);
446 446
 		}
@@ -613,7 +613,7 @@  discard block
 block discarded – undo
613 613
 							$cloud = $this->cloudIdManager->resolveCloudId($contact['CLOUD'][0]);
614 614
 							if (!$this->hasUserInResult($cloud->getUser())) {
615 615
 								$this->result['exact']['users'][] = [
616
-									'label' => $contact['FN'] . " ($emailAddress)",
616
+									'label' => $contact['FN']." ($emailAddress)",
617 617
 									'value' => [
618 618
 										'shareType' => Share::SHARE_TYPE_USER,
619 619
 										'shareWith' => $cloud->getUser(),
@@ -626,7 +626,7 @@  discard block
 block discarded – undo
626 626
 							$cloud = $this->cloudIdManager->resolveCloudId($contact['CLOUD'][0]);
627 627
 							if (!$this->hasUserInResult($cloud->getUser())) {
628 628
 								$this->result['users'][] = [
629
-									'label' => $contact['FN'] . " ($emailAddress)",
629
+									'label' => $contact['FN']." ($emailAddress)",
630 630
 									'value' => [
631 631
 										'shareType' => Share::SHARE_TYPE_USER,
632 632
 										'shareWith' => $cloud->getUser(),
@@ -642,7 +642,7 @@  discard block
 block discarded – undo
642 642
 							$result['exactIdMatch'] = true;
643 643
 						}
644 644
 						$result['exact'][] = [
645
-							'label' => $contact['FN'] . " ($emailAddress)",
645
+							'label' => $contact['FN']." ($emailAddress)",
646 646
 							'value' => [
647 647
 								'shareType' => Share::SHARE_TYPE_EMAIL,
648 648
 								'shareWith' => $emailAddress,
@@ -650,7 +650,7 @@  discard block
 block discarded – undo
650 650
 						];
651 651
 					} else {
652 652
 						$result['results'][] = [
653
-							'label' => $contact['FN'] . " ($emailAddress)",
653
+							'label' => $contact['FN']." ($emailAddress)",
654 654
 							'value' => [
655 655
 								'shareType' => Share::SHARE_TYPE_EMAIL,
656 656
 								'shareWith' => $emailAddress,
@@ -686,11 +686,11 @@  discard block
 block discarded – undo
686 686
 		$lookupServerUrl = rtrim($lookupServerUrl, '/');
687 687
 		$result = [];
688 688
 
689
-		if($isEnabled === 'yes') {
689
+		if ($isEnabled === 'yes') {
690 690
 			try {
691 691
 				$client = $this->clientService->newClient();
692 692
 				$response = $client->get(
693
-					$lookupServerUrl . '/users?search=' . urlencode($search),
693
+					$lookupServerUrl.'/users?search='.urlencode($search),
694 694
 					[
695 695
 						'timeout' => 10,
696 696
 						'connect_timeout' => 3,
@@ -747,12 +747,12 @@  discard block
 block discarded – undo
747 747
 	 */
748 748
 	protected function getPaginationLink($page, array $params) {
749 749
 		if ($this->isV2()) {
750
-			$url = $this->urlGenerator->getAbsoluteURL('/ocs/v2.php/apps/files_sharing/api/v1/sharees') . '?';
750
+			$url = $this->urlGenerator->getAbsoluteURL('/ocs/v2.php/apps/files_sharing/api/v1/sharees').'?';
751 751
 		} else {
752
-			$url = $this->urlGenerator->getAbsoluteURL('/ocs/v1.php/apps/files_sharing/api/v1/sharees') . '?';
752
+			$url = $this->urlGenerator->getAbsoluteURL('/ocs/v1.php/apps/files_sharing/api/v1/sharees').'?';
753 753
 		}
754 754
 		$params['page'] = $page + 1;
755
-		$link = '<' . $url . http_build_query($params) . '>; rel="next"';
755
+		$link = '<'.$url.http_build_query($params).'>; rel="next"';
756 756
 
757 757
 		return $link;
758 758
 	}
Please login to merge, or discard this patch.