Extract attachments

This commit is contained in:
Andrey Golovizin 2021-07-26 20:52:55 +02:00
parent b1e9a349c8
commit 424ce25b91
2 changed files with 59 additions and 24 deletions

View file

@ -29,6 +29,14 @@ fn main() -> Result<(), anyhow::Error> {
println!("Date: {}", date);
}
println!("---");
print!("{}", message.body()?.text.unwrap_or_default());
let parts = message.parts()?;
println!("{}", parts.text.unwrap_or_default());
for (i, attachment) in parts.attachments.iter().enumerate() {
println!(
"[{}] {}",
i,
&attachment.filename.as_deref().unwrap_or("<unnamed>")
);
}
Ok(())
}

View file

@ -1,32 +1,52 @@
use mailparse::{DispositionType, MailHeaderMap, ParsedMail};
#[derive(PartialEq, Eq, Debug, Default)]
pub struct Body {
pub struct Parts {
pub text: Option<String>,
pub html: Option<String>,
pub attachments: Vec<Attachment>,
}
#[derive(PartialEq, Eq, Debug)]
pub struct Attachment {
pub filename: Option<String>,
pub ctype: mime::Mime,
pub body: Vec<u8>,
}
impl Attachment {
fn from_part(part: &ParsedMail) -> anyhow::Result<Self> {
let content_disposition = dbg!(part.get_content_disposition());
Ok(Self {
filename: content_disposition.params.get("filename").cloned(),
ctype: part.ctype.mimetype.parse()?,
body: part.get_body_raw()?,
})
}
}
pub trait ParsedMailExt {
fn is_attachment(&self) -> bool;
fn subject(&self) -> Option<String>;
fn date(&self) -> Result<Option<chrono::DateTime<chrono::FixedOffset>>, anyhow::Error>;
fn body(&self) -> Result<Body, anyhow::Error>;
fn parts(&self) -> Result<Parts, anyhow::Error>;
}
fn find_body(message: &ParsedMail, body: &mut Body) -> Result<(), anyhow::Error> {
fn find_body(message: &ParsedMail, parts: &mut Parts) -> Result<(), anyhow::Error> {
if !message.subparts.is_empty() {
for subpart in &message.subparts {
find_body(subpart, parts)?;
}
} else {
if message.is_attachment() {
parts.attachments.push(Attachment::from_part(message)?);
return Ok(());
}
let mimetype: mime::Mime = message.ctype.mimetype.parse()?;
if mimetype == mime::TEXT_PLAIN {
body.text.get_or_insert(message.get_body()?);
parts.text.get_or_insert(message.get_body()?);
} else if mimetype == mime::TEXT_HTML {
body.html.get_or_insert(message.get_body()?);
}
for subpart in &message.subparts {
find_body(subpart, body)?;
if body.text.is_some() && body.html.is_some() {
return Ok(());
parts.html.get_or_insert(message.get_body()?);
}
}
Ok(())
@ -49,8 +69,8 @@ impl<'a> ParsedMailExt for ParsedMail<'a> {
Ok(date)
}
fn body(&self) -> Result<Body, anyhow::Error> {
let mut body = Body::default();
fn parts(&self) -> Result<Parts, anyhow::Error> {
let mut body = Parts::default();
find_body(self, &mut body)?;
Ok(body)
}
@ -152,10 +172,11 @@ TsSbamFrw6EgcMWZw61sb2hhLgo=
)
);
assert_eq!(
message.body()?,
Body {
message.parts()?,
Parts {
text: Some("Prostý text.".to_string()),
html: None,
..Default::default()
},
);
Ok(())
@ -174,10 +195,11 @@ TsSbamFrw6EgcMWZw61sb2hhLgo=
)
);
assert_eq!(
message.body()?,
Body {
message.parts()?,
Parts {
text: Some("Prostý text.".to_string()),
html: Some("<strong>Tučný HTML text</strong>".to_string()),
..Default::default()
},
);
Ok(())
@ -199,10 +221,15 @@ TsSbamFrw6EgcMWZw61sb2hhLgo=
)
);
assert_eq!(
message.body()?,
Body {
message.parts()?,
Parts {
text: Some("Prostý text.".to_string()),
html: Some("<strong>Tučný HTML text</strong>".to_string()),
attachments: vec![Attachment {
filename: Some("test.txt".to_string()),
ctype: mime::TEXT_PLAIN,
body: "Nějaká příloha.\n".into(),
}]
},
);
Ok(())