From 7d207b2c76158139b62ff45a58cbe753d29b1f0b Mon Sep 17 00:00:00 2001 From: Robert Stepanek Date: Fri, 10 Jan 2025 10:28:25 +0100 Subject: [PATCH] xapian_wrap.cpp: support phrase search for email addresses Quotes signify phrase search in free text queries but were not supported in email queries since we supported querying for sub-parts of an email address. This reintroduces phrase search for email queries. --- .../tiny-tests/JMAPEmail/email_query_emailaddress | 15 +++++++++++++++ imap/xapian_wrap.cpp | 14 ++++++++++++-- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/cassandane/tiny-tests/JMAPEmail/email_query_emailaddress b/cassandane/tiny-tests/JMAPEmail/email_query_emailaddress index 249553edf6..95f75ba770 100644 --- a/cassandane/tiny-tests/JMAPEmail/email_query_emailaddress +++ b/cassandane/tiny-tests/JMAPEmail/email_query_emailaddress @@ -180,6 +180,21 @@ EOF }, { to => decode('utf-8', 'Dømi <*@*dømi.fo>'), wantIds => [$ids[4]], + }, { + to => 'foo.bar@xxx.example.com', + wantIds => [$ids[0]], + }, { + to => 'Jon Doe ', + wantIds => [$ids[0]], + }, { + to => '"foo.bar@xxx.example.com"', + wantIds => [$ids[0]], + }, { + to => '"Jon Doe "', + wantIds => [$ids[0]], + }, { + to => '"foo*@*example.com"', + wantIds => [], }); foreach (@tests) { diff --git a/imap/xapian_wrap.cpp b/imap/xapian_wrap.cpp index d537bb6111..c6051ddf6f 100644 --- a/imap/xapian_wrap.cpp +++ b/imap/xapian_wrap.cpp @@ -1632,6 +1632,7 @@ static Xapian::Query *query_new_email(const xapian_db_t *db, const char *searchstr) { std::string prefix(get_term_prefix(partnum)); + bool is_phrase_query = false; unsigned queryflags = Xapian::QueryParser::FLAG_PHRASE | Xapian::QueryParser::FLAG_WILDCARD; @@ -1641,6 +1642,14 @@ static Xapian::Query *query_new_email(const xapian_db_t *db, db->parser->set_stemming_strategy(Xapian::QueryParser::STEM_NONE); std::string str = Xapian::Unicode::tolower(searchstr); + // Remove enclosing phrase search quotes, if any. + if (str.size() >= 2 && str.front() == '"' && str.back() == '"') { + str.erase(str.end() - 1); + str.erase(str.begin()); + if (str.empty()) return NULL; + is_phrase_query = true; + } + size_t atsign_pos = str.find('@'); if (atsign_pos == std::string::npos || atsign_pos == str.length() - 1) { @@ -1660,7 +1669,8 @@ static Xapian::Query *query_new_email(const xapian_db_t *db, email.append(str, 0, atsign_pos); } - if (!email.empty() && email[email.length() - 1] == '*') { + if (!is_phrase_query && + !email.empty() && email[email.length() - 1] == '*') { wildcard_localpart = true; email.erase(email.length() - 1, 1); } @@ -1682,7 +1692,7 @@ static Xapian::Query *query_new_email(const xapian_db_t *db, bool subdomain_only = false; std::string domain(str, atsign_pos + 1); - if (domain.length() && domain[0] == '*') { + if (!is_phrase_query && domain.length() && domain[0] == '*') { wildcard_domain = true; domain.erase(0, 1); if (domain.length() && domain[0] == '.') {