Completed
Push — master ( 92dbcd...cb88d7 )
by Jörn Friedrich
12:20
created

ShareesController::getRemote()   D

Complexity

Conditions 18
Paths 92

Size

Total Lines 112
Code Lines 58

Duplication

Lines 26
Ratio 23.21 %

Importance

Changes 0
Metric Value
cc 18
eloc 58
nc 92
nop 1
dl 26
loc 112
rs 4.7996
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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

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

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

function doesNotAcceptNull(stdClass $x) { }

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

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

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
146
			foreach ($userGroups as $userGroup) {
147
				$usersTmp = $this->groupManager->findUsersInGroup($userGroup, $search, $this->limit, $this->offset);
148
				foreach ($usersTmp as $uid => $user) {
149
					$users[$uid] = $user;
150
				}
151
			}
152
		} else {
153
			// Search in all users
154
			$usersTmp = $this->userManager->find($search, $this->limit, $this->offset);
155
156
			foreach ($usersTmp as $user) {
157
				$users[$user->getUID()] = $user;
158
			}
159
		}
160
161 View Code Duplication
		if (!$this->shareeEnumeration || sizeof($users) < $this->limit) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
162
			$this->reachedEndFor[] = 'users';
163
		}
164
165
		$foundUserById = false;
166
		$lowerSearch = strtolower($search);
167
		foreach ($users as $uid => $user) {
168
			/* @var $user IUser */
169
			if (strtolower($uid) === $lowerSearch || strtolower($user->getDisplayName()) === $lowerSearch || strtolower($user->getEMailAddress()) === $lowerSearch) {
170
				if (strtolower($uid) === $lowerSearch) {
171
					$foundUserById = true;
172
				}
173
				$this->result['exact']['users'][] = [
174
					'label' => $user->getDisplayName(),
175
					'value' => [
176
						'shareType' => Share::SHARE_TYPE_USER,
177
						'shareWith' => $uid,
178
					],
179
				];
180 View Code Duplication
			} else {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
181
				$this->result['users'][] = [
182
					'label' => $user->getDisplayName(),
183
					'value' => [
184
						'shareType' => Share::SHARE_TYPE_USER,
185
						'shareWith' => $uid,
186
					],
187
				];
188
			}
189
		}
190
191
		if ($this->offset === 0 && !$foundUserById) {
192
			// On page one we try if the search result has a direct hit on the
193
			// user id and if so, we add that to the exact match list
194
			$user = $this->userManager->get($search);
195
			if ($user instanceof IUser) {
196
				$addUser = true;
197
198
				if ($this->shareWithGroupOnly) {
199
					// Only add, if we have a common group
200
					$commonGroups = array_intersect($userGroups, $this->groupManager->getUserGroupIds($user));
201
					$addUser = !empty($commonGroups);
202
				}
203
204
				if ($addUser) {
205
					array_push($this->result['exact']['users'], [
206
						'label' => $user->getDisplayName(),
207
						'value' => [
208
							'shareType' => Share::SHARE_TYPE_USER,
209
							'shareWith' => $user->getUID(),
210
						],
211
					]);
212
				}
213
			}
214
		}
215
216
		if (!$this->shareeEnumeration) {
217
			$this->result['users'] = [];
218
		}
219
	}
220
221
	/**
222
	 * @param string $search
223
	 */
224
	protected function getGroups($search) {
225
		$this->result['groups'] = $this->result['exact']['groups'] = [];
226
227
		$groups = $this->groupManager->search($search, $this->limit, $this->offset, 'sharing');
228
		$groupIds = array_map(function (IGroup $group) { return $group->getGID(); }, $groups);
229
230 View Code Duplication
		if (!$this->shareeEnumeration || sizeof($groups) < $this->limit) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
231
			$this->reachedEndFor[] = 'groups';
232
		}
233
234
		$userGroups =  [];
235
		if (!empty($groups) && ($this->shareWithGroupOnly || $this->shareeEnumerationGroupMembers)) {
236
			// Intersect all the groups that match with the groups this user is a member of
237
			$userGroups = $this->groupManager->getUserGroups($this->userSession->getUser(), 'sharing');
238
			$userGroups = array_map(function (IGroup $group) { return $group->getGID(); }, $userGroups);
239
			$groupIds = array_intersect($groupIds, $userGroups);
240
		}
241
242
		$lowerSearch = strtolower($search);
243
		foreach ($groups as $group) {
244
			// FIXME: use a more efficient approach
245
			$gid = $group->getGID();
246
			if (!in_array($gid, $groupIds)) {
247
				continue;
248
			}
249
			if (strtolower($gid) === $lowerSearch || strtolower($group->getDisplayName()) === $lowerSearch) {
250
				$this->result['exact']['groups'][] = [
251
					'label' => $group->getDisplayName(),
252
					'value' => [
253
						'shareType' => Share::SHARE_TYPE_GROUP,
254
						'shareWith' => $gid,
255
					],
256
				];
257 View Code Duplication
			} else {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
258
				$this->result['groups'][] = [
259
					'label' => $group->getDisplayName(),
260
					'value' => [
261
						'shareType' => Share::SHARE_TYPE_GROUP,
262
						'shareWith' => $gid,
263
					],
264
				];
265
			}
266
		}
267
268
		if ($this->offset === 0 && empty($this->result['exact']['groups'])) {
269
			// On page one we try if the search result has a direct hit on the
270
			// user id and if so, we add that to the exact match list
271
			$group = $this->groupManager->get($search);
272
			if ($group instanceof IGroup && (!$this->shareWithGroupOnly || in_array($group->getGID(), $userGroups))) {
273
				array_push($this->result['exact']['groups'], [
274
					'label' => $group->getDisplayName(),
275
					'value' => [
276
						'shareType' => Share::SHARE_TYPE_GROUP,
277
						'shareWith' => $group->getGID(),
278
					],
279
				]);
280
			}
281
		}
282
283
		if (!$this->shareeEnumeration) {
284
			$this->result['groups'] = [];
285
		}
286
	}
287
288
	/**
289
	 * @param string $search
290
	 * @return array possible sharees
291
	 */
292
	protected function getRemote($search) {
293
		$this->result['remotes'] = [];
294
		// Fetch remote search properties from app config
295
		$searchProperties = explode(',', $this->config->getAppValue('dav', 'remote_search_properties', 'CLOUD,FN'));
296
		// Search in contacts
297
		$addressBookContacts = $this->contactsManager->search($search, $searchProperties, [], $this->limit, $this->offset);
298
		$foundRemoteById = false;
299
		foreach ($addressBookContacts as $contact) {
300
301
			if (isset($contact['isLocalSystemBook'])) {
302
				// We only want remote users
303
				continue;
304
			}
305
			if (!isset($contact['CLOUD'])) {
306
				// we need a cloud id to setup a remote share
307
				continue;
308
			}
309
310
			// we can have multiple cloud domains, always convert to an array
311
			$cloudIds = $contact['CLOUD'];
312
			if (!is_array($cloudIds)) {
313
				$cloudIds = [$cloudIds];
314
			}
315
316
			$lowerSearch = strtolower($search);
317
			foreach ($cloudIds as $cloudId) {
318
				list(, $serverUrl) = $this->splitUserRemote($cloudId);
319
320
321 View Code Duplication
				if (strtolower($cloudId) === $lowerSearch) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
322
					$foundRemoteById = true;
323
					// Save this as an exact match and continue with next CLOUD
324
					$this->result['exact']['remotes'][] = [
325
						'label' => $contact['FN'],
326
						'value' => [
327
							'shareType' => Share::SHARE_TYPE_REMOTE,
328
							'shareWith' => $cloudId,
329
							'server' => $serverUrl,
330
						],
331
					];
332
					continue;
333
				}
334
335
				// CLOUD matching is done above
336
				unset($searchProperties['CLOUD']);
337
				foreach($searchProperties as $property) {
338
					// do we even have this property for this contact/
339
					if(!isset($contact[$property])) {
340
						// Skip this property since our contact doesnt have it
341
						continue;
342
					}
343
					// check if we have a match
344
					$values = $contact[$property];
345
					if(!is_array($values)) {
346
						$values = [$values];
347
					}
348
					foreach($values as $value) {
349
						// check if we have an exact match
350 View Code Duplication
						if(strtolower($value) === $lowerSearch) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
351
							$this->result['exact']['remotes'][] = [
352
								'label' => $contact['FN'],
353
								'value' => [
354
									'shareType' => Share::SHARE_TYPE_REMOTE,
355
									'shareWith' => $cloudId,
356
									'server' => $serverUrl,
357
								],
358
							];
359
360
							// Now skip to next CLOUD
361
							continue 3;
362
						}
363
					}
364
				}
365
366
				// If we get here, we didnt find an exact match, so add to other matches
367
				$this->result['remotes'][] = [
368
					'label' => $contact['FN'],
369
					'value' => [
370
						'shareType' => Share::SHARE_TYPE_REMOTE,
371
						'shareWith' => $cloudId,
372
						'server' => $serverUrl,
373
					],
374
				];
375
376
			}
377
		}
378
379
		// remove the exact user results if we dont allow autocomplete
380
		if (!$this->shareeEnumeration) {
381
			$this->result['remotes'] = [];
382
		}
383
384
385
		if (!$foundRemoteById && substr_count($search, '@') >= 1 && $this->offset === 0
386
			// if an exact local user is found, only keep the remote entry if
387
			// its domain does not matches the trusted domains
388
			// (if it does, it is a user whose local login domain matches the ownCloud
389
			// instance domain)
390
			&& (empty($this->result['exact']['users'])
391
				|| !$this->isInstanceDomain($search))
392
		) {
393
			$this->result['exact']['remotes'][] = [
394
				'label' => $search,
395
				'value' => [
396
					'shareType' => Share::SHARE_TYPE_REMOTE,
397
					'shareWith' => $search,
398
				],
399
			];
400
		}
401
402
		$this->reachedEndFor[] = 'remotes';
403
	}
404
405
	/**
406
	 * split user and remote from federated cloud id
407
	 *
408
	 * @param string $address federated share address
409
	 * @return array [user, remoteURL]
410
	 * @throws \Exception
411
	 */
412
	public function splitUserRemote($address) {
413
		if (strpos($address, '@') === false) {
414
			throw new \Exception('Invalid Federated Cloud ID');
415
		}
416
417
		// Find the first character that is not allowed in user names
418
		$id = str_replace('\\', '/', $address);
419
		$posSlash = strpos($id, '/');
420
		$posColon = strpos($id, ':');
421
422 View Code Duplication
		if ($posSlash === false && $posColon === false) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
423
			$invalidPos = strlen($id);
424
		} else if ($posSlash === false) {
425
			$invalidPos = $posColon;
426
		} else if ($posColon === false) {
427
			$invalidPos = $posSlash;
428
		} else {
429
			$invalidPos = min($posSlash, $posColon);
430
		}
431
432
		// Find the last @ before $invalidPos
433
		$pos = $lastAtPos = 0;
434 View Code Duplication
		while ($lastAtPos !== false && $lastAtPos <= $invalidPos) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
435
			$pos = $lastAtPos;
436
			$lastAtPos = strpos($id, '@', $pos + 1);
437
		}
438
439 View Code Duplication
		if ($pos !== false) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
440
			$user = substr($id, 0, $pos);
441
			$remote = substr($id, $pos + 1);
442
			$remote = $this->fixRemoteURL($remote);
443
			if (!empty($user) && !empty($remote)) {
444
				return [$user, $remote];
445
			}
446
		}
447
448
		throw new \Exception('Invalid Federated Cloud ID');
449
	}
450
451
	/**
452
	 * Strips away a potential file names and trailing slashes:
453
	 * - http://localhost
454
	 * - http://localhost/
455
	 * - http://localhost/index.php
456
	 * - http://localhost/index.php/s/{shareToken}
457
	 *
458
	 * all return: http://localhost
459
	 *
460
	 * @param string $remote
461
	 * @return string
462
	 */
463 View Code Duplication
	protected function fixRemoteURL($remote) {
464
		$remote = str_replace('\\', '/', $remote);
465
		if ($fileNamePosition = strpos($remote, '/index.php')) {
466
			$remote = substr($remote, 0, $fileNamePosition);
467
		}
468
		$remote = rtrim($remote, '/');
469
470
		return $remote;
471
	}
472
473
	/**
474
	 * @NoAdminRequired
475
	 * @NoCSRFRequired
476
	 *
477
	 * @param string $search
478
	 * @param string $itemType
479
	 * @param int $page
480
	 * @param int $perPage
481
	 * @return array|DataResponse
482
	 */
483
	public function search($search = '', $itemType = null, $page = 1, $perPage = 200) {
484
485
		if ($perPage <= 0) {
486
			return [ 'statuscode' => Http::STATUS_BAD_REQUEST,
487
				'message' => 'Invalid perPage argument'];
488
		}
489
		if ($page <= 0) {
490
			return [ 'statuscode' => Http::STATUS_BAD_REQUEST,
491
				'message' => 'Invalid page'];
492
		}
493
494
		$shareTypes = [
495
			Share::SHARE_TYPE_USER,
496
		];
497
498
		if ($this->shareManager->allowGroupSharing()) {
499
			$shareTypes[] = Share::SHARE_TYPE_GROUP;
500
		}
501
502
		$shareTypes[] = Share::SHARE_TYPE_REMOTE;
503
504
		if (isset($_GET['shareType']) && is_array($_GET['shareType'])) {
505
			$shareTypes = array_intersect($shareTypes, $_GET['shareType']);
506
			sort($shareTypes);
507
508
		} else if (isset($_GET['shareType']) && is_numeric($_GET['shareType'])) {
509
			$shareTypes = array_intersect($shareTypes, [(int) $_GET['shareType']]);
510
			sort($shareTypes);
511
		}
512
513
		if (in_array(Share::SHARE_TYPE_REMOTE, $shareTypes) && !$this->isRemoteSharingAllowed($itemType)) {
514
			// Remove remote shares from type array, because it is not allowed.
515
			$shareTypes = array_diff($shareTypes, [Share::SHARE_TYPE_REMOTE]);
516
		}
517
518
		$this->shareWithGroupOnly = $this->config->getAppValue('core', 'shareapi_only_share_with_group_members', 'no') === 'yes';
519
		$this->shareeEnumeration = $this->config->getAppValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes') === 'yes';
520
		if ($this->shareeEnumeration) {
521
			$this->shareeEnumerationGroupMembers = $this->config->getAppValue('core', 'shareapi_share_dialog_user_enumeration_group_members', 'no') === 'yes';
522
		} else {
523
			$this->shareeEnumerationGroupMembers = false;
524
		}
525
		$this->limit = (int) $perPage;
526
		$this->offset = $perPage * ($page - 1);
527
528
		return $this->searchSharees($search, $itemType, $shareTypes, $page, $perPage);
529
	}
530
531
	/**
532
	 * Method to get out the static call for better testing
533
	 *
534
	 * @param string $itemType
535
	 * @return bool
536
	 */
537
	protected function isRemoteSharingAllowed($itemType) {
538
		try {
539
			$backend = Share::getBackend($itemType);
540
			return $backend->isShareTypeAllowed(Share::SHARE_TYPE_REMOTE);
541
		} catch (\Exception $e) {
542
			return false;
543
		}
544
	}
545
546
	/**
547
	 * Testable search function that does not need globals
548
	 *
549
	 * @param string $search
550
	 * @param string $itemType
551
	 * @param array $shareTypes
552
	 * @param int $page
553
	 * @param int $perPage
554
	 * @return DataResponse|array
555
	 */
556
	protected function searchSharees($search, $itemType, array $shareTypes, $page, $perPage) {
557
		// Verify arguments
558
		if ($itemType === null) {
559
			return [ 'statuscode' => Http::STATUS_BAD_REQUEST,
560
				'message' => 'Missing itemType'];
561
		}
562
563
		// Get users
564
		if (in_array(Share::SHARE_TYPE_USER, $shareTypes)) {
565
			$this->getUsers($search);
566
		}
567
568
		// Get groups
569
		if (in_array(Share::SHARE_TYPE_GROUP, $shareTypes)) {
570
			$this->getGroups($search);
571
		}
572
573
		// Get remote
574
		if (in_array(Share::SHARE_TYPE_REMOTE, $shareTypes)) {
575
			$this->getRemote($search);
576
		}
577
578
		$response = new DataResponse(['data' => $this->result]);
579
580
		if (sizeof($this->reachedEndFor) < 3) {
581
			$response->addHeader('Link', $this->getPaginationLink($page, [
582
				'search' => $search,
583
				'itemType' => $itemType,
584
				'shareType' => $shareTypes,
585
				'perPage' => $perPage,
586
			]));
587
		}
588
589
		return $response;
590
	}
591
592
	/**
593
	 * Generates a bunch of pagination links for the current page
594
	 *
595
	 * @param int $page Current page
596
	 * @param array $params Parameters for the URL
597
	 * @return string
598
	 */
599
	protected function getPaginationLink($page, array $params) {
600
		if ($this->isV2()) {
601
			$url = $this->urlGenerator->getAbsoluteURL('/ocs/v2.php/apps/files_sharing/api/v1/sharees') . '?';
602
		} else {
603
			$url = $this->urlGenerator->getAbsoluteURL('/ocs/v1.php/apps/files_sharing/api/v1/sharees') . '?';
604
		}
605
		$params['page'] = $page + 1;
606
		$link = '<' . $url . http_build_query($params) . '>; rel="next"';
607
608
		return $link;
609
	}
610
611
	/**
612
	 * @return bool
613
	 */
614
	protected function isV2() {
615
		return $this->request->getScriptName() === '/ocs/v2.php';
616
	}
617
618
	/**
619
	 * Checks whether the given target's domain part matches one of the server's
620
	 * trusted domain entries
621
	 *
622
	 * @param string $target target
623
	 * @return true if one match was found, false otherwise
624
	 */
625
	protected function isInstanceDomain($target) {
626
		if (strpos($target, '/') !== false) {
627
			// not a proper email-like format with domain name
628
			return false;
629
		}
630
		$parts = explode('@', $target);
631
		if (count($parts) === 1) {
632
			// no "@" sign
633
			return false;
634
		}
635
		$domainName = $parts[count($parts) - 1];
636
		$trustedDomains = $this->config->getSystemValue('trusted_domains', []);
637
638
		return in_array($domainName, $trustedDomains, true);
639
	}
640
}
641