/*
 * SIP Session Timer (sst) module - support for tracking dialogs and
 * SIP Session Timers.
 *
 * Copyright (C) 2006 SOMA Networks, INC.
 * Written by: Ron Winacott (karwin)
 *
 * This file is part of Kamailio, a free SIP server.
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 *
 * 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 sst/sst.c
 *  \brief SST :: Core
 *  \ingroup sst
 *  Modules: \ref sst
 */

/*! \defgroup sst SIP Session Timers module
 *	Implementation of SIP Session Timers - a way to make sure a SIP dialog is alive
 *	and force hangup if a UA involved in a dialog stops responding.
 *
 *  - \ref SST_support
 */

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

#include "../../core/kemi.h"

#include "../../modules/sl/sl.h"
#include "sst_handlers.h" /* also includes sr_module.h needed by
                             handlers */
#ifdef STATISTICS
#include "../../core/counters.h"
#endif

MODULE_VERSION

static int mod_init(void);


/** SL API structure */
sl_api_t slb;

/*
 * statistic variables
 */
int sst_enable_stats = 1;
stat_var *expired_sst = 0;

/*!
 * The name of the AVP the dialog module will use to hold the timeout
 * value so we can set the AVP in the dialog callbacks so the dialog
 * code will set the dialog lifetime when it returns from the INVITE
 * and IN_ROUTE callbacks.
 */
pv_spec_t timeout_avp;
static char *timeout_spec = 0; /*!< Place holder for the passed in name */

/*!
 * The default or script parameter for the requested MIN-SE: value for
 * this proxy. (in seconds) If the passed in value is 0, then this
 * proxy will except any value from the UAC as its min-SE value. If
 * the value is NOT set then the default will be asserted.
 */
unsigned int sst_minSE = 90;

/*!
 * Should the PROXY (us) reject (with a 422 reply) and SE < sst_minSE
 * requests is it can. Default is YES.
 */
unsigned int sst_reject = 1;

/*! The sst message flag value */
static int sst_flag = -1;


/*
 * Binding to the dialog module
 */
struct dlg_binds dialog_st;
struct dlg_binds *dlg_binds = &dialog_st;

/* clang-format off */
/*
 * Script commands we export.
 */
static cmd_export_t cmds[] = {
	{"sstCheckMin", (cmd_function)sst_check_min, 1, 0, 0, REQUEST_ROUTE | ONREPLY_ROUTE},
	{0, 0, 0, 0, 0, 0}
};

/*
 * Script parameters
 */
static param_export_t mod_params[] = {
	{"enable_stats", PARAM_INT, &sst_enable_stats},
	{"min_se", PARAM_INT, &sst_minSE},
	{"timeout_avp", PARAM_STRING, &timeout_spec},
	{"reject_to_small", PARAM_INT, &sst_reject},
	{"sst_flag", PARAM_INT, &sst_flag},
	{0, 0, 0}
};

#ifdef STATISTICS
/*
 * Export the statistics we have
 */
static stat_export_t mod_stats[] = {
	{"expired_sst", 0, &expired_sst},
	{0, 0, 0}
};
#endif /* STATISTICS */

struct module_exports exports = {
	"sst",           /* module's name */
	DEFAULT_DLFLAGS, /* dlopen flags */
	cmds,            /* exported functions */
	mod_params,      /* exported parameters */
	0,               /* exported rpc functions */
	0,               /* exported pseudo-variables */
	0,               /* response handling function */
	mod_init,        /* module init function */
	0,               /* per-child init function */
	0                /* module destroy function */
};
/* clang-format on */

/**
 * The initialization function, called when the module is loaded by
 * the script. This function is called only once.
 *
 * Bind to the dialog module and set up the callbacks. Also initialize
 * the shared memory to store our internal information in.
 *
 * @return 0 to continue to load the Kamailio, -1 to stop the loading
 * and abort Kamailio.
 */
static int mod_init(void)
{
	str s;

#ifdef STATISTICS
	/* register statistics */
	if(sst_enable_stats != 0) {
		if(register_module_stats("sst", mod_stats) != 0) {
			LM_ERR("failed to register core statistics\n");
			return -1;
		}
	}
#endif


	if(sst_flag == -1) {
		LM_ERR("no sst flag set!!\n");
		return -1;
	} else if(sst_flag > MAX_FLAG) {
		LM_ERR("invalid sst flag %d!!\n", sst_flag);
		return -1;
	}

	if(timeout_spec != NULL) {
		LM_DBG("Dialog AVP is %s", timeout_spec);
		s.s = timeout_spec;
		s.len = strlen(s.s);
		if(pv_parse_spec(&s, &timeout_avp) == 0
				&& (timeout_avp.type != PVT_AVP)) {
			LM_ERR("malformed or non AVP timeout AVP definition in '%s'\n",
					timeout_spec);
			return -1;
		}
	}

	/* bind the SL API */
	if(sl_load_api(&slb) != 0) {
		LM_ERR("cannot bind to SL API\n");
		return -1;
	}

	/* Init the handlers */
	sst_handler_init(
			(timeout_spec ? &timeout_avp : 0), sst_minSE, sst_flag, sst_reject);

	/* Register the main (static) dialog call back. */
	if(load_dlg_api(&dialog_st) != 0) {
		LM_ERR("failed to load dialog hooks");
		return (-1);
	}

	/* Load dialog hooks */
	dialog_st.register_dlgcb(
			NULL, DLGCB_CREATED, sst_dialog_created_CB, NULL, NULL);

	return 0;
}

/**
 *
 */
/* clang-format off */
static sr_kemi_t sr_kemi_sst_exports[] = {
	{ str_init("sst"), str_init("sst_check_min"),
		SR_KEMIP_INT, ki_sst_check_min,
		{ SR_KEMIP_INT, 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_sst_exports);
	return 0;
}
