Passed
Push — master ( 31cc07...56b08c )
by Joas
24:48 queued 11s
created

MailPlugin::isCurrentUser()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 2
nc 2
nop 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * @copyright Copyright (c) 2017 Arthur Schiwon <[email protected]>
4
 *
5
 * @author Arthur Schiwon <[email protected]>
6
 * @author Christoph Wurst <[email protected]>
7
 * @author Joas Schilling <[email protected]>
8
 * @author Julius Härtl <[email protected]>
9
 * @author Tobia De Koninck <[email protected]>
10
 *
11
 * @license GNU AGPL version 3 or any later version
12
 *
13
 * This program is free software: you can redistribute it and/or modify
14
 * it under the terms of the GNU Affero General Public License as
15
 * published by the Free Software Foundation, either version 3 of the
16
 * License, or (at your option) any later version.
17
 *
18
 * This program is distributed in the hope that it will be useful,
19
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21
 * GNU Affero General Public License for more details.
22
 *
23
 * You should have received a copy of the GNU Affero General Public License
24
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
25
 *
26
 */
27
28
namespace OC\Collaboration\Collaborators;
29
30
use OC\KnownUser\KnownUserService;
31
use OCP\Collaboration\Collaborators\ISearchPlugin;
32
use OCP\Collaboration\Collaborators\ISearchResult;
33
use OCP\Collaboration\Collaborators\SearchResultType;
34
use OCP\Contacts\IManager;
35
use OCP\Federation\ICloudId;
36
use OCP\Federation\ICloudIdManager;
37
use OCP\IConfig;
38
use OCP\IGroupManager;
39
use OCP\IUser;
40
use OCP\IUserSession;
41
use OCP\Share\IShare;
42
43
class MailPlugin implements ISearchPlugin {
44
	/* @var bool */
45
	protected $shareWithGroupOnly;
46
	/* @var bool */
47
	protected $shareeEnumeration;
48
	/* @var bool */
49
	protected $shareeEnumerationInGroupOnly;
50
	/* @var bool */
51
	protected $shareeEnumerationPhone;
52
	/* @var bool */
53
	protected $shareeEnumerationFullMatch;
54
55
	/** @var IManager */
56
	private $contactsManager;
57
	/** @var ICloudIdManager */
58
	private $cloudIdManager;
59
	/** @var IConfig */
60
	private $config;
61
62
	/** @var IGroupManager */
63
	private $groupManager;
64
	/** @var KnownUserService */
65
	private $knownUserService;
66
	/** @var IUserSession */
67
	private $userSession;
68
69
	public function __construct(IManager $contactsManager,
70
								ICloudIdManager $cloudIdManager,
71
								IConfig $config,
72
								IGroupManager $groupManager,
73
								KnownUserService $knownUserService,
74
								IUserSession $userSession) {
75
		$this->contactsManager = $contactsManager;
76
		$this->cloudIdManager = $cloudIdManager;
77
		$this->config = $config;
78
		$this->groupManager = $groupManager;
79
		$this->knownUserService = $knownUserService;
80
		$this->userSession = $userSession;
81
82
		$this->shareeEnumeration = $this->config->getAppValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes') === 'yes';
83
		$this->shareWithGroupOnly = $this->config->getAppValue('core', 'shareapi_only_share_with_group_members', 'no') === 'yes';
84
		$this->shareeEnumerationInGroupOnly = $this->shareeEnumeration && $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_group', 'no') === 'yes';
85
		$this->shareeEnumerationPhone = $this->shareeEnumeration && $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_phone', 'no') === 'yes';
86
		$this->shareeEnumerationFullMatch = $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_full_match', 'yes') === 'yes';
87
	}
88
89
	/**
90
	 * @param $search
91
	 * @param $limit
92
	 * @param $offset
93
	 * @param ISearchResult $searchResult
94
	 * @return bool
95
	 * @since 13.0.0
96
	 */
97
	public function search($search, $limit, $offset, ISearchResult $searchResult) {
98
		$currentUserId = $this->userSession->getUser()->getUID();
99
100
		$result = $userResults = ['wide' => [], 'exact' => []];
101
		$userType = new SearchResultType('users');
102
		$emailType = new SearchResultType('emails');
103
104
		// Search in contacts
105
		$addressBookContacts = $this->contactsManager->search($search, ['EMAIL', 'FN'], ['limit' => $limit, 'offset' => $offset]);
106
		$lowerSearch = strtolower($search);
107
		foreach ($addressBookContacts as $contact) {
108
			if (isset($contact['EMAIL'])) {
109
				$emailAddresses = $contact['EMAIL'];
110
				if (\is_string($emailAddresses)) {
111
					$emailAddresses = [$emailAddresses];
112
				}
113
				foreach ($emailAddresses as $type => $emailAddress) {
114
					$displayName = $emailAddress;
115
					$emailAddressType = null;
116
					if (\is_array($emailAddress)) {
117
						$emailAddressData = $emailAddress;
118
						$emailAddress = $emailAddressData['value'];
119
						$emailAddressType = $emailAddressData['type'];
120
					}
121
					if (isset($contact['FN'])) {
122
						$displayName = $contact['FN'] . ' (' . $emailAddress . ')';
123
					}
124
					$exactEmailMatch = strtolower($emailAddress) === $lowerSearch;
125
126
					if (isset($contact['isLocalSystemBook'])) {
127
						if ($this->shareWithGroupOnly) {
128
							/*
129
							 * Check if the user may share with the user associated with the e-mail of the just found contact
130
							 */
131
							$userGroups = $this->groupManager->getUserGroupIds($this->userSession->getUser());
132
							$found = false;
133
							foreach ($userGroups as $userGroup) {
134
								if ($this->groupManager->isInGroup($contact['UID'], $userGroup)) {
135
									$found = true;
136
									break;
137
								}
138
							}
139
							if (!$found) {
140
								continue;
141
							}
142
						}
143
						if ($exactEmailMatch && $this->shareeEnumerationFullMatch) {
144
							try {
145
								$cloud = $this->cloudIdManager->resolveCloudId($contact['CLOUD'][0]);
146
							} catch (\InvalidArgumentException $e) {
147
								continue;
148
							}
149
150
							if (!$this->isCurrentUser($cloud) && !$searchResult->hasResult($userType, $cloud->getUser())) {
151
								$singleResult = [[
152
									'label' => $displayName,
153
									'uuid' => $contact['UID'],
154
									'name' => $contact['FN'],
155
									'value' => [
156
										'shareType' => IShare::TYPE_USER,
157
										'shareWith' => $cloud->getUser(),
158
									],
159
									'shareWithDisplayNameUnique' => !empty($emailAddress) ? $emailAddress : $cloud->getUser()
160
161
								]];
162
								$searchResult->addResultSet($userType, [], $singleResult);
163
								$searchResult->markExactIdMatch($emailType);
164
							}
165
							return false;
166
						}
167
168
						if ($this->shareeEnumeration) {
169
							try {
170
								$cloud = $this->cloudIdManager->resolveCloudId($contact['CLOUD'][0]);
171
							} catch (\InvalidArgumentException $e) {
172
								continue;
173
							}
174
175
							$addToWide = !($this->shareeEnumerationInGroupOnly || $this->shareeEnumerationPhone);
176
							if (!$addToWide && $this->shareeEnumerationPhone && $this->knownUserService->isKnownToUser($currentUserId, $contact['UID'])) {
177
								$addToWide = true;
178
							}
179
180
							if (!$addToWide && $this->shareeEnumerationInGroupOnly) {
181
								$addToWide = false;
182
								$userGroups = $this->groupManager->getUserGroupIds($this->userSession->getUser());
183
								foreach ($userGroups as $userGroup) {
184
									if ($this->groupManager->isInGroup($contact['UID'], $userGroup)) {
185
										$addToWide = true;
186
										break;
187
									}
188
								}
189
							}
190
							if ($addToWide && !$this->isCurrentUser($cloud) && !$searchResult->hasResult($userType, $cloud->getUser())) {
191
								$userResults['wide'][] = [
192
									'label' => $displayName,
193
									'uuid' => $contact['UID'],
194
									'name' => $contact['FN'],
195
									'value' => [
196
										'shareType' => IShare::TYPE_USER,
197
										'shareWith' => $cloud->getUser(),
198
									],
199
									'shareWithDisplayNameUnique' => !empty($emailAddress) ? $emailAddress : $cloud->getUser()
200
								];
201
								continue;
202
							}
203
						}
204
						continue;
205
					}
206
207
					if ($exactEmailMatch
208
						|| (isset($contact['FN']) && strtolower($contact['FN']) === $lowerSearch)) {
209
						if ($exactEmailMatch) {
210
							$searchResult->markExactIdMatch($emailType);
211
						}
212
						$result['exact'][] = [
213
							'label' => $displayName,
214
							'uuid' => $contact['UID'],
215
							'name' => $contact['FN'],
216
							'type' => $emailAddressType ?? '',
217
							'value' => [
218
								'shareType' => IShare::TYPE_EMAIL,
219
								'shareWith' => $emailAddress,
220
							],
221
						];
222
					} else {
223
						$result['wide'][] = [
224
							'label' => $displayName,
225
							'uuid' => $contact['UID'],
226
							'name' => $contact['FN'],
227
							'type' => $emailAddressType ?? '',
228
							'value' => [
229
								'shareType' => IShare::TYPE_EMAIL,
230
								'shareWith' => $emailAddress,
231
							],
232
						];
233
					}
234
				}
235
			}
236
		}
237
238
		$reachedEnd = true;
239
		if (!$this->shareeEnumeration) {
240
			$result['wide'] = [];
241
			$userResults['wide'] = [];
242
		} else {
243
			$reachedEnd = (count($result['wide']) < $offset + $limit) &&
244
				(count($userResults['wide']) < $offset + $limit);
245
246
			$result['wide'] = array_slice($result['wide'], $offset, $limit);
247
			$userResults['wide'] = array_slice($userResults['wide'], $offset, $limit);
248
		}
249
250
251
		if (!$searchResult->hasExactIdMatch($emailType) && filter_var($search, FILTER_VALIDATE_EMAIL)) {
252
			$result['exact'][] = [
253
				'label' => $search,
254
				'uuid' => $search,
255
				'value' => [
256
					'shareType' => IShare::TYPE_EMAIL,
257
					'shareWith' => $search,
258
				],
259
			];
260
		}
261
262
		if (!empty($userResults['wide'])) {
263
			$searchResult->addResultSet($userType, $userResults['wide'], []);
264
		}
265
		$searchResult->addResultSet($emailType, $result['wide'], $result['exact']);
266
267
		return !$reachedEnd;
268
	}
269
270
	public function isCurrentUser(ICloudId $cloud): bool {
271
		$currentUser = $this->userSession->getUser();
272
		return $currentUser instanceof IUser ? $currentUser->getUID() === $cloud->getUser() : false;
273
	}
274
}
275