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