summaryrefslogtreecommitdiff
path: root/toxav/msi.c
diff options
context:
space:
mode:
Diffstat (limited to 'toxav/msi.c')
-rw-r--r--toxav/msi.c1424
1 files changed, 1424 insertions, 0 deletions
diff --git a/toxav/msi.c b/toxav/msi.c
new file mode 100644
index 00000000..8f69d942
--- /dev/null
+++ b/toxav/msi.c
@@ -0,0 +1,1424 @@
1/** toxmsi.c
2 *
3 * Copyright (C) 2013 Tox project All Rights Reserved.
4 *
5 * This file is part of Tox.
6 *
7 * Tox is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * Tox is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with Tox. If not, see <http://www.gnu.org/licenses/>.
19 *
20 *
21 * Report bugs/suggestions at #tox-dev @ freenode.net:6667
22 */
23
24
25#ifdef HAVE_CONFIG_H
26#include "config.h"
27#endif /* HAVE_CONFIG_H */
28
29#define _BSD_SOURCE
30
31#include "msi.h"
32#include "event.h"
33#include "../toxcore/util.h"
34#include "../toxcore/network.h"
35#include "../toxcore/Messenger.h"
36
37#include <assert.h>
38#include <unistd.h>
39#include <string.h>
40#include <stdlib.h>
41
42#define same(x, y) strcmp((const char*) x, (const char*) y) == 0
43
44#define MSI_MAXMSG_SIZE 1024
45
46#define TYPE_REQUEST 1
47#define TYPE_RESPONSE 2
48
49unsigned char *VERSION_STRING = (unsigned char *)"0.3.1";
50#define VERSION_STRLEN 5
51
52#define CT_AUDIO_HEADER_VALUE "AUDIO"
53#define CT_VIDEO_HEADER_VALUE "VIDEO"
54
55
56/* Define default timeout for a request.
57 * There is no behavior specified by the msi on what will
58 * client do on timeout, but to call timeout callback.
59 */
60#define m_deftout 10000 /* in milliseconds */
61
62/**
63 * Protocol:
64 *
65 * | desc. ( 1 byte ) | length ( 2 bytes ) | value ( length bytes ) |
66 *
67 * ie.
68 *
69 * | 0x1 | 0x0 0x7 | "version"
70 *
71 * Means: it's field value with length of 7 bytes and value of "version"
72 * It's similar to amp protocol
73 */
74
75
76#define GENERIC_HEADER(header) \
77typedef struct _MSIHeader##header { \
78uint8_t* header_value; \
79uint16_t size; \
80} MSIHeader##header;
81
82
83GENERIC_HEADER ( Version )
84GENERIC_HEADER ( Request )
85GENERIC_HEADER ( Response )
86GENERIC_HEADER ( CallType )
87GENERIC_HEADER ( UserAgent )
88GENERIC_HEADER ( CallId )
89GENERIC_HEADER ( Info )
90GENERIC_HEADER ( Reason )
91GENERIC_HEADER ( CryptoKey )
92GENERIC_HEADER ( Nonce )
93
94
95/**
96 * @brief This is the message structure. It contains all of the headers and
97 * destination/source of the message stored in friend_id.
98 *
99 */
100typedef struct _MSIMessage {
101
102 MSIHeaderVersion version;
103 MSIHeaderRequest request;
104 MSIHeaderResponse response;
105 MSIHeaderCallType calltype;
106 MSIHeaderUserAgent useragent;
107 MSIHeaderInfo info;
108 MSIHeaderReason reason;
109 MSIHeaderCallId callid;
110 MSIHeaderCryptoKey cryptokey;
111 MSIHeaderNonce nonce;
112
113 struct _MSIMessage *next;
114
115 int friend_id;
116
117} MSIMessage;
118
119
120
121static MSICallback callbacks[10] = {0};
122
123
124/* define strings for the identifiers */
125#define VERSION_FIELD "Version"
126#define REQUEST_FIELD "Request"
127#define RESPONSE_FIELD "Response"
128#define INFO_FIELD "INFO"
129#define REASON_FIELD "Reason"
130#define CALLTYPE_FIELD "Call-type"
131#define USERAGENT_FIELD "User-agent"
132#define CALLID_FIELD "Call-id"
133#define CRYPTOKEY_FIELD "Crypto-key"
134#define NONCE_FIELD "Nonce"
135
136/* protocol descriptors */
137#define end_byte 0x0
138#define field_byte 0x1
139#define value_byte 0x2
140
141
142typedef enum {
143 invite,
144 start,
145 cancel,
146 reject,
147 end,
148
149} MSIRequest;
150
151
152/**
153 * @brief Get string value for request.
154 *
155 * @param request The request.
156 * @return const uint8_t* The string
157 */
158static inline const uint8_t *stringify_request ( MSIRequest request )
159{
160 static const uint8_t *strings[] = {
161 ( uint8_t * ) "INVITE",
162 ( uint8_t * ) "START",
163 ( uint8_t * ) "CANCEL",
164 ( uint8_t * ) "REJECT",
165 ( uint8_t * ) "END"
166 };
167
168 return strings[request];
169}
170
171
172typedef enum {
173 ringing,
174 starting,
175 ending,
176 error
177
178} MSIResponse;
179
180
181/**
182 * @brief Get string value for response.
183 *
184 * @param response The response.
185 * @return const uint8_t* The string
186 */
187static inline const uint8_t *stringify_response ( MSIResponse response )
188{
189 static const uint8_t *strings[] = {
190 ( uint8_t * ) "ringing",
191 ( uint8_t * ) "starting",
192 ( uint8_t * ) "ending",
193 ( uint8_t * ) "error"
194 };
195
196 return strings[response];
197}
198
199
200#define ON_HEADER(iterator, header, descriptor, size_const) \
201( memcmp(iterator, descriptor, size_const) == 0){ /* Okay */ \
202 iterator += size_const; /* Set iterator at begining of value part */ \
203 if ( *iterator != value_byte ) { assert(0); return -1; }\
204 iterator ++;\
205 uint16_t _value_size = (uint16_t) *(iterator ) << 8 | \
206 (uint16_t) *(iterator + 1); \
207 header.header_value = calloc(sizeof(uint8_t), _value_size); \
208 header.size = _value_size; \
209 memcpy(header.header_value, iterator + 2, _value_size);\
210 iterator = iterator + 2 + _value_size; /* set iterator at new header or end_byte */ \
211}
212
213/**
214 * @brief Parse raw 'data' received from socket into MSIMessage struct.
215 * Every message has to have end value of 'end_byte' or _undefined_ behavior
216 * occures. The best practice is to check the end of the message at the handle_packet.
217 *
218 * @param msg Container.
219 * @param data The data.
220 * @return int
221 * @retval -1 Error occured.
222 * @retval 0 Success.
223 */
224int parse_raw_data ( MSIMessage *msg, const uint8_t *data, uint16_t length )
225{
226 assert ( msg );
227
228 if ( data[length - 1] ) /* End byte must have value 0 */
229 return -1;
230
231 const uint8_t *_it = data;
232
233 while ( *_it ) {/* until end_byte is hit */
234
235 uint16_t itedlen = (_it - data) + 2;
236
237 if ( *_it == field_byte && itedlen < length ) {
238
239 uint16_t _size = ( uint16_t ) * ( _it + 1 ) << 8 |
240 ( uint16_t ) * ( _it + 2 );
241
242 if ( itedlen + _size > length ) return -1;
243
244 _it += 3; /* place it at the field value beginning */
245
246 switch ( _size ) { /* Compare the size of the hardcoded values ( vary fast and convenient ) */
247
248 case 4: { /* INFO header */
249 if ON_HEADER ( _it, msg->info, INFO_FIELD, 4 )
250 }
251 break;
252
253 case 5: { /* NONCE header */
254 if ON_HEADER ( _it, msg->nonce, NONCE_FIELD, 5 )
255 }
256 break;
257
258 case 6: { /* Reason header */
259 if ON_HEADER ( _it, msg->reason, REASON_FIELD, 6 )
260 }
261 break;
262
263 case 7: { /* Version, Request, Call-id headers */
264 if ON_HEADER ( _it, msg->version, VERSION_FIELD, 7 )
265 else if ON_HEADER ( _it, msg->request, REQUEST_FIELD, 7 )
266 else if ON_HEADER ( _it, msg->callid, CALLID_FIELD, 7 )
267 }
268 break;
269
270 case 8: { /* Response header */
271 if ON_HEADER ( _it, msg->response, RESPONSE_FIELD, 8 )
272 }
273 break;
274
275 case 9: { /* Call-type header */
276 if ON_HEADER ( _it, msg->calltype, CALLTYPE_FIELD, 9 )
277 }
278 break;
279
280 case 10: { /* User-agent, Crypto-key headers */
281 if ON_HEADER ( _it, msg->useragent, USERAGENT_FIELD, 10 )
282 else if ON_HEADER ( _it, msg->cryptokey, CRYPTOKEY_FIELD, 10 )
283 }
284 break;
285
286 default:
287 return -1;
288 }
289 } else return -1;
290
291 /* If it's anything else return failure as the message is invalid */
292
293 }
294
295 return 0;
296}
297
298
299#define ALLOCATE_HEADER( var, mheader_value, t_size) \
300var.header_value = calloc(sizeof *mheader_value, t_size); \
301memcpy(var.header_value, mheader_value, t_size); \
302var.size = t_size;
303
304
305/**
306 * @brief Speaks for it self.
307 *
308 * @param msg The message.
309 * @return void
310 */
311void free_message ( MSIMessage *msg )
312{
313 assert ( msg );
314
315 free ( msg->calltype.header_value );
316 free ( msg->request.header_value );
317 free ( msg->response.header_value );
318 free ( msg->useragent.header_value );
319 free ( msg->version.header_value );
320 free ( msg->info.header_value );
321 free ( msg->cryptokey.header_value );
322 free ( msg->nonce.header_value );
323 free ( msg->reason.header_value );
324 free ( msg->callid.header_value );
325
326 free ( msg );
327}
328
329
330/**
331 * @brief Create the message.
332 *
333 * @param type Request or response.
334 * @param type_id Type of request/response.
335 * @return MSIMessage* Created message.
336 * @retval NULL Error occured.
337 */
338MSIMessage *msi_new_message ( uint8_t type, const uint8_t *type_id )
339{
340 MSIMessage *_retu = calloc ( sizeof ( MSIMessage ), 1 );
341 assert ( _retu );
342
343 if ( type == TYPE_REQUEST ) {
344 ALLOCATE_HEADER ( _retu->request, type_id, strlen ( (const char *)type_id ) )
345
346 } else if ( type == TYPE_RESPONSE ) {
347 ALLOCATE_HEADER ( _retu->response, type_id, strlen ( (const char *)type_id ) )
348
349 } else {
350 free_message ( _retu );
351 return NULL;
352 }
353
354 ALLOCATE_HEADER ( _retu->version, VERSION_STRING, strlen ( (const char *)VERSION_STRING ) )
355
356 return _retu;
357}
358
359
360/**
361 * @brief Parse data from handle_packet.
362 *
363 * @param data The data.
364 * @return MSIMessage* Parsed message.
365 * @retval NULL Error occured.
366 */
367MSIMessage *parse_message ( const uint8_t *data, uint16_t length )
368{
369 assert ( data );
370
371 MSIMessage *_retu = calloc ( sizeof ( MSIMessage ), 1 );
372 assert ( _retu );
373
374 memset ( _retu, 0, sizeof ( MSIMessage ) );
375
376 if ( parse_raw_data ( _retu, data, length ) == -1 ) {
377
378 free_message ( _retu );
379 return NULL;
380 }
381
382 if ( !_retu->version.header_value || VERSION_STRLEN != _retu->version.size ||
383 memcmp ( _retu->version.header_value, VERSION_STRING, VERSION_STRLEN ) != 0 ) {
384
385 free_message ( _retu );
386 return NULL;
387 }
388
389 return _retu;
390}
391
392
393
394/**
395 * @brief Speaks for it self.
396 *
397 * @param dest Container.
398 * @param header_field Field.
399 * @param header_value Field value.
400 * @param value_len Length of field value.
401 * @param length Pointer to container length.
402 * @return uint8_t* Iterated container.
403 */
404uint8_t *append_header_to_string (
405 uint8_t *dest,
406 const uint8_t *header_field,
407 const uint8_t *header_value,
408 uint16_t value_len,
409 uint16_t *length )
410{
411 assert ( dest );
412 assert ( header_value );
413 assert ( header_field );
414
415 const uint8_t *_hvit = header_value;
416 uint16_t _total = 6 + value_len; /* 6 is known plus header value len + field len*/
417
418 *dest = field_byte; /* Set the first byte */
419
420 uint8_t *_getback_byte = dest + 1; /* remeber the byte we were on */
421 dest += 3; /* swith to 4th byte where field value starts */
422
423 /* Now set the field value and calculate it's length */
424 uint16_t _i = 0;
425
426 for ( ; header_field[_i]; ++_i ) {
427 *dest = header_field[_i];
428 ++dest;
429 };
430
431 _total += _i;
432
433 /* Now set the length of the field byte */
434 *_getback_byte = ( uint8_t ) _i >> 8;
435
436 _getback_byte++;
437
438 *_getback_byte = ( uint8_t ) _i;
439
440 /* for value part do it regulary */
441 *dest = value_byte;
442
443 dest++;
444
445 *dest = ( uint8_t ) value_len >> 8;
446
447 dest++;
448
449 *dest = ( uint8_t ) value_len;
450
451 dest++;
452
453 for ( _i = value_len; _i; --_i ) {
454 *dest = *_hvit;
455 ++_hvit;
456 ++dest;
457 }
458
459 *length += _total;
460 return dest;
461}
462
463
464#define CLEAN_ASSIGN(added, var, field, header)\
465if ( header.header_value ) { var = append_header_to_string(var, (const uint8_t*)field, header.header_value, header.size, &added); }
466
467
468/**
469 * @brief Convert MSIMessage struct to _sendable_ string.
470 *
471 * @param msg The message.
472 * @param dest Destination.
473 * @return uint16_t It's final size.
474 */
475uint16_t message_to_string ( MSIMessage *msg, uint8_t *dest )
476{
477 assert ( msg );
478 assert ( dest );
479
480 uint8_t *_iterated = dest;
481 uint16_t _size = 0;
482
483 CLEAN_ASSIGN ( _size, _iterated, VERSION_FIELD, msg->version );
484 CLEAN_ASSIGN ( _size, _iterated, REQUEST_FIELD, msg->request );
485 CLEAN_ASSIGN ( _size, _iterated, RESPONSE_FIELD, msg->response );
486 CLEAN_ASSIGN ( _size, _iterated, CALLTYPE_FIELD, msg->calltype );
487 CLEAN_ASSIGN ( _size, _iterated, USERAGENT_FIELD, msg->useragent );
488 CLEAN_ASSIGN ( _size, _iterated, INFO_FIELD, msg->info );
489 CLEAN_ASSIGN ( _size, _iterated, CALLID_FIELD, msg->callid );
490 CLEAN_ASSIGN ( _size, _iterated, REASON_FIELD, msg->reason );
491 CLEAN_ASSIGN ( _size, _iterated, CRYPTOKEY_FIELD, msg->cryptokey );
492 CLEAN_ASSIGN ( _size, _iterated, NONCE_FIELD, msg->nonce );
493
494 *_iterated = end_byte;
495 _size ++;
496
497 return _size;
498}
499
500
501#define GENERIC_SETTER_DEFINITION(header) \
502void msi_msg_set_##header ( MSIMessage* _msg, const uint8_t* header_value, uint16_t _size ) \
503{ assert(_msg); assert(header_value); \
504 free(_msg->header.header_value); \
505 ALLOCATE_HEADER( _msg->header, header_value, _size )}
506
507GENERIC_SETTER_DEFINITION ( calltype )
508GENERIC_SETTER_DEFINITION ( useragent )
509GENERIC_SETTER_DEFINITION ( reason )
510GENERIC_SETTER_DEFINITION ( info )
511GENERIC_SETTER_DEFINITION ( callid )
512GENERIC_SETTER_DEFINITION ( cryptokey )
513GENERIC_SETTER_DEFINITION ( nonce )
514
515
516/**
517 * @brief Generate _random_ alphanumerical string.
518 *
519 * @param str Destination.
520 * @param size Size of string.
521 * @return void
522 */
523void t_randomstr ( uint8_t *str, size_t size )
524{
525 assert ( str );
526
527 static const uint8_t _bytes[] =
528 "0123456789"
529 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
530 "abcdefghijklmnopqrstuvwxyz";
531
532 size_t _it = 0;
533
534 for ( ; _it < size; _it++ ) {
535 str[_it] = _bytes[ random_int() % 61 ];
536 }
537}
538
539
540typedef enum {
541 error_deadcall = 1, /* has call id but it's from old call */
542 error_id_mismatch, /* non-existing call */
543
544 error_no_callid, /* not having call id */
545 error_no_call, /* no call in session */
546 error_no_crypto_key, /* no crypto key */
547
548 error_busy
549
550} MSICallError; /* Error codes */
551
552
553/**
554 * @brief Stringify error code.
555 *
556 * @param error_code The code.
557 * @return const uint8_t* The string.
558 */
559static inline const uint8_t *stringify_error ( MSICallError error_code )
560{
561 static const uint8_t *strings[] = {
562 ( uint8_t * ) "",
563 ( uint8_t * ) "Using dead call",
564 ( uint8_t * ) "Call id not set to any call",
565 ( uint8_t * ) "Call id not available",
566 ( uint8_t * ) "No active call in session",
567 ( uint8_t * ) "No Crypto-key set",
568 ( uint8_t * ) "Callee busy"
569 };
570
571 return strings[error_code];
572}
573
574
575/**
576 * @brief Convert error_code into string.
577 *
578 * @param error_code The code.
579 * @return const uint8_t* The string.
580 */
581static inline const uint8_t *stringify_error_code ( MSICallError error_code )
582{
583 static const uint8_t *strings[] = {
584 ( uint8_t * ) "",
585 ( uint8_t * ) "1",
586 ( uint8_t * ) "2",
587 ( uint8_t * ) "3",
588 ( uint8_t * ) "4",
589 ( uint8_t * ) "5",
590 ( uint8_t * ) "6"
591 };
592
593 return strings[error_code];
594}
595
596
597/**
598 * @brief Speaks for it self.
599 *
600 * @param session Control session.
601 * @param msg The message.
602 * @param to Where to.
603 * @return int
604 * @retval -1 Error occured.
605 * @retval 0 Success.
606 */
607int send_message ( MSISession *session, MSIMessage *msg, uint32_t to )
608{
609 msi_msg_set_callid ( msg, session->call->id, CALL_ID_LEN );
610
611 uint8_t _msg_string_final [MSI_MAXMSG_SIZE];
612 uint16_t _length = message_to_string ( msg, _msg_string_final );
613
614 return m_msi_packet(session->messenger_handle, to, _msg_string_final, _length) ? 0 : -1;
615}
616
617
618/**
619 * @brief Speaks for it self.
620 *
621 * @param session Control session.
622 * @param msg The message.
623 * @param peer_id The peer.
624 * @return void
625 */
626void flush_peer_type ( MSISession *session, MSIMessage *msg, int peer_id )
627{
628 if ( msg->calltype.header_value ) {
629 if ( strcmp ( ( const char * ) msg->calltype.header_value, CT_AUDIO_HEADER_VALUE ) == 0 ) {
630 session->call->type_peer[peer_id] = type_audio;
631
632 } else if ( strcmp ( ( const char * ) msg->calltype.header_value, CT_VIDEO_HEADER_VALUE ) == 0 ) {
633 session->call->type_peer[peer_id] = type_video;
634 } else {} /* Error */
635 } else {} /* Error */
636}
637
638void handle_remote_connection_change(Messenger *messenger, int friend_num, uint8_t status, void *session_p)
639{
640 MSISession *session = session_p;
641
642 switch ( status ) {
643 case 0: { /* Went offline */
644 if ( session->call ) {
645 int i = 0;
646
647 for ( ; i < session->call->peer_count; i ++ )
648 if ( session->call->peers[i] == friend_num ) {
649 msi_stopcall(session); /* Stop the call for now */
650 return;
651 }
652 }
653 }
654 break;
655
656 default:
657 break;
658 }
659}
660
661/**
662 * @brief Sends error response to peer.
663 *
664 * @param session The session.
665 * @param errid The id.
666 * @param to Where to?
667 * @return int
668 * @retval 0 It's always success.
669 */
670int handle_error ( MSISession *session, MSICallError errid, uint32_t to )
671{
672 MSIMessage *_msg_error = msi_new_message ( TYPE_RESPONSE, stringify_response ( error ) );
673
674 const uint8_t *_error_code_str = stringify_error_code ( errid );
675
676 msi_msg_set_reason ( _msg_error, _error_code_str, strlen ( ( const char * ) _error_code_str ) );
677 send_message ( session, _msg_error, to );
678 free_message ( _msg_error );
679
680 session->last_error_id = errid;
681 session->last_error_str = stringify_error ( errid );
682
683 event.rise ( callbacks[MSI_OnError], session->agent_handler );
684
685 return 0;
686}
687
688
689/**
690 * @brief Determine the error if any.
691 *
692 * @param session Control session.
693 * @param msg The message.
694 * @return int
695 * @retval -1 No error.
696 * @retval 0 Error occured and response sent.
697 */
698int has_call_error ( MSISession *session, MSIMessage *msg )
699{
700 if ( !msg->callid.header_value ) {
701 return handle_error ( session, error_no_callid, msg->friend_id );
702
703 } else if ( !session->call ) {
704 return handle_error ( session, error_no_call, msg->friend_id );
705
706 } else if ( memcmp ( session->call->id, msg->callid.header_value, CALL_ID_LEN ) != 0 ) {
707 return handle_error ( session, error_id_mismatch, msg->friend_id );
708
709 }
710
711 return -1;
712}
713
714
715/**
716 * @brief Function called at request timeout.
717 *
718 * @param arg Control session
719 * @return void*
720 */
721void *handle_timeout ( void *arg )
722{
723 /* Send hangup either way */
724 MSISession *_session = arg;
725
726 if ( _session && _session->call ) {
727
728 uint32_t *_peers = _session->call->peers;
729 uint16_t _peer_count = _session->call->peer_count;
730
731
732 /* Cancel all? */
733 uint16_t _it = 0;
734
735 for ( ; _it < _peer_count; _it++ )
736 msi_cancel ( arg, _peers[_it], (const uint8_t *)"Timeout" );
737
738 }
739
740 ( *callbacks[MSI_OnRequestTimeout] ) ( _session->agent_handler );
741 ( *callbacks[MSI_OnEnding ] ) ( _session->agent_handler );
742
743 return NULL;
744}
745
746
747/**
748 * @brief Add peer to peer list.
749 *
750 * @param call What call.
751 * @param peer_id Its id.
752 * @return void
753 */
754void add_peer( MSICall *call, int peer_id )
755{
756 if ( !call->peers ) {
757 call->peers = calloc(sizeof(int), 1);
758 call->peer_count = 1;
759 } else {
760 call->peer_count ++;
761 call->peers = realloc( call->peers, sizeof(int) * call->peer_count);
762 }
763
764 call->peers[call->peer_count - 1] = peer_id;
765}
766
767
768/**
769 * @brief Speaks for it self.
770 *
771 * @param session Control session.
772 * @param peers Amount of peers. (Currently it only supports 1)
773 * @param ringing_timeout Ringing timeout.
774 * @return MSICall* The created call.
775 */
776MSICall *init_call ( MSISession *session, int peers, int ringing_timeout )
777{
778 assert ( session );
779 assert ( peers );
780
781 MSICall *_call = calloc ( sizeof ( MSICall ), 1 );
782 _call->type_peer = calloc ( sizeof ( MSICallType ), peers );
783
784 assert ( _call );
785 assert ( _call->type_peer );
786
787 /*_call->_participant_count = _peers;*/
788
789 _call->request_timer_id = 0;
790 _call->ringing_timer_id = 0;
791
792 _call->key_local = NULL;
793 _call->key_peer = NULL;
794 _call->nonce_local = NULL;
795 _call->nonce_peer = NULL;
796
797 _call->ringing_tout_ms = ringing_timeout;
798
799 pthread_mutex_init ( &_call->mutex, NULL );
800
801 return _call;
802}
803
804
805/**
806 * @brief Terminate the call.
807 *
808 * @param session Control session.
809 * @return int
810 * @retval -1 Error occured.
811 * @retval 0 Success.
812 */
813int terminate_call ( MSISession *session )
814{
815 assert ( session );
816
817 if ( !session->call )
818 return -1;
819
820
821 /* Check event loop and cancel timed events if there are any
822 * NOTE: This has to be done before possibly
823 * locking the mutex the second time
824 */
825 event.timer_release ( session->call->request_timer_id );
826 event.timer_release ( session->call->ringing_timer_id );
827
828 /* Get a handle */
829 pthread_mutex_lock ( &session->call->mutex );
830
831 MSICall *_call = session->call;
832 session->call = NULL;
833
834 free ( _call->type_peer );
835 free ( _call->key_local );
836 free ( _call->key_peer );
837 free ( _call->peers);
838
839 /* Release handle */
840 pthread_mutex_unlock ( &_call->mutex );
841
842 pthread_mutex_destroy ( &_call->mutex );
843
844 free ( _call );
845
846 return 0;
847}
848
849
850/********** Request handlers **********/
851int handle_recv_invite ( MSISession *session, MSIMessage *msg )
852{
853 assert ( session );
854
855 if ( session->call ) {
856 handle_error ( session, error_busy, msg->friend_id );
857 return 0;
858 }
859
860 if ( !msg->callid.header_value ) {
861 handle_error ( session, error_no_callid, msg->friend_id );
862 return 0;
863 }
864
865 session->call = init_call ( session, 1, 0 );
866 memcpy ( session->call->id, msg->callid.header_value, CALL_ID_LEN );
867 session->call->state = call_starting;
868
869 add_peer( session->call, msg->friend_id);
870
871 flush_peer_type ( session, msg, 0 );
872
873 MSIMessage *_msg_ringing = msi_new_message ( TYPE_RESPONSE, stringify_response ( ringing ) );
874 send_message ( session, _msg_ringing, msg->friend_id );
875 free_message ( _msg_ringing );
876
877 event.rise ( callbacks[MSI_OnInvite], session->agent_handler );
878
879 return 1;
880}
881int handle_recv_start ( MSISession *session, MSIMessage *msg )
882{
883 assert ( session );
884
885 if ( has_call_error ( session, msg ) == 0 )
886 return 0;
887
888 if ( !msg->cryptokey.header_value )
889 return handle_error ( session, error_no_crypto_key, msg->friend_id );
890
891 session->call->state = call_active;
892
893 session->call->key_peer = calloc ( sizeof ( uint8_t ), crypto_secretbox_KEYBYTES );
894 memcpy ( session->call->key_peer, msg->cryptokey.header_value, crypto_secretbox_KEYBYTES );
895
896 session->call->nonce_peer = calloc ( sizeof ( uint8_t ), crypto_box_NONCEBYTES );
897 memcpy ( session->call->nonce_peer, msg->nonce.header_value, crypto_box_NONCEBYTES );
898
899 flush_peer_type ( session, msg, 0 );
900
901 event.rise ( callbacks[MSI_OnStart], session->agent_handler );
902
903 return 1;
904}
905int handle_recv_reject ( MSISession *session, MSIMessage *msg )
906{
907 assert ( session );
908
909 if ( has_call_error ( session, msg ) == 0 )
910 return 0;
911
912
913 MSIMessage *_msg_end = msi_new_message ( TYPE_REQUEST, stringify_request ( end ) );
914 send_message ( session, _msg_end, msg->friend_id );
915 free_message ( _msg_end );
916
917 event.timer_release ( session->call->request_timer_id );
918 event.rise ( callbacks[MSI_OnReject], session->agent_handler );
919 session->call->request_timer_id = event.timer_alloc ( handle_timeout, session, m_deftout );
920
921 return 1;
922}
923int handle_recv_cancel ( MSISession *session, MSIMessage *msg )
924{
925 assert ( session );
926
927 if ( has_call_error ( session, msg ) == 0 )
928 return 0;
929
930
931 terminate_call ( session );
932
933 event.rise ( callbacks[MSI_OnCancel], session->agent_handler );
934
935 return 1;
936}
937int handle_recv_end ( MSISession *session, MSIMessage *msg )
938{
939 assert ( session );
940
941 if ( has_call_error ( session, msg ) == 0 )
942 return 0;
943
944
945 MSIMessage *_msg_ending = msi_new_message ( TYPE_RESPONSE, stringify_response ( ending ) );
946 send_message ( session, _msg_ending, msg->friend_id );
947 free_message ( _msg_ending );
948
949 terminate_call ( session );
950
951 event.rise ( callbacks[MSI_OnEnd], session->agent_handler );
952
953 return 1;
954}
955
956/********** Response handlers **********/
957int handle_recv_ringing ( MSISession *session, MSIMessage *msg )
958{
959 assert ( session );
960
961 if ( has_call_error ( session, msg ) == 0 )
962 return 0;
963
964 session->call->ringing_timer_id = event.timer_alloc ( handle_timeout, session, session->call->ringing_tout_ms );
965 event.rise ( callbacks[MSI_OnRinging], session->agent_handler );
966
967 return 1;
968}
969int handle_recv_starting ( MSISession *session, MSIMessage *msg )
970{
971 assert ( session );
972
973 if ( has_call_error ( session, msg ) == 0 )
974 return 0;
975
976 if ( !msg->cryptokey.header_value ) {
977 return handle_error ( session, error_no_crypto_key, msg->friend_id );
978 }
979
980 /* Generate local key/nonce to send */
981 session->call->key_local = calloc ( sizeof ( uint8_t ), crypto_secretbox_KEYBYTES );
982 new_symmetric_key ( session->call->key_local );
983
984 session->call->nonce_local = calloc ( sizeof ( uint8_t ), crypto_box_NONCEBYTES );
985 new_nonce ( session->call->nonce_local );
986
987 /* Save peer key/nonce */
988 session->call->key_peer = calloc ( sizeof ( uint8_t ), crypto_secretbox_KEYBYTES );
989 memcpy ( session->call->key_peer, msg->cryptokey.header_value, crypto_secretbox_KEYBYTES );
990
991 session->call->nonce_peer = calloc ( sizeof ( uint8_t ), crypto_box_NONCEBYTES );
992 memcpy ( session->call->nonce_peer, msg->nonce.header_value, crypto_box_NONCEBYTES );
993
994 session->call->state = call_active;
995
996 MSIMessage *_msg_start = msi_new_message ( TYPE_REQUEST, stringify_request ( start ) );
997 msi_msg_set_cryptokey ( _msg_start, session->call->key_local, crypto_secretbox_KEYBYTES );
998 msi_msg_set_nonce ( _msg_start, session->call->nonce_local, crypto_box_NONCEBYTES );
999 send_message ( session, _msg_start, msg->friend_id );
1000 free_message ( _msg_start );
1001
1002 flush_peer_type ( session, msg, 0 );
1003
1004 event.rise ( callbacks[MSI_OnStarting], session->agent_handler );
1005 event.timer_release ( session->call->ringing_timer_id );
1006
1007 return 1;
1008}
1009int handle_recv_ending ( MSISession *session, MSIMessage *msg )
1010{
1011 assert ( session );
1012
1013 if ( has_call_error ( session, msg ) == 0 )
1014 return 0;
1015
1016
1017 terminate_call ( session );
1018
1019 event.rise ( callbacks[MSI_OnEnding], session->agent_handler );
1020
1021 return 1;
1022}
1023int handle_recv_error ( MSISession *session, MSIMessage *msg )
1024{
1025 assert ( session );
1026 assert ( session->call );
1027
1028 /* Handle error accordingly */
1029 if ( msg->reason.header_value ) {
1030 session->last_error_id = atoi ( ( const char * ) msg->reason.header_value );
1031 session->last_error_str = stringify_error ( session->last_error_id );
1032 }
1033
1034 terminate_call ( session );
1035
1036 event.rise ( callbacks[MSI_OnEnding], session->agent_handler );
1037
1038 return 1;
1039}
1040
1041
1042/**
1043 * @brief BASIC call flow:
1044 *
1045 * ALICE BOB
1046 * | invite --> |
1047 * | |
1048 * | <-- ringing |
1049 * | |
1050 * | <-- starting |
1051 * | |
1052 * | start --> |
1053 * | |
1054 * | <-- MEDIA TRANS --> |
1055 * | |
1056 * | end --> |
1057 * | |
1058 * | <-- ending |
1059 *
1060 * Alice calls Bob by sending invite packet.
1061 * Bob recvs the packet and sends an ringing packet;
1062 * which notifies Alice that her invite is acknowledged.
1063 * Ringing screen shown on both sides.
1064 * Bob accepts the invite for a call by sending starting packet.
1065 * Alice recvs the starting packet and sends the started packet to
1066 * inform Bob that she recved the starting packet.
1067 * Now the media transmission is established ( i.e. RTP transmission ).
1068 * Alice hangs up and sends end packet.
1069 * Bob recves the end packet and sends ending packet
1070 * as the acknowledgement that the call is ending.
1071 *
1072 *
1073 */
1074void msi_handle_packet ( Messenger *messenger, int source, uint8_t *data, uint16_t length, void *object )
1075{
1076 /* Unused */
1077 (void)messenger;
1078
1079 MSISession *_session = object;
1080 MSIMessage *_msg;
1081
1082 if ( !length ) return;
1083
1084 _msg = parse_message ( data, length );
1085
1086 if ( !_msg ) return;
1087
1088 _msg->friend_id = source;
1089
1090
1091 /* Now handle message */
1092
1093 if ( _msg->request.header_value ) { /* Handle request */
1094
1095 const uint8_t *_request_value = _msg->request.header_value;
1096
1097 if ( same ( _request_value, stringify_request ( invite ) ) ) {
1098 handle_recv_invite ( _session, _msg );
1099
1100 } else if ( same ( _request_value, stringify_request ( start ) ) ) {
1101 handle_recv_start ( _session, _msg );
1102
1103 } else if ( same ( _request_value, stringify_request ( cancel ) ) ) {
1104 handle_recv_cancel ( _session, _msg );
1105
1106 } else if ( same ( _request_value, stringify_request ( reject ) ) ) {
1107 handle_recv_reject ( _session, _msg );
1108
1109 } else if ( same ( _request_value, stringify_request ( end ) ) ) {
1110 handle_recv_end ( _session, _msg );
1111 }
1112
1113 else {
1114 free_message ( _msg );
1115 return;
1116 }
1117
1118 } else if ( _msg->response.header_value ) { /* Handle response */
1119
1120 const uint8_t *_response_value = _msg->response.header_value;
1121
1122 if ( same ( _response_value, stringify_response ( ringing ) ) ) {
1123 handle_recv_ringing ( _session, _msg );
1124
1125 } else if ( same ( _response_value, stringify_response ( starting ) ) ) {
1126 handle_recv_starting ( _session, _msg );
1127
1128 } else if ( same ( _response_value, stringify_response ( ending ) ) ) {
1129 handle_recv_ending ( _session, _msg );
1130
1131 } else if ( same ( _response_value, stringify_response ( error ) ) ) {
1132 handle_recv_error ( _session, _msg );
1133 } else {
1134 free_message ( _msg );
1135 return;
1136 }
1137
1138 /* Got response so cancel timer */
1139 if ( _session->call )
1140 event.timer_release ( _session->call->request_timer_id );
1141
1142 }
1143
1144 free_message ( _msg );
1145}
1146
1147
1148/********************************************************************************************************************
1149 * *******************************************************************************************************************
1150 ********************************************************************************************************************
1151 ********************************************************************************************************************
1152 ********************************************************************************************************************
1153 *
1154 *
1155 *
1156 * PUBLIC API FUNCTIONS IMPLEMENTATIONS
1157 *
1158 *
1159 *
1160 ********************************************************************************************************************
1161 ********************************************************************************************************************
1162 ********************************************************************************************************************
1163 ********************************************************************************************************************
1164 ********************************************************************************************************************/
1165
1166
1167
1168
1169
1170
1171
1172
1173/**
1174 * @brief Callback setter.
1175 *
1176 * @param callback The callback.
1177 * @param id The id.
1178 * @return void
1179 */
1180void msi_register_callback ( MSICallback callback, MSICallbackID id )
1181{
1182 callbacks[id] = callback;
1183}
1184
1185
1186/**
1187 * @brief Start the control session.
1188 *
1189 * @param messenger Tox* object.
1190 * @param user_agent User agent, i.e. 'Venom'; 'QT-gui'
1191 * @return MSISession* The created session.
1192 * @retval NULL Error occured.
1193 */
1194MSISession *msi_init_session ( Messenger *messenger, const uint8_t *ua_name )
1195{
1196 assert ( messenger );
1197
1198 MSISession *_retu = calloc ( sizeof ( MSISession ), 1 );
1199 assert ( _retu );
1200
1201 _retu->ua_name = ua_name;
1202 _retu->messenger_handle = messenger;
1203 _retu->agent_handler = NULL;
1204
1205 _retu->call = NULL;
1206
1207 _retu->frequ = 10000; /* default value? */
1208 _retu->call_timeout = 30000; /* default value? */
1209
1210
1211 m_callback_msi_packet(messenger, msi_handle_packet, _retu );
1212
1213 /* This is called when remote terminates session */
1214 m_callback_connectionstatus_internal_av(messenger, handle_remote_connection_change, _retu);
1215
1216 return _retu;
1217}
1218
1219
1220/**
1221 * @brief Terminate control session.
1222 *
1223 * @param session The session
1224 * @return int
1225 */
1226int msi_terminate_session ( MSISession *session )
1227{
1228 assert ( session );
1229
1230 int _status = 0;
1231
1232 terminate_call ( session );
1233 m_callback_msi_packet((struct Messenger *) session->messenger_handle, NULL, NULL);
1234
1235
1236 /* TODO: Clean it up more? */
1237
1238 free ( session );
1239 return _status;
1240}
1241
1242
1243/**
1244 * @brief Send invite request to friend_id.
1245 *
1246 * @param session Control session.
1247 * @param call_type Type of the call. Audio or Video(both audio and video)
1248 * @param rngsec Ringing timeout.
1249 * @param friend_id The friend.
1250 * @return int
1251 */
1252int msi_invite ( MSISession *session, MSICallType call_type, uint32_t rngsec, uint32_t friend_id )
1253{
1254 assert ( session );
1255
1256 MSIMessage *_msg_invite = msi_new_message ( TYPE_REQUEST, stringify_request ( invite ) );
1257
1258 session->call = init_call ( session, 1, rngsec ); /* Just one for now */
1259 t_randomstr ( session->call->id, CALL_ID_LEN );
1260
1261 add_peer(session->call, friend_id );
1262
1263 session->call->type_local = call_type;
1264 /* Do whatever with message */
1265
1266 if ( call_type == type_audio ) {
1267 msi_msg_set_calltype
1268 ( _msg_invite, ( const uint8_t * ) CT_AUDIO_HEADER_VALUE, strlen ( CT_AUDIO_HEADER_VALUE ) );
1269 } else {
1270 msi_msg_set_calltype
1271 ( _msg_invite, ( const uint8_t * ) CT_VIDEO_HEADER_VALUE, strlen ( CT_VIDEO_HEADER_VALUE ) );
1272 }
1273
1274 send_message ( session, _msg_invite, friend_id );
1275 free_message ( _msg_invite );
1276
1277 session->call->state = call_inviting;
1278
1279 session->call->request_timer_id = event.timer_alloc ( handle_timeout, session, m_deftout );
1280
1281 return 0;
1282}
1283
1284
1285/**
1286 * @brief Hangup active call.
1287 *
1288 * @param session Control session.
1289 * @return int
1290 * @retval -1 Error occured.
1291 * @retval 0 Success.
1292 */
1293int msi_hangup ( MSISession *session )
1294{
1295 assert ( session );
1296
1297 if ( !session->call || session->call->state != call_active )
1298 return -1;
1299
1300 MSIMessage *_msg_ending = msi_new_message ( TYPE_REQUEST, stringify_request ( end ) );
1301
1302 /* hangup for each peer */
1303 int _it = 0;
1304
1305 for ( ; _it < session->call->peer_count; _it ++ )
1306 send_message ( session, _msg_ending, session->call->peers[_it] );
1307
1308
1309 free_message ( _msg_ending );
1310
1311 session->call->request_timer_id = event.timer_alloc ( handle_timeout, session, m_deftout );
1312
1313 return 0;
1314}
1315
1316
1317/**
1318 * @brief Answer active call request.
1319 *
1320 * @param session Control session.
1321 * @param call_type Answer with Audio or Video(both).
1322 * @return int
1323 */
1324int msi_answer ( MSISession *session, MSICallType call_type )
1325{
1326 assert ( session );
1327
1328 MSIMessage *_msg_starting = msi_new_message ( TYPE_RESPONSE, stringify_response ( starting ) );
1329 session->call->type_local = call_type;
1330
1331 if ( call_type == type_audio ) {
1332 msi_msg_set_calltype
1333 ( _msg_starting, ( const uint8_t * ) CT_AUDIO_HEADER_VALUE, strlen ( CT_AUDIO_HEADER_VALUE ) );
1334 } else {
1335 msi_msg_set_calltype
1336 ( _msg_starting, ( const uint8_t * ) CT_VIDEO_HEADER_VALUE, strlen ( CT_VIDEO_HEADER_VALUE ) );
1337 }
1338
1339 /* Now set the local encryption key and pass it with STARTING message */
1340
1341 session->call->key_local = calloc ( sizeof ( uint8_t ), crypto_secretbox_KEYBYTES );
1342 new_symmetric_key ( session->call->key_local );
1343
1344 session->call->nonce_local = calloc ( sizeof ( uint8_t ), crypto_box_NONCEBYTES );
1345 new_nonce ( session->call->nonce_local );
1346
1347 msi_msg_set_cryptokey ( _msg_starting, session->call->key_local, crypto_secretbox_KEYBYTES );
1348 msi_msg_set_nonce ( _msg_starting, session->call->nonce_local, crypto_box_NONCEBYTES );
1349
1350 send_message ( session, _msg_starting, session->call->peers[session->call->peer_count - 1] );
1351 free_message ( _msg_starting );
1352
1353 session->call->state = call_active;
1354
1355 return 0;
1356}
1357
1358
1359/**
1360 * @brief Cancel request.
1361 *
1362 * @param session Control session.
1363 * @param reason Set optional reason header. Pass NULL if none.
1364 * @return int
1365 */
1366int msi_cancel ( MSISession *session, uint32_t peer, const uint8_t *reason )
1367{
1368 assert ( session );
1369
1370 MSIMessage *_msg_cancel = msi_new_message ( TYPE_REQUEST, stringify_request ( cancel ) );
1371
1372 if ( reason ) msi_msg_set_reason(_msg_cancel, reason, strlen((const char *)reason));
1373
1374 send_message ( session, _msg_cancel, peer );
1375 free_message ( _msg_cancel );
1376
1377 terminate_call ( session );
1378
1379 return 0;
1380}
1381
1382
1383/**
1384 * @brief Reject request.
1385 *
1386 * @param session Control session.
1387 * @return int
1388 */
1389int msi_reject ( MSISession *session, const uint8_t *reason )
1390{
1391 assert ( session );
1392
1393 MSIMessage *_msg_reject = msi_new_message ( TYPE_REQUEST, stringify_request ( reject ) );
1394
1395 if ( reason ) msi_msg_set_reason(_msg_reject, reason, strlen((const char *)reason) + 1);
1396
1397 send_message ( session, _msg_reject, session->call->peers[session->call->peer_count - 1] );
1398 free_message ( _msg_reject );
1399
1400 session->call->request_timer_id = event.timer_alloc ( handle_timeout, session, m_deftout );
1401
1402 return 0;
1403}
1404
1405
1406/**
1407 * @brief Terminate the current call.
1408 *
1409 * @param session Control session.
1410 * @return int
1411 */
1412int msi_stopcall ( MSISession *session )
1413{
1414 assert ( session );
1415
1416 if ( !session->call )
1417 return -1;
1418
1419 /* just terminate it */
1420
1421 terminate_call ( session );
1422
1423 return 0;
1424} \ No newline at end of file