Merge commit 'origin/ser_core_cvs'
[sip-router] / action.c
index b35a543..82812d7 100644 (file)
--- a/action.c
+++ b/action.c
@@ -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)
  */
 
@@ -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"
@@ -75,6 +78,7 @@
 #ifdef USE_SCTP
 #include "sctp_server.h"
 #endif
+#include "switch.h"
 
 #include <sys/types.h>
 #include <sys/socket.h>
@@ -102,14 +106,22 @@ int do_action(struct run_act_ctx* h, struct action* a, struct sip_msg* msg)
        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
@@ -304,7 +316,8 @@ int do_action(struct run_act_ctx* h, struct action* a, struct sip_msg* msg)
                                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;
 
@@ -448,7 +461,7 @@ int do_action(struct run_act_ctx* h, struct action* a, struct sip_msg* msg)
                        /*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){
@@ -477,6 +490,7 @@ int do_action(struct run_act_ctx* h, struct action* a, struct sip_msg* msg)
                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:
@@ -688,6 +702,7 @@ int do_action(struct run_act_ctx* h, struct action* a, struct sip_msg* msg)
                                /* host */
                                if ((a->type==SET_HOST_T)
                                                || (a->type==SET_HOSTPORT_T)
+                                               || (a->type==SET_HOSTALL_T)
                                                || (a->type==SET_HOSTPORTTRANS_T)
                                ) {
                                        tmp=a->val[0].u.string;
@@ -710,6 +725,8 @@ int do_action(struct run_act_ctx* h, struct action* a, struct sip_msg* msg)
                                        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))
@@ -777,6 +794,7 @@ int do_action(struct run_act_ctx* h, struct action* a, struct sip_msg* msg)
                                        *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);
@@ -804,7 +822,8 @@ int do_action(struct run_act_ctx* h, struct action* a, struct sip_msg* msg)
                                                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){
@@ -818,17 +837,274 @@ int do_action(struct run_act_ctx* h, struct action* a, struct sip_msg* msg)
                                }
                        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 */
@@ -897,125 +1173,20 @@ int do_action(struct run_act_ctx* h, struct action* a, struct sip_msg* msg)
 
                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:
@@ -1065,7 +1236,9 @@ int run_actions(struct run_act_ctx* h, struct action* a, struct sip_msg* msg)
 
        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;
@@ -1082,9 +1255,11 @@ end:
        /* 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;