void xslt_transform(xmlDocPtr doc, const char *xslfilename, client_t *client) { xmlDocPtr res; xsltStylesheetPtr cur; xmlChar *string; int len, problem = 0; xmlSetGenericErrorFunc ("", log_parse_failure); xsltSetGenericErrorFunc ("", log_parse_failure); thread_mutex_lock(&xsltlock); cur = xslt_get_stylesheet(xslfilename); if (cur == NULL) { thread_mutex_unlock(&xsltlock); ERROR1 ("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; thread_mutex_unlock(&xsltlock); if (problem == 0) { const char *http = "HTTP/1.0 200 OK\r\nContent-Type: text/html\r\nContent-Length: "; int buf_len = strlen (http) + 20 + len; if (string == NULL) string = xmlStrdup (""); client->respcode = 200; client_set_queue (client, NULL); client->refbuf = refbuf_new (buf_len); len = snprintf (client->refbuf->data, buf_len, "%s%d\r\n\r\n%s", http, len, string); client->refbuf->len = len; fserve_add_client (client, NULL); xmlFree (string); } else { WARN1 ("problem applying stylesheet \"%s\"", xslfilename); client_send_404 (client, "XSLT problem"); } xmlFreeDoc(res); }
int xslt_transform (xmlDocPtr doc, const char *xslfilename, client_t *client) { int i, ret; xsl_req *x; thread_rwlock_rlock (&xslt_lock); i = xslt_cached (xslfilename, NULL, client->worker->current_time.tv_sec); i = xslt_req_sheet (client, doc, xslfilename, i); x = client->shared_data; switch (i) { case -1: thread_rwlock_unlock (&xslt_lock); xmlFreeDoc (doc); client->shared_data = NULL; ret = client_send_404 (client, "Could not parse XSLT file"); break; case CACHESIZE: // delayed thread_rwlock_unlock (&xslt_lock); return 0; default: // found it and ok to use ret = xslt_send_sheet (client, doc, i); break; } if (x) { free (x->cache.filename); free (x->cache.disposition); free (x); } return ret; }
static int prefile_send (client_t *client) { int loop = 8, bytes, written = 0; worker_t *worker = client->worker; while (loop) { refbuf_t *refbuf = client->refbuf; fh_node *fh = client->shared_data; loop--; if (fserve_running == 0 || client->connection.error) return -1; if (refbuf == NULL || client->pos == refbuf->len) { if (fh->finfo.fallback) return fserve_move_listener (client); if (refbuf == NULL || refbuf->next == NULL) { if (file_in_use (fh->f)) // is there a file to read from { if (fh->finfo.limit) client->ops = &throttled_file_content_ops; else client->ops = &file_content_ops; refbuf_release (client->refbuf); client->refbuf = NULL; client->pos = 0; return client->ops->process (client); } if (client->respcode) return -1; return client_send_404 (client, NULL); } else { refbuf_t *to_go = client->refbuf; refbuf = client->refbuf = to_go->next; to_go->next = NULL; refbuf_release (to_go); } client->pos = 0; } if (refbuf->flags & WRITE_BLOCK_GENERIC) bytes = format_generic_write_to_client (client); else bytes = client->check_buffer (client); if (bytes < 0) { client->schedule_ms = worker->time_ms + (written ? 150 : 300); return 0; } written += bytes; global_add_bitrates (global.out_bitrate, bytes, worker->time_ms); if (written > 30000) break; } return 0; }
static void _handle_source_request (client_t *client, char *uri, int auth_style) { source_t *source; INFO1("Source logging in at mountpoint \"%s\"", uri); if (uri[0] != '/') { WARN0 ("source mountpoint not starting with /"); client_send_401 (client); return; } if (auth_style == ICECAST_SOURCE_AUTH) { if (connection_check_source_pass (client->parser, uri) == 0) { /* We commonly get this if the source client is using the wrong * protocol: attempt to diagnose this and return an error */ /* TODO: Do what the above comment says */ INFO1("Source (%s) attempted to login with invalid or missing password", uri); client_send_401(client); return; } } source = source_reserve (uri); if (source) { if (auth_style == SHOUTCAST_SOURCE_AUTH) { source->shoutcast_compat = 1; } source->client = client; source->parser = client->parser; source->con = client->con; if (connection_complete_source (source, 1) < 0) { source_clear_source (source); source_free_source (source); } else { refbuf_t *ok = refbuf_new (PER_CLIENT_REFBUF_SIZE); client->respcode = 200; snprintf (ok->data, PER_CLIENT_REFBUF_SIZE, "HTTP/1.0 200 OK\r\n\r\n"); ok->len = strlen (ok->data); /* we may have unprocessed data read in, so don't overwrite it */ ok->associated = client->refbuf; client->refbuf = ok; fserve_add_client_callback (client, source_client_callback, source); } } else { client_send_404 (client, "Mountpoint in use"); WARN1 ("Mountpoint %s in use", uri); } }
int fserve_client_create(client_t *httpclient, char *path) { fserve_t *client = calloc(1, sizeof(fserve_t)); int bytes; int client_limit; ice_config_t *config = config_get_config(); client_limit = config->client_limit; config_release_config(); client->file = fopen(path, "rb"); if(!client->file) { client_send_404(httpclient, "File not readable"); return -1; } client->client = httpclient; client->offset = 0; client->datasize = 0; client->ready = 0; client->buf = malloc(BUFSIZE); global_lock(); if(global.clients >= client_limit) { httpclient->respcode = 504; bytes = sock_write(httpclient->con->sock, "HTTP/1.0 504 Server Full\r\n" "Content-Type: text/html\r\n\r\n" "<b>Server is full, try again later.</b>\r\n"); if(bytes > 0) httpclient->con->sent_bytes = bytes; fserve_client_destroy(client); global_unlock(); return -1; } global.clients++; global_unlock(); httpclient->respcode = 200; bytes = sock_write(httpclient->con->sock, "HTTP/1.0 200 OK\r\n" "Content-Type: %s\r\n\r\n", fserve_content_type(path)); if(bytes > 0) httpclient->con->sent_bytes = bytes; sock_set_blocking(client->client->con->sock, SOCK_NONBLOCK); sock_set_nodelay(client->client->con->sock); thread_mutex_lock (&pending_lock); client->next = (fserve_t *)pending_list; pending_list = client; thread_mutex_unlock (&pending_lock); return 0; }
static int _free_client(void *key) { client_t *client = (client_t *)key; /* if no response has been sent then send a 404 */ if (client->respcode == 0) client_send_404 (client, "Mount unavailable"); else client_destroy(client); return 1; }
/* thread to read xsl file and add to the cache */ void *xslt_update (void *arg) { xsl_req *x = arg; client_t *client = x->client; char *fn = x->cache.filename; xsltStylesheetPtr sheet; xmlSetStructuredErrorFunc ("xsl/file", config_xml_parse_failure); xsltSetGenericErrorFunc ("", log_parse_failure); sheet = x->cache.stylesheet = xsltParseStylesheetFile (XMLSTR(fn)); if (sheet) { int i; INFO1 ("loaded stylesheet %s", x->cache.filename); if (sheet->mediaType && strcmp ((char*)sheet->mediaType, "text/html") != 0) { // avoid this lookup for html pages const char _hdr[] = "Content-Disposition: attachment; filename=\"file."; const size_t _hdrlen = sizeof (_hdr); size_t len = _hdrlen + 12; char *filename = malloc (len); // enough for name and extension strcpy (filename, _hdr); fserve_write_mime_ext ((char*)sheet->mediaType, filename + _hdrlen - 1, len - _hdrlen - 4); strcat (filename, "\"\r\n"); x->cache.disposition = filename; } // we now have a sheet, find and update. thread_rwlock_wlock (&xslt_lock); i = xslt_cached (fn, &x->cache, time(NULL)); xslt_send_sheet (client, x->doc, i); } else { WARN1 ("problem reading stylesheet \"%s\"", x->cache.filename); free (fn); xmlFreeDoc (x->doc); free (x->cache.disposition); client->shared_data = NULL; client_send_404 (client, "Could not parse XSLT file"); } thread_spin_lock (&update_lock); xsl_updating--; thread_spin_unlock (&update_lock); free (x); return NULL; }
static void _handle_source_request(connection_t *con, http_parser_t *parser, char *uri) { client_t *client; source_t *source; client = client_create(con, parser); INFO1("Source logging in at mountpoint \"%s\"", uri); if (uri[0] != '/') { WARN0 ("source mountpoint not starting with /"); client_send_401 (client); return; } if (!connection_check_source_pass(parser, uri)) { /* We commonly get this if the source client is using the wrong * protocol: attempt to diagnose this and return an error */ /* TODO: Do what the above comment says */ INFO1("Source (%s) attempted to login with invalid or missing password", uri); client_send_401(client); return; } source = source_reserve (uri); if (source) { source->client = client; source->parser = parser; source->con = con; if (connection_complete_source (source) < 0) { source->client = NULL; source_free_source (source); } else thread_create ("Source Thread", source_client_thread, source, THREAD_DETACHED); } else { client_send_404 (client, "Mountpoint in use"); WARN1 ("Mountpoint %s in use", uri); } }
/* Add client to fserve thread, client needs to have refbuf set and filled * but may provide a NULL file if no data needs to be read */ int fserve_add_client (client_t *client, FILE *file) { fserve_t *fclient = calloc (1, sizeof(fserve_t)); DEBUG0 ("Adding client to file serving engine"); if (fclient == NULL) { client_send_404 (client, "memory exhausted"); return -1; } fclient->file = file; fclient->client = client; fclient->ready = 0; fserve_add_pending (fclient); return 0; }
/* add client to file serving engine, but just write out the buffer contents, * then pass the client to the callback with the provided arg */ void fserve_add_client_callback (client_t *client, fserve_callback_t callback, void *arg) { fserve_t *fclient = calloc (1, sizeof(fserve_t)); DEBUG0 ("Adding client to file serving engine"); if (fclient == NULL) { client_send_404 (client, "memory exhausted"); return; } fclient->file = NULL; fclient->client = client; fclient->ready = 0; fclient->callback = callback; fclient->arg = arg; fserve_add_pending (fclient); }
/* wrapper function for auth thread to drop listener connections */ static void auth_remove_listener (auth_client *auth_user) { if (auth_user->auth->release_listener) auth_user->auth->release_listener (auth_user); auth_user->auth = NULL; /* client is going, so auth is not an issue at this point */ if (auth_user->client) { client_t *client = auth_user->client; client->flags &= ~CLIENT_AUTHENTICATED; if (client->worker) client_send_404 (client, NULL); else client_destroy (auth_user->client); auth_user->client = NULL; } }
/* General listener client shutdown function. Here we free up the passed client but * if the client is authenticated and there's auth available then queue it. */ int auth_release_listener (client_t *client, const char *mount, mount_proxy *mountinfo) { if (client->flags & CLIENT_AUTHENTICATED) { client_set_queue (client, NULL); if (mount && mountinfo && mountinfo->auth && mountinfo->auth->release_listener) { auth_client *auth_user = auth_client_setup (mount, client); client->flags &= ~CLIENT_ACTIVE; if (client->worker) client->ops = &auth_release_ops; // put into a wait state auth_user->process = auth_remove_listener; queue_auth_client (auth_user, mountinfo); return 0; } client->flags &= ~CLIENT_AUTHENTICATED; } return client_send_404 (client, NULL); }
static int command_show_image (client_t *client, const char *mount) { source_t *source; avl_tree_rlock (global.source_tree); source = source_find_mount_raw (mount); if (source && source->format && source->format->get_image) { thread_mutex_lock (&source->lock); avl_tree_unlock (global.source_tree); if (source->format->get_image (client, source->format) == 0) { thread_mutex_unlock (&source->lock); return fserve_setup_client (client); } thread_mutex_unlock (&source->lock); } else avl_tree_unlock (global.source_tree); return client_send_404 (client, "No image available"); }
/* thread to read xsl file and add to the cache */ void *xslt_update (void *arg) { xsl_req *x = arg; client_t *client = x->client; worker_t *worker = client ? client->worker : NULL; char *fn = x->cache.filename; x->cache.stylesheet = xsltParseStylesheetFile (XMLSTR(fn)); if (x->cache.stylesheet) { int i = x->index; if (client) fn = strdup (fn); // need to copy the filename if another lookup is to do INFO1 ("loaded stylesheet %s", x->cache.filename); thread_rwlock_wlock (&xslt_lock); free (cache[i].filename); xsltFreeStylesheet (cache[i].stylesheet); memcpy (&cache[i], &x->cache, sizeof (stylesheet_cache_t)); thread_rwlock_unlock (&xslt_lock); memset (&x->cache, 0, sizeof (stylesheet_cache_t)); if (client) { x->cache.filename = fn; client->flags |= CLIENT_ACTIVE; } } else { WARN1 ("problem reading stylesheet \"%s\"", x->cache.filename); free (fn); if (client) client_send_404 (client, "Could not parse XSLT file"); } thread_spin_lock (&update_lock); xsl_updating--; thread_spin_unlock (&update_lock); if (worker) worker_wakeup (worker); // wakeup after the decrease or it may delay if (client == NULL) free (x); return NULL; }
void connection_accept_loop(void) { connection_t *con; if (!kitsune_is_updating()) /**DSU control */ tid = thread_create("connection thread", _handle_connection, NULL, THREAD_ATTACHED); while (global.running == ICE_RUNNING) { kitsune_update("connection_accept"); /**DSU updatepoint */ con = _accept_connection(); if (con) { client_queue_t *node; ice_config_t *config; int i; client_t *client = NULL; global_lock(); if (client_create (&client, con, NULL) < 0) { global_unlock(); client_send_404 (client, "Icecast connection limit reached"); continue; } global_unlock(); /* setup client for reading incoming http */ client->refbuf->data [PER_CLIENT_REFBUF_SIZE-1] = '\000'; node = calloc (1, sizeof (client_queue_t)); if (node == NULL) { client_destroy (client); continue; } node->client = client; /* Check for special shoutcast compatability processing */ config = config_get_config(); for (i = 0; i < global.server_sockets; i++) { if (global.serversock[i] == con->serversock) { if (config->listeners[i].shoutcast_compat) node->shoutcast = 1; } } config_release_config(); sock_set_blocking (client->con->sock, SOCK_NONBLOCK); sock_set_nodelay (client->con->sock); _add_request_queue (node); stats_event_inc (NULL, "connections"); } process_request_queue (); } /* Give all the other threads notification to shut down */ thread_cond_broadcast(&global.shutdown_cond); if (tid) thread_join (tid); /* wait for all the sources to shutdown */ thread_rwlock_wlock(&_source_shutdown_rwlock); thread_rwlock_unlock(&_source_shutdown_rwlock); }
static void _handle_get_request(connection_t *con, http_parser_t *parser, char *passed_uri) { char *fullpath; client_t *client; int bytes; struct stat statbuf; source_t *source; int fileserve; char *host; int port; int i; char *serverhost = NULL; int serverport = 0; aliases *alias; ice_config_t *config; int client_limit; int ret; char *uri = passed_uri; config = config_get_config(); fileserve = config->fileserve; host = config->hostname; port = config->port; for(i = 0; i < MAX_LISTEN_SOCKETS; i++) { if(global.serversock[i] == con->serversock) { serverhost = config->listeners[i].bind_address; serverport = config->listeners[i].port; break; } } alias = config->aliases; client_limit = config->client_limit; /* there are several types of HTTP GET clients ** media clients, which are looking for a source (eg, URI = /stream.ogg) ** stats clients, which are looking for /admin/stats.xml ** and directory server authorizers, which are looking for /GUID-xxxxxxxx ** (where xxxxxx is the GUID in question) - this isn't implemented yet. ** we need to handle the latter two before the former, as the latter two ** aren't subject to the limits. */ /* TODO: add GUID-xxxxxx */ /* Handle aliases */ while(alias) { if(strcmp(uri, alias->source) == 0 && (alias->port == -1 || alias->port == serverport) && (alias->bind_address == NULL || (serverhost != NULL && strcmp(alias->bind_address, serverhost) == 0))) { uri = strdup (alias->destination); DEBUG2 ("alias has made %s into %s", passed_uri, uri); break; } alias = alias->next; } config_release_config(); /* make a client */ client = client_create(con, parser); stats_event_inc(NULL, "client_connections"); /* Dispatch all admin requests */ if (strncmp(uri, "/admin/", 7) == 0) { admin_handle_request(client, uri); if (uri != passed_uri) free (uri); return; } /* Here we are parsing the URI request to see ** if the extension is .xsl, if so, then process ** this request as an XSLT request */ fullpath = util_get_path_from_normalised_uri(uri); if (util_check_valid_extension(fullpath) == XSLT_CONTENT) { /* If the file exists, then transform it, otherwise, write a 404 */ if (stat(fullpath, &statbuf) == 0) { DEBUG0("Stats request, sending XSL transformed stats"); client->respcode = 200; bytes = sock_write(client->con->sock, "HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n"); if(bytes > 0) client->con->sent_bytes = bytes; stats_transform_xslt(client, fullpath); client_destroy(client); } else { client_send_404(client, "The file you requested could not be found"); } free(fullpath); if (uri != passed_uri) free (uri); return; } else if(fileserve && stat(fullpath, &statbuf) == 0 && #ifdef _WIN32 ((statbuf.st_mode) & _S_IFREG)) #else S_ISREG(statbuf.st_mode)) #endif { fserve_client_create(client, fullpath); free(fullpath); if (uri != passed_uri) free (uri); return; } free(fullpath); if(strcmp(util_get_extension(uri), "m3u") == 0) { char *sourceuri = strdup(uri); char *dot = strrchr(sourceuri, '.'); *dot = 0; client->respcode = 200; bytes = sock_write(client->con->sock, "HTTP/1.0 200 OK\r\n" "Content-Type: audio/x-mpegurl\r\n\r\n" "http://%s:%d%s\r\n", host, port, sourceuri ); if(bytes > 0) client->con->sent_bytes = bytes; client_destroy(client); free(sourceuri); if (uri != passed_uri) free (uri); return; } global_lock(); if (global.clients >= client_limit) { global_unlock(); client_send_404(client, "The server is already full. Try again later."); if (uri != passed_uri) free (uri); return; } global_unlock(); avl_tree_rlock(global.source_tree); source = source_find_mount(uri); if (source) { DEBUG0("Source found for client"); /* The source may not be the requested source - it might have gone * via one or more fallbacks. We only reject it for no-mount if it's * the originally requested source */ if(strcmp(uri, source->mount) == 0 && source->no_mount) { avl_tree_unlock(global.source_tree); client_send_404(client, "This mount is unavailable."); if (uri != passed_uri) free (uri); return; } if (source->running == 0) { avl_tree_unlock(global.source_tree); DEBUG0("inactive source, client dropped"); client_send_404(client, "This mount is unavailable."); if (uri != passed_uri) free (uri); return; } /* Check for any required authentication first */ if(source->authenticator != NULL) { ret = auth_check_client(source, client); if(ret != AUTH_OK) { avl_tree_unlock(global.source_tree); if (ret == AUTH_FORBIDDEN) { INFO1("Client attempted to log multiple times to source " "(\"%s\")", uri); client_send_403(client); } else { /* If not FORBIDDEN, default to 401 */ INFO1("Client attempted to log in to source (\"%s\")with " "incorrect or missing password", uri); client_send_401(client); } if (uri != passed_uri) free (uri); return; } } /* And then check that there's actually room in the server... */ global_lock(); if (global.clients >= client_limit) { global_unlock(); avl_tree_unlock(global.source_tree); client_send_404(client, "The server is already full. Try again later."); if (uri != passed_uri) free (uri); return; } /* Early-out for per-source max listeners. This gets checked again * by the source itself, later. This route gives a useful message to * the client, also. */ else if(source->max_listeners != -1 && source->listeners >= source->max_listeners) { global_unlock(); avl_tree_unlock(global.source_tree); client_send_404(client, "Too many clients on this mountpoint. Try again later."); if (uri != passed_uri) free (uri); return; } global.clients++; global_unlock(); source->format->create_client_data (source, client); source->format->client_send_headers(source->format, source, client); bytes = sock_write(client->con->sock, "\r\n"); if(bytes > 0) client->con->sent_bytes += bytes; sock_set_blocking(client->con->sock, SOCK_NONBLOCK); sock_set_nodelay(client->con->sock); avl_tree_wlock(source->pending_tree); avl_insert(source->pending_tree, (void *)client); avl_tree_unlock(source->pending_tree); } avl_tree_unlock(global.source_tree); if (!source) { DEBUG0("Source not found for client"); client_send_404(client, "The source you requested could not be found."); } if (uri != passed_uri) free (uri); }
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; xmlSetGenericErrorFunc ("", log_parse_failure); xsltSetGenericErrorFunc ("", log_parse_failure); thread_mutex_lock(&xsltlock); cur = xslt_get_stylesheet(xslfilename); if (cur == NULL) { thread_mutex_unlock(&xsltlock); ERROR1 ("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 to use */ 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) { /* the 100 is to allow for the hardcoded headers */ unsigned int full_len = strlen (mediatype) + len + 256; refbuf_t *refbuf = refbuf_new (full_len); ssize_t ret; if (string == NULL) string = xmlCharStrdup (""); ret = util_http_build_header(refbuf->data, full_len, 0, 0, 200, NULL, mediatype, NULL, NULL); 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 { WARN1 ("problem applying stylesheet \"%s\"", xslfilename); client_send_404 (client, "XSLT problem"); } thread_mutex_unlock (&xsltlock); xmlFreeDoc(res); }
static void *_handle_connection(void *arg) { char header[4096]; connection_t *con; http_parser_t *parser; char *rawuri, *uri; client_t *client; while (global.running == ICE_RUNNING) { /* grab a connection and set the socket to blocking */ while ((con = _get_connection())) { /* Handle meta-connections */ if(con->event_number > 0) { switch(con->event_number) { case EVENT_CONFIG_READ: event_config_read(con->event); break; default: ERROR1("Unknown event number: %d", con->event_number); break; } free(con); continue; } stats_event_inc(NULL, "connections"); sock_set_blocking(con->sock, SOCK_BLOCK); /* fill header with the http header */ memset(header, 0, sizeof (header)); if (util_read_header(con->sock, header, sizeof (header)) == 0) { /* either we didn't get a complete header, or we timed out */ connection_close(con); continue; } parser = httpp_create_parser(); httpp_initialize(parser, NULL); if (httpp_parse(parser, header, strlen(header))) { /* handle the connection or something */ if (strcmp("ICE", httpp_getvar(parser, HTTPP_VAR_PROTOCOL)) && strcmp("HTTP", httpp_getvar(parser, HTTPP_VAR_PROTOCOL))) { ERROR0("Bad HTTP protocol detected"); connection_close(con); httpp_destroy(parser); continue; } rawuri = httpp_getvar(parser, HTTPP_VAR_URI); uri = util_normalise_uri(rawuri); if(!uri) { client = client_create(con, parser); client_send_404(client, "The path you requested was invalid"); continue; } if (parser->req_type == httpp_req_source) { _handle_source_request(con, parser, uri); } else if (parser->req_type == httpp_req_stats) { _handle_stats_request(con, parser, uri); } else if (parser->req_type == httpp_req_get) { _handle_get_request(con, parser, uri); } else { ERROR0("Wrong request type from client"); connection_close(con); httpp_destroy(parser); } free(uri); continue; } else if(httpp_parse_icy(parser, header, strlen(header))) { /* TODO: Map incoming icy connections to /icy_0, etc. */ char mount[20]; unsigned i = 0; strcpy(mount, "/"); avl_tree_rlock(global.source_tree); while (source_find_mount (mount) != NULL) { snprintf (mount, sizeof (mount), "/icy_%u", i++); } avl_tree_unlock(global.source_tree); _handle_source_request(con, parser, mount); continue; } else { ERROR0("HTTP request parsing failed"); connection_close(con); httpp_destroy(parser); continue; } } thread_sleep (100000); } DEBUG0 ("Connection thread done"); return NULL; }
// requires xslt_lock before being called, released on return static int xslt_send_sheet (client_t *client, xmlDocPtr doc, int idx) { xmlDocPtr res; xsltStylesheetPtr cur = cache [idx].stylesheet; char **params = NULL; refbuf_t *content = NULL; int len; if (client->parser->queryvars) { // annoying but we need to surround the args with ' when passing them in int j, arg_count = client->parser->queryvars->length * 2; avl_node *node = avl_get_first (client->parser->queryvars); params = calloc (arg_count+1, sizeof (char *)); for (j = 0; node && j < arg_count; node = avl_get_next (node)) { http_var_t *param = (http_var_t *)node->key; char *tmp = util_url_escape (param->value); params[j++] = param->name; // use alloca for now, should really url esc into a supplied buffer params[j] = (char*)alloca (strlen (tmp) + 3); sprintf (params[j++], "\'%s\'", tmp); free (tmp); } params[j] = NULL; } res = xsltApplyStylesheet (cur, doc, (const char **)params); free (params); client->shared_data = NULL; if (res == NULL || xslt_SaveResultToBuf (&content, &len, res, cur) < 0) { thread_rwlock_unlock (&xslt_lock); xmlFreeDoc (res); xmlFreeDoc (doc); WARN1 ("problem applying stylesheet \"%s\"", cache [idx].filename); return client_send_404 (client, "XSLT problem"); } else { /* the 100 is to allow for the hardcoded headers */ refbuf_t *refbuf = refbuf_new (500); const char *mediatype = NULL; /* lets find out the content type to use */ 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"; } snprintf (refbuf->data, 500, "HTTP/1.0 200 OK\r\nContent-Type: %s\r\nContent-Length: %d\r\n%s" "Expires: Thu, 19 Nov 1981 08:52:00 GMT\r\n" "Cache-Control: no-store, no-cache, must-revalidate\r\n" "Pragma: no-cache\r\n%s\r\n" "Access-Control-Allow-Origin: *\r\n" "Access-Control-Allow-Headers: Origin, Accept, X-Requested-With, Content-Type\r\n" "Access-Control-Allow-Methods: GET, OPTIONS, HEAD\r\n" "\r\n", mediatype, len, cache[idx].disposition ? cache[idx].disposition : "", client_keepalive_header (client)); thread_rwlock_unlock (&xslt_lock); client->respcode = 200; client_set_queue (client, NULL); client->refbuf = refbuf; refbuf->len = strlen (refbuf->data); refbuf->next = content; } xmlFreeDoc(res); xmlFreeDoc(doc); return fserve_setup_client (client); }
int fserve_client_create(client_t *httpclient, char *path) { fserve_t *client = calloc(1, sizeof(fserve_t)); int bytes; int client_limit; ice_config_t *config = config_get_config(); struct stat file_buf; char *range = NULL; int64_t new_content_len = 0; int64_t rangenumber = 0; int rangeproblem = 0; int ret = 0; client_limit = config->client_limit; config_release_config(); client->file = fopen(path, "rb"); if(!client->file) { client_send_404(httpclient, "File not readable"); return -1; } client->client = httpclient; client->offset = 0; client->datasize = 0; client->ready = 0; client->content_length = 0; client->buf = malloc(BUFSIZE); if (stat(path, &file_buf) == 0) { client->content_length = (int64_t)file_buf.st_size; } global_lock(); if(global.clients >= client_limit) { global_unlock(); httpclient->respcode = 504; bytes = sock_write(httpclient->con->sock, "HTTP/1.0 504 Server Full\r\n" "Content-Type: text/html\r\n\r\n" "<b>Server is full, try again later.</b>\r\n"); if(bytes > 0) httpclient->con->sent_bytes = bytes; fserve_client_destroy(client); return -1; } global.clients++; global_unlock(); range = httpp_getvar (client->client->parser, "range"); if (range != NULL) { ret = sscanf(range, "bytes=" FORMAT_INT64 "-", &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 = fseek(client->file, rangenumber, SEEK_SET); if (ret != -1) { new_content_len = client->content_length - rangenumber; if (new_content_len < 0) { rangeproblem = 1; } } else { rangeproblem = 1; } if (!rangeproblem) { /* Date: is required on all HTTP1.1 responses */ char currenttime[50]; time_t now; int strflen; struct tm result; int64_t endpos = rangenumber+new_content_len-1; if (endpos < 0) { endpos = 0; } time(&now); strflen = strftime(currenttime, 50, "%a, %d-%b-%Y %X GMT", gmtime_r(&now, &result)); httpclient->respcode = 206; bytes = sock_write(httpclient->con->sock, "HTTP/1.1 206 Partial Content\r\n" "Date: %s\r\n" "Content-Length: " FORMAT_INT64 "\r\n" "Content-Range: bytes " FORMAT_INT64 \ "-" FORMAT_INT64 "/" FORMAT_INT64 "\r\n" "Content-Type: %s\r\n\r\n", currenttime, new_content_len, rangenumber, endpos, client->content_length, fserve_content_type(path)); if(bytes > 0) httpclient->con->sent_bytes = bytes; } else { httpclient->respcode = 416; bytes = sock_write(httpclient->con->sock, "HTTP/1.0 416 Request Range Not Satisfiable\r\n\r\n"); if(bytes > 0) httpclient->con->sent_bytes = bytes; fserve_client_destroy(client); return -1; } } else { /* If we run into any issues with the ranges we fallback to a normal/non-range request */ httpclient->respcode = 416; bytes = sock_write(httpclient->con->sock, "HTTP/1.0 416 Request Range Not Satisfiable\r\n\r\n"); if(bytes > 0) httpclient->con->sent_bytes = bytes; fserve_client_destroy(client); return -1; } } else { httpclient->respcode = 200; bytes = sock_write(httpclient->con->sock, "HTTP/1.0 200 OK\r\n" "Content-Length: " FORMAT_INT64 "\r\n" "Content-Type: %s\r\n\r\n", client->content_length, fserve_content_type(path)); if(bytes > 0) httpclient->con->sent_bytes = bytes; } sock_set_blocking(client->client->con->sock, SOCK_NONBLOCK); sock_set_nodelay(client->client->con->sock); thread_mutex_lock (&pending_lock); client->next = (fserve_t *)pending_list; pending_list = client; thread_mutex_unlock (&pending_lock); return 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, const char *path) { 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 (path); INFO2 ("checking for file %H (%H)", path, 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) { WARN2 ("req for file \"%H\" %s", fullpath, strerror (errno)); client_send_404 (httpclient, "The file you requested could not be 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) { const char *host = httpp_getvar (httpclient->parser, "host"); char *sourceuri = strdup (path); char *dot = strrchr(sourceuri, '.'); /* at least a couple of players (fb2k/winamp) are reported to send a * host header but without the port number. So if we are missing the * port then lets treat it as if no host line was sent */ if (host && strchr (host, ':') == NULL) host = NULL; *dot = 0; httpclient->respcode = 200; if (host == NULL) { config = config_get_config(); snprintf (httpclient->refbuf->data, BUFSIZE, "HTTP/1.0 200 OK\r\n" "Content-Type: audio/x-mpegurl\r\n\r\n" "http://%s:%d%s\r\n", config->hostname, config->port, sourceuri ); config_release_config(); } else { snprintf (httpclient->refbuf->data, BUFSIZE, "HTTP/1.0 200 OK\r\n" "Content-Type: audio/x-mpegurl\r\n\r\n" "http://%s%s\r\n", host, sourceuri ); } 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 (path); char *eol = strrchr (reference, '.'); if (eol) *eol = '\0'; doc = stats_get_xml (0, reference); free (reference); admin_send_response (doc, httpclient, TRANSFORMED, xslt_playlist_requested); xmlFreeDoc(doc); return 0; } /* on demand file serving check */ config = config_get_config(); if (config->fileserve == 0) { DEBUG1 ("on demand file \"%H\" refused", fullpath); client_send_404 (httpclient, "The file you requested could not be found"); config_release_config(); free (fullpath); return -1; } config_release_config(); if (S_ISREG (file_buf.st_mode) == 0) { client_send_404 (httpclient, "The file you requested could not be found"); WARN1 ("found requested file but there is no handler for it: %H", fullpath); free (fullpath); return -1; } file = fopen (fullpath, "rb"); if (file == NULL) { WARN1 ("Problem accessing file \"%H\"", fullpath); client_send_404 (httpclient, "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) { /* Date: is required on all HTTP1.1 responses */ char currenttime[50]; time_t now; int strflen; struct tm result; off_t endpos = rangenumber+new_content_len-1; char *type; if (endpos < 0) { endpos = 0; } time(&now); strflen = strftime(currenttime, 50, "%a, %d-%b-%Y %X GMT", gmtime_r(&now, &result)); httpclient->respcode = 206; type = fserve_content_type (path); bytes = snprintf (httpclient->refbuf->data, BUFSIZE, "HTTP/1.1 206 Partial Content\r\n" "Date: %s\r\n" "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" "Content-Type: %s\r\n\r\n", currenttime, new_content_len, rangenumber, endpos, content_length, type); free (type); } else { goto fail; } } else { goto fail; } } else { char *type = fserve_content_type(path); httpclient->respcode = 200; bytes = snprintf (httpclient->refbuf->data, BUFSIZE, "HTTP/1.0 200 OK\r\n" "Accept-Ranges: bytes\r\n" "Content-Length: %" PRI_OFF_T "\r\n" "Content-Type: %s\r\n\r\n", content_length, type); 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); httpclient->respcode = 416; sock_write (httpclient->con->sock, "HTTP/1.0 416 Request Range Not Satisfiable\r\n\r\n"); client_destroy (httpclient); return -1; }
int fserve_setup_client_fb (client_t *client, fbinfo *finfo) { fh_node *fh = &no_file; int ret = 0; if (finfo) { mount_proxy *minfo; if (finfo->flags & FS_FALLBACK && finfo->limit == 0) return -1; avl_tree_wlock (fh_cache); fh = find_fh (finfo); minfo = config_find_mount (config_get_config(), finfo->mount); if (fh) { thread_mutex_lock (&fh->lock); avl_tree_unlock (fh_cache); client->shared_data = NULL; if (minfo) { if (minfo->max_listeners >= 0 && fh->refcount > minfo->max_listeners) { thread_mutex_unlock (&fh->lock); config_release_config(); return client_send_403redirect (client, finfo->mount, "max listeners reached"); } if (check_duplicate_logins (finfo->mount, fh->clients, client, minfo->auth) == 0) { thread_mutex_unlock (&fh->lock); config_release_config(); return client_send_403 (client, "Account already in use"); } } config_release_config(); } else { if (minfo && minfo->max_listeners == 0) { avl_tree_unlock (fh_cache); config_release_config(); client->shared_data = NULL; return client_send_403redirect (client, finfo->mount, "max listeners reached"); } config_release_config(); fh = open_fh (finfo); if (fh == NULL) return client_send_404 (client, NULL); } if (fh->finfo.limit) { client->timer_start = client->worker->current_time.tv_sec; if (client->connection.sent_bytes == 0) client->timer_start -= 2; client->counter = 0; client->intro_offset = 0; global_reduce_bitrate_sampling (global.out_bitrate); } } else thread_mutex_lock (&fh->lock); client->mount = fh->finfo.mount; if (fh->finfo.type == FORMAT_TYPE_UNDEFINED) { if (client->respcode == 0) { client->refbuf->len = 0; ret = format_general_headers (fh->format, client); } } else { if (fh->format->create_client_data && client->format_data == NULL) ret = fh->format->create_client_data (fh->format, client); if (fh->format->write_buf_to_client) client->check_buffer = fh->format->write_buf_to_client; } if (ret < 0) { thread_mutex_unlock (&fh->lock); return client_send_416 (client); } fh_add_client (fh, client); thread_mutex_unlock (&fh->lock); client->shared_data = fh; if (client->check_buffer == NULL) client->check_buffer = format_generic_write_to_client; client->ops = &buffer_content_ops; client->flags &= ~CLIENT_HAS_INTRO_CONTENT; client->flags |= CLIENT_IN_FSERVE; if (client->flags & CLIENT_ACTIVE) { client->schedule_ms = client->worker->time_ms; if (finfo && finfo->flags & FS_FALLBACK) return 0; // prevent a recursive loop return client->ops->process (client); } else { worker_t *worker = client->worker; client->flags |= CLIENT_ACTIVE; worker_wakeup (worker); /* worker may of already processed client but make sure */ } return 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); }
int fserve_setup_client_fb (client_t *client, fbinfo *finfo) { fh_node *fh = &no_file; int ret = 0; refbuf_t *refbuf; ssize_t bytes; if (finfo) { mount_proxy *minfo; if (finfo->flags & FS_FALLBACK && finfo->limit == 0) return -1; avl_tree_wlock (fh_cache); fh = find_fh (finfo); minfo = config_find_mount (config_get_config(), finfo->mount); if (fh) { thread_mutex_lock (&fh->lock); avl_tree_unlock (fh_cache); client->shared_data = NULL; if (minfo) { if (minfo->max_listeners >= 0 && fh->refcount > minfo->max_listeners) { thread_mutex_unlock (&fh->lock); config_release_config(); return client_send_403redirect (client, finfo->mount, "max listeners reached"); } if (check_duplicate_logins (finfo->mount, fh->clients, client, minfo->auth) == 0) { thread_mutex_unlock (&fh->lock); config_release_config(); return client_send_403 (client, "Account already in use"); } } config_release_config(); } else { if (minfo && minfo->max_listeners == 0) { avl_tree_unlock (fh_cache); config_release_config(); client->shared_data = NULL; return client_send_403redirect (client, finfo->mount, "max listeners reached"); } config_release_config(); fh = open_fh (finfo); if (fh == NULL) return client_send_404 (client, NULL); if (fh->finfo.limit) DEBUG2 ("request for throttled file %s (bitrate %d)", fh->finfo.mount, fh->finfo.limit*8); } if (fh->finfo.limit) { client->timer_start = client->worker->current_time.tv_sec; if (client->connection.sent_bytes == 0) client->timer_start -= 2; client->counter = 0; client->intro_offset = 0; global_reduce_bitrate_sampling (global.out_bitrate); } } else { if (client->mount && (client->flags & CLIENT_AUTHENTICATED) && (client->respcode >= 300 || client->respcode < 200)) { fh = calloc (1, sizeof (no_file)); fh->finfo.mount = strdup (client->mount); fh->finfo.flags |= FS_DELETE; fh->refcount = 1; fh->f = SOCK_ERROR; thread_mutex_create (&fh->lock); } thread_mutex_lock (&fh->lock); } client->mount = fh->finfo.mount; if (fh->finfo.type == FORMAT_TYPE_UNDEFINED) { if (client->respcode == 0) { client->refbuf->len = 0; ret = format_general_headers (fh->format, client); } } else { if (fh->format->create_client_data && client->format_data == NULL) ret = fh->format->create_client_data (fh->format, client); if (fh->format->write_buf_to_client) client->check_buffer = fh->format->write_buf_to_client; } if (ret < 0) { thread_mutex_unlock (&fh->lock); return client_send_416 (client); } fh_add_client (fh, client); thread_mutex_unlock (&fh->lock); client->shared_data = fh; if (client->check_buffer == NULL) client->check_buffer = format_generic_write_to_client; // workaround for #134: fill the preallocated, but empty, chained buffer in case a range request was made if (client->flags & CLIENT_RANGE_END) { if (client->refbuf && client->refbuf->next) { refbuf = client->refbuf->next; bytes = pread (fh->f, refbuf->data, refbuf->len, client->intro_offset); if (bytes < 0) return -1; } } client->ops = &buffer_content_ops; client->flags &= ~CLIENT_HAS_INTRO_CONTENT; client->flags |= CLIENT_IN_FSERVE; if (client->flags & CLIENT_ACTIVE) { client->schedule_ms = client->worker->time_ms; if (finfo && finfo->flags & FS_FALLBACK) return 0; // prevent a recursive loop return client->ops->process (client); } else { worker_t *worker = client->worker; ret = (fh->finfo.limit) ? 0 : -1; client->flags |= CLIENT_ACTIVE; worker_wakeup (worker); /* worker may of already processed client but make sure */ } return ret; }
/* 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, const char *path) { struct stat file_buf; char *fullpath; int m3u_requested = 0, m3u_file_available = 1; int xspf_requested = 0, xspf_file_available = 1; int ret = -1; ice_config_t *config; fbinfo finfo; char fsize[20]; fullpath = util_get_path_from_normalised_uri (path, 0); DEBUG2 ("checking for file %s (%s)", path, fullpath); if (strcmp (util_get_extension (fullpath), "m3u") == 0) m3u_requested = 1; if (strcmp (util_get_extension (fullpath), "xspf") == 0) xspf_requested = 1; /* 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 && xspf_requested == 0) { if (redirect_client (path, httpclient) == 0) { if ((httpclient->flags & CLIENT_SKIP_ACCESSLOG) == 0) WARN2 ("req for file \"%s\" %s", fullpath, strerror (errno)); ret = client_send_404 (httpclient, "The file you requested could not be found"); } free (fullpath); return ret; } m3u_file_available = 0; xspf_file_available = 0; } client_set_queue (httpclient, NULL); httpclient->refbuf = refbuf_new (4096); if (m3u_requested && m3u_file_available == 0) { const char *host = httpp_getvar (httpclient->parser, "host"), *args = httpp_getvar (httpclient->parser, HTTPP_VAR_QUERYARGS), *at = "", *user = "", *pass =""; char *sourceuri = strdup (path); char *dot = strrchr (sourceuri, '.'); char *protocol = "http"; const char *agent = httpp_getvar (httpclient->parser, "user-agent"); int x; char scratch[1000]; if (agent) { if (strstr (agent, "QTS") || strstr (agent, "QuickTime")) protocol = "icy"; } /* at least a couple of players (fb2k/winamp) are reported to send a * host header but without the port number. So if we are missing the * port then lets treat it as if no host line was sent */ if (host && strchr (host, ':') == NULL) host = NULL; *dot = 0; if (httpclient->username && httpclient->password) { at = "@"; user = httpclient->username; pass = httpclient->password; } httpclient->respcode = 200; if (host == NULL) { config = config_get_config(); x = snprintf (scratch, sizeof scratch, "%s://%s%s%s%s%s:%d%s%s\r\n", protocol, user, at[0]?":":"", pass, at, config->hostname, config->port, sourceuri, args?args:""); config_release_config(); } else { x = snprintf (scratch, sizeof scratch, "%s://%s%s%s%s%s%s%s\r\n", protocol, user, at[0]?":":"", pass, at, host, sourceuri, args?args:""); } snprintf (httpclient->refbuf->data, BUFSIZE, "HTTP/1.0 200 OK\r\n" "Content-Length: %d\r\n" "%s\r\n" "Content-Type: audio/x-mpegurl\r\n\r\n%s", x, client_keepalive_header (httpclient), scratch); httpclient->refbuf->len = strlen (httpclient->refbuf->data); free (sourceuri); free (fullpath); return fserve_setup_client_fb (httpclient, NULL); } if (xspf_requested && xspf_file_available == 0) { xmlDocPtr doc; char *reference = strdup (path); char *eol = strrchr (reference, '.'); if (eol) *eol = '\0'; doc = stats_get_xml (0, reference); free (reference); free (fullpath); return admin_send_response (doc, httpclient, XSLT, "xspf.xsl"); } /* on demand file serving check */ config = config_get_config(); if (config->fileserve == 0) { config_release_config(); DEBUG1 ("on demand file \"%s\" refused", fullpath); free (fullpath); return client_send_404 (httpclient, "The file you requested could not be found"); } config_release_config(); if (S_ISREG (file_buf.st_mode) == 0) { WARN1 ("found requested file but there is no handler for it: %s", fullpath); free (fullpath); return client_send_404 (httpclient, "The file you requested could not be found"); } free (fullpath); finfo.flags = 0; finfo.mount = (char *)path; finfo.fallback = NULL; finfo.limit = 0; finfo.type = FORMAT_TYPE_UNDEFINED; snprintf (fsize, 20, "%" PRId64, (int64_t)file_buf.st_size); httpp_setvar (httpclient->parser, "__FILESIZE", fsize); stats_event_inc (NULL, "file_connections"); return fserve_setup_client_fb (httpclient, &finfo); }
/* Called when activating a source. Verifies that the source count is not * exceeded and applies any initial parameters. */ int connection_complete_source (source_t *source, int response) { ice_config_t *config = config_get_config(); global_lock (); DEBUG1 ("sources count is %d", global.sources); if (global.sources < config->source_limit) { char *contenttype; mount_proxy *mountinfo; format_type_t format_type; /* setup format handler */ contenttype = httpp_getvar (source->parser, "content-type"); if (contenttype != NULL) { format_type = format_get_type (contenttype); if (format_type == FORMAT_ERROR) { global_unlock(); config_release_config(); if (response) { client_send_404 (source->client, "Content-type not supported"); source->client = NULL; } WARN1("Content-type \"%s\" not supported, dropping source", contenttype); return -1; } } else { WARN0("No content-type header, falling back to backwards compatibility mode " "for icecast 1.x relays. Assuming content is mp3."); format_type = FORMAT_TYPE_GENERIC; } if (format_get_plugin (format_type, source) < 0) { global_unlock(); config_release_config(); if (response) { client_send_404 (source->client, "internal format allocation problem"); source->client = NULL; } WARN1 ("plugin format failed for \"%s\"", source->mount); return -1; } global.sources++; stats_event_args (NULL, "sources", "%d", global.sources); global_unlock(); source->running = 1; mountinfo = config_find_mount (config, source->mount); if (mountinfo == NULL) source_update_settings (config, source, mountinfo); source_recheck_mounts (); config_release_config(); source->shutdown_rwlock = &_source_shutdown_rwlock; DEBUG0 ("source is ready to start"); return 0; } WARN1("Request to add source when maximum source limit " "reached %d", global.sources); global_unlock(); config_release_config(); if (response) { client_send_404 (source->client, "too many sources connected"); source->client = NULL; } return -1; }
int xslt_transform (xmlDocPtr doc, const char *xslfilename, client_t *client) { xmlDocPtr res; xsltStylesheetPtr cur; int len; refbuf_t *content = NULL; char **params = 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); ERROR1 ("problem reading stylesheet \"%s\"", xslfilename); return client_send_404 (client, "Could not parse XSLT file"); } if (client->parser->queryvars) { // annoying but we need to surround the args with ' when passing them in int i, arg_count = client->parser->queryvars->length * 2; avl_node *node = avl_get_first (client->parser->queryvars); params = calloc (arg_count+1, sizeof (char *)); for (i = 0; node && i < arg_count; node = avl_get_next (node)) { http_var_t *param = (http_var_t *)node->key; char *tmp = util_url_escape (param->value); params[i++] = param->name; // use alloca for now, should really url esc into a supplied buffer params[i] = (char*)alloca (strlen (tmp) + 3); sprintf (params[i++], "\'%s\'", tmp); free (tmp); } params[i] = NULL; } res = xsltApplyStylesheet (cur, doc, (const char **)params); free (params); if (res == NULL || xslt_SaveResultToBuf (&content, &len, res, cur) < 0) { thread_mutex_unlock (&xsltlock); xmlFreeDoc (res); WARN1 ("problem applying stylesheet \"%s\"", xslfilename); return client_send_404 (client, "XSLT problem"); } else { /* the 100 is to allow for the hardcoded headers */ refbuf_t *refbuf = refbuf_new (100); const char *mediatype = NULL; /* lets find out the content type to use */ 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"; } snprintf (refbuf->data, 100, "HTTP/1.0 200 OK\r\nContent-Type: %s\r\nContent-Length: %d\r\n\r\n", mediatype, len); thread_mutex_unlock (&xsltlock); client->respcode = 200; client_set_queue (client, NULL); client->refbuf = refbuf; refbuf->len = strlen (refbuf->data); refbuf->next = content; } xmlFreeDoc(res); return fserve_setup_client (client); }
/* Called when activating a source. Verifies that the source count is not * exceeded and applies any initial parameters. */ int connection_complete_source (source_t *source) { ice_config_t *config = config_get_config(); global_lock (); DEBUG1 ("sources count is %d", global.sources); if (global.sources < config->source_limit) { char *contenttype; mount_proxy *mountproxy = config->mounts; format_type_t format_type; /* setup format handler */ contenttype = httpp_getvar (source->parser, "content-type"); if (contenttype != NULL) { format_type = format_get_type (contenttype); if (format_type == FORMAT_ERROR) { global_unlock(); config_release_config(); if (source->client) client_send_404 (source->client, "Content-type not supported"); WARN1("Content-type \"%s\" not supported, dropping source", contenttype); return -1; } } else { WARN0("No content-type header, falling back to backwards compatibility mode " "for icecast 1.x relays. Assuming content is mp3."); format_type = FORMAT_TYPE_MP3; } if (format_get_plugin (format_type, source) < 0) { global_unlock(); config_release_config(); if (source->client) client_send_404 (source->client, "internal format allocation problem"); WARN1 ("plugin format failed for \"%s\"", source->mount); return -1; } global.sources++; global_unlock(); /* set global settings first */ source->queue_size_limit = config->queue_size_limit; source->timeout = config->source_timeout; source->burst_size = config->burst_size; /* for relays, we don't yet have a client, however we do require one * to retrieve the stream from. This is created here, quite late, * because we can't use this client to return an error code/message, * so we only do this once we know we're going to accept the source. */ if (source->client == NULL) source->client = client_create (source->con, source->parser); while (mountproxy) { if (strcmp (mountproxy->mountname, source->mount) == 0) { source_apply_mount (source, mountproxy); break; } mountproxy = mountproxy->next; } config_release_config(); source->shutdown_rwlock = &_source_shutdown_rwlock; DEBUG0 ("source is ready to start"); return 0; } WARN1("Request to add source when maximum source limit " "reached %d", global.sources); global_unlock(); config_release_config(); if (source->client) client_send_404 (source->client, "too many sources connected"); return -1; }