/*
 * Copyright (C) 2011 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
 *
 */

#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>

#include "../../core/ver.h"
#include "../../core/trim.h"
#include "../../core/sr_module.h"
#include "../../core/nonsip_hooks.h"
#include "../../core/kemi.h"
#include "../../modules/xhttp/api.h"
#include "xhttp_rpc.h"
#include "xhttp_rpc_fnc.h"

/** @addtogroup xhttp_rpc
 * @ingroup modules
 * @{
 *
 * <h1>Overview of Operation</h1>
 * This module provides a web interface for RPC management interface.
 * It is built on top of the xhttp API module.
 */

/** @file
 *
 * This is the main file of xhttp_rpc module which contains all the functions
 * related to http processing, as well as the module interface.
 */

MODULE_VERSION

str XHTTP_RPC_REASON_OK = str_init("OK");
str XHTTP_RPC_CONTENT_TYPE_TEXT_HTML = str_init("text/html");


xhttp_rpc_mod_cmds_t *xhttp_rpc_mod_cmds = NULL;
int xhttp_rpc_mod_cmds_size = 0;

/* FIXME: this should be initialized in ../../core/ver.c */
int full_version_len;
int ver_name_len;

static int mod_init(void);
static int child_init(int rank);
static int xhttp_rpc_dispatch(sip_msg_t* msg, char* s1, char* s2);


/** The context of the xhttp_rpc request being processed.
 *
 * This is a global variable that records the context of the xhttp_rpc request
 * being currently processed.
 * @sa rpc_ctx
 */
static rpc_ctx_t ctx;

static xhttp_api_t xhttp_api;

/** Pointers to the functions that implement the RPC interface
 * of xhttp_rpc module
 */
static rpc_t func_param;

str xhttp_rpc_root = str_init("rpc");
int buf_size = 0;
char error_buf[ERROR_REASON_BUF_LEN];

static cmd_export_t cmds[] = {
	{"dispatch_xhttp_rpc",(cmd_function)xhttp_rpc_dispatch,0,0,0,
			REQUEST_ROUTE|EVENT_ROUTE},
	{0, 0, 0, 0, 0, 0}
};

static param_export_t params[] = {
	{"xhttp_rpc_root",	PARAM_STR,	&xhttp_rpc_root},
	{"xhttp_rpc_buf_size",	INT_PARAM,	&buf_size},
	{0, 0, 0}
};

/** module exports */
struct module_exports exports= {
	"xhttp_rpc",		/* module name */
	DEFAULT_DLFLAGS,	/* dlopen flags */
	cmds,				/* cmd (cfg function) exports */
	params,				/* param exports */
	0,					/* RPC method exports */
	0,					/* pv exports */
	0,					/* response handling function */
	mod_init,			/* module init function */
	child_init,			/* per-child init function */
	0					/* module destroy function */
};


/** Implementation of rpc_fault function required by the management API.
 *
 * This function will be called whenever a management function
 * indicates that an error ocurred while it was processing the request. The
 * function takes the reply code and reason phrase as parameters, these will
 * be put in the body of the reply.
 *
 * @param ctx A pointer to the context structure of the request being
 *            processed.
 * @param code Reason code.
 * @param fmt Formatting string used to build the reason phrase.
 */
static void rpc_fault(rpc_ctx_t* ctx, int code, char* fmt, ...)
{
	va_list ap;
	struct xhttp_rpc_reply *reply = &ctx->reply;

	reply->code = code;
	va_start(ap, fmt);
	vsnprintf(error_buf, ERROR_REASON_BUF_LEN, fmt, ap);
	va_end(ap);
	reply->reason.len = strlen(error_buf);
	reply->reason.s = error_buf;
	/* reset body so we can print the error */
	reply->body.len = 0;

	return;
}


/**
 */
static void free_data_struct(struct rpc_data_struct *rpc_d)
{
	struct rpc_data_struct *ds;

	if (!rpc_d) {
		LM_ERR("Atempting to free NULL rpc_data_struct\n");
		return;
	}
	while (rpc_d) {
		ds = rpc_d->next;
		pkg_free(rpc_d);
		rpc_d = ds;
	}
	return;
}


/**
 */
static struct rpc_data_struct *new_data_struct(rpc_ctx_t* ctx)
{
	struct rpc_data_struct *ds;

	if (!ctx) return NULL;
	ds = (struct rpc_data_struct*)pkg_malloc(sizeof(struct rpc_data_struct));
	if (!ds) {
		PKG_MEM_ERROR;
		rpc_fault(ctx, 500, "Internal Server Error (oom)");
		return NULL;
	}
	memset(ds, 0, sizeof(struct rpc_data_struct));
	ds->ctx = ctx;

	return ds;
}


/** Initialize xhttp_rpc reply data structure.
 *
 * This function initializes the data structure that contains all data related
 * to the xhttp_rpc reply being created. The function must be called before any
 * other function that adds data to the reply.
 * @param ctx rpc_ctx_t structure to be initialized.
 * @return 0 on success, a negative number on error.
 */
static int init_xhttp_rpc_reply(rpc_ctx_t *ctx)
{
	struct xhttp_rpc_reply *reply = &ctx->reply;

	reply->code = 200;
	reply->reason = XHTTP_RPC_REASON_OK;
	reply->buf.s = pkg_malloc(buf_size);
	if (!reply->buf.s) {
		PKG_MEM_ERROR;
		rpc_fault(ctx, 500, "Internal Server Error (No memory left)");
		return -1;
	}
	reply->buf.len = buf_size;
	reply->body.s = reply->buf.s;
	reply->body.len = 0;
	return 0;
}


/** Implementation of rpc_send function required by the management API.
 *
 * This is the function that will be called whenever a management function
 * asks the management interface to send the reply to the client.
 * The SIP/HTTP reply sent to
 * the client will be always 200 OK, if an error ocurred on the server then it
 * will be indicated in the html document in body.
 *
 * @param ctx A pointer to the context structure of the xhttp_rpc request that
 *            generated the reply.
 * @return 1 if the reply was already sent, 0 on success, a negative number on
 *            error
 */
static int rpc_send(rpc_ctx_t* ctx)
{
	struct xhttp_rpc_reply* reply;

	if (ctx->reply_sent) return 1;

	reply = &ctx->reply;

	if (0!=xhttp_rpc_build_page(ctx)){
		rpc_fault(ctx, 500, "Internal Server Error");
	}

	ctx->reply_sent = 1;
	if (reply->body.len)
		xhttp_api.reply(ctx->msg, reply->code, &reply->reason,
			&XHTTP_RPC_CONTENT_TYPE_TEXT_HTML, &reply->body);
	else
		xhttp_api.reply(ctx->msg, reply->code, &reply->reason,
			&XHTTP_RPC_CONTENT_TYPE_TEXT_HTML, &reply->reason);

	if (reply->buf.s) {
		pkg_free(reply->buf.s);
		reply->buf.s = NULL;
		reply->buf.len = 0;
	}
	if (ctx->arg.s) {
		pkg_free(ctx->arg.s);
		ctx->arg.s = NULL;
		ctx->arg.len = 0;
	}
	if (ctx->data_structs) {
		free_data_struct(ctx->data_structs);
		ctx->data_structs = NULL;
	}

	return 0;
}


/** Converts the variables provided in parameter ap according to formatting
 * string provided in parameter fmt into HTML format.
 *
 * This function takes the parameters provided in ap parameter and creates
 * HTML formatted parameters that will be put in the html document.
 * The format of input parameters is described in formatting string
 * fmt which follows the syntax of the management API. In the case of
 * an error the function will generate an error reply in err_reply parameter
 * instead.
 * @param ctx An error reply document will be generated here if the
 *                  function encounters a problem while processing input
 *                  parameters.
 * @param fmt Formatting string of the management API.
 * @param ap A pointer to the array of input parameters.
 *
 */
static int print_value(rpc_ctx_t* ctx, char fmt, va_list* ap, str *id)

{
	str body;
	str *sp;
	char buf[PRINT_VALUE_BUF_LEN];
	time_t dt;
	struct tm t;

	switch(fmt) {
	case 'd':
		body.s = sint2str(va_arg(*ap, int), &body.len);
		break;
	case 'f':
		body.s = buf;
		body.len = snprintf(buf, PRINT_VALUE_BUF_LEN,
				"%f", va_arg(*ap, double));
		if (body.len < 0) {
			LM_ERR("Error while converting double\n");
			return -1;
		}
		break;
	case 'b':
		body.len = 1;
		body.s = ((va_arg(*ap, int)==0)?"0":"1");
		break;
	case 't':
		body.s = buf;
		body.len = sizeof("19980717T14:08:55") - 1;
		dt = va_arg(*ap, time_t);
		gmtime_r(&dt, &t);
		if (strftime(buf, PRINT_VALUE_BUF_LEN,
				"%Y%m%dT%H:%M:%S", &t) == 0) {
			LM_ERR("Error while converting time\n");
			return -1;
		}
		break;
	case 's':
		body.s = va_arg(*ap, char*);
		body.len = strlen(body.s);
		break;
	case 'S':
		sp = va_arg(*ap, str*);
		body = *sp;
		break;
	default:
		body.len = 0;
		body.s = NULL;
		LM_ERR("Invalid formatting character [%c]\n", fmt);
		return -1;
	}
	if (0!=xhttp_rpc_build_content(ctx, &body, id)) {
		rpc_fault(ctx, 500, "Internal Server Error");
		return -1;
	}
	return 0;
}


/** Implementation of rpc_add function required by the management API.
 *
 * This function will be called when an RPC management function calls
 * rpc->add to add a parameter to the xhttp_rpc reply being generated.
 */
static int rpc_add(rpc_ctx_t* ctx, char* fmt, ...)
{
	void **void_ptr;
	struct rpc_data_struct *ds;
	va_list ap;

	if (0!=xhttp_rpc_build_content(ctx, NULL, NULL)) {
		rpc_fault(ctx, 500, "Internal Server Error");
		return -1;
	}
	va_start(ap, fmt);
	while(*fmt) {
		if (*fmt == '{' || *fmt == '[') {
			void_ptr = va_arg(ap, void**);
			ds = new_data_struct(ctx);
			if (!ds) goto err;
			if (ctx->data_structs) free_data_struct(ctx->data_structs);
			ctx->data_structs = ds;
			*void_ptr = ds;
		} else {
			if (print_value(ctx, *fmt, &ap, NULL) < 0) goto err;
		}
		fmt++;
	}
	va_end(ap);
	return 0;
err:
	va_end(ap);
	return -1;
}


/** Implementation of rpc->scan function required by the management API.
 *
 * This is the function that will be called whenever a management function
 * calls rpc->scan to get the value of parameter from the xhttp_rpc
 * request. This function will extract the current parameter from the xhttp_rpc
 * URL and attempts to convert it to the type requested by the management
 * function that called it.
 */
static int rpc_scan(rpc_ctx_t* ctx, char* fmt, ...)
{
	int *int_ptr;
	char **char_ptr;
	double *double_ptr;
	str *str_ptr;

	str arg;

	int mandatory_param = 1;
	int modifiers = 0;
	char* orig_fmt;
	va_list ap;

	orig_fmt=fmt;
	va_start(ap, fmt);
	while(*fmt) {
		switch(*fmt) {
		case '*': /* start of optional parameters */
			mandatory_param = 0;
			modifiers++;
			fmt++;
			continue;
			break;
		case '.': /* autoconvert */
			modifiers++;
			fmt++;
			continue;
			break;
		case 'b': /* Bool */
		case 't': /* Date and time */
		case 'd': /* Integer */
			xhttp_rpc_get_next_arg(ctx, &arg);
			if (arg.len==0)
				goto read_error;
			int_ptr = va_arg(ap, int*);
			*int_ptr = strtol(arg.s, 0, 0);
			break;
		case 'f': /* double */
			xhttp_rpc_get_next_arg(ctx, &arg);
			if (arg.len==0)
				goto read_error;
			double_ptr = va_arg(ap, double*);
			*double_ptr = strtod(arg.s, 0);
			break;
		case 's': /* zero terminated string */
			xhttp_rpc_get_next_arg(ctx, &arg);
			if (arg.len==0)
				goto read_error;
			char_ptr = va_arg(ap, char**);
			*char_ptr = arg.s;
			break;
		case 'S': /* str structure */
			xhttp_rpc_get_next_arg(ctx, &arg);
			if (arg.len==0)
				goto read_error;
			str_ptr = va_arg(ap, str*);
			*str_ptr = arg;
			break;
		case '{':
			xhttp_rpc_get_next_arg(ctx, &arg);
			if (arg.len==0)
				goto read_error;
			LM_ERR("Unsupported param type [{]\n");
			rpc_fault(ctx, 500, "Unsupported param type [{]");
			goto error;
			break;
		default:
			LM_ERR("Invalid param type in formatting string: [%c]\n", *fmt);
			rpc_fault(ctx, 500,
				"Internal Server Error (inval formatting str)");
			goto error;
		}
		fmt++;
	}
	va_end(ap);
	return (int)(fmt-orig_fmt)-modifiers;
read_error:
	if (mandatory_param) rpc_fault(ctx, 400, "Invalid parameter value");
error:
	va_end(ap);
	return -((int)(fmt-orig_fmt)-modifiers);
}


/** Implementation of rpc_rpl_printf function required by the management API.
 *
 * This function will be called whenever an RPC management function calls
 * rpc-printf to add a parameter to the xhttp_rpc reply being constructed.
 */
static int rpc_rpl_printf(rpc_ctx_t* ctx, char* fmt, ...)
{
	int n, size;
	char *p;
	va_list ap;

	if (0!=xhttp_rpc_build_content(ctx, NULL, NULL)) {
		rpc_fault(ctx, 500, "Internal Server Error");
		return -1;
	}

	p = ctx->reply.body.s + ctx->reply.body.len;
	size = ctx->reply.buf.len - ctx->reply.body.len;
	va_start(ap, fmt);
	n = vsnprintf(p, size, fmt, ap);
	va_end(ap);
	if (n > -1 && n < size) {
		ctx->reply.body.len += n;
		p += n;
	} else {
		LM_ERR("oom\n");
		rpc_fault(ctx, 500, "Internal Server Error (oom)");
		return -1;
	}
	if (0!=xhttp_rpc_insert_break(ctx)) {
		LM_ERR("oom\n");
		rpc_fault(ctx, 500, "Internal Server Error (oom)");
		return -1;
	}

	return 0;
}


/** Adds a new member to structure.
 */
static int rpc_struct_add(struct rpc_data_struct* rpc_s, char* fmt, ...)
{
	va_list ap;
	void **void_ptr;
	str member_name;
	rpc_ctx_t *ctx = rpc_s->ctx;
	struct rpc_data_struct *ds, *s;

	if (!ctx) {
		LM_ERR("Invalid context\n");
		return -1;
	}
	if (!ctx->data_structs) {
		LM_ERR("Invalid structs\n");
		return -1;
	}
	s = ds = ctx->data_structs;
	ctx->struc_depth = 0;
	while (s) {
		if (s == rpc_s) {
			if (s->next) {
				free_data_struct(s->next);
				s->next = NULL;
			}
			break;
		}
		ctx->struc_depth++;
		ds = s;
		s = s->next;
	}
	if (!s)
		s = ds;
	va_start(ap, fmt);
	while(*fmt) {
		member_name.s = va_arg(ap, char*);
		member_name.len = (member_name.s?strlen(member_name.s):0);
		if (*fmt == '{' || *fmt == '[') {
			void_ptr = va_arg(ap, void**);
			ds = new_data_struct(ctx);
			if (!ds) goto err;
			s->next = ds;
			*void_ptr = ds;
			if (0!=xhttp_rpc_build_content(ctx, NULL, &member_name))
				goto err;
		} else {
			if (print_value(ctx, *fmt, &ap, &member_name) < 0) goto err;
		}
		fmt++;
	}
	va_end(ap);
	return 0;
err:
	va_end(ap);
	return -1;
}


/** Adds a new member to array.
 */
static int rpc_array_add(struct rpc_data_struct* rpc_s, char* fmt, ...)
{
	va_list ap;
	void **void_ptr;
	rpc_ctx_t *ctx = rpc_s->ctx;
	struct rpc_data_struct *ds, *s;

	if (!ctx) {
		LM_ERR("Invalid context\n");
		return -1;
	}
	if (!ctx->data_structs) {
		LM_ERR("Invalid structs\n");
		return -1;
	}
	s = ds = ctx->data_structs;
	ctx->struc_depth = 0;
	while (s) {
		if (s == rpc_s) {
			if (s->next) {
				free_data_struct(s->next);
				s->next = NULL;
			}
			break;
		}
		ctx->struc_depth++;
		ds = s;
		s = s->next;
	}
	if (!s)
		s = ds;
	va_start(ap, fmt);
	while(*fmt) {
		if (*fmt == '{' || *fmt == '[') {
			void_ptr = va_arg(ap, void**);
			ds = new_data_struct(ctx);
			if (!ds) goto err;
			s->next = ds;
			*void_ptr = ds;
			if (0!=xhttp_rpc_build_content(ctx, NULL, NULL))
				goto err;
		} else {
			if (print_value(ctx, *fmt, &ap, NULL) < 0) goto err;
		}
		fmt++;
	}
	va_end(ap);
	return 0;
err:
	va_end(ap);
	return -1;
}


static int rpc_struct_scan(struct rpc_data_struct* rpc_s, char* fmt, ...)
{
	LM_ERR("Not implemented\n");
	return -1;
}


/** Create a new member from formatting string and add it to a structure.
 */
static int rpc_struct_printf(struct rpc_data_struct* rpc_s, char* member_name, char* fmt, ...)
{
	va_list ap;
	char buf[PRINT_VALUE_BUF_LEN];
	int len;
	str _name,_body;
	rpc_ctx_t *ctx = rpc_s->ctx;

	if (!ctx) {
		LM_ERR("Invalid context\n");
		return -1;
	}

	va_start(ap, fmt);
	len=vsnprintf(buf, PRINT_VALUE_BUF_LEN, fmt, ap);
	va_end(ap);
	if ((len<0) || (len>PRINT_VALUE_BUF_LEN)){
		LM_ERR("buffer size exceeded [%d]\n", PRINT_VALUE_BUF_LEN);
		return -1;
	}

	_name.s = member_name;
	_name.len = strlen(member_name);
	_body.s = buf;
	_body.len = len;
	if (0!=xhttp_rpc_build_content(ctx, &_body, &_name)) return -1;

	return 0;
}


/** Returns the RPC capabilities supported by the xmlrpc driver.
 */
static rpc_capabilities_t rpc_capabilities(rpc_ctx_t* ctx)
{
	/* No support for async commands.
	 */
	return 0;
}


/** Returns a new "delayed reply" context.
 * Creates a new delayed reply context in shm and returns it.
 * @return 0 - not supported, already replied, or no more memory;
 *         !=0 pointer to the special delayed ctx.
 * Note1: one should use the returned ctx reply context to build a reply and
 *  when finished call rpc_delayed_ctx_close().
 * Note2: adding pieces to the reply in different processes is not supported.
 */
static struct rpc_delayed_ctx* rpc_delayed_ctx_new(rpc_ctx_t* ctx)
{
	return NULL;
}


/** Closes a "delayed reply" context and sends the reply.
 * If no reply has been sent the reply will be built and sent automatically.
 * See the notes from rpc_new_delayed_ctx()
 */
static void rpc_delayed_ctx_close(struct rpc_delayed_ctx* dctx)
{
	return;
}


static int mod_init(void)
{
	int i;

	/* bind the XHTTP API */
	if (xhttp_load_api(&xhttp_api) < 0) {
		LM_ERR("cannot bind to XHTTP API\n");
		return -1;
	}

	/* Check xhttp_rpc_buf_size param */
	if (buf_size == 0)
		buf_size = pkg_mem_size/3;

	/* Check xhttp_rpc_root param */
	for(i=0;i<xhttp_rpc_root.len;i++){
		if ( !isalnum(xhttp_rpc_root.s[i]) && xhttp_rpc_root.s[i]!='_') {
			LM_ERR("bad xhttp_rpc_root param [%.*s], char [%c] "
				"- use only alphanumerical chars\n",
				xhttp_rpc_root.len, xhttp_rpc_root.s,
				xhttp_rpc_root.s[i]);
			return -1;
		}
	}

	memset(&func_param, 0, sizeof(func_param));
	func_param.send = (rpc_send_f)rpc_send;
	func_param.fault = (rpc_fault_f)rpc_fault;
	func_param.add = (rpc_add_f)rpc_add;
	func_param.scan = (rpc_scan_f)rpc_scan;
	func_param.rpl_printf = (rpc_rpl_printf_f)rpc_rpl_printf;
	func_param.struct_add = (rpc_struct_add_f)rpc_struct_add;
	func_param.array_add = (rpc_array_add_f)rpc_array_add;
	func_param.struct_scan = (rpc_struct_scan_f)rpc_struct_scan;
	func_param.struct_printf = (rpc_struct_printf_f)rpc_struct_printf;
	func_param.capabilities = (rpc_capabilities_f)rpc_capabilities;
	func_param.delayed_ctx_new = (rpc_delayed_ctx_new_f)rpc_delayed_ctx_new;
	func_param.delayed_ctx_close =
		(rpc_delayed_ctx_close_f)rpc_delayed_ctx_close;

	return 0;
}

static int child_init(int rank)
{
	int i, j;
	int len;
	xhttp_rpc_mod_cmds_t *cmds;
	/* rpc_export_t *rpc_e; */

	if(rank==PROC_MAIN || rank==PROC_TCP_MAIN)
		return 0; /* do nothing for the main process */

	if (rank==PROC_INIT)
	{
		/* building a cache of rpc module commands */
		xhttp_rpc_mod_cmds =
			(xhttp_rpc_mod_cmds_t*)pkg_malloc(sizeof(xhttp_rpc_mod_cmds_t));
		if (xhttp_rpc_mod_cmds==NULL){
			PKG_MEM_ERROR;
			return -1;
		}
		xhttp_rpc_mod_cmds->rpc_e_index = 0;
		xhttp_rpc_mod_cmds->mod.s = NULL;
		xhttp_rpc_mod_cmds->mod.len = 0;
		xhttp_rpc_mod_cmds->size = 0;
		xhttp_rpc_mod_cmds_size = 1;
		cmds = xhttp_rpc_mod_cmds;
		for(i=0; i<rpc_sarray_crt_size; i++){
			len = strlen(rpc_sarray[i]->name);
			j = 0;
			while (j<len && rpc_sarray[i]->name[j]!='.')
				j++;
			if (j==len) {
				LM_DBG("dropping invalid command format [%.*s]\n",
						len, rpc_sarray[i]->name);
			} else {
				if (cmds->mod.len==0) {
					/* this is the first module */
					cmds->rpc_e_index = i;
					cmds->mod.s = (char*)&rpc_sarray[i]->name[0];
					cmds->mod.len = j;
					cmds->size++;
				} else if (cmds->mod.len==j &&
					strncmp(cmds->mod.s,
						(char*)&rpc_sarray[i]->name[0],
						j)==0){
					cmds->size++;
				} else {
					cmds = (xhttp_rpc_mod_cmds_t*)
						pkg_realloc(xhttp_rpc_mod_cmds,
							(xhttp_rpc_mod_cmds_size+1)*
							sizeof(xhttp_rpc_mod_cmds_t));
					if (cmds==NULL){
						LM_ERR("oom\n");
						return -1;
					}
					xhttp_rpc_mod_cmds = cmds;
					cmds = &xhttp_rpc_mod_cmds[xhttp_rpc_mod_cmds_size];
					cmds->rpc_e_index = i;
					cmds->mod.s = (char*)&rpc_sarray[i]->name[0];
					cmds->mod.len = j;
					xhttp_rpc_mod_cmds_size++;
					cmds->size = 1;
				}
			}
		}
		/*
		for(i=0; i<xhttp_rpc_mod_cmds_size; i++){
			for (j=0; j<xhttp_rpc_mod_cmds[i].size; j++){
				rpc_e = rpc_sarray[xhttp_rpc_mod_cmds[i].rpc_e_index+j];
				LM_DBG("[%p] => [%p]->[%.*s] [%p]->[%s]\n",
					rpc_e,
					xhttp_rpc_mod_cmds[i].mod.s,
					xhttp_rpc_mod_cmds[i].mod.len,
					xhttp_rpc_mod_cmds[i].mod.s,
					rpc_e->name,
					rpc_e->name);
			}
		}
		*/
	}

	full_version_len = strlen(full_version);
	ver_name_len = strlen(ver_name);
	return 0;
}


static int ki_xhttp_rpc_dispatch(sip_msg_t* msg)
{
	rpc_export_t* rpc_e;
	str arg = {NULL, 0};
	int ret = 0;
	int i;

	if(!IS_HTTP(msg)) {
		LM_DBG("Got non HTTP msg\n");
		return NONSIP_MSG_PASS;
	}

	/* Init xhttp_rpc context */
	if (ctx.reply.buf.s) LM_ERR("Unexpected buf value [%p][%d]\n",
				ctx.reply.buf.s, ctx.reply.buf.len);
	memset(&ctx, 0, sizeof(rpc_ctx_t));
	ctx.msg = msg;
	ctx.mod = ctx.cmd = -1;
	if (init_xhttp_rpc_reply(&ctx) < 0) goto send_reply;

	/* Extract arguments from url */
	if (0!=xhttp_rpc_parse_url(&msg->first_line.u.request.uri,
				&ctx.mod, &ctx.cmd, &arg)){
		rpc_fault(&ctx, 500, "Bad URL");
		goto send_reply;
	}

	if (arg.s) {
		if (arg.len) {
			/* Unescape args */
			ctx.arg.s = pkg_malloc((arg.len+1)*sizeof(char));
			if (ctx.arg.s==NULL){
				PKG_MEM_ERROR;
				rpc_fault(&ctx, 500, "Internal Server Error (oom)");
				goto send_reply;
			}
			for(i=0;i<arg.len;i++) if (arg.s[i]=='+') arg.s[i]=' ';
			if (0>un_escape(&arg, &ctx.arg)) {
				LM_ERR("unable to escape [%.*s]\n", arg.len, arg.s);
				rpc_fault(&ctx, 500, "Bad arg in URL");
				goto send_reply;
			}
			ctx.arg.s[ctx.arg.len] = '\0';
			ctx.arg.len++;
			ctx.arg2scan = ctx.arg;
		}
		ctx.arg_received = 1;
	} else {
		goto send_reply;
	}
	
	/*
	rpc_e=find_rpc_export((char*)rpc_sarray[xhttp_rpc_mod_cmds[ctx.mod].rpc_e_index+ctx.cmd]->name, 0);
	if ((rpc_e==NULL) || (rpc_e->function==NULL)){
		LM_ERR("Unable to find rpc command [%s]\n",
		rpc_sarray[xhttp_rpc_mod_cmds[ctx.mod].rpc_e_index+ctx.cmd]->name);
		rpc_fault(&ctx, 500, "Method not found");
		goto send_reply;
	}
	*/
	rpc_e=rpc_sarray[xhttp_rpc_mod_cmds[ctx.mod].rpc_e_index+ctx.cmd];
	rpc_e->function(&func_param, &ctx);

send_reply:
	if (!ctx.reply_sent) {
		ret = rpc_send(&ctx);
	}
	if (ret < 0) return -1;
	return 0;
}

static int xhttp_rpc_dispatch(sip_msg_t* msg, char* s1, char* s2)
{
	return ki_xhttp_rpc_dispatch(msg);
}

/**
 *
 */
/* clang-format off */
static sr_kemi_t sr_kemi_xhttp_rpc_exports[] = {
	{ str_init("xhttp_rpc"), str_init("dispatch"),
		SR_KEMIP_INT, ki_xhttp_rpc_dispatch,
		{ SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE,
			SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
	},

	{ {0, 0}, {0, 0}, 0, NULL, { 0, 0, 0, 0, 0, 0 } }
};
/* clang-format on */

/**
 *
 */
int mod_register(char *path, int *dlflags, void *p1, void *p2)
{
	sr_kemi_modules_add(sr_kemi_xhttp_rpc_exports);
	return 0;
}

/** @} */