- add underlay api implementation
[oweals/gnunet.git] / src / conversation / gnunet-service-conversation.c
index 3b610c007f7a501e4411a47b8e847b89e1993b04..df8d4e9f24cf9bfdde73202854738f136e7edef3 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,
 #define RING_TIMEOUT GNUNET_TIME_UNIT_DAYS
 
 
+/**
+ * A line connects a local client with a mesh channel (or, if it is an
+ * open line, is waiting for a mesh channel).
+ */
+struct Line;
+
 /**
  * The possible connection status
  */
-enum LineStatus
+enum ChannelStatus
 {
-  /**
-   * We are waiting for incoming calls.
-   */
-  LS_CALLEE_LISTEN,
-
   /**
    * Our phone is ringing, waiting for the client to pick up.
    */
-  LS_CALLEE_RINGING,
+  CS_CALLEE_RINGING,
 
   /**
    * We are talking!
    */
-  LS_CALLEE_CONNECTED,
+  CS_CALLEE_CONNECTED,
 
   /**
    * We're in shutdown, sending hangup messages before cleaning up.
    */
-  LS_CALLEE_SHUTDOWN,
+  CS_CALLEE_SHUTDOWN,
 
   /**
    * We are waiting for the phone to be picked up.
    */
-  LS_CALLER_CALLING,
+  CS_CALLER_CALLING,
 
   /**
    * We are talking!
    */
-  LS_CALLER_CONNECTED,
+  CS_CALLER_CONNECTED,
 
   /**
    * We're in shutdown, sending hangup messages before cleaning up.
    */
-  LS_CALLER_SHUTDOWN
+  CS_CALLER_SHUTDOWN
+
 };
 
 
 /**
- * A line connects a local client with a mesh tunnel (or, if it is an
- * open line, is waiting for a mesh tunnel).
+ * A `struct Channel` represents a mesh channel, which is a P2P
+ * connection to another conversation service.  Multiple channels can
+ * be attached the the same `struct Line`, which represents a local
+ * client.  We keep them in a linked list.
  */
-struct Line
+struct Channel
 {
+
   /**
-   * Kept in a DLL.
+   * This is a DLL.
    */
-  struct Line *next;
+  struct Channel *next;
 
   /**
-   * Kept in a DLL.
+   * This is a DLL.
    */
-  struct Line *prev;
+  struct Channel *prev;
 
   /**
-   * Handle for the reliable tunnel (contol data)
+   * Line associated with the channel.
    */
-  struct GNUNET_MESH_Tunnel *tunnel_reliable;
-  
+  struct Line *line;
+
   /**
-   * Handle for unreliable tunnel (audio data)
+   * Handle for the reliable channel (contol data)
    */
-  struct GNUNET_MESH_Tunnel *tunnel_unreliable;
+  struct GNUNET_MESH_Channel *channel_reliable;
+
+  /**
+   * Handle for unreliable channel (audio data)
+   */
+  struct GNUNET_MESH_Channel *channel_unreliable;
 
   /**
    * Transmit handle for pending audio messages
@@ -121,11 +131,6 @@ struct Line
    */
   struct GNUNET_MQ_Handle *reliable_mq;
 
-  /**
-   * Handle to the line client.
-   */
-  struct GNUNET_SERVER_Client *client;
-
   /**
    * Target of the line, if we are the caller.
    */
@@ -142,9 +147,9 @@ struct Line
   size_t audio_size;
 
   /**
-   * Our line number.
+   * Channel identifier.
    */
-  uint32_t local_line;
+  uint32_t cid;
 
   /**
    * Remote line number.
@@ -153,8 +158,61 @@ struct Line
 
   /**
    * Current status of this line.
-   */ 
-  enum LineStatus status;
+   */
+  enum ChannelStatus status;
+
+  /**
+   * #GNUNET_YES if the channel was suspended by the other peer.
+   */
+  int8_t suspended_remote;
+
+  /**
+   * #GNUNET_YES if the channel was suspended by the local client.
+   */
+  int8_t suspended_local;
+
+};
+
+
+/**
+ * A `struct Line` connects a local client with mesh channels.
+ */
+struct Line
+{
+  /**
+   * Kept in a DLL.
+   */
+  struct Line *next;
+
+  /**
+   * Kept in a DLL.
+   */
+  struct Line *prev;
+
+  /**
+   * This is a DLL.
+   */
+  struct Channel *channel_head;
+
+  /**
+   * This is a DLL.
+   */
+  struct Channel *channel_tail;
+
+  /**
+   * Handle to the line client.
+   */
+  struct GNUNET_SERVER_Client *client;
+
+  /**
+   * Generator for channel IDs.
+   */
+  uint32_t cid_gen;
+
+  /**
+   * Our line number.
+   */
+  uint32_t local_line;
 
 };
 
@@ -212,7 +270,7 @@ handle_client_register_message (void *cls,
   const struct ClientPhoneRegisterMessage *msg;
   struct Line *line;
 
-  msg = (struct ClientPhoneRegisterMessage *) message;
+  msg = (const struct ClientPhoneRegisterMessage *) message;
   line = GNUNET_SERVER_client_get_user_context (client, struct Line);
   if (NULL != line)
   {
@@ -227,7 +285,7 @@ handle_client_register_message (void *cls,
   GNUNET_CONTAINER_DLL_insert (lines_head,
                                lines_tail,
                                line);
-  line->local_line = ntohl (msg->line);
+  line->local_line = ntohl (msg->line) & (~ (1 << 31));
   GNUNET_SERVER_receive_done (client, GNUNET_OK);
 }
 
@@ -247,19 +305,10 @@ handle_client_pickup_message (void *cls,
   const struct ClientPhonePickupMessage *msg;
   struct GNUNET_MQ_Envelope *e;
   struct MeshPhonePickupMessage *mppm;
-  const char *meta;
   struct Line *line;
-  size_t len;
+  struct Channel *ch;
 
-  msg = (struct ClientPhonePickupMessage *) message;
-  meta = (const char *) &msg[1];
-  len = ntohs (msg->header.size) - sizeof (struct ClientPhonePickupMessage);
-  if ( (0 == len) ||
-       ('\0' != meta[len - 1]) )
-  {
-    meta = NULL;
-    len = 0;
-  }
+  msg = (const struct ClientPhonePickupMessage *) message;
   line = GNUNET_SERVER_client_get_user_context (client, struct Line);
   if (NULL == line)
   {
@@ -267,114 +316,121 @@ handle_client_pickup_message (void *cls,
     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
     return;
   }
-  switch (line->status)
+  for (ch = line->channel_head; NULL != ch; ch = ch->next)
+    if (msg->cid == ch->cid)
+      break;
+  if (NULL == ch)
   {
-  case LS_CALLEE_LISTEN:
+    /* could have been destroyed asynchronously, ignore message */
     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "Ignoring client's PICKUP message, caller has HUNG UP already\n");
-    GNUNET_SERVER_receive_done (client, GNUNET_OK);
-    break;
-  case LS_CALLEE_RINGING:
-    line->status = LS_CALLEE_CONNECTED;
+                "Channel %u not found\n",
+                msg->cid);
+    return;
+  }
+  switch (ch->status)
+  {
+  case CS_CALLEE_RINGING:
+    ch->status = CS_CALLEE_CONNECTED;
     break;
-  case LS_CALLEE_CONNECTED:
+  case CS_CALLEE_CONNECTED:
     GNUNET_break (0);
     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
     return;
-  case LS_CALLEE_SHUTDOWN:
+  case CS_CALLEE_SHUTDOWN:
     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                 "Ignoring client's PICKUP message, line is in SHUTDOWN\n");
     GNUNET_SERVER_receive_done (client, GNUNET_OK);
     break;
-  case LS_CALLER_CALLING:
-  case LS_CALLER_CONNECTED:
-  case LS_CALLER_SHUTDOWN:
+  case CS_CALLER_CALLING:
+  case CS_CALLER_CONNECTED:
+  case CS_CALLER_SHUTDOWN:
     GNUNET_break (0);
     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
     return;
   }
-  line->status = LS_CALLEE_CONNECTED;
+  GNUNET_break (CS_CALLEE_CONNECTED == ch->status);
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Sending PICK_UP message to mesh with meta data `%s'\n",
-              meta);
-  e = GNUNET_MQ_msg_extra (mppm,
-                           len,
-                           GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_PICK_UP);
-  memcpy (&mppm[1], meta, len);
-  GNUNET_MQ_send (line->reliable_mq, e);
+              "Sending PICK_UP message to mesh\n");
+  e = GNUNET_MQ_msg (mppm,
+                     GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_PICK_UP);
+  GNUNET_MQ_send (ch->reliable_mq, e);
   GNUNET_SERVER_receive_done (client, GNUNET_OK);
 }
 
 
 /**
- * Destroy the mesh tunnels of a line.
+ * Destroy a channel.
  *
- * @param line line to shutdown tunnels of
+ * @param ch channel to destroy.
  */
 static void
-destroy_line_mesh_tunnels (struct Line *line)
+destroy_line_mesh_channels (struct Channel *ch)
 {
-  struct GNUNET_MESH_Tunnel *t;
+  struct Line *line = ch->line;
+  struct GNUNET_MESH_Channel *t;
 
-  if (NULL != line->reliable_mq)
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Destroying mesh channels\n");
+  if (NULL != ch->reliable_mq)
   {
-    GNUNET_MQ_destroy (line->reliable_mq);
-    line->reliable_mq = NULL;
+    GNUNET_MQ_destroy (ch->reliable_mq);
+    ch->reliable_mq = NULL;
   }
-  if (NULL != line->unreliable_mth)
+  if (NULL != ch->unreliable_mth)
   {
-    GNUNET_MESH_notify_transmit_ready_cancel (line->unreliable_mth);
-    line->unreliable_mth = NULL;
+    GNUNET_MESH_notify_transmit_ready_cancel (ch->unreliable_mth);
+    ch->unreliable_mth = NULL;
   }
-  if (NULL != (t = line->tunnel_unreliable))
+  if (NULL != (t = ch->channel_unreliable))
   {
-    line->tunnel_unreliable = NULL;
-    GNUNET_MESH_tunnel_destroy (t);
+    ch->channel_unreliable = NULL;
+    GNUNET_MESH_channel_destroy (t);
   }
-  if (NULL != (t = line->tunnel_reliable))
+  if (NULL != (t = ch->channel_reliable))
   {
-    line->tunnel_reliable = NULL;
-    GNUNET_MESH_tunnel_destroy (t);
+    ch->channel_reliable = NULL;
+    GNUNET_MESH_channel_destroy (t);
   }
+  GNUNET_CONTAINER_DLL_remove (line->channel_head,
+                               line->channel_tail,
+                               ch);
+  GNUNET_free_non_null (ch->audio_data);
+  GNUNET_free (ch);
 }
 
 
 /**
  * We are done signalling shutdown to the other peer.  Close down
- * (or reset) the line.
+ * the channel.
  *
- * @param cls the `struct Line` to reset/terminate
+ * @param cls the `struct Channel` to reset/terminate
  */
 static void
 mq_done_finish_caller_shutdown (void *cls)
 {
-  struct Line *line = cls;
+  struct Channel *ch = cls;
 
-  switch (line->status)
+  switch (ch->status)
   {
-  case LS_CALLEE_LISTEN:
+  case CS_CALLEE_RINGING:
     GNUNET_break (0);
     break;
-  case LS_CALLEE_RINGING:
+  case CS_CALLEE_CONNECTED:
     GNUNET_break (0);
     break;
-  case LS_CALLEE_CONNECTED:
-    GNUNET_break (0);
+  case CS_CALLEE_SHUTDOWN:
+    destroy_line_mesh_channels (ch);
     break;
-  case LS_CALLEE_SHUTDOWN:
-    line->status = LS_CALLEE_LISTEN;
-    destroy_line_mesh_tunnels (line);
-    return;
-  case LS_CALLER_CALLING:
-    line->status = LS_CALLER_SHUTDOWN;
+  case CS_CALLER_CALLING:
+    GNUNET_break (0);
     break;
-  case LS_CALLER_CONNECTED:
-    line->status = LS_CALLER_SHUTDOWN;
+  case CS_CALLER_CONNECTED:
+    GNUNET_break (0);
     break;
-  case LS_CALLER_SHUTDOWN:
-    destroy_line_mesh_tunnels (line);
+  case CS_CALLER_SHUTDOWN:
+    destroy_line_mesh_channels (ch);
     break;
-  }  
+  }
 }
 
 
@@ -393,19 +449,81 @@ handle_client_hangup_message (void *cls,
   const struct ClientPhoneHangupMessage *msg;
   struct GNUNET_MQ_Envelope *e;
   struct MeshPhoneHangupMessage *mhum;
-  const char *meta;
   struct Line *line;
-  size_t len;
+  struct Channel *ch;
 
-  msg = (struct ClientPhoneHangupMessage *) message;
-  meta = (const char *) &msg[1];
-  len = ntohs (msg->header.size) - sizeof (struct ClientPhoneHangupMessage);
-  if ( (0 == len) ||
-       ('\0' != meta[len - 1]) )
+  msg = (const struct ClientPhoneHangupMessage *) message;
+  line = GNUNET_SERVER_client_get_user_context (client, struct Line);
+  if (NULL == line)
   {
-    meta = NULL;
-    len = 0;
+    GNUNET_break (0);
+    GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
+    return;
   }
+  for (ch = line->channel_head; NULL != ch; ch = ch->next)
+    if (msg->cid == ch->cid)
+      break;
+  if (NULL == ch)
+  {
+    /* could have been destroyed asynchronously, ignore message */
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "Channel %u not found\n",
+                msg->cid);
+    return;
+  }
+
+  switch (ch->status)
+  {
+  case CS_CALLEE_RINGING:
+    ch->status = CS_CALLEE_SHUTDOWN;
+    break;
+  case CS_CALLEE_CONNECTED:
+    ch->status = CS_CALLEE_SHUTDOWN;
+    break;
+  case CS_CALLEE_SHUTDOWN:
+    /* maybe the other peer closed asynchronously... */
+    return;
+  case CS_CALLER_CALLING:
+    ch->status = CS_CALLER_SHUTDOWN;
+    break;
+  case CS_CALLER_CONNECTED:
+    ch->status = CS_CALLER_SHUTDOWN;
+    break;
+  case CS_CALLER_SHUTDOWN:
+    /* maybe the other peer closed asynchronously... */
+    return;
+  }
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Sending HANG_UP message via mesh\n");
+  e = GNUNET_MQ_msg (mhum,
+                     GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_HANG_UP);
+  GNUNET_MQ_notify_sent (e,
+                         &mq_done_finish_caller_shutdown,
+                         ch);
+  GNUNET_MQ_send (ch->reliable_mq, e);
+  GNUNET_SERVER_receive_done (client, GNUNET_OK);
+}
+
+
+/**
+ * Function to handle a suspend request message from the client
+ *
+ * @param cls closure, NULL
+ * @param client the client from which the message is
+ * @param message the message from the client
+ */
+static void
+handle_client_suspend_message (void *cls,
+                               struct GNUNET_SERVER_Client *client,
+                               const struct GNUNET_MessageHeader *message)
+{
+  const struct ClientPhoneSuspendMessage *msg;
+  struct GNUNET_MQ_Envelope *e;
+  struct MeshPhoneSuspendMessage *mhum;
+  struct Line *line;
+  struct Channel *ch;
+
+  msg = (const struct ClientPhoneSuspendMessage *) message;
   line = GNUNET_SERVER_client_get_user_context (client, struct Line);
   if (NULL == line)
   {
@@ -413,50 +531,132 @@ handle_client_hangup_message (void *cls,
     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
     return;
   }
-  switch (line->status)
+  for (ch = line->channel_head; NULL != ch; ch = ch->next)
+    if (msg->cid == ch->cid)
+      break;
+  if (NULL == ch)
+  {
+    /* could have been destroyed asynchronously, ignore message */
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "Channel %u not found\n",
+                msg->cid);
+    return;
+  }
+  if (GNUNET_YES == ch->suspended_local)
   {
-  case LS_CALLEE_LISTEN:
     GNUNET_break (0);
-    GNUNET_SERVER_receive_done (client, GNUNET_OK);
+    GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
     return;
-  case LS_CALLEE_RINGING:
-    line->status = LS_CALLEE_SHUTDOWN;
-    break;
-  case LS_CALLEE_CONNECTED:
-    line->status = LS_CALLEE_SHUTDOWN;
+  }
+  switch (ch->status)
+  {
+  case CS_CALLEE_RINGING:
+    GNUNET_break (0);
+    GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
+    return;
+  case CS_CALLEE_CONNECTED:
+    ch->suspended_local = GNUNET_YES;
     break;
-  case LS_CALLEE_SHUTDOWN:
+  case CS_CALLEE_SHUTDOWN:
+    /* maybe the other peer closed asynchronously... */
+    return;
+  case CS_CALLER_CALLING:
     GNUNET_break (0);
-    GNUNET_SERVER_receive_done (client, GNUNET_OK);
+    GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
     return;
-  case LS_CALLER_CALLING:
-    line->status = LS_CALLER_SHUTDOWN;
+  case CS_CALLER_CONNECTED:
+    ch->suspended_local = GNUNET_YES;
     break;
-  case LS_CALLER_CONNECTED:
-    line->status = LS_CALLER_SHUTDOWN;
+  case CS_CALLER_SHUTDOWN:
+    /* maybe the other peer closed asynchronously... */
+    return;
+  }
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Sending SUSPEND message via mesh\n");
+  e = GNUNET_MQ_msg (mhum,
+                     GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_SUSPEND);
+  GNUNET_MQ_send (ch->reliable_mq, e);
+  GNUNET_SERVER_receive_done (client, GNUNET_OK);
+}
+
+
+/**
+ * Function to handle a resume request message from the client
+ *
+ * @param cls closure, NULL
+ * @param client the client from which the message is
+ * @param message the message from the client
+ */
+static void
+handle_client_resume_message (void *cls,
+                              struct GNUNET_SERVER_Client *client,
+                              const struct GNUNET_MessageHeader *message)
+{
+  const struct ClientPhoneResumeMessage *msg;
+  struct GNUNET_MQ_Envelope *e;
+  struct MeshPhoneResumeMessage *mhum;
+  struct Line *line;
+  struct Channel *ch;
+
+  msg = (const struct ClientPhoneResumeMessage *) message;
+  line = GNUNET_SERVER_client_get_user_context (client, struct Line);
+  if (NULL == line)
+  {
+    GNUNET_break (0);
+    GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
+    return;
+  }
+  for (ch = line->channel_head; NULL != ch; ch = ch->next)
+    if (msg->cid == ch->cid)
+      break;
+  if (NULL == ch)
+  {
+    /* could have been destroyed asynchronously, ignore message */
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "Channel %u not found\n",
+                msg->cid);
+    return;
+  }
+  if (GNUNET_YES != ch->suspended_local)
+  {
+    GNUNET_break (0);
+    GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
+    return;
+  }
+  switch (ch->status)
+  {
+  case CS_CALLEE_RINGING:
+    GNUNET_break (0);
+    GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
+    return;
+  case CS_CALLEE_CONNECTED:
+    ch->suspended_local = GNUNET_NO;
     break;
-  case LS_CALLER_SHUTDOWN:
+  case CS_CALLEE_SHUTDOWN:
+    /* maybe the other peer closed asynchronously... */
+    return;
+  case CS_CALLER_CALLING:
     GNUNET_break (0);
-    GNUNET_SERVER_receive_done (client, GNUNET_OK);
+    GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
+    return;
+  case CS_CALLER_CONNECTED:
+    ch->suspended_local = GNUNET_NO;
+    break;
+  case CS_CALLER_SHUTDOWN:
+    /* maybe the other peer closed asynchronously... */
     return;
   }
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Sending HANG_UP message via mesh with meta data `%s'\n",
-              meta);
-  e = GNUNET_MQ_msg_extra (mhum,
-                           len,
-                           GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_HANG_UP);
-  memcpy (&mhum[1], meta, len);
-  GNUNET_MQ_notify_sent (e,
-                         &mq_done_finish_caller_shutdown,
-                         line);
-  GNUNET_MQ_send (line->reliable_mq, e);
+              "Sending RESUME message via mesh\n");
+  e = GNUNET_MQ_msg (mhum,
+                     GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_RESUME);
+  GNUNET_MQ_send (ch->reliable_mq, e);
   GNUNET_SERVER_receive_done (client, GNUNET_OK);
 }
 
 
 /**
- * Function to handle call request the client
+ * Function to handle call request from the client
  *
  * @param cls closure, NULL
  * @param client the client from which the message is
@@ -469,10 +669,11 @@ handle_client_call_message (void *cls,
 {
   const struct ClientCallMessage *msg;
   struct Line *line;
+  struct Channel *ch;
   struct GNUNET_MQ_Envelope *e;
   struct MeshPhoneRingMessage *ring;
 
-  msg = (struct ClientCallMessage *) message;
+  msg = (const struct ClientCallMessage *) message;
   line = GNUNET_SERVER_client_get_user_context (client, struct Line);
   if (NULL != line)
   {
@@ -482,41 +683,45 @@ handle_client_call_message (void *cls,
   }
   line = GNUNET_new (struct Line);
   line->client = client;
+  line->local_line = (local_line_cnt++) | (1 << 31);
   GNUNET_SERVER_client_set_user_context (client, line);
   GNUNET_SERVER_notification_context_add (nc, client);
-  line->target = msg->target;
   GNUNET_CONTAINER_DLL_insert (lines_head,
                                lines_tail,
                                line);
-  line->remote_line = ntohl (msg->line);
-  line->status = LS_CALLER_CALLING;
-  line->tunnel_reliable = GNUNET_MESH_tunnel_create (mesh,
-                                                     line,
+  ch = GNUNET_new (struct Channel);
+  ch->line = line;
+  GNUNET_CONTAINER_DLL_insert (line->channel_head,
+                               line->channel_tail,
+                               ch);
+  ch->target = msg->target;
+  ch->remote_line = ntohl (msg->line);
+  ch->status = CS_CALLER_CALLING;
+  ch->channel_reliable = GNUNET_MESH_channel_create (mesh,
+                                                     ch,
                                                      &msg->target,
                                                      GNUNET_APPLICATION_TYPE_CONVERSATION_CONTROL,
-                                                     GNUNET_NO,
-                                                     GNUNET_YES);
-  line->reliable_mq = GNUNET_MESH_mq_create (line->tunnel_reliable);
-  line->local_line = local_line_cnt++;
+                                                     GNUNET_MESH_OPTION_RELIABLE);
+  ch->reliable_mq = GNUNET_MESH_mq_create (ch->channel_reliable);
   e = GNUNET_MQ_msg (ring, GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_RING);
   ring->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_CONVERSATION_RING);
   ring->purpose.size = htonl (sizeof (struct GNUNET_PeerIdentity) * 2 +
                               sizeof (struct GNUNET_TIME_AbsoluteNBO) +
                               sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose) +
-                              sizeof (struct GNUNET_CRYPTO_EccPublicSignKey));
-  GNUNET_CRYPTO_ecc_key_get_public_for_signature (&msg->caller_id,
-                                                  &ring->caller_id);
+                              sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey));
+  GNUNET_CRYPTO_ecdsa_key_get_public (&msg->caller_id,
+                                      &ring->caller_id);
   ring->remote_line = msg->line;
   ring->source_line = line->local_line;
   ring->target = msg->target;
   ring->source = my_identity;
   ring->expiration_time = GNUNET_TIME_absolute_hton (GNUNET_TIME_relative_to_absolute (RING_TIMEOUT));
-  GNUNET_CRYPTO_ecc_sign (&msg->caller_id,
-                          &ring->purpose,
-                          &ring->signature);
+  GNUNET_CRYPTO_ecdsa_sign (&msg->caller_id,
+                            &ring->purpose,
+                            &ring->signature);
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
               "Sending RING message via mesh\n");
-  GNUNET_MQ_send (line->reliable_mq, e);
+  GNUNET_MQ_send (ch->reliable_mq, e);
   GNUNET_SERVER_receive_done (client, GNUNET_OK);
 }
 
@@ -524,7 +729,7 @@ handle_client_call_message (void *cls,
 /**
  * Transmit audio data via unreliable mesh channel.
  *
- * @param cls the `struct Line` we are transmitting for
+ * @param cls the `struct Channel` we are transmitting for
  * @param size number of bytes available in @a buf
  * @param buf where to copy the data
  * @return number of bytes copied to @a buf
@@ -534,26 +739,26 @@ transmit_line_audio (void *cls,
                      size_t size,
                      void *buf)
 {
-  struct Line *line = cls;
+  struct Channel *ch = cls;
   struct MeshAudioMessage *mam = buf;
-  
-  line->unreliable_mth = NULL;
+
+  ch->unreliable_mth = NULL;
   if ( (NULL == buf) ||
-       (size < sizeof (struct MeshAudioMessage) + line->audio_size) )
+       (size < sizeof (struct MeshAudioMessage) + ch->audio_size) )
     {
     /* eh, other error handling? */
     return 0;
   }
-  mam->header.size = htons (sizeof (struct MeshAudioMessage) + line->audio_size);
+  mam->header.size = htons (sizeof (struct MeshAudioMessage) + ch->audio_size);
   mam->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_AUDIO);
-  mam->remote_line = htonl (line->remote_line);
-  memcpy (&mam[1], line->audio_data, line->audio_size);
-  GNUNET_free (line->audio_data);
-  line->audio_data = NULL;
+  mam->remote_line = htonl (ch->remote_line);
+  memcpy (&mam[1], ch->audio_data, ch->audio_size);
+  GNUNET_free (ch->audio_data);
+  ch->audio_data = NULL;
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Sending %u bytes of audio data via mesh\n",
-              line->audio_size);
-  return sizeof (struct MeshAudioMessage) + line->audio_size;  
+              "Sending %u bytes of audio data on line %u via mesh\n",
+              ch->audio_size, ch->remote_line);
+  return sizeof (struct MeshAudioMessage) + ch->audio_size;
 }
 
 
@@ -571,10 +776,11 @@ handle_client_audio_message (void *cls,
 {
   const struct ClientAudioMessage *msg;
   struct Line *line;
+  struct Channel *ch;
   size_t size;
 
   size = ntohs (message->size) - sizeof (struct ClientAudioMessage);
-  msg = (struct ClientAudioMessage *) message;
+  msg = (const struct ClientAudioMessage *) message;
   line = GNUNET_SERVER_client_get_user_context (client, struct Line);
   if (NULL == line)
   {
@@ -582,76 +788,82 @@ handle_client_audio_message (void *cls,
     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
     return;
   }
-  switch (line->status)
+  for (ch = line->channel_head; NULL != ch; ch = ch->next)
+    if (msg->cid == ch->cid)
+      break;
+  if (NULL == ch)
   {
-  case LS_CALLEE_LISTEN:
-    /* could be OK if the line just was closed by the other side */
+    /* could have been destroyed asynchronously, ignore message */
     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "Audio data dropped, channel is down\n");
-    GNUNET_SERVER_receive_done (client, GNUNET_OK);
-    break;
-  case LS_CALLEE_RINGING:
-  case LS_CALLER_CALLING:
+                "Channel %u not found\n",
+                msg->cid);
+    return;
+  }
+
+  switch (ch->status)
+  {
+  case CS_CALLEE_RINGING:
+  case CS_CALLER_CALLING:
     GNUNET_break (0);
     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
     return;
-  case LS_CALLEE_CONNECTED:
-  case LS_CALLER_CONNECTED:
+  case CS_CALLEE_CONNECTED:
+  case CS_CALLER_CONNECTED:
     /* common case, handled below */
     break;
-  case LS_CALLEE_SHUTDOWN:
-  case LS_CALLER_SHUTDOWN:
+  case CS_CALLEE_SHUTDOWN:
+  case CS_CALLER_SHUTDOWN:
     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
                 "Mesh audio channel in shutdown; audio data dropped\n");
     GNUNET_SERVER_receive_done (client, GNUNET_OK);
     return;
   }
-  if (NULL == line->tunnel_unreliable)
+  if (NULL == ch->channel_unreliable)
   {
     GNUNET_log (GNUNET_ERROR_TYPE_INFO | GNUNET_ERROR_TYPE_BULK,
                 _("Mesh audio channel not ready; audio data dropped\n"));
-    GNUNET_SERVER_receive_done (client, GNUNET_OK);    
+    GNUNET_SERVER_receive_done (client, GNUNET_OK);
     return;
   }
-  if (NULL != line->unreliable_mth)
+  if (NULL != ch->unreliable_mth)
   {
     /* NOTE: we may want to not do this and instead combine the data */
     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                 "Bandwidth insufficient; dropping previous audio data segment with %u bytes\n",
-                (unsigned int) line->audio_size);
-    GNUNET_MESH_notify_transmit_ready_cancel (line->unreliable_mth);
-    line->unreliable_mth = NULL;
-    GNUNET_free (line->audio_data);
-    line->audio_data = NULL;
+                (unsigned int) ch->audio_size);
+    GNUNET_MESH_notify_transmit_ready_cancel (ch->unreliable_mth);
+    ch->unreliable_mth = NULL;
+    GNUNET_free (ch->audio_data);
+    ch->audio_data = NULL;
   }
-  line->audio_size = size;
-  line->audio_data = GNUNET_malloc (line->audio_size);
-  memcpy (line->audio_data,
+  ch->audio_size = size;
+  ch->audio_data = GNUNET_malloc (ch->audio_size);
+  memcpy (ch->audio_data,
           &msg[1],
           size);
-  line->unreliable_mth = GNUNET_MESH_notify_transmit_ready (line->tunnel_unreliable,
-                                                            GNUNET_NO,
-                                                            GNUNET_TIME_UNIT_FOREVER_REL,
-                                                            sizeof (struct MeshAudioMessage) 
-                                                            + line->audio_size,
-                                                            &transmit_line_audio,
-                                                            line);
+  ch->unreliable_mth = GNUNET_MESH_notify_transmit_ready (ch->channel_unreliable,
+                                                          GNUNET_NO,
+                                                          GNUNET_TIME_UNIT_FOREVER_REL,
+                                                          sizeof (struct MeshAudioMessage)
+                                                          + ch->audio_size,
+                                                          &transmit_line_audio,
+                                                          ch);
   GNUNET_SERVER_receive_done (client, GNUNET_OK);
 }
 
 
 /**
- * We are done signalling shutdown to the other peer.  
- * Destroy the tunnel.
+ * We are done signalling shutdown to the other peer.
+ * Destroy the channel.
  *
- * @param cls the `struct GNUNET_MESH_tunnel` to destroy
+ * @param cls the `struct GNUNET_MESH_channel` to destroy
  */
 static void
-mq_done_destroy_tunnel (void *cls)
+mq_done_destroy_channel (void *cls)
 {
-  struct GNUNET_MESH_Tunnel *tunnel = cls;
-  
-  GNUNET_MESH_tunnel_destroy (tunnel);
+  struct GNUNET_MESH_Channel *channel = cls;
+
+  GNUNET_MESH_channel_destroy (channel);
 }
 
 
@@ -659,30 +871,33 @@ mq_done_destroy_tunnel (void *cls)
  * Function to handle a ring message incoming over mesh
  *
  * @param cls closure, NULL
- * @param tunnel the tunnel over which the message arrived
- * @param tunnel_ctx the tunnel context, can be NULL
+ * @param channel the channel over which the message arrived
+ * @param channel_ctx the channel context, can be NULL
+ *                    or point to the `struct Channel`
  * @param message the incoming message
  * @return #GNUNET_OK
  */
 static int
 handle_mesh_ring_message (void *cls,
-                          struct GNUNET_MESH_Tunnel *tunnel,
-                          void **tunnel_ctx,
+                          struct GNUNET_MESH_Channel *channel,
+                          void **channel_ctx,
                           const struct GNUNET_MessageHeader *message)
 {
   const struct MeshPhoneRingMessage *msg;
   struct Line *line;
+  struct Channel *ch;
   struct GNUNET_MQ_Envelope *e;
-  struct MeshPhoneBusyMessage *busy;
+  struct MeshPhoneHangupMessage *hang_up;
   struct ClientPhoneRingMessage cring;
-  
+  struct GNUNET_MQ_Handle *reliable_mq;
+
   msg = (const struct MeshPhoneRingMessage *) message;
   if ( (msg->purpose.size != htonl (sizeof (struct GNUNET_PeerIdentity) * 2 +
                                     sizeof (struct GNUNET_TIME_AbsoluteNBO) +
                                     sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose) +
-                                    sizeof (struct GNUNET_CRYPTO_EccPublicSignKey))) ||
+                                    sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey))) ||
        (GNUNET_OK !=
-        GNUNET_CRYPTO_ecc_verify (GNUNET_SIGNATURE_PURPOSE_CONVERSATION_RING,
+        GNUNET_CRYPTO_ecdsa_verify (GNUNET_SIGNATURE_PURPOSE_CONVERSATION_RING,
                                   &msg->purpose,
                                   &msg->signature,
                                   &msg->caller_id)) )
@@ -690,31 +905,40 @@ handle_mesh_ring_message (void *cls,
     GNUNET_break_op (0);
     return GNUNET_SYSERR;
   }
-  for (line = lines_head; NULL != line; line = line->next)  
-    if ( (line->local_line == ntohl (msg->remote_line)) &&
-         (LS_CALLEE_LISTEN == line->status) )
+  for (line = lines_head; NULL != line; line = line->next)
+    if (line->local_line == ntohl (msg->remote_line))
       break;
-  if (NULL == line) 
+  if (NULL == line)
   {
     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                _("No available phone for incoming call on line %u, sending BUSY signal\n"),
+                _("No available phone for incoming call on line %u, sending HANG_UP signal\n"),
                 ntohl (msg->remote_line));
-    e = GNUNET_MQ_msg (busy, GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_BUSY);
+    e = GNUNET_MQ_msg (hang_up,
+                       GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_HANG_UP);
     GNUNET_MQ_notify_sent (e,
-                           &mq_done_destroy_tunnel,
-                           tunnel);
-    GNUNET_MQ_send (line->reliable_mq, e);
-    GNUNET_MESH_receive_done (tunnel); /* needed? */
+                           &mq_done_destroy_channel,
+                           channel);
+    reliable_mq = GNUNET_MESH_mq_create (channel);
+    GNUNET_MQ_send (reliable_mq, e);
+    /* FIXME: do we need to clean up reliable_mq somehow/somewhere? */
+    GNUNET_MESH_receive_done (channel); /* needed? */
     return GNUNET_OK;
   }
-  line->status = LS_CALLEE_RINGING;
-  line->remote_line = ntohl (msg->source_line);
-  line->tunnel_reliable = tunnel;
-  line->reliable_mq = GNUNET_MESH_mq_create (line->tunnel_reliable);
-  *tunnel_ctx = line;
+  ch = GNUNET_new (struct Channel);
+  ch->line = line;
+  GNUNET_CONTAINER_DLL_insert (line->channel_head,
+                               line->channel_tail,
+                               ch);
+  ch->status = CS_CALLEE_RINGING;
+  ch->remote_line = ntohl (msg->source_line);
+  ch->channel_reliable = channel;
+  ch->reliable_mq = GNUNET_MESH_mq_create (ch->channel_reliable);
+  ch->cid = line->cid_gen++;
+  ch->target = msg->source;
+  *channel_ctx = ch;
   cring.header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_RING);
   cring.header.size = htons (sizeof (cring));
-  cring.reserved = htonl (0);
+  cring.cid = ch->cid;
   cring.caller_id = msg->caller_id;
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
               "Sending RING message to client\n");
@@ -722,7 +946,7 @@ handle_mesh_ring_message (void *cls,
                                               line->client,
                                               &cring.header,
                                               GNUNET_NO);
-  GNUNET_MESH_receive_done (tunnel);
+  GNUNET_MESH_receive_done (channel);
   return GNUNET_OK;
 }
 
@@ -731,81 +955,56 @@ handle_mesh_ring_message (void *cls,
  * Function to handle a hangup message incoming over mesh
  *
  * @param cls closure, NULL
- * @param tunnel the tunnel over which the message arrived
- * @param tunnel_ctx the tunnel context, can be NULL
+ * @param channel the channel over which the message arrived
+ * @param channel_ctx the channel context, can be NULL
+ *                    or point to the `struct Channel`
  * @param message the incoming message
  * @return #GNUNET_OK
  */
 static int
 handle_mesh_hangup_message (void *cls,
-                            struct GNUNET_MESH_Tunnel *tunnel,
-                            void **tunnel_ctx,
+                            struct GNUNET_MESH_Channel *channel,
+                            void **channel_ctx,
                             const struct GNUNET_MessageHeader *message)
 {
-  struct Line *line = *tunnel_ctx;
-  const struct MeshPhoneHangupMessage *msg;
-  const char *reason;
-  size_t len = ntohs (message->size) - sizeof (struct MeshPhoneHangupMessage);
-  char buf[len + sizeof (struct ClientPhoneHangupMessage)];
-  struct ClientPhoneHangupMessage *hup;
-  
-  msg = (const struct MeshPhoneHangupMessage *) message;
-  len = ntohs (msg->header.size) - sizeof (struct MeshPhoneHangupMessage);
-  reason = (const char *) &msg[1];
-  if ( (0 == len) ||
-       ('\0' != reason[len - 1]) )
-  {
-    reason = NULL;
-    len = 0;
-  }
-  if (NULL == line)
+  struct Channel *ch = *channel_ctx;
+  struct Line *line;
+  struct ClientPhoneHangupMessage hup;
+  enum ChannelStatus status;
+
+  if (NULL == ch)
   {
     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "HANGUP message received for non-existing line, dropping tunnel.\n");
+                "HANGUP message received for non-existing line, dropping channel.\n");
     return GNUNET_SYSERR;
   }
-  *tunnel_ctx = NULL;
-  switch (line->status)
+  line = ch->line;
+  *channel_ctx = NULL;
+  hup.header.size = htons (sizeof (hup));
+  hup.header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP);
+  hup.cid = ch->cid;
+  status = ch->status;
+  GNUNET_MESH_receive_done (channel);
+  destroy_line_mesh_channels (ch);
+  switch (status)
   {
-  case LS_CALLEE_LISTEN:
-    GNUNET_break (0);
-    return GNUNET_SYSERR;
-  case LS_CALLEE_RINGING:
-    line->status = LS_CALLEE_LISTEN;
-    destroy_line_mesh_tunnels (line);
-    break;
-  case LS_CALLEE_CONNECTED:
-    line->status = LS_CALLEE_LISTEN;
-    destroy_line_mesh_tunnels (line);
+  case CS_CALLEE_RINGING:
+  case CS_CALLEE_CONNECTED:
     break;
-  case LS_CALLEE_SHUTDOWN:
-    line->status = LS_CALLEE_LISTEN;
-    destroy_line_mesh_tunnels (line);
+  case CS_CALLEE_SHUTDOWN:
     return GNUNET_OK;
-  case LS_CALLER_CALLING:
-    line->status = LS_CALLER_SHUTDOWN;
-    mq_done_finish_caller_shutdown (line);
+  case CS_CALLER_CALLING:
+  case CS_CALLER_CONNECTED:
     break;
-  case LS_CALLER_CONNECTED:
-    line->status = LS_CALLER_SHUTDOWN;
-    mq_done_finish_caller_shutdown (line);
-    break;
-  case LS_CALLER_SHUTDOWN:
-    mq_done_finish_caller_shutdown (line);
+  case CS_CALLER_SHUTDOWN:
     return GNUNET_OK;
   }
-  hup = (struct ClientPhoneHangupMessage *) buf;
-  hup->header.size = sizeof (buf);
-  hup->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP);
-  memcpy (&hup[1], reason, len);
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Sending HANG UP message to client with reason `%s'\n",
-              reason);
+              "Sending HANG UP message to client\n");
   GNUNET_SERVER_notification_context_unicast (nc,
                                               line->client,
-                                              &hup->header,
+                                              &hup.header,
                                               GNUNET_NO);
-  GNUNET_MESH_receive_done (tunnel);
   return GNUNET_OK;
 }
 
@@ -814,154 +1013,192 @@ handle_mesh_hangup_message (void *cls,
  * Function to handle a pickup message incoming over mesh
  *
  * @param cls closure, NULL
- * @param tunnel the tunnel over which the message arrived
- * @param tunnel_ctx the tunnel context, can be NULL
+ * @param channel the channel over which the message arrived
+ * @param channel_ctx the channel context, can be NULL
+ *                    or point to the `struct Channel`
  * @param message the incoming message
  * @return #GNUNET_OK
  */
 static int
 handle_mesh_pickup_message (void *cls,
-                            struct GNUNET_MESH_Tunnel *tunnel,
-                            void **tunnel_ctx,
+                            struct GNUNET_MESH_Channel *channel,
+                            void **channel_ctx,
                             const struct GNUNET_MessageHeader *message)
 {
-  const struct MeshPhonePickupMessage *msg;
-  struct Line *line = *tunnel_ctx;
-  const char *metadata;
-  size_t len = ntohs (message->size) - sizeof (struct MeshPhonePickupMessage);
-  char buf[len + sizeof (struct ClientPhonePickupMessage)];
-  struct ClientPhonePickupMessage *pick;
-  
-  msg = (const struct MeshPhonePickupMessage *) message;
-  len = ntohs (msg->header.size) - sizeof (struct MeshPhonePickupMessage);
-  metadata = (const char *) &msg[1];
-  if ( (0 == len) ||
-       ('\0' != metadata[len - 1]) )
-  {
-    metadata = NULL;
-    len = 0;
-  }
-  if (NULL == line)
+  struct Channel *ch = *channel_ctx;
+  struct Line *line;
+  struct ClientPhonePickupMessage pick;
+
+  if (NULL == ch)
   {
     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "PICKUP message received for non-existing line, dropping tunnel.\n");
+                "PICKUP message received for non-existing channel, dropping channel.\n");
     return GNUNET_SYSERR;
   }
-  GNUNET_MESH_receive_done (tunnel);
-  switch (line->status)
+  line = ch->line;
+  GNUNET_MESH_receive_done (channel);
+  switch (ch->status)
   {
-  case LS_CALLEE_LISTEN:
-    GNUNET_break (0);
-    return GNUNET_SYSERR;
-  case LS_CALLEE_RINGING:
-  case LS_CALLEE_CONNECTED:
+  case CS_CALLEE_RINGING:
+  case CS_CALLEE_CONNECTED:
     GNUNET_break_op (0);
-    destroy_line_mesh_tunnels (line);
-    line->status = LS_CALLEE_LISTEN;
+    destroy_line_mesh_channels (ch);
     return GNUNET_SYSERR;
-  case LS_CALLEE_SHUTDOWN:
+  case CS_CALLEE_SHUTDOWN:
     GNUNET_break_op (0);
-    line->status = LS_CALLEE_LISTEN;
-    destroy_line_mesh_tunnels (line);
+    destroy_line_mesh_channels (ch);
     break;
-  case LS_CALLER_CALLING:
-    line->status = LS_CALLER_CONNECTED;
+  case CS_CALLER_CALLING:
+    ch->status = CS_CALLER_CONNECTED;
     break;
-  case LS_CALLER_CONNECTED:
+  case CS_CALLER_CONNECTED:
     GNUNET_break_op (0);
     return GNUNET_OK;
-  case LS_CALLER_SHUTDOWN:
+  case CS_CALLER_SHUTDOWN:
     GNUNET_break_op (0);
-    mq_done_finish_caller_shutdown (line);
+    mq_done_finish_caller_shutdown (ch);
     return GNUNET_SYSERR;
   }
-  pick = (struct ClientPhonePickupMessage *) buf;
-  pick->header.size = sizeof (buf);
-  pick->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_PICKED_UP);
-  memcpy (&pick[1], metadata, len);
+  pick.header.size = htons (sizeof (pick));
+  pick.header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_PICKED_UP);
+  pick.cid = ch->cid;
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Sending PICKED UP message to client with metadata `%s'\n",
-              metadata);
+              "Sending PICKED UP message to client\n");
   GNUNET_SERVER_notification_context_unicast (nc,
                                               line->client,
-                                              &pick->header,
+                                              &pick.header,
                                               GNUNET_NO);
-  line->tunnel_unreliable = GNUNET_MESH_tunnel_create (mesh,
-                                                       line,
-                                                       &line->target,
+  ch->channel_unreliable = GNUNET_MESH_channel_create (mesh,
+                                                       ch,
+                                                       &ch->target,
                                                        GNUNET_APPLICATION_TYPE_CONVERSATION_AUDIO,
-                                                       GNUNET_YES,
-                                                       GNUNET_NO);
-  if (NULL == line->tunnel_unreliable)
+                                                       GNUNET_MESH_OPTION_DEFAULT);
+  if (NULL == ch->channel_unreliable)
   {
     GNUNET_break (0);
-  } 
+  }
   return GNUNET_OK;
 }
 
 
 /**
- * Function to handle a busy message incoming over mesh
+ * Function to handle a suspend message incoming over mesh
  *
  * @param cls closure, NULL
- * @param tunnel the tunnel over which the message arrived
- * @param tunnel_ctx the tunnel context, can be NULL
+ * @param channel the channel over which the message arrived
+ * @param channel_ctx the channel context, can be NULL
+ *                    or point to the `struct Channel`
  * @param message the incoming message
  * @return #GNUNET_OK
  */
 static int
-handle_mesh_busy_message (void *cls,
-                          struct GNUNET_MESH_Tunnel *tunnel,
-                          void **tunnel_ctx,
-                          const struct GNUNET_MessageHeader *message)
+handle_mesh_suspend_message (void *cls,
+                             struct GNUNET_MESH_Channel *channel,
+                             void **channel_ctx,
+                             const struct GNUNET_MessageHeader *message)
 {
-  struct Line *line = *tunnel_ctx;
-  struct ClientPhoneBusyMessage busy;
+  struct Channel *ch = *channel_ctx;
+  struct Line *line;
+  struct ClientPhoneSuspendMessage suspend;
 
-  if (NULL == line)
+  if (NULL == ch)
   {
     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "HANGUP message received for non-existing line, dropping tunnel.\n");
+                "SUSPEND message received for non-existing line, dropping channel.\n");
     return GNUNET_SYSERR;
   }
-  busy.header.size = sizeof (busy);
-  busy.header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_BUSY);
-  GNUNET_MESH_receive_done (tunnel);
-  *tunnel_ctx = NULL;
-  switch (line->status)
+  line = ch->line;
+  suspend.header.size = htons (sizeof (suspend));
+  suspend.header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_SUSPEND);
+  suspend.cid = ch->cid;
+  GNUNET_MESH_receive_done (channel);
+  switch (ch->status)
   {
-  case LS_CALLEE_LISTEN:
-    GNUNET_break (0);
-    return GNUNET_SYSERR;
-  case LS_CALLEE_RINGING:
+  case CS_CALLEE_RINGING:
     GNUNET_break_op (0);
     break;
-  case LS_CALLEE_CONNECTED:
-    GNUNET_break_op (0);
+  case CS_CALLEE_CONNECTED:
+    ch->suspended_remote = GNUNET_YES;
     break;
-  case LS_CALLEE_SHUTDOWN:
+  case CS_CALLEE_SHUTDOWN:
+    return GNUNET_OK;
+  case CS_CALLER_CALLING:
     GNUNET_break_op (0);
     break;
-  case LS_CALLER_CALLING:
+  case CS_CALLER_CONNECTED:
+    ch->suspended_remote = GNUNET_YES;
+    break;
+  case CS_CALLER_SHUTDOWN:
+    return GNUNET_OK;
+  }
+  GNUNET_SERVER_notification_context_unicast (nc,
+                                              line->client,
+                                              &suspend.header,
+                                              GNUNET_NO);
+  return GNUNET_OK;
+}
+
+
+/**
+ * Function to handle a resume message incoming over mesh
+ *
+ * @param cls closure, NULL
+ * @param channel the channel over which the message arrived
+ * @param channel_ctx the channel context, can be NULL
+ *                    or point to the `struct Channel`
+ * @param message the incoming message
+ * @return #GNUNET_OK
+ */
+static int
+handle_mesh_resume_message (void *cls,
+                             struct GNUNET_MESH_Channel *channel,
+                             void **channel_ctx,
+                             const struct GNUNET_MessageHeader *message)
+{
+  struct Channel *ch = *channel_ctx;
+  struct Line *line;
+  struct ClientPhoneResumeMessage resume;
+
+  if (NULL == ch)
+  {
     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "Sending BUSY message to client\n");
-    GNUNET_SERVER_notification_context_unicast (nc,
-                                                line->client,
-                                                &busy.header,
-                                                GNUNET_NO);
-    line->status = LS_CALLER_SHUTDOWN;
-    mq_done_finish_caller_shutdown (line);
+                "RESUME message received for non-existing line, dropping channel.\n");
+    return GNUNET_SYSERR;
+  }
+  line = ch->line;
+  resume.header.size = htons (sizeof (resume));
+  resume.header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_RESUME);
+  resume.cid = ch->cid;
+  GNUNET_MESH_receive_done (channel);
+  if (GNUNET_YES != ch->suspended_remote)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "RESUME message received for non-suspended channel, dropping channel.\n");
+    return GNUNET_SYSERR;
+  }
+  switch (ch->status)
+  {
+  case CS_CALLEE_RINGING:
+    GNUNET_break (0);
     break;
-  case LS_CALLER_CONNECTED:
-    GNUNET_break_op (0);
-    line->status = LS_CALLER_SHUTDOWN;
-    mq_done_finish_caller_shutdown (line);
+  case CS_CALLEE_CONNECTED:
+    ch->suspended_remote = GNUNET_NO;
     break;
-  case LS_CALLER_SHUTDOWN:
-    GNUNET_break_op (0);
-    mq_done_finish_caller_shutdown (line);
+  case CS_CALLEE_SHUTDOWN:
+    return GNUNET_OK;
+  case CS_CALLER_CALLING:
+    GNUNET_break (0);
+    break;
+  case CS_CALLER_CONNECTED:
+    ch->suspended_remote = GNUNET_NO;
     break;
+  case CS_CALLER_SHUTDOWN:
+    return GNUNET_OK;
   }
+  GNUNET_SERVER_notification_context_unicast (nc,
+                                              line->client,
+                                              &resume.header,
+                                              GNUNET_NO);
   return GNUNET_OK;
 }
 
@@ -970,30 +1207,32 @@ handle_mesh_busy_message (void *cls,
  * Function to handle an audio message incoming over mesh
  *
  * @param cls closure, NULL
- * @param tunnel the tunnel over which the message arrived
- * @param tunnel_ctx the tunnel context, can be NULL
+ * @param channel the channel over which the message arrived
+ * @param channel_ctx the channel context, can be NULL
+ *                    or point to the `struct Channel`
  * @param message the incoming message
  * @return #GNUNET_OK
  */
 static int
 handle_mesh_audio_message (void *cls,
-                           struct GNUNET_MESH_Tunnel *tunnel,
-                           void **tunnel_ctx,
+                           struct GNUNET_MESH_Channel *channel,
+                           void **channel_ctx,
                            const struct GNUNET_MessageHeader *message)
 {
   const struct MeshAudioMessage *msg;
-  struct Line *line = *tunnel_ctx;
+  struct Channel *ch = *channel_ctx;
+  struct Line *line;
   struct GNUNET_PeerIdentity sender;
   size_t msize = ntohs (message->size) - sizeof (struct MeshAudioMessage);
   char buf[msize + sizeof (struct ClientAudioMessage)];
   struct ClientAudioMessage *cam;
-  const union GNUNET_MESH_TunnelInfo *info;
-  
+  const union GNUNET_MESH_ChannelInfo *info;
+
   msg = (const struct MeshAudioMessage *) message;
-  if (NULL == line)
+  if (NULL == ch)
   {
-    info = GNUNET_MESH_tunnel_get_info (tunnel,
-                                        GNUNET_MESH_OPTION_PEER);
+    info = GNUNET_MESH_channel_get_info (channel,
+                                         GNUNET_MESH_OPTION_PEER);
     if (NULL == info)
     {
       GNUNET_break (0);
@@ -1001,22 +1240,27 @@ handle_mesh_audio_message (void *cls,
     }
     sender = info->peer;
     for (line = lines_head; NULL != line; line = line->next)
-      if ( (line->local_line == ntohl (msg->remote_line)) &&
-           (LS_CALLEE_CONNECTED == line->status) &&
-           (0 == memcmp (&line->target,
-                         &sender,
-                         sizeof (struct GNUNET_PeerIdentity))) &&
-           (NULL == line->tunnel_unreliable) )
-        break;
-    if (NULL == line)
+      if (line->local_line == ntohl (msg->remote_line))
+      {
+        for (ch = line->channel_head; NULL != ch; ch = ch->next)
+        {
+          if ( (CS_CALLEE_CONNECTED == ch->status) &&
+               (0 == memcmp (&ch->target,
+                             &sender,
+                             sizeof (struct GNUNET_PeerIdentity))) &&
+               (NULL == ch->channel_unreliable) )
+            break;
+        }
+      }
+    if (NULL == ch)
     {
       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                   "Received AUDIO data for non-existing line %u, dropping.\n",
                   ntohl (msg->remote_line));
       return GNUNET_SYSERR;
-    }    
-    line->tunnel_unreliable = tunnel;
-    *tunnel_ctx = line;
+    }
+    ch->channel_unreliable = channel;
+    *channel_ctx = ch;
   }
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
               "Forwarding %u bytes of AUDIO data to client\n",
@@ -1024,103 +1268,115 @@ handle_mesh_audio_message (void *cls,
   cam = (struct ClientAudioMessage *) buf;
   cam->header.size = htons (sizeof (buf));
   cam->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO);
+  cam->cid = ch->cid;
   memcpy (&cam[1], &msg[1], msize);
   GNUNET_SERVER_notification_context_unicast (nc,
-                                              line->client,
+                                              ch->line->client,
                                               &cam->header,
                                               GNUNET_YES);
-  GNUNET_MESH_receive_done (tunnel);
+  GNUNET_MESH_receive_done (channel);
   return GNUNET_OK;
 }
 
 
 /**
- * Method called whenever another peer has added us to a tunnel
+ * Method called whenever another peer has added us to a channel
  * the other peer initiated.
  *
  * @param cls closure
- * @param tunnel new handle to the tunnel
- * @param initiator peer that started the tunnel
+ * @param channel new handle to the channel
+ * @param initiator peer that started the channel
  * @param port port
- * @return initial tunnel context for the tunnel (can be NULL -- that's not an error)
+ * @param options channel option flags
+ * @return initial channel context for the channel;
+ *         (can be NULL -- that's not an error)
  */
 static void *
-inbound_tunnel (void *cls,
-                struct GNUNET_MESH_Tunnel *tunnel,
-               const struct GNUNET_PeerIdentity *initiator, 
-                uint32_t port)
+inbound_channel (void *cls,
+                struct GNUNET_MESH_Channel *channel,
+               const struct GNUNET_PeerIdentity *initiator,
+                uint32_t port, enum GNUNET_MESH_ChannelOption options)
 {
   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-             _("Received incoming tunnel on port %u\n"), 
+             _("Received incoming channel on port %u\n"),
               (unsigned int) port);
   return NULL;
 }
 
 
 /**
- * Function called whenever an inbound tunnel is destroyed.  Should clean up
+ * Function called whenever an inbound channel is destroyed.  Should clean up
  * any associated state.
  *
  * @param cls closure (set from #GNUNET_MESH_connect)
- * @param tunnel connection to the other end (henceforth invalid)
- * @param tunnel_ctx place where local state associated
- *                   with the tunnel is stored
+ * @param channel connection to the other end (henceforth invalid)
+ * @param channel_ctx place where local state associated
+ *                   with the channel is stored;
+ *                   may point to the `struct Channel`
  */
 static void
 inbound_end (void *cls,
-             const struct GNUNET_MESH_Tunnel *tunnel,
-            void *tunnel_ctx)
+             const struct GNUNET_MESH_Channel *channel,
+            void *channel_ctx)
 {
-  struct Line *line = tunnel_ctx;
+  struct Channel *ch = channel_ctx;
+  struct Line *line;
   struct ClientPhoneHangupMessage hup;
 
-  if (NULL == line)
+  if (NULL == ch)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "Mesh channel destroyed, but channel is unknown to us\n");
     return;
-  if (line->tunnel_unreliable == tunnel)
+  }
+  line = ch->line;
+  if (ch->channel_unreliable == channel)
   {
-    if (NULL != line->unreliable_mth)
+    if (NULL != ch->unreliable_mth)
     {
-      GNUNET_MESH_notify_transmit_ready_cancel (line->unreliable_mth);
-      line->unreliable_mth = NULL;
+      GNUNET_MESH_notify_transmit_ready_cancel (ch->unreliable_mth);
+      ch->unreliable_mth = NULL;
     }
-    line->tunnel_unreliable = NULL;
+    ch->channel_unreliable = NULL;
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "Unreliable channel destroyed\n");
     return;
   }
-  if (line->tunnel_reliable != tunnel)
+  if (ch->channel_reliable != channel)
+  {
+    /* recursive call, I'm the one destroying 'ch' right now */
     return;
-  line->tunnel_reliable = NULL;
+  }
+  ch->channel_reliable = NULL;
 
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-             "Mesh tunnel destroyed by mesh\n");
-  hup.header.size = sizeof (hup);
+             "Mesh channel destroyed by mesh in state %d\n",
+              ch->status);
+  hup.header.size = htons (sizeof (hup));
   hup.header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP);
-  switch (line->status)
+  hup.cid = ch->cid;
+  switch (ch->status)
   {
-  case LS_CALLEE_LISTEN:
-    GNUNET_break (0);
-    break;
-  case LS_CALLEE_RINGING:
-  case LS_CALLEE_CONNECTED:
+  case CS_CALLEE_RINGING:
+  case CS_CALLEE_CONNECTED:
     GNUNET_SERVER_notification_context_unicast (nc,
                                                 line->client,
                                                 &hup.header,
                                                 GNUNET_NO);
-    line->status = LS_CALLEE_LISTEN;
     break;
-  case LS_CALLEE_SHUTDOWN:
-    line->status = LS_CALLEE_LISTEN;
+  case CS_CALLEE_SHUTDOWN:
     break;
-  case LS_CALLER_CALLING:
-  case LS_CALLER_CONNECTED:
+  case CS_CALLER_CALLING:
+  case CS_CALLER_CONNECTED:
     GNUNET_SERVER_notification_context_unicast (nc,
                                                 line->client,
                                                 &hup.header,
                                                 GNUNET_NO);
     break;
-  case LS_CALLER_SHUTDOWN:
+  case CS_CALLER_SHUTDOWN:
     break;
   }
-  destroy_line_mesh_tunnels (line);
+  destroy_line_mesh_channels (ch);
 }
 
 
@@ -1131,7 +1387,7 @@ inbound_end (void *cls,
  * @param client identification of the client
  */
 static void
-handle_client_disconnect (void *cls, 
+handle_client_disconnect (void *cls,
                           struct GNUNET_SERVER_Client *client)
 {
   struct Line *line;
@@ -1141,21 +1397,21 @@ handle_client_disconnect (void *cls,
   line = GNUNET_SERVER_client_get_user_context (client, struct Line);
   if (NULL == line)
     return;
-  GNUNET_SERVER_client_set_user_context (client, NULL);
+  GNUNET_SERVER_client_set_user_context (client, (void *)NULL);
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
               "Client disconnected, closing line\n");
   GNUNET_CONTAINER_DLL_remove (lines_head,
                                lines_tail,
                                line);
-  destroy_line_mesh_tunnels (line);
-  GNUNET_free_non_null (line->audio_data);
+  while (NULL != line->channel_head)
+    destroy_line_mesh_channels (line->channel_head);
   GNUNET_free (line);
 }
 
 
 /**
  * Shutdown nicely
- * 
+ *
  * @param cls closure, NULL
  * @param tc the task context
  */
@@ -1163,6 +1419,19 @@ static void
 do_shutdown (void *cls,
              const struct GNUNET_SCHEDULER_TaskContext *tc)
 {
+  struct Line *line;
+  struct Channel *ch;
+
+  while (NULL != (line = lines_head))
+  {
+    while (NULL != (ch = line->channel_head))
+      destroy_line_mesh_channels (ch);
+    GNUNET_CONTAINER_DLL_remove (lines_head,
+                                 lines_tail,
+                                 line);
+    GNUNET_SERVER_client_set_user_context (line->client, (void *) NULL);
+    GNUNET_free (line);
+  }
   if (NULL != mesh)
   {
     GNUNET_MESH_disconnect (mesh);
@@ -1184,7 +1453,7 @@ do_shutdown (void *cls,
  * @param c configuration
  */
 static void
-run (void *cls, 
+run (void *cls,
      struct GNUNET_SERVER_Handle *server,
      const struct GNUNET_CONFIGURATION_Handle *c)
 {
@@ -1194,13 +1463,19 @@ run (void *cls,
      sizeof (struct ClientPhoneRegisterMessage)},
     {&handle_client_pickup_message, NULL,
      GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_PICK_UP,
-     0},
+     sizeof (struct ClientPhonePickupMessage) },
+    {&handle_client_suspend_message, NULL,
+     GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_SUSPEND,
+     sizeof (struct ClientPhoneSuspendMessage) },
+    {&handle_client_resume_message, NULL,
+     GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_RESUME,
+     sizeof (struct ClientPhoneResumeMessage) },
     {&handle_client_hangup_message, NULL,
      GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP,
-     0},
+     sizeof (struct ClientPhoneHangupMessage) },
     {&handle_client_call_message, NULL,
      GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_CALL,
-     0},
+     sizeof (struct ClientCallMessage) },
     {&handle_client_audio_message, NULL,
      GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO,
      0},
@@ -1210,34 +1485,37 @@ run (void *cls,
     {&handle_mesh_ring_message,
      GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_RING,
      sizeof (struct MeshPhoneRingMessage)},
-    {&handle_mesh_hangup_message, 
+    {&handle_mesh_hangup_message,
      GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_HANG_UP,
-     0},
-    {&handle_mesh_pickup_message, 
+     sizeof (struct MeshPhoneHangupMessage)},
+    {&handle_mesh_pickup_message,
      GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_PICK_UP,
-     0},
-    {&handle_mesh_busy_message, 
-     GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_BUSY,
-     sizeof (struct MeshPhoneBusyMessage)},
+     sizeof (struct MeshPhonePickupMessage)},
+    {&handle_mesh_suspend_message,
+     GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_SUSPEND,
+     sizeof (struct MeshPhoneSuspendMessage)},
+    {&handle_mesh_resume_message,
+     GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_RESUME,
+     sizeof (struct MeshPhoneResumeMessage)},
     {&handle_mesh_audio_message, GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_AUDIO,
      0},
     {NULL, 0, 0}
   };
-  static uint32_t ports[] = { 
+  static uint32_t ports[] = {
     GNUNET_APPLICATION_TYPE_CONVERSATION_CONTROL,
     GNUNET_APPLICATION_TYPE_CONVERSATION_AUDIO,
-    0 
+    0
   };
 
   cfg = c;
   GNUNET_assert (GNUNET_OK ==
-                 GNUNET_CRYPTO_get_host_identity (cfg,
+                 GNUNET_CRYPTO_get_peer_identity (cfg,
                                                   &my_identity));
   mesh = GNUNET_MESH_connect (cfg,
                              NULL,
-                             &inbound_tunnel,
-                             &inbound_end, 
-                              mesh_handlers, 
+                             &inbound_channel,
+                             &inbound_end,
+                              mesh_handlers,
                               ports);
 
   if (NULL == mesh)
@@ -1249,7 +1527,7 @@ run (void *cls,
   nc = GNUNET_SERVER_notification_context_create (server, 16);
   GNUNET_SERVER_add_handlers (server, server_handlers);
   GNUNET_SERVER_disconnect_notify (server, &handle_client_disconnect, NULL);
-  GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, 
+  GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
                                 &do_shutdown,
                                NULL);
 }
@@ -1263,12 +1541,12 @@ run (void *cls,
  * @return 0 ok, 1 on error
  */
 int
-main (int argc, 
+main (int argc,
       char *const *argv)
 {
   return (GNUNET_OK ==
          GNUNET_SERVICE_run (argc, argv,
-                              "conversation", 
+                              "conversation",
                               GNUNET_SERVICE_OPTION_NONE,
                              &run, NULL)) ? 0 : 1;
 }