-avoid use after free
[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'.\nPlease /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   phone = GNUNET_CONVERSATION_phone_create (cfg,
336                                             caller_id,
337                                             &phone_event_handler, NULL);
338   /* FIXME: get record and print full GNS record info later here... */
339   if (NULL == phone)
340   {
341     FPRINTF (stderr,
342              "%s",
343              _("Failed to setup phone (internal error)\n"));
344     phone_state = PS_ERROR;
345   }
346   else
347   {
348     GNUNET_CONVERSATION_phone_get_record (phone,
349                                           &rd);
350     GNUNET_free_non_null (address);
351     address = GNUNET_GNSRECORD_value_to_string (rd.record_type,
352                                                 rd.data,
353                                                 rd.data_size);
354     if (verbose)
355       FPRINTF (stdout,
356                _("Phone active on line %u\n"),
357                (unsigned int) line);
358     phone_state = PS_LISTEN;
359   }
360 }
361
362
363 /**
364  * Function called with an event emitted by a call.
365  *
366  * @param cls closure, NULL
367  * @param code type of the event on the call
368  */
369 static void
370 call_event_handler (void *cls,
371                     enum GNUNET_CONVERSATION_CallEventCode code)
372 {
373   switch (code)
374   {
375   case GNUNET_CONVERSATION_EC_CALL_RINGING:
376     GNUNET_break (CS_RESOLVING == call_state);
377     if (verbose)
378       FPRINTF (stdout,
379                "%s",
380                _("Resolved address. Now ringing other party.\n"));
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   call = GNUNET_CONVERSATION_call_start (cfg,
528                                          caller_id,
529                                          arg,
530                                          speaker,
531                                          mic,
532                                          &call_event_handler, NULL);
533 }
534
535
536 /**
537  * Accepting an incoming call
538  *
539  * @param args arguments given to the command
540  */
541 static void
542 do_accept (const char *args)
543 {
544   struct CallList *cl;
545   char buf[32];
546
547   if ( (NULL != call) &&
548        (CS_SUSPENDED != call_state) )
549   {
550     FPRINTF (stderr,
551              _("You are calling someone else already, hang up first!\n"));
552     return;
553   }
554   switch (phone_state)
555   {
556   case PS_LOOKUP_EGO:
557     GNUNET_break (0);
558     break;
559   case PS_LISTEN:
560     /* this is the expected state */
561     break;
562   case PS_ACCEPTED:
563     FPRINTF (stderr,
564              _("You are answering call from `%s', hang up or suspend that call first!\n"),
565              peer_name);
566     return;
567   case PS_ERROR:
568     GNUNET_break (0);
569     break;
570   }
571   cl = cl_head;
572   if (NULL == cl)
573   {
574     FPRINTF (stderr,
575              _("There is no incoming call to accept here!\n"));
576     return;
577   }
578   if ( (NULL != cl->next) || (NULL != args) )
579   {
580     for (cl = cl_head; NULL != cl; cl = cl->next)
581     {
582       GNUNET_snprintf (buf, sizeof (buf),
583                        "%u",
584                        cl->caller_num);
585       if (0 == strcmp (buf, args))
586         break;
587     }
588   }
589   if (NULL == cl)
590   {
591     FPRINTF (stderr,
592              _("There is no incoming call `%s' to accept right now!\n"),
593              args);
594     return;
595   }
596   cl_active = cl;
597   GNUNET_free_non_null (peer_name);
598   peer_name = GNUNET_strdup (cl->caller_id);
599   phone_state = PS_ACCEPTED;
600   GNUNET_CONVERSATION_caller_pick_up (cl->caller,
601                                       &caller_event_handler,
602                                       cl,
603                                       speaker,
604                                       mic);
605 }
606
607
608 /**
609  * Print address information for this phone.
610  *
611  * @param args arguments given to the command
612  */
613 static void
614 do_address (const char *args)
615 {
616   if (NULL == address)
617   {
618     FPRINTF (stdout,
619              "%s",
620              _("We currently do not have an address.\n"));
621     return;
622   }
623   FPRINTF (stdout,
624            "%s\n",
625            address);
626 }
627
628
629 /**
630  * Accepting an incoming call
631  *
632  * @param args arguments given to the command
633  */
634 static void
635 do_status (const char *args)
636 {
637   struct CallList *cl;
638
639   switch (phone_state)
640   {
641   case PS_LOOKUP_EGO:
642     FPRINTF (stdout,
643              _("We are currently trying to locate the private key for the ego `%s'.\n"),
644              ego_name);
645     break;
646   case PS_LISTEN:
647     FPRINTF (stdout,
648              _("We are listening for incoming calls for ego `%s' on line %u.\n"),
649              ego_name,
650              line);
651     break;
652   case PS_ACCEPTED:
653     FPRINTF (stdout,
654              _("You are having a conversation with `%s'.\n"),
655              peer_name);
656     break;
657   case PS_ERROR:
658     FPRINTF (stdout,
659              _("We had an internal error setting up our phone line. You can still make calls.\n"));
660     break;
661   }
662   if (NULL != call)
663   {
664     switch (call_state)
665     {
666     case CS_RESOLVING:
667       FPRINTF (stdout,
668                _("We are trying to find the network address to call `%s'.\n"),
669                peer_name);
670       break;
671     case CS_RINGING:
672       FPRINTF (stdout,
673                _("We are calling `%s', his phone should be ringing.\n"),
674                peer_name);
675       break;
676     case CS_CONNECTED:
677       FPRINTF (stdout,
678                _("You are having a conversation with `%s'.\n"),
679                peer_name);
680       break;
681     case CS_SUSPENDED:
682       /* ok to accept incoming call right now */
683       break;
684     }
685   }
686   if ( (NULL != cl_head) &&
687        ( (cl_head != cl_active) ||
688          (cl_head != cl_tail) ) )
689   {
690     FPRINTF (stdout,
691              "%s",
692              _("Calls waiting:\n"));
693     for (cl = cl_head; NULL != cl; cl = cl->next)
694     {
695       if (cl == cl_active)
696         continue;
697       FPRINTF (stdout,
698                _("#%u: `%s'\n"),
699                cl->caller_num,
700                cl->caller_id);
701     }
702     FPRINTF (stdout,
703              "%s",
704              "\n");
705   }
706 }
707
708
709 /**
710  * Suspending a call
711  *
712  * @param args arguments given to the command
713  */
714 static void
715 do_suspend (const char *args)
716 {
717   if (NULL != call)
718   {
719     switch (call_state)
720     {
721     case CS_RESOLVING:
722     case CS_RINGING:
723     case CS_SUSPENDED:
724       FPRINTF (stderr,
725                "%s",
726                _("There is no call that could be suspended right now.\n"));
727       return;
728     case CS_CONNECTED:
729       call_state = CS_SUSPENDED;
730       GNUNET_CONVERSATION_call_suspend (call);
731       return;
732     }
733   }
734   switch (phone_state)
735   {
736   case PS_LOOKUP_EGO:
737   case PS_LISTEN:
738   case PS_ERROR:
739     FPRINTF (stderr,
740              "%s",
741              _("There is no call that could be suspended right now.\n"));
742     return;
743   case PS_ACCEPTED:
744     /* expected state, do rejection logic */
745     break;
746   }
747   GNUNET_assert (NULL != cl_active);
748   GNUNET_CONVERSATION_caller_suspend (cl_active->caller);
749   cl_active = NULL;
750   phone_state = PS_LISTEN;
751 }
752
753
754 /**
755  * Resuming a call
756  *
757  * @param args arguments given to the command
758  */
759 static void
760 do_resume (const char *args)
761 {
762   struct CallList *cl;
763   char buf[32];
764
765   if (NULL != call)
766   {
767     switch (call_state)
768     {
769     case CS_RESOLVING:
770     case CS_RINGING:
771     case CS_CONNECTED:
772       FPRINTF (stderr,
773                "%s",
774                _("There is no call that could be resumed right now.\n"));
775       return;
776     case CS_SUSPENDED:
777       call_state = CS_CONNECTED;
778       GNUNET_CONVERSATION_call_resume (call,
779                                        speaker,
780                                        mic);
781       return;
782     }
783   }
784   switch (phone_state)
785   {
786   case PS_LOOKUP_EGO:
787   case PS_ERROR:
788     FPRINTF (stderr,
789              "%s",
790              _("There is no call that could be resumed right now.\n"));
791     return;
792   case PS_LISTEN:
793     /* expected state, do resume logic */
794     break;
795   case PS_ACCEPTED:
796     FPRINTF (stderr,
797              _("Already talking with `%s', cannot resume a call right now.\n"),
798              peer_name);
799     return;
800   }
801   GNUNET_assert (NULL == cl_active);
802   cl = cl_head;
803   if (NULL == cl)
804   {
805     FPRINTF (stderr,
806              _("There is no incoming call to resume here!\n"));
807     return;
808   }
809   if ( (NULL != cl->next) || (NULL != args) )
810   {
811     for (cl = cl_head; NULL != cl; cl = cl->next)
812     {
813       GNUNET_snprintf (buf, sizeof (buf),
814                        "%u",
815                        cl->caller_num);
816       if (0 == strcmp (buf, args))
817         break;
818     }
819   }
820   if (NULL == cl)
821   {
822     FPRINTF (stderr,
823              _("There is no incoming call `%s' to resume right now!\n"),
824              args);
825     return;
826   }
827   cl_active = cl;
828   GNUNET_CONVERSATION_caller_resume (cl_active->caller,
829                                      speaker,
830                                      mic);
831   phone_state = PS_ACCEPTED;
832 }
833
834
835 /**
836  * Rejecting a call
837  *
838  * @param args arguments given to the command
839  */
840 static void
841 do_reject (const char *args)
842 {
843   struct CallList *cl;
844   char buf[32];
845
846   if (NULL != call)
847   {
848     GNUNET_CONVERSATION_call_stop (call);
849     call = NULL;
850     return;
851   }
852   switch (phone_state)
853   {
854   case PS_LOOKUP_EGO:
855   case PS_ERROR:
856     FPRINTF (stderr,
857              "%s",
858              _("There is no call that could be cancelled right now.\n"));
859     return;
860   case PS_LISTEN:
861     /* look for active incoming calls to refuse */
862     cl = cl_head;
863     if (NULL == cl)
864     {
865       FPRINTF (stderr,
866                _("There is no incoming call to refuse here!\n"));
867       return;
868     }
869     if ( (NULL != cl->next) || (NULL != args) )
870     {
871       for (cl = cl_head; NULL != cl; cl = cl->next)
872       {
873         GNUNET_snprintf (buf, sizeof (buf),
874                          "%u",
875                          cl->caller_num);
876         if (0 == strcmp (buf, args))
877           break;
878       }
879     }
880     if (NULL == cl)
881     {
882       FPRINTF (stderr,
883                _("There is no incoming call `%s' to refuse right now!\n"),
884                args);
885       return;
886     }
887     GNUNET_CONVERSATION_caller_hang_up (cl->caller);
888     GNUNET_CONTAINER_DLL_remove (cl_head,
889                                  cl_tail,
890                                  cl);
891     GNUNET_free (cl->caller_id);
892     GNUNET_free (cl);
893     break;
894   case PS_ACCEPTED:
895     /* expected state, do rejection logic */
896     GNUNET_assert (NULL != cl_active);
897     GNUNET_CONVERSATION_caller_hang_up (cl_active->caller);
898     cl_active = NULL;
899     phone_state = PS_LISTEN;
900     break;
901   }
902 }
903
904
905 /**
906  * List of supported commands.
907  */
908 static struct VoipCommand commands[] = {
909   {"/address", &do_address,
910    gettext_noop ("Use `/address' to find out which address this phone should have in GNS")},
911   {"/call", &do_call,
912    gettext_noop ("Use `/call USER.gnu' to call USER")},
913   {"/accept", &do_accept,
914    gettext_noop ("Use `/accept #NUM' to accept incoming call #NUM")},
915   {"/suspend", &do_suspend,
916    gettext_noop ("Use `/suspend' to suspend the active call")},
917   {"/resume", &do_resume,
918    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.")},
919   {"/cancel", &do_reject,
920    gettext_noop ("Use `/cancel' to reject or terminate a call")},
921   {"/status", &do_status,
922    gettext_noop ("Use `/status' to print status information")},
923   {"/quit", &do_quit,
924    gettext_noop ("Use `/quit' to terminate gnunet-conversation")},
925   {"/help", &do_help,
926    gettext_noop ("Use `/help command' to get help for a specific command")},
927   {"", &do_unknown,
928    NULL},
929   {NULL, NULL, NULL},
930 };
931
932
933 /**
934  * Action function to print help for the command shell.
935  *
936  * @param args arguments given to the command
937  */
938 static void
939 do_help (const char *args)
940 {
941   unsigned int i;
942
943   i = 0;
944   while ( (NULL != args) &&
945           (0 != strlen (args)) &&
946           (commands[i].Action != &do_help))
947   {
948     if (0 ==
949         strncasecmp (&args[1], &commands[i].command[1], strlen (args) - 1))
950     {
951       FPRINTF (stdout,
952                "%s\n",
953                gettext (commands[i].helptext));
954       return;
955     }
956     i++;
957   }
958   i = 0;
959   FPRINTF (stdout,
960            "%s",
961            "Available commands:\n");
962   while (commands[i].Action != &do_help)
963   {
964     FPRINTF (stdout,
965              "%s\n",
966              gettext (commands[i].command));
967     i++;
968   }
969   FPRINTF (stdout,
970            "%s",
971            "\n");
972   FPRINTF (stdout,
973            "%s\n",
974            gettext (commands[i].helptext));
975 }
976
977
978 /**
979  * Task run during shutdown.
980  *
981  * @param cls NULL
982  * @param tc unused
983  */
984 static void
985 do_stop_task (void *cls,
986               const struct GNUNET_SCHEDULER_TaskContext *tc)
987 {
988   if (NULL != call)
989   {
990     GNUNET_CONVERSATION_call_stop (call);
991     call = NULL;
992   }
993   if (NULL != phone)
994   {
995     GNUNET_CONVERSATION_phone_destroy (phone);
996     phone = NULL;
997   }
998   if (GNUNET_SCHEDULER_NO_TASK != handle_cmd_task)
999   {
1000     GNUNET_SCHEDULER_cancel (handle_cmd_task);
1001     handle_cmd_task = GNUNET_SCHEDULER_NO_TASK;
1002   }
1003   if (NULL != id)
1004   {
1005     GNUNET_IDENTITY_disconnect (id);
1006     id = NULL;
1007   }
1008   GNUNET_SPEAKER_destroy (speaker);
1009   speaker = NULL;
1010   GNUNET_MICROPHONE_destroy (mic);
1011   mic = NULL;
1012   GNUNET_free (ego_name);
1013   ego_name = NULL;
1014   GNUNET_CONFIGURATION_destroy (cfg);
1015   cfg = NULL;
1016   GNUNET_free_non_null (peer_name);
1017   phone_state = PS_ERROR;
1018 }
1019
1020
1021 /**
1022  * Task to handle commands from the terminal.
1023  *
1024  * @param cls NULL
1025  * @param tc scheduler context
1026  */
1027 static void
1028 handle_command (void *cls,
1029                 const struct GNUNET_SCHEDULER_TaskContext *tc)
1030 {
1031   char message[MAX_MESSAGE_LENGTH + 1];
1032   const char *ptr;
1033   size_t i;
1034
1035   handle_cmd_task =
1036     GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
1037                                     stdin_fh,
1038                                     &handle_command, NULL);
1039   /* read message from command line and handle it */
1040   memset (message, 0, MAX_MESSAGE_LENGTH + 1);
1041   if (NULL == fgets (message, MAX_MESSAGE_LENGTH, stdin))
1042     return;
1043   if (0 == strlen (message))
1044     return;
1045   if (message[strlen (message) - 1] == '\n')
1046     message[strlen (message) - 1] = '\0';
1047   if (0 == strlen (message))
1048     return;
1049   i = 0;
1050   while ((NULL != commands[i].command) &&
1051          (0 != strncasecmp (commands[i].command, message,
1052                             strlen (commands[i].command))))
1053     i++;
1054   ptr = &message[strlen (commands[i].command)];
1055   while (isspace ((int) *ptr))
1056     ptr++;
1057   if ('\0' == ptr)
1058     ptr = NULL;
1059   commands[i].Action (ptr);
1060 }
1061
1062
1063 /**
1064  * Function called by identity service with information about egos.
1065  *
1066  * @param cls NULL
1067  * @param ego ego handle
1068  * @param ctx unused
1069  * @param name name of the ego
1070  */
1071 static void
1072 identity_cb (void *cls,
1073              struct GNUNET_IDENTITY_Ego *ego,
1074              void **ctx,
1075              const char *name)
1076 {
1077   if (NULL == name)
1078     return;
1079   if (ego == caller_id)
1080   {
1081     if (verbose)
1082       FPRINTF (stdout,
1083                _("Name of our ego changed to `%s'\n"),
1084                name);
1085     GNUNET_free (ego_name);
1086     ego_name = GNUNET_strdup (name);
1087     return;
1088   }
1089   if (0 != strcmp (name,
1090                    ego_name))
1091     return;
1092   if (NULL == ego)
1093   {
1094     if (verbose)
1095       FPRINTF (stdout,
1096                _("Our ego `%s' was deleted!\n"),
1097                ego_name);
1098     caller_id = NULL;
1099     return;
1100   }
1101   caller_id = ego;
1102   GNUNET_CONFIGURATION_set_value_number (cfg,
1103                                          "CONVERSATION",
1104                                          "LINE",
1105                                          line);
1106   start_phone ();
1107 }
1108
1109
1110 /**
1111  * Main function that will be run by the scheduler.
1112  *
1113  * @param cls closure
1114  * @param args remaining command-line arguments
1115  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
1116  * @param c configuration
1117  */
1118 static void
1119 run (void *cls,
1120      char *const *args,
1121      const char *cfgfile,
1122      const struct GNUNET_CONFIGURATION_Handle *c)
1123 {
1124   cfg = GNUNET_CONFIGURATION_dup (c);
1125   speaker = GNUNET_SPEAKER_create_from_hardware (cfg);
1126   mic = GNUNET_MICROPHONE_create_from_hardware (cfg);
1127   if (NULL == ego_name)
1128   {
1129     FPRINTF (stderr,
1130              "%s",
1131              _("You must specify the NAME of an ego to use\n"));
1132     return;
1133   }
1134   id = GNUNET_IDENTITY_connect (cfg,
1135                                 &identity_cb,
1136                                 NULL);
1137   handle_cmd_task =
1138     GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_UI,
1139                                         &handle_command, NULL);
1140   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &do_stop_task,
1141                                 NULL);
1142 }
1143
1144
1145 /**
1146  * The main function to conversation.
1147  *
1148  * @param argc number of arguments from the command line
1149  * @param argv command line arguments
1150  * @return 0 ok, 1 on error
1151  */
1152 int
1153 main (int argc, char *const *argv)
1154 {
1155   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
1156     {'e', "ego", "NAME",
1157      gettext_noop ("sets the NAME of the ego to use for the phone (and name resolution)"),
1158      1, &GNUNET_GETOPT_set_string, &ego_name},
1159     {'p', "phone", "LINE",
1160       gettext_noop ("sets the LINE to use for the phone"),
1161      1, &GNUNET_GETOPT_set_uint, &line},
1162     GNUNET_GETOPT_OPTION_END
1163   };
1164   int flags;
1165   int ret;
1166
1167   flags = fcntl (0, F_GETFL, 0);
1168   flags |= O_NONBLOCK;
1169   fcntl (0, F_SETFL, flags);
1170   stdin_fh = GNUNET_DISK_get_handle_from_int_fd (0);
1171   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
1172     return 2;
1173   ret = GNUNET_PROGRAM_run (argc, argv,
1174                             "gnunet-conversation",
1175                             gettext_noop ("Enables having a conversation with other GNUnet users."),
1176                             options, &run, NULL);
1177   GNUNET_free ((void *) argv);
1178   return (GNUNET_OK == ret) ? 0 : 1;
1179 }
1180
1181 /* end of gnunet-conversation.c */