You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
kamailio/modules/xmpp/xode.c

868 lines
20 KiB

/*
* 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.
*
* Jabber
* Copyright (C) 1998-1999 The Jabber Team http://jabber.org/
*/
/*! \file
* \ingroup xmpp
*/
#include "xode.h"
static int _xode_strcmp(const char *a, const char *b)
{
if(a == NULL || b == NULL) return -1;
return strcmp(a,b);
}
/*! \brief Internal routines */
static xode _xode_new(xode_pool p, const char* name, unsigned int type)
{
xode result = NULL;
if (type > XODE_TYPE_LAST)
return NULL;
if (type != XODE_TYPE_CDATA && name == NULL)
return NULL;
if (p == NULL)
{
p = xode_pool_heap(1*1024);
}
/* Allocate & zero memory */
result = (xode)xode_pool_malloc(p, sizeof(_xode));
memset(result, '\0', sizeof(_xode));
/* Initialize fields */
if (type != XODE_TYPE_CDATA)
result->name = xode_pool_strdup(p,name);
result->type = type;
result->p = p;
return result;
}
static xode _xode_appendsibling(xode lastsibling, const char* name, unsigned int type)
{
xode result;
result = _xode_new(xode_get_pool(lastsibling), name, type);
if (result != NULL)
{
/* Setup sibling pointers */
result->prev = lastsibling;
lastsibling->next = result;
}
return result;
}
static xode _xode_insert(xode parent, const char* name, unsigned int type)
{
xode result;
if(parent == NULL || name == NULL) return NULL;
/* If parent->firstchild is NULL, simply create a new node for the first child */
if (parent->firstchild == NULL)
{
result = _xode_new(parent->p, name, type);
parent->firstchild = result;
}
/* Otherwise, append this to the lastchild */
else
{
result= _xode_appendsibling(parent->lastchild, name, type);
}
result->parent = parent;
parent->lastchild = result;
return result;
}
static xode _xode_search(xode firstsibling, const char* name, unsigned int type)
{
xode current;
/* Walk the sibling list, looking for a XODE_TYPE_TAG xode with
the specified name */
current = firstsibling;
while (current != NULL)
{
if (name != NULL && (current->type == type) && (_xode_strcmp(current->name, name) == 0))
return current;
else
current = current->next;
}
return NULL;
}
static char* _xode_merge(xode_pool p, char* dest, unsigned int destsize, const char* src, unsigned int srcsize)
{
char* result;
result = (char*)xode_pool_malloc(p, destsize + srcsize + 1);
memcpy(result, dest, destsize);
memcpy(result+destsize, src, srcsize);
result[destsize + srcsize] = '\0';
/* WARNING: major ugly hack: since we're throwing the old data away, let's jump in the xode_pool and subtract it from the size, this is for xmlstream's big-node checking */
p->size -= destsize;
return result;
}
static void _xode_hidesibling(xode 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 _xode_tag2str(xode_spool s, xode node, int flag)
{
xode tmp;
if(flag==0 || flag==1)
{
xode_spooler(s,"<",xode_get_name(node),s);
tmp = xode_get_firstattrib(node);
while(tmp) {
xode_spooler(s," ",xode_get_name(tmp),"='",xode_strescape(xode_get_pool(node),xode_get_data(tmp)),"'",s);
tmp = xode_get_nextsibling(tmp);
}
if(flag==0)
xode_spool_add(s,"/>");
else
xode_spool_add(s,">");
}
else
{
xode_spooler(s,"</",xode_get_name(node),">",s);
}
}
static xode_spool _xode_tospool(xode node)
{
xode_spool s;
int level=0,dir=0;
xode tmp;
if(!node || xode_get_type(node) != XODE_TYPE_TAG)
return NULL;
s = xode_spool_newfrompool(xode_get_pool(node));
if(!s) return(NULL);
while(1)
{
if(dir==0)
{
if(xode_get_type(node) == XODE_TYPE_TAG)
{
if(xode_has_children(node))
{
_xode_tag2str(s,node,1);
node = xode_get_firstchild(node);
level++;
continue;
}
else
{
_xode_tag2str(s,node,0);
}
}
else
{
xode_spool_add(s,xode_strescape(xode_get_pool(node),xode_get_data(node)));
}
}
tmp = xode_get_nextsibling(node);
if(!tmp)
{
node = xode_get_parent(node);
level--;
if(level>=0) _xode_tag2str(s,node,2);
if(level<1) break;
dir = 1;
}
else
{
node = tmp;
dir = 0;
}
}
return s;
}
/* External routines */
/*
* xode_new_tag -- create a tag node
* Automatically creates a memory xode_pool for the node.
*
* parameters
* name -- name of the tag
*
* returns
* a pointer to the tag node
* or NULL if it was unsuccessful
*/
xode xode_new(const char* name)
{
return _xode_new(NULL, name, XODE_TYPE_TAG);
}
/*
* alias for 'xode_new'
*/
xode xode_new_tag(const char* name)
{
return _xode_new(NULL, name, XODE_TYPE_TAG);
}
/*
* xode_new_tag_pool -- create a tag node within given pool
*
* parameters
* p -- previously created memory pool
* name -- name of the tag
*
* returns
* a pointer to the tag node
* or NULL if it was unsuccessful
*/
xode xode_new_frompool(xode_pool p, const char* name)
{
return _xode_new(p, name, XODE_TYPE_TAG);
}
/*
* xode_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 unsuccessful
*/
xode xode_insert_tag(xode parent, const char* name)
{
return _xode_insert(parent, name, XODE_TYPE_TAG);
}
/*
* xode_insert_cdata -- append character data to a tag
* If last child of the parent is CDATA, merges CDATA nodes. Otherwise
* creates a CDATA node, and appends it to the parent's child list.
*
* 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 unsuccessful
*/
xode xode_insert_cdata(xode parent, const char* CDATA, unsigned int size)
{
xode result;
if(CDATA == NULL || parent == NULL)
return NULL;
if(size == -1)
size = strlen(CDATA);
if ((parent->lastchild != NULL) && (parent->lastchild->type == XODE_TYPE_CDATA))
{
result = parent->lastchild;
result->data = _xode_merge(result->p, result->data, result->data_sz, CDATA, size);
result->data_sz = result->data_sz + size;
}
else
{
result = _xode_insert(parent, "", XODE_TYPE_CDATA);
if (result != NULL)
{
result->data = (char*)xode_pool_malloc(result->p, size + 1);
memcpy(result->data, CDATA, size);
result->data[size] = '\0';
result->data_sz = size;
}
}
return result;
}
/*
* xode_gettag -- find given tag in an xode 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
* or any combination: "name/name/?attrib", etc
*
* results
* a pointer to the tag matching search criteria
* or NULL if search was unsuccessful
*/
xode xode_get_tag(xode parent, const char* name)
{
char *str, *slash, *qmark, *equals;
xode step, ret;
if(parent == NULL || parent->firstchild == NULL || name == NULL || name == '\0') return NULL;
if(strstr(name, "/") == NULL && strstr(name,"?") == NULL)
return _xode_search(parent->firstchild, name, XODE_TYPE_TAG);
/* jer's note: why can't I modify the name directly, why do I have to strdup it? damn c grrr! */
str = strdup(name);
slash = strstr(str, "/");
qmark = strstr(str, "?");
equals = strstr(str, "=");
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 = xode_get_nextsibling(step))
{
if(xode_get_type(step) != XODE_TYPE_TAG)
continue;
if(*str != '\0')
if(_xode_strcmp(xode_get_name(step),str) != 0)
continue;
if(xode_get_attrib(step,qmark) == NULL)
continue;
if(equals != NULL && _xode_strcmp(xode_get_attrib(step,qmark),equals) != 0)
continue;
break;
}
free(str);
return step;
}
*slash = '\0';
++slash;
for(step = parent->firstchild; step != NULL; step = xode_get_nextsibling(step))
{
if(xode_get_type(step) != XODE_TYPE_TAG) continue;
if(_xode_strcmp(xode_get_name(step),str) != 0)
continue;
ret = xode_get_tag(step, slash);
if(ret != NULL)
{
free(str);
return ret;
}
}
free(str);
return NULL;
}
/* return the cdata from any tag */
char *xode_get_tagdata(xode parent, const char *name)
{
xode tag;
tag = xode_get_tag(parent, name);
if(tag == NULL) return NULL;
return xode_get_data(tag);
}
void xode_put_attrib(xode owner, const char* name, const char* value)
{
xode attrib;
if(owner == NULL || name == NULL || value == NULL) return;
/* If there are no existing attributes, allocate a new one to start
the list */
if (owner->firstattrib == NULL)
{
attrib = _xode_new(owner->p, name, XODE_TYPE_ATTRIB);
owner->firstattrib = attrib;
owner->lastattrib = attrib;
}
else
{
attrib = _xode_search(owner->firstattrib, name, XODE_TYPE_ATTRIB);
if(attrib == NULL)
{
attrib = _xode_appendsibling(owner->lastattrib, name, XODE_TYPE_ATTRIB);
owner->lastattrib = attrib;
}
}
/* Update the value of the attribute */
attrib->data_sz = strlen(value);
attrib->data = xode_pool_strdup(owner->p, value);
}
char* xode_get_attrib(xode owner, const char* name)
{
xode attrib;
if (owner != NULL && owner->firstattrib != NULL)
{
attrib = _xode_search(owner->firstattrib, name, XODE_TYPE_ATTRIB);
if (attrib != NULL)
return (char*)attrib->data;
}
return NULL;
}
void xode_put_vattrib(xode owner, const char* name, void *value)
{
xode attrib;
if (owner != NULL)
{
attrib = _xode_search(owner->firstattrib, name, XODE_TYPE_ATTRIB);
if (attrib == NULL)
{
xode_put_attrib(owner, name, "");
attrib = _xode_search(owner->firstattrib, name, XODE_TYPE_ATTRIB);
}
if (attrib != NULL)
attrib->firstchild = (xode)value;
}
}
void* xode_get_vattrib(xode owner, const char* name)
{
xode attrib;
if (owner != NULL && owner->firstattrib != NULL)
{
attrib = _xode_search(owner->firstattrib, name, XODE_TYPE_ATTRIB);
if (attrib != NULL)
return (void*)attrib->firstchild;
}
return NULL;
}
xode xode_get_firstattrib(xode parent)
{
if (parent != NULL)
return parent->firstattrib;
return NULL;
}
xode xode_get_firstchild(xode parent)
{
if (parent != NULL)
return parent->firstchild;
return NULL;
}
xode xode_get_lastchild(xode parent)
{
if (parent != NULL)
return parent->lastchild;
return NULL;
}
xode xode_get_nextsibling(xode sibling)
{
if (sibling != NULL)
return sibling->next;
return NULL;
}
xode xode_get_prevsibling(xode sibling)
{
if (sibling != NULL)
return sibling->prev;
return NULL;
}
xode xode_get_parent(xode node)
{
if (node != NULL)
return node->parent;
return NULL;
}
char* xode_get_name(xode node)
{
if (node != NULL)
return node->name;
return NULL;
}
char* xode_get_data(xode node)
{
xode cur;
if(node == NULL) return NULL;
if(xode_get_type(node) == XODE_TYPE_TAG) /* loop till we find a CDATA */
{
for(cur = xode_get_firstchild(node); cur != NULL; cur = xode_get_nextsibling(cur))
if(xode_get_type(cur) == XODE_TYPE_CDATA)
return cur->data;
}else{
return node->data;
}
return NULL;
}
int xode_get_datasz(xode node)
{
if( node == NULL )
{
return (int)(long)NULL;
}
else if(xode_get_type(node) == XODE_TYPE_TAG) /* loop till we find a CDATA */
{
xode cur;
for(cur = xode_get_firstchild(node); cur != NULL; cur = xode_get_nextsibling(cur))
if(xode_get_type(cur) == XODE_TYPE_CDATA)
return cur->data_sz;
}else{
return node->data_sz;
}
return (int)(long)NULL;
}
int xode_get_type(xode node)
{
if (node != NULL)
{
return node->type;
}
return (int)(long)NULL;
}
int xode_has_children(xode node)
{
if ((node != NULL) && (node->firstchild != NULL))
return 1;
return 0;
}
int xode_has_attribs(xode node)
{
if ((node != NULL) && (node->firstattrib != NULL))
return 1;
return 0;
}
xode_pool xode_get_pool(xode node)
{
if (node != NULL)
return node->p;
return (xode_pool)NULL;
}
void xode_hide(xode child)
{
xode parent;
if(child == NULL || child->parent == NULL)
return;
parent = child->parent;
/* first fix up at the child level */
_xode_hidesibling(child);
/* next fix up at the parent level */
if(parent->firstchild == child)
parent->firstchild = child->next;
if(parent->lastchild == child)
parent->lastchild = child->prev;
}
void xode_hide_attrib(xode parent, const char *name)
{
xode attrib;
if(parent == NULL || parent->firstattrib == NULL || name == NULL)
return;
attrib = _xode_search(parent->firstattrib, name, XODE_TYPE_ATTRIB);
if(attrib == NULL)
return;
/* first fix up at the child level */
_xode_hidesibling(attrib);
/* next fix up at the parent level */
if(parent->firstattrib == attrib)
parent->firstattrib = attrib->next;
if(parent->lastattrib == attrib)
parent->lastattrib = attrib->prev;
}
/*
* xode2str -- convert given xode tree into a string
*
* parameters
* node -- pointer to the xode structure
*
* results
* a pointer to the created string
* or NULL if it was unsuccessful
*/
char *xode_to_str(xode node)
{
return xode_spool_tostr(_xode_tospool(node));
}
/* loop through both a and b comparing everything, attribs, cdata, children, etc */
int xode_cmp(xode a, xode b)
{
int ret = 0;
while(1)
{
if(a == NULL && b == NULL)
return 0;
if(a == NULL || b == NULL)
return -1;
if(xode_get_type(a) != xode_get_type(b))
return -1;
switch(xode_get_type(a))
{
case XODE_TYPE_ATTRIB:
ret = _xode_strcmp(xode_get_name(a), xode_get_name(b));
if(ret != 0)
return -1;
ret = _xode_strcmp(xode_get_data(a), xode_get_data(b));
if(ret != 0)
return -1;
break;
case XODE_TYPE_TAG:
ret = _xode_strcmp(xode_get_name(a), xode_get_name(b));
if(ret != 0)
return -1;
ret = xode_cmp(xode_get_firstattrib(a), xode_get_firstattrib(b));
if(ret != 0)
return -1;
ret = xode_cmp(xode_get_firstchild(a), xode_get_firstchild(b));
if(ret != 0)
return -1;
break;
case XODE_TYPE_CDATA:
ret = _xode_strcmp(xode_get_data(a), xode_get_data(b));
if(ret != 0)
return -1;
}
a = xode_get_nextsibling(a);
b = xode_get_nextsibling(b);
}
}
xode xode_insert_tagnode(xode parent, xode node)
{
xode child;
child = xode_insert_tag(parent, xode_get_name(node));
if (xode_has_attribs(node))
xode_insert_node(child, xode_get_firstattrib(node));
if (xode_has_children(node))
xode_insert_node(child, xode_get_firstchild(node));
return child;
}
/* places copy of node and node's siblings in parent */
void xode_insert_node(xode parent, xode node)
{
if(node == NULL || parent == NULL)
return;
while(node != NULL)
{
switch(xode_get_type(node))
{
case XODE_TYPE_ATTRIB:
xode_put_attrib(parent, xode_get_name(node), xode_get_data(node));
break;
case XODE_TYPE_TAG:
xode_insert_tagnode(parent, node);
break;
case XODE_TYPE_CDATA:
xode_insert_cdata(parent, xode_get_data(node), xode_get_datasz(node));
}
node = xode_get_nextsibling(node);
}
}
/* produce full duplicate of x with a new xode_pool, x must be a tag! */
xode xode_dup(xode x)
{
xode x2;
if(x == NULL)
return NULL;
x2 = xode_new(xode_get_name(x));
if (xode_has_attribs(x))
xode_insert_node(x2, xode_get_firstattrib(x));
if (xode_has_children(x))
xode_insert_node(x2, xode_get_firstchild(x));
return x2;
}
xode xode_dup_frompool(xode_pool p, xode x)
{
xode x2;
if(x == NULL)
return NULL;
x2 = xode_new_frompool(p, xode_get_name(x));
if (xode_has_attribs(x))
xode_insert_node(x2, xode_get_firstattrib(x));
if (xode_has_children(x))
xode_insert_node(x2, xode_get_firstchild(x));
return x2;
}
xode xode_wrap(xode x,const char *wrapper)
{
xode wrap;
if(x==NULL||wrapper==NULL) return NULL;
wrap=xode_new_frompool(xode_get_pool(x),wrapper);
if(wrap==NULL) return NULL;
wrap->firstchild=x;
wrap->lastchild=x;
x->parent=wrap;
return wrap;
}
void xode_free(xode node)
{
if(node == NULL)
return;
xode_pool_free(node->p);
}
void
_xode_to_prettystr( xode_spool s, xode x, int deep )
{
int i;
xode y;
if(xode_get_type(x) != XODE_TYPE_TAG) return;
for(i=0; i<deep; i++) xode_spool_add(s, "\t");
xode_spooler( s , "<" , xode_get_name(x) , s );
y = xode_get_firstattrib(x);
while( y )
{
xode_spooler( s , " " , xode_get_name(y) , "='", xode_get_data(y) , "'" , s );
y = xode_get_nextsibling( y );
}
xode_spool_add(s,">");
xode_spool_add(s,"\n");
if( xode_get_data(x))
{
for(i=0; i<=deep; i++) xode_spool_add(s, "\t");
xode_spool_add( s , xode_get_data(x));
}
y = xode_get_firstchild(x);
while( y )
{
_xode_to_prettystr(s , y, deep+1);
y = xode_get_nextsibling(y);
xode_spool_add(s,"\n");
}
for(i=0; i<deep; i++) xode_spool_add(s, "\t");
xode_spooler( s , "</" , xode_get_name(x) , ">" , s );
return;
}
char *
xode_to_prettystr( xode x )
{
xode_spool s;
if( !x) return NULL;
s = xode_spool_newfrompool( xode_get_pool(x));
_xode_to_prettystr( s , x, 0 );
return xode_spool_tostr(s);
}