gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[GNUnet-SVN] r14364 - in gnunet: . src/chat src/core src/include


From: gnunet
Subject: [GNUnet-SVN] r14364 - in gnunet: . src/chat src/core src/include
Date: Mon, 7 Feb 2011 00:27:01 +0100

Author: grothoff
Date: 2011-02-07 00:27:01 +0100 (Mon, 07 Feb 2011)
New Revision: 14364

Modified:
   gnunet/AUTHORS
   gnunet/src/chat/Makefile.am
   gnunet/src/chat/chat.c
   gnunet/src/chat/chat.h
   gnunet/src/chat/gnunet-chat.c
   gnunet/src/chat/gnunet-service-chat.c
   gnunet/src/core/core.h
   gnunet/src/include/gnunet_chat_service.h
Log:
more chat code from Mantis #1657

Modified: gnunet/AUTHORS
===================================================================
--- gnunet/AUTHORS      2011-02-06 23:25:56 UTC (rev 14363)
+++ gnunet/AUTHORS      2011-02-06 23:27:01 UTC (rev 14364)
@@ -8,6 +8,7 @@
 Nils Durner <address@hidden>
 Safey Allah Mohammed <address@hidden>
 Philipp T�lke <address@hidden>
+Vitaly Minko <address@hidden>
 
 Code contributions also came from:
 Adam Warrington [ UPnP ]

Modified: gnunet/src/chat/Makefile.am
===================================================================
--- gnunet/src/chat/Makefile.am 2011-02-06 23:25:56 UTC (rev 14363)
+++ gnunet/src/chat/Makefile.am 2011-02-06 23:27:01 UTC (rev 14364)
@@ -37,3 +37,85 @@
   $(top_builddir)/src/chat/libgnunetchat.la \
   $(top_builddir)/src/util/libgnunetutil.la \
   $(GN_LIBINTL)
+
+check_PROGRAMS = \
+ test_chat \
+ test_chat_acknowledgement \
+ test_chat_anonymous \
+ test_chat_authentication \
+ test_chat_p2p \
+ test_chat_acknowledgement_p2p \
+ test_chat_anonymous_p2p \
+ test_chat_authentication_p2p \
+ test_chat_private \
+ test_chat_private_p2p
+
+if !DISABLE_TEST_RUN
+TESTS = $(check_PROGRAMS)
+endif
+
+test_chat_SOURCES = \
+ test_chat.c
+test_chat_LDADD = \
+  $(top_builddir)/src/chat/libgnunetchat.la \
+  $(top_builddir)/src/util/libgnunetutil.la
+
+test_chat_acknowledgement_SOURCES = \
+ test_chat.c
+test_chat_acknowledgement_LDADD = \
+  $(top_builddir)/src/chat/libgnunetchat.la \
+  $(top_builddir)/src/util/libgnunetutil.la
+
+test_chat_anonymous_SOURCES = \
+ test_chat.c
+test_chat_anonymous_LDADD = \
+  $(top_builddir)/src/chat/libgnunetchat.la \
+  $(top_builddir)/src/util/libgnunetutil.la
+
+test_chat_authentication_SOURCES = \
+ test_chat.c
+test_chat_authentication_LDADD = \
+  $(top_builddir)/src/chat/libgnunetchat.la \
+  $(top_builddir)/src/util/libgnunetutil.la
+
+test_chat_p2p_SOURCES = \
+ test_chat.c
+test_chat_p2p_LDADD = \
+  $(top_builddir)/src/chat/libgnunetchat.la \
+  $(top_builddir)/src/util/libgnunetutil.la
+
+test_chat_acknowledgement_p2p_SOURCES = \
+ test_chat.c
+test_chat_acknowledgement_p2p_LDADD = \
+  $(top_builddir)/src/chat/libgnunetchat.la \
+  $(top_builddir)/src/util/libgnunetutil.la
+
+test_chat_anonymous_p2p_SOURCES = \
+ test_chat.c
+test_chat_anonymous_p2p_LDADD = \
+  $(top_builddir)/src/chat/libgnunetchat.la \
+  $(top_builddir)/src/util/libgnunetutil.la
+
+test_chat_authentication_p2p_SOURCES = \
+ test_chat.c
+test_chat_authentication_p2p_LDADD = \
+  $(top_builddir)/src/chat/libgnunetchat.la \
+  $(top_builddir)/src/util/libgnunetutil.la
+
+test_chat_private_SOURCES = \
+ test_chat_private.c
+test_chat_private_LDADD = \
+  $(top_builddir)/src/chat/libgnunetchat.la \
+  $(top_builddir)/src/util/libgnunetutil.la
+
+test_chat_private_p2p_SOURCES = \
+ test_chat_private.c
+test_chat_private_p2p_LDADD = \
+  $(top_builddir)/src/chat/libgnunetchat.la \
+  $(top_builddir)/src/util/libgnunetutil.la
+
+EXTRA_DIST = \
+  test_chat_data.conf \
+  test_chat_peer1.conf \
+  test_chat_peer2.conf \
+  test_chat_peer3.conf

Modified: gnunet/src/chat/chat.c
===================================================================
--- gnunet/src/chat/chat.c      2011-02-06 23:25:56 UTC (rev 14363)
+++ gnunet/src/chat/chat.c      2011-02-06 23:27:01 UTC (rev 14364)
@@ -32,12 +32,12 @@
 #include "gnunet_signatures.h"
 #include "chat.h"
 
-#define DEBUG_CHAT GNUNET_YES
+#define DEBUG_CHAT GNUNET_NO
 #define NICK_IDENTITY_PREFIX ".chat_identity_"
 
 
 /**
- * Handle for a (joined) chat room.
+ * Handle for a chat room.
  */
 struct GNUNET_CHAT_Room
 {
@@ -53,6 +53,12 @@
 
   struct MemberList *members;
 
+  int is_joined;
+
+  GNUNET_CHAT_JoinCallback join_callback;
+
+  void *join_callback_cls;
+
   GNUNET_CHAT_MessageCallback message_callback;
 
   void *message_callback_cls;
@@ -221,7 +227,9 @@
   struct JoinNotificationMessage *join_msg;
   struct ReceiveNotificationMessage *received_msg;
   struct ConfirmationReceiptMessage *receipt;
+  struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pkey;
   GNUNET_HashCode id;
+  const GNUNET_HashCode *sender;
   struct GNUNET_CONTAINER_MetaData *meta;
   struct GNUNET_CHAT_SendReceiptContext *src;
   struct MemberList *pos;
@@ -261,11 +269,30 @@
                          sizeof (struct 
GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
                          &pos->id);
       GNUNET_PSEUDONYM_add (room->cfg, &pos->id, meta);
+      pos->next = room->members;
+      room->members = pos;
+      if (GNUNET_NO == room->is_joined)
+       {
+         GNUNET_CRYPTO_rsa_key_get_public (room->my_private_key, &pkey);
+         if (0 == memcmp (&join_msg->public_key,
+                          &pkey,
+                          sizeof (struct 
GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded)))
+           {
+             room->join_callback (room->join_callback_cls);
+             room->is_joined = GNUNET_YES;
+           }
+         else
+           {
+             GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                         _("The current user must be the the first one 
joined\n"));
+             GNUNET_break (0);
+             return;
+           }
+       }
+      else 
       room->member_list_callback (room->member_list_callback_cls,
                                  meta, &join_msg->public_key,
                                  ntohl (join_msg->msg_options));
-      pos->next = room->members;
-      room->members = pos;
       break;
     case GNUNET_MESSAGE_TYPE_CHAT_LEAVE_NOTIFICATION:
 #if DEBUG_CHAT
@@ -347,6 +374,13 @@
          memcpy (message_content, &received_msg[1], msg_len);
        }
       message_content[msg_len] = '\0';
+      if (0 != (ntohl (received_msg->msg_options) & GNUNET_CHAT_MSG_ANONYMOUS))
+       {
+         sender = NULL;
+         meta = NULL;
+       }
+      else
+       {
       pos = room->members;
       while ((NULL != pos) &&
             (0 != memcmp (&pos->id,
@@ -354,11 +388,15 @@
                           sizeof (GNUNET_HashCode))))
        pos = pos->next;
       GNUNET_assert (NULL != pos);
+         sender = &received_msg->sender;
+         meta = pos->meta;
+       }
       room->message_callback (room->message_callback_cls,
                              room,
-                             &received_msg->sender,
-                             pos->meta,
+                             sender,
+                             meta,
                              message_content,
+                             GNUNET_TIME_absolute_ntoh 
(received_msg->timestamp),
                              ntohl (received_msg->msg_options));
       if (message_content != decrypted_msg)
        GNUNET_free (message_content);
@@ -378,9 +416,7 @@
                                     room,
                                     ntohl (receipt->sequence_number),
                                     GNUNET_TIME_absolute_ntoh 
(receipt->timestamp),
-                                    &receipt->target,
-                                    &receipt->content,
-                                    &receipt->signature);
+                                    &receipt->target);
       break;
     default:
       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
@@ -510,8 +546,9 @@
     {
 #if DEBUG_CHAT
       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                 "Could not transmit join request\n");
+                 "Could not transmit join request, retrying...\n");
 #endif
+      GNUNET_CHAT_rejoin_room (chat_room);
       return 0;
     }
 #if DEBUG_CHAT
@@ -542,6 +579,10 @@
                  _("Could not serialize metadata\n"));
       return 0;
     }
+  GNUNET_CLIENT_receive (chat_room->client,
+                        &receive_results,
+                        chat_room,
+                        GNUNET_TIME_UNIT_FOREVER_REL);
   return size_of_join;
 }
 
@@ -623,6 +664,8 @@
                       struct GNUNET_CONTAINER_MetaData *member_info,
                       const char *room_name,
                       enum GNUNET_CHAT_MsgOptions msg_options,
+                      GNUNET_CHAT_JoinCallback joinCallback,
+                      void *join_cls,
                       GNUNET_CHAT_MessageCallback messageCallback,
                       void *message_cls,
                       GNUNET_CHAT_MemberListCallback memberCallback,
@@ -654,11 +697,32 @@
                  _("Failed to connect to the chat service\n"));
       return NULL;
     }
+  if (NULL == joinCallback)
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                 _("Undefined mandatory parameter: joinCallback\n"));
+      return NULL;
+    }
+  if (NULL == messageCallback)
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                 _("Undefined mandatory parameter: messageCallback\n"));
+      return NULL;
+    }
+  if (NULL == memberCallback)
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                 _("Undefined mandatory parameter: memberCallback\n"));
+      return NULL;
+    }
   chat_room = GNUNET_malloc (sizeof (struct GNUNET_CHAT_Room));
   chat_room->msg_options = msg_options;
   chat_room->room_name = GNUNET_strdup (room_name);
   chat_room->member_info = GNUNET_CONTAINER_meta_data_duplicate (member_info);
   chat_room->my_private_key = priv_key;
+  chat_room->is_joined = GNUNET_NO;
+  chat_room->join_callback = joinCallback;
+  chat_room->join_callback_cls = join_cls;
   chat_room->message_callback = messageCallback;
   chat_room->message_callback_cls = message_cls;
   chat_room->member_list_callback = memberCallback;
@@ -668,10 +732,6 @@
   chat_room->cfg = cfg;
   chat_room->client = client;
   chat_room->members = NULL;
-  GNUNET_CLIENT_receive (client,
-                        &receive_results,
-                        chat_room,
-                        GNUNET_TIME_UNIT_FOREVER_REL);
   if (GNUNET_SYSERR == GNUNET_CHAT_rejoin_room (chat_room))
     {
       GNUNET_CHAT_leave_room (chat_room);
@@ -717,6 +777,8 @@
   msg_to_send->header.type = htons (GNUNET_MESSAGE_TYPE_CHAT_TRANSMIT_REQUEST);
   msg_to_send->msg_options = htonl (smc->options);
   msg_to_send->sequence_number = htonl (smc->sequence_number);
+  msg_to_send->timestamp =
+    GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_get ());
   msg_to_send->reserved = htonl (0);
   if (NULL == smc->receiver)
     memset (&msg_to_send->target, 0, sizeof (GNUNET_HashCode));
@@ -770,13 +832,15 @@
 #if DEBUG_CHAT
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending a message\n");
 #endif
-  *sequence_number = ++room->sequence_number;
+  room->sequence_number++;
+  if (NULL != sequence_number)
+    *sequence_number = room->sequence_number;
   smc = GNUNET_malloc (sizeof (struct GNUNET_CHAT_SendMessageContext));
   smc->chat_room = room;
   smc->message = GNUNET_strdup (message);
   smc->options = options;
   smc->receiver = receiver;
-  smc->sequence_number = *sequence_number;
+  smc->sequence_number = room->sequence_number;
   msg_size = strlen (message) + sizeof (struct TransmitRequestMessage);
   GNUNET_CLIENT_notify_transmit_ready (room->client,
                                       msg_size,

Modified: gnunet/src/chat/chat.h
===================================================================
--- gnunet/src/chat/chat.h      2011-02-06 23:25:56 UTC (rev 14363)
+++ gnunet/src/chat/chat.h      2011-02-06 23:27:01 UTC (rev 14364)
@@ -70,8 +70,13 @@
   uint32_t reserved GNUNET_PACKED;
 
   /**
+   * Timestamp of the message.
+   */
+  struct GNUNET_TIME_AbsoluteNBO timestamp;
+
+  /**
    * Hash of the public key of the pseudonym of the sender of the message.
-   * TBD: Should be all zeros for anonymous.
+   * Should be all zeros for anonymous.
    */
   GNUNET_HashCode sender;
 
@@ -122,6 +127,11 @@
   uint32_t sequence_number GNUNET_PACKED;
 
   /**
+   * Timestamp of the message.
+   */
+  struct GNUNET_TIME_AbsoluteNBO timestamp;
+
+  /**
    * Who should receive this message?  Set to all zeros for "everyone".
    */
   GNUNET_HashCode target;
@@ -131,12 +141,15 @@
 
 /**
  * Receipt sent from a message receiver to the service to confirm delivery of
- * a chat message.
+ * a chat message and from the service to sender of the original message to
+ * acknowledge delivery.
  */
 struct ConfirmationReceiptMessage
 {
   /**
-   * Message type will be GNUNET_MESSAGE_TYPE_CHAT_CONFIRMATION_RECEIPT 
+   * Message type will be
+   * GNUNET_MESSAGE_TYPE_CHAT_CONFIRMATION_RECEIPT when sending from client,
+   * GNUNET_MESSAGE_TYPE_CHAT_CONFIRMATION_NOTIFICATION when sending to client.
    */
   struct GNUNET_MessageHeader header;
 
@@ -351,8 +364,9 @@
 
 /**
  * Message send by one peer to another to indicate receiving of a chat message.
- * After this struct, the remaining bytes are the actual text message.  If the
- * mesasge is private, then the text is encrypted, otherwise it's plaintext.
+ * This struct is followed by the room name (only if the message is anonymous)
+ * and then the remaining bytes are the actual text message.  If the mesasge is
+ * private, then the text is encrypted, otherwise it's plaintext.
  */
 struct P2PReceiveNotificationMessage
 {
@@ -372,13 +386,23 @@
   uint32_t sequence_number GNUNET_PACKED;
 
   /**
+   * Length of the room name. This is only used for anonymous messages.
+   */
+  uint16_t room_name_len GNUNET_PACKED;
+
+  /**
    * Reserved (for alignment).
    */
-  uint32_t reserved GNUNET_PACKED;
+  uint16_t reserved GNUNET_PACKED;
 
   /**
+   * Timestamp of the message.
+   */
+  struct GNUNET_TIME_AbsoluteNBO timestamp;
+
+  /**
    * Hash of the public key of the pseudonym of the sender of the message
-   * TBD: Should be all zeros for anonymous.
+   * Should be all zeros for anonymous.
    */
   GNUNET_HashCode sender;
 

Modified: gnunet/src/chat/gnunet-chat.c
===================================================================
--- gnunet/src/chat/gnunet-chat.c       2011-02-06 23:25:56 UTC (rev 14363)
+++ gnunet/src/chat/gnunet-chat.c       2011-02-06 23:27:01 UTC (rev 14364)
@@ -78,6 +78,20 @@
 
 
 /**
+ * Callback used for notification that we have joined the room.
+ *
+ * @param cls closure
+ * @return GNUNET_OK
+ */
+static int
+join_cb (void *cls)
+{
+  fprintf (stdout, _("Joined\n"));
+  return GNUNET_OK;
+}
+
+
+/**
  * Callback used for notification about incoming messages.
  *
  * @param cls closure, NULL
@@ -93,11 +107,13 @@
 receive_cb (void *cls,
            struct GNUNET_CHAT_Room *room,
            const GNUNET_HashCode *sender,
-           const struct GNUNET_CONTAINER_MetaData *meta,
+           const struct GNUNET_CONTAINER_MetaData *member_info,
            const char *message,
+           struct GNUNET_TIME_Absolute timestamp,
            enum GNUNET_CHAT_MsgOptions options)
 {
   char *nick;
+  char *time;
   const char *fmt;
 
   if (NULL != sender)
@@ -109,43 +125,43 @@
     {
     case GNUNET_CHAT_MSG_OPTION_NONE:
     case GNUNET_CHAT_MSG_ANONYMOUS:
-      fmt = _("`%s' said: %s\n");
+      fmt = _("(%s) `%s' said: %s\n");
       break;
     case GNUNET_CHAT_MSG_PRIVATE:
-      fmt = _("`%s' said to you: %s\n");
+      fmt = _("(%s) `%s' said to you: %s\n");
       break;
     case GNUNET_CHAT_MSG_PRIVATE | GNUNET_CHAT_MSG_ANONYMOUS:
-      fmt = _("`%s' said to you: %s\n");
+      fmt = _("(%s) `%s' said to you: %s\n");
       break;
     case GNUNET_CHAT_MSG_AUTHENTICATED:
-      fmt = _("`%s' said for sure: %s\n");
+      fmt = _("(%s) `%s' said for sure: %s\n");
       break;
     case GNUNET_CHAT_MSG_PRIVATE | GNUNET_CHAT_MSG_AUTHENTICATED:
-      fmt = _("`%s' said to you for sure: %s\n");
+      fmt = _("(%s) `%s' said to you for sure: %s\n");
       break;
     case GNUNET_CHAT_MSG_ACKNOWLEDGED:
-      fmt = _("`%s' was confirmed that you received: %s\n");
+      fmt = _("(%s) `%s' was confirmed that you received: %s\n");
       break;
     case GNUNET_CHAT_MSG_PRIVATE | GNUNET_CHAT_MSG_ACKNOWLEDGED:
-      fmt = _("`%s' was confirmed that you and only you received: %s\n");
+      fmt = _("(%s) `%s' was confirmed that you and only you received: %s\n");
       break;
     case GNUNET_CHAT_MSG_AUTHENTICATED | GNUNET_CHAT_MSG_ACKNOWLEDGED:
-      fmt = _("`%s' was confirmed that you received from him or her: %s\n");
+      fmt = _("(%s) `%s' was confirmed that you received from him or her: 
%s\n");
       break;
     case GNUNET_CHAT_MSG_AUTHENTICATED | GNUNET_CHAT_MSG_PRIVATE | 
GNUNET_CHAT_MSG_ACKNOWLEDGED:
-      fmt =
-       _
-       ("`%s' was confirmed that you and only you received from him or her: 
%s\n");
+      fmt = _("(%s) `%s' was confirmed that you and only you received from him 
or her: %s\n");
       break;
     case GNUNET_CHAT_MSG_OFF_THE_RECORD:
-      fmt = _("`%s' said off the record: %s\n");
+      fmt = _("(%s) `%s' said off the record: %s\n");
       break;
     default:
-      fmt = _("<%s> said using an unknown message type: %s\n");
+      fmt = _("(%s) <%s> said using an unknown message type: %s\n");
       break;
     }
-  fprintf (stdout, fmt, nick, message);
+  time = GNUNET_STRINGS_absolute_time_to_string (timestamp);
+  fprintf (stdout, fmt, time, nick, message);
   GNUNET_free (nick);
+  GNUNET_free (time);
   return GNUNET_OK;
 }
 
@@ -168,9 +184,7 @@
                 struct GNUNET_CHAT_Room *room,
                 uint32_t orig_seq_number,
                 struct GNUNET_TIME_Absolute timestamp,
-                const GNUNET_HashCode *receiver,
-                const GNUNET_HashCode *msg_hash,
-                const struct GNUNET_CRYPTO_RsaSignature *receipt)
+                const GNUNET_HashCode *receiver)
 {
   char *nick;
 
@@ -248,18 +262,6 @@
 
 
 static int
-do_transmit (const char *msg, const void *xtra)
-{
-  uint32_t seq;
-  GNUNET_CHAT_send_message (room,
-                           msg,
-                           GNUNET_CHAT_MSG_OPTION_NONE,
-                           NULL, &seq);
-  return GNUNET_OK;
-}
-
-
-static int
 do_join (const char *arg, const void *xtra)
 {
   char *my_name;
@@ -276,6 +278,7 @@
                                meta,
                                room_name,
                                -1,
+                               &join_cb, NULL,
                                &receive_cb, NULL,
                                &member_list_cb, NULL,
                                &confirmation_cb, NULL, &me);
@@ -285,7 +288,7 @@
       return GNUNET_SYSERR;
     }
   my_name = GNUNET_PSEUDONYM_id_to_name (cfg, &me);
-  fprintf (stdout, _("Joined room `%s' as user `%s'\n"), room_name, my_name);
+  fprintf (stdout, _("Joining room `%s' as user `%s'...\n"), room_name, 
my_name);
   GNUNET_free (my_name);
   return GNUNET_OK;
 }
@@ -315,6 +318,7 @@
                                meta,
                                room_name,
                                -1,
+                               &join_cb, NULL,
                                &receive_cb, NULL,
                                &member_list_cb, NULL,
                                &confirmation_cb, NULL, &me);
@@ -355,8 +359,20 @@
 
 
 static int
-do_pm (const char *msg, const void *xtra)
+do_send (const char *msg, const void *xtra)
 {
+  uint32_t seq;
+  GNUNET_CHAT_send_message (room,
+                           msg,
+                           GNUNET_CHAT_MSG_OPTION_NONE,
+                           NULL, &seq);
+  return GNUNET_OK;
+}
+
+
+static int
+do_send_pm (const char *msg, const void *xtra)
+{
   char *user;
   GNUNET_HashCode uid;
   GNUNET_HashCode pid;
@@ -404,7 +420,7 @@
 
 
 static int
-do_transmit_sig (const char *msg, const void *xtra)
+do_send_sig (const char *msg, const void *xtra)
 {
   uint32_t seq;
   GNUNET_CHAT_send_message (room,
@@ -416,7 +432,7 @@
 
 
 static int
-do_transmit_ack (const char *msg, const void *xtra)
+do_send_ack (const char *msg, const void *xtra)
 {
   uint32_t seq;
   GNUNET_CHAT_send_message (room,
@@ -428,6 +444,18 @@
 
 
 static int
+do_send_anonymous (const char *msg, const void *xtra)
+{
+  uint32_t seq;
+  GNUNET_CHAT_send_message (room,
+                           msg,
+                           GNUNET_CHAT_MSG_ANONYMOUS,
+                           NULL, &seq);
+  return GNUNET_OK;
+}
+
+
+static int
 do_quit (const char *args, const void *xtra)
 {
   return GNUNET_SYSERR;
@@ -454,19 +482,24 @@
    gettext_noop
    ("Use `/nick nickname' to change your nickname.  This will cause you to"
     " leave the current room and immediately rejoin it with the new name.")},
-  {"/msg ", &do_pm,
+  {"/msg ", &do_send_pm,
    gettext_noop
    ("Use `/msg nickname message' to send a private message to the specified"
     " user")},
-  {"/notice ", &do_pm,
+  {"/notice ", &do_send_pm,
    gettext_noop ("The `/notice' command is an alias for `/msg'")},
-  {"/query ", &do_pm,
+  {"/query ", &do_send_pm,
    gettext_noop ("The `/query' command is an alias for `/msg'")},
-  {"/sig ", &do_transmit_sig,
+  {"/sig ", &do_send_sig,
    gettext_noop ("Use `/sig message' to send a signed public message")},
-  {"/ack ", &do_transmit_ack,
+  {"/ack ", &do_send_ack,
    gettext_noop
    ("Use `/ack message' to require signed acknowledgment of the message")},
+  {"/anonymous ", &do_send_anonymous,
+   gettext_noop
+   ("Use `/anonymous message' to send a public anonymous message")},
+  {"/anon ", &do_send_anonymous,
+   gettext_noop ("The `/anon' command is an alias for `/anonymous'")},
   {"/quit", &do_quit,
    gettext_noop ("Use `/quit' to terminate gnunet-chat")},
   {"/leave", &do_quit,
@@ -479,13 +512,9 @@
   /* Add standard commands:
      /whois (print metadata),
      /ignore (set flag, check on receive!) */
-  /* Add special commands (currently supported):
-     + anonymous msgs
-     + authenticated msgs
-   */
   /* the following three commands must be last! */
   {"/", &do_unknown, NULL},
-  {"", &do_transmit, NULL},
+  {"", &do_send, NULL},
   {NULL, NULL, NULL},
 };
 
@@ -615,6 +644,7 @@
                                meta,
                                room_name,
                                -1,
+                               &join_cb, NULL,
                                &receive_cb, NULL,
                                &member_list_cb, NULL,
                                &confirmation_cb, NULL, &me);
@@ -628,7 +658,7 @@
       return;
     }
   my_name = GNUNET_PSEUDONYM_id_to_name (cfg, &me);
-  fprintf (stdout, _("Joined room `%s' as user `%s'\n"), room_name, my_name);
+  fprintf (stdout, _("Joining room `%s' as user `%s'...\n"), room_name, 
my_name);
   GNUNET_free (my_name);
   handle_cmd_task =
     GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_UI,

Modified: gnunet/src/chat/gnunet-service-chat.c
===================================================================
--- gnunet/src/chat/gnunet-service-chat.c       2011-02-06 23:25:56 UTC (rev 
14363)
+++ gnunet/src/chat/gnunet-service-chat.c       2011-02-06 23:27:01 UTC (rev 
14364)
@@ -33,9 +33,10 @@
 #include "gnunet_signatures.h"
 #include "chat.h"
 
-#define DEBUG_CHAT_SERVICE GNUNET_YES
+#define DEBUG_CHAT_SERVICE GNUNET_NO
 #define MAX_TRANSMIT_DELAY GNUNET_TIME_relative_multiply 
(GNUNET_TIME_UNIT_SECONDS, 60)
 #define QUEUE_SIZE 16
+#define MAX_ANONYMOUS_MSG_LIST_LENGTH 16
 
 
 /**
@@ -93,7 +94,21 @@
 
 };
 
+/**
+ * Linked list of recent anonymous messages.
+ */
+struct AnonymousMessage
+{
+  struct AnonymousMessage *next;
 
+  /**
+   * Hash of the message.
+   */
+  GNUNET_HashCode hash;
+
+};
+
+
 /**
  * Handle to the core service (NULL until we've connected to it).
  */
@@ -119,7 +134,57 @@
  */
 struct GNUNET_SERVER_NotificationContext *nc = NULL;
 
+/**
+ * Head of the list of recent anonymous messages.
+ */
+static struct AnonymousMessage *anonymous_list_head = NULL;
+ 
 
+static void
+remember_anonymous_message (const struct P2PReceiveNotificationMessage 
*p2p_rnmsg)
+{
+  static GNUNET_HashCode hash;
+  struct AnonymousMessage *anon_msg;
+  struct AnonymousMessage *prev;
+  int anon_list_len;
+
+  GNUNET_CRYPTO_hash (p2p_rnmsg, ntohs (p2p_rnmsg->header.size), &hash);
+  anon_msg = GNUNET_malloc (sizeof (struct AnonymousMessage));
+  anon_msg->hash = hash;
+  anon_msg->next = anonymous_list_head;
+  anonymous_list_head = anon_msg;
+  anon_list_len = 1;
+  prev = NULL;
+  while ((NULL != anon_msg->next))
+    {
+      prev = anon_msg;
+      anon_msg = anon_msg->next;
+      anon_list_len++;
+    }
+  if (anon_list_len == MAX_ANONYMOUS_MSG_LIST_LENGTH)
+    {
+      GNUNET_free (anon_msg);
+      if (NULL != prev)
+       prev->next = NULL;
+    }
+}
+
+
+static int
+lookup_anonymous_message (const struct P2PReceiveNotificationMessage 
*p2p_rnmsg)
+{
+  static GNUNET_HashCode hash;
+  struct AnonymousMessage *anon_msg;
+
+  GNUNET_CRYPTO_hash (p2p_rnmsg, ntohs (p2p_rnmsg->header.size), &hash);
+  anon_msg = anonymous_list_head;
+  while ((NULL != anon_msg) &&
+        (0 != memcmp (&anon_msg->hash, &hash, sizeof (GNUNET_HashCode))))
+    anon_msg = anon_msg->next;
+  return (NULL != anon_msg);
+}
+
+
 /**
  * Transmit a message notification to the peer.
  *
@@ -141,9 +206,17 @@
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
              "Transmitting P2P message notification\n");
 #endif
+  if (buf == NULL)
+    {
+      /* client disconnected */
+#if DEBUG_CHAT_SERVICE
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                 "Buffer is NULL, dropping the message\n");
+#endif
+      return 0;
+    }
   msg_size = ntohs (my_msg->header.size);
   GNUNET_assert (size >= msg_size);
-  GNUNET_assert (NULL != buf);
   memcpy (m, my_msg, msg_size);
   GNUNET_free (my_msg);
   return msg_size;
@@ -205,8 +278,10 @@
   struct GNUNET_CRYPTO_AesSessionKey key;
   char encrypted_msg[MAX_MESSAGE_LENGTH];
   const char *room;
+  size_t room_len;
   int msg_len;
-  int priv_msg;
+  int is_priv;
+  int is_anon;
 
   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Client sent a chat message\n");
   if (ntohs (message->size) <= sizeof (struct TransmitRequestMessage))
@@ -218,8 +293,8 @@
     }
   trmsg = (const struct TransmitRequestMessage *) message;
   msg_len = ntohs (trmsg->header.size) - sizeof (struct 
TransmitRequestMessage);
-  priv_msg = (ntohl (trmsg->msg_options) & GNUNET_CHAT_MSG_PRIVATE) != 0;
-  if (priv_msg)
+  is_priv = (0 != (ntohl (trmsg->msg_options) & GNUNET_CHAT_MSG_PRIVATE));
+  if (is_priv)
     {
 #if DEBUG_CHAT_SERVICE
       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Encrypting the message text\n");
@@ -244,7 +319,7 @@
                              msg_len);
   rnmsg->header.type = htons (GNUNET_MESSAGE_TYPE_CHAT_MESSAGE_NOTIFICATION);
   rnmsg->msg_options = trmsg->msg_options;
-  rnmsg->sequence_number = trmsg->sequence_number;
+  rnmsg->timestamp = trmsg->timestamp;
   pos = client_list_head;
   while ((NULL != pos) && (pos->client != client))
     pos = pos->next;
@@ -260,12 +335,19 @@
     }
   room = pos->room;
   pos->msg_sequence_number = ntohl (trmsg->sequence_number);
-  if (0 == (ntohl (trmsg->msg_options) & GNUNET_CHAT_MSG_ANONYMOUS))
-    rnmsg->sender = pos->id;
+  is_anon = (0 != (ntohl (trmsg->msg_options) & GNUNET_CHAT_MSG_ANONYMOUS));
+  if (is_anon)
+    {
+    memset (&rnmsg->sender, 0, sizeof (GNUNET_HashCode));
+      rnmsg->sequence_number = 0;
+    }
   else
-    memset (&rnmsg->sender, 0, sizeof (GNUNET_HashCode));
-  if (priv_msg)
     {
+      rnmsg->sender = pos->id;
+      rnmsg->sequence_number = trmsg->sequence_number;
+    }
+  if (is_priv)
+    {
 #if DEBUG_CHAT_SERVICE
       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                  "Encrypting the session key using the public key of '%s'\n",
@@ -323,7 +405,7 @@
          (NULL != pos->client) &&
          (pos->client != client))
        {
-         if (((!priv_msg) ||
+         if (((!is_priv) ||
               (0 == memcmp (&trmsg->target,
                             &pos->id,
                             sizeof (GNUNET_HashCode)))) &&
@@ -341,16 +423,25 @@
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
              "Broadcasting message to neighbour peers\n");
 #endif
+  if (is_anon)
+    {
+      room_len = strlen (room);
+      p2p_rnmsg = GNUNET_malloc (sizeof (struct P2PReceiveNotificationMessage) 
+
+                                msg_len + room_len);
+      p2p_rnmsg->header.size =
+       htons (sizeof (struct P2PReceiveNotificationMessage) + msg_len +
+              room_len);
+      p2p_rnmsg->room_name_len = htons (room_len);
+      memcpy ((char *) &p2p_rnmsg[1], room, room_len);
+      memcpy ((char *) &p2p_rnmsg[1] + room_len, &trmsg[1], msg_len);
+    }
+  else
+    {
   p2p_rnmsg = GNUNET_malloc (sizeof (struct P2PReceiveNotificationMessage) +
                             msg_len);
-  p2p_rnmsg->header.size = htons (sizeof (struct 
P2PReceiveNotificationMessage) +
-                                 msg_len);
-  p2p_rnmsg->header.type = htons 
(GNUNET_MESSAGE_TYPE_CHAT_P2P_MESSAGE_NOTIFICATION);
-  p2p_rnmsg->msg_options = trmsg->msg_options;
-  p2p_rnmsg->sequence_number = trmsg->sequence_number;
-  memcpy (&p2p_rnmsg->sender, &rnmsg->sender, sizeof (GNUNET_HashCode));
-  p2p_rnmsg->target = trmsg->target;
-  if (priv_msg)
+      p2p_rnmsg->header.size =
+       htons (sizeof (struct P2PReceiveNotificationMessage) + msg_len);
+      if (is_priv)
     {
       memcpy (&p2p_rnmsg[1], encrypted_msg, msg_len);
       memcpy (&p2p_rnmsg->encrypted_key,
@@ -358,9 +449,17 @@
              sizeof (struct GNUNET_CRYPTO_RsaEncryptedData));
     }
   else
-    {
       memcpy (&p2p_rnmsg[1], &trmsg[1], msg_len);
     }
+  p2p_rnmsg->header.type = htons 
(GNUNET_MESSAGE_TYPE_CHAT_P2P_MESSAGE_NOTIFICATION);
+  p2p_rnmsg->msg_options = trmsg->msg_options;
+  p2p_rnmsg->sequence_number = trmsg->sequence_number;
+  p2p_rnmsg->timestamp = trmsg->timestamp;
+  p2p_rnmsg->reserved = 0;
+  p2p_rnmsg->sender = rnmsg->sender;
+  p2p_rnmsg->target = trmsg->target;
+  if (is_anon)
+    remember_anonymous_message (p2p_rnmsg);
   GNUNET_CORE_iterate_peers (cfg,
                             &send_message_noficiation,
                             p2p_rnmsg);
@@ -591,9 +690,17 @@
              "Transmitting P2P confirmation receipt to '%s'\n",
              GNUNET_h2s (&receipt->target));
 #endif
+  if (buf == NULL)
+    {
+      /* client disconnected */
+#if DEBUG_CHAT_SERVICE
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                 "Buffer is NULL, dropping the message\n");
+#endif
+      return 0;
+    }
   msg_size = sizeof (struct P2PConfirmationReceiptMessage);
   GNUNET_assert (size >= msg_size);
-  GNUNET_assert (NULL != buf);
   memcpy (buf, receipt, msg_size);
   GNUNET_free (receipt);
   return msg_size;
@@ -765,9 +872,17 @@
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
              "Transmitting P2P leave notification\n");
 #endif
+  if (buf == NULL)
+    {
+      /* client disconnected */
+#if DEBUG_CHAT_SERVICE
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                 "Buffer is NULL, dropping the message\n");
+#endif
+      return 0;
+    }
   msg_size = sizeof (struct P2PLeaveNotificationMessage);
   GNUNET_assert (size >= msg_size);
-  GNUNET_assert (NULL != buf);
   m = buf;
   m->header.type = htons (GNUNET_MESSAGE_TYPE_CHAT_P2P_LEAVE_NOTIFICATION);
   m->header.size = htons (msg_size);
@@ -787,7 +902,6 @@
                        const struct GNUNET_TRANSPORT_ATS_Information *atsi)
 {
   struct ChatClient *entry = cls;
-  struct GNUNET_CORE_TransmitHandle *th;
   struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *public_key;
   size_t msg_size;
 
@@ -806,14 +920,15 @@
       msg_size = sizeof (struct P2PLeaveNotificationMessage);
       public_key = GNUNET_memdup (&entry->public_key,
                                  sizeof (struct 
GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded));
-      th = GNUNET_CORE_notify_transmit_ready (core,
+      if (NULL == GNUNET_CORE_notify_transmit_ready (core,
                                              1,
                                              MAX_TRANSMIT_DELAY,
                                              peer,
                                              msg_size,
                                              
&transmit_leave_notification_to_peer,
-                                             public_key);
-      GNUNET_assert (NULL != th);
+                                                    public_key))
+       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                   _("Failed to queue a leave notification\n"));
     }
 }
 
@@ -1108,8 +1223,12 @@
   struct ChatClient *sender;
   struct ChatClient *pos;
   static GNUNET_HashCode all_zeros;
-  int priv_msg;
+  int is_priv;
+  int is_anon;
   uint16_t msg_len;
+  uint16_t room_name_len;
+  char *room_name = NULL;
+  char *text;
 
   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Got P2P message notification\n");
   if (ntohs (message->size) <= sizeof (struct P2PReceiveNotificationMessage))
@@ -1119,6 +1238,37 @@
       return GNUNET_SYSERR;
     }
   p2p_rnmsg = (const struct P2PReceiveNotificationMessage *) message;
+  msg_len = ntohs (p2p_rnmsg->header.size) -
+    sizeof (struct P2PReceiveNotificationMessage);
+
+  is_anon = (0 != (ntohl (p2p_rnmsg->msg_options) & 
GNUNET_CHAT_MSG_ANONYMOUS));
+  if (is_anon)
+    {
+      room_name_len = ntohs (p2p_rnmsg->room_name_len);
+      if (msg_len <= room_name_len)
+       {
+         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                     "Malformed message: wrong length of the room name\n");
+         GNUNET_break_op (0);
+         return GNUNET_SYSERR;
+       }
+      msg_len -= room_name_len;
+      if (lookup_anonymous_message (p2p_rnmsg))
+       {
+#if DEBUG_CHAT_SERVICE
+         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                     "This anonymous message has already been handled.");
+#endif
+         return GNUNET_OK;
+       }
+      remember_anonymous_message (p2p_rnmsg);
+      room_name = GNUNET_malloc (room_name_len + 1);
+      memcpy (room_name, (char *) &p2p_rnmsg[1], room_name_len);
+      room_name[room_name_len] = '\0';
+      text = (char *) &p2p_rnmsg[1] + room_name_len;
+    }
+  else
+    {
   sender = client_list_head;
   while ((NULL != sender) &&
         (0 != memcmp (&sender->id,
@@ -1127,10 +1277,13 @@
     sender = sender->next;
   if (NULL == sender)
     {
-      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+         /* not an error since the sender may have left before we got the
+            message */
+#if DEBUG_CHAT_SERVICE
+         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                  "Unknown source. Rejecting the message\n");
-      GNUNET_break_op (0);
-      return GNUNET_SYSERR;
+#endif
+         return GNUNET_OK;
     }
   if (sender->msg_sequence_number >= ntohl (p2p_rnmsg->sequence_number))
     {
@@ -1138,15 +1291,19 @@
       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                  "This message has already been handled."
                  " Sequence numbers (msg/sender): %u/%u\n",
-                 ntohl (p2p_rnmsg->sequence_number), 
sender->msg_sequence_number);
+                     ntohl (p2p_rnmsg->sequence_number),
+                     sender->msg_sequence_number);
 #endif
       return GNUNET_OK;
     }
   sender->msg_sequence_number = ntohl (p2p_rnmsg->sequence_number);
-  msg_len = ntohs (p2p_rnmsg->header.size) -
-    sizeof (struct P2PReceiveNotificationMessage);
+      room_name = sender->room;
+      text = (char *) &p2p_rnmsg[1];
+    }
+
 #if DEBUG_CHAT_SERVICE
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending message to local room 
members\n");
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+             "Sending message to local room members\n");
 #endif
   rnmsg = GNUNET_malloc (sizeof (struct ReceiveNotificationMessage) + msg_len);
   rnmsg->header.size = htons (sizeof (struct ReceiveNotificationMessage) +
@@ -1154,21 +1311,22 @@
   rnmsg->header.type = htons (GNUNET_MESSAGE_TYPE_CHAT_MESSAGE_NOTIFICATION);
   rnmsg->msg_options = p2p_rnmsg->msg_options;
   rnmsg->sequence_number = p2p_rnmsg->sequence_number;
-  priv_msg = (0 != memcmp (&all_zeros,
+  rnmsg->timestamp = p2p_rnmsg->timestamp;
+  is_priv = (0 != memcmp (&all_zeros,
                           &p2p_rnmsg->target, sizeof (GNUNET_HashCode)));
-  if (priv_msg)
+  if (is_priv)
     memcpy (&rnmsg->encrypted_key,
            &p2p_rnmsg->encrypted_key,
            sizeof (struct GNUNET_CRYPTO_RsaEncryptedData));
-  memcpy (&rnmsg->sender, &p2p_rnmsg->sender, sizeof (GNUNET_HashCode));
-  memcpy (&rnmsg[1], &p2p_rnmsg[1], msg_len);
+  rnmsg->sender = p2p_rnmsg->sender;
+  memcpy (&rnmsg[1], text, msg_len);
   pos = client_list_head;
   while (NULL != pos)
     {
-      if ((0 == strcmp (sender->room, pos->room)) &&
+      if ((0 == strcmp (room_name, pos->room)) &&
          (NULL != pos->client))
        {
-         if (((!priv_msg) ||
+         if (((!is_priv) ||
               (0 == memcmp (&p2p_rnmsg->target,
                             &pos->id,
                             sizeof (GNUNET_HashCode)))) &&
@@ -1182,6 +1340,8 @@
        }
       pos = pos->next;
     }
+  if (is_anon)
+    GNUNET_free (room_name);
 #if DEBUG_CHAT_SERVICE
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
              "Broadcasting message notification to neighbour peers\n");
@@ -1441,6 +1601,9 @@
 cleanup_task (void *cls,
              const struct GNUNET_SCHEDULER_TaskContext *tc)
 {
+  struct AnonymousMessage *next_msg;
+  struct ChatClient *next_client;
+
   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Cleaning up\n");
   if (NULL != core)
     {
@@ -1452,6 +1615,20 @@
       GNUNET_SERVER_notification_context_destroy (nc);
       nc = NULL;
     }
+  while (NULL != client_list_head)
+    {
+      next_client = client_list_head->next;
+      GNUNET_free (client_list_head->room);
+      GNUNET_free_non_null (client_list_head->member_info);
+      GNUNET_free (client_list_head);
+      client_list_head = next_client;
+    }
+  while (NULL != anonymous_list_head)
+    {
+      next_msg = anonymous_list_head->next;
+      GNUNET_free (anonymous_list_head);
+      anonymous_list_head = next_msg;
+    }
 }
 
 

Modified: gnunet/src/core/core.h
===================================================================
--- gnunet/src/core/core.h      2011-02-06 23:25:56 UTC (rev 14363)
+++ gnunet/src/core/core.h      2011-02-06 23:27:01 UTC (rev 14364)
@@ -30,7 +30,7 @@
 /**
  * General core debugging.
  */
-#define DEBUG_CORE GNUNET_NO
+#define DEBUG_CORE GNUNET_YES
 
 /**
  * Debugging interaction core-clients.

Modified: gnunet/src/include/gnunet_chat_service.h
===================================================================
--- gnunet/src/include/gnunet_chat_service.h    2011-02-06 23:25:56 UTC (rev 
14363)
+++ gnunet/src/include/gnunet_chat_service.h    2011-02-06 23:27:01 UTC (rev 
14364)
@@ -89,6 +89,14 @@
 struct GNUNET_CHAT_Room;
 
 /**
+ * Callback used for notification that we have joined the room.
+ *
+ * @param cls closure
+ * @return GNUNET_OK
+ */
+typedef int (*GNUNET_CHAT_JoinCallback) (void *cls);
+
+/**
  * Callback used for notification about incoming messages.
  *
  * @param cls closure
@@ -96,6 +104,7 @@
  * @param sender what is the ID of the sender? (maybe NULL)
  * @param member_info information about the joining member
  * @param message the message text
+ * @param timestamp when was the message sent?
  * @param options options for the message
  * @return GNUNET_OK to accept the message now, GNUNET_NO to
  *         accept (but user is away), GNUNET_SYSERR to signal denied delivery
@@ -105,11 +114,13 @@
                                            const GNUNET_HashCode *sender,
                                            const struct 
GNUNET_CONTAINER_MetaData *member_info,
                                            const char *message,
+                                           struct GNUNET_TIME_Absolute 
timestamp,
                                            enum GNUNET_CHAT_MsgOptions 
options);
 
 /**
  * Callback used for notification that another room member has joined or left.
  *
+ * @param cls closure
  * @param member_info will be non-null if the member is joining, NULL if he is
  *        leaving
  * @param member_id hash of public key of the user (for unique identification)
@@ -129,8 +140,6 @@
  * @param orig_seq_number sequence number of the original message
  * @param timestamp when was the message received?
  * @param receiver who is confirming the receipt?
- * @param msg_hash hash of the original message
- * @param receipt signature confirming delivery
  * @return GNUNET_OK to continue, GNUNET_SYSERR to refuse processing further
  *         confirmations from anyone for this message
  */
@@ -138,9 +147,7 @@
                                                struct GNUNET_CHAT_Room *room,
                                                uint32_t orig_seq_number,
                                                struct GNUNET_TIME_Absolute 
timestamp,
-                                               const GNUNET_HashCode *receiver,
-                                               const GNUNET_HashCode *msg_hash,
-                                               const struct 
GNUNET_CRYPTO_RsaSignature *receipt);
+                                               const GNUNET_HashCode 
*receiver);
 
 /**
  * Join a chat room.
@@ -153,6 +160,8 @@
  * @param member_info information about the joining member
  * @param room_name name of the room
  * @param msg_options message options of the joining user
+ * @param joinCallback which function to call when we've joined the room
+ * @param join_cls argument to callback
  * @param messageCallback which function to call if a message has
  *        been received?
  * @param message_cls argument to callback
@@ -170,6 +179,8 @@
                       struct GNUNET_CONTAINER_MetaData *member_info,
                       const char *room_name,
                       enum GNUNET_CHAT_MsgOptions msg_options,
+                      GNUNET_CHAT_JoinCallback joinCallback,
+                      void *join_cls,
                       GNUNET_CHAT_MessageCallback messageCallback,
                       void *message_cls,
                       GNUNET_CHAT_MemberListCallback memberCallback,




reply via email to

[Prev in Thread] Current Thread [Next in Thread]