/*
 * Copyright (C) 2001-2003 FhG Fokus
 *
 * This file is part of Kamailio, a free SIP server.
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 *
 * Kamailio 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
 *
 * Kamailio 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.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 */


/*!
 * \file
 * \brief Kamailio core :: Attribute value pair handling (AVP)
 * \ingroup core
 * Module: \ref core
 */

#include <assert.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>

#include <stdio.h>

#include "sr_module.h"
#include "dprint.h"
#include "str.h"
#include "ut.h"
#include "mem/shm_mem.h"
#include "mem/mem.h"
#include "usr_avp.h"

enum idx
{
	IDX_FROM_URI = 0,
	IDX_TO_URI,
	IDX_FROM_USER,
	IDX_TO_USER,
	IDX_FROM_DOMAIN,
	IDX_TO_DOMAIN,
	IDX_MAX
};


struct avp_galias
{
	str alias;
	struct avp_spec avp;
	struct avp_galias *next;
};

static struct avp_galias *galiases = 0;

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;

/* AVP flags */
int registered_avpflags_no = 0;
static char *registered_avpflags[MAX_AVPFLAG];

/* 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);

	/* 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) {
		SHM_MEM_ERROR;
		return -1;
	}
	*def_glist = 0;
	*crt_glist = def_glist;
	return 0;
}


/*
 * Select active AVP list based on the value of flags
 */
static avp_list_t *select_list(avp_flags_t flags)
{
	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) {
		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];
		}
	} else if(flags & AVP_CLASS_GLOBAL) {
		return *crt_glist;
	}

	return NULL;
}

inline static avp_id_t compute_ID(str *name)
{
	char *p;
	avp_id_t id;

	id = 0;
	for(p = name->s + name->len - 1; p >= name->s; p--)
		id ^= *p;
	return id;
}


avp_t *create_avp(avp_flags_t flags, avp_name_t name, avp_value_t val)
{
	avp_t *avp;
	str *s;
	struct str_num_data *sid;
	struct str_str_data *ssd;
	int len;

	/* compute the required mem size */
	len = sizeof(struct usr_avp);
	if(flags & AVP_NAME_STR) {
		if(name.s.s == 0 || name.s.len == 0) {
			LM_ERR("NULL or EMPTY NAME AVP!");
			goto error;
		}
		if(flags & AVP_VAL_STR) {
			len += sizeof(struct str_str_data) - sizeof(union usr_avp_data)
				   + name.s.len + 1 /* Terminating zero for regex search */
				   + val.s.len + 1; /* Value is zero terminated */
		} else {
			len += sizeof(struct str_num_data) - sizeof(union usr_avp_data)
				   + name.s.len + 1; /* Terminating zero for regex search */
		}
	} else {
		if(name.n == 0) {
			LM_ERR("0 ID AVP!");
			goto error;
		}
		if(flags & AVP_VAL_STR) {
			len += sizeof(str) - sizeof(union usr_avp_data) + val.s.len + 1;
		}
	}

	avp = (struct usr_avp *)shm_malloc(len);
	if(avp == 0) {
		SHM_MEM_ERROR;
		return 0;
	}

	avp->flags = flags;
	avp->id = (flags & AVP_NAME_STR) ? compute_ID(&name.s) : name.n;
	avp->next = NULL;

	switch(flags & (AVP_NAME_STR | AVP_VAL_STR)) {
		case 0:
			/* avp type ID, int value */
			avp->d.l = val.n;
			break;
		case AVP_NAME_STR:
			/* avp type str, int value */
			sid = (struct str_num_data *)&avp->d.data[0];
			sid->val = val.n;
			sid->name.len = name.s.len;
			sid->name.s = (char *)sid + sizeof(struct str_num_data);
			memcpy(sid->name.s, name.s.s, name.s.len);
			sid->name.s[name.s.len] = '\0'; /* Zero terminator */
			break;
		case AVP_VAL_STR:
			/* avp type ID, str value */
			s = (str *)&avp->d.data[0];
			s->len = val.s.len;
			s->s = (char *)s + sizeof(str);
			memcpy(s->s, val.s.s, s->len);
			s->s[s->len] = 0;
			break;
		case AVP_NAME_STR | AVP_VAL_STR:
			/* avp type str, str value */
			ssd = (struct str_str_data *)&avp->d.data[0];
			ssd->name.len = name.s.len;
			ssd->name.s = (char *)ssd + sizeof(struct str_str_data);
			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;
			ssd->val.s = ssd->name.s + ssd->name.len + 1;
			memcpy(ssd->val.s, val.s.s, val.s.len);
			ssd->val.s[ssd->val.len] = 0;
			break;
	}
	return avp;
error:
	return 0;
}

int add_avp_list(
		avp_list_t *list, avp_flags_t flags, avp_name_t name, avp_value_t val)
{
	avp_t *avp;

	assert(list != 0);

	if((avp = create_avp(flags, name, val))) {
		avp->next = *list;
		*list = avp;
		return 0;
	}

	return -1;
}


int add_avp(avp_flags_t flags, avp_name_t name, avp_value_t val)
{
	avp_flags_t avp_class;
	avp_list_t *list;

	/* Add avp to uri class if no class has been
	      * specified by the caller
	      */
	if((flags & AVP_CLASS_ALL) == 0)
		flags |= AVP_CLASS_URI;
	if((flags & AVP_TRACK_ALL) == 0)
		flags |= AVP_TRACK_FROM;
	if(!(list = select_list(flags)))
		return -1;

	if(flags & AVP_CLASS_URI)
		avp_class = AVP_CLASS_URI;
	else if(flags & AVP_CLASS_USER)
		avp_class = AVP_CLASS_USER;
	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);
}

int add_avp_before(
		avp_t *avp, avp_flags_t flags, avp_name_t name, avp_value_t 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))) {
		LM_ERR("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 */
inline str *get_avp_name(avp_t *avp)
{
	struct str_num_data *sid;
	struct str_str_data *ssd;

	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 */
			sid = (struct str_num_data *)&avp->d.data[0];
			return &sid->name;
		case AVP_NAME_STR | AVP_VAL_STR:
			/* avp type str, str value */
			ssd = (struct str_str_data *)&avp->d.data[0];
			return &ssd->name;
	}

	LM_ERR("unknown avp type (name&val) %d\n",
			avp->flags & (AVP_NAME_STR | AVP_VAL_STR));
	return 0;
}


inline void get_avp_val(avp_t *avp, avp_value_t *val)
{
	str *s;
	struct str_num_data *sid;
	struct str_str_data *ssd;

	if(avp == 0 || val == 0)
		return;

	switch(avp->flags & (AVP_NAME_STR | AVP_VAL_STR)) {
		case 0:
			/* avp type ID, int value */
			val->n = avp->d.l;
			break;
		case AVP_NAME_STR:
			/* avp type str, int value */
			sid = (struct str_num_data *)&avp->d.data[0];
			val->n = sid->val;
			break;
		case AVP_VAL_STR:
			/* avp type ID, str value */
			s = (str *)&avp->d.data[0];
			val->s = *s;
			break;
		case AVP_NAME_STR | AVP_VAL_STR:
			/* avp type str, str value */
			ssd = (struct str_str_data *)&avp->d.data[0];
			val->s = ssd->val;
			break;
	}
}


/* Return the current list of user attributes */
avp_list_t get_avp_list(avp_flags_t flags)
{
	avp_list_t *list;

	list = select_list(flags);
	return (list ? *list : NULL);
}


/*
 * Compare given id with id in avp, return true if they match
 */
static inline int match_by_id(avp_t *avp, avp_id_t id)
{
	if(avp->id == id && (avp->flags & AVP_NAME_STR) == 0) {
		return 1;
	}
	return 0;
}


/*
 * Compare given name with name in avp, return true if they are same
 */
static inline int match_by_name(avp_t *avp, avp_id_t id, str *name)
{
	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;
	}
	return 0;
}


/*
 * 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)
{
	regmatch_t pmatch;
	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 ? */
		return 0;
	if(!avp_name->s) /* AVP name validation */
		return 0;
	if(regexec(re, avp_name->s, 1, &pmatch, 0) == 0) { /* re match ? */
		return 1;
	}
	return 0;
}


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)
{
	avp_t *ret;
	static struct search_state st;
	avp_list_t *list;

	if(ident.name.s.s == 0 && ident.name.s.len == 0) {
		LM_ERR("0 ID or NULL NAME AVP!");
		return 0;
	}

	switch(ident.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(!state) {
		memset(&st, 0, sizeof(struct search_state));
		state = &st;
	}

	if((ident.flags & AVP_CLASS_ALL) == 0) {
		/* The caller did not specify any class to search in, so enable
		      * all of them by default
		      */
		ident.flags |= AVP_CLASS_ALL;

		if((ident.flags & AVP_TRACK_ALL) == 0) {
			/* The caller did not specify even the track to search in, so search
		     * in the track_from
		     */
			ident.flags |= AVP_TRACK_FROM;
		}
	}

	if(!(list = select_list(ident.flags)))
		return NULL;

	state->flags = ident.flags;
	state->avp = *list;
	state->name = ident.name;

	if(ident.flags & AVP_NAME_STR) {
		state->id = compute_ID(&ident.name.s);
	}

	ret = search_next_avp(state, val);

	/* Make sure that search next avp stays in the same class as the first
	 * avp found */
	if(ret) {
		state->flags =
				(ident.flags & ~AVP_CLASS_ALL) | (ret->flags & AVP_CLASS_ALL);
	}
	return ret;
}

avp_t *search_next_avp(struct search_state *s, avp_value_t *val)
{
	int matched;
	avp_t *avp;
	avp_list_t *list;

	if(s == 0) {
		LM_ERR("Invalid parameter value\n");
		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) {
		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) {
				matched = match_by_name(s->avp, s->id, &s->name.s);
			} 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;
			}
		}

		if(s->flags & AVP_CLASS_URI) {
			s->flags &= ~AVP_CLASS_URI;
			list = select_list(s->flags);
		} else if(s->flags & AVP_CLASS_USER) {
			s->flags &= ~AVP_CLASS_USER;
			list = select_list(s->flags);
		} else if(s->flags & AVP_CLASS_DOMAIN) {
			s->flags &= ~AVP_CLASS_DOMAIN;
			list = select_list(s->flags);
		} else {
			s->flags &= ~AVP_CLASS_GLOBAL;
			return 0;
		}
		if(!list)
			return 0;
		s->avp = *list;
	}

	return 0;
}

int search_reverse(
		avp_t *cur, struct search_state *st, avp_index_t index, avp_list_t *ret)
{
	avp_index_t 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(
		avp_flags_t flags, avp_name_t name, avp_value_t *val, avp_index_t 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;
}

/********* free functions ********/
void destroy_avp(avp_t *avp_del)
{
	int i;
	avp_t *avp, *avp_prev;

	for(i = 0; i < IDX_MAX; i++) {
		for(avp_prev = 0, avp = *crt_list[i]; avp;
				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;
			}
		}
	}

	for(avp_prev = 0, avp = **crt_glist; avp; avp_prev = avp, avp = avp->next) {
		if(avp == avp_del) {
			if(avp_prev) {
				avp_prev->next = avp->next;
			} else {
				**crt_glist = avp->next;
			}
			shm_free(avp);
			return;
		}
	}
}


void destroy_avp_list_unsafe(avp_list_t *list)
{
	avp_t *avp, *foo;

	avp = *list;
	while(avp) {
		foo = avp;
		avp = avp->next;
		shm_free_unsafe(foo);
	}
	*list = 0;
}


inline void destroy_avp_list(avp_list_t *list)
{
	avp_t *avp, *foo;

	LM_DBG("destroying list %p\n", *list);
	avp = *list;
	while(avp) {
		foo = avp;
		avp = avp->next;
		shm_free(foo);
	}
	*list = 0;
}

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;
}

void reset_avps(void)
{
	int i;
	for(i = 0; i < IDX_MAX; i++) {
		crt_list[i] = &def_list[i];
		destroy_avp_list(crt_list[i]);
	}
}


avp_list_t *set_avp_list(avp_flags_t flags, avp_list_t *list)
{
	avp_list_t *prev;

	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) {
		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;
	}

	return prev;
}


/********* global aliases functions ********/

static inline int check_avp_galias(
		str *alias, avp_flags_t type, avp_name_t 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) {
				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))
					return -1;
			} else {
				if(avp_name.n == ga->avp.name.n)
					return -1;
			}
		}
	}
	return 0;
}


int add_avp_galias(str *alias, avp_flags_t type, avp_name_t avp_name)
{
	struct avp_galias *ga;

	if((type & AVP_NAME_STR && (!avp_name.s.s || !avp_name.s.len)) || !alias
			|| !alias->s || !alias->len) {
		LM_ERR("null params received\n");
		goto error;
	}

	if(check_avp_galias(alias, type, avp_name) != 0) {
		LM_ERR("duplicate alias/avp entry\n");
		goto error;
	}

	ga = (struct avp_galias *)pkg_malloc(sizeof(struct avp_galias));
	if(ga == 0) {
		PKG_MEM_ERROR;
		goto error;
	}

	ga->alias.s = (char *)pkg_malloc(alias->len + 1);
	if(ga->alias.s == 0) {
		PKG_MEM_ERROR;
		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) {
		ga->avp.name.s.s = (char *)pkg_malloc(avp_name.s.len + 1);
		if(ga->avp.name.s.s == 0) {
			PKG_MEM_ERROR;
			goto error2;
		}
		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;
		LM_DBG("registering <%s> for avp name <%s>\n", ga->alias.s,
				ga->avp.name.s.s);
	} else {
		ga->avp.name.n = avp_name.n;
		LM_DBG("registering <%s> for avp id <%ld>\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;
}


int lookup_avp_galias(str *alias, avp_flags_t *type, avp_name_t *avp_name)
{
	struct avp_galias *ga;

	for(ga = galiases; ga; ga = ga->next)
		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;
		}

	return -1;
}


/* parsing functions */
#define ERR_IF_CONTAINS(name, chr)                                      \
	if(memchr(name->s, chr, name->len)) {                               \
		LM_ERR("Unexpected control character '%c' in AVP name\n", chr); \
		goto error;                                                     \
	}

int parse_avp_name(
		str *name, avp_flags_t *type, avp_name_t *avp_name, int *index)
{
	int ret;
	avp_ident_t attr;

	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;
}


/** 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)
 *          \<track\> = 'f' | 't'
 *                   (from or to)
 *          \<class\> = 'r' | 'u' | 'd' | 'g'
 *                    (uri, user, domain or global)
 *          \<index\> = \<number\> | '-' \<number\> | ''
 *                    (the avp index, if missing it means AVP_INDEX_ALL, but
 *                     its 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
 */
int parse_avp_ident(str *name, avp_ident_t *attr)
{
	unsigned int id;
	char c;
	char *p;
	str s;

	if(name == 0 || name->s == 0 || name->len == 0) {
		LM_ERR("NULL name or name->s or name->len\n");
		goto error;
	}

	attr->index = 0;
	LM_DBG("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];
		name->s += 2;
		name->len -= 2;
		if(name->len == 0)
			goto error;
		switch(c) {
			case 's':
			case 'S':
				attr->flags = AVP_NAME_STR;
				attr->name.s = *name;
				break;
			case 'i':
			case 'I':
				attr->flags = 0;
				if(str2int(name, &id) != 0) {
					LM_ERR("invalid ID <%.*s> - not a number\n", name->len,
							name->s);
					goto error;
				}
				attr->name.n = (int)id;
				break;
			default:
				LM_ERR("unsupported type [%c]\n", c);
				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 {
			LM_ERR("AVP unknown class prefix '%.*s'\n", name->len, name->s);
			goto error;
		}
		if(name->len == 0) {
			LM_ERR("AVP name not specified after the prefix separator\n");
			goto error;
		}
		switch(id) {
			case 'f':
				attr->flags = AVP_TRACK_FROM;
				break;
			case 't':
				attr->flags = AVP_TRACK_TO;
				break;
			case 0x6672: /* 'fr' */
				attr->flags = AVP_TRACK_FROM | AVP_CLASS_URI;
				break;
			case 0x7472: /* 'tr' */
				attr->flags = AVP_TRACK_TO | AVP_CLASS_URI;
				break;
			case 0x6675: /* 'fu' */
				attr->flags = AVP_TRACK_FROM | AVP_CLASS_USER;
				break;
			case 0x7475: /* 'tu' */
				attr->flags = AVP_TRACK_TO | AVP_CLASS_USER;
				break;
			case 0x6664: /* 'fd' */
				attr->flags = AVP_TRACK_FROM | AVP_CLASS_DOMAIN;
				break;
			case 0x7464: /* 'td' */
				attr->flags = AVP_TRACK_TO | AVP_CLASS_DOMAIN;
				break;
			case 'g':
				attr->flags = AVP_TRACK_ALL | AVP_CLASS_GLOBAL;
				break;
			default:
				if(id < 1 << 8)
					LM_ERR("AVP unknown class prefix '%c'\n", id);
				else
					LM_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) {
				LM_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) {
				attr->flags |= AVP_INDEX_ALL;
			} else {
				if(s.s[0] == '-') {
					attr->flags |= AVP_INDEX_BACKWARD;
					s.s++;
					s.len--;
				} else {
					attr->flags |= AVP_INDEX_FORWARD;
				}
				if((str2int(&s, &id) != 0) || (id == 0)) {
					LM_ERR("Invalid AVP index '%.*s'\n", s.len, s.s);
					goto error;
				}
				attr->index = id;
			}
			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] == '/')) {
			attr->name.re = pkg_malloc(sizeof(regex_t));
			if(!attr->name.re) {
				PKG_MEM_ERROR;
				goto error;
			}
			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);
				attr->name.re = 0;
				name->s[name->len - 1] = '/';
				goto error;
			}
			name->s[name->len - 1] = '/';
			attr->flags |= AVP_NAME_RE;
		} else {
			ERR_IF_CONTAINS(name, '/');
			attr->flags |= AVP_NAME_STR;
			attr->name.s = *name;
		}
	} else {
		/*default is string name*/
		attr->flags = AVP_NAME_STR;
		attr->name.s = *name;
	}

	return 0;
error:
	return -1;
}

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 {
			regfree(attr->name.re);
			pkg_free(attr->name.re);
		}
	}
}

int km_parse_avp_spec(str *name, avp_flags_t *type, avp_name_t *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) {
		/* might be kamailio avp alias or ser avp name style */
		if(lookup_avp_galias(name, type, avp_name) == 0)
			return 0; /* found */
	}
	return parse_avp_name(name, type, avp_name, &index);
}


int parse_avp_spec(
		str *name, avp_flags_t *type, avp_name_t *avp_name, int *index)
{
	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) {
			LM_ERR("empty alias\n");
			return -1;
		}
		alias.s = name->s + 1;
		alias.len = name->len - 1;
		return lookup_avp_galias(&alias, type, avp_name);
	} else {
		return parse_avp_name(name, type, avp_name, index);
	}
}

void free_avp_name(avp_flags_t *type, avp_name_t *avp_name)
{
	if((*type & AVP_NAME_RE) && (avp_name->re)) {
		regfree(avp_name->re);
		pkg_free(avp_name->re);
		avp_name->re = 0;
	}
}

int add_avp_galias_str(char *alias_definition)
{
	avp_name_t avp_name;
	char *s;
	str name;
	str alias;
	avp_flags_t type;
	int index;

	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;
		}

		if(parse_avp_name(&name, &type, &avp_name, &index) != 0) {
			LM_ERR("<%.*s> not a valid AVP name\n", name.len, name.s);
			goto error;
		}

		if(add_avp_galias(&alias, type, avp_name) != 0) {
			LM_ERR("add global alias failed\n");
			goto error;
		}
	} /*end while*/

	return 0;
parse_error:
	LM_ERR("parse error in <%s> around pos %ld\n", alias_definition,
			(long)(s - alias_definition));
error:
	return -1;
}


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;
}


void delete_avp(avp_flags_t flags, avp_name_t name)
{
	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);
	}
}

/* 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) {
		if(registered_avpflags_no >= MAX_AVPFLAG) {
			LM_ERR("cannot register new avp flag ('%s'), max.number of flags "
				   "(%d) reached\n",
					name, MAX_AVPFLAG);
			return -1;
		}
		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;
}
