Completed
Push — master ( 9debcc...2df5b9 )
by
unknown
16:34
created

ShareesController::__construct()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 25
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 23
nc 1
nop 11
dl 0
loc 25
rs 8.8571
c 0
b 0
f 0

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
/**
3
 * @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) 2018, 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\IConfig;
34
use OCP\IGroup;
35
use OCP\IGroupManager;
36
use OCP\ILogger;
37
use OCP\IRequest;
38
use OCP\IURLGenerator;
39
use OCP\IUser;
40
use OCP\IUserManager;
41
use OCP\IUserSession;
42
use OCP\Share;
43
use OCA\Files_Sharing\SharingBlacklist;
44
45
class ShareesController extends OCSController {
46
47
	/** @var IGroupManager */
48
	protected $groupManager;
49
50
	/** @var IUserManager */
51
	protected $userManager;
52
53
	/** @var IManager */
54
	protected $contactsManager;
55
56
	/** @var IConfig */
57
	protected $config;
58
59
	/** @var IUserSession */
60
	protected $userSession;
61
62
	/** @var IRequest */
63
	protected $request;
64
65
	/** @var IURLGenerator */
66
	protected $urlGenerator;
67
68
	/** @var ILogger */
69
	protected $logger;
70
71
	/** @var \OCP\Share\IManager */
72
	protected $shareManager;
73
74
	/** @var bool */
75
	protected $shareWithGroupOnly = false;
76
77
	/** @var bool */
78
	protected $shareWithMembershipGroupOnly = false;
79
80
	/** @var bool */
81
	protected $shareeEnumeration = true;
82
83
	/** @var bool */
84
	protected $shareeEnumerationGroupMembers = false;
85
86
	/** @var int */
87
	protected $offset = 0;
88
89
	/** @var int */
90
	protected $limit = 10;
91
92
	/** @var array */
93
	protected $result = [
94
		'exact' => [
95
			'users' => [],
96
			'groups' => [],
97
			'remotes' => [],
98
		],
99
		'users' => [],
100
		'groups' => [],
101
		'remotes' => [],
102
	];
103
104
	protected $reachedEndFor = [];
105
106
	/**
107
	 * @var string
108
	 */
109
	protected $additionalInfoField;
110
111
	/** @var SharingBlacklist */
112
	protected $sharingBlacklist;
113
114
	/**
115
	 * @param IGroupManager $groupManager
116
	 * @param IUserManager $userManager
117
	 * @param IManager $contactsManager
118
	 * @param IConfig $config
119
	 * @param IUserSession $userSession
120
	 * @param IURLGenerator $urlGenerator
121
	 * @param IRequest $request
122
	 * @param ILogger $logger
123
	 * @param \OCP\Share\IManager $shareManager
124
	 */
125
	public function __construct($appName,
126
			IRequest $request,
127
			IGroupManager $groupManager,
128
			IUserManager $userManager,
129
			IManager $contactsManager,
130
			IConfig $config,
131
			IUserSession $userSession,
132
			IURLGenerator $urlGenerator,
133
			ILogger $logger,
134
			\OCP\Share\IManager $shareManager,
135
			SharingBlacklist $sharingBlacklist) {
136
		parent::__construct($appName, $request);
137
138
		$this->groupManager = $groupManager;
139
		$this->userManager = $userManager;
140
		$this->contactsManager = $contactsManager;
141
		$this->config = $config;
142
		$this->userSession = $userSession;
143
		$this->urlGenerator = $urlGenerator;
144
		$this->request = $request;
145
		$this->logger = $logger;
146
		$this->shareManager = $shareManager;
147
		$this->sharingBlacklist = $sharingBlacklist;
148
		$this->additionalInfoField = $this->config->getAppValue('core', 'user_additional_info_field', '');
149
	}
150
151
	/**
152
	 * @param string $search
153
	 */
154
	protected function getUsers($search) {
155
		$this->result['users'] = $this->result['exact']['users'] = $users = [];
156
157
		$userGroups = [];
158
		if ($this->shareWithGroupOnly || $this->shareeEnumerationGroupMembers) {
159
			// Search in all the groups this user is part of
160
			$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...
161
			foreach ($userGroups as $userGroup) {
162
				$usersTmp = $this->groupManager->findUsersInGroup($userGroup, $search, $this->limit, $this->offset);
163
				foreach ($usersTmp as $uid => $user) {
164
					$users[$uid] = $user;
165
				}
166
			}
167
		} else {
168
			// Search in all users
169
			$usersTmp = $this->userManager->find($search, $this->limit, $this->offset);
170
171
			foreach ($usersTmp as $user) {
172
				$users[$user->getUID()] = $user;
173
			}
174
		}
175
176 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...
177
			$this->reachedEndFor[] = 'users';
178
		}
179
180
		$foundUserById = false;
181
		$lowerSearch = \strtolower($search);
182
		foreach ($users as $uid => $user) {
183
			/* @var $user IUser */
184
			$entry = [
185
				'label' => $user->getDisplayName(),
186
				'value' => [
187
					'shareType' => Share::SHARE_TYPE_USER,
188
					'shareWith' => $uid,
189
				],
190
			];
191
			$additionalInfo = $this->getAdditionalUserInfo($user);
192
			if ($additionalInfo !== null) {
193
				$entry['value']['shareWithAdditionalInfo'] = $additionalInfo;
194
			}
195
196
			if (
197
				// Check if the uid is the same
198
				\strtolower($uid) === $lowerSearch
199
				// Check if exact display name
200
				|| \strtolower($user->getDisplayName()) === $lowerSearch
201
				// Check if exact first email
202
				|| \strtolower($user->getEMailAddress()) === $lowerSearch
203
				// Check for exact search term matches (when mail attributes configured as search terms + no enumeration)
204
				|| \in_array($lowerSearch, \array_map('strtolower', $user->getSearchTerms()))) {
205
				if (\strtolower($uid) === $lowerSearch) {
206
					$foundUserById = true;
207
				}
208
				$this->result['exact']['users'][] = $entry;
209
			} else {
210
				$this->result['users'][] = $entry;
211
			}
212
		}
213
214
		if ($this->offset === 0 && !$foundUserById) {
215
			// On page one we try if the search result has a direct hit on the
216
			// user id and if so, we add that to the exact match list
217
			$user = $this->userManager->get($search);
218
			if ($user instanceof IUser) {
219
				$addUser = true;
220
221
				if ($this->shareWithGroupOnly) {
222
					// Only add, if we have a common group
223
					$commonGroups = \array_intersect($userGroups, $this->groupManager->getUserGroupIds($user));
224
					$addUser = !empty($commonGroups);
225
				}
226
227
				if ($addUser) {
228
					$entry = [
229
						'label' => $user->getDisplayName(),
230
						'value' => [
231
							'shareType' => Share::SHARE_TYPE_USER,
232
							'shareWith' => $user->getUID(),
233
						],
234
					];
235
					$additionalInfo = $this->getAdditionalUserInfo($user);
236
					if ($additionalInfo !== null) {
237
						$entry['value']['shareWithAdditionalInfo'] = $additionalInfo;
238
					}
239
					\array_push($this->result['exact']['users'], $entry);
240
				}
241
			}
242
		}
243
244
		if (!$this->shareeEnumeration) {
245
			$this->result['users'] = [];
246
		}
247
	}
248
249
	/**
250
	 * Returns the additional info to display behind the display name as configured.
251
	 *
252
	 * @param IUser $user user for which to retrieve the additional info
253
	 * @return string|null additional info or null if none to be displayed
254
	 */
255 View Code Duplication
	protected function getAdditionalUserInfo(IUser $user) {
256
		if ($this->additionalInfoField === 'email') {
257
			return $user->getEMailAddress();
258
		} elseif ($this->additionalInfoField === 'id') {
259
			return $user->getUID();
260
		}
261
		return null;
262
	}
263
264
	/**
265
	 * @param string $search
266
	 */
267
	protected function getGroups($search) {
268
		$this->result['groups'] = $this->result['exact']['groups'] = [];
269
270
		$groups = $this->groupManager->search($search, $this->limit, $this->offset, 'sharing');
271
		$groupIds = \array_map(function (IGroup $group) {
272
			return $group->getGID();
273
		}, $groups);
274
275 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...
276
			$this->reachedEndFor[] = 'groups';
277
		}
278
279
		$userGroups =  [];
280
		if (!empty($groups) && ($this->shareWithMembershipGroupOnly || $this->shareeEnumerationGroupMembers)) {
281
			// Intersect all the groups that match with the groups this user is a member of
282
			$userGroups = $this->groupManager->getUserGroups($this->userSession->getUser(), 'sharing');
283
			$userGroups = \array_map(function (IGroup $group) {
284
				return $group->getGID();
285
			}, $userGroups);
286
			$groupIds = \array_intersect($groupIds, $userGroups);
287
		}
288
289
		$lowerSearch = \strtolower($search);
290
		foreach ($groups as $group) {
291
			// FIXME: use a more efficient approach
292
			$gid = $group->getGID();
293
			if (!\in_array($gid, $groupIds) || $this->sharingBlacklist->isGroupBlacklisted($group)) {
294
				continue;
295
			}
296
			if (\strtolower($gid) === $lowerSearch || \strtolower($group->getDisplayName()) === $lowerSearch) {
297
				$this->result['exact']['groups'][] = [
298
					'label' => $group->getDisplayName(),
299
					'value' => [
300
						'shareType' => Share::SHARE_TYPE_GROUP,
301
						'shareWith' => $gid,
302
					],
303
				];
304
			} else {
305
				$this->result['groups'][] = [
306
					'label' => $group->getDisplayName(),
307
					'value' => [
308
						'shareType' => Share::SHARE_TYPE_GROUP,
309
						'shareWith' => $gid,
310
					],
311
				];
312
			}
313
		}
314
315
		if ($this->offset === 0 && empty($this->result['exact']['groups'])) {
316
			// On page one we try if the search result has a direct hit on the
317
			// user id and if so, we add that to the exact match list
318
			$group = $this->groupManager->get($search);
319
			if ($group instanceof IGroup && !$this->sharingBlacklist->isGroupBlacklisted($group) &&
320
					(!$this->shareWithMembershipGroupOnly || \in_array($group->getGID(), $userGroups))) {
321
				\array_push($this->result['exact']['groups'], [
322
					'label' => $group->getDisplayName(),
323
					'value' => [
324
						'shareType' => Share::SHARE_TYPE_GROUP,
325
						'shareWith' => $group->getGID(),
326
					],
327
				]);
328
			}
329
		}
330
331
		if (!$this->shareeEnumeration) {
332
			$this->result['groups'] = [];
333
		}
334
	}
335
336
	/**
337
	 * @param string $search
338
	 * @return array possible sharees
339
	 */
340
	protected function getRemote($search) {
341
		$this->result['remotes'] = [];
342
		// Fetch remote search properties from app config
343
		$searchProperties = \explode(',', $this->config->getAppValue('dav', 'remote_search_properties', 'CLOUD,FN'));
344
		// Search in contacts
345
		$addressBookContacts = $this->contactsManager->search($search, $searchProperties, [], $this->limit, $this->offset);
346
		$foundRemoteById = false;
347
		foreach ($addressBookContacts as $contact) {
348
			if (isset($contact['isLocalSystemBook'])) {
349
				// We only want remote users
350
				continue;
351
			}
352
			if (!isset($contact['CLOUD'])) {
353
				// we need a cloud id to setup a remote share
354
				continue;
355
			}
356
357
			// we can have multiple cloud domains, always convert to an array
358
			$cloudIds = $contact['CLOUD'];
359
			if (!\is_array($cloudIds)) {
360
				$cloudIds = [$cloudIds];
361
			}
362
363
			$lowerSearch = \strtolower($search);
364
			foreach ($cloudIds as $cloudId) {
365
				list(, $serverUrl) = $this->splitUserRemote($cloudId);
366
367 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...
368
					$foundRemoteById = true;
369
					// Save this as an exact match and continue with next CLOUD
370
					$this->result['exact']['remotes'][] = [
371
						'label' => $contact['FN'],
372
						'value' => [
373
							'shareType' => Share::SHARE_TYPE_REMOTE,
374
							'shareWith' => $cloudId,
375
							'server' => $serverUrl,
376
						],
377
					];
378
					continue;
379
				}
380
381
				// CLOUD matching is done above
382
				unset($searchProperties['CLOUD']);
383
				foreach ($searchProperties as $property) {
384
					// do we even have this property for this contact/
385
					if (!isset($contact[$property])) {
386
						// Skip this property since our contact doesnt have it
387
						continue;
388
					}
389
					// check if we have a match
390
					$values = $contact[$property];
391
					if (!\is_array($values)) {
392
						$values = [$values];
393
					}
394
					foreach ($values as $value) {
395
						// check if we have an exact match
396 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...
397
							$this->result['exact']['remotes'][] = [
398
								'label' => $contact['FN'],
399
								'value' => [
400
									'shareType' => Share::SHARE_TYPE_REMOTE,
401
									'shareWith' => $cloudId,
402
									'server' => $serverUrl,
403
								],
404
							];
405
406
							// Now skip to next CLOUD
407
							continue 3;
408
						}
409
					}
410
				}
411
412
				// If we get here, we didnt find an exact match, so add to other matches
413
				$this->result['remotes'][] = [
414
					'label' => $contact['FN'],
415
					'value' => [
416
						'shareType' => Share::SHARE_TYPE_REMOTE,
417
						'shareWith' => $cloudId,
418
						'server' => $serverUrl,
419
					],
420
				];
421
			}
422
		}
423
424
		// remove the exact user results if we dont allow autocomplete
425
		if (!$this->shareeEnumeration) {
426
			$this->result['remotes'] = [];
427
		}
428
429
		if (!$foundRemoteById && \substr_count($search, '@') >= 1 && $this->offset === 0
430
			// if an exact local user is found, only keep the remote entry if
431
			// its domain does not matches the trusted domains
432
			// (if it does, it is a user whose local login domain matches the ownCloud
433
			// instance domain)
434
			&& (empty($this->result['exact']['users'])
435
				|| !$this->isInstanceDomain($search))
436
		) {
437
			$this->result['exact']['remotes'][] = [
438
				'label' => $search,
439
				'value' => [
440
					'shareType' => Share::SHARE_TYPE_REMOTE,
441
					'shareWith' => $search,
442
				],
443
			];
444
		}
445
446
		$this->reachedEndFor[] = 'remotes';
447
	}
448
449
	/**
450
	 * split user and remote from federated cloud id
451
	 *
452
	 * @param string $address federated share address
453
	 * @return array [user, remoteURL]
454
	 * @throws \Exception
455
	 */
456
	public function splitUserRemote($address) {
457
		if (\strpos($address, '@') === false) {
458
			throw new \Exception('Invalid Federated Cloud ID');
459
		}
460
461
		// Find the first character that is not allowed in user names
462
		$id = \str_replace('\\', '/', $address);
463
		$posSlash = \strpos($id, '/');
464
		$posColon = \strpos($id, ':');
465
466 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...
467
			$invalidPos = \strlen($id);
468
		} elseif ($posSlash === false) {
469
			$invalidPos = $posColon;
470
		} elseif ($posColon === false) {
471
			$invalidPos = $posSlash;
472
		} else {
473
			$invalidPos = \min($posSlash, $posColon);
474
		}
475
476
		// Find the last @ before $invalidPos
477
		$pos = $lastAtPos = 0;
478 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...
479
			$pos = $lastAtPos;
480
			$lastAtPos = \strpos($id, '@', $pos + 1);
481
		}
482
483 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...
484
			$user = \substr($id, 0, $pos);
485
			$remote = \substr($id, $pos + 1);
486
			$remote = $this->fixRemoteURL($remote);
487
			if (!empty($user) && !empty($remote)) {
488
				return [$user, $remote];
489
			}
490
		}
491
492
		throw new \Exception('Invalid Federated Cloud ID');
493
	}
494
495
	/**
496
	 * Strips away a potential file names and trailing slashes:
497
	 * - http://localhost
498
	 * - http://localhost/
499
	 * - http://localhost/index.php
500
	 * - http://localhost/index.php/s/{shareToken}
501
	 *
502
	 * all return: http://localhost
503
	 *
504
	 * @param string $remote
505
	 * @return string
506
	 */
507 View Code Duplication
	protected function fixRemoteURL($remote) {
508
		$remote = \str_replace('\\', '/', $remote);
509
		if ($fileNamePosition = \strpos($remote, '/index.php')) {
510
			$remote = \substr($remote, 0, $fileNamePosition);
511
		}
512
		$remote = \rtrim($remote, '/');
513
514
		return $remote;
515
	}
516
517
	/**
518
	 * @NoAdminRequired
519
	 * @NoCSRFRequired
520
	 *
521
	 * @param string $search
522
	 * @param string $itemType
523
	 * @param int $page
524
	 * @param int $perPage
525
	 * @return array|DataResponse
526
	 */
527
	public function search($search = '', $itemType = null, $page = 1, $perPage = 200) {
528
		if ($perPage <= 0) {
529
			return [ 'statuscode' => Http::STATUS_BAD_REQUEST,
530
				'message' => 'Invalid perPage argument'];
531
		}
532
		if ($page <= 0) {
533
			return [ 'statuscode' => Http::STATUS_BAD_REQUEST,
534
				'message' => 'Invalid page'];
535
		}
536
537
		$shareTypes = [
538
			Share::SHARE_TYPE_USER,
539
		];
540
541
		if ($this->shareManager->allowGroupSharing()) {
542
			$shareTypes[] = Share::SHARE_TYPE_GROUP;
543
		}
544
545
		$shareTypes[] = Share::SHARE_TYPE_REMOTE;
546
547
		if (isset($_GET['shareType']) && \is_array($_GET['shareType'])) {
548
			$shareTypes = \array_intersect($shareTypes, $_GET['shareType']);
549
			\sort($shareTypes);
550
		} elseif (isset($_GET['shareType']) && \is_numeric($_GET['shareType'])) {
551
			$shareTypes = \array_intersect($shareTypes, [(int) $_GET['shareType']]);
552
			\sort($shareTypes);
553
		}
554
555
		if (\in_array(Share::SHARE_TYPE_REMOTE, $shareTypes) && !$this->isRemoteSharingAllowed($itemType)) {
556
			// Remove remote shares from type array, because it is not allowed.
557
			$shareTypes = \array_diff($shareTypes, [Share::SHARE_TYPE_REMOTE]);
558
		}
559
560
		$this->shareWithGroupOnly = $this->config->getAppValue('core', 'shareapi_only_share_with_group_members', 'no') === 'yes';
561
		$this->shareWithMembershipGroupOnly = $this->config->getAppValue('core', 'shareapi_only_share_with_membership_groups', 'no') === 'yes';
562
		$this->shareeEnumeration = $this->config->getAppValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes') === 'yes';
563
		if ($this->shareeEnumeration) {
564
			$this->shareeEnumerationGroupMembers = $this->config->getAppValue('core', 'shareapi_share_dialog_user_enumeration_group_members', 'no') === 'yes';
565
		} else {
566
			$this->shareeEnumerationGroupMembers = false;
567
		}
568
		$this->limit = (int) $perPage;
569
		$this->offset = $perPage * ($page - 1);
570
571
		return $this->searchSharees($search, $itemType, $shareTypes, $page, $perPage);
572
	}
573
574
	/**
575
	 * Method to get out the static call for better testing
576
	 *
577
	 * @param string $itemType
578
	 * @return bool
579
	 */
580
	protected function isRemoteSharingAllowed($itemType) {
581
		try {
582
			$backend = Share::getBackend($itemType);
583
			return $backend->isShareTypeAllowed(Share::SHARE_TYPE_REMOTE);
584
		} catch (\Exception $e) {
585
			return false;
586
		}
587
	}
588
589
	/**
590
	 * Testable search function that does not need globals
591
	 *
592
	 * @param string $search
593
	 * @param string $itemType
594
	 * @param array $shareTypes
595
	 * @param int $page
596
	 * @param int $perPage
597
	 * @return DataResponse|array
598
	 */
599
	protected function searchSharees($search, $itemType, array $shareTypes, $page, $perPage) {
600
		// Verify arguments
601
		if ($itemType === null) {
602
			return [ 'statuscode' => Http::STATUS_BAD_REQUEST,
603
				'message' => 'Missing itemType'];
604
		}
605
606
		// Get users
607
		if (\in_array(Share::SHARE_TYPE_USER, $shareTypes)) {
608
			$this->getUsers($search);
609
		}
610
611
		// Get groups
612
		if (\in_array(Share::SHARE_TYPE_GROUP, $shareTypes)) {
613
			$this->getGroups($search);
614
		}
615
616
		// Get remote
617
		if (\in_array(Share::SHARE_TYPE_REMOTE, $shareTypes)) {
618
			$this->getRemote($search);
619
		}
620
621
		$response = new DataResponse(['data' => $this->result]);
622
623
		if (\sizeof($this->reachedEndFor) < 3) {
624
			$response->addHeader('Link', $this->getPaginationLink($page, [
625
				'search' => $search,
626
				'itemType' => $itemType,
627
				'shareType' => $shareTypes,
628
				'perPage' => $perPage,
629
			]));
630
		}
631
632
		return $response;
633
	}
634
635
	/**
636
	 * Generates a bunch of pagination links for the current page
637
	 *
638
	 * @param int $page Current page
639
	 * @param array $params Parameters for the URL
640
	 * @return string
641
	 */
642
	protected function getPaginationLink($page, array $params) {
643
		if ($this->isV2()) {
644
			$url = $this->urlGenerator->getAbsoluteURL('/ocs/v2.php/apps/files_sharing/api/v1/sharees') . '?';
645
		} else {
646
			$url = $this->urlGenerator->getAbsoluteURL('/ocs/v1.php/apps/files_sharing/api/v1/sharees') . '?';
647
		}
648
		$params['page'] = $page + 1;
649
		$link = '<' . $url . \http_build_query($params) . '>; rel="next"';
650
651
		return $link;
652
	}
653
654
	/**
655
	 * @return bool
656
	 */
657
	protected function isV2() {
658
		return $this->request->getScriptName() === '/ocs/v2.php';
659
	}
660
661
	/**
662
	 * Checks whether the given target's domain part matches one of the server's
663
	 * trusted domain entries
664
	 *
665
	 * @param string $target target
666
	 * @return true if one match was found, false otherwise
667
	 */
668
	protected function isInstanceDomain($target) {
669
		if (\strpos($target, '/') !== false) {
670
			// not a proper email-like format with domain name
671
			return false;
672
		}
673
		$parts = \explode('@', $target);
674
		if (\count($parts) === 1) {
675
			// no "@" sign
676
			return false;
677
		}
678
		$domainName = $parts[\count($parts) - 1];
679
		$trustedDomains = $this->config->getSystemValue('trusted_domains', []);
680
681
		return \in_array($domainName, $trustedDomains, true);
682
	}
683
}
684