Queue autoconfig post install
This commit is contained in:
@@ -37,13 +37,9 @@ impl SendDsn for Server {
|
||||
if !message.message.return_path.is_empty() {
|
||||
// Build DSN
|
||||
if let Some(dsn) = message.build_dsn(self).await {
|
||||
let mut dsn_message = self.new_message("", "", "", message.span_id);
|
||||
let mut dsn_message = self.new_message("", message.span_id);
|
||||
dsn_message
|
||||
.add_recipient_parts(
|
||||
message.message.return_path.as_str(),
|
||||
message.message.return_path_lcase.as_str(),
|
||||
self,
|
||||
)
|
||||
.add_recipient_parts(message.message.return_path.as_str(), self)
|
||||
.await;
|
||||
|
||||
// Sign message
|
||||
@@ -81,7 +77,7 @@ impl SendDsn for Server {
|
||||
trc::event!(
|
||||
Delivery(trc::DeliveryEvent::DsnSuccess),
|
||||
SpanId = message.span_id,
|
||||
To = rcpt.address_lcase.clone(),
|
||||
To = rcpt.address.clone(),
|
||||
Hostname = response.hostname.clone(),
|
||||
Code = response.response.code,
|
||||
Details = response.response.message.to_string(),
|
||||
@@ -91,7 +87,7 @@ impl SendDsn for Server {
|
||||
trc::event!(
|
||||
Delivery(trc::DeliveryEvent::DsnTempFail),
|
||||
SpanId = message.span_id,
|
||||
To = rcpt.address_lcase.clone(),
|
||||
To = rcpt.address.clone(),
|
||||
Hostname = response.entity.clone(),
|
||||
Details = response.details.to_string(),
|
||||
NextRetry = trc::Value::Timestamp(rcpt.retry.due),
|
||||
@@ -105,7 +101,7 @@ impl SendDsn for Server {
|
||||
trc::event!(
|
||||
Delivery(trc::DeliveryEvent::DsnPermFail),
|
||||
SpanId = message.span_id,
|
||||
To = rcpt.address_lcase.clone(),
|
||||
To = rcpt.address.clone(),
|
||||
Hostname = response.entity.clone(),
|
||||
Details = response.details.to_string(),
|
||||
Total = rcpt.retry.inner,
|
||||
@@ -115,7 +111,7 @@ impl SendDsn for Server {
|
||||
trc::event!(
|
||||
Delivery(trc::DeliveryEvent::DsnTempFail),
|
||||
SpanId = message.span_id,
|
||||
To = rcpt.address_lcase.clone(),
|
||||
To = rcpt.address.clone(),
|
||||
Details = "Concurrency limited",
|
||||
NextRetry = trc::Value::Timestamp(rcpt.retry.due),
|
||||
Expires = rcpt
|
||||
|
||||
@@ -53,14 +53,12 @@ pub struct Message {
|
||||
pub created: u64,
|
||||
pub blob_hash: BlobHash,
|
||||
|
||||
pub return_path: String,
|
||||
pub recipients: Vec<Recipient>,
|
||||
|
||||
pub received_from_ip: IpAddr,
|
||||
pub received_via_port: u16,
|
||||
|
||||
pub return_path: String,
|
||||
pub return_path_lcase: String,
|
||||
pub return_path_domain: String,
|
||||
pub recipients: Vec<Recipient>,
|
||||
|
||||
pub flags: u64,
|
||||
pub env_id: Option<String>,
|
||||
pub priority: i16,
|
||||
@@ -105,7 +103,6 @@ pub enum QuotaKey {
|
||||
)]
|
||||
pub struct Recipient {
|
||||
pub address: String,
|
||||
pub address_lcase: String,
|
||||
|
||||
pub retry: Schedule<u32>,
|
||||
pub notify: Schedule<u32>,
|
||||
@@ -268,7 +265,7 @@ impl<'x> QueueEnvelope<'x> {
|
||||
pub fn new(message: &'x Message, rcpt: &'x Recipient) -> Self {
|
||||
Self {
|
||||
message,
|
||||
domain: rcpt.address_lcase.domain_part(),
|
||||
domain: rcpt.address.domain_part(),
|
||||
rcpt,
|
||||
mx: "",
|
||||
remote_ip: IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)),
|
||||
@@ -280,22 +277,24 @@ impl<'x> QueueEnvelope<'x> {
|
||||
impl<'x> ResolveVariable for QueueEnvelope<'x> {
|
||||
fn resolve_variable(&self, variable: u32) -> expr::Variable<'x> {
|
||||
match variable {
|
||||
V_SENDER => self.message.return_path_lcase.as_str().into(),
|
||||
V_SENDER_DOMAIN => self.message.return_path_domain.as_str().into(),
|
||||
V_SENDER => self.message.return_path.as_str().into(),
|
||||
V_SENDER_DOMAIN => self.message.return_path.domain_part().into(),
|
||||
V_RECIPIENT_DOMAIN => self.domain.into(),
|
||||
V_RECIPIENT => self.rcpt.address_lcase.as_str().into(),
|
||||
V_RECIPIENT => self.rcpt.address.as_str().into(),
|
||||
V_RECIPIENTS => self
|
||||
.message
|
||||
.recipients
|
||||
.iter()
|
||||
.map(|r| Variable::from(r.address_lcase.as_str()))
|
||||
.map(|r| Variable::from(r.address.as_str()))
|
||||
.collect::<Vec<_>>()
|
||||
.into(),
|
||||
V_QUEUE_RETRY_NUM => self.rcpt.retry.inner.into(),
|
||||
V_QUEUE_NOTIFY_NUM => self.rcpt.notify.inner.into(),
|
||||
V_QUEUE_EXPIRES_IN => match &self.rcpt.expires {
|
||||
QueueExpiry::Ttl(time) => (*time + self.message.created).saturating_sub(now()),
|
||||
QueueExpiry::Attempts(count) => (*count) as u64,
|
||||
QueueExpiry::Attempts(count) => {
|
||||
(count.saturating_sub(self.rcpt.retry.inner)) as u64
|
||||
}
|
||||
}
|
||||
.into(),
|
||||
V_QUEUE_LAST_STATUS => self.rcpt.status.to_compact_string().into(),
|
||||
@@ -353,12 +352,12 @@ impl<'x> ResolveVariable for QueueEnvelope<'x> {
|
||||
impl ResolveVariable for Message {
|
||||
fn resolve_variable(&self, variable: u32) -> expr::Variable<'_> {
|
||||
match variable {
|
||||
V_SENDER => self.return_path_lcase.as_str().into(),
|
||||
V_SENDER_DOMAIN => self.return_path_domain.as_str().into(),
|
||||
V_SENDER => self.return_path.as_str().into(),
|
||||
V_SENDER_DOMAIN => self.return_path.domain_part().into(),
|
||||
V_RECIPIENTS => self
|
||||
.recipients
|
||||
.iter()
|
||||
.map(|r| Variable::from(r.address_lcase.as_str()))
|
||||
.map(|r| Variable::from(r.address.as_str()))
|
||||
.collect::<Vec<_>>()
|
||||
.into(),
|
||||
V_PRIORITY => self.priority.into(),
|
||||
|
||||
@@ -64,7 +64,7 @@ impl HasQueueQuota for Server {
|
||||
let mut seen_domains = AHashSet::new();
|
||||
for quota in &self.core.smtp.queue.quota.rcpt_domain {
|
||||
for (rcpt_idx, rcpt) in message.message.recipients.iter().enumerate() {
|
||||
if seen_domains.insert(rcpt.address_lcase.domain_part())
|
||||
if seen_domains.insert(rcpt.address.domain_part())
|
||||
&& !self
|
||||
.check_quota(
|
||||
quota,
|
||||
@@ -192,7 +192,7 @@ impl MessageWrapper {
|
||||
&rcpt.status,
|
||||
Status::Completed(_) | Status::PermanentFailure(_)
|
||||
) {
|
||||
if seen_domains.insert(rcpt.address_lcase.domain_part()) {
|
||||
if seen_domains.insert(rcpt.address.domain_part()) {
|
||||
quota_ids.push(((pos + 1) as u64) << 32);
|
||||
}
|
||||
quota_ids.push((pos + 1) as u64);
|
||||
|
||||
@@ -39,13 +39,7 @@ pub struct QueuedMessages {
|
||||
}
|
||||
|
||||
pub trait SmtpSpool: Sync + Send {
|
||||
fn new_message(
|
||||
&self,
|
||||
return_path: impl Into<String>,
|
||||
return_path_lcase: impl Into<String>,
|
||||
return_path_domain: impl Into<String>,
|
||||
span_id: u64,
|
||||
) -> MessageWrapper;
|
||||
fn new_message(&self, return_path: impl Into<String>, span_id: u64) -> MessageWrapper;
|
||||
|
||||
fn next_event(&self, queue: &mut Queue) -> impl Future<Output = QueuedMessages> + Send;
|
||||
|
||||
@@ -74,13 +68,7 @@ pub trait SmtpSpool: Sync + Send {
|
||||
}
|
||||
|
||||
impl SmtpSpool for Server {
|
||||
fn new_message(
|
||||
&self,
|
||||
return_path: impl Into<String>,
|
||||
return_path_lcase: impl Into<String>,
|
||||
return_path_domain: impl Into<String>,
|
||||
span_id: u64,
|
||||
) -> MessageWrapper {
|
||||
fn new_message(&self, return_path: impl Into<String>, span_id: u64) -> MessageWrapper {
|
||||
let created = SystemTime::now()
|
||||
.duration_since(SystemTime::UNIX_EPOCH)
|
||||
.map_or(0, |d| d.as_secs());
|
||||
@@ -93,8 +81,6 @@ impl SmtpSpool for Server {
|
||||
message: Message {
|
||||
created,
|
||||
return_path: return_path.into(),
|
||||
return_path_lcase: return_path_lcase.into(),
|
||||
return_path_domain: return_path_domain.into(),
|
||||
recipients: Vec::with_capacity(1),
|
||||
flags: 0,
|
||||
env_id: None,
|
||||
@@ -389,7 +375,7 @@ impl MessageWrapper {
|
||||
.message
|
||||
.recipients
|
||||
.iter()
|
||||
.map(|r| trc::Value::String(r.address_lcase.as_str().into()))
|
||||
.map(|r| trc::Value::String(r.address.as_str().into()))
|
||||
.collect::<Vec<_>>(),
|
||||
Size = self.message.size,
|
||||
NextRetry = self
|
||||
@@ -492,16 +478,10 @@ impl MessageWrapper {
|
||||
true
|
||||
}
|
||||
|
||||
pub async fn add_recipient_parts(
|
||||
&mut self,
|
||||
rcpt: impl Into<String>,
|
||||
rcpt_lcase: impl Into<String>,
|
||||
server: &Server,
|
||||
) {
|
||||
pub async fn add_recipient_parts(&mut self, rcpt: impl Into<String>, server: &Server) {
|
||||
// Resolve queue
|
||||
self.message.recipients.push(Recipient {
|
||||
address: rcpt.into(),
|
||||
address_lcase: rcpt_lcase.into(),
|
||||
status: Status::Scheduled,
|
||||
flags: 0,
|
||||
orcpt: None,
|
||||
@@ -530,10 +510,9 @@ impl MessageWrapper {
|
||||
recipient.queue = queue.virtual_queue;
|
||||
}
|
||||
|
||||
pub async fn add_recipient(&mut self, rcpt: impl Into<String>, server: &Server) {
|
||||
let rcpt = rcpt.into();
|
||||
let rcpt_lcase = rcpt.to_lowercase();
|
||||
self.add_recipient_parts(rcpt, rcpt_lcase, server).await;
|
||||
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 {
|
||||
@@ -691,7 +670,7 @@ impl MessageWrapper {
|
||||
|
||||
pub fn has_domain(&self, domains: &[String]) -> bool {
|
||||
self.message.recipients.iter().any(|r| {
|
||||
let domain = r.address_lcase.domain_part();
|
||||
let domain = r.address.domain_part();
|
||||
domains.iter().any(|dd| dd == domain)
|
||||
}) || self
|
||||
.message
|
||||
@@ -704,7 +683,7 @@ impl MessageWrapper {
|
||||
impl ArchivedMessage {
|
||||
pub fn has_domain(&self, domains: &[String]) -> bool {
|
||||
self.recipients.iter().any(|r| {
|
||||
let domain = r.address_lcase.domain_part();
|
||||
let domain = r.address.domain_part();
|
||||
domains.iter().any(|dd| dd == domain)
|
||||
}) || self
|
||||
.return_path
|
||||
@@ -712,22 +691,22 @@ impl ArchivedMessage {
|
||||
.is_some_and(|(_, domain)| domains.iter().any(|dd| dd == domain))
|
||||
}
|
||||
|
||||
pub fn next_delivery_event(&self) -> u64 {
|
||||
let mut next_delivery = now();
|
||||
pub fn next_delivery_event(&self, queue: Option<QueueName>) -> Option<u64> {
|
||||
let mut next_delivery = None;
|
||||
|
||||
for (pos, rcpt) in self
|
||||
.recipients
|
||||
.iter()
|
||||
.filter(|d| {
|
||||
matches!(
|
||||
d.status,
|
||||
ArchivedStatus::Scheduled | ArchivedStatus::TemporaryFailure(_)
|
||||
)
|
||||
})
|
||||
.enumerate()
|
||||
{
|
||||
if pos == 0 || rcpt.retry.due < next_delivery {
|
||||
next_delivery = rcpt.retry.due.into();
|
||||
for rcpt in self.recipients.iter().filter(|d| {
|
||||
matches!(
|
||||
d.status,
|
||||
ArchivedStatus::Scheduled | ArchivedStatus::TemporaryFailure(_)
|
||||
) && queue.is_none_or(|q| d.queue == q)
|
||||
}) {
|
||||
let retry_due = rcpt.retry.due.to_native();
|
||||
if let Some(next_delivery) = &mut next_delivery {
|
||||
if retry_due < *next_delivery {
|
||||
*next_delivery = retry_due;
|
||||
}
|
||||
} else {
|
||||
next_delivery = Some(retry_due);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user