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