Merge branch 'devel'
This commit is contained in:
12
README.md
12
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");
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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"] }
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
```
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
```
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user