use std::path::PathBuf; use handlebars::Handlebars; use lettre::{EmailTransport, SendmailTransport, FileEmailTransport}; use lettre_email::EmailBuilder; use serde::Serialize; use database::types::Email; use Result; mod context { #[derive(Serialize, Clone)] pub struct Verification { pub token: String, pub userid: String, pub domain: String, } #[derive(Serialize, Clone)] pub struct Deletion { pub token: String, pub domain: String, } } pub struct Service { from: String, templates: Handlebars, transport: Transport, } enum Transport { Sendmail, Filemail(PathBuf), } impl Service { /// Sends mail via sendmail. pub fn sendmail(from: String, templates: Handlebars) -> Self { Self { from: from, templates: templates, transport: Transport::Sendmail, } } /// Sends mail by storing it in the given directory. pub fn filemail(from: String, templates: Handlebars, path: PathBuf) -> Self { Self { from: from, templates: templates, transport: Transport::Filemail(path), } } pub fn send_verification(&self, userid: &Email, token: &str, domain: &str) -> Result<()> { let ctx = context::Verification { token: token.to_string(), userid: userid.to_string(), domain: domain.to_string(), }; self.send( &vec![userid.clone()], "Please verify your email address", "verify", ctx, ) } pub fn send_confirmation(&self, userids: &[Email], token: &str, domain: &str) -> Result<()> { let ctx = context::Deletion { token: token.to_string(), domain: domain.to_string(), }; self.send( userids, "Please confirm deletion of your key", "confirm", ctx, ) } fn send(&self, to: &[Email], subject: &str, template: &str, ctx: T) -> Result<()> where T: Serialize + Clone, { let tmpl_html = format!("{}-html", template); let tmpl_txt = format!("{}-txt", template); let (html, txt) = { if let (Ok(inner_html), Ok(inner_txt)) = ( self.templates.render(&tmpl_html, &ctx), self.templates.render(&tmpl_txt, &ctx), ) { (Some(inner_html), Some(inner_txt)) } else { (None, None) } }; let mut email = EmailBuilder::new() .from(self.from.clone()) .subject(subject) .alternative( html.ok_or(failure::err_msg("Email template failed to render"))?, txt.ok_or(failure::err_msg("Email template failed to render"))?, ); for recipient in to.iter() { email.add_to(recipient.to_string()); } let email = email .build() .unwrap(); match self.transport { Transport::Sendmail => { let mut transport = SendmailTransport::new(); transport.send(&email)?; }, Transport::Filemail(ref path) => { let mut transport = FileEmailTransport::new(path); transport.send(&email)?; }, } Ok(()) } }