2 This file is part of GNUnet
3 (C) 2013 Christian Grothoff (and other contributing authors)
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.
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.
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.
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
29 #include "gnunet_conversation_service.h"
30 #include "gnunet_gnsrecord_lib.h"
31 #include "gnunet_gns_service.h"
32 #include "conversation.h"
36 * Possible states of the phone.
41 * We still need to lookup the callee.
46 * The call is ringing.
51 * The call is in an active conversation.
56 * The call is in termination.
61 * The call was suspended by the caller.
66 * The call was suspended by the callee.
71 * The call was suspended by both caller and callee.
78 * Handle for an outgoing call.
80 struct GNUNET_CONVERSATION_Call
86 const struct GNUNET_CONFIGURATION_Handle *cfg;
89 * Handle to talk with CONVERSATION service.
91 struct GNUNET_CLIENT_Connection *client;
94 * Our caller identity.
96 struct GNUNET_IDENTITY_Ego *caller_id;
99 * Target callee as a GNS address/name.
106 struct GNUNET_SPEAKER_Handle *speaker;
111 struct GNUNET_MICROPHONE_Handle *mic;
114 * Function to call with events.
116 GNUNET_CONVERSATION_CallEventHandler event_handler;
119 * Closure for @e event_handler
121 void *event_handler_cls;
124 * Handle for transmitting to the CONVERSATION service.
126 struct GNUNET_MQ_Handle *mq;
129 * Connection to GNS (can be NULL).
131 struct GNUNET_GNS_Handle *gns;
134 * Active GNS lookup (or NULL).
136 struct GNUNET_GNS_LookupRequest *gns_lookup;
139 * Target phone record, only valid after the lookup is done.
141 struct GNUNET_CONVERSATION_PhoneRecord phone_record;
144 * State machine for the call.
146 enum CallState state;
152 * The call got disconnected, reconnect to the service.
154 * @param call call to reconnect
157 reconnect_call (struct GNUNET_CONVERSATION_Call *call);
161 * Process recorded audio data.
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
168 transmit_call_audio (void *cls,
172 struct GNUNET_CONVERSATION_Call *call = cls;
173 struct GNUNET_MQ_Envelope *e;
174 struct ClientAudioMessage *am;
176 GNUNET_assert (CS_ACTIVE == call->state);
177 e = GNUNET_MQ_msg_extra (am,
179 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO);
180 memcpy (&am[1], data, data_size);
181 GNUNET_MQ_send (call->mq, e);
186 * We received a `struct ClientPhoneSuspendMessage`
188 * @param cls the `struct GNUNET_CONVERSATION_Call`
189 * @param msg the message
192 handle_call_suspend (void *cls,
193 const struct GNUNET_MessageHeader *msg)
195 struct GNUNET_CONVERSATION_Call *call = cls;
201 reconnect_call (call);
205 reconnect_call (call);
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);
212 case CS_SUSPENDED_CALLEE:
213 case CS_SUSPENDED_BOTH:
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);
224 GNUNET_CONVERSATION_call_stop (call);
231 * We received a `struct ClientPhoneResumeMessage`
233 * @param cls the `struct GNUNET_CONVERSATION_Call`
234 * @param msg the message
237 handle_call_resume (void *cls,
238 const struct GNUNET_MessageHeader *msg)
240 struct GNUNET_CONVERSATION_Call *call = cls;
246 reconnect_call (call);
250 reconnect_call (call);
252 case CS_SUSPENDED_CALLER:
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,
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);
273 GNUNET_CONVERSATION_call_stop (call);
280 * We received a `struct ClientPhonePickedupMessage`
282 * @param cls the `struct GNUNET_CONVERSATION_Call`
283 * @param msg the message
286 handle_call_picked_up (void *cls,
287 const struct GNUNET_MessageHeader *msg)
289 struct GNUNET_CONVERSATION_Call *call = cls;
295 reconnect_call (call);
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,
306 case CS_SUSPENDED_CALLER:
307 case CS_SUSPENDED_CALLEE:
308 case CS_SUSPENDED_BOTH:
311 reconnect_call (call);
314 GNUNET_CONVERSATION_call_stop (call);
321 * We received a `struct ClientPhoneHangupMessage`
323 * @param cls the `struct GNUNET_CONVERSATION_Call`
324 * @param msg the message
327 handle_call_hangup (void *cls,
328 const struct GNUNET_MessageHeader *msg)
330 struct GNUNET_CONVERSATION_Call *call = cls;
336 reconnect_call (call);
339 case CS_SUSPENDED_CALLER:
340 case CS_SUSPENDED_CALLEE:
341 case CS_SUSPENDED_BOTH:
343 call->event_handler (call->event_handler_cls,
344 GNUNET_CONVERSATION_EC_CALL_HUNG_UP);
345 GNUNET_CONVERSATION_call_stop (call);
348 GNUNET_CONVERSATION_call_stop (call);
355 * We received a `struct ClientAudioMessage`
357 * @param cls the `struct GNUNET_CONVERSATION_Call`
358 * @param msg the message
361 handle_call_audio_message (void *cls,
362 const struct GNUNET_MessageHeader *msg)
364 struct GNUNET_CONVERSATION_Call *call = cls;
365 const struct ClientAudioMessage *am;
367 am = (const struct ClientAudioMessage *) msg;
372 reconnect_call (call);
376 reconnect_call (call);
378 case CS_SUSPENDED_CALLER:
379 /* can happen: we suspended, other peer did not yet
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 */
388 call->speaker->play (call->speaker->cls,
389 ntohs (msg->size) - sizeof (struct ClientAudioMessage),
393 GNUNET_CONVERSATION_call_stop (call);
400 * Iterator called on obtained result for a GNS lookup.
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
407 handle_gns_response (void *cls,
409 const struct GNUNET_GNSRECORD_Data *rd)
411 struct GNUNET_CONVERSATION_Call *call = cls;
413 struct GNUNET_MQ_Envelope *e;
414 struct ClientCallMessage *ccm;
416 call->gns_lookup = NULL;
417 for (i=0;i<rd_count;i++)
419 if (GNUNET_GNSRECORD_TYPE_PHONE == rd[i].record_type)
421 if (rd[i].data_size != sizeof (struct GNUNET_CONVERSATION_PhoneRecord))
426 memcpy (&call->phone_record,
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);
441 call->event_handler (call->event_handler_cls,
442 GNUNET_CONVERSATION_EC_CALL_GNS_FAIL);
443 GNUNET_CONVERSATION_call_stop (call);
448 * We encountered an error talking with the conversation service.
450 * @param cls the `struct GNUNET_CONVERSATION_Call`
451 * @param error details about the error
454 call_error_handler (void *cls,
455 enum GNUNET_MQ_Error error)
457 struct GNUNET_CONVERSATION_Call *call = cls;
459 if (CS_SHUTDOWN == call->state)
461 GNUNET_CONVERSATION_call_stop (call);
465 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
466 _("Internal MQ error %d\n"),
468 reconnect_call (call);
473 * The call got disconnected, reconnect to the service.
475 * @param call call to reconnect
478 reconnect_call (struct GNUNET_CONVERSATION_Call *call)
480 static struct GNUNET_MQ_MessageHandler handlers[] =
482 { &handle_call_suspend,
483 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_SUSPEND,
484 sizeof (struct ClientPhoneSuspendMessage) },
485 { &handle_call_resume,
486 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_RESUME,
487 sizeof (struct ClientPhoneResumeMessage) },
488 { &handle_call_picked_up,
489 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_PICKED_UP,
490 sizeof (struct ClientPhonePickedupMessage) },
491 { &handle_call_hangup,
492 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP,
493 sizeof (struct ClientPhoneHangupMessage) },
494 { &handle_call_audio_message,
495 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO,
499 struct GNUNET_CRYPTO_EcdsaPublicKey my_zone;
501 if (CS_ACTIVE == call->state)
503 call->speaker->disable_speaker (call->speaker->cls);
504 call->mic->disable_microphone (call->mic->cls);
506 if (NULL != call->mq)
508 GNUNET_MQ_destroy (call->mq);
511 if (NULL != call->client)
513 GNUNET_CLIENT_disconnect (call->client);
516 call->state = CS_SHUTDOWN;
517 call->client = GNUNET_CLIENT_connect ("conversation", call->cfg);
518 if (NULL == call->client)
520 call->mq = GNUNET_MQ_queue_for_connection_client (call->client,
524 call->state = CS_LOOKUP;
525 GNUNET_IDENTITY_ego_get_public_key (call->caller_id,
527 call->gns_lookup = GNUNET_GNS_lookup (call->gns,
530 GNUNET_GNSRECORD_TYPE_PHONE,
532 NULL /* FIXME: add shortening support */,
533 &handle_gns_response, call);
534 GNUNET_assert (NULL != call->gns_lookup);
539 * Call the phone of another user.
541 * @param cfg configuration to use, specifies our phone service
542 * @param caller_id identity of the caller
543 * @param callee GNS name of the callee (used to locate the callee's record)
544 * @param speaker speaker to use (will be used automatically immediately once the
545 * #GNUNET_CONVERSATION_EC_CALL_PICKED_UP event is generated); we will NOT generate
546 * a ring tone on the speaker
547 * @param mic microphone to use (will be used automatically immediately once the
548 * #GNUNET_CONVERSATION_EC_CALL_PICKED_UP event is generated)
549 * @param event_handler how to notify the owner of the phone about events
550 * @param event_handler_cls closure for @a event_handler
552 struct GNUNET_CONVERSATION_Call *
553 GNUNET_CONVERSATION_call_start (const struct GNUNET_CONFIGURATION_Handle *cfg,
554 struct GNUNET_IDENTITY_Ego *caller_id,
556 struct GNUNET_SPEAKER_Handle *speaker,
557 struct GNUNET_MICROPHONE_Handle *mic,
558 GNUNET_CONVERSATION_CallEventHandler event_handler,
559 void *event_handler_cls)
561 struct GNUNET_CONVERSATION_Call *call;
563 call = GNUNET_new (struct GNUNET_CONVERSATION_Call);
565 call->caller_id = caller_id;
566 call->callee = GNUNET_strdup (callee);
567 call->speaker = speaker;
569 call->event_handler = event_handler;
570 call->event_handler_cls = event_handler_cls;
571 call->gns = GNUNET_GNS_connect (cfg);
572 reconnect_call (call);
574 if ( (NULL == call->client) ||
575 (NULL == call->gns) )
577 GNUNET_CONVERSATION_call_stop (call);
585 * We've sent the hang up message, now finish terminating the call.
587 * @param cls the `struct GNUNET_CONVERSATION_Call` to terminate
590 finish_stop (void *cls)
592 struct GNUNET_CONVERSATION_Call *call = cls;
594 GNUNET_assert (CS_SHUTDOWN == call->state);
595 GNUNET_CONVERSATION_call_stop (call);
600 * Terminate a call. The call may be ringing or ready at this time.
602 * @param call call to terminate
605 GNUNET_CONVERSATION_call_stop (struct GNUNET_CONVERSATION_Call *call)
607 struct GNUNET_MQ_Envelope *e;
608 struct ClientPhoneHangupMessage *hang;
610 if ( (NULL != call->speaker) &&
611 (CS_ACTIVE == call->state) )
612 call->speaker->disable_speaker (call->speaker->cls);
613 if ( (NULL != call->mic) &&
614 (CS_ACTIVE == call->state) )
615 call->mic->disable_microphone (call->mic->cls);
616 if (CS_SHUTDOWN != call->state)
618 call->state = CS_SHUTDOWN;
619 e = GNUNET_MQ_msg (hang, GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP);
620 GNUNET_MQ_notify_sent (e, &finish_stop, call);
621 GNUNET_MQ_send (call->mq, e);
624 if (NULL != call->mq)
626 GNUNET_MQ_destroy (call->mq);
629 if (NULL != call->client)
631 GNUNET_CLIENT_disconnect (call->client);
634 if (NULL != call->gns_lookup)
636 GNUNET_GNS_lookup_cancel (call->gns_lookup);
637 call->gns_lookup = NULL;
639 if (NULL != call->gns)
641 GNUNET_GNS_disconnect (call->gns);
644 GNUNET_free (call->callee);
650 * Pause a call. Temporarily suspends the use of speaker and
653 * @param call call to pause
656 GNUNET_CONVERSATION_call_suspend (struct GNUNET_CONVERSATION_Call *call)
658 struct GNUNET_MQ_Envelope *e;
659 struct ClientPhoneSuspendMessage *suspend;
661 GNUNET_assert ( (CS_SUSPENDED_CALLEE == call->state) ||
662 (CS_ACTIVE == call->state) );
663 if (CS_ACTIVE == call->state)
665 call->speaker->disable_speaker (call->speaker->cls);
666 call->mic->disable_microphone (call->mic->cls);
668 call->speaker = NULL;
670 e = GNUNET_MQ_msg (suspend, GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_SUSPEND);
671 GNUNET_MQ_send (call->mq, e);
672 if (CS_SUSPENDED_CALLER == call->state)
673 call->state = CS_SUSPENDED_BOTH;
675 call->state = CS_SUSPENDED_CALLER;
680 * Resumes a call after #GNUNET_CONVERSATION_call_suspend.
682 * @param call call to resume
683 * @param speaker speaker to use
684 * a ring tone on the speaker
685 * @param mic microphone to use
688 GNUNET_CONVERSATION_call_resume (struct GNUNET_CONVERSATION_Call *call,
689 struct GNUNET_SPEAKER_Handle *speaker,
690 struct GNUNET_MICROPHONE_Handle *mic)
692 struct GNUNET_MQ_Envelope *e;
693 struct ClientPhoneResumeMessage *resume;
695 GNUNET_assert ( (CS_SUSPENDED_CALLER == call->state) ||
696 (CS_SUSPENDED_BOTH == call->state) );
697 e = GNUNET_MQ_msg (resume, GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_RESUME);
698 GNUNET_MQ_send (call->mq, e);
699 call->speaker = speaker;
701 if (CS_SUSPENDED_CALLER == call->state)
703 call->state = CS_ACTIVE;
704 call->speaker->enable_speaker (call->speaker->cls);
705 call->mic->enable_microphone (call->mic->cls,
706 &transmit_call_audio,
711 call->state = CS_SUSPENDED_CALLEE;
716 /* end of conversation_api_call.c */