aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2011-01-17 08:30:26 +0000
committerChristian Grothoff <christian@grothoff.org>2011-01-17 08:30:26 +0000
commit151801296c4218af91bbdf1f34a7e3bbfe761594 (patch)
tree56cbcd777842a610ab2b00683409d3194adab844 /src
parent08e3df5862d1ce437be0ba4eaaa0aa61a48b3658 (diff)
downloadgnunet-151801296c4218af91bbdf1f34a7e3bbfe761594.tar.gz
gnunet-151801296c4218af91bbdf1f34a7e3bbfe761594.zip
Mantis #1644, unmodified, patch by vminko
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am3
-rw-r--r--src/chat/Makefile.am39
-rw-r--r--src/chat/chat.c789
-rw-r--r--src/chat/chat.h458
-rw-r--r--src/chat/gnunet-chat.c674
-rw-r--r--src/chat/gnunet-service-chat.c1565
-rw-r--r--src/include/gnunet_chat_service.h234
-rw-r--r--src/include/gnunet_protocols.h68
-rw-r--r--src/include/gnunet_signatures.h9
9 files changed, 3837 insertions, 2 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 106e35128..2729ccfd2 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -32,4 +32,5 @@ SUBDIRS = \
32 fs \ 32 fs \
33 fragmentation \ 33 fragmentation \
34 mesh \ 34 mesh \
35 vpn 35 vpn \
36 chat
diff --git a/src/chat/Makefile.am b/src/chat/Makefile.am
new file mode 100644
index 000000000..ad099e6b1
--- /dev/null
+++ b/src/chat/Makefile.am
@@ -0,0 +1,39 @@
1INCLUDES = -I$(top_srcdir)/src/include
2
3if MINGW
4 WINFLAGS = -Wl,--no-undefined -Wl,--export-all-symbols
5endif
6
7if USE_COVERAGE
8 AM_CFLAGS = -fprofile-arcs -ftest-coverage
9endif
10
11lib_LTLIBRARIES = libgnunetchat.la
12
13libgnunetchat_la_SOURCES = \
14 chat.c chat.h
15
16libgnunetchat_la_LIBADD = \
17 $(top_builddir)/src/util/libgnunetutil.la
18
19libgnunetchat_la_LDFLAGS = \
20 $(GN_LIB_LDFLAGS) $(WINFLAGS) \
21 -version-info 0:0:0
22
23bin_PROGRAMS = \
24 gnunet-service-chat \
25 gnunet-chat
26
27gnunet_service_chat_SOURCES = \
28 gnunet-service-chat.c
29gnunet_service_chat_LDADD = \
30 $(top_builddir)/src/core/libgnunetcore.la \
31 $(top_builddir)/src/util/libgnunetutil.la \
32 $(GN_LIBINTL)
33
34gnunet_chat_SOURCES = \
35 gnunet-chat.c
36gnunet_chat_LDADD = \
37 $(top_builddir)/src/chat/libgnunetchat.la \
38 $(top_builddir)/src/util/libgnunetutil.la \
39 $(GN_LIBINTL)
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 */
diff --git a/src/chat/chat.h b/src/chat/chat.h
new file mode 100644
index 000000000..a9cf83a8c
--- /dev/null
+++ b/src/chat/chat.h
@@ -0,0 +1,458 @@
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 2, 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.h
23 * @brief support for chat
24 * @author Christian Grothoff
25 * @author Nathan Evans
26 * @author Vitaly Minko
27 */
28
29#ifndef CHAT_H
30#define CHAT_H
31
32#include "gnunet_chat_service.h"
33
34/**
35 * Constant IV since we generate a new session key per each message.
36 */
37#define INITVALUE "InitializationVectorValue"
38
39
40/**
41 * Client-service messages
42 */
43
44/**
45 * Notification sent by service to client indicating that we've received a chat
46 * message. After this struct, the remaining bytes are the actual text message.
47 * If the mesasge is private, then the text is encrypted, otherwise it's
48 * plaintext.
49 */
50struct ReceiveNotificationMessage
51{
52 /**
53 * Message type will be GNUNET_MESSAGE_TYPE_CHAT_MESSAGE_NOTIFICATION
54 */
55 struct GNUNET_MessageHeader header;
56
57 /**
58 * Message options, see GNUNET_CHAT_MsgOptions.
59 */
60 uint32_t msg_options GNUNET_PACKED;
61
62 /**
63 * Sequence number of the message (unique per sender).
64 */
65 uint32_t sequence_number GNUNET_PACKED;
66
67 /**
68 * For alignment (should be zero).
69 */
70 uint32_t reserved GNUNET_PACKED;
71
72 /**
73 * Hash of the public key of the pseudonym of the sender of the message.
74 * TBD: Should be all zeros for anonymous.
75 */
76 GNUNET_HashCode sender;
77
78 /**
79 * The encrypted session key.
80 */
81 struct GNUNET_CRYPTO_RsaEncryptedData encrypted_key;
82
83};
84
85
86/**
87 * Request sent by client to transmit a chat message to another room members.
88 * After this struct, the remaining bytes are the actual message in plaintext.
89 * Private messages are encrypted on the service side.
90 */
91struct TransmitRequestMessage
92{
93 /**
94 * Message type will be GNUNET_MESSAGE_TYPE_CHAT_TRANSMIT_REQUEST
95 */
96 struct GNUNET_MessageHeader header;
97
98 /**
99 * For alignment (should be zero).
100 */
101 uint32_t reserved GNUNET_PACKED;
102
103 /**
104 * Signature confirming receipt. Signature covers everything from header
105 * through content.
106 */
107 struct GNUNET_CRYPTO_RsaSignature signature;
108
109 /**
110 * What is being signed and why?
111 */
112 struct GNUNET_CRYPTO_RsaSignaturePurpose purpose;
113
114 /**
115 * Desired message options, see GNUNET_CHAT_MsgOptions.
116 */
117 uint32_t msg_options GNUNET_PACKED;
118
119 /**
120 * Sequence number of the message (unique per sender).
121 */
122 uint32_t sequence_number GNUNET_PACKED;
123
124 /**
125 * Who should receive this message? Set to all zeros for "everyone".
126 */
127 GNUNET_HashCode target;
128
129};
130
131
132/**
133 * Receipt sent from a message receiver to the service to confirm delivery of
134 * a chat message.
135 */
136struct ConfirmationReceiptMessage
137{
138 /**
139 * Message type will be GNUNET_MESSAGE_TYPE_CHAT_CONFIRMATION_RECEIPT
140 */
141 struct GNUNET_MessageHeader header;
142
143 /**
144 * For alignment (should be zero).
145 */
146 uint32_t reserved GNUNET_PACKED;
147
148 /**
149 * Signature confirming receipt. Signature covers everything from header
150 * through content.
151 */
152 struct GNUNET_CRYPTO_RsaSignature signature;
153
154 /**
155 * What is being signed and why?
156 */
157 struct GNUNET_CRYPTO_RsaSignaturePurpose purpose;
158
159 /**
160 * Sequence number of the original message.
161 */
162 uint32_t sequence_number GNUNET_PACKED;
163
164 /**
165 * For alignment (should be zero).
166 */
167 uint32_t reserved2 GNUNET_PACKED;
168
169 /**
170 * Time of receipt.
171 */
172 struct GNUNET_TIME_AbsoluteNBO timestamp;
173
174 /**
175 * Who is confirming the receipt?
176 */
177 GNUNET_HashCode target;
178
179 /**
180 * Who is the author of the chat message?
181 */
182 GNUNET_HashCode author;
183
184 /**
185 * Hash of the (possibly encrypted) content.
186 */
187 GNUNET_HashCode content;
188
189};
190
191
192/**
193 * Message send from client to daemon to join a chat room.
194 * This struct is followed by the room name and then
195 * the serialized ECRS meta data describing the new member.
196 */
197struct JoinRequestMessage
198{
199 /**
200 * Message type will be GNUNET_MESSAGE_TYPE_CHAT_JOIN_REQUEST
201 */
202 struct GNUNET_MessageHeader header;
203
204 /**
205 * Options. Set all options that this client is willing to receive.
206 * For example, if the client does not want to receive anonymous or
207 * OTR messages but is willing to generate acknowledgements and
208 * receive private messages, this should be set to
209 * GNUNET_CHAT_MSG_PRIVATE | GNUNET_CHAT_MSG_ACKNOWLEDGED.
210 */
211 uint32_t msg_options GNUNET_PACKED;
212
213 /**
214 * Length of the room name.
215 */
216 uint16_t room_name_len GNUNET_PACKED;
217
218 /**
219 * For alignment (should be zero).
220 */
221 uint16_t reserved GNUNET_PACKED;
222 uint32_t reserved2 GNUNET_PACKED;
223
224 /**
225 * Public key of the joining member.
226 */
227 struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded public_key;
228
229};
230
231
232/**
233 * Message send by server to client to indicate joining of another room member.
234 * This struct is followed by the serialized ECRS MetaData describing the new
235 * member.
236 */
237struct JoinNotificationMessage
238{
239 /**
240 * Message type will be GNUNET_MESSAGE_TYPE_CHAT_JOIN_NOTIFICATION
241 */
242 struct GNUNET_MessageHeader header;
243
244 /**
245 * Options. Set to all options that the new user is willing to
246 * process. For example, if the client does not want to receive
247 * anonymous or OTR messages but is willing to generate
248 * acknowledgements and receive private messages, this should be set
249 * to GNUNET_CHAT_MSG_PRIVATE | GNUNET_CHAT_MSG_ACKNOWLEDGED.
250 */
251 uint32_t msg_options GNUNET_PACKED;
252
253 /**
254 * Public key of the new user.
255 */
256 struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded public_key;
257
258};
259
260
261/**
262 * Message send by server to client to indicate leaving of another room member.
263 */
264struct LeaveNotificationMessage
265{
266 /**
267 * Message type will be GNUNET_MESSAGE_TYPE_CHAT_LEAVE_NOTIFICATION
268 */
269 struct GNUNET_MessageHeader header;
270
271 /**
272 * Reserved (for alignment).
273 */
274 uint32_t reserved GNUNET_PACKED;
275
276 /**
277 * Who is leaving?
278 */
279 struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded user;
280
281};
282
283
284/**
285 * Peer-to-peer messages
286 */
287
288/**
289 * Message send by one peer to another to indicate joining of another room
290 * member. This struct is followed by the room name and then the serialized
291 * ECRS MetaData describing the new member.
292 */
293struct P2PJoinNotificationMessage
294{
295 /**
296 * Message type will be GNUNET_MESSAGE_TYPE_CHAT_P2P_JOIN_NOTIFICATION
297 */
298 struct GNUNET_MessageHeader header;
299
300 /**
301 * Options. Set all options that this client is willing to receive.
302 * For example, if the client does not want to receive anonymous or
303 * OTR messages but is willing to generate acknowledgements and
304 * receive private messages, this should be set to
305 * GNUNET_CHAT_MSG_PRIVATE | GNUNET_CHAT_MSG_ACKNOWLEDGED.
306 */
307 uint32_t msg_options GNUNET_PACKED;
308
309 /**
310 * Length of the room name.
311 */
312 uint16_t room_name_len GNUNET_PACKED;
313
314 /**
315 * Reserved (should be zero).
316 */
317 uint16_t reserved GNUNET_PACKED;
318 uint32_t reserved2 GNUNET_PACKED;
319
320 /**
321 * Public key of the joining member.
322 */
323 struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded public_key;
324
325};
326
327
328/**
329 * Message send by one peer to another to indicate leaving of another room
330 * member.
331 */
332struct P2PLeaveNotificationMessage
333{
334 /**
335 * Message type will be GNUNET_MESSAGE_TYPE_CHAT_P2P_LEAVE_NOTIFICATION
336 */
337 struct GNUNET_MessageHeader header;
338
339 /**
340 * Reserved (for alignment).
341 */
342 uint32_t reserved GNUNET_PACKED;
343
344 /**
345 * Who is leaving?
346 */
347 struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded user;
348
349};
350
351
352/**
353 * Message send by one peer to another to indicate receiving of a chat message.
354 * After this struct, the remaining bytes are the actual text message. If the
355 * mesasge is private, then the text is encrypted, otherwise it's plaintext.
356 */
357struct P2PReceiveNotificationMessage
358{
359 /**
360 * Message type will be GNUNET_MESSAGE_TYPE_CHAT_P2P_MESSAGE_NOTIFICATION
361 */
362 struct GNUNET_MessageHeader header;
363
364 /**
365 * Message options, see GNUNET_CHAT_MsgOptions.
366 */
367 uint32_t msg_options GNUNET_PACKED;
368
369 /**
370 * Sequence number of the message (unique per sender).
371 */
372 uint32_t sequence_number GNUNET_PACKED;
373
374 /**
375 * Reserved (for alignment).
376 */
377 uint32_t reserved GNUNET_PACKED;
378
379 /**
380 * Hash of the public key of the pseudonym of the sender of the message
381 * TBD: Should be all zeros for anonymous.
382 */
383 GNUNET_HashCode sender;
384
385 /**
386 * Who should receive this message? Set to all zeros for "everyone".
387 */
388 GNUNET_HashCode target;
389
390 /**
391 * The encrypted session key.
392 */
393 struct GNUNET_CRYPTO_RsaEncryptedData encrypted_key;
394
395};
396
397
398/**
399 * Receipt sent from one peer to another to confirm delivery of a chat message.
400 */
401struct P2PConfirmationReceiptMessage
402{
403 /**
404 * Message type will be GNUNET_MESSAGE_TYPE_CHAT_P2P_CONFIRMATION_RECEIPT
405 */
406 struct GNUNET_MessageHeader header;
407
408 /**
409 * For alignment (should be zero).
410 */
411 uint32_t reserved GNUNET_PACKED;
412
413 /**
414 * Signature confirming receipt. Signature covers everything from header
415 * through content.
416 */
417 struct GNUNET_CRYPTO_RsaSignature signature;
418
419 /**
420 * What is being signed and why?
421 */
422 struct GNUNET_CRYPTO_RsaSignaturePurpose purpose;
423
424 /**
425 * Sequence number of the original message.
426 */
427 uint32_t msg_sequence_number GNUNET_PACKED;
428
429 /**
430 * Sequence number of the receipt.
431 */
432 uint32_t sequence_number GNUNET_PACKED;
433
434 /**
435 * Time of receipt.
436 */
437 struct GNUNET_TIME_AbsoluteNBO timestamp;
438
439 /**
440 * Who is confirming the receipt?
441 */
442 GNUNET_HashCode target;
443
444 /**
445 * Who is the author of the chat message?
446 */
447 GNUNET_HashCode author;
448
449 /**
450 * Hash of the (possibly encrypted) content.
451 */
452 GNUNET_HashCode content;
453
454};
455
456#endif
457
458/* end of chat.h */
diff --git a/src/chat/gnunet-chat.c b/src/chat/gnunet-chat.c
new file mode 100644
index 000000000..b960db043
--- /dev/null
+++ b/src/chat/gnunet-chat.c
@@ -0,0 +1,674 @@
1/*
2 This file is part of GNUnet.
3 (C) 2007, 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/gnunet-chat.c
23 * @brief Minimal chat command line tool
24 * @author Christian Grothoff
25 * @author Nathan Evans
26 * @author Vitaly Minko
27 */
28
29#include "platform.h"
30#include "gnunet_getopt_lib.h"
31#include "gnunet_program_lib.h"
32#include "gnunet_chat_service.h"
33#include <fcntl.h>
34
35static int ret;
36
37static const struct GNUNET_CONFIGURATION_Handle *cfg;
38
39static char *nickname;
40
41static char *room_name;
42
43static struct GNUNET_CONTAINER_MetaData *meta;
44
45static struct GNUNET_CHAT_Room *room;
46
47static GNUNET_SCHEDULER_TaskIdentifier handle_cmd_task = GNUNET_SCHEDULER_NO_TASK;
48
49struct ChatCommand
50{
51 const char *command;
52 int (*Action) (const char *arguments, const void *xtra);
53 const char *helptext;
54};
55
56struct UserList
57{
58 struct UserList *next;
59 struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pkey;
60 int ignored;
61};
62
63static struct UserList *users;
64
65static void
66free_user_list ()
67{
68 struct UserList *next;
69 while (NULL != users)
70 {
71 next = users->next;
72 GNUNET_free (users);
73 users = next;
74 }
75}
76
77static int do_help (const char *args, const void *xtra);
78
79
80/**
81 * Callback used for notification about incoming messages.
82 *
83 * @param cls closure, NULL
84 * @param room in which room was the message received?
85 * @param sender what is the ID of the sender? (maybe NULL)
86 * @param member_info information about the joining member
87 * @param message the message text
88 * @param options options for the message
89 * @return GNUNET_OK to accept the message now, GNUNET_NO to
90 * accept (but user is away), GNUNET_SYSERR to signal denied delivery
91 */
92static int
93receive_cb (void *cls,
94 struct GNUNET_CHAT_Room *room,
95 const GNUNET_HashCode *sender,
96 const struct GNUNET_CONTAINER_MetaData *meta,
97 const char *message,
98 enum GNUNET_CHAT_MsgOptions options)
99{
100 char *nick;
101 const char *fmt;
102
103 if (NULL != sender)
104 nick = GNUNET_PSEUDONYM_id_to_name (cfg, sender);
105 else
106 nick = GNUNET_strdup (_("anonymous"));
107 fmt = NULL;
108 switch (options)
109 {
110 case GNUNET_CHAT_MSG_OPTION_NONE:
111 case GNUNET_CHAT_MSG_ANONYMOUS:
112 fmt = _("`%s' said: %s\n");
113 break;
114 case GNUNET_CHAT_MSG_PRIVATE:
115 fmt = _("`%s' said to you: %s\n");
116 break;
117 case GNUNET_CHAT_MSG_PRIVATE | GNUNET_CHAT_MSG_ANONYMOUS:
118 fmt = _("`%s' said to you: %s\n");
119 break;
120 case GNUNET_CHAT_MSG_AUTHENTICATED:
121 fmt = _("`%s' said for sure: %s\n");
122 break;
123 case GNUNET_CHAT_MSG_PRIVATE | GNUNET_CHAT_MSG_AUTHENTICATED:
124 fmt = _("`%s' said to you for sure: %s\n");
125 break;
126 case GNUNET_CHAT_MSG_ACKNOWLEDGED:
127 fmt = _("`%s' was confirmed that you received: %s\n");
128 break;
129 case GNUNET_CHAT_MSG_PRIVATE | GNUNET_CHAT_MSG_ACKNOWLEDGED:
130 fmt = _("`%s' was confirmed that you and only you received: %s\n");
131 break;
132 case GNUNET_CHAT_MSG_AUTHENTICATED | GNUNET_CHAT_MSG_ACKNOWLEDGED:
133 fmt = _("`%s' was confirmed that you received from him or her: %s\n");
134 break;
135 case GNUNET_CHAT_MSG_AUTHENTICATED | GNUNET_CHAT_MSG_PRIVATE | GNUNET_CHAT_MSG_ACKNOWLEDGED:
136 fmt =
137 _
138 ("`%s' was confirmed that you and only you received from him or her: %s\n");
139 break;
140 case GNUNET_CHAT_MSG_OFF_THE_RECORD:
141 fmt = _("`%s' said off the record: %s\n");
142 break;
143 default:
144 fmt = _("<%s> said using an unknown message type: %s\n");
145 break;
146 }
147 fprintf (stdout, fmt, nick, message);
148 GNUNET_free (nick);
149 return GNUNET_OK;
150}
151
152
153/**
154 * Callback used for message delivery confirmations.
155 *
156 * @param cls closure, NULL
157 * @param room in which room was the message received?
158 * @param orig_seq_number sequence number of the original message
159 * @param timestamp when was the message received?
160 * @param receiver who is confirming the receipt?
161 * @param msg_hash hash of the original message
162 * @param receipt signature confirming delivery
163 * @return GNUNET_OK to continue, GNUNET_SYSERR to refuse processing further
164 * confirmations from anyone for this message
165 */
166static int
167confirmation_cb (void *cls,
168 struct GNUNET_CHAT_Room *room,
169 uint32_t orig_seq_number,
170 struct GNUNET_TIME_Absolute timestamp,
171 const GNUNET_HashCode *receiver,
172 const GNUNET_HashCode *msg_hash,
173 const struct GNUNET_CRYPTO_RsaSignature *receipt)
174{
175 char *nick;
176
177 nick = GNUNET_PSEUDONYM_id_to_name (cfg, receiver);
178 fprintf (stdout, _("'%s' acknowledged message #%d\n"), nick, orig_seq_number);
179 return GNUNET_OK;
180}
181
182
183/**
184 * Callback used for notification that another room member has joined or left.
185 *
186 * @param member_info will be non-null if the member is joining, NULL if he is
187 * leaving
188 * @param member_id hash of public key of the user (for unique identification)
189 * @param options what types of messages is this member willing to receive?
190 * @return GNUNET_OK
191 */
192static int
193member_list_cb (void *cls,
194 const struct GNUNET_CONTAINER_MetaData *member_info,
195 const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *member_id,
196 enum GNUNET_CHAT_MsgOptions options)
197{
198 char *nick;
199 GNUNET_HashCode id;
200 struct UserList *pos;
201 struct UserList *prev;
202
203 GNUNET_CRYPTO_hash (member_id,
204 sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
205 &id);
206 nick = GNUNET_PSEUDONYM_id_to_name (cfg, &id);
207 fprintf (stdout, member_info != NULL
208 ? _("`%s' entered the room\n") : _("`%s' left the room\n"), nick);
209 GNUNET_free (nick);
210 if (NULL != member_info)
211 {
212 /* user joining */
213 pos = GNUNET_malloc (sizeof (struct UserList));
214 pos->next = users;
215 pos->pkey = *member_id;
216 pos->ignored = GNUNET_NO;
217 users = pos;
218 }
219 else
220 {
221 /* user leaving */
222 prev = NULL;
223 pos = users;
224 while ((NULL != pos) &&
225 (0 != memcmp (&pos->pkey,
226 member_id,
227 sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded))))
228 {
229 prev = pos;
230 pos = pos->next;
231 }
232 if (NULL == pos)
233 {
234 GNUNET_break (0);
235 }
236 else
237 {
238 if (NULL == prev)
239 users = pos->next;
240 else
241 prev->next = pos->next;
242 GNUNET_free (pos);
243 }
244 }
245 return GNUNET_OK;
246}
247
248
249static int
250do_transmit (const char *msg, const void *xtra)
251{
252 uint32_t seq;
253 GNUNET_CHAT_send_message (room,
254 msg,
255 GNUNET_CHAT_MSG_OPTION_NONE,
256 NULL, &seq);
257 return GNUNET_OK;
258}
259
260
261static int
262do_join (const char *arg, const void *xtra)
263{
264 char *my_name;
265 GNUNET_HashCode me;
266
267 if (arg[0] == '#')
268 arg++; /* ignore first hash */
269 GNUNET_CHAT_leave_room (room);
270 free_user_list ();
271 GNUNET_free (room_name);
272 room_name = GNUNET_strdup (arg);
273 room = GNUNET_CHAT_join_room (cfg,
274 nickname,
275 meta,
276 room_name,
277 -1,
278 &receive_cb, NULL,
279 &member_list_cb, NULL,
280 &confirmation_cb, NULL, &me);
281 if (NULL == room)
282 {
283 fprintf (stdout, _("Could not change username\n"));
284 return GNUNET_SYSERR;
285 }
286 my_name = GNUNET_PSEUDONYM_id_to_name (cfg, &me);
287 fprintf (stdout, _("Joined room `%s' as user `%s'\n"), room_name, my_name);
288 GNUNET_free (my_name);
289 return GNUNET_OK;
290}
291
292
293static int
294do_nick (const char *msg, const void *xtra)
295{
296 char *my_name;
297 GNUNET_HashCode me;
298
299 GNUNET_CHAT_leave_room (room);
300 free_user_list ();
301 GNUNET_free (nickname);
302 GNUNET_CONTAINER_meta_data_destroy (meta);
303 nickname = GNUNET_strdup (msg);
304 meta = GNUNET_CONTAINER_meta_data_create ();
305 GNUNET_CONTAINER_meta_data_insert (meta,
306 "<gnunet>",
307 EXTRACTOR_METATYPE_TITLE,
308 EXTRACTOR_METAFORMAT_UTF8,
309 "text/plain",
310 nickname,
311 strlen(nickname)+1);
312 room = GNUNET_CHAT_join_room (cfg,
313 nickname,
314 meta,
315 room_name,
316 -1,
317 &receive_cb, NULL,
318 &member_list_cb, NULL,
319 &confirmation_cb, NULL, &me);
320 if (NULL == room)
321 {
322 fprintf (stdout, _("Could not change username\n"));
323 return GNUNET_SYSERR;
324 }
325 my_name = GNUNET_PSEUDONYM_id_to_name (cfg, &me);
326 fprintf (stdout, _("Changed username to `%s'\n"), my_name);
327 GNUNET_free (my_name);
328 return GNUNET_OK;
329}
330
331
332static int
333do_names (const char *msg, const void *xtra)
334{
335 char *name;
336 struct UserList *pos;
337 GNUNET_HashCode pid;
338
339 fprintf (stdout, _("Users in room `%s': "), room_name);
340 pos = users;
341 while (NULL != pos)
342 {
343 GNUNET_CRYPTO_hash (&pos->pkey,
344 sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
345 &pid);
346 name = GNUNET_PSEUDONYM_id_to_name (cfg, &pid);
347 fprintf (stdout, "`%s' ", name);
348 GNUNET_free (name);
349 pos = pos->next;
350 }
351 fprintf (stdout, "\n");
352 return GNUNET_OK;
353}
354
355
356static int
357do_pm (const char *msg, const void *xtra)
358{
359 char *user;
360 GNUNET_HashCode uid;
361 GNUNET_HashCode pid;
362 uint32_t seq;
363 struct UserList *pos;
364
365 if (NULL == strstr (msg, " "))
366 {
367 fprintf (stderr, _("Syntax: /msg USERNAME MESSAGE"));
368 return GNUNET_OK;
369 }
370 user = GNUNET_strdup (msg);
371 strstr (user, " ")[0] = '\0';
372 msg += strlen (user) + 1;
373 if (GNUNET_OK != GNUNET_PSEUDONYM_name_to_id (cfg, user, &uid))
374 {
375 fprintf (stderr, _("Unknown user `%s'\n"), user);
376 GNUNET_free (user);
377 return GNUNET_OK;
378 }
379 pos = users;
380 while (NULL != pos)
381 {
382 GNUNET_CRYPTO_hash (&pos->pkey,
383 sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
384 &pid);
385 if (0 == memcmp (&pid, &uid, sizeof (GNUNET_HashCode)))
386 break;
387 pos = pos->next;
388 }
389 if (NULL == pos)
390 {
391 fprintf (stderr, _("User `%s' is currently not in the room!\n"), user);
392 GNUNET_free (user);
393 return GNUNET_OK;
394 }
395 GNUNET_CHAT_send_message (room,
396 msg,
397 GNUNET_CHAT_MSG_PRIVATE,
398 &pos->pkey,
399 &seq);
400 GNUNET_free (user);
401 return GNUNET_OK;
402}
403
404
405static int
406do_transmit_sig (const char *msg, const void *xtra)
407{
408 uint32_t seq;
409 GNUNET_CHAT_send_message (room,
410 msg,
411 GNUNET_CHAT_MSG_AUTHENTICATED,
412 NULL, &seq);
413 return GNUNET_OK;
414}
415
416
417static int
418do_transmit_ack (const char *msg, const void *xtra)
419{
420 uint32_t seq;
421 GNUNET_CHAT_send_message (room,
422 msg,
423 GNUNET_CHAT_MSG_ACKNOWLEDGED,
424 NULL, &seq);
425 return GNUNET_OK;
426}
427
428
429static int
430do_quit (const char *args, const void *xtra)
431{
432 return GNUNET_SYSERR;
433}
434
435
436static int
437do_unknown (const char *msg, const void *xtra)
438{
439 fprintf (stderr, _("Unknown command `%s'\n"), msg);
440 return GNUNET_OK;
441}
442
443
444/**
445 * List of supported IRC commands. The order matters!
446 */
447static struct ChatCommand commands[] = {
448 {"/join ", &do_join,
449 gettext_noop
450 ("Use `/join #roomname' to join a chat room. Joining a room will cause you"
451 " to leave the current room")},
452 {"/nick ", &do_nick,
453 gettext_noop
454 ("Use `/nick nickname' to change your nickname. This will cause you to"
455 " leave the current room and immediately rejoin it with the new name.")},
456 {"/msg ", &do_pm,
457 gettext_noop
458 ("Use `/msg nickname message' to send a private message to the specified"
459 " user")},
460 {"/notice ", &do_pm,
461 gettext_noop ("The `/notice' command is an alias for `/msg'")},
462 {"/query ", &do_pm,
463 gettext_noop ("The `/query' command is an alias for `/msg'")},
464 {"/sig ", &do_transmit_sig,
465 gettext_noop ("Use `/sig message' to send a signed public message")},
466 {"/ack ", &do_transmit_ack,
467 gettext_noop
468 ("Use `/ack message' to require signed acknowledgment of the message")},
469 {"/quit", &do_quit,
470 gettext_noop ("Use `/quit' to terminate gnunet-chat")},
471 {"/leave", &do_quit,
472 gettext_noop ("The `/leave' command is an alias for `/quit'")},
473 {"/names", &do_names,
474 gettext_noop
475 ("Use `/names' to list all of the current members in the chat room")},
476 {"/help", &do_help,
477 gettext_noop ("Use `/help command' to get help for a specific command")},
478 /* Add standard commands:
479 /whois (print metadata),
480 /ignore (set flag, check on receive!) */
481 /* Add special commands (currently supported):
482 + anonymous msgs
483 + authenticated msgs
484 */
485 /* the following three commands must be last! */
486 {"/", &do_unknown, NULL},
487 {"", &do_transmit, NULL},
488 {NULL, NULL, NULL},
489};
490
491
492static int
493do_help (const char *args, const void *xtra)
494{
495 int i;
496 i = 0;
497 while ((NULL != args) &&
498 (0 != strlen (args)) && (commands[i].Action != &do_help))
499 {
500 if (0 ==
501 strncasecmp (&args[1], &commands[i].command[1], strlen (args) - 1))
502 {
503 fprintf (stdout, "%s\n", gettext (commands[i].helptext));
504 return GNUNET_OK;
505 }
506 i++;
507 }
508 i = 0;
509 fprintf (stdout, "Available commands:");
510 while (commands[i].Action != &do_help)
511 {
512 fprintf (stdout, " %s", gettext (commands[i].command));
513 i++;
514 }
515 fprintf (stdout, "\n");
516 fprintf (stdout, "%s\n", gettext (commands[i].helptext));
517 return GNUNET_OK;
518}
519
520
521static void
522do_stop_task (void *cls,
523 const struct GNUNET_SCHEDULER_TaskContext *tc)
524{
525 GNUNET_CHAT_leave_room (room);
526 if (handle_cmd_task != GNUNET_SCHEDULER_NO_TASK)
527 {
528 GNUNET_SCHEDULER_cancel (handle_cmd_task);
529 handle_cmd_task = GNUNET_SCHEDULER_NO_TASK;
530 }
531 free_user_list ();
532 GNUNET_CONTAINER_meta_data_destroy (meta);
533 GNUNET_free (room_name);
534 GNUNET_free (nickname);
535}
536
537
538void
539handle_command (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
540{
541 char message[MAX_MESSAGE_LENGTH + 1];
542 int i;
543
544 /* read message from command line and handle it */
545 memset (message, 0, MAX_MESSAGE_LENGTH + 1);
546 if (NULL == fgets (message, MAX_MESSAGE_LENGTH, stdin))
547 goto next;
548 if (strlen (message) == 0)
549 goto next;
550 if (message[strlen (message) - 1] == '\n')
551 message[strlen (message) - 1] = '\0';
552 if (strlen (message) == 0)
553 goto next;
554 i = 0;
555 while ((NULL != commands[i].command) &&
556 (0 != strncasecmp (commands[i].command,
557 message, strlen (commands[i].command))))
558 i++;
559 if (GNUNET_OK !=
560 commands[i].Action (&message[strlen (commands[i].command)], NULL))
561 goto out;
562
563next:
564 handle_cmd_task =
565 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
566 100),
567 &handle_command,
568 NULL);
569 return;
570
571out:
572 handle_cmd_task = GNUNET_SCHEDULER_NO_TASK;
573 GNUNET_SCHEDULER_shutdown ();
574}
575
576
577/**
578 * Main function that will be run by the scheduler.
579 *
580 * @param cls closure, NULL
581 * @param args remaining command-line arguments
582 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
583 * @param cfg configuration
584 */
585static void
586run (void *cls,
587 char *const *args,
588 const char *cfgfile,
589 const struct GNUNET_CONFIGURATION_Handle *c)
590{
591 GNUNET_HashCode me;
592 char *my_name;
593
594 cfg = c;
595 /* check arguments */
596 if (NULL == nickname)
597 {
598 fprintf (stderr, _("You must specify a nickname\n"));
599 ret = -1;
600 return;
601 }
602 if (NULL == room_name)
603 room_name = GNUNET_strdup ("gnunet");
604 meta = GNUNET_CONTAINER_meta_data_create ();
605 GNUNET_CONTAINER_meta_data_insert (meta,
606 "<gnunet>",
607 EXTRACTOR_METATYPE_TITLE,
608 EXTRACTOR_METAFORMAT_UTF8,
609 "text/plain",
610 nickname,
611 strlen(nickname)+1);
612 room = GNUNET_CHAT_join_room (cfg,
613 nickname,
614 meta,
615 room_name,
616 -1,
617 &receive_cb, NULL,
618 &member_list_cb, NULL,
619 &confirmation_cb, NULL, &me);
620 if (NULL == room)
621 {
622 fprintf (stderr, _("Failed to join room `%s'\n"), room_name);
623 GNUNET_free (room_name);
624 GNUNET_free (nickname);
625 GNUNET_CONTAINER_meta_data_destroy (meta);
626 ret = -1;
627 return;
628 }
629 my_name = GNUNET_PSEUDONYM_id_to_name (cfg, &me);
630 fprintf (stdout, _("Joined room `%s' as user `%s'\n"), room_name, my_name);
631 GNUNET_free (my_name);
632 handle_cmd_task =
633 GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_UI,
634 &handle_command,
635 NULL);
636 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
637 &do_stop_task,
638 NULL);
639}
640
641
642/**
643 * The main function to chat via GNUnet.
644 *
645 * @param argc number of arguments from the command line
646 * @param argv command line arguments
647 * @return 0 ok, 1 on error
648 */
649int
650main (int argc, char *const *argv)
651{
652 int flags;
653 static const struct GNUNET_GETOPT_CommandLineOption options[] = {
654 {'n', "nick", "NAME",
655 gettext_noop ("set the nickname to use (required)"),
656 1, &GNUNET_GETOPT_set_string, &nickname},
657 {'r', "room", "NAME",
658 gettext_noop ("set the chat room to join"),
659 1, &GNUNET_GETOPT_set_string, &room_name},
660 GNUNET_GETOPT_OPTION_END
661 };
662
663 flags = fcntl (0, F_GETFL, 0);
664 flags |= O_NONBLOCK;
665 fcntl (0, F_SETFL, flags);
666 return (GNUNET_OK ==
667 GNUNET_PROGRAM_run (argc,
668 argv,
669 "gnunet-chat",
670 gettext_noop ("Join a chat on GNUnet."),
671 options, &run, NULL)) ? ret : 1;
672}
673
674/* end of gnunet-chat.c */
diff --git a/src/chat/gnunet-service-chat.c b/src/chat/gnunet-service-chat.c
new file mode 100644
index 000000000..4cf869595
--- /dev/null
+++ b/src/chat/gnunet-service-chat.c
@@ -0,0 +1,1565 @@
1/*
2 This file is part of GNUnet.
3 (C) 2009, 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/gnunet-service-caht.c
23 * @brief program that tracks template
24 * @author Christian Grothoff
25 * @author Vitaly Minko
26 */
27
28#include "platform.h"
29#include "gnunet_core_service.h"
30#include "gnunet_crypto_lib.h"
31#include "gnunet_protocols.h"
32#include "gnunet_service_lib.h"
33#include "gnunet_signatures.h"
34#include "chat.h"
35
36#define DEBUG_CHAT_SERVICE GNUNET_YES
37#define MAX_TRANSMIT_DELAY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 60)
38#define QUEUE_SIZE 16
39
40
41/**
42 * Linked list of our current clients.
43 */
44struct ChatClient
45{
46 struct ChatClient *next;
47
48 /**
49 * Handle for a chat client (NULL for external clients).
50 */
51 struct GNUNET_SERVER_Client *client;
52
53 /**
54 * Public key of the client.
55 */
56 struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded public_key;
57
58 /**
59 * Name of the room which the client is in.
60 */
61 char *room;
62
63 /**
64 * Serialized metadata of the client.
65 */
66 char *member_info;
67
68 /**
69 * Hash of the public key (for convenience).
70 */
71 GNUNET_HashCode id;
72
73 /**
74 * Options which the client is willing to receive.
75 */
76 uint32_t msg_options;
77
78 /**
79 * Length of serialized metadata in member_info.
80 */
81 uint16_t meta_len;
82
83 /**
84 * Sequence number of the last message sent by the client.
85 */
86 uint32_t msg_sequence_number;
87
88 /**
89 * Sequence number of the last receipt sent by the client.
90 * Used to discard already processed receipts.
91 */
92 uint32_t rcpt_sequence_number;
93
94};
95
96
97/**
98 * Handle to the core service (NULL until we've connected to it).
99 */
100static struct GNUNET_CORE_Handle *core;
101
102/**
103 * Our configuration.
104 */
105static const struct GNUNET_CONFIGURATION_Handle *cfg;
106
107/**
108 * The identity of this host.
109 */
110static const struct GNUNET_PeerIdentity *me;
111
112/**
113 * Head of the list of current clients.
114 */
115static struct ChatClient *client_list_head = NULL;
116
117/**
118 * Notification context containing all connected clients.
119 */
120struct GNUNET_SERVER_NotificationContext *nc = NULL;
121
122
123/**
124 * Transmit a message notification to the peer.
125 *
126 * @param cls closure, pointer to the 'struct P2PReceiveNotificationMessage'
127 * @param size number of bytes available in buf
128 * @param buf where the callee should write the message
129 * @return number of bytes written to buf
130 */
131static size_t
132transmit_message_notification_to_peer (void *cls,
133 size_t size,
134 void *buf)
135{
136 struct P2PReceiveNotificationMessage *my_msg = cls;
137 struct P2PReceiveNotificationMessage *m = buf;
138 size_t msg_size;
139
140#if DEBUG_CHAT_SERVICE
141 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
142 "Transmitting P2P message notification\n");
143#endif
144 msg_size = ntohs (my_msg->header.size);
145 GNUNET_assert (size >= msg_size);
146 GNUNET_assert (NULL != buf);
147 memcpy (m, my_msg, msg_size);
148 GNUNET_free (my_msg);
149 return msg_size;
150}
151
152
153/**
154 * Ask to send a message notification to the peer.
155 */
156static void
157send_message_noficiation (void *cls,
158 const struct GNUNET_PeerIdentity *peer,
159 const struct GNUNET_TRANSPORT_ATS_Information *atsi)
160{
161 struct P2PReceiveNotificationMessage *msg = cls;
162 struct P2PReceiveNotificationMessage *my_msg;
163 struct GNUNET_CORE_TransmitHandle *th;
164
165 if (NULL == peer)
166 GNUNET_free (msg);
167 else
168 {
169#if DEBUG_CHAT_SERVICE
170 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
171 "Sending message notification to `%s'\n", GNUNET_i2s (peer));
172#endif
173 my_msg = GNUNET_memdup (msg, ntohs (msg->header.size));
174 th = GNUNET_CORE_notify_transmit_ready (core,
175 1,
176 MAX_TRANSMIT_DELAY,
177 peer,
178 ntohs (msg->header.size),
179 &transmit_message_notification_to_peer,
180 my_msg);
181 GNUNET_assert (NULL != th);
182 }
183}
184
185
186/**
187 * A client sent a chat message. Encrypt the message text if the message is
188 * private. Send the message to local room members and to all connected peers.
189 *
190 * @param cls closure, NULL
191 * @param client identification of the client
192 * @param message the actual message
193 */
194static void
195handle_transmit_request (void *cls,
196 struct GNUNET_SERVER_Client *client,
197 const struct GNUNET_MessageHeader *message)
198{
199 static GNUNET_HashCode all_zeros;
200 const struct TransmitRequestMessage *trmsg;
201 struct ReceiveNotificationMessage *rnmsg;
202 struct P2PReceiveNotificationMessage *p2p_rnmsg;
203 struct ChatClient *pos;
204 struct ChatClient *target;
205 struct GNUNET_CRYPTO_AesSessionKey key;
206 char encrypted_msg[MAX_MESSAGE_LENGTH];
207 const char *room;
208 int msg_len;
209 int priv_msg;
210
211 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Client sent a chat message\n");
212 if (ntohs (message->size) <= sizeof (struct TransmitRequestMessage))
213 {
214 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Malformed message: wrong size\n");
215 GNUNET_break (0);
216 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
217 return;
218 }
219 trmsg = (const struct TransmitRequestMessage *) message;
220 msg_len = ntohs (trmsg->header.size) - sizeof (struct TransmitRequestMessage);
221 priv_msg = (ntohl (trmsg->msg_options) & GNUNET_CHAT_MSG_PRIVATE) != 0;
222 if (priv_msg)
223 {
224#if DEBUG_CHAT_SERVICE
225 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Encrypting the message text\n");
226#endif
227 GNUNET_CRYPTO_aes_create_session_key (&key);
228 msg_len = GNUNET_CRYPTO_aes_encrypt (&trmsg[1],
229 msg_len,
230 &key,
231 (const struct GNUNET_CRYPTO_AesInitializationVector *) INITVALUE,
232 encrypted_msg);
233 if (-1 == msg_len)
234 {
235 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
236 "Could not encrypt the message text\n");
237 GNUNET_break (0);
238 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
239 return;
240 }
241 }
242 rnmsg = GNUNET_malloc (sizeof (struct ReceiveNotificationMessage) + msg_len);
243 rnmsg->header.size = htons (sizeof (struct ReceiveNotificationMessage) +
244 msg_len);
245 rnmsg->header.type = htons (GNUNET_MESSAGE_TYPE_CHAT_MESSAGE_NOTIFICATION);
246 rnmsg->msg_options = trmsg->msg_options;
247 rnmsg->sequence_number = trmsg->sequence_number;
248 pos = client_list_head;
249 while ((NULL != pos) && (pos->client != client))
250 pos = pos->next;
251 if (NULL == pos)
252 {
253 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
254 "The client is not a member of a chat room. Client has to "
255 "join a chat room first\n");
256 GNUNET_break (0);
257 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
258 GNUNET_free (rnmsg);
259 return;
260 }
261 room = pos->room;
262 pos->msg_sequence_number = ntohl (trmsg->sequence_number);
263 if (0 == (ntohl (trmsg->msg_options) & GNUNET_CHAT_MSG_ANONYMOUS))
264 rnmsg->sender = pos->id;
265 else
266 memset (&rnmsg->sender, 0, sizeof (GNUNET_HashCode));
267 if (priv_msg)
268 {
269#if DEBUG_CHAT_SERVICE
270 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
271 "Encrypting the session key using the public key of '%s'\n",
272 GNUNET_h2s (&trmsg->target));
273#endif
274 if (0 == memcmp (&all_zeros, &trmsg->target, sizeof (GNUNET_HashCode)))
275 {
276 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
277 "Malformed message: private, but no target\n");
278 GNUNET_break (0);
279 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
280 GNUNET_free (rnmsg);
281 return;
282 }
283 memcpy (&rnmsg[1], encrypted_msg, msg_len);
284 target = client_list_head;
285 while ((NULL != target) &&
286 (0 != memcmp (&target->id,
287 &trmsg->target,
288 sizeof (GNUNET_HashCode))))
289 target = target->next;
290 if (NULL == target)
291 {
292 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
293 "Unknown target of the private message\n");
294 GNUNET_break (0);
295 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
296 GNUNET_free (rnmsg);
297 return;
298 }
299 if (GNUNET_SYSERR == GNUNET_CRYPTO_rsa_encrypt (&key,
300 sizeof (struct GNUNET_CRYPTO_AesSessionKey),
301 &target->public_key,
302 &rnmsg->encrypted_key))
303 {
304 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
305 "Could not encrypt the session key\n");
306 GNUNET_break (0);
307 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
308 GNUNET_free (rnmsg);
309 return;
310 }
311 }
312 else
313 {
314 memcpy (&rnmsg[1], &trmsg[1], msg_len);
315 }
316 pos = client_list_head;
317#if DEBUG_CHAT_SERVICE
318 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending message to local room members\n");
319#endif
320 while (NULL != pos)
321 {
322 if ((0 == strcmp (room, pos->room)) &&
323 (NULL != pos->client) &&
324 (pos->client != client))
325 {
326 if (((!priv_msg) ||
327 (0 == memcmp (&trmsg->target,
328 &pos->id,
329 sizeof (GNUNET_HashCode)))) &&
330 (0 == (ntohl (trmsg->msg_options) & (~pos->msg_options))))
331 {
332 GNUNET_SERVER_notification_context_unicast (nc,
333 pos->client,
334 &rnmsg->header,
335 GNUNET_NO);
336 }
337 }
338 pos = pos->next;
339 }
340#if DEBUG_CHAT_SERVICE
341 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
342 "Broadcasting message to neighbour peers\n");
343#endif
344 p2p_rnmsg = GNUNET_malloc (sizeof (struct P2PReceiveNotificationMessage) +
345 msg_len);
346 p2p_rnmsg->header.size = htons (sizeof (struct P2PReceiveNotificationMessage) +
347 msg_len);
348 p2p_rnmsg->header.type = htons (GNUNET_MESSAGE_TYPE_CHAT_P2P_MESSAGE_NOTIFICATION);
349 p2p_rnmsg->msg_options = trmsg->msg_options;
350 p2p_rnmsg->sequence_number = trmsg->sequence_number;
351 memcpy (&p2p_rnmsg->sender, &rnmsg->sender, sizeof (GNUNET_HashCode));
352 p2p_rnmsg->target = trmsg->target;
353 if (priv_msg)
354 {
355 memcpy (&p2p_rnmsg[1], encrypted_msg, msg_len);
356 memcpy (&p2p_rnmsg->encrypted_key,
357 &rnmsg->encrypted_key,
358 sizeof (struct GNUNET_CRYPTO_RsaEncryptedData));
359 }
360 else
361 {
362 memcpy (&p2p_rnmsg[1], &trmsg[1], msg_len);
363 }
364 GNUNET_CORE_iterate_peers (cfg,
365 &send_message_noficiation,
366 p2p_rnmsg);
367 GNUNET_SERVER_receive_done (client, GNUNET_OK);
368 GNUNET_free (rnmsg);
369}
370
371
372/**
373 * Transmit a join notification to the peer.
374 *
375 * @param cls closure, pointer to the 'struct ChatClient'
376 * @param size number of bytes available in buf
377 * @param buf where the callee should write the message
378 * @return number of bytes written to buf
379 */
380static size_t
381transmit_join_notification_to_peer (void *cls,
382 size_t size,
383 void *buf)
384{
385 struct ChatClient *entry = cls;
386 struct P2PJoinNotificationMessage *m = buf;
387 size_t room_len;
388 size_t meta_len;
389 size_t msg_size;
390 char *roomptr;
391
392#if DEBUG_CHAT_SERVICE
393 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
394 "Transmitting P2P join notification\n");
395#endif
396 room_len = strlen (entry->room);
397 meta_len = entry->meta_len;
398 msg_size = sizeof (struct P2PJoinNotificationMessage) + meta_len + room_len;
399 GNUNET_assert (size >= msg_size);
400 GNUNET_assert (NULL != buf);
401 m = buf;
402 m->header.type = htons (GNUNET_MESSAGE_TYPE_CHAT_P2P_JOIN_NOTIFICATION);
403 m->header.size = htons (msg_size);
404 m->msg_options = htonl (entry->msg_options);
405 m->room_name_len = htons (room_len);
406 m->reserved = htons (0);
407 m->public_key = entry->public_key;
408 roomptr = (char *) &m[1];
409 memcpy (roomptr, entry->room, room_len);
410 if (meta_len > 0)
411 memcpy (&roomptr[room_len], entry->member_info, meta_len);
412 return msg_size;
413}
414
415
416/**
417 * Ask to send a join notification to the peer.
418 */
419static void
420send_join_noficiation (void *cls,
421 const struct GNUNET_PeerIdentity *peer,
422 const struct GNUNET_TRANSPORT_ATS_Information *atsi)
423{
424 struct ChatClient *entry = cls;
425 struct GNUNET_CORE_TransmitHandle *th;
426 size_t msg_size;
427
428 if (NULL != peer)
429 {
430#if DEBUG_CHAT_SERVICE
431 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
432 "Sending join notification to `%s'\n", GNUNET_i2s (peer));
433#endif
434 msg_size = sizeof (struct P2PJoinNotificationMessage) +
435 strlen (entry->room) +
436 entry->meta_len;
437 th = GNUNET_CORE_notify_transmit_ready (core,
438 1,
439 MAX_TRANSMIT_DELAY,
440 peer,
441 msg_size,
442 &transmit_join_notification_to_peer,
443 entry);
444 GNUNET_assert (NULL != th);
445 }
446}
447
448
449/**
450 * A client asked for entering a chat room. Add the new member to the list of
451 * clients and notify remaining room members.
452 *
453 * @param cls closure, NULL
454 * @param client identification of the client
455 * @param message the actual message
456 */
457static void
458handle_join_request (void *cls,
459 struct GNUNET_SERVER_Client *client,
460 const struct GNUNET_MessageHeader *message)
461{
462 const struct JoinRequestMessage *jrmsg;
463 char *room_name;
464 const char *roomptr;
465 uint16_t header_size;
466 uint16_t meta_len;
467 uint16_t room_name_len;
468 struct ChatClient *new_entry;
469 struct ChatClient *entry;
470 struct JoinNotificationMessage *jnmsg;
471 struct JoinNotificationMessage *entry_jnmsg;
472
473 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Client sent a join request\n");
474 if (ntohs (message->size) <= sizeof (struct JoinRequestMessage))
475 {
476 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Malformed message: wrong size\n");
477 GNUNET_break (0);
478 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
479 return;
480 }
481 jrmsg = (const struct JoinRequestMessage *) message;
482 header_size = ntohs (jrmsg->header.size);
483 room_name_len = ntohs (jrmsg->room_name_len);
484 if (header_size - sizeof (struct JoinRequestMessage) <=
485 room_name_len)
486 {
487 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
488 "Malformed message: wrong length of the room name\n");
489 GNUNET_break (0);
490 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
491 return;
492 }
493 meta_len =
494 header_size - sizeof (struct JoinRequestMessage) - room_name_len;
495 roomptr = (const char *) &jrmsg[1];
496 room_name = GNUNET_malloc (room_name_len + 1);
497 memcpy (room_name, roomptr, room_name_len);
498 room_name[room_name_len] = '\0';
499 new_entry = GNUNET_malloc (sizeof (struct ChatClient));
500 memset (new_entry, 0, sizeof (struct ChatClient));
501 new_entry->client = client;
502 new_entry->room = room_name;
503 new_entry->public_key = jrmsg->public_key;
504 new_entry->meta_len = meta_len;
505 if (meta_len > 0)
506 {
507 new_entry->member_info = GNUNET_malloc (meta_len);
508 memcpy (new_entry->member_info, &roomptr[room_name_len], meta_len);
509 }
510 else
511 new_entry->member_info = NULL;
512 GNUNET_CRYPTO_hash (&new_entry->public_key,
513 sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
514 &new_entry->id);
515 new_entry->msg_options = ntohl (jrmsg->msg_options);
516 new_entry->next = client_list_head;
517 client_list_head = new_entry;
518#if DEBUG_CHAT_SERVICE
519 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
520 "Synchronizing room members between local clients\n");
521#endif
522 jnmsg = GNUNET_malloc (sizeof (struct JoinNotificationMessage) + meta_len);
523 jnmsg->header.type = htons (GNUNET_MESSAGE_TYPE_CHAT_JOIN_NOTIFICATION);
524 jnmsg->header.size =
525 htons (sizeof (struct JoinNotificationMessage) + meta_len);
526 jnmsg->msg_options = jrmsg->msg_options;
527 jnmsg->public_key = new_entry->public_key;
528 memcpy (&jnmsg[1], &roomptr[room_name_len], meta_len);
529 GNUNET_SERVER_notification_context_add (nc, client);
530 entry = client_list_head;
531 while (NULL != entry)
532 {
533 if (0 == strcmp (room_name, entry->room))
534 {
535 if (NULL != entry->client)
536 GNUNET_SERVER_notification_context_unicast (nc,
537 entry->client,
538 &jnmsg->header,
539 GNUNET_NO);
540 if (entry->client != client)
541 {
542 entry_jnmsg =
543 GNUNET_malloc (sizeof (struct JoinNotificationMessage) +
544 entry->meta_len);
545 entry_jnmsg->header.type =
546 htons (GNUNET_MESSAGE_TYPE_CHAT_JOIN_NOTIFICATION);
547 entry_jnmsg->header.size =
548 htons (sizeof (struct JoinNotificationMessage) +
549 entry->meta_len);
550 entry_jnmsg->msg_options = entry->msg_options;
551 entry_jnmsg->public_key = entry->public_key;
552 memcpy (&entry_jnmsg[1], entry->member_info, entry->meta_len);
553 GNUNET_SERVER_notification_context_unicast (nc,
554 client,
555 &entry_jnmsg->header,
556 GNUNET_NO);
557 GNUNET_free (entry_jnmsg);
558 }
559 }
560 entry = entry->next;
561 }
562#if DEBUG_CHAT_SERVICE
563 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
564 "Broadcasting join notification to neighbour peers\n");
565#endif
566 GNUNET_CORE_iterate_peers (cfg,
567 &send_join_noficiation,
568 new_entry);
569 GNUNET_SERVER_receive_done (client, GNUNET_OK);
570 GNUNET_free (jnmsg);
571}
572
573/**
574 * Transmit a confirmation receipt to the peer.
575 *
576 * @param cls closure, pointer to the 'struct P2PConfirmationReceiptMessage'
577 * @param size number of bytes available in buf
578 * @param buf where the callee should write the message
579 * @return number of bytes written to buf
580 */
581static size_t
582transmit_confirmation_receipt_to_peer (void *cls,
583 size_t size,
584 void *buf)
585{
586 struct P2PConfirmationReceiptMessage *receipt = cls;
587 size_t msg_size;
588
589#if DEBUG_CHAT_SERVICE
590 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
591 "Transmitting P2P confirmation receipt to '%s'\n",
592 GNUNET_h2s (&receipt->target));
593#endif
594 msg_size = sizeof (struct P2PConfirmationReceiptMessage);
595 GNUNET_assert (size >= msg_size);
596 GNUNET_assert (NULL != buf);
597 memcpy (buf, receipt, msg_size);
598 GNUNET_free (receipt);
599 return msg_size;
600}
601
602
603/**
604 * Ask to send a confirmation receipt to the peer.
605 */
606static void
607send_confirmation_receipt (void *cls,
608 const struct GNUNET_PeerIdentity *peer,
609 const struct GNUNET_TRANSPORT_ATS_Information *atsi)
610{
611 struct P2PConfirmationReceiptMessage *receipt = cls;
612 struct P2PConfirmationReceiptMessage *my_receipt;
613 struct GNUNET_CORE_TransmitHandle *th;
614 size_t msg_size;
615
616 if (NULL == peer)
617 GNUNET_free (receipt);
618 else
619 {
620#if DEBUG_CHAT_SERVICE
621 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
622 "Sending confirmation receipt to `%s'\n", GNUNET_i2s (peer));
623#endif
624 msg_size = sizeof (struct P2PConfirmationReceiptMessage);
625 my_receipt = GNUNET_memdup (receipt,
626 sizeof (struct P2PConfirmationReceiptMessage));
627 th = GNUNET_CORE_notify_transmit_ready (core,
628 1,
629 MAX_TRANSMIT_DELAY,
630 peer,
631 msg_size,
632 &transmit_confirmation_receipt_to_peer,
633 my_receipt);
634 GNUNET_assert (NULL != th);
635 }
636}
637
638
639/**
640 * A client sent a confirmation receipt. Broadcast the receipt to all connected
641 * peers if the author of the original message is a local client. Otherwise
642 * check the signature and notify the user if the signature is valid.
643 *
644 * @param cls closure, NULL
645 * @param client identification of the client
646 * @param message the actual message
647 */
648static void
649handle_acknowledge_request (void *cls,
650 struct GNUNET_SERVER_Client *client,
651 const struct GNUNET_MessageHeader *message)
652{
653 const struct ConfirmationReceiptMessage *receipt;
654 struct ConfirmationReceiptMessage *crmsg;
655 struct P2PConfirmationReceiptMessage *p2p_crmsg;
656 struct ChatClient *target;
657 struct ChatClient *author;
658
659 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Client sent a confirmation receipt\n");
660 receipt = (const struct ConfirmationReceiptMessage *) message;
661 author = client_list_head;
662 while ((NULL != author) &&
663 (0 != memcmp (&receipt->author,
664 &author->id,
665 sizeof (GNUNET_HashCode))))
666 author = author->next;
667 if (NULL == author)
668 {
669 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
670 "Unknown author of the original message\n");
671 GNUNET_break (0);
672 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
673 return;
674 }
675 target = client_list_head;
676 while ((NULL != target) &&
677 (0 != memcmp (&receipt->target,
678 &target->id,
679 sizeof (GNUNET_HashCode))))
680 target = target->next;
681 if (NULL == target)
682 {
683 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
684 "Unknown target of the confirmation receipt\n");
685 GNUNET_break (0);
686 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
687 return;
688 }
689 if (NULL == author->client)
690 {
691 target->rcpt_sequence_number++;
692#if DEBUG_CHAT_SERVICE
693 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
694 "Broadcasting %s's receipt #%u to neighbour peers\n",
695 GNUNET_h2s (&target->id), target->rcpt_sequence_number);
696#endif
697 p2p_crmsg = GNUNET_malloc (sizeof (struct P2PConfirmationReceiptMessage));
698 p2p_crmsg->header.size = htons (sizeof (struct P2PConfirmationReceiptMessage));
699 p2p_crmsg->header.type = htons (GNUNET_MESSAGE_TYPE_CHAT_P2P_CONFIRMATION_RECEIPT);
700 p2p_crmsg->signature = receipt->signature;
701 p2p_crmsg->purpose = receipt->purpose;
702 p2p_crmsg->msg_sequence_number = receipt->sequence_number;
703 p2p_crmsg->timestamp = receipt->timestamp;
704 p2p_crmsg->target = receipt->target;
705 p2p_crmsg->author = receipt->author;
706 p2p_crmsg->content = receipt->content;
707 p2p_crmsg->sequence_number = htonl (target->rcpt_sequence_number);
708 GNUNET_CORE_iterate_peers (cfg,
709 &send_confirmation_receipt,
710 p2p_crmsg);
711 }
712 else
713 {
714#if DEBUG_CHAT_SERVICE
715 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
716 "Verifying signature of the receipt\n");
717#endif
718 if (GNUNET_OK !=
719 GNUNET_CRYPTO_rsa_verify (GNUNET_SIGNATURE_PURPOSE_CHAT_RECEIPT,
720 &receipt->purpose,
721 &receipt->signature,
722 &target->public_key))
723 {
724 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
725 "Invalid signature of the receipt\n");
726 GNUNET_break (0);
727 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
728 return;
729 }
730#if DEBUG_CHAT_SERVICE
731 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
732 "Sending receipt to the client which sent the original message\n");
733#endif
734 crmsg = GNUNET_memdup (receipt, sizeof (struct ConfirmationReceiptMessage));
735 crmsg->header.type = htons (GNUNET_MESSAGE_TYPE_CHAT_CONFIRMATION_NOTIFICATION);
736 GNUNET_SERVER_notification_context_unicast (nc,
737 author->client,
738 &crmsg->header,
739 GNUNET_NO);
740 GNUNET_free (crmsg);
741 }
742 GNUNET_SERVER_receive_done (client, GNUNET_OK);
743}
744
745
746/**
747 * Transmit a leave notification to the peer.
748 *
749 * @param cls closure, pointer to the
750 * 'struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded'
751 * @param size number of bytes available in buf
752 * @param buf where the callee should write the message
753 * @return number of bytes written to buf
754 */
755static size_t
756transmit_leave_notification_to_peer (void *cls,
757 size_t size,
758 void *buf)
759{
760 struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *public_key = cls;
761 struct P2PLeaveNotificationMessage *m = buf;
762 size_t msg_size;
763
764#if DEBUG_CHAT_SERVICE
765 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
766 "Transmitting P2P leave notification\n");
767#endif
768 msg_size = sizeof (struct P2PLeaveNotificationMessage);
769 GNUNET_assert (size >= msg_size);
770 GNUNET_assert (NULL != buf);
771 m = buf;
772 m->header.type = htons (GNUNET_MESSAGE_TYPE_CHAT_P2P_LEAVE_NOTIFICATION);
773 m->header.size = htons (msg_size);
774 m->reserved = htons (0);
775 m->user = *public_key;
776 GNUNET_free (public_key);
777 return msg_size;
778}
779
780
781/**
782 * Ask to send a leave notification to the peer.
783 */
784static void
785send_leave_noficiation (void *cls,
786 const struct GNUNET_PeerIdentity *peer,
787 const struct GNUNET_TRANSPORT_ATS_Information *atsi)
788{
789 struct ChatClient *entry = cls;
790 struct GNUNET_CORE_TransmitHandle *th;
791 struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *public_key;
792 size_t msg_size;
793
794 if (NULL == peer)
795 {
796 GNUNET_free (entry->room);
797 GNUNET_free_non_null (entry->member_info);
798 GNUNET_free (entry);
799 }
800 else
801 {
802#if DEBUG_CHAT_SERVICE
803 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
804 "Sending leave notification to `%s'\n", GNUNET_i2s (peer));
805#endif
806 msg_size = sizeof (struct P2PLeaveNotificationMessage);
807 public_key = GNUNET_memdup (&entry->public_key,
808 sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded));
809 th = GNUNET_CORE_notify_transmit_ready (core,
810 1,
811 MAX_TRANSMIT_DELAY,
812 peer,
813 msg_size,
814 &transmit_leave_notification_to_peer,
815 public_key);
816 GNUNET_assert (NULL != th);
817 }
818}
819
820
821/**
822 * A client disconnected. Remove all of its data structure entries and notify
823 * remaining room members.
824 *
825 * @param cls closure, NULL
826 * @param client identification of the client
827 */
828static void
829handle_client_disconnect (void *cls,
830 struct GNUNET_SERVER_Client *client)
831{
832 struct ChatClient *entry;
833 struct ChatClient *pos;
834 struct ChatClient *prev;
835 struct LeaveNotificationMessage lnmsg;
836
837 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Client disconnected\n");
838 pos = client_list_head;
839 prev = NULL;
840 while ((NULL != pos) && (pos->client != client))
841 {
842 prev = pos;
843 pos = pos->next;
844 }
845 if (NULL == pos)
846 {
847#if DEBUG_CHAT_SERVICE
848 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
849 "No such client. There is nothing to do\n");
850#endif
851 return;
852 }
853 if (NULL == prev)
854 client_list_head = pos->next;
855 else
856 prev->next = pos->next;
857 entry = client_list_head;
858#if DEBUG_CHAT_SERVICE
859 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
860 "Notifying local room members that the client has disconnected\n");
861#endif
862 lnmsg.header.size = htons (sizeof (struct LeaveNotificationMessage));
863 lnmsg.header.type = htons (GNUNET_MESSAGE_TYPE_CHAT_LEAVE_NOTIFICATION);
864 lnmsg.reserved = htonl (0);
865 lnmsg.user = pos->public_key;
866 while (NULL != entry)
867 {
868 if ((0 == strcmp (pos->room, entry->room)) &&
869 (NULL != entry->client))
870 {
871 GNUNET_SERVER_notification_context_unicast (nc,
872 entry->client,
873 &lnmsg.header,
874 GNUNET_NO);
875 }
876 entry = entry->next;
877 }
878#if DEBUG_CHAT_SERVICE
879 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
880 "Broadcasting leave notification to neighbour peers\n");
881#endif
882 GNUNET_CORE_iterate_peers (cfg,
883 &send_leave_noficiation,
884 pos);
885}
886
887
888/**
889 * Handle P2P join notification.
890 *
891 * @param cls closure, always NULL
892 * @param other the other peer involved
893 * @param message the actual message
894 * @param atsi performance information
895 * @return GNUNET_OK to keep the connection open,
896 * GNUNET_SYSERR to close it (signal serious error)
897 */
898static int
899handle_p2p_join_notification (void *cls,
900 const struct GNUNET_PeerIdentity *other,
901 const struct GNUNET_MessageHeader *message,
902 const struct GNUNET_TRANSPORT_ATS_Information *atsi)
903{
904 const struct P2PJoinNotificationMessage *p2p_jnmsg;
905 char *room_name;
906 const char *roomptr;
907 uint16_t header_size;
908 uint16_t meta_len;
909 uint16_t room_name_len;
910 struct ChatClient *new_entry;
911 struct ChatClient *entry;
912 struct JoinNotificationMessage *jnmsg;
913 GNUNET_HashCode id;
914
915 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Got P2P join notification\n");
916 if (ntohs (message->size) <= sizeof (struct P2PJoinNotificationMessage))
917 {
918 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Malformed message: wrong size\n");
919 GNUNET_break_op (0);
920 return GNUNET_SYSERR;
921 }
922 p2p_jnmsg = (const struct P2PJoinNotificationMessage *) message;
923 header_size = ntohs (p2p_jnmsg->header.size);
924 room_name_len = ntohs (p2p_jnmsg->room_name_len);
925 if (header_size - sizeof (struct P2PJoinNotificationMessage) <=
926 room_name_len)
927 {
928 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
929 "Malformed message: wrong length of the room name\n");
930 GNUNET_break_op (0);
931 return GNUNET_SYSERR;
932 }
933 GNUNET_CRYPTO_hash (&p2p_jnmsg->public_key,
934 sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
935 &id);
936 entry = client_list_head;
937 while (NULL != entry)
938 {
939 if (0 == memcmp (&entry->id, &id, sizeof (GNUNET_HashCode)))
940 {
941#if DEBUG_CHAT_SERVICE
942 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
943 "The client has already joined. There is nothing to do\n");
944#endif
945 return GNUNET_OK;
946 }
947 entry = entry->next;
948 }
949 meta_len =
950 header_size - sizeof (struct P2PJoinNotificationMessage) - room_name_len;
951 roomptr = (const char *) &p2p_jnmsg[1];
952 room_name = GNUNET_malloc (room_name_len + 1);
953 memcpy (room_name, roomptr, room_name_len);
954 room_name[room_name_len] = '\0';
955 new_entry = GNUNET_malloc (sizeof (struct ChatClient));
956 memset (new_entry, 0, sizeof (struct ChatClient));
957 new_entry->id = id;
958 new_entry->client = NULL;
959 new_entry->room = room_name;
960 new_entry->public_key = p2p_jnmsg->public_key;
961 new_entry->meta_len = meta_len;
962 if (meta_len > 0)
963 {
964 new_entry->member_info = GNUNET_malloc (meta_len);
965 memcpy (new_entry->member_info, &roomptr[room_name_len], meta_len);
966 }
967 else
968 new_entry->member_info = NULL;
969 new_entry->msg_options = ntohl (p2p_jnmsg->msg_options);
970 new_entry->next = client_list_head;
971 client_list_head = new_entry;
972#if DEBUG_CHAT_SERVICE
973 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
974 "Notifying local room members that we have a new client\n");
975#endif
976 jnmsg = GNUNET_malloc (sizeof (struct JoinNotificationMessage) + meta_len);
977 jnmsg->header.type = htons (GNUNET_MESSAGE_TYPE_CHAT_JOIN_NOTIFICATION);
978 jnmsg->header.size =
979 htons (sizeof (struct JoinNotificationMessage) + meta_len);
980 jnmsg->msg_options = p2p_jnmsg->msg_options;
981 jnmsg->public_key = new_entry->public_key;
982 memcpy (&jnmsg[1], &roomptr[room_name_len], meta_len);
983 entry = client_list_head;
984 while (NULL != entry)
985 {
986 if ((0 == strcmp (room_name, entry->room)) &&
987 (NULL != entry->client))
988 {
989 GNUNET_SERVER_notification_context_unicast (nc,
990 entry->client,
991 &jnmsg->header,
992 GNUNET_NO);
993 }
994 entry = entry->next;
995 }
996#if DEBUG_CHAT_SERVICE
997 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
998 "Broadcasting join notification to neighbour peers\n");
999#endif
1000 GNUNET_CORE_iterate_peers (cfg,
1001 &send_join_noficiation,
1002 new_entry);
1003 GNUNET_free (jnmsg);
1004 return GNUNET_OK;
1005}
1006
1007
1008/**
1009 * Handle P2P leave notification.
1010 *
1011 * @param cls closure, always NULL
1012 * @param other the other peer involved
1013 * @param message the actual message
1014 * @param atsi performance information
1015 * @return GNUNET_OK to keep the connection open,
1016 * GNUNET_SYSERR to close it (signal serious error)
1017 */
1018static int
1019handle_p2p_leave_notification (void *cls,
1020 const struct GNUNET_PeerIdentity *other,
1021 const struct GNUNET_MessageHeader *message,
1022 const struct GNUNET_TRANSPORT_ATS_Information *atsi)
1023{
1024 const struct P2PLeaveNotificationMessage *p2p_lnmsg;
1025 GNUNET_HashCode id;
1026 struct ChatClient *pos;
1027 struct ChatClient *prev;
1028 struct ChatClient *entry;
1029 struct LeaveNotificationMessage lnmsg;
1030
1031 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Got P2P leave notification\n");
1032 p2p_lnmsg = (const struct P2PLeaveNotificationMessage *) message;
1033 GNUNET_CRYPTO_hash (&p2p_lnmsg->user,
1034 sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
1035 &id);
1036 pos = client_list_head;
1037 prev = NULL;
1038 while (NULL != pos)
1039 {
1040 if (0 == memcmp (&pos->id, &id, sizeof (GNUNET_HashCode)))
1041 break;
1042 prev = pos;
1043 pos = pos->next;
1044 }
1045 if (NULL == pos)
1046 {
1047#if DEBUG_CHAT_SERVICE
1048 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1049 "No such client. There is nothing to do\n");
1050#endif
1051 return GNUNET_OK;
1052 }
1053 if (NULL == prev)
1054 client_list_head = pos->next;
1055 else
1056 prev->next = pos->next;
1057#if DEBUG_CHAT_SERVICE
1058 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1059 "Notifying local room members that the client has gone away\n");
1060#endif
1061 lnmsg.header.size = htons (sizeof (struct LeaveNotificationMessage));
1062 lnmsg.header.type = htons (GNUNET_MESSAGE_TYPE_CHAT_LEAVE_NOTIFICATION);
1063 lnmsg.reserved = htonl (0);
1064 lnmsg.user = pos->public_key;
1065 entry = client_list_head;
1066 while (NULL != entry)
1067 {
1068 if (0 == strcmp (pos->room, entry->room) &&
1069 (NULL != entry->client))
1070 {
1071 GNUNET_SERVER_notification_context_unicast (nc,
1072 entry->client,
1073 &lnmsg.header,
1074 GNUNET_NO);
1075 }
1076 entry = entry->next;
1077 }
1078#if DEBUG_CHAT_SERVICE
1079 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1080 "Broadcasting leave notification to neighbour peers\n");
1081#endif
1082 GNUNET_CORE_iterate_peers (cfg,
1083 &send_leave_noficiation,
1084 pos);
1085 return GNUNET_OK;
1086}
1087
1088
1089/**
1090 * Handle P2P message notification.
1091 *
1092 * @param cls closure, always NULL
1093 * @param other the other peer involved
1094 * @param message the actual message
1095 * @param atsi performance information
1096 * @return GNUNET_OK to keep the connection open,
1097 * GNUNET_SYSERR to close it (signal serious error)
1098 */
1099static int
1100handle_p2p_message_notification (void *cls,
1101 const struct GNUNET_PeerIdentity *other,
1102 const struct GNUNET_MessageHeader *message,
1103 const struct GNUNET_TRANSPORT_ATS_Information *atsi)
1104{
1105 const struct P2PReceiveNotificationMessage *p2p_rnmsg;
1106 struct P2PReceiveNotificationMessage *my_p2p_rnmsg;
1107 struct ReceiveNotificationMessage *rnmsg;
1108 struct ChatClient *sender;
1109 struct ChatClient *pos;
1110 static GNUNET_HashCode all_zeros;
1111 int priv_msg;
1112 uint16_t msg_len;
1113
1114 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Got P2P message notification\n");
1115 if (ntohs (message->size) <= sizeof (struct P2PReceiveNotificationMessage))
1116 {
1117 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Malformed message: wrong size\n");
1118 GNUNET_break_op (0);
1119 return GNUNET_SYSERR;
1120 }
1121 p2p_rnmsg = (const struct P2PReceiveNotificationMessage *) message;
1122 sender = client_list_head;
1123 while ((NULL != sender) &&
1124 (0 != memcmp (&sender->id,
1125 &p2p_rnmsg->sender,
1126 sizeof (GNUNET_HashCode))))
1127 sender = sender->next;
1128 if (NULL == sender)
1129 {
1130 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1131 "Unknown source. Rejecting the message\n");
1132 GNUNET_break_op (0);
1133 return GNUNET_SYSERR;
1134 }
1135 if (sender->msg_sequence_number >= ntohl (p2p_rnmsg->sequence_number))
1136 {
1137#if DEBUG_CHAT_SERVICE
1138 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1139 "This message has already been handled."
1140 " Sequence numbers (msg/sender): %u/%u\n",
1141 ntohl (p2p_rnmsg->sequence_number), sender->msg_sequence_number);
1142#endif
1143 return GNUNET_OK;
1144 }
1145 sender->msg_sequence_number = ntohl (p2p_rnmsg->sequence_number);
1146 msg_len = ntohs (p2p_rnmsg->header.size) -
1147 sizeof (struct P2PReceiveNotificationMessage);
1148#if DEBUG_CHAT_SERVICE
1149 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending message to local room members\n");
1150#endif
1151 rnmsg = GNUNET_malloc (sizeof (struct ReceiveNotificationMessage) + msg_len);
1152 rnmsg->header.size = htons (sizeof (struct ReceiveNotificationMessage) +
1153 msg_len);
1154 rnmsg->header.type = htons (GNUNET_MESSAGE_TYPE_CHAT_MESSAGE_NOTIFICATION);
1155 rnmsg->msg_options = p2p_rnmsg->msg_options;
1156 rnmsg->sequence_number = p2p_rnmsg->sequence_number;
1157 priv_msg = (0 != memcmp (&all_zeros,
1158 &p2p_rnmsg->target, sizeof (GNUNET_HashCode)));
1159 if (priv_msg)
1160 memcpy (&rnmsg->encrypted_key,
1161 &p2p_rnmsg->encrypted_key,
1162 sizeof (struct GNUNET_CRYPTO_RsaEncryptedData));
1163 memcpy (&rnmsg->sender, &p2p_rnmsg->sender, sizeof (GNUNET_HashCode));
1164 memcpy (&rnmsg[1], &p2p_rnmsg[1], msg_len);
1165 pos = client_list_head;
1166 while (NULL != pos)
1167 {
1168 if ((0 == strcmp (sender->room, pos->room)) &&
1169 (NULL != pos->client))
1170 {
1171 if (((!priv_msg) ||
1172 (0 == memcmp (&p2p_rnmsg->target,
1173 &pos->id,
1174 sizeof (GNUNET_HashCode)))) &&
1175 (0 == (ntohl (p2p_rnmsg->msg_options) & (~pos->msg_options))))
1176 {
1177 GNUNET_SERVER_notification_context_unicast (nc,
1178 pos->client,
1179 &rnmsg->header,
1180 GNUNET_NO);
1181 }
1182 }
1183 pos = pos->next;
1184 }
1185#if DEBUG_CHAT_SERVICE
1186 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1187 "Broadcasting message notification to neighbour peers\n");
1188#endif
1189 my_p2p_rnmsg = GNUNET_memdup (p2p_rnmsg, ntohs (p2p_rnmsg->header.size));
1190 GNUNET_CORE_iterate_peers (cfg,
1191 &send_message_noficiation,
1192 my_p2p_rnmsg);
1193 GNUNET_free (rnmsg);
1194 return GNUNET_OK;
1195}
1196
1197
1198/**
1199 * Handle P2P sync request.
1200 *
1201 * @param cls closure, always NULL
1202 * @param other the other peer involved
1203 * @param message the actual message
1204 * @param atsi performance information
1205 * @return GNUNET_OK to keep the connection open,
1206 * GNUNET_SYSERR to close it (signal serious error)
1207 */
1208static int
1209handle_p2p_sync_request (void *cls,
1210 const struct GNUNET_PeerIdentity *other,
1211 const struct GNUNET_MessageHeader *message,
1212 const struct GNUNET_TRANSPORT_ATS_Information *atsi)
1213{
1214 struct ChatClient *entry;
1215 struct GNUNET_CORE_TransmitHandle *th;
1216 size_t msg_size;
1217
1218 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Got P2P sync request\n");
1219#if DEBUG_CHAT_SERVICE
1220 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1221 "Notifying the requester of all known clients\n");
1222#endif
1223 entry = client_list_head;
1224 while (NULL != entry)
1225 {
1226 msg_size = sizeof (struct P2PJoinNotificationMessage) +
1227 strlen (entry->room) +
1228 entry->meta_len;
1229 th = GNUNET_CORE_notify_transmit_ready (core,
1230 1,
1231 MAX_TRANSMIT_DELAY,
1232 other,
1233 msg_size,
1234 &transmit_join_notification_to_peer,
1235 entry);
1236 GNUNET_assert (NULL != th);
1237 entry = entry->next;
1238 }
1239 return GNUNET_OK;
1240}
1241
1242
1243/**
1244 * Handle P2P confirmation receipt.
1245 *
1246 * @param cls closure, always NULL
1247 * @param other the other peer involved
1248 * @param message the actual message
1249 * @param atsi performance information
1250 * @return GNUNET_OK to keep the connection open,
1251 * GNUNET_SYSERR to close it (signal serious error)
1252 */
1253static int
1254handle_p2p_confirmation_receipt (void *cls,
1255 const struct GNUNET_PeerIdentity *other,
1256 const struct GNUNET_MessageHeader *message,
1257 const struct GNUNET_TRANSPORT_ATS_Information *atsi)
1258{
1259 const struct P2PConfirmationReceiptMessage *p2p_crmsg;
1260 struct P2PConfirmationReceiptMessage *my_p2p_crmsg;
1261 struct ConfirmationReceiptMessage *crmsg;
1262 struct ChatClient *target;
1263 struct ChatClient *author;
1264
1265 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Got P2P confirmation receipt\n");
1266 p2p_crmsg = (const struct P2PConfirmationReceiptMessage *) message;
1267 target = client_list_head;
1268 while ((NULL != target) &&
1269 (0 != memcmp (&target->id,
1270 &p2p_crmsg->target,
1271 sizeof (GNUNET_HashCode))))
1272 target = target->next;
1273 if (NULL == target)
1274 {
1275 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1276 "Unknown source of the receipt. Rejecting the message\n");
1277 GNUNET_break_op (0);
1278 return GNUNET_SYSERR;
1279 }
1280 if (target->rcpt_sequence_number >= ntohl (p2p_crmsg->sequence_number))
1281 {
1282#if DEBUG_CHAT_SERVICE
1283 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1284 "This receipt has already been handled."
1285 " Sequence numbers (msg/sender): %u/%u\n",
1286 ntohl (p2p_crmsg->sequence_number), target->rcpt_sequence_number);
1287#endif
1288 return GNUNET_OK;
1289 }
1290 target->rcpt_sequence_number = ntohl (p2p_crmsg->sequence_number);
1291 author = client_list_head;
1292 while ((NULL != author) &&
1293 (0 != memcmp (&author->id,
1294 &p2p_crmsg->author,
1295 sizeof (GNUNET_HashCode))))
1296 author = author->next;
1297 if (NULL == author)
1298 {
1299 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1300 "Unknown addressee. Rejecting the receipt\n");
1301 GNUNET_break_op (0);
1302 return GNUNET_SYSERR;
1303 }
1304
1305 if (NULL == author->client)
1306 {
1307#if DEBUG_CHAT_SERVICE
1308 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1309 "The author of the original message is not a local client."
1310 " Broadcasting receipt to neighbour peers\n");
1311#endif
1312 my_p2p_crmsg = GNUNET_memdup (p2p_crmsg, sizeof (struct P2PConfirmationReceiptMessage));
1313 GNUNET_CORE_iterate_peers (cfg,
1314 &send_confirmation_receipt,
1315 my_p2p_crmsg);
1316 }
1317 else
1318 {
1319#if DEBUG_CHAT_SERVICE
1320 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1321 "The author of the original message is a local client."
1322 " Verifying signature of the receipt\n");
1323#endif
1324 crmsg = GNUNET_malloc (sizeof (struct ConfirmationReceiptMessage));
1325 crmsg->header.size = htons (sizeof (struct ConfirmationReceiptMessage));
1326 crmsg->header.type = htons (GNUNET_MESSAGE_TYPE_CHAT_CONFIRMATION_NOTIFICATION);
1327 crmsg->signature = p2p_crmsg->signature;
1328 crmsg->purpose = p2p_crmsg->purpose;
1329 crmsg->sequence_number = p2p_crmsg->msg_sequence_number;
1330 crmsg->reserved2 = 0;
1331 crmsg->timestamp = p2p_crmsg->timestamp;
1332 crmsg->target = p2p_crmsg->target;
1333 crmsg->author = p2p_crmsg->author;
1334 crmsg->content = p2p_crmsg->content;
1335 if (GNUNET_OK !=
1336 GNUNET_CRYPTO_rsa_verify (GNUNET_SIGNATURE_PURPOSE_CHAT_RECEIPT,
1337 &crmsg->purpose,
1338 &crmsg->signature,
1339 &target->public_key))
1340 {
1341 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1342 "Invalid signature of the receipt\n");
1343 GNUNET_break_op (0);
1344 return GNUNET_SYSERR;
1345 }
1346#if DEBUG_CHAT_SERVICE
1347 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1348 "The author of the original message is a local client."
1349 " Sending receipt to the client\n");
1350#endif
1351 GNUNET_SERVER_notification_context_unicast (nc,
1352 author->client,
1353 &crmsg->header,
1354 GNUNET_NO);
1355 GNUNET_free (crmsg);
1356 }
1357 return GNUNET_OK;
1358}
1359
1360
1361/**
1362 * Transmit a sync request to the peer.
1363 *
1364 * @param cls closure, NULL
1365 * @param size number of bytes available in buf
1366 * @param buf where the callee should write the message
1367 * @return number of bytes written to buf
1368 */
1369static size_t
1370transmit_sync_request_to_peer (void *cls,
1371 size_t size,
1372 void *buf)
1373{
1374 struct GNUNET_MessageHeader *m = buf;
1375 size_t msg_size;
1376
1377#if DEBUG_CHAT_SERVICE
1378 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Transmitting P2P sync request\n");
1379#endif
1380 msg_size = sizeof (struct GNUNET_MessageHeader);
1381 GNUNET_assert (size >= msg_size);
1382 GNUNET_assert (NULL != buf);
1383 m = buf;
1384 m->type = htons (GNUNET_MESSAGE_TYPE_CHAT_P2P_SYNC_REQUEST);
1385 m->size = htons (msg_size);
1386 return msg_size;
1387}
1388
1389
1390/**
1391 * Method called whenever a peer connects.
1392 *
1393 * @param cls closure
1394 * @param peer peer identity this notification is about
1395 * @param atsi performance data
1396 */
1397static void
1398peer_connect_handler (void *cls,
1399 const struct GNUNET_PeerIdentity *peer,
1400 const struct GNUNET_TRANSPORT_ATS_Information *atsi)
1401{
1402 struct GNUNET_CORE_TransmitHandle *th;
1403
1404 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1405 "Peer connected: %s\n", GNUNET_i2s (peer));
1406 if (0 == memcmp (peer, me, sizeof (struct GNUNET_PeerIdentity)))
1407 return;
1408 th = GNUNET_CORE_notify_transmit_ready (core,
1409 1,
1410 MAX_TRANSMIT_DELAY,
1411 peer,
1412 sizeof (struct GNUNET_MessageHeader),
1413 &transmit_sync_request_to_peer,
1414 NULL);
1415 GNUNET_assert (NULL != th);
1416}
1417
1418
1419/**
1420 * Method called whenever a peer disconnects.
1421 *
1422 * @param cls closure, not used
1423 * @param peer peer identity this notification is about
1424 */
1425static void
1426peer_disconnect_handler (void *cls,
1427 const struct GNUNET_PeerIdentity *peer)
1428{
1429 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1430 "Peer disconnected: %s\n", GNUNET_i2s (peer));
1431}
1432
1433
1434/**
1435 * Task run during shutdown.
1436 *
1437 * @param cls unused
1438 * @param tc unused
1439 */
1440static void
1441cleanup_task (void *cls,
1442 const struct GNUNET_SCHEDULER_TaskContext *tc)
1443{
1444 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Cleaning up\n");
1445 if (NULL != core)
1446 {
1447 GNUNET_CORE_disconnect (core);
1448 core = NULL;
1449 }
1450 if (NULL != nc)
1451 {
1452 GNUNET_SERVER_notification_context_destroy (nc);
1453 nc = NULL;
1454 }
1455}
1456
1457
1458/**
1459 * To be called on core init/fail.
1460 *
1461 * @param cls closure, NULL
1462 * @param server handle to the server for this service
1463 * @param identity the public identity of this peer
1464 * @param publicKey the public key of this peer
1465 */
1466static void
1467core_init (void *cls,
1468 struct GNUNET_CORE_Handle *server,
1469 const struct GNUNET_PeerIdentity *my_identity,
1470 const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *publicKey)
1471{
1472 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Core initialized\n");
1473 me = my_identity;
1474}
1475
1476
1477/**
1478 * Process chat requests.
1479 *
1480 * @param cls closure, NULL
1481 * @param server the initialized server
1482 * @param cfg configuration to use
1483 */
1484static void
1485run (void *cls,
1486 struct GNUNET_SERVER_Handle *server,
1487 const struct GNUNET_CONFIGURATION_Handle *c)
1488{
1489 static const struct GNUNET_SERVER_MessageHandler handlers[] =
1490 {
1491 { &handle_join_request, NULL,
1492 GNUNET_MESSAGE_TYPE_CHAT_JOIN_REQUEST, 0 },
1493 { &handle_transmit_request, NULL,
1494 GNUNET_MESSAGE_TYPE_CHAT_TRANSMIT_REQUEST, 0 },
1495 { &handle_acknowledge_request, NULL,
1496 GNUNET_MESSAGE_TYPE_CHAT_CONFIRMATION_RECEIPT,
1497 sizeof (struct ConfirmationReceiptMessage) },
1498 { NULL, NULL, 0, 0 }
1499 };
1500 static const struct GNUNET_CORE_MessageHandler p2p_handlers[] =
1501 {
1502 { &handle_p2p_join_notification,
1503 GNUNET_MESSAGE_TYPE_CHAT_P2P_JOIN_NOTIFICATION, 0 },
1504 { &handle_p2p_leave_notification,
1505 GNUNET_MESSAGE_TYPE_CHAT_P2P_LEAVE_NOTIFICATION,
1506 sizeof (struct P2PLeaveNotificationMessage) },
1507 { &handle_p2p_message_notification,
1508 GNUNET_MESSAGE_TYPE_CHAT_P2P_MESSAGE_NOTIFICATION, 0 },
1509 { &handle_p2p_sync_request,
1510 GNUNET_MESSAGE_TYPE_CHAT_P2P_SYNC_REQUEST,
1511 sizeof (struct GNUNET_MessageHeader) },
1512 { &handle_p2p_confirmation_receipt,
1513 GNUNET_MESSAGE_TYPE_CHAT_P2P_CONFIRMATION_RECEIPT,
1514 sizeof (struct P2PConfirmationReceiptMessage) },
1515 { NULL, 0, 0 }
1516 };
1517
1518 GNUNET_log_setup ("gnunet-service-chat",
1519#if DEBUG_CHAT_SERVICE
1520 "DEBUG",
1521#else
1522 "WARNING",
1523#endif
1524 NULL);
1525 cfg = c;
1526 nc = GNUNET_SERVER_notification_context_create (server, 16);
1527 GNUNET_SERVER_add_handlers (server, handlers);
1528 core = GNUNET_CORE_connect (cfg,
1529 QUEUE_SIZE,
1530 NULL,
1531 &core_init,
1532 &peer_connect_handler,
1533 &peer_disconnect_handler,
1534 NULL,
1535 NULL, GNUNET_NO,
1536 NULL, GNUNET_NO,
1537 p2p_handlers);
1538 GNUNET_SERVER_disconnect_notify (server,
1539 &handle_client_disconnect,
1540 NULL);
1541 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
1542 &cleanup_task,
1543 NULL);
1544}
1545
1546
1547/**
1548 * The main function for the chat service.
1549 *
1550 * @param argc number of arguments from the command line
1551 * @param argv command line arguments
1552 * @return 0 ok, 1 on error
1553 */
1554int
1555main (int argc, char *const *argv)
1556{
1557 return (GNUNET_OK ==
1558 GNUNET_SERVICE_run (argc,
1559 argv,
1560 "chat",
1561 GNUNET_SERVICE_OPTION_NONE,
1562 &run, NULL)) ? 0 : 1;
1563}
1564
1565/* end of gnunet-service-chat.c */
diff --git a/src/include/gnunet_chat_service.h b/src/include/gnunet_chat_service.h
new file mode 100644
index 000000000..059dff254
--- /dev/null
+++ b/src/include/gnunet_chat_service.h
@@ -0,0 +1,234 @@
1/*
2 This file is part of GNUnet.
3 (C) 2009, 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 include/gnunet_chat_service.h
23 * @brief API for chatting via GNUnet
24 * @author Christian Grothoff
25 * @author Nathan Evans
26 * @author Vitaly Minko
27 */
28
29#ifndef GNUNET_CHAT_SERVICE_H
30#define GNUNET_CHAT_SERVICE_H
31
32#include "gnunet_util_lib.h"
33
34#ifdef __cplusplus
35extern "C"
36{
37#if 0 /* keep Emacsens' auto-indent happy */
38}
39#endif
40#endif
41
42
43#define GNUNET_CHAT_VERSION 0x00000003
44#define MAX_MESSAGE_LENGTH (32 * 1024)
45
46/**
47 * Options for messaging. Compatible options can be OR'ed together.
48 */
49enum GNUNET_CHAT_MsgOptions
50 {
51 /**
52 * No special options.
53 */
54 GNUNET_CHAT_MSG_OPTION_NONE = 0,
55
56 /**
57 * Encrypt the message so that only the receiver can decrypt it.
58 */
59 GNUNET_CHAT_MSG_PRIVATE = 1,
60
61 /**
62 * Hide the identity of the sender.
63 */
64 GNUNET_CHAT_MSG_ANONYMOUS = 2,
65
66 /**
67 * Sign the content, authenticating the sender (using the provided private
68 * key, which may represent a pseudonym).
69 */
70 GNUNET_CHAT_MSG_AUTHENTICATED = 4,
71
72 /**
73 * Require signed acknowledgment before completing delivery (and of course,
74 * only acknowledge if delivery is guaranteed).
75 */
76 GNUNET_CHAT_MSG_ACKNOWLEDGED = 8,
77
78 /**
79 * Authenticate for the receiver, but ensure that receiver cannot prove
80 * authenticity to third parties later. (not yet implemented)
81 */
82 GNUNET_CHAT_MSG_OFF_THE_RECORD = 16,
83
84 };
85
86/**
87 * Handle for a (joined) chat room.
88 */
89struct GNUNET_CHAT_Room;
90
91/**
92 * Callback used for notification about incoming messages.
93 *
94 * @param cls closure
95 * @param room in which room was the message received?
96 * @param sender what is the ID of the sender? (maybe NULL)
97 * @param member_info information about the joining member
98 * @param message the message text
99 * @param options options for the message
100 * @return GNUNET_OK to accept the message now, GNUNET_NO to
101 * accept (but user is away), GNUNET_SYSERR to signal denied delivery
102 */
103typedef int (*GNUNET_CHAT_MessageCallback) (void *cls,
104 struct GNUNET_CHAT_Room *room,
105 const GNUNET_HashCode *sender,
106 const struct GNUNET_CONTAINER_MetaData *member_info,
107 const char *message,
108 enum GNUNET_CHAT_MsgOptions options);
109
110/**
111 * Callback used for notification that another room member has joined or left.
112 *
113 * @param member_info will be non-null if the member is joining, NULL if he is
114 * leaving
115 * @param member_id hash of public key of the user (for unique identification)
116 * @param options what types of messages is this member willing to receive?
117 * @return GNUNET_OK
118 */
119typedef int (*GNUNET_CHAT_MemberListCallback) (void *cls,
120 const struct GNUNET_CONTAINER_MetaData *member_info,
121 const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *member_id,
122 enum GNUNET_CHAT_MsgOptions options);
123
124/**
125 * Callback used for message delivery confirmations.
126 *
127 * @param cls closure
128 * @param room in which room was the message received?
129 * @param orig_seq_number sequence number of the original message
130 * @param timestamp when was the message received?
131 * @param receiver who is confirming the receipt?
132 * @param msg_hash hash of the original message
133 * @param receipt signature confirming delivery
134 * @return GNUNET_OK to continue, GNUNET_SYSERR to refuse processing further
135 * confirmations from anyone for this message
136 */
137typedef int (*GNUNET_CHAT_MessageConfirmation) (void *cls,
138 struct GNUNET_CHAT_Room *room,
139 uint32_t orig_seq_number,
140 struct GNUNET_TIME_Absolute timestamp,
141 const GNUNET_HashCode *receiver,
142 const GNUNET_HashCode *msg_hash,
143 const struct GNUNET_CRYPTO_RsaSignature *receipt);
144
145/**
146 * Join a chat room.
147 *
148 * @param cfg configuration
149 * @param nick_name nickname of the user joining (used to
150 * determine which public key to use);
151 * the nickname should probably also
152 * be used in the member_info (as "EXTRACTOR_TITLE")
153 * @param member_info information about the joining member
154 * @param room_name name of the room
155 * @param msg_options message options of the joining user
156 * @param messageCallback which function to call if a message has
157 * been received?
158 * @param message_cls argument to callback
159 * @param memberCallback which function to call for join/leave notifications
160 * @param member_cls argument to callback
161 * @param confirmationCallback which function to call for confirmations
162 * (maybe NULL)
163 * @param confirmation_cls argument to callback
164 * @param me member ID (pseudonym)
165 * @return NULL on error
166 */
167struct GNUNET_CHAT_Room *
168GNUNET_CHAT_join_room (const struct GNUNET_CONFIGURATION_Handle *cfg,
169 const char *nick_name,
170 struct GNUNET_CONTAINER_MetaData *member_info,
171 const char *room_name,
172 enum GNUNET_CHAT_MsgOptions msg_options,
173 GNUNET_CHAT_MessageCallback messageCallback,
174 void *message_cls,
175 GNUNET_CHAT_MemberListCallback memberCallback,
176 void *member_cls,
177 GNUNET_CHAT_MessageConfirmation confirmationCallback,
178 void *confirmation_cls,
179 GNUNET_HashCode *me);
180
181/**
182 * Send a message.
183 *
184 * @param room handle for the chat room
185 * @param message message to be sent
186 * @param options options for the message
187 * @param receiver use NULL to send to everyone in the room
188 * @param sequence_number where to write the sequence id of the message
189 */
190void
191GNUNET_CHAT_send_message (struct GNUNET_CHAT_Room *room,
192 const char *message,
193 enum GNUNET_CHAT_MsgOptions options,
194 const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *receiver,
195 uint32_t *sequence_number);
196
197
198/**
199 * Leave a chat room.
200 */
201void
202GNUNET_CHAT_leave_room (struct GNUNET_CHAT_Room *chat_room);
203
204
205#if 0
206/* these are not yet implemented / supported */
207/**
208 * Callback function to iterate over rooms.
209 *
210 * @return GNUNET_OK to continue, GNUNET_SYSERR to abort iteration
211 */
212typedef int (*GNUNET_CHAT_RoomIterator) (const char *room,
213 const char *topic, void *cls);
214
215/**
216 * List all of the (publically visible) chat rooms.
217 * @return number of rooms on success, GNUNET_SYSERR if iterator aborted
218 */
219int GNUNET_CHAT_list_rooms (struct GNUNET_GE_Context *ectx,
220 struct GNUNET_GC_Configuration *cfg,
221 GNUNET_CHAT_RoomIterator it, void *cls);
222#endif
223
224
225#if 0 /* keep Emacsens' auto-indent happy */
226{
227#endif
228#ifdef __cplusplus
229}
230#endif
231
232#endif
233
234/* end of gnunet_chat_service.h */
diff --git a/src/include/gnunet_protocols.h b/src/include/gnunet_protocols.h
index 20fea005a..d1b670aa3 100644
--- a/src/include/gnunet_protocols.h
+++ b/src/include/gnunet_protocols.h
@@ -738,13 +738,79 @@ extern "C"
738 738
739 739
740/** 740/**
741 * Message sent from client to join a chat room.
742 */
743#define GNUNET_MESSAGE_TYPE_CHAT_JOIN_REQUEST 300
744
745/**
746 * Message sent to client to indicate joining of another room member.
747 */
748#define GNUNET_MESSAGE_TYPE_CHAT_JOIN_NOTIFICATION 301
749
750/**
751 * Message sent to client to indicate leaving of another room member.
752 */
753#define GNUNET_MESSAGE_TYPE_CHAT_LEAVE_NOTIFICATION 302
754
755/**
756 * Notification sent by service to client indicating that we've received a chat
757 * message.
758 */
759#define GNUNET_MESSAGE_TYPE_CHAT_MESSAGE_NOTIFICATION 303
760
761/**
762 * Request sent by client to transmit a chat message to another room members.
763 */
764#define GNUNET_MESSAGE_TYPE_CHAT_TRANSMIT_REQUEST 304
765
766/**
767 * Receipt sent from a message receiver to the service to confirm delivery of
768 * a chat message.
769 */
770#define GNUNET_MESSAGE_TYPE_CHAT_CONFIRMATION_RECEIPT 305
771
772/**
773 * Notification sent from the service to the original sender
774 * to acknowledge delivery of a chat message.
775 */
776#define GNUNET_MESSAGE_TYPE_CHAT_CONFIRMATION_NOTIFICATION 306
777
778/**
779 * P2P message sent to indicate joining of another room member.
780 */
781#define GNUNET_MESSAGE_TYPE_CHAT_P2P_JOIN_NOTIFICATION 307
782
783/**
784 * P2P message sent to indicate leaving of another room member.
785 */
786#define GNUNET_MESSAGE_TYPE_CHAT_P2P_LEAVE_NOTIFICATION 308
787
788/**
789 * P2P message sent to a newly connected peer to request its known clients in
790 * order to synchronize room members.
791 */
792#define GNUNET_MESSAGE_TYPE_CHAT_P2P_SYNC_REQUEST 309
793
794/**
795 * Notification sent from one peer to another to indicate that we have received
796 * a chat message.
797 */
798#define GNUNET_MESSAGE_TYPE_CHAT_P2P_MESSAGE_NOTIFICATION 310
799
800/**
801 * P2P receipt confirming delivery of a chat message.
802 */
803#define GNUNET_MESSAGE_TYPE_CHAT_P2P_CONFIRMATION_RECEIPT 311
804
805
806/**
741 * Type used to match 'all' message types. 807 * Type used to match 'all' message types.
742 */ 808 */
743#define GNUNET_MESSAGE_TYPE_ALL 65535 809#define GNUNET_MESSAGE_TYPE_ALL 65535
744 810
745/* 811/*
746 TODO: 812 TODO:
747 - applications (FS, VPN, CHAT, TRACEKIT, TBENCH) 813 - applications (FS, VPN, TRACEKIT, TBENCH)
748*/ 814*/
749 815
750 816
diff --git a/src/include/gnunet_signatures.h b/src/include/gnunet_signatures.h
index 98c449486..4c443cc00 100644
--- a/src/include/gnunet_signatures.h
+++ b/src/include/gnunet_signatures.h
@@ -102,6 +102,15 @@ extern "C"
102 */ 102 */
103#define GNUNET_SIGNATURE_PURPOSE_DNS_RECORD 11 103#define GNUNET_SIGNATURE_PURPOSE_DNS_RECORD 11
104 104
105/**
106 * Signature of a chat message.
107 */
108#define GNUNET_SIGNATURE_PURPOSE_CHAT_MESSAGE 12
109
110/**
111 * Signature of confirmation receipt for a chat message.
112 */
113#define GNUNET_SIGNATURE_PURPOSE_CHAT_RECEIPT 13
105 114
106#if 0 /* keep Emacsens' auto-indent happy */ 115#if 0 /* keep Emacsens' auto-indent happy */
107{ 116{