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
85 const struct GNUNET_CONFIGURATION_Handle *cfg;
88 * Our caller identity.
90 struct GNUNET_IDENTITY_Ego *caller_id;
93 * Target callee as a GNS address/name.
100 struct GNUNET_SPEAKER_Handle *speaker;
105 struct GNUNET_MICROPHONE_Handle *mic;
108 * Function to call with events.
110 GNUNET_CONVERSATION_CallEventHandler event_handler;
113 * Closure for @e event_handler
115 void *event_handler_cls;
118 * Handle for transmitting to the CONVERSATION service.
120 struct GNUNET_MQ_Handle *mq;
123 * Connection to GNS (can be NULL).
125 struct GNUNET_GNS_Handle *gns;
128 * Active GNS lookup (or NULL).
130 struct GNUNET_GNS_LookupWithTldRequest *gns_lookup;
133 * Target phone record, only valid after the lookup is done.
135 struct GNUNET_CONVERSATION_PhoneRecord phone_record;
138 * State machine for the call.
140 enum CallState state;
145 * The call got disconnected, reconnect to the service.
147 * @param call call to reconnect
150 fail_call (struct GNUNET_CONVERSATION_Call *call);
154 * Process recorded audio data.
156 * @param cls closure with the `struct GNUNET_CONVERSATION_Call`
157 * @param data_size number of bytes in @a data
158 * @param data audio data to play
161 transmit_call_audio (void *cls,
165 struct GNUNET_CONVERSATION_Call *call = cls;
166 struct GNUNET_MQ_Envelope *e;
167 struct ClientAudioMessage *am;
169 GNUNET_assert (CS_ACTIVE == call->state);
170 e = GNUNET_MQ_msg_extra (am,
172 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO);
173 GNUNET_memcpy (&am[1],
176 GNUNET_MQ_send (call->mq,
182 * We received a #GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_SUSPEND.
184 * @param cls the `struct GNUNET_CONVERSATION_Call`
185 * @param msg the message
188 handle_call_suspend (void *cls,
189 const struct ClientPhoneSuspendMessage *msg)
191 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);
212 case CS_SUSPENDED_CALLEE:
213 case CS_SUSPENDED_BOTH:
218 call->state = CS_SUSPENDED_CALLEE;
219 call->speaker->disable_speaker (call->speaker->cls);
220 call->mic->disable_microphone (call->mic->cls);
221 call->event_handler (call->event_handler_cls,
222 GNUNET_CONVERSATION_EC_CALL_SUSPENDED);
226 GNUNET_CONVERSATION_call_stop (call);
233 * We received a #GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_RESUME.
235 * @param cls the `struct GNUNET_CONVERSATION_Call`
236 * @param msg the message
239 handle_call_resume (void *cls,
240 const struct ClientPhoneResumeMessage *msg)
242 struct GNUNET_CONVERSATION_Call *call = cls;
257 case CS_SUSPENDED_CALLER:
261 case CS_SUSPENDED_CALLEE:
262 call->state = CS_ACTIVE;
263 call->speaker->enable_speaker (call->speaker->cls);
264 call->mic->enable_microphone (call->mic->cls,
265 &transmit_call_audio,
267 call->event_handler (call->event_handler_cls,
268 GNUNET_CONVERSATION_EC_CALL_RESUMED);
271 case CS_SUSPENDED_BOTH:
272 call->state = CS_SUSPENDED_CALLER;
273 call->event_handler (call->event_handler_cls,
274 GNUNET_CONVERSATION_EC_CALL_RESUMED);
282 GNUNET_CONVERSATION_call_stop (call);
289 * We received a #GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_PICKED_UP.
291 * @param cls the `struct GNUNET_CONVERSATION_Call`
292 * @param msg the message
295 handle_call_picked_up (void *cls,
296 const struct ClientPhonePickedupMessage *msg)
298 struct GNUNET_CONVERSATION_Call *call = cls;
309 call->state = CS_ACTIVE;
310 call->speaker->enable_speaker (call->speaker->cls);
311 call->mic->enable_microphone (call->mic->cls,
312 &transmit_call_audio,
314 call->event_handler (call->event_handler_cls,
315 GNUNET_CONVERSATION_EC_CALL_PICKED_UP);
318 case CS_SUSPENDED_CALLER:
319 case CS_SUSPENDED_CALLEE:
320 case CS_SUSPENDED_BOTH:
327 GNUNET_CONVERSATION_call_stop (call);
334 * We received a #GNUNET_MESSAGE_TYPE_CONVERSATION_CS_HANG_UP.
336 * @param cls the `struct GNUNET_CONVERSATION_Call`
337 * @param msg the message
340 handle_call_hangup (void *cls,
341 const struct ClientPhoneHangupMessage *msg)
343 struct GNUNET_CONVERSATION_Call *call = cls;
344 GNUNET_CONVERSATION_CallEventHandler eh;
356 case CS_SUSPENDED_CALLER:
357 case CS_SUSPENDED_CALLEE:
358 case CS_SUSPENDED_BOTH:
360 eh = call->event_handler;
361 eh_cls = call->event_handler_cls;
362 GNUNET_CONVERSATION_call_stop (call);
364 GNUNET_CONVERSATION_EC_CALL_HUNG_UP);
368 GNUNET_CONVERSATION_call_stop (call);
375 * We received a `struct ClientAudioMessage`, check it is well-formed.
377 * @param cls the `struct GNUNET_CONVERSATION_Call`
378 * @param msg the message
379 * @return #GNUNET_OK (always well-formed)
382 check_call_audio (void *cls,
383 const struct ClientAudioMessage *am)
387 /* any payload is OK */
393 * We received a `struct ClientAudioMessage`
395 * @param cls the `struct GNUNET_CONVERSATION_Call`
396 * @param msg the message
399 handle_call_audio (void *cls,
400 const struct ClientAudioMessage *am)
402 struct GNUNET_CONVERSATION_Call *call = cls;
416 case CS_SUSPENDED_CALLER:
417 /* can happen: we suspended, other peer did not yet
421 case CS_SUSPENDED_CALLEE:
422 case CS_SUSPENDED_BOTH:
423 /* can (rarely) also happen: other peer suspended, but cadet might
424 have had delayed data on the unreliable channel */
428 call->speaker->play (call->speaker->cls,
429 ntohs (am->header.size) - sizeof(struct
435 GNUNET_CONVERSATION_call_stop (call);
442 * Iterator called on obtained result for a GNS lookup.
444 * @param cls closure with the `struct GNUNET_CONVERSATION_Call`
445 * @param was_gns #GNUNET_NO if name was not a GNS name
446 * @param rd_count number of records in @a rd
447 * @param rd the records in reply
450 handle_gns_response (void *cls,
453 const struct GNUNET_GNSRECORD_Data *rd)
455 struct GNUNET_CONVERSATION_Call *call = cls;
456 struct GNUNET_MQ_Envelope *e;
457 struct ClientCallMessage *ccm;
460 GNUNET_break (NULL != call->gns_lookup);
461 GNUNET_break (CS_LOOKUP == call->state);
462 call->gns_lookup = NULL;
463 for (uint32_t i = 0; i < rd_count; i++)
465 if (GNUNET_GNSRECORD_TYPE_PHONE == rd[i].record_type)
467 if (rd[i].data_size != sizeof(struct GNUNET_CONVERSATION_PhoneRecord))
472 GNUNET_memcpy (&call->phone_record,
475 e = GNUNET_MQ_msg (ccm,
476 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_CALL);
477 ccm->line_port = call->phone_record.line_port;
478 ccm->target = call->phone_record.peer;
479 ccm->caller_id = *GNUNET_IDENTITY_ego_get_private_key (call->caller_id);
480 GNUNET_MQ_send (call->mq,
482 call->state = CS_RINGING;
483 call->event_handler (call->event_handler_cls,
484 GNUNET_CONVERSATION_EC_CALL_RINGING);
489 call->event_handler (call->event_handler_cls,
490 GNUNET_CONVERSATION_EC_CALL_GNS_FAIL);
491 GNUNET_CONVERSATION_call_stop (call);
496 * We encountered an error talking with the conversation service.
498 * @param cls the `struct GNUNET_CONVERSATION_Call`
499 * @param error details about the error
502 call_error_handler (void *cls,
503 enum GNUNET_MQ_Error error)
505 struct GNUNET_CONVERSATION_Call *call = cls;
508 if (CS_SHUTDOWN == call->state)
510 GNUNET_CONVERSATION_call_stop (call);
513 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
515 "Connection to conversation service lost, trying to reconnect\n"));
521 * The call got disconnected, destroy the handle.
523 * @param call call to reconnect
526 fail_call (struct GNUNET_CONVERSATION_Call *call)
528 if (CS_ACTIVE == call->state)
530 call->speaker->disable_speaker (call->speaker->cls);
531 call->mic->disable_microphone (call->mic->cls);
533 if (NULL != call->mq)
535 GNUNET_MQ_destroy (call->mq);
538 call->state = CS_SHUTDOWN;
539 call->event_handler (call->event_handler_cls,
540 GNUNET_CONVERSATION_EC_CALL_ERROR);
541 GNUNET_CONVERSATION_call_stop (call);
546 * Call the phone of another user.
548 * @param cfg configuration to use, specifies our phone service
549 * @param caller_id identity of the caller
550 * @param callee GNS name of the callee (used to locate the callee's record)
551 * @param speaker speaker to use (will be used automatically immediately once the
552 * #GNUNET_CONVERSATION_EC_CALL_PICKED_UP event is generated); we will NOT generate
553 * a ring tone on the speaker
554 * @param mic microphone to use (will be used automatically immediately once the
555 * #GNUNET_CONVERSATION_EC_CALL_PICKED_UP event is generated)
556 * @param event_handler how to notify the owner of the phone about events
557 * @param event_handler_cls closure for @a event_handler
558 * @return handle for the call, NULL on hard errors
560 struct GNUNET_CONVERSATION_Call *
561 GNUNET_CONVERSATION_call_start (const struct GNUNET_CONFIGURATION_Handle *cfg,
562 struct GNUNET_IDENTITY_Ego *caller_id,
564 struct GNUNET_SPEAKER_Handle *speaker,
565 struct GNUNET_MICROPHONE_Handle *mic,
566 GNUNET_CONVERSATION_CallEventHandler
568 void *event_handler_cls)
570 struct GNUNET_CONVERSATION_Call *call
571 = GNUNET_new (struct GNUNET_CONVERSATION_Call);
572 struct GNUNET_MQ_MessageHandler handlers[] = {
573 GNUNET_MQ_hd_fixed_size (call_suspend,
574 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_SUSPEND,
575 struct ClientPhoneSuspendMessage,
577 GNUNET_MQ_hd_fixed_size (call_resume,
578 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_RESUME,
579 struct ClientPhoneResumeMessage,
581 GNUNET_MQ_hd_fixed_size (call_picked_up,
582 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_PICKED_UP,
583 struct ClientPhonePickedupMessage,
585 GNUNET_MQ_hd_fixed_size (call_hangup,
586 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP,
587 struct ClientPhoneHangupMessage,
589 GNUNET_MQ_hd_var_size (call_audio,
590 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO,
591 struct ClientAudioMessage,
593 GNUNET_MQ_handler_end ()
596 call->mq = GNUNET_CLIENT_connect (cfg,
601 if (NULL == call->mq)
608 call->caller_id = caller_id;
609 call->callee = GNUNET_strdup (callee);
610 call->speaker = speaker;
612 call->event_handler = event_handler;
613 call->event_handler_cls = event_handler_cls;
614 call->gns = GNUNET_GNS_connect (cfg);
615 if (NULL == call->gns)
617 GNUNET_CONVERSATION_call_stop (call);
620 call->state = CS_LOOKUP;
621 call->gns_lookup = GNUNET_GNS_lookup_with_tld (call->gns,
623 GNUNET_GNSRECORD_TYPE_PHONE,
625 &handle_gns_response,
627 if (NULL == call->gns_lookup)
629 GNUNET_CONVERSATION_call_stop (call);
637 * Terminate a call. The call may be ringing or ready at this time.
639 * @param call call to terminate
642 GNUNET_CONVERSATION_call_stop (struct GNUNET_CONVERSATION_Call *call)
644 if ((NULL != call->speaker) &&
645 (CS_ACTIVE == call->state))
646 call->speaker->disable_speaker (call->speaker->cls);
647 if ((NULL != call->mic) &&
648 (CS_ACTIVE == call->state))
649 call->mic->disable_microphone (call->mic->cls);
650 if (CS_SHUTDOWN != call->state)
652 call->state = CS_SHUTDOWN;
654 if (NULL != call->mq)
656 GNUNET_MQ_destroy (call->mq);
659 if (NULL != call->gns_lookup)
661 GNUNET_GNS_lookup_with_tld_cancel (call->gns_lookup);
662 call->gns_lookup = NULL;
664 if (NULL != call->gns)
666 GNUNET_GNS_disconnect (call->gns);
669 GNUNET_free (call->callee);
675 * Pause a call. Temporarily suspends the use of speaker and
678 * @param call call to pause
681 GNUNET_CONVERSATION_call_suspend (struct GNUNET_CONVERSATION_Call *call)
683 struct GNUNET_MQ_Envelope *e;
684 struct ClientPhoneSuspendMessage *suspend;
686 GNUNET_assert ((CS_SUSPENDED_CALLEE == call->state) ||
687 (CS_ACTIVE == call->state));
688 if (CS_ACTIVE == call->state)
690 call->speaker->disable_speaker (call->speaker->cls);
691 call->mic->disable_microphone (call->mic->cls);
693 call->speaker = NULL;
695 e = GNUNET_MQ_msg (suspend,
696 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_SUSPEND);
697 GNUNET_MQ_send (call->mq,
699 if (CS_SUSPENDED_CALLER == call->state)
700 call->state = CS_SUSPENDED_BOTH;
702 call->state = CS_SUSPENDED_CALLER;
707 * Resumes a call after #GNUNET_CONVERSATION_call_suspend.
709 * @param call call to resume
710 * @param speaker speaker to use
711 * a ring tone on the speaker
712 * @param mic microphone to use
715 GNUNET_CONVERSATION_call_resume (struct GNUNET_CONVERSATION_Call *call,
716 struct GNUNET_SPEAKER_Handle *speaker,
717 struct GNUNET_MICROPHONE_Handle *mic)
719 struct GNUNET_MQ_Envelope *e;
720 struct ClientPhoneResumeMessage *resume;
722 GNUNET_assert ((CS_SUSPENDED_CALLER == call->state) ||
723 (CS_SUSPENDED_BOTH == call->state));
724 e = GNUNET_MQ_msg (resume, GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_RESUME);
725 GNUNET_MQ_send (call->mq, e);
726 call->speaker = speaker;
728 if (CS_SUSPENDED_CALLER == call->state)
730 call->state = CS_ACTIVE;
731 call->speaker->enable_speaker (call->speaker->cls);
732 call->mic->enable_microphone (call->mic->cls,
733 &transmit_call_audio,
738 call->state = CS_SUSPENDED_CALLEE;
743 /* end of conversation_api_call.c */