Пример #1
0
/* call this to verify that the HTTP data has been sent and if so setup
 * callbacks to the appropriate format functions
 */
int format_check_http_buffer(source_t *source, client_t *client)
{
    refbuf_t *refbuf = client->refbuf;

    if (refbuf == NULL)
        return -1;

    if (client->respcode == 0)
    {
        ICECAST_LOG_DEBUG("processing pending client headers");

        if (format_prepare_headers (source, client) < 0)
        {
            ICECAST_LOG_ERROR("internal problem, dropping client");
            client->con->error = 1;
            return -1;
        }
        client->respcode = 200;
        stats_event_inc(NULL, "listeners");
        stats_event_inc(NULL, "listener_connections");
        stats_event_inc(source->mount, "listener_connections");
    }

    if (client->pos == refbuf->len)
    {
        client->write_to_client = source->format->write_buf_to_client;
        client->check_buffer = format_check_file_buffer;
        client->intro_offset = 0;
        client->pos = refbuf->len = 4096;
        return -1;
    }
    return 0;
}
Пример #2
0
void event_config_read(void *arg)
{
    int ret;
    ice_config_t *config;
    ice_config_t new_config;
    /* reread config file */

    config = config_grab_config(); /* Both to get the lock, and to be able
                                     to find out the config filename */
    xmlSetGenericErrorFunc ("config", log_parse_failure);
    ret = config_parse_file(config->config_filename, &new_config);
    if(ret < 0) {
        ICECAST_LOG_ERROR("Error parsing config, not replacing existing config");
        switch(ret) {
            case CONFIG_EINSANE:
                ICECAST_LOG_ERROR("Config filename null or blank");
                break;
            case CONFIG_ENOROOT:
                ICECAST_LOG_ERROR("Root element not found in %s", config->config_filename);
                break;
            case CONFIG_EBADROOT:
                ICECAST_LOG_ERROR("Not an icecast2 config file: %s",
                        config->config_filename);
                break;
            default:
                ICECAST_LOG_ERROR("Parse error in reading %s", config->config_filename);
                break;
        }
        config_release_config();
    }
    else {
        config_clear(config);
        config_set_config(&new_config);
        config = config_get_config_unlocked();
        restart_logging (config);
        yp_recheck_config (config);
        fserve_recheck_mime_types (config);
        stats_global (config);
        config_release_config();
        slave_update_all_mounts();
    }
}
Пример #3
0
static int ebml_wrote(ebml_t *ebml, int len)
{

    int b;

    if (ebml->header_size == 0)
    {
        if ((ebml->header_position + len) > EBML_HEADER_MAX_SIZE)
        {
            ICECAST_LOG_ERROR("EBML Header too large, failing");
            return -1;
        }

/*
        ICECAST_LOG_DEBUG("EBML: Adding to header, ofset is %d size is %d adding %d",
                          ebml->header_size, ebml->header_position, len);
*/

        memcpy(ebml->header + ebml->header_position, ebml->input_buffer, len);
        ebml->header_position += len;
    }
    else
    {
        memcpy(ebml->buffer + ebml->position, ebml->input_buffer, len);
    }

    for (b = 0; b < len - 4; b++)
    {
        if (!memcmp(ebml->input_buffer + b, ebml->cluster_id, 4))
        {
/*
            ICECAST_LOG_DEBUG("EBML: found cluster");
*/

            if (ebml->header_size == 0)
            {
                ebml->header_size = ebml->header_position - len + b;
                memcpy(ebml->buffer, ebml->input_buffer + b, len - b);
                ebml->position = len - b;
                ebml->cluster_start = -1;
                return len;
            }
            else
            {
                ebml->cluster_start = ebml->position + b;
            }
        }
    }

    ebml->position += len;

    return len;

}
Пример #4
0
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;
}
Пример #5
0
static refbuf_t *ebml_get_buffer(source_t *source)
{

    ebml_source_state_t *ebml_source_state = source->format->_state;
    format_plugin_t *format = source->format;
    char *data = NULL;
    int bytes = 0;
    refbuf_t *refbuf;
    int ret;

    while (1)
    {

        if ((bytes = ebml_read_space(ebml_source_state->ebml)) > 0)
        {
            refbuf = refbuf_new(bytes);
            ebml_read(ebml_source_state->ebml, refbuf->data, bytes);

            if (ebml_source_state->header == NULL)
            {
                ebml_source_state->header = refbuf;
                continue;
            }

            if (ebml_last_was_sync(ebml_source_state->ebml))
            {
                refbuf->sync_point = 1;
            }
            return refbuf;

        }
        else
        {

            data = ebml_write_buffer(ebml_source_state->ebml, EBML_SLICE_SIZE);
            bytes = client_read_bytes (source->client, data, EBML_SLICE_SIZE);
            if (bytes <= 0)
            {
                ebml_wrote (ebml_source_state->ebml, 0);
                return NULL;
            }
            format->read_bytes += bytes;
            ret = ebml_wrote (ebml_source_state->ebml, bytes);
            if (ret != bytes) {
                ICECAST_LOG_ERROR("Problem processing stream");
                source->running = 0;
                return NULL;
            }
        }
    }
}
Пример #6
0
void refbuf_release(refbuf_t *self)
{
    if (self == NULL)
        return;
    self->_count--;
    if (self->_count == 0)
    {
        refbuf_release_associated (self->associated);
        if (self->next)
            ICECAST_LOG_ERROR("next not null");
        free(self->data);
        free(self);
    }
}
Пример #7
0
/* if 0 is returned then the client should not be touched, however if -1
 * is returned then the caller is responsible for handling the client
 */
static int add_listener_to_source (source_t *source, client_t *client)
{
    int loop = 10;
    do
    {
        ICECAST_LOG_DEBUG("max on %s is %ld (cur %lu)", source->mount,
                source->max_listeners, source->listeners);
        if (source->max_listeners == -1)
            break;
        if (source->listeners < (unsigned long)source->max_listeners)
            break;

        if (loop && source->fallback_when_full && source->fallback_mount)
        {
            source_t *next = source_find_mount (source->fallback_mount);
            if (!next) {
                ICECAST_LOG_ERROR("Fallback '%s' for full source '%s' not found", 
                        source->mount, source->fallback_mount);
                return -1;
            }

            ICECAST_LOG_INFO("stream full trying %s", next->mount);
            source = next;
            loop--;
            continue;
        }
        /* now we fail the client */
        return -1;

    } while (1);

    client->write_to_client = format_generic_write_to_client;
    client->check_buffer = format_check_http_buffer;
    client->refbuf->len = PER_CLIENT_REFBUF_SIZE;
    memset (client->refbuf->data, 0, PER_CLIENT_REFBUF_SIZE);

    /* lets add the client to the active list */
    avl_tree_wlock (source->pending_tree);
    avl_insert (source->pending_tree, client);
    avl_tree_unlock (source->pending_tree);

    if (source->running == 0 && source->on_demand)
    {
        /* enable on-demand relay to start, wake up the slave thread */
        ICECAST_LOG_DEBUG("kicking off on-demand relay");
        source->on_demand_req = 1;
    }
    ICECAST_LOG_DEBUG("Added client to %s", source->mount);
    return 0;
}
Пример #8
0
/* The auth thread main loop. */
static void *auth_run_thread (void *arg)
{
    auth_t *auth = arg;

    ICECAST_LOG_INFO("Authentication thread started");
    while (auth->running)
    {
        /* usually no clients are waiting, so don't bother taking locks */
        if (auth->head)
        {
            auth_client *auth_user;

            /* may become NULL before lock taken */
            thread_mutex_lock (&auth->lock);
            auth_user = (auth_client*)auth->head;
            if (auth_user == NULL)
            {
                thread_mutex_unlock (&auth->lock);
                continue;
            }
            ICECAST_LOG_DEBUG("%d client(s) pending on %s", auth->pending_count, auth->mount);
            auth->head = auth_user->next;
            if (auth->head == NULL)
                auth->tailp = &auth->head;
            auth->pending_count--;
            thread_mutex_unlock (&auth->lock);
            auth_user->next = NULL;

            if (auth_user->process)
                auth_user->process (auth, auth_user);
            else
                ICECAST_LOG_ERROR("client auth process not set");

            auth_client_free (auth_user);

            continue;
        }
        thread_sleep (150000);
    }
    ICECAST_LOG_INFO("Authenication thread shutting down");
    return NULL;
}
Пример #9
0
int event_get_log(event_registration_t *er, config_options_t *options) {
    event_log_t *self = calloc(1, sizeof(event_log_t));

    if (!self)
        return -1;

    self->prefix = strdup("Event");
    self->level  = ICECAST_LOGLEVEL_INFO;

    if (options) {
        do {
            if (options->type)
                continue;
            if (!options->name)
                continue;
            /* BEFORE RELEASE 2.5.0 DOCUMENT: Document supported options:
             * <option name="prefix" value="..." />
             * <option name="level" value="..." />
             */
            if (strcmp(options->name, "prefix") == 0) {
                free(self->prefix);
                self->prefix = NULL;
                if (options->value)
                    self->prefix = strdup(options->value);
            } else if (strcmp(options->name, "level") == 0) {
                self->level = ICECAST_LOGLEVEL_INFO;
                if (options->value)
                    self->level = util_str_to_loglevel(options->value);
            } else {
                ICECAST_LOG_ERROR("Unknown <option> tag with name %s.", options->name);
            }
        } while ((options = options->next));
    }

    er->state = self;
    er->emit = event_log_emit;
    er->free = event_log_free;
    return 0;
}
Пример #10
0
static void client_send_error(client_t *client, int status, int plain, const char *message)
{
    ssize_t ret;

    ret = util_http_build_header(client->refbuf->data, PER_CLIENT_REFBUF_SIZE, 0,
                                 0, status, NULL,
                                 plain ? "text/plain" : "text/html", "utf-8",
                                 plain ? message : "", NULL);

    if (ret == -1 || ret >= PER_CLIENT_REFBUF_SIZE) {
        ICECAST_LOG_ERROR("Dropping client as we can not build response headers.");
        client_send_500(client, "Header generation failed.");
        return;
    }

    if (!plain)
        snprintf(client->refbuf->data + ret, PER_CLIENT_REFBUF_SIZE - ret,
                 "<html><head><title>Error %i</title></head><body><b>%i - %s</b></body></html>\r\n",
                 status, status, message);

    client->respcode = status;
    client->refbuf->len = strlen (client->refbuf->data);
    fserve_add_client (client, NULL);
}
Пример #11
0
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);
}
Пример #12
0
int auth_get_url_auth(auth_t *authenticator, config_options_t *options)
{
    auth_url    *url_info;
    const char  *addaction      = "listener_add";
    const char  *removeaction   = "listener_remove";

    authenticator->free         = auth_url_clear;
    authenticator->adduser      = auth_url_adduser;
    authenticator->deleteuser   = auth_url_deleteuser;
    authenticator->listuser     = auth_url_listuser;

    url_info                    = calloc(1, sizeof(auth_url));
    authenticator->state        = url_info;

    /* default headers */
    url_info->auth_header       = strdup("icecast-auth-user: 1\r\n");
    url_info->timelimit_header  = strdup("icecast-auth-timelimit:");

    /* force auth thread to call function. this makes sure the auth_t is attached to client */
    authenticator->authenticate_client = url_add_client;

    while(options) {
        if(strcmp(options->name, "username") == 0) {
            free(url_info->username);
            url_info->username = strdup(options->value);
        } else if(strcmp(options->name, "password") == 0) {
            free(url_info->password);
            url_info->password = strdup(options->value);
        } else if(strcmp(options->name, "headers") == 0) {
            free(url_info->pass_headers);
            url_info->pass_headers = strdup(options->value);
        } else if(strcmp(options->name, "header_prefix") == 0) {
            free(url_info->prefix_headers);
            url_info->prefix_headers = strdup(options->value);
        } else if(strcmp(options->name, "client_add") == 0) {
            free(url_info->addurl);
            url_info->addurl = strdup(options->value);
        } else if(strcmp(options->name, "client_remove") == 0) {
            authenticator->release_client = url_remove_client;
            free(url_info->removeurl);
            url_info->removeurl = strdup(options->value);
        } else if(strcmp(options->name, "action_add") == 0) {
            addaction = options->value;
        } else if(strcmp(options->name, "action_remove") == 0) {
            removeaction = options->value;
        } else if(strcmp(options->name, "auth_header") == 0) {
            free(url_info->auth_header);
            url_info->auth_header = strdup(options->value);
        } else if (strcmp(options->name, "timelimit_header") == 0) {
            free(url_info->timelimit_header);
            url_info->timelimit_header = strdup(options->value);
        } else {
            ICECAST_LOG_ERROR("Unknown option: %s", options->name);
        }
        options = options->next;
    }

    url_info->addaction = util_url_escape(addaction);
    url_info->removeaction = util_url_escape(removeaction);

    url_info->handle = icecast_curl_new(NULL, &url_info->errormsg[0]);
    if (url_info->handle == NULL) {
        auth_url_clear(authenticator);
        return -1;
    }

    if (url_info->auth_header)
        url_info->auth_header_len = strlen (url_info->auth_header);
    if (url_info->timelimit_header)
        url_info->timelimit_header_len = strlen (url_info->timelimit_header);

    curl_easy_setopt(url_info->handle, CURLOPT_HEADERFUNCTION, handle_returned_header);

    if (url_info->username && url_info->password) {
        int len = strlen(url_info->username) + strlen(url_info->password) + 2;
        url_info->userpwd = malloc(len);
        snprintf(url_info->userpwd, len, "%s:%s",
            url_info->username, url_info->password);
    }

    ICECAST_LOG_INFO("URL based authentication setup");
    return 0;
}
Пример #13
0
static int format_prepare_headers (source_t *source, client_t *client)
{
    unsigned remaining;
    char *ptr;
    int bytes;
    int bitrate_filtered = 0;
    avl_node *node;

    remaining = client->refbuf->len;
    ptr = client->refbuf->data;
    client->respcode = 200;

    bytes = util_http_build_header(ptr, remaining, 0, 0, 200, NULL, source->format->contenttype, NULL, NULL, source, client);
    if (bytes == -1) {
        ICECAST_LOG_ERROR("Dropping client as we can not build response headers.");
        client_send_error(client, 500, 0, "Header generation failed.");
        return -1;
    } else if ((bytes + 1024) >= remaining) { /* we don't know yet how much to follow but want at least 1kB free space */
        void *new_ptr = realloc(ptr, bytes + 1024);
        if (new_ptr) {
            ICECAST_LOG_DEBUG("Client buffer reallocation succeeded.");
            client->refbuf->data = ptr = new_ptr;
            client->refbuf->len = remaining = bytes + 1024;
            bytes = util_http_build_header(ptr, remaining, 0, 0, 200, NULL, source->format->contenttype, NULL, NULL, source, client);
            if (bytes == -1 ) {
                ICECAST_LOG_ERROR("Dropping client as we can not build response headers.");
                client_send_error(client, 500, 0, "Header generation failed.");
                return -1;
            }
        } else {
            ICECAST_LOG_ERROR("Client buffer reallocation failed. Dropping client.");
            client_send_error(client, 500, 0, "Buffer reallocation failed.");
            return -1;
        }
    }

    remaining -= bytes;
    ptr += bytes;

    /* iterate through source http headers and send to client */
    avl_tree_rlock(source->parser->vars);
    node = avl_get_first(source->parser->vars);
    while (node)
    {
        int next = 1;
        http_var_t *var = (http_var_t *) node->key;
        bytes = 0;
        if (!strcasecmp(var->name, "ice-audio-info"))
        {
            /* convert ice-audio-info to icy-br */
            char *brfield = NULL;
            unsigned int bitrate;

            if (bitrate_filtered == 0)
                brfield = strstr(var->value, "bitrate=");
            if (brfield && sscanf (brfield, "bitrate=%u", &bitrate))
            {
                bytes = snprintf (ptr, remaining, "icy-br:%u\r\n", bitrate);
                next = 0;
                bitrate_filtered = 1;
            }
            else
                /* show ice-audio_info header as well because of relays */
                bytes = snprintf (ptr, remaining, "%s: %s\r\n", var->name, var->value);
        }
        else
        {
            if (strcasecmp(var->name, "ice-password") &&
                strcasecmp(var->name, "icy-metaint"))
            {
                if (!strcasecmp(var->name, "ice-name"))
                {
                    ice_config_t *config;
                    mount_proxy *mountinfo;

                    config = config_get_config();
                    mountinfo = config_find_mount (config, source->mount, MOUNT_TYPE_NORMAL);

                    if (mountinfo && mountinfo->stream_name)
                        bytes = snprintf (ptr, remaining, "icy-name:%s\r\n", mountinfo->stream_name);
                    else
                        bytes = snprintf (ptr, remaining, "icy-name:%s\r\n", var->value);

                    config_release_config();
                }
                else if (!strncasecmp("ice-", var->name, 4))
                {
                    if (!strcasecmp("ice-public", var->name))
                        bytes = snprintf (ptr, remaining, "icy-pub:%s\r\n", var->value);
                    else
                        if (!strcasecmp ("ice-bitrate", var->name))
                            bytes = snprintf (ptr, remaining, "icy-br:%s\r\n", var->value);
                        else
                            bytes = snprintf (ptr, remaining, "icy%s:%s\r\n",
                                    var->name + 3, var->value);
                }
                else
                    if (!strncasecmp("icy-", var->name, 4))
                    {
                        bytes = snprintf (ptr, remaining, "icy%s:%s\r\n",
                                var->name + 3, var->value);
                    }
            }
        }

        remaining -= bytes;
        ptr += bytes;
        if (next)
            node = avl_get_next(node);
    }
    avl_tree_unlock(source->parser->vars);

    bytes = snprintf(ptr, remaining, "\r\n");
    remaining -= bytes;
    ptr += bytes;

    client->refbuf->len -= remaining;
    if (source->format->create_client_data)
        if (source->format->create_client_data (source, client) < 0)
            return -1;
    return 0;
}
Пример #14
0
/* read mp3 data with inlined metadata from the source. Filter out the
 * metadata so that the mp3 data itself is store on the queue and the
 * metadata is is associated with it
 */
static refbuf_t *mp3_get_filter_meta (source_t *source)
{
    refbuf_t *refbuf;
    format_plugin_t *plugin = source->format;
    mp3_state *source_mp3 = plugin->_state;
    unsigned char *src;
    unsigned int bytes, mp3_block;

    if (complete_read (source) == 0)
        return NULL;

    refbuf = source_mp3->read_data;
    source_mp3->read_data = NULL;
    src = (unsigned char *)refbuf->data;

    if (source_mp3->update_metadata)
    {
        mp3_set_title (source);
        source_mp3->update_metadata = 0;
    }
    /* fill the buffer with the read data */
    bytes = source_mp3->read_count;
    refbuf->len = 0;
    while (bytes > 0)
    {
        unsigned int metadata_remaining;

        mp3_block = source_mp3->inline_metadata_interval - source_mp3->offset;

        /* is there only enough to account for mp3 data */
        if (bytes <= mp3_block)
        {
            refbuf->len += bytes;
            source_mp3->offset += bytes;
            break;
        }
        /* we have enough data to get to the metadata
         * block, but only transfer upto it */
        if (mp3_block)
        {
            src += mp3_block;
            bytes -= mp3_block;
            refbuf->len += mp3_block;
            source_mp3->offset += mp3_block;
            continue;
        }

        /* process the inline metadata, len == 0 indicates not seen any yet */
        if (source_mp3->build_metadata_len == 0)
        {
            memset (source_mp3->build_metadata, 0,
                    sizeof (source_mp3->build_metadata));
            source_mp3->build_metadata_offset = 0;
            source_mp3->build_metadata_len = 1 + (*src * 16);
        }

        /* do we have all of the metatdata block */
        metadata_remaining = source_mp3->build_metadata_len -
            source_mp3->build_metadata_offset;
        if (bytes < metadata_remaining)
        {
            memcpy (source_mp3->build_metadata +
                    source_mp3->build_metadata_offset, src, bytes);
            source_mp3->build_metadata_offset += bytes;
            break;
        }
        /* copy all bytes except the last one, that way we 
         * know a null byte terminates the message */
        memcpy (source_mp3->build_metadata + source_mp3->build_metadata_offset,
                src, metadata_remaining-1);

        /* overwrite metadata in the buffer */
        bytes -= metadata_remaining;
        memmove (src, src+metadata_remaining, bytes);

        /* assign metadata if it's greater than 1 byte, and the text has changed */
        if (source_mp3->build_metadata_len > 1 &&
                strcmp (source_mp3->build_metadata+1, source_mp3->metadata->data+1) != 0)
        {
            refbuf_t *meta = refbuf_new (source_mp3->build_metadata_len);
            memcpy (meta->data, source_mp3->build_metadata,
                    source_mp3->build_metadata_len);

	    ICECAST_LOG_DEBUG("shoutcast metadata %.*s", 4080, meta->data+1);
            if (strncmp (meta->data+1, "StreamTitle=", 12) == 0)
            {
                filter_shoutcast_metadata (source, source_mp3->build_metadata,
                        source_mp3->build_metadata_len);
                refbuf_release (source_mp3->metadata);
                source_mp3->metadata = meta;
                source_mp3->inline_url = strstr (meta->data+1, "StreamUrl='");
            }
            else
            {
                ICECAST_LOG_ERROR("Incorrect metadata format, ending stream");
                source->running = 0;
                refbuf_release (refbuf);
                refbuf_release (meta);
                return NULL;
            }
        }
        source_mp3->offset = 0;
        source_mp3->build_metadata_len = 0;
    }
    /* the data we have just read may of just been metadata */
    if (refbuf->len == 0)
    {
        refbuf_release (refbuf);
        return NULL;
    }
    refbuf->associated = source_mp3->metadata;
    refbuf_addref (source_mp3->metadata);
    refbuf->sync_point = 1;

    return refbuf;
}
Пример #15
0
/* a new BOS page has been seen so check which codec it is */
static int process_initial_page (format_plugin_t *plugin, ogg_page *page)
{
    ogg_state_t *ogg_info = plugin->_state;
    ogg_codec_t *codec;

    if (ogg_info->bos_completed)
    {
        ogg_info->bitrate = 0;
        ogg_info->codec_sync = NULL;
        /* need to zap old list of codecs when next group of BOS pages appear */
        free_ogg_codecs (ogg_info);
    }
    do
    {
        if (ogg_info->codec_count > 10)
        {
            ICECAST_LOG_ERROR("many codecs in stream, playing safe, dropping source");
            ogg_info->error = 1;
            return -1;
        }
        codec = initial_vorbis_page (plugin, page);
        if (codec)
            break;
#ifdef HAVE_THEORA
        codec = initial_theora_page (plugin, page);
        if (codec)
            break;
#endif
        codec = initial_midi_page (plugin, page);
        if (codec)
            break;
        codec = initial_flac_page (plugin, page);
        if (codec)
            break;
#ifdef HAVE_SPEEX
        codec = initial_speex_page (plugin, page);
        if (codec)
            break;
#endif
        codec = initial_kate_page (plugin, page);
        if (codec)
            break;
        codec = initial_skeleton_page (plugin, page);
        if (codec)
            break;
        codec = initial_opus_page (plugin, page);
        if (codec)
            break;

        /* any others */
        ICECAST_LOG_ERROR("Seen BOS page with unknown type");
        ogg_info->error = 1;
        return -1;
    } while (0);

    if (codec)
    {
        /* add codec to list */
        codec->next = ogg_info->codecs;
        ogg_info->codecs = codec;
        ogg_info->codec_count++;
    }

    return 0;
}
Пример #16
0
/* main plugin handler for getting a buffer for the queue. In here we
 * just add an incoming page to the codecs and process it until either
 * more data is needed or we prodice a buffer for the queue.
 */
static refbuf_t *ogg_get_buffer (source_t *source)
{
    ogg_state_t *ogg_info = source->format->_state;
    format_plugin_t *format = source->format;
    char *data = NULL;
    int bytes = 0;

    while (1)
    {
        while (1)
        {
            ogg_page page;
            refbuf_t *refbuf = NULL;
            ogg_codec_t *codec = ogg_info->current;

            /* if a codec has just been given a page then process it */
            if (codec && codec->process)
            {
                refbuf = codec->process (ogg_info, codec);
                if (refbuf)
                    return complete_buffer (source, refbuf);

                ogg_info->current = NULL;
            }

            if (ogg_sync_pageout (&ogg_info->oy, &page) > 0)
            {
                if (ogg_page_bos (&page))
                {
                    process_initial_page (source->format, &page);
                }
                else
                {
                    ogg_info->bos_completed = 1;
                    refbuf = process_ogg_page (ogg_info, &page);
                }
                if (ogg_info->error)
                {
                    ICECAST_LOG_ERROR("Problem processing stream");
                    source->running = 0;
                    return NULL;
                }
                if (refbuf)
                    return complete_buffer (source, refbuf);
                continue;
            }
            /* need more stream data */
            break;
        }
        /* we need more data to continue getting pages */
        data = ogg_sync_buffer (&ogg_info->oy, 4096);

        bytes = client_read_bytes (source->client, data, 4096);
        if (bytes <= 0)
        {
            ogg_sync_wrote (&ogg_info->oy, 0);
            return NULL;
        }
        format->read_bytes += bytes;
        ogg_sync_wrote (&ogg_info->oy, bytes);
    }
}
Пример #17
0
/* 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;
}
Пример #18
0
/* 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;
}