Coverage Report

Created: 2024-01-26 01:52

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