Merge pull request #128 from amousset/split-crates-transport

Split crates
This commit is contained in:
Alexis Mousset
2017-05-21 01:26:52 +02:00
committed by GitHub
45 changed files with 528 additions and 498 deletions

View File

@@ -4,6 +4,7 @@ rust:
- stable - stable
- beta - beta
- nightly - nightly
- 1.13.0
matrix: matrix:
allow_failures: allow_failures:
@@ -12,39 +13,25 @@ matrix:
sudo: required sudo: required
cache: cache:
apt: true
pip: true
directories: directories:
- target/debug/deps - target/debug/deps
- target/debug/build - target/debug/build
- target/release/deps - target/release/deps
- target/release/build - target/release/build
install:
- pip install 'travis-cargo<0.2' --user
- export PATH=$HOME/.local/bin:$PATH
addons: addons:
apt: apt:
packages: packages:
- postfix - postfix
- libcurl4-openssl-dev
- libelf-dev
- libdw-dev
before_script: before_script:
- smtp-sink 2525 1000& - smtp-sink 2525 1000&
- sudo chgrp -R postdrop /var/spool/postfix/maildrop - sudo chgrp -R postdrop /var/spool/postfix/maildrop
script: script:
- travis-cargo build - |
- travis-cargo test cd lettre && cargo build && cargo test
- travis-cargo doc cd ../lettre_email && cargo build && cargo test
after_success:
- ./.travis/doc.sh
- ./.travis/coverage.sh
- travis-cargo --only nightly bench
env: env:
global: global:

View File

@@ -1,20 +0,0 @@
#!/bin/bash
set -o errexit
if [ "$TRAVIS_RUST_VERSION" != "stable" ]; then
exit 0
fi
cargo test --no-run
wget https://github.com/SimonKagstrom/kcov/archive/master.tar.gz
tar xzf master.tar.gz
mkdir kcov-master/build
cd kcov-master/build
cmake ..
make
make install DESTDIR=../tmp
cd ../..
ls target/debug
./kcov-master/tmp/usr/local/bin/kcov --coveralls-id=$TRAVIS_JOB_ID --exclude-pattern=/.cargo target/kcov target/debug/lettre-*

View File

@@ -1,38 +0,0 @@
#!/bin/bash
set -o errexit
if [ "$TRAVIS_RUST_VERSION" != "stable" ] || [ "$TRAVIS_PULL_REQUEST" != "false" ]; then
exit 0
fi
cargo clean
cargo doc --no-deps
git clone --branch gh-pages "https://$GH_TOKEN@github.com/${TRAVIS_REPO_SLUG}.git" deploy_docs
cd deploy_docs
git config user.email "contact@amousset.me"
git config user.name "Alexis Mousset"
if [ "$TRAVIS_BRANCH" == "master" ]; then
rm -rf master
mv ../target/doc ./master
echo "<meta http-equiv=refresh content=0;url=lettre/index.html>" > ./master/index.html
elif [ "$TRAVIS_TAG" != "" ]; then
rm -rf $TRAVIS_TAG
mv ../target/doc ./$TRAVIS_TAG
echo "<meta http-equiv=refresh content=0;url=lettre/index.html>" > ./$TRAVIS_TAG/index.html
latest=$(echo * | tr " " "\n" | sort -V -r | head -n1)
if [ "$TRAVIS_TAG" == "$latest" ]; then
echo "<meta http-equiv=refresh content=0;url=$latest/lettre/index.html>" > index.html
fi
else
exit 0
fi
git add -A .
git commit -m "Rebuild pages at ${TRAVIS_COMMIT}"
git push --quiet origin gh-pages

View File

@@ -1,29 +1,5 @@
[package] [workspace]
members = [
name = "lettre" "lettre",
version = "0.6.2" "lettre_email",
description = "Email client" ]
readme = "README.md"
documentation = "https://docs.rs/lettre/"
repository = "https://github.com/lettre/lettre"
license = "MIT"
authors = ["Alexis Mousset <contact@amousset.me>"]
keywords = ["email", "smtp", "mailer"]
[dependencies]
bufstream = "^0.1"
email = "^0.0"
log = "^0.3"
mime = "^0.2"
openssl = "^0.9"
base64 = "~0.5.0"
hex = "^0.2.0"
rust-crypto = "^0.2"
time = "^0.1"
uuid = { version = ">=0.4, <0.6", features = ["v4"] }
[dev-dependencies]
env_logger = "^0.4"
[features]
unstable = []

View File

@@ -1,44 +0,0 @@
#![feature(test)]
extern crate lettre;
extern crate test;
use lettre::transport::smtp::SmtpTransportBuilder;
use lettre::transport::EmailTransport;
use lettre::email::EmailBuilder;
#[bench]
fn bench_simple_send(b: &mut test::Bencher) {
let mut sender = SmtpTransportBuilder::new("127.0.0.1:2525").unwrap().build();
b.iter(|| {
let email = EmailBuilder::new()
.to("root@localhost")
.from("user@localhost")
.body("Hello World!")
.subject("Hello")
.build()
.unwrap();
let result = sender.send(email);
assert!(result.is_ok());
});
}
#[bench]
fn bench_reuse_send(b: &mut test::Bencher) {
let mut sender = SmtpTransportBuilder::new("127.0.0.1:2525")
.unwrap()
.connection_reuse(true)
.build();
b.iter(|| {
let email = EmailBuilder::new()
.to("root@localhost")
.from("user@localhost")
.body("Hello World!")
.subject("Hello")
.build()
.unwrap();
let result = sender.send(email);
assert!(result.is_ok());
});
sender.close()
}

29
lettre/Cargo.toml Normal file
View File

@@ -0,0 +1,29 @@
[package]
name = "lettre"
version = "0.7.0"
description = "Email client"
readme = "README.md"
documentation = "https://docs.rs/lettre/"
repository = "https://github.com/lettre/lettre"
license = "MIT"
authors = ["Alexis Mousset <contact@amousset.me>"]
categories = ["email"]
keywords = ["email", "smtp", "mailer"]
[badges]
travis-ci = { repository = "lettre/lettre" }
[dependencies]
bufstream = "^0.1"
log = "^0.3"
openssl = "^0.9"
base64 = "^0.5"
hex = "^0.2"
rust-crypto = "^0.2"
[dev-dependencies]
env_logger = "^0.4"
[features]
unstable = []

1
lettre/LICENSE Symbolic link
View File

@@ -0,0 +1 @@
../LICENSE

1
lettre/README.md Symbolic link
View File

@@ -0,0 +1 @@
../README.md

View File

@@ -0,0 +1,37 @@
#![feature(test)]
extern crate lettre;
extern crate test;
use lettre::smtp::SmtpTransportBuilder;
use lettre::{EmailTransport, SimpleSendableEmail};
#[bench]
fn bench_simple_send(b: &mut test::Bencher) {
let mut sender = SmtpTransportBuilder::new("127.0.0.1:2525").unwrap().build();
b.iter(|| {
let email = SimpleSendableEmail::new("user@localhost",
vec!["root@localhost"],
"id",
"Hello world");
let result = sender.send(email);
assert!(result.is_ok());
});
}
#[bench]
fn bench_reuse_send(b: &mut test::Bencher) {
let mut sender = SmtpTransportBuilder::new("127.0.0.1:2525")
.unwrap()
.connection_reuse(true)
.build();
b.iter(|| {
let email = SimpleSendableEmail::new("user@localhost",
vec!["root@localhost"],
"file_id",
"Hello file");
let result = sender.send(email);
assert!(result.is_ok());
});
sender.close()
}

24
lettre/examples/smtp.rs Normal file
View File

@@ -0,0 +1,24 @@
extern crate lettre;
use lettre::{EmailTransport, SimpleSendableEmail};
use lettre::smtp::SmtpTransportBuilder;
fn main() {
let email = SimpleSendableEmail::new("user@localhost",
vec!["root@localhost"],
"file_id",
"Hello file");
// Open a local connection on port 25
let mut mailer = SmtpTransportBuilder::localhost().unwrap().build();
// Send the email
let result = mailer.send(email);
if result.is_ok() {
println!("Email sent");
} else {
println!("Could not send email: {:?}", result);
}
assert!(result.is_ok());
}

1
lettre/rustfmt.toml Symbolic link
View File

@@ -0,0 +1 @@
../rustfmt.toml

View File

@@ -1,12 +1,12 @@
//! This transport creates a file for each email, containing the envelope information and the email //! This transport creates a file for each email, containing the envelope information and the email
//! itself. //! itself.
use email::SendableEmail; use EmailTransport;
use SendableEmail;
use file::error::FileResult;
use std::fs::File; use std::fs::File;
use std::io::prelude::*; use std::io::prelude::*;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use transport::EmailTransport;
use transport::file::error::FileResult;
pub mod error; pub mod error;
@@ -34,8 +34,8 @@ impl EmailTransport<FileResult> for FileEmailTransport {
let log_line = format!("{}: from=<{}> to=<{}>\n", let log_line = format!("{}: from=<{}> to=<{}>\n",
email.message_id(), email.message_id(),
email.envelope().from, email.from(),
email.envelope().to.join("> to=<")); email.to().join("> to=<"));
try!(f.write_all(log_line.as_bytes())); try!(f.write_all(log_line.as_bytes()));
try!(f.write_all(email.message().as_bytes())); try!(f.write_all(email.message().as_bytes()));

View File

@@ -2,64 +2,8 @@
//! //!
//! ## Overview //! ## Overview
//! //!
//! This mailer is divided into: //! This mailer contains the available transports for your emails. To be sendable, the
//! //! emails have to implement `SendableEmail`.
//! * An `email` part: builds the email message
//! * A `transport` part: contains the available transports for your emails. To be sendable, the
//! emails have to implement `SendableEmail`.
//!
//! ## Creating messages
//!
//! The `email` part builds email messages. For now, it does not support attachments.
//! An email is built using an `EmailBuilder`. The simplest email could be:
//!
//! ```rust
//! use lettre::email::EmailBuilder;
//!
//! // Create an email
//! let email = EmailBuilder::new()
//! // Addresses can be specified by the tuple (email, alias)
//! .to(("user@example.org", "Firstname Lastname"))
//! // ... or by an address only
//! .from("user@example.com")
//! .subject("Hi, Hello world")
//! .text("Hello world.")
//! .build();
//!
//! assert!(email.is_ok());
//! ```
//!
//! When the `build` method is called, the `EmailBuilder` will add the missing headers (like
//! `Message-ID` or `Date`) and check for missing necessary ones (like `From` or `To`). It will
//! then generate an `Email` that can be sent.
//!
//! The `text()` method will create a plain text email, while the `html()` method will create an
//! HTML email. You can use the `alternative()` method to provide both versions, using plain text
//! as fallback for the HTML version.
//!
//! Below is a more complete example, not using method chaining:
//!
//! ```rust
//! use lettre::email::EmailBuilder;
//!
//! let mut builder = EmailBuilder::new();
//! builder.add_to(("user@example.org", "Alias name"));
//! builder.add_cc(("user@example.net", "Alias name"));
//! builder.add_from("no-reply@example.com");
//! builder.add_from("no-reply@example.eu");
//! builder.set_sender("no-reply@example.com");
//! builder.set_subject("Hello world");
//! builder.set_alternative("<h2>Hi, Hello world.</h2>", "Hi, Hello world.");
//! builder.add_reply_to("contact@example.com");
//! builder.add_header(("X-Custom-Header", "my header"));
//!
//! let email = builder.build();
//! assert!(email.is_ok());
//! ```
//!
//! See the `EmailBuilder` documentation for a complete list of methods.
//!
//! ## Sending messages
//! //!
//! The following sections describe the available transport methods to handle emails. //! The following sections describe the available transport methods to handle emails.
//! //!
@@ -93,18 +37,16 @@
//! This is the most basic example of usage: //! This is the most basic example of usage:
//! //!
//! ```rust,no_run //! ```rust,no_run
//! use lettre::transport::smtp::SmtpTransportBuilder; //! use lettre::{SimpleSendableEmail, EmailTransport};
//! use lettre::email::EmailBuilder; //! use lettre::smtp::SmtpTransportBuilder;
//! use lettre::transport::EmailTransport; //! use lettre::smtp::SecurityLevel;
//! use lettre::transport::smtp::SecurityLevel;
//! //!
//! let email = EmailBuilder::new() //! let email = SimpleSendableEmail::new(
//! .to("root@localhost") //! "user@localhost",
//! .from("user@localhost") //! vec!["root@localhost"],
//! .body("Hello World!") //! "message_id",
//! .subject("Hello") //! "Hello world"
//! .build() //! );
//! .unwrap();
//! //!
//! // Open a local connection on port 25 //! // Open a local connection on port 25
//! let mut mailer = //! let mut mailer =
@@ -118,20 +60,18 @@
//! #### Complete example //! #### Complete example
//! //!
//! ```rust,no_run //! ```rust,no_run
//! use lettre::email::EmailBuilder; //! use lettre::smtp::{SecurityLevel, SmtpTransport,
//! use lettre::transport::smtp::{SecurityLevel, SmtpTransport,
//! SmtpTransportBuilder}; //! SmtpTransportBuilder};
//! use lettre::transport::smtp::authentication::Mechanism; //! use lettre::smtp::authentication::Mechanism;
//! use lettre::transport::smtp::SUBMISSION_PORT; //! use lettre::smtp::SUBMISSION_PORT;
//! use lettre::transport::EmailTransport; //! use lettre::{SimpleSendableEmail, EmailTransport};
//! //!
//! let email = EmailBuilder::new() //! let email = SimpleSendableEmail::new(
//! .to("root@localhost") //! "user@localhost",
//! .from("user@localhost") //! vec!["root@localhost"],
//! .body("Hello World!") //! "message_id",
//! .subject("Hello") //! "Hello world"
//! .build() //! );
//! .unwrap();
//! //!
//! // Connect to a remote server on a custom port //! // Connect to a remote server on a custom port
//! let mut mailer = SmtpTransportBuilder::new(("server.tld", //! let mut mailer = SmtpTransportBuilder::new(("server.tld",
@@ -167,9 +107,9 @@
//! error handling: //! error handling:
//! //!
//! ```rust //! ```rust
//! use lettre::transport::smtp::SMTP_PORT; //! use lettre::smtp::SMTP_PORT;
//! use lettre::transport::smtp::client::Client; //! use lettre::smtp::client::Client;
//! use lettre::transport::smtp::client::net::NetworkStream; //! use lettre::smtp::client::net::NetworkStream;
//! //!
//! let mut email_client: Client<NetworkStream> = Client::new(); //! let mut email_client: Client<NetworkStream> = Client::new();
//! let _ = email_client.connect(&("localhost", SMTP_PORT), None); //! let _ = email_client.connect(&("localhost", SMTP_PORT), None);
@@ -186,17 +126,15 @@
//! The sendmail transport sends the email using the local sendmail command. //! The sendmail transport sends the email using the local sendmail command.
//! //!
//! ```rust //! ```rust
//! use lettre::transport::sendmail::SendmailTransport; //! use lettre::sendmail::SendmailTransport;
//! use lettre::transport::EmailTransport; //! use lettre::{SimpleSendableEmail, EmailTransport};
//! use lettre::email::EmailBuilder;
//! //!
//! let email = EmailBuilder::new() //! let email = SimpleSendableEmail::new(
//! .to("root@localhost") //! "user@localhost",
//! .from("user@localhost") //! vec!["root@localhost"],
//! .body("Hello World!") //! "message_id",
//! .subject("Hello") //! "Hello world"
//! .build() //! );
//! .unwrap();
//! //!
//! let mut sender = SendmailTransport::new(); //! let mut sender = SendmailTransport::new();
//! let result = sender.send(email); //! let result = sender.send(email);
@@ -209,17 +147,15 @@
//! testing purposes. //! testing purposes.
//! //!
//! ```rust //! ```rust
//! use lettre::transport::stub::StubEmailTransport; //! use lettre::stub::StubEmailTransport;
//! use lettre::transport::EmailTransport; //! use lettre::{SimpleSendableEmail, EmailTransport};
//! use lettre::email::EmailBuilder;
//! //!
//! let email = EmailBuilder::new() //! let email = SimpleSendableEmail::new(
//! .to("root@localhost") //! "user@localhost",
//! .from("user@localhost") //! vec!["root@localhost"],
//! .body("Hello World!") //! "message_id",
//! .subject("Hello") //! "Hello world"
//! .build() //! );
//! .unwrap();
//! //!
//! let mut sender = StubEmailTransport; //! let mut sender = StubEmailTransport;
//! let result = sender.send(email); //! let result = sender.send(email);
@@ -241,19 +177,17 @@
//! ```rust //! ```rust
//! use std::env::temp_dir; //! use std::env::temp_dir;
//! //!
//! use lettre::transport::file::FileEmailTransport; //! use lettre::file::FileEmailTransport;
//! use lettre::transport::EmailTransport; //! use lettre::{SimpleSendableEmail, EmailTransport};
//! use lettre::email::{EmailBuilder, SendableEmail};
//! //!
//! // Write to the local temp directory //! // Write to the local temp directory
//! let mut sender = FileEmailTransport::new(temp_dir()); //! let mut sender = FileEmailTransport::new(temp_dir());
//! let email = EmailBuilder::new() //! let email = SimpleSendableEmail::new(
//! .to("root@localhost") //! "user@localhost",
//! .from("user@localhost") //! vec!["root@localhost"],
//! .body("Hello World!") //! "message_id",
//! .subject("Hello") //! "Hello world"
//! .build() //! );
//! .unwrap();
//! //!
//! let result = sender.send(email); //! let result = sender.send(email);
//! assert!(result.is_ok()); //! assert!(result.is_ok());
@@ -275,16 +209,80 @@
#[macro_use] #[macro_use]
extern crate log; extern crate log;
#[macro_use]
extern crate mime;
extern crate base64; extern crate base64;
extern crate hex; extern crate hex;
extern crate crypto; extern crate crypto;
extern crate time;
extern crate uuid;
extern crate email as email_format;
extern crate bufstream; extern crate bufstream;
extern crate openssl; extern crate openssl;
pub mod transport; pub mod smtp;
pub mod email; pub mod sendmail;
pub mod stub;
pub mod file;
/// Email sendable by an SMTP client
pub trait SendableEmail {
/// To
fn to(&self) -> Vec<String>;
/// From
fn from(&self) -> String;
/// Message ID, used for logging
fn message_id(&self) -> String;
/// Message content
fn message(self) -> String;
}
/// Transport method for emails
pub trait EmailTransport<U> {
/// Sends the email
fn send<T: SendableEmail>(&mut self, email: T) -> U;
/// Close the transport explicitly
fn close(&mut self);
}
/// Minimal email structure
#[derive(Debug,Clone)]
pub struct SimpleSendableEmail {
/// To
to: Vec<String>,
/// From
from: String,
/// Message ID
message_id: String,
/// Message content
message: String,
}
impl SimpleSendableEmail {
/// Returns a new email
pub fn new(from_address: &str,
to_addresses: Vec<&str>,
message_id: &str,
message: &str)
-> SimpleSendableEmail {
SimpleSendableEmail {
from: from_address.to_string(),
to: to_addresses.iter().map(|s| s.to_string()).collect(),
message_id: message_id.to_string(),
message: message.to_string(),
}
}
}
impl SendableEmail for SimpleSendableEmail {
fn to(&self) -> Vec<String> {
self.to.clone()
}
fn from(&self) -> String {
self.from.clone()
}
fn message_id(&self) -> String {
self.message_id.clone()
}
fn message(self) -> String {
self.message
}
}

View File

@@ -1,12 +1,12 @@
//! This transport uilizes the sendmail executable for each email. //! This transport uilizes the sendmail executable for each email.
use email::SendableEmail;
use EmailTransport;
use SendableEmail;
use sendmail::error::SendmailResult;
use std::io::prelude::*; use std::io::prelude::*;
use std::process::{Command, Stdio}; use std::process::{Command, Stdio};
use transport::EmailTransport;
use transport::sendmail::error::SendmailResult;
pub mod error; pub mod error;
/// Sends an email using the `sendmail` command /// Sends an email using the `sendmail` command
@@ -31,10 +31,7 @@ impl EmailTransport<SendmailResult> for SendmailTransport {
fn send<T: SendableEmail>(&mut self, email: T) -> SendmailResult { fn send<T: SendableEmail>(&mut self, email: T) -> SendmailResult {
// Spawn the sendmail command // Spawn the sendmail command
let mut process = try!(Command::new(&self.command) let mut process = try!(Command::new(&self.command)
.args(&["-i", .args(&["-i", "-f", &email.from(), &email.to().join(" ")])
"-f",
&email.envelope().from,
&email.envelope().to.join(" ")])
.stdin(Stdio::piped()) .stdin(Stdio::piped())
.stdout(Stdio::piped()) .stdout(Stdio::piped())
.spawn()); .spawn());

View File

@@ -4,10 +4,10 @@ use crypto::hmac::Hmac;
use crypto::mac::Mac; use crypto::mac::Mac;
use crypto::md5::Md5; use crypto::md5::Md5;
use hex::ToHex; use hex::ToHex;
use smtp::NUL;
use smtp::error::Error;
use std::fmt; use std::fmt;
use std::fmt::{Display, Formatter}; use std::fmt::{Display, Formatter};
use transport::smtp::NUL;
use transport::smtp::error::Error;
/// Represents authentication mechanisms /// Represents authentication mechanisms
#[derive(PartialEq,Eq,Copy,Clone,Hash,Debug)] #[derive(PartialEq,Eq,Copy,Clone,Hash,Debug)]
@@ -26,13 +26,11 @@ pub enum Mechanism {
impl Display for Mechanism { impl Display for Mechanism {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, write!(f, "{}", match *self {
"{}", Mechanism::Plain => "PLAIN",
match *self { Mechanism::Login => "LOGIN",
Mechanism::Plain => "PLAIN", Mechanism::CramMd5 => "CRAM-MD5",
Mechanism::Login => "LOGIN", })
Mechanism::CramMd5 => "CRAM-MD5",
})
} }
} }

View File

@@ -1,20 +1,20 @@
//! SMTP client //! SMTP client
use bufstream::BufStream;
use openssl::ssl::SslContext;
use base64; use base64;
use bufstream::BufStream;
use openssl::ssl::SslContext;
use smtp::{CRLF, MESSAGE_ENDING};
use smtp::authentication::Mechanism;
use smtp::client::net::{Connector, NetworkStream, Timeout};
use smtp::error::{Error, SmtpResult};
use smtp::response::ResponseParser;
use std::fmt::Debug; use std::fmt::Debug;
use std::io; use std::io;
use std::io::{BufRead, Read, Write}; use std::io::{BufRead, Read, Write};
use std::net::ToSocketAddrs; use std::net::ToSocketAddrs;
use std::string::String; use std::string::String;
use std::time::Duration; use std::time::Duration;
use transport::smtp::{CRLF, MESSAGE_ENDING};
use transport::smtp::authentication::Mechanism;
use transport::smtp::client::net::{Connector, NetworkStream, Timeout};
use transport::smtp::error::{Error, SmtpResult};
use transport::smtp::response::ResponseParser;
pub mod net; pub mod net;
@@ -203,8 +203,12 @@ impl<S: Connector + Timeout + Write + Read + Debug> Client<S> {
if mechanism.supports_initial_response() { if mechanism.supports_initial_response() {
self.command(&format!("AUTH {} {}", self.command(&format!("AUTH {} {}",
mechanism, mechanism,
base64::encode_config(try!(mechanism.response(username, password, None)).as_bytes(), base64::STANDARD))) base64::encode_config(try!(mechanism.response(username,
password,
None))
.as_bytes(),
base64::STANDARD)))
} else { } else {
let encoded_challenge = match try!(self.command(&format!("AUTH {}", mechanism))) let encoded_challenge = match try!(self.command(&format!("AUTH {}", mechanism)))
.first_word() { .first_word() {
@@ -232,7 +236,8 @@ impl<S: Connector + Timeout + Write + Read + Debug> Client<S> {
let response = try!(self.command(&base64::encode_config(&try!(mechanism.response(username, let response = try!(self.command(&base64::encode_config(&try!(mechanism.response(username,
password, password,
Some(&decoded_challenge))) Some(&decoded_challenge)))
.as_bytes(), base64::STANDARD))); .as_bytes(),
base64::STANDARD)));
if !response.has_code(334) { if !response.has_code(334) {
return Ok(response); return Ok(response);

View File

@@ -2,12 +2,12 @@
use self::Error::*; use self::Error::*;
use base64::DecodeError; use base64::DecodeError;
use smtp::response::{Response, Severity};
use std::error::Error as StdError; use std::error::Error as StdError;
use std::fmt; use std::fmt;
use std::fmt::{Display, Formatter}; use std::fmt::{Display, Formatter};
use std::io; use std::io;
use std::string::FromUtf8Error; use std::string::FromUtf8Error;
use transport::smtp::response::{Response, Severity};
/// An enum of all error kinds. /// An enum of all error kinds.
#[derive(Debug)] #[derive(Debug)]

View File

@@ -1,12 +1,12 @@
//! ESMTP features //! ESMTP features
use smtp::authentication::Mechanism;
use smtp::error::Error;
use smtp::response::Response;
use std::collections::HashSet; use std::collections::HashSet;
use std::fmt; use std::fmt;
use std::fmt::{Display, Formatter}; use std::fmt::{Display, Formatter};
use std::result::Result; use std::result::Result;
use transport::smtp::authentication::Mechanism;
use transport::smtp::error::Error;
use transport::smtp::response::Response;
/// Supported ESMTP keywords /// Supported ESMTP keywords
#[derive(PartialEq,Eq,Hash,Clone,Debug)] #[derive(PartialEq,Eq,Hash,Clone,Debug)]
@@ -53,13 +53,10 @@ pub struct ServerInfo {
impl Display for ServerInfo { impl Display for ServerInfo {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, write!(f, "{} with {}", self.name, match self.features.is_empty() {
"{} with {}", true => "no supported features".to_string(),
self.name, false => format!("{:?}", self.features),
match self.features.is_empty() { })
true => "no supported features".to_string(),
false => format!("{:?}", self.features),
})
} }
} }
@@ -125,9 +122,9 @@ impl ServerInfo {
mod test { mod test {
use super::{Extension, ServerInfo}; use super::{Extension, ServerInfo};
use smtp::authentication::Mechanism;
use smtp::response::{Category, Code, Response, Severity};
use std::collections::HashSet; use std::collections::HashSet;
use transport::smtp::authentication::Mechanism;
use transport::smtp::response::{Category, Code, Response, Severity};
#[test] #[test]
fn test_extension_fmt() { fn test_extension_fmt() {

View File

@@ -1,15 +1,15 @@
//! This transport sends emails using the SMTP protocol //! This transport sends emails using the SMTP protocol
use email::SendableEmail; use EmailTransport;
use SendableEmail;
use openssl::ssl::{SslContext, SslMethod}; use openssl::ssl::{SslContext, SslMethod};
use smtp::authentication::Mechanism;
use smtp::client::Client;
use smtp::error::{Error, SmtpResult};
use smtp::extension::{Extension, ServerInfo};
use std::net::{SocketAddr, ToSocketAddrs}; use std::net::{SocketAddr, ToSocketAddrs};
use std::string::String; use std::string::String;
use std::time::Duration; use std::time::Duration;
use transport::EmailTransport;
use transport::smtp::authentication::Mechanism;
use transport::smtp::client::Client;
use transport::smtp::error::{Error, SmtpResult};
use transport::smtp::extension::{Extension, ServerInfo};
pub mod extension; pub mod extension;
pub mod authentication; pub mod authentication;
@@ -318,7 +318,9 @@ impl EmailTransport<SmtpResult> for SmtpTransport {
.as_ref() .as_ref()
.unwrap() .unwrap()
.supports_feature(&Extension::StartTls)) { .supports_feature(&Extension::StartTls)) {
(&SecurityLevel::AlwaysEncrypt, false) => return Err(From::from("Could not encrypt connection, aborting")), (&SecurityLevel::AlwaysEncrypt, false) => {
return Err(From::from("Could not encrypt connection, aborting"))
}
(&SecurityLevel::Opportunistic, false) => (), (&SecurityLevel::Opportunistic, false) => (),
(&SecurityLevel::NeverEncrypt, _) => (), (&SecurityLevel::NeverEncrypt, _) => (),
(&SecurityLevel::EncryptedWrapper, _) => (), (&SecurityLevel::EncryptedWrapper, _) => (),
@@ -387,13 +389,13 @@ impl EmailTransport<SmtpResult> for SmtpTransport {
(false, _) => None, (false, _) => None,
}; };
try_smtp!(self.client.mail(&email.envelope().from, mail_options), self); try_smtp!(self.client.mail(&email.from(), mail_options), self);
// Log the mail command // Log the mail command
info!("{}: from=<{}>", message_id, email.envelope().from); info!("{}: from=<{}>", message_id, email.from());
// Recipient // Recipient
for to_address in &email.envelope().to { for to_address in &email.to() {
try_smtp!(self.client.rcpt(&to_address), self); try_smtp!(self.client.rcpt(&to_address), self);
// Log the rcpt command // Log the rcpt command
info!("{}: to=<{}>", message_id, to_address); info!("{}: to=<{}>", message_id, to_address);

View File

@@ -3,10 +3,10 @@
use self::Category::*; use self::Category::*;
use self::Severity::*; use self::Severity::*;
use smtp::error::{Error, SmtpResult};
use std::fmt::{Display, Formatter, Result}; use std::fmt::{Display, Formatter, Result};
use std::result; use std::result;
use std::str::FromStr; use std::str::FromStr;
use transport::smtp::error::{Error, SmtpResult};
/// First digit indicates severity /// First digit indicates severity
#[derive(PartialEq,Eq,Copy,Clone,Debug)] #[derive(PartialEq,Eq,Copy,Clone,Debug)]
@@ -36,14 +36,12 @@ impl FromStr for Severity {
impl Display for Severity { impl Display for Severity {
fn fmt(&self, f: &mut Formatter) -> Result { fn fmt(&self, f: &mut Formatter) -> Result {
write!(f, write!(f, "{}", match *self {
"{}", PositiveCompletion => 2,
match *self { PositiveIntermediate => 3,
PositiveCompletion => 2, TransientNegativeCompletion => 4,
PositiveIntermediate => 3, PermanentNegativeCompletion => 5,
TransientNegativeCompletion => 4, })
PermanentNegativeCompletion => 5,
})
} }
} }
@@ -81,16 +79,14 @@ impl FromStr for Category {
impl Display for Category { impl Display for Category {
fn fmt(&self, f: &mut Formatter) -> Result { fn fmt(&self, f: &mut Formatter) -> Result {
write!(f, write!(f, "{}", match *self {
"{}", Syntax => 0,
match *self { Information => 1,
Syntax => 0, Connections => 2,
Information => 1, Unspecified3 => 3,
Connections => 2, Unspecified4 => 4,
Unspecified3 => 3, MailSystem => 5,
Unspecified4 => 4, })
MailSystem => 5,
})
} }
} }

View File

@@ -1,8 +1,8 @@
//! This transport is a stub that only logs the message, and always returns //! This transport is a stub that only logs the message, and always returns
//! success //! success
use email::SendableEmail; use EmailTransport;
use transport::EmailTransport; use SendableEmail;
pub mod error; pub mod error;
@@ -18,8 +18,8 @@ impl EmailTransport<StubResult> for StubEmailTransport {
info!("{}: from=<{}> to=<{:?}>", info!("{}: from=<{}> to=<{:?}>",
email.message_id(), email.message_id(),
email.envelope().from, email.from(),
email.envelope().to); email.to());
Ok(()) Ok(())
} }

View File

@@ -1,9 +1,8 @@
extern crate lettre; extern crate lettre;
use lettre::email::{EmailBuilder, SendableEmail}; use lettre::{EmailTransport, SendableEmail, SimpleSendableEmail};
use lettre::transport::EmailTransport;
use lettre::transport::file::FileEmailTransport; use lettre::file::FileEmailTransport;
use std::env::temp_dir; use std::env::temp_dir;
use std::fs::File; use std::fs::File;
use std::fs::remove_file; use std::fs::remove_file;
@@ -12,13 +11,10 @@ use std::io::Read;
#[test] #[test]
fn file_transport() { fn file_transport() {
let mut sender = FileEmailTransport::new(temp_dir()); let mut sender = FileEmailTransport::new(temp_dir());
let email = EmailBuilder::new() let email = SimpleSendableEmail::new("user@localhost",
.to("root@localhost") vec!["root@localhost"],
.from("user@localhost") "file_id",
.body("Hello World!") "Hello file");
.subject("Hello file")
.build()
.unwrap();
let result = sender.send(email.clone()); let result = sender.send(email.clone());
assert!(result.is_ok()); assert!(result.is_ok());

View File

@@ -0,0 +1,17 @@
extern crate lettre;
use lettre::{EmailTransport, SimpleSendableEmail};
use lettre::sendmail::SendmailTransport;
#[test]
fn sendmail_transport_simple() {
let mut sender = SendmailTransport::new();
let email = SimpleSendableEmail::new("user@localhost",
vec!["root@localhost"],
"sendmail_id",
"Hello sendmail");
let result = sender.send(email);
println!("{:?}", result);
assert!(result.is_ok());
}

View File

@@ -0,0 +1,20 @@
extern crate lettre;
use lettre::{EmailTransport, SimpleSendableEmail};
use lettre::smtp::SecurityLevel;
use lettre::smtp::SmtpTransportBuilder;
#[test]
fn smtp_transport_simple() {
let mut sender = SmtpTransportBuilder::new("127.0.0.1:2525")
.unwrap()
.security_level(SecurityLevel::Opportunistic)
.build();
let email = SimpleSendableEmail::new("user@localhost",
vec!["root@localhost"],
"smtp_id",
"Hello smtp");
let result = sender.send(email);
assert!(result.is_ok());
}

View File

@@ -0,0 +1,16 @@
extern crate lettre;
use lettre::{EmailTransport, SimpleSendableEmail};
use lettre::stub::StubEmailTransport;
#[test]
fn stub_transport() {
let mut sender = StubEmailTransport;
let email = SimpleSendableEmail::new("user@localhost",
vec!["root@localhost"],
"stub_id",
"Hello stub");
let result = sender.send(email);
assert!(result.is_ok());
}

39
lettre_email/CHANGELOG.md Normal file
View File

@@ -0,0 +1,39 @@
### v0.6.2 (2017-02-18)
#### Features
* **all**
* Update uuid crate to 0.4
* Update env-logger crate to 0.4
* Update openssl crate to 0.9
### v0.6.1 (2016-10-19)
#### Features
* **documentation**
* #91: Build seperate docs for each release
* #96: Add complete documentation information to README
#### Bugfixes
* **email**
* #85: Use address-list for "To", "From" etc.
* **tests**
* #93: Force building tests before coverage computing
### v0.6.0 (2016-05-05)
#### Features
* **email**
* multipart support
* add non-consuming methods for Email builders
#### Beaking Change
* **email**
* `add_header` does not return the builder anymore,
for consistency with other methods. Use the `header`
method instead

22
lettre_email/Cargo.toml Normal file
View File

@@ -0,0 +1,22 @@
[package]
name = "lettre_email"
version = "0.7.0"
description = "Email builder"
readme = "README.md"
documentation = "https://docs.rs/lettre_email/"
repository = "https://github.com/lettre/lettre"
license = "MIT"
authors = ["Alexis Mousset <contact@amousset.me>"]
categories = ["email"]
keywords = ["email", "mailer"]
[badges]
travis-ci = { repository = "lettre/lettre_email" }
[dependencies]
email = "^0.0"
mime = "^0.2"
time = "^0.1"
uuid = { version = ">=0.4, <0.6", features = ["v4"] }
lettre = { path = "../lettre" }

1
lettre_email/LICENSE Symbolic link
View File

@@ -0,0 +1 @@
../LICENSE

1
lettre_email/README.md Symbolic link
View File

@@ -0,0 +1 @@
../README.md

View File

@@ -0,0 +1,31 @@
extern crate lettre;
extern crate lettre_email;
use lettre_email::email::EmailBuilder;
use lettre::EmailTransport;
use lettre::smtp::SmtpTransportBuilder;
fn main() {
let email = EmailBuilder::new()
// Addresses can be specified by the tuple (email, alias)
.to(("user@example.org", "Firstname Lastname"))
// ... or by an address only
.from("user@example.com")
.subject("Hi, Hello world")
.text("Hello world.")
.build()
.unwrap();
// Open a local connection on port 25
let mut mailer = SmtpTransportBuilder::localhost().unwrap().build();
// Send the email
let result = mailer.send(email);
if result.is_ok() {
println!("Email sent");
} else {
println!("Could not send email: {:?}", result);
}
assert!(result.is_ok());
}

1
lettre_email/rustfmt.toml Symbolic link
View File

@@ -0,0 +1 @@
../rustfmt.toml

View File

@@ -1,14 +1,13 @@
//! Simple email representation //! Simple email representation
pub mod error; use error::Error;
use email::error::Error;
use email_format::{Address, Header, Mailbox, MimeMessage, MimeMultipartType}; use email_format::{Address, Header, Mailbox, MimeMessage, MimeMultipartType};
use mime::Mime; use mime::Mime;
use std::fmt; use std::fmt;
use std::fmt::{Display, Formatter}; use std::fmt::{Display, Formatter};
use time::{Tm, now}; use time::{Tm, now};
use uuid::Uuid; use uuid::Uuid;
use lettre::SendableEmail;
/// Converts an address or an address with an alias to a `Header` /// Converts an address or an address with an alias to a `Header`
pub trait IntoHeader { pub trait IntoHeader {
@@ -638,8 +637,7 @@ impl EmailBuilder {
} }
// Add the sender header, if any. // Add the sender header, if any.
if let Some(ref v) = self.sender_header { if let Some(ref v) = self.sender_header {
self.message self.message.add_header(("Sender", v.to_string().as_ref()));
.add_header(("Sender", v.to_string().as_ref()));
} }
// Calculate the envelope // Calculate the envelope
let envelope = match self.envelope { let envelope = match self.envelope {
@@ -732,58 +730,13 @@ impl EmailBuilder {
} }
} }
/// Email sendable by an SMTP client
pub trait SendableEmail {
/// Envelope
fn envelope(&self) -> &Envelope;
/// Message ID
fn message_id(&self) -> String;
/// Message content
fn message(self) -> String;
}
/// Minimal email structure
#[derive(Debug)]
pub struct SimpleSendableEmail {
/// Message envelope
envelope: Envelope,
/// Message content
message: String,
}
impl SimpleSendableEmail {
/// Returns a new email
pub fn new(from_address: String,
to_addresses: Vec<String>,
message: String)
-> SimpleSendableEmail {
SimpleSendableEmail {
envelope: Envelope {
from: from_address,
to: to_addresses,
},
message: message,
}
}
}
impl SendableEmail for SimpleSendableEmail {
fn envelope(&self) -> &Envelope {
&self.envelope
}
fn message_id(&self) -> String {
format!("{}", Uuid::new_v4())
}
fn message(self) -> String {
self.message
}
}
impl SendableEmail for Email { impl SendableEmail for Email {
fn envelope(&self) -> &Envelope { fn to(&self) -> Vec<String> {
&self.envelope self.envelope.to.clone()
}
fn from(&self) -> String {
self.envelope.from.clone()
} }
fn message_id(&self) -> String { fn message_id(&self) -> String {
@@ -823,7 +776,8 @@ pub trait ExtractableEmail {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::{Email, EmailBuilder, Envelope, IntoEmail, SendableEmail, SimpleEmail}; use lettre::SendableEmail;
use super::{Email, EmailBuilder, Envelope, IntoEmail, SimpleEmail};
use email_format::{Header, MimeMessage}; use email_format::{Header, MimeMessage};
use time::now; use time::now;
@@ -959,8 +913,8 @@ mod test {
.build() .build()
.unwrap(); .unwrap();
assert_eq!(email.envelope().from, "sender@localhost".to_string()); assert_eq!(email.from(), "sender@localhost".to_string());
assert_eq!(email.envelope().to, assert_eq!(email.to(),
vec!["user@localhost".to_string(), vec!["user@localhost".to_string(),
"cc@localhost".to_string(), "cc@localhost".to_string(),
"bcc@localhost".to_string()]); "bcc@localhost".to_string()]);

65
lettre_email/src/lib.rs Normal file
View File

@@ -0,0 +1,65 @@
//! Lettre is a mailer written in Rust. It provides a simple email builder and several transports.
//!
//! ## Overview
//!
//! The `email` part builds email messages. For now, it does not support attachments.
//! An email is built using an `EmailBuilder`. The simplest email could be:
//!
//! ```rust
//! use lettre_email::email::EmailBuilder;
//!
//! // Create an email
//! let email = EmailBuilder::new()
//! // Addresses can be specified by the tuple (email, alias)
//! .to(("user@example.org", "Firstname Lastname"))
//! // ... or by an address only
//! .from("user@example.com")
//! .subject("Hi, Hello world")
//! .text("Hello world.")
//! .build();
//!
//! assert!(email.is_ok());
//! ```
//!
//! When the `build` method is called, the `EmailBuilder` will add the missing headers (like
//! `Message-ID` or `Date`) and check for missing necessary ones (like `From` or `To`). It will
//! then generate an `Email` that can be sent.
//!
//! The `text()` method will create a plain text email, while the `html()` method will create an
//! HTML email. You can use the `alternative()` method to provide both versions, using plain text
//! as fallback for the HTML version.
//!
//! Below is a more complete example, not using method chaining:
//!
//! ```rust
//! use lettre_email::email::EmailBuilder;
//!
//! let mut builder = EmailBuilder::new();
//! builder.add_to(("user@example.org", "Alias name"));
//! builder.add_cc(("user@example.net", "Alias name"));
//! builder.add_from("no-reply@example.com");
//! builder.add_from("no-reply@example.eu");
//! builder.set_sender("no-reply@example.com");
//! builder.set_subject("Hello world");
//! builder.set_alternative("<h2>Hi, Hello world.</h2>", "Hi, Hello world.");
//! builder.add_reply_to("contact@example.com");
//! builder.add_header(("X-Custom-Header", "my header"));
//!
//! let email = builder.build();
//! assert!(email.is_ok());
//! ```
//!
//! See the `EmailBuilder` documentation for a complete list of methods.
//!
#![deny(missing_docs, unsafe_code, unstable_features, warnings, missing_debug_implementations)]
#[macro_use]
extern crate mime;
extern crate time;
extern crate uuid;
extern crate email as email_format;
extern crate lettre;
pub mod error;
pub mod email;

View File

@@ -1,2 +0,0 @@
reorder_imports = true
reorder_imported_names = true

View File

@@ -1,16 +0,0 @@
//! Represents an Email transport
pub mod smtp;
pub mod sendmail;
pub mod stub;
pub mod file;
use email::SendableEmail;
/// Transport method for emails
pub trait EmailTransport<U> {
/// Sends the email
fn send<T: SendableEmail>(&mut self, email: T) -> U;
/// Close the transport explicitly
fn close(&mut self);
}

View File

@@ -1,20 +0,0 @@
extern crate lettre;
use lettre::email::EmailBuilder;
use lettre::transport::EmailTransport;
use lettre::transport::sendmail::SendmailTransport;
#[test]
fn sendmail_transport_simple() {
let mut sender = SendmailTransport::new();
let email = EmailBuilder::new()
.to("root@localhost")
.from("user@localhost")
.body("Hello World!")
.subject("Hello sendmail")
.build()
.unwrap();
let result = sender.send(email);
println!("{:?}", result);
assert!(result.is_ok());
}

View File

@@ -1,23 +0,0 @@
extern crate lettre;
use lettre::email::EmailBuilder;
use lettre::transport::EmailTransport;
use lettre::transport::smtp::SecurityLevel;
use lettre::transport::smtp::SmtpTransportBuilder;
#[test]
fn smtp_transport_simple() {
let mut sender = SmtpTransportBuilder::new("127.0.0.1:2525")
.unwrap()
.security_level(SecurityLevel::Opportunistic)
.build();
let email = EmailBuilder::new()
.to("root@localhost")
.from("user@localhost")
.body("Hello World!")
.subject("Hello smtp")
.build()
.unwrap();
let result = sender.send(email);
assert!(result.is_ok());
}

View File

@@ -1,19 +0,0 @@
extern crate lettre;
use lettre::email::EmailBuilder;
use lettre::transport::EmailTransport;
use lettre::transport::stub::StubEmailTransport;
#[test]
fn stub_transport() {
let mut sender = StubEmailTransport;
let email = EmailBuilder::new()
.to("root@localhost")
.from("user@localhost")
.body("Hello World!")
.subject("Hello stub")
.build()
.unwrap();
let result = sender.send(email);
assert!(result.is_ok());
}