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/>.
20 * @file conversation/conversation_api.c
21 * @brief phone and caller API to the conversation service
22 * @author Simon Dieterle
23 * @author Andreas Fuchs
24 * @author Christian Grothoff
27 #include "gnunet_conversation_service.h"
28 #include "conversation.h"
32 * Possible states of a caller.
37 * The phone is ringing (user knows about incoming call).
42 * The phone is in an active conversation.
47 * We suspended the conversation.
52 * Caller suspended the conversation.
57 * Both sides suspended the conversation.
65 * A caller is the handle we have for an incoming call.
67 struct GNUNET_CONVERSATION_Caller
71 * We keep all callers in a DLL.
73 struct GNUNET_CONVERSATION_Caller *next;
76 * We keep all callers in a DLL.
78 struct GNUNET_CONVERSATION_Caller *prev;
83 struct GNUNET_CONVERSATION_Phone *phone;
86 * Function to call for phone events.
88 GNUNET_CONVERSATION_CallerEventHandler event_handler;
91 * Closure for @e event_handler
93 void *event_handler_cls;
96 * Speaker, or NULL if none is attached.
98 struct GNUNET_SPEAKER_Handle *speaker;
101 * Microphone, or NULL if none is attached.
103 struct GNUNET_MICROPHONE_Handle *mic;
106 * Identity of the person calling us.
108 struct GNUNET_CRYPTO_EcdsaPublicKey caller_id;
111 * Internal handle to identify the caller with the service.
116 * State machine for the phone.
118 enum CallerState state;
124 * Possible states of a phone.
129 * We still need to register the phone.
134 * We are waiting for calls.
142 * A phone is a device that can ring to signal an incoming call and
143 * that you can pick up to answer the call and hang up to terminate
144 * the call. You can also hang up a ringing phone immediately
145 * (without picking it up) to stop it from ringing. Phones have
146 * caller ID. You can ask the phone for its record and make that
147 * record available (via GNS) to enable others to call you.
148 * Multiple phones maybe connected to the same line (the line is
149 * something rather internal to a phone and not obvious from it).
150 * You can only have one conversation per phone at any time.
152 struct GNUNET_CONVERSATION_Phone
157 const struct GNUNET_CONFIGURATION_Handle *cfg;
160 * We keep all callers in a DLL.
162 struct GNUNET_CONVERSATION_Caller *caller_head;
165 * We keep all callers in a DLL.
167 struct GNUNET_CONVERSATION_Caller *caller_tail;
170 * Function to call for phone events.
172 GNUNET_CONVERSATION_PhoneEventHandler event_handler;
175 * Closure for @e event_handler
177 void *event_handler_cls;
180 * Connection to NAMESTORE (for reverse lookup).
182 struct GNUNET_NAMESTORE_Handle *ns;
185 * Handle for transmitting to the CONVERSATION service.
187 struct GNUNET_MQ_Handle *mq;
190 * This phone's record.
192 struct GNUNET_CONVERSATION_PhoneRecord my_record;
197 struct GNUNET_CRYPTO_EcdsaPrivateKey my_zone;
200 * State machine for the phone.
202 enum PhoneState state;
208 * The phone got disconnected, reconnect to the service.
210 * @param phone phone to reconnect
213 reconnect_phone (struct GNUNET_CONVERSATION_Phone *phone);
217 * Process recorded audio data.
219 * @param cls closure with the `struct GNUNET_CONVERSATION_Caller`
220 * @param data_size number of bytes in @a data
221 * @param data audio data to play
224 transmit_phone_audio (void *cls,
228 struct GNUNET_CONVERSATION_Caller *caller = cls;
229 struct GNUNET_CONVERSATION_Phone *phone = caller->phone;
230 struct GNUNET_MQ_Envelope *e;
231 struct ClientAudioMessage *am;
233 e = GNUNET_MQ_msg_extra (am,
235 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO);
236 am->cid = caller->cid;
237 GNUNET_memcpy (&am[1],
240 GNUNET_MQ_send (phone->mq,
246 * We received a `struct ClientPhoneRingMessage`
248 * @param cls the `struct GNUNET_CONVERSATION_Phone`
249 * @param ring the message
252 handle_phone_ring (void *cls,
253 const struct ClientPhoneRingMessage *ring)
255 struct GNUNET_CONVERSATION_Phone *phone = cls;
256 struct GNUNET_CONVERSATION_Caller *caller;
258 switch (phone->state)
264 caller = GNUNET_new (struct GNUNET_CONVERSATION_Caller);
265 caller->phone = phone;
266 GNUNET_CONTAINER_DLL_insert (phone->caller_head,
269 caller->caller_id = ring->caller_id;
270 caller->cid = ring->cid;
271 caller->state = CS_RINGING;
272 phone->event_handler (phone->event_handler_cls,
273 GNUNET_CONVERSATION_EC_PHONE_RING,
282 * Find the record of the caller matching the @a cid
284 * @param phone phone to search
285 * @param cid caller ID to search for (in NBO)
286 * @return NULL if @a cid was not found
288 static struct GNUNET_CONVERSATION_Caller *
289 find_caller (struct GNUNET_CONVERSATION_Phone *phone,
292 struct GNUNET_CONVERSATION_Caller *caller;
294 for (caller = phone->caller_head;NULL != caller;caller = caller->next)
295 if (cid == caller->cid)
302 * We received a `struct ClientPhoneHangupMessage`.
304 * @param cls the `struct GNUNET_CONVERSATION_Phone *`
305 * @param msg the message
308 handle_phone_hangup (void *cls,
309 const struct ClientPhoneHangupMessage *hang)
311 struct GNUNET_CONVERSATION_Phone *phone = cls;
312 struct GNUNET_CONVERSATION_Caller *caller;
314 caller = find_caller (phone,
318 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
319 "Received HANG_UP message for unknown caller ID %u\n",
320 (unsigned int) hang->cid);
324 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
325 "Received HANG_UP message, terminating call with `%s'\n",
326 GNUNET_GNSRECORD_pkey_to_zkey (&caller->caller_id));
327 switch (caller->state)
330 phone->event_handler (phone->event_handler_cls,
331 GNUNET_CONVERSATION_EC_PHONE_HUNG_UP,
336 caller->speaker->disable_speaker (caller->speaker->cls);
337 caller->mic->disable_microphone (caller->mic->cls);
338 phone->event_handler (phone->event_handler_cls,
339 GNUNET_CONVERSATION_EC_PHONE_HUNG_UP,
343 case CS_CALLEE_SUSPENDED:
344 case CS_CALLER_SUSPENDED:
345 case CS_BOTH_SUSPENDED:
346 phone->event_handler (phone->event_handler_cls,
347 GNUNET_CONVERSATION_EC_PHONE_HUNG_UP,
352 GNUNET_CONTAINER_DLL_remove (phone->caller_head,
355 GNUNET_free (caller);
360 * We received a `struct ClientPhoneSuspendMessage`.
362 * @param cls the `struct GNUNET_CONVERSATION_Phone`
363 * @param suspend the message
366 handle_phone_suspend (void *cls,
367 const struct ClientPhoneSuspendMessage *suspend)
369 struct GNUNET_CONVERSATION_Phone *phone = cls;
370 struct GNUNET_CONVERSATION_Caller *caller;
372 caller = find_caller (phone,
376 switch (caller->state)
382 caller->state = CS_CALLER_SUSPENDED;
383 caller->speaker->disable_speaker (caller->speaker->cls);
384 caller->mic->disable_microphone (caller->mic->cls);
385 caller->event_handler (caller->event_handler_cls,
386 GNUNET_CONVERSATION_EC_CALLER_SUSPEND);
388 case CS_CALLEE_SUSPENDED:
389 caller->state = CS_BOTH_SUSPENDED;
390 caller->event_handler (caller->event_handler_cls,
391 GNUNET_CONVERSATION_EC_CALLER_SUSPEND);
393 case CS_CALLER_SUSPENDED:
394 case CS_BOTH_SUSPENDED:
402 * We received a `struct ClientPhoneResumeMessage`.
404 * @param cls the `struct GNUNET_CONVERSATION_Phone`
405 * @param resume the message
408 handle_phone_resume (void *cls,
409 const struct ClientPhoneResumeMessage *resume)
411 struct GNUNET_CONVERSATION_Phone *phone = cls;
412 struct GNUNET_CONVERSATION_Caller *caller;
414 caller = find_caller (phone,
418 switch (caller->state)
424 case CS_CALLEE_SUSPENDED:
427 case CS_CALLER_SUSPENDED:
428 caller->state = CS_ACTIVE;
429 caller->speaker->enable_speaker (caller->speaker->cls);
430 caller->mic->enable_microphone (caller->mic->cls,
431 &transmit_phone_audio,
433 caller->event_handler (caller->event_handler_cls,
434 GNUNET_CONVERSATION_EC_CALLER_RESUME);
436 case CS_BOTH_SUSPENDED:
437 caller->state = CS_CALLEE_SUSPENDED;
438 caller->event_handler (caller->event_handler_cls,
439 GNUNET_CONVERSATION_EC_CALLER_RESUME);
446 * We received a `struct ClientAudioMessage`, check it is well-formed.
448 * @param cls the `struct GNUNET_CONVERSATION_Phone`
449 * @param am the message
450 * @return #GNUNET_OK if @a am is well-formed
453 check_phone_audio (void *cls,
454 const struct ClientAudioMessage *am)
459 /* any variable-size payload is OK */
465 * We received a `struct ClientAudioMessage`
467 * @param cls the `struct GNUNET_CONVERSATION_Phone`
468 * @param am the message
471 handle_phone_audio (void *cls,
472 const struct ClientAudioMessage *am)
474 struct GNUNET_CONVERSATION_Phone *phone = cls;
475 struct GNUNET_CONVERSATION_Caller *caller;
477 caller = find_caller (phone,
481 switch (caller->state)
487 caller->speaker->play (caller->speaker->cls,
488 ntohs (am->header.size) - sizeof (struct ClientAudioMessage),
491 case CS_CALLEE_SUSPENDED:
492 case CS_CALLER_SUSPENDED:
493 case CS_BOTH_SUSPENDED:
500 * We encountered an error talking with the conversation service.
502 * @param cls the `struct GNUNET_CONVERSATION_Phone`
503 * @param error details about the error
506 phone_error_handler (void *cls,
507 enum GNUNET_MQ_Error error)
509 struct GNUNET_CONVERSATION_Phone *phone = cls;
512 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
513 _("Connection to conversation service lost, trying to reconnect\n"));
514 reconnect_phone (phone);
519 * Clean up all callers of the given phone.
521 * @param phone phone to clean up callers for
524 clean_up_callers (struct GNUNET_CONVERSATION_Phone *phone)
526 struct GNUNET_CONVERSATION_Caller *caller;
528 while (NULL != (caller = phone->caller_head))
530 /* make sure mic/speaker are disabled *before* callback */
531 if (CS_ACTIVE == caller->state)
533 caller->speaker->disable_speaker (caller->speaker->cls);
534 caller->mic->disable_microphone (caller->mic->cls);
535 caller->state = CS_CALLER_SUSPENDED;
537 phone->event_handler (phone->event_handler_cls,
538 GNUNET_CONVERSATION_EC_PHONE_HUNG_UP,
541 GNUNET_CONVERSATION_caller_hang_up (caller);
547 * The phone got disconnected, reconnect to the service.
549 * @param phone phone to reconnect
552 reconnect_phone (struct GNUNET_CONVERSATION_Phone *phone)
554 struct GNUNET_MQ_MessageHandler handlers[] = {
555 GNUNET_MQ_hd_fixed_size (phone_ring,
556 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_RING,
557 struct ClientPhoneRingMessage,
559 GNUNET_MQ_hd_fixed_size (phone_hangup,
560 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP,
561 struct ClientPhoneHangupMessage,
563 GNUNET_MQ_hd_fixed_size (phone_suspend,
564 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_SUSPEND,
565 struct ClientPhoneSuspendMessage,
567 GNUNET_MQ_hd_fixed_size (phone_resume,
568 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_RESUME,
569 struct ClientPhoneResumeMessage,
571 GNUNET_MQ_hd_var_size (phone_audio,
572 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO,
573 struct ClientAudioMessage,
575 GNUNET_MQ_handler_end ()
577 struct GNUNET_MQ_Envelope *e;
578 struct ClientPhoneRegisterMessage *reg;
580 clean_up_callers (phone);
581 if (NULL != phone->mq)
583 GNUNET_MQ_destroy (phone->mq);
586 phone->state = PS_REGISTER;
587 phone->mq = GNUNET_CLIENT_connect (phone->cfg,
590 &phone_error_handler,
592 if (NULL == phone->mq)
594 e = GNUNET_MQ_msg (reg,
595 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_REGISTER);
596 reg->line_port = phone->my_record.line_port;
597 GNUNET_MQ_send (phone->mq,
599 phone->state = PS_READY;
604 * Create a new phone.
606 * @param cfg configuration for the phone; specifies the phone service and
607 * which line the phone is to be connected to
608 * @param ego ego to use for name resolution (when determining caller ID)
609 * @param event_handler how to notify the owner of the phone about events
610 * @param event_handler_cls closure for @a event_handler
611 * @return NULL on error (no valid line configured)
613 struct GNUNET_CONVERSATION_Phone *
614 GNUNET_CONVERSATION_phone_create (const struct GNUNET_CONFIGURATION_Handle *cfg,
615 const struct GNUNET_IDENTITY_Ego *ego,
616 GNUNET_CONVERSATION_PhoneEventHandler event_handler,
617 void *event_handler_cls)
619 struct GNUNET_CONVERSATION_Phone *phone;
621 struct GNUNET_HashCode line_port;
624 GNUNET_CONFIGURATION_get_value_string (cfg,
629 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
634 GNUNET_CRYPTO_hash (line,
637 phone = GNUNET_new (struct GNUNET_CONVERSATION_Phone);
639 GNUNET_CRYPTO_get_peer_identity (cfg,
640 &phone->my_record.peer))
647 phone->my_zone = *GNUNET_IDENTITY_ego_get_private_key (ego);
648 phone->event_handler = event_handler;
649 phone->event_handler_cls = event_handler_cls;
650 phone->ns = GNUNET_NAMESTORE_connect (cfg);
651 phone->my_record.version = htonl (1);
652 phone->my_record.reserved = htonl (0);
653 phone->my_record.line_port = line_port;
654 reconnect_phone (phone);
655 if ( (NULL == phone->mq) ||
656 (NULL == phone->ns) )
659 GNUNET_CONVERSATION_phone_destroy (phone);
667 * Fill in a namestore record with the contact information
668 * for this phone. Note that the filled in "data" value
669 * is only valid until the phone is destroyed.
671 * @param phone phone to create a record for
672 * @param rd namestore record to fill in
675 GNUNET_CONVERSATION_phone_get_record (struct GNUNET_CONVERSATION_Phone *phone,
676 struct GNUNET_GNSRECORD_Data *rd)
678 rd->data = &phone->my_record;
679 rd->expiration_time = 0;
680 rd->data_size = sizeof (struct GNUNET_CONVERSATION_PhoneRecord);
681 rd->record_type = GNUNET_GNSRECORD_TYPE_PHONE;
682 rd->flags = GNUNET_GNSRECORD_RF_NONE;
687 * Picks up a (ringing) phone. This will connect the speaker
688 * to the microphone of the other party, and vice versa.
690 * @param caller handle that identifies which caller should be answered
691 * @param event_handler how to notify about events by the caller
692 * @param event_handler_cls closure for @a event_handler
693 * @param speaker speaker to use
694 * @param mic microphone to use
697 GNUNET_CONVERSATION_caller_pick_up (struct GNUNET_CONVERSATION_Caller *caller,
698 GNUNET_CONVERSATION_CallerEventHandler event_handler,
699 void *event_handler_cls,
700 struct GNUNET_SPEAKER_Handle *speaker,
701 struct GNUNET_MICROPHONE_Handle *mic)
703 struct GNUNET_CONVERSATION_Phone *phone = caller->phone;
704 struct GNUNET_MQ_Envelope *e;
705 struct ClientPhonePickupMessage *pick;
707 GNUNET_assert (CS_RINGING == caller->state);
708 caller->speaker = speaker;
710 e = GNUNET_MQ_msg (pick,
711 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_PICK_UP);
712 pick->cid = caller->cid;
713 GNUNET_MQ_send (phone->mq,
715 caller->state = CS_ACTIVE;
716 caller->event_handler = event_handler;
717 caller->event_handler_cls = event_handler_cls;
718 caller->speaker->enable_speaker (caller->speaker->cls);
719 caller->mic->enable_microphone (caller->mic->cls,
720 &transmit_phone_audio,
726 * Hang up up a (possibly ringing) phone. This will notify the other
727 * party that we are no longer interested in talking with them.
729 * @param caller conversation to hang up on
732 GNUNET_CONVERSATION_caller_hang_up (struct GNUNET_CONVERSATION_Caller *caller)
734 struct GNUNET_CONVERSATION_Phone *phone = caller->phone;
735 struct GNUNET_MQ_Envelope *e;
736 struct ClientPhoneHangupMessage *hang;
738 switch (caller->state)
741 caller->speaker->disable_speaker (caller->speaker->cls);
742 caller->mic->disable_microphone (caller->mic->cls);
747 GNUNET_CONTAINER_DLL_remove (phone->caller_head,
750 e = GNUNET_MQ_msg (hang,
751 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP);
752 hang->cid = caller->cid;
753 GNUNET_MQ_send (phone->mq,
755 GNUNET_free (caller);
762 * @param phone phone to destroy
765 GNUNET_CONVERSATION_phone_destroy (struct GNUNET_CONVERSATION_Phone *phone)
767 clean_up_callers (phone);
768 if (NULL != phone->ns)
770 GNUNET_NAMESTORE_disconnect (phone->ns);
773 if (NULL != phone->mq)
775 GNUNET_MQ_destroy (phone->mq);
783 * Pause conversation of an active call. This will disconnect the speaker
784 * and the microphone. The call can later be resumed with
785 * #GNUNET_CONVERSATION_caller_resume.
787 * @param caller call to suspend
790 GNUNET_CONVERSATION_caller_suspend (struct GNUNET_CONVERSATION_Caller *caller)
792 struct GNUNET_CONVERSATION_Phone *phone = caller->phone;
793 struct GNUNET_MQ_Envelope *e;
794 struct ClientPhoneSuspendMessage *suspend;
796 GNUNET_assert ( (CS_ACTIVE == caller->state) ||
797 (CS_CALLER_SUSPENDED == caller->state) );
798 if (CS_ACTIVE == caller->state)
800 caller->speaker->disable_speaker (caller->speaker->cls);
801 caller->mic->disable_microphone (caller->mic->cls);
803 caller->speaker = NULL;
805 e = GNUNET_MQ_msg (suspend,
806 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_SUSPEND);
807 suspend->cid = caller->cid;
808 GNUNET_MQ_send (phone->mq,
810 if (CS_ACTIVE == caller->state)
811 caller->state = CS_CALLEE_SUSPENDED;
813 caller->state = CS_BOTH_SUSPENDED;
818 * Resume suspended conversation of a phone.
820 * @param caller call to resume
821 * @param speaker speaker to use
822 * @param mic microphone to use
825 GNUNET_CONVERSATION_caller_resume (struct GNUNET_CONVERSATION_Caller *caller,
826 struct GNUNET_SPEAKER_Handle *speaker,
827 struct GNUNET_MICROPHONE_Handle *mic)
829 struct GNUNET_CONVERSATION_Phone *phone = caller->phone;
830 struct GNUNET_MQ_Envelope *e;
831 struct ClientPhoneResumeMessage *resume;
833 GNUNET_assert ( (CS_CALLEE_SUSPENDED == caller->state) ||
834 (CS_BOTH_SUSPENDED == caller->state) );
835 caller->speaker = speaker;
837 e = GNUNET_MQ_msg (resume,
838 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_RESUME);
839 resume->cid = caller->cid;
840 GNUNET_MQ_send (phone->mq,
842 if (CS_CALLEE_SUSPENDED == caller->state)
844 caller->state = CS_ACTIVE;
845 caller->speaker->enable_speaker (caller->speaker->cls);
846 caller->mic->enable_microphone (caller->mic->cls,
847 &transmit_phone_audio,
852 caller->state = CS_CALLER_SUSPENDED;
856 /* end of conversation_api.c */