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