Skip to content

Commit f603358

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 2e9f3b3 commit f603358

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
@@ -20,6 +20,7 @@
2020
use OCP\IUserSession;
2121
use OCP\Mail\IEmailValidator;
2222
use OCP\Share\IShare;
23+
use RuntimeException;
2324

2425
class MailPlugin implements ISearchPlugin {
2526
protected bool $shareWithGroupOnly;
@@ -75,69 +76,118 @@ public function search($search, $limit, $offset, ISearchResult $searchResult): b
7576
$currentUserId = $this->userSession->getUser()->getUID();
7677
$userGroups = null;
7778

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

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

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

113-
if (isset($contact['FN'])) {
114-
$displayName = $contact['FN'] . ' (' . $emailAddress . ')';
134+
$contactGroups = $this->groupManager->getUserGroupIds($contactUser);
135+
if ($this->shareWithGroupOnly) {
136+
$userGroups ??= $this->groupManager->getUserGroupIds($this->userSession->getUser());
137+
if (array_intersect($contactGroups, array_diff($userGroups, $this->shareWithGroupOnlyExcludeGroupsList)) === []) {
138+
continue;
139+
}
115140
}
116-
$exactEmailMatch = strtolower($emailAddress) === $lowerSearch;
117141

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

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

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

139-
if ($this->shareType === IShare::TYPE_USER && !$this->isCurrentUser($cloud) && !$searchResult->hasResult($userType, $cloud->getUser())) {
140-
$singleResult = [[
177+
if (!$addToWide && $this->shareeEnumerationPhone && $this->knownUserService->isKnownToUser($currentUserId, $contact['UID'])) {
178+
$addToWide = true;
179+
}
180+
181+
if (!$addToWide && $this->shareeEnumerationInGroupOnly) {
182+
$userGroups ??= $this->groupManager->getUserGroupIds($this->userSession->getUser());
183+
$addToWide = array_intersect($contactGroups, $userGroups) !== [];
184+
}
185+
186+
if ($addToWide && !$this->isCurrentUser($cloud) && !$searchResult->hasResult($type, $cloud->getUser())) {
187+
if ($count++ >= $limit) {
188+
$hasMore = true;
189+
} else {
190+
$results['wide'][] = [
141191
'label' => $displayName,
142192
'uuid' => $contact['UID'] ?? $emailAddress,
143193
'name' => $contact['FN'] ?? $displayName,
@@ -146,116 +196,69 @@ public function search($search, $limit, $offset, ISearchResult $searchResult): b
146196
'shareWith' => $cloud->getUser(),
147197
],
148198
'shareWithDisplayNameUnique' => !empty($emailAddress) ? $emailAddress : $cloud->getUser()
149-
150-
]];
151-
$searchResult->addResultSet($userType, [], $singleResult);
152-
$searchResult->markExactIdMatch($userType);
199+
];
153200
}
154-
return false;
155201
}
202+
}
156203

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

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

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

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

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

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

258-
return !$reachedEnd;
261+
return $hasMore;
259262
}
260263

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

0 commit comments

Comments
 (0)