From 4ca1d15fe06da7035d2aa03ff20d24debdda8f63 Mon Sep 17 00:00:00 2001 From: Jos van den Oever Date: Tue, 31 Oct 2017 20:11:07 +0100 Subject: [PATCH] Read bytes when fetching or uid_fetching. --- Cargo.toml | 1 + examples/basic.rs | 2 +- examples/gmail_oauth2.rs | 2 +- src/client.rs | 44 ++++++++++++++++++++++++++++++++++++---- src/error.rs | 15 +++++++++++++- src/lib.rs | 2 ++ 6 files changed, 59 insertions(+), 7 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6af16483..782fb3dd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,6 +28,7 @@ path = "src/lib.rs" native-tls = "0.1" regex = "0.2" bufstream = "0.1" +lazy_static = "0.2" [dev-dependencies] base64 = "0.7" diff --git a/examples/basic.rs b/examples/basic.rs index 50567e64..5dbde4da 100644 --- a/examples/basic.rs +++ b/examples/basic.rs @@ -32,7 +32,7 @@ fn main() { match imap_socket.fetch("2", "body[text]") { Ok(lines) => for line in lines.iter() { - print!("{}", line); + print!("{}", String::from_utf8(line.1.clone()).unwrap()); }, Err(e) => println!("Error Fetching email 2: {}", e), }; diff --git a/examples/gmail_oauth2.rs b/examples/gmail_oauth2.rs index e2cba0bb..f03f6f5e 100644 --- a/examples/gmail_oauth2.rs +++ b/examples/gmail_oauth2.rs @@ -45,7 +45,7 @@ fn main() { match imap_socket.fetch("2", "body[text]") { Ok(lines) => for line in lines.iter() { - print!("{}", line); + print!("{}", String::from_utf8(line.1.clone()).unwrap()); }, Err(e) => println!("Error Fetching email 2: {}", e), }; diff --git a/src/client.rs b/src/client.rs index 1fcc384e..894c90da 100644 --- a/src/client.rs +++ b/src/client.rs @@ -3,6 +3,7 @@ use native_tls::{TlsConnector, TlsStream}; use std::io::{self, Read, Write}; use std::time::Duration; use bufstream::BufStream; +use regex::Regex; use super::mailbox::Mailbox; use super::authenticator::Authenticator; @@ -293,13 +294,48 @@ impl Client { parse_select_or_examine(lines) } + fn fetch_result(&mut self, first_line: String) -> Result<(u32, Vec)> { + lazy_static! { + static ref START_RE: Regex = Regex::new("^\\* \\d+ FETCH \\D*(\\d+).*\\{(\\d+)\\}\r\n$").unwrap(); + } + let (id, size) = if let Some(captures) = START_RE.captures(&first_line.clone()) { + (captures.get(1).unwrap().as_str().parse::().unwrap(), + captures.get(2).unwrap().as_str().parse::().unwrap()) + } else { + return Err(Error::Parse(ParseError::FetchResponse(first_line))); + }; + let mut data = Vec::new(); + data.resize(size, 0); + try!(self.stream.read_exact(&mut data)); + try!(self.readline()); // should be ")\r\n" + Ok((id, data)) + } + + fn fetch_common(&mut self, untagged_command: &str) -> Result)>> { + try!(self.run_command(untagged_command)); + let mut found_tag_line = false; + let start_str = format!("{}{} ", TAG_PREFIX, self.tag); + let mut results = Vec::new(); + + while !found_tag_line { + let raw_data = try!(self.readline()); + let line = String::from_utf8(raw_data).unwrap(); + if (&*line).starts_with(&*start_str) { + found_tag_line = true; + } else { + results.push(try!(self.fetch_result(line))); + } + } + Ok(results) + } + /// Fetch retreives data associated with a message in the mailbox. - pub fn fetch(&mut self, sequence_set: &str, query: &str) -> Result> { - self.run_command_and_read_response(&format!("FETCH {} {}", sequence_set, query)) + pub fn fetch(&mut self, sequence_set: &str, query: &str) -> Result)>> { + self.fetch_common(&format!("FETCH {} {}", sequence_set, query).to_string()) } - pub fn uid_fetch(&mut self, uid_set: &str, query: &str) -> Result> { - self.run_command_and_read_response(&format!("UID FETCH {} {}", uid_set, query)) + pub fn uid_fetch(&mut self, uid_set: &str, query: &str) -> Result)>> { + self.fetch_common(&format!("UID FETCH {} {}", uid_set, query).to_string()) } /// Noop always succeeds, and it does nothing. diff --git a/src/error.rs b/src/error.rs index cc029a4f..664a1893 100644 --- a/src/error.rs +++ b/src/error.rs @@ -56,6 +56,12 @@ impl From for Error { } } +impl From for Error { + fn from(err: FromUtf8Error) -> Error { + Error::Parse(ParseError::FromUtf8(err)) + } +} + impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { @@ -94,6 +100,10 @@ impl StdError for Error { #[derive(Debug)] pub enum ParseError { + // Error in the decoding of data. + FromUtf8(FromUtf8Error), + // Indicates an error parsing the fetch or uid fetch response. + FetchResponse(String), // Indicates an error parsing the status response. Such as OK, NO, and BAD. StatusResponse(Vec), // Error parsing the cabability response. @@ -114,6 +124,8 @@ impl fmt::Display for ParseError { impl StdError for ParseError { fn description(&self) -> &str { match *self { + ParseError::FromUtf8(_) => "Unable to decode the response as UTF-8.", + ParseError::FetchResponse(_) => "Unable to parse fetch response.", ParseError::StatusResponse(_) => "Unable to parse status response", ParseError::Capability(_) => "Unable to parse capability response", ParseError::Authentication(_) => "Unable to parse authentication response", @@ -122,7 +134,8 @@ impl StdError for ParseError { } fn cause(&self) -> Option<&StdError> { - match *self { + match self { + &ParseError::FromUtf8(ref e) => Some(e), _ => None, } } diff --git a/src/lib.rs b/src/lib.rs index 9901de5e..7c5a5f07 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,6 +6,8 @@ extern crate bufstream; extern crate native_tls; extern crate regex; +#[macro_use] +extern crate lazy_static; pub mod authenticator; pub mod client;