fix
[oweals/gnunet.git] / src / chat / gnunet-service-chat.c
1 /*
2      This file is part of GNUnet.
3      (C) 2009, 2011 Christian Grothoff (and other contributing authors)
4
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.
9
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.
14
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.
19 */
20
21 /**
22  * @file chat/gnunet-service-chat.c
23  * @brief service providing chat functionality
24  * @author Christian Grothoff
25  * @author Vitaly Minko
26  */
27
28 #include "platform.h"
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"
34 #include "chat.h"
35
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 QUEUE_SIZE 16
40 #define MAX_ANONYMOUS_MSG_LIST_LENGTH 16
41
42
43 /**
44  * Linked list of our current clients.
45  */
46 struct ChatClient
47 {
48   struct ChatClient *next;
49
50   /**
51    * Handle for a chat client (NULL for external clients).
52    */
53   struct GNUNET_SERVER_Client *client;
54
55   /**
56    * Public key of the client.
57    */
58   struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded public_key;
59
60   /**
61    * Name of the room which the client is in.
62    */
63   char *room;
64
65   /**
66    * Serialized metadata of the client.
67    */
68   char *member_info;
69
70   /**
71    * Hash of the public key (for convenience).
72    */
73   GNUNET_HashCode id;
74
75   /**
76    * Options which the client is willing to receive.
77    */
78   uint32_t msg_options;
79
80   /**
81    * Length of serialized metadata in member_info.
82    */
83   uint16_t meta_len;
84
85   /**
86    * Sequence number of the last message sent by the client.
87    */
88   uint32_t msg_sequence_number;
89
90   /**
91    * Sequence number of the last receipt sent by the client.
92    * Used to discard already processed receipts.
93    */
94   uint32_t rcpt_sequence_number;
95
96 };
97
98 /**
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.
102  */
103 struct ConnectedPeer
104 {
105   /**
106    * The peer's identity.
107    */
108   GNUNET_PEER_Id pid;
109 };
110
111 /**
112  * Linked list of recent anonymous messages.
113  */
114 struct AnonymousMessage
115 {
116   struct AnonymousMessage *next;
117
118   /**
119    * Hash of the message.
120    */
121   GNUNET_HashCode hash;
122
123 };
124
125
126 /**
127  * Handle to the core service (NULL until we've connected to it).
128  */
129 static struct GNUNET_CORE_Handle *core;
130
131 /**
132  * Our configuration.
133  */
134 static const struct GNUNET_CONFIGURATION_Handle *cfg;
135
136 /**
137  * The identity of this host.
138  */
139 static const struct GNUNET_PeerIdentity *me;
140
141 /**
142  * Head of the list of current clients.
143  */
144 static struct ChatClient *client_list_head = NULL;
145
146 /**
147  * Notification context containing all connected clients.
148  */
149 struct GNUNET_SERVER_NotificationContext *nc = NULL;
150
151 /**
152  * Head of the list of recent anonymous messages.
153  */
154 static struct AnonymousMessage *anonymous_list_head = NULL;
155
156 /**
157  * Map of peer identifiers to "struct ConnectedPeer" (for that peer).
158  */
159 static struct GNUNET_CONTAINER_MultiHashMap *connected_peers;
160
161
162 static void
163 remember_anonymous_message (const struct P2PReceiveNotificationMessage
164                             *p2p_rnmsg)
165 {
166   static GNUNET_HashCode hash;
167   struct AnonymousMessage *anon_msg;
168   struct AnonymousMessage *prev;
169   int anon_list_len;
170
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;
176   anon_list_len = 1;
177   prev = NULL;
178   while ((NULL != anon_msg->next))
179   {
180     prev = anon_msg;
181     anon_msg = anon_msg->next;
182     anon_list_len++;
183   }
184   if (anon_list_len == MAX_ANONYMOUS_MSG_LIST_LENGTH)
185   {
186     GNUNET_free (anon_msg);
187     if (NULL != prev)
188       prev->next = NULL;
189   }
190 }
191
192
193 static int
194 lookup_anonymous_message (const struct P2PReceiveNotificationMessage *p2p_rnmsg)
195 {
196   static GNUNET_HashCode hash;
197   struct AnonymousMessage *anon_msg;
198
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);
205 }
206
207
208 /**
209  * Transmit a message notification to the peer.
210  *
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
215  */
216 static size_t
217 transmit_message_notification_to_peer (void *cls, size_t size, void *buf)
218 {
219   struct P2PReceiveNotificationMessage *my_msg = cls;
220   struct P2PReceiveNotificationMessage *m = buf;
221   size_t msg_size;
222
223 #if DEBUG_CHAT_SERVICE
224   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
225               "Transmitting P2P message notification\n");
226 #endif
227   if (buf == NULL)
228   {
229     /* client disconnected */
230 #if DEBUG_CHAT_SERVICE
231     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
232                 "Buffer is NULL, dropping the message\n");
233 #endif
234     return 0;
235   }
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);
240   return msg_size;
241 }
242
243
244 /**
245  * Ask to send a message notification to the peer.
246  */
247 static int
248 send_message_noficiation (void *cls, const GNUNET_HashCode * key, void *value)
249 {
250   struct P2PReceiveNotificationMessage *msg = cls;
251   struct ConnectedPeer *cp = value;
252   struct GNUNET_PeerIdentity pid;
253   struct P2PReceiveNotificationMessage *my_msg;
254
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",
258               GNUNET_i2s (&pid));
259 #endif
260   my_msg = GNUNET_memdup (msg, ntohs (msg->header.size));
261   if (NULL ==
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,
265                                          my_msg))
266     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
267                 _("Failed to queue a message notification\n"));
268   return GNUNET_YES;
269 }
270
271
272 /**
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.
275  *
276  * @param cls closure, NULL
277  * @param client identification of the client
278  * @param message the actual message
279  */
280 static void
281 handle_transmit_request (void *cls, struct GNUNET_SERVER_Client *client,
282                          const struct GNUNET_MessageHeader *message)
283 {
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];
292   const char *room;
293   size_t room_len;
294   int msg_len;
295   int is_priv;
296   int is_anon;
297
298   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Client sent a chat message\n");
299   if (ntohs (message->size) <= sizeof (struct TransmitRequestMessage))
300   {
301     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Malformed message: wrong size\n");
302     GNUNET_break (0);
303     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
304     return;
305   }
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));
309   if (is_priv)
310   {
311 #if DEBUG_CHAT_SERVICE
312     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Encrypting the message text\n");
313 #endif
314     GNUNET_CRYPTO_aes_create_session_key (&key);
315     msg_len =
316         GNUNET_CRYPTO_aes_encrypt (&trmsg[1], msg_len, &key,
317                                    (const struct
318                                     GNUNET_CRYPTO_AesInitializationVector *)
319                                    INITVALUE, encrypted_msg);
320     if (-1 == msg_len)
321     {
322       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
323                   "Could not encrypt the message text\n");
324       GNUNET_break (0);
325       GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
326       return;
327     }
328   }
329   rnmsg = GNUNET_malloc (sizeof (struct ReceiveNotificationMessage) + msg_len);
330   rnmsg->header.size =
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))
337     pos = pos->next;
338   if (NULL == pos)
339   {
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");
343     GNUNET_break (0);
344     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
345     GNUNET_free (rnmsg);
346     return;
347   }
348   room = pos->room;
349   pos->msg_sequence_number = ntohl (trmsg->sequence_number);
350   is_anon = (0 != (ntohl (trmsg->msg_options) & GNUNET_CHAT_MSG_ANONYMOUS));
351   if (is_anon)
352   {
353     memset (&rnmsg->sender, 0, sizeof (GNUNET_HashCode));
354     rnmsg->sequence_number = 0;
355   }
356   else
357   {
358     rnmsg->sender = pos->id;
359     rnmsg->sequence_number = trmsg->sequence_number;
360   }
361   if (is_priv)
362   {
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));
367 #endif
368     if (0 == memcmp (&all_zeros, &trmsg->target, sizeof (GNUNET_HashCode)))
369     {
370       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
371                   "Malformed message: private, but no target\n");
372       GNUNET_break (0);
373       GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
374       GNUNET_free (rnmsg);
375       return;
376     }
377     memcpy (&rnmsg[1], encrypted_msg, msg_len);
378     target = client_list_head;
379     while ((NULL != target) &&
380            (0 !=
381             memcmp (&target->id, &trmsg->target, sizeof (GNUNET_HashCode))))
382       target = target->next;
383     if (NULL == target)
384     {
385       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
386                   "Unknown target of the private message\n");
387       GNUNET_break (0);
388       GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
389       GNUNET_free (rnmsg);
390       return;
391     }
392     if (GNUNET_SYSERR ==
393         GNUNET_CRYPTO_rsa_encrypt (&key,
394                                    sizeof (struct GNUNET_CRYPTO_AesSessionKey),
395                                    &target->public_key, &rnmsg->encrypted_key))
396     {
397       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
398                   "Could not encrypt the session key\n");
399       GNUNET_break (0);
400       GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
401       GNUNET_free (rnmsg);
402       return;
403     }
404   }
405   else
406   {
407     memcpy (&rnmsg[1], &trmsg[1], msg_len);
408   }
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");
413 #endif
414   while (NULL != pos)
415   {
416     if ((0 == strcmp (room, pos->room)) && (NULL != pos->client) &&
417         (pos->client != client))
418     {
419       if (((!is_priv) ||
420            (0 == memcmp (&trmsg->target, &pos->id, sizeof (GNUNET_HashCode))))
421           && (0 == (ntohl (trmsg->msg_options) & (~pos->msg_options))))
422       {
423         GNUNET_SERVER_notification_context_unicast (nc, pos->client,
424                                                     &rnmsg->header, GNUNET_NO);
425       }
426     }
427     pos = pos->next;
428   }
429 #if DEBUG_CHAT_SERVICE
430   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
431               "Broadcasting message to neighbour peers\n");
432 #endif
433   if (is_anon)
434   {
435     room_len = strlen (room);
436     p2p_rnmsg =
437         GNUNET_malloc (sizeof (struct P2PReceiveNotificationMessage) + msg_len +
438                        room_len);
439     p2p_rnmsg->header.size =
440         htons (sizeof (struct P2PReceiveNotificationMessage) + msg_len +
441                room_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);
445   }
446   else
447   {
448     p2p_rnmsg =
449         GNUNET_malloc (sizeof (struct P2PReceiveNotificationMessage) + msg_len);
450     p2p_rnmsg->header.size =
451         htons (sizeof (struct P2PReceiveNotificationMessage) + msg_len);
452     if (is_priv)
453     {
454       memcpy (&p2p_rnmsg[1], encrypted_msg, msg_len);
455       memcpy (&p2p_rnmsg->encrypted_key, &rnmsg->encrypted_key,
456               sizeof (struct GNUNET_CRYPTO_RsaEncryptedData));
457     }
458     else
459       memcpy (&p2p_rnmsg[1], &trmsg[1], msg_len);
460   }
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;
469   if (is_anon)
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);
475   GNUNET_free (rnmsg);
476 }
477
478
479 /**
480  * Transmit a join notification to the peer.
481  *
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
486  */
487 static size_t
488 transmit_join_notification_to_peer (void *cls, size_t size, void *buf)
489 {
490   struct ChatClient *entry = cls;
491   struct P2PJoinNotificationMessage *m = buf;
492   size_t room_len;
493   size_t meta_len;
494   size_t msg_size;
495   char *roomptr;
496
497 #if DEBUG_CHAT_SERVICE
498   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Transmitting P2P join notification\n");
499 #endif
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);
505   m = 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);
515   if (meta_len > 0)
516     memcpy (&roomptr[room_len], entry->member_info, meta_len);
517   return msg_size;
518 }
519
520
521 /**
522  * Ask to send a join notification to the peer.
523  */
524 static int
525 send_join_noficiation (void *cls, const GNUNET_HashCode * key, void *value)
526 {
527   struct ChatClient *entry = cls;
528   struct ConnectedPeer *cp = value;
529   struct GNUNET_PeerIdentity pid;
530   size_t msg_size;
531
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",
535               GNUNET_i2s (&pid));
536 #endif
537   msg_size =
538       sizeof (struct P2PJoinNotificationMessage) + strlen (entry->room) +
539       entry->meta_len;
540   if (NULL ==
541       GNUNET_CORE_notify_transmit_ready (core, GNUNET_NO, 1, MAX_TRANSMIT_DELAY,
542                                          &pid, msg_size,
543                                          &transmit_join_notification_to_peer,
544                                          entry))
545     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
546                 _("Failed to queue a join notification\n"));
547   return GNUNET_YES;
548 }
549
550
551 /**
552  * A client asked for entering a chat room.  Add the new member to the list of
553  * clients and notify remaining room members.
554  *
555  * @param cls closure, NULL
556  * @param client identification of the client
557  * @param message the actual message
558  */
559 static void
560 handle_join_request (void *cls, struct GNUNET_SERVER_Client *client,
561                      const struct GNUNET_MessageHeader *message)
562 {
563   const struct JoinRequestMessage *jrmsg;
564   char *room_name;
565   const char *roomptr;
566   uint16_t header_size;
567   uint16_t meta_len;
568   uint16_t room_name_len;
569   struct ChatClient *new_entry;
570   struct ChatClient *entry;
571   struct JoinNotificationMessage *jnmsg;
572   struct JoinNotificationMessage *entry_jnmsg;
573
574   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Client sent a join request\n");
575   if (ntohs (message->size) <= sizeof (struct JoinRequestMessage))
576   {
577     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Malformed message: wrong size\n");
578     GNUNET_break (0);
579     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
580     return;
581   }
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)
586   {
587     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
588                 "Malformed message: wrong length of the room name\n");
589     GNUNET_break (0);
590     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
591     return;
592   }
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;
604   if (meta_len > 0)
605   {
606     new_entry->member_info = GNUNET_malloc (meta_len);
607     memcpy (new_entry->member_info, &roomptr[room_name_len], meta_len);
608   }
609   else
610     new_entry->member_info = NULL;
611   GNUNET_CRYPTO_hash (&new_entry->public_key,
612                       sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
613                       &new_entry->id);
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");
620 #endif
621   jnmsg = GNUNET_malloc (sizeof (struct JoinNotificationMessage) + meta_len);
622   jnmsg->header.type = htons (GNUNET_MESSAGE_TYPE_CHAT_JOIN_NOTIFICATION);
623   jnmsg->header.size =
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)
631   {
632     if (0 == strcmp (room_name, entry->room))
633     {
634       if (NULL != entry->client)
635         GNUNET_SERVER_notification_context_unicast (nc, entry->client,
636                                                     &jnmsg->header, GNUNET_NO);
637       if (entry->client != client)
638       {
639         entry_jnmsg =
640             GNUNET_malloc (sizeof (struct JoinNotificationMessage) +
641                            entry->meta_len);
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,
651                                                     GNUNET_NO);
652         GNUNET_free (entry_jnmsg);
653       }
654     }
655     entry = entry->next;
656   }
657 #if DEBUG_CHAT_SERVICE
658   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
659               "Broadcasting join notification to neighbour peers\n");
660 #endif
661   GNUNET_CONTAINER_multihashmap_iterate (connected_peers,
662                                          &send_join_noficiation, new_entry);
663   GNUNET_SERVER_receive_done (client, GNUNET_OK);
664   GNUNET_free (jnmsg);
665 }
666
667 /**
668  * Transmit a confirmation receipt to the peer.
669  *
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
674  */
675 static size_t
676 transmit_confirmation_receipt_to_peer (void *cls, size_t size, void *buf)
677 {
678   struct P2PConfirmationReceiptMessage *receipt = cls;
679   size_t msg_size;
680
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));
685 #endif
686   if (buf == NULL)
687   {
688     /* client disconnected */
689 #if DEBUG_CHAT_SERVICE
690     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
691                 "Buffer is NULL, dropping the message\n");
692 #endif
693     return 0;
694   }
695   msg_size = sizeof (struct P2PConfirmationReceiptMessage);
696   GNUNET_assert (size >= msg_size);
697   memcpy (buf, receipt, msg_size);
698   GNUNET_free (receipt);
699   return msg_size;
700 }
701
702
703 /**
704  * Ask to send a confirmation receipt to the peer.
705  */
706 static int
707 send_confirmation_receipt (void *cls, const GNUNET_HashCode * key, void *value)
708 {
709   struct P2PConfirmationReceiptMessage *receipt = cls;
710   struct ConnectedPeer *cp = value;
711   struct GNUNET_PeerIdentity pid;
712   struct P2PConfirmationReceiptMessage *my_receipt;
713   size_t msg_size;
714
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",
718               GNUNET_i2s (&pid));
719 #endif
720   msg_size = sizeof (struct P2PConfirmationReceiptMessage);
721   my_receipt =
722       GNUNET_memdup (receipt, sizeof (struct P2PConfirmationReceiptMessage));
723   if (NULL ==
724       GNUNET_CORE_notify_transmit_ready (core, GNUNET_YES, 1,
725                                          MAX_TRANSMIT_DELAY, &pid, msg_size,
726                                          &transmit_confirmation_receipt_to_peer,
727                                          my_receipt))
728     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
729                 _("Failed to queue a confirmation receipt\n"));
730   return GNUNET_YES;
731 }
732
733
734 /**
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.
738  *
739  * @param cls closure, NULL
740  * @param client identification of the client
741  * @param message the actual message
742  */
743 static void
744 handle_acknowledge_request (void *cls, struct GNUNET_SERVER_Client *client,
745                             const struct GNUNET_MessageHeader *message)
746 {
747   const struct ConfirmationReceiptMessage *receipt;
748   struct ConfirmationReceiptMessage *crmsg;
749   struct P2PConfirmationReceiptMessage *p2p_crmsg;
750   struct ChatClient *target;
751   struct ChatClient *author;
752
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) &&
757          (0 !=
758           memcmp (&receipt->author, &author->id, sizeof (GNUNET_HashCode))))
759     author = author->next;
760   if (NULL == author)
761   {
762     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
763                 "Unknown author of the original message\n");
764     GNUNET_break (0);
765     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
766     return;
767   }
768   target = client_list_head;
769   while ((NULL != target) &&
770          (0 !=
771           memcmp (&receipt->target, &target->id, sizeof (GNUNET_HashCode))))
772     target = target->next;
773   if (NULL == target)
774   {
775     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
776                 "Unknown target of the confirmation receipt\n");
777     GNUNET_break (0);
778     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
779     return;
780   }
781   if (NULL == author->client)
782   {
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);
788 #endif
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,
805                                            p2p_crmsg);
806     GNUNET_free (p2p_crmsg);
807   }
808   else
809   {
810 #if DEBUG_CHAT_SERVICE
811     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
812                 "Verifying signature of the receipt\n");
813 #endif
814     if (GNUNET_OK !=
815         GNUNET_CRYPTO_rsa_verify (GNUNET_SIGNATURE_PURPOSE_CHAT_RECEIPT,
816                                   &receipt->purpose, &receipt->signature,
817                                   &target->public_key))
818     {
819       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
820                   "Invalid signature of the receipt\n");
821       GNUNET_break (0);
822       GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
823       return;
824     }
825 #if DEBUG_CHAT_SERVICE
826     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
827                 "Sending receipt to the client which sent the original message\n");
828 #endif
829     crmsg = GNUNET_memdup (receipt, sizeof (struct ConfirmationReceiptMessage));
830     crmsg->header.type =
831         htons (GNUNET_MESSAGE_TYPE_CHAT_CONFIRMATION_NOTIFICATION);
832     GNUNET_SERVER_notification_context_unicast (nc, author->client,
833                                                 &crmsg->header, GNUNET_NO);
834     GNUNET_free (crmsg);
835   }
836   GNUNET_SERVER_receive_done (client, GNUNET_OK);
837 }
838
839
840 /**
841  * Transmit a leave notification to the peer.
842  *
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
848  */
849 static size_t
850 transmit_leave_notification_to_peer (void *cls, size_t size, void *buf)
851 {
852   struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *public_key = cls;
853   struct P2PLeaveNotificationMessage *m = buf;
854   size_t msg_size;
855
856 #if DEBUG_CHAT_SERVICE
857   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Transmitting P2P leave notification\n");
858 #endif
859   if (buf == NULL)
860   {
861     /* client disconnected */
862 #if DEBUG_CHAT_SERVICE
863     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
864                 "Buffer is NULL, dropping the message\n");
865 #endif
866     return 0;
867   }
868   msg_size = sizeof (struct P2PLeaveNotificationMessage);
869   GNUNET_assert (size >= msg_size);
870   m = buf;
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);
876   return msg_size;
877 }
878
879
880 /**
881  * Ask to send a leave notification to the peer.
882  */
883 static int
884 send_leave_noficiation (void *cls, const GNUNET_HashCode * key, void *value)
885 {
886   struct ChatClient *entry = cls;
887   struct ConnectedPeer *cp = value;
888   struct GNUNET_PeerIdentity pid;
889   struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *public_key;
890   size_t msg_size;
891
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",
895               GNUNET_i2s (&pid));
896 #endif
897   msg_size = sizeof (struct P2PLeaveNotificationMessage);
898   public_key =
899       GNUNET_memdup (&entry->public_key,
900                      sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded));
901   if (NULL ==
902       GNUNET_CORE_notify_transmit_ready (core, GNUNET_YES, 1,
903                                          MAX_TRANSMIT_DELAY, &pid, msg_size,
904                                          &transmit_leave_notification_to_peer,
905                                          public_key))
906     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
907                 _("Failed to queue a leave notification\n"));
908   return GNUNET_YES;
909 }
910
911
912 /**
913  * A client disconnected.  Remove all of its data structure entries and notify
914  * remaining room members.
915  *
916  * @param cls closure, NULL
917  * @param client identification of the client
918  */
919 static void
920 handle_client_disconnect (void *cls, struct GNUNET_SERVER_Client *client)
921 {
922   struct ChatClient *entry;
923   struct ChatClient *pos;
924   struct ChatClient *prev;
925   struct LeaveNotificationMessage lnmsg;
926
927   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Client disconnected\n");
928   pos = client_list_head;
929   prev = NULL;
930   while ((NULL != pos) && (pos->client != client))
931   {
932     prev = pos;
933     pos = pos->next;
934   }
935   if (NULL == pos)
936   {
937 #if DEBUG_CHAT_SERVICE
938     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
939                 "No such client. There is nothing to do\n");
940 #endif
941     return;
942   }
943   if (NULL == prev)
944     client_list_head = pos->next;
945   else
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");
951 #endif
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)
957   {
958     if ((0 == strcmp (pos->room, entry->room)) && (NULL != entry->client))
959     {
960       GNUNET_SERVER_notification_context_unicast (nc, entry->client,
961                                                   &lnmsg.header, GNUNET_NO);
962     }
963     entry = entry->next;
964   }
965 #if DEBUG_CHAT_SERVICE
966   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
967               "Broadcasting leave notification to neighbour peers\n");
968 #endif
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);
973   GNUNET_free (pos);
974 }
975
976
977 /**
978  * Handle P2P join notification.
979  *
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)
987  */
988 static int
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
993                               *atsi,
994                               unsigned int atsi_count)
995 {
996   const struct P2PJoinNotificationMessage *p2p_jnmsg;
997   char *room_name;
998   const char *roomptr;
999   uint16_t header_size;
1000   uint16_t meta_len;
1001   uint16_t room_name_len;
1002   struct ChatClient *new_entry;
1003   struct ChatClient *entry;
1004   struct JoinNotificationMessage *jnmsg;
1005   GNUNET_HashCode id;
1006
1007   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Got P2P join notification\n");
1008   if (ntohs (message->size) <= sizeof (struct P2PJoinNotificationMessage))
1009   {
1010     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Malformed message: wrong size\n");
1011     GNUNET_break_op (0);
1012     return GNUNET_SYSERR;
1013   }
1014   p2p_jnmsg = (const struct P2PJoinNotificationMessage *) message;
1015   header_size = ntohs (p2p_jnmsg->header.size);
1016   room_name_len = ntohs (p2p_jnmsg->room_name_len);
1017   if (header_size - sizeof (struct P2PJoinNotificationMessage) <= room_name_len)
1018   {
1019     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1020                 "Malformed message: wrong length of the room name\n");
1021     GNUNET_break_op (0);
1022     return GNUNET_SYSERR;
1023   }
1024   GNUNET_CRYPTO_hash (&p2p_jnmsg->public_key,
1025                       sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
1026                       &id);
1027   entry = client_list_head;
1028   while (NULL != entry)
1029   {
1030     if (0 == memcmp (&entry->id, &id, sizeof (GNUNET_HashCode)))
1031     {
1032 #if DEBUG_CHAT_SERVICE
1033       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1034                   "The client has already joined. There is nothing to do\n");
1035 #endif
1036       return GNUNET_OK;
1037     }
1038     entry = entry->next;
1039   }
1040   meta_len =
1041       header_size - sizeof (struct P2PJoinNotificationMessage) - room_name_len;
1042   roomptr = (const char *) &p2p_jnmsg[1];
1043   room_name = GNUNET_malloc (room_name_len + 1);
1044   memcpy (room_name, roomptr, room_name_len);
1045   room_name[room_name_len] = '\0';
1046   new_entry = GNUNET_malloc (sizeof (struct ChatClient));
1047   memset (new_entry, 0, sizeof (struct ChatClient));
1048   new_entry->id = id;
1049   new_entry->client = NULL;
1050   new_entry->room = room_name;
1051   new_entry->public_key = p2p_jnmsg->public_key;
1052   new_entry->meta_len = meta_len;
1053   if (meta_len > 0)
1054   {
1055     new_entry->member_info = GNUNET_malloc (meta_len);
1056     memcpy (new_entry->member_info, &roomptr[room_name_len], meta_len);
1057   }
1058   else
1059     new_entry->member_info = NULL;
1060   new_entry->msg_options = ntohl (p2p_jnmsg->msg_options);
1061   new_entry->next = client_list_head;
1062   client_list_head = new_entry;
1063 #if DEBUG_CHAT_SERVICE
1064   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1065               "Notifying local room members that we have a new client\n");
1066 #endif
1067   jnmsg = GNUNET_malloc (sizeof (struct JoinNotificationMessage) + meta_len);
1068   jnmsg->header.type = htons (GNUNET_MESSAGE_TYPE_CHAT_JOIN_NOTIFICATION);
1069   jnmsg->header.size =
1070       htons (sizeof (struct JoinNotificationMessage) + meta_len);
1071   jnmsg->msg_options = p2p_jnmsg->msg_options;
1072   jnmsg->public_key = new_entry->public_key;
1073   memcpy (&jnmsg[1], &roomptr[room_name_len], meta_len);
1074   entry = client_list_head;
1075   while (NULL != entry)
1076   {
1077     if ((0 == strcmp (room_name, entry->room)) && (NULL != entry->client))
1078     {
1079       GNUNET_SERVER_notification_context_unicast (nc, entry->client,
1080                                                   &jnmsg->header, GNUNET_NO);
1081     }
1082     entry = entry->next;
1083   }
1084 #if DEBUG_CHAT_SERVICE
1085   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1086               "Broadcasting join notification to neighbour peers\n");
1087 #endif
1088   GNUNET_CONTAINER_multihashmap_iterate (connected_peers,
1089                                          &send_join_noficiation, new_entry);
1090   GNUNET_free (jnmsg);
1091   return GNUNET_OK;
1092 }
1093
1094
1095 /**
1096  * Handle P2P leave notification.
1097  *
1098  * @param cls closure, always NULL
1099  * @param other the other peer involved
1100  * @param message the actual message
1101  * @param atsi performance information
1102  * @param atsi_count number of entries in atsi
1103  * @return GNUNET_OK to keep the connection open,
1104  *         GNUNET_SYSERR to close it (signal serious error)
1105  */
1106 static int
1107 handle_p2p_leave_notification (void *cls,
1108                                const struct GNUNET_PeerIdentity *other,
1109                                const struct GNUNET_MessageHeader *message,
1110                                const struct GNUNET_ATS_Information
1111                                *atsi,
1112                                unsigned int atsi_count)
1113 {
1114   const struct P2PLeaveNotificationMessage *p2p_lnmsg;
1115   GNUNET_HashCode id;
1116   struct ChatClient *pos;
1117   struct ChatClient *prev;
1118   struct ChatClient *entry;
1119   struct LeaveNotificationMessage lnmsg;
1120
1121   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Got P2P leave notification\n");
1122   p2p_lnmsg = (const struct P2PLeaveNotificationMessage *) message;
1123   GNUNET_CRYPTO_hash (&p2p_lnmsg->user,
1124                       sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
1125                       &id);
1126   pos = client_list_head;
1127   prev = NULL;
1128   while (NULL != pos)
1129   {
1130     if (0 == memcmp (&pos->id, &id, sizeof (GNUNET_HashCode)))
1131       break;
1132     prev = pos;
1133     pos = pos->next;
1134   }
1135   if (NULL == pos)
1136   {
1137 #if DEBUG_CHAT_SERVICE
1138     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1139                 "No such client. There is nothing to do\n");
1140 #endif
1141     return GNUNET_OK;
1142   }
1143   if (NULL == prev)
1144     client_list_head = pos->next;
1145   else
1146     prev->next = pos->next;
1147 #if DEBUG_CHAT_SERVICE
1148   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1149               "Notifying local room members that the client has gone away\n");
1150 #endif
1151   lnmsg.header.size = htons (sizeof (struct LeaveNotificationMessage));
1152   lnmsg.header.type = htons (GNUNET_MESSAGE_TYPE_CHAT_LEAVE_NOTIFICATION);
1153   lnmsg.reserved = htonl (0);
1154   lnmsg.user = pos->public_key;
1155   entry = client_list_head;
1156   while (NULL != entry)
1157   {
1158     if (0 == strcmp (pos->room, entry->room) && (NULL != entry->client))
1159     {
1160       GNUNET_SERVER_notification_context_unicast (nc, entry->client,
1161                                                   &lnmsg.header, GNUNET_NO);
1162     }
1163     entry = entry->next;
1164   }
1165 #if DEBUG_CHAT_SERVICE
1166   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1167               "Broadcasting leave notification to neighbour peers\n");
1168 #endif
1169   GNUNET_CONTAINER_multihashmap_iterate (connected_peers,
1170                                          &send_leave_noficiation, pos);
1171   GNUNET_free (pos->room);
1172   GNUNET_free_non_null (pos->member_info);
1173   GNUNET_free (pos);
1174   return GNUNET_OK;
1175 }
1176
1177
1178 /**
1179  * Handle P2P message notification.
1180  *
1181  * @param cls closure, always NULL
1182  * @param other the other peer involved
1183  * @param message the actual message
1184  * @param atsi performance information
1185  * @param atsi_count number of entries in atsi
1186  * @return GNUNET_OK to keep the connection open,
1187  *         GNUNET_SYSERR to close it (signal serious error)
1188  */
1189 static int
1190 handle_p2p_message_notification (void *cls,
1191                                  const struct GNUNET_PeerIdentity *other,
1192                                  const struct GNUNET_MessageHeader *message,
1193                                  const struct GNUNET_ATS_Information
1194                                  *atsi,
1195                                  unsigned int atsi_count)
1196 {
1197   const struct P2PReceiveNotificationMessage *p2p_rnmsg;
1198   struct P2PReceiveNotificationMessage *my_p2p_rnmsg;
1199   struct ReceiveNotificationMessage *rnmsg;
1200   struct ChatClient *sender;
1201   struct ChatClient *pos;
1202   static GNUNET_HashCode all_zeros;
1203   int is_priv;
1204   int is_anon;
1205   uint16_t msg_len;
1206   uint16_t room_name_len;
1207   char *room_name = NULL;
1208   char *text;
1209
1210   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Got P2P message notification\n");
1211   if (ntohs (message->size) <= sizeof (struct P2PReceiveNotificationMessage))
1212   {
1213     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Malformed message: wrong size\n");
1214     GNUNET_break_op (0);
1215     return GNUNET_SYSERR;
1216   }
1217   p2p_rnmsg = (const struct P2PReceiveNotificationMessage *) message;
1218   msg_len =
1219       ntohs (p2p_rnmsg->header.size) -
1220       sizeof (struct P2PReceiveNotificationMessage);
1221
1222   is_anon = (0 != (ntohl (p2p_rnmsg->msg_options) & GNUNET_CHAT_MSG_ANONYMOUS));
1223   if (is_anon)
1224   {
1225     room_name_len = ntohs (p2p_rnmsg->room_name_len);
1226     if (msg_len <= room_name_len)
1227     {
1228       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1229                   "Malformed message: wrong length of the room name\n");
1230       GNUNET_break_op (0);
1231       return GNUNET_SYSERR;
1232     }
1233     msg_len -= room_name_len;
1234     if (lookup_anonymous_message (p2p_rnmsg))
1235     {
1236 #if DEBUG_CHAT_SERVICE
1237       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1238                   "This anonymous message has already been handled.");
1239 #endif
1240       return GNUNET_OK;
1241     }
1242     remember_anonymous_message (p2p_rnmsg);
1243     room_name = GNUNET_malloc (room_name_len + 1);
1244     memcpy (room_name, (char *) &p2p_rnmsg[1], room_name_len);
1245     room_name[room_name_len] = '\0';
1246     text = (char *) &p2p_rnmsg[1] + room_name_len;
1247   }
1248   else
1249   {
1250     sender = client_list_head;
1251     while ((NULL != sender) &&
1252            (0 !=
1253             memcmp (&sender->id, &p2p_rnmsg->sender, sizeof (GNUNET_HashCode))))
1254       sender = sender->next;
1255     if (NULL == sender)
1256     {
1257       /* not an error since the sender may have left before we got the
1258        * message */
1259 #if DEBUG_CHAT_SERVICE
1260       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1261                   "Unknown source. Rejecting the message\n");
1262 #endif
1263       return GNUNET_OK;
1264     }
1265     if (sender->msg_sequence_number >= ntohl (p2p_rnmsg->sequence_number))
1266     {
1267 #if DEBUG_CHAT_SERVICE
1268       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1269                   "This message has already been handled."
1270                   " Sequence numbers (msg/sender): %u/%u\n",
1271                   ntohl (p2p_rnmsg->sequence_number),
1272                   sender->msg_sequence_number);
1273 #endif
1274       return GNUNET_OK;
1275     }
1276     sender->msg_sequence_number = ntohl (p2p_rnmsg->sequence_number);
1277     room_name = sender->room;
1278     text = (char *) &p2p_rnmsg[1];
1279   }
1280
1281 #if DEBUG_CHAT_SERVICE
1282   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1283               "Sending message to local room members\n");
1284 #endif
1285   rnmsg = GNUNET_malloc (sizeof (struct ReceiveNotificationMessage) + msg_len);
1286   rnmsg->header.size =
1287       htons (sizeof (struct ReceiveNotificationMessage) + msg_len);
1288   rnmsg->header.type = htons (GNUNET_MESSAGE_TYPE_CHAT_MESSAGE_NOTIFICATION);
1289   rnmsg->msg_options = p2p_rnmsg->msg_options;
1290   rnmsg->sequence_number = p2p_rnmsg->sequence_number;
1291   rnmsg->reserved = htonl (0);
1292   rnmsg->timestamp = p2p_rnmsg->timestamp;
1293   is_priv =
1294       (0 != memcmp (&all_zeros, &p2p_rnmsg->target, sizeof (GNUNET_HashCode)));
1295   if (is_priv)
1296     memcpy (&rnmsg->encrypted_key, &p2p_rnmsg->encrypted_key,
1297             sizeof (struct GNUNET_CRYPTO_RsaEncryptedData));
1298   rnmsg->sender = p2p_rnmsg->sender;
1299   memcpy (&rnmsg[1], text, msg_len);
1300   pos = client_list_head;
1301   while (NULL != pos)
1302   {
1303     if ((0 == strcmp (room_name, pos->room)) && (NULL != pos->client))
1304     {
1305       if (((!is_priv) ||
1306            (0 ==
1307             memcmp (&p2p_rnmsg->target, &pos->id, sizeof (GNUNET_HashCode)))) &&
1308           (0 == (ntohl (p2p_rnmsg->msg_options) & (~pos->msg_options))))
1309       {
1310         GNUNET_SERVER_notification_context_unicast (nc, pos->client,
1311                                                     &rnmsg->header, GNUNET_NO);
1312       }
1313     }
1314     pos = pos->next;
1315   }
1316   if (is_anon)
1317     GNUNET_free (room_name);
1318 #if DEBUG_CHAT_SERVICE
1319   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1320               "Broadcasting message notification to neighbour peers\n");
1321 #endif
1322   my_p2p_rnmsg = GNUNET_memdup (p2p_rnmsg, ntohs (p2p_rnmsg->header.size));
1323   GNUNET_CONTAINER_multihashmap_iterate (connected_peers,
1324                                          &send_message_noficiation,
1325                                          my_p2p_rnmsg);
1326   GNUNET_free (rnmsg);
1327   return GNUNET_OK;
1328 }
1329
1330
1331 /**
1332  * Handle P2P sync request.
1333  *
1334  * @param cls closure, always NULL
1335  * @param other the other peer involved
1336  * @param message the actual message
1337  * @param atsi performance information
1338  * @param atsi_count number of entries in atsi
1339  * @return GNUNET_OK to keep the connection open,
1340  *         GNUNET_SYSERR to close it (signal serious error)
1341  */
1342 static int
1343 handle_p2p_sync_request (void *cls, const struct GNUNET_PeerIdentity *other,
1344                          const struct GNUNET_MessageHeader *message,
1345                          const struct GNUNET_ATS_Information *atsi,
1346                          unsigned int atsi_count)
1347 {
1348   struct ChatClient *entry;
1349   struct GNUNET_CORE_TransmitHandle *th;
1350   size_t msg_size;
1351
1352   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Got P2P sync request\n");
1353 #if DEBUG_CHAT_SERVICE
1354   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1355               "Notifying the requester of all known clients\n");
1356 #endif
1357   entry = client_list_head;
1358   while (NULL != entry)
1359   {
1360     msg_size =
1361         sizeof (struct P2PJoinNotificationMessage) + strlen (entry->room) +
1362         entry->meta_len;
1363     th = GNUNET_CORE_notify_transmit_ready (core, GNUNET_NO, 1,
1364                                             MAX_TRANSMIT_DELAY, other, msg_size,
1365                                             &transmit_join_notification_to_peer,
1366                                             entry);
1367     GNUNET_assert (NULL != th);
1368     entry = entry->next;
1369   }
1370   return GNUNET_OK;
1371 }
1372
1373
1374 /**
1375  * Handle P2P confirmation receipt.
1376  *
1377  * @param cls closure, always NULL
1378  * @param other the other peer involved
1379  * @param message the actual message
1380  * @param atsi performance information
1381  * @param atsi_count number of entries in atsi
1382  * @return GNUNET_OK to keep the connection open,
1383  *         GNUNET_SYSERR to close it (signal serious error)
1384  */
1385 static int
1386 handle_p2p_confirmation_receipt (void *cls,
1387                                  const struct GNUNET_PeerIdentity *other,
1388                                  const struct GNUNET_MessageHeader *message,
1389                                  const struct GNUNET_ATS_Information
1390                                  *atsi,
1391                                  unsigned int atsi_count)
1392 {
1393   const struct P2PConfirmationReceiptMessage *p2p_crmsg;
1394   struct P2PConfirmationReceiptMessage *my_p2p_crmsg;
1395   struct ConfirmationReceiptMessage *crmsg;
1396   struct ChatClient *target;
1397   struct ChatClient *author;
1398
1399   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Got P2P confirmation receipt\n");
1400   p2p_crmsg = (const struct P2PConfirmationReceiptMessage *) message;
1401   target = client_list_head;
1402   while ((NULL != target) &&
1403          (0 !=
1404           memcmp (&target->id, &p2p_crmsg->target, sizeof (GNUNET_HashCode))))
1405     target = target->next;
1406   if (NULL == target)
1407   {
1408     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1409                 "Unknown source of the receipt. Rejecting the message\n");
1410     GNUNET_break_op (0);
1411     return GNUNET_SYSERR;
1412   }
1413   if (target->rcpt_sequence_number >= ntohl (p2p_crmsg->sequence_number))
1414   {
1415 #if DEBUG_CHAT_SERVICE
1416     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1417                 "This receipt has already been handled."
1418                 " Sequence numbers (msg/sender): %u/%u\n",
1419                 ntohl (p2p_crmsg->sequence_number),
1420                 target->rcpt_sequence_number);
1421 #endif
1422     return GNUNET_OK;
1423   }
1424   target->rcpt_sequence_number = ntohl (p2p_crmsg->sequence_number);
1425   author = client_list_head;
1426   while ((NULL != author) &&
1427          (0 !=
1428           memcmp (&author->id, &p2p_crmsg->author, sizeof (GNUNET_HashCode))))
1429     author = author->next;
1430   if (NULL == author)
1431   {
1432     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1433                 "Unknown addressee. Rejecting the receipt\n");
1434     GNUNET_break_op (0);
1435     return GNUNET_SYSERR;
1436   }
1437
1438   if (NULL == author->client)
1439   {
1440 #if DEBUG_CHAT_SERVICE
1441     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1442                 "The author of the original message is not a local client."
1443                 " Broadcasting receipt to neighbour peers\n");
1444 #endif
1445     my_p2p_crmsg =
1446         GNUNET_memdup (p2p_crmsg,
1447                        sizeof (struct P2PConfirmationReceiptMessage));
1448     GNUNET_CONTAINER_multihashmap_iterate (connected_peers,
1449                                            &send_confirmation_receipt,
1450                                            my_p2p_crmsg);
1451     GNUNET_free (my_p2p_crmsg);
1452   }
1453   else
1454   {
1455 #if DEBUG_CHAT_SERVICE
1456     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1457                 "The author of the original message is a local client."
1458                 " Verifying signature of the receipt\n");
1459 #endif
1460     crmsg = GNUNET_malloc (sizeof (struct ConfirmationReceiptMessage));
1461     crmsg->header.size = htons (sizeof (struct ConfirmationReceiptMessage));
1462     crmsg->header.type =
1463         htons (GNUNET_MESSAGE_TYPE_CHAT_CONFIRMATION_NOTIFICATION);
1464     crmsg->signature = p2p_crmsg->signature;
1465     crmsg->purpose = p2p_crmsg->purpose;
1466     crmsg->sequence_number = p2p_crmsg->msg_sequence_number;
1467     crmsg->reserved2 = 0;
1468     crmsg->timestamp = p2p_crmsg->timestamp;
1469     crmsg->target = p2p_crmsg->target;
1470     crmsg->author = p2p_crmsg->author;
1471     crmsg->content = p2p_crmsg->content;
1472     if (GNUNET_OK !=
1473         GNUNET_CRYPTO_rsa_verify (GNUNET_SIGNATURE_PURPOSE_CHAT_RECEIPT,
1474                                   &crmsg->purpose, &crmsg->signature,
1475                                   &target->public_key))
1476     {
1477       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1478                   "Invalid signature of the receipt\n");
1479       GNUNET_break_op (0);
1480       return GNUNET_SYSERR;
1481     }
1482 #if DEBUG_CHAT_SERVICE
1483     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1484                 "The author of the original message is a local client."
1485                 " Sending receipt to the client\n");
1486 #endif
1487     GNUNET_SERVER_notification_context_unicast (nc, author->client,
1488                                                 &crmsg->header, GNUNET_NO);
1489     GNUNET_free (crmsg);
1490   }
1491   return GNUNET_OK;
1492 }
1493
1494
1495 /**
1496  * Transmit a sync request to the peer.
1497  *
1498  * @param cls closure, NULL
1499  * @param size number of bytes available in buf
1500  * @param buf where the callee should write the message
1501  * @return number of bytes written to buf
1502  */
1503 static size_t
1504 transmit_sync_request_to_peer (void *cls, size_t size, void *buf)
1505 {
1506   struct GNUNET_MessageHeader *m = buf;
1507   size_t msg_size;
1508
1509 #if DEBUG_CHAT_SERVICE
1510   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Transmitting P2P sync request\n");
1511 #endif
1512   msg_size = sizeof (struct GNUNET_MessageHeader);
1513   GNUNET_assert (size >= msg_size);
1514   GNUNET_assert (NULL != buf);
1515   m = buf;
1516   m->type = htons (GNUNET_MESSAGE_TYPE_CHAT_P2P_SYNC_REQUEST);
1517   m->size = htons (msg_size);
1518   return msg_size;
1519 }
1520
1521
1522 /**
1523  * Method called whenever a peer connects.
1524  *
1525  * @param cls closure
1526  * @param peer peer identity this notification is about
1527  * @param atsi performance data
1528  * @param atsi_count number of entries in atsi
1529  */
1530 static void
1531 peer_connect_handler (void *cls, const struct GNUNET_PeerIdentity *peer,
1532                       const struct GNUNET_ATS_Information *atsi,
1533                       unsigned int atsi_count)
1534 {
1535   struct ConnectedPeer *cp;
1536   struct GNUNET_CORE_TransmitHandle *th;
1537
1538   if (0 == memcmp (peer, me, sizeof (struct GNUNET_PeerIdentity)))
1539     return;
1540   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Peer connected: %s\n",
1541               GNUNET_i2s (peer));
1542   th = GNUNET_CORE_notify_transmit_ready (core, GNUNET_YES, 1,
1543                                           MAX_TRANSMIT_DELAY, peer,
1544                                           sizeof (struct GNUNET_MessageHeader),
1545                                           &transmit_sync_request_to_peer, NULL);
1546   GNUNET_assert (NULL != th);
1547   cp = GNUNET_CONTAINER_multihashmap_get (connected_peers, &peer->hashPubKey);
1548   if (NULL != cp)
1549   {
1550     GNUNET_break (0);
1551     return;
1552   }
1553   cp = GNUNET_malloc (sizeof (struct ConnectedPeer));
1554   cp->pid = GNUNET_PEER_intern (peer);
1555   GNUNET_break (GNUNET_OK ==
1556                 GNUNET_CONTAINER_multihashmap_put (connected_peers,
1557                                                    &peer->hashPubKey, cp,
1558                                                    GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
1559 }
1560
1561
1562 /**
1563  * Iterator to free peer entries.
1564  *
1565  * @param cls closure, unused
1566  * @param key current key code
1567  * @param value value in the hash map (peer entry)
1568  * @return GNUNET_YES (we should continue to iterate)
1569  */
1570 static int
1571 clean_peer (void *cls, const GNUNET_HashCode * key, void *value)
1572 {
1573   struct ConnectedPeer *cp;
1574   const struct GNUNET_PeerIdentity *peer =
1575       (const struct GNUNET_PeerIdentity *) key;
1576
1577   cp = GNUNET_CONTAINER_multihashmap_get (connected_peers, &peer->hashPubKey);
1578   if (cp == NULL)
1579     return GNUNET_YES;
1580   GNUNET_break (GNUNET_YES ==
1581                 GNUNET_CONTAINER_multihashmap_remove (connected_peers,
1582                                                       &peer->hashPubKey, cp));
1583   GNUNET_PEER_change_rc (cp->pid, -1);
1584   GNUNET_free (cp);
1585   return GNUNET_YES;
1586 }
1587
1588
1589 /**
1590  * Method called whenever a peer disconnects.
1591  *
1592  * @param cls closure, not used
1593  * @param peer peer identity this notification is about
1594  */
1595 static void
1596 peer_disconnect_handler (void *cls, const struct GNUNET_PeerIdentity *peer)
1597 {
1598
1599   if (0 == memcmp (peer, me, sizeof (struct GNUNET_PeerIdentity)))
1600     return;
1601   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Peer disconnected: %s\n",
1602               GNUNET_i2s (peer));
1603   clean_peer (NULL, (const GNUNET_HashCode *) peer, NULL);
1604 }
1605
1606
1607 /**
1608  * Task run during shutdown.
1609  *
1610  * @param cls unused
1611  * @param tc unused
1612  */
1613 static void
1614 cleanup_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1615 {
1616   struct AnonymousMessage *next_msg;
1617   struct ChatClient *next_client;
1618
1619   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Cleaning up\n");
1620   if (NULL != core)
1621   {
1622     GNUNET_CORE_disconnect (core);
1623     core = NULL;
1624   }
1625   if (NULL != nc)
1626   {
1627     GNUNET_SERVER_notification_context_destroy (nc);
1628     nc = NULL;
1629   }
1630   while (NULL != client_list_head)
1631   {
1632     next_client = client_list_head->next;
1633     GNUNET_free (client_list_head->room);
1634     GNUNET_free_non_null (client_list_head->member_info);
1635     GNUNET_free (client_list_head);
1636     client_list_head = next_client;
1637   }
1638   while (NULL != anonymous_list_head)
1639   {
1640     next_msg = anonymous_list_head->next;
1641     GNUNET_free (anonymous_list_head);
1642     anonymous_list_head = next_msg;
1643   }
1644   GNUNET_CONTAINER_multihashmap_iterate (connected_peers, &clean_peer, NULL);
1645   GNUNET_CONTAINER_multihashmap_destroy (connected_peers);
1646   connected_peers = NULL;
1647 }
1648
1649
1650 /**
1651  * To be called on core init/fail.
1652  *
1653  * @param cls closure, NULL
1654  * @param server handle to the server for this service
1655  * @param my_identity the public identity of this peer
1656  */
1657 static void
1658 core_init (void *cls, struct GNUNET_CORE_Handle *server,
1659            const struct GNUNET_PeerIdentity *my_identity)
1660 {
1661   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Core initialized\n");
1662   me = my_identity;
1663 }
1664
1665
1666 /**
1667  * Process chat requests.
1668  *
1669  * @param cls closure, NULL
1670  * @param server the initialized server
1671  * @param c configuration to use
1672  */
1673 static void
1674 run (void *cls, struct GNUNET_SERVER_Handle *server,
1675      const struct GNUNET_CONFIGURATION_Handle *c)
1676 {
1677   static const struct GNUNET_SERVER_MessageHandler handlers[] = {
1678     {&handle_join_request, NULL,
1679      GNUNET_MESSAGE_TYPE_CHAT_JOIN_REQUEST, 0},
1680     {&handle_transmit_request, NULL,
1681      GNUNET_MESSAGE_TYPE_CHAT_TRANSMIT_REQUEST, 0},
1682     {&handle_acknowledge_request, NULL,
1683      GNUNET_MESSAGE_TYPE_CHAT_CONFIRMATION_RECEIPT,
1684      sizeof (struct ConfirmationReceiptMessage)},
1685     {NULL, NULL, 0, 0}
1686   };
1687   static const struct GNUNET_CORE_MessageHandler p2p_handlers[] = {
1688     {&handle_p2p_join_notification,
1689      GNUNET_MESSAGE_TYPE_CHAT_P2P_JOIN_NOTIFICATION, 0},
1690     {&handle_p2p_leave_notification,
1691      GNUNET_MESSAGE_TYPE_CHAT_P2P_LEAVE_NOTIFICATION,
1692      sizeof (struct P2PLeaveNotificationMessage)},
1693     {&handle_p2p_message_notification,
1694      GNUNET_MESSAGE_TYPE_CHAT_P2P_MESSAGE_NOTIFICATION, 0},
1695     {&handle_p2p_sync_request,
1696      GNUNET_MESSAGE_TYPE_CHAT_P2P_SYNC_REQUEST,
1697      sizeof (struct GNUNET_MessageHeader)},
1698     {&handle_p2p_confirmation_receipt,
1699      GNUNET_MESSAGE_TYPE_CHAT_P2P_CONFIRMATION_RECEIPT,
1700      sizeof (struct P2PConfirmationReceiptMessage)},
1701     {NULL, 0, 0}
1702   };
1703
1704   GNUNET_log_setup ("gnunet-service-chat",
1705 #if DEBUG_CHAT_SERVICE
1706                     "DEBUG",
1707 #else
1708                     "WARNING",
1709 #endif
1710                     NULL);
1711   cfg = c;
1712   nc = GNUNET_SERVER_notification_context_create (server, 16);
1713   connected_peers =
1714       GNUNET_CONTAINER_multihashmap_create (EXPECTED_NEIGHBOUR_COUNT);
1715   GNUNET_SERVER_add_handlers (server, handlers);
1716   core =
1717       GNUNET_CORE_connect (cfg, QUEUE_SIZE, NULL, &core_init,
1718                            &peer_connect_handler, &peer_disconnect_handler,
1719                            NULL, GNUNET_NO, NULL, GNUNET_NO,
1720                            p2p_handlers);
1721   GNUNET_SERVER_disconnect_notify (server, &handle_client_disconnect, NULL);
1722   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &cleanup_task,
1723                                 NULL);
1724 }
1725
1726
1727 /**
1728  * The main function for the chat service.
1729  *
1730  * @param argc number of arguments from the command line
1731  * @param argv command line arguments
1732  * @return 0 ok, 1 on error
1733  */
1734 int
1735 main (int argc, char *const *argv)
1736 {
1737   return (GNUNET_OK ==
1738           GNUNET_SERVICE_run (argc, argv, "chat", GNUNET_SERVICE_OPTION_NONE,
1739                               &run, NULL)) ? 0 : 1;
1740 }
1741
1742 /* end of gnunet-service-chat.c */