MTA: Do not convert e-mail local parts to lowercase (fixes #1916)
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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<str>) -> 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<String>) -> 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<T: AsRef<str>> 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()
|
||||
|
||||
@@ -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<String>, span_id: u64) -> MessageWrapper;
|
||||
fn new_message(&self, return_path: impl AsRef<str>, span_id: u64) -> MessageWrapper;
|
||||
|
||||
fn next_event(&self, queue: &mut Queue) -> impl Future<Output = QueuedMessages> + Send;
|
||||
|
||||
@@ -68,7 +68,7 @@ pub trait SmtpSpool: Sync + Send {
|
||||
}
|
||||
|
||||
impl SmtpSpool for Server {
|
||||
fn new_message(&self, return_path: impl Into<String>, span_id: u64) -> MessageWrapper {
|
||||
fn new_message(&self, return_path: impl AsRef<str>, 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<String>, server: &Server) {
|
||||
pub async fn add_recipient(&mut self, rcpt: impl AsRef<str>, 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::<String, _>(
|
||||
@@ -515,11 +506,6 @@ impl MessageWrapper {
|
||||
recipient.queue = queue.virtual_queue;
|
||||
}
|
||||
|
||||
pub async fn add_recipient(&mut self, rcpt: impl AsRef<str>, 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<u64>) -> bool {
|
||||
// Release quota for completed deliveries
|
||||
let mut batch = BatchBuilder::new();
|
||||
|
||||
Reference in New Issue
Block a user