diff --git a/Cargo.lock b/Cargo.lock index d7a241a9..4cad696a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -27,12 +27,6 @@ dependencies = [ "nodrop", ] -[[package]] -name = "ascii_utils" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71938f30533e4d95a6d17aa530939da3842c2ab6f4f84b9dae68447e4129f74a" - [[package]] name = "atty" version = "0.2.14" @@ -109,6 +103,12 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7" +[[package]] +name = "base64" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d5ca2cd0adc3f48f9e9ea5a6bbdf9ccc0bfade884847e484d452414c7ccffb3" + [[package]] name = "binascii" version = "0.1.4" @@ -148,7 +148,6 @@ dependencies = [ "once_cell", "openssl", "percent-encoding 2.1.0", - "quoted_printable", "regex", "reqwest", "ring", @@ -482,85 +481,6 @@ version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4358a9e11b9a09cf52383b451b49a169e8d797b68aa02301ff586d70d9661ea3" -[[package]] -name = "email" -version = "0.0.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91549a51bb0241165f13d57fc4c72cef063b4088fb078b019ecbf464a45f22e4" -dependencies = [ - "base64 0.9.3", - "chrono", - "encoding", - "lazy_static", - "rand 0.4.6", - "time 0.1.43", - "version_check 0.1.5", -] - -[[package]] -name = "encoding" -version = "0.2.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b0d943856b990d12d3b55b359144ff341533e516d94098b1d3fc1ac666d36ec" -dependencies = [ - "encoding-index-japanese", - "encoding-index-korean", - "encoding-index-simpchinese", - "encoding-index-singlebyte", - "encoding-index-tradchinese", -] - -[[package]] -name = "encoding-index-japanese" -version = "1.20141219.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04e8b2ff42e9a05335dbf8b5c6f7567e5591d0d916ccef4e0b1710d32a0d0c91" -dependencies = [ - "encoding_index_tests", -] - -[[package]] -name = "encoding-index-korean" -version = "1.20141219.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dc33fb8e6bcba213fe2f14275f0963fd16f0a02c878e3095ecfdf5bee529d81" -dependencies = [ - "encoding_index_tests", -] - -[[package]] -name = "encoding-index-simpchinese" -version = "1.20141219.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d87a7194909b9118fc707194baa434a4e3b0fb6a5a757c73c3adb07aa25031f7" -dependencies = [ - "encoding_index_tests", -] - -[[package]] -name = "encoding-index-singlebyte" -version = "1.20141219.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3351d5acffb224af9ca265f435b859c7c01537c0849754d3db3fdf2bfe2ae84a" -dependencies = [ - "encoding_index_tests", -] - -[[package]] -name = "encoding-index-tradchinese" -version = "1.20141219.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd0e20d5688ce3cab59eb3ef3a2083a5c77bf496cb798dc6fcdb75f323890c18" -dependencies = [ - "encoding_index_tests", -] - -[[package]] -name = "encoding_index_tests" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a246d82be1c9d791c5dfde9a2bd045fc3cbba3fa2b11ad558f27d01712f00569" - [[package]] name = "encoding_rs" version = "0.8.22" @@ -585,15 +505,6 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" -[[package]] -name = "fast_chemail" -version = "0.9.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "495a39d30d624c2caabe6312bfead73e7717692b44e0b32df168c275a2e8e9e4" -dependencies = [ - "ascii_utils", -] - [[package]] name = "fern" version = "0.6.0" @@ -888,17 +799,6 @@ dependencies = [ "digest 0.8.1", ] -[[package]] -name = "hostname" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" -dependencies = [ - "libc", - "match_cfg", - "winapi 0.3.8", -] - [[package]] name = "html5ever" version = "0.22.5" @@ -1008,6 +908,24 @@ dependencies = [ "tokio-tls", ] +[[package]] +name = "hyperx" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81d7ed6ec7d25c4de28b999a5693f14609a8b756137b1b4cb4927d119f59ef25" +dependencies = [ + "base64 0.11.0", + "bytes 0.5.4", + "http", + "httparse", + "language-tags", + "log 0.4.8", + "mime 0.3.16", + "percent-encoding 2.1.0", + "time 0.1.43", + "unicase 2.6.0", +] + [[package]] name = "idna" version = "0.1.5" @@ -1128,20 +1046,21 @@ checksum = "b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f" [[package]] name = "lettre" version = "0.10.0-pre" -source = "git+https://github.com/lettre/lettre?rev=245c600c82ee18b766e8729f005ff453a55dce34#245c600c82ee18b766e8729f005ff453a55dce34" +source = "git+https://github.com/lettre/lettre?rev=88df2a502d537ebfdd4218fb6b9f4168fb0f7943#88df2a502d537ebfdd4218fb6b9f4168fb0f7943" dependencies = [ - "base64 0.11.0", + "base64 0.12.0", "bufstream", - "email", - "fast_chemail", - "hostname", - "log 0.4.8", + "hyperx", + "idna 0.2.0", + "line-wrap", "mime 0.3.16", "native-tls", "nom", + "once_cell", + "quoted_printable", + "regex", "serde", - "serde_json", - "time 0.2.14", + "textnonce", "uuid", ] @@ -1175,6 +1094,15 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "line-wrap" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f30344350a2a51da54c1d53be93fade8a237e545dbcc4bdbe635413f2117cab9" +dependencies = [ + "safemem", +] + [[package]] name = "lock_api" version = "0.3.4" @@ -1230,12 +1158,6 @@ dependencies = [ "tendril", ] -[[package]] -name = "match_cfg" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" - [[package]] name = "matches" version = "0.1.8" @@ -2725,6 +2647,19 @@ dependencies = [ "utf-8", ] +[[package]] +name = "textnonce" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acc659075a12c12c07bbb384862c352506707f6597f5b495f65427d08519b617" +dependencies = [ + "base64 0.12.0", + "byteorder", + "chrono", + "rand 0.7.3", + "serde", +] + [[package]] name = "textwrap" version = "0.11.0" diff --git a/Cargo.toml b/Cargo.toml index ebbfd34a..6a2a7836 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -92,9 +92,8 @@ num-traits = "0.2.11" num-derive = "0.3.0" # Email libraries -lettre = "0.10.0-pre" +lettre = { version = "0.10.0-pre", features = ["smtp-transport", "builder", "serde", "native-tls"], default-features = false } native-tls = "0.2.4" -quoted_printable = "0.4.2" # Template library handlebars = { version = "3.0.1", features = ["dir_source"] } @@ -124,7 +123,7 @@ rocket = { git = 'https://github.com/SergioBenitez/Rocket', rev = '1010f6a2a88fa rocket_contrib = { git = 'https://github.com/SergioBenitez/Rocket', rev = '1010f6a2a88fac899dec0cd2f642156908038a53' } # Use git version for timeout fix #706 -lettre = { git = 'https://github.com/lettre/lettre', rev = '245c600c82ee18b766e8729f005ff453a55dce34' } +lettre = { git = 'https://github.com/lettre/lettre', rev = '88df2a502d537ebfdd4218fb6b9f4168fb0f7943' } # For favicon extraction from main website data-url = { git = 'https://github.com/servo/rust-url', package="data-url", rev = '7f1bd6ce1c2fde599a757302a843a60e714c5f72' } diff --git a/src/error.rs b/src/error.rs index edfdbc42..ecc47d32 100644 --- a/src/error.rs +++ b/src/error.rs @@ -45,7 +45,9 @@ use std::option::NoneError as NoneErr; use std::time::SystemTimeError as TimeErr; use u2f::u2ferror::U2fError as U2fErr; use yubico::yubicoerror::YubicoError as YubiErr; -use lettre::smtp::error::Error as LettreErr; +use lettre::error::Error as LettreErr; +use lettre::address::AddressError as AddrErr; +use lettre::transport::smtp::error::Error as SmtpErr; #[derive(Serialize)] pub struct Empty {} @@ -73,7 +75,9 @@ make_error! { ReqError(ReqErr): _has_source, _api_error, RegexError(RegexErr): _has_source, _api_error, YubiError(YubiErr): _has_source, _api_error, - LetreErr(LettreErr): _has_source, _api_error, + LetreError(LettreErr):_has_source, _api_error, + AddressError(AddrErr):_has_source, _api_error, + SmtpError(SmtpErr): _has_source, _api_error, } // This is implemented by hand because NoneError doesn't implement neither Display nor Error diff --git a/src/mail.rs b/src/mail.rs index c25e31e7..9c77b5f0 100644 --- a/src/mail.rs +++ b/src/mail.rs @@ -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::(&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 html = SinglePart::builder() + .header(header::ContentType("text/html; charset=utf-8".parse().unwrap())) + .header(header::ContentTransferEncoding::QuotedPrintable) + .body(body_html); - 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 text = SinglePart::builder() + .header(header::ContentType("text/plain; charset=utf-8".parse().unwrap())) + .header(header::ContentTransferEncoding::QuotedPrintable) + .body(body_text); - let alternative = PartBuilder::new() - .message_type(MimeMultipartType::Alternative) - .child(text) - .child(html); + let alternative = MultiPart::alternative().singlepart(text).singlepart(html); - let email = EmailBuilder::new() - .to(address) - .from((CONFIG.smtp_from().as_str(), CONFIG.smtp_from_name().as_str())) + 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) - .child(alternative.build()) - .build() + .multipart(alternative) .map_err(|e| Error::new("Error building email", e.to_string()))?; - let mut transport = mailer(); - - let result = transport.send(email); - - // Explicitly close the connection, in case of error - transport.close(); - - result?; + let _ = mailer().send(&email)?; Ok(()) }