1 //! POP3 Server Example
2 //!
3 //! This example demonstrates how to set up and run a basic POP3 server
4 //! that can handle client connections, authentication, and message operations.
5 //!
6 //! Usage:
7 //! cargo run --example pop3_server
8 //!
9 //! This example shows:
10 //! - Setting up a POP3 server
11 //! - Handling client connections
12 //! - Processing POP3 commands
13 //! - Managing mailbox state
14 //! - Proper error handling
15
16 use std::collections::HashMap;
17 use std::io::{BufRead, BufReader, Write};
18 use std::net::{TcpListener, TcpStream};
19 use std::sync::{Arc, Mutex};
20 use std::thread;
21
22 /// Represents a mail message
23 #[derive(Clone, Debug)]
24 struct Message {
25 id: usize,
26 size: usize,
27 content: String,
28 deleted: bool,
29 }
30
31 /// User mailbox
32 #[derive(Clone, Debug)]
33 struct Mailbox {
34 messages: Vec<Message>,
35 }
36
37 impl Mailbox {
38 fn new() -> Self {
39 Mailbox {
40 messages: Vec::new(),
41 }
42 }
43
44 fn add_sample_messages(&mut self) {
45 // Add some sample messages for testing
46 self.messages.push(Message {
47 id: 1,
48 size: 250,
49 content: "From: sender1@example.com\r\nTo: user@example.com\r\nSubject: Test Message 1\r\n\r\nThis is the first test message.\r\n".to_string(),
50 deleted: false,
51 });
52
53 self.messages.push(Message {
54 id: 2,
55 size: 300,
56 content: "From: sender2@example.com\r\nTo: user@example.com\r\nSubject: Test Message 2\r\n\r\nThis is the second test message with more content.\r\n".to_string(),
57 deleted: false,
58 });
59
60 self.messages.push(Message {
61 id: 3,
62 size: 180,
63 content: "From: sender3@example.com\r\nTo: user@example.com\r\nSubject: Short Message\r\n\r\nBrief message.\r\n".to_string(),
64 deleted: false,
65 });
66 }
67
68 fn stat(&self) -> (usize, usize) {
69 let count = self.messages.iter().filter(|m| !m.deleted).count();
70 let size = self.messages.iter()
71 .filter(|m| !m.deleted)
72 .map(|m| m.size)
73 .sum();
74 (count, size)
75 }
76
77 fn list_all(&self) -> Vec<(usize, usize)> {
78 self.messages.iter()
79 .filter(|m| !m.deleted)
80 .map(|m| (m.id, m.size))
81 .collect()
82 }
83
84 fn get_message(&self, id: usize) -> Option<&Message> {
85 self.messages.iter()
86 .find(|m| m.id == id && !m.deleted)
87 }
88
89 fn delete_message(&mut self, id: usize) -> bool {
90 if let Some(msg) = self.messages.iter_mut().find(|m| m.id == id && !m.deleted) {
91 msg.deleted = true;
92 true
93 } else {
94 false
95 }
96 }
97
98 fn reset(&mut self) {
99 for msg in &mut self.messages {
100 msg.deleted = false;
101 }
102 }
103
104 fn commit_deletions(&mut self) {
105 self.messages.retain(|m| !m.deleted);
106 }
107 }
108
109 /// POP3 server state
110 struct Pop3Server {
111 users: Arc<Mutex<HashMap<String, String>>>, // username -> password
112 mailboxes: Arc<Mutex<HashMap<String, Mailbox>>>, // username -> mailbox
113 }
114
115 impl Pop3Server {
116 fn new() -> Self {
117 let mut users = HashMap::new();
118 users.insert("testuser".to_string(), "testpass".to_string());
119 users.insert("alice".to_string(), "password123".to_string());
120 users.insert("bob".to_string(), "secret".to_string());
121
122 let mut mailboxes = HashMap::new();
123
124 // Create sample mailbox for testuser
125 let mut testuser_mailbox = Mailbox::new();
126 testuser_mailbox.add_sample_messages();
127 mailboxes.insert("testuser".to_string(), testuser_mailbox);
128
129 // Create sample mailbox for alice
130 let mut alice_mailbox = Mailbox::new();
131 alice_mailbox.add_sample_messages();
132 mailboxes.insert("alice".to_string(), alice_mailbox);
133
134 // Create empty mailbox for bob
135 mailboxes.insert("bob".to_string(), Mailbox::new());
136
137 Pop3Server {
138 users: Arc::new(Mutex::new(users)),
139 mailboxes: Arc::new(Mutex::new(mailboxes)),
140 }
141 }
142
143 fn handle_client(&self, mut stream: TcpStream) {
144 println!("New client connected from {}", stream.peer_addr().unwrap());
145
146 // Send greeting
147 if let Err(e) = writeln!(stream, "+OK POP3 server ready") {
148 eprintln!("Error sending greeting: {}", e);
149 return;
150 }
151
152 let mut reader = BufReader::new(stream.try_clone().unwrap());
153 let mut authenticated_user: Option<String> = None;
154 let mut pending_user: Option<String> = None;
155
156 loop {
157 let mut line = String::new();
158 match reader.read_line(&mut line) {
159 Ok(0) => break, // Connection closed
160 Ok(_) => {
161 let line = line.trim();
162 println!("< {}", line);
163
164 let parts: Vec<&str> = line.splitn(2, ' ').collect();
165 let command = parts[0].to_uppercase();
166 let args = if parts.len() > 1 { parts[1] } else { "" };
167
168 let response = match command.as_str() {
169 "USER" => self.handle_user(args, &mut pending_user),
170 "PASS" => self.handle_pass(args, &pending_user, &mut authenticated_user),
171 "STAT" => self.handle_stat(&authenticated_user),
172 "LIST" => self.handle_list(&authenticated_user, args),
173 "RETR" => self.handle_retr(&authenticated_user, args),
174 "DELE" => self.handle_dele(&authenticated_user, args),
175 "NOOP" => self.handle_noop(&authenticated_user),
176 "RSET" => self.handle_rset(&authenticated_user),
177 "QUIT" => {
178 let resp = self.handle_quit(&mut authenticated_user);
179 if let Err(e) = writeln!(stream, "{}", resp) {
180 eprintln!("Error sending response: {}", e);
181 }
182 break;
183 }
184 "CAPA" => self.handle_capa(),
185 _ => "-ERR Unknown command".to_string(),
186 };
187
188 println!("> {}", response.lines().next().unwrap_or(""));
189 if let Err(e) = write!(stream, "{}", response) {
190 eprintln!("Error sending response: {}", e);
191 break;
192 }
193
194 if let Err(e) = stream.flush() {
195 eprintln!("Error flushing stream: {}", e);
196 break;
197 }
198 }
199 Err(e) => {
200 eprintln!("Error reading from client: {}", e);
201 break;
202 }
203 }
204 }
205
206 println!("Client disconnected");
207 }
208
209 fn handle_user(&self, username: &str, pending_user: &mut Option<String>) -> String {
210 if username.is_empty() {
211 return "-ERR Missing username\r\n".to_string();
212 }
213
214 let users = self.users.lock().unwrap();
215 if users.contains_key(username) {
216 *pending_user = Some(username.to_string());
217 "+OK User accepted\r\n".to_string()
218 } else {
219 "-ERR User not found\r\n".to_string()
220 }
221 }
222
223 fn handle_pass(
224 &self,
225 password: &str,
226 pending_user: &Option<String>,
227 authenticated_user: &mut Option<String>,
228 ) -> String {
229 let username = match pending_user {
230 Some(u) => u,
231 None => return "-ERR Send USER first\r\n".to_string(),
232 };
233
234 let users = self.users.lock().unwrap();
235 if let Some(stored_pass) = users.get(username) {
236 if stored_pass == password {
237 *authenticated_user = Some(username.clone());
238 return "+OK Password accepted\r\n".to_string();
239 }
240 }
241
242 "-ERR Invalid password\r\n".to_string()
243 }
244
245 fn handle_stat(&self, authenticated_user: &Option<String>) -> String {
246 let username = match authenticated_user {
247 Some(u) => u,
248 None => return "-ERR Not authenticated\r\n".to_string(),
249 };
250
251 let mailboxes = self.mailboxes.lock().unwrap();
252 if let Some(mailbox) = mailboxes.get(username) {
253 let (count, size) = mailbox.stat();
254 format!("+OK {} {}\r\n", count, size)
255 } else {
256 "-ERR Mailbox not found\r\n".to_string()
257 }
258 }
259
260 fn handle_list(&self, authenticated_user: &Option<String>, args: &str) -> String {
261 let username = match authenticated_user {
262 Some(u) => u,
263 None => return "-ERR Not authenticated\r\n".to_string(),
264 };
265
266 let mailboxes = self.mailboxes.lock().unwrap();
267 let mailbox = match mailboxes.get(username) {
268 Some(mb) => mb,
269 None => return "-ERR Mailbox not found\r\n".to_string(),
270 };
271
272 if args.is_empty() {
273 // List all messages
274 let (count, size) = mailbox.stat();
275 let mut response = format!("+OK {} messages ({} octets)\r\n", count, size);
276 for (id, size) in mailbox.list_all() {
277 response.push_str(&format!("{} {}\r\n", id, size));
278 }
279 response.push_str(".\r\n");
280 response
281 } else {
282 // List specific message
283 if let Ok(id) = args.parse::<usize>() {
284 if let Some(msg) = mailbox.get_message(id) {
285 format!("+OK {} {}\r\n", id, msg.size)
286 } else {
287 "-ERR No such message\r\n".to_string()
288 }
289 } else {
290 "-ERR Invalid message number\r\n".to_string()
291 }
292 }
293 }
294
295 fn handle_retr(&self, authenticated_user: &Option<String>, args: &str) -> String {
296 let username = match authenticated_user {
297 Some(u) => u,
298 None => return "-ERR Not authenticated\r\n".to_string(),
299 };
300
301 if args.is_empty() {
302 return "-ERR Missing message number\r\n".to_string();
303 }
304
305 let id = match args.parse::<usize>() {
306 Ok(id) => id,
307 Err(_) => return "-ERR Invalid message number\r\n".to_string(),
308 };
309
310 let mailboxes = self.mailboxes.lock().unwrap();
311 let mailbox = match mailboxes.get(username) {
312 Some(mb) => mb,
313 None => return "-ERR Mailbox not found\r\n".to_string(),
314 };
315
316 if let Some(msg) = mailbox.get_message(id) {
317 format!("+OK {} octets\r\n{}\r\n.\r\n", msg.size, msg.content)
318 } else {
319 "-ERR No such message\r\n".to_string()
320 }
321 }
322
323 fn handle_dele(&self, authenticated_user: &Option<String>, args: &str) -> String {
324 let username = match authenticated_user {
325 Some(u) => u,
326 None => return "-ERR Not authenticated\r\n".to_string(),
327 };
328
329 if args.is_empty() {
330 return "-ERR Missing message number\r\n".to_string();
331 }
332
333 let id = match args.parse::<usize>() {
334 Ok(id) => id,
335 Err(_) => return "-ERR Invalid message number\r\n".to_string(),
336 };
337
338 let mut mailboxes = self.mailboxes.lock().unwrap();
339 let mailbox = match mailboxes.get_mut(username) {
340 Some(mb) => mb,
341 None => return "-ERR Mailbox not found\r\n".to_string(),
342 };
343
344 if mailbox.delete_message(id) {
345 format!("+OK Message {} deleted\r\n", id)
346 } else {
347 "-ERR No such message\r\n".to_string()
348 }
349 }
350
351 fn handle_noop(&self, authenticated_user: &Option<String>) -> String {
352 if authenticated_user.is_none() {
353 return "-ERR Not authenticated\r\n".to_string();
354 }
355 "+OK\r\n".to_string()
356 }
357
358 fn handle_rset(&self, authenticated_user: &Option<String>) -> String {
359 let username = match authenticated_user {
360 Some(u) => u,
361 None => return "-ERR Not authenticated\r\n".to_string(),
362 };
363
364 let mut mailboxes = self.mailboxes.lock().unwrap();
365 if let Some(mailbox) = mailboxes.get_mut(username) {
366 mailbox.reset();
367 let (count, size) = mailbox.stat();
368 format!("+OK Maildrop has {} messages ({} octets)\r\n", count, size)
369 } else {
370 "-ERR Mailbox not found\r\n".to_string()
371 }
372 }
373
374 fn handle_quit(&self, authenticated_user: &mut Option<String>) -> String {
375 if let Some(username) = authenticated_user.take() {
376 let mut mailboxes = self.mailboxes.lock().unwrap();
377 if let Some(mailbox) = mailboxes.get_mut(&username) {
378 mailbox.commit_deletions();
379 }
380 }
381 "+OK POP3 server signing off\r\n".to_string()
382 }
383
384 fn handle_capa(&self) -> String {
385 let mut response = "+OK Capability list follows\r\n".to_string();
386 response.push_str("USER\r\n");
387 response.push_str("UIDL\r\n");
388 response.push_str("TOP\r\n");
389 response.push_str(".\r\n");
390 response
391 }
392 }
393
394 fn main() {
395 println!("========================================");
396 println!("POP3 Server Example");
397 println!("========================================\n");
398
399 let server = Arc::new(Pop3Server::new());
400
401 let listener = match TcpListener::bind("127.0.0.1:110") {
402 Ok(listener) => {
403 println!("POP3 server listening on 127.0.0.1:110");
404 println!("\nTest users:");
405 println!(" - testuser / testpass (has 3 messages)");
406 println!(" - alice / password123 (has 3 messages)");
407 println!(" - bob / secret (empty mailbox)");
408 println!("\nPress Ctrl+C to stop the server\n");
409 listener
410 }
411 Err(e) => {
412 eprintln!("Failed to bind to port 110: {}", e);
413 eprintln!("\nNote: Port 110 requires root/administrator privileges.");
414 eprintln!("Try running with sudo, or modify the code to use port 1110.");
415 return;
416 }
417 };
418
419 for stream in listener.incoming() {
420 match stream {
421 Ok(stream) => {
422 let server = Arc::clone(&server);
423 thread::spawn(move || {
424 server.handle_client(stream);
425 });
426 }
427 Err(e) => {
428 eprintln!("Error accepting connection: {}", e);
429 }
430 }
431 }
432 }