This commit is contained in:
Jon Gjengset 2018-10-31 21:15:13 -04:00
parent 510dab9604
commit e68d61a1e2
No known key found for this signature in database
GPG key ID: D64AC9D67176DC71
2 changed files with 53 additions and 34 deletions

View file

@ -1,16 +1,17 @@
use bufstream::BufStream; use bufstream::BufStream;
use native_tls::{TlsConnector, TlsStream}; use native_tls::{TlsConnector, TlsStream};
use nom::IResult; use nom::IResult;
use std::collections::HashSet;
use std::io::{self, Read, Write}; use std::io::{self, Read, Write};
use std::net::{TcpStream, ToSocketAddrs}; use std::net::{TcpStream, ToSocketAddrs};
use std::ops::{Deref, DerefMut};
use std::time::Duration; use std::time::Duration;
use std::ops::{Deref,DerefMut};
use std::collections::HashSet;
use super::authenticator::Authenticator; use super::authenticator::Authenticator;
use super::error::{Error, ParseError, Result, ValidateError}; use super::error::{Error, ParseError, Result, ValidateError};
use super::parse::{ use super::parse::{
parse_authenticate_response, parse_capabilities, parse_fetches, parse_mailbox, parse_names, parse_ids, parse_authenticate_response, parse_capabilities, parse_fetches, parse_ids, parse_mailbox,
parse_names,
}; };
use super::types::*; use super::types::*;
@ -96,7 +97,6 @@ impl<T: Read + Write> DerefMut for Session<T> {
} }
} }
/// `IdleHandle` allows a client to block waiting for changes to the remote mailbox. /// `IdleHandle` allows a client to block waiting for changes to the remote mailbox.
/// ///
/// The handle blocks using the IMAP IDLE command specificed in [RFC /// The handle blocks using the IMAP IDLE command specificed in [RFC
@ -246,9 +246,7 @@ impl<'a> SetReadTimeout for TcpStream {
impl<'a> SetReadTimeout for TlsStream<TcpStream> { impl<'a> SetReadTimeout for TlsStream<TcpStream> {
fn set_read_timeout(&mut self, timeout: Option<Duration>) -> Result<()> { fn set_read_timeout(&mut self, timeout: Option<Duration>) -> Result<()> {
self.get_ref() self.get_ref().set_read_timeout(timeout).map_err(Error::Io)
.set_read_timeout(timeout)
.map_err(Error::Io)
} }
} }
@ -316,7 +314,6 @@ pub fn secure_connect<A: ToSocketAddrs>(
} }
} }
impl Client<TcpStream> { impl Client<TcpStream> {
/// This will upgrade a regular TCP connection to use SSL. /// This will upgrade a regular TCP connection to use SSL.
/// ///
@ -345,9 +342,9 @@ macro_rules! ok_or_unauth_client_err {
($r:expr, $self:expr) => { ($r:expr, $self:expr) => {
match $r { match $r {
Ok(o) => o, Ok(o) => o,
Err(e) => return Err((e, $self)) Err(e) => return Err((e, $self)),
}
} }
};
} }
impl<T: Read + Write> Client<T> { impl<T: Read + Write> Client<T> {
@ -363,19 +360,22 @@ impl<T: Read + Write> Client<T> {
} }
/// Authenticate will authenticate with the server, using the authenticator given. /// Authenticate will authenticate with the server, using the authenticator given.
pub fn authenticate<A: Authenticator> ( pub fn authenticate<A: Authenticator>(
mut self, mut self,
auth_type: &str, auth_type: &str,
authenticator: A, authenticator: A,
) -> ::std::result::Result<Session<T>, (Error, Client<T>)> { ) -> ::std::result::Result<Session<T>, (Error, Client<T>)> {
ok_or_unauth_client_err!(self.run_command(&format!("AUTHENTICATE {}", auth_type)), self); ok_or_unauth_client_err!(
self.run_command(&format!("AUTHENTICATE {}", auth_type)),
self
);
self.do_auth_handshake(&authenticator) self.do_auth_handshake(&authenticator)
} }
/// This func does the handshake process once the authenticate command is made. /// This func does the handshake process once the authenticate command is made.
fn do_auth_handshake<A: Authenticator>( fn do_auth_handshake<A: Authenticator>(
mut self, mut self,
authenticator: &A authenticator: &A,
) -> ::std::result::Result<Session<T>, (Error, Client<T>)> { ) -> ::std::result::Result<Session<T>, (Error, Client<T>)> {
// TODO Clean up this code // TODO Clean up this code
loop { loop {
@ -386,10 +386,15 @@ impl<T: Read + Write> Client<T> {
if line.starts_with(b"+") { if line.starts_with(b"+") {
let data = ok_or_unauth_client_err!( let data = ok_or_unauth_client_err!(
parse_authenticate_response(String::from_utf8(line).unwrap()), self); parse_authenticate_response(String::from_utf8(line).unwrap()),
self
);
let auth_response = authenticator.process(data); let auth_response = authenticator.process(data);
ok_or_unauth_client_err!(self.write_line(auth_response.into_bytes().as_slice()), self); ok_or_unauth_client_err!(
self.write_line(auth_response.into_bytes().as_slice()),
self
);
} else { } else {
ok_or_unauth_client_err!(self.read_response_onto(&mut line), self); ok_or_unauth_client_err!(self.read_response_onto(&mut line), self);
return Ok(Session { conn: self.conn }); return Ok(Session { conn: self.conn });
@ -430,18 +435,20 @@ impl<T: Read + Write> Client<T> {
pub fn login( pub fn login(
mut self, mut self,
username: &str, username: &str,
password: &str password: &str,
) -> ::std::result::Result<Session<T>, (Error, Client<T>)> { ) -> ::std::result::Result<Session<T>, (Error, Client<T>)> {
let u = ok_or_unauth_client_err!(validate_str(username), self); let u = ok_or_unauth_client_err!(validate_str(username), self);
let p = ok_or_unauth_client_err!(validate_str(password), self); let p = ok_or_unauth_client_err!(validate_str(password), self);
ok_or_unauth_client_err!(self.run_command_and_check_ok(&format!("LOGIN {} {}", u, p)), self); ok_or_unauth_client_err!(
self.run_command_and_check_ok(&format!("LOGIN {} {}", u, p)),
self
);
Ok(Session { conn: self.conn }) Ok(Session { conn: self.conn })
} }
} }
impl<T: Read + Write> Session<T> {
impl <T: Read + Write> Session<T> {
/// Selects a mailbox /// Selects a mailbox
/// ///
/// Note that the server *is* allowed to unilaterally send things to the client for messages in /// Note that the server *is* allowed to unilaterally send things to the client for messages in
@ -576,7 +583,11 @@ impl <T: Read + Write> Session<T> {
/// The MOVE command is defined in [RFC 6851 - "Internet Message Access Protocol (IMAP) /// The MOVE command is defined in [RFC 6851 - "Internet Message Access Protocol (IMAP)
/// - MOVE Extension"](https://tools.ietf.org/html/rfc6851#section-3). /// - MOVE Extension"](https://tools.ietf.org/html/rfc6851#section-3).
pub fn mv(&mut self, sequence_set: &str, mailbox_name: &str) -> Result<()> { pub fn mv(&mut self, sequence_set: &str, mailbox_name: &str) -> Result<()> {
self.run_command_and_check_ok(&format!("MOVE {} {}", sequence_set, validate_str(mailbox_name)?)) self.run_command_and_check_ok(&format!(
"MOVE {} {}",
sequence_set,
validate_str(mailbox_name)?
))
} }
/// Moves each message in the uid set into the destination mailbox. /// Moves each message in the uid set into the destination mailbox.
@ -601,7 +612,8 @@ impl <T: Read + Write> Session<T> {
"LIST {} {}", "LIST {} {}",
quote!(reference_name), quote!(reference_name),
mailbox_search_pattern mailbox_search_pattern
)).and_then(parse_names) ))
.and_then(parse_names)
} }
/// The LSUB command returns a subset of names from the set of names /// The LSUB command returns a subset of names from the set of names
@ -615,7 +627,8 @@ impl <T: Read + Write> Session<T> {
"LSUB {} {}", "LSUB {} {}",
quote!(reference_name), quote!(reference_name),
mailbox_search_pattern mailbox_search_pattern
)).and_then(parse_names) ))
.and_then(parse_names)
} }
/// The STATUS command requests the status of the indicated mailbox. /// The STATUS command requests the status of the indicated mailbox.
@ -624,7 +637,8 @@ impl <T: Read + Write> Session<T> {
"STATUS {} {}", "STATUS {} {}",
validate_str(mailbox_name)?, validate_str(mailbox_name)?,
status_data_items status_data_items
)).and_then(|lines| parse_mailbox(&lines[..])) ))
.and_then(|lines| parse_mailbox(&lines[..]))
} }
/// Returns a handle that can be used to block until the state of the currently selected /// Returns a handle that can be used to block until the state of the currently selected
@ -683,7 +697,7 @@ impl <T: Read + Write> Session<T> {
} }
} }
impl <T: Read + Write> Connection<T> { impl<T: Read + Write> Connection<T> {
fn read_greeting(&mut self) -> Result<()> { fn read_greeting(&mut self) -> Result<()> {
let mut v = Vec::new(); let mut v = Vec::new();
self.readline(&mut v)?; self.readline(&mut v)?;
@ -815,7 +829,6 @@ impl <T: Read + Write> Connection<T> {
} }
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::super::error::Result; use super::super::error::Result;
@ -824,8 +837,10 @@ mod tests {
macro_rules! mock_session { macro_rules! mock_session {
($s:expr) => { ($s:expr) => {
Session { conn: Client::new($s).conn } Session {
conn: Client::new($s).conn,
} }
};
} }
#[test] #[test]
@ -1006,7 +1021,8 @@ mod tests {
let response = b"* 2 EXPUNGE\r\n\ let response = b"* 2 EXPUNGE\r\n\
* 3 EXPUNGE\r\n\ * 3 EXPUNGE\r\n\
* 4 EXPUNGE\r\n\ * 4 EXPUNGE\r\n\
a1 OK UID EXPUNGE completed\r\n".to_vec(); a1 OK UID EXPUNGE completed\r\n"
.to_vec();
let mock_stream = MockStream::new(response); let mock_stream = MockStream::new(response);
let mut session = mock_session!(mock_stream); let mut session = mock_session!(mock_stream);
session.uid_expunge("2:4").unwrap(); session.uid_expunge("2:4").unwrap();
@ -1266,7 +1282,8 @@ mod tests {
let response = b"* OK [COPYUID 1511554416 142,399 41:42] Moved UIDs.\r\n\ let response = b"* OK [COPYUID 1511554416 142,399 41:42] Moved UIDs.\r\n\
* 2 EXPUNGE\r\n\ * 2 EXPUNGE\r\n\
* 1 EXPUNGE\r\n\ * 1 EXPUNGE\r\n\
a1 OK Move completed\r\n".to_vec(); a1 OK Move completed\r\n"
.to_vec();
let mailbox_name = "MEETING"; let mailbox_name = "MEETING";
let command = format!("a1 MOVE 1:2 {}\r\n", quote!(mailbox_name)); let command = format!("a1 MOVE 1:2 {}\r\n", quote!(mailbox_name));
let mock_stream = MockStream::new(response); let mock_stream = MockStream::new(response);
@ -1283,7 +1300,8 @@ mod tests {
let response = b"* OK [COPYUID 1511554416 142,399 41:42] Moved UIDs.\r\n\ let response = b"* OK [COPYUID 1511554416 142,399 41:42] Moved UIDs.\r\n\
* 2 EXPUNGE\r\n\ * 2 EXPUNGE\r\n\
* 1 EXPUNGE\r\n\ * 1 EXPUNGE\r\n\
a1 OK Move completed\r\n".to_vec(); a1 OK Move completed\r\n"
.to_vec();
let mailbox_name = "MEETING"; let mailbox_name = "MEETING";
let command = format!("a1 UID MOVE 41:42 {}\r\n", quote!(mailbox_name)); let command = format!("a1 UID MOVE 41:42 {}\r\n", quote!(mailbox_name));
let mock_stream = MockStream::new(response); let mock_stream = MockStream::new(response);

View file

@ -112,9 +112,7 @@ pub fn parse_fetches(lines: Vec<u8>) -> ZeroCopyResult<Vec<Fetch>> {
AttributeValue::Uid(uid) => fetch.uid = Some(uid), AttributeValue::Uid(uid) => fetch.uid = Some(uid),
AttributeValue::Rfc822(rfc) => fetch.rfc822 = rfc, AttributeValue::Rfc822(rfc) => fetch.rfc822 = rfc,
AttributeValue::Rfc822Header(rfc) => fetch.rfc822_header = rfc, AttributeValue::Rfc822Header(rfc) => fetch.rfc822_header = rfc,
AttributeValue::BodySection { AttributeValue::BodySection { data, .. } => fetch.body = data,
data, ..
} => fetch.body = data,
_ => {} _ => {}
} }
} }
@ -331,7 +329,10 @@ mod tests {
2375, 2376, 2377, 2378, 2379, 2380, 2381, 2382, 2383, 2384, 2385, 2386, 2390, 2392, 2375, 2376, 2377, 2378, 2379, 2380, 2381, 2382, 2383, 2384, 2385, 2386, 2390, 2392,
2397, 2400, 2401, 2403, 2405, 2409, 2411, 2414, 2417, 2419, 2420, 2424, 2426, 2428, 2397, 2400, 2401, 2403, 2405, 2409, 2411, 2414, 2417, 2419, 2420, 2424, 2426, 2428,
2439, 2454, 2456, 2467, 2468, 2469, 2490, 2515, 2519, 2520, 2521 2439, 2454, 2456, 2467, 2468, 2469, 2490, 2515, 2519, 2520, 2521
].iter().cloned().collect() ]
.iter()
.cloned()
.collect()
); );
let lines = b"* SEARCH\r\n"; let lines = b"* SEARCH\r\n";