diff --git a/admin/error-html.xsl b/admin/error-html.xsl
new file mode 100644
index 00000000..3d65d5d5
--- /dev/null
+++ b/admin/error-html.xsl
@@ -0,0 +1,20 @@
+
+
+
+ Error
+
+
+
+
+
+
Response
+
Message
+
+
+ Error code:
+
+
+
+
+
+
diff --git a/admin/error-plaintext.xsl b/admin/error-plaintext.xsl
new file mode 100644
index 00000000..5640baf8
--- /dev/null
+++ b/admin/error-plaintext.xsl
@@ -0,0 +1,15 @@
+
+
+
+
+ Report:
+
+
+
+ Error code:
+
+
+
+
+
+
diff --git a/src/Makefile.am b/src/Makefile.am
index fae29c14..0f4255ee 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -31,6 +31,7 @@ noinst_HEADERS = \
tls.h \
refobject.h \
module.h \
+ reportxml.h \
event.h \
event_log.h \
event_exec.h \
@@ -72,6 +73,7 @@ icecast_SOURCES = \
tls.c \
refobject.c \
module.c \
+ reportxml.c \
format.c \
format_ogg.c \
format_mp3.c \
diff --git a/src/admin.c b/src/admin.c
index 73a3b951..6382ee3d 100644
--- a/src/admin.c
+++ b/src/admin.c
@@ -463,7 +463,7 @@ void admin_send_response(xmlDocPtr doc,
config_release_config();
ICECAST_LOG_DEBUG("Sending XSLT (%s)", fullpath_xslt_template);
- xslt_transform(doc, fullpath_xslt_template, client);
+ xslt_transform(doc, fullpath_xslt_template, client, 200);
free(fullpath_xslt_template);
}
}
diff --git a/src/cfgfile.c b/src/cfgfile.c
index 2dd2e547..dc1feb43 100644
--- a/src/cfgfile.c
+++ b/src/cfgfile.c
@@ -34,6 +34,8 @@
#include "util.h"
#include "auth.h"
#include "event.h"
+#include "refobject.h"
+#include "reportxml.h"
/* for config_reread_config() */
#include "yp.h"
@@ -199,6 +201,7 @@ void config_init_configuration(ice_config_t *configuration)
{
memset(configuration, 0, sizeof(ice_config_t));
_set_defaults(configuration);
+ configuration->reportxml_db = reportxml_database_new();
}
static inline void __read_int(xmlDocPtr doc, xmlNodePtr node, int *val, const char *warning)
@@ -668,6 +671,9 @@ void config_clear(ice_config_t *c)
#endif
config_clear_http_header(c->http_headers);
+
+ refobject_unref(c->reportxml_db);
+
memset(c, 0, sizeof(ice_config_t));
}
@@ -2076,6 +2082,28 @@ static void _parse_paths(xmlDocPtr doc,
configuration->adminroot_dir = (char *)temp;
if (configuration->adminroot_dir[strlen(configuration->adminroot_dir)-1] == '/')
configuration->adminroot_dir[strlen(configuration->adminroot_dir)-1] = 0;
+ } else if (xmlStrcmp(node->name, XMLSTR("reportxmldb")) == 0) {
+ reportxml_t *report;
+ xmlDocPtr dbdoc;
+
+ if (!(temp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1))) {
+ ICECAST_LOG_WARN(" setting must not be empty.");
+ continue;
+ }
+ dbdoc = xmlParseFile(temp);
+ if (!doc) {
+ ICECAST_LOG_ERROR("Can not read report xml database \"%H\" as XML", temp);
+ } else {
+ report = reportxml_parse_xmldoc(dbdoc);
+ xmlFreeDoc(dbdoc);
+ if (!report) {
+ ICECAST_LOG_ERROR("Can not parse report xml database \"%H\"", temp);
+ } else {
+ reportxml_database_add_report(configuration->reportxml_db, report);
+ ICECAST_LOG_INFO("File \"%H\" added to report xml database", temp);
+ }
+ }
+ xmlFree(temp);
} else if (xmlStrcmp(node->name, XMLSTR("resource")) == 0 || xmlStrcmp(node->name, XMLSTR("alias")) == 0) {
_parse_resource(doc, node, configuration);
}
diff --git a/src/cfgfile.h b/src/cfgfile.h
index 55ab1d98..66117a9d 100644
--- a/src/cfgfile.h
+++ b/src/cfgfile.h
@@ -223,6 +223,7 @@ struct ice_config_tag {
char *webroot_dir;
char *adminroot_dir;
resource_t *resources;
+ reportxml_database_t *reportxml_db;
char *access_log;
char *error_log;
diff --git a/src/client.c b/src/client.c
index 6e5f0e67..ffc80d39 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"
@@ -236,79 +239,73 @@ int client_read_bytes(client_t *client, void *buf, unsigned len)
return bytes;
}
-static inline void _client_send_error(client_t *client, int plain, const icecast_error_t *error)
+static inline void _client_send_error(client_t *client, const icecast_error_t *error)
{
- ssize_t ret;
- refbuf_t *data;
+ ice_config_t *config;
+ reportxml_t *report;
+ admin_format_t admin_format;
+ const char *xslt = NULL;
- if (error->http_status == 500) {
- client_send_500(client, error->message);
- return;
+ admin_format = client_get_admin_format_by_content_negotiation(client);
+
+ switch (admin_format) {
+ case ADMIN_FORMAT_RAW:
+ xslt = NULL;
+ break;
+ case ADMIN_FORMAT_TRANSFORMED:
+ xslt = CLIENT_DEFAULT_ERROR_XSL_TRANSFORMED;
+ break;
+ case ADMIN_FORMAT_PLAINTEXT:
+ xslt = CLIENT_DEFAULT_ERROR_XSL_PLAINTEXT;
+ break;
+ default:
+ client_send_500(client, "Invalid Admin Type");
+ break;
}
- data = refbuf_new(PER_CLIENT_REFBUF_SIZE);
- if (!data) {
- client_send_500(client, error->message);
- return;
+
+ config = config_get_config();
+ report = reportxml_database_build_report(config->reportxml_db, error->uuid, -1);
+ config_release_config();
+
+ if (!report) {
+ reportxml_node_t *root, *incident, *state, *text;
+
+ report = reportxml_new();
+ root = reportxml_get_root_node(report);
+ incident = reportxml_node_new(REPORTXML_NODE_TYPE_INCIDENT, NULL, NULL, NULL);
+ state = reportxml_node_new(REPORTXML_NODE_TYPE_STATE, NULL, error->uuid, NULL);
+ text = reportxml_node_new(REPORTXML_NODE_TYPE_TEXT, NULL, NULL, NULL);
+ reportxml_node_set_content(text, error->message);
+ reportxml_node_add_child(state, text);
+ reportxml_node_add_child(incident, state);
+ reportxml_node_add_child(root, incident);
+ refobject_unref(text);
+ refobject_unref(state);
+ refobject_unref(incident);
+ refobject_unref(root);
}
- client->reuse = ICECAST_REUSE_KEEPALIVE;
+ client_send_reportxml(client, report, DOCUMENT_DOMAIN_ADMIN, xslt, admin_format, error->http_status);
- ret = util_http_build_header(client->refbuf->data, PER_CLIENT_REFBUF_SIZE, 0,
- 0, error->http_status, NULL,
- plain ? "text/plain" : "text/html", "utf-8",
- NULL, NULL, client);
-
- 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(data->data, data->len, "Error %i\r\n---------\r\n\r\nMessage: %s\r\n\r\nError code: %s\r\n",
- error->http_status, error->message, error->uuid
- );
- } else {
- snprintf(data->data, data->len,
- "Error %iError %i
%s
Error code: %s
\r\n",
- error->http_status, error->http_status, error->message, error->uuid);
- }
- data->len = strlen(data->data);
-
- snprintf(client->refbuf->data + ret, PER_CLIENT_REFBUF_SIZE - ret,
- "Content-Length: %llu\r\n\r\n",
- (long long unsigned int)data->len);
-
- client->respcode = error->http_status;
- client->refbuf->len = strlen (client->refbuf->data);
- client->refbuf->next = data;
-
- fserve_add_client (client, NULL);
+ refobject_unref(report);
}
void client_send_error_by_id(client_t *client, icecast_error_id_t id)
{
const icecast_error_t *error = error_get_by_id(id);
- const char *pref;
- int plain;
if (!error) {
client_send_500(client, "Unknown error ID");
return;
}
- pref = util_http_select_best(httpp_getvar(client->parser, "accept"), "text/plain", "text/html", (const char*)NULL);
-
- if (strcmp(pref, "text/plain") == 0) {
- plain = 1;
- } else if (strcmp(pref, "text/html") == 0) {
- plain = 0;
- } else {
- plain = 1;
+ if (error->http_status == 500) {
+ client_send_500(client, error->message);
+ return;
}
- _client_send_error(client, plain, error);
+ _client_send_error(client, error);
}
void client_send_101(client_t *client, reuse_t reuse)
@@ -409,6 +406,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..f0aab70b 100644
--- a/src/client.h
+++ b/src/client.h
@@ -27,8 +27,17 @@
#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_ERROR_XSL_TRANSFORMED "error-html.xsl"
+#define CLIENT_DEFAULT_ERROR_XSL_PLAINTEXT "error-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 +142,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);
diff --git a/src/errors.c b/src/errors.c
index d570cb37..d553a523 100644
--- a/src/errors.c
+++ b/src/errors.c
@@ -132,7 +132,10 @@ static const icecast_error_t __errors[] = {
.message = "Could not parse XSLT file"},
{.id = ICECAST_ERROR_XSLT_problem, .http_status = 500,
.uuid = "d3c6e4b3-7d6e-4191-a81b-970273067ae3",
- .message = "XSLT problem"}
+ .message = "XSLT problem"},
+ {.id = ICECAST_ERROR_RECURSIVE_ERROR, .http_status = 500,
+ .uuid = "13489d5c-eae6-4bf3-889e-ec1fa9a9b9ac",
+ .message = "Recursive error"}
};
const icecast_error_t * error_get_by_id(icecast_error_id_t id) {
diff --git a/src/errors.h b/src/errors.h
index 0b14c028..babfed91 100644
--- a/src/errors.h
+++ b/src/errors.h
@@ -48,7 +48,8 @@ typedef enum {
ICECAST_ERROR_SOURCE_MOUNT_UNAVAILABLE,
ICECAST_ERROR_SOURCE_STREAM_PREPARATION_ERROR,
ICECAST_ERROR_XSLT_PARSE,
- ICECAST_ERROR_XSLT_problem
+ ICECAST_ERROR_XSLT_problem,
+ ICECAST_ERROR_RECURSIVE_ERROR
} icecast_error_id_t;
struct icecast_error_tag {
diff --git a/src/icecasttypes.h b/src/icecasttypes.h
index 36b198a4..ac3496f1 100644
--- a/src/icecasttypes.h
+++ b/src/icecasttypes.h
@@ -98,6 +98,12 @@ typedef struct module_tag module_t;
typedef struct module_container_tag module_container_t;
+/* ---[ reportxml.[ch] ]--- */
+
+typedef struct reportxml_tag reportxml_t;
+typedef struct reportxml_node_tag reportxml_node_t;
+typedef struct reportxml_database_tag reportxml_database_t;
+
/* ---[ refobject.[ch] ]--- */
typedef struct refobject_base_tag refobject_base_t;
@@ -107,6 +113,9 @@ typedef union __attribute__ ((__transparent_union__)) {
refobject_base_t *refobject_base;
module_t *module;
module_container_t *module_container;
+ reportxml_t *reportxml;
+ reportxml_node_t *reportxml_node;
+ reportxml_database_t *reportxml_database;
} refobject_t;
#else
typedef void * refobject_t;
diff --git a/src/reportxml.c b/src/reportxml.c
new file mode 100644
index 00000000..51d39fe3
--- /dev/null
+++ b/src/reportxml.c
@@ -0,0 +1,1316 @@
+/* Icecast
+ *
+ * This program is distributed under the GNU General Public License, version 2.
+ * A copy of this license is included with this source.
+ *
+ * Copyright 2018, Philipp "ph3-der-loewe" Schafft ,
+ */
+
+/**
+ * Special fast event functions
+ */
+
+#ifdef HAVE_CONFIG_H
+#include
+#endif
+
+#include
+
+#include "common/thread/thread.h"
+#include "common/avl/avl.h"
+
+#include "reportxml.h"
+#include "refobject.h"
+
+/* For XMLSTR() */
+#include "cfgfile.h"
+
+#include "logging.h"
+#define CATMODULE "reportxml"
+
+struct reportxml_tag {
+ refobject_base_t __base;
+ reportxml_node_t *root;
+};
+
+struct reportxml_node_tag {
+ refobject_base_t __base;
+ xmlNodePtr xmlnode;
+ reportxml_node_type_t type;
+ reportxml_node_t **childs;
+ size_t childs_len;
+ xmlNodePtr *xml_childs;
+ size_t xml_childs_len;
+ char *content;
+};
+
+struct reportxml_database_tag {
+ refobject_base_t __base;
+ mutex_t lock;
+ avl_tree *definitions;
+};
+
+struct nodeattr;
+
+struct nodeattr {
+ const char *name;
+ const char *type;
+ const char *def;
+ int required;
+ int (*checker)(const struct nodeattr *attr, const char *str);
+ const char *values[32];
+};
+
+enum nodecontent {
+ NC_NONE,
+ NC_CHILDS,
+ NC_CONTENT,
+ NC_XML
+};
+
+struct nodedef {
+ reportxml_node_type_t type;
+ const char *name;
+ enum nodecontent content;
+ const struct nodeattr *attr[12];
+ const reportxml_node_type_t childs[12];
+};
+
+static int __attach_copy_of_node_or_definition(reportxml_node_t *parent, reportxml_node_t *node, reportxml_database_t *db, ssize_t depth);
+static reportxml_node_t * __reportxml_database_build_node_ext(reportxml_database_t *db, const char *id, ssize_t depth, reportxml_node_type_t *acst_type_ret);
+
+static const struct nodeattr __attr__eol[1] = {{NULL, NULL, NULL, 0, NULL, {NULL}}};
+static const struct nodeattr __attr_version[1] = {{"version", "CDATA", "0.0.1", 1, NULL, {"0.0.1", NULL}}};
+static const struct nodeattr __attr_xmlns[1] = {{"xmlns", "URI", "http://icecast.org/specs/reportxml-0.0.1", 1, NULL, {"http://icecast.org/specs/reportxml-0.0.1", NULL}}};
+static const struct nodeattr __attr_id[1] = {{"id", "ID", NULL, 0, NULL, {NULL}}};
+static const struct nodeattr __attr_definition[1] = {{"definition", "UUID", NULL, 0, NULL, {NULL}}};
+static const struct nodeattr __attr__definition[1] = {{"_definition", "UUID", NULL, 0, NULL, {NULL}}}; /* for internal use only */
+static const struct nodeattr __attr_akindof[1] = {{"akindof", "UUIDs", NULL, 0, NULL, {NULL}}};
+static const struct nodeattr __attr_lang[1] = {{"lang", "LanguageCode", NULL, 0, NULL, {NULL}}};
+static const struct nodeattr __attr_dir[1] = {{"dir", NULL, NULL, 0, NULL, {"ltr", "rtl", NULL}}};
+static const struct nodeattr __attr_template[1] = {{"template", "UUID", NULL, 0, NULL, {NULL}}};
+static const struct nodeattr __attr_defines[1] = {{"defines", "UUID", NULL, 1, NULL, {NULL}}};
+static const struct nodeattr __attr_function[1] = {{"function", "CDATA", NULL, 0, NULL, {NULL}}};
+static const struct nodeattr __attr_filename[1] = {{"filename", "CDATA", NULL, 0, NULL, {NULL}}};
+static const struct nodeattr __attr_line[1] = {{"line", "CDATA", NULL, 0, NULL, {NULL}}};
+static const struct nodeattr __attr_binary[1] = {{"binary", "CDATA", NULL, 0, NULL, {NULL}}};
+static const struct nodeattr __attr_offset[1] = {{"offset", "CDATA", NULL, 0, NULL, {NULL}}};
+static const struct nodeattr __attr_absolute[1] = {{"absolute", "iso8601", NULL, 0, NULL, {NULL}}};
+static const struct nodeattr __attr_relative[1] = {{"relative", "iso8601", NULL, 0, NULL, {NULL}}};
+static const struct nodeattr __attr_name[1] = {{"name", "CDATA", NULL, 1, NULL, {NULL}}};
+static const struct nodeattr __attr_member[1] = {{"member", "CDATA", NULL, 0, NULL, {NULL}}};
+static const struct nodeattr __attr_value[1] = {{"value", "CDATA", NULL, 0, NULL, {NULL}}};
+static const struct nodeattr __attr_state[1] = {{"state", NULL, "set", 1, NULL, {"declared", "set", "uninitialized", "missing", NULL}}};
+static const struct nodeattr __attr_href[1] = {{"href", "URI", NULL, 0, NULL, {NULL}}};
+static const struct nodeattr __attr_application[1] = {{"application", "URI", NULL, 1, NULL, {NULL}}};
+static const struct nodeattr __attr__action_type[1] = {{"type", NULL, NULL, 1, NULL, {"retry", "choice", "see-other", "authenticate", "pay", "change-protocol", "slow-down", "ask-user", "ask-admin", "bug", NULL}}};
+static const struct nodeattr __attr__resource_type[1] = {{"type", NULL, NULL, 1, NULL, {"actor", "manipulation-target", "helper", "related", "result", "parameter", NULL}}};
+static const struct nodeattr __attr__value_type[1] = {{"type", NULL, NULL, 1, NULL, {"null", "int", "float", "uuid", "string", "structure", "uri", "pointer", "version", "protocol", "username", "password", NULL}}};
+static const struct nodeattr __attr__reference_type[1] = {{"type", NULL, NULL, 1, NULL, {"documentation", "log", "report", "related", NULL}}};
+
+/* Helper:
+ * grep '^ *REPORTXML_NODE_TYPE_' reportxml.h | sed 's/^\( *REPORTXML_NODE_TYPE_\([^,]*\)\),*$/\1 \2/;' | while read l s; do c=$(tr A-Z a-z <<<"$s"); printf " {%-32s \"%-16s 0, {__attr__eol}},\n" "$l," "$c\","; done
+ */
+#define __BASIC_ELEMENT __attr_id, __attr_definition, __attr_akindof, __attr__definition
+static const struct nodedef __nodedef[] = {
+ {REPORTXML_NODE_TYPE_REPORT, "report", NC_CHILDS, {__attr_id, __attr_version, __attr_xmlns, __attr__eol},
+ {REPORTXML_NODE_TYPE_INCIDENT, REPORTXML_NODE_TYPE_DEFINITION, REPORTXML_NODE_TYPE_TIMESTAMP, REPORTXML_NODE_TYPE_REFERENCE, REPORTXML_NODE_TYPE_EXTENSION, REPORTXML_NODE_TYPE__ERROR}},
+ {REPORTXML_NODE_TYPE_DEFINITION, "definition", NC_CHILDS, {__BASIC_ELEMENT, __attr_template, __attr_defines, __attr__eol},
+ {REPORTXML_NODE_TYPE_INCIDENT, REPORTXML_NODE_TYPE_STATE, REPORTXML_NODE_TYPE_TIMESTAMP, REPORTXML_NODE_TYPE_RESOURCE, REPORTXML_NODE_TYPE_REFERENCE, REPORTXML_NODE_TYPE_FIX, REPORTXML_NODE_TYPE_RESOURCE, REPORTXML_NODE_TYPE_REASON, REPORTXML_NODE_TYPE_TEXT, REPORTXML_NODE_TYPE_EXTENSION, REPORTXML_NODE_TYPE__ERROR}},
+ {REPORTXML_NODE_TYPE_INCIDENT, "incident", NC_CHILDS, {__BASIC_ELEMENT, __attr__eol},
+ {REPORTXML_NODE_TYPE_STATE, REPORTXML_NODE_TYPE_TIMESTAMP, REPORTXML_NODE_TYPE_RESOURCE, REPORTXML_NODE_TYPE_REFERENCE, REPORTXML_NODE_TYPE_FIX, REPORTXML_NODE_TYPE_REASON, REPORTXML_NODE_TYPE_EXTENSION, REPORTXML_NODE_TYPE__ERROR}},
+ {REPORTXML_NODE_TYPE_STATE, "state", NC_CHILDS, {__BASIC_ELEMENT, __attr__eol},
+ {REPORTXML_NODE_TYPE_TEXT, REPORTXML_NODE_TYPE_TIMESTAMP, REPORTXML_NODE_TYPE_BACKTRACE, REPORTXML_NODE_TYPE_EXTENSION, REPORTXML_NODE_TYPE__ERROR}},
+ {REPORTXML_NODE_TYPE_BACKTRACE, "backtrace", NC_CHILDS, {__BASIC_ELEMENT, __attr__eol},
+ {REPORTXML_NODE_TYPE_POSITION, REPORTXML_NODE_TYPE_MORE, REPORTXML_NODE_TYPE_TEXT, REPORTXML_NODE_TYPE_REFERENCE, REPORTXML_NODE_TYPE__ERROR}},
+ {REPORTXML_NODE_TYPE_POSITION, "position", NC_CHILDS, {__BASIC_ELEMENT, __attr_function, __attr_filename, __attr_line, __attr_binary, __attr_offset, __attr__eol},
+ {REPORTXML_NODE_TYPE_TEXT, REPORTXML_NODE_TYPE_REFERENCE, REPORTXML_NODE_TYPE_EXTENSION, REPORTXML_NODE_TYPE__ERROR}},
+ {REPORTXML_NODE_TYPE_MORE, "more", NC_CHILDS, {__BASIC_ELEMENT, __attr__eol},
+ {REPORTXML_NODE_TYPE_TEXT, REPORTXML_NODE_TYPE__ERROR}},
+ {REPORTXML_NODE_TYPE_FIX, "fix", NC_CHILDS, {__BASIC_ELEMENT, __attr__eol},
+ {REPORTXML_NODE_TYPE_ACTION, REPORTXML_NODE_TYPE_EXTENSION, REPORTXML_NODE_TYPE__ERROR}},
+ {REPORTXML_NODE_TYPE_ACTION, "action", NC_CHILDS, {__BASIC_ELEMENT, __attr__action_type, __attr__eol},
+ {REPORTXML_NODE_TYPE_TEXT, REPORTXML_NODE_TYPE_TIMESTAMP, REPORTXML_NODE_TYPE_VALUE, REPORTXML_NODE_TYPE_EXTENSION, REPORTXML_NODE_TYPE__ERROR}},
+ {REPORTXML_NODE_TYPE_REASON, "reason", NC_CHILDS, {__BASIC_ELEMENT, __attr__eol},
+ {REPORTXML_NODE_TYPE_TEXT, REPORTXML_NODE_TYPE_RESOURCE, REPORTXML_NODE_TYPE_REFERENCE, REPORTXML_NODE_TYPE_EXTENSION, REPORTXML_NODE_TYPE__ERROR}},
+ {REPORTXML_NODE_TYPE_TEXT, "text", NC_CONTENT, {__BASIC_ELEMENT, __attr_lang, __attr_dir, __attr__eol},
+ {REPORTXML_NODE_TYPE__ERROR}},
+ {REPORTXML_NODE_TYPE_TIMESTAMP, "timestamp", NC_NONE, {__BASIC_ELEMENT, __attr_absolute, __attr_relative, __attr__eol},
+ {REPORTXML_NODE_TYPE__ERROR}},
+ {REPORTXML_NODE_TYPE_RESOURCE, "resource", NC_CHILDS, {__BASIC_ELEMENT, __attr__resource_type, __attr_name, __attr__eol},
+ {REPORTXML_NODE_TYPE_VALUE, REPORTXML_NODE_TYPE_REFERENCE, REPORTXML_NODE_TYPE_EXTENSION, REPORTXML_NODE_TYPE__ERROR}},
+ {REPORTXML_NODE_TYPE_VALUE, "value", NC_CHILDS, {__BASIC_ELEMENT, __attr_member, __attr_value, __attr_state, __attr__value_type, __attr__eol},
+ {REPORTXML_NODE_TYPE_TEXT, REPORTXML_NODE_TYPE_REFERENCE, REPORTXML_NODE_TYPE_VALUE, REPORTXML_NODE_TYPE_POSITION, REPORTXML_NODE_TYPE_EXTENSION, REPORTXML_NODE_TYPE__ERROR}},
+ {REPORTXML_NODE_TYPE_REFERENCE, "reference", NC_CHILDS, {__BASIC_ELEMENT, __attr__reference_type, __attr_href, __attr__eol},
+ {REPORTXML_NODE_TYPE_TEXT, REPORTXML_NODE_TYPE_EXTENSION, REPORTXML_NODE_TYPE__ERROR}},
+ {REPORTXML_NODE_TYPE_EXTENSION, "extension", NC_XML, {__BASIC_ELEMENT, __attr_application, __attr__eol},
+ {REPORTXML_NODE_TYPE__ERROR}}
+};
+
+static const struct nodedef * __get_nodedef(reportxml_node_type_t type)
+{
+ size_t i;
+
+ for (i = 0; i < (sizeof(__nodedef)/sizeof(*__nodedef)); i++) {
+ if (__nodedef[i].type == type) {
+ return &(__nodedef[i]);
+ }
+ }
+
+ return NULL;
+}
+
+static const struct nodedef * __get_nodedef_by_name(const char *name)
+{
+ size_t i;
+
+ for (i = 0; i < (sizeof(__nodedef)/sizeof(*__nodedef)); i++) {
+ if (strcmp(__nodedef[i].name, name) == 0) {
+ return &(__nodedef[i]);
+ }
+ }
+
+ return NULL;
+}
+
+static const struct nodeattr * __get_nodeattr(const struct nodedef * nodedef, const char *key)
+{
+ size_t i;
+
+ if (!nodedef || !key)
+ return NULL;
+
+ for (i = 0; i < (sizeof(nodedef->attr)/sizeof(*nodedef->attr)); i++) {
+ if (nodedef->attr[i]->name == NULL) {
+ /* we reached the end of the list */
+ return NULL;
+ }
+
+ if (strcmp(nodedef->attr[i]->name, key) == 0) {
+ return nodedef->attr[i];
+ }
+ }
+
+ return NULL;
+}
+
+
+static void __report_free(refobject_t self, void **userdata)
+{
+ reportxml_t *report = REFOBJECT_TO_TYPE(self, reportxml_t *);
+
+ refobject_unref(report->root);
+}
+
+static reportxml_t * reportxml_new_with_root(reportxml_node_t *root)
+{
+ reportxml_t *ret;
+
+ if (!root)
+ return NULL;
+
+ ret = REFOBJECT_TO_TYPE(refobject_new(sizeof(reportxml_t), __report_free, NULL, NULL, NULL), reportxml_t *);
+ ret->root = root;
+
+ return ret;
+}
+
+reportxml_t * reportxml_new(void)
+{
+ reportxml_node_t *root = reportxml_node_new(REPORTXML_NODE_TYPE_REPORT, NULL, NULL, NULL);
+ reportxml_t *ret;
+
+ if (!root)
+ return NULL;
+
+ ret = reportxml_new_with_root(root);
+
+ if (!ret) {
+ refobject_unref(root);
+ return NULL;
+ }
+
+ return ret;
+}
+
+reportxml_node_t * reportxml_get_root_node(reportxml_t *report)
+{
+ if (!report)
+ return NULL;
+
+ if (refobject_ref(report->root) != 0)
+ return NULL;
+
+ return report->root;
+}
+
+reportxml_node_t * reportxml_get_node_by_attribute(reportxml_t *report, const char *key, const char *value, int include_definitions)
+{
+ if (!report || !key || !value)
+ return NULL;
+
+ return reportxml_node_get_child_by_attribute(report->root, key, value, include_definitions);
+}
+
+reportxml_t * reportxml_parse_xmldoc(xmlDocPtr doc)
+{
+ reportxml_node_t *root;
+ reportxml_t *ret;
+ xmlNodePtr xmlroot;
+
+ if (!doc)
+ return NULL;
+
+ xmlroot = xmlDocGetRootElement(doc);
+ if (!xmlroot)
+ return NULL;
+
+ root = reportxml_node_parse_xmlnode(xmlroot);
+ if (!root)
+ return NULL;
+
+ if (reportxml_node_get_type(root) != REPORTXML_NODE_TYPE_REPORT) {
+ refobject_unref(root);
+ return NULL;
+ }
+
+ ret = reportxml_new_with_root(root);
+ if (!ret) {
+ refobject_unref(root);
+ return NULL;
+ }
+
+ return ret;
+}
+
+xmlDocPtr reportxml_render_xmldoc(reportxml_t *report)
+{
+ xmlDocPtr ret;
+ xmlNodePtr node;
+
+ if (!report)
+ return NULL;
+
+ node = reportxml_node_render_xmlnode(report->root);
+ if (!node)
+ return NULL;
+
+ ret = xmlNewDoc(XMLSTR("1.0"));
+ if (!ret) {
+ xmlFreeNode(node);
+ return NULL;
+ }
+
+ xmlDocSetRootElement(ret, node);
+
+ return ret;
+}
+
+static void __report_node_free(refobject_t self, void **userdata)
+{
+ size_t i;
+
+ reportxml_node_t *node = REFOBJECT_TO_TYPE(self, reportxml_node_t *);
+ xmlFreeNode(node->xmlnode);
+
+ for (i = 0; i < node->childs_len; i++) {
+ refobject_unref(node->childs[i]);
+ }
+
+ for (i = 0; i < node->xml_childs_len; i++) {
+ xmlFreeNode(node->xml_childs[i]);
+ }
+
+ free(node->content);
+ free(node->childs);
+ free(node->xml_childs);
+}
+
+reportxml_node_t * reportxml_node_new(reportxml_node_type_t type, const char *id, const char *definition, const char *akindof)
+{
+ reportxml_node_t *ret;
+ const struct nodedef *nodedef = __get_nodedef(type);
+ size_t i;
+
+ if (!nodedef)
+ return NULL;
+
+ ret = REFOBJECT_TO_TYPE(refobject_new(sizeof(reportxml_node_t), __report_node_free, NULL, NULL, NULL), reportxml_node_t *);
+
+ if (ret == NULL)
+ return NULL;
+
+ ret->type = type;
+
+ ret->xmlnode = xmlNewNode(NULL, XMLSTR(nodedef->name));
+
+ if (!ret->xmlnode) {
+ refobject_unref(ret);
+ return NULL;
+ }
+
+ for (i = 0; i < (sizeof(nodedef->attr)/sizeof(*nodedef->attr)); i++) {
+ const struct nodeattr *nodeattr = nodedef->attr[i];
+ if (nodeattr->name == NULL)
+ break;
+
+ if (nodeattr->def) {
+ if (reportxml_node_set_attribute(ret, nodeattr->name, nodeattr->def) != 0) {
+ refobject_unref(ret);
+ return NULL;
+ }
+ }
+ }
+
+#define _set_attr(x) \
+ if ((x)) { \
+ if (reportxml_node_set_attribute(ret, # x , (x)) != 0) { \
+ refobject_unref(ret); \
+ return NULL; \
+ } \
+ }
+
+ _set_attr(id)
+ _set_attr(definition)
+ _set_attr(akindof)
+
+ return ret;
+}
+
+reportxml_node_t * reportxml_node_parse_xmlnode(xmlNodePtr xmlnode)
+{
+ reportxml_node_t *node;
+
+ const struct nodedef *nodedef;
+
+ if (!xmlnode)
+ return NULL;
+
+ nodedef = __get_nodedef_by_name((const char *)xmlnode->name);
+ if (!nodedef) {
+ ICECAST_LOG_ERROR("Can not parse XML node: Unknown name <%s>", xmlnode->name);
+ return NULL;
+ }
+
+ node = reportxml_node_new(nodedef->type, NULL, NULL, NULL);
+ if (!node)
+ return NULL;
+
+ if (xmlnode->properties) {
+ xmlAttrPtr cur = xmlnode->properties;
+
+ do {
+ xmlChar *value = xmlNodeListGetString(xmlnode->doc, cur->children, 1);
+ if (!value) {
+ refobject_unref(node);
+ return NULL;
+ }
+
+ if (reportxml_node_set_attribute(node, (const char*)cur->name, (const char*)value) != 0) {
+ refobject_unref(node);
+ ICECAST_LOG_DEBUG("Can not parse XML node: attribute \"%H\" value \"%H\" is invalid on node of type <%s>", cur->name, value, nodedef->name);
+ return NULL;
+ }
+
+ xmlFree(value);
+ } while ((cur = cur->next));
+ }
+
+ if (xmlnode->xmlChildrenNode) {
+ xmlNodePtr cur = xmlnode->xmlChildrenNode;
+
+ do {
+ if (nodedef->content == NC_XML) {
+ if (reportxml_node_add_xml_child(node, cur) != 0) {
+ refobject_unref(node);
+ return NULL;
+ }
+ } else {
+ reportxml_node_t *child;
+
+ if (xmlIsBlankNode(cur))
+ continue;
+
+ if (cur->type == XML_COMMENT_NODE)
+ continue;
+
+ if (cur->type == XML_TEXT_NODE) {
+ xmlChar *value = xmlNodeListGetString(xmlnode->doc, cur, 1);
+
+ if (!value) {
+ refobject_unref(node);
+ return NULL;
+ }
+
+ if (reportxml_node_set_content(node, (const char *)value) != 0) {
+ refobject_unref(node);
+ return NULL;
+ }
+
+ xmlFree(value);
+ continue;
+ }
+
+ child = reportxml_node_parse_xmlnode(cur);
+ if (!child) {
+ refobject_unref(node);
+ return NULL;
+ }
+
+ if (reportxml_node_add_child(node, child) != 0) {
+ refobject_unref(node);
+ return NULL;
+ }
+ }
+ } while ((cur = cur->next));
+ }
+
+ return node;
+}
+
+static reportxml_node_t * __reportxml_node_copy_with_db(reportxml_node_t *node, reportxml_database_t *db, ssize_t depth)
+{
+ reportxml_node_t *ret;
+ ssize_t child_count;
+ ssize_t xml_child_count;
+ size_t i;
+
+ if (!node)
+ return NULL;
+
+ child_count = reportxml_node_count_child(node);
+ if (child_count < 0)
+ return NULL;
+
+ xml_child_count = reportxml_node_count_xml_child(node);
+ if (xml_child_count < 0)
+ return NULL;
+
+ ret = reportxml_node_parse_xmlnode(node->xmlnode);
+ if (!ret)
+ return NULL;
+
+ if (node->content) {
+ if (reportxml_node_set_content(ret, node->content) != 0) {
+ refobject_unref(ret);
+ return NULL;
+ }
+ }
+
+ for (i = 0; i < (size_t)child_count; i++) {
+ reportxml_node_t *child = reportxml_node_get_child(node, i);
+
+ if (db && depth > 0) {
+ if (__attach_copy_of_node_or_definition(ret, child, db, depth) != 0) {
+ refobject_unref(child);
+ refobject_unref(ret);
+ return NULL;
+ }
+ refobject_unref(child);
+ } else {
+ reportxml_node_t *copy = __reportxml_node_copy_with_db(child, NULL, -1);
+
+ refobject_unref(child);
+
+ if (!copy) {
+ refobject_unref(ret);
+ return NULL;
+ }
+
+ if (reportxml_node_add_child(ret, copy) != 0) {
+ refobject_unref(copy);
+ refobject_unref(ret);
+ return NULL;
+ }
+ }
+ }
+
+ for (i = 0; i < (size_t)xml_child_count; i++) {
+ xmlNodePtr child = reportxml_node_get_xml_child(node, i);
+ if (reportxml_node_add_xml_child(ret, child) != 0) {
+ xmlFreeNode(child);
+ refobject_unref(ret);
+ return NULL;
+ }
+ xmlFreeNode(child);
+ }
+
+ return ret;
+}
+
+reportxml_node_t * reportxml_node_copy(reportxml_node_t *node)
+{
+ return __reportxml_node_copy_with_db(node, NULL, -1);
+}
+
+xmlNodePtr reportxml_node_render_xmlnode(reportxml_node_t *node)
+{
+ xmlNodePtr ret;
+ ssize_t child_count;
+ ssize_t xml_child_count;
+ size_t i;
+ xmlChar *definition;
+
+ if (!node)
+ return NULL;
+
+ child_count = reportxml_node_count_child(node);
+ if (child_count < 0)
+ return NULL;
+
+ xml_child_count = reportxml_node_count_xml_child(node);
+ if (xml_child_count < 0)
+ return NULL;
+
+ ret = xmlCopyNode(node->xmlnode, 2);
+ if (!ret)
+ return NULL;
+
+ definition = xmlGetProp(ret, XMLSTR("_definition"));
+ if (definition) {
+ xmlSetProp(ret, XMLSTR("definition"), definition);
+ xmlUnsetProp(ret, XMLSTR("_definition"));
+ xmlFree(definition);
+ }
+
+ for (i = 0; i < (size_t)child_count; i++) {
+ reportxml_node_t *child = reportxml_node_get_child(node, i);
+ xmlNodePtr xmlchild;
+
+ if (!child) {
+ xmlFreeNode(ret);
+ return NULL;
+ }
+
+ xmlchild = reportxml_node_render_xmlnode(child);
+ refobject_unref(child);
+ if (!xmlchild) {
+ xmlFreeNode(ret);
+ return NULL;
+ }
+
+ xmlAddChild(ret, xmlchild);
+ }
+
+ if (node->content) {
+ xmlNodePtr xmlchild = xmlNewText(XMLSTR(node->content));
+
+ if (!xmlchild) {
+ xmlFreeNode(ret);
+ return NULL;
+ }
+
+ xmlAddChild(ret, xmlchild);
+ }
+
+ for (i = 0; i < (size_t)xml_child_count; i++) {
+ xmlNodePtr xmlchild = reportxml_node_get_xml_child(node, i);
+
+ if (!xmlchild) {
+ xmlFreeNode(ret);
+ return NULL;
+ }
+
+ xmlAddChild(ret, xmlchild);
+ }
+
+
+ return ret;
+}
+
+reportxml_node_type_t reportxml_node_get_type(reportxml_node_t *node)
+{
+ if (!node)
+ return REPORTXML_NODE_TYPE__ERROR;
+
+ return node->type;
+}
+
+int reportxml_node_set_attribute(reportxml_node_t *node, const char *key, const char *value)
+{
+ const struct nodedef *nodedef;
+ const struct nodeattr *nodeattr;
+ size_t i;
+
+ if (!node || !key || !value)
+ return -1;
+
+ nodedef = __get_nodedef(node->type);
+ nodeattr = __get_nodeattr(nodedef, key);
+ if (!nodeattr)
+ return -1;
+
+ if (nodeattr->values[0] != NULL) {
+ int found = 0;
+ for (i = 0; i < (sizeof(nodeattr->values)/sizeof(*nodeattr->values)); i++) {
+ if (nodeattr->values[i] == NULL)
+ break;
+
+ if (strcmp(nodeattr->values[i], value) == 0) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found)
+ return -1;
+ }
+
+ if (nodeattr->checker) {
+ if (nodeattr->checker(nodeattr, value) != 0) {
+ return -1;
+ }
+ }
+
+ xmlSetProp(node->xmlnode, XMLSTR(key), XMLSTR(value));
+
+ return 0;
+}
+
+char * reportxml_node_get_attribute(reportxml_node_t *node, const char *key)
+{
+ xmlChar *k;
+ char *n;
+
+ if (!node || !key)
+ return NULL;
+
+ /* We do this super complicated thing as we do not know if we can use free() to free a xmlChar* object. */
+ k = xmlGetProp(node->xmlnode, XMLSTR(key));
+ if (!k)
+ return NULL;
+
+ n = strdup((char*)k);
+ xmlFree(k);
+
+ return n;
+}
+
+int reportxml_node_add_child(reportxml_node_t *node, reportxml_node_t *child)
+{
+ const struct nodedef *nodedef;
+ reportxml_node_t **n;
+ size_t i;
+ int found;
+
+ if (!node || !child)
+ return -1;
+
+ nodedef = __get_nodedef(node->type);
+
+ if (nodedef->content != NC_CHILDS)
+ return -1;
+
+ found = 0;
+ for (i = 0; nodedef->childs[i] != REPORTXML_NODE_TYPE__ERROR; i++) {
+ if (nodedef->childs[i] == child->type) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found)
+ return -1;
+
+ n = realloc(node->childs, sizeof(*n)*(node->childs_len + 1));
+ if (!n)
+ return -1;
+
+ node->childs = n;
+
+ if (refobject_ref(child) != 0)
+ return -1;
+
+ node->childs[node->childs_len++] = child;
+
+// ICECAST_LOG_DEBUG("Child %p added to Node %p", child, node);
+
+ return 0;
+}
+
+ssize_t reportxml_node_count_child(reportxml_node_t *node)
+{
+ if (!node)
+ return -1;
+
+ return node->childs_len;
+}
+
+reportxml_node_t * reportxml_node_get_child(reportxml_node_t *node, size_t idx)
+{
+ if (!node)
+ return NULL;
+
+ if (idx >= node->childs_len)
+ return NULL;
+
+ if (refobject_ref(node->childs[idx]) != 0)
+ return NULL;
+
+ return node->childs[idx];
+}
+
+reportxml_node_t * reportxml_node_get_child_by_attribute(reportxml_node_t *node, const char *key, const char *value, int include_definitions)
+{
+ reportxml_node_t *ret;
+ xmlChar *k;
+ size_t i;
+
+ if (!node || !key ||!value)
+ return NULL;
+
+ k = xmlGetProp(node->xmlnode, XMLSTR(key));
+ if (k != NULL) {
+ if (strcmp((const char*)k, value) == 0) {
+ xmlFree(k);
+
+ if (refobject_ref(node) != 0)
+ return NULL;
+
+ return node;
+ }
+ xmlFree(k);
+ }
+
+ if (node->type == REPORTXML_NODE_TYPE_DEFINITION && !include_definitions)
+ return NULL;
+
+ for (i = 0; i < node->childs_len; i++) {
+ ret = reportxml_node_get_child_by_attribute(node->childs[i], key, value, include_definitions);
+ if (ret != NULL)
+ return ret;
+ }
+
+ return NULL;
+}
+
+int reportxml_node_set_content(reportxml_node_t *node, const char *value)
+{
+ const struct nodedef *nodedef;
+ char *n = NULL;
+
+ if (!node)
+ return -1;
+
+ nodedef = __get_nodedef(node->type);
+
+ if (nodedef->content != NC_CONTENT)
+ return -1;
+
+ if (value) {
+ n = strdup(value);
+ if (!n)
+ return -1;
+ }
+
+ free(node->content);
+ node->content = n;
+
+ return 0;
+}
+
+char * reportxml_node_get_content(reportxml_node_t *node)
+{
+ if (!node)
+ return NULL;
+
+ if (node->content) {
+ return strdup(node->content);
+ } else {
+ return NULL;
+ }
+}
+
+int reportxml_node_add_xml_child(reportxml_node_t *node, xmlNodePtr child)
+{
+ const struct nodedef *nodedef;
+ xmlNodePtr *n;
+
+ if (!node || !child)
+ return -1;
+
+ nodedef = __get_nodedef(node->type);
+
+ if (nodedef->content != NC_XML)
+ return -1;
+
+ n = realloc(node->xml_childs, sizeof(*n)*(node->xml_childs_len + 1));
+ if (!n)
+ return -1;
+
+ node->xml_childs = n;
+
+ node->xml_childs[node->xml_childs_len] = xmlCopyNode(child, 2);
+ if (node->xml_childs[node->xml_childs_len] == NULL)
+ return -1;
+
+ node->xml_childs_len++;
+
+ return 0;
+}
+
+ssize_t reportxml_node_count_xml_child(reportxml_node_t *node)
+{
+ if (!node)
+ return -1;
+
+ return node->xml_childs_len;
+}
+
+xmlNodePtr reportxml_node_get_xml_child(reportxml_node_t *node, size_t idx)
+{
+ xmlNodePtr ret;
+
+ if (!node)
+ return NULL;
+
+ if (idx >= node->xml_childs_len)
+ return NULL;
+
+ ret = xmlCopyNode(node->xml_childs[idx], 2);
+
+ return ret;
+}
+
+static void __database_free(refobject_t self, void **userdata)
+{
+ reportxml_database_t *db = REFOBJECT_TO_TYPE(self, reportxml_database_t *);
+
+ if (db->definitions)
+ avl_tree_free(db->definitions, (avl_free_key_fun_type)refobject_unref);
+
+ thread_mutex_destroy(&(db->lock));
+}
+
+static int __compare_definitions(void *arg, void *a, void *b)
+{
+ char *id_a, *id_b;
+ int ret = 0;
+
+ id_a = reportxml_node_get_attribute(a, "defines");
+ id_b = reportxml_node_get_attribute(b, "defines");
+
+ if (!id_a || !id_b || id_a == id_b) {
+ ret = 0;
+ } else {
+ ret = strcmp(id_a, id_b);
+ }
+
+ free(id_a);
+ free(id_b);
+
+ return ret;
+}
+
+reportxml_database_t * reportxml_database_new(void)
+{
+ reportxml_database_t *ret = REFOBJECT_TO_TYPE(refobject_new(sizeof(reportxml_database_t), __database_free, NULL, NULL, NULL), reportxml_database_t *);
+
+ if (!ret)
+ return NULL;
+
+ ret->definitions = avl_tree_new(__compare_definitions, NULL);
+ if (!ret->definitions) {
+ refobject_unref(ret);
+ return NULL;
+ }
+
+ thread_mutex_create(&(ret->lock));
+
+ return ret;
+}
+
+int reportxml_database_add_report(reportxml_database_t *db, reportxml_t *report)
+{
+ reportxml_node_t *root;
+ ssize_t count;
+ size_t i;
+
+ if (!db || !report)
+ return -1;
+
+ root = reportxml_get_root_node(report);
+ if (!root)
+ return -1;
+
+ count = reportxml_node_count_child(root);
+ if (count < 0)
+ return -1;
+
+ thread_mutex_lock(&(db->lock));
+
+ for (i = 0; i < (size_t)count; i++) {
+ reportxml_node_t *node = reportxml_node_get_child(root, i);
+ reportxml_node_t *copy;
+
+ if (reportxml_node_get_type(node) != REPORTXML_NODE_TYPE_DEFINITION) {
+ refobject_unref(node);
+ continue;
+ }
+
+ copy = reportxml_node_copy(node);
+ refobject_unref(node);
+
+ if (!copy)
+ continue;
+
+ avl_insert(db->definitions, copy);
+ }
+
+ thread_mutex_unlock(&(db->lock));
+
+ return 0;
+}
+
+static int __attach_copy_of_node_or_definition(reportxml_node_t *parent, reportxml_node_t *node, reportxml_database_t *db, ssize_t depth)
+{
+ reportxml_node_t *copy;
+ reportxml_node_t *def = NULL;
+ char *definition;
+
+ if (!parent || !node || !db)
+ return -1;
+
+
+ if (depth >= 2) {
+ definition = reportxml_node_get_attribute(node, "definition");
+ if (definition) {
+ ICECAST_LOG_DEBUG("parent=%p, node=%p, depth=%zi, definition=\"%H\"", parent, node, depth, definition);
+ def = reportxml_database_build_node(db, definition, depth - 1);
+ ICECAST_LOG_DEBUG("parent=%p, node=%p, depth=%zi, Definition for \"%H\" at %p", parent, node, depth, definition, def);
+ free(definition);
+ }
+ }
+
+ if (def) {
+ ssize_t count = reportxml_node_count_child(def);
+ size_t i;
+
+ ICECAST_LOG_DEBUG("parent=%p, node=%p, depth=%zi, Found definition.", parent, node, depth);
+
+ if (count < 0) {
+ refobject_unref(def);
+ ICECAST_LOG_DEBUG("parent=%p, node=%p, depth=%zi <- -1", parent, node, depth);
+ return -1;
+ }
+
+ for (i = 0; i < (size_t)count; i++) {
+ reportxml_node_t *child = reportxml_node_get_child(def, i);
+
+ ICECAST_LOG_DEBUG("parent=%p, node=%p, depth=%zi, Itering, child #%zu (%p)", parent, node, depth, i, child);
+
+ if (__attach_copy_of_node_or_definition(parent, child, db, depth - 1) != 0) {
+ refobject_unref(child);
+ refobject_unref(def);
+ ICECAST_LOG_DEBUG("parent=%p, node=%p, depth=%zi <- -1", parent, node, depth);
+ return -1;
+ }
+
+ refobject_unref(child);
+ }
+
+ refobject_unref(def);
+
+ ICECAST_LOG_DEBUG("parent=%p, node=%p, depth=%zi <- 0", parent, node, depth);
+ return 0;
+ } else {
+ int ret;
+
+ ICECAST_LOG_DEBUG("parent=%p, node=%p, depth=%zi, Found no definition.", parent, node, depth);
+
+ copy = __reportxml_node_copy_with_db(node, db, depth - 1);
+ if (!copy) {
+ ICECAST_LOG_DEBUG("parent=%p, node=%p, depth=%zi <- -1", parent, node, depth);
+ return -1;
+ }
+
+ ret = reportxml_node_add_child(parent, copy);
+
+ refobject_unref(copy);
+
+ ICECAST_LOG_DEBUG("parent=%p, node=%p, depth=%zi <- %i", parent, node, depth, ret);
+ return ret;
+ }
+}
+
+static reportxml_node_t * __reportxml_database_build_node_ext(reportxml_database_t *db, const char *id, ssize_t depth, reportxml_node_type_t *acst_type_ret)
+{
+ reportxml_node_t *search;
+ reportxml_node_t *found;
+ reportxml_node_t *ret;
+ enum {
+ ACST_FIRST,
+ ACST_YES,
+ ACST_NO,
+ } all_childs_same_type = ACST_FIRST;
+ reportxml_node_type_t acst_type = REPORTXML_NODE_TYPE__ERROR;
+ char *template;
+ ssize_t count;
+ size_t i;
+
+ if (!db || !id)
+ return NULL;
+
+ /* Assign default depth in case it's set to -1 */
+ if (depth < 0)
+ depth = 8;
+
+ ICECAST_LOG_DEBUG("Looking up \"%H\" in database %p with depth %zu", id, db, depth);
+
+ if (!depth)
+ return NULL;
+
+ search = reportxml_node_new(REPORTXML_NODE_TYPE_DEFINITION, NULL, NULL, NULL);
+ if (!search)
+ return NULL;
+
+ if (reportxml_node_set_attribute(search, "defines", id) != 0) {
+ refobject_unref(search);
+ return NULL;
+ }
+
+ thread_mutex_lock(&(db->lock));
+ if (avl_get_by_key(db->definitions, REFOBJECT_TO_TYPE(search, void *), (void**)&found) != 0) {
+ thread_mutex_unlock(&(db->lock));
+ refobject_unref(search);
+ return NULL;
+ }
+
+ refobject_unref(search);
+
+ if (refobject_ref(found) != 0) {
+ thread_mutex_unlock(&(db->lock));
+ return NULL;
+ }
+ thread_mutex_unlock(&(db->lock));
+
+ count = reportxml_node_count_child(found);
+ if (count < 0) {
+ refobject_unref(found);
+ return NULL;
+ }
+
+ template = reportxml_node_get_attribute(found, "template");
+ if (template) {
+ reportxml_node_t *tpl = reportxml_database_build_node(db, template, depth - 1);
+
+ free(template);
+
+ if (tpl) {
+ ret = reportxml_node_copy(tpl);
+ refobject_unref(tpl);
+ } else {
+ ret = NULL;
+ }
+ } else {
+ ret = reportxml_node_new(REPORTXML_NODE_TYPE_DEFINITION, NULL, NULL, NULL);
+ }
+
+ if (!ret) {
+ refobject_unref(found);
+ return NULL;
+ }
+
+ for (i = 0; i < (size_t)count; i++) {
+ /* TODO: Look up definitions of our childs and childs' childs. */
+
+ reportxml_node_t *node = reportxml_node_get_child(found, i);
+ reportxml_node_type_t type = reportxml_node_get_type(node);
+
+ switch (all_childs_same_type) {
+ case ACST_FIRST:
+ acst_type = type;
+ all_childs_same_type = ACST_YES;
+ break;
+ case ACST_YES:
+ if (acst_type != type)
+ all_childs_same_type = ACST_NO;
+ break;
+ case ACST_NO:
+ /* noop */
+ break;
+ }
+
+ /* We want depth, not depth - 1 here. __attach_copy_of_node_or_definition() takes care of this for us. */
+ if (__attach_copy_of_node_or_definition(ret, node, db, depth) != 0) {
+ refobject_unref(node);
+ refobject_unref(found);
+ refobject_unref(ret);
+ ICECAST_LOG_ERROR("Can not attach child #%zu (%p) to attachment point (%p) in report. BAD.", i, node, ret);
+ return NULL;
+ }
+
+ refobject_unref(node);
+ }
+
+ refobject_unref(found);
+
+ if (all_childs_same_type == ACST_YES) {
+ count = reportxml_node_count_child(ret);
+ if (count < 0) {
+ refobject_unref(ret);
+ return NULL;
+ }
+
+ for (i = 0; i < (size_t)count; i++) {
+ reportxml_node_t *node = reportxml_node_get_child(ret, i);
+
+ if (!node) {
+ refobject_unref(ret);
+ return NULL;
+ }
+
+ if (reportxml_node_set_attribute(node, "_definition", id) != 0) {
+ refobject_unref(node);
+ refobject_unref(ret);
+ return NULL;
+ }
+
+ refobject_unref(node);
+ }
+ }
+
+ if (acst_type_ret) {
+ if (all_childs_same_type == ACST_YES) {
+ *acst_type_ret = acst_type;
+ } else {
+ *acst_type_ret = REPORTXML_NODE_TYPE__ERROR;
+ }
+ }
+
+ return ret;
+}
+
+reportxml_node_t * reportxml_database_build_node(reportxml_database_t *db, const char *id, ssize_t depth)
+{
+ return __reportxml_database_build_node_ext(db, id, depth, NULL);
+}
+
+/* We try to build a a report from the definition. Exat structure depends on what is defined. */
+reportxml_t * reportxml_database_build_report(reportxml_database_t *db, const char *id, ssize_t depth)
+{
+ reportxml_node_t *definition;
+ reportxml_node_t *child;
+ reportxml_node_t *root;
+ reportxml_node_t *attach_to;
+ reportxml_node_type_t type;
+ reportxml_t *ret;
+ ssize_t count;
+ size_t i;
+
+ if (!db || !id)
+ return NULL;
+
+ /* first find the definition itself. This will be some REPORTXML_NODE_TYPE_DEFINITION node. */
+ definition = __reportxml_database_build_node_ext(db, id, depth, &type);
+ if (!definition) {
+ ICECAST_LOG_WARN("No matching definition for \"%H\"", id);
+ return NULL;
+ }
+
+ /* Let's see how many children we have. */
+ count = reportxml_node_count_child(definition);
+ if (count < 0) {
+ refobject_unref(definition);
+ ICECAST_LOG_ERROR("Can not get child count from definition. BAD.");
+ return NULL;
+ } else if (count == 0) {
+ /* Empty definition? Not exactly an exciting report... */
+ ICECAST_LOG_WARN("Empty definition for \"%H\". Returning empty report. This is likely an error.", id);
+ refobject_unref(definition);
+ return reportxml_new();
+ }
+
+ if (type == REPORTXML_NODE_TYPE__ERROR) {
+ /* Now the hard part: find out what level we are. */
+ child = reportxml_node_get_child(definition, 0);
+ if (!child) {
+ refobject_unref(definition);
+ ICECAST_LOG_ERROR("Can not get first child. BAD.");
+ return NULL;
+ }
+
+ type = reportxml_node_get_type(child);
+ refobject_unref(child);
+ }
+
+ /* check for supported configurations */
+ switch (type) {
+ case REPORTXML_NODE_TYPE_INCIDENT:
+ case REPORTXML_NODE_TYPE_STATE:
+ break;
+ default:
+ refobject_unref(definition);
+ ICECAST_LOG_WARN("Unsupported type of first child.");
+ return NULL;
+ break;
+ }
+
+ ret = reportxml_new();
+ if (!ret) {
+ refobject_unref(definition);
+ ICECAST_LOG_ERROR("Can not allocate new report. BAD.");
+ return NULL;
+ }
+
+ root = reportxml_get_root_node(ret);
+ if (!ret) {
+ refobject_unref(definition);
+ refobject_unref(ret);
+ ICECAST_LOG_ERROR("Can not get root node from report. BAD.");
+ return NULL;
+ }
+
+ if (type == REPORTXML_NODE_TYPE_INCIDENT) {
+ refobject_ref(attach_to = root);
+ } else if (type == REPORTXML_NODE_TYPE_STATE) {
+ attach_to = reportxml_node_new(REPORTXML_NODE_TYPE_INCIDENT, NULL, NULL, NULL);
+ if (attach_to) {
+ if (reportxml_node_add_child(root, attach_to) != 0) {
+ refobject_unref(attach_to);
+ attach_to = NULL;
+ }
+ }
+ } else {
+ attach_to = NULL;
+ }
+
+ refobject_unref(root);
+
+ if (!attach_to) {
+ refobject_unref(definition);
+ refobject_unref(ret);
+ ICECAST_LOG_ERROR("No point to attach to in report. BAD.");
+ return NULL;
+ }
+
+ /* now move nodes. */
+
+ for (i = 0; i < (size_t)count; i++) {
+ child = reportxml_node_get_child(definition, i);
+
+ if (reportxml_node_get_type(child) == type) {
+ /* Attach definition to all childs that are the same type.
+ * As long as we work to-the-specs all childs are of the same type.
+ * But if we work in relaxed mode, there might be other tags.
+ */
+ reportxml_node_set_attribute(child, "definition", id);
+ }
+
+ /* we can directly attach as it's a already a copy. */
+ if (reportxml_node_add_child(attach_to, child) != 0) {
+ refobject_unref(definition);
+ refobject_unref(attach_to);
+ refobject_unref(ret);
+ ICECAST_LOG_ERROR("Can not attach child #%zu (%p) to attachment point (%p) in report. BAD.", i, child, attach_to);
+ return NULL;
+ }
+
+ refobject_unref(child);
+ }
+
+ refobject_unref(definition);
+ refobject_unref(attach_to);
+
+ return ret;
+}
diff --git a/src/reportxml.h b/src/reportxml.h
new file mode 100644
index 00000000..31d71f93
--- /dev/null
+++ b/src/reportxml.h
@@ -0,0 +1,65 @@
+/* Icecast
+ *
+ * This program is distributed under the GNU General Public License, version 2.
+ * A copy of this license is included with this source.
+ *
+ * Copyright 2018, Philipp "ph3-der-loewe" Schafft ,
+ */
+
+#ifndef __REPORTXML_H__
+#define __REPORTXML_H__
+
+#include
+
+#include "icecasttypes.h"
+#include "compat.h"
+
+typedef enum {
+ REPORTXML_NODE_TYPE__ERROR,
+ REPORTXML_NODE_TYPE_REPORT,
+ REPORTXML_NODE_TYPE_DEFINITION,
+ REPORTXML_NODE_TYPE_INCIDENT,
+ REPORTXML_NODE_TYPE_STATE,
+ REPORTXML_NODE_TYPE_BACKTRACE,
+ REPORTXML_NODE_TYPE_POSITION,
+ REPORTXML_NODE_TYPE_MORE,
+ REPORTXML_NODE_TYPE_FIX,
+ REPORTXML_NODE_TYPE_ACTION,
+ REPORTXML_NODE_TYPE_REASON,
+ REPORTXML_NODE_TYPE_TEXT,
+ REPORTXML_NODE_TYPE_TIMESTAMP,
+ REPORTXML_NODE_TYPE_RESOURCE,
+ REPORTXML_NODE_TYPE_VALUE,
+ REPORTXML_NODE_TYPE_REFERENCE,
+ REPORTXML_NODE_TYPE_EXTENSION
+} reportxml_node_type_t;
+
+reportxml_t * reportxml_new(void);
+reportxml_node_t * reportxml_get_root_node(reportxml_t *report);
+reportxml_node_t * reportxml_get_node_by_attribute(reportxml_t *report, const char *key, const char *value, int include_definitions);
+reportxml_t * reportxml_parse_xmldoc(xmlDocPtr doc);
+xmlDocPtr reportxml_render_xmldoc(reportxml_t *report);
+
+reportxml_node_t * reportxml_node_new(reportxml_node_type_t type, const char *id, const char *definition, const char *akindof);
+reportxml_node_t * reportxml_node_parse_xmlnode(xmlNodePtr xmlnode);
+reportxml_node_t * reportxml_node_copy(reportxml_node_t *node);
+xmlNodePtr reportxml_node_render_xmlnode(reportxml_node_t *node);
+reportxml_node_type_t reportxml_node_get_type(reportxml_node_t *node);
+int reportxml_node_set_attribute(reportxml_node_t *node, const char *key, const char *value);
+char * reportxml_node_get_attribute(reportxml_node_t *node, const char *key);
+int reportxml_node_add_child(reportxml_node_t *node, reportxml_node_t *child);
+ssize_t reportxml_node_count_child(reportxml_node_t *node);
+reportxml_node_t * reportxml_node_get_child(reportxml_node_t *node, size_t idx);
+reportxml_node_t * reportxml_node_get_child_by_attribute(reportxml_node_t *node, const char *key, const char *value, int include_definitions);
+int reportxml_node_set_content(reportxml_node_t *node, const char *value);
+char * reportxml_node_get_content(reportxml_node_t *node);
+int reportxml_node_add_xml_child(reportxml_node_t *node, xmlNodePtr child);
+ssize_t reportxml_node_count_xml_child(reportxml_node_t *node);
+xmlNodePtr reportxml_node_get_xml_child(reportxml_node_t *node, size_t idx);
+
+reportxml_database_t * reportxml_database_new(void);
+int reportxml_database_add_report(reportxml_database_t *db, reportxml_t *report);
+reportxml_node_t * reportxml_database_build_node(reportxml_database_t *db, const char *id, ssize_t depth);
+reportxml_t * reportxml_database_build_report(reportxml_database_t *db, const char *id, ssize_t depth);
+
+#endif
diff --git a/src/stats.c b/src/stats.c
index 5cfc578c..07cf534c 100644
--- a/src/stats.c
+++ b/src/stats.c
@@ -1024,7 +1024,7 @@ void stats_transform_xslt(client_t *client, const char *uri)
doc = stats_get_xml(0, mount, client->mode);
- xslt_transform(doc, xslpath, client);
+ xslt_transform(doc, xslpath, client, 200);
xmlFreeDoc(doc);
free(xslpath);
diff --git a/src/xslt.c b/src/xslt.c
index 8dd35969..366ed4c2 100644
--- a/src/xslt.c
+++ b/src/xslt.c
@@ -285,7 +285,16 @@ static xmlDocPtr custom_loader(const xmlChar *URI,
return ret;
}
-void xslt_transform(xmlDocPtr doc, const char *xslfilename, client_t *client)
+static inline void _send_error(client_t *client, icecast_error_id_t id, int old_status) {
+ if (old_status >= 400) {
+ client_send_error_by_id(client, ICECAST_ERROR_RECURSIVE_ERROR);
+ return;
+ }
+
+ client_send_error_by_id(client, id);
+}
+
+void xslt_transform(xmlDocPtr doc, const char *xslfilename, client_t *client, int status)
{
xmlDocPtr res;
xsltStylesheetPtr cur;
@@ -305,7 +314,7 @@ void xslt_transform(xmlDocPtr doc, const char *xslfilename, client_t *client)
{
thread_mutex_unlock(&xsltlock);
ICECAST_LOG_ERROR("problem reading stylesheet \"%s\"", xslfilename);
- client_send_error_by_id(client, ICECAST_ERROR_XSLT_PARSE);
+ _send_error(client, ICECAST_ERROR_XSLT_PARSE, status);
return;
}
@@ -346,10 +355,10 @@ void xslt_transform(xmlDocPtr doc, const char *xslfilename, client_t *client)
if (string == NULL)
string = xmlCharStrdup ("");
- ret = util_http_build_header(refbuf->data, full_len, 0, 0, 200, NULL, mediatype, charset, NULL, NULL, client);
+ ret = util_http_build_header(refbuf->data, full_len, 0, 0, status, NULL, mediatype, charset, 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);
+ _send_error(client, ICECAST_ERROR_GEN_HEADER_GEN_FAILED, status);
} else {
if ( full_len < (ret + (ssize_t)len + (ssize_t)64) ) {
void *new_data;
@@ -359,15 +368,15 @@ void xslt_transform(xmlDocPtr doc, const char *xslfilename, client_t *client)
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, client);
+ ret = util_http_build_header(refbuf->data, full_len, 0, 0, status, NULL, mediatype, charset, 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);
+ _send_error(client, ICECAST_ERROR_GEN_HEADER_GEN_FAILED, status);
failed = 1;
}
} else {
ICECAST_LOG_ERROR("Client buffer reallocation failed. Dropping client.");
- client_send_error_by_id(client, ICECAST_ERROR_GEN_BUFFER_REALLOC);
+ _send_error(client, ICECAST_ERROR_GEN_BUFFER_REALLOC, status);
failed = 1;
}
}
@@ -375,7 +384,7 @@ void xslt_transform(xmlDocPtr doc, const char *xslfilename, client_t *client)
if (!failed) {
snprintf(refbuf->data + ret, full_len - ret, "Content-Length: %d\r\n\r\n%s", len, string);
- client->respcode = 200;
+ client->respcode = status;
client_set_queue (client, NULL);
client->refbuf = refbuf;
refbuf->len = strlen (refbuf->data);
@@ -387,7 +396,7 @@ void xslt_transform(xmlDocPtr doc, const char *xslfilename, client_t *client)
else
{
ICECAST_LOG_WARN("problem applying stylesheet \"%s\"", xslfilename);
- client_send_error_by_id(client, ICECAST_ERROR_XSLT_problem);
+ _send_error(client, ICECAST_ERROR_XSLT_problem, status);
}
thread_mutex_unlock (&xsltlock);
xmlFreeDoc(res);
diff --git a/src/xslt.h b/src/xslt.h
index 97e771bd..feb4c2fa 100644
--- a/src/xslt.h
+++ b/src/xslt.h
@@ -16,7 +16,7 @@
#include "icecasttypes.h"
-void xslt_transform(xmlDocPtr doc, const char *xslfilename, client_t *client);
+void xslt_transform(xmlDocPtr doc, const char *xslfilename, client_t *client, int status);
void xslt_initialize(void);
void xslt_shutdown(void);