#ifndef CLIENT_FFI_H #define CLIENT_FFI_H #include #include #include #ifdef __cplusplus extern "C" { #endif /* ------------------------------------------------------------------ * Opaque handle returned by client_create. * ------------------------------------------------------------------ */ typedef struct ClientHandle ClientHandle_t; /* ------------------------------------------------------------------ * C delivery callback. * * Called for each outbound envelope. `addr_ptr/addr_len` is the * delivery address (UTF-8, not NUL-terminated). `data_ptr/data_len` * is the encrypted payload. `userdata` is the pointer supplied to * client_create. Return 0 on success, negative on error. * ------------------------------------------------------------------ */ typedef int32_t (*DeliverFn_t)( const uint8_t *addr_ptr, size_t addr_len, const uint8_t *data_ptr, size_t data_len, void *userdata); /* ------------------------------------------------------------------ * Error codes (match crates/client-ffi/src/api.rs ErrorCode) * ------------------------------------------------------------------ */ #define CLIENT_FFI_OK 0 #define CLIENT_FFI_ERR_BAD_UTF8 (-1) #define CLIENT_FFI_ERR_BAD_INTRO (-2) #define CLIENT_FFI_ERR_DELIVERY (-3) #define CLIENT_FFI_ERR_UNKNOWN (-4) /* ------------------------------------------------------------------ * Result structs — all heap-allocated fields are owned by the caller * and must be freed via the matching destroy_* function. * ------------------------------------------------------------------ */ typedef struct { int32_t error_code; uint8_t *intro_bytes_ptr; size_t intro_bytes_len; } CreateIntroResult_t; typedef struct { int32_t error_code; /* UTF-8, NOT NUL-terminated */ uint8_t *convo_id_ptr; size_t convo_id_len; } CreateConvoResult_t; typedef struct { int32_t error_code; bool has_content; bool is_new_convo; /* UTF-8, NOT NUL-terminated. NULL when has_content is false. */ uint8_t *convo_id_ptr; size_t convo_id_len; /* NULL when has_content is false. */ uint8_t *content_ptr; size_t content_len; } PushInboundResult_t; /* ------------------------------------------------------------------ * Lifecycle * ------------------------------------------------------------------ */ /** * Create an ephemeral in-memory client. * * @param name_ptr UTF-8 installation name (need not be NUL-terminated). * @param name_len Byte length of name_ptr. * @param callback Non-null delivery callback. * @param userdata Opaque pointer forwarded to each callback invocation. * @return Opaque handle, or NULL on error. Free with client_destroy. */ ClientHandle_t *client_create( const uint8_t *name_ptr, size_t name_len, DeliverFn_t callback, void *userdata); /** * Destroy a client handle. Must not be used after this call. */ void client_destroy(ClientHandle_t *handle); /* ------------------------------------------------------------------ * Identity * ------------------------------------------------------------------ */ /** * Return a pointer to the installation name bytes (UTF-8, not NUL-terminated). * The pointer is valid until the next mutating call or client_destroy. * The caller must NOT free it. * * @param out_len Receives the byte length of the returned string. */ const uint8_t *client_installation_name(const ClientHandle_t *handle, size_t *out_len); /* ------------------------------------------------------------------ * Intro bundle * ------------------------------------------------------------------ */ /** * Generate a serialised introduction bundle for out-of-band sharing. * Always call destroy_create_intro_result when done. */ void client_create_intro_bundle(ClientHandle_t *handle, CreateIntroResult_t *out); /** Free the result from client_create_intro_bundle. */ void destroy_create_intro_result(CreateIntroResult_t *result); /* ------------------------------------------------------------------ * Create conversation * ------------------------------------------------------------------ */ /** * Parse an intro bundle and initiate a private conversation. * Outbound envelopes are dispatched through the delivery callback. * Always call destroy_create_convo_result when done. */ void client_create_conversation( ClientHandle_t *handle, const uint8_t *bundle_ptr, size_t bundle_len, const uint8_t *content_ptr, size_t content_len, CreateConvoResult_t *out); /** Free the result from client_create_conversation. */ void destroy_create_convo_result(CreateConvoResult_t *result); /* ------------------------------------------------------------------ * Send message * ------------------------------------------------------------------ */ /** * Encrypt content and dispatch outbound envelopes. * * @param convo_id_ptr UTF-8 conversation ID (not NUL-terminated). * @return CLIENT_FFI_OK on success, error code otherwise. */ int32_t client_send_message( ClientHandle_t *handle, const uint8_t *convo_id_ptr, size_t convo_id_len, const uint8_t *content_ptr, size_t content_len); /* ------------------------------------------------------------------ * Push inbound * ------------------------------------------------------------------ */ /** * Decrypt an inbound payload. has_content is false for protocol frames. * Always call destroy_push_inbound_result when error_code == CLIENT_FFI_OK. */ void client_push_inbound( ClientHandle_t *handle, const uint8_t *payload_ptr, size_t payload_len, PushInboundResult_t *out); /** Free the result from client_push_inbound. */ void destroy_push_inbound_result(PushInboundResult_t *result); /* ------------------------------------------------------------------ * ClientOps — vtable used by c-test so C can call the Rust client-ffi * functions indirectly via function pointers supplied by the Rust harness. * * This avoids the linker-ordering problem that arises when C archives * are searched before Rust rlibs: the C code has no direct undefined * references to Rust symbols; it only uses the pointers it receives. * ------------------------------------------------------------------ */ typedef struct { ClientHandle_t *(*create)( const uint8_t *name_ptr, size_t name_len, DeliverFn_t callback, void *userdata); void (*destroy)(ClientHandle_t *handle); void (*create_intro_bundle)(ClientHandle_t *handle, CreateIntroResult_t *out); void (*destroy_intro_result)(CreateIntroResult_t *result); void (*create_conversation)( ClientHandle_t *handle, const uint8_t *bundle_ptr, size_t bundle_len, const uint8_t *content_ptr, size_t content_len, CreateConvoResult_t *out); void (*destroy_convo_result)(CreateConvoResult_t *result); int32_t (*send_message)( ClientHandle_t *handle, const uint8_t *convo_id_ptr, size_t convo_id_len, const uint8_t *content_ptr, size_t content_len); void (*push_inbound)( ClientHandle_t *handle, const uint8_t *payload_ptr, size_t payload_len, PushInboundResult_t *out); void (*destroy_inbound_result)(PushInboundResult_t *result); } ClientOps; #ifdef __cplusplus } #endif #endif /* CLIENT_FFI_H */