adding documentations and examples and tweeks to be more rustful

This commit is contained in:
Dan Ballard 2021-09-08 00:32:56 -07:00
parent f00daf245b
commit 652526fffb
8 changed files with 137 additions and 37 deletions

View File

@ -3,8 +3,9 @@ name = "libcwtch"
version = "0.1.0"
authors = ["Dan Ballard <dan@mindstab.net>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
license = "MIT"
description = "libcwtch is an interface to a Cwtch app that allows creating of profiles to communicate with contacts over the Cwtch protocol"
repository = "https://git.openprivacy.ca/cwtch.im/libcwtch-rs"
[dependencies]
libc = "0.2"

View File

@ -1,6 +1,8 @@
# libCwtch-rs
Rust bindings for libCwtch
Rust bindings for [libCwtch](https://git.openprivacy.ca/cwtch.im/libcwtch-go/)
Example echobot in examples/echobot.rs (`cargo run --example echobot` -- assumes tor is on $PATH)
## Building
@ -15,7 +17,7 @@ the 'preamble from import "C"' section as it imports headers required for the C
but that we don't want to create rust bindings for (like importing stdlib.h). Then:
```
bindgen libCwtch.h -o src/gobindings/mod.rs
bindgen libCwtch.h -o src/cwtchlib_go/bindings.rs
```
### Todo

51
examples/echobot.rs Normal file
View File

@ -0,0 +1,51 @@
use std::{thread};
use libcwtch;
use libcwtch::CwtchLib;
use libcwtch::structs::{*};
fn main() {
let bot_home: String = "example_cwtch_dir".to_string();
std::fs::remove_dir_all(&bot_home);
std::fs::create_dir_all(&bot_home).unwrap();
let cwtch = libcwtch::new_cwtchlib_go();
println!("start_cwtch");
let ret = cwtch.start_cwtch(bot_home.as_str(), "");
println!("start_cwtch returned {}", ret);
let event_loop_handle = thread::spawn(move || {
loop {
let event_str = cwtch.get_appbus_event();
println!("event: {}", event_str);
let event: CwtchEvent = serde_json::from_str(&event_str).unwrap();
match event.event_type.as_str() {
"CwtchStarted" => {
println!("event CwtchStarted!");
println!("Creating bot");
cwtch.create_profile("Echobot", "be gay do crime");
},
"NewPeer" => {
println!("\n***** {} at {} *****\n", event.data["name"], event.data["Identity"]);
// process json for profile, contacts and servers...else {
let profile = Profile::new(&event.data["Identity"], &event.data["name"], &event.data["picture"], &event.data["ContactsJson"], &event.data["ServerList"]);
print!("profile: {:?}", profile);
}
"NewMessageFromPeer" => {
let to = event.data["ProfileOnion"].to_string();
let conversation = event.data["RemotePeer"].to_string();
let message: Message = serde_json::from_str(event.data["Data"].as_str()).unwrap();
let response = Message{o:1, d:message.d};
let response_json = serde_json::to_string(&response).unwrap();
cwtch.send_message(to.as_str(), conversation.as_str(), response_json.as_str());
}
_ => println!("unhandled event!"),
};
};
});
event_loop_handle.join().unwrap();
}

View File

@ -5,12 +5,9 @@
use std::ffi::{CString};
use std::ffi::{CStr};
use std::collections::HashMap;
use serde::{Deserialize, Serialize};
use super::{CwtchLib};
use crate::gobindings;
use crate::cwtchlib_go::bindings;
struct c_str_wrap {
raw: *mut i8,
@ -35,14 +32,13 @@ impl Drop for c_str_wrap {
// c_bind handles setting up c string arguments and freeing them
// c_bind!( $fn_name ( [ $string_args ]* ; [ $non_string_args : $type ]* ) $c_function -> $return_type? )
#[macro_export]
macro_rules! c_bind {
// macro for returnless fns
($func_name:ident ($($str:ident),* ; $($arg:ident: $t:ty),*) $bind_fn:ident) => {
fn $func_name(&self, $($str: &str, )* $($arg: $t, )*) {
$(let $str = c_str_wrap::new($str);)*
unsafe {
gobindings::$bind_fn($( $str.raw, $str.len, )* $($arg,)* );
bindings::$bind_fn($( $str.raw, $str.len, )* $($arg,)* );
}
}
};
@ -51,13 +47,13 @@ macro_rules! c_bind {
fn $func_name(&self, $($str: &str, )* $($arg: $t, )*) -> String {
$(let $str = c_str_wrap::new($str);)*
unsafe {
let result_ptr = gobindings::$bind_fn($( $str.raw, $str.len, )* $($arg,)* );
let result_ptr = bindings::$bind_fn($( $str.raw, $str.len, )* $($arg,)* );
let result = match CStr::from_ptr(result_ptr).to_str() {
Ok(s) => s.to_owned(),
Err(_) => "".to_string()
};
// return ownership of string memory and call the library to free it
gobindings::c_FreePointer(result_ptr);
bindings::c_FreePointer(result_ptr);
result
}
}
@ -67,26 +63,20 @@ macro_rules! c_bind {
fn $func_name(&self, $($str: &str, )* $($arg: $t, )*) -> $bind_fn_ty {
$(let $str = c_str_wrap::new($str);)*
unsafe {
let result = gobindings::$bind_fn($( $str.raw, $str.len, )* $($arg,)* );
let result = bindings::$bind_fn($( $str.raw, $str.len, )* $($arg,)* );
result
}
}
};
}
#[derive(Serialize, Deserialize, Debug)]
struct Event {
EventType: String,
Data: HashMap<String, String>
}
pub struct CwtchLibGo {}
pub struct GoCwtchLib {}
impl GoCwtchLib {
impl CwtchLibGo {
c_bind!(send_profile_event(profile, event_json;) c_SendProfileEvent);
}
impl CwtchLib for GoCwtchLib {
impl CwtchLib for CwtchLibGo {
c_bind!(start_cwtch(app_dir, tor_path;) c_StartCwtch -> i32);
c_bind!(send_app_event(event_json;) c_SendAppEvent);
c_bind!(get_appbus_event(;) c_GetAppBusEvent -> String);
@ -101,7 +91,7 @@ impl CwtchLib for GoCwtchLib {
c_bind!(send_message(profile, contact, msg;) c_SendMessage);
c_bind!(send_invitation(profile, contact, target;) c_SendInvitation);
fn reset_tor(&self) {
unsafe { gobindings::c_ResetTor(); }
unsafe { bindings::c_ResetTor(); }
}
c_bind!(create_group(profile, server, name;) c_CreateGroup);
c_bind!(delete_profile(profile, pass;) c_DeleteProfile);
@ -113,6 +103,6 @@ impl CwtchLib for GoCwtchLib {
c_bind!(set_group_attribute(profile, group, key, val;) c_SetGroupAttribute);
fn shutdown_cwtch(&self) {
unsafe { gobindings::c_ShutdownCwtch(); }
unsafe { bindings::c_ShutdownCwtch(); }
}
}

6
src/cwtchlib_go/mod.rs Normal file
View File

@ -0,0 +1,6 @@
#![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
#![allow(dead_code)]
pub mod bindings;

View File

@ -1,40 +1,84 @@
#![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
#![allow(dead_code)]
#![doc(html_logo_url = "https://git.openprivacy.ca/cwtch.im/cwtch-ui/media/branch/trunk/cwtch.png")]
mod gobindings;
mod cwtchlib_go;
mod bindings_go;
pub mod structs;
/// Interface to a Cwtch app with API matching libcwtch
pub trait CwtchLib {
/// Start a cwtch application using app_dir to store all user profile data and looking to tor_path to find tor to run
fn start_cwtch(&self, app_dir: &str, tor_path: &str) -> i32;
/// Send json of a structs::CwtchEvent to the cwtch app bus
fn send_app_event(&self, event_json: &str);
/// Pull json of a structs::CwtchEvent off the appbus for responding to
fn get_appbus_event(&self, ) -> String;
/// Create a new profile encrypted with pass
fn create_profile(&self, nick: &str, pass: &str);
/// Load any profiles encrypted by pass
fn load_profiles(&self, pass: &str);
/// Cause profile to accept contact
fn accept_contact(&self, profile: &str, contact: &str);
/// Cause profile to reject contact
fn reject_invite(&self, profile: &str, contact: &str);
/// Cause profile to block contact
fn block_contact(&self, profile: &str, contact: &str);
/// Cause profile to update contact's message to have it's flags updated
fn update_message_flags(&self, profile: &str, contact: &str, message_id: i32, message_flags: u64);
/// Get a specific message for contact of profile by index
fn get_message(&self, profile: &str, contact: &str, message_index: i32) -> String;
/// Get a specific message for contact of profile by hash
fn get_message_by_content_hash(&self, profile: &str, contact: &str, hash: &str) -> String;
/// Send json of a structs::Message from profile to contact
fn send_message(&self, profile: &str, contact: &str, msg: &str);
/// Send profile's contact an invite for/to target
fn send_invitation(&self, profile: &str, contact: &str, target: &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);
/// Delete profile with encryption/password check of pass
fn delete_profile(&self, profile: &str, pass: &str);
/// Cause profile to archive conversation with contact
fn archive_conversation(&self, profile: &str, contact: &str);
fn delete_contact(&self, profile: &str, group: &str);
/// Cause profile to delete contact/group identified by handle
fn delete_contact(&self, profile: &str, handle: &str);
/// Cuase profile to attempt to import a contact/group/keybundle identified by bundle
fn import_bundle(&self, profile: &str, bundle: &str);
/// Set a profile attribute key to val
fn set_profile_attribute(&self, profile: &str, key: &str, val: &str);
/// Set a profile's contact's attribute of key to val
fn set_contact_attribute(&self, profile: &str, contact: &str, key: &str, val: &str);
/// Set a profile's group's attribute of key to val
fn set_group_attribute(&self, profile: &str, group: &str, key: &str, val: &str);
/// Shutdown the cwtch app and associated ACN
fn shutdown_cwtch(&self, );
}
/// Create a new CwtchLib that is backed by bindings to libcwtch-go
pub fn new_cwtchlib_go() -> impl CwtchLib {
bindings_go::GoCwtchLib {}
bindings_go::CwtchLibGo {}
}

View File

@ -3,39 +3,45 @@ use std::collections::HashMap;
#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "PascalCase")]
/// Struct to serialize/deserialize events coming off the Cwtch appbus
pub struct CwtchEvent {
pub event_type: String,
pub event_ID: String,
pub event_ID: String, // event_ID because golang naming converntions in libCwtch-go
pub data: HashMap<String, String>,
}
#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
/// Struct to serialize/deserialize contacts coming from libcwtch-go
pub struct Contact {
pub onion: String,
pub name: String,
pub status: String,
pub authorization: String,
pub isGroup: bool,
pub is_group: bool,
//attr: HashMap<String, String>,
}
#[derive(Serialize, Deserialize, Debug)]
/// Struct to serialize/deserialize servers coming from libcwtch-go
pub struct Server {
pub onion: String,
pub status: String,
}
#[derive(Debug)]
/// Struct to serialize/deserialize profiles coming from libcwtch-go
pub struct Profile {
pub onion: String,
pub nick: String,
pub imagePath: String,
pub image_path: String,
pub attr: HashMap<String,String>,
pub contacts: HashMap<String, Contact>,
pub servers: HashMap<String, Server>,
}
#[derive(Debug, Serialize, Deserialize)]
/// Struct to serialize/deserialize messages sent over Cwtch between profiles / contacts
pub struct Message {
pub o: i64,
pub d: String
@ -45,10 +51,10 @@ impl Profile {
pub fn new(identity: &str, name: &str, picture: &str, contacts_json: &str, server_list: &str) -> Profile {
let contacts = Profile::process_contacts(contacts_json);
let servers = Profile::process_servers(server_list);
Profile{ onion: identity.to_string(), nick: name.to_string(), imagePath: picture.to_string(), attr: Default::default(), contacts: contacts, servers: servers }
Profile{ onion: identity.to_string(), nick: name.to_string(), image_path: picture.to_string(), attr: Default::default(), contacts: contacts, servers: servers }
}
pub fn process_contacts(constacts_json: &str) -> HashMap<String, Contact> {
fn process_contacts(constacts_json: &str) -> HashMap<String, Contact> {
let mut contacts: HashMap<String, Contact> = HashMap::new();
if constacts_json == "null" {
return contacts;
@ -61,7 +67,7 @@ impl Profile {
contacts
}
pub fn process_servers(servers_json: &str) -> HashMap<String, Server> {
fn process_servers(servers_json: &str) -> HashMap<String, Server> {
let mut servers: HashMap<String, Server> = HashMap::new();
if servers_json == "null" {
return servers;