adding documentations and examples and tweeks to be more rustful
This commit is contained in:
parent
f00daf245b
commit
652526fffb
|
@ -3,8 +3,9 @@ name = "libcwtch"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
authors = ["Dan Ballard <dan@mindstab.net>"]
|
authors = ["Dan Ballard <dan@mindstab.net>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
license = "MIT"
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
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]
|
[dependencies]
|
||||||
libc = "0.2"
|
libc = "0.2"
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
# libCwtch-rs
|
# 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
|
## 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:
|
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
|
### Todo
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
|
@ -5,12 +5,9 @@
|
||||||
|
|
||||||
use std::ffi::{CString};
|
use std::ffi::{CString};
|
||||||
use std::ffi::{CStr};
|
use std::ffi::{CStr};
|
||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
use super::{CwtchLib};
|
use super::{CwtchLib};
|
||||||
use crate::gobindings;
|
use crate::cwtchlib_go::bindings;
|
||||||
|
|
||||||
struct c_str_wrap {
|
struct c_str_wrap {
|
||||||
raw: *mut i8,
|
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 handles setting up c string arguments and freeing them
|
||||||
// c_bind!( $fn_name ( [ $string_args ]* ; [ $non_string_args : $type ]* ) $c_function -> $return_type? )
|
// c_bind!( $fn_name ( [ $string_args ]* ; [ $non_string_args : $type ]* ) $c_function -> $return_type? )
|
||||||
#[macro_export]
|
|
||||||
macro_rules! c_bind {
|
macro_rules! c_bind {
|
||||||
// macro for returnless fns
|
// macro for returnless fns
|
||||||
($func_name:ident ($($str:ident),* ; $($arg:ident: $t:ty),*) $bind_fn:ident) => {
|
($func_name:ident ($($str:ident),* ; $($arg:ident: $t:ty),*) $bind_fn:ident) => {
|
||||||
fn $func_name(&self, $($str: &str, )* $($arg: $t, )*) {
|
fn $func_name(&self, $($str: &str, )* $($arg: $t, )*) {
|
||||||
$(let $str = c_str_wrap::new($str);)*
|
$(let $str = c_str_wrap::new($str);)*
|
||||||
unsafe {
|
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 {
|
fn $func_name(&self, $($str: &str, )* $($arg: $t, )*) -> String {
|
||||||
$(let $str = c_str_wrap::new($str);)*
|
$(let $str = c_str_wrap::new($str);)*
|
||||||
unsafe {
|
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() {
|
let result = match CStr::from_ptr(result_ptr).to_str() {
|
||||||
Ok(s) => s.to_owned(),
|
Ok(s) => s.to_owned(),
|
||||||
Err(_) => "".to_string()
|
Err(_) => "".to_string()
|
||||||
};
|
};
|
||||||
// return ownership of string memory and call the library to free it
|
// return ownership of string memory and call the library to free it
|
||||||
gobindings::c_FreePointer(result_ptr);
|
bindings::c_FreePointer(result_ptr);
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -67,26 +63,20 @@ macro_rules! c_bind {
|
||||||
fn $func_name(&self, $($str: &str, )* $($arg: $t, )*) -> $bind_fn_ty {
|
fn $func_name(&self, $($str: &str, )* $($arg: $t, )*) -> $bind_fn_ty {
|
||||||
$(let $str = c_str_wrap::new($str);)*
|
$(let $str = c_str_wrap::new($str);)*
|
||||||
unsafe {
|
unsafe {
|
||||||
let result = gobindings::$bind_fn($( $str.raw, $str.len, )* $($arg,)* );
|
let result = bindings::$bind_fn($( $str.raw, $str.len, )* $($arg,)* );
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
pub struct CwtchLibGo {}
|
||||||
struct Event {
|
|
||||||
EventType: String,
|
|
||||||
Data: HashMap<String, String>
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct GoCwtchLib {}
|
impl CwtchLibGo {
|
||||||
|
|
||||||
impl GoCwtchLib {
|
|
||||||
c_bind!(send_profile_event(profile, event_json;) c_SendProfileEvent);
|
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!(start_cwtch(app_dir, tor_path;) c_StartCwtch -> i32);
|
||||||
c_bind!(send_app_event(event_json;) c_SendAppEvent);
|
c_bind!(send_app_event(event_json;) c_SendAppEvent);
|
||||||
c_bind!(get_appbus_event(;) c_GetAppBusEvent -> String);
|
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_message(profile, contact, msg;) c_SendMessage);
|
||||||
c_bind!(send_invitation(profile, contact, target;) c_SendInvitation);
|
c_bind!(send_invitation(profile, contact, target;) c_SendInvitation);
|
||||||
fn reset_tor(&self) {
|
fn reset_tor(&self) {
|
||||||
unsafe { gobindings::c_ResetTor(); }
|
unsafe { bindings::c_ResetTor(); }
|
||||||
}
|
}
|
||||||
c_bind!(create_group(profile, server, name;) c_CreateGroup);
|
c_bind!(create_group(profile, server, name;) c_CreateGroup);
|
||||||
c_bind!(delete_profile(profile, pass;) c_DeleteProfile);
|
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);
|
c_bind!(set_group_attribute(profile, group, key, val;) c_SetGroupAttribute);
|
||||||
|
|
||||||
fn shutdown_cwtch(&self) {
|
fn shutdown_cwtch(&self) {
|
||||||
unsafe { gobindings::c_ShutdownCwtch(); }
|
unsafe { bindings::c_ShutdownCwtch(); }
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
#![allow(non_upper_case_globals)]
|
||||||
|
#![allow(non_camel_case_types)]
|
||||||
|
#![allow(non_snake_case)]
|
||||||
|
#![allow(dead_code)]
|
||||||
|
|
||||||
|
pub mod bindings;
|
58
src/lib.rs
58
src/lib.rs
|
@ -1,40 +1,84 @@
|
||||||
#![allow(non_upper_case_globals)]
|
#![doc(html_logo_url = "https://git.openprivacy.ca/cwtch.im/cwtch-ui/media/branch/trunk/cwtch.png")]
|
||||||
#![allow(non_camel_case_types)]
|
|
||||||
#![allow(non_snake_case)]
|
|
||||||
#![allow(dead_code)]
|
|
||||||
|
|
||||||
mod gobindings;
|
mod cwtchlib_go;
|
||||||
mod bindings_go;
|
mod bindings_go;
|
||||||
|
|
||||||
pub mod structs;
|
pub mod structs;
|
||||||
|
|
||||||
|
/// Interface to a Cwtch app with API matching libcwtch
|
||||||
pub trait CwtchLib {
|
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;
|
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);
|
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;
|
fn get_appbus_event(&self, ) -> String;
|
||||||
|
|
||||||
|
/// Create a new profile encrypted with pass
|
||||||
fn create_profile(&self, nick: &str, pass: &str);
|
fn create_profile(&self, nick: &str, pass: &str);
|
||||||
|
|
||||||
|
/// Load any profiles encrypted by pass
|
||||||
fn load_profiles(&self, pass: &str);
|
fn load_profiles(&self, pass: &str);
|
||||||
|
|
||||||
|
/// Cause profile to accept contact
|
||||||
fn accept_contact(&self, profile: &str, contact: &str);
|
fn accept_contact(&self, profile: &str, contact: &str);
|
||||||
|
|
||||||
|
/// Cause profile to reject contact
|
||||||
fn reject_invite(&self, profile: &str, contact: &str);
|
fn reject_invite(&self, profile: &str, contact: &str);
|
||||||
|
|
||||||
|
/// Cause profile to block contact
|
||||||
fn block_contact(&self, profile: &str, contact: &str);
|
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);
|
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;
|
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;
|
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);
|
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);
|
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, );
|
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: &str, 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: &str, pass: &str);
|
||||||
|
|
||||||
|
/// Cause profile to archive conversation with contact
|
||||||
fn archive_conversation(&self, profile: &str, contact: &str);
|
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);
|
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);
|
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);
|
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);
|
fn set_group_attribute(&self, profile: &str, group: &str, key: &str, val: &str);
|
||||||
|
|
||||||
|
/// Shutdown the cwtch app and associated ACN
|
||||||
fn shutdown_cwtch(&self, );
|
fn shutdown_cwtch(&self, );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a new CwtchLib that is backed by bindings to libcwtch-go
|
||||||
pub fn new_cwtchlib_go() -> impl CwtchLib {
|
pub fn new_cwtchlib_go() -> impl CwtchLib {
|
||||||
bindings_go::GoCwtchLib {}
|
bindings_go::CwtchLibGo {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,39 +3,45 @@ use std::collections::HashMap;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
#[serde(rename_all = "PascalCase")]
|
#[serde(rename_all = "PascalCase")]
|
||||||
|
/// Struct to serialize/deserialize events coming off the Cwtch appbus
|
||||||
pub struct CwtchEvent {
|
pub struct CwtchEvent {
|
||||||
pub event_type: String,
|
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>,
|
pub data: HashMap<String, String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
/// Struct to serialize/deserialize contacts coming from libcwtch-go
|
||||||
pub struct Contact {
|
pub struct Contact {
|
||||||
pub onion: String,
|
pub onion: String,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub status: String,
|
pub status: String,
|
||||||
pub authorization: String,
|
pub authorization: String,
|
||||||
pub isGroup: bool,
|
pub is_group: bool,
|
||||||
//attr: HashMap<String, String>,
|
//attr: HashMap<String, String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
/// Struct to serialize/deserialize servers coming from libcwtch-go
|
||||||
pub struct Server {
|
pub struct Server {
|
||||||
pub onion: String,
|
pub onion: String,
|
||||||
pub status: String,
|
pub status: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
/// Struct to serialize/deserialize profiles coming from libcwtch-go
|
||||||
pub struct Profile {
|
pub struct Profile {
|
||||||
pub onion: String,
|
pub onion: String,
|
||||||
pub nick: String,
|
pub nick: String,
|
||||||
pub imagePath: String,
|
pub image_path: String,
|
||||||
pub attr: HashMap<String,String>,
|
pub attr: HashMap<String,String>,
|
||||||
pub contacts: HashMap<String, Contact>,
|
pub contacts: HashMap<String, Contact>,
|
||||||
pub servers: HashMap<String, Server>,
|
pub servers: HashMap<String, Server>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
/// Struct to serialize/deserialize messages sent over Cwtch between profiles / contacts
|
||||||
pub struct Message {
|
pub struct Message {
|
||||||
pub o: i64,
|
pub o: i64,
|
||||||
pub d: String
|
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 {
|
pub fn new(identity: &str, name: &str, picture: &str, contacts_json: &str, server_list: &str) -> Profile {
|
||||||
let contacts = Profile::process_contacts(contacts_json);
|
let contacts = Profile::process_contacts(contacts_json);
|
||||||
let servers = Profile::process_servers(server_list);
|
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();
|
let mut contacts: HashMap<String, Contact> = HashMap::new();
|
||||||
if constacts_json == "null" {
|
if constacts_json == "null" {
|
||||||
return contacts;
|
return contacts;
|
||||||
|
@ -61,7 +67,7 @@ impl Profile {
|
||||||
contacts
|
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();
|
let mut servers: HashMap<String, Server> = HashMap::new();
|
||||||
if servers_json == "null" {
|
if servers_json == "null" {
|
||||||
return servers;
|
return servers;
|
||||||
|
|
Loading…
Reference in New Issue