Skip to content

Commit 8521e55

Browse files
committed
fix(MailPlugin): Stop applying the offset twice and the limit per wide/exact
Signed-off-by: provokateurin <kate@provokateurin.de>
1 parent a6a2060 commit 8521e55

2 files changed

Lines changed: 140 additions & 137 deletions

File tree

lib/private/Collaboration/Collaborators/MailPlugin.php

Lines changed: 129 additions & 126 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
use OCP\IUserSession;
2222
use OCP\Mail\IEmailValidator;
2323
use OCP\Share\IShare;
24+
use RuntimeException;
2425

2526
class MailPlugin implements ISearchPlugin {
2627
protected bool $shareWithGroupOnly;
@@ -76,66 +77,114 @@ public function search($search, $limit, $offset, ISearchResult $searchResult): b
7677
$currentUserId = $this->userSession->getUser()->getUID();
7778
$userGroups = $this->groupManager->getUserGroupIds($this->userSession->getUser());
7879

79-
$result = $userResults = ['wide' => [], 'exact' => []];
80-
$userType = new SearchResultType('users');
81-
$emailType = new SearchResultType('emails');
80+
$hasMore = false;
81+
$count = 0;
82+
$results = ['wide' => [], 'exact' => []];
83+
$type = match ($this->shareType) {
84+
IShare::TYPE_USER => new SearchResultType('users'),
85+
IShare::TYPE_EMAIL => new SearchResultType('emails'),
86+
default => throw new RuntimeException(),
87+
};
8288

8389
// Search in contacts
8490
$addressBookContacts = $this->contactsManager->search(
8591
$search,
8692
['EMAIL', 'FN'],
8793
[
88-
'limit' => $limit,
94+
// We request one more, so we can check if there are more results available
95+
'limit' => $limit + 1,
8996
'offset' => $offset,
9097
'enumeration' => $this->shareeEnumeration,
9198
'fullmatch' => $this->shareeEnumerationFullMatch,
9299
]
93100
);
94101
$lowerSearch = strtolower($search);
95102
foreach ($addressBookContacts as $contact) {
96-
if (isset($contact['EMAIL'])) {
97-
$emailAddresses = $contact['EMAIL'];
98-
if (\is_string($emailAddresses)) {
99-
$emailAddresses = [$emailAddresses];
103+
if (!isset($contact['EMAIL'])) {
104+
continue;
105+
}
106+
107+
$emailAddresses = $contact['EMAIL'];
108+
if (\is_string($emailAddresses)) {
109+
$emailAddresses = [$emailAddresses];
110+
}
111+
foreach ($emailAddresses as $emailAddress) {
112+
$displayName = $emailAddress;
113+
$emailAddressType = null;
114+
if (\is_array($emailAddress)) {
115+
$emailAddressData = $emailAddress;
116+
$emailAddress = $emailAddressData['value'];
117+
$emailAddressType = $emailAddressData['type'];
100118
}
101-
foreach ($emailAddresses as $type => $emailAddress) {
102-
$displayName = $emailAddress;
103-
$emailAddressType = null;
104-
if (\is_array($emailAddress)) {
105-
$emailAddressData = $emailAddress;
106-
$emailAddress = $emailAddressData['value'];
107-
$emailAddressType = $emailAddressData['type'];
108-
}
109119

110-
if (!filter_var($emailAddress, FILTER_VALIDATE_EMAIL)) {
120+
if (!filter_var($emailAddress, FILTER_VALIDATE_EMAIL)) {
121+
continue;
122+
}
123+
124+
if (isset($contact['FN'])) {
125+
$displayName = $contact['FN'] . ' (' . $emailAddress . ')';
126+
}
127+
$exactEmailMatch = strtolower($emailAddress) === $lowerSearch;
128+
129+
if (isset($contact['isLocalSystemBook'])) {
130+
$contactUser = $this->userManager->get($contact['UID']);
131+
if ($contactUser === null) {
111132
continue;
112133
}
113134

114-
if (isset($contact['FN'])) {
115-
$displayName = $contact['FN'] . ' (' . $emailAddress . ')';
135+
$contactGroups = $this->groupManager->getUserGroupIds($contactUser);
136+
if ($this->shareWithGroupOnly && array_intersect($contactGroups, array_diff($userGroups, $this->shareWithGroupOnlyExcludeGroupsList)) === []) {
137+
continue;
116138
}
117-
$exactEmailMatch = strtolower($emailAddress) === $lowerSearch;
118139

119-
if (isset($contact['isLocalSystemBook'])) {
120-
$contactUser = $this->userManager->get($contact['UID']);
121-
if ($contactUser === null) {
140+
if ($exactEmailMatch && $this->shareeEnumerationFullMatch) {
141+
try {
142+
$cloud = $this->cloudIdManager->resolveCloudId($contact['CLOUD'][0] ?? '');
143+
} catch (\InvalidArgumentException $e) {
122144
continue;
123145
}
124-
$contactGroups = $this->groupManager->getUserGroupIds($contactUser);
125146

126-
if ($this->shareWithGroupOnly && array_intersect($contactGroups, array_diff($userGroups, $this->shareWithGroupOnlyExcludeGroupsList)) === []) {
127-
continue;
147+
if ($this->shareType === IShare::TYPE_USER && !$this->isCurrentUser($cloud) && !$searchResult->hasResult($type, $cloud->getUser())) {
148+
$singleResult = [[
149+
'label' => $displayName,
150+
'uuid' => $contact['UID'] ?? $emailAddress,
151+
'name' => $contact['FN'] ?? $displayName,
152+
'value' => [
153+
'shareType' => IShare::TYPE_USER,
154+
'shareWith' => $cloud->getUser(),
155+
],
156+
'shareWithDisplayNameUnique' => !empty($emailAddress) ? $emailAddress : $cloud->getUser()
157+
]];
158+
$searchResult->addResultSet($type, [], $singleResult);
159+
$searchResult->markExactIdMatch($type);
128160
}
161+
return false;
162+
}
129163

130-
if ($exactEmailMatch && $this->shareeEnumerationFullMatch) {
131-
try {
132-
$cloud = $this->cloudIdManager->resolveCloudId($contact['CLOUD'][0] ?? '');
133-
} catch (\InvalidArgumentException $e) {
164+
if ($this->shareeEnumeration && $this->shareType === IShare::TYPE_USER) {
165+
try {
166+
if (!isset($contact['CLOUD'])) {
134167
continue;
135168
}
169+
$cloud = $this->cloudIdManager->resolveCloudId($contact['CLOUD'][0] ?? '');
170+
} catch (\InvalidArgumentException $e) {
171+
continue;
172+
}
173+
$addToWide = !($this->shareeEnumerationInGroupOnly || $this->shareeEnumerationPhone);
174+
175+
if (!$addToWide && $this->shareeEnumerationPhone && $this->knownUserService->isKnownToUser($currentUserId, $contact['UID'])) {
176+
$addToWide = true;
177+
}
136178

137-
if ($this->shareType === IShare::TYPE_USER && !$this->isCurrentUser($cloud) && !$searchResult->hasResult($userType, $cloud->getUser())) {
138-
$singleResult = [[
179+
if (!$addToWide && $this->shareeEnumerationInGroupOnly) {
180+
$addToWide = array_intersect($contactGroups, $userGroups) !== [];
181+
}
182+
183+
if ($addToWide && !$this->isCurrentUser($cloud) && !$searchResult->hasResult($type, $cloud->getUser())) {
184+
if ($count++ >= $limit) {
185+
$hasMore = true;
186+
} else {
187+
$results['wide'][] = [
139188
'label' => $displayName,
140189
'uuid' => $contact['UID'] ?? $emailAddress,
141190
'name' => $contact['FN'] ?? $displayName,
@@ -144,115 +193,69 @@ public function search($search, $limit, $offset, ISearchResult $searchResult): b
144193
'shareWith' => $cloud->getUser(),
145194
],
146195
'shareWithDisplayNameUnique' => !empty($emailAddress) ? $emailAddress : $cloud->getUser()
147-
148-
]];
149-
$searchResult->addResultSet($userType, [], $singleResult);
150-
$searchResult->markExactIdMatch($userType);
196+
];
151197
}
152-
return false;
153198
}
199+
}
154200

155-
if ($this->shareeEnumeration) {
156-
try {
157-
if (!isset($contact['CLOUD'])) {
158-
continue;
159-
}
160-
$cloud = $this->cloudIdManager->resolveCloudId($contact['CLOUD'][0] ?? '');
161-
} catch (\InvalidArgumentException $e) {
162-
continue;
163-
}
164-
165-
$addToWide = !($this->shareeEnumerationInGroupOnly || $this->shareeEnumerationPhone);
166-
if (!$addToWide && $this->shareeEnumerationPhone && $this->knownUserService->isKnownToUser($currentUserId, $contact['UID'])) {
167-
$addToWide = true;
168-
}
201+
continue;
202+
}
169203

170-
if (!$addToWide && $this->shareeEnumerationInGroupOnly) {
171-
$addToWide = array_intersect($contactGroups, $userGroups) !== [];
172-
}
173-
if ($addToWide && !$this->isCurrentUser($cloud) && !$searchResult->hasResult($userType, $cloud->getUser())) {
174-
if ($this->shareType === IShare::TYPE_USER) {
175-
$userResults['wide'][] = [
176-
'label' => $displayName,
177-
'uuid' => $contact['UID'] ?? $emailAddress,
178-
'name' => $contact['FN'] ?? $displayName,
179-
'value' => [
180-
'shareType' => IShare::TYPE_USER,
181-
'shareWith' => $cloud->getUser(),
182-
],
183-
'shareWithDisplayNameUnique' => !empty($emailAddress) ? $emailAddress : $cloud->getUser()
184-
];
185-
}
186-
continue;
187-
}
188-
}
189-
continue;
190-
}
204+
if ($this->shareType !== IShare::TYPE_EMAIL) {
205+
continue;
206+
}
191207

192-
if ($this->shareType !== IShare::TYPE_EMAIL) {
193-
continue;
208+
if ($count++ >= $limit) {
209+
$hasMore = true;
210+
} elseif ($exactEmailMatch || (isset($contact['FN']) && strtolower($contact['FN']) === $lowerSearch)) {
211+
if ($exactEmailMatch) {
212+
$searchResult->markExactIdMatch($type);
194213
}
195214

196-
if ($exactEmailMatch
197-
|| (isset($contact['FN']) && strtolower($contact['FN']) === $lowerSearch)) {
198-
if ($exactEmailMatch) {
199-
$searchResult->markExactIdMatch($emailType);
200-
}
201-
$result['exact'][] = [
202-
'label' => $displayName,
203-
'uuid' => $contact['UID'] ?? $emailAddress,
204-
'name' => $contact['FN'] ?? $displayName,
205-
'type' => $emailAddressType ?? '',
206-
'value' => [
207-
'shareType' => IShare::TYPE_EMAIL,
208-
'shareWith' => $emailAddress,
209-
],
210-
];
211-
} else {
212-
$result['wide'][] = [
213-
'label' => $displayName,
214-
'uuid' => $contact['UID'] ?? $emailAddress,
215-
'name' => $contact['FN'] ?? $displayName,
216-
'type' => $emailAddressType ?? '',
217-
'value' => [
218-
'shareType' => IShare::TYPE_EMAIL,
219-
'shareWith' => $emailAddress,
220-
],
221-
];
222-
}
215+
$results['exact'][] = [
216+
'label' => $displayName,
217+
'uuid' => $contact['UID'] ?? $emailAddress,
218+
'name' => $contact['FN'] ?? $displayName,
219+
'type' => $emailAddressType ?? '',
220+
'value' => [
221+
'shareType' => IShare::TYPE_EMAIL,
222+
'shareWith' => $emailAddress,
223+
],
224+
];
225+
} else {
226+
$results['wide'][] = [
227+
'label' => $displayName,
228+
'uuid' => $contact['UID'] ?? $emailAddress,
229+
'name' => $contact['FN'] ?? $displayName,
230+
'type' => $emailAddressType ?? '',
231+
'value' => [
232+
'shareType' => IShare::TYPE_EMAIL,
233+
'shareWith' => $emailAddress,
234+
],
235+
];
223236
}
224237
}
225238
}
226239

227-
$reachedEnd = true;
228-
if ($this->shareeEnumeration) {
229-
$reachedEnd = (count($result['wide']) < $offset + $limit)
230-
&& (count($userResults['wide']) < $offset + $limit);
231-
232-
$result['wide'] = array_slice($result['wide'], $offset, $limit);
233-
$userResults['wide'] = array_slice($userResults['wide'], $offset, $limit);
234-
}
235-
236240
if ($this->shareType === IShare::TYPE_EMAIL
237-
&& !$searchResult->hasExactIdMatch($emailType) && $this->emailValidator->isValid($search)) {
238-
$result['exact'][] = [
239-
'label' => $search,
240-
'uuid' => $search,
241-
'value' => [
242-
'shareType' => IShare::TYPE_EMAIL,
243-
'shareWith' => $search,
244-
],
245-
];
241+
&& !$searchResult->hasExactIdMatch($type) && $this->emailValidator->isValid($search)) {
242+
if ($count++ >= $limit) {
243+
$hasMore = true;
244+
} else {
245+
$results['exact'][] = [
246+
'label' => $search,
247+
'uuid' => $search,
248+
'value' => [
249+
'shareType' => IShare::TYPE_EMAIL,
250+
'shareWith' => $search,
251+
],
252+
];
253+
}
246254
}
247255

248-
if ($this->shareType === IShare::TYPE_USER && !empty($userResults['wide'])) {
249-
$searchResult->addResultSet($userType, $userResults['wide'], []);
250-
}
251-
if ($this->shareType === IShare::TYPE_EMAIL) {
252-
$searchResult->addResultSet($emailType, $result['wide'], $result['exact']);
253-
}
256+
$searchResult->addResultSet($type, $results['wide'], $results['exact']);
254257

255-
return !$reachedEnd;
258+
return $hasMore;
256259
}
257260

258261
public function isCurrentUser(ICloudId $cloud): bool {

0 commit comments

Comments
 (0)