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