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