424 lines
8.5 KiB
C
424 lines
8.5 KiB
C
/*
|
|
* Copyright 2010 John-Mark Bell <jmb@netsurf-browser.org>
|
|
*
|
|
* 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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
/** \file
|
|
* HTTP header parsing functions
|
|
*/
|
|
|
|
#include <inttypes.h>
|
|
#include <stdbool.h>
|
|
#include <stddef.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#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;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* <type> + <subtype> + '/' */
|
|
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_parse_content_disposition(const char *header_value,
|
|
char **disposition_type, http_parameter **parameters)
|
|
{
|
|
const char *pos = header_value;
|
|
char *type;
|
|
http_parameter *params = NULL;
|
|
nserror error;
|
|
|
|
/* disposition-type *( ";" 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 == ';') {
|
|
error = http_parse_parameter_list(&pos, ¶ms);
|
|
if (error != NSERROR_OK) {
|
|
free(type);
|
|
return error;
|
|
}
|
|
}
|
|
|
|
*disposition_type = type;
|
|
*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);
|
|
}
|
|
}
|
|
|