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