/*
 * 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 */

/*!
 * \file
 * \brief XHTTP_PI ::
 * \ingroup xhttp_pi
 * Module: \ref xhttp_pi
 */

#include <libxml/parser.h>

#include "../../core/str.h"
#include "../../core/ut.h"
#include "../../lib/srdb1/db_ut.h"
#include "../../core/mem/mem.h"
#include "../../core/mem/shm_mem.h"
#include "../../core/config.h"
#include "../../core/globals.h"
#include "../../core/socket_info.h"
#include "../../core/resolve.h"
#include "../../core/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_LINK_CMD_NODE "link_cmd"
#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)


#define XHTTP_PI_ESC_COPY(p, str, temp_holder, temp_counter)                \
	do {                                                                    \
		(temp_holder).s = (str).s;                                          \
		(temp_holder).len = 0;                                              \
		for((temp_counter) = 0; (temp_counter) < (str).len;                 \
				(temp_counter)++) {                                         \
			switch((str).s[(temp_counter)]) {                               \
				case '<':                                                   \
					(temp_holder).len = (temp_counter) - (temp_holder).len; \
					XHTTP_PI_COPY_2(p, (temp_holder), XHTTP_PI_ESC_LT);     \
					(temp_holder).s = (str).s + (temp_counter) + 1;         \
					(temp_holder).len = (temp_counter) + 1;                 \
					break;                                                  \
				case '>':                                                   \
					(temp_holder).len = (temp_counter) - (temp_holder).len; \
					XHTTP_PI_COPY_2(p, (temp_holder), XHTTP_PI_ESC_GT);     \
					(temp_holder).s = (str).s + (temp_counter) + 1;         \
					(temp_holder).len = (temp_counter) + 1;                 \
					break;                                                  \
				case '&':                                                   \
					(temp_holder).len = (temp_counter) - (temp_holder).len; \
					XHTTP_PI_COPY_2(p, (temp_holder), XHTTP_PI_ESC_AMP);    \
					(temp_holder).s = (str).s + (temp_counter) + 1;         \
					(temp_holder).len = (temp_counter) + 1;                 \
					break;                                                  \
				case '"':                                                   \
					(temp_holder).len = (temp_counter) - (temp_holder).len; \
					XHTTP_PI_COPY_2(p, (temp_holder), XHTTP_PI_ESC_QUOT);   \
					(temp_holder).s = (str).s + (temp_counter) + 1;         \
					(temp_holder).len = (temp_counter) + 1;                 \
					break;                                                  \
				case '\'':                                                  \
					(temp_holder).len = (temp_counter) - (temp_holder).len; \
					XHTTP_PI_COPY_2(p, (temp_holder), XHTTP_PI_ESC_SQUOT);  \
					(temp_holder).s = (str).s + (temp_counter) + 1;         \
					(temp_holder).len = (temp_counter) + 1;                 \
					break;                                                  \
			}                                                               \
		}                                                                   \
		(temp_holder).len = (temp_counter) - (temp_holder).len;             \
		XHTTP_PI_COPY(p, (temp_holder));                                    \
	} while(0)


static const str XHTTP_PI_Response_Head_1 = str_init(
		"<html><head><title>"
		"Kamailio Provisioning 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 Provisioning 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_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_SQUOT_GT = 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_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=\"https://kamailio.org\">Kamailio web site</a><br/>"
				 "Copyright &copy; 2012-2014 <a "
				 "href=\"http://www.voipembedded.com/\">VoIP Embedded Inc.</a>"
				 ". All rights reserved."
				 "</div></body></html>");

#define XHTTP_PI_ROWSPAN 20
static const str XHTTP_PI_CMD_ROWSPAN = str_init("20");

static const str XHTTP_PI_ESC_LT = str_init("&lt;");	 /* < */
static const str XHTTP_PI_ESC_GT = str_init("&gt;");	 /* > */
static const str XHTTP_PI_ESC_AMP = str_init("&amp;");	 /* & */
static const str XHTTP_PI_ESC_QUOT = str_init("&quot;"); /* " */
static const str XHTTP_PI_ESC_SQUOT = str_init("&#39;"); /* ' */

static const str XHTTP_PI_HREF_1 = str_init("<a href='/");
static const str XHTTP_PI_HREF_2 = str_init("?cmd=pre&");
static const str XHTTP_PI_HREF_3 = str_init("</a>");

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) {
				SHM_MEM_ERROR;
				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_db_urls == NULL || _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) {
				SHM_MEM_ERROR;
				return -1;
			}
			db_tables->cols = cols;
			cols = &db_tables->cols[db_tables->cols_size];
			memset(cols, 0, sizeof(ph_table_col_t));
			cols->type = DB1_UNKNOWN;
			/* 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 == DB1_UNKNOWN) {
				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) {
				SHM_MEM_ERROR;
				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_db_tables == NULL || _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) {
				SHM_MEM_ERROR;
				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, str **mod_cmd_linkCmd, 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;
	str link_cmd;
	str *linkCmd;
	str *cmd_linkCmd = 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) {
				SHM_MEM_ERROR;
				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) {
				SHM_MEM_ERROR;
				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) {
					SHM_MEM_ERROR;
					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) {
					SHM_MEM_ERROR;
					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) {
					SHM_MEM_ERROR;
					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) {
					SHM_MEM_ERROR;
					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;
			}
			/* Retrieve the link_cmds */
			if(mod_cmd_linkCmd) {
				if(size)
					linkCmd = (str *)shm_realloc(
							cmd_linkCmd, (size + 1) * sizeof(str));
				else
					linkCmd = (str *)shm_malloc(sizeof(str));
				if(linkCmd == NULL) {
					SHM_MEM_ERROR;
					return -1;
				}
				cmd_linkCmd = linkCmd;
				linkCmd = &cmd_linkCmd[size];
				memset(linkCmd, 0, sizeof(str));
				/* get the link_cmd */
				link_cmd.s = ph_xmlNodeGetNodeContentByName(
						node->children, XHTTP_PI_XML_LINK_CMD_NODE);
				if(link_cmd.s != NULL) {
					link_cmd.len = strlen(link_cmd.s);
					if(link_cmd.len != 0) {
						LM_DBG("got %s=[%.*s] in %s [%.*s] %s [%.*s] %s %s\n",
								XHTTP_PI_XML_LINK_CMD_NODE, link_cmd.len,
								link_cmd.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);
						if(shm_str_dup(linkCmd, &link_cmd))
							return -1;
					}
					xmlFree(link_cmd.s);
					link_cmd.s = NULL;
					link_cmd.len = 0;
				}
			}
			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(mod_cmd_linkCmd)
			*mod_cmd_linkCmd = cmd_linkCmd;
		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) {
				SHM_MEM_ERROR;
				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,
								   NULL, &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->link_cmd,
								   &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,
								   NULL, NULL, &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, 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;
					}
				} 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,
								   NULL, &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,
								   NULL, &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, 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;
					}
				}
			} 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, 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;
					}
				}
			}
			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] link_cmd=[%.*s]\n",
							i, (*(cmds->q_keys[i])).len, (*(cmds->q_keys[i])).s,
							cmds->q_types[i],
							(cmds->link_cmd) ? (cmds->link_cmd[i]).len : 0,
							(cmds->link_cmd) ? (cmds->link_cmd[i]).s : NULL);
					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) {
				SHM_MEM_ERROR;
				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;
	ph_vals_t *cmd_vals;
	str *cmd_linkCmd;

	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_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_ops = NULL;
			/* */
			cmd_keys = _ph_modules[i].cmds[j].q_keys;
			cmd_vals = _ph_modules[i].cmds[j].q_vals;
			cmd_linkCmd = _ph_modules[i].cmds[j].link_cmd;
			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(cmd_linkCmd && cmd_linkCmd[k].s) {
					shm_free(cmd_linkCmd[k].s);
					cmd_linkCmd[k].s = 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;
			}
			if(_ph_modules[i].cmds[j].link_cmd) {
				shm_free(_ph_modules[i].cmds[j].link_cmd);
				_ph_modules[i].cmds[j].link_cmd = 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) {
			SHM_MEM_ERROR;
			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 the 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, str *page, int max_page_len,
		int mod, int cmd, str *clause, db_val_t *values)
{
	unsigned long i, j;
	char c;
	str op, arg;
	str val_str;
	str temp_holder;
	int temp_counter;
	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);
					if(i == 0 && clause) {
						XHTTP_PI_COPY(*p, XHTTP_PI_Post_Input_Hidden_2);
						XHTTP_PI_COPY(*p, *clause);
					}
					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);
					if(values) {
						XHTTP_PI_COPY(*p, XHTTP_PI_Post_Input_Hidden_2);
						switch(command->q_types[i]) {
							case DB1_STR:
							case DB1_STRING:
							case DB1_BLOB:
								if(values[i].val.str_val.s == NULL) {
									val_str.s = NULL;
									val_str.len = 0;
								} else {
									val_str.s = values[i].val.str_val.s;
									val_str.len = strlen(val_str.s);
								}
								LM_DBG("...got %.*s[0]=>"
									   "[%.*s][%.*s]\n",
										command->q_keys[i]->len,
										command->q_keys[i]->s,
										values[i].val.str_val.len,
										values[i].val.str_val.s, val_str.len,
										val_str.s);
								if(val_str.len) {
									XHTTP_PI_ESC_COPY(*p, val_str, temp_holder,
											temp_counter);
								}
								break;
							case DB1_INT:
								val_str.s = *p;
								val_str.len = max_page_len - page->len;
								if(db_int2str(values[i].val.int_val, val_str.s,
										   &val_str.len)
										!= 0) {
									LM_ERR("Unable to convert int [%d]\n",
											values[i].val.int_val);
									goto error;
								}
								*p += val_str.len;
								page->len += val_str.len;
								LM_DBG("   got %.*s[0]=>"
									   "[%d][%.*s]\n",
										command->q_keys[i]->len,
										command->q_keys[i]->s,
										values[i].val.int_val, val_str.len,
										val_str.s);
								break;
							case DB1_BITMAP:
								val_str.s = *p;
								val_str.len = max_page_len - page->len;
								if(db_int2str(values[i].val.bitmap_val,
										   val_str.s, &val_str.len)
										!= 0) {
									LM_ERR("Unable to convert bitmap [%d]\n",
											values[i].val.bitmap_val);
									goto error;
								}
								*p += val_str.len;
								page->len += val_str.len;
								LM_DBG("   got %.*s[0]=>"
									   "[%d][%.*s]\n",
										command->q_keys[i]->len,
										command->q_keys[i]->s,
										values[i].val.bitmap_val, val_str.len,
										val_str.s);
								break;
							case DB1_BIGINT:
								val_str.s = *p;
								val_str.len = max_page_len - page->len;
								if(db_longlong2str(values[i].val.ll_val,
										   val_str.s, &val_str.len)
										!= 0) {
									LM_ERR("Unable to convert bigint [%-lld]\n",
											values[i].val.ll_val);
									goto error;
								}
								*p += val_str.len;
								page->len += val_str.len;
								LM_DBG("   got %.*s[0]=>"
									   "[%-lld][%.*s]\n",
										command->q_keys[i]->len,
										command->q_keys[i]->s,
										values[i].val.ll_val, val_str.len,
										val_str.s);
								break;
							case DB1_DOUBLE:
								val_str.s = *p;
								val_str.len = max_page_len - page->len;
								if(db_double2str(values[i].val.double_val,
										   val_str.s, &val_str.len)
										!= 0) {
									LM_ERR("Unable to convert double "
										   "[%-10.2f]\n",
											values[i].val.double_val);
									goto error;
								}
								*p += val_str.len;
								page->len += val_str.len;
								LM_DBG("   got %.*s[0]=>"
									   "[%-10.2f][%.*s]\n",
										command->q_keys[i]->len,
										command->q_keys[i]->s,
										values[i].val.double_val, val_str.len,
										val_str.s);
								break;
							case DB1_DATETIME:
								val_str.s = *p;
								val_str.len = max_page_len - page->len;
								if(db_time2str(values[i].val.time_val,
										   val_str.s, &val_str.len)
										!= 0) {
									LM_ERR("Unable to convert time [%ld]\n",
											(unsigned long int)values[i]
													.val.time_val);
									goto error;
								}
								*p += val_str.len;
								page->len += val_str.len;
								LM_DBG("   got %.*s[0]=>"
									   "[%ld][%.*s]\n",
										command->q_keys[i]->len,
										command->q_keys[i]->s,
										(unsigned long int)values[i]
												.val.time_val,
										val_str.len, val_str.s);
								break;
							default:
								LM_ERR("unexpected type [%d] "
									   "for [%.*s]\n",
										command->q_types[i],
										command->q_keys[i]->len,
										command->q_keys[i]->s);
						}
					}
					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 command 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_SQUOT_GT,
			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, str *clause, db_val_t *values)
{
	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 commands 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_SQUOT_GT,
				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_SQUOT_GT,
					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, &ctx->reply.body,
							   max_page_len, mod, cmd, clause, values)
							!= 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, &ctx->reply.body, max_page_len,
						   mod, cmd, clause, values)
						!= 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] (%d)\n", arg->len, arg->s, proto,
						host.len, host.s, port, (int)flags);
				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] (%d)\n", arg->len, arg->s, proto,
						host.len, host.s, port, (int)flags);
				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;
				}
				LM_DBG("[%.*s] has new flags [%d]\n", key->len, key->s, flags);
				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;
				}
				LM_DBG("[%.*s] has new flags [%d]\n", key->len, key->s, flags);
				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;
				}
				LM_DBG("[%.*s] has new flags [%d]\n", key->len, key->s, flags);
				continue;
			}
			LM_DBG("[%.*s] has flags [%d]\n", key->len, key->s, flags);
			if(flags) {
				XHTTP_PI_BUILD_REPLY(ctx, "Unknown 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 *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;
	str temp_holder;
	int temp_counter;
	int i;
	int j;
	int max_page_len = ctx->reply.buf.len;
	ph_cmd_t *command;

	int _len;
	int link_on;

	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 = NULL;
	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, NULL, NULL);

	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, NULL, NULL);

	LM_DBG("got arg cmd=[%.*s]\n", arg_val.len, arg_val.s);

	command = &ph_modules[mod].cmds[cmd];

	if(arg_val.len == 3 && strncmp(arg_val.s, "pre", 3) == 0) {
		/* We prebuild values only for update */
		if(command->type != DB_CAP_UPDATE) {
			LM_ERR("command [%.*s] is not DB_CAP_UPDATE type\n",
					command->name.len, command->name.s);
			return ph_build_content(ctx, NULL, NULL);
		}
		/* We prebuild values only for single clause update command */
		if(command->c_keys_size != 1) {
			LM_ERR("command [%.*s] has [%d] clause keys\n", command->name.len,
					command->name.s, command->c_keys_size);
			return ph_build_content(ctx, NULL, NULL);
		}
		LM_DBG("[%.*s] with clause key [%.*s]\n", command->name.len,
				command->name.s, command->c_keys[0]->len,
				command->c_keys[0]->s);

		arg_name.s = command->c_keys[0]->s;
		arg_name.len = command->c_keys[0]->len;
		ph_parse_arg(&arg_url, &arg_name, &arg_val);
		if(arg_val.s == NULL) {
			LM_ERR("missing clause key [%.*s] in args\n",
					command->c_keys[0]->len, command->c_keys[0]->s);
			return ph_build_content(ctx, NULL, NULL);
		}

		LM_DBG("got clause [%.*s] with value [%.*s]\n", command->c_keys[0]->len,
				command->c_keys[0]->s, arg_val.len, arg_val.s);

		c_vals =
				(db_val_t *)pkg_malloc(command->c_keys_size * sizeof(db_val_t));
		if(c_vals == NULL) {
			PKG_MEM_ERROR;
			return -1;
		}
		memset(c_vals, 0, command->c_keys_size * sizeof(db_val_t));

		val = &c_vals[0];
		val->type = command->c_types[0];
		ret = getVal(val, command->c_types[0], command->c_keys[0],
				command->db_table, &arg_val, ctx);
		if(ret < 0)
			goto error;
		else if(ret > 0)
			goto finish_page;

		/* Let's run the query to get the values for the record to update*/
		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 finish_page;
		}

		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, &res)
				< 0) {
			XHTTP_PI_COMPLETE_REPLY(ctx, "Error while querying database.");
			goto finish_page;
		}
		nr_rows = RES_ROW_N(res);
		switch(nr_rows) {
			case 0:
				LM_ERR("no record on clause key [%.*s]\n",
						command->c_keys[0]->len, command->c_keys[0]->s);
				if(c_vals) {
					pkg_free(c_vals);
					c_vals = NULL;
				}
				goto finish_page;
			case 1:
				LM_DBG("got [%d] rows for key [%.*s]\n", nr_rows,
						command->c_keys[0]->len, command->c_keys[0]->s);
				break;
			default:
				LM_ERR("to many records [%d] on clause key [%.*s]\n", nr_rows,
						command->c_keys[0]->len, command->c_keys[0]->s);
				goto finish_page;
		}

		rows = RES_ROWS(res);
		values = ROW_VALUES(rows);
		ret = ph_build_content(ctx, &arg_val, values);
		db_url->http_dbf.free_result(db_url->http_db_handle, res);
		//res = NULL;
		return ret;
	} else if(arg_val.len == 2 && strncmp(arg_val.s, "on", 2) == 0) {
		/* allocate c_vals array */
		if(command->c_keys_size) {
			c_vals = (db_val_t *)pkg_malloc(
					command->c_keys_size * sizeof(db_val_t));
			if(c_vals == NULL) {
				PKG_MEM_ERROR;
				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->type != DB_CAP_QUERY) {
		q_vals =
				(db_val_t *)pkg_malloc(command->q_keys_size * sizeof(db_val_t));
		if(q_vals == NULL) {
			PKG_MEM_ERROR;
			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;
	}
	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;
				}
				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, &res)
						< 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);
						/* BEGIN */
						link_on = 0;
						if(command->link_cmd && command->link_cmd[j].s) {
							link_on = 1;
							XHTTP_PI_COPY(p, XHTTP_PI_HREF_1);
							if(xhttp_pi_root.len) {
								XHTTP_PI_COPY_2(
										p, xhttp_pi_root, XHTTP_PI_SLASH);
							}
							XHTTP_PI_COPY_2(
									p, ph_modules[mod].module, XHTTP_PI_SLASH);
							XHTTP_PI_COPY(
									p, command->link_cmd
											   [j]); /* this is the command */
							XHTTP_PI_COPY_3(p, XHTTP_PI_HREF_2,
									*command->q_keys[j],
									XHTTP_PI_ATTR_VAL_SEPARATOR);
						}
						/* END */
						switch(command->q_types[j]) {
							case DB1_STR:
							case DB1_STRING:
							case DB1_BLOB:
								if(values[j].val.str_val.s == NULL) {
									val_str.s = NULL;
									val_str.len = 0;
								} else {
									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);
								if(val_str.len) {
									if(link_on) {
										XHTTP_PI_ESC_COPY(p, val_str,
												temp_holder, temp_counter);
										XHTTP_PI_COPY(p, XHTTP_PI_SQUOT_GT);
									}
									XHTTP_PI_ESC_COPY(p, val_str, temp_holder,
											temp_counter);
								} else {
									if(link_on) {
										XHTTP_PI_COPY(p, XHTTP_PI_NBSP);
										XHTTP_PI_COPY(p, XHTTP_PI_SQUOT_GT);
									}
									XHTTP_PI_COPY(p, 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;
								if(link_on)
									XHTTP_PI_COPY_2(
											p, XHTTP_PI_SQUOT_GT, val_str);
								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;
								if(link_on)
									XHTTP_PI_COPY_2(
											p, XHTTP_PI_SQUOT_GT, val_str);
								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;
								if(link_on)
									XHTTP_PI_COPY_2(
											p, XHTTP_PI_SQUOT_GT, val_str);
								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;
								if(link_on)
									XHTTP_PI_COPY_2(
											p, XHTTP_PI_SQUOT_GT, val_str);
								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 "
										   "[%" TIME_T_FMT "]\n",
											TIME_T_CAST(
													values[j].val.time_val));
									goto error;
								}
								p += val_str.len;
								ctx->reply.body.len += val_str.len;
								if(link_on)
									XHTTP_PI_COPY_2(
											p, XHTTP_PI_SQUOT_GT, val_str);
								LM_DBG("   got %.*s[%d]=>"
									   "[%" TIME_T_FMT "][%.*s]\n",
										command->q_keys[j]->len,
										command->q_keys[j]->s, i,
										TIME_T_CAST(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);
						}
						if(link_on)
							XHTTP_PI_COPY(p, XHTTP_PI_HREF_3);
						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;
}
