use std::time::Duration;
use lettre::message::{Mailbox, Message};
use lettre::transport::smtp::authentication::Credentials;
use lettre::transport::smtp::client::{Tls, TlsParameters};
use lettre::transport::smtp::{Error as SmtpError, SmtpTransport};
use lettre::{Address, Transport};
use super::generic::{GenericNotifier, Notification, DISPATCH_TIMEOUT_SECONDS};
use crate::config::config::ConfigNotify;
use crate::APP_CONF;
pub struct EmailNotifier;
impl GenericNotifier for EmailNotifier {
fn attempt(notify: &ConfigNotify, notification: &Notification) -> Result<(), bool> {
if let Some(ref email_config) = notify.email {
let nodes_label = notification.replicas.join(", ");
let mut message = String::new();
if notification.startup == true {
message.push_str(&format!(
"Status startup alert from: {}\n",
APP_CONF.branding.page_title
));
} else if notification.changed == true {
message.push_str(&format!(
"Status change report from: {}\n",
APP_CONF.branding.page_title
));
} else {
message.push_str(&format!(
"Status unchanged reminder from: {}\n",
APP_CONF.branding.page_title
));
}
message.push_str("\n--\n");
message.push_str(&format!("Status: {:?}\n", notification.status));
message.push_str(&format!("Nodes: {}\n", &nodes_label));
message.push_str(&format!("Time: {}\n", ¬ification.time));
message.push_str(&format!("URL: {}", APP_CONF.branding.page_url.as_str()));
message.push_str("\n--\n");
message.push_str("\n");
message.push_str("To unsubscribe, please edit your status page configuration.");
debug!("will send email notification with message: {}", &message);
let email_message = Message::builder()
.to(Mailbox::new(
None,
email_config.to.parse::<Address>().or(Err(true))?,
))
.from(Mailbox::new(
Some(APP_CONF.branding.page_title.to_owned()),
email_config.from.parse::<Address>().or(Err(true))?,
))
.subject(if nodes_label.is_empty() {
notification.status.as_str().to_uppercase()
} else {
format!(
"{} | {}",
notification.status.as_str().to_uppercase(),
&nodes_label
)
})
.body(message)
.or(Err(true))?;
let transport = match acquire_transport(
&email_config.smtp_host,
email_config.smtp_port,
email_config.smtp_username.to_owned(),
email_config.smtp_password.to_owned(),
email_config.smtp_encrypt,
) {
Ok(email_config) => email_config,
Err(err) => {
error!("failed to build email transport: {err}");
return Err(true);
}
};
if let Err(err) = transport.send(&email_message) {
error!("failed to send email: {err}");
return Err(true);
}
return Ok(());
}
Err(false)
}
fn can_notify(notify: &ConfigNotify, notification: &Notification) -> bool {
if let Some(ref email_config) = notify.email {
notification.expected(email_config.reminders_only)
} else {
false
}
}
fn name() -> &'static str {
"email"
}
}
fn acquire_transport(
smtp_host: &str,
smtp_port: u16,
smtp_username: Option<String>,
smtp_password: Option<String>,
smtp_encrypt: bool,
) -> Result<SmtpTransport, SmtpError> {
let credentials = if let (Some(smtp_username_value), Some(smtp_password_value)) =
(smtp_username, smtp_password)
{
Some(Credentials::new(
smtp_username_value.to_owned(),
smtp_password_value.to_owned(),
))
} else {
None
};
let tls_wrapper = match TlsParameters::new(smtp_host.into()) {
Ok(parameters) if smtp_encrypt => Tls::Required(parameters),
Ok(parameters) => Tls::Opportunistic(parameters),
Err(e) => return Err(e),
};
let mut mailer = SmtpTransport::builder_dangerous(smtp_host)
.port(smtp_port)
.tls(tls_wrapper)
.timeout(Some(Duration::from_secs(DISPATCH_TIMEOUT_SECONDS)));
if let Some(credentials_value) = credentials {
mailer = mailer.credentials(credentials_value);
}
Ok(mailer.build())
}