use std::ffi::CStr; use std::os::raw::c_char; use std::ptr; mod trait_provider; mod error; mod security; mod session; pub mod crypto; pub mod device; pub mod provider_selfhosted; use trait_provider::{Provider, ActivateResponse, HeartbeatResponse, LicenseStatus}; use provider_selfhosted::SelfHostedProvider; pub struct CraftContext { pub provider: Option>, pub initialized: bool, } impl CraftContext { pub fn new() -> Self { CraftContext { provider: None, initialized: false } } } #[repr(C)] pub struct CraftResult { pub success: i32, pub message: *const c_char, } #[repr(C)] pub struct LicenseInfo { pub is_licensed: i32, pub expiration_date: i64, pub feature_names: *const *const c_char, pub feature_values: *const i32, pub feature_count: i32, } unsafe fn c_str_to_string(ptr: *const c_char) -> String { if ptr.is_null() { String::new() } else { CStr::from_ptr(ptr).to_string_lossy().into_owned() } } static OK_MSG: &[u8] = b"ok\0"; fn ok_result() -> CraftResult { CraftResult { success: 1, message: OK_MSG.as_ptr() as *const c_char } } fn fail_result() -> CraftResult { static FAIL: &[u8] = b"failure\0"; CraftResult { success: 0, message: FAIL.as_ptr() as *const c_char } } fn parse_provider(config: &str) -> String { serde_json::from_str::(config) .ok() .and_then(|v| v.get("provider").and_then(|p| p.as_str().map(|s| s.to_string()))) .unwrap_or_else(|| "selfhosted".to_string()) } #[no_mangle] pub extern "C" fn craft_initialize(config_json: *const c_char) -> *mut CraftContext { let config_str = unsafe { c_str_to_string(config_json) }; let mut ctx = Box::new(CraftContext::new()); let mut provider: Box = match parse_provider(&config_str).as_str() { "selfhosted" => Box::new(SelfHostedProvider::new()), _ => Box::new(SelfHostedProvider::new()), }; match provider.initialize(&ctx, &config_str) { Ok(()) => { ctx.provider = Some(provider); ctx.initialized = true; } Err(_) => { ctx.initialized = false; } } Box::into_raw(ctx) } #[no_mangle] pub extern "C" fn craft_activate( handle: *mut CraftContext, license_key: *const c_char, _config_json: *const c_char, ) -> CraftResult { if handle.is_null() { return fail_result(); } let ctx = unsafe { &*handle }; let key = unsafe { c_str_to_string(license_key) }; ctx.provider.as_ref() .and_then(|p| p.activate(ctx, &key).ok()) .map_or_else(fail_result, |_| ok_result()) } #[no_mangle] pub extern "C" fn craft_check_license(handle: *mut CraftContext) -> CraftResult { if handle.is_null() { return fail_result(); } let ctx = unsafe { &*handle }; ctx.provider.as_ref() .and_then(|p| p.check_license(ctx).ok()) .map_or_else(fail_result, |s| if s.licensed { ok_result() } else { fail_result() }) } #[no_mangle] pub extern "C" fn craft_get_license_info(handle: *mut CraftContext) -> *mut LicenseInfo { if handle.is_null() { return ptr::null_mut(); } let ctx = unsafe { &*handle }; ctx.provider.as_ref() .map(|p| p.get_license_info(ctx)) .map_or(ptr::null_mut(), |info| Box::into_raw(Box::new(info))) } #[no_mangle] pub extern "C" fn craft_free_license_info(info: *mut LicenseInfo) { if !info.is_null() { unsafe { drop(Box::from_raw(info)); } } } #[no_mangle] pub extern "C" fn craft_has_feature(handle: *mut CraftContext, feature_name: *const c_char) -> i32 { if handle.is_null() { return 0; } let ctx = unsafe { &*handle }; let name = unsafe { c_str_to_string(feature_name) }; ctx.provider.as_ref().map_or(0, |p| if p.has_feature(ctx, &name) { 1 } else { 0 }) } #[no_mangle] pub extern "C" fn craft_release(handle: *mut CraftContext) -> CraftResult { if handle.is_null() { return fail_result(); } let ctx = unsafe { &mut *handle }; if let Some(ref mut p) = ctx.provider { let _ = p.release(); } ok_result() } #[no_mangle] pub extern "C" fn craft_heartbeat(handle: *mut CraftContext) -> CraftResult { if handle.is_null() { return fail_result(); } let ctx = unsafe { &*handle }; ctx.provider.as_ref() .and_then(|p| p.heartbeat(ctx).ok()) .map_or_else(fail_result, |_| ok_result()) } #[no_mangle] pub extern "C" fn craft_destroy(handle: *mut CraftContext) { if !handle.is_null() { unsafe { let ctx = &mut *handle; if let Some(ref mut p) = ctx.provider { p.close(); } drop(Box::from_raw(handle)); } } }