| head 1.23; |
| access; |
| symbols |
| libshout-2_0:1.23 |
| libshout-2_0b3:1.23 |
| libshout-2_0b2:1.22 |
| libshout_2_0b1:1.22 |
| libogg2-zerocopy:1.8.0.2 |
| start:1.1.1.1 |
| xiph:1.1.1; |
| locks; strict; |
| comment @ * @; |
| |
| |
| 1.23 |
| date 2003.07.07.01.49.27; author brendan; state Exp; |
| branches; |
| next 1.22; |
| |
| 1.22 |
| date 2003.06.18.15.52.25; author karl; state Exp; |
| branches; |
| next 1.21; |
| |
| 1.21 |
| date 2003.06.18.11.13.11; author karl; state Exp; |
| branches; |
| next 1.20; |
| |
| 1.20 |
| date 2003.06.09.22.30.09; author brendan; state Exp; |
| branches; |
| next 1.19; |
| |
| 1.19 |
| date 2003.06.05.17.09.12; author brendan; state Exp; |
| branches; |
| next 1.18; |
| |
| 1.18 |
| date 2003.03.15.02.10.18; author msmith; state Exp; |
| branches; |
| next 1.17; |
| |
| 1.17 |
| date 2003.03.09.22.56.46; author karl; state Exp; |
| branches; |
| next 1.16; |
| |
| 1.16 |
| date 2003.03.08.16.05.38; author karl; state Exp; |
| branches; |
| next 1.15; |
| |
| 1.15 |
| date 2003.03.08.05.27.17; author msmith; state Exp; |
| branches; |
| next 1.14; |
| |
| 1.14 |
| date 2003.03.08.04.57.02; author msmith; state Exp; |
| branches; |
| next 1.13; |
| |
| 1.13 |
| date 2003.03.06.01.55.20; author brendan; state Exp; |
| branches; |
| next 1.12; |
| |
| 1.12 |
| date 2003.01.17.09.01.04; author msmith; state Exp; |
| branches; |
| next 1.11; |
| |
| 1.11 |
| date 2003.01.16.05.48.31; author brendan; state Exp; |
| branches; |
| next 1.10; |
| |
| 1.10 |
| date 2003.01.15.23.46.56; author brendan; state Exp; |
| branches; |
| next 1.9; |
| |
| 1.9 |
| date 2002.12.31.06.28.39; author msmith; state Exp; |
| branches; |
| next 1.8; |
| |
| 1.8 |
| date 2002.08.16.14.22.44; author msmith; state Exp; |
| branches; |
| next 1.7; |
| |
| 1.7 |
| date 2002.08.05.14.48.03; author msmith; state Exp; |
| branches; |
| next 1.6; |
| |
| 1.6 |
| date 2002.05.03.15.04.56; author msmith; state Exp; |
| branches; |
| next 1.5; |
| |
| 1.5 |
| date 2002.04.05.09.28.25; author msmith; state Exp; |
| branches; |
| next 1.4; |
| |
| 1.4 |
| date 2002.02.11.09.11.18; author msmith; state Exp; |
| branches; |
| next 1.3; |
| |
| 1.3 |
| date 2001.10.20.07.40.09; author jack; state Exp; |
| branches; |
| next 1.2; |
| |
| 1.2 |
| date 2001.10.20.04.41.54; author jack; state Exp; |
| branches; |
| next 1.1; |
| |
| 1.1 |
| date 2001.09.10.02.28.47; author jack; state Exp; |
| branches |
| 1.1.1.1; |
| next ; |
| |
| 1.1.1.1 |
| date 2001.09.10.02.28.47; author jack; state Exp; |
| branches; |
| next ; |
| |
| |
| desc |
| @@ |
| |
| |
| 1.23 |
| log |
| @httpp goes through the rinse cycle |
| @ |
| text |
| @/* Httpp.c |
| ** |
| ** http parsing engine |
| */ |
| |
| #ifdef HAVE_CONFIG_H |
| #include <config.h> |
| #endif |
| |
| #include <stdio.h> |
| |
| #include <stdlib.h> |
| #include <string.h> |
| #include <ctype.h> |
| #ifdef HAVE_STRINGS_H |
| #include <strings.h> |
| #endif |
| |
| #include <avl/avl.h> |
| #include "httpp.h" |
| |
| #ifdef _WIN32 |
| #define strcasecmp stricmp |
| #endif |
| |
| #define MAX_HEADERS 32 |
| |
| /* internal functions */ |
| |
| /* misc */ |
| static char *_lowercase(char *str); |
| |
| /* for avl trees */ |
| static int _compare_vars(void *compare_arg, void *a, void *b); |
| static int _free_vars(void *key); |
| |
| http_parser_t *httpp_create_parser(void) |
| { |
| return (http_parser_t *)malloc(sizeof(http_parser_t)); |
| } |
| |
| void httpp_initialize(http_parser_t *parser, http_varlist_t *defaults) |
| { |
| http_varlist_t *list; |
| |
| parser->req_type = httpp_req_none; |
| parser->uri = NULL; |
| parser->vars = avl_tree_new(_compare_vars, NULL); |
| parser->queryvars = avl_tree_new(_compare_vars, NULL); |
| |
| /* now insert the default variables */ |
| list = defaults; |
| while (list != NULL) { |
| httpp_setvar(parser, list->var.name, list->var.value); |
| list = list->next; |
| } |
| } |
| |
| static int split_headers(char *data, unsigned long len, char **line) |
| { |
| /* first we count how many lines there are |
| ** and set up the line[] array |
| */ |
| int lines = 0; |
| unsigned long i; |
| line[lines] = data; |
| for (i = 0; i < len && lines < MAX_HEADERS; i++) { |
| if (data[i] == '\r') |
| data[i] = '\0'; |
| if (data[i] == '\n') { |
| lines++; |
| data[i] = '\0'; |
| if (i + 1 < len) { |
| if (data[i + 1] == '\n' || data[i + 1] == '\r') |
| break; |
| line[lines] = &data[i + 1]; |
| } |
| } |
| } |
| |
| i++; |
| while (data[i] == '\n') i++; |
| |
| return lines; |
| } |
| |
| static void parse_headers(http_parser_t *parser, char **line, int lines) |
| { |
| int i,l; |
| int whitespace, where, slen; |
| char *name = NULL; |
| char *value = NULL; |
| |
| /* parse the name: value lines. */ |
| for (l = 1; l < lines; l++) { |
| where = 0; |
| whitespace = 0; |
| name = line[l]; |
| value = NULL; |
| slen = strlen(line[l]); |
| for (i = 0; i < slen; i++) { |
| if (line[l][i] == ':') { |
| whitespace = 1; |
| line[l][i] = '\0'; |
| } else { |
| if (whitespace) { |
| whitespace = 0; |
| while (i < slen && line[l][i] == ' ') |
| i++; |
| |
| if (i < slen) |
| value = &line[l][i]; |
| |
| break; |
| } |
| } |
| } |
| |
| if (name != NULL && value != NULL) { |
| httpp_setvar(parser, _lowercase(name), value); |
| name = NULL; |
| value = NULL; |
| } |
| } |
| } |
| |
| int httpp_parse_response(http_parser_t *parser, char *http_data, unsigned long len, char *uri) |
| { |
| char *data; |
| char *line[MAX_HEADERS]; |
| int lines, slen,i, whitespace=0, where=0,code; |
| char *version=NULL, *resp_code=NULL, *message=NULL; |
| |
| if(http_data == NULL) |
| return 0; |
| |
| /* make a local copy of the data, including 0 terminator */ |
| data = (char *)malloc(len+1); |
| if (data == NULL) return 0; |
| memcpy(data, http_data, len); |
| data[len] = 0; |
| |
| lines = split_headers(data, len, line); |
| |
| /* In this case, the first line contains: |
| * VERSION RESPONSE_CODE MESSAGE, such as HTTP/1.0 200 OK |
| */ |
| slen = strlen(line[0]); |
| version = line[0]; |
| for(i=0; i < slen; i++) { |
| if(line[0][i] == ' ') { |
| line[0][i] = 0; |
| whitespace = 1; |
| } else if(whitespace) { |
| whitespace = 0; |
| where++; |
| if(where == 1) |
| resp_code = &line[0][i]; |
| else { |
| message = &line[0][i]; |
| break; |
| } |
| } |
| } |
| |
| if(version == NULL || resp_code == NULL || message == NULL) { |
| free(data); |
| return 0; |
| } |
| |
| httpp_setvar(parser, HTTPP_VAR_ERROR_CODE, resp_code); |
| code = atoi(resp_code); |
| if(code < 200 || code >= 300) { |
| httpp_setvar(parser, HTTPP_VAR_ERROR_MESSAGE, message); |
| } |
| |
| httpp_setvar(parser, HTTPP_VAR_URI, uri); |
| httpp_setvar(parser, HTTPP_VAR_REQ_TYPE, "NONE"); |
| |
| parse_headers(parser, line, lines); |
| |
| free(data); |
| |
| return 1; |
| } |
| |
| static int hex(char c) |
| { |
| if(c >= '0' && c <= '9') |
| return c - '0'; |
| else if(c >= 'A' && c <= 'F') |
| return c - 'A' + 10; |
| else if(c >= 'a' && c <= 'f') |
| return c - 'a' + 10; |
| else |
| return -1; |
| } |
| |
| static char *url_escape(char *src) |
| { |
| int len = strlen(src); |
| unsigned char *decoded; |
| int i; |
| char *dst; |
| int done = 0; |
| |
| decoded = calloc(1, len + 1); |
| |
| dst = decoded; |
| |
| for(i=0; i < len; i++) { |
| switch(src[i]) { |
| case '%': |
| if(i+2 >= len) { |
| free(decoded); |
| return NULL; |
| } |
| if(hex(src[i+1]) == -1 || hex(src[i+2]) == -1 ) { |
| free(decoded); |
| return NULL; |
| } |
| |
| *dst++ = hex(src[i+1]) * 16 + hex(src[i+2]); |
| i+= 2; |
| break; |
| case '#': |
| done = 1; |
| break; |
| case 0: |
| free(decoded); |
| return NULL; |
| break; |
| default: |
| *dst++ = src[i]; |
| break; |
| } |
| if(done) |
| break; |
| } |
| |
| *dst = 0; /* null terminator */ |
| |
| return decoded; |
| } |
| |
| /** TODO: This is almost certainly buggy in some cases */ |
| static void parse_query(http_parser_t *parser, char *query) |
| { |
| int len; |
| int i=0; |
| char *key = query; |
| char *val=NULL; |
| |
| if(!query || !*query) |
| return; |
| |
| len = strlen(query); |
| |
| while(i<len) { |
| switch(query[i]) { |
| case '&': |
| query[i] = 0; |
| if(val && key) |
| httpp_set_query_param(parser, key, val); |
| key = query+i+1; |
| break; |
| case '=': |
| query[i] = 0; |
| val = query+i+1; |
| break; |
| } |
| i++; |
| } |
| |
| if(val && key) { |
| httpp_set_query_param(parser, key, val); |
| } |
| } |
| |
| /* The old shoutcast procotol. Don't look at this, it's really nasty */ |
| int httpp_parse_icy(http_parser_t *parser, char *http_data, unsigned long len) |
| { |
| char *data; |
| char *line[MAX_HEADERS]; |
| int lines; |
| |
| if(http_data == NULL) |
| return 0; |
| |
| data = malloc(len + 1); |
| memcpy(data, http_data, len); |
| data[len] = 0; |
| |
| lines = split_headers(data, len, line); |
| |
| /* Now, this protocol looks like: |
| * sourcepassword\n |
| * headers: as normal\n" |
| * \n |
| */ |
| |
| parser->req_type = httpp_req_source; |
| httpp_setvar(parser, HTTPP_VAR_URI, "/"); |
| httpp_setvar(parser, HTTPP_VAR_ICYPASSWORD, line[0]); |
| httpp_setvar(parser, HTTPP_VAR_PROTOCOL, "ICY"); |
| httpp_setvar(parser, HTTPP_VAR_REQ_TYPE, "SOURCE"); |
| /* This protocol is evil */ |
| httpp_setvar(parser, HTTPP_VAR_VERSION, "666"); |
| |
| parse_headers(parser, line, lines); |
| |
| free(data); |
| |
| return 1; |
| } |
| |
| int httpp_parse(http_parser_t *parser, char *http_data, unsigned long len) |
| { |
| char *data, *tmp; |
| char *line[MAX_HEADERS]; /* limited to 32 lines, should be more than enough */ |
| int i; |
| int lines; |
| char *req_type = NULL; |
| char *uri = NULL; |
| char *version = NULL; |
| int whitespace, where, slen; |
| |
| if (http_data == NULL) |
| return 0; |
| |
| /* make a local copy of the data, including 0 terminator */ |
| data = (char *)malloc(len+1); |
| if (data == NULL) return 0; |
| memcpy(data, http_data, len); |
| data[len] = 0; |
| |
| lines = split_headers(data, len, line); |
| |
| /* parse the first line special |
| ** the format is: |
| ** REQ_TYPE URI VERSION |
| ** eg: |
| ** GET /index.html HTTP/1.0 |
| */ |
| where = 0; |
| whitespace = 0; |
| slen = strlen(line[0]); |
| req_type = line[0]; |
| for (i = 0; i < slen; i++) { |
| if (line[0][i] == ' ') { |
| whitespace = 1; |
| line[0][i] = '\0'; |
| } else { |
| /* we're just past the whitespace boundry */ |
| if (whitespace) { |
| whitespace = 0; |
| where++; |
| switch (where) { |
| case 1: |
| uri = &line[0][i]; |
| break; |
| case 2: |
| version = &line[0][i]; |
| break; |
| } |
| } |
| } |
| } |
| |
| if (strcasecmp("GET", req_type) == 0) { |
| parser->req_type = httpp_req_get; |
| } else if (strcasecmp("POST", req_type) == 0) { |
| parser->req_type = httpp_req_post; |
| } else if (strcasecmp("HEAD", req_type) == 0) { |
| parser->req_type = httpp_req_head; |
| } else if (strcasecmp("SOURCE", req_type) == 0) { |
| parser->req_type = httpp_req_source; |
| } else if (strcasecmp("PLAY", req_type) == 0) { |
| parser->req_type = httpp_req_play; |
| } else if (strcasecmp("STATS", req_type) == 0) { |
| parser->req_type = httpp_req_stats; |
| } else { |
| parser->req_type = httpp_req_unknown; |
| } |
| |
| if (uri != NULL && strlen(uri) > 0) { |
| char *query; |
| if((query = strchr(uri, '?')) != NULL) { |
| *query = 0; |
| query++; |
| parse_query(parser, query); |
| } |
| |
| parser->uri = strdup(uri); |
| } else { |
| free(data); |
| return 0; |
| } |
| |
| if ((version != NULL) && ((tmp = strchr(version, '/')) != NULL)) { |
| tmp[0] = '\0'; |
| if ((strlen(version) > 0) && (strlen(&tmp[1]) > 0)) { |
| httpp_setvar(parser, HTTPP_VAR_PROTOCOL, version); |
| httpp_setvar(parser, HTTPP_VAR_VERSION, &tmp[1]); |
| } else { |
| free(data); |
| return 0; |
| } |
| } else { |
| free(data); |
| return 0; |
| } |
| |
| if (parser->req_type != httpp_req_none && parser->req_type != httpp_req_unknown) { |
| switch (parser->req_type) { |
| case httpp_req_get: |
| httpp_setvar(parser, HTTPP_VAR_REQ_TYPE, "GET"); |
| break; |
| case httpp_req_post: |
| httpp_setvar(parser, HTTPP_VAR_REQ_TYPE, "POST"); |
| break; |
| case httpp_req_head: |
| httpp_setvar(parser, HTTPP_VAR_REQ_TYPE, "HEAD"); |
| break; |
| case httpp_req_source: |
| httpp_setvar(parser, HTTPP_VAR_REQ_TYPE, "SOURCE"); |
| break; |
| case httpp_req_play: |
| httpp_setvar(parser, HTTPP_VAR_REQ_TYPE, "PLAY"); |
| break; |
| case httpp_req_stats: |
| httpp_setvar(parser, HTTPP_VAR_REQ_TYPE, "STATS"); |
| break; |
| default: |
| break; |
| } |
| } else { |
| free(data); |
| return 0; |
| } |
| |
| if (parser->uri != NULL) { |
| httpp_setvar(parser, HTTPP_VAR_URI, parser->uri); |
| } else { |
| free(data); |
| return 0; |
| } |
| |
| parse_headers(parser, line, lines); |
| |
| free(data); |
| |
| return 1; |
| } |
| |
| void httpp_setvar(http_parser_t *parser, char *name, char *value) |
| { |
| http_var_t *var; |
| |
| if (name == NULL || value == NULL) |
| return; |
| |
| var = (http_var_t *)malloc(sizeof(http_var_t)); |
| if (var == NULL) return; |
| |
| var->name = strdup(name); |
| var->value = strdup(value); |
| |
| if (httpp_getvar(parser, name) == NULL) { |
| avl_insert(parser->vars, (void *)var); |
| } else { |
| avl_delete(parser->vars, (void *)var, _free_vars); |
| avl_insert(parser->vars, (void *)var); |
| } |
| } |
| |
| char *httpp_getvar(http_parser_t *parser, char *name) |
| { |
| http_var_t var; |
| http_var_t *found; |
| void *fp; |
| |
| fp = &found; |
| var.name = name; |
| var.value = NULL; |
| |
| if (avl_get_by_key(parser->vars, &var, fp) == 0) |
| return found->value; |
| else |
| return NULL; |
| } |
| |
| void httpp_set_query_param(http_parser_t *parser, char *name, char *value) |
| { |
| http_var_t *var; |
| |
| if (name == NULL || value == NULL) |
| return; |
| |
| var = (http_var_t *)malloc(sizeof(http_var_t)); |
| if (var == NULL) return; |
| |
| var->name = strdup(name); |
| var->value = url_escape(value); |
| |
| if (httpp_get_query_param(parser, name) == NULL) { |
| avl_insert(parser->queryvars, (void *)var); |
| } else { |
| avl_delete(parser->queryvars, (void *)var, _free_vars); |
| avl_insert(parser->queryvars, (void *)var); |
| } |
| } |
| |
| char *httpp_get_query_param(http_parser_t *parser, char *name) |
| { |
| http_var_t var; |
| http_var_t *found; |
| void *fp; |
| |
| fp = &found; |
| var.name = name; |
| var.value = NULL; |
| |
| if (avl_get_by_key(parser->queryvars, (void *)&var, fp) == 0) |
| return found->value; |
| else |
| return NULL; |
| } |
| |
| void httpp_clear(http_parser_t *parser) |
| { |
| parser->req_type = httpp_req_none; |
| if (parser->uri) |
| free(parser->uri); |
| parser->uri = NULL; |
| avl_tree_free(parser->vars, _free_vars); |
| avl_tree_free(parser->queryvars, _free_vars); |
| parser->vars = NULL; |
| } |
| |
| void httpp_destroy(http_parser_t *parser) |
| { |
| httpp_clear(parser); |
| free(parser); |
| } |
| |
| static char *_lowercase(char *str) |
| { |
| char *p = str; |
| for (; *p != '\0'; p++) |
| *p = tolower(*p); |
| |
| return str; |
| } |
| |
| static int _compare_vars(void *compare_arg, void *a, void *b) |
| { |
| http_var_t *vara, *varb; |
| |
| vara = (http_var_t *)a; |
| varb = (http_var_t *)b; |
| |
| return strcmp(vara->name, varb->name); |
| } |
| |
| static int _free_vars(void *key) |
| { |
| http_var_t *var; |
| |
| var = (http_var_t *)key; |
| |
| if (var->name) |
| free(var->name); |
| if (var->value) |
| free(var->value); |
| free(var); |
| |
| return 1; |
| } |
| |
| @ |
| |
| |
| 1.22 |
| log |
| @ermmm, let's use the right operator. |
| @ |
| text |
| @d34 2 |
| a35 2 |
| int _compare_vars(void *compare_arg, void *a, void *b); |
| int _free_vars(void *key); |
| d556 1 |
| a556 1 |
| int _compare_vars(void *compare_arg, void *a, void *b) |
| d566 1 |
| a566 1 |
| int _free_vars(void *key) |
| @ |
| |
| |
| 1.21 |
| log |
| @minor cleanup, removes compiler warning, makes it static, and doesn't |
| re-evaluate string length each time. |
| @ |
| text |
| @d550 1 |
| a550 1 |
| for (; *p |= '\0'; p++) |
| @ |
| |
| |
| 1.20 |
| log |
| @gcc 3.3 warns: dereferencing type-punned pointer will break strict-aliasing rules |
| @ |
| text |
| @d31 1 |
| a31 1 |
| char *_lowercase(char *str); |
| d547 1 |
| a547 1 |
| char *_lowercase(char *str) |
| d549 3 |
| a551 3 |
| long i; |
| for (i = 0; i < strlen(str); i++) |
| str[i] = tolower(str[i]); |
| @ |
| |
| |
| 1.19 |
| log |
| @Karl's patch for freebsd, minus the sys/select.h test which breaks on OS X. |
| Also enables IPV6 in libshout! |
| @ |
| text |
| @d481 1 |
| d483 1 |
| d487 1 |
| a487 1 |
| if (avl_get_by_key(parser->vars, (void *)&var, (void **)&found) == 0) |
| d518 1 |
| d520 1 |
| d524 1 |
| a524 1 |
| if (avl_get_by_key(parser->queryvars, (void *)&var, (void **)&found) == 0) |
| @ |
| |
| |
| 1.18 |
| log |
| @Brendan was getting pissed off about inconsistent indentation styles. |
| Convert all tabs to 4 spaces. All code must now use 4 space indents. |
| @ |
| text |
| @d15 3 |
| @ |
| |
| |
| 1.17 |
| log |
| @reduce include file namespace clutter for libshout and the associated |
| smaller libs. |
| @ |
| text |
| @d36 1 |
| a36 1 |
| return (http_parser_t *)malloc(sizeof(http_parser_t)); |
| d41 1 |
| a41 1 |
| http_varlist_t *list; |
| d43 11 |
| a53 11 |
| parser->req_type = httpp_req_none; |
| parser->uri = NULL; |
| parser->vars = avl_tree_new(_compare_vars, NULL); |
| parser->queryvars = avl_tree_new(_compare_vars, NULL); |
| |
| /* now insert the default variables */ |
| list = defaults; |
| while (list != NULL) { |
| httpp_setvar(parser, list->var.name, list->var.value); |
| list = list->next; |
| } |
| d58 4 |
| a61 4 |
| /* first we count how many lines there are |
| ** and set up the line[] array |
| */ |
| int lines = 0; |
| d63 11 |
| a73 11 |
| line[lines] = data; |
| for (i = 0; i < len && lines < MAX_HEADERS; i++) { |
| if (data[i] == '\r') |
| data[i] = '\0'; |
| if (data[i] == '\n') { |
| lines++; |
| data[i] = '\0'; |
| if (i + 1 < len) { |
| if (data[i + 1] == '\n' || data[i + 1] == '\r') |
| break; |
| line[lines] = &data[i + 1]; |
| d75 2 |
| a76 2 |
| } |
| } |
| d78 2 |
| a79 2 |
| i++; |
| while (data[i] == '\n') i++; |
| d87 35 |
| a121 35 |
| int whitespace, where, slen; |
| char *name = NULL; |
| char *value = NULL; |
| |
| /* parse the name: value lines. */ |
| for (l = 1; l < lines; l++) { |
| where = 0; |
| whitespace = 0; |
| name = line[l]; |
| value = NULL; |
| slen = strlen(line[l]); |
| for (i = 0; i < slen; i++) { |
| if (line[l][i] == ':') { |
| whitespace = 1; |
| line[l][i] = '\0'; |
| } else { |
| if (whitespace) { |
| whitespace = 0; |
| while (i < slen && line[l][i] == ' ') |
| i++; |
| |
| if (i < slen) |
| value = &line[l][i]; |
| |
| break; |
| } |
| } |
| } |
| |
| if (name != NULL && value != NULL) { |
| httpp_setvar(parser, _lowercase(name), value); |
| name = NULL; |
| value = NULL; |
| } |
| } |
| d126 4 |
| a129 4 |
| char *data; |
| char *line[MAX_HEADERS]; |
| int lines, slen,i, whitespace=0, where=0,code; |
| char *version=NULL, *resp_code=NULL, *message=NULL; |
| d131 2 |
| a132 2 |
| if(http_data == NULL) |
| return 0; |
| d134 5 |
| a138 39 |
| /* make a local copy of the data, including 0 terminator */ |
| data = (char *)malloc(len+1); |
| if (data == NULL) return 0; |
| memcpy(data, http_data, len); |
| data[len] = 0; |
| |
| lines = split_headers(data, len, line); |
| |
| /* In this case, the first line contains: |
| * VERSION RESPONSE_CODE MESSAGE, such as HTTP/1.0 200 OK |
| */ |
| slen = strlen(line[0]); |
| version = line[0]; |
| for(i=0; i < slen; i++) { |
| if(line[0][i] == ' ') { |
| line[0][i] = 0; |
| whitespace = 1; |
| } else if(whitespace) { |
| whitespace = 0; |
| where++; |
| if(where == 1) |
| resp_code = &line[0][i]; |
| else { |
| message = &line[0][i]; |
| break; |
| } |
| } |
| } |
| |
| if(version == NULL || resp_code == NULL || message == NULL) { |
| free(data); |
| return 0; |
| } |
| |
| httpp_setvar(parser, HTTPP_VAR_ERROR_CODE, resp_code); |
| code = atoi(resp_code); |
| if(code < 200 || code >= 300) { |
| httpp_setvar(parser, HTTPP_VAR_ERROR_MESSAGE, message); |
| } |
| d140 1 |
| a140 2 |
| httpp_setvar(parser, HTTPP_VAR_URI, uri); |
| httpp_setvar(parser, HTTPP_VAR_REQ_TYPE, "NONE"); |
| d142 20 |
| a161 1 |
| parse_headers(parser, line, lines); |
| d163 4 |
| a166 1 |
| free(data); |
| d168 14 |
| a181 1 |
| return 1; |
| d186 8 |
| a193 8 |
| if(c >= '0' && c <= '9') |
| return c - '0'; |
| else if(c >= 'A' && c <= 'F') |
| return c - 'A' + 10; |
| else if(c >= 'a' && c <= 'f') |
| return c - 'a' + 10; |
| else |
| return -1; |
| d198 39 |
| a236 39 |
| int len = strlen(src); |
| unsigned char *decoded; |
| int i; |
| char *dst; |
| int done = 0; |
| |
| decoded = calloc(1, len + 1); |
| |
| dst = decoded; |
| |
| for(i=0; i < len; i++) { |
| switch(src[i]) { |
| case '%': |
| if(i+2 >= len) { |
| free(decoded); |
| return NULL; |
| } |
| if(hex(src[i+1]) == -1 || hex(src[i+2]) == -1 ) { |
| free(decoded); |
| return NULL; |
| } |
| |
| *dst++ = hex(src[i+1]) * 16 + hex(src[i+2]); |
| i+= 2; |
| break; |
| case '#': |
| done = 1; |
| break; |
| case 0: |
| free(decoded); |
| return NULL; |
| break; |
| default: |
| *dst++ = src[i]; |
| break; |
| } |
| if(done) |
| break; |
| } |
| d238 1 |
| a238 1 |
| *dst = 0; /* null terminator */ |
| d240 1 |
| a240 1 |
| return decoded; |
| d246 29 |
| a274 29 |
| int len; |
| int i=0; |
| char *key = query; |
| char *val=NULL; |
| |
| if(!query || !*query) |
| return; |
| |
| len = strlen(query); |
| |
| while(i<len) { |
| switch(query[i]) { |
| case '&': |
| query[i] = 0; |
| if(val && key) |
| httpp_set_query_param(parser, key, val); |
| key = query+i+1; |
| break; |
| case '=': |
| query[i] = 0; |
| val = query+i+1; |
| break; |
| } |
| i++; |
| } |
| |
| if(val && key) { |
| httpp_set_query_param(parser, key, val); |
| } |
| d291 1 |
| a291 1 |
| lines = split_headers(data, len, line); |
| d316 80 |
| a395 74 |
| char *data, *tmp; |
| char *line[MAX_HEADERS]; /* limited to 32 lines, should be more than enough */ |
| int i; |
| int lines; |
| char *req_type = NULL; |
| char *uri = NULL; |
| char *version = NULL; |
| int whitespace, where, slen; |
| |
| if (http_data == NULL) |
| return 0; |
| |
| /* make a local copy of the data, including 0 terminator */ |
| data = (char *)malloc(len+1); |
| if (data == NULL) return 0; |
| memcpy(data, http_data, len); |
| data[len] = 0; |
| |
| lines = split_headers(data, len, line); |
| |
| /* parse the first line special |
| ** the format is: |
| ** REQ_TYPE URI VERSION |
| ** eg: |
| ** GET /index.html HTTP/1.0 |
| */ |
| where = 0; |
| whitespace = 0; |
| slen = strlen(line[0]); |
| req_type = line[0]; |
| for (i = 0; i < slen; i++) { |
| if (line[0][i] == ' ') { |
| whitespace = 1; |
| line[0][i] = '\0'; |
| } else { |
| /* we're just past the whitespace boundry */ |
| if (whitespace) { |
| whitespace = 0; |
| where++; |
| switch (where) { |
| case 1: |
| uri = &line[0][i]; |
| break; |
| case 2: |
| version = &line[0][i]; |
| break; |
| } |
| } |
| } |
| } |
| |
| if (strcasecmp("GET", req_type) == 0) { |
| parser->req_type = httpp_req_get; |
| } else if (strcasecmp("POST", req_type) == 0) { |
| parser->req_type = httpp_req_post; |
| } else if (strcasecmp("HEAD", req_type) == 0) { |
| parser->req_type = httpp_req_head; |
| } else if (strcasecmp("SOURCE", req_type) == 0) { |
| parser->req_type = httpp_req_source; |
| } else if (strcasecmp("PLAY", req_type) == 0) { |
| parser->req_type = httpp_req_play; |
| } else if (strcasecmp("STATS", req_type) == 0) { |
| parser->req_type = httpp_req_stats; |
| } else { |
| parser->req_type = httpp_req_unknown; |
| } |
| |
| if (uri != NULL && strlen(uri) > 0) { |
| char *query; |
| if((query = strchr(uri, '?')) != NULL) { |
| *query = 0; |
| query++; |
| parse_query(parser, query); |
| } |
| d397 10 |
| a406 2 |
| parser->uri = strdup(uri); |
| } else { |
| d411 27 |
| a437 48 |
| if ((version != NULL) && ((tmp = strchr(version, '/')) != NULL)) { |
| tmp[0] = '\0'; |
| if ((strlen(version) > 0) && (strlen(&tmp[1]) > 0)) { |
| httpp_setvar(parser, HTTPP_VAR_PROTOCOL, version); |
| httpp_setvar(parser, HTTPP_VAR_VERSION, &tmp[1]); |
| } else { |
| free(data); |
| return 0; |
| } |
| } else { |
| free(data); |
| return 0; |
| } |
| |
| if (parser->req_type != httpp_req_none && parser->req_type != httpp_req_unknown) { |
| switch (parser->req_type) { |
| case httpp_req_get: |
| httpp_setvar(parser, HTTPP_VAR_REQ_TYPE, "GET"); |
| break; |
| case httpp_req_post: |
| httpp_setvar(parser, HTTPP_VAR_REQ_TYPE, "POST"); |
| break; |
| case httpp_req_head: |
| httpp_setvar(parser, HTTPP_VAR_REQ_TYPE, "HEAD"); |
| break; |
| case httpp_req_source: |
| httpp_setvar(parser, HTTPP_VAR_REQ_TYPE, "SOURCE"); |
| break; |
| case httpp_req_play: |
| httpp_setvar(parser, HTTPP_VAR_REQ_TYPE, "PLAY"); |
| break; |
| case httpp_req_stats: |
| httpp_setvar(parser, HTTPP_VAR_REQ_TYPE, "STATS"); |
| break; |
| default: |
| break; |
| } |
| } else { |
| free(data); |
| return 0; |
| } |
| |
| if (parser->uri != NULL) { |
| httpp_setvar(parser, HTTPP_VAR_URI, parser->uri); |
| } else { |
| free(data); |
| return 0; |
| } |
| d439 6 |
| a444 1 |
| parse_headers(parser, line, lines); |
| d446 3 |
| a448 1 |
| free(data); |
| d450 1 |
| a450 1 |
| return 1; |
| d455 1 |
| a455 1 |
| http_var_t *var; |
| d457 2 |
| a458 2 |
| if (name == NULL || value == NULL) |
| return; |
| d460 2 |
| a461 2 |
| var = (http_var_t *)malloc(sizeof(http_var_t)); |
| if (var == NULL) return; |
| d463 9 |
| a471 9 |
| var->name = strdup(name); |
| var->value = strdup(value); |
| |
| if (httpp_getvar(parser, name) == NULL) { |
| avl_insert(parser->vars, (void *)var); |
| } else { |
| avl_delete(parser->vars, (void *)var, _free_vars); |
| avl_insert(parser->vars, (void *)var); |
| } |
| d476 2 |
| a477 2 |
| http_var_t var; |
| http_var_t *found; |
| d479 2 |
| a480 2 |
| var.name = name; |
| var.value = NULL; |
| d482 4 |
| a485 4 |
| if (avl_get_by_key(parser->vars, (void *)&var, (void **)&found) == 0) |
| return found->value; |
| else |
| return NULL; |
| d490 1 |
| a490 1 |
| http_var_t *var; |
| d492 2 |
| a493 2 |
| if (name == NULL || value == NULL) |
| return; |
| d495 2 |
| a496 2 |
| var = (http_var_t *)malloc(sizeof(http_var_t)); |
| if (var == NULL) return; |
| d498 9 |
| a506 9 |
| var->name = strdup(name); |
| var->value = url_escape(value); |
| |
| if (httpp_get_query_param(parser, name) == NULL) { |
| avl_insert(parser->queryvars, (void *)var); |
| } else { |
| avl_delete(parser->queryvars, (void *)var, _free_vars); |
| avl_insert(parser->queryvars, (void *)var); |
| } |
| d511 2 |
| a512 2 |
| http_var_t var; |
| http_var_t *found; |
| d514 2 |
| a515 2 |
| var.name = name; |
| var.value = NULL; |
| d517 4 |
| a520 4 |
| if (avl_get_by_key(parser->queryvars, (void *)&var, (void **)&found) == 0) |
| return found->value; |
| else |
| return NULL; |
| d525 7 |
| a531 7 |
| parser->req_type = httpp_req_none; |
| if (parser->uri) |
| free(parser->uri); |
| parser->uri = NULL; |
| avl_tree_free(parser->vars, _free_vars); |
| avl_tree_free(parser->queryvars, _free_vars); |
| parser->vars = NULL; |
| d536 2 |
| a537 2 |
| httpp_clear(parser); |
| free(parser); |
| d542 3 |
| a544 3 |
| long i; |
| for (i = 0; i < strlen(str); i++) |
| str[i] = tolower(str[i]); |
| d546 1 |
| a546 1 |
| return str; |
| d551 1 |
| a551 1 |
| http_var_t *vara, *varb; |
| d553 2 |
| a554 2 |
| vara = (http_var_t *)a; |
| varb = (http_var_t *)b; |
| d556 1 |
| a556 1 |
| return strcmp(vara->name, varb->name); |
| d561 1 |
| a561 1 |
| http_var_t *var; |
| d563 1 |
| a563 1 |
| var = (http_var_t *)key; |
| d565 5 |
| a569 5 |
| if (var->name) |
| free(var->name); |
| if (var->value) |
| free(var->value); |
| free(var); |
| d571 1 |
| a571 1 |
| return 1; |
| @ |
| |
| |
| 1.16 |
| log |
| @include the automake config.h file if the application defines one |
| @ |
| text |
| @d16 1 |
| a16 1 |
| #include "avl.h" |
| @ |
| |
| |
| 1.15 |
| log |
| @Set another parameter in the icy protocol parse that logging expects |
| @ |
| text |
| @d6 4 |
| @ |
| |
| |
| 1.14 |
| log |
| @Added support for shoutcast login protocol (ewww...) |
| @ |
| text |
| @d299 1 |
| @ |
| |
| |
| 1.13 |
| log |
| @Use gnu archive ACX_PTHREAD macro to figure out how to compile thread support. |
| Also make it possible to build libshout without threads, albeit without locking |
| in the resolver or avl trees. |
| New option --disable-pthread too. |
| @ |
| text |
| @d273 36 |
| d387 4 |
| a390 2 |
| } else |
| parser->uri = NULL; |
| @ |
| |
| |
| 1.12 |
| log |
| @Fix some warnings, fix cflags. |
| @ |
| text |
| @a11 1 |
| #include "thread.h" |
| @ |
| |
| |
| 1.11 |
| log |
| @Indentation again, don't mind me |
| @ |
| text |
| @d58 2 |
| a59 1 |
| int i, lines = 0; |
| @ |
| |
| |
| 1.10 |
| log |
| @Make indentation consistent before doing other work |
| @ |
| text |
| @d472 1 |
| a472 1 |
| var.value = NULL; |
| @ |
| |
| |
| 1.9 |
| log |
| @mp3 metadata complete. Still untested. |
| @ |
| text |
| @d43 1 |
| a43 1 |
| parser->queryvars = avl_tree_new(_compare_vars, NULL); |
| d122 4 |
| a125 4 |
| char *data; |
| char *line[MAX_HEADERS]; |
| int lines, slen,i, whitespace=0, where=0,code; |
| char *version=NULL, *resp_code=NULL, *message=NULL; |
| d127 2 |
| a128 2 |
| if(http_data == NULL) |
| return 0; |
| d134 1 |
| a134 1 |
| data[len] = 0; |
| d136 1 |
| a136 1 |
| lines = split_headers(data, len, line); |
| d138 20 |
| a157 22 |
| /* In this case, the first line contains: |
| * VERSION RESPONSE_CODE MESSAGE, such as |
| * HTTP/1.0 200 OK |
| */ |
| slen = strlen(line[0]); |
| version = line[0]; |
| for(i=0; i < slen; i++) { |
| if(line[0][i] == ' ') { |
| line[0][i] = 0; |
| whitespace = 1; |
| } |
| else if(whitespace) { |
| whitespace = 0; |
| where++; |
| if(where == 1) |
| resp_code = &line[0][i]; |
| else { |
| message = &line[0][i]; |
| break; |
| } |
| } |
| } |
| d159 4 |
| a162 10 |
| if(version == NULL || resp_code == NULL || message == NULL) { |
| free(data); |
| return 0; |
| } |
| |
| httpp_setvar(parser, HTTPP_VAR_ERROR_CODE, resp_code); |
| code = atoi(resp_code); |
| if(code < 200 || code >= 300) { |
| httpp_setvar(parser, HTTPP_VAR_ERROR_MESSAGE, message); |
| } |
| d164 7 |
| a170 1 |
| httpp_setvar(parser, HTTPP_VAR_URI, uri); |
| d173 1 |
| a173 1 |
| parse_headers(parser, line, lines); |
| d182 8 |
| a189 8 |
| if(c >= '0' && c <= '9') |
| return c - '0'; |
| else if(c >= 'A' && c <= 'F') |
| return c - 'A' + 10; |
| else if(c >= 'a' && c <= 'f') |
| return c - 'a' + 10; |
| else |
| return -1; |
| d194 39 |
| a232 39 |
| int len = strlen(src); |
| unsigned char *decoded; |
| int i; |
| char *dst; |
| int done = 0; |
| |
| decoded = calloc(1, len + 1); |
| |
| dst = decoded; |
| |
| for(i=0; i < len; i++) { |
| switch(src[i]) { |
| case '%': |
| if(i+2 >= len) { |
| free(decoded); |
| return NULL; |
| } |
| if(hex(src[i+1]) == -1 || hex(src[i+2]) == -1 ) { |
| free(decoded); |
| return NULL; |
| } |
| |
| *dst++ = hex(src[i+1]) * 16 + hex(src[i+2]); |
| i+= 2; |
| break; |
| case '#': |
| done = 1; |
| break; |
| case 0: |
| free(decoded); |
| return NULL; |
| break; |
| default: |
| *dst++ = src[i]; |
| break; |
| } |
| if(done) |
| break; |
| } |
| d234 1 |
| a234 1 |
| *dst = 0; /* null terminator */ |
| d236 1 |
| a236 1 |
| return decoded; |
| d242 29 |
| a270 30 |
| int len; |
| int i=0; |
| char *key = query; |
| char *val=NULL; |
| |
| if(!query || !*query) |
| return; |
| |
| len = strlen(query); |
| |
| while(i<len) { |
| switch(query[i]) { |
| case '&': |
| query[i] = 0; |
| if(val && key) { |
| httpp_set_query_param(parser, key, val); |
| } |
| key = query+i+1; |
| break; |
| case '=': |
| query[i] = 0; |
| val = query+i+1; |
| break; |
| } |
| i++; |
| } |
| |
| if(val && key) { |
| httpp_set_query_param(parser, key, val); |
| } |
| d282 1 |
| a282 1 |
| int whitespace, where, slen; |
| d291 1 |
| a291 1 |
| data[len] = 0; |
| d293 1 |
| a293 1 |
| lines = split_headers(data, len, line); |
| d342 7 |
| a348 8 |
| if (uri != NULL && strlen(uri) > 0) |
| { |
| char *query; |
| if((query = strchr(uri, '?')) != NULL) { |
| *query = 0; |
| query++; |
| parse_query(parser, query); |
| } |
| d351 1 |
| a351 2 |
| } |
| else |
| d396 1 |
| a396 1 |
| if (parser->uri != NULL) { |
| d403 1 |
| a403 1 |
| parse_headers(parser, line, lines); |
| d437 1 |
| a437 1 |
| var.value = NULL; |
| d493 2 |
| a494 2 |
| httpp_clear(parser); |
| free(parser); |
| @ |
| |
| |
| 1.8 |
| log |
| @bugfixes for httpp_parse_response |
| @ |
| text |
| @d43 1 |
| d182 94 |
| d345 9 |
| a353 1 |
| if (uri != NULL && strlen(uri) > 0) |
| d355 1 |
| d442 1 |
| a442 1 |
| var.value = NULL; |
| d450 35 |
| d492 1 |
| @ |
| |
| |
| 1.7 |
| log |
| @Cleaned up version of Ciaran Anscomb's relaying patch. |
| @ |
| text |
| @d165 1 |
| a168 2 |
| free(data); |
| return 0; |
| d172 1 |
| a172 1 |
| httpp_setvar(parser, HTTPP_VAR_REQ_TYPE, "RELAY"); |
| @ |
| |
| |
| 1.6 |
| log |
| @Memory leaks. Lots of little ones. |
| @ |
| text |
| @d52 1 |
| a52 1 |
| int httpp_parse(http_parser_t *parser, char *http_data, unsigned long len) |
| a53 21 |
| char *data, *tmp; |
| char *line[MAX_HEADERS]; /* limited to 32 lines, should be more than enough */ |
| int i, l, retlen; |
| int lines; |
| char *req_type = NULL; |
| char *uri = NULL; |
| char *version = NULL; |
| char *name = NULL; |
| char *value = NULL; |
| int whitespace, where; |
| int slen; |
| |
| if (http_data == NULL) |
| return 0; |
| |
| /* make a local copy of the data, including 0 terminator */ |
| data = (char *)malloc(len+1); |
| if (data == NULL) return 0; |
| memcpy(data, http_data, len); |
| data[len] = 0; |
| |
| d57 1 |
| a57 1 |
| lines = 0; |
| d75 128 |
| a202 1 |
| retlen = i; |
| d298 1 |
| a298 1 |
| if (parser->uri != NULL) { |
| d305 1 |
| a305 31 |
| /* parse the name: value lines. */ |
| for (l = 1; l < lines; l++) { |
| where = 0; |
| whitespace = 0; |
| name = line[l]; |
| value = NULL; |
| slen = strlen(line[l]); |
| for (i = 0; i < slen; i++) { |
| if (line[l][i] == ':') { |
| whitespace = 1; |
| line[l][i] = '\0'; |
| } else { |
| if (whitespace) { |
| whitespace = 0; |
| while (i < slen && line[l][i] == ' ') |
| i++; |
| |
| if (i < slen) |
| value = &line[l][i]; |
| |
| break; |
| } |
| } |
| } |
| |
| if (name != NULL && value != NULL) { |
| httpp_setvar(parser, _lowercase(name), value); |
| name = NULL; |
| value = NULL; |
| } |
| } |
| d309 1 |
| a309 1 |
| return retlen; |
| @ |
| |
| |
| 1.5 |
| log |
| @Buffer overflows. |
| |
| Requires a change to the format plugin interface - jack: if you want this |
| done differently, feel free to change it (or ask me to). |
| @ |
| text |
| @d271 1 |
| a271 1 |
| void httpp_destroy(http_parser_t *parser) |
| d279 6 |
| @ |
| |
| |
| 1.4 |
| log |
| @Bunch of fixes: |
| |
| - connections are now matched to format plugins based on content-type headers, |
| and are rejected if there isn't a format handler for that content-type, or |
| there is no content-type at all. |
| - format_vorbis now handles pages with granulepos of -1 in the headers |
| correctly (this happens if the headers are fairly large, because of |
| many comments, for example). |
| - various #include fixes. |
| - buffer overflow in httpp.c fixed. |
| @ |
| text |
| @d6 2 |
| d20 2 |
| d55 1 |
| a55 1 |
| char *line[32]; /* limited to 32 lines, should be more than enough */ |
| d80 1 |
| a80 1 |
| for (i = 0; i < len; i++) { |
| @ |
| |
| |
| 1.3 |
| log |
| @Thanks to Akos Maroy <darkeye@@tyrell.hu> for this. These variables need to |
| be uppercase always in order to comply with the HTTP specification. |
| While not a problem internal to icecast, they were slipping into the log |
| files and breaking some less-than-robust parsers. |
| @ |
| text |
| @d65 2 |
| a66 2 |
| /* make a local copy of the data */ |
| data = (char *)malloc(len); |
| d69 1 |
| d81 3 |
| a83 3 |
| if (i + 1 < len) |
| if (data[i + 1] == '\n' || data[i + 1] == '\r') { |
| data[i] = '\0'; |
| a84 3 |
| } |
| data[i] = '\0'; |
| if (i < len - 1) |
| d86 1 |
| @ |
| |
| |
| 1.2 |
| log |
| @Win32 compatibility courtesy of Oddsock. |
| @ |
| text |
| @d150 1 |
| a150 1 |
| httpp_setvar(parser, HTTPP_VAR_PROTOCOL, _lowercase(version)); |
| d164 1 |
| a164 1 |
| httpp_setvar(parser, HTTPP_VAR_REQ_TYPE, "get"); |
| d167 1 |
| a167 1 |
| httpp_setvar(parser, HTTPP_VAR_REQ_TYPE, "post"); |
| d170 1 |
| a170 1 |
| httpp_setvar(parser, HTTPP_VAR_REQ_TYPE, "head"); |
| d173 1 |
| a173 1 |
| httpp_setvar(parser, HTTPP_VAR_REQ_TYPE, "source"); |
| d176 1 |
| a176 1 |
| httpp_setvar(parser, HTTPP_VAR_REQ_TYPE, "play"); |
| d179 1 |
| a179 1 |
| httpp_setvar(parser, HTTPP_VAR_REQ_TYPE, "stats"); |
| @ |
| |
| |
| 1.1 |
| log |
| @Initial revision |
| @ |
| text |
| @d14 4 |
| d54 5 |
| a58 4 |
| char *req_type; |
| char *uri; |
| char *version; |
| char *name, *value; |
| @ |
| |
| |
| 1.1.1.1 |
| log |
| @move to cvs |
| @ |
| text |
| @@ |