コード例 #1
0
ファイル: auth.c プロジェクト: icqparty/radio_server
static void queue_auth_client (auth_client *auth_user, mount_proxy *mountinfo)
{
    auth_t *auth;

    if (auth_user == NULL)
        return;
    auth_user->next = NULL;
    if (mountinfo)
    {
        auth = mountinfo->auth;
        thread_mutex_lock (&auth->lock);
        if (auth_user->client)
            auth_user->client->auth = auth;
        auth->refcount++;
    }
    else
    {
        if (auth_user->client == NULL || auth_user->client->auth == NULL)
        {
            ICECAST_LOG_WARN("internal state is incorrect for %p", auth_user->client);
            return;
        }
        auth = auth_user->client->auth;
        thread_mutex_lock (&auth->lock);
    }
    ICECAST_LOG_DEBUG("...refcount on auth_t %s is now %d", auth->mount, auth->refcount);
    *auth->tailp = auth_user;
    auth->tailp = &auth_user->next;
    auth->pending_count++;
    ICECAST_LOG_INFO("auth on %s has %d pending", auth->mount, auth->pending_count);
    thread_mutex_unlock (&auth->lock);
}
コード例 #2
0
ファイル: client.c プロジェクト: TeddyRilliot/icecast
/* create a client_t with the provided connection and parser details. Return
 * 0 on success, -1 if server limit has been reached.  In either case a
 * client_t is returned just in case a message needs to be returned. Should
 * be called with global lock held.
 */
int client_create (client_t **c_ptr, connection_t *con, http_parser_t *parser)
{
    ice_config_t *config;
    client_t *client = (client_t *)calloc(1, sizeof(client_t));
    int ret = -1;

    if (client == NULL)
        abort();

    config = config_get_config ();

    global.clients++;
    if (config->client_limit < global.clients)
        ICECAST_LOG_WARN("server client limit reached (%d/%d)", config->client_limit, global.clients);
    else
        ret = 0;

    config_release_config ();

    stats_event_args (NULL, "clients", "%d", global.clients);
    client->con = con;
    client->parser = parser;
    client->refbuf = refbuf_new (PER_CLIENT_REFBUF_SIZE);
    client->refbuf->len = 0; /* force reader code to ignore buffer contents */
    client->pos = 0;
    client->write_to_client = format_generic_write_to_client;
    *c_ptr = client;

    return ret;
}
コード例 #3
0
/* wrapper for stats_event, this takes a charset to convert from */
void stats_event_conv(const char *mount, const char *name, const char *value, const char *charset)
{
    const char *metadata = value;
    xmlBufferPtr conv = xmlBufferCreate ();

    if (charset)
    {
        xmlCharEncodingHandlerPtr handle = xmlFindCharEncodingHandler (charset);

        if (handle)
        {
            xmlBufferPtr raw = xmlBufferCreate ();
            xmlBufferAdd (raw, (const xmlChar *)value, strlen (value));
            if (xmlCharEncInFunc (handle, conv, raw) > 0)
                metadata = (char *)xmlBufferContent (conv);
            xmlBufferFree (raw);
            xmlCharEncCloseFunc (handle);
        }
        else
            ICECAST_LOG_WARN("No charset found for \"%s\"", charset);
    }

    stats_event (mount, name, metadata);
    xmlBufferFree (conv);
}
コード例 #4
0
ファイル: format.c プロジェクト: Boltsie/Icecast-Server
format_type_t format_get_type (const char *contenttype)
{
    if(strcmp(contenttype, "application/x-ogg") == 0)
        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/ogg") == 0)
        return FORMAT_TYPE_OGG;
    else if(strcmp(contenttype, "video/ogg") == 0)
        return FORMAT_TYPE_OGG;
    else if(strcmp(contenttype, "audio/webm") == 0)
        return FORMAT_TYPE_EBML;
    else if(strcmp(contenttype, "video/webm") == 0)
        return FORMAT_TYPE_EBML;
    else if(strcmp(contenttype, "audio/x-matroska") == 0)
        return FORMAT_TYPE_EBML;
    else if(strcmp(contenttype, "video/x-matroska") == 0)
        return FORMAT_TYPE_EBML;
    else if(strcmp(contenttype, "video/x-matroska-3d") == 0)
        return FORMAT_TYPE_EBML;
    else
        /* We default to the Generic format handler, which
           can handle many more formats than just mp3.
           Let's warn that this is not well supported */
        ICECAST_LOG_WARN("Unsupported or legacy stream type: \"%s\". Falling back to generic minimal handler for best effort.", contenttype);
        return FORMAT_TYPE_GENERIC;
}
コード例 #5
0
ファイル: auth_url.c プロジェクト: TeddyRilliot/icecast
static void url_stream_auth (auth_client *auth_user)
{
    ice_config_t *config;
    int port;
    client_t *client = auth_user->client;
    auth_url *url = client->auth->state;
    char *mount, *host, *user, *pass, *ipaddr, *admin="";
    char post [4096];

    if (strchr (url->stream_auth, '@') == NULL)
    {
        if (url->userpwd)
            curl_easy_setopt (url->handle, CURLOPT_USERPWD, url->userpwd);
        else
            curl_easy_setopt (url->handle, CURLOPT_USERPWD, "");
    }
    else
        curl_easy_setopt (url->handle, CURLOPT_USERPWD, "");
    curl_easy_setopt (url->handle, CURLOPT_URL, url->stream_auth);
    curl_easy_setopt (url->handle, CURLOPT_POSTFIELDS, post);
    curl_easy_setopt (url->handle, CURLOPT_WRITEHEADER, auth_user);
    if (strcmp (auth_user->mount, httpp_getvar (client->parser, HTTPP_VAR_URI)) != 0)
        admin = "&admin=1";
    mount = util_url_escape (auth_user->mount);
    config = config_get_config ();
    host = util_url_escape (config->hostname);
    port = config->port;
    config_release_config ();
    ipaddr = util_url_escape (client->con->ip);

    if (client->username) {
        user = util_url_escape(client->username);
    } else {
        user = strdup("");
    }

    if (client->password) {
        pass = util_url_escape(client->password);
    } else {
        pass = strdup("");
    }

    snprintf (post, sizeof (post),
              "action=stream_auth&mount=%s&ip=%s&server=%s&port=%d&user=%s&pass=%s%s",
              mount, ipaddr, host, port, user, pass, admin);
    free (ipaddr);
    free (user);
    free (pass);
    free (mount);
    free (host);

    client->authenticated = 0;
    if (curl_easy_perform (url->handle))
        ICECAST_LOG_WARN("auth to server %s failed with %s", url->stream_auth, url->errormsg);
}
コード例 #6
0
ファイル: format_mp3.c プロジェクト: TeddyRilliot/icecast
static void write_mp3_to_file (struct source_tag *source, refbuf_t *refbuf)
{
    if (refbuf->len == 0)
        return;
    if (fwrite (refbuf->data, 1, refbuf->len, source->dumpfile) < (size_t)refbuf->len)
    {
        ICECAST_LOG_WARN("Write to dump file failed, disabling");
        fclose (source->dumpfile);
        source->dumpfile = NULL;
    }
}
コード例 #7
0
/* simple name=tag stat create/update */
void stats_event(const char *source, const char *name, const char *value)
{
    stats_event_t *event;

    if (value && xmlCheckUTF8 ((unsigned char *)value) == 0)
    {
        ICECAST_LOG_WARN("seen non-UTF8 data, probably incorrect metadata (%s, %s)", name, value);
        return;
    }
    event = build_event(source, name, value);
    if (event)
        queue_global_event(event);
}
コード例 #8
0
ファイル: format_ogg.c プロジェクト: TeddyRilliot/icecast
static int write_ogg_data (struct source_tag *source, refbuf_t *refbuf)
{
    int ret = 1;

    if (fwrite (refbuf->data, 1, refbuf->len, source->dumpfile) != refbuf->len)
    {
        ICECAST_LOG_WARN("Write to dump file failed, disabling");
        fclose (source->dumpfile);
        source->dumpfile = NULL;
        ret = 0;
    }
    return ret;
}
コード例 #9
0
ファイル: auth_url.c プロジェクト: TeddyRilliot/icecast
static void url_stream_end (auth_client *auth_user)
{
    char *mount, *server;
    ice_config_t *config = config_get_config ();
    mount_proxy *mountinfo = config_find_mount (config, auth_user->mount, MOUNT_TYPE_NORMAL);
    auth_t *auth = mountinfo->auth;
    auth_url *url = auth->state;
    char *stream_end_url;
    int port;
    char post [4096];

    if (url->stream_end == NULL)
    {
        config_release_config ();
        return;
    }
    server = util_url_escape (config->hostname);
    port = config->port;
    stream_end_url = strdup (url->stream_end);
    /* we don't want this auth disappearing from under us while
     * the connection is in progress */
    mountinfo->auth->refcount++;
    config_release_config ();
    mount = util_url_escape (auth_user->mount);

    snprintf (post, sizeof (post),
              "action=mount_remove&mount=%s&server=%s&port=%d", mount, server, port);
    free (server);
    free (mount);

    if (strchr (url->stream_end, '@') == NULL)
    {
        if (url->userpwd)
            curl_easy_setopt (url->handle, CURLOPT_USERPWD, url->userpwd);
        else
            curl_easy_setopt (url->handle, CURLOPT_USERPWD, "");
    }
    else
        curl_easy_setopt (url->handle, CURLOPT_USERPWD, "");
    curl_easy_setopt (url->handle, CURLOPT_URL, url->stream_end);
    curl_easy_setopt (url->handle, CURLOPT_POSTFIELDS, post);
    curl_easy_setopt (url->handle, CURLOPT_WRITEHEADER, auth_user);

    if (curl_easy_perform (url->handle))
        ICECAST_LOG_WARN("auth to server %s failed with %s", stream_end_url, url->errormsg);

    auth_release (auth);
    free (stream_end_url);
    return;
}
コード例 #10
0
ファイル: auth.c プロジェクト: icqparty/radio_server
/* Called from auth thread to process any request for source client
 * authentication. Only applies to source clients, not relays.
 */
static void stream_auth_callback (auth_t *auth, auth_client *auth_user)
{
    client_t *client = auth_user->client;

    if (auth->stream_auth)
        auth->stream_auth (auth_user);

    auth_release (auth);
    client->auth = NULL;
    if (client->authenticated)
        auth_postprocess_source (auth_user);
    else
        ICECAST_LOG_WARN("Failed auth for source \"%s\"", auth_user->mount);
}
コード例 #11
0
/* helper to apply specialised changes to a stats node */
static void modify_node_event(stats_node_t *node, stats_event_t *event)
{
    char *str;

    if (event->action == STATS_EVENT_HIDDEN)
    {
        if (event->value)
            node->hidden = 1;
        else
            node->hidden = 0;
        return;
    }
    if (event->action != STATS_EVENT_SET)
    {
        int64_t value = 0;

        switch (event->action)
        {
        case STATS_EVENT_INC:
            value = atoi (node->value)+1;
            break;
        case STATS_EVENT_DEC:
            value = atoi (node->value)-1;
            break;
        case STATS_EVENT_ADD:
            value = atoi (node->value)+atoi (event->value);
            break;
        case STATS_EVENT_SUB:
            value = atoll (node->value) - atoll (event->value);
            break;
        default:
            ICECAST_LOG_WARN("unhandled event (%d) for %s", event->action, event->source);
            break;
        }
        str = malloc (16);
        snprintf (str, 16, "%" PRId64, value);
        if (event->value == NULL)
            event->value = strdup (str);
    }
    else
        str = (char *)strdup (event->value);
    free (node->value);
    node->value = str;
    if (event->source)
        ICECAST_LOG_DEBUG("update \"%s\" %s (%s)", event->source, node->name, node->value);
    else
        ICECAST_LOG_DEBUG("update global %s (%s)", node->name, node->value);
}
コード例 #12
0
ファイル: auth.c プロジェクト: icqparty/radio_server
static auth_client *auth_client_setup (const char *mount, client_t *client)
{
    /* This will look something like "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==" */
    const char *header = httpp_getvar(client->parser, "authorization");
    char *userpass, *tmp;
    char *username, *password;
    auth_client *auth_user;

    do
    {
        if (header == NULL)
            break;

        if (strncmp(header, "Basic ", 6) == 0)
        {
            userpass = util_base64_decode (header+6);
            if (userpass == NULL)
            {
                ICECAST_LOG_WARN("Base64 decode of Authorization header \"%s\" failed",
                        header+6);
                break;
            }

            tmp = strchr(userpass, ':');
            if (tmp == NULL)
            { 
                free (userpass);
                break;
            }

            *tmp = 0;
            username = userpass;
            password = tmp+1;
            client->username = strdup (username);
            client->password = strdup (password);
            free (userpass);
            break;
        }
        ICECAST_LOG_INFO("unhandled authorization header: %s", header);

    } while (0);

    auth_user = calloc (1, sizeof(auth_client));
    auth_user->mount = strdup (mount);
    auth_user->client = client;
    return auth_user;
}
コード例 #13
0
ファイル: xslt.c プロジェクト: TeddyRilliot/icecast
static xsltStylesheetPtr xslt_get_stylesheet(const char *fn) {
    int i;
    int empty = -1;
    struct stat file;

    if(stat(fn, &file)) {
        ICECAST_LOG_WARN("Error checking for stylesheet file \"%s\": %s", fn, 
                strerror(errno));
        return NULL;
    }

    for(i=0; i < CACHESIZE; i++) {
        if(cache[i].filename)
        {
#ifdef _WIN32
            if(!stricmp(fn, cache[i].filename))
#else
            if(!strcmp(fn, cache[i].filename))
#endif
            {
                if(file.st_mtime > cache[i].last_modified)
                {
                    xsltFreeStylesheet(cache[i].stylesheet);

                    cache[i].last_modified = file.st_mtime;
                    cache[i].stylesheet = xsltParseStylesheetFile (XMLSTR(fn));
                    cache[i].cache_age = time(NULL);
                }
                ICECAST_LOG_DEBUG("Using cached sheet %i", i);
                return cache[i].stylesheet;
            }
        }
        else
            empty = i;
    }

    if(empty>=0)
        i = empty;
    else
        i = evict_cache_entry();

    cache[i].last_modified = file.st_mtime;
    cache[i].filename = strdup(fn);
    cache[i].stylesheet = xsltParseStylesheetFile (XMLSTR(fn));
    cache[i].cache_age = time(NULL);
    return cache[i].stylesheet;
}
コード例 #14
0
/* printf style formatting for stat create/update */
void stats_event_args(const char *source, char *name, char *format, ...)
{
    char buf[1024];
    va_list val;
    int ret;

    if (name == NULL)
        return;
    va_start(val, format);
    ret = vsnprintf(buf, sizeof(buf), format, val);
    va_end(val);

    if (ret < 0 || (unsigned int)ret >= sizeof (buf))
    {
        ICECAST_LOG_WARN("problem with formatting %s stat %s",
                         source==NULL ? "global" : source, name);
        return;
    }
    stats_event(source, name, buf);
}
コード例 #15
0
ファイル: auth.c プロジェクト: icqparty/radio_server
static int get_authenticator (auth_t *auth, config_options_t *options)
{
    if (auth->type == NULL)
    {
        ICECAST_LOG_WARN("no authentication type defined");
        return -1;
    }
    do
    {
        ICECAST_LOG_DEBUG("type is %s", auth->type);

        if (strcmp (auth->type, "url") == 0)
        {
#ifdef HAVE_AUTH_URL
            if (auth_get_url_auth (auth, options) < 0)
                return -1;
            break;
#else
            ICECAST_LOG_ERROR("Auth URL disabled");
            return -1;
#endif
        }
        if (strcmp (auth->type, "htpasswd") == 0)
        {
            if (auth_get_htpasswd_auth (auth, options) < 0)
                return -1;
            break;
        }

        ICECAST_LOG_ERROR("Unrecognised authenticator type: \"%s\"", auth->type);
        return -1;
    } while (0);

    while (options)
    {
        if (strcmp (options->name, "allow_duplicate_users") == 0)
            auth->allow_duplicate_users = atoi ((char*)options->value);
        options = options->next;
    }
    return 0;
}
コード例 #16
0
ファイル: auth.c プロジェクト: icqparty/radio_server
/* Add a listener. Check for any mount information that states any
 * authentication to be used.
 */
void auth_add_listener (const char *mount, client_t *client)
{
    mount_proxy *mountinfo; 
    ice_config_t *config = config_get_config();

    mountinfo = config_find_mount (config, mount, MOUNT_TYPE_NORMAL);
    if (mountinfo && mountinfo->no_mount)
    {
        config_release_config ();
        client_send_403 (client, "mountpoint unavailable");
        return;
    }
    if (mountinfo && mountinfo->auth)
    {
        auth_client *auth_user;

        if (mountinfo->auth->pending_count > 100)
        {
            config_release_config ();
            ICECAST_LOG_WARN("too many clients awaiting authentication");
            client_send_403 (client, "busy, please try again later");
            return;
        }
        auth_user = auth_client_setup (mount, client);
        auth_user->process = auth_new_listener;
        ICECAST_LOG_INFO("adding client for authentication");
        queue_auth_client (auth_user, mountinfo);
        config_release_config ();
    }
    else
    {
        int ret = add_authenticated_listener (mount, mountinfo, client);
        config_release_config ();
        if (ret < 0)
            client_send_403 (client, "max listeners reached");
    }
}
コード例 #17
0
ファイル: auth_url.c プロジェクト: TeddyRilliot/icecast
static auth_result url_add_listener (auth_client *auth_user)
{
    client_t *client = auth_user->client;
    auth_t *auth = client->auth;
    auth_url *url = auth->state;
    int res = 0, port;
    const char *agent;
    char *user_agent, *username, *password;
    const char *mountreq;
    char *mount, *ipaddr, *server;
    ice_config_t *config;
    char *userpwd = NULL, post [4096];
    ssize_t post_offset;
    char *pass_headers, *cur_header, *next_header;
    const char *header_val;
    char *header_valesc;

    if (url->addurl == NULL)
        return AUTH_OK;

    config = config_get_config ();
    server = util_url_escape (config->hostname);
    port = config->port;
    config_release_config ();

    agent = httpp_getvar (client->parser, "user-agent");
    if (agent)
        user_agent = util_url_escape (agent);
    else
        user_agent = strdup ("-");

    if (client->username)
        username = util_url_escape (client->username);
    else
        username = strdup ("");

    if (client->password)
        password = util_url_escape (client->password);
    else
        password = strdup ("");

    /* get the full uri (with query params if available) */
    mountreq = httpp_getvar (client->parser, HTTPP_VAR_RAWURI);
    if (mountreq == NULL)
        mountreq = httpp_getvar (client->parser, HTTPP_VAR_URI);
    mount = util_url_escape (mountreq);
    ipaddr = util_url_escape (client->con->ip);

    post_offset = snprintf (post, sizeof (post),
                            "action=listener_add&server=%s&port=%d&client=%lu&mount=%s"
                            "&user=%s&pass=%s&ip=%s&agent=%s",
                            server, port, client->con->id, mount, username,
                            password, ipaddr, user_agent);
    free (server);
    free (mount);
    free (user_agent);
    free (username);
    free (password);
    free (ipaddr);

    pass_headers = NULL;
    if (url->pass_headers)
        pass_headers = strdup (url->pass_headers);
    if (pass_headers)
    {
        cur_header = pass_headers;
        while (cur_header)
        {
            next_header = strstr (cur_header, ",");
            if (next_header)
            {
                *next_header=0;
                next_header++;
            }

            header_val = httpp_getvar (client->parser, cur_header);
            if (header_val)
            {
                header_valesc = util_url_escape (header_val);
                post_offset += snprintf (post+post_offset, sizeof (post)-post_offset, "&%s%s=%s",
                                         url->prefix_headers ? url->prefix_headers : "",
                                         cur_header, header_valesc);
                free (header_valesc);
            }

            cur_header = next_header;
        }
    }

    if (strchr (url->addurl, '@') == NULL)
    {
        if (url->userpwd)
            curl_easy_setopt (url->handle, CURLOPT_USERPWD, url->userpwd);
        else
        {
            /* auth'd requests may not have a user/pass, but may use query args */
            if (client->username && client->password)
            {
                size_t len = strlen (client->username) + strlen (client->password) + 2;
                userpwd = malloc (len);
                snprintf (userpwd, len, "%s:%s", client->username, client->password);
                curl_easy_setopt (url->handle, CURLOPT_USERPWD, userpwd);
            }
            else
                curl_easy_setopt (url->handle, CURLOPT_USERPWD, "");
        }
    }
    else
    {
        /* url has user/pass but libcurl may need to clear any existing settings */
        curl_easy_setopt (url->handle, CURLOPT_USERPWD, "");
    }
    curl_easy_setopt (url->handle, CURLOPT_URL, url->addurl);
    curl_easy_setopt (url->handle, CURLOPT_POSTFIELDS, post);
    curl_easy_setopt (url->handle, CURLOPT_WRITEHEADER, auth_user);
    url->errormsg[0] = '\0';

    res = curl_easy_perform (url->handle);

    free (userpwd);

    if (res)
    {
        ICECAST_LOG_WARN("auth to server %s failed with %s", url->addurl, url->errormsg);
        return AUTH_FAILED;
    }
    /* we received a response, lets see what it is */
    if (client->authenticated)
        return AUTH_OK;
    ICECAST_LOG_INFO("client auth (%s) failed with \"%s\"", url->addurl, url->errormsg);
    return AUTH_FAILED;
}
コード例 #18
0
ファイル: auth_url.c プロジェクト: TeddyRilliot/icecast
static auth_result url_remove_listener (auth_client *auth_user)
{
    client_t *client = auth_user->client;
    auth_t *auth = client->auth;
    auth_url *url = auth->state;
    time_t duration = time(NULL) - client->con->con_time;
    char *username, *password, *mount, *server;
    const char *mountreq;
    ice_config_t *config;
    int port;
    char *userpwd = NULL, post [4096];
    const char *agent;
    char *user_agent, *ipaddr;

    if (url->removeurl == NULL)
        return AUTH_OK;

    config = config_get_config ();
    server = util_url_escape (config->hostname);
    port = config->port;
    config_release_config ();

    agent = httpp_getvar (client->parser, "user-agent");
    if (agent)
        user_agent = util_url_escape (agent);
    else
        user_agent = strdup ("-");

    if (client->username)
        username = util_url_escape (client->username);
    else
        username = strdup ("");

    if (client->password)
        password = util_url_escape (client->password);
    else
        password = strdup ("");

    /* get the full uri (with query params if available) */
    mountreq = httpp_getvar (client->parser, HTTPP_VAR_RAWURI);
    if (mountreq == NULL)
        mountreq = httpp_getvar (client->parser, HTTPP_VAR_URI);
    mount = util_url_escape (mountreq);
    ipaddr = util_url_escape (client->con->ip);

    snprintf (post, sizeof (post),
              "action=listener_remove&server=%s&port=%d&client=%lu&mount=%s"
              "&user=%s&pass=%s&duration=%lu&ip=%s&agent=%s",
              server, port, client->con->id, mount, username,
              password, (long unsigned)duration, ipaddr, user_agent);
    free (server);
    free (mount);
    free (username);
    free (password);
    free (ipaddr);
    free (user_agent);

    if (strchr (url->removeurl, '@') == NULL)
    {
        if (url->userpwd)
            curl_easy_setopt (url->handle, CURLOPT_USERPWD, url->userpwd);
        else
        {
            /* auth'd requests may not have a user/pass, but may use query args */
            if (client->username && client->password)
            {
                size_t len = strlen (client->username) + strlen (client->password) + 2;
                userpwd = malloc (len);
                snprintf (userpwd, len, "%s:%s", client->username, client->password);
                curl_easy_setopt (url->handle, CURLOPT_USERPWD, userpwd);
            }
            else
                curl_easy_setopt (url->handle, CURLOPT_USERPWD, "");
        }
    }
    else
    {
        /* url has user/pass but libcurl may need to clear any existing settings */
        curl_easy_setopt (url->handle, CURLOPT_USERPWD, "");
    }
    curl_easy_setopt (url->handle, CURLOPT_URL, url->removeurl);
    curl_easy_setopt (url->handle, CURLOPT_POSTFIELDS, post);
    curl_easy_setopt (url->handle, CURLOPT_WRITEHEADER, auth_user);

    if (curl_easy_perform (url->handle))
        ICECAST_LOG_WARN("auth to server %s failed with %s", url->removeurl, url->errormsg);

    free (userpwd);

    return AUTH_OK;
}
コード例 #19
0
ファイル: xslt.c プロジェクト: TeddyRilliot/icecast
void xslt_transform(xmlDocPtr doc, const char *xslfilename, client_t *client)
{
    xmlDocPtr    res;
    xsltStylesheetPtr cur;
    xmlChar *string;
    int len, problem = 0;
    const char *mediatype = NULL;
    const char *charset = NULL;

    xmlSetGenericErrorFunc ("", log_parse_failure);
    xsltSetGenericErrorFunc ("", log_parse_failure);

    thread_mutex_lock(&xsltlock);
    cur = xslt_get_stylesheet(xslfilename);

    if (cur == NULL)
    {
        thread_mutex_unlock(&xsltlock);
        ICECAST_LOG_ERROR("problem reading stylesheet \"%s\"", xslfilename);
        client_send_404 (client, "Could not parse XSLT file");
        return;
    }

    res = xsltApplyStylesheet(cur, doc, NULL);

    if (xsltSaveResultToString (&string, &len, res, cur) < 0)
        problem = 1;

    /* lets find out the content type and character encoding to use */
    if (cur->encoding)
       charset = (char *)cur->encoding;

    if (cur->mediaType)
        mediatype = (char *)cur->mediaType;
    else
    {
        /* check method for the default, a missing method assumes xml */
        if (cur->method && xmlStrcmp (cur->method, XMLSTR("html")) == 0)
            mediatype = "text/html";
        else
            if (cur->method && xmlStrcmp (cur->method, XMLSTR("text")) == 0)
                mediatype = "text/plain";
            else
                mediatype = "text/xml";
    }
    if (problem == 0)
    {
        ssize_t ret;
        int failed = 0;
        refbuf_t *refbuf;
        size_t full_len = strlen (mediatype) + len + 1024;
        if (full_len < 4096)
            full_len = 4096;
        refbuf = refbuf_new (full_len);

        if (string == NULL)
            string = xmlCharStrdup ("");
        ret = util_http_build_header(refbuf->data, full_len, 0, 0, 200, NULL, mediatype, charset, NULL, NULL);
        if (ret == -1) {
            ICECAST_LOG_ERROR("Dropping client as we can not build response headers.");
            client_send_500(client, "Header generation failed.");
        } else {
            if ( full_len < (ret + len + 64) ) {
                void *new_data;
                full_len = ret + len + 64;
                new_data = realloc(refbuf->data, full_len);
                if (new_data) {
                    ICECAST_LOG_DEBUG("Client buffer reallocation succeeded.");
                    refbuf->data = new_data;
                    refbuf->len = full_len;
                    ret = util_http_build_header(refbuf->data, full_len, 0, 0, 200, NULL, mediatype, charset, NULL, NULL);
                    if (ret == -1) {
                        ICECAST_LOG_ERROR("Dropping client as we can not build response headers.");
                        client_send_500(client, "Header generation failed.");
                        failed = 1;
                    }
                } else {
                    ICECAST_LOG_ERROR("Client buffer reallocation failed. Dropping client.");
                    client_send_500(client, "Buffer reallocation failed.");
                    failed = 1;
                }
            }

            if (!failed) {
                  snprintf(refbuf->data + ret, full_len - ret, "Content-Length: %d\r\n\r\n%s", len, string);

                client->respcode = 200;
                client_set_queue (client, NULL);
                client->refbuf = refbuf;
                refbuf->len = strlen (refbuf->data);
                fserve_add_client (client, NULL);
            }
        }
        xmlFree (string);
    }
    else
    {
        ICECAST_LOG_WARN("problem applying stylesheet \"%s\"", xslfilename);
        client_send_404 (client, "XSLT problem");
    }
    thread_mutex_unlock (&xsltlock);
    xmlFreeDoc(res);
}
コード例 #20
0
ファイル: format_ebml.c プロジェクト: Boltsie/Icecast-Server
static void ebml_write_buf_to_file_fail (source_t *source)
{
    ICECAST_LOG_WARN("Write to dump file failed, disabling");
    fclose (source->dumpfile);
    source->dumpfile = NULL;
}
コード例 #21
0
ファイル: auth.c プロジェクト: icqparty/radio_server
auth_t *auth_get_authenticator (xmlNodePtr node)
{
    auth_t *auth = calloc (1, sizeof (auth_t));
    config_options_t *options = NULL, **next_option = &options;
    xmlNodePtr option;

    if (auth == NULL)
        return NULL;

    option = node->xmlChildrenNode;
    while (option)
    {
        xmlNodePtr current = option;
        option = option->next;
        if (xmlStrcmp (current->name, XMLSTR("option")) == 0)
        {
            config_options_t *opt = calloc (1, sizeof (config_options_t));
            opt->name = (char *)xmlGetProp (current, XMLSTR("name"));
            if (opt->name == NULL)
            {
                free(opt);
                continue;
            }
            opt->value = (char *)xmlGetProp (current, XMLSTR("value"));
            if (opt->value == NULL)
            {
                xmlFree (opt->name);
                free (opt);
                continue;
            }
            *next_option = opt;
            next_option = &opt->next;
        }
        else
            if (xmlStrcmp (current->name, XMLSTR("text")) != 0)
                ICECAST_LOG_WARN("unknown auth setting (%s)", current->name);
    }
    auth->type = (char*)xmlGetProp (node, XMLSTR("type"));
    if (get_authenticator (auth, options) < 0)
    {
        xmlFree (auth->type);
        free (auth);
        auth = NULL;
    }
    else
    {
        auth->tailp = &auth->head;
        thread_mutex_create (&auth->lock);
        auth->refcount = 1;
        auth->running = 1;
        auth->thread = thread_create ("auth thread", auth_run_thread, auth, THREAD_ATTACHED);
    }

    while (options)
    {
        config_options_t *opt = options;
        options = opt->next;
        xmlFree (opt->name);
        xmlFree (opt->value);
        free (opt);
    }
    return auth;
}
コード例 #22
0
ファイル: fserve.c プロジェクト: xiph/Icecast-Server
/* client has requested a file, so check for it and send the file.  Do not
 * refer to the client_t afterwards.  return 0 for success, -1 on error.
 */
int fserve_client_create (client_t *httpclient)
{
    int bytes;
    struct stat file_buf;
    const char *range = NULL;
    off_t new_content_len = 0;
    off_t rangenumber = 0, content_length;
    int rangeproblem = 0;
    int ret = 0;
    char *fullpath;
    int m3u_requested = 0, m3u_file_available = 1;
    const char * xslt_playlist_requested = NULL;
    int xslt_playlist_file_available = 1;
    ice_config_t *config;
    FILE *file;

    fullpath = util_get_path_from_normalised_uri(httpclient->uri);
    ICECAST_LOG_INFO("checking for file %H (%H)", httpclient->uri, fullpath);

    if (strcmp (util_get_extension (fullpath), "m3u") == 0)
        m3u_requested = 1;

    if (strcmp (util_get_extension (fullpath), "xspf") == 0)
        xslt_playlist_requested = "xspf.xsl";

    if (strcmp (util_get_extension (fullpath), "vclt") == 0)
        xslt_playlist_requested = "vclt.xsl";

    /* check for the actual file */
    if (stat (fullpath, &file_buf) != 0)
    {
        /* the m3u can be generated, but send an m3u file if available */
        if (m3u_requested == 0 && xslt_playlist_requested == NULL)
        {
            ICECAST_LOG_WARN("req for file \"%H\" %s", fullpath, strerror (errno));
            client_send_error_by_id(httpclient, ICECAST_ERROR_FSERV_FILE_NOT_FOUND);
            free (fullpath);
            return -1;
        }
        m3u_file_available = 0;
        xslt_playlist_file_available = 0;
    }

    httpclient->refbuf->len = PER_CLIENT_REFBUF_SIZE;

    if (m3u_requested && m3u_file_available == 0)
    {
        char *sourceuri = strdup(httpclient->uri);
        char *dot = strrchr(sourceuri, '.');

        *dot = 0;
        httpclient->respcode = 200;
        ret = util_http_build_header (httpclient->refbuf->data, BUFSIZE, 0,
                                      0, 200, NULL,
                                      "audio/x-mpegurl", NULL, "", NULL, httpclient);
        if (ret == -1 || ret >= (BUFSIZE - 512)) { /* we want at least 512 bytes left for the content of the playlist */
            ICECAST_LOG_ERROR("Dropping client as we can not build response headers.");
            client_send_error_by_id(httpclient, ICECAST_ERROR_GEN_HEADER_GEN_FAILED);
            free(sourceuri);
            return -1;
        }
        client_get_baseurl(httpclient, NULL, httpclient->refbuf->data + ret, BUFSIZE - ret, NULL, NULL, NULL, sourceuri, "\r\n");
        httpclient->refbuf->len = strlen (httpclient->refbuf->data);
        fserve_add_client (httpclient, NULL);
        free (sourceuri);
        free (fullpath);
        return 0;
    }
    if (xslt_playlist_requested && xslt_playlist_file_available == 0)
    {
        xmlDocPtr doc;
        char *reference = strdup(httpclient->uri);
        char *eol = strrchr (reference, '.');
        if (eol)
            *eol = '\0';
        doc = stats_get_xml (0, reference, httpclient);
        free (reference);
        admin_send_response (doc, httpclient, ADMIN_FORMAT_HTML, xslt_playlist_requested);
        xmlFreeDoc(doc);
        free (fullpath);
        return 0;
    }

    /* on demand file serving check */
    config = config_get_config();
    if (config->fileserve == 0)
    {
        ICECAST_LOG_DEBUG("on demand file \"%H\" refused. Serving static files has been disabled in the config", fullpath);
        client_send_error_by_id(httpclient, ICECAST_ERROR_FSERV_FILE_NOT_FOUND);
        config_release_config();
        free(fullpath);
        return -1;
    }
    config_release_config();

    if (S_ISREG (file_buf.st_mode) == 0)
    {
        client_send_error_by_id(httpclient, ICECAST_ERROR_FSERV_FILE_NOT_FOUND);
        ICECAST_LOG_WARN("found requested file but there is no handler for it: %H", fullpath);
        free (fullpath);
        return -1;
    }

    file = fopen (fullpath, "rb");
    if (file == NULL)
    {
        ICECAST_LOG_WARN("Problem accessing file \"%H\"", fullpath);
        client_send_error_by_id(httpclient, ICECAST_ERROR_FSERV_FILE_NOT_READABLE);
        free (fullpath);
        return -1;
    }
    free (fullpath);

    content_length = file_buf.st_size;
    range = httpp_getvar (httpclient->parser, "range");

    /* full http range handling is currently not done but we deal with the common case */
    if (range != NULL) {
        ret = 0;
        if (strncasecmp (range, "bytes=", 6) == 0)
            ret = sscanf (range+6, "%" SCN_OFF_T "-", &rangenumber);

        if (ret != 1) {
            /* format not correct, so lets just assume
               we start from the beginning */
            rangeproblem = 1;
        }
        if (rangenumber < 0) {
            rangeproblem = 1;
        }
        if (!rangeproblem) {
            ret = fseeko (file, rangenumber, SEEK_SET);
            if (ret != -1) {
                new_content_len = content_length - rangenumber;
                if (new_content_len < 0) {
                    rangeproblem = 1;
                }
            }
            else {
                rangeproblem = 1;
            }
            if (!rangeproblem) {
                off_t endpos = rangenumber+new_content_len-1;
                char *type;

                if (endpos < 0) {
                    endpos = 0;
                }
                httpclient->respcode = 206;
                type = fserve_content_type(httpclient->uri);
                bytes = util_http_build_header (httpclient->refbuf->data, BUFSIZE, 0,
                                                0, 206, NULL,
                                                type, NULL,
                                                NULL, NULL, httpclient);
                if (bytes == -1 || bytes >= (BUFSIZE - 512)) { /* we want at least 512 bytes left */
                    ICECAST_LOG_ERROR("Dropping client as we can not build response headers.");
                    client_send_error_by_id(httpclient, ICECAST_ERROR_GEN_HEADER_GEN_FAILED);
                    return -1;
                }
                bytes += snprintf (httpclient->refbuf->data + bytes, BUFSIZE - bytes,
                    "Accept-Ranges: bytes\r\n"
                    "Content-Length: %" PRI_OFF_T "\r\n"
                    "Content-Range: bytes %" PRI_OFF_T \
                    "-%" PRI_OFF_T "/%" PRI_OFF_T "\r\n\r\n",
                    new_content_len,
                    rangenumber,
                    endpos,
                    content_length);
                free (type);
            }
            else {
                goto fail;
            }
        }
        else {
            goto fail;
        }
    }
    else {
        char *type = fserve_content_type(httpclient->uri);
        httpclient->respcode = 200;
        bytes = util_http_build_header (httpclient->refbuf->data, BUFSIZE, 0,
                                        0, 200, NULL,
                                        type, NULL,
                                        NULL, NULL, httpclient);
        if (bytes == -1 || bytes >= (BUFSIZE - 512)) { /* we want at least 512 bytes left */
            ICECAST_LOG_ERROR("Dropping client as we can not build response headers.");
            client_send_error_by_id(httpclient, ICECAST_ERROR_GEN_HEADER_GEN_FAILED);
            fclose(file);
            return -1;
        }
        bytes += snprintf (httpclient->refbuf->data + bytes, BUFSIZE - bytes,
            "Accept-Ranges: bytes\r\n"
            "Content-Length: %" PRI_OFF_T "\r\n\r\n",
            content_length);
        free (type);
    }
    httpclient->refbuf->len = bytes;
    httpclient->pos = 0;

    stats_event_inc (NULL, "file_connections");
    fserve_add_client (httpclient, file);

    return 0;

fail:
    fclose (file);
    client_send_error_by_id(httpclient, ICECAST_ERROR_FSERV_REQUEST_RANGE_NOT_SATISFIABLE);
    return -1;
}
コード例 #23
0
ファイル: slave.c プロジェクト: AkiCorp/icecast-ac
/* wrapper for starting the provided relay stream */
static void check_relay_stream (relay_server *relay)
{
    if (relay->source == NULL)
    {
        if (relay->localmount[0] != '/')
        {
            ICECAST_LOG_WARN("relay mountpoint \"%s\" does not start with /, skipping",
                    relay->localmount);
            return;
        }
        /* new relay, reserve the name */
        relay->source = source_reserve (relay->localmount);
        if (relay->source)
        {
            ICECAST_LOG_DEBUG("Adding relay source at mountpoint \"%s\"", relay->localmount);
            if (relay->on_demand)
            {
                ice_config_t *config = config_get_config ();
                mount_proxy *mountinfo = config_find_mount (config, relay->localmount, MOUNT_TYPE_NORMAL);
                relay->source->on_demand = relay->on_demand;
                if (mountinfo == NULL)
                    source_update_settings (config, relay->source, mountinfo);
                config_release_config ();
                stats_event (relay->localmount, "listeners", "0");
                slave_update_all_mounts();
            }
        }
        else
        {
            if (relay->start == 0)
            {
                ICECAST_LOG_WARN("new relay but source \"%s\" already exists", relay->localmount);
                relay->start = 1;
            }
            return;
        }
    }
    do
    {
        source_t *source = relay->source;
        /* skip relay if active, not configured or just not time yet */
        if (relay->source == NULL || relay->running || relay->start > time(NULL))
            break;
        /* check if an inactive on-demand relay has a fallback that has listeners */
        if (relay->on_demand && source->on_demand_req == 0)
        {
            relay->source->on_demand = relay->on_demand;

            if (source->fallback_mount && source->fallback_override)
            {
                source_t *fallback;
                avl_tree_rlock (global.source_tree);
                fallback = source_find_mount (source->fallback_mount);
                if (fallback && fallback->running && fallback->listeners)
                {
                   ICECAST_LOG_DEBUG("fallback running %d with %lu listeners", fallback->running, fallback->listeners);
                   source->on_demand_req = 1;
                }
                avl_tree_unlock (global.source_tree);
            }
            if (source->on_demand_req == 0)
                break;
        }

        relay->start = time(NULL) + 5;
        relay->running = 1;
        relay->thread = thread_create ("Relay Thread", start_relay_stream,
                relay, THREAD_ATTACHED);
        return;

    } while (0);
    /* the relay thread may of shut down itself */
    if (relay->cleanup)
    {
        if (relay->thread)
        {
            ICECAST_LOG_DEBUG("waiting for relay thread for \"%s\"", relay->localmount);
            thread_join (relay->thread);
            relay->thread = NULL;
        }
        relay->cleanup = 0;
        relay->running = 0;

        if (relay->on_demand && relay->source)
        {
            ice_config_t *config = config_get_config ();
            mount_proxy *mountinfo = config_find_mount (config, relay->localmount, MOUNT_TYPE_NORMAL);
            source_update_settings (config, relay->source, mountinfo);
            config_release_config ();
            stats_event (relay->localmount, "listeners", "0");
        }
    }
}
コード例 #24
0
ファイル: slave.c プロジェクト: AkiCorp/icecast-ac
/* Actually open the connection and do some http parsing, handle any 302
 * responses within here.
 */
static client_t *open_relay_connection (relay_server *relay)
{
    int redirects = 0;
    char *server_id = NULL;
    ice_config_t *config;
    http_parser_t *parser = NULL;
    connection_t *con=NULL;
    char *server = strdup (relay->server);
    char *mount = strdup (relay->mount);
    int port = relay->port;
    char *auth_header;
    char header[4096];

    config = config_get_config ();
    server_id = strdup (config->server_id);
    config_release_config ();

    /* build any authentication header before connecting */
    if (relay->username && relay->password)
    {
        char *esc_authorisation;
        unsigned len = strlen(relay->username) + strlen(relay->password) + 2;

        auth_header = malloc (len);
        snprintf (auth_header, len, "%s:%s", relay->username, relay->password);
        esc_authorisation = util_base64_encode(auth_header, len);
        free(auth_header);
        len = strlen (esc_authorisation) + 24;
        auth_header = malloc (len);
        snprintf (auth_header, len,
                "Authorization: Basic %s\r\n", esc_authorisation);
        free(esc_authorisation);
    }
    else
        auth_header = strdup ("");

    while (redirects < 10)
    {
        sock_t streamsock;

        ICECAST_LOG_INFO("connecting to %s:%d", server, port);

        streamsock = sock_connect_wto_bind (server, port, relay->bind, 10);
        if (streamsock == SOCK_ERROR)
        {
            ICECAST_LOG_WARN("Failed to connect to %s:%d", server, port);
            break;
        }
        con = connection_create (streamsock, -1, strdup (server));

        /* At this point we may not know if we are relaying an mp3 or vorbis
         * stream, but only send the icy-metadata header if the relay details
         * state so (the typical case).  It's harmless in the vorbis case. If
         * we don't send in this header then relay will not have mp3 metadata.
         */
        sock_write(streamsock, "GET %s HTTP/1.0\r\n"
                "User-Agent: %s\r\n"
                "Host: %s\r\n"
                "%s"
                "%s"
                "\r\n",
                mount,
                server_id,
                server,
                relay->mp3metadata?"Icy-MetaData: 1\r\n":"",
                auth_header);
        memset (header, 0, sizeof(header));
        if (util_read_header (con->sock, header, 4096, READ_ENTIRE_HEADER) == 0)
        {
            ICECAST_LOG_ERROR("Header read failed for %s (%s:%d%s)", relay->localmount, server, port, mount);
            break;
        }
        parser = httpp_create_parser();
        httpp_initialize (parser, NULL);
        if (! httpp_parse_response (parser, header, strlen(header), relay->localmount))
        {
            ICECAST_LOG_ERROR("Error parsing relay request for %s (%s:%d%s)", relay->localmount,
                    server, port, mount);
            break;
        }
        if (strcmp (httpp_getvar (parser, HTTPP_VAR_ERROR_CODE), "302") == 0)
        {
            /* better retry the connection again but with different details */
            const char *uri, *mountpoint;
            int len;

            uri = httpp_getvar (parser, "location");
            ICECAST_LOG_INFO("redirect received %s", uri);
            if (strncmp (uri, "http://", 7) != 0)
                break;
            uri += 7;
            mountpoint = strchr (uri, '/');
            free (mount);
            if (mountpoint)
                mount = strdup (mountpoint);
            else
                mount = strdup ("/");

            len = strcspn (uri, ":/");
            port = 80;
            if (uri [len] == ':')
                port = atoi (uri+len+1);
            free (server);
            server = calloc (1, len+1);
            strncpy (server, uri, len);
            connection_close (con);
            httpp_destroy (parser);
            con = NULL;
            parser = NULL;
        }
        else
        {
            client_t *client = NULL;

            if (httpp_getvar (parser, HTTPP_VAR_ERROR_MESSAGE))
            {
                ICECAST_LOG_ERROR("Error from relay request: %s (%s)", relay->localmount,
                        httpp_getvar(parser, HTTPP_VAR_ERROR_MESSAGE));
                break;
            }
            global_lock ();
            if (client_create (&client, con, parser) < 0)
            {
                global_unlock ();
                /* make sure only the client_destory frees these */
                con = NULL;
                parser = NULL;
                client_destroy (client);
                break;
            }
            global_unlock ();
            sock_set_blocking (streamsock, 0);
            client_set_queue (client, NULL);
            free (server);
            free (mount);
            free (server_id);
            free (auth_header);

            return client;
        }
        redirects++;
    }
    /* failed, better clean up */
    free (server);
    free (mount);
    free (server_id);
    free (auth_header);
    if (con)
        connection_close (con);
    if (parser)
        httpp_destroy (parser);
    return NULL;
}
コード例 #25
0
ファイル: fserve.c プロジェクト: xiph/Icecast-Server
void fserve_recheck_mime_types(ice_config_t *config)
{
    FILE *mimefile;
    char line[4096];
    char *type, *ext, *cur;
    mime_type *mapping;
    avl_tree *new_mimetypes;

    if (config->mimetypes_fn == NULL)
        return;
    mimefile = fopen (config->mimetypes_fn, "r");
    if (mimefile == NULL)
    {
        ICECAST_LOG_WARN("Cannot open mime types file %s", config->mimetypes_fn);
        return;
    }

    new_mimetypes = avl_tree_new(_compare_mappings, NULL);

    while(fgets(line, 4096, mimefile))
    {
        line[4095] = 0;

        if(*line == 0 || *line == '#')
            continue;

        type = line;

        cur = line;

        while(*cur != ' ' && *cur != '\t' && *cur)
            cur++;
        if(*cur == 0)
            continue;

        *cur++ = 0;

        while(1) {
            while(*cur == ' ' || *cur == '\t')
                cur++;
            if(*cur == 0)
                break;

            ext = cur;
            while(*cur != ' ' && *cur != '\t' && *cur != '\n' && *cur)
                cur++;
            *cur++ = 0;
            if(*ext)
            {
                void *tmp;
                /* Add a new extension->type mapping */
                mapping = malloc(sizeof(mime_type));
                mapping->ext = strdup(ext);
                mapping->type = strdup(type);
                if (!avl_get_by_key (new_mimetypes, mapping, &tmp))
                    avl_delete (new_mimetypes, mapping, _delete_mapping);
                avl_insert (new_mimetypes, mapping);
            }
        }
    }
    fclose(mimefile);

    thread_spin_lock (&pending_lock);
    if (mimetypes)
        avl_tree_free (mimetypes, _delete_mapping);
    mimetypes = new_mimetypes;
    thread_spin_unlock (&pending_lock);
}
コード例 #26
0
ファイル: format_mp3.c プロジェクト: TeddyRilliot/icecast
/* called from the source thread when the metadata has been updated.
 * The artist title are checked and made ready for clients to send
 */
static void mp3_set_title (source_t *source)
{
    const char streamtitle[] = "StreamTitle='";
    const char streamurl[] = "StreamUrl='";
    size_t size;
    unsigned char len_byte;
    refbuf_t *p;
    unsigned int len = sizeof(streamtitle) + 2; /* the StreamTitle, quotes, ; and null */
    mp3_state *source_mp3 = source->format->_state;

    /* make sure the url data does not disappear from under us */
    thread_mutex_lock (&source_mp3->url_lock);

    /* work out message length */
    if (source_mp3->url_artist)
        len += strlen (source_mp3->url_artist);
    if (source_mp3->url_title)
        len += strlen (source_mp3->url_title);
    if (source_mp3->url_artist && source_mp3->url_title)
        len += 3;
    if (source_mp3->inline_url)
    {
        char *end = strstr (source_mp3->inline_url, "';");
        if (end)
            len += end - source_mp3->inline_url+2;
    }
    else if (source_mp3->url)
        len += strlen (source_mp3->url) + strlen (streamurl) + 2;
#define MAX_META_LEN 255*16
    if (len > MAX_META_LEN)
    {
        thread_mutex_unlock (&source_mp3->url_lock);
        ICECAST_LOG_WARN("Metadata too long at %d chars", len);
        return;
    }
    /* work out the metadata len byte */
    len_byte = (len-1) / 16 + 1;

    /* now we know how much space to allocate, +1 for the len byte */
    size = len_byte * 16 + 1;

    p = refbuf_new (size);
    if (p)
    {
        mp3_state *source_mp3 = source->format->_state;
        int r;

        memset (p->data, '\0', size);
        if (source_mp3->url_artist && source_mp3->url_title)
            r = snprintf (p->data, size, "%c%s%s - %s';", len_byte, streamtitle,
                    source_mp3->url_artist, source_mp3->url_title);
        else
            r = snprintf (p->data, size, "%c%s%s';", len_byte, streamtitle,
                    source_mp3->url_title);
        if (r > 0)
        {
            if (source_mp3->inline_url)
            {
                char *end = strstr (source_mp3->inline_url, "';");
                ssize_t urllen = size;
                if (end) urllen = end - source_mp3->inline_url + 2;
                if ((ssize_t)(size-r) > urllen)
                    snprintf (p->data+r, size-r, "StreamUrl='%s';", source_mp3->inline_url+11);
            }
            else if (source_mp3->url)
                snprintf (p->data+r, size-r, "StreamUrl='%s';", source_mp3->url);
        }
        ICECAST_LOG_DEBUG("shoutcast metadata block setup with %s", p->data+1);
        filter_shoutcast_metadata (source, p->data, size);

        refbuf_release (source_mp3->metadata);
        source_mp3->metadata = p;
    }
    thread_mutex_unlock (&source_mp3->url_lock);
}