From c9dde850b797f726dcfa5bd1489b0c3280f3f7a6 Mon Sep 17 00:00:00 2001 From: FliegendeWurst <2012gdwu+github@posteo.de> Date: Wed, 31 Mar 2021 11:43:59 +0200 Subject: [PATCH] filter: mail processing command --- Cargo.lock | 63 +++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 3 ++- src/bin/filter.rs | 52 ++++++++++++++++++++++++++++++++++++++ src/bin/sync.rs | 2 +- src/lib.rs | 13 ++++++++-- 5 files changed, 129 insertions(+), 4 deletions(-) create mode 100644 src/bin/filter.rs diff --git a/Cargo.lock b/Cargo.lock index 04cb9d7..0374068 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -278,6 +278,7 @@ dependencies = [ "itertools", "maildir", "mailparse", + "mailproc", "moins", "rusqlite", "rustls-connector", @@ -349,9 +350,11 @@ dependencies = [ [[package]] name = "maildir" version = "0.5.0" +source = "git+https://github.com/FliegendeWurst/maildir.git?branch=master#f9b8ffa12161ebed0688518f303b907a1f34eac6" dependencies = [ "gethostname", "mailparse", + "memmap", ] [[package]] @@ -365,12 +368,36 @@ dependencies = [ "quoted_printable", ] +[[package]] +name = "mailproc" +version = "0.3.1" +source = "git+https://github.com/FliegendeWurst/mailproc.git?branch=master#46e27f9a10f64b6f77b7fbbddb03e2187ca3465a" +dependencies = [ + "log", + "mailparse", + "regex", + "serde", + "serde_derive", + "subprocess", + "toml", +] + [[package]] name = "memchr" version = "2.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" +[[package]] +name = "memmap" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "moins" version = "0.5.0" @@ -677,6 +704,23 @@ dependencies = [ "libc", ] +[[package]] +name = "serde" +version = "1.0.125" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "558dc50e1a5a5fa7112ca2ce4effcb321b0300c0d4ccf0776a9f60cd89031171" + +[[package]] +name = "serde_derive" +version = "1.0.125" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b093b7a2bb58203b5da3056c05b4ec1fed827dcfdb37347a8841695263b3d06d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "smallvec" version = "1.6.1" @@ -695,6 +739,16 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "subprocess" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69b9ad6c3e1b525a55872a4d2f2d404b3c959b7bbcbfd83c364580f68ed157bd" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "syn" version = "1.0.67" @@ -729,6 +783,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "toml" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" +dependencies = [ + "serde", +] + [[package]] name = "unicode-segmentation" version = "1.7.1" diff --git a/Cargo.toml b/Cargo.toml index b384236..7fe9dc1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,7 @@ license = "GPL-3.0-or-later" [dependencies] imap = { version = "2.4.1", default-features = false } itertools = "0.10.0" -maildir = { path = "../maildir" } +maildir = { git = "https://github.com/FliegendeWurst/maildir.git", branch = "master", features = ["mmap"] } mailparse = "0.13.2" rustls-connector = "0.13.1" ascii_table = { git = "https://gitlab.com/arnekeller/ascii-table.git", branch = "master" } @@ -20,3 +20,4 @@ rusqlite = { git = "https://github.com/rusqlite/rusqlite", branch = "master", fe 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" } diff --git a/src/bin/filter.rs b/src/bin/filter.rs new file mode 100644 index 0000000..9639346 --- /dev/null +++ b/src/bin/filter.rs @@ -0,0 +1,52 @@ +use std::env; + +use anyhow::anyhow; +use inboxid::*; +use itertools::Itertools; +use mailproc::Config; + +fn main() -> Result<()> { + let args = env::args().collect_vec(); + if args.len() < 3 { + Err(anyhow!("required arguments: mailbox name, filter file path"))?; + unreachable!() + } else { + do_filtering(&args[1], &args[2]) + } +} + +fn do_filtering(mailbox: &str, config: &str) -> Result<()> { + let config = Config::load_from_path(config)?; + + let maildir = get_maildir(mailbox)?; + + let mut mails = Vec::new(); + for x in maildir.list_cur() { + mails.push(x?); + } + let mut mails = maildir.get_mails(&mut mails)?; + mails.sort_by_key(|x| x.id); + + let mut imap_session = get_imap_session()?; + imap_session.select(mailbox)?; + + for mail in mails { + if let Some(action) = mailproc::handle(&mail, &[], &config) { // TODO: provide raw bytes + println!("{:?}", action.0); + println!(" matched {}", mail.subject); + for action in action.0.action.as_ref().unwrap() { + match &*action[0] { + "mv" => { + println!(" moving to mailbox {}", action[1]); + imap_session.uid_mv(mail.id.uid.to_string(), &action[1])?; + }, + x => { + println!("WARNING: unknown action {:?}", x); + } + } + } + } + } + + Ok(()) +} diff --git a/src/bin/sync.rs b/src/bin/sync.rs index b0787bb..e7f650f 100644 --- a/src/bin/sync.rs +++ b/src/bin/sync.rs @@ -35,7 +35,7 @@ fn sync( println!("{:?}", x); names.push(x.name()); } - names = vec!["INBOX", "nebenan"]; + names = vec!["INBOX", "Github", "nebenan"]; let mut remote = HashMap::new(); diff --git a/src/lib.rs b/src/lib.rs index 12818ca..1ed270e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,8 +9,9 @@ use rusqlite::{Connection, params}; use rustls_connector::{RustlsConnector, rustls::{ClientSession, StreamOwned}}; pub type Result = std::result::Result>; +pub type ImapSession = Session>; -pub fn connect(host: &str, port: u16, user: &str, password: &str) -> Result>> { +pub fn connect(host: &str, port: u16, user: &str, password: &str) -> Result { println!("connecting.."); let stream = TcpStream::connect((host, port))?; let tls = RustlsConnector::new_with_native_certs()?; @@ -54,7 +55,7 @@ pub fn gen_id(uid_validity: u32, uid: u32) -> String { #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub struct MaildirID { uid_validity: u32, - uid: u32, + pub uid: u32, } impl TryFrom<&str> for MaildirID { @@ -157,3 +158,11 @@ pub fn remove_cow<'a>(x: &Flag<'a>) -> Flag<'static> { Flag::MayCreate => Flag::MayCreate, } } + +pub fn get_imap_session() -> Result { + let host = env::var("MAILHOST").expect("missing envvar MAILHOST"); + let user = env::var("MAILUSER").expect("missing envvar MAILUSER"); + let password = env::var("MAILPASSWORD").expect("missing envvar MAILPASSWORD"); + let port = 993; + connect(&host, port, &user, &password) +}