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