|
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. |