aboutsummaryrefslogtreecommitdiff
path: root/src/chat/chat.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/chat/chat.c')
-rw-r--r--src/chat/chat.c789
1 files changed, 789 insertions, 0 deletions
diff --git a/src/chat/chat.c b/src/chat/chat.c
new file mode 100644
index 000000000..14480c3e2
--- /dev/null
+++ b/src/chat/chat.c
@@ -0,0 +1,789 @@
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_api.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 */
42struct 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 */
77struct 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 */
96struct 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 */
128struct 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 */
145static int
146GNUNET_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 */
157static size_t
158transmit_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 */
216static void
217process_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 */
401static void
402receive_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 */
434static struct GNUNET_CRYPTO_RsaPrivateKey *
435GNUNET_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 */
496static size_t
497transmit_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 */
552static int
553GNUNET_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 */
575void
576GNUNET_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 */
620struct GNUNET_CHAT_Room *
621GNUNET_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 */
692static size_t
693transmit_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 */
760void
761GNUNET_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 */