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