Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions lib/Db/MessageMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -928,9 +928,10 @@ public function findIdsByQuery(Mailbox $mailbox, SearchQuery $query, string $sor
}
// createParameter
if ($uids !== null) {
// In the case of body+subject search we need a combination of both results,
// thus the orWhere in every other case andWhere should do the job.
if (!empty($query->getSubjects())) {
// In anyof mode or when subjects are also searched, body results must be
// OR'd with other criteria so a match in any one field is sufficient.
// In allof mode without subjects, AND is correct: all criteria must match.
if (!empty($query->getSubjects()) || $query->getMatch() === 'anyof') {
$textOrs[] = $qb->expr()->in('m.uid', $qb->createParameter('uids'));
} else {
$select->andWhere(
Expand Down
7 changes: 6 additions & 1 deletion lib/Service/Search/MailSearch.php
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ public function findMessagesGlobally(
* @throws ServiceException
*/
private function getIdsLocally(Account $account, Mailbox $mailbox, SearchQuery $query, string $sortOrder, ?int $limit): array {
if (empty($query->getBodies())) {
if (empty($query->getBodies()) || !$account->getMailAccount()->getSearchBody()) {
return $this->messageMapper->findIdsByQuery($mailbox, $query, $sortOrder, $limit);
}

Expand All @@ -159,6 +159,11 @@ private function getIdsLocally(Account $account, Mailbox $mailbox, SearchQuery $
$mailbox,
$query
);

if (empty($fromImap)) {
return $this->messageMapper->findIdsByQuery($mailbox, $query, $sortOrder, $limit);
}

return $this->messageMapper->findIdsByQuery($mailbox, $query, $sortOrder, $limit, $fromImap);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Find and examine the MessageMapper::findIdsByQuery implementation

# Locate MessageMapper and extract the findIdsByQuery method
ast-grep --pattern $'class MessageMapper {
  $$$
  findIdsByQuery($$$) {
    $$$
  }
  $$$
}'

# Also search for how the $fromImap parameter is used
rg -nP -A 20 'function findIdsByQuery\(' --glob '**/MessageMapper.php'

Repository: nextcloud/mail

Length of output: 1595


🏁 Script executed:

# Read the complete findIdsByQuery method from MessageMapper.php
sed -n '807,900p' lib/Db/MessageMapper.php

Repository: nextcloud/mail

Length of output: 4427


🏁 Script executed:

# Continue reading to see how $uids is handled and the rest of the method
sed -n '900,1000p' lib/Db/MessageMapper.php

Repository: nextcloud/mail

Length of output: 3947


🏁 Script executed:

# Continue reading to see parameter binding
sed -n '995,1020p' lib/Db/MessageMapper.php

Repository: nextcloud/mail

Length of output: 920


🏁 Script executed:

# Check the MailSearch.php file around line 167 to see how findIdsByQuery is called
sed -n '150,180p' lib/Service/Search/MailSearch.php

Repository: nextcloud/mail

Length of output: 1146


The merge uses AND logic instead of OR when no subjects are specified, breaking the intended behavior.

When $uids (IMAP body results) is provided without subject criteria (lines 923-930), the UIDs are merged using andWhere(), creating AND logic instead of OR. This breaks the expected behavior for anyof match mode.

Example: A search for "test" in body + from="user@example.com" with anyof mode should return messages from that email OR containing "test" in the body. Instead, it returns only messages that match BOTH criteria—filtering out messages that match body criteria but have a different sender.

The condition at line 927 adds UIDs to $textOrs (OR logic) only when subjects exist; otherwise it uses andWhere() (AND logic). For consistency with anyof semantics, UIDs should be added to $textOrs in all cases when body search is performed with other header criteria, not just when subjects are specified.

}

Expand Down
2 changes: 1 addition & 1 deletion src/components/SearchMessages.vue
Original file line number Diff line number Diff line change
Expand Up @@ -484,7 +484,7 @@ export default {
}

this.match = 'anyof'
this.searchInMessageBody = this.searchBody ? this.query : null
this.searchInMessageBody = this.query
this.searchInSubject = this.query
this.searchInFrom = [{ email: this.query, label: this.query }]
this.searchInTo = [{ email: this.query, label: this.query }]
Expand Down
Loading