/*
 * SIP routing engine
 *
 * Copyright (C) 2001-2003 FhG Fokus
 *
 * 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
 *
 */


/** Kamailio core :: expression evaluation, route fixups and routing lists.
 * @file route.c
 * @ingroup core
 * Module: @ref core
 */

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

#include "route.h"
#include "forward.h"
#include "dprint.h"
#include "proxy.h"
#include "action.h"
#include "lvalue.h"
#include "rvalue.h"
#include "sr_module.h"
#include "ip_addr.h"
#include "resolve.h"
#include "socket_info.h"
#include "parser/parse_uri.h"
#include "parser/parse_from.h"
#include "parser/parse_to.h"
#include "mem/mem.h"
#include "select.h"
#include "onsend.h"
#include "str_hash.h"
#include "ut.h"
#include "switch.h"
#include "cfg/cfg_struct.h"

#define RT_HASH_SIZE 8 /* route names hash */

/* main routing script table  */
struct route_list main_rt;
struct route_list onreply_rt;
struct route_list failure_rt;
struct route_list branch_rt;
struct route_list onsend_rt;
struct route_list event_rt;

int route_type = REQUEST_ROUTE;

/** script optimization level, useful for debugging.
 *  0 - no optimization
 *  1 - optimize rval expressions
 *  2 - optimize expr elems
 */
int scr_opt_lev = 9;

inline static void destroy_rlist(struct route_list *rt)
{
	struct str_hash_entry *e;
	struct str_hash_entry *tmp;

	if(rt->rlist) {
		pkg_free(rt->rlist);
		rt->rlist = 0;
		rt->entries = 0;
	}
	if(rt->names.table) {
		int i;
		for(i = 0; i < RT_HASH_SIZE; i++) {
			clist_foreach_safe(&rt->names.table[i], e, tmp, next)
			{
				pkg_free(e);
			}
		}
		pkg_free(rt->names.table);
		rt->names.table = 0;
		rt->names.size = 0;
	}
}


void destroy_routes()
{
	destroy_rlist(&main_rt);
	destroy_rlist(&onreply_rt);
	destroy_rlist(&failure_rt);
	destroy_rlist(&branch_rt);
	destroy_rlist(&event_rt);
}


/* adds route name -> i mapping
 * WARNING: it doesn't check for pre-existing routes
 * return -1 on error, route index on success
 */
static int route_add(struct route_list *rt, char *name, int i)
{
	struct str_hash_entry *e;

	e = pkg_malloc(sizeof(struct str_hash_entry));
	if(e == 0) {
		PKG_MEM_CRITICAL;
		goto error;
	}
	LM_DBG("mapping routing block (%p)[%s] to %d\n", rt, name, i);
	e->key.s = name;
	e->key.len = strlen(name);
	e->flags = 0;
	e->u.n = i;
	str_hash_add(&rt->names, e);
	return 0;
error:
	return -1;
}


/* returns -1 on error, 0 on success */
inline static int init_rlist(
		char *r_name, struct route_list *rt, int n_entries, int hash_size)
{
	rt->rlist = pkg_malloc(sizeof(struct action *) * n_entries);
	if(rt->rlist == 0) {
		PKG_MEM_CRITICAL;
		goto error;
	}
	memset(rt->rlist, 0, sizeof(struct action *) * n_entries);
	rt->idx = 1; /* idx=0 == default == reserved */
	rt->entries = n_entries;
	if(str_hash_alloc(&rt->names, hash_size) < 0) {
		LM_CRIT("\"%s\" route table: failed to alloc hash\n", r_name);
		goto error;
	}
	str_hash_init(&rt->names);
	route_add(rt, "0", 0); /* default route */

	return 0;
error:
	return -1;
}


/* init route tables */
int init_routes()
{
	if(init_rlist("main", &main_rt, RT_NO, RT_HASH_SIZE) < 0)
		goto error;
	if(init_rlist("on_reply", &onreply_rt, ONREPLY_RT_NO, RT_HASH_SIZE) < 0)
		goto error;
	if(init_rlist("failure", &failure_rt, FAILURE_RT_NO, RT_HASH_SIZE) < 0)
		goto error;
	if(init_rlist("branch", &branch_rt, BRANCH_RT_NO, RT_HASH_SIZE) < 0)
		goto error;
	if(init_rlist("on_send", &onsend_rt, ONSEND_RT_NO, RT_HASH_SIZE) < 0)
		goto error;
	if(init_rlist("event", &event_rt, EVENT_RT_NO, RT_HASH_SIZE) < 0)
		goto error;
	return 0;
error:
	destroy_routes();
	return -1;
}


static inline int route_new_list(struct route_list *rt)
{
	int ret;
	struct action **tmp;

	ret = -1;
	if(rt->idx >= rt->entries) {
		tmp = pkg_realloc(rt->rlist, 2 * rt->entries * sizeof(struct action *));
		if(tmp == 0) {
			LM_CRIT("out of memory\n");
			goto end;
		}
		/* init the newly allocated memory chunk */
		memset(&tmp[rt->entries], 0, rt->entries * sizeof(struct action *));
		rt->rlist = tmp;
		rt->entries *= 2;
	}
	if(rt->idx < rt->entries) {
		ret = rt->idx;
		rt->idx++;
	}
end:
	return ret;
}


/*
 * if the "name" route already exists, return its index, else
 * create a new empty route
 * return route index in rt->rlist or -1 on error
 */
int route_get(struct route_list *rt, char *name)
{
	int len;
	struct str_hash_entry *e;
	int i;

	len = strlen(name);
	/* check if exists and non empty*/
	e = str_hash_get(&rt->names, name, len);
	if(e) {
		i = e->u.n;
	} else {
		i = route_new_list(rt);
		if(i == -1)
			goto error;
		if(route_add(rt, name, i) < 0) {
			goto error;
		}
	}
	return i;
error:
	return -1;
}


/*
 * if the "name" route already exists, return its index, else
 * return error
 * return route index in rt->rlist or -1 on error
 */
int route_lookup(struct route_list *rt, char *name)
{
	int len;
	struct str_hash_entry *e;

	len = strlen(name);
	/* check if exists and non empty*/
	e = str_hash_get(&rt->names, name, len);
	if(e) {
		return e->u.n;
	} else {
		return -1;
	}
}


int fix_actions(struct action *a); /*fwd declaration*/


/** optimize the left side of a struct expr.
 *  @return 1 if optimized, 0 if not and -1 on error
 */
static int exp_optimize_left(struct expr *exp)
{
	struct rval_expr *rve;
	struct rvalue *rval;
	int old_ltype, old_rtype, old_op;
	int ret;

	ret = 0;
	if(exp->type != ELEM_T)
		return 0;
	old_ltype = exp->l_type;
	old_rtype = exp->r_type;
	old_op = exp->op;
	if(exp->l_type == RVEXP_O) {
		rve = exp->l.param;
		/* rve should be previously fixed/optimized */
		/* optimize exp (rval(val)) -> exp(val) */
		if(rve->op == RVE_RVAL_OP) {
			rval = &rve->left.rval;
			switch(rval->type) {
				case RV_LONG:
					if(exp->op == NO_OP) {
						exp->l_type = NUMBER_O;
						exp->l.param = 0;
						exp->r_type = NUMBER_ST;
						exp->r.numval = rval->v.l;
						rval_destroy(rval);
						pkg_free(rve);
						ret = 1;
					}
					break;
				case RV_STR:
					/* string evaluated in expression context - not
					   supported */
					break;
				case RV_BEXPR:
					if(exp->op == NO_OP) {
						/* replace the current expr. */
						*exp = *(rval->v.bexpr);
						rval_destroy(rval);
						pkg_free(rve);
						ret = 1;
					};
					break;
				case RV_ACTION_ST:
					if(exp->op == NO_OP) {
						exp->l_type = ACTION_O;
						exp->l.param = 0;
						exp->r_type = ACTION_ST;
						exp->r.param = rval->v.action;
						rval_destroy(rval);
						pkg_free(rve);
						ret = 1;
					}
					break;
				case RV_SEL:
					exp->l.select = pkg_malloc(sizeof(*exp->l.select));
					if(exp->l.select) {
						exp->l_type = SELECT_O;
						*exp->l.select = rval->v.sel;
						rval_destroy(rval);
						pkg_free(rve);
						ret = 1;
					} else {
						PKG_MEM_ERROR;
						ret = -1;
					}
					break;
				case RV_AVP:
					exp->l.attr = pkg_malloc(sizeof(*exp->l.attr));
					if(exp->l.attr) {
						exp->l_type = AVP_O;
						*exp->l.attr = rval->v.avps;
						rval_destroy(rval);
						pkg_free(rve);
						ret = 1;
					} else {
						PKG_MEM_ERROR;
						ret = -1;
					}
					break;
				case RV_PVAR:
					exp->l.param = pkg_malloc(sizeof(pv_spec_t));
					if(exp->l.param) {
						exp->l_type = PVAR_O;
						*((pv_spec_t *)exp->l.param) = rval->v.pvs;
						rval_destroy(rval);
						pkg_free(rve);
						ret = 1;
					} else {
						PKG_MEM_ERROR;
						ret = -1;
					}
					break;
				case RV_NONE:
					break;
			}
		}
	}
	if(ret > 0)
		LM_DBG("op%d(_O%d_, ST%d) => op%d(_O%d_, ST%d)\n", old_op, old_ltype,
				old_rtype, exp->op, exp->l_type, exp->r_type);
	return ret;
}


/** optimize the left side of a struct expr.
 *  @return 1 if optimized, 0 if not and -1 on error
 */
static int exp_optimize_right(struct expr *exp)
{
	struct rval_expr *rve;
	struct rvalue *rval;
	int old_ltype, old_rtype, old_op;
	int ret;

	ret = 0;
	if((exp->type != ELEM_T) || (exp->op == NO_OP))
		return 0;
	old_ltype = exp->l_type;
	old_rtype = exp->r_type;
	old_op = exp->op;
	if(exp->r_type == RVE_ST) {
		rve = exp->r.param;
		/* rve should be previously fixed/optimized */
		/* optimize exp (rval(val)) -> exp(val) */
		if(rve->op == RVE_RVAL_OP) {
			rval = &rve->left.rval;
			switch(rval->type) {
				case RV_LONG:
					exp->r_type = NUMBER_ST;
					exp->r.numval = rval->v.l;
					rval_destroy(rval);
					pkg_free(rve);
					ret = 1;
					break;
				case RV_STR:
					exp->r.str.s = pkg_malloc(rval->v.s.len + 1);
					if(exp->r.str.s) {
						exp->r.str.len = rval->v.s.len;
						memcpy(exp->r.str.s, rval->v.s.s, rval->v.s.len);
						exp->r.str.s[exp->r.str.len] = 0;
						exp->r_type = STRING_ST;
						rval_destroy(rval);
						pkg_free(rve);
						ret = 1;
					} else {
						PKG_MEM_ERROR;
						ret = -1;
					}
					break;
				case RV_BEXPR:
					/* cannot be optimized further, is an exp_elem
					   which is not constant */
					break;
				case RV_ACTION_ST:
					/* cannot be optimized further, is not constant and
					  eval_elem() does not support ACTION_ST for op!=NO_OP*/
					break;
				case RV_SEL:
					exp->r.select = pkg_malloc(sizeof(*exp->l.select));
					if(exp->r.select) {
						exp->r_type = SELECT_ST;
						*exp->r.select = rval->v.sel;
						rval_destroy(rval);
						pkg_free(rve);
						ret = 1;
					} else {
						PKG_MEM_ERROR;
						ret = -1;
					}
					break;
				case RV_AVP:
					exp->r.attr = pkg_malloc(sizeof(*exp->l.attr));
					if(exp->r.attr) {
						exp->r_type = AVP_ST;
						*exp->r.attr = rval->v.avps;
						rval_destroy(rval);
						pkg_free(rve);
						ret = 1;
					} else {
						PKG_MEM_ERROR;
						ret = -1;
					}
					break;
				case RV_PVAR:
					exp->r.param = pkg_malloc(sizeof(pv_spec_t));
					if(exp->r.param) {
						exp->r_type = PVAR_ST;
						*((pv_spec_t *)exp->r.param) = rval->v.pvs;
						rval_destroy(rval);
						pkg_free(rve);
						ret = 1;
					} else {
						PKG_MEM_ERROR;
						ret = -1;
					}
					break;
				case RV_NONE:
					ret = -1;
					break;
			}
		}
	}
	if(ret > 0)
		LM_DBG("op%d(O%d, _ST%d_) => op%d(O%d, _ST%d_)\n", old_op, old_ltype,
				old_rtype, exp->op, exp->l_type, exp->r_type);
	return ret;
}


/* traverses an expr tree and compiles the REs where necessary)
 * returns: 0 for ok, <0 if errors */
int fix_expr(struct expr *exp)
{
	regex_t *re;
	int ret;
	int len;

	ret = E_BUG;
	if(exp == 0) {
		LM_CRIT("null pointer\n");
		return E_BUG;
	}
	if(exp->type == EXP_T) {
		switch(exp->op) {
			case LOGAND_OP:
			case LOGOR_OP:
				if((ret = fix_expr(exp->l.expr)) != 0)
					return ret;
				ret = fix_expr(exp->r.expr);
				break;
			case NOT_OP:
				ret = fix_expr(exp->l.expr);
				break;
			default:
				LM_CRIT("unknown op %d\n", exp->op);
		}
	} else if(exp->type == ELEM_T) {
		/* first calculate lengths of strings  (only right side, since
			  left side can never be a string) */
		if(exp->r_type == STRING_ST) {
			if(exp->r.string)
				len = strlen(exp->r.string);
			else
				len = 0;
			exp->r.str.s = exp->r.string;
			exp->r.str.len = len;
		}
		/* then fix & optimize rve/rvals (they might be optimized
			   to non-rvals, e.g. string, avp a.s.o and needs to be done
			   before MATCH_OP and other fixups) */
		if(exp->l_type == RVEXP_O) {
			if((ret = fix_rval_expr(exp->l.param)) < 0) {
				LM_ERR("Unable to fix left rval expression\n");
				return ret;
			}
			if(scr_opt_lev >= 2)
				exp_optimize_left(exp);
		}
		if(exp->r_type == RVE_ST) {
			if((ret = fix_rval_expr(exp->r.param)) < 0) {
				LM_ERR("Unable to fix right rval expression\n");
				return ret;
			}
			if(scr_opt_lev >= 2)
				exp_optimize_right(exp);
		}


		if(exp->op == MATCH_OP) {
			/* right side either has to be string, in which case
				      * we turn it into regular expression, or it is regular
				      * expression already. In that case we do nothing
				      */
			if(exp->r_type == STRING_ST) {
				re = (regex_t *)pkg_malloc(sizeof(regex_t));
				if(re == 0) {
					PKG_MEM_CRITICAL;
					return E_OUT_OF_MEM;
				}
				if(regcomp(re, (char *)exp->r.param,
						   REG_EXTENDED | REG_NOSUB | REG_ICASE)) {
					LM_CRIT("bad re \"%s\"\n", (char *)exp->r.param);
					pkg_free(re);
					return E_BAD_RE;
				}
				/* replace the string with the re */
				pkg_free(exp->r.param);
				exp->r.re = re;
				exp->r_type = RE_ST;
			} else if(exp->r_type != RE_ST && exp->r_type != AVP_ST
					  && exp->r_type != SELECT_ST
					  && exp->r_type != SELECT_UNFIXED_ST
					  && exp->r_type != RVE_ST && exp->r_type != PVAR_ST) {
				LM_CRIT("invalid type for match\n");
				return E_BUG;
			}
		}
		if(exp->l_type == ACTION_O) {
			ret = fix_actions((struct action *)exp->r.param);
			if(ret != 0) {
				LM_CRIT("fix_actions error\n");
				return ret;
			}
		}
		if(exp->l_type == SELECT_UNFIXED_O) {
			if((ret = resolve_select(exp->l.select)) < 0) {
				LM_ERR("Unable to resolve select\n");
				print_select(exp->l.select);
				return ret;
			}
			exp->l_type = SELECT_O;
		}
		if(exp->r_type == SELECT_UNFIXED_ST) {
			if((ret = resolve_select(exp->r.select)) < 0) {
				LM_ERR("Unable to resolve select\n");
				print_select(exp->r.select);
				return ret;
			}
			exp->r_type = SELECT_ST;
		}
		/* PVAR don't need fixing */
		ret = 0;
	}
	return ret;
}


/* adds the proxies in the proxy list & resolves the hostnames */
/* returns 0 if ok, <0 on error */
int fix_actions(struct action *a)
{
	struct action *t;
	struct proxy_l *p;
	char *tmp;
	void *tmp_p;
	int ret;
	long li;
	long i;
	ksr_cmd_export_t *cmd;
	str s;
	struct hostent *he;
	struct ip_addr ip;
	struct socket_info *si;
	struct lvalue *lval;
	struct rval_expr *rve;
	struct rval_expr *err_rve;
	enum rval_type rve_type, err_type, expected_type;
	struct rvalue *rv;
	int rve_param_no;
	int proto;

	if(a == 0) {
		LM_CRIT("null pointer\n");
		return E_BUG;
	}
	for(t = a; t != 0; t = t->next) {
		switch(t->type) {
			case FORWARD_T:
			case FORWARD_TLS_T:
			case FORWARD_TCP_T:
			case FORWARD_SCTP_T:
			case FORWARD_UDP_T:
				switch(t->val[0].type) {
					case IP_ST:
						tmp = strdup(
								ip_addr2a((struct ip_addr *)t->val[0].u.data));
						if(tmp == 0) {
							LM_CRIT("memory allocation failure\n");
							ret = E_OUT_OF_MEM;
							goto error;
						}
						t->val[0].type = STRING_ST;
						t->val[0].u.string = tmp;
						/* no break */
					case STRING_ST:
						s.s = t->val[0].u.string;
						s.len = strlen(s.s);
						switch(t->type) {
							case FORWARD_TCP_T:
								proto = PROTO_TCP;
								break;
							case FORWARD_TLS_T:
								proto = PROTO_TCP;
								break;
							case FORWARD_SCTP_T:
								proto = PROTO_TCP;
								break;
							case FORWARD_UDP_T:
								proto = PROTO_TCP;
								break;
							default:
								proto = 0;
						}
						p = add_proxy(&s, t->val[1].u.number, proto);
						if(p == 0) {
							ret = E_BAD_ADDRESS;
							goto error;
						}
						t->val[0].u.data = p;
						t->val[0].type = PROXY_ST;
						break;
					case URIHOST_ST:
						break;
					default:
						LM_CRIT("invalid type %d (should be string or "
								"number)\n",
								t->type);
						ret = E_BUG;
						goto error;
				}
				break;
			case IF_T:
				if(t->val[0].type != RVE_ST) {
					LM_CRIT("invalid subtype %d for if (should be rval expr)\n",
							t->val[0].type);
					ret = E_BUG;
					goto error;
				} else if((t->val[1].type != ACTIONS_ST)
						  && (t->val[1].type != NOSUBTYPE)) {
					LM_CRIT("invalid subtype %d for if() {...} (should be "
							"action)\n",
							t->val[1].type);
					ret = E_BUG;
					goto error;
				} else if((t->val[2].type != ACTIONS_ST)
						  && (t->val[2].type != NOSUBTYPE)) {
					LM_CRIT("invalid subtype %d for if() {} else{...}(should "
							"be action)\n",
							t->val[2].type);
					ret = E_BUG;
					goto error;
				}
				rve = (struct rval_expr *)t->val[0].u.data;
				if(rve) {
					err_rve = 0;
					if(!rve_check_type(&rve_type, rve, &err_rve, &err_type,
							   &expected_type)) {
						if(err_rve)
							LM_ERR("invalid expression "
								   "(%d,%d): subexpression (%d,%d) has type"
								   " %s,  but %s is expected\n",
									rve->fpos.s_line, rve->fpos.s_col,
									err_rve->fpos.s_line, err_rve->fpos.s_col,
									rval_type_name(err_type),
									rval_type_name(expected_type));
						else
							LM_ERR("invalid expression  (%d,%d): type "
								   "mismatch?",
									rve->fpos.s_line, rve->fpos.s_col);
						ret = E_SCRIPT;
						goto error;
					}
					/* it's not an error anymore to have non-int in an if,
					   only a script warning (to allow backward compat. stuff
					   like if (@ruri)
					if (rve_type!=RV_LONG && rve_type!=RV_NONE){
						LM_ERR("fix_actions: invalid expression (%d,%d):"
								" bad type, integer expected\n",
								rve->fpos.s_line, rve->fpos.s_col);
						return E_UNSPEC;
					}
					*/
					if((ret = fix_rval_expr(t->val[0].u.data)) < 0)
						goto error;
				}
				if((t->val[1].type == ACTIONS_ST) && (t->val[1].u.data)) {
					if((ret = fix_actions((struct action *)t->val[1].u.data))
							< 0)
						goto error;
				}
				if((t->val[2].type == ACTIONS_ST) && (t->val[2].u.data)) {
					if((ret = fix_actions((struct action *)t->val[2].u.data))
							< 0)
						goto error;
				}
				break;
			case SWITCH_T:
				if(t->val[0].type != RVE_ST) {
					LM_CRIT("invalid subtype %d for switch() (should be "
							"expr)\n",
							t->val[0].type);
					ret = E_BUG;
					goto error;
				} else if(t->val[1].type != CASE_ST) {
					LM_CRIT("invalid subtype %d for switch(...){...}(should be "
							"case)\n",
							t->val[1].type);
					ret = E_BUG;
					goto error;
				}
				if(t->val[0].u.data) {
					if((ret = fix_rval_expr(t->val[0].u.data)) < 0)
						goto error;
				} else {
					LM_CRIT("null switch() expression\n");
					ret = E_BUG;
					goto error;
				}
				if((ret = fix_switch(t)) < 0)
					goto error;
				break;
			case WHILE_T:
				if(t->val[0].type != RVE_ST) {
					LM_CRIT("invalid subtype %d for while() (should be expr)\n",
							t->val[0].type);
					ret = E_BUG;
					goto error;
				} else if(t->val[1].type != ACTIONS_ST) {
					LM_CRIT("invalid subtype %d for while(...){...}(should be "
							"action)\n",
							t->val[1].type);
					ret = E_BUG;
					goto error;
				}
				rve = (struct rval_expr *)t->val[0].u.data;
				if(rve) {
					err_rve = 0;
					if(!rve_check_type(&rve_type, rve, &err_rve, &err_type,
							   &expected_type)) {
						if(err_rve)
							LM_ERR("invalid expression "
								   "(%d,%d): subexpression (%d,%d) has type"
								   " %s,  but %s is expected\n",
									rve->fpos.s_line, rve->fpos.s_col,
									err_rve->fpos.s_line, err_rve->fpos.s_col,
									rval_type_name(err_type),
									rval_type_name(expected_type));
						else
							LM_ERR("invalid expression (%d,%d): type mismatch?",
									rve->fpos.s_line, rve->fpos.s_col);
						ret = E_SCRIPT;
						goto error;
					}
					if(rve_type != RV_LONG && rve_type != RV_NONE) {
						LM_ERR("invalid expression (%d,%d): bad type, integer "
							   "expected\n",
								rve->fpos.s_line, rve->fpos.s_col);
						ret = E_SCRIPT;
						goto error;
					}
					if((ret = fix_rval_expr(t->val[0].u.data)) < 0)
						goto error;
				} else {
					LM_CRIT("null while() expression\n");
					ret = E_BUG;
					goto error;
				}
				if(t->val[1].u.data
						&& ((ret = fix_actions(
									 (struct action *)t->val[1].u.data))
								< 0)) {
					goto error;
				}
				break;
			case DROP_T:
				/* only RVEs need fixing for drop/return/break */
				if(t->val[0].type != RVE_ST)
					break;
				rve = (struct rval_expr *)t->val[0].u.data;
				if(rve) {
					err_rve = 0;
					if(!rve_check_type(&rve_type, rve, &err_rve, &err_type,
							   &expected_type)) {
						if(err_rve)
							LM_ERR("invalid expression "
								   "(%d,%d): subexpression (%d,%d) has type"
								   " %s,  but %s is expected\n",
									rve->fpos.s_line, rve->fpos.s_col,
									err_rve->fpos.s_line, err_rve->fpos.s_col,
									rval_type_name(err_type),
									rval_type_name(expected_type));
						else
							LM_ERR("invalid expression (%d,%d): type mismatch?",
									rve->fpos.s_line, rve->fpos.s_col);
						ret = E_SCRIPT;
						goto error;
					}
					if(rve_type != RV_LONG && rve_type != RV_NONE) {
						LM_ERR("invalid expression (%d,%d): bad type, integer "
							   "expected\n",
								rve->fpos.s_line, rve->fpos.s_col);
						ret = E_SCRIPT;
						goto error;
					}
					if((ret = fix_rval_expr(t->val[0].u.data)) < 0)
						goto error;
				} else {
					LM_CRIT("null drop/return expression\n");
					ret = E_BUG;
					goto error;
				}
				break;
			case ASSIGN_T:
			case ADD_T:
				if(t->val[0].type != LVAL_ST) {
					LM_CRIT("Invalid left side of assignment\n");
					ret = E_BUG;
					goto error;
				}
				if(t->val[1].type != RVE_ST) {
					LM_CRIT("Invalid right side of assignment (%d)\n",
							t->val[1].type);
					ret = E_BUG;
					goto error;
				}
				lval = t->val[0].u.data;
				if(lval->type == LV_AVP) {
					if(lval->lv.avps.type & AVP_CLASS_DOMAIN) {
						LM_ERR("You cannot change domain"
							   " attributes from the script, they are"
							   " read-only\n");
						ret = E_BUG;
						goto error;
					} else if(lval->lv.avps.type & AVP_CLASS_GLOBAL) {
						LM_ERR("You cannot change global"
							   " attributes from the script, they are"
							   "read-only\n");
						ret = E_BUG;
						goto error;
					}
				}
				if((ret = fix_rval_expr(t->val[1].u.data)) < 0)
					goto error;
				break;

			case MODULE0_T:
			case MODULE1_T:
			case MODULE2_T:
			case MODULE3_T:
			case MODULE4_T:
			case MODULE5_T:
			case MODULE6_T:
			case MODULEX_T:
				cmd = t->val[0].u.data;
				rve_param_no = 0;
				if(cmd) {
					LM_DBG("fixing %s()\n", cmd->name);
					if(t->val[1].u.number == 0) {
						ret = call_fixup(cmd->fixup, 0, 0);
						if(ret < 0)
							goto error;
					}
					for(i = 0; i < t->val[1].u.number; i++) {
						if(t->val[i + 2].type == RVE_ST) {
							rve = t->val[i + 2].u.data;
							if(rve_is_constant(rve)) {
								/* if expression is constant => evaluate it
								   as string and replace it with the corresp.
								   string */
								rv = rval_expr_eval(0, 0, rve);
								if(rv == 0
										|| rval_get_str(0, 0, &s, rv, 0) < 0) {
									ERR("failed to fix constant rve");
									if(rv)
										rval_destroy(rv);
									ret = E_BUG;
									goto error;
								}
								rval_destroy(rv);
								rve_destroy(rve);
								t->val[i + 2].type =
										STRING_ST; /*asciiz string*/
								t->val[i + 2].u.string = s.s;
								/* len is not used for now */
								t->val[i + 2].u.str.len = s.len;
								tmp_p = t->val[i + 2].u.data;
								ret = call_fixup(cmd->fixup,
										&t->val[i + 2].u.data, i + 1);
								if(t->val[i + 2].u.data != tmp_p)
									t->val[i + 2].type = MODFIXUP_ST;
								if(ret < 0)
									goto error;
							} else {
								/* expression is not constant => fixup &
								   optimize it */
								rve_param_no++;
								if((ret = fix_rval_expr(t->val[i + 2].u.data))
										< 0) {
									ERR("rve fixup failed\n");
									ret = E_BUG;
									goto error;
								}
							}
						} else if(t->val[i + 2].type == STRING_ST) {
							tmp_p = t->val[i + 2].u.data;
							ret = call_fixup(
									cmd->fixup, &t->val[i + 2].u.data, i + 1);
							if(t->val[i + 2].u.data != tmp_p)
								t->val[i + 2].type = MODFIXUP_ST;
							if(ret < 0)
								goto error;
						} else {
							BUG("invalid module function param type %d\n",
									t->val[i + 2].type);
							ret = E_BUG;
							goto error;
						}
					} /* for */
					/* here all the params are either STRING_ST
					   (constant RVEs), MODFIXUP_ST (fixed up)
					   or RVE_ST (non-ct RVEs) */
					if(rve_param_no) { /* we have to fix the type */
						if(cmd->fixup
								&& !(cmd->fixup_flags & FIXUP_F_FPARAM_RVE)
								&& cmd->free_fixup == 0) {
							BUG("non-ct RVEs (%d) in module function call"
								"that does not support them (%s)\n",
									rve_param_no, cmd->name);
							ret = E_BUG;
							goto error;
						}
						switch(t->type) {
							case MODULE1_T:
								t->type = MODULE1_RVE_T;
								break;
							case MODULE2_T:
								t->type = MODULE2_RVE_T;
								break;
							case MODULE3_T:
								t->type = MODULE3_RVE_T;
								break;
							case MODULE4_T:
								t->type = MODULE4_RVE_T;
								break;
							case MODULE5_T:
								t->type = MODULE5_RVE_T;
								break;
							case MODULE6_T:
								t->type = MODULE6_RVE_T;
								break;
							case MODULEX_T:
								t->type = MODULEX_RVE_T;
								break;
							default:
								BUG("unsupported module function type %d\n",
										t->type);
								ret = E_BUG;
								goto error;
						}
					} /* if rve_param_no */
				}
				break;
			case FORCE_SEND_SOCKET_T:
				if(t->val[0].type != SOCKID_ST) {
					LM_CRIT("invalid subtype %d for force_send_socket\n",
							t->val[0].type);
					ret = E_BUG;
					goto error;
				}
				he = resolvehost(
						((struct socket_id *)t->val[0].u.data)->addr_lst->name);
				if(he == 0) {
					LM_ERR("force_send_socket: could not resolve %s\n",
							((struct socket_id *)t->val[0].u.data)
									->addr_lst->name);
					ret = E_BAD_ADDRESS;
					goto error;
				}
				hostent2ip_addr(&ip, he, 0);
				si = find_si(&ip, ((struct socket_id *)t->val[0].u.data)->port,
						((struct socket_id *)t->val[0].u.data)->proto);
				if(si == 0) {
					LM_ERR("bad force_send_socket argument: %s:%d (" NAME
						   " doesn't listen on it)\n",
							((struct socket_id *)t->val[0].u.data)
									->addr_lst->name,
							((struct socket_id *)t->val[0].u.data)->port);
					ret = E_BAD_ADDRESS;
					goto error;
				}
				t->val[0].u.data = si;
				t->val[0].type = SOCKETINFO_ST;
				break;
			case UDP_MTU_TRY_PROTO_T:
				if(t->val[0].type != NUMBER_ST) {
					LM_CRIT("invalid subtype %d for udp_mtu_try_proto\n",
							t->val[0].type);
					ret = E_BUG;
					goto error;
				}
				switch(t->val[0].u.number) {
					case PROTO_UDP:
						t->val[0].u.number = 0;
						break;
					case PROTO_TCP:
						t->val[0].u.number = FL_MTU_TCP_FB;
						break;
					case PROTO_TLS:
						t->val[0].u.number = FL_MTU_TLS_FB;
						break;
					case PROTO_SCTP:
						t->val[0].u.number = FL_MTU_SCTP_FB;
						break;
					default:
						LM_CRIT("invalid argument for udp_mtu_try_proto (%d)\n",
								(unsigned int)t->val[0].u.number);
				}
				break;
			case APPEND_BRANCH_T:
				if(t->val[0].type != STRING_ST) {
					BUG("invalid subtype%d for append_branch_t\n",
							t->val[0].type);
					ret = E_BUG;
					goto error;
				}
				s.s = t->val[0].u.string;
				s.len = (s.s) ? strlen(s.s) : 0;
				t->val[0].u.str = s;
				t->val[0].type = STR_ST;
				break;
			case ROUTE_T:
				if(t->val[0].type == RVE_ST) {
					rve = (struct rval_expr *)t->val[0].u.data;
					if(!rve_is_constant(rve)) {
						if((ret = fix_rval_expr(t->val[0].u.data)) < 0) {
							LM_ERR("route() failed to fix rve at %s:%d\n",
									(t->cfile) ? t->cfile : "line", t->cline);
							ret = E_BUG;
							goto error;
						}
					} else {
						/* rve is constant => replace it with a string */
						if((rv = rval_expr_eval(0, 0, rve)) == 0
								|| rval_get_str(0, 0, &s, rv, 0) < 0) {
							/* out of mem. or bug ? */
							rval_destroy(rv);
							LM_ERR("route() failed to fix ct. rve at %s:%d\n",
									(t->cfile) ? t->cfile : "line", t->cline);
							ret = E_BUG;
							goto error;
						}
						rval_destroy(rv);
						rve_destroy(rve);
						t->val[0].type = STRING_ST;
						t->val[0].u.string = s.s;
						t->val[0].u.str.len = s.len; /* not used */
						/* fall-through the STRING_ST if */
					}
				}
				if(t->val[0].type == STRING_ST) {
					i = route_lookup(&main_rt, t->val[0].u.string);
					if(i < 0) {
						LM_ERR("route \"%s\" not found at %s:%d\n",
								t->val[0].u.string,
								(t->cfile) ? t->cfile : "line", t->cline);
						ret = E_SCRIPT;
						goto error;
					}
					t->val[0].type = NUMBER_ST;
					pkg_free(t->val[0].u.string);
					t->val[0].u.number = i;
				} else if(t->val[0].type != NUMBER_ST
						  && t->val[0].type != RVE_ST) {
					BUG("invalid subtype %d for route()\n", t->val[0].type);
					ret = E_BUG;
					goto error;
				}
				break;
			case CFG_SELECT_T:
				if(t->val[1].type == RVE_ST) {
					rve = t->val[1].u.data;
					if(rve_is_constant(rve)) {
						/* if expression is constant => evaluate it
						   as integer and replace it with the corresp.
						   int */
						rv = rval_expr_eval(0, 0, rve);
						if(rv == 0 || rval_get_long(0, 0, &li, rv, 0) < 0) {
							LM_ERR("failed to fix constant rve");
							if(rv)
								rval_destroy(rv);
							ret = E_BUG;
							goto error;
						}
						rval_destroy(rv);
						rve_destroy(rve);
						t->val[1].type = NUMBER_ST;
						t->val[1].u.number = li;
					} else {
						/* expression is not constant => fixup &
						   optimize it */
						if((ret = fix_rval_expr(rve)) < 0) {
							LM_ERR("rve fixup failed\n");
							ret = E_BUG;
							goto error;
						}
					}
				} else if(t->val[1].type != NUMBER_ST) {
					BUG("invalid subtype %d for cfg_select()\n",
							t->val[1].type);
					ret = E_BUG;
					goto error;
				}

			case CFG_RESET_T:
				if(t->val[0].type != STRING_ST) {
					BUG("invalid subtype %d for cfg_select() or cfg_reset()\n",
							t->val[0].type);
					ret = E_BUG;
					goto error;
				}
				tmp_p = (void *)cfg_lookup_group(
						t->val[0].u.string, strlen(t->val[0].u.string));
				if(!tmp_p) {
					LM_ERR("configuration group \"%s\" not found\n",
							t->val[0].u.string);
					ret = E_SCRIPT;
					goto error;
				}
				pkg_free(t->val[0].u.string);
				t->val[0].u.data = tmp_p;
				t->val[0].type = CFG_GROUP_ST;
				break;
			default:
				/* no fixup required for the rest */
				break;
		}
	}
	return 0;

error:
	LM_ERR("fixing failed (code=%d) at cfg:%s:%d\n", ret,
			(t->cfile) ? t->cfile : "", t->cline);
	return ret;
}


/* Compare parameters as ordinary numbers
 *
 * Left and right operands can be either numbers or
 * attributes. If either of the attributes if of string type then the length of
 * its value will be used.
 */
inline static int comp_num(int op, long left, int rtype, union exp_op *r,
		struct sip_msg *msg, struct run_act_ctx *h)
{
	int_str val;
	pv_value_t pval;
	avp_t *avp;
	long right;

	if(unlikely(op == NO_OP))
		return !(!left);
	switch(rtype) {
		case AVP_ST:
			avp = search_avp_by_index(
					r->attr->type, r->attr->name, &val, r->attr->index);
			if(avp && !(avp->flags & AVP_VAL_STR))
				right = val.n;
			else
				return (op == DIFF_OP);
			break;
		case NUMBER_ST:
			right = r->numval;
			break;
		case RVE_ST:
			if(unlikely(rval_expr_eval_long(h, msg, &right, r->param) < 0))
				return (op == DIFF_OP); /* not found/invalid */
			break;
		case PVAR_ST:
			memset(&pval, 0, sizeof(pv_value_t));
			if(unlikely(pv_get_spec_value(msg, r->param, &pval) != 0)) {
				return (op == DIFF_OP); /* error, not found => false */
			}
			if(likely(pval.flags & (PV_TYPE_INT | PV_VAL_INT))) {
				right = pval.ri;
				pv_value_destroy(&pval);
			} else {
				pv_value_destroy(&pval);
				return (op == DIFF_OP); /* not found or invalid type */
			}
			break;
		default:
			LM_CRIT("Invalid right operand (rtype: %d expr-op: %d lval: %ld)\n",
					rtype, op, left);
			return E_BUG;
	}

	switch(op) {
		case EQUAL_OP:
			return (long)left == (long)right;
		case DIFF_OP:
			return (long)left != (long)right;
		case GT_OP:
			return (long)left > (long)right;
		case LT_OP:
			return (long)left < (long)right;
		case GTE_OP:
			return (long)left >= (long)right;
		case LTE_OP:
			return (long)left <= (long)right;
		default:
			LM_CRIT("unknown operator: %d\n", op);
			return E_BUG;
	}
	return E_BUG;
}

/*
 * Compare given string "left" with right side of expression
 */
inline static int comp_str(int op, str *left, int rtype, union exp_op *r,
		struct sip_msg *msg, struct run_act_ctx *h)
{
	str *right;
	int_str val;
	str v;
	avp_t *avp;
	int ret;
	char backup;
	regex_t *re;
	unsigned int l;
	struct rvalue *rv;
	struct rval_cache rv_cache;
	pv_value_t pval;
	int destroy_pval;

	right = 0; /* warning fix */
	rv = 0;
	destroy_pval = 0;
	if(unlikely(op == NO_OP))
		return (left->s != 0);
	switch(rtype) {
		case AVP_ST:
			avp = search_avp_by_index(
					r->attr->type, r->attr->name, &val, r->attr->index);
			if(likely(avp && (avp->flags & AVP_VAL_STR)))
				right = &val.s;
			else
				return (op == DIFF_OP);
			break;
		case SELECT_ST:
			ret = run_select(&v, r->select, msg);
			if(unlikely(ret != 0))
				return (op == DIFF_OP); /* Not found or error */
			right = &v;
			break;
		case RVE_ST:
			rval_cache_init(&rv_cache);
			rv = rval_expr_eval(h, msg, r->param);
			if(unlikely(rv == 0))
				return (op == DIFF_OP); /* not found or error*/
			if(unlikely(rval_get_tmp_str(h, msg, &v, rv, 0, &rv_cache) < 0)) {
				goto error;
			}
			right = &v;
			break;
		case PVAR_ST:
			memset(&pval, 0, sizeof(pv_value_t));
			if(unlikely(pv_get_spec_value(msg, r->param, &pval) != 0)) {
				return (op == DIFF_OP); /* error, not found => false */
			}
			destroy_pval = 1;
			if(likely(pval.flags & PV_VAL_STR)) {
				right = &pval.rs;
			} else {
				pv_value_destroy(&pval);
				return (op == DIFF_OP); /* not found or invalid type */
			}
			break;
		case RE_ST:
			if(unlikely(op != MATCH_OP)) {
				LM_CRIT("Bad operator %d, ~= expected\n", op);
				goto error;
			}
			break;
		case STRING_ST: /* strings are stored as {asciiz, len } */
		case STR_ST:
			right = &r->str;
			break;
		case NUMBER_ST:
			/* "123" > 100 is not allowed by cfg.y rules
			 * but can happen as @select or $avp evaluation
			 * $test > 10
			 * the right operator MUST be number to do the conversion
			 */
			if(str2int(left, &l) < 0)
				goto error;
			return comp_num(op, l, rtype, r, msg, h);
		default:
			LM_CRIT("Bad type %d, string or RE expected\n", rtype);
			goto error;
	}

	ret = -1;
	switch(op) {
		case EQUAL_OP:
			if(left->len != right->len)
				return 0;
			ret = (strncasecmp(left->s, right->s, left->len) == 0);
			break;
		case DIFF_OP:
			if(left->len != right->len)
				return 1;
			ret = (strncasecmp(left->s, right->s, left->len) != 0);
			break;
		case MATCH_OP:
			/* this is really ugly -- we put a temporary zero-terminating
			 * character in the original string; that's because regexps
			 * take 0-terminated strings and our messages are not
			 * zero-terminated; it should not hurt as long as this function
			 * is applied to content of pkg mem, which is always the case
			 * with calls from route{}; the same goes for fline in
			 * reply_route{};
			 *
			 * also, the received function should always give us an extra
			 * character, into which we can put the 0-terminator now;
			 * an alternative would be allocating a new piece of memory,
			 * which might be too slow
			 * -jiri
			 *
			 * janakj: AVPs are zero terminated too so this is not problem
			 * either
			 */
			backup = left->s[left->len];
			left->s[left->len] = '\0';
			switch(rtype) {
				case AVP_ST:
				case SELECT_ST:
				case RVE_ST:
				case PVAR_ST:
				case STRING_ST:
				case STR_ST:
					/* we need to compile the RE on the fly */
					re = (regex_t *)pkg_malloc(sizeof(regex_t));
					if(re == 0) {
						PKG_MEM_CRITICAL;
						left->s[left->len] = backup;
						goto error;
					}
					if(regcomp(re, right->s,
							   REG_EXTENDED | REG_NOSUB | REG_ICASE)) {
						pkg_free(re);
						left->s[left->len] = backup;
						goto error;
					}
					ret = (regexec(re, left->s, 0, 0, 0) == 0);
					regfree(re);
					pkg_free(re);
					break;
				case RE_ST:
					ret = (regexec(r->re, left->s, 0, 0, 0) == 0);
					break;
				default:
					LM_CRIT("Bad operator type %d, for ~= \n", rtype);
					goto error;
			}
			left->s[left->len] = backup;
			break;
		default:
			LM_CRIT("unknown op %d\n", op);
			goto error;
	}
	if(rv) {
		rval_cache_clean(&rv_cache);
		rval_destroy(rv);
	}
	if(destroy_pval)
		pv_value_destroy(&pval);
	return ret;

error:
	if(rv) {
		rval_cache_clean(&rv_cache);
		rval_destroy(rv);
	}
	if(destroy_pval)
		pv_value_destroy(&pval);
	return (op == DIFF_OP) ? 1 : -1;
}


/* eval_elem helping function, returns str op param */
inline static int comp_string(int op, char *left, int rtype, union exp_op *r,
		struct sip_msg *msg, struct run_act_ctx *h)
{
	str s;

	s.s = left;
	s.len = strlen(left);
	return comp_str(op, &s, rtype, r, msg, h);
}


inline static int comp_avp(int op, avp_spec_t *spec, int rtype, union exp_op *r,
		struct sip_msg *msg, struct run_act_ctx *h)
{
	avp_t *avp;
	int_str val;
	union exp_op num_val;
	str tmp;
	unsigned int uval;

	if(spec->type & AVP_INDEX_ALL) {
		avp = search_first_avp(
				spec->type & ~AVP_INDEX_ALL, spec->name, NULL, NULL);
		return (avp != 0);
	}
	avp = search_avp_by_index(spec->type, spec->name, &val, spec->index);
	if(!avp)
		return (op == DIFF_OP);

	if(op == NO_OP) {
		if(avp->flags & AVP_VAL_STR) {
			return val.s.len != 0;
		} else {
			return val.n != 0;
		}
	}
	if(avp->flags & AVP_VAL_STR) {
		return comp_str(op, &val.s, rtype, r, msg, h);
	} else {
		switch(rtype) {
			case NUMBER_ST:
			case AVP_ST:
			case RVE_ST:
			case PVAR_ST:
				return comp_num(op, val.n, rtype, r, msg, h);
				break;
			case STRING_ST:
				tmp.s = r->string;
				tmp.len = strlen(r->string);
				if(str2int(&tmp, &uval) < 0) {
					LM_WARN("cannot convert string value to int (%s)\n",
							ZSW(r->string));
					goto error;
				}
				num_val.numval = uval;
				return comp_num(op, val.n, NUMBER_ST, &num_val, msg, h);
			case STR_ST:
				if(str2int(&r->str, &uval) < 0) {
					LM_WARN("cannot convert str value to int (%.*s)\n",
							r->str.len, ZSW(r->str.s));
					goto error;
				}
				num_val.numval = uval;
				return comp_num(op, val.n, NUMBER_ST, &num_val, msg, h);
			default:
				LM_CRIT("invalid type for numeric avp comparison (%d)\n",
						rtype);
				goto error;
		}
	}
error:
	return (op == DIFF_OP) ? 1 : -1;
}

/*
 * Left side of expression was select
 */
inline static int comp_select(int op, select_t *sel, int rtype, union exp_op *r,
		struct sip_msg *msg, struct run_act_ctx *h)
{
	int ret;
	str val;
	char empty_str = 0;

	ret = run_select(&val, sel, msg);
	if(ret != 0)
		return (op == DIFF_OP);

	if(op == NO_OP)
		return (val.len > 0);
	if(unlikely(val.len == 0)) {
		/* make sure the string pointer uses accessible memory range
		 * the comp_str function might dereference it
		 */
		val.s = &empty_str;
	}
	return comp_str(op, &val, rtype, r, msg, h);
}


inline static int comp_rve(int op, struct rval_expr *rve, int rtype,
		union exp_op *r, struct sip_msg *msg, struct run_act_ctx *h)
{
	long i;
	struct rvalue *rv;
	struct rvalue *rv1;
	struct rval_cache c1;

	rval_cache_init(&c1);
	if(unlikely(rval_expr_eval_rvlong(h, msg, &rv, &i, rve, &c1) < 0)) {
		LM_ERR("failure evaluating expression: bad type\n");
		i = 0; /* false */
		goto int_expr;
	}
	if(unlikely(rv)) {
		/* no int => str */
		rv1 = rval_convert(h, msg, RV_STR, rv, &c1);
		if(rv1 == NULL) {
			LM_ERR("failure converting expression\n");
			i = 0; /* false */
			goto int_expr;
		}
		i = comp_str(op, &rv1->v.s, rtype, r, msg, h);
		rval_destroy(rv1);
		rval_destroy(rv);
		rval_cache_clean(&c1);
		return i;
	}
	/* expr evaluated to int */
int_expr:
	rval_cache_clean(&c1);
	if(op == NO_OP)
		return !(!i); /* transform it into { 0, 1 } */
	return comp_num(op, i, rtype, r, msg, h);
}


inline static int comp_pvar(int op, pv_spec_t *pvs, int rtype, union exp_op *r,
		struct sip_msg *msg, struct run_act_ctx *h)
{
	pv_value_t pval;
	int ret;

	ret = 0;
	memset(&pval, 0, sizeof(pv_value_t));
	if(unlikely(pv_get_spec_value(msg, r->param, &pval) != 0)) {
		return 0; /* error, not found => false */
	}
	if(likely(pval.flags & PV_TYPE_INT)) {
		if(op == NO_OP)
			ret = !(!pval.ri);
		else
			ret = comp_num(op, pval.ri, rtype, r, msg, h);
	} else if((pval.flags == PV_VAL_NONE)
			  || (pval.flags & (PV_VAL_NULL | PV_VAL_EMPTY))) {
		if(op == NO_OP)
			ret = 0;
		else
			ret = comp_num(op, 0, rtype, r, msg, h);
	} else {
		ret = pval.rs.len != 0;
		if(op != NO_OP)
			ret = comp_num(op, ret, rtype, r, msg, h);
	}
	pv_value_destroy(&pval);
	return ret;
}


/* check_self wrapper -- it checks also for the op */
inline static int check_self_op(int op, str *s, unsigned short p)
{
	int ret;

	ret = check_self(s, p, 0);
	switch(op) {
		case EQUAL_OP:
		case MATCH_OP:
			break;
		case DIFF_OP:
			ret = (ret > 0) ? 0 : 1;
			break;
		default:
			LM_CRIT("invalid operator %d\n", op);
			ret = -1;
	}
	return ret;
}


/* eval_elem helping function, returns an op param */
inline static int comp_ip(int op, struct ip_addr *ip, int rtype,
		union exp_op *r, struct sip_msg *msg, struct run_act_ctx *ctx)
{
	struct hostent *he;
	char **h;
	int ret;
	str tmp;
	str *right;
	struct net net;
	union exp_op r_expop;
	struct rvalue *rv;
	struct rval_cache rv_cache;
	avp_t *avp;
	int_str val;
	pv_value_t pval;
	int destroy_pval;

	right = NULL; /* warning fix */
	rv = NULL;
	destroy_pval = 0;
	ret = -1;
	he = NULL; /* warning fix */
	switch(rtype) {
		case NET_ST:
			switch(op) {
				case EQUAL_OP:
					ret = (matchnet(ip, r->net) == 1);
					break;
				case DIFF_OP:
					ret = (matchnet(ip, r->net) != 1);
					break;
				default:
					goto error_op;
			}
			return ret; /* exit directly */
		case MYSELF_ST: /* check if it's one of our addresses*/
			tmp.s = ip_addr2a(ip);
			tmp.len = strlen(tmp.s);
			ret = check_self_op(op, &tmp, 0);
			return ret;
		case STRING_ST:
		case STR_ST:
			right = &r->str;
			break;
		case RVE_ST:
			rval_cache_init(&rv_cache);
			rv = rval_expr_eval(ctx, msg, r->param);
			if(unlikely(rv == 0))
				return (op == DIFF_OP); /* not found or error*/
			if(unlikely(rval_get_tmp_str(ctx, msg, &tmp, rv, 0, &rv_cache)
						< 0)) {
				goto error;
			}
			right = &tmp;
			break;
		case AVP_ST:
			/* we can still have AVP_ST due to the RVE optimisations
			   (if a RVE == $avp => rve wrapper removed => pure avp) */
			avp = search_avp_by_index(
					r->attr->type, r->attr->name, &val, r->attr->index);
			if(likely(avp && (avp->flags & AVP_VAL_STR)))
				right = &val.s;
			else
				return (op == DIFF_OP);
			break;
		case SELECT_ST:
			/* see AVP_ST comment and s/AVP_ST/SELECT_ST/ */
			ret = run_select(&tmp, r->select, msg);
			if(unlikely(ret != 0))
				return (op == DIFF_OP); /* Not found or error */
			right = &tmp;
			break;
		case PVAR_ST:
			/* see AVP_ST comment and s/AVP_ST/PVAR_ST/ */
			memset(&pval, 0, sizeof(pv_value_t));
			if(unlikely(pv_get_spec_value(msg, r->param, &pval) != 0)) {
				return (op == DIFF_OP); /* error, not found => false */
			}
			destroy_pval = 1;
			if(likely(pval.flags & PV_VAL_STR)) {
				right = &pval.rs;
			} else {
				pv_value_destroy(&pval);
				return (op == DIFF_OP); /* not found or invalid type */
			}
			break;
		case RE_ST:
			if(unlikely(op != MATCH_OP))
				goto error_op;
			/* 1: compare with ip2str*/
			ret = comp_string(op, ip_addr2a(ip), rtype, r, msg, ctx);
			if(likely(ret == 1))
				return ret;
			/* 3: (slow) rev dns the address
			* and compare with all the aliases
			* !!??!! review: remove this? */
			if(unlikely((received_dns & DO_REV_DNS)
						&& ((he = rev_resolvehost(ip)) != 0))) {
				/*  compare with primary host name */
				ret = comp_string(op, he->h_name, rtype, r, msg, ctx);
				/* compare with all the aliases */
				for(h = he->h_aliases; (ret != 1) && (*h); h++) {
					ret = comp_string(op, *h, rtype, r, msg, ctx);
				}
			} else {
				ret = 0;
			}
			return ret;
		default:
			LM_CRIT("invalid type for src_ip or dst_ip (%d)\n", rtype);
			return -1;
	}
	/* here "right" is set to the str we compare with */
	r_expop.str = *right;
	switch(op) {
		case EQUAL_OP:
			/* 0: try if ip or network (ip/mask) */
			if(mk_net_str(&net, right) == 0) {
				ret = (matchnet(ip, &net) == 1);
				break;
			}
			/* 2: resolve (name) & compare w/ all the ips */
			he = resolvehost(right->s);
			if(he == 0) {
				LM_DBG("could not resolve %s\n", r->str.s);
			} else if(he->h_addrtype == ip->af) {
				for(h = he->h_addr_list; (ret != 1) && (*h); h++) {
					ret = (memcmp(ip->u.addr, *h, ip->len) == 0);
				}
				if(ret == 1)
					break;
			}
			/* 3: (slow) rev dns the address
			 * and compare with all the aliases
			 * !!??!! review: remove this? */
			if(unlikely((received_dns & DO_REV_DNS)
						&& ((he = rev_resolvehost(ip)) != 0))) {
				/*  compare with primary host name */
				ret = comp_string(op, he->h_name, STR_ST, &r_expop, msg, ctx);
				/* compare with all the aliases */
				for(h = he->h_aliases; (ret != 1) && (*h); h++) {
					ret = comp_string(op, *h, STR_ST, &r_expop, msg, ctx);
				}
			} else {
				ret = 0;
			}
			break;
		case MATCH_OP:
			/* 0: try if ip or network (ip/mask)
			  (one should not use MATCH for that, but try to be nice)*/
			if(mk_net_str(&net, right) == 0) {
				ret = (matchnet(ip, &net) == 1);
				break;
			}
			/* 1: compare with ip2str (but only for =~)*/
			ret = comp_string(op, ip_addr2a(ip), STR_ST, &r_expop, msg, ctx);
			if(likely(ret == 1))
				break;
			/* 2: resolve (name) & compare w/ all the ips */
			he = resolvehost(right->s);
			if(he == 0) {
				LM_DBG("could not resolve %s\n", r->str.s);
			} else if(he->h_addrtype == ip->af) {
				for(h = he->h_addr_list; (ret != 1) && (*h); h++) {
					ret = (memcmp(ip->u.addr, *h, ip->len) == 0);
				}
				if(ret == 1)
					break;
			}
			/* 3: (slow) rev dns the address
			 * and compare with all the aliases
			 * !!??!! review: remove this? */
			if(unlikely((received_dns & DO_REV_DNS)
						&& ((he = rev_resolvehost(ip)) != 0))) {
				/*  compare with primary host name */
				ret = comp_string(op, he->h_name, STR_ST, &r_expop, msg, ctx);
				/* compare with all the aliases */
				for(h = he->h_aliases; (ret != 1) && (*h); h++) {
					ret = comp_string(op, *h, STR_ST, &r_expop, msg, ctx);
				}
			} else {
				ret = 0;
			}
			break;
		case DIFF_OP:
			ret = (comp_ip(EQUAL_OP, ip, STR_ST, &r_expop, msg, ctx) > 0) ? 0
																		  : 1;
			break;
		default:
			goto error_op;
	}
	if(rv) {
		rval_cache_clean(&rv_cache);
		rval_destroy(rv);
	}
	if(destroy_pval)
		pv_value_destroy(&pval);
	return ret;
error_op:
	LM_CRIT("invalid operator %d for type %d\n", op, rtype);
error:
	if(unlikely(rv)) {
		rval_cache_clean(&rv_cache);
		rval_destroy(rv);
	}
	if(destroy_pval)
		pv_value_destroy(&pval);
	return -1;
}


/* returns: 0/1 (false/true) or -1 on error */
inline static int eval_elem(
		struct run_act_ctx *h, struct expr *e, struct sip_msg *msg)
{
	struct sip_uri uri;
	int ret;
	struct onsend_info *snd_inf;
	struct ip_addr ip;
	ret = E_BUG;

	if(e->type != ELEM_T) {
		LM_CRIT("invalid type\n");
		goto error;
	}
	switch(e->l_type) {
		case METHOD_O:
			if(msg->first_line.type == SIP_REQUEST) {
				ret = comp_str(e->op, &msg->first_line.u.request.method,
						e->r_type, &e->r, msg, h);
			} else {
				if(parse_headers(msg, HDR_CSEQ_F, 0) != 0
						|| msg->cseq == NULL) {
					LM_ERR("cannot parse cseq header\n");
					goto error;
				}
				ret = comp_str(e->op, &get_cseq(msg)->method, e->r_type, &e->r,
						msg, h);
			}
			break;
		case URI_O:
			if(msg->new_uri.s) {
				if(e->r_type == MYSELF_ST) {
					if(parse_sip_msg_uri(msg) < 0)
						ret = -1;
					else
						ret = check_self_op(e->op, &msg->parsed_uri.host,
								GET_URI_PORT(&msg->parsed_uri));
				} else {
					ret = comp_str(
							e->op, &msg->new_uri, e->r_type, &e->r, msg, h);
				}
			} else {
				if(e->r_type == MYSELF_ST) {
					if(parse_sip_msg_uri(msg) < 0)
						ret = -1;
					else
						ret = check_self_op(e->op, &msg->parsed_uri.host,
								GET_URI_PORT(&msg->parsed_uri));
				} else {
					ret = comp_str(e->op, &msg->first_line.u.request.uri,
							e->r_type, &e->r, msg, h);
				}
			}
			break;

		case FROM_URI_O:
			if(parse_from_header(msg) != 0) {
				LM_ERR("bad or missing From: header\n");
				goto error;
			}
			if(e->r_type == MYSELF_ST) {
				if(parse_uri(get_from(msg)->uri.s, get_from(msg)->uri.len, &uri)
						< 0) {
					LM_ERR("bad uri in From:\n");
					goto error;
				}
				ret = check_self_op(e->op, &uri.host, GET_URI_PORT(&uri));
			} else {
				ret = comp_str(
						e->op, &get_from(msg)->uri, e->r_type, &e->r, msg, h);
			}
			break;

		case TO_URI_O:
			if((msg->to == 0)
					&& ((parse_headers(msg, HDR_TO_F, 0) == -1)
							|| (msg->to == 0))) {
				LM_ERR("bad or missing To: header\n");
				goto error;
			}
			/* to content is parsed automatically */
			if(e->r_type == MYSELF_ST) {
				if(parse_uri(get_to(msg)->uri.s, get_to(msg)->uri.len, &uri)
						< 0) {
					LM_ERR("bad uri in To:\n");
					goto error;
				}
				ret = check_self_op(e->op, &uri.host, GET_URI_PORT(&uri));
			} else {
				ret = comp_str(
						e->op, &get_to(msg)->uri, e->r_type, &e->r, msg, h);
			}
			break;

		case SRCIP_O:
			ret = comp_ip(e->op, &msg->rcv.src_ip, e->r_type, &e->r, msg, h);
			break;

		case DSTIP_O:
			ret = comp_ip(e->op, &msg->rcv.dst_ip, e->r_type, &e->r, msg, h);
			break;

		case SNDIP_O:
			snd_inf = get_onsend_info();
			if(likely(snd_inf && snd_inf->send_sock)) {
				ret = comp_ip(e->op, &snd_inf->send_sock->address, e->r_type,
						&e->r, msg, h);
			} else {
				BUG("eval_elem: snd_ip unknown (not in a onsend_route?)\n");
			}
			break;

		case TOIP_O:
			snd_inf = get_onsend_info();
			if(likely(snd_inf && snd_inf->to)) {
				su2ip_addr(&ip, snd_inf->to);
				ret = comp_ip(e->op, &ip, e->r_type, &e->r, msg, h);
			} else {
				BUG("eval_elem: to_ip unknown (not in a onsend_route?)\n");
			}
			break;

		case NUMBER_O:
			ret = !(!e->r.numval); /* !! to transform it in {0,1} */
			break;

		case ACTION_O:
			ret = run_actions(h, (struct action *)e->r.param, msg);
			if(ret <= 0)
				ret = 0;
			else
				ret = 1;
			break;

		case SRCPORT_O:
			ret = comp_num(
					e->op, (int)msg->rcv.src_port, e->r_type, &e->r, msg, h);
			break;

		case DSTPORT_O:
			ret = comp_num(
					e->op, (int)msg->rcv.dst_port, e->r_type, &e->r, msg, h);
			break;

		case SNDPORT_O:
			snd_inf = get_onsend_info();
			if(likely(snd_inf && snd_inf->send_sock)) {
				ret = comp_num(e->op, (int)snd_inf->send_sock->port_no,
						e->r_type, &e->r, msg, h);
			} else {
				BUG("eval_elem: snd_port unknown (not in a onsend_route?)\n");
			}
			break;

		case TOPORT_O:
			snd_inf = get_onsend_info();
			if(likely(snd_inf && snd_inf->to)) {
				ret = comp_num(e->op, (int)su_getport(snd_inf->to), e->r_type,
						&e->r, msg, h);
			} else {
				BUG("eval_elem: to_port unknown (not in a onsend_route?)\n");
			}
			break;

		case PROTO_O:
			ret = comp_num(e->op, msg->rcv.proto, e->r_type, &e->r, msg, h);
			break;

		case SNDPROTO_O:
			snd_inf = get_onsend_info();
			if(likely(snd_inf && snd_inf->send_sock)) {
				ret = comp_num(e->op, snd_inf->send_sock->proto, e->r_type,
						&e->r, msg, h);
			} else {
				BUG("eval_elem: snd_proto unknown (not in a onsend_route?)\n");
			}
			break;

		case AF_O:
			ret = comp_num(
					e->op, (int)msg->rcv.src_ip.af, e->r_type, &e->r, msg, h);
			break;

		case SNDAF_O:
			snd_inf = get_onsend_info();
			if(likely(snd_inf && snd_inf->send_sock)) {
				ret = comp_num(e->op, snd_inf->send_sock->address.af, e->r_type,
						&e->r, msg, h);
			} else {
				BUG("eval_elem: snd_af unknown (not in a onsend_route?)\n");
			}
			break;

		case MSGLEN_O:
			if((snd_inf = get_onsend_info()) != 0) {
				ret = comp_num(
						e->op, (int)snd_inf->len, e->r_type, &e->r, msg, h);
			} else {
				ret = comp_num(e->op, (int)msg->len, e->r_type, &e->r, msg, h);
			}
			break;

		case RETCODE_O:
			ret = comp_num(e->op, h->last_retcode, e->r_type, &e->r, msg, h);
			break;

		case AVP_O:
			ret = comp_avp(e->op, e->l.attr, e->r_type, &e->r, msg, h);
			break;

		case SELECT_O:
			ret = comp_select(e->op, e->l.select, e->r_type, &e->r, msg, h);
			break;

		case RVEXP_O:
			ret = comp_rve(e->op, e->l.param, e->r_type, &e->r, msg, h);
			break;

		case PVAR_O:
			ret = comp_pvar(e->op, e->l.param, e->r_type, &e->r, msg, h);
			break;

		case SELECT_UNFIXED_O:
			BUG("unexpected unfixed select operand %d\n", e->l_type);
			break;
			/*
	default:
		LM_CRIT("invalid operand %d\n", e->l_type);
*/
	}
	return ret;
error:
	return (e->op == DIFF_OP) ? 1 : -1;
}


/* ret= 1/0 (true/false) ,  -1 on error (evaluates as false)*/
int eval_expr(struct run_act_ctx *h, struct expr *e, struct sip_msg *msg)
{
	int ret;

	if(e->type == ELEM_T) {
		ret = eval_elem(h, e, msg);
	} else if(e->type == EXP_T) {
		switch(e->op) {
			case LOGAND_OP:
				ret = eval_expr(h, e->l.expr, msg);
				/* if error or false stop evaluating the rest */
				if(ret <= 0)
					break;
				ret = eval_expr(h, e->r.expr, msg); /*ret1 is 1*/
				break;
			case LOGOR_OP:
				ret = eval_expr(h, e->l.expr, msg);
				/* if true stop evaluating the rest */
				if(ret > 0)
					break;
				ret = eval_expr(h, e->r.expr, msg); /* ret1 is 0 */
				break;
			case NOT_OP:
				ret = eval_expr(h, e->l.expr, msg);
				ret = (ret > 0) ? 0 : 1;
				break;
			default:
				LM_CRIT("unknown op %d\n", e->op);
				ret = -1;
		}
	} else {
		LM_CRIT("unknown type %d\n", e->type);
		ret = -1;
	}
	return ret;
}


/* adds an action list to head; a must be null terminated (last a->next=0))*/
void push(struct action *a, struct action **head)
{
	struct action *t;
	if(*head == 0) {
		*head = a;
		return;
	}
	for(t = *head; t->next; t = t->next)
		;
	t->next = a;
}


int add_actions(struct action *a, struct action **head)
{
	int ret;

	LM_DBG("fixing actions...\n");
	if((ret = fix_actions(a)) != 0)
		goto error;
	push(a, head);
	return 0;

error:
	return ret;
}


static int fix_rl(struct route_list *rt)
{
	int i;
	int ret;

	for(i = 0; i < rt->idx; i++) {
		if(rt->rlist[i]) {
			if((ret = fix_actions(rt->rlist[i])) != 0) {
				return ret;
			}
		}
	}
	return 0;
}


/* fixes all action tables */
/* returns 0 if ok , <0 on error */
int fix_rls()
{
	int ret;

	if((ret = fix_rl(&main_rt)) != 0)
		return ret;
	if((ret = fix_rl(&onreply_rt)) != 0)
		return ret;
	if((ret = fix_rl(&failure_rt)) != 0)
		return ret;
	if((ret = fix_rl(&branch_rt)) != 0)
		return ret;
	if((ret = fix_rl(&onsend_rt)) != 0)
		return ret;
	if((ret = fix_rl(&event_rt)) != 0)
		return ret;

	return 0;
}


static void print_rl(struct route_list *rt, char *name)
{
	int j;

	for(j = 0; j < rt->entries; j++) {
		if(rt->rlist[j] == 0) {
			if((j == 0) && (rt == &main_rt))
				LM_DBG("WARNING: the main routing table is empty\n");
			continue;
		}
		LM_DBG("%s routing table %d:\n", name, j);
		print_actions(rt->rlist[j]);
		DBG("\n");
	}
}


/* debug function, prints routing tables */
void print_rls()
{
	if(!ksr_verbose_startup)
		return;
	print_rl(&main_rt, "");
	print_rl(&onreply_rt, "onreply");
	print_rl(&failure_rt, "failure");
	print_rl(&branch_rt, "branch");
	print_rl(&onsend_rt, "onsend");
	print_rl(&event_rt, "event");
}
