From 903ac2f1b3c30518609b9adadda50c8d61b6dbc6 Mon Sep 17 00:00:00 2001 From: Philipp Schafft Date: Thu, 7 Jun 2018 15:19:02 +0000 Subject: [PATCH 01/27] Feature: Added fundamental report xml handling --- src/Makefile.am | 2 + src/icecasttypes.h | 7 + src/reportxml.c | 606 +++++++++++++++++++++++++++++++++++++++++++++ src/reportxml.h | 54 ++++ 4 files changed, 669 insertions(+) create mode 100644 src/reportxml.c create mode 100644 src/reportxml.h 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/icecasttypes.h b/src/icecasttypes.h index 36b198a4..70ffd896 100644 --- a/src/icecasttypes.h +++ b/src/icecasttypes.h @@ -98,6 +98,11 @@ 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; + /* ---[ refobject.[ch] ]--- */ typedef struct refobject_base_tag refobject_base_t; @@ -107,6 +112,8 @@ 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; } refobject_t; #else typedef void * refobject_t; diff --git a/src/reportxml.c b/src/reportxml.c new file mode 100644 index 00000000..b6e19324 --- /dev/null +++ b/src/reportxml.c @@ -0,0 +1,606 @@ +/* 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 "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; + char *content; +}; + +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]; +}; + +struct nodedef { + reportxml_node_type_t type; + const char *name; + int has_content; + const struct nodeattr *attr[12]; +}; + +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", "xxx", 1, NULL, {"xxx", 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_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__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 +static const struct nodedef __nodedef[] = { + {REPORTXML_NODE_TYPE_REPORT, "report", 0, {__attr_id, __attr_version, __attr_xmlns, __attr__eol}}, + {REPORTXML_NODE_TYPE_DEFINITION, "definition", 0, {__BASIC_ELEMENT, __attr_template, __attr_defines, __attr__eol}}, + {REPORTXML_NODE_TYPE_INCIDENT, "incident", 0, {__BASIC_ELEMENT, __attr__eol}}, + {REPORTXML_NODE_TYPE_STATE, "state", 0, {__BASIC_ELEMENT, __attr__eol}}, + {REPORTXML_NODE_TYPE_BACKTRACE, "backtrace", 0, {__BASIC_ELEMENT, __attr__eol}}, + {REPORTXML_NODE_TYPE_POSITION, "position", 0, {__BASIC_ELEMENT, __attr_function, __attr_filename, __attr_line, __attr_binary, __attr_offset, __attr__eol}}, + {REPORTXML_NODE_TYPE_MORE, "more", 0, {__BASIC_ELEMENT, __attr__eol}}, + {REPORTXML_NODE_TYPE_FIX, "fix", 0, {__BASIC_ELEMENT, __attr__eol}}, + {REPORTXML_NODE_TYPE_ACTION, "action", 0, {__BASIC_ELEMENT, __attr__action_type, __attr__eol}}, + {REPORTXML_NODE_TYPE_REASON, "reason", 0, {__BASIC_ELEMENT, __attr__eol}}, + {REPORTXML_NODE_TYPE_TEXT, "text", 1, {__BASIC_ELEMENT, __attr_lang, __attr_dir, __attr__eol}}, + {REPORTXML_NODE_TYPE_TIMESTAMP, "timestamp", 0, {__BASIC_ELEMENT, __attr_absolute, __attr_relative, __attr__eol}}, + {REPORTXML_NODE_TYPE_RESOURCE, "resource", 0, {__BASIC_ELEMENT, __attr__resource_type, __attr_name, __attr__eol}}, + {REPORTXML_NODE_TYPE_VALUE, "value", 0, {__BASIC_ELEMENT, __attr_member, __attr_value, __attr_state, __attr__value_type, __attr__eol}}, + {REPORTXML_NODE_TYPE_REFERENCE, "reference", 0, {__BASIC_ELEMENT, __attr__reference_type, __attr_href, __attr__eol}}, + {REPORTXML_NODE_TYPE_EXTENSION, "extension", 0, {__BASIC_ELEMENT, __attr__eol}}, +}; + +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_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]); + } + + free(node->content); + free(node->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) + 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); + return NULL; + } + + xmlFree(value); + } while ((cur = cur->next)); + } + + if (xmlnode->xmlChildrenNode) { + xmlNodePtr cur = xmlnode->xmlChildrenNode; + + do { + 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; +} + +xmlNodePtr reportxml_node_render_xmlnode(reportxml_node_t *node) +{ + xmlNodePtr ret; + ssize_t count; + size_t i; + + if (!node) + return NULL; + + count = reportxml_node_count_child(node); + if (count < 0) + return NULL; + + ret = xmlCopyNode(node->xmlnode, 2); + if (!ret) + return NULL; + + for (i = 0; i < (size_t)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); + } + + 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) +{ + reportxml_node_t **n; + + if (!node || !child) + 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]; +} + +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->has_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; + } +} diff --git a/src/reportxml.h b/src/reportxml.h new file mode 100644 index 00000000..acaea4ff --- /dev/null +++ b/src/reportxml.h @@ -0,0 +1,54 @@ +/* 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_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); +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); +int reportxml_node_set_content(reportxml_node_t *node, const char *value); +char * reportxml_node_get_content(reportxml_node_t *node); + +#endif From c60e957782fc8235de28b59544c173f37218278a Mon Sep 17 00:00:00 2001 From: Philipp Schafft Date: Fri, 8 Jun 2018 08:03:06 +0000 Subject: [PATCH 02/27] Feature: Added a database to store definitions --- src/icecasttypes.h | 2 + src/reportxml.c | 357 +++++++++++++++++++++++++++++++++++++++++++++ src/reportxml.h | 6 + 3 files changed, 365 insertions(+) diff --git a/src/icecasttypes.h b/src/icecasttypes.h index 70ffd896..ac3496f1 100644 --- a/src/icecasttypes.h +++ b/src/icecasttypes.h @@ -102,6 +102,7 @@ typedef struct module_container_tag module_container_t; typedef struct reportxml_tag reportxml_t; typedef struct reportxml_node_tag reportxml_node_t; +typedef struct reportxml_database_tag reportxml_database_t; /* ---[ refobject.[ch] ]--- */ @@ -114,6 +115,7 @@ typedef union __attribute__ ((__transparent_union__)) { 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 index b6e19324..9767bb01 100644 --- a/src/reportxml.c +++ b/src/reportxml.c @@ -16,6 +16,9 @@ #include +#include "common/thread/thread.h" +#include "common/avl/avl.h" + #include "reportxml.h" #include "refobject.h" @@ -39,6 +42,12 @@ struct reportxml_node_tag { char *content; }; +struct reportxml_database_tag { + refobject_base_t __base; + mutex_t lock; + avl_tree *definitions; +}; + struct nodeattr; struct nodeattr { @@ -353,6 +362,7 @@ reportxml_node_t * reportxml_node_parse_xmlnode(xmlNodePtr xmlnode) 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; } @@ -405,6 +415,50 @@ reportxml_node_t * reportxml_node_parse_xmlnode(xmlNodePtr xmlnode) return node; } +reportxml_node_t * reportxml_node_copy(reportxml_node_t *node) +{ + reportxml_node_t *ret; + size_t count; + size_t i; + + if (!node) + return NULL; + + count = reportxml_node_count_child(node); + if (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)count; i++) { + reportxml_node_t *child = reportxml_node_copy(reportxml_node_get_child(node, i)); + + if (!child) { + refobject_unref(ret); + return NULL; + } + + if (reportxml_node_add_child(ret, child) != 0) { + refobject_unref(child); + refobject_unref(ret); + return NULL; + } + + refobject_unref(child); + } + + return ret; +} + xmlNodePtr reportxml_node_render_xmlnode(reportxml_node_t *node) { xmlNodePtr ret; @@ -604,3 +658,306 @@ char * reportxml_node_get_content(reportxml_node_t *node) return NULL; } } + +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; + size_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; +} + +reportxml_node_t * reportxml_database_build_node(reportxml_database_t *db, const char *id, ssize_t depth) +{ + reportxml_node_t *search; + reportxml_node_t *found; + reportxml_node_t *ret; + char *template; + size_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; + + 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); + + 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_t *copy = reportxml_node_copy(node); + + refobject_unref(node); + + if (!copy) { + refobject_unref(found); + refobject_unref(ret); + return NULL; + } + + if (reportxml_node_add_child(ret, copy) != 0) { + refobject_unref(found); + refobject_unref(ret); + return NULL; + } + + refobject_unref(copy); + } + + return ret; +} + +/* 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; + size_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(db, id, depth); + 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(); + } + + /* 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); + + /* 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 index acaea4ff..18713fd0 100644 --- a/src/reportxml.h +++ b/src/reportxml.h @@ -41,6 +41,7 @@ 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); @@ -51,4 +52,9 @@ reportxml_node_t * reportxml_node_get_child(reportxml_node_t *node, size_t int reportxml_node_set_content(reportxml_node_t *node, const char *value); char * reportxml_node_get_content(reportxml_node_t *node); +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 From d9686e701af48261c10ece8d33a9dc1e3a273eee Mon Sep 17 00:00:00 2001 From: Philipp Schafft Date: Fri, 8 Jun 2018 08:07:12 +0000 Subject: [PATCH 03/27] Fix: Free temp memory --- src/reportxml.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/reportxml.c b/src/reportxml.c index 9767bb01..49da67f6 100644 --- a/src/reportxml.c +++ b/src/reportxml.c @@ -802,6 +802,8 @@ reportxml_node_t * reportxml_database_build_node(reportxml_database_t *db, 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); From c4dca907abf07b56a971e09c369b62657cc90879 Mon Sep 17 00:00:00 2001 From: Philipp Schafft Date: Fri, 8 Jun 2018 08:46:45 +0000 Subject: [PATCH 04/27] Fix: Use correct data type --- src/reportxml.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/reportxml.c b/src/reportxml.c index 49da67f6..fcd242ef 100644 --- a/src/reportxml.c +++ b/src/reportxml.c @@ -418,7 +418,7 @@ reportxml_node_t * reportxml_node_parse_xmlnode(xmlNodePtr xmlnode) reportxml_node_t * reportxml_node_copy(reportxml_node_t *node) { reportxml_node_t *ret; - size_t count; + ssize_t count; size_t i; if (!node) @@ -710,7 +710,7 @@ reportxml_database_t * reportxml_database_new(void) int reportxml_database_add_report(reportxml_database_t *db, reportxml_t *report) { reportxml_node_t *root; - size_t count; + ssize_t count; size_t i; if (!db || !report) @@ -755,7 +755,7 @@ reportxml_node_t * reportxml_database_build_node(reportxml_database_t *db, reportxml_node_t *found; reportxml_node_t *ret; char *template; - size_t count; + ssize_t count; size_t i; if (!db || !id) @@ -854,7 +854,7 @@ reportxml_t * reportxml_database_build_report(reportxml_database_t *db reportxml_node_t *attach_to; reportxml_node_type_t type; reportxml_t *ret; - size_t count; + ssize_t count; size_t i; if (!db || !id) From ae489acc93226c968c43f7f6f02e8c42ffbea8bb Mon Sep 17 00:00:00 2001 From: Philipp Schafft Date: Fri, 8 Jun 2018 08:47:37 +0000 Subject: [PATCH 05/27] Fix: Ensure references are counted correctly in case reportxml_node_copy() fails --- src/reportxml.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/reportxml.c b/src/reportxml.c index fcd242ef..991e3041 100644 --- a/src/reportxml.c +++ b/src/reportxml.c @@ -440,15 +440,18 @@ reportxml_node_t * reportxml_node_copy(reportxml_node_t *node) } for (i = 0; i < (size_t)count; i++) { - reportxml_node_t *child = reportxml_node_copy(reportxml_node_get_child(node, i)); + reportxml_node_t *child = reportxml_node_get_child(node, i); + reportxml_node_t *copy = reportxml_node_copy(child); - if (!child) { + refobject_unref(child); + + if (!copy) { refobject_unref(ret); return NULL; } - if (reportxml_node_add_child(ret, child) != 0) { - refobject_unref(child); + if (reportxml_node_add_child(ret, copy) != 0) { + refobject_unref(copy); refobject_unref(ret); return NULL; } From a014f1c5cc4227a79d64e4b31abfea1a42202e64 Mon Sep 17 00:00:00 2001 From: Philipp Schafft Date: Fri, 8 Jun 2018 09:41:44 +0000 Subject: [PATCH 06/27] Feature: Added first level recursive definition lookup --- src/reportxml.c | 150 ++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 140 insertions(+), 10 deletions(-) diff --git a/src/reportxml.c b/src/reportxml.c index 991e3041..3ce10be1 100644 --- a/src/reportxml.c +++ b/src/reportxml.c @@ -66,6 +66,8 @@ struct nodedef { const struct nodeattr *attr[12]; }; +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", "xxx", 1, NULL, {"xxx", NULL}}}; @@ -752,11 +754,88 @@ int reportxml_database_add_report(reportxml_database_t *db, return 0; } -reportxml_node_t * reportxml_database_build_node(reportxml_database_t *db, const char *id, ssize_t depth) +static int __attach_copy_of_node_or_definition(reportxml_node_t *parent, reportxml_node_t *node, reportxml_database_t *db, ssize_t depth, const char *norec) +{ + 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) { + if (strcmp(definition, norec) == 0) { + /* we've already proccessed this. No need to look it up. */ + free(definition); + } else { + def = reportxml_database_build_node(db, definition, depth - 1); + ICECAST_LOG_DEBUG("Definition for \"%H\" at %p", definition, def); + if (!def) { + free(definition); + } + } + } + } + + if (def) { + ssize_t count = reportxml_node_count_child(def); + size_t i; + + ICECAST_LOG_DEBUG("Found definition."); + + if (count < 0) { + refobject_unref(def); + return -1; + } + + for (i = 0; i < (size_t)count; i++) { + reportxml_node_t *child = reportxml_node_get_child(def, i); + + if (__attach_copy_of_node_or_definition(parent, child, db, depth - 1, definition) != 0) { + refobject_unref(child); + refobject_unref(def); + free(definition); + return -1; + } + + refobject_unref(child); + } + + refobject_unref(def); + free(definition); + + return 0; + } else { + int ret; + + ICECAST_LOG_DEBUG("Found no definition."); + + copy = reportxml_node_copy(node); + if (!copy) + return -1; + + ret = reportxml_node_add_child(parent, copy); + + refobject_unref(copy); + + 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; @@ -768,6 +847,8 @@ reportxml_node_t * reportxml_database_build_node(reportxml_database_t *db, if (depth < 0) depth = 8; + ICECAST_LOG_DEBUG("Looking up \"%H\" in database %p with depth %zu", id, db, depth); + if (!depth) return NULL; @@ -826,28 +907,77 @@ reportxml_node_t * reportxml_database_build_node(reportxml_database_t *db, /* TODO: Look up definitions of our childs and childs' childs. */ reportxml_node_t *node = reportxml_node_get_child(found, i); - reportxml_node_t *copy = reportxml_node_copy(node); + 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, id) != 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); + } - if (!copy) { - refobject_unref(found); + refobject_unref(found); + + if (all_childs_same_type == ACST_YES) { + count = reportxml_node_count_child(ret); + if (count < 0) { refobject_unref(ret); return NULL; } - if (reportxml_node_add_child(ret, copy) != 0) { - refobject_unref(found); - refobject_unref(ret); - return NULL; - } + for (i = 0; i < (size_t)count; i++) { + reportxml_node_t *node = reportxml_node_get_child(ret, i); - refobject_unref(copy); + 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) { From 6f8270dcfbc126f12c459b303de1494af2d9adda Mon Sep 17 00:00:00 2001 From: Philipp Schafft Date: Fri, 8 Jun 2018 09:46:17 +0000 Subject: [PATCH 07/27] Update: Only look up first child if we really need to. --- src/reportxml.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/reportxml.c b/src/reportxml.c index 3ce10be1..1d749ca2 100644 --- a/src/reportxml.c +++ b/src/reportxml.c @@ -994,7 +994,7 @@ reportxml_t * reportxml_database_build_report(reportxml_database_t *db return NULL; /* first find the definition itself. This will be some REPORTXML_NODE_TYPE_DEFINITION node. */ - definition = reportxml_database_build_node(db, id, depth); + definition = __reportxml_database_build_node_ext(db, id, depth, &type); if (!definition) { ICECAST_LOG_WARN("No matching definition for \"%H\"", id); return NULL; @@ -1013,16 +1013,18 @@ reportxml_t * reportxml_database_build_report(reportxml_database_t *db return reportxml_new(); } - /* 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; - } + 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); + type = reportxml_node_get_type(child); + refobject_unref(child); + } /* check for supported configurations */ switch (type) { From 5d4430a64573b174f3a25474979bfbf63f430fd2 Mon Sep 17 00:00:00 2001 From: Philipp Schafft Date: Fri, 8 Jun 2018 09:51:00 +0000 Subject: [PATCH 08/27] Update: Made norec parameter optional --- src/reportxml.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/reportxml.c b/src/reportxml.c index 1d749ca2..23bc10bc 100644 --- a/src/reportxml.c +++ b/src/reportxml.c @@ -767,7 +767,7 @@ static int __attach_copy_of_node_or_definition(reportxml_node_t *parent, reportx if (depth >= 2) { definition = reportxml_node_get_attribute(node, "definition"); if (definition) { - if (strcmp(definition, norec) == 0) { + if (norec != NULL && strcmp(definition, norec) == 0) { /* we've already proccessed this. No need to look it up. */ free(definition); } else { From 93897aebb35795d39de17d8958aabbe92b8741da Mon Sep 17 00:00:00 2001 From: Philipp Schafft Date: Fri, 8 Jun 2018 11:21:47 +0000 Subject: [PATCH 09/27] Feature: Allow fully recursive lookup of definitions --- src/reportxml.c | 95 ++++++++++++++++++++++++++++++------------------- 1 file changed, 59 insertions(+), 36 deletions(-) diff --git a/src/reportxml.c b/src/reportxml.c index 23bc10bc..5e9195e8 100644 --- a/src/reportxml.c +++ b/src/reportxml.c @@ -66,6 +66,7 @@ struct nodedef { const struct nodeattr *attr[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}}}; @@ -73,6 +74,7 @@ static const struct nodeattr __attr_version[1] = {{"version", "CDA static const struct nodeattr __attr_xmlns[1] = {{"xmlns", "URI", "xxx", 1, NULL, {"xxx", 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}}}; @@ -98,7 +100,7 @@ static const struct nodeattr __attr__reference_type[1] = {{"type", 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 +#define __BASIC_ELEMENT __attr_id, __attr_definition, __attr_akindof, __attr__definition static const struct nodedef __nodedef[] = { {REPORTXML_NODE_TYPE_REPORT, "report", 0, {__attr_id, __attr_version, __attr_xmlns, __attr__eol}}, {REPORTXML_NODE_TYPE_DEFINITION, "definition", 0, {__BASIC_ELEMENT, __attr_template, __attr_defines, __attr__eol}}, @@ -417,7 +419,7 @@ reportxml_node_t * reportxml_node_parse_xmlnode(xmlNodePtr xmlnode) return node; } -reportxml_node_t * reportxml_node_copy(reportxml_node_t *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 count; @@ -443,32 +445,46 @@ reportxml_node_t * reportxml_node_copy(reportxml_node_t *node) for (i = 0; i < (size_t)count; i++) { reportxml_node_t *child = reportxml_node_get_child(node, i); - reportxml_node_t *copy = reportxml_node_copy(child); - refobject_unref(child); + 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); - if (!copy) { - refobject_unref(ret); - return NULL; + 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; + } } - - if (reportxml_node_add_child(ret, copy) != 0) { - refobject_unref(copy); - refobject_unref(ret); - return NULL; - } - - refobject_unref(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 count; size_t i; + xmlChar *definition; if (!node) return NULL; @@ -481,6 +497,13 @@ xmlNodePtr reportxml_node_render_xmlnode(reportxml_node_t *node) 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)count; i++) { reportxml_node_t *child = reportxml_node_get_child(node, i); xmlNodePtr xmlchild; @@ -600,7 +623,7 @@ int reportxml_node_add_child(reportxml_node_t *node, reportx node->childs[node->childs_len++] = child; - ICECAST_LOG_DEBUG("Child %p added to Node %p", child, node); +// ICECAST_LOG_DEBUG("Child %p added to Node %p", child, node); return 0; } @@ -754,7 +777,7 @@ int reportxml_database_add_report(reportxml_database_t *db, 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, const char *norec) +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; @@ -767,16 +790,10 @@ static int __attach_copy_of_node_or_definition(reportxml_node_t *parent, reportx if (depth >= 2) { definition = reportxml_node_get_attribute(node, "definition"); if (definition) { - if (norec != NULL && strcmp(definition, norec) == 0) { - /* we've already proccessed this. No need to look it up. */ - free(definition); - } else { - def = reportxml_database_build_node(db, definition, depth - 1); - ICECAST_LOG_DEBUG("Definition for \"%H\" at %p", definition, def); - if (!def) { - free(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); } } @@ -784,20 +801,23 @@ static int __attach_copy_of_node_or_definition(reportxml_node_t *parent, reportx ssize_t count = reportxml_node_count_child(def); size_t i; - ICECAST_LOG_DEBUG("Found definition."); + 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); - if (__attach_copy_of_node_or_definition(parent, child, db, depth - 1, definition) != 0) { + 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); - free(definition); + ICECAST_LOG_DEBUG("parent=%p, node=%p, depth=%zi <- -1", parent, node, depth); return -1; } @@ -805,22 +825,25 @@ static int __attach_copy_of_node_or_definition(reportxml_node_t *parent, reportx } refobject_unref(def); - free(definition); + ICECAST_LOG_DEBUG("parent=%p, node=%p, depth=%zi <- 0", parent, node, depth); return 0; } else { int ret; - ICECAST_LOG_DEBUG("Found no definition."); + ICECAST_LOG_DEBUG("parent=%p, node=%p, depth=%zi, Found no definition.", parent, node, depth); - copy = reportxml_node_copy(node); - if (!copy) + 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; } } @@ -924,7 +947,7 @@ static reportxml_node_t * __reportxml_database_build_node_ext(reportxml_dat } /* 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, id) != 0) { + if (__attach_copy_of_node_or_definition(ret, node, db, depth) != 0) { refobject_unref(node); refobject_unref(found); refobject_unref(ret); @@ -952,7 +975,7 @@ static reportxml_node_t * __reportxml_database_build_node_ext(reportxml_dat return NULL; } - if (reportxml_node_set_attribute(node, "definition", id) != 0) { + if (reportxml_node_set_attribute(node, "_definition", id) != 0) { refobject_unref(node); refobject_unref(ret); return NULL; From 6f9c20e2309e2dba48c5b6119752c32a50fc6ce7 Mon Sep 17 00:00:00 2001 From: Philipp Schafft Date: Fri, 8 Jun 2018 12:03:46 +0000 Subject: [PATCH 10/27] Feature: Added global (in config) reportxml database --- src/cfgfile.c | 27 +++++++++++++++++++++++++++ src/cfgfile.h | 1 + 2 files changed, 28 insertions(+) diff --git a/src/cfgfile.c b/src/cfgfile.c index 2dd2e547..361de81d 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,27 @@ 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); + } + } + 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; From fecc38452a2281815136e5e1f67e6161df3684c9 Mon Sep 17 00:00:00 2001 From: Philipp Schafft Date: Fri, 8 Jun 2018 14:46:16 +0000 Subject: [PATCH 11/27] Update: Added useful error message in case of unknown node name --- src/reportxml.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/reportxml.c b/src/reportxml.c index 5e9195e8..f3eaf7d8 100644 --- a/src/reportxml.c +++ b/src/reportxml.c @@ -347,8 +347,10 @@ reportxml_node_t * reportxml_node_parse_xmlnode(xmlNodePtr xmlnode) return NULL; nodedef = __get_nodedef_by_name((const char *)xmlnode->name); - if (!nodedef) + 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) From c1c5bd02e366d9940b34ddbe120ef1a5a0f73fd7 Mon Sep 17 00:00:00 2001 From: Philipp Schafft Date: Fri, 8 Jun 2018 14:48:28 +0000 Subject: [PATCH 12/27] Fix: Corrected type detection logic for mixed-type s --- src/reportxml.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/reportxml.c b/src/reportxml.c index f3eaf7d8..21a4e94b 100644 --- a/src/reportxml.c +++ b/src/reportxml.c @@ -1038,7 +1038,7 @@ reportxml_t * reportxml_database_build_report(reportxml_database_t *db return reportxml_new(); } - if (type != REPORTXML_NODE_TYPE__ERROR) { + 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) { From d6a95302384bca5f900111b81ca3401655b6e981 Mon Sep 17 00:00:00 2001 From: Philipp Schafft Date: Fri, 8 Jun 2018 15:18:44 +0000 Subject: [PATCH 13/27] Update: Added useful logging in case report xml database has been loaded --- src/cfgfile.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/cfgfile.c b/src/cfgfile.c index 361de81d..dc1feb43 100644 --- a/src/cfgfile.c +++ b/src/cfgfile.c @@ -2100,6 +2100,7 @@ static void _parse_paths(xmlDocPtr doc, 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); From 32a410bdd6e593bfe6c0d13583f911fca7df876d Mon Sep 17 00:00:00 2001 From: Philipp Schafft Date: Sat, 9 Jun 2018 09:22:25 +0000 Subject: [PATCH 14/27] Feature: Allow xslt_transform() to send non-200 status --- src/admin.c | 2 +- src/stats.c | 2 +- src/xslt.c | 8 ++++---- src/xslt.h | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) 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/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..12de9376 100644 --- a/src/xslt.c +++ b/src/xslt.c @@ -285,7 +285,7 @@ static xmlDocPtr custom_loader(const xmlChar *URI, return ret; } -void xslt_transform(xmlDocPtr doc, const char *xslfilename, client_t *client) +void xslt_transform(xmlDocPtr doc, const char *xslfilename, client_t *client, int status) { xmlDocPtr res; xsltStylesheetPtr cur; @@ -346,7 +346,7 @@ 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); @@ -359,7 +359,7 @@ 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); @@ -375,7 +375,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); 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); From 86a62889d249f395d2d9d412159015c333f7cfa8 Mon Sep 17 00:00:00 2001 From: Philipp Schafft Date: Wed, 27 Jun 2018 10:00:21 +0000 Subject: [PATCH 15/27] Feature: Added a way to find a node by attribute (useful for IDs). --- src/reportxml.c | 42 ++++++++++++++++++++++++++++++++++++++++++ src/reportxml.h | 2 ++ 2 files changed, 44 insertions(+) diff --git a/src/reportxml.c b/src/reportxml.c index 21a4e94b..b175b98b 100644 --- a/src/reportxml.c +++ b/src/reportxml.c @@ -217,6 +217,14 @@ reportxml_node_t * reportxml_get_root_node(reportxml_t *report) 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; @@ -652,6 +660,40 @@ reportxml_node_t * reportxml_node_get_child(reportxml_node_t *node, size_t 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; diff --git a/src/reportxml.h b/src/reportxml.h index 18713fd0..950db43b 100644 --- a/src/reportxml.h +++ b/src/reportxml.h @@ -36,6 +36,7 @@ typedef enum { 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); @@ -49,6 +50,7 @@ char * reportxml_node_get_attribute(reportxml_node_t *node, con 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); From 52429c714f7a453b110177ae13b36414eeaeeb17 Mon Sep 17 00:00:00 2001 From: Philipp Schafft Date: Wed, 27 Jun 2018 10:40:25 +0000 Subject: [PATCH 16/27] Feature: Allow attaching XML to extentions --- src/reportxml.c | 130 ++++++++++++++++++++++++++++++++++++++---------- src/reportxml.h | 3 ++ 2 files changed, 106 insertions(+), 27 deletions(-) diff --git a/src/reportxml.c b/src/reportxml.c index b175b98b..27a099dd 100644 --- a/src/reportxml.c +++ b/src/reportxml.c @@ -39,6 +39,8 @@ struct reportxml_node_tag { reportxml_node_type_t type; reportxml_node_t **childs; size_t childs_len; + xmlNodePtr *xml_childs; + size_t xml_childs_len; char *content; }; @@ -290,8 +292,13 @@ static void __report_node_free(refobject_t self, void **userdata) 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) @@ -388,40 +395,47 @@ reportxml_node_t * reportxml_node_parse_xmlnode(xmlNodePtr xmlnode) xmlNodePtr cur = xmlnode->xmlChildrenNode; do { - reportxml_node_t *child; + if (node->type == REPORTXML_NODE_TYPE_EXTENSION) { + if (reportxml_node_add_xml_child(node, cur) != 0) { + refobject_unref(node); + return NULL; + } + } else { + reportxml_node_t *child; - if (xmlIsBlankNode(cur)) - continue; + if (xmlIsBlankNode(cur)) + continue; - if (cur->type == XML_COMMENT_NODE) - continue; + if (cur->type == XML_COMMENT_NODE) + continue; - if (cur->type == XML_TEXT_NODE) { - xmlChar *value = xmlNodeListGetString(xmlnode->doc, cur, 1); + if (cur->type == XML_TEXT_NODE) { + xmlChar *value = xmlNodeListGetString(xmlnode->doc, cur, 1); - if (!value) { + 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_set_content(node, (const char *)value) != 0) { + if (reportxml_node_add_child(node, child) != 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)); } @@ -492,15 +506,20 @@ reportxml_node_t * reportxml_node_copy(reportxml_node_t *node) xmlNodePtr reportxml_node_render_xmlnode(reportxml_node_t *node) { xmlNodePtr ret; - ssize_t count; + ssize_t child_count; + ssize_t xml_child_count; size_t i; xmlChar *definition; if (!node) return NULL; - count = reportxml_node_count_child(node); - if (count < 0) + 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); @@ -514,7 +533,7 @@ xmlNodePtr reportxml_node_render_xmlnode(reportxml_node_t *node) xmlFree(definition); } - for (i = 0; i < (size_t)count; i++) { + for (i = 0; i < (size_t)child_count; i++) { reportxml_node_t *child = reportxml_node_get_child(node, i); xmlNodePtr xmlchild; @@ -544,6 +563,18 @@ xmlNodePtr reportxml_node_render_xmlnode(reportxml_node_t *node) 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; } @@ -731,6 +762,51 @@ char * reportxml_node_get_content(reportxml_node_t *node) } } +int reportxml_node_add_xml_child(reportxml_node_t *node, xmlNodePtr child) +{ + xmlNodePtr *n; + + if (!node || !child) + 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 *); diff --git a/src/reportxml.h b/src/reportxml.h index 950db43b..31d71f93 100644 --- a/src/reportxml.h +++ b/src/reportxml.h @@ -53,6 +53,9 @@ reportxml_node_t * reportxml_node_get_child(reportxml_node_t *node, size_t 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); From e24e524cb5fc148728592e643e61fb03fe71b991 Mon Sep 17 00:00:00 2001 From: Philipp Schafft Date: Wed, 27 Jun 2018 10:47:55 +0000 Subject: [PATCH 17/27] Fix: Access correct object --- src/reportxml.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/reportxml.c b/src/reportxml.c index 27a099dd..17c8e24f 100644 --- a/src/reportxml.c +++ b/src/reportxml.c @@ -395,7 +395,7 @@ reportxml_node_t * reportxml_node_parse_xmlnode(xmlNodePtr xmlnode) xmlNodePtr cur = xmlnode->xmlChildrenNode; do { - if (node->type == REPORTXML_NODE_TYPE_EXTENSION) { + if (nodedef->type == REPORTXML_NODE_TYPE_EXTENSION) { if (reportxml_node_add_xml_child(node, cur) != 0) { refobject_unref(node); return NULL; From 7ffe12bc8be17bad0bcf7537df219425223be44f Mon Sep 17 00:00:00 2001 From: Philipp Schafft Date: Wed, 27 Jun 2018 11:11:55 +0000 Subject: [PATCH 18/27] Fix: Also copy XML childs when doing a node copy --- src/reportxml.c | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/src/reportxml.c b/src/reportxml.c index 17c8e24f..9f90177c 100644 --- a/src/reportxml.c +++ b/src/reportxml.c @@ -446,14 +446,19 @@ reportxml_node_t * reportxml_node_parse_xmlnode(xmlNodePtr xmlnode) 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 count; + ssize_t child_count; + ssize_t xml_child_count; size_t i; if (!node) return NULL; - count = reportxml_node_count_child(node); - if (count < 0) + 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); @@ -467,7 +472,7 @@ static reportxml_node_t * __reportxml_node_copy_with_db(reportxml_node_t *n } } - for (i = 0; i < (size_t)count; i++) { + for (i = 0; i < (size_t)child_count; i++) { reportxml_node_t *child = reportxml_node_get_child(node, i); if (db && depth > 0) { @@ -495,6 +500,16 @@ static reportxml_node_t * __reportxml_node_copy_with_db(reportxml_node_t *n } } + 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; } From 5eac0d80cab5b2919d9644e29c8e9764e2bdec06 Mon Sep 17 00:00:00 2001 From: Philipp Schafft Date: Wed, 27 Jun 2018 11:36:25 +0000 Subject: [PATCH 19/27] Feature: Allow only the correct type of content per node --- src/reportxml.c | 57 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 38 insertions(+), 19 deletions(-) diff --git a/src/reportxml.c b/src/reportxml.c index 9f90177c..fdce4056 100644 --- a/src/reportxml.c +++ b/src/reportxml.c @@ -61,10 +61,17 @@ struct nodeattr { const char *values[32]; }; +enum nodecontent { + NC_NONE, + NC_CHILDS, + NC_CONTENT, + NC_XML +}; + struct nodedef { reportxml_node_type_t type; const char *name; - int has_content; + enum nodecontent content; const struct nodeattr *attr[12]; }; @@ -104,22 +111,22 @@ static const struct nodeattr __attr__reference_type[1] = {{"type", NULL */ #define __BASIC_ELEMENT __attr_id, __attr_definition, __attr_akindof, __attr__definition static const struct nodedef __nodedef[] = { - {REPORTXML_NODE_TYPE_REPORT, "report", 0, {__attr_id, __attr_version, __attr_xmlns, __attr__eol}}, - {REPORTXML_NODE_TYPE_DEFINITION, "definition", 0, {__BASIC_ELEMENT, __attr_template, __attr_defines, __attr__eol}}, - {REPORTXML_NODE_TYPE_INCIDENT, "incident", 0, {__BASIC_ELEMENT, __attr__eol}}, - {REPORTXML_NODE_TYPE_STATE, "state", 0, {__BASIC_ELEMENT, __attr__eol}}, - {REPORTXML_NODE_TYPE_BACKTRACE, "backtrace", 0, {__BASIC_ELEMENT, __attr__eol}}, - {REPORTXML_NODE_TYPE_POSITION, "position", 0, {__BASIC_ELEMENT, __attr_function, __attr_filename, __attr_line, __attr_binary, __attr_offset, __attr__eol}}, - {REPORTXML_NODE_TYPE_MORE, "more", 0, {__BASIC_ELEMENT, __attr__eol}}, - {REPORTXML_NODE_TYPE_FIX, "fix", 0, {__BASIC_ELEMENT, __attr__eol}}, - {REPORTXML_NODE_TYPE_ACTION, "action", 0, {__BASIC_ELEMENT, __attr__action_type, __attr__eol}}, - {REPORTXML_NODE_TYPE_REASON, "reason", 0, {__BASIC_ELEMENT, __attr__eol}}, - {REPORTXML_NODE_TYPE_TEXT, "text", 1, {__BASIC_ELEMENT, __attr_lang, __attr_dir, __attr__eol}}, - {REPORTXML_NODE_TYPE_TIMESTAMP, "timestamp", 0, {__BASIC_ELEMENT, __attr_absolute, __attr_relative, __attr__eol}}, - {REPORTXML_NODE_TYPE_RESOURCE, "resource", 0, {__BASIC_ELEMENT, __attr__resource_type, __attr_name, __attr__eol}}, - {REPORTXML_NODE_TYPE_VALUE, "value", 0, {__BASIC_ELEMENT, __attr_member, __attr_value, __attr_state, __attr__value_type, __attr__eol}}, - {REPORTXML_NODE_TYPE_REFERENCE, "reference", 0, {__BASIC_ELEMENT, __attr__reference_type, __attr_href, __attr__eol}}, - {REPORTXML_NODE_TYPE_EXTENSION, "extension", 0, {__BASIC_ELEMENT, __attr__eol}}, + {REPORTXML_NODE_TYPE_REPORT, "report", NC_CHILDS, {__attr_id, __attr_version, __attr_xmlns, __attr__eol}}, + {REPORTXML_NODE_TYPE_DEFINITION, "definition", NC_CHILDS, {__BASIC_ELEMENT, __attr_template, __attr_defines, __attr__eol}}, + {REPORTXML_NODE_TYPE_INCIDENT, "incident", NC_CHILDS, {__BASIC_ELEMENT, __attr__eol}}, + {REPORTXML_NODE_TYPE_STATE, "state", NC_CHILDS, {__BASIC_ELEMENT, __attr__eol}}, + {REPORTXML_NODE_TYPE_BACKTRACE, "backtrace", NC_CHILDS, {__BASIC_ELEMENT, __attr__eol}}, + {REPORTXML_NODE_TYPE_POSITION, "position", NC_CHILDS, {__BASIC_ELEMENT, __attr_function, __attr_filename, __attr_line, __attr_binary, __attr_offset, __attr__eol}}, + {REPORTXML_NODE_TYPE_MORE, "more", NC_CHILDS, {__BASIC_ELEMENT, __attr__eol}}, + {REPORTXML_NODE_TYPE_FIX, "fix", NC_CHILDS, {__BASIC_ELEMENT, __attr__eol}}, + {REPORTXML_NODE_TYPE_ACTION, "action", NC_CHILDS, {__BASIC_ELEMENT, __attr__action_type, __attr__eol}}, + {REPORTXML_NODE_TYPE_REASON, "reason", NC_CHILDS, {__BASIC_ELEMENT, __attr__eol}}, + {REPORTXML_NODE_TYPE_TEXT, "text", NC_CONTENT, {__BASIC_ELEMENT, __attr_lang, __attr_dir, __attr__eol}}, + {REPORTXML_NODE_TYPE_TIMESTAMP, "timestamp", NC_NONE, {__BASIC_ELEMENT, __attr_absolute, __attr_relative, __attr__eol}}, + {REPORTXML_NODE_TYPE_RESOURCE, "resource", NC_CHILDS, {__BASIC_ELEMENT, __attr__resource_type, __attr_name, __attr__eol}}, + {REPORTXML_NODE_TYPE_VALUE, "value", NC_CHILDS, {__BASIC_ELEMENT, __attr_member, __attr_value, __attr_state, __attr__value_type, __attr__eol}}, + {REPORTXML_NODE_TYPE_REFERENCE, "reference", NC_CHILDS, {__BASIC_ELEMENT, __attr__reference_type, __attr_href, __attr__eol}}, + {REPORTXML_NODE_TYPE_EXTENSION, "extension", NC_XML, {__BASIC_ELEMENT, __attr__eol}}, }; static const struct nodedef * __get_nodedef(reportxml_node_type_t type) @@ -395,7 +402,7 @@ reportxml_node_t * reportxml_node_parse_xmlnode(xmlNodePtr xmlnode) xmlNodePtr cur = xmlnode->xmlChildrenNode; do { - if (nodedef->type == REPORTXML_NODE_TYPE_EXTENSION) { + if (nodedef->content == NC_XML) { if (reportxml_node_add_xml_child(node, cur) != 0) { refobject_unref(node); return NULL; @@ -663,11 +670,17 @@ char * reportxml_node_get_attribute(reportxml_node_t *node, con int reportxml_node_add_child(reportxml_node_t *node, reportxml_node_t *child) { + const struct nodedef *nodedef; reportxml_node_t **n; if (!node || !child) return -1; + nodedef = __get_nodedef(node->type); + + if (nodedef->content != NC_CHILDS) + return -1; + n = realloc(node->childs, sizeof(*n)*(node->childs_len + 1)); if (!n) return -1; @@ -750,7 +763,7 @@ int reportxml_node_set_content(reportxml_node_t *node, const nodedef = __get_nodedef(node->type); - if (!nodedef->has_content) + if (nodedef->content != NC_CONTENT) return -1; if (value) { @@ -779,11 +792,17 @@ char * reportxml_node_get_content(reportxml_node_t *node) 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; From 8ea908e332769f695365fd8533e66351b8d0aba5 Mon Sep 17 00:00:00 2001 From: Philipp Schafft Date: Wed, 27 Jun 2018 12:26:45 +0000 Subject: [PATCH 20/27] Feature: Only allow those nodes as childs that are allowed as per specs --- src/reportxml.c | 62 ++++++++++++++++++++++++++++++++++++------------- 1 file changed, 46 insertions(+), 16 deletions(-) diff --git a/src/reportxml.c b/src/reportxml.c index fdce4056..a0906547 100644 --- a/src/reportxml.c +++ b/src/reportxml.c @@ -73,6 +73,7 @@ struct nodedef { 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); @@ -111,22 +112,38 @@ static const struct nodeattr __attr__reference_type[1] = {{"type", NULL */ #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_DEFINITION, "definition", NC_CHILDS, {__BASIC_ELEMENT, __attr_template, __attr_defines, __attr__eol}}, - {REPORTXML_NODE_TYPE_INCIDENT, "incident", NC_CHILDS, {__BASIC_ELEMENT, __attr__eol}}, - {REPORTXML_NODE_TYPE_STATE, "state", NC_CHILDS, {__BASIC_ELEMENT, __attr__eol}}, - {REPORTXML_NODE_TYPE_BACKTRACE, "backtrace", NC_CHILDS, {__BASIC_ELEMENT, __attr__eol}}, - {REPORTXML_NODE_TYPE_POSITION, "position", NC_CHILDS, {__BASIC_ELEMENT, __attr_function, __attr_filename, __attr_line, __attr_binary, __attr_offset, __attr__eol}}, - {REPORTXML_NODE_TYPE_MORE, "more", NC_CHILDS, {__BASIC_ELEMENT, __attr__eol}}, - {REPORTXML_NODE_TYPE_FIX, "fix", NC_CHILDS, {__BASIC_ELEMENT, __attr__eol}}, - {REPORTXML_NODE_TYPE_ACTION, "action", NC_CHILDS, {__BASIC_ELEMENT, __attr__action_type, __attr__eol}}, - {REPORTXML_NODE_TYPE_REASON, "reason", NC_CHILDS, {__BASIC_ELEMENT, __attr__eol}}, - {REPORTXML_NODE_TYPE_TEXT, "text", NC_CONTENT, {__BASIC_ELEMENT, __attr_lang, __attr_dir, __attr__eol}}, - {REPORTXML_NODE_TYPE_TIMESTAMP, "timestamp", NC_NONE, {__BASIC_ELEMENT, __attr_absolute, __attr_relative, __attr__eol}}, - {REPORTXML_NODE_TYPE_RESOURCE, "resource", NC_CHILDS, {__BASIC_ELEMENT, __attr__resource_type, __attr_name, __attr__eol}}, - {REPORTXML_NODE_TYPE_VALUE, "value", NC_CHILDS, {__BASIC_ELEMENT, __attr_member, __attr_value, __attr_state, __attr__value_type, __attr__eol}}, - {REPORTXML_NODE_TYPE_REFERENCE, "reference", NC_CHILDS, {__BASIC_ELEMENT, __attr__reference_type, __attr_href, __attr__eol}}, - {REPORTXML_NODE_TYPE_EXTENSION, "extension", NC_XML, {__BASIC_ELEMENT, __attr__eol}}, + {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__eol}, + {REPORTXML_NODE_TYPE__ERROR}} }; static const struct nodedef * __get_nodedef(reportxml_node_type_t type) @@ -672,6 +689,8 @@ int reportxml_node_add_child(reportxml_node_t *node, reportx { const struct nodedef *nodedef; reportxml_node_t **n; + size_t i; + int found; if (!node || !child) return -1; @@ -681,6 +700,17 @@ int reportxml_node_add_child(reportxml_node_t *node, reportx 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; From 7fadb6ce8650afd932543caa7035e3af87cf06f1 Mon Sep 17 00:00:00 2001 From: Philipp Schafft Date: Wed, 27 Jun 2018 12:45:27 +0000 Subject: [PATCH 21/27] 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); From 6168573e867296415a5e1adad84b2c5469d8fc1c Mon Sep 17 00:00:00 2001 From: Philipp Schafft Date: Wed, 27 Jun 2018 16:07:21 +0000 Subject: [PATCH 22/27] Feature: Render error messages via Report XML and XSLT --- admin/error-html.xsl | 17 +++++++ admin/error-plaintext.xsl | 10 ++++ src/client.c | 100 ++++++++++++++++++-------------------- src/client.h | 2 + src/errors.c | 5 +- src/errors.h | 3 +- src/xslt.c | 19 ++++++-- 7 files changed, 96 insertions(+), 60 deletions(-) create mode 100644 admin/error-html.xsl create mode 100644 admin/error-plaintext.xsl diff --git a/admin/error-html.xsl b/admin/error-html.xsl new file mode 100644 index 00000000..98c19417 --- /dev/null +++ b/admin/error-html.xsl @@ -0,0 +1,17 @@ + + + + Error + + +
+ +
+

Response

+

Message

+

+
+
+
+
+
diff --git a/admin/error-plaintext.xsl b/admin/error-plaintext.xsl new file mode 100644 index 00000000..cece3b4a --- /dev/null +++ b/admin/error-plaintext.xsl @@ -0,0 +1,10 @@ + + + + + Report: + + + + + diff --git a/src/client.c b/src/client.c index 9f694518..ffc80d39 100644 --- a/src/client.c +++ b/src/client.c @@ -239,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 %i

Error %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) diff --git a/src/client.h b/src/client.h index 08757304..f0aab70b 100644 --- a/src/client.h +++ b/src/client.h @@ -29,6 +29,8 @@ #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 { 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/xslt.c b/src/xslt.c index 12de9376..366ed4c2 100644 --- a/src/xslt.c +++ b/src/xslt.c @@ -285,6 +285,15 @@ static xmlDocPtr custom_loader(const xmlChar *URI, return ret; } +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; @@ -305,7 +314,7 @@ void xslt_transform(xmlDocPtr doc, const char *xslfilename, client_t *client, in { 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; } @@ -349,7 +358,7 @@ void xslt_transform(xmlDocPtr doc, const char *xslfilename, client_t *client, in 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; @@ -362,12 +371,12 @@ void xslt_transform(xmlDocPtr doc, const char *xslfilename, client_t *client, in 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; } } @@ -387,7 +396,7 @@ void xslt_transform(xmlDocPtr doc, const char *xslfilename, client_t *client, in 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); From 4188bd899fd22de5bd5f7c894d31e8c2cc51a764 Mon Sep 17 00:00:00 2001 From: Philipp Schafft Date: Fri, 29 Jun 2018 09:39:43 +0000 Subject: [PATCH 23/27] Feature: Add definition ID to the first child of generated reports --- src/reportxml.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/reportxml.c b/src/reportxml.c index a0906547..c4974b73 100644 --- a/src/reportxml.c +++ b/src/reportxml.c @@ -1288,6 +1288,11 @@ reportxml_t * reportxml_database_build_report(reportxml_database_t *db for (i = 0; i < (size_t)count; i++) { child = reportxml_node_get_child(definition, i); + if (i == 0) { + /* Attach definition to the first child only. */ + 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); From 0c7073f04f864ef4874aed4b19efdf9bc44bf998 Mon Sep 17 00:00:00 2001 From: Philipp Schafft Date: Fri, 29 Jun 2018 09:42:25 +0000 Subject: [PATCH 24/27] Update: Attach definition to childs based on their type --- src/reportxml.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/reportxml.c b/src/reportxml.c index c4974b73..77eb0c03 100644 --- a/src/reportxml.c +++ b/src/reportxml.c @@ -1288,8 +1288,11 @@ reportxml_t * reportxml_database_build_report(reportxml_database_t *db for (i = 0; i < (size_t)count; i++) { child = reportxml_node_get_child(definition, i); - if (i == 0) { - /* Attach definition to the first child only. */ + 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); } From 962805df82ab1ad777242c5440e9497ce6340a3f Mon Sep 17 00:00:00 2001 From: Philipp Schafft Date: Fri, 29 Jun 2018 10:46:37 +0000 Subject: [PATCH 25/27] Update: Added attribute "application" for node "extension" --- src/reportxml.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/reportxml.c b/src/reportxml.c index 77eb0c03..a6c0d3a3 100644 --- a/src/reportxml.c +++ b/src/reportxml.c @@ -102,6 +102,7 @@ static const struct nodeattr __attr_member[1] = {{"member", "CDA 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}}}; @@ -142,7 +143,7 @@ static const struct nodedef __nodedef[] = { {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__eol}, + {REPORTXML_NODE_TYPE_EXTENSION, "extension", NC_XML, {__BASIC_ELEMENT, __attr_application, __attr__eol}, {REPORTXML_NODE_TYPE__ERROR}} }; From 4e7985c168d2fcb06b34213e66ad31bc77be9bab Mon Sep 17 00:00:00 2001 From: Philipp Schafft Date: Fri, 29 Jun 2018 13:30:30 +0000 Subject: [PATCH 26/27] Fix: Allocate XML namespace --- src/reportxml.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/reportxml.c b/src/reportxml.c index a6c0d3a3..51d39fe3 100644 --- a/src/reportxml.c +++ b/src/reportxml.c @@ -81,7 +81,7 @@ static reportxml_node_t * __reportxml_database_build_node_ext(reportxml_dat 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", "xxx", 1, NULL, {"xxx", 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 */ From bc6a960315b9bba5fae78cd7430e4f1b8b6f2a39 Mon Sep 17 00:00:00 2001 From: Philipp Schafft Date: Fri, 29 Jun 2018 14:26:31 +0000 Subject: [PATCH 27/27] Feature: Display error code if known --- admin/error-html.xsl | 3 +++ admin/error-plaintext.xsl | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/admin/error-html.xsl b/admin/error-html.xsl index 98c19417..3d65d5d5 100644 --- a/admin/error-html.xsl +++ b/admin/error-html.xsl @@ -10,6 +10,9 @@

Response

Message

+ +

Error code:

+
diff --git a/admin/error-plaintext.xsl b/admin/error-plaintext.xsl index cece3b4a..5640baf8 100644 --- a/admin/error-plaintext.xsl +++ b/admin/error-plaintext.xsl @@ -5,6 +5,11 @@ Report: + + Error code: + + +