compiling part/part.rs
This commit is contained in:
parent
18b081cb9a
commit
d0f346c92f
10 changed files with 88 additions and 57 deletions
|
@ -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),
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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>>,
|
||||||
|
|
|
@ -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)]
|
||||||
|
|
|
@ -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)),
|
||||||
|
|
|
@ -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],
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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),
|
||||||
|
|
|
@ -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> {
|
||||||
|
|
Loading…
Reference in a new issue