mirror of
https://github.com/FliegendeWurst/inboxid.git
synced 2024-11-22 00:45:01 +00:00
list: multipart message reading
This commit is contained in:
parent
9282813429
commit
f0def7776b
38
Cargo.lock
generated
38
Cargo.lock
generated
@ -279,10 +279,12 @@ dependencies = [
|
||||
"maildir",
|
||||
"mailparse",
|
||||
"mailproc",
|
||||
"mime2ext",
|
||||
"moins",
|
||||
"rusqlite",
|
||||
"rustls-connector",
|
||||
"rustyline",
|
||||
"subprocess",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -294,6 +296,12 @@ dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "0.4.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736"
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.50"
|
||||
@ -398,10 +406,20 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mime2ext"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b88947611258697e12f8602a44003b0885ca5fe30f27132d63c8f47fe98f2f2e"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "moins"
|
||||
version = "0.5.0"
|
||||
source = "git+https://github.com/FliegendeWurst/moins?branch=master#46df794709b7fdc23e83e61d97c75b11e8248f0f"
|
||||
source = "git+https://github.com/FliegendeWurst/moins?branch=master#d30052f34176336d27c35c8e62fa56fce70b03ea"
|
||||
dependencies = [
|
||||
"termion",
|
||||
]
|
||||
@ -501,9 +519,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "quoted_printable"
|
||||
version = "0.4.2"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "47b080c5db639b292ac79cbd34be0cfc5d36694768d8341109634d90b86930e2"
|
||||
checksum = "1238256b09923649ec89b08104c4dfe9f6cb2fea734a5db5384e44916d59e9c5"
|
||||
|
||||
[[package]]
|
||||
name = "radix_trie"
|
||||
@ -709,6 +727,9 @@ name = "serde"
|
||||
version = "1.0.125"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "558dc50e1a5a5fa7112ca2ce4effcb321b0300c0d4ccf0776a9f60cd89031171"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
@ -721,6 +742,17 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.64"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "799e97dc9fdae36a5c8b8f2cae9ce2ee9fdce2058c57a93e6099d919fd982f79"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.6.1"
|
||||
|
@ -21,3 +21,5 @@ rustyline = "8.0.0"
|
||||
moins = { git = "https://github.com/FliegendeWurst/moins", branch = "master" }
|
||||
anyhow = "1.0.40"
|
||||
mailproc = { git = "https://github.com/FliegendeWurst/mailproc.git", branch = "master" }
|
||||
subprocess = "0.2.6"
|
||||
mime2ext = "0.1.2"
|
||||
|
@ -1,8 +1,9 @@
|
||||
use std::{array::IntoIter, env};
|
||||
use std::{array::IntoIter, collections::HashSet, env, fs};
|
||||
|
||||
use ascii_table::{Align, AsciiTable, Column};
|
||||
use inboxid::*;
|
||||
use itertools::Itertools;
|
||||
use mailparse::ParsedMail;
|
||||
use rustyline::{Editor, error::ReadlineError};
|
||||
|
||||
fn main() -> Result<()> {
|
||||
@ -64,19 +65,71 @@ fn show_listing(mailbox: &str) -> Result<()> {
|
||||
return Ok(());
|
||||
}
|
||||
let mut rl = Editor::<()>::new();
|
||||
let mut state = Initial;
|
||||
let mut to_delete = HashSet::new();
|
||||
loop {
|
||||
let readline = rl.readline(">> ");
|
||||
let readline = rl.readline(&match state {
|
||||
Initial => ">> ".to_owned(),
|
||||
MailSelected(x) => format!("{} >> ", mails.len() - x),
|
||||
AwaitingSave(_, _) => "out? >> ".to_owned()
|
||||
});
|
||||
match readline {
|
||||
Ok(line) => {
|
||||
let idx = mails.len() - line.trim().parse::<usize>().unwrap();
|
||||
let mail = &mails[idx];
|
||||
println!("{}", std::str::from_utf8(&mail.get_body_raw().unwrap()).unwrap());
|
||||
for x in &mail.subparts {
|
||||
if x.ctype.mimetype == "text/html" {
|
||||
continue; // TODO
|
||||
let input_idx = line.trim().parse::<usize>();
|
||||
match state {
|
||||
Initial => {
|
||||
if let Ok(idx) = input_idx {
|
||||
let idx = mails.len() - idx;
|
||||
let mail = &mails[idx];
|
||||
if mail.ctype.mimetype.starts_with("text/") {
|
||||
let raw_body = mail.get_body_raw();
|
||||
let content = std::str::from_utf8(raw_body.as_deref().unwrap())?;
|
||||
moins::Moins::run(content, None);
|
||||
} else if mail.ctype.mimetype.starts_with("multipart/") {
|
||||
mail.print_tree_structure(0, &mut 1);
|
||||
state = MailSelected(idx);
|
||||
} else {
|
||||
state = AwaitingSave(&*mail, None);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
},
|
||||
MailSelected(mail_idx) => {
|
||||
let mail = &mails[mail_idx];
|
||||
if let Ok(idx) = input_idx {
|
||||
let part = mail.get_tree_part(&mut 1, idx).unwrap();
|
||||
if part.ctype.mimetype.starts_with("text/") {
|
||||
let raw_body = part.get_body_raw();
|
||||
let content = std::str::from_utf8(raw_body.as_deref().unwrap())?;
|
||||
moins::Moins::run(content, None);
|
||||
} else {
|
||||
state = AwaitingSave(part, Some(mail_idx));
|
||||
}
|
||||
continue;
|
||||
} else if line.is_empty() {
|
||||
state = Initial;
|
||||
continue;
|
||||
}
|
||||
},
|
||||
AwaitingSave(mail, idx) => {
|
||||
if line == "open" {
|
||||
let path = if let Some(ext) = mime2ext::mime2ext(&mail.ctype.mimetype) {
|
||||
format!("/tmp/mail_content.{}", ext)
|
||||
} else {
|
||||
"/tmp/mail_content".to_owned()
|
||||
};
|
||||
fs::write(&path, &mail.get_body_raw()?)?;
|
||||
let mut p = subprocess::Popen::create(&["xdg-open", &path], Default::default())?;
|
||||
p.wait()?;
|
||||
to_delete.insert(path);
|
||||
state = if let Some(idx) = idx {
|
||||
MailSelected(idx)
|
||||
} else {
|
||||
Initial
|
||||
};
|
||||
continue;
|
||||
}
|
||||
}
|
||||
let mut content = x.get_body().unwrap();
|
||||
moins::Moins::run(&mut content, None);
|
||||
}
|
||||
},
|
||||
Err(ReadlineError::Interrupted) => {
|
||||
@ -90,7 +143,20 @@ fn show_listing(mailbox: &str) -> Result<()> {
|
||||
break
|
||||
}
|
||||
}
|
||||
println!("unknown command!");
|
||||
}
|
||||
|
||||
for x in to_delete {
|
||||
let _ = fs::remove_file(x);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
enum State<'a> {
|
||||
Initial,
|
||||
MailSelected(usize),
|
||||
AwaitingSave(&'a ParsedMail<'a>, Option<usize>)
|
||||
}
|
||||
|
||||
use State::*;
|
||||
|
31
src/lib.rs
31
src/lib.rs
@ -109,6 +109,37 @@ impl<'a> Deref for EasyMail<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub trait MailExtension {
|
||||
fn print_tree_structure(&self, depth: usize, counter: &mut usize);
|
||||
fn get_tree_part(&self, counter: &mut usize, target: usize) -> Option<&ParsedMail>;
|
||||
}
|
||||
|
||||
impl MailExtension for ParsedMail<'_> {
|
||||
fn print_tree_structure(&self, depth: usize, counter: &mut usize) {
|
||||
if depth == 0 {
|
||||
println!("{}", self.ctype.mimetype);
|
||||
}
|
||||
for mail in &self.subparts {
|
||||
println!("{}-> {} [{}]", " ".repeat(depth), mail.ctype.mimetype, counter);
|
||||
*counter += 1;
|
||||
mail.print_tree_structure(depth + 1, counter);
|
||||
}
|
||||
}
|
||||
|
||||
fn get_tree_part(&self, counter: &mut usize, target: usize) -> Option<&ParsedMail> {
|
||||
for mail in &self.subparts {
|
||||
if *counter == target {
|
||||
return Some(mail);
|
||||
}
|
||||
*counter += 1;
|
||||
if let Some(x) = mail.get_tree_part(counter, target) {
|
||||
return Some(x);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub trait MaildirExtension {
|
||||
fn get_file(&self, name: &str) -> std::result::Result<String, io::Error>;
|
||||
fn save_file(&self, name: &str, content: &str) -> std::result::Result<(), io::Error>;
|
||||
|
Loading…
Reference in New Issue
Block a user