renaming ats files
[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_NO
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 *p2p_rnmsg)
164 {
165   static GNUNET_HashCode hash;
166   struct AnonymousMessage *anon_msg;
167   struct AnonymousMessage *prev;
168   int anon_list_len;
169
170   GNUNET_CRYPTO_hash (p2p_rnmsg, ntohs (p2p_rnmsg->header.size), &hash);
171   anon_msg = GNUNET_malloc (sizeof (struct AnonymousMessage));
172   anon_msg->hash = hash;
173   anon_msg->next = anonymous_list_head;
174   anonymous_list_head = anon_msg;
175   anon_list_len = 1;
176   prev = NULL;
177   while ((NULL != anon_msg->next))
178     {
179       prev = anon_msg;
180       anon_msg = anon_msg->next;
181       anon_list_len++;
182     }
183   if (anon_list_len == MAX_ANONYMOUS_MSG_LIST_LENGTH)
184     {
185       GNUNET_free (anon_msg);
186       if (NULL != prev)
187         prev->next = NULL;
188     }
189 }
190
191
192 static int
193 lookup_anonymous_message (const struct P2PReceiveNotificationMessage *p2p_rnmsg)
194 {
195   static GNUNET_HashCode hash;
196   struct AnonymousMessage *anon_msg;
197
198   GNUNET_CRYPTO_hash (p2p_rnmsg, ntohs (p2p_rnmsg->header.size), &hash);
199   anon_msg = anonymous_list_head;
200   while ((NULL != anon_msg) &&
201          (0 != memcmp (&anon_msg->hash, &hash, sizeof (GNUNET_HashCode))))
202     anon_msg = anon_msg->next;
203   return (NULL != anon_msg);
204 }
205
206
207 /**
208  * Transmit a message notification to the peer.
209  *
210  * @param cls closure, pointer to the 'struct P2PReceiveNotificationMessage'
211  * @param size number of bytes available in buf
212  * @param buf where the callee should write the message
213  * @return number of bytes written to buf
214  */
215 static size_t
216 transmit_message_notification_to_peer (void *cls,
217                                        size_t size,
218                                        void *buf)
219 {
220   struct P2PReceiveNotificationMessage *my_msg = cls;
221   struct P2PReceiveNotificationMessage *m = buf;
222   size_t msg_size;
223
224 #if DEBUG_CHAT_SERVICE
225   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
226               "Transmitting P2P message notification\n");
227 #endif
228   if (buf == NULL)
229     {
230       /* client disconnected */
231 #if DEBUG_CHAT_SERVICE
232       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
233                   "Buffer is NULL, dropping the message\n");
234 #endif
235       return 0;
236     }
237   msg_size = ntohs (my_msg->header.size);
238   GNUNET_assert (size >= msg_size);
239   memcpy (m, my_msg, msg_size);
240   GNUNET_free (my_msg);
241   return msg_size;
242 }
243
244
245 /**
246  * Ask to send a message notification to the peer.
247  */
248 static int
249 send_message_noficiation (void *cls,
250                           const GNUNET_HashCode *key,
251                           void *value)
252 {
253   struct P2PReceiveNotificationMessage *msg = cls;
254   struct ConnectedPeer *cp = value;
255   struct GNUNET_PeerIdentity pid;
256   struct P2PReceiveNotificationMessage *my_msg;
257
258   GNUNET_PEER_resolve (cp->pid, &pid);
259 #if DEBUG_CHAT_SERVICE
260   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
261               "Sending message notification to `%s'\n", GNUNET_i2s (&pid));
262 #endif
263   my_msg = GNUNET_memdup (msg, ntohs (msg->header.size));
264   if (NULL == GNUNET_CORE_notify_transmit_ready (core,
265                                                  GNUNET_NO,
266                                                  1,
267                                                  MAX_TRANSMIT_DELAY,
268                                                  &pid,
269                                                  ntohs (msg->header.size),
270                                                  &transmit_message_notification_to_peer,
271                                                  my_msg))
272     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
273                 _("Failed to queue a message notification\n"));
274   return GNUNET_YES;
275 }
276
277
278 /**
279  * A client sent a chat message.  Encrypt the message text if the message is
280  * private.  Send the message to local room members and to all connected peers.
281  *
282  * @param cls closure, NULL
283  * @param client identification of the client
284  * @param message the actual message
285  */
286 static void
287 handle_transmit_request (void *cls,
288                          struct GNUNET_SERVER_Client *client,
289                          const struct GNUNET_MessageHeader *message)
290 {
291   static GNUNET_HashCode all_zeros;
292   const struct TransmitRequestMessage *trmsg;
293   struct ReceiveNotificationMessage *rnmsg;
294   struct P2PReceiveNotificationMessage *p2p_rnmsg;
295   struct ChatClient *pos;
296   struct ChatClient *target;
297   struct GNUNET_CRYPTO_AesSessionKey key;
298   char encrypted_msg[MAX_MESSAGE_LENGTH];
299   const char *room;
300   size_t room_len;
301   int msg_len;
302   int is_priv;
303   int is_anon;
304
305   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Client sent a chat message\n");
306   if (ntohs (message->size) <= sizeof (struct TransmitRequestMessage))
307     {
308       GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Malformed message: wrong size\n");
309       GNUNET_break (0);
310       GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
311       return;
312     }
313   trmsg = (const struct TransmitRequestMessage *) message;
314   msg_len = ntohs (trmsg->header.size) - sizeof (struct TransmitRequestMessage);
315   is_priv = (0 != (ntohl (trmsg->msg_options) & GNUNET_CHAT_MSG_PRIVATE));
316   if (is_priv)
317     {
318 #if DEBUG_CHAT_SERVICE
319       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Encrypting the message text\n");
320 #endif
321       GNUNET_CRYPTO_aes_create_session_key (&key);
322       msg_len = GNUNET_CRYPTO_aes_encrypt (&trmsg[1],
323                                            msg_len,
324                                            &key,
325                                            (const struct GNUNET_CRYPTO_AesInitializationVector *) INITVALUE,
326                                            encrypted_msg);
327       if (-1 == msg_len)
328         {
329           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
330                       "Could not encrypt the message text\n");
331           GNUNET_break (0);
332           GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
333           return;
334         }
335     }
336   rnmsg = GNUNET_malloc (sizeof (struct ReceiveNotificationMessage) + msg_len);
337   rnmsg->header.size = htons (sizeof (struct ReceiveNotificationMessage) +
338                               msg_len);
339   rnmsg->header.type = htons (GNUNET_MESSAGE_TYPE_CHAT_MESSAGE_NOTIFICATION);
340   rnmsg->msg_options = trmsg->msg_options;
341   rnmsg->timestamp = trmsg->timestamp;
342   pos = client_list_head;
343   while ((NULL != pos) && (pos->client != client))
344     pos = pos->next;
345   if (NULL == pos)
346     {
347       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
348                   "The client is not a member of a chat room. Client has to "
349                   "join a chat room first\n");
350       GNUNET_break (0);
351       GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
352       GNUNET_free (rnmsg);
353       return;
354     }
355   room = pos->room;
356   pos->msg_sequence_number = ntohl (trmsg->sequence_number);
357   is_anon = (0 != (ntohl (trmsg->msg_options) & GNUNET_CHAT_MSG_ANONYMOUS));
358   if (is_anon)
359     {
360     memset (&rnmsg->sender, 0, sizeof (GNUNET_HashCode));
361       rnmsg->sequence_number = 0;
362     }
363   else
364     {
365       rnmsg->sender = pos->id;
366       rnmsg->sequence_number = trmsg->sequence_number;
367     }
368   if (is_priv)
369     {
370 #if DEBUG_CHAT_SERVICE
371       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
372                   "Encrypting the session key using the public key of '%s'\n",
373                   GNUNET_h2s (&trmsg->target));
374 #endif
375       if (0 == memcmp (&all_zeros, &trmsg->target, sizeof (GNUNET_HashCode)))
376         {
377           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
378                       "Malformed message: private, but no target\n");
379           GNUNET_break (0);
380           GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
381           GNUNET_free (rnmsg);
382           return;
383         }
384       memcpy (&rnmsg[1], encrypted_msg, msg_len);
385       target = client_list_head;
386       while ((NULL != target) &&
387              (0 != memcmp (&target->id,
388                            &trmsg->target,
389                            sizeof (GNUNET_HashCode))))
390         target = target->next;
391       if (NULL == target)
392         {
393           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
394                       "Unknown target of the private message\n");
395           GNUNET_break (0);
396           GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
397           GNUNET_free (rnmsg);
398           return;
399         }
400       if (GNUNET_SYSERR == GNUNET_CRYPTO_rsa_encrypt (&key,
401                                                       sizeof (struct GNUNET_CRYPTO_AesSessionKey),
402                                                       &target->public_key,
403                                                       &rnmsg->encrypted_key))
404         {
405           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
406                       "Could not encrypt the session key\n");
407           GNUNET_break (0);
408           GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
409           GNUNET_free (rnmsg);
410           return;
411         }
412     }
413   else
414     {
415       memcpy (&rnmsg[1], &trmsg[1], msg_len);
416     }
417   pos = client_list_head;
418 #if DEBUG_CHAT_SERVICE
419   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending message to local room members\n");
420 #endif
421   while (NULL != pos)
422     {
423       if ((0 == strcmp (room, pos->room)) &&
424           (NULL != pos->client) &&
425           (pos->client != client))
426         {
427           if (((!is_priv) ||
428                (0 == memcmp (&trmsg->target,
429                              &pos->id,
430                              sizeof (GNUNET_HashCode)))) &&
431               (0 == (ntohl (trmsg->msg_options) & (~pos->msg_options))))
432             {
433               GNUNET_SERVER_notification_context_unicast (nc,
434                                                           pos->client,
435                                                           &rnmsg->header,
436                                                           GNUNET_NO);
437             }
438         }
439       pos = pos->next;
440     }
441 #if DEBUG_CHAT_SERVICE
442   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
443               "Broadcasting message to neighbour peers\n");
444 #endif
445   if (is_anon)
446     {
447       room_len = strlen (room);
448       p2p_rnmsg = GNUNET_malloc (sizeof (struct P2PReceiveNotificationMessage) +
449                                  msg_len + room_len);
450       p2p_rnmsg->header.size =
451         htons (sizeof (struct P2PReceiveNotificationMessage) + msg_len +
452                room_len);
453       p2p_rnmsg->room_name_len = htons (room_len);
454       memcpy ((char *) &p2p_rnmsg[1], room, room_len);
455       memcpy ((char *) &p2p_rnmsg[1] + room_len, &trmsg[1], msg_len);
456     }
457   else
458     {
459   p2p_rnmsg = GNUNET_malloc (sizeof (struct P2PReceiveNotificationMessage) +
460                              msg_len);
461       p2p_rnmsg->header.size =
462         htons (sizeof (struct P2PReceiveNotificationMessage) + msg_len);
463       if (is_priv)
464     {
465       memcpy (&p2p_rnmsg[1], encrypted_msg, msg_len);
466       memcpy (&p2p_rnmsg->encrypted_key,
467               &rnmsg->encrypted_key,
468               sizeof (struct GNUNET_CRYPTO_RsaEncryptedData));
469     }
470   else
471       memcpy (&p2p_rnmsg[1], &trmsg[1], msg_len);
472     }
473   p2p_rnmsg->header.type = htons (GNUNET_MESSAGE_TYPE_CHAT_P2P_MESSAGE_NOTIFICATION);
474   p2p_rnmsg->msg_options = trmsg->msg_options;
475   p2p_rnmsg->sequence_number = trmsg->sequence_number;
476   p2p_rnmsg->timestamp = trmsg->timestamp;
477   p2p_rnmsg->reserved = htons (0);
478   p2p_rnmsg->sender = rnmsg->sender;
479   p2p_rnmsg->target = trmsg->target;
480   if (is_anon)
481     remember_anonymous_message (p2p_rnmsg);
482   GNUNET_CONTAINER_multihashmap_iterate (connected_peers,
483                                          &send_message_noficiation,
484                                          p2p_rnmsg);
485   GNUNET_free (p2p_rnmsg);
486   GNUNET_SERVER_receive_done (client, GNUNET_OK);
487   GNUNET_free (rnmsg);
488 }
489
490
491 /**
492  * Transmit a join notification to the peer.
493  *
494  * @param cls closure, pointer to the 'struct ChatClient'
495  * @param size number of bytes available in buf
496  * @param buf where the callee should write the message
497  * @return number of bytes written to buf
498  */
499 static size_t
500 transmit_join_notification_to_peer (void *cls,
501                                     size_t size,
502                                     void *buf)
503 {
504   struct ChatClient *entry = cls;
505   struct P2PJoinNotificationMessage *m = buf;
506   size_t room_len;
507   size_t meta_len;
508   size_t msg_size;
509   char *roomptr;
510
511 #if DEBUG_CHAT_SERVICE
512   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
513               "Transmitting P2P join notification\n");
514 #endif
515   room_len = strlen (entry->room);
516   meta_len = entry->meta_len;
517   msg_size = sizeof (struct P2PJoinNotificationMessage) + meta_len + room_len;
518   GNUNET_assert (size >= msg_size);
519   GNUNET_assert (NULL != buf);
520   m = buf;
521   m->header.type = htons (GNUNET_MESSAGE_TYPE_CHAT_P2P_JOIN_NOTIFICATION);
522   m->header.size = htons (msg_size);
523   m->msg_options = htonl (entry->msg_options);
524   m->room_name_len = htons (room_len);
525   m->reserved = htons (0);
526   m->reserved2 = htonl (0);
527   m->public_key = entry->public_key;
528   roomptr = (char *) &m[1];
529   memcpy (roomptr, entry->room, room_len);
530   if (meta_len > 0)
531     memcpy (&roomptr[room_len], entry->member_info, meta_len);
532   return msg_size;
533 }
534
535
536 /**
537  * Ask to send a join notification to the peer.
538  */
539 static int
540 send_join_noficiation (void *cls,
541                        const GNUNET_HashCode *key,
542                        void *value)
543 {
544   struct ChatClient *entry = cls;
545   struct ConnectedPeer *cp = value;
546   struct GNUNET_PeerIdentity pid;
547   size_t msg_size;
548
549   GNUNET_PEER_resolve (cp->pid, &pid);
550 #if DEBUG_CHAT_SERVICE
551   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
552               "Sending join notification to `%s'\n", GNUNET_i2s (&pid));
553 #endif
554   msg_size = sizeof (struct P2PJoinNotificationMessage) +
555     strlen (entry->room) + 
556     entry->meta_len;
557   if (NULL == GNUNET_CORE_notify_transmit_ready (core,
558                                                  GNUNET_NO,
559                                                  1,
560                                                  MAX_TRANSMIT_DELAY,
561                                                  &pid,
562                                                  msg_size,
563                                                  &transmit_join_notification_to_peer,
564                                                  entry))
565     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
566                 _("Failed to queue a join notification\n"));
567   return GNUNET_YES;
568 }
569
570
571 /**
572  * A client asked for entering a chat room.  Add the new member to the list of
573  * clients and notify remaining room members.
574  *
575  * @param cls closure, NULL
576  * @param client identification of the client
577  * @param message the actual message
578  */
579 static void
580 handle_join_request (void *cls,
581                      struct GNUNET_SERVER_Client *client,
582                      const struct GNUNET_MessageHeader *message)
583 {
584   const struct JoinRequestMessage *jrmsg;
585   char *room_name;
586   const char *roomptr;
587   uint16_t header_size;
588   uint16_t meta_len;
589   uint16_t room_name_len;
590   struct ChatClient *new_entry;
591   struct ChatClient *entry;
592   struct JoinNotificationMessage *jnmsg;
593   struct JoinNotificationMessage *entry_jnmsg;
594
595   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Client sent a join request\n");
596   if (ntohs (message->size) <= sizeof (struct JoinRequestMessage))
597     {
598       GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Malformed message: wrong size\n");
599       GNUNET_break (0);
600       GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
601       return;
602     }
603   jrmsg = (const struct JoinRequestMessage *) message;
604   header_size = ntohs (jrmsg->header.size);
605   room_name_len = ntohs (jrmsg->room_name_len);
606   if (header_size - sizeof (struct JoinRequestMessage) <=
607       room_name_len)
608     {
609       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
610                   "Malformed message: wrong length of the room name\n");
611       GNUNET_break (0);
612       GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
613       return;
614     }
615   meta_len =
616     header_size - sizeof (struct JoinRequestMessage) - room_name_len;
617   roomptr = (const char *) &jrmsg[1];
618   room_name = GNUNET_malloc (room_name_len + 1);
619   memcpy (room_name, roomptr, room_name_len);
620   room_name[room_name_len] = '\0';
621   new_entry = GNUNET_malloc (sizeof (struct ChatClient));
622   memset (new_entry, 0, sizeof (struct ChatClient));
623   new_entry->client = client;
624   new_entry->room = room_name;
625   new_entry->public_key = jrmsg->public_key;
626   new_entry->meta_len = meta_len;
627   if (meta_len > 0)
628     {
629       new_entry->member_info = GNUNET_malloc (meta_len);
630       memcpy (new_entry->member_info, &roomptr[room_name_len], meta_len);
631     }
632   else
633     new_entry->member_info = NULL;
634   GNUNET_CRYPTO_hash (&new_entry->public_key,
635                       sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
636                       &new_entry->id);
637   new_entry->msg_options = ntohl (jrmsg->msg_options);
638   new_entry->next = client_list_head;
639   client_list_head = new_entry;
640 #if DEBUG_CHAT_SERVICE
641   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
642               "Synchronizing room members between local clients\n");
643 #endif
644   jnmsg = GNUNET_malloc (sizeof (struct JoinNotificationMessage) + meta_len);
645   jnmsg->header.type = htons (GNUNET_MESSAGE_TYPE_CHAT_JOIN_NOTIFICATION);
646   jnmsg->header.size =
647     htons (sizeof (struct JoinNotificationMessage) + meta_len);
648   jnmsg->msg_options = jrmsg->msg_options;
649   jnmsg->public_key = new_entry->public_key;
650   memcpy (&jnmsg[1], &roomptr[room_name_len], meta_len);
651   GNUNET_SERVER_notification_context_add (nc, client);
652   entry = client_list_head;
653   while (NULL != entry)
654     {
655       if (0 == strcmp (room_name, entry->room))
656         {
657           if (NULL != entry->client)
658             GNUNET_SERVER_notification_context_unicast (nc,
659                                                         entry->client,
660                                                         &jnmsg->header,
661                                                         GNUNET_NO);
662           if (entry->client != client)
663             {
664               entry_jnmsg =
665                 GNUNET_malloc (sizeof (struct JoinNotificationMessage) +
666                                entry->meta_len);
667               entry_jnmsg->header.type =
668                 htons (GNUNET_MESSAGE_TYPE_CHAT_JOIN_NOTIFICATION);
669               entry_jnmsg->header.size =
670                 htons (sizeof (struct JoinNotificationMessage) +
671                        entry->meta_len);
672               entry_jnmsg->msg_options = entry->msg_options;
673               entry_jnmsg->public_key = entry->public_key;
674               memcpy (&entry_jnmsg[1], entry->member_info, entry->meta_len);
675               GNUNET_SERVER_notification_context_unicast (nc,
676                                                           client,
677                                                           &entry_jnmsg->header,
678                                                           GNUNET_NO);
679               GNUNET_free (entry_jnmsg);
680             }
681         }
682       entry = entry->next;
683     }
684 #if DEBUG_CHAT_SERVICE
685   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
686               "Broadcasting join notification to neighbour peers\n");
687 #endif
688   GNUNET_CONTAINER_multihashmap_iterate (connected_peers,
689                                          &send_join_noficiation,
690                                          new_entry);
691   GNUNET_SERVER_receive_done (client, GNUNET_OK);
692   GNUNET_free (jnmsg);
693 }
694
695 /**
696  * Transmit a confirmation receipt to the peer.
697  *
698  * @param cls closure, pointer to the 'struct P2PConfirmationReceiptMessage'
699  * @param size number of bytes available in buf
700  * @param buf where the callee should write the message
701  * @return number of bytes written to buf
702  */
703 static size_t
704 transmit_confirmation_receipt_to_peer (void *cls,
705                                        size_t size,
706                                        void *buf)
707 {
708   struct P2PConfirmationReceiptMessage *receipt = cls;
709   size_t msg_size;
710
711 #if DEBUG_CHAT_SERVICE
712   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
713               "Transmitting P2P confirmation receipt to '%s'\n",
714               GNUNET_h2s (&receipt->target));
715 #endif
716   if (buf == NULL)
717     {
718       /* client disconnected */
719 #if DEBUG_CHAT_SERVICE
720       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
721                   "Buffer is NULL, dropping the message\n");
722 #endif
723       return 0;
724     }
725   msg_size = sizeof (struct P2PConfirmationReceiptMessage);
726   GNUNET_assert (size >= msg_size);
727   memcpy (buf, receipt, msg_size);
728   GNUNET_free (receipt);
729   return msg_size;
730 }
731
732
733 /**
734  * Ask to send a confirmation receipt to the peer.
735  */
736 static int
737 send_confirmation_receipt (void *cls,
738                            const GNUNET_HashCode *key,
739                            void *value)
740 {
741   struct P2PConfirmationReceiptMessage *receipt = cls;
742   struct ConnectedPeer *cp = value;
743   struct GNUNET_PeerIdentity pid;
744   struct P2PConfirmationReceiptMessage *my_receipt;
745   size_t msg_size;
746
747   GNUNET_PEER_resolve (cp->pid, &pid);
748 #if DEBUG_CHAT_SERVICE
749   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
750               "Sending confirmation receipt to `%s'\n", GNUNET_i2s (&pid));
751 #endif
752   msg_size = sizeof (struct P2PConfirmationReceiptMessage);
753   my_receipt = GNUNET_memdup (receipt,
754                               sizeof (struct P2PConfirmationReceiptMessage));
755   if (NULL == GNUNET_CORE_notify_transmit_ready (core,
756                                                  GNUNET_YES,
757                                                  1,
758                                                  MAX_TRANSMIT_DELAY,
759                                                  &pid,
760                                                  msg_size,
761                                                  &transmit_confirmation_receipt_to_peer,
762                                                  my_receipt))
763     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
764                 _("Failed to queue a confirmation receipt\n"));
765   return GNUNET_YES;
766 }
767
768
769 /**
770  * A client sent a confirmation receipt.  Broadcast the receipt to all connected
771  * peers if the author of the original message is a local client.  Otherwise
772  * check the signature and notify the user if the signature is valid.
773  *
774  * @param cls closure, NULL
775  * @param client identification of the client
776  * @param message the actual message
777  */
778 static void
779 handle_acknowledge_request (void *cls,
780                             struct GNUNET_SERVER_Client *client,
781                             const struct GNUNET_MessageHeader *message)
782 {
783   const struct ConfirmationReceiptMessage *receipt;
784   struct ConfirmationReceiptMessage *crmsg;
785   struct P2PConfirmationReceiptMessage *p2p_crmsg;
786   struct ChatClient *target;
787   struct ChatClient *author;
788
789   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Client sent a confirmation receipt\n");
790   receipt = (const struct ConfirmationReceiptMessage *) message;
791   author = client_list_head;
792   while ((NULL != author) &&
793          (0 != memcmp (&receipt->author,
794                        &author->id,
795                        sizeof (GNUNET_HashCode))))
796     author = author->next;
797   if (NULL == author)
798     {
799       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
800                   "Unknown author of the original message\n");
801       GNUNET_break (0);
802       GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
803       return;
804     }
805   target = client_list_head;
806   while ((NULL != target) &&
807          (0 != memcmp (&receipt->target,
808                        &target->id,
809                        sizeof (GNUNET_HashCode))))
810     target = target->next;
811   if (NULL == target)
812     {
813       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
814                   "Unknown target of the confirmation receipt\n");
815       GNUNET_break (0);
816       GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
817       return;
818     }
819   if (NULL == author->client)
820     {
821       target->rcpt_sequence_number++;
822 #if DEBUG_CHAT_SERVICE
823       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
824                   "Broadcasting %s's receipt #%u to neighbour peers\n",
825                   GNUNET_h2s (&target->id), target->rcpt_sequence_number);
826 #endif
827       p2p_crmsg = GNUNET_malloc (sizeof (struct P2PConfirmationReceiptMessage));
828       p2p_crmsg->header.size = htons (sizeof (struct P2PConfirmationReceiptMessage));
829       p2p_crmsg->header.type = htons (GNUNET_MESSAGE_TYPE_CHAT_P2P_CONFIRMATION_RECEIPT);
830       p2p_crmsg->reserved = htonl (0);
831       p2p_crmsg->signature = receipt->signature;
832       p2p_crmsg->purpose = receipt->purpose;
833       p2p_crmsg->msg_sequence_number = receipt->sequence_number;
834       p2p_crmsg->timestamp = receipt->timestamp;
835       p2p_crmsg->target = receipt->target;
836       p2p_crmsg->author = receipt->author;
837       p2p_crmsg->content = receipt->content;
838       p2p_crmsg->sequence_number = htonl (target->rcpt_sequence_number);
839       GNUNET_CONTAINER_multihashmap_iterate (connected_peers,
840                                              &send_confirmation_receipt,
841                                              p2p_crmsg);
842       GNUNET_free (p2p_crmsg);
843     }
844   else
845     {
846 #if DEBUG_CHAT_SERVICE
847       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
848                   "Verifying signature of the receipt\n");
849 #endif
850       if (GNUNET_OK !=
851           GNUNET_CRYPTO_rsa_verify (GNUNET_SIGNATURE_PURPOSE_CHAT_RECEIPT,
852                                     &receipt->purpose,
853                                     &receipt->signature,
854                                     &target->public_key))
855         {
856           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
857                       "Invalid signature of the receipt\n");
858           GNUNET_break (0);
859           GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
860           return;
861         }
862 #if DEBUG_CHAT_SERVICE
863       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
864                   "Sending receipt to the client which sent the original message\n");
865 #endif
866       crmsg = GNUNET_memdup (receipt, sizeof (struct ConfirmationReceiptMessage));
867       crmsg->header.type = htons (GNUNET_MESSAGE_TYPE_CHAT_CONFIRMATION_NOTIFICATION);
868       GNUNET_SERVER_notification_context_unicast (nc,
869                                                   author->client,
870                                                   &crmsg->header,
871                                                   GNUNET_NO);
872       GNUNET_free (crmsg);
873     }
874   GNUNET_SERVER_receive_done (client, GNUNET_OK);
875 }
876
877
878 /**
879  * Transmit a leave notification to the peer.
880  *
881  * @param cls closure, pointer to the
882  *        'struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded'
883  * @param size number of bytes available in buf
884  * @param buf where the callee should write the message
885  * @return number of bytes written to buf
886  */
887 static size_t
888 transmit_leave_notification_to_peer (void *cls,
889                                      size_t size,
890                                      void *buf)
891 {
892   struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *public_key = cls;
893   struct P2PLeaveNotificationMessage *m = buf;
894   size_t msg_size;
895
896 #if DEBUG_CHAT_SERVICE
897   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
898               "Transmitting P2P leave notification\n");
899 #endif
900   if (buf == NULL)
901     {
902       /* client disconnected */
903 #if DEBUG_CHAT_SERVICE
904       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
905                   "Buffer is NULL, dropping the message\n");
906 #endif
907       return 0;
908     }
909   msg_size = sizeof (struct P2PLeaveNotificationMessage);
910   GNUNET_assert (size >= msg_size);
911   m = buf;
912   m->header.type = htons (GNUNET_MESSAGE_TYPE_CHAT_P2P_LEAVE_NOTIFICATION);
913   m->header.size = htons (msg_size);
914   m->reserved = htonl (0);
915   m->user = *public_key;
916   GNUNET_free (public_key);
917   return msg_size;
918 }
919
920
921 /**
922  * Ask to send a leave notification to the peer.
923  */
924 static int
925 send_leave_noficiation (void *cls,
926                         const GNUNET_HashCode *key,
927                         void *value) 
928 {
929   struct ChatClient *entry = cls;
930   struct ConnectedPeer *cp = value;
931   struct GNUNET_PeerIdentity pid;
932   struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *public_key;
933   size_t msg_size;
934
935   GNUNET_PEER_resolve (cp->pid, &pid);
936 #if DEBUG_CHAT_SERVICE
937   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
938               "Sending leave notification to `%s'\n", GNUNET_i2s (&pid));
939 #endif
940   msg_size = sizeof (struct P2PLeaveNotificationMessage);
941   public_key = GNUNET_memdup (&entry->public_key,
942                               sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded));
943   if (NULL == GNUNET_CORE_notify_transmit_ready (core,
944                                                  GNUNET_YES,
945                                                  1,
946                                                  MAX_TRANSMIT_DELAY,
947                                                  &pid,
948                                                  msg_size,
949                                                  &transmit_leave_notification_to_peer,
950                                                  public_key))
951     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
952                 _("Failed to queue a leave notification\n"));
953   return GNUNET_YES;
954 }
955
956
957 /**
958  * A client disconnected.  Remove all of its data structure entries and notify
959  * remaining room members.
960  *
961  * @param cls closure, NULL
962  * @param client identification of the client
963  */
964 static void
965 handle_client_disconnect (void *cls,
966                           struct GNUNET_SERVER_Client *client)
967 {
968   struct ChatClient *entry;
969   struct ChatClient *pos;
970   struct ChatClient *prev;
971   struct LeaveNotificationMessage lnmsg;
972
973   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Client disconnected\n");
974   pos = client_list_head;
975   prev = NULL;
976   while ((NULL != pos) && (pos->client != client))
977     {
978       prev = pos;
979       pos = pos->next;
980     }
981   if (NULL == pos)
982     {
983 #if DEBUG_CHAT_SERVICE
984       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
985                   "No such client. There is nothing to do\n");
986 #endif
987       return;
988     }
989   if (NULL == prev)
990     client_list_head = pos->next;
991   else
992     prev->next = pos->next;
993   entry = client_list_head;
994 #if DEBUG_CHAT_SERVICE
995   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
996               "Notifying local room members that the client has disconnected\n");
997 #endif
998   lnmsg.header.size = htons (sizeof (struct LeaveNotificationMessage));
999   lnmsg.header.type = htons (GNUNET_MESSAGE_TYPE_CHAT_LEAVE_NOTIFICATION);
1000   lnmsg.reserved = htonl (0);
1001   lnmsg.user = pos->public_key;
1002   while (NULL != entry)
1003     {
1004       if ((0 == strcmp (pos->room, entry->room)) &&
1005           (NULL != entry->client))
1006         {
1007           GNUNET_SERVER_notification_context_unicast (nc,
1008                                                       entry->client,
1009                                                       &lnmsg.header,
1010                                                       GNUNET_NO);
1011         }
1012       entry = entry->next;
1013     }
1014 #if DEBUG_CHAT_SERVICE
1015   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1016               "Broadcasting leave notification to neighbour peers\n");
1017 #endif
1018   GNUNET_CONTAINER_multihashmap_iterate (connected_peers,
1019                                          &send_leave_noficiation,
1020                                          pos);
1021   GNUNET_free (pos->room);
1022   GNUNET_free_non_null (pos->member_info);
1023   GNUNET_free (pos);
1024 }
1025
1026
1027 /**
1028  * Handle P2P join notification.
1029  *
1030  * @param cls closure, always NULL
1031  * @param other the other peer involved
1032  * @param message the actual message
1033  * @param atsi performance information
1034  * @return GNUNET_OK to keep the connection open,
1035  *         GNUNET_SYSERR to close it (signal serious error)
1036  */
1037 static int
1038 handle_p2p_join_notification (void *cls,
1039                               const struct GNUNET_PeerIdentity *other,
1040                               const struct GNUNET_MessageHeader *message,
1041                               const struct GNUNET_TRANSPORT_ATS_Information *atsi)
1042 {
1043   const struct P2PJoinNotificationMessage *p2p_jnmsg;
1044   char *room_name;
1045   const char *roomptr;
1046   uint16_t header_size;
1047   uint16_t meta_len;
1048   uint16_t room_name_len;
1049   struct ChatClient *new_entry;
1050   struct ChatClient *entry;
1051   struct JoinNotificationMessage *jnmsg;
1052   GNUNET_HashCode id;
1053
1054   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Got P2P join notification\n");
1055   if (ntohs (message->size) <= sizeof (struct P2PJoinNotificationMessage))
1056     {
1057       GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Malformed message: wrong size\n");
1058       GNUNET_break_op (0);
1059       return GNUNET_SYSERR;
1060     }
1061   p2p_jnmsg = (const struct P2PJoinNotificationMessage *) message;
1062   header_size = ntohs (p2p_jnmsg->header.size);
1063   room_name_len = ntohs (p2p_jnmsg->room_name_len);
1064   if (header_size - sizeof (struct P2PJoinNotificationMessage) <=
1065       room_name_len)
1066     {
1067       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1068                   "Malformed message: wrong length of the room name\n");
1069       GNUNET_break_op (0);
1070       return GNUNET_SYSERR;
1071     }
1072   GNUNET_CRYPTO_hash (&p2p_jnmsg->public_key,
1073                       sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
1074                       &id);
1075   entry = client_list_head;
1076   while (NULL != entry)
1077     {
1078       if (0 == memcmp (&entry->id, &id, sizeof (GNUNET_HashCode)))
1079         {
1080 #if DEBUG_CHAT_SERVICE
1081           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1082                       "The client has already joined. There is nothing to do\n");
1083 #endif
1084           return GNUNET_OK;
1085         }
1086       entry = entry->next;
1087     }
1088   meta_len =
1089     header_size - sizeof (struct P2PJoinNotificationMessage) - room_name_len;
1090   roomptr = (const char *) &p2p_jnmsg[1];
1091   room_name = GNUNET_malloc (room_name_len + 1);
1092   memcpy (room_name, roomptr, room_name_len);
1093   room_name[room_name_len] = '\0';
1094   new_entry = GNUNET_malloc (sizeof (struct ChatClient));
1095   memset (new_entry, 0, sizeof (struct ChatClient));
1096   new_entry->id = id;
1097   new_entry->client = NULL;
1098   new_entry->room = room_name;
1099   new_entry->public_key = p2p_jnmsg->public_key;
1100   new_entry->meta_len = meta_len;
1101   if (meta_len > 0)
1102     {
1103       new_entry->member_info = GNUNET_malloc (meta_len);
1104       memcpy (new_entry->member_info, &roomptr[room_name_len], meta_len);
1105     }
1106   else
1107     new_entry->member_info = NULL;
1108   new_entry->msg_options = ntohl (p2p_jnmsg->msg_options);
1109   new_entry->next = client_list_head;
1110   client_list_head = new_entry;
1111 #if DEBUG_CHAT_SERVICE
1112   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1113               "Notifying local room members that we have a new client\n");
1114 #endif
1115   jnmsg = GNUNET_malloc (sizeof (struct JoinNotificationMessage) + meta_len);
1116   jnmsg->header.type = htons (GNUNET_MESSAGE_TYPE_CHAT_JOIN_NOTIFICATION);
1117   jnmsg->header.size =
1118     htons (sizeof (struct JoinNotificationMessage) + meta_len);
1119   jnmsg->msg_options = p2p_jnmsg->msg_options;
1120   jnmsg->public_key = new_entry->public_key;
1121   memcpy (&jnmsg[1], &roomptr[room_name_len], meta_len);
1122   entry = client_list_head;
1123   while (NULL != entry)
1124     {
1125       if ((0 == strcmp (room_name, entry->room)) &&
1126           (NULL != entry->client))
1127         {
1128           GNUNET_SERVER_notification_context_unicast (nc,
1129                                                       entry->client,
1130                                                       &jnmsg->header,
1131                                                       GNUNET_NO);
1132         }
1133       entry = entry->next;
1134     }
1135 #if DEBUG_CHAT_SERVICE
1136   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1137               "Broadcasting join notification to neighbour peers\n");
1138 #endif
1139   GNUNET_CONTAINER_multihashmap_iterate (connected_peers,
1140                                          &send_join_noficiation,
1141                                          new_entry);
1142   GNUNET_free (jnmsg);
1143   return GNUNET_OK;
1144 }
1145
1146
1147 /**
1148  * Handle P2P leave notification.
1149  *
1150  * @param cls closure, always NULL
1151  * @param other the other peer involved
1152  * @param message the actual message
1153  * @param atsi performance information
1154  * @return GNUNET_OK to keep the connection open,
1155  *         GNUNET_SYSERR to close it (signal serious error)
1156  */
1157 static int
1158 handle_p2p_leave_notification (void *cls,
1159                                const struct GNUNET_PeerIdentity *other,
1160                                const struct GNUNET_MessageHeader *message,
1161                                const struct GNUNET_TRANSPORT_ATS_Information *atsi)
1162 {
1163   const struct P2PLeaveNotificationMessage *p2p_lnmsg;
1164   GNUNET_HashCode id;
1165   struct ChatClient *pos;
1166   struct ChatClient *prev;
1167   struct ChatClient *entry;
1168   struct LeaveNotificationMessage lnmsg;
1169
1170   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Got P2P leave notification\n");
1171   p2p_lnmsg = (const struct P2PLeaveNotificationMessage *) message;
1172   GNUNET_CRYPTO_hash (&p2p_lnmsg->user,
1173                       sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
1174                       &id);
1175   pos = client_list_head;
1176   prev = NULL;
1177   while (NULL != pos)
1178     {
1179       if (0 == memcmp (&pos->id, &id, sizeof (GNUNET_HashCode)))
1180         break;
1181       prev = pos;
1182       pos = pos->next;
1183     }
1184   if (NULL == pos)
1185     {
1186 #if DEBUG_CHAT_SERVICE
1187       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1188                   "No such client. There is nothing to do\n");
1189 #endif
1190       return GNUNET_OK;
1191     }
1192   if (NULL == prev)
1193     client_list_head = pos->next;
1194   else
1195     prev->next = pos->next;
1196 #if DEBUG_CHAT_SERVICE
1197   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1198               "Notifying local room members that the client has gone away\n");
1199 #endif
1200   lnmsg.header.size = htons (sizeof (struct LeaveNotificationMessage));
1201   lnmsg.header.type = htons (GNUNET_MESSAGE_TYPE_CHAT_LEAVE_NOTIFICATION);
1202   lnmsg.reserved = htonl (0);
1203   lnmsg.user = pos->public_key;
1204   entry = client_list_head;
1205   while (NULL != entry)
1206     {
1207       if (0 == strcmp (pos->room, entry->room) &&
1208           (NULL != entry->client))
1209         {
1210           GNUNET_SERVER_notification_context_unicast (nc,
1211                                                       entry->client,
1212                                                       &lnmsg.header,
1213                                                       GNUNET_NO);
1214         }
1215       entry = entry->next;
1216     }
1217 #if DEBUG_CHAT_SERVICE
1218   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1219               "Broadcasting leave notification to neighbour peers\n");
1220 #endif
1221   GNUNET_CONTAINER_multihashmap_iterate (connected_peers,
1222                                          &send_leave_noficiation,
1223                                          pos);
1224   GNUNET_free (pos->room);
1225   GNUNET_free_non_null (pos->member_info);
1226   GNUNET_free (pos);
1227   return GNUNET_OK;
1228 }
1229
1230
1231 /**
1232  * Handle P2P message notification.
1233  *
1234  * @param cls closure, always NULL
1235  * @param other the other peer involved
1236  * @param message the actual message
1237  * @param atsi performance information
1238  * @return GNUNET_OK to keep the connection open,
1239  *         GNUNET_SYSERR to close it (signal serious error)
1240  */
1241 static int
1242 handle_p2p_message_notification (void *cls,
1243                                  const struct GNUNET_PeerIdentity *other,
1244                                  const struct GNUNET_MessageHeader *message,
1245                                  const struct GNUNET_TRANSPORT_ATS_Information *atsi)
1246 {
1247   const struct P2PReceiveNotificationMessage *p2p_rnmsg;
1248   struct P2PReceiveNotificationMessage *my_p2p_rnmsg;
1249   struct ReceiveNotificationMessage *rnmsg;
1250   struct ChatClient *sender;
1251   struct ChatClient *pos;
1252   static GNUNET_HashCode all_zeros;
1253   int is_priv;
1254   int is_anon;
1255   uint16_t msg_len;
1256   uint16_t room_name_len;
1257   char *room_name = NULL;
1258   char *text;
1259
1260   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Got P2P message notification\n");
1261   if (ntohs (message->size) <= sizeof (struct P2PReceiveNotificationMessage))
1262     {
1263       GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Malformed message: wrong size\n");
1264       GNUNET_break_op (0);
1265       return GNUNET_SYSERR;
1266     }
1267   p2p_rnmsg = (const struct P2PReceiveNotificationMessage *) message;
1268   msg_len = ntohs (p2p_rnmsg->header.size) -
1269     sizeof (struct P2PReceiveNotificationMessage);
1270
1271   is_anon = (0 != (ntohl (p2p_rnmsg->msg_options) & GNUNET_CHAT_MSG_ANONYMOUS));
1272   if (is_anon)
1273     {
1274       room_name_len = ntohs (p2p_rnmsg->room_name_len);
1275       if (msg_len <= room_name_len)
1276         {
1277           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1278                       "Malformed message: wrong length of the room name\n");
1279           GNUNET_break_op (0);
1280           return GNUNET_SYSERR;
1281         }
1282       msg_len -= room_name_len;
1283       if (lookup_anonymous_message (p2p_rnmsg))
1284         {
1285 #if DEBUG_CHAT_SERVICE
1286           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1287                       "This anonymous message has already been handled.");
1288 #endif
1289           return GNUNET_OK;
1290         }
1291       remember_anonymous_message (p2p_rnmsg);
1292       room_name = GNUNET_malloc (room_name_len + 1);
1293       memcpy (room_name, (char *) &p2p_rnmsg[1], room_name_len);
1294       room_name[room_name_len] = '\0';
1295       text = (char *) &p2p_rnmsg[1] + room_name_len;
1296     }
1297   else
1298     {
1299   sender = client_list_head;
1300   while ((NULL != sender) &&
1301          (0 != memcmp (&sender->id,
1302                        &p2p_rnmsg->sender,
1303                        sizeof (GNUNET_HashCode))))
1304     sender = sender->next;
1305   if (NULL == sender)
1306     {
1307           /* not an error since the sender may have left before we got the
1308              message */
1309 #if DEBUG_CHAT_SERVICE
1310           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1311                   "Unknown source. Rejecting the message\n");
1312 #endif
1313           return GNUNET_OK;
1314     }
1315   if (sender->msg_sequence_number >= ntohl (p2p_rnmsg->sequence_number))
1316     {
1317 #if DEBUG_CHAT_SERVICE
1318       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1319                   "This message has already been handled."
1320                   " Sequence numbers (msg/sender): %u/%u\n",
1321                       ntohl (p2p_rnmsg->sequence_number),
1322                       sender->msg_sequence_number);
1323 #endif
1324       return GNUNET_OK;
1325     }
1326   sender->msg_sequence_number = ntohl (p2p_rnmsg->sequence_number);
1327       room_name = sender->room;
1328       text = (char *) &p2p_rnmsg[1];
1329     }
1330
1331 #if DEBUG_CHAT_SERVICE
1332   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1333               "Sending message to local room members\n");
1334 #endif
1335   rnmsg = GNUNET_malloc (sizeof (struct ReceiveNotificationMessage) + msg_len);
1336   rnmsg->header.size = htons (sizeof (struct ReceiveNotificationMessage) +
1337                               msg_len);
1338   rnmsg->header.type = htons (GNUNET_MESSAGE_TYPE_CHAT_MESSAGE_NOTIFICATION);
1339   rnmsg->msg_options = p2p_rnmsg->msg_options;
1340   rnmsg->sequence_number = p2p_rnmsg->sequence_number;
1341   rnmsg->reserved = htonl (0);
1342   rnmsg->timestamp = p2p_rnmsg->timestamp;
1343   is_priv = (0 != memcmp (&all_zeros,
1344                            &p2p_rnmsg->target, sizeof (GNUNET_HashCode)));
1345   if (is_priv)
1346     memcpy (&rnmsg->encrypted_key,
1347             &p2p_rnmsg->encrypted_key,
1348             sizeof (struct GNUNET_CRYPTO_RsaEncryptedData));
1349   rnmsg->sender = p2p_rnmsg->sender;
1350   memcpy (&rnmsg[1], text, msg_len);
1351   pos = client_list_head;
1352   while (NULL != pos)
1353     {
1354       if ((0 == strcmp (room_name, pos->room)) &&
1355           (NULL != pos->client))
1356         {
1357           if (((!is_priv) ||
1358                (0 == memcmp (&p2p_rnmsg->target,
1359                              &pos->id,
1360                              sizeof (GNUNET_HashCode)))) &&
1361               (0 == (ntohl (p2p_rnmsg->msg_options) & (~pos->msg_options))))
1362             {
1363               GNUNET_SERVER_notification_context_unicast (nc,
1364                                                           pos->client,
1365                                                           &rnmsg->header,
1366                                                           GNUNET_NO);
1367             }
1368         }
1369       pos = pos->next;
1370     }
1371   if (is_anon)
1372     GNUNET_free (room_name);
1373 #if DEBUG_CHAT_SERVICE
1374   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1375               "Broadcasting message notification to neighbour peers\n");
1376 #endif
1377   my_p2p_rnmsg = GNUNET_memdup (p2p_rnmsg, ntohs (p2p_rnmsg->header.size));
1378   GNUNET_CONTAINER_multihashmap_iterate (connected_peers,
1379                                          &send_message_noficiation,
1380                                          my_p2p_rnmsg);
1381   GNUNET_free (rnmsg);
1382   return GNUNET_OK;
1383 }
1384
1385
1386 /**
1387  * Handle P2P sync request.
1388  *
1389  * @param cls closure, always NULL
1390  * @param other the other peer involved
1391  * @param message the actual message
1392  * @param atsi performance information
1393  * @return GNUNET_OK to keep the connection open,
1394  *         GNUNET_SYSERR to close it (signal serious error)
1395  */
1396 static int
1397 handle_p2p_sync_request (void *cls,
1398                          const struct GNUNET_PeerIdentity *other,
1399                          const struct GNUNET_MessageHeader *message,
1400                          const struct GNUNET_TRANSPORT_ATS_Information *atsi)
1401 {
1402   struct ChatClient *entry;
1403   struct GNUNET_CORE_TransmitHandle *th;
1404   size_t msg_size;
1405
1406   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Got P2P sync request\n");
1407 #if DEBUG_CHAT_SERVICE
1408   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1409               "Notifying the requester of all known clients\n");
1410 #endif
1411   entry = client_list_head;
1412   while (NULL != entry)
1413     {
1414       msg_size = sizeof (struct P2PJoinNotificationMessage) +
1415         strlen (entry->room) + 
1416         entry->meta_len;
1417       th = GNUNET_CORE_notify_transmit_ready (core,
1418                                               GNUNET_NO,
1419                                               1,
1420                                               MAX_TRANSMIT_DELAY,
1421                                               other,
1422                                               msg_size,
1423                                               &transmit_join_notification_to_peer,
1424                                               entry);
1425       GNUNET_assert (NULL != th);
1426       entry = entry->next;
1427     }
1428   return GNUNET_OK;
1429 }
1430
1431
1432 /**
1433  * Handle P2P confirmation receipt.
1434  *
1435  * @param cls closure, always NULL
1436  * @param other the other peer involved
1437  * @param message the actual message
1438  * @param atsi performance information
1439  * @return GNUNET_OK to keep the connection open,
1440  *         GNUNET_SYSERR to close it (signal serious error)
1441  */
1442 static int
1443 handle_p2p_confirmation_receipt (void *cls,
1444                                  const struct GNUNET_PeerIdentity *other,
1445                                  const struct GNUNET_MessageHeader *message,
1446                                  const struct GNUNET_TRANSPORT_ATS_Information *atsi)
1447 {
1448   const struct P2PConfirmationReceiptMessage *p2p_crmsg;
1449   struct P2PConfirmationReceiptMessage *my_p2p_crmsg;
1450   struct ConfirmationReceiptMessage *crmsg;
1451   struct ChatClient *target;
1452   struct ChatClient *author;
1453
1454   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Got P2P confirmation receipt\n");
1455   p2p_crmsg = (const struct P2PConfirmationReceiptMessage *) message;
1456   target = client_list_head;
1457   while ((NULL != target) &&
1458          (0 != memcmp (&target->id,
1459                        &p2p_crmsg->target,
1460                        sizeof (GNUNET_HashCode))))
1461     target = target->next;
1462   if (NULL == target)
1463     {
1464       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1465                   "Unknown source of the receipt. Rejecting the message\n");
1466       GNUNET_break_op (0);
1467       return GNUNET_SYSERR;
1468     }
1469   if (target->rcpt_sequence_number >= ntohl (p2p_crmsg->sequence_number))
1470     {
1471 #if DEBUG_CHAT_SERVICE
1472       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1473                   "This receipt has already been handled."
1474                   " Sequence numbers (msg/sender): %u/%u\n",
1475                   ntohl (p2p_crmsg->sequence_number), target->rcpt_sequence_number);
1476 #endif
1477       return GNUNET_OK;
1478     }
1479   target->rcpt_sequence_number = ntohl (p2p_crmsg->sequence_number);
1480   author = client_list_head;
1481   while ((NULL != author) &&
1482          (0 != memcmp (&author->id,
1483                        &p2p_crmsg->author,
1484                        sizeof (GNUNET_HashCode))))
1485     author = author->next;
1486   if (NULL == author)
1487     {
1488       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1489                   "Unknown addressee. Rejecting the receipt\n");
1490       GNUNET_break_op (0);
1491       return GNUNET_SYSERR;
1492     }
1493
1494   if (NULL == author->client)
1495     {
1496 #if DEBUG_CHAT_SERVICE
1497       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1498                   "The author of the original message is not a local client."
1499                   " Broadcasting receipt to neighbour peers\n");
1500 #endif
1501       my_p2p_crmsg = GNUNET_memdup (p2p_crmsg, sizeof (struct P2PConfirmationReceiptMessage));
1502       GNUNET_CONTAINER_multihashmap_iterate (connected_peers,
1503                                              &send_confirmation_receipt,
1504                                              my_p2p_crmsg);
1505       GNUNET_free (my_p2p_crmsg);
1506     }
1507   else
1508     {
1509 #if DEBUG_CHAT_SERVICE
1510       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1511                   "The author of the original message is a local client."
1512                   " Verifying signature of the receipt\n");
1513 #endif
1514       crmsg = GNUNET_malloc (sizeof (struct ConfirmationReceiptMessage));
1515       crmsg->header.size = htons (sizeof (struct ConfirmationReceiptMessage));
1516       crmsg->header.type = htons (GNUNET_MESSAGE_TYPE_CHAT_CONFIRMATION_NOTIFICATION);
1517       crmsg->signature = p2p_crmsg->signature;
1518       crmsg->purpose = p2p_crmsg->purpose;
1519       crmsg->sequence_number = p2p_crmsg->msg_sequence_number;
1520       crmsg->reserved2 = 0;
1521       crmsg->timestamp = p2p_crmsg->timestamp;
1522       crmsg->target = p2p_crmsg->target;
1523       crmsg->author = p2p_crmsg->author;
1524       crmsg->content = p2p_crmsg->content;
1525       if (GNUNET_OK !=
1526           GNUNET_CRYPTO_rsa_verify (GNUNET_SIGNATURE_PURPOSE_CHAT_RECEIPT,
1527                                     &crmsg->purpose,
1528                                     &crmsg->signature,
1529                                     &target->public_key))
1530         {
1531           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1532                       "Invalid signature of the receipt\n");
1533           GNUNET_break_op (0);
1534           return GNUNET_SYSERR;
1535         }
1536 #if DEBUG_CHAT_SERVICE
1537       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1538                   "The author of the original message is a local client."
1539                   " Sending receipt to the client\n");
1540 #endif
1541       GNUNET_SERVER_notification_context_unicast (nc,
1542                                                   author->client,
1543                                                   &crmsg->header,
1544                                                   GNUNET_NO);
1545       GNUNET_free (crmsg);
1546     }
1547   return GNUNET_OK;
1548 }
1549
1550
1551 /**
1552  * Transmit a sync request to the peer.
1553  *
1554  * @param cls closure, NULL
1555  * @param size number of bytes available in buf
1556  * @param buf where the callee should write the message
1557  * @return number of bytes written to buf
1558  */
1559 static size_t
1560 transmit_sync_request_to_peer (void *cls,
1561                                size_t size,
1562                                void *buf)
1563 {
1564   struct GNUNET_MessageHeader *m = buf;
1565   size_t msg_size;
1566
1567 #if DEBUG_CHAT_SERVICE
1568   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Transmitting P2P sync request\n");
1569 #endif
1570   msg_size = sizeof (struct GNUNET_MessageHeader);
1571   GNUNET_assert (size >= msg_size);
1572   GNUNET_assert (NULL != buf);
1573   m = buf;
1574   m->type = htons (GNUNET_MESSAGE_TYPE_CHAT_P2P_SYNC_REQUEST);
1575   m->size = htons (msg_size);
1576   return msg_size;
1577 }
1578
1579
1580 /**
1581  * Method called whenever a peer connects.
1582  *
1583  * @param cls closure
1584  * @param peer peer identity this notification is about
1585  * @param atsi performance data
1586  */
1587 static void 
1588 peer_connect_handler (void *cls,
1589                       const struct GNUNET_PeerIdentity *peer,
1590                       const struct GNUNET_TRANSPORT_ATS_Information *atsi)
1591 {
1592   struct ConnectedPeer *cp;
1593   struct GNUNET_CORE_TransmitHandle *th;
1594
1595   if (0 == memcmp (peer, me, sizeof (struct GNUNET_PeerIdentity)))
1596     return;
1597   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1598               "Peer connected: %s\n", GNUNET_i2s (peer));
1599   th = GNUNET_CORE_notify_transmit_ready (core,
1600                                           GNUNET_YES,
1601                                           1,
1602                                           MAX_TRANSMIT_DELAY,
1603                                           peer,
1604                                           sizeof (struct GNUNET_MessageHeader),
1605                                           &transmit_sync_request_to_peer,
1606                                           NULL);
1607   GNUNET_assert (NULL != th);
1608   cp = GNUNET_CONTAINER_multihashmap_get (connected_peers,
1609                                           &peer->hashPubKey);
1610   if (NULL != cp)
1611     {
1612       GNUNET_break (0);
1613       return;
1614     }
1615   cp = GNUNET_malloc (sizeof (struct ConnectedPeer));
1616   cp->pid = GNUNET_PEER_intern (peer);
1617   GNUNET_break (GNUNET_OK ==
1618                 GNUNET_CONTAINER_multihashmap_put (connected_peers,
1619                                                    &peer->hashPubKey,
1620                                                    cp,
1621                                                    GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
1622 }
1623
1624
1625 /**
1626  * Iterator to free peer entries.
1627  *
1628  * @param cls closure, unused
1629  * @param key current key code
1630  * @param value value in the hash map (peer entry)
1631  * @return GNUNET_YES (we should continue to iterate)
1632  */
1633 static int 
1634 clean_peer (void *cls,
1635             const GNUNET_HashCode * key,
1636             void *value)
1637 {
1638   struct ConnectedPeer *cp;
1639   const struct GNUNET_PeerIdentity *peer = (const struct GNUNET_PeerIdentity *) key;
1640
1641   cp = GNUNET_CONTAINER_multihashmap_get (connected_peers,
1642                                           &peer->hashPubKey);
1643   if (cp == NULL)
1644     return GNUNET_YES;
1645   GNUNET_break (GNUNET_YES ==
1646                 GNUNET_CONTAINER_multihashmap_remove (connected_peers,
1647                                                       &peer->hashPubKey,
1648                                                       cp));
1649   GNUNET_PEER_change_rc (cp->pid, -1);
1650   GNUNET_free (cp);
1651   return GNUNET_YES;
1652 }
1653
1654
1655 /**
1656  * Method called whenever a peer disconnects.
1657  *
1658  * @param cls closure, not used
1659  * @param peer peer identity this notification is about
1660  */
1661 static void
1662 peer_disconnect_handler (void *cls,
1663                          const struct GNUNET_PeerIdentity *peer)
1664 {
1665
1666   if (0 == memcmp (peer, me, sizeof (struct GNUNET_PeerIdentity)))
1667     return;
1668   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1669               "Peer disconnected: %s\n", GNUNET_i2s (peer));
1670   clean_peer (NULL, (const GNUNET_HashCode *) peer, NULL);
1671 }
1672
1673
1674 /**
1675  * Task run during shutdown.
1676  *
1677  * @param cls unused
1678  * @param tc unused
1679  */
1680 static void
1681 cleanup_task (void *cls,
1682               const struct GNUNET_SCHEDULER_TaskContext *tc)
1683 {
1684   struct AnonymousMessage *next_msg;
1685   struct ChatClient *next_client;
1686
1687   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Cleaning up\n");
1688   if (NULL != core)
1689     {
1690       GNUNET_CORE_disconnect (core);
1691       core = NULL;
1692     }
1693   if (NULL != nc)
1694     {
1695       GNUNET_SERVER_notification_context_destroy (nc);
1696       nc = NULL;
1697     }
1698   while (NULL != client_list_head)
1699     {
1700       next_client = client_list_head->next;
1701       GNUNET_free (client_list_head->room);
1702       GNUNET_free_non_null (client_list_head->member_info);
1703       GNUNET_free (client_list_head);
1704       client_list_head = next_client;
1705     }
1706   while (NULL != anonymous_list_head)
1707     {
1708       next_msg = anonymous_list_head->next;
1709       GNUNET_free (anonymous_list_head);
1710       anonymous_list_head = next_msg;
1711     }
1712   GNUNET_CONTAINER_multihashmap_iterate (connected_peers,
1713                                          &clean_peer,
1714                                          NULL);
1715   GNUNET_CONTAINER_multihashmap_destroy (connected_peers);
1716   connected_peers = NULL;
1717 }
1718
1719
1720 /**
1721  * To be called on core init/fail.
1722  *
1723  * @param cls closure, NULL
1724  * @param server handle to the server for this service
1725  * @param my_identity the public identity of this peer
1726  * @param publicKey the public key of this peer
1727  */
1728 static void
1729 core_init (void *cls,
1730            struct GNUNET_CORE_Handle *server,
1731            const struct GNUNET_PeerIdentity *my_identity,
1732            const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *publicKey)
1733 {
1734   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Core initialized\n");
1735   me = my_identity;
1736 }
1737
1738
1739 /**
1740  * Process chat requests.
1741  *
1742  * @param cls closure, NULL
1743  * @param server the initialized server
1744  * @param c configuration to use
1745  */
1746 static void
1747 run (void *cls,
1748      struct GNUNET_SERVER_Handle *server,
1749      const struct GNUNET_CONFIGURATION_Handle *c)
1750 {
1751   static const struct GNUNET_SERVER_MessageHandler handlers[] =
1752     {
1753       { &handle_join_request, NULL,
1754         GNUNET_MESSAGE_TYPE_CHAT_JOIN_REQUEST, 0 },
1755       { &handle_transmit_request, NULL,
1756         GNUNET_MESSAGE_TYPE_CHAT_TRANSMIT_REQUEST, 0 },
1757       { &handle_acknowledge_request, NULL,
1758         GNUNET_MESSAGE_TYPE_CHAT_CONFIRMATION_RECEIPT,
1759         sizeof (struct ConfirmationReceiptMessage) },
1760       { NULL, NULL, 0, 0 }
1761     };
1762   static const struct GNUNET_CORE_MessageHandler p2p_handlers[] =
1763     {
1764       { &handle_p2p_join_notification,
1765         GNUNET_MESSAGE_TYPE_CHAT_P2P_JOIN_NOTIFICATION, 0 },
1766       { &handle_p2p_leave_notification,
1767         GNUNET_MESSAGE_TYPE_CHAT_P2P_LEAVE_NOTIFICATION,
1768         sizeof (struct P2PLeaveNotificationMessage) },
1769       { &handle_p2p_message_notification,
1770         GNUNET_MESSAGE_TYPE_CHAT_P2P_MESSAGE_NOTIFICATION, 0 },
1771       { &handle_p2p_sync_request,
1772         GNUNET_MESSAGE_TYPE_CHAT_P2P_SYNC_REQUEST,
1773         sizeof (struct GNUNET_MessageHeader) },
1774       { &handle_p2p_confirmation_receipt,
1775         GNUNET_MESSAGE_TYPE_CHAT_P2P_CONFIRMATION_RECEIPT,
1776         sizeof (struct P2PConfirmationReceiptMessage) },
1777       { NULL, 0, 0 }
1778     };
1779
1780   GNUNET_log_setup ("gnunet-service-chat",
1781 #if DEBUG_CHAT_SERVICE
1782                     "DEBUG",
1783 #else
1784                     "WARNING",
1785 #endif
1786                     NULL);
1787   cfg = c;
1788   nc = GNUNET_SERVER_notification_context_create (server, 16);
1789   connected_peers = GNUNET_CONTAINER_multihashmap_create (EXPECTED_NEIGHBOUR_COUNT);
1790   GNUNET_SERVER_add_handlers (server, handlers);
1791   core = GNUNET_CORE_connect (cfg,
1792                               QUEUE_SIZE,
1793                               NULL,
1794                               &core_init,
1795                               &peer_connect_handler,
1796                               &peer_disconnect_handler,
1797                               NULL,
1798                               NULL, GNUNET_NO,
1799                               NULL, GNUNET_NO,
1800                               p2p_handlers);
1801   GNUNET_SERVER_disconnect_notify (server, 
1802                                    &handle_client_disconnect,
1803                                    NULL);
1804   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
1805                                 &cleanup_task,
1806                                 NULL);
1807 }
1808
1809
1810 /**
1811  * The main function for the chat service.
1812  *
1813  * @param argc number of arguments from the command line
1814  * @param argv command line arguments
1815  * @return 0 ok, 1 on error
1816  */
1817 int
1818 main (int argc, char *const *argv)
1819 {
1820   return (GNUNET_OK ==
1821           GNUNET_SERVICE_run (argc,
1822                               argv,
1823                               "chat",
1824                               GNUNET_SERVICE_OPTION_NONE,
1825                               &run, NULL)) ? 0 : 1;
1826 }
1827
1828 /* end of gnunet-service-chat.c */