HAXXX: A crude form of setup message acceptance

This commit is contained in:
2020-05-23 22:49:15 +01:00
parent 67a8715a25
commit 373311e826
5 changed files with 140 additions and 58 deletions

View File

@@ -36,7 +36,7 @@ Here's where we're at right now:
- [~] Contacts handling
- [x] Text messages
- [ ] Multimedia messages
- [ ] Setup messages
- [~] Setup messages
- [ ] Import/Export
- [ ] Group chats
- [ ] Geolocation messages
@@ -167,6 +167,26 @@ development. Just run the `telepathy-padfoot` binary as the same user that your
chat client will be running as. It registers to the DBUS **session bus**, and
will be picked up next time your chat client scans (which may need a restart).
### Setup messages
If you send an autocrypt setup message while a padfoot connection is up, it will
notice it and open a channel asking you to reply with a message like:
```
IMEX: <id> nnnn-nnnn-nnnn-nnnn-nnnn-nnnn-nnnn-nnnn-nnnn
```
The ID is the delta-generated message ID, while the rest is the setup code. No
whitespace!
This bit is still extremely janky; it should instead be a magic channel type or
action button of some kind. It is, however, functional.
Delta wants us to enable the "Send copy to self" option in settings, but that
isn't exposed in padfoot yet, so messages you send from padfoot won't appear in
other clients. Messages from other clients won't appear either, because messages
from yourself are mostly ingnored by padfoot. Still, it's progress.
### Autogenerated telepathy DBUS bindings
It makes use of the `dbus-codegen-rust` crate to convert the

View File

@@ -162,4 +162,34 @@ impl Channel {
.add(messages_iface)
.add(type_text_iface)
}
fn try_process_setupmsg(self: &Self, text: String) {
if !text.starts_with("IMEX: ") {
return;
};
// Expected form: "IMEX: <msg-id> <setupcode>
let mut iter = text.split_whitespace();
iter.next(); // Ignore the prefix
let msg_id = match iter.next() {
Some(txt) => match txt.parse::<u32>() {
Ok(id) => id,
_ => return,
},
_ => return,
};
let setup_code = match iter.next() {
Some(txt) => txt,
_ => return,
};
let ctx = self.ctx.read().unwrap();
if let Err(e) =
dc::imex::continue_key_transfer(&ctx, dc::message::MsgId::new(msg_id), &setup_code)
{
println!("Failed to apply setup code {}: {}", msg_id, e);
}
}
}

View File

@@ -34,10 +34,16 @@ impl telepathy::ChannelInterfaceMessages for Channel {
}
let text_opt = content["content"].0.as_str().map(|s| s.to_string());
let mut delta_msg = Message::new(Viewtype::Text); // FIXME: this won't always be plaintext
delta_msg.set_text(text_opt.clone());
if let Some(text) = text_opt.clone() {
self.try_process_setupmsg(text);
};
let ctx = self.ctx.read().unwrap();
let msg_id = match dc::chat::send_msg(&ctx, self.chat_id, &mut delta_msg) {
Ok(msg_id) => msg_id,
Err(e) => {

View File

@@ -377,43 +377,15 @@ impl Connection {
let chan_path = Connection::build_channel_path(path.clone(), chat_id);
let c2 = Arc::clone(&chans);
let chans = c2.read().unwrap();
let u_ctx = ctx.clone();
let ctx = u_ctx.read().unwrap();
// Autocreate channel if it doesn't already exist
// FIXME: unknown contacts need more care than this
if chans.contains_key(&chan_path) {
print!("Message {} received for {}...", msg_id, chan_path);
// Emit new message signals for the channel
if let Ok(msg) = dc::message::Message::load_from_db(
&ctx.clone().read().unwrap(),
msg_id,
) {
// Ignore messages that are self-originated.
// FIXME: special-case self-chats
if msg.get_from_id() == dc::constants::DC_CONTACT_ID_SELF {
println!("from ourselves, skipping");
continue;
}
let parts = convert_msg(&msg);
let sig = telepathy::ChannelInterfaceMessagesMessageReceived {
message: parts,
}
.to_emit_message(&chan_path);
actq.send(DbusAction::Signal(sig)).unwrap();
println!("OK!");
} else {
println!(" couldn't find message, not sending signal");
}
// FIXME: We MUST also send a Text.Received signal
} else {
if !chans.contains_key(&chan_path) {
print!("Channel for {} doesn't exist yet, creating it...", chat_id);
let contacts =
dc::chat::get_chat_contacts(&ctx.clone().read().unwrap(), chat_id);
let contacts = dc::chat::get_chat_contacts(&ctx, chat_id);
if contacts.len() > 1 {
println!("...{} contacts in chat, ignoring!", contacts.len());
continue;
@@ -426,7 +398,7 @@ impl Connection {
let chan = Channel::new(
actq.clone(),
chat_id,
ctx.clone(),
u_ctx.clone(),
*handle, // initiator is the remote contact
chan_path,
false, // FIXME: this needs to handle requested channels
@@ -436,7 +408,40 @@ impl Connection {
actq.send(act).unwrap();
println!("OK");
continue;
}
// Since the channel exists, emit new message signals
print!("Message {} received for {}...", msg_id, chan_path);
let msg = match dc::message::Message::load_from_db(&ctx, msg_id) {
Ok(m) => m,
Err(e) => {
println!("Can't load from database, skipping: {}", e);
continue;
}
};
// Ignore messages that are self-originated.
// FIXME: special-case self-chats
if msg.get_from_id() == dc::constants::DC_CONTACT_ID_SELF
&& !msg.is_setupmessage()
{
println!("from ourselves, skipping");
continue;
}
let parts = convert_msg(&msg);
let sig =
telepathy::ChannelInterfaceMessagesMessageReceived { message: parts }
.to_emit_message(&chan_path);
actq.send(DbusAction::Signal(sig)).unwrap();
// FIXME: We MUST also send a Text.Received signal
println!("OK!");
}
DbusAction::FreshMessages => {

View File

@@ -6,32 +6,53 @@ use std::collections::HashMap;
// Turns a deltachat::message::Message into a Telepathy Message_Part_List
pub fn convert_msg(msg: &Message) -> Vec<HashMap<String, VarArg>> {
let mut parts = Vec::new();
let mut props = HashMap::new();
let msg_id = msg.get_id();
if msg.is_setupmessage() {
return convert_setupmessage(msg);
}
props.insert(
let mut parts = vec![build_props(msg)];
if let Some(text) = msg.get_text() {
parts.push(build_plain(&text));
}
// TODO: other parts. Can a deltachat message have multiple parts?
parts
}
fn convert_setupmessage(msg: &Message) -> Vec<HashMap<String, VarArg>> {
let msg_id = msg.get_id();
vec![
build_props(msg),
build_plain(&format!("Setup message received. To apply it, reply with:\nIMEX: {} nnnn-nnnn-nnnn-nnnn-nnnn-nnnn-nnnn-nnnn-nnnn\nNo whitespace in the setup-code!", msg_id.to_u32())),
]
}
fn build_props(msg: &Message) -> HashMap<String, VarArg> {
let msg_id = msg.get_id();
let mut out = HashMap::new();
out.insert(
"message-token".to_string(),
var_str(format!("{}", msg_id.to_u32())),
);
props.insert("message-sent".to_string(), var_i64(msg.get_timestamp()));
props.insert(
out.insert("message-sent".to_string(), var_i64(msg.get_timestamp()));
out.insert(
"message-received".to_string(),
var_i64(msg.get_received_timestamp()),
);
props.insert("message-sender".to_string(), var_u32(msg.get_from_id()));
out.insert("message-sender".to_string(), var_u32(msg.get_from_id()));
// props.insert("message-sender-id", var_str()); // This doesn't need to be sent
// props.insert("sender-nickname", var_str()); // Can we get away without this one?
props.insert("message-type".to_string(), var_u32(0)); // normal
out.insert("message-type".to_string(), var_u32(0)); // normal
// These relate to superseded messages
// props.insert("supersedes", var_str());
// props.insert("original-message-sent", var_i64());
// props.insert("original-message-received", var_i64());
props.insert("pending-message-id".to_string(), var_u32(msg_id.to_u32()));
parts.push(props);
out.insert("pending-message-id".to_string(), var_u32(msg_id.to_u32()));
// Don't need these
// props.insert("interface", var_str());
@@ -39,17 +60,17 @@ pub fn convert_msg(msg: &Message) -> Vec<HashMap<String, VarArg>> {
// props.insert("silent", var_bool());
// props.insert("rescued", var_bool());
if let Some(text) = msg.get_text() {
let mut part = HashMap::new();
part.insert(
"content-type".to_string(),
var_str("text/plain".to_string()),
);
part.insert("content".to_string(), var_str(text));
parts.push(part);
}
// TODO: other parts. Can a deltachat message have multiple parts?
parts
out
}
fn build_plain(text: &str) -> HashMap<String, VarArg> {
let mut out = HashMap::new();
out.insert(
"content-type".to_string(),
var_str("text/plain".to_string()),
);
out.insert("content".to_string(), var_str(text.to_string()));
out
}