Fix hasAttachment query for binary attachments

This commit is contained in:
onevcat 2026-02-09 10:29:53 +09:00
parent 69c33bd178
commit 313e6e41ad
2 changed files with 50 additions and 5 deletions

View file

@ -8,7 +8,8 @@ use crate::message::{
index::{MAX_MESSAGE_PARTS, extractors::VisitTextArchived},
metadata::{
ArchivedMessageMetadata, ArchivedMetadataHeaderName, ArchivedMetadataHeaderValue,
ArchivedMetadataPartType, DecodedPartContent, MESSAGE_RECEIVED_MASK, MetadataHeaderName,
ArchivedMetadataPartType, DecodedPartContent, MESSAGE_HAS_ATTACHMENT,
MESSAGE_RECEIVED_MASK, MetadataHeaderName,
},
};
use mail_parser::{DateTime, decoders::html::html_to_text, parsers::fields::thread::thread_name};
@ -339,10 +340,11 @@ impl ArchivedMessageMetadata {
#[cfg(feature = "test_mode")]
document.set_unknown_language(default_language);
let has_attachment =
document.has_field(&(SearchField::Email(EmailSearchField::Attachment)));
// Email.hasAttachment should reflect the presence of attachments, not whether attachment
// *text* was indexed.
let has_attachment = (self.rcvd_attach.to_native() & MESSAGE_HAS_ATTACHMENT) != 0;
document.index_bool(EmailSearchField::HasAttachment, has_attachment);
document
}
}

View file

@ -88,8 +88,51 @@ pub async fn test(params: &mut JMAPTest, insert: bool) {
MAX_THREADS
);
// Wait for indexing to complete
// Regression test: Email/query filter {hasAttachment:true} should match emails with binary
// attachments (not just those with attachment text indexed).
let sent_at = now();
let binary_anchor = "stalwart-hasattachment-binary";
let binary_message = MessageBuilder::new()
.from(("Sender", "sender@domain.com"))
.to(("Recipient", "rcpt@domain.com"))
.subject("Binary attachment regression")
.date(Date::new(sent_at as i64 + 10_000))
.message_id(binary_anchor)
.text_body("binary attachment")
.attachment("application/octet-stream", "file.bin", &[0u8; 8][..])
.write_to_vec()
.unwrap();
client
.email_import(
binary_message,
[Id::new(2000u64).to_string()],
Vec::<String>::new().into(),
Some(sent_at as i64 + 10_000),
)
.await
.unwrap();
// Wait for indexing to complete (covers both the bulk import and the regression message).
wait_for_index(&server).await;
let binary_email_id = get_anchor(client, binary_anchor)
.await
.expect("binary regression email should be findable by Message-Id");
let ids = client
.email_query(
email::query::Filter::has_attachment(true).into(),
None::<Vec<_>>,
)
.await
.unwrap()
.take_ids();
assert!(
ids.iter().any(|id| id.to_string() == binary_email_id),
"expected hasAttachment query to include imported binary attachment message"
);
}
let can_stem = !params.server.search_store().is_mysql();