-remove trailing whitespace
[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     call = NULL;
300     start_phone ();
301     break;
302   case GNUNET_CONVERSATION_EC_BUSY:
303     GNUNET_break (CS_RINGING == state);
304     FPRINTF (stdout,
305              "%s",
306              _("Line busy\n"));
307     call = NULL;
308     start_phone ();
309     break;
310   case GNUNET_CONVERSATION_EC_TERMINATED:
311     GNUNET_break ( (CS_RINGING == state) ||
312                    (CS_CONNECTED == state) );
313     FPRINTF (stdout,
314              _("Call terminated: %s\n"),
315              va_arg (va, const char *));
316     call = NULL;
317     start_phone ();
318     break;
319   }
320   va_end (va);
321 }
322
323
324 /**
325  * Function declareation for executing a action
326  *
327  * @param arguments arguments given to the function
328  */
329 typedef void (*ActionFunction) (const char *arguments);
330
331
332 /**
333  * Structure which defines a command
334  */
335 struct VoipCommand
336 {
337   /**
338    * Command the user needs to enter.
339    */
340   const char *command;
341
342   /**
343    * Function to call on command.
344    */
345   ActionFunction Action;
346
347   /**
348    * Help text for the command.
349    */
350   const char *helptext;
351 };
352
353
354 /**
355  * Action function to print help for the command shell.
356  *
357  * @param args arguments given to the command
358  */
359 static void
360 do_help (const char *args);
361
362
363 /**
364  * Terminate the client
365  *
366  * @param args arguments given to the command
367  */
368 static void
369 do_quit (const char *args)
370 {
371   GNUNET_SCHEDULER_shutdown ();
372 }
373
374
375 /**
376  * Handler for unknown command.
377  *
378  * @param msg arguments given to the command
379  */
380 static void
381 do_unknown (const char *msg)
382 {
383   FPRINTF (stderr,
384            _("Unknown command `%s'\n"),
385            msg);
386 }
387
388
389 /**
390  * Initiating a new call
391  *
392  * @param arg arguments given to the command
393  */
394 static void
395 do_call (const char *arg)
396 {
397   if (NULL == caller_id)
398   {
399     FPRINTF (stderr,
400              _("Ego `%s' not available\n"),
401              ego_name);
402     return;
403   }
404   switch (state)
405   {
406   case CS_LOOKUP_EGO:
407     FPRINTF (stderr,
408              _("Ego `%s' not available\n"),
409              ego_name);
410     return;
411   case CS_LISTEN:
412     /* ok to call! */
413     break;
414   case CS_RING:
415     FPRINTF (stdout,
416              _("Hanging up on incoming phone call from `%s' to call `%s'.\n"),
417              peer_name,
418              arg);
419     GNUNET_CONVERSATION_phone_hang_up (phone, NULL);
420     break;
421   case CS_ACCEPTED:
422     FPRINTF (stderr,
423              _("You are already in a conversation with `%s', refusing to call `%s'.\n"),
424              peer_name,
425              arg);
426     return;
427   case CS_RESOLVING:
428   case CS_RINGING:
429     FPRINTF (stderr,
430              _("Aborting call to `%s'\n"),
431              peer_name);
432     GNUNET_CONVERSATION_call_stop (call, NULL);
433     call = NULL;
434     break;
435   case CS_CONNECTED:
436     FPRINTF (stderr,
437              _("You are already in a conversation with `%s', refusing to call `%s'.\n"),
438              peer_name,
439              arg);
440     return;
441   case CS_ERROR:
442     /* ok to call */
443     break;
444   }
445   GNUNET_assert (NULL == call);
446   if (NULL != phone)
447   {
448     GNUNET_CONVERSATION_phone_destroy (phone);
449     phone = NULL;
450   }
451   GNUNET_free_non_null (peer_name);
452   peer_name = GNUNET_strdup (arg);
453   call = GNUNET_CONVERSATION_call_start (cfg,
454                                          caller_id,
455                                          arg,
456                                          speaker,
457                                          mic,
458                                          &call_event_handler, NULL);
459   state = CS_RESOLVING;
460 }
461
462
463 /**
464  * Accepting an incoming call
465  *
466  * @param args arguments given to the command
467  */
468 static void
469 do_accept (const char *args)
470 {
471   switch (state)
472   {
473   case CS_LOOKUP_EGO:
474   case CS_LISTEN:
475   case CS_ERROR:
476     FPRINTF (stderr,
477              _("There is no incoming call to be accepted!\n"));
478     return;
479   case CS_RING:
480     /* this is the expected state */
481     break;
482   case CS_ACCEPTED:
483     FPRINTF (stderr,
484              _("You are already in a conversation with `%s'.\n"),
485              peer_name);
486     return;
487   case CS_RESOLVING:
488   case CS_RINGING:
489     FPRINTF (stderr,
490              _("You are trying to call `%s', cannot accept incoming calls right now.\n"),
491              peer_name);
492     return;
493   case CS_CONNECTED:
494     FPRINTF (stderr,
495              _("You are already in a conversation with `%s'.\n"),
496              peer_name);
497     return;
498   }
499   GNUNET_assert (NULL != phone);
500   GNUNET_CONVERSATION_phone_pick_up (phone,
501                                      args,
502                                      speaker,
503                                      mic);
504   state = CS_ACCEPTED;
505 }
506
507
508 /**
509  * Print address information for this phone.
510  *
511  * @param args arguments given to the command
512  */
513 static void
514 do_address (const char *args)
515 {
516   if (NULL == address)
517   {
518     FPRINTF (stdout,
519              "%s",
520              _("We currently do not have an address.\n"));
521     return;
522   }
523   FPRINTF (stdout,
524            "%s\n",
525            address);
526 }
527
528
529 /**
530  * Accepting an incoming call
531  *
532  * @param args arguments given to the command
533  */
534 static void
535 do_status (const char *args)
536 {
537   switch (state)
538   {
539   case CS_LOOKUP_EGO:
540     FPRINTF (stdout,
541              _("We are currently trying to locate the private key for the ego `%s'.\n"),
542              ego_name);
543     break;
544   case CS_LISTEN:
545     FPRINTF (stdout,
546              _("We are listening for incoming calls for ego `%s' on line %u.\n"),
547              ego_name,
548              line);
549     break;
550   case CS_RING:
551     FPRINTF (stdout,
552              _("The phone is rining. `%s' is trying to call us.\n"),
553              peer_name);
554     break;
555   case CS_ACCEPTED:
556   case CS_CONNECTED:
557     FPRINTF (stdout,
558              _("You are having a conversation with `%s'.\n"),
559              peer_name);
560     break;
561   case CS_RESOLVING:
562     FPRINTF (stdout,
563              _("We are trying to find the network address to call `%s'.\n"),
564              peer_name);
565     break;
566   case CS_RINGING:
567     FPRINTF (stdout,
568              _("We are calling `%s', his phone should be ringing.\n"),
569              peer_name);
570     break;
571   case CS_ERROR:
572     FPRINTF (stdout,
573              _("We had an internal error setting up our phone line. You can still make calls.\n"));
574     break;
575   }
576 }
577
578
579 /**
580  * Rejecting a call
581  *
582  * @param args arguments given to the command
583  */
584 static void
585 do_reject (const char *args)
586 {
587   switch (state)
588   {
589   case CS_LOOKUP_EGO:
590   case CS_LISTEN:
591   case CS_ERROR:
592     FPRINTF (stderr,
593              "%s",
594              _("There is no call that could be cancelled right now.\n"));
595     return;
596   case CS_RING:
597   case CS_ACCEPTED:
598   case CS_RESOLVING:
599   case CS_RINGING:
600   case CS_CONNECTED:
601     /* expected state, do rejection logic */
602     break;
603   }
604   if (NULL == call)
605   {
606     GNUNET_assert (NULL != phone);
607     GNUNET_CONVERSATION_phone_hang_up (phone,
608                                        args);
609     state = CS_LISTEN;
610   }
611   else
612   {
613     GNUNET_CONVERSATION_call_stop (call, args);
614     call = NULL;
615     start_phone ();
616   }
617 }
618
619
620 /**
621  * List of supported commands.
622  */
623 static struct VoipCommand commands[] = {
624   {"/address", &do_address,
625    gettext_noop ("Use `/address' to find out which address this phone should have in GNS")},
626   {"/call", &do_call,
627    gettext_noop ("Use `/call USER.gnu' to call USER")},
628   {"/accept", &do_accept,
629    gettext_noop ("Use `/accept MESSAGE' to accept an incoming call")},
630   {"/cancel", &do_reject,
631    gettext_noop ("Use `/cancel MESSAGE' to reject or terminate a call")},
632   {"/status", &do_status,
633    gettext_noop ("Use `/status' to print status information")},
634   {"/quit", &do_quit,
635    gettext_noop ("Use `/quit' to terminate gnunet-conversation")},
636   {"/help", &do_help,
637    gettext_noop ("Use `/help command' to get help for a specific command")},
638   {"", &do_unknown,
639    NULL},
640   {NULL, NULL, NULL},
641 };
642
643
644 /**
645  * Action function to print help for the command shell.
646  *
647  * @param arguments arguments given to the command
648  */
649 static void
650 do_help (const char *args)
651 {
652   unsigned int i;
653
654   i = 0;
655   while ( (NULL != args) &&
656           (0 != strlen (args)) &&
657           (commands[i].Action != &do_help))
658   {
659     if (0 ==
660         strncasecmp (&args[1], &commands[i].command[1], strlen (args) - 1))
661     {
662       FPRINTF (stdout,
663                "%s\n",
664                gettext (commands[i].helptext));
665       return;
666     }
667     i++;
668   }
669   i = 0;
670   FPRINTF (stdout,
671            "%s",
672            "Available commands:\n");
673   while (commands[i].Action != &do_help)
674   {
675     FPRINTF (stdout,
676              "%s\n",
677              gettext (commands[i].command));
678     i++;
679   }
680   FPRINTF (stdout,
681            "%s",
682            "\n");
683   FPRINTF (stdout,
684            "%s\n",
685            gettext (commands[i].helptext));
686 }
687
688
689 /**
690  * Task run during shutdown.
691  *
692  * @param cls NULL
693  * @param tc unused
694  */
695 static void
696 do_stop_task (void *cls,
697               const struct GNUNET_SCHEDULER_TaskContext *tc)
698 {
699   if (NULL != call)
700   {
701     GNUNET_CONVERSATION_call_stop (call, NULL);
702     call = NULL;
703   }
704   if (NULL != phone)
705   {
706     GNUNET_CONVERSATION_phone_destroy (phone);
707     phone = NULL;
708   }
709   if (GNUNET_SCHEDULER_NO_TASK != handle_cmd_task)
710   {
711     GNUNET_SCHEDULER_cancel (handle_cmd_task);
712     handle_cmd_task = GNUNET_SCHEDULER_NO_TASK;
713   }
714   if (NULL != id)
715   {
716     GNUNET_IDENTITY_disconnect (id);
717     id = NULL;
718   }
719   GNUNET_SPEAKER_destroy (speaker);
720   speaker = NULL;
721   GNUNET_MICROPHONE_destroy (mic);
722   mic = NULL;
723   GNUNET_free (ego_name);
724   ego_name = NULL;
725   GNUNET_CONFIGURATION_destroy (cfg);
726   cfg = NULL;
727   GNUNET_free_non_null (peer_name);
728   state = CS_ERROR;
729 }
730
731
732 /**
733  * Task to handle commands from the terminal.
734  *
735  * @param cls NULL
736  * @param tc scheduler context
737  */
738 static void
739 handle_command (void *cls,
740                 const struct GNUNET_SCHEDULER_TaskContext *tc)
741 {
742   char message[MAX_MESSAGE_LENGTH + 1];
743   const char *ptr;
744   size_t i;
745
746   handle_cmd_task =
747     GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
748                                     stdin_fh,
749                                     &handle_command, NULL);
750   /* read message from command line and handle it */
751   memset (message, 0, MAX_MESSAGE_LENGTH + 1);
752   if (NULL == fgets (message, MAX_MESSAGE_LENGTH, stdin))
753     return;
754   if (0 == strlen (message))
755     return;
756   if (message[strlen (message) - 1] == '\n')
757     message[strlen (message) - 1] = '\0';
758   if (0 == strlen (message))
759     return;
760   i = 0;
761   while ((NULL != commands[i].command) &&
762          (0 != strncasecmp (commands[i].command, message,
763                             strlen (commands[i].command))))
764     i++;
765   ptr = &message[strlen (commands[i].command)];
766   while (isspace ((int) *ptr))
767     ptr++;
768   commands[i].Action (ptr);
769 }
770
771
772 /**
773  * Function called by identity service with information about egos.
774  *
775  * @param cls NULL
776  * @param ego ego handle
777  * @param ctx unused
778  * @param name name of the ego
779  */
780 static void
781 identity_cb (void *cls,
782              struct GNUNET_IDENTITY_Ego *ego,
783              void **ctx,
784              const char *name)
785 {
786   if (NULL == name)
787     return;
788   if (ego == caller_id)
789   {
790     if (verbose)
791       FPRINTF (stdout,
792                _("Name of our ego changed to `%s'\n"),
793                name);
794     GNUNET_free (ego_name);
795     ego_name = GNUNET_strdup (name);
796     return;
797   }
798   if (0 != strcmp (name,
799                    ego_name))
800     return;
801   if (NULL == ego)
802   {
803     if (verbose)
804       FPRINTF (stdout,
805                _("Our ego `%s' was deleted!\n"),
806                ego_name);
807     caller_id = NULL;
808     return;
809   }
810   caller_id = ego;
811   GNUNET_CONFIGURATION_set_value_number (cfg,
812                                          "CONVERSATION",
813                                          "LINE",
814                                          line);
815   start_phone ();
816 }
817
818
819 /**
820  * Main function that will be run by the scheduler.
821  *
822  * @param cls closure
823  * @param args remaining command-line arguments
824  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
825  * @param c configuration
826  */
827 static void
828 run (void *cls,
829      char *const *args,
830      const char *cfgfile,
831      const struct GNUNET_CONFIGURATION_Handle *c)
832 {
833   cfg = GNUNET_CONFIGURATION_dup (c);
834   speaker = GNUNET_SPEAKER_create_from_hardware (cfg);
835   mic = GNUNET_MICROPHONE_create_from_hardware (cfg);
836   if (NULL == ego_name)
837   {
838     FPRINTF (stderr,
839              "%s",
840              _("You must specify the NAME of an ego to use\n"));
841     return;
842   }
843   id = GNUNET_IDENTITY_connect (cfg,
844                                 &identity_cb,
845                                 NULL);
846   handle_cmd_task =
847     GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_UI,
848                                         &handle_command, NULL);
849   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &do_stop_task,
850                                 NULL);
851 }
852
853
854 /**
855  * The main function to conversation.
856  *
857  * @param argc number of arguments from the command line
858  * @param argv command line arguments
859  * @return 0 ok, 1 on error
860  */
861 int
862 main (int argc, char *const *argv)
863 {
864   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
865     {'e', "ego", "NAME",
866      gettext_noop ("sets the NAME of the ego to use for the phone (and name resolution)"),
867      1, &GNUNET_GETOPT_set_string, &ego_name},
868     {'p', "phone", "LINE",
869       gettext_noop ("sets the LINE to use for the phone"),
870      1, &GNUNET_GETOPT_set_uint, &line},
871     GNUNET_GETOPT_OPTION_END
872   };
873   int flags;
874   int ret;
875
876   flags = fcntl (0, F_GETFL, 0);
877   flags |= O_NONBLOCK;
878   fcntl (0, F_SETFL, flags);
879   stdin_fh = GNUNET_DISK_get_handle_from_int_fd (0);
880   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
881     return 2;
882   ret = GNUNET_PROGRAM_run (argc, argv,
883                             "gnunet-conversation",
884                             gettext_noop ("Enables having a conversation with other GNUnet users."),
885                             options, &run, NULL);
886   GNUNET_free ((void *) argv);
887   return (GNUNET_OK == ret) ? 0 : 1;
888 }
889
890 /* end of gnunet-conversation.c */