rescue logic

This commit is contained in:
Quentin 2023-06-16 10:50:37 +02:00
parent c62f803a95
commit 0e23e49199
Signed by: quentin
GPG key ID: E9602264D639FF68
3 changed files with 39 additions and 7 deletions

View file

@ -1,10 +1,9 @@
use nom::{ use nom::{
IResult, IResult,
branch::alt, branch::alt,
bytes::complete::take_while1, bytes::complete::{is_not, take_while1, take_while, tag},
bytes::complete::tag,
character::complete::space0, character::complete::space0,
combinator::{map, opt}, combinator::{map, opt, recognize},
multi::{many0, many1, fold_many0, separated_list1}, multi::{many0, many1, fold_many0, separated_list1},
sequence::{terminated, preceded, pair, tuple}, sequence::{terminated, preceded, pair, tuple},
}; };
@ -25,7 +24,7 @@ use crate::{datetime, trace, model};
/// See: https://www.rfc-editor.org/rfc/rfc5322.html#section-2.2 /// See: https://www.rfc-editor.org/rfc/rfc5322.html#section-2.2
pub fn section(input: &str) -> IResult<&str, HeaderSection> { pub fn section(input: &str) -> IResult<&str, HeaderSection> {
let (input, headers) = fold_many0( let (input, headers) = fold_many0(
alt((header_field, unknown_field)), alt((header_field, unknown_field, rescue)),
HeaderSection::default, HeaderSection::default,
|mut section, head| { |mut section, head| {
match head { match head {
@ -109,6 +108,11 @@ pub fn section(input: &str) -> IResult<&str, HeaderSection> {
HeaderField::Optional(name, body) => { HeaderField::Optional(name, body) => {
section.optional.insert(name, body); section.optional.insert(name, body);
} }
// Rescue
HeaderField::Rescue(x) => {
section.unparsed.push(x);
}
}; };
section section
} }
@ -149,7 +153,10 @@ pub enum HeaderField<'a> {
ReturnPath(Option<model::MailboxRef>), ReturnPath(Option<model::MailboxRef>),
// 3.6.8. Optional Fields // 3.6.8. Optional Fields
Optional(&'a str, String) Optional(&'a str, String),
// None
Rescue(&'a str),
} }
/// Parse one known header field /// Parse one known header field
@ -285,6 +292,21 @@ fn field_name(input: &str) -> IResult<&str, &str> {
)(input) )(input)
} }
/// Rescue rule
///
/// Something went wrong while parsing headers,
/// trying to fix parsing by consuming
/// one unfolded header line.
///
/// ```abnf
/// rescue = *(*any FWS) *any CRLF
fn rescue(input: &str) -> IResult<&str, HeaderField> {
map(recognize(pair(
many0(pair(is_not("\r\n"), fws)),
pair(is_not("\r\n"), perm_crlf,
))), |x| HeaderField::Rescue(x))(input)
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
@ -447,6 +469,14 @@ mod tests {
fn test_invalid_field_name() { fn test_invalid_field_name() {
assert!(header_field("Unknown: unknown\r\n").is_err()); assert!(header_field("Unknown: unknown\r\n").is_err());
} }
#[test]
fn test_rescue() {
assert_eq!(
rescue("Héron: élan\r\n\tnoël: test\r\n"),
Ok(("", HeaderField::Rescue("Héron: élan\r\n\tnoël: test\r\n"))),
);
}
} }

View file

@ -101,6 +101,5 @@ pub struct HeaderSection<'a> {
// 3.6.8. Optional Fields // 3.6.8. Optional Fields
pub optional: HashMap<&'a str, String>, pub optional: HashMap<&'a str, String>,
//pub failed: HashMap<&'a str, String>, pub unparsed: Vec<&'a str>,
//pub garbage: &'a str,
} }

View file

@ -20,6 +20,9 @@ Subject: Re: Saying Hello
Comments: A simple message Comments: A simple message
Comments: Not that complicated Comments: Not that complicated
Keywords: hello, world Keywords: hello, world
Héron: Raté
Raté raté
raté raté
Keywords: salut, le, monde Keywords: salut, le, monde
Message-ID: <3456@example.net> Message-ID: <3456@example.net>
In-Reply-To: <1234@local.machine.example> In-Reply-To: <1234@local.machine.example>