mirror of
https://gitlab.com/mailcat-devs/mailcat.git
synced 2025-12-20 13:23:26 +01:00
Extract attachments
This commit is contained in:
parent
b1e9a349c8
commit
424ce25b91
2 changed files with 59 additions and 24 deletions
10
src/main.rs
10
src/main.rs
|
|
@ -29,6 +29,14 @@ fn main() -> Result<(), anyhow::Error> {
|
||||||
println!("Date: {}", date);
|
println!("Date: {}", date);
|
||||||
}
|
}
|
||||||
println!("---");
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,33 +1,53 @@
|
||||||
use mailparse::{DispositionType, MailHeaderMap, ParsedMail};
|
use mailparse::{DispositionType, MailHeaderMap, ParsedMail};
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Debug, Default)]
|
#[derive(PartialEq, Eq, Debug, Default)]
|
||||||
pub struct Body {
|
pub struct Parts {
|
||||||
pub text: Option<String>,
|
pub text: Option<String>,
|
||||||
pub html: 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 {
|
pub trait ParsedMailExt {
|
||||||
fn is_attachment(&self) -> bool;
|
fn is_attachment(&self) -> bool;
|
||||||
fn subject(&self) -> Option<String>;
|
fn subject(&self) -> Option<String>;
|
||||||
fn date(&self) -> Result<Option<chrono::DateTime<chrono::FixedOffset>>, anyhow::Error>;
|
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.is_attachment() {
|
if !message.subparts.is_empty() {
|
||||||
return Ok(());
|
for subpart in &message.subparts {
|
||||||
}
|
find_body(subpart, parts)?;
|
||||||
let mimetype: mime::Mime = message.ctype.mimetype.parse()?;
|
}
|
||||||
if mimetype == mime::TEXT_PLAIN {
|
} else {
|
||||||
body.text.get_or_insert(message.get_body()?);
|
if message.is_attachment() {
|
||||||
} else if mimetype == mime::TEXT_HTML {
|
parts.attachments.push(Attachment::from_part(message)?);
|
||||||
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(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
let mimetype: mime::Mime = message.ctype.mimetype.parse()?;
|
||||||
|
if mimetype == mime::TEXT_PLAIN {
|
||||||
|
parts.text.get_or_insert(message.get_body()?);
|
||||||
|
} else if mimetype == mime::TEXT_HTML {
|
||||||
|
parts.html.get_or_insert(message.get_body()?);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
@ -49,8 +69,8 @@ impl<'a> ParsedMailExt for ParsedMail<'a> {
|
||||||
Ok(date)
|
Ok(date)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn body(&self) -> Result<Body, anyhow::Error> {
|
fn parts(&self) -> Result<Parts, anyhow::Error> {
|
||||||
let mut body = Body::default();
|
let mut body = Parts::default();
|
||||||
find_body(self, &mut body)?;
|
find_body(self, &mut body)?;
|
||||||
Ok(body)
|
Ok(body)
|
||||||
}
|
}
|
||||||
|
|
@ -152,10 +172,11 @@ TsSbamFrw6EgcMWZw61sb2hhLgo=
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
message.body()?,
|
message.parts()?,
|
||||||
Body {
|
Parts {
|
||||||
text: Some("Prostý text.".to_string()),
|
text: Some("Prostý text.".to_string()),
|
||||||
html: None,
|
html: None,
|
||||||
|
..Default::default()
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
@ -174,10 +195,11 @@ TsSbamFrw6EgcMWZw61sb2hhLgo=
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
message.body()?,
|
message.parts()?,
|
||||||
Body {
|
Parts {
|
||||||
text: Some("Prostý text.".to_string()),
|
text: Some("Prostý text.".to_string()),
|
||||||
html: Some("<strong>Tučný HTML text</strong>".to_string()),
|
html: Some("<strong>Tučný HTML text</strong>".to_string()),
|
||||||
|
..Default::default()
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
@ -199,10 +221,15 @@ TsSbamFrw6EgcMWZw61sb2hhLgo=
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
message.body()?,
|
message.parts()?,
|
||||||
Body {
|
Parts {
|
||||||
text: Some("Prostý text.".to_string()),
|
text: Some("Prostý text.".to_string()),
|
||||||
html: Some("<strong>Tučný HTML text</strong>".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(())
|
Ok(())
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue