Merge commit 'origin/andrei/type_conversion'
[sip-router] / rvalue.c
index f75c6e9..b874ced 100644 (file)
--- a/rvalue.c
+++ b/rvalue.c
  * --------
  *  2008-12-01  initial version (andrei)
  *  2009-04-24  added support for defined, strempty, strlen (andrei)
+ *  2009-04-28  int and str automatic conversions: (int)undef=0,
+ *               (str)undef="", (int)""=0, (int)"123"=123, (int)"abc"=0
+ *              handle undef == expr, in function of the UNDEF_EQ_* defines.
+ *              (andrei)
+ */
+
+/* special defines:
+ *
+ *  UNDEF_EQ_* - how to behave when undef is on the right side of a generic
+ *               compare operator
+ *  UNDEF_EQ_ALWAYS_FALSE:  undef  == something  is always false
+ *  UNDEF_EQ_UNDEF_TRUE  :  undef == something false except for undef==undef
+ *                          which is true
+ *  no UNDEF_EQ* define  :  undef == expr => convert undef to typeof(expr)
+ *                          and perform normal ==. undef == undef will be
+ *                          converted to string and it will be true
+ *                          ("" == "")
+ * NOTE: expr == undef, with defined(expr) is always evaluated this way:
+         expr == (type_of(expr))undef
  */
 
 #include "rvalue.h"
@@ -111,7 +130,7 @@ void rve_destroy(struct rval_expr* rve)
 
 void rval_cache_clean(struct rval_cache* rvc)
 {
-       if (rvc->cache_type==RV_CACHE_PVAR){
+       if ((rvc->cache_type==RV_CACHE_PVAR) && (rvc->val_type!=RV_NONE)){
                pv_value_destroy(&rvc->c.pval);
        }
        rvc->cache_type=RV_CACHE_EMPTY;
@@ -261,7 +280,7 @@ struct rvalue* rval_new(enum rval_type t, union rval_val* v, int extra_size)
   * rval_cache_clean()'en when no longer needed.
   *
   * @param rv - target rvalue
-  * @param val_cache - value cache, might be filled if non-null, 
+  * @param val_cache - write-only: value cache, might be filled if non-null,
   *                    it _must_ be rval_cache_clean()'en when done.
   * @return - basic type or RV_NONE on error
   */
@@ -288,36 +307,39 @@ inline static enum rval_type rval_get_btype(struct run_act_ctx* h,
                case RV_PVAR:
                        if (likely(val_cache && val_cache->cache_type==RV_CACHE_EMPTY)){
                                pv=&val_cache->c.pval;
+                               val_cache->cache_type=RV_CACHE_PVAR;
                        }else{
                                val_cache=0;
                                pv=&tmp_pval;
                        }
                        memset(pv, 0, sizeof(tmp_pval));
                        if (likely(pv_get_spec_value(msg, &rv->v.pvs, pv)==0)){
-                               if (pv->flags & PV_VAL_STR){
-                                       if (unlikely(val_cache==0)) pv_value_destroy(pv);
-                                       else{
-                                               val_cache->cache_type=RV_CACHE_PVAR;
-                                               val_cache->val_type=RV_STR;
-                                       }
-                                       return RV_STR;
-                               }else if (pv->flags & PV_TYPE_INT){
-                                       if (unlikely(val_cache==0)) pv_value_destroy(pv);
-                                       else{
-                                               val_cache->cache_type=RV_CACHE_PVAR;
+                               if (pv->flags & PV_TYPE_INT){
+                                       if (likely(val_cache!=0))
                                                val_cache->val_type=RV_INT;
-                                       }
+                                       else
+                                               pv_value_destroy(pv);
                                        return RV_INT;
+                               }else if (pv->flags & PV_VAL_STR){
+                                       if (likely(val_cache!=0))
+                                               val_cache->val_type=RV_STR;
+                                       else
+                                               pv_value_destroy(pv);
+                                       return RV_STR;
                                }else{
                                        pv_value_destroy(pv);
+                                       if (likely(val_cache!=0))
+                                               val_cache->val_type=RV_NONE; /* undefined */
                                        goto error;
                                }
                        }else{
+                               if (likely(val_cache!=0))
+                                       val_cache->val_type=RV_NONE; /* undefined */
                                goto error;
                        }
                        break;
                case RV_AVP:
-                       if (likely(val_cache && val_cache==RV_CACHE_EMPTY)){
+                       if (likely(val_cache && val_cache->cache_type==RV_CACHE_EMPTY)){
                                ptype=&val_cache->val_type;
                                avpv=&val_cache->c.avp_val;
                                val_cache->cache_type=RV_CACHE_AVP;
@@ -337,7 +359,6 @@ inline static enum rval_type rval_get_btype(struct run_act_ctx* h,
                                }
                        }else{
                                *ptype=RV_NONE;
-                               if (val_cache) val_cache->cache_type=RV_CACHE_EMPTY;
                                goto error;
                        }
                        break;
@@ -390,6 +411,10 @@ enum rval_type rve_guess_type( struct rval_expr* rve)
                case RVE_LTE_OP:
                case RVE_EQ_OP:
                case RVE_DIFF_OP:
+               case RVE_IEQ_OP:
+               case RVE_IDIFF_OP:
+               case RVE_STREQ_OP:
+               case RVE_STRDIFF_OP:
                case RVE_IPLUS_OP:
                case RVE_STRLEN_OP:
                case RVE_STREMPTY_OP:
@@ -450,6 +475,10 @@ int rve_is_constant(struct rval_expr* rve)
                case RVE_LTE_OP:
                case RVE_EQ_OP:
                case RVE_DIFF_OP:
+               case RVE_IEQ_OP:
+               case RVE_IDIFF_OP:
+               case RVE_STREQ_OP:
+               case RVE_STRDIFF_OP:
                case RVE_PLUS_OP:
                case RVE_IPLUS_OP:
                case RVE_CONCAT_OP:
@@ -502,6 +531,10 @@ static int rve_op_unary(enum rval_expr_op op)
                case RVE_LTE_OP:
                case RVE_EQ_OP:
                case RVE_DIFF_OP:
+               case RVE_IEQ_OP:
+               case RVE_IDIFF_OP:
+               case RVE_STREQ_OP:
+               case RVE_STRDIFF_OP:
                case RVE_PLUS_OP:
                case RVE_IPLUS_OP:
                case RVE_CONCAT_OP:
@@ -564,6 +597,8 @@ int rve_check_type(enum rval_type* type, struct rval_expr* rve,
                case RVE_GTE_OP:
                case RVE_LT_OP:
                case RVE_LTE_OP:
+               case RVE_IEQ_OP:
+               case RVE_IDIFF_OP:
                case RVE_IPLUS_OP:
                        *type=RV_INT;
                        if (rve_check_type(&type1, rve->left.rve, bad_rve, bad_t, exp_t)){
@@ -644,6 +679,30 @@ int rve_check_type(enum rval_type* type, struct rval_expr* rve,
                                }
                        }
                        break;
+               case RVE_STREQ_OP:
+               case RVE_STRDIFF_OP:
+                       *type=RV_INT;
+                       if (rve_check_type(&type1, rve->left.rve, bad_rve, bad_t, exp_t)){
+                               if (rve_check_type(&type2, rve->right.rve, bad_rve, bad_t,
+                                                                       exp_t)){
+                                       if ((type2!=type1) && (type1!=RV_NONE) &&
+                                                       (type2!=RV_NONE) &&
+                                                       !(type1==RV_STR && type2==RV_INT)){
+                                               if (bad_rve) *bad_rve=rve->right.rve;
+                                               if (bad_t) *bad_t=type2;
+                                               if (exp_t) *exp_t=type1;
+                                               return 0;
+                                       }
+                                       if (type1==RV_INT){
+                                               if (bad_rve) *bad_rve=rve->left.rve;
+                                               if (bad_t) *bad_t=type1;
+                                               if (exp_t) *exp_t=RV_STR;
+                                               return 0;
+                                       }
+                                       return 1;
+                               }
+                       }
+                       break;
                case RVE_STRLEN_OP:
                case RVE_STREMPTY_OP:
                case RVE_DEFINED_OP:
@@ -659,6 +718,11 @@ int rve_check_type(enum rval_type* type, struct rval_expr* rve,
                        }
                        break;
                case RVE_NONE_OP:
+               default:
+                       BUG("unexpected rve op %d\n", rve->op);
+                       if (bad_rve) *bad_rve=rve;
+                       if (bad_t) *bad_t=RV_NONE;
+                       if (exp_t) *exp_t=RV_STR;
                        break;
        }
        return 0;
@@ -668,6 +732,15 @@ int rve_check_type(enum rval_type* type, struct rval_expr* rve,
 
 /** get the integer value of an rvalue.
   * *i=(int)rv
+  * if rv == undefined select, avp or pvar, return 0.
+  * if an error occurs while evaluating a select, avp or pvar, behave as
+  * for the undefined case (and return success).
+  * @param h - script context handle
+  * @param msg - sip msg
+  * @param i   - pointer to int, where the conversion result will be stored
+  * @param rv   - rvalue to be converted
+  * @param cache - cached rv value (read-only), can be 0
+  *
   * @return 0 on success, \<0 on error and EXPR_DROP on drop
  */
 int rval_get_int(struct run_act_ctx* h, struct sip_msg* msg,
@@ -677,12 +750,19 @@ int rval_get_int(struct run_act_ctx* h, struct sip_msg* msg,
        avp_t* r_avp;
        int_str avp_val;
        pv_value_t pval;
+       str tmp;
+       str* s;
+       int r;
+       int destroy_pval;
        
+       destroy_pval=0;
+       s=0;
        switch(rv->type){
                case RV_INT:
                        *i=rv->v.l;
                        break;
                case RV_STR:
+                       s=&rv->v.s;
                        goto rv_str;
                case RV_BEXPR:
                        *i=eval_expr(h, rv->v.bexpr, msg);
@@ -698,36 +778,51 @@ int rval_get_int(struct run_act_ctx* h, struct sip_msg* msg,
                                *i=0;
                        break;
                case RV_SEL:
+                       r=run_select(&tmp, &rv->v.sel, msg);
+                       if (unlikely(r!=0)){
+                               if (r<0)
+                                       goto eval_error;
+                               else /* i>0  => undefined */
+                                       goto undef;
+                       }
+                       s=&tmp;
                        goto rv_str;
                case RV_AVP:
                        if (unlikely(cache && cache->cache_type==RV_CACHE_AVP)){
                                if (likely(cache->val_type==RV_INT)){
                                        *i=cache->c.avp_val.n;
-                               }else if (cache->val_type==RV_STR)
+                               }else if (cache->val_type==RV_STR){
+                                       s=&cache->c.avp_val.s;
                                        goto rv_str;
-                               else goto error;
+                               }else if (cache->val_type==RV_NONE)
+                                       goto undef;
+                               else goto error_cache;
                        }else{
                                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 (unlikely(r_avp->flags & AVP_VAL_STR)){
+                                               s=&avp_val.s;
                                                goto rv_str;
                                        }else{
                                                *i=avp_val.n;
                                        }
                                }else{
-                                       goto error;
+                                       goto undef;
                                }
                        }
                        break;
                case RV_PVAR:
                        if (unlikely(cache && cache->cache_type==RV_CACHE_PVAR)){
-                               if (likely((cache->val_type==RV_INT) || 
+                               if (likely((cache->val_type==RV_INT) ||
                                                                (cache->c.pval.flags & PV_VAL_INT))){
                                        *i=cache->c.pval.ri;
-                               }else if (cache->val_type==RV_STR)
+                               }else if (cache->val_type==RV_STR){
+                                       s=&cache->c.pval.rs;
                                        goto rv_str;
-                               else goto error;
+                               }else if (cache->val_type==RV_NONE)
+                                       goto undef;
+                               else goto error_cache;
                        }else{
                                memset(&pval, 0, sizeof(pval));
                                if (likely(pv_get_spec_value(msg, &rv->v.pvs, &pval)==0)){
@@ -735,14 +830,17 @@ int rval_get_int(struct run_act_ctx* h, struct sip_msg* msg,
                                                *i=pval.ri;
                                                pv_value_destroy(&pval);
                                        }else if (likely(pval.flags & PV_VAL_STR)){
-                                               pv_value_destroy(&pval);
+                                               destroy_pval=1; /* we must pv_value_destroy() later*/
+                                               s=&pval.rs;
                                                goto rv_str;
                                        }else{
+                                               /* no PV_VAL_STR and no PV_VAL_INT => undef
+                                                  (PV_VAL_NULL) */
                                                pv_value_destroy(&pval);
-                                               goto error;
+                                               goto undef;
                                        }
                                }else{
-                                       goto error;
+                                       goto eval_error;
                                }
                        }
                        break;
@@ -751,10 +849,27 @@ int rval_get_int(struct run_act_ctx* h, struct sip_msg* msg,
                        goto error;
        }
        return 0;
+undef:
+eval_error: /* same as undefined */
+       /* handle undefined => result 0, return success */
+       *i=0;
+       return 0;
+error_cache:
+       BUG("invalid cached value:cache type %d, value type %d\n",
+                       cache?cache->cache_type:0, cache?cache->val_type:0);
 rv_str:
-       /* rv is of string type => error */
-       /* ERR("string in int expression\n"); */
+       /* rv is of string type => try to convert it to int */
+       /* if "" => 0 (most likely case) */
+       if (likely(s->len==0)) *i=0;
+       else if (unlikely(str2sint(s, i)!=0)){
+               /* error converting to int => non numeric => 0 */
+               *i=0;
+       }
+       if (destroy_pval)
+               pv_value_destroy(&pval);
+       return 0;
 error:
+       *i=0;
        return -1;
 }
 
@@ -762,6 +877,9 @@ error:
 
 /** get the string value of an rv in a tmp variable
   * *s=(str)rv
+  * if rv == undefined select, avp or pvar, return "".
+  * if an error occurs while evaluating a select, avp or pvar, behave as
+  * for the undefined case (and return success).
   * The result points either to a temporary string or inside
   * new_cache. new_cache must be non zero, initialized previously,
   * and it _must_ be rval_cache_clean(...)'ed when done.
@@ -771,9 +889,9 @@ error:
   * @param h - script context handle
   * @param msg - sip msg
   * @param tmpv - str return value (pointer to a str struct that will be
-  *               be filled.
+  *               be filled with the conversion result)
   * @param rv   - rvalue to be converted
-  * @param cache - cached rv value (read-only)
+  * @param cache - cached rv value (read-only), can be 0
   * @param tmp_cache - used for temporary storage (so that tmpv will not
   *                 point to possible freed data), it must be non-null,
   *                 initialized and cleaned afterwards.
@@ -814,10 +932,9 @@ int rval_get_tmp_str(struct run_act_ctx* h, struct sip_msg* msg,
                        i=run_select(tmpv, &rv->v.sel, msg);
                        if (unlikely(i!=0)){
                                if (i<0){
-                                       goto error;
-                               }else { /* i>0 */
-                                       tmpv->s="";
-                                       tmpv->len=0;
+                                       goto eval_error;
+                               }else { /* i>0  => undefined */
+                                       goto undef;
                                }
                        }
                        break;
@@ -828,7 +945,9 @@ int rval_get_tmp_str(struct run_act_ctx* h, struct sip_msg* msg,
                                }else if (cache->val_type==RV_INT){
                                        i=cache->c.avp_val.n;
                                        tmpv->s=int2str(i, &tmpv->len);
-                               }else goto error;
+                               }else if (cache->val_type==RV_NONE){
+                                       goto undef;
+                               }else goto error_cache;
                        }else{
                                r_avp = search_avp_by_index(rv->v.avps.type, rv->v.avps.name,
                                                                                        &tmp_cache->c.avp_val,
@@ -842,9 +961,7 @@ int rval_get_tmp_str(struct run_act_ctx* h, struct sip_msg* msg,
                                                i=tmp_cache->c.avp_val.n;
                                                tmpv->s=int2str(i, &tmpv->len);
                                        }
-                               }else{
-                                       goto error;
-                               }
+                               }else goto undef;
                        }
                        break;
                case RV_PVAR:
@@ -854,7 +971,9 @@ int rval_get_tmp_str(struct run_act_ctx* h, struct sip_msg* msg,
                                }else if (cache->val_type==RV_INT){
                                        i=cache->c.pval.ri;
                                        tmpv->s=int2str(i, &tmpv->len);
-                               }else goto error;
+                               }else if (cache->val_type==RV_NONE){
+                                       goto undef;
+                               }else goto error_cache;
                        }else{
                                memset(&tmp_cache->c.pval, 0, sizeof(tmp_cache->c.pval));
                                if (likely(pv_get_spec_value(msg, &rv->v.pvs,
@@ -871,11 +990,13 @@ int rval_get_tmp_str(struct run_act_ctx* h, struct sip_msg* msg,
                                                pv_value_destroy(&tmp_cache->c.pval);
                                                tmpv->s=int2str(i, &tmpv->len);
                                        }else{
+                                               /* no PV_VAL_STR and no PV_VAL_INT => undef
+                                                  (PV_VAL_NULL) */
                                                pv_value_destroy(&tmp_cache->c.pval);
-                                               goto error;
+                                               goto undef;
                                        }
                                }else{
-                                       goto error;
+                                       goto eval_error;
                                }
                        }
                        break;
@@ -884,7 +1005,18 @@ int rval_get_tmp_str(struct run_act_ctx* h, struct sip_msg* msg,
                        goto error;
        }
        return 0;
+undef:
+eval_error: /* same as undefined */
+       /* handle undefined => result "", return success */
+       tmpv->s="";
+       tmpv->len=0;
+       return 0;
+error_cache:
+       BUG("invalid cached value:cache type %d, value type %d\n",
+                       cache?cache->cache_type:0, cache?cache->val_type:0);
 error:
+       tmpv->s="";
+       tmpv->len=0;
        return -1;
 }
 
@@ -1047,9 +1179,11 @@ inline static int int_intop2(int* res, enum rval_expr_op op, int v1, int v2)
                        *res=v1 <= v2;
                        break;
                case RVE_EQ_OP:
+               case RVE_IEQ_OP:
                        *res=v1 == v2;
                        break;
                case RVE_DIFF_OP:
+               case RVE_IDIFF_OP:
                        *res=v1 != v2;
                        break;
                case RVE_CONCAT_OP:
@@ -1068,12 +1202,19 @@ inline static int int_intop2(int* res, enum rval_expr_op op, int v1, int v2)
 inline static int bool_strop2( enum rval_expr_op op, int* res,
                                                                str* s1, str* s2)
 {
-       if (s1->len!=s2->len)
-               *res= op==RVE_DIFF_OP;
-       else if (memcmp(s1->s, s2->s, s1->len)==0)
-               *res= op==RVE_EQ_OP;
-       else
-               *res= op==RVE_DIFF_OP;
+       switch(op){
+               case RVE_EQ_OP:
+               case RVE_STREQ_OP:
+                       *res= (s1->len==s2->len) && (memcmp(s1->s, s2->s, s1->len)==0);
+                       break;
+               case RVE_DIFF_OP:
+               case RVE_STRDIFF_OP:
+                       *res= (s1->len!=s2->len) || (memcmp(s1->s, s2->s, s1->len)!=0);
+                       break;
+               default:
+                       BUG("rv unsupported intop %d\n", op);
+                       return -1;
+       }
        return 0;
 }
 
@@ -1386,9 +1527,11 @@ error:
 /** checks if rv is defined.
  * @param res - set to the result 1 - defined, 0 not defined
  * @return 0 on success, -1 on error
- * Can use cached rvalues (c1).
- * Note: a rv can be undefined if it's an undefined avp or pvar or
+ * Can use cached rvalues (cache).
+ * Note: a rv can be undefined if it's an undefined avp or pvar or select or
  * if it's NONE
+ * Note2: an error in the avp, pvar or select search is equivalent to 
+ *  undefined (and it's not reported)
  */
 inline static int rv_defined(struct run_act_ctx* h,
                                                 struct sip_msg* msg, int* res,
@@ -1397,13 +1540,21 @@ inline static int rv_defined(struct run_act_ctx* h,
        avp_t* r_avp;
        int_str avp_val;
        pv_value_t pval;
+       str tmp;
        
        *res=1;
        switch(rv->type){
+               case RV_SEL:
+                       if (unlikely(cache && cache->cache_type==RV_CACHE_SELECT)){
+                               *res=(cache->val_type!=RV_NONE);
+                       }else
+                               /* run select returns 0 on success, -1 on error and >0 on 
+                                  undefined. error is considered undefined */
+                               *res=(run_select(&tmp, &rv->v.sel, msg)==0);
+                       break;
                case RV_AVP:
                        if (unlikely(cache && cache->cache_type==RV_CACHE_AVP)){
-                               if (cache->val_type==RV_NONE)
-                                       *res=0;
+                               *res=(cache->val_type!=RV_NONE);
                        }else{
                                r_avp = search_avp_by_index(rv->v.avps.type, rv->v.avps.name,
                                                                                        &avp_val, rv->v.avps.index);
@@ -1415,8 +1566,7 @@ inline static int rv_defined(struct run_act_ctx* h,
                case RV_PVAR:
                        /* PV_VAL_NULL or pv_get_spec_value error => undef */
                        if (unlikely(cache && cache->cache_type==RV_CACHE_PVAR)){
-                               if (cache->val_type==RV_NONE)
-                                       *res=0;
+                               *res=(cache->val_type!=RV_NONE);
                        }else{
                                memset(&pval, 0, sizeof(pval));
                                if (likely(pv_get_spec_value(msg, &rv->v.pvs, &pval)==0)){
@@ -1467,7 +1617,7 @@ int rval_expr_eval_int( struct run_act_ctx* h, struct sip_msg* msg,
                                                int* res, struct rval_expr* rve)
 {
        int i1, i2, ret;
-       struct rval_cache c1;
+       struct rval_cache c1, c2;
        struct rvalue* rv1;
        struct rvalue* rv2;
        
@@ -1495,6 +1645,8 @@ int rval_expr_eval_int( struct run_act_ctx* h, struct sip_msg* msg,
                case RVE_GTE_OP:
                case RVE_LT_OP:
                case RVE_LTE_OP:
+               case RVE_IEQ_OP:
+               case RVE_IDIFF_OP:
                        if (unlikely(
                                        (ret=rval_expr_eval_int(h, msg, &i1, rve->left.rve)) <0) )
                                break;
@@ -1534,10 +1686,14 @@ int rval_expr_eval_int( struct run_act_ctx* h, struct sip_msg* msg,
                case RVE_EQ_OP:
                case RVE_DIFF_OP:
                        /* if left is string, eval left & right as string and
-                          use string diff, else eval as int */
+                        *   use string diff.
+                        * if left is int eval as int using int diff
+                        * if left is undef, look at right and convert to right type
+                        */
                        rval_cache_init(&c1);
                        if (unlikely( (ret=rval_expr_eval_rvint(h, msg, &rv1, &i1,
                                                                                                        rve->left.rve, &c1))<0)){
+                               /* error */
                                rval_cache_clean(&c1);
                                break;
                        }
@@ -1546,20 +1702,67 @@ int rval_expr_eval_int( struct run_act_ctx* h, struct sip_msg* msg,
                                rval_cache_clean(&c1);
                                if (unlikely( (ret=rval_expr_eval_int(h, msg, &i2,
                                                                                                                rve->right.rve)) <0) )
-                                       break;
+                                       break;  /* error */
                                ret=int_intop2(res, rve->op, i1, i2);
                        }else{
-                               if (unlikely((rv2=rval_expr_eval(h, msg,
-                                                                                                       rve->right.rve))==0)){
+                               /* not int => str or undef */
+                               /* check for undefined left operand */
+                               if (unlikely( c1.cache_type!=RV_CACHE_EMPTY &&
+                                                               c1.val_type==RV_NONE)){
+#ifdef UNDEF_EQ_ALWAYS_FALSE
+                                       /* undef == something  always false
+                                          undef != something  always true*/
+                                       ret=(rve->op==RVE_DIFF_OP);
+#elif defined UNDEF_EQ_UNDEF_TRUE
+                                       /* undef == something defined always false
+                                          undef == undef true */
+                                       if (int_rve_defined(h, msg, &i2, rve->right.rve)<0){
+                                               /* error */
+                                               rval_cache_clean(&c1);
+                                               rval_destroy(rv1);
+                                               break;
+                                       }
+                                       ret=(!i2) ^ (rve->op==RVE_DIFF_OP);
+#else  /* ! UNDEF_EQ_* */
+                                       /*  undef == val
+                                        *  => convert to (type_of(val)) (undef) == val */
+                                       rval_cache_init(&c2);
+                                       if (unlikely( (ret=rval_expr_eval_rvint(h, msg, &rv2, &i2,
+                                                                                                       rve->right.rve, &c2))<0)){
+                                               /* error */
+                                               rval_cache_clean(&c1);
+                                               rval_cache_clean(&c2);
+                                               rval_destroy(rv1);
+                                               break;
+                                       }
+                                       if (rv2==0){
+                                               /* int */
+                                               ret=int_intop2(res, rve->op, 0 /* undef */, i2);
+                                       }else{
+                                               /* str or undef */
+                                               ret=rval_str_lop2(h, msg, res, rve->op, rv1, &c1,
+                                                                                       rv2, &c2);
+                                               rval_cache_clean(&c2);
+                                               rval_destroy(rv2);
+                                       }
+#endif /* UNDEF_EQ_* */
+                                       rval_cache_clean(&c1);
                                        rval_destroy(rv1);
+                               }else{ 
+                                       /* left value == defined and != int => str
+                                        * => lval == (str) val */
+                                       if (unlikely((rv2=rval_expr_eval(h, msg,
+                                                                                                               rve->right.rve))==0)){
+                                               /* error */
+                                               rval_destroy(rv1);
+                                               rval_cache_clean(&c1);
+                                               break;
+                                       }
+                                       ret=rval_str_lop2(h, msg, res, rve->op, rv1, &c1, rv2, 0);
                                        rval_cache_clean(&c1);
-                                       ret=-1;
-                                       break;
+                                       rval_destroy(rv1);
+                                       rval_destroy(rv2);
                                }
-                               ret=rval_str_lop2(h, msg, res, rve->op, rv1, &c1, rv2, 0);
-                               rval_cache_clean(&c1);
-                               rval_destroy(rv1);
-                               rval_destroy(rv2);
                        }
                        break;
 #if 0
@@ -1586,6 +1789,21 @@ int rval_expr_eval_int( struct run_act_ctx* h, struct sip_msg* msg,
                case RVE_DEFINED_OP:
                        ret=int_rve_defined(h, msg, res, rve->left.rve);
                        break;
+               case RVE_STREQ_OP:
+               case RVE_STRDIFF_OP:
+                       if (unlikely((rv1=rval_expr_eval(h, msg, rve->left.rve))==0)){
+                               ret=-1;
+                               break;
+                       }
+                       if (unlikely((rv2=rval_expr_eval(h, msg, rve->right.rve))==0)){
+                               rval_destroy(rv1);
+                               ret=-1;
+                               break;
+                       }
+                       ret=rval_str_lop2(h, msg, res, rve->op, rv1, 0, rv2, 0);
+                       rval_destroy(rv1);
+                       rval_destroy(rv2);
+                       break;
                case RVE_STRLEN_OP:
                case RVE_STREMPTY_OP:
                        if (unlikely((rv1=rval_expr_eval(h, msg, rve->left.rve))==0)){
@@ -1666,7 +1884,11 @@ int rval_expr_eval_rvint(                           struct run_act_ctx* h,
                case RVE_LTE_OP:
                case RVE_EQ_OP:
                case RVE_DIFF_OP:
+               case RVE_IEQ_OP:
+               case RVE_IDIFF_OP:
                case RVE_IPLUS_OP:
+               case RVE_STREQ_OP:
+               case RVE_STRDIFF_OP:
                case RVE_STRLEN_OP:
                case RVE_STREMPTY_OP:
                case RVE_DEFINED_OP:
@@ -1764,7 +1986,11 @@ struct rvalue* rval_expr_eval(struct run_act_ctx* h, struct sip_msg* msg,
                case RVE_LTE_OP:
                case RVE_EQ_OP:
                case RVE_DIFF_OP:
+               case RVE_IEQ_OP:
+               case RVE_IDIFF_OP:
                case RVE_IPLUS_OP:
+               case RVE_STREQ_OP:
+               case RVE_STRDIFF_OP:
                case RVE_STRLEN_OP:
                case RVE_STREMPTY_OP:
                case RVE_DEFINED_OP:
@@ -1820,6 +2046,7 @@ struct rvalue* rval_expr_eval(struct run_act_ctx* h, struct sip_msg* msg,
                                        }
                                        break;
                                case RV_STR:
+                               case RV_NONE:
                                        rv2=rval_expr_eval(h, msg, rve->right.rve);
                                        if (unlikely(rv2==0)){
                                                ERR("rval expression evaluation failed\n");
@@ -1830,9 +2057,6 @@ struct rvalue* rval_expr_eval(struct run_act_ctx* h, struct sip_msg* msg,
                                        break;
                                default:
                                        BUG("rv unsupported basic type %d\n", type);
-                               case RV_NONE:
-                                       rval_cache_clean(&c1);
-                                       goto error;
                        }
                        rval_cache_clean(&c1);
                        break;
@@ -2019,6 +2243,10 @@ struct rval_expr* mk_rval_expr2(enum rval_expr_op op, struct rval_expr* rve1,
                case RVE_IPLUS_OP:
                case RVE_EQ_OP:
                case RVE_DIFF_OP:
+               case RVE_IEQ_OP:
+               case RVE_IDIFF_OP:
+               case RVE_STREQ_OP:
+               case RVE_STRDIFF_OP:
                case RVE_CONCAT_OP:
                        break;
                default:
@@ -2056,6 +2284,9 @@ static int rve_op_is_assoc(enum rval_expr_op op)
                case RVE_MINUS_OP:
                        return 0;
                case RVE_PLUS_OP:
+                       /* the generic plus is not assoc, e.g.
+                          "a" + 1 + "2" => "a12" in one case and "a3" in the other */
+                       return 0;
                case RVE_IPLUS_OP:
                case RVE_CONCAT_OP:
                case RVE_MUL_OP:
@@ -2071,6 +2302,10 @@ static int rve_op_is_assoc(enum rval_expr_op op)
                case RVE_LTE_OP:
                case RVE_EQ_OP:
                case RVE_DIFF_OP:
+               case RVE_IEQ_OP:
+               case RVE_IDIFF_OP:
+               case RVE_STREQ_OP:
+               case RVE_STRDIFF_OP:
                        return 0;
        }
        return 0;
@@ -2079,7 +2314,7 @@ static int rve_op_is_assoc(enum rval_expr_op op)
 
 
 /** returns true if the operator is commutative. */
-static int rve_op_is_commutative(enum rval_expr_op op, enum rval_type type)
+static int rve_op_is_commutative(enum rval_expr_op op)
 {
        switch(op){
                case RVE_NONE_OP:
@@ -2096,23 +2331,34 @@ static int rve_op_is_commutative(enum rval_expr_op op, enum rval_type type)
                case RVE_MINUS_OP:
                        return 0;
                case RVE_PLUS_OP:
-                       return type==RV_INT; /* commutative only for INT*/
+                       /* non commut. when diff. type 
+                          (e.g 1 + "2" != "2" + 1 ) => non commut. in general
+                          (specific same type versions are covered by IPLUS & CONCAT) */
+                       return 0;
                case RVE_IPLUS_OP:
                case RVE_MUL_OP:
                case RVE_BAND_OP:
                case RVE_BOR_OP:
-                       return 1;
                case RVE_LAND_OP:
                case RVE_LOR_OP:
+               case RVE_IEQ_OP:
+               case RVE_IDIFF_OP:
+               case RVE_STREQ_OP:
+               case RVE_STRDIFF_OP:
                        return 1;
                case RVE_GT_OP:
                case RVE_GTE_OP:
                case RVE_LT_OP:
                case RVE_LTE_OP:
-               case RVE_EQ_OP:
-               case RVE_DIFF_OP:
                case RVE_CONCAT_OP:
                        return 0;
+               case RVE_DIFF_OP:
+               case RVE_EQ_OP:
+                       /* non. commut. in general, only for same type e.g.:
+                          "" == 0  diff. 0 == "" ( "" == "0" and 0 == 0)
+                          same type versions are covered by IEQ, IDIFF, STREQ, STRDIFF
+                        */
+                       return 0 /* asymmetrical undef handling */;
        }
        return 0;
 }
@@ -2421,8 +2667,19 @@ static int rve_opt_01(struct rval_expr* rve, enum rval_type rve_type)
                        case RVE_PLUS_OP:
                        case RVE_IPLUS_OP:
                                /* we must make sure that this is an int PLUS
-                                  (because "foo"+0 is valid => "foo0") */
-                               if ((i==0) && ((op==RVE_IPLUS_OP)||(rve_type==RV_INT))){
+                                  (because "foo"+0 is valid => "foo0")
+                                 Even if overall type is RV_INT, it's still not safe
+                                 to optimize a generic PLUS: 0 + $v is not always equivalent
+                                 to $v (e.g. 0+"1" ==1 != "1") => optimize only if
+                                 IPLUS (always safe since it converts to int first) or
+                                 if generic PLUS and result is integer (rve_type) and
+                                 expression is of the form $v+ct (and not ct+$v).
+                                 TODO: dropping PLUS_OP all together and relying on the
+                                  optimizer replacing safe PLUS_OP with IPLUS_OP or CONCAT_OP
+                                  will simplify things.
+                                */
+                               if ((i==0) && ((op==RVE_IPLUS_OP)||
+                                                       (rve_type==RV_INT && ct_rve==rve->right.rve))){
                                        /* $v +  0 -> $v
                                         *  0 + $v -> $v */
                                        rve_destroy(ct_rve);
@@ -2484,6 +2741,7 @@ static int rve_opt_01(struct rval_expr* rve, enum rval_type rve_type)
                                }
                                break;
                        case RVE_EQ_OP:
+                       case RVE_STREQ_OP:
                                if (rv->v.s.len==0){
                                        /* $v == "" -> strempty($v) 
                                           "" == $v -> strempty ($v) */
@@ -2600,6 +2858,25 @@ static int rve_optimize(struct rval_expr* rve)
                                                rve->fpos.e_line, rve->fpos.e_col);
                        }
                }
+               /* e1 EQ_OP e2 -> change op if we know e1 basic type
+                  e1 DIFF_OP e2 -> change op if we know e2 basic type */
+               if (rve->op==RVE_EQ_OP || rve->op==RVE_DIFF_OP){
+                       l_type=rve_guess_type(rve->left.rve);
+                       if (l_type==RV_INT){
+                               rve->op=(rve->op==RVE_EQ_OP)?RVE_IEQ_OP:RVE_IDIFF_OP;
+                               DBG("FIXUP RVE (%d,%d-%d,%d): changed ==/!= into interger"
+                                               " ==/!=\n",
+                                               rve->fpos.s_line, rve->fpos.s_col,
+                                               rve->fpos.e_line, rve->fpos.e_col);
+                       }else if (l_type==RV_STR){
+                               rve->op=RVE_CONCAT_OP;
+                               rve->op=(rve->op==RVE_EQ_OP)?RVE_STREQ_OP:RVE_STRDIFF_OP;
+                               DBG("FIXUP RVE (%d,%d-%d,%d): changed ==/!= into string"
+                                               " ==/!=\n",
+                                               rve->fpos.s_line, rve->fpos.s_col,
+                                               rve->fpos.e_line, rve->fpos.e_col);
+                       }
+               }
                
                /* $v * 0 => 0; $v * 1 => $v (for *, /, &, |, &&, ||, +, -) */
                if (rve_opt_01(rve, type)==1){
@@ -2651,7 +2928,7 @@ static int rve_optimize(struct rval_expr* rve)
                                                                trv->v.s.len, trv->v.s.s, rve->op);
                                        ret=1;
                                }else if (rve_is_constant(rve->left.rve->left.rve) &&
-                                                       rve_op_is_commutative(rve->op, type)){
+                                                       rve_op_is_commutative(rve->op)){
                                        /* op(op(a, $v), b) => op(op(a, b), $v) */
                                        /* rv= op(a, b) */
                                        tmp_rve.op=rve->op;
@@ -2693,7 +2970,7 @@ static int rve_optimize(struct rval_expr* rve)
                        if ((rve->op==rve->right.rve->op) && rve_op_is_assoc(rve->op)){
                                /* op(a, op(...)) */
                                if (rve_is_constant(rve->right.rve->right.rve) &&
-                                               rve_op_is_commutative(rve->op, type)){
+                                               rve_op_is_commutative(rve->op)){
                                        /* op(a, op($v, b)) => op(op(a, b), $v) */
                                        /* rv= op(a, b) */
                                        tmp_rve.op=rve->op;
@@ -2824,6 +3101,10 @@ int fix_rval_expr(void** p)
                case RVE_IPLUS_OP:
                case RVE_EQ_OP:
                case RVE_DIFF_OP:
+               case RVE_IEQ_OP:
+               case RVE_IDIFF_OP:
+               case RVE_STREQ_OP:
+               case RVE_STRDIFF_OP:
                case RVE_CONCAT_OP:
                        ret=fix_rval_expr((void**)&rve->left.rve);
                        if (ret<0) return ret;