#![doc(html_logo_url =
"https://raw.githubusercontent.com/maidsafe/QA/master/Images/maidsafe_logo.png",
html_favicon_url = "http://maidsafe.net/img/favicon.ico",
html_root_url = "http://maidsafe.github.io/safe_authenticator")]
#![forbid(exceeding_bitshifts, mutable_transmutes, no_mangle_const_items,
unknown_crate_types, warnings)]
#![deny(bad_style, deprecated, improper_ctypes, missing_docs,
non_shorthand_field_patterns, overflowing_literals, plugin_as_library,
private_no_mangle_fns, private_no_mangle_statics, stable_features,
unconditional_recursion, unknown_lints, unused,
unused_allocation, unused_attributes, unused_comparisons, unused_features,
unused_parens, while_true)]
#![warn(trivial_casts, trivial_numeric_casts, unused_extern_crates, unused_import_braces,
unused_qualifications, unused_results)]
#![allow(box_pointers, missing_copy_implementations, missing_debug_implementations,
variant_size_differences)]
#![cfg_attr(feature="cargo-clippy", deny(clippy, unicode_not_nfc, wrong_pub_self_convention,
option_unwrap_used))]
#![cfg_attr(feature="cargo-clippy", allow(implicit_hasher, too_many_arguments, use_debug))]
extern crate config_file_handler;
#[macro_use]
extern crate ffi_utils;
extern crate futures;
#[macro_use]
extern crate log;
extern crate maidsafe_utilities;
extern crate routing;
extern crate serde;
#[macro_use]
extern crate serde_derive;
extern crate rust_sodium;
#[macro_use]
extern crate safe_core;
extern crate tiny_keccak;
extern crate tokio_core;
#[macro_use]
extern crate unwrap;
#[cfg(any(test, feature = "testing"))]
extern crate rand;
pub mod ffi;
pub use ffi::*;
pub use ffi::apps::*;
pub use ffi::ipc::*;
pub use ffi::logging::*;
mod access_container;
mod app_auth;
mod app_container;
mod config;
mod errors;
mod ipc;
mod revocation;
mod std_dirs;
#[cfg(any(test, feature = "testing"))]
#[macro_use]
pub mod test_utils;
#[cfg(test)]
mod tests;
pub use self::errors::AuthError;
use futures::Future;
use futures::stream::Stream;
use futures::sync::mpsc;
use maidsafe_utilities::thread::{self, Joiner};
use safe_core::{Client, CoreError, CoreMsg, CoreMsgTx, FutureExt, NetworkEvent, NetworkTx,
event_loop};
#[cfg(feature = "use-mock-routing")]
use safe_core::MockRouting;
use std::sync::Mutex;
use std::sync::mpsc::sync_channel;
use tokio_core::reactor::{Core, Handle};
pub type AuthFuture<T> = Future<Item = T, Error = AuthError>;
macro_rules! try_tx {
($result:expr, $tx:ident) => {
match $result {
Ok(res) => res,
Err(e) => { return unwrap!($tx.send(Err((None, AuthError::from(e))))); }
}
}
}
pub struct Authenticator {
pub core_tx: Mutex<CoreMsgTx<()>>,
_core_joiner: Joiner,
}
impl Authenticator {
pub fn send<F>(&self, f: F) -> Result<(), AuthError>
where
F: FnOnce(&Client<()>) -> Option<Box<Future<Item = (), Error = ()>>> + Send + 'static,
{
let msg = CoreMsg::new(|client, _| f(client));
let core_tx = unwrap!(self.core_tx.lock());
core_tx.unbounded_send(msg).map_err(AuthError::from)
}
pub fn create_acc<S, N>(
locator: S,
password: S,
invitation: S,
disconnect_notifier: N,
) -> Result<Self, AuthError>
where
N: FnMut() + Send + 'static,
S: Into<String>,
{
let locator = locator.into();
let password = password.into();
let invitation = invitation.into();
Self::create_acc_impl(
move |el_h, core_tx, net_tx| {
Client::registered(&locator, &password, &invitation, el_h, core_tx, net_tx)
},
disconnect_notifier,
)
}
fn create_acc_impl<F: 'static + Send, N>(
create_client_fn: F,
mut disconnect_notifier: N,
) -> Result<Self, AuthError>
where
N: FnMut() + Send + 'static,
F: FnOnce(Handle, CoreMsgTx<()>, NetworkTx) -> Result<Client<()>, CoreError>,
{
let (tx, rx) = sync_channel(0);
let joiner = thread::named("Core Event Loop", move || {
let el = try_tx!(Core::new(), tx);
let el_h = el.handle();
let (core_tx, core_rx) = mpsc::unbounded();
let core_tx2 = core_tx.clone();
let (net_tx, net_rx) = mpsc::unbounded::<NetworkEvent>();
let net_obs_fut = net_rx
.then(move |net_event| {
if let Ok(NetworkEvent::Disconnected) = net_event {
disconnect_notifier();
}
ok!(())
})
.for_each(|_| Ok(()));
el_h.spawn(net_obs_fut);
let client = try_tx!(create_client_fn(el_h, core_tx.clone(), net_tx), tx);
unwrap!(core_tx.unbounded_send(CoreMsg::new(move |client, &()| {
std_dirs::create(client)
.map_err(|error| AuthError::AccountContainersCreation(error.to_string()))
.then(move |res| {
match res {
Ok(_) => unwrap!(tx.send(Ok(core_tx2))),
Err(error) => unwrap!(tx.send(Err((Some(core_tx2), error)))),
}
Ok(())
})
.into_box()
.into()
})));
event_loop::run(el, &client, &(), core_rx);
});
let core_tx = match rx.recv()? {
Ok(core_tx) => core_tx,
Err((None, e)) => return Err(e),
Err((Some(core_tx), e)) => {
core_tx.unbounded_send(CoreMsg::build_terminator())?;
return Err(e);
}
};
Ok(Authenticator {
core_tx: Mutex::new(core_tx),
_core_joiner: joiner,
})
}
pub fn login<S, N>(locator: S, password: S, disconnect_notifier: N) -> Result<Self, AuthError>
where
S: Into<String>,
N: FnMut() + Send + 'static,
{
let locator = locator.into();
let password = password.into();
Self::login_impl(
move |el_h, core_tx, net_tx| Client::login(&locator, &password, el_h, core_tx, net_tx),
disconnect_notifier,
)
}
pub fn login_impl<F: Send + 'static, N>(
create_client_fn: F,
mut disconnect_notifier: N,
) -> Result<Self, AuthError>
where
F: FnOnce(Handle, CoreMsgTx<()>, NetworkTx) -> Result<Client<()>, CoreError>,
N: FnMut() + Send + 'static,
{
let (tx, rx) = sync_channel(0);
let joiner = thread::named("Core Event Loop", move || {
let el = try_tx!(Core::new(), tx);
let el_h = el.handle();
let (core_tx, core_rx) = mpsc::unbounded();
let (net_tx, net_rx) = mpsc::unbounded::<NetworkEvent>();
let core_tx_clone = core_tx.clone();
let net_obs_fut = net_rx
.then(move |net_event| {
if let Ok(NetworkEvent::Disconnected) = net_event {
disconnect_notifier();
}
ok!(())
})
.for_each(|_| Ok(()));
el_h.spawn(net_obs_fut);
let client = try_tx!(create_client_fn(el_h, core_tx_clone, net_tx), tx);
if !try_tx!(client.std_dirs_created(), tx) {
let tx2 = tx.clone();
let core_tx2 = core_tx.clone();
let core_tx3 = core_tx.clone();
unwrap!(core_tx.unbounded_send(CoreMsg::new(move |client, &()| {
std_dirs::create(client)
.map(move |()| {
unwrap!(tx.send(Ok(core_tx2)));
})
.map_err(move |e| {
unwrap!(tx2.send(Err((Some(core_tx3), e))));
})
.into_box()
.into()
})));
} else {
unwrap!(tx.send(Ok(core_tx)));
}
event_loop::run(el, &client, &(), core_rx);
});
let core_tx = match rx.recv()? {
Ok(core_tx) => core_tx,
Err((None, e)) => return Err(e),
Err((Some(core_tx), e)) => {
core_tx.unbounded_send(CoreMsg::build_terminator())?;
return Err(e);
}
};
Ok(Authenticator {
core_tx: Mutex::new(core_tx),
_core_joiner: joiner,
})
}
}
#[cfg(feature = "use-mock-routing")]
impl Authenticator {
#[allow(unused)]
fn login_with_hook<F, S, N>(
locator: S,
password: S,
disconnect_notifier: N,
routing_wrapper_fn: F,
) -> Result<Self, AuthError>
where
S: Into<String>,
F: Fn(MockRouting) -> MockRouting + Send + 'static,
N: FnMut() + Send + 'static,
{
let locator = locator.into();
let password = password.into();
Self::login_impl(
move |el_h, core_tx, net_tx| {
Client::login_with_hook(
&locator,
&password,
el_h,
core_tx,
net_tx,
routing_wrapper_fn,
)
},
disconnect_notifier,
)
}
#[allow(unused)]
fn create_acc_with_hook<F, S, N>(
locator: S,
password: S,
invitation: S,
disconnect_notifier: N,
routing_wrapper_fn: F,
) -> Result<Self, AuthError>
where
N: FnMut() + Send + 'static,
F: Fn(MockRouting) -> MockRouting + Send + 'static,
S: Into<String>,
{
let locator = locator.into();
let password = password.into();
let invitation = invitation.into();
Self::create_acc_impl(
move |el_h, core_tx_clone, net_tx| {
Client::registered_with_hook(
&locator,
&password,
&invitation,
el_h,
core_tx_clone,
net_tx,
routing_wrapper_fn,
)
},
disconnect_notifier,
)
}
}
impl Drop for Authenticator {
fn drop(&mut self) {
debug!("Authenticator is now being dropped.");
let core_tx = unwrap!(self.core_tx.lock());
let msg = CoreMsg::build_terminator();
if let Err(e) = core_tx.unbounded_send(msg) {
info!("Unexpected error in drop: {:?}", e);
}
}
}