Oregami
Repositories/oxedyne/fe2o3

oxedyne/fe2o3/fe2o3_mail/examples/imap_probe.rs

2.4 KiB, 1 run

created by r1870400018:13334, which is this file's identity for as long as the history lasts, whatever it is later renamed to

download · who wrote it · its history

1//! Probe a live IMAP server: connect over TLS, read the greeting, list the
2//! capabilities. With no credentials it stops there, checking only that a
3//! junk login is refused cleanly. With them it selects INBOX and prints the
4//! newest few headers.
5//!
6//! ```text
7//! cargo run -p oxedyne_fe2o3_mail --example imap_probe -- imap.gmail.com 993 [user] [app-password]
8//! ```
9
10use oxedyne_fe2o3_core::prelude::*;
11use oxedyne_fe2o3_net::imap::client::{
12 FetchWhat,
13 ImapClient,
14 ImapConfig,
15 Security,
16};
17
18#[tokio::main]
19async fn main() -> Outcome<()> {
20 let args: Vec<String> = std::env::args().collect();
21 let host = args.get(1).cloned().unwrap_or_else(|| fmt!("imap.gmail.com"));
22 let port: u16 = args.get(2).and_then(|s| s.parse().ok()).unwrap_or(993);
23
24 let cfg = ImapConfig::new(host.clone(), port, Security::ImplicitTls);
25 println!("Connecting to {}:{} over TLS...", host, port);
26 let mut c = res!(ImapClient::connect(&cfg).await);
27 println!("Connected. Capabilities: {:?}", res!(c.capability().await));
28
29 let user = match args.get(3) {
30 Some(u) => u.clone(),
31 None => {
32 println!("No credentials given; checking that a junk login is refused.");
33 match c.login("nobody@example.invalid", "not-a-password").await {
34 Ok(()) => println!("UNEXPECTED: the server accepted a junk login."),
35 Err(e) => println!("Refused, as it should be: {}", e),
36 }
37 return Ok(());
38 }
39 };
40 let pass = args.get(4).cloned().unwrap_or_default();
41
42 res!(c.login(&user, &pass).await);
43 println!("Logged in as {}.", user);
44 for mb in res!(c.list("", "*").await) {
45 println!(" mailbox: {} (delim {:?}, {:?})", mb.name, mb.delimiter, mb.attrs);
46 }
47 let st = res!(c.select("INBOX").await);
48 println!("INBOX: {} messages, uidvalidity {}, uidnext {}",
49 st.exists, st.uid_validity, st.uid_next);
50
51 let from = st.uid_next.saturating_sub(3).max(1);
52 let uids = res!(c.uid_search(&fmt!("UID {}:*", from)).await);
53 for msg in res!(c.uid_fetch(&uids, FetchWhat::Headers).await) {
54 let head = String::from_utf8_lossy(&msg.body);
55 let subj = head.lines()
56 .find(|l| l.to_lowercase().starts_with("subject:"))
57 .unwrap_or("(no subject)")
58 .to_string();
59 println!(" uid {} [{}] {} ({} bytes)",
60 msg.uid, msg.flags.join(","), subj.trim(), msg.size);
61 }
62 res!(c.logout().await);
63 Ok(())
64}