Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
58026d8318 | ||
|
|
d86900e37e | ||
|
|
9dadc4d6eb | ||
|
|
bb7815a366 | ||
|
|
b24c0a367d | ||
|
|
e0637c9ed4 | ||
|
|
6c3de0e85b | ||
|
|
692f8a5346 | ||
|
|
60f06f1682 | ||
|
|
29b130c919 |
@@ -1,3 +1,12 @@
|
|||||||
|
<a name="v0.8.4"></a>
|
||||||
|
### v0.8.4 (2020-11-11)
|
||||||
|
|
||||||
|
#### Bug Fixes
|
||||||
|
|
||||||
|
* **transport**
|
||||||
|
|
||||||
|
* **SECURITY**: Prevent argument injection in sendmail transport
|
||||||
|
|
||||||
<a name="v0.8.2"></a>
|
<a name="v0.8.2"></a>
|
||||||
### v0.8.2 (2018-05-03)
|
### v0.8.2 (2018-05-03)
|
||||||
|
|
||||||
|
|||||||
@@ -46,7 +46,6 @@ lettre_email = "0.8"
|
|||||||
```rust,no_run
|
```rust,no_run
|
||||||
extern crate lettre;
|
extern crate lettre;
|
||||||
extern crate lettre_email;
|
extern crate lettre_email;
|
||||||
extern crate mime;
|
|
||||||
|
|
||||||
use lettre::{EmailTransport, SmtpTransport};
|
use lettre::{EmailTransport, SmtpTransport};
|
||||||
use lettre_email::EmailBuilder;
|
use lettre_email::EmailBuilder;
|
||||||
@@ -60,7 +59,6 @@ fn main() {
|
|||||||
.from("user@example.com")
|
.from("user@example.com")
|
||||||
.subject("Hi, Hello world")
|
.subject("Hi, Hello world")
|
||||||
.text("Hello world.")
|
.text("Hello world.")
|
||||||
.attachment(Path::new("Cargo.toml"), None, &mime::TEXT_PLAIN).unwrap()
|
|
||||||
.build()
|
.build()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
|
|
||||||
name = "lettre"
|
name = "lettre"
|
||||||
version = "0.8.2" # remember to update html_root_url
|
version = "0.8.4" # remember to update html_root_url
|
||||||
description = "Email client"
|
description = "Email client"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
homepage = "http://lettre.at"
|
homepage = "http://lettre.at"
|
||||||
@@ -20,7 +20,7 @@ is-it-maintained-open-issues = { repository = "lettre/lettre" }
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
log = "^0.4"
|
log = "^0.4"
|
||||||
nom = { version = "^3.2", optional = true }
|
nom = { version = "^4.0", optional = true }
|
||||||
bufstream = { version = "^0.1", optional = true }
|
bufstream = { version = "^0.1", optional = true }
|
||||||
native-tls = { version = "^0.1", optional = true }
|
native-tls = { version = "^0.1", optional = true }
|
||||||
base64 = { version = "^0.9", optional = true }
|
base64 = { version = "^0.9", optional = true }
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
//! emails have to implement `SendableEmail`.
|
//! emails have to implement `SendableEmail`.
|
||||||
//!
|
//!
|
||||||
|
|
||||||
#![doc(html_root_url = "https://docs.rs/lettre/0.8.2")]
|
#![doc(html_root_url = "https://docs.rs/lettre/0.8.4")]
|
||||||
#![deny(missing_docs, missing_debug_implementations, missing_copy_implementations, trivial_casts,
|
#![deny(missing_docs, missing_debug_implementations, missing_copy_implementations, trivial_casts,
|
||||||
trivial_numeric_casts, unsafe_code, unstable_features, unused_import_braces,
|
trivial_numeric_casts, unsafe_code, unstable_features, unused_import_braces,
|
||||||
unused_qualifications)]
|
unused_qualifications)]
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ impl<'a, T: Read + 'a> EmailTransport<'a, T, SendmailResult> for SendmailTranspo
|
|||||||
Some(address) => address.to_string(),
|
Some(address) => address.to_string(),
|
||||||
None => "\"\"".to_string(),
|
None => "\"\"".to_string(),
|
||||||
},
|
},
|
||||||
|
"--",
|
||||||
&to_addresses.join(" "),
|
&to_addresses.join(" "),
|
||||||
])
|
])
|
||||||
.stdin(Stdio::piped())
|
.stdin(Stdio::piped())
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ pub enum Error {
|
|||||||
/// TLS error
|
/// TLS error
|
||||||
Tls(native_tls::Error),
|
Tls(native_tls::Error),
|
||||||
/// Parsing error
|
/// Parsing error
|
||||||
Parsing(nom::simple_errors::Err),
|
Parsing(nom::ErrorKind),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for Error {
|
impl Display for Error {
|
||||||
@@ -77,7 +77,7 @@ impl StdError for Error {
|
|||||||
Utf8Parsing(ref err) => Some(&*err),
|
Utf8Parsing(ref err) => Some(&*err),
|
||||||
Io(ref err) => Some(&*err),
|
Io(ref err) => Some(&*err),
|
||||||
Tls(ref err) => Some(&*err),
|
Tls(ref err) => Some(&*err),
|
||||||
Parsing(ref err) => Some(&*err),
|
Parsing(_) => None,
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -95,8 +95,8 @@ impl From<native_tls::Error> for Error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<nom::simple_errors::Err> for Error {
|
impl From<nom::ErrorKind> for Error {
|
||||||
fn from(err: nom::simple_errors::Err) -> Error {
|
fn from(err: nom::ErrorKind) -> Error {
|
||||||
Parsing(err)
|
Parsing(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
//! SMTP response, containing a mandatory return code and an optional text
|
//! SMTP response, containing a mandatory return code and an optional text
|
||||||
//! message
|
//! message
|
||||||
|
|
||||||
use nom::{crlf, ErrorKind as NomErrorKind, IResult as NomResult};
|
use nom::{crlf, ErrorKind as NomErrorKind};
|
||||||
use nom::simple_errors::Err as NomError;
|
|
||||||
use std::fmt::{Display, Formatter, Result};
|
use std::fmt::{Display, Formatter, Result};
|
||||||
use std::result;
|
use std::result;
|
||||||
use std::str::{FromStr, from_utf8};
|
use std::str::{FromStr, from_utf8};
|
||||||
@@ -126,13 +125,12 @@ pub struct Response {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl FromStr for Response {
|
impl FromStr for Response {
|
||||||
type Err = NomError;
|
type Err = NomErrorKind;
|
||||||
|
|
||||||
fn from_str(s: &str) -> result::Result<Response, NomError> {
|
fn from_str(s: &str) -> result::Result<Response, NomErrorKind> {
|
||||||
match parse_response(s.as_bytes()) {
|
match parse_response(s.as_bytes()) {
|
||||||
NomResult::Done(_, res) => Ok(res),
|
Ok((_, res)) => Ok(res),
|
||||||
NomResult::Error(e) => Err(e),
|
Err(e) => Err(e.into_error_kind()),
|
||||||
NomResult::Incomplete(_) => Err(NomErrorKind::Complete),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -106,6 +106,14 @@ impl IntoEmail for SimpleEmail {
|
|||||||
builder.set_subject(self.subject.unwrap());
|
builder.set_subject(self.subject.unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if self.in_reply_to.is_some() {
|
||||||
|
builder.add_in_reply_to(self.in_reply_to.unwrap().into_mailbox());
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.reference.is_some() {
|
||||||
|
builder.add_reference(self.reference.unwrap().into_mailbox());
|
||||||
|
}
|
||||||
|
|
||||||
// No date for now
|
// No date for now
|
||||||
|
|
||||||
match (self.text, self.html) {
|
match (self.text, self.html) {
|
||||||
@@ -131,6 +139,8 @@ pub struct SimpleEmail {
|
|||||||
cc: Vec<Mailbox>,
|
cc: Vec<Mailbox>,
|
||||||
bcc: Vec<Mailbox>,
|
bcc: Vec<Mailbox>,
|
||||||
reply_to: Option<Mailbox>,
|
reply_to: Option<Mailbox>,
|
||||||
|
in_reply_to: Option<Mailbox>,
|
||||||
|
reference: Option<Mailbox>,
|
||||||
subject: Option<String>,
|
subject: Option<String>,
|
||||||
date: Option<Tm>,
|
date: Option<Tm>,
|
||||||
html: Option<String>,
|
html: Option<String>,
|
||||||
@@ -184,6 +194,18 @@ impl SimpleEmail {
|
|||||||
self.cc.push(address.into_mailbox());
|
self.cc.push(address.into_mailbox());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Adds a `In-Reply-To` to the header
|
||||||
|
pub fn in_reply_to<A: IntoMailbox>(mut self, address: A) -> SimpleEmail {
|
||||||
|
self.in_reply_to = Some(address.into_mailbox());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds a `Reference` to the header
|
||||||
|
pub fn reference<A: IntoMailbox>(mut self, address: A) -> SimpleEmail {
|
||||||
|
self.reference = Some(address.into_mailbox());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Adds a `Bcc` header and stores the recipient address
|
/// Adds a `Bcc` header and stores the recipient address
|
||||||
pub fn bcc<A: IntoMailbox>(mut self, address: A) -> SimpleEmail {
|
pub fn bcc<A: IntoMailbox>(mut self, address: A) -> SimpleEmail {
|
||||||
self.add_bcc(address);
|
self.add_bcc(address);
|
||||||
@@ -296,6 +318,10 @@ pub struct EmailBuilder {
|
|||||||
envelope: Option<Envelope>,
|
envelope: Option<Envelope>,
|
||||||
/// Date issued
|
/// Date issued
|
||||||
date_issued: bool,
|
date_issued: bool,
|
||||||
|
/// Reference Header
|
||||||
|
reference_header: Vec<Address>,
|
||||||
|
/// In-Reply-To Header
|
||||||
|
in_reply_to_header: Vec<Address>
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Simple email representation
|
/// Simple email representation
|
||||||
@@ -392,6 +418,8 @@ impl EmailBuilder {
|
|||||||
sender_header: None,
|
sender_header: None,
|
||||||
envelope: None,
|
envelope: None,
|
||||||
date_issued: false,
|
date_issued: false,
|
||||||
|
reference_header: vec![],
|
||||||
|
in_reply_to_header: vec![]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -471,6 +499,28 @@ impl EmailBuilder {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Adds a `In-Reply-To` header
|
||||||
|
pub fn add_in_reply_to<A: IntoMailbox>(&mut self, address: A) {
|
||||||
|
self.in_reply_to_header.push(Address::Mailbox(address.into_mailbox()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds a `Reference` header
|
||||||
|
pub fn add_reference<A: IntoMailbox>(&mut self, address: A) {
|
||||||
|
self.reference_header.push(Address::Mailbox(address.into_mailbox()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds a `In-Reply-To` header
|
||||||
|
pub fn in_reply_to<A: IntoMailbox>(mut self, address: A) -> EmailBuilder {
|
||||||
|
self.add_in_reply_to(address);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds a `Reference` header
|
||||||
|
pub fn reference<A: IntoMailbox>(mut self, address: A) -> EmailBuilder {
|
||||||
|
self.add_reference(address);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Adds a `Reply-To` header
|
/// Adds a `Reply-To` header
|
||||||
pub fn add_reply_to<A: IntoMailbox>(&mut self, address: A) {
|
pub fn add_reply_to<A: IntoMailbox>(&mut self, address: A) {
|
||||||
let mailbox = address.into_mailbox();
|
let mailbox = address.into_mailbox();
|
||||||
@@ -773,16 +823,24 @@ impl EmailBuilder {
|
|||||||
self.message
|
self.message
|
||||||
.add_header(Header::new_with_value("Cc".into(), self.cc_header).unwrap());
|
.add_header(Header::new_with_value("Cc".into(), self.cc_header).unwrap());
|
||||||
}
|
}
|
||||||
if !self.bcc_header.is_empty() {
|
|
||||||
self.message
|
|
||||||
.add_header(Header::new_with_value("Bcc".into(), self.bcc_header).unwrap());
|
|
||||||
}
|
|
||||||
if !self.reply_to_header.is_empty() {
|
if !self.reply_to_header.is_empty() {
|
||||||
self.message.add_header(
|
self.message.add_header(
|
||||||
Header::new_with_value("Reply-To".into(), self.reply_to_header).unwrap(),
|
Header::new_with_value("Reply-To".into(), self.reply_to_header).unwrap(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !self.reference_header.is_empty() {
|
||||||
|
self.message.add_header(
|
||||||
|
Header::new_with_value("Reference".into(), self.reference_header).unwrap(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if !self.in_reply_to_header.is_empty() {
|
||||||
|
self.message.add_header(
|
||||||
|
Header::new_with_value("In-Reply-To".into(), self.in_reply_to_header).unwrap(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if !self.date_issued {
|
if !self.date_issued {
|
||||||
self.message
|
self.message
|
||||||
.add_header(("Date", Tm::rfc822z(&now()).to_string().as_ref()));
|
.add_header(("Date", Tm::rfc822z(&now()).to_string().as_ref()));
|
||||||
@@ -902,7 +960,7 @@ mod test {
|
|||||||
"Date: {}\r\nSubject: Hello\r\nX-test: value\r\nSender: \
|
"Date: {}\r\nSubject: Hello\r\nX-test: value\r\nSender: \
|
||||||
<sender@localhost>\r\nTo: <user@localhost>\r\nFrom: \
|
<sender@localhost>\r\nTo: <user@localhost>\r\nFrom: \
|
||||||
<user@localhost>\r\nCc: \"Alias\" <cc@localhost>\r\n\
|
<user@localhost>\r\nCc: \"Alias\" <cc@localhost>\r\n\
|
||||||
Bcc: <bcc@localhost>\r\nReply-To: <reply@localhost>\r\n\
|
Reply-To: <reply@localhost>\r\n\
|
||||||
MIME-Version: 1.0\r\nMessage-ID: \
|
MIME-Version: 1.0\r\nMessage-ID: \
|
||||||
<{}.lettre@localhost>\r\n\r\nHello World!\r\n",
|
<{}.lettre@localhost>\r\n\r\nHello World!\r\n",
|
||||||
date_now.rfc822z(),
|
date_now.rfc822z(),
|
||||||
|
|||||||
@@ -83,6 +83,56 @@ fn main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
You can specify custom TLS settings:
|
||||||
|
|
||||||
|
```rust,no_run
|
||||||
|
extern crate native_tls;
|
||||||
|
extern crate lettre;
|
||||||
|
extern crate lettre_email;
|
||||||
|
|
||||||
|
use native_tls::TlsConnector;
|
||||||
|
use native_tls::{Protocol};
|
||||||
|
use lettre::smtp::authentication::{Credentials, Mechanism};
|
||||||
|
use lettre::{EmailTransport, SimpleSendableEmail, ClientTlsParameters, ClientSecurity};
|
||||||
|
use lettre::smtp::ConnectionReuseParameters;
|
||||||
|
use lettre::smtp::{SmtpTransportBuilder};
|
||||||
|
use lettre_email::EmailBuilder;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let email = SimpleSendableEmail::new(
|
||||||
|
"user@localhost".to_string(),
|
||||||
|
&["root@localhost".to_string()],
|
||||||
|
"message_id".to_string(),
|
||||||
|
"Hello world".to_string(),
|
||||||
|
).unwrap();
|
||||||
|
|
||||||
|
let mut tls_builder = TlsConnector::builder().unwrap();
|
||||||
|
tls_builder.supported_protocols(&[Protocol::Tlsv10]).unwrap();
|
||||||
|
let tls_parameters =
|
||||||
|
ClientTlsParameters::new(
|
||||||
|
"smtp.example.com".to_string(),
|
||||||
|
tls_builder.build().unwrap()
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut mailer = SmtpTransportBuilder::new(
|
||||||
|
("smtp.example.com", 465), ClientSecurity::Wrapper(tls_parameters)
|
||||||
|
)
|
||||||
|
.expect("Failed to create transport")
|
||||||
|
.authentication_mechanism(Mechanism::Login)
|
||||||
|
.credentials(Credentials::new(
|
||||||
|
"example_username".to_string(), "example_password".to_string()
|
||||||
|
))
|
||||||
|
.connection_reuse(ConnectionReuseParameters::ReuseUnlimited)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let result = mailer.send(&email);
|
||||||
|
|
||||||
|
assert!(result.is_ok());
|
||||||
|
|
||||||
|
mailer.close();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
#### Lower level
|
#### Lower level
|
||||||
|
|
||||||
You can also send commands, here is a simple email transaction without
|
You can also send commands, here is a simple email transaction without
|
||||||
|
|||||||
Reference in New Issue
Block a user