Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
What's new
10
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Open sidebar
Xiph.Org
Icecast-Server
Commits
0db820c9
Commit
0db820c9
authored
Apr 28, 2018
by
Philipp Schafft
🦁
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Feature: Added parser for Accept*:-headers
parent
3b55f169
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
462 additions
and
0 deletions
+462
-0
src/util.c
src/util.c
+444
-0
src/util.h
src/util.h
+18
-0
No files found.
src/util.c
View file @
0db820c9
...
...
@@ -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
)
{
...
...
src/util.h
View file @
0db820c9
...
...
@@ -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
{
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment