mirror of
https://github.com/FliegendeWurst/inboxid.git
synced 2024-11-09 10:50:40 +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",
|
"maildir",
|
||||||
"mailparse",
|
"mailparse",
|
||||||
"mailproc",
|
"mailproc",
|
||||||
|
"mime2ext",
|
||||||
"moins",
|
"moins",
|
||||||
"rusqlite",
|
"rusqlite",
|
||||||
"rustls-connector",
|
"rustls-connector",
|
||||||
"rustyline",
|
"rustyline",
|
||||||
|
"subprocess",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -294,6 +296,12 @@ dependencies = [
|
|||||||
"either",
|
"either",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itoa"
|
||||||
|
version = "0.4.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "js-sys"
|
name = "js-sys"
|
||||||
version = "0.3.50"
|
version = "0.3.50"
|
||||||
@ -398,10 +406,20 @@ dependencies = [
|
|||||||
"winapi",
|
"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]]
|
[[package]]
|
||||||
name = "moins"
|
name = "moins"
|
||||||
version = "0.5.0"
|
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 = [
|
dependencies = [
|
||||||
"termion",
|
"termion",
|
||||||
]
|
]
|
||||||
@ -501,9 +519,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quoted_printable"
|
name = "quoted_printable"
|
||||||
version = "0.4.2"
|
version = "0.4.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "47b080c5db639b292ac79cbd34be0cfc5d36694768d8341109634d90b86930e2"
|
checksum = "1238256b09923649ec89b08104c4dfe9f6cb2fea734a5db5384e44916d59e9c5"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "radix_trie"
|
name = "radix_trie"
|
||||||
@ -709,6 +727,9 @@ name = "serde"
|
|||||||
version = "1.0.125"
|
version = "1.0.125"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "558dc50e1a5a5fa7112ca2ce4effcb321b0300c0d4ccf0776a9f60cd89031171"
|
checksum = "558dc50e1a5a5fa7112ca2ce4effcb321b0300c0d4ccf0776a9f60cd89031171"
|
||||||
|
dependencies = [
|
||||||
|
"serde_derive",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
@ -721,6 +742,17 @@ dependencies = [
|
|||||||
"syn",
|
"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]]
|
[[package]]
|
||||||
name = "smallvec"
|
name = "smallvec"
|
||||||
version = "1.6.1"
|
version = "1.6.1"
|
||||||
|
@ -21,3 +21,5 @@ rustyline = "8.0.0"
|
|||||||
moins = { git = "https://github.com/FliegendeWurst/moins", branch = "master" }
|
moins = { git = "https://github.com/FliegendeWurst/moins", branch = "master" }
|
||||||
anyhow = "1.0.40"
|
anyhow = "1.0.40"
|
||||||
mailproc = { git = "https://github.com/FliegendeWurst/mailproc.git", branch = "master" }
|
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 ascii_table::{Align, AsciiTable, Column};
|
||||||
use inboxid::*;
|
use inboxid::*;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
use mailparse::ParsedMail;
|
||||||
use rustyline::{Editor, error::ReadlineError};
|
use rustyline::{Editor, error::ReadlineError};
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
@ -64,19 +65,71 @@ fn show_listing(mailbox: &str) -> Result<()> {
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
let mut rl = Editor::<()>::new();
|
let mut rl = Editor::<()>::new();
|
||||||
|
let mut state = Initial;
|
||||||
|
let mut to_delete = HashSet::new();
|
||||||
loop {
|
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 {
|
match readline {
|
||||||
Ok(line) => {
|
Ok(line) => {
|
||||||
let idx = mails.len() - line.trim().parse::<usize>().unwrap();
|
let input_idx = line.trim().parse::<usize>();
|
||||||
let mail = &mails[idx];
|
match state {
|
||||||
println!("{}", std::str::from_utf8(&mail.get_body_raw().unwrap()).unwrap());
|
Initial => {
|
||||||
for x in &mail.subparts {
|
if let Ok(idx) = input_idx {
|
||||||
if x.ctype.mimetype == "text/html" {
|
let idx = mails.len() - idx;
|
||||||
continue; // TODO
|
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) => {
|
Err(ReadlineError::Interrupted) => {
|
||||||
@ -90,7 +143,20 @@ fn show_listing(mailbox: &str) -> Result<()> {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
println!("unknown command!");
|
||||||
|
}
|
||||||
|
|
||||||
|
for x in to_delete {
|
||||||
|
let _ = fs::remove_file(x);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
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 {
|
pub trait MaildirExtension {
|
||||||
fn get_file(&self, name: &str) -> std::result::Result<String, io::Error>;
|
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>;
|
fn save_file(&self, name: &str, content: &str) -> std::result::Result<(), io::Error>;
|
||||||
|
Loading…
Reference in New Issue
Block a user