diff --git a/.gitignore b/.gitignore index 927e72a8b..58f557c38 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,3 @@ **/*.rs.bk Cargo.lock *.db -deltachat.h \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index 19e7560ff..a0f211d0f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,18 +1,12 @@ [package] -name = "deltachat-core-rust" +name = "deltachat" version = "0.1.0" authors = ["dignifiedquire "] edition = "2018" -[lib] -name = "deltachat" -crate-type = ["cdylib", "staticlib"] - [build-dependencies] cc = "1.0.35" pkg-config = "0.3" -cbindgen = "0.8.3" - [dependencies] c2rust-bitfields = "0.1.0" @@ -25,3 +19,8 @@ rand = "0.6.5" smallvec = "0.6.9" libsqlite3-sys = "0.14.0" reqwest = "0.9.15" + +[workspace] +members = [ + "deltachat-ffi" +] \ No newline at end of file diff --git a/README.md b/README.md index b9da68c01..1f8c809a4 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,15 @@ # Delta Chat Rust Current commit on deltachat/deltachate-core: `12ef73c8e76185f9b78e844ea673025f56a959ab`. + +## Development + +```sh +# run example +$ cargo run --example simple +# build header file +$ cargo build -p deltachat_ffi --release +$ cat deltachat-ffi/deltachat.h +# run tests +$ cargo test --all +``` diff --git a/build.rs b/build.rs index f865838da..dd24e9efd 100644 --- a/build.rs +++ b/build.rs @@ -1,9 +1,5 @@ extern crate cc; -use std::env; - -const VERSION: &'static str = env!("CARGO_PKG_VERSION"); - fn link_dylib(lib: &str) { println!("cargo:rustc-link-lib=dylib={}", lib); } @@ -31,29 +27,6 @@ fn build_tools() { println!("rerun-if-changed=misc.c"); } -fn generate_bindings() { - let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); - let cfg = cbindgen::Config::from_root_or_default(std::path::Path::new(&crate_dir)); - let c = cbindgen::Builder::new() - .with_config(cfg) - .with_crate(crate_dir) - .with_header(format!("/* deltachat Header Version {} */", VERSION)) - // .with_language(cbindgen::Language::C) - .generate(); - - // This is needed to ensure we don't panic if there are errors in the crates code - // but rather just tell the rest of the system we can't proceed. - match c { - Ok(res) => { - res.write_to_file("deltachat.h"); - } - Err(err) => { - eprintln!("unable to generate bindings: {:#?}", err); - std::process::exit(1); - } - } -} - fn main() { build_tools(); @@ -74,6 +47,4 @@ fn main() { } else if std::env::var("TARGET").unwrap().contains("linux") { link_dylib("etpan"); } - - // generate_bindings(); } diff --git a/deltachat-ffi/.gitignore b/deltachat-ffi/.gitignore new file mode 100644 index 000000000..5bf0f62f9 --- /dev/null +++ b/deltachat-ffi/.gitignore @@ -0,0 +1,5 @@ +/target/ +**/*.rs.bk +Cargo.lock +.profile +*.h diff --git a/deltachat-ffi/Cargo.toml b/deltachat-ffi/Cargo.toml new file mode 100644 index 000000000..f080a1bb1 --- /dev/null +++ b/deltachat-ffi/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "deltachat_ffi" +version = "0.1.0" +description = "Deltachat FFI" +authors = ["dignifiedquire "] +edition = "2018" +readme = "README.md" +license = "MIT OR Apache-2.0" + +keywords = ["deltachat", "chat", "openpgp", "email", "encryption"] +categories = ["cryptography", "std", "email"] + +[lib] +crate-type = ["cdylib", "staticlib"] + +[dependencies] +deltachat = { path = "../" } +libc = "0.2" + +[build-dependencies] +cbindgen = "0.8" diff --git a/deltachat-ffi/README.md b/deltachat-ffi/README.md new file mode 100644 index 000000000..d924fb795 --- /dev/null +++ b/deltachat-ffi/README.md @@ -0,0 +1 @@ +# Delta Chat C Interface diff --git a/deltachat-ffi/build.rs b/deltachat-ffi/build.rs new file mode 100644 index 000000000..acdfeee57 --- /dev/null +++ b/deltachat-ffi/build.rs @@ -0,0 +1,23 @@ +fn main() { + let crate_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap(); + + let cfg = cbindgen::Config::from_root_or_default(std::path::Path::new(&crate_dir)); + + let c = cbindgen::Builder::new() + .with_config(cfg) + .with_crate(crate_dir) + .with_language(cbindgen::Language::C) + .generate(); + + // This is needed to ensure we don't panic if there are errors in the crates code + // but rather just tell the rest of the system we can't proceed. + match c { + Ok(res) => { + res.write_to_file("deltachat.h"); + } + Err(err) => { + eprintln!("unable to generate bindings: {:#?}", err); + std::process::exit(1); + } + } +} diff --git a/deltachat-ffi/cbindgen.toml b/deltachat-ffi/cbindgen.toml new file mode 100644 index 000000000..fd9be063d --- /dev/null +++ b/deltachat-ffi/cbindgen.toml @@ -0,0 +1,3 @@ +[parse] +parse_deps = true +include = ["deltachat"] diff --git a/deltachat-ffi/src/lib.rs b/deltachat-ffi/src/lib.rs new file mode 100644 index 000000000..d346d8f80 --- /dev/null +++ b/deltachat-ffi/src/lib.rs @@ -0,0 +1,128 @@ +#![allow( + non_camel_case_types, + non_snake_case, + non_upper_case_globals, + non_upper_case_globals, + non_camel_case_types, + non_snake_case +)] +use deltachat::*; +use libc; + +pub const DC_VERSION_STR: &'static str = "0.43.0\x00"; + +#[no_mangle] +pub type dc_array_t = dc_array::dc_array_t; + +#[no_mangle] +pub unsafe extern "C" fn dc_array_unref(a: *mut dc_array::dc_array_t) { + dc_array::dc_array_unref(a) +} + +#[no_mangle] +pub unsafe extern "C" fn dc_array_add_uint(array: *mut dc_array_t, item: libc::c_ulong) { + dc_array::dc_array_add_uint(array, item) +} +#[no_mangle] +pub unsafe extern "C" fn dc_array_add_id(array: *mut dc_array_t, item: libc::c_uint) { + dc_array::dc_array_add_id(array, item) +} +#[no_mangle] +pub unsafe extern "C" fn dc_array_add_ptr(array: *mut dc_array_t, item: *mut libc::c_void) { + dc_array::dc_array_add_ptr(array, item) +} + +#[no_mangle] +pub unsafe extern "C" fn dc_array_get_cnt(array: *const dc_array_t) -> libc::c_ulong { + dc_array::dc_array_get_cnt(array) +} +#[no_mangle] +pub unsafe extern "C" fn dc_array_get_uint( + array: *const dc_array_t, + index: libc::c_ulong, +) -> libc::c_ulong { + dc_array::dc_array_get_uint(array, index) +} +#[no_mangle] +pub unsafe extern "C" fn dc_array_get_id( + array: *const dc_array_t, + index: libc::c_ulong, +) -> libc::c_uint { + dc_array::dc_array_get_id(array, index) +} +#[no_mangle] +pub unsafe extern "C" fn dc_array_get_ptr( + array: *const dc_array_t, + index: libc::c_ulong, +) -> *mut libc::c_void { + dc_array::dc_array_get_ptr(array, index) +} +#[no_mangle] +pub unsafe extern "C" fn dc_array_get_latitude( + array: *const dc_array_t, + index: libc::c_ulong, +) -> libc::c_double { + dc_array::dc_array_get_latitude(array, index) +} +#[no_mangle] +pub unsafe extern "C" fn dc_array_get_longitude( + array: *const dc_array_t, + index: libc::c_ulong, +) -> libc::c_double { + dc_array::dc_array_get_longitude(array, index) +} +#[no_mangle] +pub unsafe extern "C" fn dc_array_get_accuracy( + array: *const dc_array_t, + index: libc::c_ulong, +) -> libc::c_double { + dc_array::dc_array_get_accuracy(array, index) +} +#[no_mangle] +pub unsafe extern "C" fn dc_array_get_timestamp( + array: *const dc_array_t, + index: libc::c_ulong, +) -> libc::c_long { + dc_array::dc_array_get_timestamp(array, index) +} +#[no_mangle] +pub unsafe extern "C" fn dc_array_get_chat_id( + array: *const dc_array_t, + index: libc::c_ulong, +) -> libc::c_uint { + dc_array::dc_array_get_chat_id(array, index) +} +#[no_mangle] +pub unsafe extern "C" fn dc_array_get_contact_id( + array: *const dc_array_t, + index: libc::c_ulong, +) -> libc::c_uint { + dc_array::dc_array_get_contact_id(array, index) +} +#[no_mangle] +pub unsafe extern "C" fn dc_array_get_msg_id( + array: *const dc_array_t, + index: libc::c_ulong, +) -> libc::c_uint { + dc_array::dc_array_get_msg_id(array, index) +} +#[no_mangle] +pub unsafe extern "C" fn dc_array_get_marker( + array: *const dc_array_t, + index: libc::c_ulong, +) -> *mut libc::c_char { + dc_array::dc_array_get_marker(array, index) +} + +#[no_mangle] +pub unsafe extern "C" fn dc_array_search_id( + array: *const dc_array_t, + needle: libc::c_uint, + ret_index: *mut libc::c_ulong, +) -> libc::c_int { + dc_array::dc_array_search_id(array, needle, ret_index) +} +#[no_mangle] +pub unsafe extern "C" fn dc_array_get_raw(array: *const dc_array_t) -> *const libc::c_ulong { + dc_array::dc_array_get_raw(array) +} diff --git a/examples/simple.rs b/examples/simple.rs new file mode 100644 index 000000000..93af71221 --- /dev/null +++ b/examples/simple.rs @@ -0,0 +1,140 @@ +extern crate deltachat; + +use std::ffi::{CStr, CString}; +use std::ptr::NonNull; + +use deltachat::constants::Event; +use deltachat::dc_chat::*; +use deltachat::dc_chatlist::*; +use deltachat::dc_configure::*; +use deltachat::dc_contact::*; +use deltachat::dc_context::*; +use deltachat::dc_job::{ + dc_perform_imap_fetch, dc_perform_imap_idle, dc_perform_imap_jobs, dc_perform_smtp_idle, + dc_perform_smtp_jobs, +}; +use deltachat::dc_lot::*; + +fn cb(_ctx: *mut dc_context_t, event: Event, data1: u64, data2: u64) -> u64 { + println!("[{:?}]", event); + + match event { + Event::HTTP_GET => { + let url = unsafe { CStr::from_ptr(data1 as *const _).to_str().unwrap() }; + + match reqwest::get(url) { + Ok(ref mut res) => { + let c_res = CString::new(res.text().unwrap()).unwrap(); + // need to use strdup to allocate the result with malloc + // so it can be `free`d later. + unsafe { libc::strdup(c_res.as_ptr()) as u64 } + } + Err(err) => { + println!("failed to download: {}: {:?}", url, err); + 0 + } + } + } + Event::INFO | Event::WARNING | Event::ERROR => { + println!( + " {}", + unsafe { CStr::from_ptr(data2 as *const _) } + .to_str() + .unwrap() + ); + 0 + } + _ => 0, + } +} + +struct Wrapper(NonNull); + +unsafe impl std::marker::Send for Wrapper {} +unsafe impl std::marker::Sync for Wrapper {} + +fn main() { + unsafe { + let ctx = dc_context_new(Some(cb), std::ptr::null_mut(), std::ptr::null_mut()); + let info = dc_get_info(ctx); + let info_s = CStr::from_ptr(info); + println!("info: {}", info_s.to_str().unwrap()); + + let sendable_ctx = Wrapper(NonNull::new(ctx).unwrap()); + let t1 = std::thread::spawn(move || loop { + dc_perform_imap_jobs(sendable_ctx.0.as_ptr()); + dc_perform_imap_fetch(sendable_ctx.0.as_ptr()); + dc_perform_imap_idle(sendable_ctx.0.as_ptr()); + }); + + let sendable_ctx = Wrapper(NonNull::new(ctx).unwrap()); + let t2 = std::thread::spawn(move || loop { + dc_perform_smtp_jobs(sendable_ctx.0.as_ptr()); + dc_perform_smtp_idle(sendable_ctx.0.as_ptr()); + }); + + let dbfile = CString::new("../deltachat-core/build/hello2.db").unwrap(); + println!("opening dir"); + dc_open(ctx, dbfile.as_ptr(), std::ptr::null()); + + if dc_is_configured(ctx) == 0 { + println!("configuring"); + dc_set_config( + ctx, + CString::new("addr").unwrap().as_ptr(), + CString::new("d@testrun.org").unwrap().as_ptr(), + ); + dc_set_config( + ctx, + CString::new("mail_pw").unwrap().as_ptr(), + CString::new("***").unwrap().as_ptr(), + ); + dc_configure(ctx); + } + + std::thread::sleep_ms(4000); + + let email = CString::new("dignifiedquire@gmail.com").unwrap(); + println!("sending a message"); + let contact_id = dc_create_contact(ctx, std::ptr::null(), email.as_ptr()); + let chat_id = dc_create_chat_by_contact_id(ctx, contact_id); + let msg_text = CString::new("Hi, here is my first message!").unwrap(); + dc_send_text_msg(ctx, chat_id, msg_text.as_ptr()); + + println!("fetching chats.."); + let chats = dc_get_chatlist(ctx, 0, std::ptr::null(), 0); + + for i in 0..dc_chatlist_get_cnt(chats) { + let summary = dc_chatlist_get_summary(chats, 0, std::ptr::null_mut()); + let text1 = dc_lot_get_text1(summary); + let text2 = dc_lot_get_text2(summary); + + let text1_s = if !text1.is_null() { + Some(CStr::from_ptr(text1)) + } else { + None + }; + let text2_s = if !text2.is_null() { + Some(CStr::from_ptr(text2)) + } else { + None + }; + println!("chat: {} - {:?} - {:?}", i, text1_s, text2_s,); + dc_lot_unref(summary); + } + dc_chatlist_unref(chats); + + // let msglist = dc_get_chat_msgs(ctx, chat_id, 0, 0); + // for i in 0..dc_array_get_cnt(msglist) { + // let msg_id = dc_array_get_id(msglist, i); + // let msg = dc_get_msg(context, msg_id); + // let text = CStr::from_ptr(dc_msg_get_text(msg)).unwrap(); + // println!("Message {}: {}\n", i + 1, text.to_str().unwrap()); + // dc_msg_unref(msg); + // } + // dc_array_unref(msglist); + + t1.join().unwrap(); + t2.join().unwrap(); + } +} diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 000000000..c51666e8f --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1 @@ +edition = "2018" \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 0f025c1e0..d99ef180e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -69,146 +69,3 @@ pub mod types; pub mod constants; pub use self::constants::*; - -#[cfg(test)] -mod tests { - use std::ffi::{CStr, CString}; - use std::ptr::NonNull; - - use crate::constants::Event; - use crate::dc_chat::*; - use crate::dc_chatlist::*; - use crate::dc_configure::*; - use crate::dc_contact::*; - use crate::dc_context::*; - use crate::dc_job::{ - dc_perform_imap_fetch, dc_perform_imap_idle, dc_perform_imap_jobs, dc_perform_smtp_idle, - dc_perform_smtp_jobs, - }; - use crate::dc_lot::*; - - fn cb(_ctx: *mut dc_context_t, event: Event, data1: u64, data2: u64) -> u64 { - println!("[{:?}]", event); - - match event { - Event::HTTP_GET => { - let url = unsafe { CStr::from_ptr(data1 as *const _).to_str().unwrap() }; - - match reqwest::get(url) { - Ok(ref mut res) => { - let c_res = CString::new(res.text().unwrap()).unwrap(); - // need to use strdup to allocate the result with malloc - // so it can be `free`d later. - unsafe { libc::strdup(c_res.as_ptr()) as u64 } - } - Err(err) => { - println!("failed to download: {}: {:?}", url, err); - 0 - } - } - } - Event::INFO | Event::WARNING | Event::ERROR => { - println!( - " {}", - unsafe { CStr::from_ptr(data2 as *const _) } - .to_str() - .unwrap() - ); - 0 - } - _ => 0, - } - } - - struct Wrapper(NonNull); - - unsafe impl std::marker::Send for Wrapper {} - unsafe impl std::marker::Sync for Wrapper {} - - #[test] - fn test_basics() { - unsafe { - let mut ctx = dc_context_new(Some(cb), std::ptr::null_mut(), std::ptr::null_mut()); - let info = dc_get_info(ctx); - let info_s = CStr::from_ptr(info); - println!("info: {}", info_s.to_str().unwrap()); - - let sendable_ctx = Wrapper(NonNull::new(ctx).unwrap()); - let t1 = std::thread::spawn(move || loop { - dc_perform_imap_jobs(sendable_ctx.0.as_ptr()); - dc_perform_imap_fetch(sendable_ctx.0.as_ptr()); - dc_perform_imap_idle(sendable_ctx.0.as_ptr()); - }); - - let sendable_ctx = Wrapper(NonNull::new(ctx).unwrap()); - let t2 = std::thread::spawn(move || loop { - dc_perform_smtp_jobs(sendable_ctx.0.as_ptr()); - dc_perform_smtp_idle(sendable_ctx.0.as_ptr()); - }); - - let dbfile = CString::new("../deltachat-core/build/hello2.db").unwrap(); - println!("opening dir"); - dc_open(ctx, dbfile.as_ptr(), std::ptr::null()); - - if dc_is_configured(ctx) == 0 { - println!("configuring"); - dc_set_config( - ctx, - CString::new("addr").unwrap().as_ptr(), - CString::new("d@testrun.org").unwrap().as_ptr(), - ); - dc_set_config( - ctx, - CString::new("mail_pw").unwrap().as_ptr(), - CString::new("***").unwrap().as_ptr(), - ); - dc_configure(ctx); - } - - std::thread::sleep_ms(4000); - - let email = CString::new("dignifiedquire@gmail.com").unwrap(); - println!("sending a message"); - let contact_id = dc_create_contact(ctx, std::ptr::null(), email.as_ptr()); - let chat_id = dc_create_chat_by_contact_id(ctx, contact_id); - let msg_text = CString::new("Hi, here is my first message!").unwrap(); - dc_send_text_msg(ctx, chat_id, msg_text.as_ptr()); - - println!("fetching chats.."); - let chats = dc_get_chatlist(ctx, 0, std::ptr::null(), 0); - - for i in 0..dc_chatlist_get_cnt(chats) { - let summary = dc_chatlist_get_summary(chats, 0, std::ptr::null_mut()); - let text1 = dc_lot_get_text1(summary); - let text2 = dc_lot_get_text2(summary); - - let text1_s = if !text1.is_null() { - Some(CStr::from_ptr(text1)) - } else { - None - }; - let text2_s = if !text2.is_null() { - Some(CStr::from_ptr(text2)) - } else { - None - }; - println!("chat: {} - {:?} - {:?}", i, text1_s, text2_s,); - dc_lot_unref(summary); - } - dc_chatlist_unref(chats); - - // let msglist = dc_get_chat_msgs(ctx, chat_id, 0, 0); - // for i in 0..dc_array_get_cnt(msglist) { - // let msg_id = dc_array_get_id(msglist, i); - // let msg = dc_get_msg(context, msg_id); - // let text = CStr::from_ptr(dc_msg_get_text(msg)).unwrap(); - // println!("Message {}: {}\n", i + 1, text.to_str().unwrap()); - // dc_msg_unref(msg); - // } - // dc_array_unref(msglist); - - t1.join().unwrap(); - t2.join().unwrap(); - } - } -}