add events wrapping enum #18

Merged
sarah merged 7 commits from events into main 2022-07-21 15:56:25 +00:00
7 changed files with 1161 additions and 118 deletions

90
Cargo.lock generated
View File

@ -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,14 +144,35 @@ checksum = "1b03d17f364a3a042d5e5d46b053bbbf82c92c9430c592dd4c064dc6ee997125"
name = "libcwtch"
version = "0.3.2"
dependencies = [
"chrono",
"hex-literal",
"libc",
"serde",
"serde_json",
"serde_repr",
"serde_with",
"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"
@ -194,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"
@ -245,6 +296,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 +324,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"

View File

@ -18,3 +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"

View File

@ -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::<i32>().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!"),
};

View File

@ -8,7 +8,8 @@ use std::ffi::CString;
use super::CwtchLib;
use crate::cwtchlib_go::bindings;
use crate::{CwtchError, structs::*};
use crate::{ConversationID, CwtchError, FileKey, ProfileIdentity, ServerIdentity, structs::*};
use crate::event::Event;
struct c_str_wrap {
raw: *mut i8,
@ -86,42 +87,113 @@ 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);
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);
c_bind!(get_appbus_event(;;) c_GetAppBusEvent -> String);
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);
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.as_str(), conversation_id.into())
}
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) {
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(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(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(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(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(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(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(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) {
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) {
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) {
self._verify_or_resume_download(String::from(profile).as_str(), conversation_id.into(), String::from(file_key).as_str())
}
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<Option<String>, CwtchError> {
let resp = self._get_profile_attribute(profile, key);
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) {
self._delete_profile(String::from(profile).as_str(), pass)
}
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(profile.as_str(), conversation_id.into())
}
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) {
self._set_profile_attribute(String::from(profile).as_str(), key, val)
}
fn get_profile_attribute(&self, profile: &ProfileIdentity, key: &str) -> Result<Option<String>, 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,
Err(e) => return Err(e.to_string()),
@ -131,9 +203,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<Option<String>, 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(String::from(profile).as_str(), conversation_id.into(), key, val)
}
fn get_conversation_attribute(&self, profile: &ProfileIdentity, conversation_id: ConversationID, key: &str) -> Result<Option<String>, 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,
Err(e) => return Err(e.to_string()),
@ -143,11 +217,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(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(String::from(profile).as_str(), old_pass, new_pass, new_pass_again)
}
fn export_profile(&self, profile: &ProfileIdentity, filename: &str) {
self._export_profile(String::from(profile).as_str(), filename)
}
c_bind!(import_profile(filename: &str, password: &str;;) c_ImportProfile -> String);
fn shutdown_cwtch(&self) {
unsafe {
bindings::c_ShutdownCwtch();
@ -156,12 +238,26 @@ 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(String::from(server).as_str(), 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(String::from(server).as_str())
}
fn stop_server(&self, server: ServerIdentity) {
self._stop_server(String::from(server).as_str())
}
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(String::from(server).as_str(), key, val)
}
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)
}
}

822
src/event.rs Normal file
View File

@ -0,0 +1,822 @@
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, Settings};
#[derive(Debug, Clone, Serialize, Deserialize, Hash, Eq, PartialEq)]
/// Profile ID used to refer to profiles in Cwtch
pub struct ProfileIdentity(String);
impl From<String> for ProfileIdentity {
fn from(x: String) -> Self {
ProfileIdentity(x)
}
}
impl From<ProfileIdentity> 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<&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);
impl From<String> for ContactIdentity {
fn from(x: String) -> Self {
ContactIdentity(x)
}
}
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(pub i32) ;
impl From<i32> for ConversationID {
fn from(x: i32) -> Self {
ConversationID(x)
}
}
impl From<ConversationID> for i32 {
fn from(x: ConversationID) -> Self {
x.0
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Hash, Eq, PartialEq)]
/// Group ID used to refer to a Group in Cwtch
pub struct GroupID(String) ;
impl From<String> for GroupID {
fn from(x: String) -> Self {
GroupID(x)
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Hash, Eq, PartialEq)]
/// Server ID user to refer to a server in Cwtch
pub struct ServerIdentity(String);
impl From<String> for ServerIdentity {
fn from(x: String) -> Self {
ServerIdentity(x)
}
}
impl From<&str> for ServerIdentity {
fn from(x: &str) -> Self {
ServerIdentity(x.to_string())
}
}
impl From<ServerIdentity> for String {
fn from(x: ServerIdentity) -> Self {
x.into()
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Hash, Eq, PartialEq)]
/// FileKey ID user to refer to a file share in Cwtch
pub struct FileKey(String);
impl From<String> for FileKey {
fn from(x: String) -> Self {
FileKey(x)
}
}
impl From<FileKey> for String {
fn from(x: FileKey) -> Self {
x.into()
}
}
#[derive(Debug, Clone)]
/// Enum for type of notification a UI/client should emit for a new message
pub enum MessageNotification {
/// means no notification
None,
/// means emit a notification that a message event happened only
SimpleEvent,
/// means emit a notification event with Conversation handle included
ContactInfo,
}
impl From<String> for MessageNotification {
fn from(str: String) -> MessageNotification {
match str.as_str() {
"None" => MessageNotification::None,
"SimpleEvent" => MessageNotification::SimpleEvent,
"ContactInfo" => MessageNotification::ContactInfo,
_ => MessageNotification::None,
}
}
}
#[derive(Debug, Clone)]
/// 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
}
impl From<String> for NetworkCheckStatus {
fn from(str: String) -> NetworkCheckStatus {
match str.as_str() {
"Error" => NetworkCheckStatus::Error,
"Success" => NetworkCheckStatus::Success,
_ => NetworkCheckStatus::Error,
}
}
}
#[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
DefaultPassword,
/// indicates a user supplied password is being used on this server for storage encryption
Outdated
Review

deprecate?

deprecate?
Outdated
Review

still being populated by lcg

still being populated by lcg
Password,
/// indicates no password and no encryption is being used on server storage
dan marked this conversation as resolved Outdated
Outdated
Review

we could let serde parse into this structure

we could let serde parse into this structure
NoPassword
}
dan marked this conversation as resolved Outdated
Outdated
Review

same as above

same as above
impl From<String> 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, Clone)]
/// 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
}
impl From<String> for ServerIntent {
fn from(str: String) -> ServerIntent {
match str.as_str() {
"running" => ServerIntent::Running,
"stopped" => ServerIntent::Stopped,
_ => ServerIntent::Stopped,
}
}
}
#[derive(Debug, Clone)]
/// 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 field
profile_id: 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,
/// The deserialized profile with contacts and server info
profile_data: Result<Profile, String>,
},
/// 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: Settings,
},
/// 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 of deleted peer
profile_id: ProfileIdentity
},
/// 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 {
/// 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,
/// Notice from libCwtch that the storage migration is complete, profile being loaded resuming
DoneStorageMigration,
// app server events
dan marked this conversation as resolved Outdated
Outdated
Review

Parse into MessageOverlay?

Parse into MessageOverlay?
/// A new server has been loaded
NewServer {
/// 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 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 of server
server: ServerIdentity,
/// was deletion a success
success: bool,
/// optional error string in case of failure to delete
error: Option<String>,
},
/// Stats info for a server, periodically emited
ServerStatsUpdate {
/// 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,
},
// profile events
/// A new message was received
NewMessageFromPeer {
/// identity field
profile_id: ProfileIdentity,
/// conversation id
conversation_id: ConversationID,
/// contact id
contact_id: ContactIdentity,
/// name of contact
nick: String,
/// time message was received
timestamp_received: DateTime<FixedOffset>,
/// the message
message: Message,
/// 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 {
/// identity field
profile_id: ProfileIdentity,
/// conversation id
conversation_id: ConversationID,
/// contact id
contact_id: 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<FixedOffset>,
},
/// A peer has changed state
PeerStateChange {
/// identity field
profile_id: ProfileIdentity,
/// contact id
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_id: 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 {
/// identity field
Outdated
Review

Isn't this time UTC?

Isn't this time `UTC`?
Outdated
Review

DateTime::parse_from_rfc3339 returns a DateTime

DateTime::parse_from_rfc3339 returns a DateTime<FixedOffset>
profile_id: ProfileIdentity,
/// contact id
contact_id: ContactIdentity,
/// key of info
key: String,
/// data of info
dan marked this conversation as resolved Outdated
Outdated
Review

Message Overlay?

Message Overlay?
data: String,
},
/// a profile attribute has been updated with a new value
UpdatedProfileAttribute {
/// identity field
profile_id: ProfileIdentity,
/// attribute key
key: String,
/// attribute new value
value: String,
},
/// emited to confirm ack of a message succeeded
IndexedAcknowledgement {
/// identity field
profile_id: ProfileIdentity,
/// conversation id
conversation_id: i32,
/// index of message acked
index: i32,
},
/// emited to signal failure to ack a message
IndexedFailure {
/// identity field
profile_id: ProfileIdentity,
/// conversation id
conversation_id: ConversationID,
/// index of failure of message to ack
index: i32,
/// contact id
contact_id: ContactIdentity,
/// error string
error: String,
},
/// a peer has acked a message
PeerAcknowledgement {
/// identity field
profile_id: ProfileIdentity,
/// message id this is an ack to
event_id: String,
/// contact id
contact_id: ContactIdentity,
/// conversation id
conversation_id: i32,
},
/// New message received on a group
NewMessageFromGroup {
/// identity field
profile_id: ProfileIdentity,
/// conversation id
conversation_id: ConversationID,
/// time of message
timestamp_sent: DateTime<FixedOffset>,
/// contact id
contact_id: ContactIdentity,
/// message index
index: i32,
/// the message
message: Message,
/// 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 {
/// identity field
profile_id: 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 {
/// identity field
profile_id: 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_id: 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 {
/// identity field
profile_id: ProfileIdentity,
/// conversation id
contact_id: 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<String>,
},
/// result of a call to share a file, the filekey and manifest to operate on it
ShareManifest {
/// identity field
profile_id: ProfileIdentity,
/// filekey
filekey: FileKey,
/// serialized manifest of the share
serialized_manifest: String,
},
/// Information on a peer fileshare has been received
ManifestSizeReceived {
/// identity field
profile_id: ProfileIdentity,
/// filekey
filekey: FileKey,
/// size of manifest received for a share
manifest_size: i32,
/// contact id
contact_id: ContactIdentity,
},
/// An error has occured while trying to parse a peer sharefile
ManifestError {
/// identity field
profile_id: ProfileIdentity,
/// contact id
contact_id: ContactIdentity,
/// filekey
filekey: FileKey,
},
/// A peer message about a shared file has been received
ManifestReceived {
/// identity field
profile_id: ProfileIdentity,
/// contact id
contact_id: ContactIdentity,
/// filekey
filekey: FileKey,
/// serialized manifest
serialized_manifest: String,
},
/// a received manfiest has been saved
ManifestSaved {
/// identity field
profile_id: ProfileIdentity,
/// contact id
contact_id: 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 {
/// identity field
profile_id: 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, ??
/// 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<String, String>,
},
}
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_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(),
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"].clone().into(),
&cwtch_event.data["name"],
&cwtch_event.data["picture"],
&cwtch_event.data["ContactsJson"],
&cwtch_event.data["ServerList"],
)
},
"NewMessageFromPeer" => Event::NewMessageFromPeer {
profile_id: cwtch_event.data["ProfileOnion"].clone().into(),
conversation_id: cwtch_event.data["ConversationID"].parse().unwrap_or(-2).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: Message::from_json(&cwtch_event.data["Data"]),
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 {
profile_id: cwtch_event.data["ProfileOnion"].clone().into(),
conversation_id: cwtch_event.data["ConversationID"].clone().parse().unwrap_or(-2).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(),
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 {
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()).expect("could not parse settings from libCwtch-go"),
},
"PeerDeleted" => Event::PeerDeleted { profile_id: 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 { version: cwtch_event.data["Data"].clone()},
"NetworkError" => Event::NetworkStatus {
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_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_id: cwtch_event.data["ProfileOnion"].clone().into(),
key: cwtch_event.data["Key"].clone(),
value: cwtch_event.data["Data"].clone(),
},
"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),
},
"ShareManifest" => Event::ShareManifest {
profile_id: cwtch_event.data["ProfileOnion"].clone().into(),
filekey: cwtch_event.data["FileKey"].clone().into(),
serialized_manifest: cwtch_event.data["SerializedManifest"].clone(),
},
"NewServer" => Event::NewServer {
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()),
autostart: cwtch_event.data["Autostart"].parse().unwrap_or(false),
running: cwtch_event.data["Running"].parse().unwrap_or(false),
},
"ServerIntentUpdate" => Event::ServerIntentUpdate {
server: cwtch_event.data["Identity"].clone().into(),
intent: ServerIntent::from(cwtch_event.data["Intent"].clone())
},
"ServerDeleted" => Event::ServerDeleted {
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()),
None => None,
}
},
"ServerStatsUpdate" => Event::ServerStatsUpdate {
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 {
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_id: cwtch_event.data["Handle"].clone().into(),
},
"ManifestError" => Event::ManifestError {
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_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_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_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_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_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_id: cwtch_event.data["RemotePeer"].clone().into(),
nick: cwtch_event.data["Nick"].clone(),
message: Message::from_json(&cwtch_event.data["Data"]),
notification: MessageNotification::from(cwtch_event.data["notification"].clone()),
picture: cwtch_event.data["picture"].clone(),
},
"GroupCreated" => Event::GroupCreated {
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(),
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 {
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(),
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 {
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_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),
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(),
},
}
}
}

View File

@ -3,11 +3,15 @@
#![doc(html_root_url = "https://git.openprivacy.ca/cwtch.im/libcwtch-rs")]
#![deny(missing_docs)]
use crate::event::{ConversationID, Event, FileKey, ProfileIdentity, ServerIdentity};
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;
/// Additional structs for advnaced event handling and converstion helpers
pub mod event;
/// Error type for Cwtch lib related errors, intended for use with Result
pub type CwtchError = String;
@ -24,10 +28,10 @@ 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) -> String;
fn get_appbus_event(&self) -> Event;
/// Create a new profile encrypted with pass
fn create_profile(&self, nick: &str, pass: &str);
@ -36,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,
file_key: FileKey,
);
/// 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: FileKey);
/// 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: 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: &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<Option<String>, CwtchError>;
fn get_profile_attribute(&self, profile: &ProfileIdentity, key: &str) -> Result<Option<String>, 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,
@ -125,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<Option<String>, CwtchError>;
fn get_conversation_attribute(&self, profile: &ProfileIdentity, conversation_id: ConversationID, key: &str) -> Result<Option<String>, 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;
@ -148,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);
@ -166,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;

View File

@ -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
@ -29,9 +31,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,
@ -81,7 +83,7 @@ pub struct CwtchEvent {
pub data: HashMap<String, String>,
}
#[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<String, AccessControl>;
#[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<String, String>,
}
#[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<String, String>,
/// map of conversation [ onion => conversation ]
pub conversations: HashMap<i32, Conversation>,
pub conversations: HashMap<ConversationID, Conversation>,
/// map of servers [ onion => server ]
pub servers: HashMap<String, Server>,
}
#[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<i32> 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<HashMap<i32, Conversation>, String> {
let mut conversations: HashMap<i32, Conversation> = HashMap::new();
fn process_conversations(conversations_json: &str) -> Result<HashMap<ConversationID, Conversation>, String> {
let mut conversations: HashMap<ConversationID, Conversation> = 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<i32> {
match self.conversations.values().filter(|c| c.handle == handle).next() {
pub fn find_conversation_id_by_handle(&self, contact_id: ContactIdentity) -> Option<ConversationID> {
match self.conversations.values().filter(|c| c.contact_id == contact_id).next() {
Some(conversation) => Some(conversation.identifier),
None => None
}