usr_avp.c
717bc5da
 /*
  * $Id$
  *
53c7e0f1
  * Copyright (C) 2001-2003 FhG Fokus
717bc5da
  *
  * This file is part of ser, a free SIP server.
  *
  * ser is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version
  *
  * For a license to use the ser software under conditions
  * other than those described here, or to purchase support for this
  * software, please contact iptel.org by e-mail at the following addresses:
  *    info@iptel.org
  *
  * ser is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
  *
85d4627f
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
717bc5da
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  *
  * History:
  * ---------
2be21e31
  *  2004-07-21  created (bogdan)
7dfb54d7
  *  2004-10-09  interface more flexible - more function available (bogdan)
cf152fbd
  *  2004-11-07  AVP string values are kept 0 terminated (bogdan)
7dfb54d7
  *  2004-11-14  global aliases support added
145e3fb3
  *  2005-01-05  parse avp name according new syntax
717bc5da
  */
 
 
2be21e31
 #include <assert.h>
7dfb54d7
 #include <ctype.h>
 #include <string.h>
 #include <stdlib.h>
2be21e31
 
85d4627f
 #include <stdio.h>
 
717bc5da
 #include "sr_module.h"
 #include "dprint.h"
590612d3
 #include "str.h"
 #include "ut.h"
2be21e31
 #include "mem/shm_mem.h"
7dfb54d7
 #include "mem/mem.h"
717bc5da
 #include "usr_avp.h"
 
3da52f21
 enum idx {
e61ba5d2
 	IDX_FROM_URI = 0,
 	IDX_TO_URI,
 	IDX_FROM_USER,
3da52f21
 	IDX_TO_USER,
 	IDX_FROM_DOMAIN,
 	IDX_TO_DOMAIN,
 	IDX_MAX
d34839a3
 };
 
3da52f21
 
7dfb54d7
 struct avp_galias {
 	str alias;
 	struct avp_spec  avp;
 	struct avp_galias *next;
 };
717bc5da
 
7dfb54d7
 static struct avp_galias *galiases = 0;
717bc5da
 
3da52f21
 static avp_list_t def_list[IDX_MAX];    /* Default AVP lists */
 static avp_list_t* crt_list[IDX_MAX];  /* Pointer to current AVP lists */
 
 /* Global AVP related variables go to shm mem */
 static avp_list_t* def_glist;
 static avp_list_t** crt_glist;
 
85d4627f
 /* AVP flags */
 int registered_avpflags_no = 0;
 static char *registered_avpflags[MAX_AVPFLAG];
3da52f21
 
 /* Initialize AVP lists in private memory and allocate memory
  * for shared lists
  */
 int init_avps(void)
 {
 	int i;
 	     /* Empty default lists */
 	memset(def_list, 0, sizeof(avp_list_t) * IDX_MAX);
85d4627f
 
3da52f21
 	     /* Point current pointers to default lists */
 	for(i = 0; i < IDX_MAX; i++) {
 		crt_list[i] = &def_list[i];
 	}
 
 	def_glist = (avp_list_t*)shm_malloc(sizeof(avp_list_t));
 	crt_glist = (avp_list_t**)shm_malloc(sizeof(avp_list_t*));
 	if (!def_glist || !crt_glist) {
 		LOG(L_ERR, "ERROR: No memory to allocate default global AVP list\n");
 		return -1;
 	}
 	*def_glist = 0;
 	*crt_glist = def_glist;
 	return 0;
 }
 
35f927fb
 
3da52f21
 /*
  * Select active AVP list based on the value of flags
  */
14102f39
 static avp_list_t* select_list(avp_flags_t flags)
3da52f21
 {
e61ba5d2
 	if (flags & AVP_CLASS_URI) {
 		if (flags & AVP_TRACK_TO) {
 			return crt_list[IDX_TO_URI];
 		} else {
 			return crt_list[IDX_FROM_URI];
 		}
 	} else if (flags & AVP_CLASS_USER) {
3da52f21
 		if (flags & AVP_TRACK_TO) {
 			return crt_list[IDX_TO_USER];
 		} else {
 			return crt_list[IDX_FROM_USER];
 		}
 	} else if (flags & AVP_CLASS_DOMAIN) {
 		if (flags & AVP_TRACK_TO) {
 			return crt_list[IDX_TO_DOMAIN];
 		} else {
 			return crt_list[IDX_FROM_DOMAIN];
 		}
d576152f
 	} else if (flags & AVP_CLASS_GLOBAL) {
 		return *crt_glist;
3da52f21
 	}
35f927fb
 
d576152f
 	return NULL;
3da52f21
 }
2be21e31
 
14102f39
 inline static avp_id_t compute_ID( str *name )
717bc5da
 {
2be21e31
 	char *p;
14102f39
 	avp_id_t id;
2be21e31
 
 	id=0;
 	for( p=name->s+name->len-1 ; p>=name->s ; p-- )
 		id ^= *p;
 	return id;
 }
 
 
14102f39
 avp_t *create_avp (avp_flags_t flags, avp_name_t name, avp_value_t val)
2be21e31
 {
35f927fb
 	avp_t *avp;
2be21e31
 	str *s;
 	struct str_int_data *sid;
 	struct str_str_data *ssd;
 	int len;
 
3da52f21
 	if (name.s.s == 0 && name.s.len == 0) {
6f432b99
 		LOG(L_ERR,"ERROR:avp:add_avp: 0 ID or NULL NAME AVP!");
 		goto error;
 	}
85d4627f
 
2be21e31
 	/* compute the required mem size */
 	len = sizeof(struct usr_avp);
 	if (flags&AVP_NAME_STR) {
3da52f21
 		if ( name.s.s==0 || name.s.len==0) {
6f432b99
 			LOG(L_ERR,"ERROR:avp:add_avp: EMPTY NAME AVP!");
 			goto error;
 		}
9d7f3515
 		if (flags&AVP_VAL_STR) {
fdb74581
 			len += sizeof(struct str_str_data)-sizeof(union usr_avp_data)
3da52f21
 				+ name.s.len + 1 /* Terminating zero for regex search */
 				+ val.s.len + 1; /* Value is zero terminated */
9d7f3515
 		} else {
fdb74581
 			len += sizeof(struct str_int_data)-sizeof(union usr_avp_data)
3da52f21
 				+ name.s.len + 1; /* Terminating zero for regex search */
9d7f3515
 		}
 	} else if (flags&AVP_VAL_STR) {
fdb74581
 		len += sizeof(str)-sizeof(union usr_avp_data) + val.s.len + 1;
9d7f3515
 	}
2be21e31
 
 	avp = (struct usr_avp*)shm_malloc( len );
 	if (avp==0) {
 		LOG(L_ERR,"ERROR:avp:add_avp: no more shm mem\n");
145e3fb3
 		return 0;
2be21e31
 	}
 
598694f6
 	avp->flags = flags;
3da52f21
 	avp->id = (flags&AVP_NAME_STR)? compute_ID(&name.s) : name.n ;
145e3fb3
 	avp->next = NULL;
2be21e31
 
 	switch ( flags&(AVP_NAME_STR|AVP_VAL_STR) )
 	{
 		case 0:
 			/* avp type ID, int value */
fdb74581
 			avp->d.l = val.n;
2be21e31
 			break;
 		case AVP_NAME_STR:
 			/* avp type str, int value */
fdb74581
 			sid = (struct str_int_data*)&avp->d.data[0];
2be21e31
 			sid->val = val.n;
3da52f21
 			sid->name.len =name.s.len;
2be21e31
 			sid->name.s = (char*)sid + sizeof(struct str_int_data);
3da52f21
 			memcpy( sid->name.s , name.s.s, name.s.len);
 			sid->name.s[name.s.len] = '\0'; /* Zero terminator */
2be21e31
 			break;
 		case AVP_VAL_STR:
 			/* avp type ID, str value */
fdb74581
 			s = (str*)&avp->d.data[0];
3da52f21
 			s->len = val.s.len;
2be21e31
 			s->s = (char*)s + sizeof(str);
3da52f21
 			memcpy( s->s, val.s.s , s->len);
cf152fbd
 			s->s[s->len] = 0;
2be21e31
 			break;
 		case AVP_NAME_STR|AVP_VAL_STR:
 			/* avp type str, str value */
fdb74581
 			ssd = (struct str_str_data*)&avp->d.data[0];
3da52f21
 			ssd->name.len = name.s.len;
2be21e31
 			ssd->name.s = (char*)ssd + sizeof(struct str_str_data);
3da52f21
 			memcpy( ssd->name.s , name.s.s, name.s.len);
 			ssd->name.s[name.s.len]='\0'; /* Zero terminator */
 			ssd->val.len = val.s.len;
9d7f3515
 			ssd->val.s = ssd->name.s + ssd->name.len + 1;
3da52f21
 			memcpy( ssd->val.s , val.s.s, val.s.len);
cf152fbd
 			ssd->val.s[ssd->val.len] = 0;
2be21e31
 			break;
717bc5da
 	}
145e3fb3
 	return avp;
2be21e31
 error:
145e3fb3
 	return 0;
 }
 
14102f39
 int add_avp_list(avp_list_t* list, avp_flags_t flags, avp_name_t name, avp_value_t val)
145e3fb3
 {
 	avp_t *avp;
 
 	assert(list != 0);
 
 	if ((avp = create_avp(flags, name, val))) {
 		avp->next = *list;
 		*list = avp;
 		return 0;
 	}
85d4627f
 
2be21e31
 	return -1;
717bc5da
 }
 
 
14102f39
 int add_avp(avp_flags_t flags, avp_name_t name, avp_value_t val)
598694f6
 {
14102f39
 	avp_flags_t avp_class;
3da52f21
 	avp_list_t* list;
 
e61ba5d2
 	     /* Add avp to uri class if no class has been
3da52f21
 	      * specified by the caller
 	      */
e61ba5d2
 	if ((flags & AVP_CLASS_ALL) == 0) flags |= AVP_CLASS_URI;
3da52f21
 	if ((flags & AVP_TRACK_ALL) == 0) flags |= AVP_TRACK_FROM;
d576152f
 	if (!(list = select_list(flags)))
 		return -1;
3da52f21
 
e61ba5d2
 	if (flags & AVP_CLASS_URI) avp_class = AVP_CLASS_URI;
 	else if (flags & AVP_CLASS_USER) avp_class = AVP_CLASS_USER;
3da52f21
 	else if (flags & AVP_CLASS_DOMAIN) avp_class = AVP_CLASS_DOMAIN;
 	else avp_class = AVP_CLASS_GLOBAL;
 
 	     /* Make that only the selected class is set
 	      * if the caller set more classes in flags
 	      */
 	return add_avp_list(list, flags & (~(AVP_CLASS_ALL) | avp_class), name, val);
598694f6
 }
 
14102f39
 int add_avp_before(avp_t *avp, avp_flags_t flags, avp_name_t name, avp_value_t val)
145e3fb3
 {
 	avp_t *new_avp;
85d4627f
 
145e3fb3
 	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);
85d4627f
 
145e3fb3
 	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;
 }
598694f6
 
a564ae7a
 /* get value functions */
35f927fb
 inline str* get_avp_name(avp_t *avp)
590612d3
 {
8768defd
 	
2be21e31
 	switch ( avp->flags&(AVP_NAME_STR|AVP_VAL_STR) )
 	{
 		case 0:
 			/* avp type ID, int value */
 		case AVP_VAL_STR:
 			/* avp type ID, str value */
 			return 0;
 		case AVP_NAME_STR:
 			/* avp type str, int value */
fdb74581
 			return &((struct str_int_data*)&avp->d.data[0])->name;
2be21e31
 		case AVP_NAME_STR|AVP_VAL_STR:
 			/* avp type str, str value */
fdb74581
 			return &((struct str_str_data*)&avp->d.data[0])->name;
2be21e31
 	}
 
 	LOG(L_ERR,"BUG:avp:get_avp_name: unknown avp type (name&val) %d\n",
3da52f21
 	    avp->flags&(AVP_NAME_STR|AVP_VAL_STR));
2be21e31
 	return 0;
590612d3
 }
 
717bc5da
 
14102f39
 inline void get_avp_val(avp_t *avp, avp_value_t *val)
717bc5da
 {
8768defd
 	
2be21e31
 	if (avp==0 || val==0)
 		return;
717bc5da
 
2be21e31
 	switch ( avp->flags&(AVP_NAME_STR|AVP_VAL_STR) ) {
 		case 0:
 			/* avp type ID, int value */
fdb74581
 			val->n = avp->d.l;
2be21e31
 			break;
 		case AVP_NAME_STR:
 			/* avp type str, int value */
fdb74581
 			val->n = ((struct str_int_data*)&avp->d.data[0])->val;
2be21e31
 			break;
 		case AVP_VAL_STR:
 			/* avp type ID, str value */
fdb74581
 			val->s = *(str*)&avp->d.data[0];
2be21e31
 			break;
 		case AVP_NAME_STR|AVP_VAL_STR:
 			/* avp type str, str value */
fdb74581
 			val->s = ((struct str_str_data*)&avp->d.data[0])->val;
2be21e31
 			break;
717bc5da
 	}
 }
 
 
35f927fb
 /* Return the current list of user attributes */
14102f39
 avp_list_t get_avp_list(avp_flags_t flags)
a564ae7a
 {
d576152f
 	avp_list_t *list;
 
 	list = select_list(flags);
 	return (list ? *list : NULL);
35f927fb
 }
717bc5da
 
 
35f927fb
 /*
  * Compare given id with id in avp, return true if they match
  */
14102f39
 static inline int match_by_id(avp_t* avp, avp_id_t id)
2be21e31
 {
35f927fb
 	if (avp->id == id && (avp->flags&AVP_NAME_STR)==0) {
 		return 1;
2be21e31
 	}
 	return 0;
717bc5da
 }
 
 
35f927fb
 /*
  * Compare given name with name in avp, return true if they are same
  */
14102f39
 static inline int match_by_name(avp_t* avp, avp_id_t id, str* name)
590612d3
 {
35f927fb
 	str* avp_name;
 	if (id==avp->id && avp->flags&AVP_NAME_STR &&
 	    (avp_name=get_avp_name(avp))!=0 && avp_name->len==name->len
 	    && !strncasecmp( avp_name->s, name->s, name->len) ) {
 		return 1;
 	}
2be21e31
 	return 0;
590612d3
 }
 
 
35f927fb
 /*
  * Compare name with name in AVP using regular expressions, return
  * true if they match
  */
 static inline int match_by_re(avp_t* avp, regex_t* re)
9d7f3515
 {
 	regmatch_t pmatch;
35f927fb
 	str * avp_name;
 	     /* AVP identifiable by name ? */
 	if (!(avp->flags&AVP_NAME_STR)) return 0;
 	if ((avp_name=get_avp_name(avp))==0) /* valid AVP name ? */
9d7f3515
 		return 0;
35f927fb
 	if (!avp_name->s) /* AVP name validation */
 		return 0;
 	if (regexec(re, avp_name->s, 1, &pmatch,0)==0) { /* re match ? */
 		return 1;
9d7f3515
 	}
 	return 0;
 }
 
2be21e31
 
14102f39
 avp_t *search_first_avp(avp_flags_t flags, avp_name_t name, avp_value_t *val, struct search_state* s)
 {
 	avp_ident_t id;
 	id.flags = flags;
 	id.name = name;
 	id.index = 0;
 	return search_avp (id, val, s);
 }
 
 avp_t *search_avp (avp_ident_t ident, avp_value_t* val, struct search_state* state)
590612d3
 {
3da52f21
 	avp_t* ret;
35f927fb
 	static struct search_state st;
3da52f21
 	avp_list_t* list;
590612d3
 
14102f39
 	if (ident.name.s.s==0 && ident.name.s.len == 0) {
6f432b99
 		LOG(L_ERR,"ERROR:avp:search_first_avp: 0 ID or NULL NAME AVP!");
 		return 0;
 	}
85d4627f
 
14102f39
 	switch (ident.flags & AVP_INDEX_ALL) {
145e3fb3
 		case AVP_INDEX_BACKWARD:
 		case AVP_INDEX_FORWARD:
 			WARN("AVP specified with index, but not used for search\n");
 			break;
 	}
6f432b99
 
14102f39
 	if (!state) state = &st;
35f927fb
 
14102f39
 	if ((ident.flags & AVP_CLASS_ALL) == 0) {
35f927fb
 		     /* The caller did not specify any class to search in, so enable
 		      * all of them by default
 		      */
14102f39
 		ident.flags |= AVP_CLASS_ALL;
85d4627f
 
14102f39
 		if ((ident.flags & AVP_TRACK_ALL) == 0) {
fd6d4dc9
 		    /* The caller did not specify even the track to search in, so search
 		     * in the track_from
145e3fb3
 		     */
fd6d4dc9
 			ident.flags |= AVP_TRACK_FROM;
145e3fb3
 		}
35f927fb
 	}
3da52f21
 
d576152f
 	if (!(list = select_list(ident.flags)))
 		return NULL;
3da52f21
 
14102f39
 	state->flags = ident.flags;
 	state->avp = *list;
 	state->name = ident.name;
35f927fb
 
14102f39
 	if (ident.flags & AVP_NAME_STR) {
 		state->id = compute_ID(&ident.name.s);
9d7f3515
 	}
35f927fb
 
14102f39
         ret = search_next_avp(state, val);
3da52f21
 
 	     /* Make sure that search next avp stays in the same class as the first
 	      * avp found
 	      */
14102f39
 	if (state && ret) state->flags = (ident.flags & ~AVP_CLASS_ALL) | (ret->flags & AVP_CLASS_ALL);
3da52f21
 	return ret;
590612d3
 }
 
14102f39
 avp_t *search_next_avp(struct search_state* s, avp_value_t *val )
590612d3
 {
35f927fb
 	int matched;
3da52f21
 	avp_t* avp;
d576152f
 	avp_list_t *list;
9d7f3515
 
35f927fb
 	if (s == 0) {
 		LOG(L_ERR, "search_next:avp: Invalid parameter value\n");
2be21e31
 		return 0;
9d7f3515
 	}
590612d3
 
145e3fb3
 	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;
 	}
 
35f927fb
 	while(1) {
 		for( ; s->avp; s->avp = s->avp->next) {
 			if (s->flags & AVP_NAME_RE) {
 				matched = match_by_re(s->avp, s->name.re);
 			} else if (s->flags & AVP_NAME_STR) {
3da52f21
 				matched = match_by_name(s->avp, s->id, &s->name.s);
35f927fb
 			} else {
 				matched = match_by_id(s->avp, s->name.n);
 			}
 			if (matched) {
 				avp = s->avp;
 				s->avp = s->avp->next;
 				if (val) get_avp_val(avp, val);
 				return avp;
 			}
 		}
590612d3
 
e61ba5d2
 		if (s->flags & AVP_CLASS_URI) {
 			s->flags &= ~AVP_CLASS_URI;
d576152f
 			list = select_list(s->flags);
e61ba5d2
 		} else if (s->flags & AVP_CLASS_USER) {
3da52f21
 			s->flags &= ~AVP_CLASS_USER;
d576152f
 			list = select_list(s->flags);
3da52f21
 		} else if (s->flags & AVP_CLASS_DOMAIN) {
 			s->flags &= ~AVP_CLASS_DOMAIN;
d576152f
 			list = select_list(s->flags);
35f927fb
 		} else {
3da52f21
 			s->flags &= ~AVP_CLASS_GLOBAL;
35f927fb
 			return 0;
 		}
d576152f
 		if (!list) return 0;
 		s->avp = *list;
9d7f3515
 	}
590612d3
 
35f927fb
 	return 0;
2be21e31
 }
590612d3
 
145e3fb3
 int search_reverse( avp_t *cur, struct search_state* st,
14102f39
                      avp_index_t index, avp_list_t *ret)
145e3fb3
 {
14102f39
 	avp_index_t lvl;
85d4627f
 
145e3fb3
 	if (!cur)
 		return 0;
 	lvl = search_reverse(search_next_avp(st, NULL), st, index, ret)+1;
 	if (index==lvl)
 		*ret=cur;
 	return lvl;
 }
85d4627f
 
14102f39
 avp_t *search_avp_by_index( avp_flags_t flags, avp_name_t name,
85d4627f
                             avp_value_t *val, avp_index_t index)
145e3fb3
 {
 	avp_t *ret, *cur;
 	struct search_state st;
85d4627f
 
145e3fb3
 	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;
 	}
85d4627f
 
145e3fb3
 	return 0;
85d4627f
 }
590612d3
 
3da52f21
 /* FIXME */
2be21e31
 /********* free functions ********/
32a19992
 
3da52f21
 void destroy_avp(avp_t *avp_del)
2be21e31
 {
3da52f21
 	int i;
35f927fb
 	avp_t *avp, *avp_prev;
2be21e31
 
3da52f21
 	for (i = 0; i < IDX_MAX; i++) {
85d4627f
 		for( avp_prev=0,avp=*crt_list[i] ; avp ;
3da52f21
 		     avp_prev=avp,avp=avp->next ) {
 			if (avp==avp_del) {
 				if (avp_prev) {
 					avp_prev->next=avp->next;
 				} else {
 					*crt_list[i] = avp->next;
 				}
 				shm_free(avp);
 				return;
 			}
 		}
 	}
 
85d4627f
 	for( avp_prev=0,avp=**crt_glist ; avp ;
3da52f21
 	     avp_prev=avp,avp=avp->next ) {
2be21e31
 		if (avp==avp_del) {
3da52f21
 			if (avp_prev) {
2be21e31
 				avp_prev->next=avp->next;
3da52f21
 			} else {
 				**crt_glist = avp->next;
 			}
2be21e31
 			shm_free(avp);
 			return;
590612d3
 		}
 	}
 }
 
 
3da52f21
 void destroy_avp_list_unsafe(avp_list_t* list)
590612d3
 {
35f927fb
 	avp_t *avp, *foo;
590612d3
 
2be21e31
 	avp = *list;
 	while( avp ) {
 		foo = avp;
 		avp = avp->next;
 		shm_free_unsafe( foo );
 	}
 	*list = 0;
590612d3
 }
 
 
3da52f21
 inline void destroy_avp_list(avp_list_t* list)
2be21e31
 {
35f927fb
 	avp_t *avp, *foo;
2be21e31
 
7dfb54d7
 	DBG("DEBUG:destroy_avp_list: destroying list %p\n", *list);
2be21e31
 	avp = *list;
 	while( avp ) {
 		foo = avp;
 		avp = avp->next;
 		shm_free( foo );
 	}
 	*list = 0;
 }
590612d3
 
6bdb84e1
 int reset_avp_list(int flags)
 {
     int i;
     if (flags & AVP_CLASS_URI) {
 	if (flags & AVP_TRACK_FROM) i = IDX_FROM_URI;
 	else i = IDX_TO_URI;
     } else if (flags & AVP_CLASS_USER) {
 	if (flags & AVP_TRACK_FROM) i = IDX_FROM_USER;
 	else i = IDX_TO_USER;
     } else if (flags & AVP_CLASS_DOMAIN) {
 	if (flags & AVP_TRACK_FROM) i = IDX_FROM_DOMAIN;
 	else i = IDX_TO_DOMAIN;
     } else return -1;
 
     crt_list[i] = &def_list[i];
     destroy_avp_list(crt_list[i]);
     return 0;
 }
2be21e31
 
3da52f21
 void reset_avps(void)
590612d3
 {
3da52f21
 	int i;
 	for(i = 0; i < IDX_MAX; i++) {
 		crt_list[i] = &def_list[i];
 		destroy_avp_list(crt_list[i]);
2be21e31
 	}
717bc5da
 }
 
7dfb54d7
 
14102f39
 avp_list_t* set_avp_list( avp_flags_t flags, avp_list_t* list )
35f927fb
 {
3da52f21
 	avp_list_t* prev;
35f927fb
 
e61ba5d2
 	if (flags & AVP_CLASS_URI) {
 		if (flags & AVP_TRACK_FROM) {
 			prev = crt_list[IDX_FROM_URI];
 			crt_list[IDX_FROM_URI] = list;
 		} else {
 			prev = crt_list[IDX_TO_URI];
 			crt_list[IDX_TO_URI] = list;
 		}
 	} else if (flags & AVP_CLASS_USER) {
3da52f21
 		if (flags & AVP_TRACK_FROM) {
 			prev = crt_list[IDX_FROM_USER];
 			crt_list[IDX_FROM_USER] = list;
 		} else {
 			prev = crt_list[IDX_TO_USER];
 			crt_list[IDX_TO_USER] = list;
 		}
 	} else if (flags & AVP_CLASS_DOMAIN) {
 		if (flags & AVP_TRACK_FROM) {
 			prev = crt_list[IDX_FROM_DOMAIN];
 			crt_list[IDX_FROM_DOMAIN] = list;
 		} else {
 			prev = crt_list[IDX_TO_DOMAIN];
 			crt_list[IDX_TO_DOMAIN] = list;
 		}
 	} else {
 		prev = *crt_glist;
 	        *crt_glist = list;
 	}
35f927fb
 
7a2138c3
 	return prev;
35f927fb
 }
7dfb54d7
 
 
 /********* global aliases functions ********/
 
 static inline int check_avp_galias(str *alias, int type, int_str avp_name)
 {
 	struct avp_galias *ga;
 
 	type &= AVP_NAME_STR;
 
 	for( ga=galiases ; ga ; ga=ga->next ) {
 		/* check for duplicated alias names */
 		if ( alias->len==ga->alias.len &&
 		(strncasecmp( alias->s, ga->alias.s, alias->len)==0) )
 			return -1;
 		/*check for duplicated avp names */
 		if (type==ga->avp.type) {
 			if (type&AVP_NAME_STR){
3da52f21
 				if (avp_name.s.len==ga->avp.name.s.len &&
 				    (strncasecmp(avp_name.s.s, ga->avp.name.s.s,
 						 avp_name.s.len)==0) )
7dfb54d7
 					return -1;
 			} else {
 				if (avp_name.n==ga->avp.name.n)
 					return -1;
 			}
 		}
 	}
 	return 0;
 }
 
 
 int add_avp_galias(str *alias, int type, int_str avp_name)
 {
 	struct avp_galias *ga;
 
3da52f21
 	if ((type&AVP_NAME_STR && (!avp_name.s.s ||
 				   !avp_name.s.len)) ||!alias || !alias->s ||
7dfb54d7
 		!alias->len ){
 		LOG(L_ERR, "ERROR:add_avp_galias: null params received\n");
 		goto error;
 	}
 
 	if (check_avp_galias(alias,type,avp_name)!=0) {
 		LOG(L_ERR, "ERROR:add_avp_galias: duplicate alias/avp entry\n");
 		goto error;
 	}
 
 	ga = (struct avp_galias*)pkg_malloc( sizeof(struct avp_galias) );
 	if (ga==0) {
 		LOG(L_ERR, "ERROR:add_avp_galias: no more pkg memory\n");
 		goto error;
 	}
 
 	ga->alias.s = (char*)pkg_malloc( alias->len+1 );
 	if (ga->alias.s==0) {
 		LOG(L_ERR, "ERROR:add_avp_galias: no more pkg memory\n");
 		goto error1;
 	}
 	memcpy( ga->alias.s, alias->s, alias->len);
 	ga->alias.len = alias->len;
 
 	ga->avp.type = type&AVP_NAME_STR;
 
 	if (type&AVP_NAME_STR) {
3da52f21
 		ga->avp.name.s.s = (char*)pkg_malloc( avp_name.s.len+1 );
 		if (ga->avp.name.s.s==0) {
7dfb54d7
 			LOG(L_ERR, "ERROR:add_avp_galias: no more pkg memory\n");
 			goto error2;
 		}
3da52f21
 		ga->avp.name.s.len = avp_name.s.len;
 		memcpy( ga->avp.name.s.s, avp_name.s.s, avp_name.s.len);
 		ga->avp.name.s.s[avp_name.s.len] = 0;
7dfb54d7
 		DBG("DEBUG:add_avp_galias: registering <%s> for avp name <%s>\n",
3da52f21
 			ga->alias.s, ga->avp.name.s.s);
7dfb54d7
 	} else {
 		ga->avp.name.n = avp_name.n;
 		DBG("DEBUG:add_avp_galias: registering <%s> for avp id <%d>\n",
 			ga->alias.s, ga->avp.name.n);
 	}
 
 	ga->next = galiases;
 	galiases = ga;
 
 	return 0;
 error2:
 	pkg_free(ga->alias.s);
 error1:
 	pkg_free(ga);
 error:
 	return -1;
 }
 
 
d34839a3
 int lookup_avp_galias(str *alias, int *type, int_str *avp_name)
7dfb54d7
 {
 	struct avp_galias *ga;
 
 	for( ga=galiases ; ga ; ga=ga->next )
d34839a3
 		if (alias->len==ga->alias.len &&
 		(strncasecmp( alias->s, ga->alias.s, alias->len)==0) ) {
 			*type = ga->avp.type;
 			*avp_name = ga->avp.name;
 			return 0;
 		}
7dfb54d7
 
d34839a3
 	return -1;
7dfb54d7
 }
 
 
 /* parsing functions */
145e3fb3
 #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; \
 	}
7dfb54d7
 
145e3fb3
 int parse_avp_name( str *name, int *type, int_str *avp_name, int *index)
7dfb54d7
 {
14102f39
 	int ret;
 	avp_ident_t attr;
85d4627f
 
14102f39
 	ret=parse_avp_ident(name, &attr);
 	if (!ret) {
 		if (type) *type = attr.flags;
 		if (avp_name) *avp_name = attr.name;
 		if (index) *index = attr.index;
 	}
 	return ret;
 }
 
36fe0059
 
 /** parse an avp indentifier.
  *
  * Parses the following avp indentifier forms:
  *       - "i:<number>"  - old form, deprecated  (e.g. i:42)
  *       - "s:<string>"  - old form, deprecated  (e.g. s:foo)
  *       - "<track>.<name>"                      (e.g.: f.bar)
  *       - "<track>.<name>[<index>]"             (e.g.: f.bar[1])
  *       - "<track><class>.<name>"               (e.g:  tu.bar)
  *       - "<track><class>.<name>[<index>]"      (e.g:  fd.bar[2])
  *       - "<string>"                            (e.g.: foo)
  * Where:
  *          <string> = ascii string
  *          <id>   = ascii string w/o '[', ']', '.' and '/'
  *          <name> = <id> | '/' regex '/'
  *                   (Note: regex use is deprecated)
70346b5b
  *          <track> = 'f' | 't'
  *                   (from or to)
  *          <class> = 'r' | 'u' | 'd' | 'g'
  *                    (uri, user, domain or global)
36fe0059
  *          <index> = <number> | '-' <number> | ''
  *                    (the avp index, if missing it means AVP_INDEX_ALL, but
  *                     it's use is deprecated)
  * More examples:
  *       "fr.bar[1]"  - from track, uri class, avp "bar", the value 1.
  *       "tu./^foo/"  - to track,  user class, all avps for which the name
  *                      starts with foo (note RE in avp names are deprecated).
  *        "t.did"     - to track, "did" avp
  *
  * @param name  - avp identifier
  * @param *attr - the result will be stored here
  * @return 0 on success, -1 on error
  */
14102f39
 int parse_avp_ident( str *name, avp_ident_t* attr)
 {
7dfb54d7
 	unsigned int id;
 	char c;
145e3fb3
 	char *p;
 	str s;
7dfb54d7
 
145e3fb3
 	if (name==0 || name->s==0 || name->len==0) {
 		ERR("NULL name or name->s or name->len\n");
d34839a3
 		goto error;
145e3fb3
 	}
d34839a3
 
14102f39
 	attr->index = 0;
8b93b26b
 	DBG("Parsing '%.*s'\n", name->len, name->s);
80d2811c
 	if (name->len>=2 && name->s[1]==':') { /* old fashion i: or s: */
94cd6af2
 	        /* WARN("i: and s: avp name syntax is deprecated!\n"); */
7dfb54d7
 		c = name->s[0];
 		name->s += 2;
 		name->len -= 2;
 		if (name->len==0)
 			goto error;
 		switch (c) {
 			case 's': case 'S':
14102f39
 				attr->flags = AVP_NAME_STR;
 				attr->name.s = *name;
7dfb54d7
 				break;
 			case 'i': case 'I':
14102f39
 				attr->flags = 0;
7dfb54d7
 				if (str2int( name, &id)!=0) {
145e3fb3
 					ERR("invalid ID "
7dfb54d7
 						"<%.*s> - not a number\n", name->len, name->s);
 					goto error;
 				}
14102f39
 				attr->name.n = (int)id;
7dfb54d7
 				break;
 			default:
145e3fb3
 				ERR("unsupported type "
7dfb54d7
 					"[%c]\n", c);
 				goto error;
 		}
145e3fb3
 	} 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 {
42cbd4be
 			ERR("AVP unknown class prefix '%.*s'\n", name->len, name->s);
145e3fb3
 			goto error;
 		}
 		if (name->len==0) {
 			ERR("AVP name not specified after the prefix separator\n");
 			goto error;
 		}
 		switch (id) {
 			case 'f':
fd6d4dc9
 				attr->flags = AVP_TRACK_FROM;
145e3fb3
 				break;
 			case 't':
fd6d4dc9
 				attr->flags = AVP_TRACK_TO;
 				break;
e61ba5d2
 			case 0x6672: /* 'fr' */
 				attr->flags = AVP_TRACK_FROM | AVP_CLASS_URI;
 				break;
 			case 0x7472: /* 'tr' */
 				attr->flags = AVP_TRACK_TO | AVP_CLASS_URI;
 				break;				
fd6d4dc9
 			case 0x6675: /* 'fu' */
 				attr->flags = AVP_TRACK_FROM | AVP_CLASS_USER;
 				break;
 			case 0x7475: /* 'tu' */
14102f39
 				attr->flags = AVP_TRACK_TO | AVP_CLASS_USER;
145e3fb3
 				break;
fd6d4dc9
 			case 0x6664: /* 'fd' */
14102f39
 				attr->flags = AVP_TRACK_FROM | AVP_CLASS_DOMAIN;
145e3fb3
 				break;
fd6d4dc9
 			case 0x7464: /* 'td' */
14102f39
 				attr->flags = AVP_TRACK_TO | AVP_CLASS_DOMAIN;
145e3fb3
 				break;
 			case 'g':
14102f39
 				attr->flags = AVP_TRACK_ALL | AVP_CLASS_GLOBAL;
145e3fb3
 				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");
85d4627f
 				goto error;
145e3fb3
 			}
 			s.s=p+1;
80d2811c
 			s.len=name->len-(p-name->s)-2; /* [ and ] */
145e3fb3
 			if (s.len == 0) {
14102f39
 				attr->flags |= AVP_INDEX_ALL;
145e3fb3
 			} else {
 				if (s.s[0]=='-') {
14102f39
 					attr->flags |= AVP_INDEX_BACKWARD;
145e3fb3
 					s.s++;s.len--;
 				} else {
14102f39
 					attr->flags |= AVP_INDEX_FORWARD;
85d4627f
 				}
145e3fb3
 				if ((str2int(&s, &id) != 0)||(id==0)) {
 					ERR("Invalid AVP index '%.*s'\n", s.len, s.s);
 					goto error;
 				}
14102f39
 				attr->index = id;
145e3fb3
 			}
 			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]=='/')) {
14102f39
 			attr->name.re=pkg_malloc(sizeof(regex_t));
 			if (!attr->name.re) {
145e3fb3
 				BUG("No free memory to allocate AVP_NAME_RE regex\n");
 				goto error;
 			}
14102f39
 			name->s[name->len-1]=0;
 			if (regcomp(attr->name.re, name->s+1, REG_EXTENDED|REG_NOSUB|REG_ICASE)) {
 				pkg_free(attr->name.re);
36fe0059
 				attr->name.re=0;
14102f39
 				name->s[name->len-1] = '/';
145e3fb3
 				goto error;
 			}
14102f39
 			name->s[name->len-1] = '/';
 			attr->flags |= AVP_NAME_RE;
145e3fb3
 		} else {
 			ERR_IF_CONTAINS(name,'/');
14102f39
 			attr->flags |= AVP_NAME_STR;
 			attr->name.s = *name;
145e3fb3
 		}
7dfb54d7
 	} else {
 		/*default is string name*/
14102f39
 		attr->flags = AVP_NAME_STR;
 		attr->name.s = *name;
7dfb54d7
 	}
 
 	return 0;
 error:
 	return -1;
 }
 
5137b02d
 void free_avp_ident(avp_ident_t* attr)
 {
 	if (attr->flags & AVP_NAME_RE) {
 		if (! attr->name.re) {
 			BUG("attr ident @%p has the regexp flag set, but no regexp.\n",
 					attr);
 #ifdef EXTRA_DEBUG
 			abort();
 #endif
 		} else {
61c7aeed
 			regfree(attr->name.re);
5137b02d
 			pkg_free(attr->name.re);
 		}
 	}
 }
7dfb54d7
 
34b688d7
 int km_parse_avp_spec( str *name, int *type, int_str *avp_name)
 {
 	char *p;
 	int index = 0;
 
 	if (name==0 || name->s==0 || name->len==0)
 		return -1;
 
 	p = (char*)memchr((void*)name->s, ':', name->len);
 	if (p==NULL) {
9c77d532
 		/* might be kamailio avp alias or ser avp name style */
 		if(lookup_avp_galias( name, type, avp_name)==0)
 			return 0; /* found */
34b688d7
 	}
9c77d532
 	return parse_avp_name( name, type, avp_name, &index);
34b688d7
 }
 
 
145e3fb3
 int parse_avp_spec( str *name, int *type, int_str *avp_name, int *index)
d34839a3
 {
 	str alias;
 
 	if (name==0 || name->s==0 || name->len==0)
 		return -1;
 
 	if (name->s[0]==GALIAS_CHAR_MARKER) {
 		/* it's an avp alias */
 		if (name->len==1) {
 			LOG(L_ERR,"ERROR:parse_avp_spec: empty alias\n");
 			return -1;
 		}
 		alias.s = name->s+1;
 		alias.len = name->len-1;
 		return lookup_avp_galias( &alias, type, avp_name);
 	} else {
145e3fb3
 		return parse_avp_name( name, type, avp_name, index);
d34839a3
 	}
 }
 
36fe0059
 void free_avp_name(avp_flags_t *type, int_str *avp_name)
145e3fb3
 {
36fe0059
 	if ((*type & AVP_NAME_RE) && (avp_name->re)){
 		regfree(avp_name->re);
145e3fb3
 		pkg_free(avp_name->re);
36fe0059
 		avp_name->re=0;
 	}
145e3fb3
 }
d34839a3
 
7dfb54d7
 int add_avp_galias_str(char *alias_definition)
 {
 	int_str avp_name;
 	char *s;
 	str  name;
 	str  alias;
 	int  type;
145e3fb3
 	int  index;
85d4627f
 
7dfb54d7
 	s = alias_definition;
 	while(*s && isspace((int)*s))
 		s++;
 
 	while (*s) {
 		/* parse alias name */
 		alias.s = s;
 		while(*s && *s!=';' && !isspace((int)*s) && *s!='=')
 			s++;
 		if (alias.s==s || *s==0 || *s==';')
 			goto parse_error;
 		alias.len = s-alias.s;
 		while(*s && isspace((int)*s))
 			s++;
 		/* equal sign */
 		if (*s!='=')
 			goto parse_error;
 		s++;
 		while(*s && isspace((int)*s))
 			s++;
 		/* avp name */
 		name.s = s;
 		while(*s && *s!=';' && !isspace((int)*s))
 			s++;
 		if (name.s==s)
 			goto parse_error;
 		name.len = s-name.s;
 		while(*s && isspace((int)*s))
 			s++;
 		/* check end */
 		if (*s!=0 && *s!=';')
 			goto parse_error;
 		if (*s==';') {
 			for( s++ ; *s && isspace((int)*s) ; s++ );
 			if (*s==0)
 				goto parse_error;
 		}
 
145e3fb3
 		if (parse_avp_name( &name, &type, &avp_name, &index)!=0) {
7dfb54d7
 			LOG(L_ERR, "ERROR:add_avp_galias_str: <%.*s> not a valid AVP "
 				"name\n", name.len, name.s);
 			goto error;
 		}
 
 		if (add_avp_galias( &alias, type, avp_name)!=0) {
 			LOG(L_ERR, "ERROR:add_avp_galias_str: add global alias failed\n");
 			goto error;
 		}
 	} /*end while*/
 
 	return 0;
 parse_error:
 	LOG(L_ERR, "ERROR:add_avp_galias_str: parse error in <%s> around "
 		"pos %ld\n", alias_definition, (long)(s-alias_definition));
 error:
 	return -1;
 }
3da52f21
 
 
63701a5b
 int destroy_avps(avp_flags_t flags, avp_name_t name, int all)
 {
 	struct search_state st;
 	avp_t* avp;
 	int n;
 	
 	n = 0;
 	avp = search_first_avp(flags, name, 0, &st);
 	while (avp) {
 		destroy_avp(avp);
 		n++;
 		if (!all) break;
 		avp = search_next_avp(&st, 0);
 	}
 	return n;
 }
 
 
14102f39
 void delete_avp(avp_flags_t flags, avp_name_t name)
3da52f21
 {
 	struct search_state st;
 	avp_t* avp;
 
 	avp = search_first_avp(flags, name, 0, &st);
 	while(avp) {
 		destroy_avp(avp);
 		avp = search_next_avp(&st, 0);
 	}
 }
85d4627f
 
 /* AVP flags functions */
 
 /* name2id conversion is intended to use during fixup (cfg parsing and modinit) only therefore no hash is used */
 avp_flags_t register_avpflag(char* name) {
 	avp_flags_t ret;
 	ret = get_avpflag_no(name);
 	if (ret == 0) {
cead8fc3
 		if (registered_avpflags_no >= MAX_AVPFLAG) {
 			LOG(L_ERR, "register_avpflag: cannot register new avp flag ('%s'), max.number of flags (%d) reached\n", name, MAX_AVPFLAG);
 			return -1;
 		}
85d4627f
 		ret = 1<<(AVP_CUSTOM_FLAGS+registered_avpflags_no);
 		registered_avpflags[registered_avpflags_no++] = name;
 	}
 	return ret;
 }
 
 avp_flags_t get_avpflag_no(char* name) {
 	int i;
 	for (i=0; i<registered_avpflags_no; i++) {
 		if (strcasecmp(name, registered_avpflags[i])==0)
 			return 1<<(AVP_CUSTOM_FLAGS+i);
 	}
 	return 0;
 }