diff --git a/src/main.rs b/src/main.rs index c8e3a88..a6a7b5f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,11 @@ +mod parsing; + +use parsing::ParsedMailEx; + use anyhow::anyhow; use clap::Clap; - use mailparse::MailHeaderMap; + use std::io::Read; /// MIME mail viewer. @@ -38,9 +42,10 @@ fn main() -> Result<(), anyhow::Error> { dbg!(&date_str); let date = chrono::DateTime::parse_from_rfc2822(&date_str)?; println!("Date: {}", date); - // dbg!(&message); + dbg!(&message.ctype); + dbg!(&message.get_content_disposition()); // - let body = message.get_body()?; + let body = message.get_body_text()?.unwrap_or_default(); println!("---"); print!("{}", body); Ok(()) diff --git a/src/parsing.rs b/src/parsing.rs new file mode 100644 index 0000000..052a418 --- /dev/null +++ b/src/parsing.rs @@ -0,0 +1,90 @@ +use mailparse::{DispositionType, MailParseError, ParsedMail}; + +pub trait ParsedMailEx { + fn is_attachment(&self) -> bool; + + fn get_body_text(&self) -> Result, MailParseError>; +} + +impl<'a> ParsedMailEx for ParsedMail<'a> { + fn is_attachment(&self) -> bool { + self.get_content_disposition().disposition == DispositionType::Attachment + } + + fn get_body_text(&self) -> Result, MailParseError> { + if self.is_attachment() { + return Ok(None); + } + if self.ctype.mimetype == "text/plain" { + return Ok(Some(self.get_body()?)); + } + for subpart in &self.subparts { + if let Some(body) = subpart.get_body_text()? { + return Ok(Some(body)); + } + } + Ok(None) + } +} + +#[cfg(test)] +mod test { + use super::*; + + const TEXT_PLAIN: &str = r#"From: Andrey Golovizin +To: ag@sologoc.com +Subject: Plain text body +Date: Sat, 17 Jul 2021 18:04:10 +0200 +Message-ID: <2148453.iZASKD2KPV@sakuragaoka> +MIME-Version: 1.0 +Content-Transfer-Encoding: quoted-printable +Content-Type: text/plain; charset="iso-8859-1" + +Prost=FD text. +"#; + + const MULTIPART_ALTERNATIVE: &str = r#"From: Andrey Golovizin +Subject: Plain text with html +Date: Sat, 17 Jul 2021 18:13:26 +0200 +Message-ID: <3609140.kQq0lBPeGt@sakuragaoka> +MIME-Version: 1.0 +Content-Type: multipart/alternative; boundary="nextPart3377186.iIbC2pHGDl" +Content-Transfer-Encoding: 7Bit + +This is a multi-part message in MIME format. + +--nextPart3377186.iIbC2pHGDl +Content-Transfer-Encoding: quoted-printable +Content-Type: text/plain; charset="iso-8859-1" + +Prost=FD text. +--nextPart3377186.iIbC2pHGDl +Content-Transfer-Encoding: quoted-printable +Content-Type: text/html; charset="UTF-8" + + + + + +

N=C4=9Bjaky HTML text.

+ + +--nextPart3377186.iIbC2pHGDl-- + + "#; + + #[test] + fn test_plain_text_body() -> Result<(), anyhow::Error> { + let message = mailparse::parse_mail(TEXT_PLAIN.as_bytes())?; + assert_eq!(message.get_body_text()?.unwrap_or_default(), "Prostý text."); + Ok(()) + } + + #[test] + fn test_multipart_alternative_text_body() -> Result<(), anyhow::Error> { + let message = mailparse::parse_mail(MULTIPART_ALTERNATIVE.as_bytes())?; + assert_eq!(message.get_body_text()?.unwrap_or_default(), "Prostý text."); + Ok(()) + } +}