generic header support
This commit is contained in:
parent
4baa976283
commit
59f550b439
4 changed files with 75 additions and 26 deletions
|
@ -1,9 +1,13 @@
|
|||
use nom::{
|
||||
IResult,
|
||||
branch::alt,
|
||||
bytes::complete::{tag_no_case, tag, take_while1},
|
||||
character::complete::space0,
|
||||
combinator::map,
|
||||
multi::many0,
|
||||
sequence::{pair, terminated, tuple},
|
||||
};
|
||||
use crate::text::whitespace::{foldable_line, obs_crlf};
|
||||
use crate::text::misc_token::{Unstructured, unstructured};
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
|
@ -24,6 +28,16 @@ impl<'a, T> CompFieldList<'a, T> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn header<'a, T>(fx: impl Fn(&'a [u8]) -> IResult<&[u8], T> + Copy)
|
||||
-> impl Fn(&'a [u8]) -> IResult<&[u8], CompFieldList<T>>
|
||||
{
|
||||
move |input| map(terminated(many0(alt((
|
||||
map(fx, CompField::Known),
|
||||
map(opt_field, |(k,v)| CompField::Unknown(k,v)),
|
||||
map(foldable_line, CompField::Bad),
|
||||
))), obs_crlf), CompFieldList)(input)
|
||||
}
|
||||
|
||||
pub fn field_name<'a>(name: &'static [u8]) -> impl Fn(&'a [u8]) -> IResult<&'a [u8], &'a [u8]> {
|
||||
move |input| {
|
||||
terminated(
|
||||
|
@ -43,13 +57,14 @@ pub fn field_name<'a>(name: &'static [u8]) -> impl Fn(&'a [u8]) -> IResult<&'a [
|
|||
/// ; ":".
|
||||
/// ```
|
||||
pub fn opt_field(input: &[u8]) -> IResult<&[u8], (&[u8], Unstructured)> {
|
||||
pair(
|
||||
terminated(
|
||||
take_while1(|c| c >= 0x21 && c <= 0x7E && c != 0x3A),
|
||||
tuple((space0, tag(b":"), space0)),
|
||||
),
|
||||
unstructured,
|
||||
)(input)
|
||||
terminated(
|
||||
pair(
|
||||
terminated(
|
||||
take_while1(|c| c >= 0x21 && c <= 0x7E && c != 0x3A),
|
||||
tuple((space0, tag(b":"), space0)),
|
||||
),
|
||||
unstructured,
|
||||
), obs_crlf)(input)
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -8,9 +8,10 @@ use nom::{
|
|||
use crate::text::whitespace::obs_crlf;
|
||||
use crate::text::misc_token::{Unstructured, unstructured};
|
||||
use crate::rfc5322::identification::{MessageID, msg_id};
|
||||
use crate::header::field_name;
|
||||
use crate::header::{field_name, header, CompFieldList};
|
||||
use crate::mime::r#type::{NaiveType, naive_type};
|
||||
use crate::mime::mechanism::{Mechanism, mechanism};
|
||||
//use crate::mime::mime::MIME;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Content<'a> {
|
||||
|
@ -19,8 +20,11 @@ pub enum Content<'a> {
|
|||
ID(MessageID<'a>),
|
||||
Description(Unstructured<'a>),
|
||||
}
|
||||
/*impl<'a> CompFieldList<Content<'a>> {
|
||||
pub fn to_mime(&self) -> MIME { self.into() }
|
||||
}*/
|
||||
|
||||
fn field(input: &[u8]) -> IResult<&[u8], Content> {
|
||||
fn content(input: &[u8]) -> IResult<&[u8], Content> {
|
||||
terminated(alt((
|
||||
preceded(field_name(b"content-type"), map(naive_type, Content::Type)),
|
||||
preceded(field_name(b"content-transfer-encoding"), map(mechanism, Content::TransferEncoding)),
|
||||
|
@ -34,10 +38,12 @@ mod tests {
|
|||
use super::*;
|
||||
use crate::mime::r#type::*;
|
||||
use crate::mime::charset::EmailCharset;
|
||||
use crate::text::misc_token::MIMEWord;
|
||||
use crate::text::quoted::QuotedString;
|
||||
|
||||
#[test]
|
||||
fn test_content_type() {
|
||||
let (rest, content) = field(b"Content-Type: text/plain; charset=UTF-8; format=flowed\r\n").unwrap();
|
||||
let (rest, content) = content(b"Content-Type: text/plain; charset=UTF-8; format=flowed\r\n").unwrap();
|
||||
assert_eq!(&b""[..], rest);
|
||||
|
||||
if let Content::Type(nt) = content {
|
||||
|
@ -52,4 +58,41 @@ mod tests {
|
|||
panic!("Expected Content::Type, got {:?}", content);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_header() {
|
||||
let fullmail: &[u8] = r#"Date: Sat, 8 Jul 2023 07:14:29 +0200
|
||||
From: Grrrnd Zero <grrrndzero@example.org>
|
||||
To: John Doe <jdoe@machine.example>
|
||||
Subject: Re: Saying Hello
|
||||
Message-ID: <NTAxNzA2AC47634Y366BAMTY4ODc5MzQyODY0ODY5@www.grrrndzero.org>
|
||||
MIME-Version: 1.0
|
||||
Content-Type: multipart/alternative;
|
||||
boundary="b1_e376dc71bafc953c0b0fdeb9983a9956"
|
||||
Content-Transfer-Encoding: 7bit
|
||||
|
||||
This is a multipart message.
|
||||
|
||||
"#.as_bytes();
|
||||
|
||||
assert_eq!(
|
||||
map(header(content), CompFieldList::known)(fullmail),
|
||||
Ok((
|
||||
&b"This is a multipart message.\n\n"[..],
|
||||
vec![
|
||||
Content::Type(NaiveType {
|
||||
main: &b"multipart"[..],
|
||||
sub: &b"alternative"[..],
|
||||
params: vec![
|
||||
Parameter {
|
||||
name: &b"boundary"[..],
|
||||
value: MIMEWord::Quoted(QuotedString(vec![&b"b1_e376dc71bafc953c0b0fdeb9983a9956"[..]])),
|
||||
}
|
||||
]
|
||||
}),
|
||||
Content::TransferEncoding(Mechanism::_7Bit),
|
||||
],
|
||||
)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,9 +13,9 @@ use crate::text::words::{mime_atom};
|
|||
// --------- NAIVE TYPE
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct NaiveType<'a> {
|
||||
main: &'a [u8],
|
||||
sub: &'a [u8],
|
||||
params: Vec<Parameter<'a>>,
|
||||
pub main: &'a [u8],
|
||||
pub sub: &'a [u8],
|
||||
pub params: Vec<Parameter<'a>>,
|
||||
}
|
||||
impl<'a> NaiveType<'a> {
|
||||
pub fn to_type(&self) -> Type { self.into() }
|
||||
|
@ -29,8 +29,8 @@ pub fn naive_type(input: &[u8]) -> IResult<&[u8], NaiveType> {
|
|||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct Parameter<'a> {
|
||||
name: &'a [u8],
|
||||
value: MIMEWord<'a>,
|
||||
pub name: &'a [u8],
|
||||
pub value: MIMEWord<'a>,
|
||||
}
|
||||
pub fn parameter(input: &[u8]) -> IResult<&[u8], Parameter> {
|
||||
map(tuple((mime_atom, tag(b"="), mime_word)), |(name, _, value)| Parameter { name, value })(input)
|
||||
|
|
|
@ -15,7 +15,7 @@ use crate::rfc5322::identification::{MessageID, MessageIDList, msg_id, msg_list}
|
|||
use crate::rfc5322::trace::{ReceivedLog, return_path, received_log};
|
||||
use crate::rfc5322::mime::{Version, version};
|
||||
use crate::rfc5322::message::Message;
|
||||
use crate::header::*;
|
||||
use crate::header::{header, field_name, CompFieldList};
|
||||
use crate::text::misc_token::{Unstructured, PhraseList, unstructured, phrase_list};
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
|
@ -87,15 +87,6 @@ pub fn field(input: &[u8]) -> IResult<&[u8], Field> {
|
|||
)), obs_crlf)(input)
|
||||
}
|
||||
|
||||
pub fn header(input: &[u8]) -> IResult<&[u8], CompFieldList<Field>> {
|
||||
map(terminated(many0(alt((
|
||||
map(field, CompField::Known),
|
||||
map(opt_field, |(k,v)| CompField::Unknown(k,v)),
|
||||
map(foldable_line, CompField::Bad),
|
||||
))), obs_crlf), CompFieldList)(input)
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
@ -115,7 +106,7 @@ This is the plain text body of the message. Note the blank line
|
|||
between the header information and the body of the message.";
|
||||
|
||||
assert_eq!(
|
||||
map(header, |v| FieldList(v.known()).message())(fullmail),
|
||||
map(header(field), |v| FieldList(v.known()).message())(fullmail),
|
||||
Ok((
|
||||
&b"This is the plain text body of the message. Note the blank line\nbetween the header information and the body of the message."[..],
|
||||
Message {
|
||||
|
|
Loading…
Reference in a new issue