Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
53f9bada4c | ||
|
|
ce7d55ffa8 | ||
|
|
a6ea43a842 | ||
|
|
eac29768ae | ||
|
|
d944aed9d3 | ||
|
|
67318ac759 | ||
|
|
90999bfc24 | ||
|
|
7635830399 | ||
|
|
4fbd06e18b | ||
|
|
5247e2c2aa | ||
|
|
8e21de8de3 | ||
|
|
d976f48b7b | ||
|
|
9ed51a2d3d |
17
.travis.yml
17
.travis.yml
@@ -1,9 +1,16 @@
|
||||
language: rust
|
||||
|
||||
rust:
|
||||
- stable
|
||||
- beta
|
||||
- nightly
|
||||
|
||||
matrix:
|
||||
allow_failures:
|
||||
- rust: nightly
|
||||
|
||||
sudo: false
|
||||
|
||||
cache:
|
||||
apt: true
|
||||
pip: true
|
||||
@@ -12,9 +19,11 @@ cache:
|
||||
- target/debug/build
|
||||
- target/release/deps
|
||||
- target/release/build
|
||||
|
||||
install:
|
||||
- pip install 'travis-cargo<0.2' --user
|
||||
- export PATH=$HOME/.local/bin:$PATH
|
||||
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
@@ -22,16 +31,20 @@ addons:
|
||||
- libcurl4-openssl-dev
|
||||
- libelf-dev
|
||||
- libdw-dev
|
||||
|
||||
before_script:
|
||||
- smtp-sink 2525 1000&
|
||||
|
||||
script:
|
||||
- travis-cargo build
|
||||
- travis-cargo test
|
||||
- travis-cargo doc
|
||||
- travis-cargo --only stable doc-upload
|
||||
|
||||
after_success:
|
||||
- ./.travis/doc.sh
|
||||
- ./.travis/coverage.sh
|
||||
- travis-cargo --only nightly bench
|
||||
- travis-cargo --only stable coveralls --no-sudo
|
||||
|
||||
env:
|
||||
global:
|
||||
secure: "MaZ3TzuaAHuxmxQkfJdqRfkh7/ieScJRk0T/2yjysZhDMTYyRmp5wh/zkfW1ADuG0uc4Pqsxrsh1J9SVO7O0U5NJA8NKZi/pgiL+FHh0g4YtlHxy2xmFNB5am3Kyc+E7B4XylwTbA9S8ublVM0nvX7yX/a5fbwEUInVk2bA8fpc="
|
||||
|
||||
20
.travis/coverage.sh
Executable file
20
.travis/coverage.sh
Executable file
@@ -0,0 +1,20 @@
|
||||
#!/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-*
|
||||
38
.travis/doc.sh
Executable file
38
.travis/doc.sh
Executable file
@@ -0,0 +1,38 @@
|
||||
#!/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
|
||||
16
CHANGELOG.md
16
CHANGELOG.md
@@ -1,3 +1,19 @@
|
||||
### 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
|
||||
|
||||
@@ -10,7 +10,9 @@ All code must be formatted using `rustfmt`.
|
||||
|
||||
Each commit message consists of a header, a body and a footer. The header has a special format that includes a type, a scope and a subject:
|
||||
|
||||
```text
|
||||
<type>(<scope>): <subject> <BLANK LINE> <body> <BLANK LINE> <footer>
|
||||
```
|
||||
|
||||
Any line of the commit message cannot be longer 72 characters.
|
||||
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
[package]
|
||||
|
||||
name = "lettre"
|
||||
version = "0.6.0"
|
||||
version = "0.6.1"
|
||||
description = "Email client"
|
||||
readme = "README.md"
|
||||
documentation = "http://lettre.github.io/lettre/"
|
||||
documentation = "https://lettre.github.io/lettre/"
|
||||
repository = "https://github.com/lettre/lettre"
|
||||
homepage = "http://lettre.github.io/"
|
||||
license = "MIT"
|
||||
authors = ["Alexis Mousset <contact@amousset.me>"]
|
||||
keywords = ["email", "smtp", "mailer"]
|
||||
|
||||
33
README.md
33
README.md
@@ -1,13 +1,34 @@
|
||||
# lettre
|
||||
[](https://travis-ci.org/lettre/lettre)
|
||||
[](https://ci.appveyor.com/project/amousset/lettre/branch/master)
|
||||
[](https://coveralls.io/github/lettre/lettre?branch=master)
|
||||
[](https://crates.io/crates/lettre)
|
||||
[](https://coveralls.io/github/lettre/lettre?branch=master)
|
||||
[](https://crates.io/crates/lettre)
|
||||
[](./LICENSE)
|
||||
[](https://gitter.im/lettre/lettre?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
|
||||
|
||||
This is an email library written in Rust.
|
||||
See the [documentation](http://lettre.github.io/lettre) for more information.
|
||||
|
||||
## Features
|
||||
|
||||
Lettre provides the following features:
|
||||
|
||||
* Multiple transport methods
|
||||
* Unicode support (for email content and addresses)
|
||||
* Secure delivery with SMTP using encryption and authentication
|
||||
* Easy email builders
|
||||
|
||||
## Documentation
|
||||
|
||||
Released versions:
|
||||
|
||||
* [latest](https://lettre.github.io/lettre/)
|
||||
* [v0.6.1](https://lettre.github.io/lettre/v0.6.1/lettre/)
|
||||
* [v0.6.0](https://lettre.github.io/lettre/v0.6.0/lettre/)
|
||||
* [v0.5.1](https://lettre.github.io/lettre/v0.5.1/lettre/)
|
||||
|
||||
Development version:
|
||||
|
||||
* [master](https://lettre.github.io/lettre/master/lettre/)
|
||||
|
||||
## Install
|
||||
|
||||
@@ -15,15 +36,15 @@ To use this library, add the following to your `Cargo.toml`:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
lettre = "0.5"
|
||||
lettre = "0.6"
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
The tests require a mail server listening locally on port 25.
|
||||
The tests require an open mail server listening locally on port 25.
|
||||
|
||||
## License
|
||||
|
||||
This program is distributed under the terms of the MIT license.
|
||||
|
||||
See LICENSE for details.
|
||||
See [LICENSE](./LICENSE) for details.
|
||||
|
||||
@@ -3,7 +3,7 @@ pub mod error;
|
||||
|
||||
use email::error::Error;
|
||||
|
||||
use email_format::{Header, Mailbox, MimeMessage, MimeMultipartType};
|
||||
use email_format::{Header, Mailbox, Address, MimeMessage, MimeMultipartType};
|
||||
use mime::Mime;
|
||||
use std::fmt;
|
||||
use std::fmt::{Display, Formatter};
|
||||
@@ -237,6 +237,16 @@ pub struct PartBuilder {
|
||||
pub struct EmailBuilder {
|
||||
/// Message
|
||||
message: PartBuilder,
|
||||
/// The recipients' addresses for the mail header
|
||||
to_header: Vec<Address>,
|
||||
/// The sender addresses for the mail header
|
||||
from_header: Vec<Address>,
|
||||
/// The Cc addresses for the mail header
|
||||
cc_header: Vec<Address>,
|
||||
/// The Reply-To addresses for the mail header
|
||||
reply_to_header: Vec<Address>,
|
||||
/// The sender address for the mail header
|
||||
sender_header: Option<Mailbox>,
|
||||
/// The envelope recipients' addresses
|
||||
to: Vec<String>,
|
||||
/// The envelope sender address
|
||||
@@ -345,6 +355,11 @@ impl EmailBuilder {
|
||||
pub fn new() -> EmailBuilder {
|
||||
EmailBuilder {
|
||||
message: PartBuilder::new(),
|
||||
to_header: vec![],
|
||||
from_header: vec![],
|
||||
cc_header: vec![],
|
||||
reply_to_header: vec![],
|
||||
sender_header: None,
|
||||
to: vec![],
|
||||
from: None,
|
||||
date_issued: false,
|
||||
@@ -382,8 +397,8 @@ impl EmailBuilder {
|
||||
/// Adds a `From` header and stores the sender address
|
||||
pub fn add_from<A: ToMailbox>(&mut self, address: A) {
|
||||
let mailbox = address.to_mailbox();
|
||||
self.message.add_header(("From", mailbox.to_string().as_ref()));
|
||||
self.from = Some(mailbox.address);
|
||||
self.from = Some(mailbox.address.clone());
|
||||
self.from_header.push(Address::Mailbox(mailbox));
|
||||
}
|
||||
|
||||
/// Adds a `To` header and stores the recipient address
|
||||
@@ -395,8 +410,8 @@ impl EmailBuilder {
|
||||
/// Adds a `To` header and stores the recipient address
|
||||
pub fn add_to<A: ToMailbox>(&mut self, address: A) {
|
||||
let mailbox = address.to_mailbox();
|
||||
self.message.add_header(("To", mailbox.to_string().as_ref()));
|
||||
self.to.push(mailbox.address);
|
||||
self.to.push(mailbox.address.clone());
|
||||
self.to_header.push(Address::Mailbox(mailbox));
|
||||
}
|
||||
|
||||
/// Adds a `Cc` header and stores the recipient address
|
||||
@@ -408,8 +423,8 @@ impl EmailBuilder {
|
||||
/// Adds a `Cc` header and stores the recipient address
|
||||
pub fn add_cc<A: ToMailbox>(&mut self, address: A) {
|
||||
let mailbox = address.to_mailbox();
|
||||
self.message.add_header(("Cc", mailbox.to_string().as_ref()));
|
||||
self.to.push(mailbox.address);
|
||||
self.to.push(mailbox.address.clone());
|
||||
self.cc_header.push(Address::Mailbox(mailbox));
|
||||
}
|
||||
|
||||
/// Adds a `Reply-To` header
|
||||
@@ -421,7 +436,7 @@ impl EmailBuilder {
|
||||
/// Adds a `Reply-To` header
|
||||
pub fn add_reply_to<A: ToMailbox>(&mut self, address: A) {
|
||||
let mailbox = address.to_mailbox();
|
||||
self.message.add_header(("Reply-To", mailbox.to_string().as_ref()));
|
||||
self.reply_to_header.push(Address::Mailbox(mailbox));
|
||||
}
|
||||
|
||||
/// Adds a `Sender` header
|
||||
@@ -433,8 +448,8 @@ impl EmailBuilder {
|
||||
/// Adds a `Sender` header
|
||||
pub fn set_sender<A: ToMailbox>(&mut self, address: A) {
|
||||
let mailbox = address.to_mailbox();
|
||||
self.message.add_header(("Sender", mailbox.to_string().as_ref()));
|
||||
self.from = Some(mailbox.address);
|
||||
self.from = Some(mailbox.address.clone());
|
||||
self.sender_header = Some(mailbox);
|
||||
}
|
||||
|
||||
/// Adds a `Subject` header
|
||||
@@ -544,6 +559,38 @@ impl EmailBuilder {
|
||||
if self.to.is_empty() {
|
||||
return Err(Error::MissingTo);
|
||||
}
|
||||
// If there are multiple addresses in "From", the "Sender" is required.
|
||||
if self.from_header.len() >= 2 && self.sender_header.is_none() {
|
||||
// So, we must find something to put as Sender.
|
||||
for possible_sender in self.from_header.iter() {
|
||||
// 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());
|
||||
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());
|
||||
}
|
||||
// Add the sender header, if any.
|
||||
if let Some(v) = self.sender_header {
|
||||
self.message.add_header(("Sender", v.to_string().as_ref()));
|
||||
}
|
||||
// Add the collected addresses as mailbox-list all at once.
|
||||
// The unwraps are fine because the conversions for Vec<Address> never errs.
|
||||
self.message.add_header(Header::new_with_value("To".into(), self.to_header).unwrap());
|
||||
self.message.add_header(Header::new_with_value("From".into(), self.from_header).unwrap());
|
||||
if !self.cc_header.is_empty() {
|
||||
self.message.add_header(Header::new_with_value("Cc".into(), self.cc_header).unwrap());
|
||||
}
|
||||
if !self.reply_to_header.is_empty() {
|
||||
self.message.add_header(Header::new_with_value("Reply-To".into(),
|
||||
self.reply_to_header)
|
||||
.unwrap());
|
||||
}
|
||||
|
||||
|
||||
if !self.date_issued {
|
||||
self.message.add_header(("Date", Tm::rfc822z(&now()).to_string().as_ref()));
|
||||
@@ -678,6 +725,27 @@ mod test {
|
||||
assert_eq!(current_message.to_string(), email.message_id());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multiple_from() {
|
||||
let email_builder = EmailBuilder::new();
|
||||
let date_now = now();
|
||||
let email = email_builder.to("anna@example.com")
|
||||
.from("dieter@example.com")
|
||||
.from("joachim@example.com")
|
||||
.date(&date_now)
|
||||
.subject("Invitation")
|
||||
.body("We invite you!")
|
||||
.build()
|
||||
.unwrap();
|
||||
assert_eq!(format!("{}", email),
|
||||
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()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_simple_email_builder() {
|
||||
let email_builder = EmailBuilder::new();
|
||||
@@ -696,10 +764,10 @@ mod test {
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(format!("{}", email),
|
||||
format!("To: <user@localhost>\r\nFrom: <user@localhost>\r\nCc: \"Alias\" \
|
||||
<cc@localhost>\r\nReply-To: <reply@localhost>\r\nSender: \
|
||||
<sender@localhost>\r\nDate: {}\r\nSubject: Hello\r\nX-test: \
|
||||
value\r\nMIME-Version: 1.0\r\nMessage-ID: \
|
||||
format!("Date: {}\r\nSubject: Hello\r\nX-test: value\r\nSender: \
|
||||
<sender@localhost>\r\nTo: <user@localhost>\r\nFrom: \
|
||||
<user@localhost>\r\nCc: \"Alias\" <cc@localhost>\r\nReply-To: \
|
||||
<reply@localhost>\r\nMIME-Version: 1.0\r\nMessage-ID: \
|
||||
<{}.lettre@localhost>\r\n\r\nHello World!\r\n",
|
||||
date_now.rfc822z(),
|
||||
email.message_id()));
|
||||
|
||||
Reference in New Issue
Block a user