From 9e764d72a1bfb7a85d2c48ce16ffe9e698ec82b4 Mon Sep 17 00:00:00 2001 From: Nick Thomas Date: Wed, 27 May 2020 01:19:43 +0100 Subject: [PATCH] Add image receipt to the text channel This is disturbingly useless with Empathy because it doesn't know how to display text/html parts, or plain image/png parts either :/ Time to start playing with KDE Spacebar? How does it handle them? --- src/padfoot/channel/messages.rs | 9 ++- src/padfoot/connection.rs | 14 +++-- src/padfoot/message.rs | 105 +++++++++++++++++++++++++------- src/padfoot/var_arg.rs | 4 ++ 4 files changed, 104 insertions(+), 28 deletions(-) diff --git a/src/padfoot/channel/messages.rs b/src/padfoot/channel/messages.rs index 6256b78..e7ef44f 100644 --- a/src/padfoot/channel/messages.rs +++ b/src/padfoot/channel/messages.rs @@ -43,6 +43,7 @@ impl telepathy::ChannelInterfaceMessages for Channel { }; let ctx = self.ctx.read().unwrap(); + let blobdir = ctx.get_blobdir(); let msg_id = match dc::chat::send_msg(&ctx, self.chat_id, &mut delta_msg) { Ok(msg_id) => msg_id, @@ -53,7 +54,7 @@ impl telepathy::ChannelInterfaceMessages for Channel { }; let token = format!("{}", msg_id.to_u32()); - let dbus_parts = convert_msg(&delta_msg); + let dbus_parts = convert_msg(blobdir, &delta_msg).map_err(|_| MethodErr::no_arg())?; let messages_sig = telepathy::ChannelInterfaceMessagesMessageSent { content: dbus_parts, @@ -90,7 +91,7 @@ impl telepathy::ChannelInterfaceMessages for Channel { fn supported_content_types(&self) -> Result> { println!("Channel::supported_content_types()"); - Ok(vec!["text/plain".to_string()]) // TODO: image support + Ok(vec!["*/*".to_string()]) } fn message_types(&self) -> Result> { @@ -111,13 +112,15 @@ impl telepathy::ChannelInterfaceMessages for Channel { let mut out = Vec::>>::new(); let ctx = self.ctx.read().unwrap(); + let blobdir = ctx.get_blobdir(); for msg_id in dc::chat::get_chat_msgs(&ctx, self.chat_id, 0, None) { if let Ok(msg) = dc::message::Message::load_from_db(&ctx, msg_id) { match msg.get_state() { MessageState::InFresh | MessageState::InNoticed => { println!(" A message: {:?}", msg); - out.push(convert_msg(&msg)) + let parts = convert_msg(blobdir, &msg).map_err(|_| MethodErr::no_arg())?; + out.push(parts); } _ => continue, } diff --git a/src/padfoot/connection.rs b/src/padfoot/connection.rs index f8b7466..4e26530 100644 --- a/src/padfoot/connection.rs +++ b/src/padfoot/connection.rs @@ -401,6 +401,7 @@ impl Connection { let chans = c2.read().unwrap(); let u_ctx = ctx.clone(); let ctx = u_ctx.read().unwrap(); + let blobdir = ctx.get_blobdir(); // Autocreate channel if it doesn't already exist // FIXME: unknown contacts need more care than this @@ -453,11 +454,16 @@ impl Connection { continue; } - let parts = convert_msg(&msg); + let parts = convert_msg(blobdir, &msg); + if parts.is_err() { + println!("can't convert, skipping: {}", parts.unwrap_err()); + continue; + } - let sig = - telepathy::ChannelInterfaceMessagesMessageReceived { message: parts } - .to_emit_message(&chan_path); + let sig = telepathy::ChannelInterfaceMessagesMessageReceived { + message: parts.unwrap(), + } + .to_emit_message(&chan_path); actq.send(DbusAction::Signal(sig)).unwrap(); diff --git a/src/padfoot/message.rs b/src/padfoot/message.rs index 9f0ff31..d6acd65 100644 --- a/src/padfoot/message.rs +++ b/src/padfoot/message.rs @@ -1,35 +1,47 @@ -use crate::padfoot::{var_i64, var_str, var_u32, VarArg}; +use crate::padfoot::{var_bytearray, var_i64, var_str, var_u32, VarArg}; -use deltachat::message::Message; +use dc::constants::Viewtype as Vt; +use dc::message::Message; +use deltachat as dc; use std::collections::HashMap; +type Part = HashMap; +type Result = std::result::Result>; + // Turns a deltachat::message::Message into a Telepathy Message_Part_List -pub fn convert_msg(msg: &Message) -> Vec> { +pub fn convert_msg(blobdir: &std::path::Path, msg: &Message) -> Result> { if msg.is_setupmessage() { - return convert_setupmessage(msg); + return Ok(convert_setupmessage(msg)); } - let mut parts = vec![build_props(msg)]; + let mut parts = vec![make_props(msg)]; - if let Some(text) = msg.get_text() { - parts.push(build_plain(&text)); - } + // TODO: Check. Can a deltachat message have multiple parts? Can an image + // viewtype have text as well? + let result = match msg.get_viewtype() { + Vt::Text => build_txt(msg), + Vt::Unknown => build_unknown(msg), + Vt::Audio | Vt::Voice => build_snd(msg), + Vt::Video => build_vid(msg), + _ => build_attachment(blobdir, msg), + }; - // TODO: other parts. Can a deltachat message have multiple parts? - - parts + result.map(|mut more| { + parts.append(&mut more); + parts + }) } -fn convert_setupmessage(msg: &Message) -> Vec> { +fn convert_setupmessage(msg: &Message) -> Vec { 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())), + make_props(msg), + make_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 { +fn make_props(msg: &Message) -> Part { let msg_id = msg.get_id(); let mut out = HashMap::new(); @@ -63,14 +75,65 @@ fn build_props(msg: &Message) -> HashMap { out } -fn build_plain(text: &str) -> HashMap { +fn make_plain(text: &str) -> Part { + make_content("text/plain", None, var_str(text.to_string())) +} + +fn make_content(type_: &str, id: Option<&str>, content: VarArg) -> Part { 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.insert("content-type".to_string(), var_str(type_.to_string())); + out.insert("content".to_string(), content); + + id.map(|txt| out.insert("identifier".to_string(), var_str(txt.to_string()))); out } + +fn build_snd(_msg: &Message) -> Result> { + Ok(vec![make_plain("(a sound file was received)")]) +} + +fn build_txt(msg: &Message) -> Result> { + Ok(vec![make_plain( + &msg.get_text().unwrap_or_else(|| "".to_string()), + )]) +} + +fn build_unknown(_msg: &Message) -> Result> { + Ok(vec![make_plain("(a message of unknown type was received)")]) +} + +fn build_vid(_msg: &Message) -> Result> { + Ok(vec![make_plain("(a video was received)")]) +} + +// The message contains a file. Detect the content-type and construct a part +// containing the data in full. +fn build_attachment(blobdir: &std::path::Path, msg: &Message) -> Result> { + let mime = msg + .get_filemime() + .unwrap_or_else(|| "application/octet-stream".to_string()); + let filename = msg.get_filename().ok_or("Failed to get filename")?; + + let path: std::path::PathBuf = [blobdir, &std::path::Path::new(&filename)].iter().collect(); + // let mut path = std::path::PathBuf::from(blobdir); + // path.push(filename); + + let data = + std::fs::read(&path).map_err(|e| format!("Failed to read file {:?}: {}", path, e))?; + + println!("MIME type for attachment: {}", mime); + + let html = make_content( + "text/html", + None, + var_str("".to_string()), + ); + + let txt = make_plain("(an image was sent but cannot be displayed)"); + + let blob = make_content(&mime, Some("picture"), var_bytearray(data)); + + Ok(vec![html, txt, blob]) +} diff --git a/src/padfoot/var_arg.rs b/src/padfoot/var_arg.rs index 1ee99d1..cecc852 100644 --- a/src/padfoot/var_arg.rs +++ b/src/padfoot/var_arg.rs @@ -20,6 +20,10 @@ pub fn var_bool(item: bool) -> VarArg { var_arg(Box::new(item)) } +pub fn var_bytearray(item: std::vec::Vec) -> VarArg { + var_arg(Box::new(item)) +} + pub fn var_u32(item: u32) -> VarArg { var_arg(Box::new(item)) }