summaryrefslogtreecommitdiff
path: root/toxav/phone.c
diff options
context:
space:
mode:
Diffstat (limited to 'toxav/phone.c')
-rwxr-xr-xtoxav/phone.c737
1 files changed, 737 insertions, 0 deletions
diff --git a/toxav/phone.c b/toxav/phone.c
new file mode 100755
index 00000000..b55a072c
--- /dev/null
+++ b/toxav/phone.c
@@ -0,0 +1,737 @@
1/** phone.c
2 *
3 * NOTE NOTE NOTE NOTE NOTE NOTE
4 *
5 * This file is for testing/reference purposes only, hence
6 * it is _poorly_ designed and it does not fully reflect the
7 * quaility of msi nor rtp. Although toxmsi* and toxrtp* are tested
8 * there is always possiblity of crashes. If crash occures,
9 * contact me ( mannol ) on either irc channel #tox-dev @ freenode.net:6667
10 * or eniz_vukovic@hotmail.com
11 *
12 * NOTE NOTE NOTE NOTE NOTE NOTE
13 *
14 * Copyright (C) 2013 Tox project All Rights Reserved.
15 *
16 * This file is part of Tox.
17 *
18 * Tox is free software: you can redistribute it and/or modify
19 * it under the terms of the GNU General Public License as published by
20 * the Free Software Foundation, either version 3 of the License, or
21 * (at your option) any later version.
22 *
23 * Tox is distributed in the hope that it will be useful,
24 * but WITHOUT ANY WARRANTY; without even the implied warranty of
25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26 * GNU General Public License for more details.
27 *
28 * You should have received a copy of the GNU General Public License
29 * along with Tox. If not, see <http://www.gnu.org/licenses/>.
30 *
31 */
32
33#ifdef HAVE_CONFIG_H
34#include "config.h"
35#endif /* HAVE_CONFIG_H */
36
37#define _BSD_SOURCE
38#define _GNU_SOURCE
39
40#include <stdio.h>
41#include <string.h>
42#include <stdlib.h>
43
44#include "toxmsi.h"
45#include "toxrtp.h"
46#include <stdarg.h>
47#include <unistd.h>
48#include <assert.h>
49
50#include "../toxcore/network.h"
51#include "../toxcore/event.h"
52#include "../toxcore/tox.h"
53
54/* Define client version */
55#define _USERAGENT "v.0.3.0"
56
57
58typedef struct av_friend_s {
59 int _id;
60 int _active; /* 0=false; 1=true; */
61} av_friend_t;
62
63typedef struct av_session_s {
64 MSISession* _msi;
65
66 RTPSession* _rtp_audio;
67 RTPSession* _rtp_video;
68
69 pthread_mutex_t _mutex;
70
71 Tox* _messenger;
72 av_friend_t* _friends;
73 int _friend_cout;
74 uint8_t _my_public_id[200];
75} av_session_t;
76
77
78void av_allocate_friend(av_session_t* _phone, int _id, int _active)
79{
80 static int _new_id = 0;
81
82 if ( !_phone->_friends ) {
83 _phone->_friends = calloc(sizeof(av_friend_t), 1);
84 _phone->_friend_cout = 1;
85 } else{
86 _phone->_friend_cout ++;
87 _phone->_friends = realloc(_phone->_friends, sizeof(av_friend_t) * _phone->_friend_cout);
88 }
89
90 if ( _id = -1 ) {
91 _phone->_friends->_id = _new_id;
92 _new_id ++;
93 } else _phone->_friends->_id = _id;
94
95 _phone->_friends->_active = _active;
96}
97av_friend_t* av_get_friend(av_session_t* _phone, int _id)
98{
99 av_friend_t* _friends = _phone->_friends;
100
101 if ( !_friends ) return NULL;
102
103 int _it = 0;
104 for (; _it < _phone->_friend_cout; _it ++)
105 if ( _friends[_it]._id == _id )
106 return _friends + _it;
107
108 return NULL;
109}
110
111
112/***************** MISC *****************/
113
114void INFO (const char* _format, ...)
115{
116 printf("\r[!] ");
117 va_list _arg;
118 va_start (_arg, _format);
119 vfprintf (stdout, _format, _arg);
120 va_end (_arg);
121 printf("\n\r >> ");
122 fflush(stdout);
123}
124
125unsigned char *hex_string_to_bin(char hex_string[])
126{
127 size_t i, len = strlen(hex_string);
128 unsigned char *val = calloc(sizeof(char), len);
129 char *pos = hex_string;
130
131 for (i = 0; i < len; ++i, pos += 2)
132 sscanf(pos, "%2hhx", &val[i]);
133
134 return val;
135}
136
137int getinput( char* _buff, size_t _limit, int* _len )
138{
139 if ( fgets(_buff, _limit, stdin) == NULL )
140 return -1;
141
142 *_len = strlen(_buff) - 1;
143
144 /* Get rid of newline */
145 _buff[*_len] = '\0';
146
147 return 0;
148}
149
150char* trim_spaces ( char* buff )
151{
152
153 int _i = 0, _len = strlen(buff);
154
155 char* container = calloc(sizeof(char), _len);
156 int _ci = 0;
157
158 for ( ; _i < _len; _i++ ) {
159 while ( _i < _len && buff[_i] == ' ' )
160 _i++;
161
162 if ( _i < _len ){
163 container[_ci] = buff[_i];
164 _ci ++;
165 }
166 }
167
168 memcpy( buff, container, _ci );
169 buff[_ci] = '\0';
170 free(container);
171 return buff;
172}
173
174#define FRADDR_TOSTR_CHUNK_LEN 8
175
176static void fraddr_to_str(uint8_t *id_bin, char *id_str)
177{
178 uint i, delta = 0, pos_extra, sum_extra = 0;
179
180 for (i = 0; i < TOX_FRIEND_ADDRESS_SIZE; i++) {
181 sprintf(&id_str[2 * i + delta], "%02hhX", id_bin[i]);
182
183 if ((i + 1) == TOX_CLIENT_ID_SIZE)
184 pos_extra = 2 * (i + 1) + delta;
185
186 if (i >= TOX_CLIENT_ID_SIZE)
187 sum_extra |= id_bin[i];
188
189 if (!((i + 1) % FRADDR_TOSTR_CHUNK_LEN)) {
190 id_str[2 * (i + 1) + delta] = ' ';
191 delta++;
192 }
193 }
194
195 id_str[2 * i + delta] = 0;
196
197 if (!sum_extra)
198 id_str[pos_extra] = 0;
199}
200
201void* phone_handle_media_transport_poll ( void* _hmtc_args_p )
202{
203 RTPMessage* _audio_msg, * _video_msg;
204 av_session_t* _phone = _hmtc_args_p;
205 MSISession* _session = _phone->_msi;
206
207 RTPSession* _rtp_audio = _phone->_rtp_audio;
208 RTPSession* _rtp_video = _phone->_rtp_video;
209
210
211 Tox* _messenger = _phone->_messenger;
212
213
214 while ( _session->call ) {
215
216 _audio_msg = rtp_recv_msg ( _rtp_audio );
217 _video_msg = rtp_recv_msg ( _rtp_video );
218
219 if ( _audio_msg ) {
220 /* Do whatever with msg
221 printf("%d - %s\n", _audio_msg->header->sequnum, _audio_msg->data);*/
222 rtp_free_msg ( _rtp_audio, _audio_msg );
223 }
224
225 if ( _video_msg ) {
226 /* Do whatever with msg
227 p rintf("%d - %s\n", _video_msg->header->sequnum, _video_msg->data);*/
228 rtp_free_msg ( _rtp_video, _video_msg );
229 }
230
231 /*
232 * Send test message to the 'remote'
233 */
234 rtp_send_msg ( _rtp_audio, _messenger, (const uint8_t*)"audio\0", 6 );
235
236 if ( _session->call->type_local == type_video ){ /* if local call send video */
237 rtp_send_msg ( _rtp_video, _messenger, (const uint8_t*)"video\0", 6 );
238 }
239
240 _audio_msg = _video_msg = NULL;
241
242
243 /* Send ~1k messages per second
244 * That _should_ be enough for both Audio and Video
245 */
246 usleep ( 1000 );
247 /* -------------------- */
248 }
249
250 if ( _audio_msg ) rtp_free_msg(_rtp_audio, _audio_msg);
251 rtp_release_session_recv(_rtp_audio);
252 rtp_terminate_session(_rtp_audio, _messenger);
253
254 if ( _video_msg ) rtp_free_msg(_rtp_video, _video_msg);
255 rtp_release_session_recv(_rtp_video);
256 rtp_terminate_session(_rtp_video, _messenger);
257
258 INFO("Media thread finished!");
259
260 pthread_exit ( NULL );
261}
262
263int phone_startmedia_loop ( av_session_t* _phone )
264{
265 if ( !_phone ){
266 return -1;
267 }
268
269 _phone->_rtp_audio = rtp_init_session (
270 type_audio,
271 _phone->_messenger,
272 _phone->_msi->call->peers[0],
273 _phone->_msi->call->key_peer,
274 _phone->_msi->call->key_local,
275 _phone->_msi->call->nonce_peer,
276 _phone->_msi->call->nonce_local
277 );
278
279 _phone->_rtp_audio = rtp_init_session (
280 type_video,
281 _phone->_messenger,
282 _phone->_msi->call->peers[0],
283 _phone->_msi->call->key_peer,
284 _phone->_msi->call->key_local,
285 _phone->_msi->call->nonce_peer,
286 _phone->_msi->call->nonce_local
287 );
288
289
290 if ( 0 > event.rise(phone_handle_media_transport_poll, _phone) )
291 {
292 printf("Error while starting phone_handle_media_transport_poll()\n");
293 return -1;
294 }
295 else return 0;
296}
297
298
299/* Some example callbacks */
300
301void* callback_recv_invite ( void* _arg )
302{
303 const char* _call_type;
304
305 MSISession* _msi = _arg;
306
307 switch ( _msi->call->type_peer[_msi->call->peer_count - 1] ){
308 case type_audio:
309 _call_type = "audio";
310 break;
311 case type_video:
312 _call_type = "video";
313 break;
314 }
315
316 INFO( "Incoming %s call!", _call_type );
317
318}
319void* callback_recv_ringing ( void* _arg )
320{
321 INFO ( "Ringing!" );
322}
323void* callback_recv_starting ( void* _arg )
324{
325 MSISession* _session = _arg;
326 if ( 0 != phone_startmedia_loop(_session->agent_handler) ){
327 INFO("Starting call failed!");
328 } else {
329 INFO ("Call started! ( press h to hangup )");
330 }
331}
332void* callback_recv_ending ( void* _arg )
333{
334 INFO ( "Call ended!" );
335}
336
337void* callback_recv_error ( void* _arg )
338{
339 MSISession* _session = _arg;
340
341 INFO( "Error: %s", _session->last_error_str );
342}
343
344void* callback_call_started ( void* _arg )
345{
346 MSISession* _session = _arg;
347 if ( 0 != phone_startmedia_loop(_session->agent_handler) ){
348 INFO("Starting call failed!");
349 } else {
350 INFO ("Call started! ( press h to hangup )");
351 }
352
353}
354void* callback_call_canceled ( void* _arg )
355{
356 INFO ( "Call canceled!" );
357}
358void* callback_call_rejected ( void* _arg )
359{
360 INFO ( "Call rejected!" );
361}
362void* callback_call_ended ( void* _arg )
363{
364 INFO ( "Call ended!" );
365}
366
367void* callback_requ_timeout ( void* _arg )
368{
369 INFO( "No answer! " );
370}
371
372int av_connect_to_dht(av_session_t* _phone, char* _dht_key, const char* _dht_addr, unsigned short _dht_port)
373{
374 unsigned char *_binary_string = hex_string_to_bin(_dht_key);
375
376 uint16_t _port = htons(_dht_port);
377
378 int _if = tox_bootstrap_from_address(_phone->_messenger, _dht_addr, 1, _port, _binary_string );
379
380 free(_binary_string);
381
382 return _if ? 0 : -1;
383}
384
385av_session_t* av_init_session()
386{
387 av_session_t* _retu = malloc(sizeof(av_session_t));
388
389 /* Initialize our mutex */
390 pthread_mutex_init ( &_retu->_mutex, NULL );
391
392 _retu->_messenger = tox_new(1);
393
394 if ( !_retu->_messenger ) {
395 fprintf ( stderr, "tox_new() failed!\n" );
396 return NULL;
397 }
398
399 _retu->_friends = NULL;
400
401 _retu->_rtp_audio = NULL;
402 _retu->_rtp_video = NULL;
403
404 uint8_t _byte_address[TOX_FRIEND_ADDRESS_SIZE];
405 tox_get_address(_retu->_messenger, _byte_address );
406 fraddr_to_str( _byte_address, _retu->_my_public_id );
407
408
409 /* Initialize msi */
410 _retu->_msi = msi_init_session ( _retu->_messenger, _USERAGENT );
411
412 if ( !_retu->_msi ) {
413 fprintf ( stderr, "msi_init_session() failed\n" );
414 return NULL;
415 }
416
417 _retu->_msi->agent_handler = _retu;
418
419 /* ------------------ */
420 msi_register_callback(callback_call_started, cb_onstart);
421 msi_register_callback(callback_call_canceled, cb_oncancel);
422 msi_register_callback(callback_call_rejected, cb_onreject);
423 msi_register_callback(callback_call_ended, cb_onend);
424 msi_register_callback(callback_recv_invite, cb_oninvite);
425
426 msi_register_callback(callback_recv_ringing, cb_ringing);
427 msi_register_callback(callback_recv_starting, cb_starting);
428 msi_register_callback(callback_recv_ending, cb_ending);
429
430 msi_register_callback(callback_recv_error, cb_error);
431 msi_register_callback(callback_requ_timeout, cb_timeout);
432 /* ------------------ */
433
434 return _retu;
435}
436
437int av_terminate_session(av_session_t* _phone)
438{
439 if ( _phone->_msi->call ){
440 msi_hangup(_phone->_msi); /* Hangup the phone first */
441 }
442
443 free(_phone->_friends);
444 msi_terminate_session(_phone->_msi);
445 pthread_mutex_destroy ( &_phone->_mutex );
446
447 Tox* _p = _phone->_messenger;
448 _phone->_messenger = NULL; usleep(100000); /* Wait for tox_pool to end */
449 tox_kill(_p);
450
451 printf("\r[i] Quit!\n");
452 return 0;
453}
454
455/****** AV HELPER FUNCTIONS ******/
456
457/* Auto accept friend request */
458void av_friend_requ(uint8_t *_public_key, uint8_t *_data, uint16_t _length, void *_userdata)
459{
460 av_session_t* _phone = _userdata;
461 av_allocate_friend (_phone, -1, 0);
462
463 INFO("Got friend request with message: %s", _data);
464
465 tox_add_friend_norequest(_phone->_messenger, _public_key);
466
467 INFO("Auto-accepted! Friend id: %d", _phone->_friends->_id );
468}
469
470void av_friend_active(Tox *_messenger, int _friendnumber, uint8_t *_string, uint16_t _length, void *_userdata)
471{
472 av_session_t* _phone = _userdata;
473 INFO("Friend no. %d is online", _friendnumber);
474
475 av_friend_t* _this_friend = av_get_friend(_phone, _friendnumber);
476
477 if ( !_this_friend ) {
478 INFO("But it's not registered!");
479 return;
480 }
481
482 (*_this_friend)._active = 1;
483}
484
485int av_add_friend(av_session_t* _phone, char* _friend_hash)
486{
487 trim_spaces(_friend_hash);
488
489 unsigned char *_bin_string = hex_string_to_bin(_friend_hash);
490 int _number = tox_add_friend(_phone->_messenger, _bin_string, (uint8_t *)"Tox phone "_USERAGENT, sizeof("Tox phone "_USERAGENT));
491 free(_bin_string);
492
493 if ( _number >= 0) {
494 INFO("Added friend as %d", _number );
495 av_allocate_friend(_phone, _number, 0);
496 }
497 else
498 INFO("Unknown error %i", _number );
499
500 return _number;
501}
502/*********************************/
503
504void do_phone ( av_session_t* _phone )
505{
506 INFO("Welcome to tox_phone version: " _USERAGENT "\n"
507 "Usage: \n"
508 "f [pubkey] (add friend)\n"
509 "c [a/v] (type) [friend] (friend id) (calls friend if online)\n"
510 "h (if call is active hang up)\n"
511 "a [a/v] (answer incoming call: a - audio / v - audio + video (audio is default))\n"
512 "r (reject incoming call)\n"
513 "q (quit)\n"
514 "================================================================================"
515 );
516
517 while ( 1 )
518 {
519 char _line [ 1500 ];
520 int _len;
521
522 if ( -1 == getinput(_line, 1500, &_len) ){
523 printf(" >> ");
524 fflush(stdout);
525 continue;
526 }
527
528 if ( _len > 1 && _line[1] != ' ' && _line[1] != '\n' ){
529 INFO("Invalid input!");
530 continue;
531 }
532
533 switch (_line[0]){
534
535 case 'f':
536 {
537 char _id [128];
538 strncpy(_id, _line + 2, 128);
539
540 av_add_friend(_phone, _id);
541
542 } break;
543 case 'c':
544 {
545 if ( _phone->_msi->call ){
546 INFO("Already in a call");
547 break;
548 }
549
550 MSICallType _ctype;
551
552 if ( _len < 5 ){
553 INFO("Invalid input; usage: c a/v [friend]");
554 break;
555 }
556 else if ( _line[2] == 'a' || _line[2] != 'v' ){ /* default and audio */
557 _ctype = type_audio;
558 }
559 else { /* video */
560 _ctype = type_video;
561 }
562
563 char* _end;
564 int _friend = strtol(_line + 4, &_end, 10);
565
566 if ( *_end ){
567 INFO("Friend num has to be numerical value");
568 break;
569 }
570
571 /* Set timeout */
572 msi_invite ( _phone->_msi, _ctype, 10 * 1000, _friend );
573 INFO("Calling friend: %d!", _friend);
574
575 } break;
576 case 'h':
577 {
578 if ( !_phone->_msi->call ){
579 INFO("No call!");
580 break;
581 }
582
583 msi_hangup(_phone->_msi);
584
585 INFO("Hung up...");
586
587 } break;
588 case 'a':
589 {
590
591 if ( _phone->_msi->call && _phone->_msi->call->state != call_starting ) {
592 break;
593 }
594
595 if ( _len > 1 && _line[2] == 'v' )
596 msi_answer(_phone->_msi, type_video);
597 else
598 msi_answer(_phone->_msi, type_audio);
599
600 } break;
601 case 'r':
602 {
603 if ( _phone->_msi->call && _phone->_msi->call->state != call_starting ){
604 break;
605 }
606
607 msi_reject(_phone->_msi);
608
609 INFO("Call Rejected...");
610
611 } break;
612 case 'q':
613 {
614 INFO("Quitting!");
615 return;
616 }
617 default:
618 {
619 INFO("Invalid command!");
620 } break;
621
622 }
623
624 }
625}
626
627void* tox_poll (void* _messenger_p)
628{
629 Tox** _messenger = _messenger_p;
630 while( *_messenger ) {
631 tox_do(*_messenger);
632 usleep(10000);
633 }
634
635 pthread_exit(NULL);
636}
637
638int av_wait_dht(av_session_t* _phone, int _wait_seconds, const char* _ip, char* _key, unsigned short _port)
639{
640 if ( !_wait_seconds )
641 return -1;
642
643 int _waited = 0;
644
645 while( !tox_isconnected(_phone->_messenger) ) {
646
647 if ( -1 == av_connect_to_dht(_phone, _key, _ip, _port) )
648 {
649 INFO("Could not connect to: %s", _ip);
650 av_terminate_session(_phone);
651 return -1;
652 }
653
654 if ( _waited >= _wait_seconds ) return 0;
655
656 printf(".");
657 fflush(stdout);
658
659 _waited ++;
660 usleep(1000000);
661 }
662
663 int _r = _wait_seconds - _waited;
664 return _r ? _r : 1;
665}
666/* ---------------------- */
667
668int print_help ( const char* _name )
669{
670 printf ( "Usage: %s [IP] [PORT] [KEY]\n"
671 "\t[IP] (DHT ip)\n"
672 "\t[PORT] (DHT port)\n"
673 "\t[KEY] (DHT public key)\n"
674 "P.S. Friends and key are stored in ./tox_phone.conf\n"
675 ,_name );
676 return 1;
677}
678
679int main ( int argc, char* argv [] )
680{
681 if ( argc < 1 || argc < 4 )
682 return print_help(argv[0]);
683
684 char* _convertable;
685
686 int _wait_seconds = 5;
687
688 const char* _ip = argv[1];
689 char* _key = argv[3];
690 unsigned short _port = strtol(argv[2], &_convertable, 10);
691
692 if ( *_convertable ){
693 printf("Invalid port: cannot convert string to long: %s", _convertable);
694 return 1;
695 }
696
697 av_session_t* _phone = av_init_session();
698
699 tox_callback_friend_request(_phone->_messenger, av_friend_requ, _phone);
700 tox_callback_status_message(_phone->_messenger, av_friend_active, _phone);
701
702 system("clear");
703
704 INFO("\r================================================================================\n"
705 "[!] Trying dht@%s:%d"
706 , _ip, _port);
707
708 /* Start tox protocol */
709 event.rise( tox_poll, &_phone->_messenger );
710
711 /* Just clean one line */
712 printf("\r \r");
713 fflush(stdout);
714
715 int _r;
716 for ( _r = 0; _r == 0; _r = av_wait_dht(_phone, _wait_seconds, _ip, _key, _port) ) _wait_seconds --;
717
718
719 if ( -1 == _r ) {
720 INFO("Error while connecting to dht: %s:%d", _ip, _port);
721 av_terminate_session(_phone);
722 return 1;
723 }
724
725 INFO("CONNECTED!\n"
726 "================================================================================\n"
727 "%s\n"
728 "================================================================================"
729 , _phone->_my_public_id );
730
731
732 do_phone (_phone);
733
734 av_terminate_session(_phone);
735
736 return 0;
737}