-fix record expiration in test
[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 <fcntl.h>
34
35 static int ret;
36
37 static const struct GNUNET_CONFIGURATION_Handle *cfg;
38
39 static char *nickname;
40
41 static char *room_name;
42
43 static struct GNUNET_CONTAINER_MetaData *meta;
44
45 static struct GNUNET_CHAT_Room *room;
46
47 static GNUNET_SCHEDULER_TaskIdentifier handle_cmd_task;
48
49 typedef int (*ActionFunction)(const char *argumetns, const void *xtra);
50
51 struct ChatCommand
52 {
53   const char *command;
54   ActionFunction Action;
55   const char *helptext;
56 };
57
58 struct UserList
59 {
60   struct UserList *next;
61   struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pkey;
62   int ignored;
63 };
64
65 static struct UserList *users;
66
67 static void
68 free_user_list ()
69 {
70   struct UserList *next;
71
72   while (NULL != users)
73   {
74     next = users->next;
75     GNUNET_free (users);
76     users = next;
77   }
78 }
79
80 static int
81 do_help (const char *args, const void *xtra);
82
83
84 /**
85  * Callback used for notification that we have joined the room.
86  *
87  * @param cls closure
88  * @return GNUNET_OK
89  */
90 static int
91 join_cb (void *cls)
92 {
93   FPRINTF (stdout, "%s",  _("Joined\n"));
94   return GNUNET_OK;
95 }
96
97
98 /**
99  * Callback used for notification about incoming messages.
100  *
101  * @param cls closure, NULL
102  * @param room in which room was the message received?
103  * @param sender what is the ID of the sender? (maybe NULL)
104  * @param member_info information about the joining member
105  * @param message the message text
106  * @param timestamp time when the member joined
107  * @param options options for the message
108  * @return GNUNET_OK to accept the message now, GNUNET_NO to
109  *         accept (but user is away), GNUNET_SYSERR to signal denied delivery
110  */
111 static int
112 receive_cb (void *cls, struct GNUNET_CHAT_Room *room,
113             const struct GNUNET_HashCode * sender,
114             const struct GNUNET_CONTAINER_MetaData *member_info,
115             const char *message, struct GNUNET_TIME_Absolute timestamp,
116             enum GNUNET_CHAT_MsgOptions options)
117 {
118   char *non_unique_nick;
119   char *nick;
120   int nick_is_a_dup;
121   char *time;
122   const char *fmt;
123
124   if (NULL == sender)
125     nick = GNUNET_strdup (_("anonymous"));
126   else
127   {
128     if (GNUNET_OK != GNUNET_PSEUDONYM_get_info (cfg,
129         sender, NULL, NULL, &non_unique_nick, &nick_is_a_dup)
130         || (nick_is_a_dup == GNUNET_YES))
131     {
132       GNUNET_free (non_unique_nick);
133       non_unique_nick = GNUNET_strdup (_("anonymous"));
134     }
135     nick = GNUNET_PSEUDONYM_name_uniquify (cfg, sender, non_unique_nick, NULL);
136     GNUNET_free (non_unique_nick);
137   }
138
139   fmt = NULL;
140   switch ((int) options)
141   {
142   case GNUNET_CHAT_MSG_OPTION_NONE:
143   case GNUNET_CHAT_MSG_ANONYMOUS:
144     fmt = _("(%s) `%s' said: %s\n");
145     break;
146   case GNUNET_CHAT_MSG_PRIVATE:
147     fmt = _("(%s) `%s' said to you: %s\n");
148     break;
149   case GNUNET_CHAT_MSG_PRIVATE | GNUNET_CHAT_MSG_ANONYMOUS:
150     fmt = _("(%s) `%s' said to you: %s\n");
151     break;
152   case GNUNET_CHAT_MSG_AUTHENTICATED:
153     fmt = _("(%s) `%s' said for sure: %s\n");
154     break;
155   case GNUNET_CHAT_MSG_PRIVATE | GNUNET_CHAT_MSG_AUTHENTICATED:
156     fmt = _("(%s) `%s' said to you for sure: %s\n");
157     break;
158   case GNUNET_CHAT_MSG_ACKNOWLEDGED:
159     fmt = _("(%s) `%s' was confirmed that you received: %s\n");
160     break;
161   case GNUNET_CHAT_MSG_PRIVATE | GNUNET_CHAT_MSG_ACKNOWLEDGED:
162     fmt = _("(%s) `%s' was confirmed that you and only you received: %s\n");
163     break;
164   case GNUNET_CHAT_MSG_AUTHENTICATED | GNUNET_CHAT_MSG_ACKNOWLEDGED:
165     fmt = _("(%s) `%s' was confirmed that you received from him or her: %s\n");
166     break;
167   case GNUNET_CHAT_MSG_AUTHENTICATED | GNUNET_CHAT_MSG_PRIVATE | GNUNET_CHAT_MSG_ACKNOWLEDGED:
168     fmt =
169         _
170         ("(%s) `%s' was confirmed that you and only you received from him or her: %s\n");
171     break;
172   case GNUNET_CHAT_MSG_OFF_THE_RECORD:
173     fmt = _("(%s) `%s' said off the record: %s\n");
174     break;
175   default:
176     fmt = _("(%s) <%s> said using an unknown message type: %s\n");
177     break;
178   }
179   time = GNUNET_STRINGS_absolute_time_to_string (timestamp);
180   FPRINTF (stdout, fmt, time, nick, message);
181   GNUNET_free (nick);
182   GNUNET_free (time);
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_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_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_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_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_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_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_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_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_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_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 */