From 1658f1717ec9eb7e9dd25ded64ac64edfbfb50c2 Mon Sep 17 00:00:00 2001 From: oddsock Date: Wed, 26 Feb 2003 23:52:23 +0000 Subject: [PATCH] added parsing of new icy-audio-info header which will be used to communicate things like samplerate/quality/number of channels to icecast2. This info will be then forwarded to the yp servers for better stream info. also factored out some logic in source_main into common functions added a few new routines into util.c (taken from Brendan's updates to libshout) svn path=/trunk/icecast/; revision=4379 --- src/source.c | 213 ++++++++++++++++++++++++++++++++++++++++----------- src/source.h | 2 + src/util.c | 136 ++++++++++++++++++++++++++++++++ src/util.h | 14 ++++ src/yp.c | 23 ++++-- src/yp.h | 1 + 6 files changed, 339 insertions(+), 50 deletions(-) diff --git a/src/source.c b/src/source.c index ab91b1e6..2a9f355a 100644 --- a/src/source.c +++ b/src/source.c @@ -34,12 +34,28 @@ #undef CATMODULE #define CATMODULE "source" +#define YP_SERVER_NAME 1 +#define YP_SERVER_DESC 2 +#define YP_SERVER_GENRE 3 +#define YP_SERVER_URL 4 +#define YP_BITRATE 5 +#define YP_AUDIO_INFO 6 +#define YP_SERVER_TYPE 7 +#define YP_CURRENT_SONG 8 +#define YP_URL_TIMEOUT 9 +#define YP_TOUCH_INTERVAL 10 +#define YP_LAST_TOUCH 11 + /* avl tree helper */ static int _compare_clients(void *compare_arg, void *a, void *b); static int _remove_client(void *key); static int _free_client(void *key); +static int _parse_audio_info(source_t *source, char *s); +static void _add_yp_info(source_t *source, char *stat_name, + void *info, int type); -source_t *source_create(client_t *client, connection_t *con, http_parser_t *parser, const char *mount, format_type_t type) +source_t *source_create(client_t *client, connection_t *con, + http_parser_t *parser, const char *mount, format_type_t type) { int i = 0; source_t *src; @@ -57,6 +73,7 @@ source_t *source_create(client_t *client, connection_t *con, http_parser_t *pars src->num_yp_directories = 0; src->listeners = 0; src->send_return = 0; + src->audio_info = util_dict_new(); for (i=0;inum_yp_directories;i++) { if (config_get_config()->yp_url[i]) { src->ypdata[src->num_yp_directories] = yp_create_ypdata(); @@ -124,6 +141,7 @@ int source_free_source(void *key) for (i=0; inum_yp_directories; i++) { yp_destroy_ypdata(source->ypdata[i]); } + util_dict_free(source->audio_info); free(source); return 1; @@ -149,9 +167,11 @@ void *source_main(void *arg) refbuf_t *refbuf, *abuf; int data_done; - int listeners = 0; - int i=0; - int suppress_yp = 0; + int listeners = 0; + int i=0; + int suppress_yp = 0; + util_dict *audio_info; + char *ai; long queue_limit = config_get_config()->queue_size_limit; @@ -191,59 +211,32 @@ void *source_main(void *arg) stats_event(source->mount, "listeners", "0"); source->listeners = 0; if ((s = httpp_getvar(source->parser, "ice-name"))) { - for (i=0;inum_yp_directories;i++) { - if (source->ypdata[i]->server_name) { - free(source->ypdata[i]->server_name); - } - source->ypdata[i]->server_name = malloc(strlen(s) +1); - strcpy(source->ypdata[i]->server_name, s); - } - stats_event(source->mount, "name", s); + _add_yp_info(source, "server name", s, YP_SERVER_NAME); } if ((s = httpp_getvar(source->parser, "ice-url"))) { - for (i=0;inum_yp_directories;i++) { - if (source->ypdata[i]->server_url) { - free(source->ypdata[i]->server_url); - } - source->ypdata[i]->server_url = malloc(strlen(s) +1); - strcpy(source->ypdata[i]->server_url, s); - } - stats_event(source->mount, "url", s); + _add_yp_info(source, "server url", s, YP_SERVER_URL); } if ((s = httpp_getvar(source->parser, "ice-genre"))) { - for (i=0;inum_yp_directories;i++) { - if (source->ypdata[i]->server_genre) { - free(source->ypdata[i]->server_genre); - } - source->ypdata[i]->server_genre = malloc(strlen(s) +1); - strcpy(source->ypdata[i]->server_genre, s); - } - stats_event(source->mount, "genre", s); + _add_yp_info(source, "genre", s, YP_SERVER_GENRE); } if ((s = httpp_getvar(source->parser, "ice-bitrate"))) { - for (i=0;inum_yp_directories;i++) { - if (source->ypdata[i]->bitrate) { - free(source->ypdata[i]->bitrate); - } - source->ypdata[i]->bitrate = malloc(strlen(s) +1); - strcpy(source->ypdata[i]->bitrate, s); - } - stats_event(source->mount, "bitrate", s); + _add_yp_info(source, "bitrate", s, YP_BITRATE); } if ((s = httpp_getvar(source->parser, "ice-description"))) { - for (i=0;inum_yp_directories;i++) { - if (source->ypdata[i]->server_desc) { - free(source->ypdata[i]->server_desc); - } - source->ypdata[i]->server_desc = malloc(strlen(s) +1); - strcpy(source->ypdata[i]->server_desc, s); - } - stats_event(source->mount, "description", s); + _add_yp_info(source, "server description", s, YP_SERVER_DESC); } if ((s = httpp_getvar(source->parser, "ice-private"))) { stats_event(source->mount, "public", s); suppress_yp = atoi(s); } + if ((s = httpp_getvar(source->parser, "ice-audio-info"))) { + if (_parse_audio_info(source, s)) { + ai = util_dict_urlencode(source->audio_info, '&'); + _add_yp_info(source, "audio info", + ai, + YP_AUDIO_INFO); + } + } for (i=0;inum_yp_directories;i++) { if (source->ypdata[i]->server_type) { free(source->ypdata[i]->server_type); @@ -275,8 +268,14 @@ void *source_main(void *arg) current_time = time(NULL); + _add_yp_info(source, "last_touch", (void *)current_time, + YP_LAST_TOUCH); + for (i=0;inum_yp_directories;i++) { - source->ypdata[i]->yp_last_touch = current_time; + /* Give the source 5 seconds to update the metadata + before we do our first touch */ + source->ypdata[i]->yp_last_touch = current_time - + source->ypdata[i]->yp_touch_interval + 5; /* Don't permit touch intervals of less than 30 seconds */ if (source->ypdata[i]->yp_touch_interval <= 30) { source->ypdata[i]->yp_touch_interval = 30; @@ -622,3 +621,127 @@ static int _free_client(void *key) return 1; } +static int _parse_audio_info(source_t *source, char *s) +{ + char *token; + char *pvar; + char *variable; + char *value; + + while ((token = strtok(s,";")) != NULL) { + 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)); + util_dict_set(source->audio_info, variable, value); + stats_event(source->mount, variable, value); + } + if (variable) { + free(variable); + } + if (value) { + free(value); + } + } + s = NULL; + } + return 1; +} + +static void _add_yp_info(source_t *source, char *stat_name, + void *info, int type) +{ + int i; + if (!info) { + return; + } + for (i=0;inum_yp_directories;i++) { + switch (type) { + case YP_SERVER_NAME: + if (source->ypdata[i]->server_name) { + free(source->ypdata[i]->server_name); + } + source->ypdata[i]->server_name = + malloc(strlen((char *)info) +1); + strcpy(source->ypdata[i]->server_name, (char *)info); + stats_event(source->mount, stat_name, (char *)info); + break; + case YP_SERVER_DESC: + if (source->ypdata[i]->server_desc) { + free(source->ypdata[i]->server_desc); + } + source->ypdata[i]->server_desc = + malloc(strlen((char *)info) +1); + strcpy(source->ypdata[i]->server_desc, (char *)info); + stats_event(source->mount, stat_name, (char *)info); + break; + case YP_SERVER_GENRE: + if (source->ypdata[i]->server_genre) { + free(source->ypdata[i]->server_genre); + } + source->ypdata[i]->server_genre = + malloc(strlen((char *)info) +1); + strcpy(source->ypdata[i]->server_genre, (char *)info); + stats_event(source->mount, stat_name, (char *)info); + break; + case YP_SERVER_URL: + if (source->ypdata[i]->server_url) { + free(source->ypdata[i]->server_url); + } + source->ypdata[i]->server_url = + malloc(strlen((char *)info) +1); + strcpy(source->ypdata[i]->server_url, (char *)info); + stats_event(source->mount, stat_name, (char *)info); + break; + case YP_BITRATE: + if (source->ypdata[i]->bitrate) { + free(source->ypdata[i]->bitrate); + } + source->ypdata[i]->bitrate = + malloc(strlen((char *)info) +1); + strcpy(source->ypdata[i]->bitrate, (char *)info); + stats_event(source->mount, stat_name, (char *)info); + break; + case YP_AUDIO_INFO: + if (source->ypdata[i]->audio_info) { + free(source->ypdata[i]->audio_info); + } + source->ypdata[i]->audio_info = + malloc(strlen((char *)info) +1); + strcpy(source->ypdata[i]->audio_info, (char *)info); + break; + case YP_SERVER_TYPE: + if (source->ypdata[i]->server_type) { + free(source->ypdata[i]->server_type); + } + source->ypdata[i]->server_type = + malloc(strlen((char *)info) +1); + strcpy(source->ypdata[i]->server_type, (char *)info); + break; + case YP_CURRENT_SONG: + if (source->ypdata[i]->current_song) { + free(source->ypdata[i]->current_song); + } + source->ypdata[i]->current_song = + malloc(strlen((char *)info) +1); + strcpy(source->ypdata[i]->current_song, (char *)info); + stats_event(source->mount, stat_name, (char *)info); + break; + case YP_URL_TIMEOUT: + source->ypdata[i]->yp_url_timeout = (int)info; + break; + case YP_LAST_TOUCH: + source->ypdata[i]->yp_last_touch = (int)info; + break; + case YP_TOUCH_INTERVAL: + source->ypdata[i]->yp_touch_interval = (int)info; + break; + } + } +} diff --git a/src/source.h b/src/source.h index 06ffc026..4a2701a8 100644 --- a/src/source.h +++ b/src/source.h @@ -3,6 +3,7 @@ #include "config.h" #include "yp.h" +#include "util.h" #include "format.h" typedef struct source_tag @@ -27,6 +28,7 @@ typedef struct source_tag rwlock_t *shutdown_rwlock; ypdata_t *ypdata[MAX_YP_DIRECTORIES]; + util_dict *audio_info; int num_yp_directories; long listeners; long max_listeners; diff --git a/src/util.c b/src/util.c index 4405ca64..cd49d582 100644 --- a/src/util.c +++ b/src/util.c @@ -385,3 +385,139 @@ char *util_base64_decode(unsigned char *input) return result; } +util_dict *util_dict_new(void) +{ + return (util_dict *)calloc(1, sizeof(util_dict)); +} + +void util_dict_free(util_dict *dict) +{ + util_dict *next; + + while (dict) { + next = dict->next; + + if (dict->key) + free (dict->key); + if (dict->val) + free (dict->val); + free (dict); + + dict = next; + } +} + +const char *util_dict_get(util_dict *dict, const char *key) +{ + while (dict) { + if (!strcmp(key, dict->key)) + return dict->val; + dict = dict->next; + } +} + +int util_dict_set(util_dict *dict, const char *key, const char *val) +{ + util_dict *prev; + + if (!dict || !key) { + ERROR0("NULL values passed to util_dict_set()"); + return 0; + } + + prev = NULL; + while (dict) { + if (!dict->key || !strcmp(dict->key, key)) + break; + prev = dict; + dict = dict->next; + } + + if (!dict) { + dict = util_dict_new(); + if (!dict) { + ERROR0("unable to allocate new dictionary"); + return 0; + } + if (prev) + prev->next = dict; + } + + if (dict->key) + free (dict->val); + else if (!(dict->key = strdup(key))) { + if (prev) + prev->next = NULL; + util_dict_free (dict); + + ERROR0("unable to allocate new dictionary key"); + return 0; + } + + dict->val = strdup(val); + if (!dict->val) { + ERROR0("unable to allocate new dictionary value"); + return 0; + } + + return 1; +} + +/* given a dictionary, URL-encode each key and val and + stringify them in order as key=val&key=val... if val + is set, or just key&key if val is NULL. + TODO: Memory management needs overhaul. */ +char *util_dict_urlencode(util_dict *dict, char delim) +{ + char *res, *tmp; + char *enc; + int start = 1; + + for (res = NULL; dict; dict = dict->next) { + /* encode key */ + if (!dict->key) + continue; + if (!(enc = util_url_escape(dict->key))) { + if (res) + free(res); + return NULL; + } + if (start) { + if (!(res = malloc(strlen(enc) + 1))) { + free(enc); + return NULL; + } + sprintf(res, "%s", enc); + free(enc); + start = 0; + } else { + if (!(tmp = realloc(res, strlen(res) + strlen(enc) + 2))) { + free(enc); + free(res); + return NULL; + } else + res = tmp; + sprintf(res + strlen(res), "%c%s", delim, enc); + free(enc); + } + + /* encode value */ + if (!dict->val) + continue; + if (!(enc = util_url_escape(dict->val))) { + free(res); + return NULL; + } + + if (!(tmp = realloc(res, strlen(res) + strlen(enc) + 2))) { + free(enc); + free(res); + return NULL; + } else + res = tmp; + sprintf(res + strlen(res), "=%s", enc); + free(enc); + } + + return res; +} diff --git a/src/util.h b/src/util.h index b350882f..e8fc7e62 100644 --- a/src/util.h +++ b/src/util.h @@ -16,4 +16,18 @@ char *util_base64_decode(unsigned char *input); char *util_url_escape(char *src); +/* String dictionary type, without support for NULL keys, or multiple + * instances of the same key */ +typedef struct _util_dict { + char *key; + char *val; + struct _util_dict *next; +} util_dict; + +util_dict *util_dict_new(void); +void util_dict_free(util_dict *dict); +/* dict, key must not be NULL. */ +int util_dict_set(util_dict *dict, const char *key, const char *val); +const char *util_dict_get(util_dict *dict, const char *key); +char *util_dict_urlencode(util_dict *dict, char delim); #endif /* __UTIL_H__ */ diff --git a/src/yp.c b/src/yp.c index 0447a6be..4b72c0b2 100644 --- a/src/yp.c +++ b/src/yp.c @@ -196,8 +196,9 @@ int yp_add(source_t *source, int which) if (ok) { if (source->ypdata[i]) { - url_size = strlen("action=add&sn=&genre=&cpswd=&desc=&url=" - "&listenurl=&type=&b=") + 1; + url_size = strlen("action=add&sn=&genre=&cpswd=" + "&desc=&url=&listenurl=&type=&b=&") + + 1; if (source->ypdata[i]->server_name) { url_size += strlen(source->ypdata[i]->server_name); } @@ -261,10 +262,18 @@ int yp_add(source_t *source, int which) source->ypdata[i]->current_song = (char *)malloc(1); source->ypdata[i]->current_song[0] = 0; } + if (source->ypdata[i]->audio_info) { + url_size += strlen(source->ypdata[i]->audio_info); + } + else { + source->ypdata[i]->audio_info = (char *)malloc(1); + source->ypdata[i]->audio_info[0] = 0; + } + url_size += 1024; url = malloc(url_size); - sprintf(url, "action=add&sn=%s&genre=%s&cpswd=%s&desc=%s&url=%s" - "&listenurl=%s&type=%s&b=%s", + sprintf(url, "action=add&sn=%s&genre=%s&cpswd=%s&desc=" + "%s&url=%s&listenurl=%s&type=%s&b=%s&%s", source->ypdata[i]->server_name, source->ypdata[i]->server_genre, source->ypdata[i]->cluster_password, @@ -272,7 +281,8 @@ int yp_add(source_t *source, int which) source->ypdata[i]->server_url, source->ypdata[i]->listen_url, source->ypdata[i]->server_type, - source->ypdata[i]->bitrate); + source->ypdata[i]->bitrate, + source->ypdata[i]->audio_info); curl_con = curl_get_connection(); if (curl_con < 0) { @@ -350,6 +360,9 @@ void yp_destroy_ypdata(ypdata_t *ypdata) if (ypdata->server_type) { free(ypdata->server_type); } + if (ypdata->audio_info) { + free(ypdata->audio_info); + } free(ypdata); } } diff --git a/src/yp.h b/src/yp.h index 1e314eaa..4b5fcc2f 100644 --- a/src/yp.h +++ b/src/yp.h @@ -16,6 +16,7 @@ typedef struct ypdata_tag char *server_url; char *listen_url; char *bitrate; + char *audio_info; char *server_type; char *current_song; char *yp_url;