2 This file is part of GNUnet.
3 (C) 2009, 2011 Christian Grothoff (and other contributing authors)
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
22 * @file chat/gnunet-service-chat.c
23 * @brief service providing chat functionality
24 * @author Christian Grothoff
25 * @author Vitaly Minko
29 #include "gnunet_core_service.h"
30 #include "gnunet_crypto_lib.h"
31 #include "gnunet_protocols.h"
32 #include "gnunet_service_lib.h"
33 #include "gnunet_signatures.h"
36 #define DEBUG_CHAT_SERVICE GNUNET_EXTRA_LOGGING
37 #define MAX_TRANSMIT_DELAY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 60)
38 #define EXPECTED_NEIGHBOUR_COUNT 16
39 #define MAX_ANONYMOUS_MSG_LIST_LENGTH 16
43 * Linked list of our current clients.
47 struct ChatClient *next;
50 * Handle for a chat client (NULL for external clients).
52 struct GNUNET_SERVER_Client *client;
55 * Public key of the client.
57 struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded public_key;
60 * Name of the room which the client is in.
65 * Serialized metadata of the client.
70 * Hash of the public key (for convenience).
75 * Options which the client is willing to receive.
80 * Length of serialized metadata in member_info.
85 * Sequence number of the last message sent by the client.
87 uint32_t msg_sequence_number;
90 * Sequence number of the last receipt sent by the client.
91 * Used to discard already processed receipts.
93 uint32_t rcpt_sequence_number;
98 * Information about a peer that we are connected to.
99 * We track data that is useful for determining which
100 * peers should receive our requests.
105 * The peer's identity.
111 * Linked list of recent anonymous messages.
113 struct AnonymousMessage
115 struct AnonymousMessage *next;
118 * Hash of the message.
120 GNUNET_HashCode hash;
126 * Handle to the core service (NULL until we've connected to it).
128 static struct GNUNET_CORE_Handle *core;
133 static const struct GNUNET_CONFIGURATION_Handle *cfg;
136 * The identity of this host.
138 static const struct GNUNET_PeerIdentity *me;
141 * Head of the list of current clients.
143 static struct ChatClient *client_list_head = NULL;
146 * Notification context containing all connected clients.
148 struct GNUNET_SERVER_NotificationContext *nc = NULL;
151 * Head of the list of recent anonymous messages.
153 static struct AnonymousMessage *anonymous_list_head = NULL;
156 * Map of peer identifiers to "struct ConnectedPeer" (for that peer).
158 static struct GNUNET_CONTAINER_MultiHashMap *connected_peers;
162 remember_anonymous_message (const struct P2PReceiveNotificationMessage
165 static GNUNET_HashCode hash;
166 struct AnonymousMessage *anon_msg;
167 struct AnonymousMessage *prev;
170 GNUNET_CRYPTO_hash (p2p_rnmsg, ntohs (p2p_rnmsg->header.size), &hash);
171 anon_msg = GNUNET_malloc (sizeof (struct AnonymousMessage));
172 anon_msg->hash = hash;
173 anon_msg->next = anonymous_list_head;
174 anonymous_list_head = anon_msg;
177 while ((NULL != anon_msg->next))
180 anon_msg = anon_msg->next;
183 if (anon_list_len == MAX_ANONYMOUS_MSG_LIST_LENGTH)
185 GNUNET_free (anon_msg);
193 lookup_anonymous_message (const struct P2PReceiveNotificationMessage *p2p_rnmsg)
195 static GNUNET_HashCode hash;
196 struct AnonymousMessage *anon_msg;
198 GNUNET_CRYPTO_hash (p2p_rnmsg, ntohs (p2p_rnmsg->header.size), &hash);
199 anon_msg = anonymous_list_head;
200 while ((NULL != anon_msg) &&
201 (0 != memcmp (&anon_msg->hash, &hash, sizeof (GNUNET_HashCode))))
202 anon_msg = anon_msg->next;
203 return (NULL != anon_msg);
208 * Transmit a message notification to the peer.
210 * @param cls closure, pointer to the 'struct P2PReceiveNotificationMessage'
211 * @param size number of bytes available in buf
212 * @param buf where the callee should write the message
213 * @return number of bytes written to buf
216 transmit_message_notification_to_peer (void *cls, size_t size, void *buf)
218 struct P2PReceiveNotificationMessage *my_msg = cls;
219 struct P2PReceiveNotificationMessage *m = buf;
222 #if DEBUG_CHAT_SERVICE
223 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
224 "Transmitting P2P message notification\n");
228 /* client disconnected */
229 #if DEBUG_CHAT_SERVICE
230 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
231 "Buffer is NULL, dropping the message\n");
235 msg_size = ntohs (my_msg->header.size);
236 GNUNET_assert (size >= msg_size);
237 memcpy (m, my_msg, msg_size);
238 GNUNET_free (my_msg);
244 * Ask to send a message notification to the peer.
247 send_message_noficiation (void *cls, const GNUNET_HashCode * key, void *value)
249 struct P2PReceiveNotificationMessage *msg = cls;
250 struct ConnectedPeer *cp = value;
251 struct GNUNET_PeerIdentity pid;
252 struct P2PReceiveNotificationMessage *my_msg;
254 GNUNET_PEER_resolve (cp->pid, &pid);
255 #if DEBUG_CHAT_SERVICE
256 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending message notification to `%s'\n",
259 my_msg = GNUNET_memdup (msg, ntohs (msg->header.size));
261 GNUNET_CORE_notify_transmit_ready (core, GNUNET_NO, 1, MAX_TRANSMIT_DELAY,
262 &pid, ntohs (msg->header.size),
263 &transmit_message_notification_to_peer,
265 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
266 _("Failed to queue a message notification\n"));
272 * A client sent a chat message. Encrypt the message text if the message is
273 * private. Send the message to local room members and to all connected peers.
275 * @param cls closure, NULL
276 * @param client identification of the client
277 * @param message the actual message
280 handle_transmit_request (void *cls, struct GNUNET_SERVER_Client *client,
281 const struct GNUNET_MessageHeader *message)
283 static GNUNET_HashCode all_zeros;
284 const struct TransmitRequestMessage *trmsg;
285 struct ReceiveNotificationMessage *rnmsg;
286 struct P2PReceiveNotificationMessage *p2p_rnmsg;
287 struct ChatClient *pos;
288 struct ChatClient *target;
289 struct GNUNET_CRYPTO_AesSessionKey key;
290 char encrypted_msg[MAX_MESSAGE_LENGTH];
297 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Client sent a chat message\n");
298 if (ntohs (message->size) <= sizeof (struct TransmitRequestMessage))
300 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Malformed message: wrong size\n");
302 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
305 trmsg = (const struct TransmitRequestMessage *) message;
306 msg_len = ntohs (trmsg->header.size) - sizeof (struct TransmitRequestMessage);
307 is_priv = (0 != (ntohl (trmsg->msg_options) & GNUNET_CHAT_MSG_PRIVATE));
310 #if DEBUG_CHAT_SERVICE
311 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Encrypting the message text\n");
313 GNUNET_CRYPTO_aes_create_session_key (&key);
315 GNUNET_CRYPTO_aes_encrypt (&trmsg[1], msg_len, &key,
317 GNUNET_CRYPTO_AesInitializationVector *)
318 INITVALUE, encrypted_msg);
321 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
322 "Could not encrypt the message text\n");
324 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
328 rnmsg = GNUNET_malloc (sizeof (struct ReceiveNotificationMessage) + msg_len);
330 htons (sizeof (struct ReceiveNotificationMessage) + msg_len);
331 rnmsg->header.type = htons (GNUNET_MESSAGE_TYPE_CHAT_MESSAGE_NOTIFICATION);
332 rnmsg->msg_options = trmsg->msg_options;
333 rnmsg->timestamp = trmsg->timestamp;
334 pos = client_list_head;
335 while ((NULL != pos) && (pos->client != client))
339 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
340 "The client is not a member of a chat room. Client has to "
341 "join a chat room first\n");
343 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
348 pos->msg_sequence_number = ntohl (trmsg->sequence_number);
349 is_anon = (0 != (ntohl (trmsg->msg_options) & GNUNET_CHAT_MSG_ANONYMOUS));
352 memset (&rnmsg->sender, 0, sizeof (GNUNET_HashCode));
353 rnmsg->sequence_number = 0;
357 rnmsg->sender = pos->id;
358 rnmsg->sequence_number = trmsg->sequence_number;
362 #if DEBUG_CHAT_SERVICE
363 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
364 "Encrypting the session key using the public key of '%s'\n",
365 GNUNET_h2s (&trmsg->target));
367 if (0 == memcmp (&all_zeros, &trmsg->target, sizeof (GNUNET_HashCode)))
369 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
370 "Malformed message: private, but no target\n");
372 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
376 memcpy (&rnmsg[1], encrypted_msg, msg_len);
377 target = client_list_head;
378 while ((NULL != target) &&
380 memcmp (&target->id, &trmsg->target, sizeof (GNUNET_HashCode))))
381 target = target->next;
384 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
385 "Unknown target of the private message\n");
387 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
392 GNUNET_CRYPTO_rsa_encrypt (&key,
393 sizeof (struct GNUNET_CRYPTO_AesSessionKey),
394 &target->public_key, &rnmsg->encrypted_key))
396 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
397 "Could not encrypt the session key\n");
399 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
406 memcpy (&rnmsg[1], &trmsg[1], msg_len);
408 pos = client_list_head;
409 #if DEBUG_CHAT_SERVICE
410 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
411 "Sending message to local room members\n");
415 if ((0 == strcmp (room, pos->room)) && (NULL != pos->client) &&
416 (pos->client != client))
419 (0 == memcmp (&trmsg->target, &pos->id, sizeof (GNUNET_HashCode))))
420 && (0 == (ntohl (trmsg->msg_options) & (~pos->msg_options))))
422 GNUNET_SERVER_notification_context_unicast (nc, pos->client,
423 &rnmsg->header, GNUNET_NO);
428 #if DEBUG_CHAT_SERVICE
429 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
430 "Broadcasting message to neighbour peers\n");
434 room_len = strlen (room);
436 GNUNET_malloc (sizeof (struct P2PReceiveNotificationMessage) + msg_len +
438 p2p_rnmsg->header.size =
439 htons (sizeof (struct P2PReceiveNotificationMessage) + msg_len +
441 p2p_rnmsg->room_name_len = htons (room_len);
442 memcpy ((char *) &p2p_rnmsg[1], room, room_len);
443 memcpy ((char *) &p2p_rnmsg[1] + room_len, &trmsg[1], msg_len);
448 GNUNET_malloc (sizeof (struct P2PReceiveNotificationMessage) + msg_len);
449 p2p_rnmsg->header.size =
450 htons (sizeof (struct P2PReceiveNotificationMessage) + msg_len);
453 memcpy (&p2p_rnmsg[1], encrypted_msg, msg_len);
454 memcpy (&p2p_rnmsg->encrypted_key, &rnmsg->encrypted_key,
455 sizeof (struct GNUNET_CRYPTO_RsaEncryptedData));
458 memcpy (&p2p_rnmsg[1], &trmsg[1], msg_len);
460 p2p_rnmsg->header.type =
461 htons (GNUNET_MESSAGE_TYPE_CHAT_P2P_MESSAGE_NOTIFICATION);
462 p2p_rnmsg->msg_options = trmsg->msg_options;
463 p2p_rnmsg->sequence_number = trmsg->sequence_number;
464 p2p_rnmsg->timestamp = trmsg->timestamp;
465 p2p_rnmsg->reserved = htons (0);
466 p2p_rnmsg->sender = rnmsg->sender;
467 p2p_rnmsg->target = trmsg->target;
469 remember_anonymous_message (p2p_rnmsg);
470 GNUNET_CONTAINER_multihashmap_iterate (connected_peers,
471 &send_message_noficiation, p2p_rnmsg);
472 GNUNET_free (p2p_rnmsg);
473 GNUNET_SERVER_receive_done (client, GNUNET_OK);
479 * Transmit a join notification to the peer.
481 * @param cls closure, pointer to the 'struct ChatClient'
482 * @param size number of bytes available in buf
483 * @param buf where the callee should write the message
484 * @return number of bytes written to buf
487 transmit_join_notification_to_peer (void *cls, size_t size, void *buf)
489 struct ChatClient *entry = cls;
490 struct P2PJoinNotificationMessage *m = buf;
496 #if DEBUG_CHAT_SERVICE
497 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Transmitting P2P join notification\n");
499 room_len = strlen (entry->room);
500 meta_len = entry->meta_len;
501 msg_size = sizeof (struct P2PJoinNotificationMessage) + meta_len + room_len;
502 GNUNET_assert (size >= msg_size);
503 GNUNET_assert (NULL != buf);
505 m->header.type = htons (GNUNET_MESSAGE_TYPE_CHAT_P2P_JOIN_NOTIFICATION);
506 m->header.size = htons (msg_size);
507 m->msg_options = htonl (entry->msg_options);
508 m->room_name_len = htons (room_len);
509 m->reserved = htons (0);
510 m->reserved2 = htonl (0);
511 m->public_key = entry->public_key;
512 roomptr = (char *) &m[1];
513 memcpy (roomptr, entry->room, room_len);
515 memcpy (&roomptr[room_len], entry->member_info, meta_len);
521 * Ask to send a join notification to the peer.
524 send_join_noficiation (void *cls, const GNUNET_HashCode * key, void *value)
526 struct ChatClient *entry = cls;
527 struct ConnectedPeer *cp = value;
528 struct GNUNET_PeerIdentity pid;
531 GNUNET_PEER_resolve (cp->pid, &pid);
532 #if DEBUG_CHAT_SERVICE
533 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending join notification to `%s'\n",
537 sizeof (struct P2PJoinNotificationMessage) + strlen (entry->room) +
540 GNUNET_CORE_notify_transmit_ready (core, GNUNET_NO, 1, MAX_TRANSMIT_DELAY,
542 &transmit_join_notification_to_peer,
544 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
545 _("Failed to queue a join notification\n"));
551 * A client asked for entering a chat room. Add the new member to the list of
552 * clients and notify remaining room members.
554 * @param cls closure, NULL
555 * @param client identification of the client
556 * @param message the actual message
559 handle_join_request (void *cls, struct GNUNET_SERVER_Client *client,
560 const struct GNUNET_MessageHeader *message)
562 const struct JoinRequestMessage *jrmsg;
565 uint16_t header_size;
567 uint16_t room_name_len;
568 struct ChatClient *new_entry;
569 struct ChatClient *entry;
570 struct JoinNotificationMessage *jnmsg;
571 struct JoinNotificationMessage *entry_jnmsg;
573 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Client sent a join request\n");
574 if (ntohs (message->size) <= sizeof (struct JoinRequestMessage))
576 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Malformed message: wrong size\n");
578 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
581 jrmsg = (const struct JoinRequestMessage *) message;
582 header_size = ntohs (jrmsg->header.size);
583 room_name_len = ntohs (jrmsg->room_name_len);
584 if (header_size - sizeof (struct JoinRequestMessage) <= room_name_len)
586 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
587 "Malformed message: wrong length of the room name\n");
589 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
592 meta_len = header_size - sizeof (struct JoinRequestMessage) - room_name_len;
593 roomptr = (const char *) &jrmsg[1];
594 room_name = GNUNET_malloc (room_name_len + 1);
595 memcpy (room_name, roomptr, room_name_len);
596 room_name[room_name_len] = '\0';
597 new_entry = GNUNET_malloc (sizeof (struct ChatClient));
598 memset (new_entry, 0, sizeof (struct ChatClient));
599 new_entry->client = client;
600 new_entry->room = room_name;
601 new_entry->public_key = jrmsg->public_key;
602 new_entry->meta_len = meta_len;
605 new_entry->member_info = GNUNET_malloc (meta_len);
606 memcpy (new_entry->member_info, &roomptr[room_name_len], meta_len);
609 new_entry->member_info = NULL;
610 GNUNET_CRYPTO_hash (&new_entry->public_key,
611 sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
613 new_entry->msg_options = ntohl (jrmsg->msg_options);
614 new_entry->next = client_list_head;
615 client_list_head = new_entry;
616 #if DEBUG_CHAT_SERVICE
617 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
618 "Synchronizing room members between local clients\n");
620 jnmsg = GNUNET_malloc (sizeof (struct JoinNotificationMessage) + meta_len);
621 jnmsg->header.type = htons (GNUNET_MESSAGE_TYPE_CHAT_JOIN_NOTIFICATION);
623 htons (sizeof (struct JoinNotificationMessage) + meta_len);
624 jnmsg->msg_options = jrmsg->msg_options;
625 jnmsg->public_key = new_entry->public_key;
626 memcpy (&jnmsg[1], &roomptr[room_name_len], meta_len);
627 GNUNET_SERVER_notification_context_add (nc, client);
628 entry = client_list_head;
629 while (NULL != entry)
631 if (0 == strcmp (room_name, entry->room))
633 if (NULL != entry->client)
634 GNUNET_SERVER_notification_context_unicast (nc, entry->client,
635 &jnmsg->header, GNUNET_NO);
636 if (entry->client != client)
639 GNUNET_malloc (sizeof (struct JoinNotificationMessage) +
641 entry_jnmsg->header.type =
642 htons (GNUNET_MESSAGE_TYPE_CHAT_JOIN_NOTIFICATION);
643 entry_jnmsg->header.size =
644 htons (sizeof (struct JoinNotificationMessage) + entry->meta_len);
645 entry_jnmsg->msg_options = entry->msg_options;
646 entry_jnmsg->public_key = entry->public_key;
647 memcpy (&entry_jnmsg[1], entry->member_info, entry->meta_len);
648 GNUNET_SERVER_notification_context_unicast (nc, client,
649 &entry_jnmsg->header,
651 GNUNET_free (entry_jnmsg);
656 #if DEBUG_CHAT_SERVICE
657 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
658 "Broadcasting join notification to neighbour peers\n");
660 GNUNET_CONTAINER_multihashmap_iterate (connected_peers,
661 &send_join_noficiation, new_entry);
662 GNUNET_SERVER_receive_done (client, GNUNET_OK);
667 * Transmit a confirmation receipt to the peer.
669 * @param cls closure, pointer to the 'struct P2PConfirmationReceiptMessage'
670 * @param size number of bytes available in buf
671 * @param buf where the callee should write the message
672 * @return number of bytes written to buf
675 transmit_confirmation_receipt_to_peer (void *cls, size_t size, void *buf)
677 struct P2PConfirmationReceiptMessage *receipt = cls;
680 #if DEBUG_CHAT_SERVICE
681 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
682 "Transmitting P2P confirmation receipt to '%s'\n",
683 GNUNET_h2s (&receipt->target));
687 /* client disconnected */
688 #if DEBUG_CHAT_SERVICE
689 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
690 "Buffer is NULL, dropping the message\n");
694 msg_size = sizeof (struct P2PConfirmationReceiptMessage);
695 GNUNET_assert (size >= msg_size);
696 memcpy (buf, receipt, msg_size);
697 GNUNET_free (receipt);
703 * Ask to send a confirmation receipt to the peer.
706 send_confirmation_receipt (void *cls, const GNUNET_HashCode * key, void *value)
708 struct P2PConfirmationReceiptMessage *receipt = cls;
709 struct ConnectedPeer *cp = value;
710 struct GNUNET_PeerIdentity pid;
711 struct P2PConfirmationReceiptMessage *my_receipt;
714 GNUNET_PEER_resolve (cp->pid, &pid);
715 #if DEBUG_CHAT_SERVICE
716 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending confirmation receipt to `%s'\n",
719 msg_size = sizeof (struct P2PConfirmationReceiptMessage);
721 GNUNET_memdup (receipt, sizeof (struct P2PConfirmationReceiptMessage));
723 GNUNET_CORE_notify_transmit_ready (core, GNUNET_YES, 1,
724 MAX_TRANSMIT_DELAY, &pid, msg_size,
725 &transmit_confirmation_receipt_to_peer,
727 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
728 _("Failed to queue a confirmation receipt\n"));
734 * A client sent a confirmation receipt. Broadcast the receipt to all connected
735 * peers if the author of the original message is a local client. Otherwise
736 * check the signature and notify the user if the signature is valid.
738 * @param cls closure, NULL
739 * @param client identification of the client
740 * @param message the actual message
743 handle_acknowledge_request (void *cls, struct GNUNET_SERVER_Client *client,
744 const struct GNUNET_MessageHeader *message)
746 const struct ConfirmationReceiptMessage *receipt;
747 struct ConfirmationReceiptMessage *crmsg;
748 struct P2PConfirmationReceiptMessage *p2p_crmsg;
749 struct ChatClient *target;
750 struct ChatClient *author;
752 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Client sent a confirmation receipt\n");
753 receipt = (const struct ConfirmationReceiptMessage *) message;
754 author = client_list_head;
755 while ((NULL != author) &&
757 memcmp (&receipt->author, &author->id, sizeof (GNUNET_HashCode))))
758 author = author->next;
761 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
762 "Unknown author of the original message\n");
764 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
767 target = client_list_head;
768 while ((NULL != target) &&
770 memcmp (&receipt->target, &target->id, sizeof (GNUNET_HashCode))))
771 target = target->next;
774 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
775 "Unknown target of the confirmation receipt\n");
777 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
780 if (NULL == author->client)
782 target->rcpt_sequence_number++;
783 #if DEBUG_CHAT_SERVICE
784 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
785 "Broadcasting %s's receipt #%u to neighbour peers\n",
786 GNUNET_h2s (&target->id), target->rcpt_sequence_number);
788 p2p_crmsg = GNUNET_malloc (sizeof (struct P2PConfirmationReceiptMessage));
789 p2p_crmsg->header.size =
790 htons (sizeof (struct P2PConfirmationReceiptMessage));
791 p2p_crmsg->header.type =
792 htons (GNUNET_MESSAGE_TYPE_CHAT_P2P_CONFIRMATION_RECEIPT);
793 p2p_crmsg->reserved = htonl (0);
794 p2p_crmsg->signature = receipt->signature;
795 p2p_crmsg->purpose = receipt->purpose;
796 p2p_crmsg->msg_sequence_number = receipt->sequence_number;
797 p2p_crmsg->timestamp = receipt->timestamp;
798 p2p_crmsg->target = receipt->target;
799 p2p_crmsg->author = receipt->author;
800 p2p_crmsg->content = receipt->content;
801 p2p_crmsg->sequence_number = htonl (target->rcpt_sequence_number);
802 GNUNET_CONTAINER_multihashmap_iterate (connected_peers,
803 &send_confirmation_receipt,
805 GNUNET_free (p2p_crmsg);
809 #if DEBUG_CHAT_SERVICE
810 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
811 "Verifying signature of the receipt\n");
814 GNUNET_CRYPTO_rsa_verify (GNUNET_SIGNATURE_PURPOSE_CHAT_RECEIPT,
815 &receipt->purpose, &receipt->signature,
816 &target->public_key))
818 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
819 "Invalid signature of the receipt\n");
821 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
824 #if DEBUG_CHAT_SERVICE
825 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
826 "Sending receipt to the client which sent the original message\n");
828 crmsg = GNUNET_memdup (receipt, sizeof (struct ConfirmationReceiptMessage));
830 htons (GNUNET_MESSAGE_TYPE_CHAT_CONFIRMATION_NOTIFICATION);
831 GNUNET_SERVER_notification_context_unicast (nc, author->client,
832 &crmsg->header, GNUNET_NO);
835 GNUNET_SERVER_receive_done (client, GNUNET_OK);
840 * Transmit a leave notification to the peer.
842 * @param cls closure, pointer to the
843 * 'struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded'
844 * @param size number of bytes available in buf
845 * @param buf where the callee should write the message
846 * @return number of bytes written to buf
849 transmit_leave_notification_to_peer (void *cls, size_t size, void *buf)
851 struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *public_key = cls;
852 struct P2PLeaveNotificationMessage *m = buf;
855 #if DEBUG_CHAT_SERVICE
856 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Transmitting P2P leave notification\n");
860 /* client disconnected */
861 #if DEBUG_CHAT_SERVICE
862 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
863 "Buffer is NULL, dropping the message\n");
867 msg_size = sizeof (struct P2PLeaveNotificationMessage);
868 GNUNET_assert (size >= msg_size);
870 m->header.type = htons (GNUNET_MESSAGE_TYPE_CHAT_P2P_LEAVE_NOTIFICATION);
871 m->header.size = htons (msg_size);
872 m->reserved = htonl (0);
873 m->user = *public_key;
874 GNUNET_free (public_key);
880 * Ask to send a leave notification to the peer.
883 send_leave_noficiation (void *cls, const GNUNET_HashCode * key, void *value)
885 struct ChatClient *entry = cls;
886 struct ConnectedPeer *cp = value;
887 struct GNUNET_PeerIdentity pid;
888 struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *public_key;
891 GNUNET_PEER_resolve (cp->pid, &pid);
892 #if DEBUG_CHAT_SERVICE
893 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending leave notification to `%s'\n",
896 msg_size = sizeof (struct P2PLeaveNotificationMessage);
898 GNUNET_memdup (&entry->public_key,
899 sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded));
901 GNUNET_CORE_notify_transmit_ready (core, GNUNET_YES, 1,
902 MAX_TRANSMIT_DELAY, &pid, msg_size,
903 &transmit_leave_notification_to_peer,
905 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
906 _("Failed to queue a leave notification\n"));
912 * A client disconnected. Remove all of its data structure entries and notify
913 * remaining room members.
915 * @param cls closure, NULL
916 * @param client identification of the client
919 handle_client_disconnect (void *cls, struct GNUNET_SERVER_Client *client)
921 struct ChatClient *entry;
922 struct ChatClient *pos;
923 struct ChatClient *prev;
924 struct LeaveNotificationMessage lnmsg;
926 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Client disconnected\n");
927 pos = client_list_head;
929 while ((NULL != pos) && (pos->client != client))
936 #if DEBUG_CHAT_SERVICE
937 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
938 "No such client. There is nothing to do\n");
943 client_list_head = pos->next;
945 prev->next = pos->next;
946 entry = client_list_head;
947 #if DEBUG_CHAT_SERVICE
948 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
949 "Notifying local room members that the client has disconnected\n");
951 lnmsg.header.size = htons (sizeof (struct LeaveNotificationMessage));
952 lnmsg.header.type = htons (GNUNET_MESSAGE_TYPE_CHAT_LEAVE_NOTIFICATION);
953 lnmsg.reserved = htonl (0);
954 lnmsg.user = pos->public_key;
955 while (NULL != entry)
957 if ((0 == strcmp (pos->room, entry->room)) && (NULL != entry->client))
959 GNUNET_SERVER_notification_context_unicast (nc, entry->client,
960 &lnmsg.header, GNUNET_NO);
964 #if DEBUG_CHAT_SERVICE
965 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
966 "Broadcasting leave notification to neighbour peers\n");
968 GNUNET_CONTAINER_multihashmap_iterate (connected_peers,
969 &send_leave_noficiation, pos);
970 GNUNET_free (pos->room);
971 GNUNET_free_non_null (pos->member_info);
977 * Handle P2P join notification.
979 * @param cls closure, always NULL
980 * @param other the other peer involved
981 * @param message the actual message
982 * @param atsi performance information
983 * @param atsi_count number of entries in atsi
984 * @return GNUNET_OK to keep the connection open,
985 * GNUNET_SYSERR to close it (signal serious error)
988 handle_p2p_join_notification (void *cls,
989 const struct GNUNET_PeerIdentity *other,
990 const struct GNUNET_MessageHeader *message,
991 const struct GNUNET_ATS_Information *atsi,
992 unsigned int atsi_count)
994 const struct P2PJoinNotificationMessage *p2p_jnmsg;
997 uint16_t header_size;
999 uint16_t room_name_len;
1000 struct ChatClient *new_entry;
1001 struct ChatClient *entry;
1002 struct JoinNotificationMessage *jnmsg;
1005 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Got P2P join notification\n");
1006 if (ntohs (message->size) <= sizeof (struct P2PJoinNotificationMessage))
1008 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Malformed message: wrong size\n");
1009 GNUNET_break_op (0);
1010 return GNUNET_SYSERR;
1012 p2p_jnmsg = (const struct P2PJoinNotificationMessage *) message;
1013 header_size = ntohs (p2p_jnmsg->header.size);
1014 room_name_len = ntohs (p2p_jnmsg->room_name_len);
1015 if (header_size - sizeof (struct P2PJoinNotificationMessage) <= room_name_len)
1017 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1018 "Malformed message: wrong length of the room name\n");
1019 GNUNET_break_op (0);
1020 return GNUNET_SYSERR;
1022 GNUNET_CRYPTO_hash (&p2p_jnmsg->public_key,
1023 sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
1025 entry = client_list_head;
1026 while (NULL != entry)
1028 if (0 == memcmp (&entry->id, &id, sizeof (GNUNET_HashCode)))
1030 #if DEBUG_CHAT_SERVICE
1031 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1032 "The client has already joined. There is nothing to do\n");
1036 entry = entry->next;
1039 header_size - sizeof (struct P2PJoinNotificationMessage) - room_name_len;
1040 roomptr = (const char *) &p2p_jnmsg[1];
1041 room_name = GNUNET_malloc (room_name_len + 1);
1042 memcpy (room_name, roomptr, room_name_len);
1043 room_name[room_name_len] = '\0';
1044 new_entry = GNUNET_malloc (sizeof (struct ChatClient));
1045 memset (new_entry, 0, sizeof (struct ChatClient));
1047 new_entry->client = NULL;
1048 new_entry->room = room_name;
1049 new_entry->public_key = p2p_jnmsg->public_key;
1050 new_entry->meta_len = meta_len;
1053 new_entry->member_info = GNUNET_malloc (meta_len);
1054 memcpy (new_entry->member_info, &roomptr[room_name_len], meta_len);
1057 new_entry->member_info = NULL;
1058 new_entry->msg_options = ntohl (p2p_jnmsg->msg_options);
1059 new_entry->next = client_list_head;
1060 client_list_head = new_entry;
1061 #if DEBUG_CHAT_SERVICE
1062 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1063 "Notifying local room members that we have a new client\n");
1065 jnmsg = GNUNET_malloc (sizeof (struct JoinNotificationMessage) + meta_len);
1066 jnmsg->header.type = htons (GNUNET_MESSAGE_TYPE_CHAT_JOIN_NOTIFICATION);
1067 jnmsg->header.size =
1068 htons (sizeof (struct JoinNotificationMessage) + meta_len);
1069 jnmsg->msg_options = p2p_jnmsg->msg_options;
1070 jnmsg->public_key = new_entry->public_key;
1071 memcpy (&jnmsg[1], &roomptr[room_name_len], meta_len);
1072 entry = client_list_head;
1073 while (NULL != entry)
1075 if ((0 == strcmp (room_name, entry->room)) && (NULL != entry->client))
1077 GNUNET_SERVER_notification_context_unicast (nc, entry->client,
1078 &jnmsg->header, GNUNET_NO);
1080 entry = entry->next;
1082 #if DEBUG_CHAT_SERVICE
1083 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1084 "Broadcasting join notification to neighbour peers\n");
1086 GNUNET_CONTAINER_multihashmap_iterate (connected_peers,
1087 &send_join_noficiation, new_entry);
1088 GNUNET_free (jnmsg);
1094 * Handle P2P leave notification.
1096 * @param cls closure, always NULL
1097 * @param other the other peer involved
1098 * @param message the actual message
1099 * @param atsi performance information
1100 * @param atsi_count number of entries in atsi
1101 * @return GNUNET_OK to keep the connection open,
1102 * GNUNET_SYSERR to close it (signal serious error)
1105 handle_p2p_leave_notification (void *cls,
1106 const struct GNUNET_PeerIdentity *other,
1107 const struct GNUNET_MessageHeader *message,
1108 const struct GNUNET_ATS_Information *atsi,
1109 unsigned int atsi_count)
1111 const struct P2PLeaveNotificationMessage *p2p_lnmsg;
1113 struct ChatClient *pos;
1114 struct ChatClient *prev;
1115 struct ChatClient *entry;
1116 struct LeaveNotificationMessage lnmsg;
1118 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Got P2P leave notification\n");
1119 p2p_lnmsg = (const struct P2PLeaveNotificationMessage *) message;
1120 GNUNET_CRYPTO_hash (&p2p_lnmsg->user,
1121 sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
1123 pos = client_list_head;
1127 if (0 == memcmp (&pos->id, &id, sizeof (GNUNET_HashCode)))
1134 #if DEBUG_CHAT_SERVICE
1135 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1136 "No such client. There is nothing to do\n");
1141 client_list_head = pos->next;
1143 prev->next = pos->next;
1144 #if DEBUG_CHAT_SERVICE
1145 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1146 "Notifying local room members that the client has gone away\n");
1148 lnmsg.header.size = htons (sizeof (struct LeaveNotificationMessage));
1149 lnmsg.header.type = htons (GNUNET_MESSAGE_TYPE_CHAT_LEAVE_NOTIFICATION);
1150 lnmsg.reserved = htonl (0);
1151 lnmsg.user = pos->public_key;
1152 entry = client_list_head;
1153 while (NULL != entry)
1155 if (0 == strcmp (pos->room, entry->room) && (NULL != entry->client))
1157 GNUNET_SERVER_notification_context_unicast (nc, entry->client,
1158 &lnmsg.header, GNUNET_NO);
1160 entry = entry->next;
1162 #if DEBUG_CHAT_SERVICE
1163 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1164 "Broadcasting leave notification to neighbour peers\n");
1166 GNUNET_CONTAINER_multihashmap_iterate (connected_peers,
1167 &send_leave_noficiation, pos);
1168 GNUNET_free (pos->room);
1169 GNUNET_free_non_null (pos->member_info);
1176 * Handle P2P message notification.
1178 * @param cls closure, always NULL
1179 * @param other the other peer involved
1180 * @param message the actual message
1181 * @param atsi performance information
1182 * @param atsi_count number of entries in atsi
1183 * @return GNUNET_OK to keep the connection open,
1184 * GNUNET_SYSERR to close it (signal serious error)
1187 handle_p2p_message_notification (void *cls,
1188 const struct GNUNET_PeerIdentity *other,
1189 const struct GNUNET_MessageHeader *message,
1190 const struct GNUNET_ATS_Information *atsi,
1191 unsigned int atsi_count)
1193 const struct P2PReceiveNotificationMessage *p2p_rnmsg;
1194 struct P2PReceiveNotificationMessage *my_p2p_rnmsg;
1195 struct ReceiveNotificationMessage *rnmsg;
1196 struct ChatClient *sender;
1197 struct ChatClient *pos;
1198 static GNUNET_HashCode all_zeros;
1202 uint16_t room_name_len;
1203 char *room_name = NULL;
1206 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Got P2P message notification\n");
1207 if (ntohs (message->size) <= sizeof (struct P2PReceiveNotificationMessage))
1209 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Malformed message: wrong size\n");
1210 GNUNET_break_op (0);
1211 return GNUNET_SYSERR;
1213 p2p_rnmsg = (const struct P2PReceiveNotificationMessage *) message;
1215 ntohs (p2p_rnmsg->header.size) -
1216 sizeof (struct P2PReceiveNotificationMessage);
1218 is_anon = (0 != (ntohl (p2p_rnmsg->msg_options) & GNUNET_CHAT_MSG_ANONYMOUS));
1221 room_name_len = ntohs (p2p_rnmsg->room_name_len);
1222 if (msg_len <= room_name_len)
1224 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1225 "Malformed message: wrong length of the room name\n");
1226 GNUNET_break_op (0);
1227 return GNUNET_SYSERR;
1229 msg_len -= room_name_len;
1230 if (lookup_anonymous_message (p2p_rnmsg))
1232 #if DEBUG_CHAT_SERVICE
1233 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1234 "This anonymous message has already been handled.");
1238 remember_anonymous_message (p2p_rnmsg);
1239 room_name = GNUNET_malloc (room_name_len + 1);
1240 memcpy (room_name, (char *) &p2p_rnmsg[1], room_name_len);
1241 room_name[room_name_len] = '\0';
1242 text = (char *) &p2p_rnmsg[1] + room_name_len;
1246 sender = client_list_head;
1247 while ((NULL != sender) &&
1249 memcmp (&sender->id, &p2p_rnmsg->sender, sizeof (GNUNET_HashCode))))
1250 sender = sender->next;
1253 /* not an error since the sender may have left before we got the
1255 #if DEBUG_CHAT_SERVICE
1256 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1257 "Unknown source. Rejecting the message\n");
1261 if (sender->msg_sequence_number >= ntohl (p2p_rnmsg->sequence_number))
1263 #if DEBUG_CHAT_SERVICE
1264 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1265 "This message has already been handled."
1266 " Sequence numbers (msg/sender): %u/%u\n",
1267 ntohl (p2p_rnmsg->sequence_number),
1268 sender->msg_sequence_number);
1272 sender->msg_sequence_number = ntohl (p2p_rnmsg->sequence_number);
1273 room_name = sender->room;
1274 text = (char *) &p2p_rnmsg[1];
1277 #if DEBUG_CHAT_SERVICE
1278 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1279 "Sending message to local room members\n");
1281 rnmsg = GNUNET_malloc (sizeof (struct ReceiveNotificationMessage) + msg_len);
1282 rnmsg->header.size =
1283 htons (sizeof (struct ReceiveNotificationMessage) + msg_len);
1284 rnmsg->header.type = htons (GNUNET_MESSAGE_TYPE_CHAT_MESSAGE_NOTIFICATION);
1285 rnmsg->msg_options = p2p_rnmsg->msg_options;
1286 rnmsg->sequence_number = p2p_rnmsg->sequence_number;
1287 rnmsg->reserved = htonl (0);
1288 rnmsg->timestamp = p2p_rnmsg->timestamp;
1290 (0 != memcmp (&all_zeros, &p2p_rnmsg->target, sizeof (GNUNET_HashCode)));
1292 memcpy (&rnmsg->encrypted_key, &p2p_rnmsg->encrypted_key,
1293 sizeof (struct GNUNET_CRYPTO_RsaEncryptedData));
1294 rnmsg->sender = p2p_rnmsg->sender;
1295 memcpy (&rnmsg[1], text, msg_len);
1296 pos = client_list_head;
1299 if ((0 == strcmp (room_name, pos->room)) && (NULL != pos->client))
1303 memcmp (&p2p_rnmsg->target, &pos->id, sizeof (GNUNET_HashCode)))) &&
1304 (0 == (ntohl (p2p_rnmsg->msg_options) & (~pos->msg_options))))
1306 GNUNET_SERVER_notification_context_unicast (nc, pos->client,
1307 &rnmsg->header, GNUNET_NO);
1313 GNUNET_free (room_name);
1314 #if DEBUG_CHAT_SERVICE
1315 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1316 "Broadcasting message notification to neighbour peers\n");
1318 my_p2p_rnmsg = GNUNET_memdup (p2p_rnmsg, ntohs (p2p_rnmsg->header.size));
1319 GNUNET_CONTAINER_multihashmap_iterate (connected_peers,
1320 &send_message_noficiation,
1322 GNUNET_free (rnmsg);
1328 * Handle P2P sync request.
1330 * @param cls closure, always NULL
1331 * @param other the other peer involved
1332 * @param message the actual message
1333 * @param atsi performance information
1334 * @param atsi_count number of entries in atsi
1335 * @return GNUNET_OK to keep the connection open,
1336 * GNUNET_SYSERR to close it (signal serious error)
1339 handle_p2p_sync_request (void *cls, const struct GNUNET_PeerIdentity *other,
1340 const struct GNUNET_MessageHeader *message,
1341 const struct GNUNET_ATS_Information *atsi,
1342 unsigned int atsi_count)
1344 struct ChatClient *entry;
1345 struct GNUNET_CORE_TransmitHandle *th;
1348 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Got P2P sync request\n");
1349 #if DEBUG_CHAT_SERVICE
1350 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1351 "Notifying the requester of all known clients\n");
1353 entry = client_list_head;
1354 while (NULL != entry)
1357 sizeof (struct P2PJoinNotificationMessage) + strlen (entry->room) +
1359 th = GNUNET_CORE_notify_transmit_ready (core, GNUNET_NO, 1,
1360 MAX_TRANSMIT_DELAY, other, msg_size,
1361 &transmit_join_notification_to_peer,
1363 GNUNET_assert (NULL != th);
1364 entry = entry->next;
1371 * Handle P2P confirmation receipt.
1373 * @param cls closure, always NULL
1374 * @param other the other peer involved
1375 * @param message the actual message
1376 * @param atsi performance information
1377 * @param atsi_count number of entries in atsi
1378 * @return GNUNET_OK to keep the connection open,
1379 * GNUNET_SYSERR to close it (signal serious error)
1382 handle_p2p_confirmation_receipt (void *cls,
1383 const struct GNUNET_PeerIdentity *other,
1384 const struct GNUNET_MessageHeader *message,
1385 const struct GNUNET_ATS_Information *atsi,
1386 unsigned int atsi_count)
1388 const struct P2PConfirmationReceiptMessage *p2p_crmsg;
1389 struct P2PConfirmationReceiptMessage *my_p2p_crmsg;
1390 struct ConfirmationReceiptMessage *crmsg;
1391 struct ChatClient *target;
1392 struct ChatClient *author;
1394 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Got P2P confirmation receipt\n");
1395 p2p_crmsg = (const struct P2PConfirmationReceiptMessage *) message;
1396 target = client_list_head;
1397 while ((NULL != target) &&
1399 memcmp (&target->id, &p2p_crmsg->target, sizeof (GNUNET_HashCode))))
1400 target = target->next;
1403 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1404 "Unknown source of the receipt. Rejecting the message\n");
1405 GNUNET_break_op (0);
1406 return GNUNET_SYSERR;
1408 if (target->rcpt_sequence_number >= ntohl (p2p_crmsg->sequence_number))
1410 #if DEBUG_CHAT_SERVICE
1411 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1412 "This receipt has already been handled."
1413 " Sequence numbers (msg/sender): %u/%u\n",
1414 ntohl (p2p_crmsg->sequence_number),
1415 target->rcpt_sequence_number);
1419 target->rcpt_sequence_number = ntohl (p2p_crmsg->sequence_number);
1420 author = client_list_head;
1421 while ((NULL != author) &&
1423 memcmp (&author->id, &p2p_crmsg->author, sizeof (GNUNET_HashCode))))
1424 author = author->next;
1427 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1428 "Unknown addressee. Rejecting the receipt\n");
1429 GNUNET_break_op (0);
1430 return GNUNET_SYSERR;
1433 if (NULL == author->client)
1435 #if DEBUG_CHAT_SERVICE
1436 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1437 "The author of the original message is not a local client."
1438 " Broadcasting receipt to neighbour peers\n");
1441 GNUNET_memdup (p2p_crmsg,
1442 sizeof (struct P2PConfirmationReceiptMessage));
1443 GNUNET_CONTAINER_multihashmap_iterate (connected_peers,
1444 &send_confirmation_receipt,
1446 GNUNET_free (my_p2p_crmsg);
1450 #if DEBUG_CHAT_SERVICE
1451 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1452 "The author of the original message is a local client."
1453 " Verifying signature of the receipt\n");
1455 crmsg = GNUNET_malloc (sizeof (struct ConfirmationReceiptMessage));
1456 crmsg->header.size = htons (sizeof (struct ConfirmationReceiptMessage));
1457 crmsg->header.type =
1458 htons (GNUNET_MESSAGE_TYPE_CHAT_CONFIRMATION_NOTIFICATION);
1459 crmsg->signature = p2p_crmsg->signature;
1460 crmsg->purpose = p2p_crmsg->purpose;
1461 crmsg->sequence_number = p2p_crmsg->msg_sequence_number;
1462 crmsg->reserved2 = 0;
1463 crmsg->timestamp = p2p_crmsg->timestamp;
1464 crmsg->target = p2p_crmsg->target;
1465 crmsg->author = p2p_crmsg->author;
1466 crmsg->content = p2p_crmsg->content;
1468 GNUNET_CRYPTO_rsa_verify (GNUNET_SIGNATURE_PURPOSE_CHAT_RECEIPT,
1469 &crmsg->purpose, &crmsg->signature,
1470 &target->public_key))
1472 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1473 "Invalid signature of the receipt\n");
1474 GNUNET_break_op (0);
1475 return GNUNET_SYSERR;
1477 #if DEBUG_CHAT_SERVICE
1478 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1479 "The author of the original message is a local client."
1480 " Sending receipt to the client\n");
1482 GNUNET_SERVER_notification_context_unicast (nc, author->client,
1483 &crmsg->header, GNUNET_NO);
1484 GNUNET_free (crmsg);
1491 * Transmit a sync request to the peer.
1493 * @param cls closure, NULL
1494 * @param size number of bytes available in buf
1495 * @param buf where the callee should write the message
1496 * @return number of bytes written to buf
1499 transmit_sync_request_to_peer (void *cls, size_t size, void *buf)
1501 struct GNUNET_MessageHeader *m = buf;
1504 #if DEBUG_CHAT_SERVICE
1505 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Transmitting P2P sync request\n");
1507 msg_size = sizeof (struct GNUNET_MessageHeader);
1508 GNUNET_assert (size >= msg_size);
1509 GNUNET_assert (NULL != buf);
1511 m->type = htons (GNUNET_MESSAGE_TYPE_CHAT_P2P_SYNC_REQUEST);
1512 m->size = htons (msg_size);
1518 * Method called whenever a peer connects.
1520 * @param cls closure
1521 * @param peer peer identity this notification is about
1522 * @param atsi performance data
1523 * @param atsi_count number of entries in atsi
1526 peer_connect_handler (void *cls, const struct GNUNET_PeerIdentity *peer,
1527 const struct GNUNET_ATS_Information *atsi,
1528 unsigned int atsi_count)
1530 struct ConnectedPeer *cp;
1531 struct GNUNET_CORE_TransmitHandle *th;
1533 if (0 == memcmp (peer, me, sizeof (struct GNUNET_PeerIdentity)))
1535 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Peer connected: %s\n",
1537 th = GNUNET_CORE_notify_transmit_ready (core, GNUNET_YES, 1,
1538 MAX_TRANSMIT_DELAY, peer,
1539 sizeof (struct GNUNET_MessageHeader),
1540 &transmit_sync_request_to_peer, NULL);
1541 GNUNET_assert (NULL != th);
1542 cp = GNUNET_CONTAINER_multihashmap_get (connected_peers, &peer->hashPubKey);
1548 cp = GNUNET_malloc (sizeof (struct ConnectedPeer));
1549 cp->pid = GNUNET_PEER_intern (peer);
1550 GNUNET_break (GNUNET_OK ==
1551 GNUNET_CONTAINER_multihashmap_put (connected_peers,
1552 &peer->hashPubKey, cp,
1553 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
1558 * Iterator to free peer entries.
1560 * @param cls closure, unused
1561 * @param key current key code
1562 * @param value value in the hash map (peer entry)
1563 * @return GNUNET_YES (we should continue to iterate)
1566 clean_peer (void *cls, const GNUNET_HashCode * key, void *value)
1568 struct ConnectedPeer *cp;
1569 const struct GNUNET_PeerIdentity *peer =
1570 (const struct GNUNET_PeerIdentity *) key;
1572 cp = GNUNET_CONTAINER_multihashmap_get (connected_peers, &peer->hashPubKey);
1575 GNUNET_break (GNUNET_YES ==
1576 GNUNET_CONTAINER_multihashmap_remove (connected_peers,
1577 &peer->hashPubKey, cp));
1578 GNUNET_PEER_change_rc (cp->pid, -1);
1585 * Method called whenever a peer disconnects.
1587 * @param cls closure, not used
1588 * @param peer peer identity this notification is about
1591 peer_disconnect_handler (void *cls, const struct GNUNET_PeerIdentity *peer)
1594 if (0 == memcmp (peer, me, sizeof (struct GNUNET_PeerIdentity)))
1596 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Peer disconnected: %s\n",
1598 clean_peer (NULL, (const GNUNET_HashCode *) peer, NULL);
1603 * Task run during shutdown.
1609 cleanup_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1611 struct AnonymousMessage *next_msg;
1612 struct ChatClient *next_client;
1614 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Cleaning up\n");
1617 GNUNET_CORE_disconnect (core);
1622 GNUNET_SERVER_notification_context_destroy (nc);
1625 while (NULL != client_list_head)
1627 next_client = client_list_head->next;
1628 GNUNET_free (client_list_head->room);
1629 GNUNET_free_non_null (client_list_head->member_info);
1630 GNUNET_free (client_list_head);
1631 client_list_head = next_client;
1633 while (NULL != anonymous_list_head)
1635 next_msg = anonymous_list_head->next;
1636 GNUNET_free (anonymous_list_head);
1637 anonymous_list_head = next_msg;
1639 GNUNET_CONTAINER_multihashmap_iterate (connected_peers, &clean_peer, NULL);
1640 GNUNET_CONTAINER_multihashmap_destroy (connected_peers);
1641 connected_peers = NULL;
1646 * To be called on core init/fail.
1648 * @param cls closure, NULL
1649 * @param server handle to the server for this service
1650 * @param my_identity the public identity of this peer
1653 core_init (void *cls, struct GNUNET_CORE_Handle *server,
1654 const struct GNUNET_PeerIdentity *my_identity)
1656 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Core initialized\n");
1662 * Process chat requests.
1664 * @param cls closure, NULL
1665 * @param server the initialized server
1666 * @param c configuration to use
1669 run (void *cls, struct GNUNET_SERVER_Handle *server,
1670 const struct GNUNET_CONFIGURATION_Handle *c)
1672 static const struct GNUNET_SERVER_MessageHandler handlers[] = {
1673 {&handle_join_request, NULL,
1674 GNUNET_MESSAGE_TYPE_CHAT_JOIN_REQUEST, 0},
1675 {&handle_transmit_request, NULL,
1676 GNUNET_MESSAGE_TYPE_CHAT_TRANSMIT_REQUEST, 0},
1677 {&handle_acknowledge_request, NULL,
1678 GNUNET_MESSAGE_TYPE_CHAT_CONFIRMATION_RECEIPT,
1679 sizeof (struct ConfirmationReceiptMessage)},
1682 static const struct GNUNET_CORE_MessageHandler p2p_handlers[] = {
1683 {&handle_p2p_join_notification,
1684 GNUNET_MESSAGE_TYPE_CHAT_P2P_JOIN_NOTIFICATION, 0},
1685 {&handle_p2p_leave_notification,
1686 GNUNET_MESSAGE_TYPE_CHAT_P2P_LEAVE_NOTIFICATION,
1687 sizeof (struct P2PLeaveNotificationMessage)},
1688 {&handle_p2p_message_notification,
1689 GNUNET_MESSAGE_TYPE_CHAT_P2P_MESSAGE_NOTIFICATION, 0},
1690 {&handle_p2p_sync_request,
1691 GNUNET_MESSAGE_TYPE_CHAT_P2P_SYNC_REQUEST,
1692 sizeof (struct GNUNET_MessageHeader)},
1693 {&handle_p2p_confirmation_receipt,
1694 GNUNET_MESSAGE_TYPE_CHAT_P2P_CONFIRMATION_RECEIPT,
1695 sizeof (struct P2PConfirmationReceiptMessage)},
1699 GNUNET_log_setup ("gnunet-service-chat",
1700 #if DEBUG_CHAT_SERVICE
1707 nc = GNUNET_SERVER_notification_context_create (server, 16);
1709 GNUNET_CONTAINER_multihashmap_create (EXPECTED_NEIGHBOUR_COUNT);
1710 GNUNET_SERVER_add_handlers (server, handlers);
1712 GNUNET_CORE_connect (cfg, NULL, &core_init,
1713 &peer_connect_handler, &peer_disconnect_handler,
1714 NULL, GNUNET_NO, NULL, GNUNET_NO, p2p_handlers);
1715 GNUNET_SERVER_disconnect_notify (server, &handle_client_disconnect, NULL);
1716 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &cleanup_task,
1722 * The main function for the chat service.
1724 * @param argc number of arguments from the command line
1725 * @param argv command line arguments
1726 * @return 0 ok, 1 on error
1729 main (int argc, char *const *argv)
1731 return (GNUNET_OK ==
1732 GNUNET_SERVICE_run (argc, argv, "chat", GNUNET_SERVICE_OPTION_NONE,
1733 &run, NULL)) ? 0 : 1;
1736 /* end of gnunet-service-chat.c */