/work/toxcore/tox_events.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* SPDX-License-Identifier: GPL-3.0-or-later |
2 | | * Copyright © 2022-2026 The TokTok team. |
3 | | */ |
4 | | |
5 | | #include "tox_events.h" |
6 | | |
7 | | #include <assert.h> |
8 | | #include <string.h> |
9 | | |
10 | | #include "attributes.h" |
11 | | #include "bin_pack.h" |
12 | | #include "bin_unpack.h" |
13 | | #include "ccompat.h" |
14 | | #include "events/events_alloc.h" |
15 | | #include "logger.h" |
16 | | #include "mem.h" |
17 | | #include "tox.h" |
18 | | #include "tox_event.h" |
19 | | #include "tox_private.h" |
20 | | #include "tox_struct.h" // IWYU pragma: keep |
21 | | |
22 | | /***************************************************** |
23 | | * |
24 | | * :: Set up event handlers. |
25 | | * |
26 | | *****************************************************/ |
27 | | |
28 | | void tox_events_init(Tox *tox) |
29 | 372 | { |
30 | 372 | tox_callback_conference_connected(tox, tox_events_handle_conference_connected); |
31 | 372 | tox_callback_conference_invite(tox, tox_events_handle_conference_invite); |
32 | 372 | tox_callback_conference_message(tox, tox_events_handle_conference_message); |
33 | 372 | tox_callback_conference_peer_list_changed(tox, tox_events_handle_conference_peer_list_changed); |
34 | 372 | tox_callback_conference_peer_name(tox, tox_events_handle_conference_peer_name); |
35 | 372 | tox_callback_conference_title(tox, tox_events_handle_conference_title); |
36 | 372 | tox_callback_file_chunk_request(tox, tox_events_handle_file_chunk_request); |
37 | 372 | tox_callback_file_recv_chunk(tox, tox_events_handle_file_recv_chunk); |
38 | 372 | tox_callback_file_recv_control(tox, tox_events_handle_file_recv_control); |
39 | 372 | tox_callback_file_recv(tox, tox_events_handle_file_recv); |
40 | 372 | tox_callback_friend_connection_status(tox, tox_events_handle_friend_connection_status); |
41 | 372 | tox_callback_friend_lossless_packet(tox, tox_events_handle_friend_lossless_packet); |
42 | 372 | tox_callback_friend_lossy_packet(tox, tox_events_handle_friend_lossy_packet); |
43 | 372 | tox_callback_friend_message(tox, tox_events_handle_friend_message); |
44 | 372 | tox_callback_friend_name(tox, tox_events_handle_friend_name); |
45 | 372 | tox_callback_friend_read_receipt(tox, tox_events_handle_friend_read_receipt); |
46 | 372 | tox_callback_friend_request(tox, tox_events_handle_friend_request); |
47 | 372 | tox_callback_friend_status_message(tox, tox_events_handle_friend_status_message); |
48 | 372 | tox_callback_friend_status(tox, tox_events_handle_friend_status); |
49 | 372 | tox_callback_friend_typing(tox, tox_events_handle_friend_typing); |
50 | 372 | tox_callback_self_connection_status(tox, tox_events_handle_self_connection_status); |
51 | 372 | tox_callback_group_peer_name(tox, tox_events_handle_group_peer_name); |
52 | 372 | tox_callback_group_peer_status(tox, tox_events_handle_group_peer_status); |
53 | 372 | tox_callback_group_topic(tox, tox_events_handle_group_topic); |
54 | 372 | tox_callback_group_privacy_state(tox, tox_events_handle_group_privacy_state); |
55 | 372 | tox_callback_group_voice_state(tox, tox_events_handle_group_voice_state); |
56 | 372 | tox_callback_group_topic_lock(tox, tox_events_handle_group_topic_lock); |
57 | 372 | tox_callback_group_peer_limit(tox, tox_events_handle_group_peer_limit); |
58 | 372 | tox_callback_group_password(tox, tox_events_handle_group_password); |
59 | 372 | tox_callback_group_message(tox, tox_events_handle_group_message); |
60 | 372 | tox_callback_group_private_message(tox, tox_events_handle_group_private_message); |
61 | 372 | tox_callback_group_custom_packet(tox, tox_events_handle_group_custom_packet); |
62 | 372 | tox_callback_group_custom_private_packet(tox, tox_events_handle_group_custom_private_packet); |
63 | 372 | tox_callback_group_invite(tox, tox_events_handle_group_invite); |
64 | 372 | tox_callback_group_peer_join(tox, tox_events_handle_group_peer_join); |
65 | 372 | tox_callback_group_peer_exit(tox, tox_events_handle_group_peer_exit); |
66 | 372 | tox_callback_group_self_join(tox, tox_events_handle_group_self_join); |
67 | 372 | tox_callback_group_join_fail(tox, tox_events_handle_group_join_fail); |
68 | 372 | tox_callback_group_moderation(tox, tox_events_handle_group_moderation); |
69 | 372 | tox_callback_dht_nodes_response(tox, tox_events_handle_dht_nodes_response); |
70 | 372 | } |
71 | | |
72 | | uint32_t tox_events_get_size(const Tox_Events *events) |
73 | 360k | { |
74 | 360k | return events == nullptr ? 0 : events->events_size; |
75 | 360k | } |
76 | | |
77 | | static const Tox_Event *_Nullable tox_events_get_events(const Tox_Events *_Nullable events) |
78 | 3.17k | { |
79 | 3.17k | return events == nullptr ? nullptr : events->events; |
80 | 3.17k | } |
81 | | |
82 | | const Tox_Event *tox_events_get(const Tox_Events *events, uint32_t index) |
83 | 345k | { |
84 | 345k | if (index >= tox_events_get_size(events)) { |
85 | 0 | return nullptr; |
86 | 0 | } |
87 | | |
88 | 345k | return &events->events[index]; |
89 | 345k | } |
90 | | |
91 | | Tox_Events *tox_events_iterate(Tox *tox, const Tox_Iterate_Options *options, Tox_Err_Events_Iterate *error) |
92 | 119k | { |
93 | 119k | const Tox_System *sys = tox_get_system(tox); |
94 | 119k | Tox_Events_State state = {TOX_ERR_EVENTS_ITERATE_OK, sys->mem, nullptr}; |
95 | | |
96 | 119k | tox_iterate_with_options(tox, options, &state); |
97 | | |
98 | 119k | if (error != nullptr) { |
99 | 119k | *error = state.error; |
100 | 119k | } |
101 | | |
102 | 119k | const bool fail_hard = tox_iterate_options_get_fail_hard(options); |
103 | | |
104 | 119k | if (fail_hard && state.error != TOX_ERR_EVENTS_ITERATE_OK) { |
105 | 0 | tox_events_free(state.events); |
106 | 0 | return nullptr; |
107 | 0 | } |
108 | | |
109 | 119k | return state.events; |
110 | 119k | } |
111 | | |
112 | | static bool tox_event_pack_handler(const void *_Nonnull arr, uint32_t index, const Logger *_Nonnull logger, Bin_Pack *_Nonnull bp) |
113 | 1.85k | { |
114 | 1.85k | const Tox_Event *events = (const Tox_Event *)arr; |
115 | 1.85k | assert(events != nullptr); |
116 | 1.85k | return tox_event_pack(&events[index], bp); |
117 | 1.85k | } |
118 | | |
119 | | static bool tox_events_pack_handler(const void *_Nullable obj, const Logger *_Nullable logger, Bin_Pack *_Nonnull bp) |
120 | 3.17k | { |
121 | 3.17k | const Tox_Events *events = (const Tox_Events *)obj; |
122 | 3.17k | return bin_pack_obj_array(bp, tox_event_pack_handler, tox_events_get_events(events), tox_events_get_size(events), logger); |
123 | 3.17k | } |
124 | | |
125 | | uint32_t tox_events_bytes_size(const Tox_Events *events) |
126 | 1.39k | { |
127 | 1.39k | return bin_pack_obj_size(tox_events_pack_handler, events, nullptr); |
128 | 1.39k | } |
129 | | |
130 | | bool tox_events_get_bytes(const Tox_Events *events, uint8_t *bytes) |
131 | 1.78k | { |
132 | 1.78k | return bin_pack_obj(tox_events_pack_handler, events, nullptr, bytes, UINT32_MAX); |
133 | 1.78k | } |
134 | | |
135 | | static bool tox_events_unpack_handler(void *_Nonnull obj, Bin_Unpack *_Nonnull bu) |
136 | 1.09k | { |
137 | 1.09k | Tox_Events *events = (Tox_Events *)obj; |
138 | | |
139 | 1.09k | uint32_t size; |
140 | 1.09k | if (!bin_unpack_array(bu, &size)) { |
141 | 68 | return false; |
142 | 68 | } |
143 | | |
144 | 2.32k | for (uint32_t i = 0; i < size; ++i) { |
145 | 1.74k | Tox_Event event = {TOX_EVENT_INVALID}; |
146 | 1.74k | if (!tox_event_unpack_into(&event, bu, events->mem)) { |
147 | 437 | tox_event_destruct(&event, events->mem); |
148 | 437 | return false; |
149 | 437 | } |
150 | | |
151 | 1.30k | if (!tox_events_add(events, &event)) { |
152 | 2 | tox_event_destruct(&event, events->mem); |
153 | 2 | return false; |
154 | 2 | } |
155 | 1.30k | } |
156 | | |
157 | | // Invariant: if all adds worked, the events size must be the input array size. |
158 | 584 | assert(tox_events_get_size(events) == size); |
159 | 584 | return true; |
160 | 584 | } |
161 | | |
162 | | Tox_Events *tox_events_load(const Tox_System *sys, const uint8_t *bytes, uint32_t bytes_size) |
163 | 1.09k | { |
164 | 1.09k | Tox_Events *events = (Tox_Events *)mem_alloc(sys->mem, sizeof(Tox_Events)); |
165 | | |
166 | 1.09k | if (events == nullptr) { |
167 | 1 | return nullptr; |
168 | 1 | } |
169 | | |
170 | 1.09k | *events = (Tox_Events) { |
171 | 1.09k | nullptr |
172 | 1.09k | }; |
173 | 1.09k | events->mem = sys->mem; |
174 | | |
175 | 1.09k | if (!bin_unpack_obj(sys->mem, tox_events_unpack_handler, events, bytes, bytes_size)) { |
176 | 507 | tox_events_free(events); |
177 | 507 | return nullptr; |
178 | 507 | } |
179 | | |
180 | 584 | return events; |
181 | 1.09k | } |
182 | | |
183 | | void tox_events_dispatch(Tox *tox, Tox_Events *events, void *user_data) |
184 | 0 | { |
185 | 0 | if (events == nullptr) { |
186 | 0 | return; |
187 | 0 | } |
188 | | |
189 | 0 | const uint32_t size = tox_events_get_size(events); |
190 | |
|
191 | 0 | for (uint32_t i = 0; i < size; ++i) { |
192 | 0 | const Tox_Event *event = tox_events_get(events, i); |
193 | 0 | const Tox_Event_Type type = tox_event_get_type(event); |
194 | |
|
195 | 0 | switch (type) { |
196 | 0 | case TOX_EVENT_SELF_CONNECTION_STATUS: { |
197 | 0 | tox_events_handle_self_connection_status_dispatch(tox, event->data.self_connection_status, user_data); |
198 | 0 | break; |
199 | 0 | } |
200 | | |
201 | 0 | case TOX_EVENT_FRIEND_REQUEST: { |
202 | 0 | tox_events_handle_friend_request_dispatch(tox, event->data.friend_request, user_data); |
203 | 0 | break; |
204 | 0 | } |
205 | | |
206 | 0 | case TOX_EVENT_FRIEND_CONNECTION_STATUS: { |
207 | 0 | tox_events_handle_friend_connection_status_dispatch(tox, event->data.friend_connection_status, user_data); |
208 | 0 | break; |
209 | 0 | } |
210 | | |
211 | 0 | case TOX_EVENT_FRIEND_LOSSY_PACKET: { |
212 | 0 | tox_events_handle_friend_lossy_packet_dispatch(tox, event->data.friend_lossy_packet, user_data); |
213 | 0 | break; |
214 | 0 | } |
215 | | |
216 | 0 | case TOX_EVENT_FRIEND_LOSSLESS_PACKET: { |
217 | 0 | tox_events_handle_friend_lossless_packet_dispatch(tox, event->data.friend_lossless_packet, user_data); |
218 | 0 | break; |
219 | 0 | } |
220 | | |
221 | 0 | case TOX_EVENT_FRIEND_NAME: { |
222 | 0 | tox_events_handle_friend_name_dispatch(tox, event->data.friend_name, user_data); |
223 | 0 | break; |
224 | 0 | } |
225 | | |
226 | 0 | case TOX_EVENT_FRIEND_STATUS: { |
227 | 0 | tox_events_handle_friend_status_dispatch(tox, event->data.friend_status, user_data); |
228 | 0 | break; |
229 | 0 | } |
230 | | |
231 | 0 | case TOX_EVENT_FRIEND_STATUS_MESSAGE: { |
232 | 0 | tox_events_handle_friend_status_message_dispatch(tox, event->data.friend_status_message, user_data); |
233 | 0 | break; |
234 | 0 | } |
235 | | |
236 | 0 | case TOX_EVENT_FRIEND_MESSAGE: { |
237 | 0 | tox_events_handle_friend_message_dispatch(tox, event->data.friend_message, user_data); |
238 | 0 | break; |
239 | 0 | } |
240 | | |
241 | 0 | case TOX_EVENT_FRIEND_READ_RECEIPT: { |
242 | 0 | tox_events_handle_friend_read_receipt_dispatch(tox, event->data.friend_read_receipt, user_data); |
243 | 0 | break; |
244 | 0 | } |
245 | | |
246 | 0 | case TOX_EVENT_FRIEND_TYPING: { |
247 | 0 | tox_events_handle_friend_typing_dispatch(tox, event->data.friend_typing, user_data); |
248 | 0 | break; |
249 | 0 | } |
250 | | |
251 | 0 | case TOX_EVENT_FILE_CHUNK_REQUEST: { |
252 | 0 | tox_events_handle_file_chunk_request_dispatch(tox, event->data.file_chunk_request, user_data); |
253 | 0 | break; |
254 | 0 | } |
255 | | |
256 | 0 | case TOX_EVENT_FILE_RECV: { |
257 | 0 | tox_events_handle_file_recv_dispatch(tox, event->data.file_recv, user_data); |
258 | 0 | break; |
259 | 0 | } |
260 | | |
261 | 0 | case TOX_EVENT_FILE_RECV_CHUNK: { |
262 | 0 | tox_events_handle_file_recv_chunk_dispatch(tox, event->data.file_recv_chunk, user_data); |
263 | 0 | break; |
264 | 0 | } |
265 | | |
266 | 0 | case TOX_EVENT_FILE_RECV_CONTROL: { |
267 | 0 | tox_events_handle_file_recv_control_dispatch(tox, event->data.file_recv_control, user_data); |
268 | 0 | break; |
269 | 0 | } |
270 | | |
271 | 0 | case TOX_EVENT_CONFERENCE_INVITE: { |
272 | 0 | tox_events_handle_conference_invite_dispatch(tox, event->data.conference_invite, user_data); |
273 | 0 | break; |
274 | 0 | } |
275 | | |
276 | 0 | case TOX_EVENT_CONFERENCE_CONNECTED: { |
277 | 0 | tox_events_handle_conference_connected_dispatch(tox, event->data.conference_connected, user_data); |
278 | 0 | break; |
279 | 0 | } |
280 | | |
281 | 0 | case TOX_EVENT_CONFERENCE_PEER_LIST_CHANGED: { |
282 | 0 | tox_events_handle_conference_peer_list_changed_dispatch(tox, event->data.conference_peer_list_changed, user_data); |
283 | 0 | break; |
284 | 0 | } |
285 | | |
286 | 0 | case TOX_EVENT_CONFERENCE_PEER_NAME: { |
287 | 0 | tox_events_handle_conference_peer_name_dispatch(tox, event->data.conference_peer_name, user_data); |
288 | 0 | break; |
289 | 0 | } |
290 | | |
291 | 0 | case TOX_EVENT_CONFERENCE_TITLE: { |
292 | 0 | tox_events_handle_conference_title_dispatch(tox, event->data.conference_title, user_data); |
293 | 0 | break; |
294 | 0 | } |
295 | | |
296 | 0 | case TOX_EVENT_CONFERENCE_MESSAGE: { |
297 | 0 | tox_events_handle_conference_message_dispatch(tox, event->data.conference_message, user_data); |
298 | 0 | break; |
299 | 0 | } |
300 | | |
301 | 0 | case TOX_EVENT_GROUP_PEER_NAME: { |
302 | 0 | tox_events_handle_group_peer_name_dispatch(tox, event->data.group_peer_name, user_data); |
303 | 0 | break; |
304 | 0 | } |
305 | | |
306 | 0 | case TOX_EVENT_GROUP_PEER_STATUS: { |
307 | 0 | tox_events_handle_group_peer_status_dispatch(tox, event->data.group_peer_status, user_data); |
308 | 0 | break; |
309 | 0 | } |
310 | | |
311 | 0 | case TOX_EVENT_GROUP_TOPIC: { |
312 | 0 | tox_events_handle_group_topic_dispatch(tox, event->data.group_topic, user_data); |
313 | 0 | break; |
314 | 0 | } |
315 | | |
316 | 0 | case TOX_EVENT_GROUP_PRIVACY_STATE: { |
317 | 0 | tox_events_handle_group_privacy_state_dispatch(tox, event->data.group_privacy_state, user_data); |
318 | 0 | break; |
319 | 0 | } |
320 | | |
321 | 0 | case TOX_EVENT_GROUP_VOICE_STATE: { |
322 | 0 | tox_events_handle_group_voice_state_dispatch(tox, event->data.group_voice_state, user_data); |
323 | 0 | break; |
324 | 0 | } |
325 | | |
326 | 0 | case TOX_EVENT_GROUP_TOPIC_LOCK: { |
327 | 0 | tox_events_handle_group_topic_lock_dispatch(tox, event->data.group_topic_lock, user_data); |
328 | 0 | break; |
329 | 0 | } |
330 | | |
331 | 0 | case TOX_EVENT_GROUP_PEER_LIMIT: { |
332 | 0 | tox_events_handle_group_peer_limit_dispatch(tox, event->data.group_peer_limit, user_data); |
333 | 0 | break; |
334 | 0 | } |
335 | | |
336 | 0 | case TOX_EVENT_GROUP_PASSWORD: { |
337 | 0 | tox_events_handle_group_password_dispatch(tox, event->data.group_password, user_data); |
338 | 0 | break; |
339 | 0 | } |
340 | | |
341 | 0 | case TOX_EVENT_GROUP_MESSAGE: { |
342 | 0 | tox_events_handle_group_message_dispatch(tox, event->data.group_message, user_data); |
343 | 0 | break; |
344 | 0 | } |
345 | | |
346 | 0 | case TOX_EVENT_GROUP_PRIVATE_MESSAGE: { |
347 | 0 | tox_events_handle_group_private_message_dispatch(tox, event->data.group_private_message, user_data); |
348 | 0 | break; |
349 | 0 | } |
350 | | |
351 | 0 | case TOX_EVENT_GROUP_CUSTOM_PACKET: { |
352 | 0 | tox_events_handle_group_custom_packet_dispatch(tox, event->data.group_custom_packet, user_data); |
353 | 0 | break; |
354 | 0 | } |
355 | | |
356 | 0 | case TOX_EVENT_GROUP_CUSTOM_PRIVATE_PACKET: { |
357 | 0 | tox_events_handle_group_custom_private_packet_dispatch(tox, event->data.group_custom_private_packet, user_data); |
358 | 0 | break; |
359 | 0 | } |
360 | | |
361 | 0 | case TOX_EVENT_GROUP_INVITE: { |
362 | 0 | tox_events_handle_group_invite_dispatch(tox, event->data.group_invite, user_data); |
363 | 0 | break; |
364 | 0 | } |
365 | | |
366 | 0 | case TOX_EVENT_GROUP_PEER_JOIN: { |
367 | 0 | tox_events_handle_group_peer_join_dispatch(tox, event->data.group_peer_join, user_data); |
368 | 0 | break; |
369 | 0 | } |
370 | | |
371 | 0 | case TOX_EVENT_GROUP_PEER_EXIT: { |
372 | 0 | tox_events_handle_group_peer_exit_dispatch(tox, event->data.group_peer_exit, user_data); |
373 | 0 | break; |
374 | 0 | } |
375 | | |
376 | 0 | case TOX_EVENT_GROUP_SELF_JOIN: { |
377 | 0 | tox_events_handle_group_self_join_dispatch(tox, event->data.group_self_join, user_data); |
378 | 0 | break; |
379 | 0 | } |
380 | | |
381 | 0 | case TOX_EVENT_GROUP_JOIN_FAIL: { |
382 | 0 | tox_events_handle_group_join_fail_dispatch(tox, event->data.group_join_fail, user_data); |
383 | 0 | break; |
384 | 0 | } |
385 | | |
386 | 0 | case TOX_EVENT_GROUP_MODERATION: { |
387 | 0 | tox_events_handle_group_moderation_dispatch(tox, event->data.group_moderation, user_data); |
388 | 0 | break; |
389 | 0 | } |
390 | | |
391 | 0 | case TOX_EVENT_DHT_NODES_RESPONSE: { |
392 | 0 | tox_events_handle_dht_nodes_response_dispatch(tox, event->data.dht_nodes_response, user_data); |
393 | 0 | break; |
394 | 0 | } |
395 | | |
396 | 0 | case TOX_EVENT_INVALID: |
397 | 0 | break; |
398 | 0 | } |
399 | 0 | } |
400 | 0 | } |
401 | | |
402 | | bool tox_events_equal(const Tox_System *sys, const Tox_Events *a, const Tox_Events *b) |
403 | 403 | { |
404 | 403 | assert(sys != nullptr); |
405 | 403 | assert(sys->mem != nullptr); |
406 | | |
407 | 403 | const uint32_t a_size = tox_events_bytes_size(a); |
408 | 403 | const uint32_t b_size = tox_events_bytes_size(b); |
409 | | |
410 | 403 | if (a_size != b_size) { |
411 | 0 | return false; |
412 | 0 | } |
413 | | |
414 | 403 | uint8_t *a_bytes = (uint8_t *)mem_balloc(sys->mem, a_size); |
415 | 403 | uint8_t *b_bytes = (uint8_t *)mem_balloc(sys->mem, b_size); |
416 | | |
417 | 403 | if (a_bytes == nullptr || b_bytes == nullptr) { |
418 | 0 | mem_delete(sys->mem, b_bytes); |
419 | 0 | mem_delete(sys->mem, a_bytes); |
420 | 0 | return false; |
421 | 0 | } |
422 | | |
423 | 403 | tox_events_get_bytes(a, a_bytes); |
424 | 403 | tox_events_get_bytes(b, b_bytes); |
425 | | |
426 | 403 | const bool ret = memcmp(a_bytes, b_bytes, a_size) == 0; |
427 | | |
428 | 403 | mem_delete(sys->mem, b_bytes); |
429 | 403 | mem_delete(sys->mem, a_bytes); |
430 | | |
431 | 403 | return ret; |
432 | 403 | } |