diff --git a/Cargo.lock b/Cargo.lock index ff0a61b..957363c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -53,6 +53,12 @@ version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9a60d744a80c30fcb657dfe2c1b22bcb3e814c1a1e3674f32bf5820b570fbff" +[[package]] +name = "arrayref" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" + [[package]] name = "arrayvec" version = "0.4.12" @@ -62,6 +68,12 @@ dependencies = [ "nodrop", ] +[[package]] +name = "arrayvec" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8" + [[package]] name = "ascii_utils" version = "0.9.3" @@ -255,6 +267,17 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" +[[package]] +name = "blake2b_simd" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8fb2d74254a3a0b5cac33ac9f8ed0e44aa50378d9dbb2e5d83bd21ed1dc2c8a" +dependencies = [ + "arrayref", + "arrayvec 0.5.1", + "constant_time_eq", +] + [[package]] name = "block-buffer" version = "0.7.3" @@ -473,6 +496,12 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0dbbb57365263e881e805dc77d94697c9118fd94d8da011240555aa7b23445bd" +[[package]] +name = "constant_time_eq" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" + [[package]] name = "core-foundation" version = "0.7.0" @@ -748,6 +777,28 @@ dependencies = [ "generic-array", ] +[[package]] +name = "directories" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "551a778172a450d7fc12e629ca3b0428d00f6afa9a43da1b630d54604e97371c" +dependencies = [ + "cfg-if", + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afa0b23de8fd801745c471deffa6e12d248f962c9fd4b4c33787b055599bde7b" +dependencies = [ + "cfg-if", + "libc", + "redox_users", + "winapi 0.3.8", +] + [[package]] name = "doc-comment" version = "0.3.3" @@ -1461,7 +1512,7 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7043aa5c05dd34fb73b47acb8c3708eac428de4545ea3682ed2f11293ebd890" dependencies = [ - "arrayvec", + "arrayvec 0.4.12", "cfg-if", "rustc_version", "ryu", @@ -2163,6 +2214,17 @@ version = "0.1.56" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" +[[package]] +name = "redox_users" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09b23093265f8d200fa7b4c2c76297f47e681c655f6f1285a8780d6a022f7431" +dependencies = [ + "getrandom", + "redox_syscall", + "rust-argon2", +] + [[package]] name = "regex" version = "1.3.7" @@ -2291,6 +2353,18 @@ dependencies = [ "time", ] +[[package]] +name = "rust-argon2" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bc8af4bda8e1ff4932523b94d3dd20ee30a87232323eda55903ffd71d2fb017" +dependencies = [ + "base64 0.11.0", + "blake2b_simd", + "constant_time_eq", + "crossbeam-utils", +] + [[package]] name = "rustc-demangle" version = "0.1.16" @@ -2647,6 +2721,7 @@ dependencies = [ "anyhow", "dbus", "deltachat", + "directories", "rand 0.7.3", ] diff --git a/Cargo.toml b/Cargo.toml index e1e760f..457165c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,7 @@ license = "MIT" anyhow = "1.0" dbus = "0.8" deltachat = { git = "https://github.com/deltachat/deltachat-core-rust", tag="1.31.0" } +directories = "2.0" rand = "0.7" [profile.release] diff --git a/README.md b/README.md index 34ce26f..4f3a13d 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ Here's where we're at right now: - [x] Connect to DBUS - [x] Advertise enough properties / interfaces to become visible in Empathy - [~] Connect to deltachat-core-rust -- [ ] Set up an account via autoconfiguration +- [x] Set up an account via autoconfiguration - [ ] Set up an account manually - [ ] Contacts handling - [ ] Text messages diff --git a/src/main.rs b/src/main.rs index 0d6114d..9c7187f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -23,8 +23,8 @@ fn run() -> Result<()> { let f = Factory::new_fn::<()>(); let mut tree = f.tree(()); - let cm_iface = telepathy::connection_manager_server(&f, (), move |_| cm_rc.clone() ); - let proto_iface = telepathy::protocol_server(&f, (), move |_| proto_rc.clone() ); + let cm_iface = telepathy::connection_manager_server(&f, (), move |_| cm_rc.clone()); + let proto_iface = telepathy::protocol_server(&f, (), move |_| proto_rc.clone()); tree = tree.add( f.object_path(CM_OBJECT_PATH, ()) diff --git a/src/padfoot/connection.rs b/src/padfoot/connection.rs index 640d128..9c028fa 100644 --- a/src/padfoot/connection.rs +++ b/src/padfoot/connection.rs @@ -1,7 +1,15 @@ use crate::telepathy; use dbus::blocking::{stdintf::org_freedesktop_dbus::RequestNameReply, LocalConnection}; use dbus::tree; + +use dc::config::Config; +use dc::context::Context; +use dc::Event; +use deltachat as dc; + use std::collections::HashMap; +use std::sync::{Arc, RwLock}; +use std::thread; use std::time::Duration; pub const CONN_BUS_NAME: &'static str = "org.freedesktop.Telepathy.Connection.padfoot.delta"; @@ -10,8 +18,7 @@ pub const CONN_OBJECT_PATH: &'static str = "/org/freedesktop/Telepathy/Connectio #[derive(Debug)] pub struct Connection { id: String, - account: String, - password: String, + ctx: Arc>, } fn escape_one(b: u8) -> String { @@ -68,6 +75,70 @@ mod tests { } impl Connection { + pub fn new(params: HashMap<&str, super::Variant>) -> Result { + let err = Err(dbus::tree::MethodErr::no_arg()); + + let acct = match params.get("account") { + Some(variant) => match variant.0.as_str() { + Some(str) => str.to_string(), + None => return err, + }, + None => return err, + }; + + let id = escape(acct.to_owned()); + + let password = match params.get("password") { + Some(variant) => match variant.0.as_str() { + Some(str) => str.to_string(), + None => return err, + }, + None => return err, + }; + + let mut dbfile = directories::ProjectDirs::from("gs", "ur", "telepathy-padfoot") + .ok_or_else(|| tree::MethodErr::no_arg()) + .and_then(|p| Ok(p.data_local_dir().to_path_buf()))?; + + dbfile.push(&id); + dbfile.push("db.sqlite3"); + + // FIXME: how to give it access to the connection (initialized later)? + let id2 = id.clone(); + let f = move |_c: &Context, e: Event| { + match e { + Event::Info(msg) => println!("Connection<{}>: INFO: {}", id2, msg), + Event::Warning(msg) => println!("Connection<{}>: WARN : {}", id2, msg), + Event::Error(msg) | Event::ErrorNetwork(msg) | Event::ErrorSelfNotInGroup(msg) => { + println!("Connection<{}>: ERR : {}", id2, msg) + } + _ => println!("Connection<{}>: unhandled event received: {:?}", id2, e), + }; + }; + + let ctx = + Context::new(Box::new(f), "telepathy-padfoot".to_string(), dbfile).map_err(|e| { + println!("Connection<{}>: couldn't get delta context: {}", id, e); + tree::MethodErr::no_arg() // FIXME: better error handling + })?; + + ctx.set_config(Config::Addr, Some(&acct)) + .map_err(|_e| tree::MethodErr::no_arg())?; + ctx.set_config(Config::MailPw, Some(&password)) + .map_err(|_e| tree::MethodErr::no_arg())?; + ctx.set_config(Config::SentboxWatch, Some(&"Sent")) + .map_err(|_e| tree::MethodErr::no_arg())?; + + if !ctx.is_configured() { + ctx.configure(); + }; + + Ok(Connection { + id: id, + ctx: Arc::new(RwLock::new(ctx)), + }) + } + // This is run inside its own thread // // FIXME: running several +process+ loops sure is convenient, but it also @@ -75,6 +146,7 @@ impl Connection { pub fn run(self) { let bus = self.bus(); let path = self.path(); + let ctx = self.ctx.clone(); let c_rc = std::rc::Rc::new(self); let f = tree::Factory::new_fn::<()>(); @@ -108,7 +180,60 @@ impl Connection { _ => println!("{} listening on {}", c.unique_name(), bus), // All other responses we can get are a success }; - loop { + // Set up delta jobs last in case registering to DBUS fails + // "Borrowed" from https://github.com/deltachat/deltachat-core-rust/blob/master/examples/simple.rs + let running = Arc::new(RwLock::new(true)); + + let inbox_ctx = ctx.clone(); + let r1 = running.clone(); + let _t1 = thread::spawn(move || { + while *r1.read().unwrap() { + dc::job::perform_inbox_jobs(&inbox_ctx.read().unwrap()); + if *r1.read().unwrap() { + dc::job::perform_inbox_fetch(&inbox_ctx.read().unwrap()); + + if *r1.read().unwrap() { + dc::job::perform_inbox_idle(&inbox_ctx.read().unwrap()); + } + } + } + }); + + let smtp_ctx = ctx.clone(); + let r1 = running.clone(); + let _t2 = thread::spawn(move || { + while *r1.read().unwrap() { + dc::job::perform_smtp_jobs(&smtp_ctx.read().unwrap()); + if *r1.read().unwrap() { + dc::job::perform_smtp_idle(&smtp_ctx.read().unwrap()); + } + } + }); + + let mvbox_ctx = ctx.clone(); + let r1 = running.clone(); + let _t3 = thread::spawn(move || { + while *r1.read().unwrap() { + dc::job::perform_mvbox_fetch(&mvbox_ctx.read().unwrap()); + if *r1.read().unwrap() { + dc::job::perform_mvbox_idle(&mvbox_ctx.read().unwrap()); + } + } + }); + + let sentbox_ctx = ctx.clone(); + let r1 = running.clone(); + let _t4 = thread::spawn(move || { + while *r1.read().unwrap() { + dc::job::perform_sentbox_fetch(&sentbox_ctx.read().unwrap()); + if *r1.read().unwrap() { + dc::job::perform_sentbox_idle(&sentbox_ctx.read().unwrap()); + } + } + }); + + let r1 = running.clone(); + while *r1.read().unwrap() { match c.process(Duration::from_millis(100)) { Err(e) => { println!("Error processing: {}", e); @@ -121,34 +246,6 @@ impl Connection { } } - pub fn new(params: HashMap<&str, super::Variant>) -> Result { - let err = Err(dbus::tree::MethodErr::no_arg()); - - let acct = match params.get("account") { - Some(variant) => match variant.0.as_str() { - Some(str) => str.to_string(), - None => return err, - }, - None => return err, - }; - - let id = escape(acct.to_owned()); - - let password = match params.get("password") { - Some(variant) => match variant.0.as_str() { - Some(str) => str.to_string(), - None => return err, - }, - None => return err, - }; - - Ok(Connection { - id: id, - account: acct, - password: password, - }) - } - pub fn bus(&self) -> String { CONN_BUS_NAME.to_owned() + "." + &self.id } @@ -159,7 +256,9 @@ impl Connection { } impl AsRef for std::rc::Rc { - fn as_ref(&self) -> &(dyn telepathy::Connection + 'static) { &**self } + fn as_ref(&self) -> &(dyn telepathy::Connection + 'static) { + &**self + } } impl telepathy::Connection for Connection { diff --git a/src/padfoot/connection_manager.rs b/src/padfoot/connection_manager.rs index cfe66cd..3876ec2 100644 --- a/src/padfoot/connection_manager.rs +++ b/src/padfoot/connection_manager.rs @@ -15,17 +15,22 @@ pub struct ConnectionManager { } impl AsRef for std::rc::Rc { - fn as_ref(&self) -> &(dyn telepathy::ConnectionManager + 'static) { &**self } + fn as_ref(&self) -> &(dyn telepathy::ConnectionManager + 'static) { + &**self + } } impl ConnectionManager { pub fn new() -> (Self, mpsc::Receiver) { let (msg_s, msg_r) = std::sync::mpsc::channel::(); - (ConnectionManager { - conns: std::sync::Mutex::new(HashSet::::new()), - sender: msg_s, - }, msg_r) + ( + ConnectionManager { + conns: std::sync::Mutex::new(HashSet::::new()), + sender: msg_s, + }, + msg_r, + ) } fn create_connection( @@ -64,7 +69,9 @@ impl ConnectionManager { let dbus_cm_path = dbus::strings::Path::new(CM_OBJECT_PATH.to_string()) .expect("Object path should meet DBUS requirements"); - self.sender.send(sig.to_emit_message(&dbus_cm_path)).expect("send signal"); + self.sender + .send(sig.to_emit_message(&dbus_cm_path)) + .expect("send signal"); // The bus name *must* be org.freedesktop.Telepathy.Connection.padfoot.delta. Ok((bus, dbus_conn_path)) @@ -92,7 +99,7 @@ impl telepathy::ConnectionManager for ConnectionManager { protocol: &str, params: HashMap<&str, super::Variant>, ) -> Result<(String, dbus::Path<'static>), tree::MethodErr> { - println!("CM::request_connection({}, {:?})", protocol, params); + println!("CM::request_connection({}, ...)", protocol); match protocol { super::PROTO_NAME => self.create_connection(params), diff --git a/src/padfoot/protocol.rs b/src/padfoot/protocol.rs index ccdb233..e0ca2cd 100644 --- a/src/padfoot/protocol.rs +++ b/src/padfoot/protocol.rs @@ -76,7 +76,9 @@ pub fn requestables() -> Vec { } impl AsRef for std::rc::Rc { - fn as_ref(&self) -> &(dyn telepathy::Protocol + 'static) { &**self } + fn as_ref(&self) -> &(dyn telepathy::Protocol + 'static) { + &**self + } } impl telepathy::Protocol for Protocol {