diff --git a/README.md b/README.md index ecf24cd..943c8fb 100644 --- a/README.md +++ b/README.md @@ -48,12 +48,12 @@ extern crate lettre; extern crate lettre_email; extern crate mime; -use lettre::{EmailTransport, SmtpTransport}; -use lettre_email::EmailBuilder; +use lettre::{Transport, SmtpClient}; +use lettre_email::Email; use std::path::Path; fn main() { - let email = EmailBuilder::new() + let email = Email::builder() // Addresses can be specified by the tuple (email, alias) .to(("user@example.org", "Firstname Lastname")) // ... or by an address only @@ -65,10 +65,10 @@ fn main() { .unwrap(); // Open a local connection on port 25 - let mut mailer = SmtpTransport::builder_unencrypted_localhost().unwrap() - .build(); + let mut mailer = SmtpClient::new_unencrypted_localhost().unwrap() + .transport(); // Send the email - let result = mailer.send(&email); + let result = mailer.send(email.into()); if result.is_ok() { println!("Email sent"); diff --git a/lettre/benches/transport_smtp.rs b/lettre/benches/transport_smtp.rs index 560acbe..0b2d7c1 100644 --- a/lettre/benches/transport_smtp.rs +++ b/lettre/benches/transport_smtp.rs @@ -3,8 +3,8 @@ extern crate lettre; extern crate test; -use lettre::{ClientSecurity, SmtpTransport}; -use lettre::{EmailAddress, EmailTransport, SimpleSendableEmail}; +use lettre::{ClientSecurity, Envelope, SmtpTransport}; +use lettre::{EmailAddress, SendableEmail, Transport}; use lettre::smtp::ConnectionReuseParameters; #[bench] @@ -13,13 +13,15 @@ fn bench_simple_send(b: &mut test::Bencher) { .unwrap() .build(); b.iter(|| { - let email = SimpleSendableEmail::new( - EmailAddress::new("user@localhost".to_string()), - vec![EmailAddress::new("root@localhost".to_string())], + let email = SendableEmail::new( + Envelope::new( + Some(EmailAddress::new("user@localhost".to_string()).unwrap()), + vec![EmailAddress::new("root@localhost".to_string()).unwrap()], + ).unwrap(), "id".to_string(), - "Hello world".to_string(), + "Hello ß☺ example".to_string().into_bytes(), ); - let result = sender.send(&email); + let result = sender.send(email); assert!(result.is_ok()); }); } @@ -31,13 +33,15 @@ fn bench_reuse_send(b: &mut test::Bencher) { .connection_reuse(ConnectionReuseParameters::ReuseUnlimited) .build(); b.iter(|| { - let email = SimpleSendableEmail::new( - EmailAddress::new("user@localhost".to_string()), - vec![EmailAddress::new("root@localhost".to_string())], + let email = SendableEmail::new( + Envelope::new( + Some(EmailAddress::new("user@localhost".to_string()).unwrap()), + vec![EmailAddress::new("root@localhost".to_string()).unwrap()], + ).unwrap(), "id".to_string(), - "Hello world".to_string(), + "Hello ß☺ example".to_string().into_bytes(), ); - let result = sender.send(&email); + let result = sender.send(email); assert!(result.is_ok()); }); sender.close() diff --git a/lettre/examples/smtp.rs b/lettre/examples/smtp.rs index 51eff1b..c75a1fa 100644 --- a/lettre/examples/smtp.rs +++ b/lettre/examples/smtp.rs @@ -1,24 +1,24 @@ extern crate env_logger; extern crate lettre; -use lettre::{EmailTransport, SimpleSendableEmail, SmtpTransport}; +use lettre::{EmailAddress, Envelope, SendableEmail, SmtpClient, Transport}; fn main() { env_logger::init(); - let email = SimpleSendableEmail::new( - "user@localhost".to_string(), - &["root@localhost".to_string()], - "my-message-id".to_string(), - "Hello ß☺ example".to_string(), - ).unwrap(); + let email = SendableEmail::new( + Envelope::new( + Some(EmailAddress::new("user@localhost".to_string()).unwrap()), + vec![EmailAddress::new("root@localhost".to_string()).unwrap()], + ).unwrap(), + "id".to_string(), + "Hello ß☺ example".to_string().into_bytes(), + ); // Open a local connection on port 25 - let mut mailer = SmtpTransport::builder_unencrypted_localhost() - .unwrap() - .build(); + let mut mailer = SmtpClient::new_unencrypted_localhost().unwrap().transport(); // 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/mod.rs b/lettre/src/file/mod.rs index db94f3a..f5e1962 100644 --- a/lettre/src/file/mod.rs +++ b/lettre/src/file/mod.rs @@ -3,13 +3,12 @@ //! It can be useful for testing purposes, or if you want to keep track of sent messages. //! -use EmailTransport; +use Transport; +use Envelope; 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}; @@ -18,38 +17,46 @@ pub mod error; /// Writes the content and the envelope information to a file #[derive(Debug)] #[cfg_attr(feature = "serde-impls", derive(Serialize, Deserialize))] -pub struct FileEmailTransport { +pub struct FileTransport { path: PathBuf, } -impl FileEmailTransport { +impl FileTransport { /// Creates a new transport to the given directory - pub fn new>(path: P) -> FileEmailTransport { + pub fn new>(path: P) -> FileTransport { let mut path_buf = PathBuf::new(); path_buf.push(path); - FileEmailTransport { path: path_buf } + FileTransport { path: path_buf } } } -impl<'a, T: Read + 'a> EmailTransport<'a, T> for FileEmailTransport { +#[derive(PartialEq, Eq, Clone, Debug)] +#[cfg_attr(feature = "serde-impls", derive(Serialize, Deserialize))] +struct SerializableEmail { + envelope: Envelope, + message_id: String, + message: Vec, +} + +impl<'a> Transport<'a> for FileTransport { type Result = FileResult; - fn send + 'a>(&mut self, email: &'a U) -> FileResult { + fn send(&mut self, email: SendableEmail) -> FileResult { + let message_id = email.message_id().to_string(); + let envelope = email.envelope().clone(); + let mut file = self.path.clone(); - file.push(format!("{}.txt", email.message_id())); + file.push(format!("{}.json", 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 serialized = serde_json::to_string(&SerializableEmail { + envelope, + message_id, + message: email.message_to_string()?.as_bytes().to_vec(), + })?; - let simple_email = SimpleSendableEmail::new_with_envelope( - email.envelope().clone(), - email.message_id().to_string(), - message_content, - ); - - f.write_all(serde_json::to_string(&simple_email)?.as_bytes())?; + f.write_all(serialized.as_bytes())?; Ok(()) } diff --git a/lettre/src/lib.rs b/lettre/src/lib.rs index 0777714..b120073 100644 --- a/lettre/src/lib.rs +++ b/lettre/src/lib.rs @@ -1,13 +1,11 @@ //! Lettre is a mailer written in Rust. It provides a simple email builder and several transports. //! -//! This mailer contains the available transports for your emails. To be sendable, the -//! emails have to implement `SendableEmail`. +//! This mailer contains the available transports for your emails. //! #![doc(html_root_url = "https://docs.rs/lettre/0.9.0")] -#![deny(missing_docs, missing_debug_implementations, missing_copy_implementations, trivial_casts, - trivial_numeric_casts, unsafe_code, unstable_features, unused_import_braces, - unused_qualifications)] +#![deny(missing_copy_implementations, trivial_casts, trivial_numeric_casts, unsafe_code, + unstable_features, unused_import_braces, unused_qualifications)] #[cfg(feature = "smtp-transport")] extern crate base64; #[cfg(feature = "smtp-transport")] @@ -28,6 +26,8 @@ extern crate native_tls; #[macro_use] extern crate nom; #[cfg(feature = "serde-impls")] +extern crate serde; +#[cfg(feature = "serde-impls")] #[macro_use] extern crate serde_derive; #[cfg(feature = "file-transport")] @@ -42,15 +42,17 @@ pub mod stub; pub mod file; #[cfg(feature = "file-transport")] -pub use file::FileEmailTransport; +pub use file::FileTransport; #[cfg(feature = "sendmail-transport")] pub use sendmail::SendmailTransport; #[cfg(feature = "smtp-transport")] -pub use smtp::{ClientSecurity, SmtpTransport}; +pub use smtp::{ClientSecurity, SmtpClient, SmtpTransport}; #[cfg(feature = "smtp-transport")] pub use smtp::client::net::ClientTlsParameters; use std::fmt::{self, Display, Formatter}; use std::io::Read; +use std::io::Cursor; +use std::io; use std::error::Error as StdError; use std::str::FromStr; @@ -150,133 +152,74 @@ impl Envelope { pub fn from(&self) -> Option<&EmailAddress> { self.reverse_path.as_ref() } +} - /// Creates a new builder - pub fn builder() -> EnvelopeBuilder { - EnvelopeBuilder::new() +pub enum Message { + Reader(Box), + Bytes(Cursor>), +} + +impl Read for Message { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + match *self { + Message::Reader(ref mut rdr) => rdr.read(buf), + Message::Bytes(ref mut rdr) => rdr.read(buf), + } } } -/// Simple email envelope representation -#[derive(PartialEq, Eq, Clone, Debug, Default)] -pub struct EnvelopeBuilder { - /// The envelope recipients' addresses - to: Vec, - /// The envelope sender address - from: Option, +/// Sendable email structure +pub struct SendableEmail { + envelope: Envelope, + message_id: String, + message: Message, } -impl EnvelopeBuilder { - /// Constructs an envelope with no recipients and an empty sender - pub fn new() -> Self { - EnvelopeBuilder { - to: vec![], - from: None, +impl SendableEmail { + pub fn new(envelope: Envelope, message_id: String, message: Vec) -> SendableEmail { + SendableEmail { + envelope, + message_id, + message: Message::Bytes(Cursor::new(message)), } } - /// Adds a recipient - pub fn to>(mut self, address: S) -> Self { - self.add_to(address); - self + pub fn new_with_reader( + envelope: Envelope, + message_id: String, + message: Box, + ) -> SendableEmail { + SendableEmail { + envelope, + message_id, + message: Message::Reader(message), + } } - /// Adds a recipient - pub fn add_to>(&mut self, address: S) { - self.to.push(address.into()); + pub fn envelope(&self) -> &Envelope { + &self.envelope } - /// Sets the sender - pub fn from>(mut self, address: S) -> Self { - self.set_from(address); - self + pub fn message_id(&self) -> &str { + &self.message_id } - /// Sets the sender - pub fn set_from>(&mut self, address: S) { - self.from = Some(address.into()); + pub fn message(self) -> Message { + self.message } - /// Build the envelope - pub fn build(self) -> EmailResult { - Envelope::new(self.from, self.to) + pub fn message_to_string(mut self) -> Result { + let mut message_content = String::new(); + self.message.read_to_string(&mut message_content)?; + Ok(message_content) } } -/// Email sendable by an SMTP client -pub trait SendableEmail<'a, T: Read + 'a> { - /// Envelope - fn envelope(&self) -> Envelope; - /// Message ID, used for logging - fn message_id(&self) -> String; - /// Message content - fn message(&'a self) -> Box; -} - /// Transport method for emails -pub trait EmailTransport<'a, U: Read + 'a> { +pub trait Transport<'a> { /// Result type for the transport type Result; /// Sends the email - fn send + 'a>(&mut self, email: &'a T) -> Self::Result; -} - -/// Minimal email structure -#[derive(Debug, Clone)] -#[cfg_attr(feature = "serde-impls", derive(Serialize, Deserialize))] -pub struct SimpleSendableEmail { - /// Envelope - envelope: Envelope, - /// Message ID - message_id: String, - /// Message content - message: Vec, -} - -impl SimpleSendableEmail { - /// Returns a new email - pub fn new( - from_address: String, - to_addresses: &[String], - message_id: String, - message: String, - ) -> EmailResult { - let to: Result, Error> = to_addresses - .iter() - .map(|x| EmailAddress::new(x.clone())) - .collect(); - Ok(SimpleSendableEmail::new_with_envelope( - Envelope::new(Some(EmailAddress::new(from_address)?), to?)?, - message_id, - message, - )) - } - - /// Returns a new email from a valid envelope - pub fn new_with_envelope( - envelope: Envelope, - message_id: String, - message: String, - ) -> SimpleSendableEmail { - SimpleSendableEmail { - envelope, - message_id, - message: message.into_bytes(), - } - } -} - -impl<'a> SendableEmail<'a, &'a [u8]> for SimpleSendableEmail { - fn envelope(&self) -> Envelope { - self.envelope.clone() - } - - fn message_id(&self) -> String { - self.message_id.clone() - } - - fn message(&'a self) -> Box<&[u8]> { - Box::new(self.message.as_slice()) - } + fn send(&mut self, email: SendableEmail) -> Self::Result; } diff --git a/lettre/src/sendmail/mod.rs b/lettre/src/sendmail/mod.rs index 018b6bd..1f2dc5b 100644 --- a/lettre/src/sendmail/mod.rs +++ b/lettre/src/sendmail/mod.rs @@ -1,7 +1,8 @@ //! The sendmail transport sends the email using the local sendmail command. //! -use {EmailTransport, SendableEmail}; +use Transport; +use SendableEmail; use sendmail::error::SendmailResult; use std::io::Read; use std::io::prelude::*; @@ -32,19 +33,19 @@ impl SendmailTransport { } } -impl<'a, T: Read + 'a> EmailTransport<'a, T> for SendmailTransport { +impl<'a> Transport<'a> for SendmailTransport { type Result = SendmailResult; - fn send + 'a>(&mut self, email: &'a U) -> SendmailResult { - let envelope = email.envelope(); + fn send(&mut self, email: SendableEmail) -> SendmailResult { + let message_id = email.message_id().to_string(); // Spawn the sendmail command - let to_addresses: Vec = envelope.to().iter().map(|x| x.to_string()).collect(); + let to_addresses: Vec = email.envelope.to().iter().map(|x| x.to_string()).collect(); let mut process = Command::new(&self.command) .args(&[ "-i", "-f", - &match envelope.from() { + &match email.envelope().from() { Some(address) => address.to_string(), None => "\"\"".to_string(), }, @@ -67,7 +68,7 @@ impl<'a, T: Read + 'a> EmailTransport<'a, T> for SendmailTransport { Err(error) => return Err(From::from(error)), } - info!("Wrote message to stdin"); + info!("Wrote {} message to stdin", message_id); if let Ok(output) = process.wait_with_output() { if output.status.success() { diff --git a/lettre/src/smtp/client/mod.rs b/lettre/src/smtp/client/mod.rs index 6d2d423..7ab3296 100644 --- a/lettre/src/smtp/client/mod.rs +++ b/lettre/src/smtp/client/mod.rs @@ -77,7 +77,7 @@ fn escape_crlf(string: &str) -> String { /// Structure that implements the SMTP client #[derive(Debug, Default)] -pub struct Client { +pub struct InnerClient { /// TCP stream between client and server /// Value is None before connection stream: Option>, @@ -90,16 +90,16 @@ macro_rules! return_err ( ); #[cfg_attr(feature = "cargo-clippy", allow(new_without_default_derive))] -impl Client { +impl InnerClient { /// Creates a new SMTP client /// /// It does not connects to the server, but only creates the `Client` - pub fn new() -> Client { - Client { stream: None } + pub fn new() -> InnerClient { + InnerClient { stream: None } } } -impl Client { +impl InnerClient { /// Closes the SMTP transaction if possible pub fn close(&mut self) { let _ = self.command(QuitCommand); @@ -194,10 +194,11 @@ impl Client { } /// Sends the message content - pub fn message(&mut self, mut message: Box) -> SmtpResult { + pub fn message(&mut self, message: Box) -> SmtpResult { let mut out_buf: Vec = vec![]; let mut codec = ClientCodec::new(); - let mut message_reader = BufReader::new(message.as_mut()); + + let mut message_reader = BufReader::new(message); loop { out_buf.clear(); diff --git a/lettre/src/smtp/mod.rs b/lettre/src/smtp/mod.rs index 4d7cba2..b99172b 100644 --- a/lettre/src/smtp/mod.rs +++ b/lettre/src/smtp/mod.rs @@ -14,18 +14,16 @@ //! * SMTPUTF8 ([RFC 6531](http://tools.ietf.org/html/rfc6531)) //! -use EmailTransport; -use SendableEmail; +use {SendableEmail, Transport}; use native_tls::TlsConnector; use smtp::authentication::{Credentials, Mechanism, DEFAULT_ENCRYPTED_MECHANISMS, DEFAULT_UNENCRYPTED_MECHANISMS}; -use smtp::client::Client; +use smtp::client::InnerClient; use smtp::client::net::ClientTlsParameters; use smtp::client::net::DEFAULT_TLS_PROTOCOLS; 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; @@ -93,7 +91,7 @@ pub enum ConnectionReuseParameters { /// Contains client configuration #[allow(missing_debug_implementations)] -pub struct SmtpTransportBuilder { +pub struct SmtpClient { /// Enable connection reuse connection_reuse: ConnectionReuseParameters, /// Name sent during EHLO @@ -114,7 +112,7 @@ pub struct SmtpTransportBuilder { } /// Builder for the SMTP `SmtpTransport` -impl SmtpTransportBuilder { +impl SmtpClient { /// Creates a new SMTP client /// /// Defaults are: @@ -123,14 +121,11 @@ impl SmtpTransportBuilder { /// * No authentication /// * No SMTPUTF8 support /// * A 60 seconds timeout for smtp commands - pub fn new( - addr: A, - security: ClientSecurity, - ) -> Result { + pub fn new(addr: A, security: ClientSecurity) -> Result { let mut addresses = addr.to_socket_addrs()?; match addresses.next() { - Some(addr) => Ok(SmtpTransportBuilder { + Some(addr) => Ok(SmtpClient { server_addr: addr, security, smtp_utf8: false, @@ -144,41 +139,59 @@ impl SmtpTransportBuilder { } } + /// Simple and secure transport, should be used when possible. + /// Creates an encrypted transport over submission port, using the provided domain + /// to validate TLS certificates. + pub fn new_simple(domain: &str) -> Result { + let mut tls_builder = TlsConnector::builder()?; + tls_builder.supported_protocols(DEFAULT_TLS_PROTOCOLS)?; + + let tls_parameters = + ClientTlsParameters::new(domain.to_string(), tls_builder.build().unwrap()); + + SmtpClient::new( + (domain, SUBMISSION_PORT), + ClientSecurity::Required(tls_parameters), + ) + } + + /// Creates a new local SMTP client to port 25 + pub fn new_unencrypted_localhost() -> Result { + SmtpClient::new(("localhost", SMTP_PORT), ClientSecurity::None) + } + /// Enable SMTPUTF8 if the server supports it - pub fn smtp_utf8(mut self, enabled: bool) -> SmtpTransportBuilder { + pub fn smtp_utf8(mut self, enabled: bool) -> SmtpClient { self.smtp_utf8 = enabled; self } /// Set the name used during EHLO - pub fn hello_name(mut self, name: ClientId) -> SmtpTransportBuilder { + pub fn hello_name(mut self, name: ClientId) -> SmtpClient { self.hello_name = name; self } /// Enable connection reuse - pub fn connection_reuse( - mut self, - parameters: ConnectionReuseParameters, - ) -> SmtpTransportBuilder { + pub fn connection_reuse(mut self, parameters: ConnectionReuseParameters) -> SmtpClient { self.connection_reuse = parameters; self } /// Set the client credentials - pub fn credentials>(mut self, credentials: S) -> SmtpTransportBuilder { + pub fn credentials>(mut self, credentials: S) -> SmtpClient { self.credentials = Some(credentials.into()); self } /// Set the authentication mechanism to use - pub fn authentication_mechanism(mut self, mechanism: Mechanism) -> SmtpTransportBuilder { + pub fn authentication_mechanism(mut self, mechanism: Mechanism) -> SmtpClient { self.authentication_mechanism = Some(mechanism); self } /// Set the timeout duration - pub fn timeout(mut self, timeout: Option) -> SmtpTransportBuilder { + pub fn timeout(mut self, timeout: Option) -> SmtpClient { self.timeout = timeout; self } @@ -186,7 +199,7 @@ impl SmtpTransportBuilder { /// Build the SMTP client /// /// It does not connect to the server, but only creates the `SmtpTransport` - pub fn build(self) -> SmtpTransport { + pub fn transport(self) -> SmtpTransport { SmtpTransport::new(self) } } @@ -209,9 +222,9 @@ pub struct SmtpTransport { /// SmtpTransport variable states state: State, /// Information about the client - client_info: SmtpTransportBuilder, + client_info: SmtpClient, /// Low level client - client: Client, + client: InnerClient, } macro_rules! try_smtp ( @@ -230,40 +243,11 @@ macro_rules! try_smtp ( ); 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. - pub fn simple_builder(domain: &str) -> Result { - let mut tls_builder = TlsConnector::builder()?; - tls_builder.supported_protocols(DEFAULT_TLS_PROTOCOLS)?; - - let tls_parameters = - ClientTlsParameters::new(domain.to_string(), tls_builder.build().unwrap()); - - SmtpTransportBuilder::new( - (domain, SUBMISSION_PORT), - ClientSecurity::Required(tls_parameters), - ) - } - - /// Creates a new configurable builder - pub fn builder( - addr: A, - security: ClientSecurity, - ) -> Result { - SmtpTransportBuilder::new(addr, security) - } - - /// Creates a new local SMTP client to port 25 - pub fn builder_unencrypted_localhost() -> Result { - SmtpTransportBuilder::new(("localhost", SMTP_PORT), ClientSecurity::None) - } - /// Creates a new SMTP client /// /// It does not connect to the server, but only creates the `SmtpTransport` - pub fn new(builder: SmtpTransportBuilder) -> SmtpTransport { - let client = Client::new(); + pub fn new(builder: SmtpClient) -> SmtpTransport { + let client = InnerClient::new(); SmtpTransport { client, @@ -306,15 +290,13 @@ impl<'a> SmtpTransport { } } -impl<'a, T: Read + 'a> EmailTransport<'a, T> for SmtpTransport { +impl<'a> Transport<'a> for SmtpTransport { type Result = SmtpResult; /// Sends an email #[cfg_attr(feature = "cargo-clippy", allow(match_same_arms, cyclomatic_complexity))] - fn send + 'a>(&mut self, email: &'a U) -> SmtpResult { - // Extract email information - let message_id = email.message_id(); - let envelope = email.envelope(); + fn send(&mut self, email: SendableEmail) -> SmtpResult { + let message_id = email.message_id().to_string(); // Check if the connection is still available if (self.state.connection_reuse_count > 0) && (!self.client.is_connected()) { @@ -419,8 +401,10 @@ impl<'a, T: Read + 'a> EmailTransport<'a, T> for SmtpTransport { } try_smtp!( - self.client - .command(MailCommand::new(envelope.from().cloned(), mail_options,)), + self.client.command(MailCommand::new( + email.envelope().from().cloned(), + mail_options, + )), self ); @@ -428,14 +412,14 @@ impl<'a, T: Read + 'a> EmailTransport<'a, T> for SmtpTransport { info!( "{}: from=<{}>", message_id, - match envelope.from() { + match email.envelope().from() { Some(address) => address.to_string(), None => "".to_string(), } ); // Recipient - for to_address in envelope.to() { + for to_address in email.envelope().to() { try_smtp!( self.client .command(RcptCommand::new(to_address.clone(), vec![]),), @@ -449,7 +433,7 @@ impl<'a, T: Read + 'a> EmailTransport<'a, T> for SmtpTransport { try_smtp!(self.client.command(DataCommand), self); // Message content - let result = self.client.message(email.message()); + let result = self.client.message(Box::new(email.message())); if result.is_ok() { // Increment the connection reuse counter diff --git a/lettre/src/stub/mod.rs b/lettre/src/stub/mod.rs index efa7034..1030e0c 100644 --- a/lettre/src/stub/mod.rs +++ b/lettre/src/stub/mod.rs @@ -2,44 +2,42 @@ //! testing purposes. //! -use EmailTransport; +use Transport; use SendableEmail; -use std::io::Read; /// This transport logs the message envelope and returns the given response #[derive(Debug, Clone, Copy)] -pub struct StubEmailTransport { +pub struct StubTransport { response: StubResult, } -impl StubEmailTransport { +impl StubTransport { /// Creates a new transport that always returns the given response - pub fn new(response: StubResult) -> StubEmailTransport { - StubEmailTransport { response } + pub fn new(response: StubResult) -> StubTransport { + StubTransport { response } } /// Creates a new transport that always returns a success response - pub fn new_positive() -> StubEmailTransport { - StubEmailTransport { response: Ok(()) } + pub fn new_positive() -> StubTransport { + StubTransport { response: Ok(()) } } } /// SMTP result type pub type StubResult = Result<(), ()>; -impl<'a, T: Read + 'a> EmailTransport<'a, T> for StubEmailTransport { +impl<'a> Transport<'a> for StubTransport { type Result = StubResult; - fn send>(&mut self, email: &'a U) -> StubResult { - let envelope = email.envelope(); + fn send(&mut self, email: SendableEmail) -> StubResult { info!( "{}: from=<{}> to=<{:?}>", email.message_id(), - match envelope.from() { + match email.envelope().from() { Some(address) => address.to_string(), None => "".to_string(), }, - envelope.to() + email.envelope().to() ); self.response } diff --git a/lettre/tests/transport_file.rs b/lettre/tests/transport_file.rs index 9dc1a82..572fd79 100644 --- a/lettre/tests/transport_file.rs +++ b/lettre/tests/transport_file.rs @@ -4,8 +4,8 @@ extern crate lettre; #[cfg(feature = "file-transport")] mod test { - use lettre::{EmailTransport, SendableEmail, SimpleSendableEmail}; - use lettre::file::FileEmailTransport; + use lettre::{EmailAddress, Envelope, SendableEmail, Transport}; + use lettre::file::FileTransport; use std::env::temp_dir; use std::fs::File; use std::fs::remove_file; @@ -13,25 +13,28 @@ mod test { #[test] fn file_transport() { - let mut sender = FileEmailTransport::new(temp_dir()); - let email = SimpleSendableEmail::new( - "user@localhost".to_string(), - &["root@localhost".to_string()], - "file_id".to_string(), - "Hello file".to_string(), - ).unwrap(); - let result = sender.send(&email); + let mut sender = FileTransport::new(temp_dir()); + let email = SendableEmail::new( + Envelope::new( + Some(EmailAddress::new("user@localhost".to_string()).unwrap()), + vec![EmailAddress::new("root@localhost".to_string()).unwrap()], + ).unwrap(), + "id".to_string(), + "Hello ß☺ example".to_string().into_bytes(), + ); + let message_id = email.message_id().to_string(); + + let result = sender.send(email); assert!(result.is_ok()); - let message_id = email.message_id(); - let file = format!("{}/{}.txt", temp_dir().to_str().unwrap(), message_id); + let file = format!("{}/{}.json", temp_dir().to_str().unwrap(), message_id); let mut f = File::open(file.clone()).unwrap(); let mut buffer = String::new(); let _ = f.read_to_string(&mut buffer); assert_eq!( buffer, - "{\"envelope\":{\"forward_path\":[\"root@localhost\"],\"reverse_path\":\"user@localhost\"},\"message_id\":\"file_id\",\"message\":[72,101,108,108,111,32,102,105,108,101]}" + "{\"envelope\":{\"forward_path\":[\"root@localhost\"],\"reverse_path\":\"user@localhost\"},\"message_id\":\"id\",\"message\":[72,101,108,108,111,32,195,159,226,152,186,32,101,120,97,109,112,108,101]}" ); remove_file(file).unwrap(); diff --git a/lettre/tests/transport_sendmail.rs b/lettre/tests/transport_sendmail.rs index 0d8109b..06a83bd 100644 --- a/lettre/tests/transport_sendmail.rs +++ b/lettre/tests/transport_sendmail.rs @@ -3,21 +3,22 @@ extern crate lettre; #[cfg(test)] #[cfg(feature = "sendmail-transport")] mod test { - - use lettre::{EmailTransport, SimpleSendableEmail}; + use lettre::{EmailAddress, Envelope, SendableEmail, Transport}; use lettre::sendmail::SendmailTransport; #[test] fn sendmail_transport_simple() { let mut sender = SendmailTransport::new(); - let email = SimpleSendableEmail::new( - "user@localhost".to_string(), - &["root@localhost".to_string()], - "sendmail_id".to_string(), - "Hello sendmail".to_string(), - ).unwrap(); + let email = SendableEmail::new( + Envelope::new( + Some(EmailAddress::new("user@localhost".to_string()).unwrap()), + vec![EmailAddress::new("root@localhost".to_string()).unwrap()], + ).unwrap(), + "id".to_string(), + "Hello ß☺ example".to_string().into_bytes(), + ); - 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 25fdc14..ff7c31c 100644 --- a/lettre/tests/transport_smtp.rs +++ b/lettre/tests/transport_smtp.rs @@ -3,22 +3,24 @@ extern crate lettre; #[cfg(test)] #[cfg(feature = "smtp-transport")] mod test { - - use lettre::{ClientSecurity, EmailTransport, SimpleSendableEmail, SmtpTransport}; + use lettre::{ClientSecurity, EmailAddress, Envelope, SendableEmail, SmtpClient, Transport}; #[test] fn smtp_transport_simple() { - let mut sender = SmtpTransport::builder("127.0.0.1:2525", ClientSecurity::None) - .unwrap() - .build(); - let email = SimpleSendableEmail::new( - "user@localhost".to_string(), - &["root@localhost".to_string()], - "smtp_id".to_string(), - "Hello smtp".to_string(), - ).unwrap(); + let email = SendableEmail::new( + Envelope::new( + Some(EmailAddress::new("user@localhost".to_string()).unwrap()), + vec![EmailAddress::new("root@localhost".to_string()).unwrap()], + ).unwrap(), + "id".to_string(), + "Hello ß☺ example".to_string().into_bytes(), + ); - sender.send(&email).unwrap(); + SmtpClient::new("127.0.0.1:2525", ClientSecurity::None) + .unwrap() + .transport() + .send(email) + .unwrap(); } } diff --git a/lettre/tests/transport_stub.rs b/lettre/tests/transport_stub.rs index b9a38cd..54b8c34 100644 --- a/lettre/tests/transport_stub.rs +++ b/lettre/tests/transport_stub.rs @@ -1,19 +1,29 @@ extern crate lettre; -use lettre::{EmailTransport, SimpleSendableEmail}; -use lettre::stub::StubEmailTransport; +use lettre::{EmailAddress, Envelope, SendableEmail, Transport}; +use lettre::stub::StubTransport; #[test] fn stub_transport() { - let mut sender_ok = StubEmailTransport::new_positive(); - let mut sender_ko = StubEmailTransport::new(Err(())); - let email = SimpleSendableEmail::new( - "user@localhost".to_string(), - &["root@localhost".to_string()], - "stub_id".to_string(), - "Hello stub".to_string(), - ).unwrap(); + let mut sender_ok = StubTransport::new_positive(); + let mut sender_ko = StubTransport::new(Err(())); + let email_ok = SendableEmail::new( + Envelope::new( + Some(EmailAddress::new("user@localhost".to_string()).unwrap()), + vec![EmailAddress::new("root@localhost".to_string()).unwrap()], + ).unwrap(), + "id".to_string(), + "Hello ß☺ example".to_string().into_bytes(), + ); + let email_ko = SendableEmail::new( + Envelope::new( + Some(EmailAddress::new("user@localhost".to_string()).unwrap()), + vec![EmailAddress::new("root@localhost".to_string()).unwrap()], + ).unwrap(), + "id".to_string(), + "Hello ß☺ example".to_string().into_bytes(), + ); - sender_ok.send(&email).unwrap(); - sender_ko.send(&email).unwrap_err(); + sender_ok.send(email_ok).unwrap(); + sender_ko.send(email_ko).unwrap_err(); } diff --git a/lettre_email/Cargo.toml b/lettre_email/Cargo.toml index d84c058..22585b2 100644 --- a/lettre_email/Cargo.toml +++ b/lettre_email/Cargo.toml @@ -23,7 +23,7 @@ lettre = { version = "^0.9", path = "../lettre", features = ["smtp-transport"] } glob = "0.2" [dependencies] -email = "^0.0" +email = { git = "https://github.com/lettre/rust-email" } mime = "^0.3" time = "^0.1" uuid = { version = "^0.6", features = ["v4"] } diff --git a/lettre_email/examples/smtp.rs b/lettre_email/examples/smtp.rs index 761fbe9..bd3b879 100644 --- a/lettre_email/examples/smtp.rs +++ b/lettre_email/examples/smtp.rs @@ -2,12 +2,12 @@ extern crate lettre; extern crate lettre_email; extern crate mime; -use lettre::{EmailTransport, SmtpTransport}; -use lettre_email::EmailBuilder; +use lettre::{SmtpClient, Transport}; +use lettre_email::Email; use std::path::Path; fn main() { - let email = EmailBuilder::new() + let email = Email::builder() // Addresses can be specified by the tuple (email, alias) .to(("user@example.org", "Firstname Lastname")) // ... or by an address only @@ -19,11 +19,9 @@ fn main() { .unwrap(); // Open a local connection on port 25 - let mut mailer = SmtpTransport::builder_unencrypted_localhost() - .unwrap() - .build(); + let mut mailer = SmtpClient::new_unencrypted_localhost().unwrap().transport(); // Send the email - let result = mailer.send(&email); + let result = mailer.send(email.into()); if result.is_ok() { println!("Email sent"); diff --git a/lettre_email/src/lib.rs b/lettre_email/src/lib.rs index a47e090..8bdbdf9 100644 --- a/lettre_email/src/lib.rs +++ b/lettre_email/src/lib.rs @@ -26,187 +26,6 @@ use time::{now, Tm}; use uuid::Uuid; use std::str::FromStr; -/// Converts an address or an address with an alias to a `Header` -pub trait IntoHeader { - /// Converts to a `Header` struct - fn into_header(self) -> Header; -} - -impl IntoHeader for Header { - fn into_header(self) -> Header { - self - } -} - -impl, T: Into> IntoHeader for (S, T) { - fn into_header(self) -> Header { - let (name, value) = self; - Header::new(name.into(), value.into()) - } -} - -/// Converts an address or an address with an alias to a `Mailbox` -pub trait IntoMailbox { - /// Converts to a `Mailbox` struct - fn into_mailbox(self) -> Mailbox; -} - -impl IntoMailbox for Mailbox { - fn into_mailbox(self) -> Mailbox { - self - } -} - -impl<'a> IntoMailbox for &'a str { - fn into_mailbox(self) -> Mailbox { - Mailbox::new(self.into()) - } -} - -impl IntoMailbox for String { - fn into_mailbox(self) -> Mailbox { - Mailbox::new(self) - } -} - -impl, T: Into> IntoMailbox for (S, T) { - fn into_mailbox(self) -> Mailbox { - let (address, alias) = self; - Mailbox::new_with_name(alias.into(), address.into()) - } -} - -/// Can be transformed to a sendable email -pub trait IntoEmail { - /// Builds an email - fn into_email(self) -> Result; -} - -impl IntoEmail for SimpleEmail { - fn into_email(self) -> Result { - let mut builder = EmailBuilder::new(); - - if self.from.is_some() { - builder = builder.from(self.from.unwrap()); - } - - for to_address in self.to { - builder = builder.to(to_address.into_mailbox()); - } - - for cc_address in self.cc { - builder = builder.cc(cc_address.into_mailbox()); - } - - if self.reply_to.is_some() { - builder = builder.reply_to(self.reply_to.unwrap().into_mailbox()); - } - - if self.subject.is_some() { - builder = builder.subject(self.subject.unwrap()); - } - - // No date for now - - builder = match (self.text, self.html) { - (Some(text), Some(html)) => builder.alternative(html, text), - (Some(text), None) => builder.text(text), - (None, Some(html)) => builder.html(html), - (None, None) => builder, - }; - - for header in self.headers { - builder = builder.header(header.into_header()); - } - - builder.build() - } -} - -/// Simple representation of an email, useful for some transports -#[derive(PartialEq, Eq, Clone, Debug, Default)] -pub struct SimpleEmail { - from: Option, - to: Vec, - cc: Vec, - bcc: Vec, - reply_to: Option, - subject: Option, - date: Option, - html: Option, - text: Option, - attachments: Vec, - headers: Vec
, -} - -impl SimpleEmail { - /// Adds a generic header - pub fn header(mut self, header: A) -> SimpleEmail { - self.headers.push(header.into_header()); - self - } - - /// Adds a `From` header and stores the sender address - pub fn from(mut self, address: A) -> SimpleEmail { - self.from = Some(address.into_mailbox()); - self - } - - /// Adds a `To` header and stores the recipient address - pub fn to(mut self, address: A) -> SimpleEmail { - self.to.push(address.into_mailbox()); - self - } - - /// Adds a `Cc` header and stores the recipient address - pub fn cc(mut self, address: A) -> SimpleEmail { - self.cc.push(address.into_mailbox()); - self - } - - /// Adds a `Bcc` header and stores the recipient address - pub fn bcc(mut self, address: A) -> SimpleEmail { - self.bcc.push(address.into_mailbox()); - self - } - - /// Adds a `Reply-To` header - pub fn reply_to(mut self, address: A) -> SimpleEmail { - self.reply_to = Some(address.into_mailbox()); - self - } - - /// Adds a `Subject` header - pub fn subject>(mut self, subject: S) -> SimpleEmail { - self.subject = Some(subject.into()); - self - } - - /// Adds a `Date` header with the given date - pub fn date(mut self, date: Tm) -> SimpleEmail { - self.date = Some(date); - self - } - - /// Adds an attachment to the message - pub fn attachment>(mut self, path: S) -> SimpleEmail { - self.attachments.push(path.into()); - self - } - - /// Sets the email body to plain text content - pub fn text>(mut self, body: S) -> SimpleEmail { - self.text = Some(body.into()); - self - } - - /// Sets the email body to HTML content - pub fn html>(mut self, body: S) -> SimpleEmail { - self.html = Some(body.into()); - self - } -} - /// Builds a `MimeMessage` structure #[derive(PartialEq, Eq, Clone, Debug)] pub struct PartBuilder { @@ -226,17 +45,17 @@ pub struct EmailBuilder { /// Message message: PartBuilder, /// The recipients' addresses for the mail header - to_header: Vec
, + to: Vec
, /// The sender addresses for the mail header - from_header: Vec
, + from: Vec
, /// The Cc addresses for the mail header - cc_header: Vec
, + cc: Vec
, /// The Bcc addresses for the mail header - bcc_header: Vec
, + bcc: Vec
, /// The Reply-To addresses for the mail header - reply_to_header: Vec
, + reply_to: Vec
, /// The sender address for the mail header - sender_header: Option, + sender: Option, /// The envelope envelope: Option, /// Date issued @@ -254,6 +73,23 @@ pub struct Email { message_id: Uuid, } +impl Into for Email { + fn into(self) -> SendableEmail { + SendableEmail::new( + self.envelope.clone(), + self.message_id.to_string(), + self.message, + ) + } +} + +impl Email { + /// TODO + pub fn builder() -> EmailBuilder { + EmailBuilder::new() + } +} + impl PartBuilder { /// Creates a new empty part pub fn new() -> PartBuilder { @@ -263,8 +99,8 @@ impl PartBuilder { } /// Adds a generic header - pub fn header(mut self, header: A) -> PartBuilder { - self.message.headers.insert(header.into_header()); + pub fn header>(mut self, header: A) -> PartBuilder { + self.message.headers.insert(header.into()); self } @@ -303,12 +139,12 @@ impl EmailBuilder { pub fn new() -> EmailBuilder { EmailBuilder { message: PartBuilder::new(), - to_header: vec![], - from_header: vec![], - cc_header: vec![], - bcc_header: vec![], - reply_to_header: vec![], - sender_header: None, + to: vec![], + from: vec![], + cc: vec![], + bcc: vec![], + reply_to: vec![], + sender: None, envelope: None, date_issued: false, } @@ -321,50 +157,50 @@ impl EmailBuilder { } /// Add a generic header - pub fn header(mut self, header: A) -> EmailBuilder { + pub fn header>(mut self, header: A) -> EmailBuilder { self.message = self.message.header(header); self } /// Adds a `From` header and stores the sender address - pub fn from(mut self, address: A) -> EmailBuilder { - let mailbox = address.into_mailbox(); - self.from_header.push(Address::Mailbox(mailbox)); + pub fn from>(mut self, address: A) -> EmailBuilder { + let mailbox = address.into(); + self.from.push(Address::Mailbox(mailbox)); self } /// Adds a `To` header and stores the recipient address - pub fn to(mut self, address: A) -> EmailBuilder { - let mailbox = address.into_mailbox(); - self.to_header.push(Address::Mailbox(mailbox)); + pub fn to>(mut self, address: A) -> EmailBuilder { + let mailbox = address.into(); + self.to.push(Address::Mailbox(mailbox)); self } /// Adds a `Cc` header and stores the recipient address - pub fn cc(mut self, address: A) -> EmailBuilder { - let mailbox = address.into_mailbox(); - self.cc_header.push(Address::Mailbox(mailbox)); + pub fn cc>(mut self, address: A) -> EmailBuilder { + let mailbox = address.into(); + self.cc.push(Address::Mailbox(mailbox)); self } /// Adds a `Bcc` header and stores the recipient address - pub fn bcc(mut self, address: A) -> EmailBuilder { - let mailbox = address.into_mailbox(); - self.bcc_header.push(Address::Mailbox(mailbox)); + pub fn bcc>(mut self, address: A) -> EmailBuilder { + let mailbox = address.into(); + self.bcc.push(Address::Mailbox(mailbox)); self } /// Adds a `Reply-To` header - pub fn reply_to(mut self, address: A) -> EmailBuilder { - let mailbox = address.into_mailbox(); - self.reply_to_header.push(Address::Mailbox(mailbox)); + pub fn reply_to>(mut self, address: A) -> EmailBuilder { + let mailbox = address.into(); + self.reply_to.push(Address::Mailbox(mailbox)); self } /// Adds a `Sender` header - pub fn sender(mut self, address: A) -> EmailBuilder { - let mailbox = address.into_mailbox(); - self.sender_header = Some(mailbox); + pub fn sender>(mut self, address: A) -> EmailBuilder { + let mailbox = address.into(); + self.sender = Some(mailbox); self } @@ -454,7 +290,6 @@ impl EmailBuilder { self.child(text) } - /// Sets the email body to HTML content pub fn html>(self, body: S) -> EmailBuilder { let html = PartBuilder::new() @@ -489,10 +324,13 @@ impl EmailBuilder { )) .build(); - let alternate = PartBuilder::new().message_type(MimeMultipartType::Alternative) - .child(text).child(html); + let alternate = PartBuilder::new() + .message_type(MimeMultipartType::Alternative) + .child(text) + .child(html); - self.message_type(MimeMultipartType::Mixed).child(alternate.build()) + self.message_type(MimeMultipartType::Mixed) + .child(alternate.build()) } /// Sets the envelope for manual destination control @@ -506,49 +344,45 @@ impl EmailBuilder { /// Builds the Email pub fn build(mut self) -> Result { // If there are multiple addresses in "From", the "Sender" is required. - if self.from_header.len() >= 2 && self.sender_header.is_none() { + if self.from.len() >= 2 && self.sender.is_none() { // So, we must find something to put as Sender. - for possible_sender in &self.from_header { + for possible_sender in &self.from { // Only a mailbox can be used as sender, not Address::Group. if let Address::Mailbox(ref mbx) = *possible_sender { - self.sender_header = Some(mbx.clone()); + self.sender = Some(mbx.clone()); break; } } // Address::Group is not yet supported, so the line below will never panic. // If groups are supported one day, add another Error for this case // and return it here, if sender_header is still None at this point. - assert!(self.sender_header.is_some()); + assert!(self.sender.is_some()); } // Add the sender header, if any. - if let Some(ref v) = self.sender_header { - self . message = self.message.header(("Sender", v.to_string().as_ref())); + if let Some(ref v) = self.sender { + self.message = self.message.header(("Sender", v.to_string().as_ref())); } // Calculate the envelope let envelope = match self.envelope { Some(e) => e, None => { // we need to generate the envelope - let mut e = Envelope::builder(); + let mut to = vec![]; // add all receivers in to_header and cc_header - for receiver in self.to_header - .iter() - .chain(self.cc_header.iter()) - .chain(self.bcc_header.iter()) - { + for receiver in self.to.iter().chain(self.cc.iter()).chain(self.bcc.iter()) { match *receiver { - Address::Mailbox(ref m) => e.add_to(EmailAddress::from_str(&m.address)?), + Address::Mailbox(ref m) => to.push(EmailAddress::from_str(&m.address)?), Address::Group(_, ref ms) => for m in ms.iter() { - e.add_to(EmailAddress::from_str(&m.address.clone())?); + to.push(EmailAddress::from_str(&m.address.clone())?); }, } } - e.set_from(EmailAddress::from_str(&match self.sender_header { + let from = Some(EmailAddress::from_str(&match self.sender { Some(x) => x.address.clone(), // if we have a sender_header, use it None => { // use a from header - debug_assert!(self.from_header.len() <= 1); // else we'd have sender_header - match self.from_header.first() { + debug_assert!(self.from.len() <= 1); // else we'd have sender_header + match self.from.first() { Some(a) => match *a { // if we have a from header Address::Mailbox(ref mailbox) => mailbox.address.clone(), // use it @@ -564,33 +398,32 @@ impl EmailBuilder { } } })?); - e.build()? + Envelope::new(from, to)? } }; // Add the collected addresses as mailbox-list all at once. // The unwraps are fine because the conversions for Vec
never errs. - if !self.to_header.is_empty() { + if !self.to.is_empty() { self.message = self.message - .header(Header::new_with_value("To".into(), self.to_header).unwrap()); + .header(Header::new_with_value("To".into(), self.to).unwrap()); } - if !self.from_header.is_empty() { + if !self.from.is_empty() { self.message = self.message - .header(Header::new_with_value("From".into(), self.from_header).unwrap()); + .header(Header::new_with_value("From".into(), self.from).unwrap()); } else { return Err(Error::Email(EmailError::MissingFrom)); } - if !self.cc_header.is_empty() { + if !self.cc.is_empty() { self.message = self.message - .header(Header::new_with_value("Cc".into(), self.cc_header).unwrap()); + .header(Header::new_with_value("Cc".into(), self.cc).unwrap()); } - if !self.bcc_header.is_empty() { + if !self.bcc.is_empty() { self.message = self.message - .header(Header::new_with_value("Bcc".into(), self.bcc_header).unwrap()); + .header(Header::new_with_value("Bcc".into(), self.bcc).unwrap()); } - if !self.reply_to_header.is_empty() { - self.message = self.message.header( - Header::new_with_value("Reply-To".into(), self.reply_to_header).unwrap(), - ); + if !self.reply_to.is_empty() { + self.message = self.message + .header(Header::new_with_value("Reply-To".into(), self.reply_to).unwrap()); } if !self.date_issued { @@ -617,31 +450,17 @@ impl EmailBuilder { } } -impl<'a> SendableEmail<'a, &'a [u8]> for Email { - fn envelope(&self) -> Envelope { - self.envelope.clone() - } - - fn message_id(&self) -> String { - self.message_id.to_string() - } - - fn message(&'a self) -> Box<&[u8]> { - Box::new(self.message.as_slice()) - } -} - #[cfg(test)] mod test { - use super::EmailBuilder; - use lettre::{EmailAddress, SendableEmail}; + use super::{EmailBuilder, SendableEmail}; + use lettre::EmailAddress; use time::now; #[test] fn test_multiple_from() { let email_builder = EmailBuilder::new(); let date_now = now(); - let email = email_builder + let email: SendableEmail = email_builder .to("anna@example.com") .from("dieter@example.com") .from("joachim@example.com") @@ -649,16 +468,18 @@ mod test { .subject("Invitation") .body("We invite you!") .build() - .unwrap(); + .unwrap() + .into(); + let id = email.message_id().to_string(); assert_eq!( - format!("{}", String::from_utf8_lossy(email.message().as_ref())), + email.message_to_string().unwrap(), format!( "Date: {}\r\nSubject: Invitation\r\nSender: \ \r\nTo: \r\nFrom: \ , \r\nMIME-Version: \ 1.0\r\nMessage-ID: <{}.lettre@localhost>\r\n\r\nWe invite you!\r\n", date_now.rfc822z(), - email.message_id() + id ) ); } @@ -668,7 +489,7 @@ mod test { let email_builder = EmailBuilder::new(); let date_now = now(); - let email = email_builder + let email: SendableEmail = email_builder .to("user@localhost") .from("user@localhost") .cc(("cc@localhost", "Alias")) @@ -680,10 +501,11 @@ mod test { .subject("Hello") .header(("X-test", "value")) .build() - .unwrap(); - + .unwrap() + .into(); + let id = email.message_id().to_string(); assert_eq!( - format!("{}", String::from_utf8_lossy(email.message().as_ref())), + email.message_to_string().unwrap(), format!( "Date: {}\r\nSubject: Hello\r\nX-test: value\r\nSender: \ \r\nTo: \r\nFrom: \ @@ -692,7 +514,7 @@ mod test { MIME-Version: 1.0\r\nMessage-ID: \ <{}.lettre@localhost>\r\n\r\nHello World!\r\n", date_now.rfc822z(), - email.message_id() + id ) ); } @@ -702,7 +524,7 @@ mod test { let email_builder = EmailBuilder::new(); let date_now = now(); - let email = email_builder + let email: SendableEmail = email_builder .to("user@localhost") .from("user@localhost") .cc(("cc@localhost", "Alias")) @@ -714,7 +536,8 @@ mod test { .subject("Hello") .header(("X-test", "value")) .build() - .unwrap(); + .unwrap() + .into(); assert_eq!( email.envelope().from().unwrap().to_string(), diff --git a/website/content/creating-messages/email.md b/website/content/creating-messages/email.md index 77fae7e..449f587 100644 --- a/website/content/creating-messages/email.md +++ b/website/content/creating-messages/email.md @@ -10,11 +10,11 @@ An email is built using an `EmailBuilder`. The simplest email could be: ```rust extern crate lettre_email; -use lettre_email::EmailBuilder; +use lettre_email::Email; fn main() { // Create an email - let email = EmailBuilder::new() + let email = Email::builder() // Addresses can be specified by the tuple (email, alias) .to(("user@example.org", "Firstname Lastname")) // ... or by an address only diff --git a/website/content/sending-messages/file.md b/website/content/sending-messages/file.md index 8f3a4b0..a7dac05 100644 --- a/website/content/sending-messages/file.md +++ b/website/content/sending-messages/file.md @@ -9,20 +9,22 @@ extern crate lettre; use std::env::temp_dir; -use lettre::file::FileEmailTransport; -use lettre::{SimpleSendableEmail, EmailTransport}; +use lettre::file::FileTransport; +use lettre::{Transport, Envelope, EmailAddress, SendableEmail}; fn main() { // Write to the local temp directory - let mut sender = FileEmailTransport::new(temp_dir()); - let email = SimpleSendableEmail::new( - "user@localhost".to_string(), - &["root@localhost".to_string()], - "message_id".to_string(), - "Hello world".to_string(), - ).unwrap(); + let mut sender = FileTransport::new(temp_dir()); + let email = SendableEmail::new( + Envelope::new( + Some(EmailAddress::new("user@localhost".to_string()).unwrap()), + vec![EmailAddress::new("root@localhost".to_string()).unwrap()], + ).unwrap(), + "id".to_string(), + "Hello world".to_string().into_bytes(), + ); - let result = sender.send(&email); + let result = sender.send(email); assert!(result.is_ok()); } ``` diff --git a/website/content/sending-messages/sendmail.md b/website/content/sending-messages/sendmail.md index d1d7d79..5800c2a 100644 --- a/website/content/sending-messages/sendmail.md +++ b/website/content/sending-messages/sendmail.md @@ -6,18 +6,20 @@ The sendmail transport sends the email using the local sendmail command. extern crate lettre; use lettre::sendmail::SendmailTransport; -use lettre::{SimpleSendableEmail, EmailTransport}; +use lettre::{SendableEmail, Envelope, EmailAddress, Transport}; fn main() { - let email = SimpleSendableEmail::new( - "user@localhost".to_string(), - &["root@localhost".to_string()], - "message_id".to_string(), - "Hello world".to_string(), - ).unwrap(); + let email = SendableEmail::new( + Envelope::new( + Some(EmailAddress::new("user@localhost".to_string()).unwrap()), + vec![EmailAddress::new("root@localhost".to_string()).unwrap()], + ).unwrap(), + "id".to_string(), + "Hello world".to_string().into_bytes(), + ); let mut sender = SendmailTransport::new(); - let result = sender.send(&email); + let result = sender.send(email); assert!(result.is_ok()); } ``` diff --git a/website/content/sending-messages/smtp.md b/website/content/sending-messages/smtp.md index 3d83768..9adb7ef 100644 --- a/website/content/sending-messages/smtp.md +++ b/website/content/sending-messages/smtp.md @@ -20,21 +20,23 @@ This is the most basic example of usage: ```rust,no_run extern crate lettre; -use lettre::{SimpleSendableEmail, EmailTransport, SmtpTransport}; +use lettre::{SendableEmail, EmailAddress, Transport, Envelope, SmtpClient}; fn main() { - let email = SimpleSendableEmail::new( - "user@localhost".to_string(), - &["root@localhost".to_string()], - "message_id".to_string(), - "Hello world".to_string(), - ).unwrap(); + let email = SendableEmail::new( + Envelope::new( + Some(EmailAddress::new("user@localhost".to_string()).unwrap()), + vec![EmailAddress::new("root@localhost".to_string()).unwrap()], + ).unwrap(), + "id".to_string(), + "Hello world".to_string().into_bytes(), + ); // Open a local connection on port 25 let mut mailer = - SmtpTransport::builder_unencrypted_localhost().unwrap().build(); + SmtpClient::new_unencrypted_localhost().unwrap().transport(); // Send the email - let result = mailer.send(&email); + let result = mailer.send(email); assert!(result.is_ok()); } @@ -46,20 +48,31 @@ fn main() { extern crate lettre; use lettre::smtp::authentication::{Credentials, Mechanism}; -use lettre::{SimpleSendableEmail, EmailTransport, SmtpTransport}; +use lettre::{SendableEmail, Envelope, EmailAddress, Transport, SmtpClient}; use lettre::smtp::extension::ClientId; use lettre::smtp::ConnectionReuseParameters; fn main() { - let email = SimpleSendableEmail::new( - "user@localhost".to_string(), - &["root@localhost".to_string()], - "message_id".to_string(), - "Hello world".to_string(), - ).unwrap(); + let email_1 = SendableEmail::new( + Envelope::new( + Some(EmailAddress::new("user@localhost".to_string()).unwrap()), + vec![EmailAddress::new("root@localhost".to_string()).unwrap()], + ).unwrap(), + "id1".to_string(), + "Hello world".to_string().into_bytes(), + ); + + let email_2 = SendableEmail::new( + Envelope::new( + Some(EmailAddress::new("user@localhost".to_string()).unwrap()), + vec![EmailAddress::new("root@localhost".to_string()).unwrap()], + ).unwrap(), + "id2".to_string(), + "Hello world a second time".to_string().into_bytes(), + ); // Connect to a remote server on a custom port - let mut mailer = SmtpTransport::simple_builder("server.tld").unwrap() + let mut mailer = SmtpClient::new_simple("server.tld").unwrap() // Set the name sent during EHLO/HELO, default is `localhost` .hello_name(ClientId::Domain("my.hostname.tld".to_string())) // Add credentials for authentication @@ -69,13 +82,13 @@ fn main() { // Configure expected authentication mechanism .authentication_mechanism(Mechanism::Plain) // Enable connection reuse - .connection_reuse(ConnectionReuseParameters::ReuseUnlimited).build(); + .connection_reuse(ConnectionReuseParameters::ReuseUnlimited).transport(); - let result_1 = mailer.send(&email); + let result_1 = mailer.send(email_1); 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_2); assert!(result_2.is_ok()); // Explicitly close the SMTP transaction as we enabled connection reuse @@ -93,13 +106,13 @@ extern crate lettre; use lettre::EmailAddress; use lettre::smtp::SMTP_PORT; -use lettre::smtp::client::Client; +use lettre::smtp::client::InnerClient; use lettre::smtp::client::net::NetworkStream; use lettre::smtp::extension::ClientId; use lettre::smtp::commands::*; fn main() { - let mut email_client: Client = Client::new(); + let mut email_client: InnerClient = InnerClient::new(); let _ = email_client.connect(&("localhost", SMTP_PORT), None); let _ = email_client.command(EhloCommand::new(ClientId::new("my_hostname".to_string()))); let _ = email_client.command( diff --git a/website/content/sending-messages/stub.md b/website/content/sending-messages/stub.md index 1c12377..2d13a5b 100644 --- a/website/content/sending-messages/stub.md +++ b/website/content/sending-messages/stub.md @@ -6,19 +6,21 @@ testing purposes. ```rust extern crate lettre; -use lettre::stub::StubEmailTransport; -use lettre::{SimpleSendableEmail, EmailTransport}; +use lettre::stub::StubTransport; +use lettre::{SendableEmail, Envelope, EmailAddress, Transport}; fn main() { - let email = SimpleSendableEmail::new( - "user@localhost".to_string(), - &["root@localhost".to_string()], - "message_id".to_string(), - "Hello world".to_string(), - ).unwrap(); + let email = SendableEmail::new( + Envelope::new( + Some(EmailAddress::new("user@localhost".to_string()).unwrap()), + vec![EmailAddress::new("root@localhost".to_string()).unwrap()], + ).unwrap(), + "id".to_string(), + "Hello world".to_string().into_bytes(), + ); - let mut sender = StubEmailTransport::new_positive(); - let result = sender.send(&email); + let mut sender = StubTransport::new_positive(); + let result = sender.send(email); assert!(result.is_ok()); } ```