aboutsummaryrefslogtreecommitdiff
path: root/src/contrib/service/conversation/gnunet-conversation.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/contrib/service/conversation/gnunet-conversation.c')
-rw-r--r--src/contrib/service/conversation/gnunet-conversation.c1232
1 files changed, 1232 insertions, 0 deletions
diff --git a/src/contrib/service/conversation/gnunet-conversation.c b/src/contrib/service/conversation/gnunet-conversation.c
new file mode 100644
index 000000000..41fc7bd0b
--- /dev/null
+++ b/src/contrib/service/conversation/gnunet-conversation.c
@@ -0,0 +1,1232 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2013 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file conversation/gnunet-conversation.c
22 * @brief conversation implementation
23 * @author Simon Dieterle
24 * @author Andreas Fuchs
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28#include "gnunet_constants.h"
29#include "gnunet_gnsrecord_lib.h"
30#include "gnunet_conversation_service.h"
31#include "gnunet_namestore_service.h"
32
33/**
34 * Maximum length allowed for the command line input.
35 */
36#define MAX_MESSAGE_LENGTH 1024
37
38#define XSTRINGIFY(x) STRINGIFY (x)
39
40#define STRINGIFY(x) (#x)
41
42/**
43 * Possible states of the phone.
44 */
45enum PhoneState
46{
47 /**
48 * We're waiting for our own idenitty.
49 */
50 PS_LOOKUP_EGO,
51
52 /**
53 * We're listening for calls
54 */
55 PS_LISTEN,
56
57 /**
58 * We accepted an incoming phone call.
59 */
60 PS_ACCEPTED,
61
62 /**
63 * Internal error
64 */
65 PS_ERROR
66};
67
68
69/**
70 * States for current outgoing call.
71 */
72enum CallState
73{
74 /**
75 * We are looking up some other participant.
76 */
77 CS_RESOLVING,
78
79 /**
80 * We are now ringing the other participant.
81 */
82 CS_RINGING,
83
84 /**
85 * The other party accepted our call and we are now connected.
86 */
87 CS_CONNECTED,
88
89 /**
90 * The call is currently suspended (by us).
91 */
92 CS_SUSPENDED
93};
94
95
96/**
97 * List of incoming calls
98 */
99struct CallList
100{
101 /**
102 * A DLL.
103 */
104 struct CallList *prev;
105
106 /**
107 * A DLL.
108 */
109 struct CallList *next;
110
111 /**
112 * Handle to hang up or activate.
113 */
114 struct GNUNET_CONVERSATION_Caller *caller;
115
116 /**
117 * Public key identifying the caller.
118 */
119 struct GNUNET_CRYPTO_PublicKey caller_id;
120
121 /**
122 * Unique number of the call.
123 */
124 unsigned int caller_num;
125};
126
127
128/**
129 * Phone handle
130 */
131static struct GNUNET_CONVERSATION_Phone *phone;
132
133/**
134 * Call handle (for active outgoing call).
135 */
136static struct GNUNET_CONVERSATION_Call *call;
137
138/**
139 * Caller handle (for active incoming call).
140 * This call handler is NOT in the #cl_head / #cl_tail list.
141 */
142static struct CallList *cl_active;
143
144/**
145 * Head of calls waiting to be accepted.
146 */
147static struct CallList *cl_head;
148
149/**
150 * Tail of calls waiting to be accepted.
151 */
152static struct CallList *cl_tail;
153
154/**
155 * Desired phone line (string to be converted to a hash).
156 */
157static char *line;
158
159/**
160 * Task which handles the commands
161 */
162static struct GNUNET_SCHEDULER_Task *handle_cmd_task;
163
164/**
165 * Our speaker.
166 */
167static struct GNUNET_SPEAKER_Handle *speaker;
168
169/**
170 * Our microphone.
171 */
172static struct GNUNET_MICROPHONE_Handle *mic;
173
174/**
175 * Our configuration.
176 */
177static struct GNUNET_CONFIGURATION_Handle *cfg;
178
179/**
180 * Our ego.
181 */
182static struct GNUNET_IDENTITY_Ego *my_caller_id;
183
184/**
185 * Handle to identity service.
186 */
187static struct GNUNET_IDENTITY_Handle *id;
188
189/**
190 * Name of our ego.
191 */
192static char *ego_name;
193
194/**
195 * Public key of active conversation partner (if any).
196 */
197static struct GNUNET_CRYPTO_PublicKey peer_key;
198
199/**
200 * Name of active conversation partner (if any).
201 */
202static char *peer_name;
203
204/**
205 * File handle for stdin.
206 */
207static struct GNUNET_DISK_FileHandle *stdin_fh;
208
209/**
210 * Our phone's current state.
211 */
212static enum PhoneState phone_state;
213
214/**
215 * Our call's current state.
216 */
217static enum CallState call_state;
218
219/**
220 * Counts the number of incoming calls we have had so far.
221 */
222static unsigned int caller_num_gen;
223
224/**
225 * GNS address for this phone.
226 */
227static char *address;
228
229/**
230 * Be verbose.
231 */
232static int verbose;
233
234
235/**
236 * Function called with an event emitted by a phone.
237 *
238 * @param cls closure
239 * @param code type of the event
240 * @param caller handle for the caller
241 * @param caller_id public key of the caller (in GNS)
242 */
243static void
244phone_event_handler (void *cls,
245 enum GNUNET_CONVERSATION_PhoneEventCode code,
246 struct GNUNET_CONVERSATION_Caller *caller,
247 const struct GNUNET_CRYPTO_PublicKey *caller_id)
248{
249 struct CallList *cl;
250
251 (void) cls;
252 switch (code)
253 {
254 case GNUNET_CONVERSATION_EC_PHONE_RING:
255 /*
256 * FIXME: we should be playing our ringtones from contrib/sounds now!
257 *
258 ring_my_bell();
259 *
260 * see https://gstreamer.freedesktop.org/documentation/application-development/highlevel/playback-components.html on how to play a wav using the gst framework being used here
261 */fprintf (
262 stdout,
263 _ (
264 "Incoming call from `%s'. Please /accept %u or /cancel %u the call.\n"),
265 GNUNET_GNSRECORD_pkey_to_zkey (caller_id),
266 caller_num_gen,
267 caller_num_gen);
268 cl = GNUNET_new (struct CallList);
269 cl->caller = caller;
270 cl->caller_id = *caller_id;
271 cl->caller_num = caller_num_gen++;
272 GNUNET_CONTAINER_DLL_insert (cl_head, cl_tail, cl);
273 break;
274
275 case GNUNET_CONVERSATION_EC_PHONE_HUNG_UP:
276 for (cl = cl_head; NULL != cl; cl = cl->next)
277 if (caller == cl->caller)
278 break;
279 if ((NULL == cl) && (caller == cl_active->caller))
280 cl = cl_active;
281 if (NULL == cl)
282 {
283 GNUNET_break (0);
284 return;
285 }
286 fprintf (stdout,
287 _ ("Call from `%s' terminated\n"),
288 GNUNET_GNSRECORD_pkey_to_zkey (&cl->caller_id));
289 if (cl == cl_active)
290 {
291 cl_active = NULL;
292 phone_state = PS_LISTEN;
293 }
294 else
295 {
296 GNUNET_CONTAINER_DLL_remove (cl_head, cl_tail, cl);
297 }
298 GNUNET_free (cl);
299 break;
300 }
301}
302
303
304/**
305 * Function called with an event emitted by a caller.
306 *
307 * @param cls closure with the `struct CallList` of the caller
308 * @param code type of the event issued by the caller
309 */
310static void
311caller_event_handler (void *cls, enum GNUNET_CONVERSATION_CallerEventCode code)
312{
313 struct CallList *cl = cls;
314
315 switch (code)
316 {
317 case GNUNET_CONVERSATION_EC_CALLER_SUSPEND:
318 fprintf (stdout,
319 _ ("Call from `%s' suspended by other user\n"),
320 GNUNET_GNSRECORD_pkey_to_zkey (&cl->caller_id));
321 break;
322
323 case GNUNET_CONVERSATION_EC_CALLER_RESUME:
324 fprintf (stdout,
325 _ ("Call from `%s' resumed by other user\n"),
326 GNUNET_GNSRECORD_pkey_to_zkey (&cl->caller_id));
327 break;
328 }
329}
330
331
332/**
333 * Start our phone.
334 */
335static void
336start_phone ()
337{
338 struct GNUNET_GNSRECORD_Data rd;
339
340 if (NULL == my_caller_id)
341 {
342 fprintf (stderr,
343 _ ("Ego `%s' no longer available, phone is now down.\n"),
344 ego_name);
345 phone_state = PS_LOOKUP_EGO;
346 return;
347 }
348 GNUNET_assert (NULL == phone);
349 phone = GNUNET_CONVERSATION_phone_create (cfg,
350 my_caller_id,
351 &phone_event_handler,
352 NULL);
353 /* FIXME: get record and print full GNS record info later here... */
354 if (NULL == phone)
355 {
356 fprintf (stderr, "%s", _ ("Failed to setup phone (internal error)\n"));
357 phone_state = PS_ERROR;
358 }
359 else
360 {
361 GNUNET_CONVERSATION_phone_get_record (phone, &rd);
362 GNUNET_free (address);
363 address =
364 GNUNET_GNSRECORD_value_to_string (rd.record_type, rd.data, rd.data_size);
365 fprintf (
366 stdout,
367 _ (
368 "Phone active at `%s'. Type `/help' for a list of available commands\n"),
369 address);
370 phone_state = PS_LISTEN;
371 }
372}
373
374
375/**
376 * Function called with an event emitted by a call.
377 *
378 * @param cls closure, NULL
379 * @param code type of the event on the call
380 */
381static void
382call_event_handler (void *cls, enum GNUNET_CONVERSATION_CallEventCode code)
383{
384 (void) cls;
385
386 switch (code)
387 {
388 case GNUNET_CONVERSATION_EC_CALL_RINGING:
389 GNUNET_break (CS_RESOLVING == call_state);
390 fprintf (stdout,
391 _ ("Resolved address of `%s'. Now ringing other party.\n"),
392 peer_name);
393 call_state = CS_RINGING;
394 break;
395
396 case GNUNET_CONVERSATION_EC_CALL_PICKED_UP:
397 GNUNET_break (CS_RINGING == call_state);
398 fprintf (stdout, _ ("Connection established to `%s'\n"), peer_name);
399 call_state = CS_CONNECTED;
400 break;
401
402 case GNUNET_CONVERSATION_EC_CALL_GNS_FAIL:
403 GNUNET_break (CS_RESOLVING == call_state);
404 fprintf (stdout, _ ("Failed to resolve `%s'\n"), peer_name);
405 GNUNET_free (peer_name);
406 peer_name = NULL;
407 call = NULL;
408 break;
409
410 case GNUNET_CONVERSATION_EC_CALL_HUNG_UP:
411 fprintf (stdout, _ ("Call to `%s' terminated\n"), peer_name);
412 GNUNET_free (peer_name);
413 peer_name = NULL;
414 call = NULL;
415 break;
416
417 case GNUNET_CONVERSATION_EC_CALL_SUSPENDED:
418 GNUNET_break (CS_CONNECTED == call_state);
419 fprintf (stdout,
420 _ ("Connection to `%s' suspended (by other user)\n"),
421 peer_name);
422 break;
423
424 case GNUNET_CONVERSATION_EC_CALL_RESUMED:
425 GNUNET_break (CS_CONNECTED == call_state);
426 fprintf (stdout,
427 _ ("Connection to `%s' resumed (by other user)\n"),
428 peer_name);
429 break;
430
431 case GNUNET_CONVERSATION_EC_CALL_ERROR:
432 fprintf (stdout, _ ("Error with the call, restarting it\n"));
433 GNUNET_free (peer_name);
434 peer_name = NULL;
435 call = NULL;
436 break;
437 }
438}
439
440
441/**
442 * Function declareation for executing a action
443 *
444 * @param arguments arguments given to the function
445 */
446typedef void (*ActionFunction) (const char *arguments);
447
448
449/**
450 * Structure which defines a command
451 */
452struct VoipCommand
453{
454 /**
455 * Command the user needs to enter.
456 */
457 const char *command;
458
459 /**
460 * Function to call on command.
461 */
462 ActionFunction Action;
463
464 /**
465 * Help text for the command.
466 */
467 const char *helptext;
468};
469
470
471/**
472 * Action function to print help for the command shell.
473 *
474 * @param args arguments given to the command
475 */
476static void
477do_help (const char *args);
478
479
480/**
481 * Terminate the client
482 *
483 * @param args arguments given to the command
484 */
485static void
486do_quit (const char *args)
487{
488 (void) args;
489 GNUNET_SCHEDULER_shutdown ();
490}
491
492
493/**
494 * Handler for unknown command.
495 *
496 * @param msg arguments given to the command
497 */
498static void
499do_unknown (const char *msg)
500{
501 fprintf (stderr, _ ("Unknown command `%s'\n"), msg);
502}
503
504
505/**
506 * Initiating a new call
507 *
508 * @param arg arguments given to the command
509 */
510static void
511do_call (const char *arg)
512{
513 if (NULL == my_caller_id)
514 {
515 fprintf (stderr, _ ("Ego `%s' not available\n"), ego_name);
516 return;
517 }
518 if (NULL != call)
519 {
520 fprintf (stderr,
521 _ ("You are calling someone else already, hang up first!\n"));
522 return;
523 }
524 switch (phone_state)
525 {
526 case PS_LOOKUP_EGO:
527 fprintf (stderr, _ ("Ego `%s' not available\n"), ego_name);
528 return;
529
530 case PS_LISTEN:
531 /* ok to call! */
532 break;
533
534 case PS_ACCEPTED:
535 fprintf (
536 stderr,
537 _ (
538 "You are answering call from `%s', hang up or suspend that call first!\n"),
539 GNUNET_GNSRECORD_pkey_to_zkey (&peer_key));
540 return;
541
542 case PS_ERROR:
543 /* ok to call */
544 break;
545 }
546 if (NULL == arg)
547 {
548 fprintf (stderr, _ ("Call recipient missing.\n"));
549 do_help ("/call");
550 return;
551 }
552 peer_name = GNUNET_strdup (arg);
553 call_state = CS_RESOLVING;
554 GNUNET_assert (NULL == call);
555 call = GNUNET_CONVERSATION_call_start (cfg,
556 my_caller_id,
557 arg,
558 speaker,
559 mic,
560 &call_event_handler,
561 NULL);
562}
563
564
565/**
566 * Accepting an incoming call
567 *
568 * @param args arguments given to the command
569 */
570static void
571do_accept (const char *args)
572{
573 struct CallList *cl;
574 char buf[32];
575
576 if ((NULL != call) && (CS_SUSPENDED != call_state))
577 {
578 fprintf (stderr,
579 _ ("You are calling someone else already, hang up first!\n"));
580 return;
581 }
582 switch (phone_state)
583 {
584 case PS_LOOKUP_EGO:
585 GNUNET_break (0);
586 break;
587
588 case PS_LISTEN:
589 /* this is the expected state */
590 break;
591
592 case PS_ACCEPTED:
593 fprintf (
594 stderr,
595 _ (
596 "You are answering call from `%s', hang up or suspend that call first!\n"),
597 GNUNET_GNSRECORD_pkey_to_zkey (&peer_key));
598 return;
599
600 case PS_ERROR:
601 GNUNET_break (0);
602 break;
603 }
604 cl = cl_head;
605 if (NULL == cl)
606 {
607 fprintf (stderr, _ ("There is no incoming call to accept here!\n"));
608 return;
609 }
610 if ((NULL != cl->next) || (NULL != args))
611 {
612 for (cl = cl_head; NULL != cl; cl = cl->next)
613 {
614 GNUNET_snprintf (buf, sizeof(buf), "%u", cl->caller_num);
615 if (0 == strcmp (buf, args))
616 break;
617 }
618 }
619 if (NULL == cl)
620 {
621 fprintf (stderr,
622 _ ("There is no incoming call `%s' to accept right now!\n"),
623 args);
624 return;
625 }
626 GNUNET_CONTAINER_DLL_remove (cl_head, cl_tail, cl);
627 cl_active = cl;
628 peer_key = cl->caller_id;
629 phone_state = PS_ACCEPTED;
630 GNUNET_CONVERSATION_caller_pick_up (cl->caller,
631 &caller_event_handler,
632 cl,
633 speaker,
634 mic);
635}
636
637
638/**
639 * Print address information for this phone.
640 *
641 * @param args arguments given to the command
642 */
643static void
644do_address (const char *args)
645{
646 (void) args;
647 if (NULL == address)
648 {
649 fprintf (stdout, "%s", _ ("We currently do not have an address.\n"));
650 return;
651 }
652 fprintf (stdout, "%s\n", address);
653}
654
655
656/**
657 * Accepting an incoming call
658 *
659 * @param args arguments given to the command
660 */
661static void
662do_status (const char *args)
663{
664 struct CallList *cl;
665
666 (void) args;
667 switch (phone_state)
668 {
669 case PS_LOOKUP_EGO:
670 fprintf (
671 stdout,
672 _ (
673 "We are currently trying to locate the private key for the ego `%s'.\n"),
674 ego_name);
675 break;
676
677 case PS_LISTEN:
678 fprintf (stdout,
679 _ (
680 "We are listening for incoming calls for ego `%s' on line `%s'.\n"),
681 ego_name,
682 line);
683 break;
684
685 case PS_ACCEPTED:
686 fprintf (stdout,
687 _ ("You are having a conversation with `%s'.\n"),
688 GNUNET_GNSRECORD_pkey_to_zkey (&peer_key));
689 ;
690 break;
691
692 case PS_ERROR:
693 fprintf (
694 stdout,
695 _ (
696 "We had an internal error setting up our phone line. You can still make calls.\n"));
697 break;
698 }
699 if (NULL != call)
700 {
701 switch (call_state)
702 {
703 case CS_RESOLVING:
704 fprintf (stdout,
705 _ ("We are trying to find the network address to call `%s'.\n"),
706 peer_name);
707 break;
708
709 case CS_RINGING:
710 fprintf (stdout,
711 _ ("We are calling `%s', their phone should be ringing.\n"),
712 peer_name);
713 break;
714
715 case CS_CONNECTED:
716 fprintf (stdout,
717 _ ("You are having a conversation with `%s'.\n"),
718 peer_name);
719 break;
720
721 case CS_SUSPENDED:
722 /* ok to accept incoming call right now */
723 break;
724 }
725 }
726 if ((NULL != cl_head) && ((cl_head != cl_active) || (cl_head != cl_tail)))
727 {
728 fprintf (stdout, "%s", _ ("Calls waiting:\n"));
729 for (cl = cl_head; NULL != cl; cl = cl->next)
730 {
731 if (cl == cl_active)
732 continue;
733 fprintf (stdout,
734 _ ("#%u: `%s'\n"),
735 cl->caller_num,
736 GNUNET_GNSRECORD_pkey_to_zkey (&cl->caller_id));
737 }
738 fprintf (stdout, "%s", "\n");
739 }
740}
741
742
743/**
744 * Suspending a call
745 *
746 * @param args arguments given to the command
747 */
748static void
749do_suspend (const char *args)
750{
751 (void) args;
752 if (NULL != call)
753 {
754 switch (call_state)
755 {
756 case CS_RESOLVING:
757 case CS_RINGING:
758 case CS_SUSPENDED:
759 fprintf (stderr,
760 "%s",
761 _ ("There is no call that could be suspended right now.\n"));
762 return;
763
764 case CS_CONNECTED:
765 call_state = CS_SUSPENDED;
766 GNUNET_CONVERSATION_call_suspend (call);
767 return;
768 }
769 }
770 switch (phone_state)
771 {
772 case PS_LOOKUP_EGO:
773 case PS_LISTEN:
774 case PS_ERROR:
775 fprintf (stderr,
776 "%s",
777 _ ("There is no call that could be suspended right now.\n"));
778 return;
779
780 case PS_ACCEPTED:
781 /* expected state, do rejection logic */
782 break;
783 }
784 GNUNET_assert (NULL != cl_active);
785 GNUNET_CONVERSATION_caller_suspend (cl_active->caller);
786 cl_active = NULL;
787 phone_state = PS_LISTEN;
788}
789
790
791/**
792 * Resuming a call
793 *
794 * @param args arguments given to the command
795 */
796static void
797do_resume (const char *args)
798{
799 struct CallList *cl;
800 char buf[32];
801
802 if (NULL != call)
803 {
804 switch (call_state)
805 {
806 case CS_RESOLVING:
807 case CS_RINGING:
808 case CS_CONNECTED:
809 fprintf (stderr,
810 "%s",
811 _ ("There is no call that could be resumed right now.\n"));
812 return;
813
814 case CS_SUSPENDED:
815 call_state = CS_CONNECTED;
816 GNUNET_CONVERSATION_call_resume (call, speaker, mic);
817 return;
818 }
819 }
820 switch (phone_state)
821 {
822 case PS_LOOKUP_EGO:
823 case PS_ERROR:
824 fprintf (stderr,
825 "%s",
826 _ ("There is no call that could be resumed right now.\n"));
827 return;
828
829 case PS_LISTEN:
830 /* expected state, do resume logic */
831 break;
832
833 case PS_ACCEPTED:
834 fprintf (stderr,
835 _ ("Already talking with `%s', cannot resume a call right now.\n"),
836 GNUNET_GNSRECORD_pkey_to_zkey (&peer_key));
837 return;
838 }
839 GNUNET_assert (NULL == cl_active);
840 cl = cl_head;
841 if (NULL == cl)
842 {
843 fprintf (stderr, _ ("There is no incoming call to resume here!\n"));
844 return;
845 }
846 if ((NULL != cl->next) || (NULL != args))
847 {
848 for (cl = cl_head; NULL != cl; cl = cl->next)
849 {
850 GNUNET_snprintf (buf, sizeof(buf), "%u", cl->caller_num);
851 if (0 == strcmp (buf, args))
852 break;
853 }
854 }
855 if (NULL == cl)
856 {
857 fprintf (stderr,
858 _ ("There is no incoming call `%s' to resume right now!\n"),
859 args);
860 return;
861 }
862 cl_active = cl;
863 GNUNET_CONVERSATION_caller_resume (cl_active->caller, speaker, mic);
864 phone_state = PS_ACCEPTED;
865}
866
867
868/**
869 * Rejecting a call
870 *
871 * @param args arguments given to the command
872 */
873static void
874do_reject (const char *args)
875{
876 struct CallList *cl;
877 char buf[32];
878
879 if (NULL != call)
880 {
881 GNUNET_CONVERSATION_call_stop (call);
882 call = NULL;
883 return;
884 }
885 switch (phone_state)
886 {
887 case PS_LOOKUP_EGO:
888 case PS_ERROR:
889 fprintf (stderr,
890 "%s",
891 _ ("There is no call that could be cancelled right now.\n"));
892 return;
893
894 case PS_LISTEN:
895 /* look for active incoming calls to refuse */
896 cl = cl_head;
897 if (NULL == cl)
898 {
899 fprintf (stderr, _ ("There is no incoming call to refuse here!\n"));
900 return;
901 }
902 if ((NULL != cl->next) || (NULL != args))
903 {
904 for (cl = cl_head; NULL != cl; cl = cl->next)
905 {
906 GNUNET_snprintf (buf, sizeof(buf), "%u", cl->caller_num);
907 if (0 == strcmp (buf, args))
908 break;
909 }
910 }
911 if (NULL == cl)
912 {
913 fprintf (stderr,
914 _ ("There is no incoming call `%s' to refuse right now!\n"),
915 args);
916 return;
917 }
918 GNUNET_CONVERSATION_caller_hang_up (cl->caller);
919 GNUNET_CONTAINER_DLL_remove (cl_head, cl_tail, cl);
920 GNUNET_free (cl);
921 break;
922
923 case PS_ACCEPTED:
924 /* expected state, do rejection logic */
925 GNUNET_assert (NULL != cl_active);
926 GNUNET_CONVERSATION_caller_hang_up (cl_active->caller);
927 cl_active = NULL;
928 phone_state = PS_LISTEN;
929 break;
930 }
931}
932
933
934/**
935 * List of supported commands.
936 */
937static struct VoipCommand commands[] = {
938 { "/address",
939 &do_address,
940 gettext_noop (
941 "Use `/address' to find out which address this phone should have in GNS") },
942 { "/call", &do_call, gettext_noop ("Use `/call USER.gnu' to call USER") },
943 { "/accept",
944 &do_accept,
945 gettext_noop ("Use `/accept #NUM' to accept incoming call #NUM") },
946 { "/suspend",
947 &do_suspend,
948 gettext_noop ("Use `/suspend' to suspend the active call") },
949 { "/resume",
950 &do_resume,
951 gettext_noop (
952 "Use `/resume [#NUM]' to resume a call, #NUM is needed to resume incoming calls, no argument is needed to resume the current outgoing call.") },
953 { "/cancel",
954 &do_reject,
955 gettext_noop ("Use `/cancel' to reject or terminate a call") },
956 { "/status",
957 &do_status,
958 gettext_noop ("Use `/status' to print status information") },
959 { "/quit",
960 &do_quit,
961 gettext_noop ("Use `/quit' to terminate gnunet-conversation") },
962 { "/help",
963 &do_help,
964 gettext_noop ("Use `/help command' to get help for a specific command") },
965 { "", &do_unknown, NULL },
966 { NULL, NULL, NULL },
967};
968
969
970/**
971 * Action function to print help for the command shell.
972 *
973 * @param args arguments given to the command
974 */
975static void
976do_help (const char *args)
977{
978 unsigned int i;
979
980 i = 0;
981 while ((NULL != args) && (0 != strlen (args)) &&
982 (commands[i].Action != &do_help))
983 {
984 if (0 == strncasecmp (&args[1], &commands[i].command[1], strlen (args) - 1))
985 {
986 fprintf (stdout, "%s\n", gettext (commands[i].helptext));
987 return;
988 }
989 i++;
990 }
991 i = 0;
992 fprintf (stdout, "%s", "Available commands:\n");
993 while (commands[i].Action != &do_help)
994 {
995 fprintf (stdout, "%s\n", gettext (commands[i].command));
996 i++;
997 }
998 fprintf (stdout, "%s", "\n");
999 fprintf (stdout, "%s\n", gettext (commands[i].helptext));
1000}
1001
1002
1003/**
1004 * Task run during shutdown.
1005 *
1006 * @param cls NULL
1007 */
1008static void
1009do_stop_task (void *cls)
1010{
1011 (void) cls;
1012 if (NULL != call)
1013 {
1014 GNUNET_CONVERSATION_call_stop (call);
1015 call = NULL;
1016 }
1017 if (NULL != phone)
1018 {
1019 GNUNET_CONVERSATION_phone_destroy (phone);
1020 phone = NULL;
1021 }
1022 if (NULL != handle_cmd_task)
1023 {
1024 GNUNET_SCHEDULER_cancel (handle_cmd_task);
1025 handle_cmd_task = NULL;
1026 }
1027 if (NULL != id)
1028 {
1029 GNUNET_IDENTITY_disconnect (id);
1030 id = NULL;
1031 }
1032 GNUNET_SPEAKER_destroy (speaker);
1033 speaker = NULL;
1034 GNUNET_MICROPHONE_destroy (mic);
1035 mic = NULL;
1036 GNUNET_free (ego_name);
1037 ego_name = NULL;
1038 GNUNET_free (peer_name);
1039 peer_name = NULL;
1040 phone_state = PS_ERROR;
1041}
1042
1043
1044/**
1045 * Handle user command.
1046 *
1047 * @param message command the user typed in
1048 * @param str_len number of bytes to process in @a message
1049 */
1050static void
1051handle_command_string (char *message, size_t str_len)
1052{
1053 size_t i;
1054 const char *ptr;
1055
1056 if (0 == str_len)
1057 return;
1058 if (message[str_len - 1] == '\n')
1059 message[str_len - 1] = '\0';
1060 if (message[str_len - 2] == '\r')
1061 message[str_len - 2] = '\0';
1062 if (0 == strlen (message))
1063 return;
1064 i = 0;
1065 while (
1066 (NULL != commands[i].command) &&
1067 (0 !=
1068 strncasecmp (commands[i].command, message, strlen (commands[i].command))))
1069 i++;
1070 ptr = &message[strlen (commands[i].command)];
1071 while (isspace ((unsigned char) *ptr))
1072 ptr++;
1073 if ('\0' == *ptr)
1074 ptr = NULL;
1075 commands[i].Action (ptr);
1076}
1077
1078
1079/**
1080 * Task to handle commands from the terminal.
1081 *
1082 * @param cls NULL
1083 */
1084static void
1085handle_command (void *cls)
1086{
1087 char message[MAX_MESSAGE_LENGTH + 1];
1088
1089 (void) cls;
1090 handle_cmd_task =
1091 GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
1092 stdin_fh,
1093 &handle_command,
1094 NULL);
1095 /* read message from command line and handle it */
1096 memset (message, 0, MAX_MESSAGE_LENGTH + 1);
1097 if (NULL == fgets (message, MAX_MESSAGE_LENGTH, stdin))
1098 return;
1099 handle_command_string (message, strlen (message));
1100}
1101
1102
1103/**
1104 * Function called by identity service with information about egos.
1105 *
1106 * @param cls NULL
1107 * @param ego ego handle
1108 * @param ctx unused
1109 * @param name name of the ego
1110 */
1111static void
1112identity_cb (void *cls,
1113 struct GNUNET_IDENTITY_Ego *ego,
1114 void **ctx,
1115 const char *name)
1116{
1117 (void) cls;
1118 (void) ctx;
1119 if (NULL == name)
1120 return;
1121 if (ego == my_caller_id)
1122 {
1123 if (verbose)
1124 fprintf (stdout, _ ("Name of our ego changed to `%s'\n"), name);
1125 GNUNET_free (ego_name);
1126 ego_name = GNUNET_strdup (name);
1127 return;
1128 }
1129 if (0 != strcmp (name, ego_name))
1130 return;
1131 if (NULL == ego)
1132 {
1133 if (verbose)
1134 fprintf (stdout, _ ("Our ego `%s' was deleted!\n"), ego_name);
1135 my_caller_id = NULL;
1136 return;
1137 }
1138 my_caller_id = ego;
1139 GNUNET_CONFIGURATION_set_value_string (cfg, "CONVERSATION", "LINE", line);
1140 start_phone ();
1141}
1142
1143
1144/**
1145 * Main function that will be run by the scheduler.
1146 *
1147 * @param cls closure
1148 * @param args remaining command-line arguments
1149 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
1150 * @param c configuration
1151 */
1152static void
1153run (void *cls,
1154 char *const *args,
1155 const char *cfgfile,
1156 const struct GNUNET_CONFIGURATION_Handle *c)
1157{
1158 (void) cls;
1159 (void) args;
1160 (void) cfgfile;
1161 cfg = GNUNET_CONFIGURATION_dup (c);
1162 speaker = GNUNET_SPEAKER_create_from_hardware (cfg);
1163 mic = GNUNET_MICROPHONE_create_from_hardware (cfg);
1164 if (NULL == ego_name)
1165 {
1166 fprintf (stderr, "%s", _ ("You must specify the NAME of an ego to use\n"));
1167 return;
1168 }
1169 id = GNUNET_IDENTITY_connect (cfg, &identity_cb, NULL);
1170 handle_cmd_task =
1171 GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_UI,
1172 &handle_command,
1173 NULL);
1174 GNUNET_SCHEDULER_add_shutdown (&do_stop_task, NULL);
1175}
1176
1177
1178/**
1179 * The main function to conversation.
1180 *
1181 * @param argc number of arguments from the command line
1182 * @param argv command line arguments
1183 * @return 0 ok, 1 on error
1184 */
1185int
1186main (int argc, char *const *argv)
1187{
1188 struct GNUNET_GETOPT_CommandLineOption options[] =
1189 { GNUNET_GETOPT_option_string (
1190 'e',
1191 "ego",
1192 "NAME",
1193 gettext_noop ("sets the NAME of the ego to use for the caller ID"),
1194 &ego_name),
1195 GNUNET_GETOPT_option_string ('p',
1196 "phone",
1197 "LINE",
1198 gettext_noop (
1199 "sets the LINE to use for the phone"),
1200 &line),
1201 GNUNET_GETOPT_OPTION_END };
1202 int ret;
1203
1204 int flags;
1205 flags = fcntl (0, F_GETFL, 0);
1206 flags |= O_NONBLOCK;
1207 if (0 != fcntl (0, F_SETFL, flags))
1208 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "fcntl");
1209 stdin_fh = GNUNET_DISK_get_handle_from_int_fd (0);
1210
1211 if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
1212 return 2;
1213 ret =
1214 GNUNET_PROGRAM_run (argc,
1215 argv,
1216 "gnunet-conversation",
1217 gettext_noop (
1218 "Enables having a conversation with other GNUnet users."),
1219 options,
1220 &run,
1221 NULL);
1222 GNUNET_free_nz ((void *) argv);
1223 if (NULL != cfg)
1224 {
1225 GNUNET_CONFIGURATION_destroy (cfg);
1226 cfg = NULL;
1227 }
1228 return (GNUNET_OK == ret) ? 0 : 1;
1229}
1230
1231
1232/* end of gnunet-conversation.c */