From f6968c95ec7224598373dcf152780e1e6c20dd76 Mon Sep 17 00:00:00 2001 From: Jos van den Oever Date: Mon, 6 Mar 2017 21:59:50 +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 | 46 +++++++++++++++++++++++++++++++++++----- src/error.rs | 16 +++++++++++++- src/lib.rs | 2 ++ 6 files changed, 61 insertions(+), 8 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 0a639350..d326fee3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,7 @@ path = "src/lib.rs" [dependencies] openssl = "0.9" regex = "0.2" +lazy_static = "0.2" [dev-dependencies] base64 = "0.2" diff --git a/examples/basic.rs b/examples/basic.rs index f38dc12c..c5c02f23 100644 --- a/examples/basic.rs +++ b/examples/basic.rs @@ -31,7 +31,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 30d2180f..fa4fb4a5 100644 --- a/examples/gmail_oauth2.rs +++ b/examples/gmail_oauth2.rs @@ -36,7 +36,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 728f7738..7dcf12ff 100644 --- a/src/client.rs +++ b/src/client.rs @@ -2,11 +2,12 @@ use std::net::{TcpStream, ToSocketAddrs}; use openssl::ssl::{SslConnector, SslStream}; use std::io::{self, Read, Write}; use std::time::Duration; +use regex::Regex; use super::mailbox::Mailbox; 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}; +use super::error::{Error, Result, ParseError}; static TAG_PREFIX: &'static str = "a"; const INITIAL_TAG: u32 = 0; @@ -270,13 +271,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).to_string()) + 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).to_string()) + 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 02df5358..da884167 100644 --- a/src/error.rs +++ b/src/error.rs @@ -3,6 +3,7 @@ use std::result; use std::fmt; use std::error::Error as StdError; use std::net::TcpStream; +use std::string::FromUtf8Error; use openssl::ssl::HandshakeError as SslError; @@ -37,6 +38,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 { @@ -70,6 +77,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. @@ -89,6 +100,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" @@ -96,7 +109,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 18ef6678..c379b672 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,6 +5,8 @@ extern crate openssl; extern crate regex; +#[macro_use] +extern crate lazy_static; pub mod authenticator; pub mod client;