- add underlay api implementation
[oweals/gnunet.git] / src / conversation / conversation_api.c
index 72437f428c8a437e4e6bcb1f7ebe2ccc3d59d054..24cd52eb275ff8b2ff40df4dc890beb9cc4147a7 100644 (file)
@@ -1,17 +1,17 @@
 /*
   This file is part of GNUnet
   (C) 2013 Christian Grothoff (and other contributing authors)
-  
+
   GNUnet is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published
   by the Free Software Foundation; either version 3, or (at your
   option) any later version.
-  
+
   GNUnet is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   General Public License for more details.
-  
+
   You should have received a copy of the GNU General Public License
   along with GNUnet; see the file COPYING.  If not, write to the
   Free Software Foundation, Inc., 59 Temple Place - Suite 330,
  */
 
 /**
- * @file conversation/conversation_api2.c
- * @brief API to the conversation service
+ * @file conversation/conversation_api.c
+ * @brief phone and caller API to the conversation service
  * @author Simon Dieterle
  * @author Andreas Fuchs
  * @author Christian Grothoff
  */
 #include "platform.h"
 #include "gnunet_conversation_service.h"
+#include "gnunet_gnsrecord_lib.h"
 #include "gnunet_gns_service.h"
 #include "conversation.h"
 
 
 /**
- * Possible states of the phone.
+ * Possible states of a caller.
  */
-enum PhoneState
+enum CallerState
 {
   /**
-   * We still need to register the phone.
+   * We still need to reverse lookup the caller ID.
    */
-  PS_REGISTER = 0,
+  CS_RESOLVE,
+
+  /**
+   * The phone is ringing (user knows about incoming call).
+   */
+  CS_RINGING,
+
+  /**
+   * The phone is in an active conversation.
+   */
+  CS_ACTIVE,
 
   /**
-   * We are waiting for a call.
+   * We suspended the conversation.
    */
-  PS_WAITING,
+  CS_CALLEE_SUSPENDED,
 
   /**
-   * The phone is ringing.
+   * Caller suspended the conversation.
    */
-  PS_RINGING,
+  CS_CALLER_SUSPENDED,
 
   /**
-   * The phone is in an active conversation.
+   * Both sides suspended the conversation.
+   */
+  CS_BOTH_SUSPENDED
+};
+
+
+
+/**
+ * A caller is the handle we have for an incoming call.
+ */
+struct GNUNET_CONVERSATION_Caller
+{
+
+  /**
+   * We keep all callers in a DLL.
+   */
+  struct GNUNET_CONVERSATION_Caller *next;
+
+  /**
+   * We keep all callers in a DLL.
+   */
+  struct GNUNET_CONVERSATION_Caller *prev;
+
+  /**
+   * Our phone.
+   */
+  struct GNUNET_CONVERSATION_Phone *phone;
+
+  /**
+   * Function to call for phone events.
+   */
+  GNUNET_CONVERSATION_CallerEventHandler event_handler;
+
+  /**
+   * Closure for @e event_handler
+   */
+  void *event_handler_cls;
+
+  /**
+   * Speaker, or NULL if none is attached.
+   */
+  struct GNUNET_SPEAKER_Handle *speaker;
+
+  /**
+   * Microphone, or NULL if none is attached.
+   */
+  struct GNUNET_MICROPHONE_Handle *mic;
+
+  /**
+   * Active NAMESTORE lookup (or NULL).
+   */
+  struct GNUNET_NAMESTORE_QueueEntry *qe;
+
+  /**
+   * Identity of the person calling us.
+   */
+  struct GNUNET_CRYPTO_EcdsaPublicKey caller_id;
+
+  /**
+   * Caller ID of the person calling us as a string.
    */
-  PS_ACTIVE
+  char *caller_id_str;
+
+  /**
+   * Internal handle to identify the caller with the service.
+   */
+  uint32_t cid;
+
+  /**
+   * State machine for the phone.
+   */
+  enum CallerState state;
+
+};
+
+
+/**
+ * Possible states of a phone.
+ */
+enum PhoneState
+{
+  /**
+   * We still need to register the phone.
+   */
+  PS_REGISTER = 0,
+
+  /**
+   * We are waiting for calls.
+   */
+  PS_READY
+
 };
 
 
@@ -75,41 +174,36 @@ struct GNUNET_CONVERSATION_Phone
    * Our configuration.
    */
   const struct GNUNET_CONFIGURATION_Handle *cfg;
-  
+
   /**
    * Handle to talk with CONVERSATION service.
    */
   struct GNUNET_CLIENT_Connection *client;
 
   /**
-   * Function to call for phone events.
+   * We keep all callers in a DLL.
    */
-  GNUNET_CONVERSATION_EventHandler event_handler;
+  struct GNUNET_CONVERSATION_Caller *caller_head;
 
   /**
-   * Closure for @e event_handler
+   * We keep all callers in a DLL.
    */
-  void *event_handler_cls;
+  struct GNUNET_CONVERSATION_Caller *caller_tail;
 
   /**
-   * Speaker, or NULL if none is attached.
+   * Function to call for phone events.
    */
-  struct GNUNET_SPEAKER_Handle *speaker;
+  GNUNET_CONVERSATION_PhoneEventHandler event_handler;
 
   /**
-   * Microphone, or NULL if none is attached.
+   * Closure for @e event_handler
    */
-  struct GNUNET_MICROPHONE_Handle *mic;
+  void *event_handler_cls;
 
   /**
    * Connection to NAMESTORE (for reverse lookup).
-   */ 
-  struct GNUNET_NAMESTORE_Handle *ns;
-
-  /**
-   * Active NAMESTORE lookup (or NULL).
    */
-  struct GNUNET_NAMESTORE_QueueEntry *qe;
+  struct GNUNET_NAMESTORE_Handle *ns;
 
   /**
    * Handle for transmitting to the CONVERSATION service.
@@ -119,17 +213,12 @@ struct GNUNET_CONVERSATION_Phone
   /**
    * This phone's record.
    */
-  struct GNUNET_CONVERSATION_PhoneRecord my_record;  
+  struct GNUNET_CONVERSATION_PhoneRecord my_record;
 
   /**
    * My GNS zone.
    */
-  struct GNUNET_CRYPTO_EccPrivateKey my_zone;
-
-  /**
-   * Identity of the person calling us (valid while in state #PS_RINGING).
-   */
-  struct GNUNET_CRYPTO_EccPublicSignKey caller_id;
+  struct GNUNET_CRYPTO_EcdsaPrivateKey my_zone;
 
   /**
    * State machine for the phone.
@@ -151,7 +240,7 @@ reconnect_phone (struct GNUNET_CONVERSATION_Phone *phone);
 /**
  * We have resolved the caller ID using our name service.
  *
- * @param cls the `struct GNUNET_CONVERSATION_Phone`
+ * @param cls the `struct GNUNET_CONVERSATION_Caller`
  * @param zone our zone used for resolution
  * @param label name of the caller
  * @param rd_count number of records we have in @a rd
@@ -159,23 +248,52 @@ reconnect_phone (struct GNUNET_CONVERSATION_Phone *phone);
  */
 static void
 handle_caller_name (void *cls,
-                    const struct GNUNET_CRYPTO_EccPrivateKey *zone,
+                    const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone,
                     const char *label,
                     unsigned int rd_count,
-                    const struct GNUNET_NAMESTORE_RecordData *rd)
+                    const struct GNUNET_GNSRECORD_Data *rd)
 {
-  struct GNUNET_CONVERSATION_Phone *phone = cls;
+  struct GNUNET_CONVERSATION_Caller *caller = cls;
+  struct GNUNET_CONVERSATION_Phone *phone = caller->phone;
   char *name;
 
-  phone->qe = NULL;
+  caller->qe = NULL;
   if (NULL == label)
-    name = GNUNET_strdup (GNUNET_NAMESTORE_pkey_to_zkey (&phone->caller_id));
+    name = GNUNET_strdup (GNUNET_GNSRECORD_pkey_to_zkey (&caller->caller_id));
   else
-    GNUNET_asprintf (&name, "%.gnu", label);
+    GNUNET_asprintf (&name, "%s.gnu", label);
+  caller->caller_id_str = name;
+  caller->state = CS_RINGING;
   phone->event_handler (phone->event_handler_cls,
-                        GNUNET_CONVERSATION_EC_RING,
+                        GNUNET_CONVERSATION_EC_PHONE_RING,
+                        caller,
                         name);
-  GNUNET_free (name);
+}
+
+
+/**
+ * Process recorded audio data.
+ *
+ * @param cls closure with the `struct GNUNET_CONVERSATION_Caller`
+ * @param data_size number of bytes in @a data
+ * @param data audio data to play
+ */
+static void
+transmit_phone_audio (void *cls,
+                      size_t data_size,
+                      const void *data)
+{
+  struct GNUNET_CONVERSATION_Caller *caller = cls;
+  struct GNUNET_CONVERSATION_Phone *phone = caller->phone;
+  struct GNUNET_MQ_Envelope *e;
+  struct ClientAudioMessage *am;
+
+  e = GNUNET_MQ_msg_extra (am,
+                           data_size,
+                           GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO);
+  am->cid = caller->cid;
+  memcpy (&am[1], data, data_size);
+  GNUNET_MQ_send (phone->mq, e);
 }
 
 
@@ -191,6 +309,7 @@ handle_phone_ring (void *cls,
 {
   struct GNUNET_CONVERSATION_Phone *phone = cls;
   const struct ClientPhoneRingMessage *ring;
+  struct GNUNET_CONVERSATION_Caller *caller;
 
   ring = (const struct ClientPhoneRingMessage *) msg;
   switch (phone->state)
@@ -198,22 +317,20 @@ handle_phone_ring (void *cls,
   case PS_REGISTER:
     GNUNET_assert (0);
     break;
-  case PS_WAITING:
-    phone->state = PS_RINGING;
-    phone->caller_id = ring->caller_id;
-    phone->qe = GNUNET_NAMESTORE_zone_to_name (phone->ns,
-                                               &phone->my_zone,
-                                               &ring->caller_id,
-                                               &handle_caller_name,
-                                               phone);
-    break;
-  case PS_RINGING:
-    GNUNET_break (0);
-    reconnect_phone (phone);
-    break;
-  case PS_ACTIVE:
-    GNUNET_break (0);
-    reconnect_phone (phone);
+  case PS_READY:
+    caller = GNUNET_new (struct GNUNET_CONVERSATION_Caller);
+    caller->phone = phone;
+    GNUNET_CONTAINER_DLL_insert (phone->caller_head,
+                                 phone->caller_tail,
+                                 caller);
+    caller->state = CS_RESOLVE;
+    caller->caller_id = ring->caller_id;
+    caller->cid = ring->cid;
+    caller->qe = GNUNET_NAMESTORE_zone_to_name (phone->ns,
+                                                &phone->my_zone,
+                                                &ring->caller_id,
+                                                &handle_caller_name,
+                                                caller);
     break;
   }
 }
@@ -231,47 +348,153 @@ handle_phone_hangup (void *cls,
 {
   struct GNUNET_CONVERSATION_Phone *phone = cls;
   const struct ClientPhoneHangupMessage *hang;
-  size_t len;
-  const char *reason;
+  struct GNUNET_CONVERSATION_Caller *caller;
 
   hang = (const struct ClientPhoneHangupMessage *) msg;
-  reason = (const char *) &hang[1];
-  len = htons (hang->header.size) - sizeof (struct ClientPhoneHangupMessage);
-  if ( (0 == len) ||
-       ('\0' != reason[len-1]) )
+  for (caller = phone->caller_head; NULL != caller; caller = caller->next)
+    if (hang->cid == caller->cid)
+      break;
+  if (NULL == caller)
   {
-    GNUNET_break (0);
-    reconnect_phone (phone);
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "Received HANG_UP message for unknown caller ID %u\n",
+                (unsigned int) hang->cid);
     return;
   }
-  switch (phone->state)
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Received HANG_UP message, terminating call with `%s'\n",
+              caller->caller_id_str);
+  switch (caller->state)
   {
-  case PS_REGISTER:
-    GNUNET_assert (0);
+  case CS_RESOLVE:
+    /* application doesn't even know about call yet */
+    GNUNET_NAMESTORE_cancel (caller->qe);
+    caller->qe = NULL;
     break;
-  case PS_WAITING:
-    GNUNET_break (0);
-    reconnect_phone (phone);
+  case CS_RINGING:
+    phone->event_handler (phone->event_handler_cls,
+                          GNUNET_CONVERSATION_EC_PHONE_HUNG_UP,
+                          caller,
+                          caller->caller_id_str);
     break;
-  case PS_RINGING:
-    if (NULL != phone->qe)
-    {
-      GNUNET_NAMESTORE_cancel (phone->qe);
-      phone->qe = NULL;
-      phone->state = PS_WAITING;
-      break;
-    }
-    phone->state = PS_WAITING;
+  case CS_ACTIVE:
+    caller->speaker->disable_speaker (caller->speaker->cls);
+    caller->mic->disable_microphone (caller->mic->cls);
     phone->event_handler (phone->event_handler_cls,
-                          GNUNET_CONVERSATION_EC_TERMINATED,
-                          reason);
+                          GNUNET_CONVERSATION_EC_PHONE_HUNG_UP,
+                          caller,
+                          caller->caller_id_str);
     break;
-  case PS_ACTIVE:
-    GNUNET_break (NULL == phone->qe);
-    phone->state = PS_WAITING;
+  case CS_CALLEE_SUSPENDED:
+  case CS_CALLER_SUSPENDED:
+  case CS_BOTH_SUSPENDED:
     phone->event_handler (phone->event_handler_cls,
-                          GNUNET_CONVERSATION_EC_TERMINATED,
-                          reason);
+                          GNUNET_CONVERSATION_EC_PHONE_HUNG_UP,
+                          caller,
+                          caller->caller_id_str);
+    break;
+  }
+  GNUNET_CONTAINER_DLL_remove (phone->caller_head,
+                               phone->caller_tail,
+                               caller);
+  GNUNET_free (caller);
+}
+
+
+/**
+ * We received a `struct ClientPhoneSuspendMessage`.
+ *
+ * @param cls the `struct GNUNET_CONVERSATION_Phone`
+ * @param msg the message
+ */
+static void
+handle_phone_suspend (void *cls,
+                     const struct GNUNET_MessageHeader *msg)
+{
+  struct GNUNET_CONVERSATION_Phone *phone = cls;
+  struct GNUNET_CONVERSATION_Caller *caller;
+  const struct ClientPhoneSuspendMessage *suspend;
+
+  suspend = (const struct ClientPhoneSuspendMessage *) msg;
+  for (caller = phone->caller_head; NULL != caller; caller = caller->next)
+    if (suspend->cid == caller->cid)
+      break;
+  if (NULL == caller)
+    return;
+  switch (caller->state)
+  {
+  case CS_RESOLVE:
+    GNUNET_break_op (0);
+    break;
+  case CS_RINGING:
+    GNUNET_break_op (0);
+    break;
+  case CS_ACTIVE:
+    caller->state = CS_CALLER_SUSPENDED;
+    caller->speaker->disable_speaker (caller->speaker->cls);
+    caller->mic->disable_microphone (caller->mic->cls);
+    caller->event_handler (caller->event_handler_cls,
+                           GNUNET_CONVERSATION_EC_CALLER_SUSPEND);
+    break;
+  case CS_CALLEE_SUSPENDED:
+    caller->state = CS_BOTH_SUSPENDED;
+    caller->event_handler (caller->event_handler_cls,
+                           GNUNET_CONVERSATION_EC_CALLER_SUSPEND);
+    break;
+  case CS_CALLER_SUSPENDED:
+  case CS_BOTH_SUSPENDED:
+    GNUNET_break_op (0);
+    break;
+  }
+}
+
+
+/**
+ * We received a `struct ClientPhoneResumeMessage`.
+ *
+ * @param cls the `struct GNUNET_CONVERSATION_Phone`
+ * @param msg the message
+ */
+static void
+handle_phone_resume (void *cls,
+                     const struct GNUNET_MessageHeader *msg)
+{
+  struct GNUNET_CONVERSATION_Phone *phone = cls;
+  struct GNUNET_CONVERSATION_Caller *caller;
+  const struct ClientPhoneResumeMessage *resume;
+
+  resume = (const struct ClientPhoneResumeMessage *) msg;
+  for (caller = phone->caller_head; NULL != caller; caller = caller->next)
+    if (resume->cid == caller->cid)
+      break;
+  if (NULL == caller)
+    return;
+  switch (caller->state)
+  {
+  case CS_RESOLVE:
+    GNUNET_break_op (0);
+    break;
+  case CS_RINGING:
+    GNUNET_break_op (0);
+    break;
+  case CS_ACTIVE:
+  case CS_CALLEE_SUSPENDED:
+    GNUNET_break_op (0);
+    break;
+  case CS_CALLER_SUSPENDED:
+    caller->state = CS_ACTIVE;
+    caller->speaker->enable_speaker (caller->speaker->cls);
+    caller->mic->enable_microphone (caller->mic->cls,
+                                    &transmit_phone_audio,
+                                    caller);
+    caller->event_handler (caller->event_handler_cls,
+                           GNUNET_CONVERSATION_EC_CALLER_RESUME);
+    break;
+  case CS_BOTH_SUSPENDED:
+    caller->state = CS_CALLEE_SUSPENDED;
+    caller->event_handler (caller->event_handler_cls,
+                           GNUNET_CONVERSATION_EC_CALLER_RESUME);
     break;
   }
 }
@@ -289,32 +512,37 @@ handle_phone_audio_message (void *cls,
 {
   struct GNUNET_CONVERSATION_Phone *phone = cls;
   const struct ClientAudioMessage *am;
+  struct GNUNET_CONVERSATION_Caller *caller;
 
   am = (const struct ClientAudioMessage *) msg;
-  switch (phone->state)
+  for (caller = phone->caller_head; NULL != caller; caller = caller->next)
+    if (am->cid == caller->cid)
+      break;
+  if (NULL == caller)
+    return;
+  switch (caller->state)
   {
-  case PS_REGISTER:
-    GNUNET_assert (0);
+  case CS_RESOLVE:
+    GNUNET_break_op (0);
     break;
-  case PS_WAITING:
-    GNUNET_break (0);
-    reconnect_phone (phone);
+  case CS_RINGING:
+    GNUNET_break_op (0);
     break;
-  case PS_RINGING:
-    GNUNET_break (0);
-    reconnect_phone (phone);
+  case CS_ACTIVE:
+    caller->speaker->play (caller->speaker->cls,
+                           ntohs (msg->size) - sizeof (struct ClientAudioMessage),
+                           &am[1]);
     break;
-  case PS_ACTIVE:
-    phone->speaker->play (phone->speaker->cls,
-                          ntohs (msg->size) - sizeof (struct ClientAudioMessage),
-                          &am[1]);
+  case CS_CALLEE_SUSPENDED:
+  case CS_CALLER_SUSPENDED:
+  case CS_BOTH_SUSPENDED:
     break;
   }
 }
 
 
 /**
- * We encountered an error talking with the conversation service. 
+ * We encountered an error talking with the conversation service.
  *
  * @param cls the `struct GNUNET_CONVERSATION_Phone`
  * @param error details about the error
@@ -324,15 +552,41 @@ phone_error_handler (void *cls,
                      enum GNUNET_MQ_Error error)
 {
   struct GNUNET_CONVERSATION_Phone *phone = cls;
-  
-  GNUNET_break (0);
-  FPRINTF (stderr,
-           _("Internal error %d\n"),
-           error);
+
+  GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+              _("Connection to conversation service lost, trying to reconnect\n"));
   reconnect_phone (phone);
 }
 
 
+/**
+ * Clean up all callers of the given phone.
+ *
+ * @param phone phone to clean up callers for
+ */
+static void
+clean_up_callers (struct GNUNET_CONVERSATION_Phone *phone)
+{
+  struct GNUNET_CONVERSATION_Caller *caller;
+
+  while (NULL != (caller = phone->caller_head))
+  {
+    /* make sure mic/speaker are disabled *before* callback */
+    if (CS_ACTIVE == caller->state)
+    {
+      caller->speaker->disable_speaker (caller->speaker->cls);
+      caller->mic->disable_microphone (caller->mic->cls);
+      caller->state = CS_CALLER_SUSPENDED;
+    }
+    phone->event_handler (phone->event_handler_cls,
+                          GNUNET_CONVERSATION_EC_PHONE_HUNG_UP,
+                          caller,
+                          caller->caller_id_str);
+    GNUNET_CONVERSATION_caller_hang_up (caller);
+  }
+}
+
+
 /**
  * The phone got disconnected, reconnect to the service.
  *
@@ -344,19 +598,26 @@ reconnect_phone (struct GNUNET_CONVERSATION_Phone *phone)
   static struct GNUNET_MQ_MessageHandler handlers[] =
   {
     { &handle_phone_ring,
-      GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_RING, 
+      GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_RING,
       sizeof (struct ClientPhoneRingMessage) },
     { &handle_phone_hangup,
       GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP,
-      0 },
+      sizeof (struct ClientPhoneHangupMessage) },
+    { &handle_phone_suspend,
+      GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_SUSPEND,
+      sizeof (struct ClientPhoneSuspendMessage) },
+    { &handle_phone_resume,
+      GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_RESUME,
+      sizeof (struct ClientPhoneResumeMessage) },
     { &handle_phone_audio_message,
       GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO,
-      0 },    
-    { NULL, 0, 0 }    
+      0 },
+    { NULL, 0, 0 }
   };
   struct GNUNET_MQ_Envelope *e;
   struct ClientPhoneRegisterMessage *reg;
 
+  clean_up_callers (phone);
   if (NULL != phone->mq)
   {
     GNUNET_MQ_destroy (phone->mq);
@@ -378,7 +639,7 @@ reconnect_phone (struct GNUNET_CONVERSATION_Phone *phone)
   e = GNUNET_MQ_msg (reg, GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_REGISTER);
   reg->line = phone->my_record.line;
   GNUNET_MQ_send (phone->mq, e);
-  phone->state = PS_WAITING;
+  phone->state = PS_READY;
 }
 
 
@@ -390,25 +651,28 @@ reconnect_phone (struct GNUNET_CONVERSATION_Phone *phone)
  * @param ego ego to use for name resolution (when determining caller ID)
  * @param event_handler how to notify the owner of the phone about events
  * @param event_handler_cls closure for @a event_handler
+ * @return NULL on error (no valid line configured)
  */
 struct GNUNET_CONVERSATION_Phone *
 GNUNET_CONVERSATION_phone_create (const struct GNUNET_CONFIGURATION_Handle *cfg,
                                   const struct GNUNET_IDENTITY_Ego *ego,
-                                  GNUNET_CONVERSATION_EventHandler event_handler,
+                                  GNUNET_CONVERSATION_PhoneEventHandler event_handler,
                                  void *event_handler_cls)
 {
   struct GNUNET_CONVERSATION_Phone *phone;
   unsigned long long line;
 
   if (GNUNET_OK !=
-      GNUNET_CONFIGURATION_get_value_number (cfg, 
+      GNUNET_CONFIGURATION_get_value_number (cfg,
                                              "CONVERSATION",
                                              "LINE",
                                              &line))
     return NULL;
+  if (line >= (1 << 31))
+    return NULL;
   phone = GNUNET_new (struct GNUNET_CONVERSATION_Phone);
   if (GNUNET_OK !=
-      GNUNET_CRYPTO_get_host_identity (cfg,
+      GNUNET_CRYPTO_get_peer_identity (cfg,
                                        &phone->my_record.peer))
   {
     GNUNET_break (0);
@@ -444,72 +708,50 @@ GNUNET_CONVERSATION_phone_create (const struct GNUNET_CONFIGURATION_Handle *cfg,
  */
 void
 GNUNET_CONVERSATION_phone_get_record (struct GNUNET_CONVERSATION_Phone *phone,
-                                     struct GNUNET_NAMESTORE_RecordData *rd)
+                                     struct GNUNET_GNSRECORD_Data *rd)
 {
   rd->data = &phone->my_record;
   rd->expiration_time = 0;
   rd->data_size = sizeof (struct GNUNET_CONVERSATION_PhoneRecord);
-  rd->record_type = GNUNET_NAMESTORE_TYPE_PHONE;
-  rd->flags = GNUNET_NAMESTORE_RF_NONE;
+  rd->record_type = GNUNET_GNSRECORD_TYPE_PHONE;
+  rd->flags = GNUNET_GNSRECORD_RF_NONE;
 }
 
 
 /**
- * Process recorded audio data.
- *
- * @param cls closure with the `struct GNUNET_CONVERSATION_Phone`
- * @param data_size number of bytes in @a data
- * @param data audio data to play
- */
-static void
-transmit_phone_audio (void *cls,
-                      size_t data_size,
-                      const void *data)
-{
-  struct GNUNET_CONVERSATION_Phone *phone = cls;
-  struct GNUNET_MQ_Envelope *e;
-  struct ClientAudioMessage *am;
-
-  GNUNET_assert (PS_ACTIVE == phone->state);
-  e = GNUNET_MQ_msg_extra (am, 
-                           data_size,
-                           GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO);
-  memcpy (&am[1], data, data_size);
-  GNUNET_MQ_send (phone->mq, e);
-}
-
-
-/**
- * Picks up a (ringing) phone.  This will connect the speaker 
+ * Picks up a (ringing) phone.  This will connect the speaker
  * to the microphone of the other party, and vice versa.
  *
- * @param phone phone to pick up
- * @param metadata meta data to give to the other user about the pick up event
+ * @param caller handle that identifies which caller should be answered
+ * @param event_handler how to notify about events by the caller
+ * @param event_handler_cls closure for @a event_handler
  * @param speaker speaker to use
  * @param mic microphone to use
  */
 void
-GNUNET_CONVERSATION_phone_pick_up (struct GNUNET_CONVERSATION_Phone *phone,
-                                   const char *metadata,
-                                   struct GNUNET_SPEAKER_Handle *speaker,
-                                   struct GNUNET_MICROPHONE_Handle *mic)
+GNUNET_CONVERSATION_caller_pick_up (struct GNUNET_CONVERSATION_Caller *caller,
+                                    GNUNET_CONVERSATION_CallerEventHandler event_handler,
+                                    void *event_handler_cls,
+                                    struct GNUNET_SPEAKER_Handle *speaker,
+                                    struct GNUNET_MICROPHONE_Handle *mic)
 {
+  struct GNUNET_CONVERSATION_Phone *phone = caller->phone;
   struct GNUNET_MQ_Envelope *e;
   struct ClientPhonePickupMessage *pick;
-  size_t slen;
-
-  GNUNET_assert (PS_RINGING == phone->state);
-  phone->speaker = speaker;
-  phone->mic = mic;
-  slen = strlen (metadata) + 1;
-  e = GNUNET_MQ_msg_extra (pick, slen, GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_PICK_UP);
-  memcpy (&pick[1], metadata, slen);
+
+  GNUNET_assert (CS_RINGING == caller->state);
+  caller->speaker = speaker;
+  caller->mic = mic;
+  e = GNUNET_MQ_msg (pick, GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_PICK_UP);
+  pick->cid = caller->cid;
   GNUNET_MQ_send (phone->mq, e);
-  phone->state = PS_ACTIVE;
-  phone->speaker->enable_speaker (phone->speaker->cls);
-  phone->mic->enable_microphone (phone->mic->cls,
-                                 &transmit_phone_audio,
-                                 phone);
+  caller->state = CS_ACTIVE;
+  caller->event_handler = event_handler;
+  caller->event_handler_cls = event_handler_cls;
+  caller->speaker->enable_speaker (caller->speaker->cls);
+  caller->mic->enable_microphone (caller->mic->cls,
+                                  &transmit_phone_audio,
+                                  caller);
 }
 
 
@@ -517,28 +759,36 @@ GNUNET_CONVERSATION_phone_pick_up (struct GNUNET_CONVERSATION_Phone *phone,
  * Hang up up a (possibly ringing) phone.  This will notify the other
  * party that we are no longer interested in talking with them.
  *
- * @param phone phone to pick up
- * @param reason text we give to the other party about why we terminated the conversation
+ * @param caller conversation to hang up on
  */
 void
-GNUNET_CONVERSATION_phone_hang_up (struct GNUNET_CONVERSATION_Phone *phone,
-                                   const char *reason)
+GNUNET_CONVERSATION_caller_hang_up (struct GNUNET_CONVERSATION_Caller *caller)
 {
+  struct GNUNET_CONVERSATION_Phone *phone = caller->phone;
   struct GNUNET_MQ_Envelope *e;
   struct ClientPhoneHangupMessage *hang;
-  size_t slen;
-
-  GNUNET_assert ( (PS_RINGING == phone->state) ||
-                  (PS_ACTIVE == phone->state) );
-  phone->speaker->disable_speaker (phone->speaker->cls);
-  phone->mic->disable_microphone (phone->mic->cls);
-  phone->speaker = NULL;
-  phone->mic = NULL;
-  slen = strlen (reason) + 1;
-  e = GNUNET_MQ_msg_extra (hang, slen, GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP);
-  memcpy (&hang[1], reason, slen);
+
+  switch (caller->state)
+  {
+  case CS_RESOLVE:
+    GNUNET_NAMESTORE_cancel (caller->qe);
+    caller->qe = NULL;
+    break;
+  case CS_ACTIVE:
+    caller->speaker->disable_speaker (caller->speaker->cls);
+    caller->mic->disable_microphone (caller->mic->cls);
+    break;
+  default:
+    break;
+  }
+  GNUNET_CONTAINER_DLL_remove (phone->caller_head,
+                               phone->caller_tail,
+                               caller);
+  GNUNET_free_non_null (caller->caller_id_str);
+  GNUNET_free (caller);
+  e = GNUNET_MQ_msg (hang,
+                     GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP);
   GNUNET_MQ_send (phone->mq, e);
-  phone->state = PS_WAITING;
 }
 
 
@@ -550,21 +800,7 @@ GNUNET_CONVERSATION_phone_hang_up (struct GNUNET_CONVERSATION_Phone *phone,
 void
 GNUNET_CONVERSATION_phone_destroy (struct GNUNET_CONVERSATION_Phone *phone)
 {
-  if (NULL != phone->speaker)
-  {
-    phone->speaker->disable_speaker (phone->speaker->cls);
-    phone->speaker = NULL;
-  }
-  if (NULL != phone->mic)
-  {
-    phone->mic->disable_microphone (phone->mic->cls);
-    phone->mic = NULL;
-  }
-  if (NULL != phone->qe)
-  {
-    GNUNET_NAMESTORE_cancel (phone->qe);
-    phone->qe = NULL;
-  }
+  clean_up_callers (phone);
   if (NULL != phone->ns)
   {
     GNUNET_NAMESTORE_disconnect (phone->ns);
@@ -584,524 +820,74 @@ GNUNET_CONVERSATION_phone_destroy (struct GNUNET_CONVERSATION_Phone *phone)
 }
 
 
-/* ******************************* Call API *************************** */
-
-/**
- * Possible states of the phone.
- */
-enum CallState
-{
-  /**
-   * We still need to lookup the callee.
-   */
-  CS_LOOKUP = 0,
-
-  /**
-   * The call is ringing.
-   */
-  CS_RINGING,
-
-  /**
-   * The call is in an active conversation.
-   */
-  CS_ACTIVE,
-
-  /**
-   * The call is in termination.
-   */
-  CS_SHUTDOWN
-};
-
-
-/**
- * Handle for an outgoing call.
- */
-struct GNUNET_CONVERSATION_Call
-{
-
-  /**
-   * Our configuration.
-   */
-  const struct GNUNET_CONFIGURATION_Handle *cfg;
-  
-  /**
-   * Handle to talk with CONVERSATION service.
-   */
-  struct GNUNET_CLIENT_Connection *client;
-
-  /**
-   * Our caller identity.
-   */
-  struct GNUNET_IDENTITY_Ego *caller_id;
-
-  /**
-   * Target callee as a GNS address/name.
-   */
-  char *callee;
-
-  /**
-   * Our speaker.
-   */
-  struct GNUNET_SPEAKER_Handle *speaker;
-
-  /**
-   * Our microphone.
-   */
-  struct GNUNET_MICROPHONE_Handle *mic;
-  
-  /**
-   * Function to call with events.
-   */
-  GNUNET_CONVERSATION_EventHandler event_handler;
-
-  /**
-   * Closure for @e event_handler
-   */
-  void *event_handler_cls;
-
-  /**
-   * Handle for transmitting to the CONVERSATION service.
-   */
-  struct GNUNET_MQ_Handle *mq;
-
-  /**
-   * Connection to GNS (can be NULL).
-   */ 
-  struct GNUNET_GNS_Handle *gns;
-
-  /**
-   * Active GNS lookup (or NULL).
-   */
-  struct GNUNET_GNS_LookupRequest *gns_lookup;
-
-  /**
-   * Target phone record, only valid after the lookup is done.
-   */
-  struct GNUNET_CONVERSATION_PhoneRecord phone_record;
-
-  /**
-   * State machine for the call.
-   */
-  enum CallState state;
-
-};
-
-
-/**
- * The call got disconnected, reconnect to the service.
- *
- * @param call call to reconnect
- */
-static void
-reconnect_call (struct GNUNET_CONVERSATION_Call *call);
-
-
-/**
- * We received a `struct ClientPhoneBusyMessage`
- *
- * @param cls the `struct GNUNET_CONVERSATION_Call`
- * @param msg the message
- */
-static void
-handle_call_busy (void *cls,
-                  const struct GNUNET_MessageHeader *msg)
-{
-  struct GNUNET_CONVERSATION_Call *call = cls;
-
-  switch (call->state)
-  {
-  case CS_LOOKUP:
-    GNUNET_break (0);
-    reconnect_call (call);
-    break;
-  case CS_RINGING:
-    call->event_handler (call->event_handler_cls,
-                         GNUNET_CONVERSATION_EC_BUSY);
-    GNUNET_CONVERSATION_call_stop (call, NULL);
-    break;
-  case CS_ACTIVE:
-    GNUNET_break (0);
-    reconnect_call (call);
-    break;
-  case CS_SHUTDOWN:
-    GNUNET_CONVERSATION_call_stop (call, NULL);
-    break;
-  }
-}
-
-
-/**
- * Process recorded audio data.
- *
- * @param cls closure with the `struct GNUNET_CONVERSATION_Call`
- * @param data_size number of bytes in @a data
- * @param data audio data to play
- */
-static void
-transmit_call_audio (void *cls,
-                     size_t data_size,
-                     const void *data)
-{
-  struct GNUNET_CONVERSATION_Call *call = cls;
-  struct GNUNET_MQ_Envelope *e;
-  struct ClientAudioMessage *am;
-
-  GNUNET_assert (CS_ACTIVE == call->state);
-  e = GNUNET_MQ_msg_extra (am, 
-                           data_size,
-                           GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO);
-  memcpy (&am[1], data, data_size);
-  GNUNET_MQ_send (call->mq, e);
-}
-
-
-/**
- * We received a `struct ClientPhonePickedupMessage`
- *
- * @param cls the `struct GNUNET_CONVERSATION_Call`
- * @param msg the message
- */
-static void
-handle_call_picked_up (void *cls,
-                       const struct GNUNET_MessageHeader *msg)
-{
-  struct GNUNET_CONVERSATION_Call *call = cls;
-  const struct ClientPhonePickedupMessage *am;
-  const char *metadata;
-  size_t size;
-
-  am = (const struct ClientPhonePickedupMessage *) msg;
-  size = ntohs (am->header.size) - sizeof (struct ClientPhonePickedupMessage);
-  metadata = (const char *) &am[1];
-  if ( (0 == size) ||
-       ('\0' != metadata[size - 1]) )
-    metadata = NULL;  
-  switch (call->state)
-  {
-  case CS_LOOKUP:
-    GNUNET_break (0);
-    reconnect_call (call);
-    break;
-  case CS_RINGING:
-    call->state = CS_ACTIVE;
-    call->event_handler (call->event_handler_cls,
-                         GNUNET_CONVERSATION_EC_READY,
-                         metadata);
-    call->speaker->enable_speaker (call->speaker->cls);
-    call->mic->enable_microphone (call->mic->cls,
-                                  &transmit_call_audio,
-                                  call);
-    break;
-  case CS_ACTIVE:
-    GNUNET_break (0);
-    reconnect_call (call);
-    break;
-  case CS_SHUTDOWN:
-    GNUNET_CONVERSATION_call_stop (call, NULL);
-    break;
-  }
-}
-
-
 /**
- * We received a `struct ClientPhoneHangupMessage`
+ * Pause conversation of an active call.  This will disconnect the speaker
+ * and the microphone.  The call can later be resumed with
+ * #GNUNET_CONVERSATION_caller_resume.
  *
- * @param cls the `struct GNUNET_CONVERSATION_Call`
- * @param msg the message
+ * @param caller call to suspend
  */
-static void
-handle_call_hangup (void *cls,
-                    const struct GNUNET_MessageHeader *msg)
-{
-  struct GNUNET_CONVERSATION_Call *call = cls;
-  const struct ClientPhoneHangupMessage *am;
-  const char *reason;
-  size_t size;
-
-  am = (const struct ClientPhoneHangupMessage *) msg;
-  size = ntohs (am->header.size) - sizeof (struct ClientPhoneHangupMessage);
-  reason = (const char *) &am[1];
-  if ( (0 == size) ||
-       ('\0' != reason[size - 1]) )
-    reason = NULL;  
-  switch (call->state)
-  {
-  case CS_LOOKUP:
-    GNUNET_break (0);
-    reconnect_call (call);
-    break;
-  case CS_RINGING:
-    call->event_handler (call->event_handler_cls,
-                         GNUNET_CONVERSATION_EC_TERMINATED,
-                         reason);
-    GNUNET_CONVERSATION_call_stop (call, NULL);
-    return;
-  case CS_ACTIVE:
-    call->event_handler (call->event_handler_cls,
-                         GNUNET_CONVERSATION_EC_TERMINATED,
-                         reason);
-    GNUNET_CONVERSATION_call_stop (call, NULL);
-    return;
-  case CS_SHUTDOWN:
-    GNUNET_CONVERSATION_call_stop (call, NULL);
-    break;
-  }
-}
-
-
-/**
- * We received a `struct ClientAudioMessage`
- *
- * @param cls the `struct GNUNET_CONVERSATION_Call`
- * @param msg the message
- */
-static void
-handle_call_audio_message (void *cls,
-                           const struct GNUNET_MessageHeader *msg)
-{
-  struct GNUNET_CONVERSATION_Call *call = cls;
-  const struct ClientAudioMessage *am;
-
-  am = (const struct ClientAudioMessage *) msg;
-  switch (call->state)
-  {
-  case CS_LOOKUP:
-    GNUNET_break (0);
-    reconnect_call (call);
-    break;
-  case CS_RINGING:
-    GNUNET_break (0);
-    reconnect_call (call);
-    break;
-  case CS_ACTIVE:
-    call->speaker->play (call->speaker->cls,
-                         ntohs (msg->size) - sizeof (struct ClientAudioMessage),
-                         &am[1]);
-    break;
-  case CS_SHUTDOWN:
-    GNUNET_CONVERSATION_call_stop (call, NULL);
-    break;
-
-  }
-}
-
-
-/**
- * Iterator called on obtained result for a GNS lookup.
- *
- * @param cls closure with the `struct GNUNET_CONVERSATION_Call`
- * @param rd_count number of records in @a rd
- * @param rd the records in reply
- */
-static void 
-handle_gns_response (void *cls,
-                     uint32_t rd_count,
-                     const struct GNUNET_NAMESTORE_RecordData *rd)
+void
+GNUNET_CONVERSATION_caller_suspend (struct GNUNET_CONVERSATION_Caller *caller)
 {
-  struct GNUNET_CONVERSATION_Call *call = cls;
-  uint32_t i;
+  struct GNUNET_CONVERSATION_Phone *phone = caller->phone;
   struct GNUNET_MQ_Envelope *e;
-  struct ClientCallMessage *ccm;
-
-  for (i=0;i<rd_count;i++)
-  {
-    if (GNUNET_NAMESTORE_TYPE_PHONE == rd[i].record_type)
-    {
-      if (rd[i].data_size != sizeof (struct GNUNET_CONVERSATION_PhoneRecord))
-      {
-        GNUNET_break_op (0);
-        continue;
-      }      
-      memcpy (&call->phone_record,
-              rd[i].data,
-              rd[i].data_size);
-      e = GNUNET_MQ_msg (ccm, GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_CALL);
-      ccm->line = call->phone_record.line;
-      ccm->target = call->phone_record.peer;
-      ccm->caller_id = *GNUNET_IDENTITY_ego_get_private_key (call->caller_id);
-      GNUNET_MQ_send (call->mq, e);
-      call->state = CS_RINGING;
-      call->event_handler (call->event_handler_cls,
-                           GNUNET_CONVERSATION_EC_RINGING);
-      return;
-    }
-  }
-  /* not found */
-  call->event_handler (call->event_handler_cls,
-                       GNUNET_CONVERSATION_EC_GNS_FAIL);
-  GNUNET_CONVERSATION_call_stop (call, NULL);
-}
-
-
-/**
- * We encountered an error talking with the conversation service. 
- *
- * @param cls the `struct GNUNET_CONVERSATION_Call`
- * @param error details about the error
- */
-static void
-call_error_handler (void *cls,
-                    enum GNUNET_MQ_Error error)
-{
-  struct GNUNET_CONVERSATION_Call *call = cls;
-
-  GNUNET_break (0);
-  reconnect_call (call);
-}
-
+  struct ClientPhoneSuspendMessage *suspend;
 
-/**
- * The call got disconnected, reconnect to the service.
- *
- * @param call call to reconnect
- */
-static void
-reconnect_call (struct GNUNET_CONVERSATION_Call *call)
-{
-  static struct GNUNET_MQ_MessageHandler handlers[] =
-  {
-    { &handle_call_busy,
-      GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_BUSY, 
-      sizeof (struct ClientPhoneBusyMessage) },
-    { &handle_call_picked_up,
-      GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_PICKED_UP, 
-      0 },
-    { &handle_call_hangup,
-      GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP,
-      0 },
-    { &handle_call_audio_message,
-      GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO,
-      0 },    
-    { NULL, 0, 0 }    
-  };
- if (NULL != call->mq)
-  {
-    GNUNET_MQ_destroy (call->mq);
-    call->mq = NULL;
-  }
-  if (NULL != call->client)
+  GNUNET_assert ( (CS_ACTIVE == caller->state) ||
+                  (CS_CALLER_SUSPENDED == caller->state) );
+  if (CS_ACTIVE == caller->state)
   {
-    GNUNET_CLIENT_disconnect (call->client);
-    call->client = NULL;
+    caller->speaker->disable_speaker (caller->speaker->cls);
+    caller->mic->disable_microphone (caller->mic->cls);
   }
-  call->client = GNUNET_CLIENT_connect ("conversation", call->cfg);
-  if (NULL == call->client)
-    return;
-  call->mq = GNUNET_MQ_queue_for_connection_client (call->client,
-                                                    handlers,
-                                                    &call_error_handler,
-                                                    call);
-}
-
-
-/**
- * Call the phone of another user.
- *
- * @param cfg configuration to use, specifies our phone service
- * @param caller_id identity of the caller
- * @param callee GNS name of the callee (used to locate the callee's record)
- * @param speaker speaker to use (will be used automatically immediately once the
- *        #GNUNET_CONVERSATION_EC_READY event is generated); we will NOT generate
- *        a ring tone on the speaker
- * @param mic microphone to use (will be used automatically immediately once the
- *        #GNUNET_CONVERSATION_EC_READY event is generated)
- * @param event_handler how to notify the owner of the phone about events
- * @param event_handler_cls closure for @a event_handler
- */
-struct GNUNET_CONVERSATION_Call *
-GNUNET_CONVERSATION_call_start (const struct GNUNET_CONFIGURATION_Handle *cfg,
-                               struct GNUNET_IDENTITY_Ego *caller_id,
-                               const char *callee,
-                               struct GNUNET_SPEAKER_Handle *speaker,
-                               struct GNUNET_MICROPHONE_Handle *mic,
-                               GNUNET_CONVERSATION_EventHandler event_handler,
-                               void *event_handler_cls)
-{
-  struct GNUNET_CONVERSATION_Call *call;
-  struct GNUNET_CRYPTO_EccPublicSignKey my_zone;
-
-  GNUNET_IDENTITY_ego_get_public_key (caller_id,
-                                      &my_zone);
-  call = GNUNET_new (struct GNUNET_CONVERSATION_Call);
-  call->cfg = cfg;
-  call->caller_id = caller_id;
-  call->callee = GNUNET_strdup (callee);
-  call->speaker = speaker;
-  call->mic = mic;
-  call->event_handler = event_handler;
-  call->event_handler_cls = event_handler_cls;
-  call->gns = GNUNET_GNS_connect (cfg);
-  reconnect_call (call);
-
-  if ( (NULL == call->client) ||
-       (NULL == call->gns) )
-  {
-    GNUNET_CONVERSATION_call_stop (call, NULL);
-    return NULL;
-  }
-  call->gns_lookup = GNUNET_GNS_lookup (call->gns, callee,
-                                        &my_zone,
-                                        GNUNET_NAMESTORE_TYPE_PHONE,
-                                        GNUNET_NO,
-                                        NULL /* FIXME: add shortening support */,
-                                        &handle_gns_response, call);
-  GNUNET_assert (NULL != call->gns_lookup);
-  return call;
+  caller->speaker = NULL;
+  caller->mic = NULL;
+  e = GNUNET_MQ_msg (suspend, GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_SUSPEND);
+  suspend->cid = caller->cid;
+  GNUNET_MQ_send (phone->mq, e);
+  if (CS_ACTIVE == caller->state)
+    caller->state = CS_CALLEE_SUSPENDED;
+  else
+    caller->state = CS_BOTH_SUSPENDED;
 }
 
 
 /**
- * Terminate a call.  The call may be ringing or ready at this time.
+ * Resume suspended conversation of a phone.
  *
- * @param call call to terminate
- * @param reason if the call was active (ringing or ready) this will be the
- *        reason given to the other user for why we hung up
+ * @param caller call to resume
+ * @param speaker speaker to use
+ * @param mic microphone to use
  */
 void
-GNUNET_CONVERSATION_call_stop (struct GNUNET_CONVERSATION_Call *call,
-                              const char *reason)
+GNUNET_CONVERSATION_caller_resume (struct GNUNET_CONVERSATION_Caller *caller,
+                                   struct GNUNET_SPEAKER_Handle *speaker,
+                                   struct GNUNET_MICROPHONE_Handle *mic)
 {
-  if (NULL != reason)
-  {
-    // FIXME: transmit reason to service... (not implemented!)
-    GNUNET_break (0);
-    // return;
-  }
-  if (NULL != call->speaker)
-  {
-    if (CS_ACTIVE == call->state)
-      call->speaker->disable_speaker (call->speaker->cls);
-    call->speaker = NULL;
-  }
-  if (NULL != call->mic)
-  {
-    if (CS_ACTIVE == call->state)
-      call->mic->disable_microphone (call->mic->cls);
-    call->mic =NULL;
-  }
-  if (NULL != call->mq)
-  {
-    GNUNET_MQ_destroy (call->mq);
-    call->mq = NULL;
-  }
-  if (NULL != call->client)
-  {
-    GNUNET_CLIENT_disconnect (call->client);
-    call->client = NULL;
-  }
-  if (NULL != call->gns_lookup)
+  struct GNUNET_CONVERSATION_Phone *phone = caller->phone;
+  struct GNUNET_MQ_Envelope *e;
+  struct ClientPhoneResumeMessage *resume;
+
+  GNUNET_assert ( (CS_CALLEE_SUSPENDED == caller->state) ||
+                  (CS_BOTH_SUSPENDED == caller->state) );
+  caller->speaker = speaker;
+  caller->mic = mic;
+  e = GNUNET_MQ_msg (resume, GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_RESUME);
+  resume->cid = caller->cid;
+  GNUNET_MQ_send (phone->mq, e);
+  if (CS_CALLEE_SUSPENDED == caller->state)
   {
-    GNUNET_GNS_lookup_cancel (call->gns_lookup);
-    call->gns_lookup = NULL;
+    caller->state = CS_ACTIVE;
+    caller->speaker->enable_speaker (caller->speaker->cls);
+    caller->mic->enable_microphone (caller->mic->cls,
+                                    &transmit_phone_audio,
+                                    caller);
   }
-  if (NULL != call->gns)
+  else
   {
-    GNUNET_GNS_disconnect (call->gns);
-    call->gns = NULL;
+    caller->state = CS_CALLER_SUSPENDED;
   }
-
-  GNUNET_free (call);
 }
 
-
 /* end of conversation_api.c */