pub mod apps;
pub mod logging;
pub mod ipc;
use Authenticator;
use config_file_handler;
use errors::AuthError;
use ffi_utils::{FFI_RESULT_OK, FfiResult, OpaqueCtx, catch_unwind_cb, from_c_str};
use futures::Future;
use safe_core::FutureExt;
use safe_core::ffi::AccountInfo as FfiAccountInfo;
use std::ffi::{CStr, CString, OsStr};
use std::os::raw::{c_char, c_void};
#[no_mangle]
pub unsafe extern "C" fn create_acc(
account_locator: *const c_char,
account_password: *const c_char,
invitation: *const c_char,
user_data: *mut c_void,
o_disconnect_notifier_cb: extern "C" fn(user_data: *mut c_void),
o_cb: extern "C" fn(user_data: *mut c_void,
result: *const FfiResult,
authenticator: *mut Authenticator),
) {
let user_data = OpaqueCtx(user_data);
catch_unwind_cb(user_data, o_cb, || -> Result<_, AuthError> {
trace!("Authenticator - create a client account.");
let acc_locator = from_c_str(account_locator)?;
let acc_password = from_c_str(account_password)?;
let invitation = from_c_str(invitation)?;
let authenticator =
Authenticator::create_acc(acc_locator, acc_password, invitation, move || {
o_disconnect_notifier_cb(user_data.0)
})?;
o_cb(
user_data.0,
FFI_RESULT_OK,
Box::into_raw(Box::new(authenticator)),
);
Ok(())
})
}
#[no_mangle]
pub unsafe extern "C" fn login(
account_locator: *const c_char,
account_password: *const c_char,
user_data: *mut c_void,
o_disconnect_notifier_cb: unsafe extern "C" fn(user_data: *mut c_void),
o_cb: extern "C" fn(user_data: *mut c_void,
result: *const FfiResult,
authenticaor: *mut Authenticator),
) {
let user_data = OpaqueCtx(user_data);
catch_unwind_cb(user_data, o_cb, || -> Result<_, AuthError> {
trace!("Authenticator - log in a registered client.");
let acc_locator = from_c_str(account_locator)?;
let acc_password = from_c_str(account_password)?;
let authenticator = Authenticator::login(acc_locator, acc_password, move || {
o_disconnect_notifier_cb(user_data.0)
})?;
o_cb(
user_data.0,
FFI_RESULT_OK,
Box::into_raw(Box::new(authenticator)),
);
Ok(())
})
}
#[no_mangle]
pub unsafe extern "C" fn auth_reconnect(
auth: *mut Authenticator,
user_data: *mut c_void,
o_cb: extern "C" fn(user_data: *mut c_void, result: *const FfiResult),
) {
catch_unwind_cb(user_data, o_cb, || -> Result<_, AuthError> {
let user_data = OpaqueCtx(user_data);
(*auth).send(move |client| {
try_cb!(
client.restart_routing().map_err(AuthError::from),
user_data.0,
o_cb
);
o_cb(user_data.0, FFI_RESULT_OK);
None
})
})
}
#[no_mangle]
pub unsafe extern "C" fn auth_account_info(
auth: *mut Authenticator,
user_data: *mut c_void,
o_cb: extern "C" fn(user_data: *mut c_void,
result: *const FfiResult,
account_info: *const FfiAccountInfo),
) {
catch_unwind_cb(user_data, o_cb, || -> Result<_, AuthError> {
let user_data = OpaqueCtx(user_data);
(*auth).send(move |client| {
client
.get_account_info()
.map(move |acc_info| {
let ffi_acc = FfiAccountInfo {
mutations_done: acc_info.mutations_done,
mutations_available: acc_info.mutations_available,
};
o_cb(user_data.0, FFI_RESULT_OK, &ffi_acc);
})
.map_err(move |e| {
call_result_cb!(Err::<(), _>(AuthError::from(e)), user_data, o_cb);
})
.into_box()
.into()
})
})
}
#[no_mangle]
pub unsafe extern "C" fn auth_exe_file_stem(
user_data: *mut c_void,
o_cb: extern "C" fn(user_data: *mut c_void,
result: *const FfiResult,
filename: *const c_char),
) {
catch_unwind_cb(user_data, o_cb, || -> Result<_, AuthError> {
if let Ok(path) = config_file_handler::exe_file_stem()?.into_string() {
let path_c_str = CString::new(path)?;
o_cb(user_data, FFI_RESULT_OK, path_c_str.as_ptr());
} else {
call_result_cb!(
Err::<(), _>(AuthError::from(
"config_file_handler returned invalid string",
)),
user_data,
o_cb
);
}
Ok(())
});
}
#[no_mangle]
pub unsafe extern "C" fn auth_set_additional_search_path(
new_path: *const c_char,
user_data: *mut c_void,
o_cb: extern "C" fn(user_data: *mut c_void, result: *const FfiResult),
) {
catch_unwind_cb(user_data, o_cb, || -> Result<_, AuthError> {
let new_path = CStr::from_ptr(new_path).to_str()?;
config_file_handler::set_additional_search_path(OsStr::new(new_path));
o_cb(user_data, FFI_RESULT_OK);
Ok(())
});
}
#[no_mangle]
pub unsafe extern "C" fn auth_free(auth: *mut Authenticator) {
let _ = Box::from_raw(auth);
}
#[cfg(test)]
mod tests {
use super::*;
use Authenticator;
use ffi_utils::test_utils::call_1;
use routing::ImmutableData;
use safe_core::ffi::AccountInfo;
use safe_core::utils;
use std::ffi::CString;
use std::os::raw::c_void;
#[test]
fn create_account_and_login() {
let acc_locator = unwrap!(CString::new(unwrap!(utils::generate_random_string(10))));
let acc_password = unwrap!(CString::new(unwrap!(utils::generate_random_string(10))));
let invitation = unwrap!(CString::new(unwrap!(utils::generate_random_string(10))));
{
let auth_h: *mut Authenticator = unsafe {
unwrap!(call_1(|ud, cb| {
create_acc(
acc_locator.as_ptr(),
acc_password.as_ptr(),
invitation.as_ptr(),
ud,
disconnect_cb,
cb,
)
}))
};
assert!(!auth_h.is_null());
unsafe { auth_free(auth_h) };
}
{
let auth_h: *mut Authenticator = unsafe {
unwrap!(call_1(|ud, cb| {
login(
acc_locator.as_ptr(),
acc_password.as_ptr(),
ud,
disconnect_cb,
cb,
)
}))
};
assert!(!auth_h.is_null());
unsafe { auth_free(auth_h) };
}
extern "C" fn disconnect_cb(_user_data: *mut c_void) {
panic!("Disconnect occurred")
}
}
#[cfg(all(test, feature = "use-mock-routing"))]
#[test]
fn network_status_callback() {
use ffi_utils::test_utils::{UserData, call_0, call_1_with_custom,
send_via_user_data_custom};
use std::time::Duration;
use std::sync::mpsc::{self, Receiver, Sender};
let acc_locator = unwrap!(CString::new(unwrap!(utils::generate_random_string(10))));
let acc_password = unwrap!(CString::new(unwrap!(utils::generate_random_string(10))));
let invitation = unwrap!(CString::new(unwrap!(utils::generate_random_string(10))));
{
let (tx, rx): (Sender<()>, Receiver<()>) = mpsc::channel();
let mut custom_ud: UserData = Default::default();
let ptr: *const _ = &tx;
custom_ud.custom = ptr as *mut c_void;
let auth: *mut Authenticator = unsafe {
unwrap!(call_1_with_custom(&mut custom_ud, |ud, cb| {
create_acc(
acc_locator.as_ptr(),
acc_password.as_ptr(),
invitation.as_ptr(),
ud,
disconnect_cb,
cb,
)
}))
};
unsafe {
unwrap!((*auth).send(move |client| {
client.simulate_network_disconnect();
None
}));
}
unwrap!(rx.recv_timeout(Duration::from_secs(15)));
unsafe { unwrap!(call_0(|ud, cb| auth_reconnect(auth, ud, cb))) };
let result = rx.recv_timeout(Duration::from_secs(1));
match result {
Err(_) => (),
_ => panic!("Disconnect callback was called"),
}
unsafe { unwrap!(call_0(|ud, cb| auth_reconnect(auth, ud, cb))) };
unwrap!(rx.recv_timeout(Duration::from_secs(15)));
let result = rx.recv_timeout(Duration::from_secs(1));
match result {
Err(_) => (),
_ => panic!("Disconnect callback was called"),
}
unsafe { auth_free(auth) };
}
extern "C" fn disconnect_cb(user_data: *mut c_void) {
unsafe {
send_via_user_data_custom(user_data, ());
}
}
}
#[test]
fn account_info() {
let acc_locator = unwrap!(CString::new(unwrap!(utils::generate_random_string(10))));
let acc_password = unwrap!(CString::new(unwrap!(utils::generate_random_string(10))));
let invitation = unwrap!(CString::new(unwrap!(utils::generate_random_string(10))));
let auth: *mut Authenticator = unsafe {
unwrap!(call_1(|ud, cb| {
create_acc(
acc_locator.as_ptr(),
acc_password.as_ptr(),
invitation.as_ptr(),
ud,
disconnect_cb,
cb,
)
}))
};
let orig_stats: AccountInfo =
unsafe { unwrap!(call_1(|ud, cb| auth_account_info(auth, ud, cb))) };
assert!(orig_stats.mutations_available > 0);
unsafe {
unwrap!((*auth).send(move |client| {
client
.put_idata(ImmutableData::new(vec![1, 2, 3]))
.map_err(move |_| ())
.into_box()
.into()
}));
}
let stats: AccountInfo =
unsafe { unwrap!(call_1(|ud, cb| auth_account_info(auth, ud, cb))) };
assert_eq!(stats.mutations_done, orig_stats.mutations_done + 1);
assert_eq!(
stats.mutations_available,
orig_stats.mutations_available - 1
);
unsafe { auth_free(auth) };
}
extern "C" fn disconnect_cb(_user_data: *mut c_void) {
panic!("Disconnect occurred")
}
}