-fix mesh tunnel destruction
[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_conversation_service.h"
30 #include "gnunet_namestore_service.h"
31
32
33 /**
34  * Maximum length allowed for the command line input.
35  */
36 #define MAX_MESSAGE_LENGTH 1024
37
38
39 /**
40  * Possible states of the program.
41  */
42 enum ConversationState
43 {
44   /**
45    * We're waiting for our own idenitty.
46    */
47   CS_LOOKUP_EGO,
48
49   /**
50    * We're listening for calls
51    */
52   CS_LISTEN,
53
54   /**
55    * Our phone is ringing.
56    */
57   CS_RING,
58
59   /**
60    * We accepted an incoming phone call.
61    */
62   CS_ACCEPTED,
63
64   /**
65    * We are looking up some other participant.
66    */
67   CS_RESOLVING,
68
69   /**
70    * We are now ringing the other participant.
71    */
72   CS_RINGING,
73
74   /**
75    * The other party accepted our call and we are now connected.
76    */
77   CS_CONNECTED,
78
79   /**
80    * Internal error
81    */
82   CS_ERROR
83
84 };
85
86
87 /**
88  * Phone handle
89  */
90 static struct GNUNET_CONVERSATION_Phone *phone;
91
92 /**
93  * Call handle
94  */
95 static struct GNUNET_CONVERSATION_Call *call;
96
97 /**
98  * Desired phone line.
99  */
100 static unsigned int line;
101
102 /**
103  * Task which handles the commands
104  */
105 static GNUNET_SCHEDULER_TaskIdentifier handle_cmd_task;
106
107 /**
108  * Our speaker.
109  */
110 static struct GNUNET_SPEAKER_Handle *speaker;
111
112 /**
113  * Our microphone.
114  */
115 static struct GNUNET_MICROPHONE_Handle *mic;
116
117 /**
118  * Our configuration.
119  */
120 static struct GNUNET_CONFIGURATION_Handle *cfg;
121
122 /**
123  * Our ego.
124  */
125 static struct GNUNET_IDENTITY_Ego *caller_id;
126
127 /**
128  * Handle to identity service.
129  */
130 static struct GNUNET_IDENTITY_Handle *id;
131
132 /**
133  * Name of our ego.
134  */
135 static char *ego_name;
136
137 /**
138  * Name of conversation partner (if any).
139  */
140 static char *peer_name;
141
142 /**
143  * File handle for stdin.
144  */
145 static struct GNUNET_DISK_FileHandle *stdin_fh;
146
147 /**
148  * Our current state.
149  */
150 static enum ConversationState state;
151
152 /**
153  * GNS address for this phone.
154  */
155 static char *address;
156
157 /**
158  * Be verbose.
159  */
160 static int verbose;
161
162
163 /**
164  * Function called with an event emitted by a phone.
165  *
166  * @param cls closure
167  * @param code type of the event on the phone
168  * @param ... additional information, depends on @a code
169  */
170 static void
171 phone_event_handler (void *cls,
172                      enum GNUNET_CONVERSATION_EventCode code,
173                      ...)
174 {
175   va_list va;
176   
177   va_start (va, code);
178   switch (code)
179   {
180   case GNUNET_CONVERSATION_EC_RING:
181     GNUNET_break (CS_LISTEN == state);
182     GNUNET_free_non_null (peer_name);
183     peer_name = GNUNET_strdup (va_arg (va, const char *));
184     FPRINTF (stdout,
185              _("Incoming call from `%s'.\nPlease /accept or /cancel the call.\n"),
186              peer_name);
187     state = CS_RING;
188     break;
189   case GNUNET_CONVERSATION_EC_RINGING:
190     GNUNET_break (0);
191     break;
192   case GNUNET_CONVERSATION_EC_READY:
193     GNUNET_break (0);
194     break;
195   case GNUNET_CONVERSATION_EC_GNS_FAIL:
196     GNUNET_break (0);
197     break;
198   case GNUNET_CONVERSATION_EC_BUSY:
199     GNUNET_break (0);
200     break;
201   case GNUNET_CONVERSATION_EC_TERMINATED:
202     GNUNET_break ( (CS_RING == state) ||
203                    (CS_ACCEPTED == state) );
204     FPRINTF (stdout,
205              _("Call terminated: %s\n"),
206              va_arg (va, const char *));
207     state = CS_LISTEN;
208     break;
209   }
210   va_end (va);
211 }
212
213
214 /**
215  * Start our phone.
216  */
217 static void
218 start_phone ()
219 {
220   struct GNUNET_NAMESTORE_RecordData rd;
221
222   if (NULL == caller_id)
223   {
224     FPRINTF (stderr,
225              _("Ego `%s' no longer available, phone is now down.\n"),
226              ego_name);
227     state = CS_LOOKUP_EGO;
228     return;
229   }
230   phone = GNUNET_CONVERSATION_phone_create (cfg,
231                                             caller_id,
232                                             &phone_event_handler, NULL);
233   /* FIXME: get record and print full GNS record info later here... */
234   if (NULL == phone)
235   {
236     FPRINTF (stderr,
237              "%s",
238              _("Failed to setup phone (internal error)\n"));
239     state = CS_ERROR;
240   }
241   else
242   {
243     GNUNET_CONVERSATION_phone_get_record (phone,
244                                           &rd);
245     GNUNET_free_non_null (address);
246     address = GNUNET_NAMESTORE_value_to_string (rd.record_type,
247                                                 rd.data,
248                                                 rd.data_size);
249     if (verbose)
250       FPRINTF (stdout,
251                _("Phone active on line %u\n"),
252                (unsigned int) line);
253     state = CS_LISTEN;
254   }
255 }
256
257
258 /**
259  * Function called with an event emitted by a phone.
260  *
261  * @param cls closure
262  * @param code type of the event on the phone
263  * @param ... additional information, depends on @a code
264  */
265 static void
266 call_event_handler (void *cls,
267                     enum GNUNET_CONVERSATION_EventCode code,
268                     ...)
269 {
270   va_list va;
271   
272   va_start (va, code);
273   switch (code)
274   {
275   case GNUNET_CONVERSATION_EC_RING:
276     GNUNET_break (0);
277     break;
278   case GNUNET_CONVERSATION_EC_RINGING:
279     GNUNET_break (CS_RESOLVING == state);
280     if (verbose)
281       FPRINTF (stdout,
282                "%s",
283                _("Resolved address. Now ringing other party.\n"));
284     state = CS_RINGING;
285     break;
286   case GNUNET_CONVERSATION_EC_READY:
287     GNUNET_break (CS_RINGING == state);
288     FPRINTF (stdout,
289              _("Connection established to `%s': %s\n"),
290              peer_name,
291              va_arg (va, const char *));
292     state = CS_CONNECTED;
293     break;
294   case GNUNET_CONVERSATION_EC_GNS_FAIL:
295     GNUNET_break (CS_RESOLVING == state);
296     FPRINTF (stdout,
297              _("Failed to resolve `%s'\n"),
298              ego_name);
299     GNUNET_CONVERSATION_call_stop (call, NULL);
300     call = NULL;
301     start_phone ();
302     break;
303   case GNUNET_CONVERSATION_EC_BUSY:
304     GNUNET_break (CS_RINGING == state);
305     FPRINTF (stdout,
306              "%s",
307              _("Line busy\n"));
308     GNUNET_CONVERSATION_call_stop (call, NULL);
309     call = NULL;
310     start_phone ();
311     break;
312   case GNUNET_CONVERSATION_EC_TERMINATED:
313     GNUNET_break ( (CS_RINGING == state) ||
314                    (CS_CONNECTED == state) );
315     FPRINTF (stdout,
316              _("Call terminated: %s\n"),
317              va_arg (va, const char *));
318     GNUNET_CONVERSATION_call_stop (call, NULL);
319     call = NULL;
320     start_phone ();
321     break;
322   }
323   va_end (va);
324 }
325
326
327 /**
328  * Function declareation for executing a action
329  *
330  * @param arguments arguments given to the function
331  */
332 typedef void (*ActionFunction) (const char *arguments);
333
334
335 /**
336  * Structure which defines a command
337  */
338 struct VoipCommand
339 {
340   /**
341    * Command the user needs to enter.
342    */
343   const char *command;
344   
345   /**
346    * Function to call on command.
347    */
348   ActionFunction Action;
349
350   /**
351    * Help text for the command.
352    */
353   const char *helptext;
354 };
355
356
357 /**
358  * Action function to print help for the command shell.
359  *
360  * @param arguments arguments given to the command
361  */
362 static void
363 do_help (const char *args);
364
365
366 /**
367  * Terminate the client
368  *
369  * @param args arguments given to the command
370  */
371 static void
372 do_quit (const char *args)
373 {
374   GNUNET_SCHEDULER_shutdown ();
375 }
376
377
378 /**
379  * Handler for unknown command.
380  *
381  * @param args arguments given to the command
382  */
383 static void
384 do_unknown (const char *msg)
385 {
386   FPRINTF (stderr, 
387            _("Unknown command `%s'\n"), 
388            msg);
389 }
390
391
392 /**
393  * Initiating a new call
394  *
395  * @param args arguments given to the command
396  */
397 static void
398 do_call (const char *arg)
399 {
400   if (NULL == caller_id)
401   {
402     FPRINTF (stderr,
403              _("Ego `%s' not available\n"),
404              ego_name);
405     return;
406   }
407   switch (state)
408   {
409   case CS_LOOKUP_EGO: 
410     FPRINTF (stderr,
411              _("Ego `%s' not available\n"),
412              ego_name);
413     return;
414   case CS_LISTEN:
415     /* ok to call! */
416     break;
417   case CS_RING:
418     FPRINTF (stdout,
419              _("Hanging up on incoming phone call from `%s' to call `%s'.\n"),
420              peer_name,
421              arg);
422     GNUNET_CONVERSATION_phone_hang_up (phone, NULL);
423     break;
424   case CS_ACCEPTED:
425     FPRINTF (stderr,
426              _("You are already in a conversation with `%s', refusing to call `%s'.\n"),
427              peer_name,
428              arg);
429     return;
430   case CS_RESOLVING:
431   case CS_RINGING:
432     FPRINTF (stderr,
433              _("Aborting call to `%s'\n"),
434              peer_name);
435     GNUNET_CONVERSATION_call_stop (call, NULL);
436     call = NULL;
437     break;
438   case CS_CONNECTED:
439     FPRINTF (stderr,
440              _("You are already in a conversation with `%s', refusing to call `%s'.\n"),
441              peer_name,
442              arg);
443     return;
444   case CS_ERROR:
445     /* ok to call */
446     break;
447   }
448   GNUNET_assert (NULL == call);
449   if (NULL != phone)
450   {
451     GNUNET_CONVERSATION_phone_destroy (phone);
452     phone = NULL;
453   }
454   GNUNET_free_non_null (peer_name);
455   peer_name = GNUNET_strdup (arg);
456   call = GNUNET_CONVERSATION_call_start (cfg,
457                                          caller_id,
458                                          arg,
459                                          speaker,
460                                          mic,
461                                          &call_event_handler, NULL);
462   state = CS_RESOLVING;
463 }
464
465
466 /**
467  * Accepting an incoming call
468  *
469  * @param args arguments given to the command
470  */
471 static void
472 do_accept (const char *args)
473 {
474   switch (state)
475   {
476   case CS_LOOKUP_EGO:     
477   case CS_LISTEN:
478   case CS_ERROR:
479     FPRINTF (stderr,
480              _("There is no incoming call to be accepted!\n"));
481     return;
482   case CS_RING:
483     /* this is the expected state */
484     break;
485   case CS_ACCEPTED:
486     FPRINTF (stderr,
487              _("You are already in a conversation with `%s'.\n"),
488              peer_name);
489     return;
490   case CS_RESOLVING:
491   case CS_RINGING:
492     FPRINTF (stderr,
493              _("You are trying to call `%s', cannot accept incoming calls right now.\n"),
494              peer_name);
495     return;
496   case CS_CONNECTED:
497     FPRINTF (stderr,
498              _("You are already in a conversation with `%s'.\n"),
499              peer_name);
500     return;
501   }
502   GNUNET_assert (NULL != phone);
503   GNUNET_CONVERSATION_phone_pick_up (phone, 
504                                      args,
505                                      speaker,
506                                      mic);
507   state = CS_ACCEPTED;
508 }
509
510
511 /**
512  * Print address information for this phone.
513  *
514  * @param args arguments given to the command
515  */
516 static void
517 do_address (const char *args)
518 {
519   if (NULL == address)
520   {
521     FPRINTF (stdout,
522              "%s",
523              _("We currently do not have an address.\n"));
524     return;
525   }
526   FPRINTF (stdout,
527            "%s\n",
528            address);
529 }
530
531
532 /**
533  * Accepting an incoming call
534  *
535  * @param args arguments given to the command
536  */
537 static void
538 do_status (const char *args)
539 {
540   switch (state)
541   {
542   case CS_LOOKUP_EGO: 
543     FPRINTF (stdout,
544              _("We are currently trying to locate the private key for the ego `%s'.\n"),
545              ego_name);
546     break;
547   case CS_LISTEN:
548     FPRINTF (stdout,
549              _("We are listening for incoming calls for ego `%s' on line %u.\n"),
550              ego_name,
551              line);
552     break;
553   case CS_RING:
554     FPRINTF (stdout,
555              _("The phone is rining. `%s' is trying to call us.\n"),
556              peer_name);
557     break;
558   case CS_ACCEPTED:
559   case CS_CONNECTED:
560     FPRINTF (stdout,
561              _("You are having a conversation with `%s'.\n"),
562              peer_name);
563     break;
564   case CS_RESOLVING:
565     FPRINTF (stdout,
566              _("We are trying to find the network address to call `%s'.\n"),
567              peer_name);
568     break;
569   case CS_RINGING:
570     FPRINTF (stdout,
571              _("We are calling `%s', his phone should be ringing.\n"),
572              peer_name);
573     break;
574   case CS_ERROR:
575     FPRINTF (stdout,
576              _("We had an internal error setting up our phone line. You can still make calls.\n"));
577     break;
578   }
579 }
580
581
582 /**
583  * Rejecting a call
584  *
585  * @param args arguments given to the command
586  */
587 static void
588 do_reject (const char *args)
589 {
590   switch (state)
591   {
592   case CS_LOOKUP_EGO: 
593   case CS_LISTEN:
594   case CS_ERROR:
595     FPRINTF (stderr,
596              "%s",
597              _("There is no call that could be cancelled right now.\n"));
598     return;
599   case CS_RING:
600   case CS_ACCEPTED:
601   case CS_RESOLVING:
602   case CS_RINGING:
603   case CS_CONNECTED:
604     /* expected state, do rejection logic */
605     break;
606   }
607   if (NULL == call)
608   {
609     GNUNET_assert (NULL != phone);
610     GNUNET_CONVERSATION_phone_hang_up (phone, 
611                                        args);
612     state = CS_LISTEN;
613   }
614   else
615   {
616     GNUNET_CONVERSATION_call_stop (call, args);
617     call = NULL;
618     start_phone ();
619   }
620 }
621
622
623 /**
624  * List of supported commands.
625  */
626 static struct VoipCommand commands[] = {
627   {"/address", &do_address, 
628    gettext_noop ("Use `/address' to find out which address this phone should have in GNS")},
629   {"/call", &do_call, 
630    gettext_noop ("Use `/call USER.gnu' to call USER")},
631   {"/accept", &do_accept,
632    gettext_noop ("Use `/accept MESSAGE' to accept an incoming call")},
633   {"/cancel", &do_reject,
634    gettext_noop ("Use `/cancel MESSAGE' to reject or terminate a call")},
635   {"/status", &do_status,
636    gettext_noop ("Use `/status' to print status information")},
637   {"/quit", &do_quit, 
638    gettext_noop ("Use `/quit' to terminate gnunet-conversation")},
639   {"/help", &do_help,
640    gettext_noop ("Use `/help command' to get help for a specific command")},
641   {"", &do_unknown, 
642    NULL},
643   {NULL, NULL, NULL},
644 };
645
646
647 /**
648  * Action function to print help for the command shell.
649  *
650  * @param arguments arguments given to the command
651  */
652 static void
653 do_help (const char *args)
654 {
655   unsigned int i;
656   
657   i = 0; 
658   while ( (NULL != args) &&
659           (0 != strlen (args)) &&
660           (commands[i].Action != &do_help))
661   {
662     if (0 ==
663         strncasecmp (&args[1], &commands[i].command[1], strlen (args) - 1))
664     {
665       FPRINTF (stdout, 
666                "%s\n",
667                gettext (commands[i].helptext));
668       return;
669     }
670     i++;
671   }
672   i = 0;
673   FPRINTF (stdout, 
674            "%s", 
675            "Available commands:\n");
676   while (commands[i].Action != &do_help)
677   {
678     FPRINTF (stdout, 
679              "%s\n", 
680              gettext (commands[i].command));
681     i++;
682   }
683   FPRINTF (stdout,
684            "%s",
685            "\n");
686   FPRINTF (stdout,
687            "%s\n",
688            gettext (commands[i].helptext));
689 }
690
691
692 /**
693  * Task run during shutdown.
694  *
695  * @param cls NULL
696  * @param tc unused
697  */
698 static void
699 do_stop_task (void *cls,
700               const struct GNUNET_SCHEDULER_TaskContext *tc)
701 {
702   if (NULL != call)
703   {
704     GNUNET_CONVERSATION_call_stop (call, NULL);
705     call = NULL;
706   }
707   if (NULL != phone)
708   {
709     GNUNET_CONVERSATION_phone_destroy (phone);
710     phone = NULL;
711   }
712   if (GNUNET_SCHEDULER_NO_TASK != handle_cmd_task)
713   {
714     GNUNET_SCHEDULER_cancel (handle_cmd_task);
715     handle_cmd_task = GNUNET_SCHEDULER_NO_TASK;
716   } 
717   if (NULL != id)
718   {
719     GNUNET_IDENTITY_disconnect (id);
720     id = NULL;
721   }
722   GNUNET_SPEAKER_destroy (speaker);
723   speaker = NULL;
724   GNUNET_MICROPHONE_destroy (mic);
725   mic = NULL;
726   GNUNET_free (ego_name);
727   ego_name = NULL;
728   GNUNET_CONFIGURATION_destroy (cfg);
729   cfg = NULL;
730   GNUNET_free_non_null (peer_name);
731   state = CS_ERROR;
732 }
733
734
735 /**
736  * Task to handle commands from the terminal.
737  *
738  * @param cls NULL
739  * @param tc scheduler context
740  */
741 static void
742 handle_command (void *cls,
743                 const struct GNUNET_SCHEDULER_TaskContext *tc)
744 {
745   char message[MAX_MESSAGE_LENGTH + 1];
746   const char *ptr;
747   size_t i;
748
749   handle_cmd_task =
750     GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
751                                     stdin_fh,
752                                     &handle_command, NULL);
753   /* read message from command line and handle it */
754   memset (message, 0, MAX_MESSAGE_LENGTH + 1);
755   if (NULL == fgets (message, MAX_MESSAGE_LENGTH, stdin))
756     return;
757   if (0 == strlen (message))
758     return;
759   if (message[strlen (message) - 1] == '\n')
760     message[strlen (message) - 1] = '\0';
761   if (0 == strlen (message))
762     return;
763   i = 0;
764   while ((NULL != commands[i].command) &&
765          (0 != strncasecmp (commands[i].command, message,
766                             strlen (commands[i].command))))
767     i++;
768   ptr = &message[strlen (commands[i].command)];
769   while (isspace ((int) *ptr))
770     ptr++;
771   commands[i].Action (ptr);
772 }
773
774
775 /**
776  * Function called by identity service with information about egos.
777  *
778  * @param cls NULL
779  * @param ego ego handle
780  * @param ctx unused
781  * @param name name of the ego
782  */
783 static void
784 identity_cb (void *cls,
785              struct GNUNET_IDENTITY_Ego *ego,
786              void **ctx,
787              const char *name)
788 {
789   if (NULL == name)
790     return;
791   if (ego == caller_id)
792   {
793     if (verbose)
794       FPRINTF (stdout,
795                _("Name of our ego changed to `%s'\n"),
796                name);
797     GNUNET_free (ego_name);
798     ego_name = GNUNET_strdup (name);
799     return;
800   }
801   if (0 != strcmp (name,
802                    ego_name))
803     return;
804   if (NULL == ego)
805   {    
806     if (verbose)
807       FPRINTF (stdout,
808                _("Our ego `%s' was deleted!\n"),
809                ego_name);
810     caller_id = NULL;
811     return;
812   }
813   caller_id = ego;
814   GNUNET_CONFIGURATION_set_value_number (cfg,
815                                          "CONVERSATION",
816                                          "LINE",
817                                          line);
818   start_phone ();
819 }
820
821
822 /**
823  * Main function that will be run by the scheduler.
824  *
825  * @param cls closure
826  * @param args remaining command-line arguments
827  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
828  * @param c configuration
829  */
830 static void
831 run (void *cls,
832      char *const *args, 
833      const char *cfgfile,
834      const struct GNUNET_CONFIGURATION_Handle *c)
835 {
836   cfg = GNUNET_CONFIGURATION_dup (c);
837   speaker = GNUNET_SPEAKER_create_from_hardware (cfg);
838   mic = GNUNET_MICROPHONE_create_from_hardware (cfg);
839   if (NULL == ego_name)
840   {
841     FPRINTF (stderr,
842              "%s",
843              _("You must specify the NAME of an ego to use\n"));
844     return;
845   }
846   id = GNUNET_IDENTITY_connect (cfg,
847                                 &identity_cb,
848                                 NULL);
849   handle_cmd_task =
850     GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_UI,
851                                         &handle_command, NULL);
852   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &do_stop_task,
853                                 NULL);
854 }
855
856
857 /** 
858  * The main function to conversation.
859  *
860  * @param argc number of arguments from the command line
861  * @param argv command line arguments
862  * @return 0 ok, 1 on error
863  */
864 int
865 main (int argc, char *const *argv)
866 {
867   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
868     {'e', "ego", "NAME",
869      gettext_noop ("sets the NAME of the ego to use for the phone (and name resolution)"),
870      1, &GNUNET_GETOPT_set_string, &ego_name},
871     {'p', "phone", "LINE",
872       gettext_noop ("sets the LINE to use for the phone"),
873      1, &GNUNET_GETOPT_set_uint, &line},
874     GNUNET_GETOPT_OPTION_END
875   };
876   int flags;
877   int ret;
878
879   flags = fcntl (0, F_GETFL, 0);
880   flags |= O_NONBLOCK;
881   fcntl (0, F_SETFL, flags);
882   stdin_fh = GNUNET_DISK_get_handle_from_int_fd (0);
883   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
884     return 2;
885   ret = GNUNET_PROGRAM_run (argc, argv,
886                             "gnunet-conversation",
887                             gettext_noop ("Enables having a conversation with other GNUnet users."),
888                             options, &run, NULL);
889   GNUNET_free ((void *) argv);
890   return (GNUNET_OK == ret) ? 0 : 1;
891 }
892
893 /* end of gnunet-conversation.c */