Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,12 @@ maintenance = { status = "actively-developed" }
is-it-maintained-issue-resolution = { repository = "jonhoo/rust-imap" }
is-it-maintained-open-issues = { repository = "jonhoo/rust-imap" }

[features]
tls = ["native-tls"]
default = ["tls"]

[dependencies]
native-tls = "0.2.2"
native-tls = { version = "0.2.2", optional = true }
regex = "1.0"
bufstream = "0.1"
imap-proto = "0.9.0"
Expand All @@ -35,3 +39,15 @@ lazy_static = "1.4"
lettre = "0.9"
lettre_email = "0.9"
rustls-connector = "0.8.0"

[[example]]
name = "basic"
required-features = ["default"]

[[example]]
name = "gmail_oauth2"
required-features = ["default"]

[[test]]
name = "imap_integration"
required-features = ["default"]
36 changes: 3 additions & 33 deletions src/client.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use base64;
use bufstream::BufStream;
#[cfg(feature = "tls")]
use native_tls::{TlsConnector, TlsStream};
use nom;
use std::collections::HashSet;
Expand Down Expand Up @@ -109,39 +110,6 @@ impl<T: Read + Write> DerefMut for Session<T> {
}
}

/// Connect to a server using an insecure TCP connection.
///
/// The returned [`Client`] is unauthenticated; to access session-related methods (through
/// [`Session`]), use [`Client::login`] or [`Client::authenticate`].
///
/// Consider using [`connect`] for a secured connection where possible.
/// You can upgrade an insecure client to a secure one using [`Client::secure`].
/// ```rust,no_run
/// # extern crate native_tls;
/// # extern crate imap;
/// # use std::io;
/// # use native_tls::TlsConnector;
/// # fn main() {
/// // a plain, unencrypted TCP connection
/// let client = imap::connect_insecure(("imap.example.org", 143)).unwrap();
///
/// // upgrade to SSL
/// let tls = TlsConnector::builder().build().unwrap();
/// let tls_client = client.secure("imap.example.org", &tls);
/// # }
/// ```
pub fn connect_insecure<A: ToSocketAddrs>(addr: A) -> Result<Client<TcpStream>> {
match TcpStream::connect(addr) {
Ok(stream) => {
let mut socket = Client::new(stream);

socket.read_greeting()?;
Ok(socket)
}
Err(e) => Err(Error::Io(e)),
}
}

/// Connect to a server using a TLS-encrypted connection.
///
/// The returned [`Client`] is unauthenticated; to access session-related methods (through
Expand All @@ -162,6 +130,7 @@ pub fn connect_insecure<A: ToSocketAddrs>(addr: A) -> Result<Client<TcpStream>>
/// let client = imap::connect(("imap.example.org", 993), "imap.example.org", &tls).unwrap();
/// # }
/// ```
#[cfg(feature = "tls")]
pub fn connect<A: ToSocketAddrs, S: AsRef<str>>(
addr: A,
domain: S,
Expand All @@ -186,6 +155,7 @@ impl Client<TcpStream> {
/// This will upgrade an IMAP client from using a regular TCP connection to use TLS.
///
/// The domain parameter is required to perform hostname verification.
#[cfg(feature = "tls")]
pub fn secure<S: AsRef<str>>(
mut self,
domain: S,
Expand Down
16 changes: 16 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,17 @@
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::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`.
Expand All @@ -22,8 +25,10 @@ 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<TcpStream>),
/// An error from the `native_tls` library while managing the socket.
#[cfg(feature = "tls")]
Tls(TlsError),
/// A BAD response from the IMAP server.
Bad(String),
Expand All @@ -38,6 +43,8 @@ pub enum Error {
Validate(ValidateError),
/// Error appending an e-mail.
Append,
#[doc(hidden)]
__Nonexhaustive,
}

impl From<IoError> for Error {
Expand All @@ -58,12 +65,14 @@ impl<T> From<BufError<T>> for Error {
}
}

#[cfg(feature = "tls")]
impl From<TlsHandshakeError<TcpStream>> for Error {
fn from(err: TlsHandshakeError<TcpStream>) -> Error {
Error::TlsHandshake(err)
}
}

#[cfg(feature = "tls")]
impl From<TlsError> for Error {
fn from(err: TlsError) -> Error {
Error::Tls(err)
Expand All @@ -80,7 +89,9 @@ 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::No(ref data) | Error::Bad(ref data) => {
Expand All @@ -95,21 +106,26 @@ impl StdError for Error {
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",
Error::__Nonexhaustive => "Unknown",
}
}

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,
Expand Down
2 changes: 2 additions & 0 deletions src/extensions/idle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

use crate::client::Session;
use crate::error::{Error, Result};
#[cfg(feature = "tls")]
use native_tls::TlsStream;
use std::io::{self, Read, Write};
use std::net::TcpStream;
Expand Down Expand Up @@ -164,6 +165,7 @@ impl<'a> SetReadTimeout for TcpStream {
}
}

#[cfg(feature = "tls")]
impl<'a> SetReadTimeout for TlsStream<TcpStream> {
fn set_read_timeout(&mut self, timeout: Option<Duration>) -> Result<()> {
self.get_ref().set_read_timeout(timeout).map_err(Error::Io)
Expand Down
20 changes: 18 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,29 @@
//! let body = std::str::from_utf8(body)
//! .expect("message was not valid utf-8")
//! .to_string();
//!
//!
//! // be nice to the server and log out
//! imap_session.logout()?;
//!
//!
//! Ok(Some(body))
//! }
//! ```
//!
//! ## Opting out of `native_tls`
//!
//! For situations where using openssl becomes problematic, you can disable the
//! default feature which provides integration with the `native_tls` crate. One major
//! reason you might want to do this is cross-compiling. To opt out of native_tls, add
//! this to your Cargo.toml file:
//!
//! ```toml
//! [dependencies.imap]
//! version = "<some version>"
//! default-features = false
//! ```
//!
//! Even without `native_tls`, you can still use TLS by leveraging the pure Rust `rustls`
//! crate. See the example/rustls.rs file for a working example.
#![deny(missing_docs)]
#![warn(rust_2018_idioms)]

Expand Down
22 changes: 6 additions & 16 deletions tests/imap_integration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,26 +47,16 @@ fn smtp(user: &str) -> lettre::SmtpTransport {
.transport()
}

#[test]
fn connect_insecure() {
imap::connect_insecure(&format!(
"{}:3143",
std::env::var("TEST_HOST").unwrap_or("127.0.0.1".to_string())
))
.unwrap();
}

#[test]
#[ignore]
fn connect_insecure_then_secure() {
let host = std::env::var("TEST_HOST").unwrap_or("127.0.0.1".to_string());
let stream = TcpStream::connect((host.as_ref(), 3143)).unwrap();

// ignored because of https://github.com/greenmail-mail-test/greenmail/issues/135
imap::connect_insecure(&format!(
"{}:3143",
std::env::var("TEST_HOST").unwrap_or("127.0.0.1".to_string())
))
.unwrap()
.secure("imap.example.com", &tls())
.unwrap();
imap::Client::new(stream)
.secure("imap.example.com", &tls())
.unwrap();
}

#[test]
Expand Down