use crate::telepathy; use dbus::message::SignalArgs; use dbus::tree::MethodErr; use deltachat as dc; use std::thread; use super::Connection; #[derive(Debug, PartialEq, Eq)] pub enum ConnState { Initial, Connected, Disconnected, } pub fn connection_interfaces() -> Vec { vec![ "org.freedesktop.Telepathy.Connection".to_string(), "org.freedesktop.Telepathy.Connection.Interface.Avatars".to_string(), "org.freedesktop.Telepathy.Connection.Interface.Contacts".to_string(), // "org.freedesktop.Telepathy.Connection.Interface.ContactList".to_string(), "org.freedesktop.Telepathy.Connection.Interface.Requests".to_string(), "org.freedesktop.Telepathy.Connection.Interface.SimplePresence".to_string(), ] } impl AsRef for std::rc::Rc { fn as_ref(&self) -> &(dyn telepathy::Connection + 'static) { &**self } } impl telepathy::Connection for Connection { // In connect(), we start the threads that drive the deltachat context fn connect(&self) -> Result<(), MethodErr> { println!("Connection<{}>::connect()", self.id()); let inbox_ctx = self.ctx.clone(); let state = self.state.clone(); let id = self.id(); let _inbox_thread = thread::spawn(move || { while *state.read().unwrap() != ConnState::Disconnected { dc::job::perform_inbox_jobs(&inbox_ctx.read().unwrap()); if *state.read().unwrap() != ConnState::Disconnected { dc::job::perform_inbox_fetch(&inbox_ctx.read().unwrap()); if *state.read().unwrap() != ConnState::Disconnected { dc::job::perform_inbox_idle(&inbox_ctx.read().unwrap()); } } } println!("Connection<{}>::connect(): inbox thread exited", id); }); let smtp_ctx = self.ctx.clone(); let state = self.state.clone(); let id = self.id(); let _smtp_thread = thread::spawn(move || { while *state.read().unwrap() != ConnState::Disconnected { dc::job::perform_smtp_jobs(&smtp_ctx.read().unwrap()); if *state.read().unwrap() != ConnState::Disconnected { dc::job::perform_smtp_idle(&smtp_ctx.read().unwrap()); } } println!("Connection<{}>::connect(): smtp thread exited", id); }); let mvbox_ctx = self.ctx.clone(); let state = self.state.clone(); let id = self.id(); let _mvbox_thread = thread::spawn(move || { while *state.read().unwrap() != ConnState::Disconnected { dc::job::perform_mvbox_fetch(&mvbox_ctx.read().unwrap()); if *state.read().unwrap() != ConnState::Disconnected { dc::job::perform_mvbox_idle(&mvbox_ctx.read().unwrap()); } } println!("Connection<{}>::connect(): mvbox thread exited", id); }); let sentbox_ctx = self.ctx.clone(); let state = self.state.clone(); let id = self.id(); let _sentbox_thread = thread::spawn(move || { while *state.read().unwrap() != ConnState::Disconnected { dc::job::perform_sentbox_fetch(&sentbox_ctx.read().unwrap()); if *state.read().unwrap() != ConnState::Disconnected { dc::job::perform_sentbox_idle(&sentbox_ctx.read().unwrap()); } } println!("Connection<{}>::connect(): sentbox thread exited", id); }); // Just pretend to be connected all the time for now. Tracking IMAP+SMTP // state is a pain let state = self.state.clone(); let mut w = state.write().unwrap(); *w = ConnState::Connected; // Emit a StatusChanged signal for the benefit of others, but the caller // learns from our RPC response let sig = telepathy::ConnectionStatusChanged { status: 0, // Connected reason: 1, // Requested }; let dbus_conn_path = dbus::strings::Path::new(self.path()) .expect("Object path should meet DBUS requirements"); self.msgq .lock() .unwrap() .push_back(sig.to_emit_message(&dbus_conn_path)); Ok(()) } fn disconnect(&self) -> Result<(), MethodErr> { println!("Connection<{}>::disconnect()", self.id()); let ctx = self.ctx.read().unwrap(); let state = self.state.clone(); let mut w = state.write().unwrap(); *w = ConnState::Disconnected; dc::job::interrupt_inbox_idle(&ctx); dc::job::interrupt_smtp_idle(&ctx); dc::job::interrupt_sentbox_idle(&ctx); dc::job::interrupt_mvbox_idle(&ctx); // FIXME: we need to signal to the CM that they should remove the // connection from the active list Ok(()) } fn interfaces(&self) -> Result, MethodErr> { println!("Connection<{}>::interfaces()", self.id()); self.get_interfaces() } fn get_interfaces(&self) -> Result, MethodErr> { println!("Connection<{}>::get_interfaces()", self.id()); Ok(connection_interfaces()) } fn get_protocol(&self) -> Result { println!("Connection<{}>::get_protocol()", self.id()); Ok(crate::padfoot::PROTO_NAME.to_string()) } fn self_handle(&self) -> Result { println!("Connection<{}>::self_handle()", self.id()); self.get_self_handle() } fn get_self_handle(&self) -> Result { println!("Connection<{}>::get_self_handle()", self.id()); Ok(dc::constants::DC_CONTACT_ID_SELF) } fn status(&self) -> Result { println!("Connection<{}>::status()", self.id()); self.get_status() } fn get_status(&self) -> Result { match *self.state.clone().read().unwrap() { ConnState::Initial | ConnState::Disconnected => Ok(2), ConnState::Connected => Ok(0), } } fn hold_handles(&self, handle_type: u32, handles: Vec) -> Result<(), MethodErr> { println!( "Connection<{}>::hold_handles({}, {:?})", self.id(), handle_type, handles ); // Since HasImmortalHandles is true, this doesn't need to do anything Ok(()) } fn inspect_handles( &self, handle_type: u32, handles: Vec, ) -> Result, MethodErr> { println!( "Connection<{}>::inspect_handles({}, {:?})", self.id(), handle_type, handles ); Err(MethodErr::no_arg()) // FIXME: should be NotImplemented? } fn list_channels(&self) -> Result, String, u32, u32)>, MethodErr> { println!("Connection<{}>::list_channels()", self.id()); Err(MethodErr::no_arg()) // FIXME: should be NotImplemented? } fn release_handles(&self, handle_type: u32, handles: Vec) -> Result<(), MethodErr> { println!( "Connection<{}>::release_handles({}, {:?})", self.id(), handle_type, handles ); // Since HasImmortalHandles is true, we don't need to do anything Ok(()) } fn request_channel( &self, type_: &str, handle_type: u32, handle: u32, suppress_handler: bool, ) -> Result, MethodErr> { println!( "Connection<{}>::request_channel({}, {}, {}, {})", self.id(), type_, handle_type, handle, suppress_handler ); Err(MethodErr::no_arg()) // FIXME: should be NotImplemented? } fn request_handles( &self, handle_type: u32, identifiers: Vec<&str>, ) -> Result, MethodErr> { println!( "Connection<{}>::request_handles({}, {:?})", self.id(), handle_type, identifiers ); Err(MethodErr::no_arg()) // FIXME: should be NotImplemented? } fn add_client_interest(&self, tokens: Vec<&str>) -> Result<(), MethodErr> { println!( "Connection<{}>::add_client_interest({:?})", self.id(), tokens ); Err(MethodErr::no_arg()) // FIXME: should be NotImplemented? } fn remove_client_interest(&self, tokens: Vec<&str>) -> Result<(), MethodErr> { println!( "Connection<{}>::remove_client_interest({:?})", self.id(), tokens ); Err(MethodErr::no_arg()) // FIXME: should be NotImplemented? } fn self_id(&self) -> Result { println!("Connection<{}>::self_id()", self.id()); let contact = match dc::contact::Contact::get_by_id( &self.ctx.read().unwrap(), dc::constants::DC_CONTACT_ID_SELF, ) { Ok(c) => c, Err(e) => { println!(" err: {}", e); return Err(MethodErr::no_arg()); } }; Ok(contact.get_addr().to_string()) } fn has_immortal_handles(&self) -> Result { println!("Connection<{}>::has_immortal_handles()", self.id()); Ok(true) } }