src/modules/textopsx/textopsx.c
a4e0d017
 /**
  *
  * Copyright (C) 2001-2003 FhG Fokus
  *
b1e49b5a
  * This file is part of Kamailio, a free SIP server.
a4e0d017
  *
  * 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.
  *
  * 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
a4e0d017
  */
 
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
0c830402
 #include <fnmatch.h>
a4e0d017
 
cf83221d
 #include "../../core/sr_module.h"
 #include "../../core/dprint.h"
 #include "../../core/data_lump.h"
 #include "../../core/msg_translator.h"
 #include "../../core/tcp_options.h"
 #include "../../core/mod_fix.h"
 #include "../../core/parser/parse_hname2.h"
 #include "../../core/select.h"
 #include "../../core/select_buf.h"
ba64899e
 #include "../../core/kemi.h"
ba6119e3
 
a4e0d017
 
4945d6f7
 #include "api.h"
a4e0d017
 
 MODULE_VERSION
 
 static int msg_apply_changes_f(sip_msg_t *msg, char *str1, char *str2);
 
b1e49b5a
 static int change_reply_status_f(sip_msg_t *, char *, char *);
 static int change_reply_status_fixup(void **param, int param_no);
a4e0d017
 
7b95ddda
 static int change_reply_status_code_f(sip_msg_t *, char *, char *);
 
b1e49b5a
 static int w_keep_hf_f(sip_msg_t *, char *, char *);
0c830402
 
b1e49b5a
 static int w_fnmatch2_f(sip_msg_t *, char *, char *);
 static int w_fnmatch3_f(sip_msg_t *, char *, char *, char *);
 static int fixup_fnmatch(void **param, int param_no);
1b6d5372
 
b1e49b5a
 static int w_remove_body_f(struct sip_msg *, char *, char *);
ba6119e3
 
b1e49b5a
 static int incexc_hf_value_f(struct sip_msg *msg, char *, char *);
 static int include_hf_value_fixup(void **, int);
 static int exclude_hf_value_fixup(void **, int);
 static int hf_value_exists_fixup(void **, int);
ba6119e3
 
b1e49b5a
 static int insupddel_hf_value_f(struct sip_msg *msg, char *_hname, char *_val);
 static int append_hf_value_fixup(void **param, int param_no);
 static int insert_hf_value_fixup(void **param, int param_no);
 static int remove_hf_value_fixup(void **param, int param_no);
 static int assign_hf_value_fixup(void **param, int param_no);
 static int remove_hf_value2_fixup(void **param, int param_no);
 static int assign_hf_value2_fixup(void **param, int param_no);
ba6119e3
 
ba3ce508
 static int w_hf_iterator_start(sip_msg_t *msg, char *piname, char *p2);
 static int w_hf_iterator_next(sip_msg_t *msg, char *piname, char *p2);
24172c08
 static int w_hf_iterator_prev(sip_msg_t *msg, char *piname, char *p2);
ba3ce508
 static int w_hf_iterator_end(sip_msg_t *msg, char *piname, char *p2);
adc3bc18
 static int w_hf_iterator_rm(sip_msg_t *msg, char *piname, char *p2);
3f3fb6ad
 static int w_hf_iterator_append(sip_msg_t *msg, char *piname, char *phtext);
d65adfb7
 static int w_hf_iterator_insert(sip_msg_t *msg, char *piname, char *phtext);
ba3ce508
 
0a13b912
 static int w_bl_iterator_start(sip_msg_t *msg, char *piname, char *p2);
 static int w_bl_iterator_next(sip_msg_t *msg, char *piname, char *p2);
 static int w_bl_iterator_end(sip_msg_t *msg, char *piname, char *p2);
 static int w_bl_iterator_rm(sip_msg_t *msg, char *piname, char *p2);
 
4945d6f7
 static int bind_textopsx(textopsx_api_t *tob);
42fdf377
 
843c3bf6
 static int mod_init(void);
 
ba6119e3
 extern select_row_t sel_declaration[];
 
0a13b912
 static void hf_iterator_init(void);
ba3ce508
 static int pv_parse_hf_iterator_name(pv_spec_t *sp, str *in);
 static int pv_get_hf_iterator_hname(sip_msg_t *msg, pv_param_t *param,
 		pv_value_t *res);
 static int pv_get_hf_iterator_hbody(sip_msg_t *msg, pv_param_t *param,
 		pv_value_t *res);
 
0a13b912
 static void bl_iterator_init(void);
 static int pv_parse_bl_iterator_name(pv_spec_t *sp, str *in);
 static int pv_get_bl_iterator_value(sip_msg_t *msg, pv_param_t *param,
 		pv_value_t *res);
 
ba3ce508
 static pv_export_t mod_pvs[] = {
 	{ {"hfitname", sizeof("hfitname")-1}, PVT_OTHER, pv_get_hf_iterator_hname, 0,
 		pv_parse_hf_iterator_name, 0, 0, 0 },
7f4d7725
 	{ {"hfitbody", sizeof("hfitbody")-1}, PVT_OTHER, pv_get_hf_iterator_hbody, 0,
ba3ce508
 		pv_parse_hf_iterator_name, 0, 0, 0 },
0a13b912
 	{ {"blitval", sizeof("blitval")-1}, PVT_OTHER, pv_get_bl_iterator_value, 0,
 		pv_parse_bl_iterator_name, 0, 0, 0 },
ba3ce508
 	{ {0, 0}, 0, 0, 0, 0, 0, 0, 0 }
 };
 
a4e0d017
 /* cfg functions */
b1e49b5a
 /* clag-format off */
a4e0d017
 static cmd_export_t cmds[] = {
e41b385a
 	{"msg_apply_changes", (cmd_function)msg_apply_changes_f, 0, 0, 0,
 			REQUEST_ROUTE | ONREPLY_ROUTE},
 	{"change_reply_status", change_reply_status_f, 2,
 			change_reply_status_fixup, 0, ONREPLY_ROUTE},
7b95ddda
 	{"change_reply_status_code", change_reply_status_code_f, 1,
 			fixup_igp_null, 0, ONREPLY_ROUTE},
e41b385a
 	{"remove_body", (cmd_function)w_remove_body_f, 0, 0, 0, ANY_ROUTE},
 	{"keep_hf", (cmd_function)w_keep_hf_f, 0, fixup_regexp_null, 0, ANY_ROUTE},
 	{"keep_hf", (cmd_function)w_keep_hf_f, 1, fixup_regexp_null, 0, ANY_ROUTE},
 	{"fnmatch", (cmd_function)w_fnmatch2_f, 2, fixup_fnmatch, 0, ANY_ROUTE},
 	{"fnmatch", (cmd_function)w_fnmatch3_f, 3, fixup_fnmatch, 0, ANY_ROUTE},
 	{"append_hf_value", insupddel_hf_value_f, 2, append_hf_value_fixup, 0,
 			REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE | BRANCH_ROUTE},
 	{"insert_hf_value", insupddel_hf_value_f, 2, insert_hf_value_fixup, 0,
 			REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE | BRANCH_ROUTE},
 	{"remove_hf_value", insupddel_hf_value_f, 1, remove_hf_value_fixup, 0,
 			REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE | BRANCH_ROUTE},
 	{"assign_hf_value", insupddel_hf_value_f, 2, assign_hf_value_fixup, 0,
 			REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE | BRANCH_ROUTE},
 	{"remove_hf_value2", insupddel_hf_value_f, 1, remove_hf_value2_fixup, 0,
 			REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE | BRANCH_ROUTE},
 	{"assign_hf_value2", insupddel_hf_value_f, 2, assign_hf_value2_fixup, 0,
 			REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE | BRANCH_ROUTE},
 	{"include_hf_value", incexc_hf_value_f, 2, include_hf_value_fixup, 0,
 			REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE | BRANCH_ROUTE},
 	{"exclude_hf_value", incexc_hf_value_f, 2, exclude_hf_value_fixup, 0,
 			REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE | BRANCH_ROUTE},
 	{"hf_value_exists", incexc_hf_value_f, 2, hf_value_exists_fixup, 0,
 			REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE | BRANCH_ROUTE},
ba3ce508
 	{"hf_iterator_start", w_hf_iterator_start, 1, fixup_spve_null,
 			fixup_free_spve_null, ANY_ROUTE},
 	{"hf_iterator_next", w_hf_iterator_next, 1, fixup_spve_null,
 			fixup_free_spve_null, ANY_ROUTE},
24172c08
 	{"hf_iterator_prev", w_hf_iterator_prev, 1, fixup_spve_null,
 			fixup_free_spve_null, ANY_ROUTE},
ba3ce508
 	{"hf_iterator_end", w_hf_iterator_end, 1, fixup_spve_null,
 			fixup_free_spve_null, ANY_ROUTE},
adc3bc18
 	{"hf_iterator_rm", w_hf_iterator_rm, 1, fixup_spve_null,
 			fixup_free_spve_null, ANY_ROUTE},
3f3fb6ad
 	{"hf_iterator_append", w_hf_iterator_append, 2, fixup_spve_spve,
 			fixup_free_spve_spve, ANY_ROUTE},
d65adfb7
 	{"hf_iterator_insert", w_hf_iterator_insert, 2, fixup_spve_spve,
 			fixup_free_spve_spve, ANY_ROUTE},
0a13b912
 	{"bl_iterator_start", w_bl_iterator_start, 1, fixup_spve_null,
 			fixup_free_spve_null, ANY_ROUTE},
 	{"bl_iterator_next", w_bl_iterator_next, 1, fixup_spve_null,
 			fixup_free_spve_null, ANY_ROUTE},
 	{"bl_iterator_end", w_bl_iterator_end, 1, fixup_spve_null,
 			fixup_free_spve_null, ANY_ROUTE},
 	{"bl_iterator_rm", w_bl_iterator_rm, 1, fixup_spve_null,
 			fixup_free_spve_null, ANY_ROUTE},
e41b385a
 
 	{"bind_textopsx", (cmd_function)bind_textopsx, 1, 0, 0, ANY_ROUTE},
 
 	{0, 0, 0, 0, 0, 0}
 };
a4e0d017
 
 /* module exports structure */
b1e49b5a
 struct module_exports exports = {
e41b385a
 	"textopsx",		/* module name */
 	DEFAULT_DLFLAGS,	/* dlopen flags */
 	cmds,			/* exported cfg functions */
 	0,				/* exported cfg parameters */
 	0,				/* exported RPC methods */
ba3ce508
 	mod_pvs,		/* exported pseudo-variables */
e41b385a
 	0,				/* response handling function */
 	mod_init,		/* module init function */
 	0,				/* per-child init function */
 	0,				/* destroy function */
a4e0d017
 };
b1e49b5a
 /* clag-format on */
a4e0d017
 
 
843c3bf6
 /**
  * init module function
  */
 static int mod_init(void)
 {
 #ifdef USE_TCP
 	tcp_set_clone_rcvbuf(1);
 #endif
ba6119e3
 	register_select_table(sel_declaration);
ba3ce508
 	hf_iterator_init();
0a13b912
 	bl_iterator_init();
ba3ce508
 
843c3bf6
 	return 0;
 }
 
a4e0d017
 /**
  *
  */
0a626a25
 static int ki_msg_update_buffer(sip_msg_t *msg, str *obuf)
a4e0d017
 {
0a626a25
 	if(obuf==NULL || obuf->s==NULL || obuf->len<=0) {
 		LM_ERR("invalid buffer parameter\n");
a4e0d017
 		return -1;
 	}
 
0a626a25
 	if(obuf->len >= BUF_SIZE) {
 		LM_ERR("new buffer is too large (%d)\n", obuf->len);
a4e0d017
 		return -1;
 	}
 
6fadac0f
 	return sip_msg_update_buffer(msg, obuf);
a4e0d017
 }
 
0a626a25
 /**
  *
  */
 static int ki_msg_set_buffer(sip_msg_t *msg, str *obuf)
 {
 	if(msg->first_line.type != SIP_REPLY && get_route_type() != REQUEST_ROUTE) {
 		LM_ERR("invalid usage - not in request route or a reply\n");
 		return -1;
 	}
 
 	return ki_msg_update_buffer(msg, obuf);
 }
 
 /**
  *
  */
 static int ki_msg_apply_changes(sip_msg_t *msg)
 {
6fadac0f
 	return sip_msg_apply_changes(msg);
0a626a25
 }
 
ba64899e
 /**
  *
  */
 static int msg_apply_changes_f(sip_msg_t *msg, char *str1, char *str2)
 {
7261efbc
 	return sip_msg_apply_changes(msg);
ba64899e
 }
a4e0d017
 
 /**
  *
  */
b1e49b5a
 static int change_reply_status_fixup(void **param, int param_no)
a4e0d017
 {
b1e49b5a
 	if(param_no == 1) {
a4e0d017
 		return fixup_var_int_12(param, param_no);
b1e49b5a
 	} else if(param_no == 2)
4be7b35e
 		return fixup_var_pve_str_12(param, param_no);
a4e0d017
 	else
 		return 0;
 }
 
 /**
  *
  */
ba64899e
 static int ki_change_reply_status(sip_msg_t *msg, int code, str *reason)
a4e0d017
 {
b1e49b5a
 	struct lump *l;
 	char *ch;
 
ba64899e
 	if(reason==NULL || (reason->len<=0)) {
 		LM_ERR("invalid reason parameter\n");
a4e0d017
 		return -1;
 	}
 
b1e49b5a
 	if((code < 100) || (code > 699)) {
 		LM_ERR("wrong status code: %d\n", code);
a4e0d017
 		return -1;
 	}
 
b1e49b5a
 	if(((code < 300) || (msg->REPLY_STATUS < 300))
 			&& (code / 100 != msg->REPLY_STATUS / 100)) {
 		LM_ERR("the class of provisional or "
 				   "positive final replies cannot be changed\n");
a4e0d017
 		return -1;
 	}
 
 	/* rewrite the status code directly in the message buffer */
 	msg->first_line.u.reply.statuscode = code;
b1e49b5a
 	msg->first_line.u.reply.status.s[2] = code % 10 + '0';
 	code /= 10;
 	msg->first_line.u.reply.status.s[1] = code % 10 + '0';
 	code /= 10;
a4e0d017
 	msg->first_line.u.reply.status.s[0] = code + '0';
 
b1e49b5a
 	l = del_lump(msg, msg->first_line.u.reply.reason.s - msg->buf,
 			msg->first_line.u.reply.reason.len, 0);
 	if(!l) {
 		LM_ERR("Failed to add del lump\n");
a4e0d017
 		return -1;
 	}
 	/* clone the reason phrase, the lumps need to be pkg allocated */
ba64899e
 	ch = (char *)pkg_malloc(reason->len);
b1e49b5a
 	if(!ch) {
 		LM_ERR("Not enough memory\n");
a4e0d017
 		return -1;
 	}
ba64899e
 	memcpy(ch, reason->s, reason->len);
 	if(insert_new_lump_after(l, ch, reason->len, 0) == 0) {
 		LM_ERR("failed to add new lump: %.*s\n", reason->len, ch);
a4e0d017
 		pkg_free(ch);
 		return -1;
 	}
 
 	return 1;
 }
 
 
42fdf377
 /**
  *
  */
ba64899e
 static int change_reply_status_f(
 		struct sip_msg *msg, char *_code, char *_reason)
 {
 	int code;
 	str reason;
 
 	if(get_int_fparam(&code, msg, (fparam_t *)_code)
 			|| get_str_fparam(&reason, msg, (fparam_t *)_reason)) {
 		LM_ERR("cannot get parameters\n");
 		return -1;
 	}
 	return ki_change_reply_status(msg, code, &reason);
 }
 
 
7b95ddda
 /**
  *
  */
 static int ki_change_reply_status_code(sip_msg_t *msg, int code)
 {
 	if((code < 100) || (code > 699)) {
 		LM_ERR("wrong status code: %d\n", code);
 		return -1;
 	}
 
 	if(((code < 300) || (msg->REPLY_STATUS < 300))
 			&& (code / 100 != msg->REPLY_STATUS / 100)) {
 		LM_ERR("the class of provisional or "
 				   "positive final replies cannot be changed\n");
 		return -1;
 	}
 
 	/* rewrite the status code directly in the message buffer */
 	msg->first_line.u.reply.statuscode = code;
 	msg->first_line.u.reply.status.s[2] = code % 10 + '0';
 	code /= 10;
 	msg->first_line.u.reply.status.s[1] = code % 10 + '0';
 	code /= 10;
 	msg->first_line.u.reply.status.s[0] = code + '0';
 
 	return 1l;
 }
 
 /**
  *
  */
 static int change_reply_status_code_f(sip_msg_t *msg, char *pcode, char *p2)
 {
 	int code;
 
 	if(fixup_get_ivalue(msg, (gparam_t*)pcode, &code)<0) {
 		LM_ERR("cannot get parameters\n");
 		return -1;
 	}
 
 	return ki_change_reply_status_code(msg, code);
 }
 
ba64899e
 /**
  *
  */
 static int ki_remove_body(struct sip_msg *msg)
42fdf377
 {
b1e49b5a
 	str body = {0, 0};
42fdf377
 
 	body.len = 0;
 	body.s = get_body(msg);
b1e49b5a
 	if(body.s == 0) {
42fdf377
 		LM_DBG("no body in the message\n");
 		return 1;
 	}
 	body.len = msg->buf + msg->len - body.s;
b1e49b5a
 	if(body.len <= 0) {
42fdf377
 		LM_DBG("empty body in the message\n");
 		return 1;
 	}
b1e49b5a
 	if(del_lump(msg, body.s - msg->buf, body.len, 0) == 0) {
42fdf377
 		LM_ERR("cannot remove body\n");
 		return -1;
 	}
 	return 1;
 }
4945d6f7
 
1b6d5372
 
 /**
  *
  */
ba64899e
 static int w_remove_body_f(struct sip_msg *msg, char *p1, char *p2)
 {
 	return ki_remove_body(msg);
 }
 
 
 /**
  *
  */
 static int keep_hf_helper(sip_msg_t *msg, regex_t *re)
1b6d5372
 {
 	struct hdr_field *hf;
 	regmatch_t pmatch;
 	char c;
b1e49b5a
 	struct lump *l;
1b6d5372
 
 	/* we need to be sure we have seen all HFs */
7987122d
 	if(parse_headers(msg, HDR_EOH_F, 0) == -1) {
 		LM_ERR("Error while parsing message\n");
 		return -1;
 	}
b1e49b5a
 	for(hf = msg->headers; hf; hf = hf->next) {
1b6d5372
 		switch(hf->type) {
 			case HDR_FROM_T:
 			case HDR_TO_T:
 			case HDR_CALLID_T:
 			case HDR_CSEQ_T:
 			case HDR_VIA_T:
 			case HDR_VIA2_T:
 			case HDR_CONTACT_T:
 			case HDR_CONTENTLENGTH_T:
 			case HDR_CONTENTTYPE_T:
 			case HDR_ROUTE_T:
 			case HDR_RECORDROUTE_T:
 			case HDR_MAXFORWARDS_T:
 				continue;
b1e49b5a
 			default:;
1b6d5372
 		}
 
b1e49b5a
 		if(re == NULL) {
7456a25e
 			/* no regex to match => remove all */
b1e49b5a
 			l = del_lump(msg, hf->name.s - msg->buf, hf->len, 0);
 			if(l == 0) {
 				LM_ERR("cannot remove header [%.*s]\n", hf->name.len,
 						hf->name.s);
1b6d5372
 				return -1;
 			}
 		} else {
b5816cae
 			STR_VTOZ(hf->name.s[hf->name.len], c);
b1e49b5a
 			if(regexec(re, hf->name.s, 1, &pmatch, 0) != 0) {
7456a25e
 				/* no match => remove */
b5816cae
 				STR_ZTOV(hf->name.s[hf->name.len], c);
b1e49b5a
 				l = del_lump(msg, hf->name.s - msg->buf, hf->len, 0);
 				if(l == 0) {
 					LM_ERR("cannot remove header [%.*s]\n", hf->name.len,
 							hf->name.s);
7456a25e
 					return -1;
 				}
 			} else {
b5816cae
 				STR_ZTOV(hf->name.s[hf->name.len], c);
7456a25e
 			}
1b6d5372
 		}
 	}
 
 	return -1;
 }
 
ba64899e
 
0c830402
 /**
  *
  */
ba64899e
 static int w_keep_hf_f(struct sip_msg *msg, char *key, char *foo)
 {
 	regex_t *re;
 
 	if(key) {
 		re = (regex_t *)key;
 	} else {
 		re = NULL;
 	}
 	return keep_hf_helper(msg, re);
 }
 
 
 /**
  *
  */
 static int ki_keep_hf(sip_msg_t *msg)
 {
 	return keep_hf_helper(msg, NULL);
 }
 
 
 /**
  *
  */
 static int ki_keep_hf_re(sip_msg_t *msg, str *sre)
 {
 	regex_t re;
 	int ret;
 
 	if(sre==NULL || sre->len<=0)
 		return keep_hf_helper(msg, NULL);
 
 	memset(&re, 0, sizeof(regex_t));
 	if (regcomp(&re, sre->s, REG_EXTENDED|REG_ICASE|REG_NEWLINE)!=0) {
 		LM_ERR("failed to compile regex: %.*s\n", sre->len, sre->s);
 		return -1;
 	}
 	ret = keep_hf_helper(msg, &re);
 	regfree(&re);
 	return ret;
 }
 
 
 /**
  *
  */
 static int w_fnmatch_ex(str *val, str *match, str *flags)
0c830402
 {
 	int i;
 	i = 0;
 #ifdef FNM_CASEFOLD
b1e49b5a
 	if(flags && (flags->s[0] == 'i' || flags->s[0] == 'I'))
0c830402
 		i = FNM_CASEFOLD;
 #endif
b1e49b5a
 	if(fnmatch(match->s, val->s, i) == 0)
0c830402
 		return 0;
 	return -1;
 }
 
 /**
  *
  */
 static int w_fnmatch2_f(sip_msg_t *msg, char *val, char *match)
 {
 	str sval;
 	str smatch;
b1e49b5a
 	if(get_str_fparam(&sval, msg, (fparam_t *)val) < 0
 			|| get_str_fparam(&smatch, msg, (fparam_t *)match) < 0) {
0c830402
 		LM_ERR("invalid parameters");
 		return -1;
 	}
ba64899e
 	if(w_fnmatch_ex(&sval, &smatch, NULL) < 0)
0c830402
 		return -1;
 	return 1;
 }
 
 /**
  *
  */
 static int w_fnmatch3_f(sip_msg_t *msg, char *val, char *match, char *flags)
 {
 	str sval;
 	str smatch;
 	str sflags;
b1e49b5a
 	if(get_str_fparam(&sval, msg, (fparam_t *)val) < 0
 			|| get_str_fparam(&smatch, msg, (fparam_t *)match) < 0
 			|| get_str_fparam(&sflags, msg, (fparam_t *)flags) < 0) {
0c830402
 		LM_ERR("invalid parameters");
 		return -1;
 	}
ba64899e
 	if(w_fnmatch_ex(&sval, &smatch, &sflags) < 0)
0c830402
 		return -1;
 	return 1;
 }
 
ba64899e
 /**
  *
  */
 static int ki_fnmatch(sip_msg_t *msg, str *val, str *match)
 {
 	return w_fnmatch_ex(val, match, NULL);
 }
 
 /**
  *
  */
 static int ki_fnmatch_ex(sip_msg_t *msg, str *val, str *match, str *flags)
 {
 	return w_fnmatch_ex(val, match, flags);
 }
 
0c830402
 /**
  *
  */
b1e49b5a
 static int fixup_fnmatch(void **param, int param_no)
0c830402
 {
b1e49b5a
 	if(param_no == 1) {
0c830402
 		return fixup_var_pve_12(param, param_no);
b1e49b5a
 	} else if(param_no == 2) {
0c830402
 		return fixup_var_pve_12(param, param_no);
b1e49b5a
 	} else if(param_no == 3) {
0c830402
 		return fixup_var_pve_12(param, param_no);
 	} else {
 		return 0;
 	}
 }
 
4945d6f7
 /*
  * Function to load the textops api.
  */
b1e49b5a
 static int bind_textopsx(textopsx_api_t *tob)
 {
 	if(tob == NULL) {
 		LM_WARN("textopsx_binds: Cannot load textopsx API into a NULL "
 				"pointer\n");
4945d6f7
 		return -1;
 	}
 	tob->msg_apply_changes = msg_apply_changes_f;
 	return 0;
 }
ba6119e3
 
 
 /**
  * functions operating on header value
  */
 #define HNF_ALL 0x01
 #define HNF_IDX 0x02
 
 #define MAX_HF_VALUE_STACK 10
 
b1e49b5a
 enum
 {
 	hnoInsert,
 	hnoAppend,
 	hnoAssign,
 	hnoRemove,
 	hnoInclude,
 	hnoExclude,
 	hnoIsIncluded,
 	hnoGetValue,
 	hnoGetValueUri,
 	hnoGetValueName,
 	hnoRemove2,
 	hnoAssign2,
 	hnoGetValue2
 };
ba6119e3
 
b1e49b5a
 struct hname_data
 {
ba6119e3
 	int oper;
 	int htype;
 	str hname;
 	int flags;
 	int idx;
 	str param;
 };
 
b1e49b5a
 #define is_space(_p) \
 	((_p) == '\t' || (_p) == '\n' || (_p) == '\r' || (_p) == ' ')
ba6119e3
 
b1e49b5a
 #define eat_spaces(_p)       \
 	while(is_space(*(_p))) { \
 		(_p)++;              \
 	}
ba6119e3
 
b1e49b5a
 #define is_alphanum(_p)                                           \
 	(((_p) >= 'a' && (_p) <= 'z') || ((_p) >= 'A' && (_p) <= 'Z') \
 			|| ((_p) >= '0' && (_p) <= '9') || (_p) == '_' || (_p) == '-')
ba6119e3
 
b1e49b5a
 #define eat_while_alphanum(_p)  \
 	while(is_alphanum(*(_p))) { \
 		(_p)++;                 \
 	}
ba6119e3
 
b1e49b5a
 static int fixup_hvalue_param(void **param, int param_no)
 {
ba6119e3
 	return fixup_spve_null(param, 1);
 }
 
b1e49b5a
 static int eval_hvalue_param(sip_msg_t *msg, gparam_t *val, str *s)
 {
 	if(fixup_get_svalue(msg, val, s) < 0) {
ba6119e3
 		LM_ERR("could not get string param value\n");
 		return E_UNSPEC;
 	}
 	return 1;
 }
 
 /* parse:  hname [ ([] | [*] | [number]) ] [ "." param ] */
b1e49b5a
 static int fixup_hname_param(char *hname, struct hname_data **h)
 {
ba6119e3
 	struct hdr_field hdr;
 	char *savep, savec;
 
 	*h = pkg_malloc(sizeof(**h));
b1e49b5a
 	if(!*h)
 		return E_OUT_OF_MEM;
ba6119e3
 	memset(*h, 0, sizeof(**h));
 
 	memset(&hdr, 0, sizeof(hdr));
 	eat_spaces(hname);
 	(*h)->hname.s = hname;
 	savep = hname;
 	eat_while_alphanum(hname);
 	(*h)->hname.len = hname - (*h)->hname.s;
 	savec = *hname;
 	*hname = ':';
b1e49b5a
 	parse_hname2_short(
 			(*h)->hname.s, (*h)->hname.s + (*h)->hname.len + 1, &hdr);
ba6119e3
 	*hname = savec;
 
b1e49b5a
 	if(hdr.type == HDR_ERROR_T)
 		goto err;
ba6119e3
 	(*h)->htype = hdr.type;
 
 	eat_spaces(hname);
 	savep = hname;
b1e49b5a
 	if(*hname == '[') {
ba6119e3
 		hname++;
 		eat_spaces(hname);
 		savep = hname;
 		(*h)->flags |= HNF_IDX;
b1e49b5a
 		if(*hname == '*') {
ba6119e3
 			(*h)->flags |= HNF_ALL;
 			hname++;
b1e49b5a
 		} else if(*hname != ']') {
 			char *c;
ba6119e3
 			(*h)->idx = strtol(hname, &c, 10);
b1e49b5a
 			if(hname == c)
 				goto err;
ba6119e3
 			hname = c;
 		}
 		eat_spaces(hname);
 		savep = hname;
b1e49b5a
 		if(*hname != ']')
 			goto err;
ba6119e3
 		hname++;
 	}
 	eat_spaces(hname);
 	savep = hname;
b1e49b5a
 	if(*hname == '.') {
ba6119e3
 		hname++;
 		eat_spaces(hname);
 		savep = hname;
 		(*h)->param.s = hname;
 		eat_while_alphanum(hname);
b1e49b5a
 		(*h)->param.len = hname - (*h)->param.s;
 		if((*h)->param.len == 0)
 			goto err;
 	} else {
ba6119e3
 		(*h)->param.s = hname;
 	}
 	savep = hname;
b1e49b5a
 	if(*hname != '\0')
 		goto err;
ba6119e3
 	(*h)->hname.s[(*h)->hname.len] = '\0';
 	(*h)->param.s[(*h)->param.len] = '\0';
 	return 0;
 err:
 	pkg_free(*h);
b1e49b5a
 	LM_ERR("cannot parse header near '%s'\n", savep);
ba6119e3
 	return E_CFG;
 }
 
b1e49b5a
 static int fixup_hname_str(void **param, int param_no)
 {
 	if(param_no == 1) {
 		struct hname_data *h;
ba6119e3
 		int res = fixup_hname_param(*param, &h);
b1e49b5a
 		if(res < 0)
 			return res;
ba6119e3
 		*param = h;
b1e49b5a
 	} else if(param_no == 2) {
ba6119e3
 		return fixup_hvalue_param(param, param_no);
 	}
 	return 0;
 }
 
80bed453
 static int fixup_free_hname_str(void **param, int param_no)
 {
 	if(param_no == 1) {
 		struct hname_data *h;
 		h = (struct hname_data *)(*param);
 		pkg_free(h);
 		return 0;
 	} else if(param_no == 2) {
 		return fixup_free_spve_null(param, 1);
 	}
 	return 0;
 }
ba6119e3
 
b1e49b5a
 static int find_next_hf(
 		struct sip_msg *msg, struct hname_data *hname, struct hdr_field **hf)
 {
 	if(!*hf) {
 		if(parse_headers(msg, HDR_EOH_F, 0) == -1) {
 			LM_ERR("Error while parsing message\n");
ba6119e3
 			return -1;
 		}
 		*hf = msg->headers;
b1e49b5a
 	} else {
ba6119e3
 		*hf = (*hf)->next;
 	}
b1e49b5a
 	for(; *hf; *hf = (*hf)->next) {
 		if(hname->htype == HDR_OTHER_T) {
 			if((*hf)->name.len == hname->hname.len
 					&& strncasecmp(
 							   (*hf)->name.s, hname->hname.s, (*hf)->name.len)
 							   == 0)
ba6119e3
 				return 1;
b1e49b5a
 		} else if(hname->htype == (*hf)->type) {
ba6119e3
 			return 1;
 		}
 	}
 	return 0;
 }
 
b1e49b5a
 static int find_next_value(char **start, char *end, str *val, str *lump_val)
 {
ba6119e3
 	int quoted = 0;
 	lump_val->s = *start;
b1e49b5a
 	while(*start < end && is_space(**start))
 		(*start)++;
ba6119e3
 	val->s = *start;
b1e49b5a
 	while(*start < end && (**start != ',' || quoted)) {
 		if(**start == '\"' && (!quoted || (*start)[-1] != '\\'))
ba6119e3
 			quoted = ~quoted;
 		(*start)++;
 	}
 	val->len = *start - val->s;
b1e49b5a
 	while(val->len > 0 && is_space(val->s[val->len - 1]))
 		val->len--;
 	/* we cannot automatically strip quotes!!! an example why: "name" <sip:ssss>;param="bar"
ba6119e3
 	if (val->len >= 2 && val->s[0] == '\"' && val->s[val->len-1] == '\"') {
 		val->s++;
 		val->len -= 2;
 	}
 */
b1e49b5a
 	while(*start < end && **start != ',')
 		(*start)++;
 	if(*start < end) {
ba6119e3
 		(*start)++;
 	}
 	lump_val->len = *start - lump_val->s;
 	return (*start < end);
 }
 
b1e49b5a
 static void adjust_lump_val_for_delete(struct hdr_field *hf, str *lump_val)
 {
 	if(lump_val->s + lump_val->len == hf->body.s + hf->body.len) {
 		if(lump_val->s > hf->body.s) {
 			/* in case if is it last value in header save position of last delimiter to remove it with rightmost value */
ba6119e3
 			lump_val->s--;
 			lump_val->len++;
 		}
 	}
 }
 
b1e49b5a
 static int find_hf_value_idx(struct sip_msg *msg, struct hname_data *hname,
 		struct hdr_field **hf, str *val, str *lump_val)
 {
ba6119e3
 	int res;
 	char *p;
b1e49b5a
 	if(hname->flags & HNF_ALL || hname->idx == 0)
 		return -1;
ba6119e3
 	*hf = 0;
b1e49b5a
 	if(hname->idx > 0) {
ba6119e3
 		int idx;
 		idx = hname->idx;
 		do {
 			res = find_next_hf(msg, hname, hf);
b1e49b5a
 			if(res < 0)
 				return -1;
 			if(*hf) {
 				if(val) {
ba6119e3
 					lump_val->len = 0;
 					p = (*hf)->body.s;
 					do {
b1e49b5a
 						res = find_next_value(&p,
 								(*hf)->body.s + (*hf)->body.len, val, lump_val);
ba6119e3
 						idx--;
b1e49b5a
 					} while(res && idx);
 				} else {
ba6119e3
 					idx--;
 				}
 			}
b1e49b5a
 		} while(*hf && idx);
 	} else if(hname->idx < 0) { /* search from the bottom */
 		struct hf_value_stack
 		{
ba6119e3
 			str val, lump_val;
b1e49b5a
 			struct hdr_field *hf;
ba6119e3
 		} stack[MAX_HF_VALUE_STACK];
 		int stack_pos, stack_num;
 
b1e49b5a
 		if(-hname->idx > MAX_HF_VALUE_STACK)
 			return -1;
ba6119e3
 		stack_pos = stack_num = 0;
 		do {
 			res = find_next_hf(msg, hname, hf);
b1e49b5a
 			if(res < 0)
 				return -1;
 			if(*hf) {
ba6119e3
 				stack[stack_pos].lump_val.len = 0;
 				p = (*hf)->body.s;
 				do {
 					stack[stack_pos].hf = *hf;
b1e49b5a
 					if(val)
 						res = find_next_value(&p,
 								(*hf)->body.s + (*hf)->body.len,
 								&stack[stack_pos].val,
 								&stack[stack_pos].lump_val);
ba6119e3
 					else
 						res = 0;
 					stack_pos++;
b1e49b5a
 					if(stack_pos >= MAX_HF_VALUE_STACK)
ba6119e3
 						stack_pos = 0;
b1e49b5a
 					if(stack_num < MAX_HF_VALUE_STACK)
ba6119e3
 						stack_num++;
 
b1e49b5a
 				} while(res);
ba6119e3
 			}
b1e49b5a
 		} while(*hf);
ba6119e3
 
b1e49b5a
 		if(-hname->idx <= stack_num) {
ba6119e3
 			stack_pos += hname->idx;
b1e49b5a
 			if(stack_pos < 0)
ba6119e3
 				stack_pos += MAX_HF_VALUE_STACK;
 			*hf = stack[stack_pos].hf;
b1e49b5a
 			if(val) {
ba6119e3
 				*val = stack[stack_pos].val;
 				*lump_val = stack[stack_pos].lump_val;
 			}
b1e49b5a
 		} else {
ba6119e3
 			*hf = 0;
 		}
b1e49b5a
 	} else
ba6119e3
 		return -1;
b1e49b5a
 	return *hf ? 1 : 0;
ba6119e3
 }
 
b1e49b5a
 static int find_hf_value_param(struct hname_data *hname, str *param_area,
 		str *value, str *lump_upd, str *lump_del)
 {
ba6119e3
 	int i, j, found;
 
 	i = 0;
b1e49b5a
 	while(1) {
ba6119e3
 		lump_del->s = param_area->s + i;
b1e49b5a
 		for(; i < param_area->len && is_space(param_area->s[i]); i++)
 			;
 		if(i < param_area->len
 				&& param_area->s[i] == ';') { /* found a param ? */
ba6119e3
 			i++;
b1e49b5a
 			for(; i < param_area->len && is_space(param_area->s[i]); i++)
 				;
ba6119e3
 			j = i;
b1e49b5a
 			for(; i < param_area->len && !is_space(param_area->s[i])
 					&& param_area->s[i] != '=' && param_area->s[i] != ';';
 					i++)
 				;
ba6119e3
 
b1e49b5a
 			found = hname->param.len == i - j
 					&& !strncasecmp(hname->param.s, param_area->s + j, i - j);
 			lump_upd->s = param_area->s + i;
 			value->s = param_area->s + i;
ba6119e3
 			value->len = 0;
b1e49b5a
 			for(; i < param_area->len && is_space(param_area->s[i]); i++)
 				;
 			if(i < param_area->len && param_area->s[i] == '=') {
ba6119e3
 				i++;
b1e49b5a
 				for(; i < param_area->len && is_space(param_area->s[i]); i++)
 					;
 				value->s = param_area->s + i;
 				if(i < param_area->len) {
 					if(param_area->s[i] == '\"') {
ba6119e3
 						i++;
 						value->s++;
b1e49b5a
 						for(; i < param_area->len; i++) {
 							if(param_area->s[i] == '\"') {
ba6119e3
 								i++;
 								break;
 							}
 							value->len++;
 						}
b1e49b5a
 					} else {
 						for(; i < param_area->len && !is_space(param_area->s[i])
 								&& param_area->s[i] != ';';
 								i++, value->len++)
 							;
ba6119e3
 					}
 				}
 			}
b1e49b5a
 			if(found) {
 				lump_del->len = param_area->s + i - lump_del->s;
 				lump_upd->len = param_area->s + i - lump_upd->s;
ba6119e3
 				return 1;
 			}
b1e49b5a
 		} else { /* not found, return last correct position, should be end of param area */
ba6119e3
 			lump_del->len = 0;
 			return 0;
 		}
 	}
 }
 
 /* parse:  something param_name=param_value something [ "," something param_name="param_value" ....]
  * 'something' is required by Authenticate
  */
b1e49b5a
 static int find_hf_value2_param(struct hname_data *hname, str *param_area,
 		str *value, str *lump_upd, str *lump_del, char *delim)
 {
ba6119e3
 	int i, j, k, found, comma_flag;
 
 	i = 0;
 	*delim = 0;
 	lump_del->len = 0;
b1e49b5a
 	while(i < param_area->len) {
ba6119e3
 
 		lump_del->s = param_area->s + i;
b1e49b5a
 		while(i < param_area->len && is_space(param_area->s[i]))
 			i++;
ba6119e3
 		comma_flag = i < param_area->len && param_area->s[i] == ',';
b1e49b5a
 		if(comma_flag)
 			i++;
 		while(i < param_area->len && is_space(param_area->s[i]))
 			i++;
ba6119e3
 
b1e49b5a
 		if(i < param_area->len
 				&& is_alphanum(param_area->s[i])) { /* found a param name ? */
ba6119e3
 			j = i;
b1e49b5a
 			if(!*delim)
 				*delim = ' ';
 			while(i < param_area->len && is_alphanum(param_area->s[i]))
 				i++;
ba6119e3
 
 			k = i;
b1e49b5a
 			while(i < param_area->len && is_space(param_area->s[i]))
 				i++;
ba6119e3
 			lump_upd->s = param_area->s + i;
b1e49b5a
 			if(i < param_area->len
 					&& param_area->s[i]
 							   == '=') { /* if equal then it's the param */
ba6119e3
 				*delim = ',';
 				i++;
b1e49b5a
 				found = hname->param.len == k - j
 						&& !strncasecmp(
 								   hname->param.s, param_area->s + j, k - j);
 				while(i < param_area->len && is_space(param_area->s[i]))
 					i++;
ba6119e3
 
b1e49b5a
 				value->s = param_area->s + i;
ba6119e3
 				value->len = 0;
b1e49b5a
 				if(i < param_area->len) {
 					if(param_area->s[i] == '\"') {
ba6119e3
 						i++;
 						value->s++;
b1e49b5a
 						for(; i < param_area->len; i++) {
 							if(param_area->s[i] == '\"') {
ba6119e3
 								i++;
 								break;
 							}
 							value->len++;
 						}
b1e49b5a
 					} else {
 						for(; i < param_area->len && !is_space(param_area->s[i])
 								&& param_area->s[i] != ',';
 								i++, value->len++)
 							;
ba6119e3
 					}
 				}
b1e49b5a
 				if(found) {
 					lump_upd->len = param_area->s + i - lump_upd->s;
 					lump_del->len = param_area->s + i - lump_del->s;
ba6119e3
 
b1e49b5a
 					while(i < param_area->len && is_space(param_area->s[i]))
 						i++;
ba6119e3
 
b1e49b5a
 					if(!comma_flag && i < param_area->len
 							&& param_area->s[i] == ',') {
ba6119e3
 						i++;
b1e49b5a
 						lump_del->len = param_area->s + i - lump_del->s;
ba6119e3
 					}
 					return 1;
 				}
 			}
b1e49b5a
 			while(i < param_area->len && is_space(param_area->s[i]))
 				i++;
 		} else {
 			while(i < param_area->len && !is_space(param_area->s[i])
 					&& !(param_area->s[i] != ','))
 				i++;
ba6119e3
 		}
 	}
 	lump_del->s = param_area->s + i;
 	return 0;
 }
 
b1e49b5a
 static int insert_header_lump(struct sip_msg *msg, char *msg_position,
 		int lump_before, str *hname, str *val)
 {
 	struct lump *anchor;
ba6119e3
 	char *s;
 	int len;
 
 	anchor = anchor_lump(msg, msg_position - msg->buf, 0, 0);
b1e49b5a
 	if(anchor == 0) {
 		LM_ERR("Can't get anchor\n");
ba6119e3
 		return -1;
 	}
 
b1e49b5a
 	len = hname->len + 2 + val->len + 2;
ba6119e3
 
b1e49b5a
 	s = (char *)pkg_malloc(len);
 	if(!s) {
 		LM_ERR("not enough memory\n");
ba6119e3
 		return -1;
 	}
 
 	memcpy(s, hname->s, hname->len);
 	s[hname->len] = ':';
b1e49b5a
 	s[hname->len + 1] = ' ';
 	memcpy(s + hname->len + 2, val->s, val->len);
 	s[hname->len + 2 + val->len] = '\r';
 	s[hname->len + 2 + val->len + 1] = '\n';
 
 	if((lump_before ? insert_new_lump_before(anchor, s, len, 0)
 					: insert_new_lump_after(anchor, s, len, 0))
 			== 0) {
 		LM_ERR("Can't insert lump\n");
ba6119e3
 		pkg_free(s);
 		return -1;
 	}
 	return 1;
 }
 
b1e49b5a
 static int insert_value_lump(struct sip_msg *msg, struct hdr_field *hf,
 		char *msg_position, int lump_before, str *val)
 {
 	struct lump *anchor;
ba6119e3
 	char *s;
 	int len;
 
 	anchor = anchor_lump(msg, msg_position - msg->buf, 0, 0);
b1e49b5a
 	if(anchor == 0) {
 		LM_ERR("Can't get anchor\n");
ba6119e3
 		return -1;
 	}
 
b1e49b5a
 	len = val->len + 1;
ba6119e3
 
b1e49b5a
 	s = (char *)pkg_malloc(len);
 	if(!s) {
 		LM_ERR("not enough memory\n");
ba6119e3
 		return -1;
 	}
 
b1e49b5a
 	if(!hf) {
ba6119e3
 		memcpy(s, val->s, val->len);
 		len--;
b1e49b5a
 	} else if(msg_position == hf->body.s + hf->body.len) {
ba6119e3
 		s[0] = ',';
b1e49b5a
 		memcpy(s + 1, val->s, val->len);
 	} else {
ba6119e3
 		memcpy(s, val->s, val->len);
 		s[val->len] = ',';
 	}
b1e49b5a
 	if((lump_before ? insert_new_lump_before(anchor, s, len, 0)
 					: insert_new_lump_after(anchor, s, len, 0))
 			== 0) {
 		LM_ERR("Can't insert lump\n");
ba6119e3
 		pkg_free(s);
 		return -1;
 	}
 	return 1;
 }
 
b1e49b5a
 static int delete_value_lump(
 		struct sip_msg *msg, struct hdr_field *hf, str *val)
 {
 	struct lump *l;
ba6119e3
 	/* TODO: check already existing lumps */
b1e49b5a
 	if(hf && val->s == hf->body.s
 			&& val->len == hf->body.len) /* check if remove whole haeder? */
 		l = del_lump(msg, hf->name.s - msg->buf, hf->len, 0);
ba6119e3
 	else
b1e49b5a
 		l = del_lump(msg, val->s - msg->buf, val->len, 0);
 	if(l == 0) {
 		LM_ERR("not enough memory\n");
ba6119e3
 		return -1;
 	}
 	return 1;
 }
 
80bed453
 static int ki_modify_hf(sip_msg_t *msg, str *hexp, str *val,
 	fixup_function fixf, cmd_function cmdf)
 {
 	int ret;
 	char *s1 = NULL;
 	char *s2 = NULL;
 	void *p1 = NULL;
 	void *p2 = NULL;
 
 	s1 = as_asciiz(hexp);
 	p1 = s1;
 	if(fixf(&p1, 1)!=0) {
 		LM_ERR("failed to fix first parameter\n");
 		p1 = NULL;
 		goto error;
 	}
5d813bb3
 	if(val && val->s!=0 && val->len>0) {
80bed453
 		s2 = as_asciiz(val);
 		p2 = s2;
 		if(fixf(&p2, 2)!=0) {
 			LM_ERR("failed to fix second parameter\n");
 			p2 = NULL;
 			goto error;
 		}
 	}
 
 	ret = cmdf(msg, (char*)p1, (char*)p2);
 
 	if(p2!=NULL) fixup_free_hname_str(&p2, 2);
 	fixup_free_hname_str(&p1, 1);
 	if(s2!=NULL) pkg_free(s2);
 	pkg_free(s1);
 	return ret;
 
 error:
 	if(p1!=NULL) fixup_free_hname_str(&p1, 1);
 	if(s2!=NULL) pkg_free(s2);
 	if(s1!=NULL) pkg_free(s1);
 	return -1;
 }
 
b1e49b5a
 static int incexc_hf_value_str_f(struct sip_msg *msg, char *_hname, str *_pval)
 {
 	struct hname_data *hname = (void *)_hname;
 	struct hdr_field *hf, *lump_hf;
ba6119e3
 	str val, hval1, hval2;
 	char *p;
 	int res;
 
 	val = *_pval;
b1e49b5a
 	if(!val.len)
 		return -1;
ba6119e3
 	hf = 0;
 	lump_hf = 0;
b1e49b5a
 	while(1) {
 		if(find_next_hf(msg, hname, &hf) < 0)
 			return -1;
 		if(!hf)
 			break;
ba6119e3
 		hval2.len = 0;
 		p = hf->body.s;
 		do {
b1e49b5a
 			res = find_next_value(
 					&p, hf->body.s + hf->body.len, &hval1, &hval2);
 			if(hval1.len && val.len == hval1.len
 					&& strncasecmp(val.s, hval1.s, val.len) == 0) {
 				switch(hname->oper) {
ba6119e3
 					case hnoIsIncluded:
 					case hnoInclude:
 						return 1;
 					case hnoExclude:
 						adjust_lump_val_for_delete(hf, &hval2);
 						delete_value_lump(msg, hf, &hval2);
 					default:
 						break;
 				}
 			}
b1e49b5a
 		} while(res);
 		switch(hname->oper) {
ba6119e3
 			case hnoInclude:
b1e49b5a
 				if(!lump_hf) {
ba6119e3
 					lump_hf = hf;
 				}
 				break;
 			default:
 				break;
 		}
 	}
b1e49b5a
 	switch(hname->oper) {
ba6119e3
 		case hnoIsIncluded:
 			return -1;
 		case hnoInclude:
b1e49b5a
 			if(lump_hf)
 				return insert_value_lump(msg, lump_hf,
 						lump_hf->body.s + lump_hf->body.len, 1, &val);
ba6119e3
 			else
b1e49b5a
 				return insert_header_lump(
 						msg, msg->unparsed, 1, &hname->hname, &val);
ba6119e3
 		default:
 			return 1;
 	}
 }
 
b1e49b5a
 static int incexc_hf_value_f(struct sip_msg *msg, char *_hname, char *_val)
ba6119e3
 {
 	str val;
 	int res;
b1e49b5a
 
 	res = eval_hvalue_param(msg, (void *)_val, &val);
 
 	if(res < 0)
 		return res;
 	if(!val.len)
 		return -1;
ba6119e3
 
 	return incexc_hf_value_str_f(msg, _hname, &val);
 }
 
b1e49b5a
 #define INCEXC_HF_VALUE_FIXUP(_func, _oper)                                  \
 	static int _func(void **param, int param_no)                             \
 	{                                                                        \
 		char *p = *param;                                                    \
 		int res = fixup_hname_str(param, param_no);                          \
 		if(res < 0)                                                          \
 			return res;                                                      \
 		if(param_no == 1) {                                                  \
 			if(((struct hname_data *)*param)->flags & HNF_IDX                \
 					|| ((struct hname_data *)*param)->param.len) {           \
 				LM_ERR("neither index nor param may be specified in '%s'\n", \
 						p);                                                  \
 				return E_CFG;                                                \
 			}                                                                \
 			((struct hname_data *)*param)->oper = _oper;                     \
 		}                                                                    \
 		return 0;                                                            \
 	}
ba6119e3
 
 INCEXC_HF_VALUE_FIXUP(include_hf_value_fixup, hnoInclude)
 INCEXC_HF_VALUE_FIXUP(exclude_hf_value_fixup, hnoExclude)
 INCEXC_HF_VALUE_FIXUP(hf_value_exists_fixup, hnoIsIncluded)
 
80bed453
 static int ki_include_hf_value(sip_msg_t *msg, str *hexp, str *val)
 {
 	return ki_modify_hf(msg, hexp, val, include_hf_value_fixup,
 			incexc_hf_value_f);
 }
 
 static int ki_exclude_hf_value(sip_msg_t *msg, str *hexp, str *val)
 {
 	return ki_modify_hf(msg, hexp, val, exclude_hf_value_fixup,
 			incexc_hf_value_f);
 }
 
 static int ki_hf_value_exists(sip_msg_t *msg, str *hexp, str *val)
 {
 	return ki_modify_hf(msg, hexp, val, hf_value_exists_fixup,
 			incexc_hf_value_f);
 }
 
b1e49b5a
 static void get_uri_and_skip_until_params(str *param_area, str *name, str *uri)
 {
ba6119e3
 	int i, quoted, uri_pos, uri_done;
 
 	name->len = 0;
 	uri->len = 0;
a2513a2b
 	uri->s = 0;
ba6119e3
 	uri_done = 0;
b1e49b5a
 	name->s = param_area->s;
 	for(i = 0; i < param_area->len && param_area->s[i] != ';';) {
 		/* [ *(token LSW)/quoted-string ] "<" addr-spec ">" | addr-spec */
ba6119e3
 		/* skip name */
b1e49b5a
 		for(quoted = 0, uri_pos = i; i < param_area->len; i++) {
 			if(!quoted) {
 				if(param_area->s[i] == '\"') {
ba6119e3
 					quoted = 1;
 					uri_pos = -1;
b1e49b5a
 				} else if(param_area->s[i] == '<' || param_area->s[i] == ';'
 						  || is_space(param_area->s[i]))
 					break;
 			} else if(param_area->s[i] == '\"' && param_area->s[i - 1] != '\\')
 				quoted = 0;
ba6119e3
 		}
b1e49b5a
 		if(!name->len)
 			name->len = param_area->s + i - name->s;
 		if(uri_pos >= 0 && !uri_done) {
 			uri->s = param_area->s + uri_pos;
 			uri->len = param_area->s + i - uri->s;
ba6119e3
 		}
 		/* skip uri */
b1e49b5a
 		while(i < param_area->len && is_space(param_area->s[i]))
 			i++;
 		if(i < param_area->len && param_area->s[i] == '<') {
 			uri->s = param_area->s + i;
ba6119e3
 			uri->len = 0;
b1e49b5a
 			for(quoted = 0; i < param_area->len; i++) {
 				if(!quoted) {
 					if(param_area->s[i] == '\"')
 						quoted = 1;
 					else if(param_area->s[i] == '>') {
 						uri->len = param_area->s + i - uri->s + 1;
ba6119e3
 						uri_done = 1;
 						break;
 					}
b1e49b5a
 				} else if(param_area->s[i] == '\"'
 						  && param_area->s[i - 1] != '\\')
 					quoted = 0;
ba6119e3
 			}
 		}
 	}
b1e49b5a
 	param_area->s += i;
 	param_area->len -= i;
 	if(uri->s == name->s)
ba6119e3
 		name->len = 0;
 }
 
b1e49b5a
 static int assign_hf_do_lumping(struct sip_msg *msg, struct hdr_field *hf,
 		struct hname_data *hname, str *value, int upd_del_fl, str *lump_upd,
 		str *lump_del, char delim)
 {
ba6119e3
 	int len, i;
 	char *s;
b1e49b5a
 	struct lump *anchor;
 
 	if(upd_del_fl) {
 		len = value ? lump_upd->len : lump_del->len;
 		if(len > 0) {
 			if(!del_lump(msg, (value ? lump_upd->s : lump_del->s) - msg->buf,
 					   len, 0)) {
 				LM_ERR("not enough memory\n");
ba6119e3
 				return -1;
 			}
 		}
b1e49b5a
 		if(value && value->len) {
ba6119e3
 			anchor = anchor_lump(msg, lump_upd->s - msg->buf, 0, 0);
b1e49b5a
 			if(anchor == 0) {
 				LM_ERR("Can't get anchor\n");
ba6119e3
 				return -1;
 			}
 
b1e49b5a
 			len = 1 + value->len;
ba6119e3
 			s = pkg_malloc(len);
b1e49b5a
 			if(!s) {
 				LM_ERR("not enough memory\n");
ba6119e3
 				return -1;
 			}
b1e49b5a
 			s[0] = '=';
 			memcpy(s + 1, value->s, value->len);
 			if((insert_new_lump_before(anchor, s, len, 0)) == 0) {
 				LM_ERR("Can't insert lump\n");
ba6119e3
 				pkg_free(s);
 				return -1;
 			}
 		}
b1e49b5a
 	} else {
 		if(!value)
 			return -1;
ba6119e3
 
 		anchor = anchor_lump(msg, lump_del->s - msg->buf, 0, 0);
b1e49b5a
 		if(anchor == 0) {
 			LM_ERR("Can't get anchor\n");
ba6119e3
 			return -1;
 		}
 
b1e49b5a
 		len = 1 + hname->param.len + (value->len ? value->len + 1 : 0);
ba6119e3
 		s = pkg_malloc(len);
b1e49b5a
 		if(!s) {
 			LM_ERR("not enough memory\n");
ba6119e3
 			return -1;
 		}
b1e49b5a
 		if(delim) {
ba6119e3
 			s[0] = delim;
 			i = 1;
b1e49b5a
 		} else {
ba6119e3
 			i = 0;
 			len--;
 		}
b1e49b5a
 		memcpy(s + i, hname->param.s, hname->param.len);
 		if(value->len) {
 			s[hname->param.len + i] = '=';
 			memcpy(s + i + hname->param.len + 1, value->s, value->len);
ba6119e3
 		}
 
b1e49b5a
 		if((insert_new_lump_before(anchor, s, len, 0)) == 0) {
 			LM_ERR("Can't insert lump\n");
ba6119e3
 			pkg_free(s);
 			return -1;
 		}
 	}
 	return 1;
 }
 
 
b1e49b5a
 static int assign_hf_process_params(struct sip_msg *msg, struct hdr_field *hf,
 		struct hname_data *hname, str *value, str *value_area)
 {
 	int r, r2, res = 0;
ba6119e3
 	str param_area, lump_upd, lump_del, dummy_val, dummy_name, dummy_uri;
 	param_area = *value_area;
 	get_uri_and_skip_until_params(&param_area, &dummy_name, &dummy_uri);
 	do {
b1e49b5a
 		r = find_hf_value_param(
 				hname, &param_area, &dummy_val, &lump_upd, &lump_del);
 		r2 = assign_hf_do_lumping(
 				msg, hf, hname, value, r, &lump_upd, &lump_del, ';');
 		if(res == 0)
ba6119e3
 			res = r2;
b1e49b5a
 		if(r && !value) { /* remove all parameters */
 			param_area.len -= lump_del.s + lump_del.len - param_area.s;
 			param_area.s = lump_del.s + lump_del.len;
ba6119e3
 		}
b1e49b5a
 	} while(!value && r);
ba6119e3
 	return res;
 }
 
b1e49b5a
 static int assign_hf_process2_params(struct sip_msg *msg, struct hdr_field *hf,
 		struct hname_data *hname, str *value)
 {
ba6119e3
 	int r, r2, res = 0;
 	str param_area, lump_upd, lump_del, dummy_val;
 	char delim;
 
 	param_area = hf->body;
 
 	do {
b1e49b5a
 		r = find_hf_value2_param(
 				hname, &param_area, &dummy_val, &lump_upd, &lump_del, &delim);
 		r2 = assign_hf_do_lumping(
 				msg, hf, hname, value, r, &lump_upd, &lump_del, delim);
 		if(res == 0)
ba6119e3
 			res = r2;
b1e49b5a
 		if(r && !value) { /* remove all parameters */
 			param_area.len -= lump_del.s + lump_del.len - param_area.s;
 			param_area.s = lump_del.s + lump_del.len;
ba6119e3
 		}
b1e49b5a
 	} while(!value && r);
ba6119e3
 	return res;
 }
 
b1e49b5a
 static int insupddel_hf_value_f(struct sip_msg *msg, char *_hname, char *_val)
 {
 	struct hname_data *hname = (void *)_hname;
 	struct hdr_field *hf;
f0186bf3
 	str val = {0};
 	str hval1, hval2;
ba6119e3
 	int res;
 
b1e49b5a
 	if(_val) {
 		res = eval_hvalue_param(msg, (void *)_val, &val);
 		if(res < 0)
 			return res;
ba6119e3
 	}
b1e49b5a
 	switch(hname->oper) {
ba6119e3
 		case hnoAppend:
b1e49b5a
 			if((hname->flags & HNF_IDX) == 0) {
 				if(parse_headers(msg, HDR_EOH_F, 0) == -1) {
 					LM_ERR("Error while parsing message\n");
ba6119e3
 					return -1;
 				}
b1e49b5a
 				return insert_header_lump(
 						msg, msg->unparsed, 1, &hname->hname, &val);
 			} else {
ba6119e3
 				res = find_hf_value_idx(msg, hname, &hf, &hval1, &hval2);
b1e49b5a
 				if(res < 0)
 					return res;
 				if(hf) {
 					return insert_value_lump(msg, hf, hval2.s + hval2.len,
 							res /* insert after, except it is last value in header */
 							,
 							&val);
 				} else {
 					return insert_header_lump(
 							msg, msg->unparsed, 1, &hname->hname, &val);
ba6119e3
 				}
 			}
 		case hnoInsert:
 			/* if !HNF_IDX is possible parse only until first hname header
 			 * but not trivial for HDR_OTHER_T header, not implemented */
 			res = find_hf_value_idx(msg, hname, &hf, &hval1, &hval2);
b1e49b5a
 			if(res < 0)
 				return res;
 			if(hf && (hname->flags & HNF_IDX) == 0) {
 				return insert_header_lump(
 						msg, hf->name.s, 1, &hname->hname, &val);
 			} else if(!hf && hname->idx == 1) {
 				return insert_header_lump(
 						msg, msg->unparsed, 1, &hname->hname, &val);
 			} else if(hf) {
ba6119e3
 				return insert_value_lump(msg, hf, hval2.s, 1, &val);
b1e49b5a
 			} else
ba6119e3
 				return -1;
 
 		case hnoRemove:
 		case hnoAssign:
b1e49b5a
 			if(hname->flags & HNF_ALL) {
 				struct hdr_field *hf = 0;
ba6119e3
 				int fl = -1;
 				do {
 					res = find_next_hf(msg, hname, &hf);
b1e49b5a
 					if(res < 0)
 						return res;
 					if(hf) {
 						if(!hname->param.len) {
ba6119e3
 							fl = 1;
 							delete_value_lump(msg, hf, &hf->body);
b1e49b5a
 						} else {
ba6119e3
 							char *p;
 							hval2.len = 0;
 							p = hf->body.s;
 							do {
b1e49b5a
 								res = find_next_value(&p,
 										hf->body.s + hf->body.len, &hval1,
 										&hval2);
 								if(assign_hf_process_params(msg, hf, hname,
 										   _val ? &val : 0, &hval1)
 										> 0)
ba6119e3
 									fl = 1;
b1e49b5a
 							} while(res);
ba6119e3
 						}
 					}
b1e49b5a
 				} while(hf);
ba6119e3
 				return fl;
b1e49b5a
 			} else {
ba6119e3
 				res = find_hf_value_idx(msg, hname, &hf, &hval1, &hval2);
b1e49b5a
 				if(res < 0)
 					return res;
 				if(hf) {
 					if(!hname->param.len) {
 						if(hname->oper == hnoRemove) {
ba6119e3
 							adjust_lump_val_for_delete(hf, &hval2);
 							return delete_value_lump(msg, hf, &hval2);
b1e49b5a
 						} else {
 							res = delete_value_lump(msg,
 									0 /* delete only value part */, &hval1);
 							if(res < 0)
 								return res;
 							if(val.len) {
 								return insert_value_lump(msg,
 										0 /* do not add delims */, hval1.s, 1,
 										&val);
ba6119e3
 							}
 							return 1;
 						}
b1e49b5a
 					} else {
 						return assign_hf_process_params(
 								msg, hf, hname, _val ? &val : 0, &hval1);
ba6119e3
 					}
 				}
 			}
 			break;
 		case hnoRemove2:
 		case hnoAssign2:
b1e49b5a
 			if(hname->flags & HNF_ALL) {
 				struct hdr_field *hf = 0;
ba6119e3
 				int fl = -1;
 				do {
 					res = find_next_hf(msg, hname, &hf);
b1e49b5a
 					if(res < 0)
 						return res;
 					if(hf) {
 						if(!hname->param.len) { /* the same as hnoRemove/hnoAssign */
ba6119e3
 							fl = 1;
 							delete_value_lump(msg, hf, &hf->body);
b1e49b5a
 						} else {
ba6119e3
 
b1e49b5a
 							if(assign_hf_process2_params(
 									   msg, hf, hname, _val ? &val : 0)
 									> 0)
ba6119e3
 								fl = 1;
 						}
 					}
b1e49b5a
 				} while(hf);
ba6119e3
 				return fl;
b1e49b5a
 			} else {
ba6119e3
 				res = find_hf_value_idx(msg, hname, &hf, 0, 0);
b1e49b5a
 				if(res < 0)
 					return res;
 				if(hf) {
 					if(!hname->param.len) {
 						if(hname->oper == hnoRemove2) {
ba6119e3
 							return delete_value_lump(msg, hf, &hf->body);
b1e49b5a
 						} else {
 							res = delete_value_lump(msg,
 									0 /* delete only value part */, &hf->body);
 							if(res < 0)
 								return res;
 							if(val.len) {
 								return insert_value_lump(msg,
 										0 /* do not add delims */, hf->body.s,
 										1, &val);
ba6119e3
 							}
 							return 1;
 						}
b1e49b5a
 					} else {
 						return assign_hf_process2_params(
 								msg, hf, hname, _val ? &val : 0);
ba6119e3
 					}
 				}
 			}
 			break;
 	}
 	return -1;
 }
 
b1e49b5a
 static int append_hf_value_fixup(void **param, int param_no)
 {
 	int res = fixup_hname_str(param, param_no);
 	if(res < 0)
 		return res;
 	if(param_no == 1) {
 		if(((struct hname_data *)*param)->flags & HNF_ALL) {
 			LM_ERR("asterisk not supported\n");
ba6119e3
 			return E_CFG;
b1e49b5a
 		} else if((((struct hname_data *)*param)->flags & HNF_IDX) == 0
 				  || !((struct hname_data *)*param)->idx) {
 			((struct hname_data *)*param)->idx = -1;
ba6119e3
 		}
b1e49b5a
 		if(((struct hname_data *)*param)->idx < -MAX_HF_VALUE_STACK) {
 			LM_ERR("index cannot be lower than %d\n", -MAX_HF_VALUE_STACK);
ba6119e3
 			return E_CFG;
 		}
b1e49b5a
 		if(((struct hname_data *)*param)->param.len) {
 			LM_ERR("param not supported\n");
ba6119e3
 			return E_CFG;
 		}
b1e49b5a
 		((struct hname_data *)*param)->oper = hnoAppend;
ba6119e3
 	}
 	return 0;
 }
 
80bed453
 static int ki_append_hf_value(sip_msg_t *msg, str *hexp, str *val)
 {
 	return ki_modify_hf(msg, hexp, val, append_hf_value_fixup,
 			insupddel_hf_value_f);
 }
 
b1e49b5a
 static int insert_hf_value_fixup(void **param, int param_no)
 {
 	int res = fixup_hname_str(param, param_no);
 	if(res < 0)
 		return res;
 	if(param_no == 1) {
 		if(((struct hname_data *)*param)->flags & HNF_ALL) {
 			LM_ERR("asterisk not supported\n");
ba6119e3
 			return E_CFG;
b1e49b5a
 		} else if((((struct hname_data *)*param)->flags & HNF_IDX) == 0
 				  || !((struct hname_data *)*param)->idx) {
 			((struct hname_data *)*param)->idx = 1;
ba6119e3
 		}
b1e49b5a
 		if(((struct hname_data *)*param)->idx < -MAX_HF_VALUE_STACK) {
 			LM_ERR("index cannot be lower than %d\n", -MAX_HF_VALUE_STACK);
ba6119e3
 			return E_CFG;
 		}
b1e49b5a
 		if(((struct hname_data *)*param)->param.len) {
 			LM_ERR("param not supported\n");
ba6119e3
 			return E_CFG;
 		}
b1e49b5a
 		((struct hname_data *)*param)->oper = hnoInsert;
ba6119e3
 	}
 	return 0;
 }
 
80bed453
 static int ki_insert_hf_value(sip_msg_t *msg, str *hexp, str *val)
 {
 	return ki_modify_hf(msg, hexp, val, insert_hf_value_fixup,
 			insupddel_hf_value_f);
 }
 
b1e49b5a
 static int remove_hf_value_fixup(void **param, int param_no)
 {
 	int res = fixup_hname_str(param, param_no);
 	if(res < 0)
 		return res;
 	if(param_no == 1) {
 		if((((struct hname_data *)*param)->flags & HNF_IDX) == 0
 				|| !((struct hname_data *)*param)->idx) {
 			((struct hname_data *)*param)->idx = 1;
 			((struct hname_data *)*param)->flags |= HNF_IDX;
ba6119e3
 		}
b1e49b5a
 		if(((struct hname_data *)*param)->idx < -MAX_HF_VALUE_STACK) {
 			LM_ERR("index cannot be lower than %d\n", -MAX_HF_VALUE_STACK);
ba6119e3
 			return E_CFG;
 		}
b1e49b5a
 		((struct hname_data *)*param)->oper = hnoRemove;
ba6119e3
 	}
 	return 0;
 }
 
80bed453
 static int ki_remove_hf_value(sip_msg_t *msg, str *hexp)
 {
 	return ki_modify_hf(msg, hexp, NULL, remove_hf_value_fixup,
 			insupddel_hf_value_f);
 }
 
b1e49b5a
 static int assign_hf_value_fixup(void **param, int param_no)
 {
 	int res = fixup_hname_str(param, param_no);
 	if(res < 0)
 		return res;
 	if(param_no == 1) {
 		if((((struct hname_data *)*param)->flags & HNF_ALL)
 				&& !((struct hname_data *)*param)->param.len) {
 			LM_ERR("asterisk not supported without param\n");
ba6119e3
 			return E_CFG;
b1e49b5a
 		} else if((((struct hname_data *)*param)->flags & HNF_IDX) == 0
 				  || !((struct hname_data *)*param)->idx) {
 			((struct hname_data *)*param)->idx = 1;
 			((struct hname_data *)*param)->flags |= HNF_IDX;
ba6119e3
 		}
b1e49b5a
 		if(((struct hname_data *)*param)->idx < -MAX_HF_VALUE_STACK) {
 			LM_ERR("index cannot be lower than %d\n", -MAX_HF_VALUE_STACK);
ba6119e3
 			return E_CFG;
 		}
b1e49b5a
 		((struct hname_data *)*param)->oper = hnoAssign;
ba6119e3
 	}
 	return 0;
 }
 
80bed453
 static int ki_assign_hf_value(sip_msg_t *msg, str *hexp, str *val)
 {
 	return ki_modify_hf(msg, hexp, val, assign_hf_value_fixup,
 			insupddel_hf_value_f);
 }
 
b1e49b5a
 static int remove_hf_value2_fixup(void **param, int param_no)
 {
 	int res = remove_hf_value_fixup(param, param_no);
 	if(res < 0)
 		return res;
 	if(param_no == 1) {
 		((struct hname_data *)*param)->oper = hnoRemove2;
ba6119e3
 	}
 	return 0;
 }
 
80bed453
 static int ki_remove_hf_value2(sip_msg_t *msg, str *hexp, str *val)
 {
 	return ki_modify_hf(msg, hexp, val, remove_hf_value2_fixup,
 			insupddel_hf_value_f);
 }
 
b1e49b5a
 static int assign_hf_value2_fixup(void **param, int param_no)
 {
 	int res = assign_hf_value_fixup(param, param_no);
 	if(res < 0)
 		return res;
 	if(param_no == 1) {
 		((struct hname_data *)*param)->oper = hnoAssign2;
ba6119e3
 	}
 	return 0;
 }
 
80bed453
 static int ki_assign_hf_value2(sip_msg_t *msg, str *hexp, str *val)
 {
 	return ki_modify_hf(msg, hexp, val, assign_hf_value2_fixup,
 			insupddel_hf_value_f);
 }
ba6119e3
 
ba3ce508
 #define HF_ITERATOR_SIZE	4
 #define HF_ITERATOR_NAME_SIZE	32
 
 typedef struct hf_iterator {
 	str name;
 	char bname[HF_ITERATOR_NAME_SIZE];
 	hdr_field_t *it;
24172c08
 	hdr_field_t *prev;
ba3ce508
 	int eoh;
 } hf_iterator_t;
 
 static hf_iterator_t _hf_iterators[HF_ITERATOR_SIZE];
 
 /**
  *
  */
0a13b912
 static void hf_iterator_init(void)
ba3ce508
 {
 	memset(_hf_iterators, 0, HF_ITERATOR_SIZE*sizeof(hf_iterator_t));
 }
 
 /**
  *
  */
 static int ki_hf_iterator_start(sip_msg_t *msg, str *iname)
 {
 	int i;
 	int k;
 
 	k = -1;
 	for(i=0; i<HF_ITERATOR_SIZE; i++) {
 		if(_hf_iterators[i].name.len>0) {
 			if(_hf_iterators[i].name.len==iname->len
 					&& strncmp(_hf_iterators[i].name.s, iname->s, iname->len)==0) {
 				k = i;
 				break;
 			}
 		} else {
 			if(k==-1) k = i;
 		}
 	}
 	if(k==-1) {
 		LM_ERR("no iterator available - max number is %d\n", HF_ITERATOR_SIZE);
 		return -1;
 	}
 	if(_hf_iterators[k].name.len<=0) {
 		if(iname->len>=HF_ITERATOR_NAME_SIZE)
 		{
 			LM_ERR("iterator name is too big [%.*s] (max %d)\n",
 					iname->len, iname->s, HF_ITERATOR_NAME_SIZE);
 			return -1;
 		}
 		strncpy(_hf_iterators[k].bname, iname->s, iname->len);
 		_hf_iterators[k].bname[iname->len] = '\0';
 		_hf_iterators[k].name.len = iname->len;
 		_hf_iterators[k].name.s = _hf_iterators[k].bname;
 	}
 	_hf_iterators[k].it = NULL;
24172c08
 	_hf_iterators[k].prev = NULL;
ba3ce508
 	_hf_iterators[k].eoh = 0;
 	if(parse_headers(msg, HDR_EOH_F, 0) == -1) {
 		LM_ERR("failed parsing message\n");
 		return -1;
 	}
 	if(msg->headers==NULL) {
 		LM_ERR("no headers for iterator [%.*s]\n", iname->len, iname->s);
 		return -1;
 	}
 	return 1;
 }
 
 /**
  *
  */
 static int w_hf_iterator_start(sip_msg_t *msg, char *piname, char *p2)
 {
 	str iname = STR_NULL;
 	if(fixup_get_svalue(msg, (gparam_t*)piname, &iname)<0) {
 		LM_ERR("failed to get iterator name\n");
 		return -1;
 	}
 	return ki_hf_iterator_start(msg, &iname);
 }
 
 /**
  *
  */
 static int ki_hf_iterator_next(sip_msg_t *msg, str *iname)
 {
 	int i;
 	int k;
 
 	k = -1;
 	for(i=0; i<HF_ITERATOR_SIZE; i++) {
 		if(_hf_iterators[i].name.len>0) {
 			if(_hf_iterators[i].name.len==iname->len
 					&& strncmp(_hf_iterators[i].name.s, iname->s, iname->len)==0) {
 				k = i;
 				break;
 			}
 		}
 	}
 	if(k==-1) {
 		LM_ERR("iterator not available [%.*s]\n", iname->len, iname->s);
 		return -1;
 	}
7f4d7725
 	if(_hf_iterators[k].eoh == 1) {
 		return -1;
 	}
 
ba3ce508
 	if(_hf_iterators[k].it == NULL) {
 		_hf_iterators[k].it = msg->headers;
 	} else {
24172c08
 		_hf_iterators[k].prev = _hf_iterators[k].it;
ba3ce508
 		_hf_iterators[k].it = _hf_iterators[k].it->next;
 	}
 	if(_hf_iterators[k].it == NULL) {
 		_hf_iterators[k].eoh = 1;
7f4d7725
 		return -1;
ba3ce508
 	}
 	return 1;
 }
 
 /**
  *
  */
 static int w_hf_iterator_next(sip_msg_t *msg, char *piname, char *p2)
 {
 	str iname = STR_NULL;
 	if(fixup_get_svalue(msg, (gparam_t*)piname, &iname)<0) {
 		LM_ERR("failed to get iterator name\n");
 		return -1;
 	}
 	return ki_hf_iterator_next(msg, &iname);
 }
 
24172c08
 /**
  *
  */
 static int ki_hf_iterator_prev(sip_msg_t *msg, str *iname)
 {
 	hdr_field_t *hf;
 	int i;
 	int k;
 
 	k = -1;
 	for(i=0; i<HF_ITERATOR_SIZE; i++) {
 		if(_hf_iterators[i].name.len>0) {
 			if(_hf_iterators[i].name.len==iname->len
 					&& strncmp(_hf_iterators[i].name.s, iname->s, iname->len)==0) {
 				k = i;
 				break;
 			}
 		}
 	}
 	if(k==-1) {
 		LM_ERR("iterator not available [%.*s]\n", iname->len, iname->s);
 		return -1;
 	}
 	if(_hf_iterators[k].eoh == 1) {
 		return -1;
 	}
 
 	if(_hf_iterators[k].prev==NULL) {
 		return ki_hf_iterator_start(msg, iname);
 	}
 
 	if(_hf_iterators[k].prev!=_hf_iterators[k].it) {
 		_hf_iterators[k].it = _hf_iterators[k].prev;
 		return 1;
 	}
 	for(hf=msg->headers; hf; hf=hf->next) {
 		if(hf->next) {
 			if(hf->next->next) {
 				if(_hf_iterators[k].it==hf->next->next) {
 					_hf_iterators[k].it = hf->next;
0a13b912
 					_hf_iterators[k].prev = hf;
24172c08
 					return 1;
 				}
 			} else {
 				if(_hf_iterators[k].it==hf->next) {
 					_hf_iterators[k].it = hf;
 					_hf_iterators[k].prev = NULL;
 					return 1;
 				}
 			}
 		}
 	}
 	return ki_hf_iterator_start(msg, iname);
 }
 
 /**
  *
  */
 static int w_hf_iterator_prev(sip_msg_t *msg, char *piname, char *p2)
 {
 	str iname = STR_NULL;
 	if(fixup_get_svalue(msg, (gparam_t*)piname, &iname)<0) {
 		LM_ERR("failed to get iterator name\n");
 		return -1;
 	}
 	return ki_hf_iterator_prev(msg, &iname);
 }
 
ba3ce508
 /**
  *
  */
 static int ki_hf_iterator_end(sip_msg_t *msg, str *iname)
 {
 	int i;
 	int k;
 
 	k = -1;
 	for(i=0; i<HF_ITERATOR_SIZE; i++) {
 		if(_hf_iterators[i].name.len>0) {
 			if(_hf_iterators[i].name.len==iname->len
 					&& strncmp(_hf_iterators[i].name.s, iname->s, iname->len)==0) {
 				k = i;
 				break;
 			}
 		}
 	}
 	if(k==-1) {
 		LM_ERR("iterator not available [%.*s]\n", iname->len, iname->s);
 		return -1;
 	}
 	_hf_iterators[k].it = NULL;
 	_hf_iterators[k].eoh = 0;
 	return 1;
 }
 
 /**
  *
  */
 static int w_hf_iterator_end(sip_msg_t *msg, char *piname, char *p2)
 {
 	str iname = STR_NULL;
 	if(fixup_get_svalue(msg, (gparam_t*)piname, &iname)<0) {
 		LM_ERR("failed to get iterator name\n");
 		return -1;
 	}
 	return ki_hf_iterator_end(msg, &iname);
 }
 
adc3bc18
 /**
  *
  */
 static int ki_hf_iterator_index(sip_msg_t *msg, str *iname)
 {
 	int i;
 	int k;
 
 	k = -1;
 	for(i=0; i<HF_ITERATOR_SIZE; i++) {
 		if(_hf_iterators[i].name.len>0) {
 			if(_hf_iterators[i].name.len==iname->len
 					&& strncmp(_hf_iterators[i].name.s, iname->s, iname->len)==0) {
 				k = i;
 				break;
 			}
 		}
 	}
 	if(k==-1) {
 		LM_ERR("iterator not available [%.*s]\n", iname->len, iname->s);
 		return -1;
 	}
 
 	return k;
 }
 
 /**
  *
  */
 static int ki_hf_iterator_rm(sip_msg_t *msg, str *iname)
 {
 	int k;
 	sr_lump_t *anchor;
 
 	k = ki_hf_iterator_index(msg, iname);
 	if(k<0 || _hf_iterators[k].it==NULL) {
 		return -1;
 	}
 	anchor = del_lump(msg, _hf_iterators[k].it->name.s - msg->buf,
 			_hf_iterators[k].it->len, 0);
 	if (anchor==0) {
 		LM_ERR("cannot remove hdr %.*s\n", _hf_iterators[k].it->name.len,
 				_hf_iterators[k].it->name.s);
 		return -1;
 	}
 	return 1;
 }
 
 /**
  *
  */
 static int w_hf_iterator_rm(sip_msg_t *msg, char *piname, char *p2)
 {
 	str iname = STR_NULL;
 	if(fixup_get_svalue(msg, (gparam_t*)piname, &iname)<0) {
 		LM_ERR("failed to get iterator name\n");
 		return -1;
 	}
 	return ki_hf_iterator_rm(msg, &iname);
 }
 
3f3fb6ad
 /**
  *
  */
 static int ki_hf_iterator_append(sip_msg_t *msg, str *iname, str *htext)
 {
 	int k;
 	sr_lump_t *anchor;
 	str sval = STR_NULL;
 
 	k = ki_hf_iterator_index(msg, iname);
 	if(k<0 || _hf_iterators[k].it==NULL) {
 		return -1;
 	}
 	anchor = anchor_lump(msg, _hf_iterators[k].it->name.s
 			+ _hf_iterators[k].it->len - msg->buf, 0, 0);
 	if (anchor==0) {
 		LM_ERR("cannot append hdr after %.*s\n", _hf_iterators[k].it->name.len,
 				_hf_iterators[k].it->name.s);
 		return -1;
 	}
 	sval.s = (char*)pkg_malloc(htext->len + 1);
 	if(sval.s==NULL) {
 		LM_ERR("failed append hdr after %.*s\n", _hf_iterators[k].it->name.len,
 				_hf_iterators[k].it->name.s);
 		return -1;
 	}
 	memcpy(sval.s, htext->s, htext->len);
 	sval.len = htext->len;
 	sval.s[sval.len] = '\0';
 
 	if (insert_new_lump_before(anchor, sval.s, sval.len, 0) == 0) {
 		LM_ERR("cannot insert lump\n");
 		pkg_free(sval.s);
 		return -1;
 	}
 	return 1;
 }
 
 /**
  *
  */
 static int w_hf_iterator_append(sip_msg_t *msg, char *piname, char *phtext)
 {
 	str iname = STR_NULL;
 	str htext = STR_NULL;
 	if(fixup_get_svalue(msg, (gparam_t*)piname, &iname)<0) {
 		LM_ERR("failed to get iterator name\n");
 		return -1;
 	}
 	if(fixup_get_svalue(msg, (gparam_t*)phtext, &htext)<0) {
 		LM_ERR("failed to get header text\n");
 		return -1;
 	}
 
 	return ki_hf_iterator_append(msg, &iname, &htext);
 }
 
d65adfb7
 /**
  *
  */
 static int ki_hf_iterator_insert(sip_msg_t *msg, str *iname, str *htext)
 {
 	int k;
 	sr_lump_t *anchor;
 	str sval = STR_NULL;
 
 	k = ki_hf_iterator_index(msg, iname);
 	if(k<0 || _hf_iterators[k].it==NULL) {
 		return -1;
 	}
 	anchor = anchor_lump(msg, _hf_iterators[k].it->name.s - msg->buf, 0, 0);
 	if (anchor==0) {
 		LM_ERR("cannot insert hdr after %.*s\n", _hf_iterators[k].it->name.len,
 				_hf_iterators[k].it->name.s);
 		return -1;
 	}
 	sval.s = (char*)pkg_malloc(htext->len + 1);
 	if(sval.s==NULL) {
 		LM_ERR("failed to insert hdr after %.*s\n", _hf_iterators[k].it->name.len,
 				_hf_iterators[k].it->name.s);
 		return -1;
 	}
 	memcpy(sval.s, htext->s, htext->len);
 	sval.len = htext->len;
 	sval.s[sval.len] = '\0';
 
 	if (insert_new_lump_before(anchor, sval.s, sval.len, 0) == 0) {
 		LM_ERR("cannot insert lump\n");
 		pkg_free(sval.s);
 		return -1;
 	}
 	return 1;
 }
 
 /**
  *
  */
 static int w_hf_iterator_insert(sip_msg_t *msg, char *piname, char *phtext)
 {
 	str iname = STR_NULL;
 	str htext = STR_NULL;
 	if(fixup_get_svalue(msg, (gparam_t*)piname, &iname)<0) {
 		LM_ERR("failed to get iterator name\n");
 		return -1;
 	}
 	if(fixup_get_svalue(msg, (gparam_t*)phtext, &htext)<0) {
 		LM_ERR("failed to get header text\n");
 		return -1;
 	}
 
 	return ki_hf_iterator_insert(msg, &iname, &htext);
 }
 
ba3ce508
 /**
  *
  */
 static int pv_parse_hf_iterator_name(pv_spec_t *sp, str *in)
 {
 	if(in->len<=0) {
 		return -1;
 	}
 
 	sp->pvp.pvn.u.isname.name.s.s = in->s;
 	sp->pvp.pvn.u.isname.name.s.len = in->len;
 	sp->pvp.pvn.u.isname.type = 0;
 	sp->pvp.pvn.type = PV_NAME_INTSTR;
 
 	return 0;
 }
 
 /**
  *
  */
 static int pv_get_hf_iterator_hname(sip_msg_t *msg, pv_param_t *param, pv_value_t *res)
 {
 	int i;
 	int k;
 	str *iname;
 
 	iname = &param->pvn.u.isname.name.s;
 	k = -1;
 	for(i=0; i<HF_ITERATOR_SIZE; i++) {
 		if(_hf_iterators[i].name.len>0) {
 			if(_hf_iterators[i].name.len==iname->len
 					&& strncmp(_hf_iterators[i].name.s, iname->s, iname->len)==0) {
 				k = i;
 				break;
 			}
 		}
 	}
 	if(k==-1) {
 		LM_ERR("iterator not available [%.*s]\n", iname->len, iname->s);
 		return pv_get_null(msg, param, res);
 	}
 
 	if(_hf_iterators[i].it==NULL) {
 		return pv_get_null(msg, param, res);
 	}
 	return pv_get_strval(msg, param, res, &_hf_iterators[i].it->name);
 }
 
 /**
  *
  */
 static int pv_get_hf_iterator_hbody(sip_msg_t *msg, pv_param_t *param, pv_value_t *res)
 {
 	int i;
 	int k;
 	str *iname;
 
 	iname = &param->pvn.u.isname.name.s;
 	k = -1;
 	for(i=0; i<HF_ITERATOR_SIZE; i++) {
 		if(_hf_iterators[i].name.len>0) {
 			if(_hf_iterators[i].name.len==iname->len
 					&& strncmp(_hf_iterators[i].name.s, iname->s, iname->len)==0) {
 				k = i;
 				break;
 			}
 		}
 	}
 	if(k==-1) {
 		LM_ERR("iterator not available [%.*s]\n", iname->len, iname->s);
 		return pv_get_null(msg, param, res);
 	}
 
 	if(_hf_iterators[i].it==NULL) {
 		return pv_get_null(msg, param, res);
 	}
 	return pv_get_strval(msg, param, res, &_hf_iterators[i].it->body);
 }
 
0a13b912
 
 /*** body line iterator */
 #define BL_ITERATOR_SIZE	4
 #define BL_ITERATOR_NAME_SIZE	32
 
 typedef struct bl_iterator {
 	str name;
 	char bname[HF_ITERATOR_NAME_SIZE];
 	str body;
 	str it;
 	int eob;
 } bl_iterator_t;
 
 static bl_iterator_t _bl_iterators[BL_ITERATOR_SIZE];
 
 /**
  *
  */
 static void bl_iterator_init(void)
 {
 	memset(_bl_iterators, 0, BL_ITERATOR_SIZE*sizeof(bl_iterator_t));
 }
 
 /**
  *
  */
 static int ki_bl_iterator_start(sip_msg_t *msg, str *iname)
 {
 	int i;
 	int k;
 
 	k = -1;
 	for(i=0; i<BL_ITERATOR_SIZE; i++) {
 		if(_bl_iterators[i].name.len>0) {
 			if(_bl_iterators[i].name.len==iname->len
 					&& strncmp(_bl_iterators[i].name.s, iname->s, iname->len)==0) {
 				k = i;
 				break;
 			}
 		} else {
 			if(k==-1) k = i;
 		}
 	}
 	if(k==-1) {
 		LM_ERR("no iterator available - max number is %d\n", BL_ITERATOR_SIZE);
 		return -1;
 	}
 	if(_bl_iterators[k].name.len<=0) {
 		if(iname->len>=BL_ITERATOR_NAME_SIZE) {
 			LM_ERR("iterator name is too big [%.*s] (max %d)\n",
 					iname->len, iname->s, BL_ITERATOR_NAME_SIZE);
 			return -1;
 		}
 		strncpy(_bl_iterators[k].bname, iname->s, iname->len);
 		_bl_iterators[k].bname[iname->len] = '\0';
 		_bl_iterators[k].name.len = iname->len;
 		_bl_iterators[k].name.s = _bl_iterators[k].bname;
 	}
 	_bl_iterators[k].it.s = NULL;
 	_bl_iterators[k].it.len = 0;
 	_bl_iterators[k].eob = 0;
 	_bl_iterators[k].body.s = get_body(msg);
 	if(_bl_iterators[k].body.s==NULL) {
 		LM_DBG("no message body\n");
 		return -1;
 	}
 	_bl_iterators[k].body.len = msg->buf + msg->len - _bl_iterators[k].body.s;
 	return 1;
 }
 
 /**
  *
  */
 static int w_bl_iterator_start(sip_msg_t *msg, char *piname, char *p2)
 {
 	str iname = STR_NULL;
 	if(fixup_get_svalue(msg, (gparam_t*)piname, &iname)<0) {
 		LM_ERR("failed to get iterator name\n");
 		return -1;
 	}
 	return ki_bl_iterator_start(msg, &iname);
 }
 
 /**
  *
  */
 static int ki_bl_iterator_next(sip_msg_t *msg, str *iname)
 {
 	int i;
 	int k;
 	char *p;
 
 	k = -1;
 	for(i=0; i<BL_ITERATOR_SIZE; i++) {
 		if(_bl_iterators[i].name.len>0) {
 			if(_bl_iterators[i].name.len==iname->len
 					&& strncmp(_bl_iterators[i].name.s, iname->s, iname->len)==0) {
 				k = i;
 				break;
 			}
 		}
 	}
 	if(k==-1) {
 		LM_ERR("iterator not available [%.*s]\n", iname->len, iname->s);
 		return -1;
 	}
 	if(_bl_iterators[k].eob == 1) {
 		return -1;
 	}
 
 	if(_bl_iterators[k].it.s==NULL) {
 		_bl_iterators[k].it.s = _bl_iterators[k].body.s;
 	}
 	p = _bl_iterators[k].it.s + _bl_iterators[k].it.len;
 	if(p>=_bl_iterators[k].body.s + _bl_iterators[k].body.len) {
 		_bl_iterators[k].it.s = NULL;
 		_bl_iterators[k].it.len = 0;
 		_bl_iterators[k].eob = 1;
 		return -1;
 	}
 	_bl_iterators[k].it.s = p;
 	while(p < _bl_iterators[k].body.s + _bl_iterators[k].body.len) {
 		if(*p=='\n') {
 			break;
 		}
 		p++;
 	}
 	_bl_iterators[k].it.len = p - _bl_iterators[k].it.s + 1;
 
 	return 1;
 }
 
 /**
  *
  */
 static int w_bl_iterator_next(sip_msg_t *msg, char *piname, char *p2)
 {
 	str iname = STR_NULL;
 	if(fixup_get_svalue(msg, (gparam_t*)piname, &iname)<0) {
 		LM_ERR("failed to get iterator name\n");
 		return -1;
 	}
 	return ki_bl_iterator_next(msg, &iname);
 }
 
 /**
  *
  */
 static int ki_bl_iterator_end(sip_msg_t *msg, str *iname)
 {
 	int i;
 	int k;
 
 	k = -1;
 	for(i=0; i<BL_ITERATOR_SIZE; i++) {
 		if(_bl_iterators[i].name.len>0) {
 			if(_bl_iterators[i].name.len==iname->len
 					&& strncmp(_bl_iterators[i].name.s, iname->s, iname->len)==0) {
 				k = i;
 				break;
 			}
 		}
 	}
 	if(k==-1) {
 		LM_ERR("iterator not available [%.*s]\n", iname->len, iname->s);
 		return -1;
 	}
 	_bl_iterators[k].it.s = NULL;
 	_bl_iterators[k].it.len = 0;
 	_bl_iterators[k].body.s = NULL;
 	_bl_iterators[k].body.len = 0;
 	_bl_iterators[k].eob = 0;
 	return 1;
 }
 
 /**
  *
  */
 static int w_bl_iterator_end(sip_msg_t *msg, char *piname, char *p2)
 {
 	str iname = STR_NULL;
 	if(fixup_get_svalue(msg, (gparam_t*)piname, &iname)<0) {
 		LM_ERR("failed to get iterator name\n");
 		return -1;
 	}
 	return ki_bl_iterator_end(msg, &iname);
 }
 
 /**
  *
  */
 static int ki_bl_iterator_index(sip_msg_t *msg, str *iname)
 {
 	int i;
 	int k;
 
 	k = -1;
 	for(i=0; i<BL_ITERATOR_SIZE; i++) {
 		if(_bl_iterators[i].name.len>0) {
 			if(_bl_iterators[i].name.len==iname->len
 					&& strncmp(_bl_iterators[i].name.s, iname->s, iname->len)==0) {
 				k = i;
 				break;
 			}
 		}
 	}
 	if(k==-1) {
 		LM_ERR("iterator not available [%.*s]\n", iname->len, iname->s);
 		return -1;
 	}
 
 	return k;
 }
 
 /**
  *
  */
 static int ki_bl_iterator_rm(sip_msg_t *msg, str *iname)
 {
 	int k;
 	sr_lump_t *anchor;
 
 	k = ki_bl_iterator_index(msg, iname);
 	if(k<0 || _bl_iterators[k].it.s==NULL || _bl_iterators[k].it.len<=0) {
 		return -1;
 	}
 	anchor = del_lump(msg, _bl_iterators[k].it.s - msg->buf,
 			_bl_iterators[k].it.len, 0);
 	if (anchor==0) {
 		LM_ERR("cannot remove line %.*s\n", _bl_iterators[k].it.len,
 				_bl_iterators[k].it.s);
 		return -1;
 	}
 	return 1;
 }
 
 /**
  *
  */
 static int w_bl_iterator_rm(sip_msg_t *msg, char *piname, char *p2)
 {
 	str iname = STR_NULL;
 	if(fixup_get_svalue(msg, (gparam_t*)piname, &iname)<0) {
 		LM_ERR("failed to get iterator name\n");
 		return -1;
 	}
 	return ki_bl_iterator_rm(msg, &iname);
 }
 
 /**
  *
  */
 static int pv_parse_bl_iterator_name(pv_spec_t *sp, str *in)
 {
 	if(in->len<=0) {
 		return -1;
 	}
 
 	sp->pvp.pvn.u.isname.name.s.s = in->s;
 	sp->pvp.pvn.u.isname.name.s.len = in->len;
 	sp->pvp.pvn.u.isname.type = 0;
 	sp->pvp.pvn.type = PV_NAME_INTSTR;
 
 	return 0;
 }
 
 /**
  *
  */
 static int pv_get_bl_iterator_value(sip_msg_t *msg, pv_param_t *param, pv_value_t *res)
 {
 	int i;
 	int k;
 	str *iname;
 
 	iname = &param->pvn.u.isname.name.s;
 	k = -1;
 	for(i=0; i<BL_ITERATOR_SIZE; i++) {
 		if(_bl_iterators[i].name.len>0) {
 			if(_bl_iterators[i].name.len==iname->len
 					&& strncmp(_bl_iterators[i].name.s, iname->s, iname->len)==0) {
 				k = i;
 				break;
 			}
 		}
 	}
 	if(k==-1) {
 		LM_ERR("iterator not available [%.*s]\n", iname->len, iname->s);
 		return pv_get_null(msg, param, res);
 	}
 
 	if(_bl_iterators[i].it.s==NULL) {
 		return pv_get_null(msg, param, res);
 	}
 	return pv_get_strval(msg, param, res, &_bl_iterators[i].it);
 }
 
ba6119e3
 /* select implementation */
b1e49b5a
 static int sel_hf_value(str *res, select_t *s, struct sip_msg *msg)
 { /* dummy */
ba6119e3
 	return 0;
 }
 
 #define _ALLOC_INC_SIZE 1024
 
b1e49b5a
 static int sel_hf_value_name(str *res, select_t *s, struct sip_msg *msg)
 {
 	struct hname_data *hname;
 	struct hdr_field *hf;
ba6119e3
 	str val, hval1, hval2, huri, dummy_name;
 	int r;
b1e49b5a
 	if(!msg) {
ba6119e3
 		struct hdr_field hdr;
 		char buf[50];
 		int i, n;
 
b1e49b5a
 		if(s->params[1].type == SEL_PARAM_STR) {
ba6119e3
 			hname = pkg_malloc(sizeof(*hname));
b1e49b5a
 			if(!hname)
 				return E_OUT_OF_MEM;
ba6119e3
 			memset(hname, 0, sizeof(*hname));
 
b1e49b5a
 			for(i = s->params[1].v.s.len - 1; i > 0; i--) {
 				if(s->params[1].v.s.s[i] == '_')
 					s->params[1].v.s.s[i] = '-';
ba6119e3
 			}
b1e49b5a
 			i = snprintf(buf, sizeof(buf) - 1, "%.*s: X\n",
 					s->params[1].v.s.len, s->params[1].v.s.s);
ba6119e3
 			buf[i] = 0;
 
 			hname->hname = s->params[1].v.s;
b1e49b5a
 			parse_hname2(buf, buf + i, &hdr);
ba6119e3
 
7987122d
 			if(hdr.type == HDR_ERROR_T) {
 				pkg_free(hname);
b1e49b5a
 				return E_CFG;
7987122d
 			}
ba6119e3
 			hname->htype = hdr.type;
 
 			s->params[1].v.p = hname;
 			s->params[1].type = SEL_PARAM_PTR;
b1e49b5a
 		} else {
ba6119e3
 			hname = s->params[1].v.p;
 		}
b1e49b5a
 		n = s->param_offset[select_level + 1]
 			- s->param_offset
 					  [select_level]; /* number of values before NESTED */
 		if(n > 2 && s->params[2].type == SEL_PARAM_INT) {
ba6119e3
 			hname->idx = s->params[2].v.i;
 			hname->flags |= HNF_IDX;
b1e49b5a
 			if(hname->idx < -MAX_HF_VALUE_STACK) {
 				LM_ERR("index cannot be lower than %d\n", -MAX_HF_VALUE_STACK);
ba6119e3
 				return E_CFG;
 			}
b1e49b5a
 			if(hname->idx == 0)
ba6119e3
 				hname->idx = 1;
 			i = 3;
b1e49b5a
 		} else {
ba6119e3
 			i = 2;
 			hname->idx = 1;
 		}
b1e49b5a
 		if(n > i && s->params[i].type == SEL_PARAM_STR) {
ba6119e3
 			hname->param = s->params[i].v.s;
b1e49b5a
 			for(i = hname->param.len - 1; i > 0; i--) {
 				if(hname->param.s[i] == '_')
 					hname->param.s[i] = '-';
ba6119e3
 			}
 		}
 		s->params[1].v.p = hname;
 		s->params[1].type = SEL_PARAM_PTR;
 		hname->oper = hnoGetValue;
 
 		return 0;
 	}
 
 	res->len = 0;
 	res->s = 0;
 	hname = s->params[1].v.p;
 
b1e49b5a
 	switch(hname->oper) {
ba6119e3
 		case hnoGetValueUri:
b1e49b5a
 			if(hname->flags & HNF_ALL || (hname->flags & HNF_IDX) == 0) {
ba6119e3
 				char *buf = NULL;
 				int buf_len = 0;
b1e49b5a
 
ba6119e3
 				hf = 0;
 				do {
 					r = find_next_hf(msg, hname, &hf);
b1e49b5a
 					if(r < 0)
 						break;
 					if(hf) {
ba6119e3
 						char *p;
 						str huri;
 						hval2.len = 0;
 						p = hf->body.s;
 						do {
b1e49b5a
 							r = find_next_value(&p, hf->body.s + hf->body.len,
 									&hval1, &hval2);
 							get_uri_and_skip_until_params(
 									&hval1, &dummy_name, &huri);
 							if(huri.len) {
 								/* TODO: normalize uri, lowercase except quoted params, add/strip < > */
 								if(*huri.s == '<') {
ba6119e3
 									huri.s++;
 									huri.len -= 2;
 								}
 							}
b1e49b5a
 							if(res->len == 0) {
 								*res = huri; /* first value, if is also last value then we don't need any buffer */
 							} else {
 								if(buf) {
 									if(res->len + huri.len + 1 > buf_len) {
 										buf_len = res->len + huri.len + 1
 												  + _ALLOC_INC_SIZE;
ba6119e3
 										res->s = pkg_realloc(buf, buf_len);
b1e49b5a
 										if(!res->s) {
ba6119e3
 											pkg_free(buf);
b1e49b5a
 											LM_ERR("cannot realloc buffer\n");
ba6119e3
 											res->len = 0;
 											return E_OUT_OF_MEM;
 										}
 										buf = res->s;
 									}
b1e49b5a
 								} else {
ba6119e3
 									/* 2nd value */
b1e49b5a
 									buf_len = res->len + huri.len + 1
 											  + _ALLOC_INC_SIZE;
ba6119e3
 									buf = pkg_malloc(buf_len);
b1e49b5a
 									if(!buf) {
 										LM_ERR("out of memory\n");
ba6119e3
 										res->len = 0;
 										return E_OUT_OF_MEM;
 									}
 									/* copy 1st value */
b1e49b5a
 									memcpy(buf, res->s, res->len);
ba6119e3
 									res->s = buf;
 								}
 								res->s[res->len] = ',';
 								res->len++;
b1e49b5a
 								if(huri.len) {
 									memcpy(res->s + res->len, huri.s, huri.len);
ba6119e3
 									res->len += huri.len;
 								}
 							}
b1e49b5a
 
 						} while(r);
ba6119e3
 					}
b1e49b5a
 				} while(hf);
 				if(buf) {
ba6119e3
 					res->s = get_static_buffer(res->len);
b1e49b5a
 					if(!res->s) {
ba6119e3
 						pkg_free(buf);
 						res->len = 0;
b1e49b5a
 						LM_ERR("cannot allocate static buffer\n");
ba6119e3
 						return E_OUT_OF_MEM;
 					}
 					memcpy(res->s, buf, res->len);
 					pkg_free(buf);
 				}
b1e49b5a
 			} else {
ba6119e3
 				r = find_hf_value_idx(msg, hname, &hf, &hval1, &hval2);
b1e49b5a
 				if(r > 0) {
ba6119e3
 					get_uri_and_skip_until_params(&hval1, &dummy_name, res);
b1e49b5a
 					if(res->len && *res->s == '<') {
 						res->s++; /* strip < & > */
 						res->len -= 2;
ba6119e3
 					}
 				}
 			}
 			break;
 		case hnoGetValueName:
b1e49b5a
 			if((hname->flags & HNF_ALL) == 0) {
ba6119e3
 				r = find_hf_value_idx(msg, hname, &hf, &hval1, &hval2);
b1e49b5a
 				if(r > 0) {
ba6119e3
 					get_uri_and_skip_until_params(&hval1, res, &dummy_name);
b1e49b5a
 					if(res->len >= 2 && res->s[0] == '\"'
 							&& res->s[res->len - 1] == '\"') {
 						res->s++; /* strip quotes */
 						res->len -= 2;
ba6119e3
 					}
 				}
 			}
 			break;
 		case hnoGetValue:
b1e49b5a
 			if(hname->flags & HNF_ALL || (hname->flags & HNF_IDX) == 0) {
ba6119e3
 				char *buf = NULL;
 				int buf_len = 0;
 
 				hf = 0;
 				do {
 					r = find_next_hf(msg, hname, &hf);
b1e49b5a
 
 					if(r < 0)
 						break;
 					if(hf) {
ba6119e3
 						char *p;
 						hval2.len = 0;
 						p = hf->body.s;
 						do {
b1e49b5a
 							r = find_next_value(&p, hf->body.s + hf->body.len,
 									&hval1, &hval2);
 							if(res->len == 0) {
ba6119e3
 								*res = hval1; /* first value, if is also last value then we don't need any buffer */
b1e49b5a
 							} else {
 								if(buf) {
 									if(res->len + hval1.len + 1 > buf_len) {
 										buf_len = res->len + hval1.len + 1
 												  + _ALLOC_INC_SIZE;
ba6119e3
 										res->s = pkg_realloc(buf, buf_len);
b1e49b5a
 										if(!res->s) {
ba6119e3
 											pkg_free(buf);
b1e49b5a
 											LM_ERR("cannot realloc buffer\n");
ba6119e3
 											res->len = 0;
 											return E_OUT_OF_MEM;
 										}
 										buf = res->s;
 									}
b1e49b5a
 								} else {
ba6119e3
 									/* 2nd value */
b1e49b5a
 									buf_len = res->len + hval1.len + 1
 											  + _ALLOC_INC_SIZE;
ba6119e3
 									buf = pkg_malloc(buf_len);
b1e49b5a
 									if(!buf) {
 										LM_ERR("out of memory\n");
ba6119e3
 										res->len = 0;
 										return E_OUT_OF_MEM;
 									}
 									/* copy 1st value */
b1e49b5a
 									memcpy(buf, res->s, res->len);
ba6119e3
 									res->s = buf;
 								}
 								res->s[res->len] = ',';
 								res->len++;
b1e49b5a
 								if(hval1.len) {
 									memcpy(res->s + res->len, hval1.s,
 											hval1.len);
ba6119e3
 									res->len += hval1.len;
 								}
 							}
b1e49b5a
 						} while(r);
ba6119e3
 					}
b1e49b5a
 				} while(hf);
 				if(buf) {
ba6119e3
 					res->s = get_static_buffer(res->len);
b1e49b5a
 					if(!res->s) {
ba6119e3
 						pkg_free(buf);
 						res->len = 0;
b1e49b5a
 						LM_ERR("cannot allocate static buffer\n");
ba6119e3
 						return E_OUT_OF_MEM;
 					}
 					memcpy(res->s, buf, res->len);
 					pkg_free(buf);
 				}
b1e49b5a
 			} else {
ba6119e3
 				r = find_hf_value_idx(msg, hname, &hf, &hval1, &hval2);
b1e49b5a
 				if(r > 0) {
 					if(hname->param.len) {
ba6119e3
 						str d1, d2;
b1e49b5a
 						get_uri_and_skip_until_params(
 								&hval1, &dummy_name, &huri);
 						if(find_hf_value_param(hname, &hval1, &val, &d1, &d2)) {
ba6119e3
 							*res = val;
 						}
b1e49b5a
 					} else {
ba6119e3
 						*res = hval1;
 					}
 				}
 			}
 			break;
 		case hnoGetValue2:
 			r = find_hf_value_idx(msg, hname, &hf, 0, 0);
b1e49b5a
 			if(r > 0) {
 				if(hname->param.len) {
ba6119e3
 					str d1, d2;
 					char c;
b1e49b5a
 					if(find_hf_value2_param(
 							   hname, &hf->body, &val, &d1, &d2, &c)) {
ba6119e3
 						*res = val;
 					}
b1e49b5a
 				} else {
ba6119e3
 					*res = hf->body;
 				}
 			}
 			break;
 		default:
 			break;
 	}
 	return 0;
 }
 
b1e49b5a
 static int sel_hf_value_name_param_name(
 		str *res, select_t *s, struct sip_msg *msg)
 {
ba6119e3
 	return sel_hf_value_name(res, s, msg);
 }
 
b1e49b5a
 static int sel_hf_value_name_param_name2(
 		str *res, select_t *s, struct sip_msg *msg)
 {
 	if(!msg) { /* eliminate "param" level */
ba6119e3
 		int n;
b1e49b5a
 		n = s->param_offset[select_level + 1] - s->param_offset[select_level];
 		s->params[n - 2] = s->params[n - 1];
ba6119e3
 	}
 	return sel_hf_value_name(res, s, msg);
 }
 
b1e49b5a
 static int sel_hf_value_name_uri(str *res, select_t *s, struct sip_msg *msg)
 {
ba6119e3
 	int r;
 	r = sel_hf_value_name(res, s, msg);
b1e49b5a
 	if(!msg && r == 0) {
 		((struct hname_data *)s->params[1].v.p)->oper = hnoGetValueUri;
ba6119e3
 	}
 	return r;
 }
 
b1e49b5a
 static int sel_hf_value_name_name(str *res, select_t *s, struct sip_msg *msg)
 {
ba6119e3
 	int r;
 	r = sel_hf_value_name(res, s, msg);
b1e49b5a
 	if(!msg && r == 0) {
 		((struct hname_data *)s->params[1].v.p)->oper = hnoGetValueName;
ba6119e3
 	}
 	return r;
 }
 
b1e49b5a
 static int sel_hf_value_exists(str *res, select_t *s, struct sip_msg *msg)
 { /* dummy */
ba6119e3
 	return 0;
 }
 
b1e49b5a
 static int sel_hf_value_exists_param(str *res, select_t *s, struct sip_msg *msg)
 {
ba6119e3
 	static char ret_val[] = "01";
b1e49b5a
 	int r;
ba6119e3
 
b1e49b5a
 	if(!msg) {
ba6119e3
 		r = sel_hf_value_name(res, s, msg);
b1e49b5a
 		if(r == 0)
 			((struct hname_data *)s->params[1].v.p)->oper = hnoIsIncluded;
ba6119e3
 		return r;
 	}
 	r = incexc_hf_value_str_f(msg, s->params[1].v.p, &s->params[2].v.s);
 	res->s = &ret_val[r > 0];
 	res->len = 1;
 
 	return 0;
 }
 
b1e49b5a
 static int sel_hf_value2(str *res, select_t *s, struct sip_msg *msg)
 { /* dummy */
ba6119e3
 	return 0;
 }
 
b1e49b5a
 static int sel_hf_value2_name(str *res, select_t *s, struct sip_msg *msg)
 {
ba6119e3
 	int r;
 	r = sel_hf_value_name(res, s, msg);
b1e49b5a
 	if(!msg && r == 0) {
 		((struct hname_data *)s->params[1].v.p)->oper = hnoGetValue2;
ba6119e3
 	}
 	return r;
 }
 
b1e49b5a
 static int sel_hf_value2_name_param_name(
 		str *res, select_t *s, struct sip_msg *msg)
 {
ba6119e3
 	return sel_hf_value2_name(res, s, msg);
 }
 
 SELECT_F(select_any_nameaddr)
 SELECT_F(select_any_uri)
 SELECT_F(select_anyheader_params)
 
 select_row_t sel_declaration[] = {
b1e49b5a
 		{NULL, SEL_PARAM_STR, STR_STATIC_INIT("hf_value"), sel_hf_value,
 				SEL_PARAM_EXPECTED},
 
 		{sel_hf_value, SEL_PARAM_STR, STR_NULL, sel_hf_value_name,
 				CONSUME_NEXT_INT | OPTIONAL | FIXUP_CALL},
 		{sel_hf_value_name, SEL_PARAM_STR, STR_STATIC_INIT("param"),
 				sel_hf_value_name_param_name2, CONSUME_NEXT_STR | FIXUP_CALL},
 		{sel_hf_value_name, SEL_PARAM_STR, STR_STATIC_INIT("p"),
 				sel_hf_value_name_param_name2, CONSUME_NEXT_STR | FIXUP_CALL},
 		{sel_hf_value_name, SEL_PARAM_STR, STR_STATIC_INIT("uri"),
 				sel_hf_value_name_uri, FIXUP_CALL},
 		{sel_hf_value_name, SEL_PARAM_STR, STR_STATIC_INIT("name"),
 				sel_hf_value_name_name, FIXUP_CALL},
 		{sel_hf_value_name, SEL_PARAM_STR, STR_STATIC_INIT("nameaddr"),
 				select_any_nameaddr,
 				NESTED | CONSUME_NEXT_STR}, /* it duplicates param,p,name,... */
 		{sel_hf_value_name, SEL_PARAM_STR, STR_STATIC_INIT("params"),
 				select_anyheader_params, NESTED},
 
 		{sel_hf_value_name_uri, SEL_PARAM_INT, STR_NULL, select_any_uri,
 				NESTED},
 		{sel_hf_value_name, SEL_PARAM_STR, STR_NULL,
 				sel_hf_value_name_param_name, FIXUP_CALL},
 
 		{NULL, SEL_PARAM_STR, STR_STATIC_INIT("hf_value_exists"),
 				sel_hf_value_exists, CONSUME_NEXT_STR | SEL_PARAM_EXPECTED},
 		{sel_hf_value_exists, SEL_PARAM_STR, STR_NULL,
 				sel_hf_value_exists_param, FIXUP_CALL},
 
 		{NULL, SEL_PARAM_STR, STR_STATIC_INIT("hf_value2"), sel_hf_value2,
 				SEL_PARAM_EXPECTED},
 		{sel_hf_value2, SEL_PARAM_STR, STR_NULL, sel_hf_value2_name,
 				CONSUME_NEXT_INT | OPTIONAL | FIXUP_CALL},
 		{sel_hf_value2_name, SEL_PARAM_STR, STR_STATIC_INIT("params"),
 				select_anyheader_params, NESTED},
 		{sel_hf_value2_name, SEL_PARAM_STR, STR_NULL,
 				sel_hf_value2_name_param_name, FIXUP_CALL},
 		{sel_hf_value2_name_param_name, SEL_PARAM_STR,
 				STR_STATIC_INIT("nameaddr"), select_any_nameaddr, NESTED},
 		{sel_hf_value2_name_param_name, SEL_PARAM_STR, STR_STATIC_INIT("uri"),
 				select_any_uri, NESTED},
 
 		{NULL, SEL_PARAM_INT, STR_NULL, NULL, 0}
ba6119e3
 };
ba64899e
 
 /**
  *
  */
 /* clang-format off */
 static sr_kemi_t sr_kemi_textopsx_exports[] = {
 	{ str_init("textopsx"), str_init("msg_apply_changes"),
 		SR_KEMIP_INT, ki_msg_apply_changes,
 		{ SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE,
 			SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
 	},
0a626a25
 	{ str_init("textopsx"), str_init("msg_set_buffer"),
 		SR_KEMIP_INT, ki_msg_set_buffer,
 		{ SR_KEMIP_STR, SR_KEMIP_NONE, SR_KEMIP_NONE,
 			SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
 	},
ba64899e
 	{ str_init("textopsx"), str_init("change_reply_status"),
 		SR_KEMIP_INT, ki_change_reply_status,
 		{ SR_KEMIP_INT, SR_KEMIP_STR, SR_KEMIP_NONE,
 			SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
 	},
 	{ str_init("textopsx"), str_init("remove_body"),
 		SR_KEMIP_INT, ki_remove_body,
 		{ SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE,
 			SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
 	},
7b95ddda
 	{ str_init("textopsx"), str_init("change_reply_status_code"),
 		SR_KEMIP_INT, ki_change_reply_status_code,
 		{ SR_KEMIP_INT, SR_KEMIP_NONE, SR_KEMIP_NONE,
 			SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
 	},
ba64899e
 	{ str_init("textopsx"), str_init("keep_hf"),
 		SR_KEMIP_INT, ki_keep_hf,
 		{ SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE,
 			SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
 	},
 	{ str_init("textopsx"), str_init("keep_hf_re"),
 		SR_KEMIP_INT, ki_keep_hf_re,
 		{ SR_KEMIP_STR, SR_KEMIP_NONE, SR_KEMIP_NONE,
 			SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
 	},
 	{ str_init("textopsx"), str_init("fnmatch"),
 		SR_KEMIP_INT, ki_fnmatch,
 		{ SR_KEMIP_STR, SR_KEMIP_STR, SR_KEMIP_NONE,
 			SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
 	},
 	{ str_init("textopsx"), str_init("fnmatch_ex"),
 		SR_KEMIP_INT, ki_fnmatch_ex,
 		{ SR_KEMIP_STR, SR_KEMIP_STR, SR_KEMIP_STR,
 			SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
 	},
80bed453
 	{ str_init("textopsx"), str_init("append_hf_value"),
 		SR_KEMIP_INT, ki_append_hf_value,
 		{ SR_KEMIP_STR, SR_KEMIP_STR, SR_KEMIP_NONE,
 			SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
 	},
 	{ str_init("textopsx"), str_init("insert_hf_value"),
 		SR_KEMIP_INT, ki_insert_hf_value,
 		{ SR_KEMIP_STR, SR_KEMIP_STR, SR_KEMIP_NONE,
 			SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
 	},
 	{ str_init("textopsx"), str_init("assign_hf_value"),
 		SR_KEMIP_INT, ki_assign_hf_value,
 		{ SR_KEMIP_STR, SR_KEMIP_STR, SR_KEMIP_NONE,
 			SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
 	},
 	{ str_init("textopsx"), str_init("assign_hf_value2"),
 		SR_KEMIP_INT, ki_assign_hf_value2,
 		{ SR_KEMIP_STR, SR_KEMIP_STR, SR_KEMIP_NONE,
 			SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
 	},
 	{ str_init("textopsx"), str_init("remove_hf_value"),
 		SR_KEMIP_INT, ki_remove_hf_value,
 		{ SR_KEMIP_STR, SR_KEMIP_NONE, SR_KEMIP_NONE,
 			SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
 	},
 	{ str_init("textopsx"), str_init("remove_hf_value2"),
 		SR_KEMIP_INT, ki_remove_hf_value2,
c921556e
 		{ SR_KEMIP_STR, SR_KEMIP_STR, SR_KEMIP_NONE,
80bed453
 			SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
 	},
 	{ str_init("textopsx"), str_init("include_hf_value"),
 		SR_KEMIP_INT, ki_include_hf_value,
 		{ SR_KEMIP_STR, SR_KEMIP_STR, SR_KEMIP_NONE,
 			SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
 	},
 	{ str_init("textopsx"), str_init("exclude_hf_value"),
 		SR_KEMIP_INT, ki_exclude_hf_value,
 		{ SR_KEMIP_STR, SR_KEMIP_STR, SR_KEMIP_NONE,
 			SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
 	},
 	{ str_init("textopsx"), str_init("hf_value_exists"),
 		SR_KEMIP_INT, ki_hf_value_exists,
 		{ SR_KEMIP_STR, SR_KEMIP_STR, SR_KEMIP_NONE,
 			SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
 	},
56e93559
 	{ str_init("textopsx"), str_init("hf_iterator_start"),
 		SR_KEMIP_INT, ki_hf_iterator_start,
 		{ SR_KEMIP_STR, SR_KEMIP_NONE, SR_KEMIP_NONE,
 			SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
 	},
 	{ str_init("textopsx"), str_init("hf_iterator_end"),
 		SR_KEMIP_INT, ki_hf_iterator_end,
 		{ SR_KEMIP_STR, SR_KEMIP_NONE, SR_KEMIP_NONE,
 			SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
 	},
 	{ str_init("textopsx"), str_init("hf_iterator_next"),
 		SR_KEMIP_INT, ki_hf_iterator_next,
 		{ SR_KEMIP_STR, SR_KEMIP_NONE, SR_KEMIP_NONE,
 			SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
 	},
24172c08
 	{ str_init("textopsx"), str_init("hf_iterator_prev"),
 		SR_KEMIP_INT, ki_hf_iterator_prev,
 		{ SR_KEMIP_STR, SR_KEMIP_NONE, SR_KEMIP_NONE,
 			SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
 	},
adc3bc18
 	{ str_init("textopsx"), str_init("hf_iterator_rm"),
 		SR_KEMIP_INT, ki_hf_iterator_rm,
 		{ SR_KEMIP_STR, SR_KEMIP_NONE, SR_KEMIP_NONE,
 			SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
 	},
d65adfb7
 	{ str_init("textopsx"), str_init("hf_iterator_append"),
 		SR_KEMIP_INT, ki_hf_iterator_append,
 		{ SR_KEMIP_STR, SR_KEMIP_STR, SR_KEMIP_NONE,
 			SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
 	},
 	{ str_init("textopsx"), str_init("hf_iterator_insert"),
 		SR_KEMIP_INT, ki_hf_iterator_insert,
 		{ SR_KEMIP_STR, SR_KEMIP_STR, SR_KEMIP_NONE,
 			SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
 	},
0a13b912
 	{ str_init("textopsx"), str_init("bl_iterator_start"),
 		SR_KEMIP_INT, ki_bl_iterator_start,
 		{ SR_KEMIP_STR, SR_KEMIP_NONE, SR_KEMIP_NONE,
 			SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
 	},
 	{ str_init("textopsx"), str_init("bl_iterator_end"),
 		SR_KEMIP_INT, ki_bl_iterator_end,
 		{ SR_KEMIP_STR, SR_KEMIP_NONE, SR_KEMIP_NONE,
 			SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
 	},
 	{ str_init("textopsx"), str_init("bl_iterator_next"),
 		SR_KEMIP_INT, ki_bl_iterator_next,
 		{ SR_KEMIP_STR, SR_KEMIP_NONE, SR_KEMIP_NONE,
 			SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
 	},
 	{ str_init("textopsx"), str_init("bl_iterator_rm"),
 		SR_KEMIP_INT, ki_bl_iterator_rm,
 		{ SR_KEMIP_STR, SR_KEMIP_NONE, SR_KEMIP_NONE,
 			SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
 	},
ba64899e
 
 	{ {0, 0}, {0, 0}, 0, NULL, { 0, 0, 0, 0, 0, 0 } }
 };
 /* clang-format on */
 
 /**
  *
  */
 int mod_register(char *path, int *dlflags, void *p1, void *p2)
 {
 	sr_kemi_modules_add(sr_kemi_textopsx_exports);
 	return 0;
17123659
 }