-sync before server reboot, work on conversation service
[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, InGNUNET_SERVERc., 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 <fcntl.h>
31
32 #define MAX_MESSAGE_LENGTH   (32 * 1024)
33
34 /**
35  * CONVERSATION handle
36  */
37 static struct GNUNET_CONVERSATION_Handle *conversation;
38
39 /**
40  * Task which handles the commands
41  */
42 static GNUNET_SCHEDULER_TaskIdentifier handle_cmd_task;
43
44 /**
45  * Function declareation for executing a action
46  */
47 typedef int (*ActionFunction) (const char *argumetns, 
48                                const void *xtra);
49
50 /**
51 * Structure which defines a command
52 */
53 struct VoipCommand
54 {
55   const char *command;
56   ActionFunction Action;
57   const char *helptext;
58 };
59
60
61 static int
62 do_help (const char *args, 
63          const void *xtra);
64
65
66 /**
67  * Method called whenever a call is incoming
68  *
69  * @param cls closure
70  * @param handle to the conversation session
71  * @param caller peer that calls you
72  */
73 static void
74 call_handler (void *cls,
75               struct GNUNET_CONVERSATION_Handle *handle,
76               const struct GNUNET_PeerIdentity *caller)
77 {
78   FPRINTF (stdout, 
79            _("Incoming call from peer: %s\n"),
80            GNUNET_i2s_full (caller));
81 }
82
83
84 /**
85  * Method called whenever a call is rejected
86  *
87  * @param cls closure
88  * @param handle to the conversation session
89  * @param reason given reason why the call was rejected
90  * @param peer peer that rejected your call
91  */
92 static void
93 reject_handler (void *cls, 
94                 struct GNUNET_CONVERSATION_Handle *handle, 
95                 enum GNUNET_CONVERSATION_RejectReason reason,
96                 const struct GNUNET_PeerIdentity *peer)
97 {
98   FPRINTF (stdout, 
99            _("Peer %s rejected your call. Reason: %d\n"),
100            GNUNET_i2s_full (peer), reason);
101 }
102
103
104 /**
105  * Method called whenever a notification is there
106  *
107  * @param cls closure
108  * @param handle to the conversation session
109  * @param type the type of the notification
110  * @param peer peer that the notification is about
111  */
112 static void
113 notification_handler (void *cls, 
114                       struct GNUNET_CONVERSATION_Handle *handle, 
115                       enum GNUNET_CONVERSATION_NotificationType type,
116                       const struct GNUNET_PeerIdentity *peer)
117 {
118   switch (type)
119   {
120   case GNUNET_CONVERSATION_NT_SERVICE_BLOCKED:
121     FPRINTF (stdout,
122              _("The service is already in use. Try again later."));    
123     break;    
124   case GNUNET_CONVERSATION_NT_NO_PEER:
125     FPRINTF (stdout, 
126              _("The Peer you were calling is no correct peer.\n"));    
127     break;    
128   case GNUNET_CONVERSATION_NT_NO_ANSWER:
129     FPRINTF (stdout, 
130              _("Peer %s did not answer your call.\n"),
131              GNUNET_i2s_full (peer));    
132     break;    
133   case GNUNET_CONVERSATION_NT_AVAILABLE_AGAIN:
134     FPRINTF (stdout,
135              _("Peer %s is now available.\n"),
136              GNUNET_i2s_full (peer));    
137     break;    
138   case GNUNET_CONVERSATION_NT_CALL_ACCEPTED:
139     FPRINTF (stdout, 
140              _("Peer %s has accepted your call.\n"),
141              GNUNET_i2s_full (peer));    
142     break;    
143   case GNUNET_CONVERSATION_NT_CALL_TERMINATED:
144     FPRINTF (stdout,
145              _("Peer %s has terminated the call.\n"),
146              GNUNET_i2s_full (peer));
147     break;
148   default:
149     GNUNET_break (0);
150   }  
151 }
152
153
154 /**
155  * Method called whenever a notification for missed calls is there
156  *
157  * @param cls closure
158  * @param handle to the conversation session
159  * @param missed_calls a list of missed calls
160  */
161 static void
162 missed_call_handler (void *cls,
163                      struct GNUNET_CONVERSATION_Handle *handle,
164                      struct GNUNET_CONVERSATION_MissedCallNotification *missed_calls)
165 {
166   FPRINTF (stdout, 
167            _("You have missed calls.\n"));
168 }
169
170
171 /**
172  * Terminating the client
173  */
174 static int
175 do_quit (const char *args, 
176          const void *xtra)
177 {
178   return GNUNET_SYSERR;
179 }
180
181
182 /**
183  *
184  */
185 static int
186 do_unknown (const char *msg, 
187             const void *xtra)
188 {
189   FPRINTF (stderr, 
190            _("Unknown command `%s'\n"), 
191            msg);
192   return GNUNET_OK;
193 }
194
195
196 /**
197  * Initiating a new call
198  */
199 static int
200 do_call (const char *arg, 
201          const void *xtra)
202 {
203   FPRINTF (stdout, 
204            _("Initiating call to: %s\n"), 
205            arg);
206   GNUNET_CONVERSATION_call (conversation, 
207                             arg, 
208                             GNUNET_YES);
209   return GNUNET_OK;
210 }
211
212
213 /**
214  * Initiating a new call
215  */
216 static int
217 do_call_peer (const char *arg, 
218               const void *xtra)
219 {
220   FPRINTF (stdout, 
221            _("Initiating call to: %s\n"), 
222            arg);
223   GNUNET_CONVERSATION_call (conversation, 
224                             arg,
225                             GNUNET_NO);
226   return GNUNET_OK;
227 }
228
229
230 /**
231  * Accepting an incoming call
232  */
233 static int
234 do_accept (const char *args, 
235            const void *xtra)
236 {
237   FPRINTF (stdout,
238            _("Accepting the call\n"));
239   GNUNET_CONVERSATION_accept (conversation);
240
241   return GNUNET_OK;
242 }
243
244
245 /**
246  * Rejecting a call
247  */
248 static int
249 do_reject (const char *args, 
250            const void *xtra)
251 {
252   FPRINTF (stdout,
253            _("Rejecting the call\n"));
254   GNUNET_CONVERSATION_reject (conversation);
255   return GNUNET_OK;
256 }
257
258
259 /**
260  * Terminating a call
261  */
262 static int
263 do_hang_up (const char *args, 
264             const void *xtra)
265 {
266   FPRINTF (stdout, 
267            _("Terminating the call\n"));
268   GNUNET_CONVERSATION_hangup (conversation);  
269   return GNUNET_OK;
270 }
271
272
273 /**
274  * List of supported commands.
275  */
276 static struct VoipCommand commands[] = {
277   {"/call ", &do_call, gettext_noop ("Use `/call gns_name'")},
278   {"/callpeer ", &do_call_peer,
279    gettext_noop ("Use `/call private_key' to call a person")},
280   {"/accept", &do_accept,
281    gettext_noop ("Use `/accept' to accept an incoming call")},
282   {"/terminate", &do_hang_up,
283    gettext_noop ("Use `/terminate' to end a call")},
284   {"/reject", &do_reject,
285    gettext_noop ("Use `/rejet' to reject an incoming call")},
286   {"/quit", &do_quit, gettext_noop ("Use `/quit' to terminate gnunet-conversation")},
287   {"/help", &do_help,
288    gettext_noop ("Use `/help command' to get help for a specific command")},
289   {"/", &do_unknown, NULL},
290   {"", &do_unknown, NULL},
291   {NULL, NULL, NULL},
292 };
293
294
295 /**
296  *
297  */
298 static int
299 do_help (const char *args, 
300          const void *xtra)
301 {
302   int i;
303
304   i = 0;
305   while ((NULL != args) && (0 != strlen (args)) &&
306          (commands[i].Action != &do_help))
307   {
308     if (0 ==
309         strncasecmp (&args[1], &commands[i].command[1], strlen (args) - 1))
310     {
311       FPRINTF (stdout, 
312                "%s\n",
313                gettext (commands[i].helptext));
314       return GNUNET_OK;
315     }
316     i++;
317   }
318   i = 0;
319   FPRINTF (stdout, 
320            "%s", 
321            "Available commands:");
322   while (commands[i].Action != &do_help)
323   {
324     FPRINTF (stdout, 
325              " %s", 
326              gettext (commands[i].command));
327     i++;
328   }
329   FPRINTF (stdout,
330            "%s",
331            "\n");
332   FPRINTF (stdout,
333            "%s\n",
334            gettext (commands[i].helptext));
335   return GNUNET_OK;
336 }
337
338
339 /**
340  *
341  */
342 static void
343 do_stop_task (void *cls,
344               const struct GNUNET_SCHEDULER_TaskContext *tc)
345 {
346   GNUNET_log (GNUNET_ERROR_TYPE_INFO, 
347               "Running shutdown task\n");
348   GNUNET_CONVERSATION_disconnect (conversation);
349   
350   if (handle_cmd_task != GNUNET_SCHEDULER_NO_TASK)
351   {
352     GNUNET_SCHEDULER_cancel (handle_cmd_task);
353     handle_cmd_task = GNUNET_SCHEDULER_NO_TASK;
354   } 
355   GNUNET_log (GNUNET_ERROR_TYPE_INFO, 
356               "Running shutdown task finished\n");
357 }
358
359
360 /**
361  *
362  */
363 static void
364 handle_command (void *cls,
365                 const struct GNUNET_SCHEDULER_TaskContext *tc)
366 {
367   char message[MAX_MESSAGE_LENGTH + 1];
368   int i;
369
370   /* read message from command line and handle it */
371   memset (message, 0, MAX_MESSAGE_LENGTH + 1);
372   if (NULL == fgets (message, MAX_MESSAGE_LENGTH, stdin))
373     goto next;
374   if (strlen (message) == 0)
375     goto next;
376   if (message[strlen (message) - 1] == '\n')
377     message[strlen (message) - 1] = '\0';
378   if (strlen (message) == 0)
379     goto next;
380   i = 0;
381   while ((NULL != commands[i].command) &&
382          (0 !=
383           strncasecmp (commands[i].command, message,
384                        strlen (commands[i].command))))
385     i++;
386   if (GNUNET_OK !=
387       commands[i].Action (&message[strlen (commands[i].command)], NULL))
388     goto out;
389
390 next:
391   handle_cmd_task =
392     GNUNET_SCHEDULER_add_delayed_with_priority (GNUNET_TIME_relative_multiply
393                                                 (GNUNET_TIME_UNIT_MILLISECONDS,
394                                                  100),
395                                                 GNUNET_SCHEDULER_PRIORITY_UI,
396                                                 &handle_command, NULL);
397   return;
398
399 out:
400   handle_cmd_task = GNUNET_SCHEDULER_NO_TASK;
401   GNUNET_SCHEDULER_shutdown ();
402 }
403
404
405 /**
406  * Main function that will be run by the scheduler.
407  *
408  * @param cls closure
409  * @param args remaining command-line arguments
410  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
411  * @param c configuration
412  */
413 static void
414 run (void *cls,
415      char *const *args, 
416      const char *cfgfile,
417      const struct GNUNET_CONFIGURATION_Handle *c)
418 {
419   if (NULL ==
420       (conversation =
421        GNUNET_CONVERSATION_connect (c, NULL, 
422                                     &call_handler,
423                                     &reject_handler,
424                                     &notification_handler,
425                                     &missed_call_handler)))
426   {
427     FPRINTF (stderr,
428              "%s",
429              _("Could not access CONVERSATION service.  Exiting.\n"));
430     return;
431   }
432
433   handle_cmd_task =
434     GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_UI,
435                                         &handle_command, NULL);
436   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &do_stop_task,
437                                 NULL);
438 }
439
440
441 /** 
442  * The main function to conversation.
443  *
444  * @param argc number of arguments from the command line
445  * @param argv command line arguments
446  * @return 0 ok, 1 on error
447  */
448 int
449 main (int argc, char *const *argv)
450 {
451   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
452     GNUNET_GETOPT_OPTION_END
453   };
454
455   int flags;
456   int ret;
457
458   flags = fcntl (0, F_GETFL, 0);
459   flags |= O_NONBLOCK;
460   fcntl (0, F_SETFL, flags);
461
462   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
463     return 2;
464
465   ret = GNUNET_PROGRAM_run (argc, argv, "gnunet-conversation",
466                             gettext_noop ("Print information about conversation."),
467                             options, &run, NULL);
468   GNUNET_free ((void *) argv);
469
470   return ret;
471 }
472
473 /* end of gnunet-conversation.c */