fix for 1685
[oweals/gnunet.git] / src / chat / chat.c
1 /*
2      This file is part of GNUnet.
3      (C) 2008, 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/chat.c
23  * @brief convenience API for sending and receiving chat messages
24  * @author Christian Grothoff
25  * @author Nathan Evans
26  * @author Vitaly Minko
27  */
28
29 #include "platform.h"
30 #include "gnunet_constants.h"
31 #include "gnunet_protocols.h"
32 #include "gnunet_signatures.h"
33 #include "chat.h"
34
35 #define DEBUG_CHAT GNUNET_NO
36 #define NICK_IDENTITY_PREFIX ".chat_identity_"
37
38
39 /**
40  * Handle for a chat room.
41  */
42 struct GNUNET_CHAT_Room
43 {
44   struct GNUNET_CLIENT_Connection *client;
45
46   const struct GNUNET_CONFIGURATION_Handle *cfg;
47
48   struct GNUNET_CONTAINER_MetaData *member_info;
49
50   char *room_name;
51
52   struct GNUNET_CRYPTO_RsaPrivateKey *my_private_key;
53
54   struct MemberList *members;
55
56   int is_joined;
57
58   GNUNET_CHAT_JoinCallback join_callback;
59
60   void *join_callback_cls;
61
62   GNUNET_CHAT_MessageCallback message_callback;
63
64   void *message_callback_cls;
65
66   GNUNET_CHAT_MemberListCallback member_list_callback;
67
68   void *member_list_callback_cls;
69
70   GNUNET_CHAT_MessageConfirmation confirmation_callback;
71
72   void *confirmation_cls;
73
74   uint32_t sequence_number;
75
76   uint32_t msg_options;
77
78 };
79
80 /**
81  * Linked list of members in the chat room.
82  */
83 struct MemberList
84 {
85   struct MemberList *next;
86
87   /**
88    * Description of the member.
89    */
90   struct GNUNET_CONTAINER_MetaData *meta;
91
92   /**
93    * Member ID (pseudonym).
94    */
95   GNUNET_HashCode id;
96
97 };
98
99 /**
100  * Context for transmitting a send-message request.
101  */
102 struct GNUNET_CHAT_SendMessageContext
103 {
104   /**
105    * Handle for the chat room.
106    */
107   struct GNUNET_CHAT_Room *chat_room;
108
109   /**
110    * Message that we're sending.
111    */
112   char *message;
113
114   /**
115    * Options for the message.
116    */
117   enum GNUNET_CHAT_MsgOptions options;
118
119   /**
120    * Receiver of the message. NULL to send to everyone in the room.
121    */
122   const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *receiver;
123
124   /**
125    * Sequence id of the message.
126    */
127   uint32_t sequence_number;
128
129 };
130
131 /**
132  * Context for transmitting a confirmation receipt.
133  */
134 struct GNUNET_CHAT_SendReceiptContext
135 {
136   /**
137    * Handle for the chat room.
138    */
139   struct GNUNET_CHAT_Room *chat_room;
140
141   /**
142    * The original message that we're going to acknowledge.
143    */
144   struct ReceiveNotificationMessage *received_msg;
145
146 };
147
148 /**
149  * Ask client to send a join request.
150  */
151 static int
152 rejoin_room (struct GNUNET_CHAT_Room *chat_room);
153
154
155 /**
156  * Transmit a confirmation receipt to the chat service.
157  *
158  * @param cls closure, pointer to the 'struct GNUNET_CHAT_SendReceiptContext'
159  * @param size number of bytes available in buf
160  * @param buf where the callee should write the message
161  * @return number of bytes written to buf
162  */
163 static size_t
164 transmit_acknowledge_request (void *cls,
165                               size_t size, 
166                               void *buf)
167 {
168   struct GNUNET_CHAT_SendReceiptContext *src = cls;
169   struct ConfirmationReceiptMessage *receipt;
170   struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pub_key;
171   uint16_t msg_len;
172   size_t msg_size;
173
174   if (NULL == buf)
175     {
176       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
177                   _("Could not transmit confirmation receipt\n"));
178       return 0;
179     }
180 #if DEBUG_CHAT
181   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
182               "Transmitting confirmation receipt to the service\n");
183 #endif
184   msg_size = sizeof (struct ConfirmationReceiptMessage);
185   GNUNET_assert (size >= msg_size);
186   receipt = buf;
187   receipt->header.size = htons (msg_size);
188   receipt->header.type =
189     htons (GNUNET_MESSAGE_TYPE_CHAT_CONFIRMATION_RECEIPT);
190   receipt->reserved = htonl (0);
191   receipt->sequence_number = src->received_msg->sequence_number;
192   receipt->reserved2 = htonl (0);
193   receipt->timestamp = GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_get ());
194   GNUNET_CRYPTO_rsa_key_get_public (src->chat_room->my_private_key, &pub_key);
195   GNUNET_CRYPTO_hash (&pub_key,
196                       sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
197                       &receipt->target);
198   receipt->author = src->received_msg->sender;
199   receipt->purpose.purpose =
200     htonl (GNUNET_SIGNATURE_PURPOSE_CHAT_RECEIPT);
201   receipt->purpose.size =
202     htonl (msg_size -
203            sizeof (struct GNUNET_MessageHeader) -
204            sizeof (uint32_t) -
205            sizeof (struct GNUNET_CRYPTO_RsaSignature));
206   msg_len = ntohs (src->received_msg->header.size) -
207     sizeof (struct ReceiveNotificationMessage);
208   GNUNET_CRYPTO_hash (&src->received_msg[1], msg_len, &receipt->content);
209   GNUNET_assert (GNUNET_OK == 
210                  GNUNET_CRYPTO_rsa_sign (src->chat_room->my_private_key,
211                                          &receipt->purpose,
212                                          &receipt->signature));
213   GNUNET_free (src->received_msg);
214   GNUNET_free (src);
215   return msg_size;
216 }
217
218
219 /**
220  * Handles messages received from the service.  Calls the proper client
221  * callback.
222  */
223 static void
224 process_result (struct GNUNET_CHAT_Room *room,
225                 const struct GNUNET_MessageHeader *reply)
226 {
227   struct LeaveNotificationMessage *leave_msg;
228   struct JoinNotificationMessage *join_msg;
229   struct ReceiveNotificationMessage *received_msg;
230   struct ConfirmationReceiptMessage *receipt;
231   struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pkey;
232   GNUNET_HashCode id;
233   const GNUNET_HashCode *sender;
234   struct GNUNET_CONTAINER_MetaData *meta;
235   struct GNUNET_CHAT_SendReceiptContext *src;
236   struct MemberList *pos;
237   struct MemberList *prev;
238   struct GNUNET_CRYPTO_AesSessionKey key;
239   char decrypted_msg[MAX_MESSAGE_LENGTH];
240   uint16_t size;
241   uint16_t meta_len;
242   uint16_t msg_len;
243   char *message_content;
244
245   size = ntohs (reply->size);
246   switch (ntohs (reply->type))
247     {
248     case GNUNET_MESSAGE_TYPE_CHAT_JOIN_NOTIFICATION:
249 #if DEBUG_CHAT
250       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got a join notification\n");
251 #endif
252       if (size < sizeof (struct JoinNotificationMessage))
253         {
254           GNUNET_break (0);
255           return;
256         }
257       join_msg = (struct JoinNotificationMessage *) reply;
258       meta_len = size - sizeof (struct JoinNotificationMessage);
259       meta =
260         GNUNET_CONTAINER_meta_data_deserialize ((const char *) &join_msg[1],
261                                                 meta_len);
262       if (NULL == meta)
263         {
264           GNUNET_break (0);
265           return;
266         }
267       pos = GNUNET_malloc (sizeof (struct MemberList));
268       pos->meta = meta;
269       GNUNET_CRYPTO_hash (&join_msg->public_key,
270                           sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
271                           &pos->id);
272       GNUNET_PSEUDONYM_add (room->cfg, &pos->id, meta);
273       pos->next = room->members;
274       room->members = pos;
275       if (GNUNET_NO == room->is_joined)
276         {
277           GNUNET_CRYPTO_rsa_key_get_public (room->my_private_key, &pkey);
278           if (0 == memcmp (&join_msg->public_key,
279                            &pkey,
280                            sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded)))
281             {
282               room->join_callback (room->join_callback_cls);
283               room->is_joined = GNUNET_YES;
284             }
285           else
286             {
287               GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
288                           _("The current user must be the the first one joined\n"));
289               GNUNET_break (0);
290               return;
291             }
292         }
293       else 
294       room->member_list_callback (room->member_list_callback_cls,
295                                   meta, &join_msg->public_key,
296                                   ntohl (join_msg->msg_options));
297       break;
298     case GNUNET_MESSAGE_TYPE_CHAT_LEAVE_NOTIFICATION:
299 #if DEBUG_CHAT
300       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got a leave notification\n");
301 #endif
302       if (size < sizeof (struct LeaveNotificationMessage))
303         {
304           GNUNET_break (0);
305           return;
306         }
307       leave_msg = (struct LeaveNotificationMessage *) reply;
308       room->member_list_callback (room->member_list_callback_cls,
309                                   NULL, &leave_msg->user,
310                                   GNUNET_CHAT_MSG_OPTION_NONE);
311       GNUNET_CRYPTO_hash (&leave_msg->user,
312                           sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
313                           &id);
314       prev = NULL;
315       pos = room->members;
316       while ((NULL != pos) &&
317              (0 != memcmp (&pos->id, &id, sizeof (GNUNET_HashCode))))
318         {
319           prev = pos;
320           pos = pos->next;
321         }
322       GNUNET_assert (NULL != pos);
323       if (NULL == prev)
324         room->members = pos->next;
325       else
326         prev->next = pos->next;
327       GNUNET_CONTAINER_meta_data_destroy (pos->meta);
328       GNUNET_free (pos);
329       break;
330     case GNUNET_MESSAGE_TYPE_CHAT_MESSAGE_NOTIFICATION:
331 #if DEBUG_CHAT
332       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got a message notification\n");
333 #endif
334       if (size <= sizeof (struct ReceiveNotificationMessage))
335         {
336           GNUNET_break (0);
337           return;
338         }
339       received_msg = (struct ReceiveNotificationMessage *) reply;
340       if (0 !=
341           (ntohl (received_msg->msg_options) & GNUNET_CHAT_MSG_ACKNOWLEDGED))
342         {
343           src = GNUNET_malloc (sizeof (struct GNUNET_CHAT_SendReceiptContext));
344           src->chat_room = room;
345           src->received_msg = GNUNET_memdup (received_msg, size);
346           GNUNET_CLIENT_notify_transmit_ready (room->client,
347                                                sizeof (struct ConfirmationReceiptMessage),
348                                                GNUNET_CONSTANTS_SERVICE_TIMEOUT,
349                                                GNUNET_YES,
350                                                &transmit_acknowledge_request,
351                                                src);
352         }
353       msg_len = size - sizeof (struct ReceiveNotificationMessage);
354       if (0 !=
355           (ntohl (received_msg->msg_options) & GNUNET_CHAT_MSG_PRIVATE))
356         {
357           if (-1 == GNUNET_CRYPTO_rsa_decrypt (room->my_private_key,
358                                                &received_msg->encrypted_key,
359                                                &key,
360                                                sizeof (struct GNUNET_CRYPTO_AesSessionKey)))
361             {
362               GNUNET_break (0);
363               return;
364             }
365           msg_len = GNUNET_CRYPTO_aes_decrypt (&received_msg[1],
366                                                msg_len,
367                                                &key,
368                                                (const struct GNUNET_CRYPTO_AesInitializationVector *) INITVALUE,
369                                                decrypted_msg);
370           message_content = decrypted_msg;
371         }
372       else
373         {
374           message_content = GNUNET_malloc (msg_len + 1);
375           memcpy (message_content, &received_msg[1], msg_len);
376         }
377       message_content[msg_len] = '\0';
378       if (0 != (ntohl (received_msg->msg_options) & GNUNET_CHAT_MSG_ANONYMOUS))
379         {
380           sender = NULL;
381           meta = NULL;
382         }
383       else
384         {
385       pos = room->members;
386       while ((NULL != pos) &&
387              (0 != memcmp (&pos->id,
388                            &received_msg->sender,
389                            sizeof (GNUNET_HashCode))))
390         pos = pos->next;
391       GNUNET_assert (NULL != pos);
392           sender = &received_msg->sender;
393           meta = pos->meta;
394         }
395       room->message_callback (room->message_callback_cls,
396                               room,
397                               sender,
398                               meta,
399                               message_content,
400                               GNUNET_TIME_absolute_ntoh (received_msg->timestamp),
401                               ntohl (received_msg->msg_options));
402       if (message_content != decrypted_msg)
403         GNUNET_free (message_content);
404       break;
405     case GNUNET_MESSAGE_TYPE_CHAT_CONFIRMATION_NOTIFICATION:
406 #if DEBUG_CHAT
407       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got a confirmation receipt\n");
408 #endif
409       if (size < sizeof (struct ConfirmationReceiptMessage))
410         {
411           GNUNET_break (0);
412           return;
413         }
414       receipt = (struct ConfirmationReceiptMessage *) reply;
415       if (NULL != room->confirmation_callback)
416         room->confirmation_callback (room->confirmation_cls,
417                                      room,
418                                      ntohl (receipt->sequence_number),
419                                      GNUNET_TIME_absolute_ntoh (receipt->timestamp),
420                                      &receipt->target);
421       break;
422     default:
423       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
424                   _("Unknown message type: '%u'\n"), ntohs (reply->type));
425       GNUNET_break_op (0);
426       break;
427     }
428 }
429
430
431 /**
432  * Listen for incoming messages on this chat room.  Also, support servers going
433  * away/coming back (i.e. rejoin chat room to keep server state up to date).
434  *
435  * @param cls closure, pointer to the 'struct GNUNET_CHAT_Room'
436  * @param msg message received, NULL on timeout or fatal error
437  */
438 static void 
439 receive_results (void *cls,
440                  const struct GNUNET_MessageHeader *msg)
441 {
442   struct GNUNET_CHAT_Room *chat_room = cls;
443
444 #if DEBUG_CHAT
445   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got a message from the service\n");
446 #endif
447   if (0 != (GNUNET_SCHEDULER_REASON_SHUTDOWN & GNUNET_SCHEDULER_get_reason ()))
448     return;
449   if (NULL == msg)
450     {
451       GNUNET_break (0);
452       rejoin_room (chat_room);
453       return;
454     }
455   process_result (chat_room, msg);
456   if (NULL == chat_room->client)
457     return; /* fatal error */
458   /* continue receiving */
459   GNUNET_CLIENT_receive (chat_room->client,
460                          &receive_results,
461                          chat_room,
462                          GNUNET_TIME_UNIT_FOREVER_REL);
463 }
464
465
466 /**
467  * Read existing private key from file or create a new one if it does not exist
468  * yet.
469  * Returns the private key on success, NULL on error.
470  */
471 static struct GNUNET_CRYPTO_RsaPrivateKey *
472 init_private_key (const struct GNUNET_CONFIGURATION_Handle *cfg,
473                   const char *nick_name)
474 {
475   char *home;
476   char *keyfile;
477   struct GNUNET_CRYPTO_RsaPrivateKey *privKey;
478
479 #if DEBUG_CHAT
480   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Initializing private key\n");
481 #endif
482   if (GNUNET_OK !=
483       GNUNET_CONFIGURATION_get_value_filename (cfg,
484                                                "chat",
485                                                "HOME",
486                                                &home))
487     {
488       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
489                   _("Configuration option `%s' in section `%s' missing\n"),
490                   "HOME",
491                   "chat");
492       return NULL;
493     }
494   GNUNET_DISK_directory_create (home);
495   if (GNUNET_OK != GNUNET_DISK_directory_test (home))
496     {
497       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
498                   _("Failed to access chat home directory `%s'\n"),
499                   home);
500       GNUNET_free (home);
501       return NULL;
502     }
503   /* read or create private key */
504   keyfile =
505     GNUNET_malloc (strlen (home) + strlen (NICK_IDENTITY_PREFIX) +
506                    strlen (nick_name) + 2);
507   strcpy (keyfile, home);
508   GNUNET_free (home);
509   if (keyfile[strlen (keyfile) - 1] != DIR_SEPARATOR)
510     strcat (keyfile, DIR_SEPARATOR_STR);
511   strcat (keyfile, NICK_IDENTITY_PREFIX);
512   strcat (keyfile, nick_name);
513   privKey = GNUNET_CRYPTO_rsa_key_create_from_file (keyfile);
514   if (NULL == privKey)
515     {
516       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
517                   _("Failed to create/open key in file `%s'\n"),
518                   keyfile);
519     }
520   GNUNET_free (keyfile);
521   return privKey;
522 }
523
524
525 /**
526  * Transmit a join request to the chat service.
527  *
528  * @param cls closure, pointer to the 'struct GNUNET_CHAT_Room'
529  * @param size number of bytes available in buf
530  * @param buf where the callee should write the message
531  * @return number of bytes written to buf
532  */
533 static size_t
534 transmit_join_request (void *cls,
535                        size_t size, 
536                        void *buf)
537 {
538   struct GNUNET_CHAT_Room *chat_room = cls;
539   struct JoinRequestMessage *join_msg;
540   char *room;
541   char *meta;
542   size_t room_len;
543   ssize_t meta_len;
544   size_t size_of_join;
545
546   if (NULL == buf)
547     {
548 #if DEBUG_CHAT
549       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
550                   "Could not transmit join request, retrying...\n");
551 #endif
552       rejoin_room (chat_room);
553       return 0;
554     }
555 #if DEBUG_CHAT
556   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
557               "Transmitting join request to the service\n");
558 #endif
559   room_len = strlen (chat_room->room_name);
560   meta_len = GNUNET_CONTAINER_meta_data_get_serialized_size (chat_room->member_info);
561   size_of_join = sizeof (struct JoinRequestMessage) + meta_len + room_len;
562   GNUNET_assert (size >= size_of_join);
563   join_msg = buf;
564   join_msg->header.size = htons (size);
565   join_msg->header.type = htons (GNUNET_MESSAGE_TYPE_CHAT_JOIN_REQUEST);
566   join_msg->msg_options = htonl (chat_room->msg_options);
567   join_msg->room_name_len = htons (room_len);
568   join_msg->reserved = htons (0);
569   join_msg->reserved2 = htonl (0);
570   GNUNET_CRYPTO_rsa_key_get_public (chat_room->my_private_key, &join_msg->public_key);
571   room = (char *) &join_msg[1];
572   memcpy (room, chat_room->room_name, room_len);
573   meta = &room[room_len];
574   if (GNUNET_SYSERR ==
575       GNUNET_CONTAINER_meta_data_serialize (chat_room->member_info,
576                                             &meta,
577                                             meta_len,
578                                             GNUNET_CONTAINER_META_DATA_SERIALIZE_FULL))
579     {
580       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
581                   _("Could not serialize metadata\n"));
582       return 0;
583     }
584   GNUNET_CLIENT_receive (chat_room->client,
585                          &receive_results,
586                          chat_room,
587                          GNUNET_TIME_UNIT_FOREVER_REL);
588   return size_of_join;
589 }
590
591
592 /**
593  * Ask to send a join request.
594  */
595 static int
596 rejoin_room (struct GNUNET_CHAT_Room *chat_room)
597 {
598   size_t size_of_join;
599
600   size_of_join = sizeof (struct JoinRequestMessage) +
601     GNUNET_CONTAINER_meta_data_get_serialized_size (chat_room->member_info) +
602     strlen (chat_room->room_name);
603   if (NULL ==
604       GNUNET_CLIENT_notify_transmit_ready (chat_room->client,
605                                            size_of_join,
606                                            GNUNET_CONSTANTS_SERVICE_TIMEOUT,
607                                            GNUNET_YES,
608                                            &transmit_join_request,
609                                            chat_room))
610     return GNUNET_SYSERR;
611   return GNUNET_OK;
612 }
613
614
615 /**
616  * Leave a chat room.
617  */
618 void
619 GNUNET_CHAT_leave_room (struct GNUNET_CHAT_Room *chat_room)
620 {
621   struct MemberList *pos;
622
623 #if DEBUG_CHAT
624   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
625               "Leaving the room '%s'\n", chat_room->room_name);
626 #endif
627   GNUNET_CLIENT_disconnect (chat_room->client, GNUNET_NO);
628   GNUNET_free (chat_room->room_name);
629   GNUNET_CONTAINER_meta_data_destroy (chat_room->member_info);
630   GNUNET_CRYPTO_rsa_key_free (chat_room->my_private_key);
631   while (NULL != chat_room->members)
632     {
633       pos = chat_room->members;
634       chat_room->members = pos->next;
635       GNUNET_CONTAINER_meta_data_destroy (pos->meta);
636       GNUNET_free (pos);
637     }
638   GNUNET_free (chat_room);
639 }
640
641
642 /**
643  * Join a chat room.
644  *
645  * @param cfg configuration
646  * @param nick_name nickname of the user joining (used to
647  *                  determine which public key to use);
648  *                  the nickname should probably also
649  *                  be used in the member_info (as "EXTRACTOR_TITLE")
650  * @param member_info information about the joining member
651  * @param room_name name of the room
652  * @param msg_options message options of the joining user
653  * @param joinCallback function to call on successful join
654  * @param join_cls closure for joinCallback 
655  * @param messageCallback which function to call if a message has
656  *        been received?
657  * @param message_cls argument to callback
658  * @param memberCallback which function to call for join/leave notifications
659  * @param member_cls argument to callback
660  * @param confirmationCallback which function to call for confirmations (maybe NULL)
661  * @param confirmation_cls argument to callback
662  * @param me member ID (pseudonym)
663  * @return NULL on error
664  */
665 struct GNUNET_CHAT_Room *
666 GNUNET_CHAT_join_room (const struct GNUNET_CONFIGURATION_Handle *cfg,
667                        const char *nick_name,
668                        struct GNUNET_CONTAINER_MetaData *member_info,
669                        const char *room_name,
670                        enum GNUNET_CHAT_MsgOptions msg_options,
671                        GNUNET_CHAT_JoinCallback joinCallback,
672                        void *join_cls,
673                        GNUNET_CHAT_MessageCallback messageCallback,
674                        void *message_cls,
675                        GNUNET_CHAT_MemberListCallback memberCallback,
676                        void *member_cls,
677                        GNUNET_CHAT_MessageConfirmation confirmationCallback,
678                        void *confirmation_cls,
679                        GNUNET_HashCode *me)
680 {
681   struct GNUNET_CHAT_Room *chat_room;
682   struct GNUNET_CRYPTO_RsaPrivateKey *priv_key;
683   struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pub_key;
684   struct GNUNET_CLIENT_Connection *client;
685
686 #if DEBUG_CHAT
687   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Joining the room '%s'\n", room_name);
688 #endif
689   priv_key = init_private_key (cfg, nick_name);
690   if (NULL == priv_key)
691     return NULL;
692   GNUNET_CRYPTO_rsa_key_get_public (priv_key, &pub_key);
693   GNUNET_CRYPTO_hash (&pub_key,
694                       sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
695                       me);
696   GNUNET_PSEUDONYM_add (cfg, me, member_info);
697   client = GNUNET_CLIENT_connect ("chat", cfg);
698   if (NULL == client)
699     {
700       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
701                   _("Failed to connect to the chat service\n"));
702       return NULL;
703     }
704   if (NULL == joinCallback)
705     {
706       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
707                   _("Undefined mandatory parameter: joinCallback\n"));
708       return NULL;
709     }
710   if (NULL == messageCallback)
711     {
712       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
713                   _("Undefined mandatory parameter: messageCallback\n"));
714       return NULL;
715     }
716   if (NULL == memberCallback)
717     {
718       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
719                   _("Undefined mandatory parameter: memberCallback\n"));
720       return NULL;
721     }
722   chat_room = GNUNET_malloc (sizeof (struct GNUNET_CHAT_Room));
723   chat_room->msg_options = msg_options;
724   chat_room->room_name = GNUNET_strdup (room_name);
725   chat_room->member_info = GNUNET_CONTAINER_meta_data_duplicate (member_info);
726   chat_room->my_private_key = priv_key;
727   chat_room->is_joined = GNUNET_NO;
728   chat_room->join_callback = joinCallback;
729   chat_room->join_callback_cls = join_cls;
730   chat_room->message_callback = messageCallback;
731   chat_room->message_callback_cls = message_cls;
732   chat_room->member_list_callback = memberCallback;
733   chat_room->member_list_callback_cls = member_cls;
734   chat_room->confirmation_callback = confirmationCallback;
735   chat_room->confirmation_cls = confirmation_cls;
736   chat_room->cfg = cfg;
737   chat_room->client = client;
738   chat_room->members = NULL;
739   if (GNUNET_SYSERR == rejoin_room (chat_room))
740     {
741       GNUNET_CHAT_leave_room (chat_room);
742       return NULL;
743     }
744   return chat_room;
745 }
746
747
748 /**
749  * Transmit a send-message request to the chat service.
750  *
751  * @param cls closure, pointer to the 'struct GNUNET_CHAT_SendMessageContext'
752  * @param size number of bytes available in buf
753  * @param buf where the callee should write the message
754  * @return number of bytes written to buf
755  */
756 static size_t
757 transmit_send_request (void *cls,
758                        size_t size, 
759                        void *buf)
760 {
761   struct GNUNET_CHAT_SendMessageContext *smc = cls;
762   struct TransmitRequestMessage *msg_to_send;
763   size_t msg_size;
764
765   if (NULL == buf)
766     {
767 #if DEBUG_CHAT
768       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
769                   "Could not transmit a chat message\n");
770 #endif
771       return 0;
772     }
773 #if DEBUG_CHAT
774   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
775               "Transmitting a chat message to the service\n");
776 #endif
777   msg_size = strlen (smc->message) + sizeof (struct TransmitRequestMessage);
778   GNUNET_assert (size >= msg_size);
779   msg_to_send = buf;
780   msg_to_send->header.size = htons (msg_size);
781   msg_to_send->header.type = htons (GNUNET_MESSAGE_TYPE_CHAT_TRANSMIT_REQUEST);
782   msg_to_send->msg_options = htonl (smc->options);
783   msg_to_send->sequence_number = htonl (smc->sequence_number);
784   msg_to_send->timestamp =
785     GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_get ());
786   msg_to_send->reserved = htonl (0);
787   if (NULL == smc->receiver)
788     memset (&msg_to_send->target, 0, sizeof (GNUNET_HashCode));
789   else
790     GNUNET_CRYPTO_hash (smc->receiver,
791                         sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
792                         &msg_to_send->target);
793   memcpy (&msg_to_send[1], smc->message, strlen (smc->message));
794   /**
795    * Client don't encode private messages since public keys of other members are
796    * stored on the service side.
797    */
798   if (smc->options & GNUNET_CHAT_MSG_AUTHENTICATED)
799     {
800       msg_to_send->purpose.purpose =
801         htonl (GNUNET_SIGNATURE_PURPOSE_CHAT_MESSAGE);
802       msg_to_send->purpose.size =
803         htonl (msg_size -
804                sizeof (struct GNUNET_MessageHeader) -
805                sizeof (struct GNUNET_CRYPTO_RsaSignature));
806       GNUNET_assert (GNUNET_OK == 
807                      GNUNET_CRYPTO_rsa_sign (smc->chat_room->my_private_key,
808                                              &msg_to_send->purpose,
809                                              &msg_to_send->signature));
810     }
811   GNUNET_free (smc->message);
812   GNUNET_free (smc);
813   return msg_size;
814 }
815
816
817 /**
818  * Send a message.
819  *
820  * @param room handle for the chat room
821  * @param message message to be sent
822  * @param options options for the message
823  * @param receiver use NULL to send to everyone in the room
824  * @param sequence_number where to write the sequence id of the message
825  */
826 void
827 GNUNET_CHAT_send_message (struct GNUNET_CHAT_Room *room,
828                           const char *message,
829                           enum GNUNET_CHAT_MsgOptions options,
830                           const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *receiver,
831                           uint32_t *sequence_number)
832 {
833   size_t msg_size;
834   struct GNUNET_CHAT_SendMessageContext *smc;
835
836 #if DEBUG_CHAT
837   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending a message\n");
838 #endif
839   room->sequence_number++;
840   if (NULL != sequence_number)
841     *sequence_number = room->sequence_number;
842   smc = GNUNET_malloc (sizeof (struct GNUNET_CHAT_SendMessageContext));
843   smc->chat_room = room;
844   smc->message = GNUNET_strdup (message);
845   smc->options = options;
846   smc->receiver = receiver;
847   smc->sequence_number = room->sequence_number;
848   msg_size = strlen (message) + sizeof (struct TransmitRequestMessage);
849   GNUNET_CLIENT_notify_transmit_ready (room->client,
850                                        msg_size,
851                                        GNUNET_CONSTANTS_SERVICE_TIMEOUT,
852                                        GNUNET_YES,
853                                        &transmit_send_request,
854                                        smc);
855 }
856
857 /* end of chat.c */