aboutsummaryrefslogtreecommitdiffstats
path: root/protocols/jabber/xmlnode.c
diff options
context:
space:
mode:
Diffstat (limited to 'protocols/jabber/xmlnode.c')
-rw-r--r--protocols/jabber/xmlnode.c705
1 files changed, 705 insertions, 0 deletions
diff --git a/protocols/jabber/xmlnode.c b/protocols/jabber/xmlnode.c
new file mode 100644
index 00000000..88dd4eef
--- /dev/null
+++ b/protocols/jabber/xmlnode.c
@@ -0,0 +1,705 @@
+/* --------------------------------------------------------------------------
+ *
+ * License
+ *
+ * The contents of this file are subject to the Jabber Open Source License
+ * Version 1.0 (the "JOSL"). You may not copy or use this file, in either
+ * source code or executable form, except in compliance with the JOSL. You
+ * may obtain a copy of the JOSL at http://www.jabber.org/ or at
+ * http://www.opensource.org/.
+ *
+ * Software distributed under the JOSL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the JOSL
+ * for the specific language governing rights and limitations under the
+ * JOSL.
+ *
+ * Copyrights
+ *
+ * Portions created by or assigned to Jabber.com, Inc. are
+ * Copyright (c) 1999-2002 Jabber.com, Inc. All Rights Reserved. Contact
+ * information for Jabber.com, Inc. is available at http://www.jabber.com/.
+ *
+ * Portions Copyright (c) 1998-1999 Jeremie Miller.
+ *
+ * Acknowledgements
+ *
+ * Special thanks to the Jabber Open Source Contributors for their
+ * suggestions and support of Jabber.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * GNU General Public License Version 2 or later (the "GPL"), in which case
+ * the provisions of the GPL are applicable instead of those above. If you
+ * wish to allow use of your version of this file only under the terms of the
+ * GPL and not to allow others to use your version of this file under the JOSL,
+ * indicate your decision by deleting the provisions above and replace them
+ * with the notice and other provisions required by the GPL. If you do not
+ * delete the provisions above, a recipient may use your version of this file
+ * under either the JOSL or the GPL.
+ *
+ *
+ * --------------------------------------------------------------------------*/
+
+#include "jabber.h"
+#include <glib.h>
+
+static xmlnode xmlnode_get_firstattrib(xmlnode parent);
+static int xmlnode_get_type(xmlnode node);
+static void xmlnode_insert_node(xmlnode parent, xmlnode node);
+
+/* Internal routines */
+static xmlnode _xmlnode_new(pool p, const char* name, unsigned int type)
+{
+ xmlnode result = NULL;
+ if (type > NTYPE_LAST)
+ return NULL;
+
+ if (type != NTYPE_CDATA && name == NULL)
+ return NULL;
+
+ if (p == NULL)
+ {
+ p = pool_heap(1*1024);
+ }
+
+ /* Allocate & zero memory */
+ result = (xmlnode)pmalloco(p, sizeof(_xmlnode));
+
+ /* Initialize fields */
+ if (type != NTYPE_CDATA)
+ result->name = pstrdup(p,name);
+ result->type = type;
+ result->p = p;
+ return result;
+}
+
+static xmlnode _xmlnode_append_sibling(xmlnode lastsibling, const char* name, unsigned int type)
+{
+ xmlnode result;
+
+ result = _xmlnode_new(xmlnode_pool(lastsibling), name, type);
+ if (result != NULL)
+ {
+ /* Setup sibling pointers */
+ result->prev = lastsibling;
+ lastsibling->next = result;
+ }
+ return result;
+}
+
+static xmlnode _xmlnode_insert(xmlnode parent, const char* name, unsigned int type)
+{
+ xmlnode result;
+
+ if(parent == NULL || (type != NTYPE_CDATA && name == NULL)) return NULL;
+
+ /* If parent->firstchild is NULL, simply create a new node for the first child */
+ if (parent->firstchild == NULL)
+ {
+ result = _xmlnode_new(parent->p, name, type);
+ parent->firstchild = result;
+ }
+ /* Otherwise, append this to the lastchild */
+ else
+ {
+ result= _xmlnode_append_sibling(parent->lastchild, name, type);
+ }
+ result->parent = parent;
+ parent->lastchild = result;
+ return result;
+
+}
+
+static xmlnode _xmlnode_search(xmlnode firstsibling, const char* name, unsigned int type)
+{
+ xmlnode current;
+
+ /* Walk the sibling list, looking for a NTYPE_TAG xmlnode with
+ the specified name */
+ current = firstsibling;
+ while (current != NULL)
+ {
+ if ((current->type == type) && (j_strcmp(current->name, name) == 0))
+ return current;
+ else
+ current = current->next;
+ }
+ return NULL;
+}
+
+static void _xmlnode_merge(xmlnode data)
+{
+ xmlnode cur;
+ char *merge, *scur;
+ int imerge;
+
+ /* get total size of all merged cdata */
+ imerge = 0;
+ for(cur = data; cur != NULL && cur->type == NTYPE_CDATA; cur = cur->next)
+ imerge += cur->data_sz;
+
+ /* copy in current data and then spin through all of them and merge */
+ scur = merge = pmalloc(data->p,imerge + 1);
+ for(cur = data; cur != NULL && cur->type == NTYPE_CDATA; cur = cur->next)
+ {
+ memcpy(scur,cur->data,cur->data_sz);
+ scur += cur->data_sz;
+ }
+ *scur = '\0';
+
+ /* this effectively hides all of the merged-in chunks */
+ data->next = cur;
+ if(cur == NULL)
+ data->parent->lastchild = data;
+ else
+ cur->prev = data;
+
+ /* reset data */
+ data->data = merge;
+ data->data_sz = imerge;
+
+}
+
+static void _xmlnode_hide_sibling(xmlnode child)
+{
+ if(child == NULL)
+ return;
+
+ if(child->prev != NULL)
+ child->prev->next = child->next;
+ if(child->next != NULL)
+ child->next->prev = child->prev;
+}
+
+static void _xmlnode_tag2str(spool s, xmlnode node, int flag)
+{
+ xmlnode tmp;
+
+ if(flag==0 || flag==1)
+ {
+ spooler(s,"<",xmlnode_get_name(node),s);
+ tmp = xmlnode_get_firstattrib(node);
+ while(tmp) {
+ spooler(s," ",xmlnode_get_name(tmp),"='",strescape(xmlnode_pool(node),xmlnode_get_data(tmp)),"'",s);
+ tmp = xmlnode_get_nextsibling(tmp);
+ }
+ if(flag==0)
+ spool_add(s,"/>");
+ else
+ spool_add(s,">");
+ }
+ else
+ {
+ spooler(s,"</",xmlnode_get_name(node),">",s);
+ }
+}
+
+static spool _xmlnode2spool(xmlnode node)
+{
+ spool s;
+ int level=0,dir=0;
+ xmlnode tmp;
+
+ if(!node || xmlnode_get_type(node)!=NTYPE_TAG)
+ return NULL;
+
+ s = spool_new(xmlnode_pool(node));
+ if(!s) return(NULL);
+
+ while(1)
+ {
+ if(dir==0)
+ {
+ if(xmlnode_get_type(node) == NTYPE_TAG)
+ {
+ if(xmlnode_has_children(node))
+ {
+ _xmlnode_tag2str(s,node,1);
+ node = xmlnode_get_firstchild(node);
+ level++;
+ continue;
+ }else{
+ _xmlnode_tag2str(s,node,0);
+ }
+ }else{
+ spool_add(s,strescape(xmlnode_pool(node),xmlnode_get_data(node)));
+ }
+ }
+
+ tmp = xmlnode_get_nextsibling(node);
+ if(!tmp)
+ {
+ node = xmlnode_get_parent(node);
+ level--;
+ if(level>=0) _xmlnode_tag2str(s,node,2);
+ if(level<1) break;
+ dir = 1;
+ }else{
+ node = tmp;
+ dir = 0;
+ }
+ }
+
+ return s;
+}
+
+
+/* External routines */
+
+
+/*
+ * xmlnode_new_tag -- create a tag node
+ * Automatically creates a memory pool for the node.
+ *
+ * parameters
+ * name -- name of the tag
+ *
+ * returns
+ * a pointer to the tag node
+ * or NULL if it was unsuccessfull
+ */
+xmlnode xmlnode_new_tag(const char* name)
+{
+ return _xmlnode_new(NULL, name, NTYPE_TAG);
+}
+
+
+/*
+ * xmlnode_insert_tag -- append a child tag to a tag
+ *
+ * parameters
+ * parent -- pointer to the parent tag
+ * name -- name of the child tag
+ *
+ * returns
+ * a pointer to the child tag node
+ * or NULL if it was unsuccessfull
+ */
+xmlnode xmlnode_insert_tag(xmlnode parent, const char* name)
+{
+ return _xmlnode_insert(parent, name, NTYPE_TAG);
+}
+
+
+/*
+ * xmlnode_insert_cdata -- append character data to a tag
+ *
+ * parameters
+ * parent -- parent tag
+ * CDATA -- character data
+ * size -- size of CDATA
+ * or -1 for null-terminated CDATA strings
+ *
+ * returns
+ * a pointer to the child CDATA node
+ * or NULL if it was unsuccessfull
+ */
+xmlnode xmlnode_insert_cdata(xmlnode parent, const char* CDATA, unsigned int size)
+{
+ xmlnode result;
+
+ if(CDATA == NULL || parent == NULL)
+ return NULL;
+
+ if(size == -1)
+ size = strlen(CDATA);
+
+ result = _xmlnode_insert(parent, NULL, NTYPE_CDATA);
+ if (result != NULL)
+ {
+ result->data = (char*)pmalloc(result->p, size + 1);
+ memcpy(result->data, CDATA, size);
+ result->data[size] = '\0';
+ result->data_sz = size;
+ }
+
+ return result;
+}
+
+
+/*
+ * xmlnode_get_tag -- find given tag in an xmlnode tree
+ *
+ * parameters
+ * parent -- pointer to the parent tag
+ * name -- "name" for the child tag of that name
+ * "name/name" for a sub child (recurses)
+ * "?attrib" to match the first tag with that attrib defined
+ * "?attrib=value" to match the first tag with that attrib and value
+ * "=cdata" to match the cdata contents of the child
+ * or any combination: "name/name/?attrib", "name=cdata", etc
+ *
+ * results
+ * a pointer to the tag matching search criteria
+ * or NULL if search was unsuccessfull
+ */
+xmlnode xmlnode_get_tag(xmlnode parent, const char* name)
+{
+ char *str, *slash, *qmark, *equals;
+ xmlnode step, ret;
+
+
+ if(parent == NULL || parent->firstchild == NULL || name == NULL || name == '\0') return NULL;
+
+ if(strstr(name, "/") == NULL && strstr(name,"?") == NULL && strstr(name, "=") == NULL)
+ return _xmlnode_search(parent->firstchild, name, NTYPE_TAG);
+
+ str = g_strdup(name);
+ slash = strstr(str, "/");
+ qmark = strstr(str, "?");
+ equals = strstr(str, "=");
+
+ if(equals != NULL && (slash == NULL || equals < slash) && (qmark == NULL || equals < qmark))
+ { /* of type =cdata */
+
+ *equals = '\0';
+ equals++;
+
+ for(step = parent->firstchild; step != NULL; step = xmlnode_get_nextsibling(step))
+ {
+ if(xmlnode_get_type(step) != NTYPE_TAG)
+ continue;
+
+ if(*str != '\0')
+ if(j_strcmp(xmlnode_get_name(step),str) != 0)
+ continue;
+
+ if(j_strcmp(xmlnode_get_data(step),equals) != 0)
+ continue;
+
+ break;
+ }
+
+ g_free(str);
+ return step;
+ }
+
+
+ if(qmark != NULL && (slash == NULL || qmark < slash))
+ { /* of type ?attrib */
+
+ *qmark = '\0';
+ qmark++;
+ if(equals != NULL)
+ {
+ *equals = '\0';
+ equals++;
+ }
+
+ for(step = parent->firstchild; step != NULL; step = xmlnode_get_nextsibling(step))
+ {
+ if(xmlnode_get_type(step) != NTYPE_TAG)
+ continue;
+
+ if(*str != '\0')
+ if(j_strcmp(xmlnode_get_name(step),str) != 0)
+ continue;
+
+ if(xmlnode_get_attrib(step,qmark) == NULL)
+ continue;
+
+ if(equals != NULL && j_strcmp(xmlnode_get_attrib(step,qmark),equals) != 0)
+ continue;
+
+ break;
+ }
+
+ g_free(str);
+ return step;
+ }
+
+
+ *slash = '\0';
+ ++slash;
+
+ for(step = parent->firstchild; step != NULL; step = xmlnode_get_nextsibling(step))
+ {
+ if(xmlnode_get_type(step) != NTYPE_TAG) continue;
+
+ if(j_strcmp(xmlnode_get_name(step),str) != 0)
+ continue;
+
+ ret = xmlnode_get_tag(step, slash);
+ if(ret != NULL)
+ {
+ g_free(str);
+ return ret;
+ }
+ }
+
+ g_free(str);
+ return NULL;
+}
+
+
+/* return the cdata from any tag */
+char *xmlnode_get_tag_data(xmlnode parent, const char *name)
+{
+ xmlnode tag;
+
+ tag = xmlnode_get_tag(parent, name);
+ if(tag == NULL) return NULL;
+
+ return xmlnode_get_data(tag);
+}
+
+
+void xmlnode_put_attrib(xmlnode owner, const char* name, const char* value)
+{
+ xmlnode attrib;
+
+ if(owner == NULL || name == NULL || value == NULL) return;
+
+ /* If there are no existing attributs, allocate a new one to start
+ the list */
+ if (owner->firstattrib == NULL)
+ {
+ attrib = _xmlnode_new(owner->p, name, NTYPE_ATTRIB);
+ owner->firstattrib = attrib;
+ owner->lastattrib = attrib;
+ }
+ else
+ {
+ attrib = _xmlnode_search(owner->firstattrib, name, NTYPE_ATTRIB);
+ if(attrib == NULL)
+ {
+ attrib = _xmlnode_append_sibling(owner->lastattrib, name, NTYPE_ATTRIB);
+ owner->lastattrib = attrib;
+ }
+ }
+ /* Update the value of the attribute */
+ attrib->data_sz = strlen(value);
+ attrib->data = pstrdup(owner->p, value);
+
+}
+
+char* xmlnode_get_attrib(xmlnode owner, const char* name)
+{
+ xmlnode attrib;
+
+ if (owner != NULL && owner->firstattrib != NULL)
+ {
+ attrib = _xmlnode_search(owner->firstattrib, name, NTYPE_ATTRIB);
+ if (attrib != NULL)
+ return (char*)attrib->data;
+ }
+ return NULL;
+}
+
+static xmlnode xmlnode_get_firstattrib(xmlnode parent)
+{
+ if (parent != NULL)
+ return parent->firstattrib;
+ return NULL;
+}
+
+xmlnode xmlnode_get_firstchild(xmlnode parent)
+{
+ if (parent != NULL)
+ return parent->firstchild;
+ return NULL;
+}
+
+xmlnode xmlnode_get_nextsibling(xmlnode sibling)
+{
+ if (sibling != NULL)
+ return sibling->next;
+ return NULL;
+}
+
+xmlnode xmlnode_get_parent(xmlnode node)
+{
+ if (node != NULL)
+ return node->parent;
+ return NULL;
+}
+
+char* xmlnode_get_name(xmlnode node)
+{
+ if (node != NULL)
+ return node->name;
+ return NULL;
+}
+
+char* xmlnode_get_data(xmlnode node)
+{
+ if(xmlnode_get_type(node) == NTYPE_TAG) /* loop till we find a CDATA in the children */
+ for(node = xmlnode_get_firstchild(node); node != NULL; node = xmlnode_get_nextsibling(node))
+ if(xmlnode_get_type(node) == NTYPE_CDATA) break;
+
+ if(node == NULL) return NULL;
+
+ /* check for a dirty node w/ unassembled cdata chunks */
+ if(xmlnode_get_type(node->next) == NTYPE_CDATA)
+ _xmlnode_merge(node);
+
+ return node->data;
+}
+
+static int xmlnode_get_datasz(xmlnode node)
+{
+ if(xmlnode_get_type(node) != NTYPE_CDATA) return 0;
+
+ /* check for a dirty node w/ unassembled cdata chunks */
+ if(xmlnode_get_type(node->next) == NTYPE_CDATA)
+ _xmlnode_merge(node);
+ return node->data_sz;
+}
+
+static int xmlnode_get_type(xmlnode node)
+{
+ if (node != NULL)
+ return node->type;
+ return NTYPE_UNDEF;
+}
+
+int xmlnode_has_children(xmlnode node)
+{
+ if ((node != NULL) && (node->firstchild != NULL))
+ return 1;
+ return 0;
+}
+
+static int xmlnode_has_attribs(xmlnode node)
+{
+ if ((node != NULL) && (node->firstattrib != NULL))
+ return 1;
+ return 0;
+}
+
+pool xmlnode_pool(xmlnode node)
+{
+ if (node != NULL)
+ return node->p;
+ return (pool)NULL;
+}
+
+void xmlnode_hide_attrib(xmlnode parent, const char *name)
+{
+ xmlnode attrib;
+
+ if(parent == NULL || parent->firstattrib == NULL || name == NULL)
+ return;
+
+ attrib = _xmlnode_search(parent->firstattrib, name, NTYPE_ATTRIB);
+ if(attrib == NULL)
+ return;
+
+ /* first fix up at the child level */
+ _xmlnode_hide_sibling(attrib);
+
+ /* next fix up at the parent level */
+ if(parent->firstattrib == attrib)
+ parent->firstattrib = attrib->next;
+ if(parent->lastattrib == attrib)
+ parent->lastattrib = attrib->prev;
+}
+
+
+
+/*
+ * xmlnode2str -- convert given xmlnode tree into a string
+ *
+ * parameters
+ * node -- pointer to the xmlnode structure
+ *
+ * results
+ * a pointer to the created string
+ * or NULL if it was unsuccessfull
+ */
+char *xmlnode2str(xmlnode node)
+{
+ return spool_print(_xmlnode2spool(node));
+}
+
+/* loop through both a and b comparing everything, attribs, cdata, children, etc */
+static int xmlnode_cmp(xmlnode a, xmlnode b)
+{
+ int ret = 0;
+
+ while(1)
+ {
+ if(a == NULL && b == NULL)
+ return 0;
+
+ if(a == NULL || b == NULL)
+ return -1;
+
+ if(xmlnode_get_type(a) != xmlnode_get_type(b))
+ return -1;
+
+ switch(xmlnode_get_type(a))
+ {
+ case NTYPE_ATTRIB:
+ ret = j_strcmp(xmlnode_get_name(a), xmlnode_get_name(b));
+ if(ret != 0)
+ return -1;
+ ret = j_strcmp(xmlnode_get_data(a), xmlnode_get_data(b));
+ if(ret != 0)
+ return -1;
+ break;
+ case NTYPE_TAG:
+ ret = j_strcmp(xmlnode_get_name(a), xmlnode_get_name(b));
+ if(ret != 0)
+ return -1;
+ ret = xmlnode_cmp(xmlnode_get_firstattrib(a), xmlnode_get_firstattrib(b));
+ if(ret != 0)
+ return -1;
+ ret = xmlnode_cmp(xmlnode_get_firstchild(a), xmlnode_get_firstchild(b));
+ if(ret != 0)
+ return -1;
+ break;
+ case NTYPE_CDATA:
+ ret = j_strcmp(xmlnode_get_data(a), xmlnode_get_data(b));
+ if(ret != 0)
+ return -1;
+ }
+ a = xmlnode_get_nextsibling(a);
+ b = xmlnode_get_nextsibling(b);
+ }
+}
+
+
+xmlnode xmlnode_insert_tag_node(xmlnode parent, xmlnode node)
+{
+ xmlnode child;
+
+ child = xmlnode_insert_tag(parent, xmlnode_get_name(node));
+ if (xmlnode_has_attribs(node))
+ xmlnode_insert_node(child, xmlnode_get_firstattrib(node));
+ if (xmlnode_has_children(node))
+ xmlnode_insert_node(child, xmlnode_get_firstchild(node));
+
+ return child;
+}
+
+/* places copy of node and node's siblings in parent */
+static void xmlnode_insert_node(xmlnode parent, xmlnode node)
+{
+ if(node == NULL || parent == NULL)
+ return;
+
+ while(node != NULL)
+ {
+ switch(xmlnode_get_type(node))
+ {
+ case NTYPE_ATTRIB:
+ xmlnode_put_attrib(parent, xmlnode_get_name(node), xmlnode_get_data(node));
+ break;
+ case NTYPE_TAG:
+ xmlnode_insert_tag_node(parent, node);
+ break;
+ case NTYPE_CDATA:
+ xmlnode_insert_cdata(parent, xmlnode_get_data(node), xmlnode_get_datasz(node));
+ }
+ node = xmlnode_get_nextsibling(node);
+ }
+}
+
+
+void xmlnode_free(xmlnode node)
+{
+ if(node == NULL)
+ return;
+
+ pool_free(node->p);
+}