-man page for gnunet-conversation
[oweals/gnunet.git] / src / conversation / gnunet-service-conversation-new.c
index 39d0432542953b7f3658f4010b422fa9921fc199..d379931f7cd77980ed65653ad6bed0c6b126e527 100644 (file)
 #include "gnunet_protocols.h"
 #include "gnunet_applications.h"
 #include "gnunet_constants.h"
+#include "gnunet_signatures.h"
 #include "gnunet_mesh_service.h"
 #include "gnunet_conversation_service.h"
 #include "conversation.h"
 
 
+/**
+ * How long is our signature on a call valid?  Needs to be long enough for time zone
+ * differences and network latency to not matter.  No strong need for it to be short,
+ * but we simply like all signatures to eventually expire.
+ */
+#define RING_TIMEOUT GNUNET_TIME_UNIT_DAYS
+
+
 /**
  * The possible connection status
  */
@@ -54,6 +63,11 @@ enum LineStatus
    */
   LS_CALLEE_CONNECTED,
 
+  /**
+   * We're in shutdown, sending hangup messages before cleaning up.
+   */
+  LS_CALLEE_SHUTDOWN,
+
   /**
    * We are waiting for the phone to be picked up.
    */
@@ -100,12 +114,42 @@ struct Line
   /**
    * Transmit handle for pending audio messages
    */
-  struct GNUNET_MESH_TransmitHandle *mth;
+  struct GNUNET_MESH_TransmitHandle *unreliable_mth;
+
+  /**
+   * Message queue for control messages
+   */
+  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.
+   */
+  struct GNUNET_PeerIdentity target;
+
+  /**
+   * Temporary buffer for audio data.
+   */
+  void *audio_data;
+
+  /**
+   * Number of bytes in @e audio_data.
+   */
+  size_t audio_size;
 
   /**
    * Our line number.
    */
-  uint32_t line;
+  uint32_t local_line;
+
+  /**
+   * Remote line number.
+   */
+  uint32_t remote_line;
 
   /**
    * Current status of this line.
@@ -130,6 +174,11 @@ static struct GNUNET_SERVER_NotificationContext *nc;
  */
 static struct GNUNET_MESH_Handle *mesh;
 
+/**
+ * Identity of this peer.
+ */
+static struct GNUNET_PeerIdentity my_identity;
+
 /**
  * Head of DLL of active lines.
  */
@@ -140,6 +189,13 @@ static struct Line *lines_head;
  */
 static struct Line *lines_tail;
 
+/**
+ * Counter for generating local line numbers.
+ * FIXME: randomize generation in the future
+ * to eliminate information leakage.
+ */
+static uint32_t local_line_cnt;
+
 
 /**
  * Function to register a phone.
@@ -154,9 +210,24 @@ handle_client_register_message (void *cls,
                                 const struct GNUNET_MessageHeader *message)
 {
   const struct ClientPhoneRegisterMessage *msg;
+  struct Line *line;
 
   msg = (struct ClientPhoneRegisterMessage *) message;
-  GNUNET_break (0); // FIXME
+  line = GNUNET_SERVER_client_get_user_context (client, struct Line);
+  if (NULL != line)
+  {
+    GNUNET_break (0);
+    GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
+    return;
+  }
+  line = GNUNET_new (struct Line);
+  line->client = client;
+  GNUNET_SERVER_notification_context_add (nc, client);
+  GNUNET_CONTAINER_DLL_insert (lines_head,
+                               lines_tail,
+                               line);
+  line->local_line = ntohl (msg->line);
+  GNUNET_SERVER_client_set_user_context (client, line);
   GNUNET_SERVER_receive_done (client, GNUNET_OK);
 }
 
@@ -174,13 +245,139 @@ handle_client_pickup_message (void *cls,
                               const struct GNUNET_MessageHeader *message)
 {
   const struct ClientPhonePickupMessage *msg;
+  struct GNUNET_MQ_Envelope *e;
+  struct MeshPhonePickupMessage *mppm;
+  const char *meta;
+  struct Line *line;
+  size_t len;
 
   msg = (struct ClientPhonePickupMessage *) message;
-  GNUNET_break (0); // FIXME
+  meta = (const char *) &msg[1];
+  len = ntohs (msg->header.size) - sizeof (struct ClientPhonePickupMessage);
+  if ( (0 == len) ||
+       ('\0' != meta[len - 1]) )
+  {
+    meta = NULL;
+    len = 0;
+  }
+  line = GNUNET_SERVER_client_get_user_context (client, struct Line);
+  if (NULL == line)
+  {
+    GNUNET_break (0);
+    GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
+    return;
+  }
+  switch (line->status)
+  {
+  case LS_CALLEE_LISTEN:
+    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;
+    break;
+  case LS_CALLEE_CONNECTED:
+    GNUNET_break (0);
+    GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
+    return;
+  case LS_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:
+    GNUNET_break (0);
+    GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
+    return;
+  }
+  line->status = LS_CALLEE_CONNECTED;
+  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);
   GNUNET_SERVER_receive_done (client, GNUNET_OK);
 }
 
 
+/**
+ * Destroy the mesh tunnels of a line.
+ *
+ * @param line line to shutdown tunnels of
+ */
+static void
+destroy_line_mesh_tunnels (struct Line *line)
+{
+  if (NULL != line->reliable_mq)
+  {
+    GNUNET_MQ_destroy (line->reliable_mq);
+    line->reliable_mq = NULL;
+  }
+  if (NULL != line->unreliable_mth)
+  {
+    GNUNET_MESH_notify_transmit_ready_cancel (line->unreliable_mth);
+    line->unreliable_mth = NULL;
+  }
+  if (NULL != line->tunnel_unreliable)
+  {
+    GNUNET_MESH_tunnel_destroy (line->tunnel_unreliable);
+    line->tunnel_unreliable = NULL;
+  }
+  if (NULL != line->tunnel_reliable)
+  {
+    GNUNET_MESH_tunnel_destroy (line->tunnel_reliable);
+    line->tunnel_reliable = NULL;
+  }
+}
+
+
+/**
+ * We are done signalling shutdown to the other peer.  Close down
+ * (or reset) the line.
+ *
+ * @param cls the `struct Line` to reset/terminate
+ */
+static void
+mq_done_finish_caller_shutdown (void *cls)
+{
+  struct Line *line = cls;
+
+  switch (line->status)
+  {
+  case LS_CALLEE_LISTEN:
+    GNUNET_break (0);
+    break;
+  case LS_CALLEE_RINGING:
+    GNUNET_break (0);
+    break;
+  case LS_CALLEE_CONNECTED:
+    GNUNET_break (0);
+    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;
+    break;
+  case LS_CALLER_CONNECTED:
+    line->status = LS_CALLER_SHUTDOWN;
+    break;
+  case LS_CALLER_SHUTDOWN:
+    destroy_line_mesh_tunnels (line);
+    GNUNET_CONTAINER_DLL_remove (lines_head,
+                                 lines_tail,
+                                 line);
+    GNUNET_free_non_null (line->audio_data);
+    GNUNET_free (line);
+    break;
+  }  
+}
+
+
 /**
  * Function to handle a hangup request message from the client
  *
@@ -194,9 +391,63 @@ handle_client_hangup_message (void *cls,
                               const struct GNUNET_MessageHeader *message)
 {
   const struct ClientPhoneHangupMessage *msg;
+  struct GNUNET_MQ_Envelope *e;
+  struct MeshPhoneHangupMessage *mhum;
+  const char *meta;
+  struct Line *line;
+  size_t len;
 
   msg = (struct ClientPhoneHangupMessage *) message;
-  GNUNET_break (0); // FIXME
+  meta = (const char *) &msg[1];
+  len = ntohs (msg->header.size) - sizeof (struct ClientPhoneHangupMessage);
+  if ( (0 == len) ||
+       ('\0' != meta[len - 1]) )
+  {
+    meta = NULL;
+    len = 0;
+  }
+  line = GNUNET_SERVER_client_get_user_context (client, struct Line);
+  if (NULL == line)
+  {
+    GNUNET_break (0);
+    GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
+    return;
+  }
+  switch (line->status)
+  {
+  case LS_CALLEE_LISTEN:
+    GNUNET_break (0);
+    GNUNET_SERVER_receive_done (client, GNUNET_OK);
+    return;
+  case LS_CALLEE_RINGING:
+    line->status = LS_CALLEE_SHUTDOWN;
+    break;
+  case LS_CALLEE_CONNECTED:
+    line->status = LS_CALLEE_SHUTDOWN;
+    break;
+  case LS_CALLEE_SHUTDOWN:
+    GNUNET_break (0);
+    GNUNET_SERVER_receive_done (client, GNUNET_OK);
+    return;
+  case LS_CALLER_CALLING:
+    line->status = LS_CALLER_SHUTDOWN;
+    break;
+  case LS_CALLER_CONNECTED:
+    line->status = LS_CALLER_SHUTDOWN;
+    break;
+  case LS_CALLER_SHUTDOWN:
+    GNUNET_break (0);
+    GNUNET_SERVER_receive_done (client, GNUNET_OK);
+    return;
+  }
+  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);
   GNUNET_SERVER_receive_done (client, GNUNET_OK);
 }
 
@@ -214,13 +465,88 @@ handle_client_call_message (void *cls,
                             const struct GNUNET_MessageHeader *message)
 {
   const struct ClientCallMessage *msg;
+  struct Line *line;
+  struct GNUNET_MQ_Envelope *e;
+  struct MeshPhoneRingMessage *ring;
 
   msg = (struct ClientCallMessage *) message;
-  GNUNET_break (0); // FIXME
+  line = GNUNET_SERVER_client_get_user_context (client, struct Line);
+  if (NULL != line)
+  {
+    GNUNET_break (0);
+    GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
+    return;
+  }
+  line = GNUNET_new (struct Line);
+  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,
+                                                     &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++;
+  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);
+  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_MQ_send (line->reliable_mq, e);
+  GNUNET_SERVER_client_set_user_context (client, line);
   GNUNET_SERVER_receive_done (client, GNUNET_OK);
 }
 
 
+/**
+ * Transmit audio data via unreliable mesh channel.
+ *
+ * @param cls the `struct Line` 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 @buf
+ */
+static size_t
+transmit_line_audio (void *cls,
+                     size_t size,
+                     void *buf)
+{
+  struct Line *line = cls;
+  struct MeshAudioMessage *mam = buf;
+  
+  line->unreliable_mth = NULL;
+  if ( (NULL == buf) ||
+       (size < sizeof (struct MeshAudioMessage) + line->audio_size) )
+    {
+    /* eh, other error handling? */
+    return 0;
+  }
+  mam->header.size = htons (sizeof (struct MeshAudioMessage) + line->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;
+  return sizeof (struct MeshAudioMessage) + line->audio_size;  
+}
+
+
 /**
  * Function to handle audio data from the client
  *
@@ -234,13 +560,84 @@ handle_client_audio_message (void *cls,
                              const struct GNUNET_MessageHeader *message)
 {
   const struct ClientAudioMessage *msg;
+  struct Line *line;
+  size_t size;
 
+  size = ntohs (message->size) - sizeof (struct ClientAudioMessage);
   msg = (struct ClientAudioMessage *) message;
-  GNUNET_break (0); // FIXME
+  line = GNUNET_SERVER_client_get_user_context (client, struct Line);
+  if (NULL == line)
+  {
+    GNUNET_break (0);
+    GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
+    return;
+  }
+  switch (line->status)
+  {
+  case LS_CALLEE_LISTEN:
+  case LS_CALLEE_RINGING:
+  case LS_CALLER_CALLING:
+    GNUNET_break (0);
+    GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
+    return;
+  case LS_CALLEE_CONNECTED:
+  case LS_CALLER_CONNECTED:
+    /* common case, handled below */
+    break;
+  case LS_CALLEE_SHUTDOWN:
+  case LS_CALLER_SHUTDOWN:
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "Mesh audio channel in shutdown; audio data dropped\n");
+    GNUNET_SERVER_receive_done (client, GNUNET_OK);
+    return;
+  }
+  if (NULL == line->tunnel_unreliable)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                _("Mesh audio channel not ready; audio data dropped\n"));
+    GNUNET_SERVER_receive_done (client, GNUNET_OK);    
+    return;
+  }
+  if (NULL != line->unreliable_mth)
+  {
+    /* NOTE: we may want to not do this and instead combine the data */
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "Dropping previous audio data segment with %u bytes\n",
+                line->audio_size);
+    GNUNET_MESH_notify_transmit_ready_cancel (line->unreliable_mth);
+    GNUNET_free (line->audio_data);
+  }
+  line->audio_size = size;
+  line->audio_data = GNUNET_malloc (line->audio_size);
+  memcpy (line->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);
   GNUNET_SERVER_receive_done (client, GNUNET_OK);
 }
 
 
+/**
+ * We are done signalling shutdown to the other peer.  
+ * Destroy the tunnel.
+ *
+ * @param cls the `struct GNUNET_MESH_tunnel` to destroy
+ */
+static void
+mq_done_destroy_tunnel (void *cls)
+{
+  struct GNUNET_MESH_Tunnel *tunnel = cls;
+  
+  GNUNET_MESH_tunnel_destroy (tunnel);
+}
+
+
 /**
  * Function to handle a ring message incoming over mesh
  *
@@ -257,9 +654,56 @@ handle_mesh_ring_message (void *cls,
                           const struct GNUNET_MessageHeader *message)
 {
   const struct MeshPhoneRingMessage *msg;
+  struct Line *line;
+  struct GNUNET_MQ_Envelope *e;
+  struct MeshPhoneBusyMessage *busy;
+  struct ClientPhoneRingMessage cring;
   
   msg = (const struct MeshPhoneRingMessage *) message;
-  GNUNET_break (0); // FIXME
+  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))) ||
+       (GNUNET_OK !=
+        GNUNET_CRYPTO_ecc_verify (GNUNET_SIGNATURE_PURPOSE_CONVERSATION_RING,
+                                  &msg->purpose,
+                                  &msg->signature,
+                                  &msg->caller_id)) )
+  {
+    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) )
+      break;
+  if (NULL == line) 
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                _("No available phone for incoming call on line %u, sending BUSY signal\n"),
+                ntohl (msg->remote_line));
+    e = GNUNET_MQ_msg (busy, GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_BUSY);
+    GNUNET_MQ_notify_sent (e,
+                           &mq_done_destroy_tunnel,
+                           tunnel);
+    GNUNET_MQ_send (line->reliable_mq, e);
+    GNUNET_MESH_receive_done (tunnel); /* 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;
+  cring.header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_RING);
+  cring.header.size = htons (sizeof (cring));
+  cring.reserved = htonl (0);
+  cring.caller_id = msg->caller_id;
+  GNUNET_SERVER_notification_context_unicast (nc,
+                                              line->client,
+                                              &cring.header,
+                                              GNUNET_NO);
+  GNUNET_MESH_receive_done (tunnel);
   return GNUNET_OK;
 }
 
@@ -279,10 +723,67 @@ handle_mesh_hangup_message (void *cls,
                             void **tunnel_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;
-  GNUNET_break (0); // FIXME
+  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)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "HANGUP message received for non-existing line, dropping tunnel.\n");
+    return GNUNET_SYSERR;
+  }
+  *tunnel_ctx = NULL;
+  switch (line->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);
+    break;
+  case LS_CALLEE_SHUTDOWN:
+    line->status = LS_CALLEE_LISTEN;
+    destroy_line_mesh_tunnels (line);
+    return GNUNET_OK;
+  case LS_CALLER_CALLING:
+    line->status = LS_CALLER_SHUTDOWN;
+    mq_done_finish_caller_shutdown (line);
+    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);
+    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_SERVER_notification_context_unicast (nc,
+                                              line->client,
+                                              &hup->header,
+                                              GNUNET_NO);
+  GNUNET_MESH_receive_done (tunnel);
   return GNUNET_OK;
 }
 
@@ -303,9 +804,69 @@ handle_mesh_pickup_message (void *cls,
                             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;
-  GNUNET_break (0); // FIXME
+  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)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "PICKUP message received for non-existing line, dropping tunnel.\n");
+    return GNUNET_SYSERR;
+  }
+  GNUNET_MESH_receive_done (tunnel);
+  switch (line->status)
+  {
+  case LS_CALLEE_LISTEN:
+    GNUNET_break (0);
+    return GNUNET_SYSERR;
+  case LS_CALLEE_RINGING:
+  case LS_CALLEE_CONNECTED:
+    GNUNET_break_op (0);
+    destroy_line_mesh_tunnels (line);
+    line->status = LS_CALLEE_LISTEN;
+    return GNUNET_SYSERR;
+  case LS_CALLEE_SHUTDOWN:
+    GNUNET_break_op (0);
+    line->status = LS_CALLEE_LISTEN;
+    destroy_line_mesh_tunnels (line);
+    break;
+  case LS_CALLER_CALLING:
+    line->status = LS_CALLER_CONNECTED;
+    break;
+  case LS_CALLER_CONNECTED:
+    GNUNET_break_op (0);
+    return GNUNET_OK;
+  case LS_CALLER_SHUTDOWN:
+    GNUNET_break_op (0);
+    mq_done_finish_caller_shutdown (line);
+    return GNUNET_SYSERR;
+  }
+  pick = (struct ClientPhonePickupMessage *) buf;
+  pick->header.size = sizeof (buf);
+  pick->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_PICK_UP);
+  memcpy (&pick[1], metadata, len);
+  GNUNET_SERVER_notification_context_unicast (nc,
+                                              line->client,
+                                              &pick->header,
+                                              GNUNET_NO);
+  line->tunnel_unreliable = GNUNET_MESH_tunnel_create (mesh,
+                                                       line,
+                                                       &line->target,
+                                                       GNUNET_APPLICATION_TYPE_CONVERSATION_AUDIO,
+                                                       GNUNET_YES,
+                                                       GNUNET_NO);
   return GNUNET_OK;
 }
 
@@ -325,10 +886,49 @@ handle_mesh_busy_message (void *cls,
                           void **tunnel_ctx,
                           const struct GNUNET_MessageHeader *message)
 {
-  const struct MeshPhoneBusyMessage *msg;
-  
-  msg = (const struct MeshPhoneBusyMessage *) message;
-  GNUNET_break (0); // FIXME
+  struct Line *line = *tunnel_ctx;
+  struct ClientPhoneBusyMessage busy;
+
+  if (NULL == line)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "HANGUP message received for non-existing line, dropping tunnel.\n");
+    return GNUNET_SYSERR;
+  }
+  busy.header.size = sizeof (busy);
+  busy.header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_BUSY);
+  GNUNET_SERVER_notification_context_unicast (nc,
+                                              line->client,
+                                              &busy.header,
+                                              GNUNET_NO);
+  GNUNET_MESH_receive_done (tunnel);
+  *tunnel_ctx = NULL;
+  switch (line->status)
+  {
+  case LS_CALLEE_LISTEN:
+    GNUNET_break (0);
+    return GNUNET_SYSERR;
+  case LS_CALLEE_RINGING:
+    GNUNET_break_op (0);
+    break;
+  case LS_CALLEE_CONNECTED:
+    GNUNET_break_op (0);
+    break;
+  case LS_CALLEE_SHUTDOWN:
+    GNUNET_break_op (0);
+    break;
+  case LS_CALLER_CALLING:
+    line->status = LS_CALLER_SHUTDOWN;
+    mq_done_finish_caller_shutdown (line);
+    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);
+    break;
+  }
   return GNUNET_OK;
 }
 
@@ -349,9 +949,44 @@ handle_mesh_audio_message (void *cls,
                            const struct GNUNET_MessageHeader *message)
 {
   const struct MeshAudioMessage *msg;
+  struct Line *line = *tunnel_ctx;
+  struct GNUNET_PeerIdentity sender;
+  size_t msize = ntohs (message->size) - sizeof (struct MeshAudioMessage);
+  char buf[msize + sizeof (struct ClientAudioMessage)];
+  struct ClientAudioMessage *cam;
   
   msg = (const struct MeshAudioMessage *) message;
-  GNUNET_break (0); // FIXME
+  if (NULL == line)
+  {
+    sender = *GNUNET_MESH_tunnel_get_info (tunnel,
+                                           GNUNET_MESH_OPTION_PEER)->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)
+    {
+      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;
+  }
+  cam = (struct ClientAudioMessage *) buf;
+  cam->header.size = htons (sizeof (buf));
+  cam->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO);
+  memcpy (&cam[1], &msg[1], msize);
+  GNUNET_SERVER_notification_context_unicast (nc,
+                                              line->client,
+                                              &cam->header,
+                                              GNUNET_YES);
+  GNUNET_MESH_receive_done (tunnel);
   return GNUNET_OK;
 }
 
@@ -372,9 +1007,9 @@ inbound_tunnel (void *cls,
                const struct GNUNET_PeerIdentity *initiator, 
                 uint32_t port)
 {
-  GNUNET_break (0); // FIXME
   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-             _("Received incoming tunnel on port %d\n"), port);
+             _("Received incoming tunnel on port %d\n"), 
+              port);
   return NULL;
 }
 
@@ -393,7 +1028,56 @@ inbound_end (void *cls,
              const struct GNUNET_MESH_Tunnel *tunnel,
             void *tunnel_ctx)
 {
-  GNUNET_break (0); // FIXME
+  struct Line *line = tunnel_ctx;
+  struct ClientPhoneHangupMessage hup;
+
+  if (NULL == line)
+    return;
+  if (line->tunnel_unreliable == tunnel)
+  {
+    line->tunnel_unreliable = NULL;
+    return;
+  }
+  hup.header.size = sizeof (hup);
+  hup.header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP);
+  switch (line->status)
+  {
+  case LS_CALLEE_LISTEN:
+    GNUNET_break (0);
+    return;
+  case LS_CALLEE_RINGING:
+  case LS_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;
+    destroy_line_mesh_tunnels (line);
+    break;
+  case LS_CALLER_CALLING:
+  case LS_CALLER_CONNECTED:
+    GNUNET_SERVER_notification_context_unicast (nc,
+                                                line->client,
+                                                &hup.header,
+                                                GNUNET_NO);
+    destroy_line_mesh_tunnels (line);
+    GNUNET_CONTAINER_DLL_remove (lines_head,
+                                 lines_tail,
+                                 line);
+    GNUNET_free_non_null (line->audio_data);
+    GNUNET_free (line);
+    break;
+  case LS_CALLER_SHUTDOWN:
+    destroy_line_mesh_tunnels (line);
+    GNUNET_CONTAINER_DLL_remove (lines_head,
+                                 lines_tail,
+                                 line);
+    GNUNET_free (line);
+    break;
+  }
 }
 
 
@@ -405,9 +1089,18 @@ inbound_end (void *cls,
  */
 static void
 handle_client_disconnect (void *cls, 
-                          struct GNUNET_SERVER_Client *cl)
+                          struct GNUNET_SERVER_Client *client)
 {
-  GNUNET_break (0); // FIXME
+  struct Line *line;
+
+  line = GNUNET_SERVER_client_get_user_context (client, struct Line);
+  if (NULL == line)
+    return;
+  GNUNET_CONTAINER_DLL_remove (lines_head,
+                               lines_tail,
+                               line);
+  GNUNET_free (line);
+  GNUNET_SERVER_client_set_user_context (client, NULL);
 }
 
 
@@ -418,9 +1111,9 @@ handle_client_disconnect (void *cls,
  * @param tc the task context
  */
 static void
-do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+do_shutdown (void *cls,
+             const struct GNUNET_SCHEDULER_TaskContext *tc)
 {
-  GNUNET_break (0); // FIXME
   if (NULL != mesh)
   {
     GNUNET_MESH_disconnect (mesh);
@@ -488,6 +1181,9 @@ run (void *cls,
   };
 
   cfg = c;
+  GNUNET_assert (GNUNET_OK ==
+                 GNUNET_CRYPTO_get_host_identity (cfg,
+                                                  &my_identity));
   mesh = GNUNET_MESH_connect (cfg,
                              NULL,
                              &inbound_tunnel,