From b12aada52d682c45aa1e66e13dc448dc894aee79 Mon Sep 17 00:00:00 2001 From: "Danilo M." Date: Thu, 1 Oct 2026 10:31:51 +0200 Subject: fix: double a quote in a search term, as notmuch expects SearchTerm::quote() escaped an embedded double quote with a backslash. notmuch has no backslash escape inside a quoted term: it doubles the quote, and a backslash-escaped one ENDS the term. Selected text holding a quote therefore let the rest of the selection reach the query as syntax. Measured on a throwaway index: under a `tag:nomatch and` scope, the backslash form of `x " or tag:inbox or id:"` matched every message, while the doubled form matched none. quote() now doubles the quote and leaves a backslash alone, since notmuch reads it literally. Truncation still runs first, so a cut cannot split a doubled pair. Co-Authored-By: Claude Opus 5.5 --- src/searchterm.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'src/searchterm.cpp') diff --git a/src/searchterm.cpp b/src/searchterm.cpp index 6bfb521..16936df 100644 --- a/src/searchterm.cpp +++ b/src/searchterm.cpp @@ -34,10 +34,13 @@ QString quote(const QString &value) if (cleaned.size() > kMaxValueLength) cleaned.truncate(kMaxValueLength); - // Backslashes first: escaping the quotes first would then escape the - // backslashes this step adds, doubling them. - cleaned.replace(QLatin1Char('\\'), QStringLiteral("\\\\")); - cleaned.replace(QLatin1Char('"'), QStringLiteral("\\\"")); + // notmuch escapes a quote inside a quoted term by DOUBLING it and has no + // backslash escape at all. A backslash-escaped quote ENDS the term, so + // `subject:"x \" or tag:inbox or id:"` let a selection walk out of the + // query it was meant to narrow: measured matching every message under a + // `tag:nomatch and` scope, where the doubled form matched none. A + // backslash is literal to notmuch and is left as it is. + cleaned.replace(QLatin1Char('"'), QStringLiteral("\"\"")); return QLatin1Char('"') + cleaned + QLatin1Char('"'); } -- cgit v1.2.3