Index: ChangeLog
===================================================================
--- ChangeLog	(revision 5715)
+++ ChangeLog	(revision 5721)
@@ -3,5 +3,5 @@
 	* Removed check for libuuid as it is no longer used.
 	* Fixed roard's 'dmx' driver.
-	* Added new plugins: dmx-random, dmx-waveform.
+	* Added new plugins: dmx-random, dmx-waveform, protocol-http.
 	* Added CPI support to roarpluginrunner.
 
Index: configure
===================================================================
--- configure	(revision 5707)
+++ configure	(revision 5721)
@@ -720,4 +720,5 @@
 test_lib_defmake ROAR_HAVE_LIBJACK       %            libjack       jack/t     -- jack/jack.h
 test_lib_defmake ROAR_HAVE_LIBDBUS       %            libdbus       dbus-1/t   -- dbus-1.0/dbus/dbus.h
+test_lib_defmake ROAR_HAVE_LIBUSTE       %            libuste       uste       -- uste.h
 test_netlibs
 
Index: plugins/universal/Makefile
===================================================================
--- plugins/universal/Makefile	(revision 5579)
+++ plugins/universal/Makefile	(revision 5721)
@@ -2,5 +2,5 @@
 include ../../Makefile.inc
 
-TARGETS_PROTO=protocol-echo$(SHARED_SUFFIX) protocol-daytime$(SHARED_SUFFIX) protocol-discard$(SHARED_SUFFIX) tic-tac-toe$(SHARED_SUFFIX)
+TARGETS_PROTO=protocol-echo$(SHARED_SUFFIX) protocol-daytime$(SHARED_SUFFIX) protocol-discard$(SHARED_SUFFIX) tic-tac-toe$(SHARED_SUFFIX) protocol-http$(SHARED_SUFFIX)
 TARGETS_MISC=plugin-info$(SHARED_SUFFIX) helloworld$(SHARED_SUFFIX)
 TARGETS=$(TARGETS_MISC) $(TARGETS_PROTO)
Index: plugins/universal/protocol-http.c
===================================================================
--- plugins/universal/protocol-http.c	(revision 5721)
+++ plugins/universal/protocol-http.c	(revision 5721)
@@ -0,0 +1,789 @@
+//protocol-http.c:
+
+/*
+ *      Copyright (C) Philipp 'ph3-der-loewe' Schafft - 2011-2012
+ *
+ *  This file is part of roard a part of RoarAudio,
+ *  a cross-platform sound system for both, home and professional use.
+ *  See README for details.
+ *
+ *  This file is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 3
+ *  as published by the Free Software Foundation.
+ *
+ *  RoarAudio 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 this software; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 51 Franklin Street, Fifth Floor,
+ *  Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include <roaraudio.h>
+
+struct resource_handle;
+
+enum ctl_cmd {
+ CMD_SET_CLIENTNAME
+};
+
+enum status {
+ STATUS_WAITING_FOR_HEADERS = 0,
+ STATUS_END_OF_HEADER,
+ STATUS_BAD_HEADER,
+ STATUS_RUNNING_DATA,
+ STATUS_DONE
+};
+
+struct input_source {
+ int userdata_si;
+ void * userdata_vp;
+ ssize_t offset;
+ struct roar_vio_calls * vio;
+ struct roar_vio_calls vio_store;
+};
+
+struct http_client {
+ enum status status;
+ struct roar_buffer * input_buffer;
+ char * method;
+ char * proto;
+ char * uri;
+ char * path, * query_string;
+ void * headersstore;
+ struct roar_keyval * headers;
+ ssize_t headerslen;
+ const struct resource_handle * resource;
+ struct input_source input;
+ struct roar_dl_librarypara * pluginpara;
+ int clientid;
+};
+
+struct resource_handle {
+ const char  * uri;
+ const char  * rawdata;
+ const ssize_t rawlen;
+ const char  * content_type;
+ int (*header_send)(struct http_client * self, struct roar_buffer ** obuffer);
+ int (*body_send)(struct http_client * self, struct roar_buffer ** obuffer);
+};
+
+static struct {
+ enum {
+  HOSTTYPE_GENERIC = 0,
+  HOSTTYPE_ROARD
+ } hosttype;
+ union {
+  struct {
+   int (*clients_set_name)(int id, const char * name);
+  } roard;
+ } hostspec;
+} __host = {.hosttype = HOSTTYPE_GENERIC};
+
+static int header_send(struct roar_buffer ** obuffer, int status, const char * msg, const char * content_type, ssize_t len);
+
+static int slow_zero_stream(struct http_client * self, struct roar_buffer ** obuffer) {
+ struct roar_buffer * buffer;
+ void * data;
+
+ self->status = STATUS_RUNNING_DATA;
+
+ if ( roar_buffer_new_data(&buffer, 1, &data) == -1 )
+  return -1;
+
+ *((char*)data) = 0;
+
+ if ( roar_buffer_moveintoqueue(obuffer, &buffer) == -1 ) {
+  roar_buffer_free(buffer);
+  return -1;
+ }
+
+ return 0;
+}
+
+static const char * __res_vio_content_type_get(struct http_client * self) {
+ const char * point = rindex(self->path, '.');
+
+ if ( point == NULL )
+  return NULL;
+
+ point++;
+
+ if ( !strcmp(point, "txt") || !strcmp(point, "text") ) {
+  return "text/plain";
+ } else if ( !strcmp(point, "htm") || !strcmp(point, "html") ) {
+  return "text/html";
+ } else if ( !strcmp(point, "css") ) {
+  return "text/css";
+ } else if ( !strcmp(point, "png") ) {
+  return "image/png";
+ } else if ( !strcmp(point, "jpg") || !strcmp(point, "jpeg") ) {
+  return "image/jpeg";
+ }
+
+ return NULL;
+}
+
+static int __res_vio_header_send(struct http_client * self, struct roar_buffer ** obuffer) {
+ struct roar_keyval * path;
+ char filename[1024];
+ const char * slash = "/";
+ const char * content_type = NULL;
+
+ if ( self->pluginpara == NULL ) {
+  roar_err_set(ROAR_ERROR_INVAL);
+  return -1;
+ }
+
+ path = roar_keyval_lookup(self->pluginpara->argv, "webroot", self->pluginpara->argc, 1);
+ if ( path == NULL ) {
+  if ( roar_error == ROAR_ERROR_NOENT )
+   roar_err_set(ROAR_ERROR_INVAL);
+  return -1;
+ }
+
+ if ( path->value == NULL ) {
+  roar_err_set(ROAR_ERROR_INVAL);
+  return -1;
+ }
+
+ if ( strstr(self->path, "..") != NULL || strstr(self->path, "#") != NULL ) {
+  roar_err_set(ROAR_ERROR_INVAL);
+  return -1;
+ }
+
+ if ( (roar_mm_strlen(path->value) + roar_mm_strlen(self->path) + 2) > sizeof(filename) ) {
+  roar_err_set(ROAR_ERROR_NAMETOOLONG);
+  return -1;
+ }
+
+ if ( self->path[0] == '/' || path->value[strlen(path->value)-1] == '/' )
+  slash = "";
+
+ snprintf(filename, sizeof(filename), "%s%s%s", path->value, slash, self->path);
+
+
+ self->input.vio = &(self->input.vio_store);
+ if ( roar_vio_open_dstr_simple(self->input.vio, filename, ROAR_VIOF_READ|ROAR_VIOF_NONBLOCK) == -1 ) {
+  return -1;
+ }
+
+ if ( roar_vio_ctl(self->input.vio, ROAR_VIO_CTL_GET_MIMETYPE, &content_type) == -1 )
+  content_type = NULL;
+
+ if ( content_type == NULL )
+  content_type = __res_vio_content_type_get(self);
+
+ header_send(obuffer, 200, NULL, content_type, -1);
+
+ self->status = STATUS_RUNNING_DATA;
+ return 0;
+}
+
+static int __res_vio_body_send(struct http_client * self, struct roar_buffer ** obuffer) {
+ const size_t len = 1024;
+ struct roar_buffer * buffer;
+ void * data;
+ ssize_t ret;
+
+ if ( roar_buffer_new_data(&buffer, len, &data) == -1 )
+  return -1;
+
+ ret = roar_vio_read(self->input.vio, data, len);
+
+ if ( ret == -1 && roar_error == ROAR_ERROR_AGAIN ) {
+  ret = 0;
+ } else if ( ret < 1 ) {
+  roar_buffer_free(buffer);
+  if ( roar_error != ROAR_ERROR_AGAIN ) {
+   self->status = STATUS_DONE;
+   roar_vio_close(self->input.vio);
+  }
+  return 0;
+ }
+
+ if ( roar_buffer_set_len(buffer, ret) == -1 ) {
+  roar_buffer_free(buffer);
+  return -1;
+ }
+
+ if ( roar_buffer_moveintoqueue(obuffer, &buffer) == -1 ) {
+  roar_buffer_free(buffer);
+  return -1;
+ }
+
+ return 0;
+}
+
+static const struct resource_handle _g_resources[] = {
+ {
+  .uri = "/test/*",
+  .rawdata = "Hello world!\n",
+  .rawlen  = 13,
+  .content_type = NULL,
+  .header_send = NULL,
+  .body_send = NULL
+ },
+ {
+  .uri = "/szs/*",
+  .rawdata = NULL,
+  .rawlen  = -1,
+  .content_type = NULL,
+  .header_send = NULL,
+  .body_send = slow_zero_stream
+ },
+ {
+  .uri = "*",
+  .rawdata = NULL,
+  .rawlen  = -1,
+  .content_type = NULL,
+  .header_send = __res_vio_header_send,
+  .body_send = __res_vio_body_send
+ }
+};
+
+
+static inline int __ret(struct http_client * self, struct roar_buffer ** obuffer) {
+ ROAR_DBG("__ret(self=%p, obuffer=%p): self->status=%i", self, obuffer, (int)self->status);
+ if ( *obuffer != NULL )
+  return 0;
+ if ( self->status == STATUS_DONE )
+  return -1;
+ return 0;
+}
+
+static int __ctl(struct http_client * self, enum ctl_cmd cmd, void * argp) {
+ switch (cmd) {
+  case CMD_SET_CLIENTNAME:
+    if ( argp == NULL ) {
+     roar_err_set(ROAR_ERROR_FAULT);
+     return -1;
+    }
+    ROAR_DBG("__ctl(self=%p, cmd=%i, argp='%s') = ?", self, (int)cmd, (const char *)argp);
+    switch (__host.hosttype) {
+     case HOSTTYPE_GENERIC:
+       roar_err_set(ROAR_ERROR_BADHOST);
+       return -1;
+      break;
+     case HOSTTYPE_ROARD:
+       __host.hostspec.roard.clients_set_name(self->clientid, argp);
+      break;
+    }
+   break;
+ }
+
+ roar_err_set(ROAR_ERROR_BADRQC);
+ return -1;
+}
+
+static void __init(struct roar_dl_librarypara * para) {
+ // TODO add some init code here.
+
+ if ( para == NULL )
+  return;
+
+ if ( !roar_dl_para_check_version(para, "roard <0/RoarAudio>", "1.0beta1") ) {
+  __host.hosttype = HOSTTYPE_ROARD;
+  __host.hostspec.roard.clients_set_name = roar_dl_getsym(ROAR_DL_HANDLE_APPLICATION, "clients_set_name", -1);
+
+  // check if *all* function have been found:
+  if ( __host.hostspec.roard.clients_set_name == NULL ) {
+   __host.hosttype = HOSTTYPE_GENERIC;
+  }
+ }
+}
+
+// this function is a wrapper for __init() with the code which should be inlined.
+static inline void _init(struct roar_dl_librarypara * para) {
+ static int inited = 0;
+ if (inited) return;
+ __init(para);
+ inited = 1;
+}
+
+static const struct resource_handle * resource_lookup_by_uri(const char * uri) {
+ size_t i;
+
+ for (i = 0; i < (sizeof(_g_resources)/sizeof(*_g_resources)); i++) {
+  if ( !roar_mm_strselcmp(_g_resources[i].uri, uri) ) {
+   return &(_g_resources[i]);
+  }
+ }
+
+ roar_err_set(ROAR_ERROR_NOENT);
+ return NULL;
+}
+
+static int resource_header_send(struct http_client * self, struct roar_buffer ** obuffer) {
+ if ( self->resource->header_send != NULL )
+  return self->resource->header_send(self, obuffer);
+
+ return header_send(obuffer, 200, NULL, self->resource->content_type, self->resource->rawlen);
+}
+
+static int resource_body_send(struct http_client * self, struct roar_buffer ** obuffer) {
+ struct roar_buffer * buffer;
+ void * data;
+ int ret;
+
+ if ( self->resource->body_send != NULL )
+  return self->resource->body_send(self, obuffer);
+
+ if ( self->resource->rawlen == -1 ) {
+  self->status = STATUS_DONE;
+  roar_err_set(ROAR_ERROR_INVAL);
+  return -1;
+ }
+
+ if ( roar_buffer_new_data(&buffer, self->resource->rawlen, &data) == -1 ) {
+  self->status = STATUS_DONE;
+  return -1;
+ }
+
+ memcpy(data, self->resource->rawdata, self->resource->rawlen);
+
+ ret = roar_buffer_moveintoqueue(obuffer, &buffer);
+ self->status = STATUS_DONE;
+
+ return ret;
+}
+
+static int header_parse(struct http_client * self) {
+ void * data;
+ size_t len;
+ size_t i;
+ char * start, * end;
+ char * start_uri;
+ char * start_headers, * end_headers = NULL;
+ char * tmp;
+ struct roar_keyval * c;
+
+ if ( roar_buffer_get_datalen(self->input_buffer, &data, &len) == -1 )
+  return -1;
+
+ ROAR_DBG("header_parse(self=%p): data='%*s'", self, (int)len, data);
+
+ if ( len < 4 )
+  return 0;
+
+ for (i = 0; i < (len - 3); i++) {
+  if ( ((const char*)data)[i] == '\r' ) {
+   ROAR_DBG("header_parse(self=%p): i=%i", self, (int)i);
+   if ( !strncmp(data + i, "\r\n\r\n", 4) ) {
+    self->status = STATUS_END_OF_HEADER;
+    end_headers = data + i;
+    break;
+   }
+  }
+ }
+
+ if ( self->status != STATUS_END_OF_HEADER ) {
+  for (i = 0; i < (len - 1); i++) {
+   if ( ((const char*)data)[i] == '\n' ) {
+    ROAR_DBG("header_parse(self=%p): i=%i", self, (int)i);
+    if ( !strncmp(data + i, "\n\n", 2) ) {
+     self->status = STATUS_END_OF_HEADER;
+     end_headers = data + i;
+     break;
+    }
+   }
+  }
+ }
+
+ if ( self->status != STATUS_END_OF_HEADER )
+  return 0;
+
+ *end_headers = 0;
+
+ end = strstr(data, " ");
+ if ( end == NULL ) {
+  self->status = STATUS_BAD_HEADER;
+  return 1;
+ }
+
+ *end = 0;
+ end++;
+ self->method = roar_mm_strdup(data);
+
+ start = end;
+
+ end = strstr(start, " ");
+ if ( end == NULL ) {
+  self->status = STATUS_BAD_HEADER;
+  return 1;
+ }
+
+ *end = 0;
+ end++;
+ self->uri = roar_mm_strdup(start);
+ start_uri = start;
+
+ start = end;
+
+ end = strstr(start, "\r");
+ if ( end == NULL )
+  end = strstr(start, "\n");
+ if ( end == NULL ) {
+  self->status = STATUS_BAD_HEADER;
+  return 1;
+ }
+
+ *end = 0;
+ end++;
+ self->proto = roar_mm_strdup(start);
+
+ start_headers = end;
+
+ start = start_uri;
+
+ end = strstr(start, "?");
+ if ( end == NULL ) {
+  self->path = roar_mm_strdup(start);
+ } else {
+  *end = 0;
+  end++;
+  self->path = roar_mm_strdup(start);
+  self->query_string = roar_mm_strdup(end);
+ }
+
+ self->headerslen = 0;
+
+ i = end_headers - start_headers + 1;
+ self->headersstore = roar_mm_memdup(start_headers, i);
+ if ( self->headersstore == NULL ) {
+  self->status = STATUS_BAD_HEADER; // TODO: FIXME: not the right error, but works. ;)
+  return -1;
+ }
+
+ start_headers = self->headersstore;
+ end_headers   = start_headers + i;
+
+ for (tmp = start_headers; tmp < end_headers;) {
+  for (; tmp < end_headers && (*tmp == '\r' || *tmp == '\n'); tmp++);
+  if ( tmp == end_headers )
+   break;
+  self->headerslen++;
+  for (; tmp < end_headers && !(*tmp == '\r' || *tmp == '\n'); tmp++);
+ }
+
+ ROAR_DBG("header_parse(self=%p): self->headerslen=%i", self, (int)self->headerslen);
+
+ self->headers = roar_mm_malloc(sizeof(struct roar_keyval)*(self->headerslen+1));
+ if ( self->headers == NULL ) {
+  self->status = STATUS_BAD_HEADER; // TODO: FIXME: not the right error, but works. ;)
+  return -1;
+ }
+ memset(self->headers, 0, sizeof(struct roar_keyval)*(self->headerslen+1));
+ c = self->headers;
+
+ for (tmp = start_headers; tmp < end_headers;) {
+  ROAR_DBG("header_parse(self=%p): tmp='%s'", self, tmp);
+  for (; tmp < end_headers && (*tmp == '\r' || *tmp == '\n'); tmp++) *tmp = '\0';
+  if ( tmp == end_headers )
+   break;
+  ROAR_DBG("header_parse(self=%p): tmp='%s'", self, tmp);
+  c->key = tmp;
+  ROAR_DBG("header_parse(self=%p): c->key='%s'", self, c->key);
+  for (; tmp < end_headers && !(*tmp == '\r' || *tmp == '\n' || *tmp == ':'); tmp++);
+  c->value = tmp;
+  for (; tmp < end_headers && !(*tmp == '\r' || *tmp == '\n'); tmp++);
+  c++;
+ }
+
+ c->key = NULL;
+ c->value = NULL;
+
+ for (i = 0; i < (size_t)self->headerslen; i++) {
+  c = &(self->headers[i]);
+  if ( c->value[0] == '\0' ) {
+   c->value = NULL;
+   continue;
+  }
+
+  c->value[0] = '\0';
+  c->value++;
+
+  for (; c->value[0] == ' '; c->value++);
+
+  if ( c->value[0] == '\0' )
+   c->value = NULL;
+ }
+
+ return 1;
+}
+
+static int header_send(struct roar_buffer ** obuffer, int status, const char * msg, const char * content_type, ssize_t len) {
+ struct roar_buffer * buffer;
+ size_t bufferlen = 1024;
+ void * data;
+ char buffer_len[64];
+
+ if ( roar_buffer_new_data(&buffer, bufferlen, &data) == -1 )
+  return -1;
+
+ if ( msg == NULL ) {
+  switch (status) {
+   case 200: msg = "OK"; break;
+   case 400: msg = "Bad Request"; break;
+   case 404: msg = "File not found"; break;
+   case 500: msg = "Internal server error"; break;
+   default:  msg = "<<<unknown status code>>>"; break;
+  }
+ }
+
+ if ( content_type == NULL )
+  content_type = "text/plain";
+
+ if ( len == (ssize_t)-1 ) {
+  buffer_len[0] = 0;
+ } else {
+  snprintf(buffer_len, sizeof(buffer_len), "Content-Length: %lu\r\n", (long unsigned int)len);
+ }
+
+/*
+Date: Sun, 29 Jul 2012 01:08:15 GMT
+Cache-Control: no-cache,no-store
+Content-Type: text/html; charset=%s
+*/
+ snprintf(data, bufferlen, "HTTP/1.0 %i %s\r\n"
+                           "Server: protocol-http (libroar plugin)\r\n"
+                           "Connection: close\r\n"
+                           "Content-Type: %s\r\n"
+                           "%s"
+                           "\r\n",
+                           status, msg, content_type, buffer_len
+                           );
+
+ if ( roar_buffer_set_len(buffer, roar_mm_strlen(data)) == -1 ) {
+  roar_buffer_free(buffer);
+  return -1;
+ }
+
+ if ( roar_buffer_moveintoqueue(obuffer, &buffer) == -1 ) {
+  roar_buffer_free(buffer);
+  return -1;
+ }
+
+ return 0;
+}
+
+static void send_errorpage(struct http_client * self, struct roar_buffer ** obuffer, int error, const char * msg) {
+ struct roar_buffer * buffer;
+ const size_t bufferlen = 1024;
+ void * data;
+ int httperror;
+
+ if ( roar_err_convert(&httperror, ROAR_ERROR_TYPE_HTTP, error, ROAR_ERROR_TYPE_ROARAUDIO) == -1 )
+  httperror = 500;
+
+ if ( msg == NULL )
+  msg = roar_error2str(error);
+
+ // send header and mark as done early so we can just return in case some of the later calls fail.
+ header_send(obuffer, httperror, msg, "text/html", -1);
+ self->status = STATUS_DONE;
+
+ if ( roar_buffer_new_data(&buffer, bufferlen, &data) == -1 )
+  return;
+
+ snprintf(data, bufferlen, "<html>\n"
+                           " <head><title>%i - %s</title></head>\n"
+                           " <body>\n"
+                           "  <h1>%i - %s</h1><hr>\n"
+                           " </body>\n"
+                           "</html>",
+                           httperror, msg, httperror, msg);
+
+ _LIBROAR_IGNORE_RET(roar_buffer_set_len(buffer, roar_mm_strlen(data)));
+
+ if ( roar_buffer_moveintoqueue(obuffer, &buffer) == -1 )
+  roar_buffer_free(buffer);
+}
+
+static void handle_client(struct http_client * self, struct roar_buffer ** obuffer) {
+ struct roar_keyval * kv;
+
+ // meta stuff:
+ // try to set client name.
+ kv = roar_keyval_lookup(self->headers, "user-agent", self->headerslen, 0);
+ if ( kv != NULL && kv->value != NULL ) {
+  __ctl(self, CMD_SET_CLIENTNAME, kv->value);
+ }
+
+ // first try lookup including query string. if nothing is found
+ // retry search with only resource path.
+ self->resource = resource_lookup_by_uri(self->uri);
+ if ( self->resource == NULL )
+  self->resource = resource_lookup_by_uri(self->path);
+
+ if ( self->resource == NULL ) {
+  send_errorpage(self, obuffer, roar_error, NULL);
+  return;
+ }
+
+ if ( resource_header_send(self, obuffer) == -1 ) {
+  send_errorpage(self, obuffer, roar_error, NULL);
+  return;
+ }
+
+ resource_body_send(self, obuffer);
+
+ //send_errorpage(self, obuffer, ROAR_ERROR_CAUSALITY, NULL);
+// send_errorpage(self, obuffer, roar_random_uint16() % ROAR_ERROR_BADLICENSE, NULL);
+ return;
+}
+
+
+static int _set_proto(int client, struct roar_vio_calls * vio, struct roar_buffer ** obuffer, void ** userdata, const struct roar_keyval * protopara, ssize_t protoparalen, struct roar_dl_librarypara * pluginpara) {
+ struct http_client * self = roar_mm_malloc(sizeof(struct http_client));
+
+ (void)vio, (void)protopara, (void)protoparalen;
+
+ if ( self == NULL )
+  return -1;
+
+ _init(pluginpara);
+
+ memset(self, 0, sizeof(*self));
+ self->status = STATUS_WAITING_FOR_HEADERS;
+ self->headerslen = (ssize_t)-1;
+ self->clientid = client;
+
+ if ( pluginpara != NULL ) {
+  roar_dl_para_ref(pluginpara);
+  self->pluginpara = pluginpara;
+ }
+
+ *userdata = self;
+
+ return __ret(self, obuffer);
+}
+
+static int _unset_proto(int client, struct roar_vio_calls * vio, struct roar_buffer ** obuffer, void ** userdata, const struct roar_keyval * protopara, ssize_t protoparalen, struct roar_dl_librarypara * pluginpara) {
+ struct http_client * self = *userdata;
+
+ (void)client, (void)vio, (void)obuffer, (void)protopara, (void)protoparalen, (void)pluginpara;
+
+ if ( self->input_buffer != NULL )
+  roar_buffer_free(self->input_buffer);
+
+ if ( self->method != NULL )
+  roar_mm_free(self->method);
+ if ( self->proto != NULL )
+  roar_mm_free(self->proto);
+ if ( self->uri != NULL )
+  roar_mm_free(self->uri);
+ if ( self->path != NULL )
+  roar_mm_free(self->path);
+ if ( self->query_string != NULL )
+  roar_mm_free(self->query_string);
+ if ( self->headersstore != NULL )
+  roar_mm_free(self->headersstore);
+ if ( self->headers != NULL )
+  roar_mm_free(self->headers);
+
+ if ( self->pluginpara != NULL )
+  roar_dl_para_unref(self->pluginpara);
+
+ roar_mm_free(self);
+ *userdata = NULL;
+
+ return 0;
+}
+
+#define _INCREMENT 256
+static int _handle(int client, struct roar_vio_calls * vio, struct roar_buffer ** obuffer, void ** userdata, const struct roar_keyval * protopara, ssize_t protoparalen, struct roar_dl_librarypara * pluginpara) {
+ struct http_client * self = *userdata;
+ void * data;
+ size_t oldlen;
+ ssize_t ret;
+
+ (void)client, (void)protopara, (void)protoparalen, (void)pluginpara;
+
+ if ( self->status != STATUS_WAITING_FOR_HEADERS )
+  return __ret(self, obuffer);
+
+ if ( self->input_buffer == NULL ) {
+  if ( roar_buffer_new_data(&(self->input_buffer), _INCREMENT, &data) == -1 )
+   return -1;
+  oldlen = 0;
+ } else {
+  if ( roar_buffer_get_len(self->input_buffer, &oldlen) == -1 )
+   return -1;
+  if ( roar_buffer_set_len(self->input_buffer, oldlen + _INCREMENT) == -1 )
+   return -1;
+  if ( roar_buffer_get_data(self->input_buffer, &data) == -1 )
+   return -1;
+ }
+
+ data += oldlen;
+
+ ret = roar_vio_read(vio, data, _INCREMENT);
+ if ( ret == (ssize_t)-1 ) {
+  // we can safely ignore return value here as we return error anyway.
+  _LIBROAR_IGNORE_RET(roar_buffer_set_len(self->input_buffer, oldlen));
+  return -1;
+ }
+
+ if ( roar_buffer_set_len(self->input_buffer, oldlen + ret) == -1 ) {
+  ROAR_WARN("_handle(*): Can not reset buffer length. BAD. Error was: %s", roar_errorstring);
+  return -1;
+ }
+
+ header_parse(self);
+
+ if ( self->status == STATUS_BAD_HEADER ) {
+  header_send(obuffer, 400, NULL, NULL, -1);
+  self->status = STATUS_DONE;
+ } else if ( self->status == STATUS_END_OF_HEADER ) {
+  handle_client(self, obuffer);
+ }
+
+ return __ret(self, obuffer);
+}
+
+// this is a dummy function only used to kill the client after all data has been flushed.
+static int _flushed(int client, struct roar_vio_calls * vio, struct roar_buffer ** obuffer, void ** userdata, const struct roar_keyval * protopara, ssize_t protoparalen, struct roar_dl_librarypara * pluginpara) {
+ struct http_client * self = *userdata;
+
+ (void)client, (void)vio, (void)protopara, (void)protoparalen, (void)pluginpara;
+
+ if ( self->status == STATUS_RUNNING_DATA )
+  resource_body_send(self, obuffer);
+
+ return __ret(self, obuffer);
+}
+
+static const struct roar_dl_proto proto = {
+ .proto = ROAR_PROTO_HTTP,
+ .description = "Hyper Text Transfer Protocol",
+ .flags = ROAR_DL_PROTO_FLAGS_NONE,
+ .set_proto = _set_proto,
+ .unset_proto = _unset_proto,
+ .handle = _handle,
+ .flush = NULL,
+ .flushed = _flushed,
+ .status = NULL
+};
+
+static int __reg_proto(struct roar_dl_librarypara * para, struct roar_dl_libraryinst * lib) {
+ (void)para, (void)lib;
+ ROAR_DL_PLUGIN_REG_FN(ROAR_DL_PROTO_SUBTYPE, proto, ROAR_DL_PROTO_VERSION);
+ return 0;
+}
+
+ROAR_DL_PLUGIN_START(protocol_http) {
+ ROAR_DL_PLUGIN_META_PRODUCT_NIV("protocol-http", ROAR_VID_ROARAUDIO, ROAR_VNAME_ROARAUDIO);
+ ROAR_DL_PLUGIN_META_VERSION(ROAR_VERSION_STRING);
+ ROAR_DL_PLUGIN_META_LICENSE_TAG(GPLv3_0);
+ ROAR_DL_PLUGIN_META_CONTACT_FLNE("Philipp", "Schafft", "ph3-der-loewe", "lion@lion.leolix.org");
+ ROAR_DL_PLUGIN_META_DESC("Implementation of the HTTP Protocol");
+
+ ROAR_DL_PLUGIN_REG(ROAR_DL_FN_PROTO, __reg_proto);
+} ROAR_DL_PLUGIN_END
+
+//ll
