Merge branch 'devel'

This commit is contained in:
Alexis Mousset
2018-05-04 00:27:04 +02:00
21 changed files with 425 additions and 631 deletions

View File

@@ -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");

View File

@@ -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()

View File

@@ -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");

View File

@@ -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<P: AsRef<Path>>(path: P) -> FileEmailTransport {
pub fn new<P: AsRef<Path>>(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<u8>,
}
impl<'a> Transport<'a> for FileTransport {
type Result = FileResult;
fn send<U: SendableEmail<'a, T> + '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(())
}

View File

@@ -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<Read + Send>),
Bytes(Cursor<Vec<u8>>),
}
impl Read for Message {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
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<EmailAddress>,
/// The envelope sender address
from: Option<EmailAddress>,
/// 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<u8>) -> SendableEmail {
SendableEmail {
envelope,
message_id,
message: Message::Bytes(Cursor::new(message)),
}
}
/// Adds a recipient
pub fn to<S: Into<EmailAddress>>(mut self, address: S) -> Self {
self.add_to(address);
self
pub fn new_with_reader(
envelope: Envelope,
message_id: String,
message: Box<Read + Send>,
) -> SendableEmail {
SendableEmail {
envelope,
message_id,
message: Message::Reader(message),
}
}
/// Adds a recipient
pub fn add_to<S: Into<EmailAddress>>(&mut self, address: S) {
self.to.push(address.into());
pub fn envelope(&self) -> &Envelope {
&self.envelope
}
/// Sets the sender
pub fn from<S: Into<EmailAddress>>(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<S: Into<EmailAddress>>(&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> {
Envelope::new(self.from, self.to)
pub fn message_to_string(mut self) -> Result<String, io::Error> {
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<T>;
}
/// 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<T: SendableEmail<'a, U> + '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<u8>,
}
impl SimpleSendableEmail {
/// Returns a new email
pub fn new(
from_address: String,
to_addresses: &[String],
message_id: String,
message: String,
) -> EmailResult<SimpleSendableEmail> {
let to: Result<Vec<EmailAddress>, 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;
}

View File

@@ -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<U: SendableEmail<'a, T> + '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<String> = envelope.to().iter().map(|x| x.to_string()).collect();
let to_addresses: Vec<String> = 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() {

View File

@@ -77,7 +77,7 @@ fn escape_crlf(string: &str) -> String {
/// Structure that implements the SMTP client
#[derive(Debug, Default)]
pub struct Client<S: Write + Read = NetworkStream> {
pub struct InnerClient<S: Write + Read = NetworkStream> {
/// TCP stream between client and server
/// Value is None before connection
stream: Option<BufStream<S>>,
@@ -90,16 +90,16 @@ macro_rules! return_err (
);
#[cfg_attr(feature = "cargo-clippy", allow(new_without_default_derive))]
impl<S: Write + Read> Client<S> {
impl<S: Write + Read> InnerClient<S> {
/// Creates a new SMTP client
///
/// It does not connects to the server, but only creates the `Client`
pub fn new() -> Client<S> {
Client { stream: None }
pub fn new() -> InnerClient<S> {
InnerClient { stream: None }
}
}
impl<S: Connector + Write + Read + Timeout + Debug> Client<S> {
impl<S: Connector + Write + Read + Timeout + Debug> InnerClient<S> {
/// Closes the SMTP transaction if possible
pub fn close(&mut self) {
let _ = self.command(QuitCommand);
@@ -194,10 +194,11 @@ impl<S: Connector + Write + Read + Timeout + Debug> Client<S> {
}
/// Sends the message content
pub fn message<T: Read>(&mut self, mut message: Box<T>) -> SmtpResult {
pub fn message(&mut self, message: Box<Read>) -> SmtpResult {
let mut out_buf: Vec<u8> = 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();

View File

@@ -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<A: ToSocketAddrs>(
addr: A,
security: ClientSecurity,
) -> Result<SmtpTransportBuilder, Error> {
pub fn new<A: ToSocketAddrs>(addr: A, security: ClientSecurity) -> Result<SmtpClient, Error> {
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<SmtpClient, Error> {
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, Error> {
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<S: Into<Credentials>>(mut self, credentials: S) -> SmtpTransportBuilder {
pub fn credentials<S: Into<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<Duration>) -> SmtpTransportBuilder {
pub fn timeout(mut self, timeout: Option<Duration>) -> 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<SmtpTransportBuilder, Error> {
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<A: ToSocketAddrs>(
addr: A,
security: ClientSecurity,
) -> Result<SmtpTransportBuilder, Error> {
SmtpTransportBuilder::new(addr, security)
}
/// Creates a new local SMTP client to port 25
pub fn builder_unencrypted_localhost() -> Result<SmtpTransportBuilder, Error> {
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<U: SendableEmail<'a, T> + '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

View File

@@ -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<U: SendableEmail<'a, T>>(&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
}

View File

@@ -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();

View File

@@ -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());
}

View File

@@ -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();
}
}

View File

@@ -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();
}

View File

@@ -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"] }

View File

@@ -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");

View File

@@ -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<S: Into<String>, T: Into<String>> 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<S: Into<String>, T: Into<String>> 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<Email, Error>;
}
impl IntoEmail for SimpleEmail {
fn into_email(self) -> Result<Email, Error> {
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<Mailbox>,
to: Vec<Mailbox>,
cc: Vec<Mailbox>,
bcc: Vec<Mailbox>,
reply_to: Option<Mailbox>,
subject: Option<String>,
date: Option<Tm>,
html: Option<String>,
text: Option<String>,
attachments: Vec<String>,
headers: Vec<Header>,
}
impl SimpleEmail {
/// Adds a generic header
pub fn header<A: IntoHeader>(mut self, header: A) -> SimpleEmail {
self.headers.push(header.into_header());
self
}
/// Adds a `From` header and stores the sender address
pub fn from<A: IntoMailbox>(mut self, address: A) -> SimpleEmail {
self.from = Some(address.into_mailbox());
self
}
/// Adds a `To` header and stores the recipient address
pub fn to<A: IntoMailbox>(mut self, address: A) -> SimpleEmail {
self.to.push(address.into_mailbox());
self
}
/// Adds a `Cc` header and stores the recipient address
pub fn cc<A: IntoMailbox>(mut self, address: A) -> SimpleEmail {
self.cc.push(address.into_mailbox());
self
}
/// Adds a `Bcc` header and stores the recipient address
pub fn bcc<A: IntoMailbox>(mut self, address: A) -> SimpleEmail {
self.bcc.push(address.into_mailbox());
self
}
/// Adds a `Reply-To` header
pub fn reply_to<A: IntoMailbox>(mut self, address: A) -> SimpleEmail {
self.reply_to = Some(address.into_mailbox());
self
}
/// Adds a `Subject` header
pub fn subject<S: Into<String>>(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<S: Into<String>>(mut self, path: S) -> SimpleEmail {
self.attachments.push(path.into());
self
}
/// Sets the email body to plain text content
pub fn text<S: Into<String>>(mut self, body: S) -> SimpleEmail {
self.text = Some(body.into());
self
}
/// Sets the email body to HTML content
pub fn html<S: Into<String>>(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<Address>,
to: Vec<Address>,
/// The sender addresses for the mail header
from_header: Vec<Address>,
from: Vec<Address>,
/// The Cc addresses for the mail header
cc_header: Vec<Address>,
cc: Vec<Address>,
/// The Bcc addresses for the mail header
bcc_header: Vec<Address>,
bcc: Vec<Address>,
/// The Reply-To addresses for the mail header
reply_to_header: Vec<Address>,
reply_to: Vec<Address>,
/// The sender address for the mail header
sender_header: Option<Mailbox>,
sender: Option<Mailbox>,
/// The envelope
envelope: Option<Envelope>,
/// Date issued
@@ -254,6 +73,23 @@ pub struct Email {
message_id: Uuid,
}
impl Into<SendableEmail> 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<A: IntoHeader>(mut self, header: A) -> PartBuilder {
self.message.headers.insert(header.into_header());
pub fn header<A: Into<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<A: IntoHeader>(mut self, header: A) -> EmailBuilder {
pub fn header<A: Into<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<A: IntoMailbox>(mut self, address: A) -> EmailBuilder {
let mailbox = address.into_mailbox();
self.from_header.push(Address::Mailbox(mailbox));
pub fn from<A: Into<Mailbox>>(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<A: IntoMailbox>(mut self, address: A) -> EmailBuilder {
let mailbox = address.into_mailbox();
self.to_header.push(Address::Mailbox(mailbox));
pub fn to<A: Into<Mailbox>>(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<A: IntoMailbox>(mut self, address: A) -> EmailBuilder {
let mailbox = address.into_mailbox();
self.cc_header.push(Address::Mailbox(mailbox));
pub fn cc<A: Into<Mailbox>>(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<A: IntoMailbox>(mut self, address: A) -> EmailBuilder {
let mailbox = address.into_mailbox();
self.bcc_header.push(Address::Mailbox(mailbox));
pub fn bcc<A: Into<Mailbox>>(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<A: IntoMailbox>(mut self, address: A) -> EmailBuilder {
let mailbox = address.into_mailbox();
self.reply_to_header.push(Address::Mailbox(mailbox));
pub fn reply_to<A: Into<Mailbox>>(mut self, address: A) -> EmailBuilder {
let mailbox = address.into();
self.reply_to.push(Address::Mailbox(mailbox));
self
}
/// Adds a `Sender` header
pub fn sender<A: IntoMailbox>(mut self, address: A) -> EmailBuilder {
let mailbox = address.into_mailbox();
self.sender_header = Some(mailbox);
pub fn sender<A: Into<Mailbox>>(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<S: Into<String>>(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<Email, Error> {
// 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<Address> 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: \
<dieter@example.com>\r\nTo: <anna@example.com>\r\nFrom: \
<dieter@example.com>, <joachim@example.com>\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: \
<sender@localhost>\r\nTo: <user@localhost>\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(),

View File

@@ -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

View File

@@ -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());
}
```

View File

@@ -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());
}
```

View File

@@ -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<NetworkStream> = Client::new();
let mut email_client: InnerClient<NetworkStream> = 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(

View File

@@ -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());
}
```