Skip to content

Commit fde8ace

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 94b2908 commit fde8ace

2 files changed

Lines changed: 144 additions & 141 deletions

File tree

lib/private/Collaboration/Collaborators/MailPlugin.php

Lines changed: 133 additions & 130 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,69 +77,118 @@ public function search($search, $limit, $offset, ISearchResult $searchResult): b
7677
$currentUserId = $this->userSession->getUser()->getUID();
7778
$userGroups = null;
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'];
118+
}
119+
120+
if (!filter_var($emailAddress, FILTER_VALIDATE_EMAIL)) {
121+
continue;
100122
}
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-
}
109123

110-
if (!filter_var($emailAddress, FILTER_VALIDATE_EMAIL)) {
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) {
137+
$userGroups ??= $this->groupManager->getUserGroupIds($this->userSession->getUser());
138+
if (array_intersect($contactGroups, array_diff($userGroups, $this->shareWithGroupOnlyExcludeGroupsList)) === []) {
139+
continue;
140+
}
116141
}
117-
$exactEmailMatch = strtolower($emailAddress) === $lowerSearch;
118142

119-
if (isset($contact['isLocalSystemBook'])) {
120-
$contactUser = $this->userManager->get($contact['UID']);
121-
if ($contactUser === null) {
143+
if ($exactEmailMatch && $this->shareeEnumerationFullMatch) {
144+
try {
145+
$cloud = $this->cloudIdManager->resolveCloudId($contact['CLOUD'][0] ?? '');
146+
} catch (\InvalidArgumentException $e) {
122147
continue;
123148
}
124-
$contactGroups = $this->groupManager->getUserGroupIds($contactUser);
125149

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

133-
if ($exactEmailMatch && $this->shareeEnumerationFullMatch) {
134-
try {
135-
$cloud = $this->cloudIdManager->resolveCloudId($contact['CLOUD'][0] ?? '');
136-
} catch (\InvalidArgumentException $e) {
167+
if ($this->shareeEnumeration && $this->shareType === IShare::TYPE_USER) {
168+
try {
169+
if (!isset($contact['CLOUD'])) {
137170
continue;
138171
}
172+
$cloud = $this->cloudIdManager->resolveCloudId($contact['CLOUD'][0] ?? '');
173+
} catch (\InvalidArgumentException $e) {
174+
continue;
175+
}
176+
$addToWide = !($this->shareeEnumerationInGroupOnly || $this->shareeEnumerationPhone);
139177

140-
if ($this->shareType === IShare::TYPE_USER && !$this->isCurrentUser($cloud) && !$searchResult->hasResult($userType, $cloud->getUser())) {
141-
$singleResult = [[
178+
if (!$addToWide && $this->shareeEnumerationPhone && $this->knownUserService->isKnownToUser($currentUserId, $contact['UID'])) {
179+
$addToWide = true;
180+
}
181+
182+
if (!$addToWide && $this->shareeEnumerationInGroupOnly) {
183+
$userGroups ??= $this->groupManager->getUserGroupIds($this->userSession->getUser());
184+
$addToWide = array_intersect($contactGroups, $userGroups) !== [];
185+
}
186+
187+
if ($addToWide && !$this->isCurrentUser($cloud) && !$searchResult->hasResult($type, $cloud->getUser())) {
188+
if ($count++ >= $limit) {
189+
$hasMore = true;
190+
} else {
191+
$results['wide'][] = [
142192
'label' => $displayName,
143193
'uuid' => $contact['UID'] ?? $emailAddress,
144194
'name' => $contact['FN'] ?? $displayName,
@@ -147,116 +197,69 @@ public function search($search, $limit, $offset, ISearchResult $searchResult): b
147197
'shareWith' => $cloud->getUser(),
148198
],
149199
'shareWithDisplayNameUnique' => !empty($emailAddress) ? $emailAddress : $cloud->getUser()
150-
151-
]];
152-
$searchResult->addResultSet($userType, [], $singleResult);
153-
$searchResult->markExactIdMatch($userType);
200+
];
154201
}
155-
return false;
156202
}
203+
}
157204

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

173-
if (!$addToWide && $this->shareeEnumerationInGroupOnly) {
174-
$userGroups ??= $this->groupManager->getUserGroupIds($this->userSession->getUser());
175-
$addToWide = array_intersect($contactGroups, $userGroups) !== [];
176-
}
177-
if ($addToWide && !$this->isCurrentUser($cloud) && !$searchResult->hasResult($userType, $cloud->getUser())) {
178-
if ($this->shareType === IShare::TYPE_USER) {
179-
$userResults['wide'][] = [
180-
'label' => $displayName,
181-
'uuid' => $contact['UID'] ?? $emailAddress,
182-
'name' => $contact['FN'] ?? $displayName,
183-
'value' => [
184-
'shareType' => IShare::TYPE_USER,
185-
'shareWith' => $cloud->getUser(),
186-
],
187-
'shareWithDisplayNameUnique' => !empty($emailAddress) ? $emailAddress : $cloud->getUser()
188-
];
189-
}
190-
continue;
191-
}
192-
}
193-
continue;
194-
}
208+
if ($this->shareType !== IShare::TYPE_EMAIL) {
209+
continue;
210+
}
195211

196-
if ($this->shareType !== IShare::TYPE_EMAIL) {
197-
continue;
212+
if ($count++ >= $limit) {
213+
$hasMore = true;
214+
} elseif ($exactEmailMatch || (isset($contact['FN']) && strtolower($contact['FN']) === $lowerSearch)) {
215+
if ($exactEmailMatch) {
216+
$searchResult->markExactIdMatch($type);
198217
}
199218

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

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

252-
if ($this->shareType === IShare::TYPE_USER && !empty($userResults['wide'])) {
253-
$searchResult->addResultSet($userType, $userResults['wide'], []);
254-
}
255-
if ($this->shareType === IShare::TYPE_EMAIL) {
256-
$searchResult->addResultSet($emailType, $result['wide'], $result['exact']);
257-
}
260+
$searchResult->addResultSet($type, $results['wide'], $results['exact']);
258261

259-
return !$reachedEnd;
262+
return $hasMore;
260263
}
261264

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

0 commit comments

Comments
 (0)