Coverage Report

Created: 2026-10-24 08:16

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