run indent twice, it alternates between two 'canonical' forms, also run whitespace...
[oweals/gnunet.git] / src / chat / chat.c
1 /*
2      This file is part of GNUnet.
3      (C) 2008, 2011 Christian Grothoff (and other contributing authors)
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 3, or (at your
8      option) any later version.
9
10      GNUnet is distributed in the hope that it will be useful, but
11      WITHOUT ANY WARRANTY; without even the implied warranty of
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19 */
20
21 /**
22  * @file chat/chat.c
23  * @brief convenience API for sending and receiving chat messages
24  * @author Christian Grothoff
25  * @author Nathan Evans
26  * @author Vitaly Minko
27  */
28
29 #include "platform.h"
30 #include "gnunet_constants.h"
31 #include "gnunet_protocols.h"
32 #include "gnunet_signatures.h"
33 #include "chat.h"
34
35 #define DEBUG_CHAT GNUNET_NO
36 #define NICK_IDENTITY_PREFIX ".chat_identity_"
37
38
39 /**
40  * Handle for a chat room.
41  */
42 struct GNUNET_CHAT_Room
43 {
44   struct GNUNET_CLIENT_Connection *client;
45
46   const struct GNUNET_CONFIGURATION_Handle *cfg;
47
48   struct GNUNET_CONTAINER_MetaData *member_info;
49
50   char *room_name;
51
52   struct GNUNET_CRYPTO_RsaPrivateKey *my_private_key;
53
54   struct MemberList *members;
55
56   int is_joined;
57
58   GNUNET_CHAT_JoinCallback join_callback;
59
60   void *join_callback_cls;
61
62   GNUNET_CHAT_MessageCallback message_callback;
63
64   void *message_callback_cls;
65
66   GNUNET_CHAT_MemberListCallback member_list_callback;
67
68   void *member_list_callback_cls;
69
70   GNUNET_CHAT_MessageConfirmation confirmation_callback;
71
72   void *confirmation_cls;
73
74   uint32_t sequence_number;
75
76   uint32_t msg_options;
77
78 };
79
80 /**
81  * Linked list of members in the chat room.
82  */
83 struct MemberList
84 {
85   struct MemberList *next;
86
87   /**
88    * Description of the member.
89    */
90   struct GNUNET_CONTAINER_MetaData *meta;
91
92   /**
93    * Member ID (pseudonym).
94    */
95   GNUNET_HashCode id;
96
97 };
98
99 /**
100  * Context for transmitting a send-message request.
101  */
102 struct GNUNET_CHAT_SendMessageContext
103 {
104   /**
105    * Handle for the chat room.
106    */
107   struct GNUNET_CHAT_Room *chat_room;
108
109   /**
110    * Message that we're sending.
111    */
112   char *message;
113
114   /**
115    * Options for the message.
116    */
117   enum GNUNET_CHAT_MsgOptions options;
118
119   /**
120    * Receiver of the message. NULL to send to everyone in the room.
121    */
122   const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *receiver;
123
124   /**
125    * Sequence id of the message.
126    */
127   uint32_t sequence_number;
128
129 };
130
131 /**
132  * Context for transmitting a confirmation receipt.
133  */
134 struct GNUNET_CHAT_SendReceiptContext
135 {
136   /**
137    * Handle for the chat room.
138    */
139   struct GNUNET_CHAT_Room *chat_room;
140
141   /**
142    * The original message that we're going to acknowledge.
143    */
144   struct ReceiveNotificationMessage *received_msg;
145
146 };
147
148 /**
149  * Ask client to send a join request.
150  */
151 static int
152 rejoin_room (struct GNUNET_CHAT_Room *chat_room);
153
154
155 /**
156  * Transmit a confirmation receipt to the chat service.
157  *
158  * @param cls closure, pointer to the 'struct GNUNET_CHAT_SendReceiptContext'
159  * @param size number of bytes available in buf
160  * @param buf where the callee should write the message
161  * @return number of bytes written to buf
162  */
163 static size_t
164 transmit_acknowledge_request (void *cls, 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   GNUNET_HashCode id;
228   const 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 (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 (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 (GNUNET_ERROR_TYPE_ERROR,
472                 _("Configuration option `%s' in section `%s' missing\n"),
473                 "HOME", "chat");
474     return NULL;
475   }
476   GNUNET_DISK_directory_create (home);
477   if (GNUNET_OK != GNUNET_DISK_directory_test (home))
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, GNUNET_NO);
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, 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_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 (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 */