mirror of
https://gitlab.xiph.org/xiph/icecast-server.git
synced 2025-01-03 14:56:34 -05:00
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
This commit is contained in:
parent
45c5bff8bb
commit
a79f0b6cae
@ -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
|
||||
|
223
src/admin.c
Normal file
223
src/admin.c
Normal file
@ -0,0 +1,223 @@
|
||||
#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;
|
||||
}
|
||||
|
9
src/admin.h
Normal file
9
src/admin.h
Normal file
@ -0,0 +1,9 @@
|
||||
#ifndef __ADMIN_H__
|
||||
#define __ADMIN_H__
|
||||
|
||||
#include "refbuf.h"
|
||||
#include "client.h"
|
||||
|
||||
void admin_handle_request(client_t *client, char *uri);
|
||||
|
||||
#endif /* __ADMIN_H__ */
|
138
src/connection.c
138
src/connection.c
@ -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;
|
||||
|
51
src/util.c
51
src/util.c
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user