Coverage Report

Created: 2024-01-26 01:52

/work/toxav/msi.c
Line
Count
Source (jump to first uncovered line)
1
/* SPDX-License-Identifier: GPL-3.0-or-later
2
 * Copyright © 2016-2018 The TokTok team.
3
 * Copyright © 2013-2015 Tox project.
4
 */
5
#include "msi.h"
6
7
#include <assert.h>
8
#include <stdbool.h>
9
#include <stdlib.h>
10
#include <string.h>
11
12
#include "../toxcore/ccompat.h"
13
#include "../toxcore/logger.h"
14
#include "../toxcore/util.h"
15
16
#define MSI_MAXMSG_SIZE 256
17
18
/**
19
 * Protocol:
20
 *
21
 * `|id [1 byte]| |size [1 byte]| |data [$size bytes]| |...{repeat}| |0 {end byte}|`
22
 */
23
24
typedef enum MSIHeaderID {
25
    ID_REQUEST = 1,
26
    ID_ERROR,
27
    ID_CAPABILITIES,
28
} MSIHeaderID;
29
30
31
typedef enum MSIRequest {
32
    REQU_INIT,
33
    REQU_PUSH,
34
    REQU_POP,
35
} MSIRequest;
36
37
38
typedef struct MSIHeaderRequest {
39
    MSIRequest value;
40
    bool exists;
41
} MSIHeaderRequest;
42
43
typedef struct MSIHeaderError {
44
    MSIError value;
45
    bool exists;
46
} MSIHeaderError;
47
48
typedef struct MSIHeaderCapabilities {
49
    uint8_t value;
50
    bool exists;
51
} MSIHeaderCapabilities;
52
53
54
typedef struct MSIMessage {
55
    MSIHeaderRequest      request;
56
    MSIHeaderError        error;
57
    MSIHeaderCapabilities capabilities;
58
} MSIMessage;
59
60
61
static void msg_init(MSIMessage *dest, MSIRequest request);
62
static int msg_parse_in(const Logger *log, MSIMessage *dest, const uint8_t *data, uint16_t length);
63
static uint8_t *msg_parse_header_out(MSIHeaderID id, uint8_t *dest, const uint8_t *value, uint8_t value_len,
64
                                     uint16_t *length);
65
static int send_message(const Messenger *m, uint32_t friend_number, const MSIMessage *msg);
66
static int send_error(const Messenger *m, uint32_t friend_number, MSIError error);
67
static bool invoke_callback(MSICall *call, MSICallbackID cb);
68
static MSICall *get_call(MSISession *session, uint32_t friend_number);
69
static MSICall *new_call(MSISession *session, uint32_t friend_number);
70
static void kill_call(MSICall *call);
71
static void on_peer_status(Messenger *m, uint32_t friend_number, uint8_t connection_status, void *user_data);
72
static void handle_init(MSICall *call, const MSIMessage *msg);
73
static void handle_push(MSICall *call, const MSIMessage *msg);
74
static void handle_pop(MSICall *call, const MSIMessage *msg);
75
static void handle_msi_packet(Messenger *m, uint32_t friend_number, const uint8_t *data, uint16_t length, void *user_data);
76
77
78
/*
79
 * Public functions
80
 */
81
82
void msi_callback_invite(MSISession *session, msi_action_cb *callback)
83
6
{
84
6
    session->invite_callback = callback;
85
6
}
86
void msi_callback_start(MSISession *session, msi_action_cb *callback)
87
6
{
88
6
    session->start_callback = callback;
89
6
}
90
void msi_callback_end(MSISession *session, msi_action_cb *callback)
91
6
{
92
6
    session->end_callback = callback;
93
6
}
94
void msi_callback_error(MSISession *session, msi_action_cb *callback)
95
6
{
96
6
    session->error_callback = callback;
97
6
}
98
void msi_callback_peertimeout(MSISession *session, msi_action_cb *callback)
99
6
{
100
6
    session->peertimeout_callback = callback;
101
6
}
102
void msi_callback_capabilities(MSISession *session, msi_action_cb *callback)
103
6
{
104
6
    session->capabilities_callback = callback;
105
6
}
106
107
MSISession *msi_new(Messenger *m)
108
6
{
109
6
    if (m == nullptr) {
110
0
        return nullptr;
111
0
    }
112
113
6
    MSISession *retu = (MSISession *)calloc(1, sizeof(MSISession));
114
115
6
    if (retu == nullptr) {
116
0
        LOGGER_ERROR(m->log, "Allocation failed! Program might misbehave!");
117
0
        return nullptr;
118
0
    }
119
120
6
    if (create_recursive_mutex(retu->mutex) != 0) {
121
0
        LOGGER_ERROR(m->log, "Failed to init mutex! Program might misbehave");
122
0
        free(retu);
123
0
        return nullptr;
124
0
    }
125
126
6
    retu->messenger = m;
127
128
6
    m_callback_msi_packet(m, handle_msi_packet, retu);
129
130
    /* This is called when remote terminates session */
131
6
    m_callback_connectionstatus_internal_av(m, on_peer_status, retu);
132
133
6
    LOGGER_DEBUG(m->log, "New msi session: %p ", (void *)retu);
134
6
    return retu;
135
6
}
136
int msi_kill(MSISession *session, const Logger *log)
137
6
{
138
6
    if (session == nullptr) {
139
0
        LOGGER_ERROR(log, "Tried to terminate non-existing session");
140
0
        return -1;
141
0
    }
142
143
6
    m_callback_msi_packet(session->messenger, nullptr, nullptr);
144
145
6
    if (pthread_mutex_trylock(session->mutex) != 0) {
146
0
        LOGGER_ERROR(log, "Failed to acquire lock on msi mutex");
147
0
        return -1;
148
0
    }
149
150
6
    if (session->calls != nullptr) {
151
0
        MSIMessage msg;
152
0
        msg_init(&msg, REQU_POP);
153
154
0
        MSICall *it = get_call(session, session->calls_head);
155
156
0
        while (it != nullptr) {
157
0
            send_message(session->messenger, it->friend_number, &msg);
158
0
            MSICall *temp_it = it;
159
0
            it = it->next;
160
0
            kill_call(temp_it); /* This will eventually free session->calls */
161
0
        }
162
0
    }
163
164
6
    pthread_mutex_unlock(session->mutex);
165
6
    pthread_mutex_destroy(session->mutex);
166
167
6
    LOGGER_DEBUG(log, "Terminated session: %p", (void *)session);
168
6
    free(session);
169
6
    return 0;
170
6
}
171
int msi_invite(MSISession *session, MSICall **call, uint32_t friend_number, uint8_t capabilities)
172
11
{
173
11
    if (session == nullptr) {
174
0
        return -1;
175
0
    }
176
177
11
    LOGGER_DEBUG(session->messenger->log, "Session: %p Inviting friend: %u", (void *)session, friend_number);
178
179
11
    if (pthread_mutex_trylock(session->mutex) != 0) {
180
0
        LOGGER_ERROR(session->messenger->log, "Failed to acquire lock on msi mutex");
181
0
        return -1;
182
0
    }
183
184
11
    if (get_call(session, friend_number) != nullptr) {
185
0
        LOGGER_ERROR(session->messenger->log, "Already in a call");
186
0
        pthread_mutex_unlock(session->mutex);
187
0
        return -1;
188
0
    }
189
190
11
    MSICall *temp = new_call(session, friend_number);
191
192
11
    if (temp == nullptr) {
193
0
        pthread_mutex_unlock(session->mutex);
194
0
        return -1;
195
0
    }
196
197
11
    temp->self_capabilities = capabilities;
198
199
11
    MSIMessage msg;
200
11
    msg_init(&msg, REQU_INIT);
201
202
11
    msg.capabilities.exists = true;
203
11
    msg.capabilities.value = capabilities;
204
205
11
    send_message(temp->session->messenger, temp->friend_number, &msg);
206
207
11
    temp->state = MSI_CALL_REQUESTING;
208
209
11
    *call = temp;
210
211
11
    LOGGER_DEBUG(session->messenger->log, "Invite sent");
212
11
    pthread_mutex_unlock(session->mutex);
213
11
    return 0;
214
11
}
215
int msi_hangup(MSICall *call)
216
11
{
217
11
    if (call == nullptr || call->session == nullptr) {
218
0
        return -1;
219
0
    }
220
221
11
    MSISession *session = call->session;
222
223
11
    LOGGER_DEBUG(session->messenger->log, "Session: %p Hanging up call with friend: %u", (void *)call->session,
224
11
                 call->friend_number);
225
226
11
    if (pthread_mutex_trylock(session->mutex) != 0) {
227
0
        LOGGER_ERROR(session->messenger->log, "Failed to acquire lock on msi mutex");
228
0
        return -1;
229
0
    }
230
231
11
    if (call->state == MSI_CALL_INACTIVE) {
232
0
        LOGGER_ERROR(session->messenger->log, "Call is in invalid state!");
233
0
        pthread_mutex_unlock(session->mutex);
234
0
        return -1;
235
0
    }
236
237
11
    MSIMessage msg;
238
11
    msg_init(&msg, REQU_POP);
239
240
11
    send_message(session->messenger, call->friend_number, &msg);
241
242
11
    kill_call(call);
243
11
    pthread_mutex_unlock(session->mutex);
244
11
    return 0;
245
11
}
246
int msi_answer(MSICall *call, uint8_t capabilities)
247
9
{
248
9
    if (call == nullptr || call->session == nullptr) {
249
0
        return -1;
250
0
    }
251
252
9
    MSISession *session = call->session;
253
254
9
    LOGGER_DEBUG(session->messenger->log, "Session: %p Answering call from: %u", (void *)call->session,
255
9
                 call->friend_number);
256
257
9
    if (pthread_mutex_trylock(session->mutex) != 0) {
258
0
        LOGGER_ERROR(session->messenger->log, "Failed to acquire lock on msi mutex");
259
0
        return -1;
260
0
    }
261
262
9
    if (call->state != MSI_CALL_REQUESTED) {
263
        /* Though sending in invalid state will not cause anything weird
264
         * Its better to not do it like a maniac */
265
0
        LOGGER_ERROR(session->messenger->log, "Call is in invalid state!");
266
0
        pthread_mutex_unlock(session->mutex);
267
0
        return -1;
268
0
    }
269
270
9
    call->self_capabilities = capabilities;
271
272
9
    MSIMessage msg;
273
9
    msg_init(&msg, REQU_PUSH);
274
275
9
    msg.capabilities.exists = true;
276
9
    msg.capabilities.value = capabilities;
277
278
9
    send_message(session->messenger, call->friend_number, &msg);
279
280
9
    call->state = MSI_CALL_ACTIVE;
281
9
    pthread_mutex_unlock(session->mutex);
282
283
9
    return 0;
284
9
}
285
int msi_change_capabilities(MSICall *call, uint8_t capabilities)
286
13
{
287
13
    if (call == nullptr || call->session == nullptr) {
288
0
        return -1;
289
0
    }
290
291
13
    MSISession *session = call->session;
292
293
13
    LOGGER_DEBUG(session->messenger->log, "Session: %p Trying to change capabilities to friend %u", (void *)call->session,
294
13
                 call->friend_number);
295
296
13
    if (pthread_mutex_trylock(session->mutex) != 0) {
297
0
        LOGGER_ERROR(session->messenger->log, "Failed to acquire lock on msi mutex");
298
0
        return -1;
299
0
    }
300
301
13
    if (call->state != MSI_CALL_ACTIVE) {
302
0
        LOGGER_ERROR(session->messenger->log, "Call is in invalid state!");
303
0
        pthread_mutex_unlock(session->mutex);
304
0
        return -1;
305
0
    }
306
307
13
    call->self_capabilities = capabilities;
308
309
13
    MSIMessage msg;
310
13
    msg_init(&msg, REQU_PUSH);
311
312
13
    msg.capabilities.exists = true;
313
13
    msg.capabilities.value = capabilities;
314
315
13
    send_message(call->session->messenger, call->friend_number, &msg);
316
317
13
    pthread_mutex_unlock(session->mutex);
318
13
    return 0;
319
13
}
320
321
322
/**
323
 * Private functions
324
 */
325
static void msg_init(MSIMessage *dest, MSIRequest request)
326
44
{
327
44
    memset(dest, 0, sizeof(*dest));
328
44
    dest->request.exists = true;
329
44
    dest->request.value = request;
330
44
}
331
332
static bool check_size(const Logger *log, const uint8_t *bytes, int *constraint, uint8_t size)
333
77
{
334
77
    *constraint -= 2 + size;
335
336
77
    if (*constraint < 1) {
337
0
        LOGGER_ERROR(log, "Read over length!");
338
0
        return false;
339
0
    }
340
341
77
    if (bytes[1] != size) {
342
0
        LOGGER_ERROR(log, "Invalid data size!");
343
0
        return false;
344
0
    }
345
346
77
    return true;
347
77
}
348
349
/** Assumes size == 1 */
350
static bool check_enum_high(const Logger *log, const uint8_t *bytes, uint8_t enum_high)
351
44
{
352
44
    if (bytes[2] > enum_high) {
353
0
        LOGGER_ERROR(log, "Failed enum high limit!");
354
0
        return false;
355
0
    }
356
357
44
    return true;
358
44
}
359
360
361
static int msg_parse_in(const Logger *log, MSIMessage *dest, const uint8_t *data, uint16_t length)
362
44
{
363
    /* Parse raw data received from socket into MSIMessage struct */
364
365
44
    assert(dest != nullptr);
366
367
44
    if (length == 0 || data[length - 1] != 0) { /* End byte must have value 0 */
368
0
        LOGGER_ERROR(log, "Invalid end byte");
369
0
        return -1;
370
0
    }
371
372
44
    memset(dest, 0, sizeof(*dest));
373
374
44
    const uint8_t *it = data;
375
44
    int size_constraint = length;
376
377
121
    while (*it != 0) {/* until end byte is hit */
378
77
        switch (*it) {
379
44
            case ID_REQUEST: {
380
44
                if (!check_size(log, it, &size_constraint, 1) ||
381
44
                        !check_enum_high(log, it, REQU_POP)) {
382
0
                    return -1;
383
0
                }
384
385
44
                dest->request.value = (MSIRequest)it[2];
386
44
                dest->request.exists = true;
387
44
                it += 3;
388
44
                break;
389
44
            }
390
391
0
            case ID_ERROR: {
392
0
                if (!check_size(log, it, &size_constraint, 1) ||
393
0
                        !check_enum_high(log, it, MSI_E_UNDISCLOSED)) {
394
0
                    return -1;
395
0
                }
396
397
0
                dest->error.value = (MSIError)it[2];
398
0
                dest->error.exists = true;
399
0
                it += 3;
400
0
                break;
401
0
            }
402
403
33
            case ID_CAPABILITIES: {
404
33
                if (!check_size(log, it, &size_constraint, 1)) {
405
0
                    return -1;
406
0
                }
407
408
33
                dest->capabilities.value = it[2];
409
33
                dest->capabilities.exists = true;
410
33
                it += 3;
411
33
                break;
412
33
            }
413
414
0
            default: {
415
0
                LOGGER_ERROR(log, "Invalid id byte");
416
0
                return -1;
417
33
            }
418
77
        }
419
77
    }
420
421
44
    if (!dest->request.exists) {
422
0
        LOGGER_ERROR(log, "Invalid request field!");
423
0
        return -1;
424
0
    }
425
426
44
    return 0;
427
44
}
428
static uint8_t *msg_parse_header_out(MSIHeaderID id, uint8_t *dest, const uint8_t *value, uint8_t value_len,
429
                                     uint16_t *length)
430
77
{
431
    /* Parse a single header for sending */
432
77
    assert(dest != nullptr);
433
77
    assert(value != nullptr);
434
77
    assert(value_len != 0);
435
436
77
    *dest = id;
437
77
    ++dest;
438
77
    *dest = value_len;
439
77
    ++dest;
440
441
77
    memcpy(dest, value, value_len);
442
443
77
    *length += 2 + value_len;
444
445
77
    return dest + value_len; /* Set to next position ready to be written */
446
77
}
447
static int send_message(const Messenger *m, uint32_t friend_number, const MSIMessage *msg)
448
44
{
449
    /* Parse and send message */
450
44
    assert(m != nullptr);
451
452
44
    uint8_t parsed [MSI_MAXMSG_SIZE];
453
454
44
    uint8_t *it = parsed;
455
44
    uint16_t size = 0;
456
457
44
    if (msg->request.exists) {
458
44
        uint8_t cast = msg->request.value;
459
44
        it = msg_parse_header_out(ID_REQUEST, it, &cast,
460
44
                                  sizeof(cast), &size);
461
44
    } else {
462
0
        LOGGER_DEBUG(m->log, "Must have request field");
463
0
        return -1;
464
0
    }
465
466
44
    if (msg->error.exists) {
467
0
        uint8_t cast = msg->error.value;
468
0
        it = msg_parse_header_out(ID_ERROR, it, &cast,
469
0
                                  sizeof(cast), &size);
470
0
    }
471
472
44
    if (msg->capabilities.exists) {
473
33
        it = msg_parse_header_out(ID_CAPABILITIES, it, &msg->capabilities.value,
474
33
                                  sizeof(msg->capabilities.value), &size);
475
33
    }
476
477
44
    if (it == parsed) {
478
0
        LOGGER_WARNING(m->log, "Parsing message failed; empty message");
479
0
        return -1;
480
0
    }
481
482
44
    *it = 0;
483
44
    ++size;
484
485
44
    if (m_msi_packet(m, friend_number, parsed, size)) {
486
44
        LOGGER_DEBUG(m->log, "Sent message");
487
44
        return 0;
488
44
    }
489
490
0
    return -1;
491
44
}
492
static int send_error(const Messenger *m, uint32_t friend_number, MSIError error)
493
0
{
494
    /* Send error message */
495
0
    assert(m != nullptr);
496
497
0
    LOGGER_DEBUG(m->log, "Sending error: %d to friend: %d", error, friend_number);
498
499
0
    MSIMessage msg;
500
0
    msg_init(&msg, REQU_POP);
501
502
0
    msg.error.exists = true;
503
0
    msg.error.value = error;
504
505
0
    send_message(m, friend_number, &msg);
506
0
    return 0;
507
0
}
508
static int invoke_callback_inner(MSICall *call, MSICallbackID id)
509
44
{
510
44
    MSISession *session = call->session;
511
44
    LOGGER_DEBUG(session->messenger->log, "invoking callback function: %d", id);
512
513
44
    switch (id) {
514
11
        case MSI_ON_INVITE:
515
11
            return session->invite_callback(session->av, call);
516
517
9
        case MSI_ON_START:
518
9
            return session->start_callback(session->av, call);
519
520
11
        case MSI_ON_END:
521
11
            return session->end_callback(session->av, call);
522
523
0
        case MSI_ON_ERROR:
524
0
            return session->error_callback(session->av, call);
525
526
0
        case MSI_ON_PEERTIMEOUT:
527
0
            return session->peertimeout_callback(session->av, call);
528
529
13
        case MSI_ON_CAPABILITIES:
530
13
            return session->capabilities_callback(session->av, call);
531
44
    }
532
533
0
    LOGGER_FATAL(session->messenger->log, "invalid callback id: %d", id);
534
0
    return -1;
535
44
}
536
static bool invoke_callback(MSICall *call, MSICallbackID cb)
537
44
{
538
44
    assert(call != nullptr);
539
540
44
    if (invoke_callback_inner(call, cb) != 0) {
541
0
        LOGGER_WARNING(call->session->messenger->log,
542
0
                       "Callback state handling failed, sending error");
543
544
        /* If no callback present or error happened while handling,
545
         * an error message will be sent to friend
546
         */
547
0
        if (call->error == MSI_E_NONE) {
548
0
            call->error = MSI_E_HANDLE;
549
0
        }
550
551
0
        return false;
552
0
    }
553
554
44
    return true;
555
44
}
556
static MSICall *get_call(MSISession *session, uint32_t friend_number)
557
55
{
558
55
    assert(session != nullptr);
559
560
55
    if (session->calls == nullptr || session->calls_tail < friend_number) {
561
22
        return nullptr;
562
22
    }
563
564
33
    return session->calls[friend_number];
565
55
}
566
static MSICall *new_call(MSISession *session, uint32_t friend_number)
567
22
{
568
22
    assert(session != nullptr);
569
570
22
    MSICall *rc = (MSICall *)calloc(1, sizeof(MSICall));
571
572
22
    if (rc == nullptr) {
573
0
        return nullptr;
574
0
    }
575
576
22
    rc->session = session;
577
22
    rc->friend_number = friend_number;
578
579
22
    if (session->calls == nullptr) { /* Creating */
580
20
        session->calls = (MSICall **)calloc(friend_number + 1, sizeof(MSICall *));
581
582
20
        if (session->calls == nullptr) {
583
0
            free(rc);
584
0
            return nullptr;
585
0
        }
586
587
20
        session->calls_tail = friend_number;
588
20
        session->calls_head = friend_number;
589
20
    } else if (session->calls_tail < friend_number) { /* Appending */
590
2
        MSICall **tmp = (MSICall **)realloc(session->calls,  (friend_number + 1) * sizeof(MSICall *));
591
592
2
        if (tmp == nullptr) {
593
0
            free(rc);
594
0
            return nullptr;
595
0
        }
596
597
2
        session->calls = tmp;
598
599
        /* Set fields in between to null */
600
2
        for (uint32_t i = session->calls_tail + 1; i < friend_number; ++i) {
601
0
            session->calls[i] = nullptr;
602
0
        }
603
604
2
        rc->prev = session->calls[session->calls_tail];
605
2
        session->calls[session->calls_tail]->next = rc;
606
607
2
        session->calls_tail = friend_number;
608
2
    } else if (session->calls_head > friend_number) { /* Inserting at front */
609
0
        rc->next = session->calls[session->calls_head];
610
0
        session->calls[session->calls_head]->prev = rc;
611
0
        session->calls_head = friend_number;
612
0
    }
613
614
22
    session->calls[friend_number] = rc;
615
22
    return rc;
616
22
}
617
static void kill_call(MSICall *call)
618
22
{
619
    /* Assume that session mutex is locked */
620
22
    if (call == nullptr) {
621
0
        return;
622
0
    }
623
624
22
    MSISession *session = call->session;
625
626
22
    LOGGER_DEBUG(session->messenger->log, "Killing call: %p", (void *)call);
627
628
22
    MSICall *prev = call->prev;
629
22
    MSICall *next = call->next;
630
631
22
    if (prev != nullptr) {
632
0
        prev->next = next;
633
22
    } else if (next != nullptr) {
634
2
        session->calls_head = next->friend_number;
635
20
    } else {
636
20
        goto CLEAR_CONTAINER;
637
20
    }
638
639
2
    if (next != nullptr) {
640
2
        next->prev = prev;
641
2
    } else if (prev != nullptr) {
642
0
        session->calls_tail = prev->friend_number;
643
0
    } else {
644
0
        goto CLEAR_CONTAINER;
645
0
    }
646
647
2
    session->calls[call->friend_number] = nullptr;
648
2
    free(call);
649
2
    return;
650
651
20
CLEAR_CONTAINER:
652
20
    session->calls_head = 0;
653
20
    session->calls_tail = 0;
654
20
    free(session->calls);
655
20
    free(call);
656
20
    session->calls = nullptr;
657
20
}
658
static void on_peer_status(Messenger *m, uint32_t friend_number, uint8_t connection_status, void *user_data)
659
0
{
660
0
    MSISession *session = (MSISession *)user_data;
661
662
0
    if (connection_status != 0) {
663
        // Friend is online.
664
0
        return;
665
0
    }
666
667
0
    LOGGER_DEBUG(m->log, "Friend %d is now offline", friend_number);
668
669
0
    pthread_mutex_lock(session->mutex);
670
0
    MSICall *call = get_call(session, friend_number);
671
672
0
    if (call == nullptr) {
673
0
        pthread_mutex_unlock(session->mutex);
674
0
        return;
675
0
    }
676
677
0
    invoke_callback(call, MSI_ON_PEERTIMEOUT); /* Failure is ignored */
678
0
    kill_call(call);
679
0
    pthread_mutex_unlock(session->mutex);
680
0
}
681
static bool try_handle_init(MSICall *call, const MSIMessage *msg)
682
11
{
683
11
    assert(call != nullptr);
684
11
    LOGGER_DEBUG(call->session->messenger->log,
685
11
                 "Session: %p Handling 'init' friend: %d", (void *)call->session, call->friend_number);
686
687
11
    if (!msg->capabilities.exists) {
688
0
        LOGGER_WARNING(call->session->messenger->log, "Session: %p Invalid capabilities on 'init'", (void *)call->session);
689
0
        call->error = MSI_E_INVALID_MESSAGE;
690
0
        return false;
691
0
    }
692
693
11
    switch (call->state) {
694
11
        case MSI_CALL_INACTIVE: {
695
            /* Call requested */
696
11
            call->peer_capabilities = msg->capabilities.value;
697
11
            call->state = MSI_CALL_REQUESTED;
698
699
11
            if (!invoke_callback(call, MSI_ON_INVITE)) {
700
0
                return false;
701
0
            }
702
703
11
            break;
704
11
        }
705
706
11
        case MSI_CALL_ACTIVE: {
707
            /* If peer sent init while the call is already
708
             * active it's probable that he is trying to
709
             * re-call us while the call is not terminated
710
             * on our side. We can assume that in this case
711
             * we can automatically answer the re-call.
712
             */
713
714
0
            LOGGER_INFO(call->session->messenger->log, "Friend is recalling us");
715
716
0
            MSIMessage out_msg;
717
0
            msg_init(&out_msg, REQU_PUSH);
718
719
0
            out_msg.capabilities.exists = true;
720
0
            out_msg.capabilities.value = call->self_capabilities;
721
722
0
            send_message(call->session->messenger, call->friend_number, &out_msg);
723
724
            /* If peer changed capabilities during re-call they will
725
             * be handled accordingly during the next step
726
             */
727
0
            break;
728
11
        }
729
730
0
        case MSI_CALL_REQUESTED: // fall-through
731
0
        case MSI_CALL_REQUESTING: {
732
0
            LOGGER_WARNING(call->session->messenger->log, "Session: %p Invalid state on 'init'", (void *)call->session);
733
0
            call->error = MSI_E_INVALID_STATE;
734
0
            return false;
735
0
        }
736
11
    }
737
738
11
    return true;
739
11
}
740
static void handle_init(MSICall *call, const MSIMessage *msg)
741
11
{
742
11
    assert(call != nullptr);
743
11
    LOGGER_DEBUG(call->session->messenger->log,
744
11
                 "Session: %p Handling 'init' friend: %d", (void *)call->session, call->friend_number);
745
746
11
    if (!try_handle_init(call, msg)) {
747
0
        send_error(call->session->messenger, call->friend_number, call->error);
748
0
        kill_call(call);
749
0
    }
750
11
}
751
static void handle_push(MSICall *call, const MSIMessage *msg)
752
22
{
753
22
    assert(call != nullptr);
754
755
22
    LOGGER_DEBUG(call->session->messenger->log, "Session: %p Handling 'push' friend: %d", (void *)call->session,
756
22
                 call->friend_number);
757
758
22
    if (!msg->capabilities.exists) {
759
0
        LOGGER_WARNING(call->session->messenger->log, "Session: %p Invalid capabilities on 'push'", (void *)call->session);
760
0
        call->error = MSI_E_INVALID_MESSAGE;
761
0
        goto FAILURE;
762
0
    }
763
764
22
    switch (call->state) {
765
13
        case MSI_CALL_ACTIVE: {
766
13
            if (call->peer_capabilities != msg->capabilities.value) {
767
                /* Only act if capabilities changed */
768
13
                LOGGER_INFO(call->session->messenger->log, "Friend is changing capabilities to: %u", msg->capabilities.value);
769
770
13
                call->peer_capabilities = msg->capabilities.value;
771
772
13
                if (!invoke_callback(call, MSI_ON_CAPABILITIES)) {
773
0
                    goto FAILURE;
774
0
                }
775
13
            }
776
777
13
            break;
778
13
        }
779
780
13
        case MSI_CALL_REQUESTING: {
781
9
            LOGGER_INFO(call->session->messenger->log, "Friend answered our call");
782
783
            /* Call started */
784
9
            call->peer_capabilities = msg->capabilities.value;
785
9
            call->state = MSI_CALL_ACTIVE;
786
787
9
            if (!invoke_callback(call, MSI_ON_START)) {
788
0
                goto FAILURE;
789
0
            }
790
791
9
            break;
792
9
        }
793
794
9
        case MSI_CALL_INACTIVE: // fall-through
795
0
        case MSI_CALL_REQUESTED: {
796
            /* Pushes during initialization state are ignored */
797
0
            LOGGER_WARNING(call->session->messenger->log, "Ignoring invalid push");
798
0
            break;
799
0
        }
800
22
    }
801
802
22
    return;
803
804
22
FAILURE:
805
0
    send_error(call->session->messenger, call->friend_number, call->error);
806
0
    kill_call(call);
807
0
}
808
static void handle_pop(MSICall *call, const MSIMessage *msg)
809
11
{
810
11
    assert(call != nullptr);
811
812
11
    LOGGER_DEBUG(call->session->messenger->log, "Session: %p Handling 'pop', friend id: %d", (void *)call->session,
813
11
                 call->friend_number);
814
815
    /* callback errors are ignored */
816
817
11
    if (msg->error.exists) {
818
0
        LOGGER_WARNING(call->session->messenger->log, "Friend detected an error: %d", msg->error.value);
819
0
        call->error = msg->error.value;
820
0
        invoke_callback(call, MSI_ON_ERROR);
821
11
    } else {
822
11
        switch (call->state) {
823
0
            case MSI_CALL_INACTIVE: {
824
0
                LOGGER_FATAL(call->session->messenger->log, "Handling what should be impossible case");
825
0
                break;
826
0
            }
827
828
9
            case MSI_CALL_ACTIVE: {
829
                /* Hangup */
830
9
                LOGGER_INFO(call->session->messenger->log, "Friend hung up on us");
831
9
                invoke_callback(call, MSI_ON_END);
832
9
                break;
833
0
            }
834
835
1
            case MSI_CALL_REQUESTING: {
836
                /* Reject */
837
1
                LOGGER_INFO(call->session->messenger->log, "Friend rejected our call");
838
1
                invoke_callback(call, MSI_ON_END);
839
1
                break;
840
0
            }
841
842
1
            case MSI_CALL_REQUESTED: {
843
                /* Cancel */
844
1
                LOGGER_INFO(call->session->messenger->log, "Friend canceled call invite");
845
1
                invoke_callback(call, MSI_ON_END);
846
1
                break;
847
0
            }
848
11
        }
849
11
    }
850
851
11
    kill_call(call);
852
11
}
853
static void handle_msi_packet(Messenger *m, uint32_t friend_number, const uint8_t *data, uint16_t length, void *user_data)
854
44
{
855
44
    MSISession *session = (MSISession *)user_data;
856
857
44
    LOGGER_DEBUG(m->log, "Got msi message");
858
859
44
    MSIMessage msg;
860
861
44
    if (msg_parse_in(m->log, &msg, data, length) == -1) {
862
0
        LOGGER_WARNING(m->log, "Error parsing message");
863
0
        send_error(m, friend_number, MSI_E_INVALID_MESSAGE);
864
0
        return;
865
0
    }
866
867
44
    LOGGER_DEBUG(m->log, "Successfully parsed message");
868
869
44
    pthread_mutex_lock(session->mutex);
870
44
    MSICall *call = get_call(session, friend_number);
871
872
44
    if (call == nullptr) {
873
11
        if (msg.request.value != REQU_INIT) {
874
0
            send_error(m, friend_number, MSI_E_STRAY_MESSAGE);
875
0
            pthread_mutex_unlock(session->mutex);
876
0
            return;
877
0
        }
878
879
11
        call = new_call(session, friend_number);
880
881
11
        if (call == nullptr) {
882
0
            send_error(m, friend_number, MSI_E_SYSTEM);
883
0
            pthread_mutex_unlock(session->mutex);
884
0
            return;
885
0
        }
886
11
    }
887
888
44
    switch (msg.request.value) {
889
11
        case REQU_INIT: {
890
11
            handle_init(call, &msg);
891
11
            break;
892
0
        }
893
894
22
        case REQU_PUSH: {
895
22
            handle_push(call, &msg);
896
22
            break;
897
0
        }
898
899
11
        case REQU_POP: {
900
11
            handle_pop(call, &msg); /* always kills the call */
901
11
            break;
902
0
        }
903
44
    }
904
905
44
    pthread_mutex_unlock(session->mutex);
906
44
}