Files
PostfixAdminRS/src/mailer.rs
T
2026-11-03 16:52:28 +01:00

87 lines
2.5 KiB
Rust

use lettre::{
AsyncSmtpTransport, AsyncTransport, Message, Tokio1Executor,
message::Mailbox,
transport::smtp::{authentication::Credentials, extension::ClientId},
};
use crate::{
config::AppConfig,
error::{AppError, AppResult},
};
fn transport(config: &AppConfig) -> AppResult<AsyncSmtpTransport<Tokio1Executor>> {
let smtp = &config.smtp;
let mut builder = match smtp.kind.as_str() {
"starttls" => AsyncSmtpTransport::<Tokio1Executor>::starttls_relay(&smtp.host)
.map_err(|e| AppError::Mail(e.to_string()))?,
"tls" => AsyncSmtpTransport::<Tokio1Executor>::relay(&smtp.host)
.map_err(|e| AppError::Mail(e.to_string()))?,
_ => AsyncSmtpTransport::<Tokio1Executor>::builder_dangerous(&smtp.host),
}
.port(smtp.port);
if !smtp.client_name.trim().is_empty() {
builder = builder.hello_name(ClientId::Domain(smtp.client_name.trim().to_string()));
}
if !smtp.username.is_empty() {
builder = builder.credentials(Credentials::new(
smtp.username.clone(),
smtp.password.clone(),
));
}
Ok(builder.build())
}
pub fn sender_named(
config: &AppConfig,
fallback: &str,
display_name: Option<&str>,
) -> AppResult<Mailbox> {
let address = if config.smtp.admin_email.is_empty() {
fallback
} else {
&config.smtp.admin_email
};
let parsed = address
.parse()
.map_err(|e| AppError::bad_request(format!("invalid sender address: {e}")))?;
let name = display_name
.filter(|value| !value.trim().is_empty())
.unwrap_or(&config.smtp.admin_name);
Ok(Mailbox::new(Some(name.to_string()), parsed))
}
pub async fn send_text(
config: &AppConfig,
from_fallback: &str,
to: &str,
subject: &str,
body: &str,
) -> AppResult<()> {
send_text_named(config, from_fallback, None, to, subject, body).await
}
pub async fn send_text_named(
config: &AppConfig,
from_fallback: &str,
from_name: Option<&str>,
to: &str,
subject: &str,
body: &str,
) -> AppResult<()> {
let to: Mailbox = to
.parse()
.map_err(|e| AppError::bad_request(format!("invalid recipient: {e}")))?;
let message = Message::builder()
.from(sender_named(config, from_fallback, from_name)?)
.to(to)
.subject(subject)
.body(body.to_owned())
.map_err(|e| AppError::Mail(e.to_string()))?;
transport(config)?
.send(message)
.await
.map_err(|e| AppError::Mail(e.to_string()))?;
Ok(())
}