mirror of
https://gitlab.xiph.org/xiph/icecast-server.git
synced 2024-12-04 14:46:30 -05:00
mp3 metadata complete. Still untested.
svn path=/trunk/httpp/; revision=4191
This commit is contained in:
parent
5275ab34b9
commit
ab8c8c6893
10
src/client.c
10
src/client.c
@ -45,6 +45,16 @@ void client_destroy(client_t *client)
|
|||||||
free(client);
|
free(client);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void client_send_400(client_t *client, char *message) {
|
||||||
|
int bytes;
|
||||||
|
bytes = sock_write(client->con->sock, "HTTP/1.0 404 File Not Found\r\n"
|
||||||
|
"Content-Type: text/html\r\n\r\n"
|
||||||
|
"<b>%s</b>\r\n", message);
|
||||||
|
if(bytes > 0) client->con->sent_bytes = bytes;
|
||||||
|
client->respcode = 404;
|
||||||
|
client_destroy(client);
|
||||||
|
}
|
||||||
|
|
||||||
void client_send_404(client_t *client, char *message) {
|
void client_send_404(client_t *client, char *message) {
|
||||||
|
|
||||||
int bytes;
|
int bytes;
|
||||||
|
@ -31,5 +31,6 @@ client_t *client_create(connection_t *con, http_parser_t *parser);
|
|||||||
void client_destroy(client_t *client);
|
void client_destroy(client_t *client);
|
||||||
void client_send_404(client_t *client, char *message);
|
void client_send_404(client_t *client, char *message);
|
||||||
void client_send_401(client_t *client);
|
void client_send_401(client_t *client);
|
||||||
|
void client_send_400(client_t *client, char *message);
|
||||||
|
|
||||||
#endif /* __CLIENT_H__ */
|
#endif /* __CLIENT_H__ */
|
||||||
|
@ -36,6 +36,7 @@
|
|||||||
#include "fserve.h"
|
#include "fserve.h"
|
||||||
#include "source.h"
|
#include "source.h"
|
||||||
#include "format.h"
|
#include "format.h"
|
||||||
|
#include "format_mp3.h"
|
||||||
|
|
||||||
#define CATMODULE "connection"
|
#define CATMODULE "connection"
|
||||||
|
|
||||||
@ -418,6 +419,65 @@ static int _check_source_pass(http_parser_t *parser)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void handle_metadata_request(client_t *client)
|
||||||
|
{
|
||||||
|
source_t *source;
|
||||||
|
char *action;
|
||||||
|
char *mount;
|
||||||
|
char *value;
|
||||||
|
mp3_state *state;
|
||||||
|
int bytes;
|
||||||
|
|
||||||
|
if(!_check_source_pass(client->parser)) {
|
||||||
|
INFO0("Metadata request with wrong or missing password");
|
||||||
|
client_send_401(client);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
action = httpp_get_query_param(client->parser, "mode");
|
||||||
|
mount = httpp_get_query_param(client->parser, "mount");
|
||||||
|
value = httpp_get_query_param(client->parser, "song");
|
||||||
|
|
||||||
|
if(value == NULL || action == NULL || mount == NULL) {
|
||||||
|
client_send_400(client, "Missing parameter");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
avl_tree_rlock(global.source_tree);
|
||||||
|
source = source_find_mount(mount);
|
||||||
|
avl_tree_unlock(global.source_tree);
|
||||||
|
|
||||||
|
if(source == NULL) {
|
||||||
|
client_send_400(client, "No such mountpoint");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(source->format->type != FORMAT_TYPE_MP3) {
|
||||||
|
client_send_400(client, "Not mp3, cannot update metadata");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(strcmp(action, "updinfo") != 0) {
|
||||||
|
client_send_400(client, "No such action");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
state = source->format->_state;
|
||||||
|
thread_mutex_lock(&(state->lock));
|
||||||
|
free(state->metadata);
|
||||||
|
state->metadata = strdup(value);
|
||||||
|
state->metadata_age++;
|
||||||
|
thread_mutex_unlock(&(state->lock));
|
||||||
|
|
||||||
|
DEBUG2("Metadata on mountpoint %s changed to \"%s\"", mount, value);
|
||||||
|
client->respcode = 200;
|
||||||
|
bytes = sock_write(client->con->sock,
|
||||||
|
"HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n"
|
||||||
|
"Update successful");
|
||||||
|
if(bytes > 0) client->con->sent_bytes = bytes;
|
||||||
|
client_destroy(client);
|
||||||
|
}
|
||||||
|
|
||||||
static void _handle_source_request(connection_t *con,
|
static void _handle_source_request(connection_t *con,
|
||||||
http_parser_t *parser, char *uri)
|
http_parser_t *parser, char *uri)
|
||||||
{
|
{
|
||||||
@ -500,7 +560,7 @@ static void _handle_get_request(connection_t *con,
|
|||||||
** aren't subject to the limits.
|
** aren't subject to the limits.
|
||||||
*/
|
*/
|
||||||
/* TODO: add GUID-xxxxxx */
|
/* TODO: add GUID-xxxxxx */
|
||||||
if (strcmp(uri, "/stats.xml") == 0) {
|
if (strcmp(uri, "/admin/stats.xml") == 0) {
|
||||||
if (!_check_source_pass(parser)) {
|
if (!_check_source_pass(parser)) {
|
||||||
INFO0("Request for stats.xml with incorrect or no password");
|
INFO0("Request for stats.xml with incorrect or no password");
|
||||||
client_send_401(client);
|
client_send_401(client);
|
||||||
@ -512,6 +572,12 @@ static void _handle_get_request(connection_t *con,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(strcmp(uri, "/admin/metadata") == 0) {
|
||||||
|
DEBUG0("Got metadata update request");
|
||||||
|
handle_metadata_request(client);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/* Here we are parsing the URI request to see
|
/* Here we are parsing the URI request to see
|
||||||
** if the extension is .xsl, if so, then process
|
** if the extension is .xsl, if so, then process
|
||||||
** this request as an XSLT request
|
** this request as an XSLT request
|
||||||
|
@ -40,6 +40,7 @@ void httpp_initialize(http_parser_t *parser, http_varlist_t *defaults)
|
|||||||
parser->req_type = httpp_req_none;
|
parser->req_type = httpp_req_none;
|
||||||
parser->uri = NULL;
|
parser->uri = NULL;
|
||||||
parser->vars = avl_tree_new(_compare_vars, NULL);
|
parser->vars = avl_tree_new(_compare_vars, NULL);
|
||||||
|
parser->queryvars = avl_tree_new(_compare_vars, NULL);
|
||||||
|
|
||||||
/* now insert the default variables */
|
/* now insert the default variables */
|
||||||
list = defaults;
|
list = defaults;
|
||||||
@ -178,6 +179,100 @@ int httpp_parse_response(http_parser_t *parser, char *http_data, unsigned long l
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int hex(char c)
|
||||||
|
{
|
||||||
|
if(c >= '0' && c <= '9')
|
||||||
|
return c - '0';
|
||||||
|
else if(c >= 'A' && c <= 'F')
|
||||||
|
return c - 'A' + 10;
|
||||||
|
else if(c >= 'a' && c <= 'f')
|
||||||
|
return c - 'a' + 10;
|
||||||
|
else
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *url_escape(char *src)
|
||||||
|
{
|
||||||
|
int len = strlen(src);
|
||||||
|
unsigned char *decoded;
|
||||||
|
int i;
|
||||||
|
char *dst;
|
||||||
|
int done = 0;
|
||||||
|
|
||||||
|
decoded = calloc(1, len + 1);
|
||||||
|
|
||||||
|
dst = decoded;
|
||||||
|
|
||||||
|
for(i=0; i < len; i++) {
|
||||||
|
switch(src[i]) {
|
||||||
|
case '%':
|
||||||
|
if(i+2 >= len) {
|
||||||
|
free(decoded);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if(hex(src[i+1]) == -1 || hex(src[i+2]) == -1 ) {
|
||||||
|
free(decoded);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
*dst++ = hex(src[i+1]) * 16 + hex(src[i+2]);
|
||||||
|
i+= 2;
|
||||||
|
break;
|
||||||
|
case '#':
|
||||||
|
done = 1;
|
||||||
|
break;
|
||||||
|
case 0:
|
||||||
|
free(decoded);
|
||||||
|
return NULL;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
*dst++ = src[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(done)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
*dst = 0; /* null terminator */
|
||||||
|
|
||||||
|
return decoded;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** TODO: This is almost certainly buggy in some cases */
|
||||||
|
static void parse_query(http_parser_t *parser, char *query)
|
||||||
|
{
|
||||||
|
int len;
|
||||||
|
int i=0;
|
||||||
|
char *key = query;
|
||||||
|
char *val=NULL;
|
||||||
|
|
||||||
|
if(!query || !*query)
|
||||||
|
return;
|
||||||
|
|
||||||
|
len = strlen(query);
|
||||||
|
|
||||||
|
while(i<len) {
|
||||||
|
switch(query[i]) {
|
||||||
|
case '&':
|
||||||
|
query[i] = 0;
|
||||||
|
if(val && key) {
|
||||||
|
httpp_set_query_param(parser, key, val);
|
||||||
|
}
|
||||||
|
key = query+i+1;
|
||||||
|
break;
|
||||||
|
case '=':
|
||||||
|
query[i] = 0;
|
||||||
|
val = query+i+1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(val && key) {
|
||||||
|
httpp_set_query_param(parser, key, val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int httpp_parse(http_parser_t *parser, char *http_data, unsigned long len)
|
int httpp_parse(http_parser_t *parser, char *http_data, unsigned long len)
|
||||||
{
|
{
|
||||||
char *data, *tmp;
|
char *data, *tmp;
|
||||||
@ -248,7 +343,16 @@ int httpp_parse(http_parser_t *parser, char *http_data, unsigned long len)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (uri != NULL && strlen(uri) > 0)
|
if (uri != NULL && strlen(uri) > 0)
|
||||||
|
{
|
||||||
|
char *query;
|
||||||
|
if((query = strchr(uri, '?')) != NULL) {
|
||||||
|
*query = 0;
|
||||||
|
query++;
|
||||||
|
parse_query(parser, query);
|
||||||
|
}
|
||||||
|
|
||||||
parser->uri = strdup(uri);
|
parser->uri = strdup(uri);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
parser->uri = NULL;
|
parser->uri = NULL;
|
||||||
|
|
||||||
@ -343,6 +447,41 @@ char *httpp_getvar(http_parser_t *parser, char *name)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void httpp_set_query_param(http_parser_t *parser, char *name, char *value)
|
||||||
|
{
|
||||||
|
http_var_t *var;
|
||||||
|
|
||||||
|
if (name == NULL || value == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var = (http_var_t *)malloc(sizeof(http_var_t));
|
||||||
|
if (var == NULL) return;
|
||||||
|
|
||||||
|
var->name = strdup(name);
|
||||||
|
var->value = url_escape(value);
|
||||||
|
|
||||||
|
if (httpp_get_query_param(parser, name) == NULL) {
|
||||||
|
avl_insert(parser->queryvars, (void *)var);
|
||||||
|
} else {
|
||||||
|
avl_delete(parser->queryvars, (void *)var, _free_vars);
|
||||||
|
avl_insert(parser->queryvars, (void *)var);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
char *httpp_get_query_param(http_parser_t *parser, char *name)
|
||||||
|
{
|
||||||
|
http_var_t var;
|
||||||
|
http_var_t *found;
|
||||||
|
|
||||||
|
var.name = name;
|
||||||
|
var.value = NULL;
|
||||||
|
|
||||||
|
if (avl_get_by_key(parser->queryvars, (void *)&var, (void **)&found) == 0)
|
||||||
|
return found->value;
|
||||||
|
else
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
void httpp_clear(http_parser_t *parser)
|
void httpp_clear(http_parser_t *parser)
|
||||||
{
|
{
|
||||||
parser->req_type = httpp_req_none;
|
parser->req_type = httpp_req_none;
|
||||||
@ -350,6 +489,7 @@ void httpp_clear(http_parser_t *parser)
|
|||||||
free(parser->uri);
|
free(parser->uri);
|
||||||
parser->uri = NULL;
|
parser->uri = NULL;
|
||||||
avl_tree_free(parser->vars, _free_vars);
|
avl_tree_free(parser->vars, _free_vars);
|
||||||
|
avl_tree_free(parser->queryvars, _free_vars);
|
||||||
parser->vars = NULL;
|
parser->vars = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,6 +33,7 @@ typedef struct http_parser_tag {
|
|||||||
httpp_request_type_e req_type;
|
httpp_request_type_e req_type;
|
||||||
char *uri;
|
char *uri;
|
||||||
avl_tree *vars;
|
avl_tree *vars;
|
||||||
|
avl_tree *queryvars;
|
||||||
} http_parser_t;
|
} http_parser_t;
|
||||||
|
|
||||||
http_parser_t *httpp_create_parser(void);
|
http_parser_t *httpp_create_parser(void);
|
||||||
@ -41,6 +42,8 @@ int httpp_parse(http_parser_t *parser, char *http_data, unsigned long len);
|
|||||||
int httpp_parse_response(http_parser_t *parser, char *http_data, unsigned long len, char *uri);
|
int httpp_parse_response(http_parser_t *parser, char *http_data, unsigned long len, char *uri);
|
||||||
void httpp_setvar(http_parser_t *parser, char *name, char *value);
|
void httpp_setvar(http_parser_t *parser, char *name, char *value);
|
||||||
char *httpp_getvar(http_parser_t *parser, char *name);
|
char *httpp_getvar(http_parser_t *parser, char *name);
|
||||||
|
void httpp_set_query_param(http_parser_t *parser, char *name, char *value);
|
||||||
|
char *httpp_get_query_param(http_parser_t *parser, char *name);
|
||||||
void httpp_destroy(http_parser_t *parser);
|
void httpp_destroy(http_parser_t *parser);
|
||||||
void httpp_clear(http_parser_t *parser);
|
void httpp_clear(http_parser_t *parser);
|
||||||
|
|
||||||
|
64
src/util.c
64
src/util.c
@ -208,50 +208,43 @@ char *util_get_path_from_normalised_uri(char *uri) {
|
|||||||
return fullpath;
|
return fullpath;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Get an absolute path (from the webroot dir) from a URI. Return NULL if the
|
char *util_url_escape(char *src)
|
||||||
* path contains 'disallowed' sequences like foo/../ (which could be used to
|
{
|
||||||
* escape from the webroot) or if it cannot be URI-decoded.
|
int len = strlen(src);
|
||||||
* Caller should free the path.
|
unsigned char *decoded;
|
||||||
*/
|
|
||||||
char *util_normalise_uri(char *uri) {
|
|
||||||
int urilen = strlen(uri);
|
|
||||||
unsigned char *path;
|
|
||||||
char *dst;
|
|
||||||
int i;
|
int i;
|
||||||
|
char *dst;
|
||||||
int done = 0;
|
int done = 0;
|
||||||
|
|
||||||
if(uri[0] != '/')
|
decoded = calloc(1, len + 1);
|
||||||
return NULL;
|
|
||||||
|
|
||||||
path = calloc(1, urilen + 1);
|
dst = decoded;
|
||||||
|
|
||||||
dst = path;
|
for(i=0; i < len; i++) {
|
||||||
|
switch(src[i]) {
|
||||||
for(i=0; i < urilen; i++) {
|
|
||||||
switch(uri[i]) {
|
|
||||||
case '%':
|
case '%':
|
||||||
if(i+2 >= urilen) {
|
if(i+2 >= len) {
|
||||||
free(path);
|
free(decoded);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
if(hex(uri[i+1]) == -1 || hex(uri[i+2]) == -1 ) {
|
if(hex(src[i+1]) == -1 || hex(src[i+2]) == -1 ) {
|
||||||
free(path);
|
free(decoded);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
*dst++ = hex(uri[i+1]) * 16 + hex(uri[i+2]);
|
*dst++ = hex(src[i+1]) * 16 + hex(src[i+2]);
|
||||||
i+= 2;
|
i+= 2;
|
||||||
break;
|
break;
|
||||||
case '#':
|
case '#':
|
||||||
done = 1;
|
done = 1;
|
||||||
break;
|
break;
|
||||||
case 0:
|
case 0:
|
||||||
ERROR0("Fatal internal logic error in util_get_path_from_uri()");
|
ERROR0("Fatal internal logic error in util_url_escape()");
|
||||||
free(path);
|
free(decoded);
|
||||||
return NULL;
|
return NULL;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
*dst++ = uri[i];
|
*dst++ = src[i];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if(done)
|
if(done)
|
||||||
@ -260,9 +253,30 @@ char *util_normalise_uri(char *uri) {
|
|||||||
|
|
||||||
*dst = 0; /* null terminator */
|
*dst = 0; /* null terminator */
|
||||||
|
|
||||||
|
return decoded;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get an absolute path (from the webroot dir) from a URI. Return NULL if the
|
||||||
|
* path contains 'disallowed' sequences like foo/../ (which could be used to
|
||||||
|
* escape from the webroot) or if it cannot be URI-decoded.
|
||||||
|
* Caller should free the path.
|
||||||
|
*/
|
||||||
|
char *util_normalise_uri(char *uri) {
|
||||||
|
char *path;
|
||||||
|
|
||||||
|
if(uri[0] != '/')
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
path = util_url_escape(uri);
|
||||||
|
|
||||||
|
if(path == NULL) {
|
||||||
|
WARN1("Error decoding URI: %s\n", uri);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/* We now have a full URI-decoded path. Check it for allowability */
|
/* We now have a full URI-decoded path. Check it for allowability */
|
||||||
if(verify_path(path))
|
if(verify_path(path))
|
||||||
return (char *)path;
|
return path;
|
||||||
else {
|
else {
|
||||||
WARN1("Rejecting invalid path \"%s\"", path);
|
WARN1("Rejecting invalid path \"%s\"", path);
|
||||||
free(path);
|
free(path);
|
||||||
|
@ -14,4 +14,6 @@ char *util_normalise_uri(char *uri);
|
|||||||
char *util_base64_encode(char *data);
|
char *util_base64_encode(char *data);
|
||||||
char *util_base64_decode(unsigned char *input);
|
char *util_base64_decode(unsigned char *input);
|
||||||
|
|
||||||
|
char *util_url_escape(char *src);
|
||||||
|
|
||||||
#endif /* __UTIL_H__ */
|
#endif /* __UTIL_H__ */
|
||||||
|
Loading…
Reference in New Issue
Block a user