--- /dev/null
+/*
+ * xml.c
+ *
+ * Copyright 2017 Michael Rasmussen <mir@datanom.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <libxml/parser.h>
+#include <libxml/xpath.h>
+#include <libxml/xpathInternals.h>
+#include <string.h>
+#include "xml.h"
+
+static gboolean debug = FALSE;
+
+static xmlDocPtr getdoc(const gchar* xml) {
+ xmlDocPtr doc;
+ doc = xmlParseMemory(xml, strlen(xml));
+
+ if (doc == NULL ) {
+ g_warning("Document not parsed successfully.");
+ return NULL;
+ }
+
+ return doc;
+}
+
+static xmlXPathObjectPtr getnodeset(xmlDocPtr doc, xmlChar *xpath, const gchar* prefix, const gchar* ns_url) {
+
+ xmlXPathContextPtr context;
+ xmlXPathObjectPtr result = NULL;
+
+ context = xmlXPathNewContext(doc);
+ if (context == NULL) {
+ g_warning("Error in xmlXPathNewContext");
+ return NULL;
+ }
+
+ if (prefix && ns_url) {
+ xmlXPathRegisterNs(context, (xmlChar *)prefix, (xmlChar *)ns_url);
+ }
+
+ result = xmlXPathEvalExpression(xpath, context);
+ xmlXPathFreeContext(context);
+ xmlFree(xpath);
+ if (result == NULL) {
+ g_warning("Error in xmlXPathEvalExpression");
+ return NULL;
+ }
+
+ if(xmlXPathNodeSetIsEmpty(result->nodesetval)){
+ xmlXPathFreeObject(result);
+ result = NULL;
+ }
+
+ return result;
+}
+
+xmlNodePtr find_node_by_name(xmlNodePtr rootnode, const xmlChar* nodename, GSList** nodes) {
+ xmlNodePtr node = rootnode;
+
+ if (!node) {
+ return NULL;
+ }
+
+ while (node != NULL) {
+
+ if (!xmlStrcmp(node->name, nodename)) {
+ if (nodes) {
+ *nodes = g_slist_append(*nodes, (gpointer) node);
+ } else {
+ return node;
+ }
+ } else if (node->children != NULL) {
+ xmlNodePtr intNode = find_node_by_name(node->children, nodename, nodes);
+ if (intNode != NULL) {
+ if (nodes) {
+ *nodes = g_slist_append(*nodes, (gpointer) node);
+ } else {
+ return intNode;
+ }
+ }
+ }
+ node = node->next;
+ }
+
+ return NULL;
+}
+
+static GSList* find_all_nodes_by_name(xmlDocPtr doc, const gchar* nodename) {
+ GSList* nodes = NULL;
+
+ find_node_by_name(doc->children, (const xmlChar *) nodename, &nodes);
+
+ return nodes;
+}
+
+void dump_node(FILE* f, xmlDocPtr doc, xmlNodePtr node) {
+ g_return_if_fail(f && doc && node);
+
+ xmlElemDump(f, doc, node);
+ fprintf(f, "\n");
+}
+
+void dump_nodes(gpointer data, gpointer user_data) {
+ g_return_if_fail(data && user_data);
+
+ xmlNodePtr node = (xmlNodePtr) data;
+ xmlDocPtr doc = (xmlDocPtr) user_data;
+
+ dump_node(stderr, doc, node);
+}
+
+void get_props(gpointer data, gpointer user_data) {
+ g_return_if_fail(data && user_data);
+
+ xmlNodePtr node = (xmlNodePtr) data;
+ Calendar* cal = (Calendar *) user_data;
+
+ xmlChar* comp = xmlGetProp(node, (const xmlChar *)"name");
+ if (comp) {
+ Component c = string_to_component((const gchar *) comp);
+ cal->components = g_slist_append(cal->components, GUINT_TO_POINTER(c));
+ xmlFree(comp);
+ }
+}
+
+GSList* find_element(const gchar* element, const gchar* xml, const gchar* prefix, const gchar* ns_url) {
+ GSList* elements = NULL;
+ xmlChar* keyword = NULL;
+
+ g_return_val_if_fail(element && xml, NULL);
+
+ xmlDocPtr doc = getdoc(xml);
+ if (doc) {
+ xmlXPathObjectPtr result = getnodeset(doc, xmlCharStrdup(element), prefix, ns_url);
+
+ if (result) {
+ xmlNodeSetPtr nodeset = result->nodesetval;
+ for (int i = 0; i < nodeset->nodeNr; i++) {
+ keyword = xmlNodeListGetString(doc, nodeset->nodeTab[i]->xmlChildrenNode, 1);
+ elements = g_slist_append(elements, (gpointer) g_strdup((gchar *) keyword));
+ xmlFree(keyword);
+ }
+ xmlXPathFreeObject(result);
+ }
+
+ xmlFreeDoc(doc);
+ }
+ xmlCleanupParser();
+
+ return elements;
+}
+
+GSList* find_node(const gchar* node, const gchar* xml) {
+ GSList* nodes = NULL;
+ xmlDocPtr doc = getdoc(xml);
+
+ if (doc) {
+ GSList* n = find_all_nodes_by_name(doc, node);
+
+ for (GSList* l = n; l; l = g_slist_next(l)) {
+ xmlNodePtr p = (xmlNodePtr) l->data;
+ nodes = g_slist_append(nodes, g_strdup((gchar *) p->name));
+ }
+
+ g_slist_free(n);
+ xmlFreeDoc(doc);
+ }
+ xmlCleanupParser();
+
+ return nodes;
+}
+
+GSList* find_calendars(const gchar* host_part, const gchar* xml) {
+ GSList* list = NULL;
+ xmlNodePtr ptr;
+ xmlNodePtr found;
+ xmlDocPtr doc = getdoc(xml);
+
+ if (doc) {
+ GSList* n = find_all_nodes_by_name(doc, "response");
+ for (GSList* l = n; l; l = g_slist_next(l)) {
+ xmlNodePtr p = (xmlNodePtr) l->data;
+ if (debug)
+ dump_node(stderr, doc, p);
+ found = find_node_by_name(p, (const xmlChar*)"resourcetype", NULL);
+ if (found) {
+ found = find_node_by_name(found, (const xmlChar*)"calendar", NULL);
+ if (found) {
+ if (debug)
+ dump_node(stderr, doc, p);
+ Calendar* cal = g_new0(Calendar, 1);
+ ptr = find_node_by_name(p, (const xmlChar*)"href", NULL);
+ if (ptr)
+ cal->url = g_strconcat(host_part, ptr->children->content, NULL);
+ ptr = find_node_by_name(p, (const xmlChar*)"displayname", NULL);
+ if (ptr)
+ cal->displayname = g_strdup((gchar *)ptr->children->content);
+ ptr = find_node_by_name(p, (const xmlChar*)"getctag", NULL);
+ if (ptr)
+ cal->ctag = g_strdup((gchar *)ptr->children->content);
+ ptr = find_node_by_name(p, (const xmlChar*)"supported-calendar-component-set", NULL);
+ if (ptr) {
+ GSList* comps = NULL;
+ find_node_by_name(ptr, (const xmlChar *)"comp", &comps);
+ g_slist_foreach(comps, (GFunc)get_props, (gpointer) cal);
+ g_slist_free(comps);
+ }
+ list = g_slist_append(list, (gpointer) cal);
+ if (debug)
+ calendar_dump(stderr, cal);
+ }
+ }
+ }
+ g_slist_free(n);
+ }
+
+ return list;
+}
+
+void set_debug_mode(gboolean mode) {
+ debug = mode;
+}