lvalue.c
1c7e485e
 /* 
  * Copyright (C) 2008 iptelorg GmbH
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
  * copyright notice and this permission notice appear in all copies.
  *
  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
b8f7648c
 
1c7e485e
 /**
  * @file 
b8f7648c
  * @brief SIP-router core :: lvalues (assignment)
  * \ingroup core
  * Module: \ref core
1c7e485e
  */
b8f7648c
 
1c7e485e
 /* 
  * History:
  * --------
  *  2008-11-30  initial version (andrei)
0789fd24
  *  2009-04-24  delete avps after finding their new value and not before
  *               (fixed $avp=$avp)
  *              when assigning something undefined (e.g. non-existing avp),
  *              delete the lvalue (similar to perl)  (andrei)
1c7e485e
  */
 
1d0661db
 
1c7e485e
 #include "lvalue.h"
 #include "dprint.h"
 #include "route.h"
 
 
 
b8f7648c
 /**
  * @brief eval rve and assign the result to an avp
  * 
  * eval rve and assign the result to an avp, lv->lv.avp=eval(rve)
  * based on do_action() ASSIGN_T.
1c7e485e
  * @param h  - script context
  * @param msg - sip msg
  * @param lv - lvalue
b8f7648c
  * @param rv - rvalue expression
1c7e485e
  * @return >= 0 on success (expr. bool value), -1 on error
  */
 inline static int lval_avp_assign(struct run_act_ctx* h, struct sip_msg* msg,
 									struct lvalue* lv, struct rvalue* rv)
 {
 	avp_spec_t* avp;
 	avp_t* r_avp;
 	avp_t* avp_mark;
 	pv_value_t pval;
 	int_str value;
 	unsigned short flags;
 	struct search_state st;
2891fdcf
 	int ret, v, destroy_pval;
0789fd24
 	int avp_add;
 
 #if 0
 	#define AVP_ASSIGN_NOVAL() \
 		/* unknown value => reset the avp in function of its type */ \
 		flags=avp->type; \
 		if (flags & AVP_VAL_STR){ \
 			value.s.s=""; \
 			value.s.len=0; \
 		}else{ \
 			value.n=0; \
 		}
 #endif
 	#define AVP_ASSIGN_NOVAL() \
 		/* no value => delete avp */ \
 		avp_add=0
1c7e485e
 	
 	destroy_pval=0;
2891fdcf
 	flags = 0;
1c7e485e
 	avp=&lv->lv.avps;
 	ret=0;
0789fd24
 	avp_add=1;
 	
1c7e485e
 	switch(rv->type){
 		case RV_NONE:
 			BUG("non-intialized rval / rval expr \n");
0789fd24
 			/* unknown value => reset the avp in function of its type */
 			flags=avp->type;
 			AVP_ASSIGN_NOVAL();
 			ret=-1;
 			break;
1c7e485e
 		case RV_INT:
 			value.n=rv->v.l;
a4ade2b9
 			flags=avp->type & ~AVP_VAL_STR;
1c7e485e
 			ret=!(!value.n);
 			break;
 		case RV_STR:
 			value.s=rv->v.s;
 			flags=avp->type | AVP_VAL_STR;
 			ret=(value.s.len>0);
 			break;
 		case RV_ACTION_ST:
a4ade2b9
 			flags=avp->type & ~AVP_VAL_STR;
65b64bca
 			if (rv->v.action) {
28a88287
 				value.n=run_actions_safe(h, rv->v.action, msg);
65b64bca
 				h->run_flags &= ~(RETURN_R_F|BREAK_R_F); /* catch return &
 														    break in expr*/
 			} else
1c7e485e
 				value.n=-1;
 			ret=value.n;
 			break;
 		case RV_BEXPR: /* logic/boolean expr. */
 			value.n=eval_expr(h, rv->v.bexpr, msg);
 			if (unlikely(value.n<0)){
 				if (value.n==EXPR_DROP) /* hack to quit on drop */
 					goto drop;
 				WARN("error in expression\n");
 				value.n=0; /* expr. is treated as false */
 			}
a4ade2b9
 			flags=avp->type & ~AVP_VAL_STR;
1c7e485e
 			ret=value.n;
 			break;
 		case RV_SEL:
0789fd24
 			flags=avp->type|AVP_VAL_STR;
1c7e485e
 			v=run_select(&value.s, &rv->v.sel, msg);
 			if (unlikely(v!=0)){
0789fd24
 				value.s.s="";
 				value.s.len=0;
1c7e485e
 				if (v<0){
 					ret=-1;
0789fd24
 					break;
 				} /* v>0 */
1c7e485e
 			}
 			ret=(value.s.len>0);
 			break;
 		case RV_AVP:
 			avp_mark=0;
 			if (unlikely((rv->v.avps.type & AVP_INDEX_ALL) == AVP_INDEX_ALL)){
0789fd24
 				/* special case: add the value to the avp */
1c7e485e
 				r_avp = search_first_avp(rv->v.avps.type, rv->v.avps.name,
 											&value, &st);
 				while(r_avp){
a4ade2b9
 					/* We take only the val type  from the source avp
 					 * and reset the class, track flags and name type  */
 					flags=(avp->type & ~(AVP_INDEX_ALL|AVP_VAL_STR)) | 
 							(r_avp->flags & ~(AVP_CLASS_ALL|AVP_TRACK_ALL|
 												AVP_NAME_STR|AVP_NAME_RE));
1c7e485e
 					if (add_avp_before(avp_mark, flags, avp->name, value)<0){
 						ERR("failed to assign avp\n");
 						ret=-1;
 						goto error;
 					}
 					/* move the mark, so the next found AVP will come before
 					   the one currently added so they will have the same 
 					   order as in the source list */
 					if (avp_mark) avp_mark=avp_mark->next;
 					else
 						avp_mark=search_first_avp(flags, avp->name, 0, 0);
 					r_avp=search_next_avp(&st, &value);
 				}
 				ret=1;
 				goto end;
 			}else{
0789fd24
 				/* normal case, value is replaced */
1c7e485e
 				r_avp = search_avp_by_index(rv->v.avps.type, rv->v.avps.name,
 											&value, rv->v.avps.index);
 				if (likely(r_avp)){
a4ade2b9
 					/* take only the val type from the source avp
 					 * and reset the class, track flags and name type  */
 					flags=(avp->type & ~AVP_VAL_STR) | (r_avp->flags & 
 								~(AVP_CLASS_ALL|AVP_TRACK_ALL|AVP_NAME_STR|
 									AVP_NAME_RE));
1c7e485e
 					ret=1;
 				}else{
0789fd24
 					/* on error, keep the type of the assigned avp, but
 					   reset it to an empty value */
 					AVP_ASSIGN_NOVAL();
 					ret=0;
 					break;
1c7e485e
 				}
 			}
 			break;
 		case RV_PVAR:
 			memset(&pval, 0, sizeof(pval));
 			if (likely(pv_get_spec_value(msg, &rv->v.pvs, &pval)==0)){
 				destroy_pval=1;
a4ade2b9
 				if (pval.flags & PV_TYPE_INT){
1c7e485e
 					value.n=pval.ri;
 					ret=value.n;
a4ade2b9
 					flags=avp->type & ~AVP_VAL_STR;
 				}else if (pval.flags & PV_VAL_STR){
 					value.s=pval.rs;
 					ret=(value.s.len>0);
 					flags=avp->type | AVP_VAL_STR;
1c7e485e
 				}else if (pval.flags==PV_VAL_NONE ||
 							(pval.flags & (PV_VAL_NULL|PV_VAL_EMPTY))){
0789fd24
 					AVP_ASSIGN_NOVAL();
1c7e485e
 					ret=0;
 				}
 			}else{
 				/* non existing pvar */
0789fd24
 				/* on error, keep the type of the assigned avp, but
 				   reset it to an empty value */
 				AVP_ASSIGN_NOVAL();
1c7e485e
 				ret=0;
 			}
 			break;
 	}
0789fd24
 	/* If the left attr was specified without indexing brackets delete
 	 * existing AVPs before adding the new value */
 	delete_avp(avp->type, avp->name);
 	if (avp_add && (add_avp(flags & ~AVP_INDEX_ALL, avp->name, value) < 0)) {
1c7e485e
 		ERR("failed to assign value to avp\n");
 		goto error;
 	}
 end:
 	if (destroy_pval) pv_value_destroy(&pval);
 	return ret;
 error:
 	if (destroy_pval) pv_value_destroy(&pval);
 	return -1;
 drop:
 	if (destroy_pval) pv_value_destroy(&pval);
 	return EXPR_DROP;
 }
 
 
 
b8f7648c
 /**
  * @brief eval rve and assign the result to a pvar
1c7e485e
  *
b8f7648c
  * eval rve and assign the result to a pvar, lv->lv.pvar=eval(rve)
  * based on do_action() ASSIGN_T.
1c7e485e
  * @param h  - script context
  * @param msg - sip msg
  * @param lv - lvalue
b8f7648c
  * @param rv - rvalue expression
1c7e485e
  * @return >= 0 on success (expr. bool value), -1 on error
  */
 inline static int lval_pvar_assign(struct run_act_ctx* h, struct sip_msg* msg,
 									struct lvalue* lv, struct rvalue* rv)
 {
 	pv_spec_t* pvar;
 	pv_value_t pval;
 	avp_t* r_avp;
 	int_str avp_val;
 	int ret;
 	int v;
 	int destroy_pval;
 	
0789fd24
 	#define PVAR_ASSIGN_NOVAL() \
 		/* no value found => "undefine" */ \
 		pv_get_null(msg, 0, &pval)
 	
1c7e485e
 	destroy_pval=0;
 	pvar=&lv->lv.pvs;
 	if (unlikely(!pv_is_w(pvar))){
 		ERR("read only pvar\n");
 		goto error;
 	}
 	memset(&pval, 0, sizeof(pval));
 	ret=0;
 	switch(rv->type){
 		case RV_NONE:
 			BUG("non-intialized rval / rval expr \n");
0789fd24
 			PVAR_ASSIGN_NOVAL();
 			ret=-1;
 			break;
1c7e485e
 		case RV_INT:
 			pval.flags=PV_TYPE_INT|PV_VAL_INT;
 			pval.ri=rv->v.l;
 			ret=!(!pval.ri);
 			break;
 		case RV_STR:
 			pval.flags=PV_VAL_STR;
 			pval.rs=rv->v.s;
 			ret=(pval.rs.len>0);
 			break;
 		case RV_ACTION_ST:
 			pval.flags=PV_TYPE_INT|PV_VAL_INT;
65b64bca
 			if (rv->v.action) {
28a88287
 				pval.ri=run_actions_safe(h, rv->v.action, msg);
65b64bca
 				h->run_flags &= ~(RETURN_R_F|BREAK_R_F); /* catch return &
 														    break in expr*/
 			} else
1c7e485e
 				pval.ri=0;
1f698797
 			ret=!(!pval.ri);
1c7e485e
 			break;
 		case RV_BEXPR: /* logic/boolean expr. */
 			pval.flags=PV_TYPE_INT|PV_VAL_INT;
 			pval.ri=eval_expr(h, rv->v.bexpr, msg);
 			if (unlikely(pval.ri<0)){
 				if (pval.ri==EXPR_DROP) /* hack to quit on drop */
 					goto drop;
 				WARN("error in expression\n");
 				pval.ri=0; /* expr. is treated as false */
 			}
1f698797
 			ret=!(!pval.ri);
1c7e485e
 			break;
 		case RV_SEL:
 			pval.flags=PV_VAL_STR;
 			v=run_select(&pval.rs, &rv->v.sel, msg);
 			if (unlikely(v!=0)){
0789fd24
 				pval.flags|=PV_VAL_EMPTY;
 				pval.rs.s="";
 				pval.rs.len=0;
1c7e485e
 				if (v<0){
 					ret=-1;
0789fd24
 					break;
1c7e485e
 				}
 			}
1f698797
 			ret=(pval.rs.len>0);
1c7e485e
 			break;
 		case RV_AVP:
 				r_avp = search_avp_by_index(rv->v.avps.type, rv->v.avps.name,
 											&avp_val, rv->v.avps.index);
 				if (likely(r_avp)){
 					if (r_avp->flags & AVP_VAL_STR){
 						pval.flags=PV_VAL_STR;
 						pval.rs=avp_val.s;
 						ret=(pval.rs.len>0);
 					}else{
 						pval.flags=PV_TYPE_INT|PV_VAL_INT;
 						pval.ri=avp_val.n;
 						ret=!(!pval.ri);
 					}
 				}else{
0789fd24
 					PVAR_ASSIGN_NOVAL();
 					ret=0; /* avp not defined (valid case) */
 					break;
1c7e485e
 				}
 			break;
 		case RV_PVAR:
 			if (likely(pv_get_spec_value(msg, &rv->v.pvs, &pval)==0)){
 				destroy_pval=1;
a4ade2b9
 				if (pval.flags & PV_TYPE_INT){
1c7e485e
 					ret=!(!pval.ri);
a4ade2b9
 				}else if (pval.flags & PV_VAL_STR){
 					ret=(pval.rs.len>0);
1c7e485e
 				}else{
0789fd24
 					/* no value / not defined (e.g. avp) -> keep the flags */
 					ret=0;
1c7e485e
 				}
 			}else{
 				ERR("non existing right pvar\n");
0789fd24
 				PVAR_ASSIGN_NOVAL();
1c7e485e
 				ret=-1;
 			}
 			break;
 	}
 	if (unlikely(pvar->setf(msg, &pvar->pvp, EQ_T, &pval)<0)){
 		ERR("setting pvar failed\n");
 		goto error;
 	}
 	if (destroy_pval) pv_value_destroy(&pval);
 	return ret;
 error:
 	if (destroy_pval) pv_value_destroy(&pval);
 	return -1;
 drop:
 	if (destroy_pval) pv_value_destroy(&pval);
 	return EXPR_DROP;
 }
 
 
 
 /** eval rve and assign the result to lv
  * lv=eval(rve)
  *
  * @param h  - script context
  * @param msg - sip msg
  * @param lv - lvalue
  * @param rve - rvalue expression
  * @return >= 0 on success (expr. bool value), -1 on error
  */
 int lval_assign(struct run_act_ctx* h, struct sip_msg* msg, 
 				struct lvalue* lv, struct rval_expr* rve)
 {
 	struct rvalue* rv;
 	int ret;
 	
 	ret=0;
 	rv=rval_expr_eval(h, msg, rve);
 	if (unlikely(rv==0)){
c744f71c
 		ERR("rval expression evaluation failed (%d,%d-%d,%d)\n",
 				rve->fpos.s_line, rve->fpos.s_col,
 				rve->fpos.e_line, rve->fpos.e_col);
1c7e485e
 		goto error;
 	}
 	switch(lv->type){
 		case LV_NONE:
bf7cacc5
 			BUG("uninitialized/invalid lvalue (%d) (cfg line: %d)\n",
 					lv->type, rve->fpos.s_line);
1c7e485e
 			goto error;
 		case LV_AVP:
 			ret=lval_avp_assign(h, msg, lv, rv);
 			break;
 		case LV_PVAR:
 			ret=lval_pvar_assign(h, msg, lv, rv);
 			break;
 	}
bf7cacc5
 	if (unlikely(ret<0)){
536e5901
 		ERR("assignment failed at pos: (%d,%d-%d,%d)\n",
bf7cacc5
 			rve->fpos.s_line, rve->fpos.s_col,
 			rve->fpos.e_line, rve->fpos.e_col);
 	}
1c7e485e
 	rval_destroy(rv);
 	return ret;
 error:
 	if (rv) rval_destroy(rv);
 	return -1;
 }