From 7fadb6ce8650afd932543caa7035e3af87cf06f1 Mon Sep 17 00:00:00 2001 From: Philipp Schafft Date: Wed, 27 Jun 2018 12:45:27 +0000 Subject: [PATCH] Feature: Added function to send XML Report to client --- src/client.c | 148 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/client.h | 8 +++ 2 files changed, 156 insertions(+) diff --git a/src/client.c b/src/client.c index 6e5f0e67..9f694518 100644 --- a/src/client.c +++ b/src/client.c @@ -38,6 +38,9 @@ #include "stats.h" #include "fserve.h" #include "errors.h" +#include "reportxml.h" +#include "refobject.h" +#include "xslt.h" #include "client.h" #include "auth.h" @@ -409,6 +412,151 @@ static inline void client_send_500(client_t *client, const char *message) client_destroy(client); } +/* this function sends a reportxml file to the client in the prefered format. */ +void client_send_reportxml(client_t *client, reportxml_t *report, document_domain_t domain, const char *xsl, admin_format_t admin_format_hint, int status) +{ + admin_format_t admin_format; + xmlDocPtr doc; + + if (!client) + return; + + if (!report) { + ICECAST_LOG_ERROR("No report xml given. Sending 500 to client %p", client); + client_send_500(client, "No report."); + return; + } + + if (!status) + status = 200; + + if (admin_format_hint == ADMIN_FORMAT_AUTO) { + admin_format = client_get_admin_format_by_content_negotiation(client); + } else { + admin_format = admin_format_hint; + } + + if (!xsl) { + switch (admin_format) { + case ADMIN_FORMAT_RAW: + /* noop, we don't need to set xsl */ + break; + case ADMIN_FORMAT_TRANSFORMED: + xsl = CLIENT_DEFAULT_REPORT_XSL_TRANSFORMED; + break; + case ADMIN_FORMAT_PLAINTEXT: + xsl = CLIENT_DEFAULT_REPORT_XSL_PLAINTEXT; + break; + default: + ICECAST_LOG_ERROR("Unsupported admin format and no XSLT file given. Sending 500 to client %p", client); + client_send_500(client, "Unsupported admin format."); + return; + break; + } + } else if (admin_format_hint == ADMIN_FORMAT_AUTO) { + ICECAST_LOG_ERROR("No explicit admin format but XSLT file given. BUG. Sending 500 to client %p", client); + client_send_500(client, "Admin type AUTO but XSLT."); + return; + } + + doc = reportxml_render_xmldoc(report); + if (!doc) { + ICECAST_LOG_ERROR("Can not render XML Document from report. Sending 500 to client %p", client); + client_send_500(client, "Can not render XML Document from report."); + return; + } + + if (admin_format == ADMIN_FORMAT_RAW) { + xmlChar *buff = NULL; + int len = 0; + size_t buf_len; + ssize_t ret; + + xmlDocDumpMemory(doc, &buff, &len); + + buf_len = len + 1024; + if (buf_len < 4096) + buf_len = 4096; + + client_set_queue(client, NULL); + client->refbuf = refbuf_new(buf_len); + + ret = util_http_build_header(client->refbuf->data, buf_len, 0, + 0, status, NULL, + "text/xml", "utf-8", + NULL, NULL, client); + if (ret < 0) { + ICECAST_LOG_ERROR("Dropping client as we can not build response headers."); + client_send_error_by_id(client, ICECAST_ERROR_GEN_HEADER_GEN_FAILED); + xmlFree(buff); + return; + } else if (buf_len < (size_t)(len + ret + 64)) { + void *new_data; + buf_len = ret + len + 64; + new_data = realloc(client->refbuf->data, buf_len); + if (new_data) { + ICECAST_LOG_DEBUG("Client buffer reallocation succeeded."); + client->refbuf->data = new_data; + client->refbuf->len = buf_len; + ret = util_http_build_header(client->refbuf->data, buf_len, 0, + 0, status, NULL, + "text/xml", "utf-8", + NULL, NULL, client); + if (ret == -1) { + ICECAST_LOG_ERROR("Dropping client as we can not build response headers."); + client_send_error_by_id(client, ICECAST_ERROR_GEN_HEADER_GEN_FAILED); + xmlFree(buff); + return; + } + } else { + ICECAST_LOG_ERROR("Client buffer reallocation failed. Dropping client."); + client_send_error_by_id(client, ICECAST_ERROR_GEN_BUFFER_REALLOC); + xmlFree(buff); + return; + } + } + + /* FIXME: in this section we hope no function will ever return -1 */ + ret += snprintf (client->refbuf->data + ret, buf_len - ret, "Content-Length: %d\r\n\r\n%s", xmlStrlen(buff), buff); + + client->refbuf->len = ret; + xmlFree(buff); + client->respcode = status; + fserve_add_client (client, NULL); + } else { + char *fullpath_xslt_template; + const char *document_domain_path; + ssize_t fullpath_xslt_template_len; + ice_config_t *config; + + config = config_get_config(); + switch (domain) { + case DOCUMENT_DOMAIN_WEB: + document_domain_path = config->webroot_dir; + break; + case DOCUMENT_DOMAIN_ADMIN: + document_domain_path = config->adminroot_dir; + break; + default: + config_release_config(); + ICECAST_LOG_ERROR("Invalid document domain. Sending 500 to client %p", client); + client_send_500(client, "Invalid document domain."); + return; + break; + } + fullpath_xslt_template_len = strlen(document_domain_path) + strlen(xsl) + strlen(PATH_SEPARATOR) + 1; + fullpath_xslt_template = malloc(fullpath_xslt_template_len); + snprintf(fullpath_xslt_template, fullpath_xslt_template_len, "%s%s%s", document_domain_path, PATH_SEPARATOR, xsl); + config_release_config(); + + ICECAST_LOG_DEBUG("Sending XSLT (%s)", fullpath_xslt_template); + xslt_transform(doc, fullpath_xslt_template, client, status); + free(fullpath_xslt_template); + } + + xmlFreeDoc(doc); +} + admin_format_t client_get_admin_format_by_content_negotiation(client_t *client) { const char *pref; diff --git a/src/client.h b/src/client.h index f097fa67..08757304 100644 --- a/src/client.h +++ b/src/client.h @@ -27,8 +27,15 @@ #include "refbuf.h" #include "module.h" +#define CLIENT_DEFAULT_REPORT_XSL_TRANSFORMED "report-html.xsl" +#define CLIENT_DEFAULT_REPORT_XSL_PLAINTEXT "report-plaintext.xsl" #define CLIENT_DEFAULT_ADMIN_FORMAT ADMIN_FORMAT_TRANSFORMED +typedef enum _document_domain_tag { + DOCUMENT_DOMAIN_WEB, + DOCUMENT_DOMAIN_ADMIN +} document_domain_t; + typedef enum _protocol_tag { ICECAST_PROTOCOL_HTTP = 0, ICECAST_PROTOCOL_SHOUTCAST @@ -133,6 +140,7 @@ void client_send_error_by_id(client_t *client, icecast_error_id_t id); void client_send_101(client_t *client, reuse_t reuse); void client_send_204(client_t *client); void client_send_426(client_t *client, reuse_t reuse); +void client_send_reportxml(client_t *client, reportxml_t *report, document_domain_t domain, const char *xsl, admin_format_t admin_format_hint, int status); admin_format_t client_get_admin_format_by_content_negotiation(client_t *client); int client_send_bytes (client_t *client, const void *buf, unsigned len); int client_read_bytes (client_t *client, void *buf, unsigned len);