Coverage Report

Created: 2026-10-24 08:16

/work/toxcore/Messenger.c
Line
Count
Source (jump to first uncovered line)
1
/* SPDX-License-Identifier: GPL-3.0-or-later
2
 * Copyright © 2016-2026 The TokTok team.
3
 * Copyright © 2013 Tox project.
4
 */
5
6
/**
7
 * An implementation of a simple text chat only messenger on the tox network core.
8
 */
9
#include "Messenger.h"
10
11
#include <assert.h>
12
#include <stdio.h>
13
#include <string.h>
14
#include <time.h>
15
16
#include "DHT.h"
17
#include "TCP_client.h"
18
#include "TCP_connection.h"
19
#include "TCP_server.h"
20
#include "announce.h"
21
#include "attributes.h"
22
#include "bin_pack.h"
23
#include "bin_unpack.h"
24
#include "ccompat.h"
25
#include "crypto_core.h"
26
#include "forwarding.h"
27
#include "friend_connection.h"
28
#include "friend_requests.h"
29
#include "group_announce.h"
30
#include "group_chats.h"
31
#include "group_common.h"
32
#include "group_onion_announce.h"
33
#include "logger.h"
34
#include "mem.h"
35
#include "mono_time.h"
36
#include "net_crypto.h"
37
#include "net_profile.h"
38
#include "network.h"
39
#include "onion.h"
40
#include "onion_announce.h"
41
#include "onion_client.h"
42
#include "state.h"
43
#include "util.h"
44
45
static_assert(MAX_CONCURRENT_FILE_PIPES <= UINT8_MAX + 1,
46
              "uint8_t cannot represent all file transfer numbers");
47
48
static const Friend empty_friend = {{0}};
49
50
static const uint8_t *_Nullable nc_dht_get_shared_key_sent_wrapper(void *_Nonnull obj, const uint8_t *_Nonnull public_key)
51
957
{
52
957
    DHT *dht = (DHT *)obj;
53
957
    return dht_get_shared_key_sent(dht, public_key);
54
957
}
55
56
static const uint8_t *_Nonnull nc_dht_get_self_public_key_wrapper(const void *_Nonnull obj)
57
529
{
58
529
    const DHT *dht = (const DHT *)obj;
59
529
    return dht_get_self_public_key(dht);
60
529
}
61
62
static const uint8_t *_Nonnull nc_dht_get_self_secret_key_wrapper(const void *_Nonnull obj)
63
1.84k
{
64
1.84k
    const DHT *dht = (const DHT *)obj;
65
1.84k
    return dht_get_self_secret_key(dht);
66
1.84k
}
67
68
static const Net_Crypto_DHT_Funcs m_dht_funcs = {
69
    nc_dht_get_shared_key_sent_wrapper,
70
    nc_dht_get_self_public_key_wrapper,
71
    nc_dht_get_self_secret_key_wrapper,
72
};
73
74
/**
75
 * Determines if the friendnumber passed is valid in the Messenger object.
76
 *
77
 * @param friendnumber The index in the friend list.
78
 */
79
bool friend_is_valid(const Messenger *m, int32_t friendnumber)
80
127
{
81
127
    return (uint32_t)friendnumber < m->numfriends && m->friendlist[friendnumber].status != 0;
82
127
}
83
84
/** @brief Set the size of the friend list to numfriends.
85
 *
86
 * @retval -1 if mem_vrealloc fails.
87
 */
88
static int realloc_friendlist(Messenger *_Nonnull m, uint32_t num)
89
751
{
90
751
    if (num == 0) {
91
2
        mem_delete(m->mem, m->friendlist);
92
2
        m->friendlist = nullptr;
93
2
        return 0;
94
2
    }
95
96
749
    Friend *newfriendlist = (Friend *)mem_vrealloc(m->mem, m->friendlist, num, sizeof(Friend));
97
98
749
    if (newfriendlist == nullptr) {
99
0
        return -1;
100
0
    }
101
102
749
    m->friendlist = newfriendlist;
103
749
    return 0;
104
749
}
105
106
/** @return the friend number associated to that public key.
107
 * @retval -1 if no such friend.
108
 */
109
int32_t getfriend_id(const Messenger *m, const uint8_t *real_pk)
110
8.46k
{
111
15.4k
    for (uint32_t i = 0; i < m->numfriends; ++i) {
112
7.26k
        if (m->friendlist[i].status > 0 && pk_equal(real_pk, m->friendlist[i].real_pk)) {
113
323
            return i;
114
323
        }
115
7.26k
    }
116
117
8.14k
    return -1;
118
8.46k
}
119
120
/** @brief Copies the public key associated to that friend id into real_pk buffer.
121
 *
122
 * Make sure that real_pk is of size CRYPTO_PUBLIC_KEY_SIZE.
123
 *
124
 * @retval 0 if success.
125
 * @retval -1 if failure.
126
 */
127
int get_real_pk(const Messenger *m, int32_t friendnumber, uint8_t *real_pk)
128
3
{
129
3
    if (!m_friend_exists(m, friendnumber)) {
130
0
        return -1;
131
0
    }
132
133
3
    memcpy(real_pk, m->friendlist[friendnumber].real_pk, CRYPTO_PUBLIC_KEY_SIZE);
134
3
    return 0;
135
3
}
136
137
/** @return friend connection id on success.
138
 * @retval -1 if failure.
139
 */
140
int getfriendcon_id(const Messenger *m, int32_t friendnumber)
141
84
{
142
84
    if (!m_friend_exists(m, friendnumber)) {
143
0
        return -1;
144
0
    }
145
146
84
    return m->friendlist[friendnumber].friendcon_id;
147
84
}
148
149
/**
150
 * Format: `[real_pk (32 bytes)][nospam number (4 bytes)][checksum (2 bytes)]`
151
 *
152
 * @param[out] address FRIEND_ADDRESS_SIZE byte address to give to others.
153
 */
154
void getaddress(const Messenger *m, uint8_t *address)
155
113k
{
156
113k
    pk_copy(address, nc_get_self_public_key(m->net_crypto));
157
113k
    uint32_t nospam = get_nospam(m->fr);
158
113k
    memcpy(address + CRYPTO_PUBLIC_KEY_SIZE, &nospam, sizeof(nospam));
159
113k
    uint16_t checksum = data_checksum(address, FRIEND_ADDRESS_SIZE - sizeof(checksum));
160
113k
    memcpy(address + CRYPTO_PUBLIC_KEY_SIZE + sizeof(nospam), &checksum, sizeof(checksum));
161
113k
}
162
163
static bool send_online_packet(const Messenger *_Nonnull m, int friendcon_id)
164
872
{
165
872
    const uint8_t packet[1] = {PACKET_ID_ONLINE};
166
872
    return write_cryptpacket(m->net_crypto, friend_connection_crypt_connection_id(m->fr_c, friendcon_id), packet,
167
872
                             sizeof(packet), false) != -1;
168
872
}
169
170
static bool send_offline_packet(const Messenger *_Nonnull m, int friendcon_id)
171
1
{
172
1
    const uint8_t packet[1] = {PACKET_ID_OFFLINE};
173
1
    return write_cryptpacket(m->net_crypto, friend_connection_crypt_connection_id(m->fr_c, friendcon_id), packet,
174
1
                             sizeof(packet), false) != -1;
175
1
}
176
177
static int m_handle_status(void *_Nonnull object, int friendcon_id, bool status, void *_Nullable userdata);
178
static int m_handle_packet(void *_Nonnull object, int friendcon_id, const uint8_t *_Nonnull data, uint16_t length, void *_Nullable userdata);
179
static int m_handle_lossy_packet(void *_Nonnull object, int friendcon_id, const uint8_t *_Nonnull data, uint16_t length,
180
                                 void *_Nullable userdata);
181
static int32_t init_new_friend(Messenger *_Nonnull m, const uint8_t *_Nonnull real_pk, uint8_t status)
182
749
{
183
749
    if (m->numfriends == UINT32_MAX) {
184
0
        LOGGER_ERROR(m->log, "Friend list full: we have more than 4 billion friends");
185
        /* This is technically incorrect, but close enough. */
186
0
        return FAERR_NOMEM;
187
0
    }
188
189
    /* Resize the friend list if necessary. */
190
749
    if (realloc_friendlist(m, m->numfriends + 1) != 0) {
191
0
        return FAERR_NOMEM;
192
0
    }
193
194
749
    m->friendlist[m->numfriends] = empty_friend;
195
196
749
    const int friendcon_id = new_friend_connection(m->fr_c, real_pk);
197
198
749
    if (friendcon_id == -1) {
199
0
        return FAERR_NOMEM;
200
0
    }
201
202
1.90k
    for (uint32_t i = 0; i <= m->numfriends; ++i) {
203
1.90k
        if (m->friendlist[i].status == NOFRIEND) {
204
749
            m->friendlist[i].status = status;
205
749
            m->friendlist[i].friendcon_id = friendcon_id;
206
749
            m->friendlist[i].friendrequest_lastsent = 0;
207
749
            pk_copy(m->friendlist[i].real_pk, real_pk);
208
749
            m->friendlist[i].statusmessage_length = 0;
209
749
            m->friendlist[i].userstatus = USERSTATUS_NONE;
210
749
            m->friendlist[i].is_typing = false;
211
749
            m->friendlist[i].message_id = 0;
212
749
            friend_connection_callbacks(m->fr_c, friendcon_id, MESSENGER_CALLBACK_INDEX, &m_handle_status, &m_handle_packet,
213
749
                                        &m_handle_lossy_packet, m, i);
214
215
749
            if (m->numfriends == i) {
216
749
                ++m->numfriends;
217
749
            }
218
219
749
            if (friend_con_connected(m->fr_c, friendcon_id) == FRIENDCONN_STATUS_CONNECTED) {
220
0
                send_online_packet(m, friendcon_id);
221
0
            }
222
223
749
            return i;
224
749
        }
225
1.90k
    }
226
227
0
    return FAERR_NOMEM;
228
749
}
229
230
static int32_t m_add_friend_contact_norequest(Messenger *_Nonnull m, const uint8_t *_Nonnull real_pk)
231
576
{
232
576
    if (getfriend_id(m, real_pk) != -1) {
233
11
        return FAERR_ALREADYSENT;
234
11
    }
235
236
565
    if (pk_equal(real_pk, nc_get_self_public_key(m->net_crypto))) {
237
0
        return FAERR_OWNKEY;
238
0
    }
239
240
565
    return init_new_friend(m, real_pk, FRIEND_CONFIRMED);
241
565
}
242
243
/**
244
 * Add a friend.
245
 *
246
 * Set the data that will be sent along with friend request.
247
 *
248
 * @param address is the address of the friend (returned by getaddress of the friend
249
 *   you wish to add) it must be FRIEND_ADDRESS_SIZE bytes.
250
 * @param data is the data.
251
 * @param length is the length.
252
 *
253
 * @return the friend number if success.
254
 * @retval FA_TOOLONG if message length is too long.
255
 * @retval FAERR_NOMESSAGE if no message (message length must be >= 1 byte).
256
 * @retval FAERR_OWNKEY if user's own key.
257
 * @retval FAERR_ALREADYSENT if friend request already sent or already a friend.
258
 * @retval FAERR_BADCHECKSUM if bad checksum in address.
259
 * @retval FAERR_SETNEWNOSPAM if the friend was already there but the nospam was different.
260
 *   (the nospam for that friend was set to the new one).
261
 * @retval FAERR_NOMEM if increasing the friend list size fails.
262
 */
263
int32_t m_addfriend(Messenger *m, const uint8_t *address, const uint8_t *data, uint16_t length)
264
274
{
265
274
    if (length > MAX_FRIEND_REQUEST_DATA_SIZE) {
266
12
        return FAERR_TOOLONG;
267
12
    }
268
269
262
    uint8_t real_pk[CRYPTO_PUBLIC_KEY_SIZE];
270
262
    pk_copy(real_pk, address);
271
272
262
    if (!public_key_valid(real_pk)) {
273
10
        return FAERR_BADCHECKSUM;
274
10
    }
275
276
252
    uint16_t check;
277
252
    const uint16_t checksum = data_checksum(address, FRIEND_ADDRESS_SIZE - sizeof(checksum));
278
252
    memcpy(&check, address + CRYPTO_PUBLIC_KEY_SIZE + sizeof(uint32_t), sizeof(check));
279
280
252
    if (check != checksum) {
281
1
        return FAERR_BADCHECKSUM;
282
1
    }
283
284
251
    if (length < 1) {
285
11
        return FAERR_NOMESSAGE;
286
11
    }
287
288
240
    if (pk_equal(real_pk, nc_get_self_public_key(m->net_crypto))) {
289
11
        return FAERR_OWNKEY;
290
11
    }
291
292
229
    const int32_t friend_id = getfriend_id(m, real_pk);
293
294
229
    if (friend_id != -1) {
295
45
        if (m->friendlist[friend_id].status >= FRIEND_CONFIRMED) {
296
10
            return FAERR_ALREADYSENT;
297
10
        }
298
299
35
        uint32_t nospam;
300
35
        memcpy(&nospam, address + CRYPTO_PUBLIC_KEY_SIZE, sizeof(nospam));
301
302
35
        if (m->friendlist[friend_id].friendrequest_nospam == nospam) {
303
19
            return FAERR_ALREADYSENT;
304
19
        }
305
306
16
        m->friendlist[friend_id].friendrequest_nospam = nospam;
307
16
        return FAERR_SETNEWNOSPAM;
308
35
    }
309
310
184
    const int32_t ret = init_new_friend(m, real_pk, FRIEND_ADDED);
311
312
184
    if (ret < 0) {
313
0
        return ret;
314
0
    }
315
316
184
    m->friendlist[ret].friendrequest_timeout = FRIENDREQUEST_TIMEOUT;
317
184
    memcpy(m->friendlist[ret].info, data, length);
318
184
    m->friendlist[ret].info_size = length;
319
184
    memcpy(&m->friendlist[ret].friendrequest_nospam, address + CRYPTO_PUBLIC_KEY_SIZE, sizeof(uint32_t));
320
321
184
    return ret;
322
184
}
323
324
int32_t m_addfriend_norequest(Messenger *m, const uint8_t *real_pk)
325
617
{
326
617
    if (!public_key_valid(real_pk)) {
327
31
        return FAERR_BADCHECKSUM;
328
31
    }
329
330
586
    if (pk_equal(real_pk, nc_get_self_public_key(m->net_crypto))) {
331
10
        return FAERR_OWNKEY;
332
10
    }
333
334
576
    return m_add_friend_contact_norequest(m, real_pk);
335
586
}
336
337
static int clear_receipts(Messenger *_Nonnull m, int32_t friendnumber)
338
763
{
339
763
    if (!m_friend_exists(m, friendnumber)) {
340
0
        return -1;
341
0
    }
342
343
763
    struct Receipts *receipts = m->friendlist[friendnumber].receipts_start;
344
345
32.3k
    while (receipts != nullptr) {
346
31.5k
        struct Receipts *temp_r = receipts->next;
347
31.5k
        mem_delete(m->mem, receipts);
348
31.5k
        receipts = temp_r;
349
31.5k
    }
350
351
763
    m->friendlist[friendnumber].receipts_start = nullptr;
352
763
    m->friendlist[friendnumber].receipts_end = nullptr;
353
763
    return 0;
354
763
}
355
356
static int add_receipt(Messenger *_Nonnull m, int32_t friendnumber, uint32_t packet_num, uint32_t msg_id)
357
113k
{
358
113k
    if (!m_friend_exists(m, friendnumber)) {
359
0
        return -1;
360
0
    }
361
362
113k
    struct Receipts *new_receipts = (struct Receipts *)mem_alloc(m->mem, sizeof(struct Receipts));
363
364
113k
    if (new_receipts == nullptr) {
365
0
        return -1;
366
0
    }
367
368
113k
    new_receipts->packet_num = packet_num;
369
113k
    new_receipts->msg_id = msg_id;
370
371
113k
    if (m->friendlist[friendnumber].receipts_start == nullptr) {
372
11
        m->friendlist[friendnumber].receipts_start = new_receipts;
373
113k
    } else {
374
113k
        m->friendlist[friendnumber].receipts_end->next = new_receipts;
375
113k
    }
376
377
113k
    m->friendlist[friendnumber].receipts_end = new_receipts;
378
113k
    new_receipts->next = nullptr;
379
113k
    return 0;
380
113k
}
381
/**
382
 * return -1 on failure.
383
 * return 0 if packet was received.
384
 */
385
static int friend_received_packet(const Messenger *_Nonnull m, int32_t friendnumber, uint32_t number)
386
85.2k
{
387
85.2k
    if (!m_friend_exists(m, friendnumber)) {
388
0
        return -1;
389
0
    }
390
391
85.2k
    return cryptpacket_received(m->net_crypto, friend_connection_crypt_connection_id(m->fr_c,
392
85.2k
                                m->friendlist[friendnumber].friendcon_id), number);
393
85.2k
}
394
395
bool m_create_group_connection(Messenger *m, GC_Chat *chat)
396
119
{
397
119
    random_bytes(m->rng, chat->m_group_public_key, CRYPTO_PUBLIC_KEY_SIZE);
398
119
    const int friendcon_id = new_friend_connection(m->fr_c, chat->m_group_public_key);
399
400
119
    if (friendcon_id == -1) {
401
0
        return false;
402
0
    }
403
404
119
    const Friend_Conn *connection = get_conn(m->fr_c, friendcon_id);
405
406
119
    if (connection == nullptr) {
407
0
        return false;
408
0
    }
409
410
119
    chat->friend_connection_id = friendcon_id;
411
412
119
    if (friend_con_connected(m->fr_c, friendcon_id) == FRIENDCONN_STATUS_CONNECTED) {
413
0
        send_online_packet(m, friendcon_id);
414
0
    }
415
416
119
    const int onion_friend_number = friend_conn_get_onion_friendnum(connection);
417
119
    Onion_Friend *onion_friend = onion_get_friend(m->onion_c, (uint16_t)onion_friend_number);
418
419
119
    onion_friend_set_gc_public_key(onion_friend, get_chat_id(&chat->chat_public_key));
420
119
    onion_friend_set_gc_data(onion_friend, nullptr, 0);
421
422
119
    return true;
423
119
}
424
425
/**
426
 * Kills the friend connection for a groupchat.
427
 */
428
void m_kill_group_connection(Messenger *m, const GC_Chat *chat)
429
124
{
430
124
    remove_request_received(m->fr, chat->m_group_public_key);
431
432
124
    friend_connection_callbacks(m->fr_c, chat->friend_connection_id, MESSENGER_CALLBACK_INDEX, nullptr,
433
124
                                nullptr, nullptr, nullptr, 0);
434
435
124
    if (friend_con_connected(m->fr_c, chat->friend_connection_id) == FRIENDCONN_STATUS_CONNECTED) {
436
0
        send_offline_packet(m, chat->friend_connection_id);
437
0
    }
438
439
124
    kill_friend_connection(m->fr_c, chat->friend_connection_id);
440
124
}
441
442
static int do_receipts(Messenger *_Nonnull m, int32_t friendnumber, void *_Nullable userdata)
443
45.5k
{
444
45.5k
    if (!m_friend_exists(m, friendnumber)) {
445
0
        return -1;
446
0
    }
447
448
45.5k
    struct Receipts *receipts = m->friendlist[friendnumber].receipts_start;
449
450
127k
    while (receipts != nullptr) {
451
82.3k
        if (friend_received_packet(m, friendnumber, receipts->packet_num) == -1) {
452
689
            break;
453
689
        }
454
455
81.7k
        if (m->read_receipt != nullptr) {
456
81.7k
            m->read_receipt(m, friendnumber, receipts->msg_id, userdata);
457
81.7k
        }
458
459
81.7k
        struct Receipts *r_next = receipts->next;
460
461
81.7k
        mem_delete(m->mem, receipts);
462
463
81.7k
        m->friendlist[friendnumber].receipts_start = r_next;
464
465
81.7k
        receipts = r_next;
466
81.7k
    }
467
468
45.5k
    if (m->friendlist[friendnumber].receipts_start == nullptr) {
469
44.8k
        m->friendlist[friendnumber].receipts_end = nullptr;
470
44.8k
    }
471
472
45.5k
    return 0;
473
45.5k
}
474
475
/** @brief Remove a friend.
476
 *
477
 * @retval 0 if success.
478
 * @retval -1 if failure.
479
 */
480
int m_delfriend(Messenger *m, int32_t friendnumber)
481
2
{
482
2
    if (!m_friend_exists(m, friendnumber)) {
483
0
        return -1;
484
0
    }
485
486
2
    clear_receipts(m, friendnumber);
487
2
    remove_request_received(m->fr, m->friendlist[friendnumber].real_pk);
488
2
    friend_connection_callbacks(m->fr_c, m->friendlist[friendnumber].friendcon_id, MESSENGER_CALLBACK_INDEX, nullptr,
489
2
                                nullptr, nullptr, nullptr, 0);
490
491
2
    if (friend_con_connected(m->fr_c, m->friendlist[friendnumber].friendcon_id) == FRIENDCONN_STATUS_CONNECTED) {
492
1
        send_offline_packet(m, m->friendlist[friendnumber].friendcon_id);
493
1
    }
494
495
2
    kill_friend_connection(m->fr_c, m->friendlist[friendnumber].friendcon_id);
496
2
    m->friendlist[friendnumber] = empty_friend;
497
498
2
    uint32_t i;
499
500
4
    for (i = m->numfriends; i != 0; --i) {
501
2
        if (m->friendlist[i - 1].status != NOFRIEND) {
502
0
            break;
503
0
        }
504
2
    }
505
506
2
    m->numfriends = i;
507
508
2
    if (realloc_friendlist(m, m->numfriends) != 0) {
509
0
        return FAERR_NOMEM;
510
0
    }
511
512
2
    return 0;
513
2
}
514
515
int m_get_friend_connectionstatus(const Messenger *m, int32_t friendnumber)
516
124k
{
517
124k
    if (!m_friend_exists(m, friendnumber)) {
518
99
        return -1;
519
99
    }
520
521
123k
    if (m->friendlist[friendnumber].status != FRIEND_ONLINE) {
522
75.1k
        return CONNECTION_NONE;
523
75.1k
    }
524
525
48.8k
    bool direct_connected = false;
526
48.8k
    uint32_t num_online_relays = 0;
527
48.8k
    const int crypt_conn_id = friend_connection_crypt_connection_id(m->fr_c, m->friendlist[friendnumber].friendcon_id);
528
529
48.8k
    if (!crypto_connection_status(m->net_crypto, crypt_conn_id, &direct_connected, &num_online_relays)) {
530
0
        return CONNECTION_NONE;
531
0
    }
532
533
48.8k
    if (direct_connected) {
534
40.0k
        return CONNECTION_UDP;
535
40.0k
    }
536
537
8.79k
    if (num_online_relays != 0) {
538
6.23k
        return CONNECTION_TCP;
539
6.23k
    }
540
541
    /* if we have a valid friend connection but do not have an established connection
542
     * we leave the connection status unchanged until the friend connection is either
543
     * established or dropped.
544
     */
545
2.55k
    return m->friendlist[friendnumber].last_connection_udp_tcp;
546
8.79k
}
547
548
/**
549
 * Checks if there exists a friend with given friendnumber.
550
 *
551
 * @param friendnumber The index in the friend list.
552
 *
553
 * @retval true if friend exists.
554
 * @retval false if friend doesn't exist.
555
 */
556
bool m_friend_exists(const Messenger *m, int32_t friendnumber)
557
497k
{
558
497k
    return (unsigned int)friendnumber < m->numfriends && m->friendlist[friendnumber].status != 0;
559
497k
}
560
561
/** @brief Send a message of type to an online friend.
562
 *
563
 * @retval -1 if friend not valid.
564
 * @retval -2 if too large.
565
 * @retval -3 if friend not online.
566
 * @retval -4 if send failed (because queue is full).
567
 * @retval -5 if bad type.
568
 * @retval 0 if success.
569
 *
570
 * The value in message_id will be passed to your read_receipt callback when the other receives the message.
571
 */
572
int m_send_message_generic(Messenger *m, int32_t friendnumber, uint8_t type, const uint8_t *message, uint32_t length,
573
                           uint32_t *message_id)
574
120k
{
575
120k
    if (type > MESSAGE_ACTION) {
576
0
        LOGGER_WARNING(m->log, "message type %d is invalid", type);
577
0
        return -5;
578
0
    }
579
580
120k
    if (!m_friend_exists(m, friendnumber)) {
581
0
        LOGGER_WARNING(m->log, "friend number %d is invalid", friendnumber);
582
0
        return -1;
583
0
    }
584
585
120k
    if (length >= MAX_CRYPTO_DATA_SIZE) {
586
1
        LOGGER_WARNING(m->log, "message length %u is too large", length);
587
1
        return -2;
588
1
    }
589
590
120k
    if (m->friendlist[friendnumber].status != FRIEND_ONLINE) {
591
0
        LOGGER_WARNING(m->log, "friend %d is not online", friendnumber);
592
0
        return -3;
593
0
    }
594
595
120k
    VLA(uint8_t, packet, length + 1);
596
120k
    packet[0] = PACKET_ID_MESSAGE + type;
597
598
120k
    assert(message != nullptr);
599
120k
    memcpy(packet + 1, message, length);
600
601
120k
    const int64_t packet_num = write_cryptpacket(m->net_crypto, friend_connection_crypt_connection_id(m->fr_c,
602
120k
                               m->friendlist[friendnumber].friendcon_id), packet, length + 1, false);
603
604
120k
    if (packet_num == -1) {
605
7.23k
        return -4;
606
7.23k
    }
607
608
113k
    const uint32_t msg_id = ++m->friendlist[friendnumber].message_id;
609
610
113k
    add_receipt(m, friendnumber, packet_num, msg_id);
611
612
113k
    if (message_id != nullptr) {
613
113k
        *message_id = msg_id;
614
113k
    }
615
616
113k
    return 0;
617
120k
}
618
619
static bool write_cryptpacket_id(const Messenger *_Nonnull m, int32_t friendnumber, uint8_t packet_id, const uint8_t *_Nonnull data, uint32_t length, bool congestion_control)
620
1.82k
{
621
1.82k
    if (!m_friend_exists(m, friendnumber)) {
622
0
        return false;
623
0
    }
624
625
1.82k
    if (length >= MAX_CRYPTO_DATA_SIZE || m->friendlist[friendnumber].status != FRIEND_ONLINE) {
626
0
        return false;
627
0
    }
628
629
1.82k
    VLA(uint8_t, packet, length + 1);
630
1.82k
    packet[0] = packet_id;
631
632
1.82k
    assert(data != nullptr);
633
1.82k
    memcpy(packet + 1, data, length);
634
635
1.82k
    return write_cryptpacket(m->net_crypto, friend_connection_crypt_connection_id(m->fr_c,
636
1.82k
                             m->friendlist[friendnumber].friendcon_id), packet, length + 1, congestion_control) != -1;
637
1.82k
}
638
639
/** @brief Send a name packet to friendnumber.
640
 * length is the length with the NULL terminator.
641
 */
642
static bool m_sendname(const Messenger *_Nonnull m, int32_t friendnumber, const uint8_t *_Nonnull name, uint16_t length)
643
436
{
644
436
    if (length > MAX_NAME_LENGTH) {
645
0
        return false;
646
0
    }
647
648
436
    return write_cryptpacket_id(m, friendnumber, PACKET_ID_NICKNAME, name, length, false);
649
436
}
650
651
/** @brief Set the name and name_length of a friend.
652
 *
653
 * name must be a string of maximum MAX_NAME_LENGTH length.
654
 * length must be at least 1 byte.
655
 * length is the length of name with the NULL terminator.
656
 *
657
 * @retval 0 if success.
658
 * @retval -1 if failure.
659
 */
660
int setfriendname(Messenger *m, int32_t friendnumber, const uint8_t *name, uint16_t length)
661
279
{
662
279
    if (!m_friend_exists(m, friendnumber)) {
663
0
        return -1;
664
0
    }
665
666
279
    if (length > MAX_NAME_LENGTH || length == 0) {
667
229
        return -1;
668
229
    }
669
670
50
    m->friendlist[friendnumber].name_length = length;
671
50
    memcpy(m->friendlist[friendnumber].name, name, length);
672
50
    return 0;
673
279
}
674
675
/** @brief Set our nickname.
676
 *
677
 * name must be a string of maximum MAX_NAME_LENGTH length.
678
 * length must be at least 1 byte.
679
 * length is the length of name with the NULL terminator.
680
 *
681
 * @retval 0 if success.
682
 * @retval -1 if failure.
683
 */
684
int setname(Messenger *m, const uint8_t *name, uint16_t length)
685
328
{
686
328
    if (length > MAX_NAME_LENGTH) {
687
0
        return -1;
688
0
    }
689
690
328
    if (m->name_length == length && (length == 0 || memcmp(name, m->name, length) == 0)) {
691
83
        return 0;
692
83
    }
693
694
245
    if (length > 0) {
695
245
        memcpy(m->name, name, length);
696
245
    }
697
698
245
    m->name_length = length;
699
700
581
    for (uint32_t i = 0; i < m->numfriends; ++i) {
701
336
        m->friendlist[i].name_sent = false;
702
336
    }
703
704
245
    return 0;
705
328
}
706
707
/**
708
 * @brief Get your nickname.
709
 *
710
 * m - The messenger context to use.
711
 * name needs to be a valid memory location with a size of at least MAX_NAME_LENGTH bytes.
712
 *
713
 * @return length of the name.
714
 * @retval 0 on error.
715
 */
716
uint16_t getself_name(const Messenger *m, uint8_t *name)
717
4
{
718
4
    if (name == nullptr) {
719
0
        return 0;
720
0
    }
721
722
4
    memcpy(name, m->name, m->name_length);
723
724
4
    return m->name_length;
725
4
}
726
727
/** @brief Get name of friendnumber and put it in name.
728
 *
729
 * name needs to be a valid memory location with a size of at least MAX_NAME_LENGTH (128) bytes.
730
 *
731
 * @return length of name if success.
732
 * @retval -1 if failure.
733
 */
734
int getname(const Messenger *m, int32_t friendnumber, uint8_t *name)
735
3
{
736
3
    if (!m_friend_exists(m, friendnumber)) {
737
0
        return -1;
738
0
    }
739
740
3
    memcpy(name, m->friendlist[friendnumber].name, m->friendlist[friendnumber].name_length);
741
3
    return m->friendlist[friendnumber].name_length;
742
3
}
743
744
int m_get_name_size(const Messenger *m, int32_t friendnumber)
745
5
{
746
5
    if (!m_friend_exists(m, friendnumber)) {
747
0
        return -1;
748
0
    }
749
750
5
    return m->friendlist[friendnumber].name_length;
751
5
}
752
753
int m_get_self_name_size(const Messenger *m)
754
6
{
755
6
    return m->name_length;
756
6
}
757
758
int m_set_statusmessage(Messenger *m, const uint8_t *status, uint16_t length)
759
628
{
760
628
    if (length > MAX_STATUSMESSAGE_LENGTH) {
761
0
        return -1;
762
0
    }
763
764
628
    if (m->statusmessage_length == length && (length == 0 || memcmp(m->statusmessage, status, length) == 0)) {
765
164
        return 0;
766
164
    }
767
768
464
    if (length > 0) {
769
464
        memcpy(m->statusmessage, status, length);
770
464
    }
771
772
464
    m->statusmessage_length = length;
773
774
817
    for (uint32_t i = 0; i < m->numfriends; ++i) {
775
353
        m->friendlist[i].statusmessage_sent = false;
776
353
    }
777
778
464
    return 0;
779
628
}
780
781
static bool userstatus_from_int(uint8_t status, Userstatus *_Nonnull out_enum)
782
1.31k
{
783
1.31k
    switch (status) {
784
1.05k
        case USERSTATUS_NONE: {
785
1.05k
            *out_enum = USERSTATUS_NONE;
786
1.05k
            return true;
787
0
        }
788
789
9
        case USERSTATUS_AWAY: {
790
9
            *out_enum = USERSTATUS_AWAY;
791
9
            return true;
792
0
        }
793
794
84
        case USERSTATUS_BUSY: {
795
84
            *out_enum = USERSTATUS_BUSY;
796
84
            return true;
797
0
        }
798
799
0
        case USERSTATUS_INVALID: {
800
0
            *out_enum = USERSTATUS_INVALID;
801
0
            return true;
802
0
        }
803
804
163
        default: {
805
163
            *out_enum = USERSTATUS_INVALID;
806
163
            return false;
807
0
        }
808
1.31k
    }
809
1.31k
}
810
811
int m_set_userstatus(Messenger *m, uint8_t status)
812
590
{
813
590
    if (status >= USERSTATUS_INVALID) {
814
258
        return -1;
815
258
    }
816
817
332
    if (m->userstatus == status) {
818
169
        return 0;
819
169
    }
820
821
163
    userstatus_from_int(status, &m->userstatus);
822
823
332
    for (uint32_t i = 0; i < m->numfriends; ++i) {
824
169
        m->friendlist[i].userstatus_sent = false;
825
169
    }
826
827
163
    return 0;
828
332
}
829
830
/**
831
 * Guaranteed to be at most MAX_STATUSMESSAGE_LENGTH.
832
 *
833
 * @return the length of friendnumber's status message, including null on success.
834
 * @retval -1 on failure.
835
 */
836
int m_get_statusmessage_size(const Messenger *m, int32_t friendnumber)
837
9
{
838
9
    if (!m_friend_exists(m, friendnumber)) {
839
0
        return -1;
840
0
    }
841
842
9
    return m->friendlist[friendnumber].statusmessage_length;
843
9
}
844
845
/** @brief Copy friendnumber's status message into buf, truncating if size is over maxlen.
846
 *
847
 * Get the size you need to allocate from m_get_statusmessage_size.
848
 * The self variant will copy our own status message.
849
 *
850
 * @return the length of the copied data on success
851
 * @retval -1 on failure.
852
 */
853
int m_copy_statusmessage(const Messenger *m, int32_t friendnumber, uint8_t *buf, uint32_t maxlen)
854
3
{
855
3
    if (!m_friend_exists(m, friendnumber)) {
856
0
        return -1;
857
0
    }
858
859
    // TODO(iphydf): This should be uint16_t and min_u16. If maxlen exceeds
860
    // uint16_t's range, it won't affect the result.
861
3
    const uint32_t msglen = min_u32(maxlen, m->friendlist[friendnumber].statusmessage_length);
862
863
3
    memcpy(buf, m->friendlist[friendnumber].statusmessage, msglen);
864
3
    memzero(buf + msglen, maxlen - msglen);
865
3
    return msglen;
866
3
}
867
868
/** @return the size of friendnumber's user status.
869
 * Guaranteed to be at most MAX_STATUSMESSAGE_LENGTH.
870
 */
871
int m_get_self_statusmessage_size(const Messenger *m)
872
4
{
873
4
    return m->statusmessage_length;
874
4
}
875
876
int m_copy_self_statusmessage(const Messenger *m, uint8_t *buf)
877
2
{
878
2
    memcpy(buf, m->statusmessage, m->statusmessage_length);
879
2
    return m->statusmessage_length;
880
2
}
881
882
uint8_t m_get_userstatus(const Messenger *m, int32_t friendnumber)
883
0
{
884
0
    if (!m_friend_exists(m, friendnumber)) {
885
0
        return USERSTATUS_INVALID;
886
0
    }
887
888
0
    uint8_t status = m->friendlist[friendnumber].userstatus;
889
890
0
    if (status >= USERSTATUS_INVALID) {
891
0
        status = USERSTATUS_NONE;
892
0
    }
893
894
0
    return status;
895
0
}
896
897
uint8_t m_get_self_userstatus(const Messenger *m)
898
3
{
899
3
    return m->userstatus;
900
3
}
901
902
uint64_t m_get_last_online(const Messenger *m, int32_t friendnumber)
903
1
{
904
1
    if (!m_friend_exists(m, friendnumber)) {
905
0
        return UINT64_MAX;
906
0
    }
907
908
1
    return m->friendlist[friendnumber].last_seen_time;
909
1
}
910
911
int m_set_usertyping(Messenger *m, int32_t friendnumber, bool is_typing)
912
2
{
913
2
    if (!m_friend_exists(m, friendnumber)) {
914
0
        return -1;
915
0
    }
916
917
2
    if (m->friendlist[friendnumber].user_istyping == is_typing) {
918
0
        return 0;
919
0
    }
920
921
2
    m->friendlist[friendnumber].user_istyping = is_typing;
922
2
    m->friendlist[friendnumber].user_istyping_sent = false;
923
924
2
    return 0;
925
2
}
926
927
int m_get_istyping(const Messenger *m, int32_t friendnumber)
928
14
{
929
14
    if (!m_friend_exists(m, friendnumber)) {
930
0
        return -1;
931
0
    }
932
933
14
    return m->friendlist[friendnumber].is_typing ? 1 : 0;
934
14
}
935
936
static bool send_statusmessage(const Messenger *_Nonnull m, int32_t friendnumber, const uint8_t *_Nonnull status, uint16_t length)
937
435
{
938
435
    return write_cryptpacket_id(m, friendnumber, PACKET_ID_STATUSMESSAGE, status, length, false);
939
435
}
940
941
static bool send_userstatus(const Messenger *_Nonnull m, int32_t friendnumber, uint8_t status)
942
437
{
943
437
    return write_cryptpacket_id(m, friendnumber, PACKET_ID_USERSTATUS, &status, sizeof(status), false);
944
437
}
945
946
static bool send_user_istyping(const Messenger *_Nonnull m, int32_t friendnumber, bool is_typing)
947
436
{
948
436
    const uint8_t typing = is_typing ? 1 : 0;
949
436
    return write_cryptpacket_id(m, friendnumber, PACKET_ID_TYPING, &typing, sizeof(typing), false);
950
436
}
951
952
static int set_friend_statusmessage(const Messenger *_Nonnull m, int32_t friendnumber, const uint8_t *_Nonnull status, uint16_t length)
953
713
{
954
713
    if (!m_friend_exists(m, friendnumber)) {
955
0
        return -1;
956
0
    }
957
958
713
    if (length > MAX_STATUSMESSAGE_LENGTH) {
959
167
        return -1;
960
167
    }
961
962
546
    if (length > 0) {
963
59
        memcpy(m->friendlist[friendnumber].statusmessage, status, length);
964
59
    }
965
966
546
    m->friendlist[friendnumber].statusmessage_length = length;
967
546
    return 0;
968
713
}
969
970
static void set_friend_userstatus(const Messenger *_Nonnull m, int32_t friendnumber, uint8_t status)
971
715
{
972
715
    userstatus_from_int(status, &m->friendlist[friendnumber].userstatus);
973
715
}
974
975
static void set_friend_typing(const Messenger *_Nonnull m, int32_t friendnumber, bool is_typing)
976
435
{
977
435
    m->friendlist[friendnumber].is_typing = is_typing;
978
435
}
979
980
/** Set the function that will be executed when a friend request is received. */
981
void m_callback_friendrequest(Messenger *m, m_friend_request_cb *function)
982
1.40k
{
983
1.40k
    m->friend_request = function;
984
1.40k
}
985
986
/** Set the function that will be executed when a message from a friend is received. */
987
void m_callback_friendmessage(Messenger *m, m_friend_message_cb *function)
988
1.40k
{
989
1.40k
    m->friend_message = function;
990
1.40k
}
991
992
void m_callback_namechange(Messenger *m, m_friend_name_cb *function)
993
1.40k
{
994
1.40k
    m->friend_namechange = function;
995
1.40k
}
996
997
void m_callback_statusmessage(Messenger *m, m_friend_status_message_cb *function)
998
1.40k
{
999
1.40k
    m->friend_statusmessagechange = function;
1000
1.40k
}
1001
1002
void m_callback_userstatus(Messenger *m, m_friend_status_cb *function)
1003
1.40k
{
1004
1.40k
    m->friend_userstatuschange = function;
1005
1.40k
}
1006
1007
void m_callback_typingchange(Messenger *m, m_friend_typing_cb *function)
1008
1.40k
{
1009
1.40k
    m->friend_typingchange = function;
1010
1.40k
}
1011
1012
void m_callback_read_receipt(Messenger *m, m_friend_read_receipt_cb *function)
1013
1.40k
{
1014
1.40k
    m->read_receipt = function;
1015
1.40k
}
1016
1017
void m_callback_connectionstatus(Messenger *m, m_friend_connection_status_cb *function)
1018
1.40k
{
1019
1.40k
    m->friend_connectionstatuschange = function;
1020
1.40k
}
1021
1022
void m_callback_core_connection(Messenger *m, m_self_connection_status_cb *function)
1023
1.40k
{
1024
1.40k
    m->core_connection_change = function;
1025
1.40k
}
1026
1027
static void check_friend_tcp_udp(Messenger *_Nonnull m, int32_t friendnumber, void *_Nullable userdata)
1028
46.0k
{
1029
46.0k
    const int last_connection_udp_tcp = m->friendlist[friendnumber].last_connection_udp_tcp;
1030
46.0k
    const int ret = m_get_friend_connectionstatus(m, friendnumber);
1031
1032
46.0k
    if (ret == -1) {
1033
0
        return;
1034
0
    }
1035
1036
46.0k
    if (last_connection_udp_tcp != ret) {
1037
450
        if (m->friend_connectionstatuschange != nullptr) {
1038
450
            m->friend_connectionstatuschange(m, friendnumber, ret, userdata);
1039
450
        }
1040
450
    }
1041
1042
46.0k
    m->friendlist[friendnumber].last_connection_udp_tcp = (Connection_Status)ret;
1043
46.0k
}
1044
1045
static void break_files(const Messenger *_Nonnull m, int32_t friendnumber);
1046
1047
static void check_friend_connectionstatus(Messenger *_Nonnull m, int32_t friendnumber, uint8_t status, void *_Nullable userdata)
1048
788
{
1049
788
    if (status == NOFRIEND) {
1050
0
        return;
1051
0
    }
1052
1053
788
    const bool was_online = m->friendlist[friendnumber].status == FRIEND_ONLINE;
1054
788
    const bool is_online = status == FRIEND_ONLINE;
1055
1056
788
    if (is_online != was_online) {
1057
450
        if (was_online) {
1058
14
            break_files(m, friendnumber);
1059
14
            clear_receipts(m, friendnumber);
1060
436
        } else {
1061
436
            m->friendlist[friendnumber].name_sent = false;
1062
436
            m->friendlist[friendnumber].userstatus_sent = false;
1063
436
            m->friendlist[friendnumber].statusmessage_sent = false;
1064
436
            m->friendlist[friendnumber].user_istyping_sent = false;
1065
436
        }
1066
1067
450
        m->friendlist[friendnumber].status = status;
1068
1069
450
        check_friend_tcp_udp(m, friendnumber, userdata);
1070
450
    }
1071
788
}
1072
1073
static void set_friend_status(Messenger *_Nonnull m, int32_t friendnumber, uint8_t status, void *_Nullable userdata)
1074
788
{
1075
788
    check_friend_connectionstatus(m, friendnumber, status, userdata);
1076
788
    m->friendlist[friendnumber].status = status;
1077
788
}
1078
1079
/*** CONFERENCES */
1080
1081
/** @brief Set the callback for conference invites. */
1082
void m_callback_conference_invite(Messenger *m, m_conference_invite_cb *function)
1083
3.58k
{
1084
3.58k
    m->conference_invite = function;
1085
3.58k
}
1086
1087
/** @brief the callback for group invites. */
1088
void m_callback_group_invite(Messenger *m, m_group_invite_cb *function)
1089
1.40k
{
1090
1.40k
    m->group_invite = function;
1091
1.40k
}
1092
1093
/** @brief Send a conference invite packet.
1094
 *
1095
 * return true on success
1096
 * return false on failure
1097
 */
1098
bool send_conference_invite_packet(const Messenger *m, int32_t friendnumber, const uint8_t *data, uint16_t length)
1099
58
{
1100
58
    return write_cryptpacket_id(m, friendnumber, PACKET_ID_INVITE_CONFERENCE, data, length, false);
1101
58
}
1102
1103
/** @brief Send a group invite packet.
1104
 *
1105
 * @retval true if success
1106
 */
1107
bool send_group_invite_packet(const Messenger *m, uint32_t friendnumber, const uint8_t *packet, uint16_t length)
1108
9
{
1109
9
    return write_cryptpacket_id(m, friendnumber, PACKET_ID_INVITE_GROUPCHAT, packet, length, false);
1110
9
}
1111
1112
/*** FILE SENDING */
1113
1114
/** @brief Set the callback for file send requests. */
1115
void callback_file_sendrequest(Messenger *m, m_file_recv_cb *function)
1116
1.40k
{
1117
1.40k
    m->file_sendrequest = function;
1118
1.40k
}
1119
1120
/** @brief Set the callback for file control requests. */
1121
void callback_file_control(Messenger *m, m_file_recv_control_cb *function)
1122
1.40k
{
1123
1.40k
    m->file_filecontrol = function;
1124
1.40k
}
1125
1126
/** @brief Set the callback for file data. */
1127
void callback_file_data(Messenger *m, m_file_recv_chunk_cb *function)
1128
1.40k
{
1129
1.40k
    m->file_filedata = function;
1130
1.40k
}
1131
1132
/** @brief Set the callback for file request chunk. */
1133
void callback_file_reqchunk(Messenger *m, m_file_chunk_request_cb *function)
1134
1.40k
{
1135
1.40k
    m->file_reqchunk = function;
1136
1.40k
}
1137
1138
12
#define MAX_FILENAME_LENGTH 255
1139
1140
/** @brief Copy the file transfer file id to file_id
1141
 *
1142
 * @retval 0 on success.
1143
 * @retval -1 if friend not valid.
1144
 * @retval -2 if filenumber not valid
1145
 */
1146
int file_get_id(const Messenger *m, int32_t friendnumber, uint32_t filenumber, uint8_t *file_id)
1147
1
{
1148
1
    if (!m_friend_exists(m, friendnumber)) {
1149
0
        return -1;
1150
0
    }
1151
1152
1
    if (m->friendlist[friendnumber].status != FRIEND_ONLINE) {
1153
0
        return -2;
1154
0
    }
1155
1156
1
    uint32_t temp_filenum;
1157
1
    bool inbound;
1158
1
    uint8_t file_number;
1159
1160
1
    if (filenumber >= (1 << 16)) {
1161
0
        inbound = true;
1162
0
        temp_filenum = (filenumber >> 16) - 1;
1163
1
    } else {
1164
1
        inbound = false;
1165
1
        temp_filenum = filenumber;
1166
1
    }
1167
1168
1
    if (temp_filenum >= MAX_CONCURRENT_FILE_PIPES) {
1169
0
        return -2;
1170
0
    }
1171
1172
1
    file_number = temp_filenum;
1173
1174
1
    const struct File_Transfers *const ft = inbound
1175
1
                                                ? &m->friendlist[friendnumber].file_receiving[file_number]
1176
1
                                                : &m->friendlist[friendnumber].file_sending[file_number];
1177
1178
1
    if (ft->status == FILESTATUS_NONE) {
1179
0
        return -2;
1180
0
    }
1181
1182
1
    memcpy(file_id, ft->id, FILE_ID_LENGTH);
1183
1
    return 0;
1184
1
}
1185
1186
int32_t file_by_id(const Messenger *m, uint32_t friendnumber, const uint8_t *file_id)
1187
3
{
1188
3
    if (friendnumber >= m->numfriends || m->friendlist[friendnumber].status == 0) {
1189
1
        return -1;
1190
1
    }
1191
1192
258
    for (uint32_t j = 0; j < MAX_CONCURRENT_FILE_PIPES; ++j) {
1193
257
        if (m->friendlist[friendnumber].file_sending[j].status != FILESTATUS_NONE) {
1194
2
            if (memcmp(m->friendlist[friendnumber].file_sending[j].id, file_id, FILE_ID_LENGTH) == 0) {
1195
1
                return (int32_t)j;
1196
1
            }
1197
2
        }
1198
1199
256
        if (m->friendlist[friendnumber].file_receiving[j].status != FILESTATUS_NONE) {
1200
0
            if (memcmp(m->friendlist[friendnumber].file_receiving[j].id, file_id, FILE_ID_LENGTH) == 0) {
1201
0
                return (int32_t)((j + 1) << 16);
1202
0
            }
1203
0
        }
1204
256
    }
1205
1206
1
    return -2;
1207
2
}
1208
1209
/** @brief Send a file send request.
1210
 * Maximum filename length is 255 bytes.
1211
 * @retval 1 on success
1212
 * @retval 0 on failure
1213
 */
1214
static bool file_sendrequest(const Messenger *_Nonnull m, int32_t friendnumber, uint8_t filenumber, uint32_t file_type, uint64_t filesize, const uint8_t *_Nonnull file_id,
1215
                             const uint8_t *_Nonnull filename, uint16_t filename_length)
1216
4
{
1217
4
    if (!m_friend_exists(m, friendnumber)) {
1218
0
        return false;
1219
0
    }
1220
1221
4
    if (filename_length > MAX_FILENAME_LENGTH) {
1222
0
        return false;
1223
0
    }
1224
1225
4
    const uint16_t packet_size = 1 + sizeof(file_type) + sizeof(filesize) + FILE_ID_LENGTH + filename_length;
1226
4
    VLA(uint8_t, packet, packet_size);
1227
4
    packet[0] = filenumber;
1228
4
    file_type = net_htonl(file_type);
1229
4
    memcpy(packet + 1, &file_type, sizeof(file_type));
1230
4
    net_pack_u64(packet + 1 + sizeof(file_type), filesize);
1231
4
    memcpy(packet + 1 + sizeof(file_type) + sizeof(filesize), file_id, FILE_ID_LENGTH);
1232
1233
4
    if (filename_length > 0) {
1234
4
        memcpy(packet + 1 + sizeof(file_type) + sizeof(filesize) + FILE_ID_LENGTH, filename, filename_length);
1235
4
    }
1236
1237
4
    return write_cryptpacket_id(m, friendnumber, PACKET_ID_FILE_SENDREQUEST, packet, packet_size, false);
1238
4
}
1239
1240
/** @brief Send a file send request.
1241
 *
1242
 * Maximum filename length is 255 bytes.
1243
 *
1244
 * @return file number on success
1245
 * @retval -1 if friend not found.
1246
 * @retval -2 if filename length invalid.
1247
 * @retval -3 if no more file sending slots left.
1248
 * @retval -4 if could not send packet (friend offline).
1249
 */
1250
long int new_filesender(const Messenger *m, int32_t friendnumber, uint32_t file_type, uint64_t filesize,
1251
                        const uint8_t *file_id, const uint8_t *filename, uint16_t filename_length)
1252
4
{
1253
4
    if (!m_friend_exists(m, friendnumber)) {
1254
0
        return -1;
1255
0
    }
1256
1257
4
    if (filename_length > MAX_FILENAME_LENGTH) {
1258
0
        return -2;
1259
0
    }
1260
1261
4
    uint32_t i;
1262
1263
4
    for (i = 0; i < MAX_CONCURRENT_FILE_PIPES; ++i) {
1264
4
        if (m->friendlist[friendnumber].file_sending[i].status == FILESTATUS_NONE) {
1265
4
            break;
1266
4
        }
1267
4
    }
1268
1269
4
    if (i == MAX_CONCURRENT_FILE_PIPES) {
1270
0
        return -3;
1271
0
    }
1272
1273
4
    if (!file_sendrequest(m, friendnumber, i, file_type, filesize, file_id, filename, filename_length)) {
1274
0
        return -4;
1275
0
    }
1276
1277
4
    struct File_Transfers *ft = &m->friendlist[friendnumber].file_sending[i];
1278
1279
4
    ft->status = FILESTATUS_NOT_ACCEPTED;
1280
1281
4
    ft->size = filesize;
1282
1283
4
    ft->transferred = 0;
1284
1285
4
    ft->requested = 0;
1286
1287
4
    ft->paused = FILE_PAUSE_NOT;
1288
1289
4
    memcpy(ft->id, file_id, FILE_ID_LENGTH);
1290
1291
4
    return i;
1292
4
}
1293
1294
static bool send_file_control_packet(const Messenger *_Nonnull m, int32_t friendnumber, bool inbound, uint8_t filenumber,
1295
                                     uint8_t control_type, const uint8_t *_Nullable data, uint16_t data_length)
1296
5
{
1297
5
    assert(data_length == 0 || data != nullptr);
1298
5
    if ((unsigned int)(1 + 3 + data_length) > MAX_CRYPTO_DATA_SIZE) {
1299
0
        return false;
1300
0
    }
1301
1302
5
    const uint16_t packet_size = 3 + data_length;
1303
5
    VLA(uint8_t, packet, packet_size);
1304
1305
5
    packet[0] = inbound ? 1 : 0;
1306
5
    packet[1] = filenumber;
1307
5
    packet[2] = control_type;
1308
1309
5
    if (data_length > 0) {
1310
1
        memcpy(packet + 3, data, data_length);
1311
1
    }
1312
1313
5
    return write_cryptpacket_id(m, friendnumber, PACKET_ID_FILE_CONTROL, packet, packet_size, false);
1314
5
}
1315
1316
/** @brief Send a file control request.
1317
 *
1318
 * @retval 0 on success
1319
 * @retval -1 if friend not valid.
1320
 * @retval -2 if friend not online.
1321
 * @retval -3 if file number invalid.
1322
 * @retval -4 if file control is bad.
1323
 * @retval -5 if file already paused.
1324
 * @retval -6 if resume file failed because it was only paused by the other.
1325
 * @retval -7 if resume file failed because it wasn't paused.
1326
 * @retval -8 if packet failed to send.
1327
 */
1328
int file_control(const Messenger *m, int32_t friendnumber, uint32_t filenumber, unsigned int control)
1329
4
{
1330
4
    if (!m_friend_exists(m, friendnumber)) {
1331
0
        return -1;
1332
0
    }
1333
1334
4
    if (m->friendlist[friendnumber].status != FRIEND_ONLINE) {
1335
0
        return -2;
1336
0
    }
1337
1338
4
    uint32_t temp_filenum;
1339
4
    bool inbound;
1340
4
    uint8_t file_number;
1341
1342
4
    if (filenumber >= (1 << 16)) {
1343
4
        inbound = true;
1344
4
        temp_filenum = (filenumber >> 16) - 1;
1345
4
    } else {
1346
0
        inbound = false;
1347
0
        temp_filenum = filenumber;
1348
0
    }
1349
1350
4
    if (temp_filenum >= MAX_CONCURRENT_FILE_PIPES) {
1351
0
        return -3;
1352
0
    }
1353
1354
4
    file_number = temp_filenum;
1355
1356
4
    struct File_Transfers *ft;
1357
1358
4
    if (inbound) {
1359
4
        ft = &m->friendlist[friendnumber].file_receiving[file_number];
1360
4
    } else {
1361
0
        ft = &m->friendlist[friendnumber].file_sending[file_number];
1362
0
    }
1363
1364
4
    if (ft->status == FILESTATUS_NONE) {
1365
0
        return -3;
1366
0
    }
1367
1368
4
    if (control > FILECONTROL_KILL) {
1369
0
        return -4;
1370
0
    }
1371
1372
4
    if (control == FILECONTROL_PAUSE && ((ft->paused & FILE_PAUSE_US) != 0 || ft->status != FILESTATUS_TRANSFERRING)) {
1373
0
        return -5;
1374
0
    }
1375
1376
4
    if (control == FILECONTROL_ACCEPT) {
1377
3
        if (ft->status == FILESTATUS_TRANSFERRING) {
1378
0
            if ((ft->paused & FILE_PAUSE_US) == 0) {
1379
0
                if ((ft->paused & FILE_PAUSE_OTHER) != 0) {
1380
0
                    return -6;
1381
0
                }
1382
1383
0
                return -7;
1384
0
            }
1385
3
        } else {
1386
3
            if (ft->status != FILESTATUS_NOT_ACCEPTED) {
1387
0
                return -7;
1388
0
            }
1389
1390
3
            if (!inbound) {
1391
0
                return -6;
1392
0
            }
1393
3
        }
1394
3
    }
1395
1396
4
    if (send_file_control_packet(m, friendnumber, inbound, file_number, control, nullptr, 0)) {
1397
4
        switch (control) {
1398
1
            case FILECONTROL_KILL: {
1399
1
                if (!inbound && (ft->status == FILESTATUS_TRANSFERRING || ft->status == FILESTATUS_FINISHED)) {
1400
                    // We are actively sending that file, remove from list
1401
0
                    --m->friendlist[friendnumber].num_sending_files;
1402
0
                }
1403
1404
1
                ft->status = FILESTATUS_NONE;
1405
1
                break;
1406
0
            }
1407
0
            case FILECONTROL_PAUSE: {
1408
0
                ft->paused |= FILE_PAUSE_US;
1409
0
                break;
1410
0
            }
1411
3
            case FILECONTROL_ACCEPT: {
1412
3
                ft->status = FILESTATUS_TRANSFERRING;
1413
1414
3
                if ((ft->paused & FILE_PAUSE_US) != 0) {
1415
0
                    ft->paused ^= FILE_PAUSE_US;
1416
0
                }
1417
3
                break;
1418
0
            }
1419
4
        }
1420
4
    } else {
1421
0
        return -8;
1422
0
    }
1423
1424
4
    return 0;
1425
4
}
1426
1427
/** @brief Send a seek file control request.
1428
 *
1429
 * @retval 0 on success
1430
 * @retval -1 if friend not valid.
1431
 * @retval -2 if friend not online.
1432
 * @retval -3 if file number invalid.
1433
 * @retval -4 if not receiving file.
1434
 * @retval -5 if file status wrong.
1435
 * @retval -6 if position bad.
1436
 * @retval -8 if packet failed to send.
1437
 */
1438
int file_seek(const Messenger *m, int32_t friendnumber, uint32_t filenumber, uint64_t position)
1439
1
{
1440
1
    if (!m_friend_exists(m, friendnumber)) {
1441
0
        return -1;
1442
0
    }
1443
1444
1
    if (m->friendlist[friendnumber].status != FRIEND_ONLINE) {
1445
0
        return -2;
1446
0
    }
1447
1448
1
    if (filenumber < (1 << 16)) {
1449
        // Not receiving.
1450
0
        return -4;
1451
0
    }
1452
1453
1
    const uint32_t temp_filenum = (filenumber >> 16) - 1;
1454
1455
1
    if (temp_filenum >= MAX_CONCURRENT_FILE_PIPES) {
1456
0
        return -3;
1457
0
    }
1458
1459
1
    assert(temp_filenum <= UINT8_MAX);
1460
1
    const uint8_t file_number = temp_filenum;
1461
1462
    // We're always receiving at this point.
1463
1
    struct File_Transfers *ft = &m->friendlist[friendnumber].file_receiving[file_number];
1464
1465
1
    if (ft->status == FILESTATUS_NONE) {
1466
0
        return -3;
1467
0
    }
1468
1469
1
    if (ft->status != FILESTATUS_NOT_ACCEPTED) {
1470
0
        return -5;
1471
0
    }
1472
1473
1
    if (position >= ft->size) {
1474
0
        return -6;
1475
0
    }
1476
1477
1
    uint8_t sending_pos[sizeof(uint64_t)];
1478
1
    net_pack_u64(sending_pos, position);
1479
1480
1
    if (send_file_control_packet(m, friendnumber, true, file_number, FILECONTROL_SEEK, sending_pos,
1481
1
                                 sizeof(sending_pos))) {
1482
1
        ft->transferred = position;
1483
1
    } else {
1484
0
        return -8;
1485
0
    }
1486
1487
1
    return 0;
1488
1
}
1489
1490
/** @return packet number on success.
1491
 * @retval -1 on failure.
1492
 */
1493
static int64_t send_file_data_packet(const Messenger *_Nonnull m, int32_t friendnumber, uint8_t filenumber, const uint8_t *_Nullable data,
1494
                                     uint16_t length)
1495
1.14k
{
1496
1.14k
    assert(length == 0 || data != nullptr);
1497
1.14k
    if (!m_friend_exists(m, friendnumber)) {
1498
0
        return -1;
1499
0
    }
1500
1501
1.14k
    const uint16_t packet_size = 2 + length;
1502
1.14k
    VLA(uint8_t, packet, packet_size);
1503
1.14k
    packet[0] = PACKET_ID_FILE_DATA;
1504
1.14k
    packet[1] = filenumber;
1505
1506
1.14k
    if (length > 0) {
1507
1.14k
        memcpy(packet + 2, data, length);
1508
1.14k
    }
1509
1510
1.14k
    return write_cryptpacket(m->net_crypto, friend_connection_crypt_connection_id(m->fr_c,
1511
1.14k
                             m->friendlist[friendnumber].friendcon_id), packet, packet_size, true);
1512
1.14k
}
1513
1514
8.04k
#define MAX_FILE_DATA_SIZE (MAX_CRYPTO_DATA_SIZE - 2)
1515
1.25k
#define MIN_SLOTS_FREE (CRYPTO_MIN_QUEUE_LENGTH / 4)
1516
/** @brief Send file data.
1517
 *
1518
 * @retval 0 on success
1519
 * @retval -1 if friend not valid.
1520
 * @retval -2 if friend not online.
1521
 * @retval -3 if filenumber invalid.
1522
 * @retval -4 if file transfer not transferring.
1523
 * @retval -5 if bad data size.
1524
 * @retval -6 if packet queue full.
1525
 * @retval -7 if wrong position.
1526
 */
1527
int send_file_data(const Messenger *m, int32_t friendnumber, uint32_t filenumber, uint64_t position,
1528
                   const uint8_t *data, uint16_t length)
1529
1.14k
{
1530
1.14k
    assert(length == 0 || data != nullptr);
1531
1532
1.14k
    if (!m_friend_exists(m, friendnumber)) {
1533
0
        return -1;
1534
0
    }
1535
1536
1.14k
    if (m->friendlist[friendnumber].status != FRIEND_ONLINE) {
1537
0
        return -2;
1538
0
    }
1539
1540
1.14k
    if (filenumber >= MAX_CONCURRENT_FILE_PIPES) {
1541
0
        return -3;
1542
0
    }
1543
1544
1.14k
    struct File_Transfers *ft = &m->friendlist[friendnumber].file_sending[filenumber];
1545
1546
1.14k
    if (ft->status != FILESTATUS_TRANSFERRING) {
1547
0
        return -4;
1548
0
    }
1549
1550
1.14k
    if (length > MAX_FILE_DATA_SIZE) {
1551
0
        return -5;
1552
0
    }
1553
1554
1.14k
    if (ft->size - ft->transferred < length) {
1555
0
        return -5;
1556
0
    }
1557
1558
1.14k
    if (ft->size != UINT64_MAX && length != MAX_FILE_DATA_SIZE && (ft->transferred + length) != ft->size) {
1559
0
        return -5;
1560
0
    }
1561
1562
1.14k
    if (position != ft->transferred || (ft->requested <= position && ft->size != 0)) {
1563
0
        return -7;
1564
0
    }
1565
1566
    /* Prevent file sending from filling up the entire buffer preventing messages from being sent.
1567
     * TODO(irungentoo): remove */
1568
1.14k
    if (crypto_num_free_sendqueue_slots(m->net_crypto, friend_connection_crypt_connection_id(m->fr_c,
1569
1.14k
                                        m->friendlist[friendnumber].friendcon_id)) < MIN_SLOTS_FREE) {
1570
0
        return -6;
1571
0
    }
1572
1573
1.14k
    const int64_t ret = send_file_data_packet(m, friendnumber, filenumber, data, length);
1574
1575
1.14k
    if (ret != -1) {
1576
        // TODO(irungentoo): record packet ids to check if other received complete file.
1577
1.14k
        ft->transferred += length;
1578
1579
1.14k
        if (length != MAX_FILE_DATA_SIZE || ft->size == ft->transferred) {
1580
3
            ft->status = FILESTATUS_FINISHED;
1581
3
            ft->last_packet_number = ret;
1582
3
        }
1583
1584
1.14k
        return 0;
1585
1.14k
    }
1586
1587
0
    return -6;
1588
1.14k
}
1589
1590
/**
1591
 * Iterate over all file transfers and request chunks (from the client) for each
1592
 * of them.
1593
 *
1594
 * The free_slots parameter is updated by this function.
1595
 *
1596
 * @param m Our messenger object.
1597
 * @param friendnumber The friend we're sending files to.
1598
 * @param userdata The client userdata to pass along to chunk request callbacks.
1599
 * @param free_slots A pointer to the number of free send queue slots in the
1600
 *   crypto connection.
1601
 * @return true if there's still work to do, false otherwise.
1602
 *
1603
 */
1604
static bool do_all_filetransfers(Messenger *_Nonnull m, int32_t friendnumber, void *_Nullable userdata, uint32_t *_Nonnull free_slots)
1605
4.33k
{
1606
4.33k
    Friend *const friendcon = &m->friendlist[friendnumber];
1607
1608
    // Iterate over file transfers as long as we're sending files
1609
1.09M
    for (uint32_t i = 0; i < MAX_CONCURRENT_FILE_PIPES; ++i) {
1610
1.08M
        if (friendcon->num_sending_files == 0) {
1611
            // no active file transfers anymore
1612
3
            return false;
1613
3
        }
1614
1615
1.08M
        if (*free_slots == 0) {
1616
            // send buffer full enough
1617
81
            return false;
1618
81
        }
1619
1620
1.08M
        struct File_Transfers *const ft = &friendcon->file_sending[i];
1621
1622
1.08M
        if (ft->status == FILESTATUS_NONE || ft->status == FILESTATUS_NOT_ACCEPTED) {
1623
            // Filetransfers not actively sending, nothing to do
1624
1.08M
            continue;
1625
1.08M
        }
1626
1627
4.32k
        if (max_speed_reached(m->net_crypto, friend_connection_crypt_connection_id(
1628
4.32k
                                  m->fr_c, friendcon->friendcon_id))) {
1629
0
            LOGGER_DEBUG(m->log, "maximum connection speed reached");
1630
            // connection doesn't support any more data
1631
0
            return false;
1632
0
        }
1633
1634
        // If the file transfer is complete, we request a chunk of size 0.
1635
4.32k
        if (ft->status == FILESTATUS_FINISHED && friend_received_packet(m, friendnumber, ft->last_packet_number) == 0) {
1636
3
            if (m->file_reqchunk != nullptr) {
1637
3
                m->file_reqchunk(m, friendnumber, i, ft->transferred, 0, userdata);
1638
3
            }
1639
1640
            // Now it's inactive, we're no longer sending this.
1641
3
            ft->status = FILESTATUS_NONE;
1642
3
            --friendcon->num_sending_files;
1643
4.32k
        } else if (ft->status == FILESTATUS_TRANSFERRING && ft->paused == FILE_PAUSE_NOT) {
1644
1.50k
            if (ft->size == 0) {
1645
                /* Send 0 data to friend if file is 0 length. */
1646
0
                send_file_data(m, friendnumber, i, 0, nullptr, 0);
1647
0
                continue;
1648
0
            }
1649
1650
1.50k
            if (ft->size == ft->requested) {
1651
                // This file transfer is done.
1652
356
                continue;
1653
356
            }
1654
1655
1.14k
            const uint16_t length = min_u64(ft->size - ft->requested, MAX_FILE_DATA_SIZE);
1656
1.14k
            const uint64_t position = ft->requested;
1657
1.14k
            ft->requested += length;
1658
1659
1.14k
            if (m->file_reqchunk != nullptr) {
1660
1.14k
                m->file_reqchunk(m, friendnumber, i, position, length, userdata);
1661
1.14k
            }
1662
1663
            // The allocated slot is no longer free.
1664
1.14k
            --*free_slots;
1665
1.14k
        }
1666
4.32k
    }
1667
1668
4.24k
    return true;
1669
4.33k
}
1670
1671
static void do_reqchunk_filecb(Messenger *_Nonnull m, int32_t friendnumber, void *_Nullable userdata)
1672
45.5k
{
1673
    // We're not currently doing any file transfers.
1674
45.5k
    if (m->friendlist[friendnumber].num_sending_files == 0) {
1675
45.4k
        return;
1676
45.4k
    }
1677
1678
    // The number of packet slots left in the sendbuffer.
1679
    // This is a per friend count (CRYPTO_PACKET_BUFFER_SIZE).
1680
105
    uint32_t free_slots = crypto_num_free_sendqueue_slots(
1681
105
                              m->net_crypto,
1682
105
                              friend_connection_crypt_connection_id(
1683
105
                                  m->fr_c,
1684
105
                                  m->friendlist[friendnumber].friendcon_id));
1685
1686
    // We keep MIN_SLOTS_FREE slots free for other packets, otherwise file
1687
    // transfers might block other traffic for a long time.
1688
105
    free_slots = max_s32(0, (int32_t)free_slots - MIN_SLOTS_FREE);
1689
1690
    // Maximum number of outer loops below. If the client doesn't send file
1691
    // chunks from within the chunk request callback handler, we never realise
1692
    // that the file transfer has finished and may end up in an infinite loop.
1693
    //
1694
    // Request up to that number of chunks per file from the client
1695
    //
1696
    // TODO(Jfreegman): set this cap dynamically
1697
105
    const uint32_t max_ft_loops = 128;
1698
1699
4.35k
    for (uint32_t i = 0; i < max_ft_loops; ++i) {
1700
4.33k
        if (!do_all_filetransfers(m, friendnumber, userdata, &free_slots)) {
1701
84
            break;
1702
84
        }
1703
1704
4.24k
        if (free_slots == 0) {
1705
            // stop when the buffer is full enough
1706
0
            break;
1707
0
        }
1708
4.24k
    }
1709
105
}
1710
1711
/** @brief Run this when the friend disconnects.
1712
 * Kill all current file transfers.
1713
 */
1714
static void break_files(const Messenger *m, int32_t friendnumber)
1715
14
{
1716
14
    Friend *const f = &m->friendlist[friendnumber];
1717
1718
    // TODO(irungentoo): Inform the client which file transfers get killed with a callback?
1719
3.59k
    for (uint32_t i = 0; i < MAX_CONCURRENT_FILE_PIPES; ++i) {
1720
3.58k
        f->file_sending[i].status = FILESTATUS_NONE;
1721
3.58k
        f->file_receiving[i].status = FILESTATUS_NONE;
1722
3.58k
    }
1723
14
}
1724
1725
static struct File_Transfers *_Nullable get_file_transfer(bool outbound, uint8_t filenumber, uint32_t *_Nonnull real_filenumber, Friend *_Nonnull sender)
1726
5
{
1727
5
    struct File_Transfers *ft;
1728
1729
5
    if (outbound) {
1730
5
        *real_filenumber = filenumber;
1731
5
        ft = &sender->file_sending[filenumber];
1732
5
    } else {
1733
0
        *real_filenumber = (filenumber + 1) << 16;
1734
0
        ft = &sender->file_receiving[filenumber];
1735
0
    }
1736
1737
5
    if (ft->status == FILESTATUS_NONE) {
1738
0
        return nullptr;
1739
0
    }
1740
1741
5
    return ft;
1742
5
}
1743
1744
/** @retval -1 on failure
1745
 * @retval 0 on success.
1746
 */
1747
static int handle_filecontrol(Messenger *_Nonnull m, int32_t friendnumber, bool outbound, uint8_t filenumber,
1748
                              uint8_t control_type, const uint8_t *_Nonnull data, uint16_t length, void *_Nullable userdata)
1749
5
{
1750
5
    uint32_t real_filenumber;
1751
5
    struct File_Transfers *ft = get_file_transfer(outbound, filenumber, &real_filenumber, &m->friendlist[friendnumber]);
1752
1753
5
    if (ft == nullptr) {
1754
0
        LOGGER_DEBUG(m->log, "file control (friend %d, file %d): file transfer does not exist; telling the other to kill it",
1755
0
                     friendnumber, filenumber);
1756
0
        send_file_control_packet(m, friendnumber, !outbound, filenumber, FILECONTROL_KILL, nullptr, 0);
1757
0
        return -1;
1758
0
    }
1759
1760
5
    switch (control_type) {
1761
3
        case FILECONTROL_ACCEPT: {
1762
3
            if (outbound && ft->status == FILESTATUS_NOT_ACCEPTED) {
1763
3
                ft->status = FILESTATUS_TRANSFERRING;
1764
3
                ++m->friendlist[friendnumber].num_sending_files;
1765
3
            } else {
1766
0
                if ((ft->paused & FILE_PAUSE_OTHER) != 0) {
1767
0
                    ft->paused ^= FILE_PAUSE_OTHER;
1768
0
                } else {
1769
0
                    LOGGER_DEBUG(m->log, "file control (friend %d, file %d): friend told us to resume file transfer that wasn't paused",
1770
0
                                 friendnumber, filenumber);
1771
0
                    return -1;
1772
0
                }
1773
0
            }
1774
1775
3
            if (m->file_filecontrol != nullptr) {
1776
3
                m->file_filecontrol(m, friendnumber, real_filenumber, control_type, userdata);
1777
3
            }
1778
1779
3
            return 0;
1780
3
        }
1781
1782
0
        case FILECONTROL_PAUSE: {
1783
0
            if ((ft->paused & FILE_PAUSE_OTHER) != 0 || ft->status != FILESTATUS_TRANSFERRING) {
1784
0
                LOGGER_DEBUG(m->log, "file control (friend %d, file %d): friend told us to pause file transfer that is already paused",
1785
0
                             friendnumber, filenumber);
1786
0
                return -1;
1787
0
            }
1788
1789
0
            ft->paused |= FILE_PAUSE_OTHER;
1790
1791
0
            if (m->file_filecontrol != nullptr) {
1792
0
                m->file_filecontrol(m, friendnumber, real_filenumber, control_type, userdata);
1793
0
            }
1794
1795
0
            return 0;
1796
0
        }
1797
1798
1
        case FILECONTROL_KILL: {
1799
1
            if (m->file_filecontrol != nullptr) {
1800
1
                m->file_filecontrol(m, friendnumber, real_filenumber, control_type, userdata);
1801
1
            }
1802
1803
1
            if (outbound && (ft->status == FILESTATUS_TRANSFERRING || ft->status == FILESTATUS_FINISHED)) {
1804
0
                --m->friendlist[friendnumber].num_sending_files;
1805
0
            }
1806
1807
1
            ft->status = FILESTATUS_NONE;
1808
1809
1
            return 0;
1810
0
        }
1811
1812
1
        case FILECONTROL_SEEK: {
1813
1
            uint64_t position;
1814
1815
1
            if (length != sizeof(position)) {
1816
0
                LOGGER_DEBUG(m->log, "file control (friend %d, file %d): expected payload of length %u, but got %d",
1817
0
                             friendnumber, filenumber, (unsigned int)sizeof(position), length);
1818
0
                return -1;
1819
0
            }
1820
1821
            /* seek can only be sent by the receiver to seek before resuming broken transfers. */
1822
1
            if (ft->status != FILESTATUS_NOT_ACCEPTED || !outbound) {
1823
0
                LOGGER_DEBUG(m->log,
1824
0
                             "file control (friend %d, file %d): seek was either sent by a sender or by the receiver after accepting",
1825
0
                             friendnumber, filenumber);
1826
0
                return -1;
1827
0
            }
1828
1829
1
            net_unpack_u64(data, &position);
1830
1831
1
            if (position >= ft->size) {
1832
0
                LOGGER_DEBUG(m->log,
1833
0
                             "file control (friend %d, file %d): seek position %lu exceeds file size %lu",
1834
0
                             friendnumber, filenumber, (unsigned long)position, (unsigned long)ft->size);
1835
0
                return -1;
1836
0
            }
1837
1838
1
            ft->requested = position;
1839
1
            ft->transferred = position;
1840
1
            return 0;
1841
1
        }
1842
1843
0
        default: {
1844
0
            LOGGER_DEBUG(m->log, "file control (friend %d, file %d): invalid file control: %d",
1845
0
                         friendnumber, filenumber, control_type);
1846
0
            return -1;
1847
1
        }
1848
5
    }
1849
5
}
1850
1851
static int m_handle_lossy_packet(void *object, int friendcon_id, const uint8_t *data, uint16_t length,
1852
                                 void *userdata)
1853
627
{
1854
627
    Messenger *m = (Messenger *)object;
1855
1856
627
    if (!m_friend_exists(m, friendcon_id)) {
1857
0
        return 1;
1858
0
    }
1859
1860
627
    if (m->lossy_packethandler != nullptr) {
1861
627
        m->lossy_packethandler(m, friendcon_id, data[0], data, length, userdata);
1862
627
    }
1863
1864
627
    return 1;
1865
627
}
1866
1867
void custom_lossy_packet_registerhandler(Messenger *m, m_friend_lossy_packet_cb *lossy_packethandler)
1868
1.40k
{
1869
1.40k
    m->lossy_packethandler = lossy_packethandler;
1870
1.40k
}
1871
1872
int m_send_custom_lossy_packet(const Messenger *m, int32_t friendnumber, const uint8_t *data, uint32_t length)
1873
118
{
1874
118
    if (!m_friend_exists(m, friendnumber)) {
1875
0
        return -1;
1876
0
    }
1877
1878
118
    if (length == 0 || length > MAX_CRYPTO_DATA_SIZE) {
1879
0
        return -2;
1880
0
    }
1881
1882
118
    if (data[0] < PACKET_ID_RANGE_LOSSY_START || data[0] > PACKET_ID_RANGE_LOSSY_END) {
1883
0
        return -3;
1884
0
    }
1885
1886
118
    if (m->friendlist[friendnumber].status != FRIEND_ONLINE) {
1887
0
        return -4;
1888
0
    }
1889
1890
118
    if (send_lossy_cryptpacket(m->net_crypto, friend_connection_crypt_connection_id(m->fr_c,
1891
118
                               m->friendlist[friendnumber].friendcon_id), data, length) == -1) {
1892
0
        return -5;
1893
0
    }
1894
1895
118
    return 0;
1896
118
}
1897
1898
static int handle_custom_lossless_packet(void *_Nonnull object, int friend_num, const uint8_t *_Nonnull packet, uint16_t length,
1899
        void *_Nullable userdata)
1900
1.78k
{
1901
1.78k
    Messenger *m = (Messenger *)object;
1902
1.78k
    if (!m_friend_exists(m, friend_num)) {
1903
0
        return -1;
1904
0
    }
1905
1906
1.78k
    if (packet[0] < PACKET_ID_RANGE_LOSSLESS_CUSTOM_START || packet[0] > PACKET_ID_RANGE_LOSSLESS_CUSTOM_END) {
1907
        // allow PACKET_ID_MSI packets to be handled by custom packet handler
1908
1.78k
        if (packet[0] != PACKET_ID_MSI) {
1909
1.77k
            return -1;
1910
1.77k
        }
1911
1.78k
    }
1912
1913
17
    if (m->lossless_packethandler != nullptr) {
1914
17
        m->lossless_packethandler(m, friend_num, packet[0], packet, length, userdata);
1915
17
    }
1916
1917
17
    return 1;
1918
1.78k
}
1919
1920
void custom_lossless_packet_registerhandler(Messenger *m, m_friend_lossless_packet_cb *lossless_packethandler)
1921
1.40k
{
1922
1.40k
    m->lossless_packethandler = lossless_packethandler;
1923
1.40k
}
1924
1925
int send_custom_lossless_packet(const Messenger *m, int32_t friendnumber, const uint8_t *data, uint32_t length)
1926
17
{
1927
17
    if (!m_friend_exists(m, friendnumber)) {
1928
0
        return -1;
1929
0
    }
1930
1931
17
    if (length == 0 || length > MAX_CRYPTO_DATA_SIZE) {
1932
0
        return -2;
1933
0
    }
1934
1935
17
    if ((data[0] < PACKET_ID_RANGE_LOSSLESS_CUSTOM_START || data[0] > PACKET_ID_RANGE_LOSSLESS_CUSTOM_END)
1936
17
            && data[0] != PACKET_ID_MSI) {
1937
0
        return -3;
1938
0
    }
1939
1940
17
    if (m->friendlist[friendnumber].status != FRIEND_ONLINE) {
1941
0
        return -4;
1942
0
    }
1943
1944
17
    if (write_cryptpacket(m->net_crypto, friend_connection_crypt_connection_id(m->fr_c,
1945
17
                          m->friendlist[friendnumber].friendcon_id), data, length, true) == -1) {
1946
0
        return -5;
1947
0
    }
1948
1949
17
    return 0;
1950
17
}
1951
1952
/** Function to filter out some friend requests*/
1953
static int friend_already_added(void *_Nonnull object, const uint8_t *_Nonnull public_key)
1954
137
{
1955
137
    const Messenger *m = (const Messenger *)object;
1956
1957
137
    if (getfriend_id(m, public_key) == -1) {
1958
137
        return 0;
1959
137
    }
1960
1961
0
    return -1;
1962
137
}
1963
1964
/** @brief Check for and handle a timed-out friend request.
1965
 *
1966
 * If the request has timed-out then the friend status is set back to FRIEND_ADDED.
1967
 * @param friendcon_id friendlist index of the timed-out friend
1968
 * @param t time
1969
 */
1970
static void check_friend_request_timed_out(Messenger *_Nonnull m, uint32_t friendcon_id, uint64_t t, void *_Nullable userdata)
1971
34.6k
{
1972
34.6k
    Friend *f = &m->friendlist[friendcon_id];
1973
34.6k
    if (f->friendrequest_lastsent + f->friendrequest_timeout < t) {
1974
100
        set_friend_status(m, friendcon_id, FRIEND_ADDED, userdata);
1975
        /* Double the default timeout every time if friendrequest is assumed
1976
         * to have been sent unsuccessfully.
1977
         */
1978
100
        f->friendrequest_timeout *= 2;
1979
100
    }
1980
34.6k
}
1981
1982
static int m_handle_status(void *object, int friendcon_id, bool status, void *userdata)
1983
450
{
1984
450
    Messenger *m = (Messenger *)object;
1985
450
    if (status) { /* Went online. */
1986
436
        send_online_packet(m, m->friendlist[friendcon_id].friendcon_id);
1987
436
    } else { /* Went offline. */
1988
14
        if (m->friendlist[friendcon_id].status == FRIEND_ONLINE) {
1989
13
            set_friend_status(m, friendcon_id, FRIEND_CONFIRMED, userdata);
1990
13
        }
1991
14
    }
1992
1993
450
    return 0;
1994
450
}
1995
1996
static int m_handle_packet_offline(Messenger *_Nonnull m, const int friendcon_id, const uint8_t *_Nonnull data, const uint16_t data_length, void *_Nullable userdata)
1997
1
{
1998
1
    if (data_length == 0) {
1999
1
        set_friend_status(m, friendcon_id, FRIEND_CONFIRMED, userdata);
2000
1
    }
2001
2002
1
    return 0;
2003
1
}
2004
2005
static int m_handle_packet_nickname(Messenger *_Nonnull m, const int friendcon_id, const uint8_t *_Nonnull data, const uint16_t data_length, void *_Nullable userdata)
2006
435
{
2007
435
    if (data_length > MAX_NAME_LENGTH) {
2008
0
        return 0;
2009
0
    }
2010
2011
    /* Make sure the NULL terminator is present. */
2012
435
    VLA(uint8_t, data_terminated, data_length + 1);
2013
435
    memcpy(data_terminated, data, data_length);
2014
435
    data_terminated[data_length] = 0;
2015
2016
    /* inform of namechange before we overwrite the old name */
2017
435
    if (m->friend_namechange != nullptr) {
2018
435
        m->friend_namechange(m, friendcon_id, data_terminated, data_length, userdata);
2019
435
    }
2020
2021
435
    memcpy(m->friendlist[friendcon_id].name, data_terminated, data_length);
2022
435
    m->friendlist[friendcon_id].name_length = data_length;
2023
2024
435
    return 0;
2025
435
}
2026
2027
static int m_handle_packet_statusmessage(Messenger *_Nonnull m, const int friendcon_id, const uint8_t *_Nonnull data, const uint16_t data_length, void *_Nullable userdata)
2028
434
{
2029
434
    if (data_length > MAX_STATUSMESSAGE_LENGTH) {
2030
0
        return 0;
2031
0
    }
2032
2033
    /* Make sure the NULL terminator is present. */
2034
434
    VLA(uint8_t, data_terminated, data_length + 1);
2035
434
    memcpy(data_terminated, data, data_length);
2036
434
    data_terminated[data_length] = 0;
2037
2038
434
    if (m->friend_statusmessagechange != nullptr) {
2039
434
        m->friend_statusmessagechange(m, friendcon_id, data_terminated, data_length, userdata);
2040
434
    }
2041
2042
434
    set_friend_statusmessage(m, friendcon_id, data_terminated, data_length);
2043
2044
434
    return 0;
2045
434
}
2046
2047
static int m_handle_packet_userstatus(Messenger *_Nonnull m, const int friendcon_id, const uint8_t *_Nonnull data, const uint16_t data_length, void *_Nullable userdata)
2048
436
{
2049
436
    if (data_length != 1) {
2050
0
        return 0;
2051
0
    }
2052
2053
436
    Userstatus status;
2054
436
    if (!userstatus_from_int(data[0], &status)) {
2055
0
        return 0;
2056
0
    }
2057
2058
436
    if (m->friend_userstatuschange != nullptr) {
2059
435
        m->friend_userstatuschange(m, friendcon_id, status, userdata);
2060
435
    }
2061
2062
436
    set_friend_userstatus(m, friendcon_id, status);
2063
2064
436
    return 0;
2065
436
}
2066
2067
static int m_handle_packet_typing(Messenger *_Nonnull m, const int friendcon_id, const uint8_t *_Nonnull data, const uint16_t data_length, void *_Nullable userdata)
2068
435
{
2069
435
    if (data_length != 1) {
2070
0
        return 0;
2071
0
    }
2072
2073
435
    const bool typing = data[0] != 0;
2074
2075
435
    set_friend_typing(m, friendcon_id, typing);
2076
2077
435
    if (m->friend_typingchange != nullptr) {
2078
435
        m->friend_typingchange(m, friendcon_id, typing, userdata);
2079
435
    }
2080
2081
435
    return 0;
2082
435
}
2083
2084
static int m_handle_packet_message(Messenger *_Nonnull m, const int friendcon_id, const uint8_t *_Nonnull data, const uint16_t data_length, const Message_Type message_type,
2085
                                   void *_Nullable userdata)
2086
82.7k
{
2087
82.7k
    if (data_length == 0) {
2088
0
        return 0;
2089
0
    }
2090
2091
82.7k
    const uint8_t *message = data;
2092
82.7k
    const uint16_t message_length = data_length;
2093
2094
    /* Make sure the NULL terminator is present. */
2095
82.7k
    VLA(uint8_t, message_terminated, message_length + 1);
2096
82.7k
    memcpy(message_terminated, message, message_length);
2097
82.7k
    message_terminated[message_length] = 0;
2098
2099
82.7k
    if (m->friend_message != nullptr) {
2100
82.7k
        m->friend_message(m, friendcon_id, message_type, message_terminated, message_length, userdata);
2101
82.7k
    }
2102
2103
82.7k
    return 0;
2104
82.7k
}
2105
2106
static int m_handle_packet_invite_conference(Messenger *_Nonnull m, const int friendcon_id, const uint8_t *_Nonnull data, const uint16_t data_length, void *_Nullable userdata)
2107
57
{
2108
57
    if (data_length == 0) {
2109
0
        return 0;
2110
0
    }
2111
2112
57
    if (m->conference_invite != nullptr) {
2113
57
        m->conference_invite(m, friendcon_id, data, data_length, userdata);
2114
57
    }
2115
2116
57
    return 0;
2117
57
}
2118
2119
static int m_handle_packet_file_sendrequest(Messenger *_Nonnull m, const int friendcon_id, const uint8_t *_Nonnull data, const uint16_t data_length, void *_Nullable userdata)
2120
4
{
2121
4
    const unsigned int head_length = 1 + sizeof(uint32_t) + sizeof(uint64_t) + FILE_ID_LENGTH;
2122
4
    if (data_length < head_length) {
2123
0
        return 0;
2124
0
    }
2125
2126
4
    const uint8_t filenumber = data[0];
2127
2128
#if UINT8_MAX >= MAX_CONCURRENT_FILE_PIPES
2129
2130
    if (filenumber >= MAX_CONCURRENT_FILE_PIPES) {
2131
        return 0;
2132
    }
2133
2134
#endif /* UINT8_MAX >= MAX_CONCURRENT_FILE_PIPES */
2135
2136
4
    uint64_t filesize;
2137
4
    uint32_t file_type;
2138
4
    const uint16_t filename_length = data_length - head_length;
2139
2140
4
    if (filename_length > MAX_FILENAME_LENGTH) {
2141
0
        return 0;
2142
0
    }
2143
2144
4
    memcpy(&file_type, data + 1, sizeof(file_type));
2145
4
    file_type = net_ntohl(file_type);
2146
2147
4
    net_unpack_u64(data + 1 + sizeof(uint32_t), &filesize);
2148
4
    struct File_Transfers *ft = &m->friendlist[friendcon_id].file_receiving[filenumber];
2149
2150
4
    if (ft->status != FILESTATUS_NONE) {
2151
0
        return 0;
2152
0
    }
2153
2154
4
    ft->status = FILESTATUS_NOT_ACCEPTED;
2155
4
    ft->size = filesize;
2156
4
    ft->transferred = 0;
2157
4
    ft->paused = FILE_PAUSE_NOT;
2158
4
    memcpy(ft->id, data + 1 + sizeof(uint32_t) + sizeof(uint64_t), FILE_ID_LENGTH);
2159
2160
4
    VLA(uint8_t, filename_terminated, filename_length + 1);
2161
4
    const uint8_t *filename = nullptr;
2162
2163
4
    if (filename_length > 0) {
2164
        /* Force NULL terminate file name. */
2165
4
        memcpy(filename_terminated, data + head_length, filename_length);
2166
4
        filename_terminated[filename_length] = 0;
2167
4
        filename = filename_terminated;
2168
4
    }
2169
2170
4
    uint32_t real_filenumber = filenumber;
2171
4
    real_filenumber += 1;
2172
4
    real_filenumber <<= 16;
2173
2174
4
    if (m->file_sendrequest != nullptr) {
2175
4
        m->file_sendrequest(m, friendcon_id, real_filenumber, file_type, filesize, filename, filename_length,
2176
4
                            userdata);
2177
4
    }
2178
2179
4
    return 0;
2180
4
}
2181
2182
static int m_handle_packet_file_control(Messenger *_Nonnull m, const int friendcon_id, const uint8_t *_Nonnull data, const uint16_t data_length, void *_Nullable userdata)
2183
5
{
2184
5
    if (data_length < 3) {
2185
0
        return 0;
2186
0
    }
2187
2188
    // On the other side, "outbound" is "inbound", i.e. if they send 1,
2189
    // that means "inbound" on their side, but we call it "outbound"
2190
    // here.
2191
5
    const bool outbound = data[0] == 1;
2192
5
    const uint8_t filenumber = data[1];
2193
5
    const uint8_t control_type = data[2];
2194
2195
#if UINT8_MAX >= MAX_CONCURRENT_FILE_PIPES
2196
2197
    if (filenumber >= MAX_CONCURRENT_FILE_PIPES) {
2198
        return 0;
2199
    }
2200
2201
#endif /* UINT8_MAX >= MAX_CONCURRENT_FILE_PIPES */
2202
2203
5
    if (handle_filecontrol(m, friendcon_id, outbound, filenumber, control_type, data + 3, data_length - 3, userdata) == -1) {
2204
        // TODO(iphydf): Do something different here? Right now, this
2205
        // check is pointless.
2206
0
        return 0;
2207
0
    }
2208
2209
5
    return 0;
2210
5
}
2211
2212
static int m_handle_packet_file_data(Messenger *_Nonnull m, const int friendcon_id, const uint8_t *_Nonnull data, const uint16_t data_length, void *_Nullable userdata)
2213
1.14k
{
2214
1.14k
    if (data_length < 1) {
2215
0
        return 0;
2216
0
    }
2217
2218
1.14k
    const uint8_t filenumber = data[0];
2219
2220
#if UINT8_MAX >= MAX_CONCURRENT_FILE_PIPES
2221
2222
    if (filenumber >= MAX_CONCURRENT_FILE_PIPES) {
2223
        return 0;
2224
    }
2225
2226
#endif /* UINT8_MAX >= MAX_CONCURRENT_FILE_PIPES */
2227
2228
1.14k
    struct File_Transfers *ft = &m->friendlist[friendcon_id].file_receiving[filenumber];
2229
2230
1.14k
    if (ft->status != FILESTATUS_TRANSFERRING) {
2231
0
        return 0;
2232
0
    }
2233
2234
1.14k
    uint64_t position = ft->transferred;
2235
1.14k
    uint32_t real_filenumber = filenumber;
2236
1.14k
    real_filenumber += 1;
2237
1.14k
    real_filenumber <<= 16;
2238
1.14k
    uint16_t file_data_length = data_length - 1;
2239
1.14k
    const uint8_t *file_data;
2240
2241
1.14k
    if (file_data_length == 0) {
2242
0
        file_data = nullptr;
2243
1.14k
    } else {
2244
1.14k
        file_data = data + 1;
2245
1.14k
    }
2246
2247
    /* Prevent more data than the filesize from being passed to clients. */
2248
1.14k
    if ((ft->transferred + file_data_length) > ft->size) {
2249
0
        file_data_length = ft->size - ft->transferred;
2250
0
    }
2251
2252
1.14k
    if (m->file_filedata != nullptr) {
2253
1.14k
        m->file_filedata(m, friendcon_id, real_filenumber, position, file_data, file_data_length, userdata);
2254
1.14k
    }
2255
2256
1.14k
    ft->transferred += file_data_length;
2257
2258
1.14k
    if (file_data_length > 0 && (ft->transferred >= ft->size || file_data_length != MAX_FILE_DATA_SIZE)) {
2259
3
        file_data_length = 0;
2260
3
        file_data = nullptr;
2261
3
        position = ft->transferred;
2262
2263
        /* Full file received. */
2264
3
        if (m->file_filedata != nullptr) {
2265
3
            m->file_filedata(m, friendcon_id, real_filenumber, position, file_data, file_data_length, userdata);
2266
3
        }
2267
3
    }
2268
2269
    /* Data is zero, filetransfer is over. */
2270
1.14k
    if (file_data_length == 0) {
2271
3
        ft->status = FILESTATUS_NONE;
2272
3
    }
2273
2274
1.14k
    return 0;
2275
1.14k
}
2276
2277
static int m_handle_packet_invite_groupchat(Messenger *_Nonnull m, const int friendcon_id, const uint8_t *_Nonnull data, const uint16_t data_length, void *_Nullable userdata)
2278
9
{
2279
    // first two bytes are messenger packet type and group invite type
2280
9
    if (data_length < 2 + GC_JOIN_DATA_LENGTH) {
2281
0
        return 0;
2282
0
    }
2283
2284
9
    const uint8_t invite_type = data[1];
2285
9
    const uint8_t *join_data = data + 2;
2286
9
    const uint32_t join_data_len = data_length - 2;
2287
2288
9
    if (m->group_invite != nullptr && data[1] == GROUP_INVITE && data_length != 2 + GC_JOIN_DATA_LENGTH) {
2289
3
        if (group_not_added(m->group_handler, join_data, join_data_len)) {
2290
3
            m->group_invite(m, friendcon_id, join_data, GC_JOIN_DATA_LENGTH,
2291
3
                            join_data + GC_JOIN_DATA_LENGTH, join_data_len - GC_JOIN_DATA_LENGTH, userdata);
2292
3
        }
2293
6
    } else if (invite_type == GROUP_INVITE_ACCEPTED) {
2294
3
        handle_gc_invite_accepted_packet(m->group_handler, friendcon_id, join_data, join_data_len);
2295
3
    } else if (invite_type == GROUP_INVITE_CONFIRMATION) {
2296
3
        handle_gc_invite_confirmed_packet(m->group_handler, friendcon_id, join_data, join_data_len);
2297
3
    }
2298
2299
9
    return 0;
2300
9
}
2301
2302
static int m_handle_packet(void *object, int friendcon_id, const uint8_t *data, uint16_t length, void *userdata)
2303
87.4k
{
2304
87.4k
    Messenger *m = (Messenger *)object;
2305
87.4k
    if (length == 0) {
2306
0
        return -1;
2307
0
    }
2308
2309
87.4k
    const uint8_t packet_id = data[0];
2310
87.4k
    const uint8_t *payload = data + 1;
2311
87.4k
    const uint16_t payload_length = length - 1;
2312
2313
87.4k
    if (m->friendlist[friendcon_id].status != FRIEND_ONLINE) {
2314
438
        if (packet_id == PACKET_ID_ONLINE && length == 1) {
2315
436
            set_friend_status(m, friendcon_id, FRIEND_ONLINE, userdata);
2316
436
            send_online_packet(m, m->friendlist[friendcon_id].friendcon_id);
2317
436
        } else {
2318
2
            return -1;
2319
2
        }
2320
438
    }
2321
2322
87.4k
    switch (packet_id) {
2323
        // TODO(Green-Sky): now all return 0 on error AND success, make errors errors?
2324
1
        case PACKET_ID_OFFLINE:
2325
1
            return m_handle_packet_offline(m, friendcon_id, payload, payload_length, userdata);
2326
435
        case PACKET_ID_NICKNAME:
2327
435
            return m_handle_packet_nickname(m, friendcon_id, payload, payload_length, userdata);
2328
434
        case PACKET_ID_STATUSMESSAGE:
2329
434
            return m_handle_packet_statusmessage(m, friendcon_id, payload, payload_length, userdata);
2330
436
        case PACKET_ID_USERSTATUS:
2331
436
            return m_handle_packet_userstatus(m, friendcon_id, payload, payload_length, userdata);
2332
435
        case PACKET_ID_TYPING:
2333
435
            return m_handle_packet_typing(m, friendcon_id, payload, payload_length, userdata);
2334
82.7k
        case PACKET_ID_MESSAGE:
2335
82.7k
            return m_handle_packet_message(m, friendcon_id, payload, payload_length, MESSAGE_NORMAL, userdata);
2336
0
        case PACKET_ID_ACTION:
2337
0
            return m_handle_packet_message(m, friendcon_id, payload, payload_length, MESSAGE_ACTION, userdata);
2338
57
        case PACKET_ID_INVITE_CONFERENCE:
2339
57
            return m_handle_packet_invite_conference(m, friendcon_id, payload, payload_length, userdata);
2340
4
        case PACKET_ID_FILE_SENDREQUEST:
2341
4
            return m_handle_packet_file_sendrequest(m, friendcon_id, payload, payload_length, userdata);
2342
5
        case PACKET_ID_FILE_CONTROL:
2343
5
            return m_handle_packet_file_control(m, friendcon_id, payload, payload_length, userdata);
2344
1.14k
        case PACKET_ID_FILE_DATA:
2345
1.14k
            return m_handle_packet_file_data(m, friendcon_id, payload, payload_length, userdata);
2346
16
        case PACKET_ID_MSI:
2347
16
            return handle_custom_lossless_packet(object, friendcon_id, data, length, userdata);
2348
9
        case PACKET_ID_INVITE_GROUPCHAT:
2349
9
            return m_handle_packet_invite_groupchat(m, friendcon_id, payload, payload_length, userdata);
2350
87.4k
    }
2351
2352
1.77k
    return handle_custom_lossless_packet(object, friendcon_id, data, length, userdata);
2353
87.4k
}
2354
2355
static void do_friends(Messenger *_Nonnull m, void *_Nullable userdata)
2356
124k
{
2357
124k
    const uint64_t temp_time = mono_time_get(m->mono_time);
2358
289k
    for (uint32_t i = 0; i < m->numfriends; ++i) {
2359
165k
        if (m->friendlist[i].status == FRIEND_ADDED) {
2360
16.7k
            const int fr = send_friend_request_packet(m->fr_c, m->friendlist[i].friendcon_id, m->friendlist[i].friendrequest_nospam,
2361
16.7k
                           m->friendlist[i].info,
2362
16.7k
                           m->friendlist[i].info_size);
2363
2364
16.7k
            if (fr >= 0) {
2365
238
                set_friend_status(m, i, FRIEND_REQUESTED, userdata);
2366
238
                m->friendlist[i].friendrequest_lastsent = temp_time;
2367
238
            }
2368
16.7k
        }
2369
2370
165k
        if (m->friendlist[i].status == FRIEND_REQUESTED) {
2371
            /* If we didn't connect to friend after successfully sending him a friend
2372
             * request the request is deemed unsuccessful so we set the status back to
2373
             * FRIEND_ADDED and try again.
2374
             */
2375
34.6k
            check_friend_request_timed_out(m, i, temp_time, userdata);
2376
34.6k
        }
2377
2378
165k
        if (m->friendlist[i].status == FRIEND_ONLINE) { /* friend is online. */
2379
45.5k
            if (!m->friendlist[i].name_sent) {
2380
436
                if (m_sendname(m, i, m->name, m->name_length)) {
2381
436
                    m->friendlist[i].name_sent = true;
2382
436
                }
2383
436
            }
2384
2385
45.5k
            if (!m->friendlist[i].statusmessage_sent) {
2386
435
                if (send_statusmessage(m, i, m->statusmessage, m->statusmessage_length)) {
2387
435
                    m->friendlist[i].statusmessage_sent = true;
2388
435
                }
2389
435
            }
2390
2391
45.5k
            if (!m->friendlist[i].userstatus_sent) {
2392
437
                if (send_userstatus(m, i, m->userstatus)) {
2393
437
                    m->friendlist[i].userstatus_sent = true;
2394
437
                }
2395
437
            }
2396
2397
45.5k
            if (!m->friendlist[i].user_istyping_sent) {
2398
436
                if (send_user_istyping(m, i, m->friendlist[i].user_istyping)) {
2399
436
                    m->friendlist[i].user_istyping_sent = true;
2400
436
                }
2401
436
            }
2402
2403
45.5k
            check_friend_tcp_udp(m, i, userdata);
2404
45.5k
            do_receipts(m, i, userdata);
2405
45.5k
            do_reqchunk_filecb(m, i, userdata);
2406
2407
45.5k
            m->friendlist[i].last_seen_time = (uint64_t) time(nullptr);
2408
45.5k
        }
2409
165k
    }
2410
124k
}
2411
2412
static void m_connection_status_callback(Messenger *_Nonnull m, void *_Nullable userdata)
2413
124k
{
2414
124k
    const Onion_Connection_Status conn_status = onion_connection_status(m->onion_c);
2415
124k
    if (conn_status != m->last_connection_status) {
2416
362
        if (m->core_connection_change != nullptr) {
2417
362
            m->core_connection_change(m, conn_status, userdata);
2418
362
        }
2419
2420
362
        m->last_connection_status = conn_status;
2421
362
    }
2422
124k
}
2423
2424
124k
#define DUMPING_CLIENTS_FRIENDS_EVERY_N_SECONDS 60UL
2425
2426
/** @brief Minimum messenger run interval in ms
2427
 * TODO(mannol): A/V
2428
 */
2429
692
#define MIN_RUN_INTERVAL 50
2430
2431
/**
2432
 * @brief Return the time in milliseconds before `do_messenger()` should be called again
2433
 *   for optimal performance.
2434
 *
2435
 * @return time (in ms) before the next `do_messenger()` needs to be run on success.
2436
 */
2437
uint32_t messenger_run_interval(const Messenger *m)
2438
346
{
2439
346
    const uint32_t crypto_interval = crypto_run_interval(m->net_crypto);
2440
2441
346
    if (crypto_interval > MIN_RUN_INTERVAL) {
2442
346
        return MIN_RUN_INTERVAL;
2443
346
    }
2444
2445
0
    return crypto_interval;
2446
346
}
2447
2448
/** @brief Attempts to create a DHT announcement for a group chat with our connection info. An
2449
 * announcement can only be created if we either have a UDP or TCP connection to the network.
2450
 *
2451
 * @retval true if success.
2452
 */
2453
static bool self_announce_group(const Messenger *_Nonnull m, GC_Chat *_Nonnull chat, Onion_Friend *_Nonnull onion_friend)
2454
125
{
2455
125
    GC_Public_Announce announce = {{{{{0}}}}};
2456
2457
125
    const bool ip_port_is_set = chat->self_udp_status != SELF_UDP_STATUS_NONE;
2458
125
    const int tcp_num = tcp_copy_connected_relays(chat->tcp_conn, announce.base_announce.tcp_relays,
2459
125
                        GCA_MAX_ANNOUNCED_TCP_RELAYS);
2460
2461
125
    if (tcp_num == 0 && !ip_port_is_set) {
2462
6
        onion_friend_set_gc_data(onion_friend, nullptr, 0);
2463
6
        return false;
2464
6
    }
2465
2466
119
    announce.base_announce.tcp_relays_count = (uint8_t)tcp_num;
2467
119
    announce.base_announce.ip_port_is_set = ip_port_is_set;
2468
2469
119
    if (ip_port_is_set) {
2470
117
        memcpy(&announce.base_announce.ip_port, &chat->self_ip_port, sizeof(IP_Port));
2471
117
    }
2472
2473
119
    memcpy(announce.base_announce.peer_public_key, chat->self_public_key.enc, ENC_PUBLIC_KEY_SIZE);
2474
119
    memcpy(announce.chat_public_key, get_chat_id(&chat->chat_public_key), ENC_PUBLIC_KEY_SIZE);
2475
2476
119
    uint8_t gc_data[GCA_PUBLIC_ANNOUNCE_MAX_SIZE];
2477
119
    const int length = gca_pack_public_announce(m->log, gc_data, sizeof(gc_data), &announce);
2478
2479
119
    if (length <= 0) {
2480
0
        onion_friend_set_gc_data(onion_friend, nullptr, 0);
2481
0
        return false;
2482
0
    }
2483
2484
119
    if (gca_add_announce(m->mem, m->mono_time, m->group_announce, &announce) == nullptr) {
2485
0
        onion_friend_set_gc_data(onion_friend, nullptr, 0);
2486
0
        return false;
2487
0
    }
2488
2489
119
    onion_friend_set_gc_data(onion_friend, gc_data, (uint16_t)length);
2490
119
    chat->update_self_announces = false;
2491
119
    chat->last_time_self_announce = mono_time_get(chat->mono_time);
2492
2493
119
    if (tcp_num > 0) {
2494
2
        pk_copy(chat->announced_tcp_relay_pk, announce.base_announce.tcp_relays[0].public_key);
2495
117
    } else {
2496
117
        memzero(chat->announced_tcp_relay_pk, sizeof(chat->announced_tcp_relay_pk));
2497
117
    }
2498
2499
119
    LOGGER_DEBUG(chat->log, "Published group announce. TCP relays: %d, UDP status: %u", tcp_num,
2500
119
                 chat->self_udp_status);
2501
119
    return true;
2502
119
}
2503
2504
static void do_gc_onion_friends(const Messenger *_Nonnull m)
2505
124k
{
2506
124k
    const uint16_t num_friends = onion_get_friend_count(m->onion_c);
2507
2508
308k
    for (uint16_t i = 0; i < num_friends; ++i) {
2509
184k
        Onion_Friend *onion_friend = onion_get_friend(m->onion_c, i);
2510
2511
184k
        if (!onion_friend_is_groupchat(onion_friend)) {
2512
172k
            continue;
2513
172k
        }
2514
2515
12.3k
        GC_Chat *chat = gc_get_group_by_public_key(m->group_handler, onion_friend_get_gc_public_key(onion_friend));
2516
2517
12.3k
        if (chat == nullptr) {
2518
0
            continue;
2519
0
        }
2520
2521
12.3k
        if (chat->update_self_announces) {
2522
125
            self_announce_group(m, chat, onion_friend);
2523
125
        }
2524
12.3k
    }
2525
124k
}
2526
2527
/** @brief The main loop that needs to be run at least 20 times per second. */
2528
void do_messenger(Messenger *m, void *userdata)
2529
124k
{
2530
    // Add the TCP relays, but only if this is the first time calling do_messenger
2531
124k
    if (!m->has_added_relays) {
2532
381
        m->has_added_relays = true;
2533
2534
381
        for (uint16_t i = 0; i < m->num_loaded_relays; ++i) {
2535
0
            add_tcp_relay(m->net_crypto, &m->loaded_relays[i].ip_port, m->loaded_relays[i].public_key);
2536
0
        }
2537
2538
381
        m->num_loaded_relays = 0;
2539
2540
381
        if (m->tcp_server != nullptr) {
2541
            /* Add self tcp server. */
2542
4
            IP_Port local_ip_port;
2543
4
            local_ip_port.port = net_htons(m->options.tcp_server_port);
2544
4
            local_ip_port.ip.family = net_family_ipv4();
2545
4
            local_ip_port.ip.ip.v4 = get_ip4_loopback();
2546
4
            add_tcp_relay(m->net_crypto, &local_ip_port, tcp_server_public_key(m->tcp_server));
2547
4
        }
2548
381
    }
2549
2550
124k
    if (!m->options.udp_disabled) {
2551
88.1k
        networking_poll(m->net, userdata);
2552
88.1k
        do_dht(m->dht);
2553
88.1k
    }
2554
2555
124k
    if (m->tcp_server != nullptr) {
2556
2.18k
        do_tcp_server(m->tcp_server, m->mono_time);
2557
2.18k
    }
2558
2559
124k
    do_net_crypto(m->net_crypto, userdata);
2560
124k
    do_onion_client(m->onion_c);
2561
124k
    do_friend_connections(m->fr_c, userdata);
2562
124k
    do_friends(m, userdata);
2563
124k
    do_gc(m->group_handler, userdata);
2564
124k
    do_gca(m->mono_time, m->group_announce);
2565
124k
    do_gc_onion_friends(m);
2566
124k
    m_connection_status_callback(m, userdata);
2567
2568
124k
    if (mono_time_get(m->mono_time) > m->lastdump + DUMPING_CLIENTS_FRIENDS_EVERY_N_SECONDS) {
2569
386
        m->lastdump = mono_time_get(m->mono_time);
2570
386
        uint32_t last_pinged;
2571
2572
394k
        for (uint32_t client = 0; client < LCLIENT_LIST; ++client) {
2573
394k
            const Client_data *cptr = dht_get_close_client(m->dht, client);
2574
394k
            const IPPTsPng *const assocs[] = { &cptr->assoc4, &cptr->assoc6, nullptr };
2575
2576
1.18M
            for (const IPPTsPng * const *it = assocs; *it != nullptr; ++it) {
2577
787k
                const IPPTsPng *const assoc = *it;
2578
2579
787k
                if (ip_isset(&assoc->ip_port.ip)) {
2580
20
                    last_pinged = m->lastdump - assoc->last_pinged;
2581
2582
20
                    if (last_pinged > 999) {
2583
0
                        last_pinged = 999;
2584
0
                    }
2585
2586
20
                    Ip_Ntoa ip_str;
2587
20
                    char id_str[TOX_BYTES_TO_STRING_BUF_SIZE];
2588
20
                    bytes_to_string(cptr->public_key, CRYPTO_PUBLIC_KEY_SIZE, id_str, sizeof(id_str));
2589
20
                    LOGGER_TRACE(m->log, "C[%2u] %s:%u [%3u] %s",
2590
20
                                 client, net_ip_ntoa(&assoc->ip_port.ip, &ip_str),
2591
20
                                 net_ntohs(assoc->ip_port.port), last_pinged,
2592
20
                                 id_str);
2593
20
                }
2594
787k
            }
2595
394k
        }
2596
2597
        /* dht contains additional "friends" (requests) */
2598
386
        const uint32_t num_dhtfriends = dht_get_num_friends(m->dht);
2599
386
        VLA(int32_t, m2dht, num_dhtfriends);
2600
386
        VLA(int32_t, dht2m, num_dhtfriends);
2601
2602
1.17k
        for (uint32_t friend_idx = 0; friend_idx < num_dhtfriends; ++friend_idx) {
2603
784
            m2dht[friend_idx] = -1;
2604
784
            dht2m[friend_idx] = -1;
2605
2606
784
            if (friend_idx >= m->numfriends) {
2607
540
                continue;
2608
540
            }
2609
2610
759
            for (uint32_t dhtfriend = 0; dhtfriend < dht_get_num_friends(m->dht); ++dhtfriend) {
2611
515
                if (pk_equal(m->friendlist[friend_idx].real_pk, dht_get_friend_public_key(m->dht, dhtfriend))) {
2612
0
                    assert(dhtfriend < INT32_MAX);
2613
0
                    m2dht[friend_idx] = (int32_t)dhtfriend;
2614
0
                    break;
2615
0
                }
2616
515
            }
2617
244
        }
2618
2619
1.17k
        for (uint32_t friend_idx = 0; friend_idx < num_dhtfriends; ++friend_idx) {
2620
784
            if (m2dht[friend_idx] >= 0) {
2621
0
                assert(friend_idx < INT32_MAX);
2622
0
                dht2m[m2dht[friend_idx]] = (int32_t)friend_idx;
2623
0
            }
2624
784
        }
2625
2626
386
        if (m->numfriends != dht_get_num_friends(m->dht)) {
2627
344
            LOGGER_TRACE(m->log, "Friend num in DHT %u != friend num in msger %u", dht_get_num_friends(m->dht), m->numfriends);
2628
344
        }
2629
2630
1.17k
        for (uint32_t friend_idx = 0; friend_idx < num_dhtfriends; ++friend_idx) {
2631
784
            const Friend *const msgfptr = dht2m[friend_idx] >= 0 ?  &m->friendlist[dht2m[friend_idx]] : nullptr;
2632
784
            const DHT_Friend *const dhtfptr = dht_get_friend(m->dht, friend_idx);
2633
2634
784
            if (msgfptr != nullptr) {
2635
0
                char id_str[TOX_BYTES_TO_STRING_BUF_SIZE];
2636
0
                bytes_to_string(msgfptr->real_pk, CRYPTO_PUBLIC_KEY_SIZE, id_str, sizeof(id_str));
2637
0
                LOGGER_TRACE(m->log, "F[%2d:%2u] <%s> %s",
2638
0
                             dht2m[friend_idx], friend_idx, msgfptr->name,
2639
0
                             id_str);
2640
784
            } else {
2641
784
                char id_str[TOX_BYTES_TO_STRING_BUF_SIZE];
2642
784
                bytes_to_string(dht_friend_public_key(dhtfptr), CRYPTO_PUBLIC_KEY_SIZE, id_str, sizeof(id_str));
2643
784
                LOGGER_TRACE(m->log, "F[--:%2u] %s", friend_idx,
2644
784
                             id_str);
2645
784
            }
2646
2647
7.05k
            for (uint32_t client = 0; client < MAX_FRIEND_CLIENTS; ++client) {
2648
6.27k
                const Client_data *cptr = dht_friend_client(dhtfptr, client);
2649
6.27k
                const IPPTsPng *const assocs[] = {&cptr->assoc4, &cptr->assoc6};
2650
2651
18.8k
                for (size_t a = 0; a < sizeof(assocs) / sizeof(assocs[0]); ++a) {
2652
12.5k
                    const IPPTsPng *const assoc = assocs[a];
2653
2654
12.5k
                    if (ip_isset(&assoc->ip_port.ip)) {
2655
85
                        last_pinged = m->lastdump - assoc->last_pinged;
2656
2657
85
                        if (last_pinged > 999) {
2658
0
                            last_pinged = 999;
2659
0
                        }
2660
2661
85
                        Ip_Ntoa ip_str;
2662
85
                        char id_str[TOX_BYTES_TO_STRING_BUF_SIZE];
2663
85
                        bytes_to_string(cptr->public_key, CRYPTO_PUBLIC_KEY_SIZE, id_str, sizeof(id_str));
2664
85
                        LOGGER_TRACE(m->log, "F[%2u] => C[%2u] %s:%u [%3u] %s",
2665
85
                                     friend_idx, client, net_ip_ntoa(&assoc->ip_port.ip, &ip_str),
2666
85
                                     net_ntohs(assoc->ip_port.port), last_pinged,
2667
85
                                     id_str);
2668
85
                    }
2669
12.5k
                }
2670
6.27k
            }
2671
784
        }
2672
386
    }
2673
124k
}
2674
2675
/** new messenger format for load/save, more robust and forward compatible */
2676
2677
32
#define SAVED_FRIEND_REQUEST_SIZE 1024
2678
4.39k
#define NUM_SAVED_PATH_NODES 8
2679
2680
struct Saved_Friend {
2681
    uint8_t status;
2682
    uint8_t real_pk[CRYPTO_PUBLIC_KEY_SIZE];
2683
    uint8_t info[SAVED_FRIEND_REQUEST_SIZE]; // the data that is sent during the friend requests we do.
2684
    uint16_t info_size; // Length of the info.
2685
    uint8_t name[MAX_NAME_LENGTH];
2686
    uint16_t name_length;
2687
    uint8_t statusmessage[MAX_STATUSMESSAGE_LENGTH];
2688
    uint16_t statusmessage_length;
2689
    uint8_t userstatus;
2690
    uint32_t friendrequest_nospam;
2691
    uint8_t last_seen_time[sizeof(uint64_t)];
2692
};
2693
2694
static uint32_t friend_size(void)
2695
4.57k
{
2696
4.57k
    uint32_t data = 0;
2697
4.57k
    const struct Saved_Friend *const temp = nullptr;
2698
2699
4.57k
#define VALUE_MEMBER(data, name) \
2700
27.4k
    do {                         \
2701
27.4k
        data += sizeof(name);    \
2702
27.4k
    } while (0)
2703
4.57k
#define ARRAY_MEMBER(data, name) \
2704
22.8k
    do {                         \
2705
22.8k
        data += sizeof(name);    \
2706
22.8k
    } while (0)
2707
2708
    // Exactly the same in friend_load, friend_save, and friend_size
2709
4.57k
    VALUE_MEMBER(data, temp->status);
2710
4.57k
    ARRAY_MEMBER(data, temp->real_pk);
2711
4.57k
    ARRAY_MEMBER(data, temp->info);
2712
4.57k
    ++data; // padding
2713
4.57k
    VALUE_MEMBER(data, temp->info_size);
2714
4.57k
    ARRAY_MEMBER(data, temp->name);
2715
4.57k
    VALUE_MEMBER(data, temp->name_length);
2716
4.57k
    ARRAY_MEMBER(data, temp->statusmessage);
2717
4.57k
    ++data; // padding
2718
4.57k
    VALUE_MEMBER(data, temp->statusmessage_length);
2719
4.57k
    VALUE_MEMBER(data, temp->userstatus);
2720
4.57k
    data += 3; // padding
2721
4.57k
    VALUE_MEMBER(data, temp->friendrequest_nospam);
2722
4.57k
    ARRAY_MEMBER(data, temp->last_seen_time);
2723
2724
4.57k
#undef VALUE_MEMBER
2725
4.57k
#undef ARRAY_MEMBER
2726
2727
4.57k
    return data;
2728
4.57k
}
2729
2730
static uint8_t *_Nonnull friend_save(const struct Saved_Friend *_Nonnull temp, uint8_t *_Nonnull data)
2731
138
{
2732
138
#define VALUE_MEMBER(data, name)           \
2733
828
    do {                                   \
2734
828
        memcpy(data, &name, sizeof(name)); \
2735
828
        data += sizeof(name);              \
2736
828
    } while (0)
2737
2738
138
#define ARRAY_MEMBER(data, name)          \
2739
690
    do {                                  \
2740
690
        memcpy(data, name, sizeof(name)); \
2741
690
        data += sizeof(name);             \
2742
690
    } while (0)
2743
2744
    // Exactly the same in friend_load, friend_save, and friend_size
2745
138
    VALUE_MEMBER(data, temp->status);
2746
138
    ARRAY_MEMBER(data, temp->real_pk);
2747
138
    ARRAY_MEMBER(data, temp->info);
2748
138
    ++data; // padding
2749
138
    VALUE_MEMBER(data, temp->info_size);
2750
138
    ARRAY_MEMBER(data, temp->name);
2751
138
    VALUE_MEMBER(data, temp->name_length);
2752
138
    ARRAY_MEMBER(data, temp->statusmessage);
2753
138
    ++data; // padding
2754
138
    VALUE_MEMBER(data, temp->statusmessage_length);
2755
138
    VALUE_MEMBER(data, temp->userstatus);
2756
138
    data += 3; // padding
2757
138
    VALUE_MEMBER(data, temp->friendrequest_nospam);
2758
138
    ARRAY_MEMBER(data, temp->last_seen_time);
2759
2760
138
#undef VALUE_MEMBER
2761
138
#undef ARRAY_MEMBER
2762
2763
138
    return data;
2764
138
}
2765
2766
static const uint8_t *_Nonnull friend_load(struct Saved_Friend *_Nonnull temp, const uint8_t *_Nonnull data)
2767
536
{
2768
536
#define VALUE_MEMBER(data, name)           \
2769
3.21k
    do {                                   \
2770
3.21k
        memcpy(&name, data, sizeof(name)); \
2771
3.21k
        data += sizeof(name);              \
2772
3.21k
    } while (0)
2773
2774
536
#define ARRAY_MEMBER(data, name)          \
2775
2.68k
    do {                                  \
2776
2.68k
        memcpy(name, data, sizeof(name)); \
2777
2.68k
        data += sizeof(name);             \
2778
2.68k
    } while (0)
2779
2780
    // Exactly the same in friend_load, friend_save, and friend_size
2781
536
    VALUE_MEMBER(data, temp->status);
2782
536
    ARRAY_MEMBER(data, temp->real_pk);
2783
536
    ARRAY_MEMBER(data, temp->info);
2784
536
    ++data; // padding
2785
536
    VALUE_MEMBER(data, temp->info_size);
2786
536
    ARRAY_MEMBER(data, temp->name);
2787
536
    VALUE_MEMBER(data, temp->name_length);
2788
536
    ARRAY_MEMBER(data, temp->statusmessage);
2789
536
    ++data; // padding
2790
536
    VALUE_MEMBER(data, temp->statusmessage_length);
2791
536
    VALUE_MEMBER(data, temp->userstatus);
2792
536
    data += 3; // padding
2793
536
    VALUE_MEMBER(data, temp->friendrequest_nospam);
2794
536
    ARRAY_MEMBER(data, temp->last_seen_time);
2795
2796
536
#undef VALUE_MEMBER
2797
536
#undef ARRAY_MEMBER
2798
2799
536
    return data;
2800
536
}
2801
2802
static uint32_t m_state_plugins_size(const Messenger *_Nonnull m)
2803
1.98k
{
2804
1.98k
    const uint32_t size32 = sizeof(uint32_t);
2805
1.98k
    const uint32_t sizesubhead = size32 * 2;
2806
2807
1.98k
    uint32_t size = 0;
2808
2809
1.98k
    for (const Messenger_State_Plugin *plugin = m->options.state_plugins;
2810
19.8k
            plugin != m->options.state_plugins + m->options.state_plugins_length;
2811
17.8k
            ++plugin) {
2812
17.8k
        size += sizesubhead + plugin->size(m);
2813
17.8k
    }
2814
2815
1.98k
    return size;
2816
1.98k
}
2817
2818
/** @brief Registers a state plugin for saving, loading, and getting the size of a section of the save.
2819
 *
2820
 * @retval true on success
2821
 * @retval false on error
2822
 */
2823
bool m_register_state_plugin(Messenger *m, State_Type type, m_state_size_cb *size_callback,
2824
                             m_state_load_cb *load_callback,
2825
                             m_state_save_cb *save_callback)
2826
15.7k
{
2827
15.7k
    const uint32_t new_length = m->options.state_plugins_length + 1;
2828
15.7k
    Messenger_State_Plugin *temp = (Messenger_State_Plugin *)mem_vrealloc(
2829
15.7k
                                       m->mem, m->options.state_plugins, new_length, sizeof(Messenger_State_Plugin));
2830
2831
15.7k
    if (temp == nullptr) {
2832
52
        return false;
2833
52
    }
2834
2835
15.6k
    m->options.state_plugins = temp;
2836
15.6k
    m->options.state_plugins_length = new_length;
2837
2838
15.6k
    const uint8_t index = m->options.state_plugins_length - 1;
2839
15.6k
    m->options.state_plugins[index].type = type;
2840
15.6k
    m->options.state_plugins[index].size = size_callback;
2841
15.6k
    m->options.state_plugins[index].load = load_callback;
2842
15.6k
    m->options.state_plugins[index].save = save_callback;
2843
2844
15.6k
    return true;
2845
15.7k
}
2846
2847
static uint32_t m_plugin_size(const Messenger *_Nonnull m, State_Type type)
2848
6.65k
{
2849
22.4k
    for (uint8_t i = 0; i < m->options.state_plugins_length; ++i) {
2850
22.4k
        const Messenger_State_Plugin plugin = m->options.state_plugins[i];
2851
2852
22.4k
        if (plugin.type == type) {
2853
6.65k
            return plugin.size(m);
2854
6.65k
        }
2855
22.4k
    }
2856
2857
0
    LOGGER_ERROR(m->log, "Unknown type encountered: %u", type);
2858
2859
0
    return UINT32_MAX;
2860
6.65k
}
2861
2862
/** return size of the messenger data (for saving). */
2863
uint32_t messenger_size(const Messenger *m)
2864
1.98k
{
2865
1.98k
    return m_state_plugins_size(m);
2866
1.98k
}
2867
2868
/** Save the messenger in data (must be allocated memory of size at least `Messenger_size()`) */
2869
uint8_t *messenger_save(const Messenger *m, uint8_t *data)
2870
994
{
2871
9.93k
    for (uint8_t i = 0; i < m->options.state_plugins_length; ++i) {
2872
8.93k
        const Messenger_State_Plugin plugin = m->options.state_plugins[i];
2873
8.93k
        data = plugin.save(m, data);
2874
8.93k
    }
2875
2876
994
    return data;
2877
994
}
2878
2879
// nospam state plugin
2880
static uint32_t nospam_keys_size(const Messenger *_Nonnull m)
2881
3.53k
{
2882
3.53k
    return sizeof(uint32_t) + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_SECRET_KEY_SIZE;
2883
3.53k
}
2884
2885
static State_Load_Status load_nospam_keys(Messenger *_Nonnull m, const uint8_t *_Nonnull data, uint32_t length)
2886
548
{
2887
548
    if (length != m_plugin_size(m, STATE_TYPE_NOSPAMKEYS)) {
2888
1
        return STATE_LOAD_STATUS_ERROR;
2889
1
    }
2890
2891
547
    uint32_t nospam;
2892
547
    lendian_bytes_to_host32(&nospam, data);
2893
547
    set_nospam(m->fr, nospam);
2894
547
    load_secret_key(m->net_crypto, data + sizeof(uint32_t) + CRYPTO_PUBLIC_KEY_SIZE);
2895
2896
547
    if (!pk_equal(data + sizeof(uint32_t), nc_get_self_public_key(m->net_crypto))) {
2897
4
        LOGGER_ERROR(m->log, "public key stored in savedata does not match its secret key");
2898
4
        return STATE_LOAD_STATUS_ERROR;
2899
4
    }
2900
2901
543
    return STATE_LOAD_STATUS_CONTINUE;
2902
547
}
2903
2904
static uint8_t *_Nonnull save_nospam_keys(const Messenger *_Nonnull m, uint8_t *_Nonnull data)
2905
994
{
2906
994
    const uint32_t len = m_plugin_size(m, STATE_TYPE_NOSPAMKEYS);
2907
994
    static_assert(sizeof(get_nospam(m->fr)) == sizeof(uint32_t), "nospam doesn't fit in a 32 bit int");
2908
994
    data = state_write_section_header(data, STATE_COOKIE_TYPE, len, STATE_TYPE_NOSPAMKEYS);
2909
994
    const uint32_t nospam = get_nospam(m->fr);
2910
994
    host_to_lendian_bytes32(data, nospam);
2911
994
    save_keys(m->net_crypto, data + sizeof(uint32_t));
2912
994
    data += len;
2913
994
    return data;
2914
994
}
2915
2916
// DHT state plugin
2917
static uint32_t m_dht_size(const Messenger *_Nonnull m)
2918
2.98k
{
2919
2.98k
    return dht_size(m->dht);
2920
2.98k
}
2921
2922
static uint8_t *_Nonnull save_dht(const Messenger *_Nonnull m, uint8_t *_Nonnull data)
2923
994
{
2924
994
    const uint32_t len = m_plugin_size(m, STATE_TYPE_DHT);
2925
994
    data = state_write_section_header(data, STATE_COOKIE_TYPE, len, STATE_TYPE_DHT);
2926
994
    dht_save(m->dht, data);
2927
994
    data += len;
2928
994
    return data;
2929
994
}
2930
2931
static State_Load_Status m_dht_load(Messenger *_Nonnull m, const uint8_t *_Nonnull data, uint32_t length)
2932
712
{
2933
712
    dht_load(m->dht, data, length); // TODO(endoffile78): Should we throw an error if dht_load fails?
2934
712
    return STATE_LOAD_STATUS_CONTINUE;
2935
712
}
2936
2937
// friendlist state plugin
2938
static uint32_t saved_friendslist_size(const Messenger *_Nonnull m)
2939
2.98k
{
2940
2.98k
    return count_friendlist(m) * friend_size();
2941
2.98k
}
2942
2943
static uint8_t *_Nonnull friends_list_save(const Messenger *_Nonnull m, uint8_t *_Nonnull data)
2944
994
{
2945
994
    const uint32_t len = m_plugin_size(m, STATE_TYPE_FRIENDS);
2946
994
    data = state_write_section_header(data, STATE_COOKIE_TYPE, len, STATE_TYPE_FRIENDS);
2947
2948
994
    uint32_t num = 0;
2949
994
    uint8_t *cur_data = data;
2950
2951
1.13k
    for (uint32_t i = 0; i < m->numfriends; ++i) {
2952
138
        if (m->friendlist[i].status > 0) {
2953
138
            struct Saved_Friend temp = { 0 };
2954
138
            temp.status = m->friendlist[i].status;
2955
138
            memcpy(temp.real_pk, m->friendlist[i].real_pk, CRYPTO_PUBLIC_KEY_SIZE);
2956
2957
138
            if (temp.status < 3) {
2958
                // TODO(iphydf): Use uint16_t and min_u16 here.
2959
32
                const size_t friendrequest_length =
2960
32
                    min_u32(m->friendlist[i].info_size,
2961
32
                            min_u32(SAVED_FRIEND_REQUEST_SIZE, MAX_FRIEND_REQUEST_DATA_SIZE));
2962
32
                memcpy(temp.info, m->friendlist[i].info, friendrequest_length);
2963
2964
32
                temp.info_size = net_htons(m->friendlist[i].info_size);
2965
32
                temp.friendrequest_nospam = m->friendlist[i].friendrequest_nospam;
2966
106
            } else {
2967
106
                temp.status = 3;
2968
106
                memcpy(temp.name, m->friendlist[i].name, m->friendlist[i].name_length);
2969
106
                temp.name_length = net_htons(m->friendlist[i].name_length);
2970
106
                memcpy(temp.statusmessage, m->friendlist[i].statusmessage, m->friendlist[i].statusmessage_length);
2971
106
                temp.statusmessage_length = net_htons(m->friendlist[i].statusmessage_length);
2972
106
                temp.userstatus = m->friendlist[i].userstatus;
2973
2974
106
                net_pack_u64(temp.last_seen_time, m->friendlist[i].last_seen_time);
2975
106
            }
2976
2977
138
            uint8_t *next_data = friend_save(&temp, cur_data);
2978
138
            assert(next_data - cur_data == friend_size());
2979
138
#ifdef __LP64__
2980
138
            assert(memcmp(cur_data, &temp, friend_size()) == 0);
2981
138
#endif /* __LP64__ */
2982
138
            cur_data = next_data;
2983
138
            ++num;
2984
138
        }
2985
138
    }
2986
2987
994
    assert(cur_data - data == num * friend_size());
2988
994
    data += len;
2989
2990
994
    return data;
2991
994
}
2992
2993
static State_Load_Status friends_list_load(Messenger *_Nonnull m, const uint8_t *_Nonnull data, uint32_t length)
2994
318
{
2995
318
    const uint32_t l_friend_size = friend_size();
2996
2997
318
    if (length % l_friend_size != 0) {
2998
1
        return STATE_LOAD_STATUS_ERROR; // TODO(endoffile78): error or continue?
2999
1
    }
3000
3001
317
    const uint32_t num = length / l_friend_size;
3002
317
    const uint8_t *cur_data = data;
3003
3004
853
    for (uint32_t i = 0; i < num; ++i) {
3005
536
        struct Saved_Friend temp = { 0 };
3006
536
        const uint8_t *next_data = friend_load(&temp, cur_data);
3007
536
        assert(next_data - cur_data == l_friend_size);
3008
3009
536
        cur_data = next_data;
3010
3011
536
        if (temp.status >= 3) {
3012
331
            const int fnum = m_addfriend_norequest(m, temp.real_pk);
3013
3014
331
            if (fnum < 0) {
3015
52
                continue;
3016
52
            }
3017
3018
279
            setfriendname(m, fnum, temp.name, net_ntohs(temp.name_length));
3019
279
            set_friend_statusmessage(m, fnum, temp.statusmessage, net_ntohs(temp.statusmessage_length));
3020
279
            set_friend_userstatus(m, fnum, temp.userstatus);
3021
279
            net_unpack_u64(temp.last_seen_time, &m->friendlist[fnum].last_seen_time);
3022
279
        } else if (temp.status != 0) {
3023
            /* TODO(irungentoo): This is not a good way to do this. */
3024
129
            uint8_t address[FRIEND_ADDRESS_SIZE];
3025
129
            pk_copy(address, temp.real_pk);
3026
129
            memcpy(address + CRYPTO_PUBLIC_KEY_SIZE, &temp.friendrequest_nospam, sizeof(uint32_t));
3027
129
            uint16_t checksum = data_checksum(address, FRIEND_ADDRESS_SIZE - sizeof(checksum));
3028
129
            memcpy(address + CRYPTO_PUBLIC_KEY_SIZE + sizeof(uint32_t), &checksum, sizeof(checksum));
3029
129
            m_addfriend(m, address, temp.info, net_ntohs(temp.info_size));
3030
129
        }
3031
536
    }
3032
3033
317
    return STATE_LOAD_STATUS_CONTINUE;
3034
317
}
3035
3036
static void pack_groupchats(const GC_Session *_Nonnull c, Bin_Pack *_Nonnull bp)
3037
2.25k
{
3038
2.25k
    assert(bp != nullptr && c != nullptr);
3039
2.25k
    bin_pack_array(bp, gc_count_groups(c));
3040
3041
5.50k
    for (uint32_t i = 0; i < c->chats_index; ++i) { // this loop must match the one in gc_count_groups()
3042
3.25k
        const GC_Chat *chat = &c->chats[i];
3043
3044
3.25k
        if (!gc_group_is_valid(chat)) {
3045
1.80k
            continue;
3046
1.80k
        }
3047
3048
1.45k
        gc_group_save(chat, bp);
3049
1.45k
    }
3050
2.25k
}
3051
3052
static bool pack_groupchats_handler(const void *_Nonnull obj, const Logger *_Nonnull logger, Bin_Pack *_Nonnull bp)
3053
2.25k
{
3054
2.25k
    const GC_Session *session = (const GC_Session *)obj;
3055
2.25k
    pack_groupchats(session, bp);
3056
2.25k
    return true;  // TODO(iphydf): Return bool from pack functions.
3057
2.25k
}
3058
3059
static uint32_t saved_groups_size(const Messenger *_Nonnull m)
3060
2.11k
{
3061
2.11k
    const GC_Session *session = m->group_handler;
3062
2.11k
    return bin_pack_obj_size(pack_groupchats_handler, session, m->log);
3063
2.11k
}
3064
3065
static uint8_t *_Nonnull groups_save(const Messenger *_Nonnull m, uint8_t *_Nonnull data)
3066
986
{
3067
986
    const GC_Session *c = m->group_handler;
3068
3069
986
    const uint32_t num_groups = gc_count_groups(c);
3070
3071
986
    if (num_groups == 0) {
3072
846
        return data;
3073
846
    }
3074
3075
140
    const uint32_t len = m_plugin_size(m, STATE_TYPE_GROUPS);
3076
3077
140
    if (len == 0) {
3078
0
        return data;
3079
0
    }
3080
3081
140
    data = state_write_section_header(data, STATE_COOKIE_TYPE, len, STATE_TYPE_GROUPS);
3082
3083
140
    if (!bin_pack_obj(pack_groupchats_handler, c, m->log, data, len)) {
3084
0
        LOGGER_FATAL(m->log, "failed to pack group chats into buffer of length %u", len);
3085
0
        return data;
3086
0
    }
3087
3088
140
    data += len;
3089
3090
140
    LOGGER_DEBUG(m->log, "Saved %u groups (length %u)", num_groups, len);
3091
3092
140
    return data;
3093
140
}
3094
3095
static bool handle_groups_load(void *_Nonnull obj, Bin_Unpack *_Nonnull bu)
3096
24.5k
{
3097
24.5k
    Messenger *m = (Messenger *)obj;
3098
3099
24.5k
    uint32_t num_groups;
3100
24.5k
    if (!bin_unpack_array(bu, &num_groups)) {
3101
34
        LOGGER_ERROR(m->log, "msgpack failed to unpack groupchats array: expected array");
3102
34
        return false;
3103
34
    }
3104
3105
24.5k
    LOGGER_DEBUG(m->log, "Loading %u groups", num_groups);
3106
3107
24.6k
    for (uint32_t i = 0; i < num_groups; ++i) {
3108
24.4k
        const int group_number = gc_group_load(m->group_handler, bu);
3109
3110
24.4k
        if (group_number < 0) {
3111
24.3k
            LOGGER_WARNING(m->log, "Failed to load group %u", i);
3112
            // Can't recover trivially. We may need to skip over some data here.
3113
24.3k
            break;
3114
24.3k
        }
3115
24.4k
    }
3116
3117
24.5k
    LOGGER_DEBUG(m->log, "Successfully loaded %u groups", gc_count_groups(m->group_handler));
3118
3119
24.5k
    return true;
3120
24.5k
}
3121
3122
static State_Load_Status groups_load(Messenger *_Nonnull m, const uint8_t *_Nonnull data, uint32_t length)
3123
24.5k
{
3124
24.5k
    if (!bin_unpack_obj(m->mem, handle_groups_load, m, data, length)) {
3125
34
        LOGGER_ERROR(m->log, "msgpack failed to unpack groupchats array");
3126
34
        return STATE_LOAD_STATUS_ERROR;
3127
34
    }
3128
3129
24.5k
    return STATE_LOAD_STATUS_CONTINUE;
3130
24.5k
}
3131
3132
// name state plugin
3133
static uint32_t name_size(const Messenger *_Nonnull m)
3134
2.98k
{
3135
2.98k
    return m->name_length;
3136
2.98k
}
3137
3138
static uint8_t *_Nonnull save_name(const Messenger *_Nonnull m, uint8_t *_Nonnull data)
3139
994
{
3140
994
    const uint32_t len = m_plugin_size(m, STATE_TYPE_NAME);
3141
994
    data = state_write_section_header(data, STATE_COOKIE_TYPE, len, STATE_TYPE_NAME);
3142
994
    memcpy(data, m->name, len);
3143
994
    data += len;
3144
994
    return data;
3145
994
}
3146
3147
static State_Load_Status load_name(Messenger *_Nonnull m, const uint8_t *_Nonnull data, uint32_t length)
3148
461
{
3149
461
    if (length > 0 && length <= MAX_NAME_LENGTH) {
3150
322
        setname(m, data, length);
3151
322
    }
3152
3153
461
    return STATE_LOAD_STATUS_CONTINUE;
3154
461
}
3155
3156
// status message state plugin
3157
static uint32_t status_message_size(const Messenger *_Nonnull m)
3158
2.98k
{
3159
2.98k
    return m->statusmessage_length;
3160
2.98k
}
3161
3162
static uint8_t *_Nonnull save_status_message(const Messenger *_Nonnull m, uint8_t *_Nonnull data)
3163
994
{
3164
994
    const uint32_t len = m_plugin_size(m, STATE_TYPE_STATUSMESSAGE);
3165
994
    data = state_write_section_header(data, STATE_COOKIE_TYPE, len, STATE_TYPE_STATUSMESSAGE);
3166
994
    memcpy(data, m->statusmessage, len);
3167
994
    data += len;
3168
994
    return data;
3169
994
}
3170
3171
static State_Load_Status load_status_message(Messenger *_Nonnull m, const uint8_t *_Nonnull data, uint32_t length)
3172
730
{
3173
730
    if (length > 0 && length <= MAX_STATUSMESSAGE_LENGTH) {
3174
625
        m_set_statusmessage(m, data, length);
3175
625
    }
3176
3177
730
    return STATE_LOAD_STATUS_CONTINUE;
3178
730
}
3179
3180
// status state plugin
3181
static uint32_t status_size(const Messenger *_Nonnull m)
3182
2.98k
{
3183
2.98k
    return 1;
3184
2.98k
}
3185
3186
static uint8_t *_Nonnull save_status(const Messenger *_Nonnull m, uint8_t *_Nonnull data)
3187
994
{
3188
994
    const uint32_t len = m_plugin_size(m, STATE_TYPE_STATUS);
3189
994
    data = state_write_section_header(data, STATE_COOKIE_TYPE, len, STATE_TYPE_STATUS);
3190
994
    *data = m->userstatus;
3191
994
    data += len;
3192
994
    return data;
3193
994
}
3194
3195
static State_Load_Status load_status(Messenger *_Nonnull m, const uint8_t *_Nonnull data, uint32_t length)
3196
626
{
3197
626
    if (length == 1) {
3198
587
        m_set_userstatus(m, *data);
3199
587
    }
3200
3201
626
    return STATE_LOAD_STATUS_CONTINUE;
3202
626
}
3203
3204
// TCP Relay state plugin
3205
static uint32_t tcp_relay_size(const Messenger *_Nonnull m)
3206
1.98k
{
3207
1.98k
    return NUM_SAVED_TCP_RELAYS * packed_node_size(net_family_tcp_ipv6());
3208
1.98k
}
3209
3210
static uint8_t *_Nonnull save_tcp_relays(const Messenger *_Nonnull m, uint8_t *_Nonnull data)
3211
994
{
3212
994
    Node_format relays[NUM_SAVED_TCP_RELAYS] = {{{0}}};
3213
994
    uint8_t *temp_data = data;
3214
994
    data = state_write_section_header(temp_data, STATE_COOKIE_TYPE, 0, STATE_TYPE_TCP_RELAY);
3215
3216
994
    if (m->num_loaded_relays > 0) {
3217
32
        memcpy(relays, m->loaded_relays, sizeof(Node_format) * m->num_loaded_relays);
3218
32
    }
3219
3220
994
    uint32_t num = m->num_loaded_relays;
3221
994
    num += copy_connected_tcp_relays(m->net_crypto, relays + num, NUM_SAVED_TCP_RELAYS - num);
3222
3223
994
    const int l = pack_nodes(m->log, data, NUM_SAVED_TCP_RELAYS * packed_node_size(net_family_tcp_ipv6()), relays, num);
3224
3225
994
    if (l > 0) {
3226
32
        const uint32_t len = l;
3227
32
        data = state_write_section_header(temp_data, STATE_COOKIE_TYPE, len, STATE_TYPE_TCP_RELAY);
3228
32
        data += len;
3229
32
    }
3230
3231
994
    return data;
3232
994
}
3233
3234
static State_Load_Status load_tcp_relays(Messenger *_Nonnull m, const uint8_t *_Nonnull data, uint32_t length)
3235
640
{
3236
640
    if (length > 0) {
3237
564
        const int num = unpack_nodes(m->loaded_relays, NUM_SAVED_TCP_RELAYS, nullptr, data, length, true);
3238
3239
564
        if (num == -1) {
3240
105
            m->num_loaded_relays = 0;
3241
105
            return STATE_LOAD_STATUS_CONTINUE;
3242
105
        }
3243
3244
459
        m->num_loaded_relays = num;
3245
459
        m->has_added_relays = false;
3246
459
    }
3247
3248
535
    return STATE_LOAD_STATUS_CONTINUE;
3249
640
}
3250
3251
// path node state plugin
3252
static uint32_t path_node_size(const Messenger *_Nonnull m)
3253
1.98k
{
3254
1.98k
    return NUM_SAVED_PATH_NODES * packed_node_size(net_family_tcp_ipv6());
3255
1.98k
}
3256
3257
static uint8_t *_Nonnull save_path_nodes(const Messenger *_Nonnull m, uint8_t *_Nonnull data)
3258
994
{
3259
994
    Node_format nodes[NUM_SAVED_PATH_NODES] = {{{0}}};
3260
994
    uint8_t *temp_data = data;
3261
994
    data = state_write_section_header(data, STATE_COOKIE_TYPE, 0, STATE_TYPE_PATH_NODE);
3262
994
    const unsigned int num = onion_backup_nodes(m->onion_c, nodes, NUM_SAVED_PATH_NODES);
3263
994
    const int l = pack_nodes(m->log, data, NUM_SAVED_PATH_NODES * packed_node_size(net_family_tcp_ipv6()), nodes, num);
3264
3265
994
    if (l > 0) {
3266
38
        const uint32_t len = l;
3267
38
        data = state_write_section_header(temp_data, STATE_COOKIE_TYPE, len, STATE_TYPE_PATH_NODE);
3268
38
        data += len;
3269
38
    }
3270
3271
994
    return data;
3272
994
}
3273
3274
static State_Load_Status load_path_nodes(Messenger *_Nonnull m, const uint8_t *_Nonnull data, uint32_t length)
3275
490
{
3276
490
    if (length > 0) {
3277
418
        Node_format nodes[NUM_SAVED_PATH_NODES];
3278
418
        const int num = unpack_nodes(nodes, NUM_SAVED_PATH_NODES, nullptr, data, length, false);
3279
3280
418
        if (num == -1) {
3281
46
            return STATE_LOAD_STATUS_CONTINUE;
3282
46
        }
3283
3284
2.84k
        for (int i = 0; i < num; ++i) {
3285
2.47k
            onion_add_bs_path_node(m->onion_c, &nodes[i].ip_port, nodes[i].public_key);
3286
2.47k
        }
3287
372
    }
3288
3289
444
    return STATE_LOAD_STATUS_CONTINUE;
3290
490
}
3291
3292
static void m_register_default_plugins(Messenger *_Nonnull m)
3293
1.79k
{
3294
1.79k
    m_register_state_plugin(m, STATE_TYPE_NOSPAMKEYS, nospam_keys_size, load_nospam_keys, save_nospam_keys);
3295
1.79k
    m_register_state_plugin(m, STATE_TYPE_DHT, m_dht_size, m_dht_load, save_dht);
3296
1.79k
    m_register_state_plugin(m, STATE_TYPE_FRIENDS, saved_friendslist_size, friends_list_load, friends_list_save);
3297
1.79k
    m_register_state_plugin(m, STATE_TYPE_NAME, name_size, load_name, save_name);
3298
1.79k
    m_register_state_plugin(m, STATE_TYPE_STATUSMESSAGE, status_message_size, load_status_message,
3299
1.79k
                            save_status_message);
3300
1.79k
    m_register_state_plugin(m, STATE_TYPE_STATUS, status_size, load_status, save_status);
3301
1.79k
    if (m->options.groups_persistence_enabled) {
3302
1.37k
        m_register_state_plugin(m, STATE_TYPE_GROUPS, saved_groups_size, groups_load, groups_save);
3303
1.37k
    }
3304
1.79k
    m_register_state_plugin(m, STATE_TYPE_TCP_RELAY, tcp_relay_size, load_tcp_relays, save_tcp_relays);
3305
1.79k
    m_register_state_plugin(m, STATE_TYPE_PATH_NODE, path_node_size, load_path_nodes, save_path_nodes);
3306
1.79k
}
3307
3308
bool messenger_load_state_section(Messenger *m, const uint8_t *data, uint32_t length, uint16_t type,
3309
                                  State_Load_Status *status)
3310
29.9k
{
3311
202k
    for (uint8_t i = 0; i < m->options.state_plugins_length; ++i) {
3312
201k
        const Messenger_State_Plugin *const plugin = &m->options.state_plugins[i];
3313
3314
201k
        if (plugin->type == type) {
3315
29.0k
            *status = plugin->load(m, data, length);
3316
29.0k
            return true;
3317
29.0k
        }
3318
201k
    }
3319
3320
858
    return false;
3321
29.9k
}
3322
3323
/** @brief Return the number of friends in the instance m.
3324
 *
3325
 * You should use this to determine how much memory to allocate
3326
 * for copy_friendlist.
3327
 */
3328
uint32_t count_friendlist(const Messenger *m)
3329
29.0k
{
3330
29.0k
    uint32_t ret = 0;
3331
3332
97.8k
    for (uint32_t i = 0; i < m->numfriends; ++i) {
3333
68.8k
        if (m->friendlist[i].status > 0) {
3334
68.8k
            ++ret;
3335
68.8k
        }
3336
68.7k
    }
3337
3338
29.0k
    return ret;
3339
29.0k
}
3340
3341
/** @brief Copy a list of valid friend IDs into the array out_list.
3342
 * If out_list is NULL, returns 0.
3343
 * Otherwise, returns the number of elements copied.
3344
 * If the array was too small, the contents
3345
 * of out_list will be truncated to list_size.
3346
 */
3347
uint32_t copy_friendlist(const Messenger *m, uint32_t *out_list, uint32_t list_size)
3348
1
{
3349
1
    if (out_list == nullptr) {
3350
0
        return 0;
3351
0
    }
3352
3353
1
    if (m->numfriends == 0) {
3354
0
        return 0;
3355
0
    }
3356
3357
1
    uint32_t ret = 0;
3358
3359
3
    for (uint32_t i = 0; i < m->numfriends; ++i) {
3360
2
        if (ret >= list_size) {
3361
0
            break; /* Abandon ship */
3362
0
        }
3363
3364
2
        if (m->friendlist[i].status > 0) {
3365
2
            out_list[ret] = i;
3366
2
            ++ret;
3367
2
        }
3368
2
    }
3369
3370
1
    return ret;
3371
1
}
3372
3373
static fr_friend_request_cb m_handle_friend_request;
3374
static void m_handle_friend_request(
3375
    void *object, const uint8_t *public_key, const uint8_t *message, size_t length, void *user_data)
3376
137
{
3377
137
    Messenger *m = (Messenger *)object;
3378
137
    assert(m != nullptr);
3379
137
    m->friend_request(m, public_key, message, length, user_data);
3380
137
}
3381
3382
/** @brief Run this at startup.
3383
 *
3384
 * @return allocated instance of Messenger on success.
3385
 * @retval 0 if there are problems.
3386
 *
3387
 * if error is not NULL it will be set to one of the values in the enum above.
3388
 */
3389
Messenger *new_messenger(Mono_Time *mono_time, const Memory *mem, const Random *rng, const Network *ns,
3390
                         Messenger_Options *options, Messenger_Error *error)
3391
1.88k
{
3392
1.88k
    if (options == nullptr) {
3393
0
        return nullptr;
3394
0
    }
3395
3396
1.88k
    if (error != nullptr) {
3397
1.88k
        *error = MESSENGER_ERROR_OTHER;
3398
1.88k
    }
3399
3400
1.88k
    Messenger *m = (Messenger *)mem_alloc(mem, sizeof(Messenger));
3401
3402
1.88k
    if (m == nullptr) {
3403
1
        return nullptr;
3404
1
    }
3405
3406
1.88k
    m->log = options->log;
3407
1.88k
    m->mono_time = mono_time;
3408
1.88k
    m->mem = mem;
3409
1.88k
    m->rng = rng;
3410
1.88k
    m->ns = ns;
3411
1.88k
    m->forwarding = nullptr;
3412
1.88k
    m->announce = nullptr;
3413
1.88k
    m->tcp_server = nullptr;
3414
3415
1.88k
    Friend_Requests *fr = friendreq_new(mem);
3416
1.88k
    if (fr == nullptr) {
3417
1
        mem_delete(mem, m);
3418
1
        return nullptr;
3419
1
    }
3420
1.88k
    m->fr = fr;
3421
3422
1.88k
    unsigned int net_err = 0;
3423
3424
1.88k
    if (!options->udp_disabled && options->proxy_info.proxy_type != TCP_PROXY_NONE) {
3425
        // We don't currently support UDP over proxy.
3426
71
        LOGGER_INFO(m->log, "UDP enabled and proxy set: disabling UDP");
3427
71
        options->udp_disabled = true;
3428
71
    }
3429
3430
1.88k
    Networking_Core *net;
3431
1.88k
    if (options->udp_disabled) {
3432
116
        net = new_networking_no_udp(m->log, m->mem, m->ns);
3433
1.76k
    } else {
3434
1.76k
        IP ip;
3435
1.76k
        ip_init(&ip, options->ipv6enabled);
3436
1.76k
        net = new_networking_ex(m->log, m->mem, m->ns, &ip, options->port_range[0], options->port_range[1], &net_err);
3437
1.76k
    }
3438
3439
1.88k
    if (net == nullptr) {
3440
5
        friendreq_kill(m->fr);
3441
3442
5
        if (error != nullptr && net_err == 1) {
3443
2
            LOGGER_WARNING(m->log, "network initialisation failed (no ports available)");
3444
2
            *error = MESSENGER_ERROR_PORT;
3445
2
        }
3446
3447
5
        mem_delete(mem, m);
3448
5
        return nullptr;
3449
5
    }
3450
1.87k
    m->net = net;
3451
3452
1.87k
    DHT *dht = new_dht(m->log, m->mem, m->rng, m->ns, m->mono_time, m->net, options->hole_punching_enabled, options->local_discovery_enabled);
3453
1.87k
    if (dht == nullptr) {
3454
24
        kill_networking(m->net);
3455
24
        friendreq_kill(m->fr);
3456
24
        mem_delete(mem, m);
3457
24
        return nullptr;
3458
24
    }
3459
1.85k
    m->dht = dht;
3460
3461
1.85k
    Net_Profile *tcp_np = netprof_new(m->log, mem);
3462
1.85k
    if (tcp_np == nullptr) {
3463
2
        LOGGER_WARNING(m->log, "TCP netprof initialisation failed");
3464
2
        kill_dht(m->dht);
3465
2
        kill_networking(m->net);
3466
2
        friendreq_kill(m->fr);
3467
2
        mem_delete(mem, m);
3468
2
        return nullptr;
3469
2
    }
3470
1.84k
    m->tcp_np = tcp_np;
3471
3472
1.84k
    Net_Crypto *net_crypto = new_net_crypto(m->log, m->mem, m->rng, m->ns, m->mono_time, m->net, m->dht, &m_dht_funcs, &options->proxy_info, m->tcp_np);
3473
3474
1.84k
    if (net_crypto == nullptr) {
3475
5
        LOGGER_WARNING(m->log, "net_crypto initialisation failed");
3476
3477
5
        netprof_kill(mem, m->tcp_np);
3478
5
        kill_dht(m->dht);
3479
5
        kill_networking(m->net);
3480
5
        friendreq_kill(m->fr);
3481
5
        mem_delete(mem, m);
3482
5
        return nullptr;
3483
5
    }
3484
1.84k
    m->net_crypto = net_crypto;
3485
3486
1.84k
    GC_Announces_List *group_announce = new_gca_list(m->mem);
3487
1.84k
    if (group_announce == nullptr) {
3488
2
        LOGGER_WARNING(m->log, "DHT group chats initialisation failed");
3489
3490
2
        kill_net_crypto(m->net_crypto);
3491
2
        netprof_kill(mem, m->tcp_np);
3492
2
        kill_dht(m->dht);
3493
2
        kill_networking(m->net);
3494
2
        friendreq_kill(m->fr);
3495
2
        mem_delete(mem, m);
3496
2
        return nullptr;
3497
2
    }
3498
1.84k
    m->group_announce = group_announce;
3499
3500
1.84k
    if (options->dht_announcements_enabled) {
3501
1.84k
        m->forwarding = new_forwarding(m->log, m->mem, m->rng, m->mono_time, m->dht, m->net);
3502
1.84k
        if (m->forwarding != nullptr) {
3503
1.82k
            m->announce = new_announcements(m->log, m->mem, m->rng, m->mono_time, m->forwarding, m->dht, m->net);
3504
1.82k
        }
3505
1.84k
    }
3506
3507
1.84k
    Onion *onion = new_onion(m->log, m->mem, m->mono_time, m->rng, m->dht, m->net);
3508
1.84k
    Onion_Announce *onion_a = new_onion_announce(m->log, m->mem, m->rng, m->mono_time, m->dht, m->net);
3509
1.84k
    Onion_Client *onion_c = new_onion_client(m->log, m->mem, m->rng, m->mono_time, m->net_crypto, m->dht, m->net);
3510
1.84k
    Friend_Connections *fr_c = nullptr;
3511
3512
1.84k
    if (onion_c != nullptr) {
3513
1.83k
        fr_c = new_friend_connections(m->log, m->mem, m->mono_time, m->ns, onion_c, m->dht, m->net_crypto, m->net, options->local_discovery_enabled);
3514
1.83k
    }
3515
3516
1.84k
    if ((options->dht_announcements_enabled && (m->forwarding == nullptr || m->announce == nullptr)) ||
3517
1.84k
            onion == nullptr || onion_a == nullptr || onion_c == nullptr || fr_c == nullptr) {
3518
28
        LOGGER_WARNING(m->log, "onion initialisation failed");
3519
3520
28
        kill_onion(onion);
3521
28
        kill_onion_announce(onion_a);
3522
28
        kill_onion_client(onion_c);
3523
28
        kill_gca(m->group_announce);
3524
28
        kill_friend_connections(fr_c);
3525
28
        kill_announcements(m->announce);
3526
28
        kill_forwarding(m->forwarding);
3527
28
        kill_net_crypto(m->net_crypto);
3528
28
        netprof_kill(mem, m->tcp_np);
3529
28
        kill_dht(m->dht);
3530
28
        kill_networking(m->net);
3531
28
        friendreq_kill(m->fr);
3532
28
        mem_delete(mem, m);
3533
28
        return nullptr;
3534
28
    }
3535
1.81k
    m->onion = onion;
3536
1.81k
    m->onion_a = onion_a;
3537
1.81k
    m->onion_c = onion_c;
3538
1.81k
    m->fr_c = fr_c;
3539
3540
1.81k
    gca_onion_init(m->group_announce, m->onion_a);
3541
3542
1.81k
    GC_Session *group_handler = new_dht_groupchats(m);
3543
1.81k
    if (group_handler == nullptr) {
3544
2
        LOGGER_WARNING(m->log, "conferences initialisation failed");
3545
3546
2
        kill_onion(m->onion);
3547
2
        kill_onion_announce(m->onion_a);
3548
2
        kill_onion_client(m->onion_c);
3549
2
        kill_gca(m->group_announce);
3550
2
        kill_friend_connections(m->fr_c);
3551
2
        kill_announcements(m->announce);
3552
2
        kill_forwarding(m->forwarding);
3553
2
        kill_net_crypto(m->net_crypto);
3554
2
        netprof_kill(mem, m->tcp_np);
3555
2
        kill_dht(m->dht);
3556
2
        kill_networking(m->net);
3557
2
        friendreq_kill(m->fr);
3558
2
        mem_delete(mem, m);
3559
2
        return nullptr;
3560
2
    }
3561
1.81k
    m->group_handler = group_handler;
3562
3563
1.81k
    if (options->tcp_server_port != 0) {
3564
22
        m->tcp_server = new_tcp_server(m->log, m->mem, m->rng, m->ns, options->ipv6enabled, 1,
3565
22
                                       &options->tcp_server_port, dht_get_self_secret_key(m->dht),
3566
22
                                       m->onion, m->forwarding);
3567
3568
22
        if (m->tcp_server == nullptr) {
3569
18
            LOGGER_WARNING(m->log, "TCP server initialisation failed");
3570
3571
18
            kill_onion(m->onion);
3572
18
            kill_onion_announce(m->onion_a);
3573
18
            kill_dht_groupchats(m->group_handler);
3574
18
            kill_friend_connections(m->fr_c);
3575
18
            kill_onion_client(m->onion_c);
3576
18
            kill_gca(m->group_announce);
3577
18
            kill_announcements(m->announce);
3578
18
            kill_forwarding(m->forwarding);
3579
18
            kill_net_crypto(m->net_crypto);
3580
18
            netprof_kill(mem, m->tcp_np);
3581
18
            kill_dht(m->dht);
3582
18
            kill_networking(m->net);
3583
18
            friendreq_kill(m->fr);
3584
18
            mem_delete(mem, m);
3585
3586
18
            if (error != nullptr) {
3587
18
                *error = MESSENGER_ERROR_TCP_SERVER;
3588
18
            }
3589
3590
18
            return nullptr;
3591
18
        }
3592
22
    }
3593
3594
1.79k
    m->options = *options;
3595
1.79k
    friendreq_init(m->fr, m->fr_c);
3596
1.79k
    set_nospam(m->fr, random_u32(m->rng));
3597
1.79k
    set_filter_function(m->fr, &friend_already_added, m);
3598
3599
1.79k
    m->lastdump = 0;
3600
1.79k
    m->is_receiving_file = 0;
3601
3602
1.79k
    m_register_default_plugins(m);
3603
1.79k
    callback_friendrequest(m->fr, m_handle_friend_request, m);
3604
3605
1.79k
    if (error != nullptr) {
3606
1.79k
        *error = MESSENGER_ERROR_NONE;
3607
1.79k
    }
3608
3609
1.79k
    return m;
3610
1.81k
}
3611
3612
/** @brief Run this before closing shop.
3613
 *
3614
 * Free all datastructures.
3615
 */
3616
void kill_messenger(Messenger *m)
3617
1.79k
{
3618
1.79k
    if (m == nullptr) {
3619
0
        return;
3620
0
    }
3621
3622
1.79k
    if (m->tcp_server != nullptr) {
3623
4
        kill_tcp_server(m->tcp_server);
3624
4
    }
3625
3626
1.79k
    kill_onion(m->onion);
3627
1.79k
    kill_onion_announce(m->onion_a);
3628
1.79k
    kill_dht_groupchats(m->group_handler);
3629
1.79k
    kill_friend_connections(m->fr_c);
3630
1.79k
    kill_onion_client(m->onion_c);
3631
1.79k
    kill_gca(m->group_announce);
3632
1.79k
    kill_announcements(m->announce);
3633
1.79k
    kill_forwarding(m->forwarding);
3634
1.79k
    kill_net_crypto(m->net_crypto);
3635
1.79k
    netprof_kill(m->mem, m->tcp_np);
3636
1.79k
    kill_dht(m->dht);
3637
1.79k
    kill_networking(m->net);
3638
3639
2.54k
    for (uint32_t i = 0; i < m->numfriends; ++i) {
3640
747
        clear_receipts(m, i);
3641
747
    }
3642
3643
1.79k
    mem_delete(m->mem, m->friendlist);
3644
1.79k
    friendreq_kill(m->fr);
3645
3646
1.79k
    mem_delete(m->mem, m->options.state_plugins);
3647
1.79k
    mem_delete(m->mem, m);
3648
1.79k
}
3649
3650
bool m_is_receiving_file(Messenger *m)
3651
346
{
3652
    // Only run the expensive loop below once every 64 tox_iterate calls.
3653
346
    const uint8_t skip_count = 64;
3654
3655
346
    if (m->is_receiving_file != 0) {
3656
0
        --m->is_receiving_file;
3657
0
        return true;
3658
0
    }
3659
3660
    // TODO(iphydf): This is a very expensive loop. Consider keeping track of
3661
    // the number of live file transfers.
3662
541
    for (size_t friend_number = 0; friend_number < m->numfriends; ++friend_number) {
3663
50.1k
        for (size_t i = 0; i < MAX_CONCURRENT_FILE_PIPES; ++i) {
3664
49.9k
            if (m->friendlist[friend_number].file_receiving[i].status == FILESTATUS_TRANSFERRING) {
3665
0
                m->is_receiving_file = skip_count;
3666
0
                return true;
3667
0
            }
3668
49.9k
        }
3669
195
    }
3670
3671
346
    return false;
3672
346
}