Skip to content

Commit 09d3ead

Browse files
committed
Add rsasl for SASL authentication options
1 parent 36d0c05 commit 09d3ead

File tree

6 files changed

+501
-15
lines changed

6 files changed

+501
-15
lines changed

Cargo.lock

+128-5
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+11
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ bufstream = "0.1.3"
2727
imap-proto = "0.16.1"
2828
nom = { version = "7.1.0", default-features = false }
2929
base64 = "0.21"
30+
rsasl = { version = "2.0.0", default-features = false, features = ["std", "provider_base64"], optional = true }
3031
chrono = { version = "0.4", default-features = false, features = ["std"]}
3132
lazy_static = "1.4"
3233
ouroboros = "0.15.0"
@@ -42,6 +43,8 @@ failure = "0.1.8"
4243
mime = "0.3.4"
4344
openssl = "0.10.35"
4445

46+
rsasl = { version = "2.0.0", default-features = false, features = ["config_builder"] }
47+
4548
[[example]]
4649
name = "basic"
4750
required-features = ["default"]
@@ -69,3 +72,11 @@ required-features = ["default"]
6972
[[test]]
7073
name = "imap_integration"
7174
required-features = ["default"]
75+
76+
[[example]]
77+
name = "rustls_sasl"
78+
required-features = ["rustls-tls", "rsasl", "rsasl/registry_static"]
79+
80+
[[example]]
81+
name = "rustls_sasl_gss"
82+
required-features = ["rustls-tls", "rsasl", "rsasl/registry_static", "rsasl/gssapi"]

examples/rustls_sasl.rs

+85
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
extern crate imap;
2+
3+
use std::{env, error::Error};
4+
use rsasl::callback::{Context, Request, SessionData};
5+
use rsasl::config::SASLConfig;
6+
use rsasl::prelude::{Mechname, SessionError};
7+
use rsasl::property::{AuthId, AuthzId, Hostname, Password};
8+
9+
fn main() -> Result<(), Box<dyn Error>> {
10+
// Read config from environment or .env file
11+
let host = env::var("HOST").expect("missing envvar host");
12+
let user = env::var("MAILUSER").ok();
13+
let password = env::var("PASSWORD").ok();
14+
let port = 993;
15+
16+
if let Some(email) = fetch_inbox_top(host, user, password, port)? {
17+
println!("{}", &email);
18+
}
19+
20+
Ok(())
21+
}
22+
23+
struct MyCb {
24+
authid: Option<String>,
25+
authzid: Option<String>,
26+
passwd: Option<String>,
27+
host: String,
28+
}
29+
impl rsasl::callback::SessionCallback for MyCb {
30+
fn callback(&self, _session_data: &SessionData, _context: &Context, request: &mut Request) -> Result<(), SessionError> {
31+
if let Some(authid) = self.authid.as_deref() { request.satisfy::<AuthId>(authid)?; }
32+
if let Some(authzid) = self.authzid.as_deref() { request.satisfy::<AuthzId>(authzid)?; }
33+
if let Some(passwd) = self.passwd.as_deref() { request.satisfy::<Password>(passwd.as_bytes())?; }
34+
if let Some(authid) = self.authid.as_ref() { request.satisfy::<AuthId>(authid)?; }
35+
request.satisfy::<Hostname>(&self.host)?;
36+
Ok(())
37+
}
38+
}
39+
40+
fn fetch_inbox_top(
41+
host: String,
42+
user: Option<String>,
43+
password: Option<String>,
44+
port: u16,
45+
) -> Result<Option<String>, Box<dyn Error>> {
46+
let client = imap::ClientBuilder::new(&host, port).rustls()?;
47+
48+
let cb = MyCb {
49+
authid: user,
50+
authzid: None,
51+
passwd: password,
52+
host,
53+
};
54+
let saslconfig = SASLConfig::builder().with_defaults().with_callback(cb)?;
55+
56+
println!("SASL configuration options — enable features like 'rsasl/plain' or 'rsasl/sha2' to add available mechanisms:");
57+
println!("{saslconfig:?}");
58+
59+
// the client we have here is unauthenticated.
60+
// to do anything useful with the e-mails, we need to log in
61+
let mut imap_session = client.sasl_auth(saslconfig).map_err(|e| e.0)?;
62+
63+
// we want to fetch the first email in the INBOX mailbox
64+
imap_session.select("INBOX")?;
65+
66+
// fetch message number 1 in this mailbox, along with its RFC822 field.
67+
// RFC 822 dictates the format of the body of e-mails
68+
let messages = imap_session.fetch("1", "RFC822")?;
69+
let message = if let Some(m) = messages.iter().next() {
70+
m
71+
} else {
72+
return Ok(None);
73+
};
74+
75+
// extract the message's body
76+
let body = message.body().expect("message did not have a body!");
77+
let body = std::str::from_utf8(body)
78+
.expect("message was not valid utf-8")
79+
.to_string();
80+
81+
// be nice to the server and log out
82+
imap_session.logout()?;
83+
84+
Ok(Some(body))
85+
}

0 commit comments

Comments
 (0)