From f61807770e2fe841d67fe3fb0ccfdd51c8bc522b Mon Sep 17 00:00:00 2001 From: Maurus Decimus <11444311+mdecimus@users.noreply.github.com> Date: Thu, 10 Sep 2026 11:11:59 +0100 Subject: [PATCH] Update sieve zstd dictionary --- Cargo.lock | 2 + Cargo.toml | 3 - crates/email/src/message/delivery.rs | 2 + crates/email/src/sieve/ingest.rs | 27 ++- crates/smtp/src/outbound/local.rs | 7 + crates/smtp/src/scripts/event_loop.rs | 47 ++--- crates/smtp/src/scripts/mod.rs | 22 ++- resources/zstd-builder/Cargo.toml | 5 +- resources/zstd-builder/src/calendar.rs | 47 +---- resources/zstd-builder/src/constants.rs | 210 +++++++++++++++++++++-- resources/zstd-builder/src/contact.rs | 29 +--- resources/zstd-builder/src/script.rs | 13 +- resources/zstd-builder/src/synthetic.rs | 8 +- resources/zstd-builder/src/vacation.rs | 7 +- resources/zstd/sieve-v1.dict | Bin 32768 -> 32768 bytes tests/src/jmap/mail/vacation_response.rs | 4 +- 16 files changed, 293 insertions(+), 140 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f06111cef..9bcb1570a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7902,6 +7902,8 @@ checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" [[package]] name = "sieve-rs" version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fabd36845a9418dd73ca312f5e87f57f6662cf61d125f40a33c5349fdc24cfb" dependencies = [ "ahash", "bumpalo", diff --git a/Cargo.toml b/Cargo.toml index 1881e8ef0..187e8ec1a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,9 +36,6 @@ exclude = ["fuzz"] [workspace.dependencies] rkyv = { version = "0.8.18", features = ["little_endian", "unaligned"] } -[patch.crates-io] -sieve-rs = { path = "../sieve" } - [workspace.lints.clippy] result_unit_err = "allow" diff --git a/crates/email/src/message/delivery.rs b/crates/email/src/message/delivery.rs index 1ae7adecf..3eb7270f6 100644 --- a/crates/email/src/message/delivery.rs +++ b/crates/email/src/message/delivery.rs @@ -13,6 +13,7 @@ use common::{ }; use mail_parser::MessageParser; use registry::schema::enums::Permission; +use sieve::compiler::grammar::actions::action_redirect::Notify; use std::{borrow::Cow, future::Future}; use store::ahash::AHashMap; use types::blob_hash::BlobHash; @@ -62,6 +63,7 @@ pub struct AutogeneratedMessage { pub sender_address: String, pub recipients: Vec, pub message: Vec, + pub notify: Notify, } pub trait MailDelivery: Sync + Send { diff --git a/crates/email/src/sieve/ingest.rs b/crates/email/src/sieve/ingest.rs index f8e0d5e06..b6ce7ad90 100644 --- a/crates/email/src/sieve/ingest.rs +++ b/crates/email/src/sieve/ingest.rs @@ -21,8 +21,9 @@ use common::{ use mail_builder::headers::date::Date; use mail_parser::{HeaderName, MessageParser}; use sieve::{ - Arena, Context, Envelope, Handler, Input, Mailbox, Recipient, Reply, ScriptArena, Sieve, - SieveAction, Status, + Arena, Context, Envelope, Handler, Input, Mailbox, MessageSource, Recipient, Reply, + ScriptArena, Sieve, SieveAction, Status, + compiler::grammar::actions::action_redirect::Notify, runtime::{Script, Variable}, }; use std::{borrow::Cow, sync::Arc}; @@ -116,7 +117,18 @@ impl<'x> SieveHandler<'x> { } } - fn send_message(&mut self, recipient: Recipient<'_>, message_id: usize) { + fn send_message( + &mut self, + source: MessageSource, + recipient: Recipient<'_>, + notify: Notify, + message_id: usize, + ) { + let sender_address = if source == MessageSource::Vacation { + "" + } else { + self.mail_from + }; let Some(message) = self.messages.get(message_id) else { trc::event!( Sieve(SieveEvent::UnexpectedError), @@ -161,7 +173,7 @@ impl<'x> SieveHandler<'x> { trc::event!( Sieve(SieveEvent::SendMessage), - From = self.mail_from.to_string(), + From = sender_address.to_string(), To = recipients .iter() .map(|r| trc::Value::String(r.as_str().into())) @@ -175,9 +187,10 @@ impl<'x> SieveHandler<'x> { raw_message.extend_from_slice(message.raw_message.as_ref()); self.autogenerated.push(AutogeneratedMessage { - sender_address: self.mail_from.to_string(), + sender_address: sender_address.to_string(), recipients, message: raw_message, + notify, }); self.do_redirect = true; } @@ -324,11 +337,13 @@ impl<'x> Handler<'x> for SieveHandler<'x> { } } SieveAction::SendMessage { + source, recipient, + notify, message_id, .. } => { - self.send_message(recipient, message_id); + self.send_message(source, recipient, notify, message_id); } SieveAction::CreatedMessage { message, .. } => { self.messages.push(SieveMessage { diff --git a/crates/smtp/src/outbound/local.rs b/crates/smtp/src/outbound/local.rs index 03b9a8b2d..ed1dace7c 100644 --- a/crates/smtp/src/outbound/local.rs +++ b/crates/smtp/src/outbound/local.rs @@ -14,6 +14,7 @@ use crate::{ rcpt_spam_percentage, spool::{QueueParams, SmtpSpool}, }, + scripts::notify_flags, }; use common::Server; use email::message::delivery::{IngestMessage, IngestRecipient, LocalDeliveryStatus, MailDelivery}; @@ -108,6 +109,12 @@ impl MessageWrapper { for rcpt in autogenerated.recipients { message.expand_and_add_recipient(rcpt, server).await; } + let flags = notify_flags(&autogenerated.notify); + if flags != 0 { + for rcpt in &mut message.message.recipients { + rcpt.flags |= flags; + } + } // Queue Message message.message.size = autogenerated.message.len() as u64; diff --git a/crates/smtp/src/scripts/event_loop.rs b/crates/smtp/src/scripts/event_loop.rs index 8d301b5be..1d105a8d4 100644 --- a/crates/smtp/src/scripts/event_loop.rs +++ b/crates/smtp/src/scripts/event_loop.rs @@ -16,18 +16,16 @@ use common::{ }; use mail_parser::{Encoding, Message, MessagePart, PartType}; use sieve::{ - Arena, Context, Handler, Input, Mailbox, MatchAs, Recipient, Reply, Sieve, SieveAction, Status, - compiler::grammar::actions::action_redirect::{ByMode, ByTime, Notify, NotifyItem, Ret}, + Arena, Context, Handler, Input, Mailbox, MatchAs, MessageSource as SieveMessageSource, + Recipient, Reply, Sieve, SieveAction, Status, + compiler::grammar::actions::action_redirect::{ByMode, ByTime, Notify, Ret}, runtime::{RuntimeError, Script, Variable}, }; -use smtp_proto::{ - MAIL_BY_TRACE, MAIL_RET_FULL, MAIL_RET_HDRS, RCPT_NOTIFY_DELAY, RCPT_NOTIFY_FAILURE, - RCPT_NOTIFY_NEVER, RCPT_NOTIFY_SUCCESS, -}; +use smtp_proto::{MAIL_BY_TRACE, MAIL_RET_FULL, MAIL_RET_HDRS}; use std::{borrow::Cow, future::Future, time::Instant}; use trc::SieveEvent; -use super::{ScriptParameters, ScriptResult}; +use super::{ScriptParameters, ScriptResult, notify_flags}; const IMPLICIT_KEEP: usize = usize::MAX; const DISCARD: usize = usize::MAX - 1; @@ -63,6 +61,7 @@ enum PendingWork<'x> { arguments: Vec>, }, SendMessage { + source: SieveMessageSource, recipients: Vec, notify: Notify, return_of_content: Ret, @@ -154,6 +153,7 @@ impl<'x> Handler<'x> for SmtpHandler<'x> { }); } SieveAction::SendMessage { + source, recipient, notify, return_of_content, @@ -177,6 +177,7 @@ impl<'x> Handler<'x> for SmtpHandler<'x> { }; self.pending = Some(PendingWork::SendMessage { + source, recipients, notify, return_of_content, @@ -315,6 +316,7 @@ impl RunScript for Server { .await } Some(PendingWork::SendMessage { + source, recipients, notify, return_of_content, @@ -326,6 +328,7 @@ impl RunScript for Server { ctx.message().raw_message(), ¶ms, &script_id, + source, recipients, notify, return_of_content, @@ -442,6 +445,7 @@ trait QueueSieveMessage { original_raw_message: &[u8], params: &ScriptParameters<'_>, script_id: &str, + source: SieveMessageSource, recipients: Vec, notify: Notify, return_of_content: Ret, @@ -457,6 +461,7 @@ impl QueueSieveMessage for Server { original_raw_message: &[u8], params: &ScriptParameters<'_>, script_id: &str, + source: SieveMessageSource, recipients: Vec, notify: Notify, return_of_content: Ret, @@ -466,32 +471,18 @@ impl QueueSieveMessage for Server { let session_id = params.session_id; // Build message - let mut message = self.new_message( - params.return_path.as_str(), - MessageSource::Autogenerated, - session_id, - ); + let return_path = if source == SieveMessageSource::Vacation { + "" + } else { + params.return_path.as_str() + }; + let mut message = self.new_message(return_path, MessageSource::Autogenerated, session_id); for rcpt in recipients { message.expand_and_add_recipient(rcpt, self).await; } // Set notify flags - let mut flags = 0; - match notify { - Notify::Never => { - flags = RCPT_NOTIFY_NEVER; - } - Notify::Items(items) => { - for item in items { - flags |= match item { - NotifyItem::Success => RCPT_NOTIFY_SUCCESS, - NotifyItem::Failure => RCPT_NOTIFY_FAILURE, - NotifyItem::Delay => RCPT_NOTIFY_DELAY, - }; - } - } - Notify::Default => (), - } + let flags = notify_flags(¬ify); if flags > 0 { for rcpt in &mut message.message.recipients { rcpt.flags |= flags; diff --git a/crates/smtp/src/scripts/mod.rs b/crates/smtp/src/scripts/mod.rs index e1710290c..b2a915790 100644 --- a/crates/smtp/src/scripts/mod.rs +++ b/crates/smtp/src/scripts/mod.rs @@ -12,7 +12,12 @@ use common::{ }; use mail_parser::Message; -use sieve::{Envelope, SpamStatus, runtime::Variable}; +use sieve::{ + Envelope, SpamStatus, + compiler::grammar::actions::action_redirect::{Notify, NotifyItem}, + runtime::Variable, +}; +use smtp_proto::{RCPT_NOTIFY_DELAY, RCPT_NOTIFY_FAILURE, RCPT_NOTIFY_NEVER, RCPT_NOTIFY_SUCCESS}; pub mod envelope; pub mod event_loop; @@ -134,3 +139,18 @@ impl Default for ScriptParameters<'_> { Self::new() } } + +pub fn notify_flags(notify: &Notify) -> u64 { + match notify { + Notify::Never => RCPT_NOTIFY_NEVER, + Notify::Items(items) => items.iter().fold(0, |flags, item| { + flags + | match item { + NotifyItem::Success => RCPT_NOTIFY_SUCCESS, + NotifyItem::Failure => RCPT_NOTIFY_FAILURE, + NotifyItem::Delay => RCPT_NOTIFY_DELAY, + } + }), + Notify::Default => 0, + } +} diff --git a/resources/zstd-builder/Cargo.toml b/resources/zstd-builder/Cargo.toml index aa2d0c671..8adbe84b6 100644 --- a/resources/zstd-builder/Cargo.toml +++ b/resources/zstd-builder/Cargo.toml @@ -21,15 +21,12 @@ mail-builder = { version = "0.5" } mail-parser = { version = "0.11", features = ["full_encoding"] } rkyv = { version = "0.8.17", features = ["little_endian", "unaligned"] } serde_json = "1.0" -sieve-rs = { version = "1.0", features = ["rkyv", "serde"] } +sieve-rs = { version = "1.0" } smtp-proto = { version = "0.2.3", features = ["rkyv"] } xxhash-rust = { version = "0.8.18", features = ["xxh3"] } zstd = { version = "0.13", features = ["zdict_builder", "experimental"] } [patch.crates-io] -hickory-proto = { git = "https://github.com/hickory-dns/hickory-dns", rev = "e645086f7863eaab8d961421a348b06ea613ebd3" } -hickory-net = { git = "https://github.com/hickory-dns/hickory-dns", rev = "e645086f7863eaab8d961421a348b06ea613ebd3" } -hickory-resolver = { git = "https://github.com/hickory-dns/hickory-dns", rev = "e645086f7863eaab8d961421a348b06ea613ebd3" } sieve-rs = { path = "../../../sieve" } [profile.release] diff --git a/resources/zstd-builder/src/calendar.rs b/resources/zstd-builder/src/calendar.rs index 382ff61b5..0e69eadb1 100644 --- a/resources/zstd-builder/src/calendar.rs +++ b/resources/zstd-builder/src/calendar.rs @@ -8,10 +8,7 @@ use calcard::{ ICalendarValue, Uri, }, }; -use common::DavName; -use groupware::calendar::{ - CalendarEvent, CalendarEventData, EVENT_DRAFT, EVENT_INVITE_OTHERS, EventPreferences, -}; +use groupware::calendar::{CalendarEventContent, CalendarEventData, EventPreferences}; use crate::corpus::{Corpus, Rng, Stats, archive, collect_files, scrub}; @@ -63,15 +60,14 @@ pub fn build(dir: &Path, keep_text: bool, stats: &mut Stats) -> std::io::Result< match parser.entry() { Entry::ICalendar(real) if !real.components.is_empty() => { seed += 1; - let size = real.to_string().len(); if keep_text { - corpus.push_both(sample(&mut Rng::new(seed), real, size)); + corpus.push_both(sample(&mut Rng::new(seed), real)); } else { let mut scrubbed = real.clone(); scrub_ical(&mut Rng::new(seed ^ SCRUB_SEED), &mut scrubbed); corpus.push( - sample(&mut Rng::new(seed), scrubbed, size), - sample(&mut Rng::new(seed), real, size), + sample(&mut Rng::new(seed), scrubbed), + sample(&mut Rng::new(seed), real), ); } stats.read += 1; @@ -90,29 +86,17 @@ pub fn build(dir: &Path, keep_text: bool, stats: &mut Stats) -> std::io::Result< Ok(corpus) } -fn sample(rng: &mut Rng, ical: ICalendar, size: usize) -> Vec { - archive(&event(rng, ical, size)) +fn sample(rng: &mut Rng, ical: ICalendar) -> Vec { + archive(&event(rng, ical)) } -fn event(rng: &mut Rng, ical: ICalendar, size: usize) -> CalendarEvent { - let name = ical - .uids() - .next() - .map(|uid| uid.to_string()) - .unwrap_or_else(|| rng.token(36)); +fn event(rng: &mut Rng, ical: ICalendar) -> CalendarEventContent { let mut next_email_alarm = None; let mut data = CalendarEventData::new(ical, Tz::Floating, MAX_EXPANSIONS, &mut next_email_alarm); stabilise(&mut data); - let created = timestamp(rng); - let display_name_len = rng.range(6, 24); - CalendarEvent { - names: vec![DavName { - name: format!("{name}.ics"), - parent_id: rng.below(4) as u32, - }], - display_name: rng.chance(20).then(|| rng.token(display_name_len)), + CalendarEventContent { data, preferences: if rng.chance(15) { vec![EventPreferences { @@ -124,18 +108,7 @@ fn event(rng: &mut Rng, ical: ICalendar, size: usize) -> CalendarEvent { } else { Vec::new() }, - flags: if rng.chance(10) { - EVENT_DRAFT - } else if rng.chance(30) { - EVENT_INVITE_OTHERS - } else { - 0 - }, dead_properties: Default::default(), - size: size as u32, - created, - modified: created + rng.below(86400 * 30) as i64, - schedule_tag: rng.chance(20).then(|| rng.below(64) as u32), } } @@ -149,10 +122,6 @@ fn stabilise(data: &mut CalendarEventData) { data.alarms = alarms.into_boxed_slice(); } -fn timestamp(rng: &mut Rng) -> i64 { - 1_750_000_000 + rng.below(86400 * 365) as i64 -} - fn scrub_ical(rng: &mut Rng, ical: &mut ICalendar) { for component in ical.components.iter_mut() { for entry in component.entries.iter_mut() { diff --git a/resources/zstd-builder/src/constants.rs b/resources/zstd-builder/src/constants.rs index 49e5937cb..ee4ba7aec 100644 --- a/resources/zstd-builder/src/constants.rs +++ b/resources/zstd-builder/src/constants.rs @@ -1,6 +1,3 @@ -use serde_json::Value; -use sieve::Sieve; - use crate::corpus::Rng; const MAX_KEPT_LEN: usize = 64; @@ -36,27 +33,158 @@ const MAILBOXES: [&str; 8] = [ "Junk Mail", ]; -pub fn sanitize(script: &Sieve, rng: &mut Rng) -> Option { - let mut value = serde_json::to_value(script).ok()?; +pub fn sanitize(source: &[u8], rng: &mut Rng) -> Option> { + let source = std::str::from_utf8(source).ok()?; + let mut out = String::with_capacity(source.len()); let mut replaced = false; + let mut rest = source; - for constant in value.get_mut("constants")?.as_array_mut()? { - let text = constant.as_str()?; - if is_realistic(text) { - continue; + while let Some(token) = next_token(rest) { + out.push_str(&rest[..token.start]); + match token.kind { + Token::Comment => out.push_str(&rest[token.start..token.end]), + Token::Quoted(literal) => { + if is_realistic(&literal) { + out.push_str(&rest[token.start..token.end]); + } else { + out.push('"'); + for ch in replacement(rng).chars() { + if ch == '"' || ch == '\\' { + out.push('\\'); + } + out.push(ch); + } + out.push('"'); + replaced = true; + } + } + Token::MultiLine(body) => { + if is_realistic(&body) { + out.push_str(&rest[token.start..token.end]); + } else { + out.push_str("text:\r\n"); + out.push_str(&replacement(rng)); + out.push_str("\r\n.\r\n"); + replaced = true; + } + } } - *constant = Value::String(replacement(rng)); - replaced = true; + rest = &rest[token.end..]; } - if !replaced { - return Some(script.clone()); + if replaced { + out.push_str(rest); + Some(out.into_bytes()) + } else { + None + } +} + +enum Token { + Comment, + Quoted(String), + MultiLine(String), +} + +struct Located { + kind: Token, + start: usize, + end: usize, +} + +fn next_token(source: &str) -> Option { + let bytes = source.as_bytes(); + let mut at = 0; + + while at < bytes.len() { + let start = at; + match bytes[at] { + b'#' => { + let end = source[at..] + .find('\n') + .map_or(source.len(), |offset| at + offset); + return Located::at(Token::Comment, start, end).into(); + } + b'/' if bytes.get(at + 1) == Some(&b'*') => { + let end = source[at + 2..] + .find("*/") + .map_or(source.len(), |offset| at + 2 + offset + 2); + return Located::at(Token::Comment, start, end).into(); + } + b'"' => { + let (literal, end) = read_quoted(source, at); + return Located::at(Token::Quoted(literal), start, end).into(); + } + _ => { + if let Some((body, end)) = read_multiline(source, at) { + return Located::at(Token::MultiLine(body), start, end).into(); + } + at += 1; + } + } } - let sanitized: Sieve = serde_json::from_value(value).ok()?; - (sanitized.constant_count() == script.constant_count() - && sanitized.instruction_count() == script.instruction_count()) - .then_some(sanitized) + None +} + +fn read_quoted(source: &str, at: usize) -> (String, usize) { + let bytes = source.as_bytes(); + let mut literal = String::new(); + let mut cursor = at + 1; + + while cursor < bytes.len() { + match bytes[cursor] { + b'\\' if cursor + 1 < bytes.len() => { + literal.push_str(&source[cursor + 1..cursor + 2]); + cursor += 2; + } + b'"' => return (literal, cursor + 1), + _ => { + let width = source[cursor..].chars().next().map_or(1, char::len_utf8); + literal.push_str(&source[cursor..cursor + width]); + cursor += width; + } + } + } + + (literal, source.len()) +} + +fn read_multiline(source: &str, at: usize) -> Option<(String, usize)> { + const MARKER: &str = "text:"; + + if !source[at..] + .get(..MARKER.len())? + .eq_ignore_ascii_case(MARKER) + || source[..at] + .chars() + .next_back() + .is_some_and(|ch| ch.is_alphanumeric() || ch == '_') + { + return None; + } + + let opening = at + MARKER.len() + source[at + MARKER.len()..].find('\n')?; + let mut body = String::new(); + let mut cursor = opening + 1; + + for line in source[cursor..].split_inclusive('\n') { + let content = line.trim_end_matches(['\r', '\n']); + cursor += line.len(); + if content == "." { + return Some((body, cursor)); + } + body.push_str(content.strip_prefix("..").unwrap_or(content)); + body.push_str("\r\n"); + } + + None +} + +impl Located { + fn at(kind: Token, start: usize, end: usize) -> Self { + Located { kind, start, end } + } } fn is_realistic(constant: &str) -> bool { @@ -84,3 +212,51 @@ fn replacement(rng: &mut Rng) -> String { _ => rng.pick(&SENTENCES).to_string(), } } + +#[cfg(test)] +mod tests { + use super::*; + + fn sanitized(source: &str) -> Option { + sanitize(source.as_bytes(), &mut Rng::new(1)) + .map(|output| String::from_utf8(output).expect("utf-8 output")) + } + + #[test] + fn keeps_realistic_scripts_untouched() { + assert_eq!( + sanitized("require [\"fileinto\"];\r\nfileinto \"INBOX/Lists\";\r\n"), + None + ); + } + + #[test] + fn replaces_junk_literals() { + let output = sanitized("fileinto \"aaaaaaaaaaaaaaaa\";\r\n").expect("replaced"); + assert!(output.starts_with("fileinto \""), "{output}"); + assert!(output.ends_with("\";\r\n"), "{output}"); + assert!(!output.contains("aaaa"), "{output}"); + } + + #[test] + fn ignores_quotes_inside_comments() { + assert_eq!(sanitized("# \"aaaaaaaaaaaa\"\r\nstop;\r\n"), None); + assert_eq!(sanitized("/* \"aaaaaaaaaaaa\" */\r\nstop;\r\n"), None); + } + + #[test] + fn keeps_escaped_quotes_in_place() { + assert_eq!(sanitized("set \"a\" \"say \\\"hello\\\"\";\r\n"), None); + } + + #[test] + fn replaces_junk_multiline_blocks() { + let output = sanitized( + "notify text:\r\nxxxxxxxxxxxxxxxxxxxx\r\nxxxxxxxxxxxxxxxxxxxx\r\n.\r\nstop;\r\n", + ) + .expect("replaced"); + assert!(output.starts_with("notify text:\r\n"), "{output}"); + assert!(output.ends_with(".\r\nstop;\r\n"), "{output}"); + assert!(!output.contains("xxxx"), "{output}"); + } +} diff --git a/resources/zstd-builder/src/contact.rs b/resources/zstd-builder/src/contact.rs index 2f03a256b..50d818e7d 100644 --- a/resources/zstd-builder/src/contact.rs +++ b/resources/zstd-builder/src/contact.rs @@ -4,8 +4,7 @@ use calcard::{ Entry, Parser, vcard::{VCard, VCardParameterName, VCardParameterValue, VCardProperty, VCardValue}, }; -use common::DavName; -use groupware::contact::ContactCard; +use groupware::contact::ContactCardContent; use crate::corpus::{Corpus, Rng, Stats, archive, collect_files, scrub}; @@ -34,16 +33,12 @@ pub fn build(dir: &Path, keep_text: bool, stats: &mut Stats) -> std::io::Result< match parser.entry() { Entry::VCard(real) if !real.entries.is_empty() => { seed += 1; - let size = real.to_string().len(); if keep_text { - corpus.push_both(archive(&contact(&mut Rng::new(seed), real, size))); + corpus.push_both(archive(&contact(real))); } else { let mut scrubbed = real.clone(); scrub_vcard(&mut Rng::new(seed ^ SCRUB_SEED), &mut scrubbed); - corpus.push( - archive(&contact(&mut Rng::new(seed), scrubbed, size)), - archive(&contact(&mut Rng::new(seed), real, size)), - ); + corpus.push(archive(&contact(scrubbed)), archive(&contact(real))); } stats.read += 1; found += 1; @@ -61,24 +56,10 @@ pub fn build(dir: &Path, keep_text: bool, stats: &mut Stats) -> std::io::Result< Ok(corpus) } -fn contact(rng: &mut Rng, card: VCard, size: usize) -> ContactCard { - let name = card - .uid() - .map(|uid| uid.to_string()) - .unwrap_or_else(|| rng.token(36)); - let created = 1_750_000_000 + rng.below(86400 * 365) as i64; - - ContactCard { - names: vec![DavName { - name: format!("{name}.vcf"), - parent_id: rng.below(3) as u32, - }], - display_name: None, +fn contact(card: VCard) -> ContactCardContent { + ContactCardContent { card, dead_properties: Default::default(), - created, - modified: created + rng.below(86400 * 30) as i64, - size: size as u32, } } diff --git a/resources/zstd-builder/src/script.rs b/resources/zstd-builder/src/script.rs index 7cc793955..5b9eb9303 100644 --- a/resources/zstd-builder/src/script.rs +++ b/resources/zstd-builder/src/script.rs @@ -55,14 +55,11 @@ pub fn build(dir: &Path, stats: &mut Stats) -> std::io::Result { }; let mut rng = Rng::new(index as u64 + 1); - let Some(script) = constants::sanitize(&script, &mut rng) else { - stats.skipped += 1; - continue; - }; - let Ok(script) = script.to_bytes() else { - stats.skipped += 1; - continue; - }; + let script = constants::sanitize(&raw, &mut rng) + .and_then(|sanitized| compiler.compile(&sanitized).ok()) + .filter(|sanitized| sanitized.code_len() == script.code_len()) + .unwrap_or(script) + .to_bytes(); let name_len = rng.range(4, 20); let sample = archive(&SieveScript { diff --git a/resources/zstd-builder/src/synthetic.rs b/resources/zstd-builder/src/synthetic.rs index e8241b90b..7b381235b 100644 --- a/resources/zstd-builder/src/synthetic.rs +++ b/resources/zstd-builder/src/synthetic.rs @@ -642,13 +642,17 @@ fn metadata(rng: &mut Rng) -> Box<[Metadata]> { }]; if rng.chance(50) { entries.push(Metadata::QueueSize { - key: rng.domain().into_bytes().into_boxed_slice(), + key: quota_key(rng), id: rng.next_u64(), }); entries.push(Metadata::QueueCount { - key: rng.domain().into_bytes().into_boxed_slice(), + key: quota_key(rng), id: rng.next_u64(), }); } entries.into_boxed_slice() } + +fn quota_key(rng: &mut Rng) -> u128 { + (u128::from(rng.next_u64()) << 64) | u128::from(rng.next_u64()) +} diff --git a/resources/zstd-builder/src/vacation.rs b/resources/zstd-builder/src/vacation.rs index 10c30feb9..381b71fed 100644 --- a/resources/zstd-builder/src/vacation.rs +++ b/resources/zstd-builder/src/vacation.rs @@ -47,14 +47,9 @@ pub fn build(compiler: &Compiler, corpus: &mut Corpus, stats: &mut Stats) { continue; } }; - let Ok(script) = script.to_bytes() else { - stats.skipped += 1; - continue; - }; - object.blob_hash = BlobHash::generate(&source); object.size = source.len() as u32; - object.script = script; + object.script = script.to_bytes(); corpus.push_both(archive(&object)); stats.read += 1; diff --git a/resources/zstd/sieve-v1.dict b/resources/zstd/sieve-v1.dict index da060e510dcee6b3703ed6de21bb9586a8db21ec..5f7f0047eb8f44255b5061eecd1ed6cc796e4975 100644 GIT binary patch literal 32768 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

pXy4=gT0CGqnj>P7}0Wk$Rr4< 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 m$@=)vcA8}c%xRE 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 N98H^1Yc(EK`+rp0K(zn> diff --git a/tests/src/jmap/mail/vacation_response.rs b/tests/src/jmap/mail/vacation_response.rs index 0eb06502e..5dc0cb36e 100644 --- a/tests/src/jmap/mail/vacation_response.rs +++ b/tests/src/jmap/mail/vacation_response.rs @@ -60,7 +60,7 @@ pub async fn test(test: &TestServer) { // Await vacation response assert_message_delivery( &mut smtp_rx, - MockMessage::new("", [""], "@Kokomo"), + MockMessage::new("<>", [""], "@Kokomo"), ) .await; @@ -149,7 +149,7 @@ pub async fn test(test: &TestServer) { assert_message_delivery( &mut smtp_rx, - MockMessage::new("", [""], "@Kokomo"), + MockMessage::new("<>", [""], "@Kokomo"), ) .await;