add attestation API
[oweals/gnunet.git] / src / conversation / gnunet-conversation.c
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  */
45 enum 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  */
72 enum 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  */
99 struct 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_EcdsaPublicKey caller_id;
120
121   /**
122    * Unique number of the call.
123    */
124   unsigned int caller_num;
125 };
126
127
128 /**
129  * Phone handle
130  */
131 static struct GNUNET_CONVERSATION_Phone *phone;
132
133 /**
134  * Call handle (for active outgoing call).
135  */
136 static 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  */
142 static struct CallList *cl_active;
143
144 /**
145  * Head of calls waiting to be accepted.
146  */
147 static struct CallList *cl_head;
148
149 /**
150  * Tail of calls waiting to be accepted.
151  */
152 static struct CallList *cl_tail;
153
154 /**
155  * Desired phone line (string to be converted to a hash).
156  */
157 static char *line;
158
159 /**
160  * Task which handles the commands
161  */
162 static struct GNUNET_SCHEDULER_Task *handle_cmd_task;
163
164 /**
165  * Our speaker.
166  */
167 static struct GNUNET_SPEAKER_Handle *speaker;
168
169 /**
170  * Our microphone.
171  */
172 static struct GNUNET_MICROPHONE_Handle *mic;
173
174 /**
175  * Our configuration.
176  */
177 static struct GNUNET_CONFIGURATION_Handle *cfg;
178
179 /**
180  * Our ego.
181  */
182 static struct GNUNET_IDENTITY_Ego *my_caller_id;
183
184 /**
185  * Handle to identity service.
186  */
187 static struct GNUNET_IDENTITY_Handle *id;
188
189 /**
190  * Name of our ego.
191  */
192 static char *ego_name;
193
194 /**
195  * Public key of active conversation partner (if any).
196  */
197 static struct GNUNET_CRYPTO_EcdsaPublicKey peer_key;
198
199 /**
200  * Name of active conversation partner (if any).
201  */
202 static char *peer_name;
203
204 /**
205  * File handle for stdin.
206  */
207 static struct GNUNET_DISK_FileHandle *stdin_fh;
208
209 /**
210  * Our phone's current state.
211  */
212 static enum PhoneState phone_state;
213
214 /**
215  * Our call's current state.
216  */
217 static enum CallState call_state;
218
219 /**
220  * Counts the number of incoming calls we have had so far.
221  */
222 static unsigned int caller_num_gen;
223
224 /**
225  * GNS address for this phone.
226  */
227 static char *address;
228
229 /**
230  * Be verbose.
231  */
232 static 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  */
243 static void
244 phone_event_handler (void *cls,
245                      enum GNUNET_CONVERSATION_PhoneEventCode code,
246                      struct GNUNET_CONVERSATION_Caller *caller,
247                      const struct GNUNET_CRYPTO_EcdsaPublicKey *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  */
310 static void
311 caller_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  */
335 static void
336 start_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_non_null (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  */
381 static void
382 call_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  */
446 typedef void (*ActionFunction) (const char *arguments);
447
448
449 /**
450  * Structure which defines a command
451  */
452 struct 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  */
476 static void
477 do_help (const char *args);
478
479
480 /**
481  * Terminate the client
482  *
483  * @param args arguments given to the command
484  */
485 static void
486 do_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  */
498 static void
499 do_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  */
510 static void
511 do_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  */
570 static void
571 do_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  */
643 static void
644 do_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  */
661 static void
662 do_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  */
748 static void
749 do_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  */
796 static void
797 do_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  */
873 static void
874 do_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  */
937 static 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  */
975 static void
976 do_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  */
1008 static void
1009 do_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_non_null (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  */
1050 static void
1051 handle_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  */
1084 static void
1085 handle_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  */
1111 static void
1112 identity_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  */
1152 static void
1153 run (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  */
1185 int
1186 main (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 ((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 */