/* * Copyright 2010 John-Mark Bell * * This file is part of NetSurf, http://www.netsurf-browser.org/ * * NetSurf is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2 of the License. * * NetSurf 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 program. If not, see . */ /** \file * HTTP header parsing functions */ #include #include #include #include #include #include #include "utils/http.h" /** * Representation of an HTTP parameter */ struct http_parameter { struct http_parameter *next; /**< Next parameter in list, or NULL */ char *name; /**< Parameter name */ char *value; /**< Parameter value */ }; /** * Determine if a character is valid for an HTTP token * * \param c Character to consider * \return True if character is valid, false otherwise */ static bool http_is_token_char(uint8_t c) { /* [ 32 - 126 ] except ()<>@,;:\"/[]?={} SP HT */ if (c <= ' ' || 126 < c) return false; return (strchr("()<>@,;:\\\"/[]?={}", c) == NULL); } /** * Parse an HTTP token * * \param input Pointer to current input byte. Updated on exit. * \param value Pointer to location to receive on-heap token value. * \return NSERROR_OK on success, * NSERROR_NOMEM on memory exhaustion * * The returned value is owned by the caller */ static nserror http_parse_token(const char **input, char **value) { const uint8_t *start = (const uint8_t *) *input; const uint8_t *end; char *token; end = start; while (http_is_token_char(*end)) end++; token = malloc(end - start + 1); if (token == NULL) return NSERROR_NOMEM; memcpy(token, start, end - start); token[end - start] = '\0'; *value = token; *input = (const char *) end; return NSERROR_OK; } /** * Parse an HTTP quoted-string * * \param input Pointer to current input byte. Updated on exit. * \param value Pointer to location to receive on-heap string value. * \return NSERROR_OK on success, * NSERROR_NOMEM on memory exhaustion * * The returned value is owned by the caller */ static nserror http_parse_quoted_string(const char **input, char **value) { const uint8_t *start = (const uint8_t *) *input; const uint8_t *end; uint8_t c; char *string_value; /* <"> *( qdtext | quoted-pair ) <"> * qdtext = any TEXT except <"> * quoted-pair = "\" CHAR * TEXT = [ HT, CR, LF, 32-126, 128-255 ] * CHAR = [ 0 - 127 ] * * \todo TEXT may contain non 8859-1 chars encoded per RFC 2047 * \todo Support quoted-pairs */ if (*start == '"') { end = start = start + 1; c = *end; while (c == '\t' || c == '\r' || c == '\n' || c == ' ' || c == '!' || ('#' <= c && c <= 126) || c > 127) { end++; c = *end; } if (*end != '"') { start--; end = start; } } else { end = start; } string_value = malloc(end - start + 1); if (string_value == NULL) return NSERROR_NOMEM; memcpy(string_value, start, end - start); string_value[end - start] = '\0'; *value = string_value; if (end != start) *input = (const char *) end + 1; return NSERROR_OK; } /** * Parse an HTTP parameter * * \param input Pointer to current input byte. Updated on exit. * \param parameter Pointer to location to receive on-heap parameter. * \return NSERROR_OK on success, * NSERROR_NOMEM on memory exhaustion * * The returned parameter is owned by the caller. */ static nserror http_parse_parameter(const char **input, http_parameter **parameter) { const char *pos = *input; char *name; char *value; http_parameter *param; nserror error; /* token "=" ( token | quoted-string ) */ error = http_parse_token(&pos, &name); if (error != NSERROR_OK) return error; while (*pos == ' ' || *pos == '\t') pos++; if (*pos != '=') { value = strdup(""); if (value == NULL) { free(name); return NSERROR_NOMEM; } } else { pos++; while (*pos == ' ' || *pos == '\t') pos++; if (*pos == '"') error = http_parse_quoted_string(&pos, &value); else error = http_parse_token(&pos, &value); if (error != NSERROR_OK) { free(name); return error; } } param = malloc(sizeof(*param)); if (param == NULL) { free(value); free(name); return NSERROR_NOMEM; } param->next = NULL; param->name = name; param->value = value; *parameter = param; *input = pos; return NSERROR_OK; } /** * Parse an HTTP parameter list * * \param input Pointer to current input byte. Updated on exit. * \param parameters Pointer to location to receive on-heap parameter list. * \return NSERROR_OK on success, * NSERROR_NOMEM on memory exhaustion * * The returned parameter list is owned by the caller */ static nserror http_parse_parameter_list(const char **input, http_parameter **parameters) { const char *pos = *input; http_parameter *param; http_parameter *list = NULL; nserror error; /* 1*( ";" parameter ) */ while (*pos == ';') { pos++; while (*pos == ' ' || *pos == '\t') pos++; error = http_parse_parameter(&pos, ¶m); if (error != NSERROR_OK) { while (list != NULL) { param = list; list = param->next; free(param->name); free(param->value); free(param); } return error; } if (list != NULL) param->next = list; list = param; while (*pos == ' ' || *pos == '\t') pos++; } *parameters = list; *input = pos; return NSERROR_OK; } /* See http.h for documentation */ nserror http_parse_content_type(const char *header_value, char **media_type, http_parameter **parameters) { const char *pos = header_value; char *type; char *subtype = NULL; http_parameter *params = NULL; char *mime; size_t mime_len; nserror error; /* type "/" subtype *( ";" parameter ) */ while (*pos == ' ' || *pos == '\t') pos++; error = http_parse_token(&pos, &type); if (error != NSERROR_OK) return error; while (*pos == ' ' || *pos == '\t') pos++; if (*pos == '/') { pos++; while (*pos == ' ' || *pos == '\t') pos++; error = http_parse_token(&pos, &subtype); if (error != NSERROR_OK) { free(type); return error; } while (*pos == ' ' || *pos == '\t') pos++; if (*pos == ';') { error = http_parse_parameter_list(&pos, ¶ms); if (error != NSERROR_OK) { free(subtype); free(type); return error; } } } /* + + '/' */ mime_len = strlen(type) + (subtype != NULL ? strlen(subtype) : 0) + 1; mime = malloc(mime_len + 1); if (mime == NULL) { http_parameter_list_destroy(params); free(subtype); free(type); return NSERROR_OK; } sprintf(mime, "%s/%s", type, subtype != NULL ? subtype : ""); free(subtype); free(type); *media_type = mime; *parameters = params; return NSERROR_OK; } /* See http.h for documentation */ nserror http_parameter_list_find_item(const http_parameter *list, const char *name, const char **value) { while (list != NULL && strcasecmp(name, list->name) != 0) list = list->next; if (list == NULL) return NSERROR_NOT_FOUND; *value = list->value; return NSERROR_OK; } /* See http.h for documentation */ const http_parameter *http_parameter_list_iterate(const http_parameter *cur, const char **name, const char **value) { if (cur == NULL) return NULL; *name = cur->name; *value = cur->value; return cur->next; } /* See http.h for documentation */ void http_parameter_list_destroy(http_parameter *list) { while (list != NULL) { http_parameter *victim = list; list = victim->next; free(victim->name); free(victim->value); free(victim); } }