-fix build system issues
[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     FPRINTF (stdout,
356              _("Phone active on line %u.  Type `/help' for a list of available commands\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     FPRINTF (stdout,
378              _("Resolved address of `%s'. Now ringing other party.\n"),
379              peer_name);
380     call_state = CS_RINGING;
381     break;
382   case GNUNET_CONVERSATION_EC_CALL_PICKED_UP:
383     GNUNET_break (CS_RINGING == call_state);
384     FPRINTF (stdout,
385              _("Connection established to `%s'\n"),
386              peer_name);
387     call_state = CS_CONNECTED;
388     break;
389   case GNUNET_CONVERSATION_EC_CALL_GNS_FAIL:
390     GNUNET_break (CS_RESOLVING == call_state);
391     FPRINTF (stdout,
392              _("Failed to resolve `%s'\n"),
393              ego_name);
394     call = NULL;
395     break;
396   case GNUNET_CONVERSATION_EC_CALL_HUNG_UP:
397     FPRINTF (stdout,
398              "%s",
399              _("Call terminated\n"));
400     call = NULL;
401     break;
402   case GNUNET_CONVERSATION_EC_CALL_SUSPENDED:
403     GNUNET_break (CS_CONNECTED == call_state);
404     FPRINTF (stdout,
405              _("Connection to `%s' suspended (by other user)\n"),
406              peer_name);
407     break;
408   case GNUNET_CONVERSATION_EC_CALL_RESUMED:
409     GNUNET_break (CS_CONNECTED == call_state);
410     FPRINTF (stdout,
411              _("Connection to `%s' resumed (by other user)\n"),
412              peer_name);
413     break;
414   case GNUNET_CONVERSATION_EC_CALL_ERROR:
415     FPRINTF (stdout,
416              _("Error with the call, restarting it\n"));
417     call_state = CS_RESOLVING;
418     break;
419   }
420 }
421
422
423 /**
424  * Function declareation for executing a action
425  *
426  * @param arguments arguments given to the function
427  */
428 typedef void (*ActionFunction) (const char *arguments);
429
430
431 /**
432  * Structure which defines a command
433  */
434 struct VoipCommand
435 {
436   /**
437    * Command the user needs to enter.
438    */
439   const char *command;
440
441   /**
442    * Function to call on command.
443    */
444   ActionFunction Action;
445
446   /**
447    * Help text for the command.
448    */
449   const char *helptext;
450 };
451
452
453 /**
454  * Action function to print help for the command shell.
455  *
456  * @param args arguments given to the command
457  */
458 static void
459 do_help (const char *args);
460
461
462 /**
463  * Terminate the client
464  *
465  * @param args arguments given to the command
466  */
467 static void
468 do_quit (const char *args)
469 {
470   GNUNET_SCHEDULER_shutdown ();
471 }
472
473
474 /**
475  * Handler for unknown command.
476  *
477  * @param msg arguments given to the command
478  */
479 static void
480 do_unknown (const char *msg)
481 {
482   FPRINTF (stderr,
483            _("Unknown command `%s'\n"),
484            msg);
485 }
486
487
488 /**
489  * Initiating a new call
490  *
491  * @param arg arguments given to the command
492  */
493 static void
494 do_call (const char *arg)
495 {
496   if (NULL == caller_id)
497   {
498     FPRINTF (stderr,
499              _("Ego `%s' not available\n"),
500              ego_name);
501     return;
502   }
503   if (NULL != call)
504   {
505     FPRINTF (stderr,
506              _("You are calling someone else already, hang up first!\n"));
507     return;
508   }
509   switch (phone_state)
510   {
511   case PS_LOOKUP_EGO:
512     FPRINTF (stderr,
513              _("Ego `%s' not available\n"),
514              ego_name);
515     return;
516   case PS_LISTEN:
517     /* ok to call! */
518     break;
519   case PS_ACCEPTED:
520     FPRINTF (stderr,
521              _("You are answering call from `%s', hang up or suspend that call first!\n"),
522              peer_name);
523     return;
524   case PS_ERROR:
525     /* ok to call */
526     break;
527   }
528   GNUNET_free_non_null (peer_name);
529   if (NULL == arg)
530   {
531     FPRINTF (stderr,
532              _("Call recipient missing.\n"));
533     do_help ("/call");
534     return;
535   }
536   peer_name = GNUNET_strdup (arg);
537   call_state = CS_RESOLVING;
538   GNUNET_assert (NULL == call);
539   call = GNUNET_CONVERSATION_call_start (cfg,
540                                          caller_id,
541                                          arg,
542                                          speaker,
543                                          mic,
544                                          &call_event_handler, NULL);
545 }
546
547
548 /**
549  * Accepting an incoming call
550  *
551  * @param args arguments given to the command
552  */
553 static void
554 do_accept (const char *args)
555 {
556   struct CallList *cl;
557   char buf[32];
558
559   if ( (NULL != call) &&
560        (CS_SUSPENDED != call_state) )
561   {
562     FPRINTF (stderr,
563              _("You are calling someone else already, hang up first!\n"));
564     return;
565   }
566   switch (phone_state)
567   {
568   case PS_LOOKUP_EGO:
569     GNUNET_break (0);
570     break;
571   case PS_LISTEN:
572     /* this is the expected state */
573     break;
574   case PS_ACCEPTED:
575     FPRINTF (stderr,
576              _("You are answering call from `%s', hang up or suspend that call first!\n"),
577              peer_name);
578     return;
579   case PS_ERROR:
580     GNUNET_break (0);
581     break;
582   }
583   cl = cl_head;
584   if (NULL == cl)
585   {
586     FPRINTF (stderr,
587              _("There is no incoming call to accept here!\n"));
588     return;
589   }
590   if ( (NULL != cl->next) || (NULL != args) )
591   {
592     for (cl = cl_head; NULL != cl; cl = cl->next)
593     {
594       GNUNET_snprintf (buf, sizeof (buf),
595                        "%u",
596                        cl->caller_num);
597       if (0 == strcmp (buf, args))
598         break;
599     }
600   }
601   if (NULL == cl)
602   {
603     FPRINTF (stderr,
604              _("There is no incoming call `%s' to accept right now!\n"),
605              args);
606     return;
607   }
608   cl_active = cl;
609   GNUNET_free_non_null (peer_name);
610   peer_name = GNUNET_strdup (cl->caller_id);
611   phone_state = PS_ACCEPTED;
612   GNUNET_CONVERSATION_caller_pick_up (cl->caller,
613                                       &caller_event_handler,
614                                       cl,
615                                       speaker,
616                                       mic);
617 }
618
619
620 /**
621  * Print address information for this phone.
622  *
623  * @param args arguments given to the command
624  */
625 static void
626 do_address (const char *args)
627 {
628   if (NULL == address)
629   {
630     FPRINTF (stdout,
631              "%s",
632              _("We currently do not have an address.\n"));
633     return;
634   }
635   FPRINTF (stdout,
636            "%s\n",
637            address);
638 }
639
640
641 /**
642  * Accepting an incoming call
643  *
644  * @param args arguments given to the command
645  */
646 static void
647 do_status (const char *args)
648 {
649   struct CallList *cl;
650
651   switch (phone_state)
652   {
653   case PS_LOOKUP_EGO:
654     FPRINTF (stdout,
655              _("We are currently trying to locate the private key for the ego `%s'.\n"),
656              ego_name);
657     break;
658   case PS_LISTEN:
659     FPRINTF (stdout,
660              _("We are listening for incoming calls for ego `%s' on line %u.\n"),
661              ego_name,
662              line);
663     break;
664   case PS_ACCEPTED:
665     FPRINTF (stdout,
666              _("You are having a conversation with `%s'.\n"),
667              peer_name);
668     break;
669   case PS_ERROR:
670     FPRINTF (stdout,
671              _("We had an internal error setting up our phone line. You can still make calls.\n"));
672     break;
673   }
674   if (NULL != call)
675   {
676     switch (call_state)
677     {
678     case CS_RESOLVING:
679       FPRINTF (stdout,
680                _("We are trying to find the network address to call `%s'.\n"),
681                peer_name);
682       break;
683     case CS_RINGING:
684       FPRINTF (stdout,
685                _("We are calling `%s', his phone should be ringing.\n"),
686                peer_name);
687       break;
688     case CS_CONNECTED:
689       FPRINTF (stdout,
690                _("You are having a conversation with `%s'.\n"),
691                peer_name);
692       break;
693     case CS_SUSPENDED:
694       /* ok to accept incoming call right now */
695       break;
696     }
697   }
698   if ( (NULL != cl_head) &&
699        ( (cl_head != cl_active) ||
700          (cl_head != cl_tail) ) )
701   {
702     FPRINTF (stdout,
703              "%s",
704              _("Calls waiting:\n"));
705     for (cl = cl_head; NULL != cl; cl = cl->next)
706     {
707       if (cl == cl_active)
708         continue;
709       FPRINTF (stdout,
710                _("#%u: `%s'\n"),
711                cl->caller_num,
712                cl->caller_id);
713     }
714     FPRINTF (stdout,
715              "%s",
716              "\n");
717   }
718 }
719
720
721 /**
722  * Suspending a call
723  *
724  * @param args arguments given to the command
725  */
726 static void
727 do_suspend (const char *args)
728 {
729   if (NULL != call)
730   {
731     switch (call_state)
732     {
733     case CS_RESOLVING:
734     case CS_RINGING:
735     case CS_SUSPENDED:
736       FPRINTF (stderr,
737                "%s",
738                _("There is no call that could be suspended right now.\n"));
739       return;
740     case CS_CONNECTED:
741       call_state = CS_SUSPENDED;
742       GNUNET_CONVERSATION_call_suspend (call);
743       return;
744     }
745   }
746   switch (phone_state)
747   {
748   case PS_LOOKUP_EGO:
749   case PS_LISTEN:
750   case PS_ERROR:
751     FPRINTF (stderr,
752              "%s",
753              _("There is no call that could be suspended right now.\n"));
754     return;
755   case PS_ACCEPTED:
756     /* expected state, do rejection logic */
757     break;
758   }
759   GNUNET_assert (NULL != cl_active);
760   GNUNET_CONVERSATION_caller_suspend (cl_active->caller);
761   cl_active = NULL;
762   phone_state = PS_LISTEN;
763 }
764
765
766 /**
767  * Resuming a call
768  *
769  * @param args arguments given to the command
770  */
771 static void
772 do_resume (const char *args)
773 {
774   struct CallList *cl;
775   char buf[32];
776
777   if (NULL != call)
778   {
779     switch (call_state)
780     {
781     case CS_RESOLVING:
782     case CS_RINGING:
783     case CS_CONNECTED:
784       FPRINTF (stderr,
785                "%s",
786                _("There is no call that could be resumed right now.\n"));
787       return;
788     case CS_SUSPENDED:
789       call_state = CS_CONNECTED;
790       GNUNET_CONVERSATION_call_resume (call,
791                                        speaker,
792                                        mic);
793       return;
794     }
795   }
796   switch (phone_state)
797   {
798   case PS_LOOKUP_EGO:
799   case PS_ERROR:
800     FPRINTF (stderr,
801              "%s",
802              _("There is no call that could be resumed right now.\n"));
803     return;
804   case PS_LISTEN:
805     /* expected state, do resume logic */
806     break;
807   case PS_ACCEPTED:
808     FPRINTF (stderr,
809              _("Already talking with `%s', cannot resume a call right now.\n"),
810              peer_name);
811     return;
812   }
813   GNUNET_assert (NULL == cl_active);
814   cl = cl_head;
815   if (NULL == cl)
816   {
817     FPRINTF (stderr,
818              _("There is no incoming call to resume here!\n"));
819     return;
820   }
821   if ( (NULL != cl->next) || (NULL != args) )
822   {
823     for (cl = cl_head; NULL != cl; cl = cl->next)
824     {
825       GNUNET_snprintf (buf, sizeof (buf),
826                        "%u",
827                        cl->caller_num);
828       if (0 == strcmp (buf, args))
829         break;
830     }
831   }
832   if (NULL == cl)
833   {
834     FPRINTF (stderr,
835              _("There is no incoming call `%s' to resume right now!\n"),
836              args);
837     return;
838   }
839   cl_active = cl;
840   GNUNET_CONVERSATION_caller_resume (cl_active->caller,
841                                      speaker,
842                                      mic);
843   phone_state = PS_ACCEPTED;
844 }
845
846
847 /**
848  * Rejecting a call
849  *
850  * @param args arguments given to the command
851  */
852 static void
853 do_reject (const char *args)
854 {
855   struct CallList *cl;
856   char buf[32];
857
858   if (NULL != call)
859   {
860     GNUNET_CONVERSATION_call_stop (call);
861     call = NULL;
862     return;
863   }
864   switch (phone_state)
865   {
866   case PS_LOOKUP_EGO:
867   case PS_ERROR:
868     FPRINTF (stderr,
869              "%s",
870              _("There is no call that could be cancelled right now.\n"));
871     return;
872   case PS_LISTEN:
873     /* look for active incoming calls to refuse */
874     cl = cl_head;
875     if (NULL == cl)
876     {
877       FPRINTF (stderr,
878                _("There is no incoming call to refuse here!\n"));
879       return;
880     }
881     if ( (NULL != cl->next) || (NULL != args) )
882     {
883       for (cl = cl_head; NULL != cl; cl = cl->next)
884       {
885         GNUNET_snprintf (buf, sizeof (buf),
886                          "%u",
887                          cl->caller_num);
888         if (0 == strcmp (buf, args))
889           break;
890       }
891     }
892     if (NULL == cl)
893     {
894       FPRINTF (stderr,
895                _("There is no incoming call `%s' to refuse right now!\n"),
896                args);
897       return;
898     }
899     GNUNET_CONVERSATION_caller_hang_up (cl->caller);
900     GNUNET_CONTAINER_DLL_remove (cl_head,
901                                  cl_tail,
902                                  cl);
903     GNUNET_free (cl->caller_id);
904     GNUNET_free (cl);
905     break;
906   case PS_ACCEPTED:
907     /* expected state, do rejection logic */
908     GNUNET_assert (NULL != cl_active);
909     GNUNET_CONVERSATION_caller_hang_up (cl_active->caller);
910     cl_active = NULL;
911     phone_state = PS_LISTEN;
912     break;
913   }
914 }
915
916
917 /**
918  * List of supported commands.
919  */
920 static struct VoipCommand commands[] = {
921   {"/address", &do_address,
922    gettext_noop ("Use `/address' to find out which address this phone should have in GNS")},
923   {"/call", &do_call,
924    gettext_noop ("Use `/call USER.gnu' to call USER")},
925   {"/accept", &do_accept,
926    gettext_noop ("Use `/accept #NUM' to accept incoming call #NUM")},
927   {"/suspend", &do_suspend,
928    gettext_noop ("Use `/suspend' to suspend the active call")},
929   {"/resume", &do_resume,
930    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.")},
931   {"/cancel", &do_reject,
932    gettext_noop ("Use `/cancel' to reject or terminate a call")},
933   {"/status", &do_status,
934    gettext_noop ("Use `/status' to print status information")},
935   {"/quit", &do_quit,
936    gettext_noop ("Use `/quit' to terminate gnunet-conversation")},
937   {"/help", &do_help,
938    gettext_noop ("Use `/help command' to get help for a specific command")},
939   {"", &do_unknown,
940    NULL},
941   {NULL, NULL, NULL},
942 };
943
944
945 /**
946  * Action function to print help for the command shell.
947  *
948  * @param args arguments given to the command
949  */
950 static void
951 do_help (const char *args)
952 {
953   unsigned int i;
954
955   i = 0;
956   while ( (NULL != args) &&
957           (0 != strlen (args)) &&
958           (commands[i].Action != &do_help))
959   {
960     if (0 ==
961         strncasecmp (&args[1], &commands[i].command[1], strlen (args) - 1))
962     {
963       FPRINTF (stdout,
964                "%s\n",
965                gettext (commands[i].helptext));
966       return;
967     }
968     i++;
969   }
970   i = 0;
971   FPRINTF (stdout,
972            "%s",
973            "Available commands:\n");
974   while (commands[i].Action != &do_help)
975   {
976     FPRINTF (stdout,
977              "%s\n",
978              gettext (commands[i].command));
979     i++;
980   }
981   FPRINTF (stdout,
982            "%s",
983            "\n");
984   FPRINTF (stdout,
985            "%s\n",
986            gettext (commands[i].helptext));
987 }
988
989
990 /**
991  * Task run during shutdown.
992  *
993  * @param cls NULL
994  * @param tc unused
995  */
996 static void
997 do_stop_task (void *cls,
998               const struct GNUNET_SCHEDULER_TaskContext *tc)
999 {
1000   if (NULL != call)
1001   {
1002     GNUNET_CONVERSATION_call_stop (call);
1003     call = NULL;
1004   }
1005   if (NULL != phone)
1006   {
1007     GNUNET_CONVERSATION_phone_destroy (phone);
1008     phone = NULL;
1009   }
1010   if (GNUNET_SCHEDULER_NO_TASK != handle_cmd_task)
1011   {
1012     GNUNET_SCHEDULER_cancel (handle_cmd_task);
1013     handle_cmd_task = GNUNET_SCHEDULER_NO_TASK;
1014   }
1015   if (NULL != id)
1016   {
1017     GNUNET_IDENTITY_disconnect (id);
1018     id = NULL;
1019   }
1020   GNUNET_SPEAKER_destroy (speaker);
1021   speaker = NULL;
1022   GNUNET_MICROPHONE_destroy (mic);
1023   mic = NULL;
1024   GNUNET_free (ego_name);
1025   ego_name = NULL;
1026   GNUNET_free_non_null (peer_name);
1027   phone_state = PS_ERROR;
1028 }
1029
1030
1031 /**
1032  * Task to handle commands from the terminal.
1033  *
1034  * @param cls NULL
1035  * @param tc scheduler context
1036  */
1037 static void
1038 handle_command (void *cls,
1039                 const struct GNUNET_SCHEDULER_TaskContext *tc)
1040 {
1041   char message[MAX_MESSAGE_LENGTH + 1];
1042   const char *ptr;
1043   size_t i;
1044
1045   handle_cmd_task =
1046     GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
1047                                     stdin_fh,
1048                                     &handle_command, NULL);
1049   /* read message from command line and handle it */
1050   memset (message, 0, MAX_MESSAGE_LENGTH + 1);
1051   if (NULL == fgets (message, MAX_MESSAGE_LENGTH, stdin))
1052     return;
1053   if (0 == strlen (message))
1054     return;
1055   if (message[strlen (message) - 1] == '\n')
1056     message[strlen (message) - 1] = '\0';
1057   if (0 == strlen (message))
1058     return;
1059   i = 0;
1060   while ((NULL != commands[i].command) &&
1061          (0 != strncasecmp (commands[i].command, message,
1062                             strlen (commands[i].command))))
1063     i++;
1064   ptr = &message[strlen (commands[i].command)];
1065   while (isspace ((int) *ptr))
1066     ptr++;
1067   if ('\0' == *ptr)
1068     ptr = NULL;
1069   commands[i].Action (ptr);
1070 }
1071
1072
1073 /**
1074  * Function called by identity service with information about egos.
1075  *
1076  * @param cls NULL
1077  * @param ego ego handle
1078  * @param ctx unused
1079  * @param name name of the ego
1080  */
1081 static void
1082 identity_cb (void *cls,
1083              struct GNUNET_IDENTITY_Ego *ego,
1084              void **ctx,
1085              const char *name)
1086 {
1087   if (NULL == name)
1088     return;
1089   if (ego == caller_id)
1090   {
1091     if (verbose)
1092       FPRINTF (stdout,
1093                _("Name of our ego changed to `%s'\n"),
1094                name);
1095     GNUNET_free (ego_name);
1096     ego_name = GNUNET_strdup (name);
1097     return;
1098   }
1099   if (0 != strcmp (name,
1100                    ego_name))
1101     return;
1102   if (NULL == ego)
1103   {
1104     if (verbose)
1105       FPRINTF (stdout,
1106                _("Our ego `%s' was deleted!\n"),
1107                ego_name);
1108     caller_id = NULL;
1109     return;
1110   }
1111   caller_id = ego;
1112   GNUNET_CONFIGURATION_set_value_number (cfg,
1113                                          "CONVERSATION",
1114                                          "LINE",
1115                                          line);
1116   start_phone ();
1117 }
1118
1119
1120 /**
1121  * Main function that will be run by the scheduler.
1122  *
1123  * @param cls closure
1124  * @param args remaining command-line arguments
1125  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
1126  * @param c configuration
1127  */
1128 static void
1129 run (void *cls,
1130      char *const *args,
1131      const char *cfgfile,
1132      const struct GNUNET_CONFIGURATION_Handle *c)
1133 {
1134   cfg = GNUNET_CONFIGURATION_dup (c);
1135   speaker = GNUNET_SPEAKER_create_from_hardware (cfg);
1136   mic = GNUNET_MICROPHONE_create_from_hardware (cfg);
1137   if (NULL == ego_name)
1138   {
1139     FPRINTF (stderr,
1140              "%s",
1141              _("You must specify the NAME of an ego to use\n"));
1142     return;
1143   }
1144   id = GNUNET_IDENTITY_connect (cfg,
1145                                 &identity_cb,
1146                                 NULL);
1147   handle_cmd_task =
1148     GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_UI,
1149                                         &handle_command, NULL);
1150   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &do_stop_task,
1151                                 NULL);
1152 }
1153
1154
1155 /**
1156  * The main function to conversation.
1157  *
1158  * @param argc number of arguments from the command line
1159  * @param argv command line arguments
1160  * @return 0 ok, 1 on error
1161  */
1162 int
1163 main (int argc, char *const *argv)
1164 {
1165   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
1166     {'e', "ego", "NAME",
1167      gettext_noop ("sets the NAME of the ego to use for the phone (and name resolution)"),
1168      1, &GNUNET_GETOPT_set_string, &ego_name},
1169     {'p', "phone", "LINE",
1170       gettext_noop ("sets the LINE to use for the phone"),
1171      1, &GNUNET_GETOPT_set_uint, &line},
1172     GNUNET_GETOPT_OPTION_END
1173   };
1174   int flags;
1175   int ret;
1176
1177   flags = fcntl (0, F_GETFL, 0);
1178   flags |= O_NONBLOCK;
1179   fcntl (0, F_SETFL, flags);
1180   stdin_fh = GNUNET_DISK_get_handle_from_int_fd (0);
1181   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
1182     return 2;
1183   ret = GNUNET_PROGRAM_run (argc, argv,
1184                             "gnunet-conversation",
1185                             gettext_noop ("Enables having a conversation with other GNUnet users."),
1186                             options, &run, NULL);
1187   GNUNET_free ((void *) argv);
1188   if (NULL != cfg)
1189   {
1190     GNUNET_CONFIGURATION_destroy (cfg);
1191     cfg = NULL;
1192   }
1193   return (GNUNET_OK == ret) ? 0 : 1;
1194 }
1195
1196 /* end of gnunet-conversation.c */