#![allow(unknown_lints)]
#![warn(rust_2018_idioms)]
#![allow(clippy::redundant_closure)]
use ffi_support::{
define_bytebuffer_destructor, define_handle_map_deleter, define_string_destructor, ByteBuffer,
ConcurrentHandleMap, ExternError, FfiStr,
};
use fxa_client::{
device::{Capability as DeviceCapability, CommandFetchReason, PushSubscription},
ffi::{from_protobuf_ptr, AuthorizationParameters, MetricsParams},
migrator::MigrationState,
msg_types, FirefoxAccount,
};
use std::os::raw::c_char;
use url::Url;
lazy_static::lazy_static! {
static ref ACCOUNTS: ConcurrentHandleMap<FirefoxAccount> = ConcurrentHandleMap::new();
}
#[no_mangle]
pub extern "C" fn fxa_new(
content_url: FfiStr<'_>,
client_id: FfiStr<'_>,
redirect_uri: FfiStr<'_>,
token_server_url_override: FfiStr<'_>,
err: &mut ExternError,
) -> u64 {
log::debug!("fxa_new");
ACCOUNTS.insert_with_output(err, || {
let content_url = content_url.as_str();
let client_id = client_id.as_str();
let redirect_uri = redirect_uri.as_str();
let token_server_url_override = token_server_url_override.as_opt_str();
FirefoxAccount::new(
content_url,
client_id,
redirect_uri,
token_server_url_override,
)
})
}
#[no_mangle]
pub extern "C" fn fxa_from_json(json: FfiStr<'_>, err: &mut ExternError) -> u64 {
log::debug!("fxa_from_json");
ACCOUNTS.insert_with_result(err, || FirefoxAccount::from_json(json.as_str()))
}
#[no_mangle]
pub extern "C" fn fxa_to_json(handle: u64, error: &mut ExternError) -> *mut c_char {
log::debug!("fxa_to_json");
ACCOUNTS.call_with_result_mut(error, handle, |fxa| fxa.to_json())
}
#[no_mangle]
pub extern "C" fn fxa_profile(
handle: u64,
ignore_cache: u8,
error: &mut ExternError,
) -> ByteBuffer {
log::debug!("fxa_profile");
ACCOUNTS.call_with_result_mut(error, handle, |fxa| fxa.get_profile(ignore_cache != 0))
}
#[no_mangle]
pub extern "C" fn fxa_get_pairing_authority_url(
handle: u64,
error: &mut ExternError,
) -> *mut c_char {
log::debug!("fxa_get_pairing_authority_url");
ACCOUNTS.call_with_result(error, handle, |fxa| {
fxa.get_pairing_authority_url().map(Url::into_string)
})
}
#[no_mangle]
pub extern "C" fn fxa_get_token_server_endpoint_url(
handle: u64,
error: &mut ExternError,
) -> *mut c_char {
log::debug!("fxa_get_token_server_endpoint_url");
ACCOUNTS.call_with_result(error, handle, |fxa| {
fxa.get_token_server_endpoint_url().map(Url::into_string)
})
}
#[no_mangle]
pub extern "C" fn fxa_get_connection_success_url(
handle: u64,
error: &mut ExternError,
) -> *mut c_char {
log::debug!("fxa_get_connection_success_url");
ACCOUNTS.call_with_result(error, handle, |fxa| {
fxa.get_connection_success_url().map(Url::into_string)
})
}
#[no_mangle]
pub extern "C" fn fxa_get_manage_account_url(
handle: u64,
entrypoint: FfiStr<'_>,
error: &mut ExternError,
) -> *mut c_char {
log::debug!("fxa_get_manage_account_url");
ACCOUNTS.call_with_result_mut(error, handle, |fxa| {
fxa.get_manage_account_url(entrypoint.as_str())
.map(Url::into_string)
})
}
#[no_mangle]
pub extern "C" fn fxa_get_manage_devices_url(
handle: u64,
entrypoint: FfiStr<'_>,
error: &mut ExternError,
) -> *mut c_char {
log::debug!("fxa_get_manage_devices_url");
ACCOUNTS.call_with_result_mut(error, handle, |fxa| {
fxa.get_manage_devices_url(entrypoint.as_str())
.map(Url::into_string)
})
}
#[no_mangle]
pub unsafe extern "C" fn fxa_begin_pairing_flow(
handle: u64,
pairing_url: FfiStr<'_>,
scope: FfiStr<'_>,
entrypoint: FfiStr<'_>,
metrics_params: *const u8,
metrics_params_len: i32,
error: &mut ExternError,
) -> *mut c_char {
log::debug!("fxa_begin_pairing_flow");
ACCOUNTS.call_with_result_mut(error, handle, |fxa| {
let pairing_url = pairing_url.as_str();
let scope = scope.as_str();
let scopes: Vec<&str> = scope.split(' ').collect();
let metrics_params = from_protobuf_ptr::<MetricsParams, msg_types::MetricsParams>(
metrics_params,
metrics_params_len,
)?;
fxa.begin_pairing_flow(
&pairing_url,
&scopes,
entrypoint.as_str(),
Some(metrics_params),
)
})
}
#[no_mangle]
pub unsafe extern "C" fn fxa_begin_oauth_flow(
handle: u64,
scope: FfiStr<'_>,
entrypoint: FfiStr<'_>,
metrics_params: *const u8,
metrics_params_len: i32,
error: &mut ExternError,
) -> *mut c_char {
log::debug!("fxa_begin_oauth_flow");
ACCOUNTS.call_with_result_mut(error, handle, |fxa| {
let scope = scope.as_str();
let scopes: Vec<&str> = scope.split(' ').collect();
let metrics_params = from_protobuf_ptr::<MetricsParams, msg_types::MetricsParams>(
metrics_params,
metrics_params_len,
)?;
fxa.begin_oauth_flow(&scopes, entrypoint.as_str(), Some(metrics_params))
})
}
#[no_mangle]
pub extern "C" fn fxa_complete_oauth_flow(
handle: u64,
code: FfiStr<'_>,
state: FfiStr<'_>,
error: &mut ExternError,
) {
log::debug!("fxa_complete_oauth_flow");
ACCOUNTS.call_with_result_mut(error, handle, |fxa| {
let code = code.as_str();
let state = state.as_str();
fxa.complete_oauth_flow(code, state)
});
}
#[no_mangle]
pub extern "C" fn fxa_migrate_from_session_token(
handle: u64,
session_token: FfiStr<'_>,
k_sync: FfiStr<'_>,
k_xcs: FfiStr<'_>,
copy_session_token: u8,
error: &mut ExternError,
) -> *mut c_char {
log::debug!("fxa_migrate_from_session_token");
ACCOUNTS.call_with_result_mut(error, handle, |fxa| -> fxa_client::Result<String> {
let session_token = session_token.as_str();
let k_sync = k_sync.as_str();
let k_xcs = k_xcs.as_str();
let migration_metrics =
fxa.migrate_from_session_token(session_token, k_sync, k_xcs, copy_session_token != 0)?;
let result = serde_json::to_string(&migration_metrics)?;
Ok(result)
})
}
#[no_mangle]
pub extern "C" fn fxa_is_in_migration_state(handle: u64, error: &mut ExternError) -> u8 {
log::debug!("fxa_is_in_migration_state");
ACCOUNTS.call_with_result(error, handle, |fxa| -> fxa_client::Result<MigrationState> {
Ok(fxa.is_in_migration_state())
})
}
#[no_mangle]
pub extern "C" fn fxa_retry_migrate_from_session_token(
handle: u64,
error: &mut ExternError,
) -> *mut c_char {
log::debug!("fxa_retry_migrate_from_session_token");
ACCOUNTS.call_with_result_mut(error, handle, |fxa| -> fxa_client::Result<String> {
let migration_metrics = fxa.try_migration()?;
let result = serde_json::to_string(&migration_metrics)?;
Ok(result)
})
}
#[no_mangle]
pub extern "C" fn fxa_get_access_token(
handle: u64,
scope: FfiStr<'_>,
ttl: u64,
error: &mut ExternError,
) -> ByteBuffer {
log::debug!("fxa_get_access_token");
ACCOUNTS.call_with_result_mut(error, handle, |fxa| {
let scope = scope.as_str();
let time_left = if ttl > 0 { Some(ttl) } else { None };
fxa.get_access_token(scope, time_left)
})
}
#[no_mangle]
pub extern "C" fn fxa_get_session_token(handle: u64, error: &mut ExternError) -> *mut c_char {
log::debug!("fxa_get_session_token");
ACCOUNTS.call_with_result_mut(error, handle, |fxa| fxa.get_session_token())
}
#[no_mangle]
pub extern "C" fn fxa_check_authorization_status(
handle: u64,
error: &mut ExternError,
) -> ByteBuffer {
log::debug!("fxa_check_authorization_status");
ACCOUNTS.call_with_result_mut(error, handle, |fxa| fxa.check_authorization_status())
}
#[no_mangle]
pub extern "C" fn fxa_clear_access_token_cache(handle: u64, error: &mut ExternError) {
log::debug!("fxa_clear_access_token_cache");
ACCOUNTS.call_with_output_mut(error, handle, |fxa| fxa.clear_access_token_cache())
}
#[no_mangle]
pub extern "C" fn fxa_get_current_device_id(handle: u64, error: &mut ExternError) -> *mut c_char {
log::debug!("fxa_get_current_device_id");
ACCOUNTS.call_with_result_mut(error, handle, |fxa| fxa.get_current_device_id())
}
#[no_mangle]
pub extern "C" fn fxa_set_push_subscription(
handle: u64,
endpoint: FfiStr<'_>,
public_key: FfiStr<'_>,
auth_key: FfiStr<'_>,
error: &mut ExternError,
) {
log::debug!("fxa_set_push_subscription");
ACCOUNTS.call_with_result_mut(error, handle, |fxa| {
let ps = PushSubscription {
endpoint: endpoint.into_string(),
public_key: public_key.into_string(),
auth_key: auth_key.into_string(),
};
fxa.set_push_subscription(&ps).map(|_| ())
})
}
#[no_mangle]
pub extern "C" fn fxa_set_device_name(
handle: u64,
display_name: FfiStr<'_>,
error: &mut ExternError,
) {
log::debug!("fxa_set_device_name");
ACCOUNTS.call_with_result_mut(error, handle, |fxa| {
fxa.set_device_name(display_name.as_str()).map(|_| ())
})
}
#[no_mangle]
pub extern "C" fn fxa_get_devices(
handle: u64,
ignore_cache: u8,
error: &mut ExternError,
) -> ByteBuffer {
log::debug!("fxa_get_devices");
ACCOUNTS.call_with_result_mut(error, handle, |fxa| {
fxa.get_devices(ignore_cache != 0).map(|d| {
let devices = d.into_iter().map(|device| device.into()).collect();
fxa_client::msg_types::Devices { devices }
})
})
}
#[no_mangle]
pub unsafe extern "C" fn fxa_authorize_auth_code(
handle: u64,
auth_params: *const u8,
auth_params_len: i32,
error: &mut ExternError,
) -> *mut c_char {
log::debug!("fxa_authorize_auth_code");
ACCOUNTS.call_with_result_mut(error, handle, |fxa| {
let auth_params = from_protobuf_ptr::<
AuthorizationParameters,
msg_types::AuthorizationParams,
>(auth_params, auth_params_len)?;
fxa.authorize_code_using_session_token(auth_params)
})
}
#[no_mangle]
pub extern "C" fn fxa_handle_session_token_change(
handle: u64,
new_session_token: FfiStr<'_>,
error: &mut ExternError,
) {
log::debug!("fxa_handle_session_token_change");
ACCOUNTS.call_with_result_mut(error, handle, |fxa| {
let new_session_token = new_session_token.as_str();
fxa.handle_session_token_change(new_session_token)
})
}
#[no_mangle]
pub extern "C" fn fxa_poll_device_commands(handle: u64, error: &mut ExternError) -> ByteBuffer {
log::debug!("fxa_poll_device_commands");
ACCOUNTS.call_with_result_mut(error, handle, |fxa| {
fxa.poll_device_commands(CommandFetchReason::Poll)
.map(|cmds| {
let commands = cmds.into_iter().map(|e| e.into()).collect();
fxa_client::msg_types::IncomingDeviceCommands { commands }
})
})
}
#[no_mangle]
pub extern "C" fn fxa_disconnect(handle: u64, error: &mut ExternError) {
log::debug!("fxa_disconnect");
ACCOUNTS.call_with_result_mut(error, handle, |fxa| -> fxa_client::Result<()> {
fxa.disconnect();
Ok(())
})
}
#[no_mangle]
pub extern "C" fn fxa_handle_push_message(
handle: u64,
json_payload: FfiStr<'_>,
error: &mut ExternError,
) -> ByteBuffer {
log::debug!("fxa_handle_push_message");
ACCOUNTS.call_with_result_mut(error, handle, |fxa| {
fxa.handle_push_message(json_payload.as_str()).map(|evs| {
let events = evs.into_iter().map(|e| e.into()).collect();
fxa_client::msg_types::AccountEvents { events }
})
})
}
#[no_mangle]
pub unsafe extern "C" fn fxa_initialize_device(
handle: u64,
name: FfiStr<'_>,
device_type: i32,
capabilities_data: *const u8,
capabilities_len: i32,
error: &mut ExternError,
) {
log::debug!("fxa_initialize_device");
ACCOUNTS.call_with_result_mut(error, handle, |fxa| {
let capabilities =
DeviceCapability::from_protobuf_array_ptr(capabilities_data, capabilities_len)?;
let device_type =
msg_types::device::Type::from_i32(device_type).expect("Unknown device type code");
fxa.initialize_device(name.as_str(), device_type.into(), &capabilities)
})
}
#[no_mangle]
pub unsafe extern "C" fn fxa_ensure_capabilities(
handle: u64,
capabilities_data: *const u8,
capabilities_len: i32,
error: &mut ExternError,
) {
log::debug!("fxa_ensure_capabilities");
ACCOUNTS.call_with_result_mut(error, handle, |fxa| {
let capabilities =
DeviceCapability::from_protobuf_array_ptr(capabilities_data, capabilities_len)?;
fxa.ensure_capabilities(&capabilities)
})
}
#[no_mangle]
pub extern "C" fn fxa_send_tab(
handle: u64,
target_device_id: FfiStr<'_>,
title: FfiStr<'_>,
url: FfiStr<'_>,
error: &mut ExternError,
) {
log::debug!("fxa_send_tab");
let target = target_device_id.as_str();
let title = title.as_str();
let url = url.as_str();
ACCOUNTS.call_with_result_mut(error, handle, |fxa| fxa.send_tab(target, title, url))
}
#[no_mangle]
pub extern "C" fn fxa_get_ecosystem_anon_id(handle: u64, error: &mut ExternError) -> *mut c_char {
log::debug!("fxa_get_ecosystem_anon_id");
ACCOUNTS.call_with_result_mut(error, handle, |fxa| fxa.get_ecosystem_anon_id())
}
define_handle_map_deleter!(ACCOUNTS, fxa_free);
define_string_destructor!(fxa_str_free);
define_bytebuffer_destructor!(fxa_bytebuffer_free);
#[no_mangle]
pub extern "C" fn fxa_gather_telemetry(handle: u64, error: &mut ExternError) -> *mut c_char {
log::debug!("fxa_gather_telemetry");
ACCOUNTS.call_with_result_mut(error, handle, |fxa| fxa.gather_telemetry())
}