aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--configure.ac2
-rw-r--r--src/Makefile.am1
-rw-r--r--src/conversation/Makefile.am91
-rw-r--r--src/conversation/conversation.conf.in7
-rw-r--r--src/conversation/conversation_api.c751
-rw-r--r--src/conversation/gnunet-conversation.c419
-rw-r--r--src/conversation/gnunet-helper-audio-playback.c391
-rw-r--r--src/conversation/gnunet-helper-audio-record.c446
-rw-r--r--src/conversation/gnunet-service-conversation.c1786
-rw-r--r--src/conversation/gnunet_conversation.h163
-rw-r--r--src/conversation/gnunet_protocols_conversation.h300
-rw-r--r--src/conversation/mst.c288
-rw-r--r--src/conversation/test_voip.api.c85
13 files changed, 4730 insertions, 0 deletions
diff --git a/configure.ac b/configure.ac
index 471be3e29..c2d9bbcb5 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1296,6 +1296,8 @@ src/core/Makefile
1296src/core/core.conf 1296src/core/core.conf
1297src/consensus/Makefile 1297src/consensus/Makefile
1298src/consensus/consensus.conf 1298src/consensus/consensus.conf
1299src/conversation/Makefile
1300src/conversation/conversation.conf
1299src/datacache/Makefile 1301src/datacache/Makefile
1300src/datastore/Makefile 1302src/datastore/Makefile
1301src/datastore/datastore.conf 1303src/datastore/datastore.conf
diff --git a/src/Makefile.am b/src/Makefile.am
index c724e3ed0..bd039325a 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -16,6 +16,7 @@ if HAVE_EXPERIMENTAL
16 multicast \ 16 multicast \
17 env \ 17 env \
18 psyc \ 18 psyc \
19 conversation \
19 $(CONSENSUS) \ 20 $(CONSENSUS) \
20 $(EXPERIMENTATION) 21 $(EXPERIMENTATION)
21 # NOTE: scalarproduct is not being listed here yet as the crypto is being reworked at the moment 22 # NOTE: scalarproduct is not being listed here yet as the crypto is being reworked at the moment
diff --git a/src/conversation/Makefile.am b/src/conversation/Makefile.am
new file mode 100644
index 000000000..8e1d4b5ec
--- /dev/null
+++ b/src/conversation/Makefile.am
@@ -0,0 +1,91 @@
1SUBDIRS = .
2
3INCLUDES = \
4 -I$(top_srcdir)/src/include \
5 -I$(top_srcdir)
6
7AM_CPPFLAGS = \
8 $(GNUNET_CPPFLAGS)
9
10# Set this variable if you are using GNUNET libraries for all programs and
11# libraries. You don't then need to target-specific _LDFLAGS with GNUNET_LDFLAGS
12# AM_LDFLAGS = \
13# $(GNUNET_LDFLAGS) \
14# $(WINFLAGS) \
15# -export-dynamic
16
17lib_LTLIBRARIES = libgnunetconversation.la
18
19pkgcfgdir= $(prefix)/share/gnunet/config.d/
20
21libexecdir= $(prefix)/lib/gnunet/libexec/
22
23libgnunetconversation_la_SOURCES = \
24 conversation_api.c
25libgnunetconversation_la_LIBADD = \
26 -lgnunetutil -lgnunetgns_common -lgnunetgns
27libgnunetconversation_la_LDFLAGS = \
28 $(GNUNET_LDFLAGS) $(WINFLAGS) \
29 -version-info 0:0:0
30
31
32bin_PROGRAMS = gnunet-conversation
33
34libexec_PROGRAMS = gnunet-service-conversation \
35 gnunet-helper-audio-record \
36 gnunet-helper-audio-playback
37
38check_PROGRAMS = \
39 test_conversation_api
40
41TESTS = $(check_PROGRAMS)
42
43
44gnunet_helper_audio_record_SOURCES = \
45 gnunet-helper-audio-record.c
46gnunet_helper_audio_record_LDADD = \
47 -lgnunetutil \
48 -lpulse -lopus\
49 $(INTLLIBS)
50gnunet_helper_audio_record_LDFLAGS = \
51 $(GNUNET_LDFLAGS) $(WINFLAGS) -export-dynamic
52
53gnunet_helper_audio_playback_SOURCES = \
54 gnunet-helper-audio-playback.c
55gnunet_helper_audio_playback_LDADD = \
56 -lgnunetutil \
57 -lpulse -lopus\
58 $(INTLLIBS)
59gnunet_helper_audio_playback_LDFLAGS = \
60 $(GNUNET_LDFLAGS) $(WINFLAGS) -export-dynamic
61
62gnunet_service_conversation_SOURCES = \
63 gnunet-service-conversation.c
64gnunet_service_conversation_LDADD = \
65 -lgnunetutil -lgnunetmesh -lgnunetgns_common -lgnunetgns\
66 $(INTLLIBS)
67gnunet_service_conversation_LDFLAGS = \
68 $(GNUNET_LDFLAGS) $(WINFLAGS) -export-dynamic
69
70gnunet_conversation_SOURCES = \
71 gnunet-conversation.c
72gnunet_conversation_LDADD = \
73 -lgnunetutil -lgnunetconversation \
74 $(INTLLIBS)
75gnunet_conversation_LDFLAGS = \
76 $(GNUNET_LDFLAGS) $(WINFLAGS) -export-dynamic
77
78
79
80
81
82test_conversation_api_SOURCES = \
83 test_conversation_api.c
84test_conversation_api_LDADD = \
85 $(top_builddir)/src/conversation/libgnunetconversation.la \
86 -lgnunetutil
87test_conversation_api_LDFLAGS = \
88 $(GNUNET_LDFLAGS) $(WINFLAGS) -export-dynamic
89
90 pkgcfg_DATA = conversation.conf
91
diff --git a/src/conversation/conversation.conf.in b/src/conversation/conversation.conf.in
new file mode 100644
index 000000000..82c28e8eb
--- /dev/null
+++ b/src/conversation/conversation.conf.in
@@ -0,0 +1,7 @@
1[conversation]
2BINARY = gnunet-service-conversation
3UNIXPATH = /tmp/gnunet-service-conversation.sock
4HOME = $SERVICEHOME
5# PORT = 2106
6# @UNIXONLY@ PORT = 2087
7
diff --git a/src/conversation/conversation_api.c b/src/conversation/conversation_api.c
new file mode 100644
index 000000000..cf06e541a
--- /dev/null
+++ b/src/conversation/conversation_api.c
@@ -0,0 +1,751 @@
1/*
2 This file is part of GNUnet.
3 (C)
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 conversation/conversation_api.c
23 * @brief API for conversation
24 * @author Simon Dieterle
25 * @author Andreas Fuchs
26 * STRUCTURE:
27 * - DATA STRUCTURES
28 * - DECLARATIONS
29 * - AUXILIARY FUNCTIONS
30 * - RECEIVE HANDLERS
31 * - SEND FUNCTIONS
32 * - API CALL DEFINITIONS
33 *
34 */
35
36#include <gnunet/platform.h>
37#include <gnunet/gnunet_util_lib.h>
38#include <gnunet/gnunet_gns_service.h>
39#include "gnunet_protocols_conversation.h"
40#include "gnunet_conversation_service.h"
41
42#define MAX_TRANSMIT_DELAY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 60)
43
44/******************************************************************************/
45/************************ DATA STRUCTURES ****************************/
46/******************************************************************************/
47
48enum GNUNET_CONVERSATION_CallType
49{
50 CALLER = 0,
51 CALLEE
52};
53
54/**
55* Information about a call
56*/
57struct GNUNET_CONVERSATION_CallInformation
58{
59
60 /**
61 * Peer interacting with
62 */
63 struct GNUNET_PeerIdentity peer;
64
65 /**
66 * Type of call (incoming or outgoing)
67 */
68 int type;
69
70 /**
71 * Shows if the call ist fully established
72 */
73 int established;
74};
75
76/**
77 * Opaque handle to the service.
78 */
79struct GNUNET_CONVERSATION_Handle
80{
81
82 /**
83 * Our configuration.
84 */
85 const struct GNUNET_CONFIGURATION_Handle *cfg;
86
87 /**
88 * Handle to the server connection, to send messages later
89 */
90 struct GNUNET_CLIENT_Connection *client;
91
92 /**
93 * GNS handle
94 */
95 struct GNUNET_GNS_Handle *gns;
96
97 /**
98 * Namestore handle
99 */
100 struct GNUNET_NAMESTORE_Handle *namestore;
101
102 /**
103 * TXT record for gns
104 */
105 int txt_record_set;
106
107 /**
108 * Callback for incoming calls
109 */
110 GNUNET_CONVERSATION_CallHandler *call_handler;
111
112 /**
113 * Callback for rejected calls
114 */
115 GNUNET_CONVERSATION_RejectHandler *reject_handler;
116
117 /**
118 * Callback for notifications
119 */
120 GNUNET_CONVERSATION_NotificationHandler *notification_handler;
121
122 /**
123 * Callback for missed calls
124 */
125 GNUNET_CONVERSATION_MissedCallHandler *missed_call_handler;
126
127 /**
128 * The pointer to the call
129 */
130 struct GNUNET_CONVERSATION_CallInformation *call;
131};
132
133/******************************************************************************/
134/*********************** AUXILIARY FUNCTIONS *************************/
135/******************************************************************************/
136
137/**
138* Initialize the conversation txt record in GNS
139*/
140static void
141setup_gns_txt (struct GNUNET_CONVERSATION_Handle *handle)
142{
143 struct GNUNET_CRYPTO_EccPublicKey zone_pkey;
144 struct GNUNET_CRYPTO_EccPrivateKey *zone_key;
145 struct GNUNET_CRYPTO_EccPublicKey peer_pkey;
146 struct GNUNET_CRYPTO_EccPrivateKey *peer_key;
147 struct GNUNET_NAMESTORE_RecordData rd;
148 struct GNUNET_HashCode hash;
149 struct GNUNET_PeerIdentity peer;
150
151 char *zone_keyfile;
152 char *peer_keyfile;
153
154 rd.expiration_time = UINT64_MAX;
155
156 if (GNUNET_OK !=
157 GNUNET_CONFIGURATION_get_value_filename (handle->cfg, "gns", "ZONEKEY",
158 &zone_keyfile))
159 {
160 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to get key from cfg\n");
161 return;
162 }
163
164 if (GNUNET_OK !=
165 GNUNET_CONFIGURATION_get_value_filename (handle->cfg, "PEER",
166 "PRIVATE_KEY", &peer_keyfile))
167 {
168 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to get key from cfg\n");
169 return;
170 }
171
172 zone_key = GNUNET_CRYPTO_ecc_key_create_from_file (zone_keyfile);
173 GNUNET_CRYPTO_ecc_key_get_public (zone_key, &zone_pkey);
174 peer_key = GNUNET_CRYPTO_ecc_key_create_from_file (peer_keyfile);
175 GNUNET_CRYPTO_ecc_key_get_public (peer_key, &peer_pkey);
176
177 GNUNET_CRYPTO_hash (&peer_pkey, sizeof (peer_pkey), &hash);
178
179 peer.hashPubKey = hash;
180 const char *h = GNUNET_i2s_full (&peer);
181
182 rd.data_size = strlen (h) + 1;
183 rd.data = h;
184 rd.record_type = GNUNET_GNS_RECORD_TXT;
185 rd.flags = GNUNET_NAMESTORE_RF_AUTHORITY;
186
187 GNUNET_NAMESTORE_record_put_by_authority (handle->namestore, zone_key,
188 "conversation", 1, &rd, NULL, NULL);
189}
190
191/**
192* Callback for checking the conversation txt gns record
193*
194* @param cls closure
195* @param rd_count
196* @param rd
197*/
198static void
199check_gns_cb (void *cls, uint32_t rd_count,
200 const struct GNUNET_NAMESTORE_RecordData *rd)
201{
202 struct GNUNET_CONVERSATION_Handle *h = (struct GNUNET_CONVERSATION_Handle *) cls;
203
204 if (0 == rd_count)
205 {
206 setup_gns_txt (h);
207 }
208 else
209 {
210 h->txt_record_set = GNUNET_YES;
211 }
212
213 return;
214}
215
216/**
217* Check if the gns txt record for conversation exits
218*/
219static void
220check_gns (struct GNUNET_CONVERSATION_Handle *h)
221{
222 GNUNET_GNS_lookup (h->gns, "conversation.gads", GNUNET_GNS_RECORD_TXT,
223 GNUNET_NO, NULL, &check_gns_cb, (void *) h);
224
225 return;
226}
227
228/******************************************************************************/
229/*********************** RECEIVE HANDLERS ****************************/
230/******************************************************************************/
231
232/**
233 * Function to process all messages received from the service
234 *
235 * @param cls closure
236 * @param msg message received, NULL on timeout or fatal error
237 */
238static void
239receive_message_cb (void *cls, const struct GNUNET_MessageHeader *msg)
240{
241 struct ServerClientAvailableAnswerMessage *avbmsg;
242 struct ServerClientSessionInitiateMessage *imsg;
243 struct ServerClientSessionRejectMessage *rmsg;
244 struct GNUNET_CONVERSATION_MissedCallNotification *missed_calls;
245 struct GNUNET_CONVERSATION_Handle *h = (struct GNUNET_CONVERSATION_Handle *) cls;
246
247 if (NULL != msg)
248 {
249 switch (ntohs (msg->type))
250 {
251 case GNUNET_MESSAGE_TYPE_CONVERSATION_SC_SESSION_ACCEPT:
252 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
253 _("%s has accepted your call.\n"),
254 GNUNET_i2s_full (&(h->call->peer)));
255
256 h->notification_handler (NULL, h, NotificationType_CALL_ACCEPTED,
257 &(h->call->peer));
258 h->call->type = CALLEE;
259
260 break;
261
262 case GNUNET_MESSAGE_TYPE_CONVERSATION_SC_SESSION_REJECT:
263 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
264 _("%s has rejected your call.\n"),
265 GNUNET_i2s_full (&(h->call->peer)));
266
267 rmsg = (struct ServerClientSessionRejectMessage *) msg;
268 h->reject_handler (NULL, h, ntohs (rmsg->reason), &(h->call->peer));
269 GNUNET_free (h->call);
270 h->call = NULL;
271
272 break;
273
274 case GNUNET_MESSAGE_TYPE_CONVERSATION_SC_SESSION_TERMINATE:
275 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
276 _("%s has terminated the call.\n"),
277 GNUNET_i2s_full (&(h->call->peer)));
278
279 h->notification_handler (NULL, h, NotificationType_CALL_TERMINATED,
280 &(h->call->peer));
281 GNUNET_free (h->call);
282 h->call = NULL;
283
284 break;
285
286 case GNUNET_MESSAGE_TYPE_CONVERSATION_SC_SESSION_INITIATE:
287 imsg = (struct ServerClientSessionInitiateMessage *) msg;
288
289 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("%s wants to call you.\n"),
290 GNUNET_i2s_full (&(imsg->peer)));
291
292 h->call =
293 (struct GNUNET_CONVERSATION_CallInformation *)
294 GNUNET_malloc (sizeof (struct GNUNET_CONVERSATION_CallInformation));
295 memcpy (&(h->call->peer), &(imsg->peer),
296 sizeof (struct GNUNET_PeerIdentity));
297 h->call_handler (NULL, h, &(h->call->peer));
298 h->call->type = CALLEE;
299
300 break;
301
302 case GNUNET_MESSAGE_TYPE_CONVERSATION_SC_MISSED_CALL:
303 missed_calls =
304 (struct GNUNET_CONVERSATION_MissedCallNotification *) (msg +
305 (sizeof
306 (struct
307 GNUNET_MessageHeader)));
308 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
309 _("You &d have missed a calls.\n"),
310 missed_calls->number);
311 h->missed_call_handler (NULL, h, missed_calls);
312 break;
313
314 case GNUNET_MESSAGE_TYPE_CONVERSATION_SC_SERVICE_BLOCKED:
315 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("The service is blocked.\n"));
316 h->notification_handler (NULL, h, NotificationType_SERVICE_BLOCKED,
317 NULL);
318 break;
319
320 case GNUNET_MESSAGE_TYPE_CONVERSATION_SC_PEER_NOT_CONNECTED:
321 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
322 _("The peer you are calling is not connected.\n"));
323 h->notification_handler (NULL, h, NotificationType_NO_PEER, NULL);
324 break;
325
326 case GNUNET_MESSAGE_TYPE_CONVERSATION_SC_NO_ANSWER:
327 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
328 _("The peer you are calling does not answer.\n"));
329 h->notification_handler (NULL, h, NotificationType_NO_ANSWER,
330 &(h->call->peer));
331 break;
332
333 case GNUNET_MESSAGE_TYPE_CONVERSATION_SC_ERROR:
334 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Generic error occured.\n"));
335 break;
336
337 default:
338 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
339 _("Got unknown message type.\n"));
340 break;
341 }
342
343 }
344
345 GNUNET_CLIENT_receive (h->client, &receive_message_cb, h,
346 GNUNET_TIME_UNIT_FOREVER_REL);
347}
348
349/******************************************************************************/
350/************************ SEND FUNCTIONS ****************************/
351/******************************************************************************/
352
353/**
354 * Function called to send a session initiate message to the service.
355 * "buf" will be NULL and "size" zero if the socket was closed for writing in
356 * the meantime.
357 *
358 * @param cls closure, NULL
359 * @param size number of bytes available in buf
360 * @param buf where the callee should write the initiate message
361 * @return number of bytes written to buf
362 */
363static size_t
364transmit_session_initiate_message (void *cls, size_t size, void *buf)
365{
366 size_t msg_size;
367 struct ClientServerSessionInitiateMessage *msg;
368 struct GNUNET_CONVERSATION_Handle *h = (struct GNUNET_CONVERSATION_Handle *) cls;
369
370 msg_size = sizeof (struct ClientServerSessionInitiateMessage);
371
372 GNUNET_assert (size >= msg_size);
373 msg = buf;
374 msg->header.size = htons (msg_size);
375 msg->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_SESSION_INITIATE);
376 memcpy (&msg->peer, &(h->call->peer), sizeof (struct GNUNET_PeerIdentity));
377
378 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
379 _
380 ("Sending ClientServerSessionInitiateMessage to the service for peer: %s\n"),
381 GNUNET_i2s_full (&(h->call->peer)));
382
383 h->call->type = CALLER;
384
385 return msg_size;
386}
387
388/**
389 * Function called to send a session accept message to the service.
390 * "buf" will be NULL and "size" zero if the socket was closed for writing in
391 * the meantime.
392 *
393 * @param cls closure, NULL
394 * @param size number of bytes available in buf
395 * @param buf where the callee should write the accept message
396 * @return number of bytes written to buf
397 */
398static size_t
399transmit_session_accept_message (void *cls, size_t size, void *buf)
400{
401 size_t msg_size;
402 struct ClientServerSessionAcceptMessage *msg;
403 struct GNUNET_CONVERSATION_Handle *h = (struct GNUNET_CONVERSATION_Handle *) cls;
404
405 msg_size = sizeof (struct ClientServerSessionAcceptMessage);
406
407 GNUNET_assert (size >= msg_size);
408 msg = buf;
409 msg->header.size = htons (msg_size);
410 msg->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_SESSION_ACCEPT);
411
412 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
413 _
414 ("Sending ClienServerSessionAcceptMessage to the service for peer: %s\n"),
415 GNUNET_i2s_full (&(h->call->peer)));
416
417 h->call->established = GNUNET_YES;
418
419 return msg_size;
420}
421
422/**
423 * Function called to send a session reject message to the service.
424 * "buf" will be NULL and "size" zero if the socket was closed for writing in
425 * the meantime.
426 *
427 * @param cls closure, NULL
428 * @param size number of bytes available in buf
429 * @param buf where the callee should write the reject message
430 * @return number of bytes written to buf
431 */
432static size_t
433transmit_session_reject_message (void *cls, size_t size, void *buf)
434{
435 size_t msg_size;
436 struct ClientServerSessionRejectMessage *msg;
437 struct GNUNET_CONVERSATION_Handle *h = (struct GNUNET_CONVERSATION_Handle *) cls;
438
439 msg_size = sizeof (struct ClientServerSessionRejectMessage);
440
441 GNUNET_assert (size >= msg_size);
442 msg = buf;
443 msg->header.size = htons (msg_size);
444 msg->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_SESSION_REJECT);
445 msg->reason = htons (REJECT_REASON_NOT_WANTED);
446
447 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
448 _
449 ("Sending ClientServerSessionRejectMessage to the service for peer: %s\n"),
450 GNUNET_i2s_full (&(h->call->peer)));
451
452 GNUNET_free (h->call);
453 h->call = NULL;
454
455 return msg_size;
456}
457
458/**
459 * Function called to send a session terminate message to the service.
460 * "buf" will be NULL and "size" zero if the socket was closed for writing in
461 * the meantime.
462 *
463 * @param cls closure, NULL
464 * @param size number of bytes available in buf
465 * @param buf where the callee should write the terminate message
466 * @return number of bytes written to buf
467 */
468static size_t
469transmit_session_terminate_message (void *cls, size_t size, void *buf)
470{
471 size_t msg_size;
472 struct ClientServerSessionTerminateMessage *msg;
473 struct GNUNET_CONVERSATION_Handle *h = (struct GNUNET_CONVERSATION_Handle *) cls;
474
475 msg_size = sizeof (struct ClientServerSessionTerminateMessage);
476
477 GNUNET_assert (size >= msg_size);
478 msg = buf;
479 msg->header.size = htons (msg_size);
480 msg->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_SESSION_TERMINATE);
481
482 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
483 _
484 ("Sending ClientServerSessionTerminateMessage to the service for peer: %s\n"),
485 GNUNET_i2s_full (&(h->call->peer)));
486
487 GNUNET_free (h->call);
488 h->call = NULL;
489
490 return msg_size;
491}
492
493/**
494 * Auxiliary function to call a peer.
495 *
496 * @param h conversation handle
497 * @return
498 */
499static void
500initiate_call (struct GNUNET_CONVERSATION_Handle *h, struct GNUNET_PeerIdentity peer)
501{
502 h->call =
503 (struct GNUNET_CONVERSATION_CallInformation *)
504 GNUNET_malloc (sizeof (struct GNUNET_CONVERSATION_CallInformation));
505 memcpy (&(h->call->peer), &peer, sizeof (struct GNUNET_PeerIdentity));
506
507 GNUNET_CLIENT_notify_transmit_ready (h->client,
508 sizeof (struct
509 ClientServerSessionInitiateMessage),
510 MAX_TRANSMIT_DELAY, GNUNET_YES,
511 &transmit_session_initiate_message, h);
512
513 return;
514}
515
516/**
517 * Auxiliary function to accept a call.
518 *
519 * @param h conversation handle
520 */
521static void
522accept_call (struct GNUNET_CONVERSATION_Handle *h)
523{
524 GNUNET_CLIENT_notify_transmit_ready (h->client,
525 sizeof (struct
526 ClientServerSessionAcceptMessage),
527 MAX_TRANSMIT_DELAY, GNUNET_YES,
528 &transmit_session_accept_message, h);
529}
530
531/**
532 * Auxiliary function to reject a call.
533 *
534 * @param h conversation handle
535 */
536static void
537reject_call (struct GNUNET_CONVERSATION_Handle *h)
538{
539 GNUNET_CLIENT_notify_transmit_ready (h->client,
540 sizeof (struct
541 ClientServerSessionRejectMessage),
542 MAX_TRANSMIT_DELAY, GNUNET_YES,
543 &transmit_session_reject_message, h);
544}
545
546/**
547 * Auxiliary function to terminate a call.
548 *
549 * @param h conversation handle
550 */
551static void
552terminate_call (struct GNUNET_CONVERSATION_Handle *h)
553{
554 GNUNET_CLIENT_notify_transmit_ready (h->client,
555 sizeof (struct
556 ClientServerSessionTerminateMessage),
557 MAX_TRANSMIT_DELAY, GNUNET_YES,
558 &transmit_session_terminate_message,
559 h);
560}
561
562/**
563*
564*/
565static void
566gns_call_cb (void *cls, uint32_t rd_count,
567 const struct GNUNET_NAMESTORE_RecordData *rd)
568{
569 struct GNUNET_PeerIdentity peer;
570 char hash[104];
571 struct GNUNET_CONVERSATION_Handle *handle = (struct GNUNET_CONVERSATION_Handle *) cls;
572 int i = 0;
573
574 if (0 == rd_count)
575 {
576 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Lookup failed\n");
577 handle->notification_handler (NULL, handle, NotificationType_NO_PEER,
578 NULL);
579 }
580 else
581 {
582 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Lookup succeeded\n");
583
584 if (GNUNET_GNS_RECORD_TXT == rd[i].record_type)
585 {
586 memcpy (&hash, rd[i].data, 104);
587 GNUNET_CRYPTO_hash_from_string2 (hash, strlen (hash),
588 &(peer.hashPubKey));
589
590 initiate_call (handle, peer);
591 }
592 else
593 {
594 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "No resolution!\n");
595 handle->notification_handler (NULL, handle,
596 NotificationType_NO_PEER, NULL);
597 }
598 }
599 return;
600}
601
602/**
603* GNS lookup
604*/
605static void
606gns_lookup_and_call (struct GNUNET_CONVERSATION_Handle *h, const char *callee)
607{
608 char domain[GNUNET_DNSPARSER_MAX_NAME_LENGTH];
609 char *pos;
610
611 pos = domain;
612 strcpy (pos, "conversation");
613 pos += strlen ("conversation");
614 strcpy (pos, ".");
615 pos++;
616 strcpy (pos, callee);
617 pos += strlen (callee);
618
619 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Lookup for %s\n", domain);
620
621 GNUNET_GNS_lookup (h->gns, domain, GNUNET_GNS_RECORD_TXT, GNUNET_NO, NULL,
622 &gns_call_cb, h);
623
624 return;
625}
626
627/******************************************************************************/
628/********************** API CALL DEFINITIONS *************************/
629/******************************************************************************/
630
631struct GNUNET_CONVERSATION_Handle *
632GNUNET_CONVERSATION_connect (const struct GNUNET_CONFIGURATION_Handle *cfg, void *cls,
633 GNUNET_CONVERSATION_CallHandler * call_handler,
634 GNUNET_CONVERSATION_RejectHandler * reject_handler,
635 GNUNET_CONVERSATION_NotificationHandler * notification_handler,
636 GNUNET_CONVERSATION_MissedCallHandler * missed_call_handler)
637{
638 struct GNUNET_CONVERSATION_Handle *h;
639
640 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "GNUNET_CONVERSATION_connect()\n");
641 h = GNUNET_malloc (sizeof (struct GNUNET_CONVERSATION_Handle));
642
643 h->cfg = cfg;
644 h->call_handler = call_handler;
645 h->reject_handler = reject_handler;
646 h->notification_handler = notification_handler;
647 h->missed_call_handler = missed_call_handler;
648
649 if (NULL == (h->client = GNUNET_CLIENT_connect ("conversation", cfg)))
650 {
651 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Could not access CONVERSATION service\n");
652 GNUNET_break (0);
653 GNUNET_free (h);
654
655 return NULL;
656 }
657
658 if (NULL == (h->gns = GNUNET_GNS_connect (cfg)))
659 {
660 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Could not access GNS service\n");
661 GNUNET_break (0);
662 GNUNET_CLIENT_disconnect (h->client);
663 GNUNET_free (h);
664
665 return NULL;
666 }
667
668 if (NULL == (h->namestore = GNUNET_NAMESTORE_connect (cfg)))
669 {
670 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
671 "Could not access NAMESTORE service\n");
672 GNUNET_break (0);
673 GNUNET_CLIENT_disconnect (h->client);
674 GNUNET_GNS_disconnect (h->gns);
675 GNUNET_free (h);
676
677 return NULL;
678 }
679
680 check_gns (h);
681 GNUNET_CLIENT_receive (h->client, &receive_message_cb, h,
682 GNUNET_TIME_UNIT_FOREVER_REL);
683
684 return h;
685}
686
687void
688GNUNET_CONVERSATION_disconnect (struct GNUNET_CONVERSATION_Handle *handle)
689{
690 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "CONVERSATION DISCONNECT\n");
691
692 GNUNET_CLIENT_disconnect (handle->client);
693 GNUNET_GNS_disconnect (handle->gns);
694
695 GNUNET_free (handle);
696 handle = NULL;
697}
698
699void
700GNUNET_CONVERSATION_call (struct GNUNET_CONVERSATION_Handle *h, const char *callee,
701 int doGnsLookup)
702{
703 struct GNUNET_PeerIdentity peer;
704 if (NULL == h || NULL == h->client)
705 return;
706
707 if (GNUNET_YES == doGnsLookup)
708 {
709 gns_lookup_and_call (h, callee);
710 }
711 else
712 {
713 if (GNUNET_OK !=
714 GNUNET_CRYPTO_hash_from_string2 (callee, strlen (callee),
715 &(peer.hashPubKey)))
716 {
717 h->notification_handler (NULL, h, NotificationType_NO_PEER, NULL);
718 }
719
720 initiate_call (h, peer);
721 }
722}
723
724void
725GNUNET_CONVERSATION_hangup (struct GNUNET_CONVERSATION_Handle *h)
726{
727 if (NULL == h || NULL == h->client)
728 return;
729
730 terminate_call (h);
731}
732
733void
734GNUNET_CONVERSATION_accept (struct GNUNET_CONVERSATION_Handle *h)
735{
736 if (NULL == h || NULL == h->client)
737 return;
738
739 accept_call (h);
740}
741
742void
743GNUNET_CONVERSATION_reject (struct GNUNET_CONVERSATION_Handle *h)
744{
745 if (NULL == h || NULL == h->client)
746 return;
747
748 reject_call (h);
749}
750
751/* end of conversation_api.c */
diff --git a/src/conversation/gnunet-conversation.c b/src/conversation/gnunet-conversation.c
new file mode 100644
index 000000000..9570737f4
--- /dev/null
+++ b/src/conversation/gnunet-conversation.c
@@ -0,0 +1,419 @@
1/*
2 This file is part of GNUnet.
3 (C)
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, InGNUNET_SERVERc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file conversation/gnunet-conversation.c
23 * @brief conversation implementation
24 * @author Simon Dieterle
25 * @author Andreas Fuchs
26 */
27#include <gnunet/platform.h>
28#include <gnunet/gnunet_constants.h>
29#include <gnunet/gnunet_util_lib.h>
30#include "gnunet_conversation_service.h"
31#include <fcntl.h>
32
33#define MAX_MESSAGE_LENGTH (32 * 1024)
34
35/**
36* CONVERSATION handle
37*/
38struct GNUNET_CONVERSATION_Handle *conversation = NULL;
39
40/**
41* Task which handles the commands
42*/
43static GNUNET_SCHEDULER_TaskIdentifier handle_cmd_task;
44
45/**
46* Function declareation for executing a action
47*/
48typedef int (*ActionFunction) (const char *argumetns, const void *xtra);
49
50/**
51* Structure which defines a command
52*/
53struct VoipCommand
54{
55 const char *command;
56 ActionFunction Action;
57 const char *helptext;
58};
59
60/******************************************************************************/
61/*********************** DECLARATIONS *************************/
62/******************************************************************************/
63
64static int do_help (const char *args, const void *xtra);
65
66/******************************************************************************/
67/*********************** Functions *************************/
68/******************************************************************************/
69
70
71/**
72 * Method called whenever a call is incoming
73 *
74 * @param cls closure
75 * @param handle to the conversation session
76 * @param caller peer that calls you
77 */
78void
79call_handler (void *cls, struct GNUNET_CONVERSATION_Handle *handle,
80 const struct GNUNET_PeerIdentity *caller)
81{
82 FPRINTF (stdout, _("Incoming call from peer: %s\n"),
83 GNUNET_i2s_full (caller));
84}
85
86/**
87 * Method called whenever a call is rejected
88 *
89 * @param cls closure
90 * @param handle to the conversation session
91 * @param peer peer that rejected your call
92 */
93void
94reject_handler (void *cls, struct GNUNET_CONVERSATION_Handle *handle, int reason,
95 const struct GNUNET_PeerIdentity *peer)
96{
97 FPRINTF (stdout, _("Peer %s rejected your call. Reason: %d\n"),
98 GNUNET_i2s_full (peer), reason);
99}
100
101/**
102 * Method called whenever a notification is there
103 *
104 * @param cls closure
105 * @param handle to the conversation session
106 * @param type the type of the notification
107 * @param peer peer that the notification is about
108 */
109void
110notification_handler (void *cls, struct GNUNET_CONVERSATION_Handle *handle, int type,
111 const struct GNUNET_PeerIdentity *peer)
112{
113 switch (type)
114 {
115 case NotificationType_SERVICE_BLOCKED:
116 FPRINTF (stdout, _("The service is already in use. Try again later."));
117
118 break;
119
120 case NotificationType_NO_PEER:
121 FPRINTF (stdout, _("The Peer you were calling is no correct peer.\n"));
122
123 break;
124
125 case NotificationType_NO_ANSWER:
126 FPRINTF (stdout, _("Peer %s did not answer your call.\n"),
127 GNUNET_i2s_full (peer));
128
129 break;
130
131 case NotificationType_AVAILABLE_AGAIN:
132 FPRINTF (stdout, _("Peer %s is now available.\n"),
133 GNUNET_i2s_full (peer));
134
135 break;
136
137 case NotificationType_CALL_ACCEPTED:
138 FPRINTF (stdout, _("Peer %s has accepted your call.\n"),
139 GNUNET_i2s_full (peer));
140
141 break;
142
143 case NotificationType_CALL_TERMINATED:
144 FPRINTF (stdout, _("Peer %s has terminated the call.\n"),
145 GNUNET_i2s_full (peer));
146 break;
147 }
148
149}
150
151/**
152 * Method called whenever a notification for missed calls is there
153 *
154 * @param cls closure
155 * @param handle to the conversation session
156 * @param missed_calls a list of missed calls
157 */
158void
159missed_call_handler (void *cls, struct GNUNET_CONVERSATION_Handle *handle,
160 struct GNUNET_CONVERSATION_MissedCallNotification *missed_calls)
161{
162 FPRINTF (stdout, _("You have missed calls.\n"));
163}
164
165/**
166* Terminating the client
167*/
168static int
169do_quit (const char *args, const void *xtra)
170{
171 return GNUNET_SYSERR;
172}
173
174/**
175*
176*/
177static int
178do_unknown (const char *msg, const void *xtra)
179{
180 FPRINTF (stderr, _("Unknown command `%s'\n"), msg);
181 return GNUNET_OK;
182}
183
184/**
185* Initiating a new call
186*/
187static int
188do_call (const char *arg, const void *xtra)
189{
190 char *callee = GNUNET_strdup (arg);
191 FPRINTF (stdout, _("Initiating call to: %s\n"), callee);
192 GNUNET_CONVERSATION_call (conversation, callee, GNUNET_YES);
193
194 return GNUNET_OK;
195}
196
197/**
198* Initiating a new call
199*/
200static int
201do_call_peer (const char *arg, const void *xtra)
202{
203 char *callee = GNUNET_strdup (arg);
204 FPRINTF (stdout, _("Initiating call to: %s\n"), callee);
205 GNUNET_CONVERSATION_call (conversation, callee, GNUNET_NO);
206
207 return GNUNET_OK;
208}
209
210/**
211* Accepting an incoming call
212*/
213static int
214do_accept (const char *args, const void *xtra)
215{
216 FPRINTF (stdout, _("Accepting the call\n"));
217 GNUNET_CONVERSATION_accept (conversation);
218
219 return GNUNET_OK;
220}
221
222/**
223* Rejecting a call
224*/
225static int
226do_reject (const char *args, const void *xtra)
227{
228 FPRINTF (stdout, _("Rejecting the call\n"));
229 GNUNET_CONVERSATION_reject (conversation);
230
231 return GNUNET_OK;
232}
233
234/**
235* Terminating a call
236*/
237static int
238do_hang_up (const char *args, const void *xtra)
239{
240 FPRINTF (stdout, _("Terminating the call\n"));
241 GNUNET_CONVERSATION_hangup (conversation);
242
243 return GNUNET_OK;
244}
245
246/**
247 * List of supported commands.
248 */
249static struct VoipCommand commands[] = {
250 {"/call ", &do_call, gettext_noop ("Use `/call gads_record'")},
251 {"/callpeer ", &do_call_peer,
252 gettext_noop ("Use `/call private_key' to call a person")},
253 {"/accept", &do_accept,
254 gettext_noop ("Use `/accept' to accept an incoming call")},
255 {"/terminate", &do_hang_up,
256 gettext_noop ("Use `/terminate' to end a call")},
257 {"/reject", &do_reject,
258 gettext_noop ("Use `/rejet' to reject an incoming call")},
259 {"/quit", &do_quit, gettext_noop ("Use `/quit' to terminate gnunet-conversation")},
260 {"/help", &do_help,
261 gettext_noop ("Use `/help command' to get help for a specific command")},
262 {"/", &do_unknown, NULL},
263 {"", &do_unknown, NULL},
264 {NULL, NULL, NULL},
265};
266
267/**
268*
269*/
270static int
271do_help (const char *args, const void *xtra)
272{
273 int i;
274
275 i = 0;
276 while ((NULL != args) && (0 != strlen (args)) &&
277 (commands[i].Action != &do_help))
278 {
279 if (0 ==
280 strncasecmp (&args[1], &commands[i].command[1], strlen (args) - 1))
281 {
282 FPRINTF (stdout, "%s\n", gettext (commands[i].helptext));
283 return GNUNET_OK;
284 }
285 i++;
286 }
287 i = 0;
288 FPRINTF (stdout, "%s", "Available commands:");
289 while (commands[i].Action != &do_help)
290 {
291 FPRINTF (stdout, " %s", gettext (commands[i].command));
292 i++;
293 }
294 FPRINTF (stdout, "%s", "\n");
295 FPRINTF (stdout, "%s\n", gettext (commands[i].helptext));
296 return GNUNET_OK;
297}
298
299/**
300*
301*/
302static void
303do_stop_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
304{
305 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Running shutdown task\n");
306 GNUNET_CONVERSATION_disconnect (conversation);
307
308 if (handle_cmd_task != GNUNET_SCHEDULER_NO_TASK)
309 {
310 GNUNET_SCHEDULER_cancel (handle_cmd_task);
311 handle_cmd_task = GNUNET_SCHEDULER_NO_TASK;
312 }
313
314 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Running shutdown task finished\n");
315}
316
317/**
318*
319*/
320void
321handle_command (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
322{
323 char message[MAX_MESSAGE_LENGTH + 1];
324 int i;
325
326 /* read message from command line and handle it */
327 memset (message, 0, MAX_MESSAGE_LENGTH + 1);
328 if (NULL == fgets (message, MAX_MESSAGE_LENGTH, stdin))
329 goto next;
330 if (strlen (message) == 0)
331 goto next;
332 if (message[strlen (message) - 1] == '\n')
333 message[strlen (message) - 1] = '\0';
334 if (strlen (message) == 0)
335 goto next;
336 i = 0;
337 while ((NULL != commands[i].command) &&
338 (0 !=
339 strncasecmp (commands[i].command, message,
340 strlen (commands[i].command))))
341 i++;
342 if (GNUNET_OK !=
343 commands[i].Action (&message[strlen (commands[i].command)], NULL))
344 goto out;
345
346next:
347 handle_cmd_task =
348 GNUNET_SCHEDULER_add_delayed_with_priority (GNUNET_TIME_relative_multiply
349 (GNUNET_TIME_UNIT_MILLISECONDS,
350 100),
351 GNUNET_SCHEDULER_PRIORITY_UI,
352 &handle_command, NULL);
353 return;
354
355out:
356 handle_cmd_task = GNUNET_SCHEDULER_NO_TASK;
357 GNUNET_SCHEDULER_shutdown ();
358}
359
360/**
361 * Main function that will be run by the scheduler.
362 *
363 * @param cls closure
364 * @param args remaining command-line arguments
365 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
366 * @param c configuration
367 */
368static void
369run (void *cls, char *const *args, const char *cfgfile,
370 const struct GNUNET_CONFIGURATION_Handle *c)
371{
372 if (NULL ==
373 (conversation =
374 GNUNET_CONVERSATION_connect (c, NULL, &call_handler, &reject_handler,
375 &notification_handler, &missed_call_handler)))
376 {
377 FPRINTF (stderr, "%s", _("Could not access CONVERSATION service. Exiting.\n"));
378 return;
379 }
380
381 handle_cmd_task =
382 GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_UI,
383 &handle_command, NULL);
384 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &do_stop_task,
385 NULL);
386}
387
388/** * The main function to conversation.
389 *
390 * @param argc number of arguments from the command line
391 * @param argv command line arguments
392 * @return 0 ok, 1 on error
393 */
394int
395main (int argc, char *const *argv)
396{
397 static const struct GNUNET_GETOPT_CommandLineOption options[] = {
398 GNUNET_GETOPT_OPTION_END
399 };
400
401 int flags;
402 int ret;
403
404 flags = fcntl (0, F_GETFL, 0);
405 flags |= O_NONBLOCK;
406 fcntl (0, F_SETFL, flags);
407
408 if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
409 return 2;
410
411 ret = GNUNET_PROGRAM_run (argc, argv, "gnunet-conversation",
412 gettext_noop ("Print information about conversation."),
413 options, &run, NULL);
414 GNUNET_free ((void *) argv);
415
416 return ret;
417}
418
419/* end of gnunet-conversation.c */
diff --git a/src/conversation/gnunet-helper-audio-playback.c b/src/conversation/gnunet-helper-audio-playback.c
new file mode 100644
index 000000000..6e8ca86ff
--- /dev/null
+++ b/src/conversation/gnunet-helper-audio-playback.c
@@ -0,0 +1,391 @@
1#include <gnunet/platform.h>
2#include <gnunet/gnunet_util_lib.h>
3#include "gnunet_protocols_conversation.h"
4#include <gnunet/gnunet_constants.h>
5#include <gnunet/gnunet_core_service.h>
6
7#include <pulse/simple.h>
8#include <pulse/error.h>
9#include <pulse/rtclock.h>
10
11#include <pulse/pulseaudio.h>
12#include <opus/opus.h>
13#include <opus/opus_types.h>
14
15#define MAXLINE 4096
16
17/**
18* GNUnet Message Tokenizer
19*/
20#include "mst.c"
21
22/**
23* Pulseaudio specification. May change in the future.
24*/
25static pa_sample_spec sample_spec = {
26 .format = PA_SAMPLE_FLOAT32LE,
27 .rate = 48000,
28 .channels = 1
29};
30
31/**
32* Pulseaudio mainloop api
33*/
34static pa_mainloop_api *mainloop_api = NULL;
35
36/**
37* Pulseaudio threaded mainloop
38*/
39static pa_threaded_mainloop *m = NULL;
40
41/**
42* Pulseaudio context
43*/
44static pa_context *context = NULL;
45
46/**
47* Pulseaudio output stream
48*/
49static pa_stream *stream_out = NULL;
50
51/**
52* Pulseaudio io events
53*/
54static pa_io_event *stdio_event = NULL;
55
56/**
57* OPUS decoder
58*/
59OpusDecoder *dec = NULL;
60
61/**
62* PCM data buffer
63*/
64float *pcm_buffer;
65
66/**
67* Length of PCM buffer
68*/
69int pcm_length;
70
71/**
72* Number of samples for one frame
73*/
74int frame_size;
75
76/**
77* The sampling rate used in Pulseaudio specification
78*/
79opus_int32 sampling_rate;
80
81/**
82* Audio buffer
83*/
84static void *buffer = NULL;
85
86/**
87* Length of audio buffer
88*/
89static size_t buffer_length = 0;
90
91/**
92* Read index for transmit buffer
93*/
94static size_t buffer_index = 0;
95
96
97
98/**
99* Message callback
100*/
101static void
102stdin_receiver (void *cls, const struct GNUNET_MessageHeader *msg)
103{
104 struct AudioMessage *audio;
105
106 switch (ntohs (msg->type))
107 {
108 case GNUNET_MESSAGE_TYPE_CONVERSATION_AUDIO:
109 audio = (struct AudioMessage *) msg;
110
111 int len =
112 opus_decode_float (dec, audio->audio, audio->length, pcm_buffer,
113 frame_size, 0);
114
115 if (pa_stream_write
116 (stream_out, (uint8_t *) pcm_buffer, pcm_length, NULL, 0,
117 PA_SEEK_RELATIVE) < 0)
118 {
119
120 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
121 _("pa_stream_write() failed: %s\n"),
122 pa_strerror (pa_context_errno (context)));
123 return;
124 }
125
126 break;
127
128 default:
129 break;
130 }
131}
132
133/**
134* Pulseaudio shutdown task
135*/
136static void
137quit (int ret)
138{
139 mainloop_api->quit (mainloop_api, ret);
140 exit (ret);
141}
142
143/**
144* Write some data to the stream
145*/
146static void
147do_stream_write (size_t length)
148{
149 size_t l;
150 GNUNET_assert (length);
151
152 if (!buffer || !buffer_length)
153 {
154 return;
155 }
156
157
158 l = length;
159 if (l > buffer_length)
160 {
161 l = buffer_length;
162
163 }
164
165 if (pa_stream_write
166 (stream_out, (uint8_t *) buffer + buffer_index, l, NULL, 0,
167 PA_SEEK_RELATIVE) < 0)
168 {
169 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
170 _("pa_stream_write() failed: %s\n"),
171 pa_strerror (pa_context_errno (context)));
172 quit (1);
173 return;
174 }
175
176 buffer_length -= l;
177 buffer_index += l;
178
179 if (!buffer_length)
180 {
181 pa_xfree (buffer);
182 buffer = NULL;
183 buffer_index = buffer_length = 0;
184 }
185}
186
187/**
188* Callback when data is there for playback
189*/
190static void
191stream_write_callback (pa_stream * s, size_t length, void *userdata)
192{
193
194 if (stdio_event)
195 {
196 mainloop_api->io_enable (stdio_event, PA_IO_EVENT_INPUT);
197 }
198
199
200 if (!buffer)
201 {
202 return;
203 }
204
205
206 do_stream_write (length);
207}
208
209/**
210* Exit callback for SIGTERM and SIGINT
211*/
212static void
213exit_signal_callback (pa_mainloop_api * m, pa_signal_event * e, int sig,
214 void *userdata)
215{
216 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
217 _("gnunet-helper-audio-playback - Got signal, exiting\n"));
218 quit (1);
219}
220
221/**
222* Pulseaudio stream state callback
223*/
224static void
225context_state_callback (pa_context * c, void *userdata)
226{
227 int p;
228 GNUNET_assert (c);
229
230 switch (pa_context_get_state (c))
231 {
232 case PA_CONTEXT_CONNECTING:
233 case PA_CONTEXT_AUTHORIZING:
234 case PA_CONTEXT_SETTING_NAME:
235 break;
236
237 case PA_CONTEXT_READY:
238 {
239 GNUNET_assert (c);
240 GNUNET_assert (!stream_out);
241
242 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Connection established.\n"));
243
244
245 if (!
246 (stream_out =
247 pa_stream_new (c, "GNUNET VoIP playback", &sample_spec, NULL)))
248 {
249 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
250 _("pa_stream_new() failed: %s\n"),
251 pa_strerror (pa_context_errno (c)));
252 goto fail;
253 }
254
255 pa_stream_set_write_callback (stream_out, stream_write_callback,
256 NULL);
257
258 if ((p =
259 pa_stream_connect_playback (stream_out, NULL, NULL, 0, NULL,
260 NULL)) < 0)
261 {
262 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
263 _("pa_stream_connect_playback() failed: %s\n"),
264 pa_strerror (pa_context_errno (c)));
265 goto fail;
266 }
267
268 break;
269 }
270
271 case PA_CONTEXT_TERMINATED:
272 quit (0);
273 break;
274
275 case PA_CONTEXT_FAILED:
276 default:
277 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Connection failure: %s\n"),
278 pa_strerror (pa_context_errno (c)));
279 goto fail;
280 }
281
282 return;
283
284fail:
285 quit (1);
286
287}
288
289/**
290* Pulseaudio initialization
291*/
292void
293pa_init ()
294{
295 int r;
296
297 if (!pa_sample_spec_valid (&sample_spec))
298 {
299 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Wrong Spec\n"));
300 }
301
302 /* set up threaded playback mainloop */
303
304 if (!(m = pa_threaded_mainloop_new ()))
305 {
306 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("pa_mainloop_new() failed.\n"));
307 }
308
309 mainloop_api = pa_threaded_mainloop_get_api (m);
310
311
312 /* listen to signals */
313
314 r = pa_signal_init (mainloop_api);
315 GNUNET_assert (r == 0);
316 pa_signal_new (SIGINT, exit_signal_callback, NULL);
317 pa_signal_new (SIGTERM, exit_signal_callback, NULL);
318
319
320 /* connect to the main pulseaudio context */
321
322 if (!(context = pa_context_new (mainloop_api, "GNUnet VoIP")))
323 {
324 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("pa_context_new() failed.\n"));
325 }
326
327 pa_context_set_state_callback (context, context_state_callback, NULL);
328
329 if (pa_context_connect (context, NULL, 0, NULL) < 0)
330 {
331 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
332 _("pa_context_connect() failed: %s\n"),
333 pa_strerror (pa_context_errno (context)));
334 }
335
336 if (pa_threaded_mainloop_start (m) < 0)
337 {
338 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("pa_mainloop_run() failed.\n"));
339 }
340}
341
342/**
343* OPUS initialization
344*/
345void
346opus_init ()
347{
348 int err;
349 int channels = 1;
350 sampling_rate = 48000;
351 frame_size = sampling_rate / 50;
352 pcm_length = frame_size * channels * sizeof (float);
353
354 dec = opus_decoder_create (sampling_rate, channels, &err);
355 pcm_buffer = (float *) pa_xmalloc (frame_size * channels * sizeof (float));
356}
357
358/**
359 * The main function for the playback helper.
360 *
361 * @param argc number of arguments from the command line
362 * @param argv command line arguments
363 * @return 0 ok, 1 on error
364 */
365int
366main (int argc, char *argv[])
367{
368 char readbuf[MAXLINE];
369 struct MessageStreamTokenizer *stdin_mst;
370
371 stdin_mst = mst_create (&stdin_receiver, NULL);
372
373 opus_init ();
374 pa_init ();
375
376 while (1)
377 {
378 ssize_t ret = read (0, readbuf, sizeof (readbuf));
379
380 if (0 > ret)
381 {
382 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
383 _("Read error from STDIN: %s\n"), strerror (errno));
384 break;
385 }
386
387 mst_receive (stdin_mst, readbuf, ret);
388 }
389
390 return 0;
391}
diff --git a/src/conversation/gnunet-helper-audio-record.c b/src/conversation/gnunet-helper-audio-record.c
new file mode 100644
index 000000000..839c2f4d3
--- /dev/null
+++ b/src/conversation/gnunet-helper-audio-record.c
@@ -0,0 +1,446 @@
1#include <gnunet/platform.h>
2#include <gnunet/gnunet_util_lib.h>
3#include "gnunet_protocols_conversation.h"
4#include <gnunet/gnunet_constants.h>
5#include <gnunet/gnunet_core_service.h>
6
7#include <pulse/simple.h>
8#include <pulse/error.h>
9#include <pulse/rtclock.h>
10
11#include <pulse/pulseaudio.h>
12#include <opus/opus.h>
13#include <opus/opus_types.h>
14
15/**
16* Specification for recording. May change in the future to spec negotiation.
17*/
18static pa_sample_spec sample_spec = {
19 .format = PA_SAMPLE_FLOAT32LE,
20 .rate = 48000,
21 .channels = 1
22};
23
24/**
25* Pulseaudio mainloop api
26*/
27static pa_mainloop_api *mainloop_api = NULL;
28
29/**
30* Pulseaudio mainloop
31*/
32static pa_mainloop *m = NULL;
33
34/**
35* Pulseaudio context
36*/
37static pa_context *context = NULL;
38
39/**
40* Pulseaudio recording stream
41*/
42static pa_stream *stream_in = NULL;
43
44/**
45* Pulseaudio io events
46*/
47static pa_io_event *stdio_event = NULL;
48
49/**
50* Message tokenizer
51*/
52struct MessageStreamTokenizer *stdin_mst;
53
54/**
55* OPUS encoder
56*/
57OpusEncoder *enc = NULL;
58
59/**
60*
61*/
62unsigned char *opus_data;
63
64/**
65* PCM data buffer for one OPUS frame
66*/
67float *pcm_buffer;
68
69/**
70 * Length of the pcm data needed for one OPUS frame
71 */
72int pcm_length;
73
74/**
75* Number of samples for one frame
76*/
77int frame_size;
78
79/**
80* Maximum length of opus payload
81*/
82int max_payload_bytes = 1500;
83
84/**
85* Audio buffer
86*/
87static void *transmit_buffer = NULL;
88
89/**
90* Length of audio buffer
91*/
92static size_t transmit_buffer_length = 0;
93
94/**
95* Read index for transmit buffer
96*/
97static size_t transmit_buffer_index = 0;
98
99/**
100* Audio message skeleton
101*/
102struct AudioMessage *audio_message;
103
104
105
106/**
107* Pulseaudio shutdown task
108*/
109static void
110quit (int ret)
111{
112 mainloop_api->quit (mainloop_api, ret);
113 exit (ret);
114}
115
116
117
118/**
119* Creates OPUS packets from PCM data
120*/
121static void
122packetizer ()
123{
124
125
126 while (transmit_buffer_length >= transmit_buffer_index + pcm_length)
127 {
128
129 int ret;
130 int len;
131
132 size_t msg_size = sizeof (struct AudioMessage);
133
134 memcpy (pcm_buffer,
135 (float *) transmit_buffer +
136 (transmit_buffer_index / sizeof (float)), pcm_length);
137 len =
138 opus_encode_float (enc, pcm_buffer, frame_size, opus_data,
139 max_payload_bytes);
140
141 audio_message->length = len;
142 memcpy (audio_message->audio, opus_data, len);
143
144 if ((ret = write (1, audio_message, msg_size)) != msg_size)
145 {
146 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("write"));
147 return;
148 }
149
150 transmit_buffer_index += pcm_length;
151 }
152
153 int new_size = transmit_buffer_length - transmit_buffer_index;
154
155 if (0 != new_size)
156 {
157
158 transmit_buffer = pa_xrealloc (transmit_buffer, new_size);
159 memcpy (transmit_buffer, transmit_buffer + transmit_buffer_index,
160 new_size);
161
162 transmit_buffer_index = 0;
163 transmit_buffer_length = new_size;
164 }
165
166}
167
168/**
169* Pulseaudio callback when new data is available.
170*/
171static void
172stream_read_callback (pa_stream * s, size_t length, void *userdata)
173{
174 const void *data;
175 GNUNET_assert (s);
176 GNUNET_assert (length > 0);
177
178 if (stdio_event)
179 mainloop_api->io_enable (stdio_event, PA_IO_EVENT_OUTPUT);
180
181 if (pa_stream_peek (s, (const void **) &data, &length) < 0)
182 {
183 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("pa_stream_peek() failed: %s\n"),
184 pa_strerror (pa_context_errno (context)));
185 quit (1);
186 return;
187 }
188
189 GNUNET_assert (data);
190 GNUNET_assert (length > 0);
191
192 if (transmit_buffer)
193 {
194 transmit_buffer =
195 pa_xrealloc (transmit_buffer, transmit_buffer_length + length);
196 memcpy ((uint8_t *) transmit_buffer + transmit_buffer_length, data,
197 length);
198 transmit_buffer_length += length;
199 }
200 else
201 {
202 transmit_buffer = pa_xmalloc (length);
203 memcpy (transmit_buffer, data, length);
204 transmit_buffer_length = length;
205 transmit_buffer_index = 0;
206 }
207
208 pa_stream_drop (s);
209 packetizer ();
210}
211
212/**
213* Exit callback for SIGTERM and SIGINT
214*/
215static void
216exit_signal_callback (pa_mainloop_api * m, pa_signal_event * e, int sig,
217 void *userdata)
218{
219 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Got signal, exiting.\n"));
220 quit (1);
221}
222
223/**
224* Pulseaudio stream state callback
225*/
226static void
227stream_state_callback (pa_stream * s, void *userdata)
228{
229 GNUNET_assert (s);
230
231 switch (pa_stream_get_state (s))
232 {
233 case PA_STREAM_CREATING:
234 case PA_STREAM_TERMINATED:
235 break;
236
237 case PA_STREAM_READY:
238 if (1)
239 {
240 const pa_buffer_attr *a;
241 char cmt[PA_CHANNEL_MAP_SNPRINT_MAX],
242 sst[PA_SAMPLE_SPEC_SNPRINT_MAX];
243
244 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
245 _("Stream successfully created.\n"));
246
247 if (!(a = pa_stream_get_buffer_attr (s)))
248 {
249 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
250 _("pa_stream_get_buffer_attr() failed: %s\n"),
251 pa_strerror (pa_context_errno
252 (pa_stream_get_context (s))));
253
254 }
255 else
256 {
257 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
258 _("Buffer metrics: maxlength=%u, fragsize=%u\n"),
259 a->maxlength, a->fragsize);
260 }
261
262 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
263 _("Using sample spec '%s', channel map '%s'.\n"),
264 pa_sample_spec_snprint (sst, sizeof (sst),
265 pa_stream_get_sample_spec (s)),
266 pa_channel_map_snprint (cmt, sizeof (cmt),
267 pa_stream_get_channel_map (s)));
268
269 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
270 _("Connected to device %s (%u, %ssuspended).\n"),
271 pa_stream_get_device_name (s),
272 pa_stream_get_device_index (s),
273 pa_stream_is_suspended (s) ? "" : "not ");
274 }
275
276 break;
277
278 case PA_STREAM_FAILED:
279 default:
280 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Stream error: %s\n"),
281 pa_strerror (pa_context_errno (pa_stream_get_context (s))));
282 quit (1);
283 }
284}
285
286/**
287* Pulseaudio context state callback
288*/
289static void
290context_state_callback (pa_context * c, void *userdata)
291{
292 GNUNET_assert (c);
293
294 switch (pa_context_get_state (c))
295 {
296 case PA_CONTEXT_CONNECTING:
297 case PA_CONTEXT_AUTHORIZING:
298 case PA_CONTEXT_SETTING_NAME:
299 break;
300
301 case PA_CONTEXT_READY:
302 {
303 int r;
304
305 GNUNET_assert (c);
306 GNUNET_assert (!stream_in);
307
308 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Connection established.\n"));
309
310 if (!
311 (stream_in =
312 pa_stream_new (c, "GNUNET_VoIP recorder", &sample_spec, NULL)))
313 {
314 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
315 _("pa_stream_new() failed: %s\n"),
316 pa_strerror (pa_context_errno (c)));
317 goto fail;
318 }
319
320
321 pa_stream_set_state_callback (stream_in, stream_state_callback, NULL);
322 pa_stream_set_read_callback (stream_in, stream_read_callback, NULL);
323
324
325 if ((r = pa_stream_connect_record (stream_in, NULL, NULL, 0)) < 0)
326 {
327 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
328 _("pa_stream_connect_record() failed: %s\n"),
329 pa_strerror (pa_context_errno (c)));
330 goto fail;
331 }
332
333 break;
334 }
335
336 case PA_CONTEXT_TERMINATED:
337 quit (0);
338 break;
339
340 case PA_CONTEXT_FAILED:
341 default:
342 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Connection failure: %s\n"),
343 pa_strerror (pa_context_errno (c)));
344 goto fail;
345 }
346
347 return;
348
349fail:
350 quit (1);
351
352}
353
354/**
355 * Pulsaudio init
356 */
357void
358pa_init ()
359{
360 int r;
361 int i;
362
363 if (!pa_sample_spec_valid (&sample_spec))
364 {
365 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Wrong Spec\n"));
366 }
367
368 /* set up main record loop */
369
370 if (!(m = pa_mainloop_new ()))
371 {
372 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("pa_mainloop_new() failed.\n"));
373 }
374
375 mainloop_api = pa_mainloop_get_api (m);
376
377 /* listen to signals */
378
379 r = pa_signal_init (mainloop_api);
380 GNUNET_assert (r == 0);
381 pa_signal_new (SIGINT, exit_signal_callback, NULL);
382 pa_signal_new (SIGTERM, exit_signal_callback, NULL);
383
384 /* connect to the main pulseaudio context */
385
386 if (!(context = pa_context_new (mainloop_api, "GNUNET VoIP")))
387 {
388 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("pa_context_new() failed.\n"));
389 }
390
391 pa_context_set_state_callback (context, context_state_callback, NULL);
392
393 if (pa_context_connect (context, NULL, 0, NULL) < 0)
394 {
395 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
396 _("pa_context_connect() failed: %s\n"),
397 pa_strerror (pa_context_errno (context)));
398 }
399
400 if (pa_mainloop_run (m, &i) < 0)
401 {
402 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("pa_mainloop_run() failed.\n"));
403 }
404}
405
406/**
407 * OPUS init
408 */
409void
410opus_init ()
411{
412 opus_int32 sampling_rate = 48000;
413 frame_size = sampling_rate / 50;
414 int channels = 1;
415
416 pcm_length = frame_size * channels * sizeof (float);
417
418 int err;
419
420 enc =
421 opus_encoder_create (sampling_rate, channels, OPUS_APPLICATION_VOIP,
422 &err);
423 pcm_buffer = (float *) pa_xmalloc (pcm_length);
424 opus_data = (unsigned char *) calloc (max_payload_bytes, sizeof (char));
425
426 audio_message = pa_xmalloc (sizeof (struct AudioMessage));
427
428 audio_message->header.size = htons (sizeof (struct AudioMessage));
429 audio_message->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_AUDIO);
430}
431
432/**
433 * The main function for the record helper.
434 *
435 * @param argc number of arguments from the command line
436 * @param argv command line arguments
437 * @return 0 ok, 1 on error
438 */
439int
440main (int argc, char *argv[])
441{
442 opus_init ();
443 pa_init ();
444
445 return 0;
446}
diff --git a/src/conversation/gnunet-service-conversation.c b/src/conversation/gnunet-service-conversation.c
new file mode 100644
index 000000000..9b9b4f316
--- /dev/null
+++ b/src/conversation/gnunet-service-conversation.c
@@ -0,0 +1,1786 @@
1/*
2 This file is part of GNUnet.
3 (C)
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 conversation/gnunet-service-conversation.c
23 * @brief conversation service implementation
24 * @author Simon Dieterle
25 * @author Andreas Fuchs
26 * STRUCTURE:
27 * - Variables
28 * - AUXILIARY FUNCTIONS
29 * - SENDING FUNCTIONS CL -> SERVER
30 * - RECEIVE FUNCTIONS CL -> SERVER
31 * - SENDING FUNCTIONS MESH
32 * - RECEIVE FUNCTIONS MESH
33 * - HELPER
34 * - TUNNEL HANDLING
35 * - CLIENT HANDLING
36 */
37#include <gnunet/platform.h>
38#include <gnunet/gnunet_util_lib.h>
39#include <gnunet/gnunet_constants.h>
40#include <gnunet/gnunet_mesh_service.h>
41#include "gnunet_conversation.h"
42#include "gnunet_protocols_conversation.h"
43
44/********************************************************
45 * Ugly hack because of not working MESH API
46*/
47typedef uint32_t MESH_TunnelNumber;
48struct GNUNET_MESH_Tunnel
49{
50 struct GNUNET_MESH_Tunnel *next;
51 struct GNUNET_MESH_Tunnel *prev;
52 struct GNUNET_MESH_Handle *mesh;
53 MESH_TunnelNumber tid;
54 uint32_t port;
55 GNUNET_PEER_Id peer;
56 void *ctx;
57 unsigned int packet_size;
58 int buffering;
59 int reliable;
60 int allow_send;
61};
62
63
64/**
65 * Our configuration.
66 */
67static const struct GNUNET_CONFIGURATION_Handle *cfg;
68
69/**
70 * Head of the list of current clients.
71 */
72static struct GNUNET_CONTAINER_SList *clients;
73
74/**
75 * Notification context containing all connected clients.
76 */
77struct GNUNET_SERVER_NotificationContext *nc = NULL;
78
79/**
80* The connection status
81*/
82static struct ConnectionStatus connection;
83
84/**
85* Handle for the record helper
86*/
87static struct GNUNET_HELPER_Handle *record_helper;
88
89/** Handle for the playback handler
90*
91*/
92static struct GNUNET_HELPER_Handle *playback_helper;
93
94/**
95* Handle for mesh
96*/
97static struct GNUNET_MESH_Handle *mesh;
98
99/**
100* Transmit handle for audio messages
101*/
102static struct GNUNET_MESH_TransmitHandle *mth = NULL;
103
104/**
105* Handle for the reliable tunnel (contol data)
106*/
107static struct GNUNET_MESH_Tunnel *tunnel_reliable;
108
109/**
110* Handle for unreliable tunnel (audio data)
111*/
112static struct GNUNET_MESH_Tunnel *tunnel_unreliable;
113
114/**
115* List for missed calls
116*/
117struct GNUNET_CONTAINER_SList *missed_calls;
118
119/**
120* List for peers to notify that we are available again
121*/
122struct GNUNET_CONTAINER_SList *peers_to_notify;
123
124/**
125* Audio buffer (outgoing)
126*/
127struct GNUNET_CONTAINER_SList *audio_buffer;
128
129/**
130* The pointer to the task for sending audio
131*/
132GNUNET_SCHEDULER_TaskIdentifier audio_task;
133
134/**
135* The pointer to the task for checking timeouts an calling a peer
136*/
137GNUNET_SCHEDULER_TaskIdentifier timeout_task;
138
139/**
140* Sequencenumber for the pakets (for evaltuation purposes)
141*/
142int SequenceNumber = 0;
143
144/**
145* Timestamp for call statistics
146*/
147static struct GNUNET_TIME_Absolute start_time;
148
149/**
150 * Number of payload packes sent
151 */
152static int data_sent;
153static int data_sent_size;
154
155/**
156 * Number of payload packets received
157 */
158static int data_received;
159static int data_received_size;
160
161/******************************************************************************/
162/*********************** AUXILIARY FUNCTIONS *************************/
163/******************************************************************************/
164
165/**
166* Function which displays some call stats
167*/
168static void
169show_end_data (void)
170{
171 static struct GNUNET_TIME_Absolute end_time;
172 static struct GNUNET_TIME_Relative total_time;
173
174 end_time = GNUNET_TIME_absolute_get ();
175 total_time = GNUNET_TIME_absolute_get_difference (start_time, end_time);
176 FPRINTF (stderr, "\nResults of send\n");
177 FPRINTF (stderr, "Test time %llu ms\n",
178 (unsigned long long) total_time.rel_value);
179 FPRINTF (stderr, "Test total packets: %d\n", data_sent);
180 FPRINTF (stderr, "Test bandwidth: %f kb/s\n", data_sent_size * 1.0 / total_time.rel_value); // 4bytes * ms
181 FPRINTF (stderr, "Test throughput: %f packets/s\n\n", data_sent * 1000.0 / total_time.rel_value); // packets * ms
182
183 FPRINTF (stderr, "\nResults of recv\n");
184 FPRINTF (stderr, "Test time %llu ms\n",
185 (unsigned long long) total_time.rel_value);
186 FPRINTF (stderr, "Test total packets: %d\n", data_received);
187 FPRINTF (stderr, "Test bandwidth: %f kb/s\n", data_received_size * 1.0 / total_time.rel_value); // 4bytes * ms
188 FPRINTF (stderr, "Test throughput: %f packets/s\n\n", data_received * 1000.0 / total_time.rel_value); // packets * ms
189}
190
191/**
192* Function which sets the connection state to LISTEN
193*/
194static void
195status_to_listen (void)
196{
197
198 if (CONNECTED == connection.status)
199 {
200 show_end_data ();
201 }
202
203 if (timeout_task != GNUNET_SCHEDULER_NO_TASK)
204 {
205 GNUNET_SCHEDULER_cancel (timeout_task);
206 timeout_task = GNUNET_SCHEDULER_NO_TASK;
207 }
208
209 stop_helpers ();
210
211 connection.status = LISTEN;
212 connection.client = NULL;
213
214 data_sent = 0;
215 data_sent_size = 0;
216 data_received = 0;
217 data_received_size = 0;
218
219 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Changed connection status to %s\n"),
220 "LISTEN");
221}
222
223/**
224* Function to terminate the active call
225*/
226static void
227terminate_call ()
228{
229 size_t msg_size;
230 msg_size = sizeof (struct MeshSessionTerminateMessage);
231 struct MeshSessionTerminateMessage *message_mesh_terminate =
232 (struct MeshSessionTerminateMessage *) GNUNET_malloc (msg_size);
233
234 if (NULL == message_mesh_terminate)
235 {
236 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
237 _("Could not create MeshSessionTerminateMessage\n"));
238 status_to_listen ();
239
240 return;
241 }
242
243 message_mesh_terminate->header.size = htons (msg_size);
244 message_mesh_terminate->header.type =
245 htons (GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_SESSION_TERMINATE);
246
247 if (NULL ==
248 GNUNET_MESH_notify_transmit_ready (tunnel_reliable, 0,
249 MAX_TRANSMIT_DELAY, msg_size,
250 &transmit_mesh_message,
251 (void *) message_mesh_terminate))
252 {
253 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
254 _("Could not queue MeshSessionTerminateMessage\n"));
255 GNUNET_free (message_mesh_terminate);
256 status_to_listen ();
257 }
258}
259
260/**
261* Function to reject a call
262*
263* @param tunnel the tunnel where to reject the incoming call
264* @param reason te reson why the call is rejected
265*/
266static void
267reject_call (struct GNUNET_MESH_Tunnel *tunnel, int reason)
268{
269 size_t msg_size;
270 msg_size = sizeof (struct MeshSessionRejectMessage);
271 struct MeshSessionRejectMessage *message_mesh_reject =
272 (struct MeshSessionRejectMessage *) GNUNET_malloc (msg_size);
273
274 if (NULL == message_mesh_reject)
275 {
276 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
277 _("Could not create MeshSessionRejectMessage\n"));
278 status_to_listen ();
279
280 return;
281 }
282
283 message_mesh_reject->header.size = htons (msg_size);
284 message_mesh_reject->header.type =
285 htons (GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_SESSION_REJECT);
286 message_mesh_reject->reason = htons (reason);
287
288 if (NULL ==
289 GNUNET_MESH_notify_transmit_ready (tunnel_reliable, 0,
290 MAX_TRANSMIT_DELAY, msg_size,
291 &transmit_mesh_message,
292 (void *) message_mesh_reject))
293 {
294 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
295 _("Could not queue MeshSessionRejectMessage\n"));
296 GNUNET_free (message_mesh_reject);
297 status_to_listen ();
298 }
299}
300
301/**
302 * Check for timeout when calling a peer
303 *
304 * @param cls closure, NULL
305 * @param tc the task context (can be NULL)
306 */
307static void
308check_timeout (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
309{
310 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Call timeout\n");
311
312 if (NULL ==
313 GNUNET_SERVER_notify_transmit_ready (connection.client,
314 sizeof (struct
315 ServerClientNoAnswerMessage),
316 MAX_TRANSMIT_DELAY,
317 &transmit_server_no_answer_message,
318 NULL))
319 {
320 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
321 _("Could not queue ServerClientNoAnswerMessage\n"));
322 }
323
324 terminate_call ();
325}
326
327/******************************************************************************/
328/*********************** SENDING FUNCTIONS CL -> SERVER *******************/
329/******************************************************************************/
330
331/**
332 * Function called to send a session initiate message to the client.
333 * "buf" will be NULL and "size" zero if the socket was closed for writing in
334 * the meantime.
335 *
336 * @param cls closure, NULL
337 * @param size number of bytes available in buf
338 * @param buf where the callee should write the initiate message
339 * @return number of bytes written to buf
340 */
341static size_t
342transmit_server_initiate_message (void *cls, size_t size, void *buf)
343{
344 struct ServerClientSessionInitiateMessage *msg;
345 size_t msg_size;
346
347 msg_size = sizeof (struct ServerClientSessionInitiateMessage);
348
349 GNUNET_assert (size >= msg_size);
350
351 msg = (struct ServerClientSessionInitiateMessage *) buf;
352 msg->header.size = htons (msg_size);
353 msg->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_SC_SESSION_INITIATE);
354 memcpy (&(msg->peer), (struct GNUNET_PeerIdentity *) cls,
355 sizeof (struct GNUNET_PeerIdentity));
356
357 return msg_size;
358}
359
360/**
361 * Function called to send a session accept message to the client.
362 * "buf" will be NULL and "size" zero if the socket was closed for writing in
363 * the meantime.
364 *
365 * @param cls closure, NULL
366 * @param size number of bytes available in buf
367 * @param buf where the callee should write the accept message
368 * @return number of bytes written to buf
369 */
370static size_t
371transmit_server_accept_message (void *cls, size_t size, void *buf)
372{
373 struct ServerClientSessionAcceptMessage *msg;
374 size_t msg_size;
375
376 msg_size = sizeof (struct ServerClientSessionAcceptMessage);
377
378 GNUNET_assert (size >= msg_size);
379
380 msg = (struct ServerClientSessionAcceptMessage *) buf;
381 msg->header.size = htons (msg_size);
382 msg->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_SC_SESSION_ACCEPT);
383
384 return msg_size;
385}
386
387/**
388 * Function called to send a session reject message to the client.
389 * "buf" will be NULL and "size" zero if the socket was closed for writing in
390 * the meantime.
391 *
392 * @param cls closure, NULL
393 * @param size number of bytes available in buf
394 * @param buf where the callee should write the reject message
395 * @return number of bytes written to buf
396 */
397static size_t
398transmit_server_reject_message (void *cls, size_t size, void *buf)
399{
400 struct ServerClientSessionRejectMessage *msg;
401 size_t msg_size;
402
403 msg_size = sizeof (struct ServerClientSessionRejectMessage);
404
405 GNUNET_assert (size >= msg_size);
406
407 msg = (struct ServerClientSessionRejectMessage *) buf;
408 msg->header.size = htons (msg_size);
409 msg->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_SC_SESSION_REJECT);
410
411 if (NULL == cls)
412 {
413 msg->reason = htons (REJECT_REASON_NOT_AVAILABLE);
414 }
415 else
416 {
417 msg->reason = ((struct MeshSessionRejectMessage *) cls)->reason;
418 }
419
420 return msg_size;
421}
422
423/**
424 * Function called to send a session terminate message to the client.
425 * "buf" will be NULL and "size" zero if the socket was closed for writing in
426 * the meantime.
427 *
428 * @param cls closure, NULL
429 * @param size number of bytes available in buf
430 * @param buf where the callee should write the terminate message
431 * @return number of bytes written to buf
432 */
433static size_t
434transmit_server_terminate_message (void *cls, size_t size, void *buf)
435{
436 struct ServerClientSessionTerminateMessage *msg;
437 size_t msg_size;
438
439 msg_size = sizeof (struct ServerClientSessionTerminateMessage);
440
441 GNUNET_assert (size >= msg_size);
442
443 msg = (struct ServerClientSessionTerminateMessage *) buf;
444 msg->header.size = htons (msg_size);
445 msg->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_SC_SESSION_TERMINATE);
446
447 return msg_size;
448}
449
450/**
451 * Function called to send a missed call message to the client.
452 * "buf" will be NULL and "size" zero if the socket was closed for writing in
453 * the meantime.
454 *
455 * @param cls closure, NULL
456 * @param size number of bytes available in buf
457 * @param buf where the callee should write the missed call message
458 * @return number of bytes written to buf
459 */
460static size_t
461transmit_server_missed_call_message (void *cls, size_t size, void *buf)
462{
463 struct ServerClientMissedCallMessage *msg;
464 msg = (struct ServerClientMissedCallMessage *) cls;
465
466 memcpy (buf, msg, size);
467 GNUNET_free (msg);
468
469 return size;
470}
471
472/**
473 * Function called to send a service blocked message to the client.
474 * "buf" will be NULL and "size" zero if the socket was closed for writing in
475 * the meantime.
476 *
477 * @param cls closure, NULL
478 * @param size number of bytes available in buf
479 * @param buf where the callee should write the service blocked message
480 * @return number of bytes written to buf
481 */
482static size_t
483transmit_server_service_blocked_message (void *cls, size_t size, void *buf)
484{
485 struct ServerClientServiceBlockedMessage *msg;
486 size_t msg_size;
487
488 msg_size = sizeof (struct ServerClientServiceBlockedMessage);
489
490 GNUNET_assert (size >= msg_size);
491
492 msg = (struct ServerClientServiceBlockedMessage *) buf;
493 msg->header.size = htons (msg_size);
494 msg->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_SC_SERVICE_BLOCKED);
495
496 return msg_size;
497}
498
499/**
500 * Function called to send a peer not connected message to the client.
501 * "buf" will be NULL and "size" zero if the socket was closed for writing in
502 * the meantime.
503 *
504 * @param cls closure, NULL
505 * @param size number of bytes available in buf
506 * @param buf where the callee should write the peer not connected message
507 * @return number of bytes written to buf
508 */
509static size_t
510transmit_server_peer_not_connected_message (void *cls, size_t size, void *buf)
511{
512 struct ServerClientPeerNotConnectedMessage *msg;
513 size_t msg_size;
514
515 msg_size = sizeof (struct ServerClientPeerNotConnectedMessage);
516
517 GNUNET_assert (size >= msg_size);
518
519 msg = (struct ServerClientPeerNotConnectedMessage *) buf;
520 msg->header.size = htons (msg_size);
521 msg->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_SC_PEER_NOT_CONNECTED);
522
523 return msg_size;
524}
525
526/**
527 * Function called to send a peer no answer message to the client.
528 * "buf" will be NULL and "size" zero if the socket was closed for writing in
529 * the meantime.
530 *
531 * @param cls closure, NULL
532 * @param size number of bytes available in buf
533 * @param buf where the callee should write the peer no answer message
534 * @return number of bytes written to buf
535 */
536static size_t
537transmit_server_no_answer_message (void *cls, size_t size, void *buf)
538{
539 struct ServerClientNoAnswerMessage *msg;
540 size_t msg_size;
541
542 msg_size = sizeof (struct ServerClientNoAnswerMessage);
543
544 GNUNET_assert (size >= msg_size);
545
546 msg = (struct ServerClientNoAnswerMessage *) buf;
547 msg->header.size = htons (msg_size);
548 msg->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_SC_NO_ANSWER);
549
550 return msg_size;
551}
552
553/**
554 * Function called to send a error message to the client.
555 * "buf" will be NULL and "size" zero if the socket was closed for writing in
556 * the meantime.
557 *
558 * @param cls closure, NULL
559 * @param size number of bytes available in buf
560 * @param buf where the callee should write the error message
561 * @return number of bytes written to buf
562 */
563static size_t
564transmit_server_error_message (void *cls, size_t size, void *buf)
565{
566 struct ServerClientErrorMessage *msg;
567 size_t msg_size;
568
569 msg_size = sizeof (struct ServerClientErrorMessage);
570
571 GNUNET_assert (size >= msg_size);
572
573 msg = (struct ServerClientErrorMessage *) buf;
574 msg->header.size = htons (msg_size);
575 msg->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_SC_ERROR);
576
577 return msg_size;
578}
579
580/******************************************************************************/
581/*********************** RECEIVE FUNCTIONS CL -> SERVER ********************/
582/******************************************************************************/
583
584/**
585 * Function to handle a session initiate message from the client
586 *
587 * @param cls closure, NULL
588 * @param client the client from which the message is
589 * @param message the message from the client
590*/
591static void
592handle_session_initiate_message (void *cls,
593 struct GNUNET_SERVER_Client *client,
594 const struct GNUNET_MessageHeader *message)
595{
596 static uint32_t port = 50002;
597 size_t msg_size;
598 struct ClientServerSessionInitiateMessage *msg =
599 (struct ClientServerSessionInitiateMessage *) message;
600 struct GNUNET_PeerIdentity *peer = &(msg->peer);
601
602 GNUNET_SERVER_receive_done (client, GNUNET_OK);
603
604 if (NULL != connection.client)
605 {
606 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
607 _("There is already a peer in interaction\n"));
608 GNUNET_SERVER_notify_transmit_ready (client,
609 sizeof (struct
610 ServerClientServiceBlockedMessage),
611 MAX_TRANSMIT_DELAY,
612 &transmit_server_service_blocked_message,
613 NULL);
614
615 return;
616 }
617
618 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Creating tunnel to: %s\n"),
619 GNUNET_i2s_full (peer));
620 tunnel_reliable =
621 GNUNET_MESH_tunnel_create (mesh, NULL, peer, port, GNUNET_NO, GNUNET_NO);
622 if (NULL == tunnel_reliable)
623 {
624 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
625 _("Could not create reliable tunnel\n"));
626 GNUNET_SERVER_notify_transmit_ready (client,
627 sizeof (struct
628 ServerClientPeerNotConnectedMessage),
629 MAX_TRANSMIT_DELAY,
630 &transmit_server_peer_not_connected_message,
631 NULL);
632
633 return;
634 }
635
636 msg_size = sizeof (struct MeshSessionInitiateMessage);
637 struct MeshSessionInitiateMessage *message_mesh_initiate =
638 (struct MeshSessionInitiateMessage *) GNUNET_malloc (msg_size);
639
640 if (NULL == message_mesh_initiate)
641 {
642 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
643 _("Could not create MeshSessionInitiateMessage\n"));
644 GNUNET_MESH_tunnel_destroy (tunnel_reliable);
645 tunnel_reliable = NULL;
646 GNUNET_SERVER_notify_transmit_ready (client,
647 sizeof (struct
648 ServerClientErrorMessage),
649 MAX_TRANSMIT_DELAY,
650 &transmit_server_error_message,
651 NULL);
652
653 return;
654 }
655
656 message_mesh_initiate->header.size = htons (msg_size);
657 message_mesh_initiate->header.type =
658 htons (GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_SESSION_INITIATE);
659
660 if (NULL ==
661 GNUNET_MESH_notify_transmit_ready (tunnel_reliable, 0,
662 MAX_TRANSMIT_DELAY, msg_size,
663 &transmit_mesh_message,
664 (void *) message_mesh_initiate))
665 {
666 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
667 _("Could not queue MeshSessionInitiateMessage\n"));
668 GNUNET_MESH_tunnel_destroy (tunnel_reliable);
669 tunnel_reliable = NULL;
670 GNUNET_free (message_mesh_initiate);
671 GNUNET_SERVER_notify_transmit_ready (client,
672 sizeof (struct
673 ServerClientErrorMessage),
674 MAX_TRANSMIT_DELAY,
675 &transmit_server_error_message,
676 NULL);
677
678 return;
679 }
680
681 connection.status = CALLER;
682 connection.client = client;
683 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Changed connection status to %d\n"),
684 connection.status);
685 memcpy (&(connection.peer), peer, sizeof (struct GNUNET_PeerIdentity));
686
687 return;
688}
689
690/**
691 * Function to handle a session accept message from the client
692 *
693 * @param cls closure, NULL
694 * @param client the client from which the message is
695 * @param message the message from the client
696*/
697static void
698handle_session_accept_message (void *cls, struct GNUNET_SERVER_Client *client,
699 const struct GNUNET_MessageHeader *message)
700{
701 size_t msg_size;
702
703 GNUNET_SERVER_receive_done (client, GNUNET_OK);
704
705 if (connection.status != CALLEE)
706 {
707 // TODO send illegal command
708 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
709 _
710 ("handle_session_accept_message called when not allowed\n"));
711 return;
712 }
713
714 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Accepting the call of: %s\n"),
715 GNUNET_i2s_full (&(connection.peer)));
716
717 msg_size = sizeof (struct MeshSessionAcceptMessage);
718 struct MeshSessionAcceptMessage *message_mesh_accept =
719 (struct MeshSessionAcceptMessage *) GNUNET_malloc (msg_size);
720
721 if (NULL == message_mesh_accept)
722 {
723 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
724 _("Could not create MeshSessionAcceptMessage\n"));
725 return;
726 }
727
728 message_mesh_accept->header.size = htons (msg_size);
729 message_mesh_accept->header.type =
730 htons (GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_SESSION_ACCEPT);
731
732 if (NULL ==
733 GNUNET_MESH_notify_transmit_ready (tunnel_reliable, 0,
734 MAX_TRANSMIT_DELAY, msg_size,
735 &transmit_mesh_message,
736 (void *) message_mesh_accept))
737 {
738 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
739 _("Could not queue MeshSessionAcceptMessage\n"));
740 GNUNET_free (message_mesh_accept);
741 return;
742 }
743
744 connection.status = CONNECTED;
745 connection.client = client;
746 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Changed connection status to %d\n"),
747 connection.status);
748
749 return;
750}
751
752/**
753 * Function to handle a session reject message from the client
754 *
755 * @param cls closure, NULL
756 * @param client the client from which the message is
757 * @param message the message from the client
758*/
759static void
760handle_session_reject_message (void *cls, struct GNUNET_SERVER_Client *client,
761 const struct GNUNET_MessageHeader *message)
762{
763 struct ClientServerSessionRejectMessage *message_received;
764
765 GNUNET_SERVER_receive_done (client, GNUNET_OK);
766
767 if (connection.status != CALLEE)
768 {
769 // TODO send illegal command
770 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
771 _
772 ("handle_session_reject_message called when not allowed\n"));
773 return;
774 }
775
776 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Rejecting the call of: %s\n"),
777 GNUNET_i2s_full (&(connection.peer)));
778 message_received = (struct ClientServerSessionRejectMessage *) message;
779 reject_call (tunnel_reliable, ntohs (message_received->reason));
780
781 return;
782}
783
784/**
785 * Function to handle a session terminate message from the client
786 *
787 * @param cls closure, NULL
788 * @param client the client from which the message is
789 * @param message the message from the client
790*/
791static void
792handle_session_terminate_message (void *cls,
793 struct GNUNET_SERVER_Client *client,
794 const struct GNUNET_MessageHeader *message)
795{
796 GNUNET_SERVER_receive_done (client, GNUNET_OK);
797
798 if (connection.client == NULL || connection.status == CALLEE)
799 {
800 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
801 _
802 ("handle_session_terminate_message called when not allowed\n"));
803 return;
804 }
805
806 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Terminating the call with: %s\n"),
807 GNUNET_i2s_full (&(connection.peer)));
808 terminate_call ();
809}
810
811/******************************************************************************/
812/*********************** SENDING FUNCTIONS MESH *******************/
813/******************************************************************************/
814
815/**
816* Transmit a mesh message
817 * @param cls closure, NULL
818 * @param size number of bytes available in buf
819 * @param buf where the callee should write the message
820 * @return number of bytes written to buf
821 */
822static size_t
823transmit_mesh_message (void *cls, size_t size, void *buf)
824{
825 struct VoIPMeshMessageHeader *msg_header =
826 (struct VoIPMeshMessageHeader *) cls;
827 msg_header->SequenceNumber = SequenceNumber += 1;
828 msg_header->time = GNUNET_TIME_absolute_get ();
829
830 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Transmitting message over mesh\n"));
831
832 memcpy (buf, cls, size);
833 // Check if this is correct
834
835
836 if ((GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_SESSION_TERMINATE ==
837 ntohs (msg_header->header.type))
838 || (GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_SESSION_REJECT ==
839 ntohs (msg_header->header.type)))
840 {
841 status_to_listen ();
842 }
843 else if (GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_SESSION_INITIATE ==
844 ntohs (msg_header->header.type))
845 {
846 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Starting timeout task.\n"));
847 timeout_task =
848 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
849 (GNUNET_TIME_UNIT_SECONDS, 30),
850 &check_timeout, NULL);
851 }
852
853 GNUNET_free (cls);
854
855 return size;
856}
857
858/**
859* Transmit a audo message over mesh
860 * @param cls closure, NULL
861 * @param size number of bytes available in buf
862 * @param buf where the callee should write the message
863 * @return number of bytes written to buf
864 */
865static size_t
866transmit_mesh_audio_message (void *cls, size_t size, void *buf)
867{
868 struct AudioMessage *message = (struct AudioMessage *) cls;
869
870 if (size < sizeof (struct AudioMessage) || NULL == buf)
871 {
872 GNUNET_break (0);
873 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
874 "size %u, buf %p, data_sent %u, data_received %u\n",
875 size, buf, data_sent, data_received);
876 return 0;
877 }
878
879 memcpy (buf, message, size);
880
881 data_sent++;
882 data_sent_size += size;
883
884 GNUNET_log (GNUNET_ERROR_TYPE_INFO, " Sent packet %d\n", data_sent);
885
886 audio_task = GNUNET_SCHEDULER_add_now (&transmit_audio_task, NULL);
887
888 return size;
889}
890
891/**
892 * Task to schedule a audio transmission.
893 *
894 * @param cls Closure.
895 * @param tc Task Context.
896 */
897static void
898transmit_audio_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
899{
900 struct GNUNET_CONTAINER_SList_Iterator iterator;
901 struct AudioMessage *msg;
902 int ab_length = GNUNET_CONTAINER_slist_count (audio_buffer);
903
904 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "We have %d packets.\n", ab_length);
905
906 if (NULL == cls)
907 {
908 if (0 == ab_length && CONNECTED == connection.status)
909 {
910 audio_task =
911 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
912 (GNUNET_TIME_UNIT_MILLISECONDS, 10),
913 &transmit_audio_task, NULL);
914 return;
915 }
916
917 iterator = GNUNET_CONTAINER_slist_begin (audio_buffer);
918 msg =
919 (struct AudioMessage *) GNUNET_CONTAINER_slist_get (&iterator, NULL);
920 msg->SequenceNumber = SequenceNumber += 1;
921 msg->time = GNUNET_TIME_absolute_get ();
922
923 GNUNET_CONTAINER_slist_erase (&iterator);
924 GNUNET_CONTAINER_slist_iter_destroy (&iterator);
925 }
926 else
927 {
928 msg = (struct AudioMessage *) cls;
929 }
930
931 if (NULL == tunnel_unreliable)
932 {
933 GNUNET_CONTAINER_slist_clear (audio_buffer);
934 return;
935 }
936
937 mth = GNUNET_MESH_notify_transmit_ready (tunnel_unreliable, GNUNET_NO,
938 MAX_TRANSMIT_DELAY,
939 sizeof (struct AudioMessage),
940 &transmit_mesh_audio_message,
941 (void *) msg);
942
943 if (NULL == mth)
944 {
945 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
946 "Need to retransmit audio packet\n");
947 GNUNET_log (GNUNET_ERROR_TYPE_INFO, " in 1 ms\n");
948 audio_task =
949 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MILLISECONDS,
950 &transmit_audio_task, (void *) msg);
951 }
952}
953
954/******************************************************************************/
955/*********************** RECEIVE FUNCTIONS MESH ********************/
956/******************************************************************************/
957
958/**
959* Function to handle a initiation messaage incoming over mesh
960 * @param cls closure, NULL
961 * @param tunnel the tunnel over which the message arrived
962 * @pram tunnel_ctx the tunnel context, can be NULL
963 * @pram message the incoming message
964 *
965 * @return GNUNET_OK
966*/
967int
968handle_mesh_initiate_message (void *cls, struct GNUNET_MESH_Tunnel *tunnel,
969 void **tunnel_ctx,
970 const struct GNUNET_MessageHeader *message)
971{
972 int reject_reason;
973 //struct GNUNET_PeerIdentity *peer = (GNUNET_MESH_tunnel_get_info(tunnel, GNUNET_MESH_OPTION_PEER))->peer;
974 const struct GNUNET_PeerIdentity *peer =
975 GNUNET_PEER_resolve2 (tunnel->peer);
976
977 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
978 _("Handling MeshSessionInitiateMessage from peer: %s\n"),
979 GNUNET_i2s_full (peer));
980 GNUNET_MESH_receive_done (tunnel);
981
982 if (LISTEN != connection.status
983 || 1 > GNUNET_CONTAINER_slist_count (clients))
984 {
985
986 if (CONNECTED == connection.status)
987 {
988 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
989 _
990 ("Rejected call from %s because there is an active call"),
991 GNUNET_i2s_full (peer));
992 reject_reason = htons (REJECT_REASON_ACTIVE_CALL);
993
994 // Notifying client about missed call
995 size_t msg_size =
996 sizeof (struct ServerClientMissedCallMessage) +
997 sizeof (struct MissedCall);
998 struct ServerClientMissedCallMessage *message =
999 GNUNET_malloc (msg_size);
1000
1001 message->header.size = htons (msg_size);
1002 message->header.type =
1003 htons (GNUNET_MESSAGE_TYPE_CONVERSATION_SC_MISSED_CALL);
1004 message->number = 1;
1005
1006 memcpy (&(message->missed_call->peer), peer,
1007 sizeof (struct GNUNET_PeerIdentity));
1008 message->missed_call->time = GNUNET_TIME_absolute_get ();
1009
1010 if (NULL ==
1011 GNUNET_SERVER_notify_transmit_ready (connection.client,
1012 sizeof (struct
1013 ServerClientMissedCallMessage),
1014 MAX_TRANSMIT_DELAY,
1015 &transmit_server_missed_call_message,
1016 (void *) message))
1017 {
1018 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1019 _
1020 ("Could not queue ServerClientMissedCallMessage\n"));
1021 GNUNET_free (message);
1022 }
1023 }
1024
1025 if (1 > GNUNET_CONTAINER_slist_count (clients))
1026 {
1027 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1028 _("Got a call from %s while no client connected.\n"),
1029 GNUNET_i2s_full (peer));
1030 reject_reason = htons (REJECT_REASON_NO_CLIENT);
1031 // Store missed calls
1032 struct MissedCall call;
1033 memcpy (&(call.peer), peer, sizeof (struct GNUNET_PeerIdentity));
1034 call.time = GNUNET_TIME_absolute_get ();
1035 GNUNET_CONTAINER_slist_add_end (missed_calls,
1036 GNUNET_CONTAINER_SLIST_DISPOSITION_TRANSIENT,
1037 &call, sizeof (struct MissedCall));
1038
1039 }
1040
1041 reject_call (tunnel, reject_reason);
1042 }
1043 else
1044 {
1045 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Initiated call from: %s\n"),
1046 GNUNET_i2s_full (peer));
1047 tunnel_reliable = tunnel;
1048 connection.status = CALLEE;
1049 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1050 _("Changed connection status to %d\n"), connection.status);
1051 memcpy (&(connection.peer), peer, sizeof (struct GNUNET_PeerIdentity));
1052
1053 struct GNUNET_CONTAINER_SList_Iterator iterator =
1054 GNUNET_CONTAINER_slist_begin (clients);
1055 do
1056 {
1057 struct VoipClient *conversation_client =
1058 (struct VoipClient *) GNUNET_CONTAINER_slist_get (&iterator,
1059 NULL);
1060 struct GNUNET_SERVER_Client *client = conversation_client->client;
1061 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Client found: %p\n"),
1062 client);
1063
1064 if (NULL ==
1065 GNUNET_SERVER_notify_transmit_ready (client,
1066 sizeof (struct
1067 ServerClientSessionInitiateMessage),
1068 MAX_TRANSMIT_DELAY,
1069 &transmit_server_initiate_message,
1070 (void *) peer))
1071 {
1072 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1073 _
1074 ("Could not queue ServerClientSessionInitiateMessage\n"));
1075 }
1076
1077 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Client notified.\n"));
1078 }
1079 while (GNUNET_OK == GNUNET_CONTAINER_slist_next (&iterator));
1080
1081 GNUNET_CONTAINER_slist_iter_destroy (&iterator);
1082
1083 }
1084
1085 return GNUNET_OK;
1086}
1087
1088/**
1089* Function to handle an accept messaage incoming over mesh
1090 * @param cls closure, NULL
1091 * @param tunnel the tunnel over which the message arrived
1092 * @pram tunnel_ctx the tunnel context, can be NULL
1093 * @pram message the incoming message
1094 *
1095 * @return GNUNET_OK
1096*/
1097int
1098handle_mesh_accept_message (void *cls, struct GNUNET_MESH_Tunnel *tunnel,
1099 void **tunnel_ctx,
1100 const struct GNUNET_MessageHeader *message)
1101{
1102 static uint32_t port = 50003;
1103 //struct GNUNET_PeerIdentity *peer = (GNUNET_MESH_tunnel_get_info(tunnel, GNUNET_MESH_OPTION_PEER))->peer;
1104 const struct GNUNET_PeerIdentity *peer =
1105 GNUNET_PEER_resolve2 (tunnel->peer);
1106
1107 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1108 _
1109 ("Handling MeshSessionAccpetMessage from peer: %s (connection.peer: %s)\n"),
1110 GNUNET_i2s_full (peer), GNUNET_i2s_full (&(connection.peer)));
1111 GNUNET_MESH_receive_done (tunnel);
1112
1113 if (0 ==
1114 memcmp (peer, &(connection.peer), sizeof (struct GNUNET_PeerIdentity))
1115 && connection.status == CALLER)
1116 {
1117 tunnel_unreliable =
1118 GNUNET_MESH_tunnel_create (mesh, NULL, peer, port, GNUNET_NO,
1119 GNUNET_NO);
1120 if (NULL == tunnel_unreliable)
1121 {
1122 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1123 _("Could not create unreliable tunnel\n"));
1124
1125 status_to_listen ();
1126
1127 GNUNET_SERVER_notify_transmit_ready (connection.client,
1128 sizeof (struct
1129 ServerClientSessionRejectMessage),
1130 MAX_TRANSMIT_DELAY,
1131 &transmit_server_reject_message,
1132 NULL);
1133 return GNUNET_SYSERR;
1134 }
1135
1136 if (timeout_task != GNUNET_SCHEDULER_NO_TASK)
1137 {
1138 GNUNET_SCHEDULER_cancel (timeout_task);
1139 timeout_task = GNUNET_SCHEDULER_NO_TASK;
1140 }
1141
1142 connection.status = CONNECTED;
1143 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1144 _("Changed connection status to %d\n"), connection.status);
1145
1146 if (NULL ==
1147 GNUNET_SERVER_notify_transmit_ready (connection.client,
1148 sizeof (struct
1149 ServerClientSessionAcceptMessage),
1150 MAX_TRANSMIT_DELAY,
1151 &transmit_server_accept_message,
1152 (void *) message))
1153 {
1154 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1155 _
1156 ("Could not queue ServerClientSessionAcceptMessage\n"));
1157 return GNUNET_SYSERR;
1158 }
1159
1160 start_time = GNUNET_TIME_absolute_get ();
1161 start_helpers ();
1162 audio_task = GNUNET_SCHEDULER_add_now (&transmit_audio_task, NULL);
1163 }
1164
1165 return GNUNET_OK;
1166}
1167
1168/**
1169* Function to handle a reject messaage incoming over mesh
1170 * @param cls closure, NULL
1171 * @param tunnel the tunnel over which the message arrived
1172 * @pram tunnel_ctx the tunnel context, can be NULL
1173 * @pram message the incoming message
1174 *
1175 * @return GNUNET_OK
1176*/
1177int
1178handle_mesh_reject_message (void *cls, struct GNUNET_MESH_Tunnel *tunnel,
1179 void **tunnel_ctx,
1180 const struct GNUNET_MessageHeader *message)
1181{
1182 //struct GNUNET_PeerIdentity *peer = (GNUNET_MESH_tunnel_get_info(tunnel, GNUNET_MESH_OPTION_PEER))->peer;
1183 const struct GNUNET_PeerIdentity *peer =
1184 GNUNET_PEER_resolve2 (tunnel->peer);
1185
1186 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1187 _
1188 ("Handling MeshSessionRejectMessage from peer: %s (connection.peer: %s)\n"),
1189 GNUNET_i2s_full (peer), GNUNET_i2s_full (&(connection.peer)));
1190 GNUNET_MESH_receive_done (tunnel);
1191
1192 if (0 ==
1193 memcmp (peer, &(connection.peer), sizeof (struct GNUNET_PeerIdentity))
1194 && connection.status == CALLER)
1195 {
1196 if (NULL ==
1197 GNUNET_SERVER_notify_transmit_ready (connection.client,
1198 sizeof (struct
1199 ServerClientSessionRejectMessage),
1200 MAX_TRANSMIT_DELAY,
1201 &transmit_server_reject_message,
1202 (void *) message))
1203 {
1204 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1205 _
1206 ("Could not queue ServerClientSessionRejectMessage\n"));
1207 }
1208
1209 status_to_listen ();
1210
1211 if (NULL != tunnel_reliable)
1212 {
1213 GNUNET_MESH_tunnel_destroy (tunnel_reliable);
1214 tunnel_reliable = NULL;
1215 }
1216 }
1217
1218 return GNUNET_OK;
1219}
1220
1221/**
1222* Function to handle a terminate messaage incoming over mesh
1223 * @param cls closure, NULL
1224 * @param tunnel the tunnel over which the message arrived
1225 * @pram tunnel_ctx the tunnel context, can be NULL
1226 * @pram message the incoming message
1227 *
1228 * @return GNUNET_OK
1229*/
1230int
1231handle_mesh_terminate_message (void *cls, struct GNUNET_MESH_Tunnel *tunnel,
1232 void **tunnel_ctx,
1233 const struct GNUNET_MessageHeader *message)
1234{
1235 //struct GNUNET_PeerIdentity *peer = (GNUNET_MESH_tunnel_get_info(tunnel, GNUNET_MESH_OPTION_PEER))->peer;
1236 const struct GNUNET_PeerIdentity *peer =
1237 GNUNET_PEER_resolve2 (tunnel->peer);
1238
1239 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1240 _
1241 ("Handling MeshSessionTerminateMessage from peer: %s (connection.peer: %s)\n"),
1242 GNUNET_i2s_full (peer), GNUNET_i2s_full (&(connection.peer)));
1243 GNUNET_MESH_receive_done (tunnel);
1244
1245 if (!memcmp (peer, &(connection.peer), sizeof (struct GNUNET_PeerIdentity))
1246 && (connection.status == CONNECTED || connection.status == CALLEE))
1247 {
1248 status_to_listen ();
1249
1250 if (NULL != tunnel_unreliable)
1251 {
1252 GNUNET_MESH_tunnel_destroy (tunnel_unreliable);
1253 tunnel_unreliable = NULL;
1254 }
1255
1256 if (NULL != tunnel_reliable)
1257 {
1258 GNUNET_MESH_tunnel_destroy (tunnel_reliable);
1259 tunnel_reliable = NULL;
1260 }
1261 }
1262
1263 return GNUNET_OK;
1264}
1265
1266/**
1267* Function to handle a audio messaage incoming over mesh
1268 * @param cls closure, NULL
1269 * @param tunnel the tunnel over which the message arrived
1270 * @pram tunnel_ctx the tunnel context, can be NULL
1271 * @pram message the incoming message
1272 *
1273 * @return GNUNET_OK
1274*/
1275int
1276handle_mesh_audio_message (void *cls, struct GNUNET_MESH_Tunnel *tunnel,
1277 void **tunnel_ctx,
1278 const struct GNUNET_MessageHeader *message)
1279{
1280
1281 GNUNET_MESH_receive_done (tunnel);
1282
1283 if (CONNECTED != connection.status)
1284 return GNUNET_OK;
1285
1286
1287 struct AudioMessage *audio;
1288 size_t msg_size;
1289 msg_size = sizeof (struct AudioMessage);
1290
1291 audio = (struct AudioMessage *) message;
1292
1293 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "[RECV] %dbytes\n", audio->length);
1294
1295 if (NULL == playback_helper)
1296 return GNUNET_OK;
1297
1298 (void) GNUNET_HELPER_send (playback_helper,
1299 message, GNUNET_YES, NULL, NULL);
1300
1301 data_received++;
1302 data_received_size += msg_size;
1303
1304 return GNUNET_OK;
1305}
1306
1307/******************************************************************************/
1308/*********************** HELPER *******************/
1309/******************************************************************************/
1310
1311/**
1312* Function to process the audio from the record helper
1313 * @param cls closure, NULL
1314 * @param client NULL
1315 * @param msg the message from the helper
1316 *
1317 * @return GNUNET_OK
1318*/
1319static int
1320process_record_messages (void *cls GNUNET_UNUSED, void *client,
1321 const struct GNUNET_MessageHeader *msg)
1322{
1323 size_t msg_size;
1324 struct AudioMessage *message = (struct AudioMessage *) msg;
1325 msg_size = sizeof (struct AudioMessage);
1326
1327 GNUNET_log (GNUNET_ERROR_TYPE_INFO, " [REC] %dbyte\n", message->length);
1328 GNUNET_CONTAINER_slist_add_end (audio_buffer,
1329 GNUNET_CONTAINER_SLIST_DISPOSITION_TRANSIENT,
1330 message, msg_size);
1331
1332 return GNUNET_OK;
1333}
1334
1335/**
1336* Function to to start the playback helper
1337 *
1338 * @return 0 ok, 1 on error
1339*/
1340int
1341start_playback_helper (void)
1342{
1343 static char *playback_helper_argv[1];
1344 int success = 1;
1345
1346 playback_helper_argv[0] = "gnunet-helper-audio-playback";
1347 playback_helper = GNUNET_HELPER_start (GNUNET_NO,
1348 "gnunet-helper-audio-playback",
1349 playback_helper_argv,
1350 NULL, NULL, NULL);
1351
1352 if (NULL == playback_helper)
1353 {
1354 success = 0;
1355 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1356 _("Could not start playback audio helper.\n"));
1357 }
1358
1359 return success;
1360}
1361
1362/**
1363* Function to to start the record helper
1364 *
1365 * @return 0 ok, 1 on error
1366*/
1367int
1368start_record_helper (void)
1369{
1370 static char *record_helper_argv[1];
1371 int success = 1;
1372
1373 record_helper_argv[0] = "gnunet-helper-audio-record";
1374 record_helper = GNUNET_HELPER_start (GNUNET_NO,
1375 "gnunet-helper-audio-record",
1376 record_helper_argv,
1377 &process_record_messages, NULL, NULL);
1378
1379 if (NULL == record_helper)
1380 {
1381 success = 0;
1382 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1383 _("Could not start record audio helper\n"));
1384 }
1385
1386 return success;
1387}
1388
1389
1390/**
1391* Function to to start both helpers
1392 *
1393 * @return 0 ok, 1 on error
1394*/
1395int
1396start_helpers (void)
1397{
1398
1399 if (0 == start_playback_helper () || 0 == start_record_helper ())
1400 {
1401 stop_helpers ();
1402 return 0;
1403 }
1404
1405 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Started helpers\n"));
1406
1407 return 1;
1408}
1409
1410/**
1411* Function to to stop the playback helper
1412*/
1413void
1414stop_playback_helper (void)
1415{
1416 if (NULL != playback_helper)
1417 {
1418 GNUNET_HELPER_stop (playback_helper, GNUNET_NO);
1419 playback_helper = NULL;
1420
1421 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Stopped playback helper\n"));
1422 }
1423}
1424
1425/**
1426* Function to to stop the record helper
1427*/
1428void
1429stop_record_helper (void)
1430{
1431 if (NULL != record_helper)
1432 {
1433 GNUNET_HELPER_stop (record_helper, GNUNET_NO);
1434 record_helper = NULL;
1435
1436 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Stopped record helper\n"));
1437 }
1438}
1439
1440/**
1441* Function to stop both audio helpers
1442*/
1443void
1444stop_helpers (void)
1445{
1446 stop_playback_helper ();
1447 stop_record_helper ();
1448}
1449
1450/******************************************************************************/
1451/*********************** TUNNEL HANDLING *******************/
1452/******************************************************************************/
1453
1454/**
1455 * Method called whenever another peer has added us to a tunnel
1456 * the other peer initiated.
1457 *
1458 * @param cls closure
1459 * @param tunnel new handle to the tunnel
1460 * @param initiator peer that started the tunnel
1461 * @param port port
1462 * @return initial tunnel context for the tunnel (can be NULL -- that's not an error)
1463 */
1464static void *
1465inbound_tunnel (void *cls, struct GNUNET_MESH_Tunnel *tunnel,
1466 const struct GNUNET_PeerIdentity *initiator, uint32_t port)
1467{
1468 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1469 _("Received incoming tunnel on port %d\n"), port);
1470 if (50003 == port)
1471 {
1472 tunnel_unreliable = tunnel;
1473
1474 start_time = GNUNET_TIME_absolute_get ();
1475
1476 start_helpers ();
1477 audio_task = GNUNET_SCHEDULER_add_now (&transmit_audio_task, NULL);
1478 }
1479
1480 return NULL;
1481}
1482
1483
1484/**
1485 * Function called whenever an inbound tunnel is destroyed. Should clean up
1486 * any associated state.
1487 *
1488 * @param cls closure (set from GNUNET_MESH_connect)
1489 * @param tunnel connection to the other end (henceforth invalid)
1490 * @param tunnel_ctx place where local state associated
1491 * with the tunnel is stored
1492 */
1493static void
1494inbound_end (void *cls, const struct GNUNET_MESH_Tunnel *tunnel,
1495 void *tunnel_ctx)
1496{
1497 if (tunnel == tunnel_unreliable)
1498 {
1499 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Tunnel closed: audio\n");
1500
1501 stop_helpers ();
1502 tunnel_unreliable = NULL;
1503 }
1504
1505 if (tunnel == tunnel_reliable)
1506 {
1507 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Tunnel closed: control\n");
1508
1509 if (LISTEN != connection.status && NULL != connection.client)
1510 {
1511 if (NULL ==
1512 GNUNET_SERVER_notify_transmit_ready (connection.client,
1513 sizeof (struct
1514 ServerClientSessionTerminateMessage),
1515 MAX_TRANSMIT_DELAY,
1516 &transmit_server_terminate_message,
1517 NULL))
1518 {
1519 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1520 _
1521 ("Could not queue ServerClientSessionTerminateMessage\n"));
1522 }
1523 }
1524
1525 status_to_listen ();
1526 }
1527}
1528
1529/******************************************************************************/
1530/*********************** CLIENT HANDLING *******************/
1531/******************************************************************************/
1532
1533/**
1534 * A client connected.
1535 *
1536 * @param cls closure, NULL
1537 * @param client identification of the client
1538 */
1539
1540static void
1541handle_client_connect (void *cls, struct GNUNET_SERVER_Client *cl)
1542{
1543 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Client connected\n");
1544 struct ServerClientMissedCallMessage *message;
1545 size_t msg_size;
1546 struct VoipClient c;
1547 c.client = cl;
1548
1549 GNUNET_CONTAINER_slist_add_end (clients,
1550 GNUNET_CONTAINER_SLIST_DISPOSITION_TRANSIENT,
1551 &c, sizeof (struct VoipClient));
1552 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Client added: %p\n"), cl);
1553
1554 if (0 < GNUNET_CONTAINER_slist_count (missed_calls))
1555 {
1556 int i = 0;
1557 msg_size =
1558 sizeof (struct ServerClientMissedCallMessage) +
1559 GNUNET_CONTAINER_slist_count (missed_calls) *
1560 sizeof (struct MissedCall);
1561 message =
1562 (struct ServerClientMissedCallMessage *) GNUNET_malloc (msg_size);
1563
1564 message->header.size = htons (msg_size);
1565 message->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_SC_MISSED_CALL);
1566 message->number = GNUNET_CONTAINER_slist_count (missed_calls);
1567
1568 struct GNUNET_CONTAINER_SList_Iterator iterator =
1569 GNUNET_CONTAINER_slist_begin (missed_calls);
1570 do
1571 {
1572 memcpy (&(message->missed_call[i]),
1573 GNUNET_CONTAINER_slist_get (&iterator, NULL),
1574 sizeof (struct MissedCall));
1575 i++;
1576 }
1577 while (GNUNET_OK == GNUNET_CONTAINER_slist_next (&iterator));
1578
1579 GNUNET_CONTAINER_slist_iter_destroy (&iterator);
1580 GNUNET_CONTAINER_slist_clear (missed_calls);
1581
1582
1583 if (NULL ==
1584 GNUNET_SERVER_notify_transmit_ready (cl, msg_size,
1585 MAX_TRANSMIT_DELAY,
1586 &transmit_server_missed_call_message,
1587 (void *) message))
1588 {
1589 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1590 _("Could not queue ServerClientMissedCallMessage\n"));
1591 GNUNET_free (message);
1592 }
1593 }
1594
1595 return;
1596}
1597
1598/**
1599 * A client disconnected. Remove all of its data structure entries.
1600 *
1601 * @param cls closure, NULL
1602 * @param client identification of the client
1603 */
1604static void
1605handle_client_disconnect (void *cls, struct GNUNET_SERVER_Client *cl)
1606{
1607 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Client disconnected\n");
1608
1609 if (connection.client == cl)
1610 {
1611 if (CONNECTED == connection.status)
1612 {
1613 terminate_call ();
1614 }
1615 else
1616 {
1617 status_to_listen ();
1618 }
1619 }
1620
1621 struct GNUNET_CONTAINER_SList_Iterator iterator =
1622 GNUNET_CONTAINER_slist_begin (clients);
1623 do
1624 {
1625 if (((struct VoipClient *)
1626 GNUNET_CONTAINER_slist_get (&iterator, NULL))->client == cl)
1627 {
1628 GNUNET_CONTAINER_slist_erase (&iterator);
1629 }
1630 }
1631 while (GNUNET_OK == GNUNET_CONTAINER_slist_next (&iterator));
1632
1633 GNUNET_CONTAINER_slist_iter_destroy (&iterator);
1634
1635 return;
1636}
1637
1638/******************************************************************************/
1639/*********************** SERVICE *******************/
1640/******************************************************************************/
1641
1642/**
1643 * Shutdown nicely
1644 *
1645 * @param cls closure, NULL
1646 * @param tc the task context
1647 */
1648static void
1649do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1650{
1651 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Shutdown\n");
1652
1653 stop_helpers ();
1654
1655 if (NULL != tunnel_reliable)
1656 {
1657 GNUNET_MESH_tunnel_destroy (tunnel_reliable);
1658 }
1659
1660 if (NULL != tunnel_unreliable)
1661 {
1662 GNUNET_MESH_tunnel_destroy (tunnel_unreliable);
1663 }
1664
1665 if (NULL != mesh)
1666 {
1667 GNUNET_MESH_disconnect (mesh);
1668 }
1669
1670 if (NULL != nc)
1671 {
1672 GNUNET_SERVER_notification_context_destroy (nc);
1673 nc = NULL;
1674 }
1675
1676 GNUNET_CONTAINER_slist_destroy (audio_buffer);
1677 GNUNET_CONTAINER_slist_destroy (clients);
1678 GNUNET_CONTAINER_slist_destroy (missed_calls);
1679 GNUNET_CONTAINER_slist_destroy (peers_to_notify);
1680}
1681
1682
1683/**
1684 * Handler array for traffic received
1685 */
1686static struct GNUNET_MESH_MessageHandler mesh_handlers[] = {
1687 {&handle_mesh_initiate_message,
1688 GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_SESSION_INITIATE,
1689 sizeof (struct MeshSessionInitiateMessage)},
1690 {&handle_mesh_accept_message, GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_SESSION_ACCEPT,
1691 sizeof (struct MeshSessionAcceptMessage)},
1692 {&handle_mesh_reject_message, GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_SESSION_REJECT,
1693 sizeof (struct MeshSessionRejectMessage)},
1694 {&handle_mesh_terminate_message,
1695 GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_SESSION_TERMINATE,
1696 sizeof (struct MeshSessionTerminateMessage)},
1697 {&handle_mesh_audio_message, GNUNET_MESSAGE_TYPE_CONVERSATION_AUDIO,
1698 sizeof (struct AudioMessage)},
1699 {NULL, 0, 0}
1700};
1701
1702/**
1703 * Main function that will be run by the scheduler.
1704 *
1705 * @param cls closure
1706 * @param server server handle
1707 * @param c configuration
1708 */
1709static void
1710run (void *cls, struct GNUNET_SERVER_Handle *server,
1711 const struct GNUNET_CONFIGURATION_Handle *c)
1712{
1713
1714 static uint32_t ports[] = { 50002, 50003, NULL };
1715 cfg = c;
1716
1717 mesh = GNUNET_MESH_connect (cfg,
1718 NULL,
1719 &inbound_tunnel,
1720 &inbound_end, mesh_handlers, ports);
1721
1722 if (NULL == mesh)
1723 {
1724 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Couldn't connect to mesh\n");
1725 return;
1726 }
1727 else
1728 {
1729 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Connected to mesh\n");
1730 }
1731
1732 static const struct GNUNET_SERVER_MessageHandler server_handlers[] = {
1733 {&handle_session_initiate_message, NULL,
1734 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_SESSION_INITIATE,
1735 sizeof (struct ClientServerSessionInitiateMessage)},
1736 {&handle_session_accept_message, NULL,
1737 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_SESSION_ACCEPT,
1738 sizeof (struct ClientServerSessionAcceptMessage)},
1739 {&handle_session_reject_message, NULL,
1740 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_SESSION_REJECT,
1741 sizeof (struct ClientServerSessionRejectMessage)},
1742 {&handle_session_terminate_message, NULL,
1743 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_SESSION_TERMINATE,
1744 sizeof (struct ClientServerSessionTerminateMessage)},
1745 {NULL, NULL, 0, 0}
1746 };
1747
1748 connection.status = LISTEN;
1749
1750 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Changed connection status to %d\n"),
1751 connection.status);
1752
1753 nc = GNUNET_SERVER_notification_context_create (server, 16);
1754
1755 GNUNET_SERVER_add_handlers (server, server_handlers);
1756 GNUNET_SERVER_connect_notify (server, &handle_client_connect, NULL);
1757 GNUNET_SERVER_disconnect_notify (server, &handle_client_disconnect, NULL);
1758 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &do_shutdown,
1759 NULL);
1760
1761 clients = GNUNET_CONTAINER_slist_create ();
1762
1763 // Missed calls
1764 missed_calls = GNUNET_CONTAINER_slist_create ();
1765 peers_to_notify = GNUNET_CONTAINER_slist_create ();
1766 audio_buffer = GNUNET_CONTAINER_slist_create ();
1767
1768 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Voip service running\n"));
1769}
1770
1771/**
1772 * The main function for the conversation service.
1773 *
1774 * @param argc number of arguments from the command line
1775 * @param argv command line arguments
1776 * @return 0 ok, 1 on error
1777 */
1778int
1779main (int argc, char *const *argv)
1780{
1781 return (GNUNET_OK ==
1782 GNUNET_SERVICE_run (argc, argv, "conversation", GNUNET_SERVICE_OPTION_NONE,
1783 &run, NULL)) ? 0 : 1;
1784}
1785
1786/* end of gnunet-service-conversation.c */
diff --git a/src/conversation/gnunet_conversation.h b/src/conversation/gnunet_conversation.h
new file mode 100644
index 000000000..8b79cb73b
--- /dev/null
+++ b/src/conversation/gnunet_conversation.h
@@ -0,0 +1,163 @@
1/*
2 This file is part of GNUnet
3 (C)
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 include/gnunet_conversation.h
23 * @brief Header to the conversation service
24 * @author Simon Dieterle
25 * @author Andreas Fuchs
26 */
27#ifndef GNUNET_CONVERSATION_H
28#define GNUNET_CONVERSATION_H
29
30#ifdef __cplusplus
31extern "C"
32{
33#if 0 /* keep Emacsens' auto-indent happy */
34}
35#endif
36#endif
37
38#define MAX_TRANSMIT_DELAY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 60)
39
40/**
41* Reasons for rejecting an incoming call
42*/
43enum reject_reason
44{
45 REJECT_REASON_GENERIC = 0,
46 REJECT_REASON_NOT_AVAILABLE,
47 REJECT_REASON_NO_CLIENT,
48 REJECT_REASON_ACTIVE_CALL,
49 REJECT_REASON_NO_ANSWER
50};
51
52/*
53* The possible connection status
54*/
55enum connection_status
56{
57 LISTEN,
58 CALLER,
59 CALLEE,
60 CONNECTED
61};
62
63/**
64 * VoipClient.
65 */
66struct VoipClient
67{
68 /**
69 * Handle for a conversation client.
70 */
71 struct GNUNET_SERVER_Client *client;
72};
73
74/**
75* The connection status of the service
76*/
77struct ConnectionStatus
78{
79 /**
80 * The client which is in interaction
81 */
82 struct GNUNET_SERVER_Client *client;
83
84 /**
85 * The PeerIdentity of the peer
86 */
87 struct GNUNET_PeerIdentity peer;
88
89 /**
90 * The status (see enum)
91 */
92 int status;
93};
94
95/**
96* Iformation about a missed call
97*/
98struct MissedCall
99{
100 /**
101 * The PeerIdentity of the peer
102 */
103 struct GNUNET_PeerIdentity peer;
104
105 /**
106 * The time the call was
107 */
108 struct GNUNET_TIME_Absolute time;
109
110};
111
112/**
113* Transmit a mesh message
114 * @param cls closure, NULL
115 * @param size number of bytes available in buf
116 * @param buf where the callee should write the error message
117 * @return number of bytes written to buf
118 */
119static size_t transmit_mesh_message (void *cls, size_t size, void *buf);
120
121/**
122 * Function called to send a peer no answer message to the client.
123 * "buf" will be NULL and "size" zero if the socket was closed for writing in
124 * the meantime.
125 *
126 * @param cls closure, NULL
127 * @param size number of bytes available in buf
128 * @param buf where the callee should write the peer no answer message
129 * @return number of bytes written to buf
130 */
131static size_t
132transmit_server_no_answer_message (void *cls, size_t size, void *buf);
133
134/**
135 * Task to schedule a audio transmission.
136 *
137 * @param cls Closure.
138 * @param tc Task Context.
139 */
140static void
141transmit_audio_task (void *cls,
142 const struct GNUNET_SCHEDULER_TaskContext *tc);
143
144/**
145* Start the audio helpers
146*/
147int start_helpers (void);
148
149/**
150* Stop the audio helpers
151*/
152void stop_helpers (void);
153
154
155
156#if 0 /* keep Emacsens' auto-indent happy */
157{
158#endif
159#ifdef __cplusplus
160}
161#endif
162
163#endif
diff --git a/src/conversation/gnunet_protocols_conversation.h b/src/conversation/gnunet_protocols_conversation.h
new file mode 100644
index 000000000..b6ebd903b
--- /dev/null
+++ b/src/conversation/gnunet_protocols_conversation.h
@@ -0,0 +1,300 @@
1/*
2 This file is part of GNUnet.
3 (C)
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 include/gnunet_protocols_conversation.h
23 * @brief constants for network protocols
24 * @author Siomon Dieterle
25 * @author Andreas Fuchs
26 */
27
28#ifndef GNUNET_PROTOCOLS_CONVERSATION_H
29#define GNUNET_PROTOCOLS_CONVERSATION_H
30
31#ifdef __cplusplus
32extern "C"
33{
34#if 0 /* keep Emacsens' auto-indent happy */
35}
36#endif
37#endif
38
39
40/************************************************************************************************************************
41* Messages for the Client <-> Server communication
42*/
43
44/**
45* Client <-> Server message to initiate a new call
46*/
47#define GNUNET_MESSAGE_TYPE_CONVERSATION_CS_SESSION_INITIATE 30002
48struct ClientServerSessionInitiateMessage
49{
50 struct GNUNET_MessageHeader header;
51 struct GNUNET_PeerIdentity peer;
52};
53
54/**
55* Client <-> Server meessage to accept an incoming call
56*/
57#define GNUNET_MESSAGE_TYPE_CONVERSATION_CS_SESSION_ACCEPT 30003
58struct ClientServerSessionAcceptMessage
59{
60 struct GNUNET_MessageHeader header;
61};
62
63/**
64* Client <-> Server message to reject an incoming call
65*/
66#define GNUNET_MESSAGE_TYPE_CONVERSATION_CS_SESSION_REJECT 30004
67struct ClientServerSessionRejectMessage
68{
69 struct GNUNET_MessageHeader header;
70 int reason;
71};
72
73/**
74* Client <-> Server message to terminat a call
75*/
76#define GNUNET_MESSAGE_TYPE_CONVERSATION_CS_SESSION_TERMINATE 30005
77struct ClientServerSessionTerminateMessage
78{
79 struct GNUNET_MessageHeader header;
80};
81
82/**
83* Client <-> Server message to initiate a new call
84*/
85#define GNUNET_MESSAGE_TYPE_CONVERSATION_CS_TEST 30099
86struct ClientServerTestMessage
87{
88 struct GNUNET_MessageHeader header;
89 struct GNUNET_PeerIdentity peer;
90};
91
92/************************************************************************************************************************
93* Messages for the Server <-> Client communication
94*/
95
96/**
97* Server <-> Client message to initiate a new call
98*/
99#define GNUNET_MESSAGE_TYPE_CONVERSATION_SC_SESSION_INITIATE 30006
100struct ServerClientSessionInitiateMessage
101{
102 struct GNUNET_MessageHeader header;
103 struct GNUNET_PeerIdentity peer;
104};
105
106/**
107* Server <-> Client meessage to accept an incoming call
108*/
109#define GNUNET_MESSAGE_TYPE_CONVERSATION_SC_SESSION_ACCEPT 30007
110struct ServerClientSessionAcceptMessage
111{
112 struct GNUNET_MessageHeader header;
113};
114
115/**
116* Server <-> Client message to reject an incoming call
117*/
118#define GNUNET_MESSAGE_TYPE_CONVERSATION_SC_SESSION_REJECT 30008
119struct ServerClientSessionRejectMessage
120{
121 struct GNUNET_MessageHeader header;
122 int reason;
123 int notify;
124};
125
126/**
127* Server <-> Client message to terminat a call
128*/
129#define GNUNET_MESSAGE_TYPE_CONVERSATION_SC_SESSION_TERMINATE 30009
130struct ServerClientSessionTerminateMessage
131{
132 struct GNUNET_MessageHeader header;
133};
134
135/**
136* Server <-> Client message to signalize the client that the service is already in use
137*/
138#define GNUNET_MESSAGE_TYPE_CONVERSATION_SC_SERVICE_BLOCKED 30010
139struct ServerClientServiceBlockedMessage
140{
141 struct GNUNET_MessageHeader header;
142};
143
144/**
145* Server <-> Client message to signalize the client that the called peer is not connected
146*/
147#define GNUNET_MESSAGE_TYPE_CONVERSATION_SC_PEER_NOT_CONNECTED 30011
148struct ServerClientPeerNotConnectedMessage
149{
150 struct GNUNET_MessageHeader header;
151};
152
153/**
154* Server <-> Client message to signalize the client that called peer does not answer
155*/
156#define GNUNET_MESSAGE_TYPE_CONVERSATION_SC_NO_ANSWER 30012
157struct ServerClientNoAnswerMessage
158{
159 struct GNUNET_MessageHeader header;
160};
161
162/**
163* Server <-> Client message to notify client of missed call
164*/
165#define GNUNET_MESSAGE_TYPE_CONVERSATION_SC_MISSED_CALL 30013
166struct ServerClientMissedCallMessage
167{
168 struct GNUNET_MessageHeader header;
169 int number;
170 struct MissedCall *missed_call;
171};
172
173/**
174* Server <-> Client message to signalize the client that there occured an error
175*/
176#define GNUNET_MESSAGE_TYPE_CONVERSATION_SC_ERROR 30014
177struct ServerClientErrorMessage
178{
179 struct GNUNET_MessageHeader header;
180};
181
182/**
183* Server <-> Client message to notify client of peer being available
184*/
185#define GNUNET_MESSAGE_TYPE_CONVERSATION_SC_PEER_AVAILABLE 30015
186struct ServerClientPeerAvailableMessage
187{
188 struct GNUNET_MessageHeader header;
189 struct GNUNET_PeerIdentity peer;
190 struct GNUNET_TIME_Absolute time;
191};
192
193/************************************************************************************************************************
194* Messages for the Mesh communication
195*/
196
197struct VoIPMeshMessageHeader
198{
199 struct GNUNET_MessageHeader header;
200 int SequenceNumber;
201 struct GNUNET_TIME_Absolute time;
202};
203
204/**
205* Mesh message to sinal the remote peer the wish to initiate a new call
206*/
207#define GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_SESSION_INITIATE 40000
208struct MeshSessionInitiateMessage
209{
210 struct GNUNET_MessageHeader header;
211 int SequenceNumber;
212 struct GNUNET_TIME_Absolute time;
213 struct GNUNET_PeerIdentity peer;
214};
215
216/**
217* Mesh message to signal the remote peer the acceptance of an initiated call
218*/
219#define GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_SESSION_ACCEPT 40001
220struct MeshSessionAcceptMessage
221{
222 struct GNUNET_MessageHeader header;
223 int SequenceNumber;
224 struct GNUNET_TIME_Absolute time;
225};
226
227/**
228* Mesh message to reject an a wish to initiate a new call
229*/
230#define GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_SESSION_REJECT 40002
231struct MeshSessionRejectMessage
232{
233 struct GNUNET_MessageHeader header;
234 int SequenceNumber;
235 struct GNUNET_TIME_Absolute time;
236 int reason;
237 int notify;
238};
239
240/**
241* Mesh message to signal a remote peer the terminatation of a call
242*/
243#define GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_SESSION_TERMINATE 40003
244struct MeshSessionTerminateMessage
245{
246 struct GNUNET_MessageHeader header;
247 int SequenceNumber;
248 struct GNUNET_TIME_Absolute time;
249};
250
251/**
252* Server <-> Client message to notify client of peer being available
253*/
254#define GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PEER_AVAILABLE 40004
255struct MeshPeerAvailableMessage
256{
257 struct GNUNET_MessageHeader header;
258 int SequenceNumber;
259 struct GNUNET_TIME_Absolute time;
260 struct GNUNET_PeerIdentity peer;
261 struct GNUNET_TIME_Absolute call;
262};
263
264/************************************************************************************************************************
265* Messages for the audio communication
266*/
267
268
269#define GNUNET_MESSAGE_TYPE_CONVERSATION_TEST 50001
270struct TestMessage
271{
272 struct GNUNET_MessageHeader header;
273};
274
275/**
276* Message to transmit the audio
277*/
278#define GNUNET_MESSAGE_TYPE_CONVERSATION_AUDIO 50000
279struct AudioMessage
280{
281 struct GNUNET_MessageHeader header;
282 int SequenceNumber;
283 struct GNUNET_TIME_Absolute time;
284 int length;
285 int encrypted;
286 uint8_t audio[200];
287
288};
289
290
291#if 0 /* keep Emacsens' auto-indent happy */
292{
293#endif
294#ifdef __cplusplus
295}
296#endif
297
298/* ifndef GNUNET_PROTOCOLS_CONVERSATION_H */
299#endif
300/* end of gnunet_protocols_conversation.h */
diff --git a/src/conversation/mst.c b/src/conversation/mst.c
new file mode 100644
index 000000000..c348bdea9
--- /dev/null
+++ b/src/conversation/mst.c
@@ -0,0 +1,288 @@
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 conversation/mst.c
23 * @brief Message tokenizer
24 * @author Christian Grothoff
25 */
26
27#include <gnunet/platform.h>
28#include <gnunet/gnunet_constants.h>
29
30/**
31 * To what multiple do we align messages? 8 byte should suffice for everyone
32 * for now.
33 */
34#define ALIGN_FACTOR 8
35
36/**
37 * Smallest supported message.
38 */
39#define MIN_BUFFER_SIZE sizeof (struct GNUNET_MessageHeader)
40
41
42/**
43 * Functions with this signature are called whenever a
44 * complete message is received by the tokenizer.
45 *
46 * @param cls closure
47 * @param message the actual message
48 */
49typedef void (*MessageTokenizerCallback) (void *cls,
50 const struct
51 GNUNET_MessageHeader *
52 message);
53
54/**
55 * Handle to a message stream tokenizer.
56 */
57struct MessageStreamTokenizer
58{
59
60 /**
61 * Function to call on completed messages.
62 */
63 MessageTokenizerCallback cb;
64
65 /**
66 * Closure for cb.
67 */
68 void *cb_cls;
69
70 /**
71 * Size of the buffer (starting at 'hdr').
72 */
73 size_t curr_buf;
74
75 /**
76 * How many bytes in buffer have we already processed?
77 */
78 size_t off;
79
80 /**
81 * How many bytes in buffer are valid right now?
82 */
83 size_t pos;
84
85 /**
86 * Beginning of the buffer. Typed like this to force alignment.
87 */
88 struct GNUNET_MessageHeader *hdr;
89
90};
91
92
93/**
94 * Create a message stream tokenizer.
95 *
96 * @param cb function to call on completed messages
97 * @param cb_cls closure for cb
98 * @return handle to tokenizer
99 */
100static struct MessageStreamTokenizer *
101mst_create (MessageTokenizerCallback cb,
102 void *cb_cls)
103{
104 struct MessageStreamTokenizer *ret;
105
106 ret = malloc (sizeof (struct MessageStreamTokenizer));
107 if (NULL == ret)
108 {
109 fprintf (stderr, "Failed to allocate buffer for tokenizer\n");
110 exit (1);
111 }
112 ret->hdr = malloc (MIN_BUFFER_SIZE);
113 if (NULL == ret->hdr)
114 {
115 fprintf (stderr, "Failed to allocate buffer for alignment\n");
116 exit (1);
117 }
118 ret->curr_buf = MIN_BUFFER_SIZE;
119 ret->cb = cb;
120 ret->cb_cls = cb_cls;
121 return ret;
122}
123
124
125/**
126 * Add incoming data to the receive buffer and call the
127 * callback for all complete messages.
128 *
129 * @param mst tokenizer to use
130 * @param buf input data to add
131 * @param size number of bytes in buf
132 * @return GNUNET_OK if we are done processing (need more data)
133 * GNUNET_SYSERR if the data stream is corrupt
134 */
135static int
136mst_receive (struct MessageStreamTokenizer *mst,
137 const char *buf, size_t size)
138{
139 const struct GNUNET_MessageHeader *hdr;
140 size_t delta;
141 uint16_t want;
142 char *ibuf;
143 int need_align;
144 unsigned long offset;
145 int ret;
146
147 ret = GNUNET_OK;
148 ibuf = (char *) mst->hdr;
149 while (mst->pos > 0)
150 {
151do_align:
152 if ((mst->curr_buf - mst->off < sizeof (struct GNUNET_MessageHeader)) ||
153 (0 != (mst->off % ALIGN_FACTOR)))
154 {
155 /* need to align or need more space */
156 mst->pos -= mst->off;
157 memmove (ibuf, &ibuf[mst->off], mst->pos);
158 mst->off = 0;
159 }
160 if (mst->pos - mst->off < sizeof (struct GNUNET_MessageHeader))
161 {
162 delta =
163 GNUNET_MIN (sizeof (struct GNUNET_MessageHeader) -
164 (mst->pos - mst->off), size);
165 memcpy (&ibuf[mst->pos], buf, delta);
166 mst->pos += delta;
167 buf += delta;
168 size -= delta;
169 }
170 if (mst->pos - mst->off < sizeof (struct GNUNET_MessageHeader))
171 {
172 return GNUNET_OK;
173 }
174 hdr = (const struct GNUNET_MessageHeader *) &ibuf[mst->off];
175 want = ntohs (hdr->size);
176 if (want < sizeof (struct GNUNET_MessageHeader))
177 {
178 fprintf (stderr,
179 "Received invalid message from stdin\n");
180 exit (1);
181 }
182 if (mst->curr_buf - mst->off < want)
183 {
184 /* need more space */
185 mst->pos -= mst->off;
186 memmove (ibuf, &ibuf[mst->off], mst->pos);
187 mst->off = 0;
188 }
189 if (want > mst->curr_buf)
190 {
191 mst->hdr = realloc (mst->hdr, want);
192 if (NULL == mst->hdr)
193 {
194 fprintf (stderr, "Failed to allocate buffer for alignment\n");
195 exit (1);
196 }
197 ibuf = (char *) mst->hdr;
198 mst->curr_buf = want;
199 }
200 hdr = (const struct GNUNET_MessageHeader *) &ibuf[mst->off];
201 if (mst->pos - mst->off < want)
202 {
203 delta = GNUNET_MIN (want - (mst->pos - mst->off), size);
204 memcpy (&ibuf[mst->pos], buf, delta);
205 mst->pos += delta;
206 buf += delta;
207 size -= delta;
208 }
209 if (mst->pos - mst->off < want)
210 {
211 return GNUNET_OK;
212 }
213 mst->cb (mst->cb_cls, hdr);
214 mst->off += want;
215 if (mst->off == mst->pos)
216 {
217 /* reset to beginning of buffer, it's free right now! */
218 mst->off = 0;
219 mst->pos = 0;
220 }
221 }
222 while (size > 0)
223 {
224 if (size < sizeof (struct GNUNET_MessageHeader))
225 break;
226 offset = (unsigned long) buf;
227 need_align = (0 != offset % ALIGN_FACTOR) ? GNUNET_YES : GNUNET_NO;
228 if (GNUNET_NO == need_align)
229 {
230 /* can try to do zero-copy and process directly from original buffer */
231 hdr = (const struct GNUNET_MessageHeader *) buf;
232 want = ntohs (hdr->size);
233 if (want < sizeof (struct GNUNET_MessageHeader))
234 {
235 fprintf (stderr,
236 "Received invalid message from stdin\n");
237 exit (1);
238 }
239 if (size < want)
240 break; /* or not, buffer incomplete, so copy to private buffer... */
241 mst->cb (mst->cb_cls, hdr);
242 buf += want;
243 size -= want;
244 }
245 else
246 {
247 /* need to copy to private buffer to align;
248 * yes, we go a bit more spagetti than usual here */
249 goto do_align;
250 }
251 }
252 if (size > 0)
253 {
254 if (size + mst->pos > mst->curr_buf)
255 {
256 mst->hdr = realloc (mst->hdr, size + mst->pos);
257 if (NULL == mst->hdr)
258 {
259 fprintf (stderr, "Failed to allocate buffer for alignment\n");
260 exit (1);
261 }
262 ibuf = (char *) mst->hdr;
263 mst->curr_buf = size + mst->pos;
264 }
265 if (mst->pos + size > mst->curr_buf)
266 {
267 fprintf (stderr,
268 "Assertion failed\n");
269 exit (1);
270 }
271 memcpy (&ibuf[mst->pos], buf, size);
272 mst->pos += size;
273 }
274 return ret;
275}
276
277
278/**
279 * Destroys a tokenizer.
280 *
281 * @param mst tokenizer to destroy
282 */
283static void
284mst_destroy (struct MessageStreamTokenizer *mst)
285{
286 free (mst->hdr);
287 free (mst);
288}
diff --git a/src/conversation/test_voip.api.c b/src/conversation/test_voip.api.c
new file mode 100644
index 000000000..dd20995d2
--- /dev/null
+++ b/src/conversation/test_voip.api.c
@@ -0,0 +1,85 @@
1/*
2 This file is part of GNUnet.
3 (C)
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 * @file voip/src/test_voip_api.c
22 * @brief testcase for voip_api.c
23 */
24#include <gnunet/platform.h>
25#include <gnunet/gnunet_util_lib.h>
26#include "gnunet_voip_service.h"
27
28
29static int ok = 1;
30
31
32static void
33run (void *cls,
34 char *const *args,
35 const char *cfgfile,
36 const struct GNUNET_CONFIGURATION_Handle *cfg)
37{
38 ok = 0;
39}
40
41
42static int
43check ()
44{
45 char *const argv[] = { "test-voip-api", NULL };
46 struct GNUNET_GETOPT_CommandLineOption options[] = {
47 GNUNET_GETOPT_OPTION_END
48 };
49 struct GNUNET_OS_Process *proc;
50 char *path = GNUNET_OS_get_libexec_binary_path ( "gnunet-service-voip");
51 if (NULL == path)
52 {
53 fprintf (stderr, "Service executable not found `%s'\n", "gnunet-service-voip");
54 return;
55 }
56 proc = GNUNET_OS_start_process (GNUNET_NO, GNUNET_OS_INHERIT_STD_ALL, NULL, NULL,
57 path,
58 "gnunet-service-voip",
59 NULL);
60
61 GNUNET_free (path);
62 GNUNET_assert (NULL != proc);
63 GNUNET_PROGRAM_run (1, argv, "test-ext-voip", "nohelp",
64 options, &run, &ok);
65 if (0 != GNUNET_OS_process_kill (proc, SIGTERM))
66 {
67 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");
68 ok = 1;
69 }
70 GNUNET_OS_process_wait (proc);
71 GNUNET_OS_process_destroy (proc);
72 return ok;
73}
74
75
76int
77main (int argc, char *argv[])
78{
79 GNUNET_log_setup ("test_voip_api",
80 "WARNING",
81 NULL);
82 return check ();
83}
84
85/* end of test_voip_api.c */