2 This file is part of GNUnet
3 Copyright (C) 2013, 2016 GNUnet e.V.
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., 51 Franklin Street, Fifth Floor,
18 Boston, MA 02110-1301, 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 * Our caller identity.
91 struct GNUNET_IDENTITY_Ego *caller_id;
94 * GNS zone to use to resolve @e callee.
96 struct GNUNET_IDENTITY_Ego *zone_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 fail_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 GNUNET_memcpy (&am[1],
183 GNUNET_MQ_send (call->mq,
189 * We received a #GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_SUSPEND.
191 * @param cls the `struct GNUNET_CONVERSATION_Call`
192 * @param msg the message
195 handle_call_suspend (void *cls,
196 const struct ClientPhoneSuspendMessage *msg)
198 struct GNUNET_CONVERSATION_Call *call = cls;
210 case CS_SUSPENDED_CALLER:
211 call->state = CS_SUSPENDED_BOTH;
212 call->event_handler (call->event_handler_cls,
213 GNUNET_CONVERSATION_EC_CALL_SUSPENDED);
215 case CS_SUSPENDED_CALLEE:
216 case CS_SUSPENDED_BOTH:
220 call->state = CS_SUSPENDED_CALLEE;
221 call->speaker->disable_speaker (call->speaker->cls);
222 call->mic->disable_microphone (call->mic->cls);
223 call->event_handler (call->event_handler_cls,
224 GNUNET_CONVERSATION_EC_CALL_SUSPENDED);
227 GNUNET_CONVERSATION_call_stop (call);
234 * We received a #GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_RESUME.
236 * @param cls the `struct GNUNET_CONVERSATION_Call`
237 * @param msg the message
240 handle_call_resume (void *cls,
241 const struct ClientPhoneResumeMessage *msg)
243 struct GNUNET_CONVERSATION_Call *call = cls;
255 case CS_SUSPENDED_CALLER:
258 case CS_SUSPENDED_CALLEE:
259 call->state = CS_ACTIVE;
260 call->speaker->enable_speaker (call->speaker->cls);
261 call->mic->enable_microphone (call->mic->cls,
262 &transmit_call_audio,
264 call->event_handler (call->event_handler_cls,
265 GNUNET_CONVERSATION_EC_CALL_RESUMED);
267 case CS_SUSPENDED_BOTH:
268 call->state = CS_SUSPENDED_CALLER;
269 call->event_handler (call->event_handler_cls,
270 GNUNET_CONVERSATION_EC_CALL_RESUMED);
276 GNUNET_CONVERSATION_call_stop (call);
283 * We received a #GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_PICKED_UP.
285 * @param cls the `struct GNUNET_CONVERSATION_Call`
286 * @param msg the message
289 handle_call_picked_up (void *cls,
290 const struct ClientPhonePickedupMessage *msg)
292 struct GNUNET_CONVERSATION_Call *call = cls;
301 call->state = CS_ACTIVE;
302 call->speaker->enable_speaker (call->speaker->cls);
303 call->mic->enable_microphone (call->mic->cls,
304 &transmit_call_audio,
306 call->event_handler (call->event_handler_cls,
307 GNUNET_CONVERSATION_EC_CALL_PICKED_UP);
309 case CS_SUSPENDED_CALLER:
310 case CS_SUSPENDED_CALLEE:
311 case CS_SUSPENDED_BOTH:
317 GNUNET_CONVERSATION_call_stop (call);
324 * We received a #GNUNET_MESSAGE_TYPE_CONVERSATION_CS_HANG_UP.
326 * @param cls the `struct GNUNET_CONVERSATION_Call`
327 * @param msg the message
330 handle_call_hangup (void *cls,
331 const struct ClientPhoneHangupMessage *msg)
333 struct GNUNET_CONVERSATION_Call *call = cls;
334 GNUNET_CONVERSATION_CallEventHandler eh;
344 case CS_SUSPENDED_CALLER:
345 case CS_SUSPENDED_CALLEE:
346 case CS_SUSPENDED_BOTH:
348 eh = call->event_handler;
349 eh_cls = call->event_handler_cls;
350 GNUNET_CONVERSATION_call_stop (call);
352 GNUNET_CONVERSATION_EC_CALL_HUNG_UP);
355 GNUNET_CONVERSATION_call_stop (call);
362 * We received a `struct ClientAudioMessage`, check it is well-formed.
364 * @param cls the `struct GNUNET_CONVERSATION_Call`
365 * @param msg the message
366 * @return #GNUNET_OK (always well-formed)
369 check_call_audio (void *cls,
370 const struct ClientAudioMessage *am)
372 /* any payload is OK */
378 * We received a `struct ClientAudioMessage`
380 * @param cls the `struct GNUNET_CONVERSATION_Call`
381 * @param msg the message
384 handle_call_audio (void *cls,
385 const struct ClientAudioMessage *am)
387 struct GNUNET_CONVERSATION_Call *call = cls;
399 case CS_SUSPENDED_CALLER:
400 /* can happen: we suspended, other peer did not yet
403 case CS_SUSPENDED_CALLEE:
404 case CS_SUSPENDED_BOTH:
405 /* can (rarely) also happen: other peer suspended, but cadet might
406 have had delayed data on the unreliable channel */
409 call->speaker->play (call->speaker->cls,
410 ntohs (am->header.size) - sizeof (struct ClientAudioMessage),
414 GNUNET_CONVERSATION_call_stop (call);
421 * Iterator called on obtained result for a GNS lookup.
423 * @param cls closure with the `struct GNUNET_CONVERSATION_Call`
424 * @param rd_count number of records in @a rd
425 * @param rd the records in reply
428 handle_gns_response (void *cls,
430 const struct GNUNET_GNSRECORD_Data *rd)
432 struct GNUNET_CONVERSATION_Call *call = cls;
434 struct GNUNET_MQ_Envelope *e;
435 struct ClientCallMessage *ccm;
437 GNUNET_break (NULL != call->gns_lookup);
438 GNUNET_break (CS_LOOKUP == call->state);
439 call->gns_lookup = NULL;
440 for (i=0;i<rd_count;i++)
442 if (GNUNET_GNSRECORD_TYPE_PHONE == rd[i].record_type)
444 if (rd[i].data_size != sizeof (struct GNUNET_CONVERSATION_PhoneRecord))
449 GNUNET_memcpy (&call->phone_record,
452 e = GNUNET_MQ_msg (ccm,
453 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_CALL);
454 ccm->line_port = call->phone_record.line_port;
455 ccm->target = call->phone_record.peer;
456 ccm->caller_id = *GNUNET_IDENTITY_ego_get_private_key (call->caller_id);
457 GNUNET_MQ_send (call->mq,
459 call->state = CS_RINGING;
460 call->event_handler (call->event_handler_cls,
461 GNUNET_CONVERSATION_EC_CALL_RINGING);
466 call->event_handler (call->event_handler_cls,
467 GNUNET_CONVERSATION_EC_CALL_GNS_FAIL);
468 GNUNET_CONVERSATION_call_stop (call);
473 * We encountered an error talking with the conversation service.
475 * @param cls the `struct GNUNET_CONVERSATION_Call`
476 * @param error details about the error
479 call_error_handler (void *cls,
480 enum GNUNET_MQ_Error error)
482 struct GNUNET_CONVERSATION_Call *call = cls;
484 if (CS_SHUTDOWN == call->state)
486 GNUNET_CONVERSATION_call_stop (call);
489 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
490 _("Connection to conversation service lost, trying to reconnect\n"));
496 * The call got disconnected, destroy the handle.
498 * @param call call to reconnect
501 fail_call (struct GNUNET_CONVERSATION_Call *call)
503 if (CS_ACTIVE == call->state)
505 call->speaker->disable_speaker (call->speaker->cls);
506 call->mic->disable_microphone (call->mic->cls);
508 if (NULL != call->mq)
510 GNUNET_MQ_destroy (call->mq);
513 call->state = CS_SHUTDOWN;
514 call->event_handler (call->event_handler_cls,
515 GNUNET_CONVERSATION_EC_CALL_ERROR);
516 GNUNET_CONVERSATION_call_stop (call);
521 * Call the phone of another user.
523 * @param cfg configuration to use, specifies our phone service
524 * @param caller_id identity of the caller
525 * @param zone_id GNS zone to use to resolve @a callee
526 * @param callee GNS name of the callee (used to locate the callee's record)
527 * @param speaker speaker to use (will be used automatically immediately once the
528 * #GNUNET_CONVERSATION_EC_CALL_PICKED_UP event is generated); we will NOT generate
529 * a ring tone on the speaker
530 * @param mic microphone to use (will be used automatically immediately once the
531 * #GNUNET_CONVERSATION_EC_CALL_PICKED_UP event is generated)
532 * @param event_handler how to notify the owner of the phone about events
533 * @param event_handler_cls closure for @a event_handler
534 * @return handle for the call, NULL on hard errors
536 struct GNUNET_CONVERSATION_Call *
537 GNUNET_CONVERSATION_call_start (const struct GNUNET_CONFIGURATION_Handle *cfg,
538 struct GNUNET_IDENTITY_Ego *caller_id,
539 struct GNUNET_IDENTITY_Ego *zone_id,
541 struct GNUNET_SPEAKER_Handle *speaker,
542 struct GNUNET_MICROPHONE_Handle *mic,
543 GNUNET_CONVERSATION_CallEventHandler event_handler,
544 void *event_handler_cls)
546 struct GNUNET_CONVERSATION_Call *call
547 = GNUNET_new (struct GNUNET_CONVERSATION_Call);
548 struct GNUNET_MQ_MessageHandler handlers[] = {
549 GNUNET_MQ_hd_fixed_size (call_suspend,
550 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_SUSPEND,
551 struct ClientPhoneSuspendMessage,
553 GNUNET_MQ_hd_fixed_size (call_resume,
554 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_RESUME,
555 struct ClientPhoneResumeMessage,
557 GNUNET_MQ_hd_fixed_size (call_picked_up,
558 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_PICKED_UP,
559 struct ClientPhonePickedupMessage,
561 GNUNET_MQ_hd_fixed_size (call_hangup,
562 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP,
563 struct ClientPhoneHangupMessage,
565 GNUNET_MQ_hd_var_size (call_audio,
566 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO,
567 struct ClientAudioMessage,
569 GNUNET_MQ_handler_end ()
571 struct GNUNET_CRYPTO_EcdsaPublicKey my_zone;
573 call->mq = GNUNET_CLIENT_connecT (cfg,
578 if (NULL == call->mq)
585 call->caller_id = caller_id;
586 call->zone_id = zone_id;
587 call->callee = GNUNET_strdup (callee);
588 call->speaker = speaker;
590 call->event_handler = event_handler;
591 call->event_handler_cls = event_handler_cls;
592 call->gns = GNUNET_GNS_connect (cfg);
593 if (NULL == call->gns)
595 GNUNET_CONVERSATION_call_stop (call);
598 call->state = CS_LOOKUP;
599 GNUNET_IDENTITY_ego_get_public_key (call->zone_id,
601 call->gns_lookup = GNUNET_GNS_lookup (call->gns,
604 GNUNET_GNSRECORD_TYPE_PHONE,
606 NULL /* FIXME: add shortening support */,
607 &handle_gns_response, call);
608 GNUNET_assert (NULL != call->gns_lookup);
614 * Terminate a call. The call may be ringing or ready at this time.
616 * @param call call to terminate
619 GNUNET_CONVERSATION_call_stop (struct GNUNET_CONVERSATION_Call *call)
621 if ( (NULL != call->speaker) &&
622 (CS_ACTIVE == call->state) )
623 call->speaker->disable_speaker (call->speaker->cls);
624 if ( (NULL != call->mic) &&
625 (CS_ACTIVE == call->state) )
626 call->mic->disable_microphone (call->mic->cls);
627 if (CS_SHUTDOWN != call->state)
629 call->state = CS_SHUTDOWN;
631 if (NULL != call->mq)
633 GNUNET_MQ_destroy (call->mq);
636 if (NULL != call->gns_lookup)
638 GNUNET_GNS_lookup_cancel (call->gns_lookup);
639 call->gns_lookup = NULL;
641 if (NULL != call->gns)
643 GNUNET_GNS_disconnect (call->gns);
646 GNUNET_free (call->callee);
652 * Pause a call. Temporarily suspends the use of speaker and
655 * @param call call to pause
658 GNUNET_CONVERSATION_call_suspend (struct GNUNET_CONVERSATION_Call *call)
660 struct GNUNET_MQ_Envelope *e;
661 struct ClientPhoneSuspendMessage *suspend;
663 GNUNET_assert ( (CS_SUSPENDED_CALLEE == call->state) ||
664 (CS_ACTIVE == call->state) );
665 if (CS_ACTIVE == call->state)
667 call->speaker->disable_speaker (call->speaker->cls);
668 call->mic->disable_microphone (call->mic->cls);
670 call->speaker = NULL;
672 e = GNUNET_MQ_msg (suspend,
673 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_SUSPEND);
674 GNUNET_MQ_send (call->mq,
676 if (CS_SUSPENDED_CALLER == call->state)
677 call->state = CS_SUSPENDED_BOTH;
679 call->state = CS_SUSPENDED_CALLER;
684 * Resumes a call after #GNUNET_CONVERSATION_call_suspend.
686 * @param call call to resume
687 * @param speaker speaker to use
688 * a ring tone on the speaker
689 * @param mic microphone to use
692 GNUNET_CONVERSATION_call_resume (struct GNUNET_CONVERSATION_Call *call,
693 struct GNUNET_SPEAKER_Handle *speaker,
694 struct GNUNET_MICROPHONE_Handle *mic)
696 struct GNUNET_MQ_Envelope *e;
697 struct ClientPhoneResumeMessage *resume;
699 GNUNET_assert ( (CS_SUSPENDED_CALLER == call->state) ||
700 (CS_SUSPENDED_BOTH == call->state) );
701 e = GNUNET_MQ_msg (resume, GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_RESUME);
702 GNUNET_MQ_send (call->mq, e);
703 call->speaker = speaker;
705 if (CS_SUSPENDED_CALLER == call->state)
707 call->state = CS_ACTIVE;
708 call->speaker->enable_speaker (call->speaker->cls);
709 call->mic->enable_microphone (call->mic->cls,
710 &transmit_call_audio,
715 call->state = CS_SUSPENDED_CALLEE;
720 /* end of conversation_api_call.c */