diff --git a/Cargo.toml b/Cargo.toml index 5deabea..b541d62 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,3 +34,4 @@ lazy_static = "1.4" [dev-dependencies] lettre = "0.9" lettre_email = "0.9" +rustls-connector = "0.8.0" diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 66380c9..bfbe861 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -9,12 +9,12 @@ stages: # This represents the minimum Rust version supported. # Tests are not run as tests may require newer versions of rust. - stage: msrv - displayName: "Minimum supported Rust version: 1.32.0" + displayName: "Minimum supported Rust version: 1.36.0" dependsOn: [] jobs: - template: azure/cargo-check.yml@templates parameters: - rust: 1.32.0 + rust: 1.36.0 - stage: test displayName: Test suite dependsOn: check diff --git a/examples/README.md b/examples/README.md index 7de0c82..b1bde9d 100644 --- a/examples/README.md +++ b/examples/README.md @@ -6,3 +6,4 @@ This directory contains examples of working with the IMAP client. Examples: * basic - This is a very basic example of using the client. * gmail_oauth2 - This is an example using oauth2 for logging into gmail as a secure appplication. + * rustls - This demonstrates how to use Rustls instead of Openssl for secure connections (helpful for cross compilation). diff --git a/examples/rustls.rs b/examples/rustls.rs new file mode 100644 index 0000000..2e67e37 --- /dev/null +++ b/examples/rustls.rs @@ -0,0 +1,63 @@ +extern crate imap; +extern crate rustls_connector; + +use std::{env, error::Error, net::TcpStream}; + +use rustls_connector::RustlsConnector; + +fn main() -> Result<(), Box> { + // Read config from environment or .env file + let host = env::var("HOST").expect("missing envvar host"); + let user = env::var("MAILUSER").expect("missing envvar USER"); + let password = env::var("PASSWORD").expect("missing envvar password"); + let port = 993; + + if let Some(email) = fetch_inbox_top(host, user, password, port)? { + println!("{}", &email); + } + + Ok(()) +} + +fn fetch_inbox_top( + host: String, + user: String, + password: String, + port: u16, +) -> Result, Box> { + // Setup Rustls TcpStream + let stream = TcpStream::connect((host.as_ref(), port))?; + let tls = RustlsConnector::default(); + let tlsstream = tls.connect(&host, stream)?; + + // we pass in the domain twice to check that the server's TLS + // certificate is valid for the domain we're connecting to. + let client = imap::Client::new(tlsstream); + + // the client we have here is unauthenticated. + // to do anything useful with the e-mails, we need to log in + let mut imap_session = client.login(&user, &password).map_err(|e| e.0)?; + + // we want to fetch the first email in the INBOX mailbox + imap_session.select("INBOX")?; + + // fetch message number 1 in this mailbox, along with its RFC822 field. + // RFC 822 dictates the format of the body of e-mails + let messages = imap_session.fetch("1", "RFC822")?; + let message = if let Some(m) = messages.iter().next() { + m + } else { + return Ok(None); + }; + + // extract the message's body + let body = message.body().expect("message did not have a body!"); + 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)) +} diff --git a/src/client.rs b/src/client.rs index 8ec0db9..f517a6c 100644 --- a/src/client.rs +++ b/src/client.rs @@ -222,6 +222,9 @@ macro_rules! ok_or_unauth_client_err { impl Client { /// Creates a new client over the given stream. /// + /// For an example of how to use this method to provide a pure-Rust TLS integration, see the + /// rustls.rs in the examples/ directory. + /// /// This method primarily exists for writing tests that mock the underlying transport, but can /// also be used to support IMAP over custom tunnels. pub fn new(stream: T) -> Client {