Nils Durner <durner@gnunet.org>
Safey Allah Mohammed <safey.allah@gmail.com>
Philipp Tölke <toelke@in.tum.de>
+Vitaly Minko <vitaly.minko@gmail.com>
Code contributions also came from:
Adam Warrington [ UPnP ]
$(top_builddir)/src/chat/libgnunetchat.la \
$(top_builddir)/src/util/libgnunetutil.la \
$(GN_LIBINTL)
+
+check_PROGRAMS = \
+ test_chat \
+ test_chat_acknowledgement \
+ test_chat_anonymous \
+ test_chat_authentication \
+ test_chat_p2p \
+ test_chat_acknowledgement_p2p \
+ test_chat_anonymous_p2p \
+ test_chat_authentication_p2p \
+ test_chat_private \
+ test_chat_private_p2p
+
+if !DISABLE_TEST_RUN
+TESTS = $(check_PROGRAMS)
+endif
+
+test_chat_SOURCES = \
+ test_chat.c
+test_chat_LDADD = \
+ $(top_builddir)/src/chat/libgnunetchat.la \
+ $(top_builddir)/src/util/libgnunetutil.la
+
+test_chat_acknowledgement_SOURCES = \
+ test_chat.c
+test_chat_acknowledgement_LDADD = \
+ $(top_builddir)/src/chat/libgnunetchat.la \
+ $(top_builddir)/src/util/libgnunetutil.la
+
+test_chat_anonymous_SOURCES = \
+ test_chat.c
+test_chat_anonymous_LDADD = \
+ $(top_builddir)/src/chat/libgnunetchat.la \
+ $(top_builddir)/src/util/libgnunetutil.la
+
+test_chat_authentication_SOURCES = \
+ test_chat.c
+test_chat_authentication_LDADD = \
+ $(top_builddir)/src/chat/libgnunetchat.la \
+ $(top_builddir)/src/util/libgnunetutil.la
+
+test_chat_p2p_SOURCES = \
+ test_chat.c
+test_chat_p2p_LDADD = \
+ $(top_builddir)/src/chat/libgnunetchat.la \
+ $(top_builddir)/src/util/libgnunetutil.la
+
+test_chat_acknowledgement_p2p_SOURCES = \
+ test_chat.c
+test_chat_acknowledgement_p2p_LDADD = \
+ $(top_builddir)/src/chat/libgnunetchat.la \
+ $(top_builddir)/src/util/libgnunetutil.la
+
+test_chat_anonymous_p2p_SOURCES = \
+ test_chat.c
+test_chat_anonymous_p2p_LDADD = \
+ $(top_builddir)/src/chat/libgnunetchat.la \
+ $(top_builddir)/src/util/libgnunetutil.la
+
+test_chat_authentication_p2p_SOURCES = \
+ test_chat.c
+test_chat_authentication_p2p_LDADD = \
+ $(top_builddir)/src/chat/libgnunetchat.la \
+ $(top_builddir)/src/util/libgnunetutil.la
+
+test_chat_private_SOURCES = \
+ test_chat_private.c
+test_chat_private_LDADD = \
+ $(top_builddir)/src/chat/libgnunetchat.la \
+ $(top_builddir)/src/util/libgnunetutil.la
+
+test_chat_private_p2p_SOURCES = \
+ test_chat_private.c
+test_chat_private_p2p_LDADD = \
+ $(top_builddir)/src/chat/libgnunetchat.la \
+ $(top_builddir)/src/util/libgnunetutil.la
+
+EXTRA_DIST = \
+ test_chat_data.conf \
+ test_chat_peer1.conf \
+ test_chat_peer2.conf \
+ test_chat_peer3.conf
#include "gnunet_signatures.h"
#include "chat.h"
-#define DEBUG_CHAT GNUNET_YES
+#define DEBUG_CHAT GNUNET_NO
#define NICK_IDENTITY_PREFIX ".chat_identity_"
/**
- * Handle for a (joined) chat room.
+ * Handle for a chat room.
*/
struct GNUNET_CHAT_Room
{
struct MemberList *members;
+ int is_joined;
+
+ GNUNET_CHAT_JoinCallback join_callback;
+
+ void *join_callback_cls;
+
GNUNET_CHAT_MessageCallback message_callback;
void *message_callback_cls;
struct JoinNotificationMessage *join_msg;
struct ReceiveNotificationMessage *received_msg;
struct ConfirmationReceiptMessage *receipt;
+ struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pkey;
GNUNET_HashCode id;
+ const GNUNET_HashCode *sender;
struct GNUNET_CONTAINER_MetaData *meta;
struct GNUNET_CHAT_SendReceiptContext *src;
struct MemberList *pos;
sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
&pos->id);
GNUNET_PSEUDONYM_add (room->cfg, &pos->id, meta);
+ pos->next = room->members;
+ room->members = pos;
+ if (GNUNET_NO == room->is_joined)
+ {
+ GNUNET_CRYPTO_rsa_key_get_public (room->my_private_key, &pkey);
+ if (0 == memcmp (&join_msg->public_key,
+ &pkey,
+ sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded)))
+ {
+ room->join_callback (room->join_callback_cls);
+ room->is_joined = GNUNET_YES;
+ }
+ else
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ _("The current user must be the the first one joined\n"));
+ GNUNET_break (0);
+ return;
+ }
+ }
+ else
room->member_list_callback (room->member_list_callback_cls,
meta, &join_msg->public_key,
ntohl (join_msg->msg_options));
- pos->next = room->members;
- room->members = pos;
break;
case GNUNET_MESSAGE_TYPE_CHAT_LEAVE_NOTIFICATION:
#if DEBUG_CHAT
memcpy (message_content, &received_msg[1], msg_len);
}
message_content[msg_len] = '\0';
+ if (0 != (ntohl (received_msg->msg_options) & GNUNET_CHAT_MSG_ANONYMOUS))
+ {
+ sender = NULL;
+ meta = NULL;
+ }
+ else
+ {
pos = room->members;
while ((NULL != pos) &&
(0 != memcmp (&pos->id,
sizeof (GNUNET_HashCode))))
pos = pos->next;
GNUNET_assert (NULL != pos);
+ sender = &received_msg->sender;
+ meta = pos->meta;
+ }
room->message_callback (room->message_callback_cls,
room,
- &received_msg->sender,
- pos->meta,
+ sender,
+ meta,
message_content,
+ GNUNET_TIME_absolute_ntoh (received_msg->timestamp),
ntohl (received_msg->msg_options));
if (message_content != decrypted_msg)
GNUNET_free (message_content);
room,
ntohl (receipt->sequence_number),
GNUNET_TIME_absolute_ntoh (receipt->timestamp),
- &receipt->target,
- &receipt->content,
- &receipt->signature);
+ &receipt->target);
break;
default:
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
{
#if DEBUG_CHAT
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Could not transmit join request\n");
+ "Could not transmit join request, retrying...\n");
#endif
+ GNUNET_CHAT_rejoin_room (chat_room);
return 0;
}
#if DEBUG_CHAT
_("Could not serialize metadata\n"));
return 0;
}
+ GNUNET_CLIENT_receive (chat_room->client,
+ &receive_results,
+ chat_room,
+ GNUNET_TIME_UNIT_FOREVER_REL);
return size_of_join;
}
struct GNUNET_CONTAINER_MetaData *member_info,
const char *room_name,
enum GNUNET_CHAT_MsgOptions msg_options,
+ GNUNET_CHAT_JoinCallback joinCallback,
+ void *join_cls,
GNUNET_CHAT_MessageCallback messageCallback,
void *message_cls,
GNUNET_CHAT_MemberListCallback memberCallback,
_("Failed to connect to the chat service\n"));
return NULL;
}
+ if (NULL == joinCallback)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ _("Undefined mandatory parameter: joinCallback\n"));
+ return NULL;
+ }
+ if (NULL == messageCallback)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ _("Undefined mandatory parameter: messageCallback\n"));
+ return NULL;
+ }
+ if (NULL == memberCallback)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ _("Undefined mandatory parameter: memberCallback\n"));
+ return NULL;
+ }
chat_room = GNUNET_malloc (sizeof (struct GNUNET_CHAT_Room));
chat_room->msg_options = msg_options;
chat_room->room_name = GNUNET_strdup (room_name);
chat_room->member_info = GNUNET_CONTAINER_meta_data_duplicate (member_info);
chat_room->my_private_key = priv_key;
+ chat_room->is_joined = GNUNET_NO;
+ chat_room->join_callback = joinCallback;
+ chat_room->join_callback_cls = join_cls;
chat_room->message_callback = messageCallback;
chat_room->message_callback_cls = message_cls;
chat_room->member_list_callback = memberCallback;
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);
msg_to_send->header.type = htons (GNUNET_MESSAGE_TYPE_CHAT_TRANSMIT_REQUEST);
msg_to_send->msg_options = htonl (smc->options);
msg_to_send->sequence_number = htonl (smc->sequence_number);
+ msg_to_send->timestamp =
+ GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_get ());
msg_to_send->reserved = htonl (0);
if (NULL == smc->receiver)
memset (&msg_to_send->target, 0, sizeof (GNUNET_HashCode));
#if DEBUG_CHAT
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending a message\n");
#endif
- *sequence_number = ++room->sequence_number;
+ room->sequence_number++;
+ if (NULL != sequence_number)
+ *sequence_number = room->sequence_number;
smc = GNUNET_malloc (sizeof (struct GNUNET_CHAT_SendMessageContext));
smc->chat_room = room;
smc->message = GNUNET_strdup (message);
smc->options = options;
smc->receiver = receiver;
- smc->sequence_number = *sequence_number;
+ smc->sequence_number = room->sequence_number;
msg_size = strlen (message) + sizeof (struct TransmitRequestMessage);
GNUNET_CLIENT_notify_transmit_ready (room->client,
msg_size,
*/
uint32_t reserved GNUNET_PACKED;
+ /**
+ * Timestamp of the message.
+ */
+ struct GNUNET_TIME_AbsoluteNBO timestamp;
+
/**
* Hash of the public key of the pseudonym of the sender of the message.
- * TBD: Should be all zeros for anonymous.
+ * Should be all zeros for anonymous.
*/
GNUNET_HashCode sender;
*/
uint32_t sequence_number GNUNET_PACKED;
+ /**
+ * Timestamp of the message.
+ */
+ struct GNUNET_TIME_AbsoluteNBO timestamp;
+
/**
* Who should receive this message? Set to all zeros for "everyone".
*/
/**
* Receipt sent from a message receiver to the service to confirm delivery of
- * a chat message.
+ * a chat message and from the service to sender of the original message to
+ * acknowledge delivery.
*/
struct ConfirmationReceiptMessage
{
/**
- * Message type will be GNUNET_MESSAGE_TYPE_CHAT_CONFIRMATION_RECEIPT
+ * Message type will be
+ * GNUNET_MESSAGE_TYPE_CHAT_CONFIRMATION_RECEIPT when sending from client,
+ * GNUNET_MESSAGE_TYPE_CHAT_CONFIRMATION_NOTIFICATION when sending to client.
*/
struct GNUNET_MessageHeader header;
/**
* Message send by one peer to another to indicate receiving of a chat message.
- * After this struct, the remaining bytes are the actual text message. If the
- * mesasge is private, then the text is encrypted, otherwise it's plaintext.
+ * This struct is followed by the room name (only if the message is anonymous)
+ * and then the remaining bytes are the actual text message. If the mesasge is
+ * private, then the text is encrypted, otherwise it's plaintext.
*/
struct P2PReceiveNotificationMessage
{
*/
uint32_t sequence_number GNUNET_PACKED;
+ /**
+ * Length of the room name. This is only used for anonymous messages.
+ */
+ uint16_t room_name_len GNUNET_PACKED;
+
/**
* Reserved (for alignment).
*/
- uint32_t reserved GNUNET_PACKED;
+ uint16_t reserved GNUNET_PACKED;
+
+ /**
+ * Timestamp of the message.
+ */
+ struct GNUNET_TIME_AbsoluteNBO timestamp;
/**
* Hash of the public key of the pseudonym of the sender of the message
- * TBD: Should be all zeros for anonymous.
+ * Should be all zeros for anonymous.
*/
GNUNET_HashCode sender;
static int do_help (const char *args, const void *xtra);
+/**
+ * Callback used for notification that we have joined the room.
+ *
+ * @param cls closure
+ * @return GNUNET_OK
+ */
+static int
+join_cb (void *cls)
+{
+ fprintf (stdout, _("Joined\n"));
+ return GNUNET_OK;
+}
+
+
/**
* Callback used for notification about incoming messages.
*
receive_cb (void *cls,
struct GNUNET_CHAT_Room *room,
const GNUNET_HashCode *sender,
- const struct GNUNET_CONTAINER_MetaData *meta,
+ const struct GNUNET_CONTAINER_MetaData *member_info,
const char *message,
+ struct GNUNET_TIME_Absolute timestamp,
enum GNUNET_CHAT_MsgOptions options)
{
char *nick;
+ char *time;
const char *fmt;
if (NULL != sender)
{
case GNUNET_CHAT_MSG_OPTION_NONE:
case GNUNET_CHAT_MSG_ANONYMOUS:
- fmt = _("`%s' said: %s\n");
+ fmt = _("(%s) `%s' said: %s\n");
break;
case GNUNET_CHAT_MSG_PRIVATE:
- fmt = _("`%s' said to you: %s\n");
+ fmt = _("(%s) `%s' said to you: %s\n");
break;
case GNUNET_CHAT_MSG_PRIVATE | GNUNET_CHAT_MSG_ANONYMOUS:
- fmt = _("`%s' said to you: %s\n");
+ fmt = _("(%s) `%s' said to you: %s\n");
break;
case GNUNET_CHAT_MSG_AUTHENTICATED:
- fmt = _("`%s' said for sure: %s\n");
+ fmt = _("(%s) `%s' said for sure: %s\n");
break;
case GNUNET_CHAT_MSG_PRIVATE | GNUNET_CHAT_MSG_AUTHENTICATED:
- fmt = _("`%s' said to you for sure: %s\n");
+ fmt = _("(%s) `%s' said to you for sure: %s\n");
break;
case GNUNET_CHAT_MSG_ACKNOWLEDGED:
- fmt = _("`%s' was confirmed that you received: %s\n");
+ fmt = _("(%s) `%s' was confirmed that you received: %s\n");
break;
case GNUNET_CHAT_MSG_PRIVATE | GNUNET_CHAT_MSG_ACKNOWLEDGED:
- fmt = _("`%s' was confirmed that you and only you received: %s\n");
+ fmt = _("(%s) `%s' was confirmed that you and only you received: %s\n");
break;
case GNUNET_CHAT_MSG_AUTHENTICATED | GNUNET_CHAT_MSG_ACKNOWLEDGED:
- fmt = _("`%s' was confirmed that you received from him or her: %s\n");
+ fmt = _("(%s) `%s' was confirmed that you received from him or her: %s\n");
break;
case GNUNET_CHAT_MSG_AUTHENTICATED | GNUNET_CHAT_MSG_PRIVATE | GNUNET_CHAT_MSG_ACKNOWLEDGED:
- fmt =
- _
- ("`%s' was confirmed that you and only you received from him or her: %s\n");
+ fmt = _("(%s) `%s' was confirmed that you and only you received from him or her: %s\n");
break;
case GNUNET_CHAT_MSG_OFF_THE_RECORD:
- fmt = _("`%s' said off the record: %s\n");
+ fmt = _("(%s) `%s' said off the record: %s\n");
break;
default:
- fmt = _("<%s> said using an unknown message type: %s\n");
+ fmt = _("(%s) <%s> said using an unknown message type: %s\n");
break;
}
- fprintf (stdout, fmt, nick, message);
+ time = GNUNET_STRINGS_absolute_time_to_string (timestamp);
+ fprintf (stdout, fmt, time, nick, message);
GNUNET_free (nick);
+ GNUNET_free (time);
return GNUNET_OK;
}
struct GNUNET_CHAT_Room *room,
uint32_t orig_seq_number,
struct GNUNET_TIME_Absolute timestamp,
- const GNUNET_HashCode *receiver,
- const GNUNET_HashCode *msg_hash,
- const struct GNUNET_CRYPTO_RsaSignature *receipt)
+ const GNUNET_HashCode *receiver)
{
char *nick;
}
-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)
{
meta,
room_name,
-1,
+ &join_cb, NULL,
&receive_cb, NULL,
&member_list_cb, NULL,
&confirmation_cb, NULL, &me);
return GNUNET_SYSERR;
}
my_name = GNUNET_PSEUDONYM_id_to_name (cfg, &me);
- fprintf (stdout, _("Joined room `%s' as user `%s'\n"), room_name, my_name);
+ fprintf (stdout, _("Joining room `%s' as user `%s'...\n"), room_name, my_name);
GNUNET_free (my_name);
return GNUNET_OK;
}
meta,
room_name,
-1,
+ &join_cb, NULL,
&receive_cb, NULL,
&member_list_cb, NULL,
&confirmation_cb, NULL, &me);
static int
-do_pm (const char *msg, const void *xtra)
+do_send (const char *msg, const void *xtra)
+{
+ uint32_t seq;
+ GNUNET_CHAT_send_message (room,
+ msg,
+ GNUNET_CHAT_MSG_OPTION_NONE,
+ NULL, &seq);
+ return GNUNET_OK;
+}
+
+
+static int
+do_send_pm (const char *msg, const void *xtra)
{
char *user;
GNUNET_HashCode uid;
static int
-do_transmit_sig (const char *msg, const void *xtra)
+do_send_sig (const char *msg, const void *xtra)
{
uint32_t seq;
GNUNET_CHAT_send_message (room,
static int
-do_transmit_ack (const char *msg, const void *xtra)
+do_send_ack (const char *msg, const void *xtra)
{
uint32_t seq;
GNUNET_CHAT_send_message (room,
}
+static int
+do_send_anonymous (const char *msg, const void *xtra)
+{
+ uint32_t seq;
+ GNUNET_CHAT_send_message (room,
+ msg,
+ GNUNET_CHAT_MSG_ANONYMOUS,
+ NULL, &seq);
+ return GNUNET_OK;
+}
+
+
static int
do_quit (const char *args, const void *xtra)
{
gettext_noop
("Use `/nick nickname' to change your nickname. This will cause you to"
" leave the current room and immediately rejoin it with the new name.")},
- {"/msg ", &do_pm,
+ {"/msg ", &do_send_pm,
gettext_noop
("Use `/msg nickname message' to send a private message to the specified"
" user")},
- {"/notice ", &do_pm,
+ {"/notice ", &do_send_pm,
gettext_noop ("The `/notice' command is an alias for `/msg'")},
- {"/query ", &do_pm,
+ {"/query ", &do_send_pm,
gettext_noop ("The `/query' command is an alias for `/msg'")},
- {"/sig ", &do_transmit_sig,
+ {"/sig ", &do_send_sig,
gettext_noop ("Use `/sig message' to send a signed public message")},
- {"/ack ", &do_transmit_ack,
+ {"/ack ", &do_send_ack,
gettext_noop
("Use `/ack message' to require signed acknowledgment of the message")},
+ {"/anonymous ", &do_send_anonymous,
+ gettext_noop
+ ("Use `/anonymous message' to send a public anonymous message")},
+ {"/anon ", &do_send_anonymous,
+ gettext_noop ("The `/anon' command is an alias for `/anonymous'")},
{"/quit", &do_quit,
gettext_noop ("Use `/quit' to terminate gnunet-chat")},
{"/leave", &do_quit,
/* Add standard commands:
/whois (print metadata),
/ignore (set flag, check on receive!) */
- /* Add special commands (currently supported):
- + anonymous msgs
- + authenticated msgs
- */
/* the following three commands must be last! */
{"/", &do_unknown, NULL},
- {"", &do_transmit, NULL},
+ {"", &do_send, NULL},
{NULL, NULL, NULL},
};
meta,
room_name,
-1,
+ &join_cb, NULL,
&receive_cb, NULL,
&member_list_cb, NULL,
&confirmation_cb, NULL, &me);
return;
}
my_name = GNUNET_PSEUDONYM_id_to_name (cfg, &me);
- fprintf (stdout, _("Joined room `%s' as user `%s'\n"), room_name, my_name);
+ fprintf (stdout, _("Joining room `%s' as user `%s'...\n"), room_name, my_name);
GNUNET_free (my_name);
handle_cmd_task =
GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_UI,
#include "gnunet_signatures.h"
#include "chat.h"
-#define DEBUG_CHAT_SERVICE GNUNET_YES
+#define DEBUG_CHAT_SERVICE GNUNET_NO
#define MAX_TRANSMIT_DELAY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 60)
#define QUEUE_SIZE 16
+#define MAX_ANONYMOUS_MSG_LIST_LENGTH 16
/**
};
+/**
+ * Linked list of recent anonymous messages.
+ */
+struct AnonymousMessage
+{
+ struct AnonymousMessage *next;
+
+ /**
+ * Hash of the message.
+ */
+ GNUNET_HashCode hash;
+
+};
+
/**
* Handle to the core service (NULL until we've connected to it).
*/
struct GNUNET_SERVER_NotificationContext *nc = NULL;
+/**
+ * Head of the list of recent anonymous messages.
+ */
+static struct AnonymousMessage *anonymous_list_head = NULL;
+
+
+static void
+remember_anonymous_message (const struct P2PReceiveNotificationMessage *p2p_rnmsg)
+{
+ static GNUNET_HashCode hash;
+ struct AnonymousMessage *anon_msg;
+ struct AnonymousMessage *prev;
+ int anon_list_len;
+
+ GNUNET_CRYPTO_hash (p2p_rnmsg, ntohs (p2p_rnmsg->header.size), &hash);
+ anon_msg = GNUNET_malloc (sizeof (struct AnonymousMessage));
+ anon_msg->hash = hash;
+ anon_msg->next = anonymous_list_head;
+ anonymous_list_head = anon_msg;
+ anon_list_len = 1;
+ prev = NULL;
+ while ((NULL != anon_msg->next))
+ {
+ prev = anon_msg;
+ anon_msg = anon_msg->next;
+ anon_list_len++;
+ }
+ if (anon_list_len == MAX_ANONYMOUS_MSG_LIST_LENGTH)
+ {
+ GNUNET_free (anon_msg);
+ if (NULL != prev)
+ prev->next = NULL;
+ }
+}
+
+
+static int
+lookup_anonymous_message (const struct P2PReceiveNotificationMessage *p2p_rnmsg)
+{
+ static GNUNET_HashCode hash;
+ struct AnonymousMessage *anon_msg;
+
+ GNUNET_CRYPTO_hash (p2p_rnmsg, ntohs (p2p_rnmsg->header.size), &hash);
+ anon_msg = anonymous_list_head;
+ while ((NULL != anon_msg) &&
+ (0 != memcmp (&anon_msg->hash, &hash, sizeof (GNUNET_HashCode))))
+ anon_msg = anon_msg->next;
+ return (NULL != anon_msg);
+}
+
/**
* Transmit a message notification to the peer.
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Transmitting P2P message notification\n");
#endif
+ if (buf == NULL)
+ {
+ /* client disconnected */
+#if DEBUG_CHAT_SERVICE
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Buffer is NULL, dropping the message\n");
+#endif
+ return 0;
+ }
msg_size = ntohs (my_msg->header.size);
GNUNET_assert (size >= msg_size);
- GNUNET_assert (NULL != buf);
memcpy (m, my_msg, msg_size);
GNUNET_free (my_msg);
return msg_size;
struct GNUNET_CRYPTO_AesSessionKey key;
char encrypted_msg[MAX_MESSAGE_LENGTH];
const char *room;
+ size_t room_len;
int msg_len;
- int priv_msg;
+ int is_priv;
+ int is_anon;
GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Client sent a chat message\n");
if (ntohs (message->size) <= sizeof (struct TransmitRequestMessage))
}
trmsg = (const struct TransmitRequestMessage *) message;
msg_len = ntohs (trmsg->header.size) - sizeof (struct TransmitRequestMessage);
- priv_msg = (ntohl (trmsg->msg_options) & GNUNET_CHAT_MSG_PRIVATE) != 0;
- if (priv_msg)
+ is_priv = (0 != (ntohl (trmsg->msg_options) & GNUNET_CHAT_MSG_PRIVATE));
+ if (is_priv)
{
#if DEBUG_CHAT_SERVICE
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Encrypting the message text\n");
msg_len);
rnmsg->header.type = htons (GNUNET_MESSAGE_TYPE_CHAT_MESSAGE_NOTIFICATION);
rnmsg->msg_options = trmsg->msg_options;
- rnmsg->sequence_number = trmsg->sequence_number;
+ rnmsg->timestamp = trmsg->timestamp;
pos = client_list_head;
while ((NULL != pos) && (pos->client != client))
pos = pos->next;
}
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
+ is_anon = (0 != (ntohl (trmsg->msg_options) & GNUNET_CHAT_MSG_ANONYMOUS));
+ if (is_anon)
+ {
memset (&rnmsg->sender, 0, sizeof (GNUNET_HashCode));
- if (priv_msg)
+ rnmsg->sequence_number = 0;
+ }
+ else
+ {
+ rnmsg->sender = pos->id;
+ rnmsg->sequence_number = trmsg->sequence_number;
+ }
+ if (is_priv)
{
#if DEBUG_CHAT_SERVICE
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
(NULL != pos->client) &&
(pos->client != client))
{
- if (((!priv_msg) ||
+ if (((!is_priv) ||
(0 == memcmp (&trmsg->target,
&pos->id,
sizeof (GNUNET_HashCode)))) &&
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Broadcasting message to neighbour peers\n");
#endif
+ if (is_anon)
+ {
+ room_len = strlen (room);
+ p2p_rnmsg = GNUNET_malloc (sizeof (struct P2PReceiveNotificationMessage) +
+ msg_len + room_len);
+ p2p_rnmsg->header.size =
+ htons (sizeof (struct P2PReceiveNotificationMessage) + msg_len +
+ room_len);
+ p2p_rnmsg->room_name_len = htons (room_len);
+ memcpy ((char *) &p2p_rnmsg[1], room, room_len);
+ memcpy ((char *) &p2p_rnmsg[1] + room_len, &trmsg[1], msg_len);
+ }
+ else
+ {
p2p_rnmsg = GNUNET_malloc (sizeof (struct P2PReceiveNotificationMessage) +
msg_len);
- p2p_rnmsg->header.size = htons (sizeof (struct P2PReceiveNotificationMessage) +
- msg_len);
- p2p_rnmsg->header.type = htons (GNUNET_MESSAGE_TYPE_CHAT_P2P_MESSAGE_NOTIFICATION);
- p2p_rnmsg->msg_options = trmsg->msg_options;
- p2p_rnmsg->sequence_number = trmsg->sequence_number;
- memcpy (&p2p_rnmsg->sender, &rnmsg->sender, sizeof (GNUNET_HashCode));
- p2p_rnmsg->target = trmsg->target;
- if (priv_msg)
+ p2p_rnmsg->header.size =
+ htons (sizeof (struct P2PReceiveNotificationMessage) + msg_len);
+ if (is_priv)
{
memcpy (&p2p_rnmsg[1], encrypted_msg, msg_len);
memcpy (&p2p_rnmsg->encrypted_key,
sizeof (struct GNUNET_CRYPTO_RsaEncryptedData));
}
else
- {
memcpy (&p2p_rnmsg[1], &trmsg[1], msg_len);
}
+ p2p_rnmsg->header.type = htons (GNUNET_MESSAGE_TYPE_CHAT_P2P_MESSAGE_NOTIFICATION);
+ p2p_rnmsg->msg_options = trmsg->msg_options;
+ p2p_rnmsg->sequence_number = trmsg->sequence_number;
+ p2p_rnmsg->timestamp = trmsg->timestamp;
+ p2p_rnmsg->reserved = 0;
+ p2p_rnmsg->sender = rnmsg->sender;
+ p2p_rnmsg->target = trmsg->target;
+ if (is_anon)
+ remember_anonymous_message (p2p_rnmsg);
GNUNET_CORE_iterate_peers (cfg,
&send_message_noficiation,
p2p_rnmsg);
"Transmitting P2P confirmation receipt to '%s'\n",
GNUNET_h2s (&receipt->target));
#endif
+ if (buf == NULL)
+ {
+ /* client disconnected */
+#if DEBUG_CHAT_SERVICE
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Buffer is NULL, dropping the message\n");
+#endif
+ return 0;
+ }
msg_size = sizeof (struct P2PConfirmationReceiptMessage);
GNUNET_assert (size >= msg_size);
- GNUNET_assert (NULL != buf);
memcpy (buf, receipt, msg_size);
GNUNET_free (receipt);
return msg_size;
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Transmitting P2P leave notification\n");
#endif
+ if (buf == NULL)
+ {
+ /* client disconnected */
+#if DEBUG_CHAT_SERVICE
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Buffer is NULL, dropping the message\n");
+#endif
+ return 0;
+ }
msg_size = sizeof (struct P2PLeaveNotificationMessage);
GNUNET_assert (size >= msg_size);
- GNUNET_assert (NULL != buf);
m = buf;
m->header.type = htons (GNUNET_MESSAGE_TYPE_CHAT_P2P_LEAVE_NOTIFICATION);
m->header.size = htons (msg_size);
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;
msg_size = sizeof (struct P2PLeaveNotificationMessage);
public_key = GNUNET_memdup (&entry->public_key,
sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded));
- th = GNUNET_CORE_notify_transmit_ready (core,
+ if (NULL == GNUNET_CORE_notify_transmit_ready (core,
1,
MAX_TRANSMIT_DELAY,
peer,
msg_size,
&transmit_leave_notification_to_peer,
- public_key);
- GNUNET_assert (NULL != th);
+ public_key))
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ _("Failed to queue a leave notification\n"));
}
}
struct ChatClient *sender;
struct ChatClient *pos;
static GNUNET_HashCode all_zeros;
- int priv_msg;
+ int is_priv;
+ int is_anon;
uint16_t msg_len;
+ uint16_t room_name_len;
+ char *room_name = NULL;
+ char *text;
GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Got P2P message notification\n");
if (ntohs (message->size) <= sizeof (struct P2PReceiveNotificationMessage))
return GNUNET_SYSERR;
}
p2p_rnmsg = (const struct P2PReceiveNotificationMessage *) message;
+ msg_len = ntohs (p2p_rnmsg->header.size) -
+ sizeof (struct P2PReceiveNotificationMessage);
+
+ is_anon = (0 != (ntohl (p2p_rnmsg->msg_options) & GNUNET_CHAT_MSG_ANONYMOUS));
+ if (is_anon)
+ {
+ room_name_len = ntohs (p2p_rnmsg->room_name_len);
+ if (msg_len <= room_name_len)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Malformed message: wrong length of the room name\n");
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ msg_len -= room_name_len;
+ if (lookup_anonymous_message (p2p_rnmsg))
+ {
+#if DEBUG_CHAT_SERVICE
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "This anonymous message has already been handled.");
+#endif
+ return GNUNET_OK;
+ }
+ remember_anonymous_message (p2p_rnmsg);
+ room_name = GNUNET_malloc (room_name_len + 1);
+ memcpy (room_name, (char *) &p2p_rnmsg[1], room_name_len);
+ room_name[room_name_len] = '\0';
+ text = (char *) &p2p_rnmsg[1] + room_name_len;
+ }
+ else
+ {
sender = client_list_head;
while ((NULL != sender) &&
(0 != memcmp (&sender->id,
sender = sender->next;
if (NULL == sender)
{
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ /* not an error since the sender may have left before we got the
+ message */
+#if DEBUG_CHAT_SERVICE
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Unknown source. Rejecting the message\n");
- GNUNET_break_op (0);
- return GNUNET_SYSERR;
+#endif
+ return GNUNET_OK;
}
if (sender->msg_sequence_number >= ntohl (p2p_rnmsg->sequence_number))
{
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"This message has already been handled."
" Sequence numbers (msg/sender): %u/%u\n",
- ntohl (p2p_rnmsg->sequence_number), sender->msg_sequence_number);
+ ntohl (p2p_rnmsg->sequence_number),
+ sender->msg_sequence_number);
#endif
return GNUNET_OK;
}
sender->msg_sequence_number = ntohl (p2p_rnmsg->sequence_number);
- msg_len = ntohs (p2p_rnmsg->header.size) -
- sizeof (struct P2PReceiveNotificationMessage);
+ room_name = sender->room;
+ text = (char *) &p2p_rnmsg[1];
+ }
+
#if DEBUG_CHAT_SERVICE
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending message to local room members\n");
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Sending message to local room members\n");
#endif
rnmsg = GNUNET_malloc (sizeof (struct ReceiveNotificationMessage) + msg_len);
rnmsg->header.size = htons (sizeof (struct ReceiveNotificationMessage) +
rnmsg->header.type = htons (GNUNET_MESSAGE_TYPE_CHAT_MESSAGE_NOTIFICATION);
rnmsg->msg_options = p2p_rnmsg->msg_options;
rnmsg->sequence_number = p2p_rnmsg->sequence_number;
- priv_msg = (0 != memcmp (&all_zeros,
+ rnmsg->timestamp = p2p_rnmsg->timestamp;
+ is_priv = (0 != memcmp (&all_zeros,
&p2p_rnmsg->target, sizeof (GNUNET_HashCode)));
- if (priv_msg)
+ if (is_priv)
memcpy (&rnmsg->encrypted_key,
&p2p_rnmsg->encrypted_key,
sizeof (struct GNUNET_CRYPTO_RsaEncryptedData));
- memcpy (&rnmsg->sender, &p2p_rnmsg->sender, sizeof (GNUNET_HashCode));
- memcpy (&rnmsg[1], &p2p_rnmsg[1], msg_len);
+ rnmsg->sender = p2p_rnmsg->sender;
+ memcpy (&rnmsg[1], text, msg_len);
pos = client_list_head;
while (NULL != pos)
{
- if ((0 == strcmp (sender->room, pos->room)) &&
+ if ((0 == strcmp (room_name, pos->room)) &&
(NULL != pos->client))
{
- if (((!priv_msg) ||
+ if (((!is_priv) ||
(0 == memcmp (&p2p_rnmsg->target,
&pos->id,
sizeof (GNUNET_HashCode)))) &&
}
pos = pos->next;
}
+ if (is_anon)
+ GNUNET_free (room_name);
#if DEBUG_CHAT_SERVICE
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Broadcasting message notification to neighbour peers\n");
cleanup_task (void *cls,
const struct GNUNET_SCHEDULER_TaskContext *tc)
{
+ struct AnonymousMessage *next_msg;
+ struct ChatClient *next_client;
+
GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Cleaning up\n");
if (NULL != core)
{
GNUNET_SERVER_notification_context_destroy (nc);
nc = NULL;
}
+ while (NULL != client_list_head)
+ {
+ next_client = client_list_head->next;
+ GNUNET_free (client_list_head->room);
+ GNUNET_free_non_null (client_list_head->member_info);
+ GNUNET_free (client_list_head);
+ client_list_head = next_client;
+ }
+ while (NULL != anonymous_list_head)
+ {
+ next_msg = anonymous_list_head->next;
+ GNUNET_free (anonymous_list_head);
+ anonymous_list_head = next_msg;
+ }
}
/**
* General core debugging.
*/
-#define DEBUG_CORE GNUNET_NO
+#define DEBUG_CORE GNUNET_YES
/**
* Debugging interaction core-clients.
*/
struct GNUNET_CHAT_Room;
+/**
+ * Callback used for notification that we have joined the room.
+ *
+ * @param cls closure
+ * @return GNUNET_OK
+ */
+typedef int (*GNUNET_CHAT_JoinCallback) (void *cls);
+
/**
* Callback used for notification about incoming messages.
*
* @param sender what is the ID of the sender? (maybe NULL)
* @param member_info information about the joining member
* @param message the message text
+ * @param timestamp when was the message sent?
* @param options options for the message
* @return GNUNET_OK to accept the message now, GNUNET_NO to
* accept (but user is away), GNUNET_SYSERR to signal denied delivery
const GNUNET_HashCode *sender,
const struct GNUNET_CONTAINER_MetaData *member_info,
const char *message,
+ struct GNUNET_TIME_Absolute timestamp,
enum GNUNET_CHAT_MsgOptions options);
/**
* Callback used for notification that another room member has joined or left.
*
+ * @param cls closure
* @param member_info will be non-null if the member is joining, NULL if he is
* leaving
* @param member_id hash of public key of the user (for unique identification)
* @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
*/
struct GNUNET_CHAT_Room *room,
uint32_t orig_seq_number,
struct GNUNET_TIME_Absolute timestamp,
- const GNUNET_HashCode *receiver,
- const GNUNET_HashCode *msg_hash,
- const struct GNUNET_CRYPTO_RsaSignature *receipt);
+ const GNUNET_HashCode *receiver);
/**
* Join a chat room.
* @param member_info information about the joining member
* @param room_name name of the room
* @param msg_options message options of the joining user
+ * @param joinCallback which function to call when we've joined the room
+ * @param join_cls argument to callback
* @param messageCallback which function to call if a message has
* been received?
* @param message_cls argument to callback
struct GNUNET_CONTAINER_MetaData *member_info,
const char *room_name,
enum GNUNET_CHAT_MsgOptions msg_options,
+ GNUNET_CHAT_JoinCallback joinCallback,
+ void *join_cls,
GNUNET_CHAT_MessageCallback messageCallback,
void *message_cls,
GNUNET_CHAT_MemberListCallback memberCallback,