Browse Source

xmlops: new module for xml operations

- implements $xml(name=>spec) pseudo-variable (previously was in
  presence_xml)
- new parameter buf_size to control the max size of internal buffer for xml
  document management
Daniel-Constantin Mierla 15 years ago
parent
commit
e9438550e0

+ 19 - 0
modules/xmlops/Makefile

@@ -0,0 +1,19 @@
+# $Id$
+#
+# XML operations 
+# 
+# 
+# WARNING: do not run this directly, it should be run by the master Makefile
+
+include ../../Makefile.defs
+auto_gen=
+NAME=xmlops.so
+LIBS=
+
+DEFS+=-I/usr/include/libxml2 -I$(LOCALBASE)/include/libxml2 \
+      -I$(LOCALBASE)/include
+LIBS+=-L$(SYSBASE)/include/lib  -L$(LOCALBASE)/lib -lxml2
+
+DEFS+=-DOPENSER_MOD_INTERFACE
+
+include ../../Makefile.modules

+ 80 - 0
modules/xmlops/README

@@ -0,0 +1,80 @@
+1. XMLOPS Module
+
+Daniel-Constantin Mierla
+
+   asipto.com
+   <[email protected]>
+
+   Copyright © 2009 asipto.com
+     __________________________________________________________________
+
+   1.1. Overview
+   1.2. Dependencies
+
+        1.2.1. Kamailio Modules
+        1.2.2. External Libraries or Applications
+
+   1.3. Parameters
+
+        1.3.1. buf_size (integer)
+        1.3.2. xml_ns (str)
+
+   1.4. Pseudo-Variables
+
+        1.4.1. $xml(name=>spec)
+
+1.1. Overview
+
+   This is a module implementing funtions and pseudo-variables for XML
+   operations.
+
+1.2. Dependencies
+
+1.2.1. Kamailio Modules
+
+   The following modules must be loaded before this module:
+     * none.
+
+1.2.2. External Libraries or Applications
+
+   The following libraries or applications must be installed before
+   running kamailio with this module loaded:
+     * libxml - for compilaiton is needed the devel vesion as well.
+
+1.3. Parameters
+
+1.3.1. buf_size (integer)
+
+   Maximum size of the XML buffer.
+
+   Default value is 4096.
+
+   Example 1. Set buf_size parameter
+...
+modparam("xmlops", "buf_size", 8192)
+...
+
+1.3.2. xml_ns (str)
+
+   Register xml namespace prefix. Parameter value must have the format:
+   'prefix=uri'.
+
+   Example 2. Set xml_ns parameter
+...
+modparam("xmlops", "xml_ns", "rpid=urn:ietf:params:xml:ns:pidf:rpid")
+...
+
+1.4. Pseudo-Variables
+
+1.4.1.  $xml(name=>spec)
+
+   Pseudo-variable for XML document operations using xpath syntax. For
+   more see the Peudo-Variables Cookbook.
+
+   Example 3. xml usage
+...
+$xml(x=>doc)
+    = '<?xml version="1.0" encoding="UTF-8"?><a><b>test</b></a>';
+xlog("content of node b: $xml(x=>xpath:/a/b/text())\n");
+$xml(x=>xpath:/a/b) = "1234";
+...

+ 4 - 0
modules/xmlops/doc/Makefile

@@ -0,0 +1,4 @@
+docs = xmlops.xml
+
+docbook_dir = ../../../docbook
+include $(docbook_dir)/Makefile.module

+ 29 - 0
modules/xmlops/doc/functions.xml

@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE section PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+   "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+
+<section id="textopsx.pvs" xmlns:xi="http://www.w3.org/2001/XInclude">
+    <title>Pseudo-Variables</title>
+
+	<section id="pv-xml">
+		<title>
+		<function moreinfo="none">$xml(name=>spec)</function>
+		</title>
+		<para>
+		Pseudo-variable for XML document operations using xpath syntax. For
+		more see the Peudo-Variables Cookbook.
+		</para>
+		<example>
+		<title><function>xml</function> usage</title>
+		<programlisting format="linespecific">
+...
+$xml(x=&gt;doc)
+    = '&lt;?xml version="1.0" encoding="UTF-8"?&gt;&lt;a&gt;&lt;b&gt;test&lt;/b&gt;&lt;/a&gt;';
+xlog("content of node b: $xml(x=&gt;xpath:/a/b/text())\n");
+$xml(x=&gt;xpath:/a/b) = "1234";
+...
+</programlisting>
+		</example>
+	</section>
+
+</section>

+ 41 - 0
modules/xmlops/doc/params.xml

@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE section PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" 
+   "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+
+<section id="xmlops.parameters" xmlns:xi="http://www.w3.org/2001/XInclude">
+    <title>Parameters</title>
+
+    <section id="buf_size">
+	<title><varname>buf_size</varname> (integer)</title>
+	<para>
+	    Maximum size of the XML buffer.
+	</para>
+	<para>
+	    Default value is 4096.
+	</para>
+	<example>
+	    <title>Set <varname>buf_size</varname> parameter</title>
+	    <programlisting>
+...
+modparam("xmlops", "buf_size", 8192)
+...
+	    </programlisting>
+	</example>
+    </section>
+
+	<section id="xml_ns">
+		<title><varname>xml_ns</varname> (str)</title>
+		<para>
+		Register xml namespace prefix. Parameter value must have the format:
+		'prefix=uri'.</para>
+		<example>
+		<title>Set <varname>xml_ns</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("xmlops", "xml_ns", "rpid=urn:ietf:params:xml:ns:pidf:rpid")
+...
+</programlisting>
+		</example>
+	</section>
+
+</section>

+ 66 - 0
modules/xmlops/doc/xmlops.xml

@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE section PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+   "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+
+<section id="xmlops" xmlns:xi="http://www.w3.org/2001/XInclude">
+    <sectioninfo>
+	<authorgroup>
+		<author>
+		<firstname>Daniel-Constantin</firstname>
+		<surname>Mierla</surname>
+		<affiliation><orgname>asipto.com</orgname></affiliation>
+		<email>[email protected]</email>
+		</author>
+	</authorgroup>
+	<copyright>
+	    <year>2009</year>
+	    <holder>asipto.com</holder>
+	</copyright>
+    </sectioninfo>
+
+    <title>XMLOPS Module</title>
+
+    <section id="xmlops.overview">
+	<title>Overview</title>
+	<para>
+		This is a module implementing funtions and pseudo-variables for
+		XML operations.
+	</para>
+    </section>
+	<section>
+	<title>Dependencies</title>
+		<section>
+		<title>Kamailio Modules</title>
+			<para>
+			The following modules must be loaded before this module:
+			</para>
+			<itemizedlist>
+			<listitem>
+			<para>
+				<emphasis>none</emphasis>.
+			</para>
+			</listitem>
+			</itemizedlist>
+		</section>
+		<section>
+		<title>External Libraries or Applications</title>
+			<para>
+			The following libraries or applications must be installed before running
+			kamailio with this module loaded:
+			</para>
+			<itemizedlist>
+			<listitem>
+			<para>
+				<emphasis>libxml</emphasis> - for compilaiton is needed the devel
+				vesion as well.
+			</para>
+			</listitem>
+			</itemizedlist>
+		</section>
+    </section>
+
+    <xi:include href="params.xml"/>
+    <xi:include href="functions.xml"/>
+
+</section>
+

+ 550 - 0
modules/xmlops/pv_xml.c

@@ -0,0 +1,550 @@
+/**
+ * $Id$
+ *
+ * Copyright (C) 2009 Daniel-Constantin Mierla (asipto.com)
+ *
+ * This file is part of kamailio, a free SIP server.
+ *
+ * openser 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
+ *
+ * openser 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <libxml/tree.h>
+#include <libxml/parser.h>
+#include <libxml/xpath.h>
+#include <libxml/xpathInternals.h>
+
+#include "../../mem/mem.h"
+#include "../../parser/parse_param.h"
+#include "../../hashes.h"
+#include "../../dprint.h"
+
+#include "pv_xml.h"
+
+int pv_xml_buf_size = 4095;
+
+typedef struct _pv_xml {
+	str docname;
+	unsigned int docid;
+	str inbuf;
+	str outbuf;
+	int updated;
+    xmlDocPtr doc;
+    xmlXPathContextPtr xpathCtx; 
+    xmlXPathObjectPtr xpathObj; 
+	struct _pv_xml *next;
+} pv_xml_t;
+
+typedef struct _pv_xml_spec {
+	str docname;
+	pv_xml_t *xdoc;
+	int type;
+	pv_elem_t *pve;
+} pv_xml_spec_t;
+
+pv_xml_t *_pv_xml_root = NULL;
+
+param_t *_pv_xml_ns_root = NULL;
+
+pv_xml_t *pv_xml_get_struct(str *name)
+{
+	unsigned int docid;
+	pv_xml_t *it;
+
+	docid = get_hash1_raw(name->s, name->len);
+	it = _pv_xml_root;
+
+	while(it!=NULL)
+	{
+		if(docid == it->docid && name->len==it->docname.len 
+				&& strncmp(name->s, it->docname.s, name->len)==0)
+		{
+			LM_DBG("doc found [%.*s]\n", name->len, name->s);
+			return it;
+		}
+		it = it->next;
+	}
+
+	it = (pv_xml_t*)pkg_malloc(sizeof(pv_xml_t)+2*(pv_xml_buf_size+1));
+	if(it==NULL)
+	{
+		LM_ERR("no more pkg\n");
+		return NULL;
+	}
+	memset(it, 0, sizeof(pv_xml_t)+2*(pv_xml_buf_size+1));
+
+	it->docid = docid;
+	it->docname = *name;
+	it->inbuf.s = (char*)it + sizeof(pv_xml_t);
+	it->outbuf.s = it->inbuf.s + pv_xml_buf_size+1;
+
+	it->next = _pv_xml_root;
+	_pv_xml_root = it;
+	return it;
+}
+
+int pv_xpath_nodes_eval(pv_xml_t *xdoc)
+{
+    int size;
+    int i;
+	xmlNodeSetPtr nodes;
+	char *p;
+	xmlChar *keyword;
+	xmlBufferPtr psBuf;
+
+	if(xdoc==NULL || xdoc->doc==NULL || xdoc->xpathCtx==NULL
+			|| xdoc->xpathObj==NULL)
+		return -1;
+
+    nodes = xdoc->xpathObj->nodesetval;
+	if(nodes==NULL)
+	{
+		xdoc->outbuf.len = 0;
+		xdoc->outbuf.s[xdoc->outbuf.len] = '\0';
+		return 0;
+	}
+	size = nodes->nodeNr;
+    p = xdoc->outbuf.s;
+	for(i = 0; i < size; ++i)
+	{
+		if(nodes->nodeTab[i]==NULL)
+			continue;
+		if(i!=0)
+		{
+			*p = ',';
+			p++;
+		}
+		if(nodes->nodeTab[i]->type == XML_ATTRIBUTE_NODE)
+		{
+			keyword = xmlNodeListGetString(xdoc->doc,
+				nodes->nodeTab[i]->children, 0);
+			if(keyword != NULL)
+			{
+				strcpy(p, (char*)keyword);
+				p += strlen((char*)keyword);
+				xmlFree(keyword);
+				keyword = NULL;
+			}
+		} else {
+			if(nodes->nodeTab[i]->content!=NULL)
+			{
+				strcpy(p, (char*)nodes->nodeTab[i]->content);
+				p += strlen((char*)nodes->nodeTab[i]->content);
+			} else {
+				psBuf = xmlBufferCreate();
+				if(psBuf != NULL && xmlNodeDump(psBuf, xdoc->doc,
+						nodes->nodeTab[i], 0, 0)>0)
+				{
+					strcpy(p, (char*)xmlBufferContent(psBuf));
+					p += strlen((char*)xmlBufferContent(psBuf));
+				}
+				if(psBuf != NULL) xmlBufferFree(psBuf);
+				psBuf = NULL;
+			}
+		}
+	}
+	xdoc->outbuf.len = p - xdoc->outbuf.s;
+	xdoc->outbuf.s[xdoc->outbuf.len] = '\0';
+	return 0;
+}
+
+int pv_xpath_nodes_update(pv_xml_t *xdoc, str *val)
+{
+	xmlNodeSetPtr nodes;
+	const xmlChar* value;
+    int size;
+    int i;
+    
+	if(xdoc==NULL || xdoc->doc==NULL || xdoc->xpathCtx==NULL
+			|| xdoc->xpathObj==NULL || val==NULL)
+		return -1;
+	if(val->len>pv_xml_buf_size)
+	{
+		LM_ERR("internal buffer overflow - %d\n", val->len);
+		return -1;
+	}
+    nodes = xdoc->xpathObj->nodesetval;
+	if(nodes==NULL)
+		return 0;
+    size = nodes->nodeNr;
+
+	value = (const xmlChar*)xdoc->outbuf.s;
+	memcpy(xdoc->outbuf.s, val->s, val->len);
+	xdoc->outbuf.s[val->len] = '\0';
+	xdoc->outbuf.len = val->len;
+    
+	/*
+	 * NOTE: the nodes are processed in reverse order, i.e. reverse document
+	 *       order because xmlNodeSetContent can actually free up descendant
+	 *       of the node and such nodes may have been selected too ! Handling
+	 *       in reverse order ensure that descendant are accessed first, before
+	 *       they get removed. Mixing XPath and modifications on a tree must be
+	 *       done carefully !
+	 */
+	for(i = size - 1; i >= 0; i--) {
+		if(nodes->nodeTab[i]==NULL)
+			continue;
+	
+		xmlNodeSetContent(nodes->nodeTab[i], value);
+		/*
+		 * All the elements returned by an XPath query are pointers to
+		 * elements from the tree *except* namespace nodes where the XPath
+		 * semantic is different from the implementation in libxml2 tree.
+		 * As a result when a returned node set is freed when
+		 * xmlXPathFreeObject() is called, that routine must check the
+		 * element type. But node from the returned set may have been removed
+		 * by xmlNodeSetContent() resulting in access to freed data.
+		 * This can be exercised by running
+		 *       valgrind xpath2 test3.xml '//discarded' discarded
+		 * There is 2 ways around it:
+		 *   - make a copy of the pointers to the nodes from the result set 
+		 *     then call xmlXPathFreeObject() and then modify the nodes
+		 * or
+		 *   - remove the reference to the modified nodes from the node set
+		 *     as they are processed, if they are not namespace nodes.
+		 */
+		if (nodes->nodeTab[i]->type != XML_NAMESPACE_DECL)
+			nodes->nodeTab[i] = NULL;
+	}
+	xdoc->outbuf.s[0] = '\0';
+	xdoc->outbuf.len = 0;
+	return 0;
+}
+
+void pv_xml_register_ns(xmlXPathContextPtr xpathCtx)
+{
+	param_t *ns;
+	ns = _pv_xml_ns_root;
+	while(ns) {
+		xmlXPathRegisterNs(xpathCtx, (xmlChar*)ns->name.s,
+				(xmlChar*)ns->body.s);
+		ns = ns->next;
+	}
+}
+
+int pv_get_xml(struct sip_msg *msg,  pv_param_t *param,
+		pv_value_t *res)
+{
+	pv_xml_spec_t *pxs = NULL;
+	str xpaths;
+	int size = 0;
+	xmlChar *xmem = NULL;
+
+	pxs = (pv_xml_spec_t*)param->pvn.u.dname;
+	if(pxs->xdoc==NULL)
+		return -1;
+
+	switch(pxs->type) {
+		case 0:
+			/* get document */
+			if(pxs->xdoc->inbuf.len<=0)
+				return pv_get_null(msg, param, res);
+			if(pxs->xdoc->doc == NULL || pxs->xdoc->updated == 0)
+				return pv_get_strval(msg, param, res, &pxs->xdoc->inbuf);
+			xmlDocDumpMemory(pxs->xdoc->doc, &xmem, &size);
+			if(xmem!=NULL)
+			{
+				if(size>pv_xml_buf_size)
+				{
+					xmlFree(xmem);
+					return pv_get_null(msg, param, res);
+				}
+				memcpy(pxs->xdoc->outbuf.s, xmem, size);
+				pxs->xdoc->outbuf.s[size] = '\0';
+				pxs->xdoc->outbuf.len = size;
+				xmlFree(xmem);
+				return pv_get_strval(msg, param, res, &pxs->xdoc->outbuf);
+			}
+			return pv_get_null(msg, param, res);
+		break;
+		case 1:
+			/* get xpath element */
+			if(pxs->xdoc->doc == NULL)
+			{
+				if(pxs->xdoc->inbuf.len<=0)
+					return pv_get_null(msg, param, res);
+				pxs->xdoc->doc = xmlParseMemory(pxs->xdoc->inbuf.s,
+						pxs->xdoc->inbuf.len);
+				if(pxs->xdoc->doc == NULL)
+					return pv_get_null(msg, param, res);
+			}
+			if(pxs->xdoc->xpathCtx == NULL)
+			{
+				pxs->xdoc->xpathCtx = xmlXPathNewContext(pxs->xdoc->doc);
+				if(pxs->xdoc->xpathCtx == NULL)
+				{
+					LM_ERR("unable to create new XPath context\n");
+					xmlFreeDoc(pxs->xdoc->doc);
+					pxs->xdoc->doc = NULL;
+					return pv_get_null(msg, param, res);
+				}
+			}
+			if(pv_printf_s(msg, pxs->pve, &xpaths)!=0)
+			{
+				LM_ERR("cannot get xpath string\n");
+				return pv_get_null(msg, param, res);
+			}
+			
+			/* Evaluate xpath expression */
+			pv_xml_register_ns(pxs->xdoc->xpathCtx);
+			pxs->xdoc->xpathObj = xmlXPathEvalExpression(
+					(const xmlChar*)xpaths.s, pxs->xdoc->xpathCtx);
+			if(pxs->xdoc->xpathObj == NULL)
+			{
+				LM_ERR("unable to evaluate xpath expression [%s/%d]\n",
+						xpaths.s, xpaths.len);
+				xmlXPathFreeContext(pxs->xdoc->xpathCtx); 
+				xmlFreeDoc(pxs->xdoc->doc); 
+				pxs->xdoc->xpathCtx = NULL; 
+				pxs->xdoc->doc = NULL; 
+				return pv_get_null(msg, param, res);
+			}
+			/* Print results */
+			if(pv_xpath_nodes_eval(pxs->xdoc)<0)
+			{
+				xmlXPathFreeObject(pxs->xdoc->xpathObj);
+				xmlXPathFreeContext(pxs->xdoc->xpathCtx); 
+				xmlFreeDoc(pxs->xdoc->doc);
+				pxs->xdoc->xpathObj = NULL;
+				pxs->xdoc->xpathCtx = NULL; 
+				pxs->xdoc->doc = NULL; 
+				return pv_get_null(msg, param, res);
+			}
+			xmlXPathFreeObject(pxs->xdoc->xpathObj);
+			pxs->xdoc->xpathObj = NULL;
+			return pv_get_strval(msg, param, res, &pxs->xdoc->outbuf);
+		break;
+		default:
+			return pv_get_null(msg, param, res);
+	}
+	return pv_get_null(msg, param, res);
+}
+
+int pv_set_xml(struct sip_msg* msg, pv_param_t *param,
+		int op, pv_value_t *val)
+{
+	pv_xml_spec_t *pxs = NULL;
+	str xpaths;
+
+	pxs = (pv_xml_spec_t*)param->pvn.u.dname;
+	if(pxs->xdoc==NULL)
+		return -1;
+	if(!(val->flags&PV_VAL_STR))
+		return -1;
+
+	switch(pxs->type) {
+		case 0:
+			/* set document */
+			if(pxs->xdoc->doc!=NULL)
+			{
+				if(pxs->xdoc->xpathCtx!=NULL)
+				{
+					xmlXPathFreeContext(pxs->xdoc->xpathCtx);
+					pxs->xdoc->xpathCtx = NULL;
+				}
+				xmlFreeDoc(pxs->xdoc->doc);
+				pxs->xdoc->doc = NULL;
+			}
+			if(val->rs.len>pv_xml_buf_size)
+			{
+				LM_ERR("local buffer overflow - %d\n", val->rs.len);
+				return -1;
+			}
+			memcpy(pxs->xdoc->inbuf.s, val->rs.s, val->rs.len);
+			pxs->xdoc->inbuf.s[val->rs.len] = '\0';
+			pxs->xdoc->inbuf.len = val->rs.len;
+			pxs->xdoc->updated = 0;
+			return 0;
+		break;
+		case 1:
+			/* set xpath element */
+			if(pxs->xdoc->doc == NULL)
+			{
+				if(pxs->xdoc->inbuf.len<=0)
+					return -1;
+				pxs->xdoc->doc = xmlParseMemory(pxs->xdoc->inbuf.s,
+						pxs->xdoc->inbuf.len);
+				if(pxs->xdoc->doc == NULL)
+					return -1;
+			}
+			if(pxs->xdoc->xpathCtx == NULL)
+			{
+				pxs->xdoc->xpathCtx = xmlXPathNewContext(pxs->xdoc->doc);
+				if(pxs->xdoc->xpathCtx == NULL)
+				{
+					LM_ERR("unable to create new XPath context\n");
+					xmlFreeDoc(pxs->xdoc->doc);
+					pxs->xdoc->doc = NULL;
+					return -1;
+				}
+			}
+			if(pv_printf_s(msg, pxs->pve, &xpaths)!=0)
+			{
+				LM_ERR("cannot get xpath string\n");
+				return -1;
+			}
+			
+			/* Evaluate xpath expression */
+			pxs->xdoc->xpathObj = xmlXPathEvalExpression(
+					(const xmlChar*)xpaths.s, pxs->xdoc->xpathCtx);
+			if(pxs->xdoc->xpathObj == NULL)
+			{
+				LM_ERR("unable to evaluate xpath expression [%s]\n", xpaths.s);
+				xmlXPathFreeContext(pxs->xdoc->xpathCtx); 
+				xmlFreeDoc(pxs->xdoc->doc); 
+				pxs->xdoc->xpathCtx = NULL; 
+				pxs->xdoc->doc = NULL; 
+				return -1;
+			}
+			/* Set value */
+			if(pv_xpath_nodes_update(pxs->xdoc, &val->rs)<0)
+			{
+				LM_ERR("unable to update xpath [%s] - [%.*s]\n", xpaths.s,
+						val->rs.len, val->rs.s);
+				xmlXPathFreeObject(pxs->xdoc->xpathObj);
+				xmlXPathFreeContext(pxs->xdoc->xpathCtx); 
+				xmlFreeDoc(pxs->xdoc->doc); 
+				pxs->xdoc->xpathObj = NULL;
+				pxs->xdoc->xpathCtx = NULL; 
+				pxs->xdoc->doc = NULL; 
+				return -1;
+			}
+			pxs->xdoc->updated = 1;
+			xmlXPathFreeObject(pxs->xdoc->xpathObj);
+			pxs->xdoc->xpathObj = NULL;
+			return 0;
+		break;
+		default:
+			return -1;
+	}
+
+	return 0;
+}
+
+int pv_parse_xml_name(pv_spec_p sp, str *in)
+{
+	pv_xml_spec_t *pxs = NULL;
+	char *p;
+	str pvs;
+
+	if(in->s==NULL || in->len<=0)
+		return -1;
+
+	pxs = (pv_xml_spec_t*)pkg_malloc(sizeof(pv_xml_spec_t));
+	if(pxs==NULL)
+		return -1;
+
+	memset(pxs, 0, sizeof(pv_xml_spec_t));
+
+	p = in->s;
+
+	while(p<in->s+in->len && (*p==' ' || *p=='\t' || *p=='\n' || *p=='\r'))
+		p++;
+	if(p>in->s+in->len || *p=='\0')
+		goto error;
+	pxs->docname.s = p;
+	while(p < in->s + in->len)
+	{
+		if(*p=='=' || *p==' ' || *p=='\t' || *p=='\n' || *p=='\r')
+			break;
+		p++;
+	}
+	if(p>in->s+in->len || *p=='\0')
+		goto error;
+	pxs->docname.len = p - pxs->docname.s;
+	if(*p!='=')
+	{
+		while(p<in->s+in->len && (*p==' ' || *p=='\t' || *p=='\n' || *p=='\r'))
+			p++;
+		if(p>in->s+in->len || *p=='\0' || *p!='=')
+			goto error;
+	}
+	p++;
+	if(*p!='>')
+		goto error;
+	p++;
+
+	pvs.len = in->len - (int)(p - in->s);
+	pvs.s = p;
+	LM_DBG("xmldoc [%.*s] - key [%.*s]\n", pxs->docname.len, pxs->docname.s,
+			pvs.len, pvs.s);
+	if(pvs.len>=3 && strncmp(pvs.s, "doc", 3)==0) {
+		pxs->type = 0;
+	} else if(pvs.len>6 && strncmp(pvs.s, "xpath:", 6)==0) {
+		pvs.s += 6;
+		pvs.len -= 6;
+		pxs->type = 1;
+		LM_DBG("*** xpath expr [%.*s]\n", pvs.len, pvs.s);
+		if(pv_parse_format(&pvs, &pxs->pve)<0 || pxs->pve==NULL)
+		{
+			LM_ERR("wrong xpath format [%.*s]\n", in->len, in->s);
+			goto error;
+		}
+	} else {
+		LM_ERR("unknown key type [%.*s]\n", in->len, in->s);
+		goto error;
+	}
+	pxs->xdoc = pv_xml_get_struct(&pxs->docname);
+	sp->pvp.pvn.u.dname = (void*)pxs;
+	sp->pvp.pvn.type = PV_NAME_OTHER;
+	return 0;
+
+error:
+	if(pxs!=NULL)
+		pkg_free(pxs);
+	return -1;
+}
+
+int pv_xml_ns_param(modparam_t type, void *val)
+{
+	char *p;
+	param_t *ns;
+
+	if(val==NULL)
+		goto error;
+	ns = (param_t*)pkg_malloc(sizeof(param_t));
+
+	if(ns==NULL)
+	{
+		LM_ERR("no more pkg\n");
+		goto error;
+	}
+	memset(ns, 0, sizeof(param_t));
+
+	p = strchr((const char*)val, '=');
+	if(p==NULL)
+	{
+		ns->name.s = "";
+		ns->body.s = (char*)val;
+		ns->body.len = strlen(ns->body.s);
+	} else {
+		*p = 0;
+		p++;
+		ns->name.s = (char*)val;
+		ns->name.len = strlen(ns->name.s);
+		ns->body.s = p;
+		ns->body.len = strlen(ns->body.s);
+	}
+	ns->next = _pv_xml_ns_root;
+	_pv_xml_ns_root = ns;
+	return 0;
+error:
+	return -1;
+
+}
+

+ 38 - 0
modules/xmlops/pv_xml.h

@@ -0,0 +1,38 @@
+/**
+ * $Id$
+ *
+ * Copyright (C) 2009 Daniel-Constantin Mierla (asipto.com)
+ *
+ * This file is part of kamailio, a free SIP server.
+ *
+ * openser 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
+ *
+ * openser 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+		       
+#ifndef _PV_XML_H_
+#define _PV_XML_H_
+
+#include "../../sr_module.h"
+#include "../../pvar.h"
+
+int pv_get_xml(struct sip_msg *msg,  pv_param_t *param,
+		pv_value_t *res);
+int pv_set_xml(struct sip_msg* msg, pv_param_t *param,
+		int op, pv_value_t *val);
+
+int pv_parse_xml_name(pv_spec_p sp, str *in);
+
+int pv_xml_ns_param(modparam_t type, void *val);
+
+#endif

+ 74 - 0
modules/xmlops/xmlops_mod.c

@@ -0,0 +1,74 @@
+/**
+ * $Id$
+ *
+ * Copyright (C) 2009 Daniel-Constantin Mierla (asipto.com)
+ *
+ * This file is part of kamailio, a free SIP server.
+ *
+ * openser 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
+ *
+ * openser 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/*! \file
+ * \brief Kamailio xmlops :: Core
+ * \ingroup xmlops
+ */
+
+/*! \defgroup xmlops Xmlops :: This module implements a range of XML-based
+ * operations
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <libxml/parser.h>
+#include <time.h>
+
+#include "../../sr_module.h"
+#include "../../dprint.h"
+#include "pv_xml.h"
+
+MODULE_VERSION
+
+extern int pv_xml_buf_size;
+
+static pv_export_t mod_pvs[] = {
+	{ {"xml", sizeof("xml")-1}, PVT_OTHER, pv_get_xml, pv_set_xml,
+		pv_parse_xml_name, 0, 0, 0 },
+	{ {0, 0}, 0, 0, 0, 0, 0, 0, 0 }
+};
+
+static param_export_t params[]={
+	{ "buf_size",  INT_PARAM, &pv_xml_buf_size },
+	{ "xml_ns",    STR_PARAM|USE_FUNC_PARAM, (void*)pv_xml_ns_param },
+	{ 0, 0, 0}
+};
+
+/** module exports */
+struct module_exports exports= {
+	"presence_xml",		/* module name */
+	 DEFAULT_DLFLAGS,	/* dlopen flags */
+	 0,  			/* exported functions */
+	 params,		/* exported parameters */
+	 0,				/* exported statistics */
+	 0,				/* exported MI functions */
+	 mod_pvs,		/* exported pseudo-variables */
+	 0,				/* extra processes */
+	 0,				/* module initialization function */
+	 0,				/* response handling function */
+ 	 0,				/* destroy function */
+	 0				/* per-child init function */
+};
+