//! IMAP error types. use std::error::Error as StdError; use std::fmt; use std::io::Error as IoError; #[cfg(feature = "tls")] use std::net::TcpStream; use std::result; use std::str::Utf8Error; use base64::DecodeError; use bufstream::IntoInnerError as BufError; use imap_proto::{types::ResponseCode, Response}; #[cfg(feature = "tls")] use native_tls::Error as TlsError; #[cfg(feature = "tls")] use native_tls::HandshakeError as TlsHandshakeError; /// A convenience wrapper around `Result` for `imap::Error`. pub type Result = result::Result; /// A BAD response from the server, which indicates an error message from the server. #[derive(Debug)] #[non_exhaustive] pub struct Bad { /// Human-redable message included with the Bad response. pub information: String, /// A more specific error status code included with the Bad response. pub code: Option>, } impl fmt::Display for Bad { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.information) } } /// A NO response from the server, which indicates an operational error message from the server. #[derive(Debug)] #[non_exhaustive] pub struct No { /// Human-redable message included with the NO response. pub information: String, /// A more specific error status code included with the NO response. pub code: Option>, } impl fmt::Display for No { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.information) } } /// A set of errors that can occur in the IMAP client #[derive(Debug)] #[non_exhaustive] pub enum Error { /// An `io::Error` that occurred while trying to read or write to a network stream. Io(IoError), /// An error from the `native_tls` library during the TLS handshake. #[cfg(feature = "tls")] TlsHandshake(TlsHandshakeError), /// An error from the `native_tls` library while managing the socket. #[cfg(feature = "tls")] Tls(TlsError), /// A BAD response from the IMAP server. Bad(Bad), /// A NO response from the IMAP server. No(No), /// The connection was terminated unexpectedly. ConnectionLost, /// Error parsing a server response. Parse(ParseError), /// Command inputs were not valid [IMAP /// strings](https://tools.ietf.org/html/rfc3501#section-4.3). Validate(ValidateError), /// Error appending an e-mail. Append, } impl From for Error { fn from(err: IoError) -> Error { Error::Io(err) } } impl From for Error { fn from(err: ParseError) -> Error { Error::Parse(err) } } impl From> for Error { fn from(err: BufError) -> Error { Error::Io(err.into()) } } #[cfg(feature = "tls")] impl From> for Error { fn from(err: TlsHandshakeError) -> Error { Error::TlsHandshake(err) } } #[cfg(feature = "tls")] impl From for Error { fn from(err: TlsError) -> Error { Error::Tls(err) } } impl<'a> From> for Error { fn from(err: Response<'a>) -> Error { Error::Parse(ParseError::Unexpected(format!("{:?}", err))) } } impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { Error::Io(ref e) => fmt::Display::fmt(e, f), #[cfg(feature = "tls")] Error::Tls(ref e) => fmt::Display::fmt(e, f), #[cfg(feature = "tls")] Error::TlsHandshake(ref e) => fmt::Display::fmt(e, f), Error::Validate(ref e) => fmt::Display::fmt(e, f), Error::Parse(ref e) => fmt::Display::fmt(e, f), Error::No(ref data) => write!(f, "No Response: {}", data), Error::Bad(ref data) => write!(f, "Bad Response: {}", data), Error::ConnectionLost => f.write_str("Connection Lost"), Error::Append => f.write_str("Could not append mail to mailbox"), } } } impl StdError for Error { #[allow(deprecated)] fn description(&self) -> &str { match *self { Error::Io(ref e) => e.description(), #[cfg(feature = "tls")] Error::Tls(ref e) => e.description(), #[cfg(feature = "tls")] Error::TlsHandshake(ref e) => e.description(), Error::Parse(ref e) => e.description(), Error::Validate(ref e) => e.description(), Error::Bad(_) => "Bad Response", Error::No(_) => "No Response", Error::ConnectionLost => "Connection lost", Error::Append => "Could not append mail to mailbox", } } fn cause(&self) -> Option<&dyn StdError> { match *self { Error::Io(ref e) => Some(e), #[cfg(feature = "tls")] Error::Tls(ref e) => Some(e), #[cfg(feature = "tls")] Error::TlsHandshake(ref e) => Some(e), Error::Parse(ParseError::DataNotUtf8(_, ref e)) => Some(e), _ => None, } } } /// An error occured while trying to parse a server response. #[derive(Debug)] pub enum ParseError { /// Indicates an error parsing the status response. Such as OK, NO, and BAD. Invalid(Vec), /// An unexpected response was encountered. Unexpected(String), /// The client could not find or decode the server's authentication challenge. Authentication(String, Option), /// The client received data that was not UTF-8 encoded. DataNotUtf8(Vec, Utf8Error), } impl fmt::Display for ParseError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { ParseError::Invalid(_) => f.write_str("Unable to parse status response"), ParseError::Unexpected(_) => f.write_str("Encountered unexpected parse response"), ParseError::Authentication(_, _) => { f.write_str("Unable to parse authentication response") } ParseError::DataNotUtf8(_, _) => f.write_str("Unable to parse data as UTF-8 text"), } } } impl StdError for ParseError { fn description(&self) -> &str { match *self { ParseError::Invalid(_) => "Unable to parse status response", ParseError::Unexpected(_) => "Encountered unexpected parsed response", ParseError::Authentication(_, _) => "Unable to parse authentication response", ParseError::DataNotUtf8(_, _) => "Unable to parse data as UTF-8 text", } } fn cause(&self) -> Option<&dyn StdError> { match *self { ParseError::Authentication(_, Some(ref e)) => Some(e), _ => None, } } } /// An [invalid character](https://tools.ietf.org/html/rfc3501#section-4.3) was found in an input /// string. #[derive(Debug)] pub struct ValidateError(pub char); impl fmt::Display for ValidateError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { // print character in debug form because invalid ones are often whitespaces write!(f, "Invalid character in input: {:?}", self.0) } } impl StdError for ValidateError { fn description(&self) -> &str { "Invalid character in input" } fn cause(&self) -> Option<&dyn StdError> { None } }