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