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