diff --git a/src/admin.c b/src/admin.c index 406c4035..2fbaeb33 100644 --- a/src/admin.c +++ b/src/admin.c @@ -269,14 +269,19 @@ void admin_send_response (xmlDocPtr doc, client_t *client, xmlChar *buff = NULL; int len = 0; unsigned int buf_len; - const char *http = "HTTP/1.0 200 OK\r\n" - "Content-Type: text/xml\r\n" - "Content-Length: "; xmlDocDumpMemory(doc, &buff, &len); - buf_len = strlen (http) + len + 20; + buf_len = len + 256 /* just a random medium number */; + 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, buff); + + /* FIXME: in this section we hope no function will ever return -1 */ + len = util_http_build_header(client->refbuf->data, buf_len, 0, + 0, 200, NULL, + "text/xml", NULL, + NULL); + len += snprintf (client->refbuf->data + len, buf_len - len, "Content-Length: %d\r\n\r\n%s", len, buff); + client->refbuf->len = len; xmlFree(buff); client->respcode = 200; @@ -563,11 +568,17 @@ static void admin_handle_mount_request(client_t *client, source_t *source, static void html_success(client_t *client, char *message) { + ssize_t ret; + + ret = util_http_build_header(client->refbuf->data, PER_CLIENT_REFBUF_SIZE, 0, + 0, 200, NULL, + "text/html", NULL, + ""); + snprintf(client->refbuf->data + ret, PER_CLIENT_REFBUF_SIZE - ret, + "Admin request successful" + "

%s

", message); + client->respcode = 200; - snprintf (client->refbuf->data, PER_CLIENT_REFBUF_SIZE, - "HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n" - "Admin request successful" - "

%s

", message); client->refbuf->len = strlen (client->refbuf->data); fserve_add_client (client, NULL); } @@ -693,15 +704,18 @@ static void command_buildm3u(client_t *client, const char *mount) const char *username = NULL; const char *password = NULL; ice_config_t *config; + ssize_t ret; COMMAND_REQUIRE(client, "username", username); COMMAND_REQUIRE(client, "password", password); - client->respcode = 200; + ret = util_http_build_header(client->refbuf->data, PER_CLIENT_REFBUF_SIZE, 0, + 0, 200, NULL, + "audio/x-mpegurl", NULL, + NULL); + config = config_get_config(); - snprintf (client->refbuf->data, PER_CLIENT_REFBUF_SIZE, - "HTTP/1.0 200 OK\r\n" - "Content-Type: audio/x-mpegurl\r\n" + snprintf (client->refbuf->data + ret, PER_CLIENT_REFBUF_SIZE - ret, "Content-Disposition = attachment; filename=listen.m3u\r\n\r\n" "http://%s:%s@%s:%d%s\r\n", username, @@ -712,6 +726,7 @@ static void command_buildm3u(client_t *client, const char *mount) ); config_release_config(); + client->respcode = 200; client->refbuf->len = strlen (client->refbuf->data); fserve_add_client (client, NULL); } @@ -1014,8 +1029,10 @@ static void command_list_mounts(client_t *client, int response) if (response == PLAINTEXT) { - snprintf (client->refbuf->data, PER_CLIENT_REFBUF_SIZE, - "HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n"); + util_http_build_header(client->refbuf->data, PER_CLIENT_REFBUF_SIZE, 0, + 0, 200, NULL, + "text/html", NULL, + ""); client->refbuf->len = strlen (client->refbuf->data); client->respcode = 200; diff --git a/src/client.c b/src/client.c index f482c6b5..31752b6f 100644 --- a/src/client.c +++ b/src/client.c @@ -182,21 +182,32 @@ int client_read_bytes (client_t *client, void *buf, unsigned len) void client_send_400(client_t *client, char *message) { - snprintf (client->refbuf->data, PER_CLIENT_REFBUF_SIZE, - "HTTP/1.0 400 Bad Request\r\n" - "Content-Type: text/html\r\n\r\n" - "%s\r\n", message); + ssize_t ret; + + ret = util_http_build_header(client->refbuf->data, PER_CLIENT_REFBUF_SIZE, 0, + 0, 400, NULL, + "text/html", NULL, + ""); + + snprintf(client->refbuf->data + ret, PER_CLIENT_REFBUF_SIZE - ret, + "%s\r\n", message); + client->respcode = 400; client->refbuf->len = strlen (client->refbuf->data); fserve_add_client (client, NULL); } void client_send_404(client_t *client, char *message) { + ssize_t ret; + + ret = util_http_build_header(client->refbuf->data, PER_CLIENT_REFBUF_SIZE, 0, + 0, 404, NULL, + "text/html", NULL, + ""); + + snprintf(client->refbuf->data + ret, PER_CLIENT_REFBUF_SIZE - ret, + "%s\r\n", message); - snprintf (client->refbuf->data, PER_CLIENT_REFBUF_SIZE, - "HTTP/1.0 404 File Not Found\r\n" - "Content-Type: text/html\r\n\r\n" - "%s\r\n", message); client->respcode = 404; client->refbuf->len = strlen (client->refbuf->data); fserve_add_client (client, NULL); @@ -204,11 +215,10 @@ void client_send_404(client_t *client, char *message) { void client_send_401(client_t *client) { - snprintf (client->refbuf->data, PER_CLIENT_REFBUF_SIZE, - "HTTP/1.0 401 Authentication Required\r\n" - "WWW-Authenticate: Basic realm=\"Icecast2 Server\"\r\n" - "\r\n" - "You need to authenticate\r\n"); + util_http_build_header(client->refbuf->data, PER_CLIENT_REFBUF_SIZE, 0, + 0, 401, NULL, + "text/plain", NULL, + "You need to authenticate\r\n"); client->respcode = 401; client->refbuf->len = strlen (client->refbuf->data); fserve_add_client (client, NULL); @@ -216,10 +226,10 @@ void client_send_401(client_t *client) { void client_send_403(client_t *client, const char *reason) { - if (reason == NULL) - reason = "Forbidden"; - snprintf (client->refbuf->data, PER_CLIENT_REFBUF_SIZE, - "HTTP/1.0 403 %s\r\n\r\n", reason); + util_http_build_header(client->refbuf->data, PER_CLIENT_REFBUF_SIZE, 0, + 0, 403, reason, + "text/plain", NULL, + "Forbidden"); client->respcode = 403; client->refbuf->len = strlen (client->refbuf->data); fserve_add_client (client, NULL); diff --git a/src/format.c b/src/format.c index cb408684..344a89d8 100644 --- a/src/format.c +++ b/src/format.c @@ -298,8 +298,7 @@ static int format_prepare_headers (source_t *source, client_t *client) ptr = client->refbuf->data; client->respcode = 200; - bytes = snprintf (ptr, remaining, "HTTP/1.0 200 OK\r\n" - "Content-Type: %s\r\n", source->format->contenttype); + bytes = util_http_build_header (ptr, remaining, 0, 0, 200, NULL, source->format->contenttype, NULL, NULL); remaining -= bytes; ptr += bytes; @@ -362,17 +361,6 @@ static int format_prepare_headers (source_t *source, client_t *client) } avl_tree_unlock(source->parser->vars); - config = config_get_config(); - bytes = snprintf (ptr, remaining, "Server: %s\r\n", config->server_id); - config_release_config(); - remaining -= bytes; - ptr += bytes; - - /* prevent proxy servers from caching */ - bytes = snprintf (ptr, remaining, "Cache-Control: no-cache\r\n"); - remaining -= bytes; - ptr += bytes; - bytes = snprintf (ptr, remaining, "\r\n"); remaining -= bytes; ptr += bytes; diff --git a/src/fserve.c b/src/fserve.c index a1632bc9..3d670e90 100644 --- a/src/fserve.c +++ b/src/fserve.c @@ -454,12 +454,13 @@ int fserve_client_create (client_t *httpclient, const char *path) *dot = 0; httpclient->respcode = 200; + ret = util_http_build_header (httpclient->refbuf->data, BUFSIZE, 0, + 0, 200, NULL, + "audio/x-mpegurl", NULL, ""); 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" + config = config_get_config(); + snprintf (httpclient->refbuf->data + ret, BUFSIZE - ret, "http://%s:%d%s\r\n", config->hostname, config->port, sourceuri @@ -468,9 +469,7 @@ int fserve_client_create (client_t *httpclient, const char *path) } else { - snprintf (httpclient->refbuf->data, BUFSIZE, - "HTTP/1.0 200 OK\r\n" - "Content-Type: audio/x-mpegurl\r\n\r\n" + snprintf (httpclient->refbuf->data + ret, BUFSIZE - ret, "http://%s%s\r\n", host, sourceuri @@ -555,36 +554,27 @@ int fserve_client_create (client_t *httpclient, const char *path) 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" + bytes = util_http_build_header (httpclient->refbuf->data, BUFSIZE, 0, + 0, 206, NULL, + type, NULL, + NULL); + 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" - "Content-Type: %s\r\n\r\n", - currenttime, + "-%" PRI_OFF_T "/%" PRI_OFF_T "\r\n\r\n", new_content_len, rangenumber, endpos, - content_length, - type); + content_length); free (type); } else { @@ -598,13 +588,14 @@ int fserve_client_create (client_t *httpclient, const char *path) else { char *type = fserve_content_type(path); httpclient->respcode = 200; - bytes = snprintf (httpclient->refbuf->data, BUFSIZE, - "HTTP/1.0 200 OK\r\n" + bytes = util_http_build_header (httpclient->refbuf->data, BUFSIZE, 0, + 0, 200, NULL, + type, NULL, + NULL); + bytes += snprintf (httpclient->refbuf->data + bytes, BUFSIZE - bytes, "Accept-Ranges: bytes\r\n" - "Content-Length: %" PRI_OFF_T "\r\n" - "Content-Type: %s\r\n\r\n", - content_length, - type); + "Content-Length: %" PRI_OFF_T "\r\n\r\n", + content_length); free (type); } httpclient->refbuf->len = bytes; diff --git a/src/util.c b/src/util.c index 8f0ffd9f..c6f323d6 100644 --- a/src/util.c +++ b/src/util.c @@ -485,6 +485,85 @@ char *util_base64_decode(const char *data) return result; } +ssize_t util_http_build_header(char * out, size_t len, ssize_t offset, + int cache, + int status, const char * statusmsg, + const char * contenttype, const char * charset, + const char * datablock) { + const char * http_version = "1.0"; + ice_config_t *config; + time_t now; + struct tm result; + char currenttime_buffer[50]; + char status_buffer[80]; + char contenttype_buffer[80]; + ssize_t ret; + + if (!out) + return -1; + + if (offset == -1) + offset = strlen (out); + + out += offset; + len -= offset; + + if (status == -1) + { + status_buffer[0] = '\0'; + } + else + { + if (!statusmsg) + { + switch (status) + { + case 200: statusmsg = "OK"; break; + case 206: statusmsg = "Partial Content"; http_version = "1.1"; break; + case 400: statusmsg = "Bad Request"; break; + case 401: statusmsg = "Authentication Required"; break; + case 403: statusmsg = "Forbidden"; break; + case 404: statusmsg = "File Not Found"; break; + case 416: statusmsg = "Request Range Not Satisfiable"; break; + default: statusmsg = "(unknown status code)"; break; + } + } + snprintf (status_buffer, sizeof (status_buffer), "HTTP/%s %d %s\r\n", http_version, status, statusmsg); + } + + if (contenttype) + { + if (charset) + snprintf (contenttype_buffer, sizeof (contenttype_buffer), "Content-Type: %s; charset=%s\r\n", + contenttype, charset); + else + snprintf (contenttype_buffer, sizeof (contenttype_buffer), "Content-Type: %s\r\n", + contenttype); + } + else + { + contenttype_buffer[0] = '\0'; + } + + time(&now); + strftime(currenttime_buffer, 50, "%a, %d-%b-%Y %X GMT", gmtime_r(&now, &result)); + + config = config_get_config(); + ret = snprintf (out, len, "%sServer: %s\r\nDate: %s\r\n%s%s%s%s%s", + status_buffer, + config->server_id, + currenttime_buffer, + contenttype_buffer, + (status == 401 ? "WWW-Authenticate: Basic realm=\"Icecast2 Server\"\r\n" : ""), + (cache ? "" : "Cache-Control: no-cache\r\n"), + (datablock ? "\r\n" : ""), + (datablock ? datablock : "")); + config_release_config(); + + return ret; +} + + util_dict *util_dict_new(void) { return (util_dict *)calloc(1, sizeof(util_dict)); diff --git a/src/util.h b/src/util.h index b6674b9d..3e73356b 100644 --- a/src/util.h +++ b/src/util.h @@ -35,6 +35,33 @@ char *util_bin_to_hex(unsigned char *data, int len); char *util_url_unescape(const char *src); char *util_url_escape(const char *src); +/* Function to build up a HTTP header. + * out is the pointer to storage. + * len is the total length of out. + * offset is the offset in out we should start writing at if offset >= 0. + * In this case the 'used' length is len-offset. + * If offset is -1 we append at the end of out. + * In this case the used length is len-strlen(out). + * cache is a bool. If set allow the client to cache the content (static data). + * if unset we will send no-caching headers. + * status is the HTTP status code. If -1 no HTTP status header is generated. + * statusmsg is the status header text. If NULL a default one + * is used for the given status code. This is ignored if status is -1. + * contenttype is the value for the Content-Type header. + * if NULL no such header is generated. + * charset is the charset for the object. If NULL no header is generated + * or default is used. + * datablock is random data added to the request. + * If datablock is non NULL the end-of-header is appended as well as this datablock. + * If datablock is NULL no end-of-header nor any data is appended. + * Returns the number of bytes written or -1 on error. + */ +ssize_t util_http_build_header(char * out, size_t len, ssize_t offset, + int cache, + int status, const char * statusmsg, + const char * contenttype, const char * charset, + const char * datablock); + /* String dictionary type, without support for NULL keys, or multiple * instances of the same key */ typedef struct _util_dict { diff --git a/src/xslt.c b/src/xslt.c index 3b08ac6e..839ad35e 100644 --- a/src/xslt.c +++ b/src/xslt.c @@ -227,14 +227,16 @@ void xslt_transform(xmlDocPtr doc, const char *xslfilename, client_t *client) if (problem == 0) { /* the 100 is to allow for the hardcoded headers */ - unsigned int full_len = strlen (mediatype) + len + 100; + unsigned int full_len = strlen (mediatype) + len + 256; refbuf_t *refbuf = refbuf_new (full_len); + ssize_t ret; if (string == NULL) string = xmlCharStrdup (""); - snprintf (refbuf->data, full_len, - "HTTP/1.0 200 OK\r\nContent-Type: %s\r\nContent-Length: %d\r\n\r\n%s", - mediatype, len, string); + ret = util_http_build_header(refbuf->data, full_len, 0, 0, 200, NULL, mediatype, NULL, 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);