docserver/src/defs/mailer.rs

152 lines
5.1 KiB
Rust
Raw Normal View History

2023-07-19 04:00:41 +01:00
use lettre::{
transport::smtp::authentication::Credentials,
AsyncSmtpTransport,
AsyncTransport,
Tokio1Executor,
// Address,
message::{
Mailbox,
header::ContentType,
MultiPart,
SinglePart,
},
Message,
};
use crate::defs::AppDBs;
use pasetoken_lib::ConfigPaSeToken;
use serde_json::json;
#[derive(Clone,Debug)]
pub struct MailMessage {
pub from: Mailbox,
pub to: Mailbox,
pub reply_to: Mailbox,
}
impl MailMessage {
pub fn new(from: &str, to: &str, reply: &str) -> anyhow::Result<Self> {
let reply_to = if reply.is_empty() {
to
} else {
reply
};
Ok(
Self {
from: from.parse()?,
to: to.parse()?,
reply_to: reply_to.parse()?,
}
)
}
pub fn check(app_dbs: &AppDBs) -> String {
if app_dbs.config.smtp.is_empty() {
String::from("Error: no mail server")
} else if app_dbs.config.mail_from.is_empty() {
String::from("Error: no mail from address")
} else {
String::from("")
}
}
#[allow(dead_code)]
pub async fn send_message(&self, subject: &str, body: &str, app_dbs: &AppDBs) -> std::io::Result<()> {
match Message::builder()
.from(self.from.to_owned())
.reply_to(self.reply_to.to_owned())
.to(self.to.to_owned())
.subject(subject)
.header(ContentType::TEXT_PLAIN)
.body(body.to_owned())
{
Ok(message) => self.mail_message(message, app_dbs).await,
Err(e) =>
return Err(std::io::Error::new(
std::io::ErrorKind::NotFound,
format!("ERROR: Invalid mail: {}",e)
))
}
}
pub async fn send_html_message(&self, subject: &str, body: &str, html_body: &str, app_dbs: &AppDBs) -> std::io::Result<()> {
match Message::builder()
.from(self.from.to_owned())
.reply_to(self.reply_to.to_owned())
.to(self.to.to_owned())
.subject(subject)
.multipart(
MultiPart::alternative() // This is composed of two parts.
.singlepart(
SinglePart::builder()
.header(ContentType::TEXT_PLAIN)
.body(body.to_owned()),
)
.singlepart(
SinglePart::builder()
.header(ContentType::TEXT_HTML)
.body(html_body.to_owned()),
),
)
{
Ok(message) => self.mail_message(message, app_dbs).await,
Err(e) =>
return Err(std::io::Error::new(
std::io::ErrorKind::NotFound,
format!("ERROR: Invalid mail: {}",e)
))
}
}
pub async fn mail_message(&self, message: Message, app_dbs: &AppDBs) -> std::io::Result<()> {
let mail_cred = crate::defs::MailMessage::get_credentials(&app_dbs.config.smtp_auth, &app_dbs.config.paseto);
if ! mail_cred.contains("|") {
return Err(std::io::Error::new(
std::io::ErrorKind::NotFound,
format!("ERROR: Invalid mail credentials")
));
}
let auth_data: Vec<String> = mail_cred.split("|").map(|s| s.to_string()).collect();
if auth_data.len() < 2 {
return Err(std::io::Error::new(
std::io::ErrorKind::NotFound,
format!("ERROR: Invalid mail credentials")
));
}
let creds = Credentials::new(auth_data[0].to_owned(), auth_data[1].to_owned());
// Open a remote connection to gmail
let mailer: AsyncSmtpTransport<Tokio1Executor> =
AsyncSmtpTransport::<Tokio1Executor>::relay(&app_dbs.config.smtp)
.unwrap()
.credentials(creds)
.build();
// Send the email
match mailer.send(message).await {
Ok(_) => Ok(()),
Err(e) => Err(std::io::Error::new(
std::io::ErrorKind::NotConnected,
format!("ERROR: Could not send email: {e:?}")
))
}
}
pub fn get_credentials(token: &str, paseto_config: &ConfigPaSeToken) -> String {
match paseto_config.pasetoken() {
Ok(paseto) => {
match paseto.trusted(token, false) {
Ok(trusted_token) => {
if let Some(claims) = trusted_token.payload_claims() {
claims.get_claim("smtp_auth").unwrap_or(&json!("")).to_string().replace("\"","")
} else {
String::from("")
}
},
Err(e) => {
println!("Token not trusted: {}",e);
String::from("")
},
}
},
Err(e) => {
println!("Error collecting notify data: {}",e);
String::from("")
}
}
}
}