modules/avp/avp.c
71ce6e45
 /*
  * $Id$
  *
74ddc27b
  * Copyright (C) 2004 FhG Fokus
71ce6e45
  *
  * 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.
  *
6f43dd16
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
9e1ff448
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
71ce6e45
  */
 
ea34d2d1
 /*
  *  2005-03-28  avp_destination & xlset_destination - handle both nameaddr & uri texts (mma)
  *  2005-12-22  merge changes from private branch (mma)
  *  2006-01-03  avp_body merged (mma)
  */
6f43dd16
 
74ddc27b
 #include <string.h>
ea34d2d1
 #include <stdlib.h>
5b366894
 #ifdef EXTRA_DEBUG
 #include <assert.h>
 #endif
71ce6e45
 #include "../../sr_module.h"
 #include "../../error.h"
ea34d2d1
 #include "../../lump_struct.h"
 #include "../../data_lump.h"
 #include "../../data_lump_rpl.h"
71ce6e45
 #include "../../usr_avp.h"
74ddc27b
 #include "../../mem/mem.h"
 #include "../../parser/parse_uri.h"
 #include "../../parser/msg_parser.h"
ea34d2d1
 #include "../../parser/parse_nameaddr.h"
74ddc27b
 #include "../../ut.h"
 #include "../../dset.h"
ea34d2d1
 #include "../../trim.h"
74ddc27b
 #include "../../str.h"
 #include "../../dprint.h"
85f50b6d
 #include "../../re.h"
3ec029e8
 #include "../../action.h"
1a8280bd
 
ea34d2d1
 #include "../../parser/parse_hname2.h"
ceb2d50f
 #include "../xprint/xp_lib.h"
ea34d2d1
 #define NO_SCRIPT -1
6f43dd16
 
74ddc27b
 MODULE_VERSION
1a8280bd
 
74ddc27b
 /* name of attributed used to store flags with command flags2attr */
ea34d2d1
 #define HDR_ID 0
 #define HDR_STR 1
 
 #define PARAM_DELIM '/'
 #define VAL_TYPE_INT (1<<0)
 #define VAL_TYPE_STR (1<<1)
 
 struct hdr_name {
 	enum {hdrId, hdrStr} kind;
 	union {
 		int n;
 		str s;
 	} name;
 	char field_delimiter;
 	char array_delimiter;
 	int val_types;
 };
71ce6e45
 
ea34d2d1
 static int xlbuf_size=256;
 static char* xlbuf=NULL;
bc8d2fa5
 static str* xl_nul=NULL;
 static xl_print_log_f* xl_print=NULL;
 static xl_parse_format_f* xl_parse=NULL;
 static xl_elog_free_all_f* xl_free=NULL;
 static xl_get_nulstr_f* xl_getnul=NULL;
71ce6e45
 
5b366894
 static int mod_init();
37c73c6d
 static int set_iattr(struct sip_msg* msg, char* p1, char* p2);
 static int set_sattr(struct sip_msg* msg, char* p1, char* p2);
 static int print_attr(struct sip_msg* msg, char* p1, char* p2);
 static int del_attr(struct sip_msg* msg, char* p1, char* p2);
 static int subst_attr(struct sip_msg* msg, char* p1, char* p2);
 static int flags2attr(struct sip_msg* msg, char* p1, char* p2);
 static int attr2uri(struct sip_msg* msg, char* p1, char* p2);
 static int dump_attrs(struct sip_msg* msg, char* p1, char* p2);
 static int attr_equals(struct sip_msg* msg, char* p1, char* p2);
 static int attr_exists(struct sip_msg* msg, char* p1, char* p2);
 static int attr_equals_xl(struct sip_msg* msg, char* p1, char* p2);
 static int xlset_attr(struct sip_msg* msg, char* p1, char* p2);
bc8d2fa5
 static int xlfix_attr(struct sip_msg* msg, char* p1, char* p2);
37c73c6d
 static int insert_req(struct sip_msg* msg, char* p1, char* p2);
 static int append_req(struct sip_msg* msg, char* p1, char* p2);
 static int replace_req(struct sip_msg* msg, char* p1, char* p2);
 static int append_reply(struct sip_msg* msg, char* p1, char* p2);
 static int attr_destination(struct sip_msg* msg, char* p1, char* p2);
 static int xlset_destination(struct sip_msg* msg, char* p1, char* p2);
 static int attr_hdr_body2attrs(struct sip_msg* msg, char* p1, char* p2);
 static int attr_hdr_body2attrs2(struct sip_msg* msg, char* p1, char* p2);
 static int del_attrs(struct sip_msg* msg, char* p1, char* p2);
 
 static int set_iattr_fixup(void**, int);
 static int avpid_fixup(void**, int);
 static int subst_attr_fixup(void**, int);
 static int fixup_part(void**, int);
 static int fixup_xl_1(void**, int);
 static int fixup_attr_1_xl_2(void**, int);
 static int fixup_str_1_attr_2(void**, int);
bc8d2fa5
 static int xlfix_attr_fixup(void** param, int param_no);
37c73c6d
 static int attr_hdr_body2attrs_fixup(void**, int);
 static int attr_hdr_body2attrs2_fixup(void**, int);
 static int avpgroup_fixup(void**, int);
71ce6e45
 
 /*
  * Exported functions
  */
 static cmd_export_t cmds[] = {
37c73c6d
     {"set_iattr",         set_iattr,            2, set_iattr_fixup,            REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE},
     {"set_sattr",         set_sattr,            2, fixup_var_str_12,           REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE},
     {"set_attr",          set_sattr,            2, fixup_var_str_12,           REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE},
     {"print_attr",        print_attr,           1, avpid_fixup,                REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE},
     {"del_attr",          del_attr,             1, avpid_fixup,                REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE}, 
     {"del_attrs",         del_attrs,            1, avpgroup_fixup,             REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE}, 
     {"subst_attr",        subst_attr,           2, subst_attr_fixup,           REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE},
     {"flags2attr",        flags2attr,           1, avpid_fixup,                REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE},
     {"attr2uri",          attr2uri,             1, fixup_part,                 REQUEST_ROUTE | FAILURE_ROUTE},
     {"attr2uri",          attr2uri,             2, fixup_part,                 REQUEST_ROUTE | FAILURE_ROUTE},
     {"dump_attrs",	  dump_attrs,           0, 0,                          REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE},
     {"dump_attrs",	  dump_attrs,           1, avpgroup_fixup,             REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE},
     {"attr_equals",       attr_equals,          2, fixup_var_str_12,           REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE},
     {"attr_exists",       attr_exists,          1 , fixup_var_str_1,           REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE},
     {"attr_equals_xl",    attr_equals_xl,       2, fixup_attr_1_xl_2,          REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE},
     {"xlset_attr",        xlset_attr,           2, fixup_attr_1_xl_2,          REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE},
bc8d2fa5
     {"xlfix_attr",        xlfix_attr,           1, xlfix_attr_fixup,           REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE}, 
37c73c6d
     {"insert_attr_hf",    insert_req,           2, fixup_str_1_attr_2,         REQUEST_ROUTE | FAILURE_ROUTE},
     {"insert_attr_hf",    insert_req,           1, fixup_str_1_attr_2,         REQUEST_ROUTE | FAILURE_ROUTE},
     {"append_attr_hf",    append_req,           2, fixup_str_1_attr_2,         REQUEST_ROUTE | FAILURE_ROUTE},
     {"append_attr_hf",    append_req,           1, fixup_str_1_attr_2,         REQUEST_ROUTE | FAILURE_ROUTE},
     {"replace_attr_hf",   replace_req,          2, fixup_str_1_attr_2,         REQUEST_ROUTE | FAILURE_ROUTE},
     {"replace_attr_hf",   replace_req,          1, fixup_str_1_attr_2,         REQUEST_ROUTE | FAILURE_ROUTE},
     {"attr_to_reply",     append_reply,         2, fixup_str_1_attr_2,         REQUEST_ROUTE | FAILURE_ROUTE},
     {"attr_to_reply",     append_reply,         1, fixup_str_1_attr_2,         REQUEST_ROUTE | FAILURE_ROUTE},
0aa3897f
     {"attr_destination",  attr_destination,     1, avpid_fixup,                REQUEST_ROUTE | FAILURE_ROUTE}, 
37c73c6d
     {"xlset_destination", xlset_destination,    1, fixup_xl_1,                 REQUEST_ROUTE},
     {"hdr_body2attrs",    attr_hdr_body2attrs,  2, attr_hdr_body2attrs_fixup,  REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE},
     {"hdr_body2attrs2",   attr_hdr_body2attrs2, 2, attr_hdr_body2attrs2_fixup, REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE},
     {0, 0, 0, 0, 0}
71ce6e45
 };
 
 
 /*
  * Exported parameters
  */
 static param_export_t params[] = {
37c73c6d
     {"xlbuf_size", PARAM_INT, &xlbuf_size},
     {0, 0, 0}
71ce6e45
 };
 
 
 struct module_exports exports = {
37c73c6d
     "avp",
     cmds,       /* Exported commands */
5b366894
     0,          /* RPC */
37c73c6d
     params,     /* Exported parameters */
5b366894
     mod_init,          /* module initialization function */
37c73c6d
     0,          /* response function*/
     0,          /* destroy function */
     0,          /* oncancel function */
     0           /* per-child init function */
71ce6e45
 };
 
ea34d2d1
 
37c73c6d
 static int set_iattr_fixup(void** param, int param_no)
 {
     if (param_no == 1) {
 	return fixup_var_str_12(param, param_no);
     } else {
 	return fixup_var_int_12(param, param_no);
     }
ea34d2d1
 }
 
71ce6e45
 
37c73c6d
 static int get_avp_id(avp_ident_t* id, fparam_t* p, struct sip_msg* msg)
71ce6e45
 {
37c73c6d
     str str_id;
     avp_t* avp;
     avp_value_t val;
     int ret;
 
     switch(p->type) {
     case FPARAM_AVP:
 	avp = search_avp(p->v.avp, &val, 0);
 	if (!avp) {
 	    DBG("get_avp_id: AVP %s does not exist\n", p->orig);
 	    return -1;
 	}
8deff8f9
 	if ((avp->flags & AVP_VAL_STR) == 0) {
37c73c6d
 	    DBG("get_avp_id: Not a string AVP\n");
 	    return -1;
 	}
 	str_id = val.s;
 	break;
1a8280bd
 
37c73c6d
     case FPARAM_SELECT:
 	ret = run_select(&str_id, p->v.select, msg);
 	if (ret < 0 || ret > 0) return -1;
 	break;
55646d7b
 
2feef7be
 	case FPARAM_STR:
37c73c6d
 	str_id = p->v.str;
 	break;
71ce6e45
 
37c73c6d
     default:
 	ERR("Invalid parameter type in get_avp_id\n");
 	return -1;
     }
1a8280bd
 
37c73c6d
     return parse_avp_ident(&str_id, id);
71ce6e45
 }
 
42bd3df8
 
37c73c6d
 static int set_iattr(struct sip_msg* msg, char* p1, char* p2)
71ce6e45
 {
37c73c6d
     avp_ident_t avpid;
     int_str value;
71ce6e45
 
37c73c6d
     if (get_avp_id(&avpid, (fparam_t*)p1, msg) < 0) {
 	return -1;
     }
     
     if (get_int_fparam(&value.n, msg, (fparam_t*)p2) < 0) {
 	ERR("Error while obtaining attribute value from '%s'\n", ((fparam_t*)p1)->orig);
 	return -1;
     }
     
     if (add_avp(avpid.flags | AVP_NAME_STR, avpid.name, value) != 0) {
 	ERR("add_avp failed\n");
 	return -1;
     }
     return 1;
6f43dd16
 }
71ce6e45
 
 
37c73c6d
 static int set_sattr(struct sip_msg* msg, char* p1, char* p2)
71ce6e45
 {
37c73c6d
     avp_ident_t avpid;
     int_str value;
     
     if (get_avp_id(&avpid, (fparam_t*)p1, msg) < 0) {
 	return -1;
     }
71ce6e45
 
37c73c6d
     if (get_str_fparam(&value.s, msg, (fparam_t*)p2) < 0) {
 	ERR("Error while obtaining attribute value from '%s'\n", ((fparam_t*)p2)->orig);
 	return -1;
     }
     
     if (add_avp(avpid.flags | AVP_NAME_STR | AVP_VAL_STR, avpid.name, value) != 0) {
 	ERR("add_avp failed\n");
 	return -1;
     }
     
     return 1;
74ddc27b
 }
71ce6e45
 
 
37c73c6d
 static int avpid_fixup(void** param, int param_no)
74ddc27b
 {
37c73c6d
     if (param_no == 1) {
8d347d8b
 		if (fix_param(FPARAM_AVP, param) == 0) return 0;
 		ERR("Invalid AVP identifier: '%s'\n", (char*)*param);
 		return -1;
37c73c6d
     }
     return 0;
1a8280bd
 }
 
 
37c73c6d
 static int print_attr(struct sip_msg* msg, char* p1, char* p2)
74ddc27b
 {
37c73c6d
     fparam_t* fp;
     int_str value;
     avp_t *avp;
 
     fp = (fparam_t*)p1;
     
     avp = search_avp(fp->v.avp, &value, NULL);
     if (avp == 0) {
 	LOG(L_INFO, "AVP '%s' not found\n", fp->orig);
 	return -1;
     }
     
     if (avp->flags & AVP_VAL_STR) {
 	LOG(L_INFO, "AVP: '%s'='%.*s'\n", 
 	    fp->orig, value.s.len, ZSW(value.s.s));
     } else {
 	LOG(L_INFO, "AVP: '%s'=%d\n", fp->orig, value.n);
     }
     return 1;
1a8280bd
 }
71ce6e45
 
 
37c73c6d
 static int del_attr(struct sip_msg* msg, char* p1, char* p2)
71ce6e45
 {
37c73c6d
     fparam_t* fp;
     avp_t* avp;
     struct search_state st;	
     
     fp = (fparam_t*)p1;
     
     avp = search_avp(fp->v.avp, 0, &st);
     while (avp) {
 	destroy_avp(avp);
 	avp = search_next_avp(&st, 0);
     }
     return 1;
71ce6e45
 }
 
 
37c73c6d
 static int del_attrs(struct sip_msg* msg, char* p1, char* p2)
71ce6e45
 {
e2f3847c
     return (reset_avp_list((unsigned long)p1) == 0) ? 1 : -1;
37c73c6d
 }
 			
71ce6e45
 
37c73c6d
 static int subst_attr_fixup(void** param, int param_no)
 {
     if (param_no == 1) {
8d347d8b
 		return avpid_fixup(param, 1);
37c73c6d
     }
     if (param_no == 2) {
8d347d8b
 		if (fix_param(FPARAM_SUBST, param) != 0) return -1;
37c73c6d
     }
     return 0;
 }
71ce6e45
 
74ddc27b
 
37c73c6d
 static int subst_attr(struct sip_msg* msg, char* p1, char* p2)
 {
     avp_t* avp;
     avp_value_t val;
     str *res = NULL;
     int count;
     avp_ident_t* name = &((fparam_t*)p1)->v.avp;
 
     if ((avp = search_avp(*name, &val, NULL))) {
 	if (avp->flags & AVP_VAL_STR) {
 	    res = subst_str(val.s.s, msg, ((fparam_t*)p2)->v.subst, &count);
 	    if (res == NULL) {
 		ERR("avp_subst: error while running subst\n");
 		goto error;
 	    }
3ec029e8
 
37c73c6d
 	    DBG("avp_subst: %d, result %.*s\n", count, res->len, ZSW(res->s));
 	    val.s = *res;
 	    
 	    if (add_avp_before(avp, name->flags | AVP_VAL_STR, name->name, val)) {
 		ERR("avp_subst: error while adding new AVP\n");
 		goto error;
 	    }
 	    
 	    destroy_avp(avp);
 	    return 1;
 	} else {
 	    ERR("avp_subst: AVP has numeric value\n");
 	    goto error;
71ce6e45
 	}
37c73c6d
     } else {
 	ERR("avp_subst: AVP[%.*s] index %d, flags %x not found\n", 
 	    name->name.s.len, name->name.s.s,
 	    name->index, name->flags);
 	goto error;
     }
 
  error:
     if (res) pkg_free(res);
     return -1;
74ddc27b
 }
71ce6e45
 
 
37c73c6d
 static int flags2attr(struct sip_msg* msg, char* p1, char* p2)
6f43dd16
 {
37c73c6d
     avp_ident_t* id;
1f78880b
     int_str value;
37c73c6d
     
     value.n = msg->flags;
     
     id = &((fparam_t*)p1)->v.avp;
 
     if (add_avp(id->flags, id->name, value) != 0) {
 	ERR("add_avp failed\n");
 	return -1;
     }
     
     return 1;
 }
71ce6e45
 
55646d7b
 
37c73c6d
 static int fixup_part(void** param, int param_no) 
 {
     int i;
     fparam_t* fp;
     
     static struct {
8d347d8b
 		char* s;
 		int i;
37c73c6d
     } fixup_parse[] = {
8d347d8b
 		{"", SET_URI_T},
 		{"prefix", PREFIX_T},
 		{"uri", SET_URI_T},
 		{"username", SET_USER_T},
 		{"user", SET_USER_T},
 		{"usernamepassword", SET_USERPASS_T},
 		{"userpass", SET_USERPASS_T},
 		{"domain", SET_HOST_T},
 		{"host", SET_HOST_T},
 		{"domainport", SET_HOSTPORT_T},
 		{"hostport", SET_HOSTPORT_T},
 		{"port", SET_PORT_T},
 		{"strip", STRIP_T},
 		{"strip_tail", STRIP_TAIL_T},
 		{0, 0}
37c73c6d
     };
     
     if (param_no == 1) {
8d347d8b
 		return avpid_fixup(param, 1);
37c73c6d
     } else if (param_no == 2) {
8d347d8b
 		/* Create fparam structure */
 		if (fix_param(FPARAM_STRING, param) != 0) return -1;
 		
 		/* We will parse the string now and store the value
 		 * as int
 		 */
 		fp = (fparam_t*)*param;
 		fp->type = FPARAM_INT;
 		
 		for(i = 0; fixup_parse[i].s; i++) {
 			if (!strcasecmp(fp->orig, fixup_parse[i].s)) {
 				fp->v.i = fixup_parse[i].i;
 				return 1;
 			}
 		}
 		
 		ERR("Invalid parameter value: '%s'\n", fp->orig);
 		return -1;
37c73c6d
     }
8deff8f9
     return 0;
71ce6e45
 }
 
55646d7b
 
37c73c6d
 static int attr2uri(struct sip_msg* msg, char* p1, char* p2)
 {
     int_str value;
     avp_t* avp_entry;
     struct action act;
4955c477
 	struct run_act_ctx ra_ctx;
37c73c6d
     int pnr;
4c7f3bab
     unsigned int u;
37c73c6d
     
     if (p2) {
340b1434
 		pnr = ((fparam_t*)p2)->v.i;
37c73c6d
     } else {
340b1434
 		pnr = SET_URI_T;
37c73c6d
     }
     
     avp_entry = search_avp(((fparam_t*)p1)->v.avp, &value, NULL);
     if (avp_entry == 0) {
340b1434
 		ERR("attr2uri: AVP '%s' not found\n", ((fparam_t*)p1)->orig);
 		return -1;
37c73c6d
     }
     
     memset(&act, 0, sizeof(act));
340b1434
 	
4c7f3bab
     if ((pnr == STRIP_T) || (pnr == STRIP_TAIL_T)) {
340b1434
 		/* we need integer value for these actions */
4c7f3bab
         if (avp_entry->flags & AVP_VAL_STR) {
340b1434
 			if (str2int(&value.s, &u)) {
 				ERR("not an integer value: %.*s\n",
 					value.s.len, value.s.s);
 				return -1;
 			}
 			act.val[0].u.number = u;
 		} else {
 			act.val[0].u.number = value.n;
 		}
 		act.val[0].type = NUMBER_ST;
4c7f3bab
     } else {
340b1434
 		/* we need string value */
 		if ((avp_entry->flags & AVP_VAL_STR) == 0) {
 			act.val[0].u.string = int2str(value.n, NULL);
 		} else {
 			act.val[0].u.string = value.s.s;
 		}
 		act.val[0].type = STRING_ST;
4c7f3bab
     }
37c73c6d
     act.type = pnr;
4c7f3bab
     init_run_actions_ctx(&ra_ctx);
4955c477
     if (do_action(&ra_ctx, &act, msg) < 0) {
340b1434
 		ERR("failed to change ruri part.\n");
 		return -1;
37c73c6d
     }
     return 1;
ea34d2d1
 }
 
 
 /*
  * sends avp list to log in readable form
  *
  */
 static void dump_avp_reverse(avp_t* avp)
 {
37c73c6d
     str* name;
     int_str val;
     
     if (avp) {
 	     /* AVPs are added to front of the list, reverse by recursion */
 	dump_avp_reverse(avp->next);
 	
 	name=get_avp_name(avp);
 	get_avp_val(avp, &val);
 	switch(avp->flags&(AVP_NAME_STR|AVP_VAL_STR)) {
 	case 0:
 		 /* avp type ID, int value */
 	    LOG(L_INFO,"AVP[%d]=%d\n", avp->id, val.n);
 	    break;
 
 	case AVP_NAME_STR:
 		 /* avp type str, int value */
 	    name=get_avp_name(avp);
 	    LOG(L_INFO,"AVP[\"%.*s\"]=%d\n", name->len, name->s, val.n);
 	    break;
 
 	case AVP_VAL_STR:
 		 /* avp type ID, str value */
 	    LOG(L_INFO,"AVP[%d]=\"%.*s\"\n", avp->id, val.s.len, val.s.s);
 	    break;
 
 	case AVP_NAME_STR|AVP_VAL_STR:
 		 /* avp type str, str value */
 	    name=get_avp_name(avp);
 	    LOG(L_INFO,"AVP[\"%.*s\"]=\"%.*s\"\n", name->len, name->s, val.s.len, val.s.s);
 	    break;
ea34d2d1
 	}
37c73c6d
     }
ea34d2d1
 }
 
37c73c6d
 
 static int dump_attrs(struct sip_msg* m, char* x, char* y)
ea34d2d1
 {
37c73c6d
     avp_list_t avp_list;
     unsigned long flags;
 
     if (x) {
 	flags = (unsigned long)x;
     } else {
 	flags = AVP_CLASS_ALL | AVP_TRACK_ALL;
     }
 
6f43dd16
 
37c73c6d
     if (flags & AVP_CLASS_GLOBAL) {
 	avp_list = get_avp_list(AVP_CLASS_GLOBAL);
ee109ca9
 	INFO("class=GLOBAL\n");
 	if (!avp_list) {
37c73c6d
 	    LOG(L_INFO,"INFO: No AVP present\n");
ee109ca9
 	} else {
37c73c6d
 	    dump_avp_reverse(avp_list);
ee109ca9
 	}
37c73c6d
     }
 
     if (flags & AVP_CLASS_DOMAIN && flags & AVP_TRACK_FROM) {
 	avp_list = get_avp_list(AVP_CLASS_DOMAIN | AVP_TRACK_FROM);
ee109ca9
 	INFO("track=FROM class=DOMAIN\n");
 	if (!avp_list) {
37c73c6d
 	    LOG(L_INFO,"INFO: No AVP present\n");
ee109ca9
 	} else {
37c73c6d
 	    dump_avp_reverse(avp_list);
ee109ca9
 	}
37c73c6d
     }
 
     if (flags & AVP_CLASS_DOMAIN && flags & AVP_TRACK_TO) {
 	avp_list = get_avp_list(AVP_CLASS_DOMAIN | AVP_TRACK_TO);
ee109ca9
 	INFO("track=TO class=DOMAIN\n");
 	if (!avp_list) {
37c73c6d
 	    LOG(L_INFO,"INFO: No AVP present\n");
ee109ca9
 	} else {
37c73c6d
 	    dump_avp_reverse(avp_list);
ee109ca9
 	}
37c73c6d
     }
 
     if (flags & AVP_CLASS_USER && flags & AVP_TRACK_FROM) {
 	avp_list = get_avp_list(AVP_CLASS_USER | AVP_TRACK_FROM);
55646d7b
 	INFO("track=FROM class=USER\n");
 	if (!avp_list) {
37c73c6d
 	    LOG(L_INFO,"INFO: No AVP present\n");
55646d7b
 	} else {
37c73c6d
 	    dump_avp_reverse(avp_list);
55646d7b
 	}
37c73c6d
     }
 
     if (flags & AVP_CLASS_USER && flags & AVP_TRACK_TO) {
 	avp_list = get_avp_list(AVP_CLASS_USER | AVP_TRACK_TO);
55646d7b
 	INFO("track=TO class=USER\n");
ea34d2d1
 	if (!avp_list) {
37c73c6d
 	    LOG(L_INFO,"INFO: No AVP present\n");
ea34d2d1
 	} else {
37c73c6d
 	    dump_avp_reverse(avp_list);
ea34d2d1
 	}
37c73c6d
     }
 
     if (flags & AVP_CLASS_URI && flags & AVP_TRACK_FROM) {
 	avp_list = get_avp_list(AVP_CLASS_URI | AVP_TRACK_FROM);
6c59bd68
 	INFO("track=FROM class=URI\n");
 	if (!avp_list) {
37c73c6d
 	    LOG(L_INFO,"INFO: No AVP present\n");
6c59bd68
 	} else {
37c73c6d
 	    dump_avp_reverse(avp_list);
6c59bd68
 	}
37c73c6d
     }	
 
     if (flags & AVP_CLASS_URI && flags & AVP_TRACK_TO) {
 	avp_list = get_avp_list(AVP_CLASS_URI | AVP_TRACK_TO);
6c59bd68
 	INFO("track=TO class=URI\n");
 	if (!avp_list) {
37c73c6d
 	    LOG(L_INFO,"INFO: No AVP present\n");
6c59bd68
 	} else {
37c73c6d
 	    dump_avp_reverse(avp_list);
6c59bd68
 	}
37c73c6d
     }
     return 1;
ea34d2d1
 }
 
37c73c6d
 
 /*
  *  returns 1 if msg contains an AVP with the given name and value,
  *  returns -1 otherwise
  */
 static int attr_equals(struct sip_msg* msg, char* p1, char* p2)
ea34d2d1
 {
37c73c6d
     avp_ident_t avpid;
     int_str value, avp_value;
     avp_t* avp;
     struct search_state st;
 
     if (get_avp_id(&avpid, (fparam_t*)p1, msg) < 0) {
55646d7b
 	return -1;
37c73c6d
     }
ea34d2d1
 
37c73c6d
     if (p2 && get_str_fparam(&value.s, msg, (fparam_t*)p2) < 0) {
 	ERR("Error while obtaining attribute value from '%s'\n", ((fparam_t*)p2)->orig);
 	return -1;
     }
 
     avp = search_avp(avpid, &avp_value, &st);
     if (avp == 0) return -1;
 
     if (!p2) return 1;
     
     while (avp != 0) {
 	if (avp->flags & AVP_VAL_STR) {
 	    if ((avp_value.s.len == value.s.len) &&
 		!memcmp(avp_value.s.s, value.s.s, avp_value.s.len)) {
 		return 1;
 	    }
ea34d2d1
 	} else {
37c73c6d
 	    if (avp_value.n == str2s(value.s.s, value.s.len, 0)) {
 		return 1;
 	    }
ea34d2d1
 	}
37c73c6d
 	avp = search_next_avp(&st, &avp_value);
     }
     
     return -1;
 }
ea34d2d1
 
 
37c73c6d
 static int attr_exists(struct sip_msg* msg, char* p1, char* p2)
 {
1f78880b
 	return attr_equals(msg, p1, NULL);
37c73c6d
 }
ea34d2d1
 
6f43dd16
 
37c73c6d
 static int xl_printstr(struct sip_msg* msg, xl_elog_t* format, char** res, int* res_len)
 {
     int len;
     
     if (!format || !res) {
 	LOG(L_ERR, "xl_printstr: Called with null format or res\n");
 	return -1;
     }
     
     if (!xlbuf) {
 	xlbuf = pkg_malloc((xlbuf_size+1)*sizeof(char));
 	if (!xlbuf) {
 	    LOG(L_CRIT, "xl_printstr: No memory left for format buffer\n");
 	    return -1;
 	}
     }
     
     len = xlbuf_size;
     if (xl_print(msg, format, xlbuf, &len)<0) {
 	LOG(L_ERR, "xl_printstr: Error while formating result\n");
 	return -1;
     }
     
     if ((xl_nul) && (xl_nul->len == len) && !strncmp(xl_nul->s, xlbuf, len)) {
 	return 0;
     }
ea34d2d1
 
37c73c6d
     *res = xlbuf;
     if (res_len) {
 	*res_len=len;
     }
     return len;
ea34d2d1
 }
 
37c73c6d
 
 static int attr_equals_xl(struct sip_msg* msg, char* p1, char* format)
ea34d2d1
 {
37c73c6d
     avp_ident_t* avpid;
     avp_value_t avp_val;
     struct search_state st;
     str xl_val;
     avp_t* avp;
     
     avpid = &((fparam_t*)p1)->v.avp;
 
     if (xl_printstr(msg, (xl_elog_t*) format, &xl_val.s, &xl_val.len) > 0) {
 	for (avp = search_avp(*avpid, &avp_val, &st); avp; avp = search_next_avp(&st, &avp_val)) {
 	    if (avp->flags & AVP_VAL_STR) {
 		if ((avp_val.s.len == xl_val.len) &&
 		    !memcmp(avp_val.s.s, xl_val.s, avp_val.s.len)) return 1;
 	    } else {
 		if (avp_val.n == str2s(xl_val.s, xl_val.len, 0)) return 1;
 	    }
ea34d2d1
 	}
37c73c6d
 	return -1;
     }
     
     ERR("avp_equals_xl:Error while expanding xl_format\n");
     return -1;
ea34d2d1
 }
 
bc8d2fa5
 /* get the pointer to the xl lib functions */
 static int get_xl_functions(void)
ea34d2d1
 {
37c73c6d
     if (!xl_print) {
 	xl_print=(xl_print_log_f*)find_export("xprint", NO_SCRIPT, 0);
 	
 	if (!xl_print) {
6909bcae
 	    LOG(L_CRIT,"ERROR: cannot find \"xprint\", is module xprint loaded?\n");
37c73c6d
 	    return -1;
 	}
     }
     
     if (!xl_parse) {
 	xl_parse=(xl_parse_format_f*)find_export("xparse", NO_SCRIPT, 0);
 	
 	if (!xl_parse) {
6909bcae
 	    LOG(L_CRIT,"ERROR: cannot find \"xparse\", is module xprint loaded?\n");
37c73c6d
 	    return -1;
 	}
     }
bc8d2fa5
 
     if (!xl_free) {
 	xl_free=(xl_elog_free_all_f*)find_export("xfree", NO_SCRIPT, 0);
 	
 	if (!xl_free) {
6909bcae
 	    LOG(L_CRIT,"ERROR: cannot find \"xfree\", is module xprint loaded?\n");
bc8d2fa5
 	    return -1;
 	}
     }
 
37c73c6d
     if (!xl_nul) {
 	xl_getnul=(xl_get_nulstr_f*)find_export("xnulstr", NO_SCRIPT, 0);
 	if (xl_getnul) {
 	    xl_nul=xl_getnul();
 	}
 	
 	if (!xl_nul){
6909bcae
 	    LOG(L_CRIT,"ERROR: cannot find \"xnulstr\", is module xprint loaded?\n");
37c73c6d
 	    return -1;
 	} else {
6909bcae
 	    LOG(L_INFO,"INFO: xprint null is \"%.*s\"\n", xl_nul->len, xl_nul->s);
37c73c6d
 	}
ea34d2d1
 	
37c73c6d
     }
bc8d2fa5
 
     return 0;
 }
 
 /*
  * Convert xl format string to xl format description
  */
 static int fixup_xl_1(void** param, int param_no)
 {
     xl_elog_t* model;
 
     if (get_xl_functions()) return -1;
 
37c73c6d
     if (param_no == 1) {
 	if(*param) {
 	    if(xl_parse((char*)(*param), &model)<0) {
 		LOG(L_ERR, "ERROR: xl_fixup: wrong format[%s]\n", (char*)(*param));
 		return E_UNSPEC;
 	    }
 	    
 	    *param = (void*)model;
 	    return 0;
ea34d2d1
 	} else {
37c73c6d
 	    LOG(L_ERR, "ERROR: xl_fixup: null format\n");
 	    return E_UNSPEC;
ea34d2d1
 	}
37c73c6d
     }
     
     return 0;
ea34d2d1
 }
 
37c73c6d
 static int fixup_attr_1_xl_2(void** param, int param_no)
ea34d2d1
 {
37c73c6d
     if (param_no == 1) {
 	return avpid_fixup(param, 1);
     } else  if (param_no == 2) {
 	return fixup_xl_1(param, 1);
     }
     return 0;
 }
6f43dd16
 
ea34d2d1
 
37c73c6d
 static int xlset_attr(struct sip_msg* msg, char* p1, char* format)
 {
     avp_ident_t* avpid;
     avp_value_t val;
     
     avpid = &((fparam_t*)p1)->v.avp;
 
     if (xl_printstr(msg, (xl_elog_t*)format, &val.s.s, &val.s.len) > 0) {
 	if (add_avp(avpid->flags | AVP_VAL_STR, avpid->name, val)) {
 	    ERR("xlset_attr:Error adding new AVP\n");
 	    return -1;
ea34d2d1
 	}
37c73c6d
 	return 1;
     }
     
     ERR("xlset_attr:Error while expanding xl_format\n");
     return -1;
ea34d2d1
 }
 
bc8d2fa5
 /*
  * get the xl function pointers and fix up the AVP parameter
  */
 static int xlfix_attr_fixup(void** param, int param_no)
 {
     if (get_xl_functions()) return -1;
 
     if (param_no == 1)
 	return avpid_fixup(param, 1);
 
     return 0;
 }
 
 /* fixes an attribute containing xl formatted string to pure string runtime */
 static int xlfix_attr(struct sip_msg* msg, char* p1, char* p2)
 {
     avp_t* avp;
     avp_ident_t* avpid;
     avp_value_t val;
     xl_elog_t* format=NULL;
     int ret=-1;
     
     avpid = &((fparam_t*)p1)->v.avp;
 
     /* search the AVP */
     avp = search_avp(*avpid, &val, 0);
     if (!avp) {
 	DBG("xlfix_attr: AVP does not exist\n");
 	goto error;
     }
     if ((avp->flags & AVP_VAL_STR) == 0) {
 	DBG("xlfix_attr: Not a string AVP\n");
 	goto error;
     }
 
     /* parse the xl syntax -- AVP values are always
     zero-terminated */
     if (xl_parse(val.s.s, &format)<0) {
 	LOG(L_ERR, "ERROR: xlfix_attr: wrong format[%s]\n", val.s.s);
 	goto error;
     }
 
     if (xl_printstr(msg, format, &val.s.s, &val.s.len) > 0) {
 	/* we must delete and re-add the AVP again */
 	destroy_avp(avp);
 	if (add_avp(avpid->flags | AVP_VAL_STR, avpid->name, val)) {
 	    ERR("xlfix_attr:Error adding new AVP\n");
 	    goto error;
 	}
 	/* everything went OK */
 	ret = 1;
     }
 
 error:
     /* free the parsed xl expression */
     if (format) xl_free(format);
 
     return ret;
 }
 
37c73c6d
 
 static int request_hf_helper(struct sip_msg* msg, str* hf, avp_ident_t* ident, struct lump* anchor, struct search_state* st, int front, int reverse, int reply)
ea34d2d1
 {
37c73c6d
     struct lump* new_anchor;
     static struct search_state state;
     avp_t* avp;
     char* s;
     str fin_val;
     int len, ret;
     int_str val;
     struct hdr_field* pos, *found = NULL;
     
     if (!anchor && !reply) {
 	
 	if (parse_headers(msg, HDR_EOH_F, 0) == -1) {
 	    LOG(L_ERR, "ERROR: request_hf_helper: Error while parsing message\n");
 	    return -1;
 	}
 	
 	pos = msg->headers;
 	while (pos && (pos->type != HDR_EOH_T)) {
 	    if ((hf->len == pos->name.len)
 		&& (!strncasecmp(hf->s, pos->name.s, pos->name.len))) {
 		found = pos;
 		if (front) {
 		    break;
 		}
 	    }
 	    pos = pos->next;
 	}
ea34d2d1
 	
37c73c6d
 	if (found) {
 	    if (front) {
 		len = found->name.s - msg->buf;
 	    } else {
 		len = found->name.s + found->len - msg->buf;
 	    }
ea34d2d1
 	} else {
37c73c6d
 	    len = msg->unparsed - msg->buf;
ea34d2d1
 	}
37c73c6d
 	
 	new_anchor = anchor_lump(msg, len, 0, 0);
 	if (new_anchor == 0) {
 	    LOG(L_ERR, "ERROR: request_hf_helper: Can't get anchor\n");
 	    return -1;
 	}
     } else {
 	new_anchor = anchor;
     }
     
     if (!st) {
 	st = &state;
 	avp = search_avp(*ident, NULL, st);
 	ret = -1;
     } else {
 	avp = search_next_avp(st, NULL);
 	ret = 1;
     }
     
     if (avp) {
 	if (reverse && (request_hf_helper(msg, hf, ident, new_anchor, st, front, reverse, reply) == -1)) {
 	    return -1;
 	}
 	
 	get_avp_val(avp, &val);
 	if (avp->flags & AVP_VAL_STR) {
 	    fin_val = val.s;
 	} else {
 	    fin_val.s = int2str(val.n, &fin_val.len);
 	}
 	
 	len = hf->len + 2 + fin_val.len + 2;
 	s = (char*)pkg_malloc(len);
 	if (!s) {
 	    LOG(L_ERR, "ERROR: request_hf_helper: No memory left for data lump\n");
 	    return -1;
 	}
 	
 	memcpy(s, hf->s, hf->len);
 	memcpy(s + hf->len, ": ", 2 );
 	memcpy(s + hf->len+2, fin_val.s, fin_val.len );
 	memcpy(s + hf->len + 2 + fin_val.len, CRLF, CRLF_LEN);
 	
 	if (reply) {
 	    if (add_lump_rpl( msg, s, len, LUMP_RPL_HDR | LUMP_RPL_NODUP) == 0) {
 		LOG(L_ERR, "ERROR: request_hf_helper: Can't insert RPL lump\n");
 		pkg_free(s);
 		return -1;
 	    }
 	} else {
 	    if ((front && (insert_new_lump_before(new_anchor, s, len, 0) == 0))
 		|| (!front && (insert_new_lump_after(new_anchor, s, len, 0) == 0))) {
 		LOG(L_ERR, "ERROR: request_hf_helper: Can't insert lump\n");
 		pkg_free(s);
 		return -1;
 	    }
 	}
 	if (!reverse && (request_hf_helper(msg, hf, ident, new_anchor, st, front, reverse, reply) == -1)) {
 	    return -1;
 	}
 	return 1;
     };
     
 	 /* in case of topmost call (st==NULL) return error */
 	 /* otherwise it's OK, no more AVPs found */
     return ret; 
ea34d2d1
 }
 
37c73c6d
 
 static int fixup_str_1_attr_2(void** param, int param_no)
ea34d2d1
 {
37c73c6d
     if (param_no == 1) {
 	return fixup_var_str_12(param, 1);
     } else if (param_no == 2) {
 	return avpid_fixup(param, 1);
     }
     return 0;
 }
ea34d2d1
 
37c73c6d
 
 static int insert_req(struct sip_msg* msg, char* p1, char* p2)
 {
     avp_ident_t ident, *avp;
     str hf;
     
     if (get_str_fparam(&hf, msg, (fparam_t*)p1) < 0) {
 	ERR("Error while obtaining attribute value from '%s'\n", ((fparam_t*)p1)->orig);
 	return -1;
     }
 
     if (p2) {
 	avp = &((fparam_t*)p2)->v.avp;
     } else {
 	ident.name.s = hf;
 	ident.flags = AVP_NAME_STR;
 	ident.index = 0;
 	avp = &ident;
     }
     return (request_hf_helper(msg, &hf, avp, NULL, NULL, 1, 0, 0));
ea34d2d1
 }
 
37c73c6d
 
 static int append_req(struct sip_msg* msg, char* p1, char* p2)
ea34d2d1
 {
37c73c6d
     avp_ident_t ident, *avp;
     str hf;
6f43dd16
 
37c73c6d
     if (get_str_fparam(&hf, msg, (fparam_t*)p1) < 0) {
 	ERR("Error while obtaining attribute value from '%s'\n", ((fparam_t*)p1)->orig);
 	return -1;
     }
     
     if (p2) {
 	avp = &((fparam_t*)p2)->v.avp;
     } else {
 	ident.name.s = hf;
 	ident.flags = AVP_NAME_STR;
 	ident.index = 0;
 	avp = &ident;
     }
     return (request_hf_helper(msg, &hf, avp, NULL, NULL, 0, 1, 0));
ea34d2d1
 }
 
37c73c6d
 
 static int replace_req(struct sip_msg* msg, char* p1, char* p2)
ea34d2d1
 {
37c73c6d
     struct hdr_field* pos;
     str hf;
     
     if (get_str_fparam(&hf, msg, (fparam_t*)p1) < 0) {
 	ERR("Error while obtaining attribute value from '%s'\n", ((fparam_t*)p1)->orig);
 	return -1;
     }
     
     if (parse_headers(msg, HDR_EOH_F, 0) == -1) {
 	LOG(L_ERR, "ERROR: replace_req: Error while parsing message\n");
 	return -1;
     }
     
     pos = msg->headers;
     while (pos && (pos->type != HDR_EOH_T)) {
 	if (hf.len == pos->name.len
 	    && !strncasecmp(hf.s, pos->name.s, pos->name.len)) {
 	    if (del_lump(msg, pos->name.s - msg->buf, pos->len, 0) == 0) {
 		LOG(L_ERR,"ERROR: Can't insert del lump\n");
 		return -1;
 	    }
ea34d2d1
 	}
37c73c6d
 	pos = pos->next;
     }
     return append_req(msg, p1, p2);
 }
 
 
 static int append_reply(struct sip_msg* msg, char* p1, char* p2)
 {
     avp_ident_t ident, *avp;
     str hf;
     
     if (get_str_fparam(&hf, msg, (fparam_t*)p1) < 0) {
 	ERR("Error while obtaining attribute value from '%s'\n", ((fparam_t*)p1)->orig);
ea34d2d1
 	return -1;
37c73c6d
     }
     
     if (p2) {
 	avp = &((fparam_t*)p2)->v.avp;
     } else {
 	ident.name.s = hf;
 	ident.flags = AVP_NAME_STR;
 	ident.index = 0;
 	avp = &ident;
     }
     return (request_hf_helper(msg, &hf, avp, NULL, NULL, 0, 1, 1));
ea34d2d1
 }
 
37c73c6d
 
 static int set_destination(struct sip_msg* msg, str* dest)
85f50b6d
 {
37c73c6d
     name_addr_t nameaddr;
     
     if (!parse_nameaddr(dest, &nameaddr)) {
 	return set_dst_uri(msg, &nameaddr.uri);
     } else {
 	     /* it is just URI, pass it through */
73210ef8
 	return set_dst_uri(msg, dest);
37c73c6d
     }
 }
 
 
 static int attr_destination(struct sip_msg* msg, char* p1, char* p2)
 {
     avp_t* avp;
     avp_value_t val;
     
     if ((avp = search_avp(((fparam_t*)p1)->v.avp, &val, NULL))) {
 	if (avp->flags & AVP_VAL_STR) {
 	    if (set_destination(msg, &val.s)) {
 		LOG(L_ERR, "ERROR: avp_destination: Can't set dst uri\n");
 		return -1;
 	    };
039b182e
 		/* dst_uri changed, so it makes sense to re-use the current uri for
 			forking */
 		ruri_mark_new(); /* re-use uri for serial forking */
37c73c6d
 	    return 1;
85f50b6d
 	} else {
37c73c6d
 	    ERR("avp_destination:AVP has numeric value\n");
 	    return -1;
85f50b6d
 	}
37c73c6d
     }
     return -1;
85f50b6d
 }
5418bc0e
 
37c73c6d
 
 static int xlset_destination(struct sip_msg* msg, char* format, char* p2)
5418bc0e
 {
37c73c6d
     str val;
     
     if (xl_printstr(msg, (xl_elog_t*) format, &val.s, &val.len) > 0) {
 	DBG("Setting dest to: '%.*s'\n", val.len, val.s);
79f3c101
 	if (set_destination(msg, &val) == 0) {
37c73c6d
 	    return 1;
5418bc0e
 	}
37c73c6d
     }
     
     return -1;
5418bc0e
 }
ea34d2d1
 
37c73c6d
 
 static int attr_hdr_body2attrs(struct sip_msg* m, char* header_, char* prefix_)
 {
     char name_buf[50];
     str *prefix = (str*) prefix_;
     struct hdr_name *header = (void*) header_;
     struct hdr_field *hf;
     str s, name, val;
     int_str name2, val2;
     int val_type, arr;
     if (header->kind == HDR_STR) {
 	if (parse_headers(m, HDR_EOH_F, 0) == -1) {
1f78880b
 	    LOG(L_ERR, "ERROR: attr_hdr_body2attrs: Error while parsing message\n");
37c73c6d
 	    return -1;
ea34d2d1
 	}
37c73c6d
 	
 	for (hf=m->headers; hf; hf=hf->next) {
 	    if ( (header->name.s.len == hf->name.len)
 		 && (!strncasecmp(header->name.s.s, hf->name.s, hf->name.len)) ) {
 		break;
 	    }
ea34d2d1
 	}
37c73c6d
     }
     else {
 	if (parse_headers(m, header->name.n, 0) == -1) {
1f78880b
 	    LOG(L_ERR, "ERROR: attr_hdr_body2attrs: Error while parsing message\n");
37c73c6d
 	    return -1;
 	}
 	switch (header->name.n) {
 		 //	HDR_xxx:
 	default:
 	    hf = NULL;
 	    break;
 	}
     }
     if (!hf || !hf->body.len)
 	return 1;
     
 	 // parse body of hf
     s = hf->body;
     name_buf[0] = '\0';
     while (s.len) {
 	trim_leading(&s);
 	name.s = s.s;
 	while ( s.len &&
 		( (s.s[0] >= 'a' && s.s[0] <= 'z') ||
 		  (s.s[0] >= 'A' && s.s[0] <= 'Z') ||
 		  (s.s[0] >= '0' && s.s[0] <= '9') ||
 		  s.s[0] == '_' || s.s[0] == '-'
 		  ) ) {
 	    s.s++;
 	    s.len--;
 	}
 	if (s.s == name.s)
 	    break;
 	name.len = s.s - name.s;
 	trim_leading(&s);
 	if (!s.len)
 	    break;
 	if (s.s[0] == '=') {
 	    s.s++;
 	    s.len--;
 	    arr = -1;
 	    
 	    while (s.len) {
ea34d2d1
 		trim_leading(&s);
37c73c6d
 		val_type = 0;
ea34d2d1
 		if (!s.len)
37c73c6d
 		    break;
 		if (s.s[0] == '"') {
 		    s.s++;
 		    s.len--;
 		    val.s = s.s;
 		    
 		    s.s = q_memchr(s.s, '\"', s.len);
 		    if (!s.s)
ea34d2d1
 			break;
37c73c6d
 		    val.len = s.s - val.s;
 		    val_type = AVP_VAL_STR;
 		    s.s++;
 		    s.len -= s.s - val.s;
 		}
 		else {
 		    int r;
 		    val.s = s.s;
 		    if (s.s[0] == '+' || s.s[0] == '-') {
ea34d2d1
 			s.s++;
 			s.len--;
37c73c6d
 		    }
 		    val2.n = 0; r = 0;
 		    while (s.len) {
 			if (s.s[0] == header->field_delimiter || (header->array_delimiter && header->array_delimiter == s.s[0]))
 			    goto token_end;
 			switch (s.s[0]) {
 			case ' ':
 			case '\t':
 			case '\n':
 			case '\r':
 			    goto token_end;
 			}
 			if (!val_type && s.s[0] >= '0' && s.s[0]<= '9') {
 			    r++;
 			    val2.n *= 10;
 			    val2.n += s.s[0] - '0';
 				 // overflow detection ???
 			}
 			else {
 			    val_type = AVP_VAL_STR;
 			}
ea34d2d1
 			s.s++;
 			s.len--;
37c73c6d
 		    }
 		token_end:
 		    if (r == 0) val_type = AVP_VAL_STR;
 		    if (!val_type && val.s[0] == '-') {
 			val2.n = -val2.n;
 		    }
 		    val.len = s.s - val.s;
ea34d2d1
 		}
37c73c6d
 		trim_leading(&s);
 		if (arr >= 0 || (s.len && header->array_delimiter && header->array_delimiter == s.s[0])) {
 		    arr++;
 		    if (arr == 100)
 			LOG(L_ERR, "ERROR: avp index out of limit\n");
ea34d2d1
 		}
37c73c6d
 		if (val.len && arr < 100) {
 		    if (prefix != NULL || arr >= 0) {
 			if ((prefix?prefix->len:0)+name.len+1+((arr>=0)?3/*#99*/:0) > sizeof(name_buf)) {
 			    if (arr <= 0)
 				LOG(L_ERR, "ERROR: avp name too long\n");
 			    goto cont;
ea34d2d1
 			}
37c73c6d
 			name2.s.len = 0;
 			name2.s.s = name_buf;
 			if (prefix != NULL) {
 			    if (name_buf[0] == '\0') {
 				memcpy(&name_buf[0], prefix->s, prefix->len);
 			    }
 			    name2.s.len += prefix->len;
ea34d2d1
 			}
37c73c6d
 			if (arr <= 0) {
 			    memcpy(&name_buf[name2.s.len], name.s, name.len);
ea34d2d1
 			}
37c73c6d
 			name2.s.len += name.len;
 			if (arr >= 0) {
 			    name_buf[name2.s.len] = '#';
 			    name2.s.len++;
 			    if (arr >= 10) {
 				name_buf[name2.s.len] = '0'+ (arr / 10);
 				name2.s.len++;
 			    }
 			    name_buf[name2.s.len] = '0'+ (arr % 10);
 			    name2.s.len++;
ea34d2d1
 			}
37c73c6d
 		    }
 		    else {
 			name2.s.s = name.s;
 			name2.s.len = name.len;
 		    }
 		    if ( ((val_type & AVP_VAL_STR) && (header->val_types & VAL_TYPE_STR)) ||
 			 ((val_type & AVP_VAL_STR) == 0 && (header->val_types & VAL_TYPE_INT))  ) {
 			if (val_type) {
 			    val2.s.s = val.s;
 			    val2.s.len = val.len;
1f78880b
 			    DBG("DEBUG: attr_hdr_body2attrs: adding avp '%.*s', sval: '%.*s'\n", name2.s.len, (char*) name2.s.s, val.len, val.s);
37c73c6d
 			} else {
1f78880b
 			    DBG("DEBUG: attr_hdr_body2attrs: adding avp '%.*s', ival: '%d'\n", name2.s.len, (char*) name2.s.s, val2.n);
ea34d2d1
 			}
37c73c6d
 			if ( add_avp(AVP_NAME_STR | val_type, name2, val2)!=0) {
1f78880b
 			    LOG(L_ERR, "ERROR: attr_hdr_body2attrs: add_avp failed\n");
37c73c6d
 			    return 1;
ea34d2d1
 			}
37c73c6d
 		    }
ea34d2d1
 		}
37c73c6d
 	    cont:
 		if (s.len && header->array_delimiter && header->array_delimiter == s.s[0]) {
 		    s.s++;
 		    s.len--;
ea34d2d1
 		}
 		else {
37c73c6d
 		    break;
ea34d2d1
 		}
37c73c6d
 	    };
ea34d2d1
 	}
37c73c6d
 	if (s.len && s.s[0] == header->field_delimiter) {
 	    s.s++;
 	    s.len--;
ea34d2d1
 	}
37c73c6d
 	else {
 	    break;
74ddc27b
 	}
37c73c6d
     }
     return 1;
74ddc27b
 }
ea34d2d1
 
 
37c73c6d
 static int attr_hdr_body2attrs2(struct sip_msg* msg, char* header_, char* prefix_) 
ea34d2d1
 {
1f78880b
     return attr_hdr_body2attrs(msg, header_, prefix_);
37c73c6d
 }
6f43dd16
 
 
37c73c6d
 static int attr_hdr_body2attrs_fixup(void** param, int param_no) {
     char *c, *params;
     struct hdr_name *h;
     int n;
     str *s;
     if (param_no == 1) {
 	c = *param;
 	if (*c == '#') {
 	    c++;
 	    n = strtol(c, &params, 10);
 	    switch (*params) {
 	    case PARAM_DELIM:
 		break;
 	    case 0:
 		params = 0;
 		break;
 	    default:
1f78880b
 		LOG(L_ERR, "attr_hdr_body2attrs_fixup: bad AVP value\n");
37c73c6d
 		return E_CFG;
 	    }
 	    switch (n) {
 		//				case HDR_xxx:
 		//				case HDR_xxx:
 		//					break;
 	    default:
1f78880b
 		LOG(L_ERR, "attr_hdr_body2attrs_fixup: header name is not valid and supported HDR_xxx id '%s' resolved as %d\n", c, n);
37c73c6d
 		return E_CFG;
 	    }
 	    h = pkg_malloc(sizeof(*h));
 	    if (!h) {
1f78880b
 		LOG(L_ERR, "attr_hdr_body2attrs_fixup: out of memory\n");
37c73c6d
 		return E_OUT_OF_MEM;
 	    }
 	    
 	    h->kind = HDR_ID;
 	    h->name.n = n;
 	    pkg_free(*param);
 	    
ea34d2d1
 	}
37c73c6d
 	else {
 	    params = strchr(c, PARAM_DELIM);
 	    if (params)
 		n = params-c;
 	    else
 		n = strlen(c);
 	    if (n == 0) {
1f78880b
 		LOG(L_ERR, "attr_hdr_body2attrs_fixup: header name is empty\n");
37c73c6d
 		return E_CFG;
 	    }
 	    h = pkg_malloc(sizeof(*h)+n+1);
 	    if (!h) {
1f78880b
 		LOG(L_ERR, "attr_hdr_body2attrs_fixup: out of memory\n");
37c73c6d
 		return E_OUT_OF_MEM;
 	    }
 	    h->kind = HDR_STR;
 	    h->name.s.len = n;
 	    h->name.s.s = (char *) h + sizeof(*h);
 	    memcpy(h->name.s.s, c, n+1);
ea34d2d1
 	}
37c73c6d
 	if (params) {
 	    h->val_types = 0;
 	    while (*params) {
 		switch (*params) {
 		case 'i':
 		case 'I':
 		    h->val_types = VAL_TYPE_INT;
 		    break;
 		case 's':
 		case 'S':
 		    h->val_types = VAL_TYPE_STR;
 		    break;
 		case PARAM_DELIM:
 		    break;
 		default:
1f78880b
 		    LOG(L_ERR, "attr_hdr_body2attrs_fixup: bad field param modifier near '%s'\n", params);
37c73c6d
 		    return E_CFG;
 		}
 		params++;
 	    }
 	    if (!h->val_types) {
1f78880b
 		LOG(L_ERR, "attr_hdr_body2attrs_fixup: no field param modifier specified\n");
37c73c6d
 		return E_CFG;
 	    }
ea34d2d1
 	}
37c73c6d
 	else {
 	    h->val_types = VAL_TYPE_INT|VAL_TYPE_STR;
55646d7b
 	}
37c73c6d
 	pkg_free(*param);
 	h->field_delimiter = ',';
 	h->array_delimiter = '\0';
55646d7b
 	
37c73c6d
 	*param = h;
     }
     else if (param_no == 2) {
 	n = strlen(*param);
 	if (n == 0) {
 	    s = NULL;
55646d7b
 	}
37c73c6d
 	else {
 	    s = pkg_malloc(sizeof(*s)+n+1);
 	    if (!s) {
1f78880b
 		LOG(L_ERR, "attr_hdr_body2attrs_fixup: out of memory\n");
55646d7b
 		return E_OUT_OF_MEM;
37c73c6d
 	    }
 	    s->len = n;
 	    s->s = (char *) s + sizeof(*s);
 	    memcpy(s->s, *param, n+1);
55646d7b
 	}
37c73c6d
 	pkg_free(*param);
 	*param = s;
     }
     return 0;
55646d7b
 }
 
37c73c6d
 static int attr_hdr_body2attrs2_fixup(void** param, int param_no) 
ea34d2d1
 {
37c73c6d
     struct hdr_name *h;
1f78880b
     int res = attr_hdr_body2attrs_fixup(param, param_no);
37c73c6d
     if (res == 0 && param_no == 1) {
 	h = *param;
 	h->field_delimiter = ';';
 	h->array_delimiter = ',';
     }
     return res;
ea34d2d1
 }
85f50b6d
 
 
3ec029e8
 
37c73c6d
 static int avpgroup_fixup(void** param, int param_no)
 {
     unsigned long flags;
     char* s;
     
     if (param_no == 1) {
 	     /* Determine the track and class of attributes to be loaded */
 	s = (char*)*param;
 	flags = 0;
 	if (*s != '$' || (strlen(s) != 3 && strlen(s) != 2)) {
 	    ERR("Invalid parameter value, $xy expected\n");
 	    return -1;
3ec029e8
 	}
37c73c6d
 	switch((s[1] << 8) + s[2]) {
 	case 0x4655: /* $fu */
 	case 0x6675:
 	case 0x4675:
 	case 0x6655:
 	    flags = AVP_TRACK_FROM | AVP_CLASS_USER;
 	    break;
 	    
 	case 0x4652: /* $fr */
 	case 0x6672:
 	case 0x4672:
 	case 0x6652:
 	    flags = AVP_TRACK_FROM | AVP_CLASS_URI;
 	    break;
 	    
 	case 0x5455: /* $tu */
 	case 0x7475:
 	case 0x5475:
 	case 0x7455:
 	    flags = AVP_TRACK_TO | AVP_CLASS_USER;
 	    break;
 	    
 	case 0x5452: /* $tr */
 	case 0x7472:
 	case 0x5472:
 	case 0x7452:
 	    flags = AVP_TRACK_TO | AVP_CLASS_URI;
 	    break;
 
 	case 0x4644: /* $fd */
 	case 0x6664:
 	case 0x4664:
 	case 0x6644:
 	    flags = AVP_TRACK_FROM | AVP_CLASS_DOMAIN;
 	    break;
 
 	case 0x5444: /* $td */
 	case 0x7464:
 	case 0x5464:
 	case 0x7444:
 	    flags = AVP_TRACK_TO | AVP_CLASS_DOMAIN;
 	    break;
 
 	case 0x6700: /* $td */
 	case 0x4700:
 	    flags = AVP_CLASS_GLOBAL;
 	    break;
 	    
 	default:
 	    ERR("Invalid parameter value: '%s'\n", s);
 	    return -1;
3ec029e8
 	}
37c73c6d
 	
 	pkg_free(*param);
 	*param = (void*)flags;
1f78880b
 	return 1;
37c73c6d
     }
1f78880b
     return 0;
3ec029e8
 }
5b366894
 
 
 
 static int select_attr_fixup(str* res, select_t* s, struct sip_msg* msg)
 {
 	avp_ident_t *avp_ident;
 
 #define SEL_PARAM_IDX	1
 
 	if (! msg) { /* fixup call */
 		str attr_name;
 		
 		if (s->params[SEL_PARAM_IDX].type != SEL_PARAM_STR) {
 			ERR("attribute name expected.\n");
 			return -1;
 		}
 
 		attr_name = s->params[SEL_PARAM_IDX].v.s;
 		DEBUG("fix up for attribute '%.*s'\n", STR_FMT(&attr_name));
 
 		if (! (avp_ident = pkg_malloc(sizeof(avp_ident_t)))) {
91eadbed
 			ERR("out of mem; requested: %d.\n", (int)sizeof(avp_ident_t));
5b366894
 			return -1;
 		}
 		memset(avp_ident, 0, sizeof(avp_ident_t));
 
 		/* skip leading `$' */
 		if ((1 < attr_name.len) && (attr_name.s[0] == '$')) {
 			attr_name.len --;
 			attr_name.s ++;
 		}
 		if (parse_avp_ident(&attr_name, avp_ident) < 0) {
 			ERR("failed to parse attribute name: `%.*s'.\n", STR_FMT(&attr_name));
 			pkg_free(avp_ident);
 		}
 		s->params[SEL_PARAM_IDX].v.p = avp_ident;
 		s->params[SEL_PARAM_IDX].type = SEL_PARAM_PTR;
 	} else { /* run time call */
 		avp_t *ret;
 		avp_value_t val;
 
 #ifdef EXTRA_DEBUG
 		assert(s->params[SEL_PARAM_IDX].type == SEL_PARAM_PTR);
 #endif
 		avp_ident = s->params[SEL_PARAM_IDX].v.p;
 		ret = search_first_avp(avp_ident->flags, avp_ident->name, &val, NULL);
 		if (ret && ret->flags & AVP_VAL_STR)
 			*res = val.s;
 	}
 
 	return 0;
 
 #undef SEL_PARAM_IDX
 }
 
 SELECT_F(select_any_nameaddr)
 ABSTRACT_F(select_attr);
 
 select_row_t sel_declaration[] = {
 	{ NULL, SEL_PARAM_STR, STR_STATIC_INIT("avp"), select_attr, SEL_PARAM_EXPECTED},
 	{ NULL, SEL_PARAM_STR, STR_STATIC_INIT("attr"), select_attr, SEL_PARAM_EXPECTED},
 	{ NULL, SEL_PARAM_STR, STR_STATIC_INIT("attribute"), select_attr, SEL_PARAM_EXPECTED},
 	{ select_attr, SEL_PARAM_STR, STR_NULL, select_attr_fixup, FIXUP_CALL | CONSUME_NEXT_STR},
 
 	{ select_attr_fixup, SEL_PARAM_STR, STR_STATIC_INIT("nameaddr"), select_any_nameaddr, NESTED},
 
 	{ NULL, SEL_PARAM_INT, STR_NULL, NULL, 0}
 };
 
 static int mod_init()
 {
 	DBG("%s - initializing\n", exports.name);
 	return register_select_table(sel_declaration);
 }