support multipart/digest parsing
This commit is contained in:
parent
9ddd480b0f
commit
4a46faa670
6 changed files with 51 additions and 25 deletions
|
@ -37,7 +37,7 @@ This library does not aim at implementing a specific RFC, but to be a swiss-army
|
|||
- Maintainability - modifying the code does not create regression and is possible for someone exterior to the project.
|
||||
- Compatibility - always try to parse something, do not panic or return an error.
|
||||
- Exhaustivity - serve as a common project to encode knowledge about emails (existing mime types, existing headers, etc.).
|
||||
- Type safe - do not manipulate only strings/bytes but leverage Rust type system instead so you benefit of its safety checks at compile time.
|
||||
- Type safe - do not manipulate only strings/bytes but leverage Rust type system instead so you benefit from its safety checks at compile time.
|
||||
|
||||
[See more about this library goals in the doc/ folder](./doc/goals.md)
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ use nom::{
|
|||
use crate::header::{field_name, CompFieldList};
|
||||
use crate::imf::identification::{msg_id, MessageID};
|
||||
use crate::mime::mechanism::{mechanism, Mechanism};
|
||||
use crate::mime::AnyMIME;
|
||||
use crate::mime::{AnyMIMEWithDefault, WithDefaultType};
|
||||
use crate::mime::r#type::{naive_type, NaiveType};
|
||||
use crate::text::misc_token::{unstructured, Unstructured};
|
||||
use crate::text::whitespace::obs_crlf;
|
||||
|
@ -49,8 +49,8 @@ impl<'a> Content<'a> {
|
|||
}
|
||||
|
||||
impl<'a> CompFieldList<'a, Content<'a>> {
|
||||
pub fn to_mime(self) -> AnyMIME<'a> {
|
||||
self.known().into_iter().collect::<AnyMIME>()
|
||||
pub fn to_mime<T: WithDefaultType> (self) -> AnyMIMEWithDefault<'a, T> {
|
||||
self.known().into_iter().collect::<AnyMIMEWithDefault<T>>()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -10,6 +10,8 @@ pub mod mechanism;
|
|||
/// Content-Type representation
|
||||
pub mod r#type;
|
||||
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use crate::imf::identification::MessageID;
|
||||
use crate::mime::field::Content;
|
||||
use crate::mime::mechanism::Mechanism;
|
||||
|
@ -47,10 +49,43 @@ impl<'a> AnyMIME<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> FromIterator<Content<'a>> for AnyMIME<'a> {
|
||||
impl<'a, T: WithDefaultType> From<AnyMIMEWithDefault<'a, T>> for AnyMIME<'a> {
|
||||
fn from(a: AnyMIMEWithDefault<'a, T>) -> Self {
|
||||
a.0
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Default, Clone)]
|
||||
pub struct Generic<'a> {
|
||||
pub transfer_encoding: Mechanism<'a>,
|
||||
pub id: Option<MessageID<'a>>,
|
||||
pub description: Option<Unstructured<'a>>,
|
||||
}
|
||||
|
||||
pub trait WithDefaultType {
|
||||
fn default_type() -> AnyType;
|
||||
}
|
||||
|
||||
pub struct WithGenericDefault {}
|
||||
impl WithDefaultType for WithGenericDefault {
|
||||
fn default_type() -> AnyType {
|
||||
AnyType::Text(r#type::Text::default())
|
||||
}
|
||||
}
|
||||
pub struct WithDigestDefault {}
|
||||
impl WithDefaultType for WithDigestDefault {
|
||||
fn default_type() -> AnyType {
|
||||
AnyType::Message(r#type::Message::default())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct AnyMIMEWithDefault<'a, T: WithDefaultType>(pub AnyMIME<'a>, PhantomData<T>);
|
||||
|
||||
impl<'a, T: WithDefaultType> FromIterator<Content<'a>> for AnyMIMEWithDefault<'a, T> {
|
||||
fn from_iter<I: IntoIterator<Item = Content<'a>>>(it: I) -> Self {
|
||||
let (at, gen) = it.into_iter().fold(
|
||||
(AnyType::default(), Generic::default()),
|
||||
(T::default_type(), Generic::default()),
|
||||
|(mut at, mut section), field| {
|
||||
match field {
|
||||
Content::Type(v) => at = v.to_type(),
|
||||
|
@ -62,13 +97,6 @@ impl<'a> FromIterator<Content<'a>> for AnyMIME<'a> {
|
|||
},
|
||||
);
|
||||
|
||||
Self::from_pair(at, gen)
|
||||
Self(AnyMIME::from_pair(at, gen), PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Default, Clone)]
|
||||
pub struct Generic<'a> {
|
||||
pub transfer_encoding: Mechanism<'a>,
|
||||
pub id: Option<MessageID<'a>>,
|
||||
pub description: Option<Unstructured<'a>>,
|
||||
}
|
||||
|
|
|
@ -57,17 +57,12 @@ pub enum AnyType {
|
|||
Binary(Binary),
|
||||
}
|
||||
|
||||
impl Default for AnyType {
|
||||
fn default() -> Self {
|
||||
Self::Text(Text::default())
|
||||
}
|
||||
}
|
||||
impl<'a> From<&'a NaiveType<'a>> for AnyType {
|
||||
fn from(nt: &'a NaiveType<'a>) -> Self {
|
||||
match nt.main.to_ascii_lowercase().as_slice() {
|
||||
b"multipart" => Multipart::try_from(nt)
|
||||
.map(Self::Multipart)
|
||||
.unwrap_or(Self::default()),
|
||||
.unwrap_or(Self::Text(Text::default())),
|
||||
b"message" => Self::Message(Message::from(nt)),
|
||||
b"text" => Self::Text(Text::from(nt)),
|
||||
_ => Self::Binary(Binary::default()),
|
||||
|
|
|
@ -47,7 +47,10 @@ pub fn multipart<'a>(
|
|||
|
||||
// parse mime headers
|
||||
let (input, fields) = header(mime::field::content)(input)?;
|
||||
let mime = fields.to_mime();
|
||||
let mime = match m.0.subtype {
|
||||
mime::r#type::MultipartSubtype::Digest => fields.to_mime::<mime::WithDigestDefault>().into(),
|
||||
_ => fields.to_mime::<mime::WithGenericDefault>().into(),
|
||||
};
|
||||
|
||||
// parse raw part
|
||||
let (input, rpart) = part::part_raw(bound)(input)?;
|
||||
|
@ -75,7 +78,7 @@ pub fn message<'a>(
|
|||
move |input: &[u8]| {
|
||||
let (input, fields): (_, CompFieldList<part::field::MixedField>) =
|
||||
header(part::field::mixed_field)(input)?;
|
||||
let (in_mime, imf) = fields.sections();
|
||||
let (in_mime, imf) = fields.sections::<mime::WithGenericDefault>();
|
||||
|
||||
let part = part::to_anypart(in_mime, input);
|
||||
|
||||
|
|
|
@ -36,19 +36,19 @@ impl<'a> MixedField<'a> {
|
|||
}
|
||||
}
|
||||
impl<'a> CompFieldList<'a, MixedField<'a>> {
|
||||
pub fn sections(self) -> (mime::AnyMIME<'a>, imf::Imf<'a>) {
|
||||
pub fn sections<T: mime::WithDefaultType>(self) -> (mime::AnyMIME<'a>, imf::Imf<'a>) {
|
||||
let k = self.known();
|
||||
let (v1, v2): (Vec<MixedField>, Vec<MixedField>) =
|
||||
k.into_iter().partition(|v| v.mime().is_some());
|
||||
let mime = v1
|
||||
.into_iter()
|
||||
.filter_map(|v| v.to_mime())
|
||||
.collect::<mime::AnyMIME>();
|
||||
.collect::<mime::AnyMIMEWithDefault<T>>();
|
||||
let imf = v2
|
||||
.into_iter()
|
||||
.filter_map(|v| v.to_imf())
|
||||
.collect::<imf::Imf>();
|
||||
(mime, imf)
|
||||
(mime.into(), imf)
|
||||
}
|
||||
}
|
||||
pub fn mixed_field(input: &[u8]) -> IResult<&[u8], MixedField> {
|
||||
|
|
Loading…
Reference in a new issue