aboutsummaryrefslogtreecommitdiff
path: root/src/conversation/conversation_api_call.c
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2013-11-15 19:06:55 +0000
committerChristian Grothoff <christian@grothoff.org>2013-11-15 19:06:55 +0000
commitdd594a4b0a7dbc57ea49f8a797a7a412e53baa53 (patch)
tree6a651b061395ebd9370cef01f14655ff50e81a66 /src/conversation/conversation_api_call.c
parent1c405f3fb76f509113526678dbe9a5736dfce394 (diff)
downloadgnunet-dd594a4b0a7dbc57ea49f8a797a7a412e53baa53.tar.gz
gnunet-dd594a4b0a7dbc57ea49f8a797a7a412e53baa53.zip
-towards enabling call waiting in conversation -- creates FTBFS
Diffstat (limited to 'src/conversation/conversation_api_call.c')
-rw-r--r--src/conversation/conversation_api_call.c678
1 files changed, 678 insertions, 0 deletions
diff --git a/src/conversation/conversation_api_call.c b/src/conversation/conversation_api_call.c
new file mode 100644
index 000000000..a57bf1ddb
--- /dev/null
+++ b/src/conversation/conversation_api_call.c
@@ -0,0 +1,678 @@
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_api_call.c
23 * @brief call 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_gnsrecord_lib.h"
31#include "gnunet_gns_service.h"
32#include "conversation.h"
33
34
35/**
36 * Possible states of the phone.
37 */
38enum CallState
39{
40 /**
41 * We still need to lookup the callee.
42 */
43 CS_LOOKUP = 0,
44
45 /**
46 * The call is ringing.
47 */
48 CS_RINGING,
49
50 /**
51 * The call is in an active conversation.
52 */
53 CS_ACTIVE,
54
55 /**
56 * The call is in termination.
57 */
58 CS_SHUTDOWN,
59
60 /**
61 * The call was suspended by the caller.
62 */
63 CS_SUSPENDED_CALLER,
64
65 /**
66 * The call was suspended by the callee.
67 */
68 CS_SUSPENDED_CALLEE,
69
70 /**
71 * The call was suspended by both caller and callee.
72 */
73 CS_SUSPENDED_BOTH
74};
75
76
77/**
78 * Handle for an outgoing call.
79 */
80struct GNUNET_CONVERSATION_Call
81{
82
83 /**
84 * Our configuration.
85 */
86 const struct GNUNET_CONFIGURATION_Handle *cfg;
87
88 /**
89 * Handle to talk with CONVERSATION service.
90 */
91 struct GNUNET_CLIENT_Connection *client;
92
93 /**
94 * Our caller identity.
95 */
96 struct GNUNET_IDENTITY_Ego *caller_id;
97
98 /**
99 * Target callee as a GNS address/name.
100 */
101 char *callee;
102
103 /**
104 * Our speaker.
105 */
106 struct GNUNET_SPEAKER_Handle *speaker;
107
108 /**
109 * Our microphone.
110 */
111 struct GNUNET_MICROPHONE_Handle *mic;
112
113 /**
114 * Function to call with events.
115 */
116 GNUNET_CONVERSATION_CallEventHandler event_handler;
117
118 /**
119 * Closure for @e event_handler
120 */
121 void *event_handler_cls;
122
123 /**
124 * Handle for transmitting to the CONVERSATION service.
125 */
126 struct GNUNET_MQ_Handle *mq;
127
128 /**
129 * Connection to GNS (can be NULL).
130 */
131 struct GNUNET_GNS_Handle *gns;
132
133 /**
134 * Active GNS lookup (or NULL).
135 */
136 struct GNUNET_GNS_LookupRequest *gns_lookup;
137
138 /**
139 * Target phone record, only valid after the lookup is done.
140 */
141 struct GNUNET_CONVERSATION_PhoneRecord phone_record;
142
143 /**
144 * State machine for the call.
145 */
146 enum CallState state;
147
148};
149
150
151/**
152 * The call got disconnected, reconnect to the service.
153 *
154 * @param call call to reconnect
155 */
156static void
157reconnect_call (struct GNUNET_CONVERSATION_Call *call);
158
159
160/**
161 * Process recorded audio data.
162 *
163 * @param cls closure with the `struct GNUNET_CONVERSATION_Call`
164 * @param data_size number of bytes in @a data
165 * @param data audio data to play
166 */
167static void
168transmit_call_audio (void *cls,
169 size_t data_size,
170 const void *data)
171{
172 struct GNUNET_CONVERSATION_Call *call = cls;
173 struct GNUNET_MQ_Envelope *e;
174 struct ClientAudioMessage *am;
175
176 GNUNET_assert (CS_ACTIVE == call->state);
177 e = GNUNET_MQ_msg_extra (am,
178 data_size,
179 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO);
180 memcpy (&am[1], data, data_size);
181 GNUNET_MQ_send (call->mq, e);
182}
183
184
185/**
186 * We received a `struct ClientPhoneSuspendMessage`
187 *
188 * @param cls the `struct GNUNET_CONVERSATION_Call`
189 * @param msg the message
190 */
191static void
192handle_call_suspend (void *cls,
193 const struct GNUNET_MessageHeader *msg)
194{
195 struct GNUNET_CONVERSATION_Call *call = cls;
196
197 switch (call->state)
198 {
199 case CS_LOOKUP:
200 GNUNET_break (0);
201 reconnect_call (call);
202 break;
203 case CS_RINGING:
204 GNUNET_break_op (0);
205 reconnect_call (call);
206 break;
207 case CS_SUSPENDED_CALLER:
208 call->state = CS_SUSPENDED_BOTH;
209 call->event_handler (call->event_handler_cls,
210 GNUNET_CONVERSATION_EC_CALL_SUSPENDED);
211 break;
212 case CS_SUSPENDED_CALLEE:
213 case CS_SUSPENDED_BOTH:
214 GNUNET_break_op (0);
215 break;
216 case CS_ACTIVE:
217 call->state = CS_SUSPENDED_CALLEE;
218 call->event_handler (call->event_handler_cls,
219 GNUNET_CONVERSATION_EC_CALL_SUSPENDED);
220 call->speaker->disable_speaker (call->speaker->cls);
221 call->mic->disable_microphone (call->mic->cls);
222 break;
223 case CS_SHUTDOWN:
224 GNUNET_CONVERSATION_call_stop (call);
225 break;
226 }
227}
228
229
230/**
231 * We received a `struct ClientPhoneResumeMessage`
232 *
233 * @param cls the `struct GNUNET_CONVERSATION_Call`
234 * @param msg the message
235 */
236static void
237handle_call_resume (void *cls,
238 const struct GNUNET_MessageHeader *msg)
239{
240 struct GNUNET_CONVERSATION_Call *call = cls;
241
242 switch (call->state)
243 {
244 case CS_LOOKUP:
245 GNUNET_break (0);
246 reconnect_call (call);
247 break;
248 case CS_RINGING:
249 GNUNET_break_op (0);
250 reconnect_call (call);
251 break;
252 case CS_SUSPENDED_CALLER:
253 GNUNET_break_op (0);
254 break;
255 case CS_SUSPENDED_CALLEE:
256 call->state = CS_ACTIVE;
257 call->event_handler (call->event_handler_cls,
258 GNUNET_CONVERSATION_EC_CALL_RESUMED);
259 call->speaker->enable_speaker (call->speaker->cls);
260 call->mic->enable_microphone (call->mic->cls,
261 &transmit_call_audio,
262 call);
263 break;
264 case CS_SUSPENDED_BOTH:
265 call->state = CS_SUSPENDED_CALLER;
266 call->event_handler (call->event_handler_cls,
267 GNUNET_CONVERSATION_EC_CALL_RESUMED);
268 break;
269 case CS_ACTIVE:
270 GNUNET_break_op (0);
271 break;
272 case CS_SHUTDOWN:
273 GNUNET_CONVERSATION_call_stop (call);
274 break;
275 }
276}
277
278
279/**
280 * We received a `struct ClientPhonePickedupMessage`
281 *
282 * @param cls the `struct GNUNET_CONVERSATION_Call`
283 * @param msg the message
284 */
285static void
286handle_call_picked_up (void *cls,
287 const struct GNUNET_MessageHeader *msg)
288{
289 struct GNUNET_CONVERSATION_Call *call = cls;
290
291 switch (call->state)
292 {
293 case CS_LOOKUP:
294 GNUNET_break (0);
295 reconnect_call (call);
296 break;
297 case CS_RINGING:
298 call->state = CS_ACTIVE;
299 call->event_handler (call->event_handler_cls,
300 GNUNET_CONVERSATION_EC_CALL_PICKED_UP);
301 call->speaker->enable_speaker (call->speaker->cls);
302 call->mic->enable_microphone (call->mic->cls,
303 &transmit_call_audio,
304 call);
305 break;
306 case CS_SUSPENDED_CALLER:
307 case CS_SUSPENDED_CALLEE:
308 case CS_SUSPENDED_BOTH:
309 case CS_ACTIVE:
310 GNUNET_break (0);
311 reconnect_call (call);
312 break;
313 case CS_SHUTDOWN:
314 GNUNET_CONVERSATION_call_stop (call);
315 break;
316 }
317}
318
319
320/**
321 * We received a `struct ClientPhoneHangupMessage`
322 *
323 * @param cls the `struct GNUNET_CONVERSATION_Call`
324 * @param msg the message
325 */
326static void
327handle_call_hangup (void *cls,
328 const struct GNUNET_MessageHeader *msg)
329{
330 struct GNUNET_CONVERSATION_Call *call = cls;
331
332 switch (call->state)
333 {
334 case CS_LOOKUP:
335 GNUNET_break (0);
336 reconnect_call (call);
337 break;
338 case CS_RINGING:
339 case CS_SUSPENDED_CALLER:
340 case CS_SUSPENDED_CALLEE:
341 case CS_SUSPENDED_BOTH:
342 case CS_ACTIVE:
343 call->event_handler (call->event_handler_cls,
344 GNUNET_CONVERSATION_EC_CALL_HUNG_UP);
345 GNUNET_CONVERSATION_call_stop (call);
346 return;
347 case CS_SHUTDOWN:
348 GNUNET_CONVERSATION_call_stop (call);
349 break;
350 }
351}
352
353
354/**
355 * We received a `struct ClientAudioMessage`
356 *
357 * @param cls the `struct GNUNET_CONVERSATION_Call`
358 * @param msg the message
359 */
360static void
361handle_call_audio_message (void *cls,
362 const struct GNUNET_MessageHeader *msg)
363{
364 struct GNUNET_CONVERSATION_Call *call = cls;
365 const struct ClientAudioMessage *am;
366
367 am = (const struct ClientAudioMessage *) msg;
368 switch (call->state)
369 {
370 case CS_LOOKUP:
371 GNUNET_break (0);
372 reconnect_call (call);
373 break;
374 case CS_RINGING:
375 GNUNET_break (0);
376 reconnect_call (call);
377 break;
378 case CS_SUSPENDED_CALLER:
379 /* can happen: we suspended, other peer did not yet
380 learn about this. */
381 break;
382 case CS_SUSPENDED_CALLEE:
383 case CS_SUSPENDED_BOTH:
384 /* can (rarely) also happen: other peer suspended, but mesh might
385 have had delayed data on the unreliable channel */
386 break;
387 case CS_ACTIVE:
388 call->speaker->play (call->speaker->cls,
389 ntohs (msg->size) - sizeof (struct ClientAudioMessage),
390 &am[1]);
391 break;
392 case CS_SHUTDOWN:
393 GNUNET_CONVERSATION_call_stop (call);
394 break;
395 }
396}
397
398
399/**
400 * Iterator called on obtained result for a GNS lookup.
401 *
402 * @param cls closure with the `struct GNUNET_CONVERSATION_Call`
403 * @param rd_count number of records in @a rd
404 * @param rd the records in reply
405 */
406static void
407handle_gns_response (void *cls,
408 uint32_t rd_count,
409 const struct GNUNET_GNSRECORD_Data *rd)
410{
411 struct GNUNET_CONVERSATION_Call *call = cls;
412 uint32_t i;
413 struct GNUNET_MQ_Envelope *e;
414 struct ClientCallMessage *ccm;
415
416 call->gns_lookup = NULL;
417 for (i=0;i<rd_count;i++)
418 {
419 if (GNUNET_GNSRECORD_TYPE_PHONE == rd[i].record_type)
420 {
421 if (rd[i].data_size != sizeof (struct GNUNET_CONVERSATION_PhoneRecord))
422 {
423 GNUNET_break_op (0);
424 continue;
425 }
426 memcpy (&call->phone_record,
427 rd[i].data,
428 rd[i].data_size);
429 e = GNUNET_MQ_msg (ccm, GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_CALL);
430 ccm->line = call->phone_record.line;
431 ccm->target = call->phone_record.peer;
432 ccm->caller_id = *GNUNET_IDENTITY_ego_get_private_key (call->caller_id);
433 GNUNET_MQ_send (call->mq, e);
434 call->state = CS_RINGING;
435 call->event_handler (call->event_handler_cls,
436 GNUNET_CONVERSATION_EC_CALL_RINGING);
437 return;
438 }
439 }
440 /* not found */
441 call->event_handler (call->event_handler_cls,
442 GNUNET_CONVERSATION_EC_CALL_GNS_FAIL);
443 GNUNET_CONVERSATION_call_stop (call);
444}
445
446
447/**
448 * We encountered an error talking with the conversation service.
449 *
450 * @param cls the `struct GNUNET_CONVERSATION_Call`
451 * @param error details about the error
452 */
453static void
454call_error_handler (void *cls,
455 enum GNUNET_MQ_Error error)
456{
457 struct GNUNET_CONVERSATION_Call *call = cls;
458
459 GNUNET_break (0);
460 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
461 _("Internal MQ error %d\n"),
462 error);
463 reconnect_call (call);
464}
465
466
467/**
468 * The call got disconnected, reconnect to the service.
469 *
470 * @param call call to reconnect
471 */
472static void
473reconnect_call (struct GNUNET_CONVERSATION_Call *call)
474{
475 static struct GNUNET_MQ_MessageHandler handlers[] =
476 {
477 { &handle_call_suspend,
478 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_SUSPEND,
479 sizeof (struct ClientPhoneSuspendMessage) },
480 { &handle_call_resume,
481 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_RESUME,
482 sizeof (struct ClientPhoneResumeMessage) },
483 { &handle_call_picked_up,
484 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_PICKED_UP,
485 sizeof (struct ClientPhonePickedupMessage) },
486 { &handle_call_hangup,
487 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP,
488 sizeof (struct ClientPhoneHangupMessage) },
489 { &handle_call_audio_message,
490 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO,
491 0 },
492 { NULL, 0, 0 }
493 };
494 struct GNUNET_CRYPTO_EcdsaPublicKey my_zone;
495
496 if (CS_ACTIVE == call->state)
497 {
498 call->speaker->disable_speaker (call->speaker->cls);
499 call->mic->disable_microphone (call->mic->cls);
500 }
501 if (NULL != call->mq)
502 {
503 GNUNET_MQ_destroy (call->mq);
504 call->mq = NULL;
505 }
506 if (NULL != call->client)
507 {
508 GNUNET_CLIENT_disconnect (call->client);
509 call->client = NULL;
510 }
511 call->state = CS_SHUTDOWN;
512 call->client = GNUNET_CLIENT_connect ("conversation", call->cfg);
513 if (NULL == call->client)
514 return;
515 call->mq = GNUNET_MQ_queue_for_connection_client (call->client,
516 handlers,
517 &call_error_handler,
518 call);
519 call->state = CS_LOOKUP;
520 GNUNET_IDENTITY_ego_get_public_key (call->caller_id,
521 &my_zone);
522 call->gns_lookup = GNUNET_GNS_lookup (call->gns,
523 call->callee,
524 &my_zone,
525 GNUNET_GNSRECORD_TYPE_PHONE,
526 GNUNET_NO,
527 NULL /* FIXME: add shortening support */,
528 &handle_gns_response, call);
529 GNUNET_assert (NULL != call->gns_lookup);
530}
531
532
533/**
534 * Call the phone of another user.
535 *
536 * @param cfg configuration to use, specifies our phone service
537 * @param caller_id identity of the caller
538 * @param callee GNS name of the callee (used to locate the callee's record)
539 * @param speaker speaker to use (will be used automatically immediately once the
540 * #GNUNET_CONVERSATION_EC_READY event is generated); we will NOT generate
541 * a ring tone on the speaker
542 * @param mic microphone to use (will be used automatically immediately once the
543 * #GNUNET_CONVERSATION_EC_READY event is generated)
544 * @param event_handler how to notify the owner of the phone about events
545 * @param event_handler_cls closure for @a event_handler
546 */
547struct GNUNET_CONVERSATION_Call *
548GNUNET_CONVERSATION_call_start (const struct GNUNET_CONFIGURATION_Handle *cfg,
549 struct GNUNET_IDENTITY_Ego *caller_id,
550 const char *callee,
551 struct GNUNET_SPEAKER_Handle *speaker,
552 struct GNUNET_MICROPHONE_Handle *mic,
553 GNUNET_CONVERSATION_CallEventHandler event_handler,
554 void *event_handler_cls)
555{
556 struct GNUNET_CONVERSATION_Call *call;
557
558 call = GNUNET_new (struct GNUNET_CONVERSATION_Call);
559 call->cfg = cfg;
560 call->caller_id = caller_id;
561 call->callee = GNUNET_strdup (callee);
562 call->speaker = speaker;
563 call->mic = mic;
564 call->event_handler = event_handler;
565 call->event_handler_cls = event_handler_cls;
566 call->gns = GNUNET_GNS_connect (cfg);
567 reconnect_call (call);
568
569 if ( (NULL == call->client) ||
570 (NULL == call->gns) )
571 {
572 GNUNET_CONVERSATION_call_stop (call);
573 return NULL;
574 }
575 return call;
576}
577
578
579/**
580 * We've sent the hang up message, now finish terminating the call.
581 *
582 * @param cls the `struct GNUNET_CONVERSATION_Call` to terminate
583 */
584static void
585finish_stop (void *cls)
586{
587 struct GNUNET_CONVERSATION_Call *call = cls;
588
589 GNUNET_assert (CS_SHUTDOWN == call->state);
590 GNUNET_CONVERSATION_call_stop (call);
591}
592
593
594/**
595 * Terminate a call. The call may be ringing or ready at this time.
596 *
597 * @param call call to terminate
598 * @param reason if the call was active (ringing or ready) this will be the
599 * reason given to the other user for why we hung up
600 */
601void
602GNUNET_CONVERSATION_call_stop (struct GNUNET_CONVERSATION_Call *call)
603{
604 struct GNUNET_MQ_Envelope *e;
605 struct ClientPhoneHangupMessage *hang;
606
607 if ( (NULL != call->speaker) &&
608 (CS_ACTIVE == call->state) )
609 call->speaker->disable_speaker (call->speaker->cls);
610 if ( (NULL != call->mic) &&
611 (CS_ACTIVE == call->state) )
612 call->mic->disable_microphone (call->mic->cls);
613 if (CS_SHUTDOWN != call->state)
614 {
615 e = GNUNET_MQ_msg (hang, GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP);
616 GNUNET_MQ_notify_sent (e, &finish_stop, call);
617 GNUNET_MQ_send (call->mq, e);
618 call->state = CS_SHUTDOWN;
619 return;
620 }
621 if (NULL != call->mq)
622 {
623 GNUNET_MQ_destroy (call->mq);
624 call->mq = NULL;
625 }
626 if (NULL != call->client)
627 {
628 GNUNET_CLIENT_disconnect (call->client);
629 call->client = NULL;
630 }
631 if (NULL != call->gns_lookup)
632 {
633 GNUNET_GNS_lookup_cancel (call->gns_lookup);
634 call->gns_lookup = NULL;
635 }
636 if (NULL != call->gns)
637 {
638 GNUNET_GNS_disconnect (call->gns);
639 call->gns = NULL;
640 }
641 GNUNET_free (call->callee);
642 GNUNET_free (call);
643}
644
645
646/**
647 * Pause a call. Temporarily suspends the use of speaker and
648 * microphone.
649 *
650 * @param call call to pause
651 */
652void
653GNUNET_CONVERSATION_call_suspend (struct GNUNET_CONVERSATION_Call *call)
654{
655 GNUNET_break (0);
656}
657
658
659/**
660 * Resumes a call after #GNUNET_CONVERSATION_call_pause.
661 *
662 * @param call call to resume
663 * @param speaker speaker to use (will be used automatically immediately once the
664 * #GNUNET_CONVERSATION_EC_READY event is generated); we will NOT generate
665 * a ring tone on the speaker
666 * @param mic microphone to use (will be used automatically immediately once the
667 * #GNUNET_CONVERSATION_EC_READY event is generated)
668 */
669void
670GNUNET_CONVERSATION_call_resume (struct GNUNET_CONVERSATION_Call *call,
671 struct GNUNET_SPEAKER_Handle *speaker,
672 struct GNUNET_MICROPHONE_Handle *mic)
673{
674 GNUNET_break (0);
675}
676
677
678/* end of conversation_api_call.c */