/* * SPDX-FileCopyrightText: 2020 Stalwart Labs LLC * * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL */ use crate::{ inbound::dkim::DkimSign, queue::{ MessageSource, spool::{QueueParams, SmtpSpool}, }, }; use common::{Server, expr::if_block::IfBlock, ipc::ReportingEvent}; pub trait MtaReportSend: Sync + Send { fn is_local_report_domain( &self, domain: &str, session_id: u64, ) -> impl Future + Send; fn send_report( &self, from_addr: &str, rcpts: impl Iterator + Sync + Send> + Sync + Send, report: Vec, sign_config: &IfBlock, deliver_now: bool, parent_session_id: u64, ) -> impl Future + Send; fn send_autogenerated( &self, from_addr: impl AsRef + Sync + Send, rcpts: impl Iterator + Sync + Send> + Sync + Send, raw_message: Vec, sign_config: Option<&IfBlock>, parent_session_id: u64, ) -> impl Future + Send; fn schedule_report( &self, report: impl Into + Sync + Send, ) -> impl Future + Send; } impl MtaReportSend for Server { async fn is_local_report_domain(&self, domain: &str, session_id: u64) -> bool { match self.domain(domain).await { Ok(domain) => domain.is_some(), Err(err) => { trc::error!( err.caused_by(trc::location!()) .span_id(session_id) .details("Failed to lookup local domain") ); false } } } async fn send_report( &self, from_addr: &str, rcpts: impl Iterator + Sync + Send> + Sync + Send, report: Vec, sign_config: &IfBlock, deliver_now: bool, parent_session_id: u64, ) { // Build message let mut message = self.new_message(from_addr, MessageSource::Report, parent_session_id); for rcpt_ in rcpts { message.add_expanded_recipient(rcpt_.as_ref(), self).await; } // Schedule delivery at a random time between now and the next 3 hours if !deliver_now { #[cfg(not(feature = "test_mode"))] { use common::config::smtp::queue::QueueExpiry; use rand::RngExt; let delivery_time = rand::rng().random_range(0u64..10800u64); for rcpt in &mut message.message.recipients { rcpt.retry.due += delivery_time; rcpt.notify.due += delivery_time; if let QueueExpiry::Ttl(expires) = &mut rcpt.expires { *expires += delivery_time; } } } } // Queue message let dkim_signers = self .eval_signers(sign_config, &message.message, parent_session_id) .await; message .queue( QueueParams::new(&report, parent_session_id, self).with_dkim_signers(dkim_signers), ) .await; } async fn send_autogenerated( &self, from_addr: impl AsRef + Sync + Send, rcpts: impl Iterator + Sync + Send> + Sync + Send, raw_message: Vec, sign_config: Option<&IfBlock>, parent_session_id: u64, ) { // Build message let mut message = self.new_message( from_addr.as_ref(), MessageSource::Autogenerated, parent_session_id, ); for rcpt in rcpts { message.add_expanded_recipient(rcpt, self).await; } // Queue message let dkim_signers = if let Some(sign_config) = sign_config { self.eval_signers(sign_config, &message.message, parent_session_id) .await } else { None }; message .queue( QueueParams::new(&raw_message, parent_session_id, self) .with_dkim_signers(dkim_signers), ) .await; } async fn schedule_report(&self, report: impl Into + Sync + Send) { if self.inner.ipc.report_tx.send(report.into()).await.is_err() { trc::event!( Server(trc::ServerEvent::ThreadError), CausedBy = trc::location!(), Details = "Failed to send event to ReportScheduler" ); } } }