diff --git a/src/authenticator.rs b/src/authenticator.rs new file mode 100644 index 0000000..16004a1 --- /dev/null +++ b/src/authenticator.rs @@ -0,0 +1,3 @@ +pub trait Authenticator { + fn process(&self, String) -> String; +} diff --git a/src/client.rs b/src/client.rs index 6eaa98b..0f5e98d 100644 --- a/src/client.rs +++ b/src/client.rs @@ -3,7 +3,8 @@ use openssl::ssl::{SslContext, SslStream}; use std::io::{self, Read, Write}; use super::mailbox::Mailbox; -use super::parse::{parse_response_ok, parse_capability, parse_select_or_examine, parse_response}; +use super::authenticator::Authenticator; +use super::parse::{parse_response_ok, parse_capability, parse_select_or_examine, parse_response, parse_authenticate_response}; use super::error::{Error, Result}; static TAG_PREFIX: &'static str = "a"; @@ -59,6 +60,32 @@ impl Client { } } + pub fn authenticate(&mut self, auth_type: &str, authenticator: A) -> Result<()> { + match self.run_command(&format!("AUTHENTICATE {}\r\n", auth_type).to_string()) { + Ok(lines) => { + // TODO test this for the many authentication use cases + let data = match parse_authenticate_response(lines) { + Ok(d) => d, + Err(e) => return Err(e) + }; + let auth_response = authenticator.process(data); + match self.stream.write_all(auth_response.into_bytes().as_slice()) { + Err(e) => return Err(Error::Io(e)), + _ => {} + }; + match self.stream.write(vec![0x0d, 0x0a].as_slice()) { + Err(e) => return Err(Error::Io(e)), + _ => {} + }; + match self.read_response() { + Ok(_) => Ok(()), + Err(e) => return Err(e) + } + }, + Err(e) => Err(e) + } + } + /// Log in to the IMAP server. pub fn login(&mut self, username: & str, password: & str) -> Result<()> { self.run_command_and_check_ok(&format!("LOGIN {} {}", username, password).to_string()) diff --git a/src/error.rs b/src/error.rs index 4d2cd89..40a75a4 100644 --- a/src/error.rs +++ b/src/error.rs @@ -69,7 +69,9 @@ pub enum ParseError { // Indicates an error parsing the status response. Such as OK, NO, and BAD. StatusResponse(Vec), // Error parsing the cabability response. - Capability(Vec) + Capability(Vec), + // Authentication errors. + Authentication(Vec) } impl fmt::Display for ParseError { @@ -84,7 +86,8 @@ impl StdError for ParseError { fn description(&self) -> &str { match *self { ParseError::StatusResponse(_) => "Unable to parse status response", - ParseError::Capability(_) => "Unable to parse capability response" + ParseError::Capability(_) => "Unable to parse capability response", + ParseError::Authentication(_) => "Unable to parse authentication response" } } diff --git a/src/lib.rs b/src/lib.rs index affa530..18ef667 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,6 +6,7 @@ extern crate openssl; extern crate regex; +pub mod authenticator; pub mod client; pub mod error; pub mod mailbox; diff --git a/src/parse.rs b/src/parse.rs index 0fa7651..38e2d34 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -3,6 +3,21 @@ use regex::Regex; use super::mailbox::Mailbox; use super::error::{Error, ParseError, Result}; +pub fn parse_authenticate_response(lines: Vec) -> Result { + let authenticate_regex = Regex::new("^+ (.*)\r\n").unwrap(); + let last_line = match lines.last() { + Some(l) => l, + None => return Err(Error::Parse(ParseError::Authentication(lines.clone()))) + }; + + for cap in authenticate_regex.captures_iter(last_line) { + let data = cap.at(1).unwrap_or(""); + return Ok(String::from(data)); + } + + Err(Error::Parse(ParseError::Authentication(lines.clone()))) +} + pub fn parse_capability(lines: Vec) -> Result> { let capability_regex = Regex::new(r"^\* CAPABILITY (.*)\r\n").unwrap();