Improve IMAP example client with proper async handling and security warnings
Co-authored-by: setoelkahfi <1797197+setoelkahfi@users.noreply.github.com>
copilot-swe-agent[bot] committed
Oct 13, 2025 at 16:20 UTC
4a87e3822717db93e211475cbc3dcc0dd3e7eae4
2 files changed
+63
-30
Cargo.toml
+1
-1
@@ -9,7 +9,7 @@ futures = "0.3"
9
native-tls = "0.2"
10
openssl = "0.10"
11
regex = "1"
12
-tokio = { version = "1", features = [ "macros","io-util","net","rt", "rt-multi-thread"] }
12
+tokio = { version = "1", features = [ "macros","io-util","net","rt", "rt-multi-thread", "time"] }
13
tokio-native-tls = "0.3"
14
tokio-util = { version = "0.7", features = ["codec"] }
15
tracing = "0.1"
examples/imap_client.rs
+62
-29
@@ -1,52 +1,85 @@
1
-use std::io::{Read, Write};
2
-use std::net::TcpStream;
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
5
-fn main() -> std::io::Result<()> {
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
8
- let mut stream = TcpStream::connect("127.0.0.1:1143")?;
9
- let mut buffer = [0; 1024];
13
+ let stream = TcpStream::connect("127.0.0.1:1143").await?;
14
+ let mut reader = BufReader::new(stream);
15
16
// Read greeting
12
- let n = stream.read(&mut buffer)?;
13
- println!("Server: {}", String::from_utf8_lossy(&buffer[..n]));
17
+ let greeting = read_response(&mut reader).await?;
18
+ println!("Server: {}", greeting);
19
20
// Send CAPABILITY command
16
- stream.write_all(b"a1 CAPABILITY\r\n")?;
17
- let n = stream.read(&mut buffer)?;
18
- println!("Server: {}", String::from_utf8_lossy(&buffer[..n]));
21
+ send_command(&mut reader, b"a1 CAPABILITY\r\n").await?;
22
20
- // Login
21
- stream.write_all(b"a2 LOGIN testuser testpass\r\n")?;
22
- let n = stream.read(&mut buffer)?;
23
- println!("Server: {}", String::from_utf8_lossy(&buffer[..n]));
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
26
- stream.write_all(b"a3 LIST \"\" \"*\"\r\n")?;
27
- let n = stream.read(&mut buffer)?;
28
- println!("Server: {}", String::from_utf8_lossy(&buffer[..n]));
28
+ send_command(&mut reader, b"a3 LIST \"\" \"*\"\r\n").await?;
29
30
// Select INBOX
31
- stream.write_all(b"a4 SELECT INBOX\r\n")?;
32
- let n = stream.read(&mut buffer)?;
33
- println!("Server: {}", String::from_utf8_lossy(&buffer[..n]));
31
+ send_command(&mut reader, b"a4 SELECT INBOX\r\n").await?;
32
33
// Get status of INBOX
36
- stream.write_all(b"a5 STATUS INBOX (MESSAGES RECENT UNSEEN)\r\n")?;
37
- let n = stream.read(&mut buffer)?;
38
- println!("Server: {}", String::from_utf8_lossy(&buffer[..n]));
34
+ send_command(&mut reader, b"a5 STATUS INBOX (MESSAGES RECENT UNSEEN)\r\n").await?;
35
36
// Search all messages
41
- stream.write_all(b"a6 SEARCH ALL\r\n")?;
42
- let n = stream.read(&mut buffer)?;
43
- println!("Server: {}", String::from_utf8_lossy(&buffer[..n]));
37
+ send_command(&mut reader, b"a6 SEARCH ALL\r\n").await?;
38
39
// Logout
46
- stream.write_all(b"a7 LOGOUT\r\n")?;
47
- let n = stream.read(&mut buffer)?;
48
- println!("Server: {}", String::from_utf8_lossy(&buffer[..n]));
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
+}