mirror of
https://github.com/soywod/himalaya.git
synced 2024-07-08 18:45:13 +00:00
improve extract subparts fn
This commit is contained in:
parent
01de392977
commit
cb8610a35f
29
src/email.rs
29
src/email.rs
|
@ -1,4 +1,5 @@
|
||||||
use imap;
|
use imap;
|
||||||
|
use mailparse::{self, MailHeaderMap};
|
||||||
use rfc2047_decoder;
|
use rfc2047_decoder;
|
||||||
|
|
||||||
use crate::table::{self, DisplayCell, DisplayRow, DisplayTable};
|
use crate::table::{self, DisplayCell, DisplayRow, DisplayTable};
|
||||||
|
@ -195,3 +196,31 @@ impl<'a> DisplayTable<'a, Email<'a>> for Vec<Email<'a>> {
|
||||||
self
|
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")
|
||||||
|
}
|
||||||
|
|
45
src/imap.rs
45
src/imap.rs
|
@ -1,10 +1,10 @@
|
||||||
use imap;
|
use imap;
|
||||||
use mailparse::{self, MailHeaderMap};
|
use mailparse;
|
||||||
use native_tls::{self, TlsConnector, TlsStream};
|
use native_tls::{self, TlsConnector, TlsStream};
|
||||||
use std::{fmt, net::TcpStream, result};
|
use std::{fmt, net::TcpStream, result};
|
||||||
|
|
||||||
use crate::config;
|
use crate::config;
|
||||||
use crate::email::Email;
|
use crate::email::{self, Email};
|
||||||
use crate::mailbox::Mailbox;
|
use crate::mailbox::Mailbox;
|
||||||
|
|
||||||
// Error wrapper
|
// Error wrapper
|
||||||
|
@ -13,6 +13,7 @@ use crate::mailbox::Mailbox;
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
CreateTlsConnectorError(native_tls::Error),
|
CreateTlsConnectorError(native_tls::Error),
|
||||||
CreateImapSession(imap::Error),
|
CreateImapSession(imap::Error),
|
||||||
|
ParseEmailError(mailparse::MailParseError),
|
||||||
ReadEmailNotFoundError(String),
|
ReadEmailNotFoundError(String),
|
||||||
ReadEmailEmptyPartError(String, String),
|
ReadEmailEmptyPartError(String, String),
|
||||||
}
|
}
|
||||||
|
@ -23,6 +24,7 @@ impl fmt::Display for Error {
|
||||||
match self {
|
match self {
|
||||||
Error::CreateTlsConnectorError(err) => err.fmt(f),
|
Error::CreateTlsConnectorError(err) => err.fmt(f),
|
||||||
Error::CreateImapSession(err) => err.fmt(f),
|
Error::CreateImapSession(err) => err.fmt(f),
|
||||||
|
Error::ParseEmailError(err) => err.fmt(f),
|
||||||
Error::ReadEmailNotFoundError(uid) => {
|
Error::ReadEmailNotFoundError(uid) => {
|
||||||
write!(f, "no email found for uid {}", 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
|
// Result wrapper
|
||||||
|
|
||||||
type Result<T> = result::Result<T, Error>;
|
type Result<T> = result::Result<T, Error>;
|
||||||
|
@ -58,27 +66,6 @@ pub struct ImapConnector {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl 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> {
|
pub fn new(config: config::ServerInfo) -> Result<Self> {
|
||||||
let tls = TlsConnector::new()?;
|
let tls = TlsConnector::new()?;
|
||||||
let client = imap::connect(config.get_addr(), &config.host, &tls)?;
|
let client = imap::connect(config.get_addr(), &config.host, &tls)?;
|
||||||
|
@ -123,23 +110,23 @@ impl ImapConnector {
|
||||||
Ok(emails)
|
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)?;
|
self.sess.select(mbox)?;
|
||||||
|
|
||||||
match self.sess.uid_fetch(uid, "BODY[]")?.first() {
|
match self.sess.uid_fetch(uid, "BODY[]")?.first() {
|
||||||
None => Err(Error::ReadEmailNotFoundError(uid.to_string())),
|
None => Err(Error::ReadEmailNotFoundError(uid.to_string())),
|
||||||
Some(fetch) => {
|
Some(fetch) => {
|
||||||
let email = mailparse::parse_mail(fetch.body().unwrap_or(&[])).unwrap();
|
let bytes = fetch.body().unwrap_or(&[]);
|
||||||
let mut parts = vec![];
|
let email = mailparse::parse_mail(bytes)?;
|
||||||
Self::extract_subparts_by_mime(mime, &email, &mut parts);
|
let bodies = email::extract_text_bodies(&mime, &email);
|
||||||
|
|
||||||
if parts.len() == 0 {
|
if bodies.is_empty() {
|
||||||
Err(Error::ReadEmailEmptyPartError(
|
Err(Error::ReadEmailEmptyPartError(
|
||||||
uid.to_string(),
|
uid.to_string(),
|
||||||
mime.to_string(),
|
mime.to_string(),
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
Ok(parts.join("\r\n"))
|
Ok(bodies)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -175,16 +175,18 @@ fn run() -> Result<()> {
|
||||||
let mbox = matches.value_of("mailbox").unwrap();
|
let mbox = matches.value_of("mailbox").unwrap();
|
||||||
let uid = matches.value_of("uid").unwrap();
|
let uid = matches.value_of("uid").unwrap();
|
||||||
let mime = matches.value_of("mime-type").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") {
|
if let Some(_) = matches.subcommand_matches("write") {
|
||||||
let config = Config::new_from_file()?;
|
let config = Config::new_from_file()?;
|
||||||
let draft = editor::open_with_new_template()?;
|
let draft = editor::open_with_new_template()?;
|
||||||
|
|
||||||
|
println!("Sending ...");
|
||||||
smtp::send(&config, draft.as_bytes());
|
smtp::send(&config, draft.as_bytes());
|
||||||
|
println!("Done!");
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -45,9 +45,8 @@ pub fn send(config: &config::Config, bytes: &[u8]) {
|
||||||
.credentials(creds)
|
.credentials(creds)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
println!("Sending ...");
|
|
||||||
match mailer.send(&email) {
|
match mailer.send(&email) {
|
||||||
Ok(_) => println!("Email sent successfully!"),
|
Ok(_) => (),
|
||||||
Err(e) => panic!("Could not send email: {:?}", e),
|
Err(e) => panic!("Could not send email: {:?}", e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue