Completed
Push — master ( cf7c4a...422c3e )
by Lukas
15:02
created
apps/files_sharing/lib/Controller/ShareesAPIController.php 3 patches
Doc Comments   +4 added lines, -1 removed lines patch added patch discarded remove patch
@@ -391,7 +391,7 @@  discard block
 block discarded – undo
391 391
 	 * split user and remote from federated cloud id
392 392
 	 *
393 393
 	 * @param string $address federated share address
394
-	 * @return array [user, remoteURL]
394
+	 * @return string[] [user, remoteURL]
395 395
 	 * @throws \InvalidArgumentException
396 396
 	 */
397 397
 	public function splitUserRemote($address) {
@@ -697,6 +697,9 @@  discard block
 block discarded – undo
697 697
 		return $result;
698 698
 	}
699 699
 
700
+	/**
701
+	 * @param string $search
702
+	 */
700 703
 	protected function getLookup($search) {
701 704
 		$isEnabled = $this->config->getAppValue('files_sharing', 'lookupServerEnabled', 'no');
702 705
 		$lookupServerUrl = $this->config->getSystemValue('lookup_server', 'https://lookup.nextcloud.com');
Please login to merge, or discard this patch.
Indentation   +737 added lines, -737 removed lines patch added patch discarded remove patch
@@ -43,741 +43,741 @@
 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
-	 * @suppress PhanUndeclaredClassMethod
303
-	 */
304
-	protected function getCircles($search) {
305
-		$this->result['circles'] = $this->result['exact']['circles'] = [];
306
-
307
-		$result = \OCA\Circles\Api\Sharees::search($search, $this->limit, $this->offset);
308
-		if (array_key_exists('circles', $result['exact'])) {
309
-			$this->result['exact']['circles'] = $result['exact']['circles'];
310
-		}
311
-		if (array_key_exists('circles', $result)) {
312
-			$this->result['circles'] = $result['circles'];
313
-		}
314
-	}
315
-
316
-
317
-	/**
318
-	 * @param string $search
319
-	 * @return array
320
-	 */
321
-	protected function getRemote($search) {
322
-		$result = ['results' => [], 'exact' => []];
323
-
324
-		// Search in contacts
325
-		//@todo Pagination missing
326
-		$addressBookContacts = $this->contactsManager->search($search, ['CLOUD', 'FN']);
327
-		$result['exactIdMatch'] = false;
328
-		foreach ($addressBookContacts as $contact) {
329
-			if (isset($contact['isLocalSystemBook'])) {
330
-				continue;
331
-			}
332
-			if (isset($contact['CLOUD'])) {
333
-				$cloudIds = $contact['CLOUD'];
334
-				if (!is_array($cloudIds)) {
335
-					$cloudIds = [$cloudIds];
336
-				}
337
-				$lowerSearch = strtolower($search);
338
-				foreach ($cloudIds as $cloudId) {
339
-					try {
340
-						list(, $serverUrl) = $this->splitUserRemote($cloudId);
341
-					} catch (\InvalidArgumentException $e) {
342
-						continue;
343
-					}
344
-
345
-					if (strtolower($contact['FN']) === $lowerSearch || strtolower($cloudId) === $lowerSearch) {
346
-						if (strtolower($cloudId) === $lowerSearch) {
347
-							$result['exactIdMatch'] = true;
348
-						}
349
-						$result['exact'][] = [
350
-							'label' => $contact['FN'] . " ($cloudId)",
351
-							'value' => [
352
-								'shareType' => Share::SHARE_TYPE_REMOTE,
353
-								'shareWith' => $cloudId,
354
-								'server' => $serverUrl,
355
-							],
356
-						];
357
-					} else {
358
-						$result['results'][] = [
359
-							'label' => $contact['FN'] . " ($cloudId)",
360
-							'value' => [
361
-								'shareType' => Share::SHARE_TYPE_REMOTE,
362
-								'shareWith' => $cloudId,
363
-								'server' => $serverUrl,
364
-							],
365
-						];
366
-					}
367
-				}
368
-			}
369
-		}
370
-
371
-		if (!$this->shareeEnumeration) {
372
-			$result['results'] = [];
373
-		}
374
-
375
-		if (!$result['exactIdMatch'] && $this->cloudIdManager->isValidCloudId($search) && $this->offset === 0) {
376
-			$result['exact'][] = [
377
-				'label' => $search,
378
-				'value' => [
379
-					'shareType' => Share::SHARE_TYPE_REMOTE,
380
-					'shareWith' => $search,
381
-				],
382
-			];
383
-		}
384
-
385
-		$this->reachedEndFor[] = 'remotes';
386
-
387
-		return $result;
388
-	}
389
-
390
-	/**
391
-	 * split user and remote from federated cloud id
392
-	 *
393
-	 * @param string $address federated share address
394
-	 * @return array [user, remoteURL]
395
-	 * @throws \InvalidArgumentException
396
-	 */
397
-	public function splitUserRemote($address) {
398
-		try {
399
-			$cloudId = $this->cloudIdManager->resolveCloudId($address);
400
-			return [$cloudId->getUser(), $cloudId->getRemote()];
401
-		} catch (\InvalidArgumentException $e) {
402
-			throw new \InvalidArgumentException('Invalid Federated Cloud ID', 0, $e);
403
-		}
404
-	}
405
-
406
-	/**
407
-	 * Strips away a potential file names and trailing slashes:
408
-	 * - http://localhost
409
-	 * - http://localhost/
410
-	 * - http://localhost/index.php
411
-	 * - http://localhost/index.php/s/{shareToken}
412
-	 *
413
-	 * all return: http://localhost
414
-	 *
415
-	 * @param string $remote
416
-	 * @return string
417
-	 */
418
-	protected function fixRemoteURL($remote) {
419
-		$remote = str_replace('\\', '/', $remote);
420
-		if ($fileNamePosition = strpos($remote, '/index.php')) {
421
-			$remote = substr($remote, 0, $fileNamePosition);
422
-		}
423
-		$remote = rtrim($remote, '/');
424
-
425
-		return $remote;
426
-	}
427
-
428
-	/**
429
-	 * @NoAdminRequired
430
-	 *
431
-	 * @param string $search
432
-	 * @param string $itemType
433
-	 * @param int $page
434
-	 * @param int $perPage
435
-	 * @param int|int[] $shareType
436
-	 * @param bool $lookup
437
-	 * @return DataResponse
438
-	 * @throws OCSBadRequestException
439
-	 */
440
-	public function search($search = '', $itemType = null, $page = 1, $perPage = 200, $shareType = null, $lookup = true) {
441
-
442
-		// only search for string larger than a given threshold
443
-		$threshold = (int)$this->config->getSystemValue('sharing.minSearchStringLength', 0);
444
-		if (strlen($search) < $threshold) {
445
-			return new DataResponse($this->result);
446
-		}
447
-
448
-		// never return more than the max. number of results configured in the config.php
449
-		$maxResults = (int)$this->config->getSystemValue('sharing.maxAutocompleteResults', 0);
450
-		if ($maxResults > 0) {
451
-			$perPage = min($perPage, $maxResults);
452
-		}
453
-		if ($perPage <= 0) {
454
-			throw new OCSBadRequestException('Invalid perPage argument');
455
-		}
456
-		if ($page <= 0) {
457
-			throw new OCSBadRequestException('Invalid page');
458
-		}
459
-
460
-		$shareTypes = [
461
-			Share::SHARE_TYPE_USER,
462
-		];
463
-
464
-		if ($itemType === 'file' || $itemType === 'folder') {
465
-			if ($this->shareManager->allowGroupSharing()) {
466
-				$shareTypes[] = Share::SHARE_TYPE_GROUP;
467
-			}
468
-
469
-			if ($this->isRemoteSharingAllowed($itemType)) {
470
-				$shareTypes[] = Share::SHARE_TYPE_REMOTE;
471
-			}
472
-
473
-			if ($this->shareManager->shareProviderExists(Share::SHARE_TYPE_EMAIL)) {
474
-				$shareTypes[] = Share::SHARE_TYPE_EMAIL;
475
-			}
476
-		} else {
477
-			$shareTypes[] = Share::SHARE_TYPE_GROUP;
478
-			$shareTypes[] = Share::SHARE_TYPE_EMAIL;
479
-		}
480
-
481
-		if (\OC::$server->getAppManager()->isEnabledForUser('circles') && class_exists('\OCA\Circles\ShareByCircleProvider')) {
482
-			$shareTypes[] = Share::SHARE_TYPE_CIRCLE;
483
-		}
484
-
485
-		if (isset($_GET['shareType']) && is_array($_GET['shareType'])) {
486
-			$shareTypes = array_intersect($shareTypes, $_GET['shareType']);
487
-			sort($shareTypes);
488
-		} else if (is_numeric($shareType)) {
489
-			$shareTypes = array_intersect($shareTypes, [(int) $shareType]);
490
-			sort($shareTypes);
491
-		}
492
-
493
-		$this->shareWithGroupOnly = $this->config->getAppValue('core', 'shareapi_only_share_with_group_members', 'no') === 'yes';
494
-		$this->shareeEnumeration = $this->config->getAppValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes') === 'yes';
495
-		$this->limit = (int) $perPage;
496
-		$this->offset = $perPage * ($page - 1);
497
-
498
-		return $this->searchSharees($search, $itemType, $shareTypes, $page, $perPage, $lookup);
499
-	}
500
-
501
-	/**
502
-	 * Method to get out the static call for better testing
503
-	 *
504
-	 * @param string $itemType
505
-	 * @return bool
506
-	 */
507
-	protected function isRemoteSharingAllowed($itemType) {
508
-		try {
509
-			$backend = Share::getBackend($itemType);
510
-			return $backend->isShareTypeAllowed(Share::SHARE_TYPE_REMOTE);
511
-		} catch (\Exception $e) {
512
-			return false;
513
-		}
514
-	}
515
-
516
-	/**
517
-	 * Testable search function that does not need globals
518
-	 *
519
-	 * @param string $search
520
-	 * @param string $itemType
521
-	 * @param array $shareTypes
522
-	 * @param int $page
523
-	 * @param int $perPage
524
-	 * @param bool $lookup
525
-	 * @return DataResponse
526
-	 * @throws OCSBadRequestException
527
-	 */
528
-	protected function searchSharees($search, $itemType, array $shareTypes, $page, $perPage, $lookup) {
529
-		// Verify arguments
530
-		if ($itemType === null) {
531
-			throw new OCSBadRequestException('Missing itemType');
532
-		}
533
-
534
-		// Get users
535
-		if (in_array(Share::SHARE_TYPE_USER, $shareTypes)) {
536
-			$this->getUsers($search);
537
-		}
538
-
539
-		// Get groups
540
-		if (in_array(Share::SHARE_TYPE_GROUP, $shareTypes)) {
541
-			$this->getGroups($search);
542
-		}
543
-
544
-		// Get circles
545
-		if (in_array(Share::SHARE_TYPE_CIRCLE, $shareTypes)) {
546
-			$this->getCircles($search);
547
-		}
548
-
549
-
550
-		// Get remote
551
-		$remoteResults = ['results' => [], 'exact' => [], 'exactIdMatch' => false];
552
-		if (in_array(Share::SHARE_TYPE_REMOTE, $shareTypes)) {
553
-			$remoteResults = $this->getRemote($search);
554
-		}
555
-
556
-		// Get emails
557
-		$mailResults = ['results' => [], 'exact' => [], 'exactIdMatch' => false];
558
-		if (in_array(Share::SHARE_TYPE_EMAIL, $shareTypes)) {
559
-			$mailResults = $this->getEmail($search);
560
-		}
561
-
562
-		// Get from lookup server
563
-		if ($lookup) {
564
-			$this->getLookup($search);
565
-		}
566
-
567
-		// if we have a exact match, either for the federated cloud id or for the
568
-		// email address we only return the exact match. It is highly unlikely
569
-		// that the exact same email address and federated cloud id exists
570
-		if ($mailResults['exactIdMatch'] && !$remoteResults['exactIdMatch']) {
571
-			$this->result['emails'] = $mailResults['results'];
572
-			$this->result['exact']['emails'] = $mailResults['exact'];
573
-		} else if (!$mailResults['exactIdMatch'] && $remoteResults['exactIdMatch']) {
574
-			$this->result['remotes'] = $remoteResults['results'];
575
-			$this->result['exact']['remotes'] = $remoteResults['exact'];
576
-		} else {
577
-			$this->result['remotes'] = $remoteResults['results'];
578
-			$this->result['exact']['remotes'] = $remoteResults['exact'];
579
-			$this->result['emails'] = $mailResults['results'];
580
-			$this->result['exact']['emails'] = $mailResults['exact'];
581
-		}
582
-
583
-		$response = new DataResponse($this->result);
584
-
585
-		if (sizeof($this->reachedEndFor) < 3) {
586
-			$response->addHeader('Link', $this->getPaginationLink($page, [
587
-				'search' => $search,
588
-				'itemType' => $itemType,
589
-				'shareType' => $shareTypes,
590
-				'perPage' => $perPage,
591
-			]));
592
-		}
593
-
594
-		return $response;
595
-	}
596
-
597
-	/**
598
-	 * @param string $search
599
-	 * @return array
600
-	 */
601
-	protected function getEmail($search) {
602
-		$result = ['results' => [], 'exact' => [], 'exactIdMatch' => false];
603
-
604
-		// Search in contacts
605
-		//@todo Pagination missing
606
-		$addressBookContacts = $this->contactsManager->search($search, ['EMAIL', 'FN']);
607
-		$lowerSearch = strtolower($search);
608
-		foreach ($addressBookContacts as $contact) {
609
-			if (isset($contact['EMAIL'])) {
610
-				$emailAddresses = $contact['EMAIL'];
611
-				if (!is_array($emailAddresses)) {
612
-					$emailAddresses = [$emailAddresses];
613
-				}
614
-				foreach ($emailAddresses as $emailAddress) {
615
-					$exactEmailMatch = strtolower($emailAddress) === $lowerSearch;
616
-
617
-					if (isset($contact['isLocalSystemBook'])) {
618
-						if ($exactEmailMatch) {
619
-							try {
620
-								$cloud = $this->cloudIdManager->resolveCloudId($contact['CLOUD'][0]);
621
-							} catch (\InvalidArgumentException $e) {
622
-								continue;
623
-							}
624
-
625
-							if (!$this->hasUserInResult($cloud->getUser())) {
626
-								$this->result['exact']['users'][] = [
627
-									'label' => $contact['FN'] . " ($emailAddress)",
628
-									'value' => [
629
-										'shareType' => Share::SHARE_TYPE_USER,
630
-										'shareWith' => $cloud->getUser(),
631
-									],
632
-								];
633
-							}
634
-							return ['results' => [], 'exact' => [], 'exactIdMatch' => true];
635
-						}
636
-
637
-						if ($this->shareeEnumeration) {
638
-							try {
639
-								$cloud = $this->cloudIdManager->resolveCloudId($contact['CLOUD'][0]);
640
-							} catch (\InvalidArgumentException $e) {
641
-								continue;
642
-							}
643
-
644
-							if (!$this->hasUserInResult($cloud->getUser())) {
645
-								$this->result['users'][] = [
646
-									'label' => $contact['FN'] . " ($emailAddress)",
647
-									'value' => [
648
-										'shareType' => Share::SHARE_TYPE_USER,
649
-										'shareWith' => $cloud->getUser(),
650
-									],
651
-								];
652
-							}
653
-						}
654
-						continue;
655
-					}
656
-
657
-					if ($exactEmailMatch || strtolower($contact['FN']) === $lowerSearch) {
658
-						if ($exactEmailMatch) {
659
-							$result['exactIdMatch'] = true;
660
-						}
661
-						$result['exact'][] = [
662
-							'label' => $contact['FN'] . " ($emailAddress)",
663
-							'value' => [
664
-								'shareType' => Share::SHARE_TYPE_EMAIL,
665
-								'shareWith' => $emailAddress,
666
-							],
667
-						];
668
-					} else {
669
-						$result['results'][] = [
670
-							'label' => $contact['FN'] . " ($emailAddress)",
671
-							'value' => [
672
-								'shareType' => Share::SHARE_TYPE_EMAIL,
673
-								'shareWith' => $emailAddress,
674
-							],
675
-						];
676
-					}
677
-				}
678
-			}
679
-		}
680
-
681
-		if (!$this->shareeEnumeration) {
682
-			$result['results'] = [];
683
-		}
684
-
685
-		if (!$result['exactIdMatch'] && filter_var($search, FILTER_VALIDATE_EMAIL)) {
686
-			$result['exact'][] = [
687
-				'label' => $search,
688
-				'value' => [
689
-					'shareType' => Share::SHARE_TYPE_EMAIL,
690
-					'shareWith' => $search,
691
-				],
692
-			];
693
-		}
694
-
695
-		$this->reachedEndFor[] = 'emails';
696
-
697
-		return $result;
698
-	}
699
-
700
-	protected function getLookup($search) {
701
-		$isEnabled = $this->config->getAppValue('files_sharing', 'lookupServerEnabled', 'no');
702
-		$lookupServerUrl = $this->config->getSystemValue('lookup_server', 'https://lookup.nextcloud.com');
703
-		$lookupServerUrl = rtrim($lookupServerUrl, '/');
704
-		$result = [];
705
-
706
-		if($isEnabled === 'yes') {
707
-			try {
708
-				$client = $this->clientService->newClient();
709
-				$response = $client->get(
710
-					$lookupServerUrl . '/users?search=' . urlencode($search),
711
-					[
712
-						'timeout' => 10,
713
-						'connect_timeout' => 3,
714
-					]
715
-				);
716
-
717
-				$body = json_decode($response->getBody(), true);
718
-
719
-				$result = [];
720
-				foreach ($body as $lookup) {
721
-					$result[] = [
722
-						'label' => $lookup['federationId'],
723
-						'value' => [
724
-							'shareType' => Share::SHARE_TYPE_REMOTE,
725
-							'shareWith' => $lookup['federationId'],
726
-						],
727
-						'extra' => $lookup,
728
-					];
729
-				}
730
-			} catch (\Exception $e) {}
731
-		}
732
-
733
-		$this->result['lookup'] = $result;
734
-	}
735
-
736
-	/**
737
-	 * Check if a given user is already part of the result
738
-	 *
739
-	 * @param string $userId
740
-	 * @return bool
741
-	 */
742
-	protected function hasUserInResult($userId) {
743
-		foreach ($this->result['exact']['users'] as $result) {
744
-			if ($result['value']['shareWith'] === $userId) {
745
-				return true;
746
-			}
747
-		}
748
-
749
-		foreach ($this->result['users'] as $result) {
750
-			if ($result['value']['shareWith'] === $userId) {
751
-				return true;
752
-			}
753
-		}
754
-
755
-		return false;
756
-	}
757
-
758
-	/**
759
-	 * Generates a bunch of pagination links for the current page
760
-	 *
761
-	 * @param int $page Current page
762
-	 * @param array $params Parameters for the URL
763
-	 * @return string
764
-	 */
765
-	protected function getPaginationLink($page, array $params) {
766
-		if ($this->isV2()) {
767
-			$url = $this->urlGenerator->getAbsoluteURL('/ocs/v2.php/apps/files_sharing/api/v1/sharees') . '?';
768
-		} else {
769
-			$url = $this->urlGenerator->getAbsoluteURL('/ocs/v1.php/apps/files_sharing/api/v1/sharees') . '?';
770
-		}
771
-		$params['page'] = $page + 1;
772
-		$link = '<' . $url . http_build_query($params) . '>; rel="next"';
773
-
774
-		return $link;
775
-	}
776
-
777
-	/**
778
-	 * @return bool
779
-	 */
780
-	protected function isV2() {
781
-		return $this->request->getScriptName() === '/ocs/v2.php';
782
-	}
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
+     * @suppress PhanUndeclaredClassMethod
303
+     */
304
+    protected function getCircles($search) {
305
+        $this->result['circles'] = $this->result['exact']['circles'] = [];
306
+
307
+        $result = \OCA\Circles\Api\Sharees::search($search, $this->limit, $this->offset);
308
+        if (array_key_exists('circles', $result['exact'])) {
309
+            $this->result['exact']['circles'] = $result['exact']['circles'];
310
+        }
311
+        if (array_key_exists('circles', $result)) {
312
+            $this->result['circles'] = $result['circles'];
313
+        }
314
+    }
315
+
316
+
317
+    /**
318
+     * @param string $search
319
+     * @return array
320
+     */
321
+    protected function getRemote($search) {
322
+        $result = ['results' => [], 'exact' => []];
323
+
324
+        // Search in contacts
325
+        //@todo Pagination missing
326
+        $addressBookContacts = $this->contactsManager->search($search, ['CLOUD', 'FN']);
327
+        $result['exactIdMatch'] = false;
328
+        foreach ($addressBookContacts as $contact) {
329
+            if (isset($contact['isLocalSystemBook'])) {
330
+                continue;
331
+            }
332
+            if (isset($contact['CLOUD'])) {
333
+                $cloudIds = $contact['CLOUD'];
334
+                if (!is_array($cloudIds)) {
335
+                    $cloudIds = [$cloudIds];
336
+                }
337
+                $lowerSearch = strtolower($search);
338
+                foreach ($cloudIds as $cloudId) {
339
+                    try {
340
+                        list(, $serverUrl) = $this->splitUserRemote($cloudId);
341
+                    } catch (\InvalidArgumentException $e) {
342
+                        continue;
343
+                    }
344
+
345
+                    if (strtolower($contact['FN']) === $lowerSearch || strtolower($cloudId) === $lowerSearch) {
346
+                        if (strtolower($cloudId) === $lowerSearch) {
347
+                            $result['exactIdMatch'] = true;
348
+                        }
349
+                        $result['exact'][] = [
350
+                            'label' => $contact['FN'] . " ($cloudId)",
351
+                            'value' => [
352
+                                'shareType' => Share::SHARE_TYPE_REMOTE,
353
+                                'shareWith' => $cloudId,
354
+                                'server' => $serverUrl,
355
+                            ],
356
+                        ];
357
+                    } else {
358
+                        $result['results'][] = [
359
+                            'label' => $contact['FN'] . " ($cloudId)",
360
+                            'value' => [
361
+                                'shareType' => Share::SHARE_TYPE_REMOTE,
362
+                                'shareWith' => $cloudId,
363
+                                'server' => $serverUrl,
364
+                            ],
365
+                        ];
366
+                    }
367
+                }
368
+            }
369
+        }
370
+
371
+        if (!$this->shareeEnumeration) {
372
+            $result['results'] = [];
373
+        }
374
+
375
+        if (!$result['exactIdMatch'] && $this->cloudIdManager->isValidCloudId($search) && $this->offset === 0) {
376
+            $result['exact'][] = [
377
+                'label' => $search,
378
+                'value' => [
379
+                    'shareType' => Share::SHARE_TYPE_REMOTE,
380
+                    'shareWith' => $search,
381
+                ],
382
+            ];
383
+        }
384
+
385
+        $this->reachedEndFor[] = 'remotes';
386
+
387
+        return $result;
388
+    }
389
+
390
+    /**
391
+     * split user and remote from federated cloud id
392
+     *
393
+     * @param string $address federated share address
394
+     * @return array [user, remoteURL]
395
+     * @throws \InvalidArgumentException
396
+     */
397
+    public function splitUserRemote($address) {
398
+        try {
399
+            $cloudId = $this->cloudIdManager->resolveCloudId($address);
400
+            return [$cloudId->getUser(), $cloudId->getRemote()];
401
+        } catch (\InvalidArgumentException $e) {
402
+            throw new \InvalidArgumentException('Invalid Federated Cloud ID', 0, $e);
403
+        }
404
+    }
405
+
406
+    /**
407
+     * Strips away a potential file names and trailing slashes:
408
+     * - http://localhost
409
+     * - http://localhost/
410
+     * - http://localhost/index.php
411
+     * - http://localhost/index.php/s/{shareToken}
412
+     *
413
+     * all return: http://localhost
414
+     *
415
+     * @param string $remote
416
+     * @return string
417
+     */
418
+    protected function fixRemoteURL($remote) {
419
+        $remote = str_replace('\\', '/', $remote);
420
+        if ($fileNamePosition = strpos($remote, '/index.php')) {
421
+            $remote = substr($remote, 0, $fileNamePosition);
422
+        }
423
+        $remote = rtrim($remote, '/');
424
+
425
+        return $remote;
426
+    }
427
+
428
+    /**
429
+     * @NoAdminRequired
430
+     *
431
+     * @param string $search
432
+     * @param string $itemType
433
+     * @param int $page
434
+     * @param int $perPage
435
+     * @param int|int[] $shareType
436
+     * @param bool $lookup
437
+     * @return DataResponse
438
+     * @throws OCSBadRequestException
439
+     */
440
+    public function search($search = '', $itemType = null, $page = 1, $perPage = 200, $shareType = null, $lookup = true) {
441
+
442
+        // only search for string larger than a given threshold
443
+        $threshold = (int)$this->config->getSystemValue('sharing.minSearchStringLength', 0);
444
+        if (strlen($search) < $threshold) {
445
+            return new DataResponse($this->result);
446
+        }
447
+
448
+        // never return more than the max. number of results configured in the config.php
449
+        $maxResults = (int)$this->config->getSystemValue('sharing.maxAutocompleteResults', 0);
450
+        if ($maxResults > 0) {
451
+            $perPage = min($perPage, $maxResults);
452
+        }
453
+        if ($perPage <= 0) {
454
+            throw new OCSBadRequestException('Invalid perPage argument');
455
+        }
456
+        if ($page <= 0) {
457
+            throw new OCSBadRequestException('Invalid page');
458
+        }
459
+
460
+        $shareTypes = [
461
+            Share::SHARE_TYPE_USER,
462
+        ];
463
+
464
+        if ($itemType === 'file' || $itemType === 'folder') {
465
+            if ($this->shareManager->allowGroupSharing()) {
466
+                $shareTypes[] = Share::SHARE_TYPE_GROUP;
467
+            }
468
+
469
+            if ($this->isRemoteSharingAllowed($itemType)) {
470
+                $shareTypes[] = Share::SHARE_TYPE_REMOTE;
471
+            }
472
+
473
+            if ($this->shareManager->shareProviderExists(Share::SHARE_TYPE_EMAIL)) {
474
+                $shareTypes[] = Share::SHARE_TYPE_EMAIL;
475
+            }
476
+        } else {
477
+            $shareTypes[] = Share::SHARE_TYPE_GROUP;
478
+            $shareTypes[] = Share::SHARE_TYPE_EMAIL;
479
+        }
480
+
481
+        if (\OC::$server->getAppManager()->isEnabledForUser('circles') && class_exists('\OCA\Circles\ShareByCircleProvider')) {
482
+            $shareTypes[] = Share::SHARE_TYPE_CIRCLE;
483
+        }
484
+
485
+        if (isset($_GET['shareType']) && is_array($_GET['shareType'])) {
486
+            $shareTypes = array_intersect($shareTypes, $_GET['shareType']);
487
+            sort($shareTypes);
488
+        } else if (is_numeric($shareType)) {
489
+            $shareTypes = array_intersect($shareTypes, [(int) $shareType]);
490
+            sort($shareTypes);
491
+        }
492
+
493
+        $this->shareWithGroupOnly = $this->config->getAppValue('core', 'shareapi_only_share_with_group_members', 'no') === 'yes';
494
+        $this->shareeEnumeration = $this->config->getAppValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes') === 'yes';
495
+        $this->limit = (int) $perPage;
496
+        $this->offset = $perPage * ($page - 1);
497
+
498
+        return $this->searchSharees($search, $itemType, $shareTypes, $page, $perPage, $lookup);
499
+    }
500
+
501
+    /**
502
+     * Method to get out the static call for better testing
503
+     *
504
+     * @param string $itemType
505
+     * @return bool
506
+     */
507
+    protected function isRemoteSharingAllowed($itemType) {
508
+        try {
509
+            $backend = Share::getBackend($itemType);
510
+            return $backend->isShareTypeAllowed(Share::SHARE_TYPE_REMOTE);
511
+        } catch (\Exception $e) {
512
+            return false;
513
+        }
514
+    }
515
+
516
+    /**
517
+     * Testable search function that does not need globals
518
+     *
519
+     * @param string $search
520
+     * @param string $itemType
521
+     * @param array $shareTypes
522
+     * @param int $page
523
+     * @param int $perPage
524
+     * @param bool $lookup
525
+     * @return DataResponse
526
+     * @throws OCSBadRequestException
527
+     */
528
+    protected function searchSharees($search, $itemType, array $shareTypes, $page, $perPage, $lookup) {
529
+        // Verify arguments
530
+        if ($itemType === null) {
531
+            throw new OCSBadRequestException('Missing itemType');
532
+        }
533
+
534
+        // Get users
535
+        if (in_array(Share::SHARE_TYPE_USER, $shareTypes)) {
536
+            $this->getUsers($search);
537
+        }
538
+
539
+        // Get groups
540
+        if (in_array(Share::SHARE_TYPE_GROUP, $shareTypes)) {
541
+            $this->getGroups($search);
542
+        }
543
+
544
+        // Get circles
545
+        if (in_array(Share::SHARE_TYPE_CIRCLE, $shareTypes)) {
546
+            $this->getCircles($search);
547
+        }
548
+
549
+
550
+        // Get remote
551
+        $remoteResults = ['results' => [], 'exact' => [], 'exactIdMatch' => false];
552
+        if (in_array(Share::SHARE_TYPE_REMOTE, $shareTypes)) {
553
+            $remoteResults = $this->getRemote($search);
554
+        }
555
+
556
+        // Get emails
557
+        $mailResults = ['results' => [], 'exact' => [], 'exactIdMatch' => false];
558
+        if (in_array(Share::SHARE_TYPE_EMAIL, $shareTypes)) {
559
+            $mailResults = $this->getEmail($search);
560
+        }
561
+
562
+        // Get from lookup server
563
+        if ($lookup) {
564
+            $this->getLookup($search);
565
+        }
566
+
567
+        // if we have a exact match, either for the federated cloud id or for the
568
+        // email address we only return the exact match. It is highly unlikely
569
+        // that the exact same email address and federated cloud id exists
570
+        if ($mailResults['exactIdMatch'] && !$remoteResults['exactIdMatch']) {
571
+            $this->result['emails'] = $mailResults['results'];
572
+            $this->result['exact']['emails'] = $mailResults['exact'];
573
+        } else if (!$mailResults['exactIdMatch'] && $remoteResults['exactIdMatch']) {
574
+            $this->result['remotes'] = $remoteResults['results'];
575
+            $this->result['exact']['remotes'] = $remoteResults['exact'];
576
+        } else {
577
+            $this->result['remotes'] = $remoteResults['results'];
578
+            $this->result['exact']['remotes'] = $remoteResults['exact'];
579
+            $this->result['emails'] = $mailResults['results'];
580
+            $this->result['exact']['emails'] = $mailResults['exact'];
581
+        }
582
+
583
+        $response = new DataResponse($this->result);
584
+
585
+        if (sizeof($this->reachedEndFor) < 3) {
586
+            $response->addHeader('Link', $this->getPaginationLink($page, [
587
+                'search' => $search,
588
+                'itemType' => $itemType,
589
+                'shareType' => $shareTypes,
590
+                'perPage' => $perPage,
591
+            ]));
592
+        }
593
+
594
+        return $response;
595
+    }
596
+
597
+    /**
598
+     * @param string $search
599
+     * @return array
600
+     */
601
+    protected function getEmail($search) {
602
+        $result = ['results' => [], 'exact' => [], 'exactIdMatch' => false];
603
+
604
+        // Search in contacts
605
+        //@todo Pagination missing
606
+        $addressBookContacts = $this->contactsManager->search($search, ['EMAIL', 'FN']);
607
+        $lowerSearch = strtolower($search);
608
+        foreach ($addressBookContacts as $contact) {
609
+            if (isset($contact['EMAIL'])) {
610
+                $emailAddresses = $contact['EMAIL'];
611
+                if (!is_array($emailAddresses)) {
612
+                    $emailAddresses = [$emailAddresses];
613
+                }
614
+                foreach ($emailAddresses as $emailAddress) {
615
+                    $exactEmailMatch = strtolower($emailAddress) === $lowerSearch;
616
+
617
+                    if (isset($contact['isLocalSystemBook'])) {
618
+                        if ($exactEmailMatch) {
619
+                            try {
620
+                                $cloud = $this->cloudIdManager->resolveCloudId($contact['CLOUD'][0]);
621
+                            } catch (\InvalidArgumentException $e) {
622
+                                continue;
623
+                            }
624
+
625
+                            if (!$this->hasUserInResult($cloud->getUser())) {
626
+                                $this->result['exact']['users'][] = [
627
+                                    'label' => $contact['FN'] . " ($emailAddress)",
628
+                                    'value' => [
629
+                                        'shareType' => Share::SHARE_TYPE_USER,
630
+                                        'shareWith' => $cloud->getUser(),
631
+                                    ],
632
+                                ];
633
+                            }
634
+                            return ['results' => [], 'exact' => [], 'exactIdMatch' => true];
635
+                        }
636
+
637
+                        if ($this->shareeEnumeration) {
638
+                            try {
639
+                                $cloud = $this->cloudIdManager->resolveCloudId($contact['CLOUD'][0]);
640
+                            } catch (\InvalidArgumentException $e) {
641
+                                continue;
642
+                            }
643
+
644
+                            if (!$this->hasUserInResult($cloud->getUser())) {
645
+                                $this->result['users'][] = [
646
+                                    'label' => $contact['FN'] . " ($emailAddress)",
647
+                                    'value' => [
648
+                                        'shareType' => Share::SHARE_TYPE_USER,
649
+                                        'shareWith' => $cloud->getUser(),
650
+                                    ],
651
+                                ];
652
+                            }
653
+                        }
654
+                        continue;
655
+                    }
656
+
657
+                    if ($exactEmailMatch || strtolower($contact['FN']) === $lowerSearch) {
658
+                        if ($exactEmailMatch) {
659
+                            $result['exactIdMatch'] = true;
660
+                        }
661
+                        $result['exact'][] = [
662
+                            'label' => $contact['FN'] . " ($emailAddress)",
663
+                            'value' => [
664
+                                'shareType' => Share::SHARE_TYPE_EMAIL,
665
+                                'shareWith' => $emailAddress,
666
+                            ],
667
+                        ];
668
+                    } else {
669
+                        $result['results'][] = [
670
+                            'label' => $contact['FN'] . " ($emailAddress)",
671
+                            'value' => [
672
+                                'shareType' => Share::SHARE_TYPE_EMAIL,
673
+                                'shareWith' => $emailAddress,
674
+                            ],
675
+                        ];
676
+                    }
677
+                }
678
+            }
679
+        }
680
+
681
+        if (!$this->shareeEnumeration) {
682
+            $result['results'] = [];
683
+        }
684
+
685
+        if (!$result['exactIdMatch'] && filter_var($search, FILTER_VALIDATE_EMAIL)) {
686
+            $result['exact'][] = [
687
+                'label' => $search,
688
+                'value' => [
689
+                    'shareType' => Share::SHARE_TYPE_EMAIL,
690
+                    'shareWith' => $search,
691
+                ],
692
+            ];
693
+        }
694
+
695
+        $this->reachedEndFor[] = 'emails';
696
+
697
+        return $result;
698
+    }
699
+
700
+    protected function getLookup($search) {
701
+        $isEnabled = $this->config->getAppValue('files_sharing', 'lookupServerEnabled', 'no');
702
+        $lookupServerUrl = $this->config->getSystemValue('lookup_server', 'https://lookup.nextcloud.com');
703
+        $lookupServerUrl = rtrim($lookupServerUrl, '/');
704
+        $result = [];
705
+
706
+        if($isEnabled === 'yes') {
707
+            try {
708
+                $client = $this->clientService->newClient();
709
+                $response = $client->get(
710
+                    $lookupServerUrl . '/users?search=' . urlencode($search),
711
+                    [
712
+                        'timeout' => 10,
713
+                        'connect_timeout' => 3,
714
+                    ]
715
+                );
716
+
717
+                $body = json_decode($response->getBody(), true);
718
+
719
+                $result = [];
720
+                foreach ($body as $lookup) {
721
+                    $result[] = [
722
+                        'label' => $lookup['federationId'],
723
+                        'value' => [
724
+                            'shareType' => Share::SHARE_TYPE_REMOTE,
725
+                            'shareWith' => $lookup['federationId'],
726
+                        ],
727
+                        'extra' => $lookup,
728
+                    ];
729
+                }
730
+            } catch (\Exception $e) {}
731
+        }
732
+
733
+        $this->result['lookup'] = $result;
734
+    }
735
+
736
+    /**
737
+     * Check if a given user is already part of the result
738
+     *
739
+     * @param string $userId
740
+     * @return bool
741
+     */
742
+    protected function hasUserInResult($userId) {
743
+        foreach ($this->result['exact']['users'] as $result) {
744
+            if ($result['value']['shareWith'] === $userId) {
745
+                return true;
746
+            }
747
+        }
748
+
749
+        foreach ($this->result['users'] as $result) {
750
+            if ($result['value']['shareWith'] === $userId) {
751
+                return true;
752
+            }
753
+        }
754
+
755
+        return false;
756
+    }
757
+
758
+    /**
759
+     * Generates a bunch of pagination links for the current page
760
+     *
761
+     * @param int $page Current page
762
+     * @param array $params Parameters for the URL
763
+     * @return string
764
+     */
765
+    protected function getPaginationLink($page, array $params) {
766
+        if ($this->isV2()) {
767
+            $url = $this->urlGenerator->getAbsoluteURL('/ocs/v2.php/apps/files_sharing/api/v1/sharees') . '?';
768
+        } else {
769
+            $url = $this->urlGenerator->getAbsoluteURL('/ocs/v1.php/apps/files_sharing/api/v1/sharees') . '?';
770
+        }
771
+        $params['page'] = $page + 1;
772
+        $link = '<' . $url . http_build_query($params) . '>; rel="next"';
773
+
774
+        return $link;
775
+    }
776
+
777
+    /**
778
+     * @return bool
779
+     */
780
+    protected function isV2() {
781
+        return $this->request->getScriptName() === '/ocs/v2.php';
782
+    }
783 783
 }
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
 
@@ -347,7 +347,7 @@  discard block
 block discarded – undo
347 347
 							$result['exactIdMatch'] = true;
348 348
 						}
349 349
 						$result['exact'][] = [
350
-							'label' => $contact['FN'] . " ($cloudId)",
350
+							'label' => $contact['FN']." ($cloudId)",
351 351
 							'value' => [
352 352
 								'shareType' => Share::SHARE_TYPE_REMOTE,
353 353
 								'shareWith' => $cloudId,
@@ -356,7 +356,7 @@  discard block
 block discarded – undo
356 356
 						];
357 357
 					} else {
358 358
 						$result['results'][] = [
359
-							'label' => $contact['FN'] . " ($cloudId)",
359
+							'label' => $contact['FN']." ($cloudId)",
360 360
 							'value' => [
361 361
 								'shareType' => Share::SHARE_TYPE_REMOTE,
362 362
 								'shareWith' => $cloudId,
@@ -440,13 +440,13 @@  discard block
 block discarded – undo
440 440
 	public function search($search = '', $itemType = null, $page = 1, $perPage = 200, $shareType = null, $lookup = true) {
441 441
 
442 442
 		// only search for string larger than a given threshold
443
-		$threshold = (int)$this->config->getSystemValue('sharing.minSearchStringLength', 0);
443
+		$threshold = (int) $this->config->getSystemValue('sharing.minSearchStringLength', 0);
444 444
 		if (strlen($search) < $threshold) {
445 445
 			return new DataResponse($this->result);
446 446
 		}
447 447
 
448 448
 		// never return more than the max. number of results configured in the config.php
449
-		$maxResults = (int)$this->config->getSystemValue('sharing.maxAutocompleteResults', 0);
449
+		$maxResults = (int) $this->config->getSystemValue('sharing.maxAutocompleteResults', 0);
450 450
 		if ($maxResults > 0) {
451 451
 			$perPage = min($perPage, $maxResults);
452 452
 		}
@@ -624,7 +624,7 @@  discard block
 block discarded – undo
624 624
 
625 625
 							if (!$this->hasUserInResult($cloud->getUser())) {
626 626
 								$this->result['exact']['users'][] = [
627
-									'label' => $contact['FN'] . " ($emailAddress)",
627
+									'label' => $contact['FN']." ($emailAddress)",
628 628
 									'value' => [
629 629
 										'shareType' => Share::SHARE_TYPE_USER,
630 630
 										'shareWith' => $cloud->getUser(),
@@ -643,7 +643,7 @@  discard block
 block discarded – undo
643 643
 
644 644
 							if (!$this->hasUserInResult($cloud->getUser())) {
645 645
 								$this->result['users'][] = [
646
-									'label' => $contact['FN'] . " ($emailAddress)",
646
+									'label' => $contact['FN']." ($emailAddress)",
647 647
 									'value' => [
648 648
 										'shareType' => Share::SHARE_TYPE_USER,
649 649
 										'shareWith' => $cloud->getUser(),
@@ -659,7 +659,7 @@  discard block
 block discarded – undo
659 659
 							$result['exactIdMatch'] = true;
660 660
 						}
661 661
 						$result['exact'][] = [
662
-							'label' => $contact['FN'] . " ($emailAddress)",
662
+							'label' => $contact['FN']." ($emailAddress)",
663 663
 							'value' => [
664 664
 								'shareType' => Share::SHARE_TYPE_EMAIL,
665 665
 								'shareWith' => $emailAddress,
@@ -667,7 +667,7 @@  discard block
 block discarded – undo
667 667
 						];
668 668
 					} else {
669 669
 						$result['results'][] = [
670
-							'label' => $contact['FN'] . " ($emailAddress)",
670
+							'label' => $contact['FN']." ($emailAddress)",
671 671
 							'value' => [
672 672
 								'shareType' => Share::SHARE_TYPE_EMAIL,
673 673
 								'shareWith' => $emailAddress,
@@ -703,11 +703,11 @@  discard block
 block discarded – undo
703 703
 		$lookupServerUrl = rtrim($lookupServerUrl, '/');
704 704
 		$result = [];
705 705
 
706
-		if($isEnabled === 'yes') {
706
+		if ($isEnabled === 'yes') {
707 707
 			try {
708 708
 				$client = $this->clientService->newClient();
709 709
 				$response = $client->get(
710
-					$lookupServerUrl . '/users?search=' . urlencode($search),
710
+					$lookupServerUrl.'/users?search='.urlencode($search),
711 711
 					[
712 712
 						'timeout' => 10,
713 713
 						'connect_timeout' => 3,
@@ -764,12 +764,12 @@  discard block
 block discarded – undo
764 764
 	 */
765 765
 	protected function getPaginationLink($page, array $params) {
766 766
 		if ($this->isV2()) {
767
-			$url = $this->urlGenerator->getAbsoluteURL('/ocs/v2.php/apps/files_sharing/api/v1/sharees') . '?';
767
+			$url = $this->urlGenerator->getAbsoluteURL('/ocs/v2.php/apps/files_sharing/api/v1/sharees').'?';
768 768
 		} else {
769
-			$url = $this->urlGenerator->getAbsoluteURL('/ocs/v1.php/apps/files_sharing/api/v1/sharees') . '?';
769
+			$url = $this->urlGenerator->getAbsoluteURL('/ocs/v1.php/apps/files_sharing/api/v1/sharees').'?';
770 770
 		}
771 771
 		$params['page'] = $page + 1;
772
-		$link = '<' . $url . http_build_query($params) . '>; rel="next"';
772
+		$link = '<'.$url.http_build_query($params).'>; rel="next"';
773 773
 
774 774
 		return $link;
775 775
 	}
Please login to merge, or discard this patch.
lib/public/Federation/ICloudIdManager.php 1 patch
Indentation   +27 added lines, -27 removed lines patch added patch discarded remove patch
@@ -26,33 +26,33 @@
 block discarded – undo
26 26
  * @since 12.0.0
27 27
  */
28 28
 interface ICloudIdManager {
29
-	/**
30
-	 * @param string $cloudId
31
-	 * @return ICloudId
32
-	 * @throws \InvalidArgumentException
33
-	 *
34
-	 * @since 12.0.0
35
-	 */
36
-	public function resolveCloudId($cloudId);
29
+    /**
30
+     * @param string $cloudId
31
+     * @return ICloudId
32
+     * @throws \InvalidArgumentException
33
+     *
34
+     * @since 12.0.0
35
+     */
36
+    public function resolveCloudId($cloudId);
37 37
 
38
-	/**
39
-	 * Get the cloud id for a remote user
40
-	 *
41
-	 * @param string $user
42
-	 * @param string $remote
43
-	 * @return ICloudId
44
-	 *
45
-	 * @since 12.0.0
46
-	 */
47
-	public function getCloudId($user, $remote);
38
+    /**
39
+     * Get the cloud id for a remote user
40
+     *
41
+     * @param string $user
42
+     * @param string $remote
43
+     * @return ICloudId
44
+     *
45
+     * @since 12.0.0
46
+     */
47
+    public function getCloudId($user, $remote);
48 48
 
49
-	/**
50
-	 * Check if the input is a correctly formatted cloud id
51
-	 *
52
-	 * @param string $cloudId
53
-	 * @return bool
54
-	 *
55
-	 * @since 12.0.0
56
-	 */
57
-	public function isValidCloudId($cloudId);
49
+    /**
50
+     * Check if the input is a correctly formatted cloud id
51
+     *
52
+     * @param string $cloudId
53
+     * @return bool
54
+     *
55
+     * @since 12.0.0
56
+     */
57
+    public function isValidCloudId($cloudId);
58 58
 }
Please login to merge, or discard this patch.