aboutsummaryrefslogtreecommitdiff
path: root/src/conversation/conversation_api.c
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/conversation_api.c
parent45c9ba99503eaf130b3d298062c612e281893d6a (diff)
downloadgnunet-ac3a1c494fd48fc1d01fcc4eafa0b3f4878f5441.tar.gz
gnunet-ac3a1c494fd48fc1d01fcc4eafa0b3f4878f5441.zip
-rename fest: new to default
Diffstat (limited to 'src/conversation/conversation_api.c')
-rw-r--r--src/conversation/conversation_api.c1498
1 files changed, 930 insertions, 568 deletions
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 */