/**
 * Copyright (C) 2009
 *
 * This file is part of Kamailio.org, 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
 */

/*!
 * @defgroup kex KEX :: Kamailio Extensions
 */

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

#include "../../core/sr_module.h"
#include "../../core/dprint.h"
#include "../../core/forward.h"
#include "../../core/flags.h"
#include "../../core/dset.h"
#include "../../core/mod_fix.h"
#include "../../core/kemi.h"
#include "../../core/parser/parse_uri.h"
#include "../../core/utils/sruid.h"

#include "flags.h"
#include "km_core.h"
#include "core_stats.h"
#include "pkg_stats.h"
#include "mod_stats.h"


MODULE_VERSION


/** parameters */

/** module functions */
int w_is_myself(struct sip_msg *msg, char *uri, str *s2);
int w_is_myhost(struct sip_msg *msg, char *uri, str *s2);
int w_setdebug(struct sip_msg *msg, char *level, str *s2);
int w_resetdebug(struct sip_msg *msg, char *uri, str *s2);

static int mod_init(void);
static int child_init(int rank);
static void destroy(void);


static sruid_t _kex_sruid;

static int pv_get_sruid_val(
		struct sip_msg *msg, pv_param_t *param, pv_value_t *res);

/* clang-format off */
static pv_export_t mod_pvs[] = {
	{{"sruid", sizeof("sruid") - 1}, PVT_OTHER, pv_get_sruid_val,
		0, 0, 0, 0, 0},
	{{0, 0}, 0, 0, 0, 0, 0, 0, 0}
};

static cmd_export_t cmds[] = {
	{"setsflag", (cmd_function)w_setsflag, 1, fixup_igp_null,
			fixup_free_igp_null, ANY_ROUTE},
	{"resetsflag", (cmd_function)w_resetsflag, 1, fixup_igp_null,
			fixup_free_igp_null, ANY_ROUTE},
	{"issflagset", (cmd_function)w_issflagset, 1, fixup_igp_null,
			fixup_free_igp_null, ANY_ROUTE},
	{"setbflag", (cmd_function)w_setbflag, 1, fixup_igp_null,
			fixup_free_igp_null, ANY_ROUTE},
	{"setbflag", (cmd_function)w_setbflag, 2, fixup_igp_igp,
			fixup_free_igp_igp, ANY_ROUTE},
	{"resetbflag", (cmd_function)w_resetbflag, 1, fixup_igp_null,
			fixup_free_igp_null, ANY_ROUTE},
	{"resetbflag", (cmd_function)w_resetbflag, 2, fixup_igp_igp,
			fixup_free_igp_igp, ANY_ROUTE},
	{"isbflagset", (cmd_function)w_isbflagset, 1, fixup_igp_null,
			fixup_free_igp_null, ANY_ROUTE},
	{"isbflagset", (cmd_function)w_isbflagset, 2, fixup_igp_igp,
		fixup_free_igp_igp, ANY_ROUTE},
	{"setdsturi", (cmd_function)w_setdsturi, 1, fixup_spve_null,
			fixup_free_spve_null, ANY_ROUTE},
	{"resetdsturi", (cmd_function)w_resetdsturi, 0, 0, 0, ANY_ROUTE},
	{"isdsturiset", (cmd_function)w_isdsturiset, 0, 0, 0, ANY_ROUTE},
	{"pv_printf", (cmd_function)w_pv_printf, 2, pv_printf_fixup, 0,
			ANY_ROUTE},
	{"avp_printf", (cmd_function)w_pv_printf, 2, pv_printf_fixup, 0,
			ANY_ROUTE},
	{"is_myself", (cmd_function)w_is_myself, 1, fixup_spve_null,
			fixup_free_spve_null, ANY_ROUTE},
	{"is_myhost", (cmd_function)w_is_myhost, 1, fixup_spve_null,
			fixup_free_spve_null, ANY_ROUTE},
	{"setdebug", (cmd_function)w_setdebug, 1, fixup_igp_null,
			fixup_free_igp_null, ANY_ROUTE},
	{"resetdebug", (cmd_function)w_resetdebug, 0, 0, 0, ANY_ROUTE},

	{0, 0, 0, 0, 0, 0}
};

static param_export_t params[] = {
	{0, 0, 0}
};

/** module exports */
struct module_exports exports = {
	"kex", DEFAULT_DLFLAGS,	/* dlopen flags */
	cmds, params,
	0,					/* exported RPC methods */
	mod_pvs,			/* exported pseudo-variables */
	0,					/* response handling function */
	mod_init,			/* module initialization function */
	child_init,			/* per-child init function */
	destroy
};
/* clang-format on */

/**
 * init module function
 */
static int mod_init(void)
{
	if(sruid_init(&_kex_sruid, '-', NULL, 0) < 0)
		return -1;
#ifdef STATISTICS
	if(register_core_stats() < 0)
		return -1;
#endif
	register_pkg_proc_stats();
	pkg_proc_stats_init_rpc();

	/* per module memory debugging RPC command */
	mod_stats_init_rpc();

	return 0;
}

/**
 *
 */
static int child_init(int rank)
{
	LM_DBG("rank is (%d)\n", rank);
	if(sruid_init(&_kex_sruid, '-', NULL, 0) < 0)
		return -1;
	if(rank == PROC_INIT)
		return pkg_proc_stats_init();
	return pkg_proc_stats_myinit(rank);
}


/**
 * destroy function
 */
static void destroy(void)
{
	pkg_proc_stats_destroy();
	return;
}


/**
 *
 */
int w_is_myself(struct sip_msg *msg, char *uri, str *s2)
{
	int ret;
	str suri;
	struct sip_uri puri;

	if(fixup_get_svalue(msg, (gparam_p)uri, &suri) != 0) {
		LM_ERR("cannot get the URI parameter\n");
		return -1;
	}
	if(suri.len > 4
			&& (strncmp(suri.s, "sip:", 4) == 0
					|| strncmp(suri.s, "sips:", 5) == 0)) {
		if(parse_uri(suri.s, suri.len, &puri) != 0) {
			LM_ERR("failed to parse uri [%.*s]\n", suri.len, suri.s);
			return -1;
		}
		ret = check_self(&puri.host, (puri.port.s) ? puri.port_no : 0,
				(puri.transport_val.s) ? puri.proto : 0);
	} else {
		ret = check_self(&suri, 0, 0);
	}
	if(ret != 1)
		return -1;
	return 1;
}

/**
 *
 */
int w_is_myhost(struct sip_msg *msg, char *uri, str *s2)
{
	int ret;
	str suri;
	struct sip_uri puri;

	if(fixup_get_svalue(msg, (gparam_p)uri, &suri) != 0) {
		LM_ERR("cannot get the URI parameter\n");
		return -1;
	}
	if(suri.len > 4
			&& (strncmp(suri.s, "sip:", 4) == 0
					|| strncmp(suri.s, "sips:", 5) == 0)) {
		if(parse_uri(suri.s, suri.len, &puri) != 0) {
			LM_ERR("failed to parse uri [%.*s]\n", suri.len, suri.s);
			return -1;
		}
		ret = check_self(&puri.host, 0, 0);
	} else {
		ret = check_self(&suri, 0, 0);
	}
	if(ret != 1)
		return -1;
	return 1;
}

int w_setdebug(struct sip_msg *msg, char *level, str *s2)
{
	int lval = 0;
	if(fixup_get_ivalue(msg, (gparam_p)level, &lval) != 0) {
		LM_ERR("no debug level value\n");
		return -1;
	}
	set_local_debug_level(lval);
	return 1;
}

int w_resetdebug(struct sip_msg *msg, char *uri, str *s2)
{
	reset_local_debug_level();
	return 1;
}


static int pv_get_sruid_val(
		struct sip_msg *msg, pv_param_t *param, pv_value_t *res)
{
	if(res == NULL)
		return -1;
	if(sruid_next(&_kex_sruid) < 0)
		return pv_get_null(msg, param, res);
	return pv_get_strval(msg, param, res, &_kex_sruid.uid);
}

/**
 *
 */
static int ki_kex_setdebug(sip_msg_t *msg, int lval)
{
	set_local_debug_level(lval);
	return 1;
}

/**
 *
 */
static int ki_kex_resetdebug(sip_msg_t *msg)
{
	reset_local_debug_level();
	return 1;
}

/**
 *
 */
/* clang-format off */
static sr_kemi_t sr_kemi_kex_exports[] = {
	{ str_init("kex"), str_init("setdebug"),
		SR_KEMIP_INT, ki_kex_setdebug,
		{ SR_KEMIP_INT, SR_KEMIP_NONE, SR_KEMIP_NONE,
			SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
	},
	{ str_init("kex"), str_init("resetdebug"),
		SR_KEMIP_INT, ki_kex_resetdebug,
		{ 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_kex_exports);
	return 0;
}
