Browse Source

=AVP core extended to be aware of multiple AVPs with the same name exist in
the list. There are three possibilities of correct script syntax...

$prefix.name - it MUST be only one AVP with that name to match binary
operators or on the right side of assignment

$prefix.name[] - allowed on both sides of assignment
on left side means - enable more AVPS with the same name
on the right side - all AVPs with the name are added (correctly ordered)

$prefix.name[index] - allowed on right side of assignment and as binary
operand, corresponds to value of index-th AVP in the list (if such exists)

1 means the AVP, which was added FIRST to the list (this unfortunatelly
means, that the AVP name is scanned through the whole list)
-1 means the AVP, which was added LAST to the list

prefix is one of following
f - FROM USER
t - TO USER
fd- FROM DOMAIN
td- TO DOMAIN
g - GLOBAL

and if ommited, the default FROM USER is used

=new function parse_avp_name, needs to be enhanced (does not accept
whitespace as the script parser does)

=new function search_avp_by_index, usable even for variant without the
brackets (then it checks the name exclusivity)

=xlog module uses new avp parse and search_by_index function
format string is %avp_syntax, e.g. %$MyAVP[1]

=avp module merged functions from another branch, not updated to new AVP
syntax fully

=script parser updated to new syntax

Michal Matyska 19 years ago
parent
commit
145e3fb302
9 changed files with 425 additions and 139 deletions
  1. 44 19
      action.c
  2. 4 0
      cfg.lex
  3. 76 75
      cfg.y
  4. 2 1
      modules/tm/t_fifo.c
  5. 4 2
      modules/tm/t_funcs.c
  6. 3 3
      route.c
  7. 234 19
      usr_avp.c
  8. 15 3
      usr_avp.h
  9. 43 17
      ut.h

+ 44 - 19
action.c

@@ -1,3 +1,4 @@
+
 /*
 /*
  * $Id$
  * $Id$
  *
  *
@@ -714,6 +715,12 @@ int do_action(struct action* a, struct sip_msg* msg)
 
 
 	        case ADD_T:
 	        case ADD_T:
 	        case ASSIGN_T:
 	        case ASSIGN_T:
+		
+			/* If the left attr was specified withou indexing brackets delete
+			 * existing AVPs before adding new ones
+			 */
+			if ((a->p1.attr->type & AVP_INDEX_ALL) != AVP_INDEX_ALL) delete_avp(a->p1.attr->type, a->p1.attr->name);
+			
 			if (a->p2_type == STRING_ST) {
 			if (a->p2_type == STRING_ST) {
 				value.s = a->p2.str;
 				value.s = a->p2.str;
 				flags = a->p1.attr->type | AVP_VAL_STR;
 				flags = a->p1.attr->type | AVP_VAL_STR;
@@ -750,27 +757,45 @@ int do_action(struct action* a, struct sip_msg* msg)
 			} else if (a->p2_type == AVP_ST) {
 			} else if (a->p2_type == AVP_ST) {
 				struct search_state st;
 				struct search_state st;
 				avp_t* avp; 
 				avp_t* avp; 
+				avp_t* avp_mark;
 				
 				
-				     /* If the action is assign then remove the old avp value before adding
-				      * new ones
-				      */
-				if ((unsigned char)a->type == ASSIGN_T) delete_avp(a->p1.attr->type, a->p1.attr->name);
-
-				avp = search_first_avp(a->p2.attr->type, a->p2.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->p1.attr->type | (avp->flags & ~(AVP_CLASS_ALL|AVP_TRACK_ALL));
-					if (add_avp(flags, name, value) < 0) {
-						LOG(L_CRIT, "ERROR: Failed to assign value to attribute\n");
-					 	ret=E_UNSPEC;
+				avp_mark = NULL;
+				if ((a->p2.attr->type & AVP_INDEX_ALL) == AVP_INDEX_ALL) {
+					avp = search_first_avp(a->p2.attr->type, a->p2.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->p1.attr->type & ~AVP_INDEX_ALL) | (avp->flags & ~(AVP_CLASS_ALL|AVP_TRACK_ALL));
+						
+						if (add_avp_before(avp_mark, flags, a->p1.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->p1.attr->name, NULL, NULL);
+						}
+							
+						avp = search_next_avp(&st, &value);
+					}
+					ret = 1;
+					break;
+				} else {
+					avp = search_avp_by_index(a->p2.attr->type, a->p2.attr->name, &value, a->p2.attr->index);
+					if (avp) {
+						flags = a->p1.attr->type | (avp->flags & ~(AVP_CLASS_ALL|AVP_TRACK_ALL));
+					} else {
+						ret = E_UNSPEC;
 						break;
 						break;
 					}
 					}
-					avp = search_next_avp(&st, &value);
 				}
 				}
-				ret = 1;
-				break;
 			} else if (a->p2_type == SELECT_ST) {
 			} else if (a->p2_type == SELECT_ST) {
 				int r;
 				int r;
 				r = run_select(&value.s, a->p2.select, msg);
 				r = run_select(&value.s, a->p2.select, msg);
@@ -793,8 +818,8 @@ int do_action(struct action* a, struct sip_msg* msg)
 
 
 			/* If the action is assign then remove the old avp value
 			/* If the action is assign then remove the old avp value
 			 * before adding new ones */
 			 * before adding new ones */
-			if ((unsigned char)a->type == ASSIGN_T) delete_avp(flags, name);
-			if (add_avp(flags, name, value) < 0) {
+//			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");
 				LOG(L_CRIT, "ERROR: Failed to assign value to attribute\n");
 				ret=E_UNSPEC;
 				ret=E_UNSPEC;
 				break;
 				break;

+ 4 - 0
cfg.lex

@@ -510,6 +510,10 @@ EAT_ABLE	[\ \t\b\r]
 <ATTR>{ATTR_TODOMAIN}   { count(); return ATTR_TODOMAIN; }
 <ATTR>{ATTR_TODOMAIN}   { count(); return ATTR_TODOMAIN; }
 <ATTR>{ATTR_GLOBAL}     { count(); return ATTR_GLOBAL; }
 <ATTR>{ATTR_GLOBAL}     { count(); return ATTR_GLOBAL; }
 <ATTR>{DOT}             { count(); return DOT; }
 <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); 
 <ATTR>{ID}		{ count(); addstr(&s_buf, yytext, yyleng); 
                            yylval.strval=s_buf.s;
                            yylval.strval=s_buf.s;
 			   memset(&s_buf, 0, sizeof(s_buf));
 			   memset(&s_buf, 0, sizeof(s_buf));

+ 76 - 75
cfg.y

@@ -67,6 +67,7 @@
  * 2005-12-11  added onsend_route support, fcmd (filtered cmd),
  * 2005-12-11  added onsend_route support, fcmd (filtered cmd),
  *             snd_{ip,port,proto,af}, to_{ip,proto} (andrei)
  *             snd_{ip,port,proto,af}, to_{ip,proto} (andrei)
  * 2005-12-19  select framework (mma)
  * 2005-12-19  select framework (mma)
+ * 2006-01-06  AVP index support (mma)
  *
  *
  */
  */
 
 
@@ -336,6 +337,11 @@ static struct socket_id* mk_listen_id(char*, int, int);
 %type <strval> host_sep
 %type <strval> host_sep
 %type <intval> uri_type
 %type <intval> uri_type
 %type <attr> attr_id
 %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_any
 //%type <intval> class_id
 //%type <intval> class_id
 %type <intval> assign_op
 %type <intval> assign_op
 %type <select> select_id
 %type <select> select_id
@@ -957,7 +963,7 @@ uri_type:	URI			{$$=URI_O;}
 		;
 		;
 
 
 exp_elem:	METHOD strop STRING	{$$= mk_elem($2, METHOD_O, 0, STRING_ST, $3);}
 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 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 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  ID	{$$ = mk_elem($2, METHOD_O, 0, STRING_ST,$3); }
 		| METHOD strop error { $$=0; yyerror("string expected"); }
 		| METHOD strop error { $$=0; yyerror("string expected"); }
@@ -966,7 +972,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 STRING	{$$ = mk_elem($2, $1, 0, STRING_ST, $3); }
                 | uri_type strop host 	{$$ = 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 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 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 equalop MYSELF {$$=mk_elem($2, $1, 0, MYSELF_ST, 0); }
 		| uri_type strop error { $$=0; yyerror("string or MYSELF expected"); }
 		| uri_type strop error { $$=0; yyerror("string or MYSELF expected"); }
@@ -974,19 +980,19 @@ exp_elem:	METHOD strop STRING	{$$= mk_elem($2, METHOD_O, 0, STRING_ST, $3);}
 									" == , != or =~ expected");
 									" == , != or =~ expected");
 					}
 					}
 		| SRCPORT intop NUMBER	{ $$=mk_elem($2, SRCPORT_O, 0, NUMBER_ST, (void*)$3 ); }
 		| SRCPORT intop NUMBER	{ $$=mk_elem($2, SRCPORT_O, 0, NUMBER_ST, (void*)$3 ); }
-		| SRCPORT intop attr_id { $$=mk_elem($2, SRCPORT_O, 0, AVP_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 intop error { $$=0; yyerror("number expected"); }
 		| SRCPORT error { $$=0; yyerror("==, !=, <,>, >= or <=  expected"); }
 		| SRCPORT error { $$=0; yyerror("==, !=, <,>, >= or <=  expected"); }
 
 
 		| DSTPORT intop NUMBER	{ $$=mk_elem($2, DSTPORT_O, 0, NUMBER_ST, (void*)$3 ); }
 		| DSTPORT intop NUMBER	{ $$=mk_elem($2, DSTPORT_O, 0, NUMBER_ST, (void*)$3 ); }
-		| DSTPORT intop attr_id	{ $$=mk_elem($2, DSTPORT_O, 0, AVP_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 intop error { $$=0; yyerror("number expected"); }
 		| DSTPORT error { $$=0; yyerror("==, !=, <,>, >= or <=  expected"); }
 		| DSTPORT error { $$=0; yyerror("==, !=, <,>, >= or <=  expected"); }
 
 
 		| SNDPORT intop NUMBER	{	onsend_check("snd_port");
 		| SNDPORT intop NUMBER	{	onsend_check("snd_port");
 									$$=mk_elem($2, SNDPORT_O, 0, NUMBER_ST,
 									$$=mk_elem($2, SNDPORT_O, 0, NUMBER_ST,
 												(void*)$3 ); }
 												(void*)$3 ); }
-		| SNDPORT intop attr_id {	onsend_check("snd_port");
+		| SNDPORT intop attr_id_val {	onsend_check("snd_port");
 									$$=mk_elem($2, SNDPORT_O, 0, AVP_ST,
 									$$=mk_elem($2, SNDPORT_O, 0, AVP_ST,
 												(void*)$3 ); }
 												(void*)$3 ); }
 		| SNDPORT intop error { $$=0; yyerror("number expected"); }
 		| SNDPORT intop error { $$=0; yyerror("number expected"); }
@@ -995,14 +1001,14 @@ exp_elem:	METHOD strop STRING	{$$= mk_elem($2, METHOD_O, 0, STRING_ST, $3);}
 		| TOPORT intop NUMBER	{	onsend_check("to_port");
 		| TOPORT intop NUMBER	{	onsend_check("to_port");
 									$$=mk_elem($2, TOPORT_O, 0, NUMBER_ST,
 									$$=mk_elem($2, TOPORT_O, 0, NUMBER_ST,
 												(void*)$3 ); }
 												(void*)$3 ); }
-		| TOPORT intop attr_id {	onsend_check("to_port");
+		| TOPORT intop attr_id_val {	onsend_check("to_port");
 									$$=mk_elem($2, TOPORT_O, 0, AVP_ST,
 									$$=mk_elem($2, TOPORT_O, 0, AVP_ST,
 												(void*)$3 ); }
 												(void*)$3 ); }
 		| TOPORT intop error { $$=0; yyerror("number expected"); }
 		| TOPORT intop error { $$=0; yyerror("number expected"); }
 		| TOPORT error { $$=0; yyerror("==, !=, <,>, >= or <=  expected"); }
 		| TOPORT error { $$=0; yyerror("==, !=, <,>, >= or <=  expected"); }
 
 
 		| PROTO intop proto	{ $$=mk_elem($2, PROTO_O, 0, NUMBER_ST, (void*)$3 ); }
 		| PROTO intop proto	{ $$=mk_elem($2, PROTO_O, 0, NUMBER_ST, (void*)$3 ); }
-		| PROTO intop attr_id	{ $$=mk_elem($2, PROTO_O, 0, AVP_ST, (void*)$3 ); }
+		| PROTO intop attr_id_val	{ $$=mk_elem($2, PROTO_O, 0, AVP_ST, (void*)$3 ); }
 		| PROTO intop error { $$=0;
 		| PROTO intop error { $$=0;
 								yyerror("protocol expected (udp, tcp or tls)");
 								yyerror("protocol expected (udp, tcp or tls)");
 							}
 							}
@@ -1011,7 +1017,7 @@ exp_elem:	METHOD strop STRING	{$$= mk_elem($2, METHOD_O, 0, STRING_ST, $3);}
 		| SNDPROTO intop proto	{	onsend_check("snd_proto");
 		| SNDPROTO intop proto	{	onsend_check("snd_proto");
 									$$=mk_elem($2, SNDPROTO_O, 0, NUMBER_ST,
 									$$=mk_elem($2, SNDPROTO_O, 0, NUMBER_ST,
 										(void*)$3 ); }
 										(void*)$3 ); }
-		| SNDPROTO intop attr_id {	onsend_check("snd_proto");
+		| SNDPROTO intop attr_id_val {	onsend_check("snd_proto");
 									$$=mk_elem($2, SNDPROTO_O, 0, AVP_ST,
 									$$=mk_elem($2, SNDPROTO_O, 0, AVP_ST,
 										(void*)$3 ); }
 										(void*)$3 ); }
 		| SNDPROTO intop error { $$=0;
 		| SNDPROTO intop error { $$=0;
@@ -1020,28 +1026,28 @@ exp_elem:	METHOD strop STRING	{$$= mk_elem($2, METHOD_O, 0, STRING_ST, $3);}
 		| SNDPROTO error { $$=0; yyerror("equal/!= operator expected"); }
 		| SNDPROTO error { $$=0; yyerror("equal/!= operator expected"); }
 
 
 		| AF intop NUMBER	{ $$=mk_elem($2, AF_O, 0, NUMBER_ST,(void *) $3 ); }
 		| AF intop NUMBER	{ $$=mk_elem($2, AF_O, 0, NUMBER_ST,(void *) $3 ); }
-		| AF intop attr_id	{ $$=mk_elem($2, AF_O, 0, AVP_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 intop error { $$=0; yyerror("number expected"); }
 		| AF error { $$=0; yyerror("equal/!= operator expected"); }
 		| AF error { $$=0; yyerror("equal/!= operator expected"); }
 
 
 		| SNDAF intop NUMBER	{	onsend_check("snd_af");
 		| SNDAF intop NUMBER	{	onsend_check("snd_af");
 									$$=mk_elem($2, SNDAF_O, 0, NUMBER_ST,
 									$$=mk_elem($2, SNDAF_O, 0, NUMBER_ST,
 										(void *) $3 ); }
 										(void *) $3 ); }
-		| SNDAF intop attr_id	{	onsend_check("snd_af");
+		| SNDAF intop attr_id_val	{	onsend_check("snd_af");
 									$$=mk_elem($2, SNDAF_O, 0, AVP_ST,
 									$$=mk_elem($2, SNDAF_O, 0, AVP_ST,
 										(void *) $3 ); }
 										(void *) $3 ); }
 		| SNDAF intop error { $$=0; yyerror("number expected"); }
 		| SNDAF intop error { $$=0; yyerror("number expected"); }
 		| SNDAF error { $$=0; yyerror("equal/!= operator expected"); }
 		| SNDAF error { $$=0; yyerror("equal/!= operator expected"); }
 
 
 		| MSGLEN intop NUMBER	{ $$=mk_elem($2, MSGLEN_O, 0, NUMBER_ST, (void *) $3 ); }
 		| MSGLEN intop NUMBER	{ $$=mk_elem($2, MSGLEN_O, 0, NUMBER_ST, (void *) $3 ); }
-		| MSGLEN intop attr_id	{ $$=mk_elem($2, MSGLEN_O, 0, AVP_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 MAX_LEN	{ $$=mk_elem($2, MSGLEN_O, 0, NUMBER_ST, (void *) BUF_SIZE); }
 		| MSGLEN intop error { $$=0; yyerror("number expected"); }
 		| MSGLEN intop error { $$=0; yyerror("number expected"); }
 		| MSGLEN error { $$=0; yyerror("equal/!= operator expected"); }
 		| MSGLEN error { $$=0; yyerror("equal/!= operator expected"); }
 
 
 		| RETCODE intop NUMBER	{ $$=mk_elem($2, RETCODE_O, 0,
 		| RETCODE intop NUMBER	{ $$=mk_elem($2, RETCODE_O, 0,
 												NUMBER_ST, (void *) $3 ); }
 												NUMBER_ST, (void *) $3 ); }
-		| RETCODE intop attr_id	{ $$=mk_elem($2, RETCODE_O, 0,
+		| RETCODE intop attr_id_val	{ $$=mk_elem($2, RETCODE_O, 0,
 												AVP_ST, (void *) $3 ); }
 												AVP_ST, (void *) $3 ); }
 		| RETCODE intop error { $$=0; yyerror("number expected"); }
 		| RETCODE intop error { $$=0; yyerror("number expected"); }
 		| RETCODE error { $$=0; yyerror("equal/!= operator expected"); }
 		| RETCODE error { $$=0; yyerror("equal/!= operator expected"); }
@@ -1172,16 +1178,16 @@ exp_elem:	METHOD strop STRING	{$$= mk_elem($2, METHOD_O, 0, STRING_ST, $3);}
 		| exp_stm			{ $$=mk_elem( NO_OP, ACTION_O, 0, ACTIONS_ST, $1);  }
 		| exp_stm			{ $$=mk_elem( NO_OP, ACTION_O, 0, ACTIONS_ST, $1);  }
 		| NUMBER		{$$=mk_elem( NO_OP, NUMBER_O, 0, NUMBER_ST, (void*)$1 ); }
 		| NUMBER		{$$=mk_elem( NO_OP, NUMBER_O, 0, NUMBER_ST, (void*)$1 ); }
 
 
-		| 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_val		{$$=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); }
 
 
                 | select_id                 { $$=mk_elem( NO_OP, SELECT_O, $1, 0, 0); }
                 | 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 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 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); }
 		| select_id strop select_id { $$=mk_elem( $2, SELECT_O, $1, SELECT_ST, $3); }
 ;
 ;
 
 
@@ -1334,67 +1340,62 @@ select_id : SELECT_MARK { sel.n = 0; sel.f = 0; } select_params {
 }
 }
 ;
 ;
 
 
+attr_class_spec: ATTR_FROMUSER { s_attr->type |= AVP_TRACK_FROM | AVP_CLASS_USER; } 
+		|ATTR_TOUSER { s_attr->type |= AVP_TRACK_TO | AVP_CLASS_USER; } 
+		|ATTR_FROMDOMAIN { s_attr->type |= AVP_TRACK_FROM | AVP_CLASS_DOMAIN; } 
+		|ATTR_TODOMAIN { s_attr->type |= AVP_TRACK_TO | AVP_CLASS_DOMAIN; } 
+		|ATTR_GLOBAL { s_attr->type |= AVP_TRACK_ALL | AVP_CLASS_GLOBAL; } 
 
 
-attr_id : ATTR_MARK ID { s_attr = (struct avp_spec*)pkg_malloc(sizeof(struct avp_spec));
-                         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) { 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_FROMUSER DOT ID { s_attr = (struct avp_spec*)pkg_malloc(sizeof(struct avp_spec));
-                                       if (!s_attr) { yyerror("No memory left"); }
-                                       s_attr->type = AVP_NAME_STR | AVP_TRACK_FROM | AVP_CLASS_USER;
-                                       s_attr->name.s.s = $4; s_attr->name.s.len = strlen($4);
-                                       $$ = s_attr;
-                                     }
-        | ATTR_MARK ATTR_TOUSER DOT ID { s_attr = (struct avp_spec*)pkg_malloc(sizeof(struct avp_spec));
-                                       if (!s_attr) { yyerror("No memory left"); }
-                                       s_attr->type = AVP_NAME_STR | AVP_TRACK_TO | AVP_CLASS_USER;
-                                       s_attr->name.s.s = $4; s_attr->name.s.len = strlen($4);
-                                       $$ = s_attr;
-                                     }
-        | ATTR_MARK ATTR_FROMDOMAIN DOT ID { s_attr = (struct avp_spec*)pkg_malloc(sizeof(struct avp_spec));
-                                       if (!s_attr) { yyerror("No memory left"); }
-                                       s_attr->type = AVP_NAME_STR | AVP_TRACK_FROM | AVP_CLASS_DOMAIN;
-                                       s_attr->name.s.s = $4; s_attr->name.s.len = strlen($4);
-                                       $$ = s_attr;
-                                     }
-        | ATTR_MARK ATTR_TODOMAIN DOT ID { s_attr = (struct avp_spec*)pkg_malloc(sizeof(struct avp_spec));
-                                       if (!s_attr) { yyerror("No memory left"); }
-                                       s_attr->type = AVP_NAME_STR | AVP_TRACK_TO | AVP_CLASS_DOMAIN;
-                                       s_attr->name.s.s = $4; s_attr->name.s.len = strlen($4);
-                                       $$ = s_attr;
-                                     }
-        | ATTR_MARK ATTR_GLOBAL DOT ID { s_attr = (struct avp_spec*)pkg_malloc(sizeof(struct avp_spec));
-                                       if (!s_attr) { yyerror("No memory left"); }
-                                       s_attr->type = AVP_NAME_STR | AVP_CLASS_GLOBAL;
-                                       s_attr->name.s.s = $4; s_attr->name.s.len = strlen($4);
-                                       $$ = s_attr;
-                                     }
-//        | ATTR_MARK ATTR_TO class_id DOT ID { s_attr = (struct avp_spec*)pkg_malloc(sizeof(struct avp_spec));
-//                                              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;
-//                                            }
+attr_name_spec : ID { s_attr->type |= AVP_NAME_STR; s_attr->name.s.s = $1; s_attr->name.s.len = strlen ($1); }
+;
+	       
+attr_spec : attr_name_spec |
+	    attr_class_spec DOT attr_name_spec
+;
+
+attr_mark : ATTR_MARK 
+	{	s_attr = (struct avp_spec*)pkg_malloc(sizeof(struct avp_spec));
+                if (!s_attr) { yyerror("No memory left"); }
+                s_attr->type = 0;  
+	}
+;
+
+attr_id : attr_mark attr_spec { $$ = s_attr; }	  
+;
+
+attr_id_num_idx : attr_mark attr_spec LBRACK NUMBER RBRACK
+	       	{	s_attr->type|= (AVP_NAME_STR | ($4<0?AVP_INDEX_BACKWARD:AVP_INDEX_FORWARD)); 
+			s_attr->index = ($4<0?-$4:$4);
+	       		$$ = s_attr; 
+	       	}	  
+;
+
+attr_id_no_idx : attr_mark attr_spec LBRACK RBRACK
+	       	{	s_attr->type|= AVP_INDEX_ALL; 
+	       		$$ = s_attr;
+		}	  
+;
+
+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 | attr_id_num_idx
 ;
 ;
 
 
-assign_op : ADDEQ { $$ = ADD_T; }
-          | EQUAL { $$ = ASSIGN_T; }
+//assign_op : ADDEQ { $$ = ADD_T; }
+//          | EQUAL { $$ = ASSIGN_T; }
+assign_op : EQUAL { $$ = ASSIGN_T; }
 ;
 ;
 
 
-assign_action:   attr_id assign_op STRING  { $$=mk_action($2, AVP_ST, STRING_ST, $1, $3); }
-               | 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); }
+assign_action:   attr_id_ass assign_op STRING  { $$=mk_action($2, AVP_ST, STRING_ST, $1, $3); }
+               | attr_id_ass assign_op NUMBER  { $$=mk_action($2, AVP_ST, NUMBER_ST, $1, (void*)$3); }
+               | attr_id_ass assign_op fcmd    { $$=mk_action($2, AVP_ST, ACTION_ST, $1, $3); }
+               | attr_id_ass assign_op attr_id_any { $$=mk_action($2, AVP_ST, AVP_ST, $1, $3); }
+               | attr_id_ass assign_op select_id { $$=mk_action($2, AVP_ST, SELECT_ST, (void*)$1, (void*)$3); }
+               | attr_id_ass assign_op LPAREN exp RPAREN { $$ = mk_action($2, AVP_ST, EXPR_ST, $1, $4); }
 ;
 ;
 
 
 cmd:		FORWARD LPAREN host RPAREN	{ $$=mk_action(	FORWARD_T,
 cmd:		FORWARD LPAREN host RPAREN	{ $$=mk_action(	FORWARD_T,

+ 2 - 1
modules/tm/t_fifo.c

@@ -198,6 +198,7 @@ int parse_tw_append( modparam_t type, void* val)
 	char bar;
 	char bar;
 	str foo;
 	str foo;
 	int n;
 	int n;
+	int index;
 	
 	
 	if (val==0 || ((char*)val)[0]==0)
 	if (val==0 || ((char*)val)[0]==0)
 		return 0;
 		return 0;
@@ -314,7 +315,7 @@ int parse_tw_append( modparam_t type, void* val)
 		/* process and optimize the element name */
 		/* process and optimize the element name */
 		if (ha->type==ELEM_IS_AVP) {
 		if (ha->type==ELEM_IS_AVP) {
 			/* element is AVP */
 			/* element is AVP */
-			if ( parse_avp_spec( &foo, &n, &avp_name)!=0 ) {
+			if ( parse_avp_spec( &foo, &n, &avp_name, &index)!=0 ) {
 				LOG(L_ERR,"ERROR:tm:parse_tw_append: bad alias spec "
 				LOG(L_ERR,"ERROR:tm:parse_tw_append: bad alias spec "
 					"<%.*s>\n",foo.len, foo.s);
 					"<%.*s>\n",foo.len, foo.s);
 				goto error;
 				goto error;

+ 4 - 2
modules/tm/t_funcs.c

@@ -67,9 +67,11 @@
 static int     fr_timer_avp_type = 0;
 static int     fr_timer_avp_type = 0;
 static int_str fr_timer_avp = {0};
 static int_str fr_timer_avp = {0};
 static str     fr_timer_str;
 static str     fr_timer_str;
+static int     fr_timer_index = 0;
 static int     fr_inv_timer_avp_type = 0;
 static int     fr_inv_timer_avp_type = 0;
 static int_str fr_inv_timer_avp = {0};
 static int_str fr_inv_timer_avp = {0};
 static str     fr_inv_timer_str;
 static str     fr_inv_timer_str;
+static int     fr_inv_timer_index = 0;
 
 
 
 
 /* ----------------------------------------------------- */
 /* ----------------------------------------------------- */
@@ -298,7 +300,7 @@ int init_avp_params(char *fr_timer_param, char *fr_inv_timer_param)
 		fr_timer_str.s = fr_timer_param;
 		fr_timer_str.s = fr_timer_param;
 		fr_timer_str.len = strlen(fr_timer_str.s);
 		fr_timer_str.len = strlen(fr_timer_str.s);
 		if (parse_avp_spec( &fr_timer_str, &fr_timer_avp_type,
 		if (parse_avp_spec( &fr_timer_str, &fr_timer_avp_type,
-		&fr_timer_avp)<0) {
+		&fr_timer_avp, &fr_timer_index)<0) {
 			LOG(L_CRIT,"ERROR:tm:init_avp_params: invalid fr_timer "
 			LOG(L_CRIT,"ERROR:tm:init_avp_params: invalid fr_timer "
 				"AVP specs \"%s\"\n", fr_timer_param);
 				"AVP specs \"%s\"\n", fr_timer_param);
 			return -1;
 			return -1;
@@ -309,7 +311,7 @@ int init_avp_params(char *fr_timer_param, char *fr_inv_timer_param)
 		fr_inv_timer_str.s = fr_inv_timer_param;
 		fr_inv_timer_str.s = fr_inv_timer_param;
 		fr_inv_timer_str.len = strlen(fr_inv_timer_str.s);
 		fr_inv_timer_str.len = strlen(fr_inv_timer_str.s);
 		if (parse_avp_spec( &fr_inv_timer_str, &fr_inv_timer_avp_type, 
 		if (parse_avp_spec( &fr_inv_timer_str, &fr_inv_timer_avp_type, 
-		&fr_inv_timer_avp)<0) {
+		&fr_inv_timer_avp, &fr_inv_timer_index)<0) {
 			LOG(L_CRIT,"ERROR:tm:init_avp_params: invalid fr_inv_timer "
 			LOG(L_CRIT,"ERROR:tm:init_avp_params: invalid fr_inv_timer "
 				"AVP specs \"%s\"\n", fr_inv_timer_param);
 				"AVP specs \"%s\"\n", fr_inv_timer_param);
 			return -1;
 			return -1;

+ 3 - 3
route.c

@@ -367,7 +367,7 @@ inline static int comp_num(int op, long left, int rtype, union exp_op* r)
 	long right;
 	long right;
 	
 	
 	if (rtype == AVP_ST) {
 	if (rtype == AVP_ST) {
-		avp = search_first_avp(r->attr->type, r->attr->name, &val, 0);
+		avp = search_avp_by_index(r->attr->type, r->attr->name, &val, r->attr->index);
 		if (avp && !(avp->flags & AVP_VAL_STR)) right = val.n;
 		if (avp && !(avp->flags & AVP_VAL_STR)) right = val.n;
 		else return 0; /* Always fail */
 		else return 0; /* Always fail */
 	} else if (rtype == NUMBER_ST) {
 	} else if (rtype == NUMBER_ST) {
@@ -406,7 +406,7 @@ inline static int comp_str(int op, str* left, int rtype, union exp_op* r, struct
 	right=0; /* warning fix */
 	right=0; /* warning fix */
 	
 	
 	if (rtype == AVP_ST) {
 	if (rtype == AVP_ST) {
-		avp = search_first_avp(r->attr->type, r->attr->name, &val, 0);
+		avp = search_avp_by_index(r->attr->type, r->attr->name, &val, r->attr->index);
 		if (avp && (avp->flags & AVP_VAL_STR)) right = &val.s;
 		if (avp && (avp->flags & AVP_VAL_STR)) right = &val.s;
 		else return 0;
 		else return 0;
 	} else if (rtype == SELECT_ST) {
 	} else if (rtype == SELECT_ST) {
@@ -531,7 +531,7 @@ inline static int comp_avp(int op, avp_spec_t* spec, int rtype, union exp_op* r,
 	avp_t* avp;
 	avp_t* avp;
 	int_str val;
 	int_str val;
 
 
-	avp = search_first_avp(spec->type, spec->name, &val, 0);
+	avp = search_avp_by_index(spec->type, spec->name, &val, spec->index);
 	if (!avp) return 0;
 	if (!avp) return 0;
 
 
 	switch(op) {
 	switch(op) {

+ 234 - 19
usr_avp.c

@@ -30,6 +30,7 @@
  *  2004-10-09  interface more flexible - more function available (bogdan)
  *  2004-10-09  interface more flexible - more function available (bogdan)
  *  2004-11-07  AVP string values are kept 0 terminated (bogdan)
  *  2004-11-07  AVP string values are kept 0 terminated (bogdan)
  *  2004-11-14  global aliases support added
  *  2004-11-14  global aliases support added
+ *  2005-01-05  parse avp name according new syntax
  */
  */
 
 
 
 
@@ -131,7 +132,7 @@ inline static unsigned short compute_ID( str *name )
 }
 }
 
 
 
 
-int add_avp_list(avp_list_t* list, unsigned short flags, int_str name, int_str val)
+avp_t *create_avp (unsigned short flags, int_str name, int_str val)
 {
 {
 	avp_t *avp;
 	avp_t *avp;
 	str *s;
 	str *s;
@@ -139,13 +140,11 @@ int add_avp_list(avp_list_t* list, unsigned short flags, int_str name, int_str v
 	struct str_str_data *ssd;
 	struct str_str_data *ssd;
 	int len;
 	int len;
 
 
-	assert(list != 0);
-
 	if (name.s.s == 0 && name.s.len == 0) {
 	if (name.s.s == 0 && name.s.len == 0) {
 		LOG(L_ERR,"ERROR:avp:add_avp: 0 ID or NULL NAME AVP!");
 		LOG(L_ERR,"ERROR:avp:add_avp: 0 ID or NULL NAME AVP!");
 		goto error;
 		goto error;
 	}
 	}
-
+	
 	/* compute the required mem size */
 	/* compute the required mem size */
 	len = sizeof(struct usr_avp);
 	len = sizeof(struct usr_avp);
 	if (flags&AVP_NAME_STR) {
 	if (flags&AVP_NAME_STR) {
@@ -168,14 +167,12 @@ int add_avp_list(avp_list_t* list, unsigned short flags, int_str name, int_str v
 	avp = (struct usr_avp*)shm_malloc( len );
 	avp = (struct usr_avp*)shm_malloc( len );
 	if (avp==0) {
 	if (avp==0) {
 		LOG(L_ERR,"ERROR:avp:add_avp: no more shm mem\n");
 		LOG(L_ERR,"ERROR:avp:add_avp: no more shm mem\n");
-		goto error;
+		return 0;
 	}
 	}
 
 
 	avp->flags = flags;
 	avp->flags = flags;
 	avp->id = (flags&AVP_NAME_STR)? compute_ID(&name.s) : name.n ;
 	avp->id = (flags&AVP_NAME_STR)? compute_ID(&name.s) : name.n ;
-
-	avp->next = *list;
-	*list = avp;
+	avp->next = NULL;
 
 
 	switch ( flags&(AVP_NAME_STR|AVP_VAL_STR) )
 	switch ( flags&(AVP_NAME_STR|AVP_VAL_STR) )
 	{
 	{
@@ -213,9 +210,23 @@ int add_avp_list(avp_list_t* list, unsigned short flags, int_str name, int_str v
 			ssd->val.s[ssd->val.len] = 0;
 			ssd->val.s[ssd->val.len] = 0;
 			break;
 			break;
 	}
 	}
-
-	return 0;
+	return avp;
 error:
 error:
+	return 0;
+}
+
+int add_avp_list(avp_list_t* list, unsigned short flags, int_str name, int_str val)
+{
+	avp_t *avp;
+
+	assert(list != 0);
+
+	if ((avp = create_avp(flags, name, val))) {
+		avp->next = *list;
+		*list = avp;
+		return 0;
+	}
+	
 	return -1;
 	return -1;
 }
 }
 
 
@@ -242,6 +253,28 @@ int add_avp(unsigned short flags, int_str name, int_str val)
 	return add_avp_list(list, flags & (~(AVP_CLASS_ALL) | avp_class), name, val);
 	return add_avp_list(list, flags & (~(AVP_CLASS_ALL) | avp_class), name, val);
 }
 }
 
 
+int add_avp_before(avp_t *avp, unsigned short flags, int_str name, int_str val)
+{
+	avp_t *new_avp;
+	
+	if (!avp) {
+		return add_avp(flags, name, val);
+	}
+
+	if ((flags & AVP_CLASS_ALL) == 0) flags |= (avp->flags & AVP_CLASS_ALL);
+	if ((flags & AVP_TRACK_ALL) == 0) flags |= (avp->flags & AVP_TRACK_ALL);
+	
+	if ((avp->flags & (AVP_CLASS_ALL|AVP_TRACK_ALL)) != (flags & (AVP_CLASS_ALL|AVP_TRACK_ALL))) {
+		ERR("add_avp_before:Source and target AVPs have different CLASS/TRACK\n");
+		return -1;
+	}
+	if ((new_avp=create_avp(flags, name, val))) {
+		new_avp->next=avp->next;
+		avp->next=new_avp;
+		return 0;
+	}
+	return -1;
+}
 
 
 /* get value functions */
 /* get value functions */
 inline str* get_avp_name(avp_t *avp)
 inline str* get_avp_name(avp_t *avp)
@@ -358,6 +391,13 @@ avp_t *search_first_avp(unsigned short flags, int_str name, int_str *val, struct
 		LOG(L_ERR,"ERROR:avp:search_first_avp: 0 ID or NULL NAME AVP!");
 		LOG(L_ERR,"ERROR:avp:search_first_avp: 0 ID or NULL NAME AVP!");
 		return 0;
 		return 0;
 	}
 	}
+	
+	switch (flags & AVP_INDEX_ALL) {
+		case AVP_INDEX_BACKWARD:
+		case AVP_INDEX_FORWARD:
+			WARN("AVP specified with index, but not used for search\n");
+			break;
+	}
 
 
 	if (!s) s = &st;
 	if (!s) s = &st;
 
 
@@ -366,6 +406,17 @@ avp_t *search_first_avp(unsigned short flags, int_str name, int_str *val, struct
 		      * all of them by default
 		      * all of them by default
 		      */
 		      */
 		flags |= AVP_CLASS_ALL;
 		flags |= AVP_CLASS_ALL;
+		
+		if ((flags & AVP_TRACK_ALL) == 0) {
+		    /* The caller did not specify even the track to search in, so try
+		     * track_from first, and if not found try track_to
+		     */
+		     	ret = search_first_avp(flags | AVP_TRACK_FROM, name, val, s);
+		     	if (ret) {
+		     		return ret;
+		     	}
+		     	flags |= AVP_TRACK_TO;
+		}
 	}
 	}
 
 
 	list = select_list(flags);
 	list = select_list(flags);
@@ -399,6 +450,13 @@ avp_t *search_next_avp(struct search_state* s, int_str *val )
 		return 0;
 		return 0;
 	}
 	}
 
 
+	switch (s->flags & AVP_INDEX_ALL) {
+		case AVP_INDEX_BACKWARD:
+		case AVP_INDEX_FORWARD:
+			WARN("AVP specified with index, but not used for search\n");
+			break;
+	}
+
 	while(1) {
 	while(1) {
 		for( ; s->avp; s->avp = s->avp->next) {
 		for( ; s->avp; s->avp = s->avp->next) {
 			if (s->flags & AVP_NAME_RE) {
 			if (s->flags & AVP_NAME_RE) {
@@ -431,6 +489,54 @@ avp_t *search_next_avp(struct search_state* s, int_str *val )
 	return 0;
 	return 0;
 }
 }
 
 
+int search_reverse( avp_t *cur, struct search_state* st,
+                     unsigned short index, avp_list_t *ret)
+{
+	unsigned short lvl;
+	
+	if (!cur)
+		return 0;
+	lvl = search_reverse(search_next_avp(st, NULL), st, index, ret)+1;
+	if (index==lvl)
+		*ret=cur;
+	return lvl;
+}
+                            
+avp_t *search_avp_by_index( unsigned short flags, int_str name,
+                            int_str *val, unsigned short index) 	
+{
+	avp_t *ret, *cur;
+	struct search_state st;
+	
+	if (flags & AVP_NAME_RE) {
+		BUG("search_by_index not supported for AVP_NAME_RE\n");
+		return 0;
+	}
+	switch (flags & AVP_INDEX_ALL) {
+		case 0:
+			ret = search_first_avp(flags, name, val, &st);
+			if (!ret || search_next_avp(&st, NULL))
+				return 0;
+			else
+				return ret;
+		case AVP_INDEX_ALL:
+			BUG("search_by_index not supported for anonymous index []\n");
+			return 0;
+		case AVP_INDEX_FORWARD:
+			ret = NULL;
+			cur = search_first_avp(flags & ~AVP_INDEX_ALL, name, NULL, &st);
+			search_reverse(cur, &st, index, &ret);
+			if (ret && val)
+				get_avp_val(ret, val);
+			return ret;
+		case AVP_INDEX_BACKWARD:
+			ret = search_first_avp(flags & ~AVP_INDEX_ALL, name, val, &st);
+			for (index--; (ret && index); ret=search_next_avp(&st, val), index--);
+			return ret;
+	}
+		
+	return 0;
+}                            
 
 
 /* FIXME */
 /* FIXME */
 /********* free functions ********/
 /********* free functions ********/
@@ -647,16 +753,28 @@ int lookup_avp_galias(str *alias, int *type, int_str *avp_name)
 
 
 
 
 /* parsing functions */
 /* parsing functions */
+#define ERR_IF_CONTAINS(name,chr) \
+	if (memchr(name->s,chr,name->len)) { \
+		ERR("Unexpected control character '%c' in AVP name\n", chr); \
+		goto error; \
+	}
 
 
-int parse_avp_name( str *name, int *type, int_str *avp_name)
+int parse_avp_name( str *name, int *type, int_str *avp_name, int *index)
 {
 {
 	unsigned int id;
 	unsigned int id;
 	char c;
 	char c;
+	char *p;
+	str s;
 
 
-	if (name==0 || name->s==0 || name->len==0)
+	if (name==0 || name->s==0 || name->len==0) {
+		ERR("NULL name or name->s or name->len\n");
 		goto error;
 		goto error;
+	}
 
 
-	if (name->len>=2 && name->s[1]==':') {
+	if (index) *index = 0;
+	ERR("Parsing '%.*s'\n", name->len, name->s);
+	if (name->len>=2 && name->s[1]==':') { // old fashion i: or s:
+		WARN("i: and s: avp name syntax is deprecated!\n");
 		c = name->s[0];
 		c = name->s[0];
 		name->s += 2;
 		name->s += 2;
 		name->len -= 2;
 		name->len -= 2;
@@ -670,17 +788,108 @@ int parse_avp_name( str *name, int *type, int_str *avp_name)
 			case 'i': case 'I':
 			case 'i': case 'I':
 				*type = 0;
 				*type = 0;
 				if (str2int( name, &id)!=0) {
 				if (str2int( name, &id)!=0) {
-					LOG(L_ERR, "ERROR:parse_avp_name: invalid ID "
+					ERR("invalid ID "
 						"<%.*s> - not a number\n", name->len, name->s);
 						"<%.*s> - not a number\n", name->len, name->s);
 					goto error;
 					goto error;
 				}
 				}
 				avp_name->n = (int)id;
 				avp_name->n = (int)id;
 				break;
 				break;
 			default:
 			default:
-				LOG(L_ERR, "ERROR:parse_avp_name: unsupported type "
+				ERR("unsupported type "
 					"[%c]\n", c);
 					"[%c]\n", c);
 				goto error;
 				goto error;
 		}
 		}
+	} else if ((p=memchr(name->s, '.', name->len))) {
+		if (p-name->s==1) {
+			id=name->s[0];
+			name->s +=2;
+			name->len -=2;
+		} else if (p-name->s==2) {
+			id=name->s[0]<<8 | name->s[1];
+			name->s +=3;
+			name->len -=3;
+		} else {
+			ERR("AVP unknown class prefix '%.*s'\n", p-name->s,name->s);
+			goto error;
+		}
+		if (name->len==0) {
+			ERR("AVP name not specified after the prefix separator\n");
+			goto error;
+		}
+		switch (id) {
+			case 'f':
+				*type = AVP_TRACK_FROM | AVP_CLASS_USER;
+				break;
+			case 't':
+				*type = AVP_TRACK_TO | AVP_CLASS_USER;
+				break;
+			case 0x6664: //'fd'
+				*type = AVP_TRACK_FROM | AVP_CLASS_DOMAIN;
+				break;
+			case 0x7464: // 'td'
+				*type = AVP_TRACK_TO | AVP_CLASS_DOMAIN;
+				break;
+			case 'g':
+				*type = AVP_TRACK_ALL | AVP_CLASS_GLOBAL;
+				break;
+			default:
+				if (id < 1<<8)
+					ERR("AVP unknown class prefix '%c'\n", id);
+				else
+					ERR("AVP unknown class prefix '%c%c'\n", id>>8,id);
+				goto error;
+		}
+		if (name->s[name->len-1]==']') {
+			p=memchr(name->s, '[', name->len);
+			if (!p) {
+				ERR("missing '[' for AVP index\n");
+				goto error; 
+			}
+			s.s=p+1;
+			s.len=name->len-(p-name->s)-2; // [ and ]
+			if (s.len == 0) {
+				*type |= AVP_INDEX_ALL;
+			} else {
+				if (s.s[0]=='-') {
+					*type |= AVP_INDEX_BACKWARD;
+					s.s++;s.len--;
+				} else {
+					*type |= AVP_INDEX_FORWARD;
+				}	
+				if ((str2int(&s, &id) != 0)||(id==0)) {
+					ERR("Invalid AVP index '%.*s'\n", s.len, s.s);
+					goto error;
+				}
+				if (index){
+					*index = id;
+				} else {
+					WARN("AVP index correcly specified, but called without placeholed\n");
+				}
+			}
+			name->len=p-name->s;
+		}
+		ERR_IF_CONTAINS(name,'.');
+		ERR_IF_CONTAINS(name,'[');
+		ERR_IF_CONTAINS(name,']');
+		if ((name->len > 2) && (name->s[0]=='/') && (name->s[name->len-1]=='/')) {
+			avp_name->re=pkg_malloc(sizeof(regex_t));
+			if (!avp_name->re) {
+				BUG("No free memory to allocate AVP_NAME_RE regex\n");
+				goto error;
+			}
+			c=name->s[name->len];
+			name->s[name->len]=0;
+			if (regcomp(avp_name->re, name->s, REG_EXTENDED|REG_NOSUB|REG_ICASE)) {
+				pkg_free(avp_name->re);
+				name->s[name->len] = c;
+				goto error;
+			}
+			*type |= AVP_NAME_RE;
+		} else {
+			ERR_IF_CONTAINS(name,'/');
+			*type |= AVP_NAME_STR;
+		}
+		avp_name->s = *name;
 	} else {
 	} else {
 		/*default is string name*/
 		/*default is string name*/
 		*type = AVP_NAME_STR;
 		*type = AVP_NAME_STR;
@@ -693,7 +902,7 @@ error:
 }
 }
 
 
 
 
-int parse_avp_spec( str *name, int *type, int_str *avp_name)
+int parse_avp_spec( str *name, int *type, int_str *avp_name, int *index)
 {
 {
 	str alias;
 	str alias;
 
 
@@ -710,10 +919,15 @@ int parse_avp_spec( str *name, int *type, int_str *avp_name)
 		alias.len = name->len-1;
 		alias.len = name->len-1;
 		return lookup_avp_galias( &alias, type, avp_name);
 		return lookup_avp_galias( &alias, type, avp_name);
 	} else {
 	} else {
-		return parse_avp_name( name, type, avp_name);
+		return parse_avp_name( name, type, avp_name, index);
 	}
 	}
 }
 }
 
 
+void free_avp_name( int *type, int_str *avp_name)
+{
+	if ((*type & AVP_NAME_RE) && (avp_name->re))
+		pkg_free(avp_name->re);
+}
 
 
 int add_avp_galias_str(char *alias_definition)
 int add_avp_galias_str(char *alias_definition)
 {
 {
@@ -722,7 +936,8 @@ int add_avp_galias_str(char *alias_definition)
 	str  name;
 	str  name;
 	str  alias;
 	str  alias;
 	int  type;
 	int  type;
-
+	int  index;
+	
 	s = alias_definition;
 	s = alias_definition;
 	while(*s && isspace((int)*s))
 	while(*s && isspace((int)*s))
 		s++;
 		s++;
@@ -761,7 +976,7 @@ int add_avp_galias_str(char *alias_definition)
 				goto parse_error;
 				goto parse_error;
 		}
 		}
 
 
-		if (parse_avp_name( &name, &type, &avp_name)!=0) {
+		if (parse_avp_name( &name, &type, &avp_name, &index)!=0) {
 			LOG(L_ERR, "ERROR:add_avp_galias_str: <%.*s> not a valid AVP "
 			LOG(L_ERR, "ERROR:add_avp_galias_str: <%.*s> not a valid AVP "
 				"name\n", name.len, name.s);
 				"name\n", name.len, name.s);
 			goto error;
 			goto error;

+ 15 - 3
usr_avp.h

@@ -52,7 +52,8 @@
  *     7        core              avp is in global list
  *     7        core              avp is in global list
  *     8        core              avp is in the from avp list
  *     8        core              avp is in the from avp list
  *     9        core              avp is in the to avp list
  *     9        core              avp is in the to avp list
- *
+ *    10	core		  avp name with positive index
+ *    11	core		  avp name with negative index
  */
  */
 
 
 #include "str.h"
 #include "str.h"
@@ -65,6 +66,7 @@
 #define AVP_FR_INV_TIMER "fr_inv_timer"  /* Value of final response invite timer */
 #define AVP_FR_INV_TIMER "fr_inv_timer"  /* Value of final response invite timer */
 #define AVP_RPID         "rpid"          /* Remote-Party-ID */
 #define AVP_RPID         "rpid"          /* Remote-Party-ID */
 #define AVP_GFLAGS       "gflags"        /* global flags */
 #define AVP_GFLAGS       "gflags"        /* global flags */
+#define AVP_FLAGS	 "flags"	 /* message flags */
 
 
 struct str_int_data {
 struct str_int_data {
 	str name;
 	str name;
@@ -109,6 +111,7 @@ struct search_state {
 typedef struct avp_spec {
 typedef struct avp_spec {
 	int type;
 	int type;
 	int_str name;
 	int_str name;
+	int index;
 } avp_spec_t;
 } avp_spec_t;
 
 
 /* AVP types */
 /* AVP types */
@@ -128,6 +131,11 @@ typedef struct avp_spec {
 
 
 #define AVP_CLASS_ALL (AVP_CLASS_USER|AVP_CLASS_DOMAIN|AVP_CLASS_GLOBAL)
 #define AVP_CLASS_ALL (AVP_CLASS_USER|AVP_CLASS_DOMAIN|AVP_CLASS_GLOBAL)
 
 
+/* AVP name index */
+#define AVP_INDEX_FORWARD	(1<<10)
+#define AVP_INDEX_BACKWARD	(1<<11)
+#define AVP_INDEX_ALL		(AVP_INDEX_FORWARD | AVP_INDEX_BACKWARD)
+
 #define GALIAS_CHAR_MARKER  '$'
 #define GALIAS_CHAR_MARKER  '$'
 
 
 /* Initialize memory structures */
 /* Initialize memory structures */
@@ -135,6 +143,7 @@ int init_avps(void);
 
 
 /* add avp to the list of avps */
 /* add avp to the list of avps */
 int add_avp(unsigned short flags, int_str name, int_str val);
 int add_avp(unsigned short flags, int_str name, int_str val);
+int add_avp_before(avp_t *avp, unsigned short flags, int_str name, int_str val);
 int add_avp_list(avp_list_t* list, unsigned short flags, int_str name, int_str val);
 int add_avp_list(avp_list_t* list, unsigned short flags, int_str name, int_str val);
 
 
 /* Delete avps with given type and name */
 /* Delete avps with given type and name */
@@ -145,6 +154,8 @@ avp_t *search_first_avp( unsigned short flags, int_str name,
 			 int_str *val, struct search_state* state);
 			 int_str *val, struct search_state* state);
 avp_t *search_next_avp(struct search_state* state, int_str *val);
 avp_t *search_next_avp(struct search_state* state, int_str *val);
 
 
+avp_t *search_avp_by_index( unsigned short flags, int_str name,
+                            int_str *val, unsigned short index);
 /* free functions */
 /* free functions */
 void reset_avps(void);
 void reset_avps(void);
 
 
@@ -164,7 +175,8 @@ avp_list_t* set_avp_list(unsigned short flags, avp_list_t* list);
 int add_avp_galias_str(char *alias_definition);
 int add_avp_galias_str(char *alias_definition);
 int lookup_avp_galias(str *alias, int *type, int_str *avp_name);
 int lookup_avp_galias(str *alias, int *type, int_str *avp_name);
 int add_avp_galias(str *alias, int type, int_str avp_name);
 int add_avp_galias(str *alias, int type, int_str avp_name);
-int parse_avp_name( str *name, int *type, int_str *avp_name);
-int parse_avp_spec( str *name, int *type, int_str *avp_name);
+int parse_avp_name( str *name, int *type, int_str *avp_name, int *index);
+int parse_avp_spec( str *name, int *type, int_str *avp_name, int *index);
+void free_avp_name( int *type, int_str *avp_name);
 
 
 #endif
 #endif

+ 43 - 17
ut.h

@@ -178,27 +178,53 @@ static inline int btostr( char *p,  unsigned char val)
 
 
 #define INT2STR_MAX_LEN  (19+1+1) /* 2^64~= 16*10^18 => 19+1 digits + \0 */
 #define INT2STR_MAX_LEN  (19+1+1) /* 2^64~= 16*10^18 => 19+1 digits + \0 */
 
 
-/* returns a pointer to a static buffer containing l in asciiz & sets len */
-static inline char* int2str(unsigned long l, int* len)
+/* 
+ * returns a pointer to a static buffer containing l in asciiz (with base "base") & sets len 
+ * left padded with 0 to "size"
+ */
+static inline char* int2str_base_0pad(unsigned int l, int* len, int base, int size)
 {
 {
-	static char r[INT2STR_MAX_LEN];
-	int i;
-	
-	i=INT2STR_MAX_LEN-2;
-	r[INT2STR_MAX_LEN-1]=0; /* null terminate */
-	do{
-		r[i]=l%10+'0';
-		i--;
-		l/=10;
-	}while(l && (i>=0));
-	if (l && (i<0)){
-		LOG(L_CRIT, "BUG: int2str: overflow\n");
-	}
-	if (len) *len=(INT2STR_MAX_LEN-2)-i;
-	return &r[i+1];
+        static char r[INT2STR_MAX_LEN];
+        int i, j;
+
+        if (base < 2) {
+                BUG("base underflow\n");
+		return NULL;
+        }
+        if (base > 36) {
+                BUG("base overflow\n");
+		return NULL;
+        }
+        i=INT2STR_MAX_LEN-2;
+        j=i-size;
+        r[INT2STR_MAX_LEN-1]=0; /* null terminate */
+        do{
+                r[i]=l%base;
+                if (r[i]<10)
+                        r[i]+='0';
+                else
+                        r[i]+='a'-10;
+                i--;
+                l/=base;
+        }while((l || i>j) && (i>=0));
+        if (l && (i<0)){
+                BUG("result buffer overflow\n");
+        }
+        if (len) *len=(INT2STR_MAX_LEN-2)-i;
+        return &r[i+1];
 }
 }
 
 
+/* returns a pointer to a static buffer containing l in asciiz (with base "base") & sets len */
+static inline char* int2str_base(unsigned int l, int* len, int base)
+{
+        return int2str_base_0pad(l, len, base, 0);
+}
 
 
+/* returns a pointer to a static buffer containing l in asciiz & sets len */
+static inline char* int2str(unsigned int l, int* len)
+{
+     return int2str_base(l, len, 10);
+}
 
 
 /* faster memchr version */
 /* faster memchr version */
 static inline char* q_memchr(char* p, int c, unsigned int size)
 static inline char* q_memchr(char* p, int c, unsigned int size)