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
40 #define MAX_ANONYMOUS_MSG_LIST_LENGTH 16
44 * Linked list of our current clients.
48 struct ChatClient *next;
51 * Handle for a chat client (NULL for external clients).
53 struct GNUNET_SERVER_Client *client;
56 * Public key of the client.
58 struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded public_key;
61 * Name of the room which the client is in.
66 * Serialized metadata of the client.
71 * Hash of the public key (for convenience).
76 * Options which the client is willing to receive.
81 * Length of serialized metadata in member_info.
86 * Sequence number of the last message sent by the client.
88 uint32_t msg_sequence_number;
91 * Sequence number of the last receipt sent by the client.
92 * Used to discard already processed receipts.
94 uint32_t rcpt_sequence_number;
99 * Information about a peer that we are connected to.
100 * We track data that is useful for determining which
101 * peers should receive our requests.
106 * The peer's identity.
112 * Linked list of recent anonymous messages.
114 struct AnonymousMessage
116 struct AnonymousMessage *next;
119 * Hash of the message.
121 GNUNET_HashCode hash;
127 * Handle to the core service (NULL until we've connected to it).
129 static struct GNUNET_CORE_Handle *core;
134 static const struct GNUNET_CONFIGURATION_Handle *cfg;
137 * The identity of this host.
139 static const struct GNUNET_PeerIdentity *me;
142 * Head of the list of current clients.
144 static struct ChatClient *client_list_head = NULL;
147 * Notification context containing all connected clients.
149 struct GNUNET_SERVER_NotificationContext *nc = NULL;
152 * Head of the list of recent anonymous messages.
154 static struct AnonymousMessage *anonymous_list_head = NULL;
157 * Map of peer identifiers to "struct ConnectedPeer" (for that peer).
159 static struct GNUNET_CONTAINER_MultiHashMap *connected_peers;
163 remember_anonymous_message (const struct P2PReceiveNotificationMessage
166 static GNUNET_HashCode hash;
167 struct AnonymousMessage *anon_msg;
168 struct AnonymousMessage *prev;
171 GNUNET_CRYPTO_hash (p2p_rnmsg, ntohs (p2p_rnmsg->header.size), &hash);
172 anon_msg = GNUNET_malloc (sizeof (struct AnonymousMessage));
173 anon_msg->hash = hash;
174 anon_msg->next = anonymous_list_head;
175 anonymous_list_head = anon_msg;
178 while ((NULL != anon_msg->next))
181 anon_msg = anon_msg->next;
184 if (anon_list_len == MAX_ANONYMOUS_MSG_LIST_LENGTH)
186 GNUNET_free (anon_msg);
194 lookup_anonymous_message (const struct P2PReceiveNotificationMessage *p2p_rnmsg)
196 static GNUNET_HashCode hash;
197 struct AnonymousMessage *anon_msg;
199 GNUNET_CRYPTO_hash (p2p_rnmsg, ntohs (p2p_rnmsg->header.size), &hash);
200 anon_msg = anonymous_list_head;
201 while ((NULL != anon_msg) &&
202 (0 != memcmp (&anon_msg->hash, &hash, sizeof (GNUNET_HashCode))))
203 anon_msg = anon_msg->next;
204 return (NULL != anon_msg);
209 * Transmit a message notification to the peer.
211 * @param cls closure, pointer to the 'struct P2PReceiveNotificationMessage'
212 * @param size number of bytes available in buf
213 * @param buf where the callee should write the message
214 * @return number of bytes written to buf
217 transmit_message_notification_to_peer (void *cls, size_t size, void *buf)
219 struct P2PReceiveNotificationMessage *my_msg = cls;
220 struct P2PReceiveNotificationMessage *m = buf;
223 #if DEBUG_CHAT_SERVICE
224 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
225 "Transmitting P2P message notification\n");
229 /* client disconnected */
230 #if DEBUG_CHAT_SERVICE
231 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
232 "Buffer is NULL, dropping the message\n");
236 msg_size = ntohs (my_msg->header.size);
237 GNUNET_assert (size >= msg_size);
238 memcpy (m, my_msg, msg_size);
239 GNUNET_free (my_msg);
245 * Ask to send a message notification to the peer.
248 send_message_noficiation (void *cls, const GNUNET_HashCode * key, void *value)
250 struct P2PReceiveNotificationMessage *msg = cls;
251 struct ConnectedPeer *cp = value;
252 struct GNUNET_PeerIdentity pid;
253 struct P2PReceiveNotificationMessage *my_msg;
255 GNUNET_PEER_resolve (cp->pid, &pid);
256 #if DEBUG_CHAT_SERVICE
257 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending message notification to `%s'\n",
260 my_msg = GNUNET_memdup (msg, ntohs (msg->header.size));
262 GNUNET_CORE_notify_transmit_ready (core, GNUNET_NO, 1, MAX_TRANSMIT_DELAY,
263 &pid, ntohs (msg->header.size),
264 &transmit_message_notification_to_peer,
266 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
267 _("Failed to queue a message notification\n"));
273 * A client sent a chat message. Encrypt the message text if the message is
274 * private. Send the message to local room members and to all connected peers.
276 * @param cls closure, NULL
277 * @param client identification of the client
278 * @param message the actual message
281 handle_transmit_request (void *cls, struct GNUNET_SERVER_Client *client,
282 const struct GNUNET_MessageHeader *message)
284 static GNUNET_HashCode all_zeros;
285 const struct TransmitRequestMessage *trmsg;
286 struct ReceiveNotificationMessage *rnmsg;
287 struct P2PReceiveNotificationMessage *p2p_rnmsg;
288 struct ChatClient *pos;
289 struct ChatClient *target;
290 struct GNUNET_CRYPTO_AesSessionKey key;
291 char encrypted_msg[MAX_MESSAGE_LENGTH];
298 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Client sent a chat message\n");
299 if (ntohs (message->size) <= sizeof (struct TransmitRequestMessage))
301 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Malformed message: wrong size\n");
303 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
306 trmsg = (const struct TransmitRequestMessage *) message;
307 msg_len = ntohs (trmsg->header.size) - sizeof (struct TransmitRequestMessage);
308 is_priv = (0 != (ntohl (trmsg->msg_options) & GNUNET_CHAT_MSG_PRIVATE));
311 #if DEBUG_CHAT_SERVICE
312 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Encrypting the message text\n");
314 GNUNET_CRYPTO_aes_create_session_key (&key);
316 GNUNET_CRYPTO_aes_encrypt (&trmsg[1], msg_len, &key,
318 GNUNET_CRYPTO_AesInitializationVector *)
319 INITVALUE, encrypted_msg);
322 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
323 "Could not encrypt the message text\n");
325 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
329 rnmsg = GNUNET_malloc (sizeof (struct ReceiveNotificationMessage) + msg_len);
331 htons (sizeof (struct ReceiveNotificationMessage) + msg_len);
332 rnmsg->header.type = htons (GNUNET_MESSAGE_TYPE_CHAT_MESSAGE_NOTIFICATION);
333 rnmsg->msg_options = trmsg->msg_options;
334 rnmsg->timestamp = trmsg->timestamp;
335 pos = client_list_head;
336 while ((NULL != pos) && (pos->client != client))
340 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
341 "The client is not a member of a chat room. Client has to "
342 "join a chat room first\n");
344 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
349 pos->msg_sequence_number = ntohl (trmsg->sequence_number);
350 is_anon = (0 != (ntohl (trmsg->msg_options) & GNUNET_CHAT_MSG_ANONYMOUS));
353 memset (&rnmsg->sender, 0, sizeof (GNUNET_HashCode));
354 rnmsg->sequence_number = 0;
358 rnmsg->sender = pos->id;
359 rnmsg->sequence_number = trmsg->sequence_number;
363 #if DEBUG_CHAT_SERVICE
364 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
365 "Encrypting the session key using the public key of '%s'\n",
366 GNUNET_h2s (&trmsg->target));
368 if (0 == memcmp (&all_zeros, &trmsg->target, sizeof (GNUNET_HashCode)))
370 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
371 "Malformed message: private, but no target\n");
373 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
377 memcpy (&rnmsg[1], encrypted_msg, msg_len);
378 target = client_list_head;
379 while ((NULL != target) &&
381 memcmp (&target->id, &trmsg->target, sizeof (GNUNET_HashCode))))
382 target = target->next;
385 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
386 "Unknown target of the private message\n");
388 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
393 GNUNET_CRYPTO_rsa_encrypt (&key,
394 sizeof (struct GNUNET_CRYPTO_AesSessionKey),
395 &target->public_key, &rnmsg->encrypted_key))
397 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
398 "Could not encrypt the session key\n");
400 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
407 memcpy (&rnmsg[1], &trmsg[1], msg_len);
409 pos = client_list_head;
410 #if DEBUG_CHAT_SERVICE
411 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
412 "Sending message to local room members\n");
416 if ((0 == strcmp (room, pos->room)) && (NULL != pos->client) &&
417 (pos->client != client))
420 (0 == memcmp (&trmsg->target, &pos->id, sizeof (GNUNET_HashCode))))
421 && (0 == (ntohl (trmsg->msg_options) & (~pos->msg_options))))
423 GNUNET_SERVER_notification_context_unicast (nc, pos->client,
424 &rnmsg->header, GNUNET_NO);
429 #if DEBUG_CHAT_SERVICE
430 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
431 "Broadcasting message to neighbour peers\n");
435 room_len = strlen (room);
437 GNUNET_malloc (sizeof (struct P2PReceiveNotificationMessage) + msg_len +
439 p2p_rnmsg->header.size =
440 htons (sizeof (struct P2PReceiveNotificationMessage) + msg_len +
442 p2p_rnmsg->room_name_len = htons (room_len);
443 memcpy ((char *) &p2p_rnmsg[1], room, room_len);
444 memcpy ((char *) &p2p_rnmsg[1] + room_len, &trmsg[1], msg_len);
449 GNUNET_malloc (sizeof (struct P2PReceiveNotificationMessage) + msg_len);
450 p2p_rnmsg->header.size =
451 htons (sizeof (struct P2PReceiveNotificationMessage) + msg_len);
454 memcpy (&p2p_rnmsg[1], encrypted_msg, msg_len);
455 memcpy (&p2p_rnmsg->encrypted_key, &rnmsg->encrypted_key,
456 sizeof (struct GNUNET_CRYPTO_RsaEncryptedData));
459 memcpy (&p2p_rnmsg[1], &trmsg[1], msg_len);
461 p2p_rnmsg->header.type =
462 htons (GNUNET_MESSAGE_TYPE_CHAT_P2P_MESSAGE_NOTIFICATION);
463 p2p_rnmsg->msg_options = trmsg->msg_options;
464 p2p_rnmsg->sequence_number = trmsg->sequence_number;
465 p2p_rnmsg->timestamp = trmsg->timestamp;
466 p2p_rnmsg->reserved = htons (0);
467 p2p_rnmsg->sender = rnmsg->sender;
468 p2p_rnmsg->target = trmsg->target;
470 remember_anonymous_message (p2p_rnmsg);
471 GNUNET_CONTAINER_multihashmap_iterate (connected_peers,
472 &send_message_noficiation, p2p_rnmsg);
473 GNUNET_free (p2p_rnmsg);
474 GNUNET_SERVER_receive_done (client, GNUNET_OK);
480 * Transmit a join notification to the peer.
482 * @param cls closure, pointer to the 'struct ChatClient'
483 * @param size number of bytes available in buf
484 * @param buf where the callee should write the message
485 * @return number of bytes written to buf
488 transmit_join_notification_to_peer (void *cls, size_t size, void *buf)
490 struct ChatClient *entry = cls;
491 struct P2PJoinNotificationMessage *m = buf;
497 #if DEBUG_CHAT_SERVICE
498 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Transmitting P2P join notification\n");
500 room_len = strlen (entry->room);
501 meta_len = entry->meta_len;
502 msg_size = sizeof (struct P2PJoinNotificationMessage) + meta_len + room_len;
503 GNUNET_assert (size >= msg_size);
504 GNUNET_assert (NULL != buf);
506 m->header.type = htons (GNUNET_MESSAGE_TYPE_CHAT_P2P_JOIN_NOTIFICATION);
507 m->header.size = htons (msg_size);
508 m->msg_options = htonl (entry->msg_options);
509 m->room_name_len = htons (room_len);
510 m->reserved = htons (0);
511 m->reserved2 = htonl (0);
512 m->public_key = entry->public_key;
513 roomptr = (char *) &m[1];
514 memcpy (roomptr, entry->room, room_len);
516 memcpy (&roomptr[room_len], entry->member_info, meta_len);
522 * Ask to send a join notification to the peer.
525 send_join_noficiation (void *cls, const GNUNET_HashCode * key, void *value)
527 struct ChatClient *entry = cls;
528 struct ConnectedPeer *cp = value;
529 struct GNUNET_PeerIdentity pid;
532 GNUNET_PEER_resolve (cp->pid, &pid);
533 #if DEBUG_CHAT_SERVICE
534 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending join notification to `%s'\n",
538 sizeof (struct P2PJoinNotificationMessage) + strlen (entry->room) +
541 GNUNET_CORE_notify_transmit_ready (core, GNUNET_NO, 1, MAX_TRANSMIT_DELAY,
543 &transmit_join_notification_to_peer,
545 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
546 _("Failed to queue a join notification\n"));
552 * A client asked for entering a chat room. Add the new member to the list of
553 * clients and notify remaining room members.
555 * @param cls closure, NULL
556 * @param client identification of the client
557 * @param message the actual message
560 handle_join_request (void *cls, struct GNUNET_SERVER_Client *client,
561 const struct GNUNET_MessageHeader *message)
563 const struct JoinRequestMessage *jrmsg;
566 uint16_t header_size;
568 uint16_t room_name_len;
569 struct ChatClient *new_entry;
570 struct ChatClient *entry;
571 struct JoinNotificationMessage *jnmsg;
572 struct JoinNotificationMessage *entry_jnmsg;
574 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Client sent a join request\n");
575 if (ntohs (message->size) <= sizeof (struct JoinRequestMessage))
577 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Malformed message: wrong size\n");
579 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
582 jrmsg = (const struct JoinRequestMessage *) message;
583 header_size = ntohs (jrmsg->header.size);
584 room_name_len = ntohs (jrmsg->room_name_len);
585 if (header_size - sizeof (struct JoinRequestMessage) <= room_name_len)
587 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
588 "Malformed message: wrong length of the room name\n");
590 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
593 meta_len = header_size - sizeof (struct JoinRequestMessage) - room_name_len;
594 roomptr = (const char *) &jrmsg[1];
595 room_name = GNUNET_malloc (room_name_len + 1);
596 memcpy (room_name, roomptr, room_name_len);
597 room_name[room_name_len] = '\0';
598 new_entry = GNUNET_malloc (sizeof (struct ChatClient));
599 memset (new_entry, 0, sizeof (struct ChatClient));
600 new_entry->client = client;
601 new_entry->room = room_name;
602 new_entry->public_key = jrmsg->public_key;
603 new_entry->meta_len = meta_len;
606 new_entry->member_info = GNUNET_malloc (meta_len);
607 memcpy (new_entry->member_info, &roomptr[room_name_len], meta_len);
610 new_entry->member_info = NULL;
611 GNUNET_CRYPTO_hash (&new_entry->public_key,
612 sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
614 new_entry->msg_options = ntohl (jrmsg->msg_options);
615 new_entry->next = client_list_head;
616 client_list_head = new_entry;
617 #if DEBUG_CHAT_SERVICE
618 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
619 "Synchronizing room members between local clients\n");
621 jnmsg = GNUNET_malloc (sizeof (struct JoinNotificationMessage) + meta_len);
622 jnmsg->header.type = htons (GNUNET_MESSAGE_TYPE_CHAT_JOIN_NOTIFICATION);
624 htons (sizeof (struct JoinNotificationMessage) + meta_len);
625 jnmsg->msg_options = jrmsg->msg_options;
626 jnmsg->public_key = new_entry->public_key;
627 memcpy (&jnmsg[1], &roomptr[room_name_len], meta_len);
628 GNUNET_SERVER_notification_context_add (nc, client);
629 entry = client_list_head;
630 while (NULL != entry)
632 if (0 == strcmp (room_name, entry->room))
634 if (NULL != entry->client)
635 GNUNET_SERVER_notification_context_unicast (nc, entry->client,
636 &jnmsg->header, GNUNET_NO);
637 if (entry->client != client)
640 GNUNET_malloc (sizeof (struct JoinNotificationMessage) +
642 entry_jnmsg->header.type =
643 htons (GNUNET_MESSAGE_TYPE_CHAT_JOIN_NOTIFICATION);
644 entry_jnmsg->header.size =
645 htons (sizeof (struct JoinNotificationMessage) + entry->meta_len);
646 entry_jnmsg->msg_options = entry->msg_options;
647 entry_jnmsg->public_key = entry->public_key;
648 memcpy (&entry_jnmsg[1], entry->member_info, entry->meta_len);
649 GNUNET_SERVER_notification_context_unicast (nc, client,
650 &entry_jnmsg->header,
652 GNUNET_free (entry_jnmsg);
657 #if DEBUG_CHAT_SERVICE
658 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
659 "Broadcasting join notification to neighbour peers\n");
661 GNUNET_CONTAINER_multihashmap_iterate (connected_peers,
662 &send_join_noficiation, new_entry);
663 GNUNET_SERVER_receive_done (client, GNUNET_OK);
668 * Transmit a confirmation receipt to the peer.
670 * @param cls closure, pointer to the 'struct P2PConfirmationReceiptMessage'
671 * @param size number of bytes available in buf
672 * @param buf where the callee should write the message
673 * @return number of bytes written to buf
676 transmit_confirmation_receipt_to_peer (void *cls, size_t size, void *buf)
678 struct P2PConfirmationReceiptMessage *receipt = cls;
681 #if DEBUG_CHAT_SERVICE
682 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
683 "Transmitting P2P confirmation receipt to '%s'\n",
684 GNUNET_h2s (&receipt->target));
688 /* client disconnected */
689 #if DEBUG_CHAT_SERVICE
690 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
691 "Buffer is NULL, dropping the message\n");
695 msg_size = sizeof (struct P2PConfirmationReceiptMessage);
696 GNUNET_assert (size >= msg_size);
697 memcpy (buf, receipt, msg_size);
698 GNUNET_free (receipt);
704 * Ask to send a confirmation receipt to the peer.
707 send_confirmation_receipt (void *cls, const GNUNET_HashCode * key, void *value)
709 struct P2PConfirmationReceiptMessage *receipt = cls;
710 struct ConnectedPeer *cp = value;
711 struct GNUNET_PeerIdentity pid;
712 struct P2PConfirmationReceiptMessage *my_receipt;
715 GNUNET_PEER_resolve (cp->pid, &pid);
716 #if DEBUG_CHAT_SERVICE
717 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending confirmation receipt to `%s'\n",
720 msg_size = sizeof (struct P2PConfirmationReceiptMessage);
722 GNUNET_memdup (receipt, sizeof (struct P2PConfirmationReceiptMessage));
724 GNUNET_CORE_notify_transmit_ready (core, GNUNET_YES, 1,
725 MAX_TRANSMIT_DELAY, &pid, msg_size,
726 &transmit_confirmation_receipt_to_peer,
728 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
729 _("Failed to queue a confirmation receipt\n"));
735 * A client sent a confirmation receipt. Broadcast the receipt to all connected
736 * peers if the author of the original message is a local client. Otherwise
737 * check the signature and notify the user if the signature is valid.
739 * @param cls closure, NULL
740 * @param client identification of the client
741 * @param message the actual message
744 handle_acknowledge_request (void *cls, struct GNUNET_SERVER_Client *client,
745 const struct GNUNET_MessageHeader *message)
747 const struct ConfirmationReceiptMessage *receipt;
748 struct ConfirmationReceiptMessage *crmsg;
749 struct P2PConfirmationReceiptMessage *p2p_crmsg;
750 struct ChatClient *target;
751 struct ChatClient *author;
753 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Client sent a confirmation receipt\n");
754 receipt = (const struct ConfirmationReceiptMessage *) message;
755 author = client_list_head;
756 while ((NULL != author) &&
758 memcmp (&receipt->author, &author->id, sizeof (GNUNET_HashCode))))
759 author = author->next;
762 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
763 "Unknown author of the original message\n");
765 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
768 target = client_list_head;
769 while ((NULL != target) &&
771 memcmp (&receipt->target, &target->id, sizeof (GNUNET_HashCode))))
772 target = target->next;
775 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
776 "Unknown target of the confirmation receipt\n");
778 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
781 if (NULL == author->client)
783 target->rcpt_sequence_number++;
784 #if DEBUG_CHAT_SERVICE
785 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
786 "Broadcasting %s's receipt #%u to neighbour peers\n",
787 GNUNET_h2s (&target->id), target->rcpt_sequence_number);
789 p2p_crmsg = GNUNET_malloc (sizeof (struct P2PConfirmationReceiptMessage));
790 p2p_crmsg->header.size =
791 htons (sizeof (struct P2PConfirmationReceiptMessage));
792 p2p_crmsg->header.type =
793 htons (GNUNET_MESSAGE_TYPE_CHAT_P2P_CONFIRMATION_RECEIPT);
794 p2p_crmsg->reserved = htonl (0);
795 p2p_crmsg->signature = receipt->signature;
796 p2p_crmsg->purpose = receipt->purpose;
797 p2p_crmsg->msg_sequence_number = receipt->sequence_number;
798 p2p_crmsg->timestamp = receipt->timestamp;
799 p2p_crmsg->target = receipt->target;
800 p2p_crmsg->author = receipt->author;
801 p2p_crmsg->content = receipt->content;
802 p2p_crmsg->sequence_number = htonl (target->rcpt_sequence_number);
803 GNUNET_CONTAINER_multihashmap_iterate (connected_peers,
804 &send_confirmation_receipt,
806 GNUNET_free (p2p_crmsg);
810 #if DEBUG_CHAT_SERVICE
811 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
812 "Verifying signature of the receipt\n");
815 GNUNET_CRYPTO_rsa_verify (GNUNET_SIGNATURE_PURPOSE_CHAT_RECEIPT,
816 &receipt->purpose, &receipt->signature,
817 &target->public_key))
819 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
820 "Invalid signature of the receipt\n");
822 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
825 #if DEBUG_CHAT_SERVICE
826 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
827 "Sending receipt to the client which sent the original message\n");
829 crmsg = GNUNET_memdup (receipt, sizeof (struct ConfirmationReceiptMessage));
831 htons (GNUNET_MESSAGE_TYPE_CHAT_CONFIRMATION_NOTIFICATION);
832 GNUNET_SERVER_notification_context_unicast (nc, author->client,
833 &crmsg->header, GNUNET_NO);
836 GNUNET_SERVER_receive_done (client, GNUNET_OK);
841 * Transmit a leave notification to the peer.
843 * @param cls closure, pointer to the
844 * 'struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded'
845 * @param size number of bytes available in buf
846 * @param buf where the callee should write the message
847 * @return number of bytes written to buf
850 transmit_leave_notification_to_peer (void *cls, size_t size, void *buf)
852 struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *public_key = cls;
853 struct P2PLeaveNotificationMessage *m = buf;
856 #if DEBUG_CHAT_SERVICE
857 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Transmitting P2P leave notification\n");
861 /* client disconnected */
862 #if DEBUG_CHAT_SERVICE
863 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
864 "Buffer is NULL, dropping the message\n");
868 msg_size = sizeof (struct P2PLeaveNotificationMessage);
869 GNUNET_assert (size >= msg_size);
871 m->header.type = htons (GNUNET_MESSAGE_TYPE_CHAT_P2P_LEAVE_NOTIFICATION);
872 m->header.size = htons (msg_size);
873 m->reserved = htonl (0);
874 m->user = *public_key;
875 GNUNET_free (public_key);
881 * Ask to send a leave notification to the peer.
884 send_leave_noficiation (void *cls, const GNUNET_HashCode * key, void *value)
886 struct ChatClient *entry = cls;
887 struct ConnectedPeer *cp = value;
888 struct GNUNET_PeerIdentity pid;
889 struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *public_key;
892 GNUNET_PEER_resolve (cp->pid, &pid);
893 #if DEBUG_CHAT_SERVICE
894 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending leave notification to `%s'\n",
897 msg_size = sizeof (struct P2PLeaveNotificationMessage);
899 GNUNET_memdup (&entry->public_key,
900 sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded));
902 GNUNET_CORE_notify_transmit_ready (core, GNUNET_YES, 1,
903 MAX_TRANSMIT_DELAY, &pid, msg_size,
904 &transmit_leave_notification_to_peer,
906 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
907 _("Failed to queue a leave notification\n"));
913 * A client disconnected. Remove all of its data structure entries and notify
914 * remaining room members.
916 * @param cls closure, NULL
917 * @param client identification of the client
920 handle_client_disconnect (void *cls, struct GNUNET_SERVER_Client *client)
922 struct ChatClient *entry;
923 struct ChatClient *pos;
924 struct ChatClient *prev;
925 struct LeaveNotificationMessage lnmsg;
927 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Client disconnected\n");
928 pos = client_list_head;
930 while ((NULL != pos) && (pos->client != client))
937 #if DEBUG_CHAT_SERVICE
938 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
939 "No such client. There is nothing to do\n");
944 client_list_head = pos->next;
946 prev->next = pos->next;
947 entry = client_list_head;
948 #if DEBUG_CHAT_SERVICE
949 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
950 "Notifying local room members that the client has disconnected\n");
952 lnmsg.header.size = htons (sizeof (struct LeaveNotificationMessage));
953 lnmsg.header.type = htons (GNUNET_MESSAGE_TYPE_CHAT_LEAVE_NOTIFICATION);
954 lnmsg.reserved = htonl (0);
955 lnmsg.user = pos->public_key;
956 while (NULL != entry)
958 if ((0 == strcmp (pos->room, entry->room)) && (NULL != entry->client))
960 GNUNET_SERVER_notification_context_unicast (nc, entry->client,
961 &lnmsg.header, GNUNET_NO);
965 #if DEBUG_CHAT_SERVICE
966 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
967 "Broadcasting leave notification to neighbour peers\n");
969 GNUNET_CONTAINER_multihashmap_iterate (connected_peers,
970 &send_leave_noficiation, pos);
971 GNUNET_free (pos->room);
972 GNUNET_free_non_null (pos->member_info);
978 * Handle P2P join notification.
980 * @param cls closure, always NULL
981 * @param other the other peer involved
982 * @param message the actual message
983 * @param atsi performance information
984 * @param atsi_count number of entries in atsi
985 * @return GNUNET_OK to keep the connection open,
986 * GNUNET_SYSERR to close it (signal serious error)
989 handle_p2p_join_notification (void *cls,
990 const struct GNUNET_PeerIdentity *other,
991 const struct GNUNET_MessageHeader *message,
992 const struct GNUNET_ATS_Information *atsi,
993 unsigned int atsi_count)
995 const struct P2PJoinNotificationMessage *p2p_jnmsg;
998 uint16_t header_size;
1000 uint16_t room_name_len;
1001 struct ChatClient *new_entry;
1002 struct ChatClient *entry;
1003 struct JoinNotificationMessage *jnmsg;
1006 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Got P2P join notification\n");
1007 if (ntohs (message->size) <= sizeof (struct P2PJoinNotificationMessage))
1009 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Malformed message: wrong size\n");
1010 GNUNET_break_op (0);
1011 return GNUNET_SYSERR;
1013 p2p_jnmsg = (const struct P2PJoinNotificationMessage *) message;
1014 header_size = ntohs (p2p_jnmsg->header.size);
1015 room_name_len = ntohs (p2p_jnmsg->room_name_len);
1016 if (header_size - sizeof (struct P2PJoinNotificationMessage) <= room_name_len)
1018 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1019 "Malformed message: wrong length of the room name\n");
1020 GNUNET_break_op (0);
1021 return GNUNET_SYSERR;
1023 GNUNET_CRYPTO_hash (&p2p_jnmsg->public_key,
1024 sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
1026 entry = client_list_head;
1027 while (NULL != entry)
1029 if (0 == memcmp (&entry->id, &id, sizeof (GNUNET_HashCode)))
1031 #if DEBUG_CHAT_SERVICE
1032 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1033 "The client has already joined. There is nothing to do\n");
1037 entry = entry->next;
1040 header_size - sizeof (struct P2PJoinNotificationMessage) - room_name_len;
1041 roomptr = (const char *) &p2p_jnmsg[1];
1042 room_name = GNUNET_malloc (room_name_len + 1);
1043 memcpy (room_name, roomptr, room_name_len);
1044 room_name[room_name_len] = '\0';
1045 new_entry = GNUNET_malloc (sizeof (struct ChatClient));
1046 memset (new_entry, 0, sizeof (struct ChatClient));
1048 new_entry->client = NULL;
1049 new_entry->room = room_name;
1050 new_entry->public_key = p2p_jnmsg->public_key;
1051 new_entry->meta_len = meta_len;
1054 new_entry->member_info = GNUNET_malloc (meta_len);
1055 memcpy (new_entry->member_info, &roomptr[room_name_len], meta_len);
1058 new_entry->member_info = NULL;
1059 new_entry->msg_options = ntohl (p2p_jnmsg->msg_options);
1060 new_entry->next = client_list_head;
1061 client_list_head = new_entry;
1062 #if DEBUG_CHAT_SERVICE
1063 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1064 "Notifying local room members that we have a new client\n");
1066 jnmsg = GNUNET_malloc (sizeof (struct JoinNotificationMessage) + meta_len);
1067 jnmsg->header.type = htons (GNUNET_MESSAGE_TYPE_CHAT_JOIN_NOTIFICATION);
1068 jnmsg->header.size =
1069 htons (sizeof (struct JoinNotificationMessage) + meta_len);
1070 jnmsg->msg_options = p2p_jnmsg->msg_options;
1071 jnmsg->public_key = new_entry->public_key;
1072 memcpy (&jnmsg[1], &roomptr[room_name_len], meta_len);
1073 entry = client_list_head;
1074 while (NULL != entry)
1076 if ((0 == strcmp (room_name, entry->room)) && (NULL != entry->client))
1078 GNUNET_SERVER_notification_context_unicast (nc, entry->client,
1079 &jnmsg->header, GNUNET_NO);
1081 entry = entry->next;
1083 #if DEBUG_CHAT_SERVICE
1084 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1085 "Broadcasting join notification to neighbour peers\n");
1087 GNUNET_CONTAINER_multihashmap_iterate (connected_peers,
1088 &send_join_noficiation, new_entry);
1089 GNUNET_free (jnmsg);
1095 * Handle P2P leave notification.
1097 * @param cls closure, always NULL
1098 * @param other the other peer involved
1099 * @param message the actual message
1100 * @param atsi performance information
1101 * @param atsi_count number of entries in atsi
1102 * @return GNUNET_OK to keep the connection open,
1103 * GNUNET_SYSERR to close it (signal serious error)
1106 handle_p2p_leave_notification (void *cls,
1107 const struct GNUNET_PeerIdentity *other,
1108 const struct GNUNET_MessageHeader *message,
1109 const struct GNUNET_ATS_Information *atsi,
1110 unsigned int atsi_count)
1112 const struct P2PLeaveNotificationMessage *p2p_lnmsg;
1114 struct ChatClient *pos;
1115 struct ChatClient *prev;
1116 struct ChatClient *entry;
1117 struct LeaveNotificationMessage lnmsg;
1119 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Got P2P leave notification\n");
1120 p2p_lnmsg = (const struct P2PLeaveNotificationMessage *) message;
1121 GNUNET_CRYPTO_hash (&p2p_lnmsg->user,
1122 sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
1124 pos = client_list_head;
1128 if (0 == memcmp (&pos->id, &id, sizeof (GNUNET_HashCode)))
1135 #if DEBUG_CHAT_SERVICE
1136 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1137 "No such client. There is nothing to do\n");
1142 client_list_head = pos->next;
1144 prev->next = pos->next;
1145 #if DEBUG_CHAT_SERVICE
1146 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1147 "Notifying local room members that the client has gone away\n");
1149 lnmsg.header.size = htons (sizeof (struct LeaveNotificationMessage));
1150 lnmsg.header.type = htons (GNUNET_MESSAGE_TYPE_CHAT_LEAVE_NOTIFICATION);
1151 lnmsg.reserved = htonl (0);
1152 lnmsg.user = pos->public_key;
1153 entry = client_list_head;
1154 while (NULL != entry)
1156 if (0 == strcmp (pos->room, entry->room) && (NULL != entry->client))
1158 GNUNET_SERVER_notification_context_unicast (nc, entry->client,
1159 &lnmsg.header, GNUNET_NO);
1161 entry = entry->next;
1163 #if DEBUG_CHAT_SERVICE
1164 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1165 "Broadcasting leave notification to neighbour peers\n");
1167 GNUNET_CONTAINER_multihashmap_iterate (connected_peers,
1168 &send_leave_noficiation, pos);
1169 GNUNET_free (pos->room);
1170 GNUNET_free_non_null (pos->member_info);
1177 * Handle P2P message notification.
1179 * @param cls closure, always NULL
1180 * @param other the other peer involved
1181 * @param message the actual message
1182 * @param atsi performance information
1183 * @param atsi_count number of entries in atsi
1184 * @return GNUNET_OK to keep the connection open,
1185 * GNUNET_SYSERR to close it (signal serious error)
1188 handle_p2p_message_notification (void *cls,
1189 const struct GNUNET_PeerIdentity *other,
1190 const struct GNUNET_MessageHeader *message,
1191 const struct GNUNET_ATS_Information *atsi,
1192 unsigned int atsi_count)
1194 const struct P2PReceiveNotificationMessage *p2p_rnmsg;
1195 struct P2PReceiveNotificationMessage *my_p2p_rnmsg;
1196 struct ReceiveNotificationMessage *rnmsg;
1197 struct ChatClient *sender;
1198 struct ChatClient *pos;
1199 static GNUNET_HashCode all_zeros;
1203 uint16_t room_name_len;
1204 char *room_name = NULL;
1207 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Got P2P message notification\n");
1208 if (ntohs (message->size) <= sizeof (struct P2PReceiveNotificationMessage))
1210 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Malformed message: wrong size\n");
1211 GNUNET_break_op (0);
1212 return GNUNET_SYSERR;
1214 p2p_rnmsg = (const struct P2PReceiveNotificationMessage *) message;
1216 ntohs (p2p_rnmsg->header.size) -
1217 sizeof (struct P2PReceiveNotificationMessage);
1219 is_anon = (0 != (ntohl (p2p_rnmsg->msg_options) & GNUNET_CHAT_MSG_ANONYMOUS));
1222 room_name_len = ntohs (p2p_rnmsg->room_name_len);
1223 if (msg_len <= room_name_len)
1225 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1226 "Malformed message: wrong length of the room name\n");
1227 GNUNET_break_op (0);
1228 return GNUNET_SYSERR;
1230 msg_len -= room_name_len;
1231 if (lookup_anonymous_message (p2p_rnmsg))
1233 #if DEBUG_CHAT_SERVICE
1234 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1235 "This anonymous message has already been handled.");
1239 remember_anonymous_message (p2p_rnmsg);
1240 room_name = GNUNET_malloc (room_name_len + 1);
1241 memcpy (room_name, (char *) &p2p_rnmsg[1], room_name_len);
1242 room_name[room_name_len] = '\0';
1243 text = (char *) &p2p_rnmsg[1] + room_name_len;
1247 sender = client_list_head;
1248 while ((NULL != sender) &&
1250 memcmp (&sender->id, &p2p_rnmsg->sender, sizeof (GNUNET_HashCode))))
1251 sender = sender->next;
1254 /* not an error since the sender may have left before we got the
1256 #if DEBUG_CHAT_SERVICE
1257 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1258 "Unknown source. Rejecting the message\n");
1262 if (sender->msg_sequence_number >= ntohl (p2p_rnmsg->sequence_number))
1264 #if DEBUG_CHAT_SERVICE
1265 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1266 "This message has already been handled."
1267 " Sequence numbers (msg/sender): %u/%u\n",
1268 ntohl (p2p_rnmsg->sequence_number),
1269 sender->msg_sequence_number);
1273 sender->msg_sequence_number = ntohl (p2p_rnmsg->sequence_number);
1274 room_name = sender->room;
1275 text = (char *) &p2p_rnmsg[1];
1278 #if DEBUG_CHAT_SERVICE
1279 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1280 "Sending message to local room members\n");
1282 rnmsg = GNUNET_malloc (sizeof (struct ReceiveNotificationMessage) + msg_len);
1283 rnmsg->header.size =
1284 htons (sizeof (struct ReceiveNotificationMessage) + msg_len);
1285 rnmsg->header.type = htons (GNUNET_MESSAGE_TYPE_CHAT_MESSAGE_NOTIFICATION);
1286 rnmsg->msg_options = p2p_rnmsg->msg_options;
1287 rnmsg->sequence_number = p2p_rnmsg->sequence_number;
1288 rnmsg->reserved = htonl (0);
1289 rnmsg->timestamp = p2p_rnmsg->timestamp;
1291 (0 != memcmp (&all_zeros, &p2p_rnmsg->target, sizeof (GNUNET_HashCode)));
1293 memcpy (&rnmsg->encrypted_key, &p2p_rnmsg->encrypted_key,
1294 sizeof (struct GNUNET_CRYPTO_RsaEncryptedData));
1295 rnmsg->sender = p2p_rnmsg->sender;
1296 memcpy (&rnmsg[1], text, msg_len);
1297 pos = client_list_head;
1300 if ((0 == strcmp (room_name, pos->room)) && (NULL != pos->client))
1304 memcmp (&p2p_rnmsg->target, &pos->id, sizeof (GNUNET_HashCode)))) &&
1305 (0 == (ntohl (p2p_rnmsg->msg_options) & (~pos->msg_options))))
1307 GNUNET_SERVER_notification_context_unicast (nc, pos->client,
1308 &rnmsg->header, GNUNET_NO);
1314 GNUNET_free (room_name);
1315 #if DEBUG_CHAT_SERVICE
1316 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1317 "Broadcasting message notification to neighbour peers\n");
1319 my_p2p_rnmsg = GNUNET_memdup (p2p_rnmsg, ntohs (p2p_rnmsg->header.size));
1320 GNUNET_CONTAINER_multihashmap_iterate (connected_peers,
1321 &send_message_noficiation,
1323 GNUNET_free (rnmsg);
1329 * Handle P2P sync request.
1331 * @param cls closure, always NULL
1332 * @param other the other peer involved
1333 * @param message the actual message
1334 * @param atsi performance information
1335 * @param atsi_count number of entries in atsi
1336 * @return GNUNET_OK to keep the connection open,
1337 * GNUNET_SYSERR to close it (signal serious error)
1340 handle_p2p_sync_request (void *cls, const struct GNUNET_PeerIdentity *other,
1341 const struct GNUNET_MessageHeader *message,
1342 const struct GNUNET_ATS_Information *atsi,
1343 unsigned int atsi_count)
1345 struct ChatClient *entry;
1346 struct GNUNET_CORE_TransmitHandle *th;
1349 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Got P2P sync request\n");
1350 #if DEBUG_CHAT_SERVICE
1351 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1352 "Notifying the requester of all known clients\n");
1354 entry = client_list_head;
1355 while (NULL != entry)
1358 sizeof (struct P2PJoinNotificationMessage) + strlen (entry->room) +
1360 th = GNUNET_CORE_notify_transmit_ready (core, GNUNET_NO, 1,
1361 MAX_TRANSMIT_DELAY, other, msg_size,
1362 &transmit_join_notification_to_peer,
1364 GNUNET_assert (NULL != th);
1365 entry = entry->next;
1372 * Handle P2P confirmation receipt.
1374 * @param cls closure, always NULL
1375 * @param other the other peer involved
1376 * @param message the actual message
1377 * @param atsi performance information
1378 * @param atsi_count number of entries in atsi
1379 * @return GNUNET_OK to keep the connection open,
1380 * GNUNET_SYSERR to close it (signal serious error)
1383 handle_p2p_confirmation_receipt (void *cls,
1384 const struct GNUNET_PeerIdentity *other,
1385 const struct GNUNET_MessageHeader *message,
1386 const struct GNUNET_ATS_Information *atsi,
1387 unsigned int atsi_count)
1389 const struct P2PConfirmationReceiptMessage *p2p_crmsg;
1390 struct P2PConfirmationReceiptMessage *my_p2p_crmsg;
1391 struct ConfirmationReceiptMessage *crmsg;
1392 struct ChatClient *target;
1393 struct ChatClient *author;
1395 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Got P2P confirmation receipt\n");
1396 p2p_crmsg = (const struct P2PConfirmationReceiptMessage *) message;
1397 target = client_list_head;
1398 while ((NULL != target) &&
1400 memcmp (&target->id, &p2p_crmsg->target, sizeof (GNUNET_HashCode))))
1401 target = target->next;
1404 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1405 "Unknown source of the receipt. Rejecting the message\n");
1406 GNUNET_break_op (0);
1407 return GNUNET_SYSERR;
1409 if (target->rcpt_sequence_number >= ntohl (p2p_crmsg->sequence_number))
1411 #if DEBUG_CHAT_SERVICE
1412 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1413 "This receipt has already been handled."
1414 " Sequence numbers (msg/sender): %u/%u\n",
1415 ntohl (p2p_crmsg->sequence_number),
1416 target->rcpt_sequence_number);
1420 target->rcpt_sequence_number = ntohl (p2p_crmsg->sequence_number);
1421 author = client_list_head;
1422 while ((NULL != author) &&
1424 memcmp (&author->id, &p2p_crmsg->author, sizeof (GNUNET_HashCode))))
1425 author = author->next;
1428 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1429 "Unknown addressee. Rejecting the receipt\n");
1430 GNUNET_break_op (0);
1431 return GNUNET_SYSERR;
1434 if (NULL == author->client)
1436 #if DEBUG_CHAT_SERVICE
1437 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1438 "The author of the original message is not a local client."
1439 " Broadcasting receipt to neighbour peers\n");
1442 GNUNET_memdup (p2p_crmsg,
1443 sizeof (struct P2PConfirmationReceiptMessage));
1444 GNUNET_CONTAINER_multihashmap_iterate (connected_peers,
1445 &send_confirmation_receipt,
1447 GNUNET_free (my_p2p_crmsg);
1451 #if DEBUG_CHAT_SERVICE
1452 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1453 "The author of the original message is a local client."
1454 " Verifying signature of the receipt\n");
1456 crmsg = GNUNET_malloc (sizeof (struct ConfirmationReceiptMessage));
1457 crmsg->header.size = htons (sizeof (struct ConfirmationReceiptMessage));
1458 crmsg->header.type =
1459 htons (GNUNET_MESSAGE_TYPE_CHAT_CONFIRMATION_NOTIFICATION);
1460 crmsg->signature = p2p_crmsg->signature;
1461 crmsg->purpose = p2p_crmsg->purpose;
1462 crmsg->sequence_number = p2p_crmsg->msg_sequence_number;
1463 crmsg->reserved2 = 0;
1464 crmsg->timestamp = p2p_crmsg->timestamp;
1465 crmsg->target = p2p_crmsg->target;
1466 crmsg->author = p2p_crmsg->author;
1467 crmsg->content = p2p_crmsg->content;
1469 GNUNET_CRYPTO_rsa_verify (GNUNET_SIGNATURE_PURPOSE_CHAT_RECEIPT,
1470 &crmsg->purpose, &crmsg->signature,
1471 &target->public_key))
1473 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1474 "Invalid signature of the receipt\n");
1475 GNUNET_break_op (0);
1476 return GNUNET_SYSERR;
1478 #if DEBUG_CHAT_SERVICE
1479 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1480 "The author of the original message is a local client."
1481 " Sending receipt to the client\n");
1483 GNUNET_SERVER_notification_context_unicast (nc, author->client,
1484 &crmsg->header, GNUNET_NO);
1485 GNUNET_free (crmsg);
1492 * Transmit a sync request to the peer.
1494 * @param cls closure, NULL
1495 * @param size number of bytes available in buf
1496 * @param buf where the callee should write the message
1497 * @return number of bytes written to buf
1500 transmit_sync_request_to_peer (void *cls, size_t size, void *buf)
1502 struct GNUNET_MessageHeader *m = buf;
1505 #if DEBUG_CHAT_SERVICE
1506 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Transmitting P2P sync request\n");
1508 msg_size = sizeof (struct GNUNET_MessageHeader);
1509 GNUNET_assert (size >= msg_size);
1510 GNUNET_assert (NULL != buf);
1512 m->type = htons (GNUNET_MESSAGE_TYPE_CHAT_P2P_SYNC_REQUEST);
1513 m->size = htons (msg_size);
1519 * Method called whenever a peer connects.
1521 * @param cls closure
1522 * @param peer peer identity this notification is about
1523 * @param atsi performance data
1524 * @param atsi_count number of entries in atsi
1527 peer_connect_handler (void *cls, const struct GNUNET_PeerIdentity *peer,
1528 const struct GNUNET_ATS_Information *atsi,
1529 unsigned int atsi_count)
1531 struct ConnectedPeer *cp;
1532 struct GNUNET_CORE_TransmitHandle *th;
1534 if (0 == memcmp (peer, me, sizeof (struct GNUNET_PeerIdentity)))
1536 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Peer connected: %s\n",
1538 th = GNUNET_CORE_notify_transmit_ready (core, GNUNET_YES, 1,
1539 MAX_TRANSMIT_DELAY, peer,
1540 sizeof (struct GNUNET_MessageHeader),
1541 &transmit_sync_request_to_peer, NULL);
1542 GNUNET_assert (NULL != th);
1543 cp = GNUNET_CONTAINER_multihashmap_get (connected_peers, &peer->hashPubKey);
1549 cp = GNUNET_malloc (sizeof (struct ConnectedPeer));
1550 cp->pid = GNUNET_PEER_intern (peer);
1551 GNUNET_break (GNUNET_OK ==
1552 GNUNET_CONTAINER_multihashmap_put (connected_peers,
1553 &peer->hashPubKey, cp,
1554 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
1559 * Iterator to free peer entries.
1561 * @param cls closure, unused
1562 * @param key current key code
1563 * @param value value in the hash map (peer entry)
1564 * @return GNUNET_YES (we should continue to iterate)
1567 clean_peer (void *cls, const GNUNET_HashCode * key, void *value)
1569 struct ConnectedPeer *cp;
1570 const struct GNUNET_PeerIdentity *peer =
1571 (const struct GNUNET_PeerIdentity *) key;
1573 cp = GNUNET_CONTAINER_multihashmap_get (connected_peers, &peer->hashPubKey);
1576 GNUNET_break (GNUNET_YES ==
1577 GNUNET_CONTAINER_multihashmap_remove (connected_peers,
1578 &peer->hashPubKey, cp));
1579 GNUNET_PEER_change_rc (cp->pid, -1);
1586 * Method called whenever a peer disconnects.
1588 * @param cls closure, not used
1589 * @param peer peer identity this notification is about
1592 peer_disconnect_handler (void *cls, const struct GNUNET_PeerIdentity *peer)
1595 if (0 == memcmp (peer, me, sizeof (struct GNUNET_PeerIdentity)))
1597 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Peer disconnected: %s\n",
1599 clean_peer (NULL, (const GNUNET_HashCode *) peer, NULL);
1604 * Task run during shutdown.
1610 cleanup_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1612 struct AnonymousMessage *next_msg;
1613 struct ChatClient *next_client;
1615 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Cleaning up\n");
1618 GNUNET_CORE_disconnect (core);
1623 GNUNET_SERVER_notification_context_destroy (nc);
1626 while (NULL != client_list_head)
1628 next_client = client_list_head->next;
1629 GNUNET_free (client_list_head->room);
1630 GNUNET_free_non_null (client_list_head->member_info);
1631 GNUNET_free (client_list_head);
1632 client_list_head = next_client;
1634 while (NULL != anonymous_list_head)
1636 next_msg = anonymous_list_head->next;
1637 GNUNET_free (anonymous_list_head);
1638 anonymous_list_head = next_msg;
1640 GNUNET_CONTAINER_multihashmap_iterate (connected_peers, &clean_peer, NULL);
1641 GNUNET_CONTAINER_multihashmap_destroy (connected_peers);
1642 connected_peers = NULL;
1647 * To be called on core init/fail.
1649 * @param cls closure, NULL
1650 * @param server handle to the server for this service
1651 * @param my_identity the public identity of this peer
1654 core_init (void *cls, struct GNUNET_CORE_Handle *server,
1655 const struct GNUNET_PeerIdentity *my_identity)
1657 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Core initialized\n");
1663 * Process chat requests.
1665 * @param cls closure, NULL
1666 * @param server the initialized server
1667 * @param c configuration to use
1670 run (void *cls, struct GNUNET_SERVER_Handle *server,
1671 const struct GNUNET_CONFIGURATION_Handle *c)
1673 static const struct GNUNET_SERVER_MessageHandler handlers[] = {
1674 {&handle_join_request, NULL,
1675 GNUNET_MESSAGE_TYPE_CHAT_JOIN_REQUEST, 0},
1676 {&handle_transmit_request, NULL,
1677 GNUNET_MESSAGE_TYPE_CHAT_TRANSMIT_REQUEST, 0},
1678 {&handle_acknowledge_request, NULL,
1679 GNUNET_MESSAGE_TYPE_CHAT_CONFIRMATION_RECEIPT,
1680 sizeof (struct ConfirmationReceiptMessage)},
1683 static const struct GNUNET_CORE_MessageHandler p2p_handlers[] = {
1684 {&handle_p2p_join_notification,
1685 GNUNET_MESSAGE_TYPE_CHAT_P2P_JOIN_NOTIFICATION, 0},
1686 {&handle_p2p_leave_notification,
1687 GNUNET_MESSAGE_TYPE_CHAT_P2P_LEAVE_NOTIFICATION,
1688 sizeof (struct P2PLeaveNotificationMessage)},
1689 {&handle_p2p_message_notification,
1690 GNUNET_MESSAGE_TYPE_CHAT_P2P_MESSAGE_NOTIFICATION, 0},
1691 {&handle_p2p_sync_request,
1692 GNUNET_MESSAGE_TYPE_CHAT_P2P_SYNC_REQUEST,
1693 sizeof (struct GNUNET_MessageHeader)},
1694 {&handle_p2p_confirmation_receipt,
1695 GNUNET_MESSAGE_TYPE_CHAT_P2P_CONFIRMATION_RECEIPT,
1696 sizeof (struct P2PConfirmationReceiptMessage)},
1700 GNUNET_log_setup ("gnunet-service-chat",
1701 #if DEBUG_CHAT_SERVICE
1708 nc = GNUNET_SERVER_notification_context_create (server, 16);
1710 GNUNET_CONTAINER_multihashmap_create (EXPECTED_NEIGHBOUR_COUNT);
1711 GNUNET_SERVER_add_handlers (server, handlers);
1713 GNUNET_CORE_connect (cfg, QUEUE_SIZE, NULL, &core_init,
1714 &peer_connect_handler, &peer_disconnect_handler,
1715 NULL, GNUNET_NO, NULL, GNUNET_NO, p2p_handlers);
1716 GNUNET_SERVER_disconnect_notify (server, &handle_client_disconnect, NULL);
1717 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &cleanup_task,
1723 * The main function for the chat service.
1725 * @param argc number of arguments from the command line
1726 * @param argv command line arguments
1727 * @return 0 ok, 1 on error
1730 main (int argc, char *const *argv)
1732 return (GNUNET_OK ==
1733 GNUNET_SERVICE_run (argc, argv, "chat", GNUNET_SERVICE_OPTION_NONE,
1734 &run, NULL)) ? 0 : 1;
1737 /* end of gnunet-service-chat.c */