2 This file is part of GNUnet
3 Copyright (C) 2013, 2014 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.c
23 * @brief phone and caller 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 "conversation.h"
34 * Possible states of a caller.
39 * The phone is ringing (user knows about incoming call).
44 * The phone is in an active conversation.
49 * We suspended the conversation.
54 * Caller suspended the conversation.
59 * Both sides suspended the conversation.
67 * A caller is the handle we have for an incoming call.
69 struct GNUNET_CONVERSATION_Caller
73 * We keep all callers in a DLL.
75 struct GNUNET_CONVERSATION_Caller *next;
78 * We keep all callers in a DLL.
80 struct GNUNET_CONVERSATION_Caller *prev;
85 struct GNUNET_CONVERSATION_Phone *phone;
88 * Function to call for phone events.
90 GNUNET_CONVERSATION_CallerEventHandler event_handler;
93 * Closure for @e event_handler
95 void *event_handler_cls;
98 * Speaker, or NULL if none is attached.
100 struct GNUNET_SPEAKER_Handle *speaker;
103 * Microphone, or NULL if none is attached.
105 struct GNUNET_MICROPHONE_Handle *mic;
108 * Identity of the person calling us.
110 struct GNUNET_CRYPTO_EcdsaPublicKey caller_id;
113 * Internal handle to identify the caller with the service.
118 * State machine for the phone.
120 enum CallerState state;
126 * Possible states of a phone.
131 * We still need to register the phone.
136 * We are waiting for calls.
144 * A phone is a device that can ring to signal an incoming call and
145 * that you can pick up to answer the call and hang up to terminate
146 * the call. You can also hang up a ringing phone immediately
147 * (without picking it up) to stop it from ringing. Phones have
148 * caller ID. You can ask the phone for its record and make that
149 * record available (via GNS) to enable others to call you.
150 * Multiple phones maybe connected to the same line (the line is
151 * something rather internal to a phone and not obvious from it).
152 * You can only have one conversation per phone at any time.
154 struct GNUNET_CONVERSATION_Phone
159 const struct GNUNET_CONFIGURATION_Handle *cfg;
162 * We keep all callers in a DLL.
164 struct GNUNET_CONVERSATION_Caller *caller_head;
167 * We keep all callers in a DLL.
169 struct GNUNET_CONVERSATION_Caller *caller_tail;
172 * Function to call for phone events.
174 GNUNET_CONVERSATION_PhoneEventHandler event_handler;
177 * Closure for @e event_handler
179 void *event_handler_cls;
182 * Connection to NAMESTORE (for reverse lookup).
184 struct GNUNET_NAMESTORE_Handle *ns;
187 * Handle for transmitting to the CONVERSATION service.
189 struct GNUNET_MQ_Handle *mq;
192 * This phone's record.
194 struct GNUNET_CONVERSATION_PhoneRecord my_record;
199 struct GNUNET_CRYPTO_EcdsaPrivateKey my_zone;
202 * State machine for the phone.
204 enum PhoneState state;
210 * The phone got disconnected, reconnect to the service.
212 * @param phone phone to reconnect
215 reconnect_phone (struct GNUNET_CONVERSATION_Phone *phone);
219 * Process recorded audio data.
221 * @param cls closure with the `struct GNUNET_CONVERSATION_Caller`
222 * @param data_size number of bytes in @a data
223 * @param data audio data to play
226 transmit_phone_audio (void *cls,
230 struct GNUNET_CONVERSATION_Caller *caller = cls;
231 struct GNUNET_CONVERSATION_Phone *phone = caller->phone;
232 struct GNUNET_MQ_Envelope *e;
233 struct ClientAudioMessage *am;
235 e = GNUNET_MQ_msg_extra (am,
237 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO);
238 am->cid = caller->cid;
239 GNUNET_memcpy (&am[1],
242 GNUNET_MQ_send (phone->mq,
248 * We received a `struct ClientPhoneRingMessage`
250 * @param cls the `struct GNUNET_CONVERSATION_Phone`
251 * @param ring the message
254 handle_phone_ring (void *cls,
255 const struct ClientPhoneRingMessage *ring)
257 struct GNUNET_CONVERSATION_Phone *phone = cls;
258 struct GNUNET_CONVERSATION_Caller *caller;
260 switch (phone->state)
266 caller = GNUNET_new (struct GNUNET_CONVERSATION_Caller);
267 caller->phone = phone;
268 GNUNET_CONTAINER_DLL_insert (phone->caller_head,
271 caller->caller_id = ring->caller_id;
272 caller->cid = ring->cid;
273 caller->state = CS_RINGING;
274 phone->event_handler (phone->event_handler_cls,
275 GNUNET_CONVERSATION_EC_PHONE_RING,
284 * Find the record of the caller matching the @a cid
286 * @param phone phone to search
287 * @param cid caller ID to search for (in NBO)
288 * @return NULL if @a cid was not found
290 static struct GNUNET_CONVERSATION_Caller *
291 find_caller (struct GNUNET_CONVERSATION_Phone *phone,
294 struct GNUNET_CONVERSATION_Caller *caller;
296 for (caller = phone->caller_head;NULL != caller;caller = caller->next)
297 if (cid == caller->cid)
304 * We received a `struct ClientPhoneHangupMessage`.
306 * @param cls the `struct GNUNET_CONVERSATION_Phone *`
307 * @param msg the message
310 handle_phone_hangup (void *cls,
311 const struct ClientPhoneHangupMessage *hang)
313 struct GNUNET_CONVERSATION_Phone *phone = cls;
314 struct GNUNET_CONVERSATION_Caller *caller;
316 caller = find_caller (phone,
320 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
321 "Received HANG_UP message for unknown caller ID %u\n",
322 (unsigned int) hang->cid);
326 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
327 "Received HANG_UP message, terminating call with `%s'\n",
328 GNUNET_GNSRECORD_pkey_to_zkey (&caller->caller_id));
329 switch (caller->state)
332 phone->event_handler (phone->event_handler_cls,
333 GNUNET_CONVERSATION_EC_PHONE_HUNG_UP,
338 caller->speaker->disable_speaker (caller->speaker->cls);
339 caller->mic->disable_microphone (caller->mic->cls);
340 phone->event_handler (phone->event_handler_cls,
341 GNUNET_CONVERSATION_EC_PHONE_HUNG_UP,
345 case CS_CALLEE_SUSPENDED:
346 case CS_CALLER_SUSPENDED:
347 case CS_BOTH_SUSPENDED:
348 phone->event_handler (phone->event_handler_cls,
349 GNUNET_CONVERSATION_EC_PHONE_HUNG_UP,
354 GNUNET_CONTAINER_DLL_remove (phone->caller_head,
357 GNUNET_free (caller);
362 * We received a `struct ClientPhoneSuspendMessage`.
364 * @param cls the `struct GNUNET_CONVERSATION_Phone`
365 * @param suspend the message
368 handle_phone_suspend (void *cls,
369 const struct ClientPhoneSuspendMessage *suspend)
371 struct GNUNET_CONVERSATION_Phone *phone = cls;
372 struct GNUNET_CONVERSATION_Caller *caller;
374 caller = find_caller (phone,
378 switch (caller->state)
384 caller->state = CS_CALLER_SUSPENDED;
385 caller->speaker->disable_speaker (caller->speaker->cls);
386 caller->mic->disable_microphone (caller->mic->cls);
387 caller->event_handler (caller->event_handler_cls,
388 GNUNET_CONVERSATION_EC_CALLER_SUSPEND);
390 case CS_CALLEE_SUSPENDED:
391 caller->state = CS_BOTH_SUSPENDED;
392 caller->event_handler (caller->event_handler_cls,
393 GNUNET_CONVERSATION_EC_CALLER_SUSPEND);
395 case CS_CALLER_SUSPENDED:
396 case CS_BOTH_SUSPENDED:
404 * We received a `struct ClientPhoneResumeMessage`.
406 * @param cls the `struct GNUNET_CONVERSATION_Phone`
407 * @param resume the message
410 handle_phone_resume (void *cls,
411 const struct ClientPhoneResumeMessage *resume)
413 struct GNUNET_CONVERSATION_Phone *phone = cls;
414 struct GNUNET_CONVERSATION_Caller *caller;
416 caller = find_caller (phone,
420 switch (caller->state)
426 case CS_CALLEE_SUSPENDED:
429 case CS_CALLER_SUSPENDED:
430 caller->state = CS_ACTIVE;
431 caller->speaker->enable_speaker (caller->speaker->cls);
432 caller->mic->enable_microphone (caller->mic->cls,
433 &transmit_phone_audio,
435 caller->event_handler (caller->event_handler_cls,
436 GNUNET_CONVERSATION_EC_CALLER_RESUME);
438 case CS_BOTH_SUSPENDED:
439 caller->state = CS_CALLEE_SUSPENDED;
440 caller->event_handler (caller->event_handler_cls,
441 GNUNET_CONVERSATION_EC_CALLER_RESUME);
448 * We received a `struct ClientAudioMessage`, check it is well-formed.
450 * @param cls the `struct GNUNET_CONVERSATION_Phone`
451 * @param am the message
452 * @return #GNUNET_OK if @a am is well-formed
455 check_phone_audio (void *cls,
456 const struct ClientAudioMessage *am)
461 /* any variable-size payload is OK */
467 * We received a `struct ClientAudioMessage`
469 * @param cls the `struct GNUNET_CONVERSATION_Phone`
470 * @param am the message
473 handle_phone_audio (void *cls,
474 const struct ClientAudioMessage *am)
476 struct GNUNET_CONVERSATION_Phone *phone = cls;
477 struct GNUNET_CONVERSATION_Caller *caller;
479 caller = find_caller (phone,
483 switch (caller->state)
489 caller->speaker->play (caller->speaker->cls,
490 ntohs (am->header.size) - sizeof (struct ClientAudioMessage),
493 case CS_CALLEE_SUSPENDED:
494 case CS_CALLER_SUSPENDED:
495 case CS_BOTH_SUSPENDED:
502 * We encountered an error talking with the conversation service.
504 * @param cls the `struct GNUNET_CONVERSATION_Phone`
505 * @param error details about the error
508 phone_error_handler (void *cls,
509 enum GNUNET_MQ_Error error)
511 struct GNUNET_CONVERSATION_Phone *phone = cls;
514 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
515 _("Connection to conversation service lost, trying to reconnect\n"));
516 reconnect_phone (phone);
521 * Clean up all callers of the given phone.
523 * @param phone phone to clean up callers for
526 clean_up_callers (struct GNUNET_CONVERSATION_Phone *phone)
528 struct GNUNET_CONVERSATION_Caller *caller;
530 while (NULL != (caller = phone->caller_head))
532 /* make sure mic/speaker are disabled *before* callback */
533 if (CS_ACTIVE == caller->state)
535 caller->speaker->disable_speaker (caller->speaker->cls);
536 caller->mic->disable_microphone (caller->mic->cls);
537 caller->state = CS_CALLER_SUSPENDED;
539 phone->event_handler (phone->event_handler_cls,
540 GNUNET_CONVERSATION_EC_PHONE_HUNG_UP,
543 GNUNET_CONVERSATION_caller_hang_up (caller);
549 * The phone got disconnected, reconnect to the service.
551 * @param phone phone to reconnect
554 reconnect_phone (struct GNUNET_CONVERSATION_Phone *phone)
556 struct GNUNET_MQ_MessageHandler handlers[] = {
557 GNUNET_MQ_hd_fixed_size (phone_ring,
558 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_RING,
559 struct ClientPhoneRingMessage,
561 GNUNET_MQ_hd_fixed_size (phone_hangup,
562 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP,
563 struct ClientPhoneHangupMessage,
565 GNUNET_MQ_hd_fixed_size (phone_suspend,
566 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_SUSPEND,
567 struct ClientPhoneSuspendMessage,
569 GNUNET_MQ_hd_fixed_size (phone_resume,
570 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_RESUME,
571 struct ClientPhoneResumeMessage,
573 GNUNET_MQ_hd_var_size (phone_audio,
574 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO,
575 struct ClientAudioMessage,
577 GNUNET_MQ_handler_end ()
579 struct GNUNET_MQ_Envelope *e;
580 struct ClientPhoneRegisterMessage *reg;
582 clean_up_callers (phone);
583 if (NULL != phone->mq)
585 GNUNET_MQ_destroy (phone->mq);
588 phone->state = PS_REGISTER;
589 phone->mq = GNUNET_CLIENT_connect (phone->cfg,
592 &phone_error_handler,
594 if (NULL == phone->mq)
596 e = GNUNET_MQ_msg (reg,
597 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_REGISTER);
598 reg->line_port = phone->my_record.line_port;
599 GNUNET_MQ_send (phone->mq,
601 phone->state = PS_READY;
606 * Create a new phone.
608 * @param cfg configuration for the phone; specifies the phone service and
609 * which line the phone is to be connected to
610 * @param ego ego to use for name resolution (when determining caller ID)
611 * @param event_handler how to notify the owner of the phone about events
612 * @param event_handler_cls closure for @a event_handler
613 * @return NULL on error (no valid line configured)
615 struct GNUNET_CONVERSATION_Phone *
616 GNUNET_CONVERSATION_phone_create (const struct GNUNET_CONFIGURATION_Handle *cfg,
617 const struct GNUNET_IDENTITY_Ego *ego,
618 GNUNET_CONVERSATION_PhoneEventHandler event_handler,
619 void *event_handler_cls)
621 struct GNUNET_CONVERSATION_Phone *phone;
623 struct GNUNET_HashCode line_port;
626 GNUNET_CONFIGURATION_get_value_string (cfg,
631 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
636 GNUNET_CRYPTO_hash (line,
639 phone = GNUNET_new (struct GNUNET_CONVERSATION_Phone);
641 GNUNET_CRYPTO_get_peer_identity (cfg,
642 &phone->my_record.peer))
649 phone->my_zone = *GNUNET_IDENTITY_ego_get_private_key (ego);
650 phone->event_handler = event_handler;
651 phone->event_handler_cls = event_handler_cls;
652 phone->ns = GNUNET_NAMESTORE_connect (cfg);
653 phone->my_record.version = htonl (1);
654 phone->my_record.reserved = htonl (0);
655 phone->my_record.line_port = line_port;
656 reconnect_phone (phone);
657 if ( (NULL == phone->mq) ||
658 (NULL == phone->ns) )
661 GNUNET_CONVERSATION_phone_destroy (phone);
669 * Fill in a namestore record with the contact information
670 * for this phone. Note that the filled in "data" value
671 * is only valid until the phone is destroyed.
673 * @param phone phone to create a record for
674 * @param rd namestore record to fill in
677 GNUNET_CONVERSATION_phone_get_record (struct GNUNET_CONVERSATION_Phone *phone,
678 struct GNUNET_GNSRECORD_Data *rd)
680 rd->data = &phone->my_record;
681 rd->expiration_time = 0;
682 rd->data_size = sizeof (struct GNUNET_CONVERSATION_PhoneRecord);
683 rd->record_type = GNUNET_GNSRECORD_TYPE_PHONE;
684 rd->flags = GNUNET_GNSRECORD_RF_NONE;
689 * Picks up a (ringing) phone. This will connect the speaker
690 * to the microphone of the other party, and vice versa.
692 * @param caller handle that identifies which caller should be answered
693 * @param event_handler how to notify about events by the caller
694 * @param event_handler_cls closure for @a event_handler
695 * @param speaker speaker to use
696 * @param mic microphone to use
699 GNUNET_CONVERSATION_caller_pick_up (struct GNUNET_CONVERSATION_Caller *caller,
700 GNUNET_CONVERSATION_CallerEventHandler event_handler,
701 void *event_handler_cls,
702 struct GNUNET_SPEAKER_Handle *speaker,
703 struct GNUNET_MICROPHONE_Handle *mic)
705 struct GNUNET_CONVERSATION_Phone *phone = caller->phone;
706 struct GNUNET_MQ_Envelope *e;
707 struct ClientPhonePickupMessage *pick;
709 GNUNET_assert (CS_RINGING == caller->state);
710 caller->speaker = speaker;
712 e = GNUNET_MQ_msg (pick,
713 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_PICK_UP);
714 pick->cid = caller->cid;
715 GNUNET_MQ_send (phone->mq,
717 caller->state = CS_ACTIVE;
718 caller->event_handler = event_handler;
719 caller->event_handler_cls = event_handler_cls;
720 caller->speaker->enable_speaker (caller->speaker->cls);
721 caller->mic->enable_microphone (caller->mic->cls,
722 &transmit_phone_audio,
728 * Hang up up a (possibly ringing) phone. This will notify the other
729 * party that we are no longer interested in talking with them.
731 * @param caller conversation to hang up on
734 GNUNET_CONVERSATION_caller_hang_up (struct GNUNET_CONVERSATION_Caller *caller)
736 struct GNUNET_CONVERSATION_Phone *phone = caller->phone;
737 struct GNUNET_MQ_Envelope *e;
738 struct ClientPhoneHangupMessage *hang;
740 switch (caller->state)
743 caller->speaker->disable_speaker (caller->speaker->cls);
744 caller->mic->disable_microphone (caller->mic->cls);
749 GNUNET_CONTAINER_DLL_remove (phone->caller_head,
752 e = GNUNET_MQ_msg (hang,
753 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP);
754 hang->cid = caller->cid;
755 GNUNET_MQ_send (phone->mq,
757 GNUNET_free (caller);
764 * @param phone phone to destroy
767 GNUNET_CONVERSATION_phone_destroy (struct GNUNET_CONVERSATION_Phone *phone)
769 clean_up_callers (phone);
770 if (NULL != phone->ns)
772 GNUNET_NAMESTORE_disconnect (phone->ns);
775 if (NULL != phone->mq)
777 GNUNET_MQ_destroy (phone->mq);
785 * Pause conversation of an active call. This will disconnect the speaker
786 * and the microphone. The call can later be resumed with
787 * #GNUNET_CONVERSATION_caller_resume.
789 * @param caller call to suspend
792 GNUNET_CONVERSATION_caller_suspend (struct GNUNET_CONVERSATION_Caller *caller)
794 struct GNUNET_CONVERSATION_Phone *phone = caller->phone;
795 struct GNUNET_MQ_Envelope *e;
796 struct ClientPhoneSuspendMessage *suspend;
798 GNUNET_assert ( (CS_ACTIVE == caller->state) ||
799 (CS_CALLER_SUSPENDED == caller->state) );
800 if (CS_ACTIVE == caller->state)
802 caller->speaker->disable_speaker (caller->speaker->cls);
803 caller->mic->disable_microphone (caller->mic->cls);
805 caller->speaker = NULL;
807 e = GNUNET_MQ_msg (suspend,
808 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_SUSPEND);
809 suspend->cid = caller->cid;
810 GNUNET_MQ_send (phone->mq,
812 if (CS_ACTIVE == caller->state)
813 caller->state = CS_CALLEE_SUSPENDED;
815 caller->state = CS_BOTH_SUSPENDED;
820 * Resume suspended conversation of a phone.
822 * @param caller call to resume
823 * @param speaker speaker to use
824 * @param mic microphone to use
827 GNUNET_CONVERSATION_caller_resume (struct GNUNET_CONVERSATION_Caller *caller,
828 struct GNUNET_SPEAKER_Handle *speaker,
829 struct GNUNET_MICROPHONE_Handle *mic)
831 struct GNUNET_CONVERSATION_Phone *phone = caller->phone;
832 struct GNUNET_MQ_Envelope *e;
833 struct ClientPhoneResumeMessage *resume;
835 GNUNET_assert ( (CS_CALLEE_SUSPENDED == caller->state) ||
836 (CS_BOTH_SUSPENDED == caller->state) );
837 caller->speaker = speaker;
839 e = GNUNET_MQ_msg (resume,
840 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_RESUME);
841 resume->cid = caller->cid;
842 GNUNET_MQ_send (phone->mq,
844 if (CS_CALLEE_SUSPENDED == caller->state)
846 caller->state = CS_ACTIVE;
847 caller->speaker->enable_speaker (caller->speaker->cls);
848 caller->mic->enable_microphone (caller->mic->cls,
849 &transmit_phone_audio,
854 caller->state = CS_CALLER_SUSPENDED;
858 /* end of conversation_api.c */