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