2 This file is part of GNUnet
3 (C) 2013 Christian Grothoff (and other contributing authors)
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., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
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 "gnunet_gnsrecord_lib.h"
31 #include "gnunet_gns_service.h"
32 #include "conversation.h"
36 * Possible states of a caller.
41 * We still need to reverse lookup the caller ID.
46 * The phone is ringing (user knows about incoming call).
51 * The phone is in an active conversation.
56 * We suspended the conversation.
61 * Caller suspended the conversation.
66 * Both sides suspended the conversation.
74 * A caller is the handle we have for an incoming call.
76 struct GNUNET_CONVERSATION_Caller
80 * We keep all callers in a DLL.
82 struct GNUNET_CONVERSATION_Caller *next;
85 * We keep all callers in a DLL.
87 struct GNUNET_CONVERSATION_Caller *prev;
92 struct GNUNET_CONVERSATION_Phone *phone;
95 * Function to call for phone events.
97 GNUNET_CONVERSATION_CallerEventHandler event_handler;
100 * Closure for @e event_handler
102 void *event_handler_cls;
105 * Speaker, or NULL if none is attached.
107 struct GNUNET_SPEAKER_Handle *speaker;
110 * Microphone, or NULL if none is attached.
112 struct GNUNET_MICROPHONE_Handle *mic;
115 * Active NAMESTORE lookup (or NULL).
117 struct GNUNET_NAMESTORE_QueueEntry *qe;
120 * Identity of the person calling us.
122 struct GNUNET_CRYPTO_EcdsaPublicKey caller_id;
125 * Caller ID of the person calling us as a string.
130 * Internal handle to identify the caller with the service.
135 * State machine for the phone.
137 enum CallerState state;
143 * Possible states of a phone.
148 * We still need to register the phone.
153 * We are waiting for calls.
161 * A phone is a device that can ring to signal an incoming call and
162 * that you can pick up to answer the call and hang up to terminate
163 * the call. You can also hang up a ringing phone immediately
164 * (without picking it up) to stop it from ringing. Phones have
165 * caller ID. You can ask the phone for its record and make that
166 * record available (via GNS) to enable others to call you.
167 * Multiple phones maybe connected to the same line (the line is
168 * something rather internal to a phone and not obvious from it).
169 * You can only have one conversation per phone at any time.
171 struct GNUNET_CONVERSATION_Phone
176 const struct GNUNET_CONFIGURATION_Handle *cfg;
179 * Handle to talk with CONVERSATION service.
181 struct GNUNET_CLIENT_Connection *client;
184 * We keep all callers in a DLL.
186 struct GNUNET_CONVERSATION_Caller *caller_head;
189 * We keep all callers in a DLL.
191 struct GNUNET_CONVERSATION_Caller *caller_tail;
194 * Function to call for phone events.
196 GNUNET_CONVERSATION_PhoneEventHandler event_handler;
199 * Closure for @e event_handler
201 void *event_handler_cls;
204 * Connection to NAMESTORE (for reverse lookup).
206 struct GNUNET_NAMESTORE_Handle *ns;
209 * Handle for transmitting to the CONVERSATION service.
211 struct GNUNET_MQ_Handle *mq;
214 * This phone's record.
216 struct GNUNET_CONVERSATION_PhoneRecord my_record;
221 struct GNUNET_CRYPTO_EcdsaPrivateKey my_zone;
224 * State machine for the phone.
226 enum PhoneState state;
232 * The phone got disconnected, reconnect to the service.
234 * @param phone phone to reconnect
237 reconnect_phone (struct GNUNET_CONVERSATION_Phone *phone);
241 * We have resolved the caller ID using our name service.
243 * @param cls the `struct GNUNET_CONVERSATION_Caller`
244 * @param zone our zone used for resolution
245 * @param label name of the caller
246 * @param rd_count number of records we have in @a rd
247 * @param rd records we have for the caller's label
250 handle_caller_name (void *cls,
251 const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone,
253 unsigned int rd_count,
254 const struct GNUNET_GNSRECORD_Data *rd)
256 struct GNUNET_CONVERSATION_Caller *caller = cls;
257 struct GNUNET_CONVERSATION_Phone *phone = caller->phone;
262 name = GNUNET_strdup (GNUNET_GNSRECORD_pkey_to_zkey (&caller->caller_id));
264 GNUNET_asprintf (&name, "%s.gnu", label);
265 caller->caller_id_str = name;
266 caller->state = CS_RINGING;
267 phone->event_handler (phone->event_handler_cls,
268 GNUNET_CONVERSATION_EC_PHONE_RING,
275 * Process recorded audio data.
277 * @param cls closure with the `struct GNUNET_CONVERSATION_Caller`
278 * @param data_size number of bytes in @a data
279 * @param data audio data to play
282 transmit_phone_audio (void *cls,
286 struct GNUNET_CONVERSATION_Caller *caller = cls;
287 struct GNUNET_CONVERSATION_Phone *phone = caller->phone;
288 struct GNUNET_MQ_Envelope *e;
289 struct ClientAudioMessage *am;
291 e = GNUNET_MQ_msg_extra (am,
293 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO);
294 am->cid = caller->cid;
295 memcpy (&am[1], data, data_size);
296 GNUNET_MQ_send (phone->mq, e);
301 * We received a `struct ClientPhoneRingMessage`
303 * @param cls the `struct GNUNET_CONVERSATION_Phone`
304 * @param msg the message
307 handle_phone_ring (void *cls,
308 const struct GNUNET_MessageHeader *msg)
310 struct GNUNET_CONVERSATION_Phone *phone = cls;
311 const struct ClientPhoneRingMessage *ring;
312 struct GNUNET_CONVERSATION_Caller *caller;
314 ring = (const struct ClientPhoneRingMessage *) msg;
315 switch (phone->state)
321 caller = GNUNET_new (struct GNUNET_CONVERSATION_Caller);
322 caller->phone = phone;
323 GNUNET_CONTAINER_DLL_insert (phone->caller_head,
326 caller->state = CS_RESOLVE;
327 caller->caller_id = ring->caller_id;
328 caller->cid = ring->cid;
329 caller->qe = GNUNET_NAMESTORE_zone_to_name (phone->ns,
340 * We received a `struct ClientPhoneHangupMessage`.
342 * @param cls the `struct GNUNET_CONVERSATION_Phone`
343 * @param msg the message
346 handle_phone_hangup (void *cls,
347 const struct GNUNET_MessageHeader *msg)
349 struct GNUNET_CONVERSATION_Phone *phone = cls;
350 const struct ClientPhoneHangupMessage *hang;
351 struct GNUNET_CONVERSATION_Caller *caller;
353 hang = (const struct ClientPhoneHangupMessage *) msg;
354 for (caller = phone->caller_head; NULL != caller; caller = caller->next)
355 if (hang->cid == caller->cid)
359 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
360 "Received HANG_UP message for unknown caller ID %u\n",
361 (unsigned int) hang->cid);
365 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
366 "Received HANG_UP message, terminating call with `%s'\n",
367 caller->caller_id_str);
368 switch (caller->state)
371 /* application doesn't even know about call yet */
372 GNUNET_NAMESTORE_cancel (caller->qe);
376 phone->event_handler (phone->event_handler_cls,
377 GNUNET_CONVERSATION_EC_PHONE_HUNG_UP,
379 caller->caller_id_str);
382 caller->speaker->disable_speaker (caller->speaker->cls);
383 caller->mic->disable_microphone (caller->mic->cls);
384 phone->event_handler (phone->event_handler_cls,
385 GNUNET_CONVERSATION_EC_PHONE_HUNG_UP,
387 caller->caller_id_str);
389 case CS_CALLEE_SUSPENDED:
390 case CS_CALLER_SUSPENDED:
391 case CS_BOTH_SUSPENDED:
392 phone->event_handler (phone->event_handler_cls,
393 GNUNET_CONVERSATION_EC_PHONE_HUNG_UP,
395 caller->caller_id_str);
398 GNUNET_CONTAINER_DLL_remove (phone->caller_head,
401 GNUNET_free (caller);
406 * We received a `struct ClientPhoneSuspendMessage`.
408 * @param cls the `struct GNUNET_CONVERSATION_Phone`
409 * @param msg the message
412 handle_phone_suspend (void *cls,
413 const struct GNUNET_MessageHeader *msg)
415 struct GNUNET_CONVERSATION_Phone *phone = cls;
416 struct GNUNET_CONVERSATION_Caller *caller;
417 const struct ClientPhoneSuspendMessage *suspend;
419 suspend = (const struct ClientPhoneSuspendMessage *) msg;
420 for (caller = phone->caller_head; NULL != caller; caller = caller->next)
421 if (suspend->cid == caller->cid)
425 switch (caller->state)
434 caller->state = CS_CALLER_SUSPENDED;
435 caller->speaker->disable_speaker (caller->speaker->cls);
436 caller->mic->disable_microphone (caller->mic->cls);
437 caller->event_handler (caller->event_handler_cls,
438 GNUNET_CONVERSATION_EC_CALLER_SUSPEND);
440 case CS_CALLEE_SUSPENDED:
441 caller->state = CS_BOTH_SUSPENDED;
442 caller->event_handler (caller->event_handler_cls,
443 GNUNET_CONVERSATION_EC_CALLER_SUSPEND);
445 case CS_CALLER_SUSPENDED:
446 case CS_BOTH_SUSPENDED:
454 * We received a `struct ClientPhoneResumeMessage`.
456 * @param cls the `struct GNUNET_CONVERSATION_Phone`
457 * @param msg the message
460 handle_phone_resume (void *cls,
461 const struct GNUNET_MessageHeader *msg)
463 struct GNUNET_CONVERSATION_Phone *phone = cls;
464 struct GNUNET_CONVERSATION_Caller *caller;
465 const struct ClientPhoneResumeMessage *resume;
467 resume = (const struct ClientPhoneResumeMessage *) msg;
468 for (caller = phone->caller_head; NULL != caller; caller = caller->next)
469 if (resume->cid == caller->cid)
473 switch (caller->state)
482 case CS_CALLEE_SUSPENDED:
485 case CS_CALLER_SUSPENDED:
486 caller->state = CS_ACTIVE;
487 caller->speaker->enable_speaker (caller->speaker->cls);
488 caller->mic->enable_microphone (caller->mic->cls,
489 &transmit_phone_audio,
491 caller->event_handler (caller->event_handler_cls,
492 GNUNET_CONVERSATION_EC_CALLER_RESUME);
494 case CS_BOTH_SUSPENDED:
495 caller->state = CS_CALLEE_SUSPENDED;
496 caller->event_handler (caller->event_handler_cls,
497 GNUNET_CONVERSATION_EC_CALLER_RESUME);
504 * We received a `struct ClientAudioMessage`
506 * @param cls the `struct GNUNET_CONVERSATION_Phone`
507 * @param msg the message
510 handle_phone_audio_message (void *cls,
511 const struct GNUNET_MessageHeader *msg)
513 struct GNUNET_CONVERSATION_Phone *phone = cls;
514 const struct ClientAudioMessage *am;
515 struct GNUNET_CONVERSATION_Caller *caller;
517 am = (const struct ClientAudioMessage *) msg;
518 for (caller = phone->caller_head; NULL != caller; caller = caller->next)
519 if (am->cid == caller->cid)
523 switch (caller->state)
532 caller->speaker->play (caller->speaker->cls,
533 ntohs (msg->size) - sizeof (struct ClientAudioMessage),
536 case CS_CALLEE_SUSPENDED:
537 case CS_CALLER_SUSPENDED:
538 case CS_BOTH_SUSPENDED:
545 * We encountered an error talking with the conversation service.
547 * @param cls the `struct GNUNET_CONVERSATION_Phone`
548 * @param error details about the error
551 phone_error_handler (void *cls,
552 enum GNUNET_MQ_Error error)
554 struct GNUNET_CONVERSATION_Phone *phone = cls;
556 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
557 _("Connection to conversation service lost, trying to reconnect\n"));
558 reconnect_phone (phone);
563 * Clean up all callers of the given phone.
565 * @param phone phone to clean up callers for
568 clean_up_callers (struct GNUNET_CONVERSATION_Phone *phone)
570 struct GNUNET_CONVERSATION_Caller *caller;
572 while (NULL != (caller = phone->caller_head))
574 /* make sure mic/speaker are disabled *before* callback */
575 if (CS_ACTIVE == caller->state)
577 caller->speaker->disable_speaker (caller->speaker->cls);
578 caller->mic->disable_microphone (caller->mic->cls);
579 caller->state = CS_CALLER_SUSPENDED;
581 phone->event_handler (phone->event_handler_cls,
582 GNUNET_CONVERSATION_EC_PHONE_HUNG_UP,
584 caller->caller_id_str);
585 GNUNET_CONVERSATION_caller_hang_up (caller);
591 * The phone got disconnected, reconnect to the service.
593 * @param phone phone to reconnect
596 reconnect_phone (struct GNUNET_CONVERSATION_Phone *phone)
598 static struct GNUNET_MQ_MessageHandler handlers[] =
600 { &handle_phone_ring,
601 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_RING,
602 sizeof (struct ClientPhoneRingMessage) },
603 { &handle_phone_hangup,
604 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP,
605 sizeof (struct ClientPhoneHangupMessage) },
606 { &handle_phone_suspend,
607 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_SUSPEND,
608 sizeof (struct ClientPhoneSuspendMessage) },
609 { &handle_phone_resume,
610 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_RESUME,
611 sizeof (struct ClientPhoneResumeMessage) },
612 { &handle_phone_audio_message,
613 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO,
617 struct GNUNET_MQ_Envelope *e;
618 struct ClientPhoneRegisterMessage *reg;
620 clean_up_callers (phone);
621 if (NULL != phone->mq)
623 GNUNET_MQ_destroy (phone->mq);
626 if (NULL != phone->client)
628 GNUNET_CLIENT_disconnect (phone->client);
629 phone->client = NULL;
631 phone->state = PS_REGISTER;
632 phone->client = GNUNET_CLIENT_connect ("conversation", phone->cfg);
633 if (NULL == phone->client)
635 phone->mq = GNUNET_MQ_queue_for_connection_client (phone->client,
637 &phone_error_handler,
639 e = GNUNET_MQ_msg (reg, GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_REGISTER);
640 reg->line = phone->my_record.line;
641 GNUNET_MQ_send (phone->mq, e);
642 phone->state = PS_READY;
647 * Create a new phone.
649 * @param cfg configuration for the phone; specifies the phone service and
650 * which line the phone is to be connected to
651 * @param ego ego to use for name resolution (when determining caller ID)
652 * @param event_handler how to notify the owner of the phone about events
653 * @param event_handler_cls closure for @a event_handler
654 * @return NULL on error (no valid line configured)
656 struct GNUNET_CONVERSATION_Phone *
657 GNUNET_CONVERSATION_phone_create (const struct GNUNET_CONFIGURATION_Handle *cfg,
658 const struct GNUNET_IDENTITY_Ego *ego,
659 GNUNET_CONVERSATION_PhoneEventHandler event_handler,
660 void *event_handler_cls)
662 struct GNUNET_CONVERSATION_Phone *phone;
663 unsigned long long line;
666 GNUNET_CONFIGURATION_get_value_number (cfg,
671 if (line >= (1 << 31))
673 phone = GNUNET_new (struct GNUNET_CONVERSATION_Phone);
675 GNUNET_CRYPTO_get_peer_identity (cfg,
676 &phone->my_record.peer))
683 phone->my_zone = *GNUNET_IDENTITY_ego_get_private_key (ego);
684 phone->event_handler = event_handler;
685 phone->event_handler_cls = event_handler_cls;
686 phone->ns = GNUNET_NAMESTORE_connect (cfg);
687 phone->my_record.line = htonl ((uint32_t) line);
688 phone->my_record.version = htonl (0);
689 reconnect_phone (phone);
690 if ( (NULL == phone->client) ||
691 (NULL == phone->ns) )
694 GNUNET_CONVERSATION_phone_destroy (phone);
702 * Fill in a namestore record with the contact information
703 * for this phone. Note that the filled in "data" value
704 * is only valid until the phone is destroyed.
706 * @param phone phone to create a record for
707 * @param rd namestore record to fill in
710 GNUNET_CONVERSATION_phone_get_record (struct GNUNET_CONVERSATION_Phone *phone,
711 struct GNUNET_GNSRECORD_Data *rd)
713 rd->data = &phone->my_record;
714 rd->expiration_time = 0;
715 rd->data_size = sizeof (struct GNUNET_CONVERSATION_PhoneRecord);
716 rd->record_type = GNUNET_GNSRECORD_TYPE_PHONE;
717 rd->flags = GNUNET_GNSRECORD_RF_NONE;
722 * Picks up a (ringing) phone. This will connect the speaker
723 * to the microphone of the other party, and vice versa.
725 * @param caller handle that identifies which caller should be answered
726 * @param event_handler how to notify about events by the caller
727 * @param event_handler_cls closure for @a event_handler
728 * @param speaker speaker to use
729 * @param mic microphone to use
732 GNUNET_CONVERSATION_caller_pick_up (struct GNUNET_CONVERSATION_Caller *caller,
733 GNUNET_CONVERSATION_CallerEventHandler event_handler,
734 void *event_handler_cls,
735 struct GNUNET_SPEAKER_Handle *speaker,
736 struct GNUNET_MICROPHONE_Handle *mic)
738 struct GNUNET_CONVERSATION_Phone *phone = caller->phone;
739 struct GNUNET_MQ_Envelope *e;
740 struct ClientPhonePickupMessage *pick;
742 GNUNET_assert (CS_RINGING == caller->state);
743 caller->speaker = speaker;
745 e = GNUNET_MQ_msg (pick, GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_PICK_UP);
746 pick->cid = caller->cid;
747 GNUNET_MQ_send (phone->mq, e);
748 caller->state = CS_ACTIVE;
749 caller->event_handler = event_handler;
750 caller->event_handler_cls = event_handler_cls;
751 caller->speaker->enable_speaker (caller->speaker->cls);
752 caller->mic->enable_microphone (caller->mic->cls,
753 &transmit_phone_audio,
759 * Hang up up a (possibly ringing) phone. This will notify the other
760 * party that we are no longer interested in talking with them.
762 * @param caller conversation to hang up on
765 GNUNET_CONVERSATION_caller_hang_up (struct GNUNET_CONVERSATION_Caller *caller)
767 struct GNUNET_CONVERSATION_Phone *phone = caller->phone;
768 struct GNUNET_MQ_Envelope *e;
769 struct ClientPhoneHangupMessage *hang;
771 switch (caller->state)
774 GNUNET_NAMESTORE_cancel (caller->qe);
778 caller->speaker->disable_speaker (caller->speaker->cls);
779 caller->mic->disable_microphone (caller->mic->cls);
784 GNUNET_CONTAINER_DLL_remove (phone->caller_head,
787 GNUNET_free_non_null (caller->caller_id_str);
788 GNUNET_free (caller);
789 e = GNUNET_MQ_msg (hang,
790 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP);
791 GNUNET_MQ_send (phone->mq, e);
798 * @param phone phone to destroy
801 GNUNET_CONVERSATION_phone_destroy (struct GNUNET_CONVERSATION_Phone *phone)
803 clean_up_callers (phone);
804 if (NULL != phone->ns)
806 GNUNET_NAMESTORE_disconnect (phone->ns);
809 if (NULL != phone->mq)
811 GNUNET_MQ_destroy (phone->mq);
814 if (NULL != phone->client)
816 GNUNET_CLIENT_disconnect (phone->client);
817 phone->client = NULL;
824 * Pause conversation of an active call. This will disconnect the speaker
825 * and the microphone. The call can later be resumed with
826 * #GNUNET_CONVERSATION_caller_resume.
828 * @param caller call to suspend
831 GNUNET_CONVERSATION_caller_suspend (struct GNUNET_CONVERSATION_Caller *caller)
833 struct GNUNET_CONVERSATION_Phone *phone = caller->phone;
834 struct GNUNET_MQ_Envelope *e;
835 struct ClientPhoneSuspendMessage *suspend;
837 GNUNET_assert ( (CS_ACTIVE == caller->state) ||
838 (CS_CALLER_SUSPENDED == caller->state) );
839 if (CS_ACTIVE == caller->state)
841 caller->speaker->disable_speaker (caller->speaker->cls);
842 caller->mic->disable_microphone (caller->mic->cls);
844 caller->speaker = NULL;
846 e = GNUNET_MQ_msg (suspend, GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_SUSPEND);
847 suspend->cid = caller->cid;
848 GNUNET_MQ_send (phone->mq, e);
849 if (CS_ACTIVE == caller->state)
850 caller->state = CS_CALLEE_SUSPENDED;
852 caller->state = CS_BOTH_SUSPENDED;
857 * Resume suspended conversation of a phone.
859 * @param caller call to resume
860 * @param speaker speaker to use
861 * @param mic microphone to use
864 GNUNET_CONVERSATION_caller_resume (struct GNUNET_CONVERSATION_Caller *caller,
865 struct GNUNET_SPEAKER_Handle *speaker,
866 struct GNUNET_MICROPHONE_Handle *mic)
868 struct GNUNET_CONVERSATION_Phone *phone = caller->phone;
869 struct GNUNET_MQ_Envelope *e;
870 struct ClientPhoneResumeMessage *resume;
872 GNUNET_assert ( (CS_CALLEE_SUSPENDED == caller->state) ||
873 (CS_BOTH_SUSPENDED == caller->state) );
874 caller->speaker = speaker;
876 e = GNUNET_MQ_msg (resume, GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_RESUME);
877 resume->cid = caller->cid;
878 GNUNET_MQ_send (phone->mq, e);
879 if (CS_CALLEE_SUSPENDED == caller->state)
881 caller->state = CS_ACTIVE;
882 caller->speaker->enable_speaker (caller->speaker->cls);
883 caller->mic->enable_microphone (caller->mic->cls,
884 &transmit_phone_audio,
889 caller->state = CS_CALLER_SUSPENDED;
893 /* end of conversation_api.c */