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