diff --git a/crates/mailparsing/src/rfc5322.pest b/crates/mailparsing/src/rfc5322.pest index 55153e44..5b147e41 100644 --- a/crates/mailparsing/src/rfc5322.pest +++ b/crates/mailparsing/src/rfc5322.pest @@ -57,7 +57,7 @@ obs_qp = { "\\" ~ ( "\u{00}" | obs_no_ws_ctl | "\r" | "\n") } word = { atom | quoted_string } phrase = { (encoded_word | word)+ | obs_phrase } -obs_phrase = { (encoded_word | word) ~ (encoded_word | word | "." | cfws)* } +obs_phrase = { (encoded_word | word) ~ (encoded_word | word | dot | cfws)* } unstructured = { // This rule always matches; since obs_unstruct is a superset, diff --git a/crates/mailparsing/src/rfc5322_parser.rs b/crates/mailparsing/src/rfc5322_parser.rs index 295a513f..00890b8c 100644 --- a/crates/mailparsing/src/rfc5322_parser.rs +++ b/crates/mailparsing/src/rfc5322_parser.rs @@ -575,10 +575,42 @@ impl Parser { match p.as_rule() { Rule::word => words.push(Self::parse_word(p)?), Rule::encoded_word => words.push(Self::parse_encoded_word(p)?), - // FIXME: obs_phrase + Rule::obs_phrase => { + words.append(&mut Self::parse_obs_phrase(p)?); + } rule => { return Err(MailParsingError::HeaderParse(format!( - "Unhandled {rule:?} in parse_phrase" + "Unexpected {rule:?} in parse_phrase" + ))) + } + } + } + Ok(words) + } + + fn parse_obs_phrase(pair: Pair) -> Result> { + let mut words = vec![]; + let mut current_word = String::new(); + for p in pair.into_inner() { + match p.as_rule() { + Rule::word => { + current_word += &Self::parse_word(p)?; + } + Rule::encoded_word => { + current_word += &Self::parse_encoded_word(p)?; + } + Rule::dot => { + current_word.push('.'); + } + Rule::cfws => { + if !current_word.is_empty() { + words.push(current_word.clone()); + current_word.clear(); + } + } + rule => { + return Err(MailParsingError::HeaderParse(format!( + "Unexpected {rule:?} in parse_obs_phrase" ))) } }