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 it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your 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 Affero General Public License for more details.
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
18 SPDX-License-Identifier: AGPL3.0-or-later
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 * Target callee as a GNS address/name.
101 struct GNUNET_SPEAKER_Handle *speaker;
106 struct GNUNET_MICROPHONE_Handle *mic;
109 * Function to call with events.
111 GNUNET_CONVERSATION_CallEventHandler event_handler;
114 * Closure for @e event_handler
116 void *event_handler_cls;
119 * Handle for transmitting to the CONVERSATION service.
121 struct GNUNET_MQ_Handle *mq;
124 * Connection to GNS (can be NULL).
126 struct GNUNET_GNS_Handle *gns;
129 * Active GNS lookup (or NULL).
131 struct GNUNET_GNS_LookupWithTldRequest *gns_lookup;
134 * Target phone record, only valid after the lookup is done.
136 struct GNUNET_CONVERSATION_PhoneRecord phone_record;
139 * State machine for the call.
141 enum CallState state;
147 * The call got disconnected, reconnect to the service.
149 * @param call call to reconnect
152 fail_call (struct GNUNET_CONVERSATION_Call *call);
156 * Process recorded audio data.
158 * @param cls closure with the `struct GNUNET_CONVERSATION_Call`
159 * @param data_size number of bytes in @a data
160 * @param data audio data to play
163 transmit_call_audio (void *cls,
167 struct GNUNET_CONVERSATION_Call *call = cls;
168 struct GNUNET_MQ_Envelope *e;
169 struct ClientAudioMessage *am;
171 GNUNET_assert (CS_ACTIVE == call->state);
172 e = GNUNET_MQ_msg_extra (am,
174 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO);
175 GNUNET_memcpy (&am[1],
178 GNUNET_MQ_send (call->mq,
184 * We received a #GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_SUSPEND.
186 * @param cls the `struct GNUNET_CONVERSATION_Call`
187 * @param msg the message
190 handle_call_suspend (void *cls,
191 const struct ClientPhoneSuspendMessage *msg)
193 struct GNUNET_CONVERSATION_Call *call = cls;
206 case CS_SUSPENDED_CALLER:
207 call->state = CS_SUSPENDED_BOTH;
208 call->event_handler (call->event_handler_cls,
209 GNUNET_CONVERSATION_EC_CALL_SUSPENDED);
211 case CS_SUSPENDED_CALLEE:
212 case CS_SUSPENDED_BOTH:
216 call->state = CS_SUSPENDED_CALLEE;
217 call->speaker->disable_speaker (call->speaker->cls);
218 call->mic->disable_microphone (call->mic->cls);
219 call->event_handler (call->event_handler_cls,
220 GNUNET_CONVERSATION_EC_CALL_SUSPENDED);
223 GNUNET_CONVERSATION_call_stop (call);
230 * We received a #GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_RESUME.
232 * @param cls the `struct GNUNET_CONVERSATION_Call`
233 * @param msg the message
236 handle_call_resume (void *cls,
237 const struct ClientPhoneResumeMessage *msg)
239 struct GNUNET_CONVERSATION_Call *call = cls;
252 case CS_SUSPENDED_CALLER:
255 case CS_SUSPENDED_CALLEE:
256 call->state = CS_ACTIVE;
257 call->speaker->enable_speaker (call->speaker->cls);
258 call->mic->enable_microphone (call->mic->cls,
259 &transmit_call_audio,
261 call->event_handler (call->event_handler_cls,
262 GNUNET_CONVERSATION_EC_CALL_RESUMED);
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 #GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_PICKED_UP.
282 * @param cls the `struct GNUNET_CONVERSATION_Call`
283 * @param msg the message
286 handle_call_picked_up (void *cls,
287 const struct ClientPhonePickedupMessage *msg)
289 struct GNUNET_CONVERSATION_Call *call = cls;
299 call->state = CS_ACTIVE;
300 call->speaker->enable_speaker (call->speaker->cls);
301 call->mic->enable_microphone (call->mic->cls,
302 &transmit_call_audio,
304 call->event_handler (call->event_handler_cls,
305 GNUNET_CONVERSATION_EC_CALL_PICKED_UP);
307 case CS_SUSPENDED_CALLER:
308 case CS_SUSPENDED_CALLEE:
309 case CS_SUSPENDED_BOTH:
315 GNUNET_CONVERSATION_call_stop (call);
322 * We received a #GNUNET_MESSAGE_TYPE_CONVERSATION_CS_HANG_UP.
324 * @param cls the `struct GNUNET_CONVERSATION_Call`
325 * @param msg the message
328 handle_call_hangup (void *cls,
329 const struct ClientPhoneHangupMessage *msg)
331 struct GNUNET_CONVERSATION_Call *call = cls;
332 GNUNET_CONVERSATION_CallEventHandler eh;
343 case CS_SUSPENDED_CALLER:
344 case CS_SUSPENDED_CALLEE:
345 case CS_SUSPENDED_BOTH:
347 eh = call->event_handler;
348 eh_cls = call->event_handler_cls;
349 GNUNET_CONVERSATION_call_stop (call);
351 GNUNET_CONVERSATION_EC_CALL_HUNG_UP);
354 GNUNET_CONVERSATION_call_stop (call);
361 * We received a `struct ClientAudioMessage`, check it is well-formed.
363 * @param cls the `struct GNUNET_CONVERSATION_Call`
364 * @param msg the message
365 * @return #GNUNET_OK (always well-formed)
368 check_call_audio (void *cls,
369 const struct ClientAudioMessage *am)
373 /* any payload is OK */
379 * We received a `struct ClientAudioMessage`
381 * @param cls the `struct GNUNET_CONVERSATION_Call`
382 * @param msg the message
385 handle_call_audio (void *cls,
386 const struct ClientAudioMessage *am)
388 struct GNUNET_CONVERSATION_Call *call = cls;
400 case CS_SUSPENDED_CALLER:
401 /* can happen: we suspended, other peer did not yet
404 case CS_SUSPENDED_CALLEE:
405 case CS_SUSPENDED_BOTH:
406 /* can (rarely) also happen: other peer suspended, but cadet might
407 have had delayed data on the unreliable channel */
410 call->speaker->play (call->speaker->cls,
411 ntohs (am->header.size) - sizeof (struct ClientAudioMessage),
415 GNUNET_CONVERSATION_call_stop (call);
422 * Iterator called on obtained result for a GNS lookup.
424 * @param cls closure with the `struct GNUNET_CONVERSATION_Call`
425 * @param was_gns #GNUNET_NO if name was not a GNS name
426 * @param rd_count number of records in @a rd
427 * @param rd the records in reply
430 handle_gns_response (void *cls,
433 const struct GNUNET_GNSRECORD_Data *rd)
435 struct GNUNET_CONVERSATION_Call *call = cls;
436 struct GNUNET_MQ_Envelope *e;
437 struct ClientCallMessage *ccm;
440 GNUNET_break (NULL != call->gns_lookup);
441 GNUNET_break (CS_LOOKUP == call->state);
442 call->gns_lookup = NULL;
443 for (uint32_t i=0;i<rd_count;i++)
445 if (GNUNET_GNSRECORD_TYPE_PHONE == rd[i].record_type)
447 if (rd[i].data_size != sizeof (struct GNUNET_CONVERSATION_PhoneRecord))
452 GNUNET_memcpy (&call->phone_record,
455 e = GNUNET_MQ_msg (ccm,
456 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_CALL);
457 ccm->line_port = call->phone_record.line_port;
458 ccm->target = call->phone_record.peer;
459 ccm->caller_id = *GNUNET_IDENTITY_ego_get_private_key (call->caller_id);
460 GNUNET_MQ_send (call->mq,
462 call->state = CS_RINGING;
463 call->event_handler (call->event_handler_cls,
464 GNUNET_CONVERSATION_EC_CALL_RINGING);
469 call->event_handler (call->event_handler_cls,
470 GNUNET_CONVERSATION_EC_CALL_GNS_FAIL);
471 GNUNET_CONVERSATION_call_stop (call);
476 * We encountered an error talking with the conversation service.
478 * @param cls the `struct GNUNET_CONVERSATION_Call`
479 * @param error details about the error
482 call_error_handler (void *cls,
483 enum GNUNET_MQ_Error error)
485 struct GNUNET_CONVERSATION_Call *call = cls;
488 if (CS_SHUTDOWN == call->state)
490 GNUNET_CONVERSATION_call_stop (call);
493 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
494 _("Connection to conversation service lost, trying to reconnect\n"));
500 * The call got disconnected, destroy the handle.
502 * @param call call to reconnect
505 fail_call (struct GNUNET_CONVERSATION_Call *call)
507 if (CS_ACTIVE == call->state)
509 call->speaker->disable_speaker (call->speaker->cls);
510 call->mic->disable_microphone (call->mic->cls);
512 if (NULL != call->mq)
514 GNUNET_MQ_destroy (call->mq);
517 call->state = CS_SHUTDOWN;
518 call->event_handler (call->event_handler_cls,
519 GNUNET_CONVERSATION_EC_CALL_ERROR);
520 GNUNET_CONVERSATION_call_stop (call);
525 * Call the phone of another user.
527 * @param cfg configuration to use, specifies our phone service
528 * @param caller_id identity of the caller
529 * @param callee GNS name of the callee (used to locate the callee's record)
530 * @param speaker speaker to use (will be used automatically immediately once the
531 * #GNUNET_CONVERSATION_EC_CALL_PICKED_UP event is generated); we will NOT generate
532 * a ring tone on the speaker
533 * @param mic microphone to use (will be used automatically immediately once the
534 * #GNUNET_CONVERSATION_EC_CALL_PICKED_UP event is generated)
535 * @param event_handler how to notify the owner of the phone about events
536 * @param event_handler_cls closure for @a event_handler
537 * @return handle for the call, NULL on hard errors
539 struct GNUNET_CONVERSATION_Call *
540 GNUNET_CONVERSATION_call_start (const struct GNUNET_CONFIGURATION_Handle *cfg,
541 struct GNUNET_IDENTITY_Ego *caller_id,
543 struct GNUNET_SPEAKER_Handle *speaker,
544 struct GNUNET_MICROPHONE_Handle *mic,
545 GNUNET_CONVERSATION_CallEventHandler event_handler,
546 void *event_handler_cls)
548 struct GNUNET_CONVERSATION_Call *call
549 = GNUNET_new (struct GNUNET_CONVERSATION_Call);
550 struct GNUNET_MQ_MessageHandler handlers[] = {
551 GNUNET_MQ_hd_fixed_size (call_suspend,
552 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_SUSPEND,
553 struct ClientPhoneSuspendMessage,
555 GNUNET_MQ_hd_fixed_size (call_resume,
556 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_RESUME,
557 struct ClientPhoneResumeMessage,
559 GNUNET_MQ_hd_fixed_size (call_picked_up,
560 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_PICKED_UP,
561 struct ClientPhonePickedupMessage,
563 GNUNET_MQ_hd_fixed_size (call_hangup,
564 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP,
565 struct ClientPhoneHangupMessage,
567 GNUNET_MQ_hd_var_size (call_audio,
568 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO,
569 struct ClientAudioMessage,
571 GNUNET_MQ_handler_end ()
574 call->mq = GNUNET_CLIENT_connect (cfg,
579 if (NULL == call->mq)
586 call->caller_id = caller_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 call->gns_lookup = GNUNET_GNS_lookup_with_tld (call->gns,
601 GNUNET_GNSRECORD_TYPE_PHONE,
603 &handle_gns_response,
605 if (NULL == call->gns_lookup)
607 GNUNET_CONVERSATION_call_stop (call);
615 * Terminate a call. The call may be ringing or ready at this time.
617 * @param call call to terminate
620 GNUNET_CONVERSATION_call_stop (struct GNUNET_CONVERSATION_Call *call)
622 if ( (NULL != call->speaker) &&
623 (CS_ACTIVE == call->state) )
624 call->speaker->disable_speaker (call->speaker->cls);
625 if ( (NULL != call->mic) &&
626 (CS_ACTIVE == call->state) )
627 call->mic->disable_microphone (call->mic->cls);
628 if (CS_SHUTDOWN != call->state)
630 call->state = CS_SHUTDOWN;
632 if (NULL != call->mq)
634 GNUNET_MQ_destroy (call->mq);
637 if (NULL != call->gns_lookup)
639 GNUNET_GNS_lookup_with_tld_cancel (call->gns_lookup);
640 call->gns_lookup = NULL;
642 if (NULL != call->gns)
644 GNUNET_GNS_disconnect (call->gns);
647 GNUNET_free (call->callee);
653 * Pause a call. Temporarily suspends the use of speaker and
656 * @param call call to pause
659 GNUNET_CONVERSATION_call_suspend (struct GNUNET_CONVERSATION_Call *call)
661 struct GNUNET_MQ_Envelope *e;
662 struct ClientPhoneSuspendMessage *suspend;
664 GNUNET_assert ( (CS_SUSPENDED_CALLEE == call->state) ||
665 (CS_ACTIVE == call->state) );
666 if (CS_ACTIVE == call->state)
668 call->speaker->disable_speaker (call->speaker->cls);
669 call->mic->disable_microphone (call->mic->cls);
671 call->speaker = NULL;
673 e = GNUNET_MQ_msg (suspend,
674 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_SUSPEND);
675 GNUNET_MQ_send (call->mq,
677 if (CS_SUSPENDED_CALLER == call->state)
678 call->state = CS_SUSPENDED_BOTH;
680 call->state = CS_SUSPENDED_CALLER;
685 * Resumes a call after #GNUNET_CONVERSATION_call_suspend.
687 * @param call call to resume
688 * @param speaker speaker to use
689 * a ring tone on the speaker
690 * @param mic microphone to use
693 GNUNET_CONVERSATION_call_resume (struct GNUNET_CONVERSATION_Call *call,
694 struct GNUNET_SPEAKER_Handle *speaker,
695 struct GNUNET_MICROPHONE_Handle *mic)
697 struct GNUNET_MQ_Envelope *e;
698 struct ClientPhoneResumeMessage *resume;
700 GNUNET_assert ( (CS_SUSPENDED_CALLER == call->state) ||
701 (CS_SUSPENDED_BOTH == call->state) );
702 e = GNUNET_MQ_msg (resume, GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_RESUME);
703 GNUNET_MQ_send (call->mq, e);
704 call->speaker = speaker;
706 if (CS_SUSPENDED_CALLER == call->state)
708 call->state = CS_ACTIVE;
709 call->speaker->enable_speaker (call->speaker->cls);
710 call->mic->enable_microphone (call->mic->cls,
711 &transmit_call_audio,
716 call->state = CS_SUSPENDED_CALLEE;
721 /* end of conversation_api_call.c */