From 577d8999cfda2946b60f3f69fda37a960d90faa1 Mon Sep 17 00:00:00 2001 From: Dan Ballard Date: Wed, 6 Jul 2022 23:52:30 -0700 Subject: [PATCH 1/7] add events wrapping enum --- Cargo.lock | 78 ++++++++ Cargo.toml | 1 + src/bindings_go.rs | 9 +- src/event.rs | 476 +++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 5 +- src/structs.rs | 4 +- 6 files changed, 569 insertions(+), 4 deletions(-) create mode 100644 src/event.rs diff --git a/Cargo.lock b/Cargo.lock index 91faa43..8e7149e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,12 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + [[package]] name = "block-buffer" version = "0.10.2" @@ -17,6 +23,19 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chrono" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" +dependencies = [ + "libc", + "num-integer", + "num-traits", + "time", + "winapi", +] + [[package]] name = "cpufeatures" version = "0.2.1" @@ -125,6 +144,7 @@ checksum = "1b03d17f364a3a042d5e5d46b053bbbf82c92c9430c592dd4c064dc6ee997125" name = "libcwtch" version = "0.3.2" dependencies = [ + "chrono", "hex-literal", "libc", "serde", @@ -133,6 +153,25 @@ dependencies = [ "sha2", ] +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] + [[package]] name = "proc-macro2" version = "1.0.36" @@ -245,6 +284,17 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "time" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" +dependencies = [ + "libc", + "wasi", + "winapi", +] + [[package]] name = "typenum" version = "1.15.0" @@ -262,3 +312,31 @@ name = "version_check" version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/Cargo.toml b/Cargo.toml index fc9b081..c63047d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,3 +18,4 @@ libc = "0.2" serde_json = "1.0" serde = { version = "1.0.127", features = ["derive"] } serde_with = { version = "1.10.0" } +chrono = "0.4.19" diff --git a/src/bindings_go.rs b/src/bindings_go.rs index f3ad8f7..d6b584b 100644 --- a/src/bindings_go.rs +++ b/src/bindings_go.rs @@ -9,6 +9,7 @@ use std::ffi::CString; use super::CwtchLib; use crate::cwtchlib_go::bindings; use crate::{CwtchError, structs::*}; +use crate::event::Event; struct c_str_wrap { raw: *mut i8, @@ -86,6 +87,7 @@ pub struct CwtchLibGo {} impl CwtchLibGo { c_bind!(_get_profile_attribute(profile: &str, key: &str;;) c_GetProfileAttribute -> String); c_bind!(_get_conversation_attribute(profile: &str; conversation_id: i32; key: &str) c_GetConversationAttribute -> String); + c_bind!(_get_appbus_event(;;) c_GetAppBusEvent -> String); } impl CwtchLib for CwtchLibGo { @@ -93,7 +95,6 @@ impl CwtchLib for CwtchLibGo { c_bind!(started(;;) c_Started -> i32); c_bind!(send_app_event(event_json: &str;;) c_SendAppEvent); c_bind!(send_profile_event(profile: &str, event_jason: &str;;) c_SendProfileEvent); - c_bind!(get_appbus_event(;;) c_GetAppBusEvent -> String); c_bind!(create_profile(nick: &str, pass: &str;;) c_CreateProfile); c_bind!(load_profiles(pass: &str;;) c_LoadProfiles); c_bind!(accept_conversation(profile: &str ; conversation_id: i32 ;) c_AcceptConversation); @@ -164,4 +165,10 @@ impl CwtchLib for CwtchLibGo { c_bind!(destroy_servers(;;) c_DestroyServers); c_bind!(set_server_attribute(onion: &str, key: &str, val: &str;;) c_SetServerAttribute); c_bind!(get_debug_info(;;) c_GetDebugInfo -> String); + + fn get_appbus_event(&self) -> Event { + let event_json = self._get_appbus_event(); + let cwtch_event: CwtchEvent = serde_json::from_str(&event_json).expect("Error parsing Cwtch event"); + Event::from(&cwtch_event) + } } diff --git a/src/event.rs b/src/event.rs new file mode 100644 index 0000000..d1602bd --- /dev/null +++ b/src/event.rs @@ -0,0 +1,476 @@ +use std::collections::HashMap; + +use chrono::{DateTime, FixedOffset}; +use chrono::format::Fixed::TimezoneOffset; +use chrono::prelude::*; +use chrono::offset::LocalResult; + +use crate::structs::{ACL, ConnectionState, CwtchEvent}; + +#[derive(Debug)] +pub enum MessageNotification { + // NotificationNone enum for message["notification"] that means no notification + None, + // NotificationEvent enum for message["notification"] that means emit a notification that a message event happened only + SimpleEvent, + // NotificationConversation enum for message["notification"] that means emit a notification event with Conversation handle included + ContactInfo, +} + +impl From for MessageNotification { + fn from(str: String) -> MessageNotification { + match str.as_str() { + "None" => MessageNotification::None, + "SimpleEvent" => MessageNotification::SimpleEvent, + "ContactInfo" => MessageNotification::ContactInfo, + _ => MessageNotification::None, + } + } +} + +#[derive(Debug)] +pub enum NetworkCheckStatus { + Error, + Success +} + +impl From for NetworkCheckStatus { + fn from(str: String) -> NetworkCheckStatus { + match str.as_str() { + "Error" => NetworkCheckStatus::Error, + "Success" => NetworkCheckStatus::Success, + _ => NetworkCheckStatus::Error, + } + } +} + +#[derive(Debug)] +pub enum ServerStorageType { + DefaultPassword, + Password, + NoPassword +} + +impl From for ServerStorageType { + fn from(str: String) -> ServerStorageType { + match str.as_str() { + "storage-default-password" => ServerStorageType::DefaultPassword, + "storage-password" => ServerStorageType::Password, + "storage-no-password" => ServerStorageType::NoPassword, + _ => ServerStorageType::DefaultPassword, + } + } +} + +#[derive(Debug)] +pub enum ServerIntent { + Running, + Stopped +} + +impl From for ServerIntent { + fn from(str: String) -> ServerIntent { + match str.as_str() { + "running" => ServerIntent::Running, + "stopped" => ServerIntent::Stopped, + _ => ServerIntent::Stopped, + } + } +} + +#[derive(Debug)] +pub enum Event { + + // App events + + CwtchStarted, + NewPeer { + identity: String, + tag: String, + created: bool, + name: String, + default_picture: String, + picture: String, + online: String, + contacts_json: String, + server_json: String, //ServerList + }, + AppError { + error: String + }, + UpdateGlobalSettings { + settings: HashMap, + }, + ErrUnhandled { + name: String, + data: HashMap, + }, + PeerError { + error: String + }, + PeerDeleted { + identity: String + }, + Shutdown, + ACNStatus { + progress: i8, + status: String + }, + ACNVersion { + verstion: String, + }, + StartingStorageMiragtion, + DoneStorageMigration, + + // app server events + + NewServer { + onion: String, + server_bundle: String, + description: String, + storage_type: ServerStorageType, + autostart: bool, + running: bool, + }, + ServerIntentUpdate { + identity: String, + intent: ServerIntent + }, + ServerDeleted { + identity: String, + success: bool, + error: Option, + }, + ServerStatsUpdate { + identity: String, + total_messages: i32, + connections: i32, + }, + + // profile events + + NewMessageFromPeer { + conversation_id: i32, + handle: String, + nick: String, + timestamp_received: DateTime, + message: String, + notification: MessageNotification, + picture: String, + }, + ContactCreated { + conversation_id: i32, + remote_peer: String, + nick: String, + status: ConnectionState, + unread: i32, + picture: String, + default_picture: String, + num_messages: i32, + accepted: bool, + access_control_list: ACL, + blocked: bool, + loading: bool, + last_msg_time: DateTime, + + }, + PeerStateChange { + remote_peer: String, + connection_state: ConnectionState, + }, + NetworkStatus { + address: String, + error: String, + status: NetworkCheckStatus, + }, + ACNInfo { + handle: String, + key: String, + data: String, + }, + UpdatedProfileAttribute { + key: String, + value: String, + }, + IndexedAcknowledgement { + conversation_id: i32, + index: i32, + }, + IndexedFailure { + conversation_id: i32, + index: i32, + handle: String, + error: String + }, + PeerAcknowledgement { + event_id: String, + remote_peer: String, + conversation_id: i32, + }, + NewMessageFromGroup { + conversation_id: i32, + timestamp_sent: DateTime, + remote_peer: String, + index: i32, + message: String, + content_hash: String, + picture: String, + nick: String, + notification: MessageNotification, + }, + GroupCreated { + conversation_id: i32, + group_id: String, + group_server: String, + group_name: String, + picture: String, + access_control_list: ACL, + }, + NewGroup { + conversation_id: i32, + group_id: String, + group_server: String, + group_invite: String, + group_name: String, + picture: String, + access_control_list: ACL, + }, + ServerStateChange { + group_server: String, + state: ConnectionState + }, + NewRetValMessageFromPeer { + remote_peer: String, + scope: String, + path: String, + exists: bool, + data: String, + file_path: Option + }, + ShareManifest { + filekey: String, + serializedManifest: String, + }, + ManifestSizeReceived { + filekey: String, + manifest_size: i32, + handle: String, + }, + ManifestError { + handle: String, + filekey: String, + }, + ManifestReceived { + handle: String, + filekey: String, + serialized_manifest: String, + }, + ManifestSaved { + handle: String, + filekey: String, + serialized_manifest: String, + temp_file: String, + name_suggestion: String, + }, + FileDownloadProgressUpdate { + filekey: String, + progress: i32, + filesize_in_chunks: i32, + name_suggestion: String, + }, + // FileDownloaded, ?? +} + +impl From<&CwtchEvent> for Event { + fn from(cwtch_event: &CwtchEvent) -> Self { + match cwtch_event.event_type.as_str() { + "CwtchStarted" => Event::CwtchStarted, + "NewPeer" => Event::NewPeer { + identity: cwtch_event.data["Identity"].clone(), + tag: cwtch_event.data["tag"].clone(), + created: cwtch_event.data["Created"] == "true", + name: cwtch_event.data["name"].clone(), + default_picture: cwtch_event.data["defaultPicture"].clone(), + picture: cwtch_event.data["picture"].clone(), + online: cwtch_event.data["Online"].clone(), + contacts_json: cwtch_event.data["ContactsJson"].clone(), + server_json: cwtch_event.data["ServerList"].clone(), + }, + "NewMessageFromPeer" => Event::NewMessageFromPeer { + conversation_id: cwtch_event.data["ConversationID"].parse().unwrap_or(-2), + handle: cwtch_event.data["RemotePeer"].clone(), + nick: cwtch_event.data["Nick"].clone(), + timestamp_received: DateTime::parse_from_rfc3339(cwtch_event.data["TimestampReceived"].as_str()).unwrap_or( DateTime::from(Utc::now())), + message: cwtch_event.data["Data"].clone(), + notification: MessageNotification::from(cwtch_event.data["notification"].clone()), + picture: cwtch_event.data["Picture"].clone(), + }, + "PeerError" => Event::PeerError { error: cwtch_event.data["Error"].clone() }, + "AppError" => Event::AppError { + error: cwtch_event.data["Error"].clone(), + }, + "ContactCreated" => Event::ContactCreated { + conversation_id: cwtch_event.data["ConversationID"].clone().parse().unwrap_or(-2), + remote_peer: cwtch_event.data["RemotePeer"].clone(), + unread: cwtch_event.data["unread"].clone().parse().unwrap_or(0), + picture: cwtch_event.data["picture"].clone(), + default_picture: cwtch_event.data["defaultPicture"].clone(), + num_messages: cwtch_event.data["numMessages"].clone().parse().unwrap_or(0), + nick: cwtch_event.data["nick"].clone(), + accepted: cwtch_event.data["accepted"].clone().parse().unwrap_or(false), + status: ConnectionState::from(cwtch_event.data["status"].as_str()), + access_control_list: serde_json::from_str(cwtch_event.data["accessControlList"].as_str()).unwrap_or(ACL::new()), + blocked: cwtch_event.data["blocked"].clone().parse().unwrap_or(false), + loading: cwtch_event.data["loading"].clone().parse().unwrap_or(false), + last_msg_time: DateTime::parse_from_rfc3339(cwtch_event.data["lastMsgTime"].as_str()).unwrap_or(DateTime::from(Utc::now())), + }, + "PeerStateChange" => Event::PeerStateChange { + remote_peer: cwtch_event.data["RemotePeer"].clone(), + connection_state: ConnectionState::from(cwtch_event.data["ConnectionState"].as_str()), + }, + "UpdateGlobalSettings" => Event::UpdateGlobalSettings { + settings: serde_json::from_str(cwtch_event.data["Data"].as_str()).unwrap_or(HashMap::new()), + }, + "PeerDeleted" => Event::PeerDeleted { identity: cwtch_event.data["Identity"].clone() }, + + "ACNStatus" => Event::ACNStatus { + progress: cwtch_event.data["Progress"].parse().unwrap_or(0), + status: cwtch_event.data["Status"].clone(), + }, + "ACNVersion" => Event::ACNVersion { verstion: cwtch_event.data["Data"].clone()}, + "NetworkError" => Event::NetworkStatus { + address: cwtch_event.data["Onion"].clone(), + error: cwtch_event.data["Error"].clone(), + status: NetworkCheckStatus::from(cwtch_event.data["Status"].clone()) + }, + "ACNInfo" => Event::ACNInfo { + handle: cwtch_event.data["Handle"].clone(), + key: cwtch_event.data["Key"].clone(), + data: cwtch_event.data["Data"].clone(), + }, + "UpdatedProfileAttribute" => Event::UpdatedProfileAttribute { + key: cwtch_event.data["Key"].clone(), + value: cwtch_event.data["Data"].clone(), + }, + "IndexedAcknowledgement" => Event::IndexedAcknowledgement { + conversation_id: cwtch_event.data["ConversationID"].parse().unwrap_or(-1), + index: cwtch_event.data["Index"].parse().unwrap_or(-1), + }, + "IndexedAcknowledgement" => Event::IndexedFailure { + conversation_id: cwtch_event.data["ConversationID"].parse().unwrap_or(-1), + index: cwtch_event.data["Index"].parse().unwrap_or(-1), + handle: cwtch_event.data["Handle"].clone(), + error: cwtch_event.data["Error"].clone(), + }, + "ShareManifest" => Event::ShareManifest { + filekey: cwtch_event.data["FileKey"].clone(), + serializedManifest: cwtch_event.data["SerializedManifest"].clone(), + }, + "NewServer" => Event::NewServer { + onion: cwtch_event.data["Onion"].clone(), + server_bundle: cwtch_event.data["ServerBundle"].clone(), + description: cwtch_event.data["Description"].clone(), + storage_type: ServerStorageType::from(cwtch_event.data["StorageType"].clone()), + autostart: cwtch_event.data["Autostart"].parse().unwrap_or(false), + running: cwtch_event.data["Running"].parse().unwrap_or(false), + }, + "ServerIntentUpdate" => Event::ServerIntentUpdate { + identity: cwtch_event.data["Identity"].clone(), + intent: ServerIntent::from(cwtch_event.data["Intent"].clone()) + }, + "ServerDeleted" => Event::ServerDeleted { + identity: cwtch_event.data["Identity"].clone(), + success: cwtch_event.data["Status"].clone() == "success", + error: match cwtch_event.data.get("Error") { + Some(e) => Some(e.clone()), + None => None, + } + }, + "ServerStatsUpdate" => Event::ServerStatsUpdate { + identity: cwtch_event.data["Identity"].clone(), + total_messages: cwtch_event.data["TotalMessages"].parse().unwrap_or(0), + connections: cwtch_event.data["Connections"].parse().unwrap_or(0), + }, + "ManifestSizeReceived" => Event::ManifestSizeReceived { + filekey: cwtch_event.data["FileKey"].clone(), + manifest_size: cwtch_event.data["ManifestSize"].parse().unwrap_or(0), + handle: cwtch_event.data["Handle"].clone(), + }, + "ManifestError" => Event::ManifestError { + handle: cwtch_event.data["Handle"].clone(), + filekey: cwtch_event.data["FileKey"].clone(), + }, + "ManifestReceived" => Event::ManifestReceived { + handle: cwtch_event.data["Handle"].clone(), + filekey: cwtch_event.data["FileKey"].clone(), + serialized_manifest: cwtch_event.data["SerializedManifest"].clone(), + }, + "ManifestSaved" => Event::ManifestSaved { + handle: cwtch_event.data["Handle"].clone(), + filekey: cwtch_event.data["FileKey"].clone(), + serialized_manifest: cwtch_event.data["SerializedManifest"].clone(), + temp_file: cwtch_event.data["TempFile"].clone(), + name_suggestion: cwtch_event.data["NameSuggestion"].clone(), + }, + "FileDownloadProgressUpdate" => Event::FileDownloadProgressUpdate { + filekey: cwtch_event.data["FileKey"].clone(), + progress: cwtch_event.data["Progress"].parse().unwrap_or(0), + filesize_in_chunks: cwtch_event.data["FileSizeInChunks"].parse().unwrap_or(0), + name_suggestion: cwtch_event.data["NameSuggestion"].clone(), + }, + "PeerAcknowledgement" => Event::PeerAcknowledgement { + event_id: cwtch_event.data["EventId"].clone(), + remote_peer: cwtch_event.data["RemotePeer"].clone(), + conversation_id: cwtch_event.data["ConversationID"].parse().unwrap_or(0) + }, + "NewMessageFromGroup" => Event::NewMessageFromGroup { + timestamp_sent: DateTime::parse_from_rfc3339(cwtch_event.data["TimestampSent"].as_str()).unwrap_or( DateTime::from(Utc::now())), + index: cwtch_event.data["RemotePeer"].parse().unwrap_or(-1), + content_hash: cwtch_event.data["ContentHash"].clone(), + conversation_id: cwtch_event.data["ConversationID"].parse().unwrap_or(-2), + remote_peer: cwtch_event.data["RemotePeer"].clone(), + nick: cwtch_event.data["Nick"].clone(), + message: cwtch_event.data["Data"].clone(), + notification: MessageNotification::from(cwtch_event.data["notification"].clone()), + picture: cwtch_event.data["Picture"].clone(), + }, + "GroupCreated" => Event::GroupCreated { + conversation_id: cwtch_event.data["ConversationID"].parse().unwrap_or(-2), + group_id: cwtch_event.data["GroupID"].clone(), + group_server: cwtch_event.data["GroupServer"].clone(), + group_name: cwtch_event.data["GroupName"].clone(), + picture: cwtch_event.data["picture"].clone(), + access_control_list: serde_json::from_str(cwtch_event.data["accessControlList"].as_str()).unwrap_or(ACL::new()), + }, + "NewGroup" => Event::NewGroup { + conversation_id: cwtch_event.data["ConversationID"].parse().unwrap_or(-2), + group_id: cwtch_event.data["GroupID"].clone(), + group_server: cwtch_event.data["GroupServer"].clone(), + group_invite: cwtch_event.data["GroupInvite"].clone(), + group_name: cwtch_event.data["GroupName"].clone(), + picture: cwtch_event.data["picture"].clone(), + access_control_list: serde_json::from_str(cwtch_event.data["accessControlList"].as_str()).unwrap_or(ACL::new()), + }, + "ServerStateChange" => Event::ServerStateChange { + group_server: cwtch_event.data["GroupServer"].clone(), + state: ConnectionState::from(cwtch_event.data["ConnectionState"].as_str()), + }, + "NewRetValMessageFromPeer" => Event::NewRetValMessageFromPeer { + remote_peer: cwtch_event.data["RemotePeer"].clone(), + scope: cwtch_event.data["Scope"].clone(), + path: cwtch_event.data["Path"].clone(), + exists: cwtch_event.data["Exists"].parse().unwrap_or(false), + data: cwtch_event.data["Data"].clone(), + file_path: match cwtch_event.data.get("FilePath") { + Some(fp) => Some(fp.to_string()), + None => None, + }, + }, + + _ => Event::ErrUnhandled { + name: cwtch_event.event_type.to_string(), + data: cwtch_event.data.clone(), + }, + } + } +} diff --git a/src/lib.rs b/src/lib.rs index b84271e..7c1e53c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,11 +3,14 @@ #![doc(html_root_url = "https://git.openprivacy.ca/cwtch.im/libcwtch-rs")] #![deny(missing_docs)] +use crate::event::Event; + mod bindings_go; mod cwtchlib_go; /// Basic structs using data from Cwtch and for deserializing JSON and serializing to JSON to communicate with Cwtch pub mod structs; +pub mod event; /// Error type for Cwtch lib related errors, intended for use with Result pub type CwtchError = String; @@ -27,7 +30,7 @@ pub trait CwtchLib { fn send_profile_event(&self, profile: &str, event_json: &str); /// Pull json of a structs::CwtchEvent off the appbus for responding to - fn get_appbus_event(&self) -> String; + fn get_appbus_event(&self) -> Event; /// Create a new profile encrypted with pass fn create_profile(&self, nick: &str, pass: &str); diff --git a/src/structs.rs b/src/structs.rs index 91b41e6..e3c9b93 100644 --- a/src/structs.rs +++ b/src/structs.rs @@ -29,9 +29,9 @@ impl Default for ConnectionState { } } -impl ConnectionState { +impl From<&str> for ConnectionState { /// Creates a ConnectionState from a string sent from libcwtch-go - pub fn new(name: &str) -> Self { + fn from(name: &str) -> Self { match name { "Disconnected" => ConnectionState::Disconnected, "Connecting" => ConnectionState::Connecting, From bd5d3f7e5f025d283614934077b742c80299f29f Mon Sep 17 00:00:00 2001 From: Dan Ballard Date: Thu, 7 Jul 2022 14:30:20 -0700 Subject: [PATCH 2/7] starting to type fields --- src/event.rs | 103 +++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 80 insertions(+), 23 deletions(-) diff --git a/src/event.rs b/src/event.rs index d1602bd..dd7e3df 100644 --- a/src/event.rs +++ b/src/event.rs @@ -7,13 +7,19 @@ use chrono::offset::LocalResult; use crate::structs::{ACL, ConnectionState, CwtchEvent}; +pub type Identity = String; +pub type Handle = String; +pub type ConversationID = i32; +pub type FileKey = String; + #[derive(Debug)] +/// Enum for type of notification a UI/client should emit for a new message pub enum MessageNotification { - // NotificationNone enum for message["notification"] that means no notification + /// means no notification None, - // NotificationEvent enum for message["notification"] that means emit a notification that a message event happened only + /// means emit a notification that a message event happened only SimpleEvent, - // NotificationConversation enum for message["notification"] that means emit a notification event with Conversation handle included + /// means emit a notification event with Conversation handle included ContactInfo, } @@ -29,8 +35,11 @@ impl From for MessageNotification { } #[derive(Debug)] +/// Enum for results from NetworkCheck plugin on profiles pub enum NetworkCheckStatus { + /// There was an error, this profile cannot connect to itself Error, + /// Success connecting to self, profile is "online" Success } @@ -45,9 +54,13 @@ impl From for NetworkCheckStatus { } #[derive(Debug)] +/// Enum denoting server storage mode pub enum ServerStorageType { + /// indicates some app supplied default password is being used on this server for storage encryption DefaultPassword, + /// indicates a user supplied password is being used on this server for storage encryption Password, + /// indicates no password and no encryption is being used on server storage NoPassword } @@ -63,8 +76,11 @@ impl From for ServerStorageType { } #[derive(Debug)] +/// Enum used by servers to declare their intent to be running or stopped pub enum ServerIntent { + /// a server intends to be running Running, + /// a server intends to be stopped Stopped } @@ -79,13 +95,16 @@ impl From for ServerIntent { } #[derive(Debug)] +/// Enum for events from Cwtch appbus pub enum Event { // App events + /// Emited by libcwtch once Cwtch.Start() has been completed successfully, you can start making API calls, lcg is initialized CwtchStarted, + /// A new peer has been loaded, details of peer. Identity should be stored so further API calls can be made NewPeer { - identity: String, + identity: Identity, tag: String, created: bool, name: String, @@ -95,60 +114,72 @@ pub enum Event { contacts_json: String, server_json: String, //ServerList }, + /// Cwtch had an error at the app level (not profile level), usually in response to an API call AppError { error: String }, + /// Global settings being emited from lcg, usually in response to them being sent to be saved by client UpdateGlobalSettings { settings: HashMap, }, - ErrUnhandled { - name: String, - data: HashMap, - }, + /// A profile has an error, usually emited in response to an API call PeerError { error: String }, + /// A profile was successfully deleted, it is no longer usable PeerDeleted { identity: String }, + /// Cwtch is shutting down, stop making API calls Shutdown, + /// indicates status of the ACN (tor), usually during boot. ACNStatus { + /// the percent of ACN boot (-1 to 100) progress: i8, + /// an associated status message from the ACN status: String }, + /// Version of the ACN (currently tor) ACNVersion { verstion: String, }, + /// Notice from libCwtch that before completing load of a profile a storage migration is occuring so Start will take longer StartingStorageMiragtion, + /// Notice from libCwtch that the storage migration is complete, profile being loaded resuming DoneStorageMigration, // app server events + /// A new server has been loaded NewServer { - onion: String, + onion: Identity, server_bundle: String, description: String, storage_type: ServerStorageType, autostart: bool, running: bool, }, + /// Response to request for server intent change, indicating the server is indending the new state ServerIntentUpdate { - identity: String, + identity: Identity, intent: ServerIntent }, + /// Notice a server was deleted (in response to an API call) and is no longer usable ServerDeleted { - identity: String, + identity: Identity, success: bool, error: Option, }, + /// Stats info for a server, periodically emited ServerStatsUpdate { - identity: String, + identity: Identity, total_messages: i32, connections: i32, }, // profile events + /// A new message was received NewMessageFromPeer { conversation_id: i32, handle: String, @@ -158,9 +189,10 @@ pub enum Event { notification: MessageNotification, picture: String, }, + /// A new contact has been created (imported, added, or contacted by) ContactCreated { conversation_id: i32, - remote_peer: String, + remote_peer: Identity, nick: String, status: ConnectionState, unread: i32, @@ -174,43 +206,51 @@ pub enum Event { last_msg_time: DateTime, }, + /// A peer has changed state PeerStateChange { - remote_peer: String, + remote_peer: Identity, connection_state: ConnectionState, }, + /// Notice from the network check plugin, a profile self check test by attempting to connecting to itself NetworkStatus { - address: String, + address: Identity, error: String, status: NetworkCheckStatus, }, + /// Information from the ACN about a peer ACNInfo { - handle: String, + handle: Identity, key: String, data: String, }, + /// a profile attribute has been updated with a new value UpdatedProfileAttribute { key: String, value: String, }, + /// emited to confirm ack of a message succeeded IndexedAcknowledgement { conversation_id: i32, index: i32, }, + /// emited to signal failure to ack a message IndexedFailure { conversation_id: i32, index: i32, - handle: String, + handle: Identity, error: String }, + /// a peer has acked a message PeerAcknowledgement { event_id: String, - remote_peer: String, + remote_peer: Identity, conversation_id: i32, }, + /// New message received on a group NewMessageFromGroup { conversation_id: i32, timestamp_sent: DateTime, - remote_peer: String, + remote_peer: Identity, index: i32, message: String, content_hash: String, @@ -218,6 +258,7 @@ pub enum Event { nick: String, notification: MessageNotification, }, + /// notice a group has been created GroupCreated { conversation_id: i32, group_id: String, @@ -226,6 +267,7 @@ pub enum Event { picture: String, access_control_list: ACL, }, + /// notice a new group exists NewGroup { conversation_id: i32, group_id: String, @@ -235,43 +277,51 @@ pub enum Event { picture: String, access_control_list: ACL, }, + /// a server connection state has changed ServerStateChange { group_server: String, state: ConnectionState }, + /// A getval call to a peer has returned a response NewRetValMessageFromPeer { - remote_peer: String, + remote_peer: Identity, scope: String, path: String, exists: bool, data: String, file_path: Option }, + /// result of a call to share a file, the filekey and manifest to operate on it ShareManifest { filekey: String, serializedManifest: String, }, + /// Information on a peer fileshare has been received ManifestSizeReceived { filekey: String, manifest_size: i32, handle: String, }, + /// An error has occured while trying to parse a peer sharefile ManifestError { - handle: String, + handle: Identity, filekey: String, }, + /// A peer message about a shared file has been received ManifestReceived { - handle: String, + handle: Identity, filekey: String, serialized_manifest: String, }, + /// a received manfiest has been saved ManifestSaved { - handle: String, + handle: Identity, filekey: String, serialized_manifest: String, temp_file: String, name_suggestion: String, }, + /// periodically emited status updates about an active download of a shared file FileDownloadProgressUpdate { filekey: String, progress: i32, @@ -279,6 +329,13 @@ pub enum Event { name_suggestion: String, }, // FileDownloaded, ?? + + /// Indicates an event was sent from libCwtch that we don't handle/didn't anticipate + /// still passing it on giving the user a chance to react, but should be reported so we can handle it properly + ErrUnhandled { + name: String, + data: HashMap, + }, } impl From<&CwtchEvent> for Event { From 384c5880b2b670217927cb4bd69dc21dcbdd80f0 Mon Sep 17 00:00:00 2001 From: Dan Ballard Date: Thu, 14 Jul 2022 16:33:41 -0700 Subject: [PATCH 3/7] typed IDs for most things, docs for all --- src/event.rs | 375 +++++++++++++++++++++++++++++++++++++++++---------- src/lib.rs | 1 + 2 files changed, 304 insertions(+), 72 deletions(-) diff --git a/src/event.rs b/src/event.rs index dd7e3df..1a677d9 100644 --- a/src/event.rs +++ b/src/event.rs @@ -4,13 +4,69 @@ use chrono::{DateTime, FixedOffset}; use chrono::format::Fixed::TimezoneOffset; use chrono::prelude::*; use chrono::offset::LocalResult; +use std::convert::From; use crate::structs::{ACL, ConnectionState, CwtchEvent}; -pub type Identity = String; -pub type Handle = String; -pub type ConversationID = i32; -pub type FileKey = String; +#[derive(Debug)] +/// Profile ID used to refer to profiles in Cwtch +pub struct ProfileIdentity(String); + +impl From for ProfileIdentity { + fn from(x: String) -> Self { + ProfileIdentity(x) + } +} + +#[derive(Debug)] +/// Contact ID used to refer to contacts in Cwtch +pub struct ContactIdentity(String); + +impl From for ContactIdentity { + fn from(x: String) -> Self { + ContactIdentity(x) + } +} + +#[derive(Debug)] +/// Conversation ID user to refer to a conversation with a Contact or Group in Cwtch +pub struct ConversationID(i32) ; + +impl From for ConversationID { + fn from(x: i32) -> Self { + ConversationID(x) + } +} + +#[derive(Debug)] +/// Group ID used to refer to a Group in Cwtch +pub struct GroupID(String) ; + +impl From for GroupID { + fn from(x: String) -> Self { + GroupID(x) + } +} + +#[derive(Debug)] +/// Server ID user to refer to a server in Cwtch +pub struct ServerIdentity(String); + +impl From for ServerIdentity { + fn from(x: String) -> Self { + ServerIdentity(x) + } +} + +#[derive(Debug)] +/// FileKey ID user to refer to a file share in Cwtch +pub struct FileKey(String); + +impl From for FileKey { + fn from(x: String) -> Self { + FileKey(x) + } +} #[derive(Debug)] /// Enum for type of notification a UI/client should emit for a new message @@ -104,31 +160,44 @@ pub enum Event { CwtchStarted, /// A new peer has been loaded, details of peer. Identity should be stored so further API calls can be made NewPeer { - identity: Identity, + /// identity field + profile: ProfileIdentity, + /// optional client specified tag tag: String, + /// is this a newly created profile event, or a load created: bool, + /// user supplied name of the profile name: String, + /// default picture path default_picture: String, + /// user supplied picture path picture: String, + /// is the profile online online: String, + /// json of the contacts contacts_json: String, + /// json of known servers server_json: String, //ServerList }, /// Cwtch had an error at the app level (not profile level), usually in response to an API call AppError { + /// details of the app error that occured error: String }, /// Global settings being emited from lcg, usually in response to them being sent to be saved by client UpdateGlobalSettings { + /// map of setting names to values (https://git.openprivacy.ca/cwtch.im/libcwtch-go/src/branch/trunk/utils/settings.go) settings: HashMap, }, /// A profile has an error, usually emited in response to an API call PeerError { + /// details of the peer error that occured error: String }, /// A profile was successfully deleted, it is no longer usable PeerDeleted { - identity: String + /// identity of deleted peer + profile: ProfileIdentity }, /// Cwtch is shutting down, stop making API calls Shutdown, @@ -141,7 +210,8 @@ pub enum Event { }, /// Version of the ACN (currently tor) ACNVersion { - verstion: String, + /// version string from ACN app + version: String, }, /// Notice from libCwtch that before completing load of a profile a storage migration is occuring so Start will take longer StartingStorageMiragtion, @@ -152,28 +222,42 @@ pub enum Event { /// A new server has been loaded NewServer { - onion: Identity, + /// identity of server + server: ServerIdentity, + /// sharable / importable server bundle server_bundle: String, + /// user supplied description description: String, + /// storage mode the server is using storage_type: ServerStorageType, + /// does/should the server auto start on cwtch start autostart: bool, + /// is the server running running: bool, }, /// Response to request for server intent change, indicating the server is indending the new state ServerIntentUpdate { - identity: Identity, + /// identity of server + server: ServerIdentity, + /// intent of the server to be running or not intent: ServerIntent }, /// Notice a server was deleted (in response to an API call) and is no longer usable ServerDeleted { - identity: Identity, + /// identity of server + server: ServerIdentity, + /// was deletion a success success: bool, + /// optional error string in case of failure to delete error: Option, }, /// Stats info for a server, periodically emited ServerStatsUpdate { - identity: Identity, + /// identity of server + server: ServerIdentity, + /// count of total messages on the server total_messages: i32, + /// count of total current connections to the server connections: i32, }, @@ -181,151 +265,277 @@ pub enum Event { /// A new message was received NewMessageFromPeer { - conversation_id: i32, - handle: String, + /// identity field + profile: ProfileIdentity, + /// conversation id + conversation_id: ConversationID, + /// contact id + contact: ContactIdentity, + /// name of contact nick: String, + /// time message was received timestamp_received: DateTime, + /// the message message: String, + /// notification instructions (based on settings) notification: MessageNotification, + /// path to picture for the contact picture: String, }, /// A new contact has been created (imported, added, or contacted by) ContactCreated { - conversation_id: i32, - remote_peer: Identity, + /// identity field + profile: ProfileIdentity, + /// conversation id + conversation_id: ConversationID, + /// contact id + contact: ContactIdentity, + /// name of group nick: String, + /// connection status to group server status: ConnectionState, + /// number of unread messages in group unread: i32, + /// path to picture for group picture: String, + /// path to default picture for group default_picture: String, + /// total number of messages in group num_messages: i32, + /// has the user accepted the group accepted: bool, + /// ACL for the group access_control_list: ACL, + /// is the group blocked blocked: bool, + /// is the group syncing loading: bool, + /// time of last message from the group last_msg_time: DateTime, }, /// A peer has changed state PeerStateChange { - remote_peer: Identity, + /// identity field + profile: ProfileIdentity, + /// contact id + contact: ContactIdentity, + /// connection status to contact connection_state: ConnectionState, }, /// Notice from the network check plugin, a profile self check test by attempting to connecting to itself NetworkStatus { - address: Identity, + /// profile the check was performed on + profile: ProfileIdentity, // it's technically a profile self check + /// error if there was one (can be empty) error: String, + /// status of profile self connection check status: NetworkCheckStatus, }, /// Information from the ACN about a peer ACNInfo { - handle: Identity, + /// identity field + profile: ProfileIdentity, + /// contact id + contact: ContactIdentity, + /// key of info key: String, + /// data of info data: String, }, /// a profile attribute has been updated with a new value UpdatedProfileAttribute { + /// identity field + profile: ProfileIdentity, + /// attribute key key: String, + /// attribute new value value: String, }, /// emited to confirm ack of a message succeeded IndexedAcknowledgement { + /// identity field + profile: ProfileIdentity, + /// conversation id conversation_id: i32, + /// index of message acked index: i32, }, /// emited to signal failure to ack a message IndexedFailure { - conversation_id: i32, + /// identity field + profile: ProfileIdentity, + /// conversation id + conversation_id: ConversationID, + /// index of failure of message to ack index: i32, - handle: Identity, + /// contact id + contact: ContactIdentity, + /// error string error: String }, /// a peer has acked a message PeerAcknowledgement { + /// identity field + profile: ProfileIdentity, + /// message id this is an ack to event_id: String, - remote_peer: Identity, + /// contact id + contact: ContactIdentity, + /// conversation id conversation_id: i32, }, /// New message received on a group NewMessageFromGroup { - conversation_id: i32, + /// identity field + profile: ProfileIdentity, + /// conversation id + conversation_id: ConversationID, + /// time of message timestamp_sent: DateTime, - remote_peer: Identity, + /// contact id + contact: ContactIdentity, + /// message index index: i32, + /// the message message: String, + /// hash of the message content_hash: String, + /// path to picture for sender picture: String, + /// name of sender nick: String, + /// notification policy (based on settings) notification: MessageNotification, }, /// notice a group has been created GroupCreated { - conversation_id: i32, - group_id: String, + /// identity field + profile: ProfileIdentity, + /// conversation id + conversation_id: ConversationID, + /// group id + group_id: GroupID, + /// server the group is on group_server: String, + /// name of group group_name: String, + /// path to picture for group picture: String, + /// Access Control List for group access_control_list: ACL, }, /// notice a new group exists NewGroup { - conversation_id: i32, - group_id: String, + /// identity field + profile: ProfileIdentity, + /// conversation id + conversation_id: ConversationID, + /// group id + group_id: GroupID, + /// server the group is on group_server: String, + /// invite string group_invite: String, + /// group name group_name: String, + /// path to group picture picture: String, + /// Access Control List for group access_control_list: ACL, }, /// a server connection state has changed ServerStateChange { + /// identity field + profile: ProfileIdentity, + /// server the group is on group_server: String, + /// state of connection to server state: ConnectionState }, /// A getval call to a peer has returned a response NewRetValMessageFromPeer { - remote_peer: Identity, + /// identity field + profile: ProfileIdentity, + /// conversation id + contact: ContactIdentity, + /// scope of the val scope: String, + /// path of the val (zone.key) path: String, + /// does the queried for value exist exists: bool, + /// value data: String, + /// optional filepath if there was a downloaded component file_path: Option }, /// result of a call to share a file, the filekey and manifest to operate on it ShareManifest { - filekey: String, + /// identity field + profile: ProfileIdentity, + /// filekey + filekey: FileKey, + /// serialized manifest of the share serializedManifest: String, }, /// Information on a peer fileshare has been received ManifestSizeReceived { - filekey: String, + /// identity field + profile: ProfileIdentity, + /// filekey + filekey: FileKey, + /// size of manifest received for a share manifest_size: i32, - handle: String, + /// contact id + contact: ContactIdentity, }, /// An error has occured while trying to parse a peer sharefile ManifestError { - handle: Identity, - filekey: String, + /// identity field + profile: ProfileIdentity, + /// contact id + contact: ContactIdentity, + /// filekey + filekey: FileKey, }, /// A peer message about a shared file has been received ManifestReceived { - handle: Identity, - filekey: String, + /// identity field + profile: ProfileIdentity, + /// contact id + contact: ContactIdentity, + /// filekey + filekey: FileKey, + /// serialized manifest serialized_manifest: String, }, /// a received manfiest has been saved ManifestSaved { - handle: Identity, - filekey: String, + /// identity field + profile: ProfileIdentity, + /// contact id + contact: ContactIdentity, + /// filekey + filekey: FileKey, + /// serialized manifest serialized_manifest: String, + /// temporary storage path for share download temp_file: String, + /// contact suggested share file name name_suggestion: String, }, /// periodically emited status updates about an active download of a shared file FileDownloadProgressUpdate { - filekey: String, + /// identity field + profile: ProfileIdentity, + /// filekey + filekey: FileKey, + /// progress of download of share in chunks progress: i32, + /// size of share in chunks filesize_in_chunks: i32, + /// contact suggested name for file name_suggestion: String, }, // FileDownloaded, ?? @@ -333,7 +543,9 @@ pub enum Event { /// Indicates an event was sent from libCwtch that we don't handle/didn't anticipate /// still passing it on giving the user a chance to react, but should be reported so we can handle it properly ErrUnhandled { + /// name of unhandled event name: String, + /// map of key:val attributes of unhandled event data: HashMap, }, } @@ -343,7 +555,7 @@ impl From<&CwtchEvent> for Event { match cwtch_event.event_type.as_str() { "CwtchStarted" => Event::CwtchStarted, "NewPeer" => Event::NewPeer { - identity: cwtch_event.data["Identity"].clone(), + profile: cwtch_event.data["Identity"].clone().into(), tag: cwtch_event.data["tag"].clone(), created: cwtch_event.data["Created"] == "true", name: cwtch_event.data["name"].clone(), @@ -354,8 +566,9 @@ impl From<&CwtchEvent> for Event { server_json: cwtch_event.data["ServerList"].clone(), }, "NewMessageFromPeer" => Event::NewMessageFromPeer { - conversation_id: cwtch_event.data["ConversationID"].parse().unwrap_or(-2), - handle: cwtch_event.data["RemotePeer"].clone(), + profile: cwtch_event.data["ProfileOnion"].clone().into(), + conversation_id: cwtch_event.data["ConversationID"].parse().unwrap_or(-2).into(), + contact: cwtch_event.data["RemotePeer"].clone().into(), nick: cwtch_event.data["Nick"].clone(), timestamp_received: DateTime::parse_from_rfc3339(cwtch_event.data["TimestampReceived"].as_str()).unwrap_or( DateTime::from(Utc::now())), message: cwtch_event.data["Data"].clone(), @@ -367,8 +580,9 @@ impl From<&CwtchEvent> for Event { error: cwtch_event.data["Error"].clone(), }, "ContactCreated" => Event::ContactCreated { - conversation_id: cwtch_event.data["ConversationID"].clone().parse().unwrap_or(-2), - remote_peer: cwtch_event.data["RemotePeer"].clone(), + profile: cwtch_event.data["ProfileOnion"].clone().into(), + conversation_id: cwtch_event.data["ConversationID"].clone().parse().unwrap_or(-2).into(), + contact: cwtch_event.data["RemotePeer"].clone().into(), unread: cwtch_event.data["unread"].clone().parse().unwrap_or(0), picture: cwtch_event.data["picture"].clone(), default_picture: cwtch_event.data["defaultPicture"].clone(), @@ -382,49 +596,55 @@ impl From<&CwtchEvent> for Event { last_msg_time: DateTime::parse_from_rfc3339(cwtch_event.data["lastMsgTime"].as_str()).unwrap_or(DateTime::from(Utc::now())), }, "PeerStateChange" => Event::PeerStateChange { - remote_peer: cwtch_event.data["RemotePeer"].clone(), + profile: cwtch_event.data["ProfileOnion"].clone().into(), + contact: cwtch_event.data["RemotePeer"].clone().into(), connection_state: ConnectionState::from(cwtch_event.data["ConnectionState"].as_str()), }, "UpdateGlobalSettings" => Event::UpdateGlobalSettings { settings: serde_json::from_str(cwtch_event.data["Data"].as_str()).unwrap_or(HashMap::new()), }, - "PeerDeleted" => Event::PeerDeleted { identity: cwtch_event.data["Identity"].clone() }, + "PeerDeleted" => Event::PeerDeleted { profile: cwtch_event.data["Identity"].clone().into() }, "ACNStatus" => Event::ACNStatus { progress: cwtch_event.data["Progress"].parse().unwrap_or(0), status: cwtch_event.data["Status"].clone(), }, - "ACNVersion" => Event::ACNVersion { verstion: cwtch_event.data["Data"].clone()}, + "ACNVersion" => Event::ACNVersion { version: cwtch_event.data["Data"].clone()}, "NetworkError" => Event::NetworkStatus { - address: cwtch_event.data["Onion"].clone(), + profile: cwtch_event.data["Onion"].clone().into(), error: cwtch_event.data["Error"].clone(), status: NetworkCheckStatus::from(cwtch_event.data["Status"].clone()) }, "ACNInfo" => Event::ACNInfo { - handle: cwtch_event.data["Handle"].clone(), + profile: cwtch_event.data["ProfileOnion"].clone().into(), + contact: cwtch_event.data["Handle"].clone().into(), key: cwtch_event.data["Key"].clone(), data: cwtch_event.data["Data"].clone(), }, "UpdatedProfileAttribute" => Event::UpdatedProfileAttribute { + profile: cwtch_event.data["ProfileOnion"].clone().into(), key: cwtch_event.data["Key"].clone(), value: cwtch_event.data["Data"].clone(), }, "IndexedAcknowledgement" => Event::IndexedAcknowledgement { + profile: cwtch_event.data["ProfileOnion"].clone().into(), conversation_id: cwtch_event.data["ConversationID"].parse().unwrap_or(-1), index: cwtch_event.data["Index"].parse().unwrap_or(-1), }, "IndexedAcknowledgement" => Event::IndexedFailure { - conversation_id: cwtch_event.data["ConversationID"].parse().unwrap_or(-1), + profile: cwtch_event.data["ProfileOnion"].clone().into(), + conversation_id: cwtch_event.data["ConversationID"].parse().unwrap_or(-1).into(), index: cwtch_event.data["Index"].parse().unwrap_or(-1), - handle: cwtch_event.data["Handle"].clone(), + contact: cwtch_event.data["Handle"].clone().into(), error: cwtch_event.data["Error"].clone(), }, "ShareManifest" => Event::ShareManifest { - filekey: cwtch_event.data["FileKey"].clone(), + profile: cwtch_event.data["ProfileOnion"].clone().into(), + filekey: cwtch_event.data["FileKey"].clone().into(), serializedManifest: cwtch_event.data["SerializedManifest"].clone(), }, "NewServer" => Event::NewServer { - onion: cwtch_event.data["Onion"].clone(), + server: cwtch_event.data["Onion"].clone().into(), server_bundle: cwtch_event.data["ServerBundle"].clone(), description: cwtch_event.data["Description"].clone(), storage_type: ServerStorageType::from(cwtch_event.data["StorageType"].clone()), @@ -432,11 +652,11 @@ impl From<&CwtchEvent> for Event { running: cwtch_event.data["Running"].parse().unwrap_or(false), }, "ServerIntentUpdate" => Event::ServerIntentUpdate { - identity: cwtch_event.data["Identity"].clone(), + server: cwtch_event.data["Identity"].clone().into(), intent: ServerIntent::from(cwtch_event.data["Intent"].clone()) }, "ServerDeleted" => Event::ServerDeleted { - identity: cwtch_event.data["Identity"].clone(), + server: cwtch_event.data["Identity"].clone().into(), success: cwtch_event.data["Status"].clone() == "success", error: match cwtch_event.data.get("Error") { Some(e) => Some(e.clone()), @@ -444,64 +664,73 @@ impl From<&CwtchEvent> for Event { } }, "ServerStatsUpdate" => Event::ServerStatsUpdate { - identity: cwtch_event.data["Identity"].clone(), + server: cwtch_event.data["Identity"].clone().into(), total_messages: cwtch_event.data["TotalMessages"].parse().unwrap_or(0), connections: cwtch_event.data["Connections"].parse().unwrap_or(0), }, "ManifestSizeReceived" => Event::ManifestSizeReceived { - filekey: cwtch_event.data["FileKey"].clone(), + profile: cwtch_event.data["ProfileOnion"].clone().into(), + filekey: cwtch_event.data["FileKey"].clone().into(), manifest_size: cwtch_event.data["ManifestSize"].parse().unwrap_or(0), - handle: cwtch_event.data["Handle"].clone(), + contact: cwtch_event.data["Handle"].clone().into(), }, "ManifestError" => Event::ManifestError { - handle: cwtch_event.data["Handle"].clone(), - filekey: cwtch_event.data["FileKey"].clone(), + profile: cwtch_event.data["ProfileOnion"].clone().into(), + contact: cwtch_event.data["Handle"].clone().into(), + filekey: cwtch_event.data["FileKey"].clone().into(), }, "ManifestReceived" => Event::ManifestReceived { - handle: cwtch_event.data["Handle"].clone(), - filekey: cwtch_event.data["FileKey"].clone(), + profile: cwtch_event.data["ProfileOnion"].clone().into(), + contact: cwtch_event.data["Handle"].clone().into(), + filekey: cwtch_event.data["FileKey"].clone().into(), serialized_manifest: cwtch_event.data["SerializedManifest"].clone(), }, "ManifestSaved" => Event::ManifestSaved { - handle: cwtch_event.data["Handle"].clone(), - filekey: cwtch_event.data["FileKey"].clone(), + profile: cwtch_event.data["ProfileOnion"].clone().into(), + contact: cwtch_event.data["Handle"].clone().into(), + filekey: cwtch_event.data["FileKey"].clone().into(), serialized_manifest: cwtch_event.data["SerializedManifest"].clone(), temp_file: cwtch_event.data["TempFile"].clone(), name_suggestion: cwtch_event.data["NameSuggestion"].clone(), }, "FileDownloadProgressUpdate" => Event::FileDownloadProgressUpdate { - filekey: cwtch_event.data["FileKey"].clone(), + profile: cwtch_event.data["ProfileOnion"].clone().into(), + filekey: cwtch_event.data["FileKey"].clone().into(), progress: cwtch_event.data["Progress"].parse().unwrap_or(0), filesize_in_chunks: cwtch_event.data["FileSizeInChunks"].parse().unwrap_or(0), name_suggestion: cwtch_event.data["NameSuggestion"].clone(), }, "PeerAcknowledgement" => Event::PeerAcknowledgement { + profile: cwtch_event.data["ProfileOnion"].clone().into(), event_id: cwtch_event.data["EventId"].clone(), - remote_peer: cwtch_event.data["RemotePeer"].clone(), + contact: cwtch_event.data["RemotePeer"].clone().into(), conversation_id: cwtch_event.data["ConversationID"].parse().unwrap_or(0) }, "NewMessageFromGroup" => Event::NewMessageFromGroup { + profile: cwtch_event.data["ProfileOnion"].clone().into(), timestamp_sent: DateTime::parse_from_rfc3339(cwtch_event.data["TimestampSent"].as_str()).unwrap_or( DateTime::from(Utc::now())), index: cwtch_event.data["RemotePeer"].parse().unwrap_or(-1), content_hash: cwtch_event.data["ContentHash"].clone(), - conversation_id: cwtch_event.data["ConversationID"].parse().unwrap_or(-2), - remote_peer: cwtch_event.data["RemotePeer"].clone(), + conversation_id: cwtch_event.data["ConversationID"].parse().unwrap_or(-2).into(), + contact: cwtch_event.data["RemotePeer"].clone().into(), nick: cwtch_event.data["Nick"].clone(), message: cwtch_event.data["Data"].clone(), notification: MessageNotification::from(cwtch_event.data["notification"].clone()), picture: cwtch_event.data["Picture"].clone(), }, "GroupCreated" => Event::GroupCreated { - conversation_id: cwtch_event.data["ConversationID"].parse().unwrap_or(-2), - group_id: cwtch_event.data["GroupID"].clone(), + profile: cwtch_event.data["ProfileOnion"].clone().into(), + conversation_id: cwtch_event.data["ConversationID"].parse().unwrap_or(-2).into(), + group_id: cwtch_event.data["GroupID"].clone().into(), group_server: cwtch_event.data["GroupServer"].clone(), group_name: cwtch_event.data["GroupName"].clone(), picture: cwtch_event.data["picture"].clone(), access_control_list: serde_json::from_str(cwtch_event.data["accessControlList"].as_str()).unwrap_or(ACL::new()), }, "NewGroup" => Event::NewGroup { - conversation_id: cwtch_event.data["ConversationID"].parse().unwrap_or(-2), - group_id: cwtch_event.data["GroupID"].clone(), + profile: cwtch_event.data["ProfileOnion"].clone().into(), + conversation_id: cwtch_event.data["ConversationID"].parse().unwrap_or(-2).into(), + group_id: cwtch_event.data["GroupID"].clone().into(), group_server: cwtch_event.data["GroupServer"].clone(), group_invite: cwtch_event.data["GroupInvite"].clone(), group_name: cwtch_event.data["GroupName"].clone(), @@ -509,11 +738,13 @@ impl From<&CwtchEvent> for Event { access_control_list: serde_json::from_str(cwtch_event.data["accessControlList"].as_str()).unwrap_or(ACL::new()), }, "ServerStateChange" => Event::ServerStateChange { + profile: cwtch_event.data["ProfileOnion"].clone().into(), group_server: cwtch_event.data["GroupServer"].clone(), state: ConnectionState::from(cwtch_event.data["ConnectionState"].as_str()), }, "NewRetValMessageFromPeer" => Event::NewRetValMessageFromPeer { - remote_peer: cwtch_event.data["RemotePeer"].clone(), + profile: cwtch_event.data["ProfileOnion"].clone().into(), + contact: cwtch_event.data["RemotePeer"].clone().into(), scope: cwtch_event.data["Scope"].clone(), path: cwtch_event.data["Path"].clone(), exists: cwtch_event.data["Exists"].parse().unwrap_or(false), diff --git a/src/lib.rs b/src/lib.rs index 7c1e53c..b44de4d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,6 +10,7 @@ mod cwtchlib_go; /// Basic structs using data from Cwtch and for deserializing JSON and serializing to JSON to communicate with Cwtch pub mod structs; +/// Additional structs for advnaced event handling and converstion helpers pub mod event; /// Error type for Cwtch lib related errors, intended for use with Result From 3a685db27f0386a75c6aeddb3ad29e97fdf076b4 Mon Sep 17 00:00:00 2001 From: Dan Ballard Date: Fri, 15 Jul 2022 12:23:13 -0700 Subject: [PATCH 4/7] adding typed types to api part 1 --- src/bindings_go.rs | 154 +++++++++++++++++++++++++++++++++++---------- src/event.rs | 41 ++++++++++++ src/lib.rs | 68 ++++++++++---------- 3 files changed, 196 insertions(+), 67 deletions(-) diff --git a/src/bindings_go.rs b/src/bindings_go.rs index d6b584b..37c4874 100644 --- a/src/bindings_go.rs +++ b/src/bindings_go.rs @@ -8,7 +8,7 @@ use std::ffi::CString; use super::CwtchLib; use crate::cwtchlib_go::bindings; -use crate::{CwtchError, structs::*}; +use crate::{ConversationID, CwtchError, ProfileIdentity, ServerIdentity, structs::*}; use crate::event::Event; struct c_str_wrap { @@ -88,41 +88,111 @@ impl CwtchLibGo { c_bind!(_get_profile_attribute(profile: &str, key: &str;;) c_GetProfileAttribute -> String); c_bind!(_get_conversation_attribute(profile: &str; conversation_id: i32; key: &str) c_GetConversationAttribute -> String); c_bind!(_get_appbus_event(;;) c_GetAppBusEvent -> String); + c_bind!(_send_profile_event(profile: &str, event_jason: &str;;) c_SendProfileEvent); + c_bind!(_accept_conversation(profile: &str ; conversation_id: i32 ;) c_AcceptConversation); + c_bind!(_block_contact(profile: &str ; conversation_id: i32; ) c_BlockContact); + c_bind!(_unblock_contact(profile: &str ; conversation_id: i32; ) c_UnblockContact); + c_bind!(_get_message(profile: &str; conversation_id: i32, message_index: i32 ;) c_GetMessage -> String); + c_bind!(_get_message_by_id(profile: &str ; conversation_id: i32, message_id: i32 ;) c_GetMessageByID -> String); + c_bind!(_get_message_by_content_hash(profile: &str ; conversation_id: i32 ; hash: &str) c_GetMessagesByContentHash -> String); + c_bind!(_get_messages(profile: &str; conversation_id: i32, message_index: i32, count: i32 ;) c_GetMessages -> String); + c_bind!(_send_message(profile: &str; conversation_id: i32; msg: &str) c_SendMessage -> String); + c_bind!(_send_invitation(profile: &str; conversation_id: i32, target_id: i32;) c_SendInvitation -> String); + c_bind!(_share_file(profile: &str; conversation_id: i32; file_path: &str) c_ShareFile -> String); + c_bind!(_download_file(profile: &str; conversation_id: i32; file_path: &str, manifest_path: &str, file_key: &str) c_DownloadFile); + c_bind!(_check_download_status(profile: &str, file_key: &str;;) c_CheckDownloadStatus); + c_bind!(_verify_or_resume_download(profile: &str; conversation_id: i32; file_key: &str) c_VerifyOrResumeDownload); + c_bind!(_create_group(profile: &str, server: &str, name: &str;;) c_CreateGroup); + c_bind!(_delete_profile(profile: &str, pass: &str;;) c_DeleteProfile); + c_bind!(_archive_conversation(profile: &str; conversation_id: i32;) c_ArchiveConversation); + c_bind!(_delete_contact(profile: &str; conversation_id: i32;) c_DeleteContact); + c_bind!(_import_bundle(profile: &str, bundle: &str;;) c_ImportBundle); + c_bind!(_set_profile_attribute(profile: &str, key: &str, val: &str;;) c_SetProfileAttribute); + c_bind!(_set_conversation_attribute(profile: &str; conversation_id: i32; key: &str, val: &str) c_SetConversationAttribute); + c_bind!(_set_message_attribute(profile: &str; conversation_id: i32, channel_id: i32, message_id: i32; key: &str, val: &str) c_SetMessageAttribute); + c_bind!(_change_password(profile: &str, old_pass: &str, new_pass: &str, new_pass_again: &str;;) c_ChangePassword); + c_bind!(_export_profile(profile: &str, filename: &str;;) c_ExportProfile); + c_bind!(_delete_server(server: &str, current_password: &str;;) c_DeleteServer); + c_bind!(_launch_server(server: &str;;) c_LaunchServer); + c_bind!(_stop_server(server: &str;;) c_StopServer); + c_bind!(_set_server_attribute(server: &str, key: &str, val: &str;;) c_SetServerAttribute); } impl CwtchLib for CwtchLibGo { c_bind!(start_cwtch(app_dir: &str, tor_path: &str;;) c_StartCwtch -> i32); c_bind!(started(;;) c_Started -> i32); c_bind!(send_app_event(event_json: &str;;) c_SendAppEvent); - c_bind!(send_profile_event(profile: &str, event_jason: &str;;) c_SendProfileEvent); + fn send_profile_event(&self, profile: ProfileIdentity, event_jason: &str) { + self._send_profile_event(profile.into(), event_jason) + } c_bind!(create_profile(nick: &str, pass: &str;;) c_CreateProfile); c_bind!(load_profiles(pass: &str;;) c_LoadProfiles); - c_bind!(accept_conversation(profile: &str ; conversation_id: i32 ;) c_AcceptConversation); - c_bind!(block_contact(profile: &str ; conversation_id: i32; ) c_BlockContact); - c_bind!(unblock_contact(profile: &str ; conversation_id: i32; ) c_UnblockContact); - c_bind!(get_message(profile: &str; conversation_id: i32, message_index: i32 ;) c_GetMessage -> String); - c_bind!(get_message_by_id(profile: &str ; conversation_id: i32, message_id: i32 ;) c_GetMessageByID -> String); - c_bind!(get_message_by_content_hash(profile: &str ; conversation_id: i32 ; hash: &str) c_GetMessagesByContentHash -> String); - c_bind!(get_messages(profile: &str; conversation_id: i32, message_index: i32, count: i32 ;) c_GetMessages -> String); - c_bind!(send_message(profile: &str; conversation_id: i32; msg: &str) c_SendMessage -> String); - c_bind!(send_invitation(profile: &str; conversation_id: i32, target_id: i32;) c_SendInvitation -> String); - c_bind!(share_file(profile: &str; conversation_id: i32; file_path: &str) c_ShareFile -> String); - c_bind!(download_file(profile: &str; conversation_id: i32; file_path: &str, manifest_path: &str, file_key: &str) c_DownloadFile); - c_bind!(check_download_status(profile: &str, file_key: &str;;) c_CheckDownloadStatus); - c_bind!(verify_or_resume_download(profile: &str; conversation_id: i32; file_key: &str) c_VerifyOrResumeDownload); + fn accept_conversation(&self, profile: ProfileIdentity, conversation_id: ConversationID) { + self._accept_conversation(profile.into(), conversation_id.into()) + } + fn block_contact(&self, profile: ProfileIdentity, conversation_id: ConversationID) { + self._block_contact(profile.into(), conversation_id.into()) + } + fn unblock_contact(&self, profile: ProfileIdentity, conversation_id: ConversationID) { + self._unblock_contact(profile.into(), conversation_id.into()) + } + fn get_message(&self, profile: ProfileIdentity, conversation_id: ConversationID, message_index: i32) -> String { + self._get_message(profile.into(), conversation_id.into(), message_index) + } + fn get_message_by_id(&self, profile: ProfileIdentity, conversation_id: ConversationID, message_id: i32) -> String { + self._get_message_by_id(profile.into(), conversation_id.into(), message_id) + } + fn get_message_by_content_hash(&self, profile: ProfileIdentity, conversation_id: ConversationID, hash: &str) -> String { + self._get_message_by_content_hash(profile.into(), conversation_id.into(), hash) + } + fn get_messages(&self, profile: ProfileIdentity, conversation_id: ConversationID, message_index: i32, count: i32) -> String { + self.get_messages(profile.into(), conversation_id, message_index, count) + } + fn send_message(&self, profile: ProfileIdentity, conversation_id: ConversationID, msg: &str) -> String { + self._send_message(profile.into(), conversation_id.into(), msg) + } + fn send_invitation(&self, profile: ProfileIdentity, conversation_id: ConversationID, target_id: i32) -> String { + self._send_invitation(profile.into(), conversation_id.into(), target_id) + } + fn share_file(&self, profile: ProfileIdentity, conversation_id: ConversationID, file_path: &str) -> String { + self.share_file(profile.into(), conversation_id, file_path) + } + fn download_file(&self, profile: ProfileIdentity, conversation_id: ConversationID, file_path: &str, manifest_path: &str, file_key: &str) { + self._download_file(profile.into(), conversation_id.into(), file_path, manifest_path, file_key) + } + fn check_download_status(&self, profile: ProfileIdentity, file_key: &str) { + self._check_download_status(profile.into(), file_key) + } + fn verify_or_resume_download(&self, profile: ProfileIdentity, conversation_id: ConversationID, file_key: &str) { + self._verify_or_resume_download(profile.into(), conversation_id.into(), file_key) + } + fn reset_tor(&self) { unsafe { bindings::c_ResetTor(); } } - c_bind!(create_group(profile: &str, server: &str, name: &str;;) c_CreateGroup); - c_bind!(delete_profile(profile: &str, pass: &str;;) c_DeleteProfile); - c_bind!(archive_conversation(profile: &str; conversation_id: i32;) c_ArchiveConversation); - c_bind!(delete_contact(profile: &str; conversation_id: i32;) c_DeleteContact); - c_bind!(import_bundle(profile: &str, bundle: &str;;) c_ImportBundle); - c_bind!(set_profile_attribute(profile: &str, key: &str, val: &str;;) c_SetProfileAttribute); - fn get_profile_attribute(&self, profile: &str, key: &str) -> Result, CwtchError> { - let resp = self._get_profile_attribute(profile, key); + fn create_group(&self, profile: ProfileIdentity, server: &str, name: &str) { + self._create_group(profile.into(), server, name) + } + fn delete_profile(&self, profile: ProfileIdentity, pass: &str) { + self._delete_profile(profile.into(), pass) + } + fn archive_conversation(&self, profile: ProfileIdentity, conversation_id: ConversationID) { + self._archive_conversation(profile.into(), conversation_id.into()) + } + fn delete_contact(&self, profile: ProfileIdentity, conversation_id: ConversationID) { + self._delete_contact(profile.into(), conversation_id.into()) + } + fn import_bundle(&self, profile: ProfileIdentity, bundle: &str) { + self._import_bundle(profile.into(), bundle) + } + fn set_profile_attribute(&self, profile: ProfileIdentity, key: &str, val: &str) { + self._set_profile_attribute(profile.into(), key, val) + } + + fn get_profile_attribute(&self, profile: ProfileIdentity, key: &str) -> Result, CwtchError> { + let resp = self._get_profile_attribute(profile.into(), key); let attr: Attribute = match serde_json::from_str(&resp) { Ok(attr) => attr, Err(e) => return Err(e.to_string()), @@ -132,9 +202,11 @@ impl CwtchLib for CwtchLibGo { false => Ok(None), } } - c_bind!(set_conversation_attribute(profile: &str; conversation_id: i32; key: &str, val: &str) c_SetConversationAttribute); - fn get_conversation_attribute(&self, profile: &str, conversation_id: i32, key: &str) -> Result, CwtchError> { - let resp = self._get_conversation_attribute(profile, conversation_id, key); + fn set_conversation_attribute(&self, profile: ProfileIdentity, conversation_id: ConversationID, key: &str, val: &str) { + self._set_conversation_attribute(profile.into(), conversation_id.into(), key, val) + } + fn get_conversation_attribute(&self, profile: ProfileIdentity, conversation_id: ConversationID, key: &str) -> Result, CwtchError> { + let resp = self._get_conversation_attribute(profile.into(), conversation_id.into(), key); let attr: Attribute = match serde_json::from_str(&resp) { Ok(attr) => attr, Err(e) => return Err(e.to_string()), @@ -144,11 +216,19 @@ impl CwtchLib for CwtchLibGo { false => Ok(None), } } - c_bind!(set_message_attribute(profile: &str; conversation_id: i32, channel_id: i32, message_id: i32; key: &str, val: &str) c_SetMessageAttribute); - c_bind!(change_password(profile: &str, old_pass: &str, new_pass: &str, new_pass_again: &str;;) c_ChangePassword); - c_bind!(export_profile(profile: &str, filename: &str;;) c_ExportProfile); + fn set_message_attribute(&self, profile: ProfileIdentity, conversation_id: ConversationID, channel_id: i32, message_id: i32, key: &str, val: &str) { + self._set_message_attribute(profile.into(), conversation_id.into(), channel_id, message_id, key, val) + } + fn change_password(&self, profile: ProfileIdentity, old_pass: &str, new_pass: &str, new_pass_again: &str) { + self._change_password(profile.into(), old_pass, new_pass, new_pass_again) + } + fn export_profile(&self, profile: ProfileIdentity, filename: &str) { + self._export_profile(profile.into(), filename) + } + c_bind!(import_profile(filename: &str, password: &str;;) c_ImportProfile -> String); + fn shutdown_cwtch(&self) { unsafe { bindings::c_ShutdownCwtch(); @@ -157,13 +237,21 @@ impl CwtchLib for CwtchLibGo { c_bind!(load_servers(password: &str;;) c_LoadServers); c_bind!(create_server(password: &str, description: &str; autostart: i8;) c_CreateServer); - c_bind!(delete_server(onion: &str, current_password: &str;;) c_DeleteServer); + fn delete_server(&self, server: ServerIdentity, current_password: &str) { + self._delete_server(server.into(), current_password) + } c_bind!(launch_servers(;;) c_LaunchServers); - c_bind!(launch_server(onion: &str;;) c_LaunchServer); - c_bind!(stop_server(onion: &str;;) c_StopServer); + fn launch_server(&self, server: ServerIdentity) { + self._launch_server(server.into()) + } + fn stop_server(&self, server: ServerIdentity) { + self._stop_server(server.into()) + } c_bind!(stop_servers(;;) c_StopServers); c_bind!(destroy_servers(;;) c_DestroyServers); - c_bind!(set_server_attribute(onion: &str, key: &str, val: &str;;) c_SetServerAttribute); + fn set_server_attribute(&self, server: ServerIdentity, key: &str, val: &str) { + self._set_server_attribute(server.into(), key, val) + } c_bind!(get_debug_info(;;) c_GetDebugInfo -> String); fn get_appbus_event(&self) -> Event { diff --git a/src/event.rs b/src/event.rs index 1a677d9..1e4ba96 100644 --- a/src/event.rs +++ b/src/event.rs @@ -1,3 +1,4 @@ +use std::borrow::Borrow; use std::collections::HashMap; use chrono::{DateTime, FixedOffset}; @@ -18,6 +19,26 @@ impl From for ProfileIdentity { } } +impl From for String { + fn from(x: ProfileIdentity) -> Self { + x.into() + } +} + +impl From<&str> for ProfileIdentity { + fn from(x: &str) -> Self { + ProfileIdentity(x.to_string()) + } +} + +impl From for &str { + fn from(x: ProfileIdentity) -> Self { + //x.0.to_string().as_str() + let s: String = x.0.to_string(); + s.as_str() + } +} + #[derive(Debug)] /// Contact ID used to refer to contacts in Cwtch pub struct ContactIdentity(String); @@ -38,6 +59,12 @@ impl From for ConversationID { } } +impl From for i32 { + fn from(x: ConversationID) -> Self { + x.0 + } +} + #[derive(Debug)] /// Group ID used to refer to a Group in Cwtch pub struct GroupID(String) ; @@ -58,6 +85,20 @@ impl From for ServerIdentity { } } +impl From<&str> for ServerIdentity { + fn from(x: &str) -> Self { + ServerIdentity(x.to_string()) + } +} + +impl From for &str { + fn from(x: ServerIdentity) -> Self { + //x.0.to_string().borrow() + let s: String = x.0.to_string(); + s.as_str() + } +} + #[derive(Debug)] /// FileKey ID user to refer to a file share in Cwtch pub struct FileKey(String); diff --git a/src/lib.rs b/src/lib.rs index b44de4d..c2a225c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,7 +3,7 @@ #![doc(html_root_url = "https://git.openprivacy.ca/cwtch.im/libcwtch-rs")] #![deny(missing_docs)] -use crate::event::Event; +use crate::event::{ConversationID, Event, ProfileIdentity, ServerIdentity}; mod bindings_go; mod cwtchlib_go; @@ -28,7 +28,7 @@ pub trait CwtchLib { fn send_app_event(&self, event_json: &str); /// Send json of a structs::CwtchEvent to a cwtch profile - fn send_profile_event(&self, profile: &str, event_json: &str); + fn send_profile_event(&self, profile: ProfileIdentity, event_json: &str); /// Pull json of a structs::CwtchEvent off the appbus for responding to fn get_appbus_event(&self) -> Event; @@ -40,88 +40,88 @@ pub trait CwtchLib { fn load_profiles(&self, pass: &str); /// Cause profile to accept conversation - fn accept_conversation(&self, profile: &str, conversation_id: i32); + fn accept_conversation(&self, profile: ProfileIdentity, conversation_id: ConversationID); /// Cause profile to block conversation - fn block_contact(&self, profile: &str, conversation_id: i32); + fn block_contact(&self, profile: ProfileIdentity, conversation_id: ConversationID); /// Cause profile to unblock contact - fn unblock_contact(&self, profile: &str, conversation_id: i32); + fn unblock_contact(&self, profile: ProfileIdentity, conversation_id: ConversationID); /// Get a specific message for conversation of profile by index - fn get_message(&self, profile: &str, conversation_id: i32, message_index: i32) -> String; + fn get_message(&self, profile: ProfileIdentity, conversation_id: ConversationID, message_index: i32) -> String; /// Get a specific message for a conversation by its id - fn get_message_by_id(&self, profile: &str, conversation_id: i32, message_id: i32) -> String; + fn get_message_by_id(&self, profile: ProfileIdentity, conversation_id: ConversationID, message_id: i32) -> String; /// Get a specific message for conversation of profile by hash fn get_message_by_content_hash( &self, - profile: &str, - conversation_id: i32, + profile: ProfileIdentity, + conversation_id: ConversationID, hash: &str, ) -> String; /// Bulk get messages starting at message index and of count amoung - fn get_messages(&self, profile: &str, conversation_id: i32, message_index: i32, count: i32) -> String; + fn get_messages(&self, profile: ProfileIdentity, conversation_id: ConversationID, message_index: i32, count: i32) -> String; /// Send json of a structs::Message from profile to contact. Returns computed sent message (including index and hash values) - fn send_message(&self, profile: &str, conversation_id: i32, msg: &str) -> String; + fn send_message(&self, profile: ProfileIdentity, conversation_id: ConversationID, msg: &str) -> String; /// Send profile's contact an invite for/to target. Returns computed sent message (including index and hash values) - fn send_invitation(&self, profile: &str, conversation_id: i32, target_id: i32) -> String; + fn send_invitation(&self, profile: ProfileIdentity, conversation_id: ConversationID, target_id: i32) -> String; /// share a file file_path with a conersation. Returns computed sent message (including index and hash values) - fn share_file(&self, profile: &str, conversation_id: i32, file_path: &str) -> String; + fn share_file(&self, profile: ProfileIdentity, conversation_id: ConversationID, file_path: &str) -> String; /// download a file from a conversation to the file_path fn download_file( &self, - profile: &str, - conversation_id: i32, + profile: ProfileIdentity, + conversation_id: ConversationID, file_path: &str, manifest_path: &str, file_key: &str, ); /// Query the status of a download - fn check_download_status(&self, profile: &str, file_key: &str); + fn check_download_status(&self, profile: ProfileIdentity, file_key: &str); /// Verufy a download is done, and if not, resume it - fn verify_or_resume_download(&self, profile: &str, conversation_id: i32, file_key: &str); + fn verify_or_resume_download(&self, profile: ProfileIdentity, conversation_id: ConversationID, file_key: &str); /// Ask the ACN inside the Cwtch app to restart the tor connection fn reset_tor(&self); /// Cause profile to create a group on server with name - fn create_group(&self, profile: &str, server: &str, name: &str); + fn create_group(&self, profile: ProfileIdentity, server: &str, name: &str); /// Delete profile with encryption/password check of pass - fn delete_profile(&self, profile: &str, pass: &str); + fn delete_profile(&self, profile: ProfileIdentity, pass: &str); /// Cause profile to archive conversation with contact - fn archive_conversation(&self, profile: &str, conversation_id: i32); + fn archive_conversation(&self, profile: ProfileIdentity, conversation_id: ConversationID); /// Cause profile to delete contact/group identified by handle - fn delete_contact(&self, profile: &str, conversation_id: i32); + fn delete_contact(&self, profile: ProfileIdentity, conversation_id: ConversationID); /// Cuase profile to attempt to import a contact/group/keybundle identified by bundle - fn import_bundle(&self, profile: &str, bundle: &str); + fn import_bundle(&self, profile: ProfileIdentity, bundle: &str); /// Set a profile attribute key to val - fn set_profile_attribute(&self, profile: &str, key: &str, val: &str); + fn set_profile_attribute(&self, profile: ProfileIdentity, key: &str, val: &str); /// Get a profile attribute - fn get_profile_attribute(&self, profile: &str, key: &str) -> Result, CwtchError>; + fn get_profile_attribute(&self, profile: ProfileIdentity, key: &str) -> Result, CwtchError>; /// Set a profile's contact's attribute of key to val - fn set_conversation_attribute(&self, profile: &str, conversation_id: i32, key: &str, val: &str); + fn set_conversation_attribute(&self, profile: ProfileIdentity, conversation_id: ConversationID, key: &str, val: &str); /// Set an attribute on a message in a conversation fn set_message_attribute( &self, - profile: &str, - conversation_id: i32, + profile: ProfileIdentity, + conversation_id: ConversationID, channel_id: i32, message_id: i32, attribute_key: &str, @@ -129,13 +129,13 @@ pub trait CwtchLib { ); /// Get an attribute for a conversation - fn get_conversation_attribute(&self, profile: &str, conversation_id: i32, key: &str) -> Result, CwtchError>; + fn get_conversation_attribute(&self, profile: ProfileIdentity, conversation_id: ConversationID, key: &str) -> Result, CwtchError>; /// Change a profile's password to new_pass if old_pass is correct - fn change_password(&self, profile: &str, old_pass: &str, new_pass: &str, new_pass_again: &str); + fn change_password(&self, profile: ProfileIdentity, old_pass: &str, new_pass: &str, new_pass_again: &str); /// Export a profile to filename - fn export_profile(&self, profile: &str, filename: &str); + fn export_profile(&self, profile: ProfileIdentity, filename: &str); /// Import a profile from a file with supplied password. Json of a profile struct returned on success fn import_profile(&self, filename: &str, password: &str) -> String; @@ -152,16 +152,16 @@ pub trait CwtchLib { fn create_server(&self, password: &str, description: &str, autostart: i8); /// Delete the specified server (if password is correct) - fn delete_server(&self, onion: &str, current_password: &str); + fn delete_server(&self, server: ServerIdentity, current_password: &str); /// Launch all loaded servers fn launch_servers(&self); /// Launch the specified server - fn launch_server(&self, onion: &str); + fn launch_server(&self, server: ServerIdentity); /// Stop the specified server - fn stop_server(&self, onion: &str); + fn stop_server(&self, server: ServerIdentity); /// Stop all running servers fn stop_servers(&self); @@ -170,7 +170,7 @@ pub trait CwtchLib { fn destroy_servers(&self); /// Set the specified server's attribute of key to val - fn set_server_attribute(&self, onion: &str, key: &str, val: &str); + fn set_server_attribute(&self, server: ServerIdentity, key: &str, val: &str); /// Get debug info (mem, goroutine stats) from lcg in json fn get_debug_info(&self) -> String; From 907af56eac7552432daedc7360ef6af8099f8baa Mon Sep 17 00:00:00 2001 From: Dan Ballard Date: Fri, 15 Jul 2022 12:43:27 -0700 Subject: [PATCH 5/7] make api use types part 2 --- src/bindings_go.rs | 68 +++++++++++++++++++++++----------------------- src/event.rs | 20 ++++++-------- src/lib.rs | 8 +++--- 3 files changed, 46 insertions(+), 50 deletions(-) diff --git a/src/bindings_go.rs b/src/bindings_go.rs index 37c4874..1acb34c 100644 --- a/src/bindings_go.rs +++ b/src/bindings_go.rs @@ -8,7 +8,7 @@ use std::ffi::CString; use super::CwtchLib; use crate::cwtchlib_go::bindings; -use crate::{ConversationID, CwtchError, ProfileIdentity, ServerIdentity, structs::*}; +use crate::{ConversationID, CwtchError, FileKey, ProfileIdentity, ServerIdentity, structs::*}; use crate::event::Event; struct c_str_wrap { @@ -123,48 +123,48 @@ impl CwtchLib for CwtchLibGo { c_bind!(started(;;) c_Started -> i32); c_bind!(send_app_event(event_json: &str;;) c_SendAppEvent); fn send_profile_event(&self, profile: ProfileIdentity, event_jason: &str) { - self._send_profile_event(profile.into(), event_jason) + self._send_profile_event(String::from(profile).as_str(), event_jason) } c_bind!(create_profile(nick: &str, pass: &str;;) c_CreateProfile); c_bind!(load_profiles(pass: &str;;) c_LoadProfiles); fn accept_conversation(&self, profile: ProfileIdentity, conversation_id: ConversationID) { - self._accept_conversation(profile.into(), conversation_id.into()) + self._accept_conversation(String::from(profile).as_str(), conversation_id.into()) } fn block_contact(&self, profile: ProfileIdentity, conversation_id: ConversationID) { - self._block_contact(profile.into(), conversation_id.into()) + self._block_contact(String::from(profile).as_str(), conversation_id.into()) } fn unblock_contact(&self, profile: ProfileIdentity, conversation_id: ConversationID) { - self._unblock_contact(profile.into(), conversation_id.into()) + self._unblock_contact(String::from(profile).as_str(), conversation_id.into()) } fn get_message(&self, profile: ProfileIdentity, conversation_id: ConversationID, message_index: i32) -> String { - self._get_message(profile.into(), conversation_id.into(), message_index) + self._get_message(String::from(profile).as_str(), conversation_id.into(), message_index) } fn get_message_by_id(&self, profile: ProfileIdentity, conversation_id: ConversationID, message_id: i32) -> String { - self._get_message_by_id(profile.into(), conversation_id.into(), message_id) + self._get_message_by_id(String::from(profile).as_str(), conversation_id.into(), message_id) } fn get_message_by_content_hash(&self, profile: ProfileIdentity, conversation_id: ConversationID, hash: &str) -> String { - self._get_message_by_content_hash(profile.into(), conversation_id.into(), hash) + self._get_message_by_content_hash(String::from(profile).as_str(), conversation_id.into(), hash) } fn get_messages(&self, profile: ProfileIdentity, conversation_id: ConversationID, message_index: i32, count: i32) -> String { - self.get_messages(profile.into(), conversation_id, message_index, count) + self._get_messages(String::from(profile).as_str(), conversation_id.into(), message_index, count) } fn send_message(&self, profile: ProfileIdentity, conversation_id: ConversationID, msg: &str) -> String { - self._send_message(profile.into(), conversation_id.into(), msg) + self._send_message(String::from(profile).as_str(), conversation_id.into(), msg) } fn send_invitation(&self, profile: ProfileIdentity, conversation_id: ConversationID, target_id: i32) -> String { - self._send_invitation(profile.into(), conversation_id.into(), target_id) + self._send_invitation(String::from(profile).as_str(), conversation_id.into(), target_id) } fn share_file(&self, profile: ProfileIdentity, conversation_id: ConversationID, file_path: &str) -> String { - self.share_file(profile.into(), conversation_id, file_path) + self._share_file(String::from(profile).as_str(), conversation_id.into(), file_path) } - fn download_file(&self, profile: ProfileIdentity, conversation_id: ConversationID, file_path: &str, manifest_path: &str, file_key: &str) { - self._download_file(profile.into(), conversation_id.into(), file_path, manifest_path, file_key) + fn download_file(&self, profile: ProfileIdentity, conversation_id: ConversationID, file_path: &str, manifest_path: &str, file_key: FileKey) { + self._download_file(String::from(profile).as_str(), conversation_id.into(), file_path, manifest_path, String::from(file_key).as_str()) } - fn check_download_status(&self, profile: ProfileIdentity, file_key: &str) { - self._check_download_status(profile.into(), file_key) + fn check_download_status(&self, profile: ProfileIdentity, file_key: FileKey) { + self._check_download_status(String::from(profile).as_str(), String::from(file_key).as_str()) } - fn verify_or_resume_download(&self, profile: ProfileIdentity, conversation_id: ConversationID, file_key: &str) { - self._verify_or_resume_download(profile.into(), conversation_id.into(), file_key) + fn verify_or_resume_download(&self, profile: ProfileIdentity, conversation_id: ConversationID, file_key: FileKey) { + self._verify_or_resume_download(String::from(profile).as_str(), conversation_id.into(), String::from(file_key).as_str()) } fn reset_tor(&self) { @@ -173,26 +173,26 @@ impl CwtchLib for CwtchLibGo { } } fn create_group(&self, profile: ProfileIdentity, server: &str, name: &str) { - self._create_group(profile.into(), server, name) + self._create_group(String::from(profile).as_str(), server, name) } fn delete_profile(&self, profile: ProfileIdentity, pass: &str) { - self._delete_profile(profile.into(), pass) + self._delete_profile(String::from(profile).as_str(), pass) } fn archive_conversation(&self, profile: ProfileIdentity, conversation_id: ConversationID) { - self._archive_conversation(profile.into(), conversation_id.into()) + self._archive_conversation(String::from(profile).as_str(), conversation_id.into()) } fn delete_contact(&self, profile: ProfileIdentity, conversation_id: ConversationID) { - self._delete_contact(profile.into(), conversation_id.into()) + self._delete_contact(String::from(profile).as_str(), conversation_id.into()) } fn import_bundle(&self, profile: ProfileIdentity, bundle: &str) { - self._import_bundle(profile.into(), bundle) + self._import_bundle(String::from(profile).as_str(), bundle) } fn set_profile_attribute(&self, profile: ProfileIdentity, key: &str, val: &str) { - self._set_profile_attribute(profile.into(), key, val) + self._set_profile_attribute(String::from(profile).as_str(), key, val) } fn get_profile_attribute(&self, profile: ProfileIdentity, key: &str) -> Result, CwtchError> { - let resp = self._get_profile_attribute(profile.into(), key); + let resp = self._get_profile_attribute(String::from(profile).as_str(), key); let attr: Attribute = match serde_json::from_str(&resp) { Ok(attr) => attr, Err(e) => return Err(e.to_string()), @@ -203,10 +203,10 @@ impl CwtchLib for CwtchLibGo { } } fn set_conversation_attribute(&self, profile: ProfileIdentity, conversation_id: ConversationID, key: &str, val: &str) { - self._set_conversation_attribute(profile.into(), conversation_id.into(), key, val) + self._set_conversation_attribute(String::from(profile).as_str(), conversation_id.into(), key, val) } fn get_conversation_attribute(&self, profile: ProfileIdentity, conversation_id: ConversationID, key: &str) -> Result, CwtchError> { - let resp = self._get_conversation_attribute(profile.into(), conversation_id.into(), key); + let resp = self._get_conversation_attribute(String::from(profile).as_str(), conversation_id.into(), key); let attr: Attribute = match serde_json::from_str(&resp) { Ok(attr) => attr, Err(e) => return Err(e.to_string()), @@ -217,13 +217,13 @@ impl CwtchLib for CwtchLibGo { } } fn set_message_attribute(&self, profile: ProfileIdentity, conversation_id: ConversationID, channel_id: i32, message_id: i32, key: &str, val: &str) { - self._set_message_attribute(profile.into(), conversation_id.into(), channel_id, message_id, key, val) + self._set_message_attribute(String::from(profile).as_str(), conversation_id.into(), channel_id, message_id, key, val) } fn change_password(&self, profile: ProfileIdentity, old_pass: &str, new_pass: &str, new_pass_again: &str) { - self._change_password(profile.into(), old_pass, new_pass, new_pass_again) + self._change_password(String::from(profile).as_str(), old_pass, new_pass, new_pass_again) } fn export_profile(&self, profile: ProfileIdentity, filename: &str) { - self._export_profile(profile.into(), filename) + self._export_profile(String::from(profile).as_str(), filename) } c_bind!(import_profile(filename: &str, password: &str;;) c_ImportProfile -> String); @@ -238,19 +238,19 @@ impl CwtchLib for CwtchLibGo { c_bind!(load_servers(password: &str;;) c_LoadServers); c_bind!(create_server(password: &str, description: &str; autostart: i8;) c_CreateServer); fn delete_server(&self, server: ServerIdentity, current_password: &str) { - self._delete_server(server.into(), current_password) + self._delete_server(String::from(server).as_str(), current_password) } c_bind!(launch_servers(;;) c_LaunchServers); fn launch_server(&self, server: ServerIdentity) { - self._launch_server(server.into()) + self._launch_server(String::from(server).as_str()) } fn stop_server(&self, server: ServerIdentity) { - self._stop_server(server.into()) + self._stop_server(String::from(server).as_str()) } c_bind!(stop_servers(;;) c_StopServers); c_bind!(destroy_servers(;;) c_DestroyServers); fn set_server_attribute(&self, server: ServerIdentity, key: &str, val: &str) { - self._set_server_attribute(server.into(), key, val) + self._set_server_attribute(String::from(server).as_str(), key, val) } c_bind!(get_debug_info(;;) c_GetDebugInfo -> String); diff --git a/src/event.rs b/src/event.rs index 1e4ba96..3aaa5a7 100644 --- a/src/event.rs +++ b/src/event.rs @@ -31,14 +31,6 @@ impl From<&str> for ProfileIdentity { } } -impl From for &str { - fn from(x: ProfileIdentity) -> Self { - //x.0.to_string().as_str() - let s: String = x.0.to_string(); - s.as_str() - } -} - #[derive(Debug)] /// Contact ID used to refer to contacts in Cwtch pub struct ContactIdentity(String); @@ -91,11 +83,9 @@ impl From<&str> for ServerIdentity { } } -impl From for &str { +impl From for String { fn from(x: ServerIdentity) -> Self { - //x.0.to_string().borrow() - let s: String = x.0.to_string(); - s.as_str() + x.into() } } @@ -109,6 +99,12 @@ impl From for FileKey { } } +impl From for String { + fn from(x: FileKey) -> Self { + x.into() + } +} + #[derive(Debug)] /// Enum for type of notification a UI/client should emit for a new message pub enum MessageNotification { diff --git a/src/lib.rs b/src/lib.rs index c2a225c..52dd6f7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,7 +3,7 @@ #![doc(html_root_url = "https://git.openprivacy.ca/cwtch.im/libcwtch-rs")] #![deny(missing_docs)] -use crate::event::{ConversationID, Event, ProfileIdentity, ServerIdentity}; +use crate::event::{ConversationID, Event, FileKey, ProfileIdentity, ServerIdentity}; mod bindings_go; mod cwtchlib_go; @@ -81,14 +81,14 @@ pub trait CwtchLib { conversation_id: ConversationID, file_path: &str, manifest_path: &str, - file_key: &str, + file_key: FileKey, ); /// Query the status of a download - fn check_download_status(&self, profile: ProfileIdentity, file_key: &str); + fn check_download_status(&self, profile: ProfileIdentity, file_key: FileKey); /// Verufy a download is done, and if not, resume it - fn verify_or_resume_download(&self, profile: ProfileIdentity, conversation_id: ConversationID, file_key: &str); + fn verify_or_resume_download(&self, profile: ProfileIdentity, conversation_id: ConversationID, file_key: FileKey); /// Ask the ACN inside the Cwtch app to restart the tor connection fn reset_tor(&self); From 752d2967e08d34ebde6c795766b0506fb161a38e Mon Sep 17 00:00:00 2001 From: Dan Ballard Date: Fri, 15 Jul 2022 14:39:32 -0700 Subject: [PATCH 6/7] address PR comments --- src/event.rs | 65 +++++++++++++++++++++++++--------------------------- 1 file changed, 31 insertions(+), 34 deletions(-) diff --git a/src/event.rs b/src/event.rs index 3aaa5a7..78ff705 100644 --- a/src/event.rs +++ b/src/event.rs @@ -1,13 +1,10 @@ -use std::borrow::Borrow; use std::collections::HashMap; use chrono::{DateTime, FixedOffset}; -use chrono::format::Fixed::TimezoneOffset; use chrono::prelude::*; -use chrono::offset::LocalResult; use std::convert::From; -use crate::structs::{ACL, ConnectionState, CwtchEvent}; +use crate::structs::{ACL, ConnectionState, CwtchEvent, Message, Profile}; #[derive(Debug)] /// Profile ID used to refer to profiles in Cwtch @@ -211,10 +208,8 @@ pub enum Event { picture: String, /// is the profile online online: String, - /// json of the contacts - contacts_json: String, - /// json of known servers - server_json: String, //ServerList + /// The deserialized profile with contacts and server info + profile_data: Result, }, /// Cwtch had an error at the app level (not profile level), usually in response to an API call AppError { @@ -243,7 +238,7 @@ pub enum Event { /// the percent of ACN boot (-1 to 100) progress: i8, /// an associated status message from the ACN - status: String + status: String, }, /// Version of the ACN (currently tor) ACNVersion { @@ -277,7 +272,7 @@ pub enum Event { /// identity of server server: ServerIdentity, /// intent of the server to be running or not - intent: ServerIntent + intent: ServerIntent, }, /// Notice a server was deleted (in response to an API call) and is no longer usable ServerDeleted { @@ -313,7 +308,7 @@ pub enum Event { /// time message was received timestamp_received: DateTime, /// the message - message: String, + message: Result, /// notification instructions (based on settings) notification: MessageNotification, /// path to picture for the contact @@ -363,7 +358,8 @@ pub enum Event { /// Notice from the network check plugin, a profile self check test by attempting to connecting to itself NetworkStatus { /// profile the check was performed on - profile: ProfileIdentity, // it's technically a profile self check + profile: ProfileIdentity, + // it's technically a profile self check /// error if there was one (can be empty) error: String, /// status of profile self connection check @@ -409,7 +405,7 @@ pub enum Event { /// contact id contact: ContactIdentity, /// error string - error: String + error: String, }, /// a peer has acked a message PeerAcknowledgement { @@ -435,7 +431,7 @@ pub enum Event { /// message index index: i32, /// the message - message: String, + message: Result, /// hash of the message content_hash: String, /// path to picture for sender @@ -488,7 +484,7 @@ pub enum Event { /// server the group is on group_server: String, /// state of connection to server - state: ConnectionState + state: ConnectionState, }, /// A getval call to a peer has returned a response NewRetValMessageFromPeer { @@ -505,7 +501,7 @@ pub enum Event { /// value data: String, /// optional filepath if there was a downloaded component - file_path: Option + file_path: Option, }, /// result of a call to share a file, the filekey and manifest to operate on it ShareManifest { @@ -514,7 +510,7 @@ pub enum Event { /// filekey filekey: FileKey, /// serialized manifest of the share - serializedManifest: String, + serialized_manifest: String, }, /// Information on a peer fileshare has been received ManifestSizeReceived { @@ -592,15 +588,21 @@ impl From<&CwtchEvent> for Event { match cwtch_event.event_type.as_str() { "CwtchStarted" => Event::CwtchStarted, "NewPeer" => Event::NewPeer { - profile: cwtch_event.data["Identity"].clone().into(), - tag: cwtch_event.data["tag"].clone(), - created: cwtch_event.data["Created"] == "true", - name: cwtch_event.data["name"].clone(), - default_picture: cwtch_event.data["defaultPicture"].clone(), - picture: cwtch_event.data["picture"].clone(), - online: cwtch_event.data["Online"].clone(), - contacts_json: cwtch_event.data["ContactsJson"].clone(), - server_json: cwtch_event.data["ServerList"].clone(), + profile: cwtch_event.data["Identity"].clone().into(), + tag: cwtch_event.data["tag"].clone(), + created: cwtch_event.data["Created"] == "true", + name: cwtch_event.data["name"].clone(), + default_picture: cwtch_event.data["defaultPicture"].clone(), + picture: cwtch_event.data["picture"].clone(), + online: cwtch_event.data["Online"].clone(), + profile_data: Profile::new( + &cwtch_event.data["Identity"], + &cwtch_event.data["name"], + &cwtch_event.data["picture"], + &cwtch_event.data["ContactsJson"], + &cwtch_event.data["ServerList"], + ) + }, "NewMessageFromPeer" => Event::NewMessageFromPeer { profile: cwtch_event.data["ProfileOnion"].clone().into(), @@ -608,7 +610,7 @@ impl From<&CwtchEvent> for Event { contact: cwtch_event.data["RemotePeer"].clone().into(), nick: cwtch_event.data["Nick"].clone(), timestamp_received: DateTime::parse_from_rfc3339(cwtch_event.data["TimestampReceived"].as_str()).unwrap_or( DateTime::from(Utc::now())), - message: cwtch_event.data["Data"].clone(), + message: serde_json::from_str(&cwtch_event.data["Data"]), notification: MessageNotification::from(cwtch_event.data["notification"].clone()), picture: cwtch_event.data["Picture"].clone(), }, @@ -663,11 +665,6 @@ impl From<&CwtchEvent> for Event { key: cwtch_event.data["Key"].clone(), value: cwtch_event.data["Data"].clone(), }, - "IndexedAcknowledgement" => Event::IndexedAcknowledgement { - profile: cwtch_event.data["ProfileOnion"].clone().into(), - conversation_id: cwtch_event.data["ConversationID"].parse().unwrap_or(-1), - index: cwtch_event.data["Index"].parse().unwrap_or(-1), - }, "IndexedAcknowledgement" => Event::IndexedFailure { profile: cwtch_event.data["ProfileOnion"].clone().into(), conversation_id: cwtch_event.data["ConversationID"].parse().unwrap_or(-1).into(), @@ -678,7 +675,7 @@ impl From<&CwtchEvent> for Event { "ShareManifest" => Event::ShareManifest { profile: cwtch_event.data["ProfileOnion"].clone().into(), filekey: cwtch_event.data["FileKey"].clone().into(), - serializedManifest: cwtch_event.data["SerializedManifest"].clone(), + serialized_manifest: cwtch_event.data["SerializedManifest"].clone(), }, "NewServer" => Event::NewServer { server: cwtch_event.data["Onion"].clone().into(), @@ -751,7 +748,7 @@ impl From<&CwtchEvent> for Event { conversation_id: cwtch_event.data["ConversationID"].parse().unwrap_or(-2).into(), contact: cwtch_event.data["RemotePeer"].clone().into(), nick: cwtch_event.data["Nick"].clone(), - message: cwtch_event.data["Data"].clone(), + message: serde_json::from_str(&cwtch_event.data["Data"]), notification: MessageNotification::from(cwtch_event.data["notification"].clone()), picture: cwtch_event.data["Picture"].clone(), }, From 52b799ef7c14d35f69de1062c5cb91ab40a1f061 Mon Sep 17 00:00:00 2001 From: Dan Ballard Date: Thu, 21 Jul 2022 01:04:21 -0700 Subject: [PATCH 7/7] improvements for use with imp; migrate echobot to --- Cargo.lock | 12 +++ Cargo.toml | 1 + examples/echobot.rs | 37 +++----- src/bindings_go.rs | 59 +++++++------ src/event.rs | 210 ++++++++++++++++++++++++-------------------- src/lib.rs | 52 +++++------ src/structs.rs | 86 +++++++++++++----- 7 files changed, 262 insertions(+), 195 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8e7149e..6b100ef 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -149,6 +149,7 @@ dependencies = [ "libc", "serde", "serde_json", + "serde_repr", "serde_with", "sha2", ] @@ -233,6 +234,17 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_repr" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2ad84e47328a31223de7fed7a4f5087f2d6ddfe586cf3ca25b7a165bc0a5aed" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "serde_with" version = "1.11.0" diff --git a/Cargo.toml b/Cargo.toml index c63047d..2962911 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,4 +18,5 @@ libc = "0.2" serde_json = "1.0" serde = { version = "1.0.127", features = ["derive"] } serde_with = { version = "1.10.0" } +serde_repr = "0.1" chrono = "0.4.19" diff --git a/examples/echobot.rs b/examples/echobot.rs index 93f8b40..92770b3 100644 --- a/examples/echobot.rs +++ b/examples/echobot.rs @@ -3,6 +3,7 @@ use std::thread; use libcwtch; use libcwtch::structs::*; use libcwtch::CwtchLib; +use libcwtch::event::Event; fn main() { let bot_home = "example_cwtch_dir"; @@ -19,46 +20,30 @@ fn main() { let event_loop_handle = thread::spawn(move || { loop { - let event_str = cwtch.get_appbus_event(); - println!("event: {}", event_str); + let event = cwtch.get_appbus_event(); + println!("event: {:?}", event); - let event: CwtchEvent = - serde_json::from_str(&event_str).expect("Error parsing Cwtch event"); - match event.event_type.as_str() { - "CwtchStarted" => { + match event { + Event::CwtchStarted => { println!("event CwtchStarted!"); println!("Creating bot"); cwtch.create_profile("Echobot", "be gay do crime"); } - "NewPeer" => { + Event::NewPeer { profile_id, tag, created, name, default_picture, picture, online, profile_data } => { println!( "\n***** {} at {} *****\n", - event.data["name"], event.data["Identity"] + name, profile_id.as_str() ); // process json for profile, contacts and servers...else { - let profile = match Profile::new( - &event.data["Identity"], - &event.data["name"], - &event.data["picture"], - &event.data["ContactsJson"], - &event.data["ServerList"], - ) { - Ok(p) => p, - Err(e) => panic!("error parsing profile: {}", e), - }; + let profile = profile_data; print!("profile: {:?}", profile); } - "NewMessageFromPeer" => { - let to = &event.data["ProfileOnion"]; - let conversation_id = event.data["ConversationID"].parse::().unwrap(); - let message: Message = - serde_json::from_str(&event.data["Data"]).expect("Error parsing message"); - - let response = Message { o: 1, d: message.d }; + Event::NewMessageFromPeer { profile_id, conversation_id, contact_id: contact, nick, timestamp_received, message, notification, picture } => { + let response = Message { o: message.o.into(), d: message.d }; let response_json = serde_json::to_string(&response).expect("Error parsing json response"); - cwtch.send_message(&to, conversation_id, &response_json); + cwtch.send_message( &profile_id, conversation_id, &response_json); } _ => eprintln!("unhandled event!"), }; diff --git a/src/bindings_go.rs b/src/bindings_go.rs index 1acb34c..10e5141 100644 --- a/src/bindings_go.rs +++ b/src/bindings_go.rs @@ -122,48 +122,49 @@ impl CwtchLib for CwtchLibGo { c_bind!(start_cwtch(app_dir: &str, tor_path: &str;;) c_StartCwtch -> i32); c_bind!(started(;;) c_Started -> i32); c_bind!(send_app_event(event_json: &str;;) c_SendAppEvent); - fn send_profile_event(&self, profile: ProfileIdentity, event_jason: &str) { - self._send_profile_event(String::from(profile).as_str(), event_jason) + fn send_profile_event(&self, profile: &ProfileIdentity, event_jason: &str) { + self._send_profile_event(profile.as_str(), event_jason) + } c_bind!(create_profile(nick: &str, pass: &str;;) c_CreateProfile); c_bind!(load_profiles(pass: &str;;) c_LoadProfiles); - fn accept_conversation(&self, profile: ProfileIdentity, conversation_id: ConversationID) { - self._accept_conversation(String::from(profile).as_str(), conversation_id.into()) + fn accept_conversation(&self, profile: &ProfileIdentity, conversation_id: ConversationID) { + self._accept_conversation(profile.as_str(), conversation_id.into()) } - fn block_contact(&self, profile: ProfileIdentity, conversation_id: ConversationID) { + fn block_contact(&self, profile: &ProfileIdentity, conversation_id: ConversationID) { self._block_contact(String::from(profile).as_str(), conversation_id.into()) } - fn unblock_contact(&self, profile: ProfileIdentity, conversation_id: ConversationID) { + fn unblock_contact(&self, profile: &ProfileIdentity, conversation_id: ConversationID) { self._unblock_contact(String::from(profile).as_str(), conversation_id.into()) } - fn get_message(&self, profile: ProfileIdentity, conversation_id: ConversationID, message_index: i32) -> String { + fn get_message(&self, profile: &ProfileIdentity, conversation_id: ConversationID, message_index: i32) -> String { self._get_message(String::from(profile).as_str(), conversation_id.into(), message_index) } - fn get_message_by_id(&self, profile: ProfileIdentity, conversation_id: ConversationID, message_id: i32) -> String { + fn get_message_by_id(&self, profile: &ProfileIdentity, conversation_id: ConversationID, message_id: i32) -> String { self._get_message_by_id(String::from(profile).as_str(), conversation_id.into(), message_id) } - fn get_message_by_content_hash(&self, profile: ProfileIdentity, conversation_id: ConversationID, hash: &str) -> String { + fn get_message_by_content_hash(&self, profile: &ProfileIdentity, conversation_id: ConversationID, hash: &str) -> String { self._get_message_by_content_hash(String::from(profile).as_str(), conversation_id.into(), hash) } - fn get_messages(&self, profile: ProfileIdentity, conversation_id: ConversationID, message_index: i32, count: i32) -> String { + fn get_messages(&self, profile: &ProfileIdentity, conversation_id: ConversationID, message_index: i32, count: i32) -> String { self._get_messages(String::from(profile).as_str(), conversation_id.into(), message_index, count) } - fn send_message(&self, profile: ProfileIdentity, conversation_id: ConversationID, msg: &str) -> String { + fn send_message(&self, profile: &ProfileIdentity, conversation_id: ConversationID, msg: &str) -> String { self._send_message(String::from(profile).as_str(), conversation_id.into(), msg) } - fn send_invitation(&self, profile: ProfileIdentity, conversation_id: ConversationID, target_id: i32) -> String { + fn send_invitation(&self, profile: &ProfileIdentity, conversation_id: ConversationID, target_id: i32) -> String { self._send_invitation(String::from(profile).as_str(), conversation_id.into(), target_id) } - fn share_file(&self, profile: ProfileIdentity, conversation_id: ConversationID, file_path: &str) -> String { + fn share_file(&self, profile: &ProfileIdentity, conversation_id: ConversationID, file_path: &str) -> String { self._share_file(String::from(profile).as_str(), conversation_id.into(), file_path) } - fn download_file(&self, profile: ProfileIdentity, conversation_id: ConversationID, file_path: &str, manifest_path: &str, file_key: FileKey) { + fn download_file(&self, profile: &ProfileIdentity, conversation_id: ConversationID, file_path: &str, manifest_path: &str, file_key: FileKey) { self._download_file(String::from(profile).as_str(), conversation_id.into(), file_path, manifest_path, String::from(file_key).as_str()) } - fn check_download_status(&self, profile: ProfileIdentity, file_key: FileKey) { + fn check_download_status(&self, profile: &ProfileIdentity, file_key: FileKey) { self._check_download_status(String::from(profile).as_str(), String::from(file_key).as_str()) } - fn verify_or_resume_download(&self, profile: ProfileIdentity, conversation_id: ConversationID, file_key: FileKey) { + fn verify_or_resume_download(&self, profile: &ProfileIdentity, conversation_id: ConversationID, file_key: FileKey) { self._verify_or_resume_download(String::from(profile).as_str(), conversation_id.into(), String::from(file_key).as_str()) } @@ -172,26 +173,26 @@ impl CwtchLib for CwtchLibGo { bindings::c_ResetTor(); } } - fn create_group(&self, profile: ProfileIdentity, server: &str, name: &str) { + fn create_group(&self, profile: &ProfileIdentity, server: &str, name: &str) { self._create_group(String::from(profile).as_str(), server, name) } - fn delete_profile(&self, profile: ProfileIdentity, pass: &str) { + fn delete_profile(&self, profile: &ProfileIdentity, pass: &str) { self._delete_profile(String::from(profile).as_str(), pass) } - fn archive_conversation(&self, profile: ProfileIdentity, conversation_id: ConversationID) { + fn archive_conversation(&self, profile: &ProfileIdentity, conversation_id: ConversationID) { self._archive_conversation(String::from(profile).as_str(), conversation_id.into()) } - fn delete_contact(&self, profile: ProfileIdentity, conversation_id: ConversationID) { - self._delete_contact(String::from(profile).as_str(), conversation_id.into()) + fn delete_contact(&self, profile: &ProfileIdentity, conversation_id: ConversationID) { + self._delete_contact(profile.as_str(), conversation_id.into()) } - fn import_bundle(&self, profile: ProfileIdentity, bundle: &str) { + fn import_bundle(&self, profile: &ProfileIdentity, bundle: &str) { self._import_bundle(String::from(profile).as_str(), bundle) } - fn set_profile_attribute(&self, profile: ProfileIdentity, key: &str, val: &str) { + fn set_profile_attribute(&self, profile: &ProfileIdentity, key: &str, val: &str) { self._set_profile_attribute(String::from(profile).as_str(), key, val) } - fn get_profile_attribute(&self, profile: ProfileIdentity, key: &str) -> Result, CwtchError> { + fn get_profile_attribute(&self, profile: &ProfileIdentity, key: &str) -> Result, CwtchError> { let resp = self._get_profile_attribute(String::from(profile).as_str(), key); let attr: Attribute = match serde_json::from_str(&resp) { Ok(attr) => attr, @@ -202,10 +203,10 @@ impl CwtchLib for CwtchLibGo { false => Ok(None), } } - fn set_conversation_attribute(&self, profile: ProfileIdentity, conversation_id: ConversationID, key: &str, val: &str) { + fn set_conversation_attribute(&self, profile: &ProfileIdentity, conversation_id: ConversationID, key: &str, val: &str) { self._set_conversation_attribute(String::from(profile).as_str(), conversation_id.into(), key, val) } - fn get_conversation_attribute(&self, profile: ProfileIdentity, conversation_id: ConversationID, key: &str) -> Result, CwtchError> { + fn get_conversation_attribute(&self, profile: &ProfileIdentity, conversation_id: ConversationID, key: &str) -> Result, CwtchError> { let resp = self._get_conversation_attribute(String::from(profile).as_str(), conversation_id.into(), key); let attr: Attribute = match serde_json::from_str(&resp) { Ok(attr) => attr, @@ -216,13 +217,13 @@ impl CwtchLib for CwtchLibGo { false => Ok(None), } } - fn set_message_attribute(&self, profile: ProfileIdentity, conversation_id: ConversationID, channel_id: i32, message_id: i32, key: &str, val: &str) { + fn set_message_attribute(&self, profile: &ProfileIdentity, conversation_id: ConversationID, channel_id: i32, message_id: i32, key: &str, val: &str) { self._set_message_attribute(String::from(profile).as_str(), conversation_id.into(), channel_id, message_id, key, val) } - fn change_password(&self, profile: ProfileIdentity, old_pass: &str, new_pass: &str, new_pass_again: &str) { + fn change_password(&self, profile: &ProfileIdentity, old_pass: &str, new_pass: &str, new_pass_again: &str) { self._change_password(String::from(profile).as_str(), old_pass, new_pass, new_pass_again) } - fn export_profile(&self, profile: ProfileIdentity, filename: &str) { + fn export_profile(&self, profile: &ProfileIdentity, filename: &str) { self._export_profile(String::from(profile).as_str(), filename) } diff --git a/src/event.rs b/src/event.rs index 78ff705..6987b09 100644 --- a/src/event.rs +++ b/src/event.rs @@ -1,12 +1,14 @@ use std::collections::HashMap; +use serde::{Deserialize, Serialize}; use chrono::{DateTime, FixedOffset}; use chrono::prelude::*; use std::convert::From; -use crate::structs::{ACL, ConnectionState, CwtchEvent, Message, Profile}; +use crate::structs::{ACL, ConnectionState, CwtchEvent, Message, Profile, Settings}; -#[derive(Debug)] + +#[derive(Debug, Clone, Serialize, Deserialize, Hash, Eq, PartialEq)] /// Profile ID used to refer to profiles in Cwtch pub struct ProfileIdentity(String); @@ -28,7 +30,21 @@ impl From<&str> for ProfileIdentity { } } -#[derive(Debug)] +impl From<&ProfileIdentity> for String { + fn from(x: &ProfileIdentity) -> Self { + x.0.clone() + } +} + +impl ProfileIdentity { + /// Get &str of ProfileIdentity String + pub fn as_str(&self) -> &str { + self.0.as_str() + } +} + + +#[derive(Debug, Clone, Serialize, Deserialize, Hash, Eq, PartialEq)] /// Contact ID used to refer to contacts in Cwtch pub struct ContactIdentity(String); @@ -38,9 +54,16 @@ impl From for ContactIdentity { } } -#[derive(Debug)] +impl ContactIdentity { + /// Get &str of ContactIdentity String + pub fn as_str(&self) -> &str { + self.0.as_str() + } +} + +#[derive(Debug, Clone, Copy, Serialize, Deserialize, Hash, Eq, PartialEq)] /// Conversation ID user to refer to a conversation with a Contact or Group in Cwtch -pub struct ConversationID(i32) ; +pub struct ConversationID(pub i32) ; impl From for ConversationID { fn from(x: i32) -> Self { @@ -54,7 +77,8 @@ impl From for i32 { } } -#[derive(Debug)] + +#[derive(Debug, Clone, Serialize, Deserialize, Hash, Eq, PartialEq)] /// Group ID used to refer to a Group in Cwtch pub struct GroupID(String) ; @@ -64,7 +88,8 @@ impl From for GroupID { } } -#[derive(Debug)] + +#[derive(Debug, Clone, Serialize, Deserialize, Hash, Eq, PartialEq)] /// Server ID user to refer to a server in Cwtch pub struct ServerIdentity(String); @@ -86,7 +111,7 @@ impl From for String { } } -#[derive(Debug)] +#[derive(Debug, Clone, Serialize, Deserialize, Hash, Eq, PartialEq)] /// FileKey ID user to refer to a file share in Cwtch pub struct FileKey(String); @@ -102,7 +127,7 @@ impl From for String { } } -#[derive(Debug)] +#[derive(Debug, Clone)] /// Enum for type of notification a UI/client should emit for a new message pub enum MessageNotification { /// means no notification @@ -124,7 +149,7 @@ impl From for MessageNotification { } } -#[derive(Debug)] +#[derive(Debug, Clone)] /// Enum for results from NetworkCheck plugin on profiles pub enum NetworkCheckStatus { /// There was an error, this profile cannot connect to itself @@ -143,7 +168,7 @@ impl From for NetworkCheckStatus { } } -#[derive(Debug)] +#[derive(Debug, Clone)] /// Enum denoting server storage mode pub enum ServerStorageType { /// indicates some app supplied default password is being used on this server for storage encryption @@ -165,7 +190,7 @@ impl From for ServerStorageType { } } -#[derive(Debug)] +#[derive(Debug, Clone)] /// Enum used by servers to declare their intent to be running or stopped pub enum ServerIntent { /// a server intends to be running @@ -184,7 +209,7 @@ impl From for ServerIntent { } } -#[derive(Debug)] +#[derive(Debug, Clone)] /// Enum for events from Cwtch appbus pub enum Event { @@ -195,7 +220,7 @@ pub enum Event { /// A new peer has been loaded, details of peer. Identity should be stored so further API calls can be made NewPeer { /// identity field - profile: ProfileIdentity, + profile_id: ProfileIdentity, /// optional client specified tag tag: String, /// is this a newly created profile event, or a load @@ -219,7 +244,7 @@ pub enum Event { /// Global settings being emited from lcg, usually in response to them being sent to be saved by client UpdateGlobalSettings { /// map of setting names to values (https://git.openprivacy.ca/cwtch.im/libcwtch-go/src/branch/trunk/utils/settings.go) - settings: HashMap, + settings: Settings, }, /// A profile has an error, usually emited in response to an API call PeerError { @@ -229,7 +254,7 @@ pub enum Event { /// A profile was successfully deleted, it is no longer usable PeerDeleted { /// identity of deleted peer - profile: ProfileIdentity + profile_id: ProfileIdentity }, /// Cwtch is shutting down, stop making API calls Shutdown, @@ -298,17 +323,17 @@ pub enum Event { /// A new message was received NewMessageFromPeer { /// identity field - profile: ProfileIdentity, + profile_id: ProfileIdentity, /// conversation id conversation_id: ConversationID, /// contact id - contact: ContactIdentity, + contact_id: ContactIdentity, /// name of contact nick: String, /// time message was received timestamp_received: DateTime, /// the message - message: Result, + message: Message, /// notification instructions (based on settings) notification: MessageNotification, /// path to picture for the contact @@ -317,11 +342,11 @@ pub enum Event { /// A new contact has been created (imported, added, or contacted by) ContactCreated { /// identity field - profile: ProfileIdentity, + profile_id: ProfileIdentity, /// conversation id conversation_id: ConversationID, /// contact id - contact: ContactIdentity, + contact_id: ContactIdentity, /// name of group nick: String, /// connection status to group server @@ -349,16 +374,16 @@ pub enum Event { /// A peer has changed state PeerStateChange { /// identity field - profile: ProfileIdentity, + profile_id: ProfileIdentity, /// contact id - contact: ContactIdentity, + contact_id: ContactIdentity, /// connection status to contact connection_state: ConnectionState, }, /// Notice from the network check plugin, a profile self check test by attempting to connecting to itself NetworkStatus { /// profile the check was performed on - profile: ProfileIdentity, + profile_id: ProfileIdentity, // it's technically a profile self check /// error if there was one (can be empty) error: String, @@ -368,9 +393,9 @@ pub enum Event { /// Information from the ACN about a peer ACNInfo { /// identity field - profile: ProfileIdentity, + profile_id: ProfileIdentity, /// contact id - contact: ContactIdentity, + contact_id: ContactIdentity, /// key of info key: String, /// data of info @@ -379,7 +404,7 @@ pub enum Event { /// a profile attribute has been updated with a new value UpdatedProfileAttribute { /// identity field - profile: ProfileIdentity, + profile_id: ProfileIdentity, /// attribute key key: String, /// attribute new value @@ -388,7 +413,7 @@ pub enum Event { /// emited to confirm ack of a message succeeded IndexedAcknowledgement { /// identity field - profile: ProfileIdentity, + profile_id: ProfileIdentity, /// conversation id conversation_id: i32, /// index of message acked @@ -397,41 +422,41 @@ pub enum Event { /// emited to signal failure to ack a message IndexedFailure { /// identity field - profile: ProfileIdentity, + profile_id: ProfileIdentity, /// conversation id conversation_id: ConversationID, /// index of failure of message to ack index: i32, /// contact id - contact: ContactIdentity, + contact_id: ContactIdentity, /// error string error: String, }, /// a peer has acked a message PeerAcknowledgement { /// identity field - profile: ProfileIdentity, + profile_id: ProfileIdentity, /// message id this is an ack to event_id: String, /// contact id - contact: ContactIdentity, + contact_id: ContactIdentity, /// conversation id conversation_id: i32, }, /// New message received on a group NewMessageFromGroup { /// identity field - profile: ProfileIdentity, + profile_id: ProfileIdentity, /// conversation id conversation_id: ConversationID, /// time of message timestamp_sent: DateTime, /// contact id - contact: ContactIdentity, + contact_id: ContactIdentity, /// message index index: i32, /// the message - message: Result, + message: Message, /// hash of the message content_hash: String, /// path to picture for sender @@ -444,7 +469,7 @@ pub enum Event { /// notice a group has been created GroupCreated { /// identity field - profile: ProfileIdentity, + profile_id: ProfileIdentity, /// conversation id conversation_id: ConversationID, /// group id @@ -461,7 +486,7 @@ pub enum Event { /// notice a new group exists NewGroup { /// identity field - profile: ProfileIdentity, + profile_id: ProfileIdentity, /// conversation id conversation_id: ConversationID, /// group id @@ -480,7 +505,7 @@ pub enum Event { /// a server connection state has changed ServerStateChange { /// identity field - profile: ProfileIdentity, + profile_id: ProfileIdentity, /// server the group is on group_server: String, /// state of connection to server @@ -489,9 +514,9 @@ pub enum Event { /// A getval call to a peer has returned a response NewRetValMessageFromPeer { /// identity field - profile: ProfileIdentity, + profile_id: ProfileIdentity, /// conversation id - contact: ContactIdentity, + contact_id: ContactIdentity, /// scope of the val scope: String, /// path of the val (zone.key) @@ -506,7 +531,7 @@ pub enum Event { /// result of a call to share a file, the filekey and manifest to operate on it ShareManifest { /// identity field - profile: ProfileIdentity, + profile_id: ProfileIdentity, /// filekey filekey: FileKey, /// serialized manifest of the share @@ -515,29 +540,29 @@ pub enum Event { /// Information on a peer fileshare has been received ManifestSizeReceived { /// identity field - profile: ProfileIdentity, + profile_id: ProfileIdentity, /// filekey filekey: FileKey, /// size of manifest received for a share manifest_size: i32, /// contact id - contact: ContactIdentity, + contact_id: ContactIdentity, }, /// An error has occured while trying to parse a peer sharefile ManifestError { /// identity field - profile: ProfileIdentity, + profile_id: ProfileIdentity, /// contact id - contact: ContactIdentity, + contact_id: ContactIdentity, /// filekey filekey: FileKey, }, /// A peer message about a shared file has been received ManifestReceived { /// identity field - profile: ProfileIdentity, + profile_id: ProfileIdentity, /// contact id - contact: ContactIdentity, + contact_id: ContactIdentity, /// filekey filekey: FileKey, /// serialized manifest @@ -546,9 +571,9 @@ pub enum Event { /// a received manfiest has been saved ManifestSaved { /// identity field - profile: ProfileIdentity, + profile_id: ProfileIdentity, /// contact id - contact: ContactIdentity, + contact_id: ContactIdentity, /// filekey filekey: FileKey, /// serialized manifest @@ -561,7 +586,7 @@ pub enum Event { /// periodically emited status updates about an active download of a shared file FileDownloadProgressUpdate { /// identity field - profile: ProfileIdentity, + profile_id: ProfileIdentity, /// filekey filekey: FileKey, /// progress of download of share in chunks @@ -585,10 +610,11 @@ pub enum Event { impl From<&CwtchEvent> for Event { fn from(cwtch_event: &CwtchEvent) -> Self { + println!("EVENT: {:?}", cwtch_event); match cwtch_event.event_type.as_str() { "CwtchStarted" => Event::CwtchStarted, "NewPeer" => Event::NewPeer { - profile: cwtch_event.data["Identity"].clone().into(), + profile_id: cwtch_event.data["Identity"].clone().into(), tag: cwtch_event.data["tag"].clone(), created: cwtch_event.data["Created"] == "true", name: cwtch_event.data["name"].clone(), @@ -596,7 +622,7 @@ impl From<&CwtchEvent> for Event { picture: cwtch_event.data["picture"].clone(), online: cwtch_event.data["Online"].clone(), profile_data: Profile::new( - &cwtch_event.data["Identity"], + cwtch_event.data["Identity"].clone().into(), &cwtch_event.data["name"], &cwtch_event.data["picture"], &cwtch_event.data["ContactsJson"], @@ -605,23 +631,23 @@ impl From<&CwtchEvent> for Event { }, "NewMessageFromPeer" => Event::NewMessageFromPeer { - profile: cwtch_event.data["ProfileOnion"].clone().into(), + profile_id: cwtch_event.data["ProfileOnion"].clone().into(), conversation_id: cwtch_event.data["ConversationID"].parse().unwrap_or(-2).into(), - contact: cwtch_event.data["RemotePeer"].clone().into(), + contact_id: cwtch_event.data["RemotePeer"].clone().into(), nick: cwtch_event.data["Nick"].clone(), timestamp_received: DateTime::parse_from_rfc3339(cwtch_event.data["TimestampReceived"].as_str()).unwrap_or( DateTime::from(Utc::now())), - message: serde_json::from_str(&cwtch_event.data["Data"]), + message: Message::from_json(&cwtch_event.data["Data"]), notification: MessageNotification::from(cwtch_event.data["notification"].clone()), - picture: cwtch_event.data["Picture"].clone(), + picture: cwtch_event.data["picture"].clone(), }, "PeerError" => Event::PeerError { error: cwtch_event.data["Error"].clone() }, "AppError" => Event::AppError { error: cwtch_event.data["Error"].clone(), }, "ContactCreated" => Event::ContactCreated { - profile: cwtch_event.data["ProfileOnion"].clone().into(), + profile_id: cwtch_event.data["ProfileOnion"].clone().into(), conversation_id: cwtch_event.data["ConversationID"].clone().parse().unwrap_or(-2).into(), - contact: cwtch_event.data["RemotePeer"].clone().into(), + contact_id: cwtch_event.data["RemotePeer"].clone().into(), unread: cwtch_event.data["unread"].clone().parse().unwrap_or(0), picture: cwtch_event.data["picture"].clone(), default_picture: cwtch_event.data["defaultPicture"].clone(), @@ -635,14 +661,14 @@ impl From<&CwtchEvent> for Event { last_msg_time: DateTime::parse_from_rfc3339(cwtch_event.data["lastMsgTime"].as_str()).unwrap_or(DateTime::from(Utc::now())), }, "PeerStateChange" => Event::PeerStateChange { - profile: cwtch_event.data["ProfileOnion"].clone().into(), - contact: cwtch_event.data["RemotePeer"].clone().into(), + profile_id: cwtch_event.data["ProfileOnion"].clone().into(), + contact_id: cwtch_event.data["RemotePeer"].clone().into(), connection_state: ConnectionState::from(cwtch_event.data["ConnectionState"].as_str()), }, "UpdateGlobalSettings" => Event::UpdateGlobalSettings { - settings: serde_json::from_str(cwtch_event.data["Data"].as_str()).unwrap_or(HashMap::new()), + settings: serde_json::from_str(cwtch_event.data["Data"].as_str()).expect("could not parse settings from libCwtch-go"), }, - "PeerDeleted" => Event::PeerDeleted { profile: cwtch_event.data["Identity"].clone().into() }, + "PeerDeleted" => Event::PeerDeleted { profile_id: cwtch_event.data["Identity"].clone().into() }, "ACNStatus" => Event::ACNStatus { progress: cwtch_event.data["Progress"].parse().unwrap_or(0), @@ -650,30 +676,28 @@ impl From<&CwtchEvent> for Event { }, "ACNVersion" => Event::ACNVersion { version: cwtch_event.data["Data"].clone()}, "NetworkError" => Event::NetworkStatus { - profile: cwtch_event.data["Onion"].clone().into(), + profile_id: cwtch_event.data["ProfileOnion"].clone().into(), error: cwtch_event.data["Error"].clone(), status: NetworkCheckStatus::from(cwtch_event.data["Status"].clone()) }, "ACNInfo" => Event::ACNInfo { - profile: cwtch_event.data["ProfileOnion"].clone().into(), - contact: cwtch_event.data["Handle"].clone().into(), + profile_id: cwtch_event.data["ProfileOnion"].clone().into(), + contact_id: cwtch_event.data["Handle"].clone().into(), key: cwtch_event.data["Key"].clone(), data: cwtch_event.data["Data"].clone(), }, "UpdatedProfileAttribute" => Event::UpdatedProfileAttribute { - profile: cwtch_event.data["ProfileOnion"].clone().into(), + profile_id: cwtch_event.data["ProfileOnion"].clone().into(), key: cwtch_event.data["Key"].clone(), value: cwtch_event.data["Data"].clone(), }, - "IndexedAcknowledgement" => Event::IndexedFailure { - profile: cwtch_event.data["ProfileOnion"].clone().into(), + "IndexedAcknowledgement" => Event::IndexedAcknowledgement { + profile_id: cwtch_event.data["ProfileOnion"].clone().into(), conversation_id: cwtch_event.data["ConversationID"].parse().unwrap_or(-1).into(), index: cwtch_event.data["Index"].parse().unwrap_or(-1), - contact: cwtch_event.data["Handle"].clone().into(), - error: cwtch_event.data["Error"].clone(), }, "ShareManifest" => Event::ShareManifest { - profile: cwtch_event.data["ProfileOnion"].clone().into(), + profile_id: cwtch_event.data["ProfileOnion"].clone().into(), filekey: cwtch_event.data["FileKey"].clone().into(), serialized_manifest: cwtch_event.data["SerializedManifest"].clone(), }, @@ -703,57 +727,57 @@ impl From<&CwtchEvent> for Event { connections: cwtch_event.data["Connections"].parse().unwrap_or(0), }, "ManifestSizeReceived" => Event::ManifestSizeReceived { - profile: cwtch_event.data["ProfileOnion"].clone().into(), + profile_id: cwtch_event.data["ProfileOnion"].clone().into(), filekey: cwtch_event.data["FileKey"].clone().into(), manifest_size: cwtch_event.data["ManifestSize"].parse().unwrap_or(0), - contact: cwtch_event.data["Handle"].clone().into(), + contact_id: cwtch_event.data["Handle"].clone().into(), }, "ManifestError" => Event::ManifestError { - profile: cwtch_event.data["ProfileOnion"].clone().into(), - contact: cwtch_event.data["Handle"].clone().into(), + profile_id: cwtch_event.data["ProfileOnion"].clone().into(), + contact_id: cwtch_event.data["Handle"].clone().into(), filekey: cwtch_event.data["FileKey"].clone().into(), }, "ManifestReceived" => Event::ManifestReceived { - profile: cwtch_event.data["ProfileOnion"].clone().into(), - contact: cwtch_event.data["Handle"].clone().into(), + profile_id: cwtch_event.data["ProfileOnion"].clone().into(), + contact_id: cwtch_event.data["Handle"].clone().into(), filekey: cwtch_event.data["FileKey"].clone().into(), serialized_manifest: cwtch_event.data["SerializedManifest"].clone(), }, "ManifestSaved" => Event::ManifestSaved { - profile: cwtch_event.data["ProfileOnion"].clone().into(), - contact: cwtch_event.data["Handle"].clone().into(), + profile_id: cwtch_event.data["ProfileOnion"].clone().into(), + contact_id: cwtch_event.data["Handle"].clone().into(), filekey: cwtch_event.data["FileKey"].clone().into(), serialized_manifest: cwtch_event.data["SerializedManifest"].clone(), temp_file: cwtch_event.data["TempFile"].clone(), name_suggestion: cwtch_event.data["NameSuggestion"].clone(), }, "FileDownloadProgressUpdate" => Event::FileDownloadProgressUpdate { - profile: cwtch_event.data["ProfileOnion"].clone().into(), + profile_id: cwtch_event.data["ProfileOnion"].clone().into(), filekey: cwtch_event.data["FileKey"].clone().into(), progress: cwtch_event.data["Progress"].parse().unwrap_or(0), filesize_in_chunks: cwtch_event.data["FileSizeInChunks"].parse().unwrap_or(0), name_suggestion: cwtch_event.data["NameSuggestion"].clone(), }, "PeerAcknowledgement" => Event::PeerAcknowledgement { - profile: cwtch_event.data["ProfileOnion"].clone().into(), - event_id: cwtch_event.data["EventId"].clone(), - contact: cwtch_event.data["RemotePeer"].clone().into(), - conversation_id: cwtch_event.data["ConversationID"].parse().unwrap_or(0) + profile_id: cwtch_event.data["ProfileOnion"].clone().into(), + event_id: cwtch_event.data["EventID"].clone(), + contact_id: cwtch_event.data["RemotePeer"].clone().into(), + conversation_id: cwtch_event.data.get("ConversationID").unwrap_or(&"0".to_string()).parse().unwrap_or(0) }, "NewMessageFromGroup" => Event::NewMessageFromGroup { - profile: cwtch_event.data["ProfileOnion"].clone().into(), + profile_id: cwtch_event.data["ProfileOnion"].clone().into(), timestamp_sent: DateTime::parse_from_rfc3339(cwtch_event.data["TimestampSent"].as_str()).unwrap_or( DateTime::from(Utc::now())), index: cwtch_event.data["RemotePeer"].parse().unwrap_or(-1), content_hash: cwtch_event.data["ContentHash"].clone(), conversation_id: cwtch_event.data["ConversationID"].parse().unwrap_or(-2).into(), - contact: cwtch_event.data["RemotePeer"].clone().into(), + contact_id: cwtch_event.data["RemotePeer"].clone().into(), nick: cwtch_event.data["Nick"].clone(), - message: serde_json::from_str(&cwtch_event.data["Data"]), + message: Message::from_json(&cwtch_event.data["Data"]), notification: MessageNotification::from(cwtch_event.data["notification"].clone()), - picture: cwtch_event.data["Picture"].clone(), + picture: cwtch_event.data["picture"].clone(), }, "GroupCreated" => Event::GroupCreated { - profile: cwtch_event.data["ProfileOnion"].clone().into(), + profile_id: cwtch_event.data["ProfileOnion"].clone().into(), conversation_id: cwtch_event.data["ConversationID"].parse().unwrap_or(-2).into(), group_id: cwtch_event.data["GroupID"].clone().into(), group_server: cwtch_event.data["GroupServer"].clone(), @@ -762,7 +786,7 @@ impl From<&CwtchEvent> for Event { access_control_list: serde_json::from_str(cwtch_event.data["accessControlList"].as_str()).unwrap_or(ACL::new()), }, "NewGroup" => Event::NewGroup { - profile: cwtch_event.data["ProfileOnion"].clone().into(), + profile_id: cwtch_event.data["ProfileOnion"].clone().into(), conversation_id: cwtch_event.data["ConversationID"].parse().unwrap_or(-2).into(), group_id: cwtch_event.data["GroupID"].clone().into(), group_server: cwtch_event.data["GroupServer"].clone(), @@ -772,13 +796,13 @@ impl From<&CwtchEvent> for Event { access_control_list: serde_json::from_str(cwtch_event.data["accessControlList"].as_str()).unwrap_or(ACL::new()), }, "ServerStateChange" => Event::ServerStateChange { - profile: cwtch_event.data["ProfileOnion"].clone().into(), + profile_id: cwtch_event.data["ProfileOnion"].clone().into(), group_server: cwtch_event.data["GroupServer"].clone(), state: ConnectionState::from(cwtch_event.data["ConnectionState"].as_str()), }, "NewRetValMessageFromPeer" => Event::NewRetValMessageFromPeer { - profile: cwtch_event.data["ProfileOnion"].clone().into(), - contact: cwtch_event.data["RemotePeer"].clone().into(), + profile_id: cwtch_event.data["ProfileOnion"].clone().into(), + contact_id: cwtch_event.data["RemotePeer"].clone().into(), scope: cwtch_event.data["Scope"].clone(), path: cwtch_event.data["Path"].clone(), exists: cwtch_event.data["Exists"].parse().unwrap_or(false), diff --git a/src/lib.rs b/src/lib.rs index 52dd6f7..ee92cc5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -28,7 +28,7 @@ pub trait CwtchLib { fn send_app_event(&self, event_json: &str); /// Send json of a structs::CwtchEvent to a cwtch profile - fn send_profile_event(&self, profile: ProfileIdentity, event_json: &str); + fn send_profile_event(&self, profile: &ProfileIdentity, event_json: &str); /// Pull json of a structs::CwtchEvent off the appbus for responding to fn get_appbus_event(&self) -> Event; @@ -40,44 +40,44 @@ pub trait CwtchLib { fn load_profiles(&self, pass: &str); /// Cause profile to accept conversation - fn accept_conversation(&self, profile: ProfileIdentity, conversation_id: ConversationID); + fn accept_conversation(&self, profile: &ProfileIdentity, conversation_id: ConversationID); /// Cause profile to block conversation - fn block_contact(&self, profile: ProfileIdentity, conversation_id: ConversationID); + fn block_contact(&self, profile: &ProfileIdentity, conversation_id: ConversationID); /// Cause profile to unblock contact - fn unblock_contact(&self, profile: ProfileIdentity, conversation_id: ConversationID); + fn unblock_contact(&self, profile: &ProfileIdentity, conversation_id: ConversationID); /// Get a specific message for conversation of profile by index - fn get_message(&self, profile: ProfileIdentity, conversation_id: ConversationID, message_index: i32) -> String; + fn get_message(&self, profile: &ProfileIdentity, conversation_id: ConversationID, message_index: i32) -> String; /// Get a specific message for a conversation by its id - fn get_message_by_id(&self, profile: ProfileIdentity, conversation_id: ConversationID, message_id: i32) -> String; + fn get_message_by_id(&self, profile: &ProfileIdentity, conversation_id: ConversationID, message_id: i32) -> String; /// Get a specific message for conversation of profile by hash fn get_message_by_content_hash( &self, - profile: ProfileIdentity, + profile: &ProfileIdentity, conversation_id: ConversationID, hash: &str, ) -> String; /// Bulk get messages starting at message index and of count amoung - fn get_messages(&self, profile: ProfileIdentity, conversation_id: ConversationID, message_index: i32, count: i32) -> String; + fn get_messages(&self, profile: &ProfileIdentity, conversation_id: ConversationID, message_index: i32, count: i32) -> String; /// Send json of a structs::Message from profile to contact. Returns computed sent message (including index and hash values) - fn send_message(&self, profile: ProfileIdentity, conversation_id: ConversationID, msg: &str) -> String; + fn send_message(&self, profile: &ProfileIdentity, conversation_id: ConversationID, msg: &str) -> String; /// Send profile's contact an invite for/to target. Returns computed sent message (including index and hash values) - fn send_invitation(&self, profile: ProfileIdentity, conversation_id: ConversationID, target_id: i32) -> String; + fn send_invitation(&self, profile: &ProfileIdentity, conversation_id: ConversationID, target_id: i32) -> String; /// share a file file_path with a conersation. Returns computed sent message (including index and hash values) - fn share_file(&self, profile: ProfileIdentity, conversation_id: ConversationID, file_path: &str) -> String; + fn share_file(&self, profile: &ProfileIdentity, conversation_id: ConversationID, file_path: &str) -> String; /// download a file from a conversation to the file_path fn download_file( &self, - profile: ProfileIdentity, + profile: &ProfileIdentity, conversation_id: ConversationID, file_path: &str, manifest_path: &str, @@ -85,42 +85,42 @@ pub trait CwtchLib { ); /// Query the status of a download - fn check_download_status(&self, profile: ProfileIdentity, file_key: FileKey); + fn check_download_status(&self, profile: &ProfileIdentity, file_key: FileKey); /// Verufy a download is done, and if not, resume it - fn verify_or_resume_download(&self, profile: ProfileIdentity, conversation_id: ConversationID, file_key: FileKey); + fn verify_or_resume_download(&self, profile: &ProfileIdentity, conversation_id: ConversationID, file_key: FileKey); /// Ask the ACN inside the Cwtch app to restart the tor connection fn reset_tor(&self); /// Cause profile to create a group on server with name - fn create_group(&self, profile: ProfileIdentity, server: &str, name: &str); + fn create_group(&self, profile: &ProfileIdentity, server: &str, name: &str); /// Delete profile with encryption/password check of pass - fn delete_profile(&self, profile: ProfileIdentity, pass: &str); + fn delete_profile(&self, profile: &ProfileIdentity, pass: &str); /// Cause profile to archive conversation with contact - fn archive_conversation(&self, profile: ProfileIdentity, conversation_id: ConversationID); + fn archive_conversation(&self, profile: &ProfileIdentity, conversation_id: ConversationID); /// Cause profile to delete contact/group identified by handle - fn delete_contact(&self, profile: ProfileIdentity, conversation_id: ConversationID); + fn delete_contact(&self, profile: &ProfileIdentity, conversation_id: ConversationID); /// Cuase profile to attempt to import a contact/group/keybundle identified by bundle - fn import_bundle(&self, profile: ProfileIdentity, bundle: &str); + fn import_bundle(&self, profile: &ProfileIdentity, bundle: &str); /// Set a profile attribute key to val - fn set_profile_attribute(&self, profile: ProfileIdentity, key: &str, val: &str); + fn set_profile_attribute(&self, profile: &ProfileIdentity, key: &str, val: &str); /// Get a profile attribute - fn get_profile_attribute(&self, profile: ProfileIdentity, key: &str) -> Result, CwtchError>; + fn get_profile_attribute(&self, profile: &ProfileIdentity, key: &str) -> Result, CwtchError>; /// Set a profile's contact's attribute of key to val - fn set_conversation_attribute(&self, profile: ProfileIdentity, conversation_id: ConversationID, key: &str, val: &str); + fn set_conversation_attribute(&self, profile: &ProfileIdentity, conversation_id: ConversationID, key: &str, val: &str); /// Set an attribute on a message in a conversation fn set_message_attribute( &self, - profile: ProfileIdentity, + profile: &ProfileIdentity, conversation_id: ConversationID, channel_id: i32, message_id: i32, @@ -129,13 +129,13 @@ pub trait CwtchLib { ); /// Get an attribute for a conversation - fn get_conversation_attribute(&self, profile: ProfileIdentity, conversation_id: ConversationID, key: &str) -> Result, CwtchError>; + fn get_conversation_attribute(&self, profile: &ProfileIdentity, conversation_id: ConversationID, key: &str) -> Result, CwtchError>; /// Change a profile's password to new_pass if old_pass is correct - fn change_password(&self, profile: ProfileIdentity, old_pass: &str, new_pass: &str, new_pass_again: &str); + fn change_password(&self, profile: &ProfileIdentity, old_pass: &str, new_pass: &str, new_pass_again: &str); /// Export a profile to filename - fn export_profile(&self, profile: ProfileIdentity, filename: &str); + fn export_profile(&self, profile: &ProfileIdentity, filename: &str); /// Import a profile from a file with supplied password. Json of a profile struct returned on success fn import_profile(&self, filename: &str, password: &str) -> String; diff --git a/src/structs.rs b/src/structs.rs index e3c9b93..706c07f 100644 --- a/src/structs.rs +++ b/src/structs.rs @@ -1,10 +1,12 @@ use crate::structs::ConnectionState::Disconnected; -use crate::CwtchLib; +use crate::{ConversationID, CwtchLib, ProfileIdentity}; use serde::{Deserialize, Serialize}; use serde_with::{serde_as, DefaultOnError}; +use serde_repr::*; use std::collections::HashMap; +use crate::event::ContactIdentity; -#[derive(Serialize, Deserialize, Debug)] +#[derive(Debug, Clone, Serialize, Deserialize, Hash, Eq, PartialEq)] /// Defines the states a Cwtch connection can be in pub enum ConnectionState { /// The Cwtch connection is not conected at all @@ -81,7 +83,7 @@ pub struct CwtchEvent { pub data: HashMap, } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] #[serde(rename_all = "PascalCase")] #[allow(non_snake_case)] /// AccessControl is a type determining client assigned authorization to a peer @@ -98,15 +100,15 @@ pub struct AccessControl { pub type ACL = HashMap; #[serde_as] -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] #[serde(rename_all = "camelCase")] /// Struct to serialize/deserialize conversations coming from libcwtch-go pub struct Conversation { /// onion address / id of the conversation #[serde(alias = "onion")] - pub handle: String, + pub contact_id: ContactIdentity, /// unique identifier of the contact/conversation to be used in API access - pub identifier: i32, + pub identifier: ConversationID, // TODO TEST does this work here for serde deserializtion /// display name of the conversation, as determined in libcwtch-go from name specified by contact pub name: String, #[serde_as(deserialize_as = "DefaultOnError")] @@ -125,7 +127,7 @@ pub struct Conversation { //attr: HashMap, } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] /// Struct to serialize/deserialize servers coming from libcwtch-go pub struct Server { /// onion address of the server @@ -134,13 +136,11 @@ pub struct Server { pub status: ConnectionState, } -#[serde_as] -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Clone)] /// Struct to serialize/deserialize profiles coming from libcwtch-go pub struct Profile { /// onion address / ID of the profile - #[serde(alias = "onion")] - pub handle: String, + pub profile_id: ProfileIdentity, /// nick name of the onion as supplied by libcwtch-go based on "name" attribute pub nick: String, /// path to a profile image, controled by "picture" attribute @@ -148,22 +148,66 @@ pub struct Profile { /// all profile attributes pub attr: HashMap, /// map of conversation [ onion => conversation ] - pub conversations: HashMap, + pub conversations: HashMap, /// map of servers [ onion => server ] pub servers: HashMap, } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Serialize_repr, Deserialize_repr, PartialEq, Clone)] +#[repr(i32)] +/// Enum matching message types and their overlays as defined in Cwtch +/// - https://git.openprivacy.ca/cwtch.im/cwtch/src/commit/907a7ca638fbcbaac5d652608d455d4217597fa9/model/overlay.go +/// - https://git.openprivacy.ca/cwtch.im/cwtch-ui/src/commit/3a752b73972cbfc53b6da26116fe018ee2ac2343/lib/models/message.dart +pub enum MessageType { + /// A standard Cwtch message of text + TextMessage = 1, + /// This message is a text message but it also contains a reference to the message it is quoting + QuotedMessage = 10, + /// A shared contact message + SuggestContact = 100, + /// A group invite message + InviteGroup = 101, + /// a share file message + FileShare = 200, + /// This message is of an unsupported type so malformed + MalformedMessage, +} + +impl From for MessageType { + fn from(x: i32) -> Self { + match x { + 1 => MessageType::TextMessage, + 10 => MessageType::QuotedMessage, + 100 => MessageType::SuggestContact, + 101 => MessageType::InviteGroup, + 200 => MessageType::FileShare, + _ => MessageType::MalformedMessage, + } + } +} + + +#[derive(Debug, Serialize, Deserialize, Clone)] /// Struct to serialize/deserialize messages sent over Cwtch between profiles / conversation pub struct Message { /// overlay id that the message is targeting as defined in cwtch/model/overlay.go /// [ OverlayChat = 1, OverlayInviteContact = 100, OverlayInviteGroup = 101, OverlayFileSharing = 200 ] - pub o: i64, + pub o: MessageType, /// data of the message pub d: String, } -#[derive(Debug, Serialize, Deserialize)] +impl Message { + /// parse json into a Message + pub fn from_json(json: &str) -> Self { + match serde_json::from_str(json) { + Ok(m) => m, + Err(e) => Message{o: MessageType::MalformedMessage, d: e.to_string()} + } + } +} + +#[derive(Debug, Serialize, Deserialize, Clone)] /// Settings as defined in libcwtch-go/utils/settings.go and should be populated by handeling the UpdateGlobalSettings event emited during cwtch.start() #[allow(non_snake_case)] pub struct Settings { @@ -265,7 +309,7 @@ impl Profile { /// contacts_json as supplied by libcwtch-go, a map of conversations /// server_list as supplied by libcwtch-go, a map of servers pub fn new( - identity: &str, + identity: ProfileIdentity, name: &str, picture: &str, conversations_json: &str, @@ -280,7 +324,7 @@ impl Profile { Err(e) => return Err(e), }; Ok(Profile { - handle: identity.to_string(), + profile_id: identity, nick: name.to_string(), image_path: picture.to_string(), attr: Default::default(), @@ -289,8 +333,8 @@ impl Profile { }) } - fn process_conversations(conversations_json: &str) -> Result, String> { - let mut conversations: HashMap = HashMap::new(); + fn process_conversations(conversations_json: &str) -> Result, String> { + let mut conversations: HashMap = HashMap::new(); if conversations_json == "null" { return Ok(conversations); } @@ -320,8 +364,8 @@ impl Profile { } /// Find a conversation_id associated with a remote handle (used for some events with no conversation id like PeerStateChange) - pub fn find_conversation_id_by_handle(&self, handle: String) -> Option { - match self.conversations.values().filter(|c| c.handle == handle).next() { + pub fn find_conversation_id_by_handle(&self, contact_id: ContactIdentity) -> Option { + match self.conversations.values().filter(|c| c.contact_id == contact_id).next() { Some(conversation) => Some(conversation.identifier), None => None }