aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2013-05-29 08:19:14 +0000
committerChristian Grothoff <christian@grothoff.org>2013-05-29 08:19:14 +0000
commit1bdbb8bc0cec3650843daf5eb559ed46e9a69fd7 (patch)
tree95f01ee9c62b026c4c161a62d309f25b05acc5cc
parent022002438e4047d235a688cfd9da7b63ab990103 (diff)
downloadgnunet-1bdbb8bc0cec3650843daf5eb559ed46e9a69fd7.tar.gz
gnunet-1bdbb8bc0cec3650843daf5eb559ed46e9a69fd7.zip
-removing chat from build
-rw-r--r--configure.ac2
-rw-r--r--src/chat/Makefile.am137
-rw-r--r--src/chat/chat.c822
-rw-r--r--src/chat/chat.conf.in21
-rw-r--r--src/chat/chat.h485
-rw-r--r--src/chat/gnunet-chat.c750
-rw-r--r--src/chat/gnunet-service-chat.c1713
-rw-r--r--src/chat/test_chat.c556
-rw-r--r--src/chat/test_chat_data.conf55
-rw-r--r--src/chat/test_chat_peer1.conf92
-rw-r--r--src/chat/test_chat_peer2.conf94
-rw-r--r--src/chat/test_chat_peer3.conf93
-rw-r--r--src/chat/test_chat_private.c640
13 files changed, 0 insertions, 5460 deletions
diff --git a/configure.ac b/configure.ac
index e0cc93d79..56675cbaa 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1223,8 +1223,6 @@ src/ats/Makefile
1223src/ats/ats.conf 1223src/ats/ats.conf
1224src/ats-tool/Makefile 1224src/ats-tool/Makefile
1225src/block/Makefile 1225src/block/Makefile
1226src/chat/Makefile
1227src/chat/chat.conf
1228src/core/Makefile 1226src/core/Makefile
1229src/core/core.conf 1227src/core/core.conf
1230src/consensus/Makefile 1228src/consensus/Makefile
diff --git a/src/chat/Makefile.am b/src/chat/Makefile.am
deleted file mode 100644
index a518bfd12..000000000
--- a/src/chat/Makefile.am
+++ /dev/null
@@ -1,137 +0,0 @@
1INCLUDES = -I$(top_srcdir)/src/include
2
3pkgcfgdir= $(pkgdatadir)/config.d/
4
5libexecdir= $(pkglibdir)/libexec/
6
7pkgcfg_DATA = \
8 chat.conf
9
10if MINGW
11 WINFLAGS = -Wl,--no-undefined -Wl,--export-all-symbols
12endif
13
14if USE_COVERAGE
15 AM_CFLAGS = -fprofile-arcs -ftest-coverage
16endif
17
18lib_LTLIBRARIES = libgnunetchat.la
19
20libgnunetchat_la_SOURCES = \
21 chat.c chat.h
22
23libgnunetchat_la_LIBADD = \
24 $(top_builddir)/src/util/libgnunetutil.la \
25 $(top_builddir)/src/fs/libgnunetfs.la
26
27libgnunetchat_la_DEPENDENCIES = \
28 $(top_builddir)/src/fs/libgnunetfs.la
29
30libgnunetchat_la_LDFLAGS = \
31 $(GN_LIB_LDFLAGS) $(WINFLAGS) \
32 -version-info 0:0:0
33
34libexec_PROGRAMS = \
35 gnunet-service-chat
36
37bin_PROGRAMS = \
38 gnunet-chat
39
40gnunet_service_chat_SOURCES = \
41 gnunet-service-chat.c
42gnunet_service_chat_LDADD = \
43 $(top_builddir)/src/core/libgnunetcore.la \
44 $(top_builddir)/src/util/libgnunetutil.la \
45 $(GN_LIBINTL)
46
47gnunet_chat_SOURCES = \
48 gnunet-chat.c
49gnunet_chat_LDADD = \
50 $(top_builddir)/src/chat/libgnunetchat.la \
51 $(top_builddir)/src/fs/libgnunetfs.la \
52 $(top_builddir)/src/util/libgnunetutil.la \
53 $(GN_LIBINTL)
54gnunet_chat_DEPENDENCIES = \
55 libgnunetchat.la
56
57check_PROGRAMS = \
58 test_chat \
59 test_chat_acknowledgement \
60 test_chat_anonymous \
61 test_chat_authentication \
62 test_chat_p2p \
63 test_chat_acknowledgement_p2p \
64 test_chat_anonymous_p2p \
65 test_chat_authentication_p2p \
66 test_chat_private \
67 test_chat_private_p2p
68
69if ENABLE_TEST_RUN
70TESTS = $(check_PROGRAMS)
71endif
72
73test_chat_SOURCES = \
74 test_chat.c
75test_chat_LDADD = \
76 $(top_builddir)/src/chat/libgnunetchat.la \
77 $(top_builddir)/src/util/libgnunetutil.la
78
79test_chat_acknowledgement_SOURCES = \
80 test_chat.c
81test_chat_acknowledgement_LDADD = \
82 $(top_builddir)/src/chat/libgnunetchat.la \
83 $(top_builddir)/src/util/libgnunetutil.la
84
85test_chat_anonymous_SOURCES = \
86 test_chat.c
87test_chat_anonymous_LDADD = \
88 $(top_builddir)/src/chat/libgnunetchat.la \
89 $(top_builddir)/src/util/libgnunetutil.la
90
91test_chat_authentication_SOURCES = \
92 test_chat.c
93test_chat_authentication_LDADD = \
94 $(top_builddir)/src/chat/libgnunetchat.la \
95 $(top_builddir)/src/util/libgnunetutil.la
96
97test_chat_p2p_SOURCES = \
98 test_chat.c
99test_chat_p2p_LDADD = \
100 $(top_builddir)/src/chat/libgnunetchat.la \
101 $(top_builddir)/src/util/libgnunetutil.la
102
103test_chat_acknowledgement_p2p_SOURCES = \
104 test_chat.c
105test_chat_acknowledgement_p2p_LDADD = \
106 $(top_builddir)/src/chat/libgnunetchat.la \
107 $(top_builddir)/src/util/libgnunetutil.la
108
109test_chat_anonymous_p2p_SOURCES = \
110 test_chat.c
111test_chat_anonymous_p2p_LDADD = \
112 $(top_builddir)/src/chat/libgnunetchat.la \
113 $(top_builddir)/src/util/libgnunetutil.la
114
115test_chat_authentication_p2p_SOURCES = \
116 test_chat.c
117test_chat_authentication_p2p_LDADD = \
118 $(top_builddir)/src/chat/libgnunetchat.la \
119 $(top_builddir)/src/util/libgnunetutil.la
120
121test_chat_private_SOURCES = \
122 test_chat_private.c
123test_chat_private_LDADD = \
124 $(top_builddir)/src/chat/libgnunetchat.la \
125 $(top_builddir)/src/util/libgnunetutil.la
126
127test_chat_private_p2p_SOURCES = \
128 test_chat_private.c
129test_chat_private_p2p_LDADD = \
130 $(top_builddir)/src/chat/libgnunetchat.la \
131 $(top_builddir)/src/util/libgnunetutil.la
132
133EXTRA_DIST = \
134 test_chat_data.conf \
135 test_chat_peer1.conf \
136 test_chat_peer2.conf \
137 test_chat_peer3.conf
diff --git a/src/chat/chat.c b/src/chat/chat.c
deleted file mode 100644
index 786babe34..000000000
--- a/src/chat/chat.c
+++ /dev/null
@@ -1,822 +0,0 @@
1/*
2 This file is part of GNUnet.
3 (C) 2008, 2011 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file chat/chat.c
23 * @brief convenience API for sending and receiving chat messages
24 * @author Christian Grothoff
25 * @author Nathan Evans
26 * @author Vitaly Minko
27 */
28
29#include "platform.h"
30#include "gnunet_constants.h"
31#include "gnunet_protocols.h"
32#include "gnunet_signatures.h"
33#include "gnunet_fs_service.h"
34#include "chat.h"
35
36#define DEBUG_CHAT GNUNET_EXTRA_LOGGING
37#define NICK_IDENTITY_PREFIX ".chat_identity_"
38
39
40/**
41 * Handle for a chat room.
42 */
43struct GNUNET_CHAT_Room
44{
45 struct GNUNET_CLIENT_Connection *client;
46
47 const struct GNUNET_CONFIGURATION_Handle *cfg;
48
49 struct GNUNET_CONTAINER_MetaData *member_info;
50
51 char *room_name;
52
53 struct GNUNET_CRYPTO_RsaPrivateKey *my_private_key;
54
55 struct MemberList *members;
56
57 int is_joined;
58
59 GNUNET_CHAT_JoinCallback join_callback;
60
61 void *join_callback_cls;
62
63 GNUNET_CHAT_MessageCallback message_callback;
64
65 void *message_callback_cls;
66
67 GNUNET_CHAT_MemberListCallback member_list_callback;
68
69 void *member_list_callback_cls;
70
71 GNUNET_CHAT_MessageConfirmation confirmation_callback;
72
73 void *confirmation_cls;
74
75 uint32_t sequence_number;
76
77 uint32_t msg_options;
78
79};
80
81/**
82 * Linked list of members in the chat room.
83 */
84struct MemberList
85{
86 struct MemberList *next;
87
88 /**
89 * Description of the member.
90 */
91 struct GNUNET_CONTAINER_MetaData *meta;
92
93 /**
94 * Member ID (pseudonym).
95 */
96 struct GNUNET_HashCode id;
97
98};
99
100/**
101 * Context for transmitting a send-message request.
102 */
103struct GNUNET_CHAT_SendMessageContext
104{
105 /**
106 * Handle for the chat room.
107 */
108 struct GNUNET_CHAT_Room *chat_room;
109
110 /**
111 * Message that we're sending.
112 */
113 char *message;
114
115 /**
116 * Options for the message.
117 */
118 enum GNUNET_CHAT_MsgOptions options;
119
120 /**
121 * Receiver of the message. NULL to send to everyone in the room.
122 */
123 const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *receiver;
124
125 /**
126 * Sequence id of the message.
127 */
128 uint32_t sequence_number;
129
130};
131
132/**
133 * Context for transmitting a confirmation receipt.
134 */
135struct GNUNET_CHAT_SendReceiptContext
136{
137 /**
138 * Handle for the chat room.
139 */
140 struct GNUNET_CHAT_Room *chat_room;
141
142 /**
143 * The original message that we're going to acknowledge.
144 */
145 struct ReceiveNotificationMessage *received_msg;
146
147};
148
149/**
150 * Ask client to send a join request.
151 */
152static int
153rejoin_room (struct GNUNET_CHAT_Room *chat_room);
154
155
156/**
157 * Transmit a confirmation receipt to the chat service.
158 *
159 * @param cls closure, pointer to the 'struct GNUNET_CHAT_SendReceiptContext'
160 * @param size number of bytes available in buf
161 * @param buf where the callee should write the message
162 * @return number of bytes written to buf
163 */
164static size_t
165transmit_acknowledge_request (void *cls, size_t size, void *buf)
166{
167 struct GNUNET_CHAT_SendReceiptContext *src = cls;
168 struct ConfirmationReceiptMessage *receipt;
169 struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pub_key;
170 uint16_t msg_len;
171 size_t msg_size;
172
173 if (NULL == buf)
174 {
175 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
176 _("Could not transmit confirmation receipt\n"));
177 return 0;
178 }
179#if DEBUG_CHAT
180 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
181 "Transmitting confirmation receipt to the service\n");
182#endif
183 msg_size = sizeof (struct ConfirmationReceiptMessage);
184 GNUNET_assert (size >= msg_size);
185 receipt = buf;
186 receipt->header.size = htons (msg_size);
187 receipt->header.type = htons (GNUNET_MESSAGE_TYPE_CHAT_CONFIRMATION_RECEIPT);
188 receipt->reserved = htonl (0);
189 receipt->sequence_number = src->received_msg->sequence_number;
190 receipt->reserved2 = htonl (0);
191 receipt->timestamp = GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_get ());
192 GNUNET_CRYPTO_rsa_key_get_public (src->chat_room->my_private_key, &pub_key);
193 GNUNET_CRYPTO_hash (&pub_key,
194 sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
195 &receipt->target);
196 receipt->author = src->received_msg->sender;
197 receipt->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_CHAT_RECEIPT);
198 receipt->purpose.size =
199 htonl (msg_size - sizeof (struct GNUNET_MessageHeader) -
200 sizeof (uint32_t) - sizeof (struct GNUNET_CRYPTO_RsaSignature));
201 msg_len =
202 ntohs (src->received_msg->header.size) -
203 sizeof (struct ReceiveNotificationMessage);
204 GNUNET_CRYPTO_hash (&src->received_msg[1], msg_len, &receipt->content);
205 GNUNET_assert (GNUNET_OK ==
206 GNUNET_CRYPTO_rsa_sign (src->chat_room->my_private_key,
207 &receipt->purpose,
208 &receipt->signature));
209 GNUNET_free (src->received_msg);
210 GNUNET_free (src);
211 return msg_size;
212}
213
214
215/**
216 * Handles messages received from the service. Calls the proper client
217 * callback.
218 */
219static void
220process_result (struct GNUNET_CHAT_Room *room,
221 const struct GNUNET_MessageHeader *reply)
222{
223 struct LeaveNotificationMessage *leave_msg;
224 struct JoinNotificationMessage *join_msg;
225 struct ReceiveNotificationMessage *received_msg;
226 struct ConfirmationReceiptMessage *receipt;
227 struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pkey;
228 struct GNUNET_HashCode id;
229 const struct GNUNET_HashCode *sender;
230 struct GNUNET_CONTAINER_MetaData *meta;
231 struct GNUNET_CHAT_SendReceiptContext *src;
232 struct MemberList *pos;
233 struct MemberList *prev;
234 struct GNUNET_CRYPTO_AesSessionKey key;
235 char decrypted_msg[MAX_MESSAGE_LENGTH];
236 uint16_t size;
237 uint16_t meta_len;
238 uint16_t msg_len;
239 char *message_content;
240
241 size = ntohs (reply->size);
242 switch (ntohs (reply->type))
243 {
244 case GNUNET_MESSAGE_TYPE_CHAT_JOIN_NOTIFICATION:
245#if DEBUG_CHAT
246 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got a join notification\n");
247#endif
248 if (size < sizeof (struct JoinNotificationMessage))
249 {
250 GNUNET_break (0);
251 return;
252 }
253 join_msg = (struct JoinNotificationMessage *) reply;
254 meta_len = size - sizeof (struct JoinNotificationMessage);
255 meta =
256 GNUNET_CONTAINER_meta_data_deserialize ((const char *) &join_msg[1],
257 meta_len);
258 if (NULL == meta)
259 {
260 GNUNET_break (0);
261 return;
262 }
263 pos = GNUNET_malloc (sizeof (struct MemberList));
264 pos->meta = meta;
265 GNUNET_CRYPTO_hash (&join_msg->public_key,
266 sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
267 &pos->id);
268 GNUNET_FS_pseudonym_add (room->cfg, &pos->id, meta);
269 pos->next = room->members;
270 room->members = pos;
271 if (GNUNET_NO == room->is_joined)
272 {
273 GNUNET_CRYPTO_rsa_key_get_public (room->my_private_key, &pkey);
274 if (0 ==
275 memcmp (&join_msg->public_key, &pkey,
276 sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded)))
277 {
278 room->join_callback (room->join_callback_cls);
279 room->is_joined = GNUNET_YES;
280 }
281 else
282 {
283 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
284 _("The current user must be the the first one joined\n"));
285 GNUNET_break (0);
286 return;
287 }
288 }
289 else
290 room->member_list_callback (room->member_list_callback_cls, meta,
291 &join_msg->public_key,
292 ntohl (join_msg->msg_options));
293 break;
294 case GNUNET_MESSAGE_TYPE_CHAT_LEAVE_NOTIFICATION:
295#if DEBUG_CHAT
296 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got a leave notification\n");
297#endif
298 if (size < sizeof (struct LeaveNotificationMessage))
299 {
300 GNUNET_break (0);
301 return;
302 }
303 leave_msg = (struct LeaveNotificationMessage *) reply;
304 room->member_list_callback (room->member_list_callback_cls, NULL,
305 &leave_msg->user, GNUNET_CHAT_MSG_OPTION_NONE);
306 GNUNET_CRYPTO_hash (&leave_msg->user,
307 sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
308 &id);
309 prev = NULL;
310 pos = room->members;
311 while ((NULL != pos) &&
312 (0 != memcmp (&pos->id, &id, sizeof (struct GNUNET_HashCode))))
313 {
314 prev = pos;
315 pos = pos->next;
316 }
317 GNUNET_assert (NULL != pos);
318 if (NULL == prev)
319 room->members = pos->next;
320 else
321 prev->next = pos->next;
322 GNUNET_CONTAINER_meta_data_destroy (pos->meta);
323 GNUNET_free (pos);
324 break;
325 case GNUNET_MESSAGE_TYPE_CHAT_MESSAGE_NOTIFICATION:
326#if DEBUG_CHAT
327 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got a message notification\n");
328#endif
329 if (size <= sizeof (struct ReceiveNotificationMessage))
330 {
331 GNUNET_break (0);
332 return;
333 }
334 received_msg = (struct ReceiveNotificationMessage *) reply;
335 if (0 != (ntohl (received_msg->msg_options) & GNUNET_CHAT_MSG_ACKNOWLEDGED))
336 {
337 src = GNUNET_malloc (sizeof (struct GNUNET_CHAT_SendReceiptContext));
338 src->chat_room = room;
339 src->received_msg = GNUNET_memdup (received_msg, size);
340 GNUNET_CLIENT_notify_transmit_ready (room->client,
341 sizeof (struct
342 ConfirmationReceiptMessage),
343 GNUNET_CONSTANTS_SERVICE_TIMEOUT,
344 GNUNET_YES,
345 &transmit_acknowledge_request, src);
346 }
347 msg_len = size - sizeof (struct ReceiveNotificationMessage);
348 if (0 != (ntohl (received_msg->msg_options) & GNUNET_CHAT_MSG_PRIVATE))
349 {
350 if (-1 ==
351 GNUNET_CRYPTO_rsa_decrypt (room->my_private_key,
352 &received_msg->encrypted_key, &key,
353 sizeof (struct
354 GNUNET_CRYPTO_AesSessionKey)))
355 {
356 GNUNET_break (0);
357 return;
358 }
359 msg_len =
360 GNUNET_CRYPTO_aes_decrypt (&received_msg[1], msg_len, &key,
361 (const struct
362 GNUNET_CRYPTO_AesInitializationVector *)
363 INITVALUE, decrypted_msg);
364 message_content = decrypted_msg;
365 }
366 else
367 {
368 message_content = GNUNET_malloc (msg_len + 1);
369 memcpy (message_content, &received_msg[1], msg_len);
370 }
371 message_content[msg_len] = '\0';
372 if (0 != (ntohl (received_msg->msg_options) & GNUNET_CHAT_MSG_ANONYMOUS))
373 {
374 sender = NULL;
375 meta = NULL;
376 }
377 else
378 {
379 pos = room->members;
380 while ((NULL != pos) &&
381 (0 !=
382 memcmp (&pos->id, &received_msg->sender,
383 sizeof (struct GNUNET_HashCode))))
384 pos = pos->next;
385 GNUNET_assert (NULL != pos);
386 sender = &received_msg->sender;
387 meta = pos->meta;
388 }
389 room->message_callback (room->message_callback_cls, room, sender, meta,
390 message_content,
391 GNUNET_TIME_absolute_ntoh (received_msg->timestamp),
392 ntohl (received_msg->msg_options));
393 if (message_content != decrypted_msg)
394 GNUNET_free (message_content);
395 break;
396 case GNUNET_MESSAGE_TYPE_CHAT_CONFIRMATION_NOTIFICATION:
397#if DEBUG_CHAT
398 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got a confirmation receipt\n");
399#endif
400 if (size < sizeof (struct ConfirmationReceiptMessage))
401 {
402 GNUNET_break (0);
403 return;
404 }
405 receipt = (struct ConfirmationReceiptMessage *) reply;
406 if (NULL != room->confirmation_callback)
407 room->confirmation_callback (room->confirmation_cls, room,
408 ntohl (receipt->sequence_number),
409 GNUNET_TIME_absolute_ntoh
410 (receipt->timestamp), &receipt->target);
411 break;
412 default:
413 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Unknown message type: '%u'\n"),
414 ntohs (reply->type));
415 GNUNET_break_op (0);
416 break;
417 }
418}
419
420
421/**
422 * Listen for incoming messages on this chat room. Also, support servers going
423 * away/coming back (i.e. rejoin chat room to keep server state up to date).
424 *
425 * @param cls closure, pointer to the 'struct GNUNET_CHAT_Room'
426 * @param msg message received, NULL on timeout or fatal error
427 */
428static void
429receive_results (void *cls, const struct GNUNET_MessageHeader *msg)
430{
431 struct GNUNET_CHAT_Room *chat_room = cls;
432
433#if DEBUG_CHAT
434 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got a message from the service\n");
435#endif
436 if (0 != (GNUNET_SCHEDULER_REASON_SHUTDOWN & GNUNET_SCHEDULER_get_reason ()))
437 return;
438 if (NULL == msg)
439 {
440 GNUNET_break (0);
441 rejoin_room (chat_room);
442 return;
443 }
444 process_result (chat_room, msg);
445 if (NULL == chat_room->client)
446 return; /* fatal error */
447 /* continue receiving */
448 GNUNET_CLIENT_receive (chat_room->client, &receive_results, chat_room,
449 GNUNET_TIME_UNIT_FOREVER_REL);
450}
451
452
453/**
454 * Read existing private key from file or create a new one if it does not exist
455 * yet.
456 * Returns the private key on success, NULL on error.
457 */
458static struct GNUNET_CRYPTO_RsaPrivateKey *
459init_private_key (const struct GNUNET_CONFIGURATION_Handle *cfg,
460 const char *nick_name)
461{
462 char *home;
463 char *keyfile;
464 struct GNUNET_CRYPTO_RsaPrivateKey *privKey;
465
466#if DEBUG_CHAT
467 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Initializing private key\n");
468#endif
469 if (GNUNET_OK !=
470 GNUNET_CONFIGURATION_get_value_filename (cfg, "chat", "HOME", &home))
471 {
472 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
473 "chat", "HOME");
474 return NULL;
475 }
476 GNUNET_DISK_directory_create (home);
477 if (GNUNET_YES != GNUNET_DISK_directory_test (home, GNUNET_YES))
478 {
479 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
480 _("Failed to access chat home directory `%s'\n"), home);
481 GNUNET_free (home);
482 return NULL;
483 }
484 /* read or create private key */
485 keyfile =
486 GNUNET_malloc (strlen (home) + strlen (NICK_IDENTITY_PREFIX) +
487 strlen (nick_name) + 2);
488 strcpy (keyfile, home);
489 GNUNET_free (home);
490 if (keyfile[strlen (keyfile) - 1] != DIR_SEPARATOR)
491 strcat (keyfile, DIR_SEPARATOR_STR);
492 strcat (keyfile, NICK_IDENTITY_PREFIX);
493 strcat (keyfile, nick_name);
494 privKey = GNUNET_CRYPTO_rsa_key_create_from_file (keyfile);
495 if (NULL == privKey)
496 {
497 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
498 _("Failed to create/open key in file `%s'\n"), keyfile);
499 }
500 GNUNET_free (keyfile);
501 return privKey;
502}
503
504
505/**
506 * Transmit a join request to the chat service.
507 *
508 * @param cls closure, pointer to the 'struct GNUNET_CHAT_Room'
509 * @param size number of bytes available in buf
510 * @param buf where the callee should write the message
511 * @return number of bytes written to buf
512 */
513static size_t
514transmit_join_request (void *cls, size_t size, void *buf)
515{
516 struct GNUNET_CHAT_Room *chat_room = cls;
517 struct JoinRequestMessage *join_msg;
518 char *room;
519 char *meta;
520 size_t room_len;
521 ssize_t meta_len;
522 size_t size_of_join;
523
524 if (NULL == buf)
525 {
526#if DEBUG_CHAT
527 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
528 "Could not transmit join request, retrying...\n");
529#endif
530 rejoin_room (chat_room);
531 return 0;
532 }
533#if DEBUG_CHAT
534 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
535 "Transmitting join request to the service\n");
536#endif
537 room_len = strlen (chat_room->room_name);
538 meta_len =
539 GNUNET_CONTAINER_meta_data_get_serialized_size (chat_room->member_info);
540 size_of_join = sizeof (struct JoinRequestMessage) + meta_len + room_len;
541 GNUNET_assert (size >= size_of_join);
542 join_msg = buf;
543 join_msg->header.size = htons (size);
544 join_msg->header.type = htons (GNUNET_MESSAGE_TYPE_CHAT_JOIN_REQUEST);
545 join_msg->msg_options = htonl (chat_room->msg_options);
546 join_msg->room_name_len = htons (room_len);
547 join_msg->reserved = htons (0);
548 join_msg->reserved2 = htonl (0);
549 GNUNET_CRYPTO_rsa_key_get_public (chat_room->my_private_key,
550 &join_msg->public_key);
551 room = (char *) &join_msg[1];
552 memcpy (room, chat_room->room_name, room_len);
553 meta = &room[room_len];
554 if (GNUNET_SYSERR ==
555 GNUNET_CONTAINER_meta_data_serialize (chat_room->member_info, &meta,
556 meta_len,
557 GNUNET_CONTAINER_META_DATA_SERIALIZE_FULL))
558 {
559 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Could not serialize metadata\n"));
560 return 0;
561 }
562 GNUNET_CLIENT_receive (chat_room->client, &receive_results, chat_room,
563 GNUNET_TIME_UNIT_FOREVER_REL);
564 return size_of_join;
565}
566
567
568/**
569 * Ask to send a join request.
570 */
571static int
572rejoin_room (struct GNUNET_CHAT_Room *chat_room)
573{
574 size_t size_of_join;
575
576 size_of_join =
577 sizeof (struct JoinRequestMessage) +
578 GNUNET_CONTAINER_meta_data_get_serialized_size (chat_room->member_info) +
579 strlen (chat_room->room_name);
580 if (NULL ==
581 GNUNET_CLIENT_notify_transmit_ready (chat_room->client, size_of_join,
582 GNUNET_CONSTANTS_SERVICE_TIMEOUT,
583 GNUNET_YES, &transmit_join_request,
584 chat_room))
585 return GNUNET_SYSERR;
586 return GNUNET_OK;
587}
588
589
590/**
591 * Leave a chat room.
592 */
593void
594GNUNET_CHAT_leave_room (struct GNUNET_CHAT_Room *chat_room)
595{
596 struct MemberList *pos;
597
598#if DEBUG_CHAT
599 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Leaving the room '%s'\n",
600 chat_room->room_name);
601#endif
602 GNUNET_CLIENT_disconnect (chat_room->client);
603 GNUNET_free (chat_room->room_name);
604 GNUNET_CONTAINER_meta_data_destroy (chat_room->member_info);
605 GNUNET_CRYPTO_rsa_key_free (chat_room->my_private_key);
606 while (NULL != chat_room->members)
607 {
608 pos = chat_room->members;
609 chat_room->members = pos->next;
610 GNUNET_CONTAINER_meta_data_destroy (pos->meta);
611 GNUNET_free (pos);
612 }
613 GNUNET_free (chat_room);
614}
615
616
617/**
618 * Join a chat room.
619 *
620 * @param cfg configuration
621 * @param nick_name nickname of the user joining (used to
622 * determine which public key to use);
623 * the nickname should probably also
624 * be used in the member_info (as "EXTRACTOR_TITLE")
625 * @param member_info information about the joining member
626 * @param room_name name of the room
627 * @param msg_options message options of the joining user
628 * @param joinCallback function to call on successful join
629 * @param join_cls closure for joinCallback
630 * @param messageCallback which function to call if a message has
631 * been received?
632 * @param message_cls argument to callback
633 * @param memberCallback which function to call for join/leave notifications
634 * @param member_cls argument to callback
635 * @param confirmationCallback which function to call for confirmations (maybe NULL)
636 * @param confirmation_cls argument to callback
637 * @param me member ID (pseudonym)
638 * @return NULL on error
639 */
640struct GNUNET_CHAT_Room *
641GNUNET_CHAT_join_room (const struct GNUNET_CONFIGURATION_Handle *cfg,
642 const char *nick_name,
643 struct GNUNET_CONTAINER_MetaData *member_info,
644 const char *room_name,
645 enum GNUNET_CHAT_MsgOptions msg_options,
646 GNUNET_CHAT_JoinCallback joinCallback, void *join_cls,
647 GNUNET_CHAT_MessageCallback messageCallback,
648 void *message_cls,
649 GNUNET_CHAT_MemberListCallback memberCallback,
650 void *member_cls,
651 GNUNET_CHAT_MessageConfirmation confirmationCallback,
652 void *confirmation_cls, struct GNUNET_HashCode * me)
653{
654 struct GNUNET_CHAT_Room *chat_room;
655 struct GNUNET_CRYPTO_RsaPrivateKey *priv_key;
656 struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pub_key;
657 struct GNUNET_CLIENT_Connection *client;
658
659#if DEBUG_CHAT
660 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Joining the room '%s'\n", room_name);
661#endif
662 priv_key = init_private_key (cfg, nick_name);
663 if (NULL == priv_key)
664 return NULL;
665 GNUNET_CRYPTO_rsa_key_get_public (priv_key, &pub_key);
666 GNUNET_CRYPTO_hash (&pub_key,
667 sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
668 me);
669 GNUNET_FS_pseudonym_add (cfg, me, member_info);
670 client = GNUNET_CLIENT_connect ("chat", cfg);
671 if (NULL == client)
672 {
673 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
674 _("Failed to connect to the chat service\n"));
675 return NULL;
676 }
677 if (NULL == joinCallback)
678 {
679 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
680 _("Undefined mandatory parameter: joinCallback\n"));
681 return NULL;
682 }
683 if (NULL == messageCallback)
684 {
685 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
686 _("Undefined mandatory parameter: messageCallback\n"));
687 return NULL;
688 }
689 if (NULL == memberCallback)
690 {
691 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
692 _("Undefined mandatory parameter: memberCallback\n"));
693 return NULL;
694 }
695 chat_room = GNUNET_malloc (sizeof (struct GNUNET_CHAT_Room));
696 chat_room->msg_options = msg_options;
697 chat_room->room_name = GNUNET_strdup (room_name);
698 chat_room->member_info = GNUNET_CONTAINER_meta_data_duplicate (member_info);
699 chat_room->my_private_key = priv_key;
700 chat_room->is_joined = GNUNET_NO;
701 chat_room->join_callback = joinCallback;
702 chat_room->join_callback_cls = join_cls;
703 chat_room->message_callback = messageCallback;
704 chat_room->message_callback_cls = message_cls;
705 chat_room->member_list_callback = memberCallback;
706 chat_room->member_list_callback_cls = member_cls;
707 chat_room->confirmation_callback = confirmationCallback;
708 chat_room->confirmation_cls = confirmation_cls;
709 chat_room->cfg = cfg;
710 chat_room->client = client;
711 chat_room->members = NULL;
712 if (GNUNET_SYSERR == rejoin_room (chat_room))
713 {
714 GNUNET_CHAT_leave_room (chat_room);
715 return NULL;
716 }
717 return chat_room;
718}
719
720
721/**
722 * Transmit a send-message request to the chat service.
723 *
724 * @param cls closure, pointer to the 'struct GNUNET_CHAT_SendMessageContext'
725 * @param size number of bytes available in buf
726 * @param buf where the callee should write the message
727 * @return number of bytes written to buf
728 */
729static size_t
730transmit_send_request (void *cls, size_t size, void *buf)
731{
732 struct GNUNET_CHAT_SendMessageContext *smc = cls;
733 struct TransmitRequestMessage *msg_to_send;
734 size_t msg_size;
735
736 if (NULL == buf)
737 {
738#if DEBUG_CHAT
739 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Could not transmit a chat message\n");
740#endif
741 return 0;
742 }
743#if DEBUG_CHAT
744 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
745 "Transmitting a chat message to the service\n");
746#endif
747 msg_size = strlen (smc->message) + sizeof (struct TransmitRequestMessage);
748 GNUNET_assert (size >= msg_size);
749 msg_to_send = buf;
750 msg_to_send->header.size = htons (msg_size);
751 msg_to_send->header.type = htons (GNUNET_MESSAGE_TYPE_CHAT_TRANSMIT_REQUEST);
752 msg_to_send->msg_options = htonl (smc->options);
753 msg_to_send->sequence_number = htonl (smc->sequence_number);
754 msg_to_send->timestamp =
755 GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_get ());
756 msg_to_send->reserved = htonl (0);
757 if (NULL == smc->receiver)
758 memset (&msg_to_send->target, 0, sizeof (struct GNUNET_HashCode));
759 else
760 GNUNET_CRYPTO_hash (smc->receiver,
761 sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
762 &msg_to_send->target);
763 memcpy (&msg_to_send[1], smc->message, strlen (smc->message));
764 /**
765 * Client don't encode private messages since public keys of other members are
766 * stored on the service side.
767 */
768 if (smc->options & GNUNET_CHAT_MSG_AUTHENTICATED)
769 {
770 msg_to_send->purpose.purpose =
771 htonl (GNUNET_SIGNATURE_PURPOSE_CHAT_MESSAGE);
772 msg_to_send->purpose.size =
773 htonl (msg_size - sizeof (struct GNUNET_MessageHeader) -
774 sizeof (struct GNUNET_CRYPTO_RsaSignature));
775 GNUNET_assert (GNUNET_OK ==
776 GNUNET_CRYPTO_rsa_sign (smc->chat_room->my_private_key,
777 &msg_to_send->purpose,
778 &msg_to_send->signature));
779 }
780 GNUNET_free (smc->message);
781 GNUNET_free (smc);
782 return msg_size;
783}
784
785
786/**
787 * Send a message.
788 *
789 * @param room handle for the chat room
790 * @param message message to be sent
791 * @param options options for the message
792 * @param receiver use NULL to send to everyone in the room
793 * @param sequence_number where to write the sequence id of the message
794 */
795void
796GNUNET_CHAT_send_message (struct GNUNET_CHAT_Room *room, const char *message,
797 enum GNUNET_CHAT_MsgOptions options,
798 const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded
799 *receiver, uint32_t * sequence_number)
800{
801 size_t msg_size;
802 struct GNUNET_CHAT_SendMessageContext *smc;
803
804#if DEBUG_CHAT
805 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending a message\n");
806#endif
807 room->sequence_number++;
808 if (NULL != sequence_number)
809 *sequence_number = room->sequence_number;
810 smc = GNUNET_malloc (sizeof (struct GNUNET_CHAT_SendMessageContext));
811 smc->chat_room = room;
812 smc->message = GNUNET_strdup (message);
813 smc->options = options;
814 smc->receiver = receiver;
815 smc->sequence_number = room->sequence_number;
816 msg_size = strlen (message) + sizeof (struct TransmitRequestMessage);
817 GNUNET_CLIENT_notify_transmit_ready (room->client, msg_size,
818 GNUNET_CONSTANTS_SERVICE_TIMEOUT,
819 GNUNET_YES, &transmit_send_request, smc);
820}
821
822/* end of chat.c */
diff --git a/src/chat/chat.conf.in b/src/chat/chat.conf.in
deleted file mode 100644
index 560931e67..000000000
--- a/src/chat/chat.conf.in
+++ /dev/null
@@ -1,21 +0,0 @@
1[chat]
2AUTOSTART = YES
3@UNIXONLY@ PORT = 2090
4HOSTNAME = localhost
5HOME = $SERVICEHOME
6BINARY = gnunet-service-chat
7ACCEPT_FROM = 127.0.0.1;
8ACCEPT_FROM6 = ::1;
9UNIXPATH = /tmp/gnunet-service-chat.sock
10UNIX_MATCH_UID = NO
11UNIX_MATCH_GID = YES
12# DISABLE_SOCKET_FORWARDING = NO
13# USERNAME =
14# MAXBUF =
15# TIMEOUT =
16# DISABLEV6 =
17# BINDTO =
18# REJECT_FROM =
19# REJECT_FROM6 =
20# PREFIX =
21
diff --git a/src/chat/chat.h b/src/chat/chat.h
deleted file mode 100644
index c65b75de9..000000000
--- a/src/chat/chat.h
+++ /dev/null
@@ -1,485 +0,0 @@
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
44GNUNET_NETWORK_STRUCT_BEGIN
45
46/**
47 * Notification sent by service to client indicating that we've received a chat
48 * message. After this struct, the remaining bytes are the actual text message.
49 * If the mesasge is private, then the text is encrypted, otherwise it's
50 * plaintext.
51 */
52struct ReceiveNotificationMessage
53{
54 /**
55 * Message type will be GNUNET_MESSAGE_TYPE_CHAT_MESSAGE_NOTIFICATION
56 */
57 struct GNUNET_MessageHeader header;
58
59 /**
60 * Message options, see GNUNET_CHAT_MsgOptions.
61 */
62 uint32_t msg_options GNUNET_PACKED;
63
64 /**
65 * Sequence number of the message (unique per sender).
66 */
67 uint32_t sequence_number GNUNET_PACKED;
68
69 /**
70 * For alignment (should be zero).
71 */
72 uint32_t reserved GNUNET_PACKED;
73
74 /**
75 * Timestamp of the message.
76 */
77 struct GNUNET_TIME_AbsoluteNBO timestamp;
78
79 /**
80 * Hash of the public key of the pseudonym of the sender of the message.
81 * Should be all zeros for anonymous.
82 */
83 struct GNUNET_HashCode sender;
84
85 /**
86 * The encrypted session key.
87 */
88 struct GNUNET_CRYPTO_RsaEncryptedData encrypted_key;
89
90};
91
92
93/**
94 * Request sent by client to transmit a chat message to another room members.
95 * After this struct, the remaining bytes are the actual message in plaintext.
96 * Private messages are encrypted on the service side.
97 */
98struct TransmitRequestMessage
99{
100 /**
101 * Message type will be GNUNET_MESSAGE_TYPE_CHAT_TRANSMIT_REQUEST
102 */
103 struct GNUNET_MessageHeader header;
104
105 /**
106 * For alignment (should be zero).
107 */
108 uint32_t reserved GNUNET_PACKED;
109
110 /**
111 * Signature confirming receipt. Signature covers everything from header
112 * through content.
113 */
114 struct GNUNET_CRYPTO_RsaSignature signature;
115
116 /**
117 * What is being signed and why?
118 */
119 struct GNUNET_CRYPTO_RsaSignaturePurpose purpose;
120
121 /**
122 * Desired message options, see GNUNET_CHAT_MsgOptions.
123 */
124 uint32_t msg_options GNUNET_PACKED;
125
126 /**
127 * Sequence number of the message (unique per sender).
128 */
129 uint32_t sequence_number GNUNET_PACKED;
130
131 /**
132 * Timestamp of the message.
133 */
134 struct GNUNET_TIME_AbsoluteNBO timestamp;
135
136 /**
137 * Who should receive this message? Set to all zeros for "everyone".
138 */
139 struct GNUNET_HashCode target;
140
141};
142
143
144/**
145 * Receipt sent from a message receiver to the service to confirm delivery of
146 * a chat message and from the service to sender of the original message to
147 * acknowledge delivery.
148 */
149struct ConfirmationReceiptMessage
150{
151 /**
152 * Message type will be
153 * GNUNET_MESSAGE_TYPE_CHAT_CONFIRMATION_RECEIPT when sending from client,
154 * GNUNET_MESSAGE_TYPE_CHAT_CONFIRMATION_NOTIFICATION when sending to client.
155 */
156 struct GNUNET_MessageHeader header;
157
158 /**
159 * For alignment (should be zero).
160 */
161 uint32_t reserved GNUNET_PACKED;
162
163 /**
164 * Signature confirming receipt. Signature covers everything from header
165 * through content.
166 */
167 struct GNUNET_CRYPTO_RsaSignature signature;
168
169 /**
170 * What is being signed and why?
171 */
172 struct GNUNET_CRYPTO_RsaSignaturePurpose purpose;
173
174 /**
175 * Sequence number of the original message.
176 */
177 uint32_t sequence_number GNUNET_PACKED;
178
179 /**
180 * For alignment (should be zero).
181 */
182 uint32_t reserved2 GNUNET_PACKED;
183
184 /**
185 * Time of receipt.
186 */
187 struct GNUNET_TIME_AbsoluteNBO timestamp;
188
189 /**
190 * Who is confirming the receipt?
191 */
192 struct GNUNET_HashCode target;
193
194 /**
195 * Who is the author of the chat message?
196 */
197 struct GNUNET_HashCode author;
198
199 /**
200 * Hash of the (possibly encrypted) content.
201 */
202 struct GNUNET_HashCode content;
203
204};
205
206
207/**
208 * Message send from client to daemon to join a chat room.
209 * This struct is followed by the room name and then
210 * the serialized ECRS meta data describing the new member.
211 */
212struct JoinRequestMessage
213{
214 /**
215 * Message type will be GNUNET_MESSAGE_TYPE_CHAT_JOIN_REQUEST
216 */
217 struct GNUNET_MessageHeader header;
218
219 /**
220 * Options. Set all options that this client is willing to receive.
221 * For example, if the client does not want to receive anonymous or
222 * OTR messages but is willing to generate acknowledgements and
223 * receive private messages, this should be set to
224 * GNUNET_CHAT_MSG_PRIVATE | GNUNET_CHAT_MSG_ACKNOWLEDGED.
225 */
226 uint32_t msg_options GNUNET_PACKED;
227
228 /**
229 * Length of the room name.
230 */
231 uint16_t room_name_len GNUNET_PACKED;
232
233 /**
234 * For alignment (should be zero).
235 */
236 uint16_t reserved GNUNET_PACKED;
237 uint32_t reserved2 GNUNET_PACKED;
238
239 /**
240 * Public key of the joining member.
241 */
242 struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded public_key;
243
244};
245
246
247/**
248 * Message send by server to client to indicate joining of another room member.
249 * This struct is followed by the serialized ECRS MetaData describing the new
250 * member.
251 */
252struct JoinNotificationMessage
253{
254 /**
255 * Message type will be GNUNET_MESSAGE_TYPE_CHAT_JOIN_NOTIFICATION
256 */
257 struct GNUNET_MessageHeader header;
258
259 /**
260 * Options. Set to all options that the new user is willing to
261 * process. For example, if the client does not want to receive
262 * anonymous or OTR messages but is willing to generate
263 * acknowledgements and receive private messages, this should be set
264 * to GNUNET_CHAT_MSG_PRIVATE | GNUNET_CHAT_MSG_ACKNOWLEDGED.
265 */
266 uint32_t msg_options GNUNET_PACKED;
267
268 /**
269 * Public key of the new user.
270 */
271 struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded public_key;
272
273};
274
275
276/**
277 * Message send by server to client to indicate leaving of another room member.
278 */
279struct LeaveNotificationMessage
280{
281 /**
282 * Message type will be GNUNET_MESSAGE_TYPE_CHAT_LEAVE_NOTIFICATION
283 */
284 struct GNUNET_MessageHeader header;
285
286 /**
287 * Reserved (for alignment).
288 */
289 uint32_t reserved GNUNET_PACKED;
290
291 /**
292 * Who is leaving?
293 */
294 struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded user;
295
296};
297
298
299/**
300 * Peer-to-peer messages
301 */
302
303/**
304 * Message send by one peer to another to indicate joining of another room
305 * member. This struct is followed by the room name and then the serialized
306 * ECRS MetaData describing the new member.
307 */
308struct P2PJoinNotificationMessage
309{
310 /**
311 * Message type will be GNUNET_MESSAGE_TYPE_CHAT_P2P_JOIN_NOTIFICATION
312 */
313 struct GNUNET_MessageHeader header;
314
315 /**
316 * Options. Set all options that this client is willing to receive.
317 * For example, if the client does not want to receive anonymous or
318 * OTR messages but is willing to generate acknowledgements and
319 * receive private messages, this should be set to
320 * GNUNET_CHAT_MSG_PRIVATE | GNUNET_CHAT_MSG_ACKNOWLEDGED.
321 */
322 uint32_t msg_options GNUNET_PACKED;
323
324 /**
325 * Length of the room name.
326 */
327 uint16_t room_name_len GNUNET_PACKED;
328
329 /**
330 * Reserved (should be zero).
331 */
332 uint16_t reserved GNUNET_PACKED;
333 uint32_t reserved2 GNUNET_PACKED;
334
335 /**
336 * Public key of the joining member.
337 */
338 struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded public_key;
339
340};
341
342
343/**
344 * Message send by one peer to another to indicate leaving of another room
345 * member.
346 */
347struct P2PLeaveNotificationMessage
348{
349 /**
350 * Message type will be GNUNET_MESSAGE_TYPE_CHAT_P2P_LEAVE_NOTIFICATION
351 */
352 struct GNUNET_MessageHeader header;
353
354 /**
355 * Reserved (for alignment).
356 */
357 uint32_t reserved GNUNET_PACKED;
358
359 /**
360 * Who is leaving?
361 */
362 struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded user;
363
364};
365
366
367/**
368 * Message send by one peer to another to indicate receiving of a chat message.
369 * This struct is followed by the room name (only if the message is anonymous)
370 * and then the remaining bytes are the actual text message. If the mesasge is
371 * private, then the text is encrypted, otherwise it's plaintext.
372 */
373struct P2PReceiveNotificationMessage
374{
375 /**
376 * Message type will be GNUNET_MESSAGE_TYPE_CHAT_P2P_MESSAGE_NOTIFICATION
377 */
378 struct GNUNET_MessageHeader header;
379
380 /**
381 * Message options, see GNUNET_CHAT_MsgOptions.
382 */
383 uint32_t msg_options GNUNET_PACKED;
384
385 /**
386 * Sequence number of the message (unique per sender).
387 */
388 uint32_t sequence_number GNUNET_PACKED;
389
390 /**
391 * Length of the room name. This is only used for anonymous messages.
392 */
393 uint16_t room_name_len GNUNET_PACKED;
394
395 /**
396 * Reserved (for alignment).
397 */
398 uint16_t reserved GNUNET_PACKED;
399
400 /**
401 * Timestamp of the message.
402 */
403 struct GNUNET_TIME_AbsoluteNBO timestamp;
404
405 /**
406 * Hash of the public key of the pseudonym of the sender of the message
407 * Should be all zeros for anonymous.
408 */
409 struct GNUNET_HashCode sender;
410
411 /**
412 * Who should receive this message? Set to all zeros for "everyone".
413 */
414 struct GNUNET_HashCode target;
415
416 /**
417 * The encrypted session key.
418 */
419 struct GNUNET_CRYPTO_RsaEncryptedData encrypted_key;
420
421};
422
423
424/**
425 * Receipt sent from one peer to another to confirm delivery of a chat message.
426 */
427struct P2PConfirmationReceiptMessage
428{
429 /**
430 * Message type will be GNUNET_MESSAGE_TYPE_CHAT_P2P_CONFIRMATION_RECEIPT
431 */
432 struct GNUNET_MessageHeader header;
433
434 /**
435 * For alignment (should be zero).
436 */
437 uint32_t reserved GNUNET_PACKED;
438
439 /**
440 * Signature confirming receipt. Signature covers everything from header
441 * through content.
442 */
443 struct GNUNET_CRYPTO_RsaSignature signature;
444
445 /**
446 * What is being signed and why?
447 */
448 struct GNUNET_CRYPTO_RsaSignaturePurpose purpose;
449
450 /**
451 * Sequence number of the original message.
452 */
453 uint32_t msg_sequence_number GNUNET_PACKED;
454
455 /**
456 * Sequence number of the receipt.
457 */
458 uint32_t sequence_number GNUNET_PACKED;
459
460 /**
461 * Time of receipt.
462 */
463 struct GNUNET_TIME_AbsoluteNBO timestamp;
464
465 /**
466 * Who is confirming the receipt?
467 */
468 struct GNUNET_HashCode target;
469
470 /**
471 * Who is the author of the chat message?
472 */
473 struct GNUNET_HashCode author;
474
475 /**
476 * Hash of the (possibly encrypted) content.
477 */
478 struct GNUNET_HashCode content;
479
480};
481GNUNET_NETWORK_STRUCT_END
482
483#endif
484
485/* end of chat.h */
diff --git a/src/chat/gnunet-chat.c b/src/chat/gnunet-chat.c
deleted file mode 100644
index 2d2f8f835..000000000
--- a/src/chat/gnunet-chat.c
+++ /dev/null
@@ -1,750 +0,0 @@
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 "gnunet_fs_service.h"
34#include <fcntl.h>
35
36static int ret;
37
38static const struct GNUNET_CONFIGURATION_Handle *cfg;
39
40static char *nickname;
41
42static char *room_name;
43
44static struct GNUNET_CONTAINER_MetaData *meta;
45
46static struct GNUNET_CHAT_Room *room;
47
48static GNUNET_SCHEDULER_TaskIdentifier handle_cmd_task;
49
50typedef int (*ActionFunction)(const char *argumetns, const void *xtra);
51
52struct ChatCommand
53{
54 const char *command;
55 ActionFunction Action;
56 const char *helptext;
57};
58
59struct UserList
60{
61 struct UserList *next;
62 struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pkey;
63 int ignored;
64};
65
66static struct UserList *users;
67
68static void
69free_user_list ()
70{
71 struct UserList *next;
72
73 while (NULL != users)
74 {
75 next = users->next;
76 GNUNET_free (users);
77 users = next;
78 }
79}
80
81static int
82do_help (const char *args, const void *xtra);
83
84
85/**
86 * Callback used for notification that we have joined the room.
87 *
88 * @param cls closure
89 * @return GNUNET_OK
90 */
91static int
92join_cb (void *cls)
93{
94 FPRINTF (stdout, "%s", _("Joined\n"));
95 return GNUNET_OK;
96}
97
98
99/**
100 * Callback used for notification about incoming messages.
101 *
102 * @param cls closure, NULL
103 * @param room in which room was the message received?
104 * @param sender what is the ID of the sender? (maybe NULL)
105 * @param member_info information about the joining member
106 * @param message the message text
107 * @param timestamp time when the member joined
108 * @param options options for the message
109 * @return GNUNET_OK to accept the message now, GNUNET_NO to
110 * accept (but user is away), GNUNET_SYSERR to signal denied delivery
111 */
112static int
113receive_cb (void *cls, struct GNUNET_CHAT_Room *room,
114 const struct GNUNET_HashCode * sender,
115 const struct GNUNET_CONTAINER_MetaData *member_info,
116 const char *message, struct GNUNET_TIME_Absolute timestamp,
117 enum GNUNET_CHAT_MsgOptions options)
118{
119 char *non_unique_nick;
120 char *nick;
121 int nick_is_a_dup;
122 const char *timestr;
123 const char *fmt;
124
125 if (NULL == sender)
126 nick = GNUNET_strdup (_("anonymous"));
127 else
128 {
129 if (GNUNET_OK != GNUNET_FS_pseudonym_get_info (cfg,
130 sender, NULL, NULL, &non_unique_nick, &nick_is_a_dup)
131 || (nick_is_a_dup == GNUNET_YES))
132 {
133 GNUNET_free (non_unique_nick);
134 non_unique_nick = GNUNET_strdup (_("anonymous"));
135 }
136 nick = GNUNET_FS_pseudonym_name_uniquify (cfg, sender, non_unique_nick, NULL);
137 GNUNET_free (non_unique_nick);
138 }
139
140 fmt = NULL;
141 switch ((int) options)
142 {
143 case GNUNET_CHAT_MSG_OPTION_NONE:
144 case GNUNET_CHAT_MSG_ANONYMOUS:
145 fmt = _("(%s) `%s' said: %s\n");
146 break;
147 case GNUNET_CHAT_MSG_PRIVATE:
148 fmt = _("(%s) `%s' said to you: %s\n");
149 break;
150 case GNUNET_CHAT_MSG_PRIVATE | GNUNET_CHAT_MSG_ANONYMOUS:
151 fmt = _("(%s) `%s' said to you: %s\n");
152 break;
153 case GNUNET_CHAT_MSG_AUTHENTICATED:
154 fmt = _("(%s) `%s' said for sure: %s\n");
155 break;
156 case GNUNET_CHAT_MSG_PRIVATE | GNUNET_CHAT_MSG_AUTHENTICATED:
157 fmt = _("(%s) `%s' said to you for sure: %s\n");
158 break;
159 case GNUNET_CHAT_MSG_ACKNOWLEDGED:
160 fmt = _("(%s) `%s' was confirmed that you received: %s\n");
161 break;
162 case GNUNET_CHAT_MSG_PRIVATE | GNUNET_CHAT_MSG_ACKNOWLEDGED:
163 fmt = _("(%s) `%s' was confirmed that you and only you received: %s\n");
164 break;
165 case GNUNET_CHAT_MSG_AUTHENTICATED | GNUNET_CHAT_MSG_ACKNOWLEDGED:
166 fmt = _("(%s) `%s' was confirmed that you received from him or her: %s\n");
167 break;
168 case GNUNET_CHAT_MSG_AUTHENTICATED | GNUNET_CHAT_MSG_PRIVATE | GNUNET_CHAT_MSG_ACKNOWLEDGED:
169 fmt =
170 _
171 ("(%s) `%s' was confirmed that you and only you received from him or her: %s\n");
172 break;
173 case GNUNET_CHAT_MSG_OFF_THE_RECORD:
174 fmt = _("(%s) `%s' said off the record: %s\n");
175 break;
176 default:
177 fmt = _("(%s) <%s> said using an unknown message type: %s\n");
178 break;
179 }
180 timestr = GNUNET_STRINGS_absolute_time_to_string (timestamp);
181 FPRINTF (stdout, fmt, timestr, nick, message);
182 GNUNET_free (nick);
183 return GNUNET_OK;
184}
185
186
187/**
188 * Callback used for message delivery confirmations.
189 *
190 * @param cls closure, NULL
191 * @param room in which room was the message received?
192 * @param orig_seq_number sequence number of the original message
193 * @param timestamp when was the message received?
194 * @param receiver who is confirming the receipt?
195 * @return GNUNET_OK to continue, GNUNET_SYSERR to refuse processing further
196 * confirmations from anyone for this message
197 */
198static int
199confirmation_cb (void *cls, struct GNUNET_CHAT_Room *room,
200 uint32_t orig_seq_number,
201 struct GNUNET_TIME_Absolute timestamp,
202 const struct GNUNET_HashCode * receiver)
203{
204 char *nick;
205 char *unique_nick;
206 int nick_is_a_dup;
207
208 if (GNUNET_OK != GNUNET_FS_pseudonym_get_info (cfg,
209 receiver, NULL, NULL, &nick, &nick_is_a_dup)
210 || (nick_is_a_dup == GNUNET_YES))
211 {
212 GNUNET_free (nick);
213 nick = GNUNET_strdup (_("anonymous"));
214 }
215 unique_nick = GNUNET_FS_pseudonym_name_uniquify (cfg, receiver, nick, NULL);
216 GNUNET_free (nick);
217 FPRINTF (stdout, _("'%s' acknowledged message #%d\n"), unique_nick, orig_seq_number);
218 GNUNET_free (unique_nick);
219 return GNUNET_OK;
220}
221
222
223/**
224 * Callback used for notification that another room member has joined or left.
225 *
226 * @param cls closure (not used)
227 * @param member_info will be non-null if the member is joining, NULL if he is
228 * leaving
229 * @param member_id hash of public key of the user (for unique identification)
230 * @param options what types of messages is this member willing to receive?
231 * @return GNUNET_OK
232 */
233static int
234member_list_cb (void *cls, const struct GNUNET_CONTAINER_MetaData *member_info,
235 const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *member_id,
236 enum GNUNET_CHAT_MsgOptions options)
237{
238 char *nick;
239 char *non_unique_nick;
240 int nick_is_a_dup;
241 struct GNUNET_HashCode id;
242 struct UserList *pos;
243 struct UserList *prev;
244
245 GNUNET_CRYPTO_hash (member_id,
246 sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
247 &id);
248 if (GNUNET_OK != GNUNET_FS_pseudonym_get_info (cfg,
249 &id, NULL, NULL, &non_unique_nick, &nick_is_a_dup)
250 || (nick_is_a_dup == GNUNET_YES))
251 {
252 GNUNET_free (non_unique_nick);
253 non_unique_nick = GNUNET_strdup (_("anonymous"));
254 }
255 nick = GNUNET_FS_pseudonym_name_uniquify (cfg, &id, non_unique_nick, NULL);
256 GNUNET_free (non_unique_nick);
257
258 FPRINTF (stdout,
259 member_info !=
260 NULL ? _("`%s' entered the room\n") : _("`%s' left the room\n"),
261 nick);
262 GNUNET_free (nick);
263 if (NULL != member_info)
264 {
265 /* user joining */
266 pos = GNUNET_malloc (sizeof (struct UserList));
267 pos->next = users;
268 pos->pkey = *member_id;
269 pos->ignored = GNUNET_NO;
270 users = pos;
271 }
272 else
273 {
274 /* user leaving */
275 prev = NULL;
276 pos = users;
277 while ((NULL != pos) &&
278 (0 !=
279 memcmp (&pos->pkey, member_id,
280 sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded))))
281 {
282 prev = pos;
283 pos = pos->next;
284 }
285 if (NULL == pos)
286 {
287 GNUNET_break (0);
288 }
289 else
290 {
291 if (NULL == prev)
292 users = pos->next;
293 else
294 prev->next = pos->next;
295 GNUNET_free (pos);
296 }
297 }
298 return GNUNET_OK;
299}
300
301
302static int
303do_join (const char *arg, const void *xtra)
304{
305 char *my_name;
306 int my_name_is_a_dup;
307 struct GNUNET_HashCode me;
308
309 if (arg[0] == '#')
310 arg++; /* ignore first hash */
311 GNUNET_CHAT_leave_room (room);
312 free_user_list ();
313 GNUNET_free (room_name);
314 room_name = GNUNET_strdup (arg);
315 room =
316 GNUNET_CHAT_join_room (cfg, nickname, meta, room_name, -1, &join_cb, NULL,
317 &receive_cb, NULL, &member_list_cb, NULL,
318 &confirmation_cb, NULL, &me);
319 if (NULL == room)
320 {
321 FPRINTF (stdout, "%s", _("Could not change username\n"));
322 return GNUNET_SYSERR;
323 }
324 if ((GNUNET_OK != GNUNET_FS_pseudonym_get_info (cfg,
325 &me, NULL, NULL, &my_name, &my_name_is_a_dup)) ||
326 (my_name_is_a_dup == GNUNET_YES))
327 {
328 GNUNET_free (my_name);
329 my_name = GNUNET_strdup (_("anonymous"));
330 }
331 /* Don't uniquify our own name - other people will have a different
332 * suffix for our own name anyway.
333 */
334 FPRINTF (stdout, _("Joining room `%s' as user `%s'...\n"), room_name,
335 my_name);
336 GNUNET_free (my_name);
337 return GNUNET_OK;
338}
339
340
341static int
342do_nick (const char *msg, const void *xtra)
343{
344 char *my_name;
345 int my_name_is_a_dup;
346 struct GNUNET_HashCode me;
347
348 GNUNET_CHAT_leave_room (room);
349 free_user_list ();
350 GNUNET_free (nickname);
351 GNUNET_CONTAINER_meta_data_destroy (meta);
352 nickname = GNUNET_strdup (msg);
353 meta = GNUNET_CONTAINER_meta_data_create ();
354 GNUNET_CONTAINER_meta_data_insert (meta, "<gnunet>", EXTRACTOR_METATYPE_TITLE,
355 EXTRACTOR_METAFORMAT_UTF8, "text/plain",
356 nickname, strlen (nickname) + 1);
357 room =
358 GNUNET_CHAT_join_room (cfg, nickname, meta, room_name, -1, &join_cb, NULL,
359 &receive_cb, NULL, &member_list_cb, NULL,
360 &confirmation_cb, NULL, &me);
361 if (NULL == room)
362 {
363 FPRINTF (stdout, "%s", _("Could not change username\n"));
364 return GNUNET_SYSERR;
365 }
366 if ((GNUNET_OK != GNUNET_FS_pseudonym_get_info (cfg,
367 &me, NULL, NULL, &my_name, &my_name_is_a_dup)) ||
368 (my_name_is_a_dup == GNUNET_YES))
369 {
370 GNUNET_free (my_name);
371 my_name = GNUNET_strdup (_("anonymous"));
372 }
373 FPRINTF (stdout, _("Changed username to `%s'\n"), my_name);
374 GNUNET_free (my_name);
375 return GNUNET_OK;
376}
377
378
379static int
380do_names (const char *msg, const void *xtra)
381{
382 char *name;
383 char *unique_name;
384 int name_is_a_dup;
385 struct UserList *pos;
386 struct GNUNET_HashCode pid;
387
388 FPRINTF (stdout, _("Users in room `%s': "), room_name);
389 pos = users;
390 while (NULL != pos)
391 {
392 GNUNET_CRYPTO_hash (&pos->pkey,
393 sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
394 &pid);
395 if (GNUNET_OK != GNUNET_FS_pseudonym_get_info (cfg,
396 &pid, NULL, NULL, &name, &name_is_a_dup)
397 || (name_is_a_dup == GNUNET_YES))
398 {
399 GNUNET_free (name);
400 name = GNUNET_strdup (_("anonymous"));
401 }
402 unique_name = GNUNET_FS_pseudonym_name_uniquify (cfg, &pid, name, NULL);
403 GNUNET_free (name);
404 FPRINTF (stdout, "`%s' ", unique_name);
405 GNUNET_free (unique_name);
406 pos = pos->next;
407 }
408 FPRINTF (stdout, "%s", "\n");
409 return GNUNET_OK;
410}
411
412
413static int
414do_send (const char *msg, const void *xtra)
415{
416 uint32_t seq;
417
418 GNUNET_CHAT_send_message (room, msg, GNUNET_CHAT_MSG_OPTION_NONE, NULL, &seq);
419 return GNUNET_OK;
420}
421
422
423static int
424do_send_pm (const char *msg, const void *xtra)
425{
426 char *user;
427 struct GNUNET_HashCode uid;
428 struct GNUNET_HashCode pid;
429 uint32_t seq;
430 struct UserList *pos;
431
432 if (NULL == strstr (msg, " "))
433 {
434 FPRINTF (stderr, "%s", _("Syntax: /msg USERNAME MESSAGE"));
435 return GNUNET_OK;
436 }
437 user = GNUNET_strdup (msg);
438 strstr (user, " ")[0] = '\0';
439 msg += strlen (user) + 1;
440 if (GNUNET_OK != GNUNET_FS_pseudonym_name_to_id (cfg, user, &uid))
441 {
442 FPRINTF (stderr,
443 _("Unknown user `%s'. Make sure you specify its numeric suffix, if any.\n"),
444 user);
445 GNUNET_free (user);
446 return GNUNET_OK;
447 }
448 pos = users;
449 while (NULL != pos)
450 {
451 GNUNET_CRYPTO_hash (&pos->pkey,
452 sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
453 &pid);
454 if (0 == memcmp (&pid, &uid, sizeof (struct GNUNET_HashCode)))
455 break;
456 pos = pos->next;
457 }
458 if (NULL == pos)
459 {
460 FPRINTF (stderr, _("User `%s' is currently not in the room!\n"), user);
461 GNUNET_free (user);
462 return GNUNET_OK;
463 }
464 GNUNET_CHAT_send_message (room, msg, GNUNET_CHAT_MSG_PRIVATE, &pos->pkey,
465 &seq);
466 GNUNET_free (user);
467 return GNUNET_OK;
468}
469
470
471static int
472do_send_sig (const char *msg, const void *xtra)
473{
474 uint32_t seq;
475
476 GNUNET_CHAT_send_message (room, msg, GNUNET_CHAT_MSG_AUTHENTICATED, NULL,
477 &seq);
478 return GNUNET_OK;
479}
480
481
482static int
483do_send_ack (const char *msg, const void *xtra)
484{
485 uint32_t seq;
486
487 GNUNET_CHAT_send_message (room, msg, GNUNET_CHAT_MSG_ACKNOWLEDGED, NULL,
488 &seq);
489 return GNUNET_OK;
490}
491
492
493static int
494do_send_anonymous (const char *msg, const void *xtra)
495{
496 uint32_t seq;
497
498 GNUNET_CHAT_send_message (room, msg, GNUNET_CHAT_MSG_ANONYMOUS, NULL, &seq);
499 return GNUNET_OK;
500}
501
502
503static int
504do_quit (const char *args, const void *xtra)
505{
506 return GNUNET_SYSERR;
507}
508
509
510static int
511do_unknown (const char *msg, const void *xtra)
512{
513 FPRINTF (stderr, _("Unknown command `%s'\n"), msg);
514 return GNUNET_OK;
515}
516
517
518/**
519 * List of supported IRC commands. The order matters!
520 */
521static struct ChatCommand commands[] = {
522 {"/join ", &do_join,
523 gettext_noop
524 ("Use `/join #roomname' to join a chat room. Joining a room will cause you"
525 " to leave the current room")},
526 {"/nick ", &do_nick,
527 gettext_noop
528 ("Use `/nick nickname' to change your nickname. This will cause you to"
529 " leave the current room and immediately rejoin it with the new name.")},
530 {"/msg ", &do_send_pm,
531 gettext_noop
532 ("Use `/msg nickname message' to send a private message to the specified"
533 " user")},
534 {"/notice ", &do_send_pm,
535 gettext_noop ("The `/notice' command is an alias for `/msg'")},
536 {"/query ", &do_send_pm,
537 gettext_noop ("The `/query' command is an alias for `/msg'")},
538 {"/sig ", &do_send_sig,
539 gettext_noop ("Use `/sig message' to send a signed public message")},
540 {"/ack ", &do_send_ack,
541 gettext_noop
542 ("Use `/ack message' to require signed acknowledgment of the message")},
543 {"/anonymous ", &do_send_anonymous,
544 gettext_noop
545 ("Use `/anonymous message' to send a public anonymous message")},
546 {"/anon ", &do_send_anonymous,
547 gettext_noop ("The `/anon' command is an alias for `/anonymous'")},
548 {"/quit", &do_quit,
549 gettext_noop ("Use `/quit' to terminate gnunet-chat")},
550 {"/leave", &do_quit,
551 gettext_noop ("The `/leave' command is an alias for `/quit'")},
552 {"/names", &do_names,
553 gettext_noop
554 ("Use `/names' to list all of the current members in the chat room")},
555 {"/help", &do_help,
556 gettext_noop ("Use `/help command' to get help for a specific command")},
557 /* Add standard commands:
558 * /whois (print metadata),
559 * /ignore (set flag, check on receive!) */
560 /* the following three commands must be last! */
561 {"/", &do_unknown, NULL},
562 {"", &do_send, NULL},
563 {NULL, NULL, NULL},
564};
565
566
567static int
568do_help (const char *args, const void *xtra)
569{
570 int i;
571
572 i = 0;
573 while ((NULL != args) && (0 != strlen (args)) &&
574 (commands[i].Action != &do_help))
575 {
576 if (0 == strncasecmp (&args[1], &commands[i].command[1], strlen (args) - 1))
577 {
578 FPRINTF (stdout, "%s\n", gettext (commands[i].helptext));
579 return GNUNET_OK;
580 }
581 i++;
582 }
583 i = 0;
584 FPRINTF (stdout, "%s", "Available commands:");
585 while (commands[i].Action != &do_help)
586 {
587 FPRINTF (stdout, " %s", gettext (commands[i].command));
588 i++;
589 }
590 FPRINTF (stdout, "%s", "\n");
591 FPRINTF (stdout, "%s\n", gettext (commands[i].helptext));
592 return GNUNET_OK;
593}
594
595
596static void
597do_stop_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
598{
599 GNUNET_CHAT_leave_room (room);
600 if (handle_cmd_task != GNUNET_SCHEDULER_NO_TASK)
601 {
602 GNUNET_SCHEDULER_cancel (handle_cmd_task);
603 handle_cmd_task = GNUNET_SCHEDULER_NO_TASK;
604 }
605 free_user_list ();
606 GNUNET_CONTAINER_meta_data_destroy (meta);
607 GNUNET_free (room_name);
608 GNUNET_free (nickname);
609}
610
611
612void
613handle_command (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
614{
615 char message[MAX_MESSAGE_LENGTH + 1];
616 int i;
617
618 /* read message from command line and handle it */
619 memset (message, 0, MAX_MESSAGE_LENGTH + 1);
620 if (NULL == fgets (message, MAX_MESSAGE_LENGTH, stdin))
621 goto next;
622 if (strlen (message) == 0)
623 goto next;
624 if (message[strlen (message) - 1] == '\n')
625 message[strlen (message) - 1] = '\0';
626 if (strlen (message) == 0)
627 goto next;
628 i = 0;
629 while ((NULL != commands[i].command) &&
630 (0 !=
631 strncasecmp (commands[i].command, message,
632 strlen (commands[i].command))))
633 i++;
634 if (GNUNET_OK !=
635 commands[i].Action (&message[strlen (commands[i].command)], NULL))
636 goto out;
637
638next:
639 handle_cmd_task =
640 GNUNET_SCHEDULER_add_delayed_with_priority (GNUNET_TIME_relative_multiply
641 (GNUNET_TIME_UNIT_MILLISECONDS, 100),
642 GNUNET_SCHEDULER_PRIORITY_UI,
643 &handle_command, NULL);
644 return;
645
646out:
647 handle_cmd_task = GNUNET_SCHEDULER_NO_TASK;
648 GNUNET_SCHEDULER_shutdown ();
649}
650
651
652/**
653 * Main function that will be run by the scheduler.
654 *
655 * @param cls closure, NULL
656 * @param args remaining command-line arguments
657 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
658 * @param c configuration
659 */
660static void
661run (void *cls, char *const *args, const char *cfgfile,
662 const struct GNUNET_CONFIGURATION_Handle *c)
663{
664 struct GNUNET_HashCode me;
665 char *my_name;
666 int my_name_is_a_dup;
667
668 cfg = c;
669 /* check arguments */
670 if (NULL == nickname)
671 {
672 FPRINTF (stderr, "%s", _("You must specify a nickname\n"));
673 ret = -1;
674 return;
675 }
676 if (NULL == room_name)
677 room_name = GNUNET_strdup ("gnunet");
678 meta = GNUNET_CONTAINER_meta_data_create ();
679 GNUNET_CONTAINER_meta_data_insert (meta, "<gnunet>", EXTRACTOR_METATYPE_TITLE,
680 EXTRACTOR_METAFORMAT_UTF8, "text/plain",
681 nickname, strlen (nickname) + 1);
682 room =
683 GNUNET_CHAT_join_room (cfg, nickname, meta, room_name, -1, &join_cb, NULL,
684 &receive_cb, NULL, &member_list_cb, NULL,
685 &confirmation_cb, NULL, &me);
686 if (NULL == room)
687 {
688 FPRINTF (stderr, _("Failed to join room `%s'\n"), room_name);
689 GNUNET_free (room_name);
690 GNUNET_free (nickname);
691 GNUNET_CONTAINER_meta_data_destroy (meta);
692 ret = -1;
693 return;
694 }
695 if ((GNUNET_OK != GNUNET_FS_pseudonym_get_info (cfg,
696 &me, NULL, NULL, &my_name, &my_name_is_a_dup)) ||
697 (my_name_is_a_dup == GNUNET_YES))
698 {
699 GNUNET_free (my_name);
700 my_name = GNUNET_strdup (_("anonymous"));
701 }
702 FPRINTF (stdout, _("Joining room `%s' as user `%s'...\n"), room_name,
703 my_name);
704 GNUNET_free (my_name);
705 handle_cmd_task =
706 GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_UI,
707 &handle_command, NULL);
708 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &do_stop_task,
709 NULL);
710}
711
712
713/**
714 * The main function to chat via GNUnet.
715 *
716 * @param argc number of arguments from the command line
717 * @param argv command line arguments
718 * @return 0 ok, 1 on error
719 */
720int
721main (int argc, char *const *argv)
722{
723 int flags;
724
725 static const struct GNUNET_GETOPT_CommandLineOption options[] = {
726 {'n', "nick", "NAME",
727 gettext_noop ("set the nickname to use (required)"),
728 1, &GNUNET_GETOPT_set_string, &nickname},
729 {'r', "room", "NAME",
730 gettext_noop ("set the chat room to join"),
731 1, &GNUNET_GETOPT_set_string, &room_name},
732 GNUNET_GETOPT_OPTION_END
733 };
734
735#ifndef WINDOWS
736 flags = fcntl (0, F_GETFL, 0);
737 flags |= O_NONBLOCK;
738 fcntl (0, F_SETFL, flags);
739#endif
740
741 if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
742 return 2;
743
744 return (GNUNET_OK ==
745 GNUNET_PROGRAM_run (argc, argv, "gnunet-chat",
746 gettext_noop ("Join a chat on GNUnet."), options,
747 &run, NULL)) ? ret : 1;
748}
749
750/* end of gnunet-chat.c */
diff --git a/src/chat/gnunet-service-chat.c b/src/chat/gnunet-service-chat.c
deleted file mode 100644
index 9c3fcfb39..000000000
--- a/src/chat/gnunet-service-chat.c
+++ /dev/null
@@ -1,1713 +0,0 @@
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-chat.c
23 * @brief service providing chat functionality
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_EXTRA_LOGGING
37#define MAX_TRANSMIT_DELAY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 60)
38#define EXPECTED_NEIGHBOUR_COUNT 16
39#define MAX_ANONYMOUS_MSG_LIST_LENGTH 16
40
41
42/**
43 * Linked list of our current clients.
44 */
45struct ChatClient
46{
47 struct ChatClient *next;
48
49 /**
50 * Handle for a chat client (NULL for external clients).
51 */
52 struct GNUNET_SERVER_Client *client;
53
54 /**
55 * Public key of the client.
56 */
57 struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded public_key;
58
59 /**
60 * Name of the room which the client is in.
61 */
62 char *room;
63
64 /**
65 * Serialized metadata of the client.
66 */
67 char *member_info;
68
69 /**
70 * Hash of the public key (for convenience).
71 */
72 struct GNUNET_HashCode id;
73
74 /**
75 * Options which the client is willing to receive.
76 */
77 uint32_t msg_options;
78
79 /**
80 * Length of serialized metadata in member_info.
81 */
82 uint16_t meta_len;
83
84 /**
85 * Sequence number of the last message sent by the client.
86 */
87 uint32_t msg_sequence_number;
88
89 /**
90 * Sequence number of the last receipt sent by the client.
91 * Used to discard already processed receipts.
92 */
93 uint32_t rcpt_sequence_number;
94
95};
96
97/**
98 * Information about a peer that we are connected to.
99 * We track data that is useful for determining which
100 * peers should receive our requests.
101 */
102struct ConnectedPeer
103{
104 /**
105 * The peer's identity.
106 */
107 GNUNET_PEER_Id pid;
108};
109
110/**
111 * Linked list of recent anonymous messages.
112 */
113struct AnonymousMessage
114{
115 struct AnonymousMessage *next;
116
117 /**
118 * Hash of the message.
119 */
120 struct GNUNET_HashCode hash;
121
122};
123
124
125/**
126 * Handle to the core service (NULL until we've connected to it).
127 */
128static struct GNUNET_CORE_Handle *core;
129
130/**
131 * Our configuration.
132 */
133static const struct GNUNET_CONFIGURATION_Handle *cfg;
134
135/**
136 * The identity of this host.
137 */
138static struct GNUNET_PeerIdentity me;
139
140/**
141 * Head of the list of current clients.
142 */
143static struct ChatClient *client_list_head = NULL;
144
145/**
146 * Notification context containing all connected clients.
147 */
148struct GNUNET_SERVER_NotificationContext *nc = NULL;
149
150/**
151 * Head of the list of recent anonymous messages.
152 */
153static struct AnonymousMessage *anonymous_list_head = NULL;
154
155/**
156 * Map of peer identifiers to "struct ConnectedPeer" (for that peer).
157 */
158static struct GNUNET_CONTAINER_MultiHashMap *connected_peers;
159
160
161static void
162remember_anonymous_message (const struct P2PReceiveNotificationMessage
163 *p2p_rnmsg)
164{
165 static struct GNUNET_HashCode hash;
166 struct AnonymousMessage *anon_msg;
167 struct AnonymousMessage *prev;
168 int anon_list_len;
169
170 GNUNET_CRYPTO_hash (p2p_rnmsg, ntohs (p2p_rnmsg->header.size), &hash);
171 anon_msg = GNUNET_malloc (sizeof (struct AnonymousMessage));
172 anon_msg->hash = hash;
173 anon_msg->next = anonymous_list_head;
174 anonymous_list_head = anon_msg;
175 anon_list_len = 1;
176 prev = NULL;
177 while ((NULL != anon_msg->next))
178 {
179 prev = anon_msg;
180 anon_msg = anon_msg->next;
181 anon_list_len++;
182 }
183 if (anon_list_len == MAX_ANONYMOUS_MSG_LIST_LENGTH)
184 {
185 GNUNET_free (anon_msg);
186 if (NULL != prev)
187 prev->next = NULL;
188 }
189}
190
191
192static int
193lookup_anonymous_message (const struct P2PReceiveNotificationMessage *p2p_rnmsg)
194{
195 static struct GNUNET_HashCode hash;
196 struct AnonymousMessage *anon_msg;
197
198 GNUNET_CRYPTO_hash (p2p_rnmsg, ntohs (p2p_rnmsg->header.size), &hash);
199 anon_msg = anonymous_list_head;
200 while ((NULL != anon_msg) &&
201 (0 != memcmp (&anon_msg->hash, &hash, sizeof (struct GNUNET_HashCode))))
202 anon_msg = anon_msg->next;
203 return (NULL != anon_msg);
204}
205
206
207/**
208 * Transmit a message notification to the peer.
209 *
210 * @param cls closure, pointer to the 'struct P2PReceiveNotificationMessage'
211 * @param size number of bytes available in buf
212 * @param buf where the callee should write the message
213 * @return number of bytes written to buf
214 */
215static size_t
216transmit_message_notification_to_peer (void *cls, size_t size, void *buf)
217{
218 struct P2PReceiveNotificationMessage *my_msg = cls;
219 struct P2PReceiveNotificationMessage *m = buf;
220 size_t msg_size;
221
222#if DEBUG_CHAT_SERVICE
223 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
224 "Transmitting P2P message notification\n");
225#endif
226 if (buf == NULL)
227 {
228 /* client disconnected */
229#if DEBUG_CHAT_SERVICE
230 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
231 "Buffer is NULL, dropping the message\n");
232#endif
233 return 0;
234 }
235 msg_size = ntohs (my_msg->header.size);
236 GNUNET_assert (size >= msg_size);
237 memcpy (m, my_msg, msg_size);
238 GNUNET_free (my_msg);
239 return msg_size;
240}
241
242
243/**
244 * Ask to send a message notification to the peer.
245 */
246static int
247send_message_noficiation (void *cls, const struct GNUNET_HashCode * key, void *value)
248{
249 struct P2PReceiveNotificationMessage *msg = cls;
250 struct ConnectedPeer *cp = value;
251 struct GNUNET_PeerIdentity pid;
252 struct P2PReceiveNotificationMessage *my_msg;
253
254 GNUNET_PEER_resolve (cp->pid, &pid);
255#if DEBUG_CHAT_SERVICE
256 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending message notification to `%s'\n",
257 GNUNET_i2s (&pid));
258#endif
259 my_msg = GNUNET_memdup (msg, ntohs (msg->header.size));
260 if (NULL ==
261 GNUNET_CORE_notify_transmit_ready (core, GNUNET_NO, 1, MAX_TRANSMIT_DELAY,
262 &pid, ntohs (msg->header.size),
263 &transmit_message_notification_to_peer,
264 my_msg))
265 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
266 _("Failed to queue a message notification\n"));
267 return GNUNET_YES;
268}
269
270
271/**
272 * A client sent a chat message. Encrypt the message text if the message is
273 * private. Send the message to local room members and to all connected peers.
274 *
275 * @param cls closure, NULL
276 * @param client identification of the client
277 * @param message the actual message
278 */
279static void
280handle_transmit_request (void *cls, struct GNUNET_SERVER_Client *client,
281 const struct GNUNET_MessageHeader *message)
282{
283 static struct GNUNET_HashCode all_zeros;
284 const struct TransmitRequestMessage *trmsg;
285 struct ReceiveNotificationMessage *rnmsg;
286 struct P2PReceiveNotificationMessage *p2p_rnmsg;
287 struct ChatClient *pos;
288 struct ChatClient *target;
289 struct GNUNET_CRYPTO_AesSessionKey key;
290 char encrypted_msg[MAX_MESSAGE_LENGTH];
291 const char *room;
292 size_t room_len;
293 int msg_len;
294 int is_priv;
295 int is_anon;
296
297 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Client sent a chat message\n");
298 if (ntohs (message->size) <= sizeof (struct TransmitRequestMessage))
299 {
300 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Malformed message: wrong size\n");
301 GNUNET_break (0);
302 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
303 return;
304 }
305 trmsg = (const struct TransmitRequestMessage *) message;
306 msg_len = ntohs (trmsg->header.size) - sizeof (struct TransmitRequestMessage);
307 is_priv = (0 != (ntohl (trmsg->msg_options) & GNUNET_CHAT_MSG_PRIVATE));
308 if (is_priv)
309 {
310#if DEBUG_CHAT_SERVICE
311 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Encrypting the message text\n");
312#endif
313 GNUNET_CRYPTO_aes_create_session_key (&key);
314 msg_len =
315 GNUNET_CRYPTO_aes_encrypt (&trmsg[1], msg_len, &key,
316 (const struct
317 GNUNET_CRYPTO_AesInitializationVector *)
318 INITVALUE, encrypted_msg);
319 if (-1 == msg_len)
320 {
321 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
322 "Could not encrypt the message text\n");
323 GNUNET_break (0);
324 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
325 return;
326 }
327 }
328 rnmsg = GNUNET_malloc (sizeof (struct ReceiveNotificationMessage) + msg_len);
329 rnmsg->header.size =
330 htons (sizeof (struct ReceiveNotificationMessage) + msg_len);
331 rnmsg->header.type = htons (GNUNET_MESSAGE_TYPE_CHAT_MESSAGE_NOTIFICATION);
332 rnmsg->msg_options = trmsg->msg_options;
333 rnmsg->timestamp = trmsg->timestamp;
334 pos = client_list_head;
335 while ((NULL != pos) && (pos->client != client))
336 pos = pos->next;
337 if (NULL == pos)
338 {
339 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
340 "The client is not a member of a chat room. Client has to "
341 "join a chat room first\n");
342 GNUNET_break (0);
343 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
344 GNUNET_free (rnmsg);
345 return;
346 }
347 room = pos->room;
348 pos->msg_sequence_number = ntohl (trmsg->sequence_number);
349 is_anon = (0 != (ntohl (trmsg->msg_options) & GNUNET_CHAT_MSG_ANONYMOUS));
350 if (is_anon)
351 {
352 memset (&rnmsg->sender, 0, sizeof (struct GNUNET_HashCode));
353 rnmsg->sequence_number = 0;
354 }
355 else
356 {
357 rnmsg->sender = pos->id;
358 rnmsg->sequence_number = trmsg->sequence_number;
359 }
360 if (is_priv)
361 {
362#if DEBUG_CHAT_SERVICE
363 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
364 "Encrypting the session key using the public key of '%s'\n",
365 GNUNET_h2s (&trmsg->target));
366#endif
367 if (0 == memcmp (&all_zeros, &trmsg->target, sizeof (struct GNUNET_HashCode)))
368 {
369 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
370 "Malformed message: private, but no target\n");
371 GNUNET_break (0);
372 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
373 GNUNET_free (rnmsg);
374 return;
375 }
376 memcpy (&rnmsg[1], encrypted_msg, msg_len);
377 target = client_list_head;
378 while ((NULL != target) &&
379 (0 !=
380 memcmp (&target->id, &trmsg->target, sizeof (struct GNUNET_HashCode))))
381 target = target->next;
382 if (NULL == target)
383 {
384 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
385 "Unknown target of the private message\n");
386 GNUNET_break (0);
387 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
388 GNUNET_free (rnmsg);
389 return;
390 }
391 if (GNUNET_SYSERR ==
392 GNUNET_CRYPTO_rsa_encrypt (&key,
393 sizeof (struct GNUNET_CRYPTO_AesSessionKey),
394 &target->public_key, &rnmsg->encrypted_key))
395 {
396 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
397 "Could not encrypt the session key\n");
398 GNUNET_break (0);
399 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
400 GNUNET_free (rnmsg);
401 return;
402 }
403 }
404 else
405 {
406 memcpy (&rnmsg[1], &trmsg[1], msg_len);
407 }
408 pos = client_list_head;
409#if DEBUG_CHAT_SERVICE
410 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
411 "Sending message to local room members\n");
412#endif
413 while (NULL != pos)
414 {
415 if ((0 == strcmp (room, pos->room)) && (NULL != pos->client) &&
416 (pos->client != client))
417 {
418 if (((!is_priv) ||
419 (0 == memcmp (&trmsg->target, &pos->id, sizeof (struct GNUNET_HashCode))))
420 && (0 == (ntohl (trmsg->msg_options) & (~pos->msg_options))))
421 {
422 GNUNET_SERVER_notification_context_unicast (nc, pos->client,
423 &rnmsg->header, GNUNET_NO);
424 }
425 }
426 pos = pos->next;
427 }
428#if DEBUG_CHAT_SERVICE
429 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
430 "Broadcasting message to neighbour peers\n");
431#endif
432 if (is_anon)
433 {
434 room_len = strlen (room);
435 p2p_rnmsg =
436 GNUNET_malloc (sizeof (struct P2PReceiveNotificationMessage) + msg_len +
437 room_len);
438 p2p_rnmsg->header.size =
439 htons (sizeof (struct P2PReceiveNotificationMessage) + msg_len +
440 room_len);
441 p2p_rnmsg->room_name_len = htons (room_len);
442 memcpy ((char *) &p2p_rnmsg[1], room, room_len);
443 memcpy ((char *) &p2p_rnmsg[1] + room_len, &trmsg[1], msg_len);
444 }
445 else
446 {
447 p2p_rnmsg =
448 GNUNET_malloc (sizeof (struct P2PReceiveNotificationMessage) + msg_len);
449 p2p_rnmsg->header.size =
450 htons (sizeof (struct P2PReceiveNotificationMessage) + msg_len);
451 if (is_priv)
452 {
453 memcpy (&p2p_rnmsg[1], encrypted_msg, msg_len);
454 memcpy (&p2p_rnmsg->encrypted_key, &rnmsg->encrypted_key,
455 sizeof (struct GNUNET_CRYPTO_RsaEncryptedData));
456 }
457 else
458 memcpy (&p2p_rnmsg[1], &trmsg[1], msg_len);
459 }
460 p2p_rnmsg->header.type =
461 htons (GNUNET_MESSAGE_TYPE_CHAT_P2P_MESSAGE_NOTIFICATION);
462 p2p_rnmsg->msg_options = trmsg->msg_options;
463 p2p_rnmsg->sequence_number = trmsg->sequence_number;
464 p2p_rnmsg->timestamp = trmsg->timestamp;
465 p2p_rnmsg->reserved = htons (0);
466 p2p_rnmsg->sender = rnmsg->sender;
467 p2p_rnmsg->target = trmsg->target;
468 if (is_anon)
469 remember_anonymous_message (p2p_rnmsg);
470 GNUNET_CONTAINER_multihashmap_iterate (connected_peers,
471 &send_message_noficiation, p2p_rnmsg);
472 GNUNET_free (p2p_rnmsg);
473 GNUNET_SERVER_receive_done (client, GNUNET_OK);
474 GNUNET_free (rnmsg);
475}
476
477
478/**
479 * Transmit a join notification to the peer.
480 *
481 * @param cls closure, pointer to the 'struct ChatClient'
482 * @param size number of bytes available in buf
483 * @param buf where the callee should write the message
484 * @return number of bytes written to buf
485 */
486static size_t
487transmit_join_notification_to_peer (void *cls, size_t size, void *buf)
488{
489 struct ChatClient *entry = cls;
490 struct P2PJoinNotificationMessage *m = buf;
491 size_t room_len;
492 size_t meta_len;
493 size_t msg_size;
494 char *roomptr;
495
496#if DEBUG_CHAT_SERVICE
497 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Transmitting P2P join notification\n");
498#endif
499 room_len = strlen (entry->room);
500 meta_len = entry->meta_len;
501 msg_size = sizeof (struct P2PJoinNotificationMessage) + meta_len + room_len;
502 GNUNET_assert (size >= msg_size);
503 GNUNET_assert (NULL != buf);
504 m = buf;
505 m->header.type = htons (GNUNET_MESSAGE_TYPE_CHAT_P2P_JOIN_NOTIFICATION);
506 m->header.size = htons (msg_size);
507 m->msg_options = htonl (entry->msg_options);
508 m->room_name_len = htons (room_len);
509 m->reserved = htons (0);
510 m->reserved2 = htonl (0);
511 m->public_key = entry->public_key;
512 roomptr = (char *) &m[1];
513 memcpy (roomptr, entry->room, room_len);
514 if (meta_len > 0)
515 memcpy (&roomptr[room_len], entry->member_info, meta_len);
516 return msg_size;
517}
518
519
520/**
521 * Ask to send a join notification to the peer.
522 */
523static int
524send_join_noficiation (void *cls, const struct GNUNET_HashCode * key, void *value)
525{
526 struct ChatClient *entry = cls;
527 struct ConnectedPeer *cp = value;
528 struct GNUNET_PeerIdentity pid;
529 size_t msg_size;
530
531 GNUNET_PEER_resolve (cp->pid, &pid);
532#if DEBUG_CHAT_SERVICE
533 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending join notification to `%s'\n",
534 GNUNET_i2s (&pid));
535#endif
536 msg_size =
537 sizeof (struct P2PJoinNotificationMessage) + strlen (entry->room) +
538 entry->meta_len;
539 if (NULL ==
540 GNUNET_CORE_notify_transmit_ready (core, GNUNET_NO, 1, MAX_TRANSMIT_DELAY,
541 &pid, msg_size,
542 &transmit_join_notification_to_peer,
543 entry))
544 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
545 _("Failed to queue a join notification\n"));
546 return GNUNET_YES;
547}
548
549
550/**
551 * A client asked for entering a chat room. Add the new member to the list of
552 * clients and notify remaining room members.
553 *
554 * @param cls closure, NULL
555 * @param client identification of the client
556 * @param message the actual message
557 */
558static void
559handle_join_request (void *cls, struct GNUNET_SERVER_Client *client,
560 const struct GNUNET_MessageHeader *message)
561{
562 const struct JoinRequestMessage *jrmsg;
563 char *room_name;
564 const char *roomptr;
565 uint16_t header_size;
566 uint16_t meta_len;
567 uint16_t room_name_len;
568 struct ChatClient *new_entry;
569 struct ChatClient *entry;
570 struct JoinNotificationMessage *jnmsg;
571 struct JoinNotificationMessage *entry_jnmsg;
572
573 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Client sent a join request\n");
574 if (ntohs (message->size) <= sizeof (struct JoinRequestMessage))
575 {
576 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Malformed message: wrong size\n");
577 GNUNET_break (0);
578 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
579 return;
580 }
581 jrmsg = (const struct JoinRequestMessage *) message;
582 header_size = ntohs (jrmsg->header.size);
583 room_name_len = ntohs (jrmsg->room_name_len);
584 if (header_size - sizeof (struct JoinRequestMessage) <= room_name_len)
585 {
586 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
587 "Malformed message: wrong length of the room name\n");
588 GNUNET_break (0);
589 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
590 return;
591 }
592 meta_len = header_size - sizeof (struct JoinRequestMessage) - room_name_len;
593 roomptr = (const char *) &jrmsg[1];
594 room_name = GNUNET_malloc (room_name_len + 1);
595 memcpy (room_name, roomptr, room_name_len);
596 room_name[room_name_len] = '\0';
597 new_entry = GNUNET_malloc (sizeof (struct ChatClient));
598 memset (new_entry, 0, sizeof (struct ChatClient));
599 new_entry->client = client;
600 new_entry->room = room_name;
601 new_entry->public_key = jrmsg->public_key;
602 new_entry->meta_len = meta_len;
603 if (meta_len > 0)
604 {
605 new_entry->member_info = GNUNET_malloc (meta_len);
606 memcpy (new_entry->member_info, &roomptr[room_name_len], meta_len);
607 }
608 else
609 new_entry->member_info = NULL;
610 GNUNET_CRYPTO_hash (&new_entry->public_key,
611 sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
612 &new_entry->id);
613 new_entry->msg_options = ntohl (jrmsg->msg_options);
614 new_entry->next = client_list_head;
615 client_list_head = new_entry;
616#if DEBUG_CHAT_SERVICE
617 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
618 "Synchronizing room members between local clients\n");
619#endif
620 jnmsg = GNUNET_malloc (sizeof (struct JoinNotificationMessage) + meta_len);
621 jnmsg->header.type = htons (GNUNET_MESSAGE_TYPE_CHAT_JOIN_NOTIFICATION);
622 jnmsg->header.size =
623 htons (sizeof (struct JoinNotificationMessage) + meta_len);
624 jnmsg->msg_options = jrmsg->msg_options;
625 jnmsg->public_key = new_entry->public_key;
626 memcpy (&jnmsg[1], &roomptr[room_name_len], meta_len);
627 GNUNET_SERVER_notification_context_add (nc, client);
628 entry = client_list_head;
629 while (NULL != entry)
630 {
631 if (0 == strcmp (room_name, entry->room))
632 {
633 if (NULL != entry->client)
634 GNUNET_SERVER_notification_context_unicast (nc, entry->client,
635 &jnmsg->header, GNUNET_NO);
636 if (entry->client != client)
637 {
638 entry_jnmsg =
639 GNUNET_malloc (sizeof (struct JoinNotificationMessage) +
640 entry->meta_len);
641 entry_jnmsg->header.type =
642 htons (GNUNET_MESSAGE_TYPE_CHAT_JOIN_NOTIFICATION);
643 entry_jnmsg->header.size =
644 htons (sizeof (struct JoinNotificationMessage) + entry->meta_len);
645 entry_jnmsg->msg_options = entry->msg_options;
646 entry_jnmsg->public_key = entry->public_key;
647 memcpy (&entry_jnmsg[1], entry->member_info, entry->meta_len);
648 GNUNET_SERVER_notification_context_unicast (nc, client,
649 &entry_jnmsg->header,
650 GNUNET_NO);
651 GNUNET_free (entry_jnmsg);
652 }
653 }
654 entry = entry->next;
655 }
656#if DEBUG_CHAT_SERVICE
657 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
658 "Broadcasting join notification to neighbour peers\n");
659#endif
660 GNUNET_CONTAINER_multihashmap_iterate (connected_peers,
661 &send_join_noficiation, new_entry);
662 GNUNET_SERVER_receive_done (client, GNUNET_OK);
663 GNUNET_free (jnmsg);
664}
665
666/**
667 * Transmit a confirmation receipt to the peer.
668 *
669 * @param cls closure, pointer to the 'struct P2PConfirmationReceiptMessage'
670 * @param size number of bytes available in buf
671 * @param buf where the callee should write the message
672 * @return number of bytes written to buf
673 */
674static size_t
675transmit_confirmation_receipt_to_peer (void *cls, size_t size, void *buf)
676{
677 struct P2PConfirmationReceiptMessage *receipt = cls;
678 size_t msg_size;
679
680#if DEBUG_CHAT_SERVICE
681 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
682 "Transmitting P2P confirmation receipt to '%s'\n",
683 GNUNET_h2s (&receipt->target));
684#endif
685 if (buf == NULL)
686 {
687 /* client disconnected */
688#if DEBUG_CHAT_SERVICE
689 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
690 "Buffer is NULL, dropping the message\n");
691#endif
692 return 0;
693 }
694 msg_size = sizeof (struct P2PConfirmationReceiptMessage);
695 GNUNET_assert (size >= msg_size);
696 memcpy (buf, receipt, msg_size);
697 GNUNET_free (receipt);
698 return msg_size;
699}
700
701
702/**
703 * Ask to send a confirmation receipt to the peer.
704 */
705static int
706send_confirmation_receipt (void *cls, const struct GNUNET_HashCode * key, void *value)
707{
708 struct P2PConfirmationReceiptMessage *receipt = cls;
709 struct ConnectedPeer *cp = value;
710 struct GNUNET_PeerIdentity pid;
711 struct P2PConfirmationReceiptMessage *my_receipt;
712 size_t msg_size;
713
714 GNUNET_PEER_resolve (cp->pid, &pid);
715#if DEBUG_CHAT_SERVICE
716 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending confirmation receipt to `%s'\n",
717 GNUNET_i2s (&pid));
718#endif
719 msg_size = sizeof (struct P2PConfirmationReceiptMessage);
720 my_receipt =
721 GNUNET_memdup (receipt, sizeof (struct P2PConfirmationReceiptMessage));
722 if (NULL ==
723 GNUNET_CORE_notify_transmit_ready (core, GNUNET_YES, 1,
724 MAX_TRANSMIT_DELAY, &pid, msg_size,
725 &transmit_confirmation_receipt_to_peer,
726 my_receipt))
727 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
728 _("Failed to queue a confirmation receipt\n"));
729 return GNUNET_YES;
730}
731
732
733/**
734 * A client sent a confirmation receipt. Broadcast the receipt to all connected
735 * peers if the author of the original message is a local client. Otherwise
736 * check the signature and notify the user if the signature is valid.
737 *
738 * @param cls closure, NULL
739 * @param client identification of the client
740 * @param message the actual message
741 */
742static void
743handle_acknowledge_request (void *cls, struct GNUNET_SERVER_Client *client,
744 const struct GNUNET_MessageHeader *message)
745{
746 const struct ConfirmationReceiptMessage *receipt;
747 struct ConfirmationReceiptMessage *crmsg;
748 struct P2PConfirmationReceiptMessage *p2p_crmsg;
749 struct ChatClient *target;
750 struct ChatClient *author;
751
752 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Client sent a confirmation receipt\n");
753 receipt = (const struct ConfirmationReceiptMessage *) message;
754 author = client_list_head;
755 while ((NULL != author) &&
756 (0 !=
757 memcmp (&receipt->author, &author->id, sizeof (struct GNUNET_HashCode))))
758 author = author->next;
759 if (NULL == author)
760 {
761 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
762 "Unknown author of the original message\n");
763 GNUNET_break (0);
764 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
765 return;
766 }
767 target = client_list_head;
768 while ((NULL != target) &&
769 (0 !=
770 memcmp (&receipt->target, &target->id, sizeof (struct GNUNET_HashCode))))
771 target = target->next;
772 if (NULL == target)
773 {
774 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
775 "Unknown target of the confirmation receipt\n");
776 GNUNET_break (0);
777 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
778 return;
779 }
780 if (NULL == author->client)
781 {
782 target->rcpt_sequence_number++;
783#if DEBUG_CHAT_SERVICE
784 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
785 "Broadcasting %s's receipt #%u to neighbour peers\n",
786 GNUNET_h2s (&target->id), target->rcpt_sequence_number);
787#endif
788 p2p_crmsg = GNUNET_malloc (sizeof (struct P2PConfirmationReceiptMessage));
789 p2p_crmsg->header.size =
790 htons (sizeof (struct P2PConfirmationReceiptMessage));
791 p2p_crmsg->header.type =
792 htons (GNUNET_MESSAGE_TYPE_CHAT_P2P_CONFIRMATION_RECEIPT);
793 p2p_crmsg->reserved = htonl (0);
794 p2p_crmsg->signature = receipt->signature;
795 p2p_crmsg->purpose = receipt->purpose;
796 p2p_crmsg->msg_sequence_number = receipt->sequence_number;
797 p2p_crmsg->timestamp = receipt->timestamp;
798 p2p_crmsg->target = receipt->target;
799 p2p_crmsg->author = receipt->author;
800 p2p_crmsg->content = receipt->content;
801 p2p_crmsg->sequence_number = htonl (target->rcpt_sequence_number);
802 GNUNET_CONTAINER_multihashmap_iterate (connected_peers,
803 &send_confirmation_receipt,
804 p2p_crmsg);
805 GNUNET_free (p2p_crmsg);
806 }
807 else
808 {
809#if DEBUG_CHAT_SERVICE
810 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
811 "Verifying signature of the receipt\n");
812#endif
813 if (GNUNET_OK !=
814 GNUNET_CRYPTO_rsa_verify (GNUNET_SIGNATURE_PURPOSE_CHAT_RECEIPT,
815 &receipt->purpose, &receipt->signature,
816 &target->public_key))
817 {
818 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
819 "Invalid signature of the receipt\n");
820 GNUNET_break (0);
821 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
822 return;
823 }
824#if DEBUG_CHAT_SERVICE
825 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
826 "Sending receipt to the client which sent the original message\n");
827#endif
828 crmsg = GNUNET_memdup (receipt, sizeof (struct ConfirmationReceiptMessage));
829 crmsg->header.type =
830 htons (GNUNET_MESSAGE_TYPE_CHAT_CONFIRMATION_NOTIFICATION);
831 GNUNET_SERVER_notification_context_unicast (nc, author->client,
832 &crmsg->header, GNUNET_NO);
833 GNUNET_free (crmsg);
834 }
835 GNUNET_SERVER_receive_done (client, GNUNET_OK);
836}
837
838
839/**
840 * Transmit a leave notification to the peer.
841 *
842 * @param cls closure, pointer to the
843 * 'struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded'
844 * @param size number of bytes available in buf
845 * @param buf where the callee should write the message
846 * @return number of bytes written to buf
847 */
848static size_t
849transmit_leave_notification_to_peer (void *cls, size_t size, void *buf)
850{
851 struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *public_key = cls;
852 struct P2PLeaveNotificationMessage *m = buf;
853 size_t msg_size;
854
855#if DEBUG_CHAT_SERVICE
856 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Transmitting P2P leave notification\n");
857#endif
858 if (buf == NULL)
859 {
860 /* client disconnected */
861#if DEBUG_CHAT_SERVICE
862 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
863 "Buffer is NULL, dropping the message\n");
864#endif
865 return 0;
866 }
867 msg_size = sizeof (struct P2PLeaveNotificationMessage);
868 GNUNET_assert (size >= msg_size);
869 m = buf;
870 m->header.type = htons (GNUNET_MESSAGE_TYPE_CHAT_P2P_LEAVE_NOTIFICATION);
871 m->header.size = htons (msg_size);
872 m->reserved = htonl (0);
873 m->user = *public_key;
874 GNUNET_free (public_key);
875 return msg_size;
876}
877
878
879/**
880 * Ask to send a leave notification to the peer.
881 */
882static int
883send_leave_noficiation (void *cls, const struct GNUNET_HashCode * key, void *value)
884{
885 struct ChatClient *entry = cls;
886 struct ConnectedPeer *cp = value;
887 struct GNUNET_PeerIdentity pid;
888 struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *public_key;
889 size_t msg_size;
890
891 GNUNET_PEER_resolve (cp->pid, &pid);
892#if DEBUG_CHAT_SERVICE
893 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending leave notification to `%s'\n",
894 GNUNET_i2s (&pid));
895#endif
896 msg_size = sizeof (struct P2PLeaveNotificationMessage);
897 public_key =
898 GNUNET_memdup (&entry->public_key,
899 sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded));
900 if (NULL ==
901 GNUNET_CORE_notify_transmit_ready (core, GNUNET_YES, 1,
902 MAX_TRANSMIT_DELAY, &pid, msg_size,
903 &transmit_leave_notification_to_peer,
904 public_key))
905 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
906 _("Failed to queue a leave notification\n"));
907 return GNUNET_YES;
908}
909
910
911/**
912 * A client disconnected. Remove all of its data structure entries and notify
913 * remaining room members.
914 *
915 * @param cls closure, NULL
916 * @param client identification of the client
917 */
918static void
919handle_client_disconnect (void *cls, struct GNUNET_SERVER_Client *client)
920{
921 struct ChatClient *entry;
922 struct ChatClient *pos;
923 struct ChatClient *prev;
924 struct LeaveNotificationMessage lnmsg;
925
926 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Client disconnected\n");
927 pos = client_list_head;
928 prev = NULL;
929 while ((NULL != pos) && (pos->client != client))
930 {
931 prev = pos;
932 pos = pos->next;
933 }
934 if (NULL == pos)
935 {
936#if DEBUG_CHAT_SERVICE
937 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
938 "No such client. There is nothing to do\n");
939#endif
940 return;
941 }
942 if (NULL == prev)
943 client_list_head = pos->next;
944 else
945 prev->next = pos->next;
946 entry = client_list_head;
947#if DEBUG_CHAT_SERVICE
948 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
949 "Notifying local room members that the client has disconnected\n");
950#endif
951 lnmsg.header.size = htons (sizeof (struct LeaveNotificationMessage));
952 lnmsg.header.type = htons (GNUNET_MESSAGE_TYPE_CHAT_LEAVE_NOTIFICATION);
953 lnmsg.reserved = htonl (0);
954 lnmsg.user = pos->public_key;
955 while (NULL != entry)
956 {
957 if ((0 == strcmp (pos->room, entry->room)) && (NULL != entry->client))
958 {
959 GNUNET_SERVER_notification_context_unicast (nc, entry->client,
960 &lnmsg.header, GNUNET_NO);
961 }
962 entry = entry->next;
963 }
964#if DEBUG_CHAT_SERVICE
965 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
966 "Broadcasting leave notification to neighbour peers\n");
967#endif
968 GNUNET_CONTAINER_multihashmap_iterate (connected_peers,
969 &send_leave_noficiation, pos);
970 GNUNET_free (pos->room);
971 GNUNET_free_non_null (pos->member_info);
972 GNUNET_free (pos);
973}
974
975
976/**
977 * Handle P2P join notification.
978 *
979 * @param cls closure, always NULL
980 * @param other the other peer involved
981 * @param message the actual message
982
983 * @return GNUNET_OK to keep the connection open,
984 * GNUNET_SYSERR to close it (signal serious error)
985 */
986static int
987handle_p2p_join_notification (void *cls,
988 const struct GNUNET_PeerIdentity *other,
989 const struct GNUNET_MessageHeader *message)
990{
991 const struct P2PJoinNotificationMessage *p2p_jnmsg;
992 char *room_name;
993 const char *roomptr;
994 uint16_t header_size;
995 uint16_t meta_len;
996 uint16_t room_name_len;
997 struct ChatClient *new_entry;
998 struct ChatClient *entry;
999 struct JoinNotificationMessage *jnmsg;
1000 struct GNUNET_HashCode id;
1001
1002 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Got P2P join notification\n");
1003 if (ntohs (message->size) <= sizeof (struct P2PJoinNotificationMessage))
1004 {
1005 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Malformed message: wrong size\n");
1006 GNUNET_break_op (0);
1007 return GNUNET_SYSERR;
1008 }
1009 p2p_jnmsg = (const struct P2PJoinNotificationMessage *) message;
1010 header_size = ntohs (p2p_jnmsg->header.size);
1011 room_name_len = ntohs (p2p_jnmsg->room_name_len);
1012 if (header_size - sizeof (struct P2PJoinNotificationMessage) <= room_name_len)
1013 {
1014 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1015 "Malformed message: wrong length of the room name\n");
1016 GNUNET_break_op (0);
1017 return GNUNET_SYSERR;
1018 }
1019 GNUNET_CRYPTO_hash (&p2p_jnmsg->public_key,
1020 sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
1021 &id);
1022 entry = client_list_head;
1023 while (NULL != entry)
1024 {
1025 if (0 == memcmp (&entry->id, &id, sizeof (struct GNUNET_HashCode)))
1026 {
1027#if DEBUG_CHAT_SERVICE
1028 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1029 "The client has already joined. There is nothing to do\n");
1030#endif
1031 return GNUNET_OK;
1032 }
1033 entry = entry->next;
1034 }
1035 meta_len =
1036 header_size - sizeof (struct P2PJoinNotificationMessage) - room_name_len;
1037 roomptr = (const char *) &p2p_jnmsg[1];
1038 room_name = GNUNET_malloc (room_name_len + 1);
1039 memcpy (room_name, roomptr, room_name_len);
1040 room_name[room_name_len] = '\0';
1041 new_entry = GNUNET_malloc (sizeof (struct ChatClient));
1042 memset (new_entry, 0, sizeof (struct ChatClient));
1043 new_entry->id = id;
1044 new_entry->client = NULL;
1045 new_entry->room = room_name;
1046 new_entry->public_key = p2p_jnmsg->public_key;
1047 new_entry->meta_len = meta_len;
1048 if (meta_len > 0)
1049 {
1050 new_entry->member_info = GNUNET_malloc (meta_len);
1051 memcpy (new_entry->member_info, &roomptr[room_name_len], meta_len);
1052 }
1053 else
1054 new_entry->member_info = NULL;
1055 new_entry->msg_options = ntohl (p2p_jnmsg->msg_options);
1056 new_entry->next = client_list_head;
1057 client_list_head = new_entry;
1058#if DEBUG_CHAT_SERVICE
1059 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1060 "Notifying local room members that we have a new client\n");
1061#endif
1062 jnmsg = GNUNET_malloc (sizeof (struct JoinNotificationMessage) + meta_len);
1063 jnmsg->header.type = htons (GNUNET_MESSAGE_TYPE_CHAT_JOIN_NOTIFICATION);
1064 jnmsg->header.size =
1065 htons (sizeof (struct JoinNotificationMessage) + meta_len);
1066 jnmsg->msg_options = p2p_jnmsg->msg_options;
1067 jnmsg->public_key = new_entry->public_key;
1068 memcpy (&jnmsg[1], &roomptr[room_name_len], meta_len);
1069 entry = client_list_head;
1070 while (NULL != entry)
1071 {
1072 if ((0 == strcmp (room_name, entry->room)) && (NULL != entry->client))
1073 {
1074 GNUNET_SERVER_notification_context_unicast (nc, entry->client,
1075 &jnmsg->header, GNUNET_NO);
1076 }
1077 entry = entry->next;
1078 }
1079#if DEBUG_CHAT_SERVICE
1080 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1081 "Broadcasting join notification to neighbour peers\n");
1082#endif
1083 GNUNET_CONTAINER_multihashmap_iterate (connected_peers,
1084 &send_join_noficiation, new_entry);
1085 GNUNET_free (jnmsg);
1086 return GNUNET_OK;
1087}
1088
1089
1090/**
1091 * Handle P2P leave notification.
1092 *
1093 * @param cls closure, always NULL
1094 * @param other the other peer involved
1095 * @param message the actual message
1096 * @return GNUNET_OK to keep the connection open,
1097 * GNUNET_SYSERR to close it (signal serious error)
1098 */
1099static int
1100handle_p2p_leave_notification (void *cls,
1101 const struct GNUNET_PeerIdentity *other,
1102 const struct GNUNET_MessageHeader *message)
1103{
1104 const struct P2PLeaveNotificationMessage *p2p_lnmsg;
1105 struct GNUNET_HashCode id;
1106 struct ChatClient *pos;
1107 struct ChatClient *prev;
1108 struct ChatClient *entry;
1109 struct LeaveNotificationMessage lnmsg;
1110
1111 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Got P2P leave notification\n");
1112 p2p_lnmsg = (const struct P2PLeaveNotificationMessage *) message;
1113 GNUNET_CRYPTO_hash (&p2p_lnmsg->user,
1114 sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
1115 &id);
1116 pos = client_list_head;
1117 prev = NULL;
1118 while (NULL != pos)
1119 {
1120 if (0 == memcmp (&pos->id, &id, sizeof (struct GNUNET_HashCode)))
1121 break;
1122 prev = pos;
1123 pos = pos->next;
1124 }
1125 if (NULL == pos)
1126 {
1127#if DEBUG_CHAT_SERVICE
1128 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1129 "No such client. There is nothing to do\n");
1130#endif
1131 return GNUNET_OK;
1132 }
1133 if (NULL == prev)
1134 client_list_head = pos->next;
1135 else
1136 prev->next = pos->next;
1137#if DEBUG_CHAT_SERVICE
1138 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1139 "Notifying local room members that the client has gone away\n");
1140#endif
1141 lnmsg.header.size = htons (sizeof (struct LeaveNotificationMessage));
1142 lnmsg.header.type = htons (GNUNET_MESSAGE_TYPE_CHAT_LEAVE_NOTIFICATION);
1143 lnmsg.reserved = htonl (0);
1144 lnmsg.user = pos->public_key;
1145 entry = client_list_head;
1146 while (NULL != entry)
1147 {
1148 if (0 == strcmp (pos->room, entry->room) && (NULL != entry->client))
1149 {
1150 GNUNET_SERVER_notification_context_unicast (nc, entry->client,
1151 &lnmsg.header, GNUNET_NO);
1152 }
1153 entry = entry->next;
1154 }
1155#if DEBUG_CHAT_SERVICE
1156 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1157 "Broadcasting leave notification to neighbour peers\n");
1158#endif
1159 GNUNET_CONTAINER_multihashmap_iterate (connected_peers,
1160 &send_leave_noficiation, pos);
1161 GNUNET_free (pos->room);
1162 GNUNET_free_non_null (pos->member_info);
1163 GNUNET_free (pos);
1164 return GNUNET_OK;
1165}
1166
1167
1168/**
1169 * Handle P2P message notification.
1170 *
1171 * @param cls closure, always NULL
1172 * @param other the other peer involved
1173 * @param message the actual message
1174 * @return GNUNET_OK to keep the connection open,
1175 * GNUNET_SYSERR to close it (signal serious error)
1176 */
1177static int
1178handle_p2p_message_notification (void *cls,
1179 const struct GNUNET_PeerIdentity *other,
1180 const struct GNUNET_MessageHeader *message)
1181{
1182 const struct P2PReceiveNotificationMessage *p2p_rnmsg;
1183 struct P2PReceiveNotificationMessage *my_p2p_rnmsg;
1184 struct ReceiveNotificationMessage *rnmsg;
1185 struct ChatClient *sender;
1186 struct ChatClient *pos;
1187 static struct GNUNET_HashCode all_zeros;
1188 int is_priv;
1189 int is_anon;
1190 uint16_t msg_len;
1191 uint16_t room_name_len;
1192 char *room_name = NULL;
1193 char *text;
1194
1195 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Got P2P message notification\n");
1196 if (ntohs (message->size) <= sizeof (struct P2PReceiveNotificationMessage))
1197 {
1198 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Malformed message: wrong size\n");
1199 GNUNET_break_op (0);
1200 return GNUNET_SYSERR;
1201 }
1202 p2p_rnmsg = (const struct P2PReceiveNotificationMessage *) message;
1203 msg_len =
1204 ntohs (p2p_rnmsg->header.size) -
1205 sizeof (struct P2PReceiveNotificationMessage);
1206
1207 is_anon = (0 != (ntohl (p2p_rnmsg->msg_options) & GNUNET_CHAT_MSG_ANONYMOUS));
1208 if (is_anon)
1209 {
1210 room_name_len = ntohs (p2p_rnmsg->room_name_len);
1211 if (msg_len <= room_name_len)
1212 {
1213 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1214 "Malformed message: wrong length of the room name\n");
1215 GNUNET_break_op (0);
1216 return GNUNET_SYSERR;
1217 }
1218 msg_len -= room_name_len;
1219 if (lookup_anonymous_message (p2p_rnmsg))
1220 {
1221#if DEBUG_CHAT_SERVICE
1222 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1223 "This anonymous message has already been handled.");
1224#endif
1225 return GNUNET_OK;
1226 }
1227 remember_anonymous_message (p2p_rnmsg);
1228 room_name = GNUNET_malloc (room_name_len + 1);
1229 memcpy (room_name, (char *) &p2p_rnmsg[1], room_name_len);
1230 room_name[room_name_len] = '\0';
1231 text = (char *) &p2p_rnmsg[1] + room_name_len;
1232 }
1233 else
1234 {
1235 sender = client_list_head;
1236 while ((NULL != sender) &&
1237 (0 !=
1238 memcmp (&sender->id, &p2p_rnmsg->sender, sizeof (struct GNUNET_HashCode))))
1239 sender = sender->next;
1240 if (NULL == sender)
1241 {
1242 /* not an error since the sender may have left before we got the
1243 * message */
1244#if DEBUG_CHAT_SERVICE
1245 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1246 "Unknown source. Rejecting the message\n");
1247#endif
1248 return GNUNET_OK;
1249 }
1250 if (sender->msg_sequence_number >= ntohl (p2p_rnmsg->sequence_number))
1251 {
1252#if DEBUG_CHAT_SERVICE
1253 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1254 "This message has already been handled."
1255 " Sequence numbers (msg/sender): %u/%u\n",
1256 ntohl (p2p_rnmsg->sequence_number),
1257 sender->msg_sequence_number);
1258#endif
1259 return GNUNET_OK;
1260 }
1261 sender->msg_sequence_number = ntohl (p2p_rnmsg->sequence_number);
1262 room_name = sender->room;
1263 text = (char *) &p2p_rnmsg[1];
1264 }
1265
1266#if DEBUG_CHAT_SERVICE
1267 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1268 "Sending message to local room members\n");
1269#endif
1270 rnmsg = GNUNET_malloc (sizeof (struct ReceiveNotificationMessage) + msg_len);
1271 rnmsg->header.size =
1272 htons (sizeof (struct ReceiveNotificationMessage) + msg_len);
1273 rnmsg->header.type = htons (GNUNET_MESSAGE_TYPE_CHAT_MESSAGE_NOTIFICATION);
1274 rnmsg->msg_options = p2p_rnmsg->msg_options;
1275 rnmsg->sequence_number = p2p_rnmsg->sequence_number;
1276 rnmsg->reserved = htonl (0);
1277 rnmsg->timestamp = p2p_rnmsg->timestamp;
1278 is_priv =
1279 (0 != memcmp (&all_zeros, &p2p_rnmsg->target, sizeof (struct GNUNET_HashCode)));
1280 if (is_priv)
1281 memcpy (&rnmsg->encrypted_key, &p2p_rnmsg->encrypted_key,
1282 sizeof (struct GNUNET_CRYPTO_RsaEncryptedData));
1283 rnmsg->sender = p2p_rnmsg->sender;
1284 memcpy (&rnmsg[1], text, msg_len);
1285 pos = client_list_head;
1286 while (NULL != pos)
1287 {
1288 if ((0 == strcmp (room_name, pos->room)) && (NULL != pos->client))
1289 {
1290 if (((!is_priv) ||
1291 (0 ==
1292 memcmp (&p2p_rnmsg->target, &pos->id, sizeof (struct GNUNET_HashCode)))) &&
1293 (0 == (ntohl (p2p_rnmsg->msg_options) & (~pos->msg_options))))
1294 {
1295 GNUNET_SERVER_notification_context_unicast (nc, pos->client,
1296 &rnmsg->header, GNUNET_NO);
1297 }
1298 }
1299 pos = pos->next;
1300 }
1301 if (is_anon)
1302 GNUNET_free (room_name);
1303#if DEBUG_CHAT_SERVICE
1304 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1305 "Broadcasting message notification to neighbour peers\n");
1306#endif
1307 my_p2p_rnmsg = GNUNET_memdup (p2p_rnmsg, ntohs (p2p_rnmsg->header.size));
1308 GNUNET_CONTAINER_multihashmap_iterate (connected_peers,
1309 &send_message_noficiation,
1310 my_p2p_rnmsg);
1311 GNUNET_free (rnmsg);
1312 return GNUNET_OK;
1313}
1314
1315
1316/**
1317 * Handle P2P sync request.
1318 *
1319 * @param cls closure, always NULL
1320 * @param other the other peer involved
1321 * @param message the actual message
1322 * @return GNUNET_OK to keep the connection open,
1323 * GNUNET_SYSERR to close it (signal serious error)
1324 */
1325static int
1326handle_p2p_sync_request (void *cls, const struct GNUNET_PeerIdentity *other,
1327 const struct GNUNET_MessageHeader *message)
1328{
1329 struct ChatClient *entry;
1330 struct GNUNET_CORE_TransmitHandle *th;
1331 size_t msg_size;
1332
1333 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Got P2P sync request\n");
1334#if DEBUG_CHAT_SERVICE
1335 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1336 "Notifying the requester of all known clients\n");
1337#endif
1338 entry = client_list_head;
1339 while (NULL != entry)
1340 {
1341 msg_size =
1342 sizeof (struct P2PJoinNotificationMessage) + strlen (entry->room) +
1343 entry->meta_len;
1344 th = GNUNET_CORE_notify_transmit_ready (core, GNUNET_NO, 1,
1345 MAX_TRANSMIT_DELAY, other, msg_size,
1346 &transmit_join_notification_to_peer,
1347 entry);
1348 GNUNET_assert (NULL != th);
1349 entry = entry->next;
1350 }
1351 return GNUNET_OK;
1352}
1353
1354
1355/**
1356 * Handle P2P confirmation receipt.
1357 *
1358 * @param cls closure, always NULL
1359 * @param other the other peer involved
1360 * @param message the actual message
1361 * @return GNUNET_OK to keep the connection open,
1362 * GNUNET_SYSERR to close it (signal serious error)
1363 */
1364static int
1365handle_p2p_confirmation_receipt (void *cls,
1366 const struct GNUNET_PeerIdentity *other,
1367 const struct GNUNET_MessageHeader *message)
1368{
1369 const struct P2PConfirmationReceiptMessage *p2p_crmsg;
1370 struct P2PConfirmationReceiptMessage *my_p2p_crmsg;
1371 struct ConfirmationReceiptMessage *crmsg;
1372 struct ChatClient *target;
1373 struct ChatClient *author;
1374
1375 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Got P2P confirmation receipt\n");
1376 p2p_crmsg = (const struct P2PConfirmationReceiptMessage *) message;
1377 target = client_list_head;
1378 while ((NULL != target) &&
1379 (0 !=
1380 memcmp (&target->id, &p2p_crmsg->target, sizeof (struct GNUNET_HashCode))))
1381 target = target->next;
1382 if (NULL == target)
1383 {
1384 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1385 "Unknown source of the receipt. Rejecting the message\n");
1386 GNUNET_break_op (0);
1387 return GNUNET_SYSERR;
1388 }
1389 if (target->rcpt_sequence_number >= ntohl (p2p_crmsg->sequence_number))
1390 {
1391#if DEBUG_CHAT_SERVICE
1392 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1393 "This receipt has already been handled."
1394 " Sequence numbers (msg/sender): %u/%u\n",
1395 ntohl (p2p_crmsg->sequence_number),
1396 target->rcpt_sequence_number);
1397#endif
1398 return GNUNET_OK;
1399 }
1400 target->rcpt_sequence_number = ntohl (p2p_crmsg->sequence_number);
1401 author = client_list_head;
1402 while ((NULL != author) &&
1403 (0 !=
1404 memcmp (&author->id, &p2p_crmsg->author, sizeof (struct GNUNET_HashCode))))
1405 author = author->next;
1406 if (NULL == author)
1407 {
1408 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1409 "Unknown addressee. Rejecting the receipt\n");
1410 GNUNET_break_op (0);
1411 return GNUNET_SYSERR;
1412 }
1413
1414 if (NULL == author->client)
1415 {
1416#if DEBUG_CHAT_SERVICE
1417 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1418 "The author of the original message is not a local client."
1419 " Broadcasting receipt to neighbour peers\n");
1420#endif
1421 my_p2p_crmsg =
1422 GNUNET_memdup (p2p_crmsg,
1423 sizeof (struct P2PConfirmationReceiptMessage));
1424 GNUNET_CONTAINER_multihashmap_iterate (connected_peers,
1425 &send_confirmation_receipt,
1426 my_p2p_crmsg);
1427 GNUNET_free (my_p2p_crmsg);
1428 }
1429 else
1430 {
1431#if DEBUG_CHAT_SERVICE
1432 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1433 "The author of the original message is a local client."
1434 " Verifying signature of the receipt\n");
1435#endif
1436 crmsg = GNUNET_malloc (sizeof (struct ConfirmationReceiptMessage));
1437 crmsg->header.size = htons (sizeof (struct ConfirmationReceiptMessage));
1438 crmsg->header.type =
1439 htons (GNUNET_MESSAGE_TYPE_CHAT_CONFIRMATION_NOTIFICATION);
1440 crmsg->signature = p2p_crmsg->signature;
1441 crmsg->purpose = p2p_crmsg->purpose;
1442 crmsg->sequence_number = p2p_crmsg->msg_sequence_number;
1443 crmsg->reserved2 = 0;
1444 crmsg->timestamp = p2p_crmsg->timestamp;
1445 crmsg->target = p2p_crmsg->target;
1446 crmsg->author = p2p_crmsg->author;
1447 crmsg->content = p2p_crmsg->content;
1448 if (GNUNET_OK !=
1449 GNUNET_CRYPTO_rsa_verify (GNUNET_SIGNATURE_PURPOSE_CHAT_RECEIPT,
1450 &crmsg->purpose, &crmsg->signature,
1451 &target->public_key))
1452 {
1453 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1454 "Invalid signature of the receipt\n");
1455 GNUNET_break_op (0);
1456 return GNUNET_SYSERR;
1457 }
1458#if DEBUG_CHAT_SERVICE
1459 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1460 "The author of the original message is a local client."
1461 " Sending receipt to the client\n");
1462#endif
1463 GNUNET_SERVER_notification_context_unicast (nc, author->client,
1464 &crmsg->header, GNUNET_NO);
1465 GNUNET_free (crmsg);
1466 }
1467 return GNUNET_OK;
1468}
1469
1470
1471/**
1472 * Transmit a sync request to the peer.
1473 *
1474 * @param cls closure, NULL
1475 * @param size number of bytes available in buf
1476 * @param buf where the callee should write the message
1477 * @return number of bytes written to buf
1478 */
1479static size_t
1480transmit_sync_request_to_peer (void *cls, size_t size, void *buf)
1481{
1482 struct GNUNET_MessageHeader *m = buf;
1483 size_t msg_size;
1484
1485#if DEBUG_CHAT_SERVICE
1486 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Transmitting P2P sync request\n");
1487#endif
1488 msg_size = sizeof (struct GNUNET_MessageHeader);
1489 GNUNET_assert (size >= msg_size);
1490 GNUNET_assert (NULL != buf);
1491 m = buf;
1492 m->type = htons (GNUNET_MESSAGE_TYPE_CHAT_P2P_SYNC_REQUEST);
1493 m->size = htons (msg_size);
1494 return msg_size;
1495}
1496
1497
1498/**
1499 * Method called whenever a peer connects.
1500 *
1501 * @param cls closure
1502 * @param peer peer identity this notification is about
1503 */
1504static void
1505peer_connect_handler (void *cls, const struct GNUNET_PeerIdentity *peer)
1506{
1507 struct ConnectedPeer *cp;
1508 struct GNUNET_CORE_TransmitHandle *th;
1509
1510 if (0 == memcmp (peer, &me, sizeof (struct GNUNET_PeerIdentity)))
1511 return;
1512 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Peer connected: %s\n",
1513 GNUNET_i2s (peer));
1514 th = GNUNET_CORE_notify_transmit_ready (core, GNUNET_YES, 1,
1515 MAX_TRANSMIT_DELAY, peer,
1516 sizeof (struct GNUNET_MessageHeader),
1517 &transmit_sync_request_to_peer, NULL);
1518 GNUNET_assert (NULL != th);
1519 cp = GNUNET_CONTAINER_multihashmap_get (connected_peers, &peer->hashPubKey);
1520 if (NULL != cp)
1521 {
1522 GNUNET_break (0);
1523 return;
1524 }
1525 cp = GNUNET_malloc (sizeof (struct ConnectedPeer));
1526 cp->pid = GNUNET_PEER_intern (peer);
1527 GNUNET_break (GNUNET_OK ==
1528 GNUNET_CONTAINER_multihashmap_put (connected_peers,
1529 &peer->hashPubKey, cp,
1530 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
1531}
1532
1533
1534/**
1535 * Iterator to free peer entries.
1536 *
1537 * @param cls closure, unused
1538 * @param key current key code
1539 * @param value value in the hash map (peer entry)
1540 * @return GNUNET_YES (we should continue to iterate)
1541 */
1542static int
1543clean_peer (void *cls, const struct GNUNET_HashCode * key, void *value)
1544{
1545 struct ConnectedPeer *cp;
1546 const struct GNUNET_PeerIdentity *peer =
1547 (const struct GNUNET_PeerIdentity *) key;
1548
1549 cp = GNUNET_CONTAINER_multihashmap_get (connected_peers, &peer->hashPubKey);
1550 if (cp == NULL)
1551 return GNUNET_YES;
1552 GNUNET_break (GNUNET_YES ==
1553 GNUNET_CONTAINER_multihashmap_remove (connected_peers,
1554 &peer->hashPubKey, cp));
1555 GNUNET_PEER_change_rc (cp->pid, -1);
1556 GNUNET_free (cp);
1557 return GNUNET_YES;
1558}
1559
1560
1561/**
1562 * Method called whenever a peer disconnects.
1563 *
1564 * @param cls closure, not used
1565 * @param peer peer identity this notification is about
1566 */
1567static void
1568peer_disconnect_handler (void *cls, const struct GNUNET_PeerIdentity *peer)
1569{
1570
1571 if (0 == memcmp (peer, &me, sizeof (struct GNUNET_PeerIdentity)))
1572 return;
1573 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Peer disconnected: %s\n",
1574 GNUNET_i2s (peer));
1575 clean_peer (NULL, (const struct GNUNET_HashCode *) peer, NULL);
1576}
1577
1578
1579/**
1580 * Task run during shutdown.
1581 *
1582 * @param cls unused
1583 * @param tc unused
1584 */
1585static void
1586cleanup_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1587{
1588 struct AnonymousMessage *next_msg;
1589 struct ChatClient *next_client;
1590
1591 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Cleaning up\n");
1592 if (NULL != core)
1593 {
1594 GNUNET_CORE_disconnect (core);
1595 core = NULL;
1596 }
1597 if (NULL != nc)
1598 {
1599 GNUNET_SERVER_notification_context_destroy (nc);
1600 nc = NULL;
1601 }
1602 while (NULL != client_list_head)
1603 {
1604 next_client = client_list_head->next;
1605 GNUNET_free (client_list_head->room);
1606 GNUNET_free_non_null (client_list_head->member_info);
1607 GNUNET_free (client_list_head);
1608 client_list_head = next_client;
1609 }
1610 while (NULL != anonymous_list_head)
1611 {
1612 next_msg = anonymous_list_head->next;
1613 GNUNET_free (anonymous_list_head);
1614 anonymous_list_head = next_msg;
1615 }
1616 GNUNET_CONTAINER_multihashmap_iterate (connected_peers, &clean_peer, NULL);
1617 GNUNET_CONTAINER_multihashmap_destroy (connected_peers);
1618 connected_peers = NULL;
1619}
1620
1621
1622/**
1623 * To be called on core init/fail.
1624 *
1625 * @param cls closure, NULL
1626 * @param server handle to the server for this service
1627 * @param my_identity the public identity of this peer
1628 */
1629static void
1630core_init (void *cls, struct GNUNET_CORE_Handle *server,
1631 const struct GNUNET_PeerIdentity *my_identity)
1632{
1633 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Core initialized\n");
1634 me = *my_identity;
1635}
1636
1637
1638/**
1639 * Process chat requests.
1640 *
1641 * @param cls closure, NULL
1642 * @param server the initialized server
1643 * @param c configuration to use
1644 */
1645static void
1646run (void *cls, struct GNUNET_SERVER_Handle *server,
1647 const struct GNUNET_CONFIGURATION_Handle *c)
1648{
1649 static const struct GNUNET_SERVER_MessageHandler handlers[] = {
1650 {&handle_join_request, NULL,
1651 GNUNET_MESSAGE_TYPE_CHAT_JOIN_REQUEST, 0},
1652 {&handle_transmit_request, NULL,
1653 GNUNET_MESSAGE_TYPE_CHAT_TRANSMIT_REQUEST, 0},
1654 {&handle_acknowledge_request, NULL,
1655 GNUNET_MESSAGE_TYPE_CHAT_CONFIRMATION_RECEIPT,
1656 sizeof (struct ConfirmationReceiptMessage)},
1657 {NULL, NULL, 0, 0}
1658 };
1659 static const struct GNUNET_CORE_MessageHandler p2p_handlers[] = {
1660 {&handle_p2p_join_notification,
1661 GNUNET_MESSAGE_TYPE_CHAT_P2P_JOIN_NOTIFICATION, 0},
1662 {&handle_p2p_leave_notification,
1663 GNUNET_MESSAGE_TYPE_CHAT_P2P_LEAVE_NOTIFICATION,
1664 sizeof (struct P2PLeaveNotificationMessage)},
1665 {&handle_p2p_message_notification,
1666 GNUNET_MESSAGE_TYPE_CHAT_P2P_MESSAGE_NOTIFICATION, 0},
1667 {&handle_p2p_sync_request,
1668 GNUNET_MESSAGE_TYPE_CHAT_P2P_SYNC_REQUEST,
1669 sizeof (struct GNUNET_MessageHeader)},
1670 {&handle_p2p_confirmation_receipt,
1671 GNUNET_MESSAGE_TYPE_CHAT_P2P_CONFIRMATION_RECEIPT,
1672 sizeof (struct P2PConfirmationReceiptMessage)},
1673 {NULL, 0, 0}
1674 };
1675
1676 GNUNET_log_setup ("gnunet-service-chat",
1677#if DEBUG_CHAT_SERVICE
1678 "DEBUG",
1679#else
1680 "WARNING",
1681#endif
1682 NULL);
1683 cfg = c;
1684 nc = GNUNET_SERVER_notification_context_create (server, 16);
1685 connected_peers =
1686 GNUNET_CONTAINER_multihashmap_create (EXPECTED_NEIGHBOUR_COUNT, GNUNET_NO);
1687 GNUNET_SERVER_add_handlers (server, handlers);
1688 core =
1689 GNUNET_CORE_connect (cfg, NULL, &core_init,
1690 &peer_connect_handler, &peer_disconnect_handler,
1691 NULL, GNUNET_NO, NULL, GNUNET_NO, p2p_handlers);
1692 GNUNET_SERVER_disconnect_notify (server, &handle_client_disconnect, NULL);
1693 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &cleanup_task,
1694 NULL);
1695}
1696
1697
1698/**
1699 * The main function for the chat service.
1700 *
1701 * @param argc number of arguments from the command line
1702 * @param argv command line arguments
1703 * @return 0 ok, 1 on error
1704 */
1705int
1706main (int argc, char *const *argv)
1707{
1708 return (GNUNET_OK ==
1709 GNUNET_SERVICE_run (argc, argv, "chat", GNUNET_SERVICE_OPTION_NONE,
1710 &run, NULL)) ? 0 : 1;
1711}
1712
1713/* end of gnunet-service-chat.c */
diff --git a/src/chat/test_chat.c b/src/chat/test_chat.c
deleted file mode 100644
index 78b31f1b1..000000000
--- a/src/chat/test_chat.c
+++ /dev/null
@@ -1,556 +0,0 @@
1/*
2 This file is part of GNUnet.
3 (C) 2005, 2006, 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/test_chat.c
23 * @brief base test case for the chat library
24 * @author Christian Grothoff
25 * @author Nathan Evans
26 * @author Vitaly Minko
27 *
28 * This test case serves as a base for simple chatting, anonymous chatting,
29 * authenticated chatting and acknowledgements test cases. Based on the
30 * executable being run the correct test case will be performed. Private
31 * chatting is covered by a separate test case since it requires 3 users.
32 */
33
34#include "platform.h"
35#include "gnunet_crypto_lib.h"
36#include "gnunet_util_lib.h"
37#include "gnunet_arm_service.h"
38#include "gnunet_chat_service.h"
39
40/**
41 * How long until we give up on passing the test?
42 */
43#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 60)
44
45struct PeerContext
46{
47 struct GNUNET_CONFIGURATION_Handle *cfg;
48 struct GNUNET_OS_Process *arm_proc;
49};
50
51struct Wanted
52{
53 struct GNUNET_CONTAINER_MetaData *meta;
54
55 struct GNUNET_HashCode *sender;
56
57 char *msg;
58
59 const char *me;
60
61 enum GNUNET_CHAT_MsgOptions opt;
62
63 uint32_t sequence_number;
64
65 struct GNUNET_TIME_Absolute timestamp;
66
67 GNUNET_SCHEDULER_Task next_task;
68
69 void *next_task_cls;
70
71};
72
73static struct PeerContext p1;
74
75static struct PeerContext p2;
76
77static struct GNUNET_HashCode alice;
78
79static struct GNUNET_HashCode bob;
80
81static struct GNUNET_CHAT_Room *alice_room;
82
83static struct GNUNET_CHAT_Room *bob_room;
84
85static struct GNUNET_CONTAINER_MetaData *alice_meta;
86
87static struct GNUNET_CONTAINER_MetaData *bob_meta;
88
89static struct Wanted alice_wanted;
90
91static struct Wanted bob_wanted;
92
93static GNUNET_SCHEDULER_TaskIdentifier kill_task;
94
95static GNUNET_SCHEDULER_TaskIdentifier wait_task;
96
97static int err;
98
99static int is_ready;
100
101static int is_p2p;
102
103static int is_ackn;
104
105static int is_anon;
106
107static int is_auth;
108
109
110static void
111setup_peer (struct PeerContext *p, const char *cfgname)
112{
113 char *binary;
114
115 binary = GNUNET_OS_get_libexec_binary_path ("gnunet-service-arm");
116 p->cfg = GNUNET_CONFIGURATION_create ();
117 p->arm_proc =
118 GNUNET_OS_start_process (GNUNET_YES, GNUNET_OS_INHERIT_STD_OUT_AND_ERR, NULL, NULL, binary,
119 "gnunet-service-arm",
120 "-c", cfgname, NULL);
121 GNUNET_assert (GNUNET_OK == GNUNET_CONFIGURATION_load (p->cfg, cfgname));
122 GNUNET_free (binary);
123}
124
125
126static void
127stop_arm (struct PeerContext *p)
128{
129 if (0 != GNUNET_OS_process_kill (p->arm_proc, SIGTERM))
130 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");
131 if (GNUNET_OS_process_wait (p->arm_proc) != GNUNET_OK)
132 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "waitpid");
133 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "ARM process %u stopped\n",
134 GNUNET_OS_process_get_pid (p->arm_proc));
135 GNUNET_OS_process_destroy (p->arm_proc);
136 p->arm_proc = NULL;
137 GNUNET_CONFIGURATION_destroy (p->cfg);
138}
139
140
141static void
142abort_test (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
143{
144 if (alice_room != NULL)
145 {
146 GNUNET_CHAT_leave_room (alice_room);
147 alice_room = NULL;
148 }
149 if (bob_room != NULL)
150 {
151 GNUNET_CHAT_leave_room (bob_room);
152 bob_room = NULL;
153 }
154 err = 1;
155}
156
157
158static void
159timeout_kill (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
160{
161 fprintf (stderr,
162 "Timed out, stopping the test.\n");
163 kill_task = GNUNET_SCHEDULER_NO_TASK;
164 if (wait_task != GNUNET_SCHEDULER_NO_TASK)
165 {
166 GNUNET_SCHEDULER_cancel (wait_task);
167 wait_task = GNUNET_SCHEDULER_NO_TASK;
168 }
169 GNUNET_SCHEDULER_add_continuation (&abort_test, NULL,
170 GNUNET_SCHEDULER_REASON_PREREQ_DONE);
171}
172
173
174static int
175join_cb (void *cls)
176{
177 struct Wanted *want = cls;
178
179 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
180 "%s has joined\n", want->me);
181 if (NULL != want->next_task)
182 GNUNET_SCHEDULER_add_now (want->next_task, want->next_task_cls);
183 return GNUNET_OK;
184}
185
186
187static int
188member_list_cb (void *cls, const struct GNUNET_CONTAINER_MetaData *member_info,
189 const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *member_id,
190 enum GNUNET_CHAT_MsgOptions options)
191{
192 struct Wanted *want = cls;
193 struct GNUNET_HashCode sender;
194
195 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
196 "%s - told that %s has %s\n", want->me,
197 member_info ==
198 NULL ? NULL : GNUNET_CONTAINER_meta_data_get_by_type (member_info,
199 EXTRACTOR_METATYPE_TITLE),
200 member_info == NULL ? "left" : "joined");
201 GNUNET_CRYPTO_hash (member_id,
202 sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
203 &sender);
204 if ((0 == memcmp (&sender, want->sender, sizeof (struct GNUNET_HashCode))) &&
205 (((member_info == NULL) && (want->meta == NULL)) ||
206 ((member_info != NULL) && (want->meta != NULL) &&
207 (GNUNET_CONTAINER_meta_data_test_equal (member_info, want->meta)))) &&
208 (options == want->opt))
209 {
210 if (NULL != want->next_task)
211 GNUNET_SCHEDULER_add_now (want->next_task, want->next_task_cls);
212 }
213 else
214 {
215 GNUNET_SCHEDULER_cancel (kill_task);
216 kill_task = GNUNET_SCHEDULER_NO_TASK;
217 GNUNET_SCHEDULER_add_now (&abort_test, NULL);
218 }
219 return GNUNET_OK;
220}
221
222
223static int
224receive_cb (void *cls, struct GNUNET_CHAT_Room *room,
225 const struct GNUNET_HashCode * sender,
226 const struct GNUNET_CONTAINER_MetaData *meta, const char *message,
227 struct GNUNET_TIME_Absolute timestamp,
228 enum GNUNET_CHAT_MsgOptions options)
229{
230 struct Wanted *want = cls;
231
232 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
233
234 "%s - told that %s said %s\n", want->me,
235 meta == NULL ? NULL : GNUNET_CONTAINER_meta_data_get_by_type (meta,
236 EXTRACTOR_METATYPE_TITLE),
237 message);
238 if ((0 == strcmp (message, want->msg)) &&
239 (((sender == NULL) && (want->sender == NULL)) ||
240 ((sender != NULL) && (want->sender != NULL) &&
241 (0 == memcmp (sender, want->sender, sizeof (struct GNUNET_HashCode))))) &&
242 (GNUNET_CONTAINER_meta_data_test_equal (meta, want->meta)) &&
243 (options == want->opt) &&
244 /* Not == since the library sets the actual timestamp, so it may be
245 * slightly greater
246 */
247 (timestamp.abs_value >= want->timestamp.abs_value))
248 {
249 if (NULL != want->next_task)
250 GNUNET_SCHEDULER_add_now (want->next_task, want->next_task_cls);
251 }
252 else
253 {
254 GNUNET_SCHEDULER_cancel (kill_task);
255 kill_task = GNUNET_SCHEDULER_NO_TASK;
256 GNUNET_SCHEDULER_add_now (&abort_test, NULL);
257 }
258 return GNUNET_OK;
259}
260
261
262static int
263confirmation_cb (void *cls, struct GNUNET_CHAT_Room *room,
264 uint32_t orig_seq_number,
265 struct GNUNET_TIME_Absolute timestamp,
266 const struct GNUNET_HashCode * receiver)
267{
268 struct Wanted *want = cls;
269
270 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
271 "%s - told that %s acknowledged message #%d\n", want->me,
272 GNUNET_CONTAINER_meta_data_get_by_type (want->meta,
273 EXTRACTOR_METATYPE_TITLE),
274 orig_seq_number);
275 if ((0 == memcmp (receiver, want->sender, sizeof (struct GNUNET_HashCode))) &&
276 (orig_seq_number == want->sequence_number) &&
277 (timestamp.abs_value >= want->timestamp.abs_value))
278 {
279 if (NULL != want->next_task)
280 GNUNET_SCHEDULER_add_now (want->next_task, want->next_task_cls);
281 }
282 else
283 {
284 GNUNET_SCHEDULER_cancel (kill_task);
285 kill_task = GNUNET_SCHEDULER_NO_TASK;
286 GNUNET_SCHEDULER_add_now (&abort_test, NULL);
287 }
288 return GNUNET_OK;
289}
290
291
292static void
293wait_until_ready (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
294{
295 GNUNET_SCHEDULER_Task task = cls;
296
297 if (is_ready)
298 {
299 wait_task = GNUNET_SCHEDULER_NO_TASK;
300 GNUNET_SCHEDULER_add_now (task, NULL);
301 }
302 else
303 wait_task =
304 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
305 (GNUNET_TIME_UNIT_MILLISECONDS, 50),
306 &wait_until_ready, task);
307}
308
309
310static void
311disconnect_alice (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
312{
313 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
314 "Alice is leaving.\n");
315 if (is_p2p)
316 stop_arm (&p2);
317 GNUNET_CHAT_leave_room (alice_room);
318 alice_room = NULL;
319 GNUNET_SCHEDULER_cancel (kill_task);
320 kill_task = GNUNET_SCHEDULER_NO_TASK;
321}
322
323
324static void
325disconnect_bob (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
326{
327 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
328 "Bob is leaving.\n");
329 alice_wanted.meta = NULL;
330 alice_wanted.sender = &bob;
331 alice_wanted.msg = NULL;
332 alice_wanted.opt = 0;
333 alice_wanted.next_task = &disconnect_alice;
334 alice_wanted.next_task_cls = NULL;
335 GNUNET_CHAT_leave_room (bob_room);
336 bob_room = NULL;
337}
338
339
340static void
341set_ready (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
342{
343 is_ready = GNUNET_YES;
344}
345
346
347static void
348send_to_alice (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
349{
350 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
351 "Bob says 'Hi!'\n");
352 alice_wanted.meta = bob_meta;
353 alice_wanted.sender = &bob;
354 alice_wanted.msg = "Hi Alice!";
355 alice_wanted.opt = GNUNET_CHAT_MSG_OPTION_NONE;
356 alice_wanted.timestamp = GNUNET_TIME_absolute_get ();
357 alice_wanted.next_task = &disconnect_bob;
358 alice_wanted.next_task_cls = NULL;
359 GNUNET_CHAT_send_message (bob_room, "Hi Alice!", GNUNET_CHAT_MSG_OPTION_NONE,
360 NULL, NULL);
361}
362
363
364static void
365send_to_bob (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
366{
367 enum GNUNET_CHAT_MsgOptions options;
368 uint32_t *seq = NULL;
369
370 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
371 "Alice says 'Hi!'\n");
372 if (is_ackn)
373 {
374 options = GNUNET_CHAT_MSG_ACKNOWLEDGED;
375 alice_wanted.meta = bob_meta;
376 alice_wanted.sender = &bob;
377 alice_wanted.timestamp = GNUNET_TIME_absolute_get ();
378 alice_wanted.next_task = &disconnect_bob;
379 alice_wanted.next_task_cls = NULL;
380 bob_wanted.meta = alice_meta;
381 bob_wanted.sender = &alice;
382 bob_wanted.next_task = NULL;
383 seq = &(alice_wanted.sequence_number);
384 }
385 else if (is_anon)
386 {
387 options = GNUNET_CHAT_MSG_ANONYMOUS;
388 bob_wanted.meta = NULL;
389 bob_wanted.sender = NULL;
390 bob_wanted.next_task = &disconnect_bob;
391 }
392 else if (is_auth)
393 {
394 options = GNUNET_CHAT_MSG_AUTHENTICATED;
395 bob_wanted.meta = alice_meta;
396 bob_wanted.sender = &alice;
397 bob_wanted.next_task = &disconnect_bob;
398 }
399 else
400 {
401 options = GNUNET_CHAT_MSG_OPTION_NONE;
402 bob_wanted.meta = alice_meta;
403 bob_wanted.sender = &alice;
404 bob_wanted.next_task = &send_to_alice;
405 }
406 bob_wanted.msg = "Hi Bob!";
407 bob_wanted.opt = options;
408 bob_wanted.timestamp = GNUNET_TIME_absolute_get ();
409 bob_wanted.next_task_cls = NULL;
410 GNUNET_CHAT_send_message (alice_room, "Hi Bob!", options, NULL, seq);
411}
412
413
414static void
415prepare_for_alice_task (void *cls,
416 const struct GNUNET_SCHEDULER_TaskContext *tc)
417{
418 bob_wanted.meta = alice_meta;
419 bob_wanted.sender = &alice;
420 bob_wanted.msg = NULL;
421 bob_wanted.opt = -1;
422 bob_wanted.next_task = &set_ready;
423 bob_wanted.next_task_cls = NULL;
424}
425
426
427static void
428join_bob_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
429{
430 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
431 "Bob joining\n");
432 alice_wanted.meta = bob_meta;
433 alice_wanted.sender = &bob;
434 alice_wanted.msg = NULL;
435 alice_wanted.opt = -1;
436 alice_wanted.next_task = &wait_until_ready;
437 alice_wanted.next_task_cls = &send_to_bob;
438 bob_wanted.next_task = &prepare_for_alice_task;
439 bob_wanted.next_task_cls = NULL;
440 is_ready = GNUNET_NO;
441 bob_room =
442 GNUNET_CHAT_join_room (is_p2p ? p2.cfg : p1.cfg, "bob", bob_meta, "test",
443 -1, &join_cb, &bob_wanted, &receive_cb,
444 &bob_wanted, &member_list_cb, &bob_wanted,
445 &confirmation_cb, &bob_wanted, &bob);
446 if (NULL == bob_room)
447 {
448 GNUNET_SCHEDULER_cancel (kill_task);
449 kill_task = GNUNET_SCHEDULER_NO_TASK;
450 GNUNET_CHAT_leave_room (alice_room);
451 alice_room = NULL;
452 err = 1;
453 }
454}
455
456
457static void
458join_alice_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
459{
460 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
461 "Alice joining\n");
462 alice_wanted.next_task = &join_bob_task;
463 alice_wanted.next_task_cls = NULL;
464 alice_room =
465 GNUNET_CHAT_join_room (p1.cfg, "alice", alice_meta, "test", -1, &join_cb,
466 &alice_wanted, &receive_cb, &alice_wanted,
467 &member_list_cb, &alice_wanted, &confirmation_cb,
468 &alice_wanted, &alice);
469 if (NULL == alice_room)
470 {
471 GNUNET_SCHEDULER_cancel (kill_task);
472 kill_task = GNUNET_SCHEDULER_NO_TASK;
473 err = 1;
474 }
475}
476
477
478static void
479run (void *cls, char *const *args, const char *cfgfile,
480 const struct GNUNET_CONFIGURATION_Handle *cfg)
481{
482 if (is_p2p)
483 {
484 setup_peer (&p1, "test_chat_peer1.conf");
485 setup_peer (&p2, "test_chat_peer2.conf");
486 }
487 else
488 setup_peer (&p1, "test_chat_data.conf");
489
490 memset (&alice_wanted, 0, sizeof (struct Wanted));
491 memset (&bob_wanted, 0, sizeof (struct Wanted));
492 alice_wanted.me = "Alice";
493 bob_wanted.me = "Bob";
494 alice_meta = GNUNET_CONTAINER_meta_data_create ();
495 GNUNET_CONTAINER_meta_data_insert (alice_meta, "<gnunet>",
496 EXTRACTOR_METATYPE_TITLE,
497 EXTRACTOR_METAFORMAT_UTF8, "text/plain",
498 "Alice", strlen ("Alice") + 1);
499 bob_meta = GNUNET_CONTAINER_meta_data_create ();
500 GNUNET_CONTAINER_meta_data_insert (bob_meta, "<gnunet>",
501 EXTRACTOR_METATYPE_TITLE,
502 EXTRACTOR_METAFORMAT_UTF8, "text/plain",
503 "Bob", strlen ("Bob") + 1);
504 kill_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT, &timeout_kill, NULL);
505 GNUNET_SCHEDULER_add_now (&join_alice_task, NULL);
506}
507
508
509int
510main (int argc, char *argv[])
511{
512 char *const argvx[] = {
513 "test-chat",
514 "-c",
515 "test_chat_data.conf",
516 NULL
517 };
518 struct GNUNET_GETOPT_CommandLineOption options[] = {
519 GNUNET_GETOPT_OPTION_END
520 };
521
522 GNUNET_log_setup ("test_chat",
523 "WARNING",
524 NULL);
525 if (strstr (argv[0], "p2p") != NULL)
526 {
527 is_p2p = GNUNET_YES;
528 }
529 if (strstr (argv[0], "acknowledgment") != NULL)
530 {
531 is_ackn = GNUNET_YES;
532 }
533 else if (strstr (argv[0], "anonymous") != NULL)
534 {
535 is_anon = GNUNET_YES;
536 }
537 else if (strstr (argv[0], "authentication") != NULL)
538 {
539 is_auth = GNUNET_YES;
540 }
541 GNUNET_PROGRAM_run ((sizeof (argvx) / sizeof (char *)) - 1, argvx,
542 "test-chat", "nohelp", options, &run, NULL);
543 stop_arm (&p1);
544 GNUNET_CONTAINER_meta_data_destroy (alice_meta);
545 GNUNET_CONTAINER_meta_data_destroy (bob_meta);
546 if (is_p2p)
547 {
548 GNUNET_DISK_directory_remove ("/tmp/gnunet-test-chat-peer-1/");
549 GNUNET_DISK_directory_remove ("/tmp/gnunet-test-chat-peer-2/");
550 }
551 else
552 GNUNET_DISK_directory_remove ("/tmp/gnunet-test-chat/");
553 return err;
554}
555
556/* end of test_chat.c */
diff --git a/src/chat/test_chat_data.conf b/src/chat/test_chat_data.conf
deleted file mode 100644
index 45a6ce7c9..000000000
--- a/src/chat/test_chat_data.conf
+++ /dev/null
@@ -1,55 +0,0 @@
1[PATHS]
2SERVICEHOME = /tmp/gnunet-test-chat/
3
4[gnunetd]
5HOSTKEY = $SERVICEHOME/.hostkey
6
7[resolver]
8PORT = 42464
9HOSTNAME = localhost
10
11[transport]
12PORT = 42465
13PLUGINS =
14
15[arm]
16PORT = 42466
17HOSTNAME = localhost
18DEFAULTSERVICES = core chat
19
20[peerinfo]
21PORT = 42469
22HOSTNAME = localhost
23
24[core]
25PORT = 42470
26HOSTNAME = localhost
27
28[chat]
29PORT = 42471
30HOSTNAME = localhost
31HOME = $SERVICEHOME
32BINARY = gnunet-service-chat
33
34[testing]
35WEAKRANDOM = YES
36
37[nat]
38DISABLEV6 = YES
39BINDTO = 127.0.0.1
40ENABLE_UPNP = NO
41BEHIND_NAT = NO
42ALLOW_NAT = NO
43INTERNAL_ADDRESS = 127.0.0.1
44EXTERNAL_ADDRESS = 127.0.0.1
45
46[dns]
47AUTOSTART = NO
48
49[consensus]
50AUTOSTART = NO
51
52[nse]
53AUTOSTART = NO
54
55
diff --git a/src/chat/test_chat_peer1.conf b/src/chat/test_chat_peer1.conf
deleted file mode 100644
index 0193188de..000000000
--- a/src/chat/test_chat_peer1.conf
+++ /dev/null
@@ -1,92 +0,0 @@
1[PATHS]
2SERVICEHOME = /tmp/gnunet-test-chat-peer-1/
3
4[gnunetd]
5HOSTKEY = $SERVICEHOME/.hostkey
6
7[hostlist]
8HTTPPORT = 31000
9OPTIONS = -p
10
11[resolver]
12PORT = 31001
13HOSTNAME = localhost
14UNIXPATH = /tmp/gnunet-chat-p1-service-resolver.sock
15
16[transport]
17PORT = 31002
18UNIXPATH = /tmp/gnunet-chat-p1-service-transport.sock
19PLUGINS = tcp
20#BINARY = /home/grothoff/bin/gnunet-service-transport
21#PREFIX = valgrind
22
23[transport-tcp]
24PORT = 31003
25
26[arm]
27PORT = 31004
28UNIXPATH = /tmp/gnunet-chat-p1-service-arm.sock
29HOSTNAME = localhost
30DEFAULTSERVICES = resolver transport core topology hostlist statistics chat
31
32[core]
33PORT = 31005
34UNIXPATH = /tmp/gnunet-chat-p1-service-core.sock
35HOSTNAME = localhost
36
37[topology]
38MINIMUM-FRIENDS = 0
39FRIENDS-ONLY = NO
40AUTOCONNECT = YES
41TARGET-CONNECTION-COUNT = 16
42FRIENDS = $SERVICEHOME/friends
43BINARY = gnunet-daemon-topology
44
45[peerinfo]
46PORT = 31006
47UNIXPATH = /tmp/gnunet-chat-p1-service-peerinfo.sock
48HOSTNAME = localhost
49
50[statistics]
51PORT = 31007
52HOSTNAME = localhost
53UNIXPATH = /tmp/gnunet-chat-p1-service-statistics.sock
54
55[chat]
56PORT = 31008
57HOSTNAME = localhost
58HOME = $SERVICEHOME
59BINARY = gnunet-service-chat
60
61[testing]
62WEAKRANDOM = YES
63
64[fs]
65AUTOSTART = NO
66
67[datastore]
68AUTOSTART = NO
69
70[dht]
71AUTOSTART = NO
72
73[mesh]
74AUTOSTART = NO
75[nat]
76DISABLEV6 = YES
77BINDTO = 127.0.0.1
78ENABLE_UPNP = NO
79BEHIND_NAT = NO
80ALLOW_NAT = NO
81INTERNAL_ADDRESS = 127.0.0.1
82EXTERNAL_ADDRESS = 127.0.0.1
83
84[dns]
85AUTOSTART = NO
86
87[nse]
88AUTOSTART = NO
89
90[ats]
91PORT = 31971
92UNIXPATH = /tmp/gnunet-chat-p1-service-ats.sock
diff --git a/src/chat/test_chat_peer2.conf b/src/chat/test_chat_peer2.conf
deleted file mode 100644
index c4a31a0f3..000000000
--- a/src/chat/test_chat_peer2.conf
+++ /dev/null
@@ -1,94 +0,0 @@
1[PATHS]
2SERVICEHOME = /tmp/gnunet-test-chat-peer-2/
3
4[gnunetd]
5HOSTKEY = $SERVICEHOME/.hostkey
6
7[hostlist]
8SERVERS = http://localhost:31000/
9OPTIONS = -b
10
11[resolver]
12PORT = 32001
13HOSTNAME = localhost
14UNIXPATH = /tmp/gnunet-chat-p2-service-resolver.sock
15
16[transport]
17PORT = 32002
18UNIXPATH = /tmp/gnunet-chat-p2-service-transport.sock
19PLUGINS = tcp
20#BINARY = /home/grothoff/bin/gnunet-service-transport
21#PREFIX = valgrind
22
23[transport-tcp]
24PORT = 32003
25
26[arm]
27PORT = 32004
28UNIXPATH = /tmp/gnunet-chat-p2-service-arm.sock
29HOSTNAME = localhost
30DEFAULTSERVICES = resolver transport core topology hostlist statistics chat
31
32[core]
33PORT = 32005
34UNIXPATH = /tmp/gnunet-chat-p2-service-core.sock
35HOSTNAME = localhost
36
37[topology]
38MINIMUM-FRIENDS = 0
39FRIENDS-ONLY = NO
40AUTOCONNECT = YES
41TARGET-CONNECTION-COUNT = 16
42FRIENDS = $SERVICEHOME/friends
43BINARY = gnunet-daemon-topology
44
45[peerinfo]
46PORT = 32006
47UNIXPATH = /tmp/gnunet-chat-p2-service-peerinfo.sock
48HOSTNAME = localhost
49
50[statistics]
51PORT = 32007
52HOSTNAME = localhost
53UNIXPATH = /tmp/gnunet-chat-p2-service-statistics.sock
54
55[chat]
56PORT = 32008
57HOSTNAME = localhost
58HOME = $SERVICEHOME
59BINARY = gnunet-service-chat
60
61[testing]
62WEAKRANDOM = YES
63
64[fs]
65AUTOSTART = NO
66
67[datastore]
68AUTOSTART = NO
69
70[dht]
71AUTOSTART = NO
72
73[mesh]
74AUTOSTART = NO
75
76[nat]
77DISABLEV6 = YES
78BINDTO = 127.0.0.1
79ENABLE_UPNP = NO
80BEHIND_NAT = NO
81ALLOW_NAT = NO
82INTERNAL_ADDRESS = 127.0.0.1
83EXTERNAL_ADDRESS = 127.0.0.1
84
85[dns]
86AUTOSTART = NO
87
88[nse]
89AUTOSTART = NO
90
91[ats]
92PORT = 32971
93UNIXPATH = /tmp/gnunet-chat-p2-service-ats.sock
94
diff --git a/src/chat/test_chat_peer3.conf b/src/chat/test_chat_peer3.conf
deleted file mode 100644
index d8dcedc29..000000000
--- a/src/chat/test_chat_peer3.conf
+++ /dev/null
@@ -1,93 +0,0 @@
1[PATHS]
2SERVICEHOME = /tmp/gnunet-test-chat-peer-3/
3
4[gnunetd]
5HOSTKEY = $SERVICEHOME/.hostkey
6
7[hostlist]
8SERVERS = http://localhost:31000/
9OPTIONS = -b
10
11[resolver]
12PORT = 33001
13HOSTNAME = localhost
14UNIXPATH = /tmp/gnunet-chat-p3-service-resolver.sock
15
16[transport]
17PORT = 33002
18UNIXPATH = /tmp/gnunet-chat-p3-service-transport.sock
19PLUGINS = tcp
20#BINARY = /home/grothoff/bin/gnunet-service-transport
21#PREFIX = valgrind
22
23[transport-tcp]
24PORT = 33003
25
26[arm]
27PORT = 33004
28UNIXPATH = /tmp/gnunet-chat-p3-service-arm.sock
29HOSTNAME = localhost
30DEFAULTSERVICES = resolver transport core topology hostlist statistics chat
31
32[core]
33PORT = 33005
34UNIXPATH = /tmp/gnunet-chat-p3-service-core.sock
35HOSTNAME = localhost
36
37[topology]
38MINIMUM-FRIENDS = 0
39FRIENDS-ONLY = NO
40AUTOCONNECT = YES
41TARGET-CONNECTION-COUNT = 16
42FRIENDS = $SERVICEHOME/friends
43BINARY = gnunet-daemon-topology
44
45[peerinfo]
46PORT = 33006
47UNIXPATH = /tmp/gnunet-chat-p3-service-peerinfo.sock
48HOSTNAME = localhost
49
50[statistics]
51PORT = 33007
52HOSTNAME = localhost
53UNIXPATH = /tmp/gnunet-chat-p3-service-statistics.sock
54
55[chat]
56PORT = 33008
57HOSTNAME = localhost
58HOME = $SERVICEHOME
59BINARY = gnunet-service-chat
60
61[testing]
62WEAKRANDOM = YES
63
64[fs]
65AUTOSTART = NO
66
67[datastore]
68AUTOSTART = NO
69
70[dht]
71AUTOSTART = NO
72
73[mesh]
74AUTOSTART = NO
75
76[nat]
77DISABLEV6 = YES
78BINDTO = 127.0.0.1
79ENABLE_UPNP = NO
80BEHIND_NAT = NO
81ALLOW_NAT = NO
82INTERNAL_ADDRESS = 127.0.0.1
83EXTERNAL_ADDRESS = 127.0.0.1
84
85[dns]
86AUTOSTART = NO
87
88
89
90[nse]
91AUTOSTART = NO
92
93
diff --git a/src/chat/test_chat_private.c b/src/chat/test_chat_private.c
deleted file mode 100644
index b911d091f..000000000
--- a/src/chat/test_chat_private.c
+++ /dev/null
@@ -1,640 +0,0 @@
1/*
2 This file is part of GNUnet.
3 (C) 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/test_chat_private.c
23 * @brief testcase for private chatting
24 * @author Vitaly Minko
25 */
26
27#include "platform.h"
28#include "gnunet_crypto_lib.h"
29#include "gnunet_util_lib.h"
30#include "gnunet_arm_service.h"
31#include "gnunet_chat_service.h"
32
33#define VERBOSE GNUNET_NO
34
35/**
36 * How long until we give up on passing the test?
37 */
38#define KILL_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 60)
39
40/**
41 * How long until we give up on receiving somebody else's private message?
42 */
43#define PM_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5)
44
45struct PeerContext
46{
47 struct GNUNET_CONFIGURATION_Handle *cfg;
48 struct GNUNET_OS_Process *arm_proc;
49};
50
51struct Wanted
52{
53 struct GNUNET_CONTAINER_MetaData *meta;
54
55 struct GNUNET_HashCode *sender;
56
57 /**
58 * Alternative meta/sender is used when we expect join/leave notification
59 * from two peers and don't know which one will come first.
60 */
61 struct GNUNET_CONTAINER_MetaData *meta2;
62
63 struct GNUNET_HashCode *sender2;
64
65 char *msg;
66
67 const char *me;
68
69 enum GNUNET_CHAT_MsgOptions opt;
70
71 struct GNUNET_TIME_Absolute timestamp;
72
73 GNUNET_SCHEDULER_Task next_task;
74
75 void *next_task_cls;
76
77};
78
79static struct PeerContext p1;
80
81static struct PeerContext p2;
82
83static struct PeerContext p3;
84
85static struct GNUNET_HashCode alice;
86
87static struct GNUNET_HashCode bob;
88
89static struct GNUNET_HashCode carol;
90
91static struct GNUNET_CHAT_Room *alice_room;
92
93static struct GNUNET_CHAT_Room *bob_room;
94
95static struct GNUNET_CHAT_Room *carol_room;
96
97static struct GNUNET_CONTAINER_MetaData *alice_meta;
98
99static struct GNUNET_CONTAINER_MetaData *bob_meta;
100
101static struct GNUNET_CONTAINER_MetaData *carol_meta;
102
103static struct Wanted alice_wanted;
104
105static struct Wanted bob_wanted;
106
107static struct Wanted carol_wanted;
108
109static GNUNET_SCHEDULER_TaskIdentifier kill_task;
110
111static GNUNET_SCHEDULER_TaskIdentifier finish_task;
112
113static GNUNET_SCHEDULER_TaskIdentifier wait_task;
114
115static int err;
116
117static int alice_ready;
118
119static int bob_ready;
120
121static int is_p2p;
122
123static struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *bob_public_key;
124
125
126static void
127setup_peer (struct PeerContext *p, const char *cfgname)
128{
129 char *binary;
130
131 binary = GNUNET_OS_get_libexec_binary_path ("gnunet-service-arm");
132 p->cfg = GNUNET_CONFIGURATION_create ();
133 p->arm_proc =
134 GNUNET_OS_start_process (GNUNET_YES, GNUNET_OS_INHERIT_STD_OUT_AND_ERR, NULL, NULL, binary,
135 "gnunet-service-arm",
136 "-c", cfgname, NULL);
137 GNUNET_assert (GNUNET_OK == GNUNET_CONFIGURATION_load (p->cfg, cfgname));
138 GNUNET_free (binary);
139}
140
141
142static void
143stop_arm (struct PeerContext *p)
144{
145 if (0 != GNUNET_OS_process_kill (p->arm_proc, SIGTERM))
146 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");
147 if (GNUNET_OS_process_wait (p->arm_proc) != GNUNET_OK)
148 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "waitpid");
149 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "ARM process %u stopped\n",
150 GNUNET_OS_process_get_pid (p->arm_proc));
151 GNUNET_OS_process_destroy (p->arm_proc);
152 p->arm_proc = NULL;
153 GNUNET_CONFIGURATION_destroy (p->cfg);
154}
155
156
157static void
158abort_test (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
159{
160 if (alice_room != NULL)
161 {
162 GNUNET_CHAT_leave_room (alice_room);
163 alice_room = NULL;
164 }
165 if (bob_room != NULL)
166 {
167 GNUNET_CHAT_leave_room (bob_room);
168 bob_room = NULL;
169 }
170 if (carol_room != NULL)
171 {
172 GNUNET_CHAT_leave_room (carol_room);
173 carol_room = NULL;
174 }
175 err = 1;
176}
177
178
179static void
180timeout_kill (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
181{
182#if VERBOSE
183 printf ("Timed out, stopping the test.\n");
184#endif
185 kill_task = GNUNET_SCHEDULER_NO_TASK;
186 if (wait_task != GNUNET_SCHEDULER_NO_TASK)
187 {
188 GNUNET_SCHEDULER_cancel (wait_task);
189 wait_task = GNUNET_SCHEDULER_NO_TASK;
190 }
191 GNUNET_SCHEDULER_add_continuation (&abort_test, NULL,
192 GNUNET_SCHEDULER_REASON_PREREQ_DONE);
193}
194
195
196static int
197join_cb (void *cls)
198{
199 struct Wanted *want = cls;
200
201#if VERBOSE
202 printf ("%s has joined\n", want->me);
203#endif
204 if (NULL != want->next_task)
205 GNUNET_SCHEDULER_add_now (want->next_task, want->next_task_cls);
206 return GNUNET_OK;
207}
208
209
210static int
211member_list_cb (void *cls, const struct GNUNET_CONTAINER_MetaData *member_info,
212 const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *member_id,
213 enum GNUNET_CHAT_MsgOptions options)
214{
215 struct Wanted *want = cls;
216 struct GNUNET_HashCode sender;
217
218#if VERBOSE
219 printf ("%s - told that %s has %s\n", want->me,
220 member_info ==
221 NULL ? NULL : GNUNET_CONTAINER_meta_data_get_by_type (member_info,
222 EXTRACTOR_METATYPE_TITLE),
223 member_info == NULL ? "left" : "joined");
224#endif
225 GNUNET_CRYPTO_hash (member_id,
226 sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
227 &sender);
228 /* entertain both primary and an alternative sender/meta */
229 if (((0 == memcmp (&sender, want->sender, sizeof (struct GNUNET_HashCode))) ||
230 ((want->sender2 != NULL) &&
231 (0 == memcmp (&sender, want->sender2, sizeof (struct GNUNET_HashCode))))) &&
232 (((member_info == NULL) && (want->meta == NULL)) ||
233 ((member_info != NULL) &&
234 (((want->meta != NULL) &&
235 GNUNET_CONTAINER_meta_data_test_equal (member_info, want->meta)) ||
236 ((want->meta2 != NULL) &&
237 GNUNET_CONTAINER_meta_data_test_equal (member_info, want->meta2)))))
238 && (options == want->opt))
239 {
240 /* remember Bob's public key, we need it to send private message */
241 if (NULL == bob_public_key &&
242 (0 == memcmp (&bob, want->sender, sizeof (struct GNUNET_HashCode))))
243 bob_public_key =
244 GNUNET_memdup (member_id,
245 sizeof (struct
246 GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded));
247 if (want->sender2 != NULL)
248 {
249 /* flush alternative sender */
250 if (0 == memcmp (&sender, want->sender, sizeof (struct GNUNET_HashCode)))
251 {
252 want->sender = want->sender2;
253 want->meta = want->meta2;
254 }
255 want->sender2 = NULL;
256 want->meta2 = NULL;
257 }
258 else if (NULL != want->next_task)
259 GNUNET_SCHEDULER_add_now (want->next_task, want->next_task_cls);
260 }
261 else
262 {
263 GNUNET_SCHEDULER_cancel (kill_task);
264 kill_task = GNUNET_SCHEDULER_NO_TASK;
265 GNUNET_SCHEDULER_add_now (&abort_test, NULL);
266 }
267 return GNUNET_OK;
268}
269
270
271static int
272receive_cb (void *cls, struct GNUNET_CHAT_Room *room,
273 const struct GNUNET_HashCode * sender,
274 const struct GNUNET_CONTAINER_MetaData *meta, const char *message,
275 struct GNUNET_TIME_Absolute timestamp,
276 enum GNUNET_CHAT_MsgOptions options)
277{
278 struct Wanted *want = cls;
279
280#if VERBOSE
281 printf ("%s - told that %s said '%s'\n", want->me,
282 meta == NULL ? NULL : GNUNET_CONTAINER_meta_data_get_by_type (meta,
283 EXTRACTOR_METATYPE_TITLE),
284 message);
285#endif
286
287 if ((want->msg != NULL) && (0 == strcmp (message, want->msg)) &&
288 (((sender == NULL) && (want->sender == NULL)) ||
289 ((sender != NULL) && (want->sender != NULL) &&
290 (0 == memcmp (sender, want->sender, sizeof (struct GNUNET_HashCode))))) &&
291 (GNUNET_CONTAINER_meta_data_test_equal (meta, want->meta)) &&
292 (options == want->opt) &&
293 /* Not == since the library sets the actual timestamp, so it may be
294 * slightly greater
295 */
296 (timestamp.abs_value >= want->timestamp.abs_value))
297 {
298 if (NULL != want->next_task)
299 GNUNET_SCHEDULER_add_now (want->next_task, want->next_task_cls);
300 }
301 else
302 {
303 GNUNET_SCHEDULER_cancel (kill_task);
304 kill_task = GNUNET_SCHEDULER_NO_TASK;
305 GNUNET_SCHEDULER_cancel (finish_task);
306 finish_task = GNUNET_SCHEDULER_NO_TASK;
307 GNUNET_SCHEDULER_add_now (&abort_test, NULL);
308 }
309 return GNUNET_OK;
310}
311
312
313static void
314wait_until_all_ready (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
315{
316 GNUNET_SCHEDULER_Task task = cls;
317
318#if VERBOSE
319 printf ("Waiting...\n");
320#endif
321 if (alice_ready && bob_ready)
322 {
323 wait_task = GNUNET_SCHEDULER_NO_TASK;
324 GNUNET_SCHEDULER_add_now (task, NULL);
325 }
326 else
327 wait_task =
328 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
329 (GNUNET_TIME_UNIT_MILLISECONDS, 5000),
330 &wait_until_all_ready, task);
331}
332
333
334static void
335set_alice_ready (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
336{
337 alice_ready = GNUNET_YES;
338}
339
340
341static void
342set_bob_ready (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
343{
344 bob_ready = GNUNET_YES;
345}
346
347
348static void
349disconnect_alice (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
350{
351#if VERBOSE
352 printf ("Alice is leaving.\n");
353#endif
354 if (is_p2p)
355 stop_arm (&p2);
356 GNUNET_CHAT_leave_room (alice_room);
357 alice_room = NULL;
358 GNUNET_SCHEDULER_cancel (kill_task);
359 kill_task = GNUNET_SCHEDULER_NO_TASK;
360}
361
362
363static void
364disconnect_bob (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
365{
366#if VERBOSE
367 printf ("Bod is leaving.\n");
368#endif
369 if (is_p2p)
370 stop_arm (&p3);
371 alice_wanted.meta = NULL;
372 alice_wanted.sender = &bob;
373 alice_wanted.msg = NULL;
374 alice_wanted.opt = 0;
375 alice_wanted.next_task = &disconnect_alice;
376 alice_wanted.next_task_cls = NULL;
377 GNUNET_CHAT_leave_room (bob_room);
378 bob_room = NULL;
379}
380
381
382static void
383disconnect_carol (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
384{
385#if VERBOSE
386 printf ("Carol is leaving.\n");
387#endif
388 alice_wanted.meta = NULL;
389 alice_wanted.sender = &carol;
390 alice_wanted.msg = NULL;
391 alice_wanted.opt = 0;
392 alice_wanted.next_task = &set_alice_ready;
393 alice_wanted.next_task_cls = NULL;
394 alice_ready = GNUNET_NO;
395 bob_wanted.meta = NULL;
396 bob_wanted.sender = &carol;
397 bob_wanted.msg = NULL;
398 bob_wanted.opt = 0;
399 bob_wanted.next_task = &wait_until_all_ready;
400 bob_wanted.next_task_cls = &disconnect_bob;
401 bob_ready = GNUNET_YES;
402 GNUNET_CHAT_leave_room (carol_room);
403 carol_room = NULL;
404}
405
406
407static void
408send_from_alice_to_bob (void *cls,
409 const struct GNUNET_SCHEDULER_TaskContext *tc)
410{
411 uint32_t seq;
412
413#if VERBOSE
414 printf ("Alice says 'Hi!' to Bob\n");
415#endif
416 alice_ready = GNUNET_YES;
417 bob_ready = GNUNET_NO;
418 bob_wanted.meta = alice_meta;
419 bob_wanted.sender = &alice;
420 bob_wanted.msg = "Hi Bob!";
421 bob_wanted.opt = GNUNET_CHAT_MSG_PRIVATE;
422 bob_wanted.next_task = &set_bob_ready;
423 bob_wanted.next_task_cls = NULL;
424 /* Carol should not receive this message */
425 carol_wanted.meta = NULL;
426 carol_wanted.sender = NULL;
427 carol_wanted.msg = NULL;
428 carol_wanted.opt = 0;
429 carol_wanted.next_task = NULL;
430 carol_wanted.next_task_cls = NULL;
431 GNUNET_CHAT_send_message (alice_room, "Hi Bob!", GNUNET_CHAT_MSG_PRIVATE,
432 bob_public_key, &seq);
433 finish_task =
434 GNUNET_SCHEDULER_add_delayed (PM_TIMEOUT, &wait_until_all_ready,
435 &disconnect_carol);
436}
437
438
439static void
440prepare_bob_for_alice_task (void *cls,
441 const struct GNUNET_SCHEDULER_TaskContext *tc)
442{
443 bob_wanted.meta = alice_meta;
444 bob_wanted.sender = &alice;
445 bob_wanted.msg = NULL;
446 bob_wanted.opt = -1;
447 bob_wanted.next_task = &set_bob_ready;
448 bob_wanted.next_task_cls = NULL;
449}
450
451
452static void
453prepare_carol_for_alice_and_bob_task (void *cls,
454 const struct GNUNET_SCHEDULER_TaskContext
455 *tc)
456{
457 carol_wanted.meta = alice_meta;
458 carol_wanted.sender = &alice;
459 /* set alternative meta/sender since we don't know from which peer
460 * notification will come first */
461 carol_wanted.meta2 = bob_meta;
462 carol_wanted.sender2 = &bob;
463 carol_wanted.msg = NULL;
464 carol_wanted.opt = -1;
465 carol_wanted.next_task = &wait_until_all_ready;
466 carol_wanted.next_task_cls = &send_from_alice_to_bob;
467}
468
469
470static void
471join_carol_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
472{
473#if VERBOSE
474 printf ("Carol joining\n");
475#endif
476 alice_wanted.meta = carol_meta;
477 alice_wanted.sender = &carol;
478 alice_wanted.msg = NULL;
479 alice_wanted.opt = -1;
480 alice_wanted.next_task = &set_alice_ready;
481 alice_wanted.next_task_cls = NULL;
482 alice_ready = GNUNET_NO;
483 bob_wanted.meta = carol_meta;
484 bob_wanted.sender = &carol;
485 bob_wanted.msg = NULL;
486 bob_wanted.opt = -1;
487 bob_wanted.next_task = &set_bob_ready;
488 bob_wanted.next_task_cls = NULL;
489 bob_ready = GNUNET_NO;
490 carol_wanted.next_task = &prepare_carol_for_alice_and_bob_task;
491 carol_wanted.next_task_cls = NULL;
492 carol_room =
493 GNUNET_CHAT_join_room (is_p2p ? p3.cfg : p1.cfg, "carol", carol_meta,
494 "test", -1, &join_cb, &carol_wanted, &receive_cb,
495 &carol_wanted, &member_list_cb, &carol_wanted,
496 NULL, NULL, &carol);
497 if (NULL == carol_room)
498 {
499 GNUNET_SCHEDULER_cancel (kill_task);
500 kill_task = GNUNET_SCHEDULER_NO_TASK;
501 GNUNET_CHAT_leave_room (alice_room);
502 alice_room = NULL;
503 GNUNET_CHAT_leave_room (bob_room);
504 bob_room = NULL;
505 err = 1;
506 }
507}
508
509
510static void
511join_bob_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
512{
513#if VERBOSE
514 printf ("Bob joining\n");
515#endif
516 alice_wanted.meta = bob_meta;
517 alice_wanted.sender = &bob;
518 alice_wanted.msg = NULL;
519 alice_wanted.opt = -1;
520 alice_wanted.next_task = &wait_until_all_ready;
521 alice_wanted.next_task_cls = &join_carol_task;
522 alice_ready = GNUNET_YES;
523 bob_wanted.next_task = &prepare_bob_for_alice_task;
524 bob_wanted.next_task_cls = NULL;
525 bob_ready = GNUNET_NO;
526 bob_room =
527 GNUNET_CHAT_join_room (is_p2p ? p2.cfg : p1.cfg, "bob", bob_meta, "test",
528 -1, &join_cb, &bob_wanted, &receive_cb,
529 &bob_wanted, &member_list_cb, &bob_wanted, NULL,
530 NULL, &bob);
531 if (NULL == bob_room)
532 {
533 GNUNET_SCHEDULER_cancel (kill_task);
534 kill_task = GNUNET_SCHEDULER_NO_TASK;
535 GNUNET_CHAT_leave_room (alice_room);
536 alice_room = NULL;
537 err = 1;
538 }
539}
540
541
542static void
543join_alice_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
544{
545#if VERBOSE
546 printf ("Alice joining\n");
547#endif
548 alice_wanted.next_task = &join_bob_task;
549 alice_wanted.next_task_cls = NULL;
550 alice_room =
551 GNUNET_CHAT_join_room (p1.cfg, "alice", alice_meta, "test", -1, &join_cb,
552 &alice_wanted, &receive_cb, &alice_wanted,
553 &member_list_cb, &alice_wanted, NULL, NULL,
554 &alice);
555 if (NULL == alice_room)
556 {
557 GNUNET_SCHEDULER_cancel (kill_task);
558 kill_task = GNUNET_SCHEDULER_NO_TASK;
559 err = 1;
560 }
561}
562
563
564static void
565run (void *cls, char *const *args, const char *cfgfile,
566 const struct GNUNET_CONFIGURATION_Handle *cfg)
567{
568 if (is_p2p)
569 {
570 setup_peer (&p1, "test_chat_peer1.conf");
571 setup_peer (&p2, "test_chat_peer2.conf");
572 setup_peer (&p3, "test_chat_peer3.conf");
573 }
574 else
575 setup_peer (&p1, "test_chat_data.conf");
576
577 memset (&alice_wanted, 0, sizeof (struct Wanted));
578 memset (&bob_wanted, 0, sizeof (struct Wanted));
579 memset (&carol_wanted, 0, sizeof (struct Wanted));
580 alice_wanted.me = "Alice";
581 bob_wanted.me = "Bob";
582 carol_wanted.me = "Carol";
583 alice_meta = GNUNET_CONTAINER_meta_data_create ();
584 GNUNET_CONTAINER_meta_data_insert (alice_meta, "<gnunet>",
585 EXTRACTOR_METATYPE_TITLE,
586 EXTRACTOR_METAFORMAT_UTF8, "text/plain",
587 "Alice", strlen ("Alice") + 1);
588 bob_meta = GNUNET_CONTAINER_meta_data_create ();
589 GNUNET_CONTAINER_meta_data_insert (bob_meta, "<gnunet>",
590 EXTRACTOR_METATYPE_TITLE,
591 EXTRACTOR_METAFORMAT_UTF8, "text/plain",
592 "Bob", strlen ("Bob") + 1);
593 carol_meta = GNUNET_CONTAINER_meta_data_create ();
594 GNUNET_CONTAINER_meta_data_insert (carol_meta, "<gnunet>",
595 EXTRACTOR_METATYPE_TITLE,
596 EXTRACTOR_METAFORMAT_UTF8, "text/plain",
597 "Carol", strlen ("Carol") + 1);
598 kill_task = GNUNET_SCHEDULER_add_delayed (KILL_TIMEOUT, &timeout_kill, NULL);
599 GNUNET_SCHEDULER_add_now (&join_alice_task, NULL);
600}
601
602
603int
604main (int argc, char *argv[])
605{
606 char *const argvx[] = {
607 "test-chat",
608 "-c",
609 "test_chat_data.conf",
610 NULL
611 };
612 struct GNUNET_GETOPT_CommandLineOption options[] = {
613 GNUNET_GETOPT_OPTION_END
614 };
615
616 GNUNET_log_setup ("test_chat",
617 "WARNING",
618 NULL);
619 if (strstr (argv[0], "p2p") != NULL)
620 {
621 is_p2p = GNUNET_YES;
622 }
623 GNUNET_PROGRAM_run ((sizeof (argvx) / sizeof (char *)) - 1, argvx,
624 "test-chat", "nohelp", options, &run, NULL);
625 stop_arm (&p1);
626 GNUNET_CONTAINER_meta_data_destroy (alice_meta);
627 GNUNET_CONTAINER_meta_data_destroy (bob_meta);
628 GNUNET_CONTAINER_meta_data_destroy (carol_meta);
629 if (is_p2p)
630 {
631 GNUNET_DISK_directory_remove ("/tmp/gnunet-test-chat-peer-1/");
632 GNUNET_DISK_directory_remove ("/tmp/gnunet-test-chat-peer-2/");
633 GNUNET_DISK_directory_remove ("/tmp/gnunet-test-chat-peer-3/");
634 }
635 else
636 GNUNET_DISK_directory_remove ("/tmp/gnunet-test-chat/");
637 return err;
638}
639
640/* end of test_chat_private.c */