diff --git a/Cargo.lock b/Cargo.lock index 817a931..88a3d7c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,12 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "ahash" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "739f4a8db6605981345c5654f3a85b056ce52f37a39d34da03f25bf2151ea16e" + [[package]] name = "aho-corasick" version = "0.7.15" @@ -20,8 +26,9 @@ checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" [[package]] name = "ascii_table" version = "4.0.0-alpha" -source = "git+https://gitlab.com/arnekeller/ascii-table.git?branch=master#2d485d6b3408ed8ef2629b6be14189a312d8c60c" +source = "git+https://gitlab.com/arnekeller/ascii-table.git?branch=master#2f57befcd2af631301b633af561c7404414f6644" dependencies = [ + "unicode-segmentation", "unicode-width", ] @@ -142,6 +149,18 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "fallible-iterator" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" + +[[package]] +name = "fallible-streaming-iterator" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" + [[package]] name = "gethostname" version = "0.2.1" @@ -152,6 +171,24 @@ dependencies = [ "winapi", ] +[[package]] +name = "hashbrown" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashlink" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d99cf782f0dc4372d26846bec3de7804ceb5df083c2d4462c0b8d2330e894fa8" +dependencies = [ + "hashbrown", +] + [[package]] name = "imap" version = "2.4.1" @@ -186,6 +223,7 @@ dependencies = [ "itertools", "maildir", "mailparse", + "rusqlite", "rustls-connector", ] @@ -232,6 +270,16 @@ version = "0.2.91" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8916b1f6ca17130ec6568feccee27c156ad12037880833a3b842a823236502e7" +[[package]] +name = "libsqlite3-sys" +version = "0.21.0" +source = "git+https://github.com/rusqlite/rusqlite?branch=master#ed3bfbdf9d9e577e8d4cff937b053a0e429a4cd7" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] + [[package]] name = "log" version = "0.4.14" @@ -308,6 +356,12 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" +[[package]] +name = "pkg-config" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c" + [[package]] name = "proc-macro2" version = "1.0.24" @@ -364,6 +418,20 @@ dependencies = [ "winapi", ] +[[package]] +name = "rusqlite" +version = "0.24.2" +source = "git+https://github.com/rusqlite/rusqlite?branch=master#ed3bfbdf9d9e577e8d4cff937b053a0e429a4cd7" +dependencies = [ + "bitflags", + "fallible-iterator", + "fallible-streaming-iterator", + "hashlink", + "libsqlite3-sys", + "memchr", + "smallvec", +] + [[package]] name = "rustls" version = "0.19.0" @@ -450,6 +518,12 @@ dependencies = [ "libc", ] +[[package]] +name = "smallvec" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" + [[package]] name = "spin" version = "0.5.2" @@ -484,6 +558,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "unicode-segmentation" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0d2e7be6ae3a5fa87eed5fb451aff96f2573d2694942e40543ae0bbe19c796" + [[package]] name = "unicode-width" version = "0.1.8" @@ -502,6 +582,12 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" +[[package]] +name = "vcpkg" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b00bca6106a5e23f3eee943593759b7fcddb00554332e856d990c893966879fb" + [[package]] name = "version_check" version = "0.9.3" diff --git a/Cargo.toml b/Cargo.toml index b447c24..db2f71b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,3 +15,5 @@ mailparse = "0.13.2" rustls-connector = "0.13.1" ascii_table = { git = "https://gitlab.com/arnekeller/ascii-table.git", branch = "master" } chrono = "0.4.19" +# remove when 0.24.3 is released +rusqlite = { git = "https://github.com/rusqlite/rusqlite", branch = "master", features = ["bundled"] } diff --git a/src/bin/fetch.rs b/src/bin/fetch.rs index 746b168..6b56b67 100644 --- a/src/bin/fetch.rs +++ b/src/bin/fetch.rs @@ -4,6 +4,8 @@ use itertools::Itertools; use maildir::Maildir; use inboxid::*; +use mailparse::{MailHeaderMap, parse_headers}; +use rusqlite::params; fn main() -> Result<()> { let host = env::var("MAILHOST").expect("missing envvar MAILHOST"); @@ -23,6 +25,7 @@ fn fetch_inbox_top( mailbox: &str, maildir: Maildir, ) -> Result<()> { + let db = get_db()?; let mut imap_session = connect(host, port, user, password)?; println!("getting capabilities.."); let caps = imap_session.capabilities()?; @@ -63,14 +66,22 @@ fn fetch_inbox_top( let messages = imap_session.uid_fetch(&fetch_range, "RFC822")?; let mut largest_uid = prev_uid; + + let mut save_mail = db.prepare("INSERT INTO mail VALUES (?,?,?)")?; + for mail in messages.iter() { let uid = mail.uid.unwrap(); largest_uid = cmp::max(largest_uid, uid); println!("mail {:?}", uid); let id = format!("{}_{}", uid_validity, uid); + let uid = ((uid_validity as u64) << 32) | uid as u64; if !maildir.exists(&id).unwrap_or(false) { let mail_data = mail.body().unwrap_or_default(); maildir.store_new_with_id(&id, mail_data)?; + + let headers = parse_headers(&mail_data)?.0; + let message_id = headers.get_all_values("Message-ID").join(" "); + save_mail.execute(params![mailbox, uid, message_id])?; } } let uid = cmp::max(uid_next - 1, largest_uid); diff --git a/src/lib.rs b/src/lib.rs index d8a002b..fd47afe 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,6 +2,7 @@ use std::{env, net::TcpStream}; use imap::Session; use maildir::Maildir; +use rusqlite::{Connection, params}; use rustls_connector::{RustlsConnector, rustls::{ClientSession, StreamOwned}}; pub type Result = std::result::Result>; @@ -28,3 +29,17 @@ pub fn get_maildir(mailbox: &str) -> Result { maildir.create_dirs()?; Ok(maildir) } + +pub fn get_db() -> Result { + let db = env::var("MAILDB").expect("missing envvar MAILDB"); + let conn = Connection::open(&db)?; + + conn.execute(" + CREATE TABLE IF NOT EXISTS mail( + mailbox STRING NOT NULL, + uid INTEGER NOT NULL, + message_id STRING NOT NULL + )", params![])?; + + Ok(conn) +}