From a2ea0f6cee58ffe9c581a7135932065bb7595eca Mon Sep 17 00:00:00 2001 From: mdecimus Date: Sun, 27 Jul 2025 11:26:02 +0200 Subject: [PATCH] MTA: Do not convert e-mail local parts to lowercase (fixes #1916) --- crates/http/src/management/queue.rs | 13 +++--- crates/jmap/src/submission/get.rs | 2 +- crates/migration/src/queue.rs | 43 +++++++++---------- crates/smtp/src/inbound/data.rs | 42 +++++++++--------- crates/smtp/src/outbound/delivery.rs | 13 +++--- crates/smtp/src/outbound/local.rs | 9 ++-- crates/smtp/src/outbound/session.rs | 16 +++---- crates/smtp/src/queue/dsn.rs | 2 +- crates/smtp/src/queue/mod.rs | 61 +++++++++++++++++++++++++++ crates/smtp/src/queue/spool.rs | 26 +++--------- crates/smtp/src/reporting/mod.rs | 4 +- crates/smtp/src/scripts/event_loop.rs | 5 +-- tests/src/smtp/inbound/dmarc.rs | 6 +-- tests/src/smtp/inbound/scripts.rs | 8 ++-- tests/src/smtp/management/queue.rs | 6 +-- tests/src/smtp/outbound/lmtp.rs | 2 +- tests/src/smtp/outbound/smtp.rs | 4 +- tests/src/smtp/outbound/throttle.rs | 4 +- tests/src/smtp/queue/manager.rs | 4 +- tests/src/smtp/queue/retry.rs | 2 +- tests/src/smtp/reporting/dmarc.rs | 2 +- tests/src/smtp/reporting/tls.rs | 2 +- tests/src/smtp/session.rs | 2 +- 23 files changed, 161 insertions(+), 117 deletions(-) diff --git a/crates/http/src/management/queue.rs b/crates/http/src/management/queue.rs index 5e496b12..6be706e5 100644 --- a/crates/http/src/management/queue.rs +++ b/crates/http/src/management/queue.rs @@ -294,7 +294,7 @@ impl QueueManagement for Server { Status::Scheduled | Status::TemporaryFailure(_) ) && item .as_ref() - .is_none_or(|item| recipient.address.contains(item)) + .is_none_or(|item| recipient.address().contains(item)) { recipient.retry.due = time; if recipient @@ -383,7 +383,7 @@ impl QueueManagement for Server { if let Some(item) = params.get("filter") { // Cancel delivery for all recipients that match for rcpt in &mut message.message.recipients { - if rcpt.address.contains(item) { + if rcpt.address().contains(item) { rcpt.status = Status::PermanentFailure(ErrorDetails { entity: "localhost".to_string(), details: queue::Error::Io("Delivery canceled.".to_string()), @@ -585,7 +585,7 @@ impl Message { .recipients .iter() .map(|rcpt| Recipient { - address: rcpt.address.to_string(), + address: rcpt.address().to_string(), queue: rcpt.queue.to_string(), status: match &rcpt.status { ArchivedStatus::Scheduled => Status::Scheduled, @@ -685,13 +685,16 @@ async fn fetch_queued_messages( .as_ref() .map(|text| { message.return_path.contains(text) - || message.recipients.iter().any(|r| r.address.contains(text)) + || message + .recipients + .iter() + .any(|r| r.address().contains(text)) }) .unwrap_or_else(|| { from.as_ref() .is_none_or(|from| message.return_path.contains(from)) && to.as_ref().is_none_or(|to| { - message.recipients.iter().any(|r| r.address.contains(to)) + message.recipients.iter().any(|r| r.address().contains(to)) }) }) && before.as_ref().is_none_or(|before| { diff --git a/crates/jmap/src/submission/get.rs b/crates/jmap/src/submission/get.rs index 06cc58cb..b792fa8a 100644 --- a/crates/jmap/src/submission/get.rs +++ b/crates/jmap/src/submission/get.rs @@ -113,7 +113,7 @@ impl EmailSubmissionGet for Server { .unarchive::() .caused_by(trc::location!())?; for rcpt in queued_message.recipients.iter() { - *delivery_status.get_mut_or_insert(rcpt.address.to_string()) = + *delivery_status.get_mut_or_insert(rcpt.address().to_string()) = DeliveryStatus { smtp_reply: match &rcpt.status { ArchivedStatus::Completed(reply) => { diff --git a/crates/migration/src/queue.rs b/crates/migration/src/queue.rs index 1775992e..679bad92 100644 --- a/crates/migration/src/queue.rs +++ b/crates/migration/src/queue.rs @@ -278,33 +278,32 @@ where .into_iter() .map(|r| { let domain = &domains[r.domain_idx.as_u64() as usize]; - Recipient { - address: r.address_lcase, - status: match r.status { - Status::Scheduled => match &domain.status { - Status::Scheduled | Status::Completed(_) => Status::Scheduled, - Status::TemporaryFailure(err) => Status::TemporaryFailure( - migrate_legacy_error(&domain.domain, err), - ), - Status::PermanentFailure(err) => Status::PermanentFailure( - migrate_legacy_error(&domain.domain, err), - ), - }, - Status::Completed(details) => Status::Completed(details), + let mut rcpt = Recipient::new(r.address); + rcpt.status = match r.status { + Status::Scheduled => match &domain.status { + Status::Scheduled | Status::Completed(_) => Status::Scheduled, Status::TemporaryFailure(err) => { - Status::TemporaryFailure(migrate_host_response(err)) + Status::TemporaryFailure(migrate_legacy_error(&domain.domain, err)) } Status::PermanentFailure(err) => { - Status::PermanentFailure(migrate_host_response(err)) + Status::PermanentFailure(migrate_legacy_error(&domain.domain, err)) } }, - flags: r.flags, - orcpt: r.orcpt, - retry: domain.retry.clone(), - notify: domain.notify.clone(), - queue: QueueName::default(), - expires: QueueExpiry::Ttl(domain.expires.saturating_sub(now())), - } + Status::Completed(details) => Status::Completed(details), + Status::TemporaryFailure(err) => { + Status::TemporaryFailure(migrate_host_response(err)) + } + Status::PermanentFailure(err) => { + Status::PermanentFailure(migrate_host_response(err)) + } + }; + rcpt.flags = r.flags; + rcpt.orcpt = r.orcpt; + rcpt.retry = domain.retry.clone(); + rcpt.notify = domain.notify.clone(); + rcpt.queue = QueueName::default(); + rcpt.expires = QueueExpiry::Ttl(domain.expires.saturating_sub(now())); + rcpt }) .collect(), flags: message.flags, diff --git a/crates/smtp/src/inbound/data.rs b/crates/smtp/src/inbound/data.rs index dc98e643..e8f2b65a 100644 --- a/crates/smtp/src/inbound/data.rs +++ b/crates/smtp/src/inbound/data.rs @@ -9,7 +9,8 @@ use crate::{ core::{Session, SessionAddress, State}, inbound::milter::Modification, queue::{ - self, Message, MessageSource, MessageWrapper, QueueEnvelope, Schedule, quota::HasQueueQuota, + self, DomainPart, Message, MessageSource, MessageWrapper, QueueEnvelope, + quota::HasQueueQuota, }, reporting::analysis::AnalyzeReport, scripts::ScriptResult, @@ -709,7 +710,7 @@ impl Session { .map_or(0, |d| d.as_secs()); let mut message = Message { created, - return_path: mail_from.address_lcase, + return_path: mail_from.address.to_lowercase_domain(), recipients: Vec::with_capacity(rcpt_to.len()), flags: mail_from.flags, priority: self.data.priority, @@ -725,26 +726,23 @@ impl Session { let future_release = self.data.future_release; rcpt_to.sort_unstable(); for rcpt in rcpt_to { - message.recipients.push(queue::Recipient { - address: rcpt.address_lcase, - status: queue::Status::Scheduled, - flags: if rcpt.flags - & (RCPT_NOTIFY_DELAY - | RCPT_NOTIFY_FAILURE - | RCPT_NOTIFY_SUCCESS - | RCPT_NOTIFY_NEVER) - != 0 - { - rcpt.flags - } else { - rcpt.flags | RCPT_NOTIFY_DELAY | RCPT_NOTIFY_FAILURE - }, - orcpt: rcpt.dsn_info, - retry: Schedule::now(), - notify: Schedule::now(), - expires: QueueExpiry::Attempts(0), - queue: QueueName::default(), - }); + message.recipients.push( + queue::Recipient::new(rcpt.address) + .with_flags( + if rcpt.flags + & (RCPT_NOTIFY_DELAY + | RCPT_NOTIFY_FAILURE + | RCPT_NOTIFY_SUCCESS + | RCPT_NOTIFY_NEVER) + != 0 + { + rcpt.flags + } else { + rcpt.flags | RCPT_NOTIFY_DELAY | RCPT_NOTIFY_FAILURE + }, + ) + .with_orcpt(rcpt.dsn_info), + ); let envelope = QueueEnvelope::new(&message, message.recipients.last().unwrap()); diff --git a/crates/smtp/src/outbound/delivery.rs b/crates/smtp/src/outbound/delivery.rs index 38863edc..7f573cc8 100644 --- a/crates/smtp/src/outbound/delivery.rs +++ b/crates/smtp/src/outbound/delivery.rs @@ -18,8 +18,7 @@ use crate::queue::dsn::SendDsn; use crate::queue::spool::SmtpSpool; use crate::queue::throttle::IsAllowed; use crate::queue::{ - DomainPart, Error, FROM_REPORT, HostResponse, MessageWrapper, QueueEnvelope, QueuedMessage, - Status, + Error, FROM_REPORT, HostResponse, MessageWrapper, QueueEnvelope, QueuedMessage, Status, }; use crate::reporting::SmtpReporting; use crate::{queue::ErrorDetails, reporting::tls::TlsRptOptions}; @@ -74,7 +73,7 @@ impl QueuedMessage { Status::Scheduled | Status::TemporaryFailure(_) ) && r.queue == message.queue_name { - Some(trc::Value::String(r.address.as_str().into())) + Some(trc::Value::String(r.address().into())) } else { None } @@ -236,7 +235,7 @@ impl QueuedMessage { ); routes - .entry((rcpt.address.domain_part(), route)) + .entry((rcpt.domain_part(), route)) .or_default() .push(rcpt_idx); } @@ -1325,7 +1324,7 @@ impl MessageWrapper { SpanId = self.span_id, QueueId = self.queue_id, QueueName = self.queue_name.as_str().to_string(), - To = rcpt.address.clone(), + To = rcpt.address().to_string(), Reason = from_error_details(&err.details), Details = trc::Value::Timestamp(now), Expires = rcpt @@ -1344,7 +1343,7 @@ impl MessageWrapper { SpanId = self.span_id, QueueId = self.queue_id, QueueName = self.queue_name.as_str().to_string(), - To = rcpt.address.clone(), + To = rcpt.address().to_string(), Reason = "Message expired without any delivery attempts made.", Details = trc::Value::Timestamp(now), Expires = rcpt @@ -1355,7 +1354,7 @@ impl MessageWrapper { ); rcpt.status = Status::PermanentFailure(ErrorDetails { - entity: rcpt.address.domain_part().to_string(), + entity: rcpt.domain_part().to_string(), details: Error::Io( "Message expired without any delivery attempts made.".into(), ), diff --git a/crates/smtp/src/outbound/local.rs b/crates/smtp/src/outbound/local.rs index 04d03f21..79930264 100644 --- a/crates/smtp/src/outbound/local.rs +++ b/crates/smtp/src/outbound/local.rs @@ -29,8 +29,8 @@ impl MessageWrapper { let mut pending_recipients = Vec::new(); let mut recipient_addresses = Vec::new(); for &rcpt_idx in rcpt_idxs { - let rcpt_addr = &self.message.recipients[rcpt_idx].address; - recipient_addresses.push(rcpt_addr.clone()); + let rcpt_addr = self.message.recipients[rcpt_idx].address(); + recipient_addresses.push(rcpt_addr.to_lowercase()); pending_recipients.push((rcpt_idx, rcpt_addr)); } @@ -93,8 +93,7 @@ impl MessageWrapper { // Process autogenerated messages for autogenerated in delivery_result.autogenerated { - let mut message = - server.new_message(autogenerated.sender_address.to_lowercase(), self.span_id); + let mut message = server.new_message(autogenerated.sender_address, self.span_id); for rcpt in autogenerated.recipients { message.add_recipient(rcpt, server).await; } @@ -130,7 +129,7 @@ impl MessageWrapper { .message .recipients .into_iter() - .map(|r| trc::Value::from(r.address)) + .map(|r| trc::Value::from(r.address().to_string())) .collect::>(), ); } diff --git a/crates/smtp/src/outbound/session.rs b/crates/smtp/src/outbound/session.rs index f10752c7..54f6343e 100644 --- a/crates/smtp/src/outbound/session.rs +++ b/crates/smtp/src/outbound/session.rs @@ -173,7 +173,7 @@ impl MessageWrapper { Delivery(DeliveryEvent::RcptTo), SpanId = params.session_id, Hostname = params.hostname.to_string(), - To = rcpt.address.to_string(), + To = rcpt.address().to_string(), Code = response.code, Details = response.message.to_string(), Elapsed = time.elapsed(), @@ -193,7 +193,7 @@ impl MessageWrapper { Delivery(DeliveryEvent::RcptToRejected), SpanId = params.session_id, Hostname = params.hostname.to_string(), - To = rcpt.address.to_string(), + To = rcpt.address().to_string(), Code = response.code, Details = response.message.to_string(), Elapsed = time.elapsed(), @@ -221,7 +221,7 @@ impl MessageWrapper { Delivery(DeliveryEvent::RcptToFailed), SpanId = params.session_id, Hostname = params.hostname.to_string(), - To = rcpt.address.to_string(), + To = rcpt.address().to_string(), CausedBy = from_mail_send_error(&err), Elapsed = time.elapsed(), ); @@ -272,7 +272,7 @@ impl MessageWrapper { Delivery(DeliveryEvent::Delivered), SpanId = params.session_id, Hostname = params.hostname.to_string(), - To = rcpt.address.to_string(), + To = rcpt.address().to_string(), Code = response.code, Details = response.message.to_string(), Elapsed = time.elapsed(), @@ -332,7 +332,7 @@ impl MessageWrapper { Delivery(DeliveryEvent::Delivered), SpanId = params.session_id, Hostname = params.hostname.to_string(), - To = rcpt.address.to_string(), + To = rcpt.address().to_string(), Code = response.code, Details = response.message.to_string(), Elapsed = time.elapsed(), @@ -348,7 +348,7 @@ impl MessageWrapper { Delivery(DeliveryEvent::RcptToRejected), SpanId = params.session_id, Hostname = params.hostname.to_string(), - To = rcpt.address.to_string(), + To = rcpt.address().to_string(), Code = response.code, Details = response.message.to_string(), Elapsed = time.elapsed(), @@ -420,8 +420,8 @@ impl MessageWrapper { } fn build_rcpt_to(&self, rcpt: &Recipient, capabilities: &EhloResponse) -> String { - let mut rcpt_to = String::with_capacity(rcpt.address.len() + 60); - let _ = write!(rcpt_to, "RCPT TO:<{}>", rcpt.address); + let mut rcpt_to = String::with_capacity(rcpt.address().len() + 60); + let _ = write!(rcpt_to, "RCPT TO:<{}>", rcpt.address()); if capabilities.has_capability(EXT_DSN) { if rcpt.has_flag(RCPT_NOTIFY_SUCCESS | RCPT_NOTIFY_FAILURE | RCPT_NOTIFY_DELAY) { rcpt_to.push_str(" NOTIFY="); diff --git a/crates/smtp/src/queue/dsn.rs b/crates/smtp/src/queue/dsn.rs index 035bc5fe..fa5a9b44 100644 --- a/crates/smtp/src/queue/dsn.rs +++ b/crates/smtp/src/queue/dsn.rs @@ -39,7 +39,7 @@ impl SendDsn for Server { if let Some(dsn) = message.build_dsn(self).await { let mut dsn_message = self.new_message("", message.span_id); dsn_message - .add_recipient_parts(message.message.return_path.as_str(), self) + .add_recipient(message.message.return_path.as_str(), self) .await; // Sign message diff --git a/crates/smtp/src/queue/mod.rs b/crates/smtp/src/queue/mod.rs index 81f0f2b3..00118ba6 100644 --- a/crates/smtp/src/queue/mod.rs +++ b/crates/smtp/src/queue/mod.rs @@ -399,6 +399,49 @@ pub fn instant_to_timestamp(now: Instant, time: Instant) -> u64 { + time.checked_duration_since(now).map_or(0, |d| d.as_secs()) } +impl Recipient { + pub fn new(address: impl AsRef) -> Self { + Recipient { + address: address.to_lowercase_domain(), + status: Status::Scheduled, + flags: 0, + orcpt: None, + retry: Schedule::now(), + notify: Schedule::now(), + expires: QueueExpiry::Attempts(0), + queue: QueueName::default(), + } + } + + pub fn with_flags(mut self, flags: u64) -> Self { + self.flags = flags; + self + } + + pub fn with_orcpt(mut self, orcpt: Option) -> Self { + self.orcpt = orcpt; + self + } + + pub fn address(&self) -> &str { + &self.address + } + + pub fn domain_part(&self) -> &str { + self.address.domain_part() + } +} + +impl ArchivedRecipient { + pub fn address(&self) -> &str { + self.address.as_str() + } + + pub fn domain_part(&self) -> &str { + self.address.domain_part() + } +} + pub trait InstantFromTimestamp { fn to_instant(&self) -> Instant; } @@ -418,10 +461,28 @@ impl InstantFromTimestamp for u64 { } pub trait DomainPart { + fn to_lowercase_domain(&self) -> String; fn domain_part(&self) -> &str; } impl> DomainPart for T { + fn to_lowercase_domain(&self) -> String { + let address = self.as_ref(); + if let Some((local, domain)) = address.rsplit_once('@') { + let mut address = String::with_capacity(address.len()); + address.push_str(local); + address.push('@'); + for ch in domain.chars() { + for ch in ch.to_lowercase() { + address.push(ch); + } + } + address + } else { + address.to_string() + } + } + #[inline(always)] fn domain_part(&self) -> &str { self.as_ref() diff --git a/crates/smtp/src/queue/spool.rs b/crates/smtp/src/queue/spool.rs index 33936445..24edf739 100644 --- a/crates/smtp/src/queue/spool.rs +++ b/crates/smtp/src/queue/spool.rs @@ -13,7 +13,7 @@ use crate::queue::{ DomainPart, FROM_AUTHENTICATED, FROM_AUTOGENERATED, FROM_DSN, FROM_REPORT, FROM_UNAUTHENTICATED, FROM_UNAUTHENTICATED_DMARC, MessageWrapper, }; -use common::config::smtp::queue::{QueueExpiry, QueueName}; +use common::config::smtp::queue::QueueName; use common::ipc::QueueEvent; use common::{KV_LOCK_QUEUE_MESSAGE, Server}; use std::borrow::Cow; @@ -39,7 +39,7 @@ pub struct QueuedMessages { } pub trait SmtpSpool: Sync + Send { - fn new_message(&self, return_path: impl Into, span_id: u64) -> MessageWrapper; + fn new_message(&self, return_path: impl AsRef, span_id: u64) -> MessageWrapper; fn next_event(&self, queue: &mut Queue) -> impl Future + Send; @@ -68,7 +68,7 @@ pub trait SmtpSpool: Sync + Send { } impl SmtpSpool for Server { - fn new_message(&self, return_path: impl Into, span_id: u64) -> MessageWrapper { + fn new_message(&self, return_path: impl AsRef, span_id: u64) -> MessageWrapper { let created = SystemTime::now() .duration_since(SystemTime::UNIX_EPOCH) .map_or(0, |d| d.as_secs()); @@ -80,7 +80,7 @@ impl SmtpSpool for Server { span_id, message: Message { created, - return_path: return_path.into(), + return_path: return_path.to_lowercase_domain(), recipients: Vec::with_capacity(1), flags: 0, env_id: None, @@ -483,18 +483,9 @@ impl MessageWrapper { true } - pub async fn add_recipient_parts(&mut self, rcpt: impl Into, server: &Server) { + pub async fn add_recipient(&mut self, rcpt: impl AsRef, server: &Server) { // Resolve queue - self.message.recipients.push(Recipient { - address: rcpt.into(), - status: Status::Scheduled, - flags: 0, - orcpt: None, - retry: Schedule::now(), - notify: Schedule::now(), - expires: QueueExpiry::Attempts(0), - queue: QueueName::default(), - }); + self.message.recipients.push(Recipient::new(rcpt)); let queue = server.get_queue_or_default( &server .eval_if::( @@ -515,11 +506,6 @@ impl MessageWrapper { recipient.queue = queue.virtual_queue; } - pub async fn add_recipient(&mut self, rcpt: impl AsRef, server: &Server) { - let rcpt = rcpt.as_ref().to_lowercase(); - self.add_recipient_parts(rcpt, server).await; - } - pub async fn save_changes(mut self, server: &Server, prev_event: Option) -> bool { // Release quota for completed deliveries let mut batch = BatchBuilder::new(); diff --git a/crates/smtp/src/reporting/mod.rs b/crates/smtp/src/reporting/mod.rs index ef1aa953..6cd85bc8 100644 --- a/crates/smtp/src/reporting/mod.rs +++ b/crates/smtp/src/reporting/mod.rs @@ -114,7 +114,7 @@ impl SmtpReporting for Server { parent_session_id: u64, ) { // Build message - let mut message = self.new_message(from_addr.to_lowercase(), parent_session_id); + let mut message = self.new_message(from_addr, parent_session_id); for rcpt_ in rcpts { message.add_recipient(rcpt_.as_ref(), self).await; } @@ -161,7 +161,7 @@ impl SmtpReporting for Server { parent_session_id: u64, ) { // Build message - let mut message = self.new_message(from_addr.as_ref().to_lowercase(), parent_session_id); + let mut message = self.new_message(from_addr.as_ref(), parent_session_id); for rcpt in rcpts { message.add_recipient(rcpt, self).await; } diff --git a/crates/smtp/src/scripts/event_loop.rs b/crates/smtp/src/scripts/event_loop.rs index 8d1cf947..c788abe4 100644 --- a/crates/smtp/src/scripts/event_loop.rs +++ b/crates/smtp/src/scripts/event_loop.rs @@ -158,8 +158,7 @@ impl RunScript for Server { message_id, } => { // Build message - let mut message = - self.new_message(params.return_path.to_lowercase(), session_id); + let mut message = self.new_message(params.return_path.as_str(), session_id); match recipient { Recipient::Address(rcpt) => { message.add_recipient(rcpt, self).await; @@ -327,7 +326,7 @@ impl RunScript for Server { .message .recipients .into_iter() - .map(|r| trc::Value::from(r.address)) + .map(|r| trc::Value::from(r.address().to_string())) .collect::>(), ); } diff --git a/tests/src/smtp/inbound/dmarc.rs b/tests/src/smtp/inbound/dmarc.rs index 9e691b3c..768711bc 100644 --- a/tests/src/smtp/inbound/dmarc.rs +++ b/tests/src/smtp/inbound/dmarc.rs @@ -175,7 +175,7 @@ async fn dmarc() { // Expect SPF auth failure report let message = qr.expect_message().await; assert_eq!( - message.message.recipients.last().unwrap().address, + message.message.recipients.last().unwrap().address(), "spf-failures@example.com" ); message @@ -206,7 +206,7 @@ async fn dmarc() { // Expect DKIM auth failure report let message = qr.expect_message().await; assert_eq!( - message.message.recipients.last().unwrap().address, + message.message.recipients.last().unwrap().address(), "dkim-failures@example.com" ); message @@ -257,7 +257,7 @@ async fn dmarc() { // Expect DMARC auth failure report let message = qr.expect_message().await; assert_eq!( - message.message.recipients.last().unwrap().address, + message.message.recipients.last().unwrap().address(), "dmarc-failures@example.com" ); message diff --git a/tests/src/smtp/inbound/scripts.rs b/tests/src/smtp/inbound/scripts.rs index 2d1bf13a..641bb486 100644 --- a/tests/src/smtp/inbound/scripts.rs +++ b/tests/src/smtp/inbound/scripts.rs @@ -246,11 +246,11 @@ async fn sieve_scripts() { assert_eq!(notification.message.return_path, ""); assert_eq!(notification.message.recipients.len(), 2); assert_eq!( - notification.message.recipients.first().unwrap().address, + notification.message.recipients.first().unwrap().address(), "john@example.net" ); assert_eq!( - notification.message.recipients.last().unwrap().address, + notification.message.recipients.last().unwrap().address(), "jane@example.org" ); notification @@ -326,7 +326,7 @@ async fn sieve_scripts() { assert_eq!(redirect.message.return_path, ""); assert_eq!(redirect.message.recipients.len(), 1); assert_eq!( - redirect.message.recipients.first().unwrap().address, + redirect.message.recipients.first().unwrap().address(), "redirect@here.email" ); redirect @@ -354,7 +354,7 @@ async fn sieve_scripts() { assert_eq!(redirect.message.return_path, ""); assert_eq!(redirect.message.recipients.len(), 1); assert_eq!( - redirect.message.recipients.first().unwrap().address, + redirect.message.recipients.first().unwrap().address(), "redirect@somewhere.email" ); redirect diff --git a/tests/src/smtp/management/queue.rs b/tests/src/smtp/management/queue.rs index 824d5cc8..16eacd16 100644 --- a/tests/src/smtp/management/queue.rs +++ b/tests/src/smtp/management/queue.rs @@ -170,9 +170,9 @@ async fn manage_queue() { .message .recipients .into_iter() - .map(|r| r.address) + .map(|r| r.address().to_string()) .collect::>(), - vec!["success@foobar.org".to_string()] + vec!["success@foobar.org"] ); // Fetch and validate messages @@ -322,7 +322,7 @@ async fn manage_queue() { .message .recipients .into_iter() - .map(|r| r.address) + .map(|r| r.address().to_string()) .collect::>(), vec!["john@foobar.org".to_string()] ); diff --git a/tests/src/smtp/outbound/lmtp.rs b/tests/src/smtp/outbound/lmtp.rs index 6983495c..0c5b25c2 100644 --- a/tests/src/smtp/outbound/lmtp.rs +++ b/tests/src/smtp/outbound/lmtp.rs @@ -193,7 +193,7 @@ async fn lmtp_delivery() { .message .recipients .into_iter() - .map(|r| r.address) + .map(|r| r.address().to_string()) .collect::>(), vec![ "bill@foobar.org".to_string(), diff --git a/tests/src/smtp/outbound/smtp.rs b/tests/src/smtp/outbound/smtp.rs index 8e30cb15..abd67792 100644 --- a/tests/src/smtp/outbound/smtp.rs +++ b/tests/src/smtp/outbound/smtp.rs @@ -260,7 +260,7 @@ async fn smtp_delivery() { .message .recipients .into_iter() - .map(|r| r.address) + .map(|r| r.address().to_string()) .collect::>(); recipients.extend( remote @@ -270,7 +270,7 @@ async fn smtp_delivery() { .message .recipients .into_iter() - .map(|r| r.address), + .map(|r| r.address().to_string()), ); recipients.sort(); assert_eq!( diff --git a/tests/src/smtp/outbound/throttle.rs b/tests/src/smtp/outbound/throttle.rs index 34e28415..8dd64869 100644 --- a/tests/src/smtp/outbound/throttle.rs +++ b/tests/src/smtp/outbound/throttle.rs @@ -11,7 +11,7 @@ use crate::smtp::{ session::TestSession, }; use mail_auth::MX; -use smtp::queue::{DomainPart, Message, QueueEnvelope, Recipient, throttle::IsAllowed}; +use smtp::queue::{Message, QueueEnvelope, Recipient, throttle::IsAllowed}; use std::{ net::{IpAddr, Ipv4Addr}, time::{Duration, Instant}, @@ -286,7 +286,7 @@ impl<'x> TestQueueEnvelope<'x> for QueueEnvelope<'x> { mx, remote_ip: IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), local_ip: IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), - domain: rcpt.address.domain_part(), + domain: rcpt.domain_part(), rcpt, } } diff --git a/tests/src/smtp/queue/manager.rs b/tests/src/smtp/queue/manager.rs index 8bc9d9e0..a4be03eb 100644 --- a/tests/src/smtp/queue/manager.rs +++ b/tests/src/smtp/queue/manager.rs @@ -220,14 +220,14 @@ impl TestMessage for Message { fn rcpt(&self, name: &str) -> &Recipient { self.recipients .iter() - .find(|d| d.address == name) + .find(|d| d.address() == name) .unwrap_or_else(|| panic!("Expected rcpt {name} not found in {:?}", self.recipients)) } fn rcpt_mut(&mut self, name: &str) -> &mut Recipient { self.recipients .iter_mut() - .find(|d| d.address == name) + .find(|d| d.address() == name) .unwrap() } } diff --git a/tests/src/smtp/queue/retry.rs b/tests/src/smtp/queue/retry.rs index e17847b4..68ca626c 100644 --- a/tests/src/smtp/queue/retry.rs +++ b/tests/src/smtp/queue/retry.rs @@ -75,7 +75,7 @@ async fn queue_retry() { let message = qr.expect_message().await; assert_eq!(message.message.return_path, ""); assert_eq!( - message.message.recipients.first().unwrap().address, + message.message.recipients.first().unwrap().address(), "john@test.org" ); message diff --git a/tests/src/smtp/reporting/dmarc.rs b/tests/src/smtp/reporting/dmarc.rs index c16b64cb..565ed7e3 100644 --- a/tests/src/smtp/reporting/dmarc.rs +++ b/tests/src/smtp/reporting/dmarc.rs @@ -113,7 +113,7 @@ async fn report_dmarc() { qr.assert_no_events(); assert_eq!(message.message.recipients.len(), 1); assert_eq!( - message.message.recipients.last().unwrap().address, + message.message.recipients.last().unwrap().address(), "reports@foobar.net" ); assert_eq!(message.message.return_path, "reports@example.org"); diff --git a/tests/src/smtp/reporting/tls.rs b/tests/src/smtp/reporting/tls.rs index db910755..57f19f61 100644 --- a/tests/src/smtp/reporting/tls.rs +++ b/tests/src/smtp/reporting/tls.rs @@ -112,7 +112,7 @@ async fn report_tls() { // Expect report let message = qr.expect_message().await; assert_eq!( - message.message.recipients.last().unwrap().address, + message.message.recipients.last().unwrap().address(), "reports@foobar.org" ); assert_eq!(message.message.return_path, "reports@example.org"); diff --git a/tests/src/smtp/session.rs b/tests/src/smtp/session.rs index cf42f626..5a769e01 100644 --- a/tests/src/smtp/session.rs +++ b/tests/src/smtp/session.rs @@ -267,7 +267,7 @@ impl TestSession for Session { let rcpts = ["a@foobar.org", "b@test.net", "c@foobar.org", "d@test.net"]; for rcpt in &message.message.recipients { let idx = (rcpt.flags - 1) as usize; - assert_eq!(rcpts[idx], rcpt.address); + assert_eq!(rcpts[idx], rcpt.address()); } } }