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