compiling part/part.rs

This commit is contained in:
Quentin 2023-07-23 12:24:46 +02:00
parent 18b081cb9a
commit d0f346c92f
Signed by: quentin
GPG key ID: E9602264D639FF68
10 changed files with 88 additions and 57 deletions

View file

@ -28,8 +28,8 @@ impl<'a, T> CompFieldList<'a, T> {
} }
} }
pub fn header<'a, T>(fx: impl Fn(&'a [u8]) -> IResult<&[u8], T> + Copy) pub fn header<'a, T>(fx: impl Fn(&'a [u8]) -> IResult<&'a [u8], T> + Copy)
-> impl Fn(&'a [u8]) -> IResult<&[u8], CompFieldList<T>> -> impl Fn(&'a [u8]) -> IResult<&'a [u8], CompFieldList<T>>
{ {
move |input| map(terminated(many0(alt(( move |input| map(terminated(many0(alt((
map(fx, CompField::Known), map(fx, CompField::Known),

View file

@ -3,3 +3,4 @@ pub mod text;
pub mod header; pub mod header;
pub mod rfc5322; pub mod rfc5322;
pub mod mime; pub mod mime;
pub mod part;

View file

@ -7,7 +7,7 @@ use encoding_rs::Encoding;
/// using encoding_rs datastructures directly would lead to a loss of information. /// using encoding_rs datastructures directly would lead to a loss of information.
/// https://www.iana.org/assignments/character-sets/character-sets.xhtml /// https://www.iana.org/assignments/character-sets/character-sets.xhtml
#[allow(non_camel_case_types)] #[allow(non_camel_case_types)]
#[derive(Debug, PartialEq, Default)] #[derive(Debug, PartialEq, Default, Clone)]
pub enum EmailCharset { pub enum EmailCharset {
#[default] #[default]
US_ASCII, US_ASCII,

View file

@ -3,12 +3,20 @@ use crate::rfc5322::identification::MessageID;
use crate::text::misc_token::Unstructured; use crate::text::misc_token::Unstructured;
use crate::mime::field::Content; use crate::mime::field::Content;
use crate::mime::r#type::{AnyType, self as ctype}; //Multipart, Message, Text, Binary}; use crate::mime::r#type::{AnyType, self as ctype}; //Multipart, Message, Text, Binary};
//
#[derive(Debug, PartialEq, Clone)]
pub struct Multipart<'a>(pub ctype::Multipart, Generic<'a>); pub struct Multipart<'a>(pub ctype::Multipart, Generic<'a>);
#[derive(Debug, PartialEq, Clone)]
pub struct Message<'a>(pub ctype::Message, Generic<'a>); pub struct Message<'a>(pub ctype::Message, Generic<'a>);
#[derive(Debug, PartialEq, Clone, Default)]
pub struct Text<'a>(pub ctype::Text, Generic<'a>); pub struct Text<'a>(pub ctype::Text, Generic<'a>);
#[derive(Debug, PartialEq, Clone)]
pub struct Binary<'a>(pub ctype::Binary, Generic<'a>); pub struct Binary<'a>(pub ctype::Binary, Generic<'a>);
#[derive(Debug, PartialEq, Clone)]
pub enum AnyMIME<'a> { pub enum AnyMIME<'a> {
Mult(Multipart<'a>), Mult(Multipart<'a>),
Msg(Message<'a>), Msg(Message<'a>),
@ -47,7 +55,7 @@ impl<'a> FromIterator<Content<'a>> for AnyMIME<'a> {
} }
} }
#[derive(Debug, PartialEq, Default)] #[derive(Debug, PartialEq, Default, Clone)]
pub struct Generic<'a> { pub struct Generic<'a> {
pub transfer_encoding: Mechanism<'a>, pub transfer_encoding: Mechanism<'a>,
pub id: Option<MessageID<'a>>, pub id: Option<MessageID<'a>>,

View file

@ -68,7 +68,7 @@ impl<'a> From<&'a NaiveType<'a>> for AnyType {
} }
} }
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Clone)]
pub struct Multipart { pub struct Multipart {
pub subtype: MultipartSubtype, pub subtype: MultipartSubtype,
pub boundary: String, pub boundary: String,
@ -87,7 +87,7 @@ impl<'a> TryFrom<&'a NaiveType<'a>> for Multipart {
} }
} }
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Clone)]
pub enum MultipartSubtype { pub enum MultipartSubtype {
Alternative, Alternative,
Mixed, Mixed,
@ -109,7 +109,7 @@ impl<'a> From<&NaiveType<'a>> for MultipartSubtype {
} }
} }
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Clone)]
pub enum Message { pub enum Message {
RFC822, RFC822,
Partial, Partial,
@ -127,7 +127,7 @@ impl<'a> From<&NaiveType<'a>> for Message {
} }
} }
#[derive(Debug, PartialEq, Default)] #[derive(Debug, PartialEq, Default, Clone)]
pub struct Text { pub struct Text {
pub subtype: TextSubtype, pub subtype: TextSubtype,
pub charset: EmailCharset, pub charset: EmailCharset,
@ -144,7 +144,7 @@ impl<'a> From<&NaiveType<'a>> for Text {
} }
} }
#[derive(Debug, PartialEq, Default)] #[derive(Debug, PartialEq, Default, Clone)]
pub enum TextSubtype { pub enum TextSubtype {
#[default] #[default]
Plain, Plain,
@ -161,7 +161,7 @@ impl<'a> From<&NaiveType<'a>> for TextSubtype {
} }
} }
#[derive(Debug, PartialEq, Default)] #[derive(Debug, PartialEq, Default, Clone)]
pub struct Binary {} pub struct Binary {}
#[cfg(test)] #[cfg(test)]

View file

@ -1,22 +1,26 @@
use nom::{ use nom::{
IResult, IResult,
branch::alt, branch::alt,
bytes::complete::{is_not, tag}, bytes::complete::{is_not},
multi::many0, multi::many0,
sequence::{pair, preceded, tuple}, sequence::{pair},
combinator::{not, opt, recognize}, combinator::{map, not, recognize},
}; };
use crate::mime::r#type as ctype; use crate::mime;
use crate::mime::mime; use crate::mime::mime::{AnyMIME};
use crate::rfc5322::{self as imf}; use crate::rfc5322::{self as imf};
use crate::text::boundary::{Delimiter, boundary};
use crate::text::whitespace::obs_crlf;
use crate::text::ascii::CRLF;
use crate::header::{header, CompFieldList};
pub struct Multipart(pub mime::Multipart, pub Vec<Part<'a>>); pub struct Multipart<'a>(pub mime::mime::Multipart<'a>, pub Vec<AnyPart<'a>>);
pub struct Message(pub mime::Message, pub imf::message::Message, pub Part<'a>); pub struct Message<'a>(pub mime::mime::Message<'a>, pub imf::message::Message<'a>, pub Box<AnyPart<'a>>);
pub struct Text(pub mime::Text, pub &'a [u8]); pub struct Text<'a>(pub mime::mime::Text<'a>, pub &'a [u8]);
pub struct Binary(pub mime::Binary, pub &'a [u8]); pub struct Binary<'a>(pub mime::mime::Binary<'a>, pub &'a [u8]);
pub struct AnyPart<'a> { pub enum AnyPart<'a> {
Mult(Multipart<'a>), Mult(Multipart<'a>),
Msg(Message<'a>), Msg(Message<'a>),
Txt(Text<'a>), Txt(Text<'a>),
@ -24,89 +28,107 @@ pub struct AnyPart<'a> {
} }
pub enum MixedField<'a> { pub enum MixedField<'a> {
MIME(mime::fields::Content<'a>), MIME(mime::field::Content<'a>),
IMF(rfc5322::fields::Field<'a>), IMF(imf::field::Field<'a>),
} }
impl<'a> MixedField<'a> { impl<'a> MixedField<'a> {
pub fn mime(&self) -> Option<&mime::fields::Content<'a>> { pub fn mime(&self) -> Option<&mime::field::Content<'a>> {
match self { match self {
MIME(v) => Some(v), Self::MIME(v) => Some(v),
_ => None, _ => None,
} }
} }
pub fn imf(&self) -> Option<&rfc5322::fields::Field<'a>> { pub fn to_mime(self) -> Option<mime::field::Content<'a>> {
match self { match self {
IMF(v) => Some(v), Self::MIME(v) => Some(v),
_ => None,
}
}
pub fn imf(&self) -> Option<&imf::field::Field<'a>> {
match self {
Self::IMF(v) => Some(v),
_ => None,
}
}
pub fn to_imf(self) -> Option<imf::field::Field<'a>> {
match self {
Self::IMF(v) => Some(v),
_ => None, _ => None,
} }
} }
} }
impl<'a, MixedField> CompFieldList<'a, MixedField> { impl<'a> CompFieldList<'a, MixedField<'a>> {
pub fn sections(self) -> (mime::mime::AnyMIME<'a>, imf::message::Message<'a>) { pub fn sections(self) -> (mime::mime::AnyMIME<'a>, imf::message::Message<'a>) {
let k = self.known(); let k = self.known();
let mime = k.iter().map(MixedField::mime).flatten().collect::<mime::mime::AnyMIME>(); let (v1, v2): (Vec<MixedField>, Vec<MixedField>) = k.into_iter().partition(|v| v.mime().is_some());
let imf = k.iter().map(MixedField::imf).flatten().collect::<imf::message::Message>(); let mime = v1.into_iter().map(|v| v.to_mime()).flatten().collect::<mime::mime::AnyMIME>();
let imf = v2.into_iter().map(|v| v.to_imf()).flatten().collect::<imf::message::Message>();
(mime, imf) (mime, imf)
} }
} }
pub fn mixed_field(input: &[u8]) -> IResult<&[u8], MixedField> { pub fn mixed_field(input: &[u8]) -> IResult<&[u8], MixedField> {
alt(( alt((
map(mime::fields::content, MixedField::MIME), map(mime::field::content, MixedField::MIME),
map(rfc5322::fields::field, MixedField::IMF), map(imf::field::field, MixedField::IMF),
))(input) ))(input)
} }
pub fn message<'a>(m: mime::Message<'a>) -> impl Fn(&'a [u8]) -> IResult<&'a [u8], Message<'a>> { pub fn message<'a>(m: mime::mime::Message<'a>) -> impl Fn(&'a [u8]) -> IResult<&'a [u8], Message<'a>> {
move |input: &[u8]| { move |input: &[u8]| {
let (input, fields) = header(mixed_field)(input)?; let (input, fields) = header(mixed_field)(input)?;
let (in_mime, imf) = fields.sections(); let (in_mime, imf) = fields.sections();
let part = to_anypart(in_mime, input); let part = to_anypart(in_mime, input);
Ok((&b[], Message(m, imf, part))) Ok((&[], Message(m.clone(), imf, Box::new(part))))
} }
} }
pub fn multipart<'a>(m: mime::Multipart<'a>) -> impl Fn(&'a [u8]) -> IResult<&'a [u8], Multipart<'a>> { pub fn multipart<'a>(m: mime::mime::Multipart<'a>) -> impl Fn(&'a [u8]) -> IResult<&'a [u8], Multipart<'a>> {
move |input: &[u8]| { let m = m.clone();
let (mut input_loop, _) = part_raw(m.ctype.boundary)(input)?;
move |input| {
let bound = m.0.boundary.as_bytes();
let (mut input_loop, _) = part_raw(bound)(input)?;
let mut mparts: Vec<AnyPart> = vec![]; let mut mparts: Vec<AnyPart> = vec![];
loop { loop {
let input = match boundary(m.ctype.boundary)(input_loop) { let input = match boundary(bound)(input_loop) {
Err(_) => return Ok((input_loop, Multipart(m, mparts))), Err(_) => return Ok((input_loop, Multipart(m.clone(), mparts))),
Ok((inp, Delimiter::Last)) => return Ok((inp, Multipart(m, mparts))), Ok((inp, Delimiter::Last)) => return Ok((inp, Multipart(m.clone(), mparts))),
Ok((inp, Delimiter::Next)) => inp, Ok((inp, Delimiter::Next)) => inp,
}; };
// parse mime headers // parse mime headers
let (input, fields) = header(content)(input)?; let (input, fields) = header(mime::field::content)(input)?;
let mime = fields.to_mime(); let mime = fields.to_mime();
// parse raw part // parse raw part
let (input, rpart) = part_raw(ctype.boundary.as_bytes())(input)?; let (input, rpart) = part_raw(bound)(input)?;
// parse mime body // parse mime body
mparts.push(to_anypart(mime, rpart); mparts.push(to_anypart(mime, rpart));
input_loop = input; input_loop = input;
} }
} }
} }
pub fn to_anypart(m: AnyMIME<'a>, rpart: &[u8]) -> AnyPart<'a> { pub fn to_anypart<'a>(m: AnyMIME<'a>, rpart: &'a [u8]) -> AnyPart<'a> {
match mime { match m {
AnyMIME::Mult(a) => map(multipart(a), AnyPart::Mult)(rpart) AnyMIME::Mult(a) => map(multipart(a), AnyPart::Mult)(rpart)
.unwrap_or(AnyPart::Text(Text::default(), rpart)), .map(|v| v.1)
.unwrap_or(AnyPart::Txt(Text(mime::mime::Text::default(), rpart))),
AnyMIME::Msg(a) => map(message(a), AnyPart::Msg)(rpart) AnyMIME::Msg(a) => map(message(a), AnyPart::Msg)(rpart)
.unwrap_or(AnyPart::Text(Text::default(), rpart)), .map(|v| v.1)
.unwrap_or(AnyPart::Txt(Text(mime::mime::Text::default(), rpart))),
AnyMIME::Txt(a) => AnyPart::Txt(Text(a, rpart)), AnyMIME::Txt(a) => AnyPart::Txt(Text(a, rpart)),
AnyMIME::Bin(a) => AnyPart::Bin(Binary(a, rpart)), AnyMIME::Bin(a) => AnyPart::Bin(Binary(a, rpart)),
} }
} }
pub fn part_raw<'a>(bound: &'a [u8]) -> impl Fn(&'a [u8]) -> IResult<&'a [u8], &'a [u8]> { pub fn part_raw<'a>(bound: &[u8]) -> impl Fn(&'a [u8]) -> IResult<&'a [u8], &'a [u8]> + '_ {
move |input: &[u8]| { move |input| {
recognize(many0(pair( recognize(many0(pair(
not(boundary(bound)), not(boundary(bound)),
alt((is_not(CRLF), obs_crlf)), alt((is_not(CRLF), obs_crlf)),

View file

@ -12,7 +12,7 @@ use crate::text::whitespace::cfws;
use crate::text::words::dot_atom_text; use crate::text::words::dot_atom_text;
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Clone)]
pub struct MessageID<'a> { pub struct MessageID<'a> {
pub left: &'a [u8], pub left: &'a [u8],
pub right: &'a [u8], pub right: &'a [u8],

View file

@ -13,7 +13,7 @@ pub enum Delimiter {
Last Last
} }
pub fn boundary<'a>(boundary: &'a [u8]) -> impl Fn(&'a [u8]) -> IResult<&'a [u8], Delimiter> { pub fn boundary<'a>(boundary: &[u8]) -> impl Fn(&'a [u8]) -> IResult<&'a [u8], Delimiter> + '_ {
move |input: &[u8]| { move |input: &[u8]| {
let (rest, (_, _, _, last, _)) = tuple((opt(obs_crlf), tag(b"--"), tag(boundary), opt(tag(b"--")), opt(obs_crlf)))(input)?; let (rest, (_, _, _, last, _)) = tuple((opt(obs_crlf), tag(b"--"), tag(boundary), opt(tag(b"--")), opt(obs_crlf)))(input)?;
match last { match last {

View file

@ -43,7 +43,7 @@ pub fn encoded_word_base64(input: &[u8]) -> IResult<&[u8], EncodedWord> {
Ok((rest, parsed)) Ok((rest, parsed))
} }
#[derive(PartialEq,Debug)] #[derive(PartialEq,Debug, Clone)]
pub enum EncodedWord<'a> { pub enum EncodedWord<'a> {
Quoted(QuotedWord<'a>), Quoted(QuotedWord<'a>),
Base64(Base64Word<'a>), Base64(Base64Word<'a>),
@ -57,7 +57,7 @@ impl<'a> EncodedWord<'a> {
} }
} }
#[derive(PartialEq,Debug)] #[derive(PartialEq,Debug,Clone)]
pub struct Base64Word<'a> { pub struct Base64Word<'a> {
pub enc: &'static Encoding, pub enc: &'static Encoding,
pub content: &'a [u8], pub content: &'a [u8],
@ -72,7 +72,7 @@ impl<'a> Base64Word<'a> {
} }
} }
#[derive(PartialEq,Debug)] #[derive(PartialEq,Debug,Clone)]
pub struct QuotedWord<'a> { pub struct QuotedWord<'a> {
pub enc: &'static Encoding, pub enc: &'static Encoding,
pub chunks: Vec<QuotedChunk<'a>>, pub chunks: Vec<QuotedChunk<'a>>,
@ -100,7 +100,7 @@ impl<'a> QuotedWord<'a> {
} }
} }
#[derive(PartialEq,Debug)] #[derive(PartialEq,Debug,Clone)]
pub enum QuotedChunk<'a> { pub enum QuotedChunk<'a> {
Safe(&'a [u8]), Safe(&'a [u8]),
Encoded(u8), Encoded(u8),

View file

@ -105,7 +105,7 @@ fn is_unstructured(c: u8) -> bool {
is_vchar(c) || is_obs_no_ws_ctl(c) || c == ascii::NULL is_vchar(c) || is_obs_no_ws_ctl(c) || c == ascii::NULL
} }
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Clone)]
pub enum UnstrToken<'a> { pub enum UnstrToken<'a> {
Init, Init,
Encoded(encoding::EncodedWord<'a>), Encoded(encoding::EncodedWord<'a>),
@ -122,7 +122,7 @@ impl<'a> UnstrToken<'a> {
} }
} }
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Clone)]
pub struct Unstructured<'a>(pub Vec<UnstrToken<'a>>); pub struct Unstructured<'a>(pub Vec<UnstrToken<'a>>);
impl<'a> Unstructured<'a> { impl<'a> Unstructured<'a> {