Coverage Report

Created: 2026-10-24 08:16

/work/toxcore/tox.c
Line
Count
Source (jump to first uncovered line)
1
/* SPDX-License-Identifier: GPL-3.0-or-later
2
 * Copyright © 2016-2026 The TokTok team.
3
 * Copyright © 2013 Tox project.
4
 */
5
6
/**
7
 * The Tox public API.
8
 */
9
#ifndef _XOPEN_SOURCE
10
#define _XOPEN_SOURCE 600
11
#endif /* _XOPEN_SOURCE */
12
13
#include "tox.h"
14
15
#include <assert.h>
16
#include <stdlib.h>
17
#include <string.h>
18
19
#include "DHT.h"
20
#include "Messenger.h"
21
#include "TCP_client.h"
22
#include "attributes.h"
23
#include "ccompat.h"
24
#include "crypto_core.h"
25
#include "friend_requests.h"
26
#include "group.h"
27
#include "group_chats.h"
28
#include "group_common.h"
29
#include "logger.h"
30
#include "mem.h"
31
#include "mono_time.h"
32
#include "net.h"
33
#include "net_crypto.h"
34
#include "network.h"
35
#include "onion_client.h"
36
#include "state.h"
37
#include "tox_log_level.h"
38
#include "tox_options.h"
39
#include "tox_private.h"
40
#include "tox_struct.h" // IWYU pragma: keep
41
#include "util.h"
42
43
#include "../toxencryptsave/defines.h"
44
45
#define SET_ERROR_PARAMETER(param, x) \
46
321k
    do {                              \
47
321k
        if (param != nullptr) {       \
48
314k
            *param = x;               \
49
314k
        }                             \
50
321k
    } while (0)
51
52
static_assert(TOX_HASH_LENGTH == CRYPTO_SHA256_SIZE,
53
              "TOX_HASH_LENGTH is assumed to be equal to CRYPTO_SHA256_SIZE");
54
static_assert(FILE_ID_LENGTH == CRYPTO_SYMMETRIC_KEY_SIZE,
55
              "FILE_ID_LENGTH is assumed to be equal to CRYPTO_SYMMETRIC_KEY_SIZE");
56
static_assert(TOX_DHT_NODE_IP_STRING_SIZE == IP_NTOA_LEN,
57
              "TOX_DHT_NODE_IP_STRING_SIZE is assumed to be equal to IP_NTOA_LEN");
58
static_assert(TOX_GROUP_PEER_IP_STRING_MAX_LENGTH == IP_NTOA_LEN,
59
              "TOX_GROUP_PEER_IP_STRING_MAX_LENGTH is assumed to be equal to IP_NTOA_LEN");
60
static_assert(TOX_DHT_NODE_PUBLIC_KEY_SIZE == CRYPTO_PUBLIC_KEY_SIZE,
61
              "TOX_DHT_NODE_PUBLIC_KEY_SIZE is assumed to be equal to CRYPTO_PUBLIC_KEY_SIZE");
62
static_assert(TOX_FILE_ID_LENGTH == CRYPTO_SYMMETRIC_KEY_SIZE,
63
              "TOX_FILE_ID_LENGTH is assumed to be equal to CRYPTO_SYMMETRIC_KEY_SIZE");
64
static_assert(TOX_FILE_ID_LENGTH == TOX_HASH_LENGTH,
65
              "TOX_FILE_ID_LENGTH is assumed to be equal to TOX_HASH_LENGTH");
66
static_assert(TOX_PUBLIC_KEY_SIZE == CRYPTO_PUBLIC_KEY_SIZE,
67
              "TOX_PUBLIC_KEY_SIZE is assumed to be equal to CRYPTO_PUBLIC_KEY_SIZE");
68
static_assert(TOX_SECRET_KEY_SIZE == CRYPTO_SECRET_KEY_SIZE,
69
              "TOX_SECRET_KEY_SIZE is assumed to be equal to CRYPTO_SECRET_KEY_SIZE");
70
static_assert(TOX_MAX_NAME_LENGTH == MAX_NAME_LENGTH,
71
              "TOX_MAX_NAME_LENGTH is assumed to be equal to MAX_NAME_LENGTH");
72
static_assert(TOX_MAX_STATUS_MESSAGE_LENGTH == MAX_STATUSMESSAGE_LENGTH,
73
              "TOX_MAX_STATUS_MESSAGE_LENGTH is assumed to be equal to MAX_STATUSMESSAGE_LENGTH");
74
static_assert(TOX_GROUP_MAX_MESSAGE_LENGTH == GROUP_MAX_MESSAGE_LENGTH,
75
              "TOX_GROUP_MAX_MESSAGE_LENGTH is assumed to be equal to GROUP_MAX_MESSAGE_LENGTH");
76
static_assert(TOX_MAX_CUSTOM_PACKET_SIZE == MAX_GC_CUSTOM_LOSSLESS_PACKET_SIZE,
77
              "TOX_MAX_CUSTOM_PACKET_SIZE is assumed to be equal to MAX_GC_CUSTOM_LOSSLESS_PACKET_SIZE");
78
79
struct Tox_Userdata {
80
    Tox *_Nonnull tox;
81
    void *_Nullable user_data;
82
};
83
84
static logger_cb tox_log_handler;
85
static void tox_log_handler(void *context, Logger_Level level, const char *file, uint32_t line, const char *func,
86
                            const char *message, void *userdata)
87
1.42M
{
88
1.42M
    Tox *tox = (Tox *)context;
89
1.42M
    assert(tox != nullptr);
90
91
1.42M
    if (tox->log_callback != nullptr) {
92
1.29M
        tox->log_callback(tox, (Tox_Log_Level)level, file, line, func, message, userdata);
93
1.29M
    }
94
1.42M
}
95
96
static m_self_connection_status_cb tox_self_connection_status_handler;
97
static void tox_self_connection_status_handler(Messenger *m, Onion_Connection_Status connection_status, void *user_data)
98
362
{
99
362
    struct Tox_Userdata *tox_data = (struct Tox_Userdata *)user_data;
100
362
    if (tox_data->tox->self_connection_status_callback != nullptr) {
101
338
        tox_unlock(tox_data->tox);
102
338
        tox_data->tox->self_connection_status_callback(tox_data->tox, (Tox_Connection)connection_status, tox_data->user_data);
103
338
        tox_lock(tox_data->tox);
104
338
    }
105
362
}
106
107
static m_friend_name_cb tox_friend_name_handler;
108
static void tox_friend_name_handler(Messenger *m, uint32_t friend_number, const uint8_t *name, size_t length,
109
                                    void *user_data)
110
435
{
111
435
    struct Tox_Userdata *tox_data = (struct Tox_Userdata *)user_data;
112
435
    if (tox_data->tox->friend_name_callback != nullptr) {
113
435
        tox_unlock(tox_data->tox);
114
435
        tox_data->tox->friend_name_callback(tox_data->tox, friend_number, name, length, tox_data->user_data);
115
435
        tox_lock(tox_data->tox);
116
435
    }
117
435
}
118
119
static m_friend_status_message_cb tox_friend_status_message_handler;
120
static void tox_friend_status_message_handler(Messenger *m, uint32_t friend_number, const uint8_t *message,
121
        size_t length, void *user_data)
122
434
{
123
434
    struct Tox_Userdata *tox_data = (struct Tox_Userdata *)user_data;
124
434
    if (tox_data->tox->friend_status_message_callback != nullptr) {
125
434
        tox_unlock(tox_data->tox);
126
434
        tox_data->tox->friend_status_message_callback(tox_data->tox, friend_number, message, length, tox_data->user_data);
127
434
        tox_lock(tox_data->tox);
128
434
    }
129
434
}
130
131
static m_friend_status_cb tox_friend_status_handler;
132
static void tox_friend_status_handler(Messenger *m, uint32_t friend_number, unsigned int status, void *user_data)
133
436
{
134
436
    struct Tox_Userdata *tox_data = (struct Tox_Userdata *)user_data;
135
436
    if (tox_data->tox->friend_status_callback != nullptr) {
136
436
        tox_unlock(tox_data->tox);
137
436
        tox_data->tox->friend_status_callback(tox_data->tox, friend_number, (Tox_User_Status)status, tox_data->user_data);
138
436
        tox_lock(tox_data->tox);
139
436
    }
140
436
}
141
142
static m_friend_connection_status_cb tox_friend_connection_status_handler;
143
static void tox_friend_connection_status_handler(Messenger *m, uint32_t friend_number, unsigned int connection_status,
144
        void *user_data)
145
450
{
146
450
    struct Tox_Userdata *tox_data = (struct Tox_Userdata *)user_data;
147
450
    if (tox_data->tox->friend_connection_status_callback != nullptr) {
148
450
        tox_unlock(tox_data->tox);
149
450
        tox_data->tox->friend_connection_status_callback(tox_data->tox, friend_number, (Tox_Connection)connection_status,
150
450
                tox_data->user_data);
151
450
        tox_lock(tox_data->tox);
152
450
    }
153
450
}
154
155
static m_friend_typing_cb tox_friend_typing_handler;
156
static void tox_friend_typing_handler(Messenger *m, uint32_t friend_number, bool is_typing, void *user_data)
157
435
{
158
435
    struct Tox_Userdata *tox_data = (struct Tox_Userdata *)user_data;
159
435
    if (tox_data->tox->friend_typing_callback != nullptr) {
160
435
        tox_unlock(tox_data->tox);
161
435
        tox_data->tox->friend_typing_callback(tox_data->tox, friend_number, is_typing, tox_data->user_data);
162
435
        tox_lock(tox_data->tox);
163
435
    }
164
435
}
165
166
static m_friend_read_receipt_cb tox_friend_read_receipt_handler;
167
static void tox_friend_read_receipt_handler(Messenger *m, uint32_t friend_number, uint32_t message_id, void *user_data)
168
81.7k
{
169
81.7k
    struct Tox_Userdata *tox_data = (struct Tox_Userdata *)user_data;
170
81.7k
    if (tox_data->tox->friend_read_receipt_callback != nullptr) {
171
81.7k
        tox_unlock(tox_data->tox);
172
81.7k
        tox_data->tox->friend_read_receipt_callback(tox_data->tox, friend_number, message_id, tox_data->user_data);
173
81.7k
        tox_lock(tox_data->tox);
174
81.7k
    }
175
81.7k
}
176
177
static m_friend_request_cb tox_friend_request_handler;
178
static void tox_friend_request_handler(Messenger *m, const Tox_Public_Key public_key, const uint8_t *message, size_t length,
179
                                       void *user_data)
180
137
{
181
137
    struct Tox_Userdata *tox_data = (struct Tox_Userdata *)user_data;
182
137
    if (tox_data->tox->friend_request_callback != nullptr) {
183
137
        tox_unlock(tox_data->tox);
184
137
        tox_data->tox->friend_request_callback(tox_data->tox, public_key, message, length, tox_data->user_data);
185
137
        tox_lock(tox_data->tox);
186
137
    }
187
137
}
188
189
static m_friend_message_cb tox_friend_message_handler;
190
static void tox_friend_message_handler(Messenger *m, uint32_t friend_number, unsigned int message_type,
191
                                       const uint8_t *message, size_t length, void *user_data)
192
82.7k
{
193
82.7k
    struct Tox_Userdata *tox_data = (struct Tox_Userdata *)user_data;
194
82.7k
    if (tox_data->tox->friend_message_callback != nullptr) {
195
82.7k
        tox_unlock(tox_data->tox);
196
82.7k
        tox_data->tox->friend_message_callback(tox_data->tox, friend_number, (Tox_Message_Type)message_type, message, length,
197
82.7k
                                               tox_data->user_data);
198
82.7k
        tox_lock(tox_data->tox);
199
82.7k
    }
200
82.7k
}
201
202
static m_file_recv_control_cb tox_file_recv_control_handler;
203
static void tox_file_recv_control_handler(Messenger *m, uint32_t friend_number, uint32_t file_number,
204
        unsigned int control, void *user_data)
205
4
{
206
4
    struct Tox_Userdata *tox_data = (struct Tox_Userdata *)user_data;
207
4
    if (tox_data->tox->file_recv_control_callback != nullptr) {
208
4
        tox_unlock(tox_data->tox);
209
4
        tox_data->tox->file_recv_control_callback(tox_data->tox, friend_number, file_number, (Tox_File_Control)control,
210
4
                tox_data->user_data);
211
4
        tox_lock(tox_data->tox);
212
4
    }
213
4
}
214
215
static m_file_chunk_request_cb tox_file_chunk_request_handler;
216
static void tox_file_chunk_request_handler(Messenger *m, uint32_t friend_number, uint32_t file_number,
217
        uint64_t position, size_t length, void *user_data)
218
1.15k
{
219
1.15k
    struct Tox_Userdata *tox_data = (struct Tox_Userdata *)user_data;
220
1.15k
    if (tox_data->tox->file_chunk_request_callback != nullptr) {
221
1.15k
        tox_unlock(tox_data->tox);
222
1.15k
        tox_data->tox->file_chunk_request_callback(tox_data->tox, friend_number, file_number, position, length,
223
1.15k
                tox_data->user_data);
224
1.15k
        tox_lock(tox_data->tox);
225
1.15k
    }
226
1.15k
}
227
228
static m_file_recv_cb tox_file_recv_handler;
229
static void tox_file_recv_handler(Messenger *m, uint32_t friend_number, uint32_t file_number, uint32_t kind,
230
                                  uint64_t file_size, const uint8_t *filename, size_t filename_length, void *user_data)
231
4
{
232
4
    struct Tox_Userdata *tox_data = (struct Tox_Userdata *)user_data;
233
4
    if (tox_data->tox->file_recv_callback != nullptr) {
234
4
        tox_unlock(tox_data->tox);
235
4
        tox_data->tox->file_recv_callback(tox_data->tox, friend_number, file_number, kind, file_size, filename, filename_length,
236
4
                                          tox_data->user_data);
237
4
        tox_lock(tox_data->tox);
238
4
    }
239
4
}
240
241
static m_file_recv_chunk_cb tox_file_recv_chunk_handler;
242
static void tox_file_recv_chunk_handler(Messenger *m, uint32_t friend_number, uint32_t file_number, uint64_t position,
243
                                        const uint8_t *data, size_t length, void *user_data)
244
1.15k
{
245
1.15k
    struct Tox_Userdata *tox_data = (struct Tox_Userdata *)user_data;
246
1.15k
    if (tox_data->tox->file_recv_chunk_callback != nullptr) {
247
1.15k
        tox_unlock(tox_data->tox);
248
1.15k
        tox_data->tox->file_recv_chunk_callback(tox_data->tox, friend_number, file_number, position, data, length,
249
1.15k
                                                tox_data->user_data);
250
1.15k
        tox_lock(tox_data->tox);
251
1.15k
    }
252
1.15k
}
253
254
static g_conference_invite_cb tox_conference_invite_handler;
255
static void tox_conference_invite_handler(Messenger *m, uint32_t friend_number, int type, const uint8_t *cookie,
256
        size_t length, void *user_data)
257
28
{
258
28
    struct Tox_Userdata *tox_data = (struct Tox_Userdata *)user_data;
259
28
    if (tox_data->tox->conference_invite_callback != nullptr) {
260
28
        tox_unlock(tox_data->tox);
261
28
        tox_data->tox->conference_invite_callback(tox_data->tox, friend_number, (Tox_Conference_Type)type, cookie, length,
262
28
                tox_data->user_data);
263
28
        tox_lock(tox_data->tox);
264
28
    }
265
28
}
266
267
static g_conference_connected_cb tox_conference_connected_handler;
268
static void tox_conference_connected_handler(Messenger *m, uint32_t conference_number, void *user_data)
269
28
{
270
28
    struct Tox_Userdata *tox_data = (struct Tox_Userdata *)user_data;
271
28
    if (tox_data->tox->conference_connected_callback != nullptr) {
272
28
        tox_unlock(tox_data->tox);
273
28
        tox_data->tox->conference_connected_callback(tox_data->tox, conference_number, tox_data->user_data);
274
28
        tox_lock(tox_data->tox);
275
28
    }
276
28
}
277
278
static g_conference_message_cb tox_conference_message_handler;
279
static void tox_conference_message_handler(Messenger *m, uint32_t conference_number, uint32_t peer_number, int type,
280
        const uint8_t *message, size_t length, void *user_data)
281
19
{
282
19
    struct Tox_Userdata *tox_data = (struct Tox_Userdata *)user_data;
283
19
    if (tox_data->tox->conference_message_callback != nullptr) {
284
19
        tox_unlock(tox_data->tox);
285
19
        tox_data->tox->conference_message_callback(tox_data->tox, conference_number, peer_number, (Tox_Message_Type)type,
286
19
                message, length, tox_data->user_data);
287
19
        tox_lock(tox_data->tox);
288
19
    }
289
19
}
290
291
static title_cb tox_conference_title_handler;
292
static void tox_conference_title_handler(Messenger *m, uint32_t conference_number, uint32_t peer_number,
293
        const uint8_t *title, size_t length, void *user_data)
294
0
{
295
0
    struct Tox_Userdata *tox_data = (struct Tox_Userdata *)user_data;
296
0
    if (tox_data->tox->conference_title_callback != nullptr) {
297
0
        tox_unlock(tox_data->tox);
298
0
        tox_data->tox->conference_title_callback(tox_data->tox, conference_number, peer_number, title, length,
299
0
                tox_data->user_data);
300
0
        tox_lock(tox_data->tox);
301
0
    }
302
0
}
303
304
static peer_name_cb tox_conference_peer_name_handler;
305
static void tox_conference_peer_name_handler(Messenger *m, uint32_t conference_number, uint32_t peer_number,
306
        const uint8_t *name, size_t length, void *user_data)
307
3
{
308
3
    struct Tox_Userdata *tox_data = (struct Tox_Userdata *)user_data;
309
3
    if (tox_data->tox->conference_peer_name_callback != nullptr) {
310
3
        tox_unlock(tox_data->tox);
311
3
        tox_data->tox->conference_peer_name_callback(tox_data->tox, conference_number, peer_number, name, length,
312
3
                tox_data->user_data);
313
3
        tox_lock(tox_data->tox);
314
3
    }
315
3
}
316
317
static peer_list_changed_cb tox_conference_peer_list_changed_handler;
318
static void tox_conference_peer_list_changed_handler(Messenger *m, uint32_t conference_number, void *user_data)
319
324
{
320
324
    struct Tox_Userdata *tox_data = (struct Tox_Userdata *)user_data;
321
324
    if (tox_data->tox->conference_peer_list_changed_callback != nullptr) {
322
324
        tox_unlock(tox_data->tox);
323
324
        tox_data->tox->conference_peer_list_changed_callback(tox_data->tox, conference_number, tox_data->user_data);
324
324
        tox_lock(tox_data->tox);
325
324
    }
326
324
}
327
328
static dht_nodes_response_cb tox_dht_nodes_response_handler;
329
static void tox_dht_nodes_response_handler(const DHT *dht, const Node_format *node, void *user_data)
330
192k
{
331
192k
    struct Tox_Userdata *tox_data = (struct Tox_Userdata *)user_data;
332
192k
    if (tox_data->tox->dht_nodes_response_callback == nullptr) {
333
6.83k
        return;
334
6.83k
    }
335
336
185k
    Ip_Ntoa ip_str;
337
185k
    net_ip_ntoa(&node->ip_port.ip, &ip_str);
338
339
185k
    tox_unlock(tox_data->tox);
340
185k
    tox_data->tox->dht_nodes_response_callback(
341
185k
        tox_data->tox, node->public_key, ip_str.buf, ip_str.length, net_ntohs(node->ip_port.port),
342
185k
        tox_data->user_data);
343
185k
    tox_lock(tox_data->tox);
344
185k
}
345
346
static m_friend_lossy_packet_cb tox_friend_lossy_packet_handler;
347
static void tox_friend_lossy_packet_handler(Messenger *m, uint32_t friend_number, uint8_t packet_id,
348
        const uint8_t *data, size_t length, void *user_data)
349
627
{
350
627
    struct Tox_Userdata *tox_data = (struct Tox_Userdata *)user_data;
351
627
    assert(data != nullptr);
352
627
    assert(length > 0);
353
354
627
    if (tox_data->tox->friend_lossy_packet_callback_per_pktid[packet_id] != nullptr) {
355
118
        tox_unlock(tox_data->tox);
356
118
        tox_data->tox->friend_lossy_packet_callback_per_pktid[packet_id](tox_data->tox, friend_number, data, length,
357
118
                tox_data->user_data);
358
118
        tox_lock(tox_data->tox);
359
118
    }
360
627
}
361
362
static m_friend_lossless_packet_cb tox_friend_lossless_packet_handler;
363
static void tox_friend_lossless_packet_handler(Messenger *m, uint32_t friend_number, uint8_t packet_id,
364
        const uint8_t *data, size_t length, void *user_data)
365
17
{
366
17
    struct Tox_Userdata *tox_data = (struct Tox_Userdata *)user_data;
367
17
    assert(data != nullptr);
368
17
    assert(length > 0);
369
370
17
    if (tox_data->tox->friend_lossless_packet_callback_per_pktid[packet_id] != nullptr) {
371
17
        tox_unlock(tox_data->tox);
372
17
        tox_data->tox->friend_lossless_packet_callback_per_pktid[packet_id](tox_data->tox, friend_number, data, length,
373
17
                tox_data->user_data);
374
17
        tox_lock(tox_data->tox);
375
17
    }
376
17
}
377
378
static void tox_group_peer_name_handler(const Messenger *_Nonnull m, uint32_t group_number, GC_Peer_Id peer_id,
379
                                        const uint8_t *_Nonnull name, size_t length, void *_Nullable user_data)
380
2
{
381
2
    struct Tox_Userdata *tox_data = (struct Tox_Userdata *)user_data;
382
2
    if (tox_data->tox->group_peer_name_callback != nullptr) {
383
2
        tox_unlock(tox_data->tox);
384
2
        tox_data->tox->group_peer_name_callback(tox_data->tox, group_number, gc_peer_id_to_int(peer_id), name, length, tox_data->user_data);
385
2
        tox_lock(tox_data->tox);
386
2
    }
387
2
}
388
389
static void tox_group_peer_status_handler(const Messenger *_Nonnull m, uint32_t group_number, GC_Peer_Id peer_id,
390
        unsigned int status, void *_Nullable user_data)
391
2
{
392
2
    struct Tox_Userdata *tox_data = (struct Tox_Userdata *)user_data;
393
2
    if (tox_data->tox->group_peer_status_callback != nullptr) {
394
2
        tox_unlock(tox_data->tox);
395
2
        tox_data->tox->group_peer_status_callback(tox_data->tox, group_number, gc_peer_id_to_int(peer_id), (Tox_User_Status)status,
396
2
                tox_data->user_data);
397
2
        tox_lock(tox_data->tox);
398
2
    }
399
2
}
400
401
static void tox_group_topic_handler(const Messenger *_Nonnull m, uint32_t group_number, GC_Peer_Id peer_id, const uint8_t *_Nonnull topic,
402
                                    size_t length, void *_Nullable user_data)
403
32
{
404
32
    struct Tox_Userdata *tox_data = (struct Tox_Userdata *)user_data;
405
32
    if (tox_data->tox->group_topic_callback != nullptr) {
406
32
        tox_unlock(tox_data->tox);
407
32
        tox_data->tox->group_topic_callback(tox_data->tox, group_number, gc_peer_id_to_int(peer_id), topic, length, tox_data->user_data);
408
32
        tox_lock(tox_data->tox);
409
32
    }
410
32
}
411
412
static void tox_group_topic_lock_handler(const Messenger *_Nonnull m, uint32_t group_number, unsigned int topic_lock,
413
        void *_Nullable user_data)
414
13
{
415
13
    struct Tox_Userdata *tox_data = (struct Tox_Userdata *)user_data;
416
13
    if (tox_data->tox->group_topic_lock_callback != nullptr) {
417
13
        tox_unlock(tox_data->tox);
418
13
        tox_data->tox->group_topic_lock_callback(tox_data->tox, group_number, (Tox_Group_Topic_Lock)topic_lock,
419
13
                tox_data->user_data);
420
13
        tox_lock(tox_data->tox);
421
13
    }
422
13
}
423
424
static void tox_group_voice_state_handler(const Messenger *_Nonnull m, uint32_t group_number, unsigned int voice_state,
425
        void *_Nullable user_data)
426
16
{
427
16
    struct Tox_Userdata *tox_data = (struct Tox_Userdata *)user_data;
428
16
    if (tox_data->tox->group_voice_state_callback != nullptr) {
429
16
        tox_unlock(tox_data->tox);
430
16
        tox_data->tox->group_voice_state_callback(tox_data->tox, group_number, (Tox_Group_Voice_State)voice_state,
431
16
                tox_data->user_data);
432
16
        tox_lock(tox_data->tox);
433
16
    }
434
16
}
435
436
static void tox_group_peer_limit_handler(const Messenger *_Nonnull m, uint32_t group_number, uint32_t peer_limit,
437
        void *_Nullable user_data)
438
12
{
439
12
    struct Tox_Userdata *tox_data = (struct Tox_Userdata *)user_data;
440
12
    if (tox_data->tox->group_peer_limit_callback != nullptr) {
441
12
        tox_unlock(tox_data->tox);
442
12
        tox_data->tox->group_peer_limit_callback(tox_data->tox, group_number, peer_limit, tox_data->user_data);
443
12
        tox_lock(tox_data->tox);
444
12
    }
445
12
}
446
447
static void tox_group_privacy_state_handler(const Messenger *_Nonnull m, uint32_t group_number, unsigned int privacy_state,
448
        void *_Nullable user_data)
449
10
{
450
10
    struct Tox_Userdata *tox_data = (struct Tox_Userdata *)user_data;
451
10
    if (tox_data->tox->group_privacy_state_callback != nullptr) {
452
10
        tox_unlock(tox_data->tox);
453
10
        tox_data->tox->group_privacy_state_callback(tox_data->tox, group_number, (Tox_Group_Privacy_State)privacy_state,
454
10
                tox_data->user_data);
455
10
        tox_lock(tox_data->tox);
456
10
    }
457
10
}
458
459
static void tox_group_password_handler(const Messenger *_Nonnull m, uint32_t group_number, const uint8_t *_Nullable password,
460
                                       size_t length, void *_Nullable user_data)
461
7
{
462
7
    struct Tox_Userdata *tox_data = (struct Tox_Userdata *)user_data;
463
7
    if (tox_data->tox->group_password_callback != nullptr) {
464
7
        tox_unlock(tox_data->tox);
465
7
        tox_data->tox->group_password_callback(tox_data->tox, group_number, password, length, tox_data->user_data);
466
7
        tox_lock(tox_data->tox);
467
7
    }
468
7
}
469
470
static void tox_group_message_handler(const Messenger *_Nonnull m, uint32_t group_number, GC_Peer_Id peer_id, unsigned int type,
471
                                      const uint8_t *_Nonnull message, size_t length, Tox_Group_Message_Id message_id, void *_Nullable user_data)
472
115
{
473
115
    struct Tox_Userdata *tox_data = (struct Tox_Userdata *)user_data;
474
115
    if (tox_data->tox->group_message_callback != nullptr) {
475
115
        tox_unlock(tox_data->tox);
476
115
        tox_data->tox->group_message_callback(tox_data->tox, group_number, gc_peer_id_to_int(peer_id), (Tox_Message_Type)type, message, length,
477
115
                                              message_id, tox_data->user_data);
478
115
        tox_lock(tox_data->tox);
479
115
    }
480
115
}
481
482
static void tox_group_private_message_handler(const Messenger *_Nonnull m, uint32_t group_number, GC_Peer_Id peer_id,
483
        unsigned int type, const uint8_t *_Nonnull message, size_t length, Tox_Group_Message_Id message_id, void *_Nullable user_data)
484
2
{
485
2
    struct Tox_Userdata *tox_data = (struct Tox_Userdata *)user_data;
486
2
    if (tox_data->tox->group_private_message_callback != nullptr) {
487
2
        tox_unlock(tox_data->tox);
488
2
        tox_data->tox->group_private_message_callback(tox_data->tox, group_number, gc_peer_id_to_int(peer_id), (Tox_Message_Type)type, message,
489
2
                length, message_id, tox_data->user_data);
490
2
        tox_lock(tox_data->tox);
491
2
    }
492
2
}
493
494
static void tox_group_custom_packet_handler(const Messenger *_Nonnull m, uint32_t group_number, GC_Peer_Id peer_id,
495
        const uint8_t *_Nonnull data, size_t length, void *_Nullable user_data)
496
2
{
497
2
    struct Tox_Userdata *tox_data = (struct Tox_Userdata *)user_data;
498
2
    if (tox_data->tox->group_custom_packet_callback != nullptr) {
499
2
        tox_unlock(tox_data->tox);
500
2
        tox_data->tox->group_custom_packet_callback(tox_data->tox, group_number, gc_peer_id_to_int(peer_id), data, length, tox_data->user_data);
501
2
        tox_lock(tox_data->tox);
502
2
    }
503
2
}
504
505
static void tox_group_custom_private_packet_handler(const Messenger *_Nonnull m, uint32_t group_number, GC_Peer_Id peer_id,
506
        const uint8_t *_Nonnull data, size_t length, void *_Nullable user_data)
507
2
{
508
2
    struct Tox_Userdata *tox_data = (struct Tox_Userdata *)user_data;
509
2
    if (tox_data->tox->group_custom_private_packet_callback != nullptr) {
510
2
        tox_unlock(tox_data->tox);
511
2
        tox_data->tox->group_custom_private_packet_callback(tox_data->tox, group_number, gc_peer_id_to_int(peer_id), data, length,
512
2
                tox_data->user_data);
513
2
        tox_lock(tox_data->tox);
514
2
    }
515
2
}
516
517
static void tox_group_invite_handler(const Messenger *_Nonnull m, uint32_t friend_number, const uint8_t *_Nonnull invite_data,
518
                                     size_t length, const uint8_t *_Nonnull group_name, size_t group_name_length, void *_Nullable user_data)
519
3
{
520
3
    struct Tox_Userdata *tox_data = (struct Tox_Userdata *)user_data;
521
3
    if (tox_data->tox->group_invite_callback != nullptr) {
522
3
        tox_unlock(tox_data->tox);
523
3
        tox_data->tox->group_invite_callback(tox_data->tox, friend_number, invite_data, length, group_name, group_name_length,
524
3
                                             tox_data->user_data);
525
3
        tox_lock(tox_data->tox);
526
3
    }
527
3
}
528
529
static void tox_group_peer_join_handler(const Messenger *_Nonnull m, uint32_t group_number, GC_Peer_Id peer_id, void *_Nullable user_data)
530
68
{
531
68
    struct Tox_Userdata *tox_data = (struct Tox_Userdata *)user_data;
532
68
    if (tox_data->tox->group_peer_join_callback != nullptr) {
533
68
        tox_unlock(tox_data->tox);
534
68
        tox_data->tox->group_peer_join_callback(tox_data->tox, group_number, gc_peer_id_to_int(peer_id), tox_data->user_data);
535
68
        tox_lock(tox_data->tox);
536
68
    }
537
68
}
538
539
static void tox_group_peer_exit_handler(const Messenger *_Nonnull m, uint32_t group_number, GC_Peer_Id peer_id,
540
                                        unsigned int exit_type, const uint8_t *_Nonnull name, size_t name_length,
541
                                        const uint8_t *_Nullable part_message, size_t length, void *_Nullable user_data)
542
11
{
543
11
    struct Tox_Userdata *tox_data = (struct Tox_Userdata *)user_data;
544
11
    if (tox_data->tox->group_peer_exit_callback != nullptr) {
545
11
        tox_unlock(tox_data->tox);
546
11
        tox_data->tox->group_peer_exit_callback(tox_data->tox, group_number, gc_peer_id_to_int(peer_id),
547
11
                                                (Tox_Group_Exit_Type)exit_type, name, name_length,
548
11
                                                part_message, length, tox_data->user_data);
549
11
        tox_lock(tox_data->tox);
550
11
    }
551
11
}
552
553
static void tox_group_self_join_handler(const Messenger *_Nonnull m, uint32_t group_number, void *_Nullable user_data)
554
22
{
555
22
    struct Tox_Userdata *tox_data = (struct Tox_Userdata *)user_data;
556
22
    if (tox_data->tox->group_self_join_callback != nullptr) {
557
22
        tox_unlock(tox_data->tox);
558
22
        tox_data->tox->group_self_join_callback(tox_data->tox, group_number, tox_data->user_data);
559
22
        tox_lock(tox_data->tox);
560
22
    }
561
22
}
562
563
static void tox_group_join_fail_handler(const Messenger *_Nonnull m, uint32_t group_number, unsigned int fail_type,
564
                                        void *_Nullable user_data)
565
3
{
566
3
    struct Tox_Userdata *tox_data = (struct Tox_Userdata *)user_data;
567
3
    if (tox_data->tox->group_join_fail_callback != nullptr) {
568
3
        tox_unlock(tox_data->tox);
569
3
        tox_data->tox->group_join_fail_callback(tox_data->tox, group_number, (Tox_Group_Join_Fail)fail_type,
570
3
                                                tox_data->user_data);
571
3
        tox_lock(tox_data->tox);
572
3
    }
573
3
}
574
575
static void tox_group_moderation_handler(const Messenger *_Nonnull m, uint32_t group_number, GC_Peer_Id source_peer_number,
576
        GC_Peer_Id target_peer_number, unsigned int mod_type, void *_Nullable user_data)
577
72
{
578
72
    struct Tox_Userdata *tox_data = (struct Tox_Userdata *)user_data;
579
72
    if (tox_data->tox->group_moderation_callback != nullptr) {
580
72
        tox_unlock(tox_data->tox);
581
72
        tox_data->tox->group_moderation_callback(tox_data->tox, group_number,
582
72
                gc_peer_id_to_int(source_peer_number), gc_peer_id_to_int(target_peer_number),
583
72
                (Tox_Group_Mod_Event)mod_type, tox_data->user_data);
584
72
        tox_lock(tox_data->tox);
585
72
    }
586
72
}
587
588
bool tox_version_is_compatible(uint32_t major, uint32_t minor, uint32_t patch)
589
2
{
590
2
    return TOX_VERSION_IS_API_COMPATIBLE(major, minor, patch);
591
2
}
592
593
static State_Load_Status state_load_callback(void *_Nonnull outer, const uint8_t *_Nonnull data, uint32_t length, uint16_t type)
594
29.9k
{
595
29.9k
    const Tox *tox = (const Tox *)outer;
596
29.9k
    State_Load_Status status = STATE_LOAD_STATUS_CONTINUE;
597
598
29.9k
    if (messenger_load_state_section(tox->m, data, length, type, &status)
599
29.9k
            || conferences_load_state_section(tox->m->conferences_object, data, length, type, &status)) {
600
29.5k
        return status;
601
29.5k
    }
602
603
347
    if (type == STATE_TYPE_END) {
604
17
        if (length != 0) {
605
1
            return STATE_LOAD_STATUS_ERROR;
606
1
        }
607
608
16
        return STATE_LOAD_STATUS_END;
609
17
    }
610
611
330
    LOGGER_ERROR(tox->m->log, "Load state: contains unrecognized part (len %u, type %u)",
612
330
                 length, type);
613
614
330
    return STATE_LOAD_STATUS_CONTINUE;
615
347
}
616
617
/** Load tox from data of size length. */
618
static int tox_load(Tox *_Nonnull tox, const uint8_t *_Nonnull data, uint32_t length)
619
1.38k
{
620
1.38k
    uint32_t data32[2];
621
1.38k
    const uint32_t cookie_len = sizeof(data32);
622
623
1.38k
    if (length < cookie_len) {
624
0
        return -1;
625
0
    }
626
627
1.38k
    memcpy(data32, data, sizeof(uint32_t));
628
1.38k
    lendian_bytes_to_host32(data32 + 1, data + sizeof(uint32_t));
629
630
1.38k
    if (data32[0] != 0 || data32[1] != STATE_COOKIE_GLOBAL) {
631
2
        return -1;
632
2
    }
633
634
1.38k
    return state_load(tox->m->log, state_load_callback, tox, data + cookie_len,
635
1.38k
                      length - cookie_len, STATE_COOKIE_TYPE);
636
1.38k
}
637
638
static Tox *_Nullable tox_new_system(const struct Tox_Options *_Nullable options, Tox_Err_New *_Nullable error, const Tox_System *_Nullable sys)
639
1.88k
{
640
1.88k
    struct Tox_Options *default_options = nullptr;
641
1.88k
    if (options == nullptr) {
642
29
        Tox_Err_Options_New err;
643
29
        default_options = tox_options_new(&err);
644
645
29
        switch (err) {
646
29
            case TOX_ERR_OPTIONS_NEW_OK: {
647
29
                assert(default_options != nullptr);
648
29
                break;
649
29
            }
650
651
29
            case TOX_ERR_OPTIONS_NEW_MALLOC: {
652
0
                SET_ERROR_PARAMETER(error, TOX_ERR_NEW_MALLOC);
653
0
                return nullptr;
654
29
            }
655
29
        }
656
29
    }
657
658
1.88k
    const struct Tox_Options *const opts = options != nullptr ? options : default_options;
659
1.88k
    assert(opts != nullptr);
660
661
1.88k
    const Tox_System default_system = tox_default_system();
662
663
1.88k
    if (sys == nullptr) {
664
386
        sys = &default_system;
665
386
    }
666
667
1.88k
    if (sys->rng == nullptr || sys->ns == nullptr || sys->mem == nullptr) {
668
        // TODO(iphydf): Not quite right, but similar.
669
0
        SET_ERROR_PARAMETER(error, TOX_ERR_NEW_MALLOC);
670
0
        tox_options_free(default_options);
671
0
        return nullptr;
672
0
    }
673
674
1.88k
    const Random *const rng = sys->rng;
675
1.88k
    const Network *const ns = sys->ns;
676
1.88k
    const Memory *const mem = sys->mem;
677
678
1.88k
    Messenger_Options m_options = {nullptr};
679
680
1.88k
    m_options.dns_enabled = !tox_options_get_experimental_disable_dns(opts);
681
682
1.88k
    bool load_savedata_sk = false;
683
1.88k
    bool load_savedata_tox = false;
684
685
1.88k
    if (tox_options_get_savedata_type(opts) != TOX_SAVEDATA_TYPE_NONE) {
686
1.39k
        if (tox_options_get_savedata_data(opts) == nullptr || tox_options_get_savedata_length(opts) == 0) {
687
0
            SET_ERROR_PARAMETER(error, TOX_ERR_NEW_LOAD_BAD_FORMAT);
688
0
            tox_options_free(default_options);
689
0
            return nullptr;
690
0
        }
691
1.39k
    }
692
693
1.88k
    if (tox_options_get_savedata_type(opts) == TOX_SAVEDATA_TYPE_SECRET_KEY) {
694
1
        if (tox_options_get_savedata_length(opts) != TOX_SECRET_KEY_SIZE) {
695
0
            SET_ERROR_PARAMETER(error, TOX_ERR_NEW_LOAD_BAD_FORMAT);
696
0
            tox_options_free(default_options);
697
0
            return nullptr;
698
0
        }
699
700
1
        load_savedata_sk = true;
701
1.88k
    } else if (tox_options_get_savedata_type(opts) == TOX_SAVEDATA_TYPE_TOX_SAVE) {
702
1.39k
        if (tox_options_get_savedata_length(opts) < TOX_ENC_SAVE_MAGIC_LENGTH) {
703
1
            SET_ERROR_PARAMETER(error, TOX_ERR_NEW_LOAD_BAD_FORMAT);
704
1
            tox_options_free(default_options);
705
1
            return nullptr;
706
1
        }
707
708
1.38k
        if (memcmp(tox_options_get_savedata_data(opts), TOX_ENC_SAVE_MAGIC_NUMBER, TOX_ENC_SAVE_MAGIC_LENGTH) == 0) {
709
2
            SET_ERROR_PARAMETER(error, TOX_ERR_NEW_LOAD_ENCRYPTED);
710
2
            tox_options_free(default_options);
711
2
            return nullptr;
712
2
        }
713
714
1.38k
        load_savedata_tox = true;
715
1.38k
    }
716
717
1.88k
    m_options.ipv6enabled = tox_options_get_ipv6_enabled(opts);
718
1.88k
    m_options.udp_disabled = !tox_options_get_udp_enabled(opts);
719
1.88k
    m_options.port_range[0] = tox_options_get_start_port(opts);
720
1.88k
    m_options.port_range[1] = tox_options_get_end_port(opts);
721
1.88k
    m_options.tcp_server_port = tox_options_get_tcp_port(opts);
722
1.88k
    m_options.hole_punching_enabled = tox_options_get_hole_punching_enabled(opts);
723
1.88k
    m_options.local_discovery_enabled = tox_options_get_local_discovery_enabled(opts);
724
1.88k
    m_options.dht_announcements_enabled = tox_options_get_dht_announcements_enabled(opts);
725
1.88k
    m_options.groups_persistence_enabled = tox_options_get_experimental_groups_persistence(opts);
726
727
1.88k
    if (m_options.udp_disabled) {
728
45
        m_options.local_discovery_enabled = false;
729
45
    }
730
731
1.88k
    Tox *tox = (Tox *)mem_alloc(mem, sizeof(Tox));
732
733
1.88k
    if (tox == nullptr) {
734
1
        SET_ERROR_PARAMETER(error, TOX_ERR_NEW_MALLOC);
735
1
        tox_options_free(default_options);
736
1
        return nullptr;
737
1
    }
738
739
1.88k
    tox->log_callback = tox_options_get_log_callback(opts);
740
741
1.88k
    Logger *log = logger_new(mem);
742
743
1.88k
    if (log == nullptr) {
744
1
        SET_ERROR_PARAMETER(error, TOX_ERR_NEW_MALLOC);
745
1
        mem_delete(mem, tox);
746
1
        tox_options_free(default_options);
747
1
        return nullptr;
748
1
    }
749
750
1.88k
    tox->log = log;
751
752
1.88k
    m_options.log = tox->log;
753
754
1.88k
    logger_callback_log(tox->log, tox_log_handler, tox, tox_options_get_log_user_data(opts));
755
756
1.88k
    switch (tox_options_get_proxy_type(opts)) {
757
33
        case TOX_PROXY_TYPE_HTTP: {
758
33
            m_options.proxy_info.proxy_type = TCP_PROXY_HTTP;
759
33
            break;
760
0
        }
761
762
42
        case TOX_PROXY_TYPE_SOCKS5: {
763
42
            m_options.proxy_info.proxy_type = TCP_PROXY_SOCKS5;
764
42
            break;
765
0
        }
766
767
1.80k
        case TOX_PROXY_TYPE_NONE: {
768
1.80k
            m_options.proxy_info.proxy_type = TCP_PROXY_NONE;
769
1.80k
            break;
770
0
        }
771
772
0
        default: {
773
0
            SET_ERROR_PARAMETER(error, TOX_ERR_NEW_PROXY_BAD_TYPE);
774
0
            logger_kill(tox->log);
775
0
            mem_delete(mem, tox);
776
0
            tox_options_free(default_options);
777
0
            return nullptr;
778
0
        }
779
1.88k
    }
780
781
1.88k
    tox->sys = *sys;
782
783
1.88k
    if (m_options.proxy_info.proxy_type != TCP_PROXY_NONE) {
784
75
        if (tox_options_get_proxy_port(opts) == 0) {
785
0
            SET_ERROR_PARAMETER(error, TOX_ERR_NEW_PROXY_BAD_PORT);
786
0
            logger_kill(tox->log);
787
0
            mem_delete(mem, tox);
788
0
            tox_options_free(default_options);
789
0
            return nullptr;
790
0
        }
791
792
75
        ip_init(&m_options.proxy_info.ip_port.ip, m_options.ipv6enabled);
793
794
75
        if (m_options.ipv6enabled) {
795
71
            m_options.proxy_info.ip_port.ip.family = net_family_unspec();
796
71
        }
797
798
75
        const char *const proxy_host = tox_options_get_proxy_host(opts);
799
75
        const bool dns_enabled = !tox_options_get_experimental_disable_dns(opts);
800
801
75
        if (proxy_host == nullptr
802
75
                || !addr_resolve_or_parse_ip(ns, mem, proxy_host, &m_options.proxy_info.ip_port.ip, nullptr, dns_enabled)) {
803
0
            SET_ERROR_PARAMETER(error, TOX_ERR_NEW_PROXY_BAD_HOST);
804
            // TODO(irungentoo): TOX_ERR_NEW_PROXY_NOT_FOUND if domain.
805
0
            logger_kill(tox->log);
806
0
            mem_delete(mem, tox);
807
0
            tox_options_free(default_options);
808
0
            return nullptr;
809
0
        }
810
811
75
        m_options.proxy_info.ip_port.port = net_htons(tox_options_get_proxy_port(opts));
812
75
    }
813
814
1.88k
    Mono_Time *temp_mono_time = mono_time_new(mem, sys->mono_time_callback, sys->mono_time_user_data);
815
816
1.88k
    if (temp_mono_time == nullptr) {
817
2
        SET_ERROR_PARAMETER(error, TOX_ERR_NEW_MALLOC);
818
2
        logger_kill(tox->log);
819
2
        mem_delete(mem, tox);
820
2
        tox_options_free(default_options);
821
2
        return nullptr;
822
2
    }
823
1.88k
    tox->mono_time = temp_mono_time;
824
825
1.88k
    if (tox_options_get_experimental_thread_safety(opts)) {
826
0
        pthread_mutex_t *mutex = (pthread_mutex_t *)mem_alloc(mem, sizeof(pthread_mutex_t));
827
828
0
        if (mutex == nullptr) {
829
0
            SET_ERROR_PARAMETER(error, TOX_ERR_NEW_MALLOC);
830
0
            mono_time_free(mem, tox->mono_time);
831
0
            logger_kill(tox->log);
832
0
            mem_delete(mem, tox);
833
0
            tox_options_free(default_options);
834
0
            return nullptr;
835
0
        }
836
837
0
        pthread_mutex_init(mutex, nullptr);
838
839
0
        tox->mutex = mutex;
840
1.88k
    } else {
841
1.88k
        tox->mutex = nullptr;
842
1.88k
    }
843
844
1.88k
    tox_lock(tox);
845
846
1.88k
    Messenger_Error m_error;
847
1.88k
    Messenger *temp_m = new_messenger(tox->mono_time, mem, rng, ns, &m_options, &m_error);
848
849
1.88k
    if (temp_m == nullptr) {
850
88
        switch (m_error) {
851
2
            case MESSENGER_ERROR_PORT:
852
20
            case MESSENGER_ERROR_TCP_SERVER: {
853
20
                SET_ERROR_PARAMETER(error, TOX_ERR_NEW_PORT_ALLOC);
854
20
                break;
855
2
            }
856
68
            case MESSENGER_ERROR_OTHER:
857
68
            case MESSENGER_ERROR_NONE: {
858
68
                SET_ERROR_PARAMETER(error, TOX_ERR_NEW_MALLOC);
859
68
                break;
860
68
            }
861
88
        }
862
863
88
        mono_time_free(mem, tox->mono_time);
864
88
        tox_unlock(tox);
865
866
88
        if (tox->mutex != nullptr) {
867
0
            pthread_mutex_destroy(tox->mutex);
868
0
        }
869
870
88
        mem_delete(mem, tox->mutex);
871
88
        logger_kill(tox->log);
872
88
        mem_delete(mem, tox);
873
88
        tox_options_free(default_options);
874
88
        return nullptr;
875
88
    }
876
1.79k
    tox->m = temp_m;
877
878
1.79k
    tox->m->conferences_object = new_groupchats(tox->mono_time, mem, tox->m);
879
880
1.79k
    if (tox->m->conferences_object == nullptr) {
881
2
        kill_messenger(tox->m);
882
883
2
        mono_time_free(mem, tox->mono_time);
884
2
        tox_unlock(tox);
885
886
2
        if (tox->mutex != nullptr) {
887
0
            pthread_mutex_destroy(tox->mutex);
888
0
        }
889
890
2
        mem_delete(mem, tox->mutex);
891
2
        logger_kill(tox->log);
892
2
        mem_delete(mem, tox);
893
894
2
        SET_ERROR_PARAMETER(error, TOX_ERR_NEW_MALLOC);
895
2
        tox_options_free(default_options);
896
2
        return nullptr;
897
2
    }
898
899
1.79k
    if (load_savedata_tox
900
1.79k
            && tox_load(tox, tox_options_get_savedata_data(opts), tox_options_get_savedata_length(opts)) == -1) {
901
392
        kill_groupchats(tox->m->conferences_object);
902
392
        kill_messenger(tox->m);
903
904
392
        mono_time_free(mem, tox->mono_time);
905
392
        tox_unlock(tox);
906
907
392
        if (tox->mutex != nullptr) {
908
0
            pthread_mutex_destroy(tox->mutex);
909
0
        }
910
911
392
        mem_delete(mem, tox->mutex);
912
392
        logger_kill(tox->log);
913
392
        mem_delete(mem, tox);
914
915
392
        SET_ERROR_PARAMETER(error, TOX_ERR_NEW_LOAD_BAD_FORMAT);
916
392
        tox_options_free(default_options);
917
392
        return nullptr;
918
392
    }
919
920
1.40k
    if (load_savedata_sk) {
921
1
        load_secret_key(tox->m->net_crypto, tox_options_get_savedata_data(opts));
922
1
    }
923
924
1.40k
    m_callback_namechange(tox->m, tox_friend_name_handler);
925
1.40k
    m_callback_core_connection(tox->m, tox_self_connection_status_handler);
926
1.40k
    m_callback_statusmessage(tox->m, tox_friend_status_message_handler);
927
1.40k
    m_callback_userstatus(tox->m, tox_friend_status_handler);
928
1.40k
    m_callback_connectionstatus(tox->m, tox_friend_connection_status_handler);
929
1.40k
    m_callback_typingchange(tox->m, tox_friend_typing_handler);
930
1.40k
    m_callback_read_receipt(tox->m, tox_friend_read_receipt_handler);
931
1.40k
    m_callback_friendrequest(tox->m, tox_friend_request_handler);
932
1.40k
    m_callback_friendmessage(tox->m, tox_friend_message_handler);
933
1.40k
    callback_file_control(tox->m, tox_file_recv_control_handler);
934
1.40k
    callback_file_reqchunk(tox->m, tox_file_chunk_request_handler);
935
1.40k
    callback_file_sendrequest(tox->m, tox_file_recv_handler);
936
1.40k
    callback_file_data(tox->m, tox_file_recv_chunk_handler);
937
1.40k
    dht_callback_nodes_response(tox->m->dht, tox_dht_nodes_response_handler);
938
1.40k
    g_callback_group_invite(tox->m->conferences_object, tox_conference_invite_handler);
939
1.40k
    g_callback_group_connected(tox->m->conferences_object, tox_conference_connected_handler);
940
1.40k
    g_callback_group_message(tox->m->conferences_object, tox_conference_message_handler);
941
1.40k
    g_callback_group_title(tox->m->conferences_object, tox_conference_title_handler);
942
1.40k
    g_callback_peer_name(tox->m->conferences_object, tox_conference_peer_name_handler);
943
1.40k
    g_callback_peer_list_changed(tox->m->conferences_object, tox_conference_peer_list_changed_handler);
944
1.40k
    custom_lossy_packet_registerhandler(tox->m, tox_friend_lossy_packet_handler);
945
1.40k
    custom_lossless_packet_registerhandler(tox->m, tox_friend_lossless_packet_handler);
946
947
1.40k
    m_callback_group_invite(tox->m, tox_group_invite_handler);
948
1.40k
    gc_callback_message(tox->m, tox_group_message_handler);
949
1.40k
    gc_callback_private_message(tox->m, tox_group_private_message_handler);
950
1.40k
    gc_callback_custom_packet(tox->m, tox_group_custom_packet_handler);
951
1.40k
    gc_callback_custom_private_packet(tox->m, tox_group_custom_private_packet_handler);
952
1.40k
    gc_callback_moderation(tox->m, tox_group_moderation_handler);
953
1.40k
    gc_callback_nick_change(tox->m, tox_group_peer_name_handler);
954
1.40k
    gc_callback_status_change(tox->m, tox_group_peer_status_handler);
955
1.40k
    gc_callback_topic_change(tox->m, tox_group_topic_handler);
956
1.40k
    gc_callback_peer_limit(tox->m, tox_group_peer_limit_handler);
957
1.40k
    gc_callback_privacy_state(tox->m, tox_group_privacy_state_handler);
958
1.40k
    gc_callback_topic_lock(tox->m, tox_group_topic_lock_handler);
959
1.40k
    gc_callback_password(tox->m, tox_group_password_handler);
960
1.40k
    gc_callback_peer_join(tox->m, tox_group_peer_join_handler);
961
1.40k
    gc_callback_peer_exit(tox->m, tox_group_peer_exit_handler);
962
1.40k
    gc_callback_self_join(tox->m, tox_group_self_join_handler);
963
1.40k
    gc_callback_rejected(tox->m, tox_group_join_fail_handler);
964
1.40k
    gc_callback_voice_state(tox->m, tox_group_voice_state_handler);
965
966
1.40k
    tox_unlock(tox);
967
968
1.40k
    SET_ERROR_PARAMETER(error, TOX_ERR_NEW_OK);
969
970
1.40k
    tox_options_free(default_options);
971
1.40k
    return tox;
972
1.79k
}
973
974
Tox *_Nullable tox_new(const struct Tox_Options *_Nullable options, Tox_Err_New *_Nullable error)
975
386
{
976
386
    return tox_new_system(options, error, nullptr);
977
386
}
978
979
Tox *tox_new_testing(const Tox_Options *options, Tox_Err_New *error,
980
                     const Tox_Options_Testing *testing, Tox_Err_New_Testing *testing_error)
981
1.50k
{
982
1.50k
    if (testing == nullptr) {
983
0
        SET_ERROR_PARAMETER(error, TOX_ERR_NEW_NULL);
984
0
        SET_ERROR_PARAMETER(testing_error, TOX_ERR_NEW_TESTING_NULL);
985
0
        return nullptr;
986
0
    }
987
988
1.50k
    if (testing->operating_system == nullptr) {
989
0
        SET_ERROR_PARAMETER(error, TOX_ERR_NEW_NULL);
990
0
        SET_ERROR_PARAMETER(testing_error, TOX_ERR_NEW_TESTING_NULL);
991
0
        return nullptr;
992
0
    }
993
994
1.50k
    const Tox_System *sys = testing->operating_system;
995
996
1.50k
    if (sys->rng == nullptr || sys->ns == nullptr || sys->mem == nullptr) {
997
0
        SET_ERROR_PARAMETER(error, TOX_ERR_NEW_NULL);
998
0
        SET_ERROR_PARAMETER(testing_error, TOX_ERR_NEW_TESTING_NULL);
999
0
        return nullptr;
1000
0
    }
1001
1002
1.50k
    SET_ERROR_PARAMETER(testing_error, TOX_ERR_NEW_TESTING_OK);
1003
1.50k
    return tox_new_system(options, error, sys);
1004
1.50k
}
1005
1006
void tox_kill(Tox *_Nullable tox)
1007
1.42k
{
1008
1.42k
    if (tox == nullptr) {
1009
24
        return;
1010
24
    }
1011
1012
1.40k
    tox_lock(tox);
1013
1.40k
    LOGGER_ASSERT(tox->m->log, tox->toxav_object == nullptr, "Attempted to kill tox while toxav is still alive");
1014
1.40k
    kill_groupchats(tox->m->conferences_object);
1015
1.40k
    kill_messenger(tox->m);
1016
1.40k
    logger_kill(tox->log);
1017
1.40k
    mono_time_free(tox->sys.mem, tox->mono_time);
1018
1.40k
    tox_unlock(tox);
1019
1020
1.40k
    if (tox->mutex != nullptr) {
1021
0
        pthread_mutex_destroy(tox->mutex);
1022
0
        mem_delete(tox->sys.mem, tox->mutex);
1023
0
    }
1024
1025
1.40k
    mem_delete(tox->sys.mem, tox);
1026
1.40k
}
1027
1028
static uint32_t end_size(void)
1029
1.98k
{
1030
1.98k
    return 2 * sizeof(uint32_t);
1031
1.98k
}
1032
1033
static void end_save(uint8_t *_Nonnull data)
1034
994
{
1035
994
    state_write_section_header(data, STATE_COOKIE_TYPE, 0, STATE_TYPE_END);
1036
994
}
1037
1038
size_t tox_get_savedata_size(const Tox *_Nonnull tox)
1039
1.98k
{
1040
1.98k
    assert(tox != nullptr);
1041
1.98k
    tox_lock(tox);
1042
1.98k
    const size_t ret = 2 * sizeof(uint32_t)
1043
1.98k
                       + messenger_size(tox->m)
1044
1.98k
                       + conferences_size(tox->m->conferences_object)
1045
1.98k
                       + end_size();
1046
1.98k
    tox_unlock(tox);
1047
1.98k
    return ret;
1048
1.98k
}
1049
1050
void tox_get_savedata(const Tox *_Nonnull tox, uint8_t *_Nullable savedata)
1051
994
{
1052
994
    assert(tox != nullptr);
1053
1054
994
    if (savedata == nullptr) {
1055
0
        return;
1056
0
    }
1057
1058
994
    memzero(savedata, tox_get_savedata_size(tox));
1059
1060
994
    tox_lock(tox);
1061
1062
994
    const uint32_t size32 = sizeof(uint32_t);
1063
1064
    // write cookie
1065
994
    memzero(savedata, size32);
1066
994
    savedata += size32;
1067
994
    host_to_lendian_bytes32(savedata, STATE_COOKIE_GLOBAL);
1068
994
    savedata += size32;
1069
1070
994
    savedata = messenger_save(tox->m, savedata);
1071
994
    savedata = conferences_save(tox->m->conferences_object, savedata);
1072
994
    end_save(savedata);
1073
1074
994
    tox_unlock(tox);
1075
994
}
1076
1077
static int32_t resolve_bootstrap_node(Tox *_Nullable tox, const char *_Nullable host, uint16_t port,
1078
                                      const Tox_Dht_Id _Nonnull public_key,
1079
                                      IP_Port *_Nonnull *root, Tox_Err_Bootstrap *_Nullable error)
1080
720
{
1081
720
    assert(tox != nullptr);
1082
720
    assert(root != nullptr);
1083
1084
720
    if (host == nullptr || public_key == nullptr) {
1085
0
        SET_ERROR_PARAMETER(error, TOX_ERR_BOOTSTRAP_NULL);
1086
0
        return -1;
1087
0
    }
1088
1089
720
    if (port == 0) {
1090
1
        SET_ERROR_PARAMETER(error, TOX_ERR_BOOTSTRAP_BAD_PORT);
1091
1
        return -1;
1092
1
    }
1093
1094
719
    const int32_t count = net_getipport(tox->sys.ns, tox->sys.mem, host, root, TOX_SOCK_DGRAM, tox->m->options.dns_enabled);
1095
1096
719
    if (count < 1) {
1097
13
        LOGGER_DEBUG(tox->m->log, "could not resolve bootstrap node '%s'", host);
1098
13
        net_freeipport(tox->sys.mem, *root);
1099
13
        SET_ERROR_PARAMETER(error, TOX_ERR_BOOTSTRAP_BAD_HOST);
1100
13
        return -1;
1101
13
    }
1102
1103
706
    assert(*root != nullptr);
1104
706
    return count;
1105
706
}
1106
1107
bool tox_bootstrap(Tox *_Nonnull tox, const char *_Nonnull host, uint16_t port, const Tox_Dht_Id _Nonnull public_key, Tox_Err_Bootstrap *_Nullable error)
1108
634
{
1109
634
    IP_Port *root;
1110
634
    const int32_t count = resolve_bootstrap_node(tox, host, port, public_key, &root, error);
1111
1112
634
    if (count == -1) {
1113
7
        return false;
1114
7
    }
1115
1116
627
    tox_lock(tox);
1117
627
    assert(count >= 0);
1118
627
    bool onion_success = false;
1119
    // UDP bootstrap is default success if it's disabled (because we don't even try).
1120
627
    bool udp_success = tox->m->options.udp_disabled;
1121
1122
1.25k
    for (int32_t i = 0; i < count; ++i) {
1123
630
        if (!tox->m->options.ipv6enabled && net_family_is_ipv6(root[i].ip.family)) {
1124
            // We can't use ipv6 when it's disabled.
1125
3
            continue;
1126
3
        }
1127
1128
627
        root[i].port = net_htons(port);
1129
1130
627
        if (onion_add_bs_path_node(tox->m->onion_c, &root[i], public_key)) {
1131
            // If UDP is enabled, the caller cares about whether any of the
1132
            // bootstrap calls below will succeed. In TCP-only mode, adding
1133
            // onion path nodes successfully is sufficient.
1134
627
            onion_success = true;
1135
627
        }
1136
1137
627
        if (!tox->m->options.udp_disabled) {
1138
556
            if (dht_bootstrap(tox->m->dht, &root[i], public_key)) {
1139
                // If any of the bootstrap calls worked, we call it success.
1140
553
                udp_success = true;
1141
553
            }
1142
556
        }
1143
627
    }
1144
1145
627
    tox_unlock(tox);
1146
1147
627
    net_freeipport(tox->sys.mem, root);
1148
1149
627
    if (count == 0 || !onion_success || !udp_success) {
1150
3
        LOGGER_DEBUG(tox->m->log, "bootstrap node '%s' resolved to %d IP_Ports%s (onion: %s, UDP: %s)",
1151
3
                     host, count,
1152
3
                     count > 0 ? ", but failed to bootstrap with any of them" : "",
1153
3
                     onion_success ? "success" : "FAILURE",
1154
3
                     tox->m->options.udp_disabled ? "disabled" : (udp_success ? "success" : "FAILURE"));
1155
3
        SET_ERROR_PARAMETER(error, TOX_ERR_BOOTSTRAP_BAD_HOST);
1156
3
        return false;
1157
3
    }
1158
1159
624
    SET_ERROR_PARAMETER(error, TOX_ERR_BOOTSTRAP_OK);
1160
624
    return true;
1161
627
}
1162
1163
bool tox_add_tcp_relay(Tox *_Nonnull tox, const char *_Nonnull host, uint16_t port, const Tox_Dht_Id _Nonnull public_key,
1164
                       Tox_Err_Bootstrap *_Nullable error)
1165
86
{
1166
86
    IP_Port *root;
1167
86
    const int32_t count = resolve_bootstrap_node(tox, host, port, public_key, &root, error);
1168
1169
86
    if (count == -1) {
1170
7
        return false;
1171
7
    }
1172
1173
79
    tox_lock(tox);
1174
79
    assert(count >= 0);
1175
1176
160
    for (int32_t i = 0; i < count; ++i) {
1177
81
        root[i].port = net_htons(port);
1178
1179
81
        add_tcp_relay(tox->m->net_crypto, &root[i], public_key);
1180
81
    }
1181
1182
79
    tox_unlock(tox);
1183
1184
79
    net_freeipport(tox->sys.mem, root);
1185
1186
79
    if (count == 0) {
1187
0
        SET_ERROR_PARAMETER(error, TOX_ERR_BOOTSTRAP_BAD_HOST);
1188
0
        return false;
1189
0
    }
1190
1191
79
    SET_ERROR_PARAMETER(error, TOX_ERR_BOOTSTRAP_OK);
1192
79
    return true;
1193
79
}
1194
1195
Tox_Connection tox_self_get_connection_status(const Tox *_Nonnull tox)
1196
126k
{
1197
126k
    assert(tox != nullptr);
1198
126k
    tox_lock(tox);
1199
126k
    const Onion_Connection_Status ret = onion_connection_status(tox->m->onion_c);
1200
126k
    tox_unlock(tox);
1201
1202
126k
    switch (ret) {
1203
55.1k
        case ONION_CONNECTION_STATUS_NONE:
1204
55.1k
            return TOX_CONNECTION_NONE;
1205
1206
70.6k
        case ONION_CONNECTION_STATUS_TCP:
1207
70.6k
            return TOX_CONNECTION_TCP;
1208
1209
500
        case ONION_CONNECTION_STATUS_UDP:
1210
500
            return TOX_CONNECTION_UDP;
1211
126k
    }
1212
1213
0
    LOGGER_FATAL(tox->m->log, "impossible return value: %u", ret);
1214
0
    return TOX_CONNECTION_NONE;
1215
126k
}
1216
1217
void tox_callback_self_connection_status(Tox *_Nonnull tox, tox_self_connection_status_cb *_Nullable callback)
1218
372
{
1219
372
    assert(tox != nullptr);
1220
372
    tox->self_connection_status_callback = callback;
1221
372
}
1222
1223
uint32_t tox_iteration_interval(const Tox *_Nonnull tox)
1224
346
{
1225
346
    assert(tox != nullptr);
1226
346
    tox_lock(tox);
1227
346
    uint32_t ret = messenger_run_interval(tox->m);
1228
1229
346
    if (m_is_receiving_file(tox->m)) {
1230
0
        ret = 1;
1231
0
    }
1232
1233
346
    tox_unlock(tox);
1234
346
    return ret;
1235
346
}
1236
1237
void tox_iterate_with_options(Tox *_Nonnull tox, const Tox_Iterate_Options *_Nullable options, void *_Nullable user_data)
1238
124k
{
1239
124k
    assert(tox != nullptr);
1240
124k
    tox_lock(tox);
1241
1242
124k
    mono_time_update(tox->mono_time);
1243
1244
124k
    struct Tox_Userdata tox_data = { tox, user_data };
1245
124k
    do_messenger(tox->m, &tox_data);
1246
124k
    do_groupchats(tox->m->conferences_object, &tox_data);
1247
1248
124k
    tox_unlock(tox);
1249
124k
}
1250
1251
void tox_iterate(Tox *_Nonnull tox, void *_Nullable user_data)
1252
4.94k
{
1253
4.94k
    tox_iterate_with_options(tox, nullptr, user_data);
1254
4.94k
}
1255
1256
void tox_self_get_address(const Tox *_Nonnull tox, Tox_Address _Nullable address)
1257
113k
{
1258
113k
    assert(tox != nullptr);
1259
1260
113k
    if (address != nullptr) {
1261
113k
        tox_lock(tox);
1262
113k
        getaddress(tox->m, address);
1263
113k
        tox_unlock(tox);
1264
113k
    }
1265
113k
}
1266
1267
void tox_self_set_nospam(Tox *_Nonnull tox, uint32_t nospam)
1268
2
{
1269
2
    assert(tox != nullptr);
1270
2
    tox_lock(tox);
1271
2
    set_nospam(tox->m->fr, net_htonl(nospam));
1272
2
    tox_unlock(tox);
1273
2
}
1274
1275
uint32_t tox_self_get_nospam(const Tox *_Nonnull tox)
1276
4
{
1277
4
    assert(tox != nullptr);
1278
4
    tox_lock(tox);
1279
4
    const uint32_t ret = net_ntohl(get_nospam(tox->m->fr));
1280
4
    tox_unlock(tox);
1281
4
    return ret;
1282
4
}
1283
1284
void tox_self_get_public_key(const Tox *_Nonnull tox, Tox_Public_Key _Nullable public_key)
1285
112k
{
1286
112k
    assert(tox != nullptr);
1287
1288
112k
    if (public_key != nullptr) {
1289
112k
        tox_lock(tox);
1290
112k
        memcpy(public_key, nc_get_self_public_key(tox->m->net_crypto), CRYPTO_PUBLIC_KEY_SIZE);
1291
112k
        tox_unlock(tox);
1292
112k
    }
1293
112k
}
1294
1295
void tox_self_get_secret_key(const Tox *_Nonnull tox, Tox_Secret_Key _Nullable secret_key)
1296
1
{
1297
1
    assert(tox != nullptr);
1298
1299
1
    if (secret_key != nullptr) {
1300
1
        tox_lock(tox);
1301
1
        memcpy(secret_key, nc_get_self_secret_key(tox->m->net_crypto), CRYPTO_SECRET_KEY_SIZE);
1302
1
        tox_unlock(tox);
1303
1
    }
1304
1
}
1305
1306
bool tox_self_set_name(Tox *_Nonnull tox, const uint8_t *_Nonnull name, size_t length, Tox_Err_Set_Info *_Nullable error)
1307
6
{
1308
6
    assert(tox != nullptr);
1309
1310
6
    if (name == nullptr && length != 0) {
1311
0
        SET_ERROR_PARAMETER(error, TOX_ERR_SET_INFO_NULL);
1312
0
        return false;
1313
0
    }
1314
1315
6
    tox_lock(tox);
1316
1317
6
    if (setname(tox->m, name, length) == 0) {
1318
        // TODO(irungentoo): function to set different per group names?
1319
6
        send_name_all_groups(tox->m->conferences_object);
1320
6
        SET_ERROR_PARAMETER(error, TOX_ERR_SET_INFO_OK);
1321
6
        tox_unlock(tox);
1322
6
        return true;
1323
6
    }
1324
1325
0
    SET_ERROR_PARAMETER(error, TOX_ERR_SET_INFO_TOO_LONG);
1326
0
    tox_unlock(tox);
1327
0
    return false;
1328
6
}
1329
1330
size_t tox_self_get_name_size(const Tox *_Nonnull tox)
1331
6
{
1332
6
    assert(tox != nullptr);
1333
6
    tox_lock(tox);
1334
6
    const size_t ret = m_get_self_name_size(tox->m);
1335
6
    tox_unlock(tox);
1336
6
    return ret;
1337
6
}
1338
1339
void tox_self_get_name(const Tox *_Nonnull tox, uint8_t *_Nullable name)
1340
4
{
1341
4
    assert(tox != nullptr);
1342
1343
4
    if (name != nullptr) {
1344
4
        tox_lock(tox);
1345
4
        getself_name(tox->m, name);
1346
4
        tox_unlock(tox);
1347
4
    }
1348
4
}
1349
1350
bool tox_self_set_status_message(Tox *_Nonnull tox, const uint8_t *_Nonnull status_message, size_t length, Tox_Err_Set_Info *_Nullable error)
1351
3
{
1352
3
    assert(tox != nullptr);
1353
1354
3
    if (status_message == nullptr && length != 0) {
1355
0
        SET_ERROR_PARAMETER(error, TOX_ERR_SET_INFO_NULL);
1356
0
        return false;
1357
0
    }
1358
1359
3
    tox_lock(tox);
1360
1361
3
    if (m_set_statusmessage(tox->m, status_message, length) == 0) {
1362
3
        SET_ERROR_PARAMETER(error, TOX_ERR_SET_INFO_OK);
1363
3
        tox_unlock(tox);
1364
3
        return true;
1365
3
    }
1366
1367
0
    SET_ERROR_PARAMETER(error, TOX_ERR_SET_INFO_TOO_LONG);
1368
0
    tox_unlock(tox);
1369
0
    return false;
1370
3
}
1371
1372
size_t tox_self_get_status_message_size(const Tox *_Nonnull tox)
1373
4
{
1374
4
    assert(tox != nullptr);
1375
4
    tox_lock(tox);
1376
4
    const size_t ret = m_get_self_statusmessage_size(tox->m);
1377
4
    tox_unlock(tox);
1378
4
    return ret;
1379
4
}
1380
1381
void tox_self_get_status_message(const Tox *_Nonnull tox, uint8_t *_Nullable status_message)
1382
2
{
1383
2
    assert(tox != nullptr);
1384
1385
2
    if (status_message != nullptr) {
1386
2
        tox_lock(tox);
1387
2
        m_copy_self_statusmessage(tox->m, status_message);
1388
2
        tox_unlock(tox);
1389
2
    }
1390
2
}
1391
1392
void tox_self_set_status(Tox *_Nonnull tox, Tox_User_Status status)
1393
3
{
1394
3
    assert(tox != nullptr);
1395
3
    tox_lock(tox);
1396
3
    m_set_userstatus(tox->m, status);
1397
3
    tox_unlock(tox);
1398
3
}
1399
1400
Tox_User_Status tox_self_get_status(const Tox *_Nonnull tox)
1401
3
{
1402
3
    assert(tox != nullptr);
1403
3
    tox_lock(tox);
1404
3
    const uint8_t status = m_get_self_userstatus(tox->m);
1405
3
    tox_unlock(tox);
1406
3
    return (Tox_User_Status)status;
1407
3
}
1408
1409
static void set_friend_error(const Logger *_Nonnull log, int32_t ret, Tox_Err_Friend_Add *_Nullable error)
1410
5
{
1411
5
    switch (ret) {
1412
1
        case FAERR_TOOLONG: {
1413
1
            SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_ADD_TOO_LONG);
1414
1
            break;
1415
0
        }
1416
1417
1
        case FAERR_NOMESSAGE: {
1418
1
            SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_ADD_NO_MESSAGE);
1419
1
            break;
1420
0
        }
1421
1422
1
        case FAERR_OWNKEY: {
1423
1
            SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_ADD_OWN_KEY);
1424
1
            break;
1425
0
        }
1426
1427
1
        case FAERR_ALREADYSENT: {
1428
1
            SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_ADD_ALREADY_SENT);
1429
1
            break;
1430
0
        }
1431
1432
1
        case FAERR_BADCHECKSUM: {
1433
1
            SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_ADD_BAD_CHECKSUM);
1434
1
            break;
1435
0
        }
1436
1437
0
        case FAERR_SETNEWNOSPAM: {
1438
0
            SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_ADD_SET_NEW_NOSPAM);
1439
0
            break;
1440
0
        }
1441
1442
0
        case FAERR_NOMEM: {
1443
0
            SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_ADD_MALLOC);
1444
0
            break;
1445
0
        }
1446
1447
0
        default: {
1448
            /* can't happen */
1449
0
            LOGGER_FATAL(log, "impossible return value: %d", ret);
1450
0
            break;
1451
0
        }
1452
5
    }
1453
5
}
1454
1455
uint32_t tox_friend_add(Tox *_Nonnull tox, const Tox_Address _Nonnull address, const uint8_t *_Nonnull message,
1456
                        size_t length, Tox_Err_Friend_Add *_Nullable error)
1457
145
{
1458
145
    assert(tox != nullptr);
1459
145
    if (address == nullptr || message == nullptr) {
1460
0
        SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_ADD_NULL);
1461
0
        return UINT32_MAX;
1462
0
    }
1463
1464
145
    tox_lock(tox);
1465
145
    const int32_t ret = m_addfriend(tox->m, address, message, length);
1466
1467
145
    if (ret >= 0) {
1468
140
        SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_ADD_OK);
1469
140
        tox_unlock(tox);
1470
140
        return (uint32_t)ret;
1471
140
    }
1472
1473
5
    set_friend_error(tox->m->log, ret, error);
1474
5
    tox_unlock(tox);
1475
5
    return UINT32_MAX;
1476
145
}
1477
1478
uint32_t tox_friend_add_norequest(Tox *_Nonnull tox, const Tox_Public_Key _Nonnull public_key, Tox_Err_Friend_Add *_Nullable error)
1479
286
{
1480
286
    assert(tox != nullptr);
1481
1482
286
    if (public_key == nullptr) {
1483
0
        SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_ADD_NULL);
1484
0
        return UINT32_MAX;
1485
0
    }
1486
1487
286
    tox_lock(tox);
1488
286
    const int32_t ret = m_addfriend_norequest(tox->m, public_key);
1489
1490
286
    if (ret >= 0) {
1491
286
        SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_ADD_OK);
1492
286
        tox_unlock(tox);
1493
286
        return (uint32_t)ret;
1494
286
    }
1495
1496
0
    set_friend_error(tox->m->log, ret, error);
1497
0
    tox_unlock(tox);
1498
0
    return UINT32_MAX;
1499
286
}
1500
1501
bool tox_friend_delete(Tox *_Nonnull tox, uint32_t friend_number, Tox_Err_Friend_Delete *_Nullable error)
1502
2
{
1503
2
    assert(tox != nullptr);
1504
2
    tox_lock(tox);
1505
2
    const int ret = m_delfriend(tox->m, friend_number);
1506
2
    tox_unlock(tox);
1507
1508
    // TODO(irungentoo): handle if realloc fails?
1509
2
    if (ret == -1) {
1510
0
        SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_DELETE_FRIEND_NOT_FOUND);
1511
0
        return false;
1512
0
    }
1513
1514
2
    SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_DELETE_OK);
1515
2
    return true;
1516
2
}
1517
1518
uint32_t tox_friend_by_public_key(const Tox *_Nonnull tox, const Tox_Public_Key _Nonnull public_key, Tox_Err_Friend_By_Public_Key *_Nullable error)
1519
1
{
1520
1
    assert(tox != nullptr);
1521
1522
1
    if (public_key == nullptr) {
1523
0
        SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_BY_PUBLIC_KEY_NULL);
1524
0
        return UINT32_MAX;
1525
0
    }
1526
1527
1
    tox_lock(tox);
1528
1
    const int32_t ret = getfriend_id(tox->m, public_key);
1529
1
    tox_unlock(tox);
1530
1531
1
    if (ret == -1) {
1532
0
        SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_BY_PUBLIC_KEY_NOT_FOUND);
1533
0
        return UINT32_MAX;
1534
0
    }
1535
1536
1
    SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_BY_PUBLIC_KEY_OK);
1537
1
    assert(ret >= 0);
1538
1
    return (uint32_t)ret;
1539
1
}
1540
1541
bool tox_friend_get_public_key(const Tox *_Nonnull tox, uint32_t friend_number, Tox_Public_Key _Nullable public_key,
1542
                               Tox_Err_Friend_Get_Public_Key *_Nullable error)
1543
3
{
1544
3
    assert(tox != nullptr);
1545
1546
3
    if (public_key == nullptr) {
1547
0
        return false;
1548
0
    }
1549
1550
3
    tox_lock(tox);
1551
1552
3
    if (get_real_pk(tox->m, friend_number, public_key) == -1) {
1553
0
        SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_GET_PUBLIC_KEY_FRIEND_NOT_FOUND);
1554
0
        tox_unlock(tox);
1555
0
        return false;
1556
0
    }
1557
1558
3
    SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_GET_PUBLIC_KEY_OK);
1559
3
    tox_unlock(tox);
1560
3
    return true;
1561
3
}
1562
1563
bool tox_friend_exists(const Tox *_Nonnull tox, uint32_t friend_number)
1564
120
{
1565
120
    assert(tox != nullptr);
1566
120
    tox_lock(tox);
1567
120
    const bool ret = m_friend_exists(tox->m, friend_number);
1568
120
    tox_unlock(tox);
1569
120
    return ret;
1570
120
}
1571
1572
uint64_t tox_friend_get_last_online(const Tox *_Nonnull tox, uint32_t friend_number, Tox_Err_Friend_Get_Last_Online *_Nullable error)
1573
1
{
1574
1
    assert(tox != nullptr);
1575
1
    tox_lock(tox);
1576
1
    const uint64_t timestamp = m_get_last_online(tox->m, friend_number);
1577
1
    tox_unlock(tox);
1578
1579
1
    if (timestamp == UINT64_MAX) {
1580
0
        SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_GET_LAST_ONLINE_FRIEND_NOT_FOUND);
1581
0
        return UINT64_MAX;
1582
0
    }
1583
1584
1
    SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_GET_LAST_ONLINE_OK);
1585
1
    return timestamp;
1586
1
}
1587
1588
size_t tox_self_get_friend_list_size(const Tox *_Nonnull tox)
1589
26.1k
{
1590
26.1k
    assert(tox != nullptr);
1591
26.0k
    tox_lock(tox);
1592
26.0k
    const size_t ret = count_friendlist(tox->m);
1593
26.0k
    tox_unlock(tox);
1594
26.0k
    return ret;
1595
26.1k
}
1596
1597
void tox_self_get_friend_list(const Tox *_Nonnull tox, uint32_t *_Nullable friend_list)
1598
1
{
1599
1
    assert(tox != nullptr);
1600
1601
1
    if (friend_list != nullptr) {
1602
1
        tox_lock(tox);
1603
        // TODO(irungentoo): size parameter?
1604
1
        copy_friendlist(tox->m, friend_list, count_friendlist(tox->m));
1605
1
        tox_unlock(tox);
1606
1
    }
1607
1
}
1608
1609
size_t tox_friend_get_name_size(const Tox *_Nonnull tox, uint32_t friend_number, Tox_Err_Friend_Query *_Nullable error)
1610
5
{
1611
5
    assert(tox != nullptr);
1612
5
    tox_lock(tox);
1613
5
    const int ret = m_get_name_size(tox->m, friend_number);
1614
5
    tox_unlock(tox);
1615
1616
5
    if (ret == -1) {
1617
0
        SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_QUERY_FRIEND_NOT_FOUND);
1618
0
        return SIZE_MAX;
1619
0
    }
1620
1621
5
    SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_QUERY_OK);
1622
5
    return ret;
1623
5
}
1624
1625
bool tox_friend_get_name(const Tox *_Nonnull tox, uint32_t friend_number, uint8_t *_Nullable name,
1626
                         Tox_Err_Friend_Query *_Nullable error)
1627
3
{
1628
3
    assert(tox != nullptr);
1629
1630
3
    if (name == nullptr) {
1631
0
        SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_QUERY_NULL);
1632
0
        return false;
1633
0
    }
1634
1635
3
    tox_lock(tox);
1636
3
    const int ret = getname(tox->m, friend_number, name);
1637
3
    tox_unlock(tox);
1638
1639
3
    if (ret == -1) {
1640
0
        SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_QUERY_FRIEND_NOT_FOUND);
1641
0
        return false;
1642
0
    }
1643
1644
3
    SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_QUERY_OK);
1645
3
    return true;
1646
3
}
1647
1648
void tox_callback_friend_name(Tox *_Nonnull tox, tox_friend_name_cb *_Nullable callback)
1649
372
{
1650
372
    assert(tox != nullptr);
1651
372
    tox->friend_name_callback = callback;
1652
372
}
1653
1654
size_t tox_friend_get_status_message_size(const Tox *_Nonnull tox, uint32_t friend_number, Tox_Err_Friend_Query *_Nullable error)
1655
6
{
1656
6
    assert(tox != nullptr);
1657
6
    tox_lock(tox);
1658
6
    const int ret = m_get_statusmessage_size(tox->m, friend_number);
1659
6
    tox_unlock(tox);
1660
1661
6
    if (ret == -1) {
1662
0
        SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_QUERY_FRIEND_NOT_FOUND);
1663
0
        return SIZE_MAX;
1664
0
    }
1665
1666
6
    SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_QUERY_OK);
1667
6
    return ret;
1668
6
}
1669
1670
bool tox_friend_get_status_message(const Tox *_Nonnull tox, uint32_t friend_number, uint8_t *_Nullable status_message,
1671
                                   Tox_Err_Friend_Query *_Nullable error)
1672
3
{
1673
3
    assert(tox != nullptr);
1674
1675
3
    if (status_message == nullptr) {
1676
0
        SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_QUERY_NULL);
1677
0
        return false;
1678
0
    }
1679
1680
3
    tox_lock(tox);
1681
3
    const int size = m_get_statusmessage_size(tox->m, friend_number);
1682
1683
3
    if (size == -1) {
1684
0
        SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_QUERY_FRIEND_NOT_FOUND);
1685
0
        tox_unlock(tox);
1686
0
        return false;
1687
0
    }
1688
1689
3
    const int ret = m_copy_statusmessage(tox->m, friend_number, status_message, size);
1690
3
    LOGGER_ASSERT(tox->m->log, ret == size, "concurrency problem: friend status message changed");
1691
1692
3
    SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_QUERY_OK);
1693
3
    tox_unlock(tox);
1694
3
    return ret == size;
1695
3
}
1696
1697
void tox_callback_friend_status_message(Tox *_Nonnull tox, tox_friend_status_message_cb *_Nullable callback)
1698
372
{
1699
372
    assert(tox != nullptr);
1700
372
    tox->friend_status_message_callback = callback;
1701
372
}
1702
1703
Tox_User_Status tox_friend_get_status(const Tox *_Nonnull tox, uint32_t friend_number, Tox_Err_Friend_Query *_Nullable error)
1704
0
{
1705
0
    assert(tox != nullptr);
1706
0
    tox_lock(tox);
1707
0
    const int ret = m_get_userstatus(tox->m, friend_number);
1708
0
    tox_unlock(tox);
1709
1710
0
    if (ret == USERSTATUS_INVALID) {
1711
0
        SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_QUERY_FRIEND_NOT_FOUND);
1712
0
        return TOX_USER_STATUS_NONE;
1713
0
    }
1714
1715
0
    SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_QUERY_OK);
1716
0
    return (Tox_User_Status)ret;
1717
0
}
1718
1719
void tox_callback_friend_status(Tox *_Nonnull tox, tox_friend_status_cb *_Nullable callback)
1720
372
{
1721
372
    assert(tox != nullptr);
1722
372
    tox->friend_status_callback = callback;
1723
372
}
1724
1725
Tox_Connection tox_friend_get_connection_status(const Tox *_Nonnull tox, uint32_t friend_number, Tox_Err_Friend_Query *_Nullable error)
1726
78.0k
{
1727
78.0k
    assert(tox != nullptr);
1728
78.0k
    tox_lock(tox);
1729
78.0k
    const int ret = m_get_friend_connectionstatus(tox->m, friend_number);
1730
78.0k
    tox_unlock(tox);
1731
1732
78.0k
    if (ret == -1) {
1733
99
        SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_QUERY_FRIEND_NOT_FOUND);
1734
99
        return TOX_CONNECTION_NONE;
1735
99
    }
1736
1737
77.9k
    SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_QUERY_OK);
1738
77.9k
    return (Tox_Connection)ret;
1739
78.0k
}
1740
1741
void tox_callback_friend_connection_status(Tox *_Nonnull tox, tox_friend_connection_status_cb *_Nullable callback)
1742
372
{
1743
372
    assert(tox != nullptr);
1744
372
    tox->friend_connection_status_callback = callback;
1745
372
}
1746
1747
bool tox_friend_get_typing(const Tox *_Nonnull tox, uint32_t friend_number, Tox_Err_Friend_Query *_Nullable error)
1748
14
{
1749
14
    assert(tox != nullptr);
1750
14
    tox_lock(tox);
1751
14
    const int ret = m_get_istyping(tox->m, friend_number);
1752
14
    tox_unlock(tox);
1753
1754
14
    if (ret == -1) {
1755
0
        SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_QUERY_FRIEND_NOT_FOUND);
1756
0
        return false;
1757
0
    }
1758
1759
14
    SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_QUERY_OK);
1760
14
    return ret != 0;
1761
14
}
1762
1763
void tox_callback_friend_typing(Tox *_Nonnull tox, tox_friend_typing_cb *_Nullable callback)
1764
372
{
1765
372
    assert(tox != nullptr);
1766
372
    tox->friend_typing_callback = callback;
1767
372
}
1768
1769
bool tox_self_set_typing(Tox *_Nonnull tox, uint32_t friend_number, bool typing, Tox_Err_Set_Typing *_Nullable error)
1770
2
{
1771
2
    assert(tox != nullptr);
1772
2
    tox_lock(tox);
1773
1774
2
    if (m_set_usertyping(tox->m, friend_number, typing) == -1) {
1775
0
        SET_ERROR_PARAMETER(error, TOX_ERR_SET_TYPING_FRIEND_NOT_FOUND);
1776
0
        tox_unlock(tox);
1777
0
        return false;
1778
0
    }
1779
1780
2
    SET_ERROR_PARAMETER(error, TOX_ERR_SET_TYPING_OK);
1781
2
    tox_unlock(tox);
1782
2
    return true;
1783
2
}
1784
1785
static void set_message_error(const Logger *_Nonnull log, int ret, Tox_Err_Friend_Send_Message *_Nullable error)
1786
120k
{
1787
120k
    switch (ret) {
1788
113k
        case 0: {
1789
113k
            SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_SEND_MESSAGE_OK);
1790
113k
            break;
1791
0
        }
1792
1793
0
        case -1: {
1794
0
            SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_SEND_MESSAGE_FRIEND_NOT_FOUND);
1795
0
            break;
1796
0
        }
1797
1798
1
        case -2: {
1799
1
            SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_SEND_MESSAGE_TOO_LONG);
1800
1
            break;
1801
0
        }
1802
1803
0
        case -3: {
1804
0
            SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_SEND_MESSAGE_FRIEND_NOT_CONNECTED);
1805
0
            break;
1806
0
        }
1807
1808
7.23k
        case -4: {
1809
7.23k
            SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_SEND_MESSAGE_SENDQ);
1810
7.23k
            break;
1811
0
        }
1812
1813
0
        case -5: {
1814
0
            LOGGER_FATAL(log, "impossible: Messenger and Tox disagree on message types");
1815
0
            break;
1816
0
        }
1817
1818
0
        default: {
1819
            /* can't happen */
1820
0
            LOGGER_FATAL(log, "impossible return value: %d", ret);
1821
0
            break;
1822
0
        }
1823
120k
    }
1824
120k
}
1825
1826
uint32_t tox_friend_send_message(Tox *_Nonnull tox, uint32_t friend_number, Tox_Message_Type type, const uint8_t *_Nonnull message,
1827
                                 size_t length, Tox_Err_Friend_Send_Message *_Nullable error)
1828
120k
{
1829
120k
    assert(tox != nullptr);
1830
1831
120k
    if (message == nullptr) {
1832
0
        SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_SEND_MESSAGE_NULL);
1833
0
        return 0;
1834
0
    }
1835
1836
120k
    if (length == 0) {
1837
0
        SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_SEND_MESSAGE_EMPTY);
1838
0
        return 0;
1839
0
    }
1840
1841
120k
    uint32_t message_id = 0;
1842
120k
    tox_lock(tox);
1843
120k
    set_message_error(tox->m->log, m_send_message_generic(tox->m, friend_number, type, message, length, &message_id),
1844
120k
                      error);
1845
120k
    tox_unlock(tox);
1846
120k
    return message_id;
1847
120k
}
1848
1849
void tox_callback_friend_read_receipt(Tox *_Nonnull tox, tox_friend_read_receipt_cb *_Nullable callback)
1850
372
{
1851
372
    assert(tox != nullptr);
1852
372
    tox->friend_read_receipt_callback = callback;
1853
372
}
1854
1855
void tox_callback_friend_request(Tox *_Nonnull tox, tox_friend_request_cb *_Nullable callback)
1856
373
{
1857
373
    assert(tox != nullptr);
1858
373
    tox->friend_request_callback = callback;
1859
373
}
1860
1861
void tox_callback_friend_message(Tox *_Nonnull tox, tox_friend_message_cb *_Nullable callback)
1862
372
{
1863
372
    assert(tox != nullptr);
1864
372
    tox->friend_message_callback = callback;
1865
372
}
1866
1867
bool tox_hash(Tox_Hash _Nonnull hash, const uint8_t *_Nullable data, size_t length)
1868
1
{
1869
1
    if (hash == nullptr || (data == nullptr && length != 0)) {
1870
0
        return false;
1871
0
    }
1872
1873
1
    crypto_sha256(hash, data, length);
1874
1
    return true;
1875
1
}
1876
1877
bool tox_file_control(Tox *_Nonnull tox, uint32_t friend_number, uint32_t file_number, Tox_File_Control control,
1878
                      Tox_Err_File_Control *_Nullable error)
1879
4
{
1880
4
    assert(tox != nullptr);
1881
4
    tox_lock(tox);
1882
4
    const int ret = file_control(tox->m, friend_number, file_number, control);
1883
4
    tox_unlock(tox);
1884
1885
4
    if (ret == 0) {
1886
4
        SET_ERROR_PARAMETER(error, TOX_ERR_FILE_CONTROL_OK);
1887
4
        return true;
1888
4
    }
1889
1890
0
    switch (ret) {
1891
0
        case -1: {
1892
0
            SET_ERROR_PARAMETER(error, TOX_ERR_FILE_CONTROL_FRIEND_NOT_FOUND);
1893
0
            return false;
1894
0
        }
1895
1896
0
        case -2: {
1897
0
            SET_ERROR_PARAMETER(error, TOX_ERR_FILE_CONTROL_FRIEND_NOT_CONNECTED);
1898
0
            return false;
1899
0
        }
1900
1901
0
        case -3: {
1902
0
            SET_ERROR_PARAMETER(error, TOX_ERR_FILE_CONTROL_NOT_FOUND);
1903
0
            return false;
1904
0
        }
1905
1906
0
        case -4: {
1907
            /* can't happen (this code is returned if `control` is invalid type) */
1908
0
            LOGGER_FATAL(tox->m->log, "impossible return value: %d", ret);
1909
0
            return false;
1910
0
        }
1911
1912
0
        case -5: {
1913
0
            SET_ERROR_PARAMETER(error, TOX_ERR_FILE_CONTROL_ALREADY_PAUSED);
1914
0
            return false;
1915
0
        }
1916
1917
0
        case -6: {
1918
0
            SET_ERROR_PARAMETER(error, TOX_ERR_FILE_CONTROL_DENIED);
1919
0
            return false;
1920
0
        }
1921
1922
0
        case -7: {
1923
0
            SET_ERROR_PARAMETER(error, TOX_ERR_FILE_CONTROL_NOT_PAUSED);
1924
0
            return false;
1925
0
        }
1926
1927
0
        case -8: {
1928
0
            SET_ERROR_PARAMETER(error, TOX_ERR_FILE_CONTROL_SENDQ);
1929
0
            return false;
1930
0
        }
1931
0
    }
1932
1933
    /* can't happen */
1934
0
    LOGGER_FATAL(tox->m->log, "impossible return value: %d", ret);
1935
1936
0
    return false;
1937
0
}
1938
1939
bool tox_file_seek(Tox *_Nonnull tox, uint32_t friend_number, uint32_t file_number, uint64_t position,
1940
                   Tox_Err_File_Seek *_Nullable error)
1941
1
{
1942
1
    assert(tox != nullptr);
1943
1
    tox_lock(tox);
1944
1
    const int ret = file_seek(tox->m, friend_number, file_number, position);
1945
1
    tox_unlock(tox);
1946
1947
1
    if (ret == 0) {
1948
1
        SET_ERROR_PARAMETER(error, TOX_ERR_FILE_SEEK_OK);
1949
1
        return true;
1950
1
    }
1951
1952
0
    switch (ret) {
1953
0
        case -1: {
1954
0
            SET_ERROR_PARAMETER(error, TOX_ERR_FILE_SEEK_FRIEND_NOT_FOUND);
1955
0
            return false;
1956
0
        }
1957
1958
0
        case -2: {
1959
0
            SET_ERROR_PARAMETER(error, TOX_ERR_FILE_SEEK_FRIEND_NOT_CONNECTED);
1960
0
            return false;
1961
0
        }
1962
1963
0
        case -3: {
1964
0
            SET_ERROR_PARAMETER(error, TOX_ERR_FILE_SEEK_NOT_FOUND);
1965
0
            return false;
1966
0
        }
1967
1968
0
        case -4: // fall-through
1969
0
        case -5: {
1970
0
            SET_ERROR_PARAMETER(error, TOX_ERR_FILE_SEEK_DENIED);
1971
0
            return false;
1972
0
        }
1973
1974
0
        case -6: {
1975
0
            SET_ERROR_PARAMETER(error, TOX_ERR_FILE_SEEK_INVALID_POSITION);
1976
0
            return false;
1977
0
        }
1978
1979
0
        case -8: {
1980
0
            SET_ERROR_PARAMETER(error, TOX_ERR_FILE_SEEK_SENDQ);
1981
0
            return false;
1982
0
        }
1983
0
    }
1984
1985
    /* can't happen */
1986
0
    LOGGER_FATAL(tox->m->log, "impossible return value: %d", ret);
1987
1988
0
    return false;
1989
0
}
1990
1991
void tox_callback_file_recv_control(Tox *_Nonnull tox, tox_file_recv_control_cb *_Nullable callback)
1992
372
{
1993
372
    assert(tox != nullptr);
1994
372
    tox->file_recv_control_callback = callback;
1995
372
}
1996
1997
bool tox_file_get_file_id(const Tox *_Nonnull tox, uint32_t friend_number, uint32_t file_number, Tox_File_Id _Nonnull file_id,
1998
                          Tox_Err_File_Get *_Nullable error)
1999
1
{
2000
1
    assert(tox != nullptr);
2001
2002
1
    if (file_id == nullptr) {
2003
0
        SET_ERROR_PARAMETER(error, TOX_ERR_FILE_GET_NULL);
2004
0
        return false;
2005
0
    }
2006
2007
1
    tox_lock(tox);
2008
1
    const int ret = file_get_id(tox->m, friend_number, file_number, file_id);
2009
1
    tox_unlock(tox);
2010
2011
1
    if (ret == 0) {
2012
1
        SET_ERROR_PARAMETER(error, TOX_ERR_FILE_GET_OK);
2013
1
        return true;
2014
1
    }
2015
2016
0
    if (ret == -1) {
2017
0
        SET_ERROR_PARAMETER(error, TOX_ERR_FILE_GET_FRIEND_NOT_FOUND);
2018
0
    } else {
2019
0
        SET_ERROR_PARAMETER(error, TOX_ERR_FILE_GET_NOT_FOUND);
2020
0
    }
2021
2022
0
    return false;
2023
1
}
2024
2025
Tox_File_Number tox_file_by_id(const Tox *_Nonnull tox, Tox_Friend_Number friend_number, const Tox_File_Id _Nonnull file_id,
2026
                               Tox_Err_File_By_Id *_Nullable error)
2027
3
{
2028
3
    assert(tox != nullptr);
2029
2030
3
    if (file_id == nullptr) {
2031
0
        SET_ERROR_PARAMETER(error, TOX_ERR_FILE_BY_ID_NULL);
2032
0
        return UINT32_MAX;
2033
0
    }
2034
2035
3
    tox_lock(tox);
2036
3
    const int32_t ret = file_by_id(tox->m, friend_number, file_id);
2037
3
    tox_unlock(tox);
2038
2039
3
    if (ret >= 0) {
2040
1
        SET_ERROR_PARAMETER(error, TOX_ERR_FILE_BY_ID_OK);
2041
1
        return (Tox_File_Number)ret;
2042
1
    }
2043
2044
2
    if (ret == -1) {
2045
1
        SET_ERROR_PARAMETER(error, TOX_ERR_FILE_BY_ID_FRIEND_NOT_FOUND);
2046
1
    } else {
2047
1
        SET_ERROR_PARAMETER(error, TOX_ERR_FILE_BY_ID_NOT_FOUND);
2048
1
    }
2049
2050
2
    return UINT32_MAX;
2051
3
}
2052
2053
uint32_t tox_file_send(Tox *_Nonnull tox, uint32_t friend_number, uint32_t kind, uint64_t file_size, const Tox_File_Id _Nullable file_id,
2054
                       const uint8_t *_Nonnull filename, size_t filename_length, Tox_Err_File_Send *_Nullable error)
2055
4
{
2056
4
    assert(tox != nullptr);
2057
2058
4
    if (filename == nullptr && filename_length != 0) {
2059
0
        SET_ERROR_PARAMETER(error, TOX_ERR_FILE_SEND_NULL);
2060
0
        return UINT32_MAX;
2061
0
    }
2062
2063
4
    uint8_t f_id[FILE_ID_LENGTH];
2064
2065
4
    if (file_id == nullptr) {
2066
        /* Tox keys are 32 bytes like FILE_ID_LENGTH. */
2067
3
        new_symmetric_key(tox->sys.rng, f_id);
2068
3
        file_id = f_id;
2069
3
    }
2070
2071
4
    tox_lock(tox);
2072
4
    const long int file_num = new_filesender(tox->m, friend_number, kind, file_size, file_id, filename, filename_length);
2073
4
    tox_unlock(tox);
2074
2075
4
    if (file_num >= 0) {
2076
4
        SET_ERROR_PARAMETER(error, TOX_ERR_FILE_SEND_OK);
2077
4
        return file_num;
2078
4
    }
2079
2080
0
    switch (file_num) {
2081
0
        case -1: {
2082
0
            SET_ERROR_PARAMETER(error, TOX_ERR_FILE_SEND_FRIEND_NOT_FOUND);
2083
0
            return UINT32_MAX;
2084
0
        }
2085
2086
0
        case -2: {
2087
0
            SET_ERROR_PARAMETER(error, TOX_ERR_FILE_SEND_NAME_TOO_LONG);
2088
0
            return UINT32_MAX;
2089
0
        }
2090
2091
0
        case -3: {
2092
0
            SET_ERROR_PARAMETER(error, TOX_ERR_FILE_SEND_TOO_MANY);
2093
0
            return UINT32_MAX;
2094
0
        }
2095
2096
0
        case -4: {
2097
0
            SET_ERROR_PARAMETER(error, TOX_ERR_FILE_SEND_FRIEND_NOT_CONNECTED);
2098
0
            return UINT32_MAX;
2099
0
        }
2100
0
    }
2101
2102
    /* can't happen */
2103
0
    LOGGER_FATAL(tox->m->log, "impossible return value: %ld", file_num);
2104
2105
0
    return UINT32_MAX;
2106
0
}
2107
2108
bool tox_file_send_chunk(Tox *_Nonnull tox, uint32_t friend_number, uint32_t file_number, uint64_t position, const uint8_t *_Nullable data,
2109
                         size_t length, Tox_Err_File_Send_Chunk *_Nullable error)
2110
1.14k
{
2111
1.14k
    assert(tox != nullptr);
2112
1.14k
    assert(length == 0 || data != nullptr);
2113
1.14k
    tox_lock(tox);
2114
1.14k
    const int ret = send_file_data(tox->m, friend_number, file_number, position, data, length);
2115
1.14k
    tox_unlock(tox);
2116
2117
1.14k
    if (ret == 0) {
2118
1.14k
        SET_ERROR_PARAMETER(error, TOX_ERR_FILE_SEND_CHUNK_OK);
2119
1.14k
        return true;
2120
1.14k
    }
2121
2122
0
    switch (ret) {
2123
0
        case -1: {
2124
0
            SET_ERROR_PARAMETER(error, TOX_ERR_FILE_SEND_CHUNK_FRIEND_NOT_FOUND);
2125
0
            return false;
2126
0
        }
2127
2128
0
        case -2: {
2129
0
            SET_ERROR_PARAMETER(error, TOX_ERR_FILE_SEND_CHUNK_FRIEND_NOT_CONNECTED);
2130
0
            return false;
2131
0
        }
2132
2133
0
        case -3: {
2134
0
            SET_ERROR_PARAMETER(error, TOX_ERR_FILE_SEND_CHUNK_NOT_FOUND);
2135
0
            return false;
2136
0
        }
2137
2138
0
        case -4: {
2139
0
            SET_ERROR_PARAMETER(error, TOX_ERR_FILE_SEND_CHUNK_NOT_TRANSFERRING);
2140
0
            return false;
2141
0
        }
2142
2143
0
        case -5: {
2144
0
            SET_ERROR_PARAMETER(error, TOX_ERR_FILE_SEND_CHUNK_INVALID_LENGTH);
2145
0
            return false;
2146
0
        }
2147
2148
0
        case -6: {
2149
0
            SET_ERROR_PARAMETER(error, TOX_ERR_FILE_SEND_CHUNK_SENDQ);
2150
0
            return false;
2151
0
        }
2152
2153
0
        case -7: {
2154
0
            SET_ERROR_PARAMETER(error, TOX_ERR_FILE_SEND_CHUNK_WRONG_POSITION);
2155
0
            return false;
2156
0
        }
2157
0
    }
2158
2159
    /* can't happen */
2160
0
    LOGGER_FATAL(tox->m->log, "impossible return value: %d", ret);
2161
2162
0
    return false;
2163
0
}
2164
2165
void tox_callback_file_chunk_request(Tox *_Nonnull tox, tox_file_chunk_request_cb *_Nullable callback)
2166
372
{
2167
372
    assert(tox != nullptr);
2168
372
    tox->file_chunk_request_callback = callback;
2169
372
}
2170
2171
void tox_callback_file_recv(Tox *_Nonnull tox, tox_file_recv_cb *_Nullable callback)
2172
372
{
2173
372
    assert(tox != nullptr);
2174
372
    tox->file_recv_callback = callback;
2175
372
}
2176
2177
void tox_callback_file_recv_chunk(Tox *_Nonnull tox, tox_file_recv_chunk_cb *_Nullable callback)
2178
372
{
2179
372
    assert(tox != nullptr);
2180
372
    tox->file_recv_chunk_callback = callback;
2181
372
}
2182
2183
void tox_callback_conference_invite(Tox *_Nonnull tox, tox_conference_invite_cb *_Nullable callback)
2184
372
{
2185
372
    assert(tox != nullptr);
2186
372
    tox->conference_invite_callback = callback;
2187
372
}
2188
2189
void tox_callback_conference_connected(Tox *_Nonnull tox, tox_conference_connected_cb *_Nullable callback)
2190
372
{
2191
372
    assert(tox != nullptr);
2192
372
    tox->conference_connected_callback = callback;
2193
372
}
2194
2195
void tox_callback_conference_message(Tox *_Nonnull tox, tox_conference_message_cb *_Nullable callback)
2196
372
{
2197
372
    assert(tox != nullptr);
2198
372
    tox->conference_message_callback = callback;
2199
372
}
2200
2201
void tox_callback_conference_title(Tox *_Nonnull tox, tox_conference_title_cb *_Nullable callback)
2202
372
{
2203
372
    assert(tox != nullptr);
2204
372
    tox->conference_title_callback = callback;
2205
372
}
2206
2207
void tox_callback_conference_peer_name(Tox *_Nonnull tox, tox_conference_peer_name_cb *_Nullable callback)
2208
372
{
2209
372
    assert(tox != nullptr);
2210
372
    tox->conference_peer_name_callback = callback;
2211
372
}
2212
2213
void tox_callback_conference_peer_list_changed(Tox *_Nonnull tox, tox_conference_peer_list_changed_cb *_Nullable callback)
2214
372
{
2215
372
    assert(tox != nullptr);
2216
372
    tox->conference_peer_list_changed_callback = callback;
2217
372
}
2218
2219
uint32_t tox_conference_new(Tox *_Nonnull tox, Tox_Err_Conference_New *_Nullable error)
2220
9
{
2221
9
    assert(tox != nullptr);
2222
9
    tox_lock(tox);
2223
9
    const int ret = add_groupchat(tox->m->conferences_object, tox->sys.rng, GROUPCHAT_TYPE_TEXT);
2224
9
    tox_unlock(tox);
2225
2226
9
    if (ret == -1) {
2227
0
        SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_NEW_INIT);
2228
0
        return UINT32_MAX;
2229
0
    }
2230
2231
9
    SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_NEW_OK);
2232
9
    return ret;
2233
9
}
2234
2235
bool tox_conference_delete(Tox *_Nonnull tox, uint32_t conference_number, Tox_Err_Conference_Delete *_Nullable error)
2236
1
{
2237
1
    assert(tox != nullptr);
2238
1
    tox_lock(tox);
2239
1
    const bool ret = del_groupchat(tox->m->conferences_object, conference_number, true);
2240
1
    tox_unlock(tox);
2241
2242
1
    if (!ret) {
2243
0
        SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_DELETE_CONFERENCE_NOT_FOUND);
2244
0
        return false;
2245
0
    }
2246
2247
1
    SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_DELETE_OK);
2248
1
    return true;
2249
1
}
2250
2251
uint32_t tox_conference_peer_count(const Tox *_Nonnull tox, uint32_t conference_number, Tox_Err_Conference_Peer_Query *_Nullable error)
2252
3.48k
{
2253
3.48k
    assert(tox != nullptr);
2254
3.48k
    tox_lock(tox);
2255
3.48k
    const int ret = group_number_peers(tox->m->conferences_object, conference_number, false);
2256
3.48k
    tox_unlock(tox);
2257
2258
3.48k
    if (ret == -1) {
2259
0
        SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_PEER_QUERY_CONFERENCE_NOT_FOUND);
2260
0
        return UINT32_MAX;
2261
0
    }
2262
2263
3.48k
    SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_PEER_QUERY_OK);
2264
3.48k
    return ret;
2265
3.48k
}
2266
2267
size_t tox_conference_peer_get_name_size(const Tox *_Nonnull tox, uint32_t conference_number, uint32_t peer_number,
2268
        Tox_Err_Conference_Peer_Query *_Nullable error)
2269
0
{
2270
0
    assert(tox != nullptr);
2271
0
    tox_lock(tox);
2272
0
    const int ret = group_peername_size(tox->m->conferences_object, conference_number, peer_number, false);
2273
0
    tox_unlock(tox);
2274
2275
0
    switch (ret) {
2276
0
        case -1: {
2277
0
            SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_PEER_QUERY_CONFERENCE_NOT_FOUND);
2278
0
            return -1;
2279
0
        }
2280
2281
0
        case -2: {
2282
0
            SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_PEER_QUERY_PEER_NOT_FOUND);
2283
0
            return -1;
2284
0
        }
2285
0
    }
2286
2287
0
    SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_PEER_QUERY_OK);
2288
0
    return ret;
2289
0
}
2290
2291
bool tox_conference_peer_get_name(const Tox *_Nonnull tox, uint32_t conference_number, uint32_t peer_number, uint8_t *_Nonnull name,
2292
                                  Tox_Err_Conference_Peer_Query *_Nullable error)
2293
0
{
2294
0
    assert(tox != nullptr);
2295
0
    tox_lock(tox);
2296
0
    const int ret = group_peername(tox->m->conferences_object, conference_number, peer_number, name, false);
2297
0
    tox_unlock(tox);
2298
2299
0
    switch (ret) {
2300
0
        case -1: {
2301
0
            SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_PEER_QUERY_CONFERENCE_NOT_FOUND);
2302
0
            return false;
2303
0
        }
2304
2305
0
        case -2: {
2306
0
            SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_PEER_QUERY_PEER_NOT_FOUND);
2307
0
            return false;
2308
0
        }
2309
0
    }
2310
2311
0
    SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_PEER_QUERY_OK);
2312
0
    return true;
2313
0
}
2314
2315
bool tox_conference_peer_get_public_key(const Tox *_Nonnull tox, uint32_t conference_number, uint32_t peer_number,
2316
                                        uint8_t public_key[_Nonnull TOX_PUBLIC_KEY_SIZE], Tox_Err_Conference_Peer_Query *_Nullable error)
2317
2
{
2318
2
    assert(tox != nullptr);
2319
2
    tox_lock(tox);
2320
2
    const int ret = group_peer_pubkey(tox->m->conferences_object, conference_number, peer_number, public_key, false);
2321
2
    tox_unlock(tox);
2322
2323
2
    switch (ret) {
2324
0
        case -1: {
2325
0
            SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_PEER_QUERY_CONFERENCE_NOT_FOUND);
2326
0
            return false;
2327
0
        }
2328
2329
0
        case -2: {
2330
0
            SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_PEER_QUERY_PEER_NOT_FOUND);
2331
0
            return false;
2332
0
        }
2333
2
    }
2334
2335
2
    SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_PEER_QUERY_OK);
2336
2
    return true;
2337
2
}
2338
2339
bool tox_conference_peer_number_is_ours(const Tox *_Nonnull tox, uint32_t conference_number, uint32_t peer_number,
2340
                                        Tox_Err_Conference_Peer_Query *_Nullable error)
2341
2
{
2342
2
    assert(tox != nullptr);
2343
2
    tox_lock(tox);
2344
2
    const int ret = group_peernumber_is_ours(tox->m->conferences_object, conference_number, peer_number);
2345
2
    tox_unlock(tox);
2346
2347
2
    switch (ret) {
2348
0
        case -1: {
2349
0
            SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_PEER_QUERY_CONFERENCE_NOT_FOUND);
2350
0
            return false;
2351
0
        }
2352
2353
0
        case -2: {
2354
0
            SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_PEER_QUERY_PEER_NOT_FOUND);
2355
0
            return false;
2356
0
        }
2357
2358
0
        case -3: {
2359
0
            SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_PEER_QUERY_NO_CONNECTION);
2360
0
            return false;
2361
0
        }
2362
2
    }
2363
2364
2
    SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_PEER_QUERY_OK);
2365
2
    return ret != 0;
2366
2
}
2367
2368
uint32_t tox_conference_offline_peer_count(const Tox *_Nonnull tox, uint32_t conference_number,
2369
        Tox_Err_Conference_Peer_Query *_Nullable error)
2370
1
{
2371
1
    assert(tox != nullptr);
2372
1
    tox_lock(tox);
2373
1
    const int ret = group_number_peers(tox->m->conferences_object, conference_number, true);
2374
1
    tox_unlock(tox);
2375
2376
1
    if (ret == -1) {
2377
0
        SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_PEER_QUERY_CONFERENCE_NOT_FOUND);
2378
0
        return UINT32_MAX;
2379
0
    }
2380
2381
1
    SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_PEER_QUERY_OK);
2382
1
    return ret;
2383
1
}
2384
2385
size_t tox_conference_offline_peer_get_name_size(const Tox *_Nonnull tox, uint32_t conference_number,
2386
        uint32_t offline_peer_number,
2387
        Tox_Err_Conference_Peer_Query *_Nullable error)
2388
0
{
2389
0
    assert(tox != nullptr);
2390
0
    tox_lock(tox);
2391
0
    const int ret = group_peername_size(tox->m->conferences_object, conference_number, offline_peer_number, true);
2392
0
    tox_unlock(tox);
2393
2394
0
    switch (ret) {
2395
0
        case -1: {
2396
0
            SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_PEER_QUERY_CONFERENCE_NOT_FOUND);
2397
0
            return -1;
2398
0
        }
2399
2400
0
        case -2: {
2401
0
            SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_PEER_QUERY_PEER_NOT_FOUND);
2402
0
            return -1;
2403
0
        }
2404
0
    }
2405
2406
0
    SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_PEER_QUERY_OK);
2407
0
    return ret;
2408
0
}
2409
2410
bool tox_conference_offline_peer_get_name(const Tox *_Nonnull tox, uint32_t conference_number, uint32_t offline_peer_number,
2411
        uint8_t *_Nonnull name,
2412
        Tox_Err_Conference_Peer_Query *_Nullable error)
2413
0
{
2414
0
    assert(tox != nullptr);
2415
0
    tox_lock(tox);
2416
0
    const int ret = group_peername(tox->m->conferences_object, conference_number, offline_peer_number, name, true);
2417
0
    tox_unlock(tox);
2418
2419
0
    switch (ret) {
2420
0
        case -1: {
2421
0
            SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_PEER_QUERY_CONFERENCE_NOT_FOUND);
2422
0
            return false;
2423
0
        }
2424
2425
0
        case -2: {
2426
0
            SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_PEER_QUERY_PEER_NOT_FOUND);
2427
0
            return false;
2428
0
        }
2429
0
    }
2430
2431
0
    SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_PEER_QUERY_OK);
2432
0
    return true;
2433
0
}
2434
2435
bool tox_conference_offline_peer_get_public_key(const Tox *_Nonnull tox, uint32_t conference_number,
2436
        uint32_t offline_peer_number,
2437
        uint8_t public_key[_Nonnull TOX_PUBLIC_KEY_SIZE], Tox_Err_Conference_Peer_Query *_Nullable error)
2438
0
{
2439
0
    assert(tox != nullptr);
2440
0
    tox_lock(tox);
2441
0
    const int ret = group_peer_pubkey(tox->m->conferences_object, conference_number, offline_peer_number, public_key, true);
2442
0
    tox_unlock(tox);
2443
2444
0
    switch (ret) {
2445
0
        case -1: {
2446
0
            SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_PEER_QUERY_CONFERENCE_NOT_FOUND);
2447
0
            return false;
2448
0
        }
2449
2450
0
        case -2: {
2451
0
            SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_PEER_QUERY_PEER_NOT_FOUND);
2452
0
            return false;
2453
0
        }
2454
0
    }
2455
2456
0
    SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_PEER_QUERY_OK);
2457
0
    return true;
2458
0
}
2459
2460
uint64_t tox_conference_offline_peer_get_last_active(const Tox *_Nonnull tox, uint32_t conference_number,
2461
        uint32_t offline_peer_number,
2462
        Tox_Err_Conference_Peer_Query *_Nullable error)
2463
0
{
2464
0
    assert(tox != nullptr);
2465
0
    uint64_t last_active = UINT64_MAX;
2466
0
    tox_lock(tox);
2467
0
    const int ret = group_frozen_last_active(tox->m->conferences_object, conference_number, offline_peer_number,
2468
0
                    &last_active);
2469
0
    tox_unlock(tox);
2470
2471
0
    switch (ret) {
2472
0
        case -1: {
2473
0
            SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_PEER_QUERY_CONFERENCE_NOT_FOUND);
2474
0
            return UINT64_MAX;
2475
0
        }
2476
2477
0
        case -2: {
2478
0
            SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_PEER_QUERY_PEER_NOT_FOUND);
2479
0
            return UINT64_MAX;
2480
0
        }
2481
0
    }
2482
2483
0
    SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_PEER_QUERY_OK);
2484
0
    return last_active;
2485
0
}
2486
2487
bool tox_conference_set_max_offline(Tox *_Nonnull tox, uint32_t conference_number,
2488
                                    uint32_t max_offline,
2489
                                    Tox_Err_Conference_Set_Max_Offline *_Nullable error)
2490
1
{
2491
1
    assert(tox != nullptr);
2492
1
    tox_lock(tox);
2493
1
    const int ret = group_set_max_frozen(tox->m->conferences_object, conference_number, max_offline);
2494
1
    tox_unlock(tox);
2495
2496
1
    if (ret == -1) {
2497
0
        SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_SET_MAX_OFFLINE_CONFERENCE_NOT_FOUND);
2498
0
        return false;
2499
0
    }
2500
2501
1
    SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_SET_MAX_OFFLINE_OK);
2502
1
    return true;
2503
1
}
2504
2505
bool tox_conference_invite(Tox *_Nonnull tox, uint32_t friend_number, uint32_t conference_number,
2506
                           Tox_Err_Conference_Invite *_Nullable error)
2507
32
{
2508
32
    assert(tox != nullptr);
2509
32
    tox_lock(tox);
2510
32
    const int ret = invite_friend(tox->m->conferences_object, friend_number, conference_number);
2511
32
    tox_unlock(tox);
2512
2513
32
    switch (ret) {
2514
0
        case -1: {
2515
0
            SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_INVITE_CONFERENCE_NOT_FOUND);
2516
0
            return false;
2517
0
        }
2518
2519
0
        case -2: {
2520
0
            SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_INVITE_FAIL_SEND);
2521
0
            return false;
2522
0
        }
2523
2524
2
        case -3: {
2525
2
            SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_INVITE_NO_CONNECTION);
2526
2
            return false;
2527
0
        }
2528
32
    }
2529
2530
30
    SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_INVITE_OK);
2531
30
    return true;
2532
32
}
2533
2534
uint32_t tox_conference_join(Tox *_Nonnull tox, uint32_t friend_number, const uint8_t *_Nonnull cookie, size_t length,
2535
                             Tox_Err_Conference_Join *_Nullable error)
2536
25
{
2537
25
    assert(tox != nullptr);
2538
2539
25
    if (cookie == nullptr) {
2540
0
        SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_JOIN_NULL);
2541
0
        return UINT32_MAX;
2542
0
    }
2543
2544
25
    tox_lock(tox);
2545
25
    const int ret = join_groupchat(tox->m->conferences_object, friend_number, GROUPCHAT_TYPE_TEXT, cookie, length);
2546
25
    tox_unlock(tox);
2547
2548
25
    switch (ret) {
2549
0
        case -1: {
2550
0
            SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_JOIN_INVALID_LENGTH);
2551
0
            return UINT32_MAX;
2552
0
        }
2553
2554
0
        case -2: {
2555
0
            SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_JOIN_WRONG_TYPE);
2556
0
            return UINT32_MAX;
2557
0
        }
2558
2559
0
        case -3: {
2560
0
            SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_JOIN_FRIEND_NOT_FOUND);
2561
0
            return UINT32_MAX;
2562
0
        }
2563
2564
0
        case -4: {
2565
0
            SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_JOIN_DUPLICATE);
2566
0
            return UINT32_MAX;
2567
0
        }
2568
2569
0
        case -5: {
2570
0
            SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_JOIN_INIT_FAIL);
2571
0
            return UINT32_MAX;
2572
0
        }
2573
2574
0
        case -6: {
2575
0
            SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_JOIN_FAIL_SEND);
2576
0
            return UINT32_MAX;
2577
0
        }
2578
25
    }
2579
2580
25
    SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_JOIN_OK);
2581
25
    return ret;
2582
25
}
2583
2584
bool tox_conference_send_message(Tox *_Nonnull tox, uint32_t conference_number, Tox_Message_Type type, const uint8_t *_Nonnull message,
2585
                                 size_t length, Tox_Err_Conference_Send_Message *_Nullable error)
2586
2
{
2587
2
    assert(tox != nullptr);
2588
2
    tox_lock(tox);
2589
2
    int ret = 0;
2590
2591
2
    if (type == TOX_MESSAGE_TYPE_NORMAL) {
2592
2
        ret = group_message_send(tox->m->conferences_object, conference_number, message, length);
2593
2
    } else {
2594
0
        ret = group_action_send(tox->m->conferences_object, conference_number, message, length);
2595
0
    }
2596
2597
2
    tox_unlock(tox);
2598
2599
2
    switch (ret) {
2600
0
        case -1: {
2601
0
            SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_SEND_MESSAGE_CONFERENCE_NOT_FOUND);
2602
0
            return false;
2603
0
        }
2604
2605
0
        case -2: {
2606
0
            SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_SEND_MESSAGE_TOO_LONG);
2607
0
            return false;
2608
0
        }
2609
2610
0
        case -3: {
2611
0
            SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_SEND_MESSAGE_NO_CONNECTION);
2612
0
            return false;
2613
0
        }
2614
2615
0
        case -4: {
2616
0
            SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_SEND_MESSAGE_FAIL_SEND);
2617
0
            return false;
2618
0
        }
2619
2
    }
2620
2621
2
    SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_SEND_MESSAGE_OK);
2622
2
    return true;
2623
2
}
2624
2625
size_t tox_conference_get_title_size(const Tox *_Nonnull tox, uint32_t conference_number, Tox_Err_Conference_Title *_Nullable error)
2626
0
{
2627
0
    assert(tox != nullptr);
2628
0
    tox_lock(tox);
2629
0
    const int ret = group_title_get_size(tox->m->conferences_object, conference_number);
2630
0
    tox_unlock(tox);
2631
2632
0
    switch (ret) {
2633
0
        case -1: {
2634
0
            SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_TITLE_CONFERENCE_NOT_FOUND);
2635
0
            return -1;
2636
0
        }
2637
2638
0
        case -2: {
2639
0
            SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_TITLE_INVALID_LENGTH);
2640
0
            return -1;
2641
0
        }
2642
0
    }
2643
2644
0
    SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_TITLE_OK);
2645
0
    return ret;
2646
0
}
2647
2648
bool tox_conference_get_title(const Tox *_Nonnull tox, uint32_t conference_number, uint8_t *_Nonnull title,
2649
                              Tox_Err_Conference_Title *_Nullable error)
2650
0
{
2651
0
    assert(tox != nullptr);
2652
0
    tox_lock(tox);
2653
0
    const int ret = group_title_get(tox->m->conferences_object, conference_number, title);
2654
0
    tox_unlock(tox);
2655
2656
0
    switch (ret) {
2657
0
        case -1: {
2658
0
            SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_TITLE_CONFERENCE_NOT_FOUND);
2659
0
            return false;
2660
0
        }
2661
2662
0
        case -2: {
2663
0
            SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_TITLE_INVALID_LENGTH);
2664
0
            return false;
2665
0
        }
2666
0
    }
2667
2668
0
    SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_TITLE_OK);
2669
0
    return true;
2670
0
}
2671
2672
bool tox_conference_set_title(Tox *_Nonnull tox, uint32_t conference_number, const uint8_t *_Nonnull title, size_t length,
2673
                              Tox_Err_Conference_Title *_Nullable error)
2674
0
{
2675
0
    assert(tox != nullptr);
2676
0
    tox_lock(tox);
2677
0
    const int ret = group_title_send(tox->m->conferences_object, conference_number, title, length);
2678
0
    tox_unlock(tox);
2679
2680
0
    switch (ret) {
2681
0
        case -1: {
2682
0
            SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_TITLE_CONFERENCE_NOT_FOUND);
2683
0
            return false;
2684
0
        }
2685
2686
0
        case -2: {
2687
0
            SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_TITLE_INVALID_LENGTH);
2688
0
            return false;
2689
0
        }
2690
2691
0
        case -3: {
2692
0
            SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_TITLE_FAIL_SEND);
2693
0
            return false;
2694
0
        }
2695
0
    }
2696
2697
0
    SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_TITLE_OK);
2698
0
    return true;
2699
0
}
2700
2701
size_t tox_conference_get_chatlist_size(const Tox *_Nonnull tox)
2702
0
{
2703
0
    assert(tox != nullptr);
2704
0
    tox_lock(tox);
2705
0
    const size_t ret = count_chatlist(tox->m->conferences_object);
2706
0
    tox_unlock(tox);
2707
0
    return ret;
2708
0
}
2709
2710
void tox_conference_get_chatlist(const Tox *_Nonnull tox, uint32_t *_Nullable chatlist)
2711
1
{
2712
1
    assert(tox != nullptr);
2713
1
    tox_lock(tox);
2714
1
    const size_t list_size = count_chatlist(tox->m->conferences_object);
2715
1
    copy_chatlist(tox->m->conferences_object, chatlist, list_size);
2716
1
    tox_unlock(tox);
2717
1
}
2718
2719
Tox_Conference_Type tox_conference_get_type(const Tox *_Nonnull tox, uint32_t conference_number,
2720
        Tox_Err_Conference_Get_Type *_Nullable error)
2721
1
{
2722
1
    assert(tox != nullptr);
2723
1
    tox_lock(tox);
2724
1
    const int ret = group_get_type(tox->m->conferences_object, conference_number);
2725
1
    tox_unlock(tox);
2726
2727
1
    if (ret == -1) {
2728
0
        SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_GET_TYPE_CONFERENCE_NOT_FOUND);
2729
0
        return (Tox_Conference_Type)ret;
2730
0
    }
2731
2732
1
    SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_GET_TYPE_OK);
2733
1
    return (Tox_Conference_Type)ret;
2734
1
}
2735
2736
bool tox_conference_get_id(const Tox *_Nonnull tox, uint32_t conference_number, Tox_Conference_Id _Nonnull id)
2737
0
{
2738
0
    assert(tox != nullptr);
2739
0
    tox_lock(tox);
2740
0
    const bool ret = conference_get_id(tox->m->conferences_object, conference_number, id);
2741
0
    tox_unlock(tox);
2742
0
    return ret;
2743
0
}
2744
2745
// TODO(iphydf): Delete in 0.3.0.
2746
bool tox_conference_get_uid(const Tox *_Nonnull tox, uint32_t conference_number, Tox_Conference_Uid _Nonnull uid)
2747
0
{
2748
0
    assert(tox != nullptr);
2749
0
    return tox_conference_get_id(tox, conference_number, uid);
2750
0
}
2751
2752
uint32_t tox_conference_by_id(const Tox *_Nonnull tox, const Tox_Conference_Id _Nonnull id, Tox_Err_Conference_By_Id *_Nullable error)
2753
0
{
2754
0
    assert(tox != nullptr);
2755
2756
0
    if (id == nullptr) {
2757
0
        SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_BY_ID_NULL);
2758
0
        return UINT32_MAX;
2759
0
    }
2760
2761
0
    tox_lock(tox);
2762
0
    const int32_t ret = conference_by_id(tox->m->conferences_object, id);
2763
0
    tox_unlock(tox);
2764
2765
0
    if (ret == -1) {
2766
0
        SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_BY_ID_NOT_FOUND);
2767
0
        return UINT32_MAX;
2768
0
    }
2769
2770
0
    SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_BY_ID_OK);
2771
0
    assert(ret >= 0);
2772
0
    return (uint32_t)ret;
2773
0
}
2774
2775
// TODO(iphydf): Delete in 0.3.0.
2776
uint32_t tox_conference_by_uid(const Tox *_Nonnull tox, const Tox_Conference_Uid _Nonnull uid, Tox_Err_Conference_By_Uid *_Nullable error)
2777
0
{
2778
0
    assert(tox != nullptr);
2779
0
    Tox_Err_Conference_By_Id id_error;
2780
0
    const uint32_t res = tox_conference_by_id(tox, uid, &id_error);
2781
2782
0
    switch (id_error) {
2783
0
        case TOX_ERR_CONFERENCE_BY_ID_OK: {
2784
0
            SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_BY_UID_OK);
2785
0
            break;
2786
0
        }
2787
2788
0
        case TOX_ERR_CONFERENCE_BY_ID_NULL: {
2789
0
            SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_BY_UID_NULL);
2790
0
            break;
2791
0
        }
2792
2793
0
        case TOX_ERR_CONFERENCE_BY_ID_NOT_FOUND: {
2794
0
            SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_BY_UID_NOT_FOUND);
2795
0
            break;
2796
0
        }
2797
0
    }
2798
2799
0
    return res;
2800
0
}
2801
2802
static void set_custom_packet_error(int ret, Tox_Err_Friend_Custom_Packet *_Nullable error)
2803
135
{
2804
135
    switch (ret) {
2805
135
        case 0: {
2806
135
            SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_CUSTOM_PACKET_OK);
2807
135
            break;
2808
0
        }
2809
2810
0
        case -1: {
2811
0
            SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_CUSTOM_PACKET_FRIEND_NOT_FOUND);
2812
0
            break;
2813
0
        }
2814
2815
0
        case -2: {
2816
0
            SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_CUSTOM_PACKET_TOO_LONG);
2817
0
            break;
2818
0
        }
2819
2820
0
        case -3: {
2821
0
            SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_CUSTOM_PACKET_INVALID);
2822
0
            break;
2823
0
        }
2824
2825
0
        case -4: {
2826
0
            SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_CUSTOM_PACKET_FRIEND_NOT_CONNECTED);
2827
0
            break;
2828
0
        }
2829
2830
0
        case -5: {
2831
0
            SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_CUSTOM_PACKET_SENDQ);
2832
0
            break;
2833
0
        }
2834
135
    }
2835
135
}
2836
2837
// cppcheck-suppress constParameterPointer
2838
bool tox_friend_send_lossy_packet(Tox *_Nonnull tox, uint32_t friend_number, const uint8_t *_Nonnull data, size_t length,
2839
                                  Tox_Err_Friend_Custom_Packet *_Nullable error)
2840
118
{
2841
118
    assert(tox != nullptr);
2842
2843
118
    if (data == nullptr) {
2844
0
        SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_CUSTOM_PACKET_NULL);
2845
0
        return false;
2846
0
    }
2847
2848
118
    if (length == 0) {
2849
0
        SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_CUSTOM_PACKET_EMPTY);
2850
0
        return false;
2851
0
    }
2852
2853
118
    if (data[0] < PACKET_ID_RANGE_LOSSY_START || data[0] > PACKET_ID_RANGE_LOSSY_END) {
2854
0
        SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_CUSTOM_PACKET_INVALID);
2855
0
        return false;
2856
0
    }
2857
2858
118
    tox_lock(tox);
2859
118
    const int ret = m_send_custom_lossy_packet(tox->m, friend_number, data, length);
2860
118
    tox_unlock(tox);
2861
2862
118
    set_custom_packet_error(ret, error);
2863
2864
118
    return ret == 0;
2865
118
}
2866
2867
void tox_callback_friend_lossy_packet(Tox *_Nonnull tox, tox_friend_lossy_packet_cb *_Nullable callback)
2868
372
{
2869
372
    assert(tox != nullptr);
2870
2871
    /* start at PACKET_ID_RANGE_LOSSY_CUSTOM_START so ToxAV Packets are excluded */
2872
20.8k
    for (uint8_t i = PACKET_ID_RANGE_LOSSY_CUSTOM_START; i <= PACKET_ID_RANGE_LOSSY_END; ++i) {
2873
20.4k
        tox->friend_lossy_packet_callback_per_pktid[i] = callback;
2874
20.4k
    }
2875
372
}
2876
2877
// cppcheck-suppress constParameterPointer
2878
bool tox_friend_send_lossless_packet(Tox *_Nonnull tox, uint32_t friend_number, const uint8_t *_Nonnull data,
2879
                                     size_t length, Tox_Err_Friend_Custom_Packet *_Nullable error)
2880
17
{
2881
17
    assert(tox != nullptr);
2882
2883
17
    if (data == nullptr) {
2884
0
        SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_CUSTOM_PACKET_NULL);
2885
0
        return false;
2886
0
    }
2887
2888
17
    if (length == 0) {
2889
0
        SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_CUSTOM_PACKET_EMPTY);
2890
0
        return false;
2891
0
    }
2892
2893
17
    tox_lock(tox);
2894
17
    const int ret = send_custom_lossless_packet(tox->m, friend_number, data, length);
2895
17
    tox_unlock(tox);
2896
2897
17
    set_custom_packet_error(ret, error);
2898
2899
17
    return ret == 0;
2900
17
}
2901
2902
void tox_callback_friend_lossless_packet(Tox *_Nonnull tox, tox_friend_lossless_packet_cb *_Nullable callback)
2903
372
{
2904
372
    assert(tox != nullptr);
2905
2906
12.2k
    for (uint8_t i = PACKET_ID_RANGE_LOSSLESS_CUSTOM_START; i <= PACKET_ID_RANGE_LOSSLESS_CUSTOM_END; ++i) {
2907
11.9k
        tox->friend_lossless_packet_callback_per_pktid[i] = callback;
2908
11.9k
    }
2909
372
}
2910
2911
void tox_self_get_dht_id(const Tox *_Nonnull tox, Tox_Dht_Id _Nonnull dht_id)
2912
113k
{
2913
113k
    assert(tox != nullptr);
2914
2915
113k
    if (dht_id != nullptr) {
2916
113k
        tox_lock(tox);
2917
113k
        memcpy(dht_id, dht_get_self_public_key(tox->m->dht), CRYPTO_PUBLIC_KEY_SIZE);
2918
113k
        tox_unlock(tox);
2919
113k
    }
2920
113k
}
2921
2922
uint16_t tox_self_get_udp_port(const Tox *_Nonnull tox, Tox_Err_Get_Port *_Nullable error)
2923
113k
{
2924
113k
    assert(tox != nullptr);
2925
113k
    tox_lock(tox);
2926
113k
    const uint16_t port = tox->m == nullptr || tox->m->net == nullptr ? 0 : net_htons(net_port(tox->m->net));
2927
113k
    tox_unlock(tox);
2928
2929
113k
    if (port == 0) {
2930
31.7k
        SET_ERROR_PARAMETER(error, TOX_ERR_GET_PORT_NOT_BOUND);
2931
31.7k
        return 0;
2932
31.7k
    }
2933
2934
81.2k
    SET_ERROR_PARAMETER(error, TOX_ERR_GET_PORT_OK);
2935
81.2k
    return port;
2936
113k
}
2937
2938
uint16_t tox_self_get_tcp_port(const Tox *_Nonnull tox, Tox_Err_Get_Port *_Nullable error)
2939
2
{
2940
2
    assert(tox != nullptr);
2941
2
    tox_lock(tox);
2942
2943
2
    if (tox->m != nullptr && tox->m->tcp_server != nullptr) {
2944
2
        SET_ERROR_PARAMETER(error, TOX_ERR_GET_PORT_OK);
2945
2
        const uint16_t ret = tox->m->options.tcp_server_port;
2946
2
        tox_unlock(tox);
2947
2
        return ret;
2948
2
    }
2949
2950
0
    SET_ERROR_PARAMETER(error, TOX_ERR_GET_PORT_NOT_BOUND);
2951
0
    tox_unlock(tox);
2952
0
    return 0;
2953
2
}
2954
2955
/* GROUPCHAT FUNCTIONS */
2956
2957
void tox_callback_group_invite(Tox *_Nonnull tox, tox_group_invite_cb *_Nullable callback)
2958
372
{
2959
372
    assert(tox != nullptr);
2960
372
    tox->group_invite_callback = callback;
2961
372
}
2962
2963
void tox_callback_group_message(Tox *_Nonnull tox, tox_group_message_cb *_Nullable callback)
2964
372
{
2965
372
    assert(tox != nullptr);
2966
372
    tox->group_message_callback = callback;
2967
372
}
2968
2969
void tox_callback_group_private_message(Tox *_Nonnull tox, tox_group_private_message_cb *_Nullable callback)
2970
372
{
2971
372
    assert(tox != nullptr);
2972
372
    tox->group_private_message_callback = callback;
2973
372
}
2974
2975
void tox_callback_group_custom_packet(Tox *_Nonnull tox, tox_group_custom_packet_cb *_Nullable callback)
2976
372
{
2977
372
    assert(tox != nullptr);
2978
372
    tox->group_custom_packet_callback = callback;
2979
372
}
2980
2981
void tox_callback_group_custom_private_packet(Tox *_Nonnull tox, tox_group_custom_private_packet_cb *_Nullable callback)
2982
372
{
2983
372
    assert(tox != nullptr);
2984
372
    tox->group_custom_private_packet_callback = callback;
2985
372
}
2986
2987
void tox_callback_group_moderation(Tox *_Nonnull tox, tox_group_moderation_cb *_Nullable callback)
2988
372
{
2989
372
    assert(tox != nullptr);
2990
372
    tox->group_moderation_callback = callback;
2991
372
}
2992
2993
void tox_callback_group_peer_name(Tox *_Nonnull tox, tox_group_peer_name_cb *_Nullable callback)
2994
372
{
2995
372
    assert(tox != nullptr);
2996
372
    tox->group_peer_name_callback = callback;
2997
372
}
2998
2999
void tox_callback_group_peer_status(Tox *_Nonnull tox, tox_group_peer_status_cb *_Nullable callback)
3000
372
{
3001
372
    assert(tox != nullptr);
3002
372
    tox->group_peer_status_callback = callback;
3003
372
}
3004
3005
void tox_callback_group_topic(Tox *_Nonnull tox, tox_group_topic_cb *_Nullable callback)
3006
372
{
3007
372
    assert(tox != nullptr);
3008
372
    tox->group_topic_callback = callback;
3009
372
}
3010
3011
void tox_callback_group_privacy_state(Tox *_Nonnull tox, tox_group_privacy_state_cb *_Nullable callback)
3012
372
{
3013
372
    assert(tox != nullptr);
3014
372
    tox->group_privacy_state_callback = callback;
3015
372
}
3016
3017
void tox_callback_group_topic_lock(Tox *_Nonnull tox, tox_group_topic_lock_cb *_Nullable callback)
3018
372
{
3019
372
    assert(tox != nullptr);
3020
372
    tox->group_topic_lock_callback = callback;
3021
372
}
3022
3023
void tox_callback_group_voice_state(Tox *_Nonnull tox, tox_group_voice_state_cb *_Nullable callback)
3024
372
{
3025
372
    assert(tox != nullptr);
3026
372
    tox->group_voice_state_callback = callback;
3027
372
}
3028
3029
void tox_callback_group_peer_limit(Tox *_Nonnull tox, tox_group_peer_limit_cb *_Nullable callback)
3030
372
{
3031
372
    assert(tox != nullptr);
3032
372
    tox->group_peer_limit_callback = callback;
3033
372
}
3034
3035
void tox_callback_group_password(Tox *_Nonnull tox, tox_group_password_cb *_Nullable callback)
3036
372
{
3037
372
    assert(tox != nullptr);
3038
372
    tox->group_password_callback = callback;
3039
372
}
3040
3041
void tox_callback_group_peer_join(Tox *_Nonnull tox, tox_group_peer_join_cb *_Nullable callback)
3042
372
{
3043
372
    assert(tox != nullptr);
3044
372
    tox->group_peer_join_callback = callback;
3045
372
}
3046
3047
void tox_callback_group_peer_exit(Tox *_Nonnull tox, tox_group_peer_exit_cb *_Nullable callback)
3048
372
{
3049
372
    assert(tox != nullptr);
3050
372
    tox->group_peer_exit_callback = callback;
3051
372
}
3052
3053
void tox_callback_group_self_join(Tox *_Nonnull tox, tox_group_self_join_cb *_Nullable callback)
3054
372
{
3055
372
    assert(tox != nullptr);
3056
372
    tox->group_self_join_callback = callback;
3057
372
}
3058
3059
void tox_callback_group_join_fail(Tox *_Nonnull tox, tox_group_join_fail_cb *_Nullable callback)
3060
372
{
3061
372
    assert(tox != nullptr);
3062
372
    tox->group_join_fail_callback = callback;
3063
372
}
3064
3065
uint32_t tox_group_new(Tox *_Nonnull tox, Tox_Group_Privacy_State privacy_state, const uint8_t *_Nullable group_name,
3066
                       size_t group_name_length, const uint8_t *_Nullable name, size_t name_length, Tox_Err_Group_New *_Nullable error)
3067
11
{
3068
11
    assert(tox != nullptr);
3069
3070
11
    tox_lock(tox);
3071
11
    const int ret = gc_group_add(tox->m->group_handler, (Group_Privacy_State) privacy_state,
3072
11
                                 group_name, group_name_length, name, name_length);
3073
11
    tox_unlock(tox);
3074
3075
11
    if (ret >= 0) {
3076
11
        SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_NEW_OK);
3077
11
        return ret;
3078
11
    }
3079
3080
0
    switch (ret) {
3081
0
        case -1: {
3082
0
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_NEW_TOO_LONG);
3083
0
            return UINT32_MAX;
3084
0
        }
3085
3086
0
        case -2: {
3087
0
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_NEW_EMPTY);
3088
0
            return UINT32_MAX;
3089
0
        }
3090
3091
0
        case -3: {
3092
0
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_NEW_INIT);
3093
0
            return UINT32_MAX;
3094
0
        }
3095
3096
0
        case -4: {
3097
0
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_NEW_STATE);
3098
0
            return UINT32_MAX;
3099
0
        }
3100
3101
0
        case -5: {
3102
0
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_NEW_ANNOUNCE);
3103
0
            return UINT32_MAX;
3104
0
        }
3105
0
    }
3106
3107
    /* can't happen */
3108
0
    LOGGER_FATAL(tox->m->log, "impossible return value: %d", ret);
3109
3110
0
    return UINT32_MAX;
3111
0
}
3112
3113
uint32_t tox_group_join(Tox *_Nonnull tox, const Tox_Group_Chat_Id _Nonnull chat_id, const uint8_t *_Nullable name, size_t name_length,
3114
                        const uint8_t *_Nullable password, size_t password_length, Tox_Err_Group_Join *_Nullable error)
3115
23
{
3116
23
    assert(tox != nullptr);
3117
3118
23
    tox_lock(tox);
3119
23
    const int ret = gc_group_join(tox->m->group_handler, chat_id, name, name_length, password, password_length);
3120
23
    tox_unlock(tox);
3121
3122
23
    if (ret >= 0) {
3123
23
        SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_JOIN_OK);
3124
23
        return ret;
3125
23
    }
3126
3127
0
    switch (ret) {
3128
0
        case -1: {
3129
0
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_JOIN_INIT);
3130
0
            return UINT32_MAX;
3131
0
        }
3132
3133
0
        case -2: {
3134
0
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_JOIN_BAD_CHAT_ID);
3135
0
            return UINT32_MAX;
3136
0
        }
3137
3138
0
        case -3: {
3139
0
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_JOIN_TOO_LONG);
3140
0
            return UINT32_MAX;
3141
0
        }
3142
3143
0
        case -4: {
3144
0
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_JOIN_EMPTY);
3145
0
            return UINT32_MAX;
3146
0
        }
3147
3148
0
        case -5: {
3149
0
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_JOIN_PASSWORD);
3150
0
            return UINT32_MAX;
3151
0
        }
3152
3153
0
        case -6: {
3154
0
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_JOIN_CORE);
3155
0
            return UINT32_MAX;
3156
0
        }
3157
0
    }
3158
3159
    /* can't happen */
3160
0
    LOGGER_FATAL(tox->m->log, "impossible return value: %d", ret);
3161
3162
0
    return UINT32_MAX;
3163
0
}
3164
3165
bool tox_group_is_connected(const Tox *_Nonnull tox, uint32_t group_number, Tox_Err_Group_Is_Connected *_Nullable error)
3166
7
{
3167
7
    assert(tox != nullptr);
3168
3169
7
    tox_lock(tox);
3170
7
    const GC_Chat *chat = gc_get_group(tox->m->group_handler, group_number);
3171
3172
7
    if (chat == nullptr) {
3173
0
        SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_IS_CONNECTED_GROUP_NOT_FOUND);
3174
0
        tox_unlock(tox);
3175
0
        return false;
3176
0
    }
3177
3178
7
    SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_IS_CONNECTED_OK);
3179
3180
7
    const bool ret = chat->connection_state == CS_CONNECTED || chat->connection_state == CS_CONNECTING;
3181
7
    tox_unlock(tox);
3182
3183
7
    return ret;
3184
7
}
3185
3186
bool tox_group_disconnect(const Tox *_Nonnull tox, uint32_t group_number, Tox_Err_Group_Disconnect *_Nullable error)
3187
1
{
3188
1
    assert(tox != nullptr);
3189
3190
1
    tox_lock(tox);
3191
1
    GC_Chat *chat = gc_get_group(tox->m->group_handler, group_number);
3192
3193
1
    if (chat == nullptr) {
3194
0
        SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_DISCONNECT_GROUP_NOT_FOUND);
3195
0
        tox_unlock(tox);
3196
0
        return false;
3197
0
    }
3198
3199
1
    if (chat->connection_state == CS_DISCONNECTED) {
3200
0
        SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_DISCONNECT_ALREADY_DISCONNECTED);
3201
0
        tox_unlock(tox);
3202
0
        return false;
3203
0
    }
3204
3205
1
    const bool ret = gc_disconnect_from_group(tox->m->group_handler, chat);
3206
3207
1
    tox_unlock(tox);
3208
3209
1
    if (!ret) {
3210
0
        SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_DISCONNECT_GROUP_NOT_FOUND);
3211
0
        return false;
3212
0
    }
3213
3214
1
    SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_DISCONNECT_OK);
3215
3216
1
    return true;
3217
1
}
3218
3219
bool tox_group_reconnect(Tox *_Nonnull tox, uint32_t group_number, Tox_Err_Group_Reconnect *_Nullable error)
3220
0
{
3221
0
    assert(tox != nullptr);
3222
3223
0
    tox_lock(tox);
3224
0
    GC_Chat *chat = gc_get_group(tox->m->group_handler, group_number);
3225
3226
0
    if (chat == nullptr) {
3227
0
        SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_RECONNECT_GROUP_NOT_FOUND);
3228
0
        tox_unlock(tox);
3229
0
        return false;
3230
0
    }
3231
3232
0
    const int ret = gc_rejoin_group(tox->m->group_handler, chat, nullptr, 0);
3233
0
    tox_unlock(tox);
3234
3235
0
    switch (ret) {
3236
0
        case 0: {
3237
0
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_RECONNECT_OK);
3238
0
            return true;
3239
0
        }
3240
3241
0
        case -1: {
3242
0
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_RECONNECT_GROUP_NOT_FOUND);
3243
0
            return false;
3244
0
        }
3245
3246
0
        case -2: {
3247
0
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_RECONNECT_CORE);
3248
0
            return false;
3249
0
        }
3250
0
    }
3251
3252
    /* can't happen */
3253
0
    LOGGER_FATAL(tox->m->log, "impossible return value: %d", ret);
3254
3255
0
    return false;
3256
0
}
3257
3258
bool tox_group_leave(Tox *_Nonnull tox, uint32_t group_number, const uint8_t *_Nullable part_message, size_t length,
3259
                     Tox_Err_Group_Leave *_Nullable error)
3260
7
{
3261
7
    assert(tox != nullptr);
3262
7
    tox_lock(tox);
3263
7
    GC_Chat *chat = gc_get_group(tox->m->group_handler, group_number);
3264
3265
7
    if (chat == nullptr) {
3266
0
        SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_LEAVE_GROUP_NOT_FOUND);
3267
0
        tox_unlock(tox);
3268
0
        return false;
3269
0
    }
3270
3271
7
    const int ret = gc_group_exit(tox->m->group_handler, chat, part_message, length);
3272
7
    tox_unlock(tox);
3273
3274
7
    switch (ret) {
3275
7
        case 0: {
3276
7
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_LEAVE_OK);
3277
7
            return true;
3278
0
        }
3279
3280
0
        case -1: {
3281
0
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_LEAVE_TOO_LONG);
3282
0
            return false;
3283
0
        }
3284
3285
0
        case -2: {
3286
0
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_LEAVE_FAIL_SEND);
3287
0
            return true;   /* the group was still successfully deleted */
3288
0
        }
3289
7
    }
3290
3291
    /* can't happen */
3292
0
    LOGGER_FATAL(tox->m->log, "impossible return value: %d", ret);
3293
3294
0
    return false;
3295
7
}
3296
3297
bool tox_group_self_set_name(Tox *_Nonnull tox, uint32_t group_number, const uint8_t *_Nonnull name, size_t length,
3298
                             Tox_Err_Group_Self_Name_Set *_Nullable error)
3299
2
{
3300
2
    assert(tox != nullptr);
3301
2
    tox_lock(tox);
3302
2
    const int ret = gc_set_self_nick(tox->m, group_number, name, length);
3303
2
    tox_unlock(tox);
3304
3305
2
    switch (ret) {
3306
2
        case 0: {
3307
2
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SELF_NAME_SET_OK);
3308
2
            return true;
3309
0
        }
3310
3311
0
        case -1: {
3312
0
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SELF_NAME_SET_GROUP_NOT_FOUND);
3313
0
            return false;
3314
0
        }
3315
3316
0
        case -2: {
3317
0
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SELF_NAME_SET_TOO_LONG);
3318
0
            return false;
3319
0
        }
3320
3321
0
        case -3: {
3322
0
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SELF_NAME_SET_INVALID);
3323
0
            return false;
3324
0
        }
3325
3326
0
        case -4: {
3327
0
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SELF_NAME_SET_FAIL_SEND);
3328
0
            return false;
3329
0
        }
3330
2
    }
3331
3332
    /* can't happen */
3333
0
    LOGGER_FATAL(tox->m->log, "impossible return value: %d", ret);
3334
3335
0
    return false;
3336
2
}
3337
3338
size_t tox_group_self_get_name_size(const Tox *_Nonnull tox, uint32_t group_number, Tox_Err_Group_Self_Query *_Nullable error)
3339
1
{
3340
1
    assert(tox != nullptr);
3341
3342
1
    tox_lock(tox);
3343
1
    const GC_Chat *chat = gc_get_group(tox->m->group_handler, group_number);
3344
3345
1
    if (chat == nullptr) {
3346
0
        SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SELF_QUERY_GROUP_NOT_FOUND);
3347
0
        tox_unlock(tox);
3348
0
        return -1;
3349
0
    }
3350
3351
1
    SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SELF_QUERY_OK);
3352
3353
1
    const size_t ret = gc_get_self_nick_size(chat);
3354
1
    tox_unlock(tox);
3355
3356
1
    return ret;
3357
1
}
3358
3359
bool tox_group_self_get_name(const Tox *_Nonnull tox, uint32_t group_number, uint8_t *_Nonnull name, Tox_Err_Group_Self_Query *_Nullable error)
3360
1
{
3361
1
    assert(tox != nullptr);
3362
3363
1
    tox_lock(tox);
3364
1
    const GC_Chat *chat = gc_get_group(tox->m->group_handler, group_number);
3365
3366
1
    if (chat == nullptr) {
3367
0
        SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SELF_QUERY_GROUP_NOT_FOUND);
3368
0
        tox_unlock(tox);
3369
0
        return false;
3370
0
    }
3371
3372
1
    SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SELF_QUERY_OK);
3373
3374
1
    gc_get_self_nick(chat, name);
3375
1
    tox_unlock(tox);
3376
3377
1
    return true;
3378
1
}
3379
3380
bool tox_group_self_set_status(Tox *_Nonnull tox, uint32_t group_number, Tox_User_Status status,
3381
                               Tox_Err_Group_Self_Status_Set *_Nullable error)
3382
3
{
3383
3
    assert(tox != nullptr);
3384
3
    tox_lock(tox);
3385
3
    const int ret = gc_set_self_status(tox->m, group_number, (Group_Peer_Status) status);
3386
3
    tox_unlock(tox);
3387
3388
3
    switch (ret) {
3389
3
        case 0: {
3390
3
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SELF_STATUS_SET_OK);
3391
3
            return true;
3392
0
        }
3393
3394
0
        case -1: {
3395
0
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SELF_STATUS_SET_GROUP_NOT_FOUND);
3396
0
            return false;
3397
0
        }
3398
3399
0
        case -2: {
3400
0
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SELF_STATUS_SET_FAIL_SEND);
3401
0
            return false;
3402
0
        }
3403
3
    }
3404
3405
    /* can't happen */
3406
0
    LOGGER_FATAL(tox->m->log, "impossible return value: %d", ret);
3407
3408
0
    return false;
3409
3
}
3410
3411
Tox_User_Status tox_group_self_get_status(const Tox *_Nonnull tox, uint32_t group_number, Tox_Err_Group_Self_Query *_Nullable error)
3412
1
{
3413
1
    assert(tox != nullptr);
3414
3415
1
    tox_lock(tox);
3416
1
    const GC_Chat *chat = gc_get_group(tox->m->group_handler, group_number);
3417
3418
1
    if (chat == nullptr) {
3419
0
        SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SELF_QUERY_GROUP_NOT_FOUND);
3420
0
        tox_unlock(tox);
3421
0
        return (Tox_User_Status) - 1;
3422
0
    }
3423
3424
1
    SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SELF_QUERY_OK);
3425
3426
1
    const uint8_t status = gc_get_self_status(chat);
3427
1
    tox_unlock(tox);
3428
3429
1
    return (Tox_User_Status)status;
3430
1
}
3431
3432
Tox_Group_Role tox_group_self_get_role(const Tox *_Nonnull tox, uint32_t group_number, Tox_Err_Group_Self_Query *_Nullable error)
3433
74
{
3434
74
    assert(tox != nullptr);
3435
3436
74
    tox_lock(tox);
3437
74
    const GC_Chat *chat = gc_get_group(tox->m->group_handler, group_number);
3438
3439
74
    if (chat == nullptr) {
3440
0
        SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SELF_QUERY_GROUP_NOT_FOUND);
3441
0
        tox_unlock(tox);
3442
0
        return (Tox_Group_Role) - 1;
3443
0
    }
3444
3445
74
    SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SELF_QUERY_OK);
3446
3447
74
    const Group_Role role = gc_get_self_role(chat);
3448
74
    tox_unlock(tox);
3449
3450
74
    return (Tox_Group_Role)role;
3451
74
}
3452
3453
uint32_t tox_group_self_get_peer_id(const Tox *_Nonnull tox, uint32_t group_number, Tox_Err_Group_Self_Query *_Nullable error)
3454
26
{
3455
26
    assert(tox != nullptr);
3456
3457
26
    tox_lock(tox);
3458
26
    const GC_Chat *chat = gc_get_group(tox->m->group_handler, group_number);
3459
3460
26
    if (chat == nullptr) {
3461
0
        SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SELF_QUERY_GROUP_NOT_FOUND);
3462
0
        tox_unlock(tox);
3463
0
        return -1;
3464
0
    }
3465
3466
26
    SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SELF_QUERY_OK);
3467
3468
26
    const GC_Peer_Id ret = gc_get_self_peer_id(chat);
3469
26
    tox_unlock(tox);
3470
3471
26
    return gc_peer_id_to_int(ret);
3472
26
}
3473
3474
bool tox_group_self_get_public_key(const Tox *_Nonnull tox, uint32_t group_number, Tox_Public_Key _Nonnull public_key,
3475
                                   Tox_Err_Group_Self_Query *_Nullable error)
3476
2
{
3477
2
    assert(tox != nullptr);
3478
3479
2
    tox_lock(tox);
3480
2
    const GC_Chat *chat = gc_get_group(tox->m->group_handler, group_number);
3481
3482
2
    if (chat == nullptr) {
3483
0
        SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SELF_QUERY_GROUP_NOT_FOUND);
3484
0
        tox_unlock(tox);
3485
0
        return false;
3486
0
    }
3487
3488
2
    SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SELF_QUERY_OK);
3489
3490
2
    gc_get_self_public_key(chat, public_key);
3491
2
    tox_unlock(tox);
3492
3493
2
    return true;
3494
2
}
3495
3496
size_t tox_group_peer_get_name_size(const Tox *_Nonnull tox, uint32_t group_number, uint32_t peer_id,
3497
                                    Tox_Err_Group_Peer_Query *_Nullable error)
3498
26
{
3499
26
    assert(tox != nullptr);
3500
3501
26
    tox_lock(tox);
3502
26
    const GC_Chat *chat = gc_get_group(tox->m->group_handler, group_number);
3503
3504
26
    if (chat == nullptr) {
3505
0
        SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_PEER_QUERY_GROUP_NOT_FOUND);
3506
0
        tox_unlock(tox);
3507
0
        return -1;
3508
0
    }
3509
3510
26
    const int ret = gc_get_peer_nick_size(chat, gc_peer_id_from_int(peer_id));
3511
26
    tox_unlock(tox);
3512
3513
26
    if (ret == -1) {
3514
0
        SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_PEER_QUERY_PEER_NOT_FOUND);
3515
0
        return -1;
3516
26
    } else {
3517
26
        SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_PEER_QUERY_OK);
3518
26
        return ret;
3519
26
    }
3520
26
}
3521
3522
bool tox_group_peer_get_name(const Tox *_Nonnull tox, uint32_t group_number, uint32_t peer_id, uint8_t *_Nonnull name,
3523
                             Tox_Err_Group_Peer_Query *_Nullable error)
3524
26
{
3525
26
    assert(tox != nullptr);
3526
3527
26
    tox_lock(tox);
3528
26
    const GC_Chat *chat = gc_get_group(tox->m->group_handler, group_number);
3529
3530
26
    if (chat == nullptr) {
3531
0
        SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_PEER_QUERY_GROUP_NOT_FOUND);
3532
0
        tox_unlock(tox);
3533
0
        return false;
3534
0
    }
3535
3536
26
    const bool ret = gc_get_peer_nick(chat, gc_peer_id_from_int(peer_id), name);
3537
26
    tox_unlock(tox);
3538
3539
26
    if (!ret) {
3540
0
        SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_PEER_QUERY_PEER_NOT_FOUND);
3541
0
        return false;
3542
0
    }
3543
3544
26
    SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_PEER_QUERY_OK);
3545
26
    return true;
3546
26
}
3547
3548
Tox_User_Status tox_group_peer_get_status(const Tox *_Nonnull tox, uint32_t group_number, uint32_t peer_id,
3549
        Tox_Err_Group_Peer_Query *_Nullable error)
3550
0
{
3551
0
    assert(tox != nullptr);
3552
0
    tox_lock(tox);
3553
0
    const GC_Chat *chat = gc_get_group(tox->m->group_handler, group_number);
3554
3555
0
    if (chat == nullptr) {
3556
0
        SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_PEER_QUERY_GROUP_NOT_FOUND);
3557
0
        tox_unlock(tox);
3558
0
        return (Tox_User_Status) - 1;
3559
0
    }
3560
3561
0
    const uint8_t ret = gc_get_status(chat, gc_peer_id_from_int(peer_id));
3562
0
    tox_unlock(tox);
3563
3564
0
    if (ret == UINT8_MAX) {
3565
0
        SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_PEER_QUERY_PEER_NOT_FOUND);
3566
0
        return (Tox_User_Status) - 1;
3567
0
    }
3568
3569
0
    SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_PEER_QUERY_OK);
3570
0
    return (Tox_User_Status)ret;
3571
0
}
3572
3573
Tox_Group_Role tox_group_peer_get_role(const Tox *_Nonnull tox, uint32_t group_number, uint32_t peer_id,
3574
                                       Tox_Err_Group_Peer_Query *_Nullable error)
3575
59
{
3576
59
    assert(tox != nullptr);
3577
3578
59
    tox_lock(tox);
3579
59
    const GC_Chat *chat = gc_get_group(tox->m->group_handler, group_number);
3580
3581
59
    if (chat == nullptr) {
3582
0
        SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_PEER_QUERY_GROUP_NOT_FOUND);
3583
0
        tox_unlock(tox);
3584
0
        return (Tox_Group_Role) - 1;
3585
0
    }
3586
3587
59
    const uint8_t ret = gc_get_role(chat, gc_peer_id_from_int(peer_id));
3588
59
    tox_unlock(tox);
3589
3590
59
    if (ret == (uint8_t) -1) {
3591
0
        SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_PEER_QUERY_PEER_NOT_FOUND);
3592
0
        return (Tox_Group_Role) - 1;
3593
0
    }
3594
3595
59
    SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_PEER_QUERY_OK);
3596
59
    return (Tox_Group_Role)ret;
3597
59
}
3598
3599
bool tox_group_peer_get_public_key(const Tox *_Nonnull tox, uint32_t group_number, uint32_t peer_id,
3600
                                   Tox_Public_Key _Nonnull public_key,
3601
                                   Tox_Err_Group_Peer_Query *_Nullable error)
3602
0
{
3603
0
    assert(tox != nullptr);
3604
3605
0
    tox_lock(tox);
3606
0
    const GC_Chat *chat = gc_get_group(tox->m->group_handler, group_number);
3607
3608
0
    if (chat == nullptr) {
3609
0
        SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_PEER_QUERY_GROUP_NOT_FOUND);
3610
0
        tox_unlock(tox);
3611
0
        return false;
3612
0
    }
3613
3614
0
    const int ret = gc_get_peer_public_key_by_peer_id(chat, gc_peer_id_from_int(peer_id), public_key);
3615
0
    tox_unlock(tox);
3616
3617
0
    if (ret == -1) {
3618
0
        SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_PEER_QUERY_PEER_NOT_FOUND);
3619
0
        return false;
3620
0
    }
3621
3622
0
    SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_PEER_QUERY_OK);
3623
0
    return true;
3624
0
}
3625
3626
Tox_Connection tox_group_peer_get_connection_status(const Tox *_Nonnull tox, uint32_t group_number, uint32_t peer_id,
3627
        Tox_Err_Group_Peer_Query *_Nullable error)
3628
0
{
3629
0
    assert(tox != nullptr);
3630
3631
0
    tox_lock(tox);
3632
0
    const GC_Chat *chat = gc_get_group(tox->m->group_handler, group_number);
3633
3634
0
    if (chat == nullptr) {
3635
0
        SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_PEER_QUERY_GROUP_NOT_FOUND);
3636
0
        tox_unlock(tox);
3637
0
        return TOX_CONNECTION_NONE;
3638
0
    }
3639
3640
0
    const unsigned int ret = gc_get_peer_connection_status(chat, gc_peer_id_from_int(peer_id));
3641
0
    tox_unlock(tox);
3642
3643
0
    if (ret == 0) {
3644
0
        SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_PEER_QUERY_PEER_NOT_FOUND);
3645
0
        return TOX_CONNECTION_NONE;
3646
0
    }
3647
3648
0
    SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_PEER_QUERY_OK);
3649
0
    return (Tox_Connection)ret;
3650
0
}
3651
3652
bool tox_group_set_topic(Tox *_Nonnull tox, uint32_t group_number, const uint8_t *_Nonnull topic, size_t length,
3653
                         Tox_Err_Group_Topic_Set *_Nullable error)
3654
14
{
3655
14
    assert(tox != nullptr);
3656
3657
14
    tox_lock(tox);
3658
14
    GC_Chat *chat = gc_get_group(tox->m->group_handler, group_number);
3659
3660
14
    if (chat == nullptr) {
3661
0
        SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_TOPIC_SET_GROUP_NOT_FOUND);
3662
0
        tox_unlock(tox);
3663
0
        return false;
3664
0
    }
3665
3666
14
    if (chat->connection_state == CS_DISCONNECTED) {
3667
0
        SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_TOPIC_SET_DISCONNECTED);
3668
0
        tox_unlock(tox);
3669
0
        return false;
3670
0
    }
3671
3672
14
    const int ret = gc_set_topic(chat, topic, length);
3673
14
    tox_unlock(tox);
3674
3675
14
    switch (ret) {
3676
12
        case 0: {
3677
12
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_TOPIC_SET_OK);
3678
12
            return true;
3679
0
        }
3680
3681
0
        case -1: {
3682
0
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_TOPIC_SET_TOO_LONG);
3683
0
            return false;
3684
0
        }
3685
3686
2
        case -2: {
3687
2
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_TOPIC_SET_PERMISSIONS);
3688
2
            return false;
3689
0
        }
3690
3691
0
        case -3: {
3692
0
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_TOPIC_SET_FAIL_CREATE);
3693
0
            return false;
3694
0
        }
3695
3696
0
        case -4: {
3697
0
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_TOPIC_SET_FAIL_SEND);
3698
0
            return false;
3699
0
        }
3700
14
    }
3701
3702
    /* can't happen */
3703
0
    LOGGER_FATAL(tox->m->log, "impossible return value: %d", ret);
3704
3705
0
    return false;
3706
14
}
3707
3708
size_t tox_group_get_topic_size(const Tox *_Nonnull tox, uint32_t group_number, Tox_Err_Group_State_Query *_Nullable error)
3709
11
{
3710
11
    assert(tox != nullptr);
3711
3712
11
    tox_lock(tox);
3713
11
    const GC_Chat *chat = gc_get_group(tox->m->group_handler, group_number);
3714
3715
11
    if (chat == nullptr) {
3716
0
        SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_STATE_QUERY_GROUP_NOT_FOUND);
3717
0
        tox_unlock(tox);
3718
0
        return -1;
3719
0
    }
3720
3721
11
    SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_STATE_QUERY_OK);
3722
3723
11
    const size_t ret = gc_get_topic_size(chat);
3724
11
    tox_unlock(tox);
3725
3726
11
    return ret;
3727
11
}
3728
3729
bool tox_group_get_topic(const Tox *_Nonnull tox, uint32_t group_number, uint8_t *_Nonnull topic, Tox_Err_Group_State_Query *_Nullable error)
3730
7
{
3731
7
    assert(tox != nullptr);
3732
3733
7
    tox_lock(tox);
3734
7
    const GC_Chat *chat = gc_get_group(tox->m->group_handler, group_number);
3735
3736
7
    if (chat == nullptr) {
3737
0
        SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_STATE_QUERY_GROUP_NOT_FOUND);
3738
0
        tox_unlock(tox);
3739
0
        return false;
3740
0
    }
3741
3742
7
    gc_get_topic(chat, topic);
3743
7
    tox_unlock(tox);
3744
3745
7
    SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_STATE_QUERY_OK);
3746
7
    return true;
3747
7
}
3748
3749
size_t tox_group_get_name_size(const Tox *_Nonnull tox, uint32_t group_number, Tox_Err_Group_State_Query *_Nullable error)
3750
1
{
3751
1
    assert(tox != nullptr);
3752
3753
1
    tox_lock(tox);
3754
1
    const GC_Chat *chat = gc_get_group(tox->m->group_handler, group_number);
3755
3756
1
    if (chat == nullptr) {
3757
0
        SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_STATE_QUERY_GROUP_NOT_FOUND);
3758
0
        tox_unlock(tox);
3759
0
        return -1;
3760
0
    }
3761
3762
1
    SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_STATE_QUERY_OK);
3763
3764
1
    const size_t ret = gc_get_group_name_size(chat);
3765
1
    tox_unlock(tox);
3766
3767
1
    return ret;
3768
1
}
3769
3770
bool tox_group_get_name(const Tox *_Nonnull tox, uint32_t group_number, uint8_t *_Nonnull name, Tox_Err_Group_State_Query *_Nullable error)
3771
1
{
3772
1
    assert(tox != nullptr);
3773
3774
1
    tox_lock(tox);
3775
1
    const GC_Chat *chat = gc_get_group(tox->m->group_handler, group_number);
3776
3777
1
    if (chat == nullptr) {
3778
0
        SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_STATE_QUERY_GROUP_NOT_FOUND);
3779
0
        tox_unlock(tox);
3780
0
        return false;
3781
0
    }
3782
3783
1
    gc_get_group_name(chat, name);
3784
1
    tox_unlock(tox);
3785
3786
1
    SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_STATE_QUERY_OK);
3787
3788
1
    return true;
3789
1
}
3790
3791
bool tox_group_get_chat_id(const Tox *_Nonnull tox, uint32_t group_number, Tox_Group_Chat_Id _Nonnull chat_id, Tox_Err_Group_State_Query *_Nullable error)
3792
11
{
3793
11
    assert(tox != nullptr);
3794
3795
11
    tox_lock(tox);
3796
11
    const GC_Chat *chat = gc_get_group(tox->m->group_handler, group_number);
3797
3798
11
    if (chat == nullptr) {
3799
0
        SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_STATE_QUERY_GROUP_NOT_FOUND);
3800
0
        tox_unlock(tox);
3801
0
        return false;
3802
0
    }
3803
3804
11
    SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_STATE_QUERY_OK);
3805
11
    gc_get_chat_id(chat, chat_id);
3806
11
    tox_unlock(tox);
3807
3808
11
    return true;
3809
11
}
3810
3811
Tox_Group_Number tox_group_by_id(const Tox *_Nonnull tox, const Tox_Group_Chat_Id _Nonnull chat_id, Tox_Err_Group_By_Id *_Nullable error)
3812
5
{
3813
5
    assert(tox != nullptr);
3814
3815
5
    if (chat_id == nullptr) {
3816
0
        SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_BY_ID_NULL);
3817
0
        return UINT32_MAX;
3818
0
    }
3819
3820
5
    tox_lock(tox);
3821
5
    const GC_Chat *chat = gc_get_group_by_public_key(tox->m->group_handler, chat_id);
3822
3823
5
    if (chat == nullptr) {
3824
2
        SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_BY_ID_NOT_FOUND);
3825
2
        tox_unlock(tox);
3826
2
        return UINT32_MAX;
3827
2
    }
3828
3829
3
    SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_BY_ID_OK);
3830
3
    const uint32_t ret = chat->group_number;
3831
3
    tox_unlock(tox);
3832
3833
3
    return ret;
3834
5
}
3835
3836
uint32_t tox_group_get_group_list_size(const Tox *_Nonnull tox)
3837
3
{
3838
3
    assert(tox != nullptr);
3839
3840
3
    tox_lock(tox);
3841
3
    const uint32_t ret = gc_count_groups(tox->m->group_handler);
3842
3
    tox_unlock(tox);
3843
3844
3
    return ret;
3845
3
}
3846
3847
uint32_t tox_group_get_number_groups(const Tox *tox)
3848
0
{
3849
0
    return tox_group_get_group_list_size(tox);
3850
0
}
3851
3852
void tox_group_get_group_list(const Tox *tox, Tox_Group_Number group_list[])
3853
4
{
3854
4
    assert(tox != nullptr);
3855
3856
4
    if (group_list != nullptr) {
3857
3
        tox_lock(tox);
3858
3
        gc_get_group_list(tox->m->group_handler, group_list);
3859
3
        tox_unlock(tox);
3860
3
    }
3861
4
}
3862
3863
Tox_Group_Privacy_State tox_group_get_privacy_state(const Tox *_Nonnull tox, uint32_t group_number,
3864
        Tox_Err_Group_State_Query *_Nullable error)
3865
1
{
3866
1
    assert(tox != nullptr);
3867
1
    tox_lock(tox);
3868
1
    const GC_Chat *chat = gc_get_group(tox->m->group_handler, group_number);
3869
3870
1
    if (chat == nullptr) {
3871
0
        SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_STATE_QUERY_GROUP_NOT_FOUND);
3872
0
        tox_unlock(tox);
3873
0
        return (Tox_Group_Privacy_State) - 1;
3874
0
    }
3875
3876
1
    SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_STATE_QUERY_OK);
3877
3878
1
    const uint8_t state = gc_get_privacy_state(chat);
3879
1
    tox_unlock(tox);
3880
3881
1
    return (Tox_Group_Privacy_State)state;
3882
1
}
3883
3884
Tox_Group_Topic_Lock tox_group_get_topic_lock(const Tox *_Nonnull tox, uint32_t group_number, Tox_Err_Group_State_Query *_Nullable error)
3885
10
{
3886
10
    assert(tox != nullptr);
3887
3888
10
    tox_lock(tox);
3889
10
    const GC_Chat *chat = gc_get_group(tox->m->group_handler, group_number);
3890
3891
10
    if (chat == nullptr) {
3892
0
        SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_STATE_QUERY_GROUP_NOT_FOUND);
3893
0
        tox_unlock(tox);
3894
0
        return (Tox_Group_Topic_Lock) - 1;
3895
0
    }
3896
3897
10
    SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_STATE_QUERY_OK);
3898
3899
10
    const Group_Topic_Lock topic_lock = gc_get_topic_lock_state(chat);
3900
10
    tox_unlock(tox);
3901
3902
10
    return (Tox_Group_Topic_Lock)topic_lock;
3903
10
}
3904
3905
Tox_Group_Voice_State tox_group_get_voice_state(const Tox *_Nonnull tox, uint32_t group_number,
3906
        Tox_Err_Group_State_Query *_Nullable error)
3907
0
{
3908
0
    assert(tox != nullptr);
3909
3910
0
    tox_lock(tox);
3911
0
    const GC_Chat *chat = gc_get_group(tox->m->group_handler, group_number);
3912
3913
0
    if (chat == nullptr) {
3914
0
        SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_STATE_QUERY_GROUP_NOT_FOUND);
3915
0
        tox_unlock(tox);
3916
0
        return (Tox_Group_Voice_State) - 1;
3917
0
    }
3918
3919
0
    SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_STATE_QUERY_OK);
3920
3921
0
    const Group_Voice_State voice_state = gc_get_voice_state(chat);
3922
0
    tox_unlock(tox);
3923
3924
0
    return (Tox_Group_Voice_State)voice_state;
3925
0
}
3926
3927
uint16_t tox_group_get_peer_limit(const Tox *_Nonnull tox, uint32_t group_number, Tox_Err_Group_State_Query *_Nullable error)
3928
1
{
3929
1
    assert(tox != nullptr);
3930
3931
1
    tox_lock(tox);
3932
1
    const GC_Chat *chat = gc_get_group(tox->m->group_handler, group_number);
3933
3934
1
    if (chat == nullptr) {
3935
0
        SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_STATE_QUERY_GROUP_NOT_FOUND);
3936
0
        tox_unlock(tox);
3937
0
        return -1;
3938
0
    }
3939
3940
1
    SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_STATE_QUERY_OK);
3941
3942
1
    const uint16_t ret = gc_get_max_peers(chat);
3943
1
    tox_unlock(tox);
3944
3945
1
    return ret;
3946
1
}
3947
3948
size_t tox_group_get_password_size(const Tox *_Nonnull tox, uint32_t group_number, Tox_Err_Group_State_Query *_Nullable error)
3949
1
{
3950
1
    assert(tox != nullptr);
3951
3952
1
    tox_lock(tox);
3953
1
    const GC_Chat *chat = gc_get_group(tox->m->group_handler, group_number);
3954
3955
1
    if (chat == nullptr) {
3956
0
        SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_STATE_QUERY_GROUP_NOT_FOUND);
3957
0
        tox_unlock(tox);
3958
0
        return -1;
3959
0
    }
3960
3961
1
    SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_STATE_QUERY_OK);
3962
3963
1
    const size_t ret = gc_get_password_size(chat);
3964
1
    tox_unlock(tox);
3965
3966
1
    return ret;
3967
1
}
3968
3969
bool tox_group_get_password(const Tox *_Nonnull tox, uint32_t group_number, uint8_t *_Nonnull password,
3970
                            Tox_Err_Group_State_Query *_Nullable error)
3971
1
{
3972
1
    assert(tox != nullptr);
3973
3974
1
    tox_lock(tox);
3975
1
    const GC_Chat *chat = gc_get_group(tox->m->group_handler, group_number);
3976
3977
1
    if (chat == nullptr) {
3978
0
        SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_STATE_QUERY_GROUP_NOT_FOUND);
3979
0
        tox_unlock(tox);
3980
0
        return false;
3981
0
    }
3982
3983
1
    SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_STATE_QUERY_OK);
3984
3985
1
    gc_get_password(chat, password);
3986
1
    tox_unlock(tox);
3987
3988
1
    return true;
3989
1
}
3990
3991
Tox_Group_Message_Id tox_group_send_message(
3992
    const Tox *_Nonnull tox, uint32_t group_number, Tox_Message_Type message_type, const uint8_t *_Nonnull message,
3993
    size_t length, Tox_Err_Group_Send_Message *_Nullable error)
3994
115
{
3995
115
    assert(tox != nullptr);
3996
3997
115
    tox_lock(tox);
3998
115
    const GC_Chat *chat = gc_get_group(tox->m->group_handler, group_number);
3999
4000
115
    if (chat == nullptr) {
4001
0
        SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SEND_MESSAGE_GROUP_NOT_FOUND);
4002
0
        tox_unlock(tox);
4003
0
        return -1;
4004
0
    }
4005
4006
115
    if (chat->connection_state == CS_DISCONNECTED) {
4007
0
        SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SEND_MESSAGE_DISCONNECTED);
4008
0
        tox_unlock(tox);
4009
0
        return -1;
4010
0
    }
4011
4012
115
    uint32_t message_id = 0;
4013
115
    const int ret = gc_send_message(chat, message, length, message_type, &message_id);
4014
115
    tox_unlock(tox);
4015
4016
115
    switch (ret) {
4017
106
        case 0: {
4018
106
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SEND_MESSAGE_OK);
4019
106
            return message_id;
4020
0
        }
4021
4022
0
        case -1: {
4023
0
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SEND_MESSAGE_TOO_LONG);
4024
0
            return -1;
4025
0
        }
4026
4027
0
        case -2: {
4028
0
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SEND_MESSAGE_EMPTY);
4029
0
            return -1;
4030
0
        }
4031
4032
0
        case -3: {
4033
0
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SEND_MESSAGE_BAD_TYPE);
4034
0
            return -1;
4035
0
        }
4036
4037
9
        case -4: {
4038
9
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SEND_MESSAGE_PERMISSIONS);
4039
9
            return -1;
4040
0
        }
4041
4042
0
        case -5: {
4043
0
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SEND_MESSAGE_FAIL_SEND);
4044
0
            return -1;
4045
0
        }
4046
115
    }
4047
4048
    /* can't happen */
4049
0
    LOGGER_FATAL(tox->m->log, "impossible return value: %d", ret);
4050
4051
0
    return -1;
4052
115
}
4053
4054
Tox_Group_Message_Id tox_group_send_private_message(const Tox *_Nonnull tox, uint32_t group_number, uint32_t peer_id,
4055
        Tox_Message_Type message_type, const uint8_t *_Nonnull message, size_t length, Tox_Err_Group_Send_Private_Message *_Nullable error)
4056
2
{
4057
2
    assert(tox != nullptr);
4058
4059
2
    tox_lock(tox);
4060
2
    const GC_Chat *chat = gc_get_group(tox->m->group_handler, group_number);
4061
4062
2
    if (chat == nullptr) {
4063
0
        SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SEND_PRIVATE_MESSAGE_GROUP_NOT_FOUND);
4064
0
        tox_unlock(tox);
4065
0
        return -1;
4066
0
    }
4067
4068
2
    if (chat->connection_state == CS_DISCONNECTED) {
4069
0
        SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SEND_PRIVATE_MESSAGE_DISCONNECTED);
4070
0
        tox_unlock(tox);
4071
0
        return -1;
4072
0
    }
4073
4074
2
    uint32_t message_id = 0;
4075
2
    const int ret = gc_send_private_message(chat, gc_peer_id_from_int(peer_id), message_type, message, length, &message_id);
4076
2
    tox_unlock(tox);
4077
4078
2
    switch (ret) {
4079
2
        case 0: {
4080
2
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SEND_PRIVATE_MESSAGE_OK);
4081
2
            return message_id;
4082
0
        }
4083
4084
0
        case -1: {
4085
0
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SEND_PRIVATE_MESSAGE_TOO_LONG);
4086
0
            return -1;
4087
0
        }
4088
4089
0
        case -2: {
4090
0
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SEND_PRIVATE_MESSAGE_EMPTY);
4091
0
            return -1;
4092
0
        }
4093
4094
0
        case -3: {
4095
0
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SEND_PRIVATE_MESSAGE_PEER_NOT_FOUND);
4096
0
            return -1;
4097
0
        }
4098
4099
0
        case -4: {
4100
0
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SEND_PRIVATE_MESSAGE_BAD_TYPE);
4101
0
            return -1;
4102
0
        }
4103
4104
0
        case -5: {
4105
0
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SEND_PRIVATE_MESSAGE_PERMISSIONS);
4106
0
            return -1;
4107
0
        }
4108
4109
0
        case -6: {
4110
0
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SEND_PRIVATE_MESSAGE_FAIL_SEND);
4111
0
            return -1;
4112
0
        }
4113
2
    }
4114
4115
    /* can't happen */
4116
0
    LOGGER_FATAL(tox->m->log, "impossible return value: %d", ret);
4117
4118
0
    return -1;
4119
2
}
4120
4121
bool tox_group_send_custom_packet(const Tox *_Nonnull tox, uint32_t group_number, bool lossless, const uint8_t *_Nonnull data,
4122
                                  size_t length, Tox_Err_Group_Send_Custom_Packet *_Nullable error)
4123
2
{
4124
2
    assert(tox != nullptr);
4125
4126
2
    tox_lock(tox);
4127
2
    const GC_Chat *chat = gc_get_group(tox->m->group_handler, group_number);
4128
4129
2
    if (chat == nullptr) {
4130
0
        SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SEND_CUSTOM_PACKET_GROUP_NOT_FOUND);
4131
0
        tox_unlock(tox);
4132
0
        return false;
4133
0
    }
4134
4135
2
    if (chat->connection_state == CS_DISCONNECTED) {
4136
0
        SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SEND_CUSTOM_PACKET_DISCONNECTED);
4137
0
        tox_unlock(tox);
4138
0
        return false;
4139
0
    }
4140
4141
2
    const int ret = gc_send_custom_packet(chat, lossless, data, length);
4142
2
    tox_unlock(tox);
4143
4144
2
    switch (ret) {
4145
2
        case 0: {
4146
2
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SEND_CUSTOM_PACKET_OK);
4147
2
            return true;
4148
0
        }
4149
4150
0
        case -1: {
4151
0
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SEND_CUSTOM_PACKET_TOO_LONG);
4152
0
            return false;
4153
0
        }
4154
4155
0
        case -2: {
4156
0
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SEND_CUSTOM_PACKET_EMPTY);
4157
0
            return false;
4158
0
        }
4159
4160
0
        case -3: {
4161
0
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SEND_CUSTOM_PACKET_FAIL_SEND);
4162
0
            return false;
4163
0
        }
4164
2
    }
4165
4166
    /* can't happen */
4167
0
    LOGGER_FATAL(tox->m->log, "impossible return value: %d", ret);
4168
4169
0
    return false;
4170
2
}
4171
4172
bool tox_group_send_custom_private_packet(const Tox *_Nonnull tox, uint32_t group_number, uint32_t peer_id, bool lossless,
4173
        const uint8_t *_Nonnull data, size_t length,
4174
        Tox_Err_Group_Send_Custom_Private_Packet *_Nullable error)
4175
2
{
4176
2
    assert(tox != nullptr);
4177
4178
2
    tox_lock(tox);
4179
2
    const GC_Chat *chat = gc_get_group(tox->m->group_handler, group_number);
4180
4181
2
    if (chat == nullptr) {
4182
0
        SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SEND_CUSTOM_PRIVATE_PACKET_GROUP_NOT_FOUND);
4183
0
        tox_unlock(tox);
4184
0
        return false;
4185
0
    }
4186
4187
2
    if (chat->connection_state == CS_DISCONNECTED) {
4188
0
        SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SEND_CUSTOM_PRIVATE_PACKET_DISCONNECTED);
4189
0
        tox_unlock(tox);
4190
0
        return false;
4191
0
    }
4192
4193
2
    const int ret = gc_send_custom_private_packet(chat, lossless, gc_peer_id_from_int(peer_id), data, length);
4194
2
    tox_unlock(tox);
4195
4196
2
    switch (ret) {
4197
2
        case 0: {
4198
2
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SEND_CUSTOM_PRIVATE_PACKET_OK);
4199
2
            return true;
4200
0
        }
4201
4202
0
        case -1: {
4203
0
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SEND_CUSTOM_PRIVATE_PACKET_TOO_LONG);
4204
0
            return false;
4205
0
        }
4206
4207
0
        case -2: {
4208
0
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SEND_CUSTOM_PRIVATE_PACKET_EMPTY);
4209
0
            return false;
4210
0
        }
4211
4212
0
        case -3: {
4213
0
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SEND_CUSTOM_PRIVATE_PACKET_PEER_NOT_FOUND);
4214
0
            return false;
4215
0
        }
4216
4217
0
        case -4: {
4218
0
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SEND_CUSTOM_PRIVATE_PACKET_FAIL_SEND);
4219
0
            return false;
4220
0
        }
4221
2
    }
4222
4223
    /* can't happen */
4224
0
    LOGGER_FATAL(tox->m->log, "impossible return value: %d", ret);
4225
4226
0
    return false;
4227
2
}
4228
4229
bool tox_group_invite_friend(const Tox *_Nonnull tox, uint32_t group_number, uint32_t friend_number,
4230
                             Tox_Err_Group_Invite_Friend *_Nullable error)
4231
3
{
4232
3
    assert(tox != nullptr);
4233
4234
3
    tox_lock(tox);
4235
3
    GC_Chat *chat = gc_get_group(tox->m->group_handler, group_number);
4236
4237
3
    if (chat == nullptr) {
4238
0
        SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_INVITE_FRIEND_GROUP_NOT_FOUND);
4239
0
        tox_unlock(tox);
4240
0
        return false;
4241
0
    }
4242
4243
3
    if (chat->connection_state == CS_DISCONNECTED) {
4244
0
        SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_INVITE_FRIEND_DISCONNECTED);
4245
0
        tox_unlock(tox);
4246
0
        return false;
4247
0
    }
4248
4249
3
    if (!friend_is_valid(tox->m, friend_number)) {
4250
0
        SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_INVITE_FRIEND_FRIEND_NOT_FOUND);
4251
0
        tox_unlock(tox);
4252
0
        return false;
4253
0
    }
4254
4255
3
    const int ret = gc_invite_friend(tox->m->group_handler, chat, friend_number, send_group_invite_packet);
4256
3
    tox_unlock(tox);
4257
4258
3
    switch (ret) {
4259
3
        case 0: {
4260
3
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_INVITE_FRIEND_OK);
4261
3
            return true;
4262
0
        }
4263
4264
0
        case -1: {
4265
0
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_INVITE_FRIEND_FRIEND_NOT_FOUND);
4266
0
            return false;
4267
0
        }
4268
4269
0
        case -2: {
4270
0
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_INVITE_FRIEND_INVITE_FAIL);
4271
0
            return false;
4272
0
        }
4273
4274
0
        case -3: {
4275
0
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_INVITE_FRIEND_FAIL_SEND);
4276
0
            return false;
4277
0
        }
4278
3
    }
4279
4280
    /* can't happen */
4281
0
    LOGGER_FATAL(tox->m->log, "impossible return value: %d", ret);
4282
4283
0
    return false;
4284
3
}
4285
4286
uint32_t tox_group_invite_accept(Tox *_Nonnull tox, uint32_t friend_number, const uint8_t *_Nonnull invite_data, size_t length,
4287
                                 const uint8_t *_Nullable name, size_t name_length, const uint8_t *_Nullable password,
4288
                                 size_t password_length, Tox_Err_Group_Invite_Accept *_Nullable error)
4289
3
{
4290
3
    assert(tox != nullptr);
4291
4292
3
    if (invite_data == nullptr || name == nullptr) {
4293
0
        SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_INVITE_ACCEPT_NULL);
4294
0
        return UINT32_MAX;
4295
0
    }
4296
4297
3
    tox_lock(tox);
4298
3
    const int ret = gc_accept_invite(tox->m->group_handler, friend_number, invite_data, length, name, name_length, password,
4299
3
                                     password_length);
4300
3
    tox_unlock(tox);
4301
4302
3
    if (ret >= 0) {
4303
3
        SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_INVITE_ACCEPT_OK);
4304
3
        return ret;
4305
3
    }
4306
4307
0
    switch (ret) {
4308
0
        case -1: {
4309
0
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_INVITE_ACCEPT_BAD_INVITE);
4310
0
            return UINT32_MAX;
4311
0
        }
4312
4313
0
        case -2: {
4314
0
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_INVITE_ACCEPT_INIT_FAILED);
4315
0
            return UINT32_MAX;
4316
0
        }
4317
4318
0
        case -3: {
4319
0
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_INVITE_ACCEPT_TOO_LONG);
4320
0
            return UINT32_MAX;
4321
0
        }
4322
4323
0
        case -4: {
4324
0
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_INVITE_ACCEPT_EMPTY);
4325
0
            return UINT32_MAX;
4326
0
        }
4327
4328
0
        case -5: {
4329
0
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_INVITE_ACCEPT_PASSWORD);
4330
0
            return UINT32_MAX;
4331
0
        }
4332
4333
0
        case -6: {
4334
0
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_INVITE_ACCEPT_FRIEND_NOT_FOUND);
4335
0
            return UINT32_MAX;
4336
0
        }
4337
4338
0
        case -7: {
4339
0
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_INVITE_ACCEPT_FAIL_SEND);
4340
0
            return UINT32_MAX;
4341
0
        }
4342
0
    }
4343
4344
    /* can't happen */
4345
0
    LOGGER_FATAL(tox->m->log, "impossible return value: %d", ret);
4346
4347
0
    return UINT32_MAX;
4348
0
}
4349
4350
bool tox_group_set_password(Tox *_Nonnull tox, uint32_t group_number, const uint8_t *_Nullable password, size_t length,
4351
                            Tox_Err_Group_Set_Password *_Nullable error)
4352
5
{
4353
5
    assert(tox != nullptr);
4354
4355
5
    tox_lock(tox);
4356
5
    GC_Chat *chat = gc_get_group(tox->m->group_handler, group_number);
4357
4358
5
    if (chat == nullptr) {
4359
0
        SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SET_PASSWORD_GROUP_NOT_FOUND);
4360
0
        tox_unlock(tox);
4361
0
        return false;
4362
0
    }
4363
4364
5
    if (chat->connection_state == CS_DISCONNECTED) {
4365
0
        SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SET_PASSWORD_DISCONNECTED);
4366
0
        tox_unlock(tox);
4367
0
        return false;
4368
0
    }
4369
4370
5
    const int ret = gc_founder_set_password(chat, password, length);
4371
5
    tox_unlock(tox);
4372
4373
5
    switch (ret) {
4374
5
        case 0: {
4375
5
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SET_PASSWORD_OK);
4376
5
            return true;
4377
0
        }
4378
4379
0
        case -1: {
4380
0
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SET_PASSWORD_PERMISSIONS);
4381
0
            return false;
4382
0
        }
4383
4384
0
        case -2: {
4385
0
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SET_PASSWORD_TOO_LONG);
4386
0
            return false;
4387
0
        }
4388
4389
0
        case -3: {
4390
0
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SET_PASSWORD_FAIL_SEND);
4391
0
            return false;
4392
0
        }
4393
4394
0
        case -4: {
4395
0
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SET_PASSWORD_MALLOC);
4396
0
            return false;
4397
0
        }
4398
5
    }
4399
4400
    /* can't happen */
4401
0
    LOGGER_FATAL(tox->m->log, "impossible return value: %d", ret);
4402
4403
0
    return false;
4404
5
}
4405
4406
bool tox_group_set_privacy_state(Tox *_Nonnull tox, uint32_t group_number, Tox_Group_Privacy_State privacy_state,
4407
                                 Tox_Err_Group_Set_Privacy_State *_Nullable error)
4408
5
{
4409
5
    assert(tox != nullptr);
4410
4411
5
    tox_lock(tox);
4412
5
    const int ret = gc_founder_set_privacy_state(tox->m, group_number, (Group_Privacy_State) privacy_state);
4413
5
    tox_unlock(tox);
4414
4415
5
    switch (ret) {
4416
5
        case 0: {
4417
5
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SET_PRIVACY_STATE_OK);
4418
5
            return true;
4419
0
        }
4420
4421
0
        case -1: {
4422
0
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SET_PRIVACY_STATE_GROUP_NOT_FOUND);
4423
0
            return false;
4424
0
        }
4425
4426
0
        case -2: {
4427
0
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SET_PRIVACY_STATE_PERMISSIONS);
4428
0
            return false;
4429
0
        }
4430
4431
0
        case -3: {
4432
0
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SET_PRIVACY_STATE_DISCONNECTED);
4433
0
            return false;
4434
0
        }
4435
4436
0
        case -4: {
4437
0
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SET_PRIVACY_STATE_FAIL_SET);
4438
0
            return false;
4439
0
        }
4440
4441
0
        case -5: {
4442
0
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SET_PRIVACY_STATE_FAIL_SEND);
4443
0
            return false;
4444
0
        }
4445
5
    }
4446
4447
    /* can't happen */
4448
0
    LOGGER_FATAL(tox->m->log, "impossible return value: %d", ret);
4449
4450
0
    return false;
4451
5
}
4452
4453
bool tox_group_set_topic_lock(Tox *_Nonnull tox, uint32_t group_number, Tox_Group_Topic_Lock topic_lock,
4454
                              Tox_Err_Group_Set_Topic_Lock *_Nullable error)
4455
6
{
4456
6
    assert(tox != nullptr);
4457
4458
6
    tox_lock(tox);
4459
6
    const int ret = gc_founder_set_topic_lock(tox->m, group_number, (Group_Topic_Lock) topic_lock);
4460
6
    tox_unlock(tox);
4461
4462
6
    switch (ret) {
4463
6
        case 0: {
4464
6
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SET_TOPIC_LOCK_OK);
4465
6
            return true;
4466
0
        }
4467
4468
0
        case -1: {
4469
0
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SET_TOPIC_LOCK_GROUP_NOT_FOUND);
4470
0
            return false;
4471
0
        }
4472
4473
0
        case -2: {
4474
0
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SET_TOPIC_LOCK_INVALID);
4475
0
            return false;
4476
0
        }
4477
4478
0
        case -3: {
4479
0
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SET_TOPIC_LOCK_PERMISSIONS);
4480
0
            return false;
4481
0
        }
4482
4483
0
        case -4: {
4484
0
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SET_TOPIC_LOCK_DISCONNECTED);
4485
0
            return false;
4486
0
        }
4487
4488
0
        case -5: {
4489
0
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SET_TOPIC_LOCK_FAIL_SET);
4490
0
            return false;
4491
0
        }
4492
4493
0
        case -6: {
4494
0
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SET_TOPIC_LOCK_FAIL_SEND);
4495
0
            return false;
4496
0
        }
4497
6
    }
4498
4499
    /* can't happen */
4500
0
    LOGGER_FATAL(tox->m->log, "impossible return value: %d", ret);
4501
4502
0
    return false;
4503
6
}
4504
4505
bool tox_group_set_voice_state(Tox *_Nonnull tox, uint32_t group_number, Tox_Group_Voice_State voice_state,
4506
                               Tox_Err_Group_Set_Voice_State *_Nullable error)
4507
5
{
4508
5
    assert(tox != nullptr);
4509
5
    tox_lock(tox);
4510
5
    const int ret = gc_founder_set_voice_state(tox->m, group_number, (Group_Voice_State)voice_state);
4511
5
    tox_unlock(tox);
4512
4513
5
    switch (ret) {
4514
5
        case 0: {
4515
5
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SET_VOICE_STATE_OK);
4516
5
            return true;
4517
0
        }
4518
4519
0
        case -1: {
4520
0
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SET_VOICE_STATE_GROUP_NOT_FOUND);
4521
0
            return false;
4522
0
        }
4523
4524
0
        case -2: {
4525
0
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SET_VOICE_STATE_PERMISSIONS);
4526
0
            return false;
4527
0
        }
4528
4529
0
        case -3: {
4530
0
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SET_VOICE_STATE_DISCONNECTED);
4531
0
            return false;
4532
0
        }
4533
4534
0
        case -4: {
4535
0
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SET_VOICE_STATE_FAIL_SET);
4536
0
            return false;
4537
0
        }
4538
4539
0
        case -5: {
4540
0
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SET_VOICE_STATE_FAIL_SEND);
4541
0
            return false;
4542
0
        }
4543
5
    }
4544
4545
    /* can't happen */
4546
0
    LOGGER_FATAL(tox->m->log, "impossible return value: %d", ret);
4547
4548
0
    return false;
4549
5
}
4550
4551
bool tox_group_set_peer_limit(Tox *_Nonnull tox, uint32_t group_number, uint16_t peer_limit,
4552
                              Tox_Err_Group_Set_Peer_Limit *_Nullable error)
4553
6
{
4554
6
    assert(tox != nullptr);
4555
4556
6
    tox_lock(tox);
4557
6
    GC_Chat *chat = gc_get_group(tox->m->group_handler, group_number);
4558
4559
6
    if (chat == nullptr) {
4560
0
        SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SET_PEER_LIMIT_GROUP_NOT_FOUND);
4561
0
        tox_unlock(tox);
4562
0
        return false;
4563
0
    }
4564
4565
6
    if (chat->connection_state == CS_DISCONNECTED) {
4566
0
        SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SET_PEER_LIMIT_DISCONNECTED);
4567
0
        tox_unlock(tox);
4568
0
        return false;
4569
0
    }
4570
4571
6
    const int ret = gc_founder_set_max_peers(chat, peer_limit);
4572
6
    tox_unlock(tox);
4573
4574
6
    switch (ret) {
4575
6
        case 0: {
4576
6
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SET_PEER_LIMIT_OK);
4577
6
            return true;
4578
0
        }
4579
4580
0
        case -1: {
4581
0
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SET_PEER_LIMIT_PERMISSIONS);
4582
0
            return false;
4583
0
        }
4584
4585
0
        case -2: {
4586
0
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SET_PEER_LIMIT_FAIL_SET);
4587
0
            return false;
4588
0
        }
4589
4590
0
        case -3: {
4591
0
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SET_PEER_LIMIT_FAIL_SEND);
4592
0
            return false;
4593
0
        }
4594
6
    }
4595
4596
    /* can't happen */
4597
0
    LOGGER_FATAL(tox->m->log, "impossible return value: %d", ret);
4598
4599
0
    return false;
4600
6
}
4601
4602
bool tox_group_set_ignore(Tox *_Nonnull tox, uint32_t group_number, uint32_t peer_id, bool ignore,
4603
                          Tox_Err_Group_Set_Ignore *_Nullable error)
4604
0
{
4605
0
    assert(tox != nullptr);
4606
4607
0
    tox_lock(tox);
4608
0
    const GC_Chat *chat = gc_get_group(tox->m->group_handler, group_number);
4609
4610
0
    if (chat == nullptr) {
4611
0
        SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SET_IGNORE_GROUP_NOT_FOUND);
4612
0
        tox_unlock(tox);
4613
0
        return false;
4614
0
    }
4615
4616
0
    const int ret = gc_set_ignore(chat, gc_peer_id_from_int(peer_id), ignore);
4617
0
    tox_unlock(tox);
4618
4619
0
    switch (ret) {
4620
0
        case 0: {
4621
0
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SET_IGNORE_OK);
4622
0
            return true;
4623
0
        }
4624
4625
0
        case -1: {
4626
0
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SET_IGNORE_PEER_NOT_FOUND);
4627
0
            return false;
4628
0
        }
4629
4630
0
        case -2: {
4631
0
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SET_IGNORE_SELF);
4632
0
            return false;
4633
0
        }
4634
0
    }
4635
4636
    /* can't happen */
4637
0
    LOGGER_FATAL(tox->m->log, "impossible return value: %d", ret);
4638
4639
0
    return false;
4640
0
}
4641
4642
bool tox_group_set_role(Tox *_Nonnull tox, uint32_t group_number, uint32_t peer_id, Tox_Group_Role role,
4643
                        Tox_Err_Group_Set_Role *_Nullable error)
4644
12
{
4645
12
    assert(tox != nullptr);
4646
4647
12
    tox_lock(tox);
4648
12
    const int ret = gc_set_peer_role(tox->m, group_number, gc_peer_id_from_int(peer_id), (Group_Role) role);
4649
12
    tox_unlock(tox);
4650
4651
12
    switch (ret) {
4652
11
        case 0: {
4653
11
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SET_ROLE_OK);
4654
11
            return true;
4655
0
        }
4656
4657
0
        case -1: {
4658
0
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SET_ROLE_GROUP_NOT_FOUND);
4659
0
            return false;
4660
0
        }
4661
4662
0
        case -2: {
4663
0
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SET_ROLE_PEER_NOT_FOUND);
4664
0
            return false;
4665
0
        }
4666
4667
1
        case -3: {
4668
1
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SET_ROLE_PERMISSIONS);
4669
1
            return false;
4670
0
        }
4671
4672
0
        case -4: {
4673
0
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SET_ROLE_ASSIGNMENT);
4674
0
            return false;
4675
0
        }
4676
4677
0
        case -5: {
4678
0
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SET_ROLE_FAIL_ACTION);
4679
0
            return false;
4680
0
        }
4681
4682
0
        case -6: {
4683
0
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_SET_ROLE_SELF);
4684
0
            return false;
4685
0
        }
4686
12
    }
4687
4688
    /* can't happen */
4689
0
    LOGGER_FATAL(tox->m->log, "impossible return value: %d", ret);
4690
4691
0
    return false;
4692
12
}
4693
4694
bool tox_group_kick_peer(const Tox *_Nonnull tox, uint32_t group_number, uint32_t peer_id,
4695
                         Tox_Err_Group_Kick_Peer *_Nullable error)
4696
2
{
4697
2
    assert(tox != nullptr);
4698
4699
2
    tox_lock(tox);
4700
2
    const int ret = gc_kick_peer(tox->m, group_number, gc_peer_id_from_int(peer_id));
4701
2
    tox_unlock(tox);
4702
4703
2
    switch (ret) {
4704
1
        case 0: {
4705
1
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_KICK_PEER_OK);
4706
1
            return true;
4707
0
        }
4708
4709
0
        case -1: {
4710
0
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_KICK_PEER_GROUP_NOT_FOUND);
4711
0
            return false;
4712
0
        }
4713
4714
0
        case -2: {
4715
0
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_KICK_PEER_PEER_NOT_FOUND);
4716
0
            return false;
4717
0
        }
4718
4719
1
        case -3: {
4720
1
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_KICK_PEER_PERMISSIONS);
4721
1
            return false;
4722
0
        }
4723
4724
0
        case -4: {
4725
0
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_KICK_PEER_FAIL_ACTION);
4726
0
            return false;
4727
0
        }
4728
4729
0
        case -5: {
4730
0
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_KICK_PEER_FAIL_SEND);
4731
0
            return false;
4732
0
        }
4733
4734
0
        case -6: {
4735
0
            SET_ERROR_PARAMETER(error, TOX_ERR_GROUP_KICK_PEER_SELF);
4736
0
            return false;
4737
0
        }
4738
2
    }
4739
4740
    /* can't happen */
4741
0
    LOGGER_FATAL(tox->m->log, "impossible return value: %d", ret);
4742
4743
0
    return false;
4744
2
}
4745
4746
const Tox_System *tox_get_system(const Tox *tox)
4747
119k
{
4748
119k
    assert(tox != nullptr);
4749
119k
    return &tox->sys;
4750
119k
}
4751
4752
struct Tox_Iterate_Options {
4753
    bool fail_hard;
4754
};
4755
4756
Tox_Iterate_Options *tox_iterate_options_new(Tox_Err_Iterate_Options_New *error)
4757
30
{
4758
    // cppcheck-suppress misra-c2012-21.3
4759
30
    Tox_Iterate_Options *options = (Tox_Iterate_Options *)calloc(1, sizeof(Tox_Iterate_Options));
4760
4761
30
    if (options != nullptr) {
4762
30
        options->fail_hard = false;
4763
4764
30
        SET_ERROR_PARAMETER(error, TOX_ERR_ITERATE_OPTIONS_NEW_OK);
4765
4766
30
        return options;
4767
30
    }
4768
4769
0
    SET_ERROR_PARAMETER(error, TOX_ERR_ITERATE_OPTIONS_NEW_MALLOC);
4770
0
    return nullptr;
4771
30
}
4772
4773
void tox_iterate_options_free(Tox_Iterate_Options *options)
4774
30
{
4775
    // cppcheck-suppress misra-c2012-21.3
4776
30
    free(options);
4777
30
}
4778
4779
void tox_iterate_options_set_fail_hard(Tox_Iterate_Options *options, bool fail_hard)
4780
30
{
4781
30
    if (options != nullptr) {
4782
30
        options->fail_hard = fail_hard;
4783
30
    }
4784
30
}
4785
4786
bool tox_iterate_options_get_fail_hard(const Tox_Iterate_Options *options)
4787
119k
{
4788
119k
    return options == nullptr ? false : options->fail_hard;
4789
119k
}