diff --git a/admin/includes/header.xsl b/admin/includes/header.xsl
index 323919d2..f3f7dc6a 100644
--- a/admin/includes/header.xsl
+++ b/admin/includes/header.xsl
@@ -14,6 +14,11 @@
diff --git a/src/admin.c b/src/admin.c
index 382bf1ba..69463a1f 100644
--- a/src/admin.c
+++ b/src/admin.c
@@ -317,6 +317,17 @@ int admin_command_table_unregister(const char *prefix)
return -1;
}
+/* build an XML root node including some common tags */
+xmlNodePtr admin_build_rootnode(xmlDocPtr doc, const char *name)
+{
+ xmlNodePtr rootnode = xmlNewDocNode(doc, NULL, XMLSTR(name), NULL);
+ xmlNodePtr modules = module_container_get_modulelist_as_xml(global.modulecontainer);
+
+ xmlAddChild(rootnode, modules);
+
+ return rootnode;
+}
+
/* build an XML doc containing information about currently running sources.
* If a mountpoint is passed then that source will not be added to the XML
* doc even if the source is running */
@@ -330,7 +341,7 @@ xmlDocPtr admin_build_sourcelist(const char *mount)
time_t now = time(NULL);
doc = xmlNewDoc (XMLSTR("1.0"));
- xmlnode = xmlNewDocNode (doc, NULL, XMLSTR("icestats"), NULL);
+ xmlnode = admin_build_rootnode(doc, "icestats");
xmlDocSetRootElement(doc, xmlnode);
if (mount) {
@@ -649,7 +660,7 @@ static void command_move_clients(client_t *client,
ICECAST_LOG_INFO("source is \"%s\", destination is \"%s\"", source->mount, dest->mount);
doc = xmlNewDoc(XMLSTR("1.0"));
- node = xmlNewDocNode(doc, NULL, XMLSTR("iceresponse"), NULL);
+ node = admin_build_rootnode(doc, "iceresponse");
xmlDocSetRootElement(doc, node);
source_move_clients(source, dest);
@@ -744,7 +755,7 @@ static void command_show_listeners(client_t *client,
char buf[22];
doc = xmlNewDoc(XMLSTR("1.0"));
- node = xmlNewDocNode(doc, NULL, XMLSTR("icestats"), NULL);
+ node = admin_build_rootnode(doc, "icestats");
srcnode = xmlNewChild(node, NULL, XMLSTR("source"), NULL);
xmlSetProp(srcnode, XMLSTR("mount"), XMLSTR(source->mount));
xmlDocSetRootElement(doc, node);
@@ -902,12 +913,12 @@ static void command_manageauth(client_t *client, source_t *source, admin_format_
}
doc = xmlNewDoc(XMLSTR("1.0"));
- node = xmlNewDocNode(doc, NULL, XMLSTR("icestats"), NULL);
+ node = admin_build_rootnode(doc, "icestats");
rolenode = admin_add_role_to_authentication(auth, node);
if (message) {
- msgnode = xmlNewChild(node, NULL, XMLSTR("iceresponse"), NULL);
+ msgnode = admin_build_rootnode(doc, "iceresponse");
xmlNewTextChild(msgnode, NULL, XMLSTR("message"), XMLSTR(message));
}
@@ -941,7 +952,7 @@ static void command_kill_source(client_t *client,
xmlNodePtr node;
doc = xmlNewDoc (XMLSTR("1.0"));
- node = xmlNewDocNode(doc, NULL, XMLSTR("iceresponse"), NULL);
+ node = admin_build_rootnode(doc, "iceresponse");
xmlNewTextChild(node, NULL, XMLSTR("message"), XMLSTR("Source Removed"));
xmlNewTextChild(node, NULL, XMLSTR("return"), XMLSTR("1"));
xmlDocSetRootElement(doc, node);
@@ -971,7 +982,7 @@ static void command_kill_client(client_t *client,
listener = source_find_client(source, id);
doc = xmlNewDoc(XMLSTR("1.0"));
- node = xmlNewDocNode(doc, NULL, XMLSTR("iceresponse"), NULL);
+ node = admin_build_rootnode(doc, "iceresponse");
xmlDocSetRootElement(doc, node);
ICECAST_LOG_DEBUG("Response is %d", response);
@@ -1028,7 +1039,7 @@ static void command_metadata(client_t *client,
int same_ip = 1;
doc = xmlNewDoc(XMLSTR("1.0"));
- node = xmlNewDocNode(doc, NULL, XMLSTR("iceresponse"), NULL);
+ node = admin_build_rootnode(doc, "iceresponse");
xmlDocSetRootElement(doc, node);
ICECAST_LOG_DEBUG("Got metadata update request");
@@ -1155,7 +1166,7 @@ static void command_queue_reload(client_t *client, source_t *source, admin_forma
global_unlock();
doc = xmlNewDoc (XMLSTR("1.0"));
- node = xmlNewDocNode(doc, NULL, XMLSTR("iceresponse"), NULL);
+ node = admin_build_rootnode(doc, "iceresponse");
xmlNewTextChild(node, NULL, XMLSTR("message"), XMLSTR("Config reload queued"));
xmlNewTextChild(node, NULL, XMLSTR("return"), XMLSTR("1"));
xmlDocSetRootElement(doc, node);
@@ -1207,7 +1218,7 @@ static void command_updatemetadata(client_t *client,
xmlNodePtr node, srcnode;
doc = xmlNewDoc(XMLSTR("1.0"));
- node = xmlNewDocNode(doc, NULL, XMLSTR("icestats"), NULL);
+ node = admin_build_rootnode(doc, "icestats");
srcnode = xmlNewChild(node, NULL, XMLSTR("source"), NULL);
xmlSetProp(srcnode, XMLSTR("mount"), XMLSTR(source->mount));
xmlDocSetRootElement(doc, node);
diff --git a/src/admin.h b/src/admin.h
index 6d4b03c6..1245a194 100644
--- a/src/admin.h
+++ b/src/admin.h
@@ -21,6 +21,8 @@
#include "compat.h"
#include "resourcematch.h"
+#define ADMIN_ICESTATS_LEGACY_EXTENSION_APPLICATION "http://icecast.org/specs/legacy-icestats"
+
/* types */
#define ADMINTYPE_ERROR (-1)
#define ADMINTYPE_GENERAL 1
diff --git a/src/client.c b/src/client.c
index f12759d3..41f5ce2d 100644
--- a/src/client.c
+++ b/src/client.c
@@ -24,6 +24,8 @@
#include
#include
+#include
+
#include "common/thread/thread.h"
#include "common/avl/avl.h"
#include "common/httpp/httpp.h"
@@ -51,7 +53,7 @@
#include "listensocket.h"
#include "fastevent.h"
-/* for ADMIN_COMMAND_ERROR */
+/* for ADMIN_COMMAND_ERROR, and ADMIN_ICESTATS_LEGACY_EXTENSION_APPLICATION */
#include "admin.h"
#ifdef _WIN32
@@ -301,7 +303,6 @@ int client_read_bytes(client_t *client, void *buf, unsigned len)
static inline void _client_send_error(client_t *client, const icecast_error_t *error)
{
- ice_config_t *config;
reportxml_t *report;
admin_format_t admin_format;
const char *xslt = NULL;
@@ -323,28 +324,8 @@ static inline void _client_send_error(client_t *client, const icecast_error_t *e
break;
}
-
- 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);
- }
+ report = client_get_reportxml(error->uuid, NULL, error->message);
client_send_reportxml(client, report, DOCUMENT_DOMAIN_ADMIN, xslt, admin_format, error->http_status);
@@ -623,6 +604,72 @@ void client_send_reportxml(client_t *client, reportxml_t *report, document_domai
xmlFreeDoc(doc);
}
+static void client_get_reportxml__add_basic_stats(reportxml_t *report)
+{
+ reportxml_node_t *rootnode, *extension;
+ xmlNodePtr xmlroot;
+ xmlNodePtr modules;
+
+ rootnode = reportxml_get_root_node(report);
+
+ extension = reportxml_node_new(REPORTXML_NODE_TYPE_EXTENSION, NULL, NULL, NULL);
+ reportxml_node_set_attribute(extension, "application", ADMIN_ICESTATS_LEGACY_EXTENSION_APPLICATION);
+
+ reportxml_node_add_child(rootnode, extension);
+
+ refobject_unref(rootnode);
+
+ xmlroot = xmlNewNode(NULL, XMLSTR("icestats"));
+ modules = module_container_get_modulelist_as_xml(global.modulecontainer);
+ xmlAddChild(xmlroot, modules);
+
+
+ reportxml_node_add_xml_child(extension, xmlroot);
+ refobject_unref(extension);
+ xmlFreeNode(xmlroot);
+}
+
+reportxml_t *client_get_reportxml(const char *state_definition, const char *state_akindof, const char *state_text)
+{
+ reportxml_t *report = NULL;
+
+ if (state_definition) {
+ ice_config_t *config;
+
+ config = config_get_config();
+ report = reportxml_database_build_report(config->reportxml_db, state_definition, -1);
+ config_release_config();
+ }
+
+ if (!report) {
+ reportxml_node_t *rootnode, *incidentnode, *statenode;
+
+ report = reportxml_new();
+ rootnode = reportxml_get_root_node(report);
+ incidentnode = reportxml_node_new(REPORTXML_NODE_TYPE_INCIDENT, NULL, NULL, NULL);
+ statenode = reportxml_node_new(REPORTXML_NODE_TYPE_STATE, NULL, state_definition, state_akindof);
+
+ if (state_text) {
+ reportxml_node_t *textnode;
+
+ textnode = reportxml_node_new(REPORTXML_NODE_TYPE_TEXT, NULL, NULL, NULL);
+ reportxml_node_set_content(textnode, state_text);
+ reportxml_node_add_child(statenode, textnode);
+ refobject_unref(textnode);
+ }
+
+ reportxml_node_add_child(incidentnode, statenode);
+ reportxml_node_add_child(rootnode, incidentnode);
+ refobject_unref(statenode);
+ refobject_unref(incidentnode);
+ refobject_unref(rootnode);
+ }
+
+ client_get_reportxml__add_basic_stats(report);
+
+ return report;
+}
+
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 ce34f5e0..059fbcc1 100644
--- a/src/client.h
+++ b/src/client.h
@@ -144,6 +144,7 @@ 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);
+reportxml_t *client_get_reportxml(const char *state_definition, const char *state_akindof, const char *state_text);
admin_format_t client_get_admin_format_by_content_negotiation(client_t *client);
int client_send_bytes (client_t *client, const void *buf, unsigned len);
int client_read_bytes (client_t *client, void *buf, unsigned len);
diff --git a/src/module.c b/src/module.c
index 07e3d59a..997d4819 100644
--- a/src/module.c
+++ b/src/module.c
@@ -18,6 +18,7 @@
#include "refobject.h"
#include "module.h"
+#include "cfgfile.h" /* for XMLSTR() */
struct module_tag {
refobject_base_t __base;
@@ -26,6 +27,8 @@ struct module_tag {
size_t client_handlers_len;
module_setup_handler_t freecb;
void *userdata;
+ char *management_link_url;
+ char *management_link_title;
};
@@ -118,6 +121,37 @@ module_t * module_container_get_module(module_container_t *self, co
return ret;
}
+xmlNodePtr module_container_get_modulelist_as_xml(module_container_t *self)
+{
+ xmlNodePtr root;
+ avl_node *avlnode;
+
+ if (!self)
+ return NULL;
+
+ root = xmlNewNode(NULL, XMLSTR("modules"));
+ if (!root)
+ return NULL;
+
+ thread_mutex_lock(&(self->lock));
+ avlnode = avl_get_first(self->module);
+ while (avlnode) {
+ module_t *module = avlnode->key;
+ xmlNodePtr node = xmlNewChild(root, NULL, XMLSTR("module"), NULL);
+
+ xmlSetProp(node, XMLSTR("name"), XMLSTR(refobject_get_name(module)));
+ if (module->management_link_url)
+ xmlSetProp(node, XMLSTR("management-url"), XMLSTR(module->management_link_url));
+ if (module->management_link_title)
+ xmlSetProp(node, XMLSTR("management-title"), XMLSTR(module->management_link_title));
+
+ avlnode = avl_get_next(avlnode);
+ }
+ thread_mutex_unlock(&(self->lock));
+
+ return root;
+}
+
static void __module_free(refobject_t self, void **userdata)
{
module_t *mod = REFOBJECT_TO_TYPE(self, module_t *);
@@ -128,6 +162,9 @@ static void __module_free(refobject_t self, void **userdata)
if (mod->userdata)
free(mod->userdata);
+ free(mod->management_link_url);
+ free(mod->management_link_title);
+
thread_mutex_destroy(&(mod->lock));
}
@@ -153,6 +190,42 @@ module_t * module_new(const char *name, module_setup_handler_t newc
return ret;
}
+int module_add_link(module_t *self, const char *type, const char *url, const char *title)
+{
+ char *n_url = NULL;
+ char *n_title = NULL;
+
+ if (!self || !type)
+ return -1;
+
+ if (strcmp(type, "management-url") != 0)
+ return -1;
+
+ if (url) {
+ n_url = strdup(url);
+ if (!n_url)
+ return -1;
+ }
+
+ if (title) {
+ n_title = strdup(title);
+ if (!n_title) {
+ free(n_url);
+ return -1;
+ }
+ }
+
+ thread_mutex_lock(&(self->lock));
+ free(self->management_link_url);
+ free(self->management_link_title);
+
+ self->management_link_url = n_url;
+ self->management_link_title = n_title;
+ thread_mutex_unlock(&(self->lock));
+
+ return 0;
+}
+
const module_client_handler_t * module_get_client_handler(module_t *self, const char *name)
{
size_t i;
diff --git a/src/module.h b/src/module.h
index 8d2ad781..8b29accc 100644
--- a/src/module.h
+++ b/src/module.h
@@ -9,6 +9,8 @@
#ifndef __MODULE_H__
#define __MODULE_H__
+#include
+
#include "icecasttypes.h"
typedef void (*module_client_handler_function_t)(module_t *self, client_t *client, const char *uri);
@@ -23,9 +25,12 @@ module_container_t * module_container_new(void);
int module_container_add_module(module_container_t *self, module_t *module);
int module_container_delete_module(module_container_t *self, const char *name);
module_t * module_container_get_module(module_container_t *self, const char *name);
+xmlNodePtr module_container_get_modulelist_as_xml(module_container_t *self);
module_t * module_new(const char *name, module_setup_handler_t newcb, module_setup_handler_t freecb, void *userdata);
+int module_add_link(module_t *self, const char *type, const char *url, const char *title);
+
/* Note: Those functions are not really thread safe as (module_client_handler_t) is not thread safe. This is by design. */
const module_client_handler_t * module_get_client_handler(module_t *self, const char *name);
int module_add_client_handler(module_t *self, const module_client_handler_t *handlers, size_t len);
diff --git a/src/reportxml.c b/src/reportxml.c
index cfc21a1f..b6ea1c7e 100644
--- a/src/reportxml.c
+++ b/src/reportxml.c
@@ -286,6 +286,14 @@ reportxml_node_t * reportxml_get_node_by_attribute(reportxml_t *report, con
return reportxml_node_get_child_by_attribute(report->root, key, value, include_definitions);
}
+reportxml_node_t * reportxml_get_node_by_type(reportxml_t *report, reportxml_node_type_t type, int include_definitions)
+{
+ if (!report)
+ return NULL;
+
+ return reportxml_node_get_child_by_type(report->root, type, include_definitions);
+}
+
reportxml_t * reportxml_parse_xmldoc(xmlDocPtr doc)
{
reportxml_node_t *root;
@@ -822,6 +830,33 @@ reportxml_node_t * reportxml_node_get_child_by_attribute(reportxml_node_t *
return NULL;
}
+reportxml_node_t * reportxml_node_get_child_by_type(reportxml_node_t *node, reportxml_node_type_t type, int include_definitions)
+{
+ size_t i;
+
+ if (!node)
+ return NULL;
+
+ if (node->type == type) {
+ if (refobject_ref(node) != 0)
+ return NULL;
+ return node;
+ }
+
+ if (node->type == REPORTXML_NODE_TYPE_DEFINITION && !include_definitions)
+ return NULL;
+
+ for (i = 0; i < node->childs_len; i++) {
+ reportxml_node_t *ret;
+
+ ret = reportxml_node_get_child_by_type(node->childs[i], type, 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 2be09dc8..68d4b3d8 100644
--- a/src/reportxml.h
+++ b/src/reportxml.h
@@ -77,6 +77,8 @@ reportxml_node_t * reportxml_get_root_node(reportxml_t *report);
* s are skipped.
*/
reportxml_node_t * reportxml_get_node_by_attribute(reportxml_t *report, const char *key, const char *value, int include_definitions);
+/* This gets a node by it's type. Otherwise identical to reportxml_get_node_by_attribute() */
+reportxml_node_t * reportxml_get_node_by_type(reportxml_t *report, reportxml_node_type_t type, int include_definitions);
/* This function parses an XML document and returns the parst report XML document */
reportxml_t * reportxml_parse_xmldoc(xmlDocPtr doc);
/* This function renders an report XML document as XML structure */
@@ -108,6 +110,8 @@ ssize_t reportxml_node_count_child(reportxml_node_t *node);
reportxml_node_t * reportxml_node_get_child(reportxml_node_t *node, size_t idx);
/* This gets an child by it's value of the given attribute. See reportxml_get_node_by_attribute() for more details. */
reportxml_node_t * reportxml_node_get_child_by_attribute(reportxml_node_t *node, const char *key, const char *value, int include_definitions);
+/* This gets a child by it's type. Otherwise identical to reportxml_node_get_child_by_attribute() */
+reportxml_node_t * reportxml_node_get_child_by_type(reportxml_node_t *node, reportxml_node_type_t type, int include_definitions);
/* This gets and sets the text content of an node (used for ) */
int reportxml_node_set_content(reportxml_node_t *node, const char *value);
char * reportxml_node_get_content(reportxml_node_t *node);
diff --git a/src/stats.c b/src/stats.c
index 0f9d9ce9..73ec6a28 100644
--- a/src/stats.c
+++ b/src/stats.c
@@ -1063,12 +1063,16 @@ xmlDocPtr stats_get_xml(int show_hidden, const char *show_mount, client_t *clien
{
xmlDocPtr doc;
xmlNodePtr node;
+ xmlNodePtr modules;
source_t * source;
doc = xmlNewDoc (XMLSTR("1.0"));
node = xmlNewDocNode (doc, NULL, XMLSTR("icestats"), NULL);
xmlDocSetRootElement(doc, node);
+ modules = module_container_get_modulelist_as_xml(global.modulecontainer);
+ xmlAddChild(node, modules);
+
node = _dump_stats_to_doc(node, show_mount, show_hidden, client);
if (show_mount && node) {