diff --git a/Cargo.toml b/Cargo.toml index 07ff2fd..3010f0c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "smtp" -version = "0.0.13" +version = "0.0.15" description = "Simple SMTP client" readme = "README.md" documentation = "http://amousset.github.io/rust-smtp/smtp/" diff --git a/src/client/authentication.rs b/src/client/authentication.rs index 4984596..b3c8a5c 100644 --- a/src/client/authentication.rs +++ b/src/client/authentication.rs @@ -16,6 +16,7 @@ use crypto::md5::Md5; use crypto::mac::Mac; use NUL; +use error::Error; /// Returns a PLAIN mecanism response pub fn plain(username: &str, password: &str) -> String { @@ -23,14 +24,16 @@ pub fn plain(username: &str, password: &str) -> String { } /// Returns a CRAM-MD5 mecanism response -pub fn cram_md5(username: &str, password: &str, encoded_challenge: &str) -> String { - // TODO manage errors - let challenge = encoded_challenge.from_base64().unwrap(); +pub fn cram_md5(username: &str, password: &str, encoded_challenge: &str) -> Result { + let challenge = match encoded_challenge.from_base64() { + Ok(challenge) => challenge, + Err(error) => return Err(Error::ChallengeParsingError(error)), + }; let mut hmac = Hmac::new(Md5::new(), password.as_bytes()); hmac.input(&challenge); - format!("{} {}", username, hmac.result().code().to_hex()).as_bytes().to_base64(base64::STANDARD) + Ok(format!("{} {}", username, hmac.result().code().to_hex()).as_bytes().to_base64(base64::STANDARD)) } #[cfg(test)] @@ -45,7 +48,7 @@ mod test { #[test] fn test_cram_md5() { assert_eq!(cram_md5("alice", "wonderland", - "PDE3ODkzLjEzMjA2NzkxMjNAdGVzc2VyYWN0LnN1c2FtLmluPg=="), + "PDE3ODkzLjEzMjA2NzkxMjNAdGVzc2VyYWN0LnN1c2FtLmluPg==").unwrap(), "YWxpY2UgNjRiMmE0M2MxZjZlZDY4MDZhOTgwOTE0ZTIzZTc1ZjA="); } } diff --git a/src/client/mod.rs b/src/client/mod.rs index b9013c5..5dbd5c4 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -16,7 +16,7 @@ use std::io::{BufRead, Read, Write}; use bufstream::BufStream; use response::ResponseParser; -use error::SmtpResult; +use error::{Error, SmtpResult}; use client::net::{Connector, SmtpStream}; use client::authentication::{plain, cram_md5}; use {CRLF, MESSAGE_ENDING}; @@ -175,8 +175,14 @@ impl Client { /// Sends an AUTH command with CRAM-MD5 mecanism pub fn auth_cram_md5(&mut self, username: &str, password: &str) -> SmtpResult { - let encoded_challenge = try!(self.command("AUTH CRAM-MD5")).first_word().expect("No challenge"); - self.command(&format!("AUTH CRAM-MD5 {}", cram_md5(username, password, &encoded_challenge))) + let encoded_challenge = match try!(self.command("AUTH CRAM-MD5")).first_word() { + Some(challenge) => challenge, + None => return Err(Error::ResponseParsingError("Could not read CRAM challenge")), + }; + + let cram_response = try!(cram_md5(username, password, &encoded_challenge)); + + self.command(&format!("AUTH CRAM-MD5 {}", cram_response)) } /// Sends the message content diff --git a/src/email.rs b/src/email.rs index 4a2b4a9..cfbf6f7 100644 --- a/src/email.rs +++ b/src/email.rs @@ -103,11 +103,10 @@ impl EmailBuilder { message_id: current_message, }; - email.message.headers.insert( - Header::new_with_value("Message-ID".to_string(), - format!("<{}@rust-smtp>", current_message) - ).unwrap() - ); + match Header::new_with_value("Message-ID".to_string(), format!("<{}@rust-smtp>", current_message)) { + Ok(header) => email.message.headers.insert(header), + Err(_) => (), + } EmailBuilder { content: email, diff --git a/src/error.rs b/src/error.rs index e7f2fa6..a7ad0fa 100644 --- a/src/error.rs +++ b/src/error.rs @@ -15,6 +15,7 @@ use std::fmt::{Display, Formatter}; use std::fmt; use response::{Severity, Response}; +use serialize::base64::FromBase64Error; use self::Error::*; /// An enum of all error kinds. @@ -30,6 +31,8 @@ pub enum Error { PermanentError(Response), /// Error parsing a response ResponseParsingError(&'static str), + /// Error parsing a base64 string in response + ChallengeParsingError(FromBase64Error), /// Internal client error ClientError(&'static str), /// DNS resolution error @@ -50,6 +53,7 @@ impl StdError for Error { TransientError(_) => "a transient error occured during the SMTP transaction", PermanentError(_) => "a permanent error occured during the SMTP transaction", ResponseParsingError(_) => "an error occured while parsing an SMTP response", + ChallengeParsingError(_) => "an error occured while parsing a CRAM-MD5 challenge", ResolutionError => "Could no resolve hostname", ClientError(_) => "an unknown error occured", IoError(_) => "an I/O error occured",