Browse Source

xhttp_pi: new web provisioning interface module

osas 13 years ago
parent
commit
e7a75db198

+ 324 - 0
examples/pi_framework.xml

@@ -0,0 +1,324 @@
+<?xml version="1.0"?>
+<framework>
+	<!--	Declare all db connections
+		Each db connection MUST have:
+		 - an "id" to be identified by a tabe
+		 - a URL pointing to the actual database
+		 Supported databases:
+		 * berkeley
+		 * cassandra
+		 * cluster
+		 * flatstore
+		 * mysql
+		 * oracle
+		 * postgres
+		 * sqlite
+		 * text
+		 * unixodbc
+	-->
+	<db_url id="mysql">mysql://kamailio:kamailiorw@localhost/kamailio</db_url>
+	<!--
+	<db_url id="dbtext">text:///usr/local/etc/kamailio/dbtext</db_url>
+	-->
+	<!--	Declare all tables
+		Each table MUST have:
+		 - an "id" to be identified by a command
+		 - a "table_name" pointing to the actual table name in the database
+		 - a "db_url_id" to identify the db connection
+		 - multiple "column" nodes with "field"i, "validation" and "type":
+			/* type */
+		DB1_INT,		/**< represents an 32 bit integer number */
+		DB1_BIGINT,	/**< represents an 64 bit integer number */
+		DB1_DOUBLE,	/**< represents a floating point number */
+		DB1_STRING,	/**< represents a zero terminated const char* */
+		DB1_STR,		/**< represents a string of 'str' type */
+		DB1_DATETIME,	/**< represents date and time */
+		DB1_BLOB,	/**< represents a large binary object */
+		DB1_BITMAP	/**< an one-dimensional array of 32 flags */
+			/* validation */
+		P_HOST_PORT	/**< represents [proto:]host[:port] */
+		P_IPV4_PORT	/**< represents [proto:]IPv4[:port] */
+		IPV4		/**< represents an IPv4 */
+		URI		/**< represents a SIP URI */
+		URI_IPV4HOST	/**< represents a SIP URI w/ IPv4 as host */
+	-->
+	<db_table id="dispatcher"><table_name>dispatcher</table_name>
+		<db_url_id>mysql</db_url_id>
+		<column><field>id</field>		<type>DB1_INT</type></column>
+		<column><field>setid</field>		<type>DB1_INT</type></column>
+		<column><field>destination</field>	<type>DB1_STR</type>
+			<validate>URI_IPV4HOST</validate></column>
+		<column><field>flags</field>		<type>DB1_INT</type></column>
+		<column><field>priority</field>		<type>DB1_INT</type></column>
+		<column><field>attrs</field>		<type>DB1_STR</type></column>
+		<column><field>description</field>	<type>DB1_STR</type></column>
+	</db_table>
+	<db_table id="dialplan"><table_name>dialplan</table_name>
+		<db_url_id>mysql</db_url_id>
+		<column><field>id</field>		<type>DB1_INT</type></column>
+		<column><field>dpid</field>		<type>DB1_INT</type></column>
+		<column><field>pr</field>		<type>DB1_INT</type></column>
+		<column><field>match_op</field>		<type>DB1_INT</type></column>
+		<column><field>match_exp</field>	<type>DB1_STR</type></column>
+		<column><field>match_len</field>	<type>DB1_INT</type></column>
+		<column><field>subst_exp</field>	<type>DB1_STR</type></column>
+		<column><field>repl_exp</field>		<type>DB1_STR</type></column>
+		<column><field>attrs</field>		<type>DB1_STRING</type></column>
+	</db_table>
+	<!--	Declare all mod
+		Each mod must have:
+		 - a "mod_name"
+		 - at least one "cmd"
+		Each cmd must have:
+		 - a "cmd_name"
+		 - a "db_table_id"
+		 - a "cmd_type":	c  q  o
+			DB1_QUERY	ov m  o
+			DB1_INSERT	-  mv -
+			DB1_DELETE	mv -  -
+			DB1_UPDATE	ov mv -
+			DB1_REPLACE	-  mv -
+		Clause cols can have the following operators:
+		 - "<"		&lt;
+		 - ">"		&gt;
+		 - "="		=
+		 - "<="		&lt;=
+		 - ">="		&gt;=
+		 - "!="		!=
+	-->
+	<!-- dispatcher provisioning -->
+	<mod>   <mod_name>dispatcher</mod_name>
+		<cmd>   <cmd_name>show_destinations_with_small_setid</cmd_name>
+			<db_table_id>dispatcher</db_table_id>
+			<cmd_type>DB1_QUERY</cmd_type>
+			<clause_cols>
+				<col><field>setid</field><operator>&lt;</operator></col>
+			</clause_cols>
+			<query_cols>
+				<col><field>id</field></col>
+				<col><field>setid</field></col>
+				<col><field>destination</field></col>
+				<col><field>description</field></col>
+			</query_cols>
+		</cmd>
+		<cmd>   <cmd_name>show_all</cmd_name>
+			<db_table_id>dispatcher</db_table_id>
+			<cmd_type>DB1_QUERY</cmd_type>
+			<query_cols>
+				<col><field>id</field></col>
+				<col><field>setid</field></col>
+				<col><field>destination</field></col>
+				<col><field>flags</field></col>
+				<col><field>priority</field></col>
+				<col><field>attrs</field></col>
+				<col><field>description</field></col>
+			</query_cols>
+		</cmd>
+		<cmd>   <cmd_name>update_setid</cmd_name>
+			<db_table_id>dispatcher</db_table_id>
+			<cmd_type>DB1_UPDATE</cmd_type>
+			<clause_cols>
+				<col><field>id</field><operator>=</operator></col>
+			</clause_cols>
+			<query_cols>
+				<col><field>setid</field></col>
+			</query_cols>
+		</cmd>
+		<cmd>   <cmd_name>update_destination</cmd_name>
+			<db_table_id>dispatcher</db_table_id>
+			<cmd_type>DB1_UPDATE</cmd_type>
+			<clause_cols>
+				<col><field>id</field><operator>=</operator></col>
+			</clause_cols>
+			<query_cols>
+				<col><field>destination</field></col>
+			</query_cols>
+		</cmd>
+		<cmd>   <cmd_name>update_attr</cmd_name>
+			<db_table_id>dispatcher</db_table_id>
+			<cmd_type>DB1_UPDATE</cmd_type>
+			<clause_cols>
+				<col><field>id</field><operator>=</operator></col>
+			</clause_cols>
+			<query_cols>
+				<col><field>attrs</field></col>
+			</query_cols>
+		</cmd>
+		<cmd>   <cmd_name>update_description</cmd_name>
+			<db_table_id>dispatcher</db_table_id>
+			<cmd_type>DB1_UPDATE</cmd_type>
+			<clause_cols>
+				<col><field>id</field><operator>=</operator></col>
+			</clause_cols>
+			<query_cols>
+				<col><field>description</field></col>
+			</query_cols>
+		</cmd>
+		<cmd>   <cmd_name>add_gw</cmd_name>
+			<db_table_id>dispatcher</db_table_id>
+			<cmd_type>DB1_INSERT</cmd_type>
+			<query_cols>
+				<col><field>setid</field></col>
+				<col><field>destination</field></col>
+				<!--<col><field>flags</field></col>-->
+				<col><field>priority</field></col>
+				<col><field>attrs</field></col>
+				<col><field>description</field></col>
+			</query_cols>
+		</cmd>
+		<cmd>   <cmd_name>add_server_with_setid_100</cmd_name>
+			<db_table_id>dispatcher</db_table_id>
+			<cmd_type>DB1_INSERT</cmd_type>
+			<query_cols>
+				<col><field>setid</field>
+					<value id="100">100</value>
+				</col>
+				<col><field>destination</field></col>
+				<!--<col><field>flags</field></col>-->
+				<col><field>priority</field></col>
+				<col><field>attrs</field></col>
+				<col><field>description</field></col>
+			</query_cols>
+		</cmd>
+		<cmd>   <cmd_name>delete_by_id</cmd_name>
+			<db_table_id>dispatcher</db_table_id>
+			<cmd_type>DB1_DELETE</cmd_type>
+			<clause_cols>
+				<col><field>id</field><operator>=</operator></col>
+			</clause_cols>
+		</cmd>
+	</mod>
+	
+	<!-- dialplan provisioning -->
+	<mod>	<mod_name>dialplan</mod_name>
+		<cmd>	<cmd_name>show_all</cmd_name>
+			<db_table_id>dialplan</db_table_id>
+			<cmd_type>DB1_QUERY</cmd_type>
+			<query_cols>
+				<col><field>id</field></col>
+				<col><field>dpid</field></col>
+				<col><field>pr</field></col>
+				<col><field>match_op</field></col>
+				<col><field>match_exp</field></col>
+				<!--<col><field>match_len</field></col>-->
+				<col><field>subst_exp</field></col>
+				<col><field>repl_exp</field></col>
+				<col><field>attrs</field></col>
+			</query_cols>
+			<order_by_cols><col><field>id</field></col></order_by_cols>
+		</cmd>
+		<cmd>	<cmd_name>show_dpid</cmd_name>
+			<db_table_id>dialplan</db_table_id>
+			<cmd_type>DB1_QUERY</cmd_type>
+			<clause_cols>
+				<col><field>dpid</field><operator>=</operator></col>
+			</clause_cols>
+			<query_cols>
+				<col><field>id</field></col>
+				<col><field>dpid</field></col>
+				<col><field>pr</field></col>
+				<col><field>match_op</field></col>
+				<col><field>match_exp</field></col>
+				<col><field>match_len</field></col>
+				<col><field>subst_exp</field></col>
+				<col><field>repl_exp</field></col>
+				<col><field>attrs</field></col>
+			</query_cols>
+			<order_by_cols><col><field>id</field></col></order_by_cols>
+		</cmd>
+		<cmd>	<cmd_name>show_exact_matching</cmd_name>
+			<db_table_id>dialplan</db_table_id>
+			<cmd_type>DB1_QUERY</cmd_type>
+			<clause_cols>
+				<col><field>match_op</field><operator>=</operator>
+					<value id="equal">0</value></col>
+			</clause_cols>
+			<query_cols>
+				<col><field>id</field></col>
+				<col><field>dpid</field></col>
+				<col><field>pr</field></col>
+				<col><field>match_op</field></col>
+				<col><field>match_exp</field></col>
+				<col><field>match_len</field></col>
+				<col><field>subst_exp</field></col>
+				<col><field>repl_exp</field></col>
+				<col><field>attrs</field></col>
+			</query_cols>
+		</cmd>
+		<cmd>	<cmd_name>show_regex_matching</cmd_name>
+			<db_table_id>dialplan</db_table_id>
+			<cmd_type>DB1_QUERY</cmd_type>
+			<clause_cols>
+				<col><field>match_op</field><operator>=</operator>
+					<value id="regex">1</value></col>
+			</clause_cols>
+			<query_cols>
+				<col><field>id</field></col>
+				<col><field>dpid</field></col>
+				<col><field>pr</field></col>
+				<col><field>match_op</field></col>
+				<col><field>match_exp</field></col>
+				<col><field>match_len</field></col>
+				<col><field>subst_exp</field></col>
+				<col><field>repl_exp</field></col>
+				<col><field>attrs</field></col>
+			</query_cols>
+		</cmd>
+		<cmd>	<cmd_name>add</cmd_name>
+			<db_table_id>dialplan</db_table_id>
+			<cmd_type>DB1_INSERT</cmd_type>
+			<query_cols>
+				<col><field>dpid</field></col>
+				<col><field>pr</field></col>
+				<col><field>match_op</field>
+					<value id="equal">0</value>
+					<value id="regexp">1</value>
+				</col>
+				<col><field>match_exp</field></col>
+				<col><field>match_len</field></col>
+				<col><field>attrs</field></col>
+			</query_cols>
+		</cmd>
+		<cmd>	<cmd_name>delete</cmd_name>
+			<db_table_id>dialplan</db_table_id>
+			<cmd_type>DB1_DELETE</cmd_type>
+			<clause_cols>
+				<col><field>id</field><operator>=</operator></col>
+			</clause_cols>
+		</cmd>
+		<cmd>	<cmd_name>update_attr</cmd_name>
+			<db_table_id>dialplan</db_table_id>
+			<cmd_type>DB1_UPDATE</cmd_type>
+			<clause_cols>
+				<col><field>id</field><operator>=</operator></col>
+			</clause_cols>
+			<query_cols>
+				<col><field>attrs</field></col>
+			</query_cols>
+		</cmd>
+		<cmd>	<cmd_name>update_repl_exp</cmd_name>
+			<db_table_id>dialplan</db_table_id>
+			<cmd_type>DB1_UPDATE</cmd_type>
+			<clause_cols>
+				<col><field>id</field><operator>=</operator></col>
+			</clause_cols>
+			<query_cols>
+				<col><field>repl_exp</field></col>
+			</query_cols>
+		</cmd>
+		<cmd>	<cmd_name>replace</cmd_name>
+			<db_table_id>dialplan</db_table_id>
+			<cmd_type>DB1_REPLACE</cmd_type>
+			<query_cols>
+				<col><field>dpid</field></col>
+				<col><field>pr</field></col>
+				<col><field>match_op</field></col>
+				<col><field>match_exp</field></col>
+				<col><field>match_len</field></col>
+				<col><field>subst_exp</field></col>
+				<col><field>repl_exp</field></col>
+				<col><field>attrs</field></col>
+			</query_cols>
+		</cmd>
+	</mod>
+</framework>

+ 38 - 0
examples/xhttp.cfg

@@ -0,0 +1,38 @@
+debug=3
+fork=yes
+log_stderror=no
+
+auto_aliases=no
+
+tcp_connection_lifetime=3605
+tcp_accept_no_cl=yes
+
+mpath="/usr/local/lib64/kamailio/modules_k/:/usr/local/lib64/kamailio/modules/"
+loadmodule "/usr/local/lib64/kamailio/modules_k/xlog.so"
+loadmodule "/usr/local/lib64/kamailio/modules/db_mysql.so"
+loadmodule "/usr/local/lib64/kamailio/modules/sl.so"
+loadmodule "/usr/local/lib64/kamailio/modules_k/pv.so"
+loadmodule "/usr/local/lib64/kamailio/modules/xhttp.so"
+loadmodule "/usr/local/lib64/kamailio/modules/xhttp_rpc.so"
+ modparam("xhttp_rpc", "xhttp_rpc_root", "http_rpc")
+loadmodule "/usr/local/lib64/kamailio/modules/xhttp_pi.so"
+ modparam("xhttp_pi", "xhttp_pi_root", "http_pi")
+ modparam("xhttp_pi", "framework", "/usr/local/etc/kamailio/pi_framework.xml")
+
+route{
+	exit;
+}
+
+event_route[xhttp:request] {
+	$var(xhttp_rpc_root) = $(hu{s.substr,0,9});
+	if ($var(xhttp_rpc_root) == "/http_rpc") {
+		dispatch_xhttp_rpc();
+	}
+	$var(xhttp_rpc_root) = $(hu{s.substr,0,8});
+	if ($var(xhttp_rpc_root) == "/http_pi") {
+		dispatch_xhttp_pi();
+	}
+	else
+		xhttp_reply("200", "OK", "text/html",
+        		"<html><body>Wrong URL $hu</body></html>");
+}

+ 20 - 0
modules/xhttp_pi/Makefile

@@ -0,0 +1,20 @@
+# $Id$
+#
+# 
+# WARNING: do not run this directly, it should be run by the master Makefile
+
+include ../../Makefile.defs
+auto_gen=
+NAME=xhttp_pi.so
+DEFS +=
+LIBS +=
+
+DEFS+=-I/usr/include/libxml2 -I$(LOCALBASE)/include/libxml2 \
+      -I$(LOCALBASE)/include
+LIBS+=-L$(LOCALBASE)/lib -lxml2
+
+DEFS+=-DKAMAILIO_MOD_INTERFACE
+
+SERLIBPATH=../../lib
+SER_LIBS+=$(SERLIBPATH)/srdb1/srdb1
+include ../../Makefile.modules

+ 4 - 0
modules/xhttp_pi/doc/Makefile

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

+ 37 - 0
modules/xhttp_pi/doc/xhttp_pi.xml

@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding='ISO-8859-1'?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
+"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd" [
+
+<!-- Include general documentation entities -->
+<!ENTITY % docentities SYSTEM "../../../docbook/entities.xml">
+%docentities;
+
+]>
+
+<book xmlns:xi="http://www.w3.org/2001/XInclude">
+    <bookinfo>
+	<title>xHTTP_PI Module</title>
+	<productname class="trade">sip-router.org</productname>
+	<authorgroup>
+	    <author>
+		<firstname>Ovidiu</firstname>
+		<surname>Sas</surname>
+		<email>[email protected]</email>
+	    </author>
+	    <editor>
+		<firstname>Ovidiu</firstname>
+		<surname>Sas</surname>
+		<email>[email protected]</email>
+	    </editor>
+	</authorgroup>
+	<copyright>
+	    <year>2012</year>
+	    <holder><ulink url='http://www.voipembedded.com'>VoIPEmbedded Inc.</ulink></holder>
+	</copyright>
+    </bookinfo>
+    <toc></toc>
+    
+    <xi:include href="xhttp_pi_admin.xml"/>
+    
+    
+</book>

+ 366 - 0
modules/xhttp_pi/doc/xhttp_pi_admin.xml

@@ -0,0 +1,366 @@
+<?xml version="1.0" encoding='ISO-8859-1'?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
+"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd" [
+
+<!-- Include general documentation entities -->
+<!ENTITY % docentities SYSTEM "../../../docbook/entities.xml">
+%docentities;
+
+]>
+<!-- Module User's Guide -->
+
+<chapter>
+	
+	<title>&adminguide;</title>
+	
+	<section>
+	<title>Overview</title>
+	<para>
+		This module provides a web provisioning interface
+		for &kamailio;.  It is using the &kamailio;'s internal
+		database API to provide a simple way of manipulating
+		records inside &kamailio;'s tables.
+	</para>
+	<para>
+		The module offers:
+		<itemizedlist>
+			<listitem><para>
+			ability to connect to multiple/different databases
+			through &kamailio;'s db API;
+			(all &kamailio;'s databases are supported);
+			</para></listitem>
+			<listitem><para>
+			ability to perform data input validation through &kamailio; API;
+			</para></listitem>
+			<listitem><para>
+			ability to reconfigure the interface layout on the fly by
+			reloading the config from the xml framework via the rpc
+			command interface.
+			</para></listitem>
+		</itemizedlist>
+	</para>
+	</section>
+
+	<section>
+	<title>Usage</title>
+	<para>
+		The layout of the provisionning interface is controlled via an external
+		xml file (see the framework parameter).
+		An example of a framework xml file is provided inside the &kamailio;'s
+		examples directory.
+		The xml framework file is organized in three distinctive blocks:
+		<itemizedlist>
+			<listitem><para>
+			database connection definition block
+			</para></listitem>
+			<listitem><para>
+			table definition block
+			</para></listitem>
+			<listitem><para>
+			command definition block
+			</para></listitem>
+		</itemizedlist>
+	</para>
+	<section>
+	<title>Database connection definition block</title>
+	<para>
+		Each connection to a particular database must be defined here
+		with a unique database connection id.
+		The connection parameters are defined following the db_url param pattern
+		for all &kamailio; modules that are using a database.
+	</para>
+	<para>
+		Supported databases:
+		<itemizedlist>
+			<listitem><para>berkeley</para></listitem>
+			<listitem><para>cassandra</para></listitem>
+			<listitem><para>cluster</para></listitem>
+			<listitem><para>flatstore</para></listitem>
+			<listitem><para>mysql</para></listitem>
+			<listitem><para>oracle</para></listitem>
+			<listitem><para>postgres</para></listitem>
+			<listitem><para>sqlite</para></listitem>
+			<listitem><para>text</para></listitem>
+			<listitem><para>unixodbc</para></listitem>
+		</itemizedlist>
+	</para>
+	</section>
+	<section>
+	<title>Table definition block</title>
+	<para>
+		Each table managed through the  &kamailio; provisioning interface
+		must be defined here with a unique table id.
+		For each table, the database connection id must be specified.
+		Each table must list all columns that will be managed by the
+		&kamailio; provisioning interface.
+		Each column must have a unique field name and a type.
+		Each column may have a validation tag for validating input data.
+	</para>
+	<para>
+		Supported column types:
+		<itemizedlist>
+			<listitem><para>DB1_INT</para></listitem>
+			<listitem><para>DB1_BIGINT</para></listitem>
+			<listitem><para>DB1_DOUBLE</para></listitem>
+			<listitem><para>DB1_STRING</para></listitem>
+			<listitem><para>DB1_STR</para></listitem>
+			<listitem><para>DB1_DATETIME</para></listitem>
+			<listitem><para>DB1_BLOB</para></listitem>
+			<listitem><para>DB1_BITMAP</para></listitem>
+		</itemizedlist>
+	</para>
+	<para>
+		Supported validation methods:
+		<itemizedlist>
+			<listitem><para>
+			IPV4 - represents an IPv4 address
+			</para></listitem>
+			<listitem><para>
+			URI - represents a SIP URI
+			</para></listitem>
+			<listitem><para>
+			URI_IPV4HOST - represents a SIP URI with an IPV4 as a host
+			</para></listitem>
+			<listitem><para>
+			P_HOST_PORT - represents [proto:]host[:port]
+			</para></listitem>
+			<listitem><para>
+			P_IPV4_PORT - represents [proto:]IPv4[:port]
+			</para></listitem>
+		</itemizedlist>
+	</para>
+	</section>
+	<section>
+	<title>Command definition block</title>
+	<para>
+		Multiple provisioning commands can be grouped together.
+		Each group can have multiple commands.
+		Each command definition in a group must have the table id
+		of the table that is operating on along with the command
+		type to be performed.
+	</para>
+	<para>
+		The command type can have up to three type of column parameters:
+		<itemizedlist>
+			<listitem><para>clause columns</para></listitem>
+			<listitem><para>query columns</para></listitem>
+			<listitem><para>order by columns</para></listitem>
+		</itemizedlist>
+		Each column parameter must define the name(s) of the column(s)
+		(must match a field name in the description table identified
+		by the table id).
+		A column can accept a list of imposed values.
+		Each imposed value will have an id that will be displayed
+		on the web interface and the actual value that will be
+		used for db operations.
+		Clause columns must define operators.
+		Here's the list of supported operators:
+		'&lt;', '&gt;', '=', '&lt;=', '&gt;=', '!='.
+	</para>
+	<para>
+		Supported database command types:
+		<itemizedlist>
+			<listitem><para>DB1_QUERY - performs an SQL query
+			and supports three type of columns:</para>
+			<itemizedlist>
+				<listitem><para>clause: 0 or more columns</para></listitem>
+				<listitem><para>query: 1 column</para></listitem>
+				<listitem><para>order: 0 or 1 column</para></listitem>
+			</itemizedlist>
+			</listitem>
+			<listitem><para>DB1_INSERT - performs an SQL insert
+			and supports one type of column:</para>
+			<itemizedlist>
+				<listitem><para>query: 1 or more columns</para></listitem>
+			</itemizedlist>
+			</listitem>
+			<listitem><para>DB1_DELETE - performs an SQL delete
+			and supports one type of column:</para>
+			<itemizedlist>
+				<listitem><para>clause: 1 or more columns</para></listitem>
+			</itemizedlist>
+			</listitem>
+			<listitem><para>DB1_UPDATE - performs an SQL update
+			and supports two type of columns:</para>
+			<itemizedlist>
+				<listitem><para>clause: 0 or more columns</para></listitem>
+				<listitem><para>query: 1 or more columns</para></listitem>
+			</itemizedlist>
+			</listitem>
+			<listitem><para>DB1_REPLACE - performs an SQL replace
+			and supports one type of column:</para>
+			<itemizedlist>
+				<listitem><para>query: 1 or more columns</para></listitem>
+			</itemizedlist>
+			</listitem>
+		</itemizedlist>
+		Please note that some databases have a restricted
+		set of database command types.
+	</para>
+	</section>
+	</section>
+
+	<section>
+	<title>To-do</title>
+		<para>
+		Features to be added in the future:
+		</para>
+		<itemizedlist>
+			<listitem><para>
+			full subscriber provisionning with automatic ha1/ha1b fields;
+			</para></listitem>
+			<listitem><para>
+			accept empty values as NULL;
+			</para></listitem>
+		</itemizedlist>
+	</section>
+
+	<section>
+	<title>Dependencies</title>
+	<section>
+		<title>&kamailio; Modules</title>
+		<para>
+		The following modules must be loaded before this module:
+			<itemizedlist>
+			<listitem>
+			<para>
+				<emphasis>xhttp</emphasis> - xHTTP.
+			</para>
+			</listitem>
+			</itemizedlist>
+		</para>
+	</section>
+	<section>
+		<title>External Libraries or Applications</title>
+		<para>
+		The following libraries or applications must be installed before running
+		&kamailio; with this module loaded:
+			<itemizedlist>
+			<listitem>
+			<para>
+				<emphasis>libxml2</emphasis>
+			</para>
+			</listitem>
+			</itemizedlist>
+		</para>
+	</section>
+	</section>
+	<section>
+	<title>Parameters</title>
+	<section>
+		<title><varname>xhttp_pi_root</varname> (str)</title>
+		<para>
+			It specifies the root path for provisionning http requests.
+			The link to the provisioning web interface must be constructed
+			using the following patern:
+			http://[server_IP]:[tcp_port]/[xhttp_pi_root] 
+		</para>
+		<para>
+		<emphasis>
+			Default value is "rpc".
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>xhttp_pi_root</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("xhttp_rpc", "xhttp_pi_root", "pi")
+...
+</programlisting>
+		</example>
+	</section>
+	<section>
+		<title><varname>xhttp_pi_buf_size</varname> (int)</title>
+		<para>
+			It specifies the maximum length of the buffer used
+			to write in the HTML reply information in order to
+			build the HTML response.
+		</para>
+		<para>
+		<emphasis>
+			Default value is 0 (auto set to 1/3 of the size of the configured pkg mem).
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>xhttp_pi_buf_size</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("xhttp", "xhttp_pi_buf_size", 1024)
+...
+</programlisting>
+		</example>
+	</section>
+	<section>
+		<title><varname>framework</varname> (string)</title>
+		<para>
+			It specifies the full path for xml framework descriptor.
+		</para>
+		<para>
+		<emphasis>
+			There's no default value. This parameter is mandatory.
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>framework</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("xhttp", "framework", "/usr/local/etc/kamailio/pi_framework.xml")
+...
+</programlisting>
+		</example>
+	</section>
+	</section>
+
+	<section>
+	<title>Functions</title>
+ 	<section>
+		<title>
+		<function moreinfo="none">dispatch_xhttp_pi()</function>
+		</title>
+		<para>
+		Handle the HTTP request and generate a response.
+		</para>
+		<example>
+		<title><function>dispatch_xhttp_pi</function> usage</title>
+		<programlisting format="linespecific">
+...
+tcp_accept_no_cl=yes
+...
+loadmodule "sl.so"
+loadmodule "xhttp.so"
+loadmodule "xhttp_rpc.so"
+...
+modparam("xhttp_rpc", "xhttp_pi_root", "pi")
+...
+event_route[xhttp:request] {
+	$var(xhttp_rpc_root) = $(hu{s.substr,0,3});
+	if ($var(xhttp_rpc_root) == "/pi")
+		dispatch_xhttp_pi();
+	else
+		xhttp_reply("200", "OK", "text/html",
+				"&lt;html&gt;&lt;body&gt;Wrong URL $hu&lt;/body&gt;&lt;/html&gt;");
+}
+...
+</programlisting>
+		</example>
+	</section>
+	</section>
+
+	<section>
+	<title>Exported RPC Functions</title>
+	<section>
+		<title>
+		<function moreinfo="none">xhttp_pi.reload</function>
+		</title>
+		<para>Reloads the xml framework.</para>
+		<para>Name: <emphasis>xhttp_pi.reload</emphasis></para>
+		<para>Parameters: <emphasis>none</emphasis></para>
+		<para>RPC Command Format:</para>
+		<programlisting  format="linespecific">
+		kamctl xhttp_pi.reload
+		</programlisting>
+	</section>
+	</section>
+
+</chapter>
+

+ 102 - 0
modules/xhttp_pi/http_db_handler.c

@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2012 VoIP Embedded, Inc.
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio 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
+ *
+ * Kamailio 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
+ *
+ * 2012-10-18  initial version (osas)
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "../../lib/srdb1/db.h"
+#include "xhttp_pi_fnc.h"
+
+
+
+int connect_http_db(ph_framework_t *framework_data, int index)
+{
+	ph_db_url_t *ph_db_urls = framework_data->ph_db_urls;
+
+	if (ph_db_urls[index].http_db_handle) {
+		LM_CRIT("BUG - db connection found already open\n");
+		return -1;
+	}
+	if ((ph_db_urls[index].http_db_handle =
+		ph_db_urls[index].http_dbf.init(&ph_db_urls[index].db_url)) == NULL) {
+		return -1;
+	}
+	return 0;
+}
+
+int use_table(ph_db_table_t *db_table)
+{
+	ph_db_url_t *db_url;
+
+	if(db_table==NULL){
+		LM_ERR("null db_table handler\n");
+		return -1;
+	}
+	if(db_table->db_url==NULL){
+		LM_ERR("null db_url for table [%s]\n", db_table->name.s);
+		return -1;
+	}
+	db_url = db_table->db_url;
+	if(db_url->http_db_handle==NULL){
+		LM_ERR("null db handle for table [%s]\n", db_table->name.s);
+		return -1;
+	}
+	db_table->db_url->http_dbf.use_table(db_table->db_url->http_db_handle,
+						&db_table->name);
+	return 0;
+}
+
+int init_http_db(ph_framework_t *framework_data, int index)
+{
+	ph_db_url_t *ph_db_urls = framework_data->ph_db_urls;
+
+	if (db_bind_mod(&ph_db_urls[index].db_url,
+		&ph_db_urls[index].http_dbf) < 0) {
+		LM_ERR("Unable to bind to a database driver\n");
+		return -1;
+	}
+	if (connect_http_db(framework_data, index)!=0){
+		LM_ERR("unable to connect to the database\n");
+		return -1;
+	}
+
+	ph_db_urls[index].http_dbf.close(ph_db_urls[index].http_db_handle);
+	ph_db_urls[index].http_db_handle = NULL;
+
+	return 0;
+}
+
+
+void destroy_http_db(ph_framework_t *framework_data)
+{
+	int i;
+	ph_db_url_t *ph_db_urls = framework_data->ph_db_urls;
+
+	/* close the DB connections */
+	for(i=0;i<framework_data->ph_db_urls_size;i++){
+		if (ph_db_urls[i].http_db_handle) {
+			ph_db_urls[i].http_dbf.close(ph_db_urls[i].http_db_handle);
+			ph_db_urls[i].http_db_handle = NULL;
+		}
+	}
+}

+ 33 - 0
modules/xhttp_pi/http_db_handler.h

@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2012 VoIP Embedded, Inc.
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio 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
+ *
+ * Kamailio 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
+ *
+ * 2012-10-18  initial version (osas)
+ */
+
+
+#ifndef XHTTP_PI_HTTP_DB_HANDLER
+#define XHTTP_PI_HTTP_DB_HANDLER
+
+
+int init_http_db(ph_framework_t *framework_data, int index);
+int use_table(ph_db_table_t *db_table);
+int connect_http_db(ph_framework_t *framework_data, int index);
+void destroy_http_db(ph_framework_t *framework_data);
+
+#endif

+ 392 - 0
modules/xhttp_pi/xhttp_pi.c

@@ -0,0 +1,392 @@
+/*
+ * Copyright (C) 2012 VoIP Embedded, Inc.
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio 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
+ *
+ * Kamailio 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
+ *
+ * 2012-10-18  initial version (osas)
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "../../trim.h"
+#include "../../sr_module.h"
+#include "../../nonsip_hooks.h"
+#include "../../modules/xhttp/api.h"
+#include "../../rpc_lookup.h"
+#include "xhttp_pi.h"
+#include "xhttp_pi_fnc.h"
+#include "http_db_handler.h"
+
+/** @addtogroup xhttp_pi
+ * @ingroup modules
+ * @{
+ *
+ * <h1>Overview of Operation</h1>
+ * This module provides a web interface for DB provisioning web interface.
+ * It is built on top of the xhttp API module.
+ */
+
+/** @file
+ *
+ * This is the main file of xhttp_pi module which contains all the functions
+ * related to http processing, as well as the module interface.
+ */
+
+MODULE_VERSION
+
+str XHTTP_PI_REASON_OK = str_init("OK");
+str XHTTP_PI_CONTENT_TYPE_TEXT_HTML = str_init("text/html");
+
+
+extern ph_framework_t *ph_framework_data;
+
+
+static int mod_init(void);
+static int destroy(void);
+static int xhttp_pi_dispatch(sip_msg_t* msg, char* s1, char* s2);
+
+
+/** The context of the xhttp_pi request being processed.
+ *
+ * This is a global variable that records the context of the xhttp_pi request
+ * being currently processed.
+ * @sa pi_ctx
+ */
+static pi_ctx_t ctx;
+
+static xhttp_api_t xhttp_api;
+
+gen_lock_t* ph_lock;
+
+
+str xhttp_pi_root = str_init("http_pi");
+str filename = {NULL, 0};
+
+int buf_size = 0;
+char error_buf[ERROR_REASON_BUF_LEN];
+
+static cmd_export_t cmds[] = {
+	{"dispatch_xhttp_pi",(cmd_function)xhttp_pi_dispatch,0,0,0,REQUEST_ROUTE},
+	{0, 0, 0, 0, 0, 0}
+};
+
+static param_export_t params[] = {
+	{"xhttp_pi_root",	STR_PARAM,	&xhttp_pi_root.s},
+	{"xhttp_pi_buf_size",	INT_PARAM,	&buf_size},
+	{"framework",	STR_PARAM,	&filename.s},
+	{0, 0, 0}
+};
+
+static rpc_export_t rpc_methods[];
+
+/** module exports */
+struct module_exports exports= {
+	"xhttp_pi",
+	DEFAULT_DLFLAGS, /* dlopen flags */
+	cmds,
+	params,
+	0,		/* exported statistics */
+	0,		/* exported MI functions */
+	0,		/* exported pseudo-variables */
+	0,		/* extra processes */
+	mod_init,	/* module initialization function */
+	0,
+	(destroy_function) destroy,	/* destroy function */
+	NULL	/* per-child init function */
+};
+
+
+
+/** Implementation of pi_fault function required by the management API.
+ *
+ * This function will be called whenever a management function
+ * indicates that an error ocurred while it was processing the request. The
+ * function takes the reply code and reason phrase as parameters, these will
+ * be put in the body of the reply.
+ *
+ * @param ctx A pointer to the context structure of the request being
+ *            processed.
+ * @param code Reason code.
+ * @param fmt Formatting string used to build the reason phrase.
+ */
+static void pi_fault(pi_ctx_t* ctx, int code, char* fmt, ...)
+{
+	va_list ap;
+	struct xhttp_pi_reply *reply = &ctx->reply;
+
+	reply->code = code;
+	va_start(ap, fmt);
+	vsnprintf(error_buf, ERROR_REASON_BUF_LEN, fmt, ap);
+	va_end(ap);
+	reply->reason.len = strlen(error_buf);
+	reply->reason.s = error_buf;
+	/* reset body so we can print the error */
+	reply->body.len = 0;
+
+	return;
+}
+
+
+static int pi_send(pi_ctx_t* ctx)
+{
+	struct xhttp_pi_reply* reply;
+
+	if (ctx->reply_sent) return 1;
+
+	reply = &ctx->reply;
+
+	if (0!=ph_run_pi_cmd(ctx)){
+		LM_DBG("pi_fault(500,\"Internal Server Error\"\n");
+		pi_fault(ctx, 500, "Internal Server Error");
+	}
+
+	ctx->reply_sent = 1;
+	if (reply->body.len) {
+		xhttp_api.reply(ctx->msg, reply->code, &reply->reason,
+			&XHTTP_PI_CONTENT_TYPE_TEXT_HTML, &reply->body);
+	}
+	else {
+		LM_DBG("xhttp_api.reply(%p, %d, %.*s, %.*s, %.*s)\n",
+			ctx->msg, reply->code,
+			(&reply->reason)->len, (&reply->reason)->s,
+			(&XHTTP_PI_CONTENT_TYPE_TEXT_HTML)->len,
+			(&XHTTP_PI_CONTENT_TYPE_TEXT_HTML)->s,
+			(&reply->reason)->len, (&reply->reason)->s);
+		xhttp_api.reply(ctx->msg, reply->code, &reply->reason,
+			&XHTTP_PI_CONTENT_TYPE_TEXT_HTML, &reply->reason);
+	}
+
+	if (reply->buf.s) {
+		pkg_free(reply->buf.s);
+		reply->buf.s = NULL;
+		reply->buf.len = 0;
+	}
+	if (ctx->arg.s) {
+        pkg_free(ctx->arg.s);
+        ctx->arg.s = NULL;
+        ctx->arg.len = 0;
+    }
+
+	return 0;
+}
+
+/** Initialize xhttp_pi reply data structure.
+ *
+ * This function initializes the data structure that contains all data related
+ * to the xhttp_pi reply being created. The function must be called before any
+ * other function that adds data to the reply.
+ * @param ctx pi_ctx_t structure to be initialized.
+ * @return 0 on success, a negative number on error.
+ */
+static int init_xhttp_pi_reply(pi_ctx_t *ctx)
+{
+	struct xhttp_pi_reply *reply = &ctx->reply;
+
+	reply->code = 200;
+	reply->reason = XHTTP_PI_REASON_OK;
+	reply->buf.s = pkg_malloc(buf_size);
+	if (!reply->buf.s) {
+		LM_ERR("oom\n");
+		pi_fault(ctx, 500, "Internal Server Error (No memory left)");
+		return -1;
+	}
+	reply->buf.len = buf_size;
+	reply->body.s = reply->buf.s;
+	reply->body.len = 0;
+	return 0;
+}
+
+
+
+int ph_init_async_lock(void)
+{
+	ph_lock = lock_alloc();
+		if (ph_lock==NULL) {
+		LM_ERR("failed to create lock\n");
+		return -1;
+	}
+	if (lock_init(ph_lock)==NULL) {
+		LM_ERR("failed to init lock\n");
+		return -1;
+	}
+	return 0;
+}
+
+
+void ph_destroy_async_lock(void)
+{
+	if (ph_lock) {
+		lock_destroy(ph_lock);
+		lock_dealloc(ph_lock);
+	}
+}
+
+static int mod_init(void)
+{
+	int i;
+
+	if (rpc_register_array(rpc_methods)!=0) {
+		LM_ERR("failed to register RPC commands\n");
+		return -1;
+	}
+
+	/* bind the XHTTP API */
+	if (xhttp_load_api(&xhttp_api) < 0) {
+		LM_ERR("cannot bind to XHTTP API\n");
+		return -1;
+	}
+
+	/* Check xhttp_pi_buf_size param */
+	if (buf_size == 0)
+		buf_size = pkg_mem_size/3;
+
+	/* Check xhttp_pi_root param */
+	xhttp_pi_root.len = strlen(xhttp_pi_root.s);
+	for(i=0;i<xhttp_pi_root.len;i++){
+		if ( !isalnum(xhttp_pi_root.s[i]) && xhttp_pi_root.s[i]!='_') {
+			LM_ERR("bad xhttp_pi_root param [%.*s], char [%c] "
+				"- use only alphanumerical chars\n",
+				xhttp_pi_root.len, xhttp_pi_root.s,
+				xhttp_pi_root.s[i]);
+			return -1;
+		}
+	}
+
+	/* Check framework param */
+	if (filename.s==NULL) {
+		LM_ERR("missing framework\n");
+		return -1;
+	}
+	filename.len = strlen(filename.s);
+
+		/* building a cache of pi module commands */
+		if (0!=ph_init_cmds(&ph_framework_data, filename.s))
+			return -1;
+		for(i=0;i<ph_framework_data->ph_db_urls_size;i++){
+			LM_DBG("initializing db[%d] [%s]\n",
+				i, ph_framework_data->ph_db_urls[i].db_url.s);
+			if (init_http_db(ph_framework_data, i)!=0) {
+				LM_ERR("failed to initialize the DB support\n");
+				return -1;
+			}
+			if (connect_http_db(ph_framework_data, i)) {
+				LM_ERR("failed to connect to database\n");
+				return -1;
+			}
+		}
+
+	/* Build async lock */
+	if (ph_init_async_lock() != 0) return -1;
+
+	return 0;
+}
+
+
+int destroy(void)
+{
+	destroy_http_db(ph_framework_data);
+	ph_destroy_async_lock();
+	return 0;
+}
+
+
+static int xhttp_pi_dispatch(sip_msg_t* msg, char* s1, char* s2)
+{
+	str arg = {NULL, 0};
+	int ret = 0;
+	int i;
+
+	if(!IS_HTTP(msg)) {
+		LM_DBG("Got non HTTP msg\n");
+		return NONSIP_MSG_PASS;
+	}
+
+	/* Init xhttp_pi context */
+	if (ctx.reply.buf.s) LM_ERR("Unexpected buf value [%p][%d]\n",
+				ctx.reply.buf.s, ctx.reply.buf.len);
+	memset(&ctx, 0, sizeof(pi_ctx_t));
+	ctx.msg = msg;
+	ctx.mod = ctx.cmd = -1;
+	if (init_xhttp_pi_reply(&ctx) < 0) goto send_reply;
+
+	lock_get(ph_lock);
+	LM_DBG("ph_framework_data: [%p]->[%p]\n", &ph_framework_data, ph_framework_data);
+	/* Extract arguments from url */
+	if (0!=ph_parse_url(&msg->first_line.u.request.uri,
+				&ctx.mod, &ctx.cmd, &arg)){
+		pi_fault(&ctx, 500, "Bad URL");
+		goto send_reply;
+	}
+
+	if (arg.s) {
+		if (arg.len) {
+			/* Unescape args */
+			ctx.arg.s = pkg_malloc((arg.len)*sizeof(char));
+			if (ctx.arg.s==NULL){
+				LM_ERR("oom\n");
+				pi_fault(&ctx, 500, "Internal Server Error (oom)");
+				goto send_reply;
+			}
+			for(i=0;i<arg.len;i++) if (arg.s[i]=='+') arg.s[i]=' ';
+			if (0>un_escape(&arg, &ctx.arg)) {
+				LM_ERR("unable to escape [%.*s]\n", arg.len, arg.s);
+				pi_fault(&ctx, 500, "Bad arg in URL");
+				goto send_reply;
+			}
+		}
+		ctx.arg_received = 1;
+	} else {
+		LM_DBG("Got no arg\n");
+		goto send_reply;
+	}
+	
+send_reply:
+	LM_DBG("ph_framework_data: [%p]->[%p]\n", &ph_framework_data, ph_framework_data);
+	lock_release(ph_lock);
+	if (!ctx.reply_sent) {
+		ret = pi_send(&ctx);
+	}
+	if (ret < 0) return -1;
+	return 0;
+}
+
+
+/* rpc function documentation */
+static const char *rpc_reload_doc[2] = {
+	"Reload the xml framework", 0
+};
+
+/* rpc function implementations */
+static void rpc_reload(rpc_t *rpc, void *c) {
+	lock_get(ph_lock);
+	if (0!=ph_init_cmds(&ph_framework_data, filename.s)) {
+		rpc->printf(c, "Reload failed");
+	} else {
+		rpc->printf(c, "Reload OK");
+	}
+	lock_release(ph_lock);
+	return;
+}
+
+static rpc_export_t rpc_methods[] = {
+	{"xhttp_pi.reload", rpc_reload, rpc_reload_doc, 0},
+	{0, 0, 0, 0}
+};
+

+ 71 - 0
modules/xhttp_pi/xhttp_pi.h

@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2012 VoIP Embedded, Inc.
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio 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
+ *
+ * Kamailio 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
+ *
+ * 2012-10-18  initial version (osas)
+ */
+
+
+#ifndef _XHTTP_PI_H
+#define _XHTTP_PI_H
+
+#include "../../str.h"
+#include "../../parser/msg_parser.h"
+
+
+#define ERROR_REASON_BUF_LEN 1024
+#define PRINT_VALUE_BUF_LEN 256
+
+
+
+/** Representation of the xhttp_pi reply being constructed.
+ *
+ * This data structure describes the xhttp_pi reply that is being constructed
+ * and will be sent to the client.
+ */
+struct xhttp_pi_reply {
+	int code;	/**< Reply code which indicates the type of the reply */
+	str reason;	/**< Reason phrase text which provides human-readable
+			 * description that augments the reply code */
+	str body;	/**< The xhttp_pi http body built so far */
+	str buf;	/**< The memory buffer allocated for the reply, this is
+			 * where the body attribute of the structure points to */
+};
+
+
+/** The context of the xhttp_pi request being processed.
+ *
+ * This is the data structure that contains all data related to the xhttp_pi
+ * request being processed, such as the reply code and reason, data to be sent
+ * to the client in the reply, and so on.
+ *
+ * There is always one context per xhttp_pi request.
+ */
+typedef struct pi_ctx {
+	sip_msg_t* msg;			/**< The SIP/HTTP received message. */
+	struct xhttp_pi_reply reply;	/**< xhttp_pi reply to be sent to the client */
+	int reply_sent;
+	int mod;			/**< Module being processed */
+	int cmd;			/**< PI command being processed */
+	int arg_received;		/**< PI argument flag */
+	str arg;			/**< PI command argument */
+} pi_ctx_t;
+
+
+#endif
+

+ 2934 - 0
modules/xhttp_pi/xhttp_pi_fnc.c

@@ -0,0 +1,2934 @@
+/*
+ * Copyright (C) 2012 VoIP Embedded, Inc.
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio 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
+ *
+ * Kamailio 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
+ *
+ * 2012-10-18  initial version (osas)
+ */
+
+#include <libxml/parser.h>
+
+#include "../../str.h"
+#include "../../ut.h"
+#include "../../lib/srdb1/db_ut.h"
+#include "../../mem/mem.h"
+#include "../../mem/shm_mem.h"
+#include "../../config.h"
+#include "../../globals.h"
+#include "../../socket_info.h"
+#include "../../resolve.h"
+#include "../../parser/parse_uri.h"
+
+#include "xhttp_pi.h"
+#include "xhttp_pi_fnc.h"
+#include "http_db_handler.h"
+
+
+#define XHTTP_PI_XML_FRAMEWORK_NODE	"framework"
+#define XHTTP_PI_XML_DB1_URL_NODE		"db_url"
+#define XHTTP_PI_XML_DB1_TABLE_NODE	"db_table"
+#define XHTTP_PI_XML_TABLE_NAME_NODE	"table_name"
+#define XHTTP_PI_XML_DB1_URL_ID_NODE	"db_url_id"
+
+#define XHTTP_PI_XML_MOD_NODE		"mod"
+#define XHTTP_PI_XML_MOD_NAME_NODE	"mod_name"
+#define XHTTP_PI_XML_CMD_NODE		"cmd"
+#define XHTTP_PI_XML_DB1_TABLE_ID_NODE	"db_table_id"
+#define XHTTP_PI_XML_CMD_NAME_NODE	"cmd_name"
+#define XHTTP_PI_XML_CMD_TYPE_NODE	"cmd_type"
+#define XHTTP_PI_XML_CLAUSE_COLS_NODE	"clause_cols"
+#define XHTTP_PI_XML_QUERY_COLS_NODE	"query_cols"
+#define XHTTP_PI_XML_ORDER_BY_COLS_NODE	"order_by_cols"
+
+#define XHTTP_PI_XML_COLUMN_NODE		"column"
+#define XHTTP_PI_XML_COL_NODE		"col"
+
+#define XHTTP_PI_XML_FIELD_NODE		"field"
+#define XHTTP_PI_XML_TYPE_NODE		"type"
+#define XHTTP_PI_XML_OPERATOR_NODE	"operator"
+#define XHTTP_PI_XML_VALUE_NODE		"value"
+#define XHTTP_PI_XML_VALIDATE_NODE	"validate"
+
+#define XHTTP_PI_XML_ID_ATTR		"id"
+
+extern str xhttp_pi_root;
+
+ph_framework_t *ph_framework_data = NULL;
+
+#define XHTTP_PI_DB1_UNDEF	0
+#define XHTTP_PI_DB1_QUERY	1
+#define XHTTP_PI_DB1_INSERT	2
+#define XHTTP_PI_DB1_DELETE	3
+#define XHTTP_PI_DB1_UPDATE	4
+#define XHTTP_PI_DB1_REPLACE	5
+
+
+#define XHTTP_PI_COPY(p,str)	\
+do{	\
+	if ((int)((p)-buf)+(str).len>max_page_len) {	\
+		goto error;	\
+	}	\
+	memcpy((p), (str).s, (str).len); (p) += (str).len;	\
+}while(0)
+
+#define XHTTP_PI_COPY_2(p,str1,str2)	\
+do{	\
+	if ((int)((p)-buf)+(str1).len+(str2).len>max_page_len) {	\
+		goto error;	\
+	}	\
+	memcpy((p), (str1).s, (str1).len); (p) += (str1).len;	\
+	memcpy((p), (str2).s, (str2).len); (p) += (str2).len;	\
+}while(0)
+
+#define XHTTP_PI_COPY_3(p,str1,str2,str3)	\
+do{	\
+	if ((int)((p)-buf)+(str1).len+(str2).len+(str3).len>max_page_len) {	\
+		goto error;	\
+	}	\
+	memcpy((p), (str1).s, (str1).len); (p) += (str1).len;	\
+	memcpy((p), (str2).s, (str2).len); (p) += (str2).len;	\
+	memcpy((p), (str3).s, (str3).len); (p) += (str3).len;	\
+}while(0)
+
+#define XHTTP_PI_COPY_4(p,str1,str2,str3,str4)	\
+do{	\
+	if ((int)((p)-buf)+(str1).len+(str2).len+(str3).len+(str4).len>max_page_len) {	\
+		goto error;	\
+	}	\
+	memcpy((p), (str1).s, (str1).len); (p) += (str1).len;	\
+	memcpy((p), (str2).s, (str2).len); (p) += (str2).len;	\
+	memcpy((p), (str3).s, (str3).len); (p) += (str3).len;	\
+	memcpy((p), (str4).s, (str4).len); (p) += (str4).len;	\
+}while(0)
+
+#define XHTTP_PI_COPY_5(p,s1,s2,s3,s4,s5)	\
+do{	\
+	if ((int)((p)-buf)+(s1).len+(s2).len+(s3).len+(s4).len+(s5).len>max_page_len) {	\
+		goto error;	\
+	}	\
+	memcpy((p), (s1).s, (s1).len); (p) += (s1).len;	\
+	memcpy((p), (s2).s, (s2).len); (p) += (s2).len;	\
+	memcpy((p), (s3).s, (s3).len); (p) += (s3).len;	\
+	memcpy((p), (s4).s, (s4).len); (p) += (s4).len;	\
+	memcpy((p), (s5).s, (s5).len); (p) += (s5).len;	\
+}while(0)
+
+#define XHTTP_PI_COPY_6(p,s1,s2,s3,s4,s5,s6)	\
+do{	\
+	if ((int)((p)-buf)+(s1).len+(s2).len+(s3).len+(s4).len+(s5).len+(s6).len>max_page_len) {	\
+		goto error;	\
+	}	\
+	memcpy((p), (s1).s, (s1).len); (p) += (s1).len;	\
+	memcpy((p), (s2).s, (s2).len); (p) += (s2).len;	\
+	memcpy((p), (s3).s, (s3).len); (p) += (s3).len;	\
+	memcpy((p), (s4).s, (s4).len); (p) += (s4).len;	\
+	memcpy((p), (s5).s, (s5).len); (p) += (s5).len;	\
+	memcpy((p), (s6).s, (s6).len); (p) += (s6).len;	\
+}while(0)
+
+#define XHTTP_PI_COPY_8(p,s1,s2,s3,s4,s5,s6,s7,s8)	\
+do{	\
+	if ((int)((p)-buf)+(s1).len+(s2).len+(s3).len+(s4).len+(s5).len+(s6).len+(s7).len+(s8).len>max_page_len) {	\
+		goto error;	\
+	}	\
+	memcpy((p), (s1).s, (s1).len); (p) += (s1).len;	\
+	memcpy((p), (s2).s, (s2).len); (p) += (s2).len;	\
+	memcpy((p), (s3).s, (s3).len); (p) += (s3).len;	\
+	memcpy((p), (s4).s, (s4).len); (p) += (s4).len;	\
+	memcpy((p), (s5).s, (s5).len); (p) += (s5).len;	\
+	memcpy((p), (s6).s, (s6).len); (p) += (s6).len;	\
+	memcpy((p), (s7).s, (s7).len); (p) += (s7).len;	\
+	memcpy((p), (s8).s, (s8).len); (p) += (s8).len;	\
+}while(0)
+
+#define XHTTP_PI_COPY_9(p,s1,s2,s3,s4,s5,s6,s7,s8,s9)	\
+do{	\
+	if ((int)((p)-buf)+(s1).len+(s2).len+(s3).len+(s4).len+(s5).len+(s6).len+(s7).len+(s8).len+(s9).len>max_page_len) {	\
+		goto error;	\
+	}	\
+	memcpy((p), (s1).s, (s1).len); (p) += (s1).len;	\
+	memcpy((p), (s2).s, (s2).len); (p) += (s2).len;	\
+	memcpy((p), (s3).s, (s3).len); (p) += (s3).len;	\
+	memcpy((p), (s4).s, (s4).len); (p) += (s4).len;	\
+	memcpy((p), (s5).s, (s5).len); (p) += (s5).len;	\
+	memcpy((p), (s6).s, (s6).len); (p) += (s6).len;	\
+	memcpy((p), (s7).s, (s7).len); (p) += (s7).len;	\
+	memcpy((p), (s8).s, (s8).len); (p) += (s8).len;	\
+	memcpy((p), (s9).s, (s9).len); (p) += (s9).len;	\
+}while(0)
+
+#define XHTTP_PI_COPY_10(p,s1,s2,s3,s4,s5,s6,s7,s8,s9,s10)	\
+do{	\
+	if ((int)((p)-buf)+(s1).len+(s2).len+(s3).len+(s4).len+(s5).len+(s6).len+(s7).len+(s8).len+(s9).len+(s10).len>max_page_len) {	\
+		goto error;	\
+	}	\
+	memcpy((p), (s1).s, (s1).len); (p) += (s1).len;	\
+	memcpy((p), (s2).s, (s2).len); (p) += (s2).len;	\
+	memcpy((p), (s3).s, (s3).len); (p) += (s3).len;	\
+	memcpy((p), (s4).s, (s4).len); (p) += (s4).len;	\
+	memcpy((p), (s5).s, (s5).len); (p) += (s5).len;	\
+	memcpy((p), (s6).s, (s6).len); (p) += (s6).len;	\
+	memcpy((p), (s7).s, (s7).len); (p) += (s7).len;	\
+	memcpy((p), (s8).s, (s8).len); (p) += (s8).len;	\
+	memcpy((p), (s9).s, (s9).len); (p) += (s9).len;	\
+	memcpy((p), (s10).s, (s10).len); (p) += (s10).len;	\
+}while(0)
+
+#define XHTTP_PI_COPY_11(p,s1,s2,s3,s4,s5,s6,s7,s8,s9,s10,s11)	\
+do{	\
+	if ((int)((p)-buf)+(s1).len+(s2).len+(s3).len+(s4).len+(s5).len+(s6).len+(s7).len+(s8).len+(s9).len+(s10).len+(s11).len>max_page_len) {	\
+		goto error;	\
+	}	\
+	memcpy((p), (s1).s, (s1).len); (p) += (s1).len;	\
+	memcpy((p), (s2).s, (s2).len); (p) += (s2).len;	\
+	memcpy((p), (s3).s, (s3).len); (p) += (s3).len;	\
+	memcpy((p), (s4).s, (s4).len); (p) += (s4).len;	\
+	memcpy((p), (s5).s, (s5).len); (p) += (s5).len;	\
+	memcpy((p), (s6).s, (s6).len); (p) += (s6).len;	\
+	memcpy((p), (s7).s, (s7).len); (p) += (s7).len;	\
+	memcpy((p), (s8).s, (s8).len); (p) += (s8).len;	\
+	memcpy((p), (s9).s, (s9).len); (p) += (s9).len;	\
+	memcpy((p), (s10).s, (s10).len); (p) += (s10).len;	\
+	memcpy((p), (s11).s, (s11).len); (p) += (s11).len;	\
+}while(0)
+
+
+#define XHTTP_PI_COMPLETE_REPLY(ctx,fmt,args...)	\
+do{								\
+	_len = snprintf((ctx)->reply.body.s + (ctx)->reply.body.len,		\
+			(ctx)->reply.buf.len - (ctx)->reply.body.len,		\
+			fmt, ##args);				\
+	if(_len<0)						\
+		goto error;					\
+	else							\
+		(ctx)->reply.body.len += _len;				\
+	p = (ctx)->reply.body.s + (ctx)->reply.body.len;	\
+	XHTTP_PI_COPY(p,XHTTP_PI_Response_Menu_Cmd_td_4d);	\
+	(ctx)->reply.body.len = p - (ctx)->reply.body.s;	\
+	if(ph_build_reply_footer(ctx)<0)	\
+		goto error;					\
+}while(0)
+
+
+#define XHTTP_PI_BUILD_REPLY(ctx,fmt,args...)	\
+do{								\
+	if(ph_build_reply(ctx)<0)	\
+		goto error;					\
+	_len = snprintf((ctx)->reply.body.s + (ctx)->reply.body.len,		\
+			(ctx)->reply.buf.len - ctx->reply.body.len,		\
+			fmt, ##args);				\
+	if(_len<0)						\
+		goto error;					\
+	else							\
+		(ctx)->reply.body.len += _len;				\
+	p = (ctx)->reply.body.s + (ctx)->reply.body.len;	\
+	XHTTP_PI_COPY(p,XHTTP_PI_Response_Menu_Cmd_td_4d);	\
+	(ctx)->reply.body.len = p - (ctx)->reply.body.s;	\
+	if(ph_build_reply_footer(ctx)<0)	\
+		goto error;					\
+}while(0)
+
+
+static const str XHTTP_PI_Response_Head_1 = str_init("<html><head><title>"\
+	"Kamailio Provisionning Interface</title>"\
+	"<style type=\"text/css\">"\
+		"body{margin:0;}body,p,div,td,th,tr,form,ol,ul,li,input,textarea,select,"\
+		"a{font-family:\"lucida grande\",verdana,geneva,arial,helvetica,sans-serif;font-size:14px;}"\
+		"a:hover{text-decoration:none;}a{text-decoration:underline;}"\
+		".foot{padding-top:40px;font-size:10px;color:#333333;}"\
+		".foot a{font-size:10px;color:#000000;}"
+		"table.center{margin-left:auto;margin-right:auto;}\n"\
+	"</style>"\
+	"<meta http-equiv=\"Expires\" content=\"0\">"\
+	"<meta http-equiv=\"Pragma\" content=\"no-cache\">");
+
+
+static const str XHTTP_PI_Response_Head_2 = str_init(\
+"</head>"\
+"<body alink=\"#000000\" bgcolor=\"#ffffff\" link=\"#000000\" text=\"#000000\" vlink=\"#000000\">");
+
+static const str XHTTP_PI_Response_Title_Table_1 = str_init(\
+"<table cellspacing=\"0\" cellpadding=\"5\" width=\"100%%\" border=\"0\">"\
+	"<tr bgcolor=\"#BBDDFF\">"\
+	"<td colspan=2 valign=\"top\" align=\"left\" bgcolor=\"#EFF7FF\" width=\"100%%\">"\
+	"<br/><h2 align=\"center\">Kamailio Provisionning Interface</h2>");
+static const str XHTTP_PI_Response_Title_Table_3 = str_init("<br/></td></tr></table>\n<center>\n");
+
+static const str XHTTP_PI_Response_Menu_Table_1 = str_init("<table border=\"0\" cellpadding=\"3\" cellspacing=\"0\"><tbody><tr>\n");
+static const str XHTTP_PI_Response_Menu_Table_2 = str_init("<td><a href='");
+static const str XHTTP_PI_Response_Menu_Table_2b = str_init("<td><b><a href='");
+static const str XHTTP_PI_Response_Menu_Table_3 = str_init("'>");
+static const str XHTTP_PI_Response_Menu_Table_4 = str_init("</a><td>\n");
+static const str XHTTP_PI_Response_Menu_Table_4b = str_init("</a></b><td>\n");
+static const str XHTTP_PI_Response_Menu_Table_5 = str_init("</tr></tbody></table>\n");
+
+static const str XHTTP_PI_Response_Menu_Cmd_Table_1a = str_init("<table border=\"0\" cellpadding=\"3\" cellspacing=\"0\" width=\"90%\"><tbody>\n");
+static const str XHTTP_PI_Response_Menu_Cmd_Table_1b = str_init("<table border=\"1\" cellpadding=\"3\" cellspacing=\"0\" width=\"90%\"><tbody>\n");
+static const str XHTTP_PI_Response_Menu_Cmd_tr_1 = str_init("<tr>\n");
+static const str XHTTP_PI_Response_Menu_Cmd_td_1a = str_init("	<td width=\"10%\"><a href='");
+static const str XHTTP_PI_Response_Menu_Cmd_td_3a = str_init("'>");
+static const str XHTTP_PI_Response_Menu_Cmd_td_4a = str_init("</a></td>\n");
+static const str XHTTP_PI_Response_Menu_Cmd_td_1b = str_init("	<td align=\"left\"><b>");
+static const str XHTTP_PI_Response_Menu_Cmd_td_1c = str_init("	<td valign=\"top\" align=\"left\" rowspan=\"");
+static const str XHTTP_PI_Response_Menu_Cmd_td_1d = str_init("	<td>");
+static const str XHTTP_PI_Response_Menu_Cmd_td_1e = str_init("	<td colspan=\"99\">");
+static const str XHTTP_PI_Response_Menu_Cmd_td_1f = str_init("	<td rowspan=\"999999\">");
+static const str XHTTP_PI_Response_Menu_Cmd_td_3c = str_init("\">");
+static const str XHTTP_PI_Response_Menu_Cmd_td_4b = str_init("</b></td>\n");
+static const str XHTTP_PI_Response_Menu_Cmd_td_4c = str_init("	</td>\n");
+static const str XHTTP_PI_Response_Menu_Cmd_td_4d = str_init("</td>\n");
+static const str XHTTP_PI_Response_Menu_Cmd_tr_2 = str_init("</tr>\n");
+static const str XHTTP_PI_Response_Menu_Cmd_Table_2 = str_init("</tbody></table>\n");
+
+static const str XHTTP_PI_NBSP = str_init("&nbsp;");
+static const str XHTTP_PI_SLASH = str_init("/");
+static const str XHTTP_PI_SEMICOLON = str_init(" : ");
+
+static const str XHTTP_PI_NODE_INDENT = str_init("\t");
+static const str XHTTP_PI_NODE_SEPARATOR = str_init(":: ");
+static const str XHTTP_PI_ATTR_SEPARATOR = str_init(" ");
+static const str XHTTP_PI_ATTR_VAL_SEPARATOR = str_init("=");
+
+static const str XHTTP_PI_BREAK = str_init("<br/>");
+static const str XHTTP_PI_CODE_1 = str_init("<pre>");
+static const str XHTTP_PI_CODE_2 = str_init("</pre>");
+
+static const str XHTTP_PI_Post_Form_1 = str_init("\n"\
+"		<form name=\"input\" method=\"get\">\n"
+"			<input type=hidden name=cmd value=\"on\">\n");
+static const str XHTTP_PI_Post_Input = str_init(\
+"			");
+static const str XHTTP_PI_Post_Clause_Input = str_init("<br/>Clause:");
+static const str XHTTP_PI_Post_Values_Input = str_init("<br/>Values:");
+static const str XHTTP_PI_Post_Query_Input = str_init("<table border=\"0\" cellpadding=\"3\" cellspacing=\"0\"><tbody>\n");
+static const str XHTTP_PI_Post_Input_1 = str_init(\
+"			<tr><td><b>");
+static const str XHTTP_PI_Post_Input_Text = str_init(\
+"</b></td><td><input type=\"text\" name=\"");
+static const str XHTTP_PI_Post_Input_Select_1 = str_init(\
+"</b></td><td><select name=\"");
+static const str XHTTP_PI_Post_Input_Select_2 = str_init("\"/>");
+static const str XHTTP_PI_Post_Input_Option_1 = str_init(\
+"\n				<option value=\"");
+static const str XHTTP_PI_Post_Input_Option_2 = str_init("\">");
+static const str XHTTP_PI_Post_Input_Option_3 = str_init("</option>");
+static const str XHTTP_PI_Post_Input_Select_3 = str_init("</td></tr>\n");
+static const str XHTTP_PI_Post_Input_Hidden_1 = str_init(\
+"			<input type=hidden name=\"");
+static const str XHTTP_PI_Post_Input_Hidden_2 = str_init("\" value=\"");
+static const str XHTTP_PI_Post_Input_Hidden_3 = str_init("\">\n");
+static const str XHTTP_PI_Post_Input_3 = str_init("\"/></td></tr>\n");
+static const str XHTTP_PI_Post_Input_4 = str_init(\
+"			</tbody></table>\n");
+static const str XHTTP_PI_Post_Form_2 = str_init(\
+"			<br/><input type=\"submit\" value=\"Submit\"/>\n"\
+"		</form>\n");
+
+static const str XHTTP_PI_Response_Foot = str_init(\
+"\n</center>\n<div align=\"center\" class=\"foot\" style=\"margin:20px auto\">"\
+	"<span style='margin-left:5px;'></span>"\
+	"<a href=\"http://kamailio.org\">Kamailio web site</a><br/>"\
+	"Copyright &copy; 2012 <a href=\"http://www.voipembedded.com/\">VoIP Embedded</a>"\
+								". All rights reserved."\
+"</div></body></html>");
+
+#define XHTTP_PI_ROWSPAN 20
+static const str XHTTP_PI_CMD_ROWSPAN = str_init("20");
+
+
+xmlAttrPtr ph_xmlNodeGetAttrByName(xmlNodePtr node, const char *name)
+{
+	xmlAttrPtr attr = node->properties;
+	while (attr) {
+		if(xmlStrcasecmp(attr->name, (const xmlChar*)name)==0)
+			return attr;
+		attr = attr->next;
+	}
+	return NULL;
+}
+
+char *ph_xmlNodeGetAttrContentByName(xmlNodePtr node, const char *name)
+{
+	xmlAttrPtr attr = ph_xmlNodeGetAttrByName(node, name);
+	if (attr) return (char*)xmlNodeGetContent(attr->children);
+	else return NULL;
+}
+
+xmlNodePtr ph_xmlNodeGetNodeByName(xmlNodePtr node, const char *name)
+{
+	xmlNodePtr cur = node;
+	while (cur) {
+		if(xmlStrcasecmp(cur->name, (const xmlChar*)name)==0)
+			return cur;
+		cur = cur->next;
+	}
+	return NULL;
+}
+
+char *ph_xmlNodeGetNodeContentByName(xmlNodePtr node, const char *name)
+{
+	xmlNodePtr node1 = ph_xmlNodeGetNodeByName(node, name);
+	if (node1) return (char*)xmlNodeGetContent(node1);
+	else return NULL;
+}
+
+
+int ph_getDbUrlNodes(ph_framework_t *_ph_framework_data, xmlNodePtr framework_node)
+{
+	int i;
+	xmlNodePtr node;
+	ph_db_url_t *db_urls;
+	ph_db_url_t *ph_db_urls = NULL;
+	str id, db_url;
+
+	//_ph_framework_data->ph_db_urls_size=0;
+	for(node=framework_node->children;node;node=node->next){
+		if (xmlStrcasecmp(node->name,
+			(const xmlChar*)XHTTP_PI_XML_DB1_URL_NODE) == 0) {
+			if(_ph_framework_data->ph_db_urls_size)
+				db_urls = (ph_db_url_t*)
+					shm_realloc(_ph_framework_data->ph_db_urls,
+					(_ph_framework_data->ph_db_urls_size+1)*
+					sizeof(ph_db_url_t));
+			else
+				db_urls = (ph_db_url_t*)
+						shm_malloc(sizeof(ph_db_url_t));
+			if(db_urls==NULL) {LM_ERR("oom\n");return -1;}
+			_ph_framework_data->ph_db_urls = db_urls;
+
+			ph_db_urls = _ph_framework_data->ph_db_urls;
+			db_urls = &ph_db_urls[_ph_framework_data->ph_db_urls_size];
+			memset(db_urls, 0, sizeof(ph_db_url_t));
+
+			id.s = ph_xmlNodeGetAttrContentByName(node,
+						XHTTP_PI_XML_ID_ATTR);
+			if(id.s==NULL){
+				LM_ERR("No attribute for node %d [%s]\n",
+					_ph_framework_data->ph_db_urls_size,
+					node->name);
+				return -1;
+			}
+			id.len = strlen(id.s);
+			if(id.len==0){
+				LM_ERR("Empty attr for node %d [%s]\n",
+					_ph_framework_data->ph_db_urls_size,
+					node->name);
+				return -1;
+			}
+			if(shm_str_dup(&db_urls->id, &id)!=0) return -1;
+			xmlFree(id.s);id.s=NULL;id.len=0;
+
+			db_url.s = (char*)xmlNodeGetContent(node);
+			if(db_url.s==NULL){
+				LM_ERR("No content for node [%.*s][%s]\n",
+					db_urls->id.len, db_urls->id.s, node->name);
+				return -1;
+			}
+			db_url.len = strlen(db_url.s);
+			if(db_url.len==0){
+				LM_ERR("Empty content for node [%.*s][%s]\n",
+					db_urls->id.len, db_urls->id.s, node->name);
+				return -1;
+			}
+			if(shm_str_dup(&db_urls->db_url, &db_url)!=0) return -1;
+			xmlFree(db_url.s);db_url.s=NULL;db_url.len=0;
+
+			for(i=0;i<_ph_framework_data->ph_db_urls_size;i++){
+				if(db_urls->id.len==ph_db_urls[i].id.len &&
+                                        strncmp(ph_db_urls[i].id.s,
+                                                db_urls->id.s,
+						db_urls->id.len)==0){
+					LM_ERR("duplicated %s %s [%.*s]\n",
+						XHTTP_PI_XML_DB1_URL_NODE,
+						XHTTP_PI_XML_ID_ATTR,
+						db_urls->id.len,
+						db_urls->id.s);
+					return -1;
+				}
+				if(db_urls->db_url.len==ph_db_urls[i].db_url.len &&
+                                        strncmp(ph_db_urls[i].db_url.s,
+                                                db_urls->db_url.s,
+						db_urls->db_url.len)==0){
+					LM_ERR("duplicated %s [%.*s]\n",
+						XHTTP_PI_XML_DB1_URL_NODE,
+						db_urls->db_url.len,
+						db_urls->db_url.s);
+					return -1;
+				}
+			}
+			/* */
+			LM_DBG("got node [%s]->[%.*s][%.*s]\n",
+				node->name,
+				db_urls->id.len, db_urls->id.s,
+				db_urls->db_url.len, db_urls->db_url.s);
+			/* */
+			_ph_framework_data->ph_db_urls_size++;
+		}
+	}
+	if(_ph_framework_data->ph_db_urls_size==0){
+		LM_ERR("No [%s] node in config file\n", XHTTP_PI_XML_DB1_URL_NODE);
+		return -1;
+	}
+	/* */
+	for(i=0;i<_ph_framework_data->ph_db_urls_size;i++){
+		LM_DBG("got node %s [%d][%.*s][%.*s]\n", XHTTP_PI_XML_DB1_URL_NODE,
+			i, ph_db_urls[i].id.len, ph_db_urls[i].id.s,
+			ph_db_urls[i].db_url.len, ph_db_urls[i].db_url.s);
+	}
+	/* */
+	return 0;
+}
+
+int ph_getDbTableCols(ph_db_url_t *ph_db_urls, ph_db_table_t *db_tables,
+			const xmlNodePtr table_node)
+{
+	int i;
+	char *val;
+	int val_len;
+	xmlNodePtr node;
+	ph_table_col_t *cols;
+	str field;
+
+	for(node=table_node->children,db_tables->cols_size=0;node;node=node->next){
+		if (xmlStrcasecmp(node->name,
+			(const xmlChar*)XHTTP_PI_XML_COLUMN_NODE) == 0) {
+			if(db_tables->cols_size)
+				cols = (ph_table_col_t*)shm_realloc(db_tables->cols,
+						(db_tables->cols_size+1)*
+						sizeof(ph_table_col_t));
+			else
+				cols = (ph_table_col_t*)
+					shm_malloc(sizeof(ph_table_col_t));
+			if(cols==NULL) {LM_ERR("oom\n");return -1;}
+			db_tables->cols = cols;
+			cols = &db_tables->cols[db_tables->cols_size];
+			memset(cols, 0, sizeof(ph_table_col_t));
+			cols->type=-1;
+			/* Populate the field */
+			field.s =
+				ph_xmlNodeGetNodeContentByName(node->children,
+							XHTTP_PI_XML_FIELD_NODE);
+			if(field.s==NULL){
+				LM_ERR("missing node %s [%.*s] %s %s\n",
+					table_node->name,
+					db_tables->name.len, db_tables->name.s,
+					node->name, XHTTP_PI_XML_FIELD_NODE);
+				return -1;
+			}
+			field.len = strlen(field.s);
+			if(field.len==0){
+				LM_ERR("empty node %s [%.*s] %s %s \n",
+					table_node->name,
+					db_tables->name.len, db_tables->name.s,
+					node->name, XHTTP_PI_XML_FIELD_NODE);
+				return -1;
+			}
+			if(shm_str_dup(&cols->field, &field)!=0) return -1;
+			xmlFree(field.s);field.s=NULL;field.len=0;
+			/* Each field MUST be unique */
+			for(i=0;i<db_tables->cols_size;i++){
+				if(cols->field.len==db_tables->cols[i].field.len &&
+					strncmp(db_tables->cols[i].field.s,
+						cols->field.s,
+						cols->field.len)==0){
+					LM_ERR("duplicated %s %s [%.*s] in %s [%.*s]\n",
+						node->name, XHTTP_PI_XML_FIELD_NODE,
+						cols->field.len, cols->field.s,
+						table_node->name,
+						db_tables->name.len, db_tables->name.s);
+					return -1;
+				}
+			}
+			/* Populate the validation */
+			val = ph_xmlNodeGetNodeContentByName(node->children,
+						XHTTP_PI_XML_VALIDATE_NODE);
+			if(val){
+				val_len = strlen(val);
+				switch(val_len){
+				case 0:
+					break;
+				case 3:
+					if(strncmp("URI",val,3)==0)
+						cols->validation|=PH_FLAG_URI;
+					break;
+				case 11:
+					if(strncmp("P_HOST_PORT",val,11)==0)
+						cols->validation|=PH_FLAG_P_HOST_PORT;
+					else
+					if(strncmp("P_IPV4_PORT",val,11)==0)
+						cols->validation|=PH_FLAG_P_IPV4_PORT;
+					break;
+				case 12:
+					if(strncmp("URI_IPV4HOST",val,12)==0)
+						cols->validation|=PH_FLAG_URI_IPV4HOST;
+					break;
+				default:
+					LM_ERR("unexpected validation flag [%s] for "
+						"%s %s %s\n",
+						val, table_node->name, node->name,
+						XHTTP_PI_XML_VALIDATE_NODE);
+					xmlFree(val);
+					return -1;
+				}
+				xmlFree(val);
+			}
+			/* Populate the type */
+			val = ph_xmlNodeGetNodeContentByName(node->children,
+							XHTTP_PI_XML_TYPE_NODE);
+			if(val==NULL){
+				LM_ERR("missing node %s %s %s\n",
+					table_node->name, node->name,
+					XHTTP_PI_XML_TYPE_NODE);
+				return -1;
+			}
+			val_len = strlen(val);
+			if(val_len==0){
+				LM_ERR("empty node %s %s %s\n",
+					table_node->name, node->name,
+					XHTTP_PI_XML_TYPE_NODE);
+				xmlFree(val);
+				return -1;
+			}else if(val_len==7){
+				if(strncmp("DB1_INT",val,7)==0)
+					cols->type=DB1_INT;
+				else if(strncmp("DB1_STR",val,7)==0)
+					cols->type=DB1_STR;
+			}else if(val_len==8){
+				if(strncmp("DB1_BLOB",val,8)==0)
+					cols->type=DB1_BLOB;
+				else {
+					LM_ERR("unexpected type [%s] for %s %s %s\n",
+						val, table_node->name, node->name,
+						XHTTP_PI_XML_TYPE_NODE);
+					xmlFree(val);
+					return -1;
+				}
+			}else if(val_len==10){
+				if(strncmp("DB1_BIGINT",val,10)==0)
+					cols->type=DB1_BIGINT;
+				else if(strncmp("DB1_DOUBLE",val,10)==0)
+					cols->type=DB1_DOUBLE;
+				else if(strncmp("DB1_STRING",val,10)==0)
+					cols->type=DB1_STRING;
+				else if(strncmp("DB1_BITMAP",val,10)==0)
+					cols->type=DB1_BITMAP;
+			}else if(val_len==12){
+				if(strncmp("DB1_DATETIME",val,12)==0)
+					cols->type=DB1_DATETIME;
+			}
+			if(cols->type==-1){
+				LM_ERR("unexpected type [%s] for %s %s %s\n",
+					val, table_node->name, node->name,
+					XHTTP_PI_XML_TYPE_NODE);
+				xmlFree(val);
+				return -1;
+			}
+			xmlFree(val);
+			/* */
+			LM_DBG("got node %s [%d][%.*s][%d]\n",
+				XHTTP_PI_XML_COLUMN_NODE,
+				db_tables->cols_size,
+				db_tables->cols[db_tables->cols_size].field.len,
+				db_tables->cols[db_tables->cols_size].field.s,
+				db_tables->cols[db_tables->cols_size].type);
+			/* */
+			db_tables->cols_size++;
+		}
+	}
+	if(db_tables->cols_size==0){
+		LM_ERR("missing node: %s %s\n",
+			table_node->name, XHTTP_PI_XML_COLUMN_NODE);
+		return -1;
+	}
+	return 0;
+}
+
+int ph_getDbTables(ph_framework_t *_ph_framework_data, xmlNodePtr framework_node)
+{
+	int i;
+	int j;
+	char *val;
+	int val_len;
+	xmlNodePtr node;
+	ph_db_table_t *db_tables;
+	ph_db_table_t *ph_db_tables = NULL;
+	ph_db_url_t *ph_db_urls;
+	str id, name;
+
+	//_ph_framework_data->ph_db_tables_size=0;
+	for(node=framework_node->children;node;node=node->next){
+		if (xmlStrcasecmp(node->name,
+			(const xmlChar*)XHTTP_PI_XML_DB1_TABLE_NODE) == 0) {
+			if(_ph_framework_data->ph_db_tables_size)
+				db_tables = (ph_db_table_t*)
+					shm_realloc(_ph_framework_data->ph_db_tables,
+					(_ph_framework_data->ph_db_tables_size+1)*
+					sizeof(ph_db_table_t));
+			else
+				db_tables = (ph_db_table_t*)
+						shm_malloc(sizeof(ph_db_table_t));
+			if(db_tables==NULL) {LM_ERR("oom\n"); return -1;}
+			_ph_framework_data->ph_db_tables = db_tables;
+
+			ph_db_tables = _ph_framework_data->ph_db_tables;
+			db_tables = &ph_db_tables[_ph_framework_data->ph_db_tables_size];
+			memset(db_tables, 0, sizeof(ph_db_table_t));
+
+			/* Populate table ids */
+			id.s = ph_xmlNodeGetAttrContentByName(node,
+						XHTTP_PI_XML_ID_ATTR);
+			if(id.s==NULL){
+				LM_ERR("No attribute for node %d [%s]\n",
+					_ph_framework_data->ph_db_tables_size,
+					node->name);
+				return -1;
+			}
+			id.len = strlen(id.s);
+			if(id.len==0){
+				LM_ERR("Empty attr for node %d [%s]\n",
+					_ph_framework_data->ph_db_tables_size,
+					node->name);
+				return -1;
+			}
+			if(shm_str_dup(&db_tables->id, &id)!=0) return -1;
+			xmlFree(id.s);id.s=NULL;id.len=0;
+			/* Populate table name */
+			name.s =
+				ph_xmlNodeGetNodeContentByName(node->children,
+						XHTTP_PI_XML_TABLE_NAME_NODE);
+			if(name.s==NULL){
+				LM_ERR("No content for node [%.*s][%s]\n",
+					db_tables->id.len, db_tables->id.s,
+					node->name);
+				return -1;
+			}
+			name.len = strlen(name.s);
+			if(name.len==0){
+				LM_ERR("Empty content for node [%.*s][%s]\n",
+					db_tables->id.len, db_tables->id.s,
+					node->name);
+				return -1;
+			}
+			if(shm_str_dup(&db_tables->name, &name)!=0) return -1;
+			xmlFree(name.s);name.s=NULL;name.len=0;
+			/* Each table_id MUST be unique */
+			for(i=0;i<_ph_framework_data->ph_db_tables_size;i++){
+				if(db_tables->id.len==ph_db_tables[i].id.len &&
+                                        strncmp(ph_db_tables[i].id.s,
+                                                db_tables->id.s,
+						db_tables->id.len)==0){
+					LM_ERR("duplicated id %s %s [%.*s]\n",
+						XHTTP_PI_XML_DB1_TABLE_NODE,
+						XHTTP_PI_XML_ID_ATTR,
+						db_tables->id.len, db_tables->id.s);
+					return -1;
+				}
+			}
+			/* Populate the db_url_index */
+			val = ph_xmlNodeGetNodeContentByName(node->children,
+							XHTTP_PI_XML_DB1_URL_ID_NODE);
+			if(val==NULL){
+				LM_ERR("no %s for node %s [%.*s]\n",
+					XHTTP_PI_XML_DB1_URL_ID_NODE,
+					XHTTP_PI_XML_DB1_TABLE_NODE,
+					db_tables->name.len, db_tables->name.s);
+				return -1;
+			}
+			val_len = strlen(val);
+			if(val_len==0){
+				LM_ERR("empty %s for node %s [%.*s]\n",
+					XHTTP_PI_XML_DB1_URL_ID_NODE,
+					XHTTP_PI_XML_DB1_TABLE_NODE,
+					db_tables->name.len, db_tables->name.s);
+				return -1;
+			}
+			/* Get the db_url */
+			for(i=0;i<_ph_framework_data->ph_db_urls_size;i++){
+				ph_db_urls = _ph_framework_data->ph_db_urls;
+				if(val_len==ph_db_urls[i].id.len &&
+					strncmp(ph_db_urls[i].id.s, val, val_len)==0){
+					db_tables->db_url = &ph_db_urls[i];
+					break;
+				}
+			}
+			if (i==_ph_framework_data->ph_db_urls_size){
+				LM_ERR("bogus %s [%s] for node %s [%.*s]\n",
+					XHTTP_PI_XML_DB1_URL_ID_NODE, val,
+					XHTTP_PI_XML_DB1_TABLE_NODE,
+					db_tables->name.len, db_tables->name.s);
+				return -1;
+			}
+			xmlFree(val);
+
+			if(ph_getDbTableCols(_ph_framework_data->ph_db_urls,
+							db_tables, node)!=0)
+				return -1;
+
+			_ph_framework_data->ph_db_tables_size++;
+			/*
+			LM_DBG("got node [%s]->[%s][%s]\n",
+				node->name, db_tables->id.s, db_tables->db_table.s);
+			*/
+		}
+	}
+	if(_ph_framework_data->ph_db_tables_size==0){
+		LM_ERR("No [%s] node in config file\n", XHTTP_PI_XML_DB1_TABLE_NODE);
+		return -1;
+	}
+	/* */
+	for(i=0;i<_ph_framework_data->ph_db_tables_size;i++){
+		LM_DBG("got node %s [%d][%.*s]->[%.*s/%.*s]\n",
+			XHTTP_PI_XML_DB1_TABLE_NODE, i,
+			ph_db_tables[i].id.len, ph_db_tables[i].id.s,
+			ph_db_tables[i].db_url->db_url.len,
+			ph_db_tables[i].db_url->db_url.s,
+			ph_db_tables[i].name.len, ph_db_tables[i].name.s);
+		db_tables = &ph_db_tables[i];
+		for(j=0;j<db_tables->cols_size;j++){
+			LM_DBG("got node %s [%.*s][%d]\n",
+				XHTTP_PI_XML_COLUMN_NODE,
+				db_tables->cols[j].field.len,
+				db_tables->cols[j].field.s,
+				db_tables->cols[j].type);
+		}
+	}
+	/* */
+	return 0;
+}
+
+int ph_getColVals(ph_mod_t *module, ph_cmd_t *cmd,
+		ph_vals_t *cmd_col_vals, xmlNodePtr col_node)
+{
+	xmlNodePtr node;
+	str *vals, *col_vals = NULL;
+	str *ids, *col_ids = NULL;
+	int size = 0;
+	int i;
+	str attr;
+	str val;
+
+	for(node=col_node->children;node;node=node->next){
+		if (xmlStrcasecmp(node->name,
+			(const xmlChar*)XHTTP_PI_XML_VALUE_NODE) == 0) {
+			if(size){
+				vals = (str*)shm_realloc(col_vals,
+					(size+1)*sizeof(str));
+				ids = (str*)shm_realloc(col_ids,
+					(size+1)*sizeof(str));
+			}else{
+				vals = (str*)shm_malloc(sizeof(str));
+				ids = (str*)shm_malloc(sizeof(str));
+			}
+			if(vals==NULL||ids==NULL) {LM_ERR("oom\n"); return -1;}
+			col_vals = vals; col_ids = ids;
+			vals = &col_vals[size]; ids = &col_ids[size];
+			memset(vals, 0, sizeof(str*)); memset(ids, 0, sizeof(str*));
+			/* Retrieve the node attribute */
+			attr.s = ph_xmlNodeGetAttrContentByName(node,
+							XHTTP_PI_XML_ID_ATTR);
+			if(attr.s==NULL){
+				LM_ERR("No attribute for node\n");
+				return -1;
+			}
+			attr.len = strlen(attr.s);
+			if(attr.len==0){
+				LM_ERR("No attribute for node\n");
+				return -1;
+			}
+			if(shm_str_dup(ids, &attr)!=0) return -1;
+			xmlFree(attr.s); attr.s = NULL; attr.len = 0;
+			/* Retrieve the node value */
+			val.s = (char*)xmlNodeGetContent(node);
+			if(val.s==NULL){
+				LM_ERR("No content for node\n");
+				return -1;
+			}
+			val.len = strlen(val.s);
+			if(val.len==0){
+				LM_ERR("No content for node\n");
+				return -1;
+			}
+			if(shm_str_dup(vals, &val)!=0) return -1;
+			xmlFree(val.s); val.s = NULL; val.len = 0;
+			/*
+			LM_DBG(">  > [%d] [%p]->[%s] [%p]->[%s]\n",
+					size, ids, ids->s, vals, vals->s);
+			*/
+			size++;
+		}
+	}
+	if(size){
+		cmd_col_vals->ids = col_ids;
+		cmd_col_vals->vals = col_vals;
+		cmd_col_vals->vals_size = size;
+		/* */
+		for(i=0;i<size;i++)
+			LM_DBG(">>> [%d] [%p]->[%.*s] [%p]->[%.*s]\n", i,
+				cmd_col_vals->ids[i].s,
+				cmd_col_vals->ids[i].len, cmd_col_vals->ids[i].s,
+				cmd_col_vals->vals[i].s,
+				cmd_col_vals->vals[i].len, cmd_col_vals->vals[i].s);
+		/* */
+	}
+	return 0;
+}
+
+int ph_getCols(ph_mod_t *module, ph_cmd_t *cmd,
+		db_op_t **mod_cmd_ops, db_key_t **mod_cmd_keys,
+		db_type_t **mod_cmd_types, ph_vals_t **mod_cmd_vals,
+		int *key_size, xmlNodePtr cmd_node)
+{
+	xmlNodePtr node;
+	str *key;
+	str field;
+	db_key_t *keys;
+	db_key_t *cmd_keys = NULL;
+	int op_len;
+	char *operator;
+	char *_operator;
+	db_op_t *ops;
+	db_op_t *cmd_ops = NULL;
+	db_type_t *types;
+	db_type_t *cmd_types = NULL;
+	ph_vals_t *vals;
+	ph_vals_t *cmd_vals = NULL;
+	int i;
+	int size = 0;
+	int table_size;
+	ph_table_col_t *table_cols;
+
+	for(node=cmd_node->children;node;node=node->next){
+		if (xmlStrcasecmp(node->name,
+			(const xmlChar*)XHTTP_PI_XML_COL_NODE) == 0) {
+			if(size)
+				keys = (db_key_t*)shm_realloc(cmd_keys,
+					(size+1)*sizeof(db_key_t));
+			else
+				keys = (db_key_t*)shm_malloc(sizeof(db_key_t));
+			if (keys==NULL) {LM_ERR("oom\n");return -1;}
+			cmd_keys = keys;
+			keys = &cmd_keys[size];
+			memset(keys, 0, sizeof(db_key_t));
+			key = (str*)shm_malloc(sizeof(str));
+			if (key==NULL) {LM_ERR("oom\n");return -1;}
+			/* get the col field */
+			field.s = ph_xmlNodeGetNodeContentByName(node->children,
+						XHTTP_PI_XML_FIELD_NODE);
+			if(field.s==NULL){
+				LM_ERR("no %s in %s [%.*s] %s [%.*s] %s %s\n",
+					XHTTP_PI_XML_FIELD_NODE,
+					cmd_node->parent->parent->name,
+					module->module.len, module->module.s,
+					cmd_node->parent->name,
+					cmd->name.len, cmd->name.s,
+					cmd_node->name, node->name);
+				return -1;
+			}
+			field.len = strlen(field.s);
+			if(field.len==0){
+				LM_ERR("empty %s in %s [%.*s] %s [%.*s] %s %s\n",
+					XHTTP_PI_XML_FIELD_NODE,
+					cmd_node->parent->parent->name,
+					module->module.len, module->module.s,
+					cmd_node->parent->name,
+					cmd->name.len, cmd->name.s,
+					cmd_node->name, node->name);
+				return -1;
+			}
+			/* Each field must be valid */
+			table_size = cmd->db_table->cols_size;
+			table_cols = cmd->db_table->cols;
+			for(i=0;i<table_size;i++){
+				if(field.len==table_cols[i].field.len &&
+					strncmp(table_cols[i].field.s,
+						field.s,
+						field.len)==0) break;
+			}
+			if(i==table_size){
+				LM_ERR("invalid %s [%.*s] in %s [%.*s] %s [%.*s] %s %s"
+					" for [%.*s]\n",
+					XHTTP_PI_XML_FIELD_NODE,
+					field.len, field.s,
+					cmd_node->parent->parent->name,
+					module->module.len, module->module.s,
+					cmd_node->parent->name,
+					cmd->name.len, cmd->name.s,
+					cmd_node->name, node->name,
+					cmd->db_table->name.len, cmd->db_table->name.s);
+				return -1;
+			}
+			if(shm_str_dup(key, &field)) return -1;
+			*keys = key;
+			xmlFree(field.s); field.s = NULL; field.len = 0;
+			LM_DBG("cmd_keys=[%p] keys=[%p]->[%p]->[%.*s]\n",
+						cmd_keys, keys, *keys,
+						(*keys)->len, (*keys)->s);
+			/* Retrieve the type */
+			if(mod_cmd_types){
+				if(size)
+					types = (db_type_t*)shm_realloc(cmd_types,
+						(size+1)*sizeof(db_type_t));
+				else
+					types =
+					(db_type_t*)shm_malloc(sizeof(db_type_t));
+				if (types==NULL) {LM_ERR("oom\n");return -1;}
+				cmd_types = types;
+				types = &cmd_types[size];
+				memset(types, 0, sizeof(db_type_t));
+				*types = table_cols[i].type;
+			}
+			/* Retrieve the ops */
+			if(mod_cmd_ops){
+				if(size)
+					ops = (db_op_t*)shm_realloc(cmd_ops,
+						(size+1)*sizeof(db_op_t));
+				else
+					ops = (db_op_t*)shm_malloc(sizeof(db_op_t));
+				if (ops==NULL) {LM_ERR("oom\n");return -1;}
+				cmd_ops = ops;
+				ops = &cmd_ops[size];
+				memset(ops, 0, sizeof(db_op_t));
+				/* Retrieve the col op */
+				operator =
+					ph_xmlNodeGetNodeContentByName(node->children,
+						XHTTP_PI_XML_OPERATOR_NODE);
+				if(operator==NULL){
+					LM_ERR("no %s in %s [%.*s] %s [%.*s] %s %s\n",
+						XHTTP_PI_XML_OPERATOR_NODE,
+						cmd_node->parent->parent->name,
+						module->module.len, module->module.s,
+						cmd_node->parent->name,
+						cmd->name.len, cmd->name.s,
+						cmd_node->name, node->name);
+					return -1;
+				}
+				op_len = strlen(operator);
+				if(op_len==0){
+					LM_ERR("empty %s in %s [%.*s] %s [%.*s] %s %s\n",
+						XHTTP_PI_XML_OPERATOR_NODE,
+						cmd_node->parent->parent->name,
+						module->module.len, module->module.s,
+						cmd_node->parent->name,
+						cmd->name.len, cmd->name.s,
+						cmd_node->name, node->name);
+					return -1;
+				}else if(op_len==1){
+					if(strncmp(OP_LT,operator,1)==0)
+						*ops = operator;
+					else if(strncmp(OP_GT,operator,1)==0)
+						*ops = operator;
+					else if(strncmp(OP_EQ,operator,1)==0)
+						*ops = operator;
+				}else if(op_len==2){
+					if(strncmp(OP_LEQ,operator,2)==0)
+						*ops = operator;
+					else if(strncmp(OP_GEQ,operator,2)==0)
+						*ops = operator;
+					else if(strncmp(OP_NEQ,operator,2)==0)
+						*ops = operator;
+				}
+				if(*ops==NULL){
+					LM_ERR("unexpected %s [%s] in "
+						"%s [%.*s] %s [%.*s] %s %s\n",
+						XHTTP_PI_XML_OPERATOR_NODE,
+						operator,
+						cmd_node->parent->parent->name,
+						module->module.len, module->module.s,
+						cmd_node->parent->name,
+						cmd->name.len, cmd->name.s,
+						cmd_node->name, node->name);
+					return -1;
+				}
+				/* We need to copy the null string terminator */
+				op_len++;
+				_operator = shm_malloc(op_len);
+				if(_operator==NULL){LM_ERR("oom\n"); return -1;}
+				memcpy(_operator, operator, op_len);
+				*ops = _operator;
+				xmlFree(operator); operator = NULL; op_len = 0;
+				LM_DBG("%s [%p]=>[%p]=>[%.*s] %s [%.*s] %s %s [%.*s][%s]\n",
+					cmd_node->parent->parent->name, module,
+					module->module.s,
+					module->module.len, module->module.s,
+					cmd_node->parent->name,
+					cmd->name.len, cmd->name.s,
+					cmd_node->name, node->name,
+					(**keys).len, (**keys).s, *ops);
+			}else{
+				LM_DBG("%s [%p]=>[%p]=>[%.*s] %s [%.*s] %s %s [%.*s][]\n",
+					cmd_node->parent->parent->name, module,
+					module->module.s,
+					module->module.len, module->module.s,
+					cmd_node->parent->name,
+					cmd->name.len, cmd->name.s,
+					cmd_node->name, node->name,
+					(**keys).len, (**keys).s);
+			}
+			/* Retrieve the vals */
+			if(mod_cmd_vals){
+				if(size)
+					vals = (ph_vals_t*)shm_realloc(cmd_vals,
+						(size+1)*sizeof(ph_vals_t));
+				else
+					vals =
+					(ph_vals_t*)shm_malloc(sizeof(ph_vals_t));
+				if(vals==NULL) {LM_ERR("oom\n");return -1;}
+				cmd_vals = vals;
+				vals = &cmd_vals[size];
+				memset(vals, 0, sizeof(ph_vals_t));
+				if(ph_getColVals(module, cmd, vals, node)!=0)
+					return -1;
+			}
+			size++;
+		}
+	}
+	if(size==0){
+		LM_ERR("empty %s in %s [%.*s] %s [%.*s]\n",
+			cmd_node->name, cmd_node->parent->parent->name,
+			module->module.len, module->module.s,
+			cmd_node->parent->name, cmd->name.len, cmd->name.s);
+		return -1;
+	}else if(cmd_keys!=NULL){
+		*mod_cmd_keys = cmd_keys;
+		LM_DBG("***  mod_cmd_keys=[%p] *mod_cmd_keys=[%p] cmd_keys=[%p]\n",
+				mod_cmd_keys, *mod_cmd_keys, cmd_keys);
+		if(cmd_keys) for(i=0;i<size;i++)
+			LM_DBG("cmd_keys[%d]=[%p]->[%p]->[%.*s]\n",
+				i, &cmd_keys[i], cmd_keys[i]->s,
+				cmd_keys[i]->len, cmd_keys[i]->s);
+		if(mod_cmd_ops) *mod_cmd_ops = cmd_ops;
+		if(mod_cmd_types) *mod_cmd_types = cmd_types;
+		if(mod_cmd_vals&&cmd_vals) *mod_cmd_vals = cmd_vals;
+		if(cmd_vals) for(i=0;i<size;i++){
+			LM_DBG("cmd_vals[%d]=[%p]->[%d][%p][%p]\n",
+				i, &cmd_vals[i], cmd_vals[i].vals_size,
+				cmd_vals[i].ids, cmd_vals[i].vals);
+			for(op_len=0;op_len<cmd_vals[i].vals_size;op_len++)
+				LM_DBG("    [%d][%d] [%p]->[%.*s] [%p]->[%.*s]\n",
+					i, op_len,
+					&cmd_vals[i].ids[op_len],
+					cmd_vals[i].ids[op_len].len,
+					cmd_vals[i].ids[op_len].s,
+					&cmd_vals[i].vals[op_len],
+					cmd_vals[i].vals[op_len].len,
+					cmd_vals[i].vals[op_len].s);
+		}
+		*key_size = size;
+		if(cmd_ops) for(i=0;i<size;i++)
+			LM_DBG("cmd_ops[%d]=[%p]->[%s]\n",
+				i, &cmd_ops[i], cmd_ops[i]);
+		LM_DBG("\n");
+	}
+	return 0;
+}
+
+int ph_getCmds(ph_db_table_t *ph_db_tables, int ph_db_tables_size,
+		ph_mod_t *modules, xmlNodePtr mod_node)
+{
+	int i;
+	int j;
+	char *val;
+	int val_len;
+	ph_cmd_t *cmds;
+	xmlNodePtr node, cmd_cols;
+	str name;
+
+	for(node=mod_node->children,modules->cmds_size=0;node;node=node->next){
+		if (xmlStrcasecmp(node->name,
+			(const xmlChar*)XHTTP_PI_XML_CMD_NODE) == 0) {
+			if(modules->cmds_size)
+				cmds = (ph_cmd_t*)shm_realloc(modules->cmds,
+					(modules->cmds_size+1)*sizeof(ph_cmd_t));
+			else
+				cmds = (ph_cmd_t*)shm_malloc(sizeof(ph_cmd_t));;
+			if (cmds==NULL) {LM_ERR("oom\n");return -1;}
+			modules->cmds = cmds;
+			cmds = &modules->cmds[modules->cmds_size];
+			memset(cmds, 0, sizeof(ph_cmd_t));
+			cmds->type = -1;
+			/* Populate the cmd name */
+			name.s =
+				ph_xmlNodeGetNodeContentByName(node->children,
+						XHTTP_PI_XML_CMD_NAME_NODE);
+			if(name.s==NULL){
+				LM_ERR("no %s for %s [%.*s]\n",
+					XHTTP_PI_XML_CMD_NAME_NODE,
+					mod_node->name,
+					modules->module.len, modules->module.s);
+				return -1;
+			}
+			name.len = strlen(name.s);
+			if(name.len==0){
+				LM_ERR("empty %s for %s [%.*s]\n",
+					XHTTP_PI_XML_CMD_NAME_NODE,
+					mod_node->name,
+					modules->module.len, modules->module.s);
+				return -1;
+			}
+			if(shm_str_dup(&cmds->name, &name)!=0) return -1;
+			xmlFree(name.s);name.s=NULL;name.len=0;
+			/* Each cmd name MUST be unique */
+			for(i=0;i<modules->cmds_size;i++){
+				if(cmds->name.len==modules->cmds[i].name.len &&
+                                        strncmp(modules->cmds[i].name.s,
+                                                cmds->name.s,
+						cmds->name.len)==0){
+					LM_ERR("duplicated %s %s %s [%.*s]\n",
+						mod_node->name, modules->module.s,
+						node->name,
+						cmds->name.len, cmds->name.s);
+					return -1;
+				}
+			}
+			/* Populate the db_table_index */
+			val = ph_xmlNodeGetNodeContentByName(node->children,
+						XHTTP_PI_XML_DB1_TABLE_ID_NODE);
+			if(val==NULL){
+				LM_ERR("no %s for %s [%.*s] %s [%.*s]\n",
+					XHTTP_PI_XML_DB1_TABLE_ID_NODE,
+					mod_node->name,
+					modules->module.len, modules->module.s,
+					XHTTP_PI_XML_CMD_NODE,
+					cmds->name.len, cmds->name.s);
+				return -1;
+			}
+			val_len = strlen(val);
+			if(val_len==0){
+				LM_ERR("empty %s for %s [%.*s] %s [%.*s]\n",
+					XHTTP_PI_XML_DB1_URL_ID_NODE,
+					mod_node->name,
+					modules->module.len, modules->module.s,
+					XHTTP_PI_XML_CMD_NODE,
+					cmds->name.len, cmds->name.s);
+				xmlFree(val);
+				return -1;
+			}
+			/* Get db_table */
+			for(i=0;i<ph_db_tables_size;i++){
+				if(val_len==ph_db_tables[i].id.len &&
+					strncmp(ph_db_tables[i].id.s,
+						val, val_len)==0){
+					cmds->db_table = &ph_db_tables[i];
+					break;
+				}
+			}
+			if (i==ph_db_tables_size){
+				LM_ERR("bogus %s [%s] for %s [%.*s] %s [%.*s]\n",
+					XHTTP_PI_XML_DB1_TABLE_ID_NODE, val,
+					mod_node->name,
+					modules->module.len, modules->module.s,
+					XHTTP_PI_XML_CMD_NODE,
+					cmds->name.len, cmds->name.s);
+				xmlFree(val);
+				return -1;
+			}
+			xmlFree(val);
+			/* Get cmd_type */
+			val = ph_xmlNodeGetNodeContentByName(node->children,
+						XHTTP_PI_XML_CMD_TYPE_NODE);
+			if(val==NULL){
+				LM_ERR("no %s for %s [%.*s] %s [%.*s]\n",
+					XHTTP_PI_XML_CMD_TYPE_NODE,
+					mod_node->name,
+					modules->module.len, modules->module.s,
+					XHTTP_PI_XML_CMD_NODE,
+					cmds->name.len, cmds->name.s);
+				return -1;
+			}
+			val_len = strlen(val);
+			if(val_len==0){
+				LM_ERR("empty %s for %s [%.*s] %s [%.*s]\n",
+					XHTTP_PI_XML_CMD_TYPE_NODE,
+					mod_node->name,
+					modules->module.len, modules->module.s,
+					XHTTP_PI_XML_CMD_NODE,
+					cmds->name.len, cmds->name.s);
+				xmlFree(val);
+				return -1;
+			}else if(val_len==9){
+				if(strncmp("DB1_QUERY",val,9)==0){
+					cmds->type = DB_CAP_QUERY;
+					cmd_cols =
+					ph_xmlNodeGetNodeByName(node->children,
+						XHTTP_PI_XML_CLAUSE_COLS_NODE);
+					if(cmd_cols!=NULL)
+						if (ph_getCols( modules,
+								cmds,
+								&cmds->c_ops,
+								&cmds->c_keys,
+								&cmds->c_types,
+								&cmds->c_vals,
+								&cmds->c_keys_size,
+								cmd_cols)!=0)
+							return -1;
+					cmd_cols =
+					ph_xmlNodeGetNodeByName(node->children,
+						XHTTP_PI_XML_QUERY_COLS_NODE);
+					if(cmd_cols!=NULL){
+						if (ph_getCols( modules,
+								cmds,
+								NULL,
+								&cmds->q_keys,
+								&cmds->q_types,
+								NULL,
+								&cmds->q_keys_size,
+								cmd_cols)!=0)
+							return -1;
+					}else{
+						LM_ERR("no %s in %s [%.*s] %s [%.*s]\n",
+							XHTTP_PI_XML_QUERY_COLS_NODE,
+							mod_node->name,
+							modules->module.len,
+							modules->module.s,
+							node->name,
+							cmds->name.len,
+							cmds->name.s);
+						return -1;
+					}
+					cmd_cols =
+					ph_xmlNodeGetNodeByName(node->children,
+						XHTTP_PI_XML_ORDER_BY_COLS_NODE);
+					if(cmd_cols!=NULL){
+						if (ph_getCols( modules,
+								cmds,
+								NULL,
+								&cmds->o_keys,
+								NULL,
+								&cmds->q_vals,
+								&cmds->o_keys_size,
+								cmd_cols)!=0)
+							return -1;
+					}
+				}
+			}else if(val_len==10){
+				if(strncmp("DB1_INSERT",val,10)==0){
+					cmds->type = DB_CAP_INSERT;
+					cmd_cols =
+					ph_xmlNodeGetNodeByName(node->children,
+						XHTTP_PI_XML_QUERY_COLS_NODE);
+					if(cmd_cols!=NULL){
+						if (ph_getCols( modules,
+								cmds,
+								NULL,
+								&cmds->q_keys,
+								&cmds->q_types,
+								&cmds->q_vals,
+								&cmds->q_keys_size,
+								cmd_cols)!=0)
+							return -1;
+					}else{
+						LM_ERR("no %s in %s [%.*s] %s [%.*s]\n",
+							XHTTP_PI_XML_QUERY_COLS_NODE,
+							mod_node->name,
+							modules->module.len,
+							modules->module.s,
+							node->name,
+							cmds->name.len,
+							cmds->name.s);
+						return -1;
+					}
+				}else if(strncmp("DB1_DELETE",val,10)==0){
+					cmds->type = DB_CAP_DELETE;
+					cmd_cols =
+					ph_xmlNodeGetNodeByName(node->children,
+						XHTTP_PI_XML_CLAUSE_COLS_NODE);
+					if(cmd_cols!=NULL){
+						if (ph_getCols( modules,
+								cmds,
+								&cmds->c_ops,
+								&cmds->c_keys,
+								&cmds->c_types,
+								&cmds->c_vals,
+								&cmds->c_keys_size,
+								cmd_cols)!=0)
+							return -1;
+					}else{
+						LM_ERR("no %s in %s [%.*s] %s [%.*s]\n",
+							XHTTP_PI_XML_CLAUSE_COLS_NODE,
+							mod_node->name,
+							modules->module.len,
+							modules->module.s,
+							node->name,
+							cmds->name.len,
+							cmds->name.s);
+						return -1;
+					}
+
+				}else if(strncmp("DB1_UPDATE",val,10)==0){
+					cmds->type = DB_CAP_UPDATE;
+					cmd_cols =
+					ph_xmlNodeGetNodeByName(node->children,
+						XHTTP_PI_XML_CLAUSE_COLS_NODE);
+					if(cmd_cols!=NULL)
+						if (ph_getCols( modules,
+								cmds,
+								&cmds->c_ops,
+								&cmds->c_keys,
+								&cmds->c_types,
+								&cmds->c_vals,
+								&cmds->c_keys_size,
+								cmd_cols)!=0)
+							return -1;
+					cmd_cols =
+					ph_xmlNodeGetNodeByName(node->children,
+						XHTTP_PI_XML_QUERY_COLS_NODE);
+					if(cmd_cols!=NULL){
+						if (ph_getCols( modules,
+								cmds,
+								NULL,
+								&cmds->q_keys,
+								&cmds->q_types,
+								&cmds->q_vals,
+								&cmds->q_keys_size,
+								cmd_cols)!=0)
+							return -1;
+					}else{
+						LM_ERR("no %s in %s [%.*s] %s [%.*s]\n",
+							XHTTP_PI_XML_QUERY_COLS_NODE,
+							mod_node->name,
+							modules->module.len,
+							modules->module.s,
+							node->name,
+							cmds->name.len,
+							cmds->name.s);
+						return -1;
+					}
+				}
+			}else if(val_len==11){
+				if(strncmp("DB1_REPLACE",val,11)==0){
+					cmds->type = DB_CAP_REPLACE;
+					cmd_cols =
+					ph_xmlNodeGetNodeByName(node->children,
+						XHTTP_PI_XML_QUERY_COLS_NODE);
+					if(cmd_cols!=NULL){
+						if (ph_getCols( modules,
+								cmds,
+								NULL,
+								&cmds->q_keys,
+								&cmds->q_types,
+								&cmds->q_vals,
+								&cmds->q_keys_size,
+								cmd_cols)!=0)
+							return -1;
+					}else{
+						LM_ERR("no %s in %s [%.*s] %s [%.*s]\n",
+							XHTTP_PI_XML_QUERY_COLS_NODE,
+							mod_node->name,
+							modules->module.len,
+							modules->module.s,
+							node->name,
+							cmds->name.len,
+							cmds->name.s);
+						return -1;
+					}
+				}
+			}
+			if(cmds->type==-1){
+				LM_ERR("unexpected type [%s] for %s [%.*s] %s [%.*s]\n",
+					val, mod_node->name,
+					modules->module.len,
+					modules->module.s,
+					XHTTP_PI_XML_CMD_NODE,
+					cmds->name.len, cmds->name.s);
+				xmlFree(val);
+				return -1;
+			}
+			xmlFree(val);
+			/**/
+			LM_DBG("got node %s %s %s [%.*s] [%p]=>[%.*s]\n",
+					mod_node->name, node->name,
+					XHTTP_PI_XML_CMD_NAME_NODE,
+					cmds->name.len, cmds->name.s,
+					cmds->db_table->name.s,
+					cmds->db_table->name.len,
+					cmds->db_table->name.s);
+			if(cmds->c_keys)
+				for(i=0;i<cmds->c_keys_size;i++){
+					LM_DBG("    [%d] c_keys=[%.*s] "
+						"c_ops=[%s] c_types=[%d]\n",
+						i, (*(cmds->c_keys[i])).len,
+						(*(cmds->c_keys[i])).s,
+						cmds->c_ops[i], cmds->c_types[i]);
+					if(cmds->c_vals)
+						for(j=0;j<cmds->c_vals->vals_size;j++)
+							LM_DBG("      c_vals[%d] "
+							"id=[%.*s] val=[%.*s]\n",
+							j,
+							cmds->c_vals->ids->len,
+							cmds->c_vals->ids->s,
+							cmds->c_vals->vals->len,
+							cmds->c_vals->vals->s);
+				}
+			if(cmds->q_keys)
+				for(i=0;i<cmds->q_keys_size;i++){
+					LM_DBG("    [%d] q_keys=[%.*s] "
+						"q_types=[%d]\n",
+						i, (*(cmds->q_keys[i])).len,
+						(*(cmds->q_keys[i])).s,
+						cmds->q_types[i]);
+					if(cmds->q_vals)
+						for(j=0;j<cmds->q_vals->vals_size;j++)
+							LM_DBG("      c_vals[%d] "
+							"id=[%.*s] val=[%.*s]\n",
+							j,
+							cmds->q_vals->ids->len,
+							cmds->q_vals->ids->s,
+							cmds->q_vals->vals->len,
+							cmds->q_vals->vals->s);
+				}
+			if(cmds->o_keys)
+				for(i=0;i<cmds->o_keys_size;i++)
+					LM_DBG("    o_keys[%d]=[%.*s]\n",
+						i, (*(cmds->o_keys[i])).len,
+						(*(cmds->o_keys[i])).s);
+			/**/
+			modules->cmds_size++;
+		}
+	}
+	return 0;
+}
+
+int ph_getMods(ph_framework_t *_ph_framework_data, xmlNodePtr framework_node)
+{
+	int i;
+	ph_mod_t *modules;
+	xmlNodePtr mod_node;
+	ph_mod_t *ph_modules;
+	str module;
+
+	/* Build pi commands skeleton */
+	for(mod_node=framework_node->children,_ph_framework_data->ph_modules_size=0;
+					mod_node;mod_node=mod_node->next){
+		if (xmlStrcasecmp(mod_node->name,
+			(const xmlChar*)XHTTP_PI_XML_MOD_NODE) == 0) {
+			if(_ph_framework_data->ph_modules_size)
+				modules = (ph_mod_t*)shm_realloc(_ph_framework_data->ph_modules,
+					(_ph_framework_data->ph_modules_size+1)*
+					sizeof(ph_mod_t));
+			else
+				modules = (ph_mod_t*)shm_malloc(sizeof(ph_mod_t));
+			if(modules==NULL){
+				LM_ERR("oom\n");
+				return -1;
+			}
+			_ph_framework_data->ph_modules = modules;
+			ph_modules = _ph_framework_data->ph_modules;
+			modules = &ph_modules[_ph_framework_data->ph_modules_size];
+			memset(modules, 0, sizeof(ph_mod_t));
+
+			/* Populate module name */
+			module.s =
+				ph_xmlNodeGetNodeContentByName(mod_node->children,
+							XHTTP_PI_XML_MOD_NAME_NODE);
+			if(module.s==NULL){
+				LM_ERR("no %s for node %s\n",
+					XHTTP_PI_XML_MOD_NAME_NODE,
+					XHTTP_PI_XML_MOD_NODE);
+				return -1;
+			}
+			module.len = strlen(module.s);
+			if(module.len==0){
+				LM_ERR("empty %s for node %s\n",
+					XHTTP_PI_XML_MOD_NAME_NODE,
+					XHTTP_PI_XML_MOD_NODE);
+				return -1;
+			}
+			if(shm_str_dup(&modules->module, &module)!=0) return -1;
+			xmlFree(module.s);module.s=NULL;module.len=0;
+			/* Each mod name MUST be unique */
+			for(i=0;i<_ph_framework_data->ph_modules_size;i++){
+				if(modules->module.len==ph_modules[i].module.len &&
+                                        strncmp(ph_modules[i].module.s,
+                                                modules->module.s,
+						modules->module.len)==0){
+					LM_ERR("duplicated %s [%.*s]\n",
+						mod_node->name,
+						modules->module.len,
+						modules->module.s);
+					return -1;
+				}
+			}
+			/* Get cmds */
+			if(ph_getCmds(_ph_framework_data->ph_db_tables,
+					_ph_framework_data->ph_db_tables_size,
+					modules, mod_node)!=0)
+				return -1;
+			_ph_framework_data->ph_modules_size++;
+			LM_DBG("got node %s [%.*s]\n",
+				mod_node->name,
+				modules->module.len, modules->module.s);
+		}
+	}
+	if(_ph_framework_data->ph_modules_size==0){
+		LM_ERR("no %s node in config file\n", XHTTP_PI_XML_MOD_NODE);
+		return -1;
+	}
+	return 0;
+}
+
+
+void ph_freeDbUrlNodes(ph_db_url_t **ph_db_urls, int ph_db_urls_size)
+{
+	int i;
+	ph_db_url_t *_ph_db_urls = *ph_db_urls;
+
+	if(_ph_db_urls==NULL) return;
+	for(i=0;i<ph_db_urls_size;i++){
+		shm_free(_ph_db_urls[i].id.s);
+		_ph_db_urls[i].id.s = NULL;
+		shm_free(_ph_db_urls[i].db_url.s);
+		_ph_db_urls[i].db_url.s = NULL;
+	}
+	shm_free(*ph_db_urls); *ph_db_urls = NULL;
+	return;
+}
+void ph_freeDbTables(ph_db_table_t **ph_db_tables, int ph_db_tables_size)
+{
+	int i, j;
+	ph_db_table_t *_ph_db_tables = *ph_db_tables;
+
+	if(_ph_db_tables==NULL) return;
+	for(i=0;i<ph_db_tables_size;i++){
+		shm_free(_ph_db_tables[i].id.s);
+		_ph_db_tables[i].id.s = NULL;
+		shm_free(_ph_db_tables[i].name.s);
+		_ph_db_tables[i].name.s = NULL;
+		for(j=0;j<_ph_db_tables[i].cols_size;j++){
+			shm_free(_ph_db_tables[i].cols[j].field.s);
+			_ph_db_tables[i].cols[j].field.s = NULL;
+		}
+		shm_free(_ph_db_tables[i].cols);
+		_ph_db_tables[i].cols = NULL;
+	}
+	shm_free(*ph_db_tables); *ph_db_tables = NULL;
+	return;
+}
+void ph_freeMods(ph_mod_t **ph_modules, int ph_modules_size)
+{
+	int i, j, k;
+	ph_mod_t *_ph_modules = *ph_modules;
+	db_key_t *cmd_keys;
+	db_op_t *cmd_ops;
+	db_type_t *cmd_types;
+	ph_vals_t *cmd_vals;
+
+	if(_ph_modules==NULL) return;
+	for(i=0;i<ph_modules_size;i++){
+		if(_ph_modules[i].module.s){
+			shm_free(_ph_modules[i].module.s);
+			_ph_modules[i].module.s = NULL;
+		}
+		for(j=0;j<_ph_modules[i].cmds_size;j++){
+			if(_ph_modules[i].cmds[j].name.s){
+				shm_free(_ph_modules[i].cmds[j].name.s);
+				_ph_modules[i].cmds[j].name.s = NULL;
+			}
+			/* */
+			cmd_keys = _ph_modules[i].cmds[j].c_keys;
+			cmd_ops = _ph_modules[i].cmds[j].c_ops;
+			cmd_types = _ph_modules[i].cmds[j].c_types;
+			cmd_vals = _ph_modules[i].cmds[j].c_vals;
+			for(k=0;k<_ph_modules[i].cmds[j].c_keys_size;k++){
+				if(cmd_ops && cmd_ops[k]){
+					shm_free((char*)cmd_ops[k]);
+					cmd_ops[k] = NULL;
+				}
+				if(cmd_keys && cmd_keys[k]){
+					if(cmd_keys[k]->s){
+						shm_free(cmd_keys[k]->s);
+						cmd_keys[k]->s = NULL;
+					}
+					shm_free(cmd_keys[k]);
+					cmd_keys[k] = NULL;
+				}
+				if(cmd_vals){
+					if(cmd_vals[k].ids){
+						if(cmd_vals[k].ids->s){
+							shm_free(cmd_vals[k].ids->s);
+							cmd_vals[k].ids->s = NULL;
+						}
+						shm_free(cmd_vals[k].ids);
+						cmd_vals[k].ids = NULL;
+					}
+					if(cmd_vals[k].vals){
+						if(cmd_vals[k].vals->s){
+							shm_free(cmd_vals[k].vals->s);
+							cmd_vals[k].vals->s = NULL;
+						}
+						shm_free(cmd_vals[k].vals);
+						cmd_vals[k].vals = NULL;
+					}
+				}
+			}
+			if(_ph_modules[i].cmds[j].c_keys){
+				shm_free(_ph_modules[i].cmds[j].c_keys);
+				_ph_modules[i].cmds[j].c_keys = NULL;
+			}
+			if(_ph_modules[i].cmds[j].c_ops){
+				shm_free(_ph_modules[i].cmds[j].c_ops);
+				_ph_modules[i].cmds[j].c_ops = NULL;
+			}
+			if(_ph_modules[i].cmds[j].c_types){
+				shm_free(_ph_modules[i].cmds[j].c_types);
+				_ph_modules[i].cmds[j].c_types = NULL;
+			}
+			if(_ph_modules[i].cmds[j].c_vals){
+				shm_free(_ph_modules[i].cmds[j].c_vals);
+				_ph_modules[i].cmds[j].c_vals = NULL;
+			}
+			cmd_keys = NULL; cmd_ops = NULL;
+			cmd_types = NULL; cmd_vals = NULL;
+			/* */
+			cmd_keys = _ph_modules[i].cmds[j].q_keys;
+			cmd_types = _ph_modules[i].cmds[j].q_types;
+			cmd_vals = _ph_modules[i].cmds[j].q_vals;
+			for(k=0;k<_ph_modules[i].cmds[j].q_keys_size;k++){
+				if(cmd_keys && cmd_keys[k]){
+					if(cmd_keys[k]->s){
+						shm_free(cmd_keys[k]->s);
+						cmd_keys[k]->s = NULL;
+					}
+					shm_free(cmd_keys[k]);
+					cmd_keys[k] = NULL;
+				}
+				if(cmd_vals){
+					if(cmd_vals[k].ids){
+						if(cmd_vals[k].ids->s){
+							shm_free(cmd_vals[k].ids->s);
+							cmd_vals[k].ids->s = NULL;
+						}
+						shm_free(cmd_vals[k].ids);
+						cmd_vals[k].ids = NULL;
+					}
+					if(cmd_vals[k].vals){
+						if(cmd_vals[k].vals->s){
+							shm_free(cmd_vals[k].vals->s);
+							cmd_vals[k].vals->s = NULL;
+						}
+						shm_free(cmd_vals[k].vals);
+						cmd_vals[k].vals = NULL;
+					}
+				}
+			}
+			if(_ph_modules[i].cmds[j].q_keys){
+				shm_free(_ph_modules[i].cmds[j].q_keys);
+				_ph_modules[i].cmds[j].q_keys = NULL;
+			}
+			if(_ph_modules[i].cmds[j].q_types){
+				shm_free(_ph_modules[i].cmds[j].q_types);
+				_ph_modules[i].cmds[j].q_types = NULL;
+			}
+			if(_ph_modules[i].cmds[j].q_vals){
+				shm_free(_ph_modules[i].cmds[j].q_vals);
+				_ph_modules[i].cmds[j].q_vals = NULL;
+			}
+			cmd_keys = NULL; cmd_types = NULL; cmd_vals = NULL;
+			/* */
+			cmd_keys = _ph_modules[i].cmds[j].c_keys;
+			for(k=0;k<_ph_modules[i].cmds[j].c_keys_size;k++){
+				if(cmd_keys && cmd_keys[k]){
+					if(cmd_keys[k]->s){
+						shm_free(cmd_keys[k]->s);
+						cmd_keys[k]->s = NULL;
+					}
+					shm_free(cmd_keys[k]);
+					cmd_keys[k] = NULL;
+				}
+			}
+			if(_ph_modules[i].cmds[j].c_keys){
+				shm_free(_ph_modules[i].cmds[j].c_keys);
+				_ph_modules[i].cmds[j].c_keys = NULL;
+			}
+			cmd_keys = NULL;
+		}
+		if(_ph_modules[i].cmds){
+			shm_free(_ph_modules[i].cmds);
+			_ph_modules[i].cmds = NULL;
+		}
+	}
+	if(*ph_modules){
+		shm_free(*ph_modules); *ph_modules = NULL;
+	}
+	return;
+}
+
+int ph_init_cmds(ph_framework_t **framework_data, const char* filename)
+{
+	xmlDocPtr doc;
+	xmlNodePtr framework_node;
+	ph_framework_t *_framework_data = NULL;
+	ph_db_table_t *_ph_db_tables;
+	int _ph_db_tables_size;
+	ph_mod_t *_ph_modules;
+	int _ph_modules_size;
+
+	if(filename==NULL) {LM_ERR("NULL filename\n");return -1;}
+	doc = xmlParseFile(filename);
+	if(doc==NULL){
+		LM_ERR("Failed to parse xml file: %s\n", filename);
+		return -1;
+	}
+
+	/* Extract the framework node */
+	framework_node = ph_xmlNodeGetNodeByName(doc->children,
+						XHTTP_PI_XML_FRAMEWORK_NODE);
+	if (framework_node==NULL) {
+		LM_ERR("missing node %s\n", XHTTP_PI_XML_FRAMEWORK_NODE);
+		goto xml_error;
+	}
+
+	_framework_data = *framework_data;
+	if(_framework_data==NULL){
+		_framework_data =
+		(ph_framework_t*)shm_malloc(sizeof(ph_framework_t));
+		if(_framework_data==NULL) {LM_ERR("oom\n");goto xml_error;}
+		memset(_framework_data, 0, sizeof(ph_framework_t));
+
+		/* Extract the db_url nodes */
+		if(ph_getDbUrlNodes(_framework_data, framework_node)!=0)
+			goto xml_error;
+
+		/* Extract the db_url nodes */
+		if(ph_getDbTables(_framework_data, framework_node)!=0)
+			goto xml_error;
+
+		/* Build pi commands skeleton */
+		if(ph_getMods(_framework_data, framework_node)!=0)
+			goto xml_error;
+
+		if(doc)xmlFree(doc);doc=NULL;
+		*framework_data = _framework_data;
+	}else{ /* This is a reload */
+		_ph_db_tables = _framework_data->ph_db_tables;
+		_ph_db_tables_size = _framework_data->ph_db_tables_size;
+		_framework_data->ph_db_tables = NULL;
+		_framework_data->ph_db_tables_size = 0;
+		_ph_modules = _framework_data->ph_modules;
+		_ph_modules_size = _framework_data->ph_modules_size;
+		_framework_data->ph_modules = NULL;
+		_framework_data->ph_modules_size = 0;
+
+		/* Extract the db_url nodes */
+		if(ph_getDbTables(_framework_data, framework_node)!=0)
+			goto xml_reload_error;
+
+		/* Build pi commands skeleton */
+		if(ph_getMods(_framework_data, framework_node)!=0)
+			goto xml_reload_error;
+
+		if(doc)xmlFree(doc);doc=NULL;
+		*framework_data = _framework_data;
+
+	}
+	return 0;
+xml_error:
+	/* FIXME: free thw whole structure */
+	if(_framework_data){shm_free(_framework_data);}
+	if(doc)xmlFree(doc);doc=NULL;
+	return -1;
+xml_reload_error:
+	ph_freeDbTables(&_framework_data->ph_db_tables,
+			_framework_data->ph_db_tables_size);
+	ph_freeMods(&_framework_data->ph_modules,
+			_framework_data->ph_modules_size);
+	_framework_data->ph_db_tables = _ph_db_tables;
+	_framework_data->ph_db_tables_size = _ph_db_tables_size;
+	_framework_data->ph_modules = _ph_modules;
+	_framework_data->ph_modules_size = _ph_modules_size;
+	if(doc)xmlFree(doc);doc=NULL;
+	return -1;
+}
+
+
+
+int ph_parse_url(const str* url_str, int* mod, int* cmd, str* arg)
+{
+	int url_len = url_str->len;
+	char* url = url_str->s;
+	int index = 0;
+	int i;
+	int mod_len, cmd_len;
+	ph_mod_t *ph_modules = ph_framework_data->ph_modules;
+	
+
+	if (url_len<0) {
+		LM_ERR("Invalid url length [%d]\n", url_len);
+		return -1;
+	}
+	if (url_len==0) return 0;
+	if (url[0] != '/') {
+		LM_ERR("URL starting with [%c] instead of'/'\n", *url);
+		return -1;
+	}
+	index++;
+	if (url_len - index < xhttp_pi_root.len) {
+		LM_ERR("root path 2 short [%.*s]\n", url_len, url);
+		return -1;
+	}
+	if (strncmp(xhttp_pi_root.s, &url[index], xhttp_pi_root.len) != 0) {
+		LM_ERR("wrong root path [%.*s]\n", url_len, url);
+		return -1;
+	}
+	if (xhttp_pi_root.len) {
+		index += xhttp_pi_root.len;
+		if (url_len - index <= 0)
+			return 0;
+		if (url[index] != '/') {
+			LM_ERR("invalid root path [%s]\n", url);
+			return -1;
+		}
+		index++;
+	}
+
+	/* Looking for "mod" */
+	if (index>=url_len)
+		return 0;
+	for(i=index;i<url_len && url[i]!='/';i++);
+	mod_len = i - index;
+	for(i=0;i<ph_framework_data->ph_modules_size &&
+		(mod_len!=ph_modules[i].module.len ||
+		strncmp(&url[index], ph_modules[i].module.s,mod_len)!=0);i++);
+	if (i==ph_framework_data->ph_modules_size) {
+		LM_ERR("Invalid mod [%.*s] in url [%s]\n",
+			mod_len, &url[index], url);
+		return -1;
+	}
+	*mod = i;
+	LM_DBG("got mod [%d][%.*s]\n", *mod, mod_len, &url[index]);
+
+	index += mod_len;
+	LM_DBG("index=%d url_len=%d\n", index, url_len);
+	if (index>=url_len)
+		return 0;
+
+	/* skip over '/' */
+	index++;
+
+	/* Looking for "cmd" */
+	if (index>=url_len)
+		return 0;
+	for(i=index;i<url_len && url[i]!='/' && url[i]!='?';i++);
+	cmd_len = i - index;
+	for(i=0;i<ph_modules[*mod].cmds_size &&
+		(cmd_len!=ph_modules[*mod].cmds[i].name.len ||
+		strncmp(&url[index], ph_modules[*mod].cmds[i].name.s, cmd_len)!=0);
+		i++);
+	if (i==ph_modules[*mod].cmds_size) {
+		LM_ERR("Invalid cmd [%.*s] in url [%s]\n",
+			cmd_len, &url[index], url);
+		return -1;
+	}
+	*cmd = i;
+	LM_DBG("got cmd [%d][%.*s]\n", *cmd, cmd_len, &url[index]);
+	index += cmd_len;
+	if (index>=url_len)
+		return 0;
+
+	if (url[index]=='/') {
+		/* We expect to have here a '?' not a '/' */
+		return 0;
+	}
+
+	/* skip over '?' */
+	index++;
+	if (url_len - index>0) {
+		arg->s = &url[index];
+		arg->len = url_len - index;
+		LM_DBG("got extra [%.*s]\n", arg->len, arg->s);
+	}
+
+	return 0;
+}
+
+
+void ph_parse_arg(str* buf, str* name, str* val)
+{
+	int i;
+
+	val->s = NULL;
+	val->len = 0;
+
+	LM_DBG("looking for [%.*s] in [%p][%d]->[%.*s]\n",
+		name->len, name->s,
+		buf->s, buf->len, buf->len, buf->s);
+	if (buf->len < name->len) {
+		LM_DBG("cannot extract arg [%.*s]\n", name->len, name->s);
+		return;
+	}
+	if (strncmp(buf->s, name->s, name->len)!=0) {
+		LM_ERR("no match for arg [%.*s]\n", name->len, name->s);
+		return;
+	}
+	if (buf->s[name->len]!='=') {
+		LM_ERR("unexpected char [%c] while looking for [=]\n",
+				buf->s[name->len]);
+		return;
+	}
+	val->s =&(buf->s[name->len+1]);
+	if (name->len == buf->len) {
+		buf->s = NULL; buf->len = 0;
+		return;
+	}
+
+	for(i=name->len+1;i<buf->len&&buf->s[i]!='&';i++);
+	val->len = i - name->len -1;
+	if(i==buf->len) {
+		/* We reached the end of buf */
+		LM_DBG("advancing [%d] slots, enf of buffer, val=[%d][%.*s]\n",
+			i, val->len, val->len, val->s);
+		buf->s = NULL; buf->len = 0;
+	} else {
+		i++; /* skip over '&' */
+		buf->s = &(buf->s[i]); buf->len -= i;
+		if (buf->len) {
+			LM_DBG("advancing [%d] slots, remaining buffer"
+				" [%p][%d]->[%.*s], val=[%d][%.*s]\n",
+				i, buf->s, buf->len, buf->len, buf->s, val->len, val->len, val->s);
+		} else {
+			LM_DBG("advancing [%d] slots, no remaining buffer, val=[%d][%.*s]\n",
+				i, val->len, val->len, val->s);
+			buf->s = NULL;
+		}
+	}
+
+	return;
+}
+
+
+int ph_build_form_imput(char **p, char *buf, int max_page_len, int mod, int cmd)
+{
+	unsigned long i, j;
+	char c;
+	str op, arg;
+	ph_cmd_t *command;
+	ph_mod_t *ph_modules;
+
+	ph_modules = ph_framework_data->ph_modules;
+	command = &ph_modules[mod].cmds[cmd];
+	if(command->c_keys_size && (command->type==DB_CAP_QUERY ||
+				command->type==DB_CAP_DELETE ||
+				command->type==DB_CAP_UPDATE)){
+		XHTTP_PI_COPY_3(*p,XHTTP_PI_Post_Input,
+				XHTTP_PI_Post_Clause_Input,
+				XHTTP_PI_Post_Query_Input);
+		for(i=0;i<command->c_keys_size;i++){
+			/* FIXME: we should escape c_ops */
+			op.s = (char*)command->c_ops[i];
+			op.len = strlen(op.s);
+			arg.s = int2str(i, &arg.len);
+			switch(command->c_vals[i].vals_size){
+			case 0:
+				XHTTP_PI_COPY(*p, XHTTP_PI_Post_Input_1);
+				XHTTP_PI_COPY(*p, *command->c_keys[i]);
+				XHTTP_PI_COPY(*p, XHTTP_PI_ATTR_SEPARATOR);
+				XHTTP_PI_COPY(*p, op);
+				XHTTP_PI_COPY(*p, XHTTP_PI_Post_Input_Text);
+				XHTTP_PI_COPY(*p, arg);
+				XHTTP_PI_COPY(*p, XHTTP_PI_Post_Input_3);
+				break;
+			case 1:
+				XHTTP_PI_COPY(*p, XHTTP_PI_Post_Input_Hidden_1);
+				XHTTP_PI_COPY(*p, arg);
+				XHTTP_PI_COPY(*p, XHTTP_PI_Post_Input_Hidden_2);
+				XHTTP_PI_COPY(*p, command->c_vals[i].vals[0]);
+				XHTTP_PI_COPY(*p, XHTTP_PI_Post_Input_Hidden_3);
+				break;
+			default:
+				XHTTP_PI_COPY(*p, XHTTP_PI_Post_Input_1);
+				XHTTP_PI_COPY(*p, *command->c_keys[i]);
+				XHTTP_PI_COPY(*p, XHTTP_PI_ATTR_SEPARATOR);
+				XHTTP_PI_COPY(*p, op);
+				XHTTP_PI_COPY(*p, XHTTP_PI_Post_Input_Select_1);
+				XHTTP_PI_COPY(*p, arg);
+				XHTTP_PI_COPY(*p, XHTTP_PI_Post_Input_Select_2);
+				for(j=0;j<command->c_vals[i].vals_size;j++){
+					XHTTP_PI_COPY(*p, XHTTP_PI_Post_Input_Option_1);
+					XHTTP_PI_COPY(*p, command->c_vals[i].vals[j]);
+					XHTTP_PI_COPY(*p, XHTTP_PI_Post_Input_Option_2);
+					XHTTP_PI_COPY(*p, command->c_vals[i].ids[j]);
+					XHTTP_PI_COPY(*p, XHTTP_PI_Post_Input_Option_3);
+				}
+				XHTTP_PI_COPY(*p, XHTTP_PI_Post_Input_Select_3);
+			}
+		}
+		XHTTP_PI_COPY(*p, XHTTP_PI_Post_Input_4);
+	}
+	if(command->q_keys_size && (command->type==DB_CAP_INSERT ||
+				command->type==DB_CAP_UPDATE ||
+				command->type==DB_CAP_REPLACE)){
+		XHTTP_PI_COPY_3(*p,XHTTP_PI_Post_Input,
+				XHTTP_PI_Post_Values_Input,
+				XHTTP_PI_Post_Query_Input);
+		arg.s = &c; arg.len = 1;
+		for(i=0,c='a';i<command->q_keys_size;i++,c++){
+			if(c=='z'){
+				LM_ERR("To many q_keys\n"); return -1;
+			}
+			switch(command->q_vals[i].vals_size){
+			case 0:
+				XHTTP_PI_COPY(*p, XHTTP_PI_Post_Input_1);
+				XHTTP_PI_COPY(*p, *command->q_keys[i]);
+				XHTTP_PI_COPY(*p, XHTTP_PI_Post_Input_Text);
+				XHTTP_PI_COPY(*p, arg);
+				XHTTP_PI_COPY(*p, XHTTP_PI_Post_Input_3);
+				break;
+			case 1:
+				XHTTP_PI_COPY(*p, XHTTP_PI_Post_Input_Hidden_1);
+				XHTTP_PI_COPY(*p, arg);
+				XHTTP_PI_COPY(*p, XHTTP_PI_Post_Input_Hidden_2);
+				XHTTP_PI_COPY(*p, command->q_vals[i].vals[0]);
+				XHTTP_PI_COPY(*p, XHTTP_PI_Post_Input_Hidden_3);
+				break;
+			default:
+				XHTTP_PI_COPY(*p, XHTTP_PI_Post_Input_1);
+				XHTTP_PI_COPY(*p, *command->q_keys[i]);
+				XHTTP_PI_COPY(*p, XHTTP_PI_Post_Input_Select_1);
+				XHTTP_PI_COPY(*p, arg);
+				XHTTP_PI_COPY(*p, XHTTP_PI_Post_Input_Select_2);
+				for(j=0;j<command->q_vals[i].vals_size;j++){
+					XHTTP_PI_COPY(*p, XHTTP_PI_Post_Input_Option_1);
+					XHTTP_PI_COPY(*p, command->q_vals[i].vals[j]);
+					XHTTP_PI_COPY(*p, XHTTP_PI_Post_Input_Option_2);
+					XHTTP_PI_COPY(*p, command->q_vals[i].ids[j]);
+					XHTTP_PI_COPY(*p, XHTTP_PI_Post_Input_Option_3);
+				}
+				XHTTP_PI_COPY(*p, XHTTP_PI_Post_Input_Select_3);
+			}
+		}
+		XHTTP_PI_COPY(*p, XHTTP_PI_Post_Input_4);
+	}
+	return 0;
+error:
+	LM_ERR("buffer 2 small: *p=[%p] buf=[%p] max_page_len=[%d]\n",
+			*p, buf, max_page_len);
+	return -1;
+}
+
+
+int ph_build_header(pi_ctx_t *ctx)
+{
+	int i;
+	char *p;
+	char *buf = ctx->reply.buf.s;
+	int max_page_len = ctx->reply.buf.len;
+	ph_mod_t *ph_modules;
+
+	ph_modules = ph_framework_data->ph_modules;
+	p = ctx->reply.body.s + ctx->reply.body.len;
+
+	XHTTP_PI_COPY_4(p,XHTTP_PI_Response_Head_1,
+			XHTTP_PI_Response_Head_2,
+			XHTTP_PI_Response_Title_Table_1,
+			XHTTP_PI_Response_Title_Table_3);
+	/* Building module menu */
+	XHTTP_PI_COPY(p,XHTTP_PI_Response_Menu_Table_1);
+	for(i=0;i<ph_framework_data->ph_modules_size;i++) {
+		if(i!=ctx->mod) {
+			XHTTP_PI_COPY(p,XHTTP_PI_Response_Menu_Table_2);
+		} else {
+			XHTTP_PI_COPY(p,XHTTP_PI_Response_Menu_Table_2b);
+		}
+		XHTTP_PI_COPY(p,XHTTP_PI_SLASH);
+		if (xhttp_pi_root.len) {
+			XHTTP_PI_COPY_2(p,xhttp_pi_root,XHTTP_PI_SLASH);
+		}
+		XHTTP_PI_COPY_3(p,ph_modules[i].module,
+				XHTTP_PI_Response_Menu_Table_3,
+				ph_modules[i].module);
+		if(i!=ctx->mod) {
+			XHTTP_PI_COPY(p,XHTTP_PI_Response_Menu_Table_4);
+		} else {
+			XHTTP_PI_COPY(p,XHTTP_PI_Response_Menu_Table_4b);
+		}
+	}
+	XHTTP_PI_COPY(p,XHTTP_PI_Response_Menu_Table_5);
+
+	ctx->reply.body.len = p - ctx->reply.body.s;
+	return 0;
+error:
+	LM_ERR("buffer 2 small\n");
+	ctx->reply.body.len = p - ctx->reply.body.s;
+	return -1;
+}
+
+
+
+int ph_build_reply(pi_ctx_t *ctx)
+{
+	char *p;
+	char *buf = ctx->reply.buf.s;
+	int max_page_len = ctx->reply.buf.len;
+	ph_mod_t *ph_modules;
+
+	ph_modules = ph_framework_data->ph_modules;
+	p = ctx->reply.body.s + ctx->reply.body.len;
+
+	/* Print comand name */
+	XHTTP_PI_COPY_4(p,XHTTP_PI_Response_Menu_Cmd_Table_1b,
+			XHTTP_PI_Response_Menu_Cmd_tr_1,
+			XHTTP_PI_Response_Menu_Cmd_td_1a,
+			XHTTP_PI_SLASH);
+	if (xhttp_pi_root.len) {
+		XHTTP_PI_COPY_2(p,xhttp_pi_root, XHTTP_PI_SLASH);
+	}
+	XHTTP_PI_COPY_6(p,ph_modules[ctx->mod].module,
+			XHTTP_PI_SLASH,
+			ph_modules[ctx->mod].cmds[ctx->cmd].name,
+			XHTTP_PI_Response_Menu_Cmd_td_3a,
+			ph_modules[ctx->mod].cmds[ctx->cmd].name,
+			XHTTP_PI_Response_Menu_Cmd_td_4a);
+	/* Print cmd name */
+	XHTTP_PI_COPY_9(p,XHTTP_PI_Response_Menu_Cmd_td_1e,
+			ph_modules[ctx->mod].cmds[ctx->cmd].name,
+			XHTTP_PI_Response_Menu_Cmd_td_4d,
+			XHTTP_PI_Response_Menu_Cmd_tr_2,
+			XHTTP_PI_Response_Menu_Cmd_tr_1,
+			XHTTP_PI_Response_Menu_Cmd_td_1f,
+			XHTTP_PI_NBSP,
+			XHTTP_PI_Response_Menu_Cmd_td_4d,
+			XHTTP_PI_Response_Menu_Cmd_td_1d);
+
+	ctx->reply.body.len = p - ctx->reply.body.s;
+	return 0;
+error:
+	LM_ERR("buffer 2 small\n");
+	ctx->reply.body.len = p - ctx->reply.body.s;
+	return -1;
+}
+
+int ph_build_reply_footer(pi_ctx_t *ctx)
+{
+	char *p, *buf;
+	int max_page_len = ctx->reply.buf.len;
+	/* Here we print the footer */
+	buf = ctx->reply.buf.s;
+	p = ctx->reply.body.s + ctx->reply.body.len;
+	XHTTP_PI_COPY_3(p,XHTTP_PI_Response_Menu_Cmd_tr_2,
+			XHTTP_PI_Response_Menu_Cmd_Table_2,
+			XHTTP_PI_Response_Foot);
+	ctx->reply.body.len = p - ctx->reply.body.s;
+	return 0;
+error:
+	LM_ERR("buffer 2 small\n");
+	ctx->reply.body.len = p - ctx->reply.body.s;
+	return -1;
+}
+
+int ph_build_content(pi_ctx_t *ctx)
+{
+	char *p, *buf;
+	int mod = ctx->mod;
+	int cmd = ctx->cmd;
+	int max_page_len = ctx->reply.buf.len;
+	int j;
+	ph_mod_t *ph_modules;
+
+	ph_modules = ph_framework_data->ph_modules;
+	buf = ctx->reply.buf.s;
+	p = ctx->reply.body.s + ctx->reply.body.len;
+
+	if (mod>=0) { /* Building command menu */
+		/* Build the list of comands for the selected module */
+		XHTTP_PI_COPY_4(p,XHTTP_PI_Response_Menu_Cmd_Table_1a,
+				XHTTP_PI_Response_Menu_Cmd_tr_1,
+				XHTTP_PI_Response_Menu_Cmd_td_1a,
+				XHTTP_PI_SLASH);
+		if (xhttp_pi_root.len) {
+			XHTTP_PI_COPY_2(p,xhttp_pi_root,XHTTP_PI_SLASH);
+		}
+		XHTTP_PI_COPY_6(p,ph_modules[mod].module,
+				XHTTP_PI_SLASH,
+				ph_modules[mod].cmds[0].name,
+				XHTTP_PI_Response_Menu_Cmd_td_3a,
+				ph_modules[mod].cmds[0].name,
+				XHTTP_PI_Response_Menu_Cmd_td_4a);
+		if (cmd>=0) {
+			XHTTP_PI_COPY_3(p,XHTTP_PI_Response_Menu_Cmd_td_1b,
+					ph_modules[mod].cmds[cmd].name,
+					XHTTP_PI_Response_Menu_Cmd_td_4b);
+		}
+		XHTTP_PI_COPY(p,XHTTP_PI_Response_Menu_Cmd_tr_2);
+		for(j=1;j<ph_modules[mod].cmds_size;j++) {
+			XHTTP_PI_COPY_3(p,XHTTP_PI_Response_Menu_Cmd_tr_1,
+					XHTTP_PI_Response_Menu_Cmd_td_1a,
+					XHTTP_PI_SLASH);
+			if (xhttp_pi_root.len) {
+				XHTTP_PI_COPY_2(p,xhttp_pi_root, XHTTP_PI_SLASH);
+			}
+			XHTTP_PI_COPY_6(p,ph_modules[mod].module,
+					XHTTP_PI_SLASH,
+					ph_modules[mod].cmds[j].name,
+					XHTTP_PI_Response_Menu_Cmd_td_3a,
+					ph_modules[mod].cmds[j].name,
+					XHTTP_PI_Response_Menu_Cmd_td_4a);
+			if (cmd>=0){
+				if (j==1) {
+					XHTTP_PI_COPY_4(p,
+						XHTTP_PI_Response_Menu_Cmd_td_1c,
+						XHTTP_PI_CMD_ROWSPAN,
+						XHTTP_PI_Response_Menu_Cmd_td_3c,
+						XHTTP_PI_Post_Form_1);
+					if(ph_build_form_imput(&p, buf, max_page_len,
+							mod, cmd)!=0)
+						return -1;
+					XHTTP_PI_COPY_2(p, XHTTP_PI_Post_Form_2,
+						XHTTP_PI_Response_Menu_Cmd_td_4c);
+				} else if (j>XHTTP_PI_ROWSPAN) {
+					XHTTP_PI_COPY_3(p,
+						XHTTP_PI_Response_Menu_Cmd_td_1d,
+						XHTTP_PI_NBSP,
+						XHTTP_PI_Response_Menu_Cmd_td_4d);
+				}
+			}
+			XHTTP_PI_COPY(p,XHTTP_PI_Response_Menu_Cmd_tr_2);
+		}
+		if (cmd>=0){
+			if (j==1) {
+				XHTTP_PI_COPY_8(p,XHTTP_PI_Response_Menu_Cmd_tr_1,
+						XHTTP_PI_Response_Menu_Cmd_td_1d,
+						XHTTP_PI_NBSP,
+						XHTTP_PI_Response_Menu_Cmd_td_4d,
+						XHTTP_PI_Response_Menu_Cmd_td_1c,
+						XHTTP_PI_CMD_ROWSPAN,
+						XHTTP_PI_Response_Menu_Cmd_td_3c,
+						XHTTP_PI_Post_Form_1);
+				if(ph_build_form_imput(&p, buf, max_page_len,
+						mod, cmd)!=0)
+					return -1;
+				XHTTP_PI_COPY_3(p, XHTTP_PI_Post_Form_2,
+						XHTTP_PI_Response_Menu_Cmd_td_4c,
+						XHTTP_PI_Response_Menu_Cmd_tr_2);
+				j++;
+			}
+			for(;j<=XHTTP_PI_ROWSPAN;j++) {
+				XHTTP_PI_COPY_5(p,XHTTP_PI_Response_Menu_Cmd_tr_1,
+						XHTTP_PI_Response_Menu_Cmd_td_1d,
+						XHTTP_PI_NBSP,
+						XHTTP_PI_Response_Menu_Cmd_td_4d,
+						XHTTP_PI_Response_Menu_Cmd_tr_2);
+			}
+		}
+		XHTTP_PI_COPY_2(p,XHTTP_PI_Response_Menu_Cmd_Table_2,
+				XHTTP_PI_Response_Foot);
+	} else {
+		XHTTP_PI_COPY(p,XHTTP_PI_Response_Foot);
+	}
+
+	ctx->reply.body.len = p - ctx->reply.body.s;
+	return 0;
+error:
+	LM_ERR("buffer 2 small\n");
+	ctx->reply.body.len = p - ctx->reply.body.s;
+	return -1;
+}
+
+
+int getVal(db_val_t *val, db_type_t val_type, db_key_t key, ph_db_table_t *table,
+	str *arg, pi_ctx_t *ctx)
+{
+	char *p = ctx->reply.body.s + ctx->reply.body.len;
+	char *buf = ctx->reply.buf.s;
+	int _len, i;
+	int max_page_len = ctx->reply.buf.len;
+	ph_val_flags flags;
+
+	char c;
+	str host;
+	int port, proto;
+	struct sip_uri uri;
+
+	for(i=0;i<=table->cols_size;i++){
+		if(table->cols[i].type==val_type &&
+			table->cols[i].field.len==key->len &&
+			strncmp(table->cols[i].field.s,key->s,key->len)==0){
+			if(table->cols[i].validation==0) continue;
+			flags = table->cols[i].validation;
+			LM_DBG("[%.*s] has flags [%u]\n", key->len, key->s, flags);
+			if(flags&PH_FLAG_P_HOST_PORT){
+				flags&= ~ PH_FLAG_P_HOST_PORT;
+				c = arg->s[arg->len];
+				arg->s[arg->len] = '\0';
+				if (parse_phostport(arg->s,
+						&host.s, &host.len,
+						&port, &proto)!=0){
+					arg->s[arg->len] = c;
+					XHTTP_PI_BUILD_REPLY(ctx,
+						"Invalid [proto:]host[:port] for"
+						"%.*s [%.*s].",
+						key->len, key->s, arg->len,  arg->s);
+					goto done;
+				}
+				arg->s[arg->len] = c;
+				LM_DBG("[%.*s]->[%d][%.*s][%d]\n",
+					arg->len, arg->s, proto,
+					host.len, host.s, port);
+				continue;
+			}
+			LM_DBG("[%.*s] has flags [%d]\n", key->len, key->s, flags);
+			if(flags&PH_FLAG_P_IPV4_PORT){
+				flags&= ~ PH_FLAG_P_IPV4_PORT;
+				c = arg->s[arg->len];
+				arg->s[arg->len] = '\0';
+				if (parse_phostport(arg->s,
+						&host.s, &host.len,
+						&port, &proto)!=0){
+					arg->s[arg->len] = c;
+					XHTTP_PI_BUILD_REPLY(ctx,
+						"Invalid [proto:]IPv4[:port] for"
+						" %.*s [%.*s].",
+						key->len, key->s, arg->len,  arg->s);
+					goto done;
+				}
+				arg->s[arg->len] = c;
+				LM_DBG("[%.*s]->[%d][%.*s][%d]\n",
+					arg->len, arg->s, proto,
+					host.len, host.s, port);
+				if (str2ip(&host)==NULL) {
+					XHTTP_PI_BUILD_REPLY(ctx,
+						"Invalid IPv4 in [proto:]IPv4[:port]"
+						" %.*s [%.*s][%.*s].",
+						key->len, key->s,
+						host.len, host.s,
+						arg->len, arg->s);
+					goto done;
+				}
+				continue;
+			}
+			LM_DBG("[%.*s] has flags [%d]\n", key->len, key->s, flags);
+			if(flags&PH_FLAG_IPV4){
+				flags&= ~ PH_FLAG_IPV4;
+				if (str2ip(arg)==NULL) {
+					XHTTP_PI_BUILD_REPLY(ctx,
+						"Invalid IPv4 for %.*s [%.*s].",
+						key->len, key->s, arg->len, arg->s);
+					goto done;
+				}
+				continue;
+			}
+			LM_DBG("[%.*s] has flags [%d]\n", key->len, key->s, flags);
+			if(flags&PH_FLAG_URI){
+				flags&= ~ PH_FLAG_URI;
+				if (parse_uri(arg->s, arg->len, &uri)<0){
+					XHTTP_PI_BUILD_REPLY(ctx,
+						"Invalid SIP URI for %.*s [%.*s].",
+						key->len, key->s, arg->len, arg->s);
+					goto done;
+				}
+				continue;
+			}
+			LM_DBG("[%.*s] has flags [%d]\n", key->len, key->s, flags);
+			if(flags&PH_FLAG_URI_IPV4HOST){
+				flags&= ~ PH_FLAG_URI_IPV4HOST;
+				if (parse_uri(arg->s, arg->len, &uri)<0){
+					XHTTP_PI_BUILD_REPLY(ctx,
+						"Invalid SIP URI for %.*s [%.*s].",
+						key->len, key->s, arg->len, arg->s);
+					goto done;
+				}
+				if (str2ip(&uri.host)==NULL) {
+					XHTTP_PI_BUILD_REPLY(ctx,
+						"Invalid IPv4 host in SIP URI for"
+						" %.*s [%.*s][%.*s].",
+						key->len, key->s,
+						uri.host.len, uri.host.s,
+						arg->len, arg->s);
+					goto done;
+				}
+				continue;
+			}
+			LM_DBG("[%.*s] has flags [%d]\n", key->len, key->s, flags);
+			if(flags){
+				XHTTP_PI_BUILD_REPLY(ctx,
+					"Unkown validation [%d] for %s.",
+					table->cols[i].validation, key->s);
+				goto done;
+			}
+		}
+	}
+	switch(val_type){
+	case DB1_STR:
+	case DB1_STRING:
+	case DB1_BLOB:
+		if(arg->len){
+			val->val.str_val.s = arg->s;
+			val->val.str_val.len = arg->len;
+		}
+		break;
+	case DB1_INT:
+	case DB1_BITMAP:
+		c = arg->s[arg->len];
+		arg->s[arg->len] = '\0';
+		if(db_str2int(arg->s,&val->val.int_val)<0){
+			arg->s[arg->len] = c;
+			XHTTP_PI_BUILD_REPLY(ctx,
+				"Bogus field %.*s [%.*s].",
+				key->len, key->s, arg->len, arg->s);
+			goto done;
+		}
+		arg->s[arg->len] = c;
+		break;
+	case DB1_BIGINT:
+		c = arg->s[arg->len];
+		arg->s[arg->len] = '\0';
+		if(db_str2longlong(arg->s,&val->val.ll_val)<0){
+			arg->s[arg->len] = c;
+			XHTTP_PI_BUILD_REPLY(ctx,
+				"Bogus field %.*s [%.*s].",
+				key->len, key->s, arg->len, arg->s);
+			goto done;
+		}
+		arg->s[arg->len] = c;
+		break;
+	case DB1_DOUBLE:
+		c = arg->s[arg->len];
+		arg->s[arg->len] = '\0';
+		if(db_str2double(arg->s,&val->val.double_val)<0){
+			arg->s[arg->len] = c;
+			XHTTP_PI_BUILD_REPLY(ctx,
+				"Bogus field %.*s [%.*s].",
+				key->len, key->s, arg->len, arg->s);
+			goto done;
+		}
+		arg->s[arg->len] = c;
+		break;
+	case DB1_DATETIME:
+		c = arg->s[arg->len];
+		arg->s[arg->len] = '\0';
+		if(db_str2time(arg->s,&val->val.time_val)<0){
+			arg->s[arg->len] = c;
+			XHTTP_PI_BUILD_REPLY(ctx,
+				"Bogus field %.*s [%.*s].",
+				key->len, key->s, arg->len, arg->s);
+			goto done;
+		}
+		arg->s[arg->len] = c;
+		break;
+	default:
+		XHTTP_PI_BUILD_REPLY(ctx,
+			"Unexpected type [%d] for field [%.*s]\n",
+			val_type, key->len, key->s);
+		goto done;
+	}
+	ctx->reply.body.len = p - ctx->reply.body.s;
+	return 0;
+done:
+	ctx->reply.body.len = p - ctx->reply.body.s;
+	return 1;
+error:
+	LM_ERR("buffer 2 small\n");
+	ctx->reply.body.len = p - ctx->reply.body.s;
+	return -1;
+}
+
+
+
+int ph_run_pi_cmd(pi_ctx_t* ctx)
+{
+	char *p;
+	char *_p;
+	char *buf = ctx->reply.buf.s;
+	int ret;
+	int mod = ctx->mod;
+	int cmd = ctx->cmd;
+
+	str arg_url = {ctx->arg.s, ctx->arg.len};
+	str arg_name;
+	str arg_val;
+	//unsigned long i;
+	int i;
+	int j;
+	int max_page_len = ctx->reply.buf.len;
+	ph_cmd_t *command;
+
+	int _len;
+
+	char c;
+	db_val_t *c_vals = NULL;
+	db_val_t *q_vals = NULL;
+	db_val_t *val;
+	str val_str;
+	int nr_rows;
+	ph_db_url_t *db_url;
+	db1_res_t *res = NULL;
+	db_val_t *values;
+	db_row_t *rows;
+	ph_mod_t *ph_modules;
+
+	ph_modules = ph_framework_data->ph_modules;
+
+
+	if (0!=ph_build_header(ctx)) return -1;
+	p = ctx->reply.body.s + ctx->reply.body.len;
+
+	if (cmd<0) return ph_build_content(ctx);
+
+	arg_name.s = "cmd"; arg_name .len=3;
+	ph_parse_arg(&arg_url, &arg_name, &arg_val);
+	if (arg_val.s==NULL) return ph_build_content(ctx);
+
+	command = &ph_modules[mod].cmds[cmd];
+
+	/* allocate c_vals array */
+	if(command->c_keys_size && command->c_keys_size){
+		c_vals = (db_val_t*)pkg_malloc(command->c_keys_size*sizeof(db_val_t));
+		if(c_vals==NULL){
+			LM_ERR("oom\n");
+			return -1;
+		}
+		memset(c_vals, 0, command->c_keys_size*sizeof(db_val_t));
+		for(i=0;i<command->c_keys_size;i++){
+			arg_name.s = int2str(i, &arg_name.len);
+			ph_parse_arg(&arg_url, &arg_name, &arg_val);
+			if(arg_val.s==NULL){
+				XHTTP_PI_BUILD_REPLY(ctx,
+					"No argument for clause field #%d: %.*s.",
+					i, command->c_keys[i]->len,
+					command->c_keys[i]->s);
+				goto done;
+			}
+			if(arg_val.len==0){
+				XHTTP_PI_BUILD_REPLY(ctx,
+					"Empty argument for clause field #%d: %.*s.",
+					i, command->c_keys[i]->len,
+					command->c_keys[i]->s);
+				goto done;
+			}
+			//LM_DBG("arg_val=[%s] arg=[%s]\n", arg_val.s, arg);
+			val = &c_vals[i];
+			val->type = command->c_types[i];
+
+			ret = getVal(val, command->c_types[i], command->c_keys[i],
+				command->db_table, &arg_val, ctx);
+			if(ret<0)
+				goto error;
+			else if(ret>0)
+				goto done;
+		}
+	}
+	if(command->q_keys_size && command->q_keys_size && command->type!=DB_CAP_QUERY){
+		q_vals = (db_val_t*)pkg_malloc(command->q_keys_size*sizeof(db_val_t));
+		if(q_vals==NULL){
+			LM_ERR("oom\n");
+			return -1;
+		}
+		memset(q_vals, 0, command->q_keys_size*sizeof(db_val_t));
+		for(i=0,c='a';i<command->q_keys_size;i++,c++){
+			if(c=='z'){
+				XHTTP_PI_BUILD_REPLY(ctx,
+					"Too many query values.");
+				goto done;
+			}
+			LM_DBG("looking for arg [%c]\n", c);
+			arg_name.s = &c; arg_name.len = 1;
+			ph_parse_arg(&arg_url, &arg_name, &arg_val);
+			if(arg_val.s==NULL){
+				XHTTP_PI_BUILD_REPLY(ctx,
+					"No argument for query field #%d: %.*s.",
+					i, command->q_keys[i]->len,
+					command->q_keys[i]->s);
+				goto done;
+			}
+			if(arg_val.len==0 && (command->q_types[i]!=DB1_STR &&
+					command->q_types[i]!=DB1_STRING &&
+					command->q_types[i]!=DB1_BLOB)){
+				XHTTP_PI_BUILD_REPLY(ctx,
+					"Empty argument for query field #%d: %.*s.",
+					i, command->q_keys[i]->len,
+					command->q_keys[i]->s);
+				goto done;
+			}
+			val = &q_vals[i];
+			val->type = command->q_types[i];
+			ret = getVal(val, command->q_types[i], command->q_keys[i],
+				command->db_table, &arg_val, ctx);
+			if(ret<0)
+				goto error;
+			else if(ret>0)
+				goto done;
+
+		}
+	}
+
+	db_url = command->db_table->db_url;
+	if(use_table(command->db_table)<0){
+		XHTTP_PI_BUILD_REPLY(ctx,
+			"Error on table [%.*s].",
+			command->db_table->name.len,
+			command->db_table->name.s);
+		goto done;
+	}
+	_p = p;
+	if(ph_build_reply(ctx)<0)
+		goto error;
+	p = ctx->reply.body.s + ctx->reply.body.len;
+	switch (command->type) {
+	case DB_CAP_QUERY:
+		for(j=0;j<command->q_keys_size;j++){
+			if(j)XHTTP_PI_COPY(p,XHTTP_PI_Response_Menu_Cmd_td_1d);
+			XHTTP_PI_COPY_2(p,*(command->q_keys[j]),
+					XHTTP_PI_Response_Menu_Cmd_td_4d);
+					
+		}
+		if (DB_CAPABILITY(db_url->http_dbf, DB_CAP_FETCH)){
+			if(db_url->http_dbf.query(db_url->http_db_handle,
+				command->c_keys, command->c_ops, c_vals,
+				command->q_keys,
+				command->c_keys_size,
+				command->q_keys_size,
+				command->o_keys?*command->o_keys:0, 0) < 0){
+				XHTTP_PI_COMPLETE_REPLY(ctx,
+					"Error while querying (fetch) database.");
+				goto done;
+			}
+			/* FIXME: implement proper fetch value */
+			if(db_url->http_dbf.fetch_result(db_url->http_db_handle,
+					&res, 100)<0){
+				XHTTP_PI_COMPLETE_REPLY(ctx,
+					"Fetching rows failed.");
+				goto done;
+			}
+		}else{
+			if(db_url->http_dbf.query(db_url->http_db_handle,
+				command->c_keys, command->c_ops, c_vals,
+				command->q_keys,
+				command->c_keys_size,
+				command->q_keys_size,
+				command->o_keys?*command->o_keys:0, 0) < 0){
+				XHTTP_PI_COMPLETE_REPLY(ctx,
+					"Error while querying database.");
+				goto done;
+			}
+		}
+		nr_rows = RES_ROW_N(res);
+		do{
+			LM_DBG("loading [%i] records from db\n", nr_rows);
+			rows = RES_ROWS(res);
+			for(i=0;i<nr_rows;i++){
+				values = ROW_VALUES(rows + i);
+				XHTTP_PI_COPY(p,XHTTP_PI_Response_Menu_Cmd_tr_1);
+				for(j=0;j<command->q_keys_size;j++){
+					XHTTP_PI_COPY(p,XHTTP_PI_Response_Menu_Cmd_td_1d);
+					switch(command->q_types[j]){
+					case DB1_STR:
+					case DB1_STRING:
+					case DB1_BLOB:
+						if(values[j].val.str_val.s==NULL){
+							LM_ERR("NULL\n");
+							goto error;
+						}
+						val_str.s = values[j].val.str_val.s;
+						val_str.len = strlen(val_str.s);
+						LM_DBG("...got %.*s[%d]=>"
+							"[%.*s][%.*s]\n",
+							command->q_keys[j]->len,
+							command->q_keys[j]->s, i,
+							values[j].val.str_val.len,
+							values[j].val.str_val.s,
+							val_str.len, val_str.s);
+					XHTTP_PI_COPY(p,
+						val_str.len?val_str:XHTTP_PI_NBSP);
+						break;
+					case DB1_INT:
+						val_str.s = p;
+						val_str.len = max_page_len - ctx->reply.body.len;
+						if(db_int2str(values[j].val.int_val,
+									val_str.s, &val_str.len)!=0){
+							LM_ERR("Unable to convert int [%d]\n",
+								values[j].val.int_val);
+							goto error;
+						}
+						p += val_str.len;
+						ctx->reply.body.len += val_str.len;
+						LM_DBG("   got %.*s[%d]=>"
+							"[%d][%.*s]\n",
+							command->q_keys[j]->len,
+							command->q_keys[j]->s, i,
+							values[j].val.int_val,
+							val_str.len, val_str.s);
+						break;
+					case DB1_BITMAP:
+						val_str.s = p;
+						val_str.len = max_page_len - ctx->reply.body.len;
+						if(db_int2str(values[j].val.bitmap_val,
+									val_str.s, &val_str.len)!=0){
+							LM_ERR("Unable to convert bitmap [%d]\n",
+								values[j].val.bitmap_val);
+							goto error;
+						}
+						p += val_str.len;
+						ctx->reply.body.len += val_str.len;
+						LM_DBG("   got %.*s[%d]=>"
+							"[%d][%.*s]\n",
+							command->q_keys[j]->len,
+							command->q_keys[j]->s, i,
+							values[j].val.bitmap_val,
+							val_str.len, val_str.s);
+						break;
+					case DB1_BIGINT:
+						val_str.s = p;
+						val_str.len = max_page_len - ctx->reply.body.len;
+						if(db_longlong2str(values[j].val.ll_val,
+									val_str.s, &val_str.len)!=0){
+							LM_ERR("Unable to convert bigint [%-lld]\n",
+								values[j].val.ll_val);
+							goto error;
+						}
+						p += val_str.len;
+						ctx->reply.body.len += val_str.len;
+						LM_DBG("   got %.*s[%d]=>"
+							"[%-lld][%.*s]\n",
+							command->q_keys[j]->len,
+							command->q_keys[j]->s, i,
+							values[j].val.ll_val,
+							val_str.len, val_str.s);
+						break;
+					case DB1_DOUBLE:
+						val_str.s = p;
+						val_str.len = max_page_len - ctx->reply.body.len;
+						if(db_double2str(values[j].val.double_val,
+									val_str.s, &val_str.len)!=0){
+							LM_ERR("Unable to convert double [%-10.2f]\n",
+								values[j].val.double_val);
+							goto error;
+						}
+						p += val_str.len;
+						ctx->reply.body.len += val_str.len;
+						LM_DBG("   got %.*s[%d]=>"
+							"[%-10.2f][%.*s]\n",
+							command->q_keys[j]->len,
+							command->q_keys[j]->s, i,
+							values[j].val.double_val,
+							val_str.len, val_str.s);
+						break;
+					case DB1_DATETIME:
+						val_str.s = p;
+						val_str.len = max_page_len - ctx->reply.body.len;
+						if(db_time2str(values[j].val.time_val,
+									val_str.s, &val_str.len)!=0){
+							LM_ERR("Unable to convert double [%ld]\n",
+								values[j].val.time_val);
+							goto error;
+						}
+						p += val_str.len;
+						ctx->reply.body.len += val_str.len;
+						LM_DBG("   got %.*s[%d]=>"
+							"[%ld][%.*s]\n",
+							command->q_keys[j]->len,
+							command->q_keys[j]->s, i,
+							values[j].val.time_val,
+							val_str.len, val_str.s);
+						break;
+					default:
+						LM_ERR("unexpected type [%d] "
+							"for [%.*s]\n",
+							command->q_types[j],
+							command->q_keys[j]->len,
+							command->q_keys[j]->s);
+					}
+					XHTTP_PI_COPY(p,XHTTP_PI_Response_Menu_Cmd_td_4d);
+				}
+				XHTTP_PI_COPY(p,XHTTP_PI_Response_Menu_Cmd_tr_2);
+			}
+			/* any more data to be fetched ?*/
+			if (DB_CAPABILITY(db_url->http_dbf, DB_CAP_FETCH)){
+				if(db_url->http_dbf.fetch_result(db_url->http_db_handle,
+					&res, 100)<0){
+					LM_ERR("fetching more rows failed\n");
+					goto error;
+				}
+				nr_rows = RES_ROW_N(res);
+			}else{
+				nr_rows = 0;
+			}
+		}while (nr_rows>0);
+		db_url->http_dbf.free_result(db_url->http_db_handle, res);
+		goto finish_page;
+		break;
+	case DB_CAP_INSERT:
+		if((db_url->http_dbf.insert(db_url->http_db_handle,
+			command->q_keys, q_vals, command->q_keys_size))!=0){
+			XHTTP_PI_COMPLETE_REPLY(ctx,
+					"Unable to add record to db.");
+		}else{
+			XHTTP_PI_COMPLETE_REPLY(ctx,
+					"Record successfully added to db.");
+		}
+		goto done;
+		break;
+	case DB_CAP_DELETE:
+		if((db_url->http_dbf.delete(db_url->http_db_handle,
+			command->c_keys, command->c_ops, c_vals,
+			command->c_keys_size))!=0) {
+			XHTTP_PI_COMPLETE_REPLY(ctx,
+					"Unable to delete record.");
+		}else{
+			XHTTP_PI_COMPLETE_REPLY(ctx,
+					"Record successfully deleted from db.");
+		}
+		goto done;
+		break;
+	case DB_CAP_UPDATE:
+		if((db_url->http_dbf.update(db_url->http_db_handle,
+			command->c_keys, command->c_ops, c_vals,
+			command->q_keys, q_vals,
+			command->c_keys_size, command->q_keys_size))!=0){
+			XHTTP_PI_COMPLETE_REPLY(ctx,
+					"Unable to update record.");
+		}else{
+			XHTTP_PI_COMPLETE_REPLY(ctx,
+					"Record successfully updated.");
+		}
+		goto done;
+		break;
+	case DB_CAP_REPLACE:
+		/* FIXME: set proper values for:
+		_un number of keys to build the unique key, starting from first _k
+		_m mode - first update, then insert, or first insert, then update
+		*/
+		if((db_url->http_dbf.replace(db_url->http_db_handle,
+			command->q_keys, q_vals, command->q_keys_size,
+			/*FIXME*/0, /*FIXME*/0))!=0){
+			XHTTP_PI_COMPLETE_REPLY(ctx,
+					"Unable to replace record.");
+		}else{
+			XHTTP_PI_COMPLETE_REPLY(ctx,
+					"Record successfully replaced.");
+		}
+		break;
+	default:
+		XHTTP_PI_COMPLETE_REPLY(ctx,
+			"Corrupt data for mod=[%d] and cmd=[%d]\n", mod, cmd);
+		goto done;
+	}
+	LM_ERR("You shoudn't end up here\n");
+error:
+	if(c_vals) pkg_free(c_vals);
+	if(q_vals) pkg_free(q_vals);
+	return -1;
+
+finish_page:
+	if(c_vals) pkg_free(c_vals);
+	if(q_vals) pkg_free(q_vals);
+	ctx->reply.body.len = p - ctx->reply.body.s;
+	return ph_build_reply_footer(ctx);
+
+done:
+	if(c_vals) pkg_free(c_vals);
+	if(q_vals) pkg_free(q_vals);
+	return 0;
+}
+

+ 113 - 0
modules/xhttp_pi/xhttp_pi_fnc.h

@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2012 VoIP Embedded, Inc.
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio 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
+ *
+ * Kamailio 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
+ *
+ * 2012-10-18  initial version (osas)
+ */
+
+
+#ifndef _XHTTP_PI_FNC_H
+#define _XHTTP_PI_FNC_H
+
+#include "../../lib/srdb1/db.h"
+#include "xhttp_pi.h"
+
+/**< no validation required */
+#define PH_FLAG_NONE        0
+/**< validate as socket: [proto:]host[:port] */
+#define PH_FLAG_P_HOST_PORT (1<<0)
+/**< validate as socket: [proto:]IPv4[:port] */
+#define PH_FLAG_P_IPV4_PORT (1<<1)
+/**< validate as IPv4 */
+#define PH_FLAG_IPV4        (1<<2)
+/**< validate as SIP URI */
+#define PH_FLAG_URI     (1<<3)
+/**< validate as SIP URI w/ IPv4 host */
+#define PH_FLAG_URI_IPV4HOST    (1<<4)
+
+typedef short int ph_val_flags;
+
+typedef struct ph_db_url_ {
+    str id;
+    str db_url;
+    db1_con_t *http_db_handle;
+    db_func_t http_dbf;
+}ph_db_url_t;
+
+typedef struct ph_table_col_ {
+    str field;
+    db_type_t type;
+    ph_val_flags validation;
+}ph_table_col_t;
+typedef struct ph_db_table_ {
+    str id;
+    str name;
+    ph_db_url_t *db_url;
+    ph_table_col_t *cols;
+    int cols_size;
+}ph_db_table_t;
+
+typedef struct ph_vals_ {
+    str *ids;
+    str *vals;
+    int vals_size;
+}ph_vals_t;
+
+typedef struct ph_cmd_ {
+    str name;
+    unsigned int type;
+    ph_db_table_t *db_table;
+    db_op_t *c_ops;
+    db_key_t *c_keys;
+    db_type_t *c_types;
+    ph_vals_t *c_vals;
+    int c_keys_size;
+    db_key_t *q_keys;
+    db_type_t *q_types;
+    ph_vals_t *q_vals;
+    int q_keys_size;
+    db_key_t *o_keys;
+    int o_keys_size;
+}ph_cmd_t;
+
+typedef struct ph_mod_ {
+    str module;
+    ph_cmd_t *cmds;
+    int cmds_size;
+}ph_mod_t;
+
+typedef struct ph_framework_ {
+    ph_db_url_t *ph_db_urls;
+    int ph_db_urls_size;
+    ph_db_table_t *ph_db_tables;
+    int ph_db_tables_size;
+    ph_mod_t *ph_modules;
+    int ph_modules_size;
+}ph_framework_t;
+
+
+int ph_init_async_lock(void);
+void ph_destroy_async_lock(void);
+
+int ph_init_cmds(ph_framework_t **framework_data, const char* filename);
+int ph_parse_url(const str* url_str, int* mod, int* cmd, str* arg);
+int ph_run_pi_cmd(pi_ctx_t *ctx);
+
+
+#endif
+