Blame src/httpp/httpp.c

Packit eed494
/* Httpp.c
Packit eed494
**
Packit eed494
** http parsing engine
Packit eed494
** 
Packit eed494
** This program is distributed under the GNU General Public License, version 2.
Packit eed494
** A copy of this license is included with this source.
Packit eed494
*/
Packit eed494
Packit eed494
#ifdef HAVE_CONFIG_H
Packit eed494
 #include <config.h>
Packit eed494
#endif
Packit eed494
Packit eed494
#include <stdio.h>
Packit eed494
Packit eed494
#include <stdlib.h>
Packit eed494
#include <string.h>
Packit eed494
#include <ctype.h>
Packit eed494
#ifdef HAVE_STRINGS_H
Packit eed494
#include <strings.h>
Packit eed494
#endif
Packit eed494
Packit eed494
#include <avl/avl.h>
Packit eed494
#include "httpp.h"
Packit eed494
Packit eed494
#ifdef _WIN32
Packit eed494
#define strcasecmp stricmp
Packit eed494
#endif
Packit eed494
Packit eed494
#define MAX_HEADERS 32
Packit eed494
Packit eed494
/* internal functions */
Packit eed494
Packit eed494
/* misc */
Packit eed494
static char *_lowercase(char *str);
Packit eed494
Packit eed494
/* for avl trees */
Packit eed494
static int _compare_vars(void *compare_arg, void *a, void *b);
Packit eed494
static int _free_vars(void *key);
Packit eed494
Packit eed494
http_parser_t *httpp_create_parser(void)
Packit eed494
{
Packit eed494
    return (http_parser_t *)malloc(sizeof(http_parser_t));
Packit eed494
}
Packit eed494
Packit eed494
void httpp_initialize(http_parser_t *parser, http_varlist_t *defaults)
Packit eed494
{
Packit eed494
    http_varlist_t *list;
Packit eed494
Packit eed494
    parser->req_type = httpp_req_none;
Packit eed494
    parser->uri = NULL;
Packit eed494
    parser->vars = avl_tree_new(_compare_vars, NULL);
Packit eed494
    parser->queryvars = avl_tree_new(_compare_vars, NULL);
Packit eed494
Packit eed494
    /* now insert the default variables */
Packit eed494
    list = defaults;
Packit eed494
    while (list != NULL) {
Packit eed494
        httpp_setvar(parser, list->var.name, list->var.value);
Packit eed494
        list = list->next;
Packit eed494
    }
Packit eed494
}
Packit eed494
Packit eed494
static int split_headers(char *data, unsigned long len, char **line)
Packit eed494
{
Packit eed494
    /* first we count how many lines there are 
Packit eed494
    ** and set up the line[] array     
Packit eed494
    */
Packit eed494
    int lines = 0;
Packit eed494
    unsigned long i;
Packit eed494
    line[lines] = data;
Packit eed494
    for (i = 0; i < len && lines < MAX_HEADERS; i++) {
Packit eed494
        if (data[i] == '\r')
Packit eed494
            data[i] = '\0';
Packit eed494
        if (data[i] == '\n') {
Packit eed494
            lines++;
Packit eed494
            data[i] = '\0';
Packit eed494
            if (lines >= MAX_HEADERS)
Packit eed494
                return MAX_HEADERS;
Packit eed494
            if (i + 1 < len) {
Packit eed494
                if (data[i + 1] == '\n' || data[i + 1] == '\r')
Packit eed494
                    break;
Packit eed494
                line[lines] = &data[i + 1];
Packit eed494
            }
Packit eed494
        }
Packit eed494
    }
Packit eed494
Packit eed494
    i++;
Packit eed494
    while (i < len && data[i] == '\n') i++;
Packit eed494
Packit eed494
    return lines;
Packit eed494
}
Packit eed494
Packit eed494
static void parse_headers(http_parser_t *parser, char **line, int lines)
Packit eed494
{
Packit eed494
    int i,l;
Packit eed494
    int whitespace, where, slen;
Packit eed494
    char *name = NULL;
Packit eed494
    char *value = NULL;
Packit eed494
Packit eed494
    /* parse the name: value lines. */
Packit eed494
    for (l = 1; l < lines; l++) {
Packit eed494
        where = 0;
Packit eed494
        whitespace = 0;
Packit eed494
        name = line[l];
Packit eed494
        value = NULL;
Packit eed494
        slen = strlen(line[l]);
Packit eed494
        for (i = 0; i < slen; i++) {
Packit eed494
            if (line[l][i] == ':') {
Packit eed494
                whitespace = 1;
Packit eed494
                line[l][i] = '\0';
Packit eed494
            } else {
Packit eed494
                if (whitespace) {
Packit eed494
                    whitespace = 0;
Packit eed494
                    while (i < slen && line[l][i] == ' ')
Packit eed494
                        i++;
Packit eed494
Packit eed494
                    if (i < slen)
Packit eed494
                        value = &line[l][i];
Packit eed494
                    
Packit eed494
                    break;
Packit eed494
                }
Packit eed494
            }
Packit eed494
        }
Packit eed494
        
Packit eed494
        if (name != NULL && value != NULL) {
Packit eed494
            httpp_setvar(parser, _lowercase(name), value);
Packit eed494
            name = NULL; 
Packit eed494
            value = NULL;
Packit eed494
        }
Packit eed494
    }
Packit eed494
}
Packit eed494
Packit eed494
int httpp_parse_response(http_parser_t *parser, char *http_data, unsigned long len, char *uri)
Packit eed494
{
Packit eed494
    char *data;
Packit eed494
    char *line[MAX_HEADERS];
Packit eed494
    int lines, slen,i, whitespace=0, where=0,code;
Packit eed494
    char *version=NULL, *resp_code=NULL, *message=NULL;
Packit eed494
    
Packit eed494
    if(http_data == NULL)
Packit eed494
        return 0;
Packit eed494
Packit eed494
    /* make a local copy of the data, including 0 terminator */
Packit eed494
    data = (char *)malloc(len+1);
Packit eed494
    if (data == NULL) return 0;
Packit eed494
    memcpy(data, http_data, len);
Packit eed494
    data[len] = 0;
Packit eed494
Packit eed494
    lines = split_headers(data, len, line);
Packit eed494
Packit eed494
    /* In this case, the first line contains:
Packit eed494
     * VERSION RESPONSE_CODE MESSAGE, such as HTTP/1.0 200 OK
Packit eed494
     */
Packit eed494
    slen = strlen(line[0]);
Packit eed494
    version = line[0];
Packit eed494
    for(i=0; i < slen; i++) {
Packit eed494
        if(line[0][i] == ' ') {
Packit eed494
            line[0][i] = 0;
Packit eed494
            whitespace = 1;
Packit eed494
        } else if(whitespace) {
Packit eed494
            whitespace = 0;
Packit eed494
            where++;
Packit eed494
            if(where == 1)
Packit eed494
                resp_code = &line[0][i];
Packit eed494
            else {
Packit eed494
                message = &line[0][i];
Packit eed494
                break;
Packit eed494
            }
Packit eed494
        }
Packit eed494
    }
Packit eed494
Packit eed494
    if(version == NULL || resp_code == NULL || message == NULL) {
Packit eed494
        free(data);
Packit eed494
        return 0;
Packit eed494
    }
Packit eed494
Packit eed494
    httpp_setvar(parser, HTTPP_VAR_ERROR_CODE, resp_code);
Packit eed494
    code = atoi(resp_code);
Packit eed494
    if(code < 200 || code >= 300) {
Packit eed494
        httpp_setvar(parser, HTTPP_VAR_ERROR_MESSAGE, message);
Packit eed494
    }
Packit eed494
Packit eed494
    httpp_setvar(parser, HTTPP_VAR_URI, uri);
Packit eed494
    httpp_setvar(parser, HTTPP_VAR_REQ_TYPE, "NONE");
Packit eed494
Packit eed494
    parse_headers(parser, line, lines);
Packit eed494
Packit eed494
    free(data);
Packit eed494
Packit eed494
    return 1;
Packit eed494
}
Packit eed494
Packit eed494
static int hex(char c)
Packit eed494
{
Packit eed494
    if(c >= '0' && c <= '9')
Packit eed494
        return c - '0';
Packit eed494
    else if(c >= 'A' && c <= 'F')
Packit eed494
        return c - 'A' + 10;
Packit eed494
    else if(c >= 'a' && c <= 'f')
Packit eed494
        return c - 'a' + 10;
Packit eed494
    else
Packit eed494
        return -1;
Packit eed494
}
Packit eed494
Packit eed494
static char *url_escape(char *src)
Packit eed494
{
Packit eed494
    int len = strlen(src);
Packit eed494
    unsigned char *decoded;
Packit eed494
    int i;
Packit eed494
    char *dst;
Packit eed494
    int done = 0;
Packit eed494
Packit eed494
    decoded = calloc(1, len + 1);
Packit eed494
Packit eed494
    dst = decoded;
Packit eed494
Packit eed494
    for(i=0; i < len; i++) {
Packit eed494
        switch(src[i]) {
Packit eed494
        case '%':
Packit eed494
            if(i+2 >= len) {
Packit eed494
                free(decoded);
Packit eed494
                return NULL;
Packit eed494
            }
Packit eed494
            if(hex(src[i+1]) == -1 || hex(src[i+2]) == -1 ) {
Packit eed494
                free(decoded);
Packit eed494
                return NULL;
Packit eed494
            }
Packit eed494
Packit eed494
            *dst++ = hex(src[i+1]) * 16  + hex(src[i+2]);
Packit eed494
            i+= 2;
Packit eed494
            break;
Packit eed494
        case '+':
Packit eed494
            *dst++ = ' ';
Packit eed494
            break;
Packit eed494
        case '#':
Packit eed494
            done = 1;
Packit eed494
            break;
Packit eed494
        case 0:
Packit eed494
            free(decoded);
Packit eed494
            return NULL;
Packit eed494
            break;
Packit eed494
        default:
Packit eed494
            *dst++ = src[i];
Packit eed494
            break;
Packit eed494
        }
Packit eed494
        if(done)
Packit eed494
            break;
Packit eed494
    }
Packit eed494
Packit eed494
    *dst = 0; /* null terminator */
Packit eed494
Packit eed494
    return decoded;
Packit eed494
}
Packit eed494
Packit eed494
/** TODO: This is almost certainly buggy in some cases */
Packit eed494
static void parse_query(http_parser_t *parser, char *query)
Packit eed494
{
Packit eed494
    int len;
Packit eed494
    int i=0;
Packit eed494
    char *key = query;
Packit eed494
    char *val=NULL;
Packit eed494
Packit eed494
    if(!query || !*query)
Packit eed494
        return;
Packit eed494
Packit eed494
    len = strlen(query);
Packit eed494
Packit eed494
    while(i
Packit eed494
        switch(query[i]) {
Packit eed494
        case '&':
Packit eed494
            query[i] = 0;
Packit eed494
            if(val && key)
Packit eed494
                httpp_set_query_param(parser, key, val);
Packit eed494
            key = query+i+1;
Packit eed494
            break;
Packit eed494
        case '=':
Packit eed494
            query[i] = 0;
Packit eed494
            val = query+i+1;
Packit eed494
            break;
Packit eed494
        }
Packit eed494
        i++;
Packit eed494
    }
Packit eed494
Packit eed494
    if(val && key) {
Packit eed494
        httpp_set_query_param(parser, key, val);
Packit eed494
    }
Packit eed494
}
Packit eed494
Packit eed494
int httpp_parse(http_parser_t *parser, char *http_data, unsigned long len)
Packit eed494
{
Packit eed494
    char *data, *tmp;
Packit eed494
    char *line[MAX_HEADERS]; /* limited to 32 lines, should be more than enough */
Packit eed494
    int i;
Packit eed494
    int lines;
Packit eed494
    char *req_type = NULL;
Packit eed494
    char *uri = NULL;
Packit eed494
    char *version = NULL;
Packit eed494
    int whitespace, where, slen;
Packit eed494
Packit eed494
    if (http_data == NULL)
Packit eed494
        return 0;
Packit eed494
Packit eed494
    /* make a local copy of the data, including 0 terminator */
Packit eed494
    data = (char *)malloc(len+1);
Packit eed494
    if (data == NULL) return 0;
Packit eed494
    memcpy(data, http_data, len);
Packit eed494
    data[len] = 0;
Packit eed494
Packit eed494
    lines = split_headers(data, len, line);
Packit eed494
Packit eed494
    /* parse the first line special
Packit eed494
    ** the format is:
Packit eed494
    ** REQ_TYPE URI VERSION
Packit eed494
    ** eg:
Packit eed494
    ** GET /index.html HTTP/1.0
Packit eed494
    */
Packit eed494
    where = 0;
Packit eed494
    whitespace = 0;
Packit eed494
    slen = strlen(line[0]);
Packit eed494
    req_type = line[0];
Packit eed494
    for (i = 0; i < slen; i++) {
Packit eed494
        if (line[0][i] == ' ') {
Packit eed494
            whitespace = 1;
Packit eed494
            line[0][i] = '\0';
Packit eed494
        } else {
Packit eed494
            /* we're just past the whitespace boundry */
Packit eed494
            if (whitespace) {
Packit eed494
                whitespace = 0;
Packit eed494
                where++;
Packit eed494
                switch (where) {
Packit eed494
                case 1:
Packit eed494
                    uri = &line[0][i];
Packit eed494
                    break;
Packit eed494
                case 2:
Packit eed494
                    version = &line[0][i];
Packit eed494
                    break;
Packit eed494
                }
Packit eed494
            }
Packit eed494
        }
Packit eed494
    }
Packit eed494
Packit eed494
    if (strcasecmp("GET", req_type) == 0) {
Packit eed494
        parser->req_type = httpp_req_get;
Packit eed494
    } else if (strcasecmp("POST", req_type) == 0) {
Packit eed494
        parser->req_type = httpp_req_post;
Packit eed494
    } else if (strcasecmp("HEAD", req_type) == 0) {
Packit eed494
        parser->req_type = httpp_req_head;
Packit eed494
    } else if (strcasecmp("SOURCE", req_type) == 0) {
Packit eed494
        parser->req_type = httpp_req_source;
Packit eed494
    } else if (strcasecmp("PLAY", req_type) == 0) {
Packit eed494
        parser->req_type = httpp_req_play;
Packit eed494
    } else if (strcasecmp("STATS", req_type) == 0) {
Packit eed494
        parser->req_type = httpp_req_stats;
Packit eed494
    } else {
Packit eed494
        parser->req_type = httpp_req_unknown;
Packit eed494
    }
Packit eed494
Packit eed494
    if (uri != NULL && strlen(uri) > 0) {
Packit eed494
        char *query;
Packit eed494
        if((query = strchr(uri, '?')) != NULL) {
Packit eed494
            httpp_setvar(parser, HTTPP_VAR_RAWURI, uri);
Packit eed494
            *query = 0;
Packit eed494
            query++;
Packit eed494
            parse_query(parser, query);
Packit eed494
        }
Packit eed494
Packit eed494
        parser->uri = strdup(uri);
Packit eed494
    } else {
Packit eed494
        free(data);
Packit eed494
        return 0;
Packit eed494
    }
Packit eed494
Packit eed494
    if ((version != NULL) && ((tmp = strchr(version, '/')) != NULL)) {
Packit eed494
        tmp[0] = '\0';
Packit eed494
        if ((strlen(version) > 0) && (strlen(&tmp[1]) > 0)) {
Packit eed494
            httpp_setvar(parser, HTTPP_VAR_PROTOCOL, version);
Packit eed494
            httpp_setvar(parser, HTTPP_VAR_VERSION, &tmp[1]);
Packit eed494
        } else {
Packit eed494
            free(data);
Packit eed494
            return 0;
Packit eed494
        }
Packit eed494
    } else {
Packit eed494
        free(data);
Packit eed494
        return 0;
Packit eed494
    }
Packit eed494
Packit eed494
    if (parser->req_type != httpp_req_none && parser->req_type != httpp_req_unknown) {
Packit eed494
        switch (parser->req_type) {
Packit eed494
        case httpp_req_get:
Packit eed494
            httpp_setvar(parser, HTTPP_VAR_REQ_TYPE, "GET");
Packit eed494
            break;
Packit eed494
        case httpp_req_post:
Packit eed494
            httpp_setvar(parser, HTTPP_VAR_REQ_TYPE, "POST");
Packit eed494
            break;
Packit eed494
        case httpp_req_head:
Packit eed494
            httpp_setvar(parser, HTTPP_VAR_REQ_TYPE, "HEAD");
Packit eed494
            break;
Packit eed494
        case httpp_req_source:
Packit eed494
            httpp_setvar(parser, HTTPP_VAR_REQ_TYPE, "SOURCE");
Packit eed494
            break;
Packit eed494
        case httpp_req_play:
Packit eed494
            httpp_setvar(parser, HTTPP_VAR_REQ_TYPE, "PLAY");
Packit eed494
            break;
Packit eed494
        case httpp_req_stats:
Packit eed494
            httpp_setvar(parser, HTTPP_VAR_REQ_TYPE, "STATS");
Packit eed494
            break;
Packit eed494
        default:
Packit eed494
            break;
Packit eed494
        }
Packit eed494
    } else {
Packit eed494
        free(data);
Packit eed494
        return 0;
Packit eed494
    }
Packit eed494
Packit eed494
    if (parser->uri != NULL) {
Packit eed494
        httpp_setvar(parser, HTTPP_VAR_URI, parser->uri);
Packit eed494
    } else {
Packit eed494
        free(data);
Packit eed494
        return 0;
Packit eed494
    }
Packit eed494
Packit eed494
    parse_headers(parser, line, lines);
Packit eed494
Packit eed494
    free(data);
Packit eed494
Packit eed494
    return 1;
Packit eed494
}
Packit eed494
Packit eed494
void httpp_setvar(http_parser_t *parser, const char *name, const char *value)
Packit eed494
{
Packit eed494
    http_var_t *var;
Packit eed494
Packit eed494
    if (name == NULL || value == NULL)
Packit eed494
        return;
Packit eed494
Packit eed494
    var = (http_var_t *)malloc(sizeof(http_var_t));
Packit eed494
    if (var == NULL) return;
Packit eed494
Packit eed494
    var->name = strdup(name);
Packit eed494
    var->value = strdup(value);
Packit eed494
Packit eed494
    if (httpp_getvar(parser, name) == NULL) {
Packit eed494
        avl_insert(parser->vars, (void *)var);
Packit eed494
    } else {
Packit eed494
        avl_delete(parser->vars, (void *)var, _free_vars);
Packit eed494
        avl_insert(parser->vars, (void *)var);
Packit eed494
    }
Packit eed494
}
Packit eed494
Packit eed494
char *httpp_getvar(http_parser_t *parser, const char *name)
Packit eed494
{
Packit eed494
    http_var_t var;
Packit eed494
    http_var_t *found;
Packit eed494
    void *fp;
Packit eed494
Packit eed494
    if (parser == NULL || name == NULL)
Packit eed494
        return NULL;
Packit eed494
Packit eed494
    fp = &found;
Packit eed494
    var.name = (char*)name;
Packit eed494
    var.value = NULL;
Packit eed494
Packit eed494
    if (avl_get_by_key(parser->vars, &var, fp) == 0)
Packit eed494
        return found->value;
Packit eed494
    else
Packit eed494
        return NULL;
Packit eed494
}
Packit eed494
Packit eed494
void httpp_set_query_param(http_parser_t *parser, char *name, char *value)
Packit eed494
{
Packit eed494
    http_var_t *var;
Packit eed494
Packit eed494
    if (name == NULL || value == NULL)
Packit eed494
        return;
Packit eed494
Packit eed494
    var = (http_var_t *)malloc(sizeof(http_var_t));
Packit eed494
    if (var == NULL) return;
Packit eed494
Packit eed494
    var->name = strdup(name);
Packit eed494
    var->value = url_escape(value);
Packit eed494
Packit eed494
    if (httpp_get_query_param(parser, name) == NULL) {
Packit eed494
        avl_insert(parser->queryvars, (void *)var);
Packit eed494
    } else {
Packit eed494
        avl_delete(parser->queryvars, (void *)var, _free_vars);
Packit eed494
        avl_insert(parser->queryvars, (void *)var);
Packit eed494
    }
Packit eed494
}
Packit eed494
Packit eed494
char *httpp_get_query_param(http_parser_t *parser, char *name)
Packit eed494
{
Packit eed494
    http_var_t var;
Packit eed494
    http_var_t *found;
Packit eed494
    void *fp;
Packit eed494
Packit eed494
    fp = &found;
Packit eed494
    var.name = name;
Packit eed494
    var.value = NULL;
Packit eed494
Packit eed494
    if (avl_get_by_key(parser->queryvars, (void *)&var, fp) == 0)
Packit eed494
        return found->value;
Packit eed494
    else
Packit eed494
        return NULL;
Packit eed494
}
Packit eed494
Packit eed494
void httpp_clear(http_parser_t *parser)
Packit eed494
{
Packit eed494
    parser->req_type = httpp_req_none;
Packit eed494
    if (parser->uri)
Packit eed494
        free(parser->uri);
Packit eed494
    parser->uri = NULL;
Packit eed494
    avl_tree_free(parser->vars, _free_vars);
Packit eed494
    avl_tree_free(parser->queryvars, _free_vars);
Packit eed494
    parser->vars = NULL;
Packit eed494
}
Packit eed494
Packit eed494
void httpp_destroy(http_parser_t *parser)
Packit eed494
{
Packit eed494
    httpp_clear(parser);
Packit eed494
    free(parser);
Packit eed494
}
Packit eed494
Packit eed494
static char *_lowercase(char *str)
Packit eed494
{
Packit eed494
    char *p = str;
Packit eed494
    for (; *p != '\0'; p++)
Packit eed494
        *p = tolower(*p);
Packit eed494
Packit eed494
    return str;
Packit eed494
}
Packit eed494
Packit eed494
static int _compare_vars(void *compare_arg, void *a, void *b)
Packit eed494
{
Packit eed494
    http_var_t *vara, *varb;
Packit eed494
Packit eed494
    vara = (http_var_t *)a;
Packit eed494
    varb = (http_var_t *)b;
Packit eed494
Packit eed494
    return strcmp(vara->name, varb->name);
Packit eed494
}
Packit eed494
Packit eed494
static int _free_vars(void *key)
Packit eed494
{
Packit eed494
    http_var_t *var;
Packit eed494
Packit eed494
    var = (http_var_t *)key;
Packit eed494
Packit eed494
    if (var->name)
Packit eed494
        free(var->name);
Packit eed494
    if (var->value)
Packit eed494
        free(var->value);
Packit eed494
    free(var);
Packit eed494
Packit eed494
    return 1;
Packit eed494
}
Packit eed494