improve extract subparts fn

This commit is contained in:
Clément DOUIN 2021-01-08 00:29:53 +01:00
parent 01de392977
commit cb8610a35f
No known key found for this signature in database
GPG key ID: 69C9B9CFFDEE2DEF
4 changed files with 50 additions and 33 deletions

View file

@ -1,4 +1,5 @@
use imap;
use mailparse::{self, MailHeaderMap};
use rfc2047_decoder;
use crate::table::{self, DisplayCell, DisplayRow, DisplayTable};
@ -195,3 +196,31 @@ impl<'a> DisplayTable<'a, Email<'a>> for Vec<Email<'a>> {
self
}
}
// Utils
fn extract_text_bodies_into(mime: &str, part: &mailparse::ParsedMail, parts: &mut Vec<String>) {
match part.subparts.len() {
0 => {
if part
.get_headers()
.get_first_value("content-type")
.and_then(|v| if v.starts_with(mime) { Some(()) } else { None })
.is_some()
{
parts.push(part.get_body().unwrap_or(String::new()))
}
}
_ => {
part.subparts
.iter()
.for_each(|part| extract_text_bodies_into(mime, part, parts));
}
}
}
pub fn extract_text_bodies(mime: &str, email: &mailparse::ParsedMail) -> String {
let mut parts = vec![];
extract_text_bodies_into(mime, email, &mut parts);
parts.join("\r\n")
}

View file

@ -1,10 +1,10 @@
use imap;
use mailparse::{self, MailHeaderMap};
use mailparse;
use native_tls::{self, TlsConnector, TlsStream};
use std::{fmt, net::TcpStream, result};
use crate::config;
use crate::email::Email;
use crate::email::{self, Email};
use crate::mailbox::Mailbox;
// Error wrapper
@ -13,6 +13,7 @@ use crate::mailbox::Mailbox;
pub enum Error {
CreateTlsConnectorError(native_tls::Error),
CreateImapSession(imap::Error),
ParseEmailError(mailparse::MailParseError),
ReadEmailNotFoundError(String),
ReadEmailEmptyPartError(String, String),
}
@ -23,6 +24,7 @@ impl fmt::Display for Error {
match self {
Error::CreateTlsConnectorError(err) => err.fmt(f),
Error::CreateImapSession(err) => err.fmt(f),
Error::ParseEmailError(err) => err.fmt(f),
Error::ReadEmailNotFoundError(uid) => {
write!(f, "no email found for uid {}", uid)
}
@ -45,6 +47,12 @@ impl From<imap::Error> for Error {
}
}
impl From<mailparse::MailParseError> for Error {
fn from(err: mailparse::MailParseError) -> Error {
Error::ParseEmailError(err)
}
}
// Result wrapper
type Result<T> = result::Result<T, Error>;
@ -58,27 +66,6 @@ pub struct ImapConnector {
}
impl ImapConnector {
fn extract_subparts_by_mime(mime: &str, part: &mailparse::ParsedMail, parts: &mut Vec<String>) {
match part.subparts.len() {
0 => {
if part
.get_headers()
.get_first_value("content-type")
.and_then(|v| if v.starts_with(mime) { Some(()) } else { None })
.is_some()
{
// TODO: push part instead of body str
parts.push(part.get_body().unwrap_or(String::new()))
}
}
_ => {
part.subparts
.iter()
.for_each(|p| Self::extract_subparts_by_mime(mime, p, parts));
}
}
}
pub fn new(config: config::ServerInfo) -> Result<Self> {
let tls = TlsConnector::new()?;
let client = imap::connect(config.get_addr(), &config.host, &tls)?;
@ -123,23 +110,23 @@ impl ImapConnector {
Ok(emails)
}
pub fn read_email(&mut self, mbox: &str, uid: &str, mime: &str) -> Result<String> {
pub fn read_email_body(&mut self, mbox: &str, uid: &str, mime: &str) -> Result<String> {
self.sess.select(mbox)?;
match self.sess.uid_fetch(uid, "BODY[]")?.first() {
None => Err(Error::ReadEmailNotFoundError(uid.to_string())),
Some(fetch) => {
let email = mailparse::parse_mail(fetch.body().unwrap_or(&[])).unwrap();
let mut parts = vec![];
Self::extract_subparts_by_mime(mime, &email, &mut parts);
let bytes = fetch.body().unwrap_or(&[]);
let email = mailparse::parse_mail(bytes)?;
let bodies = email::extract_text_bodies(&mime, &email);
if parts.len() == 0 {
if bodies.is_empty() {
Err(Error::ReadEmailEmptyPartError(
uid.to_string(),
mime.to_string(),
))
} else {
Ok(parts.join("\r\n"))
Ok(bodies)
}
}
}

View file

@ -175,16 +175,18 @@ fn run() -> Result<()> {
let mbox = matches.value_of("mailbox").unwrap();
let uid = matches.value_of("uid").unwrap();
let mime = matches.value_of("mime-type").unwrap();
let email = ImapConnector::new(config.imap)?.read_email(&mbox, &uid, &mime)?;
let body = ImapConnector::new(config.imap)?.read_email_body(&mbox, &uid, &mime)?;
println!("{}", email);
println!("{}", body);
}
if let Some(_) = matches.subcommand_matches("write") {
let config = Config::new_from_file()?;
let draft = editor::open_with_new_template()?;
println!("Sending ...");
smtp::send(&config, draft.as_bytes());
println!("Done!");
}
Ok(())

View file

@ -45,9 +45,8 @@ pub fn send(config: &config::Config, bytes: &[u8]) {
.credentials(creds)
.build();
println!("Sending ...");
match mailer.send(&email) {
Ok(_) => println!("Email sent successfully!"),
Ok(_) => (),
Err(e) => panic!("Could not send email: {:?}", e),
}
}