Skip to content

Commit 7204697

Browse files
authored
Add ClientBuilder helper to make setting up TLS connections easy. (#197)
Also replaces connect() and connect_starttls() with ClientBuilder.
1 parent c443a3a commit 7204697

19 files changed

+314
-237
lines changed

.cirrus.yml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,12 @@ task:
1111
- . $HOME/.cargo/env
1212
check_script:
1313
- . $HOME/.cargo/env
14-
- cargo check --all-targets
14+
- cargo check --all-targets --all-features
1515
build_script:
1616
- . $HOME/.cargo/env
17-
- cargo build --all-targets --verbose
17+
- cargo build --all-targets --verbose --all-features
1818
test_script:
1919
- . $HOME/.cargo/env
20-
- cargo test --examples
21-
- cargo test --doc
22-
- cargo test --lib
20+
- cargo test --examples --all-features
21+
- cargo test --doc --all-features
22+
- cargo test --lib --all-features

Cargo.toml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,12 @@ categories = ["email", "network-programming"]
1515

1616
[features]
1717
tls = ["native-tls"]
18+
rustls-tls = ["rustls-connector"]
1819
default = ["tls"]
1920

2021
[dependencies]
2122
native-tls = { version = "0.2.2", optional = true }
23+
rustls-connector = { version = "0.13.1", optional = true }
2224
regex = "1.0"
2325
bufstream = "0.1"
2426
imap-proto = "0.14.1"
@@ -45,6 +47,18 @@ required-features = ["default"]
4547
name = "idle"
4648
required-features = ["default"]
4749

50+
[[example]]
51+
name = "rustls"
52+
required-features = ["rustls-tls"]
53+
54+
[[example]]
55+
name = "starttls"
56+
required-features = ["default"]
57+
58+
[[example]]
59+
name = "timeout"
60+
required-features = ["default"]
61+
4862
[[test]]
4963
name = "imap_integration"
5064
required-features = ["default"]

README.md

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ results](https://dev.azure.com/jonhoo/jonhoo/_build/latest?definitionId=11&branc
2222

2323
[@jonhoo]: https://thesquareplanet.com/
2424

25-
To connect, use the [`connect`] function. This gives you an unauthenticated [`Client`]. You can
25+
To connect, use the [`ClientBuilder`]. This gives you an unauthenticated [`Client`]. You can
2626
then use [`Client::login`] or [`Client::authenticate`] to perform username/password or
2727
challenge/response authentication respectively. This in turn gives you an authenticated
2828
[`Session`], which lets you access the mailboxes at the server.
@@ -34,16 +34,9 @@ in the documentation for the various types and methods and read the raw text the
3434
Below is a basic client example. See the `examples/` directory for more.
3535

3636
```rust
37-
extern crate imap;
38-
extern crate native_tls;
39-
4037
fn fetch_inbox_top() -> imap::error::Result<Option<String>> {
41-
let domain = "imap.example.com";
42-
let tls = native_tls::TlsConnector::builder().build().unwrap();
4338

44-
// we pass in the domain twice to check that the server's TLS
45-
// certificate is valid for the domain we're connecting to.
46-
let client = imap::connect((domain, 993), domain, &tls).unwrap();
39+
let client = imap::ClientBuilder::new("imap.example.com", 993).native_tls()?;
4740

4841
// the client we have here is unauthenticated.
4942
// to do anything useful with the e-mails, we need to log in
@@ -90,7 +83,8 @@ default-features = false
9083
```
9184

9285
Even without `native_tls`, you can still use TLS by leveraging the pure Rust `rustls`
93-
crate. See the example/rustls.rs file for a working example.
86+
crate, which is enabled with the `rustls-tls` feature. See the example/rustls.rs file
87+
for a working example.
9488

9589
## Running the test suite
9690

azure-pipelines.yml

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,13 @@ jobs:
2525
- template: install-rust.yml@templates
2626
parameters:
2727
rust: $(rust)
28-
- script: cargo check --all-targets
28+
- script: cargo check --all-targets --all-features
2929
displayName: cargo check
30-
- script: cargo test --examples
30+
- script: cargo test --examples --all-features
3131
displayName: cargo test --examples
32-
- script: cargo test --doc
32+
- script: cargo test --doc --all-features
3333
displayName: cargo test --doc
34-
- script: cargo test --lib
34+
- script: cargo test --lib --all-features
3535
displayName: cargo test --lib
3636
- script: |
3737
set -e
@@ -75,6 +75,18 @@ jobs:
7575
greenmail: greenmail
7676
env:
7777
TEST_HOST: greenmail
78+
- job: features
79+
displayName: "Check feature combinations"
80+
pool:
81+
vmImage: ubuntu-latest
82+
steps:
83+
- template: install-rust.yml@templates
84+
parameters:
85+
rust: stable
86+
- script: cargo install cargo-hack
87+
displayName: install cargo-hack
88+
- script: cargo hack --feature-powerset check --all-targets
89+
displayName: cargo hack
7890

7991
resources:
8092
repositories:

codecov.yml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
coverage:
2+
range: 70..100
3+
round: down
4+
precision: 2
5+
status:
6+
project:
7+
default:
8+
threshold: 2%
9+
10+
# Tests aren't important for coverage
11+
ignore:
12+
- "tests"
13+
14+
# Make less noisy comments
15+
comment:
16+
layout: "files"
17+
require_changes: yes

examples/README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,7 @@ This directory contains examples of working with the IMAP client.
66
Examples:
77
* basic - This is a very basic example of using the client.
88
* gmail_oauth2 - This is an example using oauth2 for logging into gmail as a secure appplication.
9+
* idle - This is an example showing how to use IDLE to monitor a mailbox.
910
* rustls - This demonstrates how to use Rustls instead of Openssl for secure connections (helpful for cross compilation).
10-
* timeout - This demonstrates how to use timeouts while connecting to an IMAP server.
11+
* starttls - This is an example showing how to use STARTTLS after connecting over plaintext.
12+
* timeout - This demonstrates how to use timeouts while connecting to an IMAP server by using a custom TCP/TLS stream initialization and creating a `Client` directly instead of using the `ClientBuilder`.

examples/basic.rs

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
extern crate imap;
2-
extern crate native_tls;
32

43
fn main() {
54
// To connect to the gmail IMAP server with this you will need to allow unsecure apps access.
@@ -9,12 +8,7 @@ fn main() {
98
}
109

1110
fn fetch_inbox_top() -> imap::error::Result<Option<String>> {
12-
let domain = "imap.example.com";
13-
let tls = native_tls::TlsConnector::builder().build().unwrap();
14-
15-
// we pass in the domain twice to check that the server's TLS
16-
// certificate is valid for the domain we're connecting to.
17-
let client = imap::connect((domain, 993), domain, &tls).unwrap();
11+
let client = imap::ClientBuilder::new("imap.example.com", 993).native_tls()?;
1812

1913
// the client we have here is unauthenticated.
2014
// to do anything useful with the e-mails, we need to log in

examples/gmail_oauth2.rs

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
extern crate base64;
22
extern crate imap;
3-
extern crate native_tls;
4-
5-
use native_tls::TlsConnector;
63

74
struct GmailOAuth2 {
85
user: String,
@@ -25,11 +22,10 @@ fn main() {
2522
user: String::from("[email protected]"),
2623
access_token: String::from("<access_token>"),
2724
};
28-
let domain = "imap.gmail.com";
29-
let port = 993;
30-
let socket_addr = (domain, port);
31-
let ssl_connector = TlsConnector::builder().build().unwrap();
32-
let client = imap::connect(socket_addr, domain, &ssl_connector).unwrap();
25+
26+
let client = imap::ClientBuilder::new("imap.gmail.com", 993)
27+
.native_tls()
28+
.expect("Could not connect to imap.gmail.com");
3329

3430
let mut imap_session = match client.authenticate("XOAUTH2", &gmail_auth) {
3531
Ok(c) => c,

examples/idle.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
use native_tls::TlsConnector;
21
use structopt::StructOpt;
32

43
#[derive(StructOpt, Debug)]
@@ -39,9 +38,10 @@ struct Opt {
3938
fn main() {
4039
let opt = Opt::from_args();
4140

42-
let ssl_conn = TlsConnector::builder().build().unwrap();
43-
let client = imap::connect((opt.server.clone(), opt.port), opt.server, &ssl_conn)
41+
let client = imap::ClientBuilder::new(opt.server.clone(), opt.port)
42+
.native_tls()
4443
.expect("Could not connect to imap server");
44+
4545
let mut imap = client
4646
.login(opt.username, opt.password)
4747
.expect("Could not authenticate");

examples/rustls.rs

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
extern crate imap;
2-
extern crate rustls_connector;
32

4-
use std::{env, error::Error, net::TcpStream};
5-
6-
use rustls_connector::RustlsConnector;
3+
use std::{env, error::Error};
74

85
fn main() -> Result<(), Box<dyn Error>> {
96
// Read config from environment or .env file
@@ -25,14 +22,7 @@ fn fetch_inbox_top(
2522
password: String,
2623
port: u16,
2724
) -> Result<Option<String>, Box<dyn Error>> {
28-
// Setup Rustls TcpStream
29-
let stream = TcpStream::connect((host.as_ref(), port))?;
30-
let tls = RustlsConnector::default();
31-
let tlsstream = tls.connect(&host, stream)?;
32-
33-
// we pass in the domain twice to check that the server's TLS
34-
// certificate is valid for the domain we're connecting to.
35-
let client = imap::Client::new(tlsstream);
25+
let client = imap::ClientBuilder::new(&host, port).rustls()?;
3626

3727
// the client we have here is unauthenticated.
3828
// to do anything useful with the e-mails, we need to log in

examples/starttls.rs

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
/**
22
* Here's an example showing how to connect to the IMAP server with STARTTLS.
3-
* The only difference with the `basic.rs` example is when using `imap::connect_starttls()` method
4-
* instead of `imap::connect()` (l. 52), and so you can connect on port 143 instead of 993
5-
* as you have to when using TLS the entire way.
3+
*
4+
* The only difference is calling `starttls()` on the `ClientBuilder` before
5+
* initiating the secure connection with `native_tls()` or `rustls()`, so you
6+
* can connect on port 143 instead of 993.
67
*
78
* The following env vars are expected to be set:
89
* - IMAP_HOST
@@ -11,9 +12,7 @@
1112
* - IMAP_PORT (supposed to be 143)
1213
*/
1314
extern crate imap;
14-
extern crate native_tls;
1515

16-
use native_tls::TlsConnector;
1716
use std::env;
1817
use std::error::Error;
1918

@@ -42,13 +41,10 @@ fn fetch_inbox_top(
4241
password: String,
4342
port: u16,
4443
) -> Result<Option<String>, Box<dyn Error>> {
45-
let domain: &str = host.as_str();
46-
47-
let tls = TlsConnector::builder().build().unwrap();
48-
49-
// we pass in the domain twice to check that the server's TLS
50-
// certificate is valid for the domain we're connecting to.
51-
let client = imap::connect_starttls((domain, port), domain, &tls).unwrap();
44+
let client = imap::ClientBuilder::new(&host, port)
45+
.starttls()
46+
.native_tls()
47+
.expect("Could not connect to server");
5248

5349
// the client we have here is unauthenticated.
5450
// to do anything useful with the e-mails, we need to log in

examples/timeout.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,7 @@ fn connect_timeout<S: AsRef<str>>(
5858
Ok(client)
5959
}
6060

61-
// resolve address and try to connect to all in order; note that this function is required to fully
62-
// mimic `imap::connect` with the usage of `ToSocketAddrs`
61+
// resolve address and try to connect to all in order
6362
fn connect_all_timeout<A: ToSocketAddrs, S: AsRef<str>>(
6463
addr: A,
6564
domain: S,

0 commit comments

Comments
 (0)