HAXXX: A crude form of setup message acceptance
This commit is contained in:
22
README.md
22
README.md
@@ -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
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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) => {
|
||||
|
@@ -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 => {
|
||||
|
@@ -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
|
||||
}
|
||||
|
Reference in New Issue
Block a user