use super::AuthError;
use crate::app_auth::{app_state, AppState};
use crate::client::AuthClient;
use crate::config;
use bincode::deserialize;
use ffi_utils::StringError;
use log::trace;
use safe_core::core_structs::{UserMetadata, METADATA_KEY};
use safe_core::ffi::ipc::resp::MetadataResponse as FfiUserMetadata;
use safe_core::ipc::req::{IpcReq, ShareMDataReq};
use safe_core::ipc::resp::IpcResp;
use safe_core::ipc::{self, IpcError, IpcMsg};
use safe_core::{Client, CoreError};
use safe_nd::Error as SndError;
use std::ffi::CString;
use xor_name::XorName;
#[allow(clippy::type_complexity)]
pub async fn decode_ipc_msg(
client: &AuthClient,
msg: IpcMsg,
) -> Result<Result<IpcMsg, (i32, String, CString)>, AuthError> {
match msg {
IpcMsg::Req {
request: IpcReq::Auth(auth_req),
req_id,
} => {
Ok(Ok(IpcMsg::Req {
req_id,
request: IpcReq::Auth(auth_req),
}))
}
IpcMsg::Req {
request: IpcReq::Unregistered(extra_data),
req_id,
} => Ok(Ok(IpcMsg::Req {
req_id,
request: IpcReq::Unregistered(extra_data),
})),
IpcMsg::Req {
request: IpcReq::ShareMData(share_mdata_req),
req_id,
} => Ok(Ok(IpcMsg::Req {
req_id,
request: IpcReq::ShareMData(share_mdata_req),
})),
IpcMsg::Req {
request: IpcReq::Containers(cont_req),
req_id,
} => {
trace!("Handling IpcReq::Containers({:?})", cont_req);
let app_id = cont_req.app.id.clone();
let c2 = client.clone();
let (_config_version, config) = config::list_apps(client).await?;
let app_state = app_state(&c2, &config, &app_id).await?;
match app_state {
AppState::Authenticated => Ok(Ok(IpcMsg::Req {
req_id,
request: IpcReq::Containers(cont_req),
})),
AppState::Revoked | AppState::NotAuthenticated => {
let error_code = safe_core::ffi::error_codes::ERR_UNKNOWN_APP;
let description = AuthError::from(IpcError::UnknownApp).to_string();
let response = IpcMsg::Resp {
response: IpcResp::Auth(Err(IpcError::UnknownApp)),
req_id,
};
let encoded_response = encode_response(&response)?;
Ok(Err((error_code, description, encoded_response)))
}
}
}
IpcMsg::Resp { .. } | IpcMsg::Revoked { .. } | IpcMsg::Err(..) => {
Err(AuthError::IpcError(IpcError::InvalidMsg))
}
}
}
pub fn encode_response(msg: &IpcMsg) -> Result<CString, IpcError> {
let response = ipc::encode_msg(msg)?;
Ok(CString::new(response).map_err(StringError::from)?)
}
enum ShareMDataError {
InvalidOwner(XorName, u64),
InvalidMetadata,
}
pub async fn decode_share_mdata_req(
client: &AuthClient,
req: &ShareMDataReq,
) -> Result<Vec<Option<FfiUserMetadata>>, AuthError> {
let user = client.public_key().await;
let num_mdata = req.mdata.len();
let mut results = Vec::with_capacity(num_mdata);
for mdata in &req.mdata {
let client = client.clone();
let name = mdata.name;
let type_tag = mdata.type_tag;
let shell = client.get_seq_mdata_shell(name, type_tag).await?;
if *shell.owner() == user {
let metadata = match client
.get_seq_mdata_value(name, type_tag, METADATA_KEY.into())
.await
{
Ok(value) => Ok(deserialize::<UserMetadata>(&value.data)
.map_err(|_| ShareMDataError::InvalidMetadata)
.and_then(
move |metadata| match metadata.into_md_response(name, type_tag) {
Ok(meta) => Ok(meta),
Err(_) => Err(ShareMDataError::InvalidMetadata),
},
)),
Err(CoreError::DataError(SndError::NoSuchEntry)) => {
let user_metadata = UserMetadata {
name: None,
description: None,
};
let user_md_response = user_metadata
.into_md_response(name, type_tag)
.map_err(|_| ShareMDataError::InvalidMetadata);
Ok(user_md_response)
}
Err(error) => Err(error),
}?;
results.push(metadata)
} else {
results.push(Err(ShareMDataError::InvalidOwner(name, type_tag)))
}
}
let mut metadata_cont = Vec::with_capacity(num_mdata);
let mut invalids = Vec::with_capacity(num_mdata);
for result in results {
match result {
Ok(metadata) => metadata_cont.push(Some(metadata)),
Err(ShareMDataError::InvalidMetadata) => metadata_cont.push(None),
Err(ShareMDataError::InvalidOwner(name, type_tag)) => invalids.push((name, type_tag)),
}
}
if invalids.is_empty() {
Ok(metadata_cont)
} else {
Err(AuthError::IpcError(IpcError::InvalidOwner(invalids)))
}
}