Commit a79f0b6c authored by Michael Smith's avatar Michael Smith

Split admin stuff out into a seperate file, add various utility functions there.

rename util_url_escape to util_url_unescape, and write a util_escape function
that actually DOES escape things. Fix all the callers of the function to call
the correct one of these two.

svn path=/trunk/icecast/; revision=4414
parent 45c5bff8
......@@ -11,7 +11,7 @@ noinst_HEADERS = config.h os.h logging.h sighandler.h connection.h global.h\
compat.h format_mp3.h fserve.h xslt.h geturl.h yp.h event.h
icecast_SOURCES = config.c main.c logging.c sighandler.c connection.c global.c\
util.c slave.c source.c stats.c refbuf.c client.c format.c format_vorbis.c\
format_mp3.c xslt.c fserve.c geturl.c yp.c event.c
format_mp3.c xslt.c fserve.c geturl.c yp.c event.c admin.c
icecast_LDADD = net/libicenet.la thread/libicethread.la httpp/libicehttpp.la\
log/libicelog.la avl/libiceavl.la timing/libicetiming.la
......
#include <string.h>
#include <stdlib.h>
#include "config.h"
#include "connection.h"
#include "refbuf.h"
#include "client.h"
#include "source.h"
#include "global.h"
#include "event.h"
#include "stats.h"
#include "format.h"
#include "format_mp3.h"
#include "logging.h"
#define CATMODULE "admin"
#define COMMAND_ERROR (-1)
#define COMMAND_FALLBACK 1
#define COMMAND_RAW_STATS 2
#define COMMAND_METADATA_UPDATE 3
int admin_get_command(char *command)
{
if(!strcmp(command, "fallbacks"))
return COMMAND_FALLBACK;
else if(!strcmp(command, "rawstats"))
return COMMAND_RAW_STATS;
else if(!strcmp(command, "stats.xml")) /* The old way */
return COMMAND_RAW_STATS;
else if(!strcmp(command, "metadata"))
return COMMAND_METADATA_UPDATE;
else
return COMMAND_ERROR;
}
static void command_fallback(client_t *client, source_t *source);
static void command_metadata(client_t *client, source_t *source);
static void command_raw_stats(client_t *client);
static void admin_handle_mount_request(client_t *client, source_t *source,
int command);
static void admin_handle_general_request(client_t *client, int command);
void admin_handle_request(client_t *client, char *uri)
{
char *mount, *command_string;
int command;
if(strncmp("/admin/", uri, 7)) {
ERROR0("Internal error: admin request isn't");
client_send_401(client);
return;
}
command_string = uri + 7;
command = admin_get_command(command_string);
if(command < 0) {
ERROR1("Error parsing command string or unrecognised command: %s",
command_string);
client_send_400(client, "Unrecognised command");
return;
}
mount = httpp_get_query_param(client->parser, "mount");
if(mount != NULL) {
source_t *source;
/* This is a mount request, handle it as such */
if(!connection_check_source_pass(client->parser, mount)) {
INFO1("Bad or missing password on mount modification admin "
"request (command: %s)", command_string);
client_send_401(client);
return;
}
avl_tree_rlock(global.source_tree);
source = source_find_mount(mount);
avl_tree_unlock(global.source_tree);
if(source == NULL) {
WARN2("Admin command %s on non-existent source %s",
command_string, mount);
client_send_400(client, "Source does not exist");
return;
}
INFO2("Received admin command %s on mount \"%s\"",
command_string, mount);
admin_handle_mount_request(client, source, command);
}
else {
if(!connection_check_admin_pass(client->parser)) {
INFO1("Bad or missing password on admin command "
"request (command: %s)", command_string);
client_send_401(client);
return;
}
admin_handle_general_request(client, command);
}
}
static void admin_handle_general_request(client_t *client, int command)
{
switch(command) {
case COMMAND_RAW_STATS:
command_raw_stats(client);
break;
default:
WARN0("General admin request not recognised");
client_send_400(client, "Unknown admin request");
return;
}
}
static void admin_handle_mount_request(client_t *client, source_t *source,
int command)
{
switch(command) {
case COMMAND_FALLBACK:
command_fallback(client, source);
break;
case COMMAND_METADATA_UPDATE:
command_metadata(client, source);
break;
default:
WARN0("Mount request not recognised");
client_send_400(client, "Mount request unknown");
return;
}
}
#define COMMAND_REQUIRE(client,name,var) \
do { \
(var) = httpp_get_query_param((client)->parser, (name)); \
if((var) == NULL) { \
client_send_400((client), "Missing parameter"); \
return; \
} \
} while(0);
static void command_success(client_t *client, char *message)
{
int bytes;
client->respcode = 200;
bytes = sock_write(client->con->sock,
"HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n"
"<html><head><title>Admin request successful</title></head>"
"<body><p>%s</p></body></html>", message);
if(bytes > 0) client->con->sent_bytes = bytes;
client_destroy(client);
}
static void command_fallback(client_t *client, source_t *source)
{
char *fallback;
char *old;
DEBUG0("Got fallback request");
COMMAND_REQUIRE(client, "fallback", fallback);
old = source->fallback_mount;
source->fallback_mount = strdup(fallback);
free(old);
command_success(client, "Fallback configured");
}
static void command_metadata(client_t *client, source_t *source)
{
char *action;
char *value;
mp3_state *state;
DEBUG0("Got metadata update request");
COMMAND_REQUIRE(client, "mode", action);
COMMAND_REQUIRE(client, "song", value);
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++;
state->metadata_raw = 0;
thread_mutex_unlock(&(state->lock));
DEBUG2("Metadata on mountpoint %s changed to \"%s\"", source->mount, value);
stats_event(source->mount, "title", value);
command_success(client, "Metadata update successful");
}
static void command_raw_stats(client_t *client) {
DEBUG0("Stats request, sending xml stats");
stats_sendxml(client);
client_destroy(client);
return;
}
#ifndef __ADMIN_H__
#define __ADMIN_H__
#include "refbuf.h"
#include "client.h"
void admin_handle_request(client_t *client, char *uri);
#endif /* __ADMIN_H__ */
......@@ -41,6 +41,7 @@
#include "format.h"
#include "format_mp3.h"
#include "event.h"
#include "admin.h"
#define CATMODULE "connection"
......@@ -437,7 +438,7 @@ static int _check_pass_ice(http_parser_t *parser, char *correctpass)
return 1;
}
static int _check_relay_pass(http_parser_t *parser)
int connection_check_relay_pass(http_parser_t *parser)
{
ice_config_t *config = config_get_config();
char *pass = config->relay_password;
......@@ -448,7 +449,7 @@ static int _check_relay_pass(http_parser_t *parser)
return _check_pass_http(parser, "relay", pass);
}
static int _check_admin_pass(http_parser_t *parser)
int connection_check_admin_pass(http_parser_t *parser)
{
ice_config_t *config = config_get_config();
char *pass = config->admin_password;
......@@ -461,7 +462,7 @@ static int _check_admin_pass(http_parser_t *parser)
return _check_pass_http(parser, "admin", pass);
}
static int _check_source_pass(http_parser_t *parser, char *mount)
int connection_check_source_pass(http_parser_t *parser, char *mount)
{
ice_config_t *config = config_get_config();
char *pass = config->source_password;
......@@ -502,108 +503,6 @@ static int _check_source_pass(http_parser_t *parser, char *mount)
return ret;
}
static void handle_fallback_request(client_t *client)
{
source_t *source;
char *mount, *value, *old;
int bytes;
mount = httpp_get_query_param(client->parser, "mount");
value = httpp_get_query_param(client->parser, "fallback");
if(!_check_source_pass(client->parser, mount)) {
INFO0("Bad or missing password on fallback configuration request");
client_send_401(client);
return;
}
if(value == 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, "Current source not found");
return;
}
old = source->fallback_mount;
source->fallback_mount = strdup(value);
free(old);
client->respcode = 200;
bytes = sock_write(client->con->sock,
"HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n"
"Fallback configured");
if(bytes > 0) client->con->sent_bytes = bytes;
client_destroy(client);
}
static void handle_metadata_request(client_t *client)
{
source_t *source;
char *action;
char *mount;
char *value;
mp3_state *state;
int bytes;
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(!_check_source_pass(client->parser, mount)) {
INFO0("Metadata request with wrong or missing password");
client_send_401(client);
return;
}
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++;
state->metadata_raw = 0;
thread_mutex_unlock(&(state->lock));
DEBUG2("Metadata on mountpoint %s changed to \"%s\"", mount, value);
stats_event(mount, "title", 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,
http_parser_t *parser, char *uri)
{
......@@ -614,7 +513,7 @@ static void _handle_source_request(connection_t *con,
INFO1("Source logging in at mountpoint \"%s\"", uri);
stats_event_inc(NULL, "source_connections");
if (!_check_source_pass(parser, uri)) {
if (!connection_check_source_pass(parser, uri)) {
INFO1("Source (%s) attempted to login with invalid or missing password", uri);
client_send_401(client);
return;
......@@ -645,7 +544,7 @@ static void _handle_stats_request(connection_t *con,
stats_event_inc(NULL, "stats_connections");
if (!_check_admin_pass(parser)) {
if (!connection_check_admin_pass(parser)) {
ERROR0("Bad password for stats connection");
connection_close(con);
httpp_destroy(parser);
......@@ -699,27 +598,10 @@ static void _handle_get_request(connection_t *con,
** aren't subject to the limits.
*/
/* TODO: add GUID-xxxxxx */
if (strcmp(uri, "/admin/stats.xml") == 0) {
if (!_check_admin_pass(parser)) {
INFO0("Request for /admin/stats.xml with incorrect or no password");
client_send_401(client);
return;
}
DEBUG0("Stats request, sending xml stats");
stats_sendxml(client);
client_destroy(client);
return;
}
if(strcmp(uri, "/admin/metadata") == 0) {
DEBUG0("Got metadata update request");
handle_metadata_request(client);
return;
}
if(strcmp(uri, "/admin/fallbacks") == 0) {
DEBUG0("Got fallback request");
handle_fallback_request(client);
/* Dispatch all admin requests */
if (strncmp(uri, "/admin/", 7) == 0) {
admin_handle_request(client, uri);
return;
}
......@@ -801,7 +683,7 @@ static void _handle_get_request(connection_t *con,
}
if (strcmp(uri, "/admin/streamlist") == 0) {
if (!_check_relay_pass(parser)) {
if (!connection_check_relay_pass(parser)) {
INFO0("Client attempted to fetch /admin/streamlist with bad password");
client_send_401(client);
} else {
......
......@@ -37,6 +37,10 @@ int connection_create_source(struct _client_tag *client, connection_t *con,
void connection_inject_event(int eventnum, void *event_data);
int connection_check_source_pass(http_parser_t *parser, char *mount);
int connection_check_relay_pass(http_parser_t *parser);
int connection_check_admin_pass(http_parser_t *parser);
extern rwlock_t _source_shutdown_rwlock;
#endif /* __CONNECTION_H__ */
......@@ -674,13 +674,10 @@ static int _parse_audio_info(source_t *source, char *s)
pvar = strchr(token, '=');
if (pvar) {
variable = (char *)malloc(pvar-token+1);
memset(variable, '\000', pvar-token+1);
strncpy(variable, token, pvar-token);
pvar++;
if (strlen(pvar)) {
value = (char *)malloc(strlen(pvar)+1);
memset(value, '\000', strlen(pvar)+1);
strncpy(value, pvar, strlen(pvar));
value = util_url_unescape(pvar);
util_dict_set(source->audio_info, variable, value);
stats_event(source->mount, variable, value);
if (value) {
......@@ -688,7 +685,7 @@ static int _parse_audio_info(source_t *source, char *s)
}
}
if (variable) {
free(variable);
free(variable);
}
}
s = NULL;
......
......@@ -216,7 +216,54 @@ char *util_get_path_from_normalised_uri(char *uri) {
return fullpath;
}
static char hexchars[16] = {
'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'
};
static char safechars[256] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
};
char *util_url_escape(char *src)
{
int len = strlen(src);
/* Efficiency not a big concern here, keep the code simple/conservative */
char *dst = calloc(1, len*3 + 1);
unsigned char *source = src;
int i,j=0;
for(i=0; i < len; i++) {
if(safechars[source[i]]) {
dst[j++] = source[i];
}
else {
dst[j] = '%';
dst[j+1] = hexchars[ source[i] & 0x15 ];
dst[j+2] = hexchars[ (source[i] >> 4) & 0x15 ];
j+= 3;
}
}
dst[j] = 0;
return dst;
}
char *util_url_unescape(char *src)
{
int len = strlen(src);
unsigned char *decoded;
......@@ -247,7 +294,7 @@ char *util_url_escape(char *src)
done = 1;
break;
case 0:
ERROR0("Fatal internal logic error in util_url_escape()");
ERROR0("Fatal internal logic error in util_url_unescape()");
free(decoded);
return NULL;
break;
......@@ -275,7 +322,7 @@ char *util_normalise_uri(char *uri) {
if(uri[0] != '/')
return NULL;
path = util_url_escape(uri);
path = util_url_unescape(uri);
if(path == NULL) {
WARN1("Error decoding URI: %s\n", uri);
......
......@@ -14,6 +14,7 @@ char *util_normalise_uri(char *uri);
char *util_base64_encode(char *data);
char *util_base64_decode(unsigned char *input);
char *util_url_unescape(char *src);
char *util_url_escape(char *src);
/* String dictionary type, without support for NULL keys, or multiple
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment