diff --git a/AUTHORS b/AUTHORS index feb09705..ef764e56 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,4 +1,4 @@ Jack Moffitt Michael Smith -oddsock +oddsock Karl Heyes diff --git a/README b/README index d30909d5..9c89aa87 100644 --- a/README +++ b/README @@ -1,8 +1,41 @@ -Icecast2 Beta 1. +icecast 2.x - README +--------------------------------------------------------------------- -This is an beta release. Not all functionality is fully implemented, though -we believe it to be very stable for all the currently available features. +Icecast is a streaming media server which currently supports Ogg +Vorbis and MP3 audio streams. It can be used to create an Internet +radio station or a privately running jukebox and many things in +between. It is very versatile in that new formats can be added +relatively easily and supports open standards for commuincation and +interaction. +Icecast is distributed under the GNU GPL, version 2. A copy of this +license is included with this software in the COPYING file. + +Prerequisites +--------------------------------------------------------------------- +icecast requires the following packages : + +* libxml2 - http://xmlsoft.org/downloads.html +* libxslt - http://xmlsoft.org/XSLT/downloads.html +* curl - http://curl.haxx.se/download.html (>= version 7.10 required) + NOTE: icecast may be compiled without curl, however this will + disable all Directory server interaction (YP). +* ogg/vorbis - http://www.vorbis.com/files (>= version 1.0 required) + +A Note About RPMS +--------------------------------------------------------------------- +This section only applies to you if your operating system uses RPMS. + +In order to build icecast, you will need to install the "devel" RPM +packages for each of the prerequisite packages in addition to the +normal RPMS for each package. + +please check the websites for each of the prerequisite packages for +appropriate download links for RPMS. + + +Build/Install +--------------------------------------------------------------------- To build icecast on a Unix platform, perform the following : Run @@ -12,9 +45,11 @@ Run To build and install this release. -A sample config file will be placed in /usr/local/etc (on UNIX) or in the current working directory (on Win32) and is called icecast.xml +A sample config file will be placed in /usr/local/etc (on UNIX) or in +the current working directory (on Win32) and is called icecast.xml -Documentation for icecast is available in the doc directory, by viewing doc/icecast2_TOC.html in a browser. +Documentation for icecast is available in the doc directory, by +viewing doc/index.html in a browser. Please email us at icecast@xiph.org or icecast-dev@xiph.org, or come and see us at irc.freenode.net, channel #icecast, if you have any troubles. diff --git a/TODO b/TODO index 86af2698..808f2a1a 100644 --- a/TODO +++ b/TODO @@ -1,33 +1,13 @@ -2.0 CRITICAL - These are the things without which 2.0 cannot be released -____________ - -- Should icecast automatically (i.e. without needing -c) look for the config - file in /etc/icecast.xml or something? - -- libshout 2.0 and ices 2.0 releases, also an ices 0.x release that works with - this. Without source clients, icecast isn't much use... - -- integrate/include all the documentation done by external groups. - -- generally we don't do proper checking for the correct versions of various - libraries (this is probably more of an issue with ices2, but it also affects - icecast) - BUGS ---- -- stats get off? this needs testing more testing. - -- some stuff (like 'genre') isn't making it into the stats dump - - logging - bytes send and time listening may both be broken? -- slave servers don't work. relay user is not respected by the source (only - admin can read /admin/streamlist), and the slave can't parse the xml result - of streamlist anyway (it expects a simple mountpoint per line) - FEATURES -------- +- Should icecast automatically (i.e. without needing -c) look for the config + file in /etc/icecast.xml or something? + - pull out vorbis comments. and send to stats. This seems to be being done, but it isn't working right. @@ -45,41 +25,21 @@ FEATURES - allow using get_predata() stuff to send an "intro" to any newly-connected user? -- stats to list currently connected clients: ip and hostname - -- stream switching (drop clients to another stream on disconnect of source) - - a) fallbacks from named location to new mountpoint - - OR b) fallbacks for connected clients to new mountpoint (so newly-connecting - clients just get a 404 on the old path) - - OR c) combination - first one, plus generic alias ability? - -- /admin/* for all admin functionality - - configuring fallbacks - - mp3 metadata injection - - remote shutdown? - - general registerable url-handlers in connection.c rather than hard-coded list (already getting unmaintainable) - httpp - split out query string for further processing -- option to use ipv6 (equiv to using ::, I think. - - abstract all admin functionality to a set of commands, and command handlers. Make /admin/* just parse according to a set of rules, and dispatch generic commands through that. Use this for alternative admin interfaces (GUI? telnet interface?) -- listener authentication (per mountpoint?) - - all timer-based functionality (yp updates, slave/relay checks) should have a single timer thread which dispatches events through the normal event mechanism (to worker threads from the main pool). This will reduce the extraneous thread count. -- atomic admin function to: set fallback from A->B, remove A, move mountpoint - B to A. Needs forced-source removal first. - - race condition between avl_tree_unlock(pending_tree) and thread_cond_wait(&fserv_cond) in fserv.c, it's a pain to fix but should be. diff --git a/conf/icecast.xml.in b/conf/icecast.xml.in index e209256d..8ed2c9c5 100644 --- a/conf/icecast.xml.in +++ b/conf/icecast.xml.in @@ -138,6 +138,12 @@ 1 + + @pkgdatadir@ diff --git a/configure.in b/configure.in index 21aa5226..db92a35e 100644 --- a/configure.in +++ b/configure.in @@ -74,7 +74,7 @@ XIPH_PATH_XSLT XIPH_VAR_APPEND([XIPH_CPPFLAGS],[$XSLT_CFLAGS]) XIPH_VAR_PREPEND([XIPH_LIBS],[$XSLT_LIBS]) -XIPH_PATH_VORBIS(, AC_MSG_ERROR([must have Ogg Vorbis v1.0 installed!])) +XIPH_PATH_VORBIS(, AC_MSG_ERROR([must have Ogg Vorbis v1.0 or above installed!])) XIPH_VAR_APPEND([XIPH_CPPFLAGS],[$VORBIS_CFLAGS]) XIPH_VAR_PREPEND([XIPH_LIBS],[$VORBIS_LIBS]) XIPH_VAR_APPEND([XIPH_LDFLAGS],[$VORBIS_LDFLAGS]) diff --git a/doc/icecast2.chm b/doc/icecast2.chm deleted file mode 100644 index 8f6c635e..00000000 Binary files a/doc/icecast2.chm and /dev/null differ diff --git a/icecast.spec b/icecast.spec index 28dd1a98..7ca69777 100644 --- a/icecast.spec +++ b/icecast.spec @@ -1,5 +1,5 @@ Name: icecast -Version: 2.0.0 +Version: 2.1.0-kh Release: 1 Summary: Xiph Streaming media server that supports multiple audio formats. diff --git a/src/admin.c b/src/admin.c index 3661f3d2..bf9aa91b 100644 --- a/src/admin.c +++ b/src/admin.c @@ -148,14 +148,14 @@ int admin_get_command(char *command) return COMMAND_TRANSFORMED_KILL_CLIENT; else if(!strcmp(command, KILLSOURCE_RAW_REQUEST)) return COMMAND_RAW_KILL_SOURCE; - else if(!strcmp(command, MANAGEAUTH_RAW_REQUEST)) - return COMMAND_RAW_MANAGEAUTH; - else if(!strcmp(command, BUILDM3U_RAW_REQUEST)) - return COMMAND_BUILDM3U; - else if(!strcmp(command, MANAGEAUTH_TRANSFORMED_REQUEST)) - return COMMAND_TRANSFORMED_MANAGEAUTH; else if(!strcmp(command, KILLSOURCE_TRANSFORMED_REQUEST)) return COMMAND_TRANSFORMED_KILL_SOURCE; + else if(!strcmp(command, MANAGEAUTH_RAW_REQUEST)) + return COMMAND_RAW_MANAGEAUTH; + else if(!strcmp(command, MANAGEAUTH_TRANSFORMED_REQUEST)) + return COMMAND_TRANSFORMED_MANAGEAUTH; + else if(!strcmp(command, BUILDM3U_RAW_REQUEST)) + return COMMAND_BUILDM3U; else if(!strcmp(command, ADM_FUNCTION_RAW_REQUEST)) return COMMAND_RAW_ADMIN_FUNCTION; else if(!strcmp(command, ADM_FUNCTION_TRANSFORMED_REQUEST)) @@ -236,8 +236,8 @@ xmlDocPtr admin_build_sourcelist(char *current_source) snprintf (buf, sizeof(buf), "%lu", (unsigned long)(now - source->con->con_time)); xmlNewChild (srcnode, NULL, "Connected", buf); - xmlNewChild (srcnode, NULL, "Format", - source->format->format_description); + xmlNewChild (srcnode, NULL, "content-type", + source->format->contenttype); if (source->authenticator) { xmlNewChild(srcnode, NULL, "authenticator", @@ -321,9 +321,18 @@ void admin_handle_request(client_t *client, char *uri) } if (command == COMMAND_SHOUTCAST_METADATA_UPDATE) { - ice_config_t *config = config_get_config (); + ice_config_t *config; + char *pass = httpp_get_query_param (client->parser, "pass"); + if (pass == NULL) + { + client_send_400 (client, "missing pass parameter"); + return; + } + config = config_get_config (); httpp_set_query_param (client->parser, "mount", config->shoutcast_mount); + httpp_setvar (client->parser, HTTPP_VAR_PROTOCOL, "ICY"); + httpp_setvar (client->parser, HTTPP_VAR_ICYPASSWORD, pass); config_release_config (); } @@ -336,9 +345,18 @@ void admin_handle_request(client_t *client, char *uri) client->authenticated = 1; } /* This is a mount request, but admin user is allowed */ - if (client->authenticated != 1) { - if (connection_check_admin_pass(client->parser)) - client->authenticated = 1; + if (client->authenticated != 1) + { + if (connection_check_admin_pass(client->parser) == 0) + { + if (connection_check_source_pass(client->parser, mount) == 0) + { + 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); @@ -353,30 +371,25 @@ void admin_handle_request(client_t *client, char *uri) } else { + if (source->running == 0 && source->on_demand == 0) + { + avl_tree_unlock (global.source_tree); + INFO2("Received admin command %s on unavailable mount \"%s\"", + command_string, mount); + client_send_400 (client, "Source is not available"); + return; + } + if (command == COMMAND_SHOUTCAST_METADATA_UPDATE && + source->shoutcast_compat == 0) + { + avl_tree_unlock (global.source_tree); + ERROR0 ("illegal change of metadata on non-shoutcast " + "compatible stream"); + client_send_400 (client, "illegal metadata call"); + return; + } INFO2("Received admin command %s on mount \"%s\"", command_string, mount); - if (source->shoutcast_compat == 0) - { - if (source->running == 0 && source->on_demand == 0) - { - INFO2("Received admin command %s on unavailable mount \"%s\"", - command_string, mount); - avl_tree_unlock (global.source_tree); - client_send_400 (client, "Source is not available"); - return; - } - if (client->authenticated != 1) - { - if (connection_check_source_pass(client->parser, mount) == 0) - { - INFO1("Bad or missing password on mount modification admin " - "request (command: %s)", command_string); - avl_tree_unlock(global.source_tree); - client_send_401(client); - return; - } - } - } admin_handle_mount_request (client, source, command); avl_tree_unlock(global.source_tree); } @@ -940,27 +953,11 @@ static void command_shoutcast_metadata(client_t *client, source_t *source) { char *action; char *value; - char *source_pass; - char *config_source_pass; - ice_config_t *config; DEBUG0("Got shoutcast metadata update request"); COMMAND_REQUIRE(client, "mode", action); COMMAND_REQUIRE(client, "song", value); - COMMAND_REQUIRE(client, "pass", source_pass); - - config = config_get_config(); - config_source_pass = strdup(config->source_password); - config_release_config(); - - if ((source->format->type != FORMAT_TYPE_MP3) && - (source->format->type != FORMAT_TYPE_NSV)) - { - thread_mutex_unlock (&source->lock); - client_send_400 (client, "Not mp3 or NSV, cannot update metadata"); - return; - } if (strcmp (action, "updinfo") != 0) { @@ -969,18 +966,6 @@ static void command_shoutcast_metadata(client_t *client, source_t *source) return; } - if (strcmp(source_pass, config_source_pass) != 0) - { - thread_mutex_unlock (&source->lock); - ERROR0("Invalid source password specified, metadata not updated"); - client_send_400 (client, "Invalid source password"); - return; - } - - if (config_source_pass) { - free(config_source_pass); - } - if (source->format && source->format->set_tag) { source->format->set_tag (source->format, "title", value); diff --git a/src/connection.c b/src/connection.c index 15391b5e..8f29d659 100644 --- a/src/connection.c +++ b/src/connection.c @@ -464,7 +464,7 @@ int connection_complete_source (source_t *source) { WARN0("No content-type header, falling back to backwards compatibility mode " "for icecast 1.x relays. Assuming content is mp3."); - format_type = FORMAT_TYPE_MP3; + format_type = FORMAT_TYPE_GENERIC; } if (format_get_plugin (format_type, source) < 0) @@ -582,13 +582,18 @@ int connection_check_admin_pass(http_parser_t *parser) ice_config_t *config = config_get_config(); char *pass = config->admin_password; char *user = config->admin_username; + char *protocol; if(!pass || !user) { config_release_config(); return 0; } - ret = _check_pass_http(parser, user, pass); + protocol = httpp_getvar (parser, HTTPP_VAR_PROTOCOL); + if (protocol && strcmp (protocol, "ICY") == 0) + ret = _check_pass_icy (parser, pass); + else + ret = _check_pass_http (parser, user, pass); config_release_config(); return ret; } @@ -881,9 +886,8 @@ static void _handle_get_request(connection_t *con, if (uri != passed_uri) free (uri); } -void _handle_shoutcast_compatible(connection_t *con, char *source_password) { +void _handle_shoutcast_compatible(connection_t *con, char *mount, char *source_password) { char shoutcast_password[256]; - char shoutcast_source[256]; char *http_compliant; int http_compliant_len = 0; char header[4096]; @@ -924,18 +928,14 @@ void _handle_shoutcast_compatible(connection_t *con, char *source_password) { /* Here we create a valid HTTP request based of the information that was passed in via the non-HTTP style protocol above. This means we can use some of our existing code to handle this case */ - memset(shoutcast_source, 0, sizeof (shoutcast_source)); - strcpy(shoutcast_source, "SOURCE / HTTP/1.0\r\n"); - http_compliant_len = strlen(shoutcast_source) + - strlen(header) + 1; + http_compliant_len = strlen(header) + strlen(mount) + 20; http_compliant = (char *)calloc(1, http_compliant_len); - sprintf(http_compliant, "%s%s", shoutcast_source, - header); + snprintf (http_compliant, http_compliant_len, + "SOURCE %s HTTP/1.0\r\n%s", mount, header); parser = httpp_create_parser(); httpp_initialize(parser, NULL); - if (httpp_parse(parser, http_compliant, - strlen(http_compliant))) { - _handle_source_request(con, parser, "/", SHOUTCAST_SOURCE_AUTH); + if (httpp_parse(parser, http_compliant, strlen(http_compliant))) { + _handle_source_request(con, parser, mount, SHOUTCAST_SOURCE_AUTH); free(http_compliant); return; } @@ -990,10 +990,12 @@ static void *_handle_connection(void *arg) if(global.serversock[i] == con->serversock) { config = config_get_config(); if (config->listeners[i].shoutcast_compat) { + char *shoutcast_mount = strdup (config->shoutcast_mount); source_password = strdup(config->source_password); config_release_config(); - _handle_shoutcast_compatible(con, source_password); + _handle_shoutcast_compatible(con, shoutcast_mount, source_password); free(source_password); + free (shoutcast_mount); continue_flag = 1; break; } diff --git a/src/format.c b/src/format.c index a01edc8b..4148cc33 100644 --- a/src/format.c +++ b/src/format.c @@ -58,75 +58,29 @@ format_type_t format_get_type(char *contenttype) return FORMAT_TYPE_OGG; /* Backwards compatibility */ else if(strcmp(contenttype, "application/ogg") == 0) return FORMAT_TYPE_OGG; /* Now blessed by IANA */ - else if(strcmp(contenttype, "audio/mpeg") == 0) - return FORMAT_TYPE_MP3; - else if(strcmp(contenttype, "audio/x-mpeg") == 0) - return FORMAT_TYPE_MP3; - else if(strcmp(contenttype, "video/nsv") == 0) - return FORMAT_TYPE_NSV; - else if(strcmp(contenttype, "audio/aac") == 0) - return FORMAT_TYPE_AAC; - else if(strcmp(contenttype, "audio/aacp") == 0) - return FORMAT_TYPE_AACPLUS; else - return FORMAT_ERROR; -} - -const char *format_get_mimetype(format_type_t type) -{ - switch(type) { - case FORMAT_TYPE_OGG: - return "application/ogg"; - break; - case FORMAT_TYPE_MP3: - return "audio/mpeg"; - break; - case FORMAT_TYPE_NSV: - return "video/nsv"; - break; - case FORMAT_TYPE_AAC: - return "audio/aac"; - break; - case FORMAT_TYPE_AACPLUS: - return "audio/aacp"; - break; - default: - return NULL; - } + /* We default to the Generic format handler, which + can handle many more formats than just mp3 */ + return FORMAT_TYPE_GENERIC; } int format_get_plugin(format_type_t type, source_t *source) { int ret = -1; - switch (type) - { - case FORMAT_TYPE_OGG: - ret = format_ogg_get_plugin (source); - break; - case FORMAT_TYPE_MP3: - ret = format_mp3_get_plugin (source); - break; - case FORMAT_TYPE_NSV: - ret = format_mp3_get_plugin (source); - source->format->format_description = "NSV Video"; - source->format->type = FORMAT_TYPE_NSV; - break; - case FORMAT_TYPE_AAC: - ret = format_mp3_get_plugin (source); - source->format->format_description = "AAC Audio"; - source->format->type = FORMAT_TYPE_AAC; - break; - case FORMAT_TYPE_AACPLUS: - ret = format_mp3_get_plugin (source); - source->format->format_description = "AACPlus Audio"; - source->format->type = FORMAT_TYPE_AACPLUS; - break; - default: - break; + switch (type) { + case FORMAT_TYPE_OGG: + ret = format_ogg_get_plugin (source); + break; + case FORMAT_TYPE_GENERIC: + ret = format_mp3_get_plugin (source); + break; + default: + break; } - stats_event (source->mount, "content-type", - format_get_mimetype(source->format->type)); + if (ret < 0) + stats_event (source->mount, "content-type", + source->format->contenttype); return ret; } @@ -274,7 +228,7 @@ static int format_prepare_headers (source_t *source, client_t *client) client->respcode = 200; bytes = snprintf (ptr, remaining, "HTTP/1.0 200 OK\r\n" - "Content-Type: %s\r\n", format_get_mimetype (source->format->type)); + "Content-Type: %s\r\n", source->format->contenttype); remaining -= bytes; ptr += bytes; diff --git a/src/format.h b/src/format.h index 1d54fe61..297f7114 100644 --- a/src/format.h +++ b/src/format.h @@ -29,11 +29,7 @@ typedef enum _format_type_tag { FORMAT_ERROR, /* No format, source not processable */ FORMAT_TYPE_OGG, - FORMAT_TYPE_VORBIS, - FORMAT_TYPE_MP3, - FORMAT_TYPE_NSV, - FORMAT_TYPE_AAC, - FORMAT_TYPE_AACPLUS + FORMAT_TYPE_GENERIC } format_type_t; typedef struct _format_plugin_tag @@ -43,7 +39,7 @@ typedef struct _format_plugin_tag /* we need to know the mount to report statistics */ char *mount; - char *format_description; + char *contenttype; refbuf_t *(*get_buffer)(struct source_tag *); int (*write_buf_to_client)(struct source_tag *source, client_t *client); @@ -59,7 +55,6 @@ typedef struct _format_plugin_tag } format_plugin_t; format_type_t format_get_type(char *contenttype); -const char *format_get_mimetype(format_type_t type); int format_get_plugin(format_type_t type, struct source_tag *source); int format_generic_write_to_client (struct source_tag *source, client_t *client); diff --git a/src/format_mp3.c b/src/format_mp3.c index 8ddc4cb3..7570b0cf 100644 --- a/src/format_mp3.c +++ b/src/format_mp3.c @@ -82,7 +82,7 @@ int format_mp3_get_plugin (source_t *source) plugin = (format_plugin_t *)calloc(1, sizeof(format_plugin_t)); - plugin->type = FORMAT_TYPE_MP3; + plugin->type = FORMAT_TYPE_GENERIC; plugin->get_buffer = mp3_get_no_meta; plugin->write_buf_to_client = format_mp3_write_buf_to_client; plugin->write_buf_to_file = write_mp3_to_file; @@ -90,9 +90,14 @@ int format_mp3_get_plugin (source_t *source) plugin->free_plugin = format_mp3_free_plugin; plugin->set_tag = mp3_set_tag; plugin->prerelease = NULL; - plugin->format_description = "MP3 audio"; plugin->apply_settings = format_mp3_apply_settings; + plugin->contenttype = httpp_getvar (source->parser, "content-type"); + if (plugin->contenttype == NULL) { + /* We default to MP3 audio for old clients without content types */ + plugin->contenttype = "audio/mpeg"; + } + plugin->_state = state; /* initial metadata needs to be blank for sending to clients and for diff --git a/src/format_ogg.c b/src/format_ogg.c index 0fcb6ece..9c0b3eb6 100644 --- a/src/format_ogg.c +++ b/src/format_ogg.c @@ -551,7 +551,6 @@ int format_ogg_get_plugin (source_t *source) plugin = (format_plugin_t *)calloc(1, sizeof(format_plugin_t)); plugin->type = FORMAT_TYPE_OGG; - plugin->format_description = "Ogg Vorbis"; plugin->get_buffer = ogg_get_buffer; plugin->write_buf_to_client = write_buf_to_client; plugin->write_buf_to_file = write_ogg_to_file; @@ -559,6 +558,7 @@ int format_ogg_get_plugin (source_t *source) plugin->free_plugin = format_ogg_free_plugin; plugin->set_tag = NULL; plugin->prerelease = refbuf_page_prerelease; + plugin->contenttype = "application/ogg"; ogg_sync_init (&state->oy); diff --git a/src/format_vorbis.c b/src/format_vorbis.c index 1f5fb9f4..b3094876 100644 --- a/src/format_vorbis.c +++ b/src/format_vorbis.c @@ -121,13 +121,13 @@ int format_ogg_get_plugin (source_t *source) plugin = (format_plugin_t *)calloc(1, sizeof(format_plugin_t)); plugin->type = FORMAT_TYPE_OGG; - plugin->format_description = "Ogg Vorbis"; plugin->get_buffer = vorbis_get_buffer; plugin->write_buf_to_client = vorbis_write_buf_to_client; plugin->write_buf_to_file = write_vorbis_to_file; plugin->create_client_data = create_vorbis_client_data; plugin->free_plugin = format_vorbis_free_plugin; plugin->set_tag = vorbis_set_tag; + plugin->contenttype = "application/ogg"; state = (vstate_t *)calloc(1, sizeof(vstate_t)); ogg_sync_init(&state->oy); diff --git a/src/fserve.c b/src/fserve.c index 5e778633..f388f9f6 100644 --- a/src/fserve.c +++ b/src/fserve.c @@ -427,14 +427,14 @@ int fserve_client_create(client_t *httpclient, char *path) bytes = sock_write(httpclient->con->sock, "HTTP/1.1 206 Partial Content\r\n" "Date: %s\r\n" - "Content-Length: %ld\r\n" + "Content-Length: " FORMAT_INT64 "\r\n" "Content-Range: bytes " FORMAT_INT64 \ "-" FORMAT_INT64 "/" FORMAT_INT64 "\r\n" "Content-Type: %s\r\n\r\n", currenttime, new_content_len, rangenumber, - rangenumber+new_content_len, + endpos, client->content_length, fserve_content_type(path)); } @@ -445,8 +445,10 @@ int fserve_client_create(client_t *httpclient, char *path) httpclient->respcode = 200; bytes = sock_write (httpclient->con->sock, "HTTP/1.0 200 OK\r\n" - "Content-Type: %s\r\n\r\n", - fserve_content_type(path)); + "Content-Length: " FORMAT_INT64 "\r\n" + "Content-Type: %s\r\n\r\n", + client->content_length, + fserve_content_type(path)); } if(bytes > 0) httpclient->con->sent_bytes = bytes; diff --git a/src/fserve.h b/src/fserve.h index b092b5e1..306dfdbf 100644 --- a/src/fserve.h +++ b/src/fserve.h @@ -20,8 +20,8 @@ typedef struct _fserve_t client_t *client; FILE *file; - int ready; int64_t content_length; + int ready; struct _fserve_t *next; } fserve_t; diff --git a/src/main.c b/src/main.c index 4cf42c34..5118bdd0 100644 --- a/src/main.c +++ b/src/main.c @@ -88,6 +88,7 @@ static void _stop_logging(void) { log_close(errorlog); log_close(accesslog); + log_close(playlistlog); } static void _initialize_subsystems(void) @@ -203,7 +204,6 @@ static int _start_logging(void) strerror(errno)); _fatal_error(buf); } - log_set_level(errorlog, config->loglevel); if(strcmp(config->access_log, "-")) { diff --git a/src/source.c b/src/source.c index 8e6cbeb5..5dbac81f 100644 --- a/src/source.c +++ b/src/source.c @@ -738,7 +738,6 @@ static void source_init (source_t *source) source->listeners = 0; stats_event_inc (NULL, "sources"); stats_event_inc (NULL, "source_total_connections"); - stats_event (source->mount, "type", source->format->format_description); if (source->con) sock_set_blocking (source->con->sock, SOCK_NONBLOCK); diff --git a/src/yp.c b/src/yp.c index ba205e9d..40ea15b7 100644 --- a/src/yp.c +++ b/src/yp.c @@ -512,7 +512,7 @@ static ypdata_t *create_yp_entry (source_t *source) break; /* ice-* is icecast, icy-* is shoutcast */ - add_yp_info (yp, "server_type", source->format->format_description, YP_SERVER_TYPE); + add_yp_info (yp, "server_type", source->format->contenttype, YP_SERVER_TYPE); if ((s = httpp_getvar(source->parser, "ice-name"))) { add_yp_info (yp, "server_name", s, YP_SERVER_NAME); } diff --git a/win32/ResizableDialog.cpp b/win32/ResizableDialog.cpp deleted file mode 100644 index f56972f4..00000000 --- a/win32/ResizableDialog.cpp +++ /dev/null @@ -1,442 +0,0 @@ -// ResizableDialog.cpp : implementation file -// -///////////////////////////////////////////////////////////////////////////// -// -// Copyright (C) 2000 by Paolo Messina -// (ppescher@yahoo.com) -// -// Free for non-commercial use. -// You may change the code to your needs, -// provided that credits to the original -// author is given in the modified files. -// -///////////////////////////////////////////////////////////////////////////// - -#include "stdafx.h" -#include "ResizableDialog.h" - -#ifdef _DEBUG -#define new DEBUG_NEW -#undef THIS_FILE -static char THIS_FILE[] = __FILE__; -#endif - -///////////////////////////////////////////////////////////////////////////// -// CResizableDialog - -inline void CResizableDialog::Construct() -{ - m_bInitDone = FALSE; - - m_bUseMinTrack = TRUE; - m_bUseMaxTrack = FALSE; - m_bUseMaxRect = FALSE; - - m_bShowGrip = TRUE; - - m_bEnableSaveRestore = FALSE; - - m_szGripSize.cx = GetSystemMetrics(SM_CXVSCROLL); - m_szGripSize.cy = GetSystemMetrics(SM_CYHSCROLL); -} - -CResizableDialog::CResizableDialog() -{ - Construct(); -} - -CResizableDialog::CResizableDialog(UINT nIDTemplate, CWnd* pParentWnd) - : CDialog(nIDTemplate, pParentWnd) -{ - Construct(); -} - -CResizableDialog::CResizableDialog(LPCTSTR lpszTemplateName, CWnd* pParentWnd) - : CDialog(lpszTemplateName, pParentWnd) -{ - Construct(); -} - -CResizableDialog::~CResizableDialog() -{ - // for safety - m_arrLayout.RemoveAll(); -} - - -BEGIN_MESSAGE_MAP(CResizableDialog, CDialog) - //{{AFX_MSG_MAP(CResizableDialog) - ON_WM_NCHITTEST() - ON_WM_GETMINMAXINFO() - ON_WM_SIZE() - ON_WM_DESTROY() - ON_WM_PAINT() - //}}AFX_MSG_MAP -END_MESSAGE_MAP() - - -///////////////////////////////////////////////////////////////////////////// -// CResizableDialog message handlers - - -BOOL CResizableDialog::OnInitDialog() -{ - CDialog::OnInitDialog(); - - UpdateGripPos(); - - // gets the template size as the min track size - CRect rc; - GetWindowRect(&rc); - m_ptMinTrackSize.x = rc.Width(); - m_ptMinTrackSize.y = rc.Height(); - - m_bInitDone = TRUE; - - return TRUE; // return TRUE unless you set the focus to a control - // EXCEPTION: OCX Property Pages should return FALSE -} - -void CResizableDialog::OnDestroy() -{ - CDialog::OnDestroy(); - - if (m_bEnableSaveRestore) - SaveWindowRect(); - - // remove old windows - m_arrLayout.RemoveAll(); -} - -void CResizableDialog::OnPaint() -{ - CPaintDC dc(this); // device context for painting - - if (m_bShowGrip && !IsZoomed()) - { - // draw size-grip - dc.DrawFrameControl(&m_rcGripRect, DFC_SCROLL, DFCS_SCROLLSIZEGRIP); - } -} - -void CResizableDialog::OnSize(UINT nType, int cx, int cy) -{ - CWnd::OnSize(nType, cx, cy); - - if (nType == SIZE_MAXHIDE || nType == SIZE_MAXSHOW) - return; // arrangement not needed - - if (m_bInitDone) - { - ArrangeLayout(); - } -} - -UINT CResizableDialog::OnNcHitTest(CPoint point) -{ - CPoint pt = point; - ScreenToClient(&pt); - - // if in size grip and in client area - if (m_bShowGrip && m_rcGripRect.PtInRect(pt) && - pt.x >= 0 && pt.y >= 0) - return HTBOTTOMRIGHT; - - return CDialog::OnNcHitTest(point); -} - -void CResizableDialog::OnGetMinMaxInfo(MINMAXINFO FAR* lpMMI) -{ - if (!m_bInitDone) - return; - - if (m_bUseMinTrack) - lpMMI->ptMinTrackSize = m_ptMinTrackSize; - - if (m_bUseMaxTrack) - lpMMI->ptMaxTrackSize = m_ptMaxTrackSize; - - if (m_bUseMaxRect) - { - lpMMI->ptMaxPosition = m_ptMaxPos; - lpMMI->ptMaxSize = m_ptMaxSize; - } -} - -// layout functions - -void CResizableDialog::AddAnchor(HWND wnd, CSize tl_type, CSize br_type) -{ - ASSERT(wnd != NULL && ::IsWindow(wnd)); - ASSERT(::IsChild(*this, wnd)); - ASSERT(tl_type != NOANCHOR); - - // get control's window class - - CString st; - GetClassName(wnd, st.GetBufferSetLength(MAX_PATH), MAX_PATH); - st.ReleaseBuffer(); - st.MakeUpper(); - - // add the style 'clipsiblings' to a GroupBox - // to avoid unnecessary repainting of controls inside - if (st == "BUTTON") - { - DWORD style = GetWindowLong(wnd, GWL_STYLE); - if (style & BS_GROUPBOX) - SetWindowLong(wnd, GWL_STYLE, style | WS_CLIPSIBLINGS); - } - - // wnd classes that don't redraw client area correctly - // when the hor scroll pos changes due to a resizing - BOOL hscroll = FALSE; - if (st == "LISTBOX") - hscroll = TRUE; - - // wnd classes that need refresh when resized - BOOL refresh = FALSE; - if (st == "STATIC") - { - DWORD style = GetWindowLong(wnd, GWL_STYLE); - - switch (style & SS_TYPEMASK) - { - case SS_LEFT: - case SS_CENTER: - case SS_RIGHT: - // word-wrapped text needs refresh - refresh = TRUE; - } - - // centered images or text need refresh - if (style & SS_CENTERIMAGE) - refresh = TRUE; - - // simple text never needs refresh - if (style & SS_TYPEMASK == SS_SIMPLE) - refresh = FALSE; - } - - // get dialog's and control's rect - CRect wndrc, objrc; - - GetClientRect(&wndrc); - ::GetWindowRect(wnd, &objrc); - ScreenToClient(&objrc); - - CSize tl_margin, br_margin; - - if (br_type == NOANCHOR) - br_type = tl_type; - - // calculate margin for the top-left corner - - tl_margin.cx = objrc.left - wndrc.Width() * tl_type.cx / 100; - tl_margin.cy = objrc.top - wndrc.Height() * tl_type.cy / 100; - - // calculate margin for the bottom-right corner - - br_margin.cx = objrc.right - wndrc.Width() * br_type.cx / 100; - br_margin.cy = objrc.bottom - wndrc.Height() * br_type.cy / 100; - - // add to the list - Layout obj(wnd, tl_type, tl_margin, br_type, br_margin, hscroll, refresh); - m_arrLayout.Add(obj); -} - -void CResizableDialog::ArrangeLayout() -{ - // update size-grip - InvalidateRect(&m_rcGripRect); - UpdateGripPos(); - InvalidateRect(&m_rcGripRect); - - // init some vars - CRect wndrc; - GetClientRect(&wndrc); - - int i, count = m_arrLayout.GetSize(); - HDWP hdwp = BeginDeferWindowPos(count); - - for (i=0; iGetWindowRect(&objrc); - ScreenToClient(&objrc); - - // calculate new top-left corner - - newrc.left = obj.tl_margin.cx + wndrc.Width() * obj.tl_type.cx / 100; - newrc.top = obj.tl_margin.cy + wndrc.Height() * obj.tl_type.cy / 100; - - // calculate new bottom-right corner - - newrc.right = obj.br_margin.cx + wndrc.Width() * obj.br_type.cx / 100; - newrc.bottom = obj.br_margin.cy + wndrc.Height() * obj.br_type.cy / 100; - - if (!newrc.EqualRect(&objrc)) - { - if (obj.adj_hscroll) - { - // needs repainting, due to horiz scrolling - int diff = newrc.Width() - objrc.Width(); - int max = wnd->GetScrollLimit(SB_HORZ); - - obj.need_refresh = FALSE; - if (max > 0 && wnd->GetScrollPos(SB_HORZ) > max - diff) - { - obj.need_refresh = TRUE; - } - } - - // set flags - DWORD flags = SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREPOSITION; - if (newrc.TopLeft() == objrc.TopLeft()) - flags |= SWP_NOMOVE; - if (newrc.Size() == objrc.Size()) - flags |= SWP_NOSIZE; - - DeferWindowPos(hdwp, obj.hwnd, NULL, newrc.left, newrc.top, - newrc.Width(), newrc.Height(), flags); - } - } - // go re-arrange child windows - EndDeferWindowPos(hdwp); - - // refresh those that need - for (i=0; iInvalidate(); - wnd->UpdateWindow(); - } - } -} - -void CResizableDialog::UpdateGripPos() -{ - // size-grip goes bottom right in the client area - - GetClientRect(&m_rcGripRect); - - m_rcGripRect.left = m_rcGripRect.right - m_szGripSize.cx; - m_rcGripRect.top = m_rcGripRect.bottom - m_szGripSize.cy; -} - -// protected members - -void CResizableDialog::ShowSizeGrip(BOOL bShow) -{ - if (m_bShowGrip != bShow) - { - m_bShowGrip = bShow; - InvalidateRect(&m_rcGripRect); - } -} - -void CResizableDialog::SetMaximizedRect(const CRect& rc) -{ - m_bUseMaxRect = TRUE; - - m_ptMaxPos = rc.TopLeft(); - m_ptMaxSize.x = rc.Width(); - m_ptMaxSize.y = rc.Height(); -} - -void CResizableDialog::ResetMaximizedRect() -{ - m_bUseMaxRect = FALSE; -} - -void CResizableDialog::SetMinTrackSize(const CSize& size) -{ - m_bUseMinTrack = TRUE; - - m_ptMinTrackSize.x = size.cx; - m_ptMinTrackSize.y = size.cy; -} - -void CResizableDialog::ResetMinTrackSize() -{ - m_bUseMinTrack = FALSE; -} - -void CResizableDialog::SetMaxTrackSize(const CSize& size) -{ - m_bUseMaxTrack = TRUE; - - m_ptMaxTrackSize.x = size.cx; - m_ptMaxTrackSize.y = size.cy; -} - -void CResizableDialog::ResetMaxTrackSize() -{ - m_bUseMaxTrack = FALSE; -} - -// NOTE: this must be called after all the other settings -// to have the dialog and its controls displayed properly -void CResizableDialog::EnableSaveRestore(LPCTSTR pszSection, LPCTSTR pszEntry) -{ - m_sSection = pszSection; - m_sEntry = pszEntry; - - m_bEnableSaveRestore = TRUE; - - LoadWindowRect(); -} - - -// used to save/restore window's size and position -// either in the registry or a private .INI file -// depending on your application settings - -#define PROFILE_FMT _T("%d,%d,%d,%d,%d,%d") - -void CResizableDialog::SaveWindowRect() -{ - CString data; - WINDOWPLACEMENT wp; - - ZeroMemory(&wp, sizeof(WINDOWPLACEMENT)); - wp.length = sizeof(WINDOWPLACEMENT); - GetWindowPlacement(&wp); - - RECT& rc = wp.rcNormalPosition; // alias - - data.Format(PROFILE_FMT, rc.left, rc.top, - rc.right, rc.bottom, wp.showCmd, wp.flags); - - AfxGetApp()->WriteProfileString(m_sSection, m_sEntry, data); -} - -void CResizableDialog::LoadWindowRect() -{ - CString data; - WINDOWPLACEMENT wp; - - data = AfxGetApp()->GetProfileString(m_sSection, m_sEntry); - - if (data.IsEmpty()) // never saved before - return; - - ZeroMemory(&wp, sizeof(WINDOWPLACEMENT)); - wp.length = sizeof(WINDOWPLACEMENT); - GetWindowPlacement(&wp); - - RECT& rc = wp.rcNormalPosition; // alias - - if (_stscanf(data, PROFILE_FMT, &rc.left, &rc.top, - &rc.right, &rc.bottom, &wp.showCmd, &wp.flags) == 6) - { - SetWindowPlacement(&wp); - } -} diff --git a/win32/ResizableDialog.h b/win32/ResizableDialog.h deleted file mode 100644 index 89c5b29d..00000000 --- a/win32/ResizableDialog.h +++ /dev/null @@ -1,173 +0,0 @@ -#if !defined(AFX_RESIZABLEDIALOG_H__INCLUDED_) -#define AFX_RESIZABLEDIALOG_H__INCLUDED_ - -#if _MSC_VER > 1000 -#pragma once -#endif // _MSC_VER > 1000 - -// ResizableDialog.h : header file -// -///////////////////////////////////////////////////////////////////////////// -// -// Copyright (C) 2000 by Paolo Messina -// (ppescher@yahoo.com) -// -// Free for non-commercial use. -// You may change the code to your needs, -// provided that credits to the original -// author is given in the modified files. -// -///////////////////////////////////////////////////////////////////////////// - -#include -#include - -// useful compatibility constants (the only one required is NOANCHOR) - -#if !defined(__SIZE_ANCHORS_) -#define __SIZE_ANCHORS_ - -const CSize - NOANCHOR(-1,-1), - TOP_LEFT(0,0), TOP_CENTER(50,0), TOP_RIGHT(100,0), - MIDDLE_LEFT(0,50), MIDDLE_CENTER(50,50), MIDDLE_RIGHT(100,50), - BOTTOM_LEFT(0,100), BOTTOM_CENTER(50,100), BOTTOM_RIGHT(100,100); - -#endif // !defined(__SIZE_ANCHORS_) - -///////////////////////////////////////////////////////////////////////////// -// CResizableDialog window - -class CResizableDialog : public CDialog -{ - -// Construction -public: - CResizableDialog(); - CResizableDialog(UINT nIDTemplate, CWnd* pParentWnd = NULL); - CResizableDialog(LPCTSTR lpszTemplateName, CWnd* pParentWnd = NULL); - -// Attributes -private: - // flags - BOOL m_bShowGrip; - BOOL m_bUseMaxTrack; - BOOL m_bUseMinTrack; - BOOL m_bUseMaxRect; - BOOL m_bEnableSaveRestore; - - // internal status - CString m_sSection; // section name and - CString m_sEntry; // entry for save/restore - - BOOL m_bInitDone; // if all internal vars initialized - - SIZE m_szGripSize; // set at construction time - - CRect m_rcGripRect; // current pos of grip - - POINT m_ptMinTrackSize; // min tracking size - POINT m_ptMaxTrackSize; // max tracking size - POINT m_ptMaxPos; // maximized position - POINT m_ptMaxSize; // maximized size - - class Layout - { - public: - HWND hwnd; - - BOOL adj_hscroll; - BOOL need_refresh; - - // upper-left corner - CSize tl_type; - CSize tl_margin; - - // bottom-right corner - CSize br_type; - CSize br_margin; - - public: - Layout() - : hwnd(NULL), adj_hscroll(FALSE), need_refresh(FALSE), - tl_type(0,0), tl_margin(0,0), - br_type(0,0), br_margin(0,0) - { - }; - - Layout(HWND hw, SIZE tl_t, SIZE tl_m, - SIZE br_t, SIZE br_m, BOOL hscroll, BOOL refresh) - { - hwnd = hw; - - adj_hscroll = hscroll; - need_refresh = refresh; - - tl_type = tl_t; - tl_margin = tl_m; - - br_type = br_t; - br_margin = br_m; - }; - }; - - CArray m_arrLayout; // list of repositionable controls - -// Operations -public: - -// Overrides - // ClassWizard generated virtual function overrides - //{{AFX_VIRTUAL(CResizableDialog) - //}}AFX_VIRTUAL - -// Implementation -public: - virtual ~CResizableDialog(); - -// used internally -private: - void Construct(); - void LoadWindowRect(); - void SaveWindowRect(); - void ArrangeLayout(); - void UpdateGripPos(); - -// callable from derived classes -//protected: -public: - void AddAnchor(HWND wnd, CSize tl_type, - CSize br_type = NOANCHOR); // add anchors to a control - void AddAnchor(UINT ctrl_ID, CSize tl_type, - CSize br_type = NOANCHOR) // add anchors to a control - { - AddAnchor(::GetDlgItem(*this, ctrl_ID), tl_type, br_type); - }; - void ShowSizeGrip(BOOL bShow); // show or hide the size grip - void SetMaximizedRect(const CRect& rc); // set window rect when maximized - void ResetMaximizedRect(); // reset to default maximized rect - void SetMinTrackSize(const CSize& size); // set minimum tracking size - void ResetMinTrackSize(); // reset to default minimum tracking size - void SetMaxTrackSize(const CSize& size); // set maximum tracking size - void ResetMaxTrackSize(); // reset to default maximum tracking size - void EnableSaveRestore(LPCTSTR pszSection, LPCTSTR pszEntry); // section and entry in app's profile - -// Generated message map functions -protected: - //{{AFX_MSG(CResizableDialog) - virtual BOOL OnInitDialog(); - afx_msg UINT OnNcHitTest(CPoint point); - afx_msg void OnGetMinMaxInfo(MINMAXINFO FAR* lpMMI); - afx_msg void OnSize(UINT nType, int cx, int cy); - afx_msg void OnDestroy(); - afx_msg void OnPaint(); - //}}AFX_MSG - DECLARE_MESSAGE_MAP() -}; - -///////////////////////////////////////////////////////////////////////////// - -//{{AFX_INSERT_LOCATION}} -// Microsoft Visual C++ will insert additional declarations immediately before the previous line. - -#endif // !defined(AFX_RESIZABLEDIALOG_H__INCLUDED_) \ No newline at end of file