1 use tokio::io::{AsyncReadExt, AsyncWriteExt, BufReader};
2 use tokio::net::TcpStream;
3 use std::time::Duration;
4
5 /// Simple example demonstrating IMAP client interaction
6 ///
7 /// NOTE: This example uses placeholder credentials ('username'/'password').
8 /// In production, always use secure authentication methods.
9 #[tokio::main]
10 async fn main() -> std::io::Result<()> {
11 println!("Connecting to IMAP server at 127.0.0.1:1143...");
12
13 let stream = TcpStream::connect("127.0.0.1:1143").await?;
14 let mut reader = BufReader::new(stream);
15
16 // Read greeting
17 let greeting = read_response(&mut reader).await?;
18 println!("Server: {}", greeting);
19
20 // Send CAPABILITY command
21 send_command(&mut reader, b"a1 CAPABILITY\r\n").await?;
22
23 // Login with placeholder credentials
24 // WARNING: Never hardcode real credentials - use environment variables or config files
25 send_command(&mut reader, b"a2 LOGIN username password\r\n").await?;
26
27 // List mailboxes
28 send_command(&mut reader, b"a3 LIST \"\" \"*\"\r\n").await?;
29
30 // Select INBOX
31 send_command(&mut reader, b"a4 SELECT INBOX\r\n").await?;
32
33 // Get status of INBOX
34 send_command(&mut reader, b"a5 STATUS INBOX (MESSAGES RECENT UNSEEN)\r\n").await?;
35
36 // Search all messages
37 send_command(&mut reader, b"a6 SEARCH ALL\r\n").await?;
38
39 // Logout
40 send_command(&mut reader, b"a7 LOGOUT\r\n").await?;
41
42 println!("IMAP session completed successfully!");
43 Ok(())
44 }
45
46 /// Helper function to send a command and read the complete response
47 async fn send_command(reader: &mut BufReader<TcpStream>, command: &[u8]) -> std::io::Result<()> {
48 reader.get_mut().write_all(command).await?;
49 let response = read_response(reader).await?;
50 println!("Server: {}", response);
51 Ok(())
52 }
53
54 /// Helper function to read a complete IMAP response
55 /// This handles multi-line responses by reading until we see a tagged completion response
56 async fn read_response(reader: &mut BufReader<TcpStream>) -> std::io::Result<String> {
57 let mut response = String::new();
58 let mut buffer = vec![0u8; 1024];
59
60 // Read data with a timeout to handle the streaming nature of IMAP
61 loop {
62 tokio::select! {
63 result = reader.read(&mut buffer) => {
64 let n = result?;
65 if n == 0 {
66 break;
67 }
68 response.push_str(&String::from_utf8_lossy(&buffer[..n]));
69
70 // Check if we have a complete response (ends with a tagged response line)
71 if response.contains(" OK ") || response.contains(" NO ") || response.contains(" BAD ") {
72 break;
73 }
74 }
75 _ = tokio::time::sleep(Duration::from_millis(100)) => {
76 // Timeout - assume response is complete
77 if !response.is_empty() {
78 break;
79 }
80 }
81 }
82 }
83
84 Ok(response.trim_end().to_string())
85 }