Add a very broken ContactList + outgoing channel implementation

Right now, messages don't show on the padfoot side any more, but they
do get sent and received successfully on the other side, and empathy
can manage contacts now, so I'm calling it an overall win.
This commit is contained in:
2020-05-21 02:30:47 +01:00
parent aae7607c7f
commit 15174ea03f
7 changed files with 245 additions and 66 deletions

View File

@@ -8,10 +8,12 @@ pub use messages::*;
mod type_text;
pub use type_text::*;
use crate::padfoot::DbusAction;
use crate::padfoot::{var_bool, var_str, var_str_vec, var_u32, DbusAction, VarArg};
use crate::telepathy;
use deltachat as dc;
use std::collections::HashMap;
use std::sync::{mpsc, Arc, RwLock};
type Result<T> = std::result::Result<T, dbus::tree::MethodErr>;
@@ -34,6 +36,7 @@ pub struct Channel {
actq: mpsc::Sender<DbusAction>,
chat_id: dc::chat::ChatId,
ctx: Arc<RwLock<dc::context::Context>>,
initiator_handle: u32,
path: dbus::Path<'static>,
requested: bool,
target_handle: u32, // Who we're talking to
@@ -49,6 +52,7 @@ impl Channel {
actq: mpsc::Sender<DbusAction>,
chat_id: dc::chat::ChatId,
ctx: Arc<RwLock<dc::context::Context>>,
initiator_handle: u32,
path: dbus::Path<'static>,
requested: bool,
target_handle: u32,
@@ -57,12 +61,46 @@ impl Channel {
actq,
chat_id,
ctx,
initiator_handle,
path,
requested,
target_handle,
}
}
// FIXME: we should be able to introspect this already???
pub fn chan_props(&self) -> HashMap<String, VarArg> {
let mut out = HashMap::<String, VarArg>::new();
out.insert(
"org.freedesktop.Telepathy.Channel.ChannelType".to_string(),
var_str(self.chan_type()),
);
out.insert(
"org.freedesktop.Telepathy.Channel.TargetHandleType".to_string(),
var_u32(self.handle_type()),
);
out.insert(
"org.freedesktop.Telepathy.Channel.TargetHandle".to_string(),
var_u32(self.target_handle),
);
out.insert(
"org.freedesktop.Telepathy.Channel.TargetID".to_string(),
var_str(self.target_contact().unwrap().get_addr().to_string()),
);
out.insert(
"org.freedesktop.Telepathy.Channel.Requested".to_string(),
var_bool(self.requested),
);
out.insert(
"org.freedesktop.Telepathy.Channel.Interfaces".to_string(),
var_str_vec(vec![
"org.freedesktop.Telepathy.Channel.Interface.Messages".to_string()
]),
);
out
}
pub fn path(&self) -> dbus::Path<'static> {
self.path.clone()
}
@@ -88,7 +126,7 @@ impl Channel {
pub fn initiator_contact(&self) -> Option<dc::contact::Contact> {
let ctx = self.ctx.read().unwrap();
dc::contact::Contact::get_by_id(&ctx, self.handle()).ok() // FIXME: this will be wrong for outbound channels
dc::contact::Contact::get_by_id(&ctx, self.initiator_handle).ok() // FIXME: this will be wrong for outbound channels
}
pub fn requested(&self) -> bool {

View File

@@ -20,7 +20,7 @@ pub use self::requests::*;
mod simple_presence;
pub use self::simple_presence::*;
use crate::padfoot::{convert_msg, var_bool, var_str, var_str_vec, var_u32, Channel, VarArg};
use crate::padfoot::{convert_msg, Channel, VarArg};
use crate::telepathy;
use dbus::blocking::{stdintf::org_freedesktop_dbus::RequestNameReply, LocalConnection};
@@ -317,12 +317,11 @@ impl Connection {
}
}
DbusAction::NewChannel(channel) => {
let requested = channel.requested();
let chan_type = channel.chan_type();
let handle_type = channel.handle_type();
let handle = channel.handle();
let target_id = channel.target_contact().unwrap().get_addr().to_string();
let chan_path = channel.path().clone();
let chan_props = channel.chan_props();
let rc_channel = Arc::new(channel);
println!("*** Creating channel {}", chan_path);
@@ -337,34 +336,6 @@ impl Connection {
t2.lock().unwrap().insert(op);
let mut chan_props = HashMap::<String, VarArg>::new();
chan_props.insert(
"org.freedesktop.Telepathy.Channel.ChannelType".to_string(),
var_str(chan_type.clone()),
);
chan_props.insert(
"org.freedesktop.Telepathy.Channel.TargetHandleType".to_string(),
var_u32(handle_type),
);
chan_props.insert(
"org.freedesktop.Telepathy.Channel.TargetHandle".to_string(),
var_u32(handle),
);
chan_props.insert(
"org.freedesktop.Telepathy.Channel.TargetID".to_string(),
var_str(target_id),
);
chan_props.insert(
"org.freedesktop.Telepathy.Channel.Requested".to_string(),
var_bool(requested),
);
chan_props.insert(
"org.freedesktop.Telepathy.Channel.Interfaces".to_string(),
var_str_vec(vec![
"org.freedesktop.Telepathy.Channel.Interface.Messages".to_string(),
]),
);
let requests_sig = telepathy::ConnectionInterfaceRequestsNewChannels {
channels: vec![(chan_path.clone(), chan_props)],
};
@@ -403,12 +374,7 @@ impl Connection {
DbusAction::IncomingMessage(chat_id, msg_id) => {
println!("*** Incoming message: {} {}", chat_id, msg_id);
// TODO: check if we have a channel for the chat
let chan_path = dbus::strings::Path::new(format!(
"{}/{}",
path.clone(),
chat_id.to_u32()
))
.unwrap();
let chan_path = Connection::build_channel_path(path.clone(), chat_id);
let c2 = Arc::clone(&chans);
let chans = c2.read().unwrap();
@@ -458,9 +424,10 @@ impl Connection {
actq.clone(),
chat_id,
ctx.clone(),
*handle, // initiator
chan_path,
false, // FIXME: this needs to handle requested channels
*handle,
dc::constants::DC_CONTACT_ID_SELF, // target is us
);
actq.send(DbusAction::NewChannel(chan)).unwrap();
actq.send(act).unwrap();
@@ -510,6 +477,15 @@ impl Connection {
self.settings.path()
}
fn build_channel_path(
path: dbus::Path<'static>,
chat_id: dc::chat::ChatId,
) -> dbus::strings::Path<'static> {
let path = format!("{}/{}", path, chat_id.to_u32());
dbus::strings::Path::new(path).expect("Must be valid")
}
fn build_tree(self) -> Arc<Mutex<dbus::tree::Tree<dbus::tree::MTFn, ()>>> {
let path = self.path();
let c_rc = std::rc::Rc::new(self);
@@ -528,7 +504,7 @@ impl Connection {
telepathy::connection_interface_contacts_server(&f, (), move |_| c_rc3.clone());
let _c_rc4 = c_rc.clone();
let _contact_list_iface =
let contact_list_iface =
telepathy::connection_interface_contact_list_server(&f, (), move |_| _c_rc4.clone());
let c_rc5 = c_rc.clone();
@@ -544,7 +520,7 @@ impl Connection {
.add(conn_iface)
.add(avatars_iface)
.add(contacts_iface)
// .add(contact_list_iface)
.add(contact_list_iface)
.add(requests_iface)
.add(simple_presence_iface),
);

View File

@@ -1,8 +1,13 @@
use crate::telepathy;
use crate::telepathy::{ConnectionInterfaceContacts, ConnectionInterfaceRequests}; // Non-deprecated channel methods
use dbus::message::SignalArgs;
use dbus::tree::MethodErr;
use dc::contact::Contact;
use deltachat as dc;
use std::collections::HashMap;
use std::convert::TryInto;
use std::thread;
use super::{Connection, DbusAction};
@@ -24,12 +29,18 @@ type ChannelInfo = (
u32, // Handle
);
type ContactSubscription = (
u32, // Subscribe state
u32, // Publish state
String, // Publish-request message
);
pub fn connection_interfaces() -> Vec<String> {
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.ContactList".to_string(),
"org.freedesktop.Telepathy.Connection.Interface.Requests".to_string(),
"org.freedesktop.Telepathy.Connection.Interface.SimplePresence".to_string(),
]
@@ -111,19 +122,49 @@ impl telepathy::Connection for Connection {
let state = self.state.clone();
let mut w = state.write().unwrap();
*w = ConnState::Connected;
let ctx = self.ctx.read().unwrap();
// Emit a StatusChanged signal for the benefit of others, but the caller
// learns from our RPC response
let sig = telepathy::ConnectionStatusChanged {
let connected_sig = telepathy::ConnectionStatusChanged {
status: 0, // Connected
reason: 1, // Requested
};
}
.to_emit_message(&self.path());
self.actq
.send(DbusAction::Signal(sig.to_emit_message(&self.path())))
.unwrap();
self.actq.send(DbusAction::Signal(connected_sig)).unwrap();
self.actq.send(DbusAction::FreshMessages).unwrap();
// If we can, emit signals on connect about the contact list
if let Ok(handles) = Contact::get_all(
&ctx,
(dc::constants::DC_GCL_ADD_SELF as usize)
.try_into()
.unwrap(),
None::<String>,
) {
println!(" HANDLES: {:?}", handles);
let mut changes = HashMap::<u32, ContactSubscription>::new();
for handle in handles {
println!(" *** Handle: {}", handle);
changes.insert(handle, (4, 4, "".to_string())); // FIXME: hardcoded lies
}
// TODO: the old signal is deprecated. The new signal requires us to
// send identifiers with it, which is a bit of a lookup here.
// let cl_sig_new = telepathy::ConnectionInterfaceContactsChangedWithID {
// }.to_emit_message(&self.path());
let cl_sig_old = telepathy::ConnectionInterfaceContactListContactsChanged {
changes,
removals: Vec::new(),
}
.to_emit_message(&self.path());
// self.actq.send(DbusAction::Signal(cl_sig_new)).unwrap();
self.actq.send(DbusAction::Signal(cl_sig_old)).unwrap();
};
Ok(())
}
@@ -287,7 +328,39 @@ impl telepathy::Connection for Connection {
identifiers
);
Err(MethodErr::no_arg()) // FIXME: should be NotImplemented?
match handle_type {
crate::padfoot::HANDLE_TYPE_CONTACT => {
let ctx = self.ctx.read().unwrap();
let mut out = Vec::<u32>::new();
// Identifiers is a list of email addresses. These can be
// contact IDs, although we may have to create contacts to get
// the ID.
//
// FIXME: will it be faster to get all and filter?
for addr in identifiers {
let id = Contact::lookup_id_by_addr(&ctx, addr, dc::contact::Origin::Unknown);
match id {
0 => {
// No contact exists for this address yet. Try to
// add one so we can have an ID.
match Contact::create(&ctx, &addr, &addr) {
Ok(new_id) => out.push(new_id),
Err(e) => {
println!("Failed to add contact {}: {}", addr, e);
return Err(MethodErr::no_arg());
}
}
}
_ => out.push(id),
}
}
Ok(out)
}
_ => Err(MethodErr::no_arg()), // FIXME: should be NotImplemented?
}
}
fn add_client_interest(&self, tokens: Vec<&str>) -> Result<()> {

View File

@@ -39,6 +39,7 @@ impl telepathy::ConnectionInterfaceContactList for Connection {
}
};
// FIXME: swap implementations so Contacts depends on ContactList?
self.get_contact_attributes(ids, vec![], true)
}
@@ -76,6 +77,8 @@ impl telepathy::ConnectionInterfaceContactList for Connection {
fn download(&self) -> Result<(), MethodErr> {
println!("Connection<{}>::download()", self.id());
// This can be a no-op since we store the contacts list in delta's DB
Ok(())
}
@@ -91,7 +94,7 @@ impl telepathy::ConnectionInterfaceContactList for Connection {
fn can_change_contact_list(&self) -> Result<bool, MethodErr> {
println!("Connection<{}>::can_change_contact_list()", self.id());
Ok(false)
Ok(true)
}
fn request_uses_message(&self) -> Result<bool, MethodErr> {
println!("Connection<{}>::request_uses_message()", self.id());
@@ -100,6 +103,11 @@ impl telepathy::ConnectionInterfaceContactList for Connection {
fn download_at_connection(&self) -> Result<bool, MethodErr> {
println!("Connection<{}>::download_at_connection()", self.id());
Ok(false)
// TODO: https://telepathy.freedesktop.org/spec/Connection_Interface_Contact_List.html#Property:DownloadAtConnection
// Connections ... SHOULD provide a corresponding parameter named
// org.freedesktop.Telepathy.Connection.Interface.ContactList.DownloadAtConnection
// with the DBus_Property flag
Ok(true)
}
}

View File

@@ -1,4 +1,4 @@
use crate::padfoot::{var_str, VarArg};
use crate::padfoot::{var_str, var_u32, VarArg};
use crate::telepathy;
use dbus::tree::MethodErr;
@@ -48,18 +48,17 @@ impl telepathy::ConnectionInterfaceContacts for Connection {
"org.freedesktop.Telepathy.Connection.Interface.Avatars/token".to_string(),
var_str("".to_string()),
);
/*
// TODO: we need to publish DBUS services on these endpoints
props.insert(
"org.freedesktop.Telepathy.Connection.Interface.ContactList/publish".to_string(),
var_arg(Box::new(4)),
);
props.insert(
"org.freedesktop.Telepathy.Connection.Interface.ContactList/subscribe".to_string(),
var_arg(Box::new(4)),
);
*/
props.insert(
"org.freedesktop.Telepathy.Connection.Interface.ContactList/publish".to_string(),
var_u32(4), // YES (faking it for now)
);
props.insert(
"org.freedesktop.Telepathy.Connection.Interface.ContactList/subscribe".to_string(),
var_u32(4), // YES (faking it for now)
);
out.insert(*id, props);
}
@@ -101,7 +100,7 @@ impl telepathy::ConnectionInterfaceContacts for Connection {
Ok(vec![
"org.freedesktop.Telepathy.Connection".to_string(),
"org.freedesktop.Telepathy.Connection.Interface.Avatars".to_string(),
// "org.freedesktop.Telepathy.Connection.Interface.ContactList".to_string(),
"org.freedesktop.Telepathy.Connection.Interface.ContactList".to_string(),
])
}
}

View File

@@ -1,7 +1,10 @@
use crate::padfoot::{requestables, VarArg};
use crate::padfoot::{get_var_str, get_var_u32, requestables, Channel, DbusAction, VarArg};
use crate::telepathy;
use dbus::tree::MethodErr;
use dc::contact::Contact;
use deltachat as dc;
use std::collections::HashMap;
use super::Connection;
@@ -35,8 +38,80 @@ impl telepathy::ConnectionInterfaceRequests for Connection {
&self,
request: HashMap<&str, VarArg>,
) -> Result<(bool, dbus::Path<'static>, HashMap<String, VarArg>)> {
let path = self.path().clone();
println!("Connection<{}>::ensure_channel({:?})", self.id(), request);
Err(MethodErr::no_arg()) // FIXME: should be NotImplemented?
// Empathy sends this for the request:
//
// {
// "org.freedesktop.Telepathy.Channel.TargetID": Variant("me@example.com"),
// "org.freedesktop.Telepathy.Channel.TargetHandleType": Variant(1),
// "org.freedesktop.Telepathy.Channel.ChannelType": Variant("org.freedesktop.Telepathy.Channel.Type.Text")
// }
// FIXME: life would be easier with TargetHandle
let target_id = request
.get("org.freedesktop.Telepathy.Channel.TargetID")
.map(|va| get_var_str(va))
.unwrap();
let target_handle_type = request
.get("org.freedesktop.Telepathy.Channel.TargetHandleType")
.map(|va| get_var_u32(va))
.unwrap();
let channel_type = request
.get("org.freedesktop.Telepathy.Channel.ChannelType")
.map(|va| get_var_str(va))
.unwrap();
// Text only
if channel_type != "org.freedesktop.Telepathy.Channel.Type.Text" {
println!(" Wrong channel type: {:?}", channel_type);
return Err(MethodErr::no_arg());
};
// IMs only
if target_handle_type != 1 {
println!(" Wrong target handle type: {:?}", target_handle_type);
return Err(MethodErr::no_arg());
};
let ctx = self.ctx.read().unwrap();
let target_handle =
Contact::lookup_id_by_addr(&ctx, target_id.clone(), dc::contact::Origin::Unknown);
if target_handle == 0 {
println!("Couldn't find target handle for {}", target_id);
return Err(MethodErr::no_arg());
};
let chat_id = dc::chat::ChatId::new(target_handle);
let channel_path = Connection::build_channel_path(path, chat_id);
// Return an existing channel if it already exists
let chans = self.channels.read().unwrap();
if let Some(channel) = chans.get(&channel_path) {
return Ok((false, channel_path, channel.chan_props()));
}
// Now we need to discover or create a chat id for the contact
let chat_id = dc::chat::create_by_contact_id(&ctx, target_handle).unwrap();
let channel = Channel::new(
self.actq.clone(),
chat_id,
self.ctx.clone(),
dc::constants::DC_CONTACT_ID_SELF, // initiator is self
channel_path.clone(),
true, // requested
target_handle,
);
let response = channel.chan_props(); // FIXME: fill with data about the channel
// Send signal
self.actq.send(DbusAction::NewChannel(channel)).unwrap();
Ok((true, channel_path, response))
}
fn channels(&self) -> Result<Vec<ChannelSpec>> {

View File

@@ -1,5 +1,7 @@
use dbus::arg::{RefArg, Variant};
use std::convert::TryInto;
pub type VarArg = Variant<Box<dyn RefArg + 'static>>;
pub fn var_arg(item: Box<dyn RefArg + 'static>) -> VarArg {
@@ -25,3 +27,11 @@ pub fn var_u32(item: u32) -> VarArg {
pub fn var_i64(item: i64) -> VarArg {
var_arg(Box::new(item))
}
pub fn get_var_str(from: &VarArg) -> String {
from.0.as_str().unwrap().to_string()
}
pub fn get_var_u32(from: &VarArg) -> u32 {
from.0.as_u64().unwrap().try_into().unwrap()
}