diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am | 1 | ||||
-rw-r--r-- | src/conversation/Makefile.am | 91 | ||||
-rw-r--r-- | src/conversation/conversation.conf.in | 7 | ||||
-rw-r--r-- | src/conversation/conversation_api.c | 751 | ||||
-rw-r--r-- | src/conversation/gnunet-conversation.c | 419 | ||||
-rw-r--r-- | src/conversation/gnunet-helper-audio-playback.c | 391 | ||||
-rw-r--r-- | src/conversation/gnunet-helper-audio-record.c | 446 | ||||
-rw-r--r-- | src/conversation/gnunet-service-conversation.c | 1786 | ||||
-rw-r--r-- | src/conversation/gnunet_conversation.h | 163 | ||||
-rw-r--r-- | src/conversation/gnunet_protocols_conversation.h | 300 | ||||
-rw-r--r-- | src/conversation/mst.c | 288 | ||||
-rw-r--r-- | src/conversation/test_voip.api.c | 85 |
12 files changed, 4728 insertions, 0 deletions
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 @@ | |||
1 | SUBDIRS = . | ||
2 | |||
3 | INCLUDES = \ | ||
4 | -I$(top_srcdir)/src/include \ | ||
5 | -I$(top_srcdir) | ||
6 | |||
7 | AM_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 | |||
17 | lib_LTLIBRARIES = libgnunetconversation.la | ||
18 | |||
19 | pkgcfgdir= $(prefix)/share/gnunet/config.d/ | ||
20 | |||
21 | libexecdir= $(prefix)/lib/gnunet/libexec/ | ||
22 | |||
23 | libgnunetconversation_la_SOURCES = \ | ||
24 | conversation_api.c | ||
25 | libgnunetconversation_la_LIBADD = \ | ||
26 | -lgnunetutil -lgnunetgns_common -lgnunetgns | ||
27 | libgnunetconversation_la_LDFLAGS = \ | ||
28 | $(GNUNET_LDFLAGS) $(WINFLAGS) \ | ||
29 | -version-info 0:0:0 | ||
30 | |||
31 | |||
32 | bin_PROGRAMS = gnunet-conversation | ||
33 | |||
34 | libexec_PROGRAMS = gnunet-service-conversation \ | ||
35 | gnunet-helper-audio-record \ | ||
36 | gnunet-helper-audio-playback | ||
37 | |||
38 | check_PROGRAMS = \ | ||
39 | test_conversation_api | ||
40 | |||
41 | TESTS = $(check_PROGRAMS) | ||
42 | |||
43 | |||
44 | gnunet_helper_audio_record_SOURCES = \ | ||
45 | gnunet-helper-audio-record.c | ||
46 | gnunet_helper_audio_record_LDADD = \ | ||
47 | -lgnunetutil \ | ||
48 | -lpulse -lopus\ | ||
49 | $(INTLLIBS) | ||
50 | gnunet_helper_audio_record_LDFLAGS = \ | ||
51 | $(GNUNET_LDFLAGS) $(WINFLAGS) -export-dynamic | ||
52 | |||
53 | gnunet_helper_audio_playback_SOURCES = \ | ||
54 | gnunet-helper-audio-playback.c | ||
55 | gnunet_helper_audio_playback_LDADD = \ | ||
56 | -lgnunetutil \ | ||
57 | -lpulse -lopus\ | ||
58 | $(INTLLIBS) | ||
59 | gnunet_helper_audio_playback_LDFLAGS = \ | ||
60 | $(GNUNET_LDFLAGS) $(WINFLAGS) -export-dynamic | ||
61 | |||
62 | gnunet_service_conversation_SOURCES = \ | ||
63 | gnunet-service-conversation.c | ||
64 | gnunet_service_conversation_LDADD = \ | ||
65 | -lgnunetutil -lgnunetmesh -lgnunetgns_common -lgnunetgns\ | ||
66 | $(INTLLIBS) | ||
67 | gnunet_service_conversation_LDFLAGS = \ | ||
68 | $(GNUNET_LDFLAGS) $(WINFLAGS) -export-dynamic | ||
69 | |||
70 | gnunet_conversation_SOURCES = \ | ||
71 | gnunet-conversation.c | ||
72 | gnunet_conversation_LDADD = \ | ||
73 | -lgnunetutil -lgnunetconversation \ | ||
74 | $(INTLLIBS) | ||
75 | gnunet_conversation_LDFLAGS = \ | ||
76 | $(GNUNET_LDFLAGS) $(WINFLAGS) -export-dynamic | ||
77 | |||
78 | |||
79 | |||
80 | |||
81 | |||
82 | test_conversation_api_SOURCES = \ | ||
83 | test_conversation_api.c | ||
84 | test_conversation_api_LDADD = \ | ||
85 | $(top_builddir)/src/conversation/libgnunetconversation.la \ | ||
86 | -lgnunetutil | ||
87 | test_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] | ||
2 | BINARY = gnunet-service-conversation | ||
3 | UNIXPATH = /tmp/gnunet-service-conversation.sock | ||
4 | HOME = $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 | |||
48 | enum GNUNET_CONVERSATION_CallType | ||
49 | { | ||
50 | CALLER = 0, | ||
51 | CALLEE | ||
52 | }; | ||
53 | |||
54 | /** | ||
55 | * Information about a call | ||
56 | */ | ||
57 | struct 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 | */ | ||
79 | struct 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 | */ | ||
140 | static void | ||
141 | setup_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 | */ | ||
198 | static void | ||
199 | check_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 | */ | ||
219 | static void | ||
220 | check_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 | */ | ||
238 | static void | ||
239 | receive_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 | */ | ||
363 | static size_t | ||
364 | transmit_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 | */ | ||
398 | static size_t | ||
399 | transmit_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 | */ | ||
432 | static size_t | ||
433 | transmit_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 | */ | ||
468 | static size_t | ||
469 | transmit_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 | */ | ||
499 | static void | ||
500 | initiate_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 | */ | ||
521 | static void | ||
522 | accept_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 | */ | ||
536 | static void | ||
537 | reject_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 | */ | ||
551 | static void | ||
552 | terminate_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 | */ | ||
565 | static void | ||
566 | gns_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 | */ | ||
605 | static void | ||
606 | gns_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 | |||
631 | struct GNUNET_CONVERSATION_Handle * | ||
632 | GNUNET_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 | |||
687 | void | ||
688 | GNUNET_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 | |||
699 | void | ||
700 | GNUNET_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 | |||
724 | void | ||
725 | GNUNET_CONVERSATION_hangup (struct GNUNET_CONVERSATION_Handle *h) | ||
726 | { | ||
727 | if (NULL == h || NULL == h->client) | ||
728 | return; | ||
729 | |||
730 | terminate_call (h); | ||
731 | } | ||
732 | |||
733 | void | ||
734 | GNUNET_CONVERSATION_accept (struct GNUNET_CONVERSATION_Handle *h) | ||
735 | { | ||
736 | if (NULL == h || NULL == h->client) | ||
737 | return; | ||
738 | |||
739 | accept_call (h); | ||
740 | } | ||
741 | |||
742 | void | ||
743 | GNUNET_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 | */ | ||
38 | struct GNUNET_CONVERSATION_Handle *conversation = NULL; | ||
39 | |||
40 | /** | ||
41 | * Task which handles the commands | ||
42 | */ | ||
43 | static GNUNET_SCHEDULER_TaskIdentifier handle_cmd_task; | ||
44 | |||
45 | /** | ||
46 | * Function declareation for executing a action | ||
47 | */ | ||
48 | typedef int (*ActionFunction) (const char *argumetns, const void *xtra); | ||
49 | |||
50 | /** | ||
51 | * Structure which defines a command | ||
52 | */ | ||
53 | struct VoipCommand | ||
54 | { | ||
55 | const char *command; | ||
56 | ActionFunction Action; | ||
57 | const char *helptext; | ||
58 | }; | ||
59 | |||
60 | /******************************************************************************/ | ||
61 | /*********************** DECLARATIONS *************************/ | ||
62 | /******************************************************************************/ | ||
63 | |||
64 | static 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 | */ | ||
78 | void | ||
79 | call_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 | */ | ||
93 | void | ||
94 | reject_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 | */ | ||
109 | void | ||
110 | notification_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 | */ | ||
158 | void | ||
159 | missed_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 | */ | ||
168 | static int | ||
169 | do_quit (const char *args, const void *xtra) | ||
170 | { | ||
171 | return GNUNET_SYSERR; | ||
172 | } | ||
173 | |||
174 | /** | ||
175 | * | ||
176 | */ | ||
177 | static int | ||
178 | do_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 | */ | ||
187 | static int | ||
188 | do_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 | */ | ||
200 | static int | ||
201 | do_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 | */ | ||
213 | static int | ||
214 | do_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 | */ | ||
225 | static int | ||
226 | do_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 | */ | ||
237 | static int | ||
238 | do_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 | */ | ||
249 | static 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 | */ | ||
270 | static int | ||
271 | do_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 | */ | ||
302 | static void | ||
303 | do_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 | */ | ||
320 | void | ||
321 | handle_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 | |||
346 | next: | ||
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 | |||
355 | out: | ||
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 | */ | ||
368 | static void | ||
369 | run (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 | ¬ification_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 | */ | ||
394 | int | ||
395 | main (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 | */ | ||
25 | static pa_sample_spec sample_spec = { | ||
26 | .format = PA_SAMPLE_FLOAT32LE, | ||
27 | .rate = 48000, | ||
28 | .channels = 1 | ||
29 | }; | ||
30 | |||
31 | /** | ||
32 | * Pulseaudio mainloop api | ||
33 | */ | ||
34 | static pa_mainloop_api *mainloop_api = NULL; | ||
35 | |||
36 | /** | ||
37 | * Pulseaudio threaded mainloop | ||
38 | */ | ||
39 | static pa_threaded_mainloop *m = NULL; | ||
40 | |||
41 | /** | ||
42 | * Pulseaudio context | ||
43 | */ | ||
44 | static pa_context *context = NULL; | ||
45 | |||
46 | /** | ||
47 | * Pulseaudio output stream | ||
48 | */ | ||
49 | static pa_stream *stream_out = NULL; | ||
50 | |||
51 | /** | ||
52 | * Pulseaudio io events | ||
53 | */ | ||
54 | static pa_io_event *stdio_event = NULL; | ||
55 | |||
56 | /** | ||
57 | * OPUS decoder | ||
58 | */ | ||
59 | OpusDecoder *dec = NULL; | ||
60 | |||
61 | /** | ||
62 | * PCM data buffer | ||
63 | */ | ||
64 | float *pcm_buffer; | ||
65 | |||
66 | /** | ||
67 | * Length of PCM buffer | ||
68 | */ | ||
69 | int pcm_length; | ||
70 | |||
71 | /** | ||
72 | * Number of samples for one frame | ||
73 | */ | ||
74 | int frame_size; | ||
75 | |||
76 | /** | ||
77 | * The sampling rate used in Pulseaudio specification | ||
78 | */ | ||
79 | opus_int32 sampling_rate; | ||
80 | |||
81 | /** | ||
82 | * Audio buffer | ||
83 | */ | ||
84 | static void *buffer = NULL; | ||
85 | |||
86 | /** | ||
87 | * Length of audio buffer | ||
88 | */ | ||
89 | static size_t buffer_length = 0; | ||
90 | |||
91 | /** | ||
92 | * Read index for transmit buffer | ||
93 | */ | ||
94 | static size_t buffer_index = 0; | ||
95 | |||
96 | |||
97 | |||
98 | /** | ||
99 | * Message callback | ||
100 | */ | ||
101 | static void | ||
102 | stdin_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 | */ | ||
136 | static void | ||
137 | quit (int ret) | ||
138 | { | ||
139 | mainloop_api->quit (mainloop_api, ret); | ||
140 | exit (ret); | ||
141 | } | ||
142 | |||
143 | /** | ||
144 | * Write some data to the stream | ||
145 | */ | ||
146 | static void | ||
147 | do_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 | */ | ||
190 | static void | ||
191 | stream_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 | */ | ||
212 | static void | ||
213 | exit_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 | */ | ||
224 | static void | ||
225 | context_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 | |||
284 | fail: | ||
285 | quit (1); | ||
286 | |||
287 | } | ||
288 | |||
289 | /** | ||
290 | * Pulseaudio initialization | ||
291 | */ | ||
292 | void | ||
293 | pa_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 | */ | ||
345 | void | ||
346 | opus_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 | */ | ||
365 | int | ||
366 | main (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 | */ | ||
18 | static pa_sample_spec sample_spec = { | ||
19 | .format = PA_SAMPLE_FLOAT32LE, | ||
20 | .rate = 48000, | ||
21 | .channels = 1 | ||
22 | }; | ||
23 | |||
24 | /** | ||
25 | * Pulseaudio mainloop api | ||
26 | */ | ||
27 | static pa_mainloop_api *mainloop_api = NULL; | ||
28 | |||
29 | /** | ||
30 | * Pulseaudio mainloop | ||
31 | */ | ||
32 | static pa_mainloop *m = NULL; | ||
33 | |||
34 | /** | ||
35 | * Pulseaudio context | ||
36 | */ | ||
37 | static pa_context *context = NULL; | ||
38 | |||
39 | /** | ||
40 | * Pulseaudio recording stream | ||
41 | */ | ||
42 | static pa_stream *stream_in = NULL; | ||
43 | |||
44 | /** | ||
45 | * Pulseaudio io events | ||
46 | */ | ||
47 | static pa_io_event *stdio_event = NULL; | ||
48 | |||
49 | /** | ||
50 | * Message tokenizer | ||
51 | */ | ||
52 | struct MessageStreamTokenizer *stdin_mst; | ||
53 | |||
54 | /** | ||
55 | * OPUS encoder | ||
56 | */ | ||
57 | OpusEncoder *enc = NULL; | ||
58 | |||
59 | /** | ||
60 | * | ||
61 | */ | ||
62 | unsigned char *opus_data; | ||
63 | |||
64 | /** | ||
65 | * PCM data buffer for one OPUS frame | ||
66 | */ | ||
67 | float *pcm_buffer; | ||
68 | |||
69 | /** | ||
70 | * Length of the pcm data needed for one OPUS frame | ||
71 | */ | ||
72 | int pcm_length; | ||
73 | |||
74 | /** | ||
75 | * Number of samples for one frame | ||
76 | */ | ||
77 | int frame_size; | ||
78 | |||
79 | /** | ||
80 | * Maximum length of opus payload | ||
81 | */ | ||
82 | int max_payload_bytes = 1500; | ||
83 | |||
84 | /** | ||
85 | * Audio buffer | ||
86 | */ | ||
87 | static void *transmit_buffer = NULL; | ||
88 | |||
89 | /** | ||
90 | * Length of audio buffer | ||
91 | */ | ||
92 | static size_t transmit_buffer_length = 0; | ||
93 | |||
94 | /** | ||
95 | * Read index for transmit buffer | ||
96 | */ | ||
97 | static size_t transmit_buffer_index = 0; | ||
98 | |||
99 | /** | ||
100 | * Audio message skeleton | ||
101 | */ | ||
102 | struct AudioMessage *audio_message; | ||
103 | |||
104 | |||
105 | |||
106 | /** | ||
107 | * Pulseaudio shutdown task | ||
108 | */ | ||
109 | static void | ||
110 | quit (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 | */ | ||
121 | static void | ||
122 | packetizer () | ||
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 | */ | ||
171 | static void | ||
172 | stream_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 | */ | ||
215 | static void | ||
216 | exit_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 | */ | ||
226 | static void | ||
227 | stream_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 | */ | ||
289 | static void | ||
290 | context_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 | |||
349 | fail: | ||
350 | quit (1); | ||
351 | |||
352 | } | ||
353 | |||
354 | /** | ||
355 | * Pulsaudio init | ||
356 | */ | ||
357 | void | ||
358 | pa_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 | */ | ||
409 | void | ||
410 | opus_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 | */ | ||
439 | int | ||
440 | main (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 | */ | ||
47 | typedef uint32_t MESH_TunnelNumber; | ||
48 | struct 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 | */ | ||
67 | static const struct GNUNET_CONFIGURATION_Handle *cfg; | ||
68 | |||
69 | /** | ||
70 | * Head of the list of current clients. | ||
71 | */ | ||
72 | static struct GNUNET_CONTAINER_SList *clients; | ||
73 | |||
74 | /** | ||
75 | * Notification context containing all connected clients. | ||
76 | */ | ||
77 | struct GNUNET_SERVER_NotificationContext *nc = NULL; | ||
78 | |||
79 | /** | ||
80 | * The connection status | ||
81 | */ | ||
82 | static struct ConnectionStatus connection; | ||
83 | |||
84 | /** | ||
85 | * Handle for the record helper | ||
86 | */ | ||
87 | static struct GNUNET_HELPER_Handle *record_helper; | ||
88 | |||
89 | /** Handle for the playback handler | ||
90 | * | ||
91 | */ | ||
92 | static struct GNUNET_HELPER_Handle *playback_helper; | ||
93 | |||
94 | /** | ||
95 | * Handle for mesh | ||
96 | */ | ||
97 | static struct GNUNET_MESH_Handle *mesh; | ||
98 | |||
99 | /** | ||
100 | * Transmit handle for audio messages | ||
101 | */ | ||
102 | static struct GNUNET_MESH_TransmitHandle *mth = NULL; | ||
103 | |||
104 | /** | ||
105 | * Handle for the reliable tunnel (contol data) | ||
106 | */ | ||
107 | static struct GNUNET_MESH_Tunnel *tunnel_reliable; | ||
108 | |||
109 | /** | ||
110 | * Handle for unreliable tunnel (audio data) | ||
111 | */ | ||
112 | static struct GNUNET_MESH_Tunnel *tunnel_unreliable; | ||
113 | |||
114 | /** | ||
115 | * List for missed calls | ||
116 | */ | ||
117 | struct GNUNET_CONTAINER_SList *missed_calls; | ||
118 | |||
119 | /** | ||
120 | * List for peers to notify that we are available again | ||
121 | */ | ||
122 | struct GNUNET_CONTAINER_SList *peers_to_notify; | ||
123 | |||
124 | /** | ||
125 | * Audio buffer (outgoing) | ||
126 | */ | ||
127 | struct GNUNET_CONTAINER_SList *audio_buffer; | ||
128 | |||
129 | /** | ||
130 | * The pointer to the task for sending audio | ||
131 | */ | ||
132 | GNUNET_SCHEDULER_TaskIdentifier audio_task; | ||
133 | |||
134 | /** | ||
135 | * The pointer to the task for checking timeouts an calling a peer | ||
136 | */ | ||
137 | GNUNET_SCHEDULER_TaskIdentifier timeout_task; | ||
138 | |||
139 | /** | ||
140 | * Sequencenumber for the pakets (for evaltuation purposes) | ||
141 | */ | ||
142 | int SequenceNumber = 0; | ||
143 | |||
144 | /** | ||
145 | * Timestamp for call statistics | ||
146 | */ | ||
147 | static struct GNUNET_TIME_Absolute start_time; | ||
148 | |||
149 | /** | ||
150 | * Number of payload packes sent | ||
151 | */ | ||
152 | static int data_sent; | ||
153 | static int data_sent_size; | ||
154 | |||
155 | /** | ||
156 | * Number of payload packets received | ||
157 | */ | ||
158 | static int data_received; | ||
159 | static int data_received_size; | ||
160 | |||
161 | /******************************************************************************/ | ||
162 | /*********************** AUXILIARY FUNCTIONS *************************/ | ||
163 | /******************************************************************************/ | ||
164 | |||
165 | /** | ||
166 | * Function which displays some call stats | ||
167 | */ | ||
168 | static void | ||
169 | show_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 | */ | ||
194 | static void | ||
195 | status_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 | */ | ||
226 | static void | ||
227 | terminate_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 | */ | ||
266 | static void | ||
267 | reject_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 | */ | ||
307 | static void | ||
308 | check_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 | */ | ||
341 | static size_t | ||
342 | transmit_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 | */ | ||
370 | static size_t | ||
371 | transmit_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 | */ | ||
397 | static size_t | ||
398 | transmit_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 | */ | ||
433 | static size_t | ||
434 | transmit_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 | */ | ||
460 | static size_t | ||
461 | transmit_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 | */ | ||
482 | static size_t | ||
483 | transmit_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 | */ | ||
509 | static size_t | ||
510 | transmit_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 | */ | ||
536 | static size_t | ||
537 | transmit_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 | */ | ||
563 | static size_t | ||
564 | transmit_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 | */ | ||
591 | static void | ||
592 | handle_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 | */ | ||
697 | static void | ||
698 | handle_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 | */ | ||
759 | static void | ||
760 | handle_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 | */ | ||
791 | static void | ||
792 | handle_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 | */ | ||
822 | static size_t | ||
823 | transmit_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 | */ | ||
865 | static size_t | ||
866 | transmit_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 | */ | ||
897 | static void | ||
898 | transmit_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 | */ | ||
967 | int | ||
968 | handle_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 | */ | ||
1097 | int | ||
1098 | handle_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 | */ | ||
1177 | int | ||
1178 | handle_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 | */ | ||
1230 | int | ||
1231 | handle_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 | */ | ||
1275 | int | ||
1276 | handle_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 | */ | ||
1319 | static int | ||
1320 | process_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 | */ | ||
1340 | int | ||
1341 | start_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 | */ | ||
1367 | int | ||
1368 | start_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 | */ | ||
1395 | int | ||
1396 | start_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 | */ | ||
1413 | void | ||
1414 | stop_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 | */ | ||
1428 | void | ||
1429 | stop_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 | */ | ||
1443 | void | ||
1444 | stop_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 | */ | ||
1464 | static void * | ||
1465 | inbound_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 | */ | ||
1493 | static void | ||
1494 | inbound_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 | |||
1540 | static void | ||
1541 | handle_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 | */ | ||
1604 | static void | ||
1605 | handle_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 | */ | ||
1648 | static void | ||
1649 | do_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 | */ | ||
1686 | static 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 | */ | ||
1709 | static void | ||
1710 | run (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 | */ | ||
1778 | int | ||
1779 | main (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 | ||
31 | extern "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 | */ | ||
43 | enum 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 | */ | ||
55 | enum connection_status | ||
56 | { | ||
57 | LISTEN, | ||
58 | CALLER, | ||
59 | CALLEE, | ||
60 | CONNECTED | ||
61 | }; | ||
62 | |||
63 | /** | ||
64 | * VoipClient. | ||
65 | */ | ||
66 | struct 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 | */ | ||
77 | struct 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 | */ | ||
98 | struct 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 | */ | ||
119 | static 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 | */ | ||
131 | static size_t | ||
132 | transmit_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 | */ | ||
140 | static void | ||
141 | transmit_audio_task (void *cls, | ||
142 | const struct GNUNET_SCHEDULER_TaskContext *tc); | ||
143 | |||
144 | /** | ||
145 | * Start the audio helpers | ||
146 | */ | ||
147 | int start_helpers (void); | ||
148 | |||
149 | /** | ||
150 | * Stop the audio helpers | ||
151 | */ | ||
152 | void 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 | ||
32 | extern "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 | ||
48 | struct 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 | ||
58 | struct 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 | ||
67 | struct 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 | ||
77 | struct 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 | ||
86 | struct 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 | ||
100 | struct 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 | ||
110 | struct 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 | ||
119 | struct 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 | ||
130 | struct 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 | ||
139 | struct 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 | ||
148 | struct 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 | ||
157 | struct 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 | ||
166 | struct 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 | ||
177 | struct 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 | ||
186 | struct 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 | |||
197 | struct 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 | ||
208 | struct 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 | ||
220 | struct 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 | ||
231 | struct 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 | ||
244 | struct 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 | ||
255 | struct 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 | ||
270 | struct 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 | ||
279 | struct 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 | */ | ||
49 | typedef void (*MessageTokenizerCallback) (void *cls, | ||
50 | const struct | ||
51 | GNUNET_MessageHeader * | ||
52 | message); | ||
53 | |||
54 | /** | ||
55 | * Handle to a message stream tokenizer. | ||
56 | */ | ||
57 | struct 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 | */ | ||
100 | static struct MessageStreamTokenizer * | ||
101 | mst_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 | */ | ||
135 | static int | ||
136 | mst_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 | { | ||
151 | do_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 | */ | ||
283 | static void | ||
284 | mst_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 | |||
29 | static int ok = 1; | ||
30 | |||
31 | |||
32 | static void | ||
33 | run (void *cls, | ||
34 | char *const *args, | ||
35 | const char *cfgfile, | ||
36 | const struct GNUNET_CONFIGURATION_Handle *cfg) | ||
37 | { | ||
38 | ok = 0; | ||
39 | } | ||
40 | |||
41 | |||
42 | static int | ||
43 | check () | ||
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 | |||
76 | int | ||
77 | main (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 */ | ||