(no commit message)
[oweals/gnunet.git] / src / transport / plugin_transport_http.c
index 085cb78a07d69b84e16364c4c3795ec4aa84a11a..7984529343dd4381b8a54a5b311ed3c0b18eb5f6 100644 (file)
  */
 
 #include "platform.h"
+#include "gnunet_constants.h"
 #include "gnunet_protocols.h"
 #include "gnunet_connection_lib.h"
 #include "gnunet_server_lib.h"
 #include "gnunet_service_lib.h"
 #include "gnunet_statistics_service.h"
 #include "gnunet_transport_service.h"
+#include "gnunet_resolver_service.h"
 #include "plugin_transport.h"
+#include "gnunet_os_lib.h"
 #include "microhttpd.h"
 #include <curl/curl.h>
 
+
+#define DEBUG_CURL GNUNET_CURL
 #define DEBUG_HTTP GNUNET_NO
 
 /**
@@ -44,7 +49,6 @@
  */
 #define HTTP_PUT_RESPONSE "Thank you!"
 
-
 /**
  * After how long do we expire an address that we
  * learned from another peer if it is not reconfirmed
  */
 #define LEARNED_ADDRESS_EXPIRATION GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 6)
 
+/**
+ * Page returned if request invalid
+ */
+#define HTTP_ERROR_RESPONSE "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\"><HTML><HEAD><TITLE>404 Not Found</TITLE></HEAD><BODY><H1>Not Found</H1>The requested URL was not found on this server.<P><HR><ADDRESS></ADDRESS></BODY></HTML>"
+
+/**
+ * Timeout for a http connect
+ */
+#define HTTP_CONNECT_TIMEOUT 30
+
+/**
+ * Timeout for a http connect
+ */
+#define HTTP_MESSAGE_INITIAL_BUFFERSIZE GNUNET_SERVER_MAX_MESSAGE_SIZE
+
 
 /**
  * Encapsulation of all of the state of the plugin.
  */
 struct Plugin;
 
+/**
+ * Network format for IPv4 addresses.
+ */
+struct IPv4HttpAddress
+{
+  /**
+   * IPv4 address, in network byte order.
+   */
+  uint32_t ipv4_addr;
+
+  /**
+   * Port number, in network byte order.
+   */
+  uint16_t u_port;
+
+};
+
 
 /**
- * Session handle for connections.
+ * Network format for IPv6 addresses.
  */
-struct Session
+struct IPv6HttpAddress
+{
+  /**
+   * IPv6 address.
+   */
+  struct in6_addr ipv6_addr;
+
+  /**
+   * Port number, in network byte order.
+   */
+  uint16_t u6_port;
+
+};
+
+
+
+/**
+ *  Message to send using http
+ */
+struct HTTP_Message
 {
+  /**
+   * Next field for linked list
+   */
+  struct HTTP_Message * next;
 
   /**
-   * Stored in a linked list.
+   * buffer containing data to send
    */
-  struct Session *next;
+  char *buf;
 
   /**
-   * Pointer to the global plugin struct.
+   * amount of data already sent
    */
-  struct Plugin *plugin;
+  size_t pos;
+
+  /**
+   * amount of data to sent
+   */
+  size_t len;
+
+  char * dest_url;
 
   /**
    * Continuation function to call once the transmission buffer
@@ -86,6 +152,24 @@ struct Session
    * Closure for transmit_cont.
    */
   void *transmit_cont_cls;
+};
+
+
+/**
+ * Session handle for connections.
+ */
+struct Session
+{
+
+  /**
+   * Stored in a linked list.
+   */
+  struct Session *next;
+
+  /**
+   * Pointer to the global plugin struct.
+   */
+  struct Plugin *plugin;
 
   /**
    * To whom are we talking to (set to our identity
@@ -103,11 +187,26 @@ struct Session
    */
   char * ip;
 
+  /**
+   * Sender's ip address to distinguish between incoming connections
+   */
+  struct sockaddr_in * addr_inbound;
+
+  /**
+   * Sender's ip address recieved by transport
+   */
+  struct sockaddr_in * addr_outbound;
+
   /**
    * Did we initiate the connection (GNUNET_YES) or the other peer (GNUNET_NO)?
    */
   unsigned int is_client;
 
+  /**
+   * Is the connection active (GNUNET_YES) or terminated (GNUNET_NO)?
+   */
+  unsigned int is_active;
+
   /**
    * At what time did we reset last_received last?
    */
@@ -125,6 +224,26 @@ struct Session
    */
   uint32_t quota;
 
+  /**
+   * Is there a HTTP/PUT in progress?
+   */
+  unsigned int is_put_in_progress;
+
+  /**
+   * Is there a HTTP/PUT in progress?
+   */
+  unsigned int is_bad_request;
+
+  /**
+   * Encoded hash
+   */
+  struct GNUNET_CRYPTO_HashAsciiEncoded hash;
+
+  struct HTTP_Message * pending_outbound_msg;;
+
+  struct HTTP_Message * pending_inbound_msg;
+
+  CURL *curl_handle;
 };
 
 /**
@@ -137,11 +256,23 @@ struct Plugin
    */
   struct GNUNET_TRANSPORT_PluginEnvironment *env;
 
+  /**
+   * Handle to the network service.
+   */
+  struct GNUNET_SERVICE_Context *service;
+
+  unsigned int port_inbound;
+
   /**
    * List of open sessions.
    */
   struct Session *sessions;
 
+  /**
+   * Number of active sessions
+   */
+
+  unsigned int session_count;
 };
 
 /**
@@ -164,11 +295,35 @@ static GNUNET_SCHEDULER_TaskIdentifier http_task_v4;
  */
 static GNUNET_SCHEDULER_TaskIdentifier http_task_v6;
 
-struct Plugin *plugin;
 
+/**
+ * The task sending data
+ */
+static GNUNET_SCHEDULER_TaskIdentifier http_task_send;
+
+
+/**
+ * Information about this plugin
+ */
+static struct Plugin *plugin;
+
+/**
+ * cURL Multihandle
+ */
 static CURLM *multi_handle;
 
-static struct sockaddr  * current_ip;
+/**
+ * Our hostname
+ */
+static char * hostname;
+
+/**
+ * Our ASCII encoded, hashed peer identity
+ * This string is used to distinguish between connections and is added to the urls
+ */
+static struct GNUNET_CRYPTO_HashAsciiEncoded my_ascii_hash_ident;
+
+struct GNUNET_TIME_Relative timeout;
 
 /**
  * Finds a http session in our linked list using peer identity as a key
@@ -193,92 +348,100 @@ static struct Session * find_session_by_pi( const struct GNUNET_PeerIdentity *pe
   return NULL;
 }
 
-#if 0
 /**
- * Finds a http session in our linked list using peer identity as a key
- * @param peer peeridentity
+ * Finds a http session in our linked list using libcurl handle as a key
+ * Needed when sending data with libcurl to differentiate between sessions
+ * @param handle peeridentity
  * @return http session corresponding to peer identity
  */
-static struct Session * find_session_by_ip( struct sockaddr_in * addr )
+static struct Session * find_session_by_curlhandle( CURL* handle )
 {
-  /*
   struct Session * cur;
 
   cur = plugin->sessions;
   while (cur != NULL)
   {
-    hc_current = cur->sender.hashPubKey;
-    if ( 0 == GNUNET_CRYPTO_hash_cmp( &hc_peer, &hc_current))
+    if ( handle == cur->curl_handle )
       return cur;
     cur = plugin->sessions->next;
   }
-  */
   return NULL;
 }
-#endif
 
 /**
- * Creates a http session in our linked list by peer identity
- * Only peer is set here, all other  fields have to be set by calling method
- * @param peer peeridentity
- * @return created http session
+ * Create a new session
+ *
+ * @param address address the peer is using inbound
+ * @param address address the peer is using outbound
+ * @param peer identity
+ * @return created session object
  */
-static struct Session * create_session_by_pi( const struct GNUNET_PeerIdentity *peer )
+static struct Session * create_session (struct sockaddr_in *addr_in, struct sockaddr_in *addr_out, const struct GNUNET_PeerIdentity *peer)
 {
-  struct Session * cur;
-  struct Session * last_in_list;
-  /* Create a new session object */
-  cur = GNUNET_malloc (sizeof (struct Session));
-  memcpy( &(cur->sender), peer, sizeof( struct GNUNET_PeerIdentity ) );
+  struct sockaddr_in  *addrin;
+  struct sockaddr_in6 *addrin6;
+  struct Session * ses = GNUNET_malloc ( sizeof( struct Session) );
+
+  ses->addr_outbound = GNUNET_malloc ( sizeof (struct sockaddr_in) );
 
-  cur->next = NULL;
 
-  /* Insert into linked list */
-  last_in_list = plugin->sessions;
-  while (last_in_list->next != NULL)
+  ses->next = NULL;
+  ses->plugin = plugin;
+  if (NULL != addr_in)
   {
-    last_in_list = last_in_list->next;
+    ses->addr_inbound  = GNUNET_malloc ( sizeof (struct sockaddr_in) );
+    memcpy(ses->addr_inbound, addr_in, sizeof (struct sockaddr_in));
+    if ( AF_INET == addr_in->sin_family)
+    {
+      ses->ip = GNUNET_malloc (INET_ADDRSTRLEN);
+      addrin = addr_in;
+      inet_ntop(addrin->sin_family,&(addrin->sin_addr),ses->ip,INET_ADDRSTRLEN);
+    }
+    if ( AF_INET6 == addr_in->sin_family)
+    {
+      ses->ip = GNUNET_malloc (INET6_ADDRSTRLEN);
+      addrin6 = (struct sockaddr_in6 *) addr_in;
+      inet_ntop(addrin6->sin6_family, &(addrin6->sin6_addr) ,ses->ip,INET6_ADDRSTRLEN);
+    }
   }
-  last_in_list->next = cur;
-
-  return cur;
-}
-
-#if 0
-/**
- * Creates a http session in our linked list by ip address
- * Only ip is set here, all other fields have to be set by calling method
- * @param peer peeridentity
- * @return created http session
- */
-static struct Session * create_session_by_ip ( struct sockaddr_in * addr )
-{
-  struct Session * cur;
-  struct Session * last_in_list;
-  /* Create a new session object */
-  cur = GNUNET_malloc (sizeof (struct Session));
-  // FIXME: memcpy( &(cur->ip), , sizeof( struct GNUNET_PeerIdentity ) );
-
-  cur->next = NULL;
+  else
+    ses->addr_inbound = NULL;
 
-  /* Insert into linked list */
-  last_in_list = plugin->sessions;
-  while (last_in_list->next != NULL)
+  if (NULL != addr_out)
   {
-    last_in_list = last_in_list->next;
+    ses->addr_outbound  = GNUNET_malloc ( sizeof (struct sockaddr_in) );
+    memcpy(ses->addr_outbound, addr_out, sizeof (struct sockaddr_in));
   }
-  last_in_list->next = cur;
+  else
+    ses->addr_outbound = NULL;
 
-  return cur;
+  memcpy(&ses->sender, peer, sizeof (struct GNUNET_PeerIdentity));
+  GNUNET_CRYPTO_hash_to_enc(&ses->sender.hashPubKey,&(ses->hash));
+  ses->is_active = GNUNET_NO;
+  ses->pending_inbound_msg = GNUNET_malloc( sizeof (struct HTTP_Message));
+  ses->pending_inbound_msg->buf = GNUNET_malloc(GNUNET_SERVER_MAX_MESSAGE_SIZE);
+  ses->pending_inbound_msg->len = GNUNET_SERVER_MAX_MESSAGE_SIZE;
+  ses->pending_inbound_msg->pos = 0;
+
+
+  return ses;
 }
-#endif
 
 /**
  * Callback called by MHD when a connection is terminated
  */
 static void requestCompletedCallback (void *cls, struct MHD_Connection * connection, void **httpSessionCache)
 {
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection was terminated\n");
+  struct Session * cs;
+
+  cs = *httpSessionCache;
+  if (cs != NULL)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection from peer `%s' was terminated\n",GNUNET_i2s(&cs->sender));
+    /* session set to inactive */
+    cs->is_active = GNUNET_NO;
+    cs->is_put_in_progress = GNUNET_NO;
+  }
   return;
 }
 
@@ -289,34 +452,13 @@ static int
 acceptPolicyCallback (void *cls,
                       const struct sockaddr *addr, socklen_t addr_len)
 {
-  struct sockaddr_in  *addrin;
-  struct sockaddr_in6 *addrin6;
-  char * address;
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Address %p\n",current_ip);
-  if (addr->sa_family == AF_INET6)
-  {
-    address = GNUNET_malloc(INET6_ADDRSTRLEN);
-    addrin6 = (struct sockaddr_in6 *) addr;
-    inet_ntop(addrin6->sin6_family, &(addrin6->sin6_addr),address,INET6_ADDRSTRLEN);
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Incoming IPv6 connection from `%s'\n",address);
-    memcpy(current_ip,addr, sizeof (struct sockaddr));
-    //current_ip = addr;
-    GNUNET_free (address);
-  }
-  if (addr->sa_family == AF_INET)
-  {
-    address = GNUNET_malloc(INET_ADDRSTRLEN);
-    addrin = (struct sockaddr_in *) addr;
-    inet_ntop(addrin->sin_family, &(addrin->sin_addr),address,INET_ADDRSTRLEN);
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Incoming IPv4 connection from `%s'\n",address);
-    memcpy(current_ip,addr, sizeof (struct sockaddr));
-    GNUNET_free (address);
-  }
   /* Every connection is accepted, nothing more to do here */
   return MHD_YES;
 }
 
 
+int serror;
+
 /**
  * Process GET or PUT request received via MHD.  For
  * GET, queue response that will send back our pending
@@ -333,62 +475,217 @@ accessHandlerCallback (void *cls,
                        const char *upload_data,
                        size_t * upload_data_size, void **httpSessionCache)
 {
-  struct Session * http_session;
   struct MHD_Response *response;
-  http_session = *httpSessionCache;
-
-  struct sockaddr *addr;
+  struct Session * cs;
+  struct Session * cs_temp;
+  const union MHD_ConnectionInfo * conn_info;
   struct sockaddr_in  *addrin;
   struct sockaddr_in6 *addrin6;
-  char * address;
+  char * address = NULL;
+  struct GNUNET_PeerIdentity pi_in;
+  int res = GNUNET_NO;
+  struct GNUNET_MessageHeader *gn_msg;
+  int send_error_to_client;
 
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Address %p %u\n",current_ip,current_ip->sa_family);
-  if ( current_ip->sa_family == AF_INET)
-  {
-    address = GNUNET_malloc(INET_ADDRSTRLEN);
-    addrin = (struct sockaddr_in *) current_ip;
-    inet_ntop(addrin->sin_family, &(addrin->sin_addr),address,INET_ADDRSTRLEN);
-  }
-  if (current_ip->sa_family == AF_INET6)
-  {
-    address = GNUNET_malloc(INET6_ADDRSTRLEN);
-    addrin6 = (struct sockaddr_in6 *) current_ip;
-    inet_ntop(addrin6->sin6_family, &(addrin6->sin6_addr),address,INET6_ADDRSTRLEN);
-  }
-
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"HTTP Daemon has an incoming `%s' request from `%s'\n",method, address);
+  gn_msg = NULL;
+  send_error_to_client = GNUNET_NO;
 
-  /* Check if new or already known session */
-  if ( NULL == http_session )
+  if ( NULL == *httpSessionCache)
   {
-    /* Create a new session */
-
-    /* Insert session into linked list*/
+    /* check url for peer identity */
+    res = GNUNET_CRYPTO_hash_from_string ( &url[1], &(pi_in.hashPubKey));
+    if ( GNUNET_SYSERR == res )
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Peer has no valid ident\n");
+      response = MHD_create_response_from_data (strlen (HTTP_ERROR_RESPONSE),HTTP_ERROR_RESPONSE, MHD_NO, MHD_NO);
+      res = MHD_queue_response (session, MHD_HTTP_NOT_FOUND, response);
+      MHD_destroy_response (response);
+      return res;
+    }
 
+    conn_info = MHD_get_connection_info(session, MHD_CONNECTION_INFO_CLIENT_ADDRESS );
+    /* Incoming IPv4 connection */
+    if ( AF_INET == conn_info->client_addr->sin_family)
+    {
+      address = GNUNET_malloc (INET_ADDRSTRLEN);
+      addrin = conn_info->client_addr;
+      inet_ntop(addrin->sin_family, &(addrin->sin_addr),address,INET_ADDRSTRLEN);
+    }
+    /* Incoming IPv6 connection */
+    if ( AF_INET6 == conn_info->client_addr->sin_family)
+    {
+      address = GNUNET_malloc (INET6_ADDRSTRLEN);
+      addrin6 = (struct sockaddr_in6 *) conn_info->client_addr;
+      inet_ntop(addrin6->sin6_family, &(addrin6->sin6_addr),address,INET6_ADDRSTRLEN);
+    }
+    /* find existing session for address */
+    cs = NULL;
+    if (plugin->session_count > 0)
+    {
+      cs = plugin->sessions;
+      while ( NULL != cs)
+      {
+
+        /* Comparison based on ip address */
+        // res = (0 == memcmp(&(conn_info->client_addr->sin_addr),&(cs->addr->sin_addr), sizeof (struct in_addr))) ? GNUNET_YES : GNUNET_NO;
+
+        /* Comparison based on ip address, port number and address family */
+        // res = (0 == memcmp((conn_info->client_addr),(cs->addr), sizeof (struct sockaddr_in))) ? GNUNET_YES : GNUNET_NO;
+
+        /* Comparison based on PeerIdentity */
+        res = (0 == memcmp(&pi_in,&(cs->sender), sizeof (struct GNUNET_PeerIdentity))) ? GNUNET_YES : GNUNET_NO;
+
+        if ( GNUNET_YES  == res)
+        {
+          /* existing session for this address found */
+          GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Session `%s' found\n",address);
+          break;
+        }
+        cs = cs->next;
+      }
+    }
+    /* no existing session, create a new one*/
+    if (cs == NULL )
+    {
+      /* create new session object */
+      cs = create_session(conn_info->client_addr, NULL, &pi_in);
+
+      /* Insert session into linked list */
+      if ( plugin->sessions == NULL)
+      {
+        plugin->sessions = cs;
+        plugin->session_count = 1;
+      }
+      cs_temp = plugin->sessions;
+      while ( cs_temp->next != NULL )
+      {
+        cs_temp = cs_temp->next;
+      }
+      if (cs_temp != cs )
+      {
+        cs_temp->next = cs;
+        plugin->session_count++;
+      }
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"New Session `%s' inserted, count %u \n", address, plugin->session_count);
+    }
     /* Set closure */
+    if (*httpSessionCache == NULL)
+    {
+      *httpSessionCache = cs;
+    }
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"HTTP Daemon has new an incoming `%s' request from peer `%s' (`[%s]:%u')\n",method, GNUNET_i2s(&cs->sender),cs->ip,cs->addr_inbound->sin_port);
+  }
+  else
+  {
+    cs = *httpSessionCache;
   }
   /* Is it a PUT or a GET request */
   if ( 0 == strcmp (MHD_HTTP_METHOD_PUT, method) )
   {
-    /* PUT method here */
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Got PUT Request with size %lu \n",(*upload_data_size));
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"URL: `%s'\n",url);
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"PUT Request: `%s'\n",upload_data);
-    /* FIXME: GNUNET_STATISTICS_update( plugin->env->stats , gettext_noop("# PUT requests"), 1, GNUNET_NO); */
-    /* No data left */
-    *upload_data_size = 0;
-    response = MHD_create_response_from_data (strlen (HTTP_PUT_RESPONSE),HTTP_PUT_RESPONSE, MHD_NO, MHD_NO);
-    MHD_queue_response (session, MHD_HTTP_OK, response);
-    MHD_destroy_response (response);
+    /* New  */
+    if ((*upload_data_size == 0) && (cs->is_put_in_progress == GNUNET_NO))
+    {
+      if (cs->pending_inbound_msg->pos !=0 )
+      {
+        GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                    _("Incoming message from peer `%s', while existing message with %u bytes was not forwarded to transport'\n"),
+                    GNUNET_i2s(&cs->sender), cs->pending_inbound_msg->pos);
+        cs->pending_inbound_msg->pos = 0;
+      }
+      /* not yet ready */
+      cs->is_put_in_progress = GNUNET_YES;
+      cs->is_bad_request = GNUNET_NO;
+      cs->is_active = GNUNET_YES;
+      return MHD_YES;
+    }
+
+    if ((*upload_data_size > 0) && (cs->is_bad_request != GNUNET_YES))
+    {
+      if ((*upload_data_size + cs->pending_inbound_msg->pos < cs->pending_inbound_msg->len) && (*upload_data_size + cs->pending_inbound_msg->pos <= GNUNET_SERVER_MAX_MESSAGE_SIZE))
+      {
+        /* copy uploaded data to buffer */
+        memcpy(&cs->pending_inbound_msg->buf[cs->pending_inbound_msg->pos],upload_data,*upload_data_size);
+        cs->pending_inbound_msg->pos += *upload_data_size;
+        *upload_data_size = 0;
+        return MHD_YES;
+      }
+      else
+      {
+        GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"%u bytes not added to message of %u bytes, message to big\n",*upload_data_size, cs->pending_inbound_msg->pos);
+        cs->is_bad_request = GNUNET_YES;
+        /* (*upload_data_size) bytes not processed */
+        return MHD_YES;
+      }
+    }
+
+    if ((cs->is_put_in_progress == GNUNET_YES) && (cs->is_bad_request == GNUNET_YES))
+    {
+      *upload_data_size = 0;
+      response = MHD_create_response_from_data (strlen (HTTP_PUT_RESPONSE),HTTP_PUT_RESPONSE, MHD_NO, MHD_NO);
+      res = MHD_queue_response (session, MHD_HTTP_REQUEST_ENTITY_TOO_LARGE, response);
+      if (res == MHD_YES)
+      {
+        GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Sent HTTP/1.1: 413 ENTITY TOO LARGE as PUT Response\n");
+        cs->is_bad_request = GNUNET_NO;
+        cs->is_put_in_progress =GNUNET_NO;
+      }
+      MHD_destroy_response (response);
+      return MHD_YES;
+    }
+
+    if ((*upload_data_size == 0) && (cs->is_put_in_progress == GNUNET_YES) && (cs->is_bad_request == GNUNET_NO))
+    {
+      send_error_to_client = GNUNET_YES;
+      struct GNUNET_MessageHeader * gn_msg = NULL;
+      /*check message and forward here */
+      /* checking size */
+      if (cs->pending_inbound_msg->pos >= sizeof (struct GNUNET_MessageHeader))
+      {
+        gn_msg = GNUNET_malloc (cs->pending_inbound_msg->pos);
+        memcpy (gn_msg,cs->pending_inbound_msg->buf,cs->pending_inbound_msg->pos);
+
+        if ((ntohs(gn_msg->size) == cs->pending_inbound_msg->pos))
+        {
+          GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Recieved GNUnet message type %u size %u and payload %u \n",ntohs (gn_msg->type), ntohs (gn_msg->size), ntohs (gn_msg->size)-sizeof(struct GNUNET_MessageHeader));
+          /* forwarding message to transport */
+          plugin->env->receive(plugin->env, &(cs->sender), gn_msg, 1, cs , cs->ip, strlen(cs->ip) );
+          send_error_to_client = GNUNET_NO;
+        }
+      }
+
+      if (send_error_to_client == GNUNET_NO)
+      {
+        response = MHD_create_response_from_data (strlen (HTTP_PUT_RESPONSE),HTTP_PUT_RESPONSE, MHD_NO, MHD_NO);
+        res = MHD_queue_response (session, MHD_HTTP_OK, response);
+        GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Sent HTTP/1.1: 200 OK as PUT Response\n",HTTP_PUT_RESPONSE, strlen (HTTP_PUT_RESPONSE), res );
+        MHD_destroy_response (response);
+      }
+      else
+      {
+        GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Recieved malformed message with %u bytes\n", cs->pending_inbound_msg->pos);
+        response = MHD_create_response_from_data (strlen (HTTP_PUT_RESPONSE),HTTP_PUT_RESPONSE, MHD_NO, MHD_NO);
+        res = MHD_queue_response (session, MHD_HTTP_BAD_REQUEST, response);
+        MHD_destroy_response (response);
+        GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Sent HTTP/1.1: 400 BAD REQUEST as PUT Response\n");
+      }
+
+      GNUNET_free_non_null (gn_msg);
+      cs->is_put_in_progress = GNUNET_NO;
+      cs->is_bad_request = GNUNET_NO;
+      cs->pending_inbound_msg->pos = 0;
+      return res;
+    }
   }
   if ( 0 == strcmp (MHD_HTTP_METHOD_GET, method) )
   {
     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Got GET Request\n");
-    //GNUNET_STATISTICS_update( plugin->env->stats , gettext_noop("# GET requests"), 1, GNUNET_NO);
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"URL: `%s'\n",url);
+    response = MHD_create_response_from_data (strlen (HTTP_PUT_RESPONSE),HTTP_PUT_RESPONSE, MHD_NO, MHD_NO);
+    res = MHD_queue_response (session, MHD_HTTP_OK, response);
+    MHD_destroy_response (response);
+    return res;
   }
-
-  GNUNET_free (address);
-  return MHD_YES;
+  return MHD_NO;
 }
 
 
@@ -456,9 +753,8 @@ http_daemon_prepare (struct MHD_Daemon *daemon_handle)
  * Call MHD to process pending requests and then go back
  * and schedule the next run.
  */
-static void
-http_daemon_run (void *cls,
-            const struct GNUNET_SCHEDULER_TaskContext *tc)
+static void http_daemon_run (void *cls,
+                             const struct GNUNET_SCHEDULER_TaskContext *tc)
 {
   struct MHD_Daemon *daemon_handle = cls;
 
@@ -479,15 +775,335 @@ http_daemon_run (void *cls,
   return;
 }
 
-static size_t read_callback(void *ptr, size_t size, size_t nmemb, void *stream)
+/**
+ * Removes a message from the linked list of messages
+ * @param ses session to remove message from
+ * @param msg message to remove
+ * @return GNUNET_SYSERR if msg not found, GNUNET_OK on success
+ */
+
+static int remove_http_message(struct Session * ses, struct HTTP_Message * msg)
 {
-  size_t retcode;
+  struct HTTP_Message * cur;
+  struct HTTP_Message * next;
+
+  cur = ses->pending_outbound_msg;
+  next = NULL;
+
+  if (cur == NULL)
+    return GNUNET_SYSERR;
+
+  if (cur == msg)
+  {
+    ses->pending_outbound_msg = cur->next;
+    GNUNET_free (cur->buf);
+    GNUNET_free (cur->dest_url);
+    GNUNET_free (cur);
+    cur = NULL;
+    return GNUNET_OK;
+  }
+
+  while (cur->next!=msg)
+  {
+    if (cur->next != NULL)
+      cur = cur->next;
+    else
+      return GNUNET_SYSERR;
+  }
+
+  cur->next = cur->next->next;
+  GNUNET_free (cur->next->buf);
+  GNUNET_free (cur->next->dest_url);
+  GNUNET_free (cur->next);
+  cur->next = NULL;
+  return GNUNET_OK;
+}
+
+
+static size_t header_function( void *ptr, size_t size, size_t nmemb, void *stream)
+{
+  char * tmp;
+  unsigned int len = size * nmemb;
+
+  tmp = GNUNET_malloc (  len+1 );
+  memcpy(tmp,ptr,len);
+  if (tmp[len-2] == 13)
+    tmp[len-2]= '\0';
+#if DEBUG_CURL
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Header: `%s'\n",tmp);
+#endif
   /*
-  fprintf(stdout, "*** Read callback: size %u, size nmemb: %u \n", size, nmemb);
-  retcode = fread(ptr, size, nmemb, stream);
+  if (0==strcmp (tmp,"HTTP/1.1 100 Continue"))
+  {
+    res->http_result_code=100;
+  }
+  if (0==strcmp (tmp,"HTTP/1.1 200 OK"))
+  {
+    res->http_result_code=200;
+  }
+  if (0==strcmp (tmp,"HTTP/1.1 400 Bad Request"))
+  {
+    res->http_result_code=400;
+  }
+  if (0==strcmp (tmp,"HTTP/1.1 404 Not Found"))
+  {
+    res->http_result_code=404;
+  }
+  if (0==strcmp (tmp,"HTTP/1.1 413 Request entity too large"))
+  {
+    res->http_result_code=413;
+  }
    */
-  retcode = 0;
-  return retcode;
+  GNUNET_free (tmp);
+  return size * nmemb;
+}
+
+/**
+ * Callback method used with libcurl
+ * Method is called when libcurl needs to read data during sending
+ * @param stream pointer where to write data
+ * @param size size of an individual element
+ * @param nmemb count of elements that can be written to the buffer
+ * @param ptr source pointer, passed to the libcurl handle
+ * @return bytes written to stream
+ */
+static size_t send_read_callback(void *stream, size_t size, size_t nmemb, void *ptr)
+{
+  struct Session * ses = ptr;
+  struct HTTP_Message * msg = ses->pending_outbound_msg;
+  unsigned int bytes_sent;
+
+  bytes_sent = 0;
+  if (msg->len > (size * nmemb))
+    return CURL_READFUNC_ABORT;
+
+  if (( msg->pos < msg->len) && (msg->len < (size * nmemb)))
+  {
+    memcpy(stream, msg->buf, msg->len);
+    msg->pos = msg->len;
+    bytes_sent = msg->len;
+  }
+
+  return bytes_sent;
+}
+
+/**
+* Callback method used with libcurl
+* Method is called when libcurl needs to write data during sending
+* @param stream pointer where to write data
+* @param size size of an individual element
+* @param nmemb count of elements that can be written to the buffer
+* @param ptr destination pointer, passed to the libcurl handle
+* @return bytes read from stream
+*/
+static size_t send_write_callback( void *stream, size_t size, size_t nmemb, void *ptr)
+{
+  char * data = malloc(size*nmemb +1);
+
+  memcpy( data, stream, size*nmemb);
+  data[size*nmemb] = '\0';
+  /* Just a dummy print for the response recieved for the PUT message */
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Recieved %u bytes: `%s' \n", size * nmemb, data);
+  free (data);
+  return (size * nmemb);
+
+}
+
+/**
+ * Function setting up file descriptors and scheduling task to run
+ * @param session session to send data to
+ * @return bytes sent to peer
+ */
+static size_t send_prepare(struct Session* session );
+
+/**
+ * Function setting up curl handle and selecting message to send
+ * @param ses session to send data to
+ * @return bytes sent to peer
+ */
+static ssize_t send_select_init (struct Session* ses )
+{
+  int bytes_sent = 0;
+  CURLMcode mret;
+  struct HTTP_Message * msg;
+
+  if ( NULL == ses->curl_handle)
+    ses->curl_handle = curl_easy_init();
+  if( NULL == ses->curl_handle)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Getting cURL handle failed\n");
+    return -1;
+  }
+  msg = ses->pending_outbound_msg;
+
+
+
+#if DEBUG_CURL
+  curl_easy_setopt(ses->curl_handle, CURLOPT_VERBOSE, 1L);
+#endif
+  curl_easy_setopt(ses->curl_handle, CURLOPT_URL, msg->dest_url);
+  curl_easy_setopt(ses->curl_handle, CURLOPT_PUT, 1L);
+  curl_easy_setopt(ses->curl_handle, CURLOPT_HEADERFUNCTION, &header_function);
+  curl_easy_setopt(ses->curl_handle, CURLOPT_READFUNCTION, send_read_callback);
+  curl_easy_setopt(ses->curl_handle, CURLOPT_READDATA, ses);
+  curl_easy_setopt(ses->curl_handle, CURLOPT_WRITEFUNCTION, send_write_callback);
+  curl_easy_setopt(ses->curl_handle, CURLOPT_READDATA, ses);
+  curl_easy_setopt(ses->curl_handle, CURLOPT_INFILESIZE_LARGE, (curl_off_t) msg->len);
+  curl_easy_setopt(ses->curl_handle, CURLOPT_TIMEOUT, (long) (timeout.value / 1000 ));
+  curl_easy_setopt(ses->curl_handle, CURLOPT_CONNECTTIMEOUT, HTTP_CONNECT_TIMEOUT);
+
+  mret = curl_multi_add_handle(multi_handle, ses->curl_handle);
+  if (mret != CURLM_OK)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                _("%s failed at %s:%d: `%s'\n"),
+                "curl_multi_add_handle", __FILE__, __LINE__,
+                curl_multi_strerror (mret));
+    return -1;
+  }
+  bytes_sent = send_prepare (ses );
+  return bytes_sent;
+}
+
+static void send_execute (void *cls,
+             const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+  int running;
+  struct CURLMsg *msg;
+  CURLMcode mret;
+  struct Session * cs = NULL;
+
+  http_task_send = GNUNET_SCHEDULER_NO_TASK;
+  if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
+    return;
+
+  do
+    {
+      running = 0;
+      mret = curl_multi_perform (multi_handle, &running);
+      if (running == 0)
+        {
+          do
+            {
+
+              msg = curl_multi_info_read (multi_handle, &running);
+              GNUNET_break (msg != NULL);
+              if (msg == NULL)
+                break;
+              /* get session for affected curl handle */
+              GNUNET_assert ( msg->easy_handle != NULL );
+              cs = find_session_by_curlhandle (msg->easy_handle);
+              GNUNET_assert ( cs != NULL );
+              switch (msg->msg)
+                {
+
+                case CURLMSG_DONE:
+                  if ( (msg->data.result != CURLE_OK) &&
+                       (msg->data.result != CURLE_GOT_NOTHING) )
+                    {
+
+                    GNUNET_log(GNUNET_ERROR_TYPE_INFO,
+                               _("%s failed for `%s' at %s:%d: `%s'\n"),
+                               "curl_multi_perform",
+                               cs->ip,
+                               __FILE__,
+                               __LINE__,
+                               curl_easy_strerror (msg->data.result));
+                    /* sending msg failed*/
+                    if ( NULL != cs->pending_outbound_msg->transmit_cont)
+                      cs->pending_outbound_msg->transmit_cont (cs->pending_outbound_msg->transmit_cont_cls,&cs->sender,GNUNET_SYSERR);
+                    }
+                  else
+                  {
+                    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                                "Send to %s completed.\n", cs->ip);
+                    if (GNUNET_OK != remove_http_message(cs, cs->pending_outbound_msg))
+                      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Message could not be removed from session `%s'", GNUNET_i2s(&cs->sender));
+
+                    curl_easy_cleanup(cs->curl_handle);
+                    cs->curl_handle=NULL;
+
+                    /* Calling transmit continuation  */
+                    if ( NULL != cs->pending_outbound_msg->transmit_cont)
+                      cs->pending_outbound_msg->transmit_cont (cs->pending_outbound_msg->transmit_cont_cls,&cs->sender,GNUNET_OK);
+
+
+                    /* send pending messages */
+                    if (cs->pending_outbound_msg != NULL)
+                    {
+                      send_select_init (cs);
+                    }
+                  }
+                  return;
+                default:
+                  break;
+                }
+
+            }
+          while ( (running > 0) );
+        }
+    }
+  while (mret == CURLM_CALL_MULTI_PERFORM);
+  send_prepare(cls);
+}
+
+
+/**
+ * Function setting up file descriptors and scheduling task to run
+ * @param ses session to send data to
+ * @return bytes sent to peer
+ */
+static size_t send_prepare(struct Session* session )
+{
+  fd_set rs;
+  fd_set ws;
+  fd_set es;
+  int max;
+  struct GNUNET_NETWORK_FDSet *grs;
+  struct GNUNET_NETWORK_FDSet *gws;
+  long to;
+  CURLMcode mret;
+
+  max = -1;
+  FD_ZERO (&rs);
+  FD_ZERO (&ws);
+  FD_ZERO (&es);
+  mret = curl_multi_fdset (multi_handle, &rs, &ws, &es, &max);
+  if (mret != CURLM_OK)
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                  _("%s failed at %s:%d: `%s'\n"),
+                  "curl_multi_fdset", __FILE__, __LINE__,
+                  curl_multi_strerror (mret));
+      return -1;
+    }
+  mret = curl_multi_timeout (multi_handle, &to);
+  if (mret != CURLM_OK)
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                  _("%s failed at %s:%d: `%s'\n"),
+                  "curl_multi_timeout", __FILE__, __LINE__,
+                  curl_multi_strerror (mret));
+      return -1;
+    }
+
+  grs = GNUNET_NETWORK_fdset_create ();
+  gws = GNUNET_NETWORK_fdset_create ();
+  GNUNET_NETWORK_fdset_copy_native (grs, &rs, max + 1);
+  GNUNET_NETWORK_fdset_copy_native (gws, &ws, max + 1);
+  http_task_send = GNUNET_SCHEDULER_add_select (plugin->env->sched,
+                                   GNUNET_SCHEDULER_PRIORITY_DEFAULT,
+                                   GNUNET_SCHEDULER_NO_TASK,
+                                   GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 0),
+                                   grs,
+                                   gws,
+                                   &send_execute,
+                                   session);
+  GNUNET_NETWORK_fdset_destroy (gws);
+  GNUNET_NETWORK_fdset_destroy (grs);
+
+  /* FIXME: return bytes REALLY sent */
+  return 0;
 }
 
 /**
@@ -499,7 +1115,7 @@ static size_t read_callback(void *ptr, size_t size, size_t nmemb, void *stream)
  * @param priority how important is the message
  * @param msgbuf the message to transmit
  * @param msgbuf_size number of bytes in 'msgbuf'
- * @param timeout when should we time out
+ * @param to when should we time out
  * @param session which session must be used (or NULL for "any")
  * @param addr the address to use (can be NULL if the plugin
  *                is "on its own" (i.e. re-use existing TCP connection))
@@ -517,50 +1133,117 @@ static size_t read_callback(void *ptr, size_t size, size_t nmemb, void *stream)
  *         and does NOT mean that the message was not transmitted (DV)
  */
 static ssize_t
-template_plugin_send (void *cls,
-                      const struct GNUNET_PeerIdentity *
-                      target,
+http_plugin_send (void *cls,
+                      const struct GNUNET_PeerIdentity *target,
                       const char *msgbuf,
                       size_t msgbuf_size,
                       unsigned int priority,
-                      struct GNUNET_TIME_Relative timeout,
+                      struct GNUNET_TIME_Relative to,
                       struct Session *session,
                       const void *addr,
                       size_t addrlen,
                       int force_address,
-                      GNUNET_TRANSPORT_TransmitContinuation
-                      cont, void *cont_cls)
+                      GNUNET_TRANSPORT_TransmitContinuation cont,
+                      void *cont_cls)
 {
+  char * address;
   struct Session* ses;
+  struct Session* ses_temp;
+  struct HTTP_Message * msg;
+  struct HTTP_Message * tmp;
   int bytes_sent = 0;
-  /*  struct Plugin *plugin = cls; */
-  CURL *curl_handle;
-  /* CURLcode res; */
 
   /* find session for peer */
   ses = find_session_by_pi (target);
-  if ( ses == NULL) create_session_by_pi (target);
+  if (NULL != ses )
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Existing session for peer `%s' found\n", GNUNET_i2s(target));
+  if ( ses == NULL)
+  {
+    /* create new session object */
 
-  char *url = "http://localhost:12389";
+    /*FIXME: what is const void * really? Assuming struct sockaddr_in * ! */
+    ses = create_session(NULL, (struct sockaddr_in *) addr, target);
+    ses->is_active = GNUNET_YES;
 
-  curl_handle = curl_easy_init();
-  if( NULL == curl_handle)
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Getting cURL handle failed\n");
-    return -1;
+    /* Insert session into linked list */
+    if ( plugin->sessions == NULL)
+    {
+      plugin->sessions = ses;
+      plugin->session_count = 1;
+    }
+    ses_temp = plugin->sessions;
+    while ( ses_temp->next != NULL )
+    {
+      ses_temp = ses_temp->next;
+    }
+    if (ses_temp != ses )
+    {
+      ses_temp->next = ses;
+      plugin->session_count++;
+    }
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"New Session `%s' inserted, count %u \n", GNUNET_i2s(target), plugin->session_count);
   }
-  curl_easy_setopt(curl_handle, CURLOPT_VERBOSE, 1L);
-  curl_easy_setopt(curl_handle, CURLOPT_READFUNCTION, read_callback);
-  curl_easy_setopt(curl_handle, CURLOPT_UPLOAD, 1L);
-  curl_easy_setopt(curl_handle, CURLOPT_PUT, 1L);
-  curl_easy_setopt(curl_handle, CURLOPT_URL, url);
-  curl_easy_setopt(curl_handle, CURLOPT_READDATA, msgbuf);
-  curl_easy_setopt(curl_handle, CURLOPT_INFILESIZE_LARGE,
-                  (curl_off_t)msgbuf_size);
 
+  GNUNET_assert (addr!=NULL);
+  unsigned int port;
 
+  /* setting url to send to */
+  if (force_address == GNUNET_YES)
+  {
+    if (addrlen == (sizeof (struct IPv4HttpAddress)))
+    {
+      address = GNUNET_malloc(INET_ADDRSTRLEN + 14 + strlen ((const char *) (&ses->hash)));
+      inet_ntop(AF_INET,&((struct IPv4HttpAddress *) addr)->ipv4_addr,address,INET_ADDRSTRLEN);
+      port = ntohs(((struct IPv4HttpAddress *) addr)->u_port);
+      GNUNET_asprintf(&address,"http://%s:%u/%s",address,port, (char *) (&ses->hash));
+    }
+    else if (addrlen == (sizeof (struct IPv6HttpAddress)))
+    {
+      address = GNUNET_malloc(INET6_ADDRSTRLEN + 14 + strlen ((const char *) (&ses->hash)));
+      inet_ntop(AF_INET6, &((struct IPv6HttpAddress *) addr)->ipv6_addr,address,INET6_ADDRSTRLEN);
+      port = ntohs(((struct IPv6HttpAddress *) addr)->u6_port);
+      GNUNET_asprintf(&address,"http://%s:%u/%s",address,port,(char *) (&ses->hash));
+    }
+    else
+      {
+        GNUNET_break (0);
+        return -1;
+    }
+  }
 
-  return bytes_sent;
+  timeout = to;
+  /* setting up message */
+  msg = GNUNET_malloc (sizeof (struct HTTP_Message));
+  msg->next = NULL;
+  msg->len = msgbuf_size;
+  msg->pos = 0;
+  msg->buf = GNUNET_malloc (msgbuf_size);
+  msg->dest_url = address;
+  msg->transmit_cont = cont;
+  msg->transmit_cont_cls = cont_cls;
+  memcpy (msg->buf,msgbuf, msgbuf_size);
+
+  /* insert created message in list of pending messages */
+  if (ses->pending_outbound_msg == NULL)
+  {
+    ses->pending_outbound_msg = msg;
+  }
+  tmp = ses->pending_outbound_msg;
+  while ( NULL != tmp->next)
+  {
+    tmp = tmp->next;
+  }
+  if ( tmp != msg)
+  {
+    tmp->next = msg;
+  }
+
+  if (msg == ses->pending_outbound_msg)
+  {
+    bytes_sent = send_select_init (ses);
+    return bytes_sent;
+  }
+  return msgbuf_size;
 }
 
 
@@ -574,9 +1257,10 @@ template_plugin_send (void *cls,
  * @param target peer from which to disconnect
  */
 static void
-template_plugin_disconnect (void *cls,
+http_plugin_disconnect (void *cls,
                             const struct GNUNET_PeerIdentity *target)
 {
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"HTTP Plugin: http_plugin_disconnect\n");
   // struct Plugin *plugin = cls;
   // FIXME
 }
@@ -597,7 +1281,7 @@ template_plugin_disconnect (void *cls,
  * @param asc_cls closure for asc
  */
 static void
-template_plugin_address_pretty_printer (void *cls,
+http_plugin_address_pretty_printer (void *cls,
                                         const char *type,
                                         const void *addr,
                                         size_t addrlen,
@@ -606,7 +1290,42 @@ template_plugin_address_pretty_printer (void *cls,
                                         GNUNET_TRANSPORT_AddressStringCallback
                                         asc, void *asc_cls)
 {
-  asc (asc_cls, NULL);
+  const struct IPv4HttpAddress *t4;
+  const struct IPv6HttpAddress *t6;
+  struct sockaddr_in a4;
+  struct sockaddr_in6 a6;
+  char * address;
+  char * ret;
+  unsigned int port;
+
+  if (addrlen == sizeof (struct IPv6HttpAddress))
+    {
+      address = GNUNET_malloc (INET6_ADDRSTRLEN);
+      t6 = addr;
+      a6.sin6_addr = t6->ipv6_addr;
+      inet_ntop(AF_INET6, &(a6.sin6_addr),address,INET6_ADDRSTRLEN);
+      port = ntohs(t6->u6_port);
+    }
+  else if (addrlen == sizeof (struct IPv4HttpAddress))
+    {
+      address = GNUNET_malloc (INET_ADDRSTRLEN);
+      t4 = addr;
+      a4.sin_addr.s_addr =  t4->ipv4_addr;
+      inet_ntop(AF_INET, &(a4.sin_addr),address,INET_ADDRSTRLEN);
+      port = ntohs(t4->u_port);
+    }
+  else
+    {
+      /* invalid address */
+      GNUNET_break_op (0);
+      asc (asc_cls, NULL);
+      return;
+    }
+
+  ret = GNUNET_malloc(strlen(address) +14);
+  GNUNET_asprintf(&ret,"http://%s:%u/",address,port);
+  GNUNET_free (address);
+  asc (asc_cls, ret);
 }
 
 
@@ -624,13 +1343,46 @@ template_plugin_address_pretty_printer (void *cls,
  *         and transport
  */
 static int
-template_plugin_address_suggested (void *cls,
+http_plugin_address_suggested (void *cls,
                                   void *addr, size_t addrlen)
 {
-  /* struct Plugin *plugin = cls; */
+  struct IPv4HttpAddress *v4;
+  struct IPv6HttpAddress *v6;
+  unsigned int port;
 
-  /* check if the address is plausible; if so,
-     add it to our list! */
+  if ((addrlen != sizeof (struct IPv4HttpAddress)) &&
+      (addrlen != sizeof (struct IPv6HttpAddress)))
+    {
+      GNUNET_break_op (0);
+      return GNUNET_SYSERR;
+    }
+  if (addrlen == sizeof (struct IPv4HttpAddress))
+    {
+      v4 = (struct IPv4HttpAddress *) addr;
+
+      port = ntohs (v4->u_port);
+      if (port != plugin->port_inbound)
+      {
+        GNUNET_break_op (0);
+        return GNUNET_SYSERR;
+      }
+    }
+  else
+    {
+      v6 = (struct IPv6HttpAddress *) addr;
+      if (IN6_IS_ADDR_LINKLOCAL (&v6->ipv6_addr))
+        {
+          GNUNET_break_op (0);
+          return GNUNET_SYSERR;
+        }
+      port = ntohs (v6->u6_port);
+      if (port != plugin->port_inbound)
+      {
+        GNUNET_break_op (0);
+        return GNUNET_SYSERR;
+      }
+
+    }
   return GNUNET_OK;
 }
 
@@ -647,12 +1399,111 @@ template_plugin_address_suggested (void *cls,
  * @return string representing the same address
  */
 static const char*
-template_plugin_address_to_string (void *cls,
+http_plugin_address_to_string (void *cls,
                                    const void *addr,
                                    size_t addrlen)
 {
-  GNUNET_break (0);
-  return NULL;
+  const struct IPv4HttpAddress *t4;
+  const struct IPv6HttpAddress *t6;
+  struct sockaddr_in a4;
+  struct sockaddr_in6 a6;
+  char * address;
+  char * ret;
+  unsigned int port;
+
+  if (addrlen == sizeof (struct IPv6HttpAddress))
+    {
+      address = GNUNET_malloc (INET6_ADDRSTRLEN);
+      t6 = addr;
+      a6.sin6_addr = t6->ipv6_addr;
+      inet_ntop(AF_INET6, &(a6.sin6_addr),address,INET6_ADDRSTRLEN);
+      port = ntohs(t6->u6_port);
+    }
+  else if (addrlen == sizeof (struct IPv4HttpAddress))
+    {
+      address = GNUNET_malloc (INET_ADDRSTRLEN);
+      t4 = addr;
+      a4.sin_addr.s_addr =  t4->ipv4_addr;
+      inet_ntop(AF_INET, &(a4.sin_addr),address,INET_ADDRSTRLEN);
+      port = ntohs(t4->u_port);
+    }
+  else
+    {
+      /* invalid address */
+      return NULL;
+    }
+
+  ret = GNUNET_malloc(strlen(address) +6);
+  GNUNET_asprintf(&ret,"%s:%u",address,port);
+  GNUNET_free (address);
+  return ret;
+}
+
+/**
+ * Add the IP of our network interface to the list of
+ * our external IP addresses.
+ *
+ * @param cls the 'struct Plugin*'
+ * @param name name of the interface
+ * @param isDefault do we think this may be our default interface
+ * @param addr address of the interface
+ * @param addrlen number of bytes in addr
+ * @return GNUNET_OK to continue iterating
+ */
+static int
+process_interfaces (void *cls,
+                    const char *name,
+                    int isDefault,
+                    const struct sockaddr *addr, socklen_t addrlen)
+{
+  struct IPv4HttpAddress t4;
+  struct IPv6HttpAddress t6;
+  int af;
+  void *arg;
+  uint16_t args;
+
+
+
+  af = addr->sa_family;
+  if (af == AF_INET)
+    {
+      if (INADDR_LOOPBACK == ntohl(((struct sockaddr_in *) addr)->sin_addr.s_addr))
+      {
+        /* skip loopback addresses */
+        return GNUNET_OK;
+      }
+      t4.ipv4_addr = ((struct sockaddr_in *) addr)->sin_addr.s_addr;
+      t4.u_port = htons (plugin->port_inbound);
+      arg = &t4;
+      args = sizeof (t4);
+    }
+  else if (af == AF_INET6)
+    {
+      if (IN6_IS_ADDR_LINKLOCAL (&((struct sockaddr_in6 *) addr)->sin6_addr))
+        {
+          /* skip link local addresses */
+          return GNUNET_OK;
+        }
+      if (IN6_IS_ADDR_LOOPBACK (&((struct sockaddr_in6 *) addr)->sin6_addr))
+        {
+          /* skip loopback addresses */
+          return GNUNET_OK;
+        }
+      memcpy (&t6.ipv6_addr,
+              &((struct sockaddr_in6 *) addr)->sin6_addr,
+              sizeof (struct in6_addr));
+      t6.u6_port = htons (plugin->port_inbound);
+      arg = &t6;
+      args = sizeof (t6);
+    }
+  else
+    {
+      GNUNET_break (0);
+      return GNUNET_OK;
+    }
+  plugin->env->notify_address(plugin->env->cls,"http",arg, args, GNUNET_TIME_UNIT_FOREVER_REL);
+
+  return GNUNET_OK;
 }
 
 /**
@@ -663,6 +1514,9 @@ libgnunet_plugin_transport_http_done (void *cls)
 {
   struct GNUNET_TRANSPORT_PluginFunctions *api = cls;
   struct Plugin *plugin = api->cls;
+  struct Session * cs;
+  struct Session * cs_next;
+  CURLMcode mret;
 
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Unloading http plugin...\n");
 
@@ -678,6 +1532,12 @@ libgnunet_plugin_transport_http_done (void *cls)
     http_task_v6 = GNUNET_SCHEDULER_NO_TASK;
   }
 
+  if ( http_task_send != GNUNET_SCHEDULER_NO_TASK)
+  {
+    GNUNET_SCHEDULER_cancel(plugin->env->sched, http_task_send);
+    http_task_send = GNUNET_SCHEDULER_NO_TASK;
+  }
+
   if (http_daemon_v4 != NULL)
   {
     MHD_stop_daemon (http_daemon_v4);
@@ -689,9 +1549,44 @@ libgnunet_plugin_transport_http_done (void *cls)
     http_daemon_v6 = NULL;
   }
 
-  curl_multi_cleanup(multi_handle);
+  mret = curl_multi_cleanup(multi_handle);
+  if ( CURLM_OK != mret)
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"curl multihandle clean up failed");
+
+  /* free all sessions */
+  cs = plugin->sessions;
+
+  while ( NULL != cs)
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Freeing session to `%s'\n",cs->ip);
+
+      cs_next = cs->next;
+      /* freeing messages */
+      struct HTTP_Message *cur;
+      struct HTTP_Message *tmp;
+      cur = cs->pending_outbound_msg;
+
+      while (cur != NULL)
+      {
+         tmp = cur->next;
+         if (NULL != cur->buf)
+           GNUNET_free (cur->buf);
+         GNUNET_free (cur);
+         cur = tmp;
+      }
+      GNUNET_free (cs->pending_inbound_msg->buf);
+      GNUNET_free (cs->pending_inbound_msg);
+      GNUNET_free (cs->ip);
+      GNUNET_free (cs->addr_inbound);
+      GNUNET_free_non_null (cs->addr_outbound);
+      GNUNET_free (cs);
+      plugin->session_count--;
+      cs = cs_next;
+
+    }
 
-  GNUNET_free (current_ip);
+  /* GNUNET_SERVICE_stop (plugin->service); */
+  GNUNET_free (hostname);
   GNUNET_free (plugin);
   GNUNET_free (api);
   return NULL;
@@ -706,21 +1601,42 @@ libgnunet_plugin_transport_http_init (void *cls)
 {
   struct GNUNET_TRANSPORT_PluginEnvironment *env = cls;
   struct GNUNET_TRANSPORT_PluginFunctions *api;
-
+  struct GNUNET_SERVICE_Context *service;
+  unsigned int timeout;
+  struct GNUNET_TIME_Relative gn_timeout;
   long long unsigned int port;
 
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Starting http plugin...\n");
+
+  service = NULL;
+  /*
+  service = GNUNET_SERVICE_start ("transport-http", env->sched, env->cfg);
+  if (service == NULL)
+    {
+      GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, "", _
+                       ("Failed to start service for `%s' transport plugin.\n"),
+                       "http");
+      return NULL;
+    }
+    */
+
   plugin = GNUNET_malloc (sizeof (struct Plugin));
   plugin->env = env;
   plugin->sessions = NULL;
+  plugin->service = service;
   api = GNUNET_malloc (sizeof (struct GNUNET_TRANSPORT_PluginFunctions));
   api->cls = plugin;
-  api->send = &template_plugin_send;
-  api->disconnect = &template_plugin_disconnect;
-  api->address_pretty_printer = &template_plugin_address_pretty_printer;
-  api->check_address = &template_plugin_address_suggested;
-  api->address_to_string = &template_plugin_address_to_string;
+  api->send = &http_plugin_send;
+  api->disconnect = &http_plugin_disconnect;
+  api->address_pretty_printer = &http_plugin_address_pretty_printer;
+  api->check_address = &http_plugin_address_suggested;
+  api->address_to_string = &http_plugin_address_to_string;
+
+  hostname = GNUNET_RESOLVER_local_fqdn_get ();
+
+  /* Hashing our identity to use it in URLs */
+  GNUNET_CRYPTO_hash_to_enc ( &(plugin->env->my_identity->hashPubKey), &my_ascii_hash_ident);
 
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Starting http plugin...\n");
   /* Reading port number from config file */
   if ((GNUNET_OK !=
        GNUNET_CONFIGURATION_get_value_number (env->cfg,
@@ -737,33 +1653,34 @@ libgnunet_plugin_transport_http_init (void *cls)
       libgnunet_plugin_transport_http_done (api);
       return NULL;
     }
-
-  current_ip = GNUNET_malloc ( sizeof(struct sockaddr_in) );
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Address %p\n",current_ip);
+  plugin->port_inbound = port;
+  gn_timeout = GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT;
+  timeout = ( gn_timeout.value / 1000);
   if ((http_daemon_v4 == NULL) && (http_daemon_v6 == NULL) && (port != 0))
     {
     http_daemon_v6 = MHD_start_daemon (MHD_USE_IPv6,
                                        port,
                                        &acceptPolicyCallback,
-                                       current_ip, &accessHandlerCallback, current_ip,
+                                       NULL , &accessHandlerCallback, NULL,
                                        MHD_OPTION_CONNECTION_LIMIT, (unsigned int) 16,
                                        MHD_OPTION_PER_IP_CONNECTION_LIMIT, (unsigned int) 1,
-                                       MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 16,
+                                       MHD_OPTION_CONNECTION_TIMEOUT, timeout,
+                                       /* FIXME: set correct limit */
                                        MHD_OPTION_CONNECTION_MEMORY_LIMIT, (size_t) (16 * 1024),
                                        MHD_OPTION_NOTIFY_COMPLETED, &requestCompletedCallback, NULL,
                                        MHD_OPTION_END);
     http_daemon_v4 = MHD_start_daemon (MHD_NO_FLAG,
                                        port,
                                        &acceptPolicyCallback,
-                                       current_ip, &accessHandlerCallback, current_ip,
+                                       NULL , &accessHandlerCallback, NULL,
                                        MHD_OPTION_CONNECTION_LIMIT, (unsigned int) 16,
                                        MHD_OPTION_PER_IP_CONNECTION_LIMIT, (unsigned int) 1,
-                                       MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 16,
+                                       MHD_OPTION_CONNECTION_TIMEOUT, timeout,
+                                       /* FIXME: set correct limit */
                                        MHD_OPTION_CONNECTION_MEMORY_LIMIT, (size_t) (16 * 1024),
                                        MHD_OPTION_NOTIFY_COMPLETED, &requestCompletedCallback, NULL,
                                        MHD_OPTION_END);
     }
-
   if (http_daemon_v4 != NULL)
     http_task_v4 = http_daemon_prepare (http_daemon_v4);
   if (http_daemon_v6 != NULL)
@@ -774,8 +1691,20 @@ libgnunet_plugin_transport_http_init (void *cls)
   if (http_task_v6 != GNUNET_SCHEDULER_NO_TASK)
     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Starting MHD with IPv4 and IPv6 on port %u\n",port);
 
+
   /* Initializing cURL */
   multi_handle = curl_multi_init();
+  if ( NULL == multi_handle )
+  {
+    GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
+                     "http",
+                     _("Could not initialize curl multi handle, failed to start http plugin!\n"),
+                     "transport-http");
+    libgnunet_plugin_transport_http_done (api);
+    return NULL;
+  }
+
+  GNUNET_OS_network_interfaces_list (&process_interfaces, plugin);
 
   return api;
 }