Просмотр исходного кода

Merge remote branch 'origin/daniel/xavp'

* origin/daniel/xavp:
  pv: export new PV class $xavp(name)
  core: introducing xavp (eXtended AVP)
  tm: set/reset head of xavps on TM events
  pv: new pv class $xavp(...)
  core: destroy xavp list once sip msg processing is done

Conflicts:
	modules/tm/h_table.c
	modules/tm/t_reply.c
	modules/tm/uac.c
	modules_k/pv/pv.c
Andrei Pelinescu-Onciul 15 лет назад
Родитель
Сommit
98529eaf53
11 измененных файлов с 1246 добавлено и 2 удалено
  1. 19 0
      modules/tm/h_table.c
  2. 7 1
      modules/tm/h_table.h
  3. 10 0
      modules/tm/t_hooks.c
  4. 21 0
      modules/tm/t_reply.c
  5. 3 0
      modules/tm/uac.c
  6. 12 1
      modules_k/pv/pv.c
  7. 518 0
      modules_k/pv/pv_xavp.c
  8. 41 0
      modules_k/pv/pv_xavp.h
  9. 12 0
      receive.c
  10. 508 0
      xavp.c
  11. 95 0
      xavp.h

+ 19 - 0
modules/tm/h_table.c

@@ -212,6 +212,10 @@ void free_cell( struct cell* dead_cell )
 		destroy_avp_list_unsafe( &dead_cell->uri_avps_from );
 	if (dead_cell->uri_avps_to)
 		destroy_avp_list_unsafe( &dead_cell->uri_avps_to );
+#ifdef WITH_XAVP
+	if (dead_cell->xavps_list)
+		xavp_destroy_list_unsafe( &dead_cell->xavps_list );
+#endif
 
 	/* the cell's body */
 	shm_free_unsafe( dead_cell );
@@ -278,6 +282,9 @@ struct cell*  build_cell( struct sip_msg* p_msg )
 	int          sip_msg_len;
 	avp_list_t* old;
 	struct tm_callback *cbs, *cbs_tmp;
+#ifdef WITH_XAVP
+	sr_xavp_t** xold;
+#endif
 
 	/* allocs a new cell */
 	/* if syn_branch==0 add space for md5 (MD5_LEN -sizeof(struct cell.md5)) */
@@ -317,6 +324,12 @@ struct cell*  build_cell( struct sip_msg* p_msg )
 	new_cell->user_avps_to = *old;
 	*old = 0;
 
+#ifdef WITH_XAVP
+	xold = xavp_set_list(&new_cell->xavps_list );
+	new_cell->xavps_list = *xold;
+	*xold = 0;
+#endif
+
 	     /* We can just store pointer to domain avps in the transaction context,
 	      * because they are read-only
 	      */
@@ -380,9 +393,15 @@ error:
 	destroy_avp_list(&new_cell->user_avps_to);
 	destroy_avp_list(&new_cell->uri_avps_from);
 	destroy_avp_list(&new_cell->uri_avps_to);
+#ifdef WITH_XAVP
+	xavp_destroy_list(&new_cell->xavps_list);
+#endif
 	shm_free(new_cell);
 	/* unlink transaction AVP list and link back the global AVP list (bogdan)*/
 	reset_avps();
+#ifdef WITH_XAVP
+	xavp_reset_list();
+#endif
 	return NULL;
 }
 

+ 7 - 1
modules/tm/h_table.h

@@ -71,6 +71,9 @@
 #include "../../types.h"
 #include "../../md5utils.h"
 #include "../../usr_avp.h"
+#ifdef WITH_XAVP
+#include "../../xavp.h"
+#endif
 #include "../../timer.h"
 #include "../../flags.h"
 #include "../../atomic_ops.h"
@@ -362,7 +365,10 @@ typedef struct cell
 	struct usr_avp *user_avps_to;
 	struct usr_avp *domain_avps_from;
 	struct usr_avp *domain_avps_to;
-	
+#ifdef WITH_XAVP
+	sr_xavp_t *xavps_list;
+#endif
+
 	/* protection against concurrent reply processing */
 	ser_lock_t   reply_mutex;
 	

+ 10 - 0
modules/tm/t_hooks.c

@@ -259,6 +259,9 @@ void run_trans_callbacks_internal(struct tmcb_head_list* cb_lst, int type,
 {
 	struct tm_callback    *cbp;
 	avp_list_t* backup_from, *backup_to, *backup_dom_from, *backup_dom_to, *backup_uri_from, *backup_uri_to;
+#ifdef WITH_XAVP
+	sr_xavp_t **backup_xavps;
+#endif
 
 	backup_uri_from = set_avp_list(AVP_CLASS_URI | AVP_TRACK_FROM,
 			&trans->uri_avps_from );
@@ -272,6 +275,10 @@ void run_trans_callbacks_internal(struct tmcb_head_list* cb_lst, int type,
 			&trans->domain_avps_from);
 	backup_dom_to = set_avp_list(AVP_CLASS_DOMAIN | AVP_TRACK_TO, 
 			&trans->domain_avps_to);
+#ifdef WITH_XAVP
+	backup_xavps = xavp_set_list(&trans->xavps_list);
+#endif
+
 	cbp=(struct tm_callback*)cb_lst->first;
 	while(cbp){
 		membar_depends(); /* make sure the cache has the correct cbp 
@@ -290,6 +297,9 @@ void run_trans_callbacks_internal(struct tmcb_head_list* cb_lst, int type,
 	set_avp_list(AVP_CLASS_USER | AVP_TRACK_FROM, backup_from );
 	set_avp_list(AVP_CLASS_URI | AVP_TRACK_TO, backup_uri_to );
 	set_avp_list(AVP_CLASS_URI | AVP_TRACK_FROM, backup_uri_from );
+#ifdef WITH_XAVP
+	xavp_set_list(backup_xavps);
+#endif
 }
 
 

+ 21 - 0
modules/tm/t_reply.c

@@ -116,6 +116,9 @@
 #include "../../data_lump.h"
 #include "../../data_lump_rpl.h"
 #include "../../usr_avp.h"
+#ifdef WITH_XAVP
+#include "../../usr_avp.h"
+#endif
 #include "../../atomic_ops.h" /* membar_write() */
 #include "../../compiler_opt.h"
 #ifdef USE_DST_BLACKLIST
@@ -692,6 +695,9 @@ void faked_env( struct cell *t, struct sip_msg *msg)
 	static avp_list_t* backup_user_from, *backup_user_to;
 	static avp_list_t* backup_domain_from, *backup_domain_to;
 	static avp_list_t* backup_uri_from, *backup_uri_to;
+#ifdef WITH_XAVP
+	static sr_xavp_t **backup_xavps;
+#endif
 	static struct socket_info* backup_si;
 
 	if (msg) {
@@ -722,6 +728,9 @@ void faked_env( struct cell *t, struct sip_msg *msg)
 		backup_user_to = set_avp_list(AVP_TRACK_TO | AVP_CLASS_USER, &t->user_avps_to );
 		backup_domain_from = set_avp_list(AVP_TRACK_FROM | AVP_CLASS_DOMAIN, &t->domain_avps_from );
 		backup_domain_to = set_avp_list(AVP_TRACK_TO | AVP_CLASS_DOMAIN, &t->domain_avps_to );
+#ifdef WITH_XAVP
+		backup_xavps = xavp_set_list(&t->xavps_list);
+#endif
 		/* set default send address to the saved value */
 		backup_si=bind_address;
 		bind_address=t->uac[0].request.dst.send_sock;
@@ -737,6 +746,9 @@ void faked_env( struct cell *t, struct sip_msg *msg)
 		set_avp_list(AVP_TRACK_TO | AVP_CLASS_DOMAIN, backup_domain_to );
 		set_avp_list(AVP_TRACK_FROM | AVP_CLASS_URI, backup_uri_from );
 		set_avp_list(AVP_TRACK_TO | AVP_CLASS_URI, backup_uri_to );
+#ifdef WITH_XAVP
+		xavp_set_list(backup_xavps);
+#endif
 		bind_address=backup_si;
 	}
 }
@@ -1857,6 +1869,9 @@ int reply_received( struct sip_msg  *p_msg )
 	avp_list_t* backup_user_from, *backup_user_to;
 	avp_list_t* backup_domain_from, *backup_domain_to;
 	avp_list_t* backup_uri_from, *backup_uri_to;
+#ifdef WITH_XAVP
+	sr_xavp_t **backup_xavps;
+#endif
 	int replies_locked;
 #ifdef USE_DNS_FAILOVER
 	int branch_ret;
@@ -2006,6 +2021,9 @@ int reply_received( struct sip_msg  *p_msg )
 		backup_user_to = set_avp_list(AVP_TRACK_TO | AVP_CLASS_USER, &t->user_avps_to );
 		backup_domain_from = set_avp_list(AVP_TRACK_FROM | AVP_CLASS_DOMAIN, &t->domain_avps_from );
 		backup_domain_to = set_avp_list(AVP_TRACK_TO | AVP_CLASS_DOMAIN, &t->domain_avps_to );
+#ifdef WITH_XAVP
+		backup_xavps = xavp_set_list(&t->xavps_list);
+#endif
 		setbflagsval(0, uac->branch_flags);
 		/* Pre- and post-script callbacks have already
 		 * been executed by the core. (Miklos)
@@ -2032,6 +2050,9 @@ int reply_received( struct sip_msg  *p_msg )
 		set_avp_list( AVP_TRACK_TO | AVP_CLASS_USER, backup_user_to );
 		set_avp_list( AVP_TRACK_FROM | AVP_CLASS_DOMAIN, backup_domain_from );
 		set_avp_list( AVP_TRACK_TO | AVP_CLASS_DOMAIN, backup_domain_to );
+#ifdef WITH_XAVP
+		xavp_set_list(backup_xavps);
+#endif
 	}
 #ifdef USE_DST_BLACKLIST
 		/* add temporary to the blacklist the source of a 503 reply */

+ 3 - 0
modules/tm/uac.c

@@ -409,6 +409,9 @@ static inline int t_uac_prepare(uac_req_t *uac_r,
 	/* better reset avp list now - anyhow, it's useless from
 	 * this point (bogdan) */
 	reset_avps();
+#ifdef WITH_XAVP
+	xavp_reset_list();
+#endif
 
 	new_cell->method.s = buf;
 	new_cell->method.len = uac_r->method->len;

+ 12 - 1
modules_k/pv/pv.c

@@ -36,7 +36,9 @@
 #include "pv_time.h"
 #include "pv_trans.h"
 #include "pv_select.h"
-
+#ifdef WITH_XAVP
+#include "pv_xavp.h"
+#endif
 
 MODULE_VERSION
 
@@ -70,6 +72,11 @@ static pv_export_t mod_pvs[] = {
 		pv_parse_select_name, 0, 0, 0 },
 	{{"snd", (sizeof("snd")-1)}, PVT_OTHER, pv_get_snd, 0,
 		pv_parse_snd_name, 0, 0, 0},
+#ifdef WITH_XAVP
+	{ {"xavp", sizeof("xavp")-1}, /* xavp */
+		PVT_OTHER, pv_get_xavp, pv_set_xavp,
+		pv_parse_xavp_name, 0, 0, 0 },
+#endif
 
 	{{"avp", (sizeof("avp")-1)}, PVT_AVP, pv_get_avp, pv_set_avp,
 		pv_parse_avp_name, pv_parse_index, 0, 0},
@@ -397,6 +404,10 @@ static cmd_export_t cmds[]={
 		ANY_ROUTE },
 	{"pv_unset",  (cmd_function)pv_unset,  1, fixup_pvar_null, 0, 
 		ANY_ROUTE },
+#ifdef WITH_XAVP
+	{"pv_xavp_print",  (cmd_function)pv_xavp_print,  0, 0, 0, 
+		ANY_ROUTE },
+#endif
 	{0,0,0,0,0,0}
 };
 

+ 518 - 0
modules_k/pv/pv_xavp.c

@@ -0,0 +1,518 @@
+/**
+ * $Id$
+ *
+ * Copyright (C) 2009 Daniel-Constantin Mierla (asipto.com) 
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifdef WITH_XAVP
+
+#include <stdio.h>
+
+#include "../../dprint.h"
+#include "../../xavp.h"
+
+#include "pv_xavp.h"
+
+int pv_xavp_get_value(struct sip_msg *msg, pv_param_t *param,
+		pv_value_t *res, sr_xavp_t *avp)
+{
+	static char _pv_xavp_buf[128];
+	str s;
+
+	switch(avp->val.type) {
+		case SR_XTYPE_NULL:
+			return pv_get_null(msg, param, res);
+		break;
+		case SR_XTYPE_INT:
+			return pv_get_sintval(msg, param, res, avp->val.v.i);
+		break;
+		case SR_XTYPE_STR:
+			return pv_get_strval(msg, param, res, &avp->val.v.s);
+		break;
+		case SR_XTYPE_TIME:
+			if(snprintf(_pv_xavp_buf, 128, "%lu", avp->val.v.t)<0)
+				return pv_get_null(msg, param, res);
+		break;
+		case SR_XTYPE_LONG:
+			if(snprintf(_pv_xavp_buf, 128, "%ld", avp->val.v.l)<0)
+				return pv_get_null(msg, param, res);
+		break;
+		case SR_XTYPE_LLONG:
+			if(snprintf(_pv_xavp_buf, 128, "%lld", avp->val.v.ll)<0)
+				return pv_get_null(msg, param, res);
+		break;
+		case SR_XTYPE_XAVP:
+			if(snprintf(_pv_xavp_buf, 128, "<<xavp:%p>>", avp->val.v.xavp)<0)
+				return pv_get_null(msg, param, res);
+		break;
+		case SR_XTYPE_DATA:
+			if(snprintf(_pv_xavp_buf, 128, "<<data:%p>>", avp->val.v.data)<0)
+				return pv_get_null(msg, param, res);
+		break;
+		default:
+			return pv_get_null(msg, param, res);
+	}
+	s.s = _pv_xavp_buf;
+	s.len = strlen(_pv_xavp_buf);
+	return pv_get_strval(msg, param, res, &s);
+}
+
+
+int pv_get_xavp(struct sip_msg *msg, pv_param_t *param,
+		pv_value_t *res)
+{
+	pv_xavp_name_t *xname=NULL;
+	sr_xavp_t *avp=NULL;
+	int idxf = 0;
+	int idx = 0;
+	int count;
+
+	if(param==NULL)
+	{
+		LM_ERR("bad parameters\n");
+		return -1;
+	}
+	xname = (pv_xavp_name_t*)param->pvn.u.dname;
+
+	if(xname->index.type==PVT_EXTRA)
+	{
+		/* get the index */
+		if(pv_get_spec_index(msg, &xname->index.pvp, &idx, &idxf)!=0)
+		{
+			LM_ERR("invalid index\n");
+			return -1;
+		}
+	}
+	/* fix the index */
+	if(idx<0)
+	{
+		count = xavp_count(&xname->name, NULL);
+		idx = count + idx + 1;
+	}
+	avp = xavp_get_by_index(&xname->name, idx, NULL);
+	if(avp==NULL)
+		return pv_get_null(msg, param, res);
+	if(xname->next==NULL)
+		return pv_xavp_get_value(msg, param, res, avp);
+
+	if(xname->next->index.type==PVT_EXTRA)
+	{
+		/* get the index */
+		if(pv_get_spec_index(msg, &xname->next->index.pvp, &idx, &idxf)!=0)
+		{
+			LM_ERR("invalid index\n");
+			return -1;
+		}
+	}
+	/* fix the index */
+	if(idx<0)
+	{
+		count = xavp_count(&xname->next->name, &avp->val.v.xavp);
+		idx = count + idx + 1;
+	}
+	avp = xavp_get_by_index(&xname->next->name, idx, &avp->val.v.xavp);
+	if(avp==NULL)
+		return pv_get_null(msg, param, res);
+	return pv_xavp_get_value(msg, param, res, avp);
+}
+
+/**
+ * $xavp(name1[idx1]=>name2[idx2])
+ */
+int pv_set_xavp(struct sip_msg* msg, pv_param_t *param,
+		int op, pv_value_t *val)
+{
+	pv_xavp_name_t *xname=NULL;
+	sr_xavp_t *avp=NULL;
+	sr_xavp_t *list=NULL;
+	sr_xval_t xval;
+	int idxf = 0;
+	int idx = 0;
+	int idxf1 = 0;
+	int idx1 = 0;
+	int count;
+
+	if(param==NULL)
+	{
+		LM_ERR("bad parameters\n");
+		return -1;
+	}
+	xname = (pv_xavp_name_t*)param->pvn.u.dname;
+
+	if(xname->index.type==PVT_EXTRA)
+	{
+		/* get the index */
+		if(pv_get_spec_index(msg, &xname->index.pvp, &idx, &idxf)!=0)
+		{
+			LM_ERR("invalid index\n");
+			return -1;
+		}
+	}
+
+	if((val==NULL) || (val->flags&PV_VAL_NULL))
+	{
+		if(xname->next==NULL)
+		{
+			if(xname->index.type==PVT_EXTRA) {
+				if(idxf==PV_IDX_ALL) {
+					xavp_rm_by_name(&xname->name, 1, NULL);
+					return 0;
+				}
+			}
+			if(idx==0) {
+				xavp_rm_by_name(&xname->name, 0, NULL);
+				return 0;
+			}
+			/* fix the index */
+			if(idx<0)
+			{
+				count = xavp_count(&xname->name, NULL);
+				idx = count + idx + 1;
+			}
+			xavp_rm_by_index(&xname->name, idx, NULL);
+			return 0;
+		}
+		
+		if(xname->next->index.type==PVT_EXTRA)
+		{
+			/* get the index */
+			if(pv_get_spec_index(msg,&xname->next->index.pvp,&idx1,&idxf1)!=0)
+			{
+				LM_ERR("invalid index!\n");
+				return -1;
+			}
+		}
+
+		if(idxf==PV_IDX_ALL) {
+			/* iterate */
+			avp = xavp_get(&xname->name, NULL);
+			while(avp) {
+				if(avp->val.type==SR_XTYPE_XAVP) {
+					if(xname->next->index.type==PVT_EXTRA) {
+						if(idxf1==PV_IDX_ALL) {
+							xavp_rm_by_name(&xname->next->name, 1,
+									&avp->val.v.xavp);
+						} else {
+							/* fix the index */
+							idx = idx1;
+							if(idx<0)
+							{
+								count = xavp_count(&xname->next->name,
+										&avp->val.v.xavp);
+								idx = count + idx1 + 1;
+							}
+							xavp_rm_by_index(&xname->next->name, idx,
+									&avp->val.v.xavp);
+						}
+					} else {
+						xavp_rm_by_name(&xname->next->name, 0,
+								&avp->val.v.xavp);
+					}
+				}
+				avp = xavp_get_next(avp);
+			}
+			return 0;
+		}
+
+		if(idx==0) {
+			avp = xavp_get(&xname->name, NULL);
+		} else {
+			/* fix the index */
+			if(idx<0)
+			{
+				count = xavp_count(&xname->name, NULL);
+				idx = count + idx + 1;
+			}
+			avp = xavp_get_by_index(&xname->name, idx, NULL);
+		}
+		if(avp) {
+			if(avp->val.type==SR_XTYPE_XAVP) {
+				if(xname->next->index.type==PVT_EXTRA) {
+					if(idxf1==PV_IDX_ALL) {
+						xavp_rm_by_name(&xname->next->name, 1,
+								&avp->val.v.xavp);
+					} else {
+						/* fix the index */
+						idx = idx1;
+						if(idx<0)
+						{
+							count = xavp_count(&xname->next->name,
+									&avp->val.v.xavp);
+							idx = count + idx1 + 1;
+						}
+						xavp_rm_by_index(&xname->next->name, idx,
+								&avp->val.v.xavp);
+					}
+				} else {
+					xavp_rm_by_name(&xname->next->name, 0,
+							&avp->val.v.xavp);
+				}
+			}
+		}
+		return 0;
+	} /* NULL assignment */
+
+	/* build xavp value */
+	memset(&xval, 0, sizeof(sr_xval_t));
+
+	if(val->flags&PV_TYPE_INT)
+	{
+		xval.type = SR_XTYPE_INT;
+		xval.v.i = val->ri;
+	} else {
+		xval.type = SR_XTYPE_STR;
+		xval.v.s = val->rs;
+	}
+
+	/* where to add */
+	if(xname->next==NULL)
+	{
+		/* xavp with single value */
+		if(xname->index.type==PVT_EXTRA) {
+			if(idxf==PV_IDX_ALL) {
+				/* ignore: should iterate and set same value to all xavps
+				 * with same name?!?! */
+				return -1;
+			}
+			/* fix the index */
+			if(idx<0)
+			{
+				count = xavp_count(&xname->name, NULL);
+				idx = count + idx + 1;
+			}
+			/* set the value */
+			if(xavp_set_value(&xname->name, idx, &xval, NULL)==NULL)
+				return -1;
+			return 0;
+		}
+		/* add new value */
+		if(xavp_add_value(&xname->name, &xval, NULL)==NULL)
+			return -1;
+		return 0;
+	}
+		
+	/* xavp with xavp list value */
+	if(xname->next->index.type==PVT_EXTRA)
+	{
+		/* get the index */
+		if(pv_get_spec_index(msg,&xname->next->index.pvp,&idx1,&idxf1)!=0)
+		{
+			LM_ERR("invalid index!\n");
+			return -1;
+		}
+	}
+
+	if(xname->index.type==PVT_EXTRA)
+	{
+		/* set the value */
+		if(idxf==PV_IDX_ALL) {
+			/* ignore: should iterate and set same value to all xavps
+			 * with same name?!?! */
+			return 0;
+		}
+
+		if(idx==0) {
+			avp = xavp_get(&xname->name, NULL);
+		} else {
+			/* fix the index */
+			if(idx<0)
+			{
+				count = xavp_count(&xname->name, NULL);
+				idx = count + idx + 1;
+			}
+			avp = xavp_get_by_index(&xname->name, idx, NULL);
+		}
+		if(avp==NULL)
+			return 0;
+
+		if(avp->val.type!=SR_XTYPE_XAVP)
+			return -1;
+			
+		if(xname->next->index.type==PVT_EXTRA) {
+			if(idxf1==PV_IDX_ALL) {
+				/* ignore: should iterate and set same value to all xavps
+				 * with same name?!?! */
+				return 0;
+			}
+			/* fix the index */
+			idx = idx1;
+			if(idx<0)
+			{
+				count = xavp_count(&xname->next->name,
+						&avp->val.v.xavp);
+				idx = count + idx1 + 1;
+			}
+			/* set value */
+			xavp_set_value(&xname->next->name, idx, &xval, &avp->val.v.xavp);
+			return 0;
+		}
+		/* add new value in sublist */
+		if(xavp_add_value(&xname->next->name, &xval, &avp->val.v.xavp)==NULL)
+			return -1;
+		return 0;
+	}
+	/* add new xavp with xavp list */
+	if(xavp_add_value(&xname->next->name, &xval, &list)==NULL)
+		return -1;
+	
+	/* build xavp value */
+	memset(&xval, 0, sizeof(sr_xval_t));
+	xval.type = SR_XTYPE_XAVP;
+	xval.v.xavp = list;
+	xavp_add_value(&xname->name, &xval, NULL);
+
+	return 0;
+}
+
+char* pv_xavp_fill_ni(str *in, pv_xavp_name_t *xname)
+{
+	char *p;
+	str idx;
+	int n;
+
+	if(in->s==NULL || in->len<=0 || xname==NULL)
+		return NULL;
+	p = in->s;
+
+	/* eat ws */
+	while(p<in->s+in->len && (*p==' ' || *p=='\t' || *p=='\n' || *p=='\r'))
+		p++;
+	if(p>in->s+in->len || *p=='\0')
+		goto error;
+	xname->name.s = p;
+	while(p < in->s + in->len)
+	{
+		if(*p=='=' || *p==' ' || *p=='\t' || *p=='\n' || *p=='\r' || *p=='[')
+			break;
+		p++;
+	}
+	xname->name.len = p - xname->name.s;
+	if(p>in->s+in->len || *p=='\0')
+		return p;
+	/* eat ws */
+	while(p<in->s+in->len && (*p==' ' || *p=='\t' || *p=='\n' || *p=='\r'))
+		p++;
+	if(p>in->s+in->len || *p=='\0')
+		return p;
+
+	if(*p!='[')
+		return p;
+	/* there is index */
+	p++;
+	idx.s = p;
+	n = 0;
+	while(p<in->s+in->len && *p!='\0')
+	{
+		if(*p==']')
+		{
+			if(n==0)
+				break;
+			n--;
+		}
+		if(*p == '[')
+			n++;
+		p++;
+	}
+	if(p>in->s+in->len || *p=='\0')
+		goto error;
+
+	if(p==idx.s)
+	{
+		LM_ERR("xavp [\"%.*s\"] does not get empty index param\n",
+				in->len, in->s);
+		goto error;
+	}
+	idx.len = p - idx.s;
+	if(pv_parse_index(&xname->index, &idx)!=0)
+	{
+		LM_ERR("idx \"%.*s\" has an invalid index param [%.*s]\n",
+					in->len, in->s, idx.len, idx.s);
+		goto error;
+	}
+	xname->index.type = PVT_EXTRA;
+	p++;
+	return p;
+error:
+	return NULL;
+}
+
+void pv_xavp_name_destroy(pv_xavp_name_t *xname)
+{
+	return;
+}
+
+int pv_parse_xavp_name(pv_spec_p sp, str *in)
+{
+	pv_xavp_name_t *xname=NULL;
+	char *p;
+	str s;
+
+	if(in->s==NULL || in->len<=0)
+		return -1;
+
+	xname = (pv_xavp_name_t*)pkg_malloc(sizeof(pv_xavp_name_t));
+	if(xname==NULL)
+		return -1;
+
+	memset(xname, 0, sizeof(pv_xavp_name_t));
+
+	s = *in;
+
+	p = pv_xavp_fill_ni(&s, xname);
+	if(p==NULL)
+		goto error;
+
+	if(*p!='=')
+		goto done;
+	p++;
+	if(*p!='>')
+		goto error;
+	p++;
+
+	s.len = in->len - (int)(p - in->s);
+	s.s = p;
+	LM_DBG("xavp sublist [%.*s] - key [%.*s]\n", xname->name.len,
+			xname->name.s, s.len, s.s);
+
+	xname->next = (pv_xavp_name_t*)pkg_malloc(sizeof(pv_xavp_name_t));
+	if(xname->next==NULL)
+		goto error;
+
+	memset(xname->next, 0, sizeof(pv_xavp_name_t));
+
+	p = pv_xavp_fill_ni(&s, xname->next);
+	if(p==NULL)
+		goto error;
+
+done:
+	sp->pvp.pvn.u.dname = (void*)xname;
+	sp->pvp.pvn.type = PV_NAME_PVAR;
+	return 0;
+
+error:
+	if(xname!=NULL) {
+		pv_xavp_name_destroy(xname);
+		pkg_free(xname);
+	}
+	return -1;
+}
+
+int pv_xavp_print(struct sip_msg* msg, char* s1, char *s2)
+{
+	xavp_print_list(NULL);
+	return 1;
+}
+
+#endif

+ 41 - 0
modules_k/pv/pv_xavp.h

@@ -0,0 +1,41 @@
+/**
+ * $Id$
+ *
+ * Copyright (C) 2009 Daniel-Constantin Mierla (asipto.com) 
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+ 
+#ifndef _PV_XAVP_H_
+#define _PV_XAVP_H_
+
+#ifdef WITH_XAVP
+
+#include "../../pvar.h"
+
+typedef struct _pv_xavp_name {
+	str name;
+	pv_spec_t index;
+	struct _pv_xavp_name *next;
+} pv_xavp_name_t;
+
+int pv_get_xavp(struct sip_msg *msg, pv_param_t *param,
+		pv_value_t *res);
+int pv_set_xavp(struct sip_msg* msg, pv_param_t *param,
+		int op, pv_value_t *val);
+int pv_parse_xavp_name(pv_spec_p sp, str *in);
+
+int pv_xavp_print(struct sip_msg* msg, char* s1, char *s2);
+
+#endif
+#endif

+ 12 - 0
receive.c

@@ -66,6 +66,9 @@
 #include "nonsip_hooks.h"
 #include "dset.h"
 #include "usr_avp.h"
+#ifdef WITH_XAVP
+#include "xavp.h"
+#endif
 #include "select_buf.h"
 
 #include "tcp_server.h" /* for tcpconn_add_alias */
@@ -279,6 +282,9 @@ end:
 #endif
 	/* free possible loaded avps -bogdan */
 	reset_avps();
+#ifdef WITH_XAVP
+	xavp_reset_list();
+#endif
 	DBG("receive_msg: cleaning up\n");
 	free_sip_msg(msg);
 	pkg_free(msg);
@@ -291,6 +297,9 @@ error_rpl:
 	/* execute post reply-script callbacks */
 	exec_post_script_cb(msg, ONREPLY_CB_TYPE);
 	reset_avps();
+#ifdef WITH_XAVP
+	xavp_reset_list();
+#endif
 	goto error02;
 #endif /* NO_ONREPLY_ROUTE_ERROR */
 error_req:
@@ -300,6 +309,9 @@ error_req:
 error03:
 	/* free possible loaded avps -bogdan */
 	reset_avps();
+#ifdef WITH_XAVP
+	xavp_reset_list();
+#endif
 error02:
 	free_sip_msg(msg);
 	pkg_free(msg);

+ 508 - 0
xavp.c

@@ -0,0 +1,508 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2009 Daniel-Constantin Mierla (asipto.com) 
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+/*
+ * History:
+ * --------
+ *  2009-05-20  created by daniel
+ */
+
+
+#ifdef WITH_XAVP
+
+#include <stdio.h>
+#include <string.h>
+
+#include "mem/shm_mem.h"
+#include "dprint.h"
+#include "hashes.h"
+#include "xavp.h"
+
+/*! XAVP list head */
+static sr_xavp_t *_xavp_list_head = 0;
+/*! Pointer to XAVP current list */
+static sr_xavp_t **_xavp_list_crt = &_xavp_list_head;
+
+void xavp_shm_free(void *p)
+{
+	shm_free(p);
+}
+
+void xavp_shm_free_unsafe(void *p)
+{
+	shm_free_unsafe(p);
+}
+
+
+void xavp_free(sr_xavp_t *xa)
+{
+	if(xa->val.type == SR_XTYPE_DATA) {
+		if(xa->val.v.data!=NULL && xa->val.v.data->pfree!=NULL) {
+			xa->val.v.data->pfree(xa->val.v.data->p, xavp_shm_free);
+			shm_free(xa->val.v.data);
+		}
+	} else if(xa->val.type == SR_XTYPE_XAVP) {
+		xavp_destroy_list(&xa->val.v.xavp);
+	}
+	shm_free(xa);
+}
+
+void xavp_free_unsafe(sr_xavp_t *xa)
+{
+	if(xa->val.type == SR_XTYPE_DATA) {
+		if(xa->val.v.data!=NULL && xa->val.v.data->pfree!=NULL) {
+			xa->val.v.data->pfree(xa->val.v.data->p, xavp_shm_free_unsafe);
+			shm_free_unsafe(xa->val.v.data);
+		}
+	} else if(xa->val.type == SR_XTYPE_XAVP) {
+		xavp_destroy_list_unsafe(&xa->val.v.xavp);
+	}
+	shm_free_unsafe(xa);
+}
+
+sr_xavp_t *xavp_add_value(str *name, sr_xval_t *val, sr_xavp_t **list)
+{
+	sr_xavp_t *avp=0;
+	int size;
+
+	if(name==NULL || name->s==NULL || val==NULL)
+		return NULL;
+
+	size = sizeof(sr_xavp_t) + name->len + 1;
+	if(val->type == SR_XTYPE_STR)
+		size += val->v.s.len + 1;
+	avp = (sr_xavp_t*)shm_malloc(size);
+	if(avp==NULL)
+		return NULL;
+	memset(avp, 0, size);
+	avp->id = get_hash1_raw(name->s, name->len);
+	avp->name.s = (char*)avp + sizeof(sr_xavp_t);
+	memcpy(avp->name.s, name->s, name->len);
+	avp->name.s[name->len] = '\0';
+	avp->name.len = name->len;
+	memcpy(&avp->val, val, sizeof(sr_xval_t));
+	if(val->type == SR_XTYPE_STR)
+	{
+		avp->val.v.s.s = avp->name.s + avp->name.len + 1;
+		memcpy(avp->val.v.s.s, val->v.s.s, val->v.s.len);
+		avp->val.v.s.s[val->v.s.len] = '\0';
+		avp->val.v.s.len = val->v.s.len;
+	}
+	if(list) {
+		avp->next = *list;
+		*list = avp;
+	} else {
+		avp->next = *_xavp_list_crt;
+		*_xavp_list_crt = avp;
+	}
+
+	return avp;
+}
+
+sr_xavp_t *xavp_set_value(str *name, int idx, sr_xval_t *val, sr_xavp_t **list)
+{
+	sr_xavp_t *avp=0;
+	sr_xavp_t *prv=0;
+	sr_xavp_t *tmp=0;
+	unsigned int id;
+	int size;
+	int n=0;
+
+	if(name==NULL || name->s==NULL || val==NULL)
+		return NULL;
+
+	id = get_hash1_raw(name->s, name->len);
+	if(list)
+		avp = *list;
+	else
+		avp=*_xavp_list_crt;
+	while(avp)
+	{
+		if(avp->id==id && avp->name.len==name->len
+				&& strncmp(avp->name.s, name->s, name->len)==0)
+		{
+			if(idx==n)
+				return avp;
+			n++;
+		}
+		prv = avp;
+		avp=avp->next;
+	}
+	if(avp==NULL)
+		return NULL;
+	tmp = avp;
+
+	size = sizeof(sr_xavp_t) + name->len + 1;
+	if(val->type == SR_XTYPE_STR)
+		size += val->v.s.len + 1;
+	avp = (sr_xavp_t*)shm_malloc(size);
+	if(avp==NULL)
+		return NULL;
+	memset(avp, 0, size);
+	avp->id = get_hash1_raw(name->s, name->len);
+	avp->name.s = (char*)avp + sizeof(sr_xavp_t);
+	memcpy(avp->name.s, name->s, name->len);
+	avp->name.s[name->len] = '\0';
+	avp->name.len = name->len;
+	memcpy(&avp->val, val, sizeof(sr_xval_t));
+	if(val->type == SR_XTYPE_STR)
+	{
+		avp->val.v.s.s = avp->name.s + avp->name.len + 1;
+		memcpy(avp->val.v.s.s, val->v.s.s, val->v.s.len);
+		avp->val.v.s.s[val->v.s.len] = '\0';
+		avp->val.v.s.len = val->v.s.len;
+	}
+	if(prv)
+	{
+			avp->next = prv->next;
+			prv->next = avp;
+	} else {
+		if(list) {
+			avp->next = *list;
+			*list = avp;
+		} else {
+			avp->next = *_xavp_list_crt;
+			*_xavp_list_crt = avp;
+		}
+	}
+	xavp_free(tmp);
+
+	return avp;
+}
+
+sr_xavp_t *xavp_get(str *name, sr_xavp_t *start)
+{
+	sr_xavp_t *avp=0;
+	unsigned int id;
+
+	if(name==NULL || name->s==NULL)
+		return NULL;
+	id = get_hash1_raw(name->s, name->len);
+	
+	if(start)
+		avp = start;
+	else
+		avp=*_xavp_list_crt;
+	while(avp)
+	{
+		if(avp->id==id && avp->name.len==name->len
+				&& strncmp(avp->name.s, name->s, name->len)==0)
+			return avp;
+		avp=avp->next;
+	}
+
+	return NULL;
+}
+
+sr_xavp_t *xavp_get_by_index(str *name, int idx, sr_xavp_t **start)
+{
+	sr_xavp_t *avp=0;
+	unsigned int id;
+	int n = 0;
+
+	if(name==NULL || name->s==NULL)
+		return NULL;
+	id = get_hash1_raw(name->s, name->len);
+	
+	if(start)
+		avp = *start;
+	else
+		avp=*_xavp_list_crt;
+	while(avp)
+	{
+		if(avp->id==id && avp->name.len==name->len
+				&& strncmp(avp->name.s, name->s, name->len)==0)
+		{
+			if(idx==n)
+				return avp;
+			n++;
+		}
+		avp=avp->next;
+	}
+
+	return NULL;
+}
+
+
+sr_xavp_t *xavp_get_next(sr_xavp_t *start)
+{
+	sr_xavp_t *avp=0;
+
+	if(start==NULL)
+		return NULL;
+	
+	avp = start->next;
+	while(avp)
+	{
+		if(avp->id==start->id && avp->name.len==start->name.len
+				&& strncmp(avp->name.s, start->name.s, start->name.len)==0)
+			return avp;
+		avp=avp->next;
+	}
+
+	return NULL;
+}
+
+
+int xavp_rm(sr_xavp_t *xa, sr_xavp_t **head)
+{
+	sr_xavp_t *avp=0;
+	sr_xavp_t *prv=0;
+
+	if(head!=NULL)
+		avp = *head;
+	else
+		avp=*_xavp_list_crt;
+
+	while(avp)
+	{
+		if(avp==xa)
+		{
+			if(prv)
+				prv->next=avp->next;
+			else
+				if(head!=NULL)
+					*head = avp->next;
+				else
+					*_xavp_list_crt = avp->next;
+			xavp_free(avp);
+			return 1;
+		}
+		prv=avp; avp=avp->next;
+	}
+	return 0;
+}
+
+
+int xavp_rm_by_name(str *name, int all, sr_xavp_t **head)
+{
+	sr_xavp_t *avp=0;
+	sr_xavp_t *foo=0;
+	sr_xavp_t *prv=0;
+	unsigned int id = 0;
+	int n=0;
+
+	if(name==NULL || name->s==NULL)
+		return 0;
+
+	id = get_hash1_raw(name->s, name->len);
+	if(head!=NULL)
+		avp = *head;
+	else
+		avp=*_xavp_list_crt;
+	while(avp)
+	{
+		foo = avp;
+		avp=avp->next;
+		if(foo->id==id && foo->name.len==name->len
+				&& strncmp(foo->name.s, name->s, name->len)==0)
+		{
+			if(prv!=NULL)
+				prv->next=foo->next;
+			else
+				if(head!=NULL)
+					*head = foo->next;
+				else
+					*_xavp_list_crt = foo->next;
+			xavp_free(foo);
+			n++;
+			if(all==0)
+				return n;
+		} else {
+			prv = foo;
+		}
+	}
+	return n;
+}
+
+int xavp_rm_by_index(str *name, int idx, sr_xavp_t **head)
+{
+	sr_xavp_t *avp=0;
+	sr_xavp_t *foo=0;
+	sr_xavp_t *prv=0;
+	unsigned int id = 0;
+	int n=0;
+
+	if(name==NULL || name->s==NULL)
+		return 0;
+	if(idx<0)
+		return 0;
+
+	id = get_hash1_raw(name->s, name->len);
+	if(head!=NULL)
+		avp = *head;
+	else
+		avp=*_xavp_list_crt;
+	while(avp)
+	{
+		foo = avp;
+		avp=avp->next;
+		if(foo->id==id && foo->name.len==name->len
+				&& strncmp(foo->name.s, name->s, name->len)==0)
+		{
+			if(idx==n)
+			{
+				if(prv!=NULL)
+					prv->next=foo->next;
+				else
+					if(head!=NULL)
+						*head = foo->next;
+					else
+						*_xavp_list_crt = foo->next;
+				xavp_free(foo);
+				return 1;
+			}
+			n++;
+		}
+		prv = foo;
+	}
+	return 0;
+}
+
+
+int xavp_count(str *name, sr_xavp_t **start)
+{
+	sr_xavp_t *avp=0;
+	unsigned int id;
+	int n = 0;
+
+	if(name==NULL || name->s==NULL)
+		return -1;
+	id = get_hash1_raw(name->s, name->len);
+	
+	if(start)
+		avp = *start;
+	else
+		avp=*_xavp_list_crt;
+	while(avp)
+	{
+		if(avp->id==id && avp->name.len==name->len
+				&& strncmp(avp->name.s, name->s, name->len)==0)
+		{
+			n++;
+		}
+		avp=avp->next;
+	}
+
+	return n;
+}
+
+void xavp_destroy_list_unsafe(sr_xavp_t **head)
+{
+	sr_xavp_t *avp, *foo;
+
+	avp = *head;
+	while(avp)
+	{
+		foo = avp;
+		avp = avp->next;
+		xavp_free_unsafe(foo);
+	}
+	*head = 0;
+}
+
+
+void xavp_destroy_list(sr_xavp_t **head)
+{
+	sr_xavp_t *avp, *foo;
+
+	LM_DBG("destroying xavp list %p\n", *head);
+	avp = *head;
+	while(avp)
+	{
+		foo = avp;
+		avp = avp->next;
+		xavp_free(foo);
+	}
+	*head = 0;
+}
+
+
+void xavp_reset_list(void)
+{
+	assert(_xavp_list_crt!=0 );
+	
+	if (_xavp_list_crt!=&_xavp_list_head)
+		_xavp_list_crt=&_xavp_list_head;
+	xavp_destroy_list(_xavp_list_crt);
+}
+
+
+sr_xavp_t **xavp_set_list(sr_xavp_t **head)
+{
+	sr_xavp_t **avp;
+	
+	assert(_xavp_list_crt!=0);
+
+	avp = _xavp_list_crt;
+	_xavp_list_crt = head;
+	return avp;
+}
+
+sr_xavp_t **xavp_get_crt_list(void)
+{
+	assert(_xavp_list_crt!=0);
+	return _xavp_list_crt;
+}
+
+void xavp_print_list(sr_xavp_t **head)
+{
+	sr_xavp_t *avp=0;
+
+	if(head!=NULL)
+		avp = *head;
+	else
+		avp=*_xavp_list_crt;
+	LM_DBG("+++++ XAVP list: %p\n", avp);
+	while(avp)
+	{
+		LM_DBG("     *** XAVP name: %s\n", avp->name.s);
+		LM_DBG("     XAVP id: %u\n", avp->id);
+		LM_DBG("     XAVP value type: %d\n", avp->val.type);
+		switch(avp->val.type) {
+			case SR_XTYPE_NULL:
+				LM_DBG("     XAVP value: <null>\n");
+			break;
+			case SR_XTYPE_INT:
+				LM_DBG("     XAVP value: %d\n", avp->val.v.i);
+			break;
+			case SR_XTYPE_STR:
+				LM_DBG("     XAVP value: %s\n", avp->val.v.s.s);
+			break;
+			case SR_XTYPE_TIME:
+				LM_DBG("     XAVP value: %lu\n", avp->val.v.t);
+			break;
+			case SR_XTYPE_LONG:
+				LM_DBG("     XAVP value: %ld\n", avp->val.v.l);
+			break;
+			case SR_XTYPE_LLONG:
+				LM_DBG("     XAVP value: %lld\n", avp->val.v.ll);
+			break;
+			case SR_XTYPE_XAVP:
+				LM_DBG("     XAVP value: <xavp:%p>\n", avp->val.v.xavp);
+				xavp_print_list(&avp->val.v.xavp);
+			break;
+			case SR_XTYPE_DATA:
+				LM_DBG("     XAVP value: <data:%p>\n", avp->val.v.data);
+			break;
+		}
+		avp = avp->next;
+	}
+	LM_DBG("----- XAVP list\n");
+}
+
+#endif

+ 95 - 0
xavp.h

@@ -0,0 +1,95 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2009 Daniel-Constantin Mierla (asipto.com) 
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+/*
+ * History:
+ * --------
+ *  2009-05-20  created by daniel
+ */
+
+
+#ifndef _SR_XAVP_H_
+#define _SR_XAVP_H_
+
+#ifdef WITH_XAVP
+
+#include <time.h>
+#include "str.h"
+
+struct _sr_xavp;
+
+typedef enum {
+	SR_XTYPE_NULL=0,
+	SR_XTYPE_INT,
+	SR_XTYPE_STR,
+	SR_XTYPE_TIME,
+	SR_XTYPE_LONG,
+	SR_XTYPE_LLONG,
+	SR_XTYPE_XAVP,
+	SR_XTYPE_DATA
+} sr_xtype_t;
+
+typedef void (*sr_xavp_sfree_f)(void *d);
+typedef void (*sr_data_free_f)(void *d, sr_xavp_sfree_f sfree);
+
+typedef struct _sr_data {
+	void *p;
+	sr_data_free_f pfree;
+} sr_data_t;
+
+typedef struct _sr_xval {
+	sr_xtype_t type;
+	union {
+		int i;
+		str s;                 /* replicated */
+		time_t t;
+		long l;
+		long long ll;
+		struct _sr_xavp *xavp; /* must be given in shm */
+		sr_data_t *data;       /* must be given in shm */
+	} v;
+} sr_xval_t;
+
+typedef struct _sr_xavp {
+	unsigned int id;
+	str name;
+	sr_xval_t val;
+	struct _sr_xavp *next;
+} sr_xavp_t;
+
+int xavp_init_head(void);
+void avpx_free(sr_xavp_t *xa);
+
+sr_xavp_t *xavp_add_value(str *name, sr_xval_t *val, sr_xavp_t **list);
+sr_xavp_t *xavp_set_value(str *name, int idx, sr_xval_t *val, sr_xavp_t **list);
+sr_xavp_t *xavp_get(str *name, sr_xavp_t *start);
+sr_xavp_t *xavp_get_by_index(str *name, int idx, sr_xavp_t **start);
+sr_xavp_t *xavp_get_next(sr_xavp_t *start);
+int xavp_rm_by_name(str *name, int all, sr_xavp_t **head);
+int xavp_rm_by_index(str *name, int idx, sr_xavp_t **head);
+int xavp_rm(sr_xavp_t *xa, sr_xavp_t **head);
+int xavp_count(str *name, sr_xavp_t **start);
+void xavp_destroy_list_unsafe(sr_xavp_t **head);
+void xavp_destroy_list(sr_xavp_t **head);
+void xavp_reset_list(void);
+sr_xavp_t **xavp_set_list(sr_xavp_t **head);
+sr_xavp_t **xavp_get_crt_list(void);
+
+void xavp_print_list(sr_xavp_t **head);
+#endif
+
+#endif