melib/email/parser: add mailing list parser module

Specifically, rfc2369 list header action list
memfd
Manos Pitsidianakis 2020-09-11 00:08:56 +03:00
parent 927a0c3cc0
commit 5cd03fff0f
Signed by: Manos Pitsidianakis
GPG Key ID: 73627C2F690DF710
2 changed files with 66 additions and 10 deletions

View File

@ -48,7 +48,7 @@ impl<'a> From<&'a [u8]> for ListAction<'a> {
impl<'a> ListAction<'a> {
pub fn parse_options_list(input: &'a [u8]) -> Option<SmallVec<[ListAction<'a>; 4]>> {
parser::generic::angle_bracket_delimeted_list(input)
parser::mailing_lists::rfc_2369_list_headers_action_list(input)
.map(|(_, mut vec)| {
/* Prefer email options first, since this _is_ a mail client after all and it's
* more automated */

View File

@ -41,8 +41,8 @@ macro_rules! to_str {
}
#[derive(Eq, PartialEq)]
pub struct ParsingError<I> {
input: I,
error: Cow<'static, str>,
pub input: I,
pub error: Cow<'static, str>,
}
impl core::fmt::Debug for ParsingError<&'_ [u8]> {
@ -269,13 +269,6 @@ pub fn mail(input: &[u8]) -> Result<(Vec<(&[u8], &[u8])>, &[u8])> {
pub mod generic {
use super::*;
pub fn angle_bracket_delimeted_list(input: &[u8]) -> IResult<&[u8], Vec<&[u8]>> {
separated_nonempty_list(is_a(","), delimited(tag("<"), take_until(">"), tag(">")))(
input.rtrim(),
)
// separated_nonempty_list!(complete!(is_a!(",")), ws!(complete!(complete!(delimited!(tag!("<"), take_until1!(">"), tag!(">")))))));
}
pub fn date(input: &[u8]) -> Result<crate::datetime::UnixTimestamp> {
let (_, mut parsed_result) = encodings::phrase(&eat_comments(input), false)?;
if let Some(pos) = parsed_result.find(b"-0000") {
@ -839,6 +832,69 @@ pub mod generic {
}
}
pub mod mailing_lists {
//! Mailing lists headers.
//!
//! Implemented RFCs:
//!
//! - [RFC2369 "The Use of URLs as Meta-Syntax for Core Mail List Commands and their Transport through Message Header Fields"](https://tools.ietf.org/html/rfc2369)
use super::*;
use generic::cfws;
///Parse the value of headers defined in RFC2369 "The Use of URLs as Meta-Syntax for Core
///Mail List Commands and their Transport through Message Header Fields"
pub fn rfc_2369_list_headers_action_list(input: &[u8]) -> IResult<&[u8], Vec<&[u8]>> {
let (input, _) = opt(cfws)(input)?;
let (input, ret) = alt((
separated_nonempty_list(
delimited(
map(opt(cfws), |_| ()),
map(is_a(", "), |_| ()),
map(opt(cfws), |_| ()),
),
delimited(tag("<"), take_until(">"), tag(">")),
),
map(delimited(tag("<"), take_until(">"), tag(">")), |el| {
vec![el]
}),
map(
delimited(
map(opt(cfws), |_| ()),
map(tag("NO"), |_| ()),
map(opt(cfws), |_| ()),
),
|_| vec![],
),
))(input)?;
let (input, _) = opt(cfws)(input)?;
Ok((input, ret))
}
#[test]
fn test_parser_rfc_2369_list() {
let s = r#"List-Help: <mailto:list@host.com?subject=help> (List Instructions)
List-Help: <mailto:list-manager@host.com?body=info>
List-Help: <mailto:list-info@host.com> (Info about the list)
List-Help: <http://www.host.com/list/>, <mailto:list-info@host.com>
List-Help: <ftp://ftp.host.com/list.txt> (FTP),
<mailto:list@host.com?subject=help>
List-Post: <mailto:list@host.com>
List-Post: <mailto:moderator@host.com> (Postings are Moderated)
List-Post: <mailto:moderator@host.com?subject=list%20posting>
List-Post: NO (posting not allowed on this list)
List-Archive: <mailto:archive@host.com?subject=index%20list>
List-Archive: <ftp://ftp.host.com/pub/list/archive/>
List-Archive: <http://www.host.com/list/archive/> (Web Archive)
"#;
let (rest, headers) = headers::headers(s.as_bytes()).unwrap();
assert!(rest.is_empty());
for (h, v) in headers {
let (rest, action_list) = rfc_2369_list_headers_action_list(v).unwrap();
assert!(rest.is_empty());
}
}
}
pub mod headers {
use super::*;