1 # POP3 Usage Scenarios and Workflows
2
3 This document provides practical, real-world scenarios demonstrating how to use the POP3 implementation in various situations.
4
5 ## Table of Contents
6
7 1. [Basic Email Retrieval](#basic-email-retrieval)
8 2. [Download and Delete Workflow](#download-and-delete-workflow)
9 3. [Selective Message Management](#selective-message-management)
10 4. [Mobile Email Client](#mobile-email-client)
11 5. [Email Backup Solution](#email-backup-solution)
12 6. [Multi-Device Synchronization](#multi-device-synchronization)
13 7. [Email Migration](#email-migration)
14 8. [Automated Email Processing](#automated-email-processing)
15 9. [Error Recovery Scenarios](#error-recovery-scenarios)
16
17 ## Basic Email Retrieval
18
19 ### Scenario: Simple Email Client
20
21 A user wants to retrieve and read their emails using a simple email client.
22
23 ```rust
24 use std::io::{BufRead, BufReader, Write};
25 use std::net::TcpStream;
26
27 fn retrieve_emails(username: &str, password: &str) -> Result<Vec<String>, Box<dyn std::error::Error>> {
28 // Connect to server
29 let mut stream = TcpStream::connect("mail.example.com:110")?;
30 let mut reader = BufReader::new(stream.try_clone()?);
31
32 // Read greeting
33 let mut line = String::new();
34 reader.read_line(&mut line)?;
35 println!("Server: {}", line.trim());
36
37 // Authenticate
38 writeln!(stream, "USER {}", username)?;
39 line.clear();
40 reader.read_line(&mut line)?;
41
42 writeln!(stream, "PASS {}", password)?;
43 line.clear();
44 reader.read_line(&mut line)?;
45
46 if !line.starts_with("+OK") {
47 return Err("Authentication failed".into());
48 }
49
50 // Get message count
51 writeln!(stream, "STAT")?;
52 line.clear();
53 reader.read_line(&mut line)?;
54
55 let parts: Vec<&str> = line.split_whitespace().collect();
56 let message_count: usize = parts.get(1).and_then(|s| s.parse().ok()).unwrap_or(0);
57
58 println!("You have {} message(s)", message_count);
59
60 // Retrieve all messages
61 let mut messages = Vec::new();
62
63 for i in 1..=message_count {
64 writeln!(stream, "RETR {}", i)?;
65 line.clear();
66 reader.read_line(&mut line)?;
67
68 if line.starts_with("+OK") {
69 let mut message = String::new();
70 loop {
71 line.clear();
72 reader.read_line(&mut line)?;
73 if line.trim() == "." {
74 break;
75 }
76 message.push_str(&line);
77 }
78 messages.push(message);
79 }
80 }
81
82 // Close connection
83 writeln!(stream, "QUIT")?;
84
85 Ok(messages)
86 }
87
88 fn main() {
89 match retrieve_emails("user@example.com", "password") {
90 Ok(messages) => {
91 for (i, msg) in messages.iter().enumerate() {
92 println!("\n=== Message {} ===", i + 1);
93 // Parse and display message
94 display_message(msg);
95 }
96 }
97 Err(e) => eprintln!("Error: {}", e),
98 }
99 }
100
101 fn display_message(message: &str) {
102 // Parse headers
103 let mut in_headers = true;
104 for line in message.lines() {
105 if line.is_empty() {
106 in_headers = false;
107 continue;
108 }
109
110 if in_headers {
111 if line.starts_with("From:") || line.starts_with("Subject:") || line.starts_with("Date:") {
112 println!("{}", line);
113 }
114 } else {
115 println!("{}", line);
116 }
117 }
118 }
119 ```
120
121 ## Download and Delete Workflow
122
123 ### Scenario: Traditional POP3 Client
124
125 User wants to download emails to their local device and remove them from the server.
126
127 ```rust
128 fn download_and_delete_emails(username: &str, password: &str, save_dir: &str)
129 -> Result<(), Box<dyn std::error::Error>>
130 {
131 let mut client = Pop3Client::connect("mail.example.com", 110)?;
132 client.authenticate(username, password)?;
133
134 let (message_count, _) = client.stat()?;
135 println!("Downloading {} message(s)...", message_count);
136
137 for i in 1..=message_count {
138 // Retrieve message
139 let message = client.retrieve(i)?;
140
141 // Save to file
142 let filename = format!("{}/message_{}.eml", save_dir, i);
143 std::fs::write(&filename, message)?;
144 println!("Saved message {} to {}", i, filename);
145
146 // Delete from server
147 client.delete(i)?;
148 println!("Deleted message {} from server", i);
149 }
150
151 // Commit deletions and close
152 client.quit()?;
153
154 println!("Download complete. {} messages saved and deleted.", message_count);
155
156 Ok(())
157 }
158
159 fn main() {
160 let save_dir = "./emails";
161 std::fs::create_dir_all(save_dir).unwrap();
162
163 if let Err(e) = download_and_delete_emails("user@example.com", "password", save_dir) {
164 eprintln!("Error: {}", e);
165 }
166 }
167 ```
168
169 ## Selective Message Management
170
171 ### Scenario: Download Only Specific Messages
172
173 User wants to download only messages from specific senders or with certain subjects.
174
175 ```rust
176 fn download_selective_messages(
177 username: &str,
178 password: &str,
179 filter_from: Option<&str>,
180 filter_subject: Option<&str>
181 ) -> Result<Vec<String>, Box<dyn std::error::Error>>
182 {
183 let mut client = Pop3Client::connect("mail.example.com", 110)?;
184 client.authenticate(username, password)?;
185
186 let messages = client.list()?;
187 let mut downloaded = Vec::new();
188
189 for (msg_num, _msg_size) in messages {
190 // Retrieve message headers using TOP command (if supported)
191 // Or retrieve full message and parse
192 let message = client.retrieve(msg_num)?;
193
194 let should_download = check_message_matches(&message, filter_from, filter_subject);
195
196 if should_download {
197 println!("Downloading message {}...", msg_num);
198 downloaded.push(message);
199 } else {
200 println!("Skipping message {}...", msg_num);
201 }
202 }
203
204 client.quit()?;
205
206 Ok(downloaded)
207 }
208
209 fn check_message_matches(message: &str, filter_from: Option<&str>, filter_subject: Option<&str>) -> bool {
210 let mut from_match = filter_from.is_none();
211 let mut subject_match = filter_subject.is_none();
212
213 for line in message.lines() {
214 if let Some(filter) = filter_from {
215 if line.starts_with("From:") && line.contains(filter) {
216 from_match = true;
217 }
218 }
219
220 if let Some(filter) = filter_subject {
221 if line.starts_with("Subject:") && line.contains(filter) {
222 subject_match = true;
223 }
224 }
225
226 if line.is_empty() {
227 break; // End of headers
228 }
229 }
230
231 from_match && subject_match
232 }
233
234 fn main() {
235 // Download only messages from boss@company.com
236 match download_selective_messages(
237 "user@example.com",
238 "password",
239 Some("boss@company.com"),
240 None
241 ) {
242 Ok(messages) => {
243 println!("Downloaded {} matching messages", messages.len());
244 }
245 Err(e) => eprintln!("Error: {}", e),
246 }
247 }
248 ```
249
250 ## Mobile Email Client
251
252 ### Scenario: Limited Bandwidth Email Access
253
254 Mobile device with limited bandwidth wants to check emails efficiently.
255
256 ```rust
257 fn check_emails_mobile(username: &str, password: &str) -> Result<(), Box<dyn std::error::Error>> {
258 let mut client = Pop3Client::connect("mail.example.com", 110)?;
259 client.authenticate(username, password)?;
260
261 // Get mailbox statistics
262 let (message_count, total_size) = client.stat()?;
263 println!("Mailbox: {} messages, {} bytes total", message_count, total_size);
264
265 // List messages to see sizes
266 let messages = client.list()?;
267
268 // Download only small messages (< 10KB) for preview
269 for (msg_num, msg_size) in messages {
270 if msg_size < 10240 {
271 println!("\nDownloading small message {} ({} bytes)...", msg_num, msg_size);
272 let message = client.retrieve(msg_num)?;
273 display_message_preview(&message);
274 } else {
275 println!("\nMessage {} is large ({} bytes), skipping...", msg_num, msg_size);
276 // Could use TOP command here to get headers only
277 }
278 }
279
280 client.quit()?;
281 Ok(())
282 }
283
284 fn display_message_preview(message: &str) {
285 println!("--- Preview ---");
286 let mut line_count = 0;
287 for line in message.lines() {
288 if line_count < 15 {
289 println!("{}", line);
290 line_count += 1;
291 } else {
292 println!("... (truncated)");
293 break;
294 }
295 }
296 }
297 ```
298
299 ## Email Backup Solution
300
301 ### Scenario: Automated Email Backup
302
303 Automatically backup all emails from server to local storage periodically.
304
305 ```rust
306 use std::path::Path;
307 use chrono::Local;
308
309 fn backup_emails(
310 username: &str,
311 password: &str,
312 backup_dir: &str
313 ) -> Result<usize, Box<dyn std::error::Error>>
314 {
315 // Create dated backup directory
316 let timestamp = Local::now().format("%Y%m%d_%H%M%S");
317 let backup_path = format!("{}/{}", backup_dir, timestamp);
318 std::fs::create_dir_all(&backup_path)?;
319
320 let mut client = Pop3Client::connect("mail.example.com", 110)?;
321 client.authenticate(username, password)?;
322
323 let (message_count, _) = client.stat()?;
324 println!("Backing up {} messages to {}...", message_count, backup_path);
325
326 let mut backed_up = 0;
327
328 for i in 1..=message_count {
329 match client.retrieve(i) {
330 Ok(message) => {
331 // Generate unique filename
332 let msg_id = extract_message_id(&message)
333 .unwrap_or_else(|| format!("msg_{}", i));
334 let filename = format!("{}/{}.eml", backup_path, sanitize_filename(&msg_id));
335
336 std::fs::write(&filename, message)?;
337 backed_up += 1;
338
339 if backed_up % 10 == 0 {
340 println!("Backed up {} messages...", backed_up);
341 }
342 }
343 Err(e) => {
344 eprintln!("Error backing up message {}: {}", i, e);
345 }
346 }
347 }
348
349 // Don't delete from server (backup only)
350 client.quit()?;
351
352 println!("Backup complete: {} messages saved to {}", backed_up, backup_path);
353
354 Ok(backed_up)
355 }
356
357 fn extract_message_id(message: &str) -> Option<String> {
358 for line in message.lines() {
359 if line.starts_with("Message-ID:") || line.starts_with("Message-Id:") {
360 return Some(line.split(':').nth(1)?.trim().to_string());
361 }
362 }
363 None
364 }
365
366 fn sanitize_filename(name: &str) -> String {
367 name.chars()
368 .map(|c| if c.is_alphanumeric() || c == '-' || c == '_' { c } else { '_' })
369 .collect()
370 }
371
372 fn main() {
373 let backup_dir = "./email_backups";
374 std::fs::create_dir_all(backup_dir).unwrap();
375
376 match backup_emails("user@example.com", "password", backup_dir) {
377 Ok(count) => println!("Successfully backed up {} emails", count),
378 Err(e) => eprintln!("Backup failed: {}", e),
379 }
380 }
381 ```
382
383 ## Multi-Device Synchronization
384
385 ### Scenario: Leave Copy on Server
386
387 User accesses email from multiple devices and wants to leave copies on server.
388
389 ```rust
390 use std::collections::HashSet;
391 use std::fs;
392
393 fn sync_with_leave_copy(
394 username: &str,
395 password: &str,
396 local_cache: &str
397 ) -> Result<(), Box<dyn std::error::Error>>
398 {
399 // Load list of already downloaded message IDs
400 let downloaded_ids = load_downloaded_ids(local_cache)?;
401
402 let mut client = Pop3Client::connect("mail.example.com", 110)?;
403 client.authenticate(username, password)?;
404
405 let messages = client.list()?;
406 let mut new_messages = Vec::new();
407
408 // Download only new messages
409 for (msg_num, _) in messages {
410 let message = client.retrieve(msg_num)?;
411 let msg_id = extract_message_id(&message)
412 .unwrap_or_else(|| format!("msg_{}", msg_num));
413
414 if !downloaded_ids.contains(&msg_id) {
415 println!("New message: {}", msg_id);
416 new_messages.push((msg_id, message));
417 }
418 }
419
420 // Don't delete from server
421 client.quit()?;
422
423 // Save new messages locally
424 for (msg_id, message) in new_messages {
425 let filename = format!("{}/{}.eml", local_cache, sanitize_filename(&msg_id));
426 fs::write(&filename, message)?;
427 save_downloaded_id(local_cache, &msg_id)?;
428 }
429
430 Ok(())
431 }
432
433 fn load_downloaded_ids(cache_dir: &str) -> Result<HashSet<String>, Box<dyn std::error::Error>> {
434 let index_file = format!("{}/downloaded.txt", cache_dir);
435
436 if !Path::new(&index_file).exists() {
437 return Ok(HashSet::new());
438 }
439
440 let content = fs::read_to_string(index_file)?;
441 Ok(content.lines().map(|s| s.to_string()).collect())
442 }
443
444 fn save_downloaded_id(cache_dir: &str, msg_id: &str) -> Result<(), Box<dyn std::error::Error>> {
445 let index_file = format!("{}/downloaded.txt", cache_dir);
446 let mut content = fs::read_to_string(&index_file).unwrap_or_default();
447 content.push_str(&format!("{}\n", msg_id));
448 fs::write(index_file, content)?;
449 Ok(())
450 }
451 ```
452
453 ## Email Migration
454
455 ### Scenario: Migrate from Old Server to New Server
456
457 Transfer all emails from one server to another.
458
459 ```rust
460 fn migrate_emails(
461 old_server: &str,
462 old_user: &str,
463 old_pass: &str,
464 new_server: &str,
465 new_user: &str,
466 new_pass: &str
467 ) -> Result<usize, Box<dyn std::error::Error>>
468 {
469 // Connect to old server (POP3)
470 let mut old_client = Pop3Client::connect(old_server, 110)?;
471 old_client.authenticate(old_user, old_pass)?;
472
473 let (message_count, _) = old_client.stat()?;
474 println!("Migrating {} messages from {} to {}...", message_count, old_server, new_server);
475
476 // Connect to new server (SMTP for delivery)
477 let mut new_client = SmtpClient::connect(new_server, 587)?;
478 new_client.authenticate(new_user, new_pass)?;
479
480 let mut migrated = 0;
481
482 for i in 1..=message_count {
483 // Retrieve from old server
484 match old_client.retrieve(i) {
485 Ok(message) => {
486 // Send to new server
487 match new_client.send_raw_message(&message) {
488 Ok(_) => {
489 migrated += 1;
490 println!("Migrated message {}/{}", migrated, message_count);
491
492 // Optionally delete from old server after successful migration
493 // old_client.delete(i)?;
494 }
495 Err(e) => {
496 eprintln!("Error migrating message {}: {}", i, e);
497 }
498 }
499 }
500 Err(e) => {
501 eprintln!("Error retrieving message {}: {}", i, e);
502 }
503 }
504
505 // Rate limiting - don't overwhelm servers
506 std::thread::sleep(std::time::Duration::from_millis(100));
507 }
508
509 old_client.quit()?;
510 new_client.quit()?;
511
512 println!("Migration complete: {}/{} messages migrated", migrated, message_count);
513
514 Ok(migrated)
515 }
516 ```
517
518 ## Automated Email Processing
519
520 ### Scenario: Process Incoming Emails Automatically
521
522 Automatically process emails based on rules (e.g., save attachments, forward specific emails).
523
524 ```rust
525 use std::path::Path;
526
527 struct EmailRule {
528 from_filter: Option<String>,
529 subject_filter: Option<String>,
530 action: EmailAction,
531 }
532
533 enum EmailAction {
534 SaveAttachments(String),
535 Forward(String),
536 Delete,
537 Archive(String),
538 }
539
540 fn process_emails_automatically(
541 username: &str,
542 password: &str,
543 rules: Vec<EmailRule>
544 ) -> Result<(), Box<dyn std::error::Error>>
545 {
546 let mut client = Pop3Client::connect("mail.example.com", 110)?;
547 client.authenticate(username, password)?;
548
549 let messages = client.list()?;
550
551 for (msg_num, _) in messages {
552 let message = client.retrieve(msg_num)?;
553
554 // Apply rules
555 for rule in &rules {
556 if matches_rule(&message, rule) {
557 println!("Message {} matches rule, applying action...", msg_num);
558 apply_action(&message, &rule.action)?;
559
560 if matches!(rule.action, EmailAction::Delete) {
561 client.delete(msg_num)?;
562 }
563 }
564 }
565 }
566
567 client.quit()?;
568 Ok(())
569 }
570
571 fn matches_rule(message: &str, rule: &EmailRule) -> bool {
572 let mut matches = true;
573
574 if let Some(from_filter) = &rule.from_filter {
575 let mut found = false;
576 for line in message.lines() {
577 if line.starts_with("From:") && line.contains(from_filter) {
578 found = true;
579 break;
580 }
581 }
582 matches = matches && found;
583 }
584
585 if let Some(subject_filter) = &rule.subject_filter {
586 let mut found = false;
587 for line in message.lines() {
588 if line.starts_with("Subject:") && line.contains(subject_filter) {
589 found = true;
590 break;
591 }
592 }
593 matches = matches && found;
594 }
595
596 matches
597 }
598
599 fn apply_action(message: &str, action: &EmailAction) -> Result<(), Box<dyn std::error::Error>> {
600 match action {
601 EmailAction::SaveAttachments(dir) => {
602 println!("Saving attachments to {}", dir);
603 // Parse message and save attachments
604 save_attachments(message, dir)?;
605 }
606 EmailAction::Forward(address) => {
607 println!("Forwarding to {}", address);
608 // Forward message
609 forward_message(message, address)?;
610 }
611 EmailAction::Delete => {
612 println!("Marking for deletion");
613 }
614 EmailAction::Archive(dir) => {
615 println!("Archiving to {}", dir);
616 let filename = format!("{}/archived_{}.eml", dir, chrono::Local::now().timestamp());
617 std::fs::write(filename, message)?;
618 }
619 }
620 Ok(())
621 }
622
623 fn save_attachments(message: &str, dir: &str) -> Result<(), Box<dyn std::error::Error>> {
624 // Simplified - in production, use proper MIME parsing library
625 std::fs::create_dir_all(dir)?;
626 println!("Attachments saved (implementation simplified)");
627 Ok(())
628 }
629
630 fn forward_message(message: &str, to: &str) -> Result<(), Box<dyn std::error::Error>> {
631 println!("Forwarding message to {} (implementation simplified)", to);
632 Ok(())
633 }
634
635 fn main() {
636 let rules = vec![
637 EmailRule {
638 from_filter: Some("newsletter@company.com".to_string()),
639 subject_filter: None,
640 action: EmailAction::Archive("./newsletters".to_string()),
641 },
642 EmailRule {
643 from_filter: Some("spam@".to_string()),
644 subject_filter: None,
645 action: EmailAction::Delete,
646 },
647 EmailRule {
648 from_filter: None,
649 subject_filter: Some("[URGENT]".to_string()),
650 action: EmailAction::Forward("mobile@example.com".to_string()),
651 },
652 ];
653
654 if let Err(e) = process_emails_automatically("user@example.com", "password", rules) {
655 eprintln!("Error: {}", e);
656 }
657 }
658 ```
659
660 ## Error Recovery Scenarios
661
662 ### Scenario: Handling Network Interruptions
663
664 Robust email retrieval with retry logic and error recovery.
665
666 ```rust
667 use std::time::Duration;
668 use std::thread;
669
670 fn retrieve_with_retry(
671 username: &str,
672 password: &str,
673 max_retries: u32
674 ) -> Result<Vec<String>, Box<dyn std::error::Error>>
675 {
676 let mut retry_count = 0;
677
678 loop {
679 match try_retrieve_emails(username, password) {
680 Ok(messages) => return Ok(messages),
681 Err(e) => {
682 retry_count += 1;
683
684 if retry_count >= max_retries {
685 return Err(format!("Failed after {} retries: {}", max_retries, e).into());
686 }
687
688 let wait_time = 2u64.pow(retry_count); // Exponential backoff
689 eprintln!("Error: {}. Retrying in {} seconds... (attempt {}/{})",
690 e, wait_time, retry_count, max_retries);
691 thread::sleep(Duration::from_secs(wait_time));
692 }
693 }
694 }
695 }
696
697 fn try_retrieve_emails(username: &str, password: &str) -> Result<Vec<String>, Box<dyn std::error::Error>> {
698 let mut client = Pop3Client::connect("mail.example.com", 110)?;
699 client.authenticate(username, password)?;
700
701 let messages = client.list()?;
702 let mut retrieved = Vec::new();
703
704 for (msg_num, _) in messages {
705 match client.retrieve(msg_num) {
706 Ok(msg) => retrieved.push(msg),
707 Err(e) => {
708 eprintln!("Warning: Failed to retrieve message {}: {}", msg_num, e);
709 // Continue with other messages
710 }
711 }
712 }
713
714 client.quit()?;
715 Ok(retrieved)
716 }
717
718 fn main() {
719 match retrieve_with_retry("user@example.com", "password", 5) {
720 Ok(messages) => {
721 println!("Successfully retrieved {} messages", messages.len());
722 }
723 Err(e) => {
724 eprintln!("Failed to retrieve emails: {}", e);
725 }
726 }
727 }
728 ```
729
730 ### Scenario: Partial Download Recovery
731
732 Resume downloading messages after interruption.
733
734 ```rust
735 use std::collections::HashSet;
736
737 fn resume_download(
738 username: &str,
739 password: &str,
740 save_dir: &str
741 ) -> Result<(), Box<dyn std::error::Error>>
742 {
743 // Check which messages are already downloaded
744 let downloaded = get_downloaded_messages(save_dir)?;
745
746 let mut client = Pop3Client::connect("mail.example.com", 110)?;
747 client.authenticate(username, password)?;
748
749 let messages = client.list()?;
750 let total = messages.len();
751 let mut downloaded_count = downloaded.len();
752
753 println!("Found {} total messages, {} already downloaded", total, downloaded_count);
754
755 for (msg_num, _) in messages {
756 if downloaded.contains(&msg_num) {
757 println!("Skipping already downloaded message {}", msg_num);
758 continue;
759 }
760
761 match client.retrieve(msg_num) {
762 Ok(message) => {
763 let filename = format!("{}/message_{}.eml", save_dir, msg_num);
764 std::fs::write(&filename, message)?;
765 downloaded_count += 1;
766 println!("Downloaded message {}/{}", downloaded_count, total);
767 }
768 Err(e) => {
769 eprintln!("Error downloading message {}: {}", msg_num, e);
770 // Save progress and exit
771 return Err(e);
772 }
773 }
774 }
775
776 client.quit()?;
777 println!("Download complete!");
778 Ok(())
779 }
780
781 fn get_downloaded_messages(save_dir: &str) -> Result<HashSet<usize>, Box<dyn std::error::Error>> {
782 let mut downloaded = HashSet::new();
783
784 if let Ok(entries) = std::fs::read_dir(save_dir) {
785 for entry in entries.flatten() {
786 if let Some(filename) = entry.file_name().to_str() {
787 if filename.starts_with("message_") && filename.ends_with(".eml") {
788 if let Some(num_str) = filename.strip_prefix("message_").and_then(|s| s.strip_suffix(".eml")) {
789 if let Ok(num) = num_str.parse::<usize>() {
790 downloaded.insert(num);
791 }
792 }
793 }
794 }
795 }
796 }
797
798 Ok(downloaded)
799 }
800 ```
801
802 ## Conclusion
803
804 These scenarios demonstrate practical uses of POP3 in various contexts:
805
806 1. **Basic Retrieval** - Simple email client functionality
807 2. **Download & Delete** - Traditional POP3 usage
808 3. **Selective Management** - Smart filtering and downloading
809 4. **Mobile Access** - Bandwidth-conscious access
810 5. **Backup** - Automated email backup
811 6. **Multi-Device** - Leave copy on server
812 7. **Migration** - Transfer emails between servers
813 8. **Automation** - Rule-based processing
814 9. **Error Recovery** - Robust error handling
815
816 Each scenario includes working code examples that can be adapted for specific use cases. The key is to choose the right approach based on your requirements for message persistence, bandwidth usage, and device synchronization.