//protocol-http.c: /* * Copyright (C) Philipp 'ph3-der-loewe' Schafft - 2011-2014 * * 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 #ifdef ROAR_HAVE_LIBUSTE #include #endif 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 void send_errorpage(struct http_client * self, struct roar_buffer ** obuffer, int error, const char * msg); 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 * indexfile) { const char * point = strrchr(indexfile != NULL ? indexfile : self->path, '.'); if ( point == NULL ) return NULL; point++; #define __type1(mime,ext) \ } else if ( !strcmp(point, (ext)) ) { \ return (mime); #define __type2(mime,ext0,ext1) \ } else if ( !strcmp(point, (ext0)) || !strcmp(point, (ext1)) ) { \ return (mime); #ifdef __FIX_VIM_SYNTAX_HIGHLIGHTING__ } #endif if ( !strcmp(point, "txt") || !strcmp(point, "text") ) { return "text/plain"; __type2("text/html", "html", "htm") __type1("text/css", "css") __type1("image/png", "png") __type2("image/jpeg", "jpeg", "jpg") } return NULL; } #ifdef ROAR_HAVE_LIBUSTE static uste_var_t __res_vio_handle_uste_build_headers(struct http_client * self) { uste_var_t root = uste_var_new_kv(); uste_var_t var; ssize_t i; if ( root == NULL ) return NULL; uste_var_set_key(root, "header"); for (i = 0; i < self->headerslen; i++) { var = uste_var_new_str(self->headers[i].value == NULL ? "" : self->headers[i].value); if ( self->headers[i].key != NULL ) uste_var_set_key(var, self->headers[i].key); uste_var_push(root, var); uste_var_unref(var); } return root; } static inline int __is_hex(char c) { if ( c >= '0' && c <= '9' ) return 1; if ( c >= 'a' && c <= 'f' ) return 1; if ( c >= 'A' && c <= 'F' ) return 1; return 0; } static inline int __hex2num(char c) { if ( c >= '0' && c <= '9' ) return c - '0'; if ( c >= 'a' && c <= 'f' ) return c - 'a' + 10; if ( c >= 'A' && c <= 'F' ) return c - 'A' + 10; return 0; } static void __res_vio_handle_uste_uridecode(char * str) { const char * c = str; for (; *c; c++, str++) { if ( *c == '%' && __is_hex(c[1]) && __is_hex(c[2]) ) { *str = __hex2num(c[1])*16 + __hex2num(c[2]); c += 2; } else if ( *c == '+' ) { *str = ' '; } else { *str = *c; } } *str = *c; } static void __res_vio_handle_uste_keyify(char * str) { if ( *str == '#' ) *str = '_'; for (;*str; str++) if (*str == '.') *str = '_'; } static uste_var_t __res_vio_handle_uste_build_getparams(struct http_client * self) { uste_var_t root = uste_var_new_kv(); uste_var_t subroot; uste_var_t var; char * qs_buffer; char * cur; char * state; char * key, * value; if ( root == NULL ) return NULL; uste_var_set_key(root, "get"); if ( self->query_string == NULL ) return root; qs_buffer = roar_mm_strdup(self->query_string); // do better handling here. if ( qs_buffer == NULL ) return root; cur = roar_mm_strtok_r(qs_buffer, "&", &state); while (cur != NULL) { key = cur; value = strstr(cur, "="); if ( value != NULL ) { *value = 0; value++; __res_vio_handle_uste_uridecode(value); } __res_vio_handle_uste_uridecode(key); __res_vio_handle_uste_keyify(key); if ( value == NULL ) { var = uste_var_new_undef(); } else { var = uste_var_new_str(value); } subroot = uste_var_pull(root, key); if ( subroot == NULL ) { subroot = uste_var_new_array(); if ( subroot != NULL ) { uste_var_set_key(subroot, key); uste_var_push(root, subroot); } } if ( subroot != NULL ) { uste_var_push(subroot, var); uste_var_unref(subroot); } uste_var_unref(var); cur = roar_mm_strtok_r(NULL, "&", &state); } roar_mm_free(qs_buffer); return root; } static uste_var_t __res_vio_handle_uste_build_rootkv(struct http_client * self) { uste_var_t root = uste_var_new_kv(); uste_var_t subroot, subsubroot; uste_var_t var; if ( root == NULL ) return NULL; #define __new_member_prefix(prefix,name) \ if ( prefix->name != NULL ) { \ var = uste_var_new_str(prefix->name); \ uste_var_set_key(var, #name); \ uste_var_push(subsubroot, var); \ uste_var_unref(var); \ } #define __new_member(name) __new_member_prefix(self,name) subroot = uste_var_new_kv(); if ( subroot != NULL ) { uste_var_set_key(subroot, "__client__"); uste_var_push(root, subroot); var = uste_var_new_int(self->clientid); uste_var_set_key(var, "clientid"); uste_var_push(subroot, var); uste_var_unref(var); subsubroot = uste_var_new_kv(); if ( subsubroot != NULL ) { uste_var_set_key(subsubroot, "http"); uste_var_push(subroot, subsubroot); __new_member(method); __new_member(proto); __new_member(uri); __new_member(path); __new_member(query_string); var = __res_vio_handle_uste_build_headers(self); uste_var_push(subsubroot, var); uste_var_unref(var); var = __res_vio_handle_uste_build_getparams(self); uste_var_push(subsubroot, var); uste_var_unref(var); uste_var_unref(subsubroot); } uste_var_unref(subroot); } subroot = uste_var_new_kv(); if ( subroot != NULL ) { uste_var_set_key(subroot, "__host__"); uste_var_push(root, subroot); subsubroot = subroot; __new_member_prefix(self->pluginpara, appname); __new_member_prefix(self->pluginpara, abiversion); uste_var_unref(subroot); } return root; } static void __res_vio_handle_uste(struct http_client * self, struct roar_buffer ** obuffer, const char * content_type, const char * filename) { struct roar_buffer * buffer = NULL; uste_renderer_t renderer; uste_parser_t parser; uste_var_t rootkv; uste_node_t root; int err; parser = uste_parser_new(self->input.vio, filename); err = roar_error; roar_vio_close(self->input.vio); if ( parser == NULL ) { send_errorpage(self, obuffer, err, NULL); return; } if ( uste_parser_parse(parser) == -1 ) { send_errorpage(self, obuffer, roar_error, NULL); return; } root = uste_parser_get_rootnode(parser); err = roar_error; uste_parser_unref(parser); if ( root == NULL ) { send_errorpage(self, obuffer, err, NULL); return; } renderer = uste_renderer_new(); err = roar_error; if ( renderer == NULL ) { uste_node_unref(root); send_errorpage(self, obuffer, err, NULL); return; } uste_stdfunc_register_all(renderer); uste_libroar_register_all(renderer); rootkv = __res_vio_handle_uste_build_rootkv(self); err = roar_error; if ( rootkv == NULL ) { uste_node_unref(root); uste_renderer_unref(renderer); send_errorpage(self, obuffer, err, NULL); return; } uste_renderer_set_rootvar(renderer, rootkv); uste_var_unref(rootkv); buffer = uste_node_render(root, renderer); err = roar_error; uste_node_unref(root); uste_renderer_unref(renderer); if ( buffer == NULL ) { send_errorpage(self, obuffer, err, NULL); return; } header_send(obuffer, 200, NULL, content_type, -1); if ( buffer != NULL ) { if ( roar_buffer_moveintoqueue(obuffer, &buffer) == -1 ) { roar_buffer_free(buffer); return; } } self->status = STATUS_DONE; } #endif static int __res_vio_check_uste(struct http_client * self, struct roar_buffer ** obuffer, const char * content_type) { const size_t len = 14; struct roar_buffer * buffer; void * data; ssize_t ret; int err; // TODO: use content type for some basic filtering. (void)content_type; *obuffer = NULL; if ( roar_buffer_new_data(&buffer, len, &data) == -1 ) return -1; ret = roar_vio_read(self->input.vio, data, len); err = roar_error; if ( ret < 1 ) { roar_buffer_free(buffer); roar_error = err; return -1; } *obuffer = buffer; if ( ret == (ssize_t)len ) { return !strncmp(data, "@@@TEMPLATE@@@", len); } else { if ( roar_buffer_set_len(buffer, ret) == -1 ) { *obuffer = NULL; roar_buffer_free(buffer); return -1; } // is this really a nice idea? return 0; } } static int __res_vio_header_send(struct http_client * self, struct roar_buffer ** obuffer) { static const char * index_files[] = {NULL, "index.html", "index.txt", "index"}; struct roar_buffer * buffer; struct roar_keyval * path; char filename[1024]; const char * slash = "/"; const char * content_type = NULL; int uste_check; size_t i; const char * indexfile = 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 = ""; uste_check = -1; for (i = 0; uste_check == -1 && i < (sizeof(index_files)/sizeof(*index_files)); i++) { indexfile = index_files[i]; snprintf(filename, sizeof(filename), "%s%s%s%s%s", path->value, slash, self->path, indexfile == NULL ? "" : "/", indexfile == NULL ? "" : indexfile); self->input.vio = &(self->input.vio_store); if ( roar_vio_open_dstr_simple(self->input.vio, filename, ROAR_VIOF_READ|ROAR_VIOF_NONBLOCK) == -1 ) { continue; } 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, indexfile); uste_check = __res_vio_check_uste(self, &buffer, content_type); if ( uste_check == -1 ) roar_vio_unref(self->input.vio); } if ( uste_check == -1 ) return -1; if ( uste_check == 1 ) { if ( buffer != NULL ) roar_buffer_free(buffer); #ifdef ROAR_HAVE_LIBUSTE __res_vio_handle_uste(self, obuffer, content_type, filename); return 0; #else roar_vio_close(self->input.vio); send_errorpage(self, obuffer, ROAR_ERROR_NOSYS, NULL); return 0; #endif } header_send(obuffer, 200, NULL, content_type, -1); if ( buffer != NULL ) { if ( roar_buffer_moveintoqueue(obuffer, &buffer) == -1 ) { roar_buffer_free(buffer); return -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 ( self->status == STATUS_DONE ) return 0; 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{%p}): self->status=%i", self, obuffer, *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.0beta8") ) { __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, (const char*)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 = "<<>>"; 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, "\n" " %i - %s\n" " \n" "

%i - %s


\n" " \n" "", 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; ROAR_DBG("_flushed(*) = ?"); 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