Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
813f09a314 | ||
|
|
6a6023431b | ||
|
|
7f6aa0ffae | ||
|
|
1830f084c0 | ||
|
|
49a995f68d | ||
|
|
9c34b5a055 |
@@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
|
|
||||||
name = "smtp"
|
name = "smtp"
|
||||||
version = "0.1.1"
|
version = "0.1.2"
|
||||||
description = "Simple SMTP client"
|
description = "Simple SMTP client"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
documentation = "http://amousset.me/rust-smtp/smtp/"
|
documentation = "http://amousset.me/rust-smtp/smtp/"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
rust-smtp [](https://travis-ci.org/amousset/rust-smtp) [](https://coveralls.io/github/amousset/rust-smtp?branch=master) [](https://crates.io/crates/smtp)[](./LICENSE)
|
rust-smtp [](https://travis-ci.org/amousset/rust-smtp) [](https://coveralls.io/github/amousset/rust-smtp?branch=master) [](https://crates.io/crates/smtp) [](./LICENSE)
|
||||||
=========
|
=========
|
||||||
|
|
||||||
This library implements a simple SMTP client.
|
This library implements a simple SMTP client.
|
||||||
|
|||||||
@@ -81,6 +81,7 @@ mod test {
|
|||||||
let mecanism = Mecanism::Plain;
|
let mecanism = Mecanism::Plain;
|
||||||
|
|
||||||
assert_eq!(mecanism.response("username", "password", None).unwrap(), "AHVzZXJuYW1lAHBhc3N3b3Jk");
|
assert_eq!(mecanism.response("username", "password", None).unwrap(), "AHVzZXJuYW1lAHBhc3N3b3Jk");
|
||||||
|
assert!(mecanism.response("username", "password", Some("test")).is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -90,5 +91,7 @@ mod test {
|
|||||||
assert_eq!(mecanism.response("alice", "wonderland",
|
assert_eq!(mecanism.response("alice", "wonderland",
|
||||||
Some("PDE3ODkzLjEzMjA2NzkxMjNAdGVzc2VyYWN0LnN1c2FtLmluPg==")).unwrap(),
|
Some("PDE3ODkzLjEzMjA2NzkxMjNAdGVzc2VyYWN0LnN1c2FtLmluPg==")).unwrap(),
|
||||||
"YWxpY2UgNjRiMmE0M2MxZjZlZDY4MDZhOTgwOTE0ZTIzZTc1ZjA=");
|
"YWxpY2UgNjRiMmE0M2MxZjZlZDY4MDZhOTgwOTE0ZTIzZTc1ZjA=");
|
||||||
|
assert!(mecanism.response("alice", "wonderland", Some("tést")).is_err());
|
||||||
|
assert!(mecanism.response("alice", "wonderland", None).is_err());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,6 +56,7 @@
|
|||||||
//! ```rust,no_run
|
//! ```rust,no_run
|
||||||
//! use smtp::sender::{Sender, SenderBuilder};
|
//! use smtp::sender::{Sender, SenderBuilder};
|
||||||
//! use smtp::email::EmailBuilder;
|
//! use smtp::email::EmailBuilder;
|
||||||
|
//! use smtp::authentication::Mecanism;
|
||||||
//!
|
//!
|
||||||
//! let mut builder = EmailBuilder::new();
|
//! let mut builder = EmailBuilder::new();
|
||||||
//! builder = builder.to(("user@example.org", "Alias name"));
|
//! builder = builder.to(("user@example.org", "Alias name"));
|
||||||
@@ -76,6 +77,8 @@
|
|||||||
//! .hello_name("my.hostname.tld")
|
//! .hello_name("my.hostname.tld")
|
||||||
//! // Add credentials for authentication
|
//! // Add credentials for authentication
|
||||||
//! .credentials("username", "password")
|
//! .credentials("username", "password")
|
||||||
|
//! // Configure accepted authetication mecanisms
|
||||||
|
//! .authentication_mecanisms(vec![Mecanism::CramMd5])
|
||||||
//! // Enable connection reuse
|
//! // Enable connection reuse
|
||||||
//! .enable_connection_reuse(true).build();
|
//! .enable_connection_reuse(true).build();
|
||||||
//!
|
//!
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::fmt::{Display, Formatter, Result};
|
use std::fmt::{Display, Formatter, Result};
|
||||||
use std::result::Result as RResult;
|
use std::result;
|
||||||
|
|
||||||
use self::Severity::*;
|
use self::Severity::*;
|
||||||
use self::Category::*;
|
use self::Category::*;
|
||||||
@@ -23,7 +23,7 @@ pub enum Severity {
|
|||||||
|
|
||||||
impl FromStr for Severity {
|
impl FromStr for Severity {
|
||||||
type Err = Error;
|
type Err = Error;
|
||||||
fn from_str(s: &str) -> RResult<Severity, Error> {
|
fn from_str(s: &str) -> result::Result<Severity, Error> {
|
||||||
match s {
|
match s {
|
||||||
"2" => Ok(PositiveCompletion),
|
"2" => Ok(PositiveCompletion),
|
||||||
"3" => Ok(PositiveIntermediate),
|
"3" => Ok(PositiveIntermediate),
|
||||||
@@ -66,7 +66,7 @@ pub enum Category {
|
|||||||
|
|
||||||
impl FromStr for Category {
|
impl FromStr for Category {
|
||||||
type Err = Error;
|
type Err = Error;
|
||||||
fn from_str(s: &str) -> RResult<Category, Error> {
|
fn from_str(s: &str) -> result::Result<Category, Error> {
|
||||||
match s {
|
match s {
|
||||||
"0" => Ok(Syntax),
|
"0" => Ok(Syntax),
|
||||||
"1" => Ok(Information),
|
"1" => Ok(Information),
|
||||||
@@ -109,7 +109,7 @@ impl FromStr for Code {
|
|||||||
type Err = Error;
|
type Err = Error;
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from_str(s: &str) -> RResult<Code, Error> {
|
fn from_str(s: &str) -> result::Result<Code, Error> {
|
||||||
if s.len() == 3 {
|
if s.len() == 3 {
|
||||||
match (s[0..1].parse::<Severity>(), s[1..2].parse::<Category>(), s[2..3].parse::<u8>()) {
|
match (s[0..1].parse::<Severity>(), s[1..2].parse::<Category>(), s[2..3].parse::<u8>()) {
|
||||||
(Ok(severity), Ok(category), Ok(detail)) => Ok(Code {severity: severity, category: category, detail: detail}),
|
(Ok(severity), Ok(category), Ok(detail)) => Ok(Code {severity: severity, category: category, detail: detail}),
|
||||||
@@ -157,7 +157,7 @@ impl ResponseParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Parses a line and return a `bool` indicating if there are more lines to come
|
/// Parses a line and return a `bool` indicating if there are more lines to come
|
||||||
pub fn read_line(&mut self, line: &str) -> RResult<bool, Error> {
|
pub fn read_line(&mut self, line: &str) -> result::Result<bool, Error> {
|
||||||
|
|
||||||
if line.len() < 3 {
|
if line.len() < 3 {
|
||||||
return Err(Error::ResponseParsingError("Wrong code length (should be 3 digit)"));
|
return Err(Error::ResponseParsingError("Wrong code length (should be 3 digit)"));
|
||||||
|
|||||||
@@ -25,6 +25,8 @@ pub struct SenderBuilder {
|
|||||||
credentials: Option<(String, String)>,
|
credentials: Option<(String, String)>,
|
||||||
/// Socket we are connecting to
|
/// Socket we are connecting to
|
||||||
server_addr: SocketAddr,
|
server_addr: SocketAddr,
|
||||||
|
/// List of authentication mecanism, sorted by priority
|
||||||
|
authentication_mecanisms: Vec<Mecanism>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Builder for the SMTP Sender
|
/// Builder for the SMTP Sender
|
||||||
@@ -41,6 +43,7 @@ impl SenderBuilder {
|
|||||||
connection_reuse_count_limit: 100,
|
connection_reuse_count_limit: 100,
|
||||||
enable_connection_reuse: false,
|
enable_connection_reuse: false,
|
||||||
hello_name: "localhost".to_string(),
|
hello_name: "localhost".to_string(),
|
||||||
|
authentication_mecanisms: vec![Mecanism::CramMd5, Mecanism::Plain],
|
||||||
}),
|
}),
|
||||||
None => Err(From::from("Could nor resolve hostname")),
|
None => Err(From::from("Could nor resolve hostname")),
|
||||||
}
|
}
|
||||||
@@ -74,6 +77,12 @@ impl SenderBuilder {
|
|||||||
self.credentials = Some((username.to_string(), password.to_string()));
|
self.credentials = Some((username.to_string(), password.to_string()));
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set the authentication mecanisms
|
||||||
|
pub fn authentication_mecanisms(mut self, mecanisms: Vec<Mecanism>) -> SenderBuilder {
|
||||||
|
self.authentication_mecanisms = mecanisms;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Build the SMTP client
|
/// Build the SMTP client
|
||||||
///
|
///
|
||||||
@@ -191,20 +200,22 @@ impl Sender {
|
|||||||
debug!("server {}", self.server_info.as_ref().unwrap());
|
debug!("server {}", self.server_info.as_ref().unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Use PLAIN AUTH in encrypted connections, CRAM-MD5 otherwise
|
|
||||||
if self.client_info.credentials.is_some() && self.state.connection_reuse_count == 0 {
|
if self.client_info.credentials.is_some() && self.state.connection_reuse_count == 0 {
|
||||||
|
|
||||||
let (username, password) = self.client_info.credentials.clone().unwrap();
|
let (username, password) = self.client_info.credentials.clone().unwrap();
|
||||||
|
|
||||||
if self.server_info.as_ref().unwrap().supports_auth_mecanism(Mecanism::CramMd5) {
|
let mut found = false;
|
||||||
let result = self.client.auth(Mecanism::CramMd5, &username, &password);
|
|
||||||
try_smtp!(result, self);
|
for mecanism in self.client_info.authentication_mecanisms.clone() {
|
||||||
} else if self.server_info.as_ref().unwrap().supports_auth_mecanism(Mecanism::Plain) {
|
if self.server_info.as_ref().unwrap().supports_auth_mecanism(mecanism) {
|
||||||
let result = self.client.auth(Mecanism::Plain, &username, &password);
|
found = true;
|
||||||
try_smtp!(result, self);
|
let result = self.client.auth(mecanism, &username, &password);
|
||||||
} else {
|
try_smtp!(result, self);
|
||||||
debug!("No supported authentication mecanisms available");
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
debug!("No supported authentication mecanisms available");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let current_message = try!(email.message_id().ok_or("Missing Message-ID"));
|
let current_message = try!(email.message_id().ok_or("Missing Message-ID"));
|
||||||
|
|||||||
Reference in New Issue
Block a user