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

ACIP client-side protocol implementation. More...

Go to the source code of this file.

Functions

asciichat_error_t acip_client_receive_and_dispatch (acip_transport_t *transport, const acip_client_callbacks_t *callbacks)
 
asciichat_error_t acip_send_image_frame (acip_transport_t *transport, const void *pixel_data, uint32_t width, uint32_t height, uint32_t pixel_format)
 
asciichat_error_t acip_send_client_join (acip_transport_t *transport, uint8_t capabilities)
 
asciichat_error_t acip_send_client_leave (acip_transport_t *transport)
 
asciichat_error_t acip_send_stream_start (acip_transport_t *transport, uint8_t stream_types)
 
asciichat_error_t acip_send_stream_stop (acip_transport_t *transport, uint8_t stream_types)
 
asciichat_error_t acip_send_capabilities (acip_transport_t *transport, const void *cap_data, size_t cap_len)
 
asciichat_error_t acip_send_protocol_version (acip_transport_t *transport, const protocol_version_packet_t *version)
 

Detailed Description

ACIP client-side protocol implementation.

Provides client-side ACIP protocol implementation for:

  • Session discovery and management
  • WebRTC signaling relay
  • String reservation (future)

ACIP (ascii-chat IP Protocol) is the wire protocol for session discovery and WebRTC signaling. ACDS is the reference server implementation.

Client-side packet sending and receiving for ascii-chat protocol. Handles client→server communication (image frames, join/leave, streaming control).

Author
Zachary Fogg me@zf.nosp@m.o.gg
Date
January 2026

Definition in file lib/network/acip/client.c.

Function Documentation

◆ acip_client_receive_and_dispatch()

asciichat_error_t acip_client_receive_and_dispatch ( acip_transport_t *  transport,
const acip_client_callbacks_t *  callbacks 
)

Definition at line 28 of file lib/network/acip/client.c.

29 {
30 if (!transport || !callbacks) {
31 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid transport or callbacks");
32 }
33
34 // Check if transport is connected
35 if (!transport->methods->is_connected(transport)) {
36 return SET_ERRNO(ERROR_NETWORK, "Transport not connected");
37 }
38
39 packet_envelope_t envelope;
40 bool enforce_encryption = (transport->crypto_ctx != NULL);
41
42 // Try to get socket from transport
43 socket_t sock = transport->methods->get_socket(transport);
44
45 if (sock != INVALID_SOCKET_VALUE) {
46 // Socket-based transport (TCP): use receive_packet_secure() for socket I/O + parsing
47 packet_recv_result_t result = receive_packet_secure(sock, transport->crypto_ctx, enforce_encryption, &envelope);
48
49 // Handle receive errors
50 if (result != PACKET_RECV_SUCCESS) {
51 if (result == PACKET_RECV_EOF) {
52 return SET_ERRNO(ERROR_NETWORK, "Connection closed (EOF)");
53 } else if (result == PACKET_RECV_SECURITY_VIOLATION) {
54 return SET_ERRNO(ERROR_CRYPTO, "Security violation: unencrypted packet when encryption required");
55 } else {
56 return SET_ERRNO(ERROR_NETWORK, "Failed to receive packet");
57 }
58 }
59 } else {
60 // Non-socket transport (WebRTC): use transport's recv() method to get complete packet
61 void *packet_data = NULL;
62 void *allocated_buffer = NULL;
63 size_t packet_len = 0;
64
65 asciichat_error_t recv_result = transport->methods->recv(transport, &packet_data, &packet_len, &allocated_buffer);
66 if (recv_result != ASCIICHAT_OK) {
67 return SET_ERRNO(ERROR_NETWORK, "Transport recv() failed");
68 }
69
70 // Parse packet header
71 if (packet_len < sizeof(packet_header_t)) {
72 buffer_pool_free(NULL, allocated_buffer, packet_len);
73 return SET_ERRNO(ERROR_NETWORK, "Packet too small: %zu < %zu", packet_len, sizeof(packet_header_t));
74 }
75
76 const packet_header_t *header = (const packet_header_t *)packet_data;
77 envelope.type = NET_TO_HOST_U16(header->type);
78 envelope.len = NET_TO_HOST_U32(header->length);
79 envelope.data = (uint8_t *)packet_data + sizeof(packet_header_t);
80 envelope.allocated_buffer = allocated_buffer;
81 envelope.allocated_size = packet_len;
82 envelope.was_encrypted = false; // WebRTC currently doesn't support encryption in this path
83
84 log_dev_every(4500 * US_PER_MS_INT, "WebRTC received packet: type=%u, len=%u, total_size=%zu", envelope.type,
85 envelope.len, packet_len);
86
87 // Validate packet length
88 if (envelope.len != packet_len - sizeof(packet_header_t)) {
89 buffer_pool_free(NULL, allocated_buffer, packet_len);
90 return SET_ERRNO(ERROR_NETWORK, "Packet length mismatch: header says %u, actual %zu", envelope.len,
91 packet_len - sizeof(packet_header_t));
92 }
93 }
94
95 // Dispatch packet to appropriate ACIP handler
96 asciichat_error_t dispatch_result =
97 acip_handle_client_packet(transport, envelope.type, envelope.data, envelope.len, callbacks);
98
99 if (dispatch_result != ASCIICHAT_OK) {
100 log_error("Packet dispatch failed for type %u: error %d", envelope.type, dispatch_result);
101 } else {
102 log_debug("Packet type %u dispatched successfully", envelope.type);
103 }
104
105 // Always free the allocated buffer (even if handler failed)
106 if (envelope.allocated_buffer) {
107 buffer_pool_free(NULL, envelope.allocated_buffer, envelope.allocated_size);
108 }
109
110 // Return handler result
111 return dispatch_result;
112}
void buffer_pool_free(buffer_pool_t *pool, void *data, size_t size)
int socket_t
asciichat_error_t acip_handle_client_packet(acip_transport_t *transport, packet_type_t type, const void *payload, size_t payload_len, const acip_client_callbacks_t *callbacks)
packet_recv_result_t receive_packet_secure(socket_t sockfd, void *crypto_ctx, bool enforce_encryption, packet_envelope_t *envelope)
Receive a packet with decryption and decompression support.
Definition packet.c:566

References acip_handle_client_packet(), buffer_pool_free(), and receive_packet_secure().

◆ acip_send_capabilities()

asciichat_error_t acip_send_capabilities ( acip_transport_t *  transport,
const void *  cap_data,
size_t  cap_len 
)

Definition at line 209 of file lib/network/acip/client.c.

209 {
210 if (!transport || !cap_data) {
211 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid transport or cap_data");
212 }
213
214 return packet_send_via_transport(transport, PACKET_TYPE_CLIENT_CAPABILITIES, cap_data, cap_len, 0);
215}
asciichat_error_t packet_send_via_transport(acip_transport_t *transport, packet_type_t type, const void *payload, size_t payload_len, uint32_t client_id)
Send packet via transport with proper header (exported for generic wrappers)
Definition send.c:41

References packet_send_via_transport().

◆ acip_send_client_join()

asciichat_error_t acip_send_client_join ( acip_transport_t *  transport,
uint8_t  capabilities 
)

Definition at line 171 of file lib/network/acip/client.c.

171 {
172 if (!transport) {
173 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid transport");
174 }
175
176 // Simple capability byte payload
177 return packet_send_via_transport(transport, PACKET_TYPE_CLIENT_JOIN, &capabilities, sizeof(capabilities), 0);
178}

References packet_send_via_transport().

◆ acip_send_client_leave()

asciichat_error_t acip_send_client_leave ( acip_transport_t *  transport)

Definition at line 180 of file lib/network/acip/client.c.

180 {
181 if (!transport) {
182 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid transport");
183 }
184
185 // Leave has no payload
186 return packet_send_via_transport(transport, PACKET_TYPE_CLIENT_LEAVE, NULL, 0, 0);
187}

References packet_send_via_transport().

◆ acip_send_image_frame()

asciichat_error_t acip_send_image_frame ( acip_transport_t *  transport,
const void *  pixel_data,
uint32_t  width,
uint32_t  height,
uint32_t  pixel_format 
)

Definition at line 118 of file lib/network/acip/client.c.

119 {
120 log_debug("★ ACIP_SEND_IMAGE_FRAME: Called with %ux%u, transport=%p, pixel_data=%p", width, height, (void *)transport,
121 pixel_data);
122
123 if (!transport || !pixel_data) {
124 log_debug("★ ACIP_SEND_IMAGE_FRAME: Invalid params");
125 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid transport or pixel_data");
126 }
127
128 // Calculate pixel data size (3 bytes per pixel for RGB24)
129 size_t pixel_size = (size_t)width * height * 3;
130 log_debug("★ ACIP_SEND_IMAGE_FRAME: pixel_size=%zu", pixel_size);
131
132 // Create image frame packet header
133 image_frame_packet_t header;
134 header.width = HOST_TO_NET_U32(width);
135 header.height = HOST_TO_NET_U32(height);
136 header.pixel_format = HOST_TO_NET_U32(pixel_format);
137 header.compressed_size = 0;
138 header.checksum = 0;
139 header.timestamp = 0;
140
141 // Calculate total size
142 size_t total_size;
143 if (checked_size_add(sizeof(header), pixel_size, &total_size) != ASCIICHAT_OK) {
144 log_debug("★ ACIP_SEND_IMAGE_FRAME: Overflow in size calculation");
145 return SET_ERRNO(ERROR_INVALID_PARAM, "Packet size overflow");
146 }
147
148 log_debug("★ ACIP_SEND_IMAGE_FRAME: total_size=%zu, allocating buffer", total_size);
149
150 // Allocate buffer
151 uint8_t *buffer = buffer_pool_alloc(NULL, total_size);
152 if (!buffer) {
153 log_debug("★ ACIP_SEND_IMAGE_FRAME: Failed to allocate %zu bytes", total_size);
154 return SET_ERRNO(ERROR_MEMORY, "Failed to allocate buffer: %zu bytes", total_size);
155 }
156
157 // Build packet
158 memcpy(buffer, &header, sizeof(header));
159 memcpy(buffer + sizeof(header), pixel_data, pixel_size);
160
161 log_debug("★ ACIP_SEND_IMAGE_FRAME: About to send packet");
162 // Send via transport
163 asciichat_error_t result = packet_send_via_transport(transport, PACKET_TYPE_IMAGE_FRAME, buffer, total_size, 0);
164
165 log_debug("★ ACIP_SEND_IMAGE_FRAME: packet_send_via_transport returned %d", result);
166
167 buffer_pool_free(NULL, buffer, total_size);
168 return result;
169}
void * buffer_pool_alloc(buffer_pool_t *pool, size_t size)
Definition buffer_pool.c:99

References buffer_pool_alloc(), buffer_pool_free(), and packet_send_via_transport().

◆ acip_send_protocol_version()

asciichat_error_t acip_send_protocol_version ( acip_transport_t *  transport,
const protocol_version_packet_t *  version 
)

Definition at line 217 of file lib/network/acip/client.c.

217 {
218 if (!transport || !version) {
219 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid transport or version");
220 }
221
222 return packet_send_via_transport(transport, PACKET_TYPE_PROTOCOL_VERSION, version, sizeof(*version), 0);
223}

References packet_send_via_transport().

◆ acip_send_stream_start()

asciichat_error_t acip_send_stream_start ( acip_transport_t *  transport,
uint8_t  stream_types 
)

Definition at line 189 of file lib/network/acip/client.c.

189 {
190 if (!transport) {
191 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid transport");
192 }
193
194 // Server expects uint32_t (4 bytes), not uint8_t
195 uint32_t stream_types_net = HOST_TO_NET_U32((uint32_t)stream_types);
196 return packet_send_via_transport(transport, PACKET_TYPE_STREAM_START, &stream_types_net, sizeof(stream_types_net), 0);
197}

References packet_send_via_transport().

◆ acip_send_stream_stop()

asciichat_error_t acip_send_stream_stop ( acip_transport_t *  transport,
uint8_t  stream_types 
)

Definition at line 199 of file lib/network/acip/client.c.

199 {
200 if (!transport) {
201 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid transport");
202 }
203
204 // Server expects uint32_t (4 bytes), not uint8_t
205 uint32_t stream_types_net = HOST_TO_NET_U32((uint32_t)stream_types);
206 return packet_send_via_transport(transport, PACKET_TYPE_STREAM_STOP, &stream_types_net, sizeof(stream_types_net), 0);
207}

References packet_send_via_transport().