From 99ca582e251084f0f3df7241a21aeb720ed294d9 Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Thu, 30 Jul 2020 18:39:12 +0200 Subject: [PATCH] implement ffi calls --- deltachat-ffi/src/lib.rs | 303 +++++++++++++++++++++++++++++++++++++++ src/accounts.rs | 56 ++++---- 2 files changed, 331 insertions(+), 28 deletions(-) diff --git a/deltachat-ffi/src/lib.rs b/deltachat-ffi/src/lib.rs index 0a1768c7c..d5ddf47d9 100644 --- a/deltachat-ffi/src/lib.rs +++ b/deltachat-ffi/src/lib.rs @@ -24,6 +24,7 @@ use std::time::{Duration, SystemTime}; use async_std::task::{block_on, spawn}; use num_traits::{FromPrimitive, ToPrimitive}; +use deltachat::accounts::Accounts; use deltachat::chat::{ChatId, ChatVisibility, MuteDuration}; use deltachat::constants::DC_MSG_ID_LAST_SPECIAL; use deltachat::contact::{Contact, Origin}; @@ -3335,3 +3336,305 @@ pub unsafe extern "C" fn dc_provider_unref(provider: *mut dc_provider_t) { // currently, there is nothing to free, the provider info is a static object. // this may change once we start localizing string. } + +// -- Accounts + +/// Struct representing a list of deltachat accounts. +pub type dc_accounts_t = Accounts; + +#[no_mangle] +pub unsafe extern "C" fn dc_accounts_new( + os_name: *const libc::c_char, + dbfile: *const libc::c_char, +) -> *mut dc_accounts_t { + setup_panic!(); + + if dbfile.is_null() { + eprintln!("ignoring careless call to dc_accounts_new()"); + return ptr::null_mut(); + } + + let os_name = if os_name.is_null() { + String::from("DcFFI") + } else { + to_string_lossy(os_name) + }; + + let accs = block_on(Accounts::new(os_name, as_path(dbfile).to_path_buf().into())); + + match accs { + Ok(accs) => Box::into_raw(Box::new(accs)), + Err(err) => { + eprintln!("failed to create accounts: {}", err); + ptr::null_mut() + } + } +} + +/// Release the accounts structure. +/// +/// This function releases the memory of the `dc_accounts_t` structure. +#[no_mangle] +pub unsafe extern "C" fn dc_accounts_unref(accounts: *mut dc_accounts_t) { + if accounts.is_null() { + eprintln!("ignoring careless call to dc_accounts_unref()"); + return; + } + let _ = Box::from_raw(accounts); +} + +#[no_mangle] +pub unsafe extern "C" fn dc_accounts_get_account( + accounts: *mut dc_accounts_t, + id: u32, +) -> *mut dc_context_t { + if accounts.is_null() { + eprintln!("ignoring careless call to dc_accounts_get_account()"); + return ptr::null_mut(); + } + + let accounts = &*accounts; + block_on(accounts.get_account(id)) + .map(|ctx| Box::into_raw(Box::new(ctx))) + .unwrap_or_else(std::ptr::null_mut) +} + +#[no_mangle] +pub unsafe extern "C" fn dc_accounts_get_selected_account( + accounts: *mut dc_accounts_t, +) -> *mut dc_context_t { + if accounts.is_null() { + eprintln!("ignoring careless call to dc_accounts_get_selected_account()"); + return ptr::null_mut(); + } + + let accounts = &*accounts; + let ctx = block_on(accounts.get_selected_account()); + Box::into_raw(Box::new(ctx)) +} + +#[no_mangle] +pub unsafe extern "C" fn dc_accounts_select_account( + accounts: *mut dc_accounts_t, + id: u32, +) -> libc::c_int { + if accounts.is_null() { + eprintln!("ignoring careless call to dc_accounts_select_account()"); + return 0; + } + + let accounts = &*accounts; + block_on(accounts.select_account(id)) + .map(|_| 1) + .unwrap_or_else(|_| 0) +} + +#[no_mangle] +pub unsafe extern "C" fn dc_accounts_add_account(accounts: *mut dc_accounts_t) -> u32 { + if accounts.is_null() { + eprintln!("ignoring careless call to dc_accounts_add_account()"); + return 0; + } + + let accounts = &*accounts; + + block_on(accounts.add_account()).unwrap_or_else(|_| 0) +} + +#[no_mangle] +pub unsafe extern "C" fn dc_accounts_remove_account( + accounts: *mut dc_accounts_t, + id: u32, +) -> libc::c_int { + if accounts.is_null() { + eprintln!("ignoring careless call to dc_accounts_remove_account()"); + return 0; + } + + let accounts = &*accounts; + + block_on(accounts.remove_account(id)) + .map(|_| 1) + .unwrap_or_else(|_| 0) +} + +#[no_mangle] +pub unsafe extern "C" fn dc_accounts_migrate_account( + accounts: *mut dc_accounts_t, + dbfile: *const libc::c_char, +) -> u32 { + if accounts.is_null() || dbfile.is_null() { + eprintln!("ignoring careless call to dc_accounts_migrate_account()"); + return 0; + } + + let accounts = &*accounts; + let dbfile = to_string_lossy(dbfile); + + block_on(accounts.migrate_account(async_std::path::PathBuf::from(dbfile))) + .map(|_| 1) + .unwrap_or_else(|_| 0) +} + +#[no_mangle] +pub unsafe extern "C" fn dc_accounts_get_all(accounts: *mut dc_accounts_t) -> *mut dc_array_t { + if accounts.is_null() { + eprintln!("ignoring careless call to dc_accounts_get_all()"); + return ptr::null_mut(); + } + + let accounts = &*accounts; + let list = block_on(accounts.get_all()); + let array: dc_array_t = list.into(); + + Box::into_raw(Box::new(array)) +} + +#[no_mangle] +pub unsafe extern "C" fn dc_accounts_import_account( + accounts: *mut dc_accounts_t, + file: *const libc::c_char, +) -> u32 { + if accounts.is_null() || file.is_null() { + eprintln!("ignoring careless call to dc_accounts_import_account()"); + return 0; + } + + let accounts = &*accounts; + let file = to_string_lossy(file); + block_on(accounts.import_account(async_std::path::PathBuf::from(file))) + .map(|_| 1) + .unwrap_or_else(|_| 0) +} + +#[no_mangle] +pub unsafe extern "C" fn dc_accounts_start_io(accounts: *mut dc_accounts_t) { + if accounts.is_null() { + eprintln!("ignoring careless call to dc_accounts_start_io()"); + return; + } + + let accounts = &*accounts; + block_on(accounts.start_io()); +} + +#[no_mangle] +pub unsafe extern "C" fn dc_accounts_stop_io(accounts: *mut dc_accounts_t) { + if accounts.is_null() { + eprintln!("ignoring careless call to dc_accounts_stop_io()"); + return; + } + + let accounts = &*accounts; + block_on(accounts.stop_io()); +} + +#[no_mangle] +pub unsafe extern "C" fn dc_accounts_maybe_network(accounts: *mut dc_accounts_t) { + if accounts.is_null() { + eprintln!("ignoring careless call to dc_accounts_mabye_network()"); + return; + } + + let accounts = &*accounts; + block_on(accounts.maybe_network()); +} + +#[no_mangle] +pub type dc_accounts_event_emitter_t = deltachat::accounts::EventEmitter; + +#[no_mangle] +pub unsafe extern "C" fn dc_accounts_get_event_emitter( + accounts: *mut dc_accounts_t, +) -> *mut dc_accounts_event_emitter_t { + if accounts.is_null() { + eprintln!("ignoring careless call to dc_accounts_get_event_emitter()"); + return ptr::null_mut(); + } + + let accounts = &*accounts; + let emitter = block_on(accounts.get_event_emitter()); + + Box::into_raw(Box::new(emitter)) +} + +#[no_mangle] +pub unsafe extern "C" fn dc_accounts_event_emitter_unref( + emitter: *mut dc_accounts_event_emitter_t, +) { + if emitter.is_null() { + eprintln!("ignoring careless call to dc_accounts_event_emitter_unref()"); + return; + } + let _ = Box::from_raw(emitter); +} + +#[no_mangle] +pub type dc_accounts_event_t = deltachat::accounts::Event; + +#[no_mangle] +pub unsafe extern "C" fn dc_accounts_event_unref(a: *mut dc_accounts_event_t) { + if a.is_null() { + eprintln!("ignoring careless call to dc_accounts_event_unref()"); + return; + } + + let _ = Box::from_raw(a); +} + +#[no_mangle] +pub unsafe extern "C" fn dc_accounts_event_get_id(event: *mut dc_accounts_event_t) -> libc::c_int { + if event.is_null() { + eprintln!("ignoring careless call to dc_accounts_event_get_id()"); + return 0; + } + + let event = &*event; + event.event.as_id() +} + +#[no_mangle] +pub unsafe extern "C" fn dc_accounts_event_get_data1_int( + event: *mut dc_accounts_event_t, +) -> libc::c_int { + if event.is_null() { + eprintln!("ignoring careless call to dc_accounts_event_get_data1_int()"); + return 0; + } + + dc_event_get_data1_int(&mut (&mut *event).event) +} + +#[no_mangle] +pub unsafe extern "C" fn dc_accounts_event_get_data2_int( + event: *mut dc_accounts_event_t, +) -> libc::c_int { + if event.is_null() { + eprintln!("ignoring careless call to dc_accounts_event_get_data2_int()"); + return 0; + } + + dc_event_get_data2_int(&mut (&mut *event).event) +} + +#[no_mangle] +pub unsafe extern "C" fn dc_accounts_event_get_data2_str( + event: *mut dc_accounts_event_t, +) -> *mut libc::c_char { + if event.is_null() { + eprintln!("ignoring careless call to dc_accounts_event_get_data2_str()"); + return ptr::null_mut(); + } + + dc_event_get_data2_str(&mut (&mut *event).event) +} + +#[no_mangle] +pub unsafe extern "C" fn dc_accounts_event_get_account_id(event: *mut dc_accounts_event_t) -> u32 { + if event.is_null() { + eprintln!("ignoring careless call to dc_accounts_event_get_account_id()"); + return 0; + } + + (&*event).id +} diff --git a/src/accounts.rs b/src/accounts.rs index 266ce0daa..eb6e2f916 100644 --- a/src/accounts.rs +++ b/src/accounts.rs @@ -19,7 +19,7 @@ use crate::error::Result; pub struct Accounts { dir: PathBuf, config: Config, - accounts: Arc>>, + accounts: Arc>>, } impl Accounts { @@ -68,7 +68,7 @@ impl Accounts { } /// Get an account by its `id`: - pub async fn get_account(&self, id: u64) -> Option { + pub async fn get_account(&self, id: u32) -> Option { self.accounts.read().await.get(&id).cloned() } @@ -84,14 +84,14 @@ impl Accounts { } /// Select the given account. - pub async fn select_account(&self, id: u64) -> Result<()> { + pub async fn select_account(&self, id: u32) -> Result<()> { self.config.select_account(id).await?; Ok(()) } /// Add a new account. - pub async fn add_account(&self) -> Result { + pub async fn add_account(&self) -> Result { let os_name = self.config.os_name().await; let account_config = self.config.new_account(&self.dir).await?; @@ -102,7 +102,7 @@ impl Accounts { } /// Remove an account. - pub async fn remove_account(&self, id: u64) -> Result<()> { + pub async fn remove_account(&self, id: u32) -> Result<()> { let ctx = self.accounts.write().await.remove(&id); ensure!(ctx.is_some(), "no account with this id: {}", id); let ctx = ctx.unwrap(); @@ -120,7 +120,7 @@ impl Accounts { } /// Migrate an existing account into this structure. - pub async fn migrate_account(&self, dbfile: PathBuf) -> Result { + pub async fn migrate_account(&self, dbfile: PathBuf) -> Result { let blobdir = Context::derive_blobdir(&dbfile); ensure!( @@ -174,12 +174,12 @@ impl Accounts { } /// Get a list of all account ids. - pub async fn get_all(&self) -> Vec { + pub async fn get_all(&self) -> Vec { self.accounts.read().await.keys().copied().collect() } /// Import a backup using a new account and selects it. - pub async fn import_account(&self, file: PathBuf) -> Result { + pub async fn import_account(&self, file: PathBuf) -> Result { let old_id = self.config.get_selected_account().await; let id = self.add_account().await?; @@ -271,7 +271,7 @@ pub struct EventEmitter(Vec); #[derive(Debug)] struct EmitterWrapper { - id: u64, + id: u32, emitter: crate::events::EventEmitter, done: AtomicBool, } @@ -279,7 +279,7 @@ struct EmitterWrapper { #[derive(Debug, Clone, PartialEq, Eq)] pub struct Event { /// The id of the account that emitted the event. - pub id: u64, + pub id: u32, pub event: crate::events::Event, } @@ -296,8 +296,8 @@ pub struct Config { struct InnerConfig { pub os_name: String, /// The currently selected account. - pub selected_account: u64, - pub next_id: u64, + pub selected_account: u32, + pub next_id: u32, pub accounts: Vec, } @@ -309,7 +309,7 @@ impl Config { os_name, accounts: Vec::new(), selected_account: 0, - next_id: 0, + next_id: 1, })), }; @@ -343,7 +343,7 @@ impl Config { }) } - pub async fn load_accounts(&self) -> Result> { + pub async fn load_accounts(&self) -> Result> { let cfg = &*self.inner.read().await; let mut accounts = HashMap::with_capacity(cfg.accounts.len()); for account_config in &cfg.accounts { @@ -380,7 +380,7 @@ impl Config { } /// Removes an existing acccount entirely. - pub async fn remove_account(&self, id: u64) -> Result<()> { + pub async fn remove_account(&self, id: u32) -> Result<()> { { let inner = &mut *self.inner.write().await; if let Some(idx) = inner.accounts.iter().position(|e| e.id == id) { @@ -396,7 +396,7 @@ impl Config { self.sync().await } - pub async fn get_account(&self, id: u64) -> Option { + pub async fn get_account(&self, id: u32) -> Option { self.inner .read() .await @@ -406,11 +406,11 @@ impl Config { .cloned() } - pub async fn get_selected_account(&self) -> u64 { + pub async fn get_selected_account(&self) -> u32 { self.inner.read().await.selected_account } - pub async fn select_account(&self, id: u64) -> Result<()> { + pub async fn select_account(&self, id: u32) -> Result<()> { { let inner = &mut *self.inner.write().await; ensure!( @@ -430,7 +430,7 @@ impl Config { #[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] pub struct AccountConfig { /// Unique id. - pub id: u64, + pub id: u32, /// Display name pub name: String, /// Root directory for all data for this account. @@ -458,7 +458,7 @@ mod tests { let accounts2 = Accounts::open(p).await.unwrap(); assert_eq!(accounts1.accounts.read().await.len(), 1); - assert_eq!(accounts1.config.get_selected_account().await, 0); + assert_eq!(accounts1.config.get_selected_account().await, 1); assert_eq!(accounts1.dir, accounts2.dir); assert_eq!( @@ -479,18 +479,18 @@ mod tests { let accounts = Accounts::new("my_os".into(), p.clone()).await.unwrap(); assert_eq!(accounts.accounts.read().await.len(), 1); - assert_eq!(accounts.config.get_selected_account().await, 0); + assert_eq!(accounts.config.get_selected_account().await, 1); let id = accounts.add_account().await.unwrap(); - assert_eq!(id, 1); + assert_eq!(id, 2); assert_eq!(accounts.config.get_selected_account().await, id); assert_eq!(accounts.accounts.read().await.len(), 2); - accounts.select_account(0).await.unwrap(); - assert_eq!(accounts.config.get_selected_account().await, 0); - - accounts.remove_account(0).await.unwrap(); + accounts.select_account(1).await.unwrap(); assert_eq!(accounts.config.get_selected_account().await, 1); + + accounts.remove_account(1).await.unwrap(); + assert_eq!(accounts.config.get_selected_account().await, 2); assert_eq!(accounts.accounts.read().await.len(), 1); } @@ -501,7 +501,7 @@ mod tests { let accounts = Accounts::new("my_os".into(), p.clone()).await.unwrap(); assert_eq!(accounts.accounts.read().await.len(), 1); - assert_eq!(accounts.config.get_selected_account().await, 0); + assert_eq!(accounts.config.get_selected_account().await, 1); let extern_dbfile: PathBuf = dir.path().join("other").into(); let ctx = Context::new("my_os".into(), extern_dbfile.clone()) @@ -518,7 +518,7 @@ mod tests { .await .unwrap(); assert_eq!(accounts.accounts.read().await.len(), 2); - assert_eq!(accounts.config.get_selected_account().await, 1); + assert_eq!(accounts.config.get_selected_account().await, 2); let ctx = accounts.get_selected_account().await; assert_eq!(