Coverage Report

Created: 2023-11-22 10:24

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