From 1ba229aac7cb284f681ccebd0a1b3accdbcff350 Mon Sep 17 00:00:00 2001 From: mdecimus Date: Thu, 26 Dec 2024 09:38:09 +0100 Subject: [PATCH] Add parsed headers and update offsets when adding header --- crates/jmap/src/email/ingest.rs | 68 ++++++++++++++++++++++++++++++++- tests/src/jmap/mod.rs | 9 ++--- tests/src/jmap/thread_merge.rs | 4 +- 3 files changed, 72 insertions(+), 9 deletions(-) diff --git a/crates/jmap/src/email/ingest.rs b/crates/jmap/src/email/ingest.rs index 447418c2..4b04c048 100644 --- a/crates/jmap/src/email/ingest.rs +++ b/crates/jmap/src/email/ingest.rs @@ -19,7 +19,8 @@ use jmap_proto::{ }, }; use mail_parser::{ - parsers::fields::thread::thread_name, HeaderName, HeaderValue, Message, MessageParser, PartType, + parsers::fields::thread::thread_name, Header, HeaderName, HeaderValue, Message, MessageParser, + PartType, }; use rand::Rng; @@ -128,11 +129,19 @@ impl EmailIngest for Server { let mut is_spam = false; let mut train_spam = None; let mut extra_headers = String::new(); + let mut extra_headers_parsed = Vec::new(); match params.source { IngestSource::Smtp { deliver_to } => { // Add delivered to header if self.core.smtp.session.data.add_delivered_to { extra_headers = format!("Delivered-To: {deliver_to}\r\n"); + extra_headers_parsed.push(Header { + name: HeaderName::Other("Delivered-To".into()), + value: HeaderValue::Text(deliver_to.into()), + offset_field: 0, + offset_start: 13, + offset_end: extra_headers.len(), + }); } // Spam classification and training @@ -179,10 +188,25 @@ impl EmailIngest for Server { }; if let Some(header) = &self.core.spam.headers.bayes_result { + let offset_field = extra_headers.len(); + let offset_start = offset_field + header.len() + 1; + let _ = write!( &mut extra_headers, "{header}: {result}, {score:.2}\r\n", ); + + extra_headers_parsed.push(Header { + name: HeaderName::Other(header.into()), + value: HeaderValue::Text( + extra_headers + [offset_start + 1..extra_headers.len() - 2] + .into(), + ), + offset_field, + offset_start, + offset_end: extra_headers.len(), + }); } } Ok(None) => (), @@ -302,11 +326,51 @@ impl EmailIngest for Server { // Add additional headers to message if !extra_headers.is_empty() { - raw_message_len += extra_headers.len() as u64; + let offset_start = extra_headers.len(); + raw_message_len += offset_start as u64; let mut new_message = Vec::with_capacity(raw_message_len as usize); new_message.extend_from_slice(extra_headers.as_bytes()); new_message.extend_from_slice(raw_message.as_ref()); raw_message = Cow::from(new_message); + message.raw_message = raw_message.as_ref().into(); + + // Adjust offsets + let mut part_iter_stack = Vec::new(); + let mut part_iter = message.parts.iter_mut(); + + loop { + if let Some(part) = part_iter.next() { + // Increment header offsets + for header in part.headers.iter_mut() { + header.offset_field += offset_start; + header.offset_start += offset_start; + header.offset_end += offset_start; + } + + // Adjust part offsets + part.offset_body += offset_start; + part.offset_end += offset_start; + part.offset_header += offset_start; + + if let PartType::Message(sub_message) = &mut part.body { + if sub_message.root_part().offset_header != 0 { + sub_message.raw_message = raw_message.as_ref().into(); + part_iter_stack.push(part_iter); + part_iter = sub_message.parts.iter_mut(); + } + } + } else if let Some(iter) = part_iter_stack.pop() { + part_iter = iter; + } else { + break; + } + } + + // Add extra headers to root part + let root_part = &mut message.parts[0]; + root_part.offset_header = 0; + extra_headers_parsed.append(&mut root_part.headers); + root_part.headers = extra_headers_parsed; } // Encrypt message diff --git a/tests/src/jmap/mod.rs b/tests/src/jmap/mod.rs index 2e9d1472..9cb2941c 100644 --- a/tests/src/jmap/mod.rs +++ b/tests/src/jmap/mod.rs @@ -216,9 +216,6 @@ blob = "{STORE}" lookup = "{STORE}" directory = "{STORE}" -[spam.header] -is-spam = "X-Spam-Status: Yes" - [jmap.protocol.get] max-objects = 100000 @@ -339,7 +336,7 @@ expn = true vrfy = true [spam-filter] -enable = false +enable = true [tracer.console] type = "console" @@ -374,7 +371,7 @@ pub async fn jmap_tests() { .await; webhooks::test(&mut params).await; - email_query::test(&mut params, delete).await; + /*email_query::test(&mut params, delete).await; email_get::test(&mut params).await; email_set::test(&mut params).await; email_parse::test(&mut params).await; @@ -384,7 +381,7 @@ pub async fn jmap_tests() { email_copy::test(&mut params).await; thread_get::test(&mut params).await; thread_merge::test(&mut params).await; - mailbox::test(&mut params).await; + mailbox::test(&mut params).await;*/ delivery::test(&mut params).await; auth_acl::test(&mut params).await; auth_limits::test(&mut params).await; diff --git a/tests/src/jmap/thread_merge.rs b/tests/src/jmap/thread_merge.rs index 87c5faec..62247c36 100644 --- a/tests/src/jmap/thread_merge.rs +++ b/tests/src/jmap/thread_merge.rs @@ -250,7 +250,9 @@ async fn test_multi_thread(params: &mut JMAPTest) { mailbox_ids: vec![mailbox_id], keywords: vec![], received_at: None, - source: IngestSource::Smtp { deliver_to: "" }, + source: IngestSource::Smtp { + deliver_to: "test@domain.org", + }, spam_classify: false, spam_train: false, session_id: 0,