mirror of
https://github.com/soywod/himalaya.git
synced 2024-07-08 18:45:13 +00:00
refactor write email command
This commit is contained in:
parent
80837044b2
commit
c7b326a695
|
@ -3,7 +3,8 @@ use rfc2047_decoder;
|
||||||
|
|
||||||
use crate::table::{self, DisplayCell, DisplayRow, DisplayTable};
|
use crate::table::{self, DisplayCell, DisplayRow, DisplayTable};
|
||||||
|
|
||||||
pub struct Uid(u32);
|
#[derive(Debug)]
|
||||||
|
pub struct Uid(pub u32);
|
||||||
|
|
||||||
impl Uid {
|
impl Uid {
|
||||||
pub fn from_fetch(fetch: &imap::types::Fetch) -> Self {
|
pub fn from_fetch(fetch: &imap::types::Fetch) -> Self {
|
||||||
|
@ -21,6 +22,7 @@ impl DisplayCell for Uid {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct Flags<'a>(Vec<imap::types::Flag<'a>>);
|
pub struct Flags<'a>(Vec<imap::types::Flag<'a>>);
|
||||||
|
|
||||||
impl Flags<'_> {
|
impl Flags<'_> {
|
||||||
|
@ -67,6 +69,7 @@ impl DisplayCell for Flags<'_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct Sender(String);
|
pub struct Sender(String);
|
||||||
|
|
||||||
impl Sender {
|
impl Sender {
|
||||||
|
@ -97,6 +100,7 @@ impl DisplayCell for Sender {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct Subject(String);
|
pub struct Subject(String);
|
||||||
|
|
||||||
impl Subject {
|
impl Subject {
|
||||||
|
@ -124,6 +128,7 @@ impl DisplayCell for Subject {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct Date(String);
|
pub struct Date(String);
|
||||||
|
|
||||||
impl Date {
|
impl Date {
|
||||||
|
@ -148,6 +153,7 @@ impl DisplayCell for Date {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct Email<'a> {
|
pub struct Email<'a> {
|
||||||
pub uid: Uid,
|
pub uid: Uid,
|
||||||
pub flags: Flags<'a>,
|
pub flags: Flags<'a>,
|
||||||
|
|
174
src/imap.rs
174
src/imap.rs
|
@ -7,6 +7,60 @@ use crate::config;
|
||||||
use crate::email::Email;
|
use crate::email::Email;
|
||||||
use crate::mailbox::Mailbox;
|
use crate::mailbox::Mailbox;
|
||||||
|
|
||||||
|
// Error wrapper
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Error {
|
||||||
|
CreateTlsConnectorError(native_tls::Error),
|
||||||
|
CreateImapSession(imap::Error),
|
||||||
|
ReadEmailNotFoundError(String),
|
||||||
|
ReadEmailEmptyPartError(String, String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Error {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Error::CreateTlsConnectorError(err) => err.fmt(f),
|
||||||
|
Error::CreateImapSession(err) => err.fmt(f),
|
||||||
|
Error::ReadEmailNotFoundError(uid) => {
|
||||||
|
write!(f, "No email found for UID {}", uid)
|
||||||
|
}
|
||||||
|
Error::ReadEmailEmptyPartError(uid, mime) => {
|
||||||
|
write!(f, "No {} content found for UID {}", mime, uid)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl error::Error for Error {
|
||||||
|
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
|
||||||
|
match *self {
|
||||||
|
Error::CreateTlsConnectorError(ref err) => Some(err),
|
||||||
|
Error::CreateImapSession(ref err) => Some(err),
|
||||||
|
Error::ReadEmailNotFoundError(_) => None,
|
||||||
|
Error::ReadEmailEmptyPartError(_, _) => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<native_tls::Error> for Error {
|
||||||
|
fn from(err: native_tls::Error) -> Error {
|
||||||
|
Error::CreateTlsConnectorError(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<imap::Error> for Error {
|
||||||
|
fn from(err: imap::Error) -> Error {
|
||||||
|
Error::CreateImapSession(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Result wrapper
|
||||||
|
|
||||||
|
type Result<T> = result::Result<T, Error>;
|
||||||
|
|
||||||
|
// Imap connector
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ImapConnector {
|
pub struct ImapConnector {
|
||||||
pub config: config::ServerInfo,
|
pub config: config::ServerInfo,
|
||||||
|
@ -14,6 +68,26 @@ 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()
|
||||||
|
{
|
||||||
|
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)?;
|
||||||
|
@ -57,92 +131,26 @@ impl ImapConnector {
|
||||||
|
|
||||||
Ok(emails)
|
Ok(emails)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Error wrapper
|
pub fn read_email(&mut self, mbox: &str, uid: &str, mime: &str) -> Result<String> {
|
||||||
|
self.sess.select(mbox)?;
|
||||||
|
|
||||||
#[derive(Debug)]
|
match self.sess.uid_fetch(uid, "BODY[]")?.first() {
|
||||||
pub enum Error {
|
None => return Err(Error::ReadEmailNotFoundError(uid.to_string())),
|
||||||
CreateTlsConnectorError(native_tls::Error),
|
Some(email_raw) => {
|
||||||
CreateImapSession(imap::Error),
|
let email = mailparse::parse_mail(email_raw.body().unwrap_or(&[])).unwrap();
|
||||||
}
|
let mut parts = vec![];
|
||||||
|
Self::extract_subparts_by_mime(mime, &email, &mut parts);
|
||||||
|
|
||||||
impl fmt::Display for Error {
|
if parts.len() == 0 {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
Err(Error::ReadEmailEmptyPartError(
|
||||||
match self {
|
uid.to_string(),
|
||||||
Error::CreateTlsConnectorError(err) => err.fmt(f),
|
mime.to_string(),
|
||||||
Error::CreateImapSession(err) => err.fmt(f),
|
))
|
||||||
|
} else {
|
||||||
|
Ok(parts.join("\r\n"))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl error::Error for Error {
|
|
||||||
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
|
|
||||||
match *self {
|
|
||||||
Error::CreateTlsConnectorError(ref err) => Some(err),
|
|
||||||
Error::CreateImapSession(ref err) => Some(err),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<native_tls::Error> for Error {
|
|
||||||
fn from(err: native_tls::Error) -> Error {
|
|
||||||
Error::CreateTlsConnectorError(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<imap::Error> for Error {
|
|
||||||
fn from(err: imap::Error) -> Error {
|
|
||||||
Error::CreateImapSession(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Result wrapper
|
|
||||||
|
|
||||||
type Result<T> = result::Result<T, Error>;
|
|
||||||
|
|
||||||
// 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()
|
|
||||||
// {
|
|
||||||
// parts.push(part.get_body().unwrap_or(String::new()))
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// _ => {
|
|
||||||
// part.subparts
|
|
||||||
// .iter()
|
|
||||||
// .for_each(|p| extract_subparts_by_mime(mime, p, parts));
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// pub fn read_email(
|
|
||||||
// imap_sess: &mut ImapSession,
|
|
||||||
// mbox: &str,
|
|
||||||
// uid: &str,
|
|
||||||
// mime: &str,
|
|
||||||
// ) -> imap::Result<()> {
|
|
||||||
// imap_sess.select(mbox)?;
|
|
||||||
|
|
||||||
// match imap_sess.uid_fetch(uid, "BODY[]")?.first() {
|
|
||||||
// None => println!("No email found in mailbox {} with UID {}", mbox, uid),
|
|
||||||
// Some(email_raw) => {
|
|
||||||
// let email = mailparse::parse_mail(email_raw.body().unwrap_or(&[])).unwrap();
|
|
||||||
// let mut parts = vec![];
|
|
||||||
// extract_subparts_by_mime(mime, &email, &mut parts);
|
|
||||||
|
|
||||||
// if parts.len() == 0 {
|
|
||||||
// println!("No {} content found for email {}!", mime, uid);
|
|
||||||
// } else {
|
|
||||||
// println!("{}", parts.join("\r\n"));
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Ok(())
|
|
||||||
// }
|
|
||||||
|
|
88
src/main.rs
88
src/main.rs
|
@ -6,14 +6,25 @@ mod smtp;
|
||||||
mod table;
|
mod table;
|
||||||
|
|
||||||
use clap::{App, Arg, SubCommand};
|
use clap::{App, Arg, SubCommand};
|
||||||
|
use std::env::temp_dir;
|
||||||
|
use std::fs::{remove_file, File};
|
||||||
|
use std::io::{Read, Write};
|
||||||
|
use std::process::{exit, Command};
|
||||||
|
|
||||||
use crate::config::Config;
|
use crate::config::Config;
|
||||||
use crate::imap::ImapConnector;
|
use crate::imap::ImapConnector;
|
||||||
use crate::table::DisplayTable;
|
use crate::table::DisplayTable;
|
||||||
|
|
||||||
// fn new_email_tpl() -> String {
|
fn main() {
|
||||||
// ["To: ", "Subject: ", ""].join("\r\n")
|
if let Err(err) = dispatch() {
|
||||||
// }
|
eprintln!("Error: {}", err);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_email_tpl() -> String {
|
||||||
|
["To: ", "Subject: ", ""].join("\r\n")
|
||||||
|
}
|
||||||
|
|
||||||
// fn forward_email_tpl() -> String {
|
// fn forward_email_tpl() -> String {
|
||||||
// ["To: ", "Subject: ", ""].join("\r\n")
|
// ["To: ", "Subject: ", ""].join("\r\n")
|
||||||
|
@ -35,12 +46,6 @@ fn uid_arg() -> Arg<'static, 'static> {
|
||||||
.required(true)
|
.required(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
|
||||||
if let Err(err) = dispatch() {
|
|
||||||
panic!(err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn dispatch() -> Result<(), imap::Error> {
|
fn dispatch() -> Result<(), imap::Error> {
|
||||||
let matches = App::new("Himalaya")
|
let matches = App::new("Himalaya")
|
||||||
.version("0.1.0")
|
.version("0.1.0")
|
||||||
|
@ -140,63 +145,68 @@ fn dispatch() -> Result<(), imap::Error> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if let Some(matches) = matches.subcommand_matches("read") {
|
if let Some(matches) = matches.subcommand_matches("read") {
|
||||||
// let mbox = matches.value_of("mailbox").unwrap();
|
let config = Config::new_from_file();
|
||||||
// let mime = matches.value_of("mime-type").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 email = ImapConnector::new(config.imap)?.read_email(&mbox, &uid, &mime)?;
|
||||||
|
|
||||||
// imap::read_email(&mut imap_sess, mbox, uid, mime).unwrap();
|
println!("{}", email);
|
||||||
// }
|
}
|
||||||
|
|
||||||
// if let Some(_) = matches.subcommand_matches("write") {
|
if let Some(_) = matches.subcommand_matches("write") {
|
||||||
// let mut draft_path = env::temp_dir();
|
let config = Config::new_from_file();
|
||||||
// draft_path.push("himalaya-draft.mail");
|
|
||||||
|
|
||||||
// fs::File::create(&draft_path)
|
let mut draft_path = temp_dir();
|
||||||
// .expect("Could not create draft file")
|
draft_path.push("himalaya-draft.mail");
|
||||||
// .write(new_email_tpl().as_bytes())
|
|
||||||
// .expect("Could not write into draft file");
|
|
||||||
|
|
||||||
// process::Command::new(env!("EDITOR"))
|
File::create(&draft_path)
|
||||||
// .arg(&draft_path)
|
.expect("Could not create draft file")
|
||||||
// .status()
|
.write(new_email_tpl().as_bytes())
|
||||||
// .expect("Could not start $EDITOR");
|
.expect("Could not write into draft file");
|
||||||
|
|
||||||
// let mut draft = String::new();
|
Command::new(env!("EDITOR"))
|
||||||
// fs::File::open(&draft_path)
|
.arg(&draft_path)
|
||||||
// .expect("Could not open draft file")
|
.status()
|
||||||
// .read_to_string(&mut draft)
|
.expect("Could not start $EDITOR");
|
||||||
// .expect("Could not read draft file");
|
|
||||||
|
|
||||||
// fs::remove_file(&draft_path).expect("Could not remove draft file");
|
let mut draft = String::new();
|
||||||
|
File::open(&draft_path)
|
||||||
|
.expect("Could not open draft file")
|
||||||
|
.read_to_string(&mut draft)
|
||||||
|
.expect("Could not read draft file");
|
||||||
|
|
||||||
// smtp::send(&config, &draft.as_bytes());
|
remove_file(&draft_path).expect("Could not remove draft file");
|
||||||
// }
|
|
||||||
|
smtp::send(&config, &draft.as_bytes());
|
||||||
|
}
|
||||||
|
|
||||||
// if let Some(_) = matches.subcommand_matches("forward") {
|
// if let Some(_) = matches.subcommand_matches("forward") {
|
||||||
|
// let config = Config::new_from_file();
|
||||||
// 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 mut draft_path = env::temp_dir();
|
// let mut draft_path = temp_dir();
|
||||||
// draft_path.push("himalaya-draft.mail");
|
// draft_path.push("himalaya-draft.mail");
|
||||||
|
|
||||||
// fs::File::create(&draft_path)
|
// File::create(&draft_path)
|
||||||
// .expect("Could not create draft file")
|
// .expect("Could not create draft file")
|
||||||
// .write(forward_email_tpl().as_bytes())
|
// .write(forward_email_tpl().as_bytes())
|
||||||
// .expect("Could not write into draft file");
|
// .expect("Could not write into draft file");
|
||||||
|
|
||||||
// process::Command::new(env!("EDITOR"))
|
// Command::new(env!("EDITOR"))
|
||||||
// .arg(&draft_path)
|
// .arg(&draft_path)
|
||||||
// .status()
|
// .status()
|
||||||
// .expect("Could not start $EDITOR");
|
// .expect("Could not start $EDITOR");
|
||||||
|
|
||||||
// let mut draft = String::new();
|
// let mut draft = String::new();
|
||||||
// fs::File::open(&draft_path)
|
// File::open(&draft_path)
|
||||||
// .expect("Could not open draft file")
|
// .expect("Could not open draft file")
|
||||||
// .read_to_string(&mut draft)
|
// .read_to_string(&mut draft)
|
||||||
// .expect("Could not read draft file");
|
// .expect("Could not read draft file");
|
||||||
|
|
||||||
// fs::remove_file(&draft_path).expect("Could not remove draft file");
|
// remove_file(&draft_path).expect("Could not remove draft file");
|
||||||
|
|
||||||
// smtp::send(&config, &draft.as_bytes());
|
// smtp::send(&config, &draft.as_bytes());
|
||||||
// }
|
// }
|
||||||
|
|
Loading…
Reference in a new issue