aboutsummaryrefslogtreecommitdiff
path: root/src/conversation
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2013-10-05 13:10:53 +0000
committerChristian Grothoff <christian@grothoff.org>2013-10-05 13:10:53 +0000
commitac3a1c494fd48fc1d01fcc4eafa0b3f4878f5441 (patch)
treea21b54452171f582444a199e70426b4c4266152b /src/conversation
parent45c9ba99503eaf130b3d298062c612e281893d6a (diff)
downloadgnunet-ac3a1c494fd48fc1d01fcc4eafa0b3f4878f5441.tar.gz
gnunet-ac3a1c494fd48fc1d01fcc4eafa0b3f4878f5441.zip
-rename fest: new to default
Diffstat (limited to 'src/conversation')
-rw-r--r--src/conversation/Makefile.am41
-rw-r--r--src/conversation/conversation_api.c1498
-rw-r--r--src/conversation/conversation_api2.c1132
-rw-r--r--src/conversation/gnunet-conversation-new.c855
-rw-r--r--src/conversation/gnunet-conversation.c818
-rw-r--r--src/conversation/gnunet-service-conversation-new.c1227
-rw-r--r--src/conversation/gnunet-service-conversation.c2485
7 files changed, 2475 insertions, 5581 deletions
diff --git a/src/conversation/Makefile.am b/src/conversation/Makefile.am
index 46d36053e..43a5a1a06 100644
--- a/src/conversation/Makefile.am
+++ b/src/conversation/Makefile.am
@@ -7,13 +7,6 @@ INCLUDES = \
7AM_CPPFLAGS = \ 7AM_CPPFLAGS = \
8 $(GNUNET_CPPFLAGS) 8 $(GNUNET_CPPFLAGS)
9 9
10# Set this variable if you are using GNUNET libraries for all programs and
11# libraries. You don't then need to target-specific _LDFLAGS with GNUNET_LDFLAGS
12# AM_LDFLAGS = \
13# $(GNUNET_LDFLAGS) \
14# $(WINFLAGS) \
15# -export-dynamic
16
17lib_LTLIBRARIES = \ 10lib_LTLIBRARIES = \
18 libgnunetmicrophone.la \ 11 libgnunetmicrophone.la \
19 libgnunetspeaker.la \ 12 libgnunetspeaker.la \
@@ -41,8 +34,7 @@ libgnunetspeaker_la_LDFLAGS = \
41 34
42 35
43libgnunetconversation_la_SOURCES = \ 36libgnunetconversation_la_SOURCES = \
44 conversation_api.c \ 37 conversation_api.c
45 conversation_api2.c
46libgnunetconversation_la_LIBADD = \ 38libgnunetconversation_la_LIBADD = \
47 $(top_builddir)/src/gns/libgnunetgns.la \ 39 $(top_builddir)/src/gns/libgnunetgns.la \
48 $(top_builddir)/src/namestore/libgnunetnamestore.la \ 40 $(top_builddir)/src/namestore/libgnunetnamestore.la \
@@ -56,12 +48,10 @@ libgnunetconversation_la_LDFLAGS = \
56 48
57bin_PROGRAMS = \ 49bin_PROGRAMS = \
58 gnunet-conversation-test \ 50 gnunet-conversation-test \
59 gnunet-conversation \ 51 gnunet-conversation
60 gnunet-conversation-new
61 52
62libexec_PROGRAMS = \ 53libexec_PROGRAMS = \
63 gnunet-service-conversation \ 54 gnunet-service-conversation
64 gnunet-service-conversation-new
65 55
66if HAVE_PULSE 56if HAVE_PULSE
67if HAVE_OPUS 57if HAVE_OPUS
@@ -100,39 +90,18 @@ gnunet_helper_audio_playback_LDFLAGS = \
100gnunet_service_conversation_SOURCES = \ 90gnunet_service_conversation_SOURCES = \
101 gnunet-service-conversation.c 91 gnunet-service-conversation.c
102gnunet_service_conversation_LDADD = \ 92gnunet_service_conversation_LDADD = \
103 $(top_builddir)/src/gns/libgnunetgns.la \
104 $(top_builddir)/src/mesh/libgnunetmesh.la \
105 $(top_builddir)/src/namestore/libgnunetnamestore.la \
106 $(top_builddir)/src/util/libgnunetutil.la \
107 $(INTLLIBS)
108gnunet_service_conversation_LDFLAGS = \
109 $(GNUNET_LDFLAGS) $(WINFLAGS)
110
111gnunet_service_conversation_new_SOURCES = \
112 gnunet-service-conversation-new.c
113gnunet_service_conversation_new_LDADD = \
114 libgnunetconversation.la \ 93 libgnunetconversation.la \
115 libgnunetspeaker.la \ 94 libgnunetspeaker.la \
116 libgnunetmicrophone.la \ 95 libgnunetmicrophone.la \
117 $(top_builddir)/src/mesh/libgnunetmesh.la \ 96 $(top_builddir)/src/mesh/libgnunetmesh.la \
118 $(top_builddir)/src/util/libgnunetutil.la \ 97 $(top_builddir)/src/util/libgnunetutil.la \
119 $(INTLLIBS) 98 $(INTLLIBS)
120 99gnunet_service_conversation_LDFLAGS = \
121gnunet_service_conversation_new_LDFLAGS = \
122 $(GNUNET_LDFLAGS) $(WINFLAGS) 100 $(GNUNET_LDFLAGS) $(WINFLAGS)
123 101
124gnunet_conversation_SOURCES = \ 102gnunet_conversation_SOURCES = \
125 gnunet-conversation.c 103 gnunet-conversation.c
126gnunet_conversation_LDADD = \ 104gnunet_conversation_LDADD = \
127 libgnunetconversation.la \
128 $(top_builddir)/src/util/libgnunetutil.la \
129 $(INTLLIBS)
130gnunet_conversation_LDFLAGS = \
131 $(GNUNET_LDFLAGS) $(WINFLAGS)
132
133gnunet_conversation_new_SOURCES = \
134 gnunet-conversation-new.c
135gnunet_conversation_new_LDADD = \
136 libgnunetmicrophone.la \ 105 libgnunetmicrophone.la \
137 libgnunetspeaker.la \ 106 libgnunetspeaker.la \
138 libgnunetconversation.la \ 107 libgnunetconversation.la \
@@ -141,7 +110,7 @@ gnunet_conversation_new_LDADD = \
141 $(top_builddir)/src/identity/libgnunetidentity.la \ 110 $(top_builddir)/src/identity/libgnunetidentity.la \
142 $(top_builddir)/src/util/libgnunetutil.la \ 111 $(top_builddir)/src/util/libgnunetutil.la \
143 $(INTLLIBS) 112 $(INTLLIBS)
144gnunet_conversation_new_LDFLAGS = \ 113gnunet_conversation_LDFLAGS = \
145 $(GNUNET_LDFLAGS) $(WINFLAGS) 114 $(GNUNET_LDFLAGS) $(WINFLAGS)
146 115
147gnunet_conversation_test_SOURCES = \ 116gnunet_conversation_test_SOURCES = \
diff --git a/src/conversation/conversation_api.c b/src/conversation/conversation_api.c
index 5bd88c01f..6ce198e64 100644
--- a/src/conversation/conversation_api.c
+++ b/src/conversation/conversation_api.c
@@ -1,770 +1,1132 @@
1/* 1/*
2 This file is part of GNUnet. 2 This file is part of GNUnet
3 (C 2013 Christian Grothoff (and other contributing authors) 3 (C) 2013 Christian Grothoff (and other contributing authors)
4 4
5 GNUnet is free software; you can redistribute it and/or modify 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 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 7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version. 8 option) any later version.
9 9
10 GNUnet is distributed in the hope that it will be useful, but 10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of 11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details. 13 General Public License for more details.
14 14
15 You should have received a copy of the GNU General Public License 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 16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330, 17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA. 18 Boston, MA 02111-1307, USA.
19*/ 19 */
20 20
21/** 21/**
22 * @file conversation/conversation_api.c 22 * @file conversation/conversation_api2.c
23 * @brief API for conversation 23 * @brief API to the conversation service
24 * @author Simon Dieterle 24 * @author Simon Dieterle
25 * @author Andreas Fuchs 25 * @author Andreas Fuchs
26 * STRUCTURE: 26 * @author Christian Grothoff
27 * - DATA STRUCTURES
28 * - DECLARATIONS
29 * - AUXILIARY FUNCTIONS
30 * - RECEIVE HANDLERS
31 * - SEND FUNCTIONS
32 * - API CALL DEFINITIONS
33 *
34 */ 27 */
35
36#include "platform.h" 28#include "platform.h"
37#include "gnunet_util_lib.h" 29#include "gnunet_conversation_service.h"
38#include "gnunet_dnsparser_lib.h"
39#include "gnunet_gns_service.h" 30#include "gnunet_gns_service.h"
40#include "gnunet_protocols.h"
41#include "conversation.h" 31#include "conversation.h"
42#include "gnunet_conversation_service.h"
43
44#define MAX_TRANSMIT_DELAY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 60)
45 32
46/******************************************************************************/
47/************************ DATA STRUCTURES ****************************/
48/******************************************************************************/
49 33
50enum GNUNET_CONVERSATION_CallType 34/**
35 * A phone record specifies which peer is hosting a given user and
36 * may also specify the phone line that is used (typically zero).
37 * The version is also right now always zero.
38 */
39struct PhoneRecord
51{ 40{
52 CALLER = 0, 41
53 CALLEE 42 /**
43 * Version of the phone record, for now always zero. We may
44 * use other versions for anonymously hosted phone lines in
45 * the future.
46 */
47 uint32_t version GNUNET_PACKED;
48
49 /**
50 * Phone line to use at the peer.
51 */
52 uint32_t line GNUNET_PACKED;
53
54 /**
55 * Identity of the peer hosting the phone service.
56 */
57 struct GNUNET_PeerIdentity peer;
58
54}; 59};
55 60
61
56/** 62/**
57* Information about a call 63 * Possible states of the phone.
58*/ 64 */
59struct GNUNET_CONVERSATION_CallInformation 65enum PhoneState
60{ 66{
67 /**
68 * We still need to register the phone.
69 */
70 PS_REGISTER = 0,
61 71
62 /** 72 /**
63 * Peer interacting with 73 * We are waiting for a call.
64 */ 74 */
65 struct GNUNET_PeerIdentity peer; 75 PS_WAITING,
66 76
67 /** 77 /**
68 * Type of call (incoming or outgoing) 78 * The phone is ringing.
69 */ 79 */
70 int type; 80 PS_RINGING,
71 81
72 /** 82 /**
73 * Shows if the call ist fully established 83 * The phone is in an active conversation.
74 */ 84 */
75 int established; 85 PS_ACTIVE
76}; 86};
77 87
88
78/** 89/**
79 * Opaque handle to the service. 90 * A phone is a device that can ring to signal an incoming call and
91 * that you can pick up to answer the call and hang up to terminate
92 * the call. You can also hang up a ringing phone immediately
93 * (without picking it up) to stop it from ringing. Phones have
94 * caller ID. You can ask the phone for its record and make that
95 * record available (via GNS) to enable others to call you.
96 * Multiple phones maybe connected to the same line (the line is
97 * something rather internal to a phone and not obvious from it).
98 * You can only have one conversation per phone at any time.
80 */ 99 */
81struct GNUNET_CONVERSATION_Handle 100struct GNUNET_CONVERSATION_Phone
82{ 101{
83
84 /** 102 /**
85 * Our configuration. 103 * Our configuration.
86 */ 104 */
87 const struct GNUNET_CONFIGURATION_Handle *cfg; 105 const struct GNUNET_CONFIGURATION_Handle *cfg;
88 106
89 /** 107 /**
90 * Handle to the server connection, to send messages later 108 * Handle to talk with CONVERSATION service.
91 */ 109 */
92 struct GNUNET_CLIENT_Connection *client; 110 struct GNUNET_CLIENT_Connection *client;
93 111
94 /** 112 /**
95 * GNS handle 113 * Function to call for phone events.
96 */ 114 */
97 struct GNUNET_GNS_Handle *gns; 115 GNUNET_CONVERSATION_EventHandler event_handler;
98 116
99 /** 117 /**
100 * Namestore handle 118 * Closure for @e event_handler
101 */ 119 */
102 struct GNUNET_NAMESTORE_Handle *namestore; 120 void *event_handler_cls;
103 121
104 /** 122 /**
105 * TXT record for gns 123 * Speaker, or NULL if none is attached.
106 */ 124 */
107 int txt_record_set; 125 struct GNUNET_SPEAKER_Handle *speaker;
108 126
109 /** 127 /**
110 * Callback for incoming calls 128 * Microphone, or NULL if none is attached.
111 */ 129 */
112 GNUNET_CONVERSATION_CallHandler *call_handler; 130 struct GNUNET_MICROPHONE_Handle *mic;
131
132 /**
133 * Connection to NAMESTORE (for reverse lookup).
134 */
135 struct GNUNET_NAMESTORE_Handle *ns;
113 136
114 /** 137 /**
115 * Callback for rejected calls 138 * Active NAMESTORE lookup (or NULL).
116 */ 139 */
117 GNUNET_CONVERSATION_RejectHandler *reject_handler; 140 struct GNUNET_NAMESTORE_QueueEntry *qe;
118 141
142 /**
143 * Handle for transmitting to the CONVERSATION service.
144 */
145 struct GNUNET_MQ_Handle *mq;
146
147 /**
148 * This phone's record.
149 */
150 struct PhoneRecord my_record;
151
119 /** 152 /**
120 * Callback for notifications 153 * My GNS zone.
121 */ 154 */
122 GNUNET_CONVERSATION_NotificationHandler *notification_handler; 155 struct GNUNET_CRYPTO_EccPrivateKey my_zone;
123 156
124 /** 157 /**
125 * Callback for missed calls 158 * Identity of the person calling us (valid while in state #PS_RINGING).
126 */ 159 */
127 GNUNET_CONVERSATION_MissedCallHandler *missed_call_handler; 160 struct GNUNET_CRYPTO_EccPublicSignKey caller_id;
128 161
129 /** 162 /**
130 * The pointer to the call 163 * State machine for the phone.
131 */ 164 */
132 struct GNUNET_CONVERSATION_CallInformation *call; 165 enum PhoneState state;
166
133}; 167};
134 168
135/******************************************************************************/
136/*********************** AUXILIARY FUNCTIONS *************************/
137/******************************************************************************/
138 169
139/** 170/**
140* Initialize the conversation txt record in GNS 171 * The phone got disconnected, reconnect to the service.
141*/ 172 *
173 * @param phone phone to reconnect
174 */
142static void 175static void
143setup_gns_txt (struct GNUNET_CONVERSATION_Handle *handle) 176reconnect_phone (struct GNUNET_CONVERSATION_Phone *phone);
144{
145 struct GNUNET_CRYPTO_EccPublicSignKey zone_pkey;
146 struct GNUNET_CRYPTO_EccPrivateKey *zone_key;
147 struct GNUNET_CRYPTO_EccPrivateKey *peer_key;
148 struct GNUNET_NAMESTORE_RecordData rd;
149 struct GNUNET_PeerIdentity peer;
150 177
151 char *zone_keyfile;
152 char *peer_keyfile;
153 178
154 rd.expiration_time = UINT64_MAX; 179/**
180 * We have resolved the caller ID using our name service.
181 *
182 * @param cls the `struct GNUNET_CONVERSATION_Phone`
183 * @param zone our zone used for resolution
184 * @param label name of the caller
185 * @param rd_count number of records we have in @a rd
186 * @param rd records we have for the caller's label
187 */
188static void
189handle_caller_name (void *cls,
190 const struct GNUNET_CRYPTO_EccPrivateKey *zone,
191 const char *label,
192 unsigned int rd_count,
193 const struct GNUNET_NAMESTORE_RecordData *rd)
194{
195 struct GNUNET_CONVERSATION_Phone *phone = cls;
196 char *name;
155 197
156 if (GNUNET_OK != 198 phone->qe = NULL;
157 GNUNET_CONFIGURATION_get_value_filename (handle->cfg, "gns", "ZONEKEY", 199 if (NULL == label)
158 &zone_keyfile)) 200 name = GNUNET_strdup (GNUNET_NAMESTORE_pkey_to_zkey (&phone->caller_id));
159 { 201 else
160 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to get key from cfg\n"); 202 GNUNET_asprintf (&name, "%.gnu", label);
161 return; 203 phone->event_handler (phone->event_handler_cls,
162 } 204 GNUNET_CONVERSATION_EC_RING,
205 name);
206 GNUNET_free (name);
207}
163 208
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 209
172 zone_key = GNUNET_CRYPTO_ecc_key_create_from_file (zone_keyfile); 210/**
173 GNUNET_CRYPTO_ecc_key_get_public_for_signature (zone_key, &zone_pkey); 211 * We received a `struct ClientPhoneRingMessage`
174 peer_key = GNUNET_CRYPTO_ecc_key_create_from_file (peer_keyfile); 212 *
175 GNUNET_CRYPTO_ecc_key_get_public_for_signature (peer_key, 213 * @param cls the `struct GNUNET_CONVERSATION_Phone`
176 &peer.public_key); 214 * @param msg the message
177 const char *h = GNUNET_i2s_full (&peer); 215 */
178 216static void
179 rd.data_size = strlen (h) + 1; 217handle_phone_ring (void *cls,
180 rd.data = h; 218 const struct GNUNET_MessageHeader *msg)
181 rd.record_type = GNUNET_DNSPARSER_TYPE_TXT; 219{
182 rd.flags = GNUNET_NAMESTORE_RF_NONE; 220 struct GNUNET_CONVERSATION_Phone *phone = cls;
183 221 const struct ClientPhoneRingMessage *ring;
184 /* FIXME: continuation? return value? */ 222
185 GNUNET_NAMESTORE_records_store (handle->namestore, 223 ring = (const struct ClientPhoneRingMessage *) msg;
186 zone_key, 224 switch (phone->state)
187 "conversation", 225 {
188 1, &rd, 226 case PS_REGISTER:
189 NULL, NULL); 227 GNUNET_assert (0);
228 break;
229 case PS_WAITING:
230 phone->state = PS_RINGING;
231 phone->caller_id = ring->caller_id;
232 phone->qe = GNUNET_NAMESTORE_zone_to_name (phone->ns,
233 &phone->my_zone,
234 &ring->caller_id,
235 &handle_caller_name,
236 phone);
237 break;
238 case PS_RINGING:
239 GNUNET_break (0);
240 reconnect_phone (phone);
241 break;
242 case PS_ACTIVE:
243 GNUNET_break (0);
244 reconnect_phone (phone);
245 break;
246 }
190} 247}
191 248
249
192/** 250/**
193* Callback for checking the conversation txt gns record 251 * We received a `struct ClientPhoneHangupMessage`.
194* 252 *
195* @param cls closure 253 * @param cls the `struct GNUNET_CONVERSATION_Phone`
196* @param rd_count 254 * @param msg the message
197* @param rd 255 */
198*/
199static void 256static void
200check_gns_cb (void *cls, uint32_t rd_count, 257handle_phone_hangup (void *cls,
201 const struct GNUNET_NAMESTORE_RecordData *rd) 258 const struct GNUNET_MessageHeader *msg)
202{ 259{
203 struct GNUNET_CONVERSATION_Handle *h = (struct GNUNET_CONVERSATION_Handle *) cls; 260 struct GNUNET_CONVERSATION_Phone *phone = cls;
204 261 const struct ClientPhoneHangupMessage *hang;
205 if (0 == rd_count) 262 size_t len;
206 { 263 const char *reason;
207 setup_gns_txt (h); 264
208 } 265 hang = (const struct ClientPhoneHangupMessage *) msg;
209 else 266 reason = (const char *) &hang[1];
267 len = htons (hang->header.size) - sizeof (struct ClientPhoneHangupMessage);
268 if ( (0 == len) ||
269 ('\0' != reason[len-1]) )
270 {
271 GNUNET_break (0);
272 reconnect_phone (phone);
273 return;
274 }
275 switch (phone->state)
276 {
277 case PS_REGISTER:
278 GNUNET_assert (0);
279 break;
280 case PS_WAITING:
281 GNUNET_break (0);
282 reconnect_phone (phone);
283 break;
284 case PS_RINGING:
285 if (NULL != phone->qe)
210 { 286 {
211 h->txt_record_set = GNUNET_YES; 287 GNUNET_NAMESTORE_cancel (phone->qe);
288 phone->qe = NULL;
289 phone->state = PS_WAITING;
290 break;
212 } 291 }
292 phone->state = PS_WAITING;
293 phone->event_handler (phone->event_handler_cls,
294 GNUNET_CONVERSATION_EC_TERMINATED,
295 reason);
296 break;
297 case PS_ACTIVE:
298 GNUNET_break (NULL == phone->qe);
299 phone->state = PS_WAITING;
300 phone->event_handler (phone->event_handler_cls,
301 GNUNET_CONVERSATION_EC_TERMINATED,
302 reason);
303 break;
304 }
213} 305}
214 306
215 307
216/** 308/**
217 * Check if the gns txt record for conversation exits 309 * We received a `struct ClientAudioMessage`
310 *
311 * @param cls the `struct GNUNET_CONVERSATION_Phone`
312 * @param msg the message
218 */ 313 */
219static void 314static void
220check_gns (struct GNUNET_CONVERSATION_Handle *h) 315handle_phone_audio_message (void *cls,
316 const struct GNUNET_MessageHeader *msg)
221{ 317{
222 GNUNET_GNS_lookup (h->gns, "conversation.gns", 318 struct GNUNET_CONVERSATION_Phone *phone = cls;
223 NULL /* FIXME_ZONE */, 319 const struct ClientAudioMessage *am;
224 GNUNET_DNSPARSER_TYPE_TXT,
225 GNUNET_NO,
226 NULL,
227 &check_gns_cb, h);
228}
229 320
321 am = (const struct ClientAudioMessage *) msg;
322 switch (phone->state)
323 {
324 case PS_REGISTER:
325 GNUNET_assert (0);
326 break;
327 case PS_WAITING:
328 GNUNET_break (0);
329 reconnect_phone (phone);
330 break;
331 case PS_RINGING:
332 GNUNET_break (0);
333 reconnect_phone (phone);
334 break;
335 case PS_ACTIVE:
336 phone->speaker->play (phone->speaker->cls,
337 ntohs (msg->size) - sizeof (struct ClientAudioMessage),
338 &am[1]);
339 break;
340 }
341}
230 342
231/******************************************************************************/
232/*********************** RECEIVE HANDLERS ****************************/
233/******************************************************************************/
234 343
235/** 344/**
236 * Function to process all messages received from the service 345 * We encountered an error talking with the conversation service.
237 * 346 *
238 * @param cls closure 347 * @param cls the `struct GNUNET_CONVERSATION_Phone`
239 * @param msg message received, NULL on timeout or fatal error 348 * @param error details about the error
240 */ 349 */
241static void 350static void
242receive_message_cb (void *cls, 351phone_error_handler (void *cls,
243 const struct GNUNET_MessageHeader *msg) 352 enum GNUNET_MQ_Error error)
244{ 353{
245 struct GNUNET_CONVERSATION_Handle *h = cls; 354 struct GNUNET_CONVERSATION_Phone *phone = cls;
246 struct ServerClientSessionInitiateMessage *imsg;
247 struct ServerClientSessionRejectMessage *rmsg;
248 struct GNUNET_CONVERSATION_MissedCallNotification *missed_calls;
249
250 if (NULL != msg)
251 {
252 switch (ntohs (msg->type))
253 {
254 case GNUNET_MESSAGE_TYPE_CONVERSATION_SC_SESSION_ACCEPT:
255 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
256 _("%s has accepted your call.\n"),
257 GNUNET_i2s_full (&(h->call->peer)));
258
259 h->notification_handler (NULL, h, GNUNET_CONVERSATION_NT_CALL_ACCEPTED,
260 &(h->call->peer));
261 h->call->type = CALLEE;
262
263 break;
264
265 case GNUNET_MESSAGE_TYPE_CONVERSATION_SC_SESSION_REJECT:
266 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
267 _("%s has rejected your call.\n"),
268 GNUNET_i2s_full (&(h->call->peer)));
269
270 rmsg = (struct ServerClientSessionRejectMessage *) msg;
271 h->reject_handler (NULL, h, ntohs (rmsg->reason), &(h->call->peer));
272 GNUNET_free (h->call);
273 h->call = NULL;
274
275 break;
276
277 case GNUNET_MESSAGE_TYPE_CONVERSATION_SC_SESSION_TERMINATE:
278 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
279 _("%s has terminated the call.\n"),
280 GNUNET_i2s_full (&(h->call->peer)));
281
282 h->notification_handler (NULL, h, GNUNET_CONVERSATION_NT_CALL_TERMINATED,
283 &(h->call->peer));
284 GNUNET_free (h->call);
285 h->call = NULL;
286
287 break;
288
289 case GNUNET_MESSAGE_TYPE_CONVERSATION_SC_SESSION_INITIATE:
290 imsg = (struct ServerClientSessionInitiateMessage *) msg;
291
292 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("%s wants to call you.\n"),
293 GNUNET_i2s_full (&(imsg->peer)));
294
295 h->call =
296 (struct GNUNET_CONVERSATION_CallInformation *)
297 GNUNET_malloc (sizeof (struct GNUNET_CONVERSATION_CallInformation));
298 memcpy (&(h->call->peer), &(imsg->peer),
299 sizeof (struct GNUNET_PeerIdentity));
300 h->call_handler (NULL, h, &(h->call->peer));
301 h->call->type = CALLEE;
302
303 break;
304
305 case GNUNET_MESSAGE_TYPE_CONVERSATION_SC_MISSED_CALL:
306 missed_calls =
307 (struct GNUNET_CONVERSATION_MissedCallNotification *) (msg +
308 (sizeof
309 (struct
310 GNUNET_MessageHeader)));
311 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
312 _("You &d have missed a calls.\n"),
313 missed_calls->number);
314 h->missed_call_handler (NULL, h, missed_calls);
315 break;
316
317 case GNUNET_MESSAGE_TYPE_CONVERSATION_SC_SERVICE_BLOCKED:
318 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("The service is blocked.\n"));
319 h->notification_handler (NULL, h, GNUNET_CONVERSATION_NT_SERVICE_BLOCKED,
320 NULL);
321 break;
322
323 case GNUNET_MESSAGE_TYPE_CONVERSATION_SC_PEER_NOT_CONNECTED:
324 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
325 _("The peer you are calling is not connected.\n"));
326 h->notification_handler (NULL, h, GNUNET_CONVERSATION_NT_NO_PEER, NULL);
327 break;
328
329 case GNUNET_MESSAGE_TYPE_CONVERSATION_SC_NO_ANSWER:
330 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
331 _("The peer you are calling does not answer.\n"));
332 h->notification_handler (NULL, h, GNUNET_CONVERSATION_NT_NO_ANSWER,
333 &(h->call->peer));
334 break;
335
336 case GNUNET_MESSAGE_TYPE_CONVERSATION_SC_ERROR:
337 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Generic error occured.\n"));
338 break;
339
340 default:
341 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
342 _("Got unknown message type.\n"));
343 break;
344 }
345 355
346 } 356 GNUNET_break (0);
347 357 reconnect_phone (phone);
348 GNUNET_CLIENT_receive (h->client, &receive_message_cb, h,
349 GNUNET_TIME_UNIT_FOREVER_REL);
350} 358}
351 359
352/******************************************************************************/
353/************************ SEND FUNCTIONS ****************************/
354/******************************************************************************/
355 360
356/** 361/**
357 * Function called to send a session initiate message to the service. 362 * The phone got disconnected, reconnect to the service.
358 * "buf" will be NULL and "size" zero if the socket was closed for writing in
359 * the meantime.
360 * 363 *
361 * @param cls closure, NULL 364 * @param phone phone to reconnect
362 * @param size number of bytes available in buf
363 * @param buf where the callee should write the initiate message
364 * @return number of bytes written to buf
365 */ 365 */
366static size_t 366static void
367transmit_session_initiate_message (void *cls, size_t size, void *buf) 367reconnect_phone (struct GNUNET_CONVERSATION_Phone *phone)
368{ 368{
369 size_t msg_size; 369 static struct GNUNET_MQ_MessageHandler handlers[] =
370 struct ClientServerSessionInitiateMessage *msg; 370 {
371 struct GNUNET_CONVERSATION_Handle *h = (struct GNUNET_CONVERSATION_Handle *) cls; 371 { &handle_phone_ring,
372 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_RING,
373 sizeof (struct ClientPhoneRingMessage) },
374 { &handle_phone_hangup,
375 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP,
376 0 },
377 { &handle_phone_audio_message,
378 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO,
379 0 },
380 { NULL, 0, 0 }
381 };
382 struct GNUNET_MQ_Envelope *e;
383 struct ClientPhoneRegisterMessage *reg;
384
385 if (NULL != phone->mq)
386 {
387 GNUNET_MQ_destroy (phone->mq);
388 phone->mq = NULL;
389 }
390 if (NULL != phone->client)
391 {
392 GNUNET_CLIENT_disconnect (phone->client);
393 phone->client = NULL;
394 }
395 phone->state = PS_REGISTER;
396 phone->client = GNUNET_CLIENT_connect ("conversation", phone->cfg);
397 if (NULL == phone->client)
398 return;
399 phone->mq = GNUNET_MQ_queue_for_connection_client (phone->client,
400 handlers,
401 &phone_error_handler,
402 phone);
403 e = GNUNET_MQ_msg (reg, GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_REGISTER);
404 reg->line = phone->my_record.line;
405 GNUNET_MQ_send (phone->mq, e);
406 phone->state = PS_WAITING;
407}
372 408
373 msg_size = sizeof (struct ClientServerSessionInitiateMessage);
374 409
375 GNUNET_assert (size >= msg_size); 410/**
376 msg = buf; 411 * Create a new phone.
377 msg->header.size = htons (msg_size); 412 *
378 msg->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_SESSION_INITIATE); 413 * @param cfg configuration for the phone; specifies the phone service and
379 memcpy (&msg->peer, &(h->call->peer), sizeof (struct GNUNET_PeerIdentity)); 414 * which line the phone is to be connected to
415 * @param ego ego to use for name resolution (when determining caller ID)
416 * @param event_handler how to notify the owner of the phone about events
417 * @param event_handler_cls closure for @a event_handler
418 */
419struct GNUNET_CONVERSATION_Phone *
420GNUNET_CONVERSATION_phone_create (const struct GNUNET_CONFIGURATION_Handle *cfg,
421 const struct GNUNET_IDENTITY_Ego *ego,
422 GNUNET_CONVERSATION_EventHandler event_handler,
423 void *event_handler_cls)
424{
425 struct GNUNET_CONVERSATION_Phone *phone;
426 unsigned long long line;
380 427
381 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 428 if (GNUNET_OK !=
382 _ 429 GNUNET_CONFIGURATION_get_value_number (cfg,
383 ("Sending ClientServerSessionInitiateMessage to the service for peer: %s\n"), 430 "CONVERSATION",
384 GNUNET_i2s_full (&(h->call->peer))); 431 "LINE",
432 &line))
433 return NULL;
434 phone = GNUNET_new (struct GNUNET_CONVERSATION_Phone);
435 if (GNUNET_OK !=
436 GNUNET_CRYPTO_get_host_identity (cfg,
437 &phone->my_record.peer))
438 {
439 GNUNET_break (0);
440 GNUNET_free (phone);
441 return NULL;
442 }
443 phone->cfg = cfg;
444 phone->my_zone = *GNUNET_IDENTITY_ego_get_private_key (ego);
445 phone->event_handler = event_handler;
446 phone->event_handler_cls = event_handler_cls;
447 phone->ns = GNUNET_NAMESTORE_connect (cfg);
448 phone->my_record.line = htonl ((uint32_t) line);
449 phone->my_record.version = htonl (0);
450 reconnect_phone (phone);
451 if ( (NULL == phone->client) ||
452 (NULL == phone->ns) )
453 {
454 GNUNET_break (0);
455 GNUNET_CONVERSATION_phone_destroy (phone);
456 return NULL;
457 }
458 return phone;
459}
385 460
386 h->call->type = CALLER;
387 461
388 return msg_size; 462/**
463 * Fill in a namestore record with the contact information
464 * for this phone. Note that the filled in "data" value
465 * is only valid until the phone is destroyed.
466 *
467 * @param phone phone to create a record for
468 * @param rd namestore record to fill in
469 */
470void
471GNUNET_CONVERSATION_phone_get_record (struct GNUNET_CONVERSATION_Phone *phone,
472 struct GNUNET_NAMESTORE_RecordData *rd)
473{
474 rd->data = &phone->my_record;
475 rd->expiration_time = 0;
476 rd->data_size = sizeof (struct PhoneRecord);
477 rd->record_type = GNUNET_NAMESTORE_TYPE_PHONE;
478 rd->flags = GNUNET_NAMESTORE_RF_NONE;
389} 479}
390 480
481
391/** 482/**
392 * Function called to send a session accept message to the service. 483 * Process recorded audio data.
393 * "buf" will be NULL and "size" zero if the socket was closed for writing in
394 * the meantime.
395 * 484 *
396 * @param cls closure, NULL 485 * @param cls closure with the `struct GNUNET_CONVERSATION_Phone`
397 * @param size number of bytes available in buf 486 * @param data_size number of bytes in @a data
398 * @param buf where the callee should write the accept message 487 * @param data audio data to play
399 * @return number of bytes written to buf
400 */ 488 */
401static size_t 489static void
402transmit_session_accept_message (void *cls, size_t size, void *buf) 490transmit_phone_audio (void *cls,
491 size_t data_size,
492 const void *data)
403{ 493{
404 size_t msg_size; 494 struct GNUNET_CONVERSATION_Phone *phone = cls;
405 struct ClientServerSessionAcceptMessage *msg; 495 struct GNUNET_MQ_Envelope *e;
406 struct GNUNET_CONVERSATION_Handle *h = (struct GNUNET_CONVERSATION_Handle *) cls; 496 struct ClientAudioMessage *am;
407 497
408 msg_size = sizeof (struct ClientServerSessionAcceptMessage); 498 GNUNET_assert (PS_ACTIVE == phone->state);
499 e = GNUNET_MQ_msg_extra (am,
500 data_size,
501 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO);
502 memcpy (&am[1], data, data_size);
503 GNUNET_MQ_send (phone->mq, e);
504}
409 505
410 GNUNET_assert (size >= msg_size);
411 msg = buf;
412 msg->header.size = htons (msg_size);
413 msg->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_SESSION_ACCEPT);
414 506
415 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 507/**
416 _ 508 * Picks up a (ringing) phone. This will connect the speaker
417 ("Sending ClienServerSessionAcceptMessage to the service for peer: %s\n"), 509 * to the microphone of the other party, and vice versa.
418 GNUNET_i2s_full (&(h->call->peer))); 510 *
511 * @param phone phone to pick up
512 * @param metadata meta data to give to the other user about the pick up event
513 * @param speaker speaker to use
514 * @param mic microphone to use
515 */
516void
517GNUNET_CONVERSATION_phone_pick_up (struct GNUNET_CONVERSATION_Phone *phone,
518 const char *metadata,
519 struct GNUNET_SPEAKER_Handle *speaker,
520 struct GNUNET_MICROPHONE_Handle *mic)
521{
522 struct GNUNET_MQ_Envelope *e;
523 struct ClientPhonePickupMessage *pick;
524 size_t slen;
525
526 GNUNET_assert (PS_RINGING == phone->state);
527 phone->speaker = speaker;
528 phone->mic = mic;
529 slen = strlen (metadata) + 1;
530 e = GNUNET_MQ_msg_extra (pick, slen, GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_PICK_UP);
531 memcpy (&pick[1], metadata, slen);
532 GNUNET_MQ_send (phone->mq, e);
533 phone->state = PS_ACTIVE;
534 phone->speaker->enable_speaker (phone->speaker->cls);
535 phone->mic->enable_microphone (phone->mic->cls,
536 &transmit_phone_audio,
537 phone);
538}
419 539
420 h->call->established = GNUNET_YES;
421 540
422 return msg_size; 541/**
542 * Hang up up a (possibly ringing) phone. This will notify the other
543 * party that we are no longer interested in talking with them.
544 *
545 * @param phone phone to pick up
546 * @param reason text we give to the other party about why we terminated the conversation
547 */
548void
549GNUNET_CONVERSATION_phone_hang_up (struct GNUNET_CONVERSATION_Phone *phone,
550 const char *reason)
551{
552 struct GNUNET_MQ_Envelope *e;
553 struct ClientPhoneHangupMessage *hang;
554 size_t slen;
555
556 GNUNET_assert ( (PS_RINGING == phone->state) ||
557 (PS_ACTIVE == phone->state) );
558 phone->speaker->disable_speaker (phone->speaker->cls);
559 phone->mic->disable_microphone (phone->mic->cls);
560 phone->speaker = NULL;
561 phone->mic = NULL;
562 slen = strlen (reason) + 1;
563 e = GNUNET_MQ_msg_extra (hang, slen, GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP);
564 memcpy (&hang[1], reason, slen);
565 GNUNET_MQ_send (phone->mq, e);
566 phone->state = PS_WAITING;
423} 567}
424 568
569
425/** 570/**
426 * Function called to send a session reject message to the service. 571 * Destroys a phone.
427 * "buf" will be NULL and "size" zero if the socket was closed for writing in
428 * the meantime.
429 * 572 *
430 * @param cls closure, NULL 573 * @param phone phone to destroy
431 * @param size number of bytes available in buf
432 * @param buf where the callee should write the reject message
433 * @return number of bytes written to buf
434 */ 574 */
435static size_t 575void
436transmit_session_reject_message (void *cls, size_t size, void *buf) 576GNUNET_CONVERSATION_phone_destroy (struct GNUNET_CONVERSATION_Phone *phone)
437{ 577{
438 size_t msg_size; 578 if (NULL != phone->speaker)
439 struct ClientServerSessionRejectMessage *msg; 579 {
440 struct GNUNET_CONVERSATION_Handle *h = (struct GNUNET_CONVERSATION_Handle *) cls; 580 phone->speaker->disable_speaker (phone->speaker->cls);
581 phone->speaker = NULL;
582 }
583 if (NULL != phone->mic)
584 {
585 phone->mic->disable_microphone (phone->mic->cls);
586 phone->mic = NULL;
587 }
588 if (NULL != phone->qe)
589 {
590 GNUNET_NAMESTORE_cancel (phone->qe);
591 phone->qe = NULL;
592 }
593 if (NULL != phone->ns)
594 {
595 GNUNET_NAMESTORE_disconnect (phone->ns);
596 phone->ns = NULL;
597 }
598 if (NULL != phone->mq)
599 {
600 GNUNET_MQ_destroy (phone->mq);
601 phone->mq = NULL;
602 }
603 if (NULL != phone->client)
604 {
605 GNUNET_CLIENT_disconnect (phone->client);
606 phone->client = NULL;
607 }
608 GNUNET_free (phone);
609}
610
611
612/* ******************************* Call API *************************** */
441 613
442 msg_size = sizeof (struct ClientServerSessionRejectMessage); 614/**
615 * Possible states of the phone.
616 */
617enum CallState
618{
619 /**
620 * We still need to lookup the callee.
621 */
622 CS_LOOKUP = 0,
443 623
444 GNUNET_assert (size >= msg_size); 624 /**
445 msg = buf; 625 * The call is ringing.
446 msg->header.size = htons (msg_size); 626 */
447 msg->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_SESSION_REJECT); 627 CS_RINGING,
448 msg->reason = htons (GNUNET_CONVERSATION_REJECT_REASON_NOT_WANTED);
449 628
450 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 629 /**
451 _ 630 * The call is in an active conversation.
452 ("Sending ClientServerSessionRejectMessage to the service for peer: %s\n"), 631 */
453 GNUNET_i2s_full (&(h->call->peer))); 632 CS_ACTIVE,
454 633
455 GNUNET_free (h->call); 634 /**
456 h->call = NULL; 635 * The call is in termination.
636 */
637 CS_SHUTDOWN
638};
457 639
458 return msg_size;
459}
460 640
461/** 641/**
462 * Function called to send a session terminate message to the service. 642 * Handle for an outgoing call.
463 * "buf" will be NULL and "size" zero if the socket was closed for writing in
464 * the meantime.
465 *
466 * @param cls closure, NULL
467 * @param size number of bytes available in buf
468 * @param buf where the callee should write the terminate message
469 * @return number of bytes written to buf
470 */ 643 */
471static size_t 644struct GNUNET_CONVERSATION_Call
472transmit_session_terminate_message (void *cls, size_t size, void *buf)
473{ 645{
474 size_t msg_size;
475 struct ClientServerSessionTerminateMessage *msg;
476 struct GNUNET_CONVERSATION_Handle *h = (struct GNUNET_CONVERSATION_Handle *) cls;
477 646
478 msg_size = sizeof (struct ClientServerSessionTerminateMessage); 647 /**
648 * Our configuration.
649 */
650 const struct GNUNET_CONFIGURATION_Handle *cfg;
651
652 /**
653 * Handle to talk with CONVERSATION service.
654 */
655 struct GNUNET_CLIENT_Connection *client;
656
657 /**
658 * Our caller identity.
659 */
660 struct GNUNET_IDENTITY_Ego *caller_id;
661
662 /**
663 * Target callee as a GNS address/name.
664 */
665 char *callee;
479 666
480 GNUNET_assert (size >= msg_size); 667 /**
481 msg = buf; 668 * Our speaker.
482 msg->header.size = htons (msg_size); 669 */
483 msg->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_SESSION_TERMINATE); 670 struct GNUNET_SPEAKER_Handle *speaker;
484 671
485 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 672 /**
486 _ 673 * Our microphone.
487 ("Sending ClientServerSessionTerminateMessage to the service for peer: %s\n"), 674 */
488 GNUNET_i2s_full (&(h->call->peer))); 675 struct GNUNET_MICROPHONE_Handle *mic;
676
677 /**
678 * Function to call with events.
679 */
680 GNUNET_CONVERSATION_EventHandler event_handler;
489 681
490 GNUNET_free (h->call); 682 /**
491 h->call = NULL; 683 * Closure for @e event_handler
684 */
685 void *event_handler_cls;
686
687 /**
688 * Handle for transmitting to the CONVERSATION service.
689 */
690 struct GNUNET_MQ_Handle *mq;
691
692 /**
693 * Connection to GNS (can be NULL).
694 */
695 struct GNUNET_GNS_Handle *gns;
696
697 /**
698 * Active GNS lookup (or NULL).
699 */
700 struct GNUNET_GNS_LookupRequest *gns_lookup;
701
702 /**
703 * Target phone record, only valid after the lookup is done.
704 */
705 struct PhoneRecord phone_record;
706
707 /**
708 * State machine for the call.
709 */
710 enum CallState state;
711
712};
492 713
493 return msg_size;
494}
495 714
496/** 715/**
497 * Auxiliary function to call a peer. 716 * The call got disconnected, reconnect to the service.
498 * 717 *
499 * @param h conversation handle 718 * @param call call to reconnect
500 * @return
501 */ 719 */
502static void 720static void
503initiate_call (struct GNUNET_CONVERSATION_Handle *h, struct GNUNET_PeerIdentity peer) 721reconnect_call (struct GNUNET_CONVERSATION_Call *call);
504{ 722
505 h->call =
506 (struct GNUNET_CONVERSATION_CallInformation *)
507 GNUNET_malloc (sizeof (struct GNUNET_CONVERSATION_CallInformation));
508 memcpy (&(h->call->peer), &peer, sizeof (struct GNUNET_PeerIdentity));
509
510 GNUNET_CLIENT_notify_transmit_ready (h->client,
511 sizeof (struct
512 ClientServerSessionInitiateMessage),
513 MAX_TRANSMIT_DELAY, GNUNET_YES,
514 &transmit_session_initiate_message, h);
515
516 return;
517}
518 723
519/** 724/**
520 * Auxiliary function to accept a call. 725 * We received a `struct ClientPhoneBusyMessage`
521 * 726 *
522 * @param h conversation handle 727 * @param cls the `struct GNUNET_CONVERSATION_Call`
728 * @param msg the message
523 */ 729 */
524static void 730static void
525accept_call (struct GNUNET_CONVERSATION_Handle *h) 731handle_call_busy (void *cls,
732 const struct GNUNET_MessageHeader *msg)
526{ 733{
527 GNUNET_CLIENT_notify_transmit_ready (h->client, 734 struct GNUNET_CONVERSATION_Call *call = cls;
528 sizeof (struct 735
529 ClientServerSessionAcceptMessage), 736 switch (call->state)
530 MAX_TRANSMIT_DELAY, GNUNET_YES, 737 {
531 &transmit_session_accept_message, h); 738 case CS_LOOKUP:
739 GNUNET_break (0);
740 reconnect_call (call);
741 break;
742 case CS_RINGING:
743 call->event_handler (call->event_handler_cls,
744 GNUNET_CONVERSATION_EC_BUSY);
745 GNUNET_CONVERSATION_call_stop (call, NULL);
746 break;
747 case CS_ACTIVE:
748 GNUNET_break (0);
749 reconnect_call (call);
750 break;
751 case CS_SHUTDOWN:
752 GNUNET_CONVERSATION_call_stop (call, NULL);
753 break;
754 }
532} 755}
533 756
534 757
535/** 758/**
536 * Auxiliary function to reject a call. 759 * Process recorded audio data.
537 * 760 *
538 * @param h conversation handle 761 * @param cls closure with the `struct GNUNET_CONVERSATION_Call`
762 * @param data_size number of bytes in @a data
763 * @param data audio data to play
539 */ 764 */
540static void 765static void
541reject_call (struct GNUNET_CONVERSATION_Handle *h) 766transmit_call_audio (void *cls,
767 size_t data_size,
768 const void *data)
542{ 769{
543 GNUNET_CLIENT_notify_transmit_ready (h->client, 770 struct GNUNET_CONVERSATION_Call *call = cls;
544 sizeof (struct 771 struct GNUNET_MQ_Envelope *e;
545 ClientServerSessionRejectMessage), 772 struct ClientAudioMessage *am;
546 MAX_TRANSMIT_DELAY, GNUNET_YES, 773
547 &transmit_session_reject_message, h); 774 GNUNET_assert (CS_ACTIVE == call->state);
775 e = GNUNET_MQ_msg_extra (am,
776 data_size,
777 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO);
778 memcpy (&am[1], data, data_size);
779 GNUNET_MQ_send (call->mq, e);
548} 780}
549 781
550 782
551/** 783/**
552 * Auxiliary function to terminate a call. 784 * We received a `struct ClientPhonePickedupMessage`
553 * 785 *
554 * @param h conversation handle 786 * @param cls the `struct GNUNET_CONVERSATION_Call`
787 * @param msg the message
555 */ 788 */
556static void 789static void
557terminate_call (struct GNUNET_CONVERSATION_Handle *h) 790handle_call_picked_up (void *cls,
791 const struct GNUNET_MessageHeader *msg)
558{ 792{
559 GNUNET_CLIENT_notify_transmit_ready (h->client, 793 struct GNUNET_CONVERSATION_Call *call = cls;
560 sizeof (struct 794 const struct ClientPhonePickedupMessage *am;
561 ClientServerSessionTerminateMessage), 795 const char *metadata;
562 MAX_TRANSMIT_DELAY, GNUNET_YES, 796 size_t size;
563 &transmit_session_terminate_message, 797
564 h); 798 am = (const struct ClientPhonePickedupMessage *) msg;
799 size = ntohs (am->header.size) - sizeof (struct ClientPhonePickedupMessage);
800 metadata = (const char *) &am[1];
801 if ( (0 == size) ||
802 ('\0' != metadata[size - 1]) )
803 metadata = NULL;
804 switch (call->state)
805 {
806 case CS_LOOKUP:
807 GNUNET_break (0);
808 reconnect_call (call);
809 break;
810 case CS_RINGING:
811 call->state = CS_ACTIVE;
812 call->event_handler (call->event_handler_cls,
813 GNUNET_CONVERSATION_EC_READY,
814 metadata);
815 call->speaker->enable_speaker (call->speaker->cls);
816 call->mic->enable_microphone (call->mic->cls,
817 &transmit_call_audio,
818 call);
819 break;
820 case CS_ACTIVE:
821 GNUNET_break (0);
822 reconnect_call (call);
823 break;
824 case CS_SHUTDOWN:
825 GNUNET_CONVERSATION_call_stop (call, NULL);
826 break;
827 }
565} 828}
566 829
567 830
568/** 831/**
832 * We received a `struct ClientPhoneHangupMessage`
569 * 833 *
834 * @param cls the `struct GNUNET_CONVERSATION_Call`
835 * @param msg the message
570 */ 836 */
571static void 837static void
572gns_call_cb (void *cls, uint32_t rd_count, 838handle_call_hangup (void *cls,
573 const struct GNUNET_NAMESTORE_RecordData *rd) 839 const struct GNUNET_MessageHeader *msg)
574{ 840{
575 struct GNUNET_CONVERSATION_Handle *handle = cls; 841 struct GNUNET_CONVERSATION_Call *call = cls;
576 struct GNUNET_PeerIdentity peer; 842 const struct ClientPhoneHangupMessage *am;
577 unsigned int i; 843 const char *reason;
578 844 size_t size;
579 for (i=0;i<rd_count;i++) 845
846 am = (const struct ClientPhoneHangupMessage *) msg;
847 size = ntohs (am->header.size) - sizeof (struct ClientPhoneHangupMessage);
848 reason = (const char *) &am[1];
849 if ( (0 == size) ||
850 ('\0' != reason[size - 1]) )
851 reason = NULL;
852 switch (call->state)
580 { 853 {
581 switch (rd[i].record_type) 854 case CS_LOOKUP:
582 { 855 GNUNET_break (0);
583 case GNUNET_DNSPARSER_TYPE_TXT: /* FIXME: use fresh record type for voide... */ 856 reconnect_call (call);
584 if (GNUNET_OK != 857 break;
585 GNUNET_CRYPTO_ecc_public_sign_key_from_string (rd[i].data, 858 case CS_RINGING:
586 rd[i].data_size, 859 call->event_handler (call->event_handler_cls,
587 &peer.public_key)) 860 GNUNET_CONVERSATION_EC_TERMINATED,
588 { 861 reason);
589 GNUNET_break_op (0); 862 GNUNET_CONVERSATION_call_stop (call, NULL);
590 continue; 863 return;
591 } 864 case CS_ACTIVE:
592 initiate_call (handle, peer); 865 call->event_handler (call->event_handler_cls,
593 return; 866 GNUNET_CONVERSATION_EC_TERMINATED,
594 default: 867 reason);
595 break; 868 GNUNET_CONVERSATION_call_stop (call, NULL);
596 } 869 return;
870 case CS_SHUTDOWN:
871 GNUNET_CONVERSATION_call_stop (call, NULL);
872 break;
597 } 873 }
598 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
599 "Lookup failed\n");
600 handle->notification_handler (NULL, handle,
601 GNUNET_CONVERSATION_NT_NO_PEER,
602 NULL);
603} 874}
604 875
605 876
606/** 877/**
607* GNS lookup 878 * We received a `struct ClientAudioMessage`
608*/ 879 *
880 * @param cls the `struct GNUNET_CONVERSATION_Call`
881 * @param msg the message
882 */
609static void 883static void
610gns_lookup_and_call (struct GNUNET_CONVERSATION_Handle *h, const char *callee) 884handle_call_audio_message (void *cls,
611{ 885 const struct GNUNET_MessageHeader *msg)
612 char domain[GNUNET_DNSPARSER_MAX_NAME_LENGTH];
613 char *pos;
614
615 pos = domain;
616 strcpy (pos, "conversation");
617 pos += strlen ("conversation");
618 strcpy (pos, ".");
619 pos++;
620 strcpy (pos, callee);
621 pos += strlen (callee);
622
623 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Lookup for %s\n", domain);
624
625 GNUNET_GNS_lookup (h->gns,
626 domain,
627 NULL /* FIXME: ZONE! */,
628 GNUNET_DNSPARSER_TYPE_TXT,
629 GNUNET_NO,
630 NULL,
631 &gns_call_cb, h);
632}
633
634
635/******************************************************************************/
636/********************** API CALL DEFINITIONS *************************/
637/******************************************************************************/
638
639struct GNUNET_CONVERSATION_Handle *
640GNUNET_CONVERSATION_connect (const struct GNUNET_CONFIGURATION_Handle *cfg,
641 void *cls,
642 GNUNET_CONVERSATION_CallHandler call_handler,
643 GNUNET_CONVERSATION_RejectHandler reject_handler,
644 GNUNET_CONVERSATION_NotificationHandler notification_handler,
645 GNUNET_CONVERSATION_MissedCallHandler missed_call_handler)
646{ 886{
647 struct GNUNET_CONVERSATION_Handle *h; 887 struct GNUNET_CONVERSATION_Call *call = cls;
648 888 const struct ClientAudioMessage *am;
649 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
650 "GNUNET_CONVERSATION_connect()\n");
651 h = GNUNET_malloc (sizeof (struct GNUNET_CONVERSATION_Handle));
652
653 h->cfg = cfg;
654 h->call_handler = call_handler;
655 h->reject_handler = reject_handler;
656 h->notification_handler = notification_handler;
657 h->missed_call_handler = missed_call_handler;
658 889
659 if (NULL == (h->client = GNUNET_CLIENT_connect ("conversation", cfg))) 890 am = (const struct ClientAudioMessage *) msg;
660 { 891 switch (call->state)
661 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Could not access CONVERSATION service\n"); 892 {
662 GNUNET_break (0); 893 case CS_LOOKUP:
663 GNUNET_free (h); 894 GNUNET_break (0);
895 reconnect_call (call);
896 break;
897 case CS_RINGING:
898 GNUNET_break (0);
899 reconnect_call (call);
900 break;
901 case CS_ACTIVE:
902 call->speaker->play (call->speaker->cls,
903 ntohs (msg->size) - sizeof (struct ClientAudioMessage),
904 &am[1]);
905 break;
906 case CS_SHUTDOWN:
907 GNUNET_CONVERSATION_call_stop (call, NULL);
908 break;
664 909
665 return NULL; 910 }
666 } 911}
667 912
668 if (NULL == (h->gns = GNUNET_GNS_connect (cfg)))
669 {
670 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Could not access GNS service\n");
671 GNUNET_break (0);
672 GNUNET_CLIENT_disconnect (h->client);
673 GNUNET_free (h);
674 913
675 return NULL; 914/**
676 } 915 * Iterator called on obtained result for a GNS lookup.
916 *
917 * @param cls closure with the `struct GNUNET_CONVERSATION_Call`
918 * @param rd_count number of records in @a rd
919 * @param rd the records in reply
920 */
921static void
922handle_gns_response (void *cls,
923 uint32_t rd_count,
924 const struct GNUNET_NAMESTORE_RecordData *rd)
925{
926 struct GNUNET_CONVERSATION_Call *call = cls;
927 uint32_t i;
928 struct GNUNET_MQ_Envelope *e;
929 struct ClientCallMessage *ccm;
677 930
678 if (NULL == (h->namestore = GNUNET_NAMESTORE_connect (cfg))) 931 for (i=0;i<rd_count;i++)
932 {
933 if (GNUNET_NAMESTORE_TYPE_PHONE == rd[i].record_type)
679 { 934 {
680 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 935 if (rd[i].data_size != sizeof (struct PhoneRecord))
681 "Could not access NAMESTORE service\n"); 936 {
682 GNUNET_break (0); 937 GNUNET_break_op (0);
683 GNUNET_CLIENT_disconnect (h->client); 938 continue;
684 GNUNET_GNS_disconnect (h->gns); 939 }
685 GNUNET_free (h); 940 memcpy (&call->phone_record,
686 941 rd[i].data,
687 return NULL; 942 rd[i].data_size);
943 e = GNUNET_MQ_msg (ccm, GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_CALL);
944 ccm->line = call->phone_record.line;
945 ccm->target = call->phone_record.peer;
946 ccm->caller_id = *GNUNET_IDENTITY_ego_get_private_key (call->caller_id);
947 GNUNET_MQ_send (call->mq, e);
948 call->state = CS_RINGING;
949 call->event_handler (call->event_handler_cls,
950 GNUNET_CONVERSATION_EC_RINGING);
951 return;
688 } 952 }
689 953 }
690 check_gns (h); 954 /* not found */
691 GNUNET_CLIENT_receive (h->client, &receive_message_cb, h, 955 call->event_handler (call->event_handler_cls,
692 GNUNET_TIME_UNIT_FOREVER_REL); 956 GNUNET_CONVERSATION_EC_GNS_FAIL);
693 957 GNUNET_CONVERSATION_call_stop (call, NULL);
694 return h;
695} 958}
696 959
697 960
698void 961/**
699GNUNET_CONVERSATION_disconnect (struct GNUNET_CONVERSATION_Handle *handle) 962 * We encountered an error talking with the conversation service.
963 *
964 * @param cls the `struct GNUNET_CONVERSATION_Call`
965 * @param error details about the error
966 */
967static void
968call_error_handler (void *cls,
969 enum GNUNET_MQ_Error error)
700{ 970{
701 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "CONVERSATION DISCONNECT\n"); 971 struct GNUNET_CONVERSATION_Call *call = cls;
702
703 GNUNET_CLIENT_disconnect (handle->client);
704 GNUNET_GNS_disconnect (handle->gns);
705 972
706 GNUNET_free (handle); 973 GNUNET_break (0);
707 handle = NULL; 974 reconnect_call (call);
708} 975}
709 976
710 977
711void 978/**
712GNUNET_CONVERSATION_call (struct GNUNET_CONVERSATION_Handle *h, 979 * The call got disconnected, reconnect to the service.
713 const char *callee, 980 *
714 int doGnsLookup) 981 * @param call call to reconnect
982 */
983static void
984reconnect_call (struct GNUNET_CONVERSATION_Call *call)
715{ 985{
716 struct GNUNET_PeerIdentity peer; 986 static struct GNUNET_MQ_MessageHandler handlers[] =
717
718 if (NULL == h || NULL == h->client)
719 return;
720
721 if (GNUNET_YES == doGnsLookup)
722 { 987 {
723 gns_lookup_and_call (h, callee); 988 { &handle_call_busy,
724 return; 989 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_BUSY,
990 sizeof (struct ClientPhoneBusyMessage) },
991 { &handle_call_picked_up,
992 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_PICKED_UP,
993 0 },
994 { &handle_call_hangup,
995 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP,
996 0 },
997 { &handle_call_audio_message,
998 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO,
999 0 },
1000 { NULL, 0, 0 }
1001 };
1002 if (NULL != call->mq)
1003 {
1004 GNUNET_MQ_destroy (call->mq);
1005 call->mq = NULL;
725 } 1006 }
726 if (GNUNET_OK != 1007 if (NULL != call->client)
727 GNUNET_CRYPTO_ecc_public_sign_key_from_string (callee,
728 strlen (callee),
729 &peer.public_key))
730 { 1008 {
731 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 1009 GNUNET_CLIENT_disconnect (call->client);
732 _("`%s' is not a valid public key\n"), 1010 call->client = NULL;
733 callee); 1011 }
734 h->notification_handler (NULL, h, GNUNET_CONVERSATION_NT_NO_PEER, NULL); 1012 call->client = GNUNET_CLIENT_connect ("conversation", call->cfg);
1013 if (NULL == call->client)
735 return; 1014 return;
736 } 1015 call->mq = GNUNET_MQ_queue_for_connection_client (call->client,
737 initiate_call (h, peer); 1016 handlers,
1017 &call_error_handler,
1018 call);
738} 1019}
739 1020
740 1021
741void 1022/**
742GNUNET_CONVERSATION_hangup (struct GNUNET_CONVERSATION_Handle *h) 1023 * Call the phone of another user.
1024 *
1025 * @param cfg configuration to use, specifies our phone service
1026 * @param caller_id identity of the caller
1027 * @param callee GNS name of the callee (used to locate the callee's record)
1028 * @param speaker speaker to use (will be used automatically immediately once the
1029 * #GNUNET_CONVERSATION_EC_READY event is generated); we will NOT generate
1030 * a ring tone on the speaker
1031 * @param mic microphone to use (will be used automatically immediately once the
1032 * #GNUNET_CONVERSATION_EC_READY event is generated)
1033 * @param event_handler how to notify the owner of the phone about events
1034 * @param event_handler_cls closure for @a event_handler
1035 */
1036struct GNUNET_CONVERSATION_Call *
1037GNUNET_CONVERSATION_call_start (const struct GNUNET_CONFIGURATION_Handle *cfg,
1038 struct GNUNET_IDENTITY_Ego *caller_id,
1039 const char *callee,
1040 struct GNUNET_SPEAKER_Handle *speaker,
1041 struct GNUNET_MICROPHONE_Handle *mic,
1042 GNUNET_CONVERSATION_EventHandler event_handler,
1043 void *event_handler_cls)
743{ 1044{
744 if (NULL == h || NULL == h->client) 1045 struct GNUNET_CONVERSATION_Call *call;
745 return; 1046 struct GNUNET_CRYPTO_EccPublicSignKey my_zone;
746 1047
747 terminate_call (h); 1048 GNUNET_IDENTITY_ego_get_public_key (caller_id,
1049 &my_zone);
1050 call = GNUNET_new (struct GNUNET_CONVERSATION_Call);
1051 call->cfg = cfg;
1052 call->caller_id = caller_id;
1053 call->callee = GNUNET_strdup (callee);
1054 call->speaker = speaker;
1055 call->mic = mic;
1056 call->event_handler = event_handler;
1057 call->event_handler_cls = event_handler_cls;
1058 call->gns = GNUNET_GNS_connect (cfg);
1059 reconnect_call (call);
1060
1061 if ( (NULL == call->client) ||
1062 (NULL == call->gns) )
1063 {
1064 GNUNET_CONVERSATION_call_stop (call, NULL);
1065 return NULL;
1066 }
1067 call->gns_lookup = GNUNET_GNS_lookup (call->gns, callee,
1068 &my_zone,
1069 GNUNET_NAMESTORE_TYPE_PHONE,
1070 GNUNET_NO,
1071 NULL /* FIXME: add shortening support */,
1072 &handle_gns_response, call);
1073 GNUNET_assert (NULL != call->gns_lookup);
1074 return call;
748} 1075}
749 1076
750 1077
1078/**
1079 * Terminate a call. The call may be ringing or ready at this time.
1080 *
1081 * @param call call to terminate
1082 * @param reason if the call was active (ringing or ready) this will be the
1083 * reason given to the other user for why we hung up
1084 */
751void 1085void
752GNUNET_CONVERSATION_accept (struct GNUNET_CONVERSATION_Handle *h) 1086GNUNET_CONVERSATION_call_stop (struct GNUNET_CONVERSATION_Call *call,
1087 const char *reason)
753{ 1088{
754 if (NULL == h || NULL == h->client) 1089 if (NULL != reason)
755 return; 1090 {
1091 // FIXME: transmit reason to service... (not implemented!)
1092 GNUNET_break (0);
1093 // return;
1094 }
1095 if (NULL != call->speaker)
1096 {
1097 if (CS_ACTIVE == call->state)
1098 call->speaker->disable_speaker (call->speaker->cls);
1099 call->speaker = NULL;
1100 }
1101 if (NULL != call->mic)
1102 {
1103 if (CS_ACTIVE == call->state)
1104 call->mic->disable_microphone (call->mic->cls);
1105 call->mic =NULL;
1106 }
1107 if (NULL != call->mq)
1108 {
1109 GNUNET_MQ_destroy (call->mq);
1110 call->mq = NULL;
1111 }
1112 if (NULL != call->client)
1113 {
1114 GNUNET_CLIENT_disconnect (call->client);
1115 call->client = NULL;
1116 }
1117 if (NULL != call->gns_lookup)
1118 {
1119 GNUNET_GNS_lookup_cancel (call->gns_lookup);
1120 call->gns_lookup = NULL;
1121 }
1122 if (NULL != call->gns)
1123 {
1124 GNUNET_GNS_disconnect (call->gns);
1125 call->gns = NULL;
1126 }
756 1127
757 accept_call (h); 1128 GNUNET_free (call);
758} 1129}
759 1130
760 1131
761void
762GNUNET_CONVERSATION_reject (struct GNUNET_CONVERSATION_Handle *h)
763{
764 if (NULL == h || NULL == h->client)
765 return;
766
767 reject_call (h);
768}
769
770/* end of conversation_api.c */ 1132/* end of conversation_api.c */
diff --git a/src/conversation/conversation_api2.c b/src/conversation/conversation_api2.c
deleted file mode 100644
index 6ce198e64..000000000
--- a/src/conversation/conversation_api2.c
+++ /dev/null
@@ -1,1132 +0,0 @@
1/*
2 This file is part of GNUnet
3 (C) 2013 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/conversation_api2.c
23 * @brief API to the conversation service
24 * @author Simon Dieterle
25 * @author Andreas Fuchs
26 * @author Christian Grothoff
27 */
28#include "platform.h"
29#include "gnunet_conversation_service.h"
30#include "gnunet_gns_service.h"
31#include "conversation.h"
32
33
34/**
35 * A phone record specifies which peer is hosting a given user and
36 * may also specify the phone line that is used (typically zero).
37 * The version is also right now always zero.
38 */
39struct PhoneRecord
40{
41
42 /**
43 * Version of the phone record, for now always zero. We may
44 * use other versions for anonymously hosted phone lines in
45 * the future.
46 */
47 uint32_t version GNUNET_PACKED;
48
49 /**
50 * Phone line to use at the peer.
51 */
52 uint32_t line GNUNET_PACKED;
53
54 /**
55 * Identity of the peer hosting the phone service.
56 */
57 struct GNUNET_PeerIdentity peer;
58
59};
60
61
62/**
63 * Possible states of the phone.
64 */
65enum PhoneState
66{
67 /**
68 * We still need to register the phone.
69 */
70 PS_REGISTER = 0,
71
72 /**
73 * We are waiting for a call.
74 */
75 PS_WAITING,
76
77 /**
78 * The phone is ringing.
79 */
80 PS_RINGING,
81
82 /**
83 * The phone is in an active conversation.
84 */
85 PS_ACTIVE
86};
87
88
89/**
90 * A phone is a device that can ring to signal an incoming call and
91 * that you can pick up to answer the call and hang up to terminate
92 * the call. You can also hang up a ringing phone immediately
93 * (without picking it up) to stop it from ringing. Phones have
94 * caller ID. You can ask the phone for its record and make that
95 * record available (via GNS) to enable others to call you.
96 * Multiple phones maybe connected to the same line (the line is
97 * something rather internal to a phone and not obvious from it).
98 * You can only have one conversation per phone at any time.
99 */
100struct GNUNET_CONVERSATION_Phone
101{
102 /**
103 * Our configuration.
104 */
105 const struct GNUNET_CONFIGURATION_Handle *cfg;
106
107 /**
108 * Handle to talk with CONVERSATION service.
109 */
110 struct GNUNET_CLIENT_Connection *client;
111
112 /**
113 * Function to call for phone events.
114 */
115 GNUNET_CONVERSATION_EventHandler event_handler;
116
117 /**
118 * Closure for @e event_handler
119 */
120 void *event_handler_cls;
121
122 /**
123 * Speaker, or NULL if none is attached.
124 */
125 struct GNUNET_SPEAKER_Handle *speaker;
126
127 /**
128 * Microphone, or NULL if none is attached.
129 */
130 struct GNUNET_MICROPHONE_Handle *mic;
131
132 /**
133 * Connection to NAMESTORE (for reverse lookup).
134 */
135 struct GNUNET_NAMESTORE_Handle *ns;
136
137 /**
138 * Active NAMESTORE lookup (or NULL).
139 */
140 struct GNUNET_NAMESTORE_QueueEntry *qe;
141
142 /**
143 * Handle for transmitting to the CONVERSATION service.
144 */
145 struct GNUNET_MQ_Handle *mq;
146
147 /**
148 * This phone's record.
149 */
150 struct PhoneRecord my_record;
151
152 /**
153 * My GNS zone.
154 */
155 struct GNUNET_CRYPTO_EccPrivateKey my_zone;
156
157 /**
158 * Identity of the person calling us (valid while in state #PS_RINGING).
159 */
160 struct GNUNET_CRYPTO_EccPublicSignKey caller_id;
161
162 /**
163 * State machine for the phone.
164 */
165 enum PhoneState state;
166
167};
168
169
170/**
171 * The phone got disconnected, reconnect to the service.
172 *
173 * @param phone phone to reconnect
174 */
175static void
176reconnect_phone (struct GNUNET_CONVERSATION_Phone *phone);
177
178
179/**
180 * We have resolved the caller ID using our name service.
181 *
182 * @param cls the `struct GNUNET_CONVERSATION_Phone`
183 * @param zone our zone used for resolution
184 * @param label name of the caller
185 * @param rd_count number of records we have in @a rd
186 * @param rd records we have for the caller's label
187 */
188static void
189handle_caller_name (void *cls,
190 const struct GNUNET_CRYPTO_EccPrivateKey *zone,
191 const char *label,
192 unsigned int rd_count,
193 const struct GNUNET_NAMESTORE_RecordData *rd)
194{
195 struct GNUNET_CONVERSATION_Phone *phone = cls;
196 char *name;
197
198 phone->qe = NULL;
199 if (NULL == label)
200 name = GNUNET_strdup (GNUNET_NAMESTORE_pkey_to_zkey (&phone->caller_id));
201 else
202 GNUNET_asprintf (&name, "%.gnu", label);
203 phone->event_handler (phone->event_handler_cls,
204 GNUNET_CONVERSATION_EC_RING,
205 name);
206 GNUNET_free (name);
207}
208
209
210/**
211 * We received a `struct ClientPhoneRingMessage`
212 *
213 * @param cls the `struct GNUNET_CONVERSATION_Phone`
214 * @param msg the message
215 */
216static void
217handle_phone_ring (void *cls,
218 const struct GNUNET_MessageHeader *msg)
219{
220 struct GNUNET_CONVERSATION_Phone *phone = cls;
221 const struct ClientPhoneRingMessage *ring;
222
223 ring = (const struct ClientPhoneRingMessage *) msg;
224 switch (phone->state)
225 {
226 case PS_REGISTER:
227 GNUNET_assert (0);
228 break;
229 case PS_WAITING:
230 phone->state = PS_RINGING;
231 phone->caller_id = ring->caller_id;
232 phone->qe = GNUNET_NAMESTORE_zone_to_name (phone->ns,
233 &phone->my_zone,
234 &ring->caller_id,
235 &handle_caller_name,
236 phone);
237 break;
238 case PS_RINGING:
239 GNUNET_break (0);
240 reconnect_phone (phone);
241 break;
242 case PS_ACTIVE:
243 GNUNET_break (0);
244 reconnect_phone (phone);
245 break;
246 }
247}
248
249
250/**
251 * We received a `struct ClientPhoneHangupMessage`.
252 *
253 * @param cls the `struct GNUNET_CONVERSATION_Phone`
254 * @param msg the message
255 */
256static void
257handle_phone_hangup (void *cls,
258 const struct GNUNET_MessageHeader *msg)
259{
260 struct GNUNET_CONVERSATION_Phone *phone = cls;
261 const struct ClientPhoneHangupMessage *hang;
262 size_t len;
263 const char *reason;
264
265 hang = (const struct ClientPhoneHangupMessage *) msg;
266 reason = (const char *) &hang[1];
267 len = htons (hang->header.size) - sizeof (struct ClientPhoneHangupMessage);
268 if ( (0 == len) ||
269 ('\0' != reason[len-1]) )
270 {
271 GNUNET_break (0);
272 reconnect_phone (phone);
273 return;
274 }
275 switch (phone->state)
276 {
277 case PS_REGISTER:
278 GNUNET_assert (0);
279 break;
280 case PS_WAITING:
281 GNUNET_break (0);
282 reconnect_phone (phone);
283 break;
284 case PS_RINGING:
285 if (NULL != phone->qe)
286 {
287 GNUNET_NAMESTORE_cancel (phone->qe);
288 phone->qe = NULL;
289 phone->state = PS_WAITING;
290 break;
291 }
292 phone->state = PS_WAITING;
293 phone->event_handler (phone->event_handler_cls,
294 GNUNET_CONVERSATION_EC_TERMINATED,
295 reason);
296 break;
297 case PS_ACTIVE:
298 GNUNET_break (NULL == phone->qe);
299 phone->state = PS_WAITING;
300 phone->event_handler (phone->event_handler_cls,
301 GNUNET_CONVERSATION_EC_TERMINATED,
302 reason);
303 break;
304 }
305}
306
307
308/**
309 * We received a `struct ClientAudioMessage`
310 *
311 * @param cls the `struct GNUNET_CONVERSATION_Phone`
312 * @param msg the message
313 */
314static void
315handle_phone_audio_message (void *cls,
316 const struct GNUNET_MessageHeader *msg)
317{
318 struct GNUNET_CONVERSATION_Phone *phone = cls;
319 const struct ClientAudioMessage *am;
320
321 am = (const struct ClientAudioMessage *) msg;
322 switch (phone->state)
323 {
324 case PS_REGISTER:
325 GNUNET_assert (0);
326 break;
327 case PS_WAITING:
328 GNUNET_break (0);
329 reconnect_phone (phone);
330 break;
331 case PS_RINGING:
332 GNUNET_break (0);
333 reconnect_phone (phone);
334 break;
335 case PS_ACTIVE:
336 phone->speaker->play (phone->speaker->cls,
337 ntohs (msg->size) - sizeof (struct ClientAudioMessage),
338 &am[1]);
339 break;
340 }
341}
342
343
344/**
345 * We encountered an error talking with the conversation service.
346 *
347 * @param cls the `struct GNUNET_CONVERSATION_Phone`
348 * @param error details about the error
349 */
350static void
351phone_error_handler (void *cls,
352 enum GNUNET_MQ_Error error)
353{
354 struct GNUNET_CONVERSATION_Phone *phone = cls;
355
356 GNUNET_break (0);
357 reconnect_phone (phone);
358}
359
360
361/**
362 * The phone got disconnected, reconnect to the service.
363 *
364 * @param phone phone to reconnect
365 */
366static void
367reconnect_phone (struct GNUNET_CONVERSATION_Phone *phone)
368{
369 static struct GNUNET_MQ_MessageHandler handlers[] =
370 {
371 { &handle_phone_ring,
372 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_RING,
373 sizeof (struct ClientPhoneRingMessage) },
374 { &handle_phone_hangup,
375 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP,
376 0 },
377 { &handle_phone_audio_message,
378 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO,
379 0 },
380 { NULL, 0, 0 }
381 };
382 struct GNUNET_MQ_Envelope *e;
383 struct ClientPhoneRegisterMessage *reg;
384
385 if (NULL != phone->mq)
386 {
387 GNUNET_MQ_destroy (phone->mq);
388 phone->mq = NULL;
389 }
390 if (NULL != phone->client)
391 {
392 GNUNET_CLIENT_disconnect (phone->client);
393 phone->client = NULL;
394 }
395 phone->state = PS_REGISTER;
396 phone->client = GNUNET_CLIENT_connect ("conversation", phone->cfg);
397 if (NULL == phone->client)
398 return;
399 phone->mq = GNUNET_MQ_queue_for_connection_client (phone->client,
400 handlers,
401 &phone_error_handler,
402 phone);
403 e = GNUNET_MQ_msg (reg, GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_REGISTER);
404 reg->line = phone->my_record.line;
405 GNUNET_MQ_send (phone->mq, e);
406 phone->state = PS_WAITING;
407}
408
409
410/**
411 * Create a new phone.
412 *
413 * @param cfg configuration for the phone; specifies the phone service and
414 * which line the phone is to be connected to
415 * @param ego ego to use for name resolution (when determining caller ID)
416 * @param event_handler how to notify the owner of the phone about events
417 * @param event_handler_cls closure for @a event_handler
418 */
419struct GNUNET_CONVERSATION_Phone *
420GNUNET_CONVERSATION_phone_create (const struct GNUNET_CONFIGURATION_Handle *cfg,
421 const struct GNUNET_IDENTITY_Ego *ego,
422 GNUNET_CONVERSATION_EventHandler event_handler,
423 void *event_handler_cls)
424{
425 struct GNUNET_CONVERSATION_Phone *phone;
426 unsigned long long line;
427
428 if (GNUNET_OK !=
429 GNUNET_CONFIGURATION_get_value_number (cfg,
430 "CONVERSATION",
431 "LINE",
432 &line))
433 return NULL;
434 phone = GNUNET_new (struct GNUNET_CONVERSATION_Phone);
435 if (GNUNET_OK !=
436 GNUNET_CRYPTO_get_host_identity (cfg,
437 &phone->my_record.peer))
438 {
439 GNUNET_break (0);
440 GNUNET_free (phone);
441 return NULL;
442 }
443 phone->cfg = cfg;
444 phone->my_zone = *GNUNET_IDENTITY_ego_get_private_key (ego);
445 phone->event_handler = event_handler;
446 phone->event_handler_cls = event_handler_cls;
447 phone->ns = GNUNET_NAMESTORE_connect (cfg);
448 phone->my_record.line = htonl ((uint32_t) line);
449 phone->my_record.version = htonl (0);
450 reconnect_phone (phone);
451 if ( (NULL == phone->client) ||
452 (NULL == phone->ns) )
453 {
454 GNUNET_break (0);
455 GNUNET_CONVERSATION_phone_destroy (phone);
456 return NULL;
457 }
458 return phone;
459}
460
461
462/**
463 * Fill in a namestore record with the contact information
464 * for this phone. Note that the filled in "data" value
465 * is only valid until the phone is destroyed.
466 *
467 * @param phone phone to create a record for
468 * @param rd namestore record to fill in
469 */
470void
471GNUNET_CONVERSATION_phone_get_record (struct GNUNET_CONVERSATION_Phone *phone,
472 struct GNUNET_NAMESTORE_RecordData *rd)
473{
474 rd->data = &phone->my_record;
475 rd->expiration_time = 0;
476 rd->data_size = sizeof (struct PhoneRecord);
477 rd->record_type = GNUNET_NAMESTORE_TYPE_PHONE;
478 rd->flags = GNUNET_NAMESTORE_RF_NONE;
479}
480
481
482/**
483 * Process recorded audio data.
484 *
485 * @param cls closure with the `struct GNUNET_CONVERSATION_Phone`
486 * @param data_size number of bytes in @a data
487 * @param data audio data to play
488 */
489static void
490transmit_phone_audio (void *cls,
491 size_t data_size,
492 const void *data)
493{
494 struct GNUNET_CONVERSATION_Phone *phone = cls;
495 struct GNUNET_MQ_Envelope *e;
496 struct ClientAudioMessage *am;
497
498 GNUNET_assert (PS_ACTIVE == phone->state);
499 e = GNUNET_MQ_msg_extra (am,
500 data_size,
501 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO);
502 memcpy (&am[1], data, data_size);
503 GNUNET_MQ_send (phone->mq, e);
504}
505
506
507/**
508 * Picks up a (ringing) phone. This will connect the speaker
509 * to the microphone of the other party, and vice versa.
510 *
511 * @param phone phone to pick up
512 * @param metadata meta data to give to the other user about the pick up event
513 * @param speaker speaker to use
514 * @param mic microphone to use
515 */
516void
517GNUNET_CONVERSATION_phone_pick_up (struct GNUNET_CONVERSATION_Phone *phone,
518 const char *metadata,
519 struct GNUNET_SPEAKER_Handle *speaker,
520 struct GNUNET_MICROPHONE_Handle *mic)
521{
522 struct GNUNET_MQ_Envelope *e;
523 struct ClientPhonePickupMessage *pick;
524 size_t slen;
525
526 GNUNET_assert (PS_RINGING == phone->state);
527 phone->speaker = speaker;
528 phone->mic = mic;
529 slen = strlen (metadata) + 1;
530 e = GNUNET_MQ_msg_extra (pick, slen, GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_PICK_UP);
531 memcpy (&pick[1], metadata, slen);
532 GNUNET_MQ_send (phone->mq, e);
533 phone->state = PS_ACTIVE;
534 phone->speaker->enable_speaker (phone->speaker->cls);
535 phone->mic->enable_microphone (phone->mic->cls,
536 &transmit_phone_audio,
537 phone);
538}
539
540
541/**
542 * Hang up up a (possibly ringing) phone. This will notify the other
543 * party that we are no longer interested in talking with them.
544 *
545 * @param phone phone to pick up
546 * @param reason text we give to the other party about why we terminated the conversation
547 */
548void
549GNUNET_CONVERSATION_phone_hang_up (struct GNUNET_CONVERSATION_Phone *phone,
550 const char *reason)
551{
552 struct GNUNET_MQ_Envelope *e;
553 struct ClientPhoneHangupMessage *hang;
554 size_t slen;
555
556 GNUNET_assert ( (PS_RINGING == phone->state) ||
557 (PS_ACTIVE == phone->state) );
558 phone->speaker->disable_speaker (phone->speaker->cls);
559 phone->mic->disable_microphone (phone->mic->cls);
560 phone->speaker = NULL;
561 phone->mic = NULL;
562 slen = strlen (reason) + 1;
563 e = GNUNET_MQ_msg_extra (hang, slen, GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP);
564 memcpy (&hang[1], reason, slen);
565 GNUNET_MQ_send (phone->mq, e);
566 phone->state = PS_WAITING;
567}
568
569
570/**
571 * Destroys a phone.
572 *
573 * @param phone phone to destroy
574 */
575void
576GNUNET_CONVERSATION_phone_destroy (struct GNUNET_CONVERSATION_Phone *phone)
577{
578 if (NULL != phone->speaker)
579 {
580 phone->speaker->disable_speaker (phone->speaker->cls);
581 phone->speaker = NULL;
582 }
583 if (NULL != phone->mic)
584 {
585 phone->mic->disable_microphone (phone->mic->cls);
586 phone->mic = NULL;
587 }
588 if (NULL != phone->qe)
589 {
590 GNUNET_NAMESTORE_cancel (phone->qe);
591 phone->qe = NULL;
592 }
593 if (NULL != phone->ns)
594 {
595 GNUNET_NAMESTORE_disconnect (phone->ns);
596 phone->ns = NULL;
597 }
598 if (NULL != phone->mq)
599 {
600 GNUNET_MQ_destroy (phone->mq);
601 phone->mq = NULL;
602 }
603 if (NULL != phone->client)
604 {
605 GNUNET_CLIENT_disconnect (phone->client);
606 phone->client = NULL;
607 }
608 GNUNET_free (phone);
609}
610
611
612/* ******************************* Call API *************************** */
613
614/**
615 * Possible states of the phone.
616 */
617enum CallState
618{
619 /**
620 * We still need to lookup the callee.
621 */
622 CS_LOOKUP = 0,
623
624 /**
625 * The call is ringing.
626 */
627 CS_RINGING,
628
629 /**
630 * The call is in an active conversation.
631 */
632 CS_ACTIVE,
633
634 /**
635 * The call is in termination.
636 */
637 CS_SHUTDOWN
638};
639
640
641/**
642 * Handle for an outgoing call.
643 */
644struct GNUNET_CONVERSATION_Call
645{
646
647 /**
648 * Our configuration.
649 */
650 const struct GNUNET_CONFIGURATION_Handle *cfg;
651
652 /**
653 * Handle to talk with CONVERSATION service.
654 */
655 struct GNUNET_CLIENT_Connection *client;
656
657 /**
658 * Our caller identity.
659 */
660 struct GNUNET_IDENTITY_Ego *caller_id;
661
662 /**
663 * Target callee as a GNS address/name.
664 */
665 char *callee;
666
667 /**
668 * Our speaker.
669 */
670 struct GNUNET_SPEAKER_Handle *speaker;
671
672 /**
673 * Our microphone.
674 */
675 struct GNUNET_MICROPHONE_Handle *mic;
676
677 /**
678 * Function to call with events.
679 */
680 GNUNET_CONVERSATION_EventHandler event_handler;
681
682 /**
683 * Closure for @e event_handler
684 */
685 void *event_handler_cls;
686
687 /**
688 * Handle for transmitting to the CONVERSATION service.
689 */
690 struct GNUNET_MQ_Handle *mq;
691
692 /**
693 * Connection to GNS (can be NULL).
694 */
695 struct GNUNET_GNS_Handle *gns;
696
697 /**
698 * Active GNS lookup (or NULL).
699 */
700 struct GNUNET_GNS_LookupRequest *gns_lookup;
701
702 /**
703 * Target phone record, only valid after the lookup is done.
704 */
705 struct PhoneRecord phone_record;
706
707 /**
708 * State machine for the call.
709 */
710 enum CallState state;
711
712};
713
714
715/**
716 * The call got disconnected, reconnect to the service.
717 *
718 * @param call call to reconnect
719 */
720static void
721reconnect_call (struct GNUNET_CONVERSATION_Call *call);
722
723
724/**
725 * We received a `struct ClientPhoneBusyMessage`
726 *
727 * @param cls the `struct GNUNET_CONVERSATION_Call`
728 * @param msg the message
729 */
730static void
731handle_call_busy (void *cls,
732 const struct GNUNET_MessageHeader *msg)
733{
734 struct GNUNET_CONVERSATION_Call *call = cls;
735
736 switch (call->state)
737 {
738 case CS_LOOKUP:
739 GNUNET_break (0);
740 reconnect_call (call);
741 break;
742 case CS_RINGING:
743 call->event_handler (call->event_handler_cls,
744 GNUNET_CONVERSATION_EC_BUSY);
745 GNUNET_CONVERSATION_call_stop (call, NULL);
746 break;
747 case CS_ACTIVE:
748 GNUNET_break (0);
749 reconnect_call (call);
750 break;
751 case CS_SHUTDOWN:
752 GNUNET_CONVERSATION_call_stop (call, NULL);
753 break;
754 }
755}
756
757
758/**
759 * Process recorded audio data.
760 *
761 * @param cls closure with the `struct GNUNET_CONVERSATION_Call`
762 * @param data_size number of bytes in @a data
763 * @param data audio data to play
764 */
765static void
766transmit_call_audio (void *cls,
767 size_t data_size,
768 const void *data)
769{
770 struct GNUNET_CONVERSATION_Call *call = cls;
771 struct GNUNET_MQ_Envelope *e;
772 struct ClientAudioMessage *am;
773
774 GNUNET_assert (CS_ACTIVE == call->state);
775 e = GNUNET_MQ_msg_extra (am,
776 data_size,
777 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO);
778 memcpy (&am[1], data, data_size);
779 GNUNET_MQ_send (call->mq, e);
780}
781
782
783/**
784 * We received a `struct ClientPhonePickedupMessage`
785 *
786 * @param cls the `struct GNUNET_CONVERSATION_Call`
787 * @param msg the message
788 */
789static void
790handle_call_picked_up (void *cls,
791 const struct GNUNET_MessageHeader *msg)
792{
793 struct GNUNET_CONVERSATION_Call *call = cls;
794 const struct ClientPhonePickedupMessage *am;
795 const char *metadata;
796 size_t size;
797
798 am = (const struct ClientPhonePickedupMessage *) msg;
799 size = ntohs (am->header.size) - sizeof (struct ClientPhonePickedupMessage);
800 metadata = (const char *) &am[1];
801 if ( (0 == size) ||
802 ('\0' != metadata[size - 1]) )
803 metadata = NULL;
804 switch (call->state)
805 {
806 case CS_LOOKUP:
807 GNUNET_break (0);
808 reconnect_call (call);
809 break;
810 case CS_RINGING:
811 call->state = CS_ACTIVE;
812 call->event_handler (call->event_handler_cls,
813 GNUNET_CONVERSATION_EC_READY,
814 metadata);
815 call->speaker->enable_speaker (call->speaker->cls);
816 call->mic->enable_microphone (call->mic->cls,
817 &transmit_call_audio,
818 call);
819 break;
820 case CS_ACTIVE:
821 GNUNET_break (0);
822 reconnect_call (call);
823 break;
824 case CS_SHUTDOWN:
825 GNUNET_CONVERSATION_call_stop (call, NULL);
826 break;
827 }
828}
829
830
831/**
832 * We received a `struct ClientPhoneHangupMessage`
833 *
834 * @param cls the `struct GNUNET_CONVERSATION_Call`
835 * @param msg the message
836 */
837static void
838handle_call_hangup (void *cls,
839 const struct GNUNET_MessageHeader *msg)
840{
841 struct GNUNET_CONVERSATION_Call *call = cls;
842 const struct ClientPhoneHangupMessage *am;
843 const char *reason;
844 size_t size;
845
846 am = (const struct ClientPhoneHangupMessage *) msg;
847 size = ntohs (am->header.size) - sizeof (struct ClientPhoneHangupMessage);
848 reason = (const char *) &am[1];
849 if ( (0 == size) ||
850 ('\0' != reason[size - 1]) )
851 reason = NULL;
852 switch (call->state)
853 {
854 case CS_LOOKUP:
855 GNUNET_break (0);
856 reconnect_call (call);
857 break;
858 case CS_RINGING:
859 call->event_handler (call->event_handler_cls,
860 GNUNET_CONVERSATION_EC_TERMINATED,
861 reason);
862 GNUNET_CONVERSATION_call_stop (call, NULL);
863 return;
864 case CS_ACTIVE:
865 call->event_handler (call->event_handler_cls,
866 GNUNET_CONVERSATION_EC_TERMINATED,
867 reason);
868 GNUNET_CONVERSATION_call_stop (call, NULL);
869 return;
870 case CS_SHUTDOWN:
871 GNUNET_CONVERSATION_call_stop (call, NULL);
872 break;
873 }
874}
875
876
877/**
878 * We received a `struct ClientAudioMessage`
879 *
880 * @param cls the `struct GNUNET_CONVERSATION_Call`
881 * @param msg the message
882 */
883static void
884handle_call_audio_message (void *cls,
885 const struct GNUNET_MessageHeader *msg)
886{
887 struct GNUNET_CONVERSATION_Call *call = cls;
888 const struct ClientAudioMessage *am;
889
890 am = (const struct ClientAudioMessage *) msg;
891 switch (call->state)
892 {
893 case CS_LOOKUP:
894 GNUNET_break (0);
895 reconnect_call (call);
896 break;
897 case CS_RINGING:
898 GNUNET_break (0);
899 reconnect_call (call);
900 break;
901 case CS_ACTIVE:
902 call->speaker->play (call->speaker->cls,
903 ntohs (msg->size) - sizeof (struct ClientAudioMessage),
904 &am[1]);
905 break;
906 case CS_SHUTDOWN:
907 GNUNET_CONVERSATION_call_stop (call, NULL);
908 break;
909
910 }
911}
912
913
914/**
915 * Iterator called on obtained result for a GNS lookup.
916 *
917 * @param cls closure with the `struct GNUNET_CONVERSATION_Call`
918 * @param rd_count number of records in @a rd
919 * @param rd the records in reply
920 */
921static void
922handle_gns_response (void *cls,
923 uint32_t rd_count,
924 const struct GNUNET_NAMESTORE_RecordData *rd)
925{
926 struct GNUNET_CONVERSATION_Call *call = cls;
927 uint32_t i;
928 struct GNUNET_MQ_Envelope *e;
929 struct ClientCallMessage *ccm;
930
931 for (i=0;i<rd_count;i++)
932 {
933 if (GNUNET_NAMESTORE_TYPE_PHONE == rd[i].record_type)
934 {
935 if (rd[i].data_size != sizeof (struct PhoneRecord))
936 {
937 GNUNET_break_op (0);
938 continue;
939 }
940 memcpy (&call->phone_record,
941 rd[i].data,
942 rd[i].data_size);
943 e = GNUNET_MQ_msg (ccm, GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_CALL);
944 ccm->line = call->phone_record.line;
945 ccm->target = call->phone_record.peer;
946 ccm->caller_id = *GNUNET_IDENTITY_ego_get_private_key (call->caller_id);
947 GNUNET_MQ_send (call->mq, e);
948 call->state = CS_RINGING;
949 call->event_handler (call->event_handler_cls,
950 GNUNET_CONVERSATION_EC_RINGING);
951 return;
952 }
953 }
954 /* not found */
955 call->event_handler (call->event_handler_cls,
956 GNUNET_CONVERSATION_EC_GNS_FAIL);
957 GNUNET_CONVERSATION_call_stop (call, NULL);
958}
959
960
961/**
962 * We encountered an error talking with the conversation service.
963 *
964 * @param cls the `struct GNUNET_CONVERSATION_Call`
965 * @param error details about the error
966 */
967static void
968call_error_handler (void *cls,
969 enum GNUNET_MQ_Error error)
970{
971 struct GNUNET_CONVERSATION_Call *call = cls;
972
973 GNUNET_break (0);
974 reconnect_call (call);
975}
976
977
978/**
979 * The call got disconnected, reconnect to the service.
980 *
981 * @param call call to reconnect
982 */
983static void
984reconnect_call (struct GNUNET_CONVERSATION_Call *call)
985{
986 static struct GNUNET_MQ_MessageHandler handlers[] =
987 {
988 { &handle_call_busy,
989 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_BUSY,
990 sizeof (struct ClientPhoneBusyMessage) },
991 { &handle_call_picked_up,
992 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_PICKED_UP,
993 0 },
994 { &handle_call_hangup,
995 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP,
996 0 },
997 { &handle_call_audio_message,
998 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO,
999 0 },
1000 { NULL, 0, 0 }
1001 };
1002 if (NULL != call->mq)
1003 {
1004 GNUNET_MQ_destroy (call->mq);
1005 call->mq = NULL;
1006 }
1007 if (NULL != call->client)
1008 {
1009 GNUNET_CLIENT_disconnect (call->client);
1010 call->client = NULL;
1011 }
1012 call->client = GNUNET_CLIENT_connect ("conversation", call->cfg);
1013 if (NULL == call->client)
1014 return;
1015 call->mq = GNUNET_MQ_queue_for_connection_client (call->client,
1016 handlers,
1017 &call_error_handler,
1018 call);
1019}
1020
1021
1022/**
1023 * Call the phone of another user.
1024 *
1025 * @param cfg configuration to use, specifies our phone service
1026 * @param caller_id identity of the caller
1027 * @param callee GNS name of the callee (used to locate the callee's record)
1028 * @param speaker speaker to use (will be used automatically immediately once the
1029 * #GNUNET_CONVERSATION_EC_READY event is generated); we will NOT generate
1030 * a ring tone on the speaker
1031 * @param mic microphone to use (will be used automatically immediately once the
1032 * #GNUNET_CONVERSATION_EC_READY event is generated)
1033 * @param event_handler how to notify the owner of the phone about events
1034 * @param event_handler_cls closure for @a event_handler
1035 */
1036struct GNUNET_CONVERSATION_Call *
1037GNUNET_CONVERSATION_call_start (const struct GNUNET_CONFIGURATION_Handle *cfg,
1038 struct GNUNET_IDENTITY_Ego *caller_id,
1039 const char *callee,
1040 struct GNUNET_SPEAKER_Handle *speaker,
1041 struct GNUNET_MICROPHONE_Handle *mic,
1042 GNUNET_CONVERSATION_EventHandler event_handler,
1043 void *event_handler_cls)
1044{
1045 struct GNUNET_CONVERSATION_Call *call;
1046 struct GNUNET_CRYPTO_EccPublicSignKey my_zone;
1047
1048 GNUNET_IDENTITY_ego_get_public_key (caller_id,
1049 &my_zone);
1050 call = GNUNET_new (struct GNUNET_CONVERSATION_Call);
1051 call->cfg = cfg;
1052 call->caller_id = caller_id;
1053 call->callee = GNUNET_strdup (callee);
1054 call->speaker = speaker;
1055 call->mic = mic;
1056 call->event_handler = event_handler;
1057 call->event_handler_cls = event_handler_cls;
1058 call->gns = GNUNET_GNS_connect (cfg);
1059 reconnect_call (call);
1060
1061 if ( (NULL == call->client) ||
1062 (NULL == call->gns) )
1063 {
1064 GNUNET_CONVERSATION_call_stop (call, NULL);
1065 return NULL;
1066 }
1067 call->gns_lookup = GNUNET_GNS_lookup (call->gns, callee,
1068 &my_zone,
1069 GNUNET_NAMESTORE_TYPE_PHONE,
1070 GNUNET_NO,
1071 NULL /* FIXME: add shortening support */,
1072 &handle_gns_response, call);
1073 GNUNET_assert (NULL != call->gns_lookup);
1074 return call;
1075}
1076
1077
1078/**
1079 * Terminate a call. The call may be ringing or ready at this time.
1080 *
1081 * @param call call to terminate
1082 * @param reason if the call was active (ringing or ready) this will be the
1083 * reason given to the other user for why we hung up
1084 */
1085void
1086GNUNET_CONVERSATION_call_stop (struct GNUNET_CONVERSATION_Call *call,
1087 const char *reason)
1088{
1089 if (NULL != reason)
1090 {
1091 // FIXME: transmit reason to service... (not implemented!)
1092 GNUNET_break (0);
1093 // return;
1094 }
1095 if (NULL != call->speaker)
1096 {
1097 if (CS_ACTIVE == call->state)
1098 call->speaker->disable_speaker (call->speaker->cls);
1099 call->speaker = NULL;
1100 }
1101 if (NULL != call->mic)
1102 {
1103 if (CS_ACTIVE == call->state)
1104 call->mic->disable_microphone (call->mic->cls);
1105 call->mic =NULL;
1106 }
1107 if (NULL != call->mq)
1108 {
1109 GNUNET_MQ_destroy (call->mq);
1110 call->mq = NULL;
1111 }
1112 if (NULL != call->client)
1113 {
1114 GNUNET_CLIENT_disconnect (call->client);
1115 call->client = NULL;
1116 }
1117 if (NULL != call->gns_lookup)
1118 {
1119 GNUNET_GNS_lookup_cancel (call->gns_lookup);
1120 call->gns_lookup = NULL;
1121 }
1122 if (NULL != call->gns)
1123 {
1124 GNUNET_GNS_disconnect (call->gns);
1125 call->gns = NULL;
1126 }
1127
1128 GNUNET_free (call);
1129}
1130
1131
1132/* end of conversation_api.c */
diff --git a/src/conversation/gnunet-conversation-new.c b/src/conversation/gnunet-conversation-new.c
deleted file mode 100644
index fdf6155fd..000000000
--- a/src/conversation/gnunet-conversation-new.c
+++ /dev/null
@@ -1,855 +0,0 @@
1/*
2 This file is part of GNUnet.
3 (C) 2013 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 * @file conversation/gnunet-conversation.c
22 * @brief conversation implementation
23 * @author Simon Dieterle
24 * @author Andreas Fuchs
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28#include "gnunet_constants.h"
29#include "gnunet_conversation_service.h"
30
31
32/**
33 * Maximum length allowed for the command line input.
34 */
35#define MAX_MESSAGE_LENGTH 1024
36
37
38/**
39 * Possible states of the program.
40 */
41enum ConversationState
42{
43 /**
44 * We're waiting for our own idenitty.
45 */
46 CS_LOOKUP_EGO,
47
48 /**
49 * We're listening for calls
50 */
51 CS_LISTEN,
52
53 /**
54 * Our phone is ringing.
55 */
56 CS_RING,
57
58 /**
59 * We accepted an incoming phone call.
60 */
61 CS_ACCEPTED,
62
63 /**
64 * We are looking up some other participant.
65 */
66 CS_RESOLVING,
67
68 /**
69 * We are now ringing the other participant.
70 */
71 CS_RINGING,
72
73 /**
74 * The other party accepted our call and we are now connected.
75 */
76 CS_CONNECTED,
77
78 /**
79 * Internal error
80 */
81 CS_ERROR
82
83};
84
85
86/**
87 * Phone handle
88 */
89static struct GNUNET_CONVERSATION_Phone *phone;
90
91/**
92 * Call handle
93 */
94static struct GNUNET_CONVERSATION_Call *call;
95
96/**
97 * Desired phone line.
98 */
99static unsigned int line;
100
101/**
102 * Task which handles the commands
103 */
104static GNUNET_SCHEDULER_TaskIdentifier handle_cmd_task;
105
106/**
107 * Our speaker.
108 */
109static struct GNUNET_SPEAKER_Handle *speaker;
110
111/**
112 * Our microphone.
113 */
114static struct GNUNET_MICROPHONE_Handle *mic;
115
116/**
117 * Our configuration.
118 */
119static struct GNUNET_CONFIGURATION_Handle *cfg;
120
121/**
122 * Our ego.
123 */
124static struct GNUNET_IDENTITY_Ego *caller_id;
125
126/**
127 * Handle to identity service.
128 */
129static struct GNUNET_IDENTITY_Handle *id;
130
131/**
132 * Name of our ego.
133 */
134static char *ego_name;
135
136/**
137 * Name of conversation partner (if any).
138 */
139static char *peer_name;
140
141/**
142 * File handle for stdin.
143 */
144static struct GNUNET_DISK_FileHandle *stdin_fh;
145
146/**
147 * Our current state.
148 */
149static enum ConversationState state;
150
151/**
152 * Be verbose.
153 */
154static int verbose;
155
156
157/**
158 * Function called with an event emitted by a phone.
159 *
160 * @param cls closure
161 * @param code type of the event on the phone
162 * @param ... additional information, depends on @a code
163 */
164static void
165phone_event_handler (void *cls,
166 enum GNUNET_CONVERSATION_EventCode code,
167 ...)
168{
169 va_list va;
170
171 va_start (va, code);
172 switch (code)
173 {
174 case GNUNET_CONVERSATION_EC_RING:
175 GNUNET_break (CS_LISTEN == state);
176 GNUNET_free_non_null (peer_name);
177 peer_name = GNUNET_strdup (va_arg (va, const char *));
178 FPRINTF (stdout,
179 _("Incoming call from `%s'.\nPlease /accept or /cancel the call.\n"),
180 peer_name);
181 state = CS_RING;
182 break;
183 case GNUNET_CONVERSATION_EC_RINGING:
184 GNUNET_break (0);
185 break;
186 case GNUNET_CONVERSATION_EC_READY:
187 GNUNET_break (0);
188 break;
189 case GNUNET_CONVERSATION_EC_GNS_FAIL:
190 GNUNET_break (0);
191 break;
192 case GNUNET_CONVERSATION_EC_BUSY:
193 GNUNET_break (0);
194 break;
195 case GNUNET_CONVERSATION_EC_TERMINATED:
196 GNUNET_break ( (CS_RING == state) ||
197 (CS_ACCEPTED == state) );
198 FPRINTF (stdout,
199 _("Call terminated: %s\n"),
200 va_arg (va, const char *));
201 state = CS_LISTEN;
202 break;
203 }
204 va_end (va);
205}
206
207
208/**
209 * Start our phone.
210 */
211static void
212start_phone ()
213{
214 if (NULL == caller_id)
215 {
216 FPRINTF (stderr,
217 _("Ego `%s' no longer available, phone is now down.\n"),
218 ego_name);
219 state = CS_LOOKUP_EGO;
220 return;
221 }
222 phone = GNUNET_CONVERSATION_phone_create (cfg,
223 caller_id,
224 &phone_event_handler, NULL);
225 /* FIXME: get record and print full GNS record info later here... */
226 if (NULL == phone)
227 {
228 FPRINTF (stderr,
229 "%s",
230 _("Failed to setup phone (internal error)\n"));
231 state = CS_ERROR;
232 }
233 else
234 {
235 if (verbose)
236 FPRINTF (stdout,
237 _("Phone active on line %u\n"),
238 (unsigned int) line);
239 state = CS_LISTEN;
240 }
241}
242
243
244/**
245 * Function called with an event emitted by a phone.
246 *
247 * @param cls closure
248 * @param code type of the event on the phone
249 * @param ... additional information, depends on @a code
250 */
251static void
252call_event_handler (void *cls,
253 enum GNUNET_CONVERSATION_EventCode code,
254 ...)
255{
256 va_list va;
257
258 va_start (va, code);
259 switch (code)
260 {
261 case GNUNET_CONVERSATION_EC_RING:
262 GNUNET_break (0);
263 break;
264 case GNUNET_CONVERSATION_EC_RINGING:
265 GNUNET_break (CS_RESOLVING == state);
266 if (verbose)
267 FPRINTF (stdout,
268 "%s",
269 _("Resolved address. Now ringing other party.\n"));
270 state = CS_RINGING;
271 break;
272 case GNUNET_CONVERSATION_EC_READY:
273 GNUNET_break (CS_RINGING == state);
274 FPRINTF (stdout,
275 _("Connection established: %s\n"),
276 va_arg (va, const char *));
277 state = CS_CONNECTED;
278 break;
279 case GNUNET_CONVERSATION_EC_GNS_FAIL:
280 GNUNET_break (CS_RESOLVING == state);
281 FPRINTF (stdout,
282 "%s",
283 _("Failed to resolve name\n"));
284 GNUNET_CONVERSATION_call_stop (call, NULL);
285 call = NULL;
286 start_phone ();
287 break;
288 case GNUNET_CONVERSATION_EC_BUSY:
289 GNUNET_break (CS_RINGING == state);
290 FPRINTF (stdout,
291 "%s",
292 _("Line busy\n"));
293 GNUNET_CONVERSATION_call_stop (call, NULL);
294 call = NULL;
295 start_phone ();
296 break;
297 case GNUNET_CONVERSATION_EC_TERMINATED:
298 GNUNET_break ( (CS_RINGING == state) ||
299 (CS_CONNECTED == state) );
300 FPRINTF (stdout,
301 _("Call terminated: %s\n"),
302 va_arg (va, const char *));
303 GNUNET_CONVERSATION_call_stop (call, NULL);
304 call = NULL;
305 start_phone ();
306 break;
307 }
308 va_end (va);
309}
310
311
312/**
313 * Function declareation for executing a action
314 *
315 * @param arguments arguments given to the function
316 */
317typedef void (*ActionFunction) (const char *arguments);
318
319
320/**
321 * Structure which defines a command
322 */
323struct VoipCommand
324{
325 /**
326 * Command the user needs to enter.
327 */
328 const char *command;
329
330 /**
331 * Function to call on command.
332 */
333 ActionFunction Action;
334
335 /**
336 * Help text for the command.
337 */
338 const char *helptext;
339};
340
341
342/**
343 * Action function to print help for the command shell.
344 *
345 * @param arguments arguments given to the command
346 */
347static void
348do_help (const char *args);
349
350
351/**
352 * Terminate the client
353 *
354 * @param args arguments given to the command
355 */
356static void
357do_quit (const char *args)
358{
359 GNUNET_SCHEDULER_shutdown ();
360}
361
362
363/**
364 * Handler for unknown command.
365 *
366 * @param args arguments given to the command
367 */
368static void
369do_unknown (const char *msg)
370{
371 FPRINTF (stderr,
372 _("Unknown command `%s'\n"),
373 msg);
374}
375
376
377/**
378 * Initiating a new call
379 *
380 * @param args arguments given to the command
381 */
382static void
383do_call (const char *arg)
384{
385 if (NULL == caller_id)
386 {
387 FPRINTF (stderr,
388 _("Ego `%s' not available\n"),
389 ego_name);
390 return;
391 }
392 switch (state)
393 {
394 case CS_LOOKUP_EGO:
395 FPRINTF (stderr,
396 _("Ego `%s' not available\n"),
397 ego_name);
398 return;
399 case CS_LISTEN:
400 /* ok to call! */
401 break;
402 case CS_RING:
403 FPRINTF (stdout,
404 _("Hanging up on incoming phone call from `%s' to call `%s'.\n"),
405 peer_name,
406 arg);
407 GNUNET_CONVERSATION_phone_hang_up (phone, NULL);
408 break;
409 case CS_ACCEPTED:
410 FPRINTF (stderr,
411 _("You are already in a conversation with `%s', refusing to call `%s'.\n"),
412 peer_name,
413 arg);
414 return;
415 case CS_RESOLVING:
416 case CS_RINGING:
417 FPRINTF (stderr,
418 _("Aborting call to `%s'\n"),
419 peer_name);
420 GNUNET_CONVERSATION_call_stop (call, NULL);
421 call = NULL;
422 break;
423 case CS_CONNECTED:
424 FPRINTF (stderr,
425 _("You are already in a conversation with `%s', refusing to call `%s'.\n"),
426 peer_name,
427 arg);
428 return;
429 case CS_ERROR:
430 /* ok to call */
431 break;
432 }
433 GNUNET_assert (NULL == call);
434 if (NULL != phone)
435 {
436 GNUNET_CONVERSATION_phone_destroy (phone);
437 phone = NULL;
438 }
439 GNUNET_free_non_null (peer_name);
440 peer_name = GNUNET_strdup (arg);
441 call = GNUNET_CONVERSATION_call_start (cfg,
442 caller_id,
443 arg,
444 speaker,
445 mic,
446 &call_event_handler, NULL);
447 state = CS_RESOLVING;
448}
449
450
451/**
452 * Accepting an incoming call
453 *
454 * @param args arguments given to the command
455 */
456static void
457do_accept (const char *args)
458{
459 switch (state)
460 {
461 case CS_LOOKUP_EGO:
462 case CS_LISTEN:
463 case CS_ERROR:
464 FPRINTF (stderr,
465 _("There is no incoming call to be accepted!\n"));
466 return;
467 case CS_RING:
468 /* this is the expected state */
469 break;
470 case CS_ACCEPTED:
471 FPRINTF (stderr,
472 _("You are already in a conversation with `%s'.\n"),
473 peer_name);
474 return;
475 case CS_RESOLVING:
476 case CS_RINGING:
477 FPRINTF (stderr,
478 _("You are trying to call `%s', cannot accept incoming calls right now.\n"),
479 peer_name);
480 return;
481 case CS_CONNECTED:
482 FPRINTF (stderr,
483 _("You are already in a conversation with `%s'.\n"),
484 peer_name);
485 return;
486 }
487 GNUNET_assert (NULL != phone);
488 GNUNET_CONVERSATION_phone_pick_up (phone,
489 args,
490 speaker,
491 mic);
492 state = CS_ACCEPTED;
493}
494
495
496/**
497 * Accepting an incoming call
498 *
499 * @param args arguments given to the command
500 */
501static void
502do_status (const char *args)
503{
504 switch (state)
505 {
506 case CS_LOOKUP_EGO:
507 FPRINTF (stdout,
508 _("We are currently trying to locate the private key for the ego `%s'.\n"),
509 ego_name);
510 break;
511 case CS_LISTEN:
512 FPRINTF (stdout,
513 _("We are listening for incoming calls for ego `%s' on line %u.\n"),
514 ego_name,
515 line);
516 break;
517 case CS_RING:
518 FPRINTF (stdout,
519 _("The phone is rining. `%s' is trying to call us.\n"),
520 peer_name);
521 break;
522 case CS_ACCEPTED:
523 case CS_CONNECTED:
524 FPRINTF (stdout,
525 _("You are having a conversation with `%s'.\n"),
526 peer_name);
527 break;
528 case CS_RESOLVING:
529 FPRINTF (stdout,
530 _("We are trying to find the network address to call `%s'.\n"),
531 peer_name);
532 break;
533 case CS_RINGING:
534 FPRINTF (stdout,
535 _("We are calling `%s', his phone should be ringing.\n"),
536 peer_name);
537 break;
538 case CS_ERROR:
539 FPRINTF (stdout,
540 _("We had an internal error setting up our phone line. You can still make calls.\n"));
541 break;
542 }
543}
544
545
546/**
547 * Rejecting a call
548 *
549 * @param args arguments given to the command
550 */
551static void
552do_reject (const char *args)
553{
554 switch (state)
555 {
556 case CS_LOOKUP_EGO:
557 case CS_LISTEN:
558 case CS_ERROR:
559 FPRINTF (stderr,
560 "%s",
561 _("There is no call that could be cancelled right now.\n"));
562 return;
563 case CS_RING:
564 case CS_ACCEPTED:
565 case CS_RESOLVING:
566 case CS_RINGING:
567 case CS_CONNECTED:
568 /* expected state, do rejection logic */
569 break;
570 }
571 if (NULL == call)
572 {
573 GNUNET_assert (NULL != phone);
574 GNUNET_CONVERSATION_phone_hang_up (phone,
575 args);
576 state = CS_LISTEN;
577 }
578 else
579 {
580 GNUNET_CONVERSATION_call_stop (call, args);
581 call = NULL;
582 start_phone ();
583 }
584}
585
586
587/**
588 * List of supported commands.
589 */
590static struct VoipCommand commands[] = {
591 {"/call", &do_call,
592 gettext_noop ("Use `/call USER.gnu'")},
593 {"/accept", &do_accept,
594 gettext_noop ("Use `/accept MESSAGE' to accept an incoming call")},
595 {"/cancel", &do_reject,
596 gettext_noop ("Use `/cancel MESSAGE' to reject or terminate a call")},
597 {"/status", &do_status,
598 gettext_noop ("Use `/status to print status information")},
599 {"/quit", &do_quit,
600 gettext_noop ("Use `/quit' to terminate gnunet-conversation")},
601 {"/help", &do_help,
602 gettext_noop ("Use `/help command' to get help for a specific command")},
603 {"", &do_unknown,
604 NULL},
605 {NULL, NULL, NULL},
606};
607
608
609/**
610 * Action function to print help for the command shell.
611 *
612 * @param arguments arguments given to the command
613 */
614static void
615do_help (const char *args)
616{
617 unsigned int i;
618
619 i = 0;
620 while ( (NULL != args) &&
621 (0 != strlen (args)) &&
622 (commands[i].Action != &do_help))
623 {
624 if (0 ==
625 strncasecmp (&args[1], &commands[i].command[1], strlen (args) - 1))
626 {
627 FPRINTF (stdout,
628 "%s\n",
629 gettext (commands[i].helptext));
630 return;
631 }
632 i++;
633 }
634 i = 0;
635 FPRINTF (stdout,
636 "%s",
637 "Available commands:\n");
638 while (commands[i].Action != &do_help)
639 {
640 FPRINTF (stdout,
641 "%s\n",
642 gettext (commands[i].command));
643 i++;
644 }
645 FPRINTF (stdout,
646 "%s",
647 "\n");
648 FPRINTF (stdout,
649 "%s\n",
650 gettext (commands[i].helptext));
651}
652
653
654/**
655 * Task run during shutdown.
656 *
657 * @param cls NULL
658 * @param tc unused
659 */
660static void
661do_stop_task (void *cls,
662 const struct GNUNET_SCHEDULER_TaskContext *tc)
663{
664 if (NULL != call)
665 {
666 GNUNET_CONVERSATION_call_stop (call, NULL);
667 call = NULL;
668 }
669 if (NULL != phone)
670 {
671 GNUNET_CONVERSATION_phone_destroy (phone);
672 phone = NULL;
673 }
674 if (GNUNET_SCHEDULER_NO_TASK != handle_cmd_task)
675 {
676 GNUNET_SCHEDULER_cancel (handle_cmd_task);
677 handle_cmd_task = GNUNET_SCHEDULER_NO_TASK;
678 }
679 if (NULL != id)
680 {
681 GNUNET_IDENTITY_disconnect (id);
682 id = NULL;
683 }
684 GNUNET_SPEAKER_destroy (speaker);
685 speaker = NULL;
686 GNUNET_MICROPHONE_destroy (mic);
687 mic = NULL;
688 GNUNET_free (ego_name);
689 ego_name = NULL;
690 GNUNET_CONFIGURATION_destroy (cfg);
691 cfg = NULL;
692 GNUNET_free_non_null (peer_name);
693 state = CS_ERROR;
694}
695
696
697/**
698 * Task to handle commands from the terminal.
699 *
700 * @param cls NULL
701 * @param tc scheduler context
702 */
703static void
704handle_command (void *cls,
705 const struct GNUNET_SCHEDULER_TaskContext *tc)
706{
707 char message[MAX_MESSAGE_LENGTH + 1];
708 const char *ptr;
709 size_t i;
710
711 handle_cmd_task =
712 GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
713 stdin_fh,
714 &handle_command, NULL);
715 /* read message from command line and handle it */
716 memset (message, 0, MAX_MESSAGE_LENGTH + 1);
717 if (NULL == fgets (message, MAX_MESSAGE_LENGTH, stdin))
718 return;
719 if (0 == strlen (message))
720 return;
721 if (message[strlen (message) - 1] == '\n')
722 message[strlen (message) - 1] = '\0';
723 if (0 == strlen (message))
724 return;
725 i = 0;
726 while ((NULL != commands[i].command) &&
727 (0 != strncasecmp (commands[i].command, message,
728 strlen (commands[i].command))))
729 i++;
730 ptr = &message[strlen (commands[i].command)];
731 while (isspace ((int) *ptr))
732 ptr++;
733 commands[i].Action (ptr);
734}
735
736
737/**
738 * Function called by identity service with information about egos.
739 *
740 * @param cls NULL
741 * @param ego ego handle
742 * @param ctx unused
743 * @param name name of the ego
744 */
745static void
746identity_cb (void *cls,
747 struct GNUNET_IDENTITY_Ego *ego,
748 void **ctx,
749 const char *name)
750{
751 if (NULL == name)
752 return;
753 if (ego == caller_id)
754 {
755 if (verbose)
756 FPRINTF (stdout,
757 _("Name of our ego changed to `%s'\n"),
758 name);
759 GNUNET_free (ego_name);
760 ego_name = GNUNET_strdup (name);
761 return;
762 }
763 if (0 != strcmp (name,
764 ego_name))
765 return;
766 if (NULL == ego)
767 {
768 if (verbose)
769 FPRINTF (stdout,
770 _("Our ego `%s' was deleted!\n"),
771 ego_name);
772 caller_id = NULL;
773 return;
774 }
775 caller_id = ego;
776 GNUNET_CONFIGURATION_set_value_number (cfg,
777 "CONVERSATION",
778 "LINE",
779 line);
780 start_phone ();
781}
782
783
784/**
785 * Main function that will be run by the scheduler.
786 *
787 * @param cls closure
788 * @param args remaining command-line arguments
789 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
790 * @param c configuration
791 */
792static void
793run (void *cls,
794 char *const *args,
795 const char *cfgfile,
796 const struct GNUNET_CONFIGURATION_Handle *c)
797{
798 cfg = GNUNET_CONFIGURATION_dup (c);
799 speaker = GNUNET_SPEAKER_create_from_hardware (cfg);
800 mic = GNUNET_MICROPHONE_create_from_hardware (cfg);
801 if (NULL == ego_name)
802 {
803 FPRINTF (stderr,
804 "%s",
805 _("You must specify the NAME of an ego to use\n"));
806 return;
807 }
808 id = GNUNET_IDENTITY_connect (cfg,
809 &identity_cb,
810 NULL);
811 handle_cmd_task =
812 GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_UI,
813 &handle_command, NULL);
814 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &do_stop_task,
815 NULL);
816}
817
818
819/**
820 * The main function to conversation.
821 *
822 * @param argc number of arguments from the command line
823 * @param argv command line arguments
824 * @return 0 ok, 1 on error
825 */
826int
827main (int argc, char *const *argv)
828{
829 static const struct GNUNET_GETOPT_CommandLineOption options[] = {
830 {'e', "ego", "NAME",
831 gettext_noop ("sets the NAME of the ego to use for the phone (and name resolution)"),
832 1, &GNUNET_GETOPT_set_string, &ego_name},
833 {'p', "phone", "LINE",
834 gettext_noop ("sets the LINE to use for the phone"),
835 1, &GNUNET_GETOPT_set_uint, &line},
836 GNUNET_GETOPT_OPTION_END
837 };
838 int flags;
839 int ret;
840
841 flags = fcntl (0, F_GETFL, 0);
842 flags |= O_NONBLOCK;
843 fcntl (0, F_SETFL, flags);
844 stdin_fh = GNUNET_DISK_get_handle_from_int_fd (0);
845 if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
846 return 2;
847 ret = GNUNET_PROGRAM_run (argc, argv,
848 "gnunet-conversation",
849 gettext_noop ("Enables having a conversation with other GNUnet users."),
850 options, &run, NULL);
851 GNUNET_free ((void *) argv);
852 return (GNUNET_OK == ret) ? 0 : 1;
853}
854
855/* end of gnunet-conversation.c */
diff --git a/src/conversation/gnunet-conversation.c b/src/conversation/gnunet-conversation.c
index a4088984b..fdf6155fd 100644
--- a/src/conversation/gnunet-conversation.c
+++ b/src/conversation/gnunet-conversation.c
@@ -14,7 +14,7 @@
14 14
15 You should have received a copy of the GNU General Public License 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 16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, InGNUNET_SERVERc., 59 Temple Place - Suite 330, 17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA. 18 Boston, MA 02111-1307, USA.
19*/ 19*/
20/** 20/**
@@ -27,14 +27,76 @@
27#include "gnunet_util_lib.h" 27#include "gnunet_util_lib.h"
28#include "gnunet_constants.h" 28#include "gnunet_constants.h"
29#include "gnunet_conversation_service.h" 29#include "gnunet_conversation_service.h"
30#include <fcntl.h>
31 30
32#define MAX_MESSAGE_LENGTH (32 * 1024)
33 31
34/** 32/**
35 * CONVERSATION handle 33 * Maximum length allowed for the command line input.
36 */ 34 */
37static struct GNUNET_CONVERSATION_Handle *conversation; 35#define MAX_MESSAGE_LENGTH 1024
36
37
38/**
39 * Possible states of the program.
40 */
41enum ConversationState
42{
43 /**
44 * We're waiting for our own idenitty.
45 */
46 CS_LOOKUP_EGO,
47
48 /**
49 * We're listening for calls
50 */
51 CS_LISTEN,
52
53 /**
54 * Our phone is ringing.
55 */
56 CS_RING,
57
58 /**
59 * We accepted an incoming phone call.
60 */
61 CS_ACCEPTED,
62
63 /**
64 * We are looking up some other participant.
65 */
66 CS_RESOLVING,
67
68 /**
69 * We are now ringing the other participant.
70 */
71 CS_RINGING,
72
73 /**
74 * The other party accepted our call and we are now connected.
75 */
76 CS_CONNECTED,
77
78 /**
79 * Internal error
80 */
81 CS_ERROR
82
83};
84
85
86/**
87 * Phone handle
88 */
89static struct GNUNET_CONVERSATION_Phone *phone;
90
91/**
92 * Call handle
93 */
94static struct GNUNET_CONVERSATION_Call *call;
95
96/**
97 * Desired phone line.
98 */
99static unsigned int line;
38 100
39/** 101/**
40 * Task which handles the commands 102 * Task which handles the commands
@@ -42,231 +104,483 @@ static struct GNUNET_CONVERSATION_Handle *conversation;
42static GNUNET_SCHEDULER_TaskIdentifier handle_cmd_task; 104static GNUNET_SCHEDULER_TaskIdentifier handle_cmd_task;
43 105
44/** 106/**
45 * Function declareation for executing a action 107 * Our speaker.
46 */ 108 */
47typedef int (*ActionFunction) (const char *argumetns, 109static struct GNUNET_SPEAKER_Handle *speaker;
48 const void *xtra);
49 110
50/** 111/**
51* Structure which defines a command 112 * Our microphone.
52*/ 113 */
53struct VoipCommand 114static struct GNUNET_MICROPHONE_Handle *mic;
54{ 115
55 const char *command; 116/**
56 ActionFunction Action; 117 * Our configuration.
57 const char *helptext; 118 */
58}; 119static struct GNUNET_CONFIGURATION_Handle *cfg;
59 120
121/**
122 * Our ego.
123 */
124static struct GNUNET_IDENTITY_Ego *caller_id;
125
126/**
127 * Handle to identity service.
128 */
129static struct GNUNET_IDENTITY_Handle *id;
60 130
61static int 131/**
62do_help (const char *args, 132 * Name of our ego.
63 const void *xtra); 133 */
134static char *ego_name;
64 135
136/**
137 * Name of conversation partner (if any).
138 */
139static char *peer_name;
140
141/**
142 * File handle for stdin.
143 */
144static struct GNUNET_DISK_FileHandle *stdin_fh;
145
146/**
147 * Our current state.
148 */
149static enum ConversationState state;
65 150
66/** 151/**
67 * Method called whenever a call is incoming 152 * Be verbose.
153 */
154static int verbose;
155
156
157/**
158 * Function called with an event emitted by a phone.
68 * 159 *
69 * @param cls closure 160 * @param cls closure
70 * @param handle to the conversation session 161 * @param code type of the event on the phone
71 * @param caller peer that calls you 162 * @param ... additional information, depends on @a code
72 */ 163 */
73static void 164static void
74call_handler (void *cls, 165phone_event_handler (void *cls,
75 struct GNUNET_CONVERSATION_Handle *handle, 166 enum GNUNET_CONVERSATION_EventCode code,
76 const struct GNUNET_PeerIdentity *caller) 167 ...)
77{ 168{
78 FPRINTF (stdout, 169 va_list va;
79 _("Incoming call from peer: %s\n"), 170
80 GNUNET_i2s_full (caller)); 171 va_start (va, code);
172 switch (code)
173 {
174 case GNUNET_CONVERSATION_EC_RING:
175 GNUNET_break (CS_LISTEN == state);
176 GNUNET_free_non_null (peer_name);
177 peer_name = GNUNET_strdup (va_arg (va, const char *));
178 FPRINTF (stdout,
179 _("Incoming call from `%s'.\nPlease /accept or /cancel the call.\n"),
180 peer_name);
181 state = CS_RING;
182 break;
183 case GNUNET_CONVERSATION_EC_RINGING:
184 GNUNET_break (0);
185 break;
186 case GNUNET_CONVERSATION_EC_READY:
187 GNUNET_break (0);
188 break;
189 case GNUNET_CONVERSATION_EC_GNS_FAIL:
190 GNUNET_break (0);
191 break;
192 case GNUNET_CONVERSATION_EC_BUSY:
193 GNUNET_break (0);
194 break;
195 case GNUNET_CONVERSATION_EC_TERMINATED:
196 GNUNET_break ( (CS_RING == state) ||
197 (CS_ACCEPTED == state) );
198 FPRINTF (stdout,
199 _("Call terminated: %s\n"),
200 va_arg (va, const char *));
201 state = CS_LISTEN;
202 break;
203 }
204 va_end (va);
81} 205}
82 206
83 207
84/** 208/**
85 * Method called whenever a call is rejected 209 * Start our phone.
86 *
87 * @param cls closure
88 * @param handle to the conversation session
89 * @param reason given reason why the call was rejected
90 * @param peer peer that rejected your call
91 */ 210 */
92static void 211static void
93reject_handler (void *cls, 212start_phone ()
94 struct GNUNET_CONVERSATION_Handle *handle,
95 enum GNUNET_CONVERSATION_RejectReason reason,
96 const struct GNUNET_PeerIdentity *peer)
97{ 213{
98 FPRINTF (stdout, 214 if (NULL == caller_id)
99 _("Peer %s rejected your call. Reason: %d\n"), 215 {
100 GNUNET_i2s_full (peer), reason); 216 FPRINTF (stderr,
217 _("Ego `%s' no longer available, phone is now down.\n"),
218 ego_name);
219 state = CS_LOOKUP_EGO;
220 return;
221 }
222 phone = GNUNET_CONVERSATION_phone_create (cfg,
223 caller_id,
224 &phone_event_handler, NULL);
225 /* FIXME: get record and print full GNS record info later here... */
226 if (NULL == phone)
227 {
228 FPRINTF (stderr,
229 "%s",
230 _("Failed to setup phone (internal error)\n"));
231 state = CS_ERROR;
232 }
233 else
234 {
235 if (verbose)
236 FPRINTF (stdout,
237 _("Phone active on line %u\n"),
238 (unsigned int) line);
239 state = CS_LISTEN;
240 }
101} 241}
102 242
103 243
104/** 244/**
105 * Method called whenever a notification is there 245 * Function called with an event emitted by a phone.
106 * 246 *
107 * @param cls closure 247 * @param cls closure
108 * @param handle to the conversation session 248 * @param code type of the event on the phone
109 * @param type the type of the notification 249 * @param ... additional information, depends on @a code
110 * @param peer peer that the notification is about
111 */ 250 */
112static void 251static void
113notification_handler (void *cls, 252call_event_handler (void *cls,
114 struct GNUNET_CONVERSATION_Handle *handle, 253 enum GNUNET_CONVERSATION_EventCode code,
115 enum GNUNET_CONVERSATION_NotificationType type, 254 ...)
116 const struct GNUNET_PeerIdentity *peer)
117{ 255{
118 switch (type) 256 va_list va;
257
258 va_start (va, code);
259 switch (code)
119 { 260 {
120 case GNUNET_CONVERSATION_NT_SERVICE_BLOCKED: 261 case GNUNET_CONVERSATION_EC_RING:
262 GNUNET_break (0);
263 break;
264 case GNUNET_CONVERSATION_EC_RINGING:
265 GNUNET_break (CS_RESOLVING == state);
266 if (verbose)
267 FPRINTF (stdout,
268 "%s",
269 _("Resolved address. Now ringing other party.\n"));
270 state = CS_RINGING;
271 break;
272 case GNUNET_CONVERSATION_EC_READY:
273 GNUNET_break (CS_RINGING == state);
121 FPRINTF (stdout, 274 FPRINTF (stdout,
122 _("The service is already in use. Try again later.")); 275 _("Connection established: %s\n"),
123 break; 276 va_arg (va, const char *));
124 case GNUNET_CONVERSATION_NT_NO_PEER: 277 state = CS_CONNECTED;
125 FPRINTF (stdout, 278 break;
126 _("The Peer you were calling is no correct peer.\n")); 279 case GNUNET_CONVERSATION_EC_GNS_FAIL:
127 break; 280 GNUNET_break (CS_RESOLVING == state);
128 case GNUNET_CONVERSATION_NT_NO_ANSWER:
129 FPRINTF (stdout,
130 _("Peer %s did not answer your call.\n"),
131 GNUNET_i2s_full (peer));
132 break;
133 case GNUNET_CONVERSATION_NT_AVAILABLE_AGAIN:
134 FPRINTF (stdout, 281 FPRINTF (stdout,
135 _("Peer %s is now available.\n"), 282 "%s",
136 GNUNET_i2s_full (peer)); 283 _("Failed to resolve name\n"));
137 break; 284 GNUNET_CONVERSATION_call_stop (call, NULL);
138 case GNUNET_CONVERSATION_NT_CALL_ACCEPTED: 285 call = NULL;
139 FPRINTF (stdout, 286 start_phone ();
140 _("Peer %s has accepted your call.\n"), 287 break;
141 GNUNET_i2s_full (peer)); 288 case GNUNET_CONVERSATION_EC_BUSY:
142 break; 289 GNUNET_break (CS_RINGING == state);
143 case GNUNET_CONVERSATION_NT_CALL_TERMINATED:
144 FPRINTF (stdout, 290 FPRINTF (stdout,
145 _("Peer %s has terminated the call.\n"), 291 "%s",
146 GNUNET_i2s_full (peer)); 292 _("Line busy\n"));
293 GNUNET_CONVERSATION_call_stop (call, NULL);
294 call = NULL;
295 start_phone ();
147 break; 296 break;
148 default: 297 case GNUNET_CONVERSATION_EC_TERMINATED:
149 GNUNET_break (0); 298 GNUNET_break ( (CS_RINGING == state) ||
150 } 299 (CS_CONNECTED == state) );
300 FPRINTF (stdout,
301 _("Call terminated: %s\n"),
302 va_arg (va, const char *));
303 GNUNET_CONVERSATION_call_stop (call, NULL);
304 call = NULL;
305 start_phone ();
306 break;
307 }
308 va_end (va);
151} 309}
152 310
153 311
154/** 312/**
155 * Method called whenever a notification for missed calls is there 313 * Function declareation for executing a action
156 * 314 *
157 * @param cls closure 315 * @param arguments arguments given to the function
158 * @param handle to the conversation session
159 * @param missed_calls a list of missed calls
160 */ 316 */
161static void 317typedef void (*ActionFunction) (const char *arguments);
162missed_call_handler (void *cls,
163 struct GNUNET_CONVERSATION_Handle *handle,
164 struct GNUNET_CONVERSATION_MissedCallNotification *missed_calls)
165{
166 FPRINTF (stdout,
167 _("You have missed calls.\n"));
168}
169 318
170 319
171/** 320/**
172 * Terminating the client 321 * Structure which defines a command
173 */ 322 */
174static int 323struct VoipCommand
175do_quit (const char *args,
176 const void *xtra)
177{ 324{
178 return GNUNET_SYSERR; 325 /**
179} 326 * Command the user needs to enter.
327 */
328 const char *command;
329
330 /**
331 * Function to call on command.
332 */
333 ActionFunction Action;
334
335 /**
336 * Help text for the command.
337 */
338 const char *helptext;
339};
340
341
342/**
343 * Action function to print help for the command shell.
344 *
345 * @param arguments arguments given to the command
346 */
347static void
348do_help (const char *args);
180 349
181 350
182/** 351/**
352 * Terminate the client
183 * 353 *
354 * @param args arguments given to the command
184 */ 355 */
185static int 356static void
186do_unknown (const char *msg, 357do_quit (const char *args)
187 const void *xtra)
188{ 358{
189 FPRINTF (stderr, 359 GNUNET_SCHEDULER_shutdown ();
190 _("Unknown command `%s'\n"),
191 msg);
192 return GNUNET_OK;
193} 360}
194 361
195 362
196/** 363/**
197 * Initiating a new call 364 * Handler for unknown command.
365 *
366 * @param args arguments given to the command
198 */ 367 */
199static int 368static void
200do_call (const char *arg, 369do_unknown (const char *msg)
201 const void *xtra)
202{ 370{
203 FPRINTF (stdout, 371 FPRINTF (stderr,
204 _("Initiating call to: %s\n"), 372 _("Unknown command `%s'\n"),
205 arg); 373 msg);
206 GNUNET_CONVERSATION_call (conversation,
207 arg,
208 GNUNET_YES);
209 return GNUNET_OK;
210} 374}
211 375
212 376
213/** 377/**
214 * Initiating a new call 378 * Initiating a new call
379 *
380 * @param args arguments given to the command
215 */ 381 */
216static int 382static void
217do_call_peer (const char *arg, 383do_call (const char *arg)
218 const void *xtra)
219{ 384{
220 FPRINTF (stdout, 385 if (NULL == caller_id)
221 _("Initiating call to: %s\n"), 386 {
222 arg); 387 FPRINTF (stderr,
223 GNUNET_CONVERSATION_call (conversation, 388 _("Ego `%s' not available\n"),
224 arg, 389 ego_name);
225 GNUNET_NO); 390 return;
226 return GNUNET_OK; 391 }
392 switch (state)
393 {
394 case CS_LOOKUP_EGO:
395 FPRINTF (stderr,
396 _("Ego `%s' not available\n"),
397 ego_name);
398 return;
399 case CS_LISTEN:
400 /* ok to call! */
401 break;
402 case CS_RING:
403 FPRINTF (stdout,
404 _("Hanging up on incoming phone call from `%s' to call `%s'.\n"),
405 peer_name,
406 arg);
407 GNUNET_CONVERSATION_phone_hang_up (phone, NULL);
408 break;
409 case CS_ACCEPTED:
410 FPRINTF (stderr,
411 _("You are already in a conversation with `%s', refusing to call `%s'.\n"),
412 peer_name,
413 arg);
414 return;
415 case CS_RESOLVING:
416 case CS_RINGING:
417 FPRINTF (stderr,
418 _("Aborting call to `%s'\n"),
419 peer_name);
420 GNUNET_CONVERSATION_call_stop (call, NULL);
421 call = NULL;
422 break;
423 case CS_CONNECTED:
424 FPRINTF (stderr,
425 _("You are already in a conversation with `%s', refusing to call `%s'.\n"),
426 peer_name,
427 arg);
428 return;
429 case CS_ERROR:
430 /* ok to call */
431 break;
432 }
433 GNUNET_assert (NULL == call);
434 if (NULL != phone)
435 {
436 GNUNET_CONVERSATION_phone_destroy (phone);
437 phone = NULL;
438 }
439 GNUNET_free_non_null (peer_name);
440 peer_name = GNUNET_strdup (arg);
441 call = GNUNET_CONVERSATION_call_start (cfg,
442 caller_id,
443 arg,
444 speaker,
445 mic,
446 &call_event_handler, NULL);
447 state = CS_RESOLVING;
227} 448}
228 449
229 450
230/** 451/**
231 * Accepting an incoming call 452 * Accepting an incoming call
453 *
454 * @param args arguments given to the command
232 */ 455 */
233static int 456static void
234do_accept (const char *args, 457do_accept (const char *args)
235 const void *xtra)
236{ 458{
237 FPRINTF (stdout, 459 switch (state)
238 _("Accepting the call\n")); 460 {
239 GNUNET_CONVERSATION_accept (conversation); 461 case CS_LOOKUP_EGO:
240 462 case CS_LISTEN:
241 return GNUNET_OK; 463 case CS_ERROR:
464 FPRINTF (stderr,
465 _("There is no incoming call to be accepted!\n"));
466 return;
467 case CS_RING:
468 /* this is the expected state */
469 break;
470 case CS_ACCEPTED:
471 FPRINTF (stderr,
472 _("You are already in a conversation with `%s'.\n"),
473 peer_name);
474 return;
475 case CS_RESOLVING:
476 case CS_RINGING:
477 FPRINTF (stderr,
478 _("You are trying to call `%s', cannot accept incoming calls right now.\n"),
479 peer_name);
480 return;
481 case CS_CONNECTED:
482 FPRINTF (stderr,
483 _("You are already in a conversation with `%s'.\n"),
484 peer_name);
485 return;
486 }
487 GNUNET_assert (NULL != phone);
488 GNUNET_CONVERSATION_phone_pick_up (phone,
489 args,
490 speaker,
491 mic);
492 state = CS_ACCEPTED;
242} 493}
243 494
244 495
245/** 496/**
246 * Rejecting a call 497 * Accepting an incoming call
498 *
499 * @param args arguments given to the command
247 */ 500 */
248static int 501static void
249do_reject (const char *args, 502do_status (const char *args)
250 const void *xtra)
251{ 503{
252 FPRINTF (stdout, 504 switch (state)
253 _("Rejecting the call\n")); 505 {
254 GNUNET_CONVERSATION_reject (conversation); 506 case CS_LOOKUP_EGO:
255 return GNUNET_OK; 507 FPRINTF (stdout,
508 _("We are currently trying to locate the private key for the ego `%s'.\n"),
509 ego_name);
510 break;
511 case CS_LISTEN:
512 FPRINTF (stdout,
513 _("We are listening for incoming calls for ego `%s' on line %u.\n"),
514 ego_name,
515 line);
516 break;
517 case CS_RING:
518 FPRINTF (stdout,
519 _("The phone is rining. `%s' is trying to call us.\n"),
520 peer_name);
521 break;
522 case CS_ACCEPTED:
523 case CS_CONNECTED:
524 FPRINTF (stdout,
525 _("You are having a conversation with `%s'.\n"),
526 peer_name);
527 break;
528 case CS_RESOLVING:
529 FPRINTF (stdout,
530 _("We are trying to find the network address to call `%s'.\n"),
531 peer_name);
532 break;
533 case CS_RINGING:
534 FPRINTF (stdout,
535 _("We are calling `%s', his phone should be ringing.\n"),
536 peer_name);
537 break;
538 case CS_ERROR:
539 FPRINTF (stdout,
540 _("We had an internal error setting up our phone line. You can still make calls.\n"));
541 break;
542 }
256} 543}
257 544
258 545
259/** 546/**
260 * Terminating a call 547 * Rejecting a call
548 *
549 * @param args arguments given to the command
261 */ 550 */
262static int 551static void
263do_hang_up (const char *args, 552do_reject (const char *args)
264 const void *xtra)
265{ 553{
266 FPRINTF (stdout, 554 switch (state)
267 _("Terminating the call\n")); 555 {
268 GNUNET_CONVERSATION_hangup (conversation); 556 case CS_LOOKUP_EGO:
269 return GNUNET_OK; 557 case CS_LISTEN:
558 case CS_ERROR:
559 FPRINTF (stderr,
560 "%s",
561 _("There is no call that could be cancelled right now.\n"));
562 return;
563 case CS_RING:
564 case CS_ACCEPTED:
565 case CS_RESOLVING:
566 case CS_RINGING:
567 case CS_CONNECTED:
568 /* expected state, do rejection logic */
569 break;
570 }
571 if (NULL == call)
572 {
573 GNUNET_assert (NULL != phone);
574 GNUNET_CONVERSATION_phone_hang_up (phone,
575 args);
576 state = CS_LISTEN;
577 }
578 else
579 {
580 GNUNET_CONVERSATION_call_stop (call, args);
581 call = NULL;
582 start_phone ();
583 }
270} 584}
271 585
272 586
@@ -274,36 +588,38 @@ do_hang_up (const char *args,
274 * List of supported commands. 588 * List of supported commands.
275 */ 589 */
276static struct VoipCommand commands[] = { 590static struct VoipCommand commands[] = {
277 {"/call ", &do_call, gettext_noop ("Use `/call gns_name'")}, 591 {"/call", &do_call,
278 {"/callpeer ", &do_call_peer, 592 gettext_noop ("Use `/call USER.gnu'")},
279 gettext_noop ("Use `/call private_key' to call a person")},
280 {"/accept", &do_accept, 593 {"/accept", &do_accept,
281 gettext_noop ("Use `/accept' to accept an incoming call")}, 594 gettext_noop ("Use `/accept MESSAGE' to accept an incoming call")},
282 {"/terminate", &do_hang_up, 595 {"/cancel", &do_reject,
283 gettext_noop ("Use `/terminate' to end a call")}, 596 gettext_noop ("Use `/cancel MESSAGE' to reject or terminate a call")},
284 {"/reject", &do_reject, 597 {"/status", &do_status,
285 gettext_noop ("Use `/rejet' to reject an incoming call")}, 598 gettext_noop ("Use `/status to print status information")},
286 {"/quit", &do_quit, gettext_noop ("Use `/quit' to terminate gnunet-conversation")}, 599 {"/quit", &do_quit,
600 gettext_noop ("Use `/quit' to terminate gnunet-conversation")},
287 {"/help", &do_help, 601 {"/help", &do_help,
288 gettext_noop ("Use `/help command' to get help for a specific command")}, 602 gettext_noop ("Use `/help command' to get help for a specific command")},
289 {"/", &do_unknown, NULL}, 603 {"", &do_unknown,
290 {"", &do_unknown, NULL}, 604 NULL},
291 {NULL, NULL, NULL}, 605 {NULL, NULL, NULL},
292}; 606};
293 607
294 608
295/** 609/**
610 * Action function to print help for the command shell.
296 * 611 *
612 * @param arguments arguments given to the command
297 */ 613 */
298static int 614static void
299do_help (const char *args, 615do_help (const char *args)
300 const void *xtra)
301{ 616{
302 int i; 617 unsigned int i;
303 618
304 i = 0; 619 i = 0;
305 while ((NULL != args) && (0 != strlen (args)) && 620 while ( (NULL != args) &&
306 (commands[i].Action != &do_help)) 621 (0 != strlen (args)) &&
622 (commands[i].Action != &do_help))
307 { 623 {
308 if (0 == 624 if (0 ==
309 strncasecmp (&args[1], &commands[i].command[1], strlen (args) - 1)) 625 strncasecmp (&args[1], &commands[i].command[1], strlen (args) - 1))
@@ -311,18 +627,18 @@ do_help (const char *args,
311 FPRINTF (stdout, 627 FPRINTF (stdout,
312 "%s\n", 628 "%s\n",
313 gettext (commands[i].helptext)); 629 gettext (commands[i].helptext));
314 return GNUNET_OK; 630 return;
315 } 631 }
316 i++; 632 i++;
317 } 633 }
318 i = 0; 634 i = 0;
319 FPRINTF (stdout, 635 FPRINTF (stdout,
320 "%s", 636 "%s",
321 "Available commands:"); 637 "Available commands:\n");
322 while (commands[i].Action != &do_help) 638 while (commands[i].Action != &do_help)
323 { 639 {
324 FPRINTF (stdout, 640 FPRINTF (stdout,
325 " %s", 641 "%s\n",
326 gettext (commands[i].command)); 642 gettext (commands[i].command));
327 i++; 643 i++;
328 } 644 }
@@ -332,73 +648,136 @@ do_help (const char *args,
332 FPRINTF (stdout, 648 FPRINTF (stdout,
333 "%s\n", 649 "%s\n",
334 gettext (commands[i].helptext)); 650 gettext (commands[i].helptext));
335 return GNUNET_OK;
336} 651}
337 652
338 653
339/** 654/**
655 * Task run during shutdown.
340 * 656 *
657 * @param cls NULL
658 * @param tc unused
341 */ 659 */
342static void 660static void
343do_stop_task (void *cls, 661do_stop_task (void *cls,
344 const struct GNUNET_SCHEDULER_TaskContext *tc) 662 const struct GNUNET_SCHEDULER_TaskContext *tc)
345{ 663{
346 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 664 if (NULL != call)
347 "Running shutdown task\n"); 665 {
348 GNUNET_CONVERSATION_disconnect (conversation); 666 GNUNET_CONVERSATION_call_stop (call, NULL);
349 667 call = NULL;
350 if (handle_cmd_task != GNUNET_SCHEDULER_NO_TASK) 668 }
669 if (NULL != phone)
670 {
671 GNUNET_CONVERSATION_phone_destroy (phone);
672 phone = NULL;
673 }
674 if (GNUNET_SCHEDULER_NO_TASK != handle_cmd_task)
351 { 675 {
352 GNUNET_SCHEDULER_cancel (handle_cmd_task); 676 GNUNET_SCHEDULER_cancel (handle_cmd_task);
353 handle_cmd_task = GNUNET_SCHEDULER_NO_TASK; 677 handle_cmd_task = GNUNET_SCHEDULER_NO_TASK;
354 } 678 }
355 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 679 if (NULL != id)
356 "Running shutdown task finished\n"); 680 {
681 GNUNET_IDENTITY_disconnect (id);
682 id = NULL;
683 }
684 GNUNET_SPEAKER_destroy (speaker);
685 speaker = NULL;
686 GNUNET_MICROPHONE_destroy (mic);
687 mic = NULL;
688 GNUNET_free (ego_name);
689 ego_name = NULL;
690 GNUNET_CONFIGURATION_destroy (cfg);
691 cfg = NULL;
692 GNUNET_free_non_null (peer_name);
693 state = CS_ERROR;
357} 694}
358 695
359 696
360/** 697/**
698 * Task to handle commands from the terminal.
361 * 699 *
700 * @param cls NULL
701 * @param tc scheduler context
362 */ 702 */
363static void 703static void
364handle_command (void *cls, 704handle_command (void *cls,
365 const struct GNUNET_SCHEDULER_TaskContext *tc) 705 const struct GNUNET_SCHEDULER_TaskContext *tc)
366{ 706{
367 char message[MAX_MESSAGE_LENGTH + 1]; 707 char message[MAX_MESSAGE_LENGTH + 1];
368 int i; 708 const char *ptr;
709 size_t i;
369 710
711 handle_cmd_task =
712 GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
713 stdin_fh,
714 &handle_command, NULL);
370 /* read message from command line and handle it */ 715 /* read message from command line and handle it */
371 memset (message, 0, MAX_MESSAGE_LENGTH + 1); 716 memset (message, 0, MAX_MESSAGE_LENGTH + 1);
372 if (NULL == fgets (message, MAX_MESSAGE_LENGTH, stdin)) 717 if (NULL == fgets (message, MAX_MESSAGE_LENGTH, stdin))
373 goto next; 718 return;
374 if (strlen (message) == 0) 719 if (0 == strlen (message))
375 goto next; 720 return;
376 if (message[strlen (message) - 1] == '\n') 721 if (message[strlen (message) - 1] == '\n')
377 message[strlen (message) - 1] = '\0'; 722 message[strlen (message) - 1] = '\0';
378 if (strlen (message) == 0) 723 if (0 == strlen (message))
379 goto next; 724 return;
380 i = 0; 725 i = 0;
381 while ((NULL != commands[i].command) && 726 while ((NULL != commands[i].command) &&
382 (0 != 727 (0 != strncasecmp (commands[i].command, message,
383 strncasecmp (commands[i].command, message, 728 strlen (commands[i].command))))
384 strlen (commands[i].command))))
385 i++; 729 i++;
386 if (GNUNET_OK != 730 ptr = &message[strlen (commands[i].command)];
387 commands[i].Action (&message[strlen (commands[i].command)], NULL)) 731 while (isspace ((int) *ptr))
388 goto out; 732 ptr++;
733 commands[i].Action (ptr);
734}
389 735
390next: 736
391 handle_cmd_task = 737/**
392 GNUNET_SCHEDULER_add_delayed_with_priority (GNUNET_TIME_relative_multiply 738 * Function called by identity service with information about egos.
393 (GNUNET_TIME_UNIT_MILLISECONDS, 739 *
394 100), 740 * @param cls NULL
395 GNUNET_SCHEDULER_PRIORITY_UI, 741 * @param ego ego handle
396 &handle_command, NULL); 742 * @param ctx unused
397 return; 743 * @param name name of the ego
398 744 */
399out: 745static void
400 handle_cmd_task = GNUNET_SCHEDULER_NO_TASK; 746identity_cb (void *cls,
401 GNUNET_SCHEDULER_shutdown (); 747 struct GNUNET_IDENTITY_Ego *ego,
748 void **ctx,
749 const char *name)
750{
751 if (NULL == name)
752 return;
753 if (ego == caller_id)
754 {
755 if (verbose)
756 FPRINTF (stdout,
757 _("Name of our ego changed to `%s'\n"),
758 name);
759 GNUNET_free (ego_name);
760 ego_name = GNUNET_strdup (name);
761 return;
762 }
763 if (0 != strcmp (name,
764 ego_name))
765 return;
766 if (NULL == ego)
767 {
768 if (verbose)
769 FPRINTF (stdout,
770 _("Our ego `%s' was deleted!\n"),
771 ego_name);
772 caller_id = NULL;
773 return;
774 }
775 caller_id = ego;
776 GNUNET_CONFIGURATION_set_value_number (cfg,
777 "CONVERSATION",
778 "LINE",
779 line);
780 start_phone ();
402} 781}
403 782
404 783
@@ -416,20 +795,19 @@ run (void *cls,
416 const char *cfgfile, 795 const char *cfgfile,
417 const struct GNUNET_CONFIGURATION_Handle *c) 796 const struct GNUNET_CONFIGURATION_Handle *c)
418{ 797{
419 if (NULL == 798 cfg = GNUNET_CONFIGURATION_dup (c);
420 (conversation = 799 speaker = GNUNET_SPEAKER_create_from_hardware (cfg);
421 GNUNET_CONVERSATION_connect (c, NULL, 800 mic = GNUNET_MICROPHONE_create_from_hardware (cfg);
422 &call_handler, 801 if (NULL == ego_name)
423 &reject_handler,
424 &notification_handler,
425 &missed_call_handler)))
426 { 802 {
427 FPRINTF (stderr, 803 FPRINTF (stderr,
428 "%s", 804 "%s",
429 _("Could not access CONVERSATION service. Exiting.\n")); 805 _("You must specify the NAME of an ego to use\n"));
430 return; 806 return;
431 } 807 }
432 808 id = GNUNET_IDENTITY_connect (cfg,
809 &identity_cb,
810 NULL);
433 handle_cmd_task = 811 handle_cmd_task =
434 GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_UI, 812 GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_UI,
435 &handle_command, NULL); 813 &handle_command, NULL);
@@ -449,25 +827,29 @@ int
449main (int argc, char *const *argv) 827main (int argc, char *const *argv)
450{ 828{
451 static const struct GNUNET_GETOPT_CommandLineOption options[] = { 829 static const struct GNUNET_GETOPT_CommandLineOption options[] = {
830 {'e', "ego", "NAME",
831 gettext_noop ("sets the NAME of the ego to use for the phone (and name resolution)"),
832 1, &GNUNET_GETOPT_set_string, &ego_name},
833 {'p', "phone", "LINE",
834 gettext_noop ("sets the LINE to use for the phone"),
835 1, &GNUNET_GETOPT_set_uint, &line},
452 GNUNET_GETOPT_OPTION_END 836 GNUNET_GETOPT_OPTION_END
453 }; 837 };
454
455 int flags; 838 int flags;
456 int ret; 839 int ret;
457 840
458 flags = fcntl (0, F_GETFL, 0); 841 flags = fcntl (0, F_GETFL, 0);
459 flags |= O_NONBLOCK; 842 flags |= O_NONBLOCK;
460 fcntl (0, F_SETFL, flags); 843 fcntl (0, F_SETFL, flags);
461 844 stdin_fh = GNUNET_DISK_get_handle_from_int_fd (0);
462 if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv)) 845 if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
463 return 2; 846 return 2;
464 847 ret = GNUNET_PROGRAM_run (argc, argv,
465 ret = GNUNET_PROGRAM_run (argc, argv, "gnunet-conversation", 848 "gnunet-conversation",
466 gettext_noop ("Print information about conversation."), 849 gettext_noop ("Enables having a conversation with other GNUnet users."),
467 options, &run, NULL); 850 options, &run, NULL);
468 GNUNET_free ((void *) argv); 851 GNUNET_free ((void *) argv);
469 852 return (GNUNET_OK == ret) ? 0 : 1;
470 return ret;
471} 853}
472 854
473/* end of gnunet-conversation.c */ 855/* end of gnunet-conversation.c */
diff --git a/src/conversation/gnunet-service-conversation-new.c b/src/conversation/gnunet-service-conversation-new.c
deleted file mode 100644
index d379931f7..000000000
--- a/src/conversation/gnunet-service-conversation-new.c
+++ /dev/null
@@ -1,1227 +0,0 @@
1/*
2 This file is part of GNUnet.
3 (C) 2013 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 * @file conversation/gnunet-service-conversation.c
22 * @brief conversation service implementation
23 * @author Simon Dieterle
24 * @author Andreas Fuchs
25 * @author Christian Grothoff
26 */
27#include "platform.h"
28#include "gnunet_util_lib.h"
29#include "gnunet_protocols.h"
30#include "gnunet_applications.h"
31#include "gnunet_constants.h"
32#include "gnunet_signatures.h"
33#include "gnunet_mesh_service.h"
34#include "gnunet_conversation_service.h"
35#include "conversation.h"
36
37
38/**
39 * How long is our signature on a call valid? Needs to be long enough for time zone
40 * differences and network latency to not matter. No strong need for it to be short,
41 * but we simply like all signatures to eventually expire.
42 */
43#define RING_TIMEOUT GNUNET_TIME_UNIT_DAYS
44
45
46/**
47 * The possible connection status
48 */
49enum LineStatus
50{
51 /**
52 * We are waiting for incoming calls.
53 */
54 LS_CALLEE_LISTEN,
55
56 /**
57 * Our phone is ringing, waiting for the client to pick up.
58 */
59 LS_CALLEE_RINGING,
60
61 /**
62 * We are talking!
63 */
64 LS_CALLEE_CONNECTED,
65
66 /**
67 * We're in shutdown, sending hangup messages before cleaning up.
68 */
69 LS_CALLEE_SHUTDOWN,
70
71 /**
72 * We are waiting for the phone to be picked up.
73 */
74 LS_CALLER_CALLING,
75
76 /**
77 * We are talking!
78 */
79 LS_CALLER_CONNECTED,
80
81 /**
82 * We're in shutdown, sending hangup messages before cleaning up.
83 */
84 LS_CALLER_SHUTDOWN
85};
86
87
88/**
89 * A line connects a local client with a mesh tunnel (or, if it is an
90 * open line, is waiting for a mesh tunnel).
91 */
92struct Line
93{
94 /**
95 * Kept in a DLL.
96 */
97 struct Line *next;
98
99 /**
100 * Kept in a DLL.
101 */
102 struct Line *prev;
103
104 /**
105 * Handle for the reliable tunnel (contol data)
106 */
107 struct GNUNET_MESH_Tunnel *tunnel_reliable;
108
109 /**
110 * Handle for unreliable tunnel (audio data)
111 */
112 struct GNUNET_MESH_Tunnel *tunnel_unreliable;
113
114 /**
115 * Transmit handle for pending audio messages
116 */
117 struct GNUNET_MESH_TransmitHandle *unreliable_mth;
118
119 /**
120 * Message queue for control messages
121 */
122 struct GNUNET_MQ_Handle *reliable_mq;
123
124 /**
125 * Handle to the line client.
126 */
127 struct GNUNET_SERVER_Client *client;
128
129 /**
130 * Target of the line, if we are the caller.
131 */
132 struct GNUNET_PeerIdentity target;
133
134 /**
135 * Temporary buffer for audio data.
136 */
137 void *audio_data;
138
139 /**
140 * Number of bytes in @e audio_data.
141 */
142 size_t audio_size;
143
144 /**
145 * Our line number.
146 */
147 uint32_t local_line;
148
149 /**
150 * Remote line number.
151 */
152 uint32_t remote_line;
153
154 /**
155 * Current status of this line.
156 */
157 enum LineStatus status;
158
159};
160
161
162/**
163 * Our configuration.
164 */
165static const struct GNUNET_CONFIGURATION_Handle *cfg;
166
167/**
168 * Notification context containing all connected clients.
169 */
170static struct GNUNET_SERVER_NotificationContext *nc;
171
172/**
173 * Handle for mesh
174 */
175static struct GNUNET_MESH_Handle *mesh;
176
177/**
178 * Identity of this peer.
179 */
180static struct GNUNET_PeerIdentity my_identity;
181
182/**
183 * Head of DLL of active lines.
184 */
185static struct Line *lines_head;
186
187/**
188 * Tail of DLL of active lines.
189 */
190static struct Line *lines_tail;
191
192/**
193 * Counter for generating local line numbers.
194 * FIXME: randomize generation in the future
195 * to eliminate information leakage.
196 */
197static uint32_t local_line_cnt;
198
199
200/**
201 * Function to register a phone.
202 *
203 * @param cls closure, NULL
204 * @param client the client from which the message is
205 * @param message the message from the client
206 */
207static void
208handle_client_register_message (void *cls,
209 struct GNUNET_SERVER_Client *client,
210 const struct GNUNET_MessageHeader *message)
211{
212 const struct ClientPhoneRegisterMessage *msg;
213 struct Line *line;
214
215 msg = (struct ClientPhoneRegisterMessage *) message;
216 line = GNUNET_SERVER_client_get_user_context (client, struct Line);
217 if (NULL != line)
218 {
219 GNUNET_break (0);
220 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
221 return;
222 }
223 line = GNUNET_new (struct Line);
224 line->client = client;
225 GNUNET_SERVER_notification_context_add (nc, client);
226 GNUNET_CONTAINER_DLL_insert (lines_head,
227 lines_tail,
228 line);
229 line->local_line = ntohl (msg->line);
230 GNUNET_SERVER_client_set_user_context (client, line);
231 GNUNET_SERVER_receive_done (client, GNUNET_OK);
232}
233
234
235/**
236 * Function to handle a pickup request message from the client
237 *
238 * @param cls closure, NULL
239 * @param client the client from which the message is
240 * @param message the message from the client
241 */
242static void
243handle_client_pickup_message (void *cls,
244 struct GNUNET_SERVER_Client *client,
245 const struct GNUNET_MessageHeader *message)
246{
247 const struct ClientPhonePickupMessage *msg;
248 struct GNUNET_MQ_Envelope *e;
249 struct MeshPhonePickupMessage *mppm;
250 const char *meta;
251 struct Line *line;
252 size_t len;
253
254 msg = (struct ClientPhonePickupMessage *) message;
255 meta = (const char *) &msg[1];
256 len = ntohs (msg->header.size) - sizeof (struct ClientPhonePickupMessage);
257 if ( (0 == len) ||
258 ('\0' != meta[len - 1]) )
259 {
260 meta = NULL;
261 len = 0;
262 }
263 line = GNUNET_SERVER_client_get_user_context (client, struct Line);
264 if (NULL == line)
265 {
266 GNUNET_break (0);
267 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
268 return;
269 }
270 switch (line->status)
271 {
272 case LS_CALLEE_LISTEN:
273 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
274 "Ignoring client's PICKUP message, caller has HUNG UP already\n");
275 GNUNET_SERVER_receive_done (client, GNUNET_OK);
276 break;
277 case LS_CALLEE_RINGING:
278 line->status = LS_CALLEE_CONNECTED;
279 break;
280 case LS_CALLEE_CONNECTED:
281 GNUNET_break (0);
282 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
283 return;
284 case LS_CALLEE_SHUTDOWN:
285 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
286 "Ignoring client's PICKUP message, line is in SHUTDOWN\n");
287 GNUNET_SERVER_receive_done (client, GNUNET_OK);
288 break;
289 case LS_CALLER_CALLING:
290 case LS_CALLER_CONNECTED:
291 case LS_CALLER_SHUTDOWN:
292 GNUNET_break (0);
293 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
294 return;
295 }
296 line->status = LS_CALLEE_CONNECTED;
297 e = GNUNET_MQ_msg_extra (mppm,
298 len,
299 GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_PICK_UP);
300 memcpy (&mppm[1], meta, len);
301 GNUNET_MQ_send (line->reliable_mq, e);
302 GNUNET_SERVER_receive_done (client, GNUNET_OK);
303}
304
305
306/**
307 * Destroy the mesh tunnels of a line.
308 *
309 * @param line line to shutdown tunnels of
310 */
311static void
312destroy_line_mesh_tunnels (struct Line *line)
313{
314 if (NULL != line->reliable_mq)
315 {
316 GNUNET_MQ_destroy (line->reliable_mq);
317 line->reliable_mq = NULL;
318 }
319 if (NULL != line->unreliable_mth)
320 {
321 GNUNET_MESH_notify_transmit_ready_cancel (line->unreliable_mth);
322 line->unreliable_mth = NULL;
323 }
324 if (NULL != line->tunnel_unreliable)
325 {
326 GNUNET_MESH_tunnel_destroy (line->tunnel_unreliable);
327 line->tunnel_unreliable = NULL;
328 }
329 if (NULL != line->tunnel_reliable)
330 {
331 GNUNET_MESH_tunnel_destroy (line->tunnel_reliable);
332 line->tunnel_reliable = NULL;
333 }
334}
335
336
337/**
338 * We are done signalling shutdown to the other peer. Close down
339 * (or reset) the line.
340 *
341 * @param cls the `struct Line` to reset/terminate
342 */
343static void
344mq_done_finish_caller_shutdown (void *cls)
345{
346 struct Line *line = cls;
347
348 switch (line->status)
349 {
350 case LS_CALLEE_LISTEN:
351 GNUNET_break (0);
352 break;
353 case LS_CALLEE_RINGING:
354 GNUNET_break (0);
355 break;
356 case LS_CALLEE_CONNECTED:
357 GNUNET_break (0);
358 break;
359 case LS_CALLEE_SHUTDOWN:
360 line->status = LS_CALLEE_LISTEN;
361 destroy_line_mesh_tunnels (line);
362 return;
363 case LS_CALLER_CALLING:
364 line->status = LS_CALLER_SHUTDOWN;
365 break;
366 case LS_CALLER_CONNECTED:
367 line->status = LS_CALLER_SHUTDOWN;
368 break;
369 case LS_CALLER_SHUTDOWN:
370 destroy_line_mesh_tunnels (line);
371 GNUNET_CONTAINER_DLL_remove (lines_head,
372 lines_tail,
373 line);
374 GNUNET_free_non_null (line->audio_data);
375 GNUNET_free (line);
376 break;
377 }
378}
379
380
381/**
382 * Function to handle a hangup request message from the client
383 *
384 * @param cls closure, NULL
385 * @param client the client from which the message is
386 * @param message the message from the client
387 */
388static void
389handle_client_hangup_message (void *cls,
390 struct GNUNET_SERVER_Client *client,
391 const struct GNUNET_MessageHeader *message)
392{
393 const struct ClientPhoneHangupMessage *msg;
394 struct GNUNET_MQ_Envelope *e;
395 struct MeshPhoneHangupMessage *mhum;
396 const char *meta;
397 struct Line *line;
398 size_t len;
399
400 msg = (struct ClientPhoneHangupMessage *) message;
401 meta = (const char *) &msg[1];
402 len = ntohs (msg->header.size) - sizeof (struct ClientPhoneHangupMessage);
403 if ( (0 == len) ||
404 ('\0' != meta[len - 1]) )
405 {
406 meta = NULL;
407 len = 0;
408 }
409 line = GNUNET_SERVER_client_get_user_context (client, struct Line);
410 if (NULL == line)
411 {
412 GNUNET_break (0);
413 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
414 return;
415 }
416 switch (line->status)
417 {
418 case LS_CALLEE_LISTEN:
419 GNUNET_break (0);
420 GNUNET_SERVER_receive_done (client, GNUNET_OK);
421 return;
422 case LS_CALLEE_RINGING:
423 line->status = LS_CALLEE_SHUTDOWN;
424 break;
425 case LS_CALLEE_CONNECTED:
426 line->status = LS_CALLEE_SHUTDOWN;
427 break;
428 case LS_CALLEE_SHUTDOWN:
429 GNUNET_break (0);
430 GNUNET_SERVER_receive_done (client, GNUNET_OK);
431 return;
432 case LS_CALLER_CALLING:
433 line->status = LS_CALLER_SHUTDOWN;
434 break;
435 case LS_CALLER_CONNECTED:
436 line->status = LS_CALLER_SHUTDOWN;
437 break;
438 case LS_CALLER_SHUTDOWN:
439 GNUNET_break (0);
440 GNUNET_SERVER_receive_done (client, GNUNET_OK);
441 return;
442 }
443 e = GNUNET_MQ_msg_extra (mhum,
444 len,
445 GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_HANG_UP);
446 memcpy (&mhum[1], meta, len);
447 GNUNET_MQ_notify_sent (e,
448 &mq_done_finish_caller_shutdown,
449 line);
450 GNUNET_MQ_send (line->reliable_mq, e);
451 GNUNET_SERVER_receive_done (client, GNUNET_OK);
452}
453
454
455/**
456 * Function to handle call request the client
457 *
458 * @param cls closure, NULL
459 * @param client the client from which the message is
460 * @param message the message from the client
461 */
462static void
463handle_client_call_message (void *cls,
464 struct GNUNET_SERVER_Client *client,
465 const struct GNUNET_MessageHeader *message)
466{
467 const struct ClientCallMessage *msg;
468 struct Line *line;
469 struct GNUNET_MQ_Envelope *e;
470 struct MeshPhoneRingMessage *ring;
471
472 msg = (struct ClientCallMessage *) message;
473 line = GNUNET_SERVER_client_get_user_context (client, struct Line);
474 if (NULL != line)
475 {
476 GNUNET_break (0);
477 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
478 return;
479 }
480 line = GNUNET_new (struct Line);
481 line->target = msg->target;
482 GNUNET_CONTAINER_DLL_insert (lines_head,
483 lines_tail,
484 line);
485 line->remote_line = ntohl (msg->line);
486 line->status = LS_CALLER_CALLING;
487 line->tunnel_reliable = GNUNET_MESH_tunnel_create (mesh,
488 line,
489 &msg->target,
490 GNUNET_APPLICATION_TYPE_CONVERSATION_CONTROL,
491 GNUNET_NO,
492 GNUNET_YES);
493 line->reliable_mq = GNUNET_MESH_mq_create (line->tunnel_reliable);
494 line->local_line = local_line_cnt++;
495 e = GNUNET_MQ_msg (ring, GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_RING);
496 ring->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_CONVERSATION_RING);
497 ring->purpose.size = htonl (sizeof (struct GNUNET_PeerIdentity) * 2 +
498 sizeof (struct GNUNET_TIME_AbsoluteNBO) +
499 sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose) +
500 sizeof (struct GNUNET_CRYPTO_EccPublicSignKey));
501 GNUNET_CRYPTO_ecc_key_get_public_for_signature (&msg->caller_id,
502 &ring->caller_id);
503 ring->remote_line = msg->line;
504 ring->source_line = line->local_line;
505 ring->target = msg->target;
506 ring->source = my_identity;
507 ring->expiration_time = GNUNET_TIME_absolute_hton (GNUNET_TIME_relative_to_absolute (RING_TIMEOUT));
508 GNUNET_CRYPTO_ecc_sign (&msg->caller_id,
509 &ring->purpose,
510 &ring->signature);
511 GNUNET_MQ_send (line->reliable_mq, e);
512 GNUNET_SERVER_client_set_user_context (client, line);
513 GNUNET_SERVER_receive_done (client, GNUNET_OK);
514}
515
516
517/**
518 * Transmit audio data via unreliable mesh channel.
519 *
520 * @param cls the `struct Line` we are transmitting for
521 * @param size number of bytes available in @a buf
522 * @param buf where to copy the data
523 * @return number of bytes copied to @buf
524 */
525static size_t
526transmit_line_audio (void *cls,
527 size_t size,
528 void *buf)
529{
530 struct Line *line = cls;
531 struct MeshAudioMessage *mam = buf;
532
533 line->unreliable_mth = NULL;
534 if ( (NULL == buf) ||
535 (size < sizeof (struct MeshAudioMessage) + line->audio_size) )
536 {
537 /* eh, other error handling? */
538 return 0;
539 }
540 mam->header.size = htons (sizeof (struct MeshAudioMessage) + line->audio_size);
541 mam->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_AUDIO);
542 mam->remote_line = htonl (line->remote_line);
543 memcpy (&mam[1], line->audio_data, line->audio_size);
544 GNUNET_free (line->audio_data);
545 line->audio_data = NULL;
546 return sizeof (struct MeshAudioMessage) + line->audio_size;
547}
548
549
550/**
551 * Function to handle audio data from the client
552 *
553 * @param cls closure, NULL
554 * @param client the client from which the message is
555 * @param message the message from the client
556 */
557static void
558handle_client_audio_message (void *cls,
559 struct GNUNET_SERVER_Client *client,
560 const struct GNUNET_MessageHeader *message)
561{
562 const struct ClientAudioMessage *msg;
563 struct Line *line;
564 size_t size;
565
566 size = ntohs (message->size) - sizeof (struct ClientAudioMessage);
567 msg = (struct ClientAudioMessage *) message;
568 line = GNUNET_SERVER_client_get_user_context (client, struct Line);
569 if (NULL == line)
570 {
571 GNUNET_break (0);
572 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
573 return;
574 }
575 switch (line->status)
576 {
577 case LS_CALLEE_LISTEN:
578 case LS_CALLEE_RINGING:
579 case LS_CALLER_CALLING:
580 GNUNET_break (0);
581 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
582 return;
583 case LS_CALLEE_CONNECTED:
584 case LS_CALLER_CONNECTED:
585 /* common case, handled below */
586 break;
587 case LS_CALLEE_SHUTDOWN:
588 case LS_CALLER_SHUTDOWN:
589 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
590 "Mesh audio channel in shutdown; audio data dropped\n");
591 GNUNET_SERVER_receive_done (client, GNUNET_OK);
592 return;
593 }
594 if (NULL == line->tunnel_unreliable)
595 {
596 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
597 _("Mesh audio channel not ready; audio data dropped\n"));
598 GNUNET_SERVER_receive_done (client, GNUNET_OK);
599 return;
600 }
601 if (NULL != line->unreliable_mth)
602 {
603 /* NOTE: we may want to not do this and instead combine the data */
604 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
605 "Dropping previous audio data segment with %u bytes\n",
606 line->audio_size);
607 GNUNET_MESH_notify_transmit_ready_cancel (line->unreliable_mth);
608 GNUNET_free (line->audio_data);
609 }
610 line->audio_size = size;
611 line->audio_data = GNUNET_malloc (line->audio_size);
612 memcpy (line->audio_data,
613 &msg[1],
614 size);
615 line->unreliable_mth = GNUNET_MESH_notify_transmit_ready (line->tunnel_unreliable,
616 GNUNET_NO,
617 GNUNET_TIME_UNIT_FOREVER_REL,
618 sizeof (struct MeshAudioMessage)
619 + line->audio_size,
620 &transmit_line_audio,
621 line);
622 GNUNET_SERVER_receive_done (client, GNUNET_OK);
623}
624
625
626/**
627 * We are done signalling shutdown to the other peer.
628 * Destroy the tunnel.
629 *
630 * @param cls the `struct GNUNET_MESH_tunnel` to destroy
631 */
632static void
633mq_done_destroy_tunnel (void *cls)
634{
635 struct GNUNET_MESH_Tunnel *tunnel = cls;
636
637 GNUNET_MESH_tunnel_destroy (tunnel);
638}
639
640
641/**
642 * Function to handle a ring message incoming over mesh
643 *
644 * @param cls closure, NULL
645 * @param tunnel the tunnel over which the message arrived
646 * @param tunnel_ctx the tunnel context, can be NULL
647 * @param message the incoming message
648 * @return #GNUNET_OK
649 */
650static int
651handle_mesh_ring_message (void *cls,
652 struct GNUNET_MESH_Tunnel *tunnel,
653 void **tunnel_ctx,
654 const struct GNUNET_MessageHeader *message)
655{
656 const struct MeshPhoneRingMessage *msg;
657 struct Line *line;
658 struct GNUNET_MQ_Envelope *e;
659 struct MeshPhoneBusyMessage *busy;
660 struct ClientPhoneRingMessage cring;
661
662 msg = (const struct MeshPhoneRingMessage *) message;
663 if ( (msg->purpose.size != htonl (sizeof (struct GNUNET_PeerIdentity) * 2 +
664 sizeof (struct GNUNET_TIME_AbsoluteNBO) +
665 sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose) +
666 sizeof (struct GNUNET_CRYPTO_EccPublicSignKey))) ||
667 (GNUNET_OK !=
668 GNUNET_CRYPTO_ecc_verify (GNUNET_SIGNATURE_PURPOSE_CONVERSATION_RING,
669 &msg->purpose,
670 &msg->signature,
671 &msg->caller_id)) )
672 {
673 GNUNET_break_op (0);
674 return GNUNET_SYSERR;
675 }
676 for (line = lines_head; NULL != line; line = line->next)
677 if ( (line->local_line == ntohl (msg->remote_line)) &&
678 (LS_CALLEE_LISTEN == line->status) )
679 break;
680 if (NULL == line)
681 {
682 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
683 _("No available phone for incoming call on line %u, sending BUSY signal\n"),
684 ntohl (msg->remote_line));
685 e = GNUNET_MQ_msg (busy, GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_BUSY);
686 GNUNET_MQ_notify_sent (e,
687 &mq_done_destroy_tunnel,
688 tunnel);
689 GNUNET_MQ_send (line->reliable_mq, e);
690 GNUNET_MESH_receive_done (tunnel); /* needed? */
691 return GNUNET_OK;
692 }
693 line->status = LS_CALLEE_RINGING;
694 line->remote_line = ntohl (msg->source_line);
695 line->tunnel_reliable = tunnel;
696 line->reliable_mq = GNUNET_MESH_mq_create (line->tunnel_reliable);
697 *tunnel_ctx = line;
698 cring.header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_RING);
699 cring.header.size = htons (sizeof (cring));
700 cring.reserved = htonl (0);
701 cring.caller_id = msg->caller_id;
702 GNUNET_SERVER_notification_context_unicast (nc,
703 line->client,
704 &cring.header,
705 GNUNET_NO);
706 GNUNET_MESH_receive_done (tunnel);
707 return GNUNET_OK;
708}
709
710
711/**
712 * Function to handle a hangup message incoming over mesh
713 *
714 * @param cls closure, NULL
715 * @param tunnel the tunnel over which the message arrived
716 * @param tunnel_ctx the tunnel context, can be NULL
717 * @param message the incoming message
718 * @return #GNUNET_OK
719 */
720static int
721handle_mesh_hangup_message (void *cls,
722 struct GNUNET_MESH_Tunnel *tunnel,
723 void **tunnel_ctx,
724 const struct GNUNET_MessageHeader *message)
725{
726 struct Line *line = *tunnel_ctx;
727 const struct MeshPhoneHangupMessage *msg;
728 const char *reason;
729 size_t len = ntohs (message->size) - sizeof (struct MeshPhoneHangupMessage);
730 char buf[len + sizeof (struct ClientPhoneHangupMessage)];
731 struct ClientPhoneHangupMessage *hup;
732
733 msg = (const struct MeshPhoneHangupMessage *) message;
734 len = ntohs (msg->header.size) - sizeof (struct MeshPhoneHangupMessage);
735 reason = (const char *) &msg[1];
736 if ( (0 == len) ||
737 ('\0' != reason[len - 1]) )
738 {
739 reason = NULL;
740 len = 0;
741 }
742 if (NULL == line)
743 {
744 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
745 "HANGUP message received for non-existing line, dropping tunnel.\n");
746 return GNUNET_SYSERR;
747 }
748 *tunnel_ctx = NULL;
749 switch (line->status)
750 {
751 case LS_CALLEE_LISTEN:
752 GNUNET_break (0);
753 return GNUNET_SYSERR;
754 case LS_CALLEE_RINGING:
755 line->status = LS_CALLEE_LISTEN;
756 destroy_line_mesh_tunnels (line);
757 break;
758 case LS_CALLEE_CONNECTED:
759 line->status = LS_CALLEE_LISTEN;
760 destroy_line_mesh_tunnels (line);
761 break;
762 case LS_CALLEE_SHUTDOWN:
763 line->status = LS_CALLEE_LISTEN;
764 destroy_line_mesh_tunnels (line);
765 return GNUNET_OK;
766 case LS_CALLER_CALLING:
767 line->status = LS_CALLER_SHUTDOWN;
768 mq_done_finish_caller_shutdown (line);
769 break;
770 case LS_CALLER_CONNECTED:
771 line->status = LS_CALLER_SHUTDOWN;
772 mq_done_finish_caller_shutdown (line);
773 break;
774 case LS_CALLER_SHUTDOWN:
775 mq_done_finish_caller_shutdown (line);
776 return GNUNET_OK;
777 }
778 hup = (struct ClientPhoneHangupMessage *) buf;
779 hup->header.size = sizeof (buf);
780 hup->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP);
781 memcpy (&hup[1], reason, len);
782 GNUNET_SERVER_notification_context_unicast (nc,
783 line->client,
784 &hup->header,
785 GNUNET_NO);
786 GNUNET_MESH_receive_done (tunnel);
787 return GNUNET_OK;
788}
789
790
791/**
792 * Function to handle a pickup message incoming over mesh
793 *
794 * @param cls closure, NULL
795 * @param tunnel the tunnel over which the message arrived
796 * @param tunnel_ctx the tunnel context, can be NULL
797 * @param message the incoming message
798 * @return #GNUNET_OK
799 */
800static int
801handle_mesh_pickup_message (void *cls,
802 struct GNUNET_MESH_Tunnel *tunnel,
803 void **tunnel_ctx,
804 const struct GNUNET_MessageHeader *message)
805{
806 const struct MeshPhonePickupMessage *msg;
807 struct Line *line = *tunnel_ctx;
808 const char *metadata;
809 size_t len = ntohs (message->size) - sizeof (struct MeshPhonePickupMessage);
810 char buf[len + sizeof (struct ClientPhonePickupMessage)];
811 struct ClientPhonePickupMessage *pick;
812
813 msg = (const struct MeshPhonePickupMessage *) message;
814 len = ntohs (msg->header.size) - sizeof (struct MeshPhonePickupMessage);
815 metadata = (const char *) &msg[1];
816 if ( (0 == len) ||
817 ('\0' != metadata[len - 1]) )
818 {
819 metadata = NULL;
820 len = 0;
821 }
822 if (NULL == line)
823 {
824 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
825 "PICKUP message received for non-existing line, dropping tunnel.\n");
826 return GNUNET_SYSERR;
827 }
828 GNUNET_MESH_receive_done (tunnel);
829 switch (line->status)
830 {
831 case LS_CALLEE_LISTEN:
832 GNUNET_break (0);
833 return GNUNET_SYSERR;
834 case LS_CALLEE_RINGING:
835 case LS_CALLEE_CONNECTED:
836 GNUNET_break_op (0);
837 destroy_line_mesh_tunnels (line);
838 line->status = LS_CALLEE_LISTEN;
839 return GNUNET_SYSERR;
840 case LS_CALLEE_SHUTDOWN:
841 GNUNET_break_op (0);
842 line->status = LS_CALLEE_LISTEN;
843 destroy_line_mesh_tunnels (line);
844 break;
845 case LS_CALLER_CALLING:
846 line->status = LS_CALLER_CONNECTED;
847 break;
848 case LS_CALLER_CONNECTED:
849 GNUNET_break_op (0);
850 return GNUNET_OK;
851 case LS_CALLER_SHUTDOWN:
852 GNUNET_break_op (0);
853 mq_done_finish_caller_shutdown (line);
854 return GNUNET_SYSERR;
855 }
856 pick = (struct ClientPhonePickupMessage *) buf;
857 pick->header.size = sizeof (buf);
858 pick->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_PICK_UP);
859 memcpy (&pick[1], metadata, len);
860 GNUNET_SERVER_notification_context_unicast (nc,
861 line->client,
862 &pick->header,
863 GNUNET_NO);
864 line->tunnel_unreliable = GNUNET_MESH_tunnel_create (mesh,
865 line,
866 &line->target,
867 GNUNET_APPLICATION_TYPE_CONVERSATION_AUDIO,
868 GNUNET_YES,
869 GNUNET_NO);
870 return GNUNET_OK;
871}
872
873
874/**
875 * Function to handle a busy message incoming over mesh
876 *
877 * @param cls closure, NULL
878 * @param tunnel the tunnel over which the message arrived
879 * @param tunnel_ctx the tunnel context, can be NULL
880 * @param message the incoming message
881 * @return #GNUNET_OK
882 */
883static int
884handle_mesh_busy_message (void *cls,
885 struct GNUNET_MESH_Tunnel *tunnel,
886 void **tunnel_ctx,
887 const struct GNUNET_MessageHeader *message)
888{
889 struct Line *line = *tunnel_ctx;
890 struct ClientPhoneBusyMessage busy;
891
892 if (NULL == line)
893 {
894 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
895 "HANGUP message received for non-existing line, dropping tunnel.\n");
896 return GNUNET_SYSERR;
897 }
898 busy.header.size = sizeof (busy);
899 busy.header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_BUSY);
900 GNUNET_SERVER_notification_context_unicast (nc,
901 line->client,
902 &busy.header,
903 GNUNET_NO);
904 GNUNET_MESH_receive_done (tunnel);
905 *tunnel_ctx = NULL;
906 switch (line->status)
907 {
908 case LS_CALLEE_LISTEN:
909 GNUNET_break (0);
910 return GNUNET_SYSERR;
911 case LS_CALLEE_RINGING:
912 GNUNET_break_op (0);
913 break;
914 case LS_CALLEE_CONNECTED:
915 GNUNET_break_op (0);
916 break;
917 case LS_CALLEE_SHUTDOWN:
918 GNUNET_break_op (0);
919 break;
920 case LS_CALLER_CALLING:
921 line->status = LS_CALLER_SHUTDOWN;
922 mq_done_finish_caller_shutdown (line);
923 break;
924 case LS_CALLER_CONNECTED:
925 line->status = LS_CALLER_SHUTDOWN;
926 mq_done_finish_caller_shutdown (line);
927 break;
928 case LS_CALLER_SHUTDOWN:
929 mq_done_finish_caller_shutdown (line);
930 break;
931 }
932 return GNUNET_OK;
933}
934
935
936/**
937 * Function to handle an audio message incoming over mesh
938 *
939 * @param cls closure, NULL
940 * @param tunnel the tunnel over which the message arrived
941 * @param tunnel_ctx the tunnel context, can be NULL
942 * @param message the incoming message
943 * @return #GNUNET_OK
944 */
945static int
946handle_mesh_audio_message (void *cls,
947 struct GNUNET_MESH_Tunnel *tunnel,
948 void **tunnel_ctx,
949 const struct GNUNET_MessageHeader *message)
950{
951 const struct MeshAudioMessage *msg;
952 struct Line *line = *tunnel_ctx;
953 struct GNUNET_PeerIdentity sender;
954 size_t msize = ntohs (message->size) - sizeof (struct MeshAudioMessage);
955 char buf[msize + sizeof (struct ClientAudioMessage)];
956 struct ClientAudioMessage *cam;
957
958 msg = (const struct MeshAudioMessage *) message;
959 if (NULL == line)
960 {
961 sender = *GNUNET_MESH_tunnel_get_info (tunnel,
962 GNUNET_MESH_OPTION_PEER)->peer;
963 for (line = lines_head; NULL != line; line = line->next)
964 if ( (line->local_line == ntohl (msg->remote_line)) &&
965 (LS_CALLEE_CONNECTED == line->status) &&
966 (0 == memcmp (&line->target,
967 &sender,
968 sizeof (struct GNUNET_PeerIdentity))) &&
969 (NULL == line->tunnel_unreliable) )
970 break;
971 if (NULL == line)
972 {
973 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
974 "Received AUDIO data for non-existing line %u, dropping.\n",
975 ntohl (msg->remote_line));
976 return GNUNET_SYSERR;
977 }
978 line->tunnel_unreliable = tunnel;
979 *tunnel_ctx = line;
980 }
981 cam = (struct ClientAudioMessage *) buf;
982 cam->header.size = htons (sizeof (buf));
983 cam->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO);
984 memcpy (&cam[1], &msg[1], msize);
985 GNUNET_SERVER_notification_context_unicast (nc,
986 line->client,
987 &cam->header,
988 GNUNET_YES);
989 GNUNET_MESH_receive_done (tunnel);
990 return GNUNET_OK;
991}
992
993
994/**
995 * Method called whenever another peer has added us to a tunnel
996 * the other peer initiated.
997 *
998 * @param cls closure
999 * @param tunnel new handle to the tunnel
1000 * @param initiator peer that started the tunnel
1001 * @param port port
1002 * @return initial tunnel context for the tunnel (can be NULL -- that's not an error)
1003 */
1004static void *
1005inbound_tunnel (void *cls,
1006 struct GNUNET_MESH_Tunnel *tunnel,
1007 const struct GNUNET_PeerIdentity *initiator,
1008 uint32_t port)
1009{
1010 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1011 _("Received incoming tunnel on port %d\n"),
1012 port);
1013 return NULL;
1014}
1015
1016
1017/**
1018 * Function called whenever an inbound tunnel is destroyed. Should clean up
1019 * any associated state.
1020 *
1021 * @param cls closure (set from #GNUNET_MESH_connect)
1022 * @param tunnel connection to the other end (henceforth invalid)
1023 * @param tunnel_ctx place where local state associated
1024 * with the tunnel is stored
1025 */
1026static void
1027inbound_end (void *cls,
1028 const struct GNUNET_MESH_Tunnel *tunnel,
1029 void *tunnel_ctx)
1030{
1031 struct Line *line = tunnel_ctx;
1032 struct ClientPhoneHangupMessage hup;
1033
1034 if (NULL == line)
1035 return;
1036 if (line->tunnel_unreliable == tunnel)
1037 {
1038 line->tunnel_unreliable = NULL;
1039 return;
1040 }
1041 hup.header.size = sizeof (hup);
1042 hup.header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP);
1043 switch (line->status)
1044 {
1045 case LS_CALLEE_LISTEN:
1046 GNUNET_break (0);
1047 return;
1048 case LS_CALLEE_RINGING:
1049 case LS_CALLEE_CONNECTED:
1050 GNUNET_SERVER_notification_context_unicast (nc,
1051 line->client,
1052 &hup.header,
1053 GNUNET_NO);
1054 line->status = LS_CALLEE_LISTEN;
1055 break;
1056 case LS_CALLEE_SHUTDOWN:
1057 line->status = LS_CALLEE_LISTEN;
1058 destroy_line_mesh_tunnels (line);
1059 break;
1060 case LS_CALLER_CALLING:
1061 case LS_CALLER_CONNECTED:
1062 GNUNET_SERVER_notification_context_unicast (nc,
1063 line->client,
1064 &hup.header,
1065 GNUNET_NO);
1066 destroy_line_mesh_tunnels (line);
1067 GNUNET_CONTAINER_DLL_remove (lines_head,
1068 lines_tail,
1069 line);
1070 GNUNET_free_non_null (line->audio_data);
1071 GNUNET_free (line);
1072 break;
1073 case LS_CALLER_SHUTDOWN:
1074 destroy_line_mesh_tunnels (line);
1075 GNUNET_CONTAINER_DLL_remove (lines_head,
1076 lines_tail,
1077 line);
1078 GNUNET_free (line);
1079 break;
1080 }
1081}
1082
1083
1084/**
1085 * A client disconnected. Remove all of its data structure entries.
1086 *
1087 * @param cls closure, NULL
1088 * @param client identification of the client
1089 */
1090static void
1091handle_client_disconnect (void *cls,
1092 struct GNUNET_SERVER_Client *client)
1093{
1094 struct Line *line;
1095
1096 line = GNUNET_SERVER_client_get_user_context (client, struct Line);
1097 if (NULL == line)
1098 return;
1099 GNUNET_CONTAINER_DLL_remove (lines_head,
1100 lines_tail,
1101 line);
1102 GNUNET_free (line);
1103 GNUNET_SERVER_client_set_user_context (client, NULL);
1104}
1105
1106
1107/**
1108 * Shutdown nicely
1109 *
1110 * @param cls closure, NULL
1111 * @param tc the task context
1112 */
1113static void
1114do_shutdown (void *cls,
1115 const struct GNUNET_SCHEDULER_TaskContext *tc)
1116{
1117 if (NULL != mesh)
1118 {
1119 GNUNET_MESH_disconnect (mesh);
1120 mesh = NULL;
1121 }
1122 if (NULL != nc)
1123 {
1124 GNUNET_SERVER_notification_context_destroy (nc);
1125 nc = NULL;
1126 }
1127}
1128
1129
1130/**
1131 * Main function that will be run by the scheduler.
1132 *
1133 * @param cls closure
1134 * @param server server handle
1135 * @param c configuration
1136 */
1137static void
1138run (void *cls,
1139 struct GNUNET_SERVER_Handle *server,
1140 const struct GNUNET_CONFIGURATION_Handle *c)
1141{
1142 static const struct GNUNET_SERVER_MessageHandler server_handlers[] = {
1143 {&handle_client_register_message, NULL,
1144 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_REGISTER,
1145 sizeof (struct ClientPhoneRegisterMessage)},
1146 {&handle_client_pickup_message, NULL,
1147 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_PICK_UP,
1148 0},
1149 {&handle_client_hangup_message, NULL,
1150 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP,
1151 0},
1152 {&handle_client_call_message, NULL,
1153 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_CALL,
1154 0},
1155 {&handle_client_audio_message, NULL,
1156 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO,
1157 0},
1158 {NULL, NULL, 0, 0}
1159 };
1160 static struct GNUNET_MESH_MessageHandler mesh_handlers[] = {
1161 {&handle_mesh_ring_message,
1162 GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_RING,
1163 sizeof (struct MeshPhoneRingMessage)},
1164 {&handle_mesh_hangup_message,
1165 GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_HANG_UP,
1166 0},
1167 {&handle_mesh_pickup_message,
1168 GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_PICK_UP,
1169 0},
1170 {&handle_mesh_busy_message,
1171 GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_BUSY,
1172 sizeof (struct MeshPhoneBusyMessage)},
1173 {&handle_mesh_audio_message, GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_AUDIO,
1174 0},
1175 {NULL, 0, 0}
1176 };
1177 static uint32_t ports[] = {
1178 GNUNET_APPLICATION_TYPE_CONVERSATION_CONTROL,
1179 GNUNET_APPLICATION_TYPE_CONVERSATION_AUDIO,
1180 0
1181 };
1182
1183 cfg = c;
1184 GNUNET_assert (GNUNET_OK ==
1185 GNUNET_CRYPTO_get_host_identity (cfg,
1186 &my_identity));
1187 mesh = GNUNET_MESH_connect (cfg,
1188 NULL,
1189 &inbound_tunnel,
1190 &inbound_end,
1191 mesh_handlers,
1192 ports);
1193
1194 if (NULL == mesh)
1195 {
1196 GNUNET_break (0);
1197 GNUNET_SCHEDULER_shutdown ();
1198 return;
1199 }
1200 nc = GNUNET_SERVER_notification_context_create (server, 16);
1201 GNUNET_SERVER_add_handlers (server, server_handlers);
1202 GNUNET_SERVER_disconnect_notify (server, &handle_client_disconnect, NULL);
1203 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
1204 &do_shutdown,
1205 NULL);
1206}
1207
1208
1209/**
1210 * The main function for the conversation service.
1211 *
1212 * @param argc number of arguments from the command line
1213 * @param argv command line arguments
1214 * @return 0 ok, 1 on error
1215 */
1216int
1217main (int argc,
1218 char *const *argv)
1219{
1220 return (GNUNET_OK ==
1221 GNUNET_SERVICE_run (argc, argv,
1222 "conversation",
1223 GNUNET_SERVICE_OPTION_NONE,
1224 &run, NULL)) ? 0 : 1;
1225}
1226
1227/* end of gnunet-service-conversation.c */
diff --git a/src/conversation/gnunet-service-conversation.c b/src/conversation/gnunet-service-conversation.c
index 4c4bdc133..d379931f7 100644
--- a/src/conversation/gnunet-service-conversation.c
+++ b/src/conversation/gnunet-service-conversation.c
@@ -22,56 +22,140 @@
22 * @brief conversation service implementation 22 * @brief conversation service implementation
23 * @author Simon Dieterle 23 * @author Simon Dieterle
24 * @author Andreas Fuchs 24 * @author Andreas Fuchs
25 * STRUCTURE: 25 * @author Christian Grothoff
26 * - Variables
27 * - AUXILIARY FUNCTIONS
28 * - SENDING FUNCTIONS CL -> SERVER
29 * - RECEIVE FUNCTIONS CL -> SERVER
30 * - SENDING FUNCTIONS MESH
31 * - RECEIVE FUNCTIONS MESH
32 * - HELPER
33 * - TUNNEL HANDLING
34 * - CLIENT HANDLING
35 */ 26 */
36#include "platform.h" 27#include "platform.h"
37#include "gnunet_util_lib.h" 28#include "gnunet_util_lib.h"
38#include "gnunet_protocols.h" 29#include "gnunet_protocols.h"
30#include "gnunet_applications.h"
39#include "gnunet_constants.h" 31#include "gnunet_constants.h"
32#include "gnunet_signatures.h"
40#include "gnunet_mesh_service.h" 33#include "gnunet_mesh_service.h"
41#include "gnunet_conversation_service.h" 34#include "gnunet_conversation_service.h"
42#include "conversation.h" 35#include "conversation.h"
43 36
44 37
38/**
39 * How long is our signature on a call valid? Needs to be long enough for time zone
40 * differences and network latency to not matter. No strong need for it to be short,
41 * but we simply like all signatures to eventually expire.
42 */
43#define RING_TIMEOUT GNUNET_TIME_UNIT_DAYS
45 44
46/* 45
47* The possible connection status 46/**
48*/ 47 * The possible connection status
49enum connection_status 48 */
49enum LineStatus
50{ 50{
51 LISTEN, 51 /**
52 CALLER, 52 * We are waiting for incoming calls.
53 CALLEE, 53 */
54 CONNECTED 54 LS_CALLEE_LISTEN,
55
56 /**
57 * Our phone is ringing, waiting for the client to pick up.
58 */
59 LS_CALLEE_RINGING,
60
61 /**
62 * We are talking!
63 */
64 LS_CALLEE_CONNECTED,
65
66 /**
67 * We're in shutdown, sending hangup messages before cleaning up.
68 */
69 LS_CALLEE_SHUTDOWN,
70
71 /**
72 * We are waiting for the phone to be picked up.
73 */
74 LS_CALLER_CALLING,
75
76 /**
77 * We are talking!
78 */
79 LS_CALLER_CONNECTED,
80
81 /**
82 * We're in shutdown, sending hangup messages before cleaning up.
83 */
84 LS_CALLER_SHUTDOWN
55}; 85};
56 86
57 87
58/******************************************************** 88/**
59 * Ugly hack because of not working MESH API 89 * A line connects a local client with a mesh tunnel (or, if it is an
60*/ 90 * open line, is waiting for a mesh tunnel).
61typedef uint32_t MESH_TunnelNumber; 91 */
62struct GNUNET_MESH_Tunnel 92struct Line
63{ 93{
64 struct GNUNET_MESH_Tunnel *next; 94 /**
65 struct GNUNET_MESH_Tunnel *prev; 95 * Kept in a DLL.
66 struct GNUNET_MESH_Handle *mesh; 96 */
67 MESH_TunnelNumber tid; 97 struct Line *next;
68 uint32_t port; 98
69 GNUNET_PEER_Id peer; 99 /**
70 void *ctx; 100 * Kept in a DLL.
71 unsigned int packet_size; 101 */
72 int buffering; 102 struct Line *prev;
73 int reliable; 103
74 int allow_send; 104 /**
105 * Handle for the reliable tunnel (contol data)
106 */
107 struct GNUNET_MESH_Tunnel *tunnel_reliable;
108
109 /**
110 * Handle for unreliable tunnel (audio data)
111 */
112 struct GNUNET_MESH_Tunnel *tunnel_unreliable;
113
114 /**
115 * Transmit handle for pending audio messages
116 */
117 struct GNUNET_MESH_TransmitHandle *unreliable_mth;
118
119 /**
120 * Message queue for control messages
121 */
122 struct GNUNET_MQ_Handle *reliable_mq;
123
124 /**
125 * Handle to the line client.
126 */
127 struct GNUNET_SERVER_Client *client;
128
129 /**
130 * Target of the line, if we are the caller.
131 */
132 struct GNUNET_PeerIdentity target;
133
134 /**
135 * Temporary buffer for audio data.
136 */
137 void *audio_data;
138
139 /**
140 * Number of bytes in @e audio_data.
141 */
142 size_t audio_size;
143
144 /**
145 * Our line number.
146 */
147 uint32_t local_line;
148
149 /**
150 * Remote line number.
151 */
152 uint32_t remote_line;
153
154 /**
155 * Current status of this line.
156 */
157 enum LineStatus status;
158
75}; 159};
76 160
77 161
@@ -81,1421 +165,831 @@ struct GNUNET_MESH_Tunnel
81static const struct GNUNET_CONFIGURATION_Handle *cfg; 165static const struct GNUNET_CONFIGURATION_Handle *cfg;
82 166
83/** 167/**
84 * Head of the list of current clients.
85 */
86static struct GNUNET_CONTAINER_SList *clients;
87
88/**
89 * Notification context containing all connected clients. 168 * Notification context containing all connected clients.
90 */ 169 */
91struct GNUNET_SERVER_NotificationContext *nc = NULL; 170static struct GNUNET_SERVER_NotificationContext *nc;
92
93/**
94* The connection status
95*/
96static struct ConnectionStatus connection;
97 171
98/** 172/**
99* Handle for the record helper 173 * Handle for mesh
100*/
101static struct GNUNET_HELPER_Handle *record_helper;
102
103/** Handle for the playback handler
104*
105*/
106static struct GNUNET_HELPER_Handle *playback_helper;
107
108/**
109* Handle for mesh
110*/
111static struct GNUNET_MESH_Handle *mesh;
112
113/**
114* Transmit handle for audio messages
115*/
116static struct GNUNET_MESH_TransmitHandle *mth = NULL;
117
118/**
119* Handle for the reliable tunnel (contol data)
120*/
121static struct GNUNET_MESH_Tunnel *tunnel_reliable;
122
123/**
124* Handle for unreliable tunnel (audio data)
125*/
126static struct GNUNET_MESH_Tunnel *tunnel_unreliable;
127
128/**
129* List for missed calls
130*/
131static struct GNUNET_CONTAINER_SList *missed_calls;
132
133/**
134* List for peers to notify that we are available again
135*/
136static struct GNUNET_CONTAINER_SList *peers_to_notify;
137
138/**
139* Audio buffer (outgoing)
140*/
141static struct GNUNET_CONTAINER_SList *audio_buffer;
142
143/**
144* The pointer to the task for sending audio
145*/
146static GNUNET_SCHEDULER_TaskIdentifier audio_task;
147
148/**
149* The pointer to the task for checking timeouts an calling a peer
150*/
151static GNUNET_SCHEDULER_TaskIdentifier timeout_task;
152
153/**
154 * Timestamp for call statistics
155 */
156static struct GNUNET_TIME_Absolute start_time;
157
158/**
159 * Number of payload packes sent
160 */ 174 */
161static int data_sent; 175static struct GNUNET_MESH_Handle *mesh;
162static int data_sent_size;
163 176
164/** 177/**
165 * Number of payload packets received 178 * Identity of this peer.
166 */ 179 */
167static int data_received; 180static struct GNUNET_PeerIdentity my_identity;
168static int data_received_size;
169
170 181
171/** 182/**
172* Transmit a mesh message 183 * Head of DLL of active lines.
173 * @param cls closure, NULL
174 * @param size number of bytes available in buf
175 * @param buf where the callee should write the error message
176 * @return number of bytes written to buf
177 */ 184 */
178static size_t transmit_mesh_message (void *cls, size_t size, void *buf); 185static struct Line *lines_head;
179 186
180/** 187/**
181 * Function called to send a peer no answer message to the client. 188 * Tail of DLL of active lines.
182 * "buf" will be NULL and "size" zero if the socket was closed for writing in
183 * the meantime.
184 *
185 * @param cls closure, NULL
186 * @param size number of bytes available in buf
187 * @param buf where the callee should write the peer no answer message
188 * @return number of bytes written to buf
189 */ 189 */
190static size_t 190static struct Line *lines_tail;
191transmit_server_no_answer_message (void *cls, size_t size, void *buf);
192 191
193/** 192/**
194 * Task to schedule a audio transmission. 193 * Counter for generating local line numbers.
195 * 194 * FIXME: randomize generation in the future
196 * @param cls Closure. 195 * to eliminate information leakage.
197 * @param tc Task Context.
198 */ 196 */
199static void 197static uint32_t local_line_cnt;
200transmit_audio_task (void *cls,
201 const struct GNUNET_SCHEDULER_TaskContext *tc);
202 198
203/**
204* Start the audio helpers
205*/
206int start_helpers (void);
207 199
208/** 200/**
209* Stop the audio helpers 201 * Function to register a phone.
210*/
211void stop_helpers (void);
212
213
214
215/******************************************************************************/
216/*********************** AUXILIARY FUNCTIONS *************************/
217/******************************************************************************/
218
219/**
220* Function which displays some call stats
221*/
222static void
223show_end_data (void)
224{
225 static struct GNUNET_TIME_Absolute end_time;
226 static struct GNUNET_TIME_Relative total_time;
227
228 end_time = GNUNET_TIME_absolute_get ();
229 total_time = GNUNET_TIME_absolute_get_difference (start_time, end_time);
230 FPRINTF (stderr, "\nResults of send\n");
231 FPRINTF (stderr, "Test time %s\n",
232 GNUNET_STRINGS_relative_time_to_string (total_time,
233 GNUNET_NO));
234 FPRINTF (stderr, "Test total packets: %d\n",
235 data_sent);
236 FPRINTF (stderr, "Test bandwidth: %f kb/s\n",
237 data_sent_size * 1000.0 / (total_time.rel_value_us + 1)); // 4bytes * us
238 FPRINTF (stderr, "Test throughput: %f packets/s\n\n",
239 data_sent * 1000000.0 / (total_time.rel_value_us + 1)); // packets * us
240
241 FPRINTF (stderr, "\nResults of recv\n");
242 FPRINTF (stderr, "Test time %s\n",
243 GNUNET_STRINGS_relative_time_to_string (total_time,
244 GNUNET_NO));
245 FPRINTF (stderr, "Test total packets: %d\n",
246 data_received);
247 FPRINTF (stderr, "Test bandwidth: %f kb/s\n",
248 data_received_size * 1000.0 / (total_time.rel_value_us + 1)); // 4bytes * us
249 FPRINTF (stderr, "Test throughput: %f packets/s\n\n",
250 data_received * 1000000.0 / (total_time.rel_value_us + 1)); // packets * us
251}
252
253/**
254* Function which sets the connection state to LISTEN
255*/
256static void
257status_to_listen (void)
258{
259
260 if (CONNECTED == connection.status)
261 {
262 show_end_data ();
263 }
264
265 if (timeout_task != GNUNET_SCHEDULER_NO_TASK)
266 {
267 GNUNET_SCHEDULER_cancel (timeout_task);
268 timeout_task = GNUNET_SCHEDULER_NO_TASK;
269 }
270
271 stop_helpers ();
272
273 connection.status = LISTEN;
274 connection.client = NULL;
275
276 data_sent = 0;
277 data_sent_size = 0;
278 data_received = 0;
279 data_received_size = 0;
280
281 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Changed connection status to %s\n"),
282 "LISTEN");
283}
284
285/**
286* Function to terminate the active call
287*/
288static void
289terminate_call ()
290{
291 size_t msg_size;
292 msg_size = sizeof (struct MeshSessionTerminateMessage);
293 struct MeshSessionTerminateMessage *message_mesh_terminate =
294 (struct MeshSessionTerminateMessage *) GNUNET_malloc (msg_size);
295
296 if (NULL == message_mesh_terminate)
297 {
298 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
299 _("Could not create MeshSessionTerminateMessage\n"));
300 status_to_listen ();
301
302 return;
303 }
304
305 message_mesh_terminate->header.size = htons (msg_size);
306 message_mesh_terminate->header.type =
307 htons (GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_SESSION_TERMINATE);
308
309 if (NULL ==
310 GNUNET_MESH_notify_transmit_ready (tunnel_reliable, 0,
311 MAX_TRANSMIT_DELAY, msg_size,
312 &transmit_mesh_message,
313 (void *) message_mesh_terminate))
314 {
315 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
316 _("Could not queue MeshSessionTerminateMessage\n"));
317 GNUNET_free (message_mesh_terminate);
318 status_to_listen ();
319 }
320}
321
322/**
323* Function to reject a call
324*
325* @param tunnel the tunnel where to reject the incoming call
326* @param reason te reson why the call is rejected
327*/
328static void
329reject_call (struct GNUNET_MESH_Tunnel *tunnel, int reason)
330{
331 size_t msg_size;
332 msg_size = sizeof (struct MeshSessionRejectMessage);
333 struct MeshSessionRejectMessage *message_mesh_reject =
334 (struct MeshSessionRejectMessage *) GNUNET_malloc (msg_size);
335
336 if (NULL == message_mesh_reject)
337 {
338 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
339 _("Could not create MeshSessionRejectMessage\n"));
340 status_to_listen ();
341
342 return;
343 }
344
345 message_mesh_reject->header.size = htons (msg_size);
346 message_mesh_reject->header.type =
347 htons (GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_SESSION_REJECT);
348 message_mesh_reject->reason = htons (reason);
349
350 if (NULL ==
351 GNUNET_MESH_notify_transmit_ready (tunnel_reliable, 0,
352 MAX_TRANSMIT_DELAY, msg_size,
353 &transmit_mesh_message,
354 (void *) message_mesh_reject))
355 {
356 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
357 _("Could not queue MeshSessionRejectMessage\n"));
358 GNUNET_free (message_mesh_reject);
359 status_to_listen ();
360 }
361}
362
363/**
364 * Check for timeout when calling a peer
365 * 202 *
366 * @param cls closure, NULL 203 * @param cls closure, NULL
367 * @param tc the task context (can be NULL) 204 * @param client the client from which the message is
205 * @param message the message from the client
368 */ 206 */
369static void 207static void
370check_timeout (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) 208handle_client_register_message (void *cls,
371{ 209 struct GNUNET_SERVER_Client *client,
372 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Call timeout\n"); 210 const struct GNUNET_MessageHeader *message)
373
374 if (NULL ==
375 GNUNET_SERVER_notify_transmit_ready (connection.client,
376 sizeof (struct
377 ServerClientNoAnswerMessage),
378 MAX_TRANSMIT_DELAY,
379 &transmit_server_no_answer_message,
380 NULL))
381 {
382 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
383 _("Could not queue ServerClientNoAnswerMessage\n"));
384 }
385
386 terminate_call ();
387}
388
389/******************************************************************************/
390/*********************** SENDING FUNCTIONS CL -> SERVER *******************/
391/******************************************************************************/
392
393/**
394 * Function called to send a session initiate message to the client.
395 * "buf" will be NULL and "size" zero if the socket was closed for writing in
396 * the meantime.
397 *
398 * @param cls closure, NULL
399 * @param size number of bytes available in buf
400 * @param buf where the callee should write the initiate message
401 * @return number of bytes written to buf
402 */
403static size_t
404transmit_server_initiate_message (void *cls, size_t size, void *buf)
405{
406 struct ServerClientSessionInitiateMessage *msg;
407 size_t msg_size;
408
409 msg_size = sizeof (struct ServerClientSessionInitiateMessage);
410
411 GNUNET_assert (size >= msg_size);
412
413 msg = (struct ServerClientSessionInitiateMessage *) buf;
414 msg->header.size = htons (msg_size);
415 msg->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_SC_SESSION_INITIATE);
416 memcpy (&(msg->peer), (struct GNUNET_PeerIdentity *) cls,
417 sizeof (struct GNUNET_PeerIdentity));
418
419 return msg_size;
420}
421
422/**
423 * Function called to send a session accept message to the client.
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 accept message
430 * @return number of bytes written to buf
431 */
432static size_t
433transmit_server_accept_message (void *cls, size_t size, void *buf)
434{ 211{
435 struct ServerClientSessionAcceptMessage *msg; 212 const struct ClientPhoneRegisterMessage *msg;
436 size_t msg_size; 213 struct Line *line;
437 214
438 msg_size = sizeof (struct ServerClientSessionAcceptMessage); 215 msg = (struct ClientPhoneRegisterMessage *) message;
439 216 line = GNUNET_SERVER_client_get_user_context (client, struct Line);
440 GNUNET_assert (size >= msg_size); 217 if (NULL != line)
441 218 {
442 msg = (struct ServerClientSessionAcceptMessage *) buf; 219 GNUNET_break (0);
443 msg->header.size = htons (msg_size); 220 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
444 msg->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_SC_SESSION_ACCEPT); 221 return;
445 222 }
446 return msg_size; 223 line = GNUNET_new (struct Line);
224 line->client = client;
225 GNUNET_SERVER_notification_context_add (nc, client);
226 GNUNET_CONTAINER_DLL_insert (lines_head,
227 lines_tail,
228 line);
229 line->local_line = ntohl (msg->line);
230 GNUNET_SERVER_client_set_user_context (client, line);
231 GNUNET_SERVER_receive_done (client, GNUNET_OK);
447} 232}
448 233
449/**
450 * Function called to send a session reject message to the client.
451 * "buf" will be NULL and "size" zero if the socket was closed for writing in
452 * the meantime.
453 *
454 * @param cls closure, NULL
455 * @param size number of bytes available in buf
456 * @param buf where the callee should write the reject message
457 * @return number of bytes written to buf
458 */
459static size_t
460transmit_server_reject_message (void *cls, size_t size, void *buf)
461{
462 struct ServerClientSessionRejectMessage *msg;
463 size_t msg_size;
464
465 msg_size = sizeof (struct ServerClientSessionRejectMessage);
466
467 GNUNET_assert (size >= msg_size);
468
469 msg = (struct ServerClientSessionRejectMessage *) buf;
470 msg->header.size = htons (msg_size);
471 msg->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_SC_SESSION_REJECT);
472
473 if (NULL == cls)
474 {
475 msg->reason = htons (GNUNET_CONVERSATION_REJECT_REASON_NOT_AVAILABLE);
476 }
477 else
478 {
479 msg->reason = ((struct MeshSessionRejectMessage *) cls)->reason;
480 }
481
482 return msg_size;
483}
484 234
485/** 235/**
486 * Function called to send a session terminate message to the client. 236 * Function to handle a pickup request message from the client
487 * "buf" will be NULL and "size" zero if the socket was closed for writing in
488 * the meantime.
489 * 237 *
490 * @param cls closure, NULL 238 * @param cls closure, NULL
491 * @param size number of bytes available in buf 239 * @param client the client from which the message is
492 * @param buf where the callee should write the terminate message 240 * @param message the message from the client
493 * @return number of bytes written to buf
494 */ 241 */
495static size_t 242static void
496transmit_server_terminate_message (void *cls, size_t size, void *buf) 243handle_client_pickup_message (void *cls,
244 struct GNUNET_SERVER_Client *client,
245 const struct GNUNET_MessageHeader *message)
497{ 246{
498 struct ServerClientSessionTerminateMessage *msg; 247 const struct ClientPhonePickupMessage *msg;
499 size_t msg_size; 248 struct GNUNET_MQ_Envelope *e;
500 249 struct MeshPhonePickupMessage *mppm;
501 msg_size = sizeof (struct ServerClientSessionTerminateMessage); 250 const char *meta;
502 251 struct Line *line;
503 GNUNET_assert (size >= msg_size); 252 size_t len;
504 253
505 msg = (struct ServerClientSessionTerminateMessage *) buf; 254 msg = (struct ClientPhonePickupMessage *) message;
506 msg->header.size = htons (msg_size); 255 meta = (const char *) &msg[1];
507 msg->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_SC_SESSION_TERMINATE); 256 len = ntohs (msg->header.size) - sizeof (struct ClientPhonePickupMessage);
508 257 if ( (0 == len) ||
509 return msg_size; 258 ('\0' != meta[len - 1]) )
259 {
260 meta = NULL;
261 len = 0;
262 }
263 line = GNUNET_SERVER_client_get_user_context (client, struct Line);
264 if (NULL == line)
265 {
266 GNUNET_break (0);
267 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
268 return;
269 }
270 switch (line->status)
271 {
272 case LS_CALLEE_LISTEN:
273 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
274 "Ignoring client's PICKUP message, caller has HUNG UP already\n");
275 GNUNET_SERVER_receive_done (client, GNUNET_OK);
276 break;
277 case LS_CALLEE_RINGING:
278 line->status = LS_CALLEE_CONNECTED;
279 break;
280 case LS_CALLEE_CONNECTED:
281 GNUNET_break (0);
282 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
283 return;
284 case LS_CALLEE_SHUTDOWN:
285 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
286 "Ignoring client's PICKUP message, line is in SHUTDOWN\n");
287 GNUNET_SERVER_receive_done (client, GNUNET_OK);
288 break;
289 case LS_CALLER_CALLING:
290 case LS_CALLER_CONNECTED:
291 case LS_CALLER_SHUTDOWN:
292 GNUNET_break (0);
293 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
294 return;
295 }
296 line->status = LS_CALLEE_CONNECTED;
297 e = GNUNET_MQ_msg_extra (mppm,
298 len,
299 GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_PICK_UP);
300 memcpy (&mppm[1], meta, len);
301 GNUNET_MQ_send (line->reliable_mq, e);
302 GNUNET_SERVER_receive_done (client, GNUNET_OK);
510} 303}
511 304
512/**
513 * Function called to send a missed call message to the client.
514 * "buf" will be NULL and "size" zero if the socket was closed for writing in
515 * the meantime.
516 *
517 * @param cls closure, NULL
518 * @param size number of bytes available in buf
519 * @param buf where the callee should write the missed call message
520 * @return number of bytes written to buf
521 */
522static size_t
523transmit_server_missed_call_message (void *cls, size_t size, void *buf)
524{
525 struct ServerClientMissedCallMessage *msg;
526 msg = (struct ServerClientMissedCallMessage *) cls;
527
528 memcpy (buf, msg, size);
529 GNUNET_free (msg);
530
531 return size;
532}
533 305
534/** 306/**
535 * Function called to send a service blocked message to the client. 307 * Destroy the mesh tunnels of a line.
536 * "buf" will be NULL and "size" zero if the socket was closed for writing in
537 * the meantime.
538 * 308 *
539 * @param cls closure, NULL 309 * @param line line to shutdown tunnels of
540 * @param size number of bytes available in buf
541 * @param buf where the callee should write the service blocked message
542 * @return number of bytes written to buf
543 */ 310 */
544static size_t 311static void
545transmit_server_service_blocked_message (void *cls, size_t size, void *buf) 312destroy_line_mesh_tunnels (struct Line *line)
546{ 313{
547 struct ServerClientServiceBlockedMessage *msg; 314 if (NULL != line->reliable_mq)
548 size_t msg_size; 315 {
549 316 GNUNET_MQ_destroy (line->reliable_mq);
550 msg_size = sizeof (struct ServerClientServiceBlockedMessage); 317 line->reliable_mq = NULL;
551 318 }
552 GNUNET_assert (size >= msg_size); 319 if (NULL != line->unreliable_mth)
553 320 {
554 msg = (struct ServerClientServiceBlockedMessage *) buf; 321 GNUNET_MESH_notify_transmit_ready_cancel (line->unreliable_mth);
555 msg->header.size = htons (msg_size); 322 line->unreliable_mth = NULL;
556 msg->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_SC_SERVICE_BLOCKED); 323 }
557 324 if (NULL != line->tunnel_unreliable)
558 return msg_size; 325 {
326 GNUNET_MESH_tunnel_destroy (line->tunnel_unreliable);
327 line->tunnel_unreliable = NULL;
328 }
329 if (NULL != line->tunnel_reliable)
330 {
331 GNUNET_MESH_tunnel_destroy (line->tunnel_reliable);
332 line->tunnel_reliable = NULL;
333 }
559} 334}
560 335
561/**
562 * Function called to send a peer not connected message to the client.
563 * "buf" will be NULL and "size" zero if the socket was closed for writing in
564 * the meantime.
565 *
566 * @param cls closure, NULL
567 * @param size number of bytes available in buf
568 * @param buf where the callee should write the peer not connected message
569 * @return number of bytes written to buf
570 */
571static size_t
572transmit_server_peer_not_connected_message (void *cls, size_t size, void *buf)
573{
574 struct ServerClientPeerNotConnectedMessage *msg;
575 size_t msg_size;
576
577 msg_size = sizeof (struct ServerClientPeerNotConnectedMessage);
578
579 GNUNET_assert (size >= msg_size);
580
581 msg = (struct ServerClientPeerNotConnectedMessage *) buf;
582 msg->header.size = htons (msg_size);
583 msg->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_SC_PEER_NOT_CONNECTED);
584
585 return msg_size;
586}
587 336
588/** 337/**
589 * Function called to send a peer no answer message to the client. 338 * We are done signalling shutdown to the other peer. Close down
590 * "buf" will be NULL and "size" zero if the socket was closed for writing in 339 * (or reset) the line.
591 * the meantime.
592 * 340 *
593 * @param cls closure, NULL 341 * @param cls the `struct Line` to reset/terminate
594 * @param size number of bytes available in buf
595 * @param buf where the callee should write the peer no answer message
596 * @return number of bytes written to buf
597 */ 342 */
598static size_t 343static void
599transmit_server_no_answer_message (void *cls, size_t size, void *buf) 344mq_done_finish_caller_shutdown (void *cls)
600{
601 struct ServerClientNoAnswerMessage *msg;
602 size_t msg_size;
603
604 msg_size = sizeof (struct ServerClientNoAnswerMessage);
605
606 GNUNET_assert (size >= msg_size);
607
608 msg = (struct ServerClientNoAnswerMessage *) buf;
609 msg->header.size = htons (msg_size);
610 msg->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_SC_NO_ANSWER);
611
612 return msg_size;
613}
614
615/**
616 * Function called to send a error message to the client.
617 * "buf" will be NULL and "size" zero if the socket was closed for writing in
618 * the meantime.
619 *
620 * @param cls closure, NULL
621 * @param size number of bytes available in buf
622 * @param buf where the callee should write the error message
623 * @return number of bytes written to buf
624 */
625static size_t
626transmit_server_error_message (void *cls, size_t size, void *buf)
627{ 345{
628 struct ServerClientErrorMessage *msg; 346 struct Line *line = cls;
629 size_t msg_size; 347
630 348 switch (line->status)
631 msg_size = sizeof (struct ServerClientErrorMessage); 349 {
632 350 case LS_CALLEE_LISTEN:
633 GNUNET_assert (size >= msg_size); 351 GNUNET_break (0);
634 352 break;
635 msg = (struct ServerClientErrorMessage *) buf; 353 case LS_CALLEE_RINGING:
636 msg->header.size = htons (msg_size); 354 GNUNET_break (0);
637 msg->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_SC_ERROR); 355 break;
638 356 case LS_CALLEE_CONNECTED:
639 return msg_size; 357 GNUNET_break (0);
358 break;
359 case LS_CALLEE_SHUTDOWN:
360 line->status = LS_CALLEE_LISTEN;
361 destroy_line_mesh_tunnels (line);
362 return;
363 case LS_CALLER_CALLING:
364 line->status = LS_CALLER_SHUTDOWN;
365 break;
366 case LS_CALLER_CONNECTED:
367 line->status = LS_CALLER_SHUTDOWN;
368 break;
369 case LS_CALLER_SHUTDOWN:
370 destroy_line_mesh_tunnels (line);
371 GNUNET_CONTAINER_DLL_remove (lines_head,
372 lines_tail,
373 line);
374 GNUNET_free_non_null (line->audio_data);
375 GNUNET_free (line);
376 break;
377 }
640} 378}
641 379
642/******************************************************************************/
643/*********************** RECEIVE FUNCTIONS CL -> SERVER ********************/
644/******************************************************************************/
645 380
646/** 381/**
647 * Function to handle a session initiate message from the client 382 * Function to handle a hangup request message from the client
648 * 383 *
649 * @param cls closure, NULL 384 * @param cls closure, NULL
650 * @param client the client from which the message is 385 * @param client the client from which the message is
651 * @param message the message from the client 386 * @param message the message from the client
652*/ 387 */
653static void 388static void
654handle_session_initiate_message (void *cls, 389handle_client_hangup_message (void *cls,
655 struct GNUNET_SERVER_Client *client, 390 struct GNUNET_SERVER_Client *client,
656 const struct GNUNET_MessageHeader *message) 391 const struct GNUNET_MessageHeader *message)
657{ 392{
658 static uint32_t port = 50002; 393 const struct ClientPhoneHangupMessage *msg;
659 size_t msg_size; 394 struct GNUNET_MQ_Envelope *e;
660 struct ClientServerSessionInitiateMessage *msg = 395 struct MeshPhoneHangupMessage *mhum;
661 (struct ClientServerSessionInitiateMessage *) message; 396 const char *meta;
662 struct GNUNET_PeerIdentity *peer = &(msg->peer); 397 struct Line *line;
663 398 size_t len;
399
400 msg = (struct ClientPhoneHangupMessage *) message;
401 meta = (const char *) &msg[1];
402 len = ntohs (msg->header.size) - sizeof (struct ClientPhoneHangupMessage);
403 if ( (0 == len) ||
404 ('\0' != meta[len - 1]) )
405 {
406 meta = NULL;
407 len = 0;
408 }
409 line = GNUNET_SERVER_client_get_user_context (client, struct Line);
410 if (NULL == line)
411 {
412 GNUNET_break (0);
413 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
414 return;
415 }
416 switch (line->status)
417 {
418 case LS_CALLEE_LISTEN:
419 GNUNET_break (0);
420 GNUNET_SERVER_receive_done (client, GNUNET_OK);
421 return;
422 case LS_CALLEE_RINGING:
423 line->status = LS_CALLEE_SHUTDOWN;
424 break;
425 case LS_CALLEE_CONNECTED:
426 line->status = LS_CALLEE_SHUTDOWN;
427 break;
428 case LS_CALLEE_SHUTDOWN:
429 GNUNET_break (0);
430 GNUNET_SERVER_receive_done (client, GNUNET_OK);
431 return;
432 case LS_CALLER_CALLING:
433 line->status = LS_CALLER_SHUTDOWN;
434 break;
435 case LS_CALLER_CONNECTED:
436 line->status = LS_CALLER_SHUTDOWN;
437 break;
438 case LS_CALLER_SHUTDOWN:
439 GNUNET_break (0);
440 GNUNET_SERVER_receive_done (client, GNUNET_OK);
441 return;
442 }
443 e = GNUNET_MQ_msg_extra (mhum,
444 len,
445 GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_HANG_UP);
446 memcpy (&mhum[1], meta, len);
447 GNUNET_MQ_notify_sent (e,
448 &mq_done_finish_caller_shutdown,
449 line);
450 GNUNET_MQ_send (line->reliable_mq, e);
664 GNUNET_SERVER_receive_done (client, GNUNET_OK); 451 GNUNET_SERVER_receive_done (client, GNUNET_OK);
665
666 if (NULL != connection.client)
667 {
668 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
669 _("There is already a peer in interaction\n"));
670 GNUNET_SERVER_notify_transmit_ready (client,
671 sizeof (struct
672 ServerClientServiceBlockedMessage),
673 MAX_TRANSMIT_DELAY,
674 &transmit_server_service_blocked_message,
675 NULL);
676
677 return;
678 }
679
680 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Creating tunnel to: %s\n"),
681 GNUNET_i2s_full (peer));
682 tunnel_reliable =
683 GNUNET_MESH_tunnel_create (mesh, NULL, peer, port, GNUNET_NO, GNUNET_NO);
684 if (NULL == tunnel_reliable)
685 {
686 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
687 _("Could not create reliable tunnel\n"));
688 GNUNET_SERVER_notify_transmit_ready (client,
689 sizeof (struct
690 ServerClientPeerNotConnectedMessage),
691 MAX_TRANSMIT_DELAY,
692 &transmit_server_peer_not_connected_message,
693 NULL);
694
695 return;
696 }
697
698 msg_size = sizeof (struct MeshSessionInitiateMessage);
699 struct MeshSessionInitiateMessage *message_mesh_initiate =
700 (struct MeshSessionInitiateMessage *) GNUNET_malloc (msg_size);
701
702 if (NULL == message_mesh_initiate)
703 {
704 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
705 _("Could not create MeshSessionInitiateMessage\n"));
706 GNUNET_MESH_tunnel_destroy (tunnel_reliable);
707 tunnel_reliable = NULL;
708 GNUNET_SERVER_notify_transmit_ready (client,
709 sizeof (struct
710 ServerClientErrorMessage),
711 MAX_TRANSMIT_DELAY,
712 &transmit_server_error_message,
713 NULL);
714
715 return;
716 }
717
718 message_mesh_initiate->header.size = htons (msg_size);
719 message_mesh_initiate->header.type =
720 htons (GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_SESSION_INITIATE);
721
722 if (NULL ==
723 GNUNET_MESH_notify_transmit_ready (tunnel_reliable, 0,
724 MAX_TRANSMIT_DELAY, msg_size,
725 &transmit_mesh_message,
726 (void *) message_mesh_initiate))
727 {
728 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
729 _("Could not queue MeshSessionInitiateMessage\n"));
730 GNUNET_MESH_tunnel_destroy (tunnel_reliable);
731 tunnel_reliable = NULL;
732 GNUNET_free (message_mesh_initiate);
733 GNUNET_SERVER_notify_transmit_ready (client,
734 sizeof (struct
735 ServerClientErrorMessage),
736 MAX_TRANSMIT_DELAY,
737 &transmit_server_error_message,
738 NULL);
739
740 return;
741 }
742
743 connection.status = CALLER;
744 connection.client = client;
745 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Changed connection status to %d\n"),
746 connection.status);
747 memcpy (&(connection.peer), peer, sizeof (struct GNUNET_PeerIdentity));
748
749 return;
750} 452}
751 453
454
752/** 455/**
753 * Function to handle a session accept message from the client 456 * Function to handle call request the client
754 * 457 *
755 * @param cls closure, NULL 458 * @param cls closure, NULL
756 * @param client the client from which the message is 459 * @param client the client from which the message is
757 * @param message the message from the client 460 * @param message the message from the client
758*/ 461 */
759static void 462static void
760handle_session_accept_message (void *cls, struct GNUNET_SERVER_Client *client, 463handle_client_call_message (void *cls,
761 const struct GNUNET_MessageHeader *message) 464 struct GNUNET_SERVER_Client *client,
465 const struct GNUNET_MessageHeader *message)
762{ 466{
763 size_t msg_size; 467 const struct ClientCallMessage *msg;
764 468 struct Line *line;
469 struct GNUNET_MQ_Envelope *e;
470 struct MeshPhoneRingMessage *ring;
471
472 msg = (struct ClientCallMessage *) message;
473 line = GNUNET_SERVER_client_get_user_context (client, struct Line);
474 if (NULL != line)
475 {
476 GNUNET_break (0);
477 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
478 return;
479 }
480 line = GNUNET_new (struct Line);
481 line->target = msg->target;
482 GNUNET_CONTAINER_DLL_insert (lines_head,
483 lines_tail,
484 line);
485 line->remote_line = ntohl (msg->line);
486 line->status = LS_CALLER_CALLING;
487 line->tunnel_reliable = GNUNET_MESH_tunnel_create (mesh,
488 line,
489 &msg->target,
490 GNUNET_APPLICATION_TYPE_CONVERSATION_CONTROL,
491 GNUNET_NO,
492 GNUNET_YES);
493 line->reliable_mq = GNUNET_MESH_mq_create (line->tunnel_reliable);
494 line->local_line = local_line_cnt++;
495 e = GNUNET_MQ_msg (ring, GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_RING);
496 ring->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_CONVERSATION_RING);
497 ring->purpose.size = htonl (sizeof (struct GNUNET_PeerIdentity) * 2 +
498 sizeof (struct GNUNET_TIME_AbsoluteNBO) +
499 sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose) +
500 sizeof (struct GNUNET_CRYPTO_EccPublicSignKey));
501 GNUNET_CRYPTO_ecc_key_get_public_for_signature (&msg->caller_id,
502 &ring->caller_id);
503 ring->remote_line = msg->line;
504 ring->source_line = line->local_line;
505 ring->target = msg->target;
506 ring->source = my_identity;
507 ring->expiration_time = GNUNET_TIME_absolute_hton (GNUNET_TIME_relative_to_absolute (RING_TIMEOUT));
508 GNUNET_CRYPTO_ecc_sign (&msg->caller_id,
509 &ring->purpose,
510 &ring->signature);
511 GNUNET_MQ_send (line->reliable_mq, e);
512 GNUNET_SERVER_client_set_user_context (client, line);
765 GNUNET_SERVER_receive_done (client, GNUNET_OK); 513 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_accept_message called when not allowed\n"));
773 return;
774 }
775
776 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Accepting the call of: %s\n"),
777 GNUNET_i2s_full (&(connection.peer)));
778
779 msg_size = sizeof (struct MeshSessionAcceptMessage);
780 struct MeshSessionAcceptMessage *message_mesh_accept =
781 (struct MeshSessionAcceptMessage *) GNUNET_malloc (msg_size);
782
783 if (NULL == message_mesh_accept)
784 {
785 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
786 _("Could not create MeshSessionAcceptMessage\n"));
787 return;
788 }
789
790 message_mesh_accept->header.size = htons (msg_size);
791 message_mesh_accept->header.type =
792 htons (GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_SESSION_ACCEPT);
793
794 if (NULL ==
795 GNUNET_MESH_notify_transmit_ready (tunnel_reliable, 0,
796 MAX_TRANSMIT_DELAY, msg_size,
797 &transmit_mesh_message,
798 (void *) message_mesh_accept))
799 {
800 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
801 _("Could not queue MeshSessionAcceptMessage\n"));
802 GNUNET_free (message_mesh_accept);
803 return;
804 }
805
806 connection.status = CONNECTED;
807 connection.client = client;
808 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Changed connection status to %d\n"),
809 connection.status);
810
811 return;
812} 514}
813 515
516
814/** 517/**
815 * Function to handle a session reject message from the client 518 * Transmit audio data via unreliable mesh channel.
816 * 519 *
817 * @param cls closure, NULL 520 * @param cls the `struct Line` we are transmitting for
818 * @param client the client from which the message is 521 * @param size number of bytes available in @a buf
819 * @param message the message from the client 522 * @param buf where to copy the data
820*/ 523 * @return number of bytes copied to @buf
821static void 524 */
822handle_session_reject_message (void *cls, struct GNUNET_SERVER_Client *client, 525static size_t
823 const struct GNUNET_MessageHeader *message) 526transmit_line_audio (void *cls,
527 size_t size,
528 void *buf)
824{ 529{
825 struct ClientServerSessionRejectMessage *message_received; 530 struct Line *line = cls;
826 531 struct MeshAudioMessage *mam = buf;
827 GNUNET_SERVER_receive_done (client, GNUNET_OK); 532
828 533 line->unreliable_mth = NULL;
829 if (connection.status != CALLEE) 534 if ( (NULL == buf) ||
535 (size < sizeof (struct MeshAudioMessage) + line->audio_size) )
830 { 536 {
831 // TODO send illegal command 537 /* eh, other error handling? */
832 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 538 return 0;
833 _ 539 }
834 ("handle_session_reject_message called when not allowed\n")); 540 mam->header.size = htons (sizeof (struct MeshAudioMessage) + line->audio_size);
835 return; 541 mam->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_AUDIO);
836 } 542 mam->remote_line = htonl (line->remote_line);
837 543 memcpy (&mam[1], line->audio_data, line->audio_size);
838 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Rejecting the call of: %s\n"), 544 GNUNET_free (line->audio_data);
839 GNUNET_i2s_full (&(connection.peer))); 545 line->audio_data = NULL;
840 message_received = (struct ClientServerSessionRejectMessage *) message; 546 return sizeof (struct MeshAudioMessage) + line->audio_size;
841 reject_call (tunnel_reliable, ntohs (message_received->reason));
842
843 return;
844} 547}
845 548
549
846/** 550/**
847 * Function to handle a session terminate message from the client 551 * Function to handle audio data from the client
848 * 552 *
849 * @param cls closure, NULL 553 * @param cls closure, NULL
850 * @param client the client from which the message is 554 * @param client the client from which the message is
851 * @param message the message from the client 555 * @param message the message from the client
852*/ 556 */
853static void 557static void
854handle_session_terminate_message (void *cls, 558handle_client_audio_message (void *cls,
855 struct GNUNET_SERVER_Client *client, 559 struct GNUNET_SERVER_Client *client,
856 const struct GNUNET_MessageHeader *message) 560 const struct GNUNET_MessageHeader *message)
857{ 561{
562 const struct ClientAudioMessage *msg;
563 struct Line *line;
564 size_t size;
565
566 size = ntohs (message->size) - sizeof (struct ClientAudioMessage);
567 msg = (struct ClientAudioMessage *) message;
568 line = GNUNET_SERVER_client_get_user_context (client, struct Line);
569 if (NULL == line)
570 {
571 GNUNET_break (0);
572 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
573 return;
574 }
575 switch (line->status)
576 {
577 case LS_CALLEE_LISTEN:
578 case LS_CALLEE_RINGING:
579 case LS_CALLER_CALLING:
580 GNUNET_break (0);
581 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
582 return;
583 case LS_CALLEE_CONNECTED:
584 case LS_CALLER_CONNECTED:
585 /* common case, handled below */
586 break;
587 case LS_CALLEE_SHUTDOWN:
588 case LS_CALLER_SHUTDOWN:
589 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
590 "Mesh audio channel in shutdown; audio data dropped\n");
591 GNUNET_SERVER_receive_done (client, GNUNET_OK);
592 return;
593 }
594 if (NULL == line->tunnel_unreliable)
595 {
596 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
597 _("Mesh audio channel not ready; audio data dropped\n"));
598 GNUNET_SERVER_receive_done (client, GNUNET_OK);
599 return;
600 }
601 if (NULL != line->unreliable_mth)
602 {
603 /* NOTE: we may want to not do this and instead combine the data */
604 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
605 "Dropping previous audio data segment with %u bytes\n",
606 line->audio_size);
607 GNUNET_MESH_notify_transmit_ready_cancel (line->unreliable_mth);
608 GNUNET_free (line->audio_data);
609 }
610 line->audio_size = size;
611 line->audio_data = GNUNET_malloc (line->audio_size);
612 memcpy (line->audio_data,
613 &msg[1],
614 size);
615 line->unreliable_mth = GNUNET_MESH_notify_transmit_ready (line->tunnel_unreliable,
616 GNUNET_NO,
617 GNUNET_TIME_UNIT_FOREVER_REL,
618 sizeof (struct MeshAudioMessage)
619 + line->audio_size,
620 &transmit_line_audio,
621 line);
858 GNUNET_SERVER_receive_done (client, GNUNET_OK); 622 GNUNET_SERVER_receive_done (client, GNUNET_OK);
859
860 if (connection.client == NULL || connection.status == CALLEE)
861 {
862 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
863 _
864 ("handle_session_terminate_message called when not allowed\n"));
865 return;
866 }
867
868 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Terminating the call with: %s\n"),
869 GNUNET_i2s_full (&(connection.peer)));
870 terminate_call ();
871} 623}
872 624
873/******************************************************************************/
874/*********************** SENDING FUNCTIONS MESH *******************/
875/******************************************************************************/
876 625
877/** 626/**
878* Transmit a mesh message 627 * We are done signalling shutdown to the other peer.
879 * @param cls closure, NULL 628 * Destroy the tunnel.
880 * @param size number of bytes available in buf 629 *
881 * @param buf where the callee should write the message 630 * @param cls the `struct GNUNET_MESH_tunnel` to destroy
882 * @return number of bytes written to buf
883 */
884static size_t
885transmit_mesh_message (void *cls, size_t size, void *buf)
886{
887 struct VoIPMeshMessageHeader *msg_header =
888 (struct VoIPMeshMessageHeader *) cls;
889
890 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Transmitting message over mesh\n"));
891
892 memcpy (buf, cls, size);
893 // Check if this is correct
894
895
896 if ((GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_SESSION_TERMINATE ==
897 ntohs (msg_header->header.type))
898 || (GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_SESSION_REJECT ==
899 ntohs (msg_header->header.type)))
900 {
901 status_to_listen ();
902 }
903 else if (GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_SESSION_INITIATE ==
904 ntohs (msg_header->header.type))
905 {
906 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Starting timeout task.\n"));
907 timeout_task =
908 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
909 (GNUNET_TIME_UNIT_SECONDS, 30),
910 &check_timeout, NULL);
911 }
912
913 GNUNET_free (cls);
914
915 return size;
916}
917
918/**
919* Transmit a audo message over mesh
920 * @param cls closure, NULL
921 * @param size number of bytes available in buf
922 * @param buf where the callee should write the message
923 * @return number of bytes written to buf
924 */
925static size_t
926transmit_mesh_audio_message (void *cls, size_t size, void *buf)
927{
928 struct AudioMessage *message = (struct AudioMessage *) cls;
929
930 if (size < sizeof (struct AudioMessage) || NULL == buf)
931 {
932 GNUNET_break (0);
933 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
934 "size %u, buf %p, data_sent %u, data_received %u\n",
935 size, buf, data_sent, data_received);
936 return 0;
937 }
938
939 memcpy (buf, message, size);
940
941 data_sent++;
942 data_sent_size += size;
943
944 GNUNET_log (GNUNET_ERROR_TYPE_INFO, " Sent packet %d\n", data_sent);
945
946 audio_task = GNUNET_SCHEDULER_add_now (&transmit_audio_task, NULL);
947
948 return size;
949}
950
951/**
952 * Task to schedule a audio transmission.
953 *
954 * @param cls Closure.
955 * @param tc Task Context.
956 */ 631 */
957static void 632static void
958transmit_audio_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) 633mq_done_destroy_tunnel (void *cls)
959{ 634{
960 struct GNUNET_CONTAINER_SList_Iterator iterator; 635 struct GNUNET_MESH_Tunnel *tunnel = cls;
961 struct AudioMessage *msg; 636
962 int ab_length = GNUNET_CONTAINER_slist_count (audio_buffer); 637 GNUNET_MESH_tunnel_destroy (tunnel);
963
964 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "We have %d packets.\n", ab_length);
965
966 if (NULL == cls)
967 {
968 if (0 == ab_length && CONNECTED == connection.status)
969 {
970 audio_task =
971 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
972 (GNUNET_TIME_UNIT_MILLISECONDS, 10),
973 &transmit_audio_task, NULL);
974 return;
975 }
976
977 iterator = GNUNET_CONTAINER_slist_begin (audio_buffer);
978 msg =
979 (struct AudioMessage *) GNUNET_CONTAINER_slist_get (&iterator, NULL);
980 GNUNET_CONTAINER_slist_erase (&iterator);
981 GNUNET_CONTAINER_slist_iter_destroy (&iterator);
982 }
983 else
984 {
985 msg = (struct AudioMessage *) cls;
986 }
987
988 if (NULL == tunnel_unreliable)
989 {
990 GNUNET_CONTAINER_slist_clear (audio_buffer);
991 return;
992 }
993
994 mth = GNUNET_MESH_notify_transmit_ready (tunnel_unreliable, GNUNET_NO,
995 MAX_TRANSMIT_DELAY,
996 sizeof (struct AudioMessage),
997 &transmit_mesh_audio_message,
998 (void *) msg);
999
1000 if (NULL == mth)
1001 {
1002 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1003 "Need to retransmit audio packet\n");
1004 GNUNET_log (GNUNET_ERROR_TYPE_INFO, " in 1 ms\n");
1005 audio_task =
1006 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MILLISECONDS,
1007 &transmit_audio_task, (void *) msg);
1008 }
1009} 638}
1010 639
1011/******************************************************************************/
1012/*********************** RECEIVE FUNCTIONS MESH ********************/
1013/******************************************************************************/
1014 640
1015/** 641/**
1016* Function to handle a initiation messaage incoming over mesh 642 * Function to handle a ring message incoming over mesh
643 *
1017 * @param cls closure, NULL 644 * @param cls closure, NULL
1018 * @param tunnel the tunnel over which the message arrived 645 * @param tunnel the tunnel over which the message arrived
1019 * @param tunnel_ctx the tunnel context, can be NULL 646 * @param tunnel_ctx the tunnel context, can be NULL
1020 * @param message the incoming message 647 * @param message the incoming message
1021 * 648 * @return #GNUNET_OK
1022 * @return GNUNET_OK 649 */
1023*/ 650static int
1024int 651handle_mesh_ring_message (void *cls,
1025handle_mesh_initiate_message (void *cls, struct GNUNET_MESH_Tunnel *tunnel, 652 struct GNUNET_MESH_Tunnel *tunnel,
1026 void **tunnel_ctx, 653 void **tunnel_ctx,
1027 const struct GNUNET_MessageHeader *message) 654 const struct GNUNET_MessageHeader *message)
1028{ 655{
1029 int reject_reason; 656 const struct MeshPhoneRingMessage *msg;
1030 //struct GNUNET_PeerIdentity *peer = (GNUNET_MESH_tunnel_get_info(tunnel, GNUNET_MESH_OPTION_PEER))->peer; 657 struct Line *line;
1031 const struct GNUNET_PeerIdentity *peer = 658 struct GNUNET_MQ_Envelope *e;
1032 GNUNET_PEER_resolve2 (tunnel->peer); 659 struct MeshPhoneBusyMessage *busy;
1033 660 struct ClientPhoneRingMessage cring;
1034 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 661
1035 _("Handling MeshSessionInitiateMessage from peer: %s\n"), 662 msg = (const struct MeshPhoneRingMessage *) message;
1036 GNUNET_i2s_full (peer)); 663 if ( (msg->purpose.size != htonl (sizeof (struct GNUNET_PeerIdentity) * 2 +
664 sizeof (struct GNUNET_TIME_AbsoluteNBO) +
665 sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose) +
666 sizeof (struct GNUNET_CRYPTO_EccPublicSignKey))) ||
667 (GNUNET_OK !=
668 GNUNET_CRYPTO_ecc_verify (GNUNET_SIGNATURE_PURPOSE_CONVERSATION_RING,
669 &msg->purpose,
670 &msg->signature,
671 &msg->caller_id)) )
672 {
673 GNUNET_break_op (0);
674 return GNUNET_SYSERR;
675 }
676 for (line = lines_head; NULL != line; line = line->next)
677 if ( (line->local_line == ntohl (msg->remote_line)) &&
678 (LS_CALLEE_LISTEN == line->status) )
679 break;
680 if (NULL == line)
681 {
682 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
683 _("No available phone for incoming call on line %u, sending BUSY signal\n"),
684 ntohl (msg->remote_line));
685 e = GNUNET_MQ_msg (busy, GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_BUSY);
686 GNUNET_MQ_notify_sent (e,
687 &mq_done_destroy_tunnel,
688 tunnel);
689 GNUNET_MQ_send (line->reliable_mq, e);
690 GNUNET_MESH_receive_done (tunnel); /* needed? */
691 return GNUNET_OK;
692 }
693 line->status = LS_CALLEE_RINGING;
694 line->remote_line = ntohl (msg->source_line);
695 line->tunnel_reliable = tunnel;
696 line->reliable_mq = GNUNET_MESH_mq_create (line->tunnel_reliable);
697 *tunnel_ctx = line;
698 cring.header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_RING);
699 cring.header.size = htons (sizeof (cring));
700 cring.reserved = htonl (0);
701 cring.caller_id = msg->caller_id;
702 GNUNET_SERVER_notification_context_unicast (nc,
703 line->client,
704 &cring.header,
705 GNUNET_NO);
1037 GNUNET_MESH_receive_done (tunnel); 706 GNUNET_MESH_receive_done (tunnel);
1038
1039 if (LISTEN != connection.status
1040 || 1 > GNUNET_CONTAINER_slist_count (clients))
1041 {
1042
1043 if (CONNECTED == connection.status)
1044 {
1045 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1046 _
1047 ("Rejected call from %s because there is an active call"),
1048 GNUNET_i2s_full (peer));
1049 reject_reason = htons (GNUNET_CONVERSATION_REJECT_REASON_ACTIVE_CALL);
1050
1051 // Notifying client about missed call
1052 size_t msg_size =
1053 sizeof (struct ServerClientMissedCallMessage) +
1054 sizeof (struct MissedCall);
1055 struct ServerClientMissedCallMessage *message =
1056 GNUNET_malloc (msg_size);
1057
1058 message->header.size = htons (msg_size);
1059 message->header.type =
1060 htons (GNUNET_MESSAGE_TYPE_CONVERSATION_SC_MISSED_CALL);
1061 message->number = 1;
1062
1063 memcpy (&(message->missed_call->peer), peer,
1064 sizeof (struct GNUNET_PeerIdentity));
1065 message->missed_call->time = GNUNET_TIME_absolute_get ();
1066
1067 if (NULL ==
1068 GNUNET_SERVER_notify_transmit_ready (connection.client,
1069 sizeof (struct
1070 ServerClientMissedCallMessage),
1071 MAX_TRANSMIT_DELAY,
1072 &transmit_server_missed_call_message,
1073 (void *) message))
1074 {
1075 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1076 _
1077 ("Could not queue ServerClientMissedCallMessage\n"));
1078 GNUNET_free (message);
1079 }
1080 }
1081
1082 if (1 > GNUNET_CONTAINER_slist_count (clients))
1083 {
1084 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1085 _("Got a call from %s while no client connected.\n"),
1086 GNUNET_i2s_full (peer));
1087 reject_reason = htons (GNUNET_CONVERSATION_REJECT_REASON_NO_CLIENT);
1088 // Store missed calls
1089 struct MissedCall call;
1090 memcpy (&(call.peer), peer, sizeof (struct GNUNET_PeerIdentity));
1091 call.time = GNUNET_TIME_absolute_get ();
1092 GNUNET_CONTAINER_slist_add_end (missed_calls,
1093 GNUNET_CONTAINER_SLIST_DISPOSITION_TRANSIENT,
1094 &call, sizeof (struct MissedCall));
1095
1096 }
1097
1098 reject_call (tunnel, reject_reason);
1099 }
1100 else
1101 {
1102 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Initiated call from: %s\n"),
1103 GNUNET_i2s_full (peer));
1104 tunnel_reliable = tunnel;
1105 connection.status = CALLEE;
1106 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1107 _("Changed connection status to %d\n"), connection.status);
1108 memcpy (&(connection.peer), peer, sizeof (struct GNUNET_PeerIdentity));
1109
1110 struct GNUNET_CONTAINER_SList_Iterator iterator =
1111 GNUNET_CONTAINER_slist_begin (clients);
1112 do
1113 {
1114 struct VoipClient *conversation_client =
1115 (struct VoipClient *) GNUNET_CONTAINER_slist_get (&iterator,
1116 NULL);
1117 struct GNUNET_SERVER_Client *client = conversation_client->client;
1118 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Client found: %p\n"),
1119 client);
1120
1121 if (NULL ==
1122 GNUNET_SERVER_notify_transmit_ready (client,
1123 sizeof (struct
1124 ServerClientSessionInitiateMessage),
1125 MAX_TRANSMIT_DELAY,
1126 &transmit_server_initiate_message,
1127 (void *) peer))
1128 {
1129 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1130 _
1131 ("Could not queue ServerClientSessionInitiateMessage\n"));
1132 }
1133
1134 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Client notified.\n"));
1135 }
1136 while (GNUNET_OK == GNUNET_CONTAINER_slist_next (&iterator));
1137
1138 GNUNET_CONTAINER_slist_iter_destroy (&iterator);
1139
1140 }
1141
1142 return GNUNET_OK; 707 return GNUNET_OK;
1143} 708}
1144 709
710
1145/** 711/**
1146* Function to handle an accept messaage incoming over mesh 712 * Function to handle a hangup message incoming over mesh
713 *
1147 * @param cls closure, NULL 714 * @param cls closure, NULL
1148 * @param tunnel the tunnel over which the message arrived 715 * @param tunnel the tunnel over which the message arrived
1149 * @param tunnel_ctx the tunnel context, can be NULL 716 * @param tunnel_ctx the tunnel context, can be NULL
1150 * @param message the incoming message 717 * @param message the incoming message
1151 * 718 * @return #GNUNET_OK
1152 * @return GNUNET_OK 719 */
1153*/ 720static int
1154int 721handle_mesh_hangup_message (void *cls,
1155handle_mesh_accept_message (void *cls, struct GNUNET_MESH_Tunnel *tunnel, 722 struct GNUNET_MESH_Tunnel *tunnel,
1156 void **tunnel_ctx, 723 void **tunnel_ctx,
1157 const struct GNUNET_MessageHeader *message) 724 const struct GNUNET_MessageHeader *message)
1158{ 725{
1159 static uint32_t port = 50003; 726 struct Line *line = *tunnel_ctx;
1160 //struct GNUNET_PeerIdentity *peer = (GNUNET_MESH_tunnel_get_info(tunnel, GNUNET_MESH_OPTION_PEER))->peer; 727 const struct MeshPhoneHangupMessage *msg;
1161 const struct GNUNET_PeerIdentity *peer = 728 const char *reason;
1162 GNUNET_PEER_resolve2 (tunnel->peer); 729 size_t len = ntohs (message->size) - sizeof (struct MeshPhoneHangupMessage);
1163 730 char buf[len + sizeof (struct ClientPhoneHangupMessage)];
1164 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 731 struct ClientPhoneHangupMessage *hup;
1165 _ 732
1166 ("Handling MeshSessionAccpetMessage from peer: %s (connection.peer: %s)\n"), 733 msg = (const struct MeshPhoneHangupMessage *) message;
1167 GNUNET_i2s_full (peer), GNUNET_i2s_full (&(connection.peer))); 734 len = ntohs (msg->header.size) - sizeof (struct MeshPhoneHangupMessage);
735 reason = (const char *) &msg[1];
736 if ( (0 == len) ||
737 ('\0' != reason[len - 1]) )
738 {
739 reason = NULL;
740 len = 0;
741 }
742 if (NULL == line)
743 {
744 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
745 "HANGUP message received for non-existing line, dropping tunnel.\n");
746 return GNUNET_SYSERR;
747 }
748 *tunnel_ctx = NULL;
749 switch (line->status)
750 {
751 case LS_CALLEE_LISTEN:
752 GNUNET_break (0);
753 return GNUNET_SYSERR;
754 case LS_CALLEE_RINGING:
755 line->status = LS_CALLEE_LISTEN;
756 destroy_line_mesh_tunnels (line);
757 break;
758 case LS_CALLEE_CONNECTED:
759 line->status = LS_CALLEE_LISTEN;
760 destroy_line_mesh_tunnels (line);
761 break;
762 case LS_CALLEE_SHUTDOWN:
763 line->status = LS_CALLEE_LISTEN;
764 destroy_line_mesh_tunnels (line);
765 return GNUNET_OK;
766 case LS_CALLER_CALLING:
767 line->status = LS_CALLER_SHUTDOWN;
768 mq_done_finish_caller_shutdown (line);
769 break;
770 case LS_CALLER_CONNECTED:
771 line->status = LS_CALLER_SHUTDOWN;
772 mq_done_finish_caller_shutdown (line);
773 break;
774 case LS_CALLER_SHUTDOWN:
775 mq_done_finish_caller_shutdown (line);
776 return GNUNET_OK;
777 }
778 hup = (struct ClientPhoneHangupMessage *) buf;
779 hup->header.size = sizeof (buf);
780 hup->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP);
781 memcpy (&hup[1], reason, len);
782 GNUNET_SERVER_notification_context_unicast (nc,
783 line->client,
784 &hup->header,
785 GNUNET_NO);
1168 GNUNET_MESH_receive_done (tunnel); 786 GNUNET_MESH_receive_done (tunnel);
1169
1170 if (0 ==
1171 memcmp (peer, &(connection.peer), sizeof (struct GNUNET_PeerIdentity))
1172 && connection.status == CALLER)
1173 {
1174 tunnel_unreliable =
1175 GNUNET_MESH_tunnel_create (mesh, NULL, peer, port, GNUNET_NO,
1176 GNUNET_NO);
1177 if (NULL == tunnel_unreliable)
1178 {
1179 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1180 _("Could not create unreliable tunnel\n"));
1181
1182 status_to_listen ();
1183
1184 GNUNET_SERVER_notify_transmit_ready (connection.client,
1185 sizeof (struct
1186 ServerClientSessionRejectMessage),
1187 MAX_TRANSMIT_DELAY,
1188 &transmit_server_reject_message,
1189 NULL);
1190 return GNUNET_SYSERR;
1191 }
1192
1193 if (timeout_task != GNUNET_SCHEDULER_NO_TASK)
1194 {
1195 GNUNET_SCHEDULER_cancel (timeout_task);
1196 timeout_task = GNUNET_SCHEDULER_NO_TASK;
1197 }
1198
1199 connection.status = CONNECTED;
1200 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1201 _("Changed connection status to %d\n"), connection.status);
1202
1203 if (NULL ==
1204 GNUNET_SERVER_notify_transmit_ready (connection.client,
1205 sizeof (struct
1206 ServerClientSessionAcceptMessage),
1207 MAX_TRANSMIT_DELAY,
1208 &transmit_server_accept_message,
1209 (void *) message))
1210 {
1211 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1212 _
1213 ("Could not queue ServerClientSessionAcceptMessage\n"));
1214 return GNUNET_SYSERR;
1215 }
1216
1217 start_time = GNUNET_TIME_absolute_get ();
1218 start_helpers ();
1219 audio_task = GNUNET_SCHEDULER_add_now (&transmit_audio_task, NULL);
1220 }
1221
1222 return GNUNET_OK; 787 return GNUNET_OK;
1223} 788}
1224 789
790
1225/** 791/**
1226* Function to handle a reject messaage incoming over mesh 792 * Function to handle a pickup message incoming over mesh
793 *
1227 * @param cls closure, NULL 794 * @param cls closure, NULL
1228 * @param tunnel the tunnel over which the message arrived 795 * @param tunnel the tunnel over which the message arrived
1229 * @param tunnel_ctx the tunnel context, can be NULL 796 * @param tunnel_ctx the tunnel context, can be NULL
1230 * @param message the incoming message 797 * @param message the incoming message
1231 * 798 * @return #GNUNET_OK
1232 * @return GNUNET_OK 799 */
1233*/ 800static int
1234int 801handle_mesh_pickup_message (void *cls,
1235handle_mesh_reject_message (void *cls, struct GNUNET_MESH_Tunnel *tunnel, 802 struct GNUNET_MESH_Tunnel *tunnel,
1236 void **tunnel_ctx, 803 void **tunnel_ctx,
1237 const struct GNUNET_MessageHeader *message) 804 const struct GNUNET_MessageHeader *message)
1238{ 805{
1239 //struct GNUNET_PeerIdentity *peer = (GNUNET_MESH_tunnel_get_info(tunnel, GNUNET_MESH_OPTION_PEER))->peer; 806 const struct MeshPhonePickupMessage *msg;
1240 const struct GNUNET_PeerIdentity *peer = 807 struct Line *line = *tunnel_ctx;
1241 GNUNET_PEER_resolve2 (tunnel->peer); 808 const char *metadata;
1242 809 size_t len = ntohs (message->size) - sizeof (struct MeshPhonePickupMessage);
1243 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 810 char buf[len + sizeof (struct ClientPhonePickupMessage)];
1244 _ 811 struct ClientPhonePickupMessage *pick;
1245 ("Handling MeshSessionRejectMessage from peer: %s (connection.peer: %s)\n"), 812
1246 GNUNET_i2s_full (peer), GNUNET_i2s_full (&(connection.peer))); 813 msg = (const struct MeshPhonePickupMessage *) message;
814 len = ntohs (msg->header.size) - sizeof (struct MeshPhonePickupMessage);
815 metadata = (const char *) &msg[1];
816 if ( (0 == len) ||
817 ('\0' != metadata[len - 1]) )
818 {
819 metadata = NULL;
820 len = 0;
821 }
822 if (NULL == line)
823 {
824 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
825 "PICKUP message received for non-existing line, dropping tunnel.\n");
826 return GNUNET_SYSERR;
827 }
1247 GNUNET_MESH_receive_done (tunnel); 828 GNUNET_MESH_receive_done (tunnel);
1248 829 switch (line->status)
1249 if (0 == 830 {
1250 memcmp (peer, &(connection.peer), sizeof (struct GNUNET_PeerIdentity)) 831 case LS_CALLEE_LISTEN:
1251 && connection.status == CALLER) 832 GNUNET_break (0);
1252 { 833 return GNUNET_SYSERR;
1253 if (NULL == 834 case LS_CALLEE_RINGING:
1254 GNUNET_SERVER_notify_transmit_ready (connection.client, 835 case LS_CALLEE_CONNECTED:
1255 sizeof (struct 836 GNUNET_break_op (0);
1256 ServerClientSessionRejectMessage), 837 destroy_line_mesh_tunnels (line);
1257 MAX_TRANSMIT_DELAY, 838 line->status = LS_CALLEE_LISTEN;
1258 &transmit_server_reject_message, 839 return GNUNET_SYSERR;
1259 (void *) message)) 840 case LS_CALLEE_SHUTDOWN:
1260 { 841 GNUNET_break_op (0);
1261 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 842 line->status = LS_CALLEE_LISTEN;
1262 _ 843 destroy_line_mesh_tunnels (line);
1263 ("Could not queue ServerClientSessionRejectMessage\n")); 844 break;
1264 } 845 case LS_CALLER_CALLING:
1265 846 line->status = LS_CALLER_CONNECTED;
1266 status_to_listen (); 847 break;
1267 848 case LS_CALLER_CONNECTED:
1268 if (NULL != tunnel_reliable) 849 GNUNET_break_op (0);
1269 { 850 return GNUNET_OK;
1270 GNUNET_MESH_tunnel_destroy (tunnel_reliable); 851 case LS_CALLER_SHUTDOWN:
1271 tunnel_reliable = NULL; 852 GNUNET_break_op (0);
1272 } 853 mq_done_finish_caller_shutdown (line);
1273 } 854 return GNUNET_SYSERR;
1274 855 }
856 pick = (struct ClientPhonePickupMessage *) buf;
857 pick->header.size = sizeof (buf);
858 pick->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_PICK_UP);
859 memcpy (&pick[1], metadata, len);
860 GNUNET_SERVER_notification_context_unicast (nc,
861 line->client,
862 &pick->header,
863 GNUNET_NO);
864 line->tunnel_unreliable = GNUNET_MESH_tunnel_create (mesh,
865 line,
866 &line->target,
867 GNUNET_APPLICATION_TYPE_CONVERSATION_AUDIO,
868 GNUNET_YES,
869 GNUNET_NO);
1275 return GNUNET_OK; 870 return GNUNET_OK;
1276} 871}
1277 872
873
1278/** 874/**
1279* Function to handle a terminate messaage incoming over mesh 875 * Function to handle a busy message incoming over mesh
876 *
1280 * @param cls closure, NULL 877 * @param cls closure, NULL
1281 * @param tunnel the tunnel over which the message arrived 878 * @param tunnel the tunnel over which the message arrived
1282 * @param tunnel_ctx the tunnel context, can be NULL 879 * @param tunnel_ctx the tunnel context, can be NULL
1283 * @param message the incoming message 880 * @param message the incoming message
1284 * 881 * @return #GNUNET_OK
1285 * @return GNUNET_OK 882 */
1286*/ 883static int
1287int 884handle_mesh_busy_message (void *cls,
1288handle_mesh_terminate_message (void *cls, struct GNUNET_MESH_Tunnel *tunnel, 885 struct GNUNET_MESH_Tunnel *tunnel,
1289 void **tunnel_ctx, 886 void **tunnel_ctx,
1290 const struct GNUNET_MessageHeader *message) 887 const struct GNUNET_MessageHeader *message)
1291{ 888{
1292 //struct GNUNET_PeerIdentity *peer = (GNUNET_MESH_tunnel_get_info(tunnel, GNUNET_MESH_OPTION_PEER))->peer; 889 struct Line *line = *tunnel_ctx;
1293 const struct GNUNET_PeerIdentity *peer = 890 struct ClientPhoneBusyMessage busy;
1294 GNUNET_PEER_resolve2 (tunnel->peer); 891
1295 892 if (NULL == line)
1296 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 893 {
1297 _ 894 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1298 ("Handling MeshSessionTerminateMessage from peer: %s (connection.peer: %s)\n"), 895 "HANGUP message received for non-existing line, dropping tunnel.\n");
1299 GNUNET_i2s_full (peer), GNUNET_i2s_full (&(connection.peer))); 896 return GNUNET_SYSERR;
897 }
898 busy.header.size = sizeof (busy);
899 busy.header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_BUSY);
900 GNUNET_SERVER_notification_context_unicast (nc,
901 line->client,
902 &busy.header,
903 GNUNET_NO);
1300 GNUNET_MESH_receive_done (tunnel); 904 GNUNET_MESH_receive_done (tunnel);
1301 905 *tunnel_ctx = NULL;
1302 if (!memcmp (peer, &(connection.peer), sizeof (struct GNUNET_PeerIdentity)) 906 switch (line->status)
1303 && (connection.status == CONNECTED || connection.status == CALLEE)) 907 {
1304 { 908 case LS_CALLEE_LISTEN:
1305 status_to_listen (); 909 GNUNET_break (0);
1306 910 return GNUNET_SYSERR;
1307 if (NULL != tunnel_unreliable) 911 case LS_CALLEE_RINGING:
1308 { 912 GNUNET_break_op (0);
1309 GNUNET_MESH_tunnel_destroy (tunnel_unreliable); 913 break;
1310 tunnel_unreliable = NULL; 914 case LS_CALLEE_CONNECTED:
1311 } 915 GNUNET_break_op (0);
1312 916 break;
1313 if (NULL != tunnel_reliable) 917 case LS_CALLEE_SHUTDOWN:
1314 { 918 GNUNET_break_op (0);
1315 GNUNET_MESH_tunnel_destroy (tunnel_reliable); 919 break;
1316 tunnel_reliable = NULL; 920 case LS_CALLER_CALLING:
1317 } 921 line->status = LS_CALLER_SHUTDOWN;
1318 } 922 mq_done_finish_caller_shutdown (line);
1319 923 break;
924 case LS_CALLER_CONNECTED:
925 line->status = LS_CALLER_SHUTDOWN;
926 mq_done_finish_caller_shutdown (line);
927 break;
928 case LS_CALLER_SHUTDOWN:
929 mq_done_finish_caller_shutdown (line);
930 break;
931 }
1320 return GNUNET_OK; 932 return GNUNET_OK;
1321} 933}
1322 934
935
1323/** 936/**
1324* Function to handle a audio messaage incoming over mesh 937 * Function to handle an audio message incoming over mesh
938 *
1325 * @param cls closure, NULL 939 * @param cls closure, NULL
1326 * @param tunnel the tunnel over which the message arrived 940 * @param tunnel the tunnel over which the message arrived
1327 * @param tunnel_ctx the tunnel context, can be NULL 941 * @param tunnel_ctx the tunnel context, can be NULL
1328 * @param message the incoming message 942 * @param message the incoming message
1329 * 943 * @return #GNUNET_OK
1330 * @return GNUNET_OK 944 */
1331*/
1332int
1333handle_mesh_audio_message (void *cls, struct GNUNET_MESH_Tunnel *tunnel,
1334 void **tunnel_ctx,
1335 const struct GNUNET_MessageHeader *message)
1336{
1337 const struct AudioMessage *audio;
1338 audio = (const struct AudioMessage *) message;
1339
1340 GNUNET_MESH_receive_done (tunnel);
1341 if (CONNECTED != connection.status)
1342 return GNUNET_OK;
1343 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1344 "[RECV] %dbytes\n",
1345 ntohs (audio->header.size));
1346 if (NULL == playback_helper)
1347 return GNUNET_OK;
1348 (void) GNUNET_HELPER_send (playback_helper,
1349 message, GNUNET_YES, NULL, NULL);
1350 return GNUNET_OK;
1351}
1352
1353/******************************************************************************/
1354/*********************** HELPER *******************/
1355/******************************************************************************/
1356
1357/**
1358* Function to process the audio from the record helper
1359 * @param cls closure, NULL
1360 * @param client NULL
1361 * @param msg the message from the helper
1362 *
1363 * @return GNUNET_OK
1364*/
1365static int 945static int
1366process_record_messages (void *cls GNUNET_UNUSED, void *client, 946handle_mesh_audio_message (void *cls,
1367 const struct GNUNET_MessageHeader *msg) 947 struct GNUNET_MESH_Tunnel *tunnel,
948 void **tunnel_ctx,
949 const struct GNUNET_MessageHeader *message)
1368{ 950{
1369 const struct AudioMessage *message = (const struct AudioMessage *) msg; 951 const struct MeshAudioMessage *msg;
1370 952 struct Line *line = *tunnel_ctx;
1371 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 953 struct GNUNET_PeerIdentity sender;
1372 " [REC] %dbyte\n", 954 size_t msize = ntohs (message->size) - sizeof (struct MeshAudioMessage);
1373 ntohs (message->header.size)); 955 char buf[msize + sizeof (struct ClientAudioMessage)];
1374 GNUNET_CONTAINER_slist_add_end (audio_buffer, 956 struct ClientAudioMessage *cam;
1375 GNUNET_CONTAINER_SLIST_DISPOSITION_TRANSIENT, 957
1376 message, ntohs (message->header.size)); 958 msg = (const struct MeshAudioMessage *) message;
1377 959 if (NULL == line)
1378 return GNUNET_OK; 960 {
1379} 961 sender = *GNUNET_MESH_tunnel_get_info (tunnel,
1380 962 GNUNET_MESH_OPTION_PEER)->peer;
1381/** 963 for (line = lines_head; NULL != line; line = line->next)
1382* Function to to start the playback helper 964 if ( (line->local_line == ntohl (msg->remote_line)) &&
1383 * 965 (LS_CALLEE_CONNECTED == line->status) &&
1384 * @return 0 ok, 1 on error 966 (0 == memcmp (&line->target,
1385*/ 967 &sender,
1386int 968 sizeof (struct GNUNET_PeerIdentity))) &&
1387start_playback_helper (void) 969 (NULL == line->tunnel_unreliable) )
1388{ 970 break;
1389 static char *playback_helper_argv[1]; 971 if (NULL == line)
1390 int success = 1;
1391
1392 playback_helper_argv[0] = "gnunet-helper-audio-playback";
1393 playback_helper = GNUNET_HELPER_start (GNUNET_NO,
1394 "gnunet-helper-audio-playback",
1395 playback_helper_argv,
1396 NULL, NULL, NULL);
1397
1398 if (NULL == playback_helper)
1399 {
1400 success = 0;
1401 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1402 _("Could not start playback audio helper.\n"));
1403 }
1404
1405 return success;
1406}
1407
1408/**
1409* Function to to start the record helper
1410 *
1411 * @return 0 ok, 1 on error
1412*/
1413int
1414start_record_helper (void)
1415{
1416 static char *record_helper_argv[1];
1417 int success = 1;
1418
1419 record_helper_argv[0] = "gnunet-helper-audio-record";
1420 record_helper = GNUNET_HELPER_start (GNUNET_NO,
1421 "gnunet-helper-audio-record",
1422 record_helper_argv,
1423 &process_record_messages, NULL, NULL);
1424
1425 if (NULL == record_helper)
1426 {
1427 success = 0;
1428 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1429 _("Could not start record audio helper\n"));
1430 }
1431
1432 return success;
1433}
1434
1435
1436/**
1437* Function to to start both helpers
1438 *
1439 * @return 0 ok, 1 on error
1440*/
1441int
1442start_helpers (void)
1443{
1444
1445 if (0 == start_playback_helper () || 0 == start_record_helper ())
1446 {
1447 stop_helpers ();
1448 return 0;
1449 }
1450
1451 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Started helpers\n"));
1452
1453 return 1;
1454}
1455
1456/**
1457* Function to to stop the playback helper
1458*/
1459void
1460stop_playback_helper (void)
1461{
1462 if (NULL != playback_helper)
1463 {
1464 GNUNET_HELPER_stop (playback_helper, GNUNET_NO);
1465 playback_helper = NULL;
1466
1467 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Stopped playback helper\n"));
1468 }
1469}
1470
1471/**
1472* Function to to stop the record helper
1473*/
1474void
1475stop_record_helper (void)
1476{
1477 if (NULL != record_helper)
1478 { 972 {
1479 GNUNET_HELPER_stop (record_helper, GNUNET_NO); 973 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1480 record_helper = NULL; 974 "Received AUDIO data for non-existing line %u, dropping.\n",
1481 975 ntohl (msg->remote_line));
1482 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Stopped record helper\n")); 976 return GNUNET_SYSERR;
1483 } 977 }
978 line->tunnel_unreliable = tunnel;
979 *tunnel_ctx = line;
980 }
981 cam = (struct ClientAudioMessage *) buf;
982 cam->header.size = htons (sizeof (buf));
983 cam->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO);
984 memcpy (&cam[1], &msg[1], msize);
985 GNUNET_SERVER_notification_context_unicast (nc,
986 line->client,
987 &cam->header,
988 GNUNET_YES);
989 GNUNET_MESH_receive_done (tunnel);
990 return GNUNET_OK;
1484} 991}
1485 992
1486/**
1487* Function to stop both audio helpers
1488*/
1489void
1490stop_helpers (void)
1491{
1492 stop_playback_helper ();
1493 stop_record_helper ();
1494}
1495
1496/******************************************************************************/
1497/*********************** TUNNEL HANDLING *******************/
1498/******************************************************************************/
1499 993
1500/** 994/**
1501 * Method called whenever another peer has added us to a tunnel 995 * Method called whenever another peer has added us to a tunnel
@@ -1508,21 +1002,14 @@ stop_helpers (void)
1508 * @return initial tunnel context for the tunnel (can be NULL -- that's not an error) 1002 * @return initial tunnel context for the tunnel (can be NULL -- that's not an error)
1509 */ 1003 */
1510static void * 1004static void *
1511inbound_tunnel (void *cls, struct GNUNET_MESH_Tunnel *tunnel, 1005inbound_tunnel (void *cls,
1512 const struct GNUNET_PeerIdentity *initiator, uint32_t port) 1006 struct GNUNET_MESH_Tunnel *tunnel,
1007 const struct GNUNET_PeerIdentity *initiator,
1008 uint32_t port)
1513{ 1009{
1514 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1010 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1515 _("Received incoming tunnel on port %d\n"), port); 1011 _("Received incoming tunnel on port %d\n"),
1516 if (50003 == port) 1012 port);
1517 {
1518 tunnel_unreliable = tunnel;
1519
1520 start_time = GNUNET_TIME_absolute_get ();
1521
1522 start_helpers ();
1523 audio_task = GNUNET_SCHEDULER_add_now (&transmit_audio_task, NULL);
1524 }
1525
1526 return NULL; 1013 return NULL;
1527} 1014}
1528 1015
@@ -1531,115 +1018,68 @@ inbound_tunnel (void *cls, struct GNUNET_MESH_Tunnel *tunnel,
1531 * Function called whenever an inbound tunnel is destroyed. Should clean up 1018 * Function called whenever an inbound tunnel is destroyed. Should clean up
1532 * any associated state. 1019 * any associated state.
1533 * 1020 *
1534 * @param cls closure (set from GNUNET_MESH_connect) 1021 * @param cls closure (set from #GNUNET_MESH_connect)
1535 * @param tunnel connection to the other end (henceforth invalid) 1022 * @param tunnel connection to the other end (henceforth invalid)
1536 * @param tunnel_ctx place where local state associated 1023 * @param tunnel_ctx place where local state associated
1537 * with the tunnel is stored 1024 * with the tunnel is stored
1538 */ 1025 */
1539static void 1026static void
1540inbound_end (void *cls, const struct GNUNET_MESH_Tunnel *tunnel, 1027inbound_end (void *cls,
1028 const struct GNUNET_MESH_Tunnel *tunnel,
1541 void *tunnel_ctx) 1029 void *tunnel_ctx)
1542{ 1030{
1543 if (tunnel == tunnel_unreliable) 1031 struct Line *line = tunnel_ctx;
1544 { 1032 struct ClientPhoneHangupMessage hup;
1545 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Tunnel closed: audio\n"); 1033
1546 1034 if (NULL == line)
1547 stop_helpers (); 1035 return;
1548 tunnel_unreliable = NULL; 1036 if (line->tunnel_unreliable == tunnel)
1549 } 1037 {
1550 1038 line->tunnel_unreliable = NULL;
1551 if (tunnel == tunnel_reliable) 1039 return;
1552 { 1040 }
1553 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Tunnel closed: control\n"); 1041 hup.header.size = sizeof (hup);
1554 1042 hup.header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP);
1555 if (LISTEN != connection.status && NULL != connection.client) 1043 switch (line->status)
1556 { 1044 {
1557 if (NULL == 1045 case LS_CALLEE_LISTEN:
1558 GNUNET_SERVER_notify_transmit_ready (connection.client, 1046 GNUNET_break (0);
1559 sizeof (struct 1047 return;
1560 ServerClientSessionTerminateMessage), 1048 case LS_CALLEE_RINGING:
1561 MAX_TRANSMIT_DELAY, 1049 case LS_CALLEE_CONNECTED:
1562 &transmit_server_terminate_message, 1050 GNUNET_SERVER_notification_context_unicast (nc,
1563 NULL)) 1051 line->client,
1564 { 1052 &hup.header,
1565 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1053 GNUNET_NO);
1566 _ 1054 line->status = LS_CALLEE_LISTEN;
1567 ("Could not queue ServerClientSessionTerminateMessage\n")); 1055 break;
1568 } 1056 case LS_CALLEE_SHUTDOWN:
1569 } 1057 line->status = LS_CALLEE_LISTEN;
1570 1058 destroy_line_mesh_tunnels (line);
1571 status_to_listen (); 1059 break;
1572 } 1060 case LS_CALLER_CALLING:
1061 case LS_CALLER_CONNECTED:
1062 GNUNET_SERVER_notification_context_unicast (nc,
1063 line->client,
1064 &hup.header,
1065 GNUNET_NO);
1066 destroy_line_mesh_tunnels (line);
1067 GNUNET_CONTAINER_DLL_remove (lines_head,
1068 lines_tail,
1069 line);
1070 GNUNET_free_non_null (line->audio_data);
1071 GNUNET_free (line);
1072 break;
1073 case LS_CALLER_SHUTDOWN:
1074 destroy_line_mesh_tunnels (line);
1075 GNUNET_CONTAINER_DLL_remove (lines_head,
1076 lines_tail,
1077 line);
1078 GNUNET_free (line);
1079 break;
1080 }
1573} 1081}
1574 1082
1575/******************************************************************************/
1576/*********************** CLIENT HANDLING *******************/
1577/******************************************************************************/
1578
1579/**
1580 * A client connected.
1581 *
1582 * @param cls closure, NULL
1583 * @param client identification of the client
1584 */
1585
1586static void
1587handle_client_connect (void *cls, struct GNUNET_SERVER_Client *cl)
1588{
1589 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Client connected\n");
1590 struct ServerClientMissedCallMessage *message;
1591 size_t msg_size;
1592 struct VoipClient c;
1593 c.client = cl;
1594
1595 GNUNET_CONTAINER_slist_add_end (clients,
1596 GNUNET_CONTAINER_SLIST_DISPOSITION_TRANSIENT,
1597 &c, sizeof (struct VoipClient));
1598 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Client added: %p\n"), cl);
1599
1600 if (0 < GNUNET_CONTAINER_slist_count (missed_calls))
1601 {
1602 int i = 0;
1603 msg_size =
1604 sizeof (struct ServerClientMissedCallMessage) +
1605 GNUNET_CONTAINER_slist_count (missed_calls) *
1606 sizeof (struct MissedCall);
1607 message =
1608 (struct ServerClientMissedCallMessage *) GNUNET_malloc (msg_size);
1609
1610 message->header.size = htons (msg_size);
1611 message->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_SC_MISSED_CALL);
1612 message->number = GNUNET_CONTAINER_slist_count (missed_calls);
1613
1614 struct GNUNET_CONTAINER_SList_Iterator iterator =
1615 GNUNET_CONTAINER_slist_begin (missed_calls);
1616 do
1617 {
1618 memcpy (&(message->missed_call[i]),
1619 GNUNET_CONTAINER_slist_get (&iterator, NULL),
1620 sizeof (struct MissedCall));
1621 i++;
1622 }
1623 while (GNUNET_OK == GNUNET_CONTAINER_slist_next (&iterator));
1624
1625 GNUNET_CONTAINER_slist_iter_destroy (&iterator);
1626 GNUNET_CONTAINER_slist_clear (missed_calls);
1627
1628
1629 if (NULL ==
1630 GNUNET_SERVER_notify_transmit_ready (cl, msg_size,
1631 MAX_TRANSMIT_DELAY,
1632 &transmit_server_missed_call_message,
1633 (void *) message))
1634 {
1635 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1636 _("Could not queue ServerClientMissedCallMessage\n"));
1637 GNUNET_free (message);
1638 }
1639 }
1640
1641 return;
1642}
1643 1083
1644/** 1084/**
1645 * A client disconnected. Remove all of its data structure entries. 1085 * A client disconnected. Remove all of its data structure entries.
@@ -1648,42 +1088,21 @@ handle_client_connect (void *cls, struct GNUNET_SERVER_Client *cl)
1648 * @param client identification of the client 1088 * @param client identification of the client
1649 */ 1089 */
1650static void 1090static void
1651handle_client_disconnect (void *cls, struct GNUNET_SERVER_Client *cl) 1091handle_client_disconnect (void *cls,
1092 struct GNUNET_SERVER_Client *client)
1652{ 1093{
1653 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Client disconnected\n"); 1094 struct Line *line;
1654 1095
1655 if (connection.client == cl) 1096 line = GNUNET_SERVER_client_get_user_context (client, struct Line);
1656 { 1097 if (NULL == line)
1657 if (CONNECTED == connection.status) 1098 return;
1658 { 1099 GNUNET_CONTAINER_DLL_remove (lines_head,
1659 terminate_call (); 1100 lines_tail,
1660 } 1101 line);
1661 else 1102 GNUNET_free (line);
1662 { 1103 GNUNET_SERVER_client_set_user_context (client, NULL);
1663 status_to_listen ();
1664 }
1665 }
1666
1667 struct GNUNET_CONTAINER_SList_Iterator iterator =
1668 GNUNET_CONTAINER_slist_begin (clients);
1669 do
1670 {
1671 if (((struct VoipClient *)
1672 GNUNET_CONTAINER_slist_get (&iterator, NULL))->client == cl)
1673 {
1674 GNUNET_CONTAINER_slist_erase (&iterator);
1675 }
1676 }
1677 while (GNUNET_OK == GNUNET_CONTAINER_slist_next (&iterator));
1678
1679 GNUNET_CONTAINER_slist_iter_destroy (&iterator);
1680
1681 return;
1682} 1104}
1683 1105
1684/******************************************************************************/
1685/*********************** SERVICE *******************/
1686/******************************************************************************/
1687 1106
1688/** 1107/**
1689 * Shutdown nicely 1108 * Shutdown nicely
@@ -1692,60 +1111,23 @@ handle_client_disconnect (void *cls, struct GNUNET_SERVER_Client *cl)
1692 * @param tc the task context 1111 * @param tc the task context
1693 */ 1112 */
1694static void 1113static void
1695do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) 1114do_shutdown (void *cls,
1115 const struct GNUNET_SCHEDULER_TaskContext *tc)
1696{ 1116{
1697 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Shutdown\n");
1698
1699 stop_helpers ();
1700
1701 if (NULL != tunnel_reliable)
1702 {
1703 GNUNET_MESH_tunnel_destroy (tunnel_reliable);
1704 }
1705
1706 if (NULL != tunnel_unreliable)
1707 {
1708 GNUNET_MESH_tunnel_destroy (tunnel_unreliable);
1709 }
1710
1711 if (NULL != mesh) 1117 if (NULL != mesh)
1712 { 1118 {
1713 GNUNET_MESH_disconnect (mesh); 1119 GNUNET_MESH_disconnect (mesh);
1714 } 1120 mesh = NULL;
1715 1121 }
1716 if (NULL != nc) 1122 if (NULL != nc)
1717 { 1123 {
1718 GNUNET_SERVER_notification_context_destroy (nc); 1124 GNUNET_SERVER_notification_context_destroy (nc);
1719 nc = NULL; 1125 nc = NULL;
1720 } 1126 }
1721
1722 GNUNET_CONTAINER_slist_destroy (audio_buffer);
1723 GNUNET_CONTAINER_slist_destroy (clients);
1724 GNUNET_CONTAINER_slist_destroy (missed_calls);
1725 GNUNET_CONTAINER_slist_destroy (peers_to_notify);
1726} 1127}
1727 1128
1728 1129
1729/** 1130/**
1730 * Handler array for traffic received
1731 */
1732static struct GNUNET_MESH_MessageHandler mesh_handlers[] = {
1733 {&handle_mesh_initiate_message,
1734 GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_SESSION_INITIATE,
1735 sizeof (struct MeshSessionInitiateMessage)},
1736 {&handle_mesh_accept_message, GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_SESSION_ACCEPT,
1737 sizeof (struct MeshSessionAcceptMessage)},
1738 {&handle_mesh_reject_message, GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_SESSION_REJECT,
1739 sizeof (struct MeshSessionRejectMessage)},
1740 {&handle_mesh_terminate_message,
1741 GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_SESSION_TERMINATE,
1742 sizeof (struct MeshSessionTerminateMessage)},
1743 {&handle_mesh_audio_message, GNUNET_MESSAGE_TYPE_CONVERSATION_AUDIO,
1744 sizeof (struct AudioMessage)},
1745 {NULL, 0, 0}
1746};
1747
1748/**
1749 * Main function that will be run by the scheduler. 1131 * Main function that will be run by the scheduler.
1750 * 1132 *
1751 * @param cls closure 1133 * @param cls closure
@@ -1753,67 +1135,77 @@ static struct GNUNET_MESH_MessageHandler mesh_handlers[] = {
1753 * @param c configuration 1135 * @param c configuration
1754 */ 1136 */
1755static void 1137static void
1756run (void *cls, struct GNUNET_SERVER_Handle *server, 1138run (void *cls,
1139 struct GNUNET_SERVER_Handle *server,
1757 const struct GNUNET_CONFIGURATION_Handle *c) 1140 const struct GNUNET_CONFIGURATION_Handle *c)
1758{ 1141{
1142 static const struct GNUNET_SERVER_MessageHandler server_handlers[] = {
1143 {&handle_client_register_message, NULL,
1144 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_REGISTER,
1145 sizeof (struct ClientPhoneRegisterMessage)},
1146 {&handle_client_pickup_message, NULL,
1147 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_PICK_UP,
1148 0},
1149 {&handle_client_hangup_message, NULL,
1150 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP,
1151 0},
1152 {&handle_client_call_message, NULL,
1153 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_CALL,
1154 0},
1155 {&handle_client_audio_message, NULL,
1156 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO,
1157 0},
1158 {NULL, NULL, 0, 0}
1159 };
1160 static struct GNUNET_MESH_MessageHandler mesh_handlers[] = {
1161 {&handle_mesh_ring_message,
1162 GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_RING,
1163 sizeof (struct MeshPhoneRingMessage)},
1164 {&handle_mesh_hangup_message,
1165 GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_HANG_UP,
1166 0},
1167 {&handle_mesh_pickup_message,
1168 GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_PICK_UP,
1169 0},
1170 {&handle_mesh_busy_message,
1171 GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_BUSY,
1172 sizeof (struct MeshPhoneBusyMessage)},
1173 {&handle_mesh_audio_message, GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_AUDIO,
1174 0},
1175 {NULL, 0, 0}
1176 };
1177 static uint32_t ports[] = {
1178 GNUNET_APPLICATION_TYPE_CONVERSATION_CONTROL,
1179 GNUNET_APPLICATION_TYPE_CONVERSATION_AUDIO,
1180 0
1181 };
1759 1182
1760 static uint32_t ports[] = { 50002, 50003, 0 };
1761 cfg = c; 1183 cfg = c;
1762 1184 GNUNET_assert (GNUNET_OK ==
1185 GNUNET_CRYPTO_get_host_identity (cfg,
1186 &my_identity));
1763 mesh = GNUNET_MESH_connect (cfg, 1187 mesh = GNUNET_MESH_connect (cfg,
1764 NULL, 1188 NULL,
1765 &inbound_tunnel, 1189 &inbound_tunnel,
1766 &inbound_end, mesh_handlers, ports); 1190 &inbound_end,
1191 mesh_handlers,
1192 ports);
1767 1193
1768 if (NULL == mesh) 1194 if (NULL == mesh)
1769 { 1195 {
1770 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Couldn't connect to mesh\n"); 1196 GNUNET_break (0);
1771 return; 1197 GNUNET_SCHEDULER_shutdown ();
1772 } 1198 return;
1773 else 1199 }
1774 {
1775 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Connected to mesh\n");
1776 }
1777
1778 static const struct GNUNET_SERVER_MessageHandler server_handlers[] = {
1779 {&handle_session_initiate_message, NULL,
1780 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_SESSION_INITIATE,
1781 sizeof (struct ClientServerSessionInitiateMessage)},
1782 {&handle_session_accept_message, NULL,
1783 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_SESSION_ACCEPT,
1784 sizeof (struct ClientServerSessionAcceptMessage)},
1785 {&handle_session_reject_message, NULL,
1786 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_SESSION_REJECT,
1787 sizeof (struct ClientServerSessionRejectMessage)},
1788 {&handle_session_terminate_message, NULL,
1789 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_SESSION_TERMINATE,
1790 sizeof (struct ClientServerSessionTerminateMessage)},
1791 {NULL, NULL, 0, 0}
1792 };
1793
1794 connection.status = LISTEN;
1795
1796 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Changed connection status to %d\n"),
1797 connection.status);
1798
1799 nc = GNUNET_SERVER_notification_context_create (server, 16); 1200 nc = GNUNET_SERVER_notification_context_create (server, 16);
1800
1801 GNUNET_SERVER_add_handlers (server, server_handlers); 1201 GNUNET_SERVER_add_handlers (server, server_handlers);
1802 GNUNET_SERVER_connect_notify (server, &handle_client_connect, NULL);
1803 GNUNET_SERVER_disconnect_notify (server, &handle_client_disconnect, NULL); 1202 GNUNET_SERVER_disconnect_notify (server, &handle_client_disconnect, NULL);
1804 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &do_shutdown, 1203 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
1204 &do_shutdown,
1805 NULL); 1205 NULL);
1806
1807 clients = GNUNET_CONTAINER_slist_create ();
1808
1809 // Missed calls
1810 missed_calls = GNUNET_CONTAINER_slist_create ();
1811 peers_to_notify = GNUNET_CONTAINER_slist_create ();
1812 audio_buffer = GNUNET_CONTAINER_slist_create ();
1813
1814 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Voip service running\n"));
1815} 1206}
1816 1207
1208
1817/** 1209/**
1818 * The main function for the conversation service. 1210 * The main function for the conversation service.
1819 * 1211 *
@@ -1822,10 +1214,13 @@ run (void *cls, struct GNUNET_SERVER_Handle *server,
1822 * @return 0 ok, 1 on error 1214 * @return 0 ok, 1 on error
1823 */ 1215 */
1824int 1216int
1825main (int argc, char *const *argv) 1217main (int argc,
1218 char *const *argv)
1826{ 1219{
1827 return (GNUNET_OK == 1220 return (GNUNET_OK ==
1828 GNUNET_SERVICE_run (argc, argv, "conversation", GNUNET_SERVICE_OPTION_NONE, 1221 GNUNET_SERVICE_run (argc, argv,
1222 "conversation",
1223 GNUNET_SERVICE_OPTION_NONE,
1829 &run, NULL)) ? 0 : 1; 1224 &run, NULL)) ? 0 : 1;
1830} 1225}
1831 1226