~nicohman/signal-rs

58466229374133a351ab1563aa57cc0d78dd5d3f — nicohman 9 months ago c9f649c
Support basic images with scli
5 files changed, 157 insertions(+), 52 deletions(-)

M Cargo.lock
M Cargo.toml
M src/main.rs
M src/scli.rs
M src/signal.rs
M Cargo.lock => Cargo.lock +10 -0
@@ 998,6 998,15 @@ dependencies = [
]

[[package]]
name = "home"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2456aef2e6b6a9784192ae780c0f15bc57df0e918585282325e8c8ac27737654"
dependencies = [
 "winapi 0.3.9",
]

[[package]]
name = "http"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 2254,6 2263,7 @@ dependencies = [
 "glib-macros",
 "glium",
 "gtk",
 "home",
 "lazy_static",
 "libhandy",
 "mio 0.7.0",

M Cargo.toml => Cargo.toml +2 -1
@@ 34,4 34,5 @@ confy = "0.4.0"
serde_repr = "0.1"
glib-macros = "0.10.0"
dbus-tokio = "0.6.0"
dbus = "0.9.0"
\ No newline at end of file
dbus = "0.9.0"
home = "0.5.3"
\ No newline at end of file

M src/main.rs => src/main.rs +3 -2
@@ 10,7 10,7 @@ extern crate gtk;
extern crate libhandy;
extern crate mio;
extern crate reqwest;
extern  crate dbus;
extern crate dbus;
extern crate serde;
extern crate serde_json;
extern crate tokio;


@@ 18,6 18,7 @@ extern crate tokio_tungstenite;
extern crate tungstenite;
extern crate url;
extern crate dbus_tokio;
extern crate home;
#[macro_use]
extern crate lazy_static;
use futures_util::StreamExt;


@@ 437,7 438,7 @@ async fn main() -> Result<()> {
                }
            }
        });
        std::thread::sleep_ms(1000);
        //std::thread::sleep_ms(1000);
        window.show_all();
        l_res_sender
            .send(SignalResponse::ShowView(View::Chats))

M src/scli.rs => src/scli.rs +109 -48
@@ 4,14 4,23 @@ use config::*;
use dbus::message::MatchRule;
use dbus::nonblock;
use dbus_tokio::connection;
use serde::{Deserialize, Deserializer, Serialize};
use futures_util::StreamExt;
use serde::{Deserialize, Deserializer, Serialize};
use signal::*;
use signal::*;
use std::fs;
use std::io::BufRead;
use std::path::*;
use std::process::Stdio;
use std::time::Duration;
lazy_static! {
    static ref SCLI_ATTACHMENT_DIRECTORY: String = home::home_dir()
        .unwrap()
        .join(".local/share/signal-cli/attachments/")
        .into_os_string()
        .into_string()
        .unwrap();
}
pub struct SCLISession {
    pub contacts: HashMap<String, Contact>,
    pub messages: HashMap<String, BTreeMap<i64, Message>>,


@@ 21,6 30,7 @@ pub struct SCLISession {
impl SCLISession {
    pub fn save(&self) {
        let datafile = DataFile::from_data(self.contacts.clone(), self.messages.clone());
        fs::rename(Path::new(&self.config.data_dir).join("data.json"), Path::new(&self.config.data_dir).join("old.data.json")).expect("Couldn't move current data file to backup");
        fs::write(
            Path::new(&self.config.data_dir).join("data.json"),
            serde_json::to_string(&datafile).unwrap().as_bytes(),


@@ 78,41 88,78 @@ impl SCLISession {
        let cmd_handle = tokio::spawn(async { child.await });
        loop {
            tokio::select! {
            	msg  = inc.1.next() => {
            		let (timestamp, source , group_id, message, attachments) : (i64, String, Vec<u8>, String, Vec<String>) = msg.unwrap().read5().unwrap();
            		let con_msg = SignalMessage {
            			message,
            			attachment: None,
            			ID: 0,
            			outgoing: false,
            			source,
            			sent_at: timestamp,
            		};
            		res_sender.send(SignalResponse::IDLessMessage(con_msg)).expect("COuldn't send IDLessMessage");
            	},
            	msg = inc_sync.1.next() => {
            		let (timestamp, mut source, dest, group_id, message, attachments) : (i64,String,String,Vec<u8>,String,Vec<String>) = msg.unwrap().read_all().unwrap();
            		let mut outgoing = false;
            		if source == config.username {
            			source = dest;
            			outgoing = true;
            		}
            		println!("{}", message);
                       		let con_msg = SignalMessage {
            			message,
            			attachment: None,
            			ID: 0,
            			outgoing,
            			source,
            			sent_at: timestamp,
            		};
            		res_sender.send(SignalResponse::IDLessMessage(con_msg)).expect("COuldn't send IDLessMessage"); 		
            	}
                /*msg_line = reader.next_line() => {
                /*msg  = inc.1.next() => {
                    let (timestamp, source , group_id, message, attachments) : (i64, String, Vec<u8>, String, Vec<String>) = msg.unwrap().read5().unwrap();
                    let con_msg = SignalMessage {
                        message,

                        attachment: None,
                        ID: 0,
                        outgoing: false,
                        source,
                        sent_at: timestamp,
                    };
                    res_sender.send(SignalResponse::IDLessMessage(con_msg)).expect("COuldn't send IDLessMessage");
                },
                msg = inc_sync.1.next() => {
                    let (timestamp, mut source, dest, group_id, message, attachments) : (i64,String,String,Vec<u8>,String,Vec<String>) = msg.unwrap().read_all().unwrap();
                    let mut outgoing = false;
                    if source == config.username {
                        source = dest;
                        outgoing = true;
                    }
                    let con_msg = SignalMessage {
                        message,
                        attachment: None,
                        ID: 0,
                        outgoing,
                        source,
                        sent_at: timestamp,
                    };
                    res_sender.send(SignalResponse::IDLessMessage(con_msg)).expect("COuldn't send IDLessMessage");
                }*/
                msg_line = reader.next_line() => {
                    println!("Incoming!");
                    let parsed : SignalCLIContainer= serde_json::from_str(&msg_line.unwrap().unwrap()).unwrap();
                    res_sender.send(SignalResponse::SignalCLIEnvelope(parsed.envelope)).expect("Couldn't send SignalCLIEnvelope");
                }*/
                    if let Some(ref data_msg) = parsed.envelope.data_message {
                                let mut attachment = vec![];
                                if let Some(ats) = &data_msg.attachments {
                                    attachment = parse_attachments(ats);
                                }
                        let con_msg = SignalMessage {
                            message: data_msg.message.clone().unwrap_or_default(),
                            ID: 0,
                            outgoing: false,
                            attachment: Some(attachment),
                            source: parsed.envelope.source.clone(),
                            sent_at: parsed.envelope.timestamp,
                        };
                        res_sender.send(SignalResponse::IDLessMessage(con_msg)).expect("Couldn't send IDLessMessage");
                    } else if let Some(ref sync_msg) = parsed.envelope.sync_message {
                        if let Some(sm) = &sync_msg.sent_message {
                                let mut outgoing = false;
                                let mut source = parsed.envelope.source.clone();
                                if parsed.envelope.source == config.username {
                                    outgoing = true;
                                    source = sm.destination.clone();
                                }
                                let mut attachment = vec![];
                                if let Some(ats) = &sm.attachments {
                                    attachment = parse_attachments(ats);
                                }
                                let con_msg = SignalMessage {
                                    message: sm.message.clone().unwrap_or_default(),
                                    ID: 0,
                                    outgoing,
                                    attachment: Some(attachment),
                                    source,
                                    sent_at: parsed.envelope.timestamp,
                                };
                                res_sender.send(SignalResponse::IDLessMessage(con_msg)).expect("Couldn't send IDLessMessage");

                        }
                    }
                }
                inc = req_receiver.recv() => {
                    if let Some(v) = inc {
                        match v {


@@ 122,16 169,19 @@ impl SCLISession {
                                //res_sender.send(SignalResponse::ContactList(contacts)).expect("Can't send ContactList");
                            }
                            SignalRequest::SendMessage { to, message } => {
                            	let (timestamp,) : (i64,) = proxy.method_call("org.asamk.Signal", "sendMessage", (&message,Vec::<String>::new(), vec![&to] )).await.unwrap();
                                let (timestamp,) : (i64,) = proxy.method_call("org.asamk.Signal", "sendMessage", (&message,Vec::<String>::new(), vec![&to] )).await.unwrap();
                                res_sender.send(SignalResponse::IDLessMessage(SignalMessage {
                                	message: message,
                                	source: to,
                                	outgoing: true,
                                	attachment: None,
                                	sent_at: timestamp,
                                	ID: 0,
                                    message: message,
                                    source: to,
                                    outgoing: true,
                                    attachment: None,
                                    sent_at: timestamp,
                                    ID: 0,
                                })).expect("Couldnt send IDLessMessage");
                            }
                            SignalRequest::AddContact{ name, phone} => {
                                res_sender.send(SignalResponse::ContactList(vec![Contact {name, tel: phone}])).expect("Couldn't send ContactList");
                            },
                            _ => {

                            }


@@ 181,7 231,7 @@ impl SCLISession {
        if let Some(v) = self.messages.get(&msg.source) {
            let last_id = v.values().map(|msg| msg.msg.ID).max().unwrap();
            let made_msg = SignalMessage {
                ID: last_id+1,
                ID: last_id + 1,
                source: msg.source,
                message: message.unwrap(),
                outgoing: false,


@@ 263,12 313,12 @@ impl SignalBackend for SCLISession {
                self.process_scli_msg(msg);
            }
            SignalResponse::IDLessMessage(mut msg) => {
            	let mut id = 0;
            	if let Some(v) = self.messages.get(&msg.source) {
            		  id = v.values().map(|msg| msg.msg.ID).max().unwrap() + 1;
            	}
            	msg.ID = id;
            	self.add_message(msg, true);
                let mut id = 0;
                if let Some(v) = self.messages.get(&msg.source) {
                    id = v.values().map(|msg| msg.msg.ID).max().unwrap() + 1;
                }
                msg.ID = id;
                self.add_message(msg, true);
            }
            SignalResponse::ContactList(contacts) => {
                for contact in contacts.into_iter() {


@@ 284,6 334,7 @@ impl SignalBackend for SCLISession {
        }
    }
}
/// The file containing the stored signal messages/contacts
#[derive(Serialize, Deserialize)]
pub struct DataFile {
    pub contacts: HashMap<String, Contact>,


@@ 309,3 360,13 @@ impl DataFile {
        Ok(serde_json::from_str(&stri)?)
    }
}
/// Turns a vec of SignalCLIAttachments into their Attachment counterparts
pub fn parse_attachments(catt: &Vec<SignalCLIAttachment>) -> Vec<Attachment> {
    catt.into_iter()
        .map(|x| Attachment {
            file: SCLI_ATTACHMENT_DIRECTORY.to_owned() + &x.id,
            file_name: x.filename.clone().unwrap_or(x.id.to_string()),
            c_type: AttachmentType::from_mime(&x.content_type),
        })
        .collect()
}

M src/signal.rs => src/signal.rs +33 -1
@@ 147,6 147,7 @@ pub struct SignalCLIMessage {
    pub is_read: Option<bool>,
    pub is_delivery: Option<bool>,
    pub data_message: Option<DataMessage>,
    pub sync_message: Option<SyncMessage>
}
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct SignalCLIContactInfo {


@@ 166,14 167,37 @@ impl SignalCLIContactInfo {
}
#[serde(rename_all = "camelCase")]
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct SyncMessage {
    pub sent_message: Option<SentMessage>,
}
#[serde(rename_all = "camelCase")]
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct SentMessage {
    pub timestamp: u64,
    pub message: Option<String>,
    pub expires_in_seconds: u64,
    pub attachments: Option<Vec<SignalCLIAttachment>>,
    //pub group_info: 
    pub destination: String,
}
#[serde(rename_all = "camelCase")]
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct DataMessage {
    pub timestamp: u64,
    pub message: Option<String>,
    pub expires_in_seconds: u64,
    // pub attachments: Option<Vec<Attachment>>,
    pub attachments: Option<Vec<SignalCLIAttachment>>,
    //pub group_info: Option<GroupInfo>,
    pub destination: Option<String>,
}
#[serde(rename_all = "camelCase")]
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct SignalCLIAttachment {
    pub content_type: String,
    pub filename: Option<String>,
    pub id: String,
    pub size: u64,
}
#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(rename_all = "PascalCase")]
pub struct SignalMessage {


@@ 230,6 254,14 @@ pub enum AttachmentType {
    File = 0,
    Video = 5,
}
impl AttachmentType {
    pub fn from_mime(mime: &str) -> AttachmentType {
        if mime.contains("image") {
            return AttachmentType::Image;
        }
        AttachmentType::File
    }
}
impl PartialEq for SignalMessage {
    fn eq(&self, other: &Self) -> bool {
        self.ID == other.ID && self.message == other.message