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