Skip to content

Commit 8e09faf

Browse files
adrgsclaude
andcommitted
fix: SMTP email parsing — preserve CRLF line endings for MIME, filter bogus attachments
Two issues with email capture: 1. Line endings normalized to \n but MIME requires \r\n for boundary and quoted-printable parsing. This caused mail-parser to fail on multipart emails (e.g., GitHub verification codes). 2. multipart/* container parts and inline text body parts were listed as "attachments" — filter them out. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 21e7827 commit 8e09faf

1 file changed

Lines changed: 15 additions & 1 deletion

File tree

src/src/smtp/mod.rs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -236,9 +236,10 @@ async fn handle_smtp_connection(
236236
}
237237

238238
// Handle dot-stuffing (lines starting with . have the . removed)
239+
// Preserve \r\n line endings for correct MIME parsing
239240
let content = line_trimmed.strip_prefix('.').unwrap_or(line_trimmed);
240241
email_data.push_str(content);
241-
email_data.push('\n');
242+
email_data.push_str("\r\n");
242243
}
243244
} else {
244245
if line_trimmed.is_empty() {
@@ -452,6 +453,19 @@ fn parse_email_headers(email_data: &str) -> ParsedEmail {
452453

453454
let attachments: Vec<SmtpAttachment> = message
454455
.attachments()
456+
.filter(|part| {
457+
// Skip MIME container types — they're structure, not real attachments
458+
let ctype = part
459+
.content_type()
460+
.map(|ct| ct.ctype().to_string())
461+
.unwrap_or_default();
462+
ctype != "multipart" && ctype != "message"
463+
})
464+
.filter(|part| {
465+
// Skip text/plain and text/html body parts (already in text_body/html_body)
466+
let is_inline_body = part.is_text() && part.attachment_name().is_none();
467+
!is_inline_body
468+
})
455469
.map(|part| {
456470
let content_type = part
457471
.content_type()

0 commit comments

Comments
 (0)