Merge commit 'origin/ser_core_cvs'
authorAndrei Pelinescu-Onciul <andrei@iptel.org>
Fri, 20 Mar 2009 20:47:32 +0000 (21:47 +0100)
committerAndrei Pelinescu-Onciul <andrei@iptel.org>
Fri, 20 Mar 2009 20:47:32 +0000 (21:47 +0100)
* commit 'origin/ser_core_cvs':
  tcp: minor optimization
  sercmd: minor makefile fix
  tcp internal: send timeout is now kept only in ticks
  core: typo fix
  tcp: typo & minor optimization
  Rename Contact parameter select 'method' to 'methods'
  Rename Contact parameter 'method' to 'methods'
  make install: avoid re-linking lib dependent modules
  build system: avoid libraries re-compiling
  fixing missing $ in ser_mysql.sh script
  Debian packaging - fixing path to mysql sql files in my_create.sh script
  The fixup function prototypes of the config variables have been
  Strip, prefix, rewriteuser, ... all the SET_* actions preserve the
  core: check & fix Content-Length when sending on tcp
  tcp: config option for the async write block size
  tcp: config option for the read buffer size
  tcp: dyn. config fix for tcp_con_lifetime
  tcp: diff. connect timeout for async & states cleanup

Conflicts:
Makefile.rules
action.c
cfg.lex
cfg.y
route_struct.h

15 files changed:
1  2 
Makefile.rules
NEWS
action.c
cfg.lex
cfg.y
core_cmd.c
dprint.c
dprint.h
modules/tm/t_fwd.c
msg_translator.c
parser/parse_param.c
parser/parse_param.h
receive.c
route_struct.h
select_core.c

diff --combined Makefile.rules
@@@ -6,7 -6,8 +6,8 @@@
  #
  
  #
- # Uses: NAME, ALLDEP, CC, CFLAGS, DEFS, INCLUDES, LIBS, MKDEP, auto_gen, 
+ # Uses: NAME, ALLDEP, CC, CFLAGS, C_DEFS, DEFS, C_INCLUDES, INCLUDES, LIBS, 
+ #       MKDEP, auto_gen, 
  # auto_gen_others, depends, objs, extra_objs, static_modules, 
  # static_modules_path, LD_RPATH
  # (all this must  be defined previously!,  see Makefile.defs & Makefile)
  #              automatically build listed SER_LIBS if needed (andrei)
  #  2008-06-23  automatically rebuild if make time defines or includes
  #              changed (via makecfg.lst)
- #
+ #  2009-03-10  support for C_DEFS and C_INCLUDES (DEFS and INCLUDES are now
+ #              used only for "temporary" defines/includes inside modules or
+ #              libs, C_DEFS and C_INCLUDES are used for the common stuff)
+ #              (andrei)
  
  
  # check if the saved cfg corresponds with the current one
  # (if not rebuild everything)
  ifeq (,$(filter $(nodep_targets),$(MAKECMDGOALS)))
  -include makecfg.lst
 -ifneq ($(strip $(C_DEFS) $(DEFS)), $(strip $(CFG_DEFS)))
 -#$(warning different defs: <$(strip $(C_DEFS) $(DEFS))> != )
 +# if trying  to build a lib automatically and the lib is already compiled,
 +# don't rebuild it if the only differences in DEFS or INCLUDES are covered
 +# by LIB_NOREBUILD_DEFS/LIB_NOREBUILD_INCLUDES
- LIB_NOREBUILD_DEFS+= -DMOD_NAME% -D%_MOD_INTERFACE -DMOD_INTERFACE_% -DSR_%
++LIB_NOREBUILD_DEFS=
 +
 +# don't rebuild if the differences are covered by NOREBUILD_DEFS or 
 +# NOREBUILD_INCLUDES
- ifneq ($(filter-out $(NOREBUILD_DEFS),$(strip $(DEFS))), $(strip $(CFG_DEFS)))
++ifneq ($(strip $(filter-out $(NOREBUILD_DEFS),\
++              $(C_DEFS) $(DEFS))),$(strip $(CFG_DEFS)))
 +#$(warning different defs: <$(strip $(DEFS))> != )
  #$(warning               : <$(strip $(CFG_DEFS))>)
  $(shell rm -f makecfg.lst)
  endif
- ifneq ($(filter-out $(NOREBUILD_INCLUDES), $(strip $(INCLUDES))),\
-               $(strip $(CFG_INCLUDES)))
 -ifneq ($(strip $(C_INCLUDES) $(INCLUDES)), $(strip $(CFG_INCLUDES)))
++ifneq ($(strip $(filter-out $(NOREBUILD_INCLUDES),\
++                      $(C_INCLUDES) $(INCLUDES))),$(strip $(CFG_INCLUDES)))
  $(shell rm -f makecfg.lst)
  endif
  endif
@@@ -49,10 -45,10 +54,10 @@@ ALLDEP+=makecfg.ls
  
  #implicit rules
  %.o:%.c  $(ALLDEP)
-       $(CC) $(CFLAGS) $(INCLUDES) $(DEFS) -c $< -o $@
+       $(CC) $(CFLAGS) $(C_INCLUDES) $(INCLUDES) $(C_DEFS) $(DEFS) -c $< -o $@
  
  %.d: %.c $(ALLDEP)
-       @set -e; $(MKDEP) $(CFLAGS) $(INCLUDES) $(DEFS) $< \
+       @set -e; $(MKDEP) $(CFLAGS) $(C_INCLUDES) $(INCLUDES) $(C_DEFS) $(DEFS) $<\
            |  sed 's#\(\($(*D)/\)\{0,1\}$(*F)\)\.o[ :]*#$*.o $@ : #g' > $@; \
            test -s $@ || ( rm -f $@; false )
  
@@@ -66,7 -62,7 +71,7 @@@ ifneq (,$(filter install install% %inst
  lib_compile_for_install=yes
  expected_lib_ipath=$(lib_target)
  else
- lib_compile_for_install=no
+ lib_compile_for_install=$(compile_for_install)
  # function: expected_lib_ipath ser_lib_dir
  expected_lib_ipath=$(1)
  endif
@@@ -103,9 -99,7 +108,9 @@@ ALL_LIBS+=$(foreach l, $(SER_LIBS), -L$
  $(NAME): librpath.lst $(SER_LIBS_DEPS)
  
  $(SER_LIBS_DEPS): FORCE
 -      $(MAKE) -wC $(dir $@)  compile_for_install=$(lib_compile_for_install)
 +      @$(MAKE) -wC $(dir $@)  compile_for_install=$(lib_compile_for_install) \
 +              NOREBUILD_DEFS="$(NOREBUILD_DEFS) $(LIB_NOREBUILD_DEFS)" \
 +              NOREBUILD_INCLUDES="$(NOREBUILD_INCLUDES) $(LIB_NOREBUILD_INCLUDES)"
  
  .PHONY: FORCE
  FORCE:
@@@ -144,10 -138,10 +149,11 @@@ librpath.lst: $(ALLDEP
        @echo LIB_RPATH_LST:=$(SER_RPATH_LST) >librpath.lst
  
  makecfg.lst:
-       @echo CFG_DEFS:=$(subst ',\', $(subst ",\", \
-               $(filter-out $(NOREBUILD_DEFS), $(strip $(DEFS))))) >>$@
-       @echo CFG_INCLUDES:=$(subst ',\', $(subst ",\", \
-               $(filter-out $(NOREBUILD_INCLUDES), $(strip $(INCLUDES))))) >>$@
 -      @echo CFG_DEFS:=\
 -              $(subst ',\', $(subst ",\", $(strip $(C_DEFS) $(DEFS)))) >>$@
 -      @echo CFG_INCLUDES:=\
 -              $(subst ',\', $(subst ",\", $(strip $(C_INCLUDES) $(INCLUDES)))) >>$@
++      @echo CFG_DEFS:=$(subst ',\', $(subst ",\",$(strip \
++                      $(filter-out $(NOREBUILD_DEFS), $(C_DEFS) $(DEFS))))) >>$@
++      @echo CFG_INCLUDES:=$(subst ',\', $(subst ",\",$(strip \
++                      $(filter-out $(NOREBUILD_INCLUDES),\
++                              $(C_INCLUDES) $(INCLUDES))))) >>$@
  .PHONY: all
  all: $(NAME) modules
  
diff --combined NEWS
--- 1/NEWS
--- 2/NEWS
+++ b/NEWS
@@@ -1,28 -1,8 +1,28 @@@
 -Release notes for SIP Express Router (ser)
 +Release notes for SIP Router (sr)
  ***********************************************
  
  $Id$
  
 +sip-router changes
 +
 +core:
 +  - support for dual module interfaces: ser and kamailio
 +config script changes:
 +  - script mode can be switched between ser compatible, kamailio compatible
 +    and max compatibility (compatible with both as much as possible), using
 +      #!SER
 +      #!KAMAILIO
 +      #!OPENSER
 +      #!ALL
 +      #!MAXCOMPAT
 +    where #!KAMAILIO is equivalent with #!OPENSER and #!ALL with #!MAXCOMPAT
 +  - support for kamailio style pvars
 +  - C-like switch()/case (integer only)
 +  - while()
 +  - max_while_loops - maximum iterations allowed for a while, can be changed
 +    at runtime. Default 100.
 +
 +
  
  
  2.1.0 changes
@@@ -256,6 -236,20 +256,20 @@@ core
                 between the short name and long name in cache as CNAME record
  
  new config variables:
+   tcp_rd_buf_size = buffer size used for tcp reads.
+                     A high buffer size increases performance on server with few
+                     connections and lot of traffic on them, but also increases
+                      memory consumption (so for lots of connection is better 
+                     to use a low value). Note also that this value limits the
+                     maximum datagram size that can be received over tcp.
+                     Default: 4096, can be changed at runtime.
+   tcp_wq_blk_size = block size used for tcp async writes. It should be big
+                     enough to hold a few datagrams. If it's smaller then a
+                     datagram (in fact a tcp write()) size, it will be rounded
+                     up. It has no influenced on the number of datagrams 
+                     queued (for that see tcp_conn_wq_max or tcp_wq_max).
+                     It has mostly debugging and testing value (can be ignored).
+                     Default: 2100 (~ 2 INVITEs), can be changed at runtime.
    tcp_no_connect = yes/no - disable connects, ser will only accept new 
                       connections, it will never try to open new ones.
                       Default: no, can be changed at runtime.
diff --combined action.c
+++ b/action.c
@@@ -46,8 -46,6 +46,8 @@@
   *              (andrei)
   *  2007-06-14  run_actions & do_action need a ctx or handle now, no more
   *               static vars (andrei)
 + *  2008-11-18  support for variable parameter module functions (andrei)
 + *  2008-12-03  use lvalues/rvalues for assignments (andrei)
   *  2008-12-17  added UDP_MTU_TRY_PROTO_T (andrei)
   */
  
@@@ -65,7 -63,6 +65,7 @@@
  #include "parser/msg_parser.h"
  #include "parser/parse_uri.h"
  #include "ut.h"
 +#include "lvalue.h"
  #include "sr_module.h"
  #include "mem/mem.h"
  #include "globals.h"
@@@ -78,7 -75,6 +78,7 @@@
  #ifdef USE_SCTP
  #include "sctp_server.h"
  #endif
 +#include "switch.h"
  
  #include <sys/types.h>
  #include <sys/socket.h>
@@@ -106,23 -102,15 +106,23 @@@ int do_action(struct run_act_ctx* h, st
        struct dest_info dst;
        char* tmp;
        char *new_uri, *end, *crt;
 +      void* f;
        int len;
        int user;
        struct sip_uri uri, next_hop;
        struct sip_uri *u;
        unsigned short port;
 -      unsigned short flags;
 -      int_str name, value;
        str* dst_host;
 +      int i, flags;
 +      struct switch_cond_table* sct;
 +      struct switch_jmp_table*  sjt;
 +      struct rval_expr* rve;
 +      struct match_cond_table* mct;
 +      struct rvalue* rv;
 +      struct rvalue* rv1;
 +      struct rval_cache c1;
 +      str s;
+       int orig_p2t;
  
        /* reset the value of error to E_UNSPEC so avoid unknowledgable
           functions to return with error (status<0) and not setting it
                                ret=E_BUG;
                                break;
                        }
 -                      LOG_(a->val[0].u.number, "<script>: ", "%s", a->val[1].u.string);
 +                      LOG_(DEFAULT_FACILITY, a->val[0].u.number, "<script>: ", "%s", 
 +                               a->val[1].u.string);
                        ret=1;
                        break;
  
                        /*ret=((ret=run_actions(rlist[a->val[0].u.number], msg))<0)?ret:1;*/
                        ret=run_actions(h, main_rt.rlist[a->val[0].u.number], msg);
                        h->last_retcode=ret;
 -                      h->run_flags&=~RETURN_R_F; /* absorb returns */
 +                      h->run_flags&=~(RETURN_R_F|BREAK_R_F); /* absorb return & break */
                        break;
                case EXEC_T:
                        if (a->val[0].type!=STRING_ST){
                case SET_HOST_T:
                case SET_HOSTPORT_T:
                case SET_HOSTPORTTRANS_T:
 +              case SET_HOSTALL_T:
                case SET_USER_T:
                case SET_USERPASS_T:
                case SET_PORT_T:
                case PREFIX_T:
                case STRIP_T:
                case STRIP_TAIL_T:
+               case SET_USERPHONE_T:
                                user=0;
                                if (a->type==STRIP_T || a->type==STRIP_TAIL_T) {
                                        if (a->val[0].type!=NUMBER_ST) {
                                                LOG(L_CRIT, "BUG: do_action: bad set*() type %d\n",
                                                        a->val[0].type);
+                                               ret=E_BUG;
                                                break;
                                        }
-                               } else if (a->val[0].type!=STRING_ST){
-                                       LOG(L_CRIT, "BUG: do_action: bad set*() type %d\n",
+                               } else if (a->type!=SET_USERPHONE_T) {
+                                       if (a->val[0].type!=STRING_ST) {
+                                               LOG(L_CRIT, "BUG: do_action: bad set*() type %d\n",
                                                        a->val[0].type);
-                                       ret=E_BUG;
-                                       break;
+                                               ret=E_BUG;
+                                               break;
+                                       }
                                }
                                if (a->type==SET_URI_T){
                                        if (msg->new_uri.s) {
                                        ret=1;
                                        break;
                                }
-                               if (msg->parsed_uri_ok==0) {
+                               if ((msg->parsed_uri_ok==0) || ((uri.flags & URI_SIP_USER_PHONE)!=0)) {
                                        if (msg->new_uri.s) {
                                                tmp=msg->new_uri.s;
                                                len=msg->new_uri.len;
                                                tmp=msg->first_line.u.request.uri.s;
                                                len=msg->first_line.u.request.uri.len;
                                        }
+                                       /* don't convert sip:user=phone to tel, otherwise we loose parameters */
+                                       orig_p2t=phone2tel;
+                                       phone2tel=0;
+                                       msg->parsed_uri_ok=0;
                                        if (parse_uri(tmp, len, &uri)<0){
+                                               phone2tel=orig_p2t;
                                                LOG(L_ERR, "ERROR: do_action: bad uri <%s>, dropping"
                                                                        " packet\n", tmp);
                                                ret=E_UNSPEC;
                                                break;
                                        }
+                                       phone2tel=orig_p2t;
                                } else {
                                        uri=msg->parsed_uri;
                                }
  
+                               /* skip SET_USERPHONE_T action if the URI is already
+                                * a tel: or tels: URI, or contains the user=phone param */
+                               if ((a->type==SET_USERPHONE_T) 
+                                       && ((uri.type==TEL_URI_T) || (uri.type==TELS_URI_T)
+                                               || ((uri.user_param_val.len==5) && (memcmp(uri.user_param_val.s, "phone", 5)==0)))
+                               ) {
+                                       ret=1;
+                                       break;
+                               }
+                               /* SET_PORT_T does not work with tel: URIs */
+                               if ((a->type==SET_PORT_T)
+                                       && ((uri.type==TEL_URI_T) || (uri.type==TELS_URI_T))
+                                       && ((uri.flags & URI_SIP_USER_PHONE)==0)
+                               ) {
+                                       LOG(L_ERR, "ERROR: do_action: port number of a tel: URI cannot be set\n");
+                                       ret=E_UNSPEC;
+                                       break;
+                               }
                                new_uri=pkg_malloc(MAX_URI_SIZE);
                                if (new_uri==0){
                                        LOG(L_ERR, "ERROR: do_action: memory allocation "
                                end=new_uri+MAX_URI_SIZE;
                                crt=new_uri;
                                /* begin copying */
-                               len=strlen("sip:"); if(crt+len>end) goto error_uri;
-                               memcpy(crt,"sip:",len);crt+=len;
+                               /* Preserve the URI scheme unless the host part needs
+                                * to be rewritten, and the shceme is tel: or tels: */
+                               switch (uri.type) {
+                               case SIP_URI_T:
+                                       len=s_sip.len;
+                                       tmp=s_sip.s;
+                                       break;
+                               case SIPS_URI_T:
+                                       len=s_sips.len;
+                                       tmp=s_sips.s;
+                                       break;
+                               case TEL_URI_T:
+                                       if ((uri.flags & URI_SIP_USER_PHONE)
+                                               || (a->type==SET_HOST_T)
+                                               || (a->type==SET_HOSTPORT_T)
+                                               || (a->type==SET_HOSTPORTTRANS_T)
+                                       ) {
+                                               len=s_sip.len;
+                                               tmp=s_sip.s;
+                                               break;
+                                       }
+                                       len=s_tel.len;
+                                       tmp=s_tel.s;
+                                       break;
+                               case TELS_URI_T:
+                                       if ((uri.flags & URI_SIP_USER_PHONE)
+                                               || (a->type==SET_HOST_T)
+                                               || (a->type==SET_HOSTPORT_T)
+                                               || (a->type==SET_HOSTPORTTRANS_T)
+                                       ) {
+                                               len=s_sips.len;
+                                               tmp=s_sips.s;
+                                               break;
+                                       }
+                                       len=s_tels.len;
+                                       tmp=s_tels.s;
+                                       break;
+                               default:
+                                       LOG(L_ERR, "ERROR: Unsupported URI scheme (%d), "
+                                               "reverted to sip:\n",
+                                               uri.type);
+                                       len=s_sip.len;
+                                       tmp=s_sip.s;
+                               }
+                               if(crt+len+1 /* colon */ >end) goto error_uri;
+                               memcpy(crt,tmp,len);crt+=len;
+                               *crt=':'; crt++;
  
                                /* user */
  
                                        memcpy(crt,tmp,len);crt+=len;
                                }
                                /* host */
-                               if (user || tmp){ /* add @ */
-                                       if(crt+1>end) goto error_uri;
-                                       *crt='@'; crt++;
-                               }
                                if ((a->type==SET_HOST_T)
                                                || (a->type==SET_HOSTPORT_T)
-                                               || (a->type==SET_HOSTPORTTRANS_T)) {
 +                                              || (a->type==SET_HOSTALL_T)
+                                               || (a->type==SET_HOSTPORTTRANS_T)
+                               ) {
                                        tmp=a->val[0].u.string;
                                        if (tmp) len = strlen(tmp);
                                        else len=0;
-                               } else {
+                               } else if ((uri.type==SIP_URI_T)
+                                       || (uri.type==SIPS_URI_T)
+                                       || (uri.flags & URI_SIP_USER_PHONE)
+                               ) {
                                        tmp=uri.host.s;
-                                       len = uri.host.len;
+                                       len=uri.host.len;
+                               } else {
+                                       tmp=0;
                                }
                                if (tmp){
+                                       if (user) { /* add @ */
+                                               if(crt+1>end) goto error_uri;
+                                               *crt='@'; crt++;
+                                       }
                                        if(crt+len>end) goto error_uri;
                                        memcpy(crt,tmp,len);crt+=len;
                                }
 +                              if(a->type==SET_HOSTALL_T)
 +                                      goto done_seturi;
                                /* port */
                                if ((a->type==SET_HOSTPORT_T)
                                                || (a->type==SET_HOSTPORTTRANS_T))
                                                memcpy(crt,tmp,len);crt+=len;
                                        }
                                }
+                               /* Add the user=phone param if a tel: or tels:
+                                * URI was converted to sip: or sips:.
+                                * (host part of a tel/tels URI was set.)
+                                * Or in case of sip: URI and SET_USERPHONE_T action */
+                               if (((((uri.type==TEL_URI_T) || (uri.type==TELS_URI_T))
+                                       && ((uri.flags & URI_SIP_USER_PHONE)==0))
+                                       && ((a->type==SET_HOST_T)
+                                               || (a->type==SET_HOSTPORT_T)
+                                               || (a->type==SET_HOSTPORTTRANS_T)))
+                                       || (a->type==SET_USERPHONE_T)
+                               ) {
+                                       tmp=";user=phone";
+                                       len=strlen(tmp);
+                                       if(crt+len>end) goto error_uri;
+                                       memcpy(crt,tmp,len);crt+=len;
+                               }
                                /* headers */
                                tmp=uri.headers.s;
                                if (tmp){
                                        *crt='?'; crt++;
                                        memcpy(crt,tmp,len);crt+=len;
                                }
 +      done_seturi:
                                *crt=0; /* null terminate the thing */
                                /* copy it to the msg */
                                if (msg->new_uri.s) pkg_free(msg->new_uri.s);
                                                ret=0;
                                                break;
                                        }
 -                                      h->run_flags &= ~RETURN_R_F; /* catch returns in expr */
 +                                      h->run_flags &= ~(RETURN_R_F|BREAK_R_F); /* catch return &
 +                                                                                                                          break in expr*/
                                        ret=1;  /*default is continue */
                                        if (v>0) {
                                                if ((a->val[1].type==ACTIONS_ST)&&a->val[1].u.data){
                                }
                        break;
                case MODULE_T:
 -                      if ( a->val[0].type==MODEXP_ST && a->val[0].u.data && ((cmd_export_t*)a->val[0].u.data)->function ){
 -                              ret=((cmd_export_t*)a->val[0].u.data)->function(msg,
 -                                      (char*)a->val[2].u.data,
 -                                      (char*)a->val[3].u.data
 -                              );
 +                      if ( a->val[0].type==MODEXP_ST && a->val[0].u.data && 
 +                                      (f=((union cmd_export_u*)a->val[0].u.data)->c.function)){
 +                              ret=((cmd_function)f)(msg,
 +                                                                              (char*)a->val[2].u.data,
 +                                                                              (char*)a->val[3].u.data
 +                                                                      );
                                if (ret==0) h->run_flags|=EXIT_R_F;
                                h->last_retcode=ret;
                        } else {
                                LOG(L_CRIT,"BUG: do_action: bad module call\n");
                        }
                        break;
 +              /* instead of using the parameter number, we use different names
 +               * for calls to functions with 3, 4, 5, 6 or variable number of
 +               * parameters due to performance reasons */
 +              case MODULE3_T:
 +                      if ( a->val[0].type==MODEXP_ST && a->val[0].u.data && 
 +                                      (f=((union cmd_export_u*)a->val[0].u.data)->c.function)){
 +                              ret=((cmd_function3)f)(msg,
 +                                                                              (char*)a->val[2].u.data,
 +                                                                              (char*)a->val[3].u.data,
 +                                                                              (char*)a->val[4].u.data
 +                                                                      );
 +                              if (ret==0) h->run_flags|=EXIT_R_F;
 +                              h->last_retcode=ret;
 +                      } else {
 +                              LOG(L_CRIT,"BUG: do_action: bad module call\n");
 +                      }
 +                      break;
 +              case MODULE4_T:
 +                      if ( a->val[0].type==MODEXP_ST && a->val[0].u.data && 
 +                                      (f=((union cmd_export_u*)a->val[0].u.data)->c.function)){
 +                              ret=((cmd_function4)f)(msg,
 +                                                                              (char*)a->val[2].u.data,
 +                                                                              (char*)a->val[3].u.data,
 +                                                                              (char*)a->val[4].u.data,
 +                                                                              (char*)a->val[5].u.data
 +                                                                      );
 +                              if (ret==0) h->run_flags|=EXIT_R_F;
 +                              h->last_retcode=ret;
 +                      } else {
 +                              LOG(L_CRIT,"BUG: do_action: bad module call\n");
 +                      }
 +                      break;
 +              case MODULE5_T:
 +                      if ( a->val[0].type==MODEXP_ST && a->val[0].u.data && 
 +                                      (f=((union cmd_export_u*)a->val[0].u.data)->c.function)){
 +                              ret=((cmd_function5)f)(msg,
 +                                                                              (char*)a->val[2].u.data,
 +                                                                              (char*)a->val[3].u.data,
 +                                                                              (char*)a->val[4].u.data,
 +                                                                              (char*)a->val[5].u.data,
 +                                                                              (char*)a->val[6].u.data
 +                                                                      );
 +                              if (ret==0) h->run_flags|=EXIT_R_F;
 +                              h->last_retcode=ret;
 +                      } else {
 +                              LOG(L_CRIT,"BUG: do_action: bad module call\n");
 +                      }
 +                      break;
 +              case MODULE6_T:
 +                      if ( a->val[0].type==MODEXP_ST && a->val[0].u.data && 
 +                                      (f=((union cmd_export_u*)a->val[0].u.data)->c.function)){
 +                              ret=((cmd_function6)f)(msg,
 +                                                                              (char*)a->val[2].u.data,
 +                                                                              (char*)a->val[3].u.data,
 +                                                                              (char*)a->val[4].u.data,
 +                                                                              (char*)a->val[5].u.data,
 +                                                                              (char*)a->val[6].u.data,
 +                                                                              (char*)a->val[7].u.data
 +                                                                      );
 +                              if (ret==0) h->run_flags|=EXIT_R_F;
 +                              h->last_retcode=ret;
 +                      } else {
 +                              LOG(L_CRIT,"BUG: do_action: bad module call\n");
 +                      }
 +                      break;
 +              case MODULEX_T:
 +                      if ( a->val[0].type==MODEXP_ST && a->val[0].u.data && 
 +                                      (f=((union cmd_export_u*)a->val[0].u.data)->c.function)){
 +                              ret=((cmd_function_var)f)(msg,
 +                                                                                      a->val[1].u.number,
 +                                                                                      &a->val[2]
 +                                                                              );
 +                              if (ret==0) h->run_flags|=EXIT_R_F;
 +                              h->last_retcode=ret;
 +                      } else {
 +                              LOG(L_CRIT,"BUG: do_action: bad module call\n");
 +                      }
 +                      break;
 +              case EVAL_T:
 +                      /* only eval the expression to account for possible
 +                         side-effect */
 +                      rval_expr_eval_int(h, msg, &v,
 +                                      (struct rval_expr*)a->val[0].u.data);
 +                      if (h->run_flags & EXIT_R_F){
 +                              ret=0;
 +                              break;
 +                      }
 +                      h->run_flags &= ~RETURN_R_F|BREAK_R_F; /* catch return & break in
 +                                                                                                        expr */
 +                      ret=1; /* default is continue */
 +                      break;
 +              case SWITCH_COND_T:
 +                      sct=(struct switch_cond_table*)a->val[1].u.data;
 +                      if (unlikely( rval_expr_eval_int(h, msg, &v,
 +                                                                      (struct rval_expr*)a->val[0].u.data) <0)){
 +                              /* handle error in expression => use default */
 +                              ret=-1;
 +                              goto sw_cond_def;
 +                      }
 +                      if (h->run_flags & EXIT_R_F){
 +                              ret=0;
 +                              break;
 +                      }
 +                      h->run_flags &= ~(RETURN_R_F|BREAK_R_F); /* catch return & break
 +                                                                                                          in expr */
 +                      ret=1; /* default is continue */
 +                      for(i=0; i<sct->n; i++)
 +                              if (sct->cond[i]==v){
 +                                      if (likely(sct->jump[i])){
 +                                              ret=run_actions(h, sct->jump[i], msg);
 +                                              h->run_flags &= ~BREAK_R_F; /* catch breaks, but let
 +                                                                                                         returns passthrough */
 +                                      }
 +                                      goto skip;
 +                              }
 +sw_cond_def:
 +                      if (sct->def){
 +                              ret=run_actions(h, sct->def, msg);
 +                              h->run_flags &= ~BREAK_R_F; /* catch breaks, but let
 +                                                                                         returns passthrough */
 +                      }
 +                      break;
 +              case SWITCH_JT_T:
 +                      sjt=(struct switch_jmp_table*)a->val[1].u.data;
 +                      if (unlikely( rval_expr_eval_int(h, msg, &v,
 +                                                                      (struct rval_expr*)a->val[0].u.data) <0)){
 +                              /* handle error in expression => use default */
 +                              ret=-1;
 +                              goto sw_jt_def;
 +                      }
 +                      if (h->run_flags & EXIT_R_F){
 +                              ret=0;
 +                              break;
 +                      }
 +                      h->run_flags &= ~(RETURN_R_F|BREAK_R_F); /* catch return & break
 +                                                                                                          in expr */
 +                      ret=1; /* default is continue */
 +                      if (likely(v >= sjt->first && v <= sjt->last)){
 +                              if (likely(sjt->tbl[v - sjt->first])){
 +                                      ret=run_actions(h, sjt->tbl[v - sjt->first], msg);
 +                                      h->run_flags &= ~BREAK_R_F; /* catch breaks, but let
 +                                                                                                 returns passthrough */
 +                              }
 +                              break; 
 +                      }else{
 +                              for(i=0; i<sjt->rest.n; i++)
 +                                      if (sjt->rest.cond[i]==v){
 +                                              if (likely(sjt->rest.jump[i])){
 +                                                      ret=run_actions(h, sjt->rest.jump[i], msg);
 +                                                      h->run_flags &= ~BREAK_R_F; /* catch breaks, but 
 +                                                                                                                 let returns pass */
 +                                              }
 +                                              goto skip;
 +                                      }
 +                      }
 +                      /* not found => try default */
 +sw_jt_def:
 +                      if (sjt->rest.def){
 +                              ret=run_actions(h, sjt->rest.def, msg);
 +                              h->run_flags &= ~BREAK_R_F; /* catch breaks, but let
 +                                                                                         returns passthrough */
 +                      }
 +                      break;
 +              case BLOCK_T:
 +                      if (likely(a->val[0].u.data)){
 +                              ret=run_actions(h, (struct action*)a->val[0].u.data, msg);
 +                              h->run_flags &= ~BREAK_R_F; /* catch breaks, but let
 +                                                                                         returns passthrough */
 +                      }
 +                      break;
 +              case MATCH_COND_T:
 +                      mct=(struct match_cond_table*)a->val[1].u.data;
 +                      rval_cache_init(&c1);
 +                      rv=0;
 +                      rv1=0;
 +                      ret=rval_expr_eval_rvint(h, msg, &rv, &v, 
 +                                                                      (struct rval_expr*)a->val[0].u.data, &c1);
 +                                                                      
 +                      if (unlikely( ret<0)){
 +                              /* handle error in expression => use default */
 +                              ret=-1;
 +                              goto match_cond_def;
 +                      }
 +                      if (h->run_flags & EXIT_R_F){
 +                              ret=0;
 +                              break;
 +                      }
 +                      h->run_flags &= ~(RETURN_R_F|BREAK_R_F); /* catch return & break
 +                                                                                                          in expr */
 +                      if (likely(rv)){
 +                              rv1=rval_convert(h, msg, RV_STR, rv, &c1);
 +                              if (unlikely(rv1==0)){
 +                                      ret=-1;
 +                                      goto match_cond_def;
 +                              }
 +                              s=rv1->v.s;
 +                      }else{
 +                              /* int result in v */
 +                              rval_cache_clean(&c1);
 +                              s.s=sint2str(v, &s.len);
 +                      }
 +                      ret=1; /* default is continue */
 +                      for(i=0; i<mct->n; i++)
 +                              if (( mct->match[i].type==MATCH_STR &&
 +                                              mct->match[i].l.s.len==s.len &&
 +                                              memcmp(mct->match[i].l.s.s, s.s, s.len) == 0 ) ||
 +                                       ( mct->match[i].type==MATCH_RE &&
 +                                        regexec(mct->match[i].l.regex, s.s, 0, 0, 0) == 0)
 +                                      ){
 +                                      if (likely(mct->jump[i])){
 +                                              ret=run_actions(h, mct->jump[i], msg);
 +                                              h->run_flags &= ~BREAK_R_F; /* catch breaks, but let
 +                                                                                                         returns passthrough */
 +                                      }
 +                                      goto match_cleanup;
 +                              }
 +match_cond_def:
 +                      if (mct->def){
 +                              ret=run_actions(h, mct->def, msg);
 +                              h->run_flags &= ~BREAK_R_F; /* catch breaks, but let
 +                                                                                         returns passthrough */
 +                      }
 +match_cleanup:
 +                      if (rv1){
 +                              rval_destroy(rv1);
 +                              rval_destroy(rv);
 +                              rval_cache_clean(&c1);
 +                      }else if (rv){
 +                              rval_destroy(rv);
 +                              rval_cache_clean(&c1);
 +                      }
 +                      break;
 +              case WHILE_T:
 +                      i=0;
 +                      flags=0;
 +                      rve=(struct rval_expr*)a->val[0].u.data;
 +                      ret=1;
 +                      while(!(flags & BREAK_R_F) && 
 +                                      (rval_expr_eval_int(h, msg, &v, rve) == 0) && v){
 +                              i++;
 +                              if (unlikely(i > cfg_get(core, core_cfg, max_while_loops))){
 +                                      LOG(L_ERR, "ERROR: runaway while (%d, %d): more then"
 +                                                              " %d loops\n", 
 +                                                              rve->fpos.s_line, rve->fpos.s_col,
 +                                                              cfg_get(core, core_cfg, max_while_loops));
 +                                      ret=-1;
 +                                      break;
 +                              }
 +                              if (likely(a->val[1].u.data)){
 +                                      ret=run_actions(h, (struct action*)a->val[1].u.data, msg);
 +                                      flags|=h->run_flags;
 +                                      h->run_flags &= ~BREAK_R_F; /* catch breaks, but let
 +                                                                                                 returns passthrough */
 +                              }
 +                      }
 +                      break;
                case FORCE_RPORT_T:
                        msg->msg_flags|=FL_FORCE_RPORT;
                        ret=1; /* continue processing */
  
                case ADD_T:
                case ASSIGN_T:
 -
 -                      /* If the left attr was specified without indexing brackets delete
 -                       * existing AVPs before adding new ones
 -                       */
 -                      if ((a->val[0].u.attr->type & AVP_INDEX_ALL) != AVP_INDEX_ALL) delete_avp(a->val[0].u.attr->type, a->val[0].u.attr->name);
 -
 -                      if (a->val[1].type == STRING_ST) {
 -                              value.s = a->val[1].u.str;
 -                              flags = a->val[0].u.attr->type | AVP_VAL_STR;
 -                              name = a->val[0].u.attr->name;
 -                              ret = 1;
 -                      } else if (a->val[1].type == NUMBER_ST) {
 -                              value.n = a->val[1].u.number;
 -                              flags = a->val[0].u.attr->type;
 -                              name = a->val[0].u.attr->name;
 +                      v=lval_assign(h, msg, (struct lvalue*)a->val[0].u.data,
 +                                                                (struct rval_expr*)a->val[1].u.data);
 +                      if (likely(v>=0))
                                ret = 1;
 -                      } else if (a->val[1].type == ACTION_ST) {
 -                              flags = a->val[0].u.attr->type;
 -                              name = a->val[0].u.attr->name;
 -                              if (a->val[1].u.data) {
 -                                      value.n = run_actions(h, (struct action*)a->val[1].u.data,
 -                                                                                      msg);
 -                              } else {
 -                                      value.n = -1;
 -                              }
 -                              ret = value.n;
 -                      } else if(a->val[1].type == EXPR_ST && a->val[1].u.data) {
 -                              v = eval_expr(h, (struct expr*)a->val[1].u.data, msg);
 -                              if (v < 0) {
 -                                      if (v == EXPR_DROP){ /* hack to quit on DROP*/
 -                                              ret = 0;
 -                                              break;
 -                                      } else {
 -                                              LOG(L_WARN,"WARNING: do_action: error in expression\n");
 -                                              v = 0; /* error is treated as false (Miklos) */
 -                                      }
 -                              }
 -
 -                              flags = a->val[0].u.attr->type;
 -                              name = a->val[0].u.attr->name;
 -                              value.n = v;
 -                      } else if (a->val[1].type == AVP_ST) {
 -                              struct search_state st;
 -                              avp_t* avp;
 -                              avp_t* avp_mark;
 -
 -                              avp_mark = NULL;
 -                              if ((a->val[1].u.attr->type & AVP_INDEX_ALL) == AVP_INDEX_ALL) {
 -                                      avp = search_first_avp(a->val[1].u.attr->type, a->val[1].u.attr->name, &value, &st);
 -                                      while(avp) {
 -                                                   /* We take only the type of value and name from the source avp
 -                                                    * and reset class and track flags
 -                                                    */
 -                                              flags = (a->val[0].u.attr->type & ~AVP_INDEX_ALL) | (avp->flags & ~(AVP_CLASS_ALL|AVP_TRACK_ALL));
 -
 -                                              if (add_avp_before(avp_mark, flags, a->val[0].u.attr->name, value) < 0) {
 -                                                      LOG(L_CRIT, "ERROR: Failed to assign value to attribute\n");
 -                                                      ret=E_UNSPEC;
 -                                                      break;
 -                                              }
 -
 -                                              /* 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, a->val[0].u.attr->name, NULL, NULL);
 -                                              }
 -
 -                                              avp = search_next_avp(&st, &value);
 -                                      }
 -                                      ret = 1;
 -                                      break;
 -                              } else {
 -                                      avp = search_avp_by_index(a->val[1].u.attr->type, a->val[1].u.attr->name, &value, a->val[1].u.attr->index);
 -                                      if (avp) {
 -                                              flags = a->val[0].u.attr->type | (avp->flags & ~(AVP_CLASS_ALL|AVP_TRACK_ALL));
 -                                              name = a->val[0].u.attr->name;
 -                                              ret = 1;
 -                                      } else {
 -                                              ret = E_UNSPEC;
 -                                              break;
 -                                      }
 -                              }
 -                      } else if (a->val[1].type == SELECT_ST) {
 -                              int r;
 -                              r = run_select(&value.s, a->val[1].u.select, msg);
 -                              if (r < 0) {
 -                                      ret=E_UNSPEC;
 -                                      break;
 -                              } else if (r > 0) {
 -                                      value.s.s = "";
 -                                      value.s.len = 0;
 -                              }
 -
 -                              flags = a->val[0].u.attr->type | AVP_VAL_STR;
 -                              name = a->val[0].u.attr->name;
 -                              ret = 1;
 -                      } else {
 -                              LOG(L_CRIT, "BUG: do_action: Bad right side of avp assignment\n");
 -                              ret=E_BUG;
 -                              break;
 -                      }
 -
 -                      /* If the action is assign then remove the old avp value
 -                       * before adding new ones */
 -/*                    if ((unsigned char)a->type == ASSIGN_T) delete_avp(flags, name); */
 -                      if (add_avp(flags & ~AVP_INDEX_ALL, name, value) < 0) {
 -                              LOG(L_CRIT, "ERROR: Failed to assign value to attribute\n");
 -                              ret=E_UNSPEC;
 -                              break;
 -                      }
 +                      else if (unlikely (v == EXPR_DROP)) /* hack to quit on DROP*/
 +                              ret=0;
 +                      else
 +                              ret=v;
                        break;
  
                default:
                        LOG(L_CRIT, "BUG: do_action: unknown type %d\n", a->type);
        }
 -/*skip:*/
 +skip:
        return ret;
  
  error_uri:
@@@ -1136,9 -1065,7 +1236,9 @@@ int run_actions(struct run_act_ctx* h, 
  
        for (t=a; t!=0; t=t->next){
                ret=do_action(h, t, msg);
 -              if (h->run_flags & (RETURN_R_F|EXIT_R_F)){
 +              /* break, return or drop/exit stop execution of the current
 +                 block */
 +              if (h->run_flags & (BREAK_R_F|RETURN_R_F|EXIT_R_F)){
                        if (h->run_flags & EXIT_R_F){
  #ifdef USE_LONGJMP
                                h->last_retcode=ret;
        /* process module onbreak handlers if present */
        if (h->rec_lev==0 && ret==0)
                for (mod=modules;mod;mod=mod->next)
 -                      if (mod->exports && mod->exports->onbreak_f) {
 -                              mod->exports->onbreak_f( msg );
 -                              DBG("DEBUG: %s onbreak handler called\n", mod->exports->name);
 +                      if ((mod->mod_interface_ver==0) && mod->exports && 
 +                                      mod->exports->v0.onbreak_f) {
 +                              mod->exports->v0.onbreak_f( msg );
 +                              DBG("DEBUG: %s onbreak handler called\n",
 +                                              mod->exports->c.name);
                        }
        return ret;
  
diff --combined cfg.lex
+++ b/cfg.lex
@@@ -75,8 -75,7 +75,9 @@@
   *  2007-11-28  added TCP_OPT_{FD_CACHE, DEFER_ACCEPT, DELAYED_ACK, SYNCNT,
   *              LINGER2, KEEPALIVE, KEEPIDLE, KEEPINTVL, KEEPCNT} (andrei)
   *  2008-01-24  added CFG_DESCRIPTION used by cfg_var (Miklos)
 + *  2008-11-28  added support for kamailio pvars and avp/pvar guessing (andrei)
 + *  2008-12-11  added support for "string1" "string2" (andrei)
+  *  2009-03-10  added SET_USERPHONE action (Miklos)
  */
  
  
        #include "usr_avp.h"
        #include "select.h"
        #include "cfg.tab.h"
 +      #include "sr_compat.h"
  
        /* states */
        #define INITIAL_S               0
        #define COMMENT_S               1
        #define COMMENT_LN_S            2
        #define STRING_S                3
 -      #define ATTR_S                  4
 -        #define SELECT_S                5
 +      #define ATTR_S                  4  /* avp/attr */
 +      #define SELECT_S                5
 +      #define AVP_PVAR_S              6  /* avp or pvar */
 +      #define PVAR_P_S                7  /* pvar: $(...)  or $foo(...)*/
 +      #define PVARID_S                8  /* $foo.bar...*/
 +      #define STR_BETWEEN_S           9
  
        #define STR_BUF_ALLOC_UNIT      128
        struct str_buf{
  
  
        static int comment_nest=0;
 +      static int p_nest=0;
        static int state=0, old_state=0, old_initial=0;
        static struct str_buf s_buf;
        int line=1;
        int column=1;
        int startcolumn=1;
 +      int startline=1;
 +      static int ign_lines=0;
 +      static int ign_columns=0;
  
        static char* addchar(struct str_buf *, char);
        static char* addstr(struct str_buf *, char*, int);
        static void count();
 +      static void count_more();
 +      static void count_ignore();
  
  
  %}
  
  /* start conditions */
 -%x STRING1 STRING2 COMMENT COMMENT_LN ATTR SELECT
 +%x STRING1 STRING2 STR_BETWEEN COMMENT COMMENT_LN ATTR SELECT AVP_PVAR PVAR_P 
 +%x PVARID
 +
 +/* config script types : #!SER  or #!KAMAILIO or #!MAX_COMPAT */
 +SER_CFG                       SER
 +KAMAILIO_CFG  KAMAILIO|OPENSER
 +MAXCOMPAT_CFG MAXCOMPAT|ALL
  
  /* action keywords */
  FORWARD       forward
@@@ -183,16 -165,13 +184,17 @@@ REVERT_URI              "revert_uri
  PREFIX                        "prefix"
  STRIP                 "strip"
  STRIP_TAIL            "strip_tail"
+ SET_USERPHONE         "userphone"
  APPEND_BRANCH "append_branch"
  IF                            "if"
  ELSE                  "else"
  SET_ADV_ADDRESS       "set_advertised_address"
  SET_ADV_PORT  "set_advertised_port"
  FORCE_SEND_SOCKET     "force_send_socket"
 +SWITCH                        "switch"
 +CASE                  "case"
 +DEFAULT                       "default"
 +WHILE                 "while"
  
  /*ACTION LVALUES*/
  URIHOST                       "uri:host"
@@@ -244,8 -223,7 +246,8 @@@ PLUS       "+
  MINUS "-"
  
  /* Attribute specification */
 -ATTR_MARK   "$"|"%"
 +ATTR_MARK   "%"
 +VAR_MARK    "$"
  SELECT_MARK  "@"
  ATTR_FROM         "f"
  ATTR_TO           "t"
@@@ -257,9 -235,6 +259,9 @@@ ATTR_FROMDOMAIN   "fd
  ATTR_TODOMAIN     "td"
  ATTR_GLOBAL       "g"
  
 +/* avp prefix */
 +AVP_PREF      (([ft][rud]?)|g)\.
 +
  /* config vars. */
  DEBUG debug
  FORK  fork
@@@ -332,6 -307,8 +334,8 @@@ TCP_OPT_FD_CACHE   "tcp_fd_cache
  TCP_OPT_BUF_WRITE     "tcp_buf_write"|"tcp_async"
  TCP_OPT_CONN_WQ_MAX   "tcp_conn_wq_max"
  TCP_OPT_WQ_MAX                "tcp_wq_max"
+ TCP_OPT_RD_BUF                "tcp_rd_buf_size"
+ TCP_OPT_WQ_BLK                "tcp_wq_blk_size"
  TCP_OPT_DEFER_ACCEPT "tcp_defer_accept"
  TCP_OPT_DELAYED_ACK   "tcp_delayed_ack"
  TCP_OPT_SYNCNT                "tcp_syncnt"
@@@ -379,7 -356,6 +383,7 @@@ MCAST_TTL          "mcast_ttl
  TOS                   "tos"
  PMTU_DISCOVERY        "pmtu_discovery"
  KILL_TIMEOUT  "exit_timeout"|"ser_kill_timeout"
 +MAX_WLOOPS            "max_while_loops"
  
  /* stun config variables */
  STUN_REFRESH_INTERVAL "stun_refresh_interval"
@@@ -415,7 -391,7 +419,7 @@@ ID                 {LETTER}{ALPHANUM}
  HEX                   [0-9a-fA-F]
  HEXNUMBER     0x{HEX}+
  OCTNUMBER     0[0-7]+
 -DECNUMBER       0|-?([1-9]{DIGIT}*)
 +DECNUMBER       0|([1-9]{DIGIT}*)
  BINNUMBER       [0-1]+b
  HEX4          {HEX}{1,4}
  IPV6ADDR      ({HEX4}":"){7}{HEX4}|({HEX4}":"){1,7}(":"{HEX4}){1,7}|":"(":"{HEX4}){1,7}|({HEX4}":"){1,7}":"|"::"
@@@ -491,6 -467,8 +495,8 @@@ EAT_ABLE   [\ \t\b\r
  <INITIAL>{STRIP_TAIL} { count(); yylval.strval=yytext; return STRIP_TAIL; }
  <INITIAL>{APPEND_BRANCH}      { count(); yylval.strval=yytext;
                                                                return APPEND_BRANCH; }
+ <INITIAL>{SET_USERPHONE}      { count(); yylval.strval=yytext;
+                                                               return SET_USERPHONE; }
  <INITIAL>{FORCE_RPORT}        { count(); yylval.strval=yytext; return FORCE_RPORT; }
  <INITIAL>{FORCE_TCP_ALIAS}    { count(); yylval.strval=yytext;
                                                                return FORCE_TCP_ALIAS; }
                                                                                return SET_ADV_PORT; }
  <INITIAL>{FORCE_SEND_SOCKET}  {       count(); yylval.strval=yytext;
                                                                        return FORCE_SEND_SOCKET; }
 +<INITIAL>{SWITCH}     { count(); yylval.strval=yytext; return SWITCH; }
 +<INITIAL>{CASE}       { count(); yylval.strval=yytext; return CASE; }
 +<INITIAL>{DEFAULT}    { count(); yylval.strval=yytext; return DEFAULT; }
 +<INITIAL>{WHILE}      { count(); yylval.strval=yytext; return WHILE; }
  
  <INITIAL>{URIHOST}    { count(); yylval.strval=yytext; return URIHOST; }
  <INITIAL>{URIPORT}    { count(); yylval.strval=yytext; return URIPORT; }
                                                                        return TCP_OPT_CONN_WQ_MAX; }
  <INITIAL>{TCP_OPT_WQ_MAX}     { count(); yylval.strval=yytext;
                                                                        return TCP_OPT_WQ_MAX; }
+ <INITIAL>{TCP_OPT_RD_BUF}     { count(); yylval.strval=yytext;
+                                                                       return TCP_OPT_RD_BUF; }
+ <INITIAL>{TCP_OPT_WQ_BLK}     { count(); yylval.strval=yytext;
+                                                                       return TCP_OPT_WQ_BLK; }
  <INITIAL>{TCP_OPT_BUF_WRITE}  { count(); yylval.strval=yytext;
                                                                        return TCP_OPT_BUF_WRITE; }
  <INITIAL>{TCP_OPT_DEFER_ACCEPT}       { count(); yylval.strval=yytext;
                                                                        return PMTU_DISCOVERY; }
  <INITIAL>{KILL_TIMEOUT}                       {       count(); yylval.strval=yytext;
                                                                        return KILL_TIMEOUT; }
 +<INITIAL>{MAX_WLOOPS}                 {       count(); yylval.strval=yytext;
 +                                                                      return MAX_WLOOPS; }
  <INITIAL>{SERVER_ID}  { count(); yylval.strval=yytext; return SERVER_ID;}
  <INITIAL>{CFG_DESCRIPTION}    { count(); yylval.strval=yytext; return CFG_DESCRIPTION; }
  <INITIAL>{LOADMODULE} { count(); yylval.strval=yytext; return LOADMODULE; }
  <SELECT>{BINNUMBER}     { count(); yylval.intval=(int)strtol(yytext, 0, 2); return NUMBER; }
  
  
 -<INITIAL>{ATTR_MARK}    { count(); state = ATTR_S; BEGIN(ATTR); return ATTR_MARK; }
 +<INITIAL>{ATTR_MARK}    { count(); state = ATTR_S; BEGIN(ATTR);
 +                                                      return ATTR_MARK; }
  <ATTR>{ATTR_FROM}       { count(); return ATTR_FROM; }
  <ATTR>{ATTR_TO}         { count(); return ATTR_TO; }
  <ATTR>{ATTR_FROMURI}    { count(); return ATTR_FROMURI; }
  <ATTR>{DOT}             { count(); return DOT; }
  <ATTR>{LBRACK}          { count(); return LBRACK; }
  <ATTR>{RBRACK}          { count(); return RBRACK; }
 -<ATTR>{STAR}          { count(); return STAR; }
 -<ATTR>{DECNUMBER}     { count(); yylval.intval=atoi(yytext);return NUMBER; }
 -<ATTR>{ID}            { count(); addstr(&s_buf, yytext, yyleng);
 -                           yylval.strval=s_buf.s;
 -                         memset(&s_buf, 0, sizeof(s_buf));
 -                           state = INITIAL_S;
 -                           BEGIN(INITIAL);
 -                         return ID;
 -                        }
 +<ATTR>{STAR}                  { count(); return STAR; }
 +<ATTR>{DECNUMBER}             { count(); yylval.intval=atoi(yytext);return NUMBER; }
 +<ATTR>{ID}                            { count(); addstr(&s_buf, yytext, yyleng);
 +                                                      yylval.strval=s_buf.s;
 +                                                      memset(&s_buf, 0, sizeof(s_buf));
 +                                                      state = INITIAL_S;
 +                                                      BEGIN(INITIAL);
 +                                                      return ID;
 +                                              }
 +
 +<INITIAL>{VAR_MARK}{LPAREN}   {
 +                                                              switch(sr_cfg_compat){
 +                                                                      case SR_COMPAT_SER:
 +                                                                              state=ATTR_S; BEGIN(ATTR);
 +                                                                              yyless(1);
 +                                                                              count();
 +                                                                              return ATTR_MARK;
 +                                                                              break;
 +                                                                      case SR_COMPAT_KAMAILIO:
 +                                                                      case SR_COMPAT_MAX:
 +                                                                      default:
 +                                                                              state = PVAR_P_S; BEGIN(PVAR_P);
 +                                                                              p_nest=1; yymore();
 +                                                                              break;
 +                                                              }
 +                                                      }
 +      /* eat everything between 2 () and return PVAR token and a string
 +         containing everything (including $ and ()) */
 +<PVAR_P>{RPAREN}                      {       p_nest--;
 +                                                              if (p_nest==0){
 +                                                                      count();
 +                                                                      addstr(&s_buf, yytext, yyleng);
 +                                                                      yylval.strval=s_buf.s;
 +                                                                      memset(&s_buf, 0, sizeof(s_buf));
 +                                                                      state=INITIAL_S;
 +                                                                      BEGIN(INITIAL);
 +                                                                      return PVAR;
 +                                                              }
 +                                                              yymore();
 +                                                      }
 +<PVAR_P>{LPAREN}                      { p_nest++; yymore(); }
 +<PVAR_P>.                                     { yymore(); }
 +
 +<PVARID>{ID}|'.'                      {yymore(); }
 +<PVARID>{LPAREN}                      {       state = PVAR_P_S; BEGIN(PVAR_P);
 +                                                              p_nest=1; yymore(); }
 +<PVARID>.                                     { yyless(0); state=INITIAL_S; BEGIN(INITIAL);
 +                                                              return PVAR;
 +                                                      }
 +
 +
 +<INITIAL>{VAR_MARK}                   {
 +                                                              switch(sr_cfg_compat){
 +                                                                      case SR_COMPAT_SER:
 +                                                                              count();
 +                                                                              state=ATTR_S; BEGIN(ATTR);
 +                                                                              return ATTR_MARK;
 +                                                                              break;
 +                                                                      case SR_COMPAT_KAMAILIO:
 +                                                                              state=PVARID_S; BEGIN(PVARID);
 +                                                                              yymore();
 +                                                                              break;
 +                                                                      case SR_COMPAT_MAX:
 +                                                                      default: 
 +                                                                              state=AVP_PVAR_S; BEGIN(AVP_PVAR);
 +                                                                              yymore();
 +                                                                              break;
 +                                                              }
 +                                                      }
 +      /* avp prefix detected -> go to avp mode */
 +<AVP_PVAR>{AVP_PREF}          |
 +<AVP_PVAR>{ID}{LBRACK}                { state = ATTR_S; BEGIN(ATTR); yyless(1); count();
 +                                                        return ATTR_MARK; }
 +<AVP_PVAR>{ID}{LPAREN}                { state = PVAR_P_S; p_nest=1; BEGIN(PVAR_P);
 +                                                              yymore(); }
 +<AVP_PVAR>{ID}                                {       count(); addstr(&s_buf, yytext, yyleng);
 +                                                              yylval.strval=s_buf.s;
 +                                                              memset(&s_buf, 0, sizeof(s_buf));
 +                                                              state = INITIAL_S;
 +                                                              BEGIN(INITIAL);
 +                                                              return AVP_OR_PVAR;
 +                                                      }
  
  <INITIAL>{IPV6ADDR}           { count(); yylval.strval=yytext; return IPV6ADDR; }
  <INITIAL>{DECNUMBER}          { count(); yylval.intval=atoi(yytext);return NUMBER; }
  <INITIAL>{COMMA}              { count(); return COMMA; }
  <INITIAL>{SEMICOLON}  { count(); return SEMICOLON; }
  <INITIAL>{COLON}      { count(); return COLON; }
 -<INITIAL>{STAR}       { count(); return STAR; }
 +<INITIAL>{STAR}               { count(); return STAR; }
  <INITIAL>{RPAREN}     { count(); return RPAREN; }
  <INITIAL>{LPAREN}     { count(); return LPAREN; }
  <INITIAL>{LBRACE}     { count(); return LBRACE; }
  <INITIAL>{CR}         { count();/* return CR;*/ }
  
  
 -<INITIAL,SELECT>{QUOTES} { count(); old_initial = YY_START; old_state = state; state=STRING_S; BEGIN(STRING1); }
 -<INITIAL>{TICK} { count(); old_initial = YY_START; old_state = state; state=STRING_S; BEGIN(STRING2); }
 +<INITIAL,SELECT>{QUOTES} { count(); old_initial = YY_START; 
 +                                                      old_state = state; state=STRING_S;
 +                                                      BEGIN(STRING1); }
 +<INITIAL>{TICK} { count(); old_initial = YY_START; old_state = state;
 +                                      state=STRING_S; BEGIN(STRING2); }
  
  
 -<STRING1>{QUOTES} { count(); state=old_state; BEGIN(old_initial);
 +<STRING1>{QUOTES} { count_more(); 
                                                yytext[yyleng-1]=0; yyleng--;
                                                addstr(&s_buf, yytext, yyleng);
 -                                              yylval.strval=s_buf.s;
 -                                              memset(&s_buf, 0, sizeof(s_buf));
 -                                              return STRING;
 +                                              BEGIN(STR_BETWEEN);
                                        }
 -<STRING2>{TICK}  { count(); state=old_state; BEGIN(old_initial);
 +<STRING2>{TICK}  { count_more(); state=old_state; BEGIN(old_initial);
                                                yytext[yyleng-1]=0; yyleng--;
                                                addstr(&s_buf, yytext, yyleng);
                                                yylval.strval=s_buf.s;
                                        }
  <STRING2>.|{EAT_ABLE}|{CR}    { yymore(); }
  
 -<STRING1>\\n          { count(); addchar(&s_buf, '\n'); }
 -<STRING1>\\r          { count(); addchar(&s_buf, '\r'); }
 -<STRING1>\\a          { count(); addchar(&s_buf, '\a'); }
 -<STRING1>\\t          { count(); addchar(&s_buf, '\t'); }
 -<STRING1>\\{QUOTES}   { count(); addchar(&s_buf, '"');  }
 -<STRING1>\\\\         { count(); addchar(&s_buf, '\\'); }
 -<STRING1>\\x{HEX}{1,2}        { count(); addchar(&s_buf,
 +<STRING1>\\n          { count_more(); addchar(&s_buf, '\n'); }
 +<STRING1>\\r          { count_more(); addchar(&s_buf, '\r'); }
 +<STRING1>\\a          { count_more(); addchar(&s_buf, '\a'); }
 +<STRING1>\\t          { count_more(); addchar(&s_buf, '\t'); }
 +<STRING1>\\{QUOTES}   { count_more(); addchar(&s_buf, '"');  }
 +<STRING1>\\\\         { count_more(); addchar(&s_buf, '\\'); }
 +<STRING1>\\x{HEX}{1,2}        { count_more(); addchar(&s_buf,
                                                                                        (char)strtol(yytext+2, 0, 16)); }
   /* don't allow \[0-7]{1}, it will eat the backreferences from
      subst_uri if allowed (although everybody should use '' in subt_uri) */
 -<STRING1>\\[0-7]{2,3} { count(); addchar(&s_buf,
 +<STRING1>\\[0-7]{2,3} { count_more(); addchar(&s_buf,
                                                                                        (char)strtol(yytext+1, 0, 8));  }
 -<STRING1>\\{CR}               { count(); } /* eat escaped CRs */
 -<STRING1>.|{EAT_ABLE}|{CR}    { addchar(&s_buf, *yytext); }
 -
 +<STRING1>\\{CR}               { count_more(); } /* eat escaped CRs */
 +<STRING1>.|{EAT_ABLE}|{CR}    { count_more(); addchar(&s_buf, *yytext); }
 +
 +<STR_BETWEEN>{EAT_ABLE}|{CR}  { count_ignore(); }
 +<STR_BETWEEN>{QUOTES}                 { count_more(); state=STRING_S;
 +                                                                BEGIN(STRING1);}
 +<STR_BETWEEN>.                                        {       
 +                                                                      yyless(0); /* reparse it */
 +                                                                      /* ignore the whitespace now that is
 +                                                                        counted, return saved string value */
 +                                                                      state=old_state; BEGIN(old_initial);
 +                                                                      yylval.strval=s_buf.s;
 +                                                                      memset(&s_buf, 0, sizeof(s_buf));
 +                                                                      return STRING;
 +                                                              }
  
  <INITIAL,COMMENT>{COM_START}  { count(); comment_nest++; state=COMMENT_S;
                                                                                BEGIN(COMMENT); }
                                                                }
  <COMMENT>.|{EAT_ABLE}|{CR}                            { count(); };
  
 +<INITIAL>{COM_LINE}!{SER_CFG}{CR}             { count();
 +                                                                                      sr_cfg_compat=SR_COMPAT_SER;}
 +<INITIAL>{COM_LINE}!{KAMAILIO_CFG}{CR}        { count(); 
 +                                                                                      sr_cfg_compat=SR_COMPAT_KAMAILIO;}
 +<INITIAL>{COM_LINE}!{MAXCOMPAT_CFG}{CR}       { count(); 
 +                                                                                              sr_cfg_compat=SR_COMPAT_MAX;}
  <INITIAL>{COM_LINE}.*{CR}     { count(); }
  
  <INITIAL>{ID}                 { count(); addstr(&s_buf, yytext, yyleng);
  
  <<EOF>>                                                       {
                                                                        switch(state){
 +                                                                              case STR_BETWEEN_S:
 +                                                                                      state=old_state;
 +                                                                                      BEGIN(old_initial);
 +                                                                                      yylval.strval=s_buf.s;
 +                                                                                      memset(&s_buf, 0, sizeof(s_buf));
 +                                                                                      return STRING;
                                                                                case STRING_S:
                                                                                        LOG(L_CRIT, "ERROR: cfg. parser: unexpected EOF in"
                                                                                                                " unclosed string\n");
                                                                                        LOG(L_CRIT, "ERROR: unexpected EOF:"
                                                                                                                "comment line open\n");
                                                                                        break;
 +                                                                              case  ATTR_S:
 +                                                                                      LOG(L_CRIT, "ERROR: unexpected EOF"
 +                                                                                                      " while parsing"
 +                                                                                                      " avp name\n");
 +                                                                                      break;
 +                                                                              case PVARID_S:
 +                                                                                      p_nest=0;
 +                                                                              case PVAR_P_S: 
 +                                                                                      LOG(L_CRIT, "ERROR: unexpected EOF"
 +                                                                                                      " while parsing pvar name"
 +                                                                                                      " (%d paranthesis open)\n",
 +                                                                                                      p_nest);
 +                                                                                      break;
 +                                                                              case AVP_PVAR_S:
 +                                                                                      LOG(L_CRIT, "ERROR: unexpected EOF"
 +                                                                                                      " while parsing"
 +                                                                                                      " avp or pvar name\n");
                                                                        }
                                                                        return 0;
                                                                }
@@@ -1075,66 -935,26 +1085,66 @@@ error
  
  
  
 -static void count()
 +/** helper function for count_*(). */
 +static void count_lc(int* l, int* c)
  {
        int i;
 -
 -      startcolumn=column;
        for (i=0; i<yyleng;i++){
                if (yytext[i]=='\n'){
 -                      line++;
 -                      column=startcolumn=1;
 +                      (*l)++;
 +                      (*c)=1;
                }else if (yytext[i]=='\t'){
 -                      column++;
 -                      /*column+=8 -(column%8);*/
 +                      (*c)++;
 +                      /*(*c)+=8 -((*c)%8);*/
                }else{
 -                      column++;
 +                      (*c)++;
                }
        }
  }
  
  
  
 +/* helper function */
 +static void count_restore_ignored()
 +{
 +      if (ign_lines) /* ignored line(s) => column has changed */
 +              column=ign_columns;
 +      else
 +              column+=ign_columns;
 +      line+=ign_lines;
 +      ign_lines=ign_columns=0;
 +}
 +
 +
 +
 +/** count/record position for stuff added to the current token. */
 +static void count_more()
 +{
 +      count_restore_ignored();
 +      count_lc(&line, &column);
 +}
 +
 +
 +
 +/** count/record position for a new token. */
 +static void count()
 +{
 +      count_restore_ignored();
 +      startline=line;
 +      startcolumn=column;
 +      count_more();
 +}
 +
 +
 +
 +/** record discarded stuff (not contained in the token) so that
 +    the next token position can be adjusted properly*/
 +static void count_ignore()
 +{
 +      count_lc(&ign_lines, &ign_columns);
 +}
 +
 +
  /* replacement yywrap, removes libfl dependency */
  int yywrap()
  {
diff --combined cfg.y
--- 1/cfg.y
--- 2/cfg.y
+++ b/cfg.y
   * 2007-11-28  added TCP_OPT_{FD_CACHE, DEFER_ACCEPT, DELAYED_ACK, SYNCNT,
   *              LINGER2, KEEPALIVE, KEEPIDLE, KEEPINTVL, KEEPCNT} (andrei)
   * 2008-01-24  added cfg_var definition (Miklos)
 + * 2008-11-18  support for variable parameter module functions (andrei)
 + * 2007-12-03  support for generalised lvalues and rvalues:
 + *               lval=rval_expr, where lval=avp|pvar  (andrei)
 + * 2007-12-06  expression are now evaluated in terms of rvalues;
 + *             NUMBER is now always positive; cleanup (andrei)
 + * 2009-01-26  case/switch() support (andrei)
+  * 2009-03-10  added SET_USERPHONE action (Miklos)
  */
  
  %{
  
  #include <stdlib.h>
  #include <stdio.h>
 +#include <stdarg.h>
  #include <sys/types.h>
  #include <sys/socket.h>
  #include <netinet/in.h>
  #include "route_struct.h"
  #include "globals.h"
  #include "route.h"
 +#include "switch.h"
  #include "dprint.h"
  #include "sr_module.h"
  #include "modparam.h"
  #include "tcp_init.h"
  #include "tcp_options.h"
  #include "sctp_options.h"
 +#include "pvar.h"
 +#include "lvalue.h"
 +#include "rvalue.h"
 +#include "sr_compat.h"
  #include "msg_translator.h"
  
  #include "config.h"
  #ifdef CORE_TLS
  #include "tls/tls_config.h"
  #endif
+ #include "timer_ticks.h"
  
  #ifdef DEBUG_DMALLOC
  #include <dmalloc.h>
  
  
  extern int yylex();
 -static void yyerror(char* s);
 +static void yyerror(char* s, ...);
 +static void yyerror_at(struct cfg_pos* pos, char* s, ...);
  static char* tmp;
  static int i_tmp;
 +static unsigned u_tmp;
  static struct socket_id* lst_tmp;
  static struct name_lst*  nl_tmp;
  static int rt;  /* Type of route block for find_export */
@@@ -198,30 -186,15 +200,30 @@@ static struct ip_addr* ip_tmp
  static struct avp_spec* s_attr;
  static select_t sel;
  static select_t* sel_ptr;
 +static pv_spec_t* pv_spec;
  static struct action *mod_func_action;
 -
 -static void warn(char* s);
 +static struct lvalue* lval_tmp;
 +static struct rvalue* rval_tmp;
 +
 +static void warn(char* s, ...);
 +static void warn_at(struct cfg_pos* pos, char* s, ...);
 +static void get_cpos(struct cfg_pos* pos);
 +static struct rval_expr* mk_rve_rval(enum rval_type, void* v);
 +static struct rval_expr* mk_rve1(enum rval_expr_op op, struct rval_expr* rve1);
 +static struct rval_expr* mk_rve2(enum rval_expr_op op, struct rval_expr* rve1,
 +                                                                      struct rval_expr* rve2);
  static struct socket_id* mk_listen_id(char*, int, int);
  static struct name_lst* mk_name_lst(char* name, int flags);
  static struct socket_id* mk_listen_id2(struct name_lst*, int, int);
  static void free_name_lst(struct name_lst* lst);
  static void free_socket_id_lst(struct socket_id* i);
  
 +static struct case_stms* mk_case_stm(struct rval_expr* ct, int is_re, 
 +                                                                      struct action* a, int* err);
 +static int case_check_type(struct case_stms* stms);
 +static int case_check_default(struct case_stms* stms);
 +
 +
  %}
  
  %union {
        char* strval;
        struct expr* expr;
        struct action* action;
 +      struct case_stms* case_stms;
        struct net* ipnet;
        struct ip_addr* ipaddr;
        struct socket_id* sockid;
        struct name_lst* name_l;
        struct avp_spec* attr;
 +      struct _pv_spec* pvar;
 +      struct lvalue* lval;
 +      struct rvalue* rval;
 +      struct rval_expr* rv_expr;
        select_t* select;
  }
  
  %token PREFIX
  %token STRIP
  %token STRIP_TAIL
+ %token SET_USERPHONE
  %token APPEND_BRANCH
  %token SET_USER
  %token SET_USERPASS
  %token SET_ADV_ADDRESS
  %token SET_ADV_PORT
  %token FORCE_SEND_SOCKET
 +%token SWITCH
 +%token CASE
 +%token DEFAULT
 +%token WHILE
  %token URIHOST
  %token URIPORT
  %token MAX_LEN
  %token TCP_OPT_BUF_WRITE
  %token TCP_OPT_CONN_WQ_MAX
  %token TCP_OPT_WQ_MAX
+ %token TCP_OPT_RD_BUF
+ %token TCP_OPT_WQ_BLK
  %token TCP_OPT_DEFER_ACCEPT
  %token TCP_OPT_DELAYED_ACK
  %token TCP_OPT_SYNCNT
  %token TOS
  %token PMTU_DISCOVERY
  %token KILL_TIMEOUT
 +%token MAX_WLOOPS
  %token CFG_DESCRIPTION
  %token SERVER_ID
  
  %token ATTR_GLOBAL
  %token ADDEQ
  
 +
  %token STUN_REFRESH_INTERVAL
  %token STUN_ALLOW_STUN
  %token STUN_ALLOW_FP
  
  
 -/* operators */
 -%nonassoc EQUAL
 -%nonassoc EQUAL_T
 -%nonassoc GT
 -%nonassoc LT
 -%nonassoc GTE
 -%nonassoc LTE
 -%nonassoc DIFF
 -%nonassoc MATCH
 +/* operators, C like precedence */
 +%right EQUAL
  %left LOG_OR
  %left LOG_AND
  %left BIN_OR
  %left BIN_AND
 +%left EQUAL_T DIFF MATCH
 +%left GT LT GTE LTE
  %left PLUS MINUS
 +%left STAR SLASH
  %right NOT
 +%left DOT
  
  /* values */
  %token <intval> NUMBER
  %token <strval> ID
  %token <strval> STRING
  %token <strval> IPV6ADDR
 +%token <strval> PVAR
 +/* not clear yet if this is an avp or pvar */
 +%token <strval> AVP_OR_PVAR
  
  /* other */
  %token COMMA
  %token RBRACE
  %token LBRACK
  %token RBRACK
 -%token SLASH
 -%token DOT
  %token CR
  %token COLON
 -%token STAR
  
  
  /*non-terminals */
 -%type <expr> exp exp_elem /*, condition*/
 -%type <action> action actions cmd fcmd if_cmd stm exp_stm assign_action
 +%type <expr> exp exp_elem
 +%type <intval> intno eint_op eint_op_onsend
 +%type <intval> eip_op eip_op_onsend
 +%type <action> action actions cmd fcmd if_cmd stm /*exp_stm*/ assign_action
 +%type <action> switch_cmd while_cmd
 +%type <case_stms> single_case case_stms
  %type <ipaddr> ipv4 ipv6 ipv6addr ip
  %type <ipnet> ipnet
  %type <strval> host
  %type <sockid>  phostport
  %type <sockid>  listen_phostport
  %type <intval> proto port
 -%type <intval> equalop strop intop binop
 -%type <strval> host_sep
 +%type <intval> equalop strop cmpop rve_cmpop rve_equalop
  %type <intval> uri_type
  %type <attr> attr_id
  %type <attr> attr_id_num_idx
  %type <attr> attr_id_no_idx
  %type <attr> attr_id_ass
 -%type <attr> attr_id_val
 +/*%type <attr> attr_id_val*/
  %type <attr> attr_id_any
  %type <attr> attr_id_any_str
 +%type <pvar> pvar
 +%type <lval> lval
 +%type <rv_expr> rval rval_expr ct_rval
 +%type <lval> avp_pvar
  /* %type <intval> class_id */
  %type <intval> assign_op
  %type <select> select_id
  %type <strval>        flag_name;
  %type <strval>        route_name;
  %type <intval> avpflag_oper
 +%type <intval> rve_un_op
 +/* %type <intval> rve_op */
  
  /*%type <route_el> rules;
    %type <route_el> rule;
@@@ -657,10 -616,6 +662,10 @@@ id_lst
        | listen_phostport id_lst       { $$=$1; $$->next=$2; }
        ;
  
 +intno: NUMBER
 +      |  MINUS %prec NOT NUMBER { $$=-$2; }
 +      ;
 +
  flags_decl:           FLAGS_DECL      flag_list
                        |       FLAGS_DECL error { yyerror("flag list expected\n"); }
  ;
@@@ -696,7 -651,7 +701,7 @@@ avpflag_spec
        }
        ;
  assign_stm:
 -      DEBUG_V EQUAL NUMBER { default_core_cfg.debug=$3; }
 +      DEBUG_V EQUAL intno { default_core_cfg.debug=$3; }
        | DEBUG_V EQUAL error  { yyerror("number  expected"); }
        | FORK  EQUAL NUMBER { dont_fork= ! $3; }
        | FORK  EQUAL error  { yyerror("boolean value expected"); }
        | DNS_TRY_NAPTR error { yyerror("boolean value expected"); }
        | DNS_SRV_LB EQUAL NUMBER   { IF_DNS_FAILOVER(default_core_cfg.dns_srv_lb=$3); }
        | DNS_SRV_LB error { yyerror("boolean value expected"); }
 -      | DNS_UDP_PREF EQUAL NUMBER { IF_NAPTR(default_core_cfg.dns_udp_pref=$3);}
 +      | DNS_UDP_PREF EQUAL intno { IF_NAPTR(default_core_cfg.dns_udp_pref=$3);}
        | DNS_UDP_PREF error { yyerror("number expected"); }
 -      | DNS_TCP_PREF EQUAL NUMBER { IF_NAPTR(default_core_cfg.dns_tcp_pref=$3);}
 +      | DNS_TCP_PREF EQUAL intno { IF_NAPTR(default_core_cfg.dns_tcp_pref=$3);}
        | DNS_TCP_PREF error { yyerror("number expected"); }
 -      | DNS_TLS_PREF EQUAL NUMBER { IF_NAPTR(default_core_cfg.dns_tls_pref=$3);}
 +      | DNS_TLS_PREF EQUAL intno { IF_NAPTR(default_core_cfg.dns_tls_pref=$3);}
        | DNS_TLS_PREF error { yyerror("number expected"); }
 -      | DNS_SCTP_PREF EQUAL NUMBER { 
 +      | DNS_SCTP_PREF EQUAL intno { 
                                                                IF_NAPTR(default_core_cfg.dns_sctp_pref=$3); }
        | DNS_SCTP_PREF error { yyerror("number expected"); }
        | DNS_RETR_TIME EQUAL NUMBER   { default_core_cfg.dns_retr_time=$3; }
        | PHONE2TEL EQUAL error { yyerror("boolean value expected"); }
        | SYN_BRANCH EQUAL NUMBER { syn_branch=$3; }
        | SYN_BRANCH EQUAL error { yyerror("boolean value expected"); }
 -      | MEMLOG EQUAL NUMBER { memlog=$3; }
 +      | MEMLOG EQUAL intno { memlog=$3; }
        | MEMLOG EQUAL error { yyerror("int value expected"); }
 -      | MEMDBG EQUAL NUMBER { memdbg=$3; }
 +      | MEMDBG EQUAL intno { memdbg=$3; }
        | MEMDBG EQUAL error { yyerror("int value expected"); }
        | SIP_WARNING EQUAL NUMBER { sip_warning=$3; }
        | SIP_WARNING EQUAL error { yyerror("boolean value expected"); }
                #endif
        }
        | TCP_CHILDREN EQUAL error { yyerror("number expected"); }
 -      | TCP_CONNECT_TIMEOUT EQUAL NUMBER {
 +      | TCP_CONNECT_TIMEOUT EQUAL intno {
                #ifdef USE_TCP
                        tcp_default_cfg.connect_timeout_s=$3;
                #else
                #endif
        }
        | TCP_CONNECT_TIMEOUT EQUAL error { yyerror("number expected"); }
 -      | TCP_SEND_TIMEOUT EQUAL NUMBER {
 +      | TCP_SEND_TIMEOUT EQUAL intno {
                #ifdef USE_TCP
-                       tcp_default_cfg.send_timeout_s=$3;
+                       tcp_default_cfg.send_timeout=S_TO_TICKS($3);
                #else
                        warn("tcp support not compiled in");
                #endif
        }
        | TCP_SEND_TIMEOUT EQUAL error { yyerror("number expected"); }
 -      | TCP_CON_LIFETIME EQUAL NUMBER {
 +      | TCP_CON_LIFETIME EQUAL intno {
                #ifdef USE_TCP
-                       tcp_default_cfg.con_lifetime_s=$3;
+                       if ($3<0)
+                               tcp_default_cfg.con_lifetime=-1;
+                       else
+                               tcp_default_cfg.con_lifetime=S_TO_TICKS($3);
                #else
                        warn("tcp support not compiled in");
                #endif
                        warn("tcp support not compiled in");
                #endif
        }
-       | TCP_OPT_WQ_MAX error { yyerror("boolean value expected"); }
+       | TCP_OPT_WQ_MAX error { yyerror("number expected"); }
+       | TCP_OPT_RD_BUF EQUAL NUMBER {
+               #ifdef USE_TCP
+                       tcp_default_cfg.rd_buf_size=$3;
+               #else
+                       warn("tcp support not compiled in");
+               #endif
+       }
+       | TCP_OPT_RD_BUF error { yyerror("number expected"); }
+       | TCP_OPT_WQ_BLK EQUAL NUMBER {
+               #ifdef USE_TCP
+                       tcp_default_cfg.wq_blk_size=$3;
+               #else
+                       warn("tcp support not compiled in");
+               #endif
+       }
+       | TCP_OPT_WQ_BLK error { yyerror("number expected"); }
        | TCP_OPT_DEFER_ACCEPT EQUAL NUMBER {
                #ifdef USE_TCP
                        tcp_default_cfg.defer_accept=$3;
        | PMTU_DISCOVERY error { yyerror("number expected"); }
        | KILL_TIMEOUT EQUAL NUMBER { ser_kill_timeout=$3; }
        | KILL_TIMEOUT EQUAL error { yyerror("number expected"); }
 +      | MAX_WLOOPS EQUAL NUMBER { default_core_cfg.max_while_loops=$3; }
 +      | MAX_WLOOPS EQUAL error { yyerror("number expected"); }
        | STUN_REFRESH_INTERVAL EQUAL NUMBER { IF_STUN(stun_refresh_interval=$3); }
        | STUN_REFRESH_INTERVAL EQUAL error{ yyerror("number expected"); }
        | STUN_ALLOW_STUN EQUAL NUMBER { IF_STUN(stun_allow_stun=$3); }
        | UDP_MTU EQUAL NUMBER { default_core_cfg.udp_mtu=$3; }
        | UDP_MTU EQUAL error { yyerror("number expected"); }
        | FORCE_RPORT EQUAL NUMBER 
-               { default_core_cfg.force_rport=$3; fix_global_req_flags(0); }
+               { default_core_cfg.force_rport=$3; fix_global_req_flags(0, 0); }
        | FORCE_RPORT EQUAL error { yyerror("boolean value expected"); }
        | UDP_MTU_TRY_PROTO EQUAL proto
-               { default_core_cfg.udp_mtu_try_proto=$3; fix_global_req_flags(0); }
+               { default_core_cfg.udp_mtu_try_proto=$3; fix_global_req_flags(0, 0); }
        | UDP_MTU_TRY_PROTO EQUAL error
                { yyerror("TCP, TLS, SCTP or UDP expected"); }
        | cfg_var
@@@ -1533,183 -1505,281 +1557,183 @@@ send_route_stm: ROUTE_SEND LBRACE actio
        }
        | ROUTE_SEND error { yyerror("invalid onsend_route statement"); }
        ;
 -/*
 -rules:
 -      rules rule { push($2, &$1); $$=$1; }
 -      | rule {$$=$1; }
 -      | rules error { $$=0; yyerror("invalid rule"); }
 -      ;
 -rule:
 -      condition actions CR {
 -              $$=0;
 -              if (add_rule($1, $2, &$$)<0) {
 -                      yyerror("error calling add_rule");
 -                      YYABORT;
 +
 +exp:  rval_expr
 +              {
 +                      if (!rve_check_type((enum rval_type*)&i_tmp, $1, 0, 0 ,0)){
 +                              yyerror("invalid expression");
 +                              $$=0;
 +                      }else if (i_tmp!=RV_INT && i_tmp!=RV_NONE){
 +                              yyerror("invalid expression type, int expected\n");
 +                              $$=0;
 +                      }else
 +                              $$=mk_elem(NO_OP, RVEXP_O, $1, 0, 0);
                }
 -      }
 -      | CR    { $$=0;}
 -      | condition error { $$=0; yyerror("bad actions in rule"); }
 -      ;
 -condition:
 -      exp {$$=$1;}
 -*/
 -exp:  exp LOG_AND exp         { $$=mk_exp(LOGAND_OP, $1, $3); }
 -      | exp LOG_OR exp        { $$=mk_exp(LOGOR_OP, $1, $3);  }
 -      | NOT exp               { $$=mk_exp(NOT_OP, $2, 0);  }
 -      | LPAREN exp RPAREN     { $$=$2; }
 -      | exp_elem              { $$=$1; }
        ;
 +
 +/* exp elem operators */
  equalop:
        EQUAL_T {$$=EQUAL_OP; }
        | DIFF  {$$=DIFF_OP; }
        ;
 -intop:        equalop {$$=$1; }
 -      | GT    {$$=GT_OP; }
 +cmpop:
 +        GT    {$$=GT_OP; }
        | LT    {$$=LT_OP; }
        | GTE   {$$=GTE_OP; }
        | LTE   {$$=LTE_OP; }
        ;
 -binop :
 -      BIN_OR { $$= BINOR_OP; }
 -      | BIN_AND { $$ = BINAND_OP; }
 -      ;
  strop:
        equalop {$$=$1; }
        | MATCH {$$=MATCH_OP; }
        ;
 +
 +
 +/* rve expr. operators */
 +rve_equalop:
 +      EQUAL_T {$$=RVE_EQ_OP; }
 +      | DIFF  {$$=RVE_DIFF_OP; }
 +      ;
 +rve_cmpop:
 +        GT    {$$=RVE_GT_OP; }
 +      | LT    {$$=RVE_LT_OP; }
 +      | GTE   {$$=RVE_GTE_OP; }
 +      | LTE   {$$=RVE_LTE_OP; }
 +      ;
 +
 +
 +
 +/* boolean expression uri operands */
  uri_type:
        URI             {$$=URI_O;}
        | FROM_URI      {$$=FROM_URI_O;}
        | TO_URI        {$$=TO_URI_O;}
        ;
  
 +
 +/* boolean expression integer operands, available only in the
 +  onsend route */
 +eint_op_onsend:
 +                      SNDPORT         { $$=SNDPORT_O; }
 +              |       TOPORT          { $$=TOPORT_O; }
 +              |       SNDPROTO        { $$=SNDPROTO_O; }
 +              |       SNDAF           { $$=SNDAF_O; }
 +              ;
 +
 +/* boolean expression integer operands */
 +eint_op:      SRCPORT         { $$=SRCPORT_O; }
 +              |       DSTPORT         { $$=DSTPORT_O; }
 +              |       PROTO           { $$=PROTO_O; }
 +              |       AF                      { $$=AF_O; }
 +              |       MSGLEN          { $$=MSGLEN_O; }
 +              |       RETCODE         { $$=RETCODE_O; }
 +              | eint_op_onsend
 +      ;
 +
 +/* boolean expression ip/ipnet operands */
 +eip_op_onsend:
 +                      SNDIP           { onsend_check("snd_ip"); $$=SNDIP_O; }
 +              |       TOIP            { onsend_check("to_ip");  $$=TOIP_O; }
 +              ;
 +
 +eip_op:               SRCIP           { $$=SRCIP_O; }
 +              |       DSTIP           { $$=DSTIP_O; }
 +              | eip_op_onsend
 +              ;
 +
 +
 +
  exp_elem:
 -      METHOD strop STRING     {$$= mk_elem($2, METHOD_O, 0, STRING_ST, $3);}
 -      | METHOD strop attr_id_val  {$$ = mk_elem($2, METHOD_O, 0, AVP_ST, $3); }
 -      | METHOD strop select_id {$$ = mk_elem($2, METHOD_O, 0, SELECT_ST, $3); }
 -      | METHOD strop  ID      {$$ = mk_elem($2, METHOD_O, 0, STRING_ST,$3); }
 +      METHOD strop %prec EQUAL_T rval_expr
 +              {$$= mk_elem($2, METHOD_O, 0, RVE_ST, $3);}
 +      | METHOD strop %prec EQUAL_T ID
 +              {$$ = mk_elem($2, METHOD_O, 0, STRING_ST,$3); }
        | METHOD strop error { $$=0; yyerror("string expected"); }
 -      | METHOD error  { $$=0; yyerror("invalid operator,== , !=, or =~ expected"); }
 -      | uri_type strop STRING {$$ = mk_elem($2, $1, 0, STRING_ST, $3); }
 -      | uri_type strop host   {$$ = mk_elem($2, $1, 0, STRING_ST, $3); }
 -      | uri_type strop attr_id_val {$$ = mk_elem($2, $1, 0, AVP_ST, $3); }
 -      | uri_type strop select_id {$$ = mk_elem($2, $1, 0, SELECT_ST, $3); }
 -      | uri_type equalop MYSELF {$$=mk_elem($2, $1, 0, MYSELF_ST, 0); }
 -      | uri_type strop error { $$=0; yyerror("string or MYSELF expected"); }
 -      | uri_type error        { $$=0; yyerror("invalid operator, == , != or =~ expected"); }
 -
 -      | SRCPORT intop NUMBER { $$=mk_elem($2, SRCPORT_O, 0, NUMBER_ST, (void*)$3 ); }
 -      | SRCPORT intop attr_id_val { $$=mk_elem($2, SRCPORT_O, 0, AVP_ST, (void*)$3 ); }
 -      | SRCPORT intop error { $$=0; yyerror("number expected"); }
 -      | SRCPORT error { $$=0; yyerror("==, !=, <,>, >= or <=  expected"); }
 -
 -      | DSTPORT intop NUMBER  { $$=mk_elem($2, DSTPORT_O, 0, NUMBER_ST, (void*)$3 ); }
 -      | DSTPORT intop attr_id_val     { $$=mk_elem($2, DSTPORT_O, 0, AVP_ST, (void*)$3 ); }
 -      | DSTPORT intop error { $$=0; yyerror("number expected"); }
 -      | DSTPORT error { $$=0; yyerror("==, !=, <,>, >= or <=  expected"); }
 -
 -      | SNDPORT intop NUMBER {
 -              onsend_check("snd_port");
 -              $$=mk_elem($2, SNDPORT_O, 0, NUMBER_ST, (void*)$3 );
 -      }
 -      | SNDPORT intop attr_id_val {
 -              onsend_check("snd_port");
 -              $$=mk_elem($2, SNDPORT_O, 0, AVP_ST, (void*)$3 );
 -      }
 -      | SNDPORT intop error { $$=0; yyerror("number expected"); }
 -      | SNDPORT error { $$=0; yyerror("==, !=, <,>, >= or <=  expected"); }
 -
 -      | TOPORT intop NUMBER {
 -              onsend_check("to_port");
 -              $$=mk_elem($2, TOPORT_O, 0, NUMBER_ST, (void*)$3 );
 -      }
 -      | TOPORT intop attr_id_val {
 -              onsend_check("to_port");
 -              $$=mk_elem($2, TOPORT_O, 0, AVP_ST, (void*)$3 );
 -      }
 -      | TOPORT intop error { $$=0; yyerror("number expected"); }
 -      | TOPORT error { $$=0; yyerror("==, !=, <,>, >= or <=  expected"); }
 -
 -      | PROTO intop proto     { $$=mk_elem($2, PROTO_O, 0, NUMBER_ST, (void*)$3 ); }
 -      | PROTO intop attr_id_val       { $$=mk_elem($2, PROTO_O, 0, AVP_ST, (void*)$3 ); }
 -      | PROTO intop error { $$=0; yyerror("protocol expected (udp, tcp or tls)"); }
 -
 -      | PROTO error { $$=0; yyerror("equal/!= operator expected"); }
 -
 -      | SNDPROTO intop proto  {
 -              onsend_check("snd_proto");
 -              $$=mk_elem($2, SNDPROTO_O, 0, NUMBER_ST, (void*)$3 );
 -      }
 -      | SNDPROTO intop attr_id_val {
 -              onsend_check("snd_proto");
 -              $$=mk_elem($2, SNDPROTO_O, 0, AVP_ST, (void*)$3 );
 -      }
 -      | SNDPROTO intop error { $$=0; yyerror("protocol expected (udp, tcp or tls)"); }
 -      | SNDPROTO error { $$=0; yyerror("equal/!= operator expected"); }
 -
 -      | AF intop NUMBER       { $$=mk_elem($2, AF_O, 0, NUMBER_ST,(void *) $3 ); }
 -      | AF intop attr_id_val  { $$=mk_elem($2, AF_O, 0, AVP_ST,(void *) $3 ); }
 -      | AF intop error { $$=0; yyerror("number expected"); }
 -      | AF error { $$=0; yyerror("equal/!= operator expected"); }
 -
 -      | SNDAF intop NUMBER {
 -              onsend_check("snd_af");
 -              $$=mk_elem($2, SNDAF_O, 0, NUMBER_ST, (void *) $3 ); }
 -      | SNDAF intop attr_id_val {
 -              onsend_check("snd_af");
 -              $$=mk_elem($2, SNDAF_O, 0, AVP_ST, (void *) $3 );
 -      }
 -      | SNDAF intop error { $$=0; yyerror("number expected"); }
 -      | SNDAF error { $$=0; yyerror("equal/!= operator expected"); }
 -
 -      | MSGLEN intop NUMBER           { $$=mk_elem($2, MSGLEN_O, 0, NUMBER_ST, (void *) $3 ); }
 -      | MSGLEN intop attr_id_val      { $$=mk_elem($2, MSGLEN_O, 0, AVP_ST, (void *) $3 ); }
 -      | MSGLEN intop MAX_LEN          { $$=mk_elem($2, MSGLEN_O, 0, NUMBER_ST, (void *) BUF_SIZE); }
 -      | MSGLEN intop error { $$=0; yyerror("number expected"); }
 -      | MSGLEN error { $$=0; yyerror("equal/!= operator expected"); }
 -
 -      | RETCODE intop NUMBER  { $$=mk_elem($2, RETCODE_O, 0, NUMBER_ST, (void *) $3 ); }
 -      | RETCODE intop attr_id_val     { $$=mk_elem($2, RETCODE_O, 0, AVP_ST, (void *) $3 ); }
 -      | RETCODE intop error { $$=0; yyerror("number expected"); }
 -      | RETCODE error { $$=0; yyerror("equal/!= operator expected"); }
 -
 -      | SRCIP equalop ipnet   { $$=mk_elem($2, SRCIP_O, 0, NET_ST, $3); }
 -      | SRCIP strop STRING {
 -              s_tmp.s=$3;
 -              s_tmp.len=strlen($3);
 -              ip_tmp=str2ip(&s_tmp);
 -      #ifdef USE_IPV6
 -              if (ip_tmp==0)
 -                      ip_tmp=str2ip6(&s_tmp);
 -      #endif
 -              if (ip_tmp) {
 -                      $$=mk_elem($2, SRCIP_O, 0, NET_ST, mk_net_bitlen(ip_tmp, ip_tmp->len*8) );
 -              } else {
 -                      $$=mk_elem($2, SRCIP_O, 0, STRING_ST, $3);
 -              }
 -      }
 -      | SRCIP strop host      { $$=mk_elem($2, SRCIP_O, 0, STRING_ST, $3); }
 -      | SRCIP equalop MYSELF  { $$=mk_elem($2, SRCIP_O, 0, MYSELF_ST, 0);
 -                                                      }
 -      | SRCIP strop error { $$=0; yyerror( "ip address or hostname expected" ); }
 -      | SRCIP error  { $$=0; yyerror("invalid operator, ==, != or =~ expected");}
 -      | DSTIP equalop ipnet   { $$=mk_elem(   $2, DSTIP_O, 0, NET_ST, (void*)$3); }
 -      | DSTIP strop STRING    {
 -              s_tmp.s=$3;
 -              s_tmp.len=strlen($3);
 -              ip_tmp=str2ip(&s_tmp);
 -      #ifdef USE_IPV6
 -              if (ip_tmp==0)
 -                      ip_tmp=str2ip6(&s_tmp);
 -      #endif /* USE_IPV6 */
 -              if (ip_tmp) {
 -                      $$=mk_elem($2, DSTIP_O, 0, NET_ST, mk_net_bitlen(ip_tmp, ip_tmp->len*8) );
 -              } else {
 -                      $$=mk_elem($2, DSTIP_O, 0, STRING_ST, $3);
 -              }
 -      }
 -      | DSTIP strop host      { $$=mk_elem(   $2, DSTIP_O, 0, STRING_ST, $3); }
 -      | DSTIP equalop MYSELF  { $$=mk_elem(   $2, DSTIP_O, 0, MYSELF_ST, 0); }
 -      | DSTIP strop error { $$=0; yyerror( "ip address or hostname expected" ); }
 -      | DSTIP error { $$=0; yyerror("invalid operator, ==, != or =~ expected"); }
 -      | SNDIP equalop ipnet {
 -              onsend_check("snd_ip");
 -              $$=mk_elem($2, SNDIP_O, 0, NET_ST, $3);
 -      }
 -      | SNDIP strop STRING    {
 -              onsend_check("snd_ip");
 -              s_tmp.s=$3;
 -              s_tmp.len=strlen($3);
 -              ip_tmp=str2ip(&s_tmp);
 -      #ifdef USE_IPV6
 -              if (ip_tmp==0)
 -                      ip_tmp=str2ip6(&s_tmp);
 -      #endif /* USE_IPV6 */
 -              if (ip_tmp) {
 -                      $$=mk_elem($2, SNDIP_O, 0, NET_ST, mk_net_bitlen(ip_tmp, ip_tmp->len*8) );
 -              } else {
 -                      $$=mk_elem($2, SNDIP_O, 0, STRING_ST, $3);
 -              }
 -      }
 -      | SNDIP strop host      {
 -              onsend_check("snd_ip");
 -              $$=mk_elem($2, SNDIP_O, 0, STRING_ST, $3);
 -      }
 -      | SNDIP equalop attr_id_val     {
 -              onsend_check("snd_ip");
 -          $$=mk_elem($2, SNDIP_O, 0, AVP_ST, (void*)$3 ); 
 -      }
 -      | SNDIP equalop MYSELF  {
 -              onsend_check("snd_ip");
 -              $$=mk_elem($2, SNDIP_O, 0, MYSELF_ST, 0);
 -      }
 -      | SNDIP strop error { $$=0; yyerror( "ip address or hostname expected" ); }
 -      | SNDIP error  { $$=0; yyerror("invalid operator, ==, != or =~ expected"); }
 -      | TOIP equalop ipnet    {
 -              onsend_check("to_ip");
 -              $$=mk_elem($2, TOIP_O, 0, NET_ST, $3);
 -      }
 -      | TOIP strop STRING     {
 -              onsend_check("to_ip");
 -              s_tmp.s=$3;
 -              s_tmp.len=strlen($3);
 -              ip_tmp=str2ip(&s_tmp);
 -      #ifdef USE_IPV6
 -              if (ip_tmp==0)
 -                      ip_tmp=str2ip6(&s_tmp);
 -      #endif /* USE_IPV6 */
 -              if (ip_tmp) {
 -                      $$=mk_elem($2, TOIP_O, 0, NET_ST, mk_net_bitlen(ip_tmp, ip_tmp->len*8) );
 -              } else {
 -                      $$=mk_elem($2, TOIP_O, 0, STRING_ST, $3);
 +      | METHOD error  
 +              { $$=0; yyerror("invalid operator,== , !=, or =~ expected"); }
 +      | uri_type strop %prec EQUAL_T rval_expr
 +              {$$ = mk_elem($2, $1, 0, RVE_ST, $3); }
 +      | uri_type strop %prec EQUAL_T MYSELF
 +              {$$=mk_elem($2, $1, 0, MYSELF_ST, 0); }
 +      | uri_type strop %prec EQUAL_T error
 +              { $$=0; yyerror("string or MYSELF expected"); }
 +      | uri_type error
 +              { $$=0; yyerror("invalid operator, == , != or =~ expected"); }
 +      | eint_op cmpop %prec GT rval_expr { $$=mk_elem($2, $1, 0, RVE_ST, $3 ); }
 +      | eint_op equalop %prec EQUAL_T rval_expr 
 +              { $$=mk_elem($2, $1, 0, RVE_ST, $3 ); }
 +      | eint_op cmpop error   { $$=0; yyerror("number expected"); }
 +      | eint_op equalop error { $$=0; yyerror("number expected"); }
 +      | eint_op error { $$=0; yyerror("==, !=, <,>, >= or <=  expected"); }
 +      | eip_op strop %prec EQUAL_T ipnet { $$=mk_elem($2, $1, 0, NET_ST, $3); }
 +      | eip_op strop %prec EQUAL_T rval_expr {
 +                      s_tmp.s=0;
 +                      $$=0;
 +                      if (rve_is_constant($3)){
 +                              i_tmp=rve_guess_type($3);
 +                              if (i_tmp==RV_INT)
 +                                      yyerror("string expected");
 +                              else if (i_tmp==RV_STR){
 +                                      if (((rval_tmp=rval_expr_eval(0, 0, $3))==0) ||
 +                                                              (rval_get_str(0, 0, &s_tmp, rval_tmp, 0)<0)){
 +                                              rval_destroy(rval_tmp);
 +                                              yyerror("bad rvalue expression");
 +                                      }else{
 +                                              rval_destroy(rval_tmp);
 +                                      }
 +                              }else{
 +                                      yyerror("BUG: unexpected dynamic type");
 +                              }
 +                      }else{
 +                                      warn("non constant rvalue in ip comparison");
 +                      }
 +                      if (s_tmp.s){
 +                              ip_tmp=str2ip(&s_tmp);
 +                      #ifdef USE_IPV6
 +                              if (ip_tmp==0)
 +                                      ip_tmp=str2ip6(&s_tmp);
 +                      #endif
 +                              pkg_free(s_tmp.s);
 +                              if (ip_tmp) {
 +                                      $$=mk_elem($2, $1, 0, NET_ST, 
 +                                                              mk_net_bitlen(ip_tmp, ip_tmp->len*8) );
 +                              } else {
 +                                      $$=mk_elem($2, $1, 0, RVE_ST, $3);
 +                              }
 +                      }else{
 +                              $$=mk_elem($2, $1, 0, RVE_ST, $3);
 +                      }
                }
 -      }
 -      | TOIP strop host       {
 -              onsend_check("to_ip");
 -              $$=mk_elem($2, TOIP_O, 0, STRING_ST, $3);
 -      }
 -      | TOIP equalop attr_id_val      {
 -              onsend_check("to_ip");
 -          $$=mk_elem($2, TOIP_O, 0, AVP_ST, (void*)$3 ); 
 -      }
 -      | TOIP equalop MYSELF  {
 -              onsend_check("to_ip");
 -              $$=mk_elem($2, TOIP_O, 0, MYSELF_ST, 0);
 -      }
 -      | TOIP strop error { $$=0; yyerror( "ip address or hostname expected" ); }
 -      | TOIP error  { $$=0; yyerror("invalid operator, ==, != or =~ expected"); }
 -
 -      | MYSELF equalop uri_type       { $$=mk_elem($2, $3, 0, MYSELF_ST, 0); }
 -      | MYSELF equalop SRCIP  { $$=mk_elem($2, SRCIP_O, 0, MYSELF_ST, 0); }
 -      | MYSELF equalop DSTIP  { $$=mk_elem($2, DSTIP_O, 0, MYSELF_ST, 0); }
 -      | MYSELF equalop SNDIP  {
 -              onsend_check("snd_ip");
 -              $$=mk_elem($2, SNDIP_O, 0, MYSELF_ST, 0);
 -      }
 -      | MYSELF equalop TOIP  {
 -              onsend_check("to_ip");
 -              $$=mk_elem($2, TOIP_O, 0, MYSELF_ST, 0);
 -      }
 -      | MYSELF equalop error { $$=0; yyerror(" URI, SRCIP or DSTIP expected"); }
 +      | eip_op strop %prec EQUAL_T host
 +              { $$=mk_elem($2, $1, 0, STRING_ST, $3); }
 +      | eip_op strop %prec EQUAL_T MYSELF
 +              { $$=mk_elem($2, $1, 0, MYSELF_ST, 0); }
 +      | eip_op strop %prec EQUAL_T error
 +              { $$=0; yyerror( "ip address or hostname expected" ); }
 +      | eip_op error
 +              { $$=0; yyerror("invalid operator, ==, != or =~ expected");}
 +      
 +      | MYSELF equalop %prec EQUAL_T uri_type
 +              { $$=mk_elem($2, $3, 0, MYSELF_ST, 0); }
 +      | MYSELF equalop %prec EQUAL_T eip_op
 +              { $$=mk_elem($2, $3, 0, MYSELF_ST, 0); }
 +      | MYSELF equalop %prec EQUAL_T error
 +              { $$=0; yyerror(" URI, SRCIP or DSTIP expected"); }
        | MYSELF error  { $$=0; yyerror ("invalid operator, == or != expected"); }
 -      | exp_stm       { $$=mk_elem( NO_OP, ACTION_O, 0, ACTIONS_ST, $1);  }
 -      | NUMBER        { $$=mk_elem( NO_OP, NUMBER_O, 0, NUMBER_ST, (void*)$1 ); }
 -
 -      | attr_id_any                           {$$=mk_elem( NO_OP, AVP_O, (void*)$1, 0, 0); }
 -      | attr_id_val strop STRING      {$$=mk_elem( $2, AVP_O, (void*)$1, STRING_ST, $3); }
 -      | attr_id_val strop select_id   {$$=mk_elem( $2, AVP_O, (void*)$1, SELECT_ST, $3); }
 -      | attr_id_val intop NUMBER      {$$=mk_elem( $2, AVP_O, (void*)$1, NUMBER_ST, (void*)$3); }
 -      | attr_id_val binop NUMBER      {$$=mk_elem( $2, AVP_O, (void*)$1, NUMBER_ST, (void*)$3); }
 -      | attr_id_val strop attr_id_val {$$=mk_elem( $2, AVP_O, (void*)$1, AVP_ST, (void*)$3); }
 -      | attr_id_val intop attr_id_val {$$=mk_elem( $2, AVP_O, (void*)$1, AVP_ST, (void*)$3); }
 -
 -      | select_id                 { $$=mk_elem( NO_OP, SELECT_O, $1, 0, 0); }
 -      | select_id strop STRING    { $$=mk_elem( $2, SELECT_O, $1, STRING_ST, $3); }
 -      | select_id strop attr_id_val   { $$=mk_elem( $2, SELECT_O, $1, AVP_ST, (void*)$3); }
 -      | select_id strop select_id { $$=mk_elem( $2, SELECT_O, $1, SELECT_ST, $3); }
 +      ;
 +/*
 +exp_elem2:
 +      rval_expr cmpop %prec GT rval_expr
 +              { $$=mk_elem( $2, RVE_ST, $1, RVE_ST, $3);}
 +      |
 +      rval_expr equalop %prec EQUAL_T rval_expr
 +              { $$=mk_elem( $2, RVE_ST, $1, RVE_ST, $3);}
 +      | rval_expr LOG_AND rval_expr
 +              { $$=mk_exp_rve(LOGAND_OP, $1, $3);}
 +      | rval_expr LOG_OR rval_expr
 +              { $$=mk_exp_rve(LOGOR_OP, $1, $3);}
  ;
 +*/
 +
  ipnet:
        ip SLASH ip     { $$=mk_net($1, $3); }
        | ip SLASH NUMBER {
        | ip    { $$=mk_net_bitlen($1, $1->len*8); }
        | ip SLASH error { $$=0; yyerror("netmask (eg:255.0.0.0 or 8) expected"); }
        ;
 -host_sep:
 -      DOT {$$=".";}
 -      | MINUS {$$="-"; }
 -      ;
  
  host:
        ID { $$=$1; }
 -      | host host_sep ID {
 +      | host DOT ID {
                $$=(char*)pkg_malloc(strlen($1)+1+strlen($3)+1);
                if ($$==0) {
                        LOG(L_CRIT, "ERROR: cfg. parser: memory allocation failure while parsing host\n");
                } else {
                        memcpy($$, $1, strlen($1));
 -                      $$[strlen($1)]=*$2;
 +                      $$[strlen($1)]='.';
 +                      memcpy($$+strlen($1)+1, $3, strlen($3));
 +                      $$[strlen($1)+1+strlen($3)]=0;
 +              }
 +              pkg_free($1);
 +              pkg_free($3);
 +      }
 +      | host MINUS ID {
 +              $$=(char*)pkg_malloc(strlen($1)+1+strlen($3)+1);
 +              if ($$==0) {
 +                      LOG(L_CRIT, "ERROR: cfg. parser: memory allocation failure while parsing host\n");
 +              } else {
 +                      memcpy($$, $1, strlen($1));
 +                      $$[strlen($1)]='-';
                        memcpy($$+strlen($1)+1, $3, strlen($3));
                        $$[strlen($1)+1+strlen($3)]=0;
                }
@@@ -1783,14 -1844,12 +1807,14 @@@ fcmd
                }
        }
        ;
 +/*
  exp_stm:
        fcmd    { $$=$1; }
        | if_cmd        { $$=$1; }
        | assign_action { $$ = $1; }
        | LBRACE actions RBRACE { $$=$2; }
        ;
 +*/
  stm:
        action  { $$=$1; }
        | LBRACE actions RBRACE { $$=$2; }
@@@ -1803,8 -1862,6 +1827,8 @@@ actions
  action:
        fcmd SEMICOLON {$$=$1;}
        | if_cmd {$$=$1;}
 +      | switch_cmd {$$=$1;}
 +      | while_cmd { $$=$1; }
        | assign_action SEMICOLON {$$=$1;}
        | SEMICOLON /* null action */ {$$=0;}
        | fcmd error { $$=0; yyerror("bad command: missing ';'?"); }
@@@ -1813,127 -1870,6 +1837,127 @@@ if_cmd
        IF exp stm              { $$=mk_action( IF_T, 3, EXPR_ST, $2, ACTIONS_ST, $3, NOSUBTYPE, 0); }
        | IF exp stm ELSE stm   { $$=mk_action( IF_T, 3, EXPR_ST, $2, ACTIONS_ST, $3, ACTIONS_ST, $5); }
        ;
 +
 +ct_rval: rval_expr {
 +                      $$=0;
 +                      if (!rve_is_constant($1)){
 +                              yyerror("constant expected");
 +                      /*
 +                      } else if (!rve_check_type((enum rval_type*)&i_tmp, $1, 0, 0 ,0)){
 +                              yyerror("invalid expression (bad type)");
 +                      }else if (i_tmp!=RV_INT){
 +                              yyerror("invalid expression type, int expected\n");
 +                      */
 +                      }else
 +                              $$=$1;
 +              }
 +;
 +single_case:
 +      CASE ct_rval COLON actions {
 +              $$=0;
 +              if ($2==0) yyerror ("bad case label");
 +              else if ((($$=mk_case_stm($2, 0, $4, &i_tmp))==0) && (i_tmp==-10)){
 +                              YYABORT;
 +              }
 +      }
 +| CASE SLASH ct_rval COLON actions {
 +              $$=0;
 +              if ($3==0) yyerror ("bad case label");
 +              else if ((($$=mk_case_stm($3, 1, $5, &i_tmp))==0) && (i_tmp==-10)){
 +                              YYABORT;
 +              }
 +      }
 +      | CASE ct_rval COLON {
 +              $$=0;
 +              if ($2==0) yyerror ("bad case label");
 +              else if ((($$=mk_case_stm($2, 0, 0, &i_tmp))==0) && (i_tmp==-10)){
 +                              YYABORT;
 +              }
 +      }
 +      | CASE SLASH ct_rval COLON {
 +              $$=0;
 +              if ($3==0) yyerror ("bad case label");
 +              else if ((($$=mk_case_stm($3, 1, 0, &i_tmp))==0) && (i_tmp==-10)){
 +                              YYABORT;
 +              }
 +      }
 +      | DEFAULT COLON actions {
 +              if ((($$=mk_case_stm(0, 0, $3, &i_tmp))==0) && (i_tmp=-10)){
 +                              YYABORT;
 +              }
 +      }
 +      | DEFAULT COLON {
 +              if ((($$=mk_case_stm(0, 0, 0, &i_tmp))==0) && (i_tmp==-10)){
 +                              YYABORT;
 +              }
 +      }
 +      | CASE error { $$=0; yyerror("bad case label"); }
 +      | CASE ct_rval COLON error { $$=0; yyerror ("bad case body"); }
 +;
 +case_stms:
 +      case_stms single_case {
 +              $$=$1;
 +              if ($2==0) yyerror ("bad case");
 +              if ($$){
 +                      *($$->append)=$2;
 +                      if (*($$->append)!=0)
 +                              $$->append=&((*($$->append))->next);
 +              }
 +      }
 +      | single_case {
 +              $$=$1;
 +              if ($1==0) yyerror ("bad case");
 +              else $$->append=&($$->next);
 +      }
 +;
 +switch_cmd:
 +        SWITCH rval_expr LBRACE case_stms RBRACE { 
 +              $$=0;
 +              if ($2==0) yyerror("bad expression in switch(...)");
 +              else if ($4==0) yyerror ("bad switch body");
 +              else if (case_check_default($4)!=0)
 +                      yyerror_at(&$2->fpos, "bad switch(): too many "
 +                                                      "\"default:\" labels\n");
 +              else if (case_check_type($4)!=0)
 +                      yyerror_at(&$2->fpos, "bad switch(): mixed integer and"
 +                                                      " string/RE cases not allowed\n");
 +              else{
 +                      $$=mk_action(SWITCH_T, 2, RVE_ST, $2, CASE_ST, $4);
 +                      if ($$==0) {
 +                              yyerror("internal error");
 +                              YYABORT;
 +                      }
 +              }
 +      }
 +      | SWITCH rval_expr LBRACE RBRACE {
 +              $$=0;
 +              warn("empty switch()");
 +              if ($2==0) yyerror("bad expression in switch(...)");
 +              else{
 +                      /* it might have sideffects, so leave it for the optimizer */
 +                      $$=mk_action(SWITCH_T, 2, RVE_ST, $2, CASE_ST, 0);
 +                      if ($$==0) {
 +                              yyerror("internal error");
 +                              YYABORT;
 +                      }
 +              }
 +      }
 +      | SWITCH error { $$=0; yyerror ("bad expression in switch(...)"); }
 +      | SWITCH rval_expr LBRACE error RBRACE 
 +              {$$=0; yyerror ("bad switch body"); }
 +;
 +
 +while_cmd:
 +      WHILE rval_expr stm {
 +              if ($2){
 +                      if (rve_is_constant($2))
 +                              warn_at(&$2->fpos, "constant value in while(...)");
 +              }else
 +                      yyerror_at(&$2->fpos, "bad while(...) expression");
 +              $$=mk_action( WHILE_T, 2, RVE_ST, $2, ACTIONS_ST, $3);
 +      }
 +;
 +
  /* class_id:
        LBRACK ATTR_USER RBRACK { $$ = AVP_CLASS_USER; }
        | LBRACK ATTR_DOMAIN RBRACK { $$ = AVP_CLASS_DOMAIN; }
@@@ -2019,7 -1955,7 +2043,7 @@@ attr_id
        attr_mark attr_spec { $$ = s_attr; }
        ;
  attr_id_num_idx:
 -      attr_mark attr_spec LBRACK NUMBER RBRACK {
 +      attr_mark attr_spec LBRACK intno RBRACK {
                s_attr->type|= (AVP_NAME_STR | ($4<0?AVP_INDEX_BACKWARD:AVP_INDEX_FORWARD));
                s_attr->index = ($4<0?-$4:$4);
                $$ = s_attr;
@@@ -2035,12 -1971,10 +2059,12 @@@ attr_id_ass
        attr_id
        | attr_id_no_idx
        ;
 +/*
  attr_id_val:
        attr_id
        | attr_id_num_idx
        ;
 +*/
  attr_id_any:
        attr_id
        | attr_id_no_idx
@@@ -2063,7 -1997,7 +2087,7 @@@ attr_id_any_str
                s.len = strlen(s.s);
                if (parse_avp_name(&s, &type, &avp_spec->name, &idx)) {
                        yyerror("error when parsing AVP");
 -                      pkg_free(avp_spec);
 +                      pkg_free(avp_spec);
                        YYABORT;
                }
                avp_spec->type = type;
                $$ = avp_spec;
        }
        ;
 +
 +pvar: PVAR {
 +                      pv_spec=pkg_malloc(sizeof(*pv_spec));
 +                      if (!pv_spec) {
 +                              yyerror("Not enough memory");
 +                              YYABORT;
 +                      }
 +                      memset(pv_spec, 0, sizeof(*pv_spec));
 +                      s_tmp.s=$1; s_tmp.len=strlen($1);
 +                      if (pv_parse_spec(&s_tmp, pv_spec)==0){
 +                              yyerror("unknown script pseudo variable");
 +                              pkg_free(pv_spec);
 +                              pv_spec=0;
 +                              YYABORT;
 +                      }
 +                      $$=pv_spec;
 +              }
 +      ;
 +
 +avp_pvar:     AVP_OR_PVAR {
 +                              lval_tmp=pkg_malloc(sizeof(*lval_tmp));
 +                              if (!lval_tmp) {
 +                                      yyerror("Not enough memory");
 +                                      YYABORT;
 +                              }
 +                              memset(lval_tmp, 0, sizeof(*lval_tmp));
 +                              s_tmp.s=$1; s_tmp.len=strlen(s_tmp.s);
 +                              if (pv_parse_spec2(&s_tmp, &lval_tmp->lv.pvs, 1)==0){
 +                                      /* not a pvar, try avps */
 +                                      lval_tmp->lv.avps.type|= AVP_NAME_STR;
 +                                      lval_tmp->lv.avps.name.s.s = s_tmp.s+1;
 +                                      lval_tmp->lv.avps.name.s.len = s_tmp.len-1;
 +                                      lval_tmp->type=LV_AVP;
 +                              }else{
 +                                      lval_tmp->type=LV_PVAR;
 +                              }
 +                              $$ = lval_tmp;
 +                              DBG("parsed ambigous avp/pvar \"%.*s\" to %d\n",
 +                                                      s_tmp.len, s_tmp.s, lval_tmp->type);
 +                      }
 +      ;
 +
 +
  /*
  assign_op:
        ADDEQ { $$ = ADD_T; }
  assign_op:
        EQUAL { $$ = ASSIGN_T; }
        ;
 +
 +
 +lval: attr_id_ass {
 +                                      lval_tmp=pkg_malloc(sizeof(*lval_tmp));
 +                                      if (!lval_tmp) {
 +                                              yyerror("Not enough memory");
 +                                              YYABORT;
 +                                      }
 +                                      lval_tmp->type=LV_AVP; lval_tmp->lv.avps=*$1;
 +                                      pkg_free($1); /* free the avp spec we just copied */
 +                                      $$=lval_tmp;
 +                              }
 +      | pvar        {
 +                                      if (!pv_is_w($1))
 +                                              yyerror("read only pvar in assignment left side");
 +                                      if ($1->trans!=0)
 +                                              yyerror("pvar with transformations in assignment"
 +                                                              " left side");
 +                                      lval_tmp=pkg_malloc(sizeof(*lval_tmp));
 +                                      if (!lval_tmp) {
 +                                              yyerror("Not enough memory");
 +                                              YYABORT;
 +                                      }
 +                                      lval_tmp->type=LV_PVAR; lval_tmp->lv.pvs=*($1);
 +                                      pkg_free($1); /* free the pvar spec we just copied */
 +                                      $$=lval_tmp;
 +                              }
 +      | avp_pvar    {
 +                                      if (($1)->type==LV_PVAR){
 +                                              if (!pv_is_w(&($1)->lv.pvs))
 +                                                      yyerror("read only pvar in assignment left side");
 +                                              if ($1->lv.pvs.trans!=0)
 +                                                      yyerror("pvar with transformations in assignment"
 +                                                                      " left side");
 +                                      }
 +                                      $$=$1;
 +                              }
 +      ;
 +
 +rval: intno                   {$$=mk_rve_rval(RV_INT, (void*)$1); }
 +      | STRING                        {       s_tmp.s=$1; s_tmp.len=strlen($1);
 +                                                      $$=mk_rve_rval(RV_STR, &s_tmp); }
 +      | attr_id_any           {$$=mk_rve_rval(RV_AVP, $1); pkg_free($1); }
 +      | pvar                          {$$=mk_rve_rval(RV_PVAR, $1); pkg_free($1); }
 +      | avp_pvar                      {
 +                                                      switch($1->type){
 +                                                              case LV_AVP:
 +                                                                      $$=mk_rve_rval(RV_AVP, &$1->lv.avps);
 +                                                                      break;
 +                                                              case LV_PVAR:
 +                                                                      $$=mk_rve_rval(RV_PVAR, &$1->lv.pvs);
 +                                                                      break;
 +                                                              default:
 +                                                                      yyerror("BUG: invalid lvalue type ");
 +                                                                      YYABORT;
 +                                                      }
 +                                                      pkg_free($1); /* not needed anymore */
 +                                              }
 +      | select_id                     {$$=mk_rve_rval(RV_SEL, $1); pkg_free($1); }
 +      | fcmd                          {$$=mk_rve_rval(RV_ACTION_ST, $1); }
 +      | exp_elem { $$=mk_rve_rval(RV_BEXPR, $1); }
 +      | LBRACE actions RBRACE {$$=mk_rve_rval(RV_ACTION_ST, $2); }
 +      | LBRACE error RBRACE   { yyerror("bad command block"); }
 +      | LPAREN assign_action RPAREN   {$$=mk_rve_rval(RV_ACTION_ST, $2); }
 +      | LPAREN error RPAREN   { yyerror("bad expression"); }
 +      ;
 +
 +
 +rve_un_op: NOT        { $$=RVE_LNOT_OP; }
 +              |  MINUS %prec NOT      { $$=RVE_UMINUS_OP; } 
 +              /* TODO: RVE_BOOL_OP, RVE_NOT_OP? */
 +      ;
 +
 +/*
 +rve_op:               PLUS            { $$=RVE_PLUS_OP; }
 +              |       MINUS           { $$=RVE_MINUS_OP; }
 +              |       STAR            { $$=RVE_MUL_OP; }
 +              |       SLASH           { $$=RVE_DIV_OP; }
 +      ;
 +*/
 +
 +rval_expr: rval                                               { $$=$1;
 +                                                                                      if ($$==0){
 +                                                                                              yyerror("out of memory\n");
 +                                                                                              YYABORT;
 +                                                                                      }
 +                                                                      }
 +              | rve_un_op %prec NOT rval_expr {$$=mk_rve1($1, $2); }
 +              | rval_expr PLUS rval_expr              {$$=mk_rve2(RVE_PLUS_OP, $1, $3); }
 +              | rval_expr MINUS rval_expr             {$$=mk_rve2(RVE_MINUS_OP, $1, $3); }
 +              | rval_expr STAR rval_expr              {$$=mk_rve2(RVE_MUL_OP, $1, $3); }
 +              | rval_expr SLASH rval_expr             {$$=mk_rve2(RVE_DIV_OP, $1, $3); }
 +              | rval_expr BIN_OR rval_expr    {$$=mk_rve2(RVE_BOR_OP, $1,  $3); }
 +              | rval_expr BIN_AND rval_expr   {$$=mk_rve2(RVE_BAND_OP, $1,  $3);}
 +              | rval_expr rve_cmpop %prec GT rval_expr { $$=mk_rve2( $2, $1, $3);}
 +              | rval_expr rve_equalop %prec EQUAL_T rval_expr
 +                      { $$=mk_rve2( $2, $1, $3);}
 +              | rval_expr LOG_AND rval_expr   { $$=mk_rve2(RVE_LAND_OP, $1, $3);}
 +              | rval_expr LOG_OR rval_expr    { $$=mk_rve2(RVE_LOR_OP, $1, $3);}
 +              | LPAREN rval_expr RPAREN               { $$=$2;}
 +              | rve_un_op %prec NOT error             { yyerror("bad expression"); }
 +              | rval_expr PLUS error                  { yyerror("bad expression"); }
 +              | rval_expr MINUS error                 { yyerror("bad expression"); }
 +              | rval_expr STAR error                  { yyerror("bad expression"); }
 +              | rval_expr SLASH error                 { yyerror("bad expression"); }
 +              | rval_expr BIN_OR error                { yyerror("bad expression"); }
 +              | rval_expr BIN_AND error               { yyerror("bad expression"); }
 +              | rval_expr rve_cmpop %prec GT error
 +                      { yyerror("bad expression"); }
 +              | rval_expr rve_equalop %prec EQUAL_T error
 +                      { yyerror("bad expression"); }
 +              | rval_expr LOG_AND error               { yyerror("bad expression"); }
 +              | rval_expr LOG_OR error                { yyerror("bad expression"); }
 +              ;
 +
 +assign_action: lval assign_op  rval_expr      { $$=mk_action($2, 2, LVAL_ST, $1, 
 +                                                                                                                        RVE_ST, $3);
 +                                                                              }
 +      ;
 +
 +/*
  assign_action:
        attr_id_ass assign_op STRING  { $$=mk_action($2, 2, AVP_ST, $1, STRING_ST, $3); }
        | attr_id_ass assign_op NUMBER  { $$=mk_action($2, 2, AVP_ST, $1, NUMBER_ST, (void*)$3); }
        | attr_id_ass assign_op select_id { $$=mk_action($2, 2, AVP_ST, (void*)$1, SELECT_ST, (void*)$3); }
        | attr_id_ass assign_op LPAREN exp RPAREN { $$ = mk_action($2, 2, AVP_ST, $1, EXPR_ST, $4); }
        ;
 +*/
 +
  avpflag_oper:
        SETAVPFLAG { $$ = 1; }
        | RESETAVPFLAG { $$ = 0; }
@@@ -2471,7 -2239,7 +2495,7 @@@ cmd
        | RETURN                        {$$=mk_action(DROP_T, 2, NUMBER_ST, (void*)1, NUMBER_ST, (void*)RETURN_R_F); }
        | RETURN NUMBER                 {$$=mk_action(DROP_T, 2, NUMBER_ST, (void*)$2, NUMBER_ST, (void*)RETURN_R_F);}
        | RETURN RETCODE                {$$=mk_action(DROP_T, 2, RETCODE_ST, 0, NUMBER_ST, (void*)RETURN_R_F);}
 -      | BREAK                         {$$=mk_action(DROP_T, 2, NUMBER_ST, 0, NUMBER_ST, (void*)RETURN_R_F); }
 +      | BREAK                         {$$=mk_action(DROP_T, 2, NUMBER_ST, 0, NUMBER_ST, (void*)BREAK_R_F); }
        | LOG_TOK LPAREN STRING RPAREN  {$$=mk_action(LOG_T, 2, NUMBER_ST,
                                                                                (void*)(L_DBG+1), STRING_ST, $3); }
        | LOG_TOK LPAREN NUMBER COMMA STRING RPAREN     {$$=mk_action(LOG_T, 2, NUMBER_ST, (void*)$3, STRING_ST, $5); }
        | STRIP LPAREN NUMBER RPAREN { $$=mk_action(STRIP_T, 1, NUMBER_ST, (void*) $3); }
        | STRIP error { $$=0; yyerror("missing '(' or ')' ?"); }
        | STRIP LPAREN error RPAREN { $$=0; yyerror("bad argument, number expected"); }
+       | SET_USERPHONE LPAREN RPAREN { $$=mk_action(SET_USERPHONE_T, 0); }
+       | SET_USERPHONE error { $$=0; yyerror("missing '(' or ')' ?"); }
        | APPEND_BRANCH LPAREN STRING COMMA STRING RPAREN {
                qvalue_t q;
                if (str2q(&q, $5, strlen($5)) < 0) {
                $$=0; yyerror("bad argument, [proto:]host[:port] expected");
        }
        | FORCE_SEND_SOCKET error {$$=0; yyerror("missing '(' or ')' ?"); }
 -      | ID {mod_func_action = mk_action(MODULE_T, 2, MODEXP_ST, NULL, NUMBER_ST, 0); } LPAREN func_params RPAREN      {
 -              mod_func_action->val[0].u.data = find_export_record($1, mod_func_action->val[1].u.number, rt);
 +      | ID {mod_func_action = mk_action(MODULE_T, 2, MODEXP_ST, NULL, NUMBER_ST,
 +                      0); } LPAREN func_params RPAREN {
 +              mod_func_action->val[0].u.data = 
 +                      find_export_record($1, mod_func_action->val[1].u.number, rt,
 +                                                              &u_tmp);
                if (mod_func_action->val[0].u.data == 0) {
 -                      if (find_export_record($1, mod_func_action->val[1].u.number, 0) ) {
 +                      if (find_export_record($1, mod_func_action->val[1].u.number, 0,
 +                                                                      &u_tmp) ) {
                                        yyerror("Command cannot be used in the block\n");
                        } else {
                                yyerror("unknown command, missing loadmodule?\n");
                        }
                        pkg_free(mod_func_action);
                        mod_func_action=0;
 +              }else{
 +                      switch( ((union cmd_export_u*)
 +                                              mod_func_action->val[0].u.data)->c.param_no){
 +                              case 0:
 +                              case 1:
 +                              case 2:
 +                                      /* MODULE_T used for 0-2 params */
 +                                      break;
 +                              case 3:
 +                                      mod_func_action->type=MODULE3_T;
 +                                      break;
 +                              case 4:
 +                                      mod_func_action->type=MODULE4_T;
 +                                      break;
 +                              case 5:
 +                                      mod_func_action->type=MODULE5_T;
 +                                      break;
 +                              case 6:
 +                                      mod_func_action->type=MODULE6_T;
 +                                      break;
 +                              case VAR_PARAM_NO:
 +                                      mod_func_action->type=MODULEX_T;
 +                                      break;
 +                              default:
 +                                      yyerror("too many parameters for function\n");
 +                                      break;
 +                      }
                }
                $$ = mod_func_action;
        }
 +      | ID error                                      { yyerror("'('')' expected (function call)");}
        ;
  func_params:
        /* empty */
        | func_params COMMA func_param { }
        | func_param {}
 -      | func_params error { yyerror("call params error\n"); YYABORT; }
 +      | func_params error { yyerror("call params error\n"); }
        ;
  func_param:
          NUMBER {
                if (mod_func_action->val[1].u.number < MAX_ACTIONS-2) {
 -                      mod_func_action->val[mod_func_action->val[1].u.number+2].type = NUMBER_ST;
 -                      mod_func_action->val[mod_func_action->val[1].u.number+2].u.number = $1;
 +                      mod_func_action->val[mod_func_action->val[1].u.number+2].type =
 +                              NUMBER_ST;
 +                      mod_func_action->val[mod_func_action->val[1].u.number+2].u.number =
 +                              $1;
                        mod_func_action->val[1].u.number++;
                } else {
                        yyerror("Too many arguments\n");
  extern int line;
  extern int column;
  extern int startcolumn;
 -static void warn(char* s)
 +extern int startline;
 +
 +
 +static void get_cpos(struct cfg_pos* pos)
  {
 -      LOG(L_WARN, "cfg. warning: (%d,%d-%d): %s\n", line, startcolumn,
 -                      column, s);
 +      pos->s_line=startline;
 +      pos->e_line=line;
 +      pos->s_col=startcolumn;
 +      pos->e_col=column-1;
 +}
 +
 +
 +static void warn_at(struct cfg_pos* p, char* format, ...)
 +{
 +      va_list ap;
 +      char s[256];
 +      
 +      va_start(ap, format);
 +      vsnprintf(s, sizeof(s), format, ap);
 +      va_end(ap);
 +      if (p->e_line!=p->s_line)
 +              LOG(L_WARN, "cfg. warning: (%d,%d-%d,%d): %s\n",
 +                                      p->s_line, p->s_col, p->e_line, p->e_col, s);
 +      else if (p->s_col!=p->e_col)
 +              LOG(L_WARN, "cfg. warning: (%d,%d-%d): %s\n",
 +                                      p->s_line, p->s_col, p->e_col, s);
 +      else
 +              LOG(L_WARN, "cfg. warning: (%d,%d): %s\n",
 +                              p->s_line, p->s_col, s);
        cfg_warnings++;
  }
  
 -static void yyerror(char* s)
 +
 +
 +static void yyerror_at(struct cfg_pos* p, char* format, ...)
  {
 -      LOG(L_CRIT, "*** PARSE ERROR *** (%d,%d-%d): %s\n", line, startcolumn,
 -                      column, s);
 +      va_list ap;
 +      char s[256];
 +      
 +      va_start(ap, format);
 +      vsnprintf(s, sizeof(s), format, ap);
 +      va_end(ap);
 +      if (p->e_line!=p->s_line)
 +              LOG(L_CRIT, "*** PARSE ERROR *** (%d,%d-%d,%d): %s\n", 
 +                                      p->s_line, p->s_col, p->e_line, p->e_col, s);
 +      else if (p->s_col!=p->e_col)
 +              LOG(L_CRIT, "*** PARSE ERROR *** (%d,%d-%d): %s\n", 
 +                                      p->s_line, p->s_col, p->e_col, s);
 +      else
 +              LOG(L_CRIT, "*** PARSE ERROR *** (%d,%d): %s\n", 
 +                                      p->s_line, p->s_col, s);
        cfg_errors++;
  }
  
  
 +
 +static void warn(char* format, ...)
 +{
 +      va_list ap;
 +      char s[256];
 +      struct cfg_pos pos;
 +      
 +      get_cpos(&pos);
 +      va_start(ap, format);
 +      vsnprintf(s, sizeof(s), format, ap);
 +      va_end(ap);
 +      warn_at(&pos, s);
 +}
 +
 +
 +
 +static void yyerror(char* format, ...)
 +{
 +      va_list ap;
 +      char s[256];
 +      struct cfg_pos pos;
 +      
 +      get_cpos(&pos);
 +      va_start(ap, format);
 +      vsnprintf(s, sizeof(s), format, ap);
 +      va_end(ap);
 +      yyerror_at(&pos, s);
 +}
 +
 +
 +
 +/** mk_rval_expr_v wrapper.
 + *  checks mk_rval_expr_v return value and sets the cfg. pos
 + *  (line and column numbers)
 + *  @return rval_expr* on success, 0 on error (@see mk_rval_expr_v)
 + */
 +static struct rval_expr* mk_rve_rval(enum rval_type type, void* v)
 +{
 +      struct rval_expr* ret;
 +      struct cfg_pos pos;
 +
 +      get_cpos(&pos);
 +      ret=mk_rval_expr_v(type, v, &pos);
 +      if (ret==0){
 +              yyerror("internal error: failed to create rval expr");
 +              /* YYABORT; */
 +      }
 +      return ret;
 +}
 +
 +
 +/** mk_rval_expr1 wrapper.
 + *  checks mk_rval_expr1 return value (!=0 and type checking)
 + *  @return rval_expr* on success, 0 on error (@see mk_rval_expr1)
 + */
 +static struct rval_expr* mk_rve1(enum rval_expr_op op, struct rval_expr* rve1)
 +{
 +      struct rval_expr* ret;
 +      struct rval_expr* bad_rve;
 +      enum rval_type type, bad_t, exp_t;
 +      
 +      if (rve1==0)
 +              return 0;
 +      ret=mk_rval_expr1(op, rve1, &rve1->fpos);
 +      if (ret && (rve_check_type(&type, ret, &bad_rve, &bad_t, &exp_t)!=1)){
 +              yyerror_at(&rve1->fpos, "bad expression: type mismatch"
 +                                      " (%s instead of %s)", rval_type_name(bad_t),
 +                                      rval_type_name(exp_t));
 +      }
 +      return ret;
 +}
 +
 +
 +/** mk_rval_expr2 wrapper.
 + *  checks mk_rval_expr2 return value (!=0 and type checking)
 + *  @return rval_expr* on success, 0 on error (@see mk_rval_expr2)
 + */
 +static struct rval_expr* mk_rve2(enum rval_expr_op op, struct rval_expr* rve1,
 +                                                                      struct rval_expr* rve2)
 +{
 +      struct rval_expr* ret;
 +      struct rval_expr* bad_rve;
 +      enum rval_type type, bad_t, exp_t;
 +      struct cfg_pos pos;
 +      
 +      if ((rve1==0) || (rve2==0))
 +              return 0;
 +      cfg_pos_join(&pos, &rve1->fpos, &rve2->fpos);
 +      ret=mk_rval_expr2(op, rve1, rve2, &pos);
 +      if (ret && (rve_check_type(&type, ret, &bad_rve, &bad_t, &exp_t)!=1)){
 +              yyerror_at(&pos, "bad expression: type mismatch:"
 +                                              " %s instead of %s at (%d,%d)",
 +                                              rval_type_name(bad_t), rval_type_name(exp_t),
 +                                              bad_rve->fpos.s_line, bad_rve->fpos.s_col);
 +      }
 +      return ret;
 +}
 +
 +
  static struct name_lst* mk_name_lst(char* host, int flags)
  {
        struct name_lst* l;
@@@ -2957,101 -2554,6 +2983,101 @@@ static void free_socket_id_lst(struct s
        }
  }
  
 +
 +/** create a temporary case statmenet structure.
 + *  *err will be filled in case of error (return == 0):
 + *   -1 - non constant expression
 + *   -2 - expression error (bad type)
 + *   -10 - memory allocation error
 + */
 +static struct case_stms* mk_case_stm(struct rval_expr* ct, int is_re,
 +                                                                                      struct action* a, int* err)
 +{
 +      struct case_stms* s;
 +      struct rval_expr* bad_rve;
 +      enum rval_type type, bad_t, exp_t;
 +      enum match_str_type t;
 +      
 +      t=MATCH_UNKNOWN;
 +      if (ct){
 +              /* if ct!=0 => case, else if ct==0 is a default */
 +              if (!rve_is_constant(ct)){
 +                      yyerror_at(&ct->fpos, "non constant expression in case");
 +                      *err=-1;
 +                      return 0;
 +              }
 +              if (rve_check_type(&type, ct, &bad_rve, &bad_t, &exp_t)!=1){
 +                      yyerror_at(&ct->fpos, "bad expression: type mismatch:"
 +                                                      " %s instead of %s at (%d,%d)",
 +                                                      rval_type_name(bad_t), rval_type_name(exp_t),
 +                                                      bad_rve->fpos.s_line, bad_rve->fpos.s_col);
 +                      *err=-2;
 +                      return 0;
 +              }
 +              if (is_re)
 +                      t=MATCH_RE;
 +              else if (type==RV_STR)
 +                      t=MATCH_STR;
 +              else
 +                      t=MATCH_INT;
 +      }
 +
 +      s=pkg_malloc(sizeof(*s));
 +      if (s==0) {
 +              yyerror("internal error: memory allocation failure");
 +              *err=-10;
 +      } else {
 +              memset(s, 0, sizeof(*s));
 +              s->ct_rve=ct;
 +              s->type=t;
 +              s->actions=a;
 +              s->next=0;
 +              s->append=0;
 +      }
 +      return s;
 +}
 +
 +
 +/*
 + * @return 0 on success, -1 on error.
 + */
 +static int case_check_type(struct case_stms* stms)
 +{
 +      struct case_stms* c;
 +      struct case_stms* s;
 +      
 +      for(c=stms; c ; c=c->next){
 +              if (!c->ct_rve) continue;
 +              for (s=c->next; s; s=s->next){
 +                      if (!s->ct_rve) continue;
 +                      if ((s->type!=c->type) &&
 +                              !(      (c->type==MATCH_STR || c->type==MATCH_RE) &&
 +                                      (s->type==MATCH_STR || s->type==MATCH_RE) ) ){
 +                                      yyerror_at(&s->ct_rve->fpos, "type mismatch in case");
 +                                      return -1;
 +                      }
 +              }
 +      }
 +      return 0;
 +}
 +
 +
 +/*
 + * @return 0 on success, -1 on error.
 + */
 +static int case_check_default(struct case_stms* stms)
 +{
 +      struct case_stms* c;
 +      int default_no;
 +      
 +      default_no=0;
 +      for(c=stms; c ; c=c->next)
 +              if (c->ct_rve==0) default_no++;
 +      return (default_no<=1)?0:-1;
 +}
 +
 +
 +
  /*
  int main(int argc, char ** argv)
  {
diff --combined core_cmd.c
@@@ -199,8 -199,7 +199,8 @@@ static void system_listMethods(rpc_t* r
        }
  
        for(t = modules; t; t = t->next) {
 -              for(ptr = t->exports->rpc_methods; ptr && ptr->name; ptr++) {
 +              if (t->mod_interface_ver!=0) continue;
 +              for(ptr = t->exports->v0.rpc_methods; ptr && ptr->name; ptr++) {
                        if (rpc->add(c, "s", ptr->name) < 0) return;
                }
        }
@@@ -234,8 -233,7 +234,8 @@@ static void system_methodHelp(rpc_t* rp
        }
  
        for(t = modules; t; t = t->next) {
 -              for(ptr = t->exports->rpc_methods; ptr && ptr->name; ptr++) {
 +              if (t->mod_interface_ver!=0) continue;
 +              for(ptr = t->exports->v0.rpc_methods; ptr && ptr->name; ptr++) {
                        if (strcmp(name, ptr->name) == 0) {
                                if (ptr->doc_str && ptr->doc_str[0]) {
                                        rpc->add(c, "s", ptr->doc_str[0]);
@@@ -567,10 -565,10 +567,10 @@@ static void core_tcp_options(rpc_t* rpc
        if (!tcp_disable){
                tcp_options_get(&t);
                rpc->add(c, "{", &handle);
-               rpc->struct_add(handle, "ddddddddddddddddddddddd",
+               rpc->struct_add(handle, "dddddddddddddddddddddd",
                        "connect_timeout", t.connect_timeout_s,
-                       "send_timeout",  t.send_timeout_s,
-                       "connection_lifetime",  t.con_lifetime_s,
+                       "send_timeout",  TICKS_TO_S(t.send_timeout),
+                       "connection_lifetime",  TICKS_TO_S(t.con_lifetime),
                        "max_connections(soft)", t.max_connections,
                        "no_connect",   t.no_connect,
                        "fd_cache",             t.fd_cache,
                        "connect_wait", t.tcp_connect_wait,
                        "conn_wq_max",  t.tcpconn_wq_max,
                        "wq_max",               t.tcp_wq_max,
-                       "wq_timeout",   TICKS_TO_S(t.tcp_wq_timeout),
                        "defer_accept", t.defer_accept,
                        "delayed_ack",  t.delayed_ack,
                        "syncnt",               t.syncnt,
diff --combined dprint.c
+++ b/dprint.c
@@@ -63,7 -63,6 +63,7 @@@ static int int_fac[]={LOG_AUTH ,  LOG_C
  
  struct log_level_info log_level_info[] = {
        {"ALERT", LOG_ALERT},     /* L_ALERT */
 +      {"CRITICAL", LOG_CRIT},   /* L_CRIT2 */
        {"BUG", LOG_CRIT},        /* L_CRIT */
        {"ERROR", LOG_ERR},       /* L_ERR */
        {"WARNING", LOG_WARNING}, /* L_WARN */
@@@ -84,7 -83,7 +84,7 @@@ int str2facility(char *s
  }
  
  /* fixup function for log_facility cfg parameter */
- int log_facility_fixup(void *handle, str *name, void **val)
+ int log_facility_fixup(void *handle, str *gname, str *name, void **val)
  {
        int     i;
  
diff --combined dprint.h
+++ b/dprint.h
  /*
   * Log levels
   */
 -#define L_ALERT               -3
 -#define L_CRIT        -2
 +#define L_ALERT               -4
 +#define L_CRIT2               -3  /* like L_CRIT, but not used for BUGs */
 +#define L_CRIT        -2  /* used only for BUG */
  #define L_ERR         -1
  #define L_WARN        0
  #define L_NOTICE      1
  #define L_INFO        2
  #define L_DBG         3
  
 +/* This is the facility value used to indicate that the caller of the macro
 + * did not override the facility. Value 0 (the defaul) is LOG_KERN on Linux
 + */
 +#define DEFAULT_FACILITY 0
 +
  #define LOG_LEVEL2NAME(level) (log_level_info[(level) - (L_ALERT)].name)
  #define LOG2SYSLOG_LEVEL(level) \
        (log_level_info[(level) - (L_ALERT)].syslog_level)
@@@ -106,7 -100,6 +106,7 @@@ struct log_level_info 
        int syslog_level;
  };
  
 +#define is_printable(level) (cfg_get(core, core_cfg, debug)>=(level))
  extern struct log_level_info log_level_info[];
  
  #ifndef NO_SIG_DEBUG
@@@ -115,7 -108,7 +115,7 @@@ extern volatile int dprint_crit
  #endif
  
  int str2facility(char *s);
- int log_facility_fixup(void *handle, str *name, void **val);
+ int log_facility_fixup(void *handle, str *gname, str *name, void **val);
  
  
  /*
  #ifdef NO_LOG
  
  #     ifdef __SUNPRO_C
 -#             define LOG_(level, prefix, fmt, ...)
 +#             define LOG_(facility, level, prefix, fmt, ...)
  #             define LOG(level, fmt, ...)
 +#             define LOG_FC(facility, level, fmt, ...)
  #     else
 -#             define LOG_(level, prefix, fmt, args...)
 +#             define LOG_(facility, level, prefix, fmt, args...)
  #             define LOG(level, fmt, args...)
 +#             define LOG_FC(facility, level, fmt, args...)
  #     endif
  
  #else
  #     endif
  
  #     ifdef __SUNPRO_C
 -#             define LOG_(level, prefix, fmt, ...) \
 +#             define LOG_(facility, level, prefix, fmt, ...) \
                        do { \
                                if (unlikely(cfg_get(core, core_cfg, debug) >= (level) && \
                                                DPRINT_NON_CRIT)) { \
                                                                        __VA_ARGS__); \
                                                } else { \
                                                        syslog(LOG2SYSLOG_LEVEL(level) | \
 -                                                                      cfg_get(core, core_cfg, log_facility),\
 +                                                                 (((facility) != DEFAULT_FACILITY) ? \
 +                                                                      (facility) : \
 +                                                                      cfg_get(core, core_cfg, log_facility)), \
                                                                        "%s: %s" fmt, LOG_LEVEL2NAME(level),\
                                                                        (prefix), __VA_ARGS__); \
                                                } \
                                                } else { \
                                                        if ((level)<L_ALERT) \
                                                                syslog(LOG2SYSLOG_LEVEL(L_ALERT) | \
 -                                                                              cfg_get(core, core_cfg, log_facility),\
 -                                                                              "%s" fmt, (prefix), __VA_ARGS__); \
 +                                                                         (((facility) != DEFAULT_FACILITY) ? \
 +                                                                              (facility) : \
 +                                                                              cfg_get(core, core_cfg, log_facility)),\
 +                                                                         "%s" fmt, (prefix), __VA_ARGS__); \
                                                        else \
                                                                syslog(LOG2SYSLOG_LEVEL(L_DBG) | \
 -                                                                              cfg_get(core, core_cfg, log_facility),\
 -                                                                              "%s" fmt, (prefix), __VA_ARGS__); \
 +                                                                         (((facility) != DEFAULT_FACILITY) ? \
 +                                                                              (facility) : \
 +                                                                              cfg_get(core, core_cfg, log_facility)),\
 +                                                                         "%s" fmt, (prefix), __VA_ARGS__); \
                                                } \
                                        } \
                                        DPRINT_CRIT_EXIT; \
                                } \
                        } while(0)
                        
 -#             define LOG(level, fmt, ...)  LOG_((level), LOC_INFO, fmt, __VA_ARGS__)
 +#             define LOG(level, fmt, ...) \
 +      LOG_(DEFAULT_FACILITY, (level), LOC_INFO, fmt, __VA_ARGS__)
 +#             define LOG_FC(facility, level, fmt, ...) \
 +      LOG_((facility), (level), LOC_INFO, fmt, __VA_ARGS__)
  
  #     else /* ! __SUNPRO_C */
 -#             define LOG_(level, prefix, fmt, args...) \
 +#             define LOG_(facility, level, prefix, fmt, args...) \
                        do { \
                                if (cfg_get(core, core_cfg, debug) >= (level) && \
                                                DPRINT_NON_CRIT) { \
                                                                        LOG_LEVEL2NAME(level),(prefix), ## args);\
                                                } else { \
                                                        syslog(LOG2SYSLOG_LEVEL(level) |\
 -                                                                      cfg_get(core, core_cfg, log_facility), \
 +                                                                 (((facility) != DEFAULT_FACILITY) ? \
 +                                                                      (facility) : \
 +                                                                      cfg_get(core, core_cfg, log_facility)), \
                                                                        "%s: %s" fmt, LOG_LEVEL2NAME(level),\
                                                                        (prefix), ## args); \
                                                } \
                                                } else { \
                                                        if ((level)<L_ALERT) \
                                                                syslog(LOG2SYSLOG_LEVEL(L_ALERT) | \
 -                                                                              cfg_get(core, core_cfg, log_facility),\
 +                                                                         (((facility) != DEFAULT_FACILITY) ? \
 +                                                                              (facility) : \
 +                                                                              cfg_get(core, core_cfg, log_facility)),\
                                                                                "%s" fmt, (prefix), ## args); \
                                                        else \
                                                                syslog(LOG2SYSLOG_LEVEL(L_DBG) | \
 -                                                                              cfg_get(core, core_cfg, log_facility),\
 +                                                                         (((facility) != DEFAULT_FACILITY) ? \
 +                                                                              (facility) : \
 +                                                                              cfg_get(core, core_cfg, log_facility)),\
                                                                                "%s" fmt, (prefix), ## args); \
                                                } \
                                        } \
                                } \
                        } while(0)
                        
 -#             define LOG(level, fmt, args...)  LOG_((level), LOC_INFO, fmt, ## args)
 +#             define LOG(level, fmt, args...) \
 +      LOG_(DEFAULT_FACILITY, (level), LOC_INFO, fmt, ## args)
 +#             define LOG_FC(facility, level, fmt, args...) \
 +      LOG_((facility), (level), LOC_INFO, fmt, ## args)
                
  #     endif /* __SUNPRO_C */
  #endif /* NO_LOG */
  #     define WARN(...)   LOG(L_WARN,   __VA_ARGS__)
  #     define NOTICE(...) LOG(L_NOTICE, __VA_ARGS__)
  #     define INFO(...)   LOG(L_INFO,   __VA_ARGS__)
 +#     define CRIT(...)    LOG(L_CRIT2,   __VA_ARGS__)
  
  #     ifdef NO_DEBUG
  #             define DBG(...)
  
  /* obsolete, do not use */
  #     define DEBUG(...) DBG(__VA_ARGS__)
 -              
 -#else
 +
 +#else /* ! __SUNPRO_C */
  #     define ALERT(fmt, args...)  LOG(L_ALERT,  fmt, ## args)
  #     define BUG(fmt, args...)    LOG(L_CRIT,   fmt, ## args)
  #     define ERR(fmt, args...)    LOG(L_ERR,    fmt, ## args)
  #     define WARN(fmt, args...)   LOG(L_WARN,   fmt, ## args)
  #     define NOTICE(fmt, args...) LOG(L_NOTICE, fmt, ## args)
  #     define INFO(fmt, args...)   LOG(L_INFO,   fmt, ## args)
 +#     define CRIT(fmt, args...)   LOG(L_CRIT2,   fmt, ## args)
  
  #     ifdef NO_DEBUG
  #             define DBG(fmt, args...)
  #endif /* __SUNPRO_C */
  
  
 +/* kamailio/openser compatibility */
 +
 +#define LM_GEN1 LOG
 +#define LM_GEN2 LOG_FC
 +#define LM_ALERT ALERT
 +#define LM_CRIT  CRIT
 +#define LM_ERR ERR
 +#define LM_WARN WARN
 +#define LM_NOTICE NOTICE
 +#define LM_INFO INFO
 +#define LM_DBG DEBUG
 +
  #endif /* !dprint_h */
diff --combined modules/tm/t_fwd.c
@@@ -97,7 -97,6 +97,7 @@@
  #include "../../data_lump.h"
  #include "../../onsend.h"
  #include "../../compiler_opt.h"
 +#include "../../route.h"
  #include "t_funcs.h"
  #include "t_hooks.h"
  #include "t_msgbuilder.h"
@@@ -127,8 -126,8 +127,8 @@@ void t_on_branch( unsigned int go_to 
  {
        struct cell *t = get_t();
  
 -       /* in MODE_REPLY and MODE_ONFAILURE T will be set to current transaction;
 -        * in MODE_REQUEST T will be set only if the transaction was already
 +       /* in REPLY_ROUTE and FAILURE_ROUTE T will be set to current transaction;
 +        * in REQUEST_ROUTE T will be set only if the transaction was already
          * created; if not -> use the static variable */
        if (!t || t==T_UNDEFINED ) {
                goto_on_branch=go_to;
@@@ -192,7 -191,6 +192,7 @@@ static char *print_uac_request( struct 
        if (unlikely(branch_route)) {
                reset_static_buffer();
                     /* run branch_route actions if provided */
 +              set_route_type(BRANCH_ROUTE);
                init_run_actions_ctx(&ra_ctx);
                if (run_actions(&ra_ctx, branch_rt.rlist[branch_route], i_req) < 0) {
                        LOG(L_ERR, "ERROR: print_uac_request: Error in run_actions\n");
@@@ -814,7 -812,7 +814,7 @@@ void e2e_cancel( struct sip_msg *cancel
                 * is called (we are already holding the reply mutex for the cancel
                 * transaction).
                 */
 -              if ((rmode==MODE_ONFAILURE) && (t_cancel==get_t()))
 +              if ((is_route_type(FAILURE_ROUTE)) && (t_cancel==get_t()))
                        t_reply_unsafe( t_cancel, cancel_msg, 500, "cancel error");
                else
                        t_reply( t_cancel, cancel_msg, 500, "cancel error");
                 * is called (we are already hold the reply mutex for the cancel
                 * transaction).
                 */
 -              if ((rmode==MODE_ONFAILURE) && (t_cancel==get_t()))
 +              if ((is_route_type(FAILURE_ROUTE)) && (t_cancel==get_t()))
                        t_reply_unsafe( t_cancel, cancel_msg, 200, CANCELING );
                else
                        t_reply( t_cancel, cancel_msg, 200, CANCELING );
                 * is called (we are already hold the reply mutex for the cancel
                 * transaction).
                 */
 -              if ((rmode==MODE_ONFAILURE) && (t_cancel==get_t()))
 +              if ((is_route_type(FAILURE_ROUTE)) && (t_cancel==get_t()))
                        t_reply_unsafe( t_cancel, cancel_msg, 200, CANCEL_DONE );
                else
                        t_reply( t_cancel, cancel_msg, 200, CANCEL_DONE );
@@@ -1048,7 -1046,7 +1048,7 @@@ int t_forward_nonack( struct cell *t, s
        if (first_branch==0) {
  #ifdef POSTPONE_MSG_CLONING
                /* update the shmem-ized msg with the lumps */
 -              if ((rmode == MODE_REQUEST) &&
 +              if ((is_route_type(REQUEST_ROUTE)) &&
                        save_msg_lumps(t->uas.request, p_msg)) {
                                LOG(L_ERR, "ERROR: t_forward_nonack: "
                                        "failed to save the message lumps\n");
        ser_error=0; /* clear branch adding errors */
        /* send them out now */
        success_branch=0;
 -      lock_replies= ! ((rmode==MODE_ONFAILURE) && (t==get_t()));
 +      lock_replies= ! ((is_route_type(FAILURE_ROUTE)) && (t==get_t()));
        for (i=first_branch; i<t->nr_of_outgoings; i++) {
                if (added_branches & (1<<i)) {
                        
@@@ -1299,7 -1297,7 +1299,7 @@@ int t_replicate(struct sip_msg *p_msg
  }
  
  /* fixup function for reparse_on_dns_failover modparam */
- int reparse_on_dns_failover_fixup(void *handle, str *name, void **val)
+ int reparse_on_dns_failover_fixup(void *handle, str *gname, str *name, void **val)
  {
  #ifdef USE_DNS_FAILOVER
        if ((int)(long)(*val) && mhomed) {
diff --combined msg_translator.c
  #include "ut.h"
  #include "pt.h"
  #include "cfg/cfg.h"
 +#include "parser/parse_to.h"
  #include "forward.h"
  
  
@@@ -163,7 -162,7 +163,7 @@@ static unsigned int global_req_flags=0
  /** per process fixup function for global_req_flags.
    * It should be called from the configuration framework.
    */
- void fix_global_req_flags( str* name)
+ void fix_global_req_flags(str* gname, str* name)
  {
        global_req_flags=0;
        switch(cfg_get(core, core_cfg, udp_mtu_try_proto)){
@@@ -1359,6 -1358,10 +1359,10 @@@ static inline int adjust_clen(struct si
        struct lump* anchor;
        char* clen_buf;
        int clen_len, body_only;
+ #ifdef USE_TCP
+       char* body;
+       int comp_clen;
+ #endif /* USE_TCP */
  
        /* Calculate message length difference caused by lumps modifying message
         * body, from this point on the message body must not be modified. Zero
                        LOG(L_ERR, "adjust_clen: error parsing content-length\n");
                        goto error;
                }
-               if (msg->content_length==0){
+               if (unlikely(msg->content_length==0)){
                        /* not present, we need to add it */
                        /* msg->unparsed should point just before the final crlf
                         * - whole message was parsed by the above parse_headers
                                goto error;
                        }
                        body_only=0;
+               }else{
+                       /* compute current content length and compare it with the
+                          one in the message */
+                       body=get_body(msg);
+                       if (unlikely(body==0)){
+                               ser_error=E_BAD_REQ;
+                               LOG(L_ERR, "adjust_clen: no message body found"
+                                               " (missing crlf?)");
+                               goto error;
+                       }
+                       comp_clen=msg->len-(int)(body-msg->buf)+body_delta;
+                       if (comp_clen!=(int)(long)msg->content_length->parsed){
+                               /* note: we don't distinguish here between received with
+                                  wrong content-length and content-length changed, we just
+                                  fix it automatically in both cases (the reason being
+                                  that an error message telling we have received a msg-
+                                  with wrong content-length is of very little use) */
+                               anchor = del_lump(msg, msg->content_length->body.s-msg->buf,
+                                                                       msg->content_length->body.len,
+                                                                       HDR_CONTENTLENGTH_T);
+                               if (anchor==0) {
+                                       LOG(L_ERR, "adjust_clen: Can't remove original"
+                                                               " Content-Length\n");
+                                       goto error;
+                               }
+                               body_only=1;
+                       }
                }
-       }
- #endif
-       if ((anchor==0) && body_delta){
+       }else
+ #endif /* USE_TCP */
+       if (body_delta){
                if (parse_headers(msg, HDR_CONTENTLENGTH_F, 0) == -1) {
                        LOG(L_ERR, "adjust_clen: Error parsing Content-Length\n");
                        goto error;
                                        goto error;
                                }
                                body_only=0;
-                       }else{
-                               DBG("adjust_clen: UDP packet with no clen => not adding one \n");
-                       }
+                       } /* else
+                               DBG("adjust_clen: UDP packet with no clen => "
+                                               "not adding one \n"); */
                }else{
                        /* Content-Length has been found, remove it */
                        anchor = del_lump(      msg, msg->content_length->body.s - msg->buf,
-                                                               msg->content_length->body.len, HDR_CONTENTLENGTH_T);
+                                                               msg->content_length->body.len,
+                                                               HDR_CONTENTLENGTH_T);
                        if (anchor==0) {
                                LOG(L_ERR, "adjust_clen: Can't remove original"
                                                        " Content-Length\n");
                                        HDR_CONTENTLENGTH_T) == 0)
                        goto error;
        }
+       
        return 0;
  error:
        if (clen_buf) pkg_free(clen_buf);
diff --combined parser/parse_param.c
  #include "parse_param.h"
  
  
 +static inline void parse_event_dialog_class(param_hooks_t* h, param_t* p)
 +{
 +
 +      if (!p->name.s) {
 +              LOG(L_ERR, "ERROR: parse_event_dialog_class: empty value\n");
 +              return;
 +      }
 +      if (!h) {
 +              LOG(L_CRIT, "BUG: parse_event_dialog_class: NULL param hook pointer\n");
 +              return;
 +      }
 +      switch(p->name.s[0]) {
 +      case 'c':
 +      case 'C':
 +              if ((p->name.len == 7) &&
 +                  (!strncasecmp(p->name.s + 1, "all-id", 6))) {
 +                      p->type = P_CALL_ID;
 +                      h->event_dialog.call_id = p;
 +              }
 +              break;
 +
 +      case 'f':
 +      case 'F':
 +              if ((p->name.len == 8) &&
 +                  (!strncasecmp(p->name.s + 1, "rom-tag", 7))) {
 +                      p->type = P_FROM_TAG;
 +                      h->event_dialog.from_tag = p;
 +              }
 +              break;
 +
 +      case 't':
 +      case 'T':
 +              if ((p->name.len == 6) &&
 +                  (!strncasecmp(p->name.s + 1, "o-tag", 5))) {
 +                      p->type = P_TO_TAG;
 +                      h->event_dialog.to_tag = p;
 +              }
 +              break;
 +
 +      case 'i':
 +      case 'I':
 +              if ((p->name.len == 27) &&
 +                  (!strncasecmp(p->name.s + 1, "nclude-session-description", 26))) {
 +                      p->type = P_ISD;
 +                      h->event_dialog.include_session_description = p;
 +              }
 +              break;
 +
 +      case 's':
 +      case 'S':
 +              if ((p->name.len == 3) &&
 +                  (!strncasecmp(p->name.s + 1, "la", 2))) {
 +                      p->type = P_SLA;
 +                      h->event_dialog.sla = p;
 +              }
 +              break;
 +      }
 +}
 +
 +
  /*
   * Try to find out parameter name, recognized parameters
-  * are q, expires and method
+  * are q, expires and methods
   */
  static inline void parse_contact_class(param_hooks_t* _h, param_t* _p)
  {
                
        case 'm':
        case 'M':
-               if ((_p->name.len == 6) &&
-                   (!strncasecmp(_p->name.s + 1, "ethod", 5))) {
-                       _p->type = P_METHOD;
-                       _h->contact.method = _p;
+               if ((_p->name.len == 7) &&
+                   (!strncasecmp(_p->name.s + 1, "ethods", 6))) {
+                       _p->type = P_METHODS;
+                       _h->contact.methods = _p;
                }
                break;
                
@@@ -388,7 -328,6 +388,7 @@@ static inline void parse_param_name(str
        switch(_c) {
        case CLASS_CONTACT: parse_contact_class(_h, _p); break;
        case CLASS_URI:     parse_uri_class(_h, _p);     break;
 +      case CLASS_EVENT_DIALOG: parse_event_dialog_class(_h, _p); break;
        default: break;
        }
  }
@@@ -595,7 -534,7 +595,7 @@@ static inline void print_param(FILE* _o
        case P_OTHER:     type = "P_OTHER";     break;
        case P_Q:         type = "P_Q";         break;
        case P_EXPIRES:   type = "P_EXPIRES";   break;
-       case P_METHOD:    type = "P_METHOD";    break;
+       case P_METHODS:   type = "P_METHODS";   break;
        case P_TRANSPORT: type = "P_TRANSPORT"; break;
        case P_LR:        type = "P_LR";        break;
        case P_R2:        type = "P_R2";        break;
        case P_DSTPORT:   type = "P_DSTPORT";   break;
        case P_INSTANCE:  type = "P_INSTANCE";  break;
        case P_FTAG:      type = "P_FTAG";      break;
 +      case P_CALL_ID:   type = "P_CALL_ID";   break;
 +      case P_FROM_TAG:  type = "P_FROM_TAG";  break;
 +      case P_TO_TAG:    type = "P_TO_TAG";    break;
 +      case P_ISD:       type = "P_ISD";       break;
 +      case P_SLA:       type = "P_SLA";       break;
        default:          type = "UNKNOWN";     break;
        }
        
diff --combined parser/parse_param.h
@@@ -46,7 -46,7 +46,7 @@@ typedef enum ptype 
        P_OTHER = 0, /* Unknown parameter */
        P_Q,         /* Contact: q parameter */
        P_EXPIRES,   /* Contact: expires parameter */
-       P_METHOD,    /* Contact: method parameter */
+       P_METHODS,   /* Contact: methods parameter */
        P_RECEIVED,  /* Contact: received parameter */
        P_TRANSPORT, /* URI: transport parameter */
        P_LR,        /* URI: lr parameter */
        P_DSTIP,     /* URI: dstip parameter */
        P_DSTPORT,   /* URi: dstport parameter */
        P_INSTANCE,  /* Contact: sip.instance parameter */
 -      P_FTAG       /* URI: ftag parameter */
 +      P_FTAG,      /* URI: ftag parameter */
 +      P_CALL_ID,   /* Dialog event package: call-id */
 +      P_FROM_TAG,  /* Dialog event package: from-tag */
 +      P_TO_TAG,    /* Dialog event package: to-tag */
 +      P_ISD,       /* Dialog event package: include-session-description */
 +      P_SLA        /* Dialog event package: sla */
  } ptype_t;
  
  
   * Class of parameters
   */
  typedef enum pclass {
 -      CLASS_ANY = 0,  /* Any parameters, well-known hooks will be not used */
 -      CLASS_CONTACT,  /* Contact parameters */
 -      CLASS_URI       /* URI parameters */
 +      CLASS_ANY = 0,      /* Any parameters, well-known hooks will be not used */
 +      CLASS_CONTACT,      /* Contact parameters */
 +      CLASS_URI,          /* URI parameters */
 +      CLASS_EVENT_DIALOG  /* Event dialog parameters */
  } pclass_t;
  
  
@@@ -94,7 -88,7 +94,7 @@@ typedef struct param 
  struct contact_hooks {
        struct param* expires;  /* expires parameter */
        struct param* q;        /* q parameter */
-       struct param* method;   /* method parameter */
+       struct param* methods;  /* methods parameter */
        struct param* received; /* received parameter */
        struct param* instance; /* sip.instance parameter */
  };
@@@ -115,21 -109,12 +115,21 @@@ struct uri_hooks 
  };
  
  
 +struct event_dialog_hooks {
 +      struct param* call_id;
 +      struct param* from_tag;
 +      struct param* to_tag;
 +      struct param* include_session_description;
 +      struct param* sla;
 +};
 +
  /*
   * Union of hooks structures for all classes
   */
  typedef union param_hooks {
        struct contact_hooks contact; /* Contact hooks */
        struct uri_hooks uri;         /* URI hooks */
 +      struct event_dialog_hooks event_dialog;
  } param_hooks_t;
  
  /**
diff --combined receive.c
+++ b/receive.c
@@@ -127,7 -127,7 +127,7 @@@ int receive_msg(char* buf, unsigned in
  
        if (msg->first_line.type==SIP_REQUEST){
                if (!IS_SIP(msg)){
-                       if (nonsip_msg_run_hooks(msg)!=NONSIP_MSG_ACCEPT);
+                       if (nonsip_msg_run_hooks(msg)!=NONSIP_MSG_ACCEPT)
                                goto end; /* drop the message */
                }
                /* sanity checks */
                if (exec_pre_req_cb(msg)==0 )
                        goto end; /* drop the request */
  
 +              set_route_type(REQUEST_ROUTE);
                /* exec the routing script */
                init_run_actions_ctx(&ra_ctx);
                if (run_actions(&ra_ctx, main_rt.rlist[DEFAULT_RT], msg)<0){
                */
                if (exec_pre_rpl_cb(msg)==0 )
                        goto end; /* drop the request */
 +
                /* exec the onreply routing script */
                if (onreply_rt.rlist[DEFAULT_RT]){
 +                      set_route_type(ONREPLY_ROUTE);
                        init_run_actions_ctx(&ra_ctx);
                        ret=run_actions(&ra_ctx, onreply_rt.rlist[DEFAULT_RT], msg);
                        if (ret<0){
diff --combined route_struct.h
@@@ -66,14 -66,12 +66,15 @@@ enum { EQUAL_OP=10, MATCH_OP, GT_OP, LT
  enum { METHOD_O=1, URI_O, FROM_URI_O, TO_URI_O, SRCIP_O, SRCPORT_O,
           DSTIP_O, DSTPORT_O, PROTO_O, AF_O, MSGLEN_O, DEFAULT_O, ACTION_O,
           NUMBER_O, AVP_O, SNDIP_O, SNDPORT_O, TOIP_O, TOPORT_O, SNDPROTO_O,
 -         SNDAF_O, RETCODE_O, SELECT_O};
 +         SNDAF_O, RETCODE_O, SELECT_O, PVAR_O, RVEXP_O};
  
  enum { FORWARD_T=1, SEND_T, DROP_T, LOG_T, ERROR_T, ROUTE_T, EXEC_T,
                SET_HOST_T, SET_HOSTPORT_T, SET_USER_T, SET_USERPASS_T,
 -              SET_PORT_T, SET_URI_T, SET_HOSTPORTTRANS_T, SET_USERPHONE_T,
 -              IF_T, MODULE_T,
 +              SET_PORT_T, SET_URI_T, SET_HOSTPORTTRANS_T, SET_HOSTALL_T,
++              SET_USERPHONE_T,
 +              IF_T, SWITCH_T /* only until fixup*/,
 +              BLOCK_T, EVAL_T, SWITCH_JT_T, SWITCH_COND_T, MATCH_COND_T, WHILE_T,
 +              MODULE_T, MODULE3_T, MODULE4_T, MODULE5_T, MODULE6_T, MODULEX_T,
                SETFLAG_T, RESETFLAG_T, ISFLAGSET_T ,
                AVPFLAG_OPER_T,
                LEN_GT_T, PREFIX_T, STRIP_T,STRIP_TAIL_T,
  };
  enum { NOSUBTYPE=0, STRING_ST, NET_ST, NUMBER_ST, IP_ST, RE_ST, PROXY_ST,
                EXPR_ST, ACTIONS_ST, MODEXP_ST, MODFIXUP_ST, URIHOST_ST, URIPORT_ST,
 -              MYSELF_ST, STR_ST, SOCKID_ST, SOCKETINFO_ST, ACTION_ST, AVP_ST, SELECT_ST,
 -              RETCODE_ST};
 +              MYSELF_ST, STR_ST, SOCKID_ST, SOCKETINFO_ST, ACTION_ST, AVP_ST,
 +              SELECT_ST, PVAR_ST,
 +              LVAL_ST,  RVE_ST,
 +              RETCODE_ST, CASE_ST,
 +              BLOCK_ST, JUMPTABLE_ST, CONDTABLE_ST, MATCH_CONDTABLE_ST
 +};
  
  /* run flags */
  #define EXIT_R_F   1
  #define RETURN_R_F 2
 +#define BREAK_R_F  4
 +
 +
 +struct cfg_pos{
 +      int s_line;
 +      int e_line;
 +      unsigned short s_col;
 +      unsigned short e_col;
 +};
 +
  
  /* Expression operand */
  union exp_op {
@@@ -151,17 -135,13 +152,17 @@@ typedef struct 
        } u;
  } action_u_t;
  
 -#define MAX_ACTIONS 4
 +/* maximum internal array/params
 + * for module function calls val[0] and val[1] store a pointer to the
 + * function and the number of params, the rest are the function params 
 + */
 +#define MAX_ACTIONS (2+6)
  
  struct action{
        int type;  /* forward, drop, log, send ...*/
        int count;
 -      action_u_t val[MAX_ACTIONS];
        struct action* next;
 +      action_u_t val[MAX_ACTIONS];
  };
  
  struct expr* mk_exp(int op, struct expr* left, struct expr* right);
@@@ -174,8 -154,5 +175,8 @@@ void print_action(struct action* a)
  void print_actions(struct action* a);
  void print_expr(struct expr* exp);
  
 +/** joins to cfg file positions into a new one. */
 +void cfg_pos_join(struct cfg_pos* res,
 +                                                      struct cfg_pos* pos1, struct cfg_pos* pos2);
  #endif
  
diff --combined select_core.c
@@@ -236,8 -236,8 +236,8 @@@ int select_contact_params_spec(str* res
                TEST_RET_res_body(c->q);
        case SEL_PARAM_EXPIRES:
                TEST_RET_res_body(c->expires);
-       case SEL_PARAM_METHOD:
-               TEST_RET_res_body(c->method);
+       case SEL_PARAM_METHODS:
+               TEST_RET_res_body(c->methods);
        case SEL_PARAM_RECEIVED:
                TEST_RET_res_body(c->received);
        case SEL_PARAM_INSTANCE:
@@@ -874,7 -874,7 +874,7 @@@ int select_event(str* res, select_t* s
                return -1;
        }
  
 -      *res = ((event_t*)msg->event->parsed)->text;
 +      *res = ((event_t*)msg->event->parsed)->name;
        return 0;
  }