2019-09-30 10:52:00 +00:00
|
|
|
use std::path::PathBuf;
|
2019-03-04 16:57:36 +00:00
|
|
|
|
2019-03-13 09:32:21 +00:00
|
|
|
use failure;
|
2018-12-25 19:06:28 +00:00
|
|
|
use handlebars::Handlebars;
|
2019-05-24 21:23:30 +00:00
|
|
|
use lettre::{Transport as LettreTransport, SendmailTransport, file::FileTransport};
|
2019-06-06 16:17:58 +00:00
|
|
|
use lettre_email::{Mailbox,EmailBuilder};
|
2019-03-13 09:32:21 +00:00
|
|
|
use url;
|
2018-11-02 10:50:57 +00:00
|
|
|
use serde::Serialize;
|
2019-07-03 21:46:01 +00:00
|
|
|
use uuid::Uuid;
|
2019-09-02 20:49:02 +00:00
|
|
|
use crate::counters;
|
2018-11-02 10:50:57 +00:00
|
|
|
|
2019-09-02 20:48:13 +00:00
|
|
|
use rocket_i18n::I18n;
|
|
|
|
use gettext_macros::i18n;
|
|
|
|
|
2019-09-30 10:52:00 +00:00
|
|
|
use crate::template_helpers;
|
2019-09-27 20:12:36 +00:00
|
|
|
|
2019-09-02 20:49:02 +00:00
|
|
|
use crate::database::types::Email;
|
|
|
|
use crate::Result;
|
2018-11-02 10:50:57 +00:00
|
|
|
|
2019-03-11 15:34:18 +00:00
|
|
|
mod context {
|
|
|
|
#[derive(Serialize, Clone)]
|
|
|
|
pub struct Verification {
|
2019-09-27 20:12:36 +00:00
|
|
|
pub lang: String,
|
2019-03-11 16:02:45 +00:00
|
|
|
pub primary_fp: String,
|
2019-03-12 15:52:46 +00:00
|
|
|
pub uri: String,
|
2019-03-11 15:34:18 +00:00
|
|
|
pub userid: String,
|
2019-03-13 09:32:21 +00:00
|
|
|
pub base_uri: String,
|
2019-03-11 15:34:18 +00:00
|
|
|
pub domain: String,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Serialize, Clone)]
|
2019-04-05 15:07:40 +00:00
|
|
|
pub struct Manage {
|
2019-09-27 20:12:36 +00:00
|
|
|
pub lang: String,
|
2019-06-06 17:03:47 +00:00
|
|
|
pub primary_fp: String,
|
2019-03-12 15:52:46 +00:00
|
|
|
pub uri: String,
|
2019-03-13 09:32:21 +00:00
|
|
|
pub base_uri: String,
|
2019-03-11 15:34:18 +00:00
|
|
|
pub domain: String,
|
|
|
|
}
|
2019-07-15 22:42:07 +00:00
|
|
|
|
|
|
|
#[derive(Serialize, Clone)]
|
|
|
|
pub struct Welcome {
|
2019-09-27 20:12:36 +00:00
|
|
|
pub lang: String,
|
2019-07-15 22:42:07 +00:00
|
|
|
pub primary_fp: String,
|
|
|
|
pub uri: String,
|
|
|
|
pub base_uri: String,
|
|
|
|
pub domain: String,
|
|
|
|
}
|
2018-11-02 10:50:57 +00:00
|
|
|
}
|
|
|
|
|
2019-03-04 15:43:10 +00:00
|
|
|
pub struct Service {
|
2019-06-06 16:17:58 +00:00
|
|
|
from: Mailbox,
|
2019-03-13 08:56:33 +00:00
|
|
|
domain: String,
|
2019-03-04 15:43:10 +00:00
|
|
|
templates: Handlebars,
|
|
|
|
transport: Transport,
|
|
|
|
}
|
|
|
|
|
|
|
|
enum Transport {
|
|
|
|
Sendmail,
|
2019-03-04 16:57:36 +00:00
|
|
|
Filemail(PathBuf),
|
2019-03-04 15:43:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Service {
|
|
|
|
/// Sends mail via sendmail.
|
2019-08-28 18:33:24 +00:00
|
|
|
pub fn sendmail(from: String, base_uri: String, template_dir: PathBuf) -> Result<Self> {
|
|
|
|
Self::new(from, base_uri, template_dir, Transport::Sendmail)
|
2019-03-04 15:43:10 +00:00
|
|
|
}
|
|
|
|
|
2019-03-04 16:57:36 +00:00
|
|
|
/// Sends mail by storing it in the given directory.
|
2019-08-28 18:33:24 +00:00
|
|
|
pub fn filemail(from: String, base_uri: String, template_dir: PathBuf, path: PathBuf) -> Result<Self> {
|
|
|
|
Self::new(from, base_uri, template_dir, Transport::Filemail(path))
|
2019-03-13 09:32:21 +00:00
|
|
|
}
|
|
|
|
|
2019-08-28 18:33:24 +00:00
|
|
|
fn new(from: String, base_uri: String, template_dir: PathBuf, transport: Transport)
|
2019-03-13 09:32:21 +00:00
|
|
|
-> Result<Self> {
|
2019-09-30 10:52:00 +00:00
|
|
|
let templates = template_helpers::load_handlebars(template_dir)?;
|
2019-03-13 09:32:21 +00:00
|
|
|
let domain =
|
|
|
|
url::Url::parse(&base_uri)
|
|
|
|
?.host_str().ok_or_else(|| failure::err_msg("No host in base-URI"))
|
|
|
|
?.to_string();
|
2019-08-28 18:33:24 +00:00
|
|
|
Ok(Self { from: from.parse().unwrap(), domain, templates, transport })
|
2019-03-04 16:57:36 +00:00
|
|
|
}
|
|
|
|
|
2019-08-28 18:33:24 +00:00
|
|
|
pub fn send_verification(
|
|
|
|
&self,
|
|
|
|
i18n: &I18n,
|
|
|
|
base_uri: &str,
|
|
|
|
tpk_name: String,
|
|
|
|
userid: &Email,
|
|
|
|
token: &str
|
|
|
|
) -> Result<()> {
|
2019-03-11 15:34:18 +00:00
|
|
|
let ctx = context::Verification {
|
2019-09-27 20:12:36 +00:00
|
|
|
lang: i18n.lang.to_string(),
|
2019-04-27 22:05:49 +00:00
|
|
|
primary_fp: tpk_name,
|
2019-06-22 23:39:36 +00:00
|
|
|
uri: format!("{}/verify/{}", base_uri, token),
|
2019-03-11 15:34:18 +00:00
|
|
|
userid: userid.to_string(),
|
2019-06-22 23:39:36 +00:00
|
|
|
base_uri: base_uri.to_owned(),
|
2019-03-13 08:56:33 +00:00
|
|
|
domain: self.domain.clone(),
|
2019-03-04 15:43:10 +00:00
|
|
|
};
|
2018-12-25 19:06:28 +00:00
|
|
|
|
2019-07-20 15:00:02 +00:00
|
|
|
counters::inc_mail_sent("verify", userid);
|
2019-07-19 18:46:16 +00:00
|
|
|
|
2019-03-04 15:43:10 +00:00
|
|
|
self.send(
|
2019-05-05 12:58:05 +00:00
|
|
|
&vec![userid],
|
2019-09-02 20:48:13 +00:00
|
|
|
&i18n!(i18n.catalog, "Verify {} for your key on {}"; userid, self.domain),
|
2019-03-04 15:43:10 +00:00
|
|
|
"verify",
|
2019-08-28 18:33:24 +00:00
|
|
|
i18n.lang,
|
2019-03-04 15:43:10 +00:00
|
|
|
ctx,
|
2019-02-07 19:58:31 +00:00
|
|
|
)
|
2019-03-04 15:43:10 +00:00
|
|
|
}
|
2018-11-02 10:50:57 +00:00
|
|
|
|
2019-08-28 18:33:24 +00:00
|
|
|
pub fn send_manage_token(
|
|
|
|
&self,
|
|
|
|
i18n: &I18n,
|
|
|
|
base_uri: &str,
|
|
|
|
tpk_name: String,
|
|
|
|
recipient: &Email,
|
|
|
|
link_path: &str,
|
|
|
|
) -> Result<()> {
|
2019-04-05 15:07:40 +00:00
|
|
|
let ctx = context::Manage {
|
2019-09-27 20:12:36 +00:00
|
|
|
lang: i18n.lang.to_string(),
|
2019-06-06 17:03:47 +00:00
|
|
|
primary_fp: tpk_name,
|
2019-06-22 23:39:36 +00:00
|
|
|
uri: format!("{}{}", base_uri, link_path),
|
|
|
|
base_uri: base_uri.to_owned(),
|
2019-04-05 15:07:40 +00:00
|
|
|
domain: self.domain.clone(),
|
|
|
|
};
|
|
|
|
|
2019-07-20 15:00:02 +00:00
|
|
|
counters::inc_mail_sent("manage", recipient);
|
2019-07-19 18:46:16 +00:00
|
|
|
|
2019-04-05 15:07:40 +00:00
|
|
|
self.send(
|
2019-05-05 12:58:05 +00:00
|
|
|
&[recipient],
|
2019-09-02 20:48:13 +00:00
|
|
|
&i18n!(i18n.catalog, "Manage your key on {}"; self.domain),
|
2019-04-05 15:07:40 +00:00
|
|
|
"manage",
|
2019-08-28 18:33:24 +00:00
|
|
|
i18n.lang,
|
2019-04-05 15:07:40 +00:00
|
|
|
ctx,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2019-08-28 18:33:24 +00:00
|
|
|
pub fn send_welcome(
|
|
|
|
&self,
|
|
|
|
i18n: &I18n,
|
|
|
|
base_uri: &str,
|
|
|
|
tpk_name: String,
|
|
|
|
userid: &Email,
|
|
|
|
token: &str
|
|
|
|
) -> Result<()> {
|
2019-07-15 22:42:07 +00:00
|
|
|
let ctx = context::Welcome {
|
2019-09-27 20:12:36 +00:00
|
|
|
lang: i18n.lang.to_string(),
|
2019-07-15 22:42:07 +00:00
|
|
|
primary_fp: tpk_name,
|
|
|
|
uri: format!("{}/upload/{}", base_uri, token),
|
|
|
|
base_uri: base_uri.to_owned(),
|
|
|
|
domain: self.domain.clone(),
|
|
|
|
};
|
|
|
|
|
2019-07-20 15:00:02 +00:00
|
|
|
counters::inc_mail_sent("welcome", userid);
|
2019-07-19 18:46:16 +00:00
|
|
|
|
2019-07-15 22:42:07 +00:00
|
|
|
self.send(
|
|
|
|
&vec![userid],
|
2019-09-02 20:48:13 +00:00
|
|
|
&i18n!(i18n.catalog, "Your key upload on {}"; self.domain),
|
2019-07-15 22:42:07 +00:00
|
|
|
"welcome",
|
2019-08-28 18:33:24 +00:00
|
|
|
i18n.lang,
|
2019-07-15 22:42:07 +00:00
|
|
|
ctx,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2019-09-27 20:12:36 +00:00
|
|
|
fn render_template(
|
|
|
|
&self,
|
|
|
|
template: &str,
|
|
|
|
locale: &str,
|
2019-09-30 10:52:00 +00:00
|
|
|
ctx: impl Serialize
|
2019-09-27 20:12:36 +00:00
|
|
|
) -> Result<(String, String)> {
|
2019-08-28 18:33:24 +00:00
|
|
|
let html = self.templates.render(&format!("{}/{}.htm", locale, template), &ctx)
|
|
|
|
.or_else(|_| self.templates.render(&format!("{}.htm", template), &ctx))
|
|
|
|
.map_err(|_| failure::err_msg("Email template failed to render"))?;
|
|
|
|
let txt = self.templates.render(&format!("{}/{}.txt", locale, template), &ctx)
|
|
|
|
.or_else(|_| self.templates.render(&format!("{}.txt", template), &ctx))
|
|
|
|
.map_err(|_| failure::err_msg("Email template failed to render"))?;
|
|
|
|
|
|
|
|
Ok((html, txt))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn send(
|
|
|
|
&self,
|
|
|
|
to: &[&Email],
|
|
|
|
subject: &str,
|
|
|
|
template: &str,
|
|
|
|
locale: &str,
|
2019-09-30 10:52:00 +00:00
|
|
|
ctx: impl Serialize
|
2019-08-28 18:33:24 +00:00
|
|
|
) -> Result<()> {
|
|
|
|
let (html, txt) = self.render_template(template, locale, ctx)?;
|
2018-11-02 10:50:57 +00:00
|
|
|
|
2019-04-05 16:23:59 +00:00
|
|
|
if cfg!(debug_assertions) {
|
|
|
|
for recipient in to.iter() {
|
|
|
|
println!("To: {}", recipient.to_string());
|
|
|
|
}
|
2019-08-28 18:33:24 +00:00
|
|
|
println!("{}", &txt);
|
2019-04-05 16:23:59 +00:00
|
|
|
}
|
|
|
|
|
2019-05-24 21:23:30 +00:00
|
|
|
let email = EmailBuilder::new()
|
2019-03-04 15:43:10 +00:00
|
|
|
.from(self.from.clone())
|
|
|
|
.subject(subject)
|
2019-08-28 18:33:24 +00:00
|
|
|
.alternative(html, txt)
|
2019-07-03 21:46:01 +00:00
|
|
|
.message_id(format!("<{}@{}>", Uuid::new_v4(), self.domain));
|
2019-03-05 13:09:23 +00:00
|
|
|
|
2019-05-24 21:23:30 +00:00
|
|
|
let email = to.iter().fold(email, |email, to| email.to(to.to_string()));
|
2019-03-05 13:09:23 +00:00
|
|
|
|
2019-06-04 14:58:44 +00:00
|
|
|
let email = email.build()?;
|
2019-03-04 15:43:10 +00:00
|
|
|
|
|
|
|
match self.transport {
|
|
|
|
Transport::Sendmail => {
|
|
|
|
let mut transport = SendmailTransport::new();
|
2019-05-24 21:23:30 +00:00
|
|
|
transport.send(email.into())?;
|
2019-03-04 15:43:10 +00:00
|
|
|
},
|
2019-03-04 16:57:36 +00:00
|
|
|
Transport::Filemail(ref path) => {
|
2019-05-24 21:23:30 +00:00
|
|
|
let mut transport = FileTransport::new(path);
|
|
|
|
transport.send(email.into())?;
|
2019-03-04 16:57:36 +00:00
|
|
|
},
|
2019-03-04 15:43:10 +00:00
|
|
|
}
|
2018-11-02 10:50:57 +00:00
|
|
|
|
2019-03-04 15:43:10 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
2018-11-02 10:50:57 +00:00
|
|
|
}
|
2019-08-28 18:33:24 +00:00
|
|
|
|