-simplify
[oweals/gnunet.git] / src / chat / gnunet-chat.c
1 /*
2      This file is part of GNUnet.
3      (C) 2007, 2008, 2011 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 /**
22  * @file chat/gnunet-chat.c
23  * @brief Minimal chat command line tool
24  * @author Christian Grothoff
25  * @author Nathan Evans
26  * @author Vitaly Minko
27  */
28
29 #include "platform.h"
30 #include "gnunet_getopt_lib.h"
31 #include "gnunet_program_lib.h"
32 #include "gnunet_chat_service.h"
33 #include "gnunet_fs_service.h"
34 #include <fcntl.h>
35
36 static int ret;
37
38 static const struct GNUNET_CONFIGURATION_Handle *cfg;
39
40 static char *nickname;
41
42 static char *room_name;
43
44 static struct GNUNET_CONTAINER_MetaData *meta;
45
46 static struct GNUNET_CHAT_Room *room;
47
48 static GNUNET_SCHEDULER_TaskIdentifier handle_cmd_task;
49
50 typedef int (*ActionFunction)(const char *argumetns, const void *xtra);
51
52 struct ChatCommand
53 {
54   const char *command;
55   ActionFunction Action;
56   const char *helptext;
57 };
58
59 struct UserList
60 {
61   struct UserList *next;
62   struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pkey;
63   int ignored;
64 };
65
66 static struct UserList *users;
67
68 static void
69 free_user_list ()
70 {
71   struct UserList *next;
72
73   while (NULL != users)
74   {
75     next = users->next;
76     GNUNET_free (users);
77     users = next;
78   }
79 }
80
81 static int
82 do_help (const char *args, const void *xtra);
83
84
85 /**
86  * Callback used for notification that we have joined the room.
87  *
88  * @param cls closure
89  * @return GNUNET_OK
90  */
91 static int
92 join_cb (void *cls)
93 {
94   FPRINTF (stdout, "%s",  _("Joined\n"));
95   return GNUNET_OK;
96 }
97
98
99 /**
100  * Callback used for notification about incoming messages.
101  *
102  * @param cls closure, NULL
103  * @param room in which room was the message received?
104  * @param sender what is the ID of the sender? (maybe NULL)
105  * @param member_info information about the joining member
106  * @param message the message text
107  * @param timestamp time when the member joined
108  * @param options options for the message
109  * @return GNUNET_OK to accept the message now, GNUNET_NO to
110  *         accept (but user is away), GNUNET_SYSERR to signal denied delivery
111  */
112 static int
113 receive_cb (void *cls, struct GNUNET_CHAT_Room *room,
114             const struct GNUNET_HashCode * sender,
115             const struct GNUNET_CONTAINER_MetaData *member_info,
116             const char *message, struct GNUNET_TIME_Absolute timestamp,
117             enum GNUNET_CHAT_MsgOptions options)
118 {
119   char *non_unique_nick;
120   char *nick;
121   int nick_is_a_dup;
122   const char *timestr;
123   const char *fmt;
124
125   if (NULL == sender)
126     nick = GNUNET_strdup (_("anonymous"));
127   else
128   {
129     if (GNUNET_OK != GNUNET_FS_pseudonym_get_info (cfg,
130         sender, NULL, NULL, &non_unique_nick, &nick_is_a_dup)
131         || (nick_is_a_dup == GNUNET_YES))
132     {
133       GNUNET_free (non_unique_nick);
134       non_unique_nick = GNUNET_strdup (_("anonymous"));
135     }
136     nick = GNUNET_FS_pseudonym_name_uniquify (cfg, sender, non_unique_nick, NULL);
137     GNUNET_free (non_unique_nick);
138   }
139
140   fmt = NULL;
141   switch ((int) options)
142   {
143   case GNUNET_CHAT_MSG_OPTION_NONE:
144   case GNUNET_CHAT_MSG_ANONYMOUS:
145     fmt = _("(%s) `%s' said: %s\n");
146     break;
147   case GNUNET_CHAT_MSG_PRIVATE:
148     fmt = _("(%s) `%s' said to you: %s\n");
149     break;
150   case GNUNET_CHAT_MSG_PRIVATE | GNUNET_CHAT_MSG_ANONYMOUS:
151     fmt = _("(%s) `%s' said to you: %s\n");
152     break;
153   case GNUNET_CHAT_MSG_AUTHENTICATED:
154     fmt = _("(%s) `%s' said for sure: %s\n");
155     break;
156   case GNUNET_CHAT_MSG_PRIVATE | GNUNET_CHAT_MSG_AUTHENTICATED:
157     fmt = _("(%s) `%s' said to you for sure: %s\n");
158     break;
159   case GNUNET_CHAT_MSG_ACKNOWLEDGED:
160     fmt = _("(%s) `%s' was confirmed that you received: %s\n");
161     break;
162   case GNUNET_CHAT_MSG_PRIVATE | GNUNET_CHAT_MSG_ACKNOWLEDGED:
163     fmt = _("(%s) `%s' was confirmed that you and only you received: %s\n");
164     break;
165   case GNUNET_CHAT_MSG_AUTHENTICATED | GNUNET_CHAT_MSG_ACKNOWLEDGED:
166     fmt = _("(%s) `%s' was confirmed that you received from him or her: %s\n");
167     break;
168   case GNUNET_CHAT_MSG_AUTHENTICATED | GNUNET_CHAT_MSG_PRIVATE | GNUNET_CHAT_MSG_ACKNOWLEDGED:
169     fmt =
170         _
171         ("(%s) `%s' was confirmed that you and only you received from him or her: %s\n");
172     break;
173   case GNUNET_CHAT_MSG_OFF_THE_RECORD:
174     fmt = _("(%s) `%s' said off the record: %s\n");
175     break;
176   default:
177     fmt = _("(%s) <%s> said using an unknown message type: %s\n");
178     break;
179   }
180   timestr = GNUNET_STRINGS_absolute_time_to_string (timestamp);
181   FPRINTF (stdout, fmt, timestr, nick, message);
182   GNUNET_free (nick);
183   return GNUNET_OK;
184 }
185
186
187 /**
188  * Callback used for message delivery confirmations.
189  *
190  * @param cls closure, NULL
191  * @param room in which room was the message received?
192  * @param orig_seq_number sequence number of the original message
193  * @param timestamp when was the message received?
194  * @param receiver who is confirming the receipt?
195  * @return GNUNET_OK to continue, GNUNET_SYSERR to refuse processing further
196  *         confirmations from anyone for this message
197  */
198 static int
199 confirmation_cb (void *cls, struct GNUNET_CHAT_Room *room,
200                  uint32_t orig_seq_number,
201                  struct GNUNET_TIME_Absolute timestamp,
202                  const struct GNUNET_HashCode * receiver)
203 {
204   char *nick;
205   char *unique_nick;
206   int nick_is_a_dup;
207
208   if (GNUNET_OK != GNUNET_FS_pseudonym_get_info (cfg,
209       receiver, NULL, NULL, &nick, &nick_is_a_dup)
210       || (nick_is_a_dup == GNUNET_YES))
211   {
212     GNUNET_free (nick);
213     nick = GNUNET_strdup (_("anonymous"));
214   }
215   unique_nick = GNUNET_FS_pseudonym_name_uniquify (cfg, receiver, nick, NULL);
216   GNUNET_free (nick);
217   FPRINTF (stdout, _("'%s' acknowledged message #%d\n"), unique_nick, orig_seq_number);
218   GNUNET_free (unique_nick);
219   return GNUNET_OK;
220 }
221
222
223 /**
224  * Callback used for notification that another room member has joined or left.
225  *
226  * @param cls closure (not used)
227  * @param member_info will be non-null if the member is joining, NULL if he is
228  *        leaving
229  * @param member_id hash of public key of the user (for unique identification)
230  * @param options what types of messages is this member willing to receive?
231  * @return GNUNET_OK
232  */
233 static int
234 member_list_cb (void *cls, const struct GNUNET_CONTAINER_MetaData *member_info,
235                 const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *member_id,
236                 enum GNUNET_CHAT_MsgOptions options)
237 {
238   char *nick;
239   char *non_unique_nick;
240   int nick_is_a_dup;
241   struct GNUNET_HashCode id;
242   struct UserList *pos;
243   struct UserList *prev;
244
245   GNUNET_CRYPTO_hash (member_id,
246                       sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
247                       &id);
248   if (GNUNET_OK != GNUNET_FS_pseudonym_get_info (cfg,
249       &id, NULL, NULL, &non_unique_nick, &nick_is_a_dup)
250       || (nick_is_a_dup == GNUNET_YES))
251   {
252     GNUNET_free (non_unique_nick);
253     non_unique_nick = GNUNET_strdup (_("anonymous"));
254   }
255   nick = GNUNET_FS_pseudonym_name_uniquify (cfg, &id, non_unique_nick, NULL);
256   GNUNET_free (non_unique_nick);
257
258   FPRINTF (stdout,
259            member_info !=
260            NULL ? _("`%s' entered the room\n") : _("`%s' left the room\n"),
261            nick);
262   GNUNET_free (nick);
263   if (NULL != member_info)
264   {
265     /* user joining */
266     pos = GNUNET_malloc (sizeof (struct UserList));
267     pos->next = users;
268     pos->pkey = *member_id;
269     pos->ignored = GNUNET_NO;
270     users = pos;
271   }
272   else
273   {
274     /* user leaving */
275     prev = NULL;
276     pos = users;
277     while ((NULL != pos) &&
278            (0 !=
279             memcmp (&pos->pkey, member_id,
280                     sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded))))
281     {
282       prev = pos;
283       pos = pos->next;
284     }
285     if (NULL == pos)
286     {
287       GNUNET_break (0);
288     }
289     else
290     {
291       if (NULL == prev)
292         users = pos->next;
293       else
294         prev->next = pos->next;
295       GNUNET_free (pos);
296     }
297   }
298   return GNUNET_OK;
299 }
300
301
302 static int
303 do_join (const char *arg, const void *xtra)
304 {
305   char *my_name;
306   int my_name_is_a_dup;
307   struct GNUNET_HashCode me;
308
309   if (arg[0] == '#')
310     arg++;                      /* ignore first hash */
311   GNUNET_CHAT_leave_room (room);
312   free_user_list ();
313   GNUNET_free (room_name);
314   room_name = GNUNET_strdup (arg);
315   room =
316       GNUNET_CHAT_join_room (cfg, nickname, meta, room_name, -1, &join_cb, NULL,
317                              &receive_cb, NULL, &member_list_cb, NULL,
318                              &confirmation_cb, NULL, &me);
319   if (NULL == room)
320   {
321     FPRINTF (stdout, "%s",  _("Could not change username\n"));
322     return GNUNET_SYSERR;
323   }
324   if ((GNUNET_OK != GNUNET_FS_pseudonym_get_info (cfg,
325       &me, NULL, NULL, &my_name, &my_name_is_a_dup)) ||
326       (my_name_is_a_dup == GNUNET_YES))
327   {
328     GNUNET_free (my_name);
329     my_name = GNUNET_strdup (_("anonymous"));
330   }
331   /* Don't uniquify our own name - other people will have a different
332    * suffix for our own name anyway.
333    */
334   FPRINTF (stdout, _("Joining room `%s' as user `%s'...\n"), room_name,
335            my_name);
336   GNUNET_free (my_name);
337   return GNUNET_OK;
338 }
339
340
341 static int
342 do_nick (const char *msg, const void *xtra)
343 {
344   char *my_name;
345   int my_name_is_a_dup;
346   struct GNUNET_HashCode me;
347
348   GNUNET_CHAT_leave_room (room);
349   free_user_list ();
350   GNUNET_free (nickname);
351   GNUNET_CONTAINER_meta_data_destroy (meta);
352   nickname = GNUNET_strdup (msg);
353   meta = GNUNET_CONTAINER_meta_data_create ();
354   GNUNET_CONTAINER_meta_data_insert (meta, "<gnunet>", EXTRACTOR_METATYPE_TITLE,
355                                      EXTRACTOR_METAFORMAT_UTF8, "text/plain",
356                                      nickname, strlen (nickname) + 1);
357   room =
358       GNUNET_CHAT_join_room (cfg, nickname, meta, room_name, -1, &join_cb, NULL,
359                              &receive_cb, NULL, &member_list_cb, NULL,
360                              &confirmation_cb, NULL, &me);
361   if (NULL == room)
362   {
363     FPRINTF (stdout, "%s",  _("Could not change username\n"));
364     return GNUNET_SYSERR;
365   }
366   if ((GNUNET_OK != GNUNET_FS_pseudonym_get_info (cfg,
367       &me, NULL, NULL, &my_name, &my_name_is_a_dup)) ||
368       (my_name_is_a_dup == GNUNET_YES))
369   {
370     GNUNET_free (my_name);
371     my_name = GNUNET_strdup (_("anonymous"));
372   }
373   FPRINTF (stdout, _("Changed username to `%s'\n"), my_name);
374   GNUNET_free (my_name);
375   return GNUNET_OK;
376 }
377
378
379 static int
380 do_names (const char *msg, const void *xtra)
381 {
382   char *name;
383   char *unique_name;
384   int name_is_a_dup;
385   struct UserList *pos;
386   struct GNUNET_HashCode pid;
387
388   FPRINTF (stdout, _("Users in room `%s': "), room_name);
389   pos = users;
390   while (NULL != pos)
391   {
392     GNUNET_CRYPTO_hash (&pos->pkey,
393                         sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
394                         &pid);
395     if (GNUNET_OK != GNUNET_FS_pseudonym_get_info (cfg,
396         &pid, NULL, NULL, &name, &name_is_a_dup)
397         || (name_is_a_dup == GNUNET_YES))
398     {
399       GNUNET_free (name);
400       name = GNUNET_strdup (_("anonymous"));
401     }
402     unique_name = GNUNET_FS_pseudonym_name_uniquify (cfg, &pid, name, NULL);
403     GNUNET_free (name);
404     FPRINTF (stdout, "`%s' ", unique_name);
405     GNUNET_free (unique_name);
406     pos = pos->next;
407   }
408   FPRINTF (stdout, "%s",  "\n");
409   return GNUNET_OK;
410 }
411
412
413 static int
414 do_send (const char *msg, const void *xtra)
415 {
416   uint32_t seq;
417
418   GNUNET_CHAT_send_message (room, msg, GNUNET_CHAT_MSG_OPTION_NONE, NULL, &seq);
419   return GNUNET_OK;
420 }
421
422
423 static int
424 do_send_pm (const char *msg, const void *xtra)
425 {
426   char *user;
427   struct GNUNET_HashCode uid;
428   struct GNUNET_HashCode pid;
429   uint32_t seq;
430   struct UserList *pos;
431
432   if (NULL == strstr (msg, " "))
433   {
434     FPRINTF (stderr, "%s",  _("Syntax: /msg USERNAME MESSAGE"));
435     return GNUNET_OK;
436   }
437   user = GNUNET_strdup (msg);
438   strstr (user, " ")[0] = '\0';
439   msg += strlen (user) + 1;
440   if (GNUNET_OK != GNUNET_FS_pseudonym_name_to_id (cfg, user, &uid))
441   {
442     FPRINTF (stderr,
443         _("Unknown user `%s'. Make sure you specify its numeric suffix, if any.\n"),
444         user);
445     GNUNET_free (user);
446     return GNUNET_OK;
447   }
448   pos = users;
449   while (NULL != pos)
450   {
451     GNUNET_CRYPTO_hash (&pos->pkey,
452                         sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
453                         &pid);
454     if (0 == memcmp (&pid, &uid, sizeof (struct GNUNET_HashCode)))
455       break;
456     pos = pos->next;
457   }
458   if (NULL == pos)
459   {
460     FPRINTF (stderr, _("User `%s' is currently not in the room!\n"), user);
461     GNUNET_free (user);
462     return GNUNET_OK;
463   }
464   GNUNET_CHAT_send_message (room, msg, GNUNET_CHAT_MSG_PRIVATE, &pos->pkey,
465                             &seq);
466   GNUNET_free (user);
467   return GNUNET_OK;
468 }
469
470
471 static int
472 do_send_sig (const char *msg, const void *xtra)
473 {
474   uint32_t seq;
475
476   GNUNET_CHAT_send_message (room, msg, GNUNET_CHAT_MSG_AUTHENTICATED, NULL,
477                             &seq);
478   return GNUNET_OK;
479 }
480
481
482 static int
483 do_send_ack (const char *msg, const void *xtra)
484 {
485   uint32_t seq;
486
487   GNUNET_CHAT_send_message (room, msg, GNUNET_CHAT_MSG_ACKNOWLEDGED, NULL,
488                             &seq);
489   return GNUNET_OK;
490 }
491
492
493 static int
494 do_send_anonymous (const char *msg, const void *xtra)
495 {
496   uint32_t seq;
497
498   GNUNET_CHAT_send_message (room, msg, GNUNET_CHAT_MSG_ANONYMOUS, NULL, &seq);
499   return GNUNET_OK;
500 }
501
502
503 static int
504 do_quit (const char *args, const void *xtra)
505 {
506   return GNUNET_SYSERR;
507 }
508
509
510 static int
511 do_unknown (const char *msg, const void *xtra)
512 {
513   FPRINTF (stderr, _("Unknown command `%s'\n"), msg);
514   return GNUNET_OK;
515 }
516
517
518 /**
519  * List of supported IRC commands. The order matters!
520  */
521 static struct ChatCommand commands[] = {
522   {"/join ", &do_join,
523    gettext_noop
524    ("Use `/join #roomname' to join a chat room. Joining a room will cause you"
525     " to leave the current room")},
526   {"/nick ", &do_nick,
527    gettext_noop
528    ("Use `/nick nickname' to change your nickname.  This will cause you to"
529     " leave the current room and immediately rejoin it with the new name.")},
530   {"/msg ", &do_send_pm,
531    gettext_noop
532    ("Use `/msg nickname message' to send a private message to the specified"
533     " user")},
534   {"/notice ", &do_send_pm,
535    gettext_noop ("The `/notice' command is an alias for `/msg'")},
536   {"/query ", &do_send_pm,
537    gettext_noop ("The `/query' command is an alias for `/msg'")},
538   {"/sig ", &do_send_sig,
539    gettext_noop ("Use `/sig message' to send a signed public message")},
540   {"/ack ", &do_send_ack,
541    gettext_noop
542    ("Use `/ack message' to require signed acknowledgment of the message")},
543   {"/anonymous ", &do_send_anonymous,
544    gettext_noop
545    ("Use `/anonymous message' to send a public anonymous message")},
546   {"/anon ", &do_send_anonymous,
547    gettext_noop ("The `/anon' command is an alias for `/anonymous'")},
548   {"/quit", &do_quit,
549    gettext_noop ("Use `/quit' to terminate gnunet-chat")},
550   {"/leave", &do_quit,
551    gettext_noop ("The `/leave' command is an alias for `/quit'")},
552   {"/names", &do_names,
553    gettext_noop
554    ("Use `/names' to list all of the current members in the chat room")},
555   {"/help", &do_help,
556    gettext_noop ("Use `/help command' to get help for a specific command")},
557   /* Add standard commands:
558    * /whois (print metadata),
559    * /ignore (set flag, check on receive!) */
560   /* the following three commands must be last! */
561   {"/", &do_unknown, NULL},
562   {"", &do_send, NULL},
563   {NULL, NULL, NULL},
564 };
565
566
567 static int
568 do_help (const char *args, const void *xtra)
569 {
570   int i;
571
572   i = 0;
573   while ((NULL != args) && (0 != strlen (args)) &&
574          (commands[i].Action != &do_help))
575   {
576     if (0 == strncasecmp (&args[1], &commands[i].command[1], strlen (args) - 1))
577     {
578       FPRINTF (stdout, "%s\n", gettext (commands[i].helptext));
579       return GNUNET_OK;
580     }
581     i++;
582   }
583   i = 0;
584   FPRINTF (stdout, "%s",  "Available commands:");
585   while (commands[i].Action != &do_help)
586   {
587     FPRINTF (stdout, " %s", gettext (commands[i].command));
588     i++;
589   }
590   FPRINTF (stdout, "%s",  "\n");
591   FPRINTF (stdout, "%s\n", gettext (commands[i].helptext));
592   return GNUNET_OK;
593 }
594
595
596 static void
597 do_stop_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
598 {
599   GNUNET_CHAT_leave_room (room);
600   if (handle_cmd_task != GNUNET_SCHEDULER_NO_TASK)
601   {
602     GNUNET_SCHEDULER_cancel (handle_cmd_task);
603     handle_cmd_task = GNUNET_SCHEDULER_NO_TASK;
604   }
605   free_user_list ();
606   GNUNET_CONTAINER_meta_data_destroy (meta);
607   GNUNET_free (room_name);
608   GNUNET_free (nickname);
609 }
610
611
612 void
613 handle_command (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
614 {
615   char message[MAX_MESSAGE_LENGTH + 1];
616   int i;
617
618   /* read message from command line and handle it */
619   memset (message, 0, MAX_MESSAGE_LENGTH + 1);
620   if (NULL == fgets (message, MAX_MESSAGE_LENGTH, stdin))
621     goto next;
622   if (strlen (message) == 0)
623     goto next;
624   if (message[strlen (message) - 1] == '\n')
625     message[strlen (message) - 1] = '\0';
626   if (strlen (message) == 0)
627     goto next;
628   i = 0;
629   while ((NULL != commands[i].command) &&
630          (0 !=
631           strncasecmp (commands[i].command, message,
632                        strlen (commands[i].command))))
633     i++;
634   if (GNUNET_OK !=
635       commands[i].Action (&message[strlen (commands[i].command)], NULL))
636     goto out;
637
638 next:
639   handle_cmd_task =
640     GNUNET_SCHEDULER_add_delayed_with_priority (GNUNET_TIME_relative_multiply
641                                                 (GNUNET_TIME_UNIT_MILLISECONDS, 100),
642                                                 GNUNET_SCHEDULER_PRIORITY_UI,
643                                                 &handle_command, NULL);
644   return;
645
646 out:
647   handle_cmd_task = GNUNET_SCHEDULER_NO_TASK;
648   GNUNET_SCHEDULER_shutdown ();
649 }
650
651
652 /**
653  * Main function that will be run by the scheduler.
654  *
655  * @param cls closure, NULL
656  * @param args remaining command-line arguments
657  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
658  * @param c configuration
659  */
660 static void
661 run (void *cls, char *const *args, const char *cfgfile,
662      const struct GNUNET_CONFIGURATION_Handle *c)
663 {
664   struct GNUNET_HashCode me;
665   char *my_name;
666   int my_name_is_a_dup;
667
668   cfg = c;
669   /* check arguments */
670   if (NULL == nickname)
671   {
672     FPRINTF (stderr, "%s",  _("You must specify a nickname\n"));
673     ret = -1;
674     return;
675   }
676   if (NULL == room_name)
677     room_name = GNUNET_strdup ("gnunet");
678   meta = GNUNET_CONTAINER_meta_data_create ();
679   GNUNET_CONTAINER_meta_data_insert (meta, "<gnunet>", EXTRACTOR_METATYPE_TITLE,
680                                      EXTRACTOR_METAFORMAT_UTF8, "text/plain",
681                                      nickname, strlen (nickname) + 1);
682   room =
683       GNUNET_CHAT_join_room (cfg, nickname, meta, room_name, -1, &join_cb, NULL,
684                              &receive_cb, NULL, &member_list_cb, NULL,
685                              &confirmation_cb, NULL, &me);
686   if (NULL == room)
687   {
688     FPRINTF (stderr, _("Failed to join room `%s'\n"), room_name);
689     GNUNET_free (room_name);
690     GNUNET_free (nickname);
691     GNUNET_CONTAINER_meta_data_destroy (meta);
692     ret = -1;
693     return;
694   }
695   if ((GNUNET_OK != GNUNET_FS_pseudonym_get_info (cfg,
696       &me, NULL, NULL, &my_name, &my_name_is_a_dup)) ||
697       (my_name_is_a_dup == GNUNET_YES))
698   {
699     GNUNET_free (my_name);
700     my_name = GNUNET_strdup (_("anonymous"));
701   }
702   FPRINTF (stdout, _("Joining room `%s' as user `%s'...\n"), room_name,
703            my_name);
704   GNUNET_free (my_name);
705   handle_cmd_task =
706       GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_UI,
707                                           &handle_command, NULL);
708   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &do_stop_task,
709                                 NULL);
710 }
711
712
713 /**
714  * The main function to chat via GNUnet.
715  *
716  * @param argc number of arguments from the command line
717  * @param argv command line arguments
718  * @return 0 ok, 1 on error
719  */
720 int
721 main (int argc, char *const *argv)
722 {
723   int flags;
724
725   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
726     {'n', "nick", "NAME",
727      gettext_noop ("set the nickname to use (required)"),
728      1, &GNUNET_GETOPT_set_string, &nickname},
729     {'r', "room", "NAME",
730      gettext_noop ("set the chat room to join"),
731      1, &GNUNET_GETOPT_set_string, &room_name},
732     GNUNET_GETOPT_OPTION_END
733   };
734
735 #ifndef WINDOWS
736   flags = fcntl (0, F_GETFL, 0);
737   flags |= O_NONBLOCK;
738   fcntl (0, F_SETFL, flags);
739 #endif
740
741   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
742     return 2;
743
744   return (GNUNET_OK ==
745           GNUNET_PROGRAM_run (argc, argv, "gnunet-chat",
746                               gettext_noop ("Join a chat on GNUnet."), options,
747                               &run, NULL)) ? ret : 1;
748 }
749
750 /* end of gnunet-chat.c */