|
|
|
@ -1,13 +1,11 @@
|
|
|
|
|
use lettre::smtp::authentication::Credentials;
|
|
|
|
|
use lettre::smtp::authentication::Mechanism as SmtpAuthMechanism;
|
|
|
|
|
use lettre::smtp::ConnectionReuseParameters;
|
|
|
|
|
use lettre::{
|
|
|
|
|
builder::{EmailBuilder, MimeMultipartType, PartBuilder},
|
|
|
|
|
ClientSecurity, ClientTlsParameters, SmtpClient, SmtpTransport, Transport,
|
|
|
|
|
};
|
|
|
|
|
use std::str::FromStr;
|
|
|
|
|
|
|
|
|
|
use lettre::message::{header, Mailbox, Message, MultiPart, SinglePart};
|
|
|
|
|
use lettre::transport::smtp::authentication::{Credentials, Mechanism as SmtpAuthMechanism};
|
|
|
|
|
use lettre::{Address, SmtpTransport, Tls, TlsParameters, Transport};
|
|
|
|
|
|
|
|
|
|
use native_tls::{Protocol, TlsConnector};
|
|
|
|
|
use percent_encoding::{percent_encode, NON_ALPHANUMERIC};
|
|
|
|
|
use quoted_printable::encode_to_str;
|
|
|
|
|
|
|
|
|
|
use crate::api::EmptyResult;
|
|
|
|
|
use crate::auth::{encode_jwt, generate_delete_claims, generate_invite_claims, generate_verify_email_claims};
|
|
|
|
@ -24,23 +22,23 @@ fn mailer() -> SmtpTransport {
|
|
|
|
|
.build()
|
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
|
|
let params = ClientTlsParameters::new(host.clone(), tls);
|
|
|
|
|
let params = TlsParameters::new(host.clone(), tls);
|
|
|
|
|
|
|
|
|
|
if CONFIG.smtp_explicit_tls() {
|
|
|
|
|
ClientSecurity::Wrapper(params)
|
|
|
|
|
Tls::Wrapper(params)
|
|
|
|
|
} else {
|
|
|
|
|
ClientSecurity::Required(params)
|
|
|
|
|
Tls::Required(params)
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
ClientSecurity::None
|
|
|
|
|
Tls::None
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
use std::time::Duration;
|
|
|
|
|
|
|
|
|
|
let smtp_client = SmtpClient::new((host.as_str(), CONFIG.smtp_port()), client_security).unwrap();
|
|
|
|
|
let smtp_client = SmtpTransport::new(host).port(CONFIG.smtp_port()).tls(client_security);
|
|
|
|
|
|
|
|
|
|
let smtp_client = match (&CONFIG.smtp_username(), &CONFIG.smtp_password()) {
|
|
|
|
|
(Some(user), Some(pass)) => smtp_client.credentials(Credentials::new(user.clone(), pass.clone())),
|
|
|
|
|
let smtp_client = match (CONFIG.smtp_username(), CONFIG.smtp_password()) {
|
|
|
|
|
(Some(user), Some(pass)) => smtp_client.credentials(Credentials::new(user, pass)),
|
|
|
|
|
_ => smtp_client,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
@ -48,19 +46,16 @@ fn mailer() -> SmtpTransport {
|
|
|
|
|
Some(mechanism) => {
|
|
|
|
|
let correct_mechanism = format!("\"{}\"", crate::util::upcase_first(mechanism.trim_matches('"')));
|
|
|
|
|
|
|
|
|
|
// TODO: Allow more than one mechanism
|
|
|
|
|
match serde_json::from_str::<SmtpAuthMechanism>(&correct_mechanism) {
|
|
|
|
|
Ok(auth_mechanism) => smtp_client.authentication_mechanism(auth_mechanism),
|
|
|
|
|
Ok(auth_mechanism) => smtp_client.authentication(vec![auth_mechanism]),
|
|
|
|
|
_ => panic!("Failure to parse mechanism. Is it proper Json? Eg. `\"Plain\"` not `Plain`"),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
_ => smtp_client,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
smtp_client
|
|
|
|
|
.smtp_utf8(true)
|
|
|
|
|
.timeout(Some(Duration::from_secs(CONFIG.smtp_timeout())))
|
|
|
|
|
.connection_reuse(ConnectionReuseParameters::NoReuse)
|
|
|
|
|
.transport()
|
|
|
|
|
smtp_client.timeout(Some(Duration::from_secs(CONFIG.smtp_timeout())))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn get_text(template_name: &'static str, data: serde_json::Value) -> Result<(String, String, String), Error> {
|
|
|
|
@ -283,38 +278,28 @@ fn send_email(address: &str, subject: &str, body_html: &str, body_text: &str) ->
|
|
|
|
|
|
|
|
|
|
let address = format!("{}@{}", address_split[1], domain_puny);
|
|
|
|
|
|
|
|
|
|
let html = PartBuilder::new()
|
|
|
|
|
.body(encode_to_str(body_html))
|
|
|
|
|
.header(("Content-Type", "text/html; charset=utf-8"))
|
|
|
|
|
.header(("Content-Transfer-Encoding", "quoted-printable"))
|
|
|
|
|
.build();
|
|
|
|
|
|
|
|
|
|
let text = PartBuilder::new()
|
|
|
|
|
.body(encode_to_str(body_text))
|
|
|
|
|
.header(("Content-Type", "text/plain; charset=utf-8"))
|
|
|
|
|
.header(("Content-Transfer-Encoding", "quoted-printable"))
|
|
|
|
|
.build();
|
|
|
|
|
|
|
|
|
|
let alternative = PartBuilder::new()
|
|
|
|
|
.message_type(MimeMultipartType::Alternative)
|
|
|
|
|
.child(text)
|
|
|
|
|
.child(html);
|
|
|
|
|
|
|
|
|
|
let email = EmailBuilder::new()
|
|
|
|
|
.to(address)
|
|
|
|
|
.from((CONFIG.smtp_from().as_str(), CONFIG.smtp_from_name().as_str()))
|
|
|
|
|
.subject(subject)
|
|
|
|
|
.child(alternative.build())
|
|
|
|
|
.build()
|
|
|
|
|
.map_err(|e| Error::new("Error building email", e.to_string()))?;
|
|
|
|
|
let html = SinglePart::builder()
|
|
|
|
|
.header(header::ContentType("text/html; charset=utf-8".parse().unwrap()))
|
|
|
|
|
.header(header::ContentTransferEncoding::QuotedPrintable)
|
|
|
|
|
.body(body_html);
|
|
|
|
|
|
|
|
|
|
let mut transport = mailer();
|
|
|
|
|
let text = SinglePart::builder()
|
|
|
|
|
.header(header::ContentType("text/plain; charset=utf-8".parse().unwrap()))
|
|
|
|
|
.header(header::ContentTransferEncoding::QuotedPrintable)
|
|
|
|
|
.body(body_text);
|
|
|
|
|
|
|
|
|
|
let result = transport.send(email);
|
|
|
|
|
let alternative = MultiPart::alternative().singlepart(text).singlepart(html);
|
|
|
|
|
|
|
|
|
|
let email = Message::builder()
|
|
|
|
|
.to(Mailbox::new(None, Address::from_str(&address)?))
|
|
|
|
|
.from(Mailbox::new(
|
|
|
|
|
Some(CONFIG.smtp_from_name()),
|
|
|
|
|
Address::from_str(&CONFIG.smtp_from())?,
|
|
|
|
|
))
|
|
|
|
|
.subject(subject)
|
|
|
|
|
.multipart(alternative)
|
|
|
|
|
.map_err(|e| Error::new("Error building email", e.to_string()))?;
|
|
|
|
|
|
|
|
|
|
// Explicitly close the connection, in case of error
|
|
|
|
|
transport.close();
|
|
|
|
|
|
|
|
|
|
result?;
|
|
|
|
|
let _ = mailer().send(&email)?;
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|