mirror of
https://gitlab.xiph.org/xiph/icecast-server.git
synced 2025-01-03 14:56:34 -05:00
Feature: Added parser for Accept*:-headers
This commit is contained in:
parent
3b55f16977
commit
0db820c9dd
444
src/util.c
444
src/util.c
@ -777,6 +777,450 @@ ssize_t util_http_build_header(char * out, size_t len, ssize_t offset,
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define __SELECT_BEST_MAX_ARGS 8
|
||||
|
||||
struct __args {
|
||||
const char *comp;
|
||||
char *group;
|
||||
int best_comp;
|
||||
int best_group;
|
||||
int best_all;
|
||||
};
|
||||
|
||||
static inline int __fill_arg(struct __args *arg, const char *str)
|
||||
{
|
||||
char *delm;
|
||||
size_t len;
|
||||
|
||||
arg->comp = str;
|
||||
arg->best_comp = 0;
|
||||
arg->best_group = 0;
|
||||
arg->best_all = 0;
|
||||
|
||||
len = strlen(str);
|
||||
arg->group = malloc(len + 2);
|
||||
if (!arg->group)
|
||||
return -1;
|
||||
|
||||
memcpy(arg->group, str, len + 1);
|
||||
|
||||
delm = strstr(arg->group, "/");
|
||||
if (delm) {
|
||||
delm[0] = '/';
|
||||
delm[1] = '*';
|
||||
delm[2] = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void __free_args(struct __args *arg, size_t len)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
free(arg->group);
|
||||
}
|
||||
}
|
||||
|
||||
// Accept: text/html, application/xhtml+xml, */*
|
||||
// Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
|
||||
|
||||
static inline int __parse_q(const char *str)
|
||||
{
|
||||
int ret = 0;
|
||||
int mul = 1000;
|
||||
|
||||
for (; *str; str++) {
|
||||
if (*str >= '0' && *str <= '9') {
|
||||
ret += mul * (*str - '0');
|
||||
mul /= 10;
|
||||
} else if (*str == '.') {
|
||||
mul = 100;
|
||||
} else {
|
||||
ICECAST_LOG_ERROR("Badly formated quality parameter found.");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int __find_q_in_index(icecast_kva_t *kv, size_t idx)
|
||||
{
|
||||
size_t i = kv->index[idx] + 1;
|
||||
size_t last;
|
||||
|
||||
if (kv->indexlen <= (idx + 1)) {
|
||||
last = kv->kvlen - 1;
|
||||
} else {
|
||||
last = kv->index[idx + 1] - 1;
|
||||
}
|
||||
|
||||
for (; i <= last; i++) {
|
||||
if (kv->kv[i].key && kv->kv[i].key && strcasecmp(kv->kv[i].key, "q") == 0) {
|
||||
return __parse_q(kv->kv[i].value);
|
||||
}
|
||||
}
|
||||
|
||||
return 1000;
|
||||
}
|
||||
|
||||
const char *util_http_select_best(const char *input, const char *first, ...)
|
||||
{
|
||||
struct __args arg[__SELECT_BEST_MAX_ARGS];
|
||||
icecast_kva_t *kv;
|
||||
const char *p;
|
||||
size_t arglen = 1;
|
||||
size_t i, h;
|
||||
va_list ap;
|
||||
int q;
|
||||
int best_q = 0;
|
||||
|
||||
if (__fill_arg(&(arg[0]), first) == -1) {
|
||||
ICECAST_LOG_ERROR("Can not allocate memory. Selecting first option.");
|
||||
return first;
|
||||
}
|
||||
va_start(ap, first);
|
||||
while ((p = (const char*)va_arg(ap, const char*))) {
|
||||
if (arglen == __SELECT_BEST_MAX_ARGS) {
|
||||
ICECAST_LOG_ERROR("More arguments given than supported. Currently %zu args are supported.", (size_t)__SELECT_BEST_MAX_ARGS);
|
||||
break;
|
||||
}
|
||||
if (__fill_arg(&(arg[arglen]), p) == -1) {
|
||||
ICECAST_LOG_ERROR("Can not allocate memory. Selecting first option.");
|
||||
__free_args(arg, arglen);
|
||||
return first;
|
||||
}
|
||||
|
||||
arglen++;
|
||||
}
|
||||
va_end(ap);
|
||||
|
||||
kv = util_parse_http_cn(input);
|
||||
if (!kv) {
|
||||
ICECAST_LOG_ERROR("Input string does not parse as KVA. Selecting first option.");
|
||||
__free_args(arg, arglen);
|
||||
return first;
|
||||
}
|
||||
|
||||
ICECAST_LOG_DEBUG("--- DUMP ---");
|
||||
for (i = 0; i < kv->kvlen; i++) {
|
||||
ICECAST_LOG_DEBUG("kv[%zu] = {.key='%H', .value='%H'}", i, kv->kv[i].key, kv->kv[i].value);
|
||||
}
|
||||
for (i = 0; i < kv->indexlen; i++) {
|
||||
ICECAST_LOG_DEBUG("index[%zu] = %zu", i, kv->index[i]);
|
||||
}
|
||||
ICECAST_LOG_DEBUG("--- END OF DUMP ---");
|
||||
|
||||
for (h = 0; h < arglen; h++) {
|
||||
for (i = 0; i < kv->indexlen; i++) {
|
||||
p = kv->kv[kv->index[i]].key;
|
||||
if (!p) {
|
||||
continue;
|
||||
}
|
||||
|
||||
q = __find_q_in_index(kv, i);
|
||||
if (best_q < q) {
|
||||
best_q = q;
|
||||
}
|
||||
|
||||
if (strcasecmp(p, arg[h].comp) == 0) {
|
||||
if (arg[h].best_comp < q) {
|
||||
arg[h].best_comp = q;
|
||||
}
|
||||
}
|
||||
|
||||
if (strcasecmp(p, arg[h].group) == 0) {
|
||||
if (arg[h].best_group < q) {
|
||||
arg[h].best_group = q;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
util_kva_free(kv);
|
||||
|
||||
p = NULL;
|
||||
for (h = 0; p == NULL && h < arglen; h++) {
|
||||
if (arg[h].best_comp == best_q) {
|
||||
p = arg[h].comp;
|
||||
}
|
||||
}
|
||||
for (h = 0; p == NULL && h < arglen; h++) {
|
||||
if (arg[h].best_group == best_q) {
|
||||
p = arg[h].comp;
|
||||
}
|
||||
}
|
||||
|
||||
__free_args(arg, arglen);
|
||||
|
||||
if (p == NULL) {
|
||||
p = first;
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
static inline void __skip_space(char **p)
|
||||
{
|
||||
for (; **p == ' '; (*p)++);
|
||||
}
|
||||
|
||||
static inline int __is_token(const char p)
|
||||
{
|
||||
return (p >= 'a' && p <= 'z') || (p >= 'A' && p <= 'Z') || (p >= '0' && p <= '9') ||
|
||||
p == '!' || p == '#' || p == '$' || p == '%' || p == '&' || p == '\'' ||
|
||||
p == '*' || p == '+' || p == '-' || p == '.' || p == '^' || p == '_' ||
|
||||
p == '|' || p == '~' || p == '/';
|
||||
|
||||
}
|
||||
|
||||
enum __tokenizer_result {
|
||||
__TOKENIZER_RESULT_COMMA,
|
||||
__TOKENIZER_RESULT_EQ,
|
||||
__TOKENIZER_RESULT_SEMICOLON,
|
||||
__TOKENIZER_RESULT_ILSEQ,
|
||||
__TOKENIZER_RESULT_EOS
|
||||
};
|
||||
|
||||
static inline enum __tokenizer_result __tokenizer_res_from_char(const char p)
|
||||
{
|
||||
switch (p) {
|
||||
case 0:
|
||||
return __TOKENIZER_RESULT_EOS;
|
||||
break;
|
||||
case ',':
|
||||
return __TOKENIZER_RESULT_COMMA;
|
||||
break;
|
||||
case '=':
|
||||
return __TOKENIZER_RESULT_EQ;
|
||||
break;
|
||||
case ';':
|
||||
return __TOKENIZER_RESULT_SEMICOLON;
|
||||
break;
|
||||
default:
|
||||
return __TOKENIZER_RESULT_ILSEQ;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static enum __tokenizer_result __tokenizer_str(char **out, char **in)
|
||||
{
|
||||
char *p, *o;
|
||||
char c;
|
||||
|
||||
__skip_space(in);
|
||||
|
||||
p = *in;
|
||||
if (*p != '"')
|
||||
return __TOKENIZER_RESULT_ILSEQ;
|
||||
|
||||
p++;
|
||||
o = p;
|
||||
|
||||
for (; (c = *p); p++) {
|
||||
if (c == '\t' || c == ' ' || c == 0x21 || (c >= 0x23 && c <= 0x5B) || (c >= 0x5D && c <= 0x7E) || (c >= 0x80 && c <= 0xFF)) {
|
||||
*(o++) = c;
|
||||
} else if (c == '\\') {
|
||||
p++;
|
||||
c = *p;
|
||||
if (c == 0) {
|
||||
return __TOKENIZER_RESULT_ILSEQ;
|
||||
} else {
|
||||
*(o++) = c;
|
||||
}
|
||||
} else if (c == '"') {
|
||||
*o = 0;
|
||||
break;
|
||||
} else {
|
||||
return __TOKENIZER_RESULT_ILSEQ;
|
||||
}
|
||||
}
|
||||
|
||||
if (*p == '"') {
|
||||
p++;
|
||||
*in = p + 1;
|
||||
__skip_space(in);
|
||||
return __tokenizer_res_from_char(*p);
|
||||
} else {
|
||||
return __TOKENIZER_RESULT_ILSEQ;
|
||||
}
|
||||
}
|
||||
|
||||
static enum __tokenizer_result __tokenizer(char **out, char **in)
|
||||
{
|
||||
char *p;
|
||||
char c = 0;
|
||||
|
||||
__skip_space(in);
|
||||
|
||||
p = *in;
|
||||
|
||||
switch (*p) {
|
||||
case '=':
|
||||
/* fall through */
|
||||
case ',':
|
||||
/* fall through */
|
||||
case ';':
|
||||
return __TOKENIZER_RESULT_ILSEQ;
|
||||
break;
|
||||
case 0:
|
||||
return __TOKENIZER_RESULT_EOS;
|
||||
break;
|
||||
case '"':
|
||||
return __tokenizer_str(out, in);
|
||||
break;
|
||||
}
|
||||
|
||||
*out = p;
|
||||
for (; __is_token(*p); p++);
|
||||
|
||||
*in = p;
|
||||
if (*p) {
|
||||
__skip_space(in);
|
||||
c = **in;
|
||||
if (c != 0) {
|
||||
(*in)++;
|
||||
}
|
||||
}
|
||||
*p = 0;
|
||||
|
||||
return __tokenizer_res_from_char(c);
|
||||
}
|
||||
|
||||
#define HTTP_CN_INDEX_INCREMENT 8
|
||||
#define HTTP_CN_KV_INCREMENT 8
|
||||
|
||||
static inline int __resize_array(void **also_ptr, void **array, size_t size, size_t *len, size_t revlen, size_t inc)
|
||||
{
|
||||
void *n;
|
||||
|
||||
if (*len > revlen)
|
||||
return 0;
|
||||
|
||||
n = realloc(*array, size*(*len + inc));
|
||||
if (!n) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(n + size * *len, 0, size * inc);
|
||||
|
||||
*also_ptr = *array = n;
|
||||
*len += inc;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
icecast_kva_t * util_parse_http_cn(const char *cnstr)
|
||||
{
|
||||
icecast_kva_t *ret;
|
||||
char *in;
|
||||
int eos = 0;
|
||||
size_t indexphylen = HTTP_CN_INDEX_INCREMENT;
|
||||
size_t kvphylen = HTTP_CN_KV_INCREMENT;
|
||||
|
||||
if (!cnstr || !*cnstr)
|
||||
return NULL;
|
||||
|
||||
ret = calloc(1, sizeof(*ret));
|
||||
|
||||
if (!ret)
|
||||
return NULL;
|
||||
|
||||
ret->_tofree[0] = in = strdup(cnstr);
|
||||
ret->_tofree[1] = ret->index = calloc(HTTP_CN_INDEX_INCREMENT, sizeof(*(ret->index)));
|
||||
ret->_tofree[2] = ret->kv = calloc(HTTP_CN_KV_INCREMENT, sizeof(*(ret->kv)));
|
||||
if (!ret->_tofree[0] || !ret->_tofree[1] || !ret->_tofree[2]) {
|
||||
util_kva_free(ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/* we have at minimum one token */
|
||||
ret->indexlen = 1;
|
||||
ret->kvlen = 1;
|
||||
|
||||
while (!eos) {
|
||||
char *out = NULL;
|
||||
enum __tokenizer_result res = __tokenizer(&out, &in);
|
||||
|
||||
switch (res) {
|
||||
case __TOKENIZER_RESULT_ILSEQ:
|
||||
ICECAST_LOG_DEBUG("Illegal byte sequence error from tokenizer.");
|
||||
util_kva_free(ret);
|
||||
return NULL;
|
||||
break;
|
||||
case __TOKENIZER_RESULT_EOS:
|
||||
/* fall through */
|
||||
case __TOKENIZER_RESULT_COMMA:
|
||||
/* fall through */
|
||||
case __TOKENIZER_RESULT_EQ:
|
||||
/* fall through */
|
||||
case __TOKENIZER_RESULT_SEMICOLON:
|
||||
ICECAST_LOG_DEBUG("OK from tokenizer.");
|
||||
/* no-op */
|
||||
break;
|
||||
}
|
||||
|
||||
if (__resize_array(&(ret->_tofree[2]), (void**)&(ret->kv), sizeof(*(ret->kv)), &kvphylen, ret->kvlen, HTTP_CN_KV_INCREMENT) == -1 ||
|
||||
__resize_array(&(ret->_tofree[1]), (void**)&(ret->index), sizeof(*(ret->index)), &indexphylen, ret->indexlen, HTTP_CN_INDEX_INCREMENT) == -1) {
|
||||
util_kva_free(ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (ret->kv[ret->kvlen-1].key == NULL) {
|
||||
ret->kv[ret->kvlen-1].key = out;
|
||||
} else if (ret->kv[ret->kvlen-1].value == NULL) {
|
||||
ret->kv[ret->kvlen-1].value = out;
|
||||
} else {
|
||||
util_kva_free(ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
switch (res) {
|
||||
case __TOKENIZER_RESULT_EOS:
|
||||
ICECAST_LOG_DEBUG("End of string from tokenizer.");
|
||||
eos = 1;
|
||||
continue;
|
||||
break;
|
||||
case __TOKENIZER_RESULT_COMMA:
|
||||
ICECAST_LOG_DEBUG("Comma from tokenizer.");
|
||||
ret->index[ret->indexlen++] = ret->kvlen;
|
||||
ret->kvlen++;
|
||||
break;
|
||||
case __TOKENIZER_RESULT_EQ:
|
||||
ICECAST_LOG_DEBUG("Eq from tokenizer.");
|
||||
/* no-op */
|
||||
break;
|
||||
case __TOKENIZER_RESULT_SEMICOLON:
|
||||
ICECAST_LOG_DEBUG("Semicolon from tokenizer.");
|
||||
ret->kvlen++;
|
||||
break;
|
||||
default:
|
||||
util_kva_free(ret);
|
||||
return NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
ICECAST_LOG_DEBUG("next...");
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void util_kva_free(icecast_kva_t *kva)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
if (!kva)
|
||||
return;
|
||||
|
||||
for (i = 0; i < (sizeof(kva->_tofree)/sizeof(*(kva->_tofree))); i++)
|
||||
free(kva->_tofree[i]);
|
||||
|
||||
free(kva);
|
||||
}
|
||||
|
||||
util_dict *util_dict_new(void)
|
||||
{
|
||||
|
18
src/util.h
18
src/util.h
@ -88,6 +88,24 @@ ssize_t util_http_build_header(char * out, size_t len, ssize_t offset,
|
||||
struct source_tag * source,
|
||||
struct _client_tag * client);
|
||||
|
||||
const char *util_http_select_best(const char *input, const char *first, ...);
|
||||
|
||||
typedef struct icecast_kv_tag {
|
||||
char *key;
|
||||
char *value;
|
||||
} icecast_kv_t;
|
||||
|
||||
typedef struct icecast_kva_tag {
|
||||
void *_tofree[3];
|
||||
size_t kvlen;
|
||||
size_t indexlen;
|
||||
size_t *index;
|
||||
icecast_kv_t *kv;
|
||||
} icecast_kva_t;
|
||||
|
||||
icecast_kva_t * util_parse_http_cn(const char *cnstr);
|
||||
void util_kva_free(icecast_kva_t *kva);
|
||||
|
||||
/* String dictionary type, without support for NULL keys, or multiple
|
||||
* instances of the same key */
|
||||
typedef struct _util_dict {
|
||||
|
Loading…
Reference in New Issue
Block a user