diff --git a/crates/mailparsing/src/header.rs b/crates/mailparsing/src/header.rs index 8ac5fdf8..ee4d810e 100644 --- a/crates/mailparsing/src/header.rs +++ b/crates/mailparsing/src/header.rs @@ -19,6 +19,13 @@ pub struct Header<'a> { conformance: HeaderConformance, } +/// Holds the result of parsing a block of headers +pub struct HeaderParseResult<'a> { + pub headers: Vec>, + pub body_offset: usize, + pub overall_conformance: HeaderConformance, +} + impl<'a> Header<'a> { pub fn with_name_value>, V: Into>>( name: N, @@ -68,10 +75,14 @@ impl<'a> Header<'a> { &self.value } - pub fn parse_headers>>(header_block: S) -> Result<(Vec, usize)> { + pub fn parse_headers>>( + header_block: S, + ) -> Result> { let header_block = header_block.into(); let mut headers = vec![]; let mut idx = 0; + let mut overall_conformance = HeaderConformance::default(); + while idx < header_block.len() { let b = header_block[idx]; if headers.is_empty() { @@ -84,6 +95,7 @@ impl<'a> Header<'a> { if b == b'\n' { // LF: End of header block idx += 1; + overall_conformance.set(HeaderConformance::NON_CANONICAL_LINE_ENDINGS, true); break; } if b == b'\r' { @@ -97,6 +109,7 @@ impl<'a> Header<'a> { )); } let (header, next) = Self::parse(header_block.slice(idx..header_block.len()))?; + overall_conformance |= header.conformance; headers.push(header); debug_assert!( idx != next + idx, @@ -104,7 +117,11 @@ impl<'a> Header<'a> { ); idx += next; } - Ok((headers, idx)) + Ok(HeaderParseResult { + headers, + body_offset: idx, + overall_conformance, + }) } pub fn parse>>(header_block: S) -> Result<(Self, usize)> { @@ -245,8 +262,20 @@ mod test { "I am the body" ); - let (headers, body_offset) = Header::parse_headers(message).unwrap(); + let HeaderParseResult { + headers, + body_offset, + overall_conformance, + } = Header::parse_headers(message).unwrap(); assert_eq!(&message[body_offset..], "I am the body"); + k9::snapshot!( + overall_conformance, + " +HeaderConformance( + NON_CANONICAL_LINE_ENDINGS, +) +" + ); k9::snapshot!( headers, r#" diff --git a/crates/mailparsing/src/mimepart.rs b/crates/mailparsing/src/mimepart.rs index 0375845a..386bb3de 100644 --- a/crates/mailparsing/src/mimepart.rs +++ b/crates/mailparsing/src/mimepart.rs @@ -1,3 +1,4 @@ +use crate::header::{HeaderConformance, HeaderParseResult}; use crate::{Header, Result, SharedString}; pub struct MimePart<'a> { @@ -7,16 +8,22 @@ pub struct MimePart<'a> { headers: Vec>, /// The index into bytes of the first non-header byte. body_offset: usize, + overall_conformance: HeaderConformance, } impl<'a> MimePart<'a> { pub fn parse>>(bytes: S) -> Result { let bytes = bytes.into(); - let (headers, body_offset) = Header::parse_headers(bytes.clone())?; + let HeaderParseResult { + headers, + body_offset, + overall_conformance, + } = Header::parse_headers(bytes.clone())?; Ok(Self { bytes, headers, body_offset, + overall_conformance, }) }