/*
 *
 * Copyright (C) 2001-2003 FhG Fokus
 *
 * 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 Kamailio core :: Config file actions
 * \ingroup core
 * Module: \ref core
 */


#include "comp_defs.h"

#include "action.h"
#include "config.h"
#include "error.h"
#include "dprint.h"
#include "proxy.h"
#include "forward.h"
#include "udp_server.h"
#include "route.h"
#include "parser/msg_parser.h"
#include "parser/parse_uri.h"
#include "ut.h"
#include "lvalue.h"
#include "sr_module.h"
#include "select_buf.h"
#include "mem/mem.h"
#include "globals.h"
#include "dset.h"
#include "onsend.h"
#include "fmsg.h"
#include "resolve.h"
#ifdef USE_TCP
#include "tcp_server.h"
#endif
#ifdef USE_SCTP
#include "sctp_core.h"
#endif
#include "switch.h"
#include "events.h"
#include "cfg/cfg_struct.h"

#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>


#ifdef DEBUG_DMALLOC
#include <dmalloc.h>
#endif

int _last_returned_code = 0;

/* current action executed from config file */
static cfg_action_t *_cfg_crt_action = 0;

/*!< maximum number of recursive calls for blocks of actions */
static unsigned int max_recursive_level = 256;

void set_max_recursive_level(unsigned int lev)
{
	max_recursive_level = lev;
}

/* return current action executed from config file */
cfg_action_t *get_cfg_crt_action(void)
{
	return _cfg_crt_action;
}

/* return line in config for current executed action */
int get_cfg_crt_line(void)
{
	if(_cfg_crt_action == 0)
		return 0;
	return _cfg_crt_action->cline;
}

/* return name of config for current executed action */
char *get_cfg_crt_file_name(void)
{
	if(_cfg_crt_action == 0)
		return 0;
	return _cfg_crt_action->cfile;
}

/* return name of routing block for current executed action */
char *get_cfg_crt_route_name(void)
{
	if(_cfg_crt_action == 0)
		return 0;
	return _cfg_crt_action->rname;
}

/* handle the exit code of a module function call.
 * (used internally in do_action())
 * @param h - script handle (h->last_retcode and h->run_flags will be set).
 * @param ret - module function (v0 or v2) retcode
 * Side-effects: sets _last_returned_code
 */
#define MODF_HANDLE_RETCODE(h, ret)                             \
	do {                                                        \
		/* if (unlikely((ret)==0)) (h)->run_flags|=EXIT_R_F; */ \
		(h)->run_flags |= EXIT_R_F & (((ret) != 0) - 1);        \
		(h)->last_retcode = (ret);                              \
		_last_returned_code = (h)->last_retcode;                \
	} while(0)


/* frees parameters converted using MODF_RVE_PARAM_CONVERT() from dst.
 * (used internally in do_action())
 * Assumes src is unchanged.
 * Side-effects: clobbers i (int).
 */
#define MODF_RVE_PARAM_FREE(cmd, src, dst)                                \
	for(i = 0; i < (dst)[1].u.number; i++) {                              \
		if((src)[i + 2].type == RVE_ST && (dst)[i + 2].u.data) {          \
			if((dst)[i + 2].type == RVE_FREE_FIXUP_ST) {                  \
				/* call free_fixup (which should restore the original
					   string) */     \
				(void)call_fixup(                                         \
						(cmd)->free_fixup, &(dst)[i + 2].u.data, i + 1);  \
			} else if((dst)[i + 2].type == FPARAM_DYN_ST) {               \
				/* completely frees fparam and restore original string */ \
				fparam_free_restore(&(dst)[i + 2].u.data);                \
			}                                                             \
			/* free allocated string */                                   \
			pkg_free((dst)[i + 2].u.data);                                \
			(dst)[i + 2].u.data = 0;                                      \
		}                                                                 \
	}


/* fills dst from src, converting RVE_ST params to STRING_ST.
 * (used internally in do_action())
 * @param src - source action_u_t array, as in the action structure
 * @param dst - destination action_u_t array, will be filled from src.
 * WARNING: dst must be cleaned when done, use MODULE_RVE_PARAM_FREE()
 * Side-effects: clobbers i (int), s (str), rv (rvalue*), might jump to error.
 */
#define MODF_RVE_PARAM_CONVERT(h, msg, cmd, src, dst)                        \
	do {                                                                     \
		(dst)[1] = (src)[1];                                                 \
		for(i = 0; i < (src)[1].u.number; i++) {                             \
			if((src)[2 + i].type == RVE_ST) {                                \
				rv = rval_expr_eval((h), (msg), (src)[i + 2].u.data);        \
				if(unlikely(rv == 0                                          \
							|| rval_get_str((h), (msg), &s, rv, 0) < 0)) {   \
					rval_destroy(rv);                                        \
					ERR("failed to convert RVE to string\n");                \
					(dst)[1].u.number = i;                                   \
					MODF_RVE_PARAM_FREE(cmd, src, dst);                      \
					goto error;                                              \
				}                                                            \
				(dst)[i + 2].type = STRING_RVE_ST;                           \
				(dst)[i + 2].u.string = s.s;                                 \
				(dst)[i + 2].u.str.len = s.len;                              \
				rval_destroy(rv);                                            \
				if((cmd)->fixup) {                                           \
					if((cmd)->free_fixup) {                                  \
						if(likely(call_fixup((cmd)->fixup,                   \
										  &(dst)[i + 2].u.data, i + 1)       \
								   >= 0)) {                                  \
							/* success => mark it for calling free fixup */  \
							if(likely((dst)[i + 2].u.data != s.s))           \
								(dst)[i + 2].type = RVE_FREE_FIXUP_ST;       \
						} else {                                             \
							/* error calling fixup => mark conv. parameter \
							   and return error */ \
							(dst)[1].u.number = i;                           \
							ERR("runtime fixup failed for %s param %d\n",    \
									(cmd)->name, i + 1);                     \
							MODF_RVE_PARAM_FREE(cmd, src, dst);              \
							goto error;                                      \
						}                                                    \
					} else if((cmd)->fixup_flags & FIXUP_F_FPARAM_RVE) {     \
						if(likely(call_fixup((cmd)->fixup,                   \
										  &(dst)[i + 2].u.data, i + 1)       \
								   >= 0)) {                                  \
							if((dst)[i + 2].u.data != s.s)                   \
								(dst)[i + 2].type = FPARAM_DYN_ST;           \
						} else {                                             \
							/* error calling fixup => mark conv. parameter \
							   and return error */ \
							(dst)[1].u.number = i;                           \
							ERR("runtime fixup failed for %s param %d\n",    \
									(cmd)->name, i + 1);                     \
							MODF_RVE_PARAM_FREE(cmd, src, dst);              \
							goto error;                                      \
						}                                                    \
					}                                                        \
				}                                                            \
			} else                                                           \
				(dst)[i + 2] = (src)[i + 2];                                 \
		}                                                                    \
	} while(0)


/* call a module function with normal STRING_ST params.
 * (used internally in do_action())
 * @param f_type - cmd_function type
 * @param h
 * @param msg
 * @param src - source action_u_t array (e.g. action->val)
 * @param params... - variable list of parameters, passed to the module
 *               function
 * Side-effects: sets ret, clobbers i (int), s (str), rv (rvalue*), cmd,
 *               might jump to error.
 *
 */
#ifdef __SUNPRO_C
#define MODF_CALL(f_type, h, msg, src, ...)                \
	do {                                                   \
		cmd = (src)[0].u.data;                             \
		ret = ((f_type)cmd->function)((msg), __VA_ARGS__); \
		MODF_HANDLE_RETCODE(h, ret);                       \
	} while(0)
#else /* ! __SUNPRO_C  (gcc, icc a.s.o) */
#define MODF_CALL(f_type, h, msg, src, params...)       \
	do {                                                \
		cmd = (src)[0].u.data;                          \
		ret = ((f_type)cmd->function)((msg), ##params); \
		MODF_HANDLE_RETCODE(h, ret);                    \
	} while(0)
#endif /* __SUNPRO_C */


/* call a module function with possible RVE params.
 * (used internally in do_action())
 * @param f_type - cmd_function type
 * @param h
 * @param msg
 * @param src - source action_u_t array (e.g. action->val)
 * @param dst - temporary action_u_t array used for conversions. It can be
 *              used for the function parameters. Its contents is not
 *              valid after the call.
 * @param params... - variable list of parameters, passed to the module
 *               function
 * Side-effects: sets ret, clobbers i (int), s (str), rv (rvalue*), f, dst,
 *               might jump to error.
 *
 */
#ifdef __SUNPRO_C
#define MODF_RVE_CALL(f_type, h, msg, src, dst, ...)       \
	do {                                                   \
		cmd = (src)[0].u.data;                             \
		MODF_RVE_PARAM_CONVERT(h, msg, cmd, src, dst);     \
		ret = ((f_type)cmd->function)((msg), __VA_ARGS__); \
		MODF_HANDLE_RETCODE(h, ret);                       \
		/* free strings allocated by us or fixups */       \
		MODF_RVE_PARAM_FREE(cmd, src, dst);                \
	} while(0)
#else /* ! __SUNPRO_C  (gcc, icc a.s.o) */
#define MODF_RVE_CALL(f_type, h, msg, src, dst, params...) \
	do {                                                   \
		cmd = (src)[0].u.data;                             \
		MODF_RVE_PARAM_CONVERT(h, msg, cmd, src, dst);     \
		ret = ((f_type)cmd->function)((msg), ##params);    \
		MODF_HANDLE_RETCODE(h, ret);                       \
		/* free strings allocated by us or fixups */       \
		MODF_RVE_PARAM_FREE(cmd, src, dst);                \
	} while(0)
#endif /* __SUNPRO_C */


/* ret= 0! if action -> end of list(e.g DROP),
      > 0 to continue processing next actions
   and <0 on error */
int do_action(struct run_act_ctx *h, struct action *a, struct sip_msg *msg)
{
	int ret;
	long li;
	long v;
	struct dest_info dst;
	char *tmp;
	char *new_uri, *end, *crt;
	ksr_cmd_export_t *cmd;
	int len;
	int user;
	struct sip_uri uri, next_hop;
	struct sip_uri *u;
	unsigned short port;
	str *dst_host;
	int i, flags;
	avp_t *avp;
	struct search_state st;
	struct switch_cond_table *sct;
	struct switch_jmp_table *sjt;
	struct rval_expr *rve;
	struct match_cond_table *mct;
	struct rvalue *rv;
	struct rvalue *rv1;
	struct rval_cache c1;
	str s;
	void *srevp[2];
	sr_event_param_t evp = {0};

	/* temporary storage space for a struct action.val[] working copy
	 (needed to transform RVE intro STRING before calling module
	   functions). [0] is not used (corresp. to the module export pointer),
	   [1] contains the number of params, and [2..] the param values.
	   We need [1], because some fixup function use it
	  (see fixup_get_param_count()).  */
	static action_u_t mod_f_params[MAX_ACTIONS];

	/* reset the value of error to E_UNSPEC so avoid unknowledgable
	   functions to return with error (status<0) and not setting it
	   leaving there previous error; cache the previous value though
	   for functions which want to process it */
	prev_ser_error = ser_error;
	ser_error = E_UNSPEC;

	/* hook for every executed action (in use by cfg debugger) */
	if(unlikely(sr_event_enabled(SREV_CFG_RUN_ACTION))) {
		srevp[0] = (void *)a;
		srevp[1] = (void *)msg;
		evp.data = (void *)srevp;
		sr_event_exec(SREV_CFG_RUN_ACTION, &evp);
	}

	ret = E_BUG;
	switch((unsigned char)a->type) {
		case DROP_T:
			switch(a->val[0].type) {
				case NUMBER_ST:
					ret = (int)a->val[0].u.number;
					break;
				case RVE_ST:
					rve = (struct rval_expr *)a->val[0].u.data;
					if(rval_expr_eval_long(h, msg, &li, rve) < 0) {
						LM_WARN("failed to eval int expression\n");
					}
					ret = (int)li;
					break;
				case RETCODE_ST:
					ret = h->last_retcode;
					break;
				default:
					BUG("unexpected subtype %d in DROP_T\n", a->val[0].type);
					ret = 0;
					goto error;
			}
			h->run_flags |= (unsigned int)a->val[1].u.number;
			if(unlikely((ret == 0) && (h->run_flags & RETURN_R_F))) {
				h->run_flags |= EXIT_R_F;
			}
			break;
		case FORWARD_T:
#ifdef USE_TCP
		case FORWARD_TCP_T:
#endif
#ifdef USE_TLS
		case FORWARD_TLS_T:
#endif
#ifdef USE_SCTP
		case FORWARD_SCTP_T:
#endif
		case FORWARD_UDP_T:
			/* init dst */
			init_dest_info(&dst);
			if(a->type == FORWARD_UDP_T)
				dst.proto = PROTO_UDP;
#ifdef USE_TCP
			else if(a->type == FORWARD_TCP_T) {
				dst.proto = PROTO_TCP;
				if(msg->msg_flags & FL_USE_OTCPID) {
					dst.id = msg->otcpid;
				}
			}
#endif
#ifdef USE_TLS
			else if(a->type == FORWARD_TLS_T) {
				dst.proto = PROTO_TLS;
				if(msg->msg_flags & FL_USE_OTCPID) {
					dst.id = msg->otcpid;
				}
			}
#endif
#ifdef USE_SCTP
			else if(a->type == FORWARD_SCTP_T)
				dst.proto = PROTO_SCTP;
#endif
			else
				dst.proto = PROTO_NONE;
			if(a->val[0].type == URIHOST_ST) {
				/*parse uri*/

				if(msg->dst_uri.len) {
					ret = parse_uri(
							msg->dst_uri.s, msg->dst_uri.len, &next_hop);
					u = &next_hop;
				} else {
					ret = parse_sip_msg_uri(msg);
					u = &msg->parsed_uri;
				}

				if(ret < 0) {
					LM_ERR("forward: bad_uri dropping packet\n");
					goto error;
				}

				switch(a->val[1].type) {
					case URIPORT_ST:
						port = u->port_no;
						break;
					case NUMBER_ST:
						port = a->val[1].u.number;
						break;
					default:
						LM_CRIT("bad forward 2nd param type (%d)\n",
								a->val[1].type);
						ret = E_UNSPEC;
						goto error_fwd_uri;
				}
				if(dst.proto == PROTO_NONE) { /* only if proto not set get it
											 from the uri */
					switch(u->proto) {
						case PROTO_NONE:
							/*dst.proto=PROTO_UDP; */
							/* no proto, try to get it from the dns */
							break;
						case PROTO_UDP:
#ifdef USE_TCP
						case PROTO_TCP:
						case PROTO_WS:
#endif
#ifdef USE_TLS
						case PROTO_TLS:
						case PROTO_WSS:
#endif
#ifdef USE_SCTP
						case PROTO_SCTP:
#endif
							dst.proto = u->proto;
							break;
						default:
							LM_ERR("forward: bad uri transport %d\n", u->proto);
							ret = E_BAD_PROTO;
							goto error_fwd_uri;
					}
#ifdef USE_TLS
					if(u->type == SIPS_URI_T) {
						if(u->proto == PROTO_UDP) {
							LM_ERR("forward: secure uri incompatible with "
								   "transport %d\n",
									u->proto);
							ret = E_BAD_PROTO;
							goto error_fwd_uri;
						} else if(u->proto != PROTO_WSS)
							dst.proto = PROTO_TLS;
						else
							dst.proto = PROTO_WSS;
					}
#endif
				}

#ifdef HONOR_MADDR
				if(u->maddr_val.s && u->maddr_val.len)
					dst_host = &u->maddr_val;
				else
#endif
					dst_host = &u->host;
#ifdef USE_COMP
				dst.comp = u->comp;
#endif
				ret = forward_request(msg, dst_host, port, &dst);
				if(ret >= 0) {
					ret = 1;
				}
			} else if((a->val[0].type == PROXY_ST)
					  && (a->val[1].type == NUMBER_ST)) {
				if(dst.proto == PROTO_NONE)
					dst.proto = msg->rcv.proto;
				proxy2su(&dst.to, (struct proxy_l *)a->val[0].u.data);
				ret = forward_request(msg, 0, 0, &dst);
				if(ret >= 0) {
					ret = 1;
					proxy_mark((struct proxy_l *)a->val[0].u.data, ret);
				} else if(ser_error != E_OK) {
					proxy_mark((struct proxy_l *)a->val[0].u.data, ret);
				}
			} else {
				LM_CRIT("bad forward() types %d, %d\n", a->val[0].type,
						a->val[1].type);
				ret = E_BUG;
				goto error;
			}
			break;
		case LOG_T:
			if((a->val[0].type != NUMBER_ST) | (a->val[1].type != STRING_ST)) {
				LM_CRIT("bad log() types %d, %d\n", a->val[0].type,
						a->val[1].type);
				ret = E_BUG;
				goto error;
			}
			LOG_FN(DEFAULT_FACILITY, a->val[0].u.number, "<script>: ", "%s",
					a->val[1].u.string);
			ret = 1;
			break;

		/* jku -- introduce a new branch */
		case APPEND_BRANCH_T:
			if(unlikely(a->val[0].type != STR_ST)) {
				LM_CRIT("bad append_branch_t %d\n", a->val[0].type);
				ret = E_BUG;
				goto error;
			}
			getbflagsval(0, (flag_t *)&flags);
			ret = append_branch(msg, &a->val[0].u.str, &msg->dst_uri,
					&msg->path_vec, a->val[1].u.number, (flag_t)flags,
					msg->force_send_socket, 0, 0, 0, 0);
			/* if the uri is the ruri and q was also not changed, mark
			   ruri as consumed, to avoid having an identical branch */
			if((a->val[0].u.str.s == 0 || a->val[0].u.str.len == 0)
					&& a->val[1].u.number == Q_UNSPECIFIED)
				ruri_mark_consumed();
			break;

		/* remove last branch */
		case REMOVE_BRANCH_T:
			if(a->val[0].type != NUMBER_ST) {
				ret = drop_sip_branch(0) ? -1 : 1;
			} else {
				ret = drop_sip_branch(a->val[0].u.number) ? -1 : 1;
			}
			break;

		/* remove all branches */
		case CLEAR_BRANCHES_T:
			clear_branches();
			ret = 1;
			break;

		/* jku begin: is_length_greater_than */
		case LEN_GT_T:
			if(a->val[0].type != NUMBER_ST) {
				LM_CRIT("bad len_gt type %d\n", a->val[0].type);
				ret = E_BUG;
				goto error;
			}
			/* LM_DBG("message length %d, max %d\n",
				msg->len, a->val[0].u.number ); */
			ret = msg->len >= a->val[0].u.number ? 1 : -1;
			break;
			/* jku end: is_length_greater_than */

			/* jku - begin : flag processing */

		case SETFLAG_T:
			if(a->val[0].type != NUMBER_ST) {
				LM_CRIT("bad setflag() type %d\n", a->val[0].type);
				ret = E_BUG;
				goto error;
			}
			if(!flag_in_range(a->val[0].u.number)) {
				ret = E_CFG;
				goto error;
			}
			setflag(msg, a->val[0].u.number);
			ret = 1;
			break;

		case RESETFLAG_T:
			if(a->val[0].type != NUMBER_ST) {
				LM_CRIT("bad resetflag() type %d\n", a->val[0].type);
				ret = E_BUG;
				goto error;
			}
			if(!flag_in_range(a->val[0].u.number)) {
				ret = E_CFG;
				goto error;
			}
			resetflag(msg, a->val[0].u.number);
			ret = 1;
			break;

		case ISFLAGSET_T:
			if(a->val[0].type != NUMBER_ST) {
				LM_CRIT("bad isflagset() type %d\n", a->val[0].type);
				ret = E_BUG;
				goto error;
			}
			if(!flag_in_range(a->val[0].u.number)) {
				ret = E_CFG;
				goto error;
			}
			ret = isflagset(msg, a->val[0].u.number);
			break;
			/* jku - end : flag processing */

		case AVPFLAG_OPER_T:
			ret = 0;
			if((a->val[0].u.attr->type & AVP_INDEX_ALL) == AVP_INDEX_ALL
					|| (a->val[0].u.attr->type & AVP_NAME_RE) != 0) {
				for(avp = search_first_avp(a->val[0].u.attr->type,
							a->val[0].u.attr->name, NULL, &st);
						avp; avp = search_next_avp(&st, NULL)) {
					switch(a->val[2].u.number) {
						/* oper: 0..reset, 1..set, -1..no change */
						case 0:
							avp->flags &= ~(avp_flags_t)a->val[1].u.number;
							break;
						case 1:
							avp->flags |= (avp_flags_t)a->val[1].u.number;
							break;
						default:;
					}
					ret = ret
						  || ((avp->flags & (avp_flags_t)a->val[1].u.number)
								  != 0);
				}
			} else {
				avp = search_avp_by_index(a->val[0].u.attr->type,
						a->val[0].u.attr->name, NULL, a->val[0].u.attr->index);
				if(avp) {
					switch(a->val[2].u.number) {
						/* oper: 0..reset, 1..set, -1..no change */
						case 0:
							avp->flags &= ~(avp_flags_t)a->val[1].u.number;
							break;
						case 1:
							avp->flags |= (avp_flags_t)a->val[1].u.number;
							break;
						default:;
					}
					ret = (avp->flags & (avp_flags_t)a->val[1].u.number) != 0;
				}
			}
			if(ret == 0)
				ret = -1;
			break;
		case ERROR_T:
			if((a->val[0].type != STRING_ST) | (a->val[1].type != STRING_ST)) {
				LM_CRIT("bad error() types %d, %d\n", a->val[0].type,
						a->val[1].type);
				ret = E_BUG;
				goto error;
			}
			LM_NOTICE("error(\"%s\", \"%s\") "
					  "not implemented yet\n",
					a->val[0].u.string, a->val[1].u.string);
			ret = 1;
			break;
		case ROUTE_T:
			if(likely(a->val[0].type == NUMBER_ST))
				i = a->val[0].u.number;
			else if(a->val[0].type == RVE_ST) {
				rv = rval_expr_eval(h, msg, a->val[0].u.data);
				rval_cache_init(&c1);
				if(unlikely(rv == 0
							|| rval_get_tmp_str(h, msg, &s, rv, 0, &c1) < 0)) {
					rval_destroy(rv);
					rval_cache_clean(&c1);
					ERR("failed to convert RVE to string\n");
					ret = E_UNSPEC;
					goto error;
				}
				i = route_lookup(&main_rt, s.s);
				if(unlikely(i < 0)) {
					ERR("route \"%s\" not found at %s:%d\n", s.s,
							(a->cfile) ? a->cfile : "line", a->cline);
					rval_destroy(rv);
					rval_cache_clean(&c1);
					s.s = 0;
					ret = E_SCRIPT;
					goto error;
				}
				rval_destroy(rv);
				rval_cache_clean(&c1);
				s.s = 0;
			} else {
				LM_CRIT("bad route() type %d\n", a->val[0].type);
				ret = E_BUG;
				goto error;
			}
			if(unlikely((i >= main_rt.idx) || (i < 0))) {
				LM_ERR("invalid routing table number in"
					   "route(%lu) at %s:%d\n",
						a->val[0].u.number, (a->cfile) ? a->cfile : "line",
						a->cline);
				ret = E_CFG;
				goto error;
			}
			/*ret=((ret=run_actions(rlist[a->val[0].u.number],msg))<0)?ret:1;*/
			ret = run_actions(h, main_rt.rlist[i], msg);
			h->last_retcode = ret;
			_last_returned_code = h->last_retcode;
			h->run_flags &=
					~(RETURN_R_F | BREAK_R_F); /* absorb return & break */
			break;
		case EXEC_T:
			if(a->val[0].type != STRING_ST) {
				LM_CRIT("bad exec() type %d\n", a->val[0].type);
				ret = E_BUG;
				goto error;
			}
			LM_NOTICE("exec(\"%s\") not fully implemented,"
					  " using dumb version...\n",
					a->val[0].u.string);
			ret = system(a->val[0].u.string);
			if(ret != 0) {
				LM_NOTICE("exec() returned %d\n", ret);
			}
			ret = 1;
			break;
		case REVERT_URI_T:
			if(msg->new_uri.s) {
				pkg_free(msg->new_uri.s);
				msg->new_uri.len = 0;
				msg->new_uri.s = 0;
				msg->parsed_uri_ok = 0; /* invalidate current parsed uri*/
				ruri_mark_new();		/* available for forking */
			};
			ret = 1;
			break;
		case SET_HOST_T:
		case SET_HOSTPORT_T:
		case SET_HOSTPORTTRANS_T:
		case SET_HOSTALL_T:
		case SET_USER_T:
		case SET_USERPASS_T:
		case SET_PORT_T:
		case SET_URI_T:
		case PREFIX_T:
		case STRIP_T:
		case STRIP_TAIL_T:
		case SET_USERPHONE_T:
			user = 0;
			if(a->type == STRIP_T || a->type == STRIP_TAIL_T) {
				if(a->val[0].type != NUMBER_ST) {
					LM_CRIT("bad set*() type %d\n", a->val[0].type);
					ret = E_BUG;
					goto error;
				}
			} else if(a->type != SET_USERPHONE_T) {
				if(a->val[0].type != STRING_ST) {
					LM_CRIT("bad set*() type %d\n", a->val[0].type);
					ret = E_BUG;
					goto error;
				}
			}
			if(a->type == SET_URI_T) {
				if(msg->new_uri.s) {
					pkg_free(msg->new_uri.s);
					msg->new_uri.len = 0;
				}
				msg->parsed_uri_ok = 0;
				len = strlen(a->val[0].u.string);
				msg->new_uri.s = pkg_malloc(len + 1);
				if(msg->new_uri.s == 0) {
					PKG_MEM_ERROR;
					ret = E_OUT_OF_MEM;
					goto error;
				}
				memcpy(msg->new_uri.s, a->val[0].u.string, len);
				msg->new_uri.s[len] = 0;
				msg->new_uri.len = len;
				ruri_mark_new(); /* available for forking */

				ret = 1;
				break;
			}
			if(msg->parsed_uri_ok == 0) {
				if(msg->new_uri.s) {
					tmp = msg->new_uri.s;
					len = msg->new_uri.len;
				} else {
					tmp = msg->first_line.u.request.uri.s;
					len = msg->first_line.u.request.uri.len;
				}
				if(parse_uri(tmp, len, &uri) < 0) {
					LM_ERR("bad uri <%s>, dropping packet\n", tmp);
					ret = E_UNSPEC;
					goto error;
				}
			} else {
				uri = msg->parsed_uri;
			}

			/* skip SET_USERPHONE_T action if the URI is already
				 * a tel: or tels: URI, or contains the user=phone param */
			if((a->type == SET_USERPHONE_T)
					&& ((uri.type == TEL_URI_T) || (uri.type == TELS_URI_T)
							|| ((uri.user_param_val.len == 5)
									&& (memcmp(uri.user_param_val.s, "phone", 5)
											== 0)))) {
				ret = 1;
				break;
			}
			/* SET_PORT_T does not work with tel: URIs */
			if((a->type == SET_PORT_T)
					&& ((uri.type == TEL_URI_T) || (uri.type == TELS_URI_T))
					&& ((uri.flags & URI_SIP_USER_PHONE) == 0)) {
				LM_ERR("port number of a tel: URI cannot be set\n");
				ret = E_UNSPEC;
				goto error;
			}

			new_uri = pkg_malloc(MAX_URI_SIZE);
			if(new_uri == 0) {
				PKG_MEM_ERROR;
				ret = E_OUT_OF_MEM;
				goto error;
			}
			end = new_uri + MAX_URI_SIZE;
			crt = new_uri;
			/* begin copying */
			/* Preserve the URI scheme unless the host part needs
				 * to be rewritten, and the shceme is tel: or tels: */
			switch(uri.type) {
				case SIP_URI_T:
					len = s_sip.len;
					tmp = s_sip.s;
					break;

				case SIPS_URI_T:
					len = s_sips.len;
					tmp = s_sips.s;
					break;

				case TEL_URI_T:
					if((uri.flags & URI_SIP_USER_PHONE)
							|| (a->type == SET_HOST_T)
							|| (a->type == SET_HOSTPORT_T)
							|| (a->type == SET_HOSTPORTTRANS_T)) {
						len = s_sip.len;
						tmp = s_sip.s;
						break;
					}
					len = s_tel.len;
					tmp = s_tel.s;
					break;

				case TELS_URI_T:
					if((uri.flags & URI_SIP_USER_PHONE)
							|| (a->type == SET_HOST_T)
							|| (a->type == SET_HOSTPORT_T)
							|| (a->type == SET_HOSTPORTTRANS_T)) {
						len = s_sips.len;
						tmp = s_sips.s;
						break;
					}
					len = s_tels.len;
					tmp = s_tels.s;
					break;

				default:
					LM_ERR("Unsupported URI scheme (%d), reverted to sip:\n",
							uri.type);
					len = s_sip.len;
					tmp = s_sip.s;
			}
			if(crt + len + 1 /* colon */ > end)
				goto error_uri;
			memcpy(crt, tmp, len);
			crt += len;
			*crt = ':';
			crt++;

			/* user */

			/* prefix (-jiri) */
			if(a->type == PREFIX_T) {
				tmp = a->val[0].u.string;
				len = strlen(tmp);
				if(crt + len > end)
					goto error_uri;
				memcpy(crt, tmp, len);
				crt += len;
				/* whatever we had before, with prefix we have username
					   now */
				user = 1;
			}

			if((a->type == SET_USER_T) || (a->type == SET_USERPASS_T)) {
				tmp = a->val[0].u.string;
				len = strlen(tmp);
			} else if(a->type == STRIP_T) {
				if(a->val[0].u.number > uri.user.len) {
					LM_WARN("too long strip asked; deleting username: %lu of "
							"<%.*s>\n",
							a->val[0].u.number, uri.user.len, uri.user.s);
					len = 0;
				} else if(a->val[0].u.number == uri.user.len) {
					len = 0;
				} else {
					tmp = uri.user.s + a->val[0].u.number;
					len = uri.user.len - a->val[0].u.number;
				}
			} else if(a->type == STRIP_TAIL_T) {
				if(a->val[0].u.number > uri.user.len) {
					LM_WARN("too long strip_tail asked; "
							" deleting username: %lu of <%.*s>\n",
							a->val[0].u.number, uri.user.len, uri.user.s);
					len = 0;
				} else if(a->val[0].u.number == uri.user.len) {
					len = 0;
				} else {
					tmp = uri.user.s;
					len = uri.user.len - a->val[0].u.number;
				}
			} else {
				tmp = uri.user.s;
				len = uri.user.len;
			}

			if(len) {
				if(crt + len > end)
					goto error_uri;
				memcpy(crt, tmp, len);
				crt += len;
				user = 1; /* we have a user field so mark it */
			}

			if(a->type == SET_USERPASS_T)
				tmp = 0;
			else
				tmp = uri.passwd.s;
			/* passwd - keep it only if user is set */
			if(user && tmp) {
				len = uri.passwd.len;
				if(crt + len + 1 > end)
					goto error_uri;
				*crt = ':';
				crt++;
				memcpy(crt, tmp, len);
				crt += len;
			}
			/* tel: URI parameters */
			if((uri.type == TEL_URI_T) || (uri.type == TELS_URI_T)) {
				tmp = uri.params.s;
				if(tmp) {
					len = uri.params.len;
					if(crt + len + 1 > end)
						goto error_uri;
					*crt = ';';
					crt++;
					memcpy(crt, tmp, len);
					crt += len;
				}
			}
			/* host */
			if((a->type == SET_HOST_T) || (a->type == SET_HOSTPORT_T)
					|| (a->type == SET_HOSTALL_T)
					|| (a->type == SET_HOSTPORTTRANS_T)) {
				tmp = a->val[0].u.string;
				if(tmp)
					len = strlen(tmp);
				else
					len = 0;
			} else if((uri.type == SIP_URI_T) || (uri.type == SIPS_URI_T)
					  || (uri.flags & URI_SIP_USER_PHONE)) {
				tmp = uri.host.s;
				len = uri.host.len;
			} else {
				tmp = 0;
			}
			if(tmp) {
				if(user) { /* add @ */
					if(crt + 1 > end)
						goto error_uri;
					*crt = '@';
					crt++;
				}
				if(crt + len > end)
					goto error_uri;
				memcpy(crt, tmp, len);
				crt += len;
			}
			if(a->type == SET_HOSTALL_T)
				goto done_seturi;
			/* port */
			if((a->type == SET_HOSTPORT_T) || (a->type == SET_HOSTPORTTRANS_T))
				tmp = 0;
			else if(a->type == SET_PORT_T) {
				tmp = a->val[0].u.string;
				if(tmp) {
					len = strlen(tmp);
					if(len == 0)
						tmp = 0;
				} else
					len = 0;
			} else {
				tmp = uri.port.s;
				len = uri.port.len;
			}
			if(tmp) {
				if(crt + len + 1 > end)
					goto error_uri;
				*crt = ':';
				crt++;
				memcpy(crt, tmp, len);
				crt += len;
			}
			/* params */
			if((a->type == SET_HOSTPORTTRANS_T) && uri.sip_params.s
					&& uri.transport.s) {
				/* bypass the transport parameter */
				if(uri.sip_params.s < uri.transport.s) {
					/* there are parameters before transport */
					len = uri.transport.s - uri.sip_params.s - 1;
					/* ignore the ';' at the end */
					if(crt + len + 1 > end)
						goto error_uri;
					*crt = ';';
					crt++;
					memcpy(crt, uri.sip_params.s, len);
					crt += len;
				}
				len = (uri.sip_params.s + uri.sip_params.len)
					  - (uri.transport.s + uri.transport.len);
				if(len > 0) {
					/* there are parameters after transport */
					if(crt + len > end)
						goto error_uri;
					tmp = uri.transport.s + uri.transport.len;
					memcpy(crt, tmp, len);
					crt += len;
				}
			} else {
				tmp = uri.sip_params.s;
				if(tmp) {
					len = uri.sip_params.len;
					if(crt + len + 1 > end)
						goto error_uri;
					*crt = ';';
					crt++;
					memcpy(crt, tmp, len);
					crt += len;
				}
			}
			/* Add the user=phone param if a tel: or tels:
				 * URI was converted to sip: or sips:.
				 * (host part of a tel/tels URI was set.)
				 * Or in case of sip: URI and SET_USERPHONE_T action */
			if(((((uri.type == TEL_URI_T) || (uri.type == TELS_URI_T))
						&& ((uri.flags & URI_SIP_USER_PHONE) == 0))
					   && ((a->type == SET_HOST_T)
							   || (a->type == SET_HOSTPORT_T)
							   || (a->type == SET_HOSTPORTTRANS_T)))
					|| (a->type == SET_USERPHONE_T)) {
				tmp = ";user=phone";
				len = strlen(tmp);
				if(crt + len > end)
					goto error_uri;
				memcpy(crt, tmp, len);
				crt += len;
			}
			/* headers */
			tmp = uri.headers.s;
			if(tmp) {
				len = uri.headers.len;
				if(crt + len + 1 > end)
					goto error_uri;
				*crt = '?';
				crt++;
				memcpy(crt, tmp, len);
				crt += len;
			}
		done_seturi:
			*crt = 0; /* null terminate the thing */
			/* copy it to the msg */
			if(msg->new_uri.s)
				pkg_free(msg->new_uri.s);
			msg->new_uri.s = new_uri;
			msg->new_uri.len = crt - new_uri;
			msg->parsed_uri_ok = 0;
			ruri_mark_new(); /* available for forking */
			ret = 1;
			break;
		case IF_T:
			rve = (struct rval_expr *)a->val[0].u.data;
			if(unlikely(rval_expr_eval_long(h, msg, &v, rve) != 0)) {
				ERR("if expression evaluation failed (%d,%d-%d,%d)\n",
						rve->fpos.s_line, rve->fpos.s_col, rve->fpos.e_line,
						rve->fpos.e_col);
				v = 0; /* false */
			}
			if(unlikely(h->run_flags & EXIT_R_F)) {
				ret = 0;
				break;
			}
			h->run_flags &= ~(RETURN_R_F | BREAK_R_F); /* catch return &
															    break in expr*/
			ret = 1;								   /*default is continue */
			if((ksr_return_mode == 0 && v > 0)
					|| (ksr_return_mode != 0 && v != 0)) {
				if((a->val[1].type == ACTIONS_ST) && a->val[1].u.data) {
					ret = run_actions(
							h, (struct action *)a->val[1].u.data, msg);
				}
			} else if((a->val[2].type == ACTIONS_ST) && a->val[2].u.data) {
				ret = run_actions(h, (struct action *)a->val[2].u.data, msg);
			}
			break;
		case MODULE0_T:
			MODF_CALL(cmd_function, h, msg, a->val, 0, 0);
			break;
		/* instead of using the parameter number, we use different names
		 * for calls to functions with 3, 4, 5, 6 or variable number of
		 * parameters due to performance reasons */
		case MODULE1_T:
			MODF_CALL(
					cmd_function, h, msg, a->val, (char *)a->val[2].u.data, 0);
			break;
		case MODULE2_T:
			MODF_CALL(cmd_function, h, msg, a->val, (char *)a->val[2].u.data,
					(char *)a->val[3].u.data);
			break;
		case MODULE3_T:
			MODF_CALL(cmd_function3, h, msg, a->val, (char *)a->val[2].u.data,
					(char *)a->val[3].u.data, (char *)a->val[4].u.data);
			break;
		case MODULE4_T:
			MODF_CALL(cmd_function4, h, msg, a->val, (char *)a->val[2].u.data,
					(char *)a->val[3].u.data, (char *)a->val[4].u.data,
					(char *)a->val[5].u.data);
			break;
		case MODULE5_T:
			MODF_CALL(cmd_function5, h, msg, a->val, (char *)a->val[2].u.data,
					(char *)a->val[3].u.data, (char *)a->val[4].u.data,
					(char *)a->val[5].u.data, (char *)a->val[6].u.data);
			break;
		case MODULE6_T:
			MODF_CALL(cmd_function6, h, msg, a->val, (char *)a->val[2].u.data,
					(char *)a->val[3].u.data, (char *)a->val[4].u.data,
					(char *)a->val[5].u.data, (char *)a->val[6].u.data,
					(char *)a->val[7].u.data);
			break;
		case MODULEX_T:
			MODF_CALL(cmd_function_var, h, msg, a->val, a->val[1].u.number,
					&a->val[2]);
			break;
		case MODULE1_RVE_T:
			MODF_RVE_CALL(cmd_function, h, msg, a->val, mod_f_params,
					(char *)mod_f_params[2].u.data, 0);
			break;
		case MODULE2_RVE_T:
			MODF_RVE_CALL(cmd_function, h, msg, a->val, mod_f_params,
					(char *)mod_f_params[2].u.data,
					(char *)mod_f_params[3].u.data);
			break;
		case MODULE3_RVE_T:
			MODF_RVE_CALL(cmd_function3, h, msg, a->val, mod_f_params,
					(char *)mod_f_params[2].u.data,
					(char *)mod_f_params[3].u.data,
					(char *)mod_f_params[4].u.data);
			break;
		case MODULE4_RVE_T:
			MODF_RVE_CALL(cmd_function4, h, msg, a->val, mod_f_params,
					(char *)mod_f_params[2].u.data,
					(char *)mod_f_params[3].u.data,
					(char *)mod_f_params[4].u.data,
					(char *)mod_f_params[5].u.data);
			break;
		case MODULE5_RVE_T:
			MODF_RVE_CALL(cmd_function5, h, msg, a->val, mod_f_params,
					(char *)mod_f_params[2].u.data,
					(char *)mod_f_params[3].u.data,
					(char *)mod_f_params[4].u.data,
					(char *)mod_f_params[5].u.data,
					(char *)mod_f_params[6].u.data);
			break;
		case MODULE6_RVE_T:
			MODF_RVE_CALL(cmd_function6, h, msg, a->val, mod_f_params,
					(char *)mod_f_params[2].u.data,
					(char *)mod_f_params[3].u.data,
					(char *)mod_f_params[4].u.data,
					(char *)mod_f_params[5].u.data,
					(char *)mod_f_params[6].u.data,
					(char *)mod_f_params[7].u.data);
			break;
		case MODULEX_RVE_T:
			MODF_RVE_CALL(cmd_function_var, h, msg, a->val, mod_f_params,
					a->val[1].u.number, &mod_f_params[2]);
			break;
		case EVAL_T:
			/* only eval the expression to account for possible
			   side-effect */
			rval_expr_eval_long(
					h, msg, &v, (struct rval_expr *)a->val[0].u.data);
			if(h->run_flags & EXIT_R_F) {
				ret = 0;
				break;
			}
			h->run_flags &= ~RETURN_R_F | BREAK_R_F; /* catch return & break in
													  expr */
			ret = 1;								 /* default is continue */
			break;
		case SWITCH_COND_T:
			sct = (struct switch_cond_table *)a->val[1].u.data;
			if(unlikely(rval_expr_eval_long(h, msg, &v,
								(struct rval_expr *)a->val[0].u.data)
						< 0)) {
				/* handle error in expression => use default */
				ret = -1;
				goto sw_cond_def;
			}
			if(h->run_flags & EXIT_R_F) {
				ret = 0;
				break;
			}
			h->run_flags &= ~(RETURN_R_F | BREAK_R_F); /* catch return & break
													    in expr */
			ret = 1;								   /* default is continue */
			for(i = 0; i < sct->n; i++)
				if(sct->cond[i] == v) {
					if(likely(sct->jump[i])) {
						ret = run_actions(h, sct->jump[i], msg);
						h->run_flags &= ~BREAK_R_F; /* catch breaks, but let
													   returns passthrough */
					}
					goto skip;
				}
		sw_cond_def:
			if(sct->def) {
				ret = run_actions(h, sct->def, msg);
				h->run_flags &= ~BREAK_R_F; /* catch breaks, but let
											   returns passthrough */
			}
			break;
		case SWITCH_JT_T:
			sjt = (struct switch_jmp_table *)a->val[1].u.data;
			if(unlikely(rval_expr_eval_long(h, msg, &v,
								(struct rval_expr *)a->val[0].u.data)
						< 0)) {
				/* handle error in expression => use default */
				ret = -1;
				goto sw_jt_def;
			}
			if(h->run_flags & EXIT_R_F) {
				ret = 0;
				break;
			}
			h->run_flags &= ~(RETURN_R_F | BREAK_R_F); /* catch return & break
													    in expr */
			ret = 1;								   /* default is continue */
			if(likely(v >= sjt->first && v <= sjt->last)) {
				if(likely(sjt->tbl[v - sjt->first])) {
					ret = run_actions(h, sjt->tbl[v - sjt->first], msg);
					h->run_flags &= ~BREAK_R_F; /* catch breaks, but let
												   returns passthrough */
				}
				break;
			} else {
				for(i = 0; i < sjt->rest.n; i++)
					if(sjt->rest.cond[i] == v) {
						if(likely(sjt->rest.jump[i])) {
							ret = run_actions(h, sjt->rest.jump[i], msg);
							h->run_flags &= ~BREAK_R_F; /* catch breaks, but
														   let returns pass */
						}
						goto skip;
					}
			}
			/* not found => try default */
		sw_jt_def:
			if(sjt->rest.def) {
				ret = run_actions(h, sjt->rest.def, msg);
				h->run_flags &= ~BREAK_R_F; /* catch breaks, but let
											   returns passthrough */
			}
			break;
		case BLOCK_T:
			if(likely(a->val[0].u.data)) {
				ret = run_actions(h, (struct action *)a->val[0].u.data, msg);
				h->run_flags &= ~BREAK_R_F; /* catch breaks, but let
											   returns passthrough */
			}
			break;
		case MATCH_COND_T:
			mct = (struct match_cond_table *)a->val[1].u.data;
			rval_cache_init(&c1);
			rv = 0;
			rv1 = 0;
			ret = rval_expr_eval_rvlong(
					h, msg, &rv, &v, (struct rval_expr *)a->val[0].u.data, &c1);

			if(unlikely(ret < 0)) {
				/* handle error in expression => use default */
				ret = -1;
				goto match_cond_def;
			}
			if(h->run_flags & EXIT_R_F) {
				ret = 0;
				break;
			}
			h->run_flags &= ~(RETURN_R_F | BREAK_R_F); /* catch return & break
													    in expr */
			if(likely(rv)) {
				rv1 = rval_convert(h, msg, RV_STR, rv, &c1);
				if(unlikely(rv1 == 0)) {
					ret = -1;
					goto match_cond_def;
				}
				s = rv1->v.s;
			} else {
				/* int result in v */
				rval_cache_clean(&c1);
				s.s = sint2str(v, &s.len);
			}
			ret = 1; /* default is continue */
			for(i = 0; i < mct->n; i++)
				if((mct->match[i].type == MATCH_STR
						   && mct->match[i].l.s.len == s.len
						   && memcmp(mct->match[i].l.s.s, s.s, s.len) == 0)
						|| (mct->match[i].type == MATCH_RE
								&& regexec(mct->match[i].l.regex, s.s, 0, 0, 0)
										   == 0)) {
					if(likely(mct->jump[i])) {
						/* make sure we cleanup first, in case run_actions()
						   exits the script directly via longjmp() */
						if(rv1) {
							rval_destroy(rv1);
							rval_destroy(rv);
							rval_cache_clean(&c1);
						} else if(rv) {
							rval_destroy(rv);
							rval_cache_clean(&c1);
						}
						ret = run_actions(h, mct->jump[i], msg);
						h->run_flags &= ~BREAK_R_F; /* catch breaks, but let
													   returns passthrough */
						goto skip;
					}
					goto match_cleanup;
				}
		match_cond_def:
			if(mct->def) {
				/* make sure we cleanup first, in case run_actions()
				   exits the script directly via longjmp() */
				if(rv1) {
					rval_destroy(rv1);
					rval_destroy(rv);
					rval_cache_clean(&c1);
				} else if(rv) {
					rval_destroy(rv);
					rval_cache_clean(&c1);
				}
				ret = run_actions(h, mct->def, msg);
				h->run_flags &= ~BREAK_R_F; /* catch breaks, but let
											   returns passthrough */
				break;
			}
		match_cleanup:
			if(rv1) {
				rval_destroy(rv1);
				rval_destroy(rv);
				rval_cache_clean(&c1);
			} else if(rv) {
				rval_destroy(rv);
				rval_cache_clean(&c1);
			}
			break;
		case WHILE_T:
			i = 0;
			flags = 0;
			rve = (struct rval_expr *)a->val[0].u.data;
			ret = 1;
			while(!(flags & (BREAK_R_F | RETURN_R_F | EXIT_R_F))
					&& (rval_expr_eval_long(h, msg, &v, rve) == 0)
					&& ((ksr_return_mode == 0 && v > 0)
							|| (ksr_return_mode != 0 && v != 0))) {
				if(cfg_get(core, core_cfg, max_while_loops) > 0)
					i++;

				if(unlikely(i > cfg_get(core, core_cfg, max_while_loops))) {
					LM_ERR("runaway while (%d, %d): more than %d loops\n",
							rve->fpos.s_line, rve->fpos.s_col,
							cfg_get(core, core_cfg, max_while_loops));
					ret = -1;
					goto error;
				}
				if(likely(a->val[1].u.data)) {
					ret = run_actions(
							h, (struct action *)a->val[1].u.data, msg);
					flags |= h->run_flags;
					h->run_flags &= ~BREAK_R_F; /* catch breaks, but let
												   returns pass-through */
				}
			}
			break;
		case FORCE_RPORT_T:
			msg->msg_flags |= FL_FORCE_RPORT;
			ret = 1; /* continue processing */
			break;
		case ADD_LOCAL_RPORT_T:
			msg->msg_flags |= FL_ADD_LOCAL_RPORT;
			ret = 1; /* continue processing */
			break;
		case UDP_MTU_TRY_PROTO_T:
			msg->msg_flags |= (msg_flags_t)a->val[0].u.number & FL_MTU_FB_MASK;
			ret = 1; /* continue processing */
			break;
		case SET_ADV_ADDR_T:
			if(a->val[0].type != STR_ST) {
				LM_CRIT("bad set_advertised_address() type %d\n",
						a->val[0].type);
				ret = E_BUG;
				goto error;
			}
			msg->set_global_address = *((str *)a->val[0].u.data);
			ret = 1; /* continue processing */
			break;
		case SET_ADV_PORT_T:
			if(a->val[0].type != STR_ST) {
				LM_CRIT("bad set_advertised_port() type %d\n", a->val[0].type);
				ret = E_BUG;
				goto error;
			}
			msg->set_global_port = *((str *)a->val[0].u.data);
			ret = 1; /* continue processing */
			break;
#ifdef USE_TCP
		case FORCE_TCP_ALIAS_T:
			if(msg->rcv.proto == PROTO_TCP
#ifdef USE_TLS
					|| msg->rcv.proto == PROTO_TLS
#endif
			) {

				if(a->val[0].type == NOSUBTYPE)
					port = msg->via1->port;
				else if(a->val[0].type == NUMBER_ST)
					port = (int)a->val[0].u.number;
				else {
					LM_CRIT("bad force_tcp_alias"
							" port type %d\n",
							a->val[0].type);
					ret = E_BUG;
					goto error;
				}

				if(tcpconn_add_alias(
						   msg->rcv.proto_reserved1, port, msg->rcv.proto)
						!= 0) {
					LM_ERR("receive_msg: tcp alias failed\n");
					ret = E_UNSPEC;
					goto error;
				}
			}
#endif
			ret = 1; /* continue processing */
			break;
		case FORCE_SEND_SOCKET_T:
			if(a->val[0].type != SOCKETINFO_ST) {
				LM_CRIT("bad force_send_socket argument"
						" type: %d\n",
						a->val[0].type);
				ret = E_BUG;
				goto error;
			}
			LM_DBG("setting send-socket to [%.*s]\n",
					((struct socket_info *)a->val[0].u.data)->sock_str.len,
					((struct socket_info *)a->val[0].u.data)->sock_str.s);
			set_force_socket(msg, (struct socket_info *)a->val[0].u.data);
			ret = 1; /* continue processing */
			break;

		case ADD_T:
		case ASSIGN_T:
			v = lval_assign(h, msg, (struct lvalue *)a->val[0].u.data,
					(struct rval_expr *)a->val[1].u.data);
			if(likely(v >= 0))
				ret = 1;
			else if(unlikely(v == EXPR_DROP)) /* hack to quit on DROP*/
				ret = 0;
			else
				ret = v;
			break;
		case SET_FWD_NO_CONNECT_T:
			msg->fwd_send_flags.f |= SND_F_FORCE_CON_REUSE;
			ret = 1; /* continue processing */
			break;
		case SET_RPL_NO_CONNECT_T:
			msg->rpl_send_flags.f |= SND_F_FORCE_CON_REUSE;
			ret = 1; /* continue processing */
			break;
		case SET_FWD_CLOSE_T:
			msg->fwd_send_flags.f |= SND_F_CON_CLOSE;
			ret = 1; /* continue processing */
			break;
		case SET_RPL_CLOSE_T:
			msg->rpl_send_flags.f |= SND_F_CON_CLOSE;
			ret = 1; /* continue processing */
			break;
		case CFG_SELECT_T:
			if(a->val[0].type != CFG_GROUP_ST) {
				BUG("unsupported parameter in CFG_SELECT_T: %d\n",
						a->val[0].type);
				ret = -1;
				goto error;
			}
			switch(a->val[1].type) {
				case NUMBER_ST:
					v = (int)a->val[1].u.number;
					break;
				case RVE_ST:
					if(rval_expr_eval_long(
							   h, msg, &v, (struct rval_expr *)a->val[1].u.data)
							< 0) {
						ret = -1;
						goto error;
					}
					break;
				default:
					BUG("unsupported group id type in CFG_SELECT_T: %d\n",
							a->val[1].type);
					ret = -1;
					goto error;
			}
			ret = (cfg_select((cfg_group_t *)a->val[0].u.data, v) == 0) ? 1
																		: -1;
			break;
		case CFG_RESET_T:
			if(a->val[0].type != CFG_GROUP_ST) {
				BUG("unsupported parameter in CFG_RESET_T: %d\n",
						a->val[0].type);
				ret = -1;
				goto error;
			}
			ret = (cfg_reset((cfg_group_t *)a->val[0].u.data) == 0) ? 1 : -1;
			break;
			/*
		default:
			LM_CRIT("unknown type %d\n", a->type);
*/
	}
skip:
	return ret;

error_uri:
	LM_ERR("set*: uri too long\n");
	if(new_uri)
		pkg_free(new_uri);
	LM_ERR("run action error at: %s:%d\n", (a->cfile) ? a->cfile : "",
			a->cline);
	return E_UNSPEC;
error_fwd_uri:
	/*free_uri(&uri); -- not needed anymore, using msg->parsed_uri*/
error:
	LM_ERR("run action error at: %s:%d\n", (a->cfile) ? a->cfile : "",
			a->cline);
	return ret;
}


/* returns: 0, or 1 on success, <0 on error */
/* (0 if drop or break encountered, 1 if not ) */
int run_actions(struct run_act_ctx *h, struct action *a, struct sip_msg *msg)
{
	struct action *t;
	int ret;
	struct timeval tvb = {0}, tve = {0};
	struct timezone tz;
	unsigned int tdiff;

	ret = E_UNSPEC;
	h->rec_lev++;
	if(unlikely(h->rec_lev > max_recursive_level)) {
		LM_ERR("too many recursive routing table lookups (%d) giving up!\n",
				h->rec_lev);
		ret = E_UNSPEC;
		goto error;
	}
	if(unlikely(h->rec_lev == 1)) {
		h->run_flags = 0;
		h->last_retcode = 0;
		_last_returned_code = h->last_retcode;
#ifdef USE_LONGJMP
		if(unlikely(setjmp(h->jmp_env))) {
			h->rec_lev = 0;
			ret = h->last_retcode;
			goto end;
		}
#endif
	}

	if(unlikely(a == 0)) {
		LM_DBG("null action list (rec_level=%d)\n", h->rec_lev);
		ret = 1;
	}

	for(t = a; t != 0; t = t->next) {

		if(unlikely(cfg_get(core, core_cfg, latency_limit_action) > 0)
				&& is_printable(cfg_get(core, core_cfg, latency_log))) {
			gettimeofday(&tvb, &tz);
		}
		_cfg_crt_action = t;
		if(unlikely(log_prefix_mode == 1)) {
			log_prefix_set(msg);
		}
		ret = do_action(h, t, msg);
		_cfg_crt_action = 0;
		if(unlikely(log_prefix_mode == 1)) {
			log_prefix_set(msg);
		}
		if(unlikely(cfg_get(core, core_cfg, latency_limit_action) > 0)
				&& is_printable(cfg_get(core, core_cfg, latency_log))) {
			gettimeofday(&tve, &tz);
			tdiff = (tve.tv_sec - tvb.tv_sec) * 1000000
					+ (tve.tv_usec - tvb.tv_usec);
			if(tdiff >= cfg_get(core, core_cfg, latency_limit_action)) {
				LOG(cfg_get(core, core_cfg, latency_log),
						"alert - action [%s (%d)]"
						" cfg [%s:%d] took too long [%u us]\n",
						is_mod_func(t)
								? ((cmd_export_t *)(t->val[0].u.data))->name
								: "corefunc",
						t->type, (t->cfile) ? t->cfile : "", t->cline, tdiff);
			}
		}
		/* break, return or drop/exit stop execution of the current
		   block */
		if(unlikely(h->run_flags & (BREAK_R_F | RETURN_R_F | EXIT_R_F))) {
			if(unlikely(h->run_flags & EXIT_R_F)) {
				h->last_retcode = ret;
				_last_returned_code = h->last_retcode;
#ifdef USE_LONGJMP
				longjmp(h->jmp_env, ret);
#endif
			}
			break;
		}
		/* ignore error returns */
	}

	h->rec_lev--;
end:
	return ret;


error:
	h->rec_lev--;
	return ret;
}


#ifdef USE_LONGJMP
/** safe version of run_actions().
 * It always return (it doesn't longjmp on forced script end).
 * @returns 0, or 1 on success, <0 on error
 * (0 if drop or break encountered, 1 if not ) */
int run_actions_safe(
		struct run_act_ctx *h, struct action *a, struct sip_msg *msg)
{
	struct run_act_ctx ctx;
	int ret;
	int ign_on_break;

	/* start with a fresh action context */
	init_run_actions_ctx(&ctx);
	ctx.last_retcode = h->last_retcode;
	ign_on_break = h->run_flags & IGNORE_ON_BREAK_R_F;
	ctx.run_flags = h->run_flags | IGNORE_ON_BREAK_R_F;
	ret = run_actions(&ctx, a, msg);
	h->last_retcode = ctx.last_retcode;
	h->run_flags = (ctx.run_flags & ~IGNORE_ON_BREAK_R_F) | ign_on_break;
	return ret;
}
#endif /* USE_LONGJMP */


int run_top_route(struct action *a, sip_msg_t *msg, struct run_act_ctx *c)
{
	struct run_act_ctx ctx;
	struct run_act_ctx *p;
	int ret;
	flag_t sfbk;

	p = (c) ? c : &ctx;
	sfbk = getsflags();
	setsflagsval(0);
	reset_static_buffer();
	init_run_actions_ctx(p);
	ret = run_actions(p, a, msg);
	setsflagsval(sfbk);
	return ret;
}


/**
 *
 */
int run_child_one_init_route(void)
{
	struct sip_msg *fmsg;
	run_act_ctx_t ctx;
	run_act_ctx_t *bctx;
	int rtb, rt;
	sr_kemi_eng_t *keng = NULL;
	str evname = str_init("core:worker-one-init");

	LM_DBG("attempting to run event_route[%s]\n", evname.s);

	if(kemi_event_route_callback.s != NULL
			&& kemi_event_route_callback.len > 0) {
		keng = sr_kemi_eng_get();
		rt = -1;
	} else {
		rt = route_get(&event_rt, evname.s);
	}
	if((keng != NULL) || (rt >= 0 && event_rt.rlist[rt] != NULL)) {
		if(cfg_child_init()) {
			return -1;
		}
		LM_DBG("executing event_route[%s] (%d)\n", evname.s, rt);
		if(faked_msg_init() < 0)
			return -1;
		fmsg = faked_msg_next();
		rtb = get_route_type();
		set_route_type(REQUEST_ROUTE);
		init_run_actions_ctx(&ctx);
		if(keng == NULL) {
			run_top_route(event_rt.rlist[rt], fmsg, &ctx);
		} else {
			bctx = sr_kemi_act_ctx_get();
			sr_kemi_act_ctx_set(&ctx);
			if(sr_kemi_route(keng, fmsg, EVENT_ROUTE,
					   &kemi_event_route_callback, &evname)
					< 0) {
				LM_ERR("error running event route kemi callback\n");
				return -1;
			}
			sr_kemi_act_ctx_set(bctx);
		}
		if(ctx.run_flags & DROP_R_F) {
			LM_ERR("exit due to 'drop' in event route\n");
			return -1;
		}
		set_route_type(rtb);
	}

	return 0;
}
