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