From 0ff9466b5f62e5674831febc9c82ddfdef1d5954 Mon Sep 17 00:00:00 2001 From: Quentin Dufour Date: Fri, 16 Jun 2023 11:41:42 +0200 Subject: [PATCH] be more tolerant on field names --- src/header.rs | 36 +++++++++++++++++++++--------------- src/parse.rs | 2 ++ 2 files changed, 23 insertions(+), 15 deletions(-) diff --git a/src/header.rs b/src/header.rs index 4eef551..4785b02 100644 --- a/src/header.rs +++ b/src/header.rs @@ -1,7 +1,7 @@ use nom::{ IResult, branch::alt, - bytes::complete::{is_not, take_while1, take_while, tag}, + bytes::complete::{is_not, take_while1, take_while, tag, tag_no_case}, character::complete::space0, combinator::{map, opt, recognize}, multi::{many0, many1, fold_many0, separated_list1}, @@ -185,36 +185,36 @@ pub fn header_field(input: &str) -> IResult<&str, HeaderField> { // 3.6.1. The Origination Date Field fn date(input: &str) -> IResult<&str, HeaderField> { - let (input, body) = preceded(pair(tag("Date:"), space0), datetime::section)(input)?; + let (input, body) = preceded(field_name_tag("Date"), datetime::section)(input)?; Ok((input, HeaderField::Date(body))) } // 3.6.2. Originator Fields fn from(input: &str) -> IResult<&str, HeaderField> { - let (input, body) = preceded(pair(tag("From:"), space0), mailbox_list)(input)?; + let (input, body) = preceded(field_name_tag("From"), mailbox_list)(input)?; Ok((input, HeaderField::From(body))) } fn sender(input: &str) -> IResult<&str, HeaderField> { - let (input, body) = preceded(pair(tag("Sender:"), space0), mailbox)(input)?; + let (input, body) = preceded(field_name_tag("Sender"), mailbox)(input)?; Ok((input, HeaderField::Sender(body))) } fn reply_to(input: &str) -> IResult<&str, HeaderField> { - let (input, body) = preceded(pair(tag("Reply-To:"), space0), address_list)(input)?; + let (input, body) = preceded(field_name_tag("Reply-To"), address_list)(input)?; Ok((input, HeaderField::ReplyTo(body))) } // 3.6.3. Destination Address Fields fn to(input: &str) -> IResult<&str, HeaderField> { - let (input, body) = preceded(pair(tag("To:"), space0), address_list)(input)?; + let (input, body) = preceded(field_name_tag("To"), address_list)(input)?; Ok((input, HeaderField::To(body))) } fn cc(input: &str) -> IResult<&str, HeaderField> { - let (input, body) = preceded(pair(tag("Cc:"), space0), address_list)(input)?; + let (input, body) = preceded(field_name_tag("Cc"), address_list)(input)?; Ok((input, HeaderField::Cc(body))) } fn bcc(input: &str) -> IResult<&str, HeaderField> { let (input, body) = preceded( - pair(tag("Bcc:"), space0), + field_name_tag("Bcc"), opt(alt((address_list, address_list_cfws))), )(input)?; @@ -223,35 +223,41 @@ fn bcc(input: &str) -> IResult<&str, HeaderField> { // 3.6.4. Identification Fields fn msg_id_field(input: &str) -> IResult<&str, HeaderField> { - let (input, body) = preceded(pair(tag("Message-ID:"), space0), msg_id)(input)?; + let (input, body) = preceded(field_name_tag("Message-ID"), msg_id)(input)?; Ok((input, HeaderField::MessageID(body))) } fn in_reply_to(input: &str) -> IResult<&str, HeaderField> { - let (input, body) = preceded(pair(tag("In-Reply-To:"), space0), many1(msg_id))(input)?; + let (input, body) = preceded(field_name_tag("In-Reply-To"), many1(msg_id))(input)?; Ok((input, HeaderField::InReplyTo(body))) } fn references(input: &str) -> IResult<&str, HeaderField> { - let (input, body) = preceded(pair(tag("References:"), space0), many1(msg_id))(input)?; + let (input, body) = preceded(field_name_tag("References"), many1(msg_id))(input)?; Ok((input, HeaderField::References(body))) } // 3.6.5. Informational Fields fn subject(input: &str) -> IResult<&str, HeaderField> { - let (input, body) = preceded(pair(tag("Subject:"), space0), unstructured)(input)?; + let (input, body) = preceded(field_name_tag("Subject"), unstructured)(input)?; Ok((input, HeaderField::Subject(body))) } fn comments(input: &str) -> IResult<&str, HeaderField> { - let (input, body) = preceded(pair(tag("Comments:"), space0), unstructured)(input)?; + let (input, body) = preceded(field_name_tag("Comments"), unstructured)(input)?; Ok((input, HeaderField::Comments(body))) } fn keywords(input: &str) -> IResult<&str, HeaderField> { let (input, body) = preceded( - pair(tag("Keywords:"), space0), + field_name_tag("Keywords"), separated_list1(tag(","), phrase), )(input)?; Ok((input, HeaderField::Keywords(body))) } +fn field_name_tag(field_name: &str) -> impl FnMut(&str) -> IResult<&str, &str> + '_ { + move |input: &str| { + recognize(tuple((tag_no_case(field_name), space0, tag(":"), space0)))(input) + } +} + // 3.6.6 Resent fields // Not implemented @@ -288,7 +294,7 @@ fn unknown_field(input: &str) -> IResult<&str, HeaderField> { fn field_name(input: &str) -> IResult<&str, &str> { terminated( take_while1(|c| c >= '\x21' && c <= '\x7E' && c != '\x3A'), - pair(tag(":"), space0) + tuple((space0, tag(":"), space0)) )(input) } diff --git a/src/parse.rs b/src/parse.rs index adc3880..115bc8b 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -19,6 +19,8 @@ Bcc: (hidden) Subject: Re: Saying Hello Comments: A simple message Comments: Not that complicated +comments : not valid but should be accepted + by the parser. Keywords: hello, world Héron: Raté Raté raté