From f07fe8687d7bb3e9d7b87ced60ef5cf063757f0f Mon Sep 17 00:00:00 2001 From: Alexis Mousset Date: Sun, 20 Aug 2017 00:42:45 +0200 Subject: [PATCH] Codec from tokio smtp (#185) feat(transport): Allow streaming emails --- .travis.yml | 2 +- README.md | 2 +- lettre/examples/smtp.rs | 2 +- lettre/src/file/error.rs | 3 +- lettre/src/file/mod.rs | 17 ++-- lettre/src/lib.rs | 29 +++--- lettre/src/sendmail/error.rs | 4 +- lettre/src/sendmail/mod.rs | 19 ++-- lettre/src/smtp/authentication.rs | 3 +- lettre/src/smtp/client/mod.rs | 157 ++++++++++++++++++++--------- lettre/src/smtp/client/net.rs | 5 +- lettre/src/smtp/commands.rs | 3 +- lettre/src/smtp/extension.rs | 4 +- lettre/src/smtp/mod.rs | 55 +++++----- lettre/src/stub/mod.rs | 11 +- lettre/tests/transport_file.rs | 6 +- lettre/tests/transport_sendmail.rs | 2 +- lettre/tests/transport_smtp.rs | 2 +- lettre/tests/transport_stub.rs | 4 +- lettre_email/examples/smtp.rs | 2 +- lettre_email/src/lib.rs | 68 ++----------- 21 files changed, 195 insertions(+), 205 deletions(-) diff --git a/.travis.yml b/.travis.yml index 3c5d46f..794c405 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,7 @@ rust: - stable - beta - nightly -- 1.15.0 +- 1.18.0 matrix: allow_failures: diff --git a/README.md b/README.md index f4527ed..9e28e31 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ Development version: ## Install -This library requires rust 1.15 or newer. +This library requires rust 1.18 or newer. To use this library, add the following to your `Cargo.toml`: ```toml diff --git a/lettre/examples/smtp.rs b/lettre/examples/smtp.rs index 67b4619..bf1c93f 100644 --- a/lettre/examples/smtp.rs +++ b/lettre/examples/smtp.rs @@ -18,7 +18,7 @@ fn main() { .unwrap() .build(); // Send the email - let result = mailer.send(email); + let result = mailer.send(&email); if result.is_ok() { println!("Email sent"); diff --git a/lettre/src/file/error.rs b/lettre/src/file/error.rs index 8283e0a..026477b 100644 --- a/lettre/src/file/error.rs +++ b/lettre/src/file/error.rs @@ -3,8 +3,7 @@ use self::Error::*; use serde_json; use std::error::Error as StdError; -use std::fmt; -use std::fmt::{Display, Formatter}; +use std::fmt::{self, Display, Formatter}; use std::io; /// An enum of all error kinds. diff --git a/lettre/src/file/mod.rs b/lettre/src/file/mod.rs index a33937c..11ae012 100644 --- a/lettre/src/file/mod.rs +++ b/lettre/src/file/mod.rs @@ -17,7 +17,7 @@ //! "Hello world".to_string(), //! ); //! -//! let result = sender.send(email); +//! let result = sender.send(&email); //! assert!(result.is_ok()); //! ``` //! Example result in `/tmp/b7c211bc-9811-45ce-8cd9-68eab575d695.txt`: @@ -37,9 +37,9 @@ use EmailTransport; use SendableEmail; use SimpleSendableEmail; use file::error::FileResult; - use serde_json; use std::fs::File; +use std::io::Read; use std::io::prelude::*; use std::path::{Path, PathBuf}; @@ -60,18 +60,21 @@ impl FileEmailTransport { } } -impl EmailTransport for FileEmailTransport { - fn send(&mut self, email: T) -> FileResult { +impl<'a, T: Read + 'a> EmailTransport<'a, T, FileResult> for FileEmailTransport { + fn send + 'a>(&mut self, email: &'a U) -> FileResult { let mut file = self.path.clone(); file.push(format!("{}.txt", email.message_id())); let mut f = File::create(file.as_path())?; + let mut message_content = String::new(); + let _ = email.message().read_to_string(&mut message_content); + let simple_email = SimpleSendableEmail::new( email.from().clone(), email.to().clone(), email.message_id().clone(), - email.message(), + message_content, ); f.write_all( @@ -80,8 +83,4 @@ impl EmailTransport for FileEmailTransport { Ok(()) } - - fn close(&mut self) { - () - } } diff --git a/lettre/src/lib.rs b/lettre/src/lib.rs index 5af8d95..4249b59 100644 --- a/lettre/src/lib.rs +++ b/lettre/src/lib.rs @@ -32,13 +32,10 @@ pub mod file; #[cfg(feature = "file-transport")] pub use file::FileEmailTransport; pub use sendmail::SendmailTransport; -pub use smtp::ClientSecurity; -pub use smtp::SmtpTransport; +pub use smtp::{SmtpTransport, ClientSecurity}; pub use smtp::client::net::ClientTlsParameters; - -use std::fmt; -use std::fmt::{Display, Formatter}; -pub use stub::StubEmailTransport; +use std::fmt::{self, Display, Formatter}; +use std::io::Read; /// Email address #[derive(PartialEq, Eq, Clone, Debug)] @@ -59,7 +56,7 @@ impl EmailAddress { } /// Email sendable by an SMTP client -pub trait SendableEmail { +pub trait SendableEmail<'a, T: Read + 'a> { /// To fn to(&self) -> Vec; /// From @@ -67,15 +64,13 @@ pub trait SendableEmail { /// Message ID, used for logging fn message_id(&self) -> String; /// Message content - fn message(self) -> String; + fn message(&'a self) -> Box; } /// Transport method for emails -pub trait EmailTransport { +pub trait EmailTransport<'a, U: Read + 'a, V> { /// Sends the email - fn send(&mut self, email: T) -> U; - /// Close the transport explicitly - fn close(&mut self); + fn send + 'a>(&mut self, email: &'a T) -> V; } /// Minimal email structure @@ -89,7 +84,7 @@ pub struct SimpleSendableEmail { /// Message ID message_id: String, /// Message content - message: String, + message: Vec, } impl SimpleSendableEmail { @@ -104,12 +99,12 @@ impl SimpleSendableEmail { from: from_address, to: to_addresses, message_id: message_id, - message: message, + message: message.into_bytes(), } } } -impl SendableEmail for SimpleSendableEmail { +impl<'a> SendableEmail<'a, &'a [u8]> for SimpleSendableEmail { fn to(&self) -> Vec { self.to.clone() } @@ -122,7 +117,7 @@ impl SendableEmail for SimpleSendableEmail { self.message_id.clone() } - fn message(self) -> String { - self.message + fn message(&'a self) -> Box<&[u8]> { + Box::new(self.message.as_slice()) } } diff --git a/lettre/src/sendmail/error.rs b/lettre/src/sendmail/error.rs index 98c7d83..f46919a 100644 --- a/lettre/src/sendmail/error.rs +++ b/lettre/src/sendmail/error.rs @@ -1,10 +1,8 @@ //! Error and result type for sendmail transport - use self::Error::*; use std::error::Error as StdError; -use std::fmt; -use std::fmt::{Display, Formatter}; +use std::fmt::{self, Display, Formatter}; use std::io; /// An enum of all error kinds. diff --git a/lettre/src/sendmail/mod.rs b/lettre/src/sendmail/mod.rs index 68b0612..6437f42 100644 --- a/lettre/src/sendmail/mod.rs +++ b/lettre/src/sendmail/mod.rs @@ -12,13 +12,13 @@ //! ); //! //! let mut sender = SendmailTransport::new(); -//! let result = sender.send(email); +//! let result = sender.send(&email); //! assert!(result.is_ok()); //! ``` -use EmailTransport; -use SendableEmail; +use {EmailTransport, SendableEmail}; use sendmail::error::SendmailResult; +use std::io::Read; use std::io::prelude::*; use std::process::{Command, Stdio}; @@ -42,8 +42,8 @@ impl SendmailTransport { } } -impl EmailTransport for SendmailTransport { - fn send(&mut self, email: T) -> SendmailResult { +impl<'a, T: Read + 'a> EmailTransport<'a, T, SendmailResult> for SendmailTransport { + fn send + 'a>(&mut self, email: &'a U) -> SendmailResult { // Spawn the sendmail command let to_addresses: Vec = email.to().iter().map(|x| x.to_string()).collect(); let mut process = Command::new(&self.command) @@ -59,8 +59,11 @@ impl EmailTransport for SendmailTransport { .stdout(Stdio::piped()) .spawn()?; + let mut message_content = String::new(); + let _ = email.message().read_to_string(&mut message_content); + match process.stdin.as_mut().unwrap().write_all( - email.message().as_bytes(), + message_content.as_bytes(), ) { Ok(_) => (), Err(error) => return Err(From::from(error)), @@ -78,8 +81,4 @@ impl EmailTransport for SendmailTransport { Err(From::from("The sendmail process stopped")) } } - - fn close(&mut self) { - () - } } diff --git a/lettre/src/smtp/authentication.rs b/lettre/src/smtp/authentication.rs index d6cd57a..8b4ef4a 100644 --- a/lettre/src/smtp/authentication.rs +++ b/lettre/src/smtp/authentication.rs @@ -10,8 +10,7 @@ use crypto::md5::Md5; use hex::ToHex; use smtp::NUL; use smtp::error::Error; -use std::fmt; -use std::fmt::{Display, Formatter}; +use std::fmt::{self, Display, Formatter}; /// Accepted authentication mecanisms on an encrypted connection /// Trying LOGIN last as it is deprecated. diff --git a/lettre/src/smtp/client/mod.rs b/lettre/src/smtp/client/mod.rs index c121094..4718ec9 100644 --- a/lettre/src/smtp/client/mod.rs +++ b/lettre/src/smtp/client/mod.rs @@ -7,39 +7,74 @@ use smtp::client::net::{ClientTlsParameters, Connector, NetworkStream, Timeout}; use smtp::commands::*; use smtp::error::{Error, SmtpResult}; use smtp::response::ResponseParser; -use std::fmt::Debug; -use std::fmt::Display; -use std::io; -use std::io::{BufRead, Read, Write}; +use std::fmt::{Debug, Display}; +use std::io::{self, BufRead, BufReader, Read, Write}; use std::net::ToSocketAddrs; use std::string::String; use std::time::Duration; - pub mod net; pub mod mock; -/// Returns the string after adding a dot at the beginning of each line starting with a dot -/// -/// Reference : https://tools.ietf.org/html/rfc5321#page-62 (4.5.2. Transparency) -#[inline] -fn escape_dot(string: &str) -> String { - if string.starts_with('.') { - format!(".{}", string) - } else { - string.to_string() - }.replace("\r.", "\r..") - .replace("\n.", "\n..") +/// The codec used for transparency +#[derive(Default, Debug)] +pub struct ClientCodec { + escape_count: u8, +} + +impl ClientCodec { + /// Creates a new client codec + pub fn new() -> Self { + ClientCodec::default() + } +} + +impl ClientCodec { + /// Adds transparency + /// TODO: replace CR and LF by CRLF + fn encode(&mut self, frame: &[u8], buf: &mut Vec) -> Result<(), Error> { + match frame.len() { + 0 => { + match self.escape_count { + 0 => buf.write_all(b"\r\n.\r\n")?, + 1 => buf.write_all(b"\n.\r\n")?, + 2 => buf.write_all(b".\r\n")?, + _ => unreachable!(), + } + self.escape_count = 0; + Ok(()) + } + _ => { + let mut start = 0; + for (idx, byte) in frame.iter().enumerate() { + match self.escape_count { + 0 => self.escape_count = if *byte == b'\r' { 1 } else { 0 }, + 1 => self.escape_count = if *byte == b'\n' { 2 } else { 0 }, + 2 => self.escape_count = if *byte == b'.' { 3 } else { 0 }, + _ => unreachable!(), + } + if self.escape_count == 3 { + self.escape_count = 0; + buf.write_all(&frame[start..idx])?; + buf.write_all(b".")?; + start = idx; + } + } + Ok(buf.write_all(&frame[start..])?) + } + + } + } } /// Returns the string replacing all the CRLF with "\" -#[inline] +/// Used for debug displays fn escape_crlf(string: &str) -> String { - string.replace(CRLF, "") + string.replace(CRLF, "") } /// Returns the string removing all the CRLF -#[inline] +/// Used for debug displays fn remove_crlf(string: &str) -> String { string.replace(CRLF, "") } @@ -140,19 +175,8 @@ impl Client { self.smtp_command(NoopCommand).is_ok() } - /// Sends an SMTP command - pub fn command(&mut self, command: &str) -> SmtpResult { - self.send_server(command, CRLF) - } - - /// Sends an SMTP command - pub fn smtp_command(&mut self, command: C) -> SmtpResult { - self.send_server(&command.to_string(), "") - } - /// Sends an AUTH command with the given mechanism, and handles challenge if needed pub fn auth(&mut self, mechanism: Mechanism, credentials: &Credentials) -> SmtpResult { - // TODO let mut challenges = 10; let mut response = self.smtp_command( @@ -176,22 +200,48 @@ impl Client { } /// Sends the message content - pub fn message(&mut self, message_content: &str) -> SmtpResult { - self.send_server(&escape_dot(message_content), MESSAGE_ENDING) + pub fn message(&mut self, mut message: Box) -> SmtpResult { + let mut in_buf: Vec = vec![]; + let mut out_buf: Vec = vec![]; + + let mut codec = ClientCodec::new(); + let mut message_reader = BufReader::new(message.as_mut()); + + loop { + in_buf.clear(); + out_buf.clear(); + match message_reader.read(&mut in_buf)? { + 0 => break, + _ => codec.encode(in_buf.as_slice(), &mut out_buf)?, + }; + + self.write_server(out_buf.as_slice())?; + } + + self.write_server(MESSAGE_ENDING.as_bytes())?; + self.get_reply() } - /// Sends a string to the server and gets the response - fn send_server(&mut self, string: &str, end: &str) -> SmtpResult { + /// Sends an SMTP command + pub fn smtp_command(&mut self, command: C) -> SmtpResult { + self.write_server(command.to_string().as_bytes())?; + self.get_reply() + } + + /// Writes a string to the server + fn write_server(&mut self, string: &[u8]) -> Result<(), Error> { if self.stream.is_none() { return Err(From::from("Connection closed")); } - write!(self.stream.as_mut().unwrap(), "{}{}", string, end)?; + self.stream.as_mut().unwrap().write(string)?; self.stream.as_mut().unwrap().flush()?; - debug!("Wrote: {}", escape_crlf(string)); - - self.get_reply() + debug!( + "Wrote: {}", + escape_crlf(String::from_utf8_lossy(string).as_ref()) + ); + Ok(()) } /// Gets the SMTP response @@ -216,20 +266,31 @@ impl Client { } else { Err(From::from(response)) } - } } #[cfg(test)] mod test { - use super::{escape_crlf, escape_dot, remove_crlf}; + use super::{ClientCodec, escape_crlf, remove_crlf}; #[test] - fn test_escape_dot() { - assert_eq!(escape_dot(".test"), "..test"); - assert_eq!(escape_dot("\r.\n.\r\n"), "\r..\n..\r\n"); - assert_eq!(escape_dot("test\r\n.test\r\n"), "test\r\n..test\r\n"); - assert_eq!(escape_dot("test\r\n.\r\ntest"), "test\r\n..\r\ntest"); + fn test_codec() { + let mut codec = ClientCodec::new(); + let mut buf: Vec = vec![]; + + assert!(codec.encode(b"test\r\n", &mut buf).is_ok()); + assert!(codec.encode(b".\r\n", &mut buf).is_ok()); + assert!(codec.encode(b"\r\ntest", &mut buf).is_ok()); + assert!(codec.encode(b"te\r\n.\r\nst", &mut buf).is_ok()); + assert!(codec.encode(b"test", &mut buf).is_ok()); + assert!(codec.encode(b"test.", &mut buf).is_ok()); + assert!(codec.encode(b"test\n", &mut buf).is_ok()); + assert!(codec.encode(b".test\n", &mut buf).is_ok()); + assert!(codec.encode(b"test", &mut buf).is_ok()); + assert_eq!( + String::from_utf8(buf).unwrap(), + "test\r\n..\r\n\r\ntestte\r\n..\r\nsttesttest.test\n.test\ntest" + ); } #[test] @@ -244,11 +305,11 @@ mod test { #[test] fn test_escape_crlf() { - assert_eq!(escape_crlf("\r\n"), ""); - assert_eq!(escape_crlf("EHLO my_name\r\n"), "EHLO my_name"); + assert_eq!(escape_crlf("\r\n"), ""); + assert_eq!(escape_crlf("EHLO my_name\r\n"), "EHLO my_name"); assert_eq!( escape_crlf("EHLO my_name\r\nSIZE 42\r\n"), - "EHLO my_nameSIZE 42" + "EHLO my_nameSIZE 42" ); } } diff --git a/lettre/src/smtp/client/net.rs b/lettre/src/smtp/client/net.rs index e85ac6f..7253aa8 100644 --- a/lettre/src/smtp/client/net.rs +++ b/lettre/src/smtp/client/net.rs @@ -2,8 +2,7 @@ use native_tls::{TlsConnector, TlsStream}; use smtp::client::mock::MockStream; -use std::io; -use std::io::{ErrorKind, Read, Write}; +use std::io::{self, ErrorKind, Read, Write}; use std::net::{Ipv4Addr, Shutdown, SocketAddr, SocketAddrV4, TcpStream}; use std::time::Duration; @@ -17,7 +16,7 @@ pub struct ClientTlsParameters { } impl ClientTlsParameters { - /// TODO + /// Creates a `ClientTlsParameters` pub fn new(domain: String, connector: TlsConnector) -> ClientTlsParameters { ClientTlsParameters { connector: connector, diff --git a/lettre/src/smtp/commands.rs b/lettre/src/smtp/commands.rs index 8fa05a2..c3e1c24 100644 --- a/lettre/src/smtp/commands.rs +++ b/lettre/src/smtp/commands.rs @@ -8,8 +8,7 @@ use smtp::error::Error; use smtp::extension::{MailParameter, RcptParameter}; use smtp::extension::ClientId; use smtp::response::Response; -use std::fmt; -use std::fmt::{Display, Formatter}; +use std::fmt::{self, Display, Formatter}; /// EHLO command #[derive(PartialEq, Clone, Debug)] diff --git a/lettre/src/smtp/extension.rs b/lettre/src/smtp/extension.rs index e010508..0d007ba 100644 --- a/lettre/src/smtp/extension.rs +++ b/lettre/src/smtp/extension.rs @@ -5,12 +5,10 @@ use smtp::error::Error; use smtp::response::Response; use smtp::util::XText; use std::collections::HashSet; -use std::fmt; -use std::fmt::{Display, Formatter}; +use std::fmt::{self, Display, Formatter}; use std::net::{Ipv4Addr, Ipv6Addr}; use std::result::Result; - /// Client identifier, the parameter to `EHLO` #[derive(PartialEq, Eq, Clone, Debug)] pub enum ClientId { diff --git a/lettre/src/smtp/mod.rs b/lettre/src/smtp/mod.rs index 7de3aa8..ce995ad 100644 --- a/lettre/src/smtp/mod.rs +++ b/lettre/src/smtp/mod.rs @@ -31,7 +31,7 @@ //! let mut mailer = //! SmtpTransport::builder_unencrypted_localhost().unwrap().build(); //! // Send the email -//! let result = mailer.send(email); +//! let result = mailer.send(&email); //! //! assert!(result.is_ok()); //! ``` @@ -59,7 +59,6 @@ //! .hello_name(ClientId::Domain("my.hostname.tld".to_string())) //! // Add credentials for authentication //! .credentials(Credentials::new("username".to_string(), "password".to_string())) -//! // FIXME security doc //! // Enable SMTPUTF8 if the server supports it //! .smtp_utf8(true) //! // Configure expected authentication mechanism @@ -67,11 +66,11 @@ //! // Enable connection reuse //! .connection_reuse(ConnectionReuseParameters::ReuseUnlimited).build(); //! -//! let result_1 = mailer.send(email.clone()); +//! let result_1 = mailer.send(&email); //! assert!(result_1.is_ok()); //! //! // The second email will use the same connection -//! let result_2 = mailer.send(email); +//! let result_2 = mailer.send(&email); //! assert!(result_2.is_ok()); //! //! // Explicitly close the SMTP transaction as we enabled connection reuse @@ -101,11 +100,10 @@ //! RcptCommand::new(EmailAddress::new("user@example.org".to_string()), vec![]) //! ); //! let _ = email_client.smtp_command(DataCommand); -//! let _ = email_client.message("Test email"); +//! let _ = email_client.message(Box::new("Test email".as_bytes())); //! let _ = email_client.smtp_command(QuitCommand); //! ``` - use EmailTransport; use SendableEmail; use native_tls::TlsConnector; @@ -116,6 +114,7 @@ use smtp::client::net::ClientTlsParameters; use smtp::commands::*; use smtp::error::{Error, SmtpResult}; use smtp::extension::{ClientId, Extension, MailBodyParameter, MailParameter, ServerInfo}; +use std::io::Read; use std::net::{SocketAddr, ToSocketAddrs}; use std::time::Duration; @@ -318,7 +317,7 @@ macro_rules! try_smtp ( }) ); -impl SmtpTransport { +impl<'a> SmtpTransport { /// Simple and secure transport, should be used when possible. /// Creates an encrypted transport over submission port, using the provided domain /// to validate TLS certificates. @@ -365,17 +364,6 @@ impl SmtpTransport { } } - /// Reset the client state - fn reset(&mut self) { - // Close the SMTP transaction if needed - self.close(); - - // Reset the client state - self.server_info = None; - self.state.panic = false; - self.state.connection_reuse_count = 0; - } - /// Gets the EHLO response and updates server information pub fn get_ehlo(&mut self) -> SmtpResult { // Extended Hello @@ -393,12 +381,28 @@ impl SmtpTransport { Ok(ehlo_response) } + + /// Closes the inner connection + pub fn close(&mut self) { + self.client.close(); + } + + /// Reset the client state + pub fn reset(&mut self) { + // Close the SMTP transaction if needed + self.close(); + + // Reset the client state + self.server_info = None; + self.state.panic = false; + self.state.connection_reuse_count = 0; + } } -impl EmailTransport for SmtpTransport { +impl<'a, T: Read + 'a> EmailTransport<'a, T, SmtpResult> for SmtpTransport { /// Sends an email #[cfg_attr(feature = "cargo-clippy", allow(match_same_arms, cyclomatic_complexity))] - fn send(&mut self, email: T) -> SmtpResult { + fn send + 'a>(&mut self, email: &'a U) -> SmtpResult { // Extract email information let message_id = email.message_id(); @@ -530,8 +534,7 @@ impl EmailTransport for SmtpTransport { try_smtp!(self.client.smtp_command(DataCommand), self); // Message content - let message = email.message(); - let result = self.client.message(&message); + let result = self.client.message(email.message()); if result.is_ok() { // Increment the connection reuse counter @@ -539,10 +542,9 @@ impl EmailTransport for SmtpTransport { // Log the message info!( - "{}: conn_use={}, size={}, status=sent ({})", + "{}: conn_use={}, status=sent ({})", message_id, self.state.connection_reuse_count, - message.len(), result .as_ref() .ok() @@ -564,9 +566,4 @@ impl EmailTransport for SmtpTransport { result } - - /// Closes the inner connection - fn close(&mut self) { - self.client.close(); - } } diff --git a/lettre/src/stub/mod.rs b/lettre/src/stub/mod.rs index 4cf1df4..a33f2d3 100644 --- a/lettre/src/stub/mod.rs +++ b/lettre/src/stub/mod.rs @@ -13,7 +13,7 @@ //! ); //! //! let mut sender = StubEmailTransport::new_positive(); -//! let result = sender.send(email); +//! let result = sender.send(&email); //! assert!(result.is_ok()); //! ``` //! @@ -27,6 +27,7 @@ use EmailTransport; use SendableEmail; use smtp::error::{Error, SmtpResult}; use smtp::response::{Code, Response}; +use std::io::Read; use std::str::FromStr; /// This transport logs the message envelope and returns the given response @@ -52,8 +53,8 @@ impl StubEmailTransport { /// SMTP result type pub type StubResult = SmtpResult; -impl EmailTransport for StubEmailTransport { - fn send(&mut self, email: T) -> StubResult { +impl<'a, T: Read + 'a> EmailTransport<'a, T, StubResult> for StubEmailTransport { + fn send>(&mut self, email: &'a U) -> StubResult { info!( "{}: from=<{}> to=<{:?}>", @@ -67,8 +68,4 @@ impl EmailTransport for StubEmailTransport { Err(Error::from(self.response.clone())) } } - - fn close(&mut self) { - () - } } diff --git a/lettre/tests/transport_file.rs b/lettre/tests/transport_file.rs index 70d818c..97dc5da 100644 --- a/lettre/tests/transport_file.rs +++ b/lettre/tests/transport_file.rs @@ -22,7 +22,7 @@ mod test { "file_id".to_string(), "Hello file".to_string(), ); - let result = sender.send(email.clone()); + let result = sender.send(&email); assert!(result.is_ok()); let message_id = email.message_id(); @@ -33,8 +33,8 @@ mod test { assert_eq!( buffer, - "{\"to\":[\"root@localhost\"],\"from\":\"user@localhost\",\ - \"message_id\":\"file_id\",\"message\":\"Hello file\"}" + "{\"to\":[\"root@localhost\"],\"from\":\"user@localhost\",\"message_id\":\ + \"file_id\",\"message\":[72,101,108,108,111,32,102,105,108,101]}" ); remove_file(file).unwrap(); diff --git a/lettre/tests/transport_sendmail.rs b/lettre/tests/transport_sendmail.rs index 9ad2079..ebdb906 100644 --- a/lettre/tests/transport_sendmail.rs +++ b/lettre/tests/transport_sendmail.rs @@ -13,7 +13,7 @@ fn sendmail_transport_simple() { "Hello sendmail".to_string(), ); - let result = sender.send(email); + let result = sender.send(&email); println!("{:?}", result); assert!(result.is_ok()); } diff --git a/lettre/tests/transport_smtp.rs b/lettre/tests/transport_smtp.rs index bf02c18..9826409 100644 --- a/lettre/tests/transport_smtp.rs +++ b/lettre/tests/transport_smtp.rs @@ -14,6 +14,6 @@ fn smtp_transport_simple() { "Hello smtp".to_string(), ); - let result = sender.send(email); + let result = sender.send(&email); assert!(result.is_ok()); } diff --git a/lettre/tests/transport_stub.rs b/lettre/tests/transport_stub.rs index 9a245b4..e8fac19 100644 --- a/lettre/tests/transport_stub.rs +++ b/lettre/tests/transport_stub.rs @@ -19,8 +19,8 @@ fn stub_transport() { "Hello stub".to_string(), ); - let result_ok = sender_ok.send(email.clone()).unwrap(); - let result_ko = sender_ko.send(email); + let result_ok = sender_ok.send(&email).unwrap(); + let result_ko = sender_ko.send(&email); assert_eq!(result_ok, response_ok); assert!(result_ko.is_err()); diff --git a/lettre_email/examples/smtp.rs b/lettre_email/examples/smtp.rs index 00e99bb..f723d21 100644 --- a/lettre_email/examples/smtp.rs +++ b/lettre_email/examples/smtp.rs @@ -20,7 +20,7 @@ fn main() { .unwrap() .build(); // Send the email - let result = mailer.send(email); + let result = mailer.send(&email); if result.is_ok() { println!("Email sent"); diff --git a/lettre_email/src/lib.rs b/lettre_email/src/lib.rs index 386303f..bbc9a6b 100644 --- a/lettre_email/src/lib.rs +++ b/lettre_email/src/lib.rs @@ -66,8 +66,6 @@ pub use email_format::{Address, Header, Mailbox, MimeMessage, MimeMultipartType} use error::Error; use lettre::{EmailAddress, SendableEmail}; use mime::Mime; -use std::fmt; -use std::fmt::{Display, Formatter}; use time::{Tm, now}; use uuid::Uuid; @@ -375,19 +373,13 @@ impl Envelope { #[derive(PartialEq, Eq, Clone, Debug)] pub struct Email { /// Message - message: MimeMessage, + message: Vec, /// Envelope envelope: Envelope, /// Message-ID message_id: Uuid, } -impl Display for Email { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - write!(f, "{}", self.message.as_string()) - } -} - impl PartBuilder { /// Creates a new empty part pub fn new() -> PartBuilder { @@ -818,14 +810,14 @@ impl EmailBuilder { } Ok(Email { - message: self.message.build(), + message: self.message.build().as_string().into_bytes(), envelope: envelope, message_id: message_id, }) } } -impl SendableEmail for Email { +impl<'a> SendableEmail<'a, &'a [u8]> for Email { fn to(&self) -> Vec { self.envelope .to @@ -842,8 +834,8 @@ impl SendableEmail for Email { format!("{}", self.message_id) } - fn message(self) -> String { - self.to_string() + fn message(&'a self) -> Box<&[u8]> { + Box::new(self.message.as_slice()) } } @@ -875,13 +867,10 @@ pub trait ExtractableEmail { #[cfg(test)] mod test { - use super::{Email, EmailBuilder, Envelope, IntoEmail, SimpleEmail}; - use email_format::{Header, MimeMessage}; + use super::{EmailBuilder, IntoEmail, SimpleEmail}; use lettre::{EmailAddress, SendableEmail}; use time::now; - use uuid::Uuid; - #[test] fn test_simple_email_builder() { let email_builder = SimpleEmail::default(); @@ -900,7 +889,7 @@ mod test { .unwrap(); assert_eq!( - format!("{}", email), + format!("{}", String::from_utf8_lossy(email.message().as_ref())), format!( "Subject: Hello\r\nContent-Type: text/plain; \ charset=utf-8\r\nX-test: value\r\nTo: \r\nFrom: \ @@ -913,43 +902,6 @@ mod test { ); } - #[test] - fn test_email_display() { - let current_message = Uuid::new_v4(); - - let mut email = Email { - message: MimeMessage::new_blank_message(), - envelope: Envelope { - to: vec![], - from: "".to_string(), - }, - message_id: current_message, - }; - - email.message.headers.insert( - Header::new_with_value( - "Message-ID".to_string(), - format!("<{}@rust-smtp>", current_message), - ).unwrap(), - ); - - email.message.headers.insert( - Header::new_with_value("To".to_string(), "to@example.com".to_string()) - .unwrap(), - ); - - email.message.body = "body".to_string(); - - assert_eq!( - format!("{}", email), - format!( - "Message-ID: <{}@rust-smtp>\r\nTo: to@example.com\r\n\r\nbody\r\n", - current_message - ) - ); - assert_eq!(current_message.to_string(), email.message_id()); - } - #[test] fn test_multiple_from() { let email_builder = EmailBuilder::new(); @@ -964,7 +916,7 @@ mod test { .build() .unwrap(); assert_eq!( - format!("{}", email), + format!("{}", String::from_utf8_lossy(email.message().as_ref())), format!( "Date: {}\r\nSubject: Invitation\r\nSender: \ \r\nTo: \r\nFrom: \ @@ -995,7 +947,7 @@ mod test { .unwrap(); assert_eq!( - format!("{}", email), + format!("{}", String::from_utf8_lossy(email.message().as_ref())), format!( "Date: {}\r\nSubject: Hello\r\nX-test: value\r\nSender: \ \r\nTo: \r\nFrom: \ @@ -1036,8 +988,6 @@ mod test { EmailAddress::new("bcc@localhost".to_string()), ] ); - let content = format!("{}", email); - assert_eq!(email.message(), content); } }