ascii-chat 0.8.38
Real-time terminal-based video chat with ASCII art conversion
Loading...
Searching...
No Matches
client.c File Reference

Go to the source code of this file.

Macros

#define WASM_LOG(msg)   EM_ASM({ console.error('[C] ' + UTF8ToString($0)); }, msg)
 
#define WASM_LOG_INT(msg, val)   EM_ASM({ console.error('[C] ' + UTF8ToString($0) + ': ' + $1); }, msg, val)
 
#define WASM_ERROR(msg)   EM_ASM({ console.error('[C] ERROR: ' + UTF8ToString($0)); }, msg)
 

Enumerations

enum  connection_state_t {
  CONNECTION_STATE_DISCONNECTED = 0 , CONNECTION_STATE_CONNECTING = 1 , CONNECTION_STATE_HANDSHAKE = 2 , CONNECTION_STATE_CONNECTED = 3 ,
  CONNECTION_STATE_ERROR = 4
}
 

Functions

 EM_JS (void, js_send_raw_packet,(const uint8_t *packet_data, size_t packet_len), { if(!Module.sendPacketCallback) { console.error('[WASM] sendPacketCallback not registered - cannot send packet');return;} const packetArray=new Uint8Array(HEAPU8.buffer, packet_data, packet_len);const packetCopy=new Uint8Array(packetArray);var pktType=packetCopy.length >=10 ?((packetCopy[8]<< 8)|packetCopy[9]) :-1;console.error('[WASM->JS] Sending raw packet:', packetCopy.length, 'bytes, type=0x'+pktType.toString(16));Module.sendPacketCallback(packetCopy);})
 
EMSCRIPTEN_KEEPALIVE int client_init_with_args (const char *args_json)
 
EMSCRIPTEN_KEEPALIVE void client_cleanup (void)
 
EMSCRIPTEN_KEEPALIVE int client_generate_keypair (void)
 
EMSCRIPTEN_KEEPALIVE int client_set_server_address (const char *server_host, int server_port)
 
EMSCRIPTEN_KEEPALIVE char * client_get_public_key_hex (void)
 
EMSCRIPTEN_KEEPALIVE int client_handle_key_exchange_init (const uint8_t *packet, size_t packet_len)
 
EMSCRIPTEN_KEEPALIVE int client_handle_auth_challenge (const uint8_t *packet, size_t packet_len)
 
EMSCRIPTEN_KEEPALIVE int client_handle_handshake_complete (const uint8_t *packet, size_t packet_len)
 
EMSCRIPTEN_KEEPALIVE int client_encrypt_packet (const uint8_t *plaintext, size_t plaintext_len, uint8_t *ciphertext, size_t ciphertext_size, size_t *out_len)
 
EMSCRIPTEN_KEEPALIVE int client_decrypt_packet (const uint8_t *ciphertext, size_t ciphertext_len, uint8_t *plaintext, size_t plaintext_size, size_t *out_len)
 
EMSCRIPTEN_KEEPALIVE char * client_parse_packet (const uint8_t *raw_packet, size_t packet_len)
 
EMSCRIPTEN_KEEPALIVE int client_serialize_packet (uint16_t packet_type, const uint8_t *payload, size_t payload_len, uint32_t client_id, uint8_t *output, size_t *out_len)
 
EMSCRIPTEN_KEEPALIVE int client_send_video_frame (const uint8_t *rgba_data, int width, int height)
 
EMSCRIPTEN_KEEPALIVE int client_get_connection_state (void)
 
EMSCRIPTEN_KEEPALIVE void client_free_string (char *ptr)
 
EMSCRIPTEN_KEEPALIVE int client_opus_encoder_init (int sample_rate, int channels, int bitrate)
 
EMSCRIPTEN_KEEPALIVE int client_opus_decoder_init (int sample_rate, int channels)
 
EMSCRIPTEN_KEEPALIVE int client_opus_encode (const int16_t *pcm_data, int frame_size, uint8_t *opus_data, int max_opus_bytes)
 
EMSCRIPTEN_KEEPALIVE int client_opus_decode (const uint8_t *opus_data, int opus_bytes, int16_t *pcm_data, int frame_size, int decode_fec)
 
EMSCRIPTEN_KEEPALIVE void client_opus_encoder_cleanup (void)
 
EMSCRIPTEN_KEEPALIVE void client_opus_decoder_cleanup (void)
 
EMSCRIPTEN_KEEPALIVE const char * get_help_text (int mode, const char *option_name)
 

Macro Definition Documentation

◆ WASM_ERROR

#define WASM_ERROR (   msg)    EM_ASM({ console.error('[C] ERROR: ' + UTF8ToString($0)); }, msg)

Definition at line 14 of file src/web/client.c.

◆ WASM_LOG

#define WASM_LOG (   msg)    EM_ASM({ console.error('[C] ' + UTF8ToString($0)); }, msg)

Definition at line 12 of file src/web/client.c.

◆ WASM_LOG_INT

#define WASM_LOG_INT (   msg,
  val 
)    EM_ASM({ console.error('[C] ' + UTF8ToString($0) + ': ' + $1); }, msg, val)

Definition at line 13 of file src/web/client.c.

Enumeration Type Documentation

◆ connection_state_t

Enumerator
CONNECTION_STATE_DISCONNECTED 
CONNECTION_STATE_CONNECTING 
CONNECTION_STATE_HANDSHAKE 
CONNECTION_STATE_CONNECTED 
CONNECTION_STATE_ERROR 

Definition at line 121 of file src/web/client.c.

121 {
connection_state_t
@ CONNECTION_STATE_ERROR
@ CONNECTION_STATE_DISCONNECTED
@ CONNECTION_STATE_CONNECTED
@ CONNECTION_STATE_HANDSHAKE
@ CONNECTION_STATE_CONNECTING

Function Documentation

◆ client_cleanup()

EMSCRIPTEN_KEEPALIVE void client_cleanup ( void  )

Definition at line 206 of file src/web/client.c.

206 {
207 WASM_LOG("=== client_cleanup CALLED ===");
208 WASM_LOG_INT(" g_initialized", g_initialized);
209 WASM_LOG_INT(" g_connection_state", g_connection_state);
210 WASM_LOG_INT(" g_crypto_handshake_ctx.state", g_crypto_handshake_ctx.state);
211
212 // Clean up crypto handshake context
213 crypto_handshake_destroy(&g_crypto_handshake_ctx);
214 memset(&g_crypto_handshake_ctx, 0, sizeof(g_crypto_handshake_ctx));
215
216 g_handshake_complete = false;
217 g_connection_state = CONNECTION_STATE_DISCONNECTED;
218 g_initialized = false;
221
222 WASM_LOG("=== client_cleanup COMPLETE ===");
223}
void crypto_handshake_destroy(crypto_handshake_context_t *ctx)
void platform_destroy(void)
Definition init.c:15
void options_state_destroy(void)
Definition rcu.c:309
#define WASM_LOG(msg)
#define WASM_LOG_INT(msg, val)

References CONNECTION_STATE_DISCONNECTED, crypto_handshake_destroy(), options_state_destroy(), platform_destroy(), WASM_LOG, and WASM_LOG_INT.

◆ client_decrypt_packet()

EMSCRIPTEN_KEEPALIVE int client_decrypt_packet ( const uint8_t *  ciphertext,
size_t  ciphertext_len,
uint8_t *  plaintext,
size_t  plaintext_size,
size_t *  out_len 
)

Decrypt a ciphertext packet

Parameters
ciphertextInput ciphertext data
ciphertext_lenLength of ciphertext
plaintextOutput buffer (allocated by caller, must be at least ciphertext_len)
plaintext_sizeSize of plaintext buffer
out_lenOutput parameter for plaintext length
Returns
0 on success, -1 on error

Definition at line 582 of file src/web/client.c.

583 {
584 if (!g_handshake_complete) {
585 WASM_ERROR("Decryption requires completed handshake");
586 return -1;
587 }
588
589 // Decrypt the packet using handshake context's crypto context
590 size_t plaintext_len = 0;
591 crypto_result_t result = crypto_decrypt(&g_crypto_handshake_ctx.crypto_ctx, ciphertext, ciphertext_len, plaintext,
592 plaintext_size, &plaintext_len);
593 if (result != CRYPTO_OK) {
594 WASM_ERROR("Decryption failed");
595 return -1;
596 }
597
598 *out_len = plaintext_len;
599 return 0;
600}
crypto_result_t crypto_decrypt(crypto_context_t *ctx, const uint8_t *ciphertext, size_t ciphertext_len, uint8_t *plaintext_out, size_t plaintext_out_size, size_t *plaintext_len_out)
#define WASM_ERROR(msg)

References crypto_decrypt(), and WASM_ERROR.

◆ client_encrypt_packet()

EMSCRIPTEN_KEEPALIVE int client_encrypt_packet ( const uint8_t *  plaintext,
size_t  plaintext_len,
uint8_t *  ciphertext,
size_t  ciphertext_size,
size_t *  out_len 
)

Encrypt a plaintext packet

Parameters
plaintextInput plaintext data
plaintext_lenLength of plaintext
ciphertextOutput buffer (allocated by caller, must be at least plaintext_len + crypto_aead_overhead)
ciphertext_sizeSize of ciphertext buffer
out_lenOutput parameter for ciphertext length
Returns
0 on success, -1 on error

Definition at line 552 of file src/web/client.c.

553 {
554 if (!g_handshake_complete) {
555 WASM_ERROR("Encryption requires completed handshake");
556 return -1;
557 }
558
559 // Encrypt the packet using handshake context's crypto context
560 size_t ciphertext_len = 0;
561 crypto_result_t result = crypto_encrypt(&g_crypto_handshake_ctx.crypto_ctx, plaintext, plaintext_len, ciphertext,
562 ciphertext_size, &ciphertext_len);
563 if (result != CRYPTO_OK) {
564 WASM_ERROR("Encryption failed");
565 return -1;
566 }
567
568 *out_len = ciphertext_len;
569 return 0;
570}
crypto_result_t crypto_encrypt(crypto_context_t *ctx, const uint8_t *plaintext, size_t plaintext_len, uint8_t *ciphertext_out, size_t ciphertext_out_size, size_t *ciphertext_len_out)

References crypto_encrypt(), and WASM_ERROR.

◆ client_free_string()

EMSCRIPTEN_KEEPALIVE void client_free_string ( char *  ptr)

Definition at line 725 of file src/web/client.c.

725 {
726 SAFE_FREE(ptr);
727}

◆ client_generate_keypair()

EMSCRIPTEN_KEEPALIVE int client_generate_keypair ( void  )

Generate client keypair for handshake

Returns
0 on success, -1 on error

Definition at line 234 of file src/web/client.c.

234 {
235 WASM_LOG("=== client_generate_keypair CALLED ===");
236 WASM_LOG_INT(" g_initialized", g_initialized);
237 WASM_LOG_INT(" g_crypto_handshake_ctx.state BEFORE", g_crypto_handshake_ctx.state);
238
239 if (!g_initialized) {
240 WASM_ERROR("Client not initialized");
241 return -1;
242 }
243
244 // Reset handshake context if previously used (reconnection / React Strict Mode remount)
245 if (g_crypto_handshake_ctx.state != CRYPTO_HANDSHAKE_DISABLED) {
246 WASM_LOG("Destroying previous handshake context before re-init");
247 WASM_LOG_INT(" State before destroy", g_crypto_handshake_ctx.state);
248 crypto_handshake_destroy(&g_crypto_handshake_ctx);
249 WASM_LOG_INT(" State after destroy", g_crypto_handshake_ctx.state);
250 memset(&g_crypto_handshake_ctx, 0, sizeof(g_crypto_handshake_ctx));
251 WASM_LOG_INT(" State after memset", g_crypto_handshake_ctx.state);
252 g_handshake_complete = false;
253 }
254
255 // Initialize crypto handshake context
256 WASM_LOG("Calling crypto_handshake_init...");
257 WASM_LOG_INT(" State before init", g_crypto_handshake_ctx.state);
258 asciichat_error_t result = crypto_handshake_init(&g_crypto_handshake_ctx, false /* is_server */);
259 if (result != ASCIICHAT_OK) {
260 WASM_LOG_INT("crypto_handshake_init FAILED, result", result);
261 WASM_LOG_INT(" State after failed init", g_crypto_handshake_ctx.state);
262 return -1;
263 }
264
265 WASM_LOG("Keypair generated successfully");
266 WASM_LOG_INT(" g_crypto_handshake_ctx.state AFTER init", g_crypto_handshake_ctx.state);
267 g_connection_state = CONNECTION_STATE_DISCONNECTED;
268 return 0;
269}
asciichat_error_t crypto_handshake_init(crypto_handshake_context_t *ctx, bool is_server)

References CONNECTION_STATE_DISCONNECTED, crypto_handshake_destroy(), crypto_handshake_init(), WASM_ERROR, WASM_LOG, and WASM_LOG_INT.

◆ client_get_connection_state()

EMSCRIPTEN_KEEPALIVE int client_get_connection_state ( void  )

Get current connection state

Returns
Connection state enum value

Definition at line 716 of file src/web/client.c.

716 {
717 return (int)g_connection_state;
718}

◆ client_get_public_key_hex()

EMSCRIPTEN_KEEPALIVE char * client_get_public_key_hex ( void  )

Get client public key as hex string

Returns
Allocated hex string (caller must free with client_free_string), or NULL on error

Definition at line 302 of file src/web/client.c.

302 {
303 if (g_crypto_handshake_ctx.state == CRYPTO_HANDSHAKE_DISABLED) {
304 WASM_ERROR("No crypto context (call client_generate_keypair first)");
305 return NULL;
306 }
307
308 // Get public key from handshake context
309 const uint8_t *pubkey = g_crypto_handshake_ctx.crypto_ctx.public_key;
310 if (!pubkey) {
311 WASM_ERROR("Public key not available in crypto context");
312 return NULL;
313 }
314
315 // Allocate buffer for hex string (32 bytes = 64 hex chars + null terminator)
316 char *hex_buffer = SAFE_MALLOC(65, char *);
317 if (!hex_buffer) {
318 WASM_ERROR("Failed to allocate hex buffer");
319 return NULL;
320 }
321
322 // Convert public key to hex
323 for (int i = 0; i < 32; i++) {
324 snprintf(hex_buffer + (i * 2), 3, "%02x", pubkey[i]);
325 }
326 hex_buffer[64] = '\0';
327
328 return hex_buffer;
329}

References WASM_ERROR.

◆ client_handle_auth_challenge()

EMSCRIPTEN_KEEPALIVE int client_handle_auth_challenge ( const uint8_t *  packet,
size_t  packet_len 
)

Handle CRYPTO_AUTH_CHALLENGE packet from server

Parameters
packetRaw packet data including header
packet_lenTotal packet length
Returns
0 on success, -1 on error

Definition at line 426 of file src/web/client.c.

426 {
427 WASM_LOG("=== client_handle_auth_challenge CALLED ===");
428 WASM_LOG_INT(" packet_len", (int)packet_len);
429 WASM_LOG_INT(" g_crypto_handshake_ctx.state", g_crypto_handshake_ctx.state);
430
431 if (!packet || packet_len == 0) {
432 WASM_ERROR("Invalid packet data");
433 return -1;
434 }
435
436 // Extract packet type and payload
437 if (packet_len < sizeof(packet_header_t)) {
438 WASM_ERROR("Packet too small for header");
439 return -1;
440 }
441
442 const packet_header_t *header = (const packet_header_t *)packet;
443 packet_type_t packet_type = ntohs(header->type);
444 const uint8_t *payload_src = packet + sizeof(packet_header_t);
445 size_t payload_len = packet_len - sizeof(packet_header_t);
446
447 WASM_LOG_INT(" packet_type", packet_type);
448 WASM_LOG_INT(" payload_len", (int)payload_len);
449
450 // Allocate payload copy from buffer pool (crypto function takes ownership and frees it)
451 uint8_t *payload = NULL;
452 if (payload_len > 0) {
453 payload = buffer_pool_alloc(NULL, payload_len);
454 if (!payload) {
455 WASM_ERROR("Failed to allocate payload buffer");
456 g_connection_state = CONNECTION_STATE_ERROR;
457 return -1;
458 }
459 memcpy(payload, payload_src, payload_len);
460 }
461
462 // Process auth challenge
463 asciichat_error_t result = crypto_handshake_client_auth_response(&g_crypto_handshake_ctx, &g_wasm_transport,
464 packet_type, payload, payload_len);
465
466 WASM_LOG_INT(" auth_response result", result);
467
468 if (result != ASCIICHAT_OK) {
469 WASM_ERROR("Failed to process AUTH_CHALLENGE");
470 g_connection_state = CONNECTION_STATE_ERROR;
471 return -1;
472 }
473
474 WASM_LOG("=== AUTH_CHALLENGE processed successfully ===");
475 return 0;
476}
void * buffer_pool_alloc(buffer_pool_t *pool, size_t size)
Definition buffer_pool.c:99
asciichat_error_t crypto_handshake_client_auth_response(crypto_handshake_context_t *ctx, acip_transport_t *transport, packet_type_t packet_type, const uint8_t *payload, size_t payload_len)

References buffer_pool_alloc(), CONNECTION_STATE_ERROR, crypto_handshake_client_auth_response(), WASM_ERROR, WASM_LOG, and WASM_LOG_INT.

◆ client_handle_handshake_complete()

EMSCRIPTEN_KEEPALIVE int client_handle_handshake_complete ( const uint8_t *  packet,
size_t  packet_len 
)

Handle CRYPTO_HANDSHAKE_COMPLETE packet from server

Parameters
packetRaw packet data including header
packet_lenTotal packet length
Returns
0 on success, -1 on error

Definition at line 485 of file src/web/client.c.

485 {
486 WASM_LOG("=== client_handle_handshake_complete CALLED ===");
487 WASM_LOG_INT(" packet_len", (int)packet_len);
488 WASM_LOG_INT(" g_crypto_handshake_ctx.state", g_crypto_handshake_ctx.state);
489
490 if (!packet || packet_len == 0) {
491 WASM_ERROR("Invalid packet data");
492 return -1;
493 }
494
495 // Extract packet type and payload
496 if (packet_len < sizeof(packet_header_t)) {
497 WASM_ERROR("Packet too small for header");
498 return -1;
499 }
500
501 const packet_header_t *header = (const packet_header_t *)packet;
502 packet_type_t packet_type = ntohs(header->type);
503 const uint8_t *payload_src = packet + sizeof(packet_header_t);
504 size_t payload_len = packet_len - sizeof(packet_header_t);
505
506 WASM_LOG_INT(" packet_type", packet_type);
507
508 // Allocate payload copy from buffer pool (crypto function takes ownership and frees it)
509 uint8_t *payload = NULL;
510 if (payload_len > 0) {
511 payload = buffer_pool_alloc(NULL, payload_len);
512 if (!payload) {
513 WASM_ERROR("Failed to allocate payload buffer");
514 g_connection_state = CONNECTION_STATE_ERROR;
515 return -1;
516 }
517 memcpy(payload, payload_src, payload_len);
518 }
519
520 // Complete handshake (takes ownership of payload and will free it)
521 asciichat_error_t result =
522 crypto_handshake_client_complete(&g_crypto_handshake_ctx, &g_wasm_transport, packet_type, payload, payload_len);
523
524 WASM_LOG_INT(" handshake_complete result", result);
525
526 if (result != ASCIICHAT_OK) {
527 WASM_ERROR("Failed to complete handshake");
528 g_connection_state = CONNECTION_STATE_ERROR;
529 return -1;
530 }
531
532 g_handshake_complete = true;
533 g_connection_state = CONNECTION_STATE_CONNECTED;
534 WASM_LOG("=== HANDSHAKE COMPLETE - session encrypted ===");
535 return 0;
536}
asciichat_error_t crypto_handshake_client_complete(crypto_handshake_context_t *ctx, acip_transport_t *transport, packet_type_t packet_type, const uint8_t *payload, size_t payload_len)

References buffer_pool_alloc(), CONNECTION_STATE_CONNECTED, CONNECTION_STATE_ERROR, crypto_handshake_client_complete(), WASM_ERROR, WASM_LOG, and WASM_LOG_INT.

◆ client_handle_key_exchange_init()

EMSCRIPTEN_KEEPALIVE int client_handle_key_exchange_init ( const uint8_t *  packet,
size_t  packet_len 
)

Handle CRYPTO_KEY_EXCHANGE_INIT packet from server This is the first step of the crypto handshake

Parameters
packetRaw packet data including header
packet_lenTotal packet length
Returns
0 on success, -1 on error

Definition at line 339 of file src/web/client.c.

339 {
340 WASM_LOG("=== client_handle_key_exchange_init CALLED ===");
341 WASM_LOG_INT(" packet_len", (int)packet_len);
342 WASM_LOG_INT(" g_crypto_handshake_ctx.state BEFORE", g_crypto_handshake_ctx.state);
343
344 // Safety check: if handshake context is not in INIT state, reinitialize it
345 // This handles cases where previous handshakes weren't properly cleaned up
346 if (g_crypto_handshake_ctx.state != CRYPTO_HANDSHAKE_INIT) {
347 WASM_LOG("Handshake context not in INIT state, reinitializing...");
348 WASM_LOG_INT(" Previous state", g_crypto_handshake_ctx.state);
349
350 // Destroy and reset
351 if (g_crypto_handshake_ctx.state != CRYPTO_HANDSHAKE_DISABLED) {
352 crypto_handshake_destroy(&g_crypto_handshake_ctx);
353 }
354 memset(&g_crypto_handshake_ctx, 0, sizeof(g_crypto_handshake_ctx));
355
356 // Reinitialize to INIT state
357 asciichat_error_t init_result = crypto_handshake_init(&g_crypto_handshake_ctx, false);
358 if (init_result != ASCIICHAT_OK) {
359 WASM_ERROR("Failed to reinitialize crypto handshake context");
360 WASM_LOG_INT(" init result", init_result);
361 return -1;
362 }
363 WASM_LOG("Crypto handshake context reinitialized");
364 }
365
366 if (!packet || packet_len == 0) {
367 WASM_ERROR("Invalid packet data");
368 return -1;
369 }
370
371 // Extract packet type and payload
372 if (packet_len < sizeof(packet_header_t)) {
373 WASM_ERROR("Packet too small for header");
374 return -1;
375 }
376
377 const packet_header_t *header = (const packet_header_t *)packet;
378 packet_type_t packet_type = ntohs(header->type);
379 const uint8_t *payload_src = packet + sizeof(packet_header_t);
380 size_t payload_len = packet_len - sizeof(packet_header_t);
381
382 WASM_LOG_INT(" packet_type", packet_type);
383 WASM_LOG_INT(" payload_len", (int)payload_len);
384
385 // Allocate payload copy from buffer pool (crypto function takes ownership and frees it)
386 // The raw packet pointer from JS cannot be passed directly because the crypto
387 // handshake function calls buffer_pool_free() on the payload when done.
388 uint8_t *payload = NULL;
389 if (payload_len > 0) {
390 payload = buffer_pool_alloc(NULL, payload_len);
391 if (!payload) {
392 WASM_ERROR("Failed to allocate payload buffer");
393 g_connection_state = CONNECTION_STATE_ERROR;
394 return -1;
395 }
396 memcpy(payload, payload_src, payload_len);
397 }
398
399 // Process key exchange using transport-abstracted handshake
400 WASM_LOG("Calling crypto_handshake_client_key_exchange...");
401
402 asciichat_error_t result = crypto_handshake_client_key_exchange(&g_crypto_handshake_ctx, &g_wasm_transport,
403 packet_type, payload, payload_len);
404
405 WASM_LOG_INT(" handshake result", result);
406 WASM_LOG_INT(" g_crypto_handshake_ctx.state AFTER", g_crypto_handshake_ctx.state);
407
408 if (result != ASCIICHAT_OK) {
409 WASM_ERROR("Failed to process KEY_EXCHANGE_INIT");
410 g_connection_state = CONNECTION_STATE_ERROR;
411 return -1;
412 }
413
414 g_connection_state = CONNECTION_STATE_HANDSHAKE;
415 WASM_LOG("=== KEY_EXCHANGE_INIT processed successfully ===");
416 return 0;
417}
asciichat_error_t crypto_handshake_client_key_exchange(crypto_handshake_context_t *ctx, acip_transport_t *transport, packet_type_t packet_type, const uint8_t *payload, size_t payload_len)

References buffer_pool_alloc(), CONNECTION_STATE_ERROR, CONNECTION_STATE_HANDSHAKE, crypto_handshake_client_key_exchange(), crypto_handshake_destroy(), crypto_handshake_init(), WASM_ERROR, WASM_LOG, and WASM_LOG_INT.

◆ client_init_with_args()

EMSCRIPTEN_KEEPALIVE int client_init_with_args ( const char *  args_json)

Initialize client mode with command-line style arguments

Parameters
args_jsonSpace-separated string of argument strings, e.g. "client --width 80 --height 40"
Returns
0 on success, -1 on error

Definition at line 141 of file src/web/client.c.

141 {
142 // Parse arguments
143 WASM_LOG("Parsing arguments...");
144 char *args_copy = strdup(args_json);
145 if (!args_copy) {
146 WASM_ERROR("strdup FAILED");
147 return -1;
148 }
149
150 // Count arguments
151 int argc = 0;
152 char *argv[64] = {NULL}; // Max 64 arguments
153 char *token = strtok(args_copy, " ");
154 while (token != NULL && argc < 63) {
155 argv[argc++] = token;
156 token = strtok(NULL, " ");
157 }
158 argv[argc] = NULL;
159 WASM_LOG_INT("Parsed arguments, argc", argc);
160
161 // Initialize options (sets up RCU, defaults, etc.)
162 WASM_LOG("Calling options_init...");
163 asciichat_error_t err = options_init(argc, argv);
164 free(args_copy);
165 WASM_LOG("client_init_with_args: START");
166
167 if (g_initialized) {
168 WASM_ERROR("Client already initialized");
169 return -1;
170 }
171
172 // Initialize platform layer
173 WASM_LOG("Calling platform_init...");
174 err = platform_init();
175 if (err != ASCIICHAT_OK) {
176 WASM_ERROR("platform_init FAILED");
177 return -1;
178 }
179 WASM_LOG("platform_init OK");
180
181 // Initialize logging to stderr (console.error in browser)
182 WASM_LOG("Calling log_init...");
183 log_init(NULL, LOG_DEBUG, true, false);
184 WASM_LOG("log_init OK");
185 log_info("WASM client initialized via logging system");
186
187 if (err != ASCIICHAT_OK) {
188 WASM_LOG_INT("options_init FAILED", err);
189 return -1;
190 }
191 WASM_LOG("options_init OK");
192
193 // Initialize ANSI color code generation
194 WASM_LOG("Calling ansi_fast_init...");
196 WASM_LOG("ansi_fast_init OK");
197
198 g_initialized = true;
199 g_connection_state = CONNECTION_STATE_DISCONNECTED;
200
201 WASM_LOG("client_init_with_args: COMPLETE");
202 return 0;
203}
void ansi_fast_init(void)
Definition ansi_fast.c:198
asciichat_error_t platform_init(void)
Definition init.c:10
asciichat_error_t options_init(int argc, char **argv)
void log_init(const char *filename, log_level_t level, bool force_stderr, bool use_mmap)

References ansi_fast_init(), CONNECTION_STATE_DISCONNECTED, log_init(), options_init(), platform_init(), WASM_ERROR, WASM_LOG, and WASM_LOG_INT.

◆ client_opus_decode()

EMSCRIPTEN_KEEPALIVE int client_opus_decode ( const uint8_t *  opus_data,
int  opus_bytes,
int16_t *  pcm_data,
int  frame_size,
int  decode_fec 
)

Decode Opus audio to PCM

Parameters
opus_dataInput Opus data
opus_bytesLength of Opus data
pcm_dataOutput buffer for PCM samples (Int16)
frame_sizeMaximum number of samples per channel
decode_fecWhether to use FEC
Returns
Number of samples decoded per channel, or -1 on error

Definition at line 819 of file src/web/client.c.

819 {
820 if (!g_opus_decoder) {
821 WASM_ERROR("Opus decoder not initialized");
822 return -1;
823 }
824
825 int decoded_samples = opus_decode(g_opus_decoder, opus_data, opus_bytes, pcm_data, frame_size, decode_fec);
826 if (decoded_samples < 0) {
827 WASM_ERROR("Opus decoding failed");
828 return -1;
829 }
830
831 return decoded_samples;
832}

References WASM_ERROR.

◆ client_opus_decoder_cleanup()

EMSCRIPTEN_KEEPALIVE void client_opus_decoder_cleanup ( void  )

Cleanup Opus decoder

Definition at line 850 of file src/web/client.c.

850 {
851 if (g_opus_decoder) {
852 opus_decoder_destroy(g_opus_decoder);
853 g_opus_decoder = NULL;
854 WASM_LOG("Opus decoder cleaned up");
855 }
856}

References WASM_LOG.

◆ client_opus_decoder_init()

EMSCRIPTEN_KEEPALIVE int client_opus_decoder_init ( int  sample_rate,
int  channels 
)

Initialize Opus decoder

Parameters
sample_rateSample rate (8000, 16000, 24000, or 48000)
channelsNumber of channels (1=mono, 2=stereo)
Returns
0 on success, -1 on error

Definition at line 768 of file src/web/client.c.

768 {
769 if (g_opus_decoder) {
770 opus_decoder_destroy(g_opus_decoder);
771 g_opus_decoder = NULL;
772 }
773
774 int error;
775 g_opus_decoder = opus_decoder_create(sample_rate, channels, &error);
776 if (error != OPUS_OK || !g_opus_decoder) {
777 WASM_ERROR("Failed to create Opus decoder");
778 return -1;
779 }
780
781 WASM_LOG("Opus decoder initialized");
782 return 0;
783}

References WASM_ERROR, and WASM_LOG.

◆ client_opus_encode()

EMSCRIPTEN_KEEPALIVE int client_opus_encode ( const int16_t *  pcm_data,
int  frame_size,
uint8_t *  opus_data,
int  max_opus_bytes 
)

Encode PCM audio to Opus

Parameters
pcm_dataInput PCM data (Int16 samples)
frame_sizeNumber of samples per channel
opus_dataOutput buffer for Opus data
max_opus_bytesMaximum size of output buffer
Returns
Number of bytes encoded, or -1 on error

Definition at line 794 of file src/web/client.c.

794 {
795 if (!g_opus_encoder) {
796 WASM_ERROR("Opus encoder not initialized");
797 return -1;
798 }
799
800 int encoded_bytes = opus_encode(g_opus_encoder, pcm_data, frame_size, opus_data, max_opus_bytes);
801 if (encoded_bytes < 0) {
802 WASM_ERROR("Opus encoding failed");
803 return -1;
804 }
805
806 return encoded_bytes;
807}

References WASM_ERROR.

◆ client_opus_encoder_cleanup()

EMSCRIPTEN_KEEPALIVE void client_opus_encoder_cleanup ( void  )

Cleanup Opus encoder

Definition at line 838 of file src/web/client.c.

838 {
839 if (g_opus_encoder) {
840 opus_encoder_destroy(g_opus_encoder);
841 g_opus_encoder = NULL;
842 WASM_LOG("Opus encoder cleaned up");
843 }
844}

References WASM_LOG.

◆ client_opus_encoder_init()

EMSCRIPTEN_KEEPALIVE int client_opus_encoder_init ( int  sample_rate,
int  channels,
int  bitrate 
)

Initialize Opus encoder

Parameters
sample_rateSample rate (8000, 16000, 24000, or 48000)
channelsNumber of channels (1=mono, 2=stereo)
bitrateTarget bitrate in bits/sec
Returns
0 on success, -1 on error

Definition at line 741 of file src/web/client.c.

741 {
742 if (g_opus_encoder) {
743 opus_encoder_destroy(g_opus_encoder);
744 g_opus_encoder = NULL;
745 }
746
747 int error;
748 g_opus_encoder = opus_encoder_create(sample_rate, channels, OPUS_APPLICATION_VOIP, &error);
749 if (error != OPUS_OK || !g_opus_encoder) {
750 WASM_ERROR("Failed to create Opus encoder");
751 return -1;
752 }
753
754 // Set bitrate
755 opus_encoder_ctl(g_opus_encoder, OPUS_SET_BITRATE(bitrate));
756
757 WASM_LOG("Opus encoder initialized");
758 return 0;
759}

References WASM_ERROR, and WASM_LOG.

◆ client_parse_packet()

EMSCRIPTEN_KEEPALIVE char * client_parse_packet ( const uint8_t *  raw_packet,
size_t  packet_len 
)

Parse a raw packet and return JSON metadata

Parameters
raw_packetRaw packet bytes including header
packet_lenLength of raw packet
Returns
Allocated JSON string with packet metadata (caller must free), or NULL on error

Definition at line 609 of file src/web/client.c.

609 {
610 if (!raw_packet || packet_len < sizeof(packet_header_t)) {
611 WASM_ERROR("Invalid packet data");
612 return NULL;
613 }
614
615 // Parse packet header directly from bytes
616 const packet_header_t *header = (const packet_header_t *)raw_packet;
617
618 // Validate magic number (convert from network byte order)
619 uint64_t magic = NET_TO_HOST_U64(header->magic);
620 if (magic != PACKET_MAGIC) {
621 WASM_ERROR("Invalid packet magic number");
622 return NULL;
623 }
624
625 // Convert header fields from network byte order
626 uint16_t type = NET_TO_HOST_U16(header->type);
627 uint32_t length = NET_TO_HOST_U32(header->length);
628 uint32_t client_id = NET_TO_HOST_U32(header->client_id);
629 uint32_t crc32 = NET_TO_HOST_U32(header->crc32);
630
631 // Build JSON response with packet metadata
632 char *json = SAFE_MALLOC(1024, char *);
633 if (!json) {
634 WASM_ERROR("Failed to allocate JSON buffer");
635 return NULL;
636 }
637
638 snprintf(json, 1024, "{\"type\":%u,\"length\":%u,\"client_id\":%u,\"crc32\":%u}", type, length, client_id, crc32);
639
640 return json;
641}

References WASM_ERROR.

◆ client_send_video_frame()

EMSCRIPTEN_KEEPALIVE int client_send_video_frame ( const uint8_t *  rgba_data,
int  width,
int  height 
)

Process a video frame and prepare it for sending

Parameters
rgba_dataRGBA pixel data from canvas
widthFrame width
heightFrame height
Returns
0 on success, -1 on error

Definition at line 691 of file src/web/client.c.

691 {
692 if (!g_initialized) {
693 WASM_ERROR("Client not initialized");
694 return -1;
695 }
696
697 // This is a placeholder - actual implementation would:
698 // 1. Convert RGBA to target format (e.g., compress with libjpeg-turbo)
699 // 2. Build frame packet
700 // 3. Encrypt if handshake complete
701 // 4. Return serialized packet via callback to JS
702
703 WASM_LOG("Video frame processing not yet implemented");
704 return 0;
705}

References WASM_ERROR, and WASM_LOG.

◆ client_serialize_packet()

EMSCRIPTEN_KEEPALIVE int client_serialize_packet ( uint16_t  packet_type,
const uint8_t *  payload,
size_t  payload_len,
uint32_t  client_id,
uint8_t *  output,
size_t *  out_len 
)

Serialize a packet structure to raw bytes

Parameters
packet_typeType of packet
payloadPacket payload data
payload_lenLength of payload
client_idClient ID to include in header
outputOutput buffer (allocated by caller)
out_lenOutput parameter for total packet length
Returns
0 on success, -1 on error

Definition at line 654 of file src/web/client.c.

655 {
656 if (!output || !out_len) {
657 WASM_ERROR("Invalid output parameters");
658 return -1;
659 }
660
661 // Calculate CRC32 of payload (use software version for WASM)
662 uint32_t crc = payload && payload_len > 0 ? asciichat_crc32_sw(payload, payload_len) : 0;
663
664 // Build packet header (convert to network byte order)
665 packet_header_t header = {.magic = HOST_TO_NET_U64(PACKET_MAGIC),
666 .type = HOST_TO_NET_U16(packet_type),
667 .length = HOST_TO_NET_U32((uint32_t)payload_len),
668 .crc32 = HOST_TO_NET_U32(crc),
669 .client_id = HOST_TO_NET_U32(client_id)};
670
671 // Copy header to output
672 memcpy(output, &header, sizeof(packet_header_t));
673
674 // Copy payload to output
675 if (payload && payload_len > 0) {
676 memcpy(output + sizeof(packet_header_t), payload, payload_len);
677 }
678
679 *out_len = sizeof(packet_header_t) + payload_len;
680 return 0;
681}
uint32_t asciichat_crc32_sw(const void *data, size_t len)
Definition crc32.c:171

References asciichat_crc32_sw(), and WASM_ERROR.

◆ client_set_server_address()

EMSCRIPTEN_KEEPALIVE int client_set_server_address ( const char *  server_host,
int  server_port 
)

Set server address for known_hosts verification

Parameters
server_hostServer hostname or IP address
server_portServer port number
Returns
0 on success, -1 on error

Definition at line 278 of file src/web/client.c.

278 {
279 if (!g_initialized) {
280 WASM_ERROR("Client not initialized");
281 return -1;
282 }
283
284 if (!server_host || server_port <= 0 || server_port > 65535) {
285 WASM_ERROR("Invalid server address parameters");
286 return -1;
287 }
288
289 // Set server IP and port in handshake context
290 SAFE_STRNCPY(g_crypto_handshake_ctx.server_ip, server_host, sizeof(g_crypto_handshake_ctx.server_ip));
291 g_crypto_handshake_ctx.server_port = server_port;
292
293 WASM_LOG("Server address set");
294 return 0;
295}

References WASM_ERROR, and WASM_LOG.

◆ EM_JS()

EM_JS ( void  ,
js_send_raw_packet  ,
(const uint8_t *packet_data, size_t packet_len)  ,
{ if(!Module.sendPacketCallback) { console.error('[WASM] sendPacketCallback not registered - cannot send packet');return;} const packetArray=new Uint8Array(HEAPU8.buffer, packet_data, packet_len);const packetCopy=new Uint8Array(packetArray);var pktType=packetCopy.length >=10 ?((packetCopy[8]<< 8)|packetCopy[9]) :-1;console.error('[WASM->JS] Sending raw packet:', packetCopy.length, 'bytes, type=0x'+pktType.toString(16));Module.sendPacketCallback(packetCopy);}   
)

◆ get_help_text()

EMSCRIPTEN_KEEPALIVE const char * get_help_text ( int  mode,
const char *  option_name 
)

Get help text for an option in a specific mode Exported for web client to retrieve tooltips for settings

Parameters
modeThe mode (0=server, 1=client, 2=mirror, etc.)
option_nameThe long name of the option (e.g., "color-mode", "fps")
Returns
Help text string, or NULL if option doesn't apply to mode

Definition at line 866 of file src/web/client.c.

866 {
867 if (!option_name || !option_name[0]) {
868 return NULL;
869 }
870
871 // Convert int mode to asciichat_mode_t
872 asciichat_mode_t mode_enum = (asciichat_mode_t)mode;
873
874 // Call the C API function
875 return options_get_help_text(mode_enum, option_name);
876}
const char * options_get_help_text(asciichat_mode_t mode, const char *option_name)
Get help text for an option in a specific mode.
Definition help_api.c:27

References options_get_help_text().