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