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