diff --git a/TODO b/TODO index a3d97ff5..93cbc5bd 100644 --- a/TODO +++ b/TODO @@ -2,14 +2,12 @@ BUGS ---- - stats get off? this needs testing more testing. -- autoconf doesn't set HAVE_POLL +- autoconf doesn't set HAVE_POLL (still true?) - some stuff (like 'genre') isn't making it into the stats dump - make install - doesn't install configs? -- pthread/bsd: -pthread instead of -lpthread (autoconf) - FEATURES -------- @@ -48,8 +46,7 @@ FEATURES - httpp - split out query string for further processing -- finish mp3 metadata: http://server:ip/admin.cgi?pass=%s&mode=updinfo&mount=%s&song=%s - +- binding to multiple ports (possibly including full ipv6 support) diff --git a/conf/icecast.xml b/conf/icecast.xml index 76b28a51..36c00dff 100644 --- a/conf/icecast.xml +++ b/conf/icecast.xml @@ -45,6 +45,17 @@ --> + + /example1.ogg + othersource + hackmemore + + + 1 + /tmp/dump-example1.ogg + /example2.ogg + + 1 diff --git a/src/config.c b/src/config.c index 10ad098a..f9243209 100644 --- a/src/config.c +++ b/src/config.c @@ -56,6 +56,7 @@ static void _parse_logging(xmlDocPtr doc, xmlNodePtr node); static void _parse_security(xmlDocPtr doc, xmlNodePtr node); static void _parse_authentication(xmlDocPtr doc, xmlNodePtr node); static void _parse_relay(xmlDocPtr doc, xmlNodePtr node); +static void _parse_mount(xmlDocPtr doc, xmlNodePtr node); static void _add_server(xmlDocPtr doc, xmlNodePtr node); void config_initialize(void) @@ -70,6 +71,7 @@ void config_shutdown(void) ice_config_dir_t *dirnode, *nextdirnode; ice_config_t *c = &_configuration; relay_server *relay, *nextrelay; + mount_proxy *mount, *nextmount; if (_config_filename) free(_config_filename); @@ -110,6 +112,17 @@ void config_shutdown(void) free(relay); relay = nextrelay; } + mount = _configuration.mounts; + while(mount) { + nextmount = mount->next; + xmlFree(mount->mountname); + xmlFree(mount->username); + xmlFree(mount->password); + xmlFree(mount->dumpfile); + xmlFree(mount->fallback_mount); + free(mount); + mount = nextmount; + } dirnode = _configuration.dir_list; while(dirnode) { nextdirnode = dirnode->next; @@ -273,6 +286,8 @@ static void _parse_root(xmlDocPtr doc, xmlNodePtr node) _parse_limits(doc, node->xmlChildrenNode); } else if (strcmp(node->name, "relay") == 0) { _parse_relay(doc, node->xmlChildrenNode); + } else if (strcmp(node->name, "mount") == 0) { + _parse_mount(doc, node->xmlChildrenNode); } else if (strcmp(node->name, "directory") == 0) { _parse_directory(doc, node->xmlChildrenNode); } else if (strcmp(node->name, "paths") == 0) { @@ -321,6 +336,55 @@ static void _parse_limits(xmlDocPtr doc, xmlNodePtr node) } while ((node = node->next)); } +static void _parse_mount(xmlDocPtr doc, xmlNodePtr node) +{ + char *tmp; + mount_proxy *mount = calloc(1, sizeof(mount_proxy)); + mount_proxy *current = _configuration.mounts; + mount_proxy *last=NULL; + + while(current) { + last = current; + current = current->next; + } + + if(last) + last->next = mount; + else + _configuration.mounts = mount; + + do { + if (node == NULL) break; + if (xmlIsBlankNode(node)) continue; + + if (strcmp(node->name, "mount-name") == 0) { + mount->mountname = (char *)xmlNodeListGetString( + doc, node->xmlChildrenNode, 1); + } + else if (strcmp(node->name, "username") == 0) { + mount->username = (char *)xmlNodeListGetString( + doc, node->xmlChildrenNode, 1); + } + else if (strcmp(node->name, "password") == 0) { + mount->password = (char *)xmlNodeListGetString( + doc, node->xmlChildrenNode, 1); + } + else if (strcmp(node->name, "dump-file") == 0) { + mount->dumpfile = (char *)xmlNodeListGetString( + doc, node->xmlChildrenNode, 1); + } + else if (strcmp(node->name, "fallback-mount") == 0) { + mount->fallback_mount = (char *)xmlNodeListGetString( + doc, node->xmlChildrenNode, 1); + } + else if (strcmp(node->name, "max-listeners") == 0) { + tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1); + mount->max_listeners = atoi(tmp); + if(tmp) xmlFree(tmp); + } + } while ((node = node->next)); +} + static void _parse_relay(xmlDocPtr doc, xmlNodePtr node) { char *tmp; diff --git a/src/config.h b/src/config.h index 2f997453..621eac23 100644 --- a/src/config.h +++ b/src/config.h @@ -22,6 +22,20 @@ typedef struct _relay_server { struct _relay_server *next; } relay_server; +typedef struct _mount_proxy { + char *mountname; /* The mountpoint this proxy is used for */ + + char *username; /* Username and password for this mountpoint. If unset, */ + char *password; /* falls back to global source password */ + + char *dumpfile; /* Filename to dump this stream to (will be appended). NULL + to not dump. */ + int max_listeners; /* Max listeners for this mountpoint only. -1 to not + limit here (i.e. only use the global limit) */ + char *fallback_mount; + struct _mount_proxy *next; +} mount_proxy; + typedef struct ice_config_tag { char *location; @@ -54,6 +68,8 @@ typedef struct ice_config_tag relay_server *relay; + mount_proxy *mounts; + char *base_dir; char *log_dir; char *webroot_dir; diff --git a/src/connection.c b/src/connection.c index 3196bbe1..e79aa332 100644 --- a/src/connection.c +++ b/src/connection.c @@ -321,7 +321,7 @@ int connection_create_source(client_t *client, connection_t *con, http_parser_t } } else { format_type_t format = FORMAT_TYPE_MP3; - ERROR0("No content-type header, falling back to backwards compatiblity mode for icecast 1.x relays. Assuming content is mp3."); + ERROR0("No content-type header, falling back to backwards compatibility mode for icecast 1.x relays. Assuming content is mp3."); source = source_create(client, con, parser, mount, format); } client->respcode = 200; @@ -418,11 +418,24 @@ static int _check_admin_pass(http_parser_t *parser) static int _check_source_pass(http_parser_t *parser, char *mount) { char *pass = config_get_config()->source_password; + char *user = "source"; int ret; - if(!pass) - pass = ""; - ret = _check_pass_http(parser, "source", pass); + mount_proxy *mountinfo = config_get_config()->mounts; + while(mountinfo) { + if(!strcmp(mountinfo->mountname, mount)) { + pass = mountinfo->password; + user = mountinfo->username; + break; + } + } + + if(!pass) { + WARN0("No source password set, rejecting source"); + return 0; + } + + ret = _check_pass_http(parser, user, pass); if(!ret && config_get_config()->ice_login) { ret = _check_pass_ice(parser, pass); diff --git a/src/format.c b/src/format.c index b18ed172..5daf5c72 100644 --- a/src/format.c +++ b/src/format.c @@ -13,6 +13,7 @@ #include "source.h" #include "format.h" +#include "global.h" #include "format_vorbis.h" #include "format_mp3.h" @@ -111,6 +112,9 @@ void format_send_general_headers(format_plugin_t *format, "%s: %s\r\n", var->name, var->value); if(bytes > 0) client->con->sent_bytes += bytes; } + bytes = sock_write(client->con->sock, + "Server: %s\r\n", ICECAST_VERSION_STRING); + if(bytes > 0) client->con->sent_bytes += bytes; node = avl_get_next(node); } avl_tree_unlock(source->parser->vars); diff --git a/src/global.h b/src/global.h index 65ba9eec..b2b3b210 100644 --- a/src/global.h +++ b/src/global.h @@ -6,6 +6,8 @@ #define ICE_RUNNING 1 #define ICE_HALTING 2 +#define ICECAST_VERSION_STRING "Icecast 2.0-alpha2/cvs" + #include "thread/thread.h" typedef struct ice_global_tag diff --git a/src/source.h b/src/source.h index 72474711..7a129422 100644 --- a/src/source.h +++ b/src/source.h @@ -28,7 +28,8 @@ typedef struct source_tag rwlock_t *shutdown_rwlock; ypdata_t *ypdata[MAX_YP_DIRECTORIES]; int num_yp_directories; - long listeners; + long listeners; + long max_listeners; } source_t; source_t *source_create(client_t *client, connection_t *con, http_parser_t *parser, const char *mount, format_type_t type);