2017-05-28 16:31 CEST

View Issue Details Jump to Notes ]
IDProjectCategoryView StatusLast Update
0001644GNUnetotherpublic2011-04-27 16:18
Reportervminko 
Assigned ToChristian Grothoff 
PrioritynormalSeverityfeatureReproducibilityalways
StatusclosedResolutionfixed 
Product Version 
Target VersionFixed in Version 
Summary0001644: chat for 0.9
DescriptionThis is a port of the 0.8 version (https://gnunet.org/svn/GNUnet/src/applications/chat/) to 0.9.
The patch should be applied to the revision #14186.
Test cases are coming soon...
TagsNo tags attached.
Attached Files
  • patch file icon gnunet-svn@14186-chat.patch (114,254 bytes) 2011-01-17 02:16 -
    Index: src/chat/chat.c
    ===================================================================
    --- src/chat/chat.c	(revision 0)
    +++ src/chat/chat.c	(revision 0)
    @@ -0,0 +1,789 @@
    +/*
    +     This file is part of GNUnet.
    +     (C) 2008, 2011 Christian Grothoff (and other contributing authors)
    +
    +     GNUnet is free software; you can redistribute it and/or modify
    +     it under the terms of the GNU General Public License as published
    +     by the Free Software Foundation; either version 3, or (at your
    +     option) any later version.
    +
    +     GNUnet is distributed in the hope that it will be useful, but
    +     WITHOUT ANY WARRANTY; without even the implied warranty of
    +     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    +     General Public License for more details.
    +
    +     You should have received a copy of the GNU General Public License
    +     along with GNUnet; see the file COPYING.  If not, write to the
    +     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
    +     Boston, MA 02111-1307, USA.
    +*/
    +
    +/**
    + * @file chat/chat_api.c
    + * @brief convenience API for sending and receiving chat messages
    + * @author Christian Grothoff
    + * @author Nathan Evans
    + * @author Vitaly Minko
    + */
    +
    +#include "platform.h"
    +#include "gnunet_constants.h"
    +#include "gnunet_protocols.h"
    +#include "gnunet_signatures.h"
    +#include "chat.h"
    +
    +#define DEBUG_CHAT GNUNET_YES
    +#define NICK_IDENTITY_PREFIX ".chat_identity_"
    +
    +
    +/**
    + * Handle for a (joined) chat room.
    + */
    +struct GNUNET_CHAT_Room
    +{
    +  struct GNUNET_CLIENT_Connection *client;
    +
    +  const struct GNUNET_CONFIGURATION_Handle *cfg;
    +
    +  struct GNUNET_CONTAINER_MetaData *member_info;
    +
    +  char *room_name;
    +
    +  struct GNUNET_CRYPTO_RsaPrivateKey *my_private_key;
    +
    +  struct MemberList *members;
    +
    +  GNUNET_CHAT_MessageCallback message_callback;
    +
    +  void *message_callback_cls;
    +
    +  GNUNET_CHAT_MemberListCallback member_list_callback;
    +
    +  void *member_list_callback_cls;
    +
    +  GNUNET_CHAT_MessageConfirmation confirmation_callback;
    +
    +  void *confirmation_cls;
    +
    +  uint32_t sequence_number;
    +
    +  uint32_t msg_options;
    +
    +};
    +
    +/**
    + * Linked list of members in the chat room.
    + */
    +struct MemberList
    +{
    +  struct MemberList *next;
    +
    +  /**
    +   * Description of the member.
    +   */
    +  struct GNUNET_CONTAINER_MetaData *meta;
    +
    +  /**
    +   * Member ID (pseudonym).
    +   */
    +  GNUNET_HashCode id;
    +
    +};
    +
    +/**
    + * Context for transmitting a send-message request.
    + */
    +struct GNUNET_CHAT_SendMessageContext
    +{
    +  /**
    +   * Handle for the chat room.
    +   */
    +  struct GNUNET_CHAT_Room *chat_room;
    +
    +  /**
    +   * Message that we're sending.
    +   */
    +  char *message;
    +
    +  /**
    +   * Options for the message.
    +   */
    +  enum GNUNET_CHAT_MsgOptions options;
    +
    +  /**
    +   * Receiver of the message. NULL to send to everyone in the room.
    +   */
    +  const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *receiver;
    +
    +  /**
    +   * Sequence id of the message.
    +   */
    +  uint32_t sequence_number;
    +
    +};
    +
    +/**
    + * Context for transmitting a confirmation receipt.
    + */
    +struct GNUNET_CHAT_SendReceiptContext
    +{
    +  /**
    +   * Handle for the chat room.
    +   */
    +  struct GNUNET_CHAT_Room *chat_room;
    +
    +  /**
    +   * The original message that we're going to acknowledge.
    +   */
    +  struct ReceiveNotificationMessage *received_msg;
    +
    +};
    +
    +/**
    + * Ask client to send a join request.
    + */
    +static int
    +GNUNET_CHAT_rejoin_room (struct GNUNET_CHAT_Room *chat_room);
    +
    +
    +/**
    + * Transmit a confirmation receipt to the chat service.
    + *
    + * @param cls closure, pointer to the 'struct GNUNET_CHAT_SendReceiptContext'
    + * @param size number of bytes available in buf
    + * @param buf where the callee should write the message
    + * @return number of bytes written to buf
    + */
    +static size_t
    +transmit_acknowledge_request (void *cls,
    +			      size_t size, 
    +			      void *buf)
    +{
    +  struct GNUNET_CHAT_SendReceiptContext *src = cls;
    +  struct ConfirmationReceiptMessage *receipt;
    +  struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pub_key;
    +  uint16_t msg_len;
    +  size_t msg_size;
    +
    +  if (NULL == buf)
    +    {
    +      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    +		  _("Could not transmit confirmation receipt\n"));
    +      return 0;
    +    }
    +#if DEBUG_CHAT
    +  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    +	      "Transmitting confirmation receipt to the service\n");
    +#endif
    +  msg_size = sizeof (struct ConfirmationReceiptMessage);
    +  GNUNET_assert (size >= msg_size);
    +  receipt = buf;
    +  receipt->header.size = htons (msg_size);
    +  receipt->header.type =
    +    htons (GNUNET_MESSAGE_TYPE_CHAT_CONFIRMATION_RECEIPT);
    +  receipt->sequence_number = src->received_msg->sequence_number;
    +  receipt->reserved2 = 0;
    +  receipt->timestamp = GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_get ());
    +  GNUNET_CRYPTO_rsa_key_get_public (src->chat_room->my_private_key, &pub_key);
    +  GNUNET_CRYPTO_hash (&pub_key,
    +		      sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
    +		      &receipt->target);
    +  receipt->author = src->received_msg->sender;
    +  receipt->purpose.purpose =
    +    htonl (GNUNET_SIGNATURE_PURPOSE_CHAT_RECEIPT);
    +  receipt->purpose.size =
    +    htonl (msg_size -
    +	   sizeof (struct GNUNET_MessageHeader) -
    +	   sizeof (uint32_t) -
    +	   sizeof (struct GNUNET_CRYPTO_RsaSignature));
    +  msg_len = ntohs (src->received_msg->header.size) -
    +    sizeof (struct ReceiveNotificationMessage);
    +  GNUNET_CRYPTO_hash (&src->received_msg[1], msg_len, &receipt->content);
    +  GNUNET_assert (GNUNET_OK == 
    +		 GNUNET_CRYPTO_rsa_sign (src->chat_room->my_private_key,
    +					 &receipt->purpose,
    +					 &receipt->signature));
    +  GNUNET_free (src->received_msg);
    +  GNUNET_free (src);
    +  return msg_size;
    +}
    +
    +
    +/**
    + * Handles messages received from the service.  Calls the proper client
    + * callback.
    + */
    +static void
    +process_result (struct GNUNET_CHAT_Room *room,
    +		const struct GNUNET_MessageHeader *reply)
    +{
    +  struct LeaveNotificationMessage *leave_msg;
    +  struct JoinNotificationMessage *join_msg;
    +  struct ReceiveNotificationMessage *received_msg;
    +  struct ConfirmationReceiptMessage *receipt;
    +  GNUNET_HashCode id;
    +  struct GNUNET_CONTAINER_MetaData *meta;
    +  struct GNUNET_CHAT_SendReceiptContext *src;
    +  struct MemberList *pos;
    +  struct MemberList *prev;
    +  struct GNUNET_CRYPTO_AesSessionKey key;
    +  char decrypted_msg[MAX_MESSAGE_LENGTH];
    +  uint16_t size;
    +  uint16_t meta_len;
    +  uint16_t msg_len;
    +  char *message_content;
    +
    +  size = ntohs (reply->size);
    +  switch (ntohs (reply->type))
    +    {
    +    case GNUNET_MESSAGE_TYPE_CHAT_JOIN_NOTIFICATION:
    +#if DEBUG_CHAT
    +      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got a join notification\n");
    +#endif
    +      if (size < sizeof (struct JoinNotificationMessage))
    +	{
    +	  GNUNET_break (0);
    +	  return;
    +	}
    +      join_msg = (struct JoinNotificationMessage *) reply;
    +      meta_len = size - sizeof (struct JoinNotificationMessage);
    +      meta =
    +	GNUNET_CONTAINER_meta_data_deserialize ((const char *) &join_msg[1],
    +						meta_len);
    +      if (NULL == meta)
    +	{
    +	  GNUNET_break (0);
    +	  return;
    +	}
    +      pos = GNUNET_malloc (sizeof (struct MemberList));
    +      pos->meta = meta;
    +      GNUNET_CRYPTO_hash (&join_msg->public_key,
    +			  sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
    +			  &pos->id);
    +      GNUNET_PSEUDONYM_add (room->cfg, &pos->id, meta);
    +      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
    +      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got a leave notification\n");
    +#endif
    +      if (size < sizeof (struct LeaveNotificationMessage))
    +	{
    +	  GNUNET_break (0);
    +	  return;
    +	}
    +      leave_msg = (struct LeaveNotificationMessage *) reply;
    +      room->member_list_callback (room->member_list_callback_cls,
    +				  NULL, &leave_msg->user,
    +				  GNUNET_CHAT_MSG_OPTION_NONE);
    +      GNUNET_CRYPTO_hash (&leave_msg->user,
    +			  sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
    +			  &id);
    +      prev = NULL;
    +      pos = room->members;
    +      while ((NULL != pos) &&
    +	     (0 != memcmp (&pos->id, &id, sizeof (GNUNET_HashCode))))
    +	{
    +	  prev = pos;
    +	  pos = pos->next;
    +	}
    +      GNUNET_assert (NULL != pos);
    +      if (NULL == prev)
    +	room->members = pos->next;
    +      else
    +	prev->next = pos->next;
    +      GNUNET_CONTAINER_meta_data_destroy (pos->meta);
    +      GNUNET_free (pos);
    +      break;
    +    case GNUNET_MESSAGE_TYPE_CHAT_MESSAGE_NOTIFICATION:
    +#if DEBUG_CHAT
    +      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got a message notification\n");
    +#endif
    +      if (size <= sizeof (struct ReceiveNotificationMessage))
    +	{
    +	  GNUNET_break (0);
    +	  return;
    +	}
    +      received_msg = (struct ReceiveNotificationMessage *) reply;
    +      if (0 !=
    +	  (ntohl (received_msg->msg_options) & GNUNET_CHAT_MSG_ACKNOWLEDGED))
    +	{
    +	  src = GNUNET_malloc (sizeof (struct GNUNET_CHAT_SendReceiptContext));
    +	  src->chat_room = room;
    +	  src->received_msg = GNUNET_memdup (received_msg, size);
    +	  GNUNET_CLIENT_notify_transmit_ready (room->client,
    +					       sizeof (struct ConfirmationReceiptMessage),
    +					       GNUNET_CONSTANTS_SERVICE_TIMEOUT,
    +					       GNUNET_YES,
    +					       &transmit_acknowledge_request,
    +					       src);
    +	}
    +      msg_len = size - sizeof (struct ReceiveNotificationMessage);
    +      if (0 !=
    +	  (ntohl (received_msg->msg_options) & GNUNET_CHAT_MSG_PRIVATE))
    +	{
    +	  if (-1 == GNUNET_CRYPTO_rsa_decrypt (room->my_private_key,
    +					       &received_msg->encrypted_key,
    +					       &key,
    +					       sizeof (struct GNUNET_CRYPTO_AesSessionKey)))
    +	    {
    +	      GNUNET_break (0);
    +	      return;
    +	    }
    +	  msg_len = GNUNET_CRYPTO_aes_decrypt (&received_msg[1],
    +					       msg_len,
    +					       &key,
    +					       (const struct GNUNET_CRYPTO_AesInitializationVector *) INITVALUE,
    +					       decrypted_msg);
    +	  message_content = decrypted_msg;
    +	}
    +      else
    +	{
    +	  message_content = GNUNET_malloc (msg_len + 1);
    +	  memcpy (message_content, &received_msg[1], msg_len);
    +	}
    +      message_content[msg_len] = '\0';
    +      pos = room->members;
    +      while ((NULL != pos) &&
    +	     (0 != memcmp (&pos->id,
    +			   &received_msg->sender,
    +			   sizeof (GNUNET_HashCode))))
    +	pos = pos->next;
    +      GNUNET_assert (NULL != pos);
    +      room->message_callback (room->message_callback_cls,
    +			      room,
    +			      &received_msg->sender,
    +			      pos->meta,
    +			      message_content,
    +			      ntohl (received_msg->msg_options));
    +      if (message_content != decrypted_msg)
    +	GNUNET_free (message_content);
    +      break;
    +    case GNUNET_MESSAGE_TYPE_CHAT_CONFIRMATION_NOTIFICATION:
    +#if DEBUG_CHAT
    +      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got a confirmation receipt\n");
    +#endif
    +      if (size < sizeof (struct ConfirmationReceiptMessage))
    +	{
    +	  GNUNET_break (0);
    +	  return;
    +	}
    +      receipt = (struct ConfirmationReceiptMessage *) reply;
    +      if (NULL != room->confirmation_callback)
    +	room->confirmation_callback (room->confirmation_cls,
    +				     room,
    +				     ntohl (receipt->sequence_number),
    +				     GNUNET_TIME_absolute_ntoh (receipt->timestamp),
    +				     &receipt->target,
    +				     &receipt->content,
    +				     &receipt->signature);
    +      break;
    +    default:
    +      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    +		  _("Unknown message type: '%u'\n"), ntohs (reply->type));
    +      GNUNET_break_op (0);
    +      break;
    +    }
    +}
    +
    +
    +/**
    + * Listen for incoming messages on this chat room.  Also, support servers going
    + * away/coming back (i.e. rejoin chat room to keep server state up to date).
    + *
    + * @param cls closure, pointer to the 'struct GNUNET_CHAT_Room'
    + * @param msg message received, NULL on timeout or fatal error
    + */
    +static void 
    +receive_results (void *cls,
    +		 const struct GNUNET_MessageHeader *msg)
    +{
    +  struct GNUNET_CHAT_Room *chat_room = cls;
    +
    +#if DEBUG_CHAT
    +  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got a message from the service\n");
    +#endif
    +  if (0 != (GNUNET_SCHEDULER_REASON_SHUTDOWN & GNUNET_SCHEDULER_get_reason ()))
    +    return;
    +  if (NULL == msg)
    +    {
    +      GNUNET_break (0);
    +      GNUNET_CHAT_rejoin_room (chat_room);
    +      return;
    +    }
    +  process_result (chat_room, msg);
    +  if (NULL == chat_room->client)
    +    return; /* fatal error */
    +  /* continue receiving */
    +  GNUNET_CLIENT_receive (chat_room->client,
    +			 &receive_results,
    +			 chat_room,
    +			 GNUNET_TIME_UNIT_FOREVER_REL);
    +}
    +
    +
    +/**
    + * Read existing private key from file or create a new one if it does not exist
    + * yet.
    + * Returns the private key on success, NULL on error.
    + */
    +static struct GNUNET_CRYPTO_RsaPrivateKey *
    +GNUNET_CHAT_initPrivateKey (const struct GNUNET_CONFIGURATION_Handle *cfg,
    +			    const char *nick_name)
    +{
    +  char *home;
    +  char *keyfile;
    +  struct GNUNET_CRYPTO_RsaPrivateKey *privKey;
    +
    +#if DEBUG_CHAT
    +  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Initializing private key\n");
    +#endif
    +  if (GNUNET_OK !=
    +      GNUNET_CONFIGURATION_get_value_filename (cfg,
    +					       "chat",
    +					       "HOME",
    +					       &home))
    +    {
    +      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    +		  _("Configuration option `%s' in section `%s' missing\n"),
    +		  "HOME",
    +		  "chat");
    +      return NULL;
    +    }
    +  GNUNET_DISK_directory_create (home);
    +  if (GNUNET_OK != GNUNET_DISK_directory_test (home))
    +    {
    +      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    +		  _("Failed to access chat home directory `%s'\n"),
    +		  home);
    +      GNUNET_free (home);
    +      return NULL;
    +    }
    +  /* read or create private key */
    +  keyfile =
    +    GNUNET_malloc (strlen (home) + strlen (NICK_IDENTITY_PREFIX) +
    +		   strlen (nick_name) + 2);
    +  strcpy (keyfile, home);
    +  GNUNET_free (home);
    +  if (keyfile[strlen (keyfile) - 1] != DIR_SEPARATOR)
    +    strcat (keyfile, DIR_SEPARATOR_STR);
    +  strcat (keyfile, NICK_IDENTITY_PREFIX);
    +  strcat (keyfile, nick_name);
    +  privKey = GNUNET_CRYPTO_rsa_key_create_from_file (keyfile);
    +  if (NULL == privKey)
    +    {
    +      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    +		  _("Failed to create/open key in file `%s'\n"),
    +		  keyfile);
    +    }
    +  GNUNET_free (keyfile);
    +  return privKey;
    +}
    +
    +
    +/**
    + * Transmit a join request to the chat service.
    + *
    + * @param cls closure, pointer to the 'struct GNUNET_CHAT_Room'
    + * @param size number of bytes available in buf
    + * @param buf where the callee should write the message
    + * @return number of bytes written to buf
    + */
    +static size_t
    +transmit_join_request (void *cls,
    +		       size_t size, 
    +		       void *buf)
    +{
    +  struct GNUNET_CHAT_Room *chat_room = cls;
    +  struct JoinRequestMessage *join_msg;
    +  char *room;
    +  char *meta;
    +  size_t room_len;
    +  ssize_t meta_len;
    +  size_t size_of_join;
    +
    +  if (NULL == buf)
    +    {
    +#if DEBUG_CHAT
    +      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    +		  "Could not transmit join request\n");
    +#endif
    +      return 0;
    +    }
    +#if DEBUG_CHAT
    +  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    +	      "Transmitting join request to the service\n");
    +#endif
    +  room_len = strlen (chat_room->room_name);
    +  meta_len = GNUNET_CONTAINER_meta_data_get_serialized_size (chat_room->member_info);
    +  size_of_join = sizeof (struct JoinRequestMessage) + meta_len + room_len;
    +  GNUNET_assert (size >= size_of_join);
    +  join_msg = buf;
    +  join_msg->header.size = htons (size);
    +  join_msg->header.type = htons (GNUNET_MESSAGE_TYPE_CHAT_JOIN_REQUEST);
    +  join_msg->msg_options = htonl (chat_room->msg_options);
    +  join_msg->room_name_len = htons (room_len);
    +  join_msg->reserved = htons (0);
    +  GNUNET_CRYPTO_rsa_key_get_public (chat_room->my_private_key, &join_msg->public_key);
    +  room = (char *) &join_msg[1];
    +  memcpy (room, chat_room->room_name, room_len);
    +  meta = &room[room_len];
    +  if (GNUNET_SYSERR ==
    +      GNUNET_CONTAINER_meta_data_serialize (chat_room->member_info,
    +					    &meta,
    +					    meta_len,
    +					    GNUNET_CONTAINER_META_DATA_SERIALIZE_FULL))
    +    {
    +      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    +		  _("Could not serialize metadata\n"));
    +      return 0;
    +    }
    +  return size_of_join;
    +}
    +
    +
    +/**
    + * Ask to send a join request.
    + */
    +static int
    +GNUNET_CHAT_rejoin_room (struct GNUNET_CHAT_Room *chat_room)
    +{
    +  size_t size_of_join;
    +
    +  size_of_join = sizeof (struct JoinRequestMessage) +
    +    GNUNET_CONTAINER_meta_data_get_serialized_size (chat_room->member_info) +
    +    strlen (chat_room->room_name);
    +  if (NULL ==
    +      GNUNET_CLIENT_notify_transmit_ready (chat_room->client,
    +					   size_of_join,
    +					   GNUNET_CONSTANTS_SERVICE_TIMEOUT,
    +					   GNUNET_YES,
    +					   &transmit_join_request,
    +					   chat_room))
    +    return GNUNET_SYSERR;
    +  return GNUNET_OK;
    +}
    +
    +
    +/**
    + * Leave a chat room.
    + */
    +void
    +GNUNET_CHAT_leave_room (struct GNUNET_CHAT_Room *chat_room)
    +{
    +  struct MemberList *pos;
    +
    +#if DEBUG_CHAT
    +  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    +	      "Leaving the room '%s'\n", chat_room->room_name);
    +#endif
    +  GNUNET_CLIENT_disconnect (chat_room->client, GNUNET_NO);
    +  GNUNET_free (chat_room->room_name);
    +  GNUNET_CONTAINER_meta_data_destroy (chat_room->member_info);
    +  GNUNET_CRYPTO_rsa_key_free (chat_room->my_private_key);
    +  while (NULL != chat_room->members)
    +    {
    +      pos = chat_room->members;
    +      chat_room->members = pos->next;
    +      GNUNET_CONTAINER_meta_data_destroy (pos->meta);
    +      GNUNET_free (pos);
    +    }
    +  GNUNET_free (chat_room);
    +}
    +
    +
    +/**
    + * Join a chat room.
    + *
    + * @param cfg configuration
    + * @param nick_name nickname of the user joining (used to
    + *                  determine which public key to use);
    + *                  the nickname should probably also
    + *                  be used in the member_info (as "EXTRACTOR_TITLE")
    + * @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 messageCallback which function to call if a message has
    + *        been received?
    + * @param message_cls argument to callback
    + * @param memberCallback which function to call for join/leave notifications
    + * @param member_cls argument to callback
    + * @param confirmationCallback which function to call for confirmations (maybe NULL)
    + * @param confirmation_cls argument to callback
    + * @param me member ID (pseudonym)
    + * @return NULL on error
    + */
    +struct GNUNET_CHAT_Room *
    +GNUNET_CHAT_join_room (const struct GNUNET_CONFIGURATION_Handle *cfg,
    +		       const char *nick_name,
    +		       struct GNUNET_CONTAINER_MetaData *member_info,
    +		       const char *room_name,
    +		       enum GNUNET_CHAT_MsgOptions msg_options,
    +		       GNUNET_CHAT_MessageCallback messageCallback,
    +		       void *message_cls,
    +		       GNUNET_CHAT_MemberListCallback memberCallback,
    +		       void *member_cls,
    +		       GNUNET_CHAT_MessageConfirmation confirmationCallback,
    +		       void *confirmation_cls,
    +		       GNUNET_HashCode *me)
    +{
    +  struct GNUNET_CHAT_Room *chat_room;
    +  struct GNUNET_CRYPTO_RsaPrivateKey *priv_key;
    +  struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pub_key;
    +  struct GNUNET_CLIENT_Connection *client;
    +
    +#if DEBUG_CHAT
    +  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Joining the room '%s'\n", room_name);
    +#endif
    +  priv_key = GNUNET_CHAT_initPrivateKey (cfg, nick_name);
    +  if (NULL == priv_key)
    +    return NULL;
    +  GNUNET_CRYPTO_rsa_key_get_public (priv_key, &pub_key);
    +  GNUNET_CRYPTO_hash (&pub_key,
    +		      sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
    +		      me);
    +  GNUNET_PSEUDONYM_add (cfg, me, member_info);
    +  client = GNUNET_CLIENT_connect ("chat", cfg);
    +  if (NULL == client)
    +    {
    +      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    +		  _("Failed to connect to the chat service\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->message_callback = messageCallback;
    +  chat_room->message_callback_cls = message_cls;
    +  chat_room->member_list_callback = memberCallback;
    +  chat_room->member_list_callback_cls = member_cls;
    +  chat_room->confirmation_callback = confirmationCallback;
    +  chat_room->confirmation_cls = confirmation_cls;
    +  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);
    +      return NULL;
    +    }
    +  return chat_room;
    +}
    +
    +
    +/**
    + * Transmit a send-message request to the chat service.
    + *
    + * @param cls closure, pointer to the 'struct GNUNET_CHAT_SendMessageContext'
    + * @param size number of bytes available in buf
    + * @param buf where the callee should write the message
    + * @return number of bytes written to buf
    + */
    +static size_t
    +transmit_send_request (void *cls,
    +		       size_t size, 
    +		       void *buf)
    +{
    +  struct GNUNET_CHAT_SendMessageContext *smc = cls;
    +  struct TransmitRequestMessage *msg_to_send;
    +  size_t msg_size;
    +
    +  if (NULL == buf)
    +    {
    +#if DEBUG_CHAT
    +      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    +		  "Could not transmit a chat message\n");
    +#endif
    +      return 0;
    +    }
    +#if DEBUG_CHAT
    +  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    +	      "Transmitting a chat message to the service\n");
    +#endif
    +  msg_size = strlen (smc->message) + sizeof (struct TransmitRequestMessage);
    +  GNUNET_assert (size >= msg_size);
    +  msg_to_send = buf;
    +  msg_to_send->header.size = htons (msg_size);
    +  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->reserved = htonl (0);
    +  if (NULL == smc->receiver)
    +    memset (&msg_to_send->target, 0, sizeof (GNUNET_HashCode));
    +  else
    +    GNUNET_CRYPTO_hash (smc->receiver,
    +			sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
    +			&msg_to_send->target);
    +  memcpy (&msg_to_send[1], smc->message, strlen (smc->message));
    +  /**
    +   * Client don't encode private messages since public keys of other members are
    +   * stored on the service side.
    +   */
    +  if (smc->options & GNUNET_CHAT_MSG_AUTHENTICATED)
    +    {
    +      msg_to_send->purpose.purpose =
    +	htonl (GNUNET_SIGNATURE_PURPOSE_CHAT_MESSAGE);
    +      msg_to_send->purpose.size =
    +	htonl (msg_size -
    +	       sizeof (struct GNUNET_MessageHeader) -
    +	       sizeof (struct GNUNET_CRYPTO_RsaSignature));
    +      GNUNET_assert (GNUNET_OK == 
    +		     GNUNET_CRYPTO_rsa_sign (smc->chat_room->my_private_key,
    +					     &msg_to_send->purpose,
    +					     &msg_to_send->signature));
    +    }
    +  GNUNET_free (smc->message);
    +  GNUNET_free (smc);
    +  return msg_size;
    +}
    +
    +
    +/**
    + * Send a message.
    + *
    + * @param room handle for the chat room
    + * @param message message to be sent
    + * @param options options for the message
    + * @param receiver use NULL to send to everyone in the room
    + * @param sequence_number where to write the sequence id of the message
    + */
    +void
    +GNUNET_CHAT_send_message (struct GNUNET_CHAT_Room *room,
    +			  const char *message,
    +			  enum GNUNET_CHAT_MsgOptions options,
    +			  const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *receiver,
    +			  uint32_t *sequence_number)
    +{
    +  size_t msg_size;
    +  struct GNUNET_CHAT_SendMessageContext *smc;
    +
    +#if DEBUG_CHAT
    +  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending a message\n");
    +#endif
    +  *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;
    +  msg_size = strlen (message) + sizeof (struct TransmitRequestMessage);
    +  GNUNET_CLIENT_notify_transmit_ready (room->client,
    +				       msg_size,
    +				       GNUNET_CONSTANTS_SERVICE_TIMEOUT,
    +				       GNUNET_YES,
    +				       &transmit_send_request,
    +				       smc);
    +}
    +
    +/* end of chat.c */
    Index: src/chat/chat.h
    ===================================================================
    --- src/chat/chat.h	(revision 0)
    +++ src/chat/chat.h	(revision 0)
    @@ -0,0 +1,458 @@
    +/*
    +     This file is part of GNUnet
    +     (C) 2008, 2011 Christian Grothoff (and other contributing authors)
    +
    +     GNUnet is free software; you can redistribute it and/or modify
    +     it under the terms of the GNU General Public License as published
    +     by the Free Software Foundation; either version 2, or (at your
    +     option) any later version.
    +
    +     GNUnet is distributed in the hope that it will be useful, but
    +     WITHOUT ANY WARRANTY; without even the implied warranty of
    +     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    +     General Public License for more details.
    +
    +     You should have received a copy of the GNU General Public License
    +     along with GNUnet; see the file COPYING.  If not, write to the
    +     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
    +     Boston, MA 02111-1307, USA.
    +*/
    +
    +/**
    + * @file chat/chat.h
    + * @brief support for chat
    + * @author Christian Grothoff
    + * @author Nathan Evans
    + * @author Vitaly Minko
    + */
    +
    +#ifndef CHAT_H
    +#define CHAT_H
    +
    +#include "gnunet_chat_service.h"
    +
    +/**
    + * Constant IV since we generate a new session key per each message.
    + */
    +#define INITVALUE "InitializationVectorValue"
    +
    +
    +/**
    + * Client-service messages
    + */
    +
    +/**
    + * Notification sent by service to client indicating that we've received 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.
    + */
    +struct ReceiveNotificationMessage
    +{
    +  /**
    +   * Message type will be GNUNET_MESSAGE_TYPE_CHAT_MESSAGE_NOTIFICATION
    +   */
    +  struct GNUNET_MessageHeader header;
    +
    +  /**
    +   * Message options, see GNUNET_CHAT_MsgOptions.
    +   */
    +  uint32_t msg_options GNUNET_PACKED;
    +
    +  /**
    +   * Sequence number of the message (unique per sender).
    +   */
    +  uint32_t sequence_number GNUNET_PACKED;
    +
    +  /**
    +   * For alignment (should be zero).
    +   */
    +  uint32_t reserved GNUNET_PACKED;
    +
    +  /**
    +   * Hash of the public key of the pseudonym of the sender of the message.
    +   * TBD: Should be all zeros for anonymous.
    +   */
    +  GNUNET_HashCode sender;
    +
    +  /**
    +   * The encrypted session key.
    +   */
    +  struct GNUNET_CRYPTO_RsaEncryptedData encrypted_key;
    +
    +};
    +
    +
    +/**
    + * Request sent by client to transmit a chat message to another room members.
    + * After this struct, the remaining bytes are the actual message in plaintext.
    + * Private messages are encrypted on the service side.
    + */
    +struct TransmitRequestMessage
    +{
    +  /**
    +   * Message type will be GNUNET_MESSAGE_TYPE_CHAT_TRANSMIT_REQUEST
    +   */
    +  struct GNUNET_MessageHeader header;
    +
    +  /**
    +   * For alignment (should be zero).
    +   */
    +  uint32_t reserved GNUNET_PACKED;
    +
    +  /**
    +   * Signature confirming receipt.  Signature covers everything from header
    +   * through content.
    +   */
    +  struct GNUNET_CRYPTO_RsaSignature signature;
    +
    +  /**
    +   * What is being signed and why?
    +   */
    +  struct GNUNET_CRYPTO_RsaSignaturePurpose purpose;
    +
    +  /**
    +   * Desired message options, see GNUNET_CHAT_MsgOptions.
    +   */
    +  uint32_t msg_options GNUNET_PACKED;
    +
    +  /**
    +   * Sequence number of the message (unique per sender).
    +   */
    +  uint32_t sequence_number GNUNET_PACKED;
    +
    +  /**
    +   * Who should receive this message?  Set to all zeros for "everyone".
    +   */
    +  GNUNET_HashCode target;
    +
    +};
    +
    +
    +/**
    + * Receipt sent from a message receiver to the service to confirm delivery of
    + * a chat message.
    + */
    +struct ConfirmationReceiptMessage
    +{
    +  /**
    +   * Message type will be GNUNET_MESSAGE_TYPE_CHAT_CONFIRMATION_RECEIPT 
    +   */
    +  struct GNUNET_MessageHeader header;
    +
    +  /**
    +   * For alignment (should be zero).
    +   */
    +  uint32_t reserved GNUNET_PACKED;
    +
    +  /**
    +   * Signature confirming receipt.  Signature covers everything from header
    +   * through content.
    +   */
    +  struct GNUNET_CRYPTO_RsaSignature signature;
    +
    +  /**
    +   * What is being signed and why?
    +   */
    +  struct GNUNET_CRYPTO_RsaSignaturePurpose purpose;
    +
    +  /**
    +   * Sequence number of the original message.
    +   */
    +  uint32_t sequence_number GNUNET_PACKED;
    +
    +  /**
    +   * For alignment (should be zero).
    +   */
    +  uint32_t reserved2 GNUNET_PACKED;
    +
    +  /**
    +   * Time of receipt.
    +   */
    +  struct GNUNET_TIME_AbsoluteNBO timestamp;
    +
    +  /**
    +   * Who is confirming the receipt?
    +   */
    +  GNUNET_HashCode target;
    +
    +  /**
    +   * Who is the author of the chat message?
    +   */
    +  GNUNET_HashCode author;
    +
    +  /**
    +   * Hash of the (possibly encrypted) content.
    +   */
    +  GNUNET_HashCode content;
    +
    +};
    +
    +
    +/**
    + * Message send from client to daemon to join a chat room.
    + * This struct is followed by the room name and then
    + * the serialized ECRS meta data describing the new member.
    + */
    +struct JoinRequestMessage
    +{
    +  /**
    +   * Message type will be GNUNET_MESSAGE_TYPE_CHAT_JOIN_REQUEST
    +   */
    +  struct GNUNET_MessageHeader header;
    +
    +  /**
    +   * Options.  Set all options that this client is willing to receive.
    +   * For example, if the client does not want to receive anonymous or
    +   * OTR messages but is willing to generate acknowledgements and
    +   * receive private messages, this should be set to
    +   * GNUNET_CHAT_MSG_PRIVATE | GNUNET_CHAT_MSG_ACKNOWLEDGED.
    +   */
    +  uint32_t msg_options GNUNET_PACKED;
    +
    +  /**
    +   * Length of the room name.
    +   */
    +  uint16_t room_name_len GNUNET_PACKED;
    +
    +  /**
    +   * For alignment (should be zero).
    +   */
    +  uint16_t reserved GNUNET_PACKED;
    +  uint32_t reserved2 GNUNET_PACKED;
    +
    +  /**
    +   * Public key of the joining member.
    +   */
    +  struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded public_key;
    +
    +};
    +
    +
    +/**
    + * Message send by server to client to indicate joining of another room member.
    + * This struct is followed by the serialized ECRS MetaData describing the new
    + * member.
    + */
    +struct JoinNotificationMessage
    +{
    +  /**
    +   * Message type will be GNUNET_MESSAGE_TYPE_CHAT_JOIN_NOTIFICATION
    +   */
    +  struct GNUNET_MessageHeader header;
    +
    +  /**
    +   * Options.  Set to all options that the new user is willing to
    +   * process.  For example, if the client does not want to receive
    +   * anonymous or OTR messages but is willing to generate
    +   * acknowledgements and receive private messages, this should be set
    +   * to GNUNET_CHAT_MSG_PRIVATE | GNUNET_CHAT_MSG_ACKNOWLEDGED.
    +   */
    +  uint32_t msg_options GNUNET_PACKED;
    +
    +  /**
    +   * Public key of the new user.
    +   */
    +  struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded public_key;
    +
    +};
    +
    +
    +/**
    + * Message send by server to client to indicate leaving of another room member.
    + */
    +struct LeaveNotificationMessage
    +{
    +  /**
    +   * Message type will be GNUNET_MESSAGE_TYPE_CHAT_LEAVE_NOTIFICATION
    +   */
    +  struct GNUNET_MessageHeader header;
    +
    +  /**
    +   * Reserved (for alignment).
    +   */
    +  uint32_t reserved GNUNET_PACKED;
    +
    +  /**
    +   * Who is leaving?
    +   */
    +  struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded user;
    +
    +};
    +
    +
    +/**
    + * Peer-to-peer messages
    + */
    +
    +/**
    + * Message send by one peer to another to indicate joining of another room
    + * member.  This struct is followed by the room name and then the serialized
    + * ECRS MetaData describing the new member.
    + */
    +struct P2PJoinNotificationMessage
    +{
    +  /**
    +   * Message type will be GNUNET_MESSAGE_TYPE_CHAT_P2P_JOIN_NOTIFICATION
    +   */
    +  struct GNUNET_MessageHeader header;
    +
    +  /**
    +   * Options.  Set all options that this client is willing to receive.
    +   * For example, if the client does not want to receive anonymous or
    +   * OTR messages but is willing to generate acknowledgements and
    +   * receive private messages, this should be set to
    +   * GNUNET_CHAT_MSG_PRIVATE | GNUNET_CHAT_MSG_ACKNOWLEDGED.
    +   */
    +  uint32_t msg_options GNUNET_PACKED;
    +
    +  /**
    +   * Length of the room name.
    +   */
    +  uint16_t room_name_len GNUNET_PACKED;
    +
    +  /**
    +   * Reserved (should be zero).
    +   */
    +  uint16_t reserved GNUNET_PACKED;
    +  uint32_t reserved2 GNUNET_PACKED;
    +
    +  /**
    +   * Public key of the joining member.
    +   */
    +  struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded public_key;
    +
    +};
    +
    +
    +/**
    + * Message send by one peer to another to indicate leaving of another room
    + * member.
    + */
    +struct P2PLeaveNotificationMessage
    +{
    +  /**
    +   * Message type will be GNUNET_MESSAGE_TYPE_CHAT_P2P_LEAVE_NOTIFICATION
    +   */
    +  struct GNUNET_MessageHeader header;
    +
    +  /**
    +   * Reserved (for alignment).
    +   */
    +  uint32_t reserved GNUNET_PACKED;
    +
    +  /**
    +   * Who is leaving?
    +   */
    +  struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded user;
    +
    +};
    +
    +
    +/**
    + * 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.
    + */
    +struct P2PReceiveNotificationMessage
    +{
    +  /**
    +   * Message type will be GNUNET_MESSAGE_TYPE_CHAT_P2P_MESSAGE_NOTIFICATION
    +   */
    +  struct GNUNET_MessageHeader header;
    +
    +  /**
    +   * Message options, see GNUNET_CHAT_MsgOptions.
    +   */
    +  uint32_t msg_options GNUNET_PACKED;
    +
    +  /**
    +   * Sequence number of the message (unique per sender).
    +   */
    +  uint32_t sequence_number GNUNET_PACKED;
    +
    +  /**
    +   * Reserved (for alignment).
    +   */
    +  uint32_t reserved GNUNET_PACKED;
    +
    +  /**
    +   * Hash of the public key of the pseudonym of the sender of the message
    +   * TBD: Should be all zeros for anonymous.
    +   */
    +  GNUNET_HashCode sender;
    +
    +  /**
    +   * Who should receive this message?  Set to all zeros for "everyone".
    +   */
    +  GNUNET_HashCode target;
    +
    +  /**
    +   * The encrypted session key.
    +   */
    +  struct GNUNET_CRYPTO_RsaEncryptedData encrypted_key;
    +
    +};
    +
    +
    +/**
    + * Receipt sent from one peer to another to confirm delivery of a chat message.
    + */
    +struct P2PConfirmationReceiptMessage
    +{
    +  /**
    +   * Message type will be GNUNET_MESSAGE_TYPE_CHAT_P2P_CONFIRMATION_RECEIPT
    +   */
    +  struct GNUNET_MessageHeader header;
    +
    +  /**
    +   * For alignment (should be zero).
    +   */
    +  uint32_t reserved GNUNET_PACKED;
    +
    +  /**
    +   * Signature confirming receipt.  Signature covers everything from header
    +   * through content.
    +   */
    +  struct GNUNET_CRYPTO_RsaSignature signature;
    +
    +  /**
    +   * What is being signed and why?
    +   */
    +  struct GNUNET_CRYPTO_RsaSignaturePurpose purpose;
    +
    +  /**
    +   * Sequence number of the original message.
    +   */
    +  uint32_t msg_sequence_number GNUNET_PACKED;
    +
    +  /**
    +   * Sequence number of the receipt.
    +   */
    +  uint32_t sequence_number GNUNET_PACKED;
    +
    +  /**
    +   * Time of receipt.
    +   */
    +  struct GNUNET_TIME_AbsoluteNBO timestamp;
    +
    +  /**
    +   * Who is confirming the receipt?
    +   */
    +  GNUNET_HashCode target;
    +
    +  /**
    +   * Who is the author of the chat message?
    +   */
    +  GNUNET_HashCode author;
    +
    +  /**
    +   * Hash of the (possibly encrypted) content.
    +   */
    +  GNUNET_HashCode content;
    +
    +};
    +
    +#endif
    +
    +/* end of chat.h */
    Index: src/chat/Makefile.am
    ===================================================================
    --- src/chat/Makefile.am	(revision 0)
    +++ src/chat/Makefile.am	(revision 0)
    @@ -0,0 +1,39 @@
    +INCLUDES = -I$(top_srcdir)/src/include
    +
    +if MINGW
    + WINFLAGS = -Wl,--no-undefined -Wl,--export-all-symbols 
    +endif
    +
    +if USE_COVERAGE
    +  AM_CFLAGS = -fprofile-arcs -ftest-coverage
    +endif
    +
    +lib_LTLIBRARIES = libgnunetchat.la
    +
    +libgnunetchat_la_SOURCES = \
    +  chat.c chat.h
    +
    +libgnunetchat_la_LIBADD = \
    +  $(top_builddir)/src/util/libgnunetutil.la
    +
    +libgnunetchat_la_LDFLAGS = \
    +  $(GN_LIB_LDFLAGS)  $(WINFLAGS) \
    +  -version-info 0:0:0
    +
    +bin_PROGRAMS = \
    + gnunet-service-chat \
    + gnunet-chat
    +
    +gnunet_service_chat_SOURCES = \
    + gnunet-service-chat.c         
    +gnunet_service_chat_LDADD = \
    +  $(top_builddir)/src/core/libgnunetcore.la \
    +  $(top_builddir)/src/util/libgnunetutil.la \
    +  $(GN_LIBINTL)
    +
    +gnunet_chat_SOURCES = \
    + gnunet-chat.c         
    +gnunet_chat_LDADD = \
    +  $(top_builddir)/src/chat/libgnunetchat.la \
    +  $(top_builddir)/src/util/libgnunetutil.la \
    +  $(GN_LIBINTL)
    Index: src/chat/gnunet-service-chat.c
    ===================================================================
    --- src/chat/gnunet-service-chat.c	(revision 0)
    +++ src/chat/gnunet-service-chat.c	(revision 0)
    @@ -0,0 +1,1565 @@
    +/*
    +     This file is part of GNUnet.
    +     (C) 2009, 2011 Christian Grothoff (and other contributing authors)
    +
    +     GNUnet is free software; you can redistribute it and/or modify
    +     it under the terms of the GNU General Public License as published
    +     by the Free Software Foundation; either version 3, or (at your
    +     option) any later version.
    +
    +     GNUnet is distributed in the hope that it will be useful, but
    +     WITHOUT ANY WARRANTY; without even the implied warranty of
    +     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    +     General Public License for more details.
    +
    +     You should have received a copy of the GNU General Public License
    +     along with GNUnet; see the file COPYING.  If not, write to the
    +     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
    +     Boston, MA 02111-1307, USA.
    +*/
    +
    +/**
    + * @file chat/gnunet-service-caht.c
    + * @brief program that tracks template
    + * @author Christian Grothoff
    + * @author Vitaly Minko
    + */
    +
    +#include "platform.h"
    +#include "gnunet_core_service.h"
    +#include "gnunet_crypto_lib.h"
    +#include "gnunet_protocols.h"
    +#include "gnunet_service_lib.h"
    +#include "gnunet_signatures.h"
    +#include "chat.h"
    +
    +#define DEBUG_CHAT_SERVICE GNUNET_YES
    +#define MAX_TRANSMIT_DELAY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 60)
    +#define QUEUE_SIZE 16
    +
    +
    +/**
    + * Linked list of our current clients.
    + */
    +struct ChatClient
    +{
    +  struct ChatClient *next;
    +
    +  /**
    +   * Handle for a chat client (NULL for external clients).
    +   */
    +  struct GNUNET_SERVER_Client *client;
    +
    +  /**
    +   * Public key of the client.
    +   */
    +  struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded public_key;
    +
    +  /**
    +   * Name of the room which the client is in.
    +   */
    +  char *room;
    +
    +  /**
    +   * Serialized metadata of the client.
    +   */
    +  char *member_info;
    +
    +  /**
    +   * Hash of the public key (for convenience).
    +   */
    +  GNUNET_HashCode id;
    +
    +  /**
    +   * Options which the client is willing to receive.
    +   */
    +  uint32_t msg_options;
    +
    +  /**
    +   * Length of serialized metadata in member_info.
    +   */
    +  uint16_t meta_len;
    +
    +  /**
    +   * Sequence number of the last message sent by the client.
    +   */
    +  uint32_t msg_sequence_number;
    +
    +  /**
    +   * Sequence number of the last receipt sent by the client.
    +   * Used to discard already processed receipts.
    +   */
    +  uint32_t rcpt_sequence_number;
    +
    +};
    +
    +
    +/**
    + * Handle to the core service (NULL until we've connected to it).
    + */
    +static struct GNUNET_CORE_Handle *core;
    +
    +/**
    + * Our configuration.
    + */
    +static const struct GNUNET_CONFIGURATION_Handle *cfg;
    +
    +/**
    + * The identity of this host.
    + */
    +static const struct GNUNET_PeerIdentity *me;
    +
    +/**
    + * Head of the list of current clients.
    + */
    +static struct ChatClient *client_list_head = NULL;
    +
    +/**
    + * Notification context containing all connected clients.
    + */
    +struct GNUNET_SERVER_NotificationContext *nc = NULL;
    +
    +
    +/**
    + * Transmit a message notification to the peer.
    + *
    + * @param cls closure, pointer to the 'struct P2PReceiveNotificationMessage'
    + * @param size number of bytes available in buf
    + * @param buf where the callee should write the message
    + * @return number of bytes written to buf
    + */
    +static size_t
    +transmit_message_notification_to_peer (void *cls,
    +				       size_t size,
    +				       void *buf)
    +{
    +  struct P2PReceiveNotificationMessage *my_msg = cls;
    +  struct P2PReceiveNotificationMessage *m = buf;
    +  size_t msg_size;
    +
    +#if DEBUG_CHAT_SERVICE
    +  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    +	      "Transmitting P2P message notification\n");
    +#endif
    +  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;
    +}
    +
    +
    +/**
    + * Ask to send a message notification to the peer.
    + */
    +static void
    +send_message_noficiation (void *cls,
    +			  const struct GNUNET_PeerIdentity *peer,
    +			  const struct GNUNET_TRANSPORT_ATS_Information *atsi)
    +{
    +  struct P2PReceiveNotificationMessage *msg = cls;
    +  struct P2PReceiveNotificationMessage *my_msg;
    +  struct GNUNET_CORE_TransmitHandle *th;
    +
    +  if (NULL == peer)
    +    GNUNET_free (msg);
    +  else
    +    {
    +#if DEBUG_CHAT_SERVICE
    +      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    +		  "Sending message notification to `%s'\n", GNUNET_i2s (peer));
    +#endif
    +      my_msg = GNUNET_memdup (msg, ntohs (msg->header.size));
    +      th = GNUNET_CORE_notify_transmit_ready (core,
    +					      1,
    +					      MAX_TRANSMIT_DELAY,
    +					      peer,
    +					      ntohs (msg->header.size),
    +					      &transmit_message_notification_to_peer,
    +					      my_msg);
    +      GNUNET_assert (NULL != th);
    +    }
    +}
    +
    +
    +/**
    + * A client sent a chat message.  Encrypt the message text if the message is
    + * private.  Send the message to local room members and to all connected peers.
    + *
    + * @param cls closure, NULL
    + * @param client identification of the client
    + * @param message the actual message
    + */
    +static void
    +handle_transmit_request (void *cls,
    +			 struct GNUNET_SERVER_Client *client,
    +			 const struct GNUNET_MessageHeader *message)
    +{
    +  static GNUNET_HashCode all_zeros;
    +  const struct TransmitRequestMessage *trmsg;
    +  struct ReceiveNotificationMessage *rnmsg;
    +  struct P2PReceiveNotificationMessage *p2p_rnmsg;
    +  struct ChatClient *pos;
    +  struct ChatClient *target;
    +  struct GNUNET_CRYPTO_AesSessionKey key;
    +  char encrypted_msg[MAX_MESSAGE_LENGTH];
    +  const char *room;
    +  int msg_len;
    +  int priv_msg;
    +
    +  GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Client sent a chat message\n");
    +  if (ntohs (message->size) <= sizeof (struct TransmitRequestMessage))
    +    {
    +      GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Malformed message: wrong size\n");
    +      GNUNET_break (0);
    +      GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
    +      return;
    +    }
    +  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)
    +    {
    +#if DEBUG_CHAT_SERVICE
    +      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Encrypting the message text\n");
    +#endif
    +      GNUNET_CRYPTO_aes_create_session_key (&key);
    +      msg_len = GNUNET_CRYPTO_aes_encrypt (&trmsg[1],
    +					   msg_len,
    +					   &key,
    +					   (const struct GNUNET_CRYPTO_AesInitializationVector *) INITVALUE,
    +					   encrypted_msg);
    +      if (-1 == msg_len)
    +	{
    +	  GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    +		      "Could not encrypt the message text\n");
    +	  GNUNET_break (0);
    +	  GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
    +	  return;
    +	}
    +    }
    +  rnmsg = GNUNET_malloc (sizeof (struct ReceiveNotificationMessage) + msg_len);
    +  rnmsg->header.size = htons (sizeof (struct ReceiveNotificationMessage) +
    +			      msg_len);
    +  rnmsg->header.type = htons (GNUNET_MESSAGE_TYPE_CHAT_MESSAGE_NOTIFICATION);
    +  rnmsg->msg_options = trmsg->msg_options;
    +  rnmsg->sequence_number = trmsg->sequence_number;
    +  pos = client_list_head;
    +  while ((NULL != pos) && (pos->client != client))
    +    pos = pos->next;
    +  if (NULL == pos)
    +    {
    +      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    +		  "The client is not a member of a chat room. Client has to "
    +		  "join a chat room first\n");
    +      GNUNET_break (0);
    +      GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
    +      GNUNET_free (rnmsg);
    +      return;
    +    }
    +  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;
    +  else
    +    memset (&rnmsg->sender, 0, sizeof (GNUNET_HashCode));
    +  if (priv_msg)
    +    {
    +#if DEBUG_CHAT_SERVICE
    +      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    +		  "Encrypting the session key using the public key of '%s'\n",
    +		  GNUNET_h2s (&trmsg->target));
    +#endif
    +      if (0 == memcmp (&all_zeros, &trmsg->target, sizeof (GNUNET_HashCode)))
    +	{
    +	  GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    +		      "Malformed message: private, but no target\n");
    +	  GNUNET_break (0);
    +	  GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
    +	  GNUNET_free (rnmsg);
    +	  return;
    +	}
    +      memcpy (&rnmsg[1], encrypted_msg, msg_len);
    +      target = client_list_head;
    +      while ((NULL != target) &&
    +	     (0 != memcmp (&target->id,
    +			   &trmsg->target,
    +			   sizeof (GNUNET_HashCode))))
    +	target = target->next;
    +      if (NULL == target)
    +	{
    +	  GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    +		      "Unknown target of the private message\n");
    +	  GNUNET_break (0);
    +	  GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
    +	  GNUNET_free (rnmsg);
    +	  return;
    +	}
    +      if (GNUNET_SYSERR == GNUNET_CRYPTO_rsa_encrypt (&key,
    +						      sizeof (struct GNUNET_CRYPTO_AesSessionKey),
    +						      &target->public_key,
    +						      &rnmsg->encrypted_key))
    +	{
    +	  GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    +		      "Could not encrypt the session key\n");
    +	  GNUNET_break (0);
    +	  GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
    +	  GNUNET_free (rnmsg);
    +	  return;
    +	}
    +    }
    +  else
    +    {
    +      memcpy (&rnmsg[1], &trmsg[1], msg_len);
    +    }
    +  pos = client_list_head;
    +#if DEBUG_CHAT_SERVICE
    +  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending message to local room members\n");
    +#endif
    +  while (NULL != pos)
    +    {
    +      if ((0 == strcmp (room, pos->room)) &&
    +	  (NULL != pos->client) &&
    +	  (pos->client != client))
    +	{
    +	  if (((!priv_msg) ||
    +	       (0 == memcmp (&trmsg->target,
    +			     &pos->id,
    +			     sizeof (GNUNET_HashCode)))) &&
    +	      (0 == (ntohl (trmsg->msg_options) & (~pos->msg_options))))
    +	    {
    +	      GNUNET_SERVER_notification_context_unicast (nc,
    +							  pos->client,
    +							  &rnmsg->header,
    +							  GNUNET_NO);
    +	    }
    +	}
    +      pos = pos->next;
    +    }
    +#if DEBUG_CHAT_SERVICE
    +  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    +	      "Broadcasting message to neighbour peers\n");
    +#endif
    +  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)
    +    {
    +      memcpy (&p2p_rnmsg[1], encrypted_msg, msg_len);
    +      memcpy (&p2p_rnmsg->encrypted_key,
    +	      &rnmsg->encrypted_key,
    +	      sizeof (struct GNUNET_CRYPTO_RsaEncryptedData));
    +    }
    +  else
    +    {
    +      memcpy (&p2p_rnmsg[1], &trmsg[1], msg_len);
    +    }
    +  GNUNET_CORE_iterate_peers (cfg,
    +			     &send_message_noficiation,
    +			     p2p_rnmsg);
    +  GNUNET_SERVER_receive_done (client, GNUNET_OK);
    +  GNUNET_free (rnmsg);
    +}
    +
    +
    +/**
    + * Transmit a join notification to the peer.
    + *
    + * @param cls closure, pointer to the 'struct ChatClient'
    + * @param size number of bytes available in buf
    + * @param buf where the callee should write the message
    + * @return number of bytes written to buf
    + */
    +static size_t
    +transmit_join_notification_to_peer (void *cls,
    +				    size_t size,
    +				    void *buf)
    +{
    +  struct ChatClient *entry = cls;
    +  struct P2PJoinNotificationMessage *m = buf;
    +  size_t room_len;
    +  size_t meta_len;
    +  size_t msg_size;
    +  char *roomptr;
    +
    +#if DEBUG_CHAT_SERVICE
    +  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    +	      "Transmitting P2P join notification\n");
    +#endif
    +  room_len = strlen (entry->room);
    +  meta_len = entry->meta_len;
    +  msg_size = sizeof (struct P2PJoinNotificationMessage) + meta_len + room_len;
    +  GNUNET_assert (size >= msg_size);
    +  GNUNET_assert (NULL != buf);
    +  m = buf;
    +  m->header.type = htons (GNUNET_MESSAGE_TYPE_CHAT_P2P_JOIN_NOTIFICATION);
    +  m->header.size = htons (msg_size);
    +  m->msg_options = htonl (entry->msg_options);
    +  m->room_name_len = htons (room_len);
    +  m->reserved = htons (0);
    +  m->public_key = entry->public_key;
    +  roomptr = (char *) &m[1];
    +  memcpy (roomptr, entry->room, room_len);
    +  if (meta_len > 0)
    +    memcpy (&roomptr[room_len], entry->member_info, meta_len);
    +  return msg_size;
    +}
    +
    +
    +/**
    + * Ask to send a join notification to the peer.
    + */
    +static void
    +send_join_noficiation (void *cls,
    +		       const struct GNUNET_PeerIdentity *peer,
    +		       const struct GNUNET_TRANSPORT_ATS_Information *atsi)
    +{
    +  struct ChatClient *entry = cls;
    +  struct GNUNET_CORE_TransmitHandle *th;
    +  size_t msg_size;
    +
    +  if (NULL != peer)
    +    {
    +#if DEBUG_CHAT_SERVICE
    +      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    +		  "Sending join notification to `%s'\n", GNUNET_i2s (peer));
    +#endif
    +      msg_size = sizeof (struct P2PJoinNotificationMessage) +
    +	strlen (entry->room) + 
    +	entry->meta_len;
    +      th = GNUNET_CORE_notify_transmit_ready (core,
    +					      1,
    +					      MAX_TRANSMIT_DELAY,
    +					      peer,
    +					      msg_size,
    +					      &transmit_join_notification_to_peer,
    +					      entry);
    +      GNUNET_assert (NULL != th);
    +    }
    +}
    +
    +
    +/**
    + * A client asked for entering a chat room.  Add the new member to the list of
    + * clients and notify remaining room members.
    + *
    + * @param cls closure, NULL
    + * @param client identification of the client
    + * @param message the actual message
    + */
    +static void
    +handle_join_request (void *cls,
    +		     struct GNUNET_SERVER_Client *client,
    +		     const struct GNUNET_MessageHeader *message)
    +{
    +  const struct JoinRequestMessage *jrmsg;
    +  char *room_name;
    +  const char *roomptr;
    +  uint16_t header_size;
    +  uint16_t meta_len;
    +  uint16_t room_name_len;
    +  struct ChatClient *new_entry;
    +  struct ChatClient *entry;
    +  struct JoinNotificationMessage *jnmsg;
    +  struct JoinNotificationMessage *entry_jnmsg;
    +
    +  GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Client sent a join request\n");
    +  if (ntohs (message->size) <= sizeof (struct JoinRequestMessage))
    +    {
    +      GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Malformed message: wrong size\n");
    +      GNUNET_break (0);
    +      GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
    +      return;
    +    }
    +  jrmsg = (const struct JoinRequestMessage *) message;
    +  header_size = ntohs (jrmsg->header.size);
    +  room_name_len = ntohs (jrmsg->room_name_len);
    +  if (header_size - sizeof (struct JoinRequestMessage) <=
    +      room_name_len)
    +    {
    +      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    +		  "Malformed message: wrong length of the room name\n");
    +      GNUNET_break (0);
    +      GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
    +      return;
    +    }
    +  meta_len =
    +    header_size - sizeof (struct JoinRequestMessage) - room_name_len;
    +  roomptr = (const char *) &jrmsg[1];
    +  room_name = GNUNET_malloc (room_name_len + 1);
    +  memcpy (room_name, roomptr, room_name_len);
    +  room_name[room_name_len] = '\0';
    +  new_entry = GNUNET_malloc (sizeof (struct ChatClient));
    +  memset (new_entry, 0, sizeof (struct ChatClient));
    +  new_entry->client = client;
    +  new_entry->room = room_name;
    +  new_entry->public_key = jrmsg->public_key;
    +  new_entry->meta_len = meta_len;
    +  if (meta_len > 0)
    +    {
    +      new_entry->member_info = GNUNET_malloc (meta_len);
    +      memcpy (new_entry->member_info, &roomptr[room_name_len], meta_len);
    +    }
    +  else
    +    new_entry->member_info = NULL;
    +  GNUNET_CRYPTO_hash (&new_entry->public_key,
    +		      sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
    +		      &new_entry->id);
    +  new_entry->msg_options = ntohl (jrmsg->msg_options);
    +  new_entry->next = client_list_head;
    +  client_list_head = new_entry;
    +#if DEBUG_CHAT_SERVICE
    +  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    +	      "Synchronizing room members between local clients\n");
    +#endif
    +  jnmsg = GNUNET_malloc (sizeof (struct JoinNotificationMessage) + meta_len);
    +  jnmsg->header.type = htons (GNUNET_MESSAGE_TYPE_CHAT_JOIN_NOTIFICATION);
    +  jnmsg->header.size =
    +    htons (sizeof (struct JoinNotificationMessage) + meta_len);
    +  jnmsg->msg_options = jrmsg->msg_options;
    +  jnmsg->public_key = new_entry->public_key;
    +  memcpy (&jnmsg[1], &roomptr[room_name_len], meta_len);
    +  GNUNET_SERVER_notification_context_add (nc, client);
    +  entry = client_list_head;
    +  while (NULL != entry)
    +    {
    +      if (0 == strcmp (room_name, entry->room))
    +	{
    +	  if (NULL != entry->client)
    +	    GNUNET_SERVER_notification_context_unicast (nc,
    +							entry->client,
    +							&jnmsg->header,
    +							GNUNET_NO);
    +	  if (entry->client != client)
    +	    {
    +	      entry_jnmsg =
    +		GNUNET_malloc (sizeof (struct JoinNotificationMessage) +
    +			       entry->meta_len);
    +	      entry_jnmsg->header.type =
    +		htons (GNUNET_MESSAGE_TYPE_CHAT_JOIN_NOTIFICATION);
    +	      entry_jnmsg->header.size =
    +		htons (sizeof (struct JoinNotificationMessage) +
    +		       entry->meta_len);
    +	      entry_jnmsg->msg_options = entry->msg_options;
    +	      entry_jnmsg->public_key = entry->public_key;
    +	      memcpy (&entry_jnmsg[1], entry->member_info, entry->meta_len);
    +	      GNUNET_SERVER_notification_context_unicast (nc,
    +							  client,
    +							  &entry_jnmsg->header,
    +							  GNUNET_NO);
    +	      GNUNET_free (entry_jnmsg);
    +	    }
    +	}
    +      entry = entry->next;
    +    }
    +#if DEBUG_CHAT_SERVICE
    +  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    +	      "Broadcasting join notification to neighbour peers\n");
    +#endif
    +  GNUNET_CORE_iterate_peers (cfg,
    +			     &send_join_noficiation,
    +			     new_entry);
    +  GNUNET_SERVER_receive_done (client, GNUNET_OK);
    +  GNUNET_free (jnmsg);
    +}
    +
    +/**
    + * Transmit a confirmation receipt to the peer.
    + *
    + * @param cls closure, pointer to the 'struct P2PConfirmationReceiptMessage'
    + * @param size number of bytes available in buf
    + * @param buf where the callee should write the message
    + * @return number of bytes written to buf
    + */
    +static size_t
    +transmit_confirmation_receipt_to_peer (void *cls,
    +				       size_t size,
    +				       void *buf)
    +{
    +  struct P2PConfirmationReceiptMessage *receipt = cls;
    +  size_t msg_size;
    +
    +#if DEBUG_CHAT_SERVICE
    +  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    +	      "Transmitting P2P confirmation receipt to '%s'\n",
    +	      GNUNET_h2s (&receipt->target));
    +#endif
    +  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;
    +}
    +
    +
    +/**
    + * Ask to send a confirmation receipt to the peer.
    + */
    +static void
    +send_confirmation_receipt (void *cls,
    +			   const struct GNUNET_PeerIdentity *peer,
    +			   const struct GNUNET_TRANSPORT_ATS_Information *atsi)
    +{
    +  struct P2PConfirmationReceiptMessage *receipt = cls;
    +  struct P2PConfirmationReceiptMessage *my_receipt;
    +  struct GNUNET_CORE_TransmitHandle *th;
    +  size_t msg_size;
    +
    +  if (NULL == peer)
    +    GNUNET_free (receipt);
    +  else
    +    {
    +#if DEBUG_CHAT_SERVICE
    +      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    +		  "Sending confirmation receipt to `%s'\n", GNUNET_i2s (peer));
    +#endif
    +      msg_size = sizeof (struct P2PConfirmationReceiptMessage);
    +      my_receipt = GNUNET_memdup (receipt,
    +				  sizeof (struct P2PConfirmationReceiptMessage));
    +      th = GNUNET_CORE_notify_transmit_ready (core,
    +					      1,
    +					      MAX_TRANSMIT_DELAY,
    +					      peer,
    +					      msg_size,
    +					      &transmit_confirmation_receipt_to_peer,
    +					      my_receipt);
    +      GNUNET_assert (NULL != th);
    +    }
    +}
    +
    +
    +/**
    + * A client sent a confirmation receipt.  Broadcast the receipt to all connected
    + * peers if the author of the original message is a local client.  Otherwise
    + * check the signature and notify the user if the signature is valid.
    + *
    + * @param cls closure, NULL
    + * @param client identification of the client
    + * @param message the actual message
    + */
    +static void
    +handle_acknowledge_request (void *cls,
    +			    struct GNUNET_SERVER_Client *client,
    +			    const struct GNUNET_MessageHeader *message)
    +{
    +  const struct ConfirmationReceiptMessage *receipt;
    +  struct ConfirmationReceiptMessage *crmsg;
    +  struct P2PConfirmationReceiptMessage *p2p_crmsg;
    +  struct ChatClient *target;
    +  struct ChatClient *author;
    +
    +  GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Client sent a confirmation receipt\n");
    +  receipt = (const struct ConfirmationReceiptMessage *) message;
    +  author = client_list_head;
    +  while ((NULL != author) &&
    +	 (0 != memcmp (&receipt->author,
    +		       &author->id,
    +		       sizeof (GNUNET_HashCode))))
    +    author = author->next;
    +  if (NULL == author)
    +    {
    +      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    +		  "Unknown author of the original message\n");
    +      GNUNET_break (0);
    +      GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
    +      return;
    +    }
    +  target = client_list_head;
    +  while ((NULL != target) &&
    +	 (0 != memcmp (&receipt->target,
    +		       &target->id,
    +		       sizeof (GNUNET_HashCode))))
    +    target = target->next;
    +  if (NULL == target)
    +    {
    +      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    +		  "Unknown target of the confirmation receipt\n");
    +      GNUNET_break (0);
    +      GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
    +      return;
    +    }
    +  if (NULL == author->client)
    +    {
    +      target->rcpt_sequence_number++;
    +#if DEBUG_CHAT_SERVICE
    +      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    +		  "Broadcasting %s's receipt #%u to neighbour peers\n",
    +		  GNUNET_h2s (&target->id), target->rcpt_sequence_number);
    +#endif
    +      p2p_crmsg = GNUNET_malloc (sizeof (struct P2PConfirmationReceiptMessage));
    +      p2p_crmsg->header.size = htons (sizeof (struct P2PConfirmationReceiptMessage));
    +      p2p_crmsg->header.type = htons (GNUNET_MESSAGE_TYPE_CHAT_P2P_CONFIRMATION_RECEIPT);
    +      p2p_crmsg->signature = receipt->signature;
    +      p2p_crmsg->purpose = receipt->purpose;
    +      p2p_crmsg->msg_sequence_number = receipt->sequence_number;
    +      p2p_crmsg->timestamp = receipt->timestamp;
    +      p2p_crmsg->target = receipt->target;
    +      p2p_crmsg->author = receipt->author;
    +      p2p_crmsg->content = receipt->content;
    +      p2p_crmsg->sequence_number = htonl (target->rcpt_sequence_number);
    +      GNUNET_CORE_iterate_peers (cfg,
    +				 &send_confirmation_receipt,
    +				 p2p_crmsg);
    +    }
    +  else
    +    {
    +#if DEBUG_CHAT_SERVICE
    +      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    +		  "Verifying signature of the receipt\n");
    +#endif
    +      if (GNUNET_OK !=
    +	  GNUNET_CRYPTO_rsa_verify (GNUNET_SIGNATURE_PURPOSE_CHAT_RECEIPT,
    +				    &receipt->purpose,
    +				    &receipt->signature,
    +				    &target->public_key))
    +	{
    +	  GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    +		      "Invalid signature of the receipt\n");
    +	  GNUNET_break (0);
    +	  GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
    +	  return;
    +	}
    +#if DEBUG_CHAT_SERVICE
    +      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    +		  "Sending receipt to the client which sent the original message\n");
    +#endif
    +      crmsg = GNUNET_memdup (receipt, sizeof (struct ConfirmationReceiptMessage));
    +      crmsg->header.type = htons (GNUNET_MESSAGE_TYPE_CHAT_CONFIRMATION_NOTIFICATION);
    +      GNUNET_SERVER_notification_context_unicast (nc,
    +						  author->client,
    +						  &crmsg->header,
    +						  GNUNET_NO);
    +      GNUNET_free (crmsg);
    +    }
    +  GNUNET_SERVER_receive_done (client, GNUNET_OK);
    +}
    +
    +
    +/**
    + * Transmit a leave notification to the peer.
    + *
    + * @param cls closure, pointer to the
    + *        'struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded'
    + * @param size number of bytes available in buf
    + * @param buf where the callee should write the message
    + * @return number of bytes written to buf
    + */
    +static size_t
    +transmit_leave_notification_to_peer (void *cls,
    +				     size_t size,
    +				     void *buf)
    +{
    +  struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *public_key = cls;
    +  struct P2PLeaveNotificationMessage *m = buf;
    +  size_t msg_size;
    +
    +#if DEBUG_CHAT_SERVICE
    +  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    +	      "Transmitting P2P leave notification\n");
    +#endif
    +  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);
    +  m->reserved = htons (0);
    +  m->user = *public_key;
    +  GNUNET_free (public_key);
    +  return msg_size;
    +}
    +
    +
    +/**
    + * Ask to send a leave notification to the peer.
    + */
    +static void
    +send_leave_noficiation (void *cls,
    +			const struct GNUNET_PeerIdentity *peer,
    +			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;
    +
    +  if (NULL == peer)
    +    {
    +      GNUNET_free (entry->room);
    +      GNUNET_free_non_null (entry->member_info);
    +      GNUNET_free (entry);
    +    }
    +  else
    +    {
    +#if DEBUG_CHAT_SERVICE
    +      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    +		  "Sending leave notification to `%s'\n", GNUNET_i2s (peer));
    +#endif
    +      msg_size = sizeof (struct P2PLeaveNotificationMessage);
    +      public_key = GNUNET_memdup (&entry->public_key,
    +				  sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded));
    +      th = GNUNET_CORE_notify_transmit_ready (core,
    +					      1,
    +					      MAX_TRANSMIT_DELAY,
    +					      peer,
    +					      msg_size,
    +					      &transmit_leave_notification_to_peer,
    +					      public_key);
    +      GNUNET_assert (NULL != th);
    +    }
    +}
    +
    +
    +/**
    + * A client disconnected.  Remove all of its data structure entries and notify
    + * remaining room members.
    + *
    + * @param cls closure, NULL
    + * @param client identification of the client
    + */
    +static void
    +handle_client_disconnect (void *cls,
    +			  struct GNUNET_SERVER_Client *client)
    +{
    +  struct ChatClient *entry;
    +  struct ChatClient *pos;
    +  struct ChatClient *prev;
    +  struct LeaveNotificationMessage lnmsg;
    +
    +  GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Client disconnected\n");
    +  pos = client_list_head;
    +  prev = NULL;
    +  while ((NULL != pos) && (pos->client != client))
    +    {
    +      prev = pos;
    +      pos = pos->next;
    +    }
    +  if (NULL == pos)
    +    {
    +#if DEBUG_CHAT_SERVICE
    +      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    +		  "No such client. There is nothing to do\n");
    +#endif
    +      return;
    +    }
    +  if (NULL == prev)
    +    client_list_head = pos->next;
    +  else
    +    prev->next = pos->next;
    +  entry = client_list_head;
    +#if DEBUG_CHAT_SERVICE
    +  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    +	      "Notifying local room members that the client has disconnected\n");
    +#endif
    +  lnmsg.header.size = htons (sizeof (struct LeaveNotificationMessage));
    +  lnmsg.header.type = htons (GNUNET_MESSAGE_TYPE_CHAT_LEAVE_NOTIFICATION);
    +  lnmsg.reserved = htonl (0);
    +  lnmsg.user = pos->public_key;
    +  while (NULL != entry)
    +    {
    +      if ((0 == strcmp (pos->room, entry->room)) &&
    +	  (NULL != entry->client))
    +	{
    +	  GNUNET_SERVER_notification_context_unicast (nc,
    +						      entry->client,
    +						      &lnmsg.header,
    +						      GNUNET_NO);
    +	}
    +      entry = entry->next;
    +    }
    +#if DEBUG_CHAT_SERVICE
    +  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    +	      "Broadcasting leave notification to neighbour peers\n");
    +#endif
    +  GNUNET_CORE_iterate_peers (cfg,
    +			     &send_leave_noficiation,
    +			     pos);
    +}
    +
    +
    +/**
    + * Handle P2P join notification.
    + *
    + * @param cls closure, always NULL
    + * @param other the other peer involved
    + * @param message the actual message
    + * @param atsi performance information
    + * @return GNUNET_OK to keep the connection open,
    + *         GNUNET_SYSERR to close it (signal serious error)
    + */
    +static int
    +handle_p2p_join_notification (void *cls,
    +			      const struct GNUNET_PeerIdentity *other,
    +			      const struct GNUNET_MessageHeader *message,
    +			      const struct GNUNET_TRANSPORT_ATS_Information *atsi)
    +{
    +  const struct P2PJoinNotificationMessage *p2p_jnmsg;
    +  char *room_name;
    +  const char *roomptr;
    +  uint16_t header_size;
    +  uint16_t meta_len;
    +  uint16_t room_name_len;
    +  struct ChatClient *new_entry;
    +  struct ChatClient *entry;
    +  struct JoinNotificationMessage *jnmsg;
    +  GNUNET_HashCode id;
    +
    +  GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Got P2P join notification\n");
    +  if (ntohs (message->size) <= sizeof (struct P2PJoinNotificationMessage))
    +    {
    +      GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Malformed message: wrong size\n");
    +      GNUNET_break_op (0);
    +      return GNUNET_SYSERR;
    +    }
    +  p2p_jnmsg = (const struct P2PJoinNotificationMessage *) message;
    +  header_size = ntohs (p2p_jnmsg->header.size);
    +  room_name_len = ntohs (p2p_jnmsg->room_name_len);
    +  if (header_size - sizeof (struct P2PJoinNotificationMessage) <=
    +      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;
    +    }
    +  GNUNET_CRYPTO_hash (&p2p_jnmsg->public_key,
    +		      sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
    +		      &id);
    +  entry = client_list_head;
    +  while (NULL != entry)
    +    {
    +      if (0 == memcmp (&entry->id, &id, sizeof (GNUNET_HashCode)))
    +	{
    +#if DEBUG_CHAT_SERVICE
    +	  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    +		      "The client has already joined. There is nothing to do\n");
    +#endif
    +	  return GNUNET_OK;
    +	}
    +      entry = entry->next;
    +    }
    +  meta_len =
    +    header_size - sizeof (struct P2PJoinNotificationMessage) - room_name_len;
    +  roomptr = (const char *) &p2p_jnmsg[1];
    +  room_name = GNUNET_malloc (room_name_len + 1);
    +  memcpy (room_name, roomptr, room_name_len);
    +  room_name[room_name_len] = '\0';
    +  new_entry = GNUNET_malloc (sizeof (struct ChatClient));
    +  memset (new_entry, 0, sizeof (struct ChatClient));
    +  new_entry->id = id;
    +  new_entry->client = NULL;
    +  new_entry->room = room_name;
    +  new_entry->public_key = p2p_jnmsg->public_key;
    +  new_entry->meta_len = meta_len;
    +  if (meta_len > 0)
    +    {
    +      new_entry->member_info = GNUNET_malloc (meta_len);
    +      memcpy (new_entry->member_info, &roomptr[room_name_len], meta_len);
    +    }
    +  else
    +    new_entry->member_info = NULL;
    +  new_entry->msg_options = ntohl (p2p_jnmsg->msg_options);
    +  new_entry->next = client_list_head;
    +  client_list_head = new_entry;
    +#if DEBUG_CHAT_SERVICE
    +  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    +	      "Notifying local room members that we have a new client\n");
    +#endif
    +  jnmsg = GNUNET_malloc (sizeof (struct JoinNotificationMessage) + meta_len);
    +  jnmsg->header.type = htons (GNUNET_MESSAGE_TYPE_CHAT_JOIN_NOTIFICATION);
    +  jnmsg->header.size =
    +    htons (sizeof (struct JoinNotificationMessage) + meta_len);
    +  jnmsg->msg_options = p2p_jnmsg->msg_options;
    +  jnmsg->public_key = new_entry->public_key;
    +  memcpy (&jnmsg[1], &roomptr[room_name_len], meta_len);
    +  entry = client_list_head;
    +  while (NULL != entry)
    +    {
    +      if ((0 == strcmp (room_name, entry->room)) &&
    +	  (NULL != entry->client))
    +	{
    +	  GNUNET_SERVER_notification_context_unicast (nc,
    +						      entry->client,
    +						      &jnmsg->header,
    +						      GNUNET_NO);
    +	}
    +      entry = entry->next;
    +    }
    +#if DEBUG_CHAT_SERVICE
    +  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    +	      "Broadcasting join notification to neighbour peers\n");
    +#endif
    +  GNUNET_CORE_iterate_peers (cfg,
    +			     &send_join_noficiation,
    +			     new_entry);
    +  GNUNET_free (jnmsg);
    +  return GNUNET_OK;
    +}
    +
    +
    +/**
    + * Handle P2P leave notification.
    + *
    + * @param cls closure, always NULL
    + * @param other the other peer involved
    + * @param message the actual message
    + * @param atsi performance information
    + * @return GNUNET_OK to keep the connection open,
    + *         GNUNET_SYSERR to close it (signal serious error)
    + */
    +static int
    +handle_p2p_leave_notification (void *cls,
    +			       const struct GNUNET_PeerIdentity *other,
    +			       const struct GNUNET_MessageHeader *message,
    +			       const struct GNUNET_TRANSPORT_ATS_Information *atsi)
    +{
    +  const struct P2PLeaveNotificationMessage *p2p_lnmsg;
    +  GNUNET_HashCode id;
    +  struct ChatClient *pos;
    +  struct ChatClient *prev;
    +  struct ChatClient *entry;
    +  struct LeaveNotificationMessage lnmsg;
    +
    +  GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Got P2P leave notification\n");
    +  p2p_lnmsg = (const struct P2PLeaveNotificationMessage *) message;
    +  GNUNET_CRYPTO_hash (&p2p_lnmsg->user,
    +		      sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
    +		      &id);
    +  pos = client_list_head;
    +  prev = NULL;
    +  while (NULL != pos)
    +    {
    +      if (0 == memcmp (&pos->id, &id, sizeof (GNUNET_HashCode)))
    +	break;
    +      prev = pos;
    +      pos = pos->next;
    +    }
    +  if (NULL == pos)
    +    {
    +#if DEBUG_CHAT_SERVICE
    +      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    +		  "No such client. There is nothing to do\n");
    +#endif
    +      return GNUNET_OK;
    +    }
    +  if (NULL == prev)
    +    client_list_head = pos->next;
    +  else
    +    prev->next = pos->next;
    +#if DEBUG_CHAT_SERVICE
    +  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    +	      "Notifying local room members that the client has gone away\n");
    +#endif
    +  lnmsg.header.size = htons (sizeof (struct LeaveNotificationMessage));
    +  lnmsg.header.type = htons (GNUNET_MESSAGE_TYPE_CHAT_LEAVE_NOTIFICATION);
    +  lnmsg.reserved = htonl (0);
    +  lnmsg.user = pos->public_key;
    +  entry = client_list_head;
    +  while (NULL != entry)
    +    {
    +      if (0 == strcmp (pos->room, entry->room) &&
    +	  (NULL != entry->client))
    +	{
    +	  GNUNET_SERVER_notification_context_unicast (nc,
    +						      entry->client,
    +						      &lnmsg.header,
    +						      GNUNET_NO);
    +	}
    +      entry = entry->next;
    +    }
    +#if DEBUG_CHAT_SERVICE
    +  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    +	      "Broadcasting leave notification to neighbour peers\n");
    +#endif
    +  GNUNET_CORE_iterate_peers (cfg,
    +			     &send_leave_noficiation,
    +			     pos);
    +  return GNUNET_OK;
    +}
    +
    +
    +/**
    + * Handle P2P message notification.
    + *
    + * @param cls closure, always NULL
    + * @param other the other peer involved
    + * @param message the actual message
    + * @param atsi performance information
    + * @return GNUNET_OK to keep the connection open,
    + *         GNUNET_SYSERR to close it (signal serious error)
    + */
    +static int
    +handle_p2p_message_notification (void *cls,
    +				 const struct GNUNET_PeerIdentity *other,
    +				 const struct GNUNET_MessageHeader *message,
    +				 const struct GNUNET_TRANSPORT_ATS_Information *atsi)
    +{
    +  const struct P2PReceiveNotificationMessage *p2p_rnmsg;
    +  struct P2PReceiveNotificationMessage *my_p2p_rnmsg;
    +  struct ReceiveNotificationMessage *rnmsg;
    +  struct ChatClient *sender;
    +  struct ChatClient *pos;
    +  static GNUNET_HashCode all_zeros;
    +  int priv_msg;
    +  uint16_t msg_len;
    +
    +  GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Got P2P message notification\n");
    +  if (ntohs (message->size) <= sizeof (struct P2PReceiveNotificationMessage))
    +    {
    +      GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Malformed message: wrong size\n");
    +      GNUNET_break_op (0);
    +      return GNUNET_SYSERR;
    +    }
    +  p2p_rnmsg = (const struct P2PReceiveNotificationMessage *) message;
    +  sender = client_list_head;
    +  while ((NULL != sender) &&
    +	 (0 != memcmp (&sender->id,
    +		       &p2p_rnmsg->sender,
    +		       sizeof (GNUNET_HashCode))))
    +    sender = sender->next;
    +  if (NULL == sender)
    +    {
    +      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    +		  "Unknown source. Rejecting the message\n");
    +      GNUNET_break_op (0);
    +      return GNUNET_SYSERR;
    +    }
    +  if (sender->msg_sequence_number >= ntohl (p2p_rnmsg->sequence_number))
    +    {
    +#if DEBUG_CHAT_SERVICE
    +      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);
    +#endif
    +      return GNUNET_OK;
    +    }
    +  sender->msg_sequence_number = ntohl (p2p_rnmsg->sequence_number);
    +  msg_len = ntohs (p2p_rnmsg->header.size) -
    +    sizeof (struct P2PReceiveNotificationMessage);
    +#if DEBUG_CHAT_SERVICE
    +  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) +
    +			      msg_len);
    +  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,
    +			   &p2p_rnmsg->target, sizeof (GNUNET_HashCode)));
    +  if (priv_msg)
    +    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);
    +  pos = client_list_head;
    +  while (NULL != pos)
    +    {
    +      if ((0 == strcmp (sender->room, pos->room)) &&
    +	  (NULL != pos->client))
    +	{
    +	  if (((!priv_msg) ||
    +	       (0 == memcmp (&p2p_rnmsg->target,
    +			     &pos->id,
    +			     sizeof (GNUNET_HashCode)))) &&
    +	      (0 == (ntohl (p2p_rnmsg->msg_options) & (~pos->msg_options))))
    +	    {
    +	      GNUNET_SERVER_notification_context_unicast (nc,
    +							  pos->client,
    +							  &rnmsg->header,
    +							  GNUNET_NO);
    +	    }
    +	}
    +      pos = pos->next;
    +    }
    +#if DEBUG_CHAT_SERVICE
    +  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    +	      "Broadcasting message notification to neighbour peers\n");
    +#endif
    +  my_p2p_rnmsg = GNUNET_memdup (p2p_rnmsg, ntohs (p2p_rnmsg->header.size));
    +  GNUNET_CORE_iterate_peers (cfg,
    +			     &send_message_noficiation,
    +			     my_p2p_rnmsg);
    +  GNUNET_free (rnmsg);
    +  return GNUNET_OK;
    +}
    +
    +
    +/**
    + * Handle P2P sync request.
    + *
    + * @param cls closure, always NULL
    + * @param other the other peer involved
    + * @param message the actual message
    + * @param atsi performance information
    + * @return GNUNET_OK to keep the connection open,
    + *         GNUNET_SYSERR to close it (signal serious error)
    + */
    +static int
    +handle_p2p_sync_request (void *cls,
    +			 const struct GNUNET_PeerIdentity *other,
    +			 const struct GNUNET_MessageHeader *message,
    +			 const struct GNUNET_TRANSPORT_ATS_Information *atsi)
    +{
    +  struct ChatClient *entry;
    +  struct GNUNET_CORE_TransmitHandle *th;
    +  size_t msg_size;
    +
    +  GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Got P2P sync request\n");
    +#if DEBUG_CHAT_SERVICE
    +  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    +	      "Notifying the requester of all known clients\n");
    +#endif
    +  entry = client_list_head;
    +  while (NULL != entry)
    +    {
    +      msg_size = sizeof (struct P2PJoinNotificationMessage) +
    +	strlen (entry->room) + 
    +	entry->meta_len;
    +      th = GNUNET_CORE_notify_transmit_ready (core,
    +					      1,
    +					      MAX_TRANSMIT_DELAY,
    +					      other,
    +					      msg_size,
    +					      &transmit_join_notification_to_peer,
    +					      entry);
    +      GNUNET_assert (NULL != th);
    +      entry = entry->next;
    +    }
    +  return GNUNET_OK;
    +}
    +
    +
    +/**
    + * Handle P2P confirmation receipt.
    + *
    + * @param cls closure, always NULL
    + * @param other the other peer involved
    + * @param message the actual message
    + * @param atsi performance information
    + * @return GNUNET_OK to keep the connection open,
    + *         GNUNET_SYSERR to close it (signal serious error)
    + */
    +static int
    +handle_p2p_confirmation_receipt (void *cls,
    +				 const struct GNUNET_PeerIdentity *other,
    +				 const struct GNUNET_MessageHeader *message,
    +				 const struct GNUNET_TRANSPORT_ATS_Information *atsi)
    +{
    +  const struct P2PConfirmationReceiptMessage *p2p_crmsg;
    +  struct P2PConfirmationReceiptMessage *my_p2p_crmsg;
    +  struct ConfirmationReceiptMessage *crmsg;
    +  struct ChatClient *target;
    +  struct ChatClient *author;
    +
    +  GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Got P2P confirmation receipt\n");
    +  p2p_crmsg = (const struct P2PConfirmationReceiptMessage *) message;
    +  target = client_list_head;
    +  while ((NULL != target) &&
    +	 (0 != memcmp (&target->id,
    +		       &p2p_crmsg->target,
    +		       sizeof (GNUNET_HashCode))))
    +    target = target->next;
    +  if (NULL == target)
    +    {
    +      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    +		  "Unknown source of the receipt. Rejecting the message\n");
    +      GNUNET_break_op (0);
    +      return GNUNET_SYSERR;
    +    }
    +  if (target->rcpt_sequence_number >= ntohl (p2p_crmsg->sequence_number))
    +    {
    +#if DEBUG_CHAT_SERVICE
    +      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    +		  "This receipt has already been handled."
    +		  " Sequence numbers (msg/sender): %u/%u\n",
    +		  ntohl (p2p_crmsg->sequence_number), target->rcpt_sequence_number);
    +#endif
    +      return GNUNET_OK;
    +    }
    +  target->rcpt_sequence_number = ntohl (p2p_crmsg->sequence_number);
    +  author = client_list_head;
    +  while ((NULL != author) &&
    +	 (0 != memcmp (&author->id,
    +		       &p2p_crmsg->author,
    +		       sizeof (GNUNET_HashCode))))
    +    author = author->next;
    +  if (NULL == author)
    +    {
    +      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    +		  "Unknown addressee. Rejecting the receipt\n");
    +      GNUNET_break_op (0);
    +      return GNUNET_SYSERR;
    +    }
    +
    +  if (NULL == author->client)
    +    {
    +#if DEBUG_CHAT_SERVICE
    +      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    +		  "The author of the original message is not a local client."
    +		  " Broadcasting receipt to neighbour peers\n");
    +#endif
    +      my_p2p_crmsg = GNUNET_memdup (p2p_crmsg, sizeof (struct P2PConfirmationReceiptMessage));
    +      GNUNET_CORE_iterate_peers (cfg,
    +				 &send_confirmation_receipt,
    +				 my_p2p_crmsg);
    +    }
    +  else
    +    {
    +#if DEBUG_CHAT_SERVICE
    +      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    +		  "The author of the original message is a local client."
    +		  " Verifying signature of the receipt\n");
    +#endif
    +      crmsg = GNUNET_malloc (sizeof (struct ConfirmationReceiptMessage));
    +      crmsg->header.size = htons (sizeof (struct ConfirmationReceiptMessage));
    +      crmsg->header.type = htons (GNUNET_MESSAGE_TYPE_CHAT_CONFIRMATION_NOTIFICATION);
    +      crmsg->signature = p2p_crmsg->signature;
    +      crmsg->purpose = p2p_crmsg->purpose;
    +      crmsg->sequence_number = p2p_crmsg->msg_sequence_number;
    +      crmsg->reserved2 = 0;
    +      crmsg->timestamp = p2p_crmsg->timestamp;
    +      crmsg->target = p2p_crmsg->target;
    +      crmsg->author = p2p_crmsg->author;
    +      crmsg->content = p2p_crmsg->content;
    +      if (GNUNET_OK !=
    +	  GNUNET_CRYPTO_rsa_verify (GNUNET_SIGNATURE_PURPOSE_CHAT_RECEIPT,
    +				    &crmsg->purpose,
    +				    &crmsg->signature,
    +				    &target->public_key))
    +	{
    +	  GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    +		      "Invalid signature of the receipt\n");
    +	  GNUNET_break_op (0);
    +	  return GNUNET_SYSERR;
    +	}
    +#if DEBUG_CHAT_SERVICE
    +      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    +		  "The author of the original message is a local client."
    +		  " Sending receipt to the client\n");
    +#endif
    +      GNUNET_SERVER_notification_context_unicast (nc,
    +						  author->client,
    +						  &crmsg->header,
    +						  GNUNET_NO);
    +      GNUNET_free (crmsg);
    +    }
    +  return GNUNET_OK;
    +}
    +
    +
    +/**
    + * Transmit a sync request to the peer.
    + *
    + * @param cls closure, NULL
    + * @param size number of bytes available in buf
    + * @param buf where the callee should write the message
    + * @return number of bytes written to buf
    + */
    +static size_t
    +transmit_sync_request_to_peer (void *cls,
    +			       size_t size,
    +			       void *buf)
    +{
    +  struct GNUNET_MessageHeader *m = buf;
    +  size_t msg_size;
    +
    +#if DEBUG_CHAT_SERVICE
    +  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Transmitting P2P sync request\n");
    +#endif
    +  msg_size = sizeof (struct GNUNET_MessageHeader);
    +  GNUNET_assert (size >= msg_size);
    +  GNUNET_assert (NULL != buf);
    +  m = buf;
    +  m->type = htons (GNUNET_MESSAGE_TYPE_CHAT_P2P_SYNC_REQUEST);
    +  m->size = htons (msg_size);
    +  return msg_size;
    +}
    +
    +
    +/**
    + * Method called whenever a peer connects.
    + *
    + * @param cls closure
    + * @param peer peer identity this notification is about
    + * @param atsi performance data
    + */
    +static void 
    +peer_connect_handler (void *cls,
    +		      const struct GNUNET_PeerIdentity *peer,
    +		      const struct GNUNET_TRANSPORT_ATS_Information *atsi)
    +{
    +  struct GNUNET_CORE_TransmitHandle *th;
    +
    +  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    +	      "Peer connected: %s\n", GNUNET_i2s (peer));
    +  if (0 == memcmp (peer, me, sizeof (struct GNUNET_PeerIdentity)))
    +    return;
    +  th = GNUNET_CORE_notify_transmit_ready (core,
    +					  1,
    +					  MAX_TRANSMIT_DELAY,
    +					  peer,
    +					  sizeof (struct GNUNET_MessageHeader),
    +					  &transmit_sync_request_to_peer,
    +					  NULL);
    +  GNUNET_assert (NULL != th);
    +}
    +
    +
    +/**
    + * Method called whenever a peer disconnects.
    + *
    + * @param cls closure, not used
    + * @param peer peer identity this notification is about
    + */
    +static void
    +peer_disconnect_handler (void *cls,
    +			 const struct GNUNET_PeerIdentity *peer)
    +{
    +  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    +	      "Peer disconnected: %s\n", GNUNET_i2s (peer));
    +}
    +
    +
    +/**
    + * Task run during shutdown.
    + *
    + * @param cls unused
    + * @param tc unused
    + */
    +static void
    +cleanup_task (void *cls,
    +	      const struct GNUNET_SCHEDULER_TaskContext *tc)
    +{
    +  GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Cleaning up\n");
    +  if (NULL != core)
    +    {
    +      GNUNET_CORE_disconnect (core);
    +      core = NULL;
    +    }
    +  if (NULL != nc)
    +    {
    +      GNUNET_SERVER_notification_context_destroy (nc);
    +      nc = NULL;
    +    }
    +}
    +
    +
    +/**
    + * To be called on core init/fail.
    + *
    + * @param cls closure, NULL
    + * @param server handle to the server for this service
    + * @param identity the public identity of this peer
    + * @param publicKey the public key of this peer
    + */
    +static void
    +core_init (void *cls,
    +	   struct GNUNET_CORE_Handle *server,
    +	   const struct GNUNET_PeerIdentity *my_identity,
    +	   const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *publicKey)
    +{
    +  GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Core initialized\n");
    +  me = my_identity;
    +}
    +
    +
    +/**
    + * Process chat requests.
    + *
    + * @param cls closure, NULL
    + * @param server the initialized server
    + * @param cfg configuration to use
    + */
    +static void
    +run (void *cls,
    +     struct GNUNET_SERVER_Handle *server,
    +     const struct GNUNET_CONFIGURATION_Handle *c)
    +{
    +  static const struct GNUNET_SERVER_MessageHandler handlers[] =
    +    {
    +      { &handle_join_request, NULL,
    +	GNUNET_MESSAGE_TYPE_CHAT_JOIN_REQUEST, 0 },
    +      { &handle_transmit_request, NULL,
    +	GNUNET_MESSAGE_TYPE_CHAT_TRANSMIT_REQUEST, 0 },
    +      { &handle_acknowledge_request, NULL,
    +	GNUNET_MESSAGE_TYPE_CHAT_CONFIRMATION_RECEIPT,
    +	sizeof (struct ConfirmationReceiptMessage) },
    +      { NULL, NULL, 0, 0 }
    +    };
    +  static const struct GNUNET_CORE_MessageHandler p2p_handlers[] =
    +    {
    +      { &handle_p2p_join_notification,
    +	GNUNET_MESSAGE_TYPE_CHAT_P2P_JOIN_NOTIFICATION, 0 },
    +      { &handle_p2p_leave_notification,
    +	GNUNET_MESSAGE_TYPE_CHAT_P2P_LEAVE_NOTIFICATION,
    +	sizeof (struct P2PLeaveNotificationMessage) },
    +      { &handle_p2p_message_notification,
    +	GNUNET_MESSAGE_TYPE_CHAT_P2P_MESSAGE_NOTIFICATION, 0 },
    +      { &handle_p2p_sync_request,
    +	GNUNET_MESSAGE_TYPE_CHAT_P2P_SYNC_REQUEST,
    +	sizeof (struct GNUNET_MessageHeader) },
    +      { &handle_p2p_confirmation_receipt,
    +	GNUNET_MESSAGE_TYPE_CHAT_P2P_CONFIRMATION_RECEIPT,
    +	sizeof (struct P2PConfirmationReceiptMessage) },
    +      { NULL, 0, 0 }
    +    };
    +
    +  GNUNET_log_setup ("gnunet-service-chat",
    +#if DEBUG_CHAT_SERVICE
    +		    "DEBUG",
    +#else
    +		    "WARNING",
    +#endif
    +		    NULL);
    +  cfg = c;
    +  nc = GNUNET_SERVER_notification_context_create (server, 16);
    +  GNUNET_SERVER_add_handlers (server, handlers);
    +  core = GNUNET_CORE_connect (cfg,
    +			      QUEUE_SIZE,
    +			      NULL,
    +			      &core_init,
    +			      &peer_connect_handler,
    +			      &peer_disconnect_handler,
    +			      NULL,
    +			      NULL, GNUNET_NO,
    +			      NULL, GNUNET_NO,
    +			      p2p_handlers);
    +  GNUNET_SERVER_disconnect_notify (server, 
    +				   &handle_client_disconnect,
    +				   NULL);
    +  GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
    +				&cleanup_task,
    +				NULL);
    +}
    +
    +
    +/**
    + * The main function for the chat service.
    + *
    + * @param argc number of arguments from the command line
    + * @param argv command line arguments
    + * @return 0 ok, 1 on error
    + */
    +int
    +main (int argc, char *const *argv)
    +{
    +  return (GNUNET_OK ==
    +	  GNUNET_SERVICE_run (argc,
    +			      argv,
    +			      "chat",
    +			      GNUNET_SERVICE_OPTION_NONE,
    +			      &run, NULL)) ? 0 : 1;
    +}
    +
    +/* end of gnunet-service-chat.c */
    Index: src/chat/gnunet-chat.c
    ===================================================================
    --- src/chat/gnunet-chat.c	(revision 0)
    +++ src/chat/gnunet-chat.c	(revision 0)
    @@ -0,0 +1,674 @@
    +/*
    +     This file is part of GNUnet.
    +     (C) 2007, 2008, 2011 Christian Grothoff (and other contributing authors)
    +
    +     GNUnet is free software; you can redistribute it and/or modify
    +     it under the terms of the GNU General Public License as published
    +     by the Free Software Foundation; either version 3, or (at your
    +     option) any later version.
    +
    +     GNUnet is distributed in the hope that it will be useful, but
    +     WITHOUT ANY WARRANTY; without even the implied warranty of
    +     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    +     General Public License for more details.
    +
    +     You should have received a copy of the GNU General Public License
    +     along with GNUnet; see the file COPYING.  If not, write to the
    +     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
    +     Boston, MA 02111-1307, USA.
    +*/
    +
    +/**
    + * @file chat/gnunet-chat.c
    + * @brief Minimal chat command line tool
    + * @author Christian Grothoff
    + * @author Nathan Evans
    + * @author Vitaly Minko
    + */
    +
    +#include "platform.h"
    +#include "gnunet_getopt_lib.h"
    +#include "gnunet_program_lib.h"
    +#include "gnunet_chat_service.h"
    +#include <fcntl.h>
    +
    +static int ret;
    +
    +static const struct GNUNET_CONFIGURATION_Handle *cfg;
    +
    +static char *nickname;
    +
    +static char *room_name;
    +
    +static struct GNUNET_CONTAINER_MetaData *meta;
    +
    +static struct GNUNET_CHAT_Room *room;
    +
    +static GNUNET_SCHEDULER_TaskIdentifier handle_cmd_task = GNUNET_SCHEDULER_NO_TASK;
    +
    +struct ChatCommand
    +{
    +  const char *command;
    +  int (*Action) (const char *arguments, const void *xtra);
    +  const char *helptext;
    +};
    +
    +struct UserList
    +{
    +  struct UserList *next;
    +  struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pkey;
    +  int ignored;
    +};
    +
    +static struct UserList *users;
    +
    +static void
    +free_user_list ()
    +{
    +  struct UserList *next;
    +  while (NULL != users)
    +    {
    +      next = users->next;
    +      GNUNET_free (users);
    +      users = next;
    +    }
    +}
    +
    +static int do_help (const char *args, const void *xtra);
    +
    +
    +/**
    + * Callback used for notification about incoming messages.
    + *
    + * @param cls closure, NULL
    + * @param room in which room was the message received?
    + * @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 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
    + */
    +static int
    +receive_cb (void *cls,
    +	    struct GNUNET_CHAT_Room *room,
    +	    const GNUNET_HashCode *sender,
    +	    const struct GNUNET_CONTAINER_MetaData *meta,
    +	    const char *message,
    +	    enum GNUNET_CHAT_MsgOptions options)
    +{
    +  char *nick;
    +  const char *fmt;
    +
    +  if (NULL != sender)
    +    nick = GNUNET_PSEUDONYM_id_to_name (cfg, sender);
    +  else
    +    nick = GNUNET_strdup (_("anonymous"));
    +  fmt = NULL;
    +  switch (options)
    +    {
    +    case GNUNET_CHAT_MSG_OPTION_NONE:
    +    case GNUNET_CHAT_MSG_ANONYMOUS:
    +      fmt = _("`%s' said: %s\n");
    +      break;
    +    case GNUNET_CHAT_MSG_PRIVATE:
    +      fmt = _("`%s' said to you: %s\n");
    +      break;
    +    case GNUNET_CHAT_MSG_PRIVATE | GNUNET_CHAT_MSG_ANONYMOUS:
    +      fmt = _("`%s' said to you: %s\n");
    +      break;
    +    case GNUNET_CHAT_MSG_AUTHENTICATED:
    +      fmt = _("`%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");
    +      break;
    +    case GNUNET_CHAT_MSG_ACKNOWLEDGED:
    +      fmt = _("`%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");
    +      break;
    +    case GNUNET_CHAT_MSG_AUTHENTICATED | GNUNET_CHAT_MSG_ACKNOWLEDGED:
    +      fmt = _("`%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");
    +      break;
    +    case GNUNET_CHAT_MSG_OFF_THE_RECORD:
    +      fmt = _("`%s' said off the record: %s\n");
    +      break;
    +    default:
    +      fmt = _("<%s> said using an unknown message type: %s\n");
    +      break;
    +    }
    +  fprintf (stdout, fmt, nick, message);
    +  GNUNET_free (nick);
    +  return GNUNET_OK;
    +}
    +
    +
    +/**
    + * Callback used for message delivery confirmations.
    + *
    + * @param cls closure, NULL
    + * @param room in which room was the message received?
    + * @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
    + */
    +static int
    +confirmation_cb (void *cls,
    +		 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)
    +{
    +  char *nick;
    +
    +  nick = GNUNET_PSEUDONYM_id_to_name (cfg, receiver);
    +  fprintf (stdout, _("'%s' acknowledged message #%d\n"), nick, orig_seq_number);
    +  return GNUNET_OK;
    +}
    +
    +
    +/**
    + * Callback used for notification that another room member has joined or left.
    + *
    + * @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)
    + * @param options what types of messages is this member willing to receive?
    + * @return GNUNET_OK
    + */
    +static int
    +member_list_cb (void *cls,
    +		const struct GNUNET_CONTAINER_MetaData *member_info,
    +		const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *member_id,
    +		enum GNUNET_CHAT_MsgOptions options)
    +{
    +  char *nick;
    +  GNUNET_HashCode id;
    +  struct UserList *pos;
    +  struct UserList *prev;
    +
    +  GNUNET_CRYPTO_hash (member_id,
    +		      sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
    +		      &id);
    +  nick = GNUNET_PSEUDONYM_id_to_name (cfg, &id);
    +  fprintf (stdout, member_info != NULL
    +	   ? _("`%s' entered the room\n") : _("`%s' left the room\n"), nick);
    +  GNUNET_free (nick);
    +  if (NULL != member_info)
    +    {
    +      /* user joining */
    +      pos = GNUNET_malloc (sizeof (struct UserList));
    +      pos->next = users;
    +      pos->pkey = *member_id;
    +      pos->ignored = GNUNET_NO;
    +      users = pos;
    +    }
    +  else
    +    {
    +      /* user leaving */
    +      prev = NULL;
    +      pos = users;
    +      while ((NULL != pos) &&
    +	     (0 != memcmp (&pos->pkey,
    +			   member_id,
    +			   sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded))))
    +	{
    +	  prev = pos;
    +	  pos = pos->next;
    +	}
    +      if (NULL == pos)
    +	{
    +	  GNUNET_break (0);
    +	}
    +      else
    +	{
    +	  if (NULL == prev)
    +	    users = pos->next;
    +	  else
    +	    prev->next = pos->next;
    +	  GNUNET_free (pos);
    +	}
    +    }
    +  return GNUNET_OK;
    +}
    +
    +
    +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;
    +  GNUNET_HashCode me;
    +
    +  if (arg[0] == '#')
    +    arg++;                      /* ignore first hash */
    +  GNUNET_CHAT_leave_room (room);
    +  free_user_list ();
    +  GNUNET_free (room_name);
    +  room_name = GNUNET_strdup (arg);
    +  room = GNUNET_CHAT_join_room (cfg,
    +				nickname,
    +				meta,
    +				room_name,
    +				-1,
    +				&receive_cb, NULL,
    +				&member_list_cb, NULL,
    +				&confirmation_cb, NULL, &me);
    +  if (NULL == room)
    +    {
    +      fprintf (stdout, _("Could not change username\n"));
    +      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);
    +  GNUNET_free (my_name);
    +  return GNUNET_OK;
    +}
    +
    +
    +static int
    +do_nick (const char *msg, const void *xtra)
    +{
    +  char *my_name;
    +  GNUNET_HashCode me;
    +
    +  GNUNET_CHAT_leave_room (room);
    +  free_user_list ();
    +  GNUNET_free (nickname);
    +  GNUNET_CONTAINER_meta_data_destroy (meta);
    +  nickname = GNUNET_strdup (msg);
    +  meta = GNUNET_CONTAINER_meta_data_create ();
    +  GNUNET_CONTAINER_meta_data_insert (meta,
    +				     "<gnunet>",
    +				     EXTRACTOR_METATYPE_TITLE,
    +				     EXTRACTOR_METAFORMAT_UTF8,
    +				     "text/plain",
    +				     nickname,
    +				     strlen(nickname)+1);
    +  room = GNUNET_CHAT_join_room (cfg,
    +				nickname,
    +				meta,
    +				room_name,
    +				-1,
    +				&receive_cb, NULL,
    +				&member_list_cb, NULL,
    +				&confirmation_cb, NULL, &me);
    +  if (NULL == room)
    +    {
    +      fprintf (stdout, _("Could not change username\n"));
    +      return GNUNET_SYSERR;
    +    }
    +  my_name = GNUNET_PSEUDONYM_id_to_name (cfg, &me);
    +  fprintf (stdout, _("Changed username to `%s'\n"), my_name);
    +  GNUNET_free (my_name);
    +  return GNUNET_OK;
    +}
    +
    +
    +static int
    +do_names (const char *msg, const void *xtra)
    +{
    +  char *name;
    +  struct UserList *pos;
    +  GNUNET_HashCode pid;
    +
    +  fprintf (stdout, _("Users in room `%s': "), room_name);
    +  pos = users;
    +  while (NULL != pos)
    +    {
    +      GNUNET_CRYPTO_hash (&pos->pkey,
    +			  sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
    +			  &pid);
    +      name = GNUNET_PSEUDONYM_id_to_name (cfg, &pid);
    +      fprintf (stdout, "`%s' ", name);
    +      GNUNET_free (name);
    +      pos = pos->next;
    +    }
    +  fprintf (stdout, "\n");
    +  return GNUNET_OK;
    +}
    +
    +
    +static int
    +do_pm (const char *msg, const void *xtra)
    +{
    +  char *user;
    +  GNUNET_HashCode uid;
    +  GNUNET_HashCode pid;
    +  uint32_t seq;
    +  struct UserList *pos;
    +
    +  if (NULL == strstr (msg, " "))
    +    {
    +      fprintf (stderr, _("Syntax: /msg USERNAME MESSAGE"));
    +      return GNUNET_OK;
    +    }
    +  user = GNUNET_strdup (msg);
    +  strstr (user, " ")[0] = '\0';
    +  msg += strlen (user) + 1;
    +  if (GNUNET_OK != GNUNET_PSEUDONYM_name_to_id (cfg, user, &uid))
    +    {
    +      fprintf (stderr, _("Unknown user `%s'\n"), user);
    +      GNUNET_free (user);
    +      return GNUNET_OK;
    +    }
    +  pos = users;
    +  while (NULL != pos)
    +    {
    +      GNUNET_CRYPTO_hash (&pos->pkey,
    +			  sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
    +			  &pid);
    +      if (0 == memcmp (&pid, &uid, sizeof (GNUNET_HashCode)))
    +	break;
    +      pos = pos->next;
    +    }
    +  if (NULL == pos)
    +    {
    +      fprintf (stderr, _("User `%s' is currently not in the room!\n"), user);
    +      GNUNET_free (user);
    +      return GNUNET_OK;
    +    }
    +  GNUNET_CHAT_send_message (room,
    +			    msg,
    +			    GNUNET_CHAT_MSG_PRIVATE,
    +			    &pos->pkey,
    +			    &seq);
    +  GNUNET_free (user);
    +  return GNUNET_OK;
    +}
    +
    +
    +static int
    +do_transmit_sig (const char *msg, const void *xtra)
    +{
    +  uint32_t seq;
    +  GNUNET_CHAT_send_message (room,
    +			    msg,
    +			    GNUNET_CHAT_MSG_AUTHENTICATED,
    +			    NULL, &seq);
    +  return GNUNET_OK;
    +}
    +
    +
    +static int
    +do_transmit_ack (const char *msg, const void *xtra)
    +{
    +  uint32_t seq;
    +  GNUNET_CHAT_send_message (room,
    +			    msg,
    +			    GNUNET_CHAT_MSG_ACKNOWLEDGED,
    +			    NULL, &seq);
    +  return GNUNET_OK;
    +}
    +
    +
    +static int
    +do_quit (const char *args, const void *xtra)
    +{
    +  return GNUNET_SYSERR;
    +}
    +
    +
    +static int
    +do_unknown (const char *msg, const void *xtra)
    +{
    +  fprintf (stderr, _("Unknown command `%s'\n"), msg);
    +  return GNUNET_OK;
    +}
    +
    +
    +/**
    + * List of supported IRC commands. The order matters!
    + */
    +static struct ChatCommand commands[] = {
    +  {"/join ", &do_join,
    +   gettext_noop
    +   ("Use `/join #roomname' to join a chat room. Joining a room will cause you"
    +    " to leave the current room")},
    +  {"/nick ", &do_nick,
    +   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,
    +   gettext_noop
    +   ("Use `/msg nickname message' to send a private message to the specified"
    +    " user")},
    +  {"/notice ", &do_pm,
    +   gettext_noop ("The `/notice' command is an alias for `/msg'")},
    +  {"/query ", &do_pm,
    +   gettext_noop ("The `/query' command is an alias for `/msg'")},
    +  {"/sig ", &do_transmit_sig,
    +   gettext_noop ("Use `/sig message' to send a signed public message")},
    +  {"/ack ", &do_transmit_ack,
    +   gettext_noop
    +   ("Use `/ack message' to require signed acknowledgment of the message")},
    +  {"/quit", &do_quit,
    +   gettext_noop ("Use `/quit' to terminate gnunet-chat")},
    +  {"/leave", &do_quit,
    +   gettext_noop ("The `/leave' command is an alias for `/quit'")},
    +  {"/names", &do_names,
    +   gettext_noop
    +   ("Use `/names' to list all of the current members in the chat room")},
    +  {"/help", &do_help,
    +   gettext_noop ("Use `/help command' to get help for a specific command")},
    +  /* 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},
    +  {NULL, NULL, NULL},
    +};
    +
    +
    +static int
    +do_help (const char *args, const void *xtra)
    +{
    +  int i;
    +  i = 0;
    +  while ((NULL != args) &&
    +	 (0 != strlen (args)) && (commands[i].Action != &do_help))
    +    {
    +      if (0 ==
    +	  strncasecmp (&args[1], &commands[i].command[1], strlen (args) - 1))
    +	{
    +	  fprintf (stdout, "%s\n", gettext (commands[i].helptext));
    +	  return GNUNET_OK;
    +	}
    +      i++;
    +    }
    +  i = 0;
    +  fprintf (stdout, "Available commands:");
    +  while (commands[i].Action != &do_help)
    +    {
    +      fprintf (stdout, " %s", gettext (commands[i].command));
    +      i++;
    +    }
    +  fprintf (stdout, "\n");
    +  fprintf (stdout, "%s\n", gettext (commands[i].helptext));
    +  return GNUNET_OK;
    +}
    +
    +
    +static void 
    +do_stop_task (void *cls,
    +	      const struct GNUNET_SCHEDULER_TaskContext *tc)
    +{
    +  GNUNET_CHAT_leave_room (room);
    +  if (handle_cmd_task != GNUNET_SCHEDULER_NO_TASK)
    +    {
    +      GNUNET_SCHEDULER_cancel (handle_cmd_task);
    +      handle_cmd_task = GNUNET_SCHEDULER_NO_TASK;
    +    }     
    +  free_user_list ();
    +  GNUNET_CONTAINER_meta_data_destroy (meta);
    +  GNUNET_free (room_name);
    +  GNUNET_free (nickname);
    +}
    +
    +
    +void
    +handle_command (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
    +{
    +  char message[MAX_MESSAGE_LENGTH + 1];
    +  int i;
    +
    +  /* read message from command line and handle it */
    +  memset (message, 0, MAX_MESSAGE_LENGTH + 1);
    +  if (NULL == fgets (message, MAX_MESSAGE_LENGTH, stdin))
    +    goto next;
    +  if (strlen (message) == 0)
    +    goto next;
    +  if (message[strlen (message) - 1] == '\n')
    +    message[strlen (message) - 1] = '\0';
    +  if (strlen (message) == 0)
    +    goto next;
    +  i = 0;
    +  while ((NULL != commands[i].command) &&
    +	 (0 != strncasecmp (commands[i].command,
    +			    message, strlen (commands[i].command))))
    +    i++;
    +  if (GNUNET_OK !=
    +      commands[i].Action (&message[strlen (commands[i].command)], NULL))
    +    goto out;
    +
    +next:
    +  handle_cmd_task =
    +    GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
    +								 100),
    +				  &handle_command,
    +				  NULL);
    +  return;
    +
    +out:
    +  handle_cmd_task = GNUNET_SCHEDULER_NO_TASK;
    +  GNUNET_SCHEDULER_shutdown ();
    +}
    +
    +
    +/**
    + * Main function that will be run by the scheduler.
    + *
    + * @param cls closure, NULL
    + * @param args remaining command-line arguments
    + * @param cfgfile name of the configuration file used (for saving, can be NULL!)
    + * @param cfg configuration
    + */
    +static void
    +run (void *cls,
    +     char *const *args,
    +     const char *cfgfile,
    +     const struct GNUNET_CONFIGURATION_Handle *c)
    +{
    +  GNUNET_HashCode me;
    +  char *my_name;
    +
    +  cfg = c;
    +  /* check arguments */
    +  if (NULL == nickname)
    +    {
    +      fprintf (stderr, _("You must specify a nickname\n"));
    +      ret = -1;
    +      return;
    +    }
    +  if (NULL == room_name)
    +    room_name = GNUNET_strdup ("gnunet");
    +  meta = GNUNET_CONTAINER_meta_data_create ();
    +  GNUNET_CONTAINER_meta_data_insert (meta,
    +				     "<gnunet>",
    +				     EXTRACTOR_METATYPE_TITLE,
    +				     EXTRACTOR_METAFORMAT_UTF8,
    +				     "text/plain",
    +				     nickname,
    +				     strlen(nickname)+1);
    +  room = GNUNET_CHAT_join_room (cfg,
    +				nickname,
    +				meta,
    +				room_name,
    +				-1,
    +				&receive_cb, NULL,
    +				&member_list_cb, NULL,
    +				&confirmation_cb, NULL, &me);
    +  if (NULL == room)
    +    {
    +      fprintf (stderr, _("Failed to join room `%s'\n"), room_name);
    +      GNUNET_free (room_name);
    +      GNUNET_free (nickname);
    +      GNUNET_CONTAINER_meta_data_destroy (meta);
    +      ret = -1;
    +      return;
    +    }
    +  my_name = GNUNET_PSEUDONYM_id_to_name (cfg, &me);
    +  fprintf (stdout, _("Joined 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,
    +					&handle_command,
    +					NULL);
    +  GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
    +				&do_stop_task,
    +				NULL);
    +}
    +
    +
    +/**
    + * The main function to chat via GNUnet.
    + *
    + * @param argc number of arguments from the command line
    + * @param argv command line arguments
    + * @return 0 ok, 1 on error
    + */
    +int
    +main (int argc, char *const *argv)
    +{
    +  int flags;
    +  static const struct GNUNET_GETOPT_CommandLineOption options[] = {
    +    {'n', "nick", "NAME",
    +     gettext_noop ("set the nickname to use (required)"),
    +     1, &GNUNET_GETOPT_set_string, &nickname},
    +    {'r', "room", "NAME",
    +     gettext_noop ("set the chat room to join"),
    +     1, &GNUNET_GETOPT_set_string, &room_name},
    +    GNUNET_GETOPT_OPTION_END
    +  };
    +
    +  flags = fcntl (0, F_GETFL, 0);
    +  flags |= O_NONBLOCK;
    +  fcntl (0, F_SETFL, flags);
    +  return (GNUNET_OK ==
    +	  GNUNET_PROGRAM_run (argc,
    +			      argv,
    +			      "gnunet-chat",
    +			      gettext_noop ("Join a chat on GNUnet."),
    +			      options, &run, NULL)) ? ret : 1;
    +}
    +
    +/* end of gnunet-chat.c */
    Index: src/include/gnunet_protocols.h
    ===================================================================
    --- src/include/gnunet_protocols.h	(revision 14186)
    +++ src/include/gnunet_protocols.h	(working copy)
    @@ -738,13 +738,79 @@
     
     
     /**
    + * Message sent from client to join a chat room.
    + */
    +#define GNUNET_MESSAGE_TYPE_CHAT_JOIN_REQUEST 300
    +
    +/**
    + * Message sent to client to indicate joining of another room member.
    + */
    +#define GNUNET_MESSAGE_TYPE_CHAT_JOIN_NOTIFICATION 301
    +
    +/**
    + * Message sent to client to indicate leaving of another room member.
    + */
    +#define GNUNET_MESSAGE_TYPE_CHAT_LEAVE_NOTIFICATION 302
    +
    +/**
    + * Notification sent by service to client indicating that we've received a chat
    + * message.
    + */
    +#define GNUNET_MESSAGE_TYPE_CHAT_MESSAGE_NOTIFICATION 303
    +
    +/**
    + * Request sent by client to transmit a chat message to another room members.
    + */
    +#define GNUNET_MESSAGE_TYPE_CHAT_TRANSMIT_REQUEST 304
    +
    +/**
    + * Receipt sent from a message receiver to the service to confirm delivery of
    + * a chat message.
    + */
    +#define GNUNET_MESSAGE_TYPE_CHAT_CONFIRMATION_RECEIPT 305
    +
    +/**
    + * Notification sent from the service to the original sender
    + * to acknowledge delivery of a chat message.
    + */
    +#define GNUNET_MESSAGE_TYPE_CHAT_CONFIRMATION_NOTIFICATION 306
    +
    +/**
    + * P2P message sent to indicate joining of another room member.
    + */
    +#define GNUNET_MESSAGE_TYPE_CHAT_P2P_JOIN_NOTIFICATION 307
    +
    +/**
    + * P2P message sent to indicate leaving of another room member.
    + */
    +#define GNUNET_MESSAGE_TYPE_CHAT_P2P_LEAVE_NOTIFICATION 308
    +
    +/**
    + * P2P message sent to a newly connected peer to request its known clients in
    + * order to synchronize room members.
    + */
    +#define GNUNET_MESSAGE_TYPE_CHAT_P2P_SYNC_REQUEST 309
    +
    +/**
    + * Notification sent from one peer to another to indicate that we have received
    + * a chat message.
    + */
    +#define GNUNET_MESSAGE_TYPE_CHAT_P2P_MESSAGE_NOTIFICATION 310
    +
    +/**
    + * P2P receipt confirming delivery of a chat message.
    + */
    +#define GNUNET_MESSAGE_TYPE_CHAT_P2P_CONFIRMATION_RECEIPT 311
    +
    +
    +/**
      * Type used to match 'all' message types.
      */
     #define GNUNET_MESSAGE_TYPE_ALL 65535
     
     /*
       TODO:
    -  - applications (FS, VPN, CHAT, TRACEKIT, TBENCH)
    +  - applications (FS, VPN, TRACEKIT, TBENCH)
     */
     
     
    Index: src/include/gnunet_chat_service.h
    ===================================================================
    --- src/include/gnunet_chat_service.h	(revision 0)
    +++ src/include/gnunet_chat_service.h	(revision 0)
    @@ -0,0 +1,234 @@
    +/*
    +     This file is part of GNUnet.
    +     (C) 2009, 2011 Christian Grothoff (and other contributing authors)
    +
    +     GNUnet is free software; you can redistribute it and/or modify
    +     it under the terms of the GNU General Public License as published
    +     by the Free Software Foundation; either version 3, or (at your
    +     option) any later version.
    +
    +     GNUnet is distributed in the hope that it will be useful, but
    +     WITHOUT ANY WARRANTY; without even the implied warranty of
    +     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    +     General Public License for more details.
    +
    +     You should have received a copy of the GNU General Public License
    +     along with GNUnet; see the file COPYING.  If not, write to the
    +     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
    +     Boston, MA 02111-1307, USA.
    +*/
    +
    +/**
    + * @file include/gnunet_chat_service.h
    + * @brief API for chatting via GNUnet 
    + * @author Christian Grothoff
    + * @author Nathan Evans
    + * @author Vitaly Minko
    + */
    +
    +#ifndef GNUNET_CHAT_SERVICE_H
    +#define GNUNET_CHAT_SERVICE_H
    +
    +#include "gnunet_util_lib.h"
    +
    +#ifdef __cplusplus
    +extern "C"
    +{
    +#if 0                           /* keep Emacsens' auto-indent happy */
    +}
    +#endif
    +#endif
    +
    +
    +#define GNUNET_CHAT_VERSION 0x00000003
    +#define MAX_MESSAGE_LENGTH (32 * 1024)
    +
    +/**
    + * Options for messaging.  Compatible options can be OR'ed together.
    + */
    +enum GNUNET_CHAT_MsgOptions
    +  {
    +    /**
    +     * No special options.
    +     */
    +    GNUNET_CHAT_MSG_OPTION_NONE = 0,
    +  
    +    /**
    +     * Encrypt the message so that only the receiver can decrypt it.
    +     */
    +    GNUNET_CHAT_MSG_PRIVATE = 1,
    +  
    +    /**
    +     * Hide the identity of the sender.
    +     */
    +    GNUNET_CHAT_MSG_ANONYMOUS = 2,
    +  
    +    /**
    +     * Sign the content, authenticating the sender (using the provided private
    +     * key, which may represent a pseudonym).
    +     */
    +    GNUNET_CHAT_MSG_AUTHENTICATED = 4,
    +  
    +    /**
    +     * Require signed acknowledgment before completing delivery (and of course,
    +     * only acknowledge if delivery is guaranteed).
    +     */
    +    GNUNET_CHAT_MSG_ACKNOWLEDGED = 8,
    +  
    +    /**
    +     * Authenticate for the receiver, but ensure that receiver cannot prove
    +     * authenticity to third parties later. (not yet implemented)
    +     */
    +    GNUNET_CHAT_MSG_OFF_THE_RECORD = 16,
    +  
    +  };
    +
    +/**
    + * Handle for a (joined) chat room.
    + */
    +struct GNUNET_CHAT_Room;
    +
    +/**
    + * Callback used for notification about incoming messages.
    + *
    + * @param cls closure
    + * @param room in which room was the message received?
    + * @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 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
    + */
    +typedef int (*GNUNET_CHAT_MessageCallback) (void *cls,
    +					    struct GNUNET_CHAT_Room *room,
    +					    const GNUNET_HashCode *sender,
    +					    const struct GNUNET_CONTAINER_MetaData *member_info,
    +					    const char *message,
    +					    enum GNUNET_CHAT_MsgOptions options);
    +
    +/**
    + * Callback used for notification that another room member has joined or left.
    + *
    + * @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)
    + * @param options what types of messages is this member willing to receive?
    + * @return GNUNET_OK
    + */
    +typedef int (*GNUNET_CHAT_MemberListCallback) (void *cls,
    +					       const struct GNUNET_CONTAINER_MetaData *member_info,
    +					       const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *member_id,
    +					       enum GNUNET_CHAT_MsgOptions options);
    +
    +/**
    + * Callback used for message delivery confirmations.
    + *
    + * @param cls closure
    + * @param room in which room was the message received?
    + * @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
    + */
    +typedef int (*GNUNET_CHAT_MessageConfirmation) (void *cls,
    +						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);
    +
    +/**
    + * Join a chat room.
    + *
    + * @param cfg configuration
    + * @param nick_name nickname of the user joining (used to
    + *                  determine which public key to use);
    + *                  the nickname should probably also
    + *                  be used in the member_info (as "EXTRACTOR_TITLE")
    + * @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 messageCallback which function to call if a message has
    + *        been received?
    + * @param message_cls argument to callback
    + * @param memberCallback which function to call for join/leave notifications
    + * @param member_cls argument to callback
    + * @param confirmationCallback which function to call for confirmations
    + *        (maybe NULL)
    + * @param confirmation_cls argument to callback
    + * @param me member ID (pseudonym)
    + * @return NULL on error
    + */
    +struct GNUNET_CHAT_Room *
    +GNUNET_CHAT_join_room (const struct GNUNET_CONFIGURATION_Handle *cfg,
    +		       const char *nick_name,
    +		       struct GNUNET_CONTAINER_MetaData *member_info,
    +		       const char *room_name,
    +		       enum GNUNET_CHAT_MsgOptions msg_options,
    +		       GNUNET_CHAT_MessageCallback messageCallback,
    +		       void *message_cls,
    +		       GNUNET_CHAT_MemberListCallback memberCallback,
    +		       void *member_cls,
    +		       GNUNET_CHAT_MessageConfirmation confirmationCallback,
    +		       void *confirmation_cls,
    +		       GNUNET_HashCode *me);
    +
    +/**
    + * Send a message.
    + *
    + * @param room handle for the chat room
    + * @param message message to be sent
    + * @param options options for the message
    + * @param receiver use NULL to send to everyone in the room
    + * @param sequence_number where to write the sequence id of the message
    + */
    +void
    +GNUNET_CHAT_send_message (struct GNUNET_CHAT_Room *room,
    +			  const char *message,
    +			  enum GNUNET_CHAT_MsgOptions options,
    +			  const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *receiver,
    +			  uint32_t *sequence_number);
    +
    +
    +/**
    + * Leave a chat room.
    + */
    +void
    +GNUNET_CHAT_leave_room (struct GNUNET_CHAT_Room *chat_room);
    +
    +
    +#if 0
    +/* these are not yet implemented / supported */
    +/**
    + * Callback function to iterate over rooms.
    + *
    + * @return GNUNET_OK to continue, GNUNET_SYSERR to abort iteration
    + */
    +typedef int (*GNUNET_CHAT_RoomIterator) (const char *room,
    +                                         const char *topic, void *cls);
    +
    +/**
    + * List all of the (publically visible) chat rooms.
    + * @return number of rooms on success, GNUNET_SYSERR if iterator aborted
    + */
    +int GNUNET_CHAT_list_rooms (struct GNUNET_GE_Context *ectx,
    +                            struct GNUNET_GC_Configuration *cfg,
    +                            GNUNET_CHAT_RoomIterator it, void *cls);
    +#endif
    +
    +
    +#if 0                           /* keep Emacsens' auto-indent happy */
    +{
    +#endif
    +#ifdef __cplusplus
    +}
    +#endif
    +
    +#endif
    +
    +/* end of gnunet_chat_service.h */
    Index: src/include/gnunet_signatures.h
    ===================================================================
    --- src/include/gnunet_signatures.h	(revision 14186)
    +++ src/include/gnunet_signatures.h	(working copy)
    @@ -102,6 +102,15 @@
      */
     #define GNUNET_SIGNATURE_PURPOSE_DNS_RECORD 11
     
    +/**
    + * Signature of a chat message.
    + */
    +#define GNUNET_SIGNATURE_PURPOSE_CHAT_MESSAGE 12
    +
    +/**
    + * Signature of confirmation receipt for a chat message.
    + */
    +#define GNUNET_SIGNATURE_PURPOSE_CHAT_RECEIPT 13
     
     #if 0                           /* keep Emacsens' auto-indent happy */
     {
    Index: src/Makefile.am
    ===================================================================
    --- src/Makefile.am	(revision 14186)
    +++ src/Makefile.am	(working copy)
    @@ -32,4 +32,5 @@
       fs \
       fragmentation \
       mesh \
    -  vpn
    +  vpn \
    +  chat
    Index: configure.ac
    ===================================================================
    --- configure.ac	(revision 14186)
    +++ configure.ac	(working copy)
    @@ -810,6 +810,7 @@
     src/transport/Makefile
     src/util/Makefile
     src/vpn/Makefile
    +src/chat/Makefile
     pkgconfig/Makefile
     pkgconfig/gnunetarm.pc
     pkgconfig/gnunetblock.pc
    
    patch file icon gnunet-svn@14186-chat.patch (114,254 bytes) 2011-01-17 02:16 +

-Relationships Relation Graph ] Dependency Graph ]
+Relationships

-Notes

~0004231

Christian Grothoff (manager)

Applied as SVN 14187.

With the next file, could you include a change adding yourself to the AUTHOR's file? I don't know which (if any) e-mail address you want to have there...
+Notes

-Issue History
Date Modified Username Field Change
2011-01-17 02:16 vminko New Issue
2011-01-17 02:16 vminko File Added: gnunet-svn@14186-chat.patch
2011-01-17 09:30 Christian Grothoff Note Added: 0004231
2011-01-17 09:30 Christian Grothoff Status new => resolved
2011-01-17 09:30 Christian Grothoff Resolution open => fixed
2011-01-17 09:30 Christian Grothoff Assigned To => Christian Grothoff
2011-04-27 16:18 Christian Grothoff Status resolved => closed
+Issue History