315 lines
10 KiB
Rust
315 lines
10 KiB
Rust
mod avatars;
|
|
pub use self::avatars::*;
|
|
|
|
#[allow(clippy::module_inception)]
|
|
mod connection;
|
|
pub use self::connection::*;
|
|
|
|
mod contacts;
|
|
pub use self::contacts::*;
|
|
|
|
mod contact_list;
|
|
pub use self::contact_list::*;
|
|
|
|
mod escape;
|
|
use self::escape::escape;
|
|
|
|
mod requests;
|
|
pub use self::requests::*;
|
|
|
|
mod simple_presence;
|
|
pub use self::simple_presence::*;
|
|
|
|
use crate::telepathy;
|
|
|
|
use dbus::blocking::{stdintf::org_freedesktop_dbus::RequestNameReply, LocalConnection};
|
|
use dbus::channel::Sender;
|
|
use dbus::tree::MethodErr;
|
|
|
|
use dc::config::Config;
|
|
use dc::context::Context;
|
|
use dc::Event;
|
|
use deltachat as dc;
|
|
|
|
use std::collections::{HashMap, HashSet, VecDeque};
|
|
use std::sync::{mpsc, Arc, Mutex, RwLock};
|
|
use std::time::Duration;
|
|
|
|
pub const CONN_BUS_NAME: &str = "org.freedesktop.Telepathy.Connection.padfoot.delta";
|
|
pub const CONN_OBJECT_PATH: &str = "/org/freedesktop/Telepathy/Connection/padfoot/delta";
|
|
|
|
#[derive(Debug)]
|
|
// A Deltachast connection uses email addresses as handles, and delta's Db IDs
|
|
pub struct Connection {
|
|
// Remove ourselves from this when done
|
|
conns: Arc<Mutex<HashSet<String>>>,
|
|
|
|
ctx: Arc<RwLock<Context>>,
|
|
settings: ConnSettings,
|
|
state: Arc<RwLock<ConnState>>,
|
|
|
|
// Used for sending out messages
|
|
msgq: Arc<Mutex<VecDeque<dbus::Message>>>,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct ConnSettings {
|
|
account: String,
|
|
password: String,
|
|
id: String,
|
|
}
|
|
|
|
impl ConnSettings {
|
|
pub fn from_params(params: HashMap<&str, super::Variant>) -> Result<Self, MethodErr> {
|
|
let err = Err(MethodErr::no_arg());
|
|
|
|
let account = 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(account.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(Self {
|
|
account,
|
|
password,
|
|
id,
|
|
})
|
|
}
|
|
|
|
pub fn id(&self) -> String {
|
|
self.id.to_owned()
|
|
}
|
|
|
|
pub fn bus(&self) -> String {
|
|
CONN_BUS_NAME.to_owned() + "." + &self.id
|
|
}
|
|
|
|
pub fn path(&self) -> String {
|
|
CONN_OBJECT_PATH.to_owned() + "/" + &self.id
|
|
}
|
|
}
|
|
|
|
impl Connection {
|
|
pub fn new(
|
|
settings: ConnSettings,
|
|
conns: Arc<Mutex<HashSet<String>>>,
|
|
) -> Result<Self, MethodErr> {
|
|
let mut dbfile = directories::ProjectDirs::from("gs", "ur", "telepathy-padfoot")
|
|
.ok_or_else(MethodErr::no_arg)
|
|
.and_then(|p| Ok(p.data_local_dir().to_path_buf()))?;
|
|
|
|
dbfile.push(settings.id());
|
|
dbfile.push("db.sqlite3");
|
|
|
|
// FIXME: how to give it access to the connection (initialized later)?
|
|
let msgq = Arc::new(Mutex::new(VecDeque::<dbus::Message>::new()));
|
|
|
|
let id = settings.id();
|
|
// Use this if we need to send messages in response to DC events:
|
|
// let msgq2 = msgq.clone();
|
|
let f = move |_c: &Context, e: Event| {
|
|
match e {
|
|
Event::Info(msg) => println!("Connection<{}>: INFO: {}", id, msg),
|
|
Event::Warning(msg) => println!("Connection<{}>: WARN : {}", id, msg),
|
|
Event::Error(msg) | Event::ErrorNetwork(msg) | Event::ErrorSelfNotInGroup(msg) => {
|
|
println!("Connection<{}>: ERR : {}", id, msg)
|
|
}
|
|
Event::ConfigureProgress(progress) => {
|
|
println!("Connection<{}>: Configuration progress: {}", id, progress)
|
|
}
|
|
Event::ImapConnected(msg) | Event::SmtpConnected(msg) => {
|
|
println!("Connection<{}>: Network: {}", id, msg);
|
|
}
|
|
/* Unhandled messages:
|
|
SmtpMessageSent(String),
|
|
ImapMessageDeleted(String),
|
|
ImapMessageMoved(String),
|
|
ImapFolderEmptied(String),
|
|
NewBlobFile(String),
|
|
DeletedBlobFile(String),
|
|
MsgsChanged
|
|
IncomingMsg
|
|
MsgDelivered
|
|
MsgFailed
|
|
MsgRead
|
|
ChatModified(ChatId),
|
|
ContactsChanged(Option<u32>),
|
|
LocationChanged(Option<u32>),
|
|
ImexProgress(usize),
|
|
ImexFileWritten(PathBuf),
|
|
SecurejoinInviterProgress
|
|
SecurejoinJoinerProgress
|
|
*/
|
|
_ => println!("Connection<{}>: unhandled event received: {:?}", id, e),
|
|
};
|
|
};
|
|
|
|
let ctx =
|
|
Context::new(Box::new(f), "telepathy-padfoot".to_string(), dbfile).map_err(|e| {
|
|
println!(
|
|
"Connection<{}>::new(): couldn't get delta context: {}",
|
|
settings.id(),
|
|
e
|
|
);
|
|
MethodErr::no_arg() // FIXME: better error handling
|
|
})?;
|
|
|
|
ctx.set_config(Config::Addr, Some(&settings.account))
|
|
.map_err(|_e| MethodErr::no_arg())?;
|
|
ctx.set_config(Config::MailPw, Some(&settings.password))
|
|
.map_err(|_e| MethodErr::no_arg())?;
|
|
ctx.set_config(Config::SentboxWatch, Some(&"Sent"))
|
|
.map_err(|_e| MethodErr::no_arg())?;
|
|
|
|
if !ctx.is_configured() {
|
|
ctx.configure();
|
|
};
|
|
|
|
Ok(Connection {
|
|
conns,
|
|
settings,
|
|
msgq,
|
|
ctx: Arc::new(RwLock::new(ctx)),
|
|
state: Arc::new(RwLock::new(ConnState::Initial)),
|
|
})
|
|
}
|
|
|
|
// This should be run inside its own thread. It will signal via the channel
|
|
// once the main loop is ready
|
|
//
|
|
// FIXME: running several +process+ loops sure is convenient, but it also
|
|
// seems inefficient...
|
|
pub fn run(self, signal: mpsc::Sender<Option<MethodErr>>) {
|
|
let id = self.id();
|
|
let bus = self.bus();
|
|
let path = self.path();
|
|
|
|
let conns = self.conns.clone();
|
|
let msgq = self.msgq.clone();
|
|
let state = self.state.clone();
|
|
let tree = self.build_tree();
|
|
|
|
// Setup DBus connection
|
|
let mut c = match LocalConnection::new_session() {
|
|
Ok(c) => c,
|
|
Err(e) => {
|
|
println!("Failed to establish DBUS session for {}: {}", bus, e);
|
|
return; // Leave early
|
|
}
|
|
};
|
|
|
|
tree.start_receive(&c);
|
|
|
|
match c.request_name(bus.clone(), false, false, true) {
|
|
Ok(RequestNameReply::Exists) => {
|
|
println!("Another process is already registered on {}", bus);
|
|
signal.send(Some(MethodErr::no_arg())).unwrap();
|
|
return;
|
|
}
|
|
Err(e) => {
|
|
println!("Failed to register {}: {}", bus, e);
|
|
signal.send(Some(MethodErr::no_arg())).unwrap();
|
|
return;
|
|
}
|
|
_ => {
|
|
// All other responses we can get are a success. We are now on
|
|
// the message bus, so the caller can proceed
|
|
println!("{} listening on {}", c.unique_name(), bus);
|
|
signal.send(None).unwrap();
|
|
}
|
|
};
|
|
|
|
// 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
|
|
while *state.read().unwrap() != ConnState::Disconnected {
|
|
if let Err(e) = c.process(Duration::from_millis(100)) {
|
|
println!("Error processing: {}", e);
|
|
|
|
break;
|
|
};
|
|
|
|
// Spend a bit of time sending any outgoing messages - signals, mostly
|
|
while let Some(msg) = msgq.lock().unwrap().pop_front() {
|
|
print!("Connection<{}>: Sending message...", id);
|
|
|
|
match c.send(msg) {
|
|
Err(e) => println!("error! {:?}", e), // FIXME: handle error better?
|
|
_ => println!("OK!"),
|
|
}
|
|
}
|
|
}
|
|
|
|
// TODO: join on threads started in connect!
|
|
|
|
let mut conns = conns.lock().expect("Mutex access");
|
|
conns.remove(&path);
|
|
}
|
|
|
|
pub fn id(&self) -> String {
|
|
self.settings.id.to_string()
|
|
}
|
|
|
|
pub fn bus(&self) -> String {
|
|
self.settings.bus()
|
|
}
|
|
|
|
pub fn path(&self) -> String {
|
|
self.settings.path()
|
|
}
|
|
|
|
fn build_tree(self) -> dbus::tree::Tree<dbus::tree::MTFn, ()> {
|
|
let path = self.path();
|
|
let c_rc = std::rc::Rc::new(self);
|
|
let f = dbus::tree::Factory::new_fn::<()>();
|
|
let mut tree = f.tree(());
|
|
|
|
let c_rc1 = c_rc.clone();
|
|
let conn_iface = telepathy::connection_server(&f, (), move |_| c_rc1.clone());
|
|
|
|
let c_rc2 = c_rc.clone();
|
|
let avatars_iface =
|
|
telepathy::connection_interface_avatars_server(&f, (), move |_| c_rc2.clone());
|
|
|
|
let c_rc3 = c_rc.clone();
|
|
let contacts_iface =
|
|
telepathy::connection_interface_contacts_server(&f, (), move |_| c_rc3.clone());
|
|
|
|
let _c_rc4 = c_rc.clone();
|
|
let _contact_list_iface =
|
|
telepathy::connection_interface_contact_list_server(&f, (), move |_| _c_rc4.clone());
|
|
|
|
let c_rc5 = c_rc.clone();
|
|
let requests_iface =
|
|
telepathy::connection_interface_requests_server(&f, (), move |_| c_rc5.clone());
|
|
|
|
let simple_presence_iface =
|
|
telepathy::connection_interface_simple_presence_server(&f, (), move |_| c_rc.clone());
|
|
|
|
tree = tree.add(
|
|
f.object_path(path, ())
|
|
.introspectable()
|
|
.add(conn_iface)
|
|
.add(avatars_iface)
|
|
.add(contacts_iface)
|
|
// .add(contact_list_iface)
|
|
.add(requests_iface)
|
|
.add(simple_presence_iface),
|
|
);
|
|
tree = tree.add(f.object_path("/", ()).introspectable());
|
|
|
|
tree
|
|
}
|
|
}
|