New script element, Jan's select function framework (@xxx), extended for
authorMichal Matyska <michal.matyska@iptel.org>
Tue, 20 Dec 2005 00:49:32 +0000 (00:49 +0000)
committerMichal Matyska <michal.matyska@iptel.org>
Tue, 20 Dec 2005 00:49:32 +0000 (00:49 +0000)
modular use. In the script it can be used as value assigned to AVP and
in the string comparision, RE matching, like:
$my_avp=@my.function[1];
if (@via.protocol=="UDP")...

The name to called function conversion is implemented as tree in a table,
modules can register their own tables in module initialization function.

Description of the construction of the table expected to follow soon in the
documentation files...

action.c
cfg.lex
cfg.y
route.c
route_struct.c
route_struct.h
select.c [new file with mode: 0644]
select.h [new file with mode: 0644]
select_core.c [new file with mode: 0644]
select_core.h [new file with mode: 0644]

index d81cf3e..40862bf 100644 (file)
--- a/action.c
+++ b/action.c
@@ -770,6 +770,20 @@ int do_action(struct action* a, struct sip_msg* msg)
                                }
                                ret = 1;
                                break;
+                       } else if (a->p2_type == SELECT_ST) {
+                               int r;
+                               r = run_select(&value.s, a->p2.select, msg);
+                               if (r < 0) {
+                                       ret=E_UNSPEC;
+                                       break;
+                               } else if (r > 0) {
+                                       value.s.s = "";
+                                       value.s.len = 0;
+                               }
+
+                               flags = a->p1.attr->type | AVP_VAL_STR;
+                               name = a->p1.attr->name;
+                               ret = 1;
                        } else {
                                LOG(L_CRIT, "BUG: do_action: Bad right side of avp assignment\n");
                                ret=E_BUG;
diff --git a/cfg.lex b/cfg.lex
index 7fb3599..ab54c59 100644 (file)
--- a/cfg.lex
+++ b/cfg.lex
@@ -70,6 +70,7 @@
        #include <stdlib.h>
        #include "ip_addr.h"
        #include "usr_avp.h"
+       #include "select.h"
        #include "cfg.tab.h"
 
        /* states */
@@ -78,6 +79,7 @@
        #define COMMENT_LN_S            2
        #define STRING_S                3
        #define ATTR_S                  4
+        #define SELECT_S                5
 
        #define STR_BUF_ALLOC_UNIT      128
        struct str_buf{
 %}
 
 /* start conditions */
-%x STRING1 STRING2 COMMENT COMMENT_LN ATTR
+%x STRING1 STRING2 COMMENT COMMENT_LN ATTR SELECT
 
 /* action keywords */
 FORWARD        forward
@@ -194,7 +196,8 @@ PLUS        "+"
 MINUS  "-"
 
 /* Attribute specification */
-ATTR_MARK   "$"|"%"|"@"
+ATTR_MARK   "$"|"%"
+SELECT_MARK  "@"
 ATTR_FROM   "from"|"f"
 ATTR_TO     "to"|"t"
 ATTR_USER   "user"|"u"
@@ -501,6 +504,22 @@ EAT_ABLE   [\ \t\b\r]
 <INITIAL>{PLUS}                { count(); return PLUS; }
 <INITIAL>{MINUS}       { count(); return MINUS; }
 
+<INITIAL>{SELECT_MARK}  { count(); state = SELECT_S; BEGIN(SELECT); return SELECT_MARK; }
+<SELECT>{ID}           { count(); addstr(&s_buf, yytext, yyleng); 
+                          yylval.strval=s_buf.s;
+                          memset(&s_buf, 0, sizeof(s_buf));
+                          return ID; 
+                        }
+<SELECT>{DOT}           { count(); return DOT; }
+<SELECT>{LBRACK}        { count(); return LBRACK; }
+<SELECT>{RBRACK}        { count(); return RBRACK; }
+<SELECT>{DECNUMBER}    { count(); yylval.intval=atoi(yytext);return NUMBER; }
+<SELECT>{HEXNUMBER}    { count(); yylval.intval=(int)strtol(yytext, 0, 16); return NUMBER; }
+<SELECT>{OCTNUMBER}    { count(); yylval.intval=(int)strtol(yytext, 0, 8); return NUMBER; }
+<SELECT>{BINNUMBER}     { count(); yylval.intval=(int)strtol(yytext, 0, 2); return NUMBER; }
+<SELECT>.               { unput(yytext[0]); state = INITIAL_S; BEGIN(INITIAL); } /* Rescan the token in INITIAL state */
+
+
 <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; }
diff --git a/cfg.y b/cfg.y
index f4f6ab9..1d166c9 100644 (file)
--- a/cfg.y
+++ b/cfg.y
@@ -91,6 +91,7 @@
 #include "name_alias.h"
 #include "ut.h"
 #include "dset.h"
+#include "select.h"
 
 #include "config.h"
 #ifdef USE_TLS
@@ -121,6 +122,9 @@ static str* str_tmp;
 static str s_tmp;
 static struct ip_addr* ip_tmp;
 static struct avp_spec* s_attr;
+static select_t sel;
+static select_t* sel_ptr;
+
 
 static void warn(char* s);
 static struct socket_id* mk_listen_id(char*, int, int);
@@ -137,6 +141,7 @@ static struct socket_id* mk_listen_id(char*, int, int);
        struct ip_addr* ipaddr;
        struct socket_id* sockid;
        struct avp_spec* attr;
+       select_t* select;
 }
 
 /* terminals */
@@ -279,6 +284,7 @@ static struct socket_id* mk_listen_id(char*, int, int);
 %token TOS
 
 %token ATTR_MARK
+%token SELECT_MARK
 %token ATTR_FROM
 %token ATTR_TO
 %token ATTR_USER
@@ -340,6 +346,7 @@ static struct socket_id* mk_listen_id(char*, int, int);
 %type <attr> attr_id
 %type <intval> class_id
 %type <intval> assign_op
+%type <select> select_id
 /*%type <route_el> rules;
   %type <route_el> rule;
 */
@@ -979,6 +986,7 @@ uri_type:   URI                     {$$=URI_O;}
 
 exp_elem:      METHOD strop STRING     {$$= mk_elem($2, METHOD_O, 0, STRING_ST, $3);}
                 | METHOD strop attr_id  {$$ = 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 error { $$=0; yyerror("string expected"); }
                | METHOD error  { $$=0; yyerror("invalid operator,"
@@ -987,6 +995,7 @@ exp_elem:   METHOD strop STRING     {$$= mk_elem($2, METHOD_O, 0, STRING_ST, $3);}
                | 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 {$$ = 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,"
@@ -1193,9 +1202,15 @@ exp_elem:        METHOD strop STRING     {$$= mk_elem($2, METHOD_O, 0, STRING_ST, $3);}
 
                | attr_id               {$$=mk_elem( NO_OP, AVP_O, (void*)$1, 0, 0); }
                | attr_id strop STRING  {$$=mk_elem( $2, AVP_O, (void*)$1, STRING_ST, $3); }
+               | attr_id strop select_id {$$=mk_elem( $2, AVP_O, (void*)$1, SELECT_ST, $3); }
                | attr_id intop NUMBER  {$$=mk_elem( $2, AVP_O, (void*)$1, NUMBER_ST, (void*)$3); }
                | attr_id binop NUMBER  {$$=mk_elem( $2, AVP_O, (void*)$1, NUMBER_ST, (void*)$3); }
-               | attr_id strop attr_id {$$=mk_elem( $2, AVP_O, (void*)$1, AVP_ST, (void*)$3); }
+                | attr_id strop attr_id {$$=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   { $$=mk_elem( $2, SELECT_O, $1, AVP_ST, (void*)$3); }
+               | select_id strop select_id { $$=mk_elem( $2, SELECT_O, $1, SELECT_ST, $3); }
 ;
 
 
@@ -1310,39 +1325,77 @@ class_id : LBRACK ATTR_USER RBRACK { $$ = AVP_CLASS_USER; }
          | LBRACK ATTR_GLOBAL RBRACK { $$ = AVP_CLASS_GLOBAL; }
 ;
 
+select_param : ID { 
+                   if (sel.n >= MAX_SELECT_PARAMS-1) {
+                           yyerror("Select identifier too long\n");
+                   }
+                   sel.params[sel.n].type = PARAM_STR; 
+                   sel.params[sel.n].v.s.s = $1;
+                   sel.params[sel.n].v.s.len = strlen($1);
+                   sel.n++;
+                  }
+             | ID LBRACK NUMBER RBRACK {
+                    if (sel.n >= MAX_SELECT_PARAMS-2) {
+                           yyerror("Select identifier too long\n");
+                    }
+                    sel.params[sel.n].type = PARAM_STR;
+                    sel.params[sel.n].v.s.s = $1;
+                    sel.params[sel.n].v.s.len = strlen($1);
+                    sel.n++;
+                    sel.params[sel.n].type = PARAM_INT;
+                    sel.params[sel.n].v.i = $3;
+                    sel.n++;
+            }
+;
+
+select_params : select_params DOT select_param
+              | select_param
+;
+
+select_id : SELECT_MARK { sel.n = 0; sel.f = 0; } select_params {
+//     if (resolve_select(&sel) < 0) yyerror("Unable to resolve select");
+       sel_ptr = (select_t*)pkg_malloc(sizeof(select_t));
+       if (!sel_ptr) {
+               yyerror("No memory left to allocate select structure\n");
+       }
+       memcpy(sel_ptr, &sel, sizeof(select_t));
+       $$ = sel_ptr;
+}
+;
+
 attr_id : ATTR_MARK ID { s_attr = (struct avp_spec*)pkg_malloc(sizeof(struct avp_spec));
-                         if (!s_attr) { LOG(L_CRIT, "No memory left"); return 0;}
+                         if (!s_attr) { yyerror("No memory left"); }
                          s_attr->type = AVP_NAME_STR;                   
                          s_attr->name.s.s = $2; s_attr->name.s.len = strlen($2); 
                          $$ = s_attr; 
                        }
         | ATTR_MARK class_id DOT ID { s_attr = (struct avp_spec*)pkg_malloc(sizeof(struct avp_spec));
-                                      if (!s_attr) { LOG(L_CRIT, "No memory left"); return 0;}
+                                      if (!s_attr) { yyerror("No memory left"); }
                                       s_attr->type = AVP_NAME_STR | $2;
                                       s_attr->name.s.s = $4; s_attr->name.s.len = strlen($4); 
                                       $$ = s_attr; 
                                     }
         | ATTR_MARK ATTR_FROM DOT ID { s_attr = (struct avp_spec*)pkg_malloc(sizeof(struct avp_spec));
-                                       if (!s_attr) { LOG(L_CRIT, "No memory left"); return 0;}
+                                       if (!s_attr) { yyerror("No memory left"); }
                                        s_attr->type = AVP_NAME_STR | AVP_TRACK_FROM;
                                        s_attr->name.s.s = $4; s_attr->name.s.len = strlen($4);
                                        $$ = s_attr;
                                      }
         | ATTR_MARK ATTR_TO DOT ID { s_attr = (struct avp_spec*)pkg_malloc(sizeof(struct avp_spec));
-                                     if (!s_attr) { LOG(L_CRIT, "No memory left"); return 0;}
+                                     if (!s_attr) { yyerror("No memory left"); }
                                      s_attr->type = AVP_NAME_STR | AVP_TRACK_TO; 
                                      s_attr->name.s.s = $4; s_attr->name.s.len = strlen($4); 
                                      $$ = s_attr;
                                    }
         | ATTR_MARK ATTR_FROM class_id DOT ID { s_attr = (struct avp_spec*)pkg_malloc(sizeof(struct avp_spec));
-                                               if (!s_attr) { LOG(L_CRIT, "No memory left"); return 0;}
+                                               if (!s_attr) { yyerror("No memory left"); }
                                                s_attr->type = AVP_NAME_STR | AVP_TRACK_FROM | $3; 
                                                s_attr->name.s.s = $5; 
                                                s_attr->name.s.len = strlen($5);
                                                $$ = s_attr;
                                               }
         | ATTR_MARK ATTR_TO class_id DOT ID { s_attr = (struct avp_spec*)pkg_malloc(sizeof(struct avp_spec));
-                                              if (!s_attr) { LOG(L_CRIT, "No memory left"); return 0;}
+                                              if (!s_attr) { yyerror("No memory left"); }
                                               s_attr->type = AVP_NAME_STR | AVP_TRACK_TO | $3;
                                               s_attr->name.s.s = $5; s_attr->name.s.len = strlen($5);
                                              $$ = s_attr;
@@ -1357,6 +1410,7 @@ assign_action:   attr_id assign_op STRING  { $$=mk_action($2, AVP_ST, STRING_ST,
                | attr_id assign_op NUMBER  { $$=mk_action($2, AVP_ST, NUMBER_ST, $1, (void*)$3); }
                | attr_id assign_op fcmd    { $$=mk_action($2, AVP_ST, ACTION_ST, $1, $3); }
                | attr_id assign_op attr_id { $$=mk_action($2, AVP_ST, AVP_ST, $1, $3); }
+               | attr_id assign_op select_id { $$=mk_action($2, AVP_ST, SELECT_ST, (void*)$1, (void*)$3); }
                | attr_id assign_op LPAREN exp RPAREN { $$ = mk_action($2, AVP_ST, EXPR_ST, $1, $4); }
 ;
 
diff --git a/route.c b/route.c
index 2cc2e98..b8bcef3 100644 (file)
--- a/route.c
+++ b/route.c
@@ -67,9 +67,9 @@
 #include "parser/parse_from.h"
 #include "parser/parse_to.h"
 #include "mem/mem.h"
+#include "select.h"
 #include "onsend.h"
 
-
 /* main routing script table  */
 struct action* rlist[RT_NO];
 /* reply routing table */
@@ -132,7 +132,7 @@ static int fix_expr(struct expr* exp)
                                        pkg_free(exp->r.param);
                                        exp->r.re=re;
                                        exp->r_type=RE_ST;
-                               }else if (exp->r_type!=RE_ST && exp->r_type != AVP_ST){
+                               }else if (exp->r_type!=RE_ST && exp->r_type != AVP_ST && exp->r_type != SELECT_ST){
                                        LOG(L_CRIT, "BUG: fix_expr : invalid type for match\n");
                                        return E_BUG;
                                }
@@ -159,6 +159,20 @@ static int fix_expr(struct expr* exp)
                                exp->r.str.s = exp->r.string;
                                exp->r.str.len = len;
                        }
+                       if (exp->l_type==SELECT_O) {
+                               if ((ret=resolve_select(exp->l.select)) < 0) {
+                                       BUG("Unable to resolve select\n");
+                                       print_select(exp->l.select);
+                                       return ret;
+                               }
+                       }
+                       if ((exp->r_type==SELECT_O)||(exp->r_type==SELECT_ST)) {
+                               if ((ret=resolve_select(exp->r.select)) < 0) {
+                                       BUG("Unable to resolve select\n");
+                                       print_select(exp->l.select);
+                                       return ret;
+                               }
+                       }
                        ret=0;
        }
        return ret;
@@ -280,6 +294,12 @@ static int fix_actions(struct action* a)
                                        len = strlen(t->p2.data);
                                        t->p2.str.s = t->p2.data;
                                        t->p2.str.len = len;
+                               } else if (t->p2_type == SELECT_ST) {
+                                       if ((ret=resolve_select(t->p2.select)) < 0) {
+                                               BUG("Unable to resolve select\n");
+                                               print_select(t->p2.select);
+                                               return ret;
+                                       }
                                }
                                break;
 
@@ -372,10 +392,11 @@ inline static int comp_num(int op, long left, int rtype, union exp_op* r)
 /*
  * Compare given string "left" with right side of expression
  */
-inline static int comp_str(int op, str* left, int rtype, union exp_op* r)
+inline static int comp_str(int op, str* left, int rtype, union exp_op* r, struct sip_msg* msg)
 {
        str* right;
        int_str val;
+       str v;
        avp_t* avp;
        int ret;
        char backup;
@@ -387,6 +408,11 @@ inline static int comp_str(int op, str* left, int rtype, union exp_op* r)
                avp = search_first_avp(r->attr->type, r->attr->name, &val, 0);
                if (avp && (avp->flags & AVP_VAL_STR)) right = &val.s;
                else return 0;
+       } else if (rtype == SELECT_ST) {
+               ret = run_select(&v, r->select, msg);
+               if (ret > 0) return 0;       /* Not found */
+               else if (ret < 0) goto error; /* Error */
+               right = &v;
        } else if ((op == MATCH_OP && rtype == RE_ST)) {
        } else if (op != MATCH_OP && rtype == STRING_ST) {
                right = &r->str;
@@ -424,7 +450,7 @@ inline static int comp_str(int op, str* left, int rtype, union exp_op* r)
                              */
                        backup=left->s[left->len];
                        left->s[left->len]='\0';
-                       if (rtype == AVP_ST) {
+                       if (rtype == AVP_ST || rtype == SELECT_ST) {
                                     /* For AVPs we need to compile the RE on the fly */
                                re=(regex_t*)pkg_malloc(sizeof(regex_t));
                                if (re==0){
@@ -499,7 +525,7 @@ error:
 }
 
 
-inline static int comp_avp(int op, avp_spec_t* spec, int rtype, union exp_op* r)
+inline static int comp_avp(int op, avp_spec_t* spec, int rtype, union exp_op* r, struct sip_msg* msg)
 {
        avp_t* avp;
        int_str val;
@@ -526,14 +552,33 @@ inline static int comp_avp(int op, avp_spec_t* spec, int rtype, union exp_op* r)
        }
 
        if (avp->flags & AVP_VAL_STR) {
-               return comp_str(op, &val.s, rtype, r);
+               return comp_str(op, &val.s, rtype, r, msg);
        } else {
                return comp_num(op, val.n, rtype, r);
        }
 }
 
+/*
+ * Left side of expression was select
+ */
+inline static int comp_select(int op, select_t* sel, int rtype, union exp_op* r, struct sip_msg* msg)
+{
+       int ret;
+       str val;
 
+       ret = run_select(&val, sel, msg);
+       if (ret < 0) return -1;
+       if (ret > 0) return 0;
 
+       switch(op) {
+       case NO_OP: return 1;
+       case BINOR_OP:
+       case BINAND_OP:  
+               ERR("Binary operators cannot be used with string selects\n");
+               return -1;
+       }
+       return comp_str(op, &val, rtype, r, msg);
+}
 
 /* check_self wrapper -- it checks also for the op */
 inline static int check_self_op(int op, str* s, unsigned short p)
@@ -657,7 +702,7 @@ static int eval_elem(struct expr* e, struct sip_msg* msg)
        switch(e->l_type){
        case METHOD_O:
                ret=comp_str(e->op, &msg->first_line.u.request.method, 
-                            e->r_type, &e->r);
+                            e->r_type, &e->r, msg);
                break;
        case URI_O:
                if(msg->new_uri.s) {
@@ -668,7 +713,7 @@ static int eval_elem(struct expr* e, struct sip_msg* msg)
                                                       msg->parsed_uri.port_no:SIP_PORT);
                        }else{
                                ret=comp_str(e->op, &msg->new_uri, 
-                                            e->r_type, &e->r);
+                                            e->r_type, &e->r, msg);
                        }
                }else{
                        if (e->r_type==MYSELF_ST){
@@ -678,7 +723,7 @@ static int eval_elem(struct expr* e, struct sip_msg* msg)
                                                       msg->parsed_uri.port_no:SIP_PORT);
                        }else{
                                ret=comp_str(e->op, &msg->first_line.u.request.uri,
-                                            e->r_type, &e->r);
+                                            e->r_type, &e->r, msg);
                        }
                }
                break;
@@ -699,7 +744,7 @@ static int eval_elem(struct expr* e, struct sip_msg* msg)
                                          uri.port_no?uri.port_no:SIP_PORT);
                }else{
                        ret=comp_str(e->op, &get_from(msg)->uri,
-                                    e->r_type, &e->r);
+                                    e->r_type, &e->r, msg);
                }
                break;
 
@@ -721,7 +766,7 @@ static int eval_elem(struct expr* e, struct sip_msg* msg)
                                          uri.port_no?uri.port_no:SIP_PORT);
                }else{
                        ret=comp_str(e->op, &get_to(msg)->uri,
-                                    e->r_type, &e->r);
+                                    e->r_type, &e->r, msg);
                }
                break;
                
@@ -837,9 +882,13 @@ static int eval_elem(struct expr* e, struct sip_msg* msg)
                break;
 
        case AVP_O:
-               ret = comp_avp(e->op, e->l.attr, e->r_type, &e->r);
+               ret = comp_avp(e->op, e->l.attr, e->r_type, &e->r, msg);
                break;
                
+       case SELECT_O:
+               ret = comp_select(e->op, e->l.select, e->r_type, &e->r, msg);
+               break;
+
        default:
                LOG(L_CRIT, "BUG: eval_elem: invalid operand %d\n",
                    e->l_type);
index a68e7f7..cf1560a 100644 (file)
@@ -174,6 +174,9 @@ void print_expr(struct expr* exp)
                        case AVP_ST:
                                DBG("attr");
                                break;
+                       case SELECT_ST:
+                               DBG("select");
+                               break;
                        
                        default:
                                DBG("UNKNOWN");
@@ -228,9 +231,11 @@ void print_expr(struct expr* exp)
                                        DBG("_myself_");
                                        break;
                        case AVP_ST:
-                               DBG("attr");
-                               break;
-                       
+                                       DBG("attr");
+                                       break;
+                       case SELECT_ST:
+                                       DBG("select");
+                                       break;
                        default:
                                        DBG("type<%d>", exp->r_type);
                }
@@ -407,6 +412,9 @@ void print_action(struct action* t)
        case AVP_ST:
                DBG("avp(%u,%.*s)", t->p1.attr->type, t->p1.attr->name.s.len, ZSW(t->p1.attr->name.s.s));
                break;
+       case SELECT_ST:
+               DBG("select");
+               break;
        default:
                DBG("type<%d>", t->p1_type);
        }
@@ -438,6 +446,9 @@ void print_action(struct action* t)
        case AVP_ST:
                DBG(", avp(%u,%.*s)", t->p2.attr->type, t->p2.attr->name.s.len, ZSW(t->p2.attr->name.s.s));
                break;
+       case SELECT_ST:
+               DBG("select");
+               break;
        default:
                DBG(", type<%d>", t->p2_type);
        }
index 5f9048d..1cd2f86 100644 (file)
@@ -41,6 +41,7 @@
 #define route_struct_h
 
 #include <regex.h>
+#include "select.h"
 #include "usr_avp.h"
 
 #define EXPR_DROP -127  /* used only by the expression and if evaluator */
@@ -62,7 +63,7 @@ enum { EQUAL_OP=10, MATCH_OP, GT_OP, LT_OP, GTE_OP, LTE_OP, DIFF_OP, NO_OP };
 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};
+          SNDAF_O, RETCODE_O, SELECT_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, 
@@ -88,14 +89,13 @@ enum { FORWARD_T=1, SEND_T, DROP_T, LOG_T, ERROR_T, ROUTE_T, EXEC_T,
 };
 enum { NOSUBTYPE=0, STRING_ST, NET_ST, NUMBER_ST, IP_ST, RE_ST, PROXY_ST,
                EXPR_ST, ACTIONS_ST, CMDF_ST, MODFIXUP_ST, URIHOST_ST, URIPORT_ST,
-               MYSELF_ST, STR_ST, SOCKID_ST, SOCKETINFO_ST, ACTION_ST, AVP_ST,
+               MYSELF_ST, STR_ST, SOCKID_ST, SOCKETINFO_ST, ACTION_ST, AVP_ST, SELECT_ST,
                RETCODE_ST};
 
 /* run flags */
 #define EXIT_R_F   1
 #define RETURN_R_F 2
 
-
 /* Expression operand */
 union exp_op {
        struct expr* expr;
@@ -104,6 +104,7 @@ union exp_op {
        void* param;
        int intval;
        avp_spec_t* attr;
+       select_t* select;
        regex_t* re;
        struct net* net;
 };
@@ -122,6 +123,7 @@ typedef union {
        str str;
        void* data;
        avp_spec_t* attr;
+       select_t* select;
 } action_u_t;
 
 struct action{
diff --git a/select.c b/select.c
new file mode 100644 (file)
index 0000000..a74bfd0
--- /dev/null
+++ b/select.c
@@ -0,0 +1,129 @@
+#include "select.h"
+#include "dprint.h"
+#include "select_core.h"
+#include "mem/mem.h"
+
+/*
+ * The main parser table list placeholder
+ * at startup use core table, modules can
+ * add their own via register_select_table call
+ */
+static select_table_t *select_list = &select_core_table;
+
+int resolve_select(select_t* s)
+{
+       select_f f, pf;
+       int param_idx = 0;
+       int table_idx = 0;
+       select_table_t* t = NULL;;
+       int accept = 0;
+       
+       f = pf = NULL;
+       while (param_idx<s->n) {
+               accept = 0;
+               for (t=select_list; t; t=t->next) {
+                       table_idx = 0;  
+                       if (!t->table) continue;
+                       while (t->table[table_idx].curr_f || t->table[table_idx].new_f) {
+                               if (t->table[table_idx].curr_f == f) {
+                                       if (t->table[table_idx].type == s->params[param_idx].type) {
+                                               switch (t->table[table_idx].type) {
+                                               case PARAM_INT:
+                                                       accept = 1;
+                                                       break;
+                                                       case PARAM_STR:
+                                                       accept = (((t->table[table_idx].name.len == s->params[param_idx].v.s.len) || !t->table[table_idx].name.len)
+                                                                  && (!t->table[table_idx].name.s || !strncasecmp(t->table[table_idx].name.s, s->params[param_idx].v.s.s, s->params[param_idx].v.s.len)));
+                                                       break;
+                                               default:
+                                                       break;
+                                               }
+                                       };
+                                       if ((t->table[table_idx].flags & IS_ALIAS)&&(!pf)) {
+                                               accept = 1;
+                                       }
+                               }
+                               if (accept) goto accepted;
+                               table_idx++;
+                       }
+               }
+               goto not_found;
+
+               accepted:
+               if (t->table[table_idx].flags & CONSUME_NEXT_STR) {
+                       if ((param_idx<s->n-1) && (s->params[param_idx+1].type == PARAM_STR)) {
+                               param_idx++;
+                       } else if (!(t->table[table_idx].flags & OPTIONAL)) {
+                               goto not_found;
+                       }
+               }
+               if (t->table[table_idx].flags & CONSUME_NEXT_INT) {
+                       if ((param_idx<s->n-1) && (s->params[param_idx+1].type == PARAM_INT)) {
+                               param_idx++;
+                       } else if (!(t->table[table_idx].flags & OPTIONAL)) {
+                               goto not_found;
+                       }
+               }
+               if (t->table[table_idx].flags & IS_ALIAS) {
+                       pf = f;
+               } else {
+                       param_idx++;
+               }
+               f = t->table[table_idx].new_f;
+       }
+
+       if (t->table[table_idx].flags & PARAM_EXPECTED) goto not_found;
+       s->f = f;
+       s->parent_f = pf;
+       return 0;
+       
+not_found:
+       return -1;
+}
+
+int run_select(str* res, select_t* s, struct sip_msg* msg)
+{
+       if (res == NULL) {
+               BUG("Select unprepared result space\n");
+               return -1;
+       }
+       if (s == 0) {
+               BUG("Select structure is NULL\n");
+               return -1;
+       }
+       if (s->f == 0) {
+               BUG("Select structure has not been resolved\n");
+               return -1;
+       }
+DBG("Calling SELECT %p \n", s->f);
+       return s->f(res, s, msg);
+}
+
+void print_select(select_t* s)
+{
+       int i;
+       DBG("select(");
+       for(i = 0; i < s->n; i++) {
+               if (s->params[i].type == PARAM_INT) {
+                       DBG("%d,", s->params[i].v.i);
+               } else {
+                       DBG("%.*s,", s->params[i].v.s.len, s->params[i].v.s.s);
+               }
+       }
+       DBG(")\n");
+}
+
+int register_select_table(select_row_t* mod_tab)
+{
+       select_table_t* t;
+       t=(select_table_t*)pkg_malloc(sizeof(select_table_t));
+       if (!t) {
+               ERR("No memory for new select_table structure\n");
+               return -1;
+       }
+       
+       t->table=mod_tab;
+       t->next=select_list;
+       select_list=t;
+       return 0;
+}
diff --git a/select.h b/select.h
new file mode 100644 (file)
index 0000000..4dc3fac
--- /dev/null
+++ b/select.h
@@ -0,0 +1,95 @@
+#ifndef _SELECT_H
+#define _SELECT_H
+
+#include "str.h"
+#include "parser/msg_parser.h"
+
+#define MAX_SELECT_PARAMS 32
+
+// Flags for parser table FLAG bitfiels
+#define DIVERSION_MASK   0x00FF
+// if DIVERSION is set and the function is accepted and has STR param
+// the param is changed into PARAM_DIV and the value is set to (flags & DIVERSION_MASK)
+#define DIVERSION        1<<8
+// if any parameter is expected at this stage
+#define PARAM_EXPECTED   1<<9
+// accept if following parameter is STR (any)
+#define CONSUME_NEXT_STR 1<<10
+// accept if following parameter is INT
+#define CONSUME_NEXT_INT 1<<11
+// next parameter is optional (use with CONSUME_NEXT_STR or CONSUME_NEXT_INT
+#define OPTIONAL         1<<12
+// if conversion to common alias is needed
+// up-to now parsed function would be stored in parent_f
+// NOTE: the parameter is not consumed for ALIAS, 
+// so you can leave it as ..,PARAM_INT, STR_NULL,..
+#define IS_ALIAS         1<<13
+
+/*
+ * Selector call parameter
+ */
+typedef enum {
+       PARAM_INT,  /* Integer parameter */
+       PARAM_STR,  /* String parameter */
+       PARAM_DIV,  /* Integer value got from parsing table */
+} select_param_type_t;
+       
+typedef union {
+       int i;  /* Integer value */
+       str s;  /* String value */
+} select_param_value_t;
+       
+typedef struct sel_param {
+        select_param_type_t type;
+        select_param_value_t v;
+} select_param_t;
+
+struct select;
+
+typedef int (*select_f)(str* res, struct select* s, struct sip_msg* msg);
+
+typedef struct select {
+       select_f f;
+       select_f parent_f;
+       select_param_t params[MAX_SELECT_PARAMS];
+       int n;
+} select_t;
+
+typedef struct {
+       select_f curr_f;
+       select_param_type_t type;
+       str name;
+       select_f new_f;
+       int flags;
+} select_row_t;
+
+typedef struct select_table {
+  select_row_t *table;
+  struct select_table *next;
+} select_table_t;
+
+/*
+ * Lookup corresponding select function based on
+ * the select parameters
+ */
+int resolve_select(select_t* s);
+
+/*
+ * Run the select function
+ */
+int run_select(str* res, select_t* s, struct sip_msg* msg);
+
+/*
+ * Print select for debugging purposes 
+ */
+void print_select(select_t* s);
+
+/*
+ * Register modules' own select parser table
+ */
+int register_select_table(select_row_t *table);
+
+#define SELECT_F(function) extern int function (str* res, select_t* s, struct sip_msg* msg);
+#define ABSTRACT_F(function) int function (str* res, select_t* s, struct sip_msg* msg) {return -1;}
+
+#endif /* _SELECT_H */
diff --git a/select_core.c b/select_core.c
new file mode 100644 (file)
index 0000000..f0c8e95
--- /dev/null
@@ -0,0 +1,3 @@
+#include "select.h"
+#include "select_core.h"
+
diff --git a/select_core.h b/select_core.h
new file mode 100644 (file)
index 0000000..1c00ec3
--- /dev/null
@@ -0,0 +1,14 @@
+#ifndef _SELECT_CORE_H
+#define _SELECT_CORE_H
+
+#include "str.h"
+#include "parser/msg_parser.h"
+#include "select.h"
+
+static select_row_t select_core[] = {
+       { NULL, PARAM_INT, STR_NULL, NULL, 0}
+};
+
+static select_table_t select_core_table = {select_core, NULL};
+
+#endif // _SELECT_CORE_H