Coverage Report

Created: 2025-08-05 10:35

/src/c-toxcore/toxcore/group.c
Line
Count
Source (jump to first uncovered line)
1
/* SPDX-License-Identifier: GPL-3.0-or-later
2
 * Copyright © 2016-2025 The TokTok team.
3
 * Copyright © 2014 Tox project.
4
 */
5
6
/**
7
 * Slightly better groupchats implementation.
8
 */
9
#include "group.h"
10
11
#include <assert.h>
12
#include <string.h>
13
14
#include "DHT.h"
15
#include "Messenger.h"
16
#include "attributes.h"
17
#include "ccompat.h"
18
#include "crypto_core.h"
19
#include "friend_connection.h"
20
#include "group_common.h"
21
#include "logger.h"
22
#include "mem.h"
23
#include "mono_time.h"
24
#include "net_crypto.h"
25
#include "network.h"
26
#include "sort.h"
27
#include "state.h"
28
#include "util.h"
29
30
enum {
31
    /** Connection is to one of the closest DESIRED_CLOSEST peers */
32
    GROUPCHAT_CONNECTION_REASON_CLOSEST     = 1 << 0,
33
34
    /** Connection is to a peer we are introducing to the conference */
35
    GROUPCHAT_CONNECTION_REASON_INTRODUCING = 1 << 1,
36
37
    /** Connection is to a peer who is introducing us to the conference */
38
    GROUPCHAT_CONNECTION_REASON_INTRODUCER  = 1 << 2,
39
};
40
41
typedef enum Groupchat_Connection_Type {
42
    GROUPCHAT_CONNECTION_NONE,
43
    GROUPCHAT_CONNECTION_CONNECTING,
44
    GROUPCHAT_CONNECTION_ONLINE,
45
} Groupchat_Connection_Type;
46
47
typedef enum Groupchat_Status {
48
    GROUPCHAT_STATUS_NONE,
49
    GROUPCHAT_STATUS_VALID,
50
    GROUPCHAT_STATUS_CONNECTED,
51
} Groupchat_Status;
52
53
27.0k
#define GROUP_ID_LENGTH CRYPTO_SYMMETRIC_KEY_SIZE
54
55
63
#define DESIRED_CLOSEST 4
56
80.9k
#define MAX_GROUP_CONNECTIONS 16
57
0
#define MAX_LAST_MESSAGE_INFOS 8
58
0
#define MAX_LOSSY_COUNT 256
59
60
/** Maximum number of frozen peers to store; `group_set_max_frozen()` overrides. */
61
12.2k
#define MAX_FROZEN_DEFAULT 128
62
63
typedef struct Message_Info {
64
    uint32_t message_number;
65
    uint8_t  message_id;
66
} Message_Info;
67
68
typedef struct Group_Peer {
69
    uint8_t     real_pk[CRYPTO_PUBLIC_KEY_SIZE];
70
    uint8_t     temp_pk[CRYPTO_PUBLIC_KEY_SIZE];
71
    bool        temp_pk_updated;
72
    bool        is_friend;
73
74
    uint64_t    last_active;
75
76
    Message_Info
77
    last_message_infos[MAX_LAST_MESSAGE_INFOS]; /* received messages, strictly decreasing in message_number */
78
    uint8_t     num_last_message_infos;
79
80
    uint8_t     nick[MAX_NAME_LENGTH];
81
    uint8_t     nick_len;
82
    bool        nick_updated;
83
84
    uint16_t peer_number;
85
86
    uint8_t  recv_lossy[MAX_LOSSY_COUNT];
87
    uint16_t bottom_lossy_number;
88
    uint16_t top_lossy_number;
89
90
    void *object;
91
} Group_Peer;
92
93
typedef struct Groupchat_Connection {
94
    uint8_t type; /* `GROUPCHAT_CONNECTION_*` */
95
    uint8_t reasons; /* bit field with flags `GROUPCHAT_CONNECTION_REASON_*` */
96
    uint32_t number;
97
    uint16_t group_number;
98
} Groupchat_Connection;
99
100
typedef struct Groupchat_Closest {
101
    /**
102
     * Whether this peer is active in the closest_peers array.
103
     */
104
    bool active;
105
    uint8_t real_pk[CRYPTO_PUBLIC_KEY_SIZE];
106
    uint8_t temp_pk[CRYPTO_PUBLIC_KEY_SIZE];
107
} Groupchat_Closest;
108
109
typedef struct Group_c {
110
    uint8_t status;
111
112
    bool need_send_name;
113
    bool title_fresh;
114
115
    Group_Peer *group;
116
    uint32_t numpeers;
117
118
    Group_Peer *frozen;
119
    uint32_t numfrozen;
120
121
    uint32_t maxfrozen;
122
123
    Groupchat_Connection connections[MAX_GROUP_CONNECTIONS];
124
125
    uint8_t real_pk[CRYPTO_PUBLIC_KEY_SIZE];
126
    Groupchat_Closest closest_peers[DESIRED_CLOSEST];
127
    uint8_t changed;
128
129
    uint8_t type;
130
    uint8_t id[GROUP_ID_LENGTH];
131
132
    uint8_t title[MAX_NAME_LENGTH];
133
    uint8_t title_len;
134
135
    uint32_t message_number;
136
    uint16_t lossy_message_number;
137
    uint16_t peer_number;
138
139
    uint64_t last_sent_ping;
140
141
    uint32_t num_introducer_connections;
142
143
    void *object;
144
145
    peer_on_join_cb *peer_on_join;
146
    peer_on_leave_cb *peer_on_leave;
147
    group_on_delete_cb *group_on_delete;
148
} Group_c;
149
150
struct Group_Chats {
151
    const Memory *mem;
152
    const Mono_Time *mono_time;
153
154
    Messenger *m;
155
    Friend_Connections *fr_c;
156
157
    Group_c *chats;
158
    uint16_t num_chats;
159
160
    g_conference_invite_cb *invite_callback;
161
    g_conference_connected_cb *connected_callback;
162
    g_conference_message_cb *message_callback;
163
    peer_name_cb *peer_name_callback;
164
    peer_list_changed_cb *peer_list_changed_callback;
165
    title_cb *title_callback;
166
167
    lossy_packet_cb *lossy_packethandlers[256];
168
};
169
170
static const Group_c empty_group_c = {0};
171
static const Group_Peer empty_group_peer = {{0}};
172
173
/**
174
 * Packet type IDs as per the protocol specification.
175
 */
176
typedef enum Group_Message_Id {
177
    GROUP_MESSAGE_PING_ID        = 0,
178
    GROUP_MESSAGE_NEW_PEER_ID    = 16,
179
    GROUP_MESSAGE_KILL_PEER_ID   = 17,
180
    GROUP_MESSAGE_FREEZE_PEER_ID = 18,
181
    GROUP_MESSAGE_NAME_ID        = 48,
182
    GROUP_MESSAGE_TITLE_ID       = 49,
183
} Group_Message_Id;
184
185
0
#define GROUP_MESSAGE_NEW_PEER_LENGTH (sizeof(uint16_t) + CRYPTO_PUBLIC_KEY_SIZE * 2)
186
0
#define GROUP_MESSAGE_KILL_PEER_LENGTH (sizeof(uint16_t))
187
188
2.38k
#define MAX_GROUP_MESSAGE_DATA_LEN (MAX_CRYPTO_DATA_SIZE - (1 + MIN_MESSAGE_PACKET_LEN))
189
190
typedef enum Invite_Id {
191
    INVITE_ID             = 0,
192
    INVITE_ACCEPT_ID      = 1,
193
    INVITE_MEMBER_ID      = 2,
194
} Invite_Id;
195
196
0
#define INVITE_PACKET_SIZE (1 + sizeof(uint16_t) + 1 + GROUP_ID_LENGTH)
197
0
#define INVITE_ACCEPT_PACKET_SIZE (1 + sizeof(uint16_t) * 2 + 1 + GROUP_ID_LENGTH)
198
0
#define INVITE_MEMBER_PACKET_SIZE (1 + sizeof(uint16_t) * 2 + 1 + GROUP_ID_LENGTH + sizeof(uint16_t))
199
200
0
#define ONLINE_PACKET_DATA_SIZE (sizeof(uint16_t) + 1 + GROUP_ID_LENGTH)
201
202
typedef enum Peer_Id {
203
    PEER_INTRODUCED_ID  = 1,
204
    PEER_QUERY_ID       = 8,
205
    PEER_RESPONSE_ID    = 9,
206
    PEER_TITLE_ID       = 10,
207
} Peer_Id;
208
209
2.38k
#define MIN_MESSAGE_PACKET_LEN (sizeof(uint16_t) * 2 + sizeof(uint32_t) + 1)
210
211
static_assert(GROUP_ID_LENGTH == CRYPTO_PUBLIC_KEY_SIZE,
212
              "GROUP_ID_LENGTH should be equal to CRYPTO_PUBLIC_KEY_SIZE");
213
214
const Mono_Time *g_mono_time(const Group_Chats *g_c)
215
0
{
216
0
    return g_c->mono_time;
217
0
}
218
219
static bool group_id_eq(const uint8_t *_Nonnull a, const uint8_t *_Nonnull b)
220
0
{
221
0
    return pk_equal(a, b);
222
0
}
223
224
static bool g_title_eq(const Group_c *_Nonnull g, const uint8_t *_Nonnull title, uint8_t title_len)
225
0
{
226
0
    return memeq(g->title, g->title_len, title, title_len);
227
0
}
228
229
static bool g_peer_nick_eq(const Group_Peer *_Nonnull peer, const uint8_t *_Nonnull nick, uint8_t nick_len)
230
6.07k
{
231
6.07k
    return memeq(peer->nick, peer->nick_len, nick, nick_len);
232
6.07k
}
233
234
/**
235
 * @retval false if the groupnumber is not valid.
236
 * @retval true if the groupnumber is valid.
237
 */
238
static bool is_groupnumber_valid(const Group_Chats *_Nonnull g_c, uint32_t groupnumber)
239
38.0k
{
240
38.0k
    return groupnumber < g_c->num_chats
241
38.0k
           && g_c->chats != nullptr
242
38.0k
           && g_c->chats[groupnumber].status != GROUPCHAT_STATUS_NONE;
243
38.0k
}
244
245
/** @brief Set the size of the groupchat list to num.
246
 *
247
 * @retval false if mem_vrealloc fails.
248
 * @retval true if it succeeds.
249
 */
250
static bool realloc_conferences(Group_Chats *_Nonnull g_c, uint16_t num)
251
6.29k
{
252
6.29k
    if (num == 0) {
253
114
        mem_delete(g_c->mem, g_c->chats);
254
114
        g_c->chats = nullptr;
255
114
        return true;
256
114
    }
257
258
6.18k
    Group_c *newgroup_chats = (Group_c *)mem_vrealloc(g_c->mem, g_c->chats, num, sizeof(Group_c));
259
260
6.18k
    if (newgroup_chats == nullptr) {
261
0
        return false;
262
0
    }
263
264
6.18k
    g_c->chats = newgroup_chats;
265
6.18k
    return true;
266
6.18k
}
267
268
static void setup_conference(Group_c *_Nonnull g)
269
12.2k
{
270
12.2k
    *g = empty_group_c;
271
12.2k
    g->maxfrozen = MAX_FROZEN_DEFAULT;
272
12.2k
}
273
274
/** @brief Create a new empty groupchat connection.
275
 *
276
 * @retval -1 on failure.
277
 * @return groupnumber on success.
278
 */
279
static int32_t create_group_chat(Group_Chats *_Nonnull g_c)
280
6.13k
{
281
472k
    for (uint16_t i = 0; i < g_c->num_chats; ++i) {
282
466k
        if (g_c->chats[i].status == GROUPCHAT_STATUS_NONE) {
283
0
            return i;
284
0
        }
285
466k
    }
286
287
6.13k
    if (realloc_conferences(g_c, g_c->num_chats + 1)) {
288
6.13k
        const uint16_t id = g_c->num_chats;
289
6.13k
        ++g_c->num_chats;
290
6.13k
        setup_conference(&g_c->chats[id]);
291
6.13k
        return id;
292
6.13k
    }
293
294
0
    return -1;
295
6.13k
}
296
297
static void wipe_group_c(const Memory *_Nonnull mem, Group_c *_Nonnull g)
298
6.13k
{
299
6.13k
    mem_delete(mem, g->frozen);
300
6.13k
    mem_delete(mem, g->group);
301
6.13k
    crypto_memzero(g, sizeof(Group_c));
302
6.13k
}
303
304
/** @brief Wipe a groupchat.
305
 *
306
 * @retval true on success.
307
 */
308
static bool wipe_group_chat(Group_Chats *_Nonnull g_c, uint32_t groupnumber)
309
6.13k
{
310
6.13k
    if (groupnumber >= g_c->num_chats || g_c->chats == nullptr) {
311
0
        return false;
312
0
    }
313
314
6.13k
    wipe_group_c(g_c->mem, &g_c->chats[groupnumber]);
315
316
6.13k
    uint16_t i;
317
318
12.2k
    for (i = g_c->num_chats; i != 0; --i) {
319
12.1k
        if (g_c->chats[i - 1].status != GROUPCHAT_STATUS_NONE) {
320
6.02k
            break;
321
6.02k
        }
322
12.1k
    }
323
324
6.13k
    if (g_c->num_chats != i) {
325
158
        g_c->num_chats = i;
326
158
        realloc_conferences(g_c, g_c->num_chats);
327
158
    }
328
329
6.13k
    return true;
330
6.13k
}
331
332
static Group_c *get_group_c(const Group_Chats *_Nonnull g_c, uint32_t groupnumber)
333
38.0k
{
334
38.0k
    if (!is_groupnumber_valid(g_c, groupnumber)) {
335
0
        return nullptr;
336
0
    }
337
338
38.0k
    return &g_c->chats[groupnumber];
339
38.0k
}
340
341
/**
342
 * check if peer with real_pk is in peer array.
343
 *
344
 * @return peer index if peer is in group.
345
 * @retval -1 if peer is not in group.
346
 *
347
 * TODO(irungentoo): make this more efficient.
348
 */
349
static int peer_in_group(const Group_c *_Nonnull g, const uint8_t *_Nonnull real_pk)
350
6.03k
{
351
6.03k
    for (uint32_t i = 0; i < g->numpeers; ++i) {
352
0
        if (pk_equal(g->group[i].real_pk, real_pk)) {
353
0
            return i;
354
0
        }
355
0
    }
356
357
6.03k
    return -1;
358
6.03k
}
359
360
static int frozen_in_group(const Group_c *_Nonnull g, const uint8_t *_Nonnull real_pk)
361
6.03k
{
362
6.16k
    for (uint32_t i = 0; i < g->numfrozen; ++i) {
363
152
        if (pk_equal(g->frozen[i].real_pk, real_pk)) {
364
29
            return i;
365
29
        }
366
152
    }
367
368
6.00k
    return -1;
369
6.03k
}
370
371
/**
372
 * check if group with the given type and id is in group array.
373
 *
374
 * @return group number if peer is in list.
375
 * @retval -1 if group is not in list.
376
 *
377
 * TODO(irungentoo): make this more efficient and maybe use constant time comparisons?
378
 */
379
static int32_t get_group_num(const Group_Chats *_Nonnull g_c, const uint8_t type, const uint8_t *_Nonnull id)
380
0
{
381
0
    for (uint16_t i = 0; i < g_c->num_chats; ++i) {
382
0
        if (g_c->chats[i].type == type && group_id_eq(g_c->chats[i].id, id)) {
383
0
            return i;
384
0
        }
385
0
    }
386
387
0
    return -1;
388
0
}
389
390
int32_t conference_by_id(const Group_Chats *g_c, const uint8_t *id)
391
0
{
392
0
    for (uint16_t i = 0; i < g_c->num_chats; ++i) {
393
0
        if (group_id_eq(g_c->chats[i].id, id)) {
394
0
            return i;
395
0
        }
396
0
    }
397
398
0
    return -1;
399
0
}
400
401
/**
402
 * check if peer with peer_number is in peer array.
403
 *
404
 * @return peer index if peer is in chat.
405
 * @retval -1 if peer is not in chat.
406
 *
407
 * TODO(irungentoo): make this more efficient.
408
 */
409
static int get_peer_index(const Group_c *_Nonnull g, uint16_t peer_number)
410
6.07k
{
411
6.07k
    for (uint32_t i = 0; i < g->numpeers; ++i) {
412
0
        if (g->group[i].peer_number == peer_number) {
413
0
            return i;
414
0
        }
415
0
    }
416
417
6.07k
    return -1;
418
6.07k
}
419
420
static uint64_t calculate_comp_value(const uint8_t *_Nonnull pk1, const uint8_t *_Nonnull pk2)
421
0
{
422
0
    uint64_t cmp1 = 0;
423
0
    uint64_t cmp2 = 0;
424
425
0
    for (size_t i = 0; i < sizeof(uint64_t); ++i) {
426
0
        cmp1 = (cmp1 << 8) + (uint64_t)pk1[i];
427
0
        cmp2 = (cmp2 << 8) + (uint64_t)pk2[i];
428
0
    }
429
430
0
    return cmp1 - cmp2;
431
0
}
432
433
typedef enum Groupchat_Closest_Change {
434
    GROUPCHAT_CLOSEST_CHANGE_NONE,
435
    GROUPCHAT_CLOSEST_CHANGE_ADDED,
436
    GROUPCHAT_CLOSEST_CHANGE_REMOVED,
437
} Groupchat_Closest_Change;
438
439
static bool add_to_closest(Group_c *_Nonnull g, const uint8_t *_Nonnull real_pk, const uint8_t *_Nonnull temp_pk)
440
6.07k
{
441
6.07k
    if (pk_equal(g->real_pk, real_pk)) {
442
6.07k
        return false;
443
6.07k
    }
444
445
7
    unsigned int index = DESIRED_CLOSEST;
446
447
35
    for (unsigned int i = 0; i < DESIRED_CLOSEST; ++i) {
448
28
        if (g->closest_peers[i].active && pk_equal(real_pk, g->closest_peers[i].real_pk)) {
449
0
            return true;
450
0
        }
451
28
    }
452
453
7
    for (unsigned int i = 0; i < DESIRED_CLOSEST; ++i) {
454
7
        if (!g->closest_peers[i].active) {
455
7
            index = i;
456
7
            break;
457
7
        }
458
7
    }
459
460
7
    if (index == DESIRED_CLOSEST) {
461
0
        uint64_t comp_val = calculate_comp_value(g->real_pk, real_pk);
462
0
        uint64_t comp_d = 0;
463
464
0
        for (unsigned int i = 0; i < (DESIRED_CLOSEST / 2); ++i) {
465
0
            const uint64_t comp = calculate_comp_value(g->real_pk, g->closest_peers[i].real_pk);
466
467
0
            if (comp > comp_val && comp > comp_d) {
468
0
                index = i;
469
0
                comp_d = comp;
470
0
            }
471
0
        }
472
473
0
        comp_val = calculate_comp_value(real_pk, g->real_pk);
474
475
0
        for (unsigned int i = DESIRED_CLOSEST / 2; i < DESIRED_CLOSEST; ++i) {
476
0
            const uint64_t comp = calculate_comp_value(g->closest_peers[i].real_pk, g->real_pk);
477
478
0
            if (comp > comp_val && comp > comp_d) {
479
0
                index = i;
480
0
                comp_d = comp;
481
0
            }
482
0
        }
483
0
    }
484
485
7
    if (index == DESIRED_CLOSEST) {
486
0
        return false;
487
0
    }
488
489
7
    uint8_t old_real_pk[CRYPTO_PUBLIC_KEY_SIZE];
490
7
    uint8_t old_temp_pk[CRYPTO_PUBLIC_KEY_SIZE];
491
7
    bool old = false;
492
493
7
    if (g->closest_peers[index].active) {
494
0
        memcpy(old_real_pk, g->closest_peers[index].real_pk, CRYPTO_PUBLIC_KEY_SIZE);
495
0
        memcpy(old_temp_pk, g->closest_peers[index].temp_pk, CRYPTO_PUBLIC_KEY_SIZE);
496
0
        old = true;
497
0
    }
498
499
7
    g->closest_peers[index].active = true;
500
7
    memcpy(g->closest_peers[index].real_pk, real_pk, CRYPTO_PUBLIC_KEY_SIZE);
501
7
    memcpy(g->closest_peers[index].temp_pk, temp_pk, CRYPTO_PUBLIC_KEY_SIZE);
502
503
7
    if (old) {
504
0
        add_to_closest(g, old_real_pk, old_temp_pk);
505
0
    }
506
507
7
    if (g->changed == GROUPCHAT_CLOSEST_CHANGE_NONE) {
508
7
        g->changed = GROUPCHAT_CLOSEST_CHANGE_ADDED;
509
7
    }
510
511
7
    return true;
512
7
}
513
514
static bool pk_in_closest_peers(const Group_c *_Nonnull g, const uint8_t *_Nonnull real_pk)
515
0
{
516
0
    for (unsigned int i = 0; i < DESIRED_CLOSEST; ++i) {
517
0
        if (!g->closest_peers[i].active) {
518
0
            continue;
519
0
        }
520
521
0
        if (pk_equal(g->closest_peers[i].real_pk, real_pk)) {
522
0
            return true;
523
0
        }
524
0
    }
525
526
0
    return false;
527
0
}
528
529
static void remove_connection_reason(Group_Chats *_Nonnull g_c, Group_c *_Nonnull g, uint16_t i, uint8_t reason);
530
531
static void purge_closest(Group_Chats *_Nonnull g_c, uint32_t groupnumber)
532
0
{
533
0
    Group_c *g = get_group_c(g_c, groupnumber);
534
535
0
    if (g == nullptr) {
536
0
        return;
537
0
    }
538
539
0
    for (uint32_t i = 0; i < MAX_GROUP_CONNECTIONS; ++i) {
540
0
        if (g->connections[i].type == GROUPCHAT_CONNECTION_NONE) {
541
0
            continue;
542
0
        }
543
544
0
        if ((g->connections[i].reasons & GROUPCHAT_CONNECTION_REASON_CLOSEST) == 0) {
545
0
            continue;
546
0
        }
547
548
0
        uint8_t real_pk[CRYPTO_PUBLIC_KEY_SIZE];
549
0
        get_friendcon_public_keys(real_pk, nullptr, g_c->fr_c, g->connections[i].number);
550
551
0
        if (!pk_in_closest_peers(g, real_pk)) {
552
0
            remove_connection_reason(g_c, g, i, GROUPCHAT_CONNECTION_REASON_CLOSEST);
553
0
        }
554
0
    }
555
0
}
556
557
static bool send_packet_online(const Friend_Connections *_Nonnull fr_c, int friendcon_id, uint16_t group_num, uint8_t type, const uint8_t *_Nonnull id);
558
559
static int add_conn_to_groupchat(Group_Chats *_Nonnull g_c, int friendcon_id, Group_c *_Nonnull g, uint8_t reason, bool lock);
560
561
static void add_closest_connections(Group_Chats *_Nonnull g_c, uint32_t groupnumber, void *_Nullable userdata)
562
0
{
563
0
    Group_c *g = get_group_c(g_c, groupnumber);
564
0
    if (g == nullptr) {
565
0
        return;
566
0
    }
567
568
0
    for (uint32_t i = 0; i < DESIRED_CLOSEST; ++i) {
569
0
        if (!g->closest_peers[i].active) {
570
0
            continue;
571
0
        }
572
573
0
        int friendcon_id = getfriend_conn_id_pk(g_c->fr_c, g->closest_peers[i].real_pk);
574
575
0
        bool fresh = false;
576
577
0
        if (friendcon_id == -1) {
578
0
            friendcon_id = new_friend_connection(g_c->fr_c, g->closest_peers[i].real_pk);
579
0
            fresh = true;
580
581
0
            if (friendcon_id == -1) {
582
0
                continue;
583
0
            }
584
585
0
            set_dht_temp_pk(g_c->fr_c, friendcon_id, g->closest_peers[i].temp_pk, userdata);
586
0
        }
587
588
0
        const int connection_index = add_conn_to_groupchat(g_c, friendcon_id, g,
589
0
                                     GROUPCHAT_CONNECTION_REASON_CLOSEST, !fresh);
590
591
0
        if (connection_index == -1) {
592
0
            if (fresh) {
593
0
                kill_friend_connection(g_c->fr_c, friendcon_id);
594
0
            }
595
596
0
            continue;
597
0
        }
598
599
0
        if (friend_con_connected(g_c->fr_c, friendcon_id) == FRIENDCONN_STATUS_CONNECTED
600
0
                && g->connections[connection_index].type == GROUPCHAT_CONNECTION_CONNECTING) {
601
0
            send_packet_online(g_c->fr_c, friendcon_id, groupnumber, g->type, g->id);
602
0
        }
603
0
    }
604
0
}
605
606
static bool connect_to_closest(Group_Chats *_Nonnull g_c, uint32_t groupnumber, void *_Nullable userdata)
607
0
{
608
0
    Group_c *g = get_group_c(g_c, groupnumber);
609
0
    if (g == nullptr) {
610
0
        return false;
611
0
    }
612
613
0
    if (g->changed == GROUPCHAT_CLOSEST_CHANGE_NONE) {
614
0
        return true;
615
0
    }
616
617
0
    if (g->changed == GROUPCHAT_CLOSEST_CHANGE_REMOVED) {
618
0
        for (uint32_t i = 0; i < g->numpeers; ++i) {
619
0
            add_to_closest(g, g->group[i].real_pk, g->group[i].temp_pk);
620
0
        }
621
0
    }
622
623
0
    purge_closest(g_c, groupnumber);
624
625
0
    add_closest_connections(g_c, groupnumber, userdata);
626
627
0
    g->changed = GROUPCHAT_CLOSEST_CHANGE_NONE;
628
629
0
    return true;
630
0
}
631
632
static int get_frozen_index(const Group_c *_Nonnull g, uint16_t peer_number)
633
6.07k
{
634
6.33k
    for (uint32_t i = 0; i < g->numfrozen; ++i) {
635
295
        if (g->frozen[i].peer_number == peer_number) {
636
41
            return i;
637
41
        }
638
295
    }
639
640
6.03k
    return -1;
641
6.07k
}
642
643
static bool delete_frozen(const Memory *_Nonnull mem, Group_c *_Nonnull g, uint32_t frozen_index)
644
70
{
645
70
    if (frozen_index >= g->numfrozen) {
646
0
        return false;
647
0
    }
648
649
70
    --g->numfrozen;
650
651
70
    if (g->numfrozen == 0) {
652
55
        mem_delete(mem, g->frozen);
653
55
        g->frozen = nullptr;
654
55
    } else {
655
15
        if (g->numfrozen != frozen_index) {
656
13
            g->frozen[frozen_index] = g->frozen[g->numfrozen];
657
13
        }
658
659
15
        Group_Peer *const frozen_temp = (Group_Peer *)mem_vrealloc(mem, g->frozen, g->numfrozen, sizeof(Group_Peer));
660
661
15
        if (frozen_temp == nullptr) {
662
0
            return false;
663
0
        }
664
665
15
        g->frozen = frozen_temp;
666
15
    }
667
668
70
    return true;
669
70
}
670
671
/** @brief Update last_active timestamp on peer, and thaw the peer if it is frozen.
672
 *
673
 * @return peer index if peer is in the conference.
674
 * @retval -1 otherwise, and on error.
675
 */
676
static int note_peer_active(Group_Chats *_Nonnull g_c, uint32_t groupnumber, uint16_t peer_number, void *_Nullable userdata)
677
6.07k
{
678
6.07k
    Group_c *g = get_group_c(g_c, groupnumber);
679
6.07k
    if (g == nullptr) {
680
0
        return -1;
681
0
    }
682
683
6.07k
    const int peer_index = get_peer_index(g, peer_number);
684
685
6.07k
    if (peer_index != -1) {
686
0
        g->group[peer_index].last_active = mono_time_get(g_c->mono_time);
687
0
        return peer_index;
688
0
    }
689
690
6.07k
    const int frozen_index = get_frozen_index(g, peer_number);
691
692
6.07k
    if (frozen_index == -1) {
693
6.03k
        return -1;
694
6.03k
    }
695
696
    /* Now thaw the peer */
697
698
41
    Group_Peer *temp = (Group_Peer *)mem_vrealloc(g_c->mem, g->group, g->numpeers + 1, sizeof(Group_Peer));
699
700
41
    if (temp == nullptr) {
701
0
        return -1;
702
0
    }
703
704
41
    const uint32_t thawed_index = g->numpeers;
705
706
41
    g->group = temp;
707
41
    g->group[thawed_index] = g->frozen[frozen_index];
708
41
    g->group[thawed_index].temp_pk_updated = false;
709
41
    g->group[thawed_index].last_active = mono_time_get(g_c->mono_time);
710
711
41
    add_to_closest(g, g->group[thawed_index].real_pk, g->group[thawed_index].temp_pk);
712
713
41
    ++g->numpeers;
714
715
41
    delete_frozen(g_c->mem, g, frozen_index);
716
717
41
    if (g_c->peer_list_changed_callback != nullptr) {
718
0
        g_c->peer_list_changed_callback(g_c->m, groupnumber, userdata);
719
0
    }
720
721
41
    if (g->peer_on_join != nullptr) {
722
0
        g->peer_on_join(g->object, groupnumber, thawed_index);
723
0
    }
724
725
41
    g->need_send_name = true;
726
727
41
    return thawed_index;
728
41
}
729
730
static bool delpeer(Group_Chats *_Nonnull g_c, uint32_t groupnumber, int peer_index, void *_Nullable userdata);
731
static void delete_any_peer_with_pk(Group_Chats *_Nonnull g_c, uint32_t groupnumber, const uint8_t *_Nonnull real_pk, void *_Nullable userdata)
732
6.03k
{
733
6.03k
    Group_c *g = get_group_c(g_c, groupnumber);
734
6.03k
    if (g == nullptr) {
735
0
        return;
736
0
    }
737
738
6.03k
    const int peer_index = peer_in_group(g, real_pk);
739
740
6.03k
    if (peer_index >= 0) {
741
0
        delpeer(g_c, groupnumber, peer_index, userdata);
742
0
    }
743
744
6.03k
    const int frozen_index = frozen_in_group(g, real_pk);
745
746
6.03k
    if (frozen_index >= 0) {
747
29
        delete_frozen(g_c->mem, g, frozen_index);
748
29
    }
749
6.03k
}
750
751
/** @brief Add a peer to the group chat, or update an existing peer.
752
 *
753
 * fresh indicates whether we should consider this information on the peer to
754
 * be current, and so should update temp_pk and consider the peer active.
755
 *
756
 * do_gc_callback indicates whether we want to trigger callbacks set by the client
757
 * via the public API. This should be set to false if this function is called
758
 * from outside of the `tox_iterate()` loop.
759
 *
760
 * @return peer_index if success or peer already in chat.
761
 * @retval -1 if error.
762
 */
763
static int addpeer(Group_Chats *_Nonnull g_c, uint32_t groupnumber, const uint8_t *_Nonnull real_pk, const uint8_t *_Nonnull temp_pk,
764
                   uint16_t peer_number, void *_Nullable userdata, bool fresh, bool do_gc_callback)
765
6.07k
{
766
6.07k
    Group_c *g = get_group_c(g_c, groupnumber);
767
6.07k
    if (g == nullptr) {
768
0
        return -1;
769
0
    }
770
771
6.07k
    const int peer_index = fresh ?
772
6.07k
                           note_peer_active(g_c, groupnumber, peer_number, userdata) :
773
6.07k
                           get_peer_index(g, peer_number);
774
775
6.07k
    if (peer_index != -1) {
776
41
        if (!pk_equal(g->group[peer_index].real_pk, real_pk)) {
777
7
            LOGGER_ERROR(g_c->m->log, "peer public key is incorrect for peer %d", peer_number);
778
7
            return -1;
779
7
        }
780
781
34
        if (fresh || !g->group[peer_index].temp_pk_updated) {
782
34
            pk_copy(g->group[peer_index].temp_pk, temp_pk);
783
34
            g->group[peer_index].temp_pk_updated = true;
784
34
        }
785
786
34
        return peer_index;
787
41
    }
788
789
6.03k
    if (!fresh) {
790
0
        const int frozen_index = get_frozen_index(g, peer_number);
791
792
0
        if (frozen_index != -1) {
793
0
            if (!pk_equal(g->frozen[frozen_index].real_pk, real_pk)) {
794
0
                return -1;
795
0
            }
796
797
0
            pk_copy(g->frozen[frozen_index].temp_pk, temp_pk);
798
799
0
            return -1;
800
0
        }
801
0
    }
802
803
6.03k
    delete_any_peer_with_pk(g_c, groupnumber, real_pk, userdata);
804
805
6.03k
    Group_Peer *temp = (Group_Peer *)mem_vrealloc(g_c->mem, g->group, g->numpeers + 1, sizeof(Group_Peer));
806
807
6.03k
    if (temp == nullptr) {
808
0
        return -1;
809
0
    }
810
811
6.03k
    temp[g->numpeers] = empty_group_peer;
812
6.03k
    g->group = temp;
813
814
6.03k
    const uint32_t new_index = g->numpeers;
815
816
6.03k
    pk_copy(g->group[new_index].real_pk, real_pk);
817
6.03k
    pk_copy(g->group[new_index].temp_pk, temp_pk);
818
6.03k
    g->group[new_index].temp_pk_updated = true;
819
6.03k
    g->group[new_index].peer_number = peer_number;
820
6.03k
    g->group[new_index].last_active = mono_time_get(g_c->mono_time);
821
6.03k
    g->group[new_index].is_friend = getfriend_id(g_c->m, real_pk) != -1;
822
6.03k
    ++g->numpeers;
823
824
6.03k
    add_to_closest(g, real_pk, temp_pk);
825
826
6.03k
    if (do_gc_callback && g_c->peer_list_changed_callback != nullptr) {
827
0
        g_c->peer_list_changed_callback(g_c->m, groupnumber, userdata);
828
0
    }
829
830
6.03k
    if (g->peer_on_join != nullptr) {
831
0
        g->peer_on_join(g->object, groupnumber, new_index);
832
0
    }
833
834
6.03k
    return new_index;
835
6.03k
}
836
837
static void remove_connection(Group_Chats *_Nonnull g_c, Group_c *_Nonnull g, uint16_t i)
838
0
{
839
0
    if ((g->connections[i].reasons & GROUPCHAT_CONNECTION_REASON_INTRODUCER) != 0) {
840
0
        --g->num_introducer_connections;
841
0
    }
842
843
0
    kill_friend_connection(g_c->fr_c, g->connections[i].number);
844
0
    g->connections[i].type = GROUPCHAT_CONNECTION_NONE;
845
0
}
846
847
static void remove_from_closest(Group_c *_Nonnull g, int peer_index)
848
0
{
849
0
    for (uint32_t i = 0; i < DESIRED_CLOSEST; ++i) {
850
0
        if (g->closest_peers[i].active
851
0
                && pk_equal(g->closest_peers[i].real_pk, g->group[peer_index].real_pk)) {
852
0
            g->closest_peers[i].active = false;
853
0
            g->changed = GROUPCHAT_CLOSEST_CHANGE_REMOVED;
854
0
            break;
855
0
        }
856
0
    }
857
0
}
858
859
/**
860
 * Delete a peer from the group chat.
861
 *
862
 * return true on success
863
 */
864
static bool delpeer(Group_Chats *g_c, uint32_t groupnumber, int peer_index, void *userdata)
865
0
{
866
0
    Group_c *g = get_group_c(g_c, groupnumber);
867
868
0
    if (g == nullptr) {
869
0
        return false;
870
0
    }
871
872
0
    remove_from_closest(g, peer_index);
873
874
0
    const int friendcon_id = getfriend_conn_id_pk(g_c->fr_c, g->group[peer_index].real_pk);
875
876
0
    if (friendcon_id != -1) {
877
0
        for (uint32_t i = 0; i < MAX_GROUP_CONNECTIONS; ++i) {
878
0
            if (g->connections[i].type == GROUPCHAT_CONNECTION_NONE) {
879
0
                continue;
880
0
            }
881
882
0
            if (g->connections[i].number == (unsigned int)friendcon_id) {
883
0
                remove_connection(g_c, g, i);
884
0
                break;
885
0
            }
886
0
        }
887
0
    }
888
889
0
    --g->numpeers;
890
891
0
    void *peer_object = g->group[peer_index].object;
892
893
0
    if (g->numpeers == 0) {
894
0
        mem_delete(g_c->mem, g->group);
895
0
        g->group = nullptr;
896
0
    } else {
897
0
        if (g->numpeers != (uint32_t)peer_index) {
898
0
            g->group[peer_index] = g->group[g->numpeers];
899
0
        }
900
901
0
        Group_Peer *temp = (Group_Peer *)mem_vrealloc(g_c->mem, g->group, g->numpeers, sizeof(Group_Peer));
902
903
0
        if (temp == nullptr) {
904
0
            return false;
905
0
        }
906
907
0
        g->group = temp;
908
0
    }
909
910
0
    if (g_c->peer_list_changed_callback != nullptr) {
911
0
        g_c->peer_list_changed_callback(g_c->m, groupnumber, userdata);
912
0
    }
913
914
0
    if (g->peer_on_leave != nullptr) {
915
0
        g->peer_on_leave(g->object, groupnumber, peer_object);
916
0
    }
917
918
0
    return true;
919
0
}
920
921
/** Order peers with friends first and with more recently active earlier */
922
static bool group_peer_less_handler(const void *_Nonnull object, const void *_Nonnull a, const void *_Nonnull b)
923
0
{
924
0
    const Group_Peer *pa = (const Group_Peer *)a;
925
0
    const Group_Peer *pb = (const Group_Peer *)b;
926
927
0
    if (((pa->is_friend ? 1 : 0) ^ (pb->is_friend ? 1 : 0)) != 0) {
928
0
        return pa->is_friend;
929
0
    }
930
931
0
    return cmp_uint(pb->last_active, pa->last_active) < 0;
932
0
}
933
934
static const void *group_peer_get_handler(const void *_Nonnull arr, uint32_t index)
935
0
{
936
0
    const Group_Peer *entries = (const Group_Peer *)arr;
937
0
    return &entries[index];
938
0
}
939
940
static void group_peer_set_handler(void *_Nonnull arr, uint32_t index, const void *_Nonnull val)
941
0
{
942
0
    Group_Peer *entries = (Group_Peer *)arr;
943
0
    const Group_Peer *entry = (const Group_Peer *)val;
944
0
    entries[index] = *entry;
945
0
}
946
947
static void *group_peer_subarr_handler(void *_Nonnull arr, uint32_t index, uint32_t size)
948
0
{
949
0
    Group_Peer *entries = (Group_Peer *)arr;
950
0
    return &entries[index];
951
0
}
952
953
static void *group_peer_alloc_handler(const void *_Nonnull object, uint32_t size)
954
0
{
955
0
    const Memory *mem = (const Memory *)object;
956
0
    Group_Peer *tmp = (Group_Peer *)mem_valloc(mem, size, sizeof(Group_Peer));
957
958
0
    if (tmp == nullptr) {
959
0
        return nullptr;
960
0
    }
961
962
0
    return tmp;
963
0
}
964
965
static void group_peer_delete_handler(const void *_Nonnull object, void *_Nonnull arr, uint32_t size)
966
0
{
967
0
    const Memory *mem = (const Memory *)object;
968
0
    mem_delete(mem, arr);
969
0
}
970
971
static const Sort_Funcs group_peer_cmp_funcs = {
972
    group_peer_less_handler,
973
    group_peer_get_handler,
974
    group_peer_set_handler,
975
    group_peer_subarr_handler,
976
    group_peer_alloc_handler,
977
    group_peer_delete_handler,
978
};
979
980
/** @brief Delete frozen peers as necessary to ensure at most `g->maxfrozen` remain.
981
 *
982
 * @retval true if any frozen peers are removed.
983
 */
984
static bool delete_old_frozen(Group_c *_Nonnull g, const Memory *_Nonnull mem)
985
0
{
986
0
    if (g->numfrozen <= g->maxfrozen) {
987
0
        return false;
988
0
    }
989
990
0
    if (g->maxfrozen == 0) {
991
0
        mem_delete(mem, g->frozen);
992
0
        g->frozen = nullptr;
993
0
        g->numfrozen = 0;
994
0
        return true;
995
0
    }
996
997
0
    merge_sort(g->frozen, g->numfrozen, mem, &group_peer_cmp_funcs);
998
999
0
    Group_Peer *temp = (Group_Peer *)mem_vrealloc(mem, g->frozen, g->maxfrozen, sizeof(Group_Peer));
1000
1001
0
    if (temp == nullptr) {
1002
0
        return false;
1003
0
    }
1004
1005
0
    g->frozen = temp;
1006
1007
0
    g->numfrozen = g->maxfrozen;
1008
1009
0
    return true;
1010
0
}
1011
1012
static bool try_send_rejoin(Group_Chats *_Nonnull g_c, Group_c *_Nonnull g, const uint8_t *_Nonnull real_pk);
1013
1014
static bool freeze_peer(Group_Chats *_Nonnull g_c, uint32_t groupnumber, int peer_index, void *_Nullable userdata)
1015
0
{
1016
0
    Group_c *g = get_group_c(g_c, groupnumber);
1017
0
    if (g == nullptr) {
1018
0
        return false;
1019
0
    }
1020
1021
0
    Group_Peer *temp = (Group_Peer *)mem_vrealloc(g_c->mem, g->frozen, g->numfrozen + 1, sizeof(Group_Peer));
1022
1023
0
    if (temp == nullptr) {
1024
0
        return false;
1025
0
    }
1026
1027
0
    g->frozen = temp;
1028
0
    g->frozen[g->numfrozen] = g->group[peer_index];
1029
0
    g->frozen[g->numfrozen].object = nullptr;
1030
1031
0
    if (!delpeer(g_c, groupnumber, peer_index, userdata)) {
1032
0
        return false;
1033
0
    }
1034
1035
0
    try_send_rejoin(g_c, g, g->frozen[g->numfrozen].real_pk);
1036
1037
0
    ++g->numfrozen;
1038
1039
0
    delete_old_frozen(g, g_c->mem);
1040
1041
0
    return true;
1042
0
}
1043
1044
/** @brief Set the nick for a peer.
1045
 *
1046
 * do_gc_callback indicates whether we want to trigger callbacks set by the client
1047
 * via the public API. This should be set to false if this function is called
1048
 * from outside of the `tox_iterate()` loop.
1049
 *
1050
 * @retval true on success.
1051
 */
1052
static bool setnick(Group_Chats *_Nonnull g_c, uint32_t groupnumber, int peer_index, const uint8_t *_Nonnull nick, uint16_t nick_len,
1053
                    void *_Nullable userdata, bool do_gc_callback)
1054
6.07k
{
1055
6.07k
    if (nick_len > MAX_NAME_LENGTH) {
1056
0
        return false;
1057
0
    }
1058
1059
6.07k
    Group_c *g = get_group_c(g_c, groupnumber);
1060
1061
6.07k
    if (g == nullptr) {
1062
0
        return false;
1063
0
    }
1064
1065
6.07k
    g->group[peer_index].nick_updated = true;
1066
1067
6.07k
    if (g_peer_nick_eq(&g->group[peer_index], nick, nick_len)) {
1068
        /* same name as already stored */
1069
5.59k
        return true;
1070
5.59k
    }
1071
1072
478
    if (nick_len > 0) {
1073
444
        memcpy(g->group[peer_index].nick, nick, nick_len);
1074
444
    }
1075
1076
478
    g->group[peer_index].nick_len = nick_len;
1077
1078
478
    if (do_gc_callback && g_c->peer_name_callback != nullptr) {
1079
0
        g_c->peer_name_callback(g_c->m, groupnumber, peer_index, nick, nick_len, userdata);
1080
0
    }
1081
1082
478
    return true;
1083
6.07k
}
1084
1085
/** @brief Set the title for a group.
1086
 *
1087
 * @retval true on success.
1088
 */
1089
static bool settitle(Group_Chats *_Nonnull g_c, uint32_t groupnumber, int peer_index, const uint8_t *_Nonnull title, uint8_t title_len,
1090
                     void *_Nullable userdata)
1091
0
{
1092
0
    if (title_len > MAX_NAME_LENGTH || title_len == 0) {
1093
0
        return false;
1094
0
    }
1095
1096
0
    Group_c *g = get_group_c(g_c, groupnumber);
1097
1098
0
    if (g == nullptr) {
1099
0
        return false;
1100
0
    }
1101
1102
0
    if (g_title_eq(g, title, title_len)) {
1103
        /* same title as already set */
1104
0
        return true;
1105
0
    }
1106
1107
0
    memcpy(g->title, title, title_len);
1108
0
    g->title_len = title_len;
1109
1110
0
    g->title_fresh = true;
1111
1112
0
    if (g_c->title_callback != nullptr) {
1113
0
        g_c->title_callback(g_c->m, groupnumber, peer_index, title, title_len, userdata);
1114
0
    }
1115
1116
0
    return true;
1117
0
}
1118
1119
/** Check if the group has no online connection, and freeze all peers if so */
1120
static void check_disconnected(Group_Chats *_Nonnull g_c, uint32_t groupnumber, void *_Nullable userdata)
1121
0
{
1122
0
    const Group_c *g = get_group_c(g_c, groupnumber);
1123
0
    if (g == nullptr) {
1124
0
        return;
1125
0
    }
1126
1127
0
    for (uint32_t i = 0; i < MAX_GROUP_CONNECTIONS; ++i) {
1128
0
        if (g->connections[i].type == GROUPCHAT_CONNECTION_ONLINE) {
1129
0
            return;
1130
0
        }
1131
0
    }
1132
1133
0
    for (uint32_t i = 0; i < g->numpeers; ++i) {
1134
0
        while (i < g->numpeers && !pk_equal(g->group[i].real_pk, g->real_pk)) {
1135
0
            freeze_peer(g_c, groupnumber, i, userdata);
1136
0
        }
1137
0
    }
1138
0
}
1139
1140
static void set_conns_type_connections(Group_Chats *_Nonnull g_c, uint32_t groupnumber, int friendcon_id, uint8_t type,
1141
                                       void *_Nullable userdata)
1142
0
{
1143
0
    Group_c *g = get_group_c(g_c, groupnumber);
1144
0
    if (g == nullptr) {
1145
0
        return;
1146
0
    }
1147
1148
0
    for (uint32_t i = 0; i < MAX_GROUP_CONNECTIONS; ++i) {
1149
0
        if (g->connections[i].type == GROUPCHAT_CONNECTION_NONE) {
1150
0
            continue;
1151
0
        }
1152
1153
0
        if (g->connections[i].number != (unsigned int)friendcon_id) {
1154
0
            continue;
1155
0
        }
1156
1157
0
        if (type == GROUPCHAT_CONNECTION_ONLINE) {
1158
0
            send_packet_online(g_c->fr_c, friendcon_id, groupnumber, g->type, g->id);
1159
0
        } else {
1160
0
            g->connections[i].type = type;
1161
0
            check_disconnected(g_c, groupnumber, userdata);
1162
0
        }
1163
0
    }
1164
0
}
1165
1166
/** Set the type for all connections with friendcon_id */
1167
static void set_conns_status_groups(Group_Chats *_Nonnull g_c, int friendcon_id, uint8_t type, void *_Nullable userdata)
1168
0
{
1169
0
    for (uint16_t i = 0; i < g_c->num_chats; ++i) {
1170
0
        set_conns_type_connections(g_c, i, friendcon_id, type, userdata);
1171
0
    }
1172
0
}
1173
1174
static void rejoin_frozen_friend(Group_Chats *_Nonnull g_c, int friendcon_id)
1175
0
{
1176
0
    uint8_t real_pk[CRYPTO_PUBLIC_KEY_SIZE];
1177
0
    get_friendcon_public_keys(real_pk, nullptr, g_c->fr_c, friendcon_id);
1178
1179
0
    for (uint16_t i = 0; i < g_c->num_chats; ++i) {
1180
0
        Group_c *g = get_group_c(g_c, i);
1181
1182
0
        if (g == nullptr) {
1183
0
            continue;
1184
0
        }
1185
1186
0
        for (uint32_t j = 0; j < g->numfrozen; ++j) {
1187
0
            if (pk_equal(g->frozen[j].real_pk, real_pk)) {
1188
0
                try_send_rejoin(g_c, g, real_pk);
1189
0
                break;
1190
0
            }
1191
0
        }
1192
0
    }
1193
0
}
1194
1195
static int g_handle_any_status(void *_Nonnull object, int friendcon_id, bool status, void *_Nullable userdata)
1196
0
{
1197
0
    Group_Chats *g_c = (Group_Chats *)object;
1198
0
    if (status) {
1199
0
        rejoin_frozen_friend(g_c, friendcon_id);
1200
0
    }
1201
1202
0
    return 0;
1203
0
}
1204
1205
static int g_handle_status(void *_Nonnull object, int friendcon_id, bool status, void *_Nullable userdata)
1206
0
{
1207
0
    Group_Chats *g_c = (Group_Chats *)object;
1208
0
    if (status) { /* Went online */
1209
0
        set_conns_status_groups(g_c, friendcon_id, GROUPCHAT_CONNECTION_ONLINE, userdata);
1210
0
    } else { /* Went offline */
1211
0
        set_conns_status_groups(g_c, friendcon_id, GROUPCHAT_CONNECTION_CONNECTING, userdata);
1212
        // TODO(irungentoo): remove timedout connections?
1213
0
    }
1214
1215
0
    return 0;
1216
0
}
1217
1218
static int g_handle_packet(void *_Nonnull object, int friendcon_id, const uint8_t *_Nonnull data, uint16_t length, void *_Nullable userdata);
1219
static int handle_lossy(void *_Nonnull object, int friendcon_id, const uint8_t *_Nonnull data, uint16_t length, void *_Nullable userdata);
1220
/** @brief Add friend to group chat.
1221
 *
1222
 * @return connections index on success
1223
 * @retval -1 on failure.
1224
 */
1225
static int add_conn_to_groupchat(Group_Chats *g_c, int friendcon_id, Group_c *g, uint8_t reason,
1226
                                 bool lock)
1227
0
{
1228
0
    uint16_t empty = MAX_GROUP_CONNECTIONS;
1229
0
    uint16_t ind = MAX_GROUP_CONNECTIONS;
1230
1231
0
    for (uint16_t i = 0; i < MAX_GROUP_CONNECTIONS; ++i) {
1232
0
        if (g->connections[i].type == GROUPCHAT_CONNECTION_NONE) {
1233
0
            empty = i;
1234
0
            continue;
1235
0
        }
1236
1237
0
        if (g->connections[i].number == (uint32_t)friendcon_id) {
1238
0
            ind = i; /* Already in list. */
1239
0
            break;
1240
0
        }
1241
0
    }
1242
1243
0
    if (ind == MAX_GROUP_CONNECTIONS) {
1244
0
        if (empty == MAX_GROUP_CONNECTIONS) {
1245
0
            return -1;
1246
0
        }
1247
1248
0
        if (lock) {
1249
0
            friend_connection_lock(g_c->fr_c, friendcon_id);
1250
0
        }
1251
1252
0
        g->connections[empty].type = GROUPCHAT_CONNECTION_CONNECTING;
1253
0
        g->connections[empty].number = friendcon_id;
1254
0
        g->connections[empty].reasons = 0;
1255
        // TODO(irungentoo):
1256
0
        friend_connection_callbacks(g_c->m->fr_c, friendcon_id, GROUPCHAT_CALLBACK_INDEX, &g_handle_status, &g_handle_packet,
1257
0
                                    &handle_lossy, g_c, friendcon_id);
1258
0
        ind = empty;
1259
0
    }
1260
1261
0
    if ((g->connections[ind].reasons & reason) == 0) {
1262
0
        g->connections[ind].reasons |= reason;
1263
1264
0
        if (reason == GROUPCHAT_CONNECTION_REASON_INTRODUCER) {
1265
0
            ++g->num_introducer_connections;
1266
0
        }
1267
0
    }
1268
1269
0
    return ind;
1270
0
}
1271
1272
static bool send_peer_introduced(const Group_Chats *_Nonnull g_c, int friendcon_id, uint16_t group_num);
1273
1274
/** @brief Removes reason for keeping connection.
1275
 *
1276
 * Kills connection if this was the last reason.
1277
 */
1278
static void remove_connection_reason(Group_Chats *g_c, Group_c *g, uint16_t i, uint8_t reason)
1279
0
{
1280
0
    if ((g->connections[i].reasons & reason) == 0) {
1281
0
        return;
1282
0
    }
1283
1284
0
    g->connections[i].reasons &= ~reason;
1285
1286
0
    if (reason == GROUPCHAT_CONNECTION_REASON_INTRODUCER) {
1287
0
        --g->num_introducer_connections;
1288
1289
0
        if (g->connections[i].type == GROUPCHAT_CONNECTION_ONLINE) {
1290
0
            send_peer_introduced(g_c, g->connections[i].number, g->connections[i].group_number);
1291
0
        }
1292
0
    }
1293
1294
0
    if (g->connections[i].reasons == 0) {
1295
0
        kill_friend_connection(g_c->fr_c, g->connections[i].number);
1296
0
        g->connections[i].type = GROUPCHAT_CONNECTION_NONE;
1297
0
    }
1298
0
}
1299
1300
/** @brief Creates a new groupchat and puts it in the chats array.
1301
 *
1302
 * @param rng Random number generator used for generating the group ID.
1303
 * @param type is one of `GROUPCHAT_TYPE_*`
1304
 *
1305
 * @return group number on success.
1306
 * @retval -1 on failure.
1307
 */
1308
int add_groupchat(Group_Chats *g_c, const Random *rng, uint8_t type)
1309
0
{
1310
0
    const int32_t groupnumber = create_group_chat(g_c);
1311
1312
0
    if (groupnumber == -1) {
1313
0
        return -1;
1314
0
    }
1315
1316
0
    Group_c *g = &g_c->chats[groupnumber];
1317
1318
0
    g->status = GROUPCHAT_STATUS_CONNECTED;
1319
0
    g->type = type;
1320
0
    new_symmetric_key(rng, g->id);
1321
0
    g->peer_number = 0; /* Founder is peer 0. */
1322
0
    memcpy(g->real_pk, nc_get_self_public_key(g_c->m->net_crypto), CRYPTO_PUBLIC_KEY_SIZE);
1323
0
    const int peer_index = addpeer(g_c, groupnumber, g->real_pk, dht_get_self_public_key(g_c->m->dht), 0, nullptr, true,
1324
0
                                   false);
1325
1326
0
    if (peer_index == -1) {
1327
0
        return -1;
1328
0
    }
1329
1330
0
    setnick(g_c, groupnumber, peer_index, g_c->m->name, g_c->m->name_length, nullptr, false);
1331
1332
0
    return groupnumber;
1333
0
}
1334
1335
static bool group_leave(const Group_Chats *_Nonnull g_c, uint32_t groupnumber, bool permanent);
1336
1337
/** @brief Delete a groupchat from the chats array, informing the group first as
1338
 * appropriate.
1339
 *
1340
 * @retval true on success.
1341
 * @retval false if groupnumber is invalid.
1342
 */
1343
bool del_groupchat(Group_Chats *g_c, uint32_t groupnumber, bool leave_permanently)
1344
2.38k
{
1345
2.38k
    Group_c *g = get_group_c(g_c, groupnumber);
1346
1347
2.38k
    if (g == nullptr) {
1348
0
        return false;
1349
0
    }
1350
1351
2.38k
    group_leave(g_c, groupnumber, leave_permanently);
1352
1353
40.4k
    for (uint32_t i = 0; i < MAX_GROUP_CONNECTIONS; ++i) {
1354
38.0k
        if (g->connections[i].type == GROUPCHAT_CONNECTION_NONE) {
1355
38.0k
            continue;
1356
38.0k
        }
1357
1358
0
        g->connections[i].type = GROUPCHAT_CONNECTION_NONE;
1359
0
        kill_friend_connection(g_c->fr_c, g->connections[i].number);
1360
0
    }
1361
1362
4.76k
    for (uint32_t i = 0; i < g->numpeers; ++i) {
1363
2.38k
        if (g->peer_on_leave != nullptr) {
1364
0
            g->peer_on_leave(g->object, groupnumber, g->group[i].object);
1365
0
        }
1366
2.38k
    }
1367
1368
2.38k
    if (g->group_on_delete != nullptr) {
1369
0
        g->group_on_delete(g->object, groupnumber);
1370
0
    }
1371
1372
2.38k
    return wipe_group_chat(g_c, groupnumber);
1373
2.38k
}
1374
1375
static const Group_Peer *peer_in_list(const Group_c *_Nonnull g, uint32_t peernumber, bool frozen)
1376
0
{
1377
0
    const Group_Peer *list = frozen ? g->frozen : g->group;
1378
0
    const uint32_t num = frozen ? g->numfrozen : g->numpeers;
1379
1380
0
    if (peernumber >= num) {
1381
0
        return nullptr;
1382
0
    }
1383
1384
0
    return &list[peernumber];
1385
0
}
1386
1387
/**
1388
 * @brief Copy the public key of (frozen, if frozen is true) peernumber who is in
1389
 *   groupnumber to pk.
1390
 *
1391
 * @param pk must be CRYPTO_PUBLIC_KEY_SIZE long.
1392
 *
1393
 * @retval 0 on success
1394
 * @retval -1 if groupnumber is invalid.
1395
 * @retval -2 if peernumber is invalid.
1396
 */
1397
int group_peer_pubkey(const Group_Chats *g_c, uint32_t groupnumber, uint32_t peernumber, uint8_t *pk, bool frozen)
1398
0
{
1399
0
    const Group_c *g = get_group_c(g_c, groupnumber);
1400
1401
0
    if (g == nullptr) {
1402
0
        return -1;
1403
0
    }
1404
1405
0
    const Group_Peer *peer = peer_in_list(g, peernumber, frozen);
1406
1407
0
    if (peer == nullptr) {
1408
0
        return -2;
1409
0
    }
1410
1411
0
    memcpy(pk, peer->real_pk, CRYPTO_PUBLIC_KEY_SIZE);
1412
0
    return 0;
1413
0
}
1414
1415
/**
1416
 * @brief Return the size of (frozen, if frozen is true) peernumber's name.
1417
 *
1418
 * @retval -1 if groupnumber is invalid.
1419
 * @retval -2 if peernumber is invalid.
1420
 */
1421
int group_peername_size(const Group_Chats *g_c, uint32_t groupnumber, uint32_t peernumber, bool frozen)
1422
0
{
1423
0
    const Group_c *g = get_group_c(g_c, groupnumber);
1424
1425
0
    if (g == nullptr) {
1426
0
        return -1;
1427
0
    }
1428
1429
0
    const Group_Peer *peer = peer_in_list(g, peernumber, frozen);
1430
1431
0
    if (peer == nullptr) {
1432
0
        return -2;
1433
0
    }
1434
1435
0
    return peer->nick_len;
1436
0
}
1437
1438
/**
1439
 * @brief Copy the name of (frozen, if frozen is true) peernumber who is in
1440
 *   groupnumber to name.
1441
 *
1442
 * @param  name must be at least MAX_NAME_LENGTH long.
1443
 *
1444
 * @return length of name if success
1445
 * @retval -1 if groupnumber is invalid.
1446
 * @retval -2 if peernumber is invalid.
1447
 */
1448
int group_peername(const Group_Chats *g_c, uint32_t groupnumber, uint32_t peernumber, uint8_t *name, bool frozen)
1449
0
{
1450
0
    const Group_c *g = get_group_c(g_c, groupnumber);
1451
1452
0
    if (g == nullptr) {
1453
0
        return -1;
1454
0
    }
1455
1456
0
    const Group_Peer *peer = peer_in_list(g, peernumber, frozen);
1457
1458
0
    if (peer == nullptr) {
1459
0
        return -2;
1460
0
    }
1461
1462
0
    if (peer->nick_len > 0) {
1463
0
        memcpy(name, peer->nick, peer->nick_len);
1464
0
    }
1465
1466
0
    return peer->nick_len;
1467
0
}
1468
1469
/**
1470
 * @brief Copy last active timestamp of frozen peernumber who is in groupnumber to
1471
 *   last_active.
1472
 *
1473
 * @retval 0 on success.
1474
 * @retval -1 if groupnumber is invalid.
1475
 * @retval -2 if peernumber is invalid.
1476
 */
1477
int group_frozen_last_active(const Group_Chats *g_c, uint32_t groupnumber, uint32_t peernumber,
1478
                             uint64_t *last_active)
1479
0
{
1480
0
    const Group_c *g = get_group_c(g_c, groupnumber);
1481
1482
0
    if (g == nullptr) {
1483
0
        return -1;
1484
0
    }
1485
1486
0
    if (peernumber >= g->numfrozen) {
1487
0
        return -2;
1488
0
    }
1489
1490
0
    *last_active = g->frozen[peernumber].last_active;
1491
0
    return 0;
1492
0
}
1493
1494
/** @brief Set maximum number of frozen peers.
1495
 *
1496
 * @retval 0 on success.
1497
 * @retval -1 if groupnumber is invalid.
1498
 */
1499
int group_set_max_frozen(const Group_Chats *g_c, uint32_t groupnumber, uint32_t maxfrozen)
1500
0
{
1501
0
    Group_c *g = get_group_c(g_c, groupnumber);
1502
1503
0
    if (g == nullptr) {
1504
0
        return -1;
1505
0
    }
1506
1507
0
    g->maxfrozen = maxfrozen;
1508
0
    delete_old_frozen(g, g_c->mem);
1509
0
    return 0;
1510
0
}
1511
1512
/**
1513
 * @return the number of (frozen, if frozen is true) peers in the group chat on success.
1514
 * @retval -1 if groupnumber is invalid.
1515
 */
1516
int group_number_peers(const Group_Chats *g_c, uint32_t groupnumber, bool frozen)
1517
0
{
1518
0
    const Group_c *g = get_group_c(g_c, groupnumber);
1519
1520
0
    if (g == nullptr) {
1521
0
        return -1;
1522
0
    }
1523
1524
0
    return frozen ? g->numfrozen : g->numpeers;
1525
0
}
1526
1527
/**
1528
 * @retval 1 if the peernumber corresponds to ours.
1529
 * @retval 0 if the peernumber is not ours.
1530
 * @retval -1 if groupnumber is invalid.
1531
 * @retval -2 if peernumber is invalid.
1532
 * @retval -3 if we are not connected to the group chat.
1533
 */
1534
int group_peernumber_is_ours(const Group_Chats *g_c, uint32_t groupnumber, uint32_t peernumber)
1535
0
{
1536
0
    const Group_c *g = get_group_c(g_c, groupnumber);
1537
1538
0
    if (g == nullptr) {
1539
0
        return -1;
1540
0
    }
1541
1542
0
    if (peernumber >= g->numpeers) {
1543
0
        return -2;
1544
0
    }
1545
1546
0
    if (g->status != GROUPCHAT_STATUS_CONNECTED) {
1547
0
        return -3;
1548
0
    }
1549
1550
0
    return (g->peer_number == g->group[peernumber].peer_number) ? 1 : 0;
1551
0
}
1552
1553
/** @brief return the type of groupchat (GROUPCHAT_TYPE_) that groupnumber is.
1554
 *
1555
 * @retval -1 on failure.
1556
 * @return type on success.
1557
 */
1558
int group_get_type(const Group_Chats *g_c, uint32_t groupnumber)
1559
0
{
1560
0
    const Group_c *g = get_group_c(g_c, groupnumber);
1561
1562
0
    if (g == nullptr) {
1563
0
        return -1;
1564
0
    }
1565
1566
0
    return g->type;
1567
0
}
1568
1569
/** @brief Copies the unique id of `group_chat[groupnumber]` into `id`.
1570
 *
1571
 * @retval false on failure.
1572
 * @retval true on success.
1573
 */
1574
bool conference_get_id(const Group_Chats *g_c, uint32_t groupnumber, uint8_t *id)
1575
0
{
1576
0
    const Group_c *g = get_group_c(g_c, groupnumber);
1577
1578
0
    if (g == nullptr) {
1579
0
        return false;
1580
0
    }
1581
1582
0
    if (id != nullptr) {
1583
0
        memcpy(id, g->id, sizeof(g->id));
1584
0
    }
1585
1586
0
    return true;
1587
0
}
1588
1589
/** @brief Send a group packet to friendcon_id.
1590
 *
1591
 * @retval true on success
1592
 * @retval false on failure
1593
 */
1594
static bool send_packet_group_peer(const Friend_Connections *_Nonnull fr_c, int friendcon_id, uint8_t packet_id, uint16_t group_num, const uint8_t *_Nonnull data, uint16_t length)
1595
0
{
1596
0
    if (1 + sizeof(uint16_t) + length > MAX_CRYPTO_DATA_SIZE) {
1597
0
        return false;
1598
0
    }
1599
1600
0
    group_num = net_htons(group_num);
1601
0
    const uint32_t packet_size = 1 + sizeof(uint16_t) + length;
1602
0
    VLA(uint8_t, packet, packet_size);
1603
0
    packet[0] = packet_id;
1604
0
    memcpy(packet + 1, &group_num, sizeof(uint16_t));
1605
0
    memcpy(packet + 1 + sizeof(uint16_t), data, length);
1606
0
    return write_cryptpacket(friendconn_net_crypto(fr_c), friend_connection_crypt_connection_id(fr_c, friendcon_id),
1607
0
                             packet, packet_size, false) != -1;
1608
0
}
1609
1610
/** @brief Send a group lossy packet to friendcon_id.
1611
 *
1612
 * @retval true on success
1613
 * @retval false on failure
1614
 */
1615
static bool send_lossy_group_peer(const Friend_Connections *_Nonnull fr_c, int friendcon_id, uint8_t packet_id, uint16_t group_num, const uint8_t *_Nonnull data, uint16_t length)
1616
0
{
1617
0
    if (1 + sizeof(uint16_t) + length > MAX_CRYPTO_DATA_SIZE) {
1618
0
        return false;
1619
0
    }
1620
1621
0
    group_num = net_htons(group_num);
1622
0
    const uint32_t packet_size = 1 + sizeof(uint16_t) + length;
1623
0
    VLA(uint8_t, packet, packet_size);
1624
0
    packet[0] = packet_id;
1625
0
    memcpy(packet + 1, &group_num, sizeof(uint16_t));
1626
0
    memcpy(packet + 1 + sizeof(uint16_t), data, length);
1627
0
    return send_lossy_cryptpacket(friendconn_net_crypto(fr_c), friend_connection_crypt_connection_id(fr_c, friendcon_id),
1628
0
                                  packet, packet_size) != -1;
1629
0
}
1630
1631
/** @brief invite friendnumber to groupnumber.
1632
 *
1633
 * @retval 0 on success.
1634
 * @retval -1 if groupnumber is invalid.
1635
 * @retval -2 if invite packet failed to send.
1636
 * @retval -3 if we are not connected to the group chat.
1637
 */
1638
int invite_friend(const Group_Chats *g_c, uint32_t friendnumber, uint32_t groupnumber)
1639
0
{
1640
0
    const Group_c *g = get_group_c(g_c, groupnumber);
1641
1642
0
    if (g == nullptr) {
1643
0
        return -1;
1644
0
    }
1645
1646
0
    if (g->status != GROUPCHAT_STATUS_CONNECTED) {
1647
0
        return -3;
1648
0
    }
1649
1650
0
    uint8_t invite[INVITE_PACKET_SIZE];
1651
0
    invite[0] = INVITE_ID;
1652
0
    const uint16_t groupchat_num = net_htons((uint16_t)groupnumber);
1653
0
    memcpy(invite + 1, &groupchat_num, sizeof(groupchat_num));
1654
0
    invite[1 + sizeof(groupchat_num)] = g->type;
1655
0
    memcpy(invite + 1 + sizeof(groupchat_num) + 1, g->id, GROUP_ID_LENGTH);
1656
1657
0
    if (send_conference_invite_packet(g_c->m, friendnumber, invite, sizeof(invite))) {
1658
0
        return 0;
1659
0
    }
1660
1661
0
    return -2;
1662
0
}
1663
1664
/** @brief Send a rejoin packet to a peer if we have a friend connection to the peer.
1665
 * @retval true if a packet was sent.
1666
 * @retval false otherwise.
1667
 */
1668
static bool try_send_rejoin(Group_Chats *g_c, Group_c *g, const uint8_t *real_pk)
1669
0
{
1670
0
    const int friendcon_id = getfriend_conn_id_pk(g_c->fr_c, real_pk);
1671
1672
0
    if (friendcon_id == -1) {
1673
0
        return false;
1674
0
    }
1675
1676
0
    uint8_t packet[1 + 1 + GROUP_ID_LENGTH];
1677
0
    packet[0] = PACKET_ID_REJOIN_CONFERENCE;
1678
0
    packet[1] = g->type;
1679
0
    memcpy(packet + 2, g->id, GROUP_ID_LENGTH);
1680
1681
0
    if (write_cryptpacket(friendconn_net_crypto(g_c->fr_c), friend_connection_crypt_connection_id(g_c->fr_c, friendcon_id),
1682
0
                          packet, sizeof(packet), false) == -1) {
1683
0
        return false;
1684
0
    }
1685
1686
0
    add_conn_to_groupchat(g_c, friendcon_id, g, GROUPCHAT_CONNECTION_REASON_INTRODUCER, true);
1687
1688
0
    return true;
1689
0
}
1690
1691
static bool send_peer_query(const Group_Chats *_Nonnull g_c, int friendcon_id, uint16_t group_num);
1692
1693
static bool send_invite_response(Group_Chats *_Nonnull g_c, int groupnumber, uint32_t friendnumber, const uint8_t *_Nonnull data, uint16_t length);
1694
1695
/** @brief Join a group (we need to have been invited first).
1696
 *
1697
 * @param expected_type is the groupchat type we expect the chat we are joining
1698
 *   to have.
1699
 *
1700
 * @return group number on success.
1701
 * @retval -1 if data length is invalid.
1702
 * @retval -2 if group is not the expected type.
1703
 * @retval -3 if friendnumber is invalid.
1704
 * @retval -4 if client is already in this group.
1705
 * @retval -5 if group instance failed to initialize.
1706
 * @retval -6 if join packet fails to send.
1707
 */
1708
int join_groupchat(Group_Chats *g_c, uint32_t friendnumber, uint8_t expected_type, const uint8_t *data, uint16_t length)
1709
0
{
1710
0
    if (length != sizeof(uint16_t) + 1 + GROUP_ID_LENGTH) {
1711
0
        return -1;
1712
0
    }
1713
1714
0
    if (data[sizeof(uint16_t)] != expected_type) {
1715
0
        return -2;
1716
0
    }
1717
1718
0
    const int friendcon_id = getfriendcon_id(g_c->m, friendnumber);
1719
1720
0
    if (friendcon_id == -1) {
1721
0
        return -3;
1722
0
    }
1723
1724
0
    if (get_group_num(g_c, data[sizeof(uint16_t)], data + sizeof(uint16_t) + 1) != -1) {
1725
0
        return -4;
1726
0
    }
1727
1728
0
    const int groupnumber = create_group_chat(g_c);
1729
1730
0
    if (groupnumber == -1) {
1731
0
        return -5;
1732
0
    }
1733
1734
0
    Group_c *g = &g_c->chats[groupnumber];
1735
1736
0
    g->status = GROUPCHAT_STATUS_VALID;
1737
0
    memcpy(g->real_pk, nc_get_self_public_key(g_c->m->net_crypto), CRYPTO_PUBLIC_KEY_SIZE);
1738
1739
0
    if (!send_invite_response(g_c, groupnumber, friendnumber, data, length)) {
1740
0
        g->status = GROUPCHAT_STATUS_NONE;
1741
0
        return -6;
1742
0
    }
1743
1744
0
    return groupnumber;
1745
0
}
1746
1747
static bool send_invite_response(Group_Chats *g_c, int groupnumber, uint32_t friendnumber, const uint8_t *data,
1748
                                 uint16_t length)
1749
0
{
1750
0
    Group_c *g = get_group_c(g_c, groupnumber);
1751
1752
0
    if (g == nullptr) {
1753
0
        return false;
1754
0
    }
1755
1756
0
    const bool member = g->status == GROUPCHAT_STATUS_CONNECTED;
1757
1758
0
    const uint32_t response_size = member ? INVITE_MEMBER_PACKET_SIZE : INVITE_ACCEPT_PACKET_SIZE;
1759
0
    VLA(uint8_t, response, response_size);
1760
0
    response[0] = member ? INVITE_MEMBER_ID : INVITE_ACCEPT_ID;
1761
0
    net_pack_u16(response + 1, groupnumber);
1762
0
    memcpy(response + 1 + sizeof(uint16_t), data, length);
1763
1764
0
    if (member) {
1765
0
        net_pack_u16(response + 1 + sizeof(uint16_t) + length, g->peer_number);
1766
0
    }
1767
1768
0
    if (!send_conference_invite_packet(g_c->m, friendnumber, response, response_size)) {
1769
0
        return false;
1770
0
    }
1771
1772
0
    if (!member) {
1773
0
        g->type = data[sizeof(uint16_t)];
1774
0
        memcpy(g->id, data + sizeof(uint16_t) + 1, GROUP_ID_LENGTH);
1775
0
    }
1776
1777
0
    uint16_t other_groupnum;
1778
0
    net_unpack_u16(data, &other_groupnum);
1779
1780
0
    const int friendcon_id = getfriendcon_id(g_c->m, friendnumber);
1781
1782
0
    if (friendcon_id == -1) {
1783
0
        return false;
1784
0
    }
1785
1786
0
    const int connection_index = add_conn_to_groupchat(g_c, friendcon_id, g, GROUPCHAT_CONNECTION_REASON_INTRODUCER, true);
1787
1788
0
    if (member) {
1789
0
        add_conn_to_groupchat(g_c, friendcon_id, g, GROUPCHAT_CONNECTION_REASON_INTRODUCING, false);
1790
0
    }
1791
1792
0
    if (connection_index != -1) {
1793
0
        g->connections[connection_index].group_number = other_groupnum;
1794
0
        g->connections[connection_index].type = GROUPCHAT_CONNECTION_ONLINE;
1795
0
    }
1796
1797
0
    send_peer_query(g_c, friendcon_id, other_groupnum);
1798
1799
0
    return true;
1800
0
}
1801
1802
/** Set handlers for custom lossy packets. */
1803
void group_lossy_packet_registerhandler(Group_Chats *g_c, uint8_t byte, lossy_packet_cb *function)
1804
0
{
1805
0
    g_c->lossy_packethandlers[byte] = function;
1806
0
}
1807
1808
/** Set the callback for group invites. */
1809
void g_callback_group_invite(Group_Chats *g_c, g_conference_invite_cb *function)
1810
1.46k
{
1811
1.46k
    g_c->invite_callback = function;
1812
1.46k
}
1813
1814
/** Set the callback for group connection. */
1815
void g_callback_group_connected(Group_Chats *g_c, g_conference_connected_cb *function)
1816
1.46k
{
1817
1.46k
    g_c->connected_callback = function;
1818
1.46k
}
1819
1820
/** Set the callback for group messages. */
1821
void g_callback_group_message(Group_Chats *g_c, g_conference_message_cb *function)
1822
1.46k
{
1823
1.46k
    g_c->message_callback = function;
1824
1.46k
}
1825
1826
/** @brief Set callback function for peer nickname changes.
1827
 *
1828
 * It gets called every time a peer changes their nickname.
1829
 */
1830
void g_callback_peer_name(Group_Chats *g_c, peer_name_cb *function)
1831
1.46k
{
1832
1.46k
    g_c->peer_name_callback = function;
1833
1.46k
}
1834
1835
/** @brief Set callback function for peer list changes.
1836
 *
1837
 * It gets called every time the name list changes(new peer, deleted peer)
1838
 */
1839
void g_callback_peer_list_changed(Group_Chats *g_c, peer_list_changed_cb *function)
1840
1.46k
{
1841
1.46k
    g_c->peer_list_changed_callback = function;
1842
1.46k
}
1843
1844
/** Set callback function for title changes. */
1845
void g_callback_group_title(Group_Chats *g_c, title_cb *function)
1846
1.46k
{
1847
1.46k
    g_c->title_callback = function;
1848
1.46k
}
1849
1850
/** @brief Set a function to be called when a new peer joins a group chat.
1851
 *
1852
 * @retval 0 on success.
1853
 * @retval -1 on failure.
1854
 */
1855
int callback_groupchat_peer_new(const Group_Chats *g_c, uint32_t groupnumber, peer_on_join_cb *function)
1856
0
{
1857
0
    Group_c *g = get_group_c(g_c, groupnumber);
1858
1859
0
    if (g == nullptr) {
1860
0
        return -1;
1861
0
    }
1862
1863
0
    g->peer_on_join = function;
1864
0
    return 0;
1865
0
}
1866
1867
/** @brief Set a function to be called when a peer leaves a group chat.
1868
 *
1869
 * @retval 0 on success.
1870
 * @retval -1 on failure.
1871
 */
1872
int callback_groupchat_peer_delete(const Group_Chats *g_c, uint32_t groupnumber, peer_on_leave_cb *function)
1873
0
{
1874
0
    Group_c *g = get_group_c(g_c, groupnumber);
1875
1876
0
    if (g == nullptr) {
1877
0
        return -1;
1878
0
    }
1879
1880
0
    g->peer_on_leave = function;
1881
0
    return 0;
1882
0
}
1883
1884
/** @brief Set a function to be called when the group chat is deleted.
1885
 *
1886
 * @retval 0 on success.
1887
 * @retval -1 on failure.
1888
 */
1889
int callback_groupchat_delete(const Group_Chats *g_c, uint32_t groupnumber, group_on_delete_cb *function)
1890
0
{
1891
0
    Group_c *g = get_group_c(g_c, groupnumber);
1892
1893
0
    if (g == nullptr) {
1894
0
        return -1;
1895
0
    }
1896
1897
0
    g->group_on_delete = function;
1898
0
    return 0;
1899
0
}
1900
1901
static int send_message_group(const Group_Chats *_Nonnull g_c, uint32_t groupnumber, uint8_t message_id, const uint8_t *_Nullable data,
1902
                              uint16_t len);
1903
/** @brief send a ping message
1904
 * return true on success
1905
 */
1906
static bool group_ping_send(const Group_Chats *_Nonnull g_c, uint32_t groupnumber)
1907
0
{
1908
0
    return send_message_group(g_c, groupnumber, GROUP_MESSAGE_PING_ID, nullptr, 0) > 0;
1909
0
}
1910
1911
/** @brief send a new_peer message
1912
 * return true on success
1913
 */
1914
static bool group_new_peer_send(const Group_Chats *_Nonnull g_c, uint32_t groupnumber, uint16_t peer_num, const uint8_t *_Nonnull real_pk, const uint8_t *_Nonnull temp_pk)
1915
0
{
1916
0
    uint8_t packet[GROUP_MESSAGE_NEW_PEER_LENGTH];
1917
1918
0
    peer_num = net_htons(peer_num);
1919
0
    memcpy(packet, &peer_num, sizeof(uint16_t));
1920
0
    memcpy(packet + sizeof(uint16_t), real_pk, CRYPTO_PUBLIC_KEY_SIZE);
1921
0
    memcpy(packet + sizeof(uint16_t) + CRYPTO_PUBLIC_KEY_SIZE, temp_pk, CRYPTO_PUBLIC_KEY_SIZE);
1922
1923
0
    return send_message_group(g_c, groupnumber, GROUP_MESSAGE_NEW_PEER_ID, packet, sizeof(packet)) > 0;
1924
0
}
1925
1926
/** @brief send a kill_peer message
1927
 * return true on success
1928
 */
1929
static bool group_kill_peer_send(const Group_Chats *_Nonnull g_c, uint32_t groupnumber, uint16_t peer_num)
1930
0
{
1931
0
    uint8_t packet[GROUP_MESSAGE_KILL_PEER_LENGTH];
1932
1933
0
    peer_num = net_htons(peer_num);
1934
0
    memcpy(packet, &peer_num, sizeof(uint16_t));
1935
1936
0
    return send_message_group(g_c, groupnumber, GROUP_MESSAGE_KILL_PEER_ID, packet, sizeof(packet)) > 0;
1937
0
}
1938
1939
/** @brief send a freeze_peer message
1940
 * return true on success
1941
 */
1942
static bool group_freeze_peer_send(const Group_Chats *_Nonnull g_c, uint32_t groupnumber, uint16_t peer_num)
1943
2.38k
{
1944
2.38k
    uint8_t packet[GROUP_MESSAGE_KILL_PEER_LENGTH];
1945
1946
2.38k
    peer_num = net_htons(peer_num);
1947
2.38k
    memcpy(packet, &peer_num, sizeof(uint16_t));
1948
1949
2.38k
    return send_message_group(g_c, groupnumber, GROUP_MESSAGE_FREEZE_PEER_ID, packet, sizeof(packet)) > 0;
1950
2.38k
}
1951
1952
/** @brief send a name message
1953
 * return true on success
1954
 */
1955
static bool group_name_send(const Group_Chats *_Nonnull g_c, uint32_t groupnumber, const uint8_t *_Nonnull nick, uint16_t nick_len)
1956
0
{
1957
0
    if (nick_len > MAX_NAME_LENGTH) {
1958
0
        return false;
1959
0
    }
1960
1961
0
    return send_message_group(g_c, groupnumber, GROUP_MESSAGE_NAME_ID, nick, nick_len) > 0;
1962
0
}
1963
1964
/** @brief send message to announce leaving group
1965
 * return true on success
1966
 */
1967
static bool group_leave(const Group_Chats *g_c, uint32_t groupnumber, bool permanent)
1968
2.38k
{
1969
2.38k
    const Group_c *g = get_group_c(g_c, groupnumber);
1970
1971
2.38k
    if (g == nullptr) {
1972
0
        return false;
1973
0
    }
1974
1975
2.38k
    if (permanent) {
1976
0
        return group_kill_peer_send(g_c, groupnumber, g->peer_number);
1977
2.38k
    } else {
1978
2.38k
        return group_freeze_peer_send(g_c, groupnumber, g->peer_number);
1979
2.38k
    }
1980
2.38k
}
1981
1982
/** @brief set the group's title, limited to MAX_NAME_LENGTH.
1983
 * @retval 0 on success
1984
 * @retval -1 if groupnumber is invalid.
1985
 * @retval -2 if title is too long or empty.
1986
 * @retval -3 if packet fails to send.
1987
 */
1988
int group_title_send(const Group_Chats *g_c, uint32_t groupnumber, const uint8_t *title, uint8_t title_len)
1989
0
{
1990
0
    Group_c *g = get_group_c(g_c, groupnumber);
1991
1992
0
    if (g == nullptr) {
1993
0
        return -1;
1994
0
    }
1995
1996
0
    if (title_len > MAX_NAME_LENGTH || title_len == 0) {
1997
0
        return -2;
1998
0
    }
1999
2000
    /* same as already set? */
2001
0
    if (g_title_eq(g, title, title_len)) {
2002
0
        return 0;
2003
0
    }
2004
2005
0
    memcpy(g->title, title, title_len);
2006
0
    g->title_len = title_len;
2007
2008
0
    if (g->numpeers == 1) {
2009
0
        return 0;
2010
0
    }
2011
2012
0
    if (send_message_group(g_c, groupnumber, GROUP_MESSAGE_TITLE_ID, title, title_len) > 0) {
2013
0
        return 0;
2014
0
    }
2015
2016
0
    return -3;
2017
0
}
2018
2019
/** @brief return the group's title size.
2020
 * @retval -1 of groupnumber is invalid.
2021
 * @retval -2 if title is too long or empty.
2022
 */
2023
int group_title_get_size(const Group_Chats *g_c, uint32_t groupnumber)
2024
0
{
2025
0
    const Group_c *g = get_group_c(g_c, groupnumber);
2026
2027
0
    if (g == nullptr) {
2028
0
        return -1;
2029
0
    }
2030
2031
0
    if (g->title_len > MAX_NAME_LENGTH || g->title_len == 0) {
2032
0
        return -2;
2033
0
    }
2034
2035
0
    return g->title_len;
2036
0
}
2037
2038
/** @brief Get group title from groupnumber and put it in title.
2039
 *
2040
 * Title needs to be a valid memory location with a size of at least MAX_NAME_LENGTH (128) bytes.
2041
 *
2042
 * @return length of copied title if success.
2043
 * @retval -1 if groupnumber is invalid.
2044
 * @retval -2 if title is too long or empty.
2045
 */
2046
int group_title_get(const Group_Chats *g_c, uint32_t groupnumber, uint8_t *title)
2047
0
{
2048
0
    const Group_c *g = get_group_c(g_c, groupnumber);
2049
2050
0
    if (g == nullptr) {
2051
0
        return -1;
2052
0
    }
2053
2054
0
    if (g->title_len > MAX_NAME_LENGTH || g->title_len == 0) {
2055
0
        return -2;
2056
0
    }
2057
2058
0
    memcpy(title, g->title, g->title_len);
2059
0
    return g->title_len;
2060
0
}
2061
2062
static bool get_peer_number(const Group_c *_Nonnull g, const uint8_t *_Nonnull real_pk, uint16_t *_Nonnull peer_number)
2063
0
{
2064
0
    const int peer_index = peer_in_group(g, real_pk);
2065
2066
0
    if (peer_index >= 0) {
2067
0
        *peer_number = g->group[peer_index].peer_number;
2068
0
        return true;
2069
0
    }
2070
2071
0
    const int frozen_index = frozen_in_group(g, real_pk);
2072
2073
0
    if (frozen_index >= 0) {
2074
0
        *peer_number = g->frozen[frozen_index].peer_number;
2075
0
        return true;
2076
0
    }
2077
2078
0
    return false;
2079
0
}
2080
2081
static void handle_friend_invite_packet(Messenger *_Nonnull m, uint32_t friend_number, const uint8_t *_Nonnull cookie, uint16_t length,
2082
                                        void *_Nullable user_data)
2083
0
{
2084
0
    Group_Chats *g_c = m->conferences_object;
2085
0
    if (length <= 1) {
2086
0
        return;
2087
0
    }
2088
2089
0
    switch (cookie[0]) {
2090
0
        case INVITE_ID: {
2091
0
            if (length != INVITE_PACKET_SIZE) {
2092
0
                return;
2093
0
            }
2094
2095
0
            const int groupnumber = get_group_num(g_c, cookie[1 + sizeof(uint16_t)], cookie + 1 + sizeof(uint16_t) + 1);
2096
2097
0
            const uint8_t *invite_data = cookie + 1;
2098
0
            const uint16_t invite_length = length - 1;
2099
2100
0
            if (groupnumber == -1) {
2101
0
                if (g_c->invite_callback != nullptr) {
2102
0
                    g_c->invite_callback(m, friend_number, invite_data[sizeof(uint16_t)], invite_data, invite_length, user_data);
2103
0
                }
2104
2105
0
                return;
2106
0
            } else {
2107
0
                const Group_c *g = get_group_c(g_c, groupnumber);
2108
2109
0
                if (g != nullptr && g->status == GROUPCHAT_STATUS_CONNECTED) {
2110
0
                    send_invite_response(g_c, groupnumber, friend_number, invite_data, invite_length);
2111
0
                }
2112
0
            }
2113
2114
0
            break;
2115
0
        }
2116
2117
0
        case INVITE_ACCEPT_ID:
2118
0
        case INVITE_MEMBER_ID: {
2119
0
            const bool member = cookie[0] == INVITE_MEMBER_ID;
2120
2121
0
            if (length != (member ? INVITE_MEMBER_PACKET_SIZE : INVITE_ACCEPT_PACKET_SIZE)) {
2122
0
                return;
2123
0
            }
2124
2125
0
            uint16_t other_groupnum;
2126
0
            uint16_t groupnum;
2127
0
            net_unpack_u16(cookie + 1, &other_groupnum);
2128
0
            net_unpack_u16(cookie + 1 + sizeof(uint16_t), &groupnum);
2129
2130
0
            Group_c *g = get_group_c(g_c, groupnum);
2131
2132
0
            if (g == nullptr) {
2133
0
                return;
2134
0
            }
2135
2136
0
            if (cookie[1 + sizeof(uint16_t) * 2] != g->type) {
2137
0
                return;
2138
0
            }
2139
2140
0
            if (!group_id_eq(cookie + 1 + sizeof(uint16_t) * 2 + 1, g->id)) {
2141
0
                return;
2142
0
            }
2143
2144
0
            uint16_t peer_number;
2145
2146
0
            if (member) {
2147
0
                net_unpack_u16(cookie + 1 + sizeof(uint16_t) * 2 + 1 + GROUP_ID_LENGTH, &peer_number);
2148
0
            } else {
2149
                /* TODO(irungentoo): what if two people enter the group at the
2150
                 * same time and are given the same peer_number by different
2151
                 * nodes? */
2152
0
                peer_number = random_u16(m->rng);
2153
2154
0
                unsigned int tries = 0;
2155
2156
0
                while (get_peer_index(g, peer_number) != -1 || get_frozen_index(g, peer_number) != -1) {
2157
0
                    peer_number = random_u16(m->rng);
2158
0
                    ++tries;
2159
2160
0
                    if (tries > 32) {
2161
0
                        return;
2162
0
                    }
2163
0
                }
2164
0
            }
2165
2166
0
            const int friendcon_id = getfriendcon_id(m, friend_number);
2167
2168
0
            if (friendcon_id == -1) {
2169
                // TODO(iphydf): Log something?
2170
0
                return;
2171
0
            }
2172
2173
0
            uint8_t real_pk[CRYPTO_PUBLIC_KEY_SIZE];
2174
0
            uint8_t temp_pk[CRYPTO_PUBLIC_KEY_SIZE];
2175
0
            get_friendcon_public_keys(real_pk, temp_pk, g_c->fr_c, friendcon_id);
2176
2177
0
            addpeer(g_c, groupnum, real_pk, temp_pk, peer_number, user_data, true, true);
2178
0
            const int connection_index = add_conn_to_groupchat(g_c, friendcon_id, g,
2179
0
                                         GROUPCHAT_CONNECTION_REASON_INTRODUCING, true);
2180
2181
0
            if (member) {
2182
0
                add_conn_to_groupchat(g_c, friendcon_id, g, GROUPCHAT_CONNECTION_REASON_INTRODUCER, false);
2183
0
                send_peer_query(g_c, friendcon_id, other_groupnum);
2184
0
            }
2185
2186
0
            if (connection_index != -1) {
2187
0
                g->connections[connection_index].group_number = other_groupnum;
2188
0
                g->connections[connection_index].type = GROUPCHAT_CONNECTION_ONLINE;
2189
0
            }
2190
2191
0
            group_new_peer_send(g_c, groupnum, peer_number, real_pk, temp_pk);
2192
2193
0
            break;
2194
0
        }
2195
2196
0
        default: {
2197
0
            return;
2198
0
        }
2199
0
    }
2200
0
}
2201
2202
/** @brief Find index of friend in the connections list.
2203
 *
2204
 * return index on success
2205
 * return -1 on failure.
2206
 */
2207
static int friend_in_connections(const Group_c *_Nonnull g, int friendcon_id)
2208
0
{
2209
0
    for (unsigned int i = 0; i < MAX_GROUP_CONNECTIONS; ++i) {
2210
0
        if (g->connections[i].type == GROUPCHAT_CONNECTION_NONE) {
2211
0
            continue;
2212
0
        }
2213
2214
0
        if (g->connections[i].number == (uint32_t)friendcon_id) {
2215
0
            return i;
2216
0
        }
2217
0
    }
2218
2219
0
    return -1;
2220
0
}
2221
2222
/** return number of connections. */
2223
static unsigned int count_connected(const Group_c *_Nonnull g)
2224
2.38k
{
2225
2.38k
    unsigned int count = 0;
2226
2227
40.4k
    for (unsigned int i = 0; i < MAX_GROUP_CONNECTIONS; ++i) {
2228
38.0k
        if (g->connections[i].type == GROUPCHAT_CONNECTION_ONLINE) {
2229
0
            ++count;
2230
0
        }
2231
38.0k
    }
2232
2233
2.38k
    return count;
2234
2.38k
}
2235
2236
static bool send_packet_online(const Friend_Connections *fr_c, int friendcon_id, uint16_t group_num,
2237
                               uint8_t type, const uint8_t *id)
2238
0
{
2239
0
    uint8_t packet[1 + ONLINE_PACKET_DATA_SIZE];
2240
0
    group_num = net_htons(group_num);
2241
0
    packet[0] = PACKET_ID_ONLINE_PACKET;
2242
0
    memcpy(packet + 1, &group_num, sizeof(uint16_t));
2243
0
    packet[1 + sizeof(uint16_t)] = type;
2244
0
    memcpy(packet + 1 + sizeof(uint16_t) + 1, id, GROUP_ID_LENGTH);
2245
0
    return write_cryptpacket(friendconn_net_crypto(fr_c), friend_connection_crypt_connection_id(fr_c, friendcon_id), packet,
2246
0
                             sizeof(packet), false) != -1;
2247
0
}
2248
2249
static bool ping_groupchat(const Group_Chats *_Nonnull g_c, uint32_t groupnumber);
2250
2251
static int handle_packet_online(const Group_Chats *_Nonnull g_c, int friendcon_id, const uint8_t *_Nonnull data, uint16_t length)
2252
0
{
2253
0
    if (length != ONLINE_PACKET_DATA_SIZE) {
2254
0
        return -1;
2255
0
    }
2256
2257
0
    const int groupnumber = get_group_num(g_c, data[sizeof(uint16_t)], data + sizeof(uint16_t) + 1);
2258
2259
0
    if (groupnumber == -1) {
2260
0
        return -1;
2261
0
    }
2262
2263
0
    uint16_t other_groupnum;
2264
0
    memcpy(&other_groupnum, data, sizeof(uint16_t));
2265
0
    other_groupnum = net_ntohs(other_groupnum);
2266
2267
0
    Group_c *g = get_group_c(g_c, groupnumber);
2268
2269
0
    if (g == nullptr) {
2270
0
        return -1;
2271
0
    }
2272
2273
0
    const int index = friend_in_connections(g, friendcon_id);
2274
2275
0
    if (index == -1) {
2276
0
        return -1;
2277
0
    }
2278
2279
0
    if (g->connections[index].type == GROUPCHAT_CONNECTION_ONLINE) {
2280
0
        return -1;
2281
0
    }
2282
2283
0
    if (count_connected(g) == 0 || (g->connections[index].reasons & GROUPCHAT_CONNECTION_REASON_INTRODUCER) != 0) {
2284
0
        send_peer_query(g_c, friendcon_id, other_groupnum);
2285
0
    }
2286
2287
0
    g->connections[index].group_number = other_groupnum;
2288
0
    g->connections[index].type = GROUPCHAT_CONNECTION_ONLINE;
2289
0
    send_packet_online(g_c->fr_c, friendcon_id, groupnumber, g->type, g->id);
2290
2291
0
    if ((g->connections[index].reasons & GROUPCHAT_CONNECTION_REASON_INTRODUCING) != 0) {
2292
0
        uint8_t real_pk[CRYPTO_PUBLIC_KEY_SIZE];
2293
0
        uint8_t temp_pk[CRYPTO_PUBLIC_KEY_SIZE];
2294
0
        get_friendcon_public_keys(real_pk, temp_pk, g_c->fr_c, friendcon_id);
2295
2296
0
        const int peer_index = peer_in_group(g, real_pk);
2297
2298
0
        if (peer_index != -1) {
2299
0
            group_new_peer_send(g_c, groupnumber, g->group[peer_index].peer_number, real_pk, temp_pk);
2300
0
        }
2301
2302
0
        g->need_send_name = true;
2303
0
    }
2304
2305
0
    ping_groupchat(g_c, groupnumber);
2306
2307
0
    return 0;
2308
0
}
2309
2310
static int handle_packet_rejoin(Group_Chats *_Nonnull g_c, int friendcon_id, const uint8_t *_Nonnull data, uint16_t length,
2311
                                void *_Nullable userdata)
2312
0
{
2313
0
    if (length < 1 + GROUP_ID_LENGTH) {
2314
0
        return -1;
2315
0
    }
2316
2317
0
    const int32_t groupnum = get_group_num(g_c, *data, data + 1);
2318
2319
0
    Group_c *g = get_group_c(g_c, groupnum);
2320
2321
0
    if (g == nullptr) {
2322
0
        return -1;
2323
0
    }
2324
2325
0
    uint8_t real_pk[CRYPTO_PUBLIC_KEY_SIZE];
2326
0
    uint8_t temp_pk[CRYPTO_PUBLIC_KEY_SIZE];
2327
0
    get_friendcon_public_keys(real_pk, temp_pk, g_c->fr_c, friendcon_id);
2328
2329
0
    uint16_t peer_number;
2330
2331
0
    if (!get_peer_number(g, real_pk, &peer_number)) {
2332
0
        return -1;
2333
0
    }
2334
2335
0
    addpeer(g_c, groupnum, real_pk, temp_pk, peer_number, userdata, true, true);
2336
0
    const int connection_index = add_conn_to_groupchat(g_c, friendcon_id, g,
2337
0
                                 GROUPCHAT_CONNECTION_REASON_INTRODUCING, true);
2338
2339
0
    if (connection_index != -1) {
2340
0
        send_packet_online(g_c->fr_c, friendcon_id, groupnum, g->type, g->id);
2341
0
    }
2342
2343
0
    return 0;
2344
0
}
2345
2346
// we could send title with invite, but then if it changes between sending and accepting inv, joinee won't see it
2347
2348
/**
2349
 * @retval true on success.
2350
 * @retval false on failure
2351
 */
2352
static bool send_peer_introduced(const Group_Chats *g_c, int friendcon_id, uint16_t group_num)
2353
0
{
2354
0
    uint8_t packet[1];
2355
0
    packet[0] = PEER_INTRODUCED_ID;
2356
0
    return send_packet_group_peer(g_c->fr_c, friendcon_id, PACKET_ID_DIRECT_CONFERENCE, group_num, packet, sizeof(packet));
2357
0
}
2358
2359
/**
2360
 * @retval true on success.
2361
 * @retval false on failure
2362
 */
2363
static bool send_peer_query(const Group_Chats *g_c, int friendcon_id, uint16_t group_num)
2364
0
{
2365
0
    uint8_t packet[1];
2366
0
    packet[0] = PEER_QUERY_ID;
2367
0
    return send_packet_group_peer(g_c->fr_c, friendcon_id, PACKET_ID_DIRECT_CONFERENCE, group_num, packet, sizeof(packet));
2368
0
}
2369
2370
/**
2371
 * @return number of peers sent on success.
2372
 * @retval 0 on failure.
2373
 */
2374
static unsigned int send_peers(const Group_Chats *_Nonnull g_c, const Group_c *_Nonnull g, int friendcon_id, uint16_t group_num)
2375
0
{
2376
0
    uint8_t response_packet[MAX_CRYPTO_DATA_SIZE - (1 + sizeof(uint16_t))];
2377
0
    response_packet[0] = PEER_RESPONSE_ID;
2378
0
    uint8_t *p = response_packet + 1;
2379
2380
0
    uint16_t sent = 0;
2381
2382
0
    for (uint32_t i = 0; i <= g->numpeers; ++i) {
2383
0
        if (i == g->numpeers
2384
0
                || (p - response_packet) + sizeof(uint16_t) + CRYPTO_PUBLIC_KEY_SIZE * 2 + 1 + g->group[i].nick_len >
2385
0
                sizeof(response_packet)) {
2386
0
            if (send_packet_group_peer(g_c->fr_c, friendcon_id, PACKET_ID_DIRECT_CONFERENCE, group_num, response_packet,
2387
0
                                       p - response_packet)) {
2388
0
                sent = i;
2389
0
            } else {
2390
0
                return sent;
2391
0
            }
2392
2393
0
            if (i == g->numpeers) {
2394
0
                break;
2395
0
            }
2396
2397
0
            p = response_packet + 1;
2398
0
        }
2399
2400
0
        const uint16_t peer_num = net_htons(g->group[i].peer_number);
2401
0
        memcpy(p, &peer_num, sizeof(peer_num));
2402
0
        p += sizeof(peer_num);
2403
0
        memcpy(p, g->group[i].real_pk, CRYPTO_PUBLIC_KEY_SIZE);
2404
0
        p += CRYPTO_PUBLIC_KEY_SIZE;
2405
0
        memcpy(p, g->group[i].temp_pk, CRYPTO_PUBLIC_KEY_SIZE);
2406
0
        p += CRYPTO_PUBLIC_KEY_SIZE;
2407
0
        *p = g->group[i].nick_len;
2408
0
        p += 1;
2409
0
        memcpy(p, g->group[i].nick, g->group[i].nick_len);
2410
0
        p += g->group[i].nick_len;
2411
0
    }
2412
2413
0
    if (g->title_len > 0) {
2414
0
        const uint32_t title_packet_size = 1 + g->title_len;
2415
0
        VLA(uint8_t, title_packet, title_packet_size);
2416
0
        title_packet[0] = PEER_TITLE_ID;
2417
0
        memcpy(title_packet + 1, g->title, g->title_len);
2418
0
        send_packet_group_peer(g_c->fr_c, friendcon_id, PACKET_ID_DIRECT_CONFERENCE, group_num,
2419
0
                               title_packet, title_packet_size);
2420
0
    }
2421
2422
0
    return sent;
2423
0
}
2424
2425
static int handle_send_peers(Group_Chats *_Nonnull g_c, uint32_t groupnumber, const uint8_t *_Nonnull data, uint16_t length,
2426
                             void *_Nullable userdata)
2427
0
{
2428
0
    if (length == 0) {
2429
0
        return -1;
2430
0
    }
2431
2432
0
    Group_c *g = get_group_c(g_c, groupnumber);
2433
2434
0
    if (g == nullptr) {
2435
0
        return -1;
2436
0
    }
2437
2438
0
    const uint8_t *d = data;
2439
2440
0
    while ((unsigned int)(length - (d - data)) >= sizeof(uint16_t) + CRYPTO_PUBLIC_KEY_SIZE * 2 + 1) {
2441
0
        uint16_t peer_num;
2442
0
        memcpy(&peer_num, d, sizeof(peer_num));
2443
0
        peer_num = net_ntohs(peer_num);
2444
0
        d += sizeof(uint16_t);
2445
2446
0
        if (g->status == GROUPCHAT_STATUS_VALID
2447
0
                && pk_equal(d, nc_get_self_public_key(g_c->m->net_crypto))) {
2448
0
            g->peer_number = peer_num;
2449
0
            g->status = GROUPCHAT_STATUS_CONNECTED;
2450
2451
0
            if (g_c->connected_callback != nullptr) {
2452
0
                g_c->connected_callback(g_c->m, groupnumber, userdata);
2453
0
            }
2454
2455
0
            g->need_send_name = true;
2456
0
        }
2457
2458
0
        const int peer_index = addpeer(g_c, groupnumber, d, d + CRYPTO_PUBLIC_KEY_SIZE, peer_num, userdata, false, true);
2459
2460
0
        if (peer_index == -1) {
2461
0
            return -1;
2462
0
        }
2463
2464
0
        d += CRYPTO_PUBLIC_KEY_SIZE * 2;
2465
0
        const uint8_t name_length = *d;
2466
0
        d += 1;
2467
2468
0
        if (name_length > (length - (d - data)) || name_length > MAX_NAME_LENGTH) {
2469
0
            return -1;
2470
0
        }
2471
2472
0
        if (!g->group[peer_index].nick_updated) {
2473
0
            setnick(g_c, groupnumber, peer_index, d, name_length, userdata, true);
2474
0
        }
2475
2476
0
        d += name_length;
2477
0
    }
2478
2479
0
    return 0;
2480
0
}
2481
2482
static void handle_direct_packet(Group_Chats *_Nonnull g_c, uint32_t groupnumber, const uint8_t *_Nonnull data, uint16_t length,
2483
                                 uint32_t connection_index, void *_Nullable userdata)
2484
0
{
2485
0
    if (length == 0) {
2486
0
        return;
2487
0
    }
2488
2489
0
    Group_c *g = get_group_c(g_c, groupnumber);
2490
2491
0
    if (g == nullptr) {
2492
0
        return;
2493
0
    }
2494
2495
0
    switch (data[0]) {
2496
0
        case PEER_INTRODUCED_ID: {
2497
0
            remove_connection_reason(g_c, g, connection_index, GROUPCHAT_CONNECTION_REASON_INTRODUCING);
2498
0
            break;
2499
0
        }
2500
2501
0
        case PEER_QUERY_ID: {
2502
0
            if (g->connections[connection_index].type != GROUPCHAT_CONNECTION_ONLINE) {
2503
0
                return;
2504
0
            }
2505
2506
0
            send_peers(g_c, g, g->connections[connection_index].number, g->connections[connection_index].group_number);
2507
0
            break;
2508
0
        }
2509
2510
0
        case PEER_RESPONSE_ID: {
2511
0
            handle_send_peers(g_c, groupnumber, data + 1, length - 1, userdata);
2512
0
            break;
2513
0
        }
2514
2515
0
        case PEER_TITLE_ID: {
2516
0
            if (!g->title_fresh) {
2517
0
                settitle(g_c, groupnumber, -1, data + 1, length - 1, userdata);
2518
0
            }
2519
2520
0
            break;
2521
0
        }
2522
0
    }
2523
0
}
2524
2525
/** @brief Send message to all connections except receiver (if receiver isn't -1)
2526
 *
2527
 * NOTE: this function appends the group chat number to the data passed to it.
2528
 *
2529
 * @return number of messages sent.
2530
 */
2531
static unsigned int send_message_all_connections(const Group_Chats *_Nonnull g_c, const Group_c *_Nonnull g, const uint8_t *_Nonnull data, uint16_t length, int receiver)
2532
0
{
2533
0
    uint16_t sent = 0;
2534
2535
0
    for (uint16_t i = 0; i < MAX_GROUP_CONNECTIONS; ++i) {
2536
0
        if (g->connections[i].type != GROUPCHAT_CONNECTION_ONLINE) {
2537
0
            continue;
2538
0
        }
2539
2540
0
        if ((int)i == receiver) {
2541
0
            continue;
2542
0
        }
2543
2544
0
        if (send_packet_group_peer(g_c->fr_c, g->connections[i].number, PACKET_ID_MESSAGE_CONFERENCE,
2545
0
                                   g->connections[i].group_number, data, length)) {
2546
0
            ++sent;
2547
0
        }
2548
0
    }
2549
2550
0
    return sent;
2551
0
}
2552
2553
/** @brief Send lossy message to all connections except receiver (if receiver isn't -1)
2554
 *
2555
 * NOTE: this function appends the group chat number to the data passed to it.
2556
 *
2557
 * @return number of messages sent.
2558
 */
2559
static unsigned int send_lossy_all_connections(const Group_Chats *_Nonnull g_c, const Group_c *_Nonnull g, const uint8_t *_Nonnull data, uint16_t length, int receiver)
2560
0
{
2561
0
    unsigned int sent = 0;
2562
0
    unsigned int num_connected_closest = 0;
2563
0
    unsigned int connected_closest[DESIRED_CLOSEST] = {0};
2564
2565
0
    for (unsigned int i = 0; i < MAX_GROUP_CONNECTIONS; ++i) {
2566
0
        if (g->connections[i].type != GROUPCHAT_CONNECTION_ONLINE) {
2567
0
            continue;
2568
0
        }
2569
2570
0
        if ((int)i == receiver) {
2571
0
            continue;
2572
0
        }
2573
2574
0
        if ((g->connections[i].reasons & GROUPCHAT_CONNECTION_REASON_CLOSEST) != 0) {
2575
0
            connected_closest[num_connected_closest] = i;
2576
0
            ++num_connected_closest;
2577
0
            continue;
2578
0
        }
2579
2580
0
        if (send_lossy_group_peer(g_c->fr_c, g->connections[i].number, PACKET_ID_LOSSY_CONFERENCE,
2581
0
                                  g->connections[i].group_number, data, length)) {
2582
0
            ++sent;
2583
0
        }
2584
0
    }
2585
2586
0
    if (num_connected_closest == 0) {
2587
0
        return sent;
2588
0
    }
2589
2590
0
    unsigned int to_send[2] = {0, 0};
2591
0
    uint64_t comp_val_old[2] = {(uint64_t) -1, (uint64_t) -1};
2592
2593
0
    for (unsigned int i = 0; i < num_connected_closest; ++i) {
2594
0
        uint8_t real_pk[CRYPTO_PUBLIC_KEY_SIZE] = {0};
2595
0
        get_friendcon_public_keys(real_pk, nullptr, g_c->fr_c, g->connections[connected_closest[i]].number);
2596
0
        const uint64_t comp_val = calculate_comp_value(g->real_pk, real_pk);
2597
2598
0
        for (uint8_t j = 0; j < 2; ++j) {
2599
0
            if (j > 0 ? (comp_val > comp_val_old[j]) : (comp_val < comp_val_old[j])) {
2600
0
                to_send[j] = connected_closest[i];
2601
0
                comp_val_old[j] = comp_val;
2602
0
            }
2603
0
        }
2604
0
    }
2605
2606
0
    for (uint8_t j = 0; j < 2; ++j) {
2607
0
        if (j > 0 && to_send[1] == to_send[0]) {
2608
0
            break;
2609
0
        }
2610
2611
0
        if (send_lossy_group_peer(g_c->fr_c, g->connections[to_send[j]].number, PACKET_ID_LOSSY_CONFERENCE,
2612
0
                                  g->connections[to_send[j]].group_number, data, length)) {
2613
0
            ++sent;
2614
0
        }
2615
0
    }
2616
2617
0
    return sent;
2618
0
}
2619
2620
/** @brief Send data of len with message_id to groupnumber.
2621
 *
2622
 * @return number of peers it was sent to on success.
2623
 * @retval -1 if groupnumber is invalid.
2624
 * @retval -2 if message is too long.
2625
 * @retval -3 if we are not connected to the group.
2626
 * @retval -4 if message failed to send.
2627
 */
2628
static int send_message_group(const Group_Chats *g_c, uint32_t groupnumber, uint8_t message_id, const uint8_t *data,
2629
                              uint16_t len)
2630
2.38k
{
2631
2.38k
    assert(len == 0 || data != nullptr);
2632
2.38k
    Group_c *g = get_group_c(g_c, groupnumber);
2633
2634
2.38k
    if (g == nullptr) {
2635
0
        return -1;
2636
0
    }
2637
2638
2.38k
    if (len > MAX_GROUP_MESSAGE_DATA_LEN) {
2639
0
        return -2;
2640
0
    }
2641
2642
2.38k
    if (g->status != GROUPCHAT_STATUS_CONNECTED || count_connected(g) == 0) {
2643
2.38k
        return -3;
2644
2.38k
    }
2645
2646
0
    const uint16_t packet_size = sizeof(uint16_t) + sizeof(uint32_t) + 1 + len;
2647
0
    VLA(uint8_t, packet, packet_size);
2648
0
    const uint16_t peer_num = net_htons(g->peer_number);
2649
0
    memcpy(packet, &peer_num, sizeof(peer_num));
2650
2651
0
    ++g->message_number;
2652
2653
0
    if (g->message_number == 0) {
2654
0
        ++g->message_number;
2655
0
    }
2656
2657
0
    const uint32_t message_num = net_htonl(g->message_number);
2658
0
    memcpy(packet + sizeof(uint16_t), &message_num, sizeof(message_num));
2659
2660
0
    packet[sizeof(uint16_t) + sizeof(uint32_t)] = message_id;
2661
2662
0
    if (len != 0) {
2663
0
        memcpy(packet + sizeof(uint16_t) + sizeof(uint32_t) + 1, data, len);
2664
0
    }
2665
2666
0
    const unsigned int ret = send_message_all_connections(g_c, g, packet, packet_size, -1);
2667
2668
0
    if (ret == 0) {
2669
0
        return -4;
2670
0
    }
2671
2672
0
    return ret;
2673
0
}
2674
2675
/** @brief send a group message
2676
 * @retval 0 on success
2677
 * @see send_message_group for error codes.
2678
 */
2679
int group_message_send(const Group_Chats *g_c, uint32_t groupnumber, const uint8_t *message, uint16_t length)
2680
0
{
2681
0
    const int ret = send_message_group(g_c, groupnumber, PACKET_ID_MESSAGE, message, length);
2682
2683
0
    if (ret > 0) {
2684
0
        return 0;
2685
0
    }
2686
2687
0
    return ret;
2688
0
}
2689
2690
/** @brief send a group action
2691
 * @retval 0 on success
2692
 * @see send_message_group for error codes.
2693
 */
2694
int group_action_send(const Group_Chats *g_c, uint32_t groupnumber, const uint8_t *action, uint16_t length)
2695
0
{
2696
0
    const int ret = send_message_group(g_c, groupnumber, PACKET_ID_ACTION, action, length);
2697
2698
0
    if (ret > 0) {
2699
0
        return 0;
2700
0
    }
2701
2702
0
    return ret;
2703
0
}
2704
2705
/** @brief High level function to send custom lossy packets.
2706
 *
2707
 * @retval -1 on failure.
2708
 * @retval 0 on success.
2709
 */
2710
int send_group_lossy_packet(const Group_Chats *g_c, uint32_t groupnumber, const uint8_t *data, uint16_t length)
2711
0
{
2712
    // TODO(irungentoo): length check here?
2713
0
    Group_c *g = get_group_c(g_c, groupnumber);
2714
2715
0
    if (g == nullptr) {
2716
0
        return -1;
2717
0
    }
2718
2719
0
    const uint16_t packet_size = sizeof(uint16_t) * 2 + length;
2720
0
    VLA(uint8_t, packet, packet_size);
2721
0
    const uint16_t peer_number = net_htons(g->peer_number);
2722
0
    memcpy(packet, &peer_number, sizeof(uint16_t));
2723
0
    const uint16_t message_num = net_htons(g->lossy_message_number);
2724
0
    memcpy(packet + sizeof(uint16_t), &message_num, sizeof(uint16_t));
2725
0
    memcpy(packet + sizeof(uint16_t) * 2, data, length);
2726
2727
0
    if (send_lossy_all_connections(g_c, g, packet, packet_size, -1) == 0) {
2728
0
        return -1;
2729
0
    }
2730
2731
0
    ++g->lossy_message_number;
2732
0
    return 0;
2733
0
}
2734
2735
static Message_Info *find_message_slot_or_reject(uint32_t message_number, uint8_t message_id, Group_Peer *_Nonnull peer)
2736
0
{
2737
0
    const bool ignore_older = message_id == GROUP_MESSAGE_NAME_ID || message_id == GROUP_MESSAGE_TITLE_ID;
2738
2739
0
    Message_Info *i;
2740
2741
0
    for (i = peer->last_message_infos; i < peer->last_message_infos + peer->num_last_message_infos; ++i) {
2742
0
        if (message_number - (i->message_number + 1) <= ((uint32_t)1 << 31)) {
2743
0
            break;
2744
0
        }
2745
2746
0
        if (message_number == i->message_number) {
2747
0
            return nullptr;
2748
0
        }
2749
2750
0
        if (ignore_older && message_id == i->message_id) {
2751
0
            return nullptr;
2752
0
        }
2753
0
    }
2754
2755
0
    return i;
2756
0
}
2757
2758
/** @brief Stores message info in `peer->last_message_infos`.
2759
 *
2760
 * @retval true if message should be processed.
2761
 * @retval false otherwise.
2762
 */
2763
static bool check_message_info(uint32_t message_number, uint8_t message_id, Group_Peer *_Nonnull peer)
2764
0
{
2765
0
    Message_Info *const i = find_message_slot_or_reject(message_number, message_id, peer);
2766
2767
0
    if (i == nullptr) {
2768
0
        return false;
2769
0
    }
2770
2771
0
    if (i == peer->last_message_infos + MAX_LAST_MESSAGE_INFOS) {
2772
0
        return false;
2773
0
    }
2774
2775
0
    if (peer->num_last_message_infos < MAX_LAST_MESSAGE_INFOS) {
2776
0
        ++peer->num_last_message_infos;
2777
0
    }
2778
2779
0
    memmove(i + 1, i, (&peer->last_message_infos[peer->num_last_message_infos - 1] - i) * sizeof(Message_Info));
2780
2781
0
    i->message_number = message_number;
2782
0
    i->message_id = message_id;
2783
2784
0
    return true;
2785
0
}
2786
2787
static void handle_message_packet_group(Group_Chats *_Nonnull g_c, uint32_t groupnumber, const uint8_t *_Nonnull data, uint16_t length,
2788
                                        uint32_t connection_index, void *_Nullable userdata)
2789
0
{
2790
0
    if (length < sizeof(uint16_t) + sizeof(uint32_t) + 1) {
2791
0
        return;
2792
0
    }
2793
2794
0
    Group_c *g = get_group_c(g_c, groupnumber);
2795
2796
0
    if (g == nullptr) {
2797
0
        return;
2798
0
    }
2799
2800
0
    uint16_t peer_number;
2801
0
    memcpy(&peer_number, data, sizeof(uint16_t));
2802
0
    peer_number = net_ntohs(peer_number);
2803
2804
0
    uint32_t message_number;
2805
0
    memcpy(&message_number, data + sizeof(uint16_t), sizeof(message_number));
2806
0
    message_number = net_ntohl(message_number);
2807
2808
0
    const uint8_t message_id = data[sizeof(uint16_t) + sizeof(message_number)];
2809
0
    const uint8_t *msg_data = data + sizeof(uint16_t) + sizeof(message_number) + 1;
2810
0
    const uint16_t msg_data_len = length - (sizeof(uint16_t) + sizeof(message_number) + 1);
2811
2812
0
    const bool ignore_frozen = message_id == GROUP_MESSAGE_FREEZE_PEER_ID;
2813
2814
0
    const int index = ignore_frozen ? get_peer_index(g, peer_number)
2815
0
                      : note_peer_active(g_c, groupnumber, peer_number, userdata);
2816
2817
0
    if (index == -1) {
2818
0
        if (ignore_frozen) {
2819
0
            return;
2820
0
        }
2821
2822
0
        if (g->connections[connection_index].type != GROUPCHAT_CONNECTION_ONLINE) {
2823
0
            return;
2824
0
        }
2825
2826
        /* If we don't know the peer this packet came from, then we query the
2827
         * list of peers from the relaying peer.
2828
         * (They wouldn't have relayed it if they didn't know the peer.) */
2829
0
        send_peer_query(g_c, g->connections[connection_index].number, g->connections[connection_index].group_number);
2830
0
        return;
2831
0
    }
2832
2833
0
    if (g->num_introducer_connections > 0 && count_connected(g) > DESIRED_CLOSEST) {
2834
0
        for (uint32_t i = 0; i < MAX_GROUP_CONNECTIONS; ++i) {
2835
0
            if (g->connections[i].type == GROUPCHAT_CONNECTION_NONE
2836
0
                    || (g->connections[i].reasons & GROUPCHAT_CONNECTION_REASON_INTRODUCER) == 0
2837
0
                    || i == connection_index) {
2838
0
                continue;
2839
0
            }
2840
2841
0
            uint8_t real_pk[CRYPTO_PUBLIC_KEY_SIZE];
2842
0
            get_friendcon_public_keys(real_pk, nullptr, g_c->fr_c, g->connections[i].number);
2843
2844
0
            if (pk_equal(g->group[index].real_pk, real_pk)) {
2845
                /* Received message from peer relayed via another peer, so
2846
                 * the introduction was successful */
2847
0
                remove_connection_reason(g_c, g, i, GROUPCHAT_CONNECTION_REASON_INTRODUCER);
2848
0
            }
2849
0
        }
2850
0
    }
2851
2852
0
    if (!check_message_info(message_number, message_id, &g->group[index])) {
2853
0
        return;
2854
0
    }
2855
2856
0
    uint8_t real_pk[CRYPTO_PUBLIC_KEY_SIZE];
2857
0
    get_friendcon_public_keys(real_pk, nullptr, g_c->fr_c, g->connections[connection_index].number);
2858
0
    const bool direct_from_sender = pk_equal(g->group[index].real_pk, real_pk);
2859
2860
0
    switch (message_id) {
2861
0
        case GROUP_MESSAGE_PING_ID: {
2862
0
            break;
2863
0
        }
2864
2865
0
        case GROUP_MESSAGE_NEW_PEER_ID: {
2866
0
            if (msg_data_len != GROUP_MESSAGE_NEW_PEER_LENGTH) {
2867
0
                return;
2868
0
            }
2869
2870
0
            uint16_t new_peer_number;
2871
0
            memcpy(&new_peer_number, msg_data, sizeof(uint16_t));
2872
0
            new_peer_number = net_ntohs(new_peer_number);
2873
0
            addpeer(g_c, groupnumber, msg_data + sizeof(uint16_t), msg_data + sizeof(uint16_t) + CRYPTO_PUBLIC_KEY_SIZE,
2874
0
                    new_peer_number, userdata, true, true);
2875
0
            break;
2876
0
        }
2877
2878
0
        case GROUP_MESSAGE_KILL_PEER_ID:
2879
0
        case GROUP_MESSAGE_FREEZE_PEER_ID: {
2880
0
            if (msg_data_len != GROUP_MESSAGE_KILL_PEER_LENGTH) {
2881
0
                return;
2882
0
            }
2883
2884
0
            uint16_t kill_peer_number;
2885
0
            memcpy(&kill_peer_number, msg_data, sizeof(uint16_t));
2886
0
            kill_peer_number = net_ntohs(kill_peer_number);
2887
2888
0
            if (peer_number == kill_peer_number) {
2889
0
                if (message_id == GROUP_MESSAGE_KILL_PEER_ID) {
2890
0
                    delpeer(g_c, groupnumber, index, userdata);
2891
0
                } else {
2892
0
                    freeze_peer(g_c, groupnumber, index, userdata);
2893
0
                }
2894
0
            } else {
2895
0
                return;
2896
                // TODO(irungentoo):
2897
0
            }
2898
2899
0
            break;
2900
0
        }
2901
2902
0
        case GROUP_MESSAGE_NAME_ID: {
2903
0
            if (!setnick(g_c, groupnumber, index, msg_data, msg_data_len, userdata, true)) {
2904
0
                return;
2905
0
            }
2906
2907
0
            break;
2908
0
        }
2909
2910
0
        case GROUP_MESSAGE_TITLE_ID: {
2911
0
            if (!settitle(g_c, groupnumber, index, msg_data, msg_data_len, userdata)) {
2912
0
                return;
2913
0
            }
2914
2915
0
            break;
2916
0
        }
2917
2918
0
        case PACKET_ID_MESSAGE: {
2919
0
            if (msg_data_len == 0) {
2920
0
                return;
2921
0
            }
2922
2923
0
            VLA(uint8_t, newmsg, msg_data_len + 1);
2924
0
            memcpy(newmsg, msg_data, msg_data_len);
2925
0
            newmsg[msg_data_len] = 0;
2926
2927
            // TODO(irungentoo):
2928
0
            if (g_c->message_callback != nullptr) {
2929
0
                g_c->message_callback(g_c->m, groupnumber, index, 0, newmsg, msg_data_len, userdata);
2930
0
            }
2931
2932
0
            break;
2933
0
        }
2934
2935
0
        case PACKET_ID_ACTION: {
2936
0
            if (msg_data_len == 0) {
2937
0
                return;
2938
0
            }
2939
2940
0
            VLA(uint8_t, newmsg, msg_data_len + 1);
2941
0
            memcpy(newmsg, msg_data, msg_data_len);
2942
0
            newmsg[msg_data_len] = 0;
2943
2944
            // TODO(irungentoo):
2945
0
            if (g_c->message_callback != nullptr) {
2946
0
                g_c->message_callback(g_c->m, groupnumber, index, 1, newmsg, msg_data_len, userdata);
2947
0
            }
2948
2949
0
            break;
2950
0
        }
2951
2952
0
        default: {
2953
0
            return;
2954
0
        }
2955
0
    }
2956
2957
    /* If the packet was received from the peer who sent the message, relay it
2958
     * back. When the sender only has one group connection (e.g. because there
2959
     * are only two peers in the group), this is the only way for them to
2960
     * receive their own message. */
2961
0
    send_message_all_connections(g_c, g, data, length, direct_from_sender ? -1 : connection_index);
2962
0
}
2963
2964
static int g_handle_packet(void *object, int friendcon_id, const uint8_t *data, uint16_t length, void *userdata)
2965
0
{
2966
0
    Group_Chats *g_c = (Group_Chats *)object;
2967
2968
0
    if (length < 1 + sizeof(uint16_t) + 1) {
2969
0
        return -1;
2970
0
    }
2971
2972
0
    if (data[0] == PACKET_ID_ONLINE_PACKET) {
2973
0
        return handle_packet_online(g_c, friendcon_id, data + 1, length - 1);
2974
0
    }
2975
2976
0
    if (data[0] == PACKET_ID_REJOIN_CONFERENCE) {
2977
0
        return handle_packet_rejoin(g_c, friendcon_id, data + 1, length - 1, userdata);
2978
0
    }
2979
2980
0
    uint16_t groupnumber;
2981
0
    memcpy(&groupnumber, data + 1, sizeof(uint16_t));
2982
0
    groupnumber = net_ntohs(groupnumber);
2983
0
    const Group_c *g = get_group_c(g_c, groupnumber);
2984
2985
0
    if (g == nullptr) {
2986
0
        return -1;
2987
0
    }
2988
2989
0
    const int index = friend_in_connections(g, friendcon_id);
2990
2991
0
    if (index == -1) {
2992
0
        return -1;
2993
0
    }
2994
2995
0
    if (data[0] == PACKET_ID_DIRECT_CONFERENCE) {
2996
0
        handle_direct_packet(g_c, groupnumber, data + 1 + sizeof(uint16_t),
2997
0
                             length - (1 + sizeof(uint16_t)), index, userdata);
2998
0
        return 0;
2999
0
    }
3000
3001
0
    if (data[0] == PACKET_ID_MESSAGE_CONFERENCE) {
3002
0
        handle_message_packet_group(g_c, groupnumber, data + 1 + sizeof(uint16_t),
3003
0
                                    length - (1 + sizeof(uint16_t)), index, userdata);
3004
0
        return 0;
3005
0
    }
3006
3007
0
    return -1;
3008
0
}
3009
3010
/** @brief Did we already receive the lossy packet or not.
3011
 *
3012
 * @retval -1 on failure.
3013
 * @retval 0 if packet was not received.
3014
 * @retval 1 if packet was received.
3015
 *
3016
 * TODO(irungentoo): test this
3017
 */
3018
static int lossy_packet_not_received(const Group_c *_Nonnull g, int peer_index, uint16_t message_number)
3019
0
{
3020
0
    if (peer_index == -1) {
3021
0
        return -1;
3022
0
    }
3023
3024
0
    if (g->group[peer_index].bottom_lossy_number == g->group[peer_index].top_lossy_number) {
3025
0
        g->group[peer_index].top_lossy_number = message_number;
3026
0
        g->group[peer_index].bottom_lossy_number = (message_number - MAX_LOSSY_COUNT) + 1;
3027
0
        g->group[peer_index].recv_lossy[message_number % MAX_LOSSY_COUNT] = 1;
3028
0
        return 0;
3029
0
    }
3030
3031
0
    if ((uint16_t)(message_number - g->group[peer_index].bottom_lossy_number) < MAX_LOSSY_COUNT) {
3032
0
        if (g->group[peer_index].recv_lossy[message_number % MAX_LOSSY_COUNT] != 0) {
3033
0
            return 1;
3034
0
        }
3035
3036
0
        g->group[peer_index].recv_lossy[message_number % MAX_LOSSY_COUNT] = 1;
3037
0
        return 0;
3038
0
    }
3039
3040
0
    if ((uint16_t)(message_number - g->group[peer_index].bottom_lossy_number) > (1 << 15)) {
3041
0
        return -1;
3042
0
    }
3043
3044
0
    const uint16_t top_distance = message_number - g->group[peer_index].top_lossy_number;
3045
3046
0
    if (top_distance >= MAX_LOSSY_COUNT) {
3047
0
        crypto_memzero(g->group[peer_index].recv_lossy, sizeof(g->group[peer_index].recv_lossy));
3048
0
    } else {  // top_distance < MAX_LOSSY_COUNT
3049
0
        for (unsigned int i = g->group[peer_index].bottom_lossy_number;
3050
0
                i != g->group[peer_index].bottom_lossy_number + top_distance;
3051
0
                ++i) {
3052
0
            g->group[peer_index].recv_lossy[i % MAX_LOSSY_COUNT] = 0;
3053
0
        }
3054
0
    }
3055
3056
0
    g->group[peer_index].top_lossy_number = message_number;
3057
0
    g->group[peer_index].bottom_lossy_number = (message_number - MAX_LOSSY_COUNT) + 1;
3058
0
    g->group[peer_index].recv_lossy[message_number % MAX_LOSSY_COUNT] = 1;
3059
3060
0
    return 0;
3061
3062
0
}
3063
3064
/** Does this group type make use of lossy packets? */
3065
static bool type_uses_lossy(uint8_t type)
3066
0
{
3067
0
    return type == GROUPCHAT_TYPE_AV;
3068
0
}
3069
3070
static int handle_lossy(void *object, int friendcon_id, const uint8_t *data, uint16_t length, void *userdata)
3071
0
{
3072
0
    const Group_Chats *g_c = (const Group_Chats *)object;
3073
3074
0
    if (data[0] != PACKET_ID_LOSSY_CONFERENCE) {
3075
0
        return -1;
3076
0
    }
3077
3078
0
    if (length < 1 + sizeof(uint16_t) * 3 + 1) {
3079
0
        return -1;
3080
0
    }
3081
3082
0
    uint16_t groupnumber;
3083
0
    uint16_t peer_number;
3084
0
    uint16_t message_number;
3085
0
    memcpy(&groupnumber, data + 1, sizeof(uint16_t));
3086
0
    memcpy(&peer_number, data + 1 + sizeof(uint16_t), sizeof(uint16_t));
3087
0
    memcpy(&message_number, data + 1 + sizeof(uint16_t) * 2, sizeof(uint16_t));
3088
0
    groupnumber = net_ntohs(groupnumber);
3089
0
    peer_number = net_ntohs(peer_number);
3090
0
    message_number = net_ntohs(message_number);
3091
3092
0
    const Group_c *g = get_group_c(g_c, groupnumber);
3093
3094
0
    if (g == nullptr) {
3095
0
        return -1;
3096
0
    }
3097
3098
0
    if (!type_uses_lossy(g->type)) {
3099
0
        return -1;
3100
0
    }
3101
3102
0
    const int index = friend_in_connections(g, friendcon_id);
3103
3104
0
    if (index == -1) {
3105
0
        return -1;
3106
0
    }
3107
3108
0
    if (peer_number == g->peer_number) {
3109
0
        return -1;
3110
0
    }
3111
3112
0
    const int peer_index = get_peer_index(g, peer_number);
3113
3114
0
    if (peer_index == -1) {
3115
0
        return -1;
3116
0
    }
3117
3118
0
    if (lossy_packet_not_received(g, peer_index, message_number) != 0) {
3119
0
        return -1;
3120
0
    }
3121
3122
0
    const uint8_t *lossy_data = data + 1 + sizeof(uint16_t) * 3;
3123
0
    uint16_t lossy_length = length - (1 + sizeof(uint16_t) * 3);
3124
0
    const uint8_t message_id = lossy_data[0];
3125
0
    ++lossy_data;
3126
0
    --lossy_length;
3127
3128
0
    send_lossy_all_connections(g_c, g, data + 1 + sizeof(uint16_t), length - (1 + sizeof(uint16_t)), index);
3129
3130
0
    if (g_c->lossy_packethandlers[message_id] == nullptr) {
3131
0
        return -1;
3132
0
    }
3133
3134
0
    if (g_c->lossy_packethandlers[message_id](g->object, groupnumber, peer_index, g->group[peer_index].object,
3135
0
            lossy_data, lossy_length) == -1) {
3136
0
        return -1;
3137
0
    }
3138
3139
0
    return 0;
3140
0
}
3141
3142
/** @brief Set the object that is tied to the group chat.
3143
 *
3144
 * @retval 0 on success.
3145
 * @retval -1 on failure
3146
 */
3147
int group_set_object(const Group_Chats *g_c, uint32_t groupnumber, void *object)
3148
0
{
3149
0
    Group_c *g = get_group_c(g_c, groupnumber);
3150
3151
0
    if (g == nullptr) {
3152
0
        return -1;
3153
0
    }
3154
3155
0
    g->object = object;
3156
0
    return 0;
3157
0
}
3158
3159
/** @brief Set the object that is tied to the group peer.
3160
 *
3161
 * @retval 0 on success.
3162
 * @retval -1 on failure
3163
 */
3164
int group_peer_set_object(const Group_Chats *g_c, uint32_t groupnumber, uint32_t peernumber, void *object)
3165
0
{
3166
0
    const Group_c *g = get_group_c(g_c, groupnumber);
3167
3168
0
    if (g == nullptr) {
3169
0
        return -1;
3170
0
    }
3171
3172
0
    if (peernumber >= g->numpeers) {
3173
0
        return -1;
3174
0
    }
3175
3176
0
    g->group[peernumber].object = object;
3177
0
    return 0;
3178
0
}
3179
3180
/** @brief Return the object tied to the group chat previously set by group_set_object.
3181
 *
3182
 * @retval NULL on failure.
3183
 * @return object on success.
3184
 */
3185
void *group_get_object(const Group_Chats *g_c, uint32_t groupnumber)
3186
0
{
3187
0
    const Group_c *g = get_group_c(g_c, groupnumber);
3188
3189
0
    if (g == nullptr) {
3190
0
        return nullptr;
3191
0
    }
3192
3193
0
    return g->object;
3194
0
}
3195
3196
/** @brief Return the object tied to the group chat peer previously set by group_peer_set_object.
3197
 *
3198
 * @retval NULL on failure.
3199
 * @return object on success.
3200
 */
3201
void *group_peer_get_object(const Group_Chats *g_c, uint32_t groupnumber, uint32_t peernumber)
3202
0
{
3203
0
    const Group_c *g = get_group_c(g_c, groupnumber);
3204
3205
0
    if (g == nullptr) {
3206
0
        return nullptr;
3207
0
    }
3208
3209
0
    if (peernumber >= g->numpeers) {
3210
0
        return nullptr;
3211
0
    }
3212
3213
0
    return g->group[peernumber].object;
3214
0
}
3215
3216
/** Interval in seconds to send ping messages */
3217
0
#define GROUP_PING_INTERVAL 20
3218
3219
static bool ping_groupchat(const Group_Chats *g_c, uint32_t groupnumber)
3220
0
{
3221
0
    Group_c *g = get_group_c(g_c, groupnumber);
3222
3223
0
    if (g == nullptr) {
3224
0
        return false;
3225
0
    }
3226
3227
0
    if (mono_time_is_timeout(g_c->mono_time, g->last_sent_ping, GROUP_PING_INTERVAL)) {
3228
0
        if (group_ping_send(g_c, groupnumber)) {
3229
0
            g->last_sent_ping = mono_time_get(g_c->mono_time);
3230
0
        }
3231
0
    }
3232
3233
0
    return true;
3234
0
}
3235
3236
/** Seconds of inactivity after which to freeze a peer */
3237
0
#define FREEZE_TIMEOUT (GROUP_PING_INTERVAL * 3)
3238
3239
static bool groupchat_freeze_timedout(Group_Chats *_Nonnull g_c, uint32_t groupnumber, void *_Nullable userdata)
3240
0
{
3241
0
    Group_c *g = get_group_c(g_c, groupnumber);
3242
0
    if (g == nullptr) {
3243
0
        return false;
3244
0
    }
3245
3246
0
    for (uint32_t i = 0; i < g->numpeers; ++i) {
3247
0
        if (g->group[i].peer_number == g->peer_number) {
3248
0
            continue;
3249
0
        }
3250
3251
0
        if (mono_time_is_timeout(g_c->mono_time, g->group[i].last_active, FREEZE_TIMEOUT)) {
3252
0
            freeze_peer(g_c, groupnumber, i, userdata);
3253
0
        }
3254
0
    }
3255
3256
0
    if (g->numpeers <= 1) {
3257
0
        g->title_fresh = false;
3258
0
    }
3259
3260
0
    return true;
3261
0
}
3262
3263
/** Push non-empty slots to start. */
3264
static void squash_connections(Group_c *_Nonnull g)
3265
0
{
3266
0
    uint16_t num_connected = 0;
3267
3268
0
    for (uint16_t i = 0; i < MAX_GROUP_CONNECTIONS; ++i) {
3269
0
        if (g->connections[i].type != GROUPCHAT_CONNECTION_NONE) {
3270
0
            g->connections[num_connected] = g->connections[i];
3271
0
            ++num_connected;
3272
0
        }
3273
0
    }
3274
3275
0
    for (uint16_t i = num_connected; i < MAX_GROUP_CONNECTIONS; ++i) {
3276
0
        g->connections[i].type = GROUPCHAT_CONNECTION_NONE;
3277
0
    }
3278
0
}
3279
3280
0
#define MIN_EMPTY_CONNECTIONS (1 + MAX_GROUP_CONNECTIONS / 10)
3281
3282
static uint16_t empty_connection_count(const Group_c *_Nonnull g)
3283
0
{
3284
0
    uint16_t to_clear = MIN_EMPTY_CONNECTIONS;
3285
3286
0
    for (uint16_t i = 0; i < MAX_GROUP_CONNECTIONS; ++i) {
3287
0
        if (g->connections[i].type == GROUPCHAT_CONNECTION_NONE) {
3288
0
            --to_clear;
3289
3290
0
            if (to_clear == 0) {
3291
0
                break;
3292
0
            }
3293
0
        }
3294
0
    }
3295
3296
0
    return to_clear;
3297
0
}
3298
3299
/**
3300
 * @brief Remove old connections as necessary to ensure we have space for new
3301
 *   connections.
3302
 *
3303
 * This invalidates connections array indices (which is
3304
 * why we do this periodically rather than on adding a connection).
3305
 */
3306
static void clean_connections(Group_Chats *_Nonnull g_c, Group_c *_Nonnull g)
3307
0
{
3308
0
    for (uint16_t to_clear = empty_connection_count(g); to_clear > 0; --to_clear) {
3309
        // Remove a connection. Prefer non-closest connections, and given
3310
        // that prefer non-online connections, and given that prefer earlier
3311
        // slots.
3312
0
        uint16_t i = 0;
3313
3314
0
        while (i < MAX_GROUP_CONNECTIONS
3315
0
                && (g->connections[i].type != GROUPCHAT_CONNECTION_CONNECTING
3316
0
                    || (g->connections[i].reasons & GROUPCHAT_CONNECTION_REASON_CLOSEST) != 0)) {
3317
0
            ++i;
3318
0
        }
3319
3320
0
        if (i == MAX_GROUP_CONNECTIONS) {
3321
0
            i = 0;
3322
3323
0
            while (i < MAX_GROUP_CONNECTIONS - to_clear
3324
0
                    && (g->connections[i].type != GROUPCHAT_CONNECTION_ONLINE
3325
0
                        || (g->connections[i].reasons & GROUPCHAT_CONNECTION_REASON_CLOSEST) != 0)) {
3326
0
                ++i;
3327
0
            }
3328
0
        }
3329
3330
0
        if (g->connections[i].type != GROUPCHAT_CONNECTION_NONE) {
3331
0
            remove_connection(g_c, g, i);
3332
0
        }
3333
0
    }
3334
3335
0
    squash_connections(g);
3336
0
}
3337
3338
/** Send current name (set in messenger) to all online groups. */
3339
void send_name_all_groups(const Group_Chats *g_c)
3340
0
{
3341
0
    for (uint16_t i = 0; i < g_c->num_chats; ++i) {
3342
0
        Group_c *g = get_group_c(g_c, i);
3343
3344
0
        if (g == nullptr) {
3345
0
            continue;
3346
0
        }
3347
3348
0
        if (g->status == GROUPCHAT_STATUS_CONNECTED) {
3349
0
            group_name_send(g_c, i, g_c->m->name, g_c->m->name_length);
3350
0
            g->need_send_name = false;
3351
0
        }
3352
0
    }
3353
0
}
3354
3355
1.05k
#define SAVED_PEER_SIZE_CONSTANT (2 * CRYPTO_PUBLIC_KEY_SIZE + sizeof(uint16_t) + sizeof(uint64_t) + 1)
3356
3357
static uint32_t saved_peer_size(const Group_Peer *_Nonnull peer)
3358
612
{
3359
612
    return SAVED_PEER_SIZE_CONSTANT + peer->nick_len;
3360
612
}
3361
3362
static uint8_t *save_peer(const Group_Peer *_Nonnull peer, uint8_t *_Nonnull data)
3363
204
{
3364
204
    memcpy(data, peer->real_pk, CRYPTO_PUBLIC_KEY_SIZE);
3365
204
    data += CRYPTO_PUBLIC_KEY_SIZE;
3366
3367
204
    memcpy(data, peer->temp_pk, CRYPTO_PUBLIC_KEY_SIZE);
3368
204
    data += CRYPTO_PUBLIC_KEY_SIZE;
3369
3370
204
    host_to_lendian_bytes16(data, peer->peer_number);
3371
204
    data += sizeof(uint16_t);
3372
3373
204
    host_to_lendian_bytes64(data, peer->last_active);
3374
204
    data += sizeof(uint64_t);
3375
3376
    // TODO(iphydf): This looks broken: nick_len can be > 255.
3377
204
    *data = peer->nick_len;
3378
204
    ++data;
3379
3380
204
    memcpy(data, peer->nick, peer->nick_len);
3381
204
    data += peer->nick_len;
3382
3383
204
    return data;
3384
204
}
3385
3386
11.4k
#define SAVED_CONF_SIZE_CONSTANT (1 + GROUP_ID_LENGTH + sizeof(uint32_t) \
3387
11.4k
      + sizeof(uint16_t) + sizeof(uint16_t) + sizeof(uint32_t) + 1)
3388
3389
static uint32_t saved_conf_size(const Group_c *_Nonnull g)
3390
4.95k
{
3391
4.95k
    uint32_t len = SAVED_CONF_SIZE_CONSTANT + g->title_len;
3392
3393
10.5k
    for (uint32_t j = 0; j < g->numpeers + g->numfrozen; ++j) {
3394
5.56k
        const Group_Peer *peer = (j < g->numpeers) ? &g->group[j] : &g->frozen[j - g->numpeers];
3395
3396
5.56k
        if (pk_equal(peer->real_pk, g->real_pk)) {
3397
4.95k
            continue;
3398
4.95k
        }
3399
3400
612
        len += saved_peer_size(peer);
3401
612
    }
3402
3403
4.95k
    return len;
3404
4.95k
}
3405
3406
/**
3407
 * Save a future message number. The save will remain valid until we have sent
3408
 * this many more messages.
3409
 */
3410
1.65k
#define SAVE_OFFSET_MESSAGE_NUMBER (1 << 16)
3411
1.65k
#define SAVE_OFFSET_LOSSY_MESSAGE_NUMBER (1 << 13)
3412
3413
static uint8_t *save_conf(const Group_c *_Nonnull g, uint8_t *_Nonnull data)
3414
1.65k
{
3415
1.65k
    *data = g->type;
3416
1.65k
    ++data;
3417
3418
1.65k
    memcpy(data, g->id, GROUP_ID_LENGTH);
3419
1.65k
    data += GROUP_ID_LENGTH;
3420
3421
1.65k
    host_to_lendian_bytes32(data, g->message_number + SAVE_OFFSET_MESSAGE_NUMBER);
3422
1.65k
    data += sizeof(uint32_t);
3423
3424
1.65k
    host_to_lendian_bytes16(data, g->lossy_message_number + SAVE_OFFSET_LOSSY_MESSAGE_NUMBER);
3425
1.65k
    data += sizeof(uint16_t);
3426
3427
1.65k
    host_to_lendian_bytes16(data, g->peer_number);
3428
1.65k
    data += sizeof(uint16_t);
3429
3430
1.65k
    uint8_t *const numsaved_location = data;
3431
1.65k
    data += sizeof(uint32_t);
3432
3433
1.65k
    *data = g->title_len;
3434
1.65k
    ++data;
3435
3436
1.65k
    memcpy(data, g->title, g->title_len);
3437
1.65k
    data += g->title_len;
3438
3439
1.65k
    uint32_t numsaved = 0;
3440
3441
3.50k
    for (uint32_t j = 0; j < g->numpeers + g->numfrozen; ++j) {
3442
1.85k
        const Group_Peer *peer = (j < g->numpeers) ? &g->group[j] : &g->frozen[j - g->numpeers];
3443
3444
1.85k
        if (pk_equal(peer->real_pk, g->real_pk)) {
3445
1.65k
            continue;
3446
1.65k
        }
3447
3448
204
        data = save_peer(peer, data);
3449
204
        ++numsaved;
3450
204
    }
3451
3452
1.65k
    host_to_lendian_bytes32(numsaved_location, numsaved);
3453
3454
1.65k
    return data;
3455
1.65k
}
3456
3457
static uint32_t conferences_section_size(const Group_Chats *_Nonnull g_c)
3458
4.40k
{
3459
4.40k
    uint32_t len = 0;
3460
3461
9.36k
    for (uint16_t i = 0; i < g_c->num_chats; ++i) {
3462
4.95k
        const Group_c *g = get_group_c(g_c, i);
3463
3464
4.95k
        if (g == nullptr || g->status != GROUPCHAT_STATUS_CONNECTED) {
3465
0
            continue;
3466
0
        }
3467
3468
4.95k
        len += saved_conf_size(g);
3469
4.95k
    }
3470
3471
4.40k
    return len;
3472
4.40k
}
3473
3474
uint32_t conferences_size(const Group_Chats *g_c)
3475
2.93k
{
3476
2.93k
    return 2 * sizeof(uint32_t) + conferences_section_size(g_c);
3477
2.93k
}
3478
3479
uint8_t *conferences_save(const Group_Chats *g_c, uint8_t *data)
3480
1.46k
{
3481
1.46k
    const uint32_t len = conferences_section_size(g_c);
3482
1.46k
    data = state_write_section_header(data, STATE_COOKIE_TYPE, len, STATE_TYPE_CONFERENCES);
3483
3484
3.12k
    for (uint16_t i = 0; i < g_c->num_chats; ++i) {
3485
1.65k
        const Group_c *g = get_group_c(g_c, i);
3486
3487
1.65k
        if (g == nullptr || g->status != GROUPCHAT_STATUS_CONNECTED) {
3488
0
            continue;
3489
0
        }
3490
3491
1.65k
        data = save_conf(g, data);
3492
1.65k
    }
3493
3494
1.46k
    return data;
3495
1.46k
}
3496
3497
/**
3498
 * @brief load_group Load a Group section from a save file
3499
 * @param g Group to load
3500
 * @param g_c Reference to all groupchats, need for utility functions
3501
 * @param data Start of the data to deserialze
3502
 * @param length Length of data
3503
 * @return 0 on error, number of consumed bytes otherwise
3504
 */
3505
static uint32_t load_group(Group_c *_Nonnull g, const Group_Chats *_Nonnull g_c, const uint8_t *_Nonnull data, uint32_t length)
3506
6.13k
{
3507
6.13k
    const uint8_t *init_data = data;
3508
3509
    // Initialize to default values so we can unconditionally free in case of an error
3510
6.13k
    setup_conference(g);
3511
3512
6.13k
    g->type = *data;
3513
6.13k
    ++data;
3514
3515
6.13k
    memcpy(g->id, data, GROUP_ID_LENGTH);
3516
6.13k
    data += GROUP_ID_LENGTH;
3517
3518
6.13k
    lendian_bytes_to_host32(&g->message_number, data);
3519
6.13k
    data += sizeof(uint32_t);
3520
3521
6.13k
    lendian_bytes_to_host16(&g->lossy_message_number, data);
3522
6.13k
    data += sizeof(uint16_t);
3523
3524
6.13k
    lendian_bytes_to_host16(&g->peer_number, data);
3525
6.13k
    data += sizeof(uint16_t);
3526
3527
6.13k
    lendian_bytes_to_host32(&g->numfrozen, data);
3528
6.13k
    data += sizeof(uint32_t);
3529
3530
6.13k
    g->title_len = *data;
3531
3532
6.13k
    if (g->title_len > MAX_NAME_LENGTH) {
3533
13
        return 0;
3534
13
    }
3535
3536
6.12k
    ++data;
3537
3538
6.12k
    assert((data - init_data) < UINT32_MAX);
3539
3540
6.12k
    if (length < (uint32_t)(data - init_data) + g->title_len) {
3541
2
        return 0;
3542
2
    }
3543
3544
6.12k
    memcpy(g->title, data, g->title_len);
3545
6.12k
    data += g->title_len;
3546
3547
6.52k
    for (uint32_t j = 0; j < g->numfrozen; ++j) {
3548
3549
442
        assert((data - init_data) < UINT32_MAX);
3550
3551
442
        if (length < (uint32_t)(data - init_data) + SAVED_PEER_SIZE_CONSTANT) {
3552
31
            return 0;
3553
31
        }
3554
3555
        // This is inefficient, but allows us to check data consistency before allocating memory
3556
411
        Group_Peer *tmp_frozen = (Group_Peer *)mem_vrealloc(g_c->mem, g->frozen, j + 1, sizeof(Group_Peer));
3557
3558
411
        if (tmp_frozen == nullptr) {
3559
            // Memory allocation failure
3560
0
            return 0;
3561
0
        }
3562
3563
411
        g->frozen = tmp_frozen;
3564
3565
411
        Group_Peer *peer = &g->frozen[j];
3566
411
        *peer = empty_group_peer;
3567
3568
411
        pk_copy(peer->real_pk, data);
3569
411
        data += CRYPTO_PUBLIC_KEY_SIZE;
3570
411
        pk_copy(peer->temp_pk, data);
3571
411
        data += CRYPTO_PUBLIC_KEY_SIZE;
3572
3573
411
        lendian_bytes_to_host16(&peer->peer_number, data);
3574
411
        data += sizeof(uint16_t);
3575
3576
411
        lendian_bytes_to_host64(&peer->last_active, data);
3577
411
        data += sizeof(uint64_t);
3578
3579
411
        peer->nick_len = *data;
3580
3581
411
        if (peer->nick_len > MAX_NAME_LENGTH) {
3582
8
            return 0;
3583
8
        }
3584
3585
403
        ++data;
3586
403
        assert((data - init_data) < UINT32_MAX);
3587
3588
403
        if (length < (uint32_t)(data - init_data) + peer->nick_len) {
3589
4
            return 0;
3590
4
        }
3591
3592
399
        memcpy(peer->nick, data, peer->nick_len);
3593
399
        data += peer->nick_len;
3594
3595
        // NOTE: this relies on friends being loaded before conferences.
3596
399
        peer->is_friend = getfriend_id(g_c->m, peer->real_pk) != -1;
3597
399
    }
3598
3599
6.07k
    if (g->numfrozen > g->maxfrozen) {
3600
0
        g->maxfrozen = g->numfrozen;
3601
0
    }
3602
3603
6.07k
    g->status = GROUPCHAT_STATUS_CONNECTED;
3604
3605
6.07k
    pk_copy(g->real_pk, nc_get_self_public_key(g_c->m->net_crypto));
3606
3607
6.07k
    assert((data - init_data) < UINT32_MAX);
3608
3609
6.07k
    return (uint32_t)(data - init_data);
3610
6.07k
}
3611
3612
static State_Load_Status load_conferences_helper(Group_Chats *_Nonnull g_c, const uint8_t *_Nonnull data, uint32_t length)
3613
468
{
3614
468
    const uint8_t *init_data = data;
3615
3616
6.53k
    while (length >= (uint32_t)(data - init_data) + SAVED_CONF_SIZE_CONSTANT) {
3617
6.13k
        const int groupnumber = create_group_chat(g_c);
3618
3619
        // Helpful for testing
3620
6.13k
        assert(groupnumber != -1);
3621
3622
6.13k
        if (groupnumber == -1) {
3623
            // If this fails there's a serious problem, don't bother with cleanup
3624
0
            LOGGER_ERROR(g_c->m->log, "conference creation failed");
3625
0
            return STATE_LOAD_STATUS_ERROR;
3626
0
        }
3627
3628
6.13k
        Group_c *g = &g_c->chats[groupnumber];
3629
3630
6.13k
        const uint32_t consumed = load_group(g, g_c, data, length - (uint32_t)(data - init_data));
3631
3632
6.13k
        if (consumed == 0) {
3633
            // remove partially loaded stuff, wipe_group_chat must be able to wipe a partially loaded group
3634
58
            const bool ret = wipe_group_chat(g_c, groupnumber);
3635
3636
            // HACK: suppress unused variable warning
3637
58
            if (!ret) {
3638
                // wipe_group_chat(...) must be able to wipe partially allocated groups
3639
0
                assert(ret);
3640
0
            }
3641
3642
58
            LOGGER_ERROR(g_c->m->log, "conference loading failed");
3643
58
            return STATE_LOAD_STATUS_ERROR;
3644
58
        }
3645
3646
6.07k
        data += consumed;
3647
3648
6.07k
        const int peer_index = addpeer(g_c, groupnumber, g->real_pk, dht_get_self_public_key(g_c->m->dht), g->peer_number,
3649
6.07k
                                       nullptr, true, false);
3650
3651
6.07k
        if (peer_index == -1) {
3652
7
            LOGGER_ERROR(g_c->m->log, "adding peer %d failed", g->peer_number);
3653
7
            return STATE_LOAD_STATUS_ERROR;
3654
7
        }
3655
3656
6.07k
        setnick(g_c, groupnumber, peer_index, g_c->m->name, g_c->m->name_length, nullptr, false);
3657
6.07k
    }
3658
3659
403
    return STATE_LOAD_STATUS_CONTINUE;
3660
468
}
3661
3662
static State_Load_Status load_conferences(Group_Chats *_Nonnull g_c, const uint8_t *_Nonnull data, uint32_t length)
3663
468
{
3664
468
    const State_Load_Status res = load_conferences_helper(g_c, data, length);
3665
3666
468
    if (res == STATE_LOAD_STATUS_CONTINUE) {
3667
403
        return res;
3668
403
    }
3669
3670
    // Loading failed, cleanup all Group_c
3671
3672
    // save locally, because wipe_group_chat(...) modifies it
3673
65
    const uint16_t num_groups = g_c->num_chats;
3674
3675
3.76k
    for (uint16_t i = 0; i < num_groups; ++i) {
3676
3.69k
        wipe_group_chat(g_c, i);
3677
3.69k
    }
3678
3679
65
    return res;
3680
468
}
3681
3682
bool conferences_load_state_section(Group_Chats *g_c, const uint8_t *data, uint32_t length, uint16_t type,
3683
                                    State_Load_Status *status)
3684
903
{
3685
903
    if (type != STATE_TYPE_CONFERENCES) {
3686
435
        return false;
3687
435
    }
3688
3689
468
    *status = load_conferences(g_c, data, length);
3690
468
    return true;
3691
903
}
3692
3693
/** Create new groupchat instance. */
3694
Group_Chats *new_groupchats(const Mono_Time *mono_time, const Memory *mem, Messenger *m)
3695
1.96k
{
3696
1.96k
    if (m == nullptr) {
3697
0
        return nullptr;
3698
0
    }
3699
3700
1.96k
    Group_Chats *temp = (Group_Chats *)mem_alloc(mem, sizeof(Group_Chats));
3701
3702
1.96k
    if (temp == nullptr) {
3703
0
        return nullptr;
3704
0
    }
3705
3706
1.96k
    temp->mem = mem;
3707
1.96k
    temp->mono_time = mono_time;
3708
1.96k
    temp->m = m;
3709
1.96k
    temp->fr_c = m->fr_c;
3710
1.96k
    m_callback_conference_invite(m, &handle_friend_invite_packet);
3711
3712
1.96k
    set_global_status_callback(m->fr_c, &g_handle_any_status, temp);
3713
3714
1.96k
    return temp;
3715
1.96k
}
3716
3717
/** main groupchats loop. */
3718
void do_groupchats(Group_Chats *g_c, void *userdata)
3719
0
{
3720
0
    for (uint16_t i = 0; i < g_c->num_chats; ++i) {
3721
0
        Group_c *g = get_group_c(g_c, i);
3722
3723
0
        if (g == nullptr) {
3724
0
            continue;
3725
0
        }
3726
3727
0
        if (g->status == GROUPCHAT_STATUS_CONNECTED) {
3728
0
            connect_to_closest(g_c, i, userdata);
3729
0
            ping_groupchat(g_c, i);
3730
0
            groupchat_freeze_timedout(g_c, i, userdata);
3731
0
            clean_connections(g_c, g);
3732
3733
0
            if (g->need_send_name) {
3734
0
                group_name_send(g_c, i, g_c->m->name, g_c->m->name_length);
3735
0
                g->need_send_name = false;
3736
0
            }
3737
0
        }
3738
0
    }
3739
3740
    // TODO(irungentoo):
3741
0
}
3742
3743
/** Free everything related with group chats. */
3744
void kill_groupchats(Group_Chats *g_c)
3745
1.96k
{
3746
1.96k
    if (g_c == nullptr) {
3747
0
        return;
3748
0
    }
3749
3750
4.34k
    for (uint16_t i = 0; i < g_c->num_chats; ++i) {
3751
2.38k
        del_groupchat(g_c, i, false);
3752
2.38k
    }
3753
3754
1.96k
    m_callback_conference_invite(g_c->m, nullptr);
3755
1.96k
    set_global_status_callback(g_c->m->fr_c, nullptr, nullptr);
3756
1.96k
    g_c->m->conferences_object = nullptr;
3757
1.96k
    mem_delete(g_c->mem, g_c);
3758
1.96k
}
3759
3760
/**
3761
 * @brief Return the number of chats in the instance m.
3762
 *
3763
 * You should use this to determine how much memory to allocate
3764
 * for copy_chatlist.
3765
 */
3766
uint32_t count_chatlist(const Group_Chats *g_c)
3767
0
{
3768
0
    uint32_t ret = 0;
3769
3770
0
    for (uint16_t i = 0; i < g_c->num_chats; ++i) {
3771
0
        if (g_c->chats[i].status != GROUPCHAT_STATUS_NONE) {
3772
0
            ++ret;
3773
0
        }
3774
0
    }
3775
3776
0
    return ret;
3777
0
}
3778
3779
/** @brief Copy a list of valid chat IDs into the array out_list.
3780
 *
3781
 * If out_list is NULL, returns 0.
3782
 * Otherwise, returns the number of elements copied.
3783
 * If the array was too small, the contents
3784
 * of out_list will be truncated to list_size.
3785
 */
3786
uint32_t copy_chatlist(const Group_Chats *g_c, uint32_t *out_list, uint32_t list_size)
3787
0
{
3788
0
    if (out_list == nullptr) {
3789
0
        return 0;
3790
0
    }
3791
3792
0
    if (g_c->num_chats == 0) {
3793
0
        return 0;
3794
0
    }
3795
3796
0
    uint32_t ret = 0;
3797
3798
0
    for (uint16_t i = 0; i < g_c->num_chats; ++i) {
3799
0
        if (ret >= list_size) {
3800
0
            break;  /* Abandon ship */
3801
0
        }
3802
3803
0
        if (g_c->chats[i].status > GROUPCHAT_STATUS_NONE) {
3804
0
            out_list[ret] = i;
3805
0
            ++ret;
3806
0
        }
3807
0
    }
3808
3809
0
    return ret;
3810
0
}