From 1ead7e597ca9684a9b01a0e265462c9374f4431a Mon Sep 17 00:00:00 2001 From: Dan Ballard Date: Fri, 3 Sep 2021 20:36:35 -0700 Subject: [PATCH] libcwtch-rs: Rust bindings for libcwtch-go --- .gitignore | 2 + Cargo.lock | 96 ++++++++++ Cargo.toml | 13 ++ README.md | 21 +++ build.rs | 16 ++ libCwtch.h | 104 +++++++++++ src/bindings_go.rs | 118 +++++++++++++ src/gobindings/mod.rs | 399 ++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 40 +++++ src/structs.rs | 75 ++++++++ 10 files changed, 884 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 README.md create mode 100644 build.rs create mode 100644 libCwtch.h create mode 100644 src/bindings_go.rs create mode 100644 src/gobindings/mod.rs create mode 100644 src/lib.rs create mode 100644 src/structs.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3a8cabc --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +.idea diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..e8e3aed --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,96 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "itoa" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" + +[[package]] +name = "libc" +version = "0.2.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320cfe77175da3a483efed4bc0adc1968ca050b098ce4f2f1c13a56626128790" + +[[package]] +name = "libcwtch" +version = "0.1.0" +dependencies = [ + "libc", + "serde", + "serde_json", +] + +[[package]] +name = "proc-macro2" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c7ed8b8c7b886ea3ed7dde405212185f423ab44682667c8c6dd14aa1d9f6612" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "ryu" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" + +[[package]] +name = "serde" +version = "1.0.127" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f03b9878abf6d14e6779d3f24f07b2cfa90352cfec4acc5aab8f1ac7f146fae8" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.127" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a024926d3432516606328597e0f224a51355a493b49fdd67e9209187cbe55ecc" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "336b10da19a12ad094b59d870ebde26a45402e5b470add4b5fd03c5048a32127" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "syn" +version = "1.0.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1873d832550d4588c3dbc20f01361ab00bfe741048f71e3fecf145a7cc18b29c" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "unicode-xid" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..f426ad1 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "libcwtch" +version = "0.1.0" +authors = ["Dan Ballard "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +libc = "0.2" +serde_json = "1.0" +serde = { version = "1.0.127", features = ["derive"] } + diff --git a/README.md b/README.md new file mode 100644 index 0000000..d8a39e4 --- /dev/null +++ b/README.md @@ -0,0 +1,21 @@ +# libCwtch-rs + +Rust bindings for libCwtch + +## Building + +### Updating libCwtch and bingings.rs with Bindgen + +``` +cargo install bindgen +``` + +libCwtch.so version is specified in build.rs. If updating, also download the corresponding libCwtch.h and delete +the 'preamble from import "C"' section as it imports headers required for the C lib to compile +but that we don't want to create rust bindings for (like importing stdlib.h). Then: + +``` +bindgen libCwtch.h -o src/gobindings/mod.rs +``` + +### Todo diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..f57d188 --- /dev/null +++ b/build.rs @@ -0,0 +1,16 @@ +use std::process::Command; +use std::env; +use std::path::Path; + +fn main() { + let out_dir = env::var_os("OUT_DIR").unwrap(); + println!("cargo:rustc-flags=-L {}", out_dir.to_str().unwrap()); + println!("cargo:rustc-link-lib=Cwtch"); + + println!("cargo:rerun-if-changed=build.rs"); + println!("cargo:rerun-if-changed=libCwtch.h"); + + let lib_cwtch_path = Path::new(&out_dir).join("libCwtch.so"); + // https://git.openprivacy.ca/cwtch.im/libcwtch-go/releases v1.2.0 + Command::new("wget").arg("https://git.openprivacy.ca/attachments/e77c69f0-9487-4808-bc23-092d943bc4a6").arg("-O").arg(lib_cwtch_path).output().expect("failed to download libCwtch.so"); +} \ No newline at end of file diff --git a/libCwtch.h b/libCwtch.h new file mode 100644 index 0000000..429db59 --- /dev/null +++ b/libCwtch.h @@ -0,0 +1,104 @@ +/* Code generated by cmd/cgo; DO NOT EDIT. */ + +/* package git.openprivacy.ca/cwtch.im/libcwtch-go */ + + +#line 1 "cgo-builtin-export-prolog" + +#include /* for ptrdiff_t below */ + +#ifndef GO_CGO_EXPORT_PROLOGUE_H +#define GO_CGO_EXPORT_PROLOGUE_H + +#ifndef GO_CGO_GOSTRING_TYPEDEF +typedef struct { const char *p; ptrdiff_t n; } _GoString_; +#endif + +#endif + +/* Start of boilerplate cgo prologue. */ +#line 1 "cgo-gcc-export-header-prolog" + +#ifndef GO_CGO_PROLOGUE_H +#define GO_CGO_PROLOGUE_H + +typedef signed char GoInt8; +typedef unsigned char GoUint8; +typedef short GoInt16; +typedef unsigned short GoUint16; +typedef int GoInt32; +typedef unsigned int GoUint32; +typedef long long GoInt64; +typedef unsigned long long GoUint64; +typedef GoInt64 GoInt; +typedef GoUint64 GoUint; +typedef __SIZE_TYPE__ GoUintptr; +typedef float GoFloat32; +typedef double GoFloat64; +typedef float _Complex GoComplex64; +typedef double _Complex GoComplex128; + +/* + static assertion to make sure the file is being used on architecture + at least with matching size of GoInt. +*/ +typedef char _check_for_64_bit_pointer_matching_GoInt[sizeof(void*)==64/8 ? 1:-1]; + +#ifndef GO_CGO_GOSTRING_TYPEDEF +typedef _GoString_ GoString; +#endif +typedef void *GoMap; +typedef void *GoChan; +typedef struct { void *t; void *v; } GoInterface; +typedef struct { void *data; GoInt len; GoInt cap; } GoSlice; + +#endif + +/* End of boilerplate cgo prologue. */ + +#ifdef __cplusplus +extern "C" { +#endif + +extern int c_StartCwtch(char* dir_c, int len, char* tor_c, int torLen); +extern void c_ReconnectCwtchForeground(); + +// A generic method for Rebroadcasting App Events from a UI +extern void c_SendAppEvent(char* json_ptr, int json_len); + +// A generic method for Rebroadcasting Profile Events from a UI +extern void c_SendProfileEvent(char* onion_ptr, int onion_len, char* json_ptr, int json_len); + +// the pointer returned from this function **must** be freed using c_Free +extern char* c_GetAppBusEvent(); +extern void c_CreateProfile(char* nick_ptr, int nick_len, char* pass_ptr, int pass_len); +extern void c_LoadProfiles(char* passwordPtr, int passwordLen); +extern void c_AcceptContact(char* profilePtr, int profileLen, char* handlePtr, int handleLen); +extern void c_RejectInvite(char* profilePtr, int profileLen, char* handlePtr, int handleLen); +extern void c_BlockContact(char* profilePtr, int profileLen, char* handlePtr, int handleLen); +extern void c_UpdateMessageFlags(char* profile_ptr, int profile_len, char* handle_ptr, int handle_len, int mIdx, long unsigned int message_flags); + +// the pointer returned from this function **must** be Freed by c_Free +extern char* c_GetMessage(char* profile_ptr, int profile_len, char* handle_ptr, int handle_len, int message_index); + +// the pointer returned from this function **must** be freed by calling c_Free +extern char* c_GetMessagesByContentHash(char* profile_ptr, int profile_len, char* handle_ptr, int handle_len, char* contenthash_ptr, int contenthash_len); + +// Dangerous function. Should only be used as documented in `MEMORY.md` +extern void c_FreePointer(char* ptr); +extern void c_SendMessage(char* profile_ptr, int profile_len, char* handle_ptr, int handle_len, char* msg_ptr, int msg_len); +extern void c_SendInvitation(char* profile_ptr, int profile_len, char* handle_ptr, int handle_len, char* target_ptr, int target_len); +extern void c_ResetTor(); +extern void c_CreateGroup(char* profile_ptr, int profile_len, char* server_ptr, int server_len, char* name_ptr, int name_len); +extern void c_DeleteProfile(char* profile_ptr, int profile_len, char* password_ptr, int password_len); +extern void c_ArchiveConversation(char* profile_ptr, int profile_len, char* contact_ptr, int contact_len); +extern void c_DeleteContact(char* profile_ptr, int profile_len, char* hanlde_ptr, int handle_len); +extern void c_ImportBundle(char* profile_ptr, int profile_len, char* bundle_ptr, int bundle_len); +extern void c_SetProfileAttribute(char* profile_ptr, int profile_len, char* key_ptr, int key_len, char* val_ptr, int val_len); +extern void c_SetContactAttribute(char* profile_ptr, int profile_len, char* contact_ptr, int contact_len, char* key_ptr, int key_len, char* val_ptr, int val_len); +extern void c_SetGroupAttribute(char* profile_ptr, int profile_len, char* group_ptr, int group_len, char* key_ptr, int key_len, char* val_ptr, int val_len); +extern void c_ShutdownCwtch(); + +#ifdef __cplusplus +} +#endif diff --git a/src/bindings_go.rs b/src/bindings_go.rs new file mode 100644 index 0000000..cad9beb --- /dev/null +++ b/src/bindings_go.rs @@ -0,0 +1,118 @@ +#![allow(non_upper_case_globals)] +#![allow(non_camel_case_types)] +#![allow(non_snake_case)] +#![allow(dead_code)] + +use std::ffi::{CString}; +use std::ffi::{CStr}; +use std::collections::HashMap; + +use serde::{Deserialize, Serialize}; + +use super::{CwtchLib}; +use crate::gobindings; + +struct c_str_wrap { + raw: *mut i8, + len: i32 +} + +impl c_str_wrap { + pub fn new(str: &str) -> c_str_wrap { + let cs = match CString::new(str) { + Ok(s) => s, + Err(_) => CString::new("").unwrap() + }; + c_str_wrap { len: cs.as_bytes().len() as i32, raw: cs.into_raw() } + } +} + +impl Drop for c_str_wrap { + fn drop(&mut self) { + unsafe { CString::from_raw(self.raw); } + } +} + +// 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,)* ); + } + } + }; + // macro for str returning fns + ($func_name:ident ($($str:ident),* ; $($arg:ident: $t:ty),* ) $bind_fn:ident -> String) => { + 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 = 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); + result + } + } + }; + // macro for value returning fns + ($func_name:ident ($($str:ident),* ; $($arg:ident: $t:ty),* ) $bind_fn:ident -> $bind_fn_ty:ty) => { + 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,)* ); + result + } + } + }; +} + +#[derive(Serialize, Deserialize, Debug)] +struct Event { + EventType: String, + Data: HashMap +} + +pub struct GoCwtchLib {} + +impl GoCwtchLib { + c_bind!(send_profile_event(profile, event_json;) c_SendProfileEvent); +} + +impl CwtchLib for GoCwtchLib { + 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); + c_bind!(create_profile(nick, pass;) c_CreateProfile); + c_bind!(load_profiles(pass;) c_LoadProfiles); + c_bind!(accept_contact(profile, contact;) c_AcceptContact); + c_bind!(reject_invite(profile, contact;) c_RejectInvite); + c_bind!(block_contact(profile, contact;) c_BlockContact); + c_bind!(update_message_flags(profile, contact; message_id: i32, message_flags: u64) c_UpdateMessageFlags); + c_bind!(get_message(profile, contact; message_index: i32) c_GetMessage -> String); + c_bind!(get_message_by_content_hash(profile, contact, hash;) c_GetMessagesByContentHash -> String); + 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(); } + } + c_bind!(create_group(profile, server, name;) c_CreateGroup); + c_bind!(delete_profile(profile, pass;) c_DeleteProfile); + c_bind!(archive_conversation(profile, contact;) c_ArchiveConversation); + c_bind!(delete_contact(profile, group;) c_DeleteContact); + c_bind!(import_bundle(profile, bundle;) c_ImportBundle); + c_bind!(set_profile_attribute(profile, key, val;) c_SetProfileAttribute); + c_bind!(set_contact_attribute(profile, contact, key, val;) c_SetContactAttribute); + c_bind!(set_group_attribute(profile, group, key, val;) c_SetGroupAttribute); + + fn shutdown_cwtch(&self) { + unsafe { gobindings::c_ShutdownCwtch(); } + } +} \ No newline at end of file diff --git a/src/gobindings/mod.rs b/src/gobindings/mod.rs new file mode 100644 index 0000000..aa967a6 --- /dev/null +++ b/src/gobindings/mod.rs @@ -0,0 +1,399 @@ +/* automatically generated by rust-bindgen 0.58.1 */ + +#[derive(PartialEq, Copy, Clone, Hash, Debug, Default)] +#[repr(C)] +pub struct __BindgenComplex { + pub re: T, + pub im: T, +} +pub type size_t = ::std::os::raw::c_ulong; +pub type wchar_t = ::std::os::raw::c_int; +#[repr(C)] +#[repr(align(16))] +#[derive(Debug, Copy, Clone)] +pub struct max_align_t { + pub __clang_max_align_nonce1: ::std::os::raw::c_longlong, + pub __bindgen_padding_0: u64, + pub __clang_max_align_nonce2: u128, +} +#[test] +fn bindgen_test_layout_max_align_t() { + assert_eq!( + ::std::mem::size_of::(), + 32usize, + concat!("Size of: ", stringify!(max_align_t)) + ); + assert_eq!( + ::std::mem::align_of::(), + 16usize, + concat!("Alignment of ", stringify!(max_align_t)) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).__clang_max_align_nonce1 as *const _ as usize + }, + 0usize, + concat!( + "Offset of field: ", + stringify!(max_align_t), + "::", + stringify!(__clang_max_align_nonce1) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).__clang_max_align_nonce2 as *const _ as usize + }, + 16usize, + concat!( + "Offset of field: ", + stringify!(max_align_t), + "::", + stringify!(__clang_max_align_nonce2) + ) + ); +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct _GoString_ { + pub p: *const ::std::os::raw::c_char, + pub n: isize, +} +#[test] +fn bindgen_test_layout__GoString_() { + assert_eq!( + ::std::mem::size_of::<_GoString_>(), + 16usize, + concat!("Size of: ", stringify!(_GoString_)) + ); + assert_eq!( + ::std::mem::align_of::<_GoString_>(), + 8usize, + concat!("Alignment of ", stringify!(_GoString_)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<_GoString_>())).p as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(_GoString_), + "::", + stringify!(p) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<_GoString_>())).n as *const _ as usize }, + 8usize, + concat!( + "Offset of field: ", + stringify!(_GoString_), + "::", + stringify!(n) + ) + ); +} +pub type GoInt8 = ::std::os::raw::c_schar; +pub type GoUint8 = ::std::os::raw::c_uchar; +pub type GoInt16 = ::std::os::raw::c_short; +pub type GoUint16 = ::std::os::raw::c_ushort; +pub type GoInt32 = ::std::os::raw::c_int; +pub type GoUint32 = ::std::os::raw::c_uint; +pub type GoInt64 = ::std::os::raw::c_longlong; +pub type GoUint64 = ::std::os::raw::c_ulonglong; +pub type GoInt = GoInt64; +pub type GoUint = GoUint64; +pub type GoUintptr = ::std::os::raw::c_ulong; +pub type GoFloat32 = f32; +pub type GoFloat64 = f64; +pub type GoComplex64 = __BindgenComplex; +pub type GoComplex128 = __BindgenComplex; +pub type _check_for_64_bit_pointer_matching_GoInt = [::std::os::raw::c_char; 1usize]; +pub type GoString = _GoString_; +pub type GoMap = *mut ::std::os::raw::c_void; +pub type GoChan = *mut ::std::os::raw::c_void; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct GoInterface { + pub t: *mut ::std::os::raw::c_void, + pub v: *mut ::std::os::raw::c_void, +} +#[test] +fn bindgen_test_layout_GoInterface() { + assert_eq!( + ::std::mem::size_of::(), + 16usize, + concat!("Size of: ", stringify!(GoInterface)) + ); + assert_eq!( + ::std::mem::align_of::(), + 8usize, + concat!("Alignment of ", stringify!(GoInterface)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).t as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(GoInterface), + "::", + stringify!(t) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).v as *const _ as usize }, + 8usize, + concat!( + "Offset of field: ", + stringify!(GoInterface), + "::", + stringify!(v) + ) + ); +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct GoSlice { + pub data: *mut ::std::os::raw::c_void, + pub len: GoInt, + pub cap: GoInt, +} +#[test] +fn bindgen_test_layout_GoSlice() { + assert_eq!( + ::std::mem::size_of::(), + 24usize, + concat!("Size of: ", stringify!(GoSlice)) + ); + assert_eq!( + ::std::mem::align_of::(), + 8usize, + concat!("Alignment of ", stringify!(GoSlice)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).data as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(GoSlice), + "::", + stringify!(data) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).len as *const _ as usize }, + 8usize, + concat!( + "Offset of field: ", + stringify!(GoSlice), + "::", + stringify!(len) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).cap as *const _ as usize }, + 16usize, + concat!( + "Offset of field: ", + stringify!(GoSlice), + "::", + stringify!(cap) + ) + ); +} +extern "C" { + pub fn c_StartCwtch( + dir_c: *mut ::std::os::raw::c_char, + len: ::std::os::raw::c_int, + tor_c: *mut ::std::os::raw::c_char, + torLen: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn c_ReconnectCwtchForeground(); +} +extern "C" { + pub fn c_SendAppEvent(json_ptr: *mut ::std::os::raw::c_char, json_len: ::std::os::raw::c_int); +} +extern "C" { + pub fn c_SendProfileEvent( + onion_ptr: *mut ::std::os::raw::c_char, + onion_len: ::std::os::raw::c_int, + json_ptr: *mut ::std::os::raw::c_char, + json_len: ::std::os::raw::c_int, + ); +} +extern "C" { + pub fn c_GetAppBusEvent() -> *mut ::std::os::raw::c_char; +} +extern "C" { + pub fn c_CreateProfile( + nick_ptr: *mut ::std::os::raw::c_char, + nick_len: ::std::os::raw::c_int, + pass_ptr: *mut ::std::os::raw::c_char, + pass_len: ::std::os::raw::c_int, + ); +} +extern "C" { + pub fn c_LoadProfiles( + passwordPtr: *mut ::std::os::raw::c_char, + passwordLen: ::std::os::raw::c_int, + ); +} +extern "C" { + pub fn c_AcceptContact( + profilePtr: *mut ::std::os::raw::c_char, + profileLen: ::std::os::raw::c_int, + handlePtr: *mut ::std::os::raw::c_char, + handleLen: ::std::os::raw::c_int, + ); +} +extern "C" { + pub fn c_RejectInvite( + profilePtr: *mut ::std::os::raw::c_char, + profileLen: ::std::os::raw::c_int, + handlePtr: *mut ::std::os::raw::c_char, + handleLen: ::std::os::raw::c_int, + ); +} +extern "C" { + pub fn c_BlockContact( + profilePtr: *mut ::std::os::raw::c_char, + profileLen: ::std::os::raw::c_int, + handlePtr: *mut ::std::os::raw::c_char, + handleLen: ::std::os::raw::c_int, + ); +} +extern "C" { + pub fn c_UpdateMessageFlags( + profile_ptr: *mut ::std::os::raw::c_char, + profile_len: ::std::os::raw::c_int, + handle_ptr: *mut ::std::os::raw::c_char, + handle_len: ::std::os::raw::c_int, + mIdx: ::std::os::raw::c_int, + message_flags: ::std::os::raw::c_ulong, + ); +} +extern "C" { + pub fn c_GetMessage( + profile_ptr: *mut ::std::os::raw::c_char, + profile_len: ::std::os::raw::c_int, + handle_ptr: *mut ::std::os::raw::c_char, + handle_len: ::std::os::raw::c_int, + message_index: ::std::os::raw::c_int, + ) -> *mut ::std::os::raw::c_char; +} +extern "C" { + pub fn c_GetMessagesByContentHash( + profile_ptr: *mut ::std::os::raw::c_char, + profile_len: ::std::os::raw::c_int, + handle_ptr: *mut ::std::os::raw::c_char, + handle_len: ::std::os::raw::c_int, + contenthash_ptr: *mut ::std::os::raw::c_char, + contenthash_len: ::std::os::raw::c_int, + ) -> *mut ::std::os::raw::c_char; +} +extern "C" { + pub fn c_FreePointer(ptr: *mut ::std::os::raw::c_char); +} +extern "C" { + pub fn c_SendMessage( + profile_ptr: *mut ::std::os::raw::c_char, + profile_len: ::std::os::raw::c_int, + handle_ptr: *mut ::std::os::raw::c_char, + handle_len: ::std::os::raw::c_int, + msg_ptr: *mut ::std::os::raw::c_char, + msg_len: ::std::os::raw::c_int, + ); +} +extern "C" { + pub fn c_SendInvitation( + profile_ptr: *mut ::std::os::raw::c_char, + profile_len: ::std::os::raw::c_int, + handle_ptr: *mut ::std::os::raw::c_char, + handle_len: ::std::os::raw::c_int, + target_ptr: *mut ::std::os::raw::c_char, + target_len: ::std::os::raw::c_int, + ); +} +extern "C" { + pub fn c_ResetTor(); +} +extern "C" { + pub fn c_CreateGroup( + profile_ptr: *mut ::std::os::raw::c_char, + profile_len: ::std::os::raw::c_int, + server_ptr: *mut ::std::os::raw::c_char, + server_len: ::std::os::raw::c_int, + name_ptr: *mut ::std::os::raw::c_char, + name_len: ::std::os::raw::c_int, + ); +} +extern "C" { + pub fn c_DeleteProfile( + profile_ptr: *mut ::std::os::raw::c_char, + profile_len: ::std::os::raw::c_int, + password_ptr: *mut ::std::os::raw::c_char, + password_len: ::std::os::raw::c_int, + ); +} +extern "C" { + pub fn c_ArchiveConversation( + profile_ptr: *mut ::std::os::raw::c_char, + profile_len: ::std::os::raw::c_int, + contact_ptr: *mut ::std::os::raw::c_char, + contact_len: ::std::os::raw::c_int, + ); +} +extern "C" { + pub fn c_DeleteContact( + profile_ptr: *mut ::std::os::raw::c_char, + profile_len: ::std::os::raw::c_int, + hanlde_ptr: *mut ::std::os::raw::c_char, + handle_len: ::std::os::raw::c_int, + ); +} +extern "C" { + pub fn c_ImportBundle( + profile_ptr: *mut ::std::os::raw::c_char, + profile_len: ::std::os::raw::c_int, + bundle_ptr: *mut ::std::os::raw::c_char, + bundle_len: ::std::os::raw::c_int, + ); +} +extern "C" { + pub fn c_SetProfileAttribute( + profile_ptr: *mut ::std::os::raw::c_char, + profile_len: ::std::os::raw::c_int, + key_ptr: *mut ::std::os::raw::c_char, + key_len: ::std::os::raw::c_int, + val_ptr: *mut ::std::os::raw::c_char, + val_len: ::std::os::raw::c_int, + ); +} +extern "C" { + pub fn c_SetContactAttribute( + profile_ptr: *mut ::std::os::raw::c_char, + profile_len: ::std::os::raw::c_int, + contact_ptr: *mut ::std::os::raw::c_char, + contact_len: ::std::os::raw::c_int, + key_ptr: *mut ::std::os::raw::c_char, + key_len: ::std::os::raw::c_int, + val_ptr: *mut ::std::os::raw::c_char, + val_len: ::std::os::raw::c_int, + ); +} +extern "C" { + pub fn c_SetGroupAttribute( + profile_ptr: *mut ::std::os::raw::c_char, + profile_len: ::std::os::raw::c_int, + group_ptr: *mut ::std::os::raw::c_char, + group_len: ::std::os::raw::c_int, + key_ptr: *mut ::std::os::raw::c_char, + key_len: ::std::os::raw::c_int, + val_ptr: *mut ::std::os::raw::c_char, + val_len: ::std::os::raw::c_int, + ); +} +extern "C" { + pub fn c_ShutdownCwtch(); +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..eacbc37 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,40 @@ +#![allow(non_upper_case_globals)] +#![allow(non_camel_case_types)] +#![allow(non_snake_case)] +#![allow(dead_code)] + +mod gobindings; +mod bindings_go; + +pub mod structs; + +pub trait CwtchLib { + fn start_cwtch(&self, app_dir: &str, tor_path: &str) -> i32; + fn send_app_event(&self, event_json: &str); + fn get_appbus_event(&self, ) -> String; + fn create_profile(&self, nick: &str, pass: &str); + fn load_profiles(&self, pass: &str); + fn accept_contact(&self, profile: &str, contact: &str); + fn reject_invite(&self, profile: &str, contact: &str); + fn block_contact(&self, profile: &str, contact: &str); + fn update_message_flags(&self, profile: &str, contact: &str, message_id: i32, message_flags: u64); + fn get_message(&self, profile: &str, contact: &str, message_index: i32) -> String; + fn get_message_by_content_hash(&self, profile: &str, contact: &str, hash: &str) -> String; + fn send_message(&self, profile: &str, contact: &str, msg: &str); + fn send_invitation(&self, profile: &str, contact: &str, target: &str); + fn reset_tor(&self, ); + fn create_group(&self, profile: &str, server: &str, name: &str); + fn delete_profile(&self, profile: &str, pass: &str); + fn archive_conversation(&self, profile: &str, contact: &str); + fn delete_contact(&self, profile: &str, group: &str); + fn import_bundle(&self, profile: &str, bundle: &str); + fn set_profile_attribute(&self, profile: &str, key: &str, val: &str); + fn set_contact_attribute(&self, profile: &str, contact: &str, key: &str, val: &str); + fn set_group_attribute(&self, profile: &str, group: &str, key: &str, val: &str); + fn shutdown_cwtch(&self, ); +} + +pub fn new_cwtchlib_go() -> impl CwtchLib { + bindings_go::GoCwtchLib {} +} + diff --git a/src/structs.rs b/src/structs.rs new file mode 100644 index 0000000..7affa49 --- /dev/null +++ b/src/structs.rs @@ -0,0 +1,75 @@ +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; + +#[derive(Serialize, Deserialize, Debug)] +#[serde(rename_all = "PascalCase")] +pub struct CwtchEvent { + pub event_type: String, + pub event_ID: String, + pub data: HashMap, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct Contact { + pub onion: String, + pub name: String, + pub status: String, + pub authorization: String, + pub isGroup: bool, + //attr: HashMap, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct Server { + pub onion: String, + pub status: String, +} + +#[derive(Debug)] +pub struct Profile { + pub onion: String, + pub nick: String, + pub imagePath: String, + pub attr: HashMap, + pub contacts: HashMap, + pub servers: HashMap, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct Message { + pub o: i64, + pub d: String +} + +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 } + } + + pub fn process_contacts(constacts_json: &str) -> HashMap { + let mut contacts: HashMap = HashMap::new(); + if constacts_json == "null" { + return contacts; + } + println!("contacts_json: '{}'", constacts_json); + let contacts_map: Vec = serde_json::from_str(constacts_json).unwrap(); + for contact in contacts_map { + contacts.insert(contact.onion.clone(), contact); + } + contacts + } + + pub fn process_servers(servers_json: &str) -> HashMap { + let mut servers: HashMap = HashMap::new(); + if servers_json == "null" { + return servers; + } + let servers_map: Vec = serde_json::from_str(servers_json).unwrap(); + for server in servers_map { + servers.insert(server.onion.clone(), server); + } + servers + } +} \ No newline at end of file