You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
kamailio/modules/textopsx/textopsx.c

1858 lines
50 KiB

This file contains invisible Unicode characters!

This file contains invisible Unicode characters that may be processed differently from what appears below. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to reveal hidden characters.

/**
*
* Copyright (C) 2001-2003 FhG Fokus
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fnmatch.h>
#include "../../sr_module.h"
#include "../../dprint.h"
#include "../../data_lump.h"
#include "../../msg_translator.h"
#include "../../tcp_options.h"
#include "../../mod_fix.h"
#include "../../parser/parse_hname2.h"
#include "../../select.h"
#include "../../select_buf.h"
#include "api.h"
MODULE_VERSION
static int msg_apply_changes_f(sip_msg_t *msg, char *str1, char *str2);
static int change_reply_status_f(sip_msg_t*, char*, char*);
static int change_reply_status_fixup(void** param, int param_no);
static int w_keep_hf_f(sip_msg_t*, char*, char*);
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);
static int w_remove_body_f(struct sip_msg*, char*, char *);
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);
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);
static int bind_textopsx(textopsx_api_t *tob);
static int mod_init(void);
extern select_row_t sel_declaration[];
/* cfg functions */
static cmd_export_t cmds[] = {
{"msg_apply_changes", (cmd_function)msg_apply_changes_f, 0,
0, REQUEST_ROUTE|ONREPLY_ROUTE },
{"change_reply_status", change_reply_status_f, 2,
change_reply_status_fixup, ONREPLY_ROUTE },
{"remove_body", (cmd_function)w_remove_body_f, 0,
0, ANY_ROUTE },
{"keep_hf", (cmd_function)w_keep_hf_f, 0,
fixup_regexp_null, ANY_ROUTE },
{"keep_hf", (cmd_function)w_keep_hf_f, 1,
fixup_regexp_null, ANY_ROUTE },
{"fnmatch", (cmd_function)w_fnmatch2_f, 2,
fixup_fnmatch, ANY_ROUTE },
{"fnmatch", (cmd_function)w_fnmatch3_f, 3,
fixup_fnmatch, ANY_ROUTE },
{"append_hf_value", insupddel_hf_value_f, 2, append_hf_value_fixup,
REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE},
{"insert_hf_value", insupddel_hf_value_f, 2, insert_hf_value_fixup,
REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE},
{"remove_hf_value", insupddel_hf_value_f, 1, remove_hf_value_fixup,
REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE},
{"assign_hf_value", insupddel_hf_value_f, 2, assign_hf_value_fixup,
REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE},
{"remove_hf_value2", insupddel_hf_value_f, 1, remove_hf_value2_fixup,
REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE},
{"assign_hf_value2", insupddel_hf_value_f, 2, assign_hf_value2_fixup,
REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE},
{"include_hf_value", incexc_hf_value_f, 2, include_hf_value_fixup,
REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE},
{"exclude_hf_value", incexc_hf_value_f, 2, exclude_hf_value_fixup,
REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE},
{"hf_value_exists", incexc_hf_value_f, 2, hf_value_exists_fixup,
REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE},
{"bind_textopsx", (cmd_function)bind_textopsx, 1,
0, ANY_ROUTE },
{0,0,0,0,0}
};
/* module exports structure */
struct module_exports exports= {
"textopsx",
cmds, /* cfg functions */
0, /* RPC methods */
0, /* cfg parameters */
mod_init, /* initialization function */
0, /* response function */
0, /* destroy function */
0, /* on_cancel function */
0 /* per-child init function */
};
/**
* init module function
*/
static int mod_init(void)
{
#ifdef USE_TCP
tcp_set_clone_rcvbuf(1);
#endif
register_select_table(sel_declaration);
return 0;
}
/**
*
*/
static int msg_apply_changes_f(sip_msg_t *msg, char *str1, char *str2)
{
struct dest_info dst;
str obuf;
sip_msg_t tmp;
if(msg->first_line.type!=SIP_REPLY && get_route_type()!=REQUEST_ROUTE)
{
LM_ERR("invalid usage - not in request route\n");
return -1;
}
init_dest_info(&dst);
dst.proto = PROTO_UDP;
if(msg->first_line.type == SIP_REPLY) {
obuf.s = generate_res_buf_from_sip_res(msg,
(unsigned int*)&obuf.len, BUILD_NO_VIA1_UPDATE);
} else {
if(msg->msg_flags & FL_RR_ADDED) {
LM_ERR("cannot apply msg changes after adding record-route"
" header - it breaks conditional 2nd header\n");
return -1;
}
obuf.s = build_req_buf_from_sip_req(msg,
(unsigned int*)&obuf.len, &dst,
BUILD_NO_PATH|BUILD_NO_LOCAL_VIA|BUILD_NO_VIA1_UPDATE);
}
if(obuf.s == NULL)
{
LM_ERR("couldn't update msg buffer content\n");
return -1;
}
if(obuf.len>=BUF_SIZE)
{
LM_ERR("new buffer overflow (%d)\n", obuf.len);
pkg_free(obuf.s);
return -1;
}
/* temporary copy */
memcpy(&tmp, msg, sizeof(sip_msg_t));
/* reset dst uri and path vector to avoid freeing - restored later */
if(msg->dst_uri.s!=NULL)
{
msg->dst_uri.s = NULL;
msg->dst_uri.len = 0;
}
if(msg->path_vec.s!=NULL)
{
msg->path_vec.s = NULL;
msg->path_vec.len = 0;
}
/* free old msg structure */
free_sip_msg(msg);
memset(msg, 0, sizeof(sip_msg_t));
/* restore msg fields */
msg->buf = tmp.buf;
msg->id = tmp.id;
msg->rcv = tmp.rcv;
msg->set_global_address = tmp.set_global_address;
msg->set_global_port = tmp.set_global_port;
msg->flags = tmp.flags;
msg->msg_flags = tmp.msg_flags;
msg->hash_index = tmp.hash_index;
msg->force_send_socket = tmp.force_send_socket;
msg->fwd_send_flags = tmp.fwd_send_flags;
msg->rpl_send_flags = tmp.rpl_send_flags;
msg->dst_uri = tmp.dst_uri;
msg->path_vec = tmp.path_vec;
memcpy(msg->buf, obuf.s, obuf.len);
msg->len = obuf.len;
msg->buf[msg->len] = '\0';
/* free new buffer - copied in the static buffer from old sip_msg_t */
pkg_free(obuf.s);
/* reparse the message */
LM_DBG("SIP message content updated - reparsing\n");
if (parse_msg(msg->buf, msg->len, msg)!=0){
LM_ERR("parsing new sip message failed [[%.*s]]\n",
msg->len, msg->buf);
/* exit config execution - sip_msg_t structure is no longer
* valid/safe for config */
return 0;
}
return 1;
}
/**
*
*/
static int change_reply_status_fixup(void** param, int param_no)
{
if (param_no == 1) {
return fixup_var_int_12(param, param_no);
} else if (param_no == 2)
return fixup_var_pve_str_12(param, param_no);
else
return 0;
}
/**
*
*/
static int change_reply_status_f(struct sip_msg* msg, char* _code, char* _reason)
{
int code;
str reason;
struct lump *l;
char *ch;
if (get_int_fparam(&code, msg, (fparam_t*)_code)
|| get_str_fparam(&reason, msg, (fparam_t*)_reason)
|| (reason.len == 0)
) {
LOG(L_ERR, "ERROR: textops: cannot get parameter\n");
return -1;
}
if ((code < 100) || (code > 699)) {
LOG(L_ERR, "ERROR: textops: wrong status code: %d\n",
code);
return -1;
}
if (((code < 300) || (msg->REPLY_STATUS < 300))
&& (code/100 != msg->REPLY_STATUS/100)
) {
LOG(L_ERR, "ERROR: textops: 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';
l = del_lump(msg,
msg->first_line.u.reply.reason.s - msg->buf,
msg->first_line.u.reply.reason.len,
0);
if (!l) {
LOG(L_ERR, "ERROR: textops(): Failed to add del lump\n");
return -1;
}
/* clone the reason phrase, the lumps need to be pkg allocated */
ch = (char *)pkg_malloc(reason.len);
if (!ch) {
LOG(L_ERR, "ERROR: textops: Not enough memory\n");
return -1;
}
memcpy(ch, reason.s, reason.len);
if (insert_new_lump_after(l, ch, reason.len, 0)==0){
LOG(L_ERR, "ERROR: textops: failed to add new lump: %.*s\n",
reason.len, ch);
pkg_free(ch);
return -1;
}
return 1;
}
/**
*
*/
static int w_remove_body_f(struct sip_msg *msg, char *p1, char *p2)
{
str body = {0,0};
body.len = 0;
body.s = get_body(msg);
if (body.s==0)
{
LM_DBG("no body in the message\n");
return 1;
}
body.len = msg->buf + msg->len - body.s;
if (body.len<=0)
{
LM_DBG("empty body in the message\n");
return 1;
}
if(del_lump(msg, body.s - msg->buf, body.len, 0) == 0)
{
LM_ERR("cannot remove body\n");
return -1;
}
return 1;
}
/**
*
*/
static int w_keep_hf_f(struct sip_msg* msg, char* key, char* foo)
{
struct hdr_field *hf;
regex_t *re;
regmatch_t pmatch;
char c;
struct lump* l;
if(key) {
re = (regex_t*)key;
} else {
re = NULL;
}
/* we need to be sure we have seen all HFs */
parse_headers(msg, HDR_EOH_F, 0);
for (hf=msg->headers; hf; hf=hf->next)
{
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;
default:
;
}
if(re==NULL) {
/* no regex to match => remove all */
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);
return -1;
}
} else {
c = hf->name.s[hf->name.len];
hf->name.s[hf->name.len] = '\0';
if (regexec(re, hf->name.s, 1, &pmatch, 0)!=0)
{
/* no match => remove */
hf->name.s[hf->name.len] = c;
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);
return -1;
}
} else {
hf->name.s[hf->name.len] = c;
}
}
}
return -1;
}
/**
*
*/
static int w_fnmatch(str *val, str *match, str *flags)
{
int i;
i = 0;
#ifdef FNM_CASEFOLD
if(flags && (flags->s[0]=='i' || flags->s[0]=='I'))
i = FNM_CASEFOLD;
#endif
if(fnmatch(match->s, val->s, i)==0)
return 0;
return -1;
}
/**
*
*/
static int w_fnmatch2_f(sip_msg_t *msg, char *val, char *match)
{
str sval;
str smatch;
if(get_str_fparam(&sval, msg, (fparam_t*)val)<0
|| get_str_fparam(&smatch, msg, (fparam_t*)match)<0)
{
LM_ERR("invalid parameters");
return -1;
}
if(w_fnmatch(&sval, &smatch, NULL)<0)
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;
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)
{
LM_ERR("invalid parameters");
return -1;
}
if(w_fnmatch(&sval, &smatch, &sflags)<0)
return -1;
return 1;
}
/**
*
*/
static int fixup_fnmatch(void** param, int param_no)
{
if (param_no == 1) {
return fixup_var_pve_12(param, param_no);
} else if (param_no == 2) {
return fixup_var_pve_12(param, param_no);
} else if (param_no == 3) {
return fixup_var_pve_12(param, param_no);
} else {
return 0;
}
}
/*
* Function to load the textops api.
*/
static int bind_textopsx(textopsx_api_t *tob){
if(tob==NULL){
LM_WARN("textopsx_binds: Cannot load textopsx API into a NULL pointer\n");
return -1;
}
tob->msg_apply_changes = msg_apply_changes_f;
return 0;
}
/**
* functions operating on header value
*/
#define HNF_ALL 0x01
#define HNF_IDX 0x02
#define MAX_HF_VALUE_STACK 10
enum {hnoInsert, hnoAppend, hnoAssign, hnoRemove, hnoInclude, hnoExclude, hnoIsIncluded, hnoGetValue, hnoGetValueUri, hnoGetValueName, hnoRemove2, hnoAssign2, hnoGetValue2};
struct hname_data {
int oper;
int htype;
str hname;
int flags;
int idx;
str param;
};
#define is_space(_p) ((_p) == '\t' || (_p) == '\n' || (_p) == '\r' || (_p) == ' ')
#define eat_spaces(_p) \
while( is_space(*(_p)) ){\
(_p)++;}
#define is_alphanum(_p) (((_p) >= 'a' && (_p) <= 'z') || ((_p) >= 'A' && (_p) <= 'Z') || ((_p) >= '0' && (_p) <= '9') || (_p) == '_' || (_p) == '-')
#define eat_while_alphanum(_p) \
while ( is_alphanum(*(_p)) ) {\
(_p)++; }
static int fixup_hvalue_param(void** param, int param_no) {
return fixup_spve_null(param, 1);
}
static int eval_hvalue_param(sip_msg_t *msg, gparam_t *val, str *s) {
if(fixup_get_svalue(msg, val, s)<0) {
LM_ERR("could not get string param value\n");
return E_UNSPEC;
}
return 1;
}
/* parse: hname [ ([] | [*] | [number]) ] [ "." param ] */
static int fixup_hname_param(char *hname, struct hname_data** h) {
struct hdr_field hdr;
char *savep, savec;
*h = pkg_malloc(sizeof(**h));
if (!*h) return E_OUT_OF_MEM;
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 = ':';
parse_hname2_short((*h)->hname.s, (*h)->hname.s+(*h)->hname.len+1, &hdr);
*hname = savec;
if (hdr.type == HDR_ERROR_T) goto err;
(*h)->htype = hdr.type;
eat_spaces(hname);
savep = hname;
if (*hname == '[') {
hname++;
eat_spaces(hname);
savep = hname;
(*h)->flags |= HNF_IDX;
if (*hname == '*') {
(*h)->flags |= HNF_ALL;
hname++;
}
else if (*hname != ']') {
char* c;
(*h)->idx = strtol(hname, &c, 10);
if (hname == c) goto err;
hname = c;
}
eat_spaces(hname);
savep = hname;
if (*hname != ']') goto err;
hname++;
}
eat_spaces(hname);
savep = hname;
if (*hname == '.') {
hname++;
eat_spaces(hname);
savep = hname;
(*h)->param.s = hname;
eat_while_alphanum(hname);
(*h)->param.len = hname-(*h)->param.s;
if ((*h)->param.len == 0) goto err;
}
else {
(*h)->param.s = hname;
}
savep = hname;
if (*hname != '\0') goto err;
(*h)->hname.s[(*h)->hname.len] = '\0';
(*h)->param.s[(*h)->param.len] = '\0';
return 0;
err:
pkg_free(*h);
LOG(L_ERR, "ERROR: textops: cannot parse header near '%s'\n", savep);
return E_CFG;
}
static int fixup_hname_str(void** param, int param_no) {
if (param_no == 1) {
struct hname_data* h;
int res = fixup_hname_param(*param, &h);
if (res < 0) return res;
*param = h;
}
else if (param_no == 2) {
return fixup_hvalue_param(param, param_no);
}
return 0;
}
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) {
LOG(L_ERR, "ERROR: textops: find_next_hf: Error while parsing message\n");
return -1;
}
*hf = msg->headers;
}
else {
*hf = (*hf)->next;
}
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)
return 1;
}
else if (hname->htype == (*hf)->type) {
return 1;
}
}
return 0;
}
static int find_next_value(char** start, char* end, str* val, str* lump_val) {
int quoted = 0;
lump_val->s = *start;
while (*start < end && is_space(**start) ) (*start)++;
val->s = *start;
while ( *start < end && (**start != ',' || quoted) ) {
if (**start == '\"' && (!quoted || (*start)[-1]!='\\') )
quoted = ~quoted;
(*start)++;
}
val->len = *start - val->s;
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"
if (val->len >= 2 && val->s[0] == '\"' && val->s[val->len-1] == '\"') {
val->s++;
val->len -= 2;
}
*/
while (*start < end && **start != ',') (*start)++;
if (*start < end) {
(*start)++;
}
lump_val->len = *start - lump_val->s;
return (*start < end);
}
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 */
lump_val->s--;
lump_val->len++;
}
}
}
static int find_hf_value_idx(struct sip_msg* msg, struct hname_data* hname, struct hdr_field** hf, str* val, str* lump_val) {
int res;
char *p;
if ( hname->flags & HNF_ALL || hname->idx == 0) return -1;
*hf = 0;
if (hname->idx > 0) {
int idx;
idx = hname->idx;
do {
res = find_next_hf(msg, hname, hf);
if (res < 0) return -1;
if (*hf) {
if (val) {
lump_val->len = 0;
p = (*hf)->body.s;
do {
res = find_next_value(&p, (*hf)->body.s+(*hf)->body.len, val, lump_val);
idx--;
} while (res && idx);
}
else {
idx--;
}
}
} while (*hf && idx);
}
else if (hname->idx < 0) { /* search from the bottom */
struct hf_value_stack {
str val, lump_val;
struct hdr_field* hf;
} stack[MAX_HF_VALUE_STACK];
int stack_pos, stack_num;
if ( -hname->idx > MAX_HF_VALUE_STACK ) return -1;
stack_pos = stack_num = 0;
do {
res = find_next_hf(msg, hname, hf);
if (res < 0) return -1;
if (*hf) {
stack[stack_pos].lump_val.len = 0;
p = (*hf)->body.s;
do {
stack[stack_pos].hf = *hf;
if (val)
res = find_next_value(&p, (*hf)->body.s+(*hf)->body.len, &stack[stack_pos].val, &stack[stack_pos].lump_val);
else
res = 0;
stack_pos++;
if (stack_pos >= MAX_HF_VALUE_STACK)
stack_pos = 0;
if (stack_num < MAX_HF_VALUE_STACK)
stack_num++;
} while (res);
}
} while (*hf);
if (-hname->idx <= stack_num) {
stack_pos += hname->idx;
if (stack_pos < 0)
stack_pos += MAX_HF_VALUE_STACK;
*hf = stack[stack_pos].hf;
if (val) {
*val = stack[stack_pos].val;
*lump_val = stack[stack_pos].lump_val;
}
}
else {
*hf = 0;
}
}
else
return -1;
return *hf?1:0;
}
static int find_hf_value_param(struct hname_data* hname, str* param_area, str* value, str* lump_upd, str* lump_del) {
int i, j, found;
i = 0;
while (1) {
lump_del->s = param_area->s + i;
for (; i < param_area->len && is_space(param_area->s[i]); i++);
if (i < param_area->len && param_area->s[i] == ';') { /* found a param ? */
i++;
for (; i < param_area->len && is_space(param_area->s[i]); i++);
j = i;
for (; i < param_area->len && !is_space(param_area->s[i]) && param_area->s[i]!='=' && param_area->s[i]!=';'; i++);
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;
value->len = 0;
for (; i < param_area->len && is_space(param_area->s[i]); i++);
if (i < param_area->len && param_area->s[i]=='=') {
i++;
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]=='\"') {
i++;
value->s++;
for (; i<param_area->len; i++) {
if (param_area->s[i]=='\"') {
i++;
break;
}
value->len++;
}
}
else {
for (; i<param_area->len && !is_space(param_area->s[i]) && param_area->s[i]!=';'; i++, value->len++);
}
}
}
if (found) {
lump_del->len = param_area->s+i - lump_del->s;
lump_upd->len = param_area->s+i - lump_upd->s;
return 1;
}
}
else { /* not found, return last correct position, should be end of param area */
lump_del->len = 0;
return 0;
}
}
}
/* parse: something param_name=param_value something [ "," something param_name="param_value" ....]
* 'something' is required by Authenticate
*/
static int find_hf_value2_param(struct hname_data* hname, str* param_area, str* value, str* lump_upd, str* lump_del, char* delim) {
int i, j, k, found, comma_flag;
i = 0;
*delim = 0;
lump_del->len = 0;
while (i < param_area->len) {
lump_del->s = param_area->s + i;
while (i<param_area->len && is_space(param_area->s[i])) i++;
comma_flag = i < param_area->len && param_area->s[i] == ',';
if (comma_flag) i++;
while (i<param_area->len && is_space(param_area->s[i])) i++;
if (i < param_area->len && is_alphanum(param_area->s[i])) { /* found a param name ? */
j = i;
if (!*delim) *delim = ' ';
while (i<param_area->len && is_alphanum(param_area->s[i])) i++;
k = i;
while (i<param_area->len && is_space(param_area->s[i])) i++;
lump_upd->s = param_area->s + i;
if (i < param_area->len && param_area->s[i] == '=') { /* if equal then it's the param */
*delim = ',';
i++;
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++;
value->s = param_area->s+i;
value->len = 0;
if (i < param_area->len) {
if (param_area->s[i]=='\"') {
i++;
value->s++;
for (; i<param_area->len; i++) {
if (param_area->s[i]=='\"') {
i++;
break;
}
value->len++;
}
}
else {
for (; i<param_area->len && !is_space(param_area->s[i]) && param_area->s[i]!=','; i++, value->len++);
}
}
if (found) {
lump_upd->len = param_area->s+i - lump_upd->s;
lump_del->len = param_area->s+i - lump_del->s;
while (i<param_area->len && is_space(param_area->s[i])) i++;
if (!comma_flag && i < param_area->len && param_area->s[i]==',') {
i++;
lump_del->len = param_area->s+i - lump_del->s;
}
return 1;
}
}
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++;
}
}
lump_del->s = param_area->s + i;
return 0;
}
static int insert_header_lump(struct sip_msg* msg, char* msg_position, int lump_before, str* hname, str *val) {
struct lump* anchor;
char *s;
int len;
anchor = anchor_lump(msg, msg_position - msg->buf, 0, 0);
if (anchor == 0) {
LOG(L_ERR, "ERROR: textops: insert_header_lump(): Can't get anchor\n");
return -1;
}
len=hname->len+2+val->len+2;
s = (char*)pkg_malloc(len);
if (!s) {
LOG(L_ERR, "ERROR: textops: insert_header_lump(): not enough memory\n");
return -1;
}
memcpy(s, hname->s, hname->len);
s[hname->len] = ':';
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) {
LOG(L_ERR, "ERROR: textops: insert_header_lump(): Can't insert lump\n");
pkg_free(s);
return -1;
}
return 1;
}
static int insert_value_lump(struct sip_msg* msg, struct hdr_field* hf, char* msg_position, int lump_before, str *val) {
struct lump* anchor;
char *s;
int len;
anchor = anchor_lump(msg, msg_position - msg->buf, 0, 0);
if (anchor == 0) {
LOG(L_ERR, "ERROR: textops: insert_value_lump(): Can't get anchor\n");
return -1;
}
len=val->len+1;
s = (char*)pkg_malloc(len);
if (!s) {
LOG(L_ERR, "ERROR: textops: insert_value_lump(): not enough memory\n");
return -1;
}
if (!hf) {
memcpy(s, val->s, val->len);
len--;
}
else if (msg_position == hf->body.s+hf->body.len) {
s[0] = ',';
memcpy(s+1, val->s, val->len);
}
else {
memcpy(s, val->s, val->len);
s[val->len] = ',';
}
if ( (lump_before?insert_new_lump_before(anchor, s, len, 0):insert_new_lump_after(anchor, s, len, 0)) == 0) {
LOG(L_ERR, "ERROR: textops: insert_value_lump(): Can't insert lump\n");
pkg_free(s);
return -1;
}
return 1;
}
static int delete_value_lump(struct sip_msg* msg, struct hdr_field* hf, str *val) {
struct lump* l;
/* TODO: check already existing lumps */
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);
else
l=del_lump(msg, val->s-msg->buf, val->len, 0);
if (l==0) {
LOG(L_ERR, "ERROR: textops: delete_value_lump: not enough memory\n");
return -1;
}
return 1;
}
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;
str val, hval1, hval2;
char *p;
int res;
val = *_pval;
if (!val.len) return -1;
hf = 0;
lump_hf = 0;
while (1) {
if (find_next_hf(msg, hname, &hf) < 0) return -1;
if (!hf) break;
hval2.len = 0;
p = hf->body.s;
do {
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) {
case hnoIsIncluded:
case hnoInclude:
return 1;
case hnoExclude:
adjust_lump_val_for_delete(hf, &hval2);
delete_value_lump(msg, hf, &hval2);
default:
break;
}
}
} while (res);
switch (hname->oper) {
case hnoInclude:
if (!lump_hf) {
lump_hf = hf;
}
break;
default:
break;
}
}
switch (hname->oper) {
case hnoIsIncluded:
return -1;
case hnoInclude:
if (lump_hf)
return insert_value_lump(msg, lump_hf, lump_hf->body.s+lump_hf->body.len, 1, &val);
else
return insert_header_lump(msg, msg->unparsed, 1, &hname->hname, &val);
default:
return 1;
}
}
static int incexc_hf_value_f(struct sip_msg* msg, char* _hname, char* _val)
{
str val;
int res;
res = eval_hvalue_param(msg, (void*) _val, &val);
if (res < 0) return res;
if (!val.len) return -1;
return incexc_hf_value_str_f(msg, _hname, &val);
}
#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 ) { \
LOG(L_ERR, "ERROR: textops: neither index nor param may be specified in '%s'\n", p);\
return E_CFG;\
}\
((struct hname_data*)*param)->oper = _oper;\
}\
return 0;\
}
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)
static void get_uri_and_skip_until_params(str *param_area, str *name, str *uri) {
int i, quoted, uri_pos, uri_done;
name->len = 0;
uri->len = 0;
uri->s = 0;
uri_done = 0;
name->s = param_area->s;
for (i=0; i<param_area->len && param_area->s[i]!=';'; ) { /* [ *(token LSW)/quoted-string ] "<" addr-spec ">" | addr-spec */
/* skip name */
for (quoted=0, uri_pos=i; i<param_area->len; i++) {
if (!quoted) {
if (param_area->s[i] == '\"') {
quoted = 1;
uri_pos = -1;
}
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;
}
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;
}
/* skip uri */
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;
uri->len = 0;
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;
uri_done = 1;
break;
}
}
else if (param_area->s[i] == '\"' && param_area->s[i-1] != '\\') quoted = 0;
}
}
}
param_area->s+= i;
param_area->len-= i;
if (uri->s == name->s)
name->len = 0;
}
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) {
int len, i;
char *s;
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)) {
LOG(L_ERR, "ERROR: textops: assign_hf_do_lumping: not enough memory\n");
return -1;
}
}
if (value && value->len) {
anchor = anchor_lump(msg, lump_upd->s - msg->buf, 0, 0);
if (anchor == 0) {
LOG(L_ERR, "ERROR: textops: assign_hf_do_lumping: Can't get anchor\n");
return -1;
}
len = 1+value->len;
s = pkg_malloc(len);
if (!s) {
LOG(L_ERR, "ERROR: textops: assign_hf_do_lumping: not enough memory\n");
return -1;
}
s[0]='=';
memcpy(s+1, value->s, value->len);
if ( (insert_new_lump_before(anchor, s, len, 0)) == 0) {
LOG(L_ERR, "ERROR: textops: assign_hf_do_lumping: Can't insert lump\n");
pkg_free(s);
return -1;
}
}
}
else {
if (!value) return -1;
anchor = anchor_lump(msg, lump_del->s - msg->buf, 0, 0);
if (anchor == 0) {
LOG(L_ERR, "ERROR: textops: assign_hf_do_lumping: Can't get anchor\n");
return -1;
}
len = 1+hname->param.len+(value->len?value->len+1:0);
s = pkg_malloc(len);
if (!s) {
LOG(L_ERR, "ERROR: textops: assign_hf_do_lumping: not enough memory\n");
return -1;
}
if (delim) {
s[0] = delim;
i = 1;
}
else {
i = 0;
len--;
}
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);
}
if ( (insert_new_lump_before(anchor, s, len, 0)) == 0) {
LOG(L_ERR, "ERROR: textops: assign_hf_do_lumping: Can't insert lump\n");
pkg_free(s);
return -1;
}
}
return 1;
}
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;
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 {
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)
res = r2;
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;
}
} while (!value && r);
return res;
}
static int assign_hf_process2_params(struct sip_msg* msg, struct hdr_field* hf, struct hname_data* hname, str* value) {
int r, r2, res = 0;
str param_area, lump_upd, lump_del, dummy_val;
char delim;
param_area = hf->body;
do {
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)
res = r2;
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;
}
} while (!value && r);
return res;
}
static int insupddel_hf_value_f(struct sip_msg* msg, char* _hname, char* _val) {
struct hname_data* hname = (void*) _hname;
struct hdr_field* hf;
str val = {0};
str hval1, hval2;
int res;
if (_val) {
res = eval_hvalue_param(msg, (void*) _val, &val);
if (res < 0) return res;
}
switch (hname->oper) {
case hnoAppend:
if ((hname->flags & HNF_IDX) == 0) {
if (parse_headers(msg, HDR_EOH_F, 0) == -1) {
LOG(L_ERR, "ERROR: textops: Error while parsing message\n");
return -1;
}
return insert_header_lump(msg, msg->unparsed, 1, &hname->hname, &val);
}
else {
res = find_hf_value_idx(msg, hname, &hf, &hval1, &hval2);
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);
}
}
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);
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) {
return insert_value_lump(msg, hf, hval2.s, 1, &val);
}
else
return -1;
case hnoRemove:
case hnoAssign:
if (hname->flags & HNF_ALL) {
struct hdr_field* hf = 0;
int fl = -1;
do {
res = find_next_hf(msg, hname, &hf);
if (res < 0) return res;
if (hf) {
if (!hname->param.len) {
fl = 1;
delete_value_lump(msg, hf, &hf->body);
}
else {
char *p;
hval2.len = 0;
p = hf->body.s;
do {
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)
fl = 1;
} while (res);
}
}
} while (hf);
return fl;
}
else {
res = find_hf_value_idx(msg, hname, &hf, &hval1, &hval2);
if (res < 0) return res;
if (hf) {
if (!hname->param.len) {
if (hname->oper == hnoRemove) {
adjust_lump_val_for_delete(hf, &hval2);
return delete_value_lump(msg, hf, &hval2);
}
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);
}
return 1;
}
}
else {
return assign_hf_process_params(msg, hf, hname, _val?&val:0, &hval1);
}
}
}
break;
case hnoRemove2:
case hnoAssign2:
if (hname->flags & HNF_ALL) {
struct hdr_field* hf = 0;
int fl = -1;
do {
res = find_next_hf(msg, hname, &hf);
if (res < 0) return res;
if (hf) {
if (!hname->param.len) { /* the same as hnoRemove/hnoAssign */
fl = 1;
delete_value_lump(msg, hf, &hf->body);
}
else {
if (assign_hf_process2_params(msg, hf, hname, _val?&val:0) > 0)
fl = 1;
}
}
} while (hf);
return fl;
}
else {
res = find_hf_value_idx(msg, hname, &hf, 0, 0);
if (res < 0) return res;
if (hf) {
if (!hname->param.len) {
if (hname->oper == hnoRemove2) {
return delete_value_lump(msg, hf, &hf->body);
}
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);
}
return 1;
}
}
else {
return assign_hf_process2_params(msg, hf, hname, _val?&val:0);
}
}
}
break;
}
return -1;
}
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 ) {
LOG(L_ERR, "ERROR: textops: asterisk not supported\n");
return E_CFG;
} else if ( (((struct hname_data*)*param)->flags & HNF_IDX) == 0 || !((struct hname_data*)*param)->idx ) {
((struct hname_data*)*param)->idx = -1;
}
if (((struct hname_data*)*param)->idx < -MAX_HF_VALUE_STACK) {
LOG(L_ERR, "ERROR: textops: index cannot be lower than %d\n", -MAX_HF_VALUE_STACK);
return E_CFG;
}
if ( ((struct hname_data*)*param)->param.len ) {
LOG(L_ERR, "ERROR: textops: param not supported\n");
return E_CFG;
}
((struct hname_data*)*param)->oper = hnoAppend;
}
return 0;
}
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 ) {
LOG(L_ERR, "ERROR: textops: asterisk not supported\n");
return E_CFG;
} else if ( (((struct hname_data*)*param)->flags & HNF_IDX) == 0 || !((struct hname_data*)*param)->idx ) {
((struct hname_data*)*param)->idx = 1;
}
if (((struct hname_data*)*param)->idx < -MAX_HF_VALUE_STACK) {
LOG(L_ERR, "ERROR: textops: index cannot be lower than %d\n", -MAX_HF_VALUE_STACK);
return E_CFG;
}
if ( ((struct hname_data*)*param)->param.len ) {
LOG(L_ERR, "ERROR: textops: param not supported\n");
return E_CFG;
}
((struct hname_data*)*param)->oper = hnoInsert;
}
return 0;
}
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;
}
if (((struct hname_data*)*param)->idx < -MAX_HF_VALUE_STACK) {
LOG(L_ERR, "ERROR: textops: index cannot be lower than %d\n", -MAX_HF_VALUE_STACK);
return E_CFG;
}
((struct hname_data*)*param)->oper = hnoRemove;
}
return 0;
}
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) {
LOG(L_ERR, "ERROR: textops: asterisk not supported without param\n");
return E_CFG;
} 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;
}
if (((struct hname_data*)*param)->idx < -MAX_HF_VALUE_STACK) {
LOG(L_ERR, "ERROR: textops: index cannot be lower than %d\n", -MAX_HF_VALUE_STACK);
return E_CFG;
}
((struct hname_data*)*param)->oper = hnoAssign;
}
return 0;
}
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;
}
return 0;
}
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;
}
return 0;
}
/* select implementation */
static int sel_hf_value(str* res, select_t* s, struct sip_msg* msg) { /* dummy */
return 0;
}
#define _ALLOC_INC_SIZE 1024
static int sel_hf_value_name(str* res, select_t* s, struct sip_msg* msg) {
struct hname_data* hname;
struct hdr_field* hf;
str val, hval1, hval2, huri, dummy_name;
int r;
if (!msg) {
struct hdr_field hdr;
char buf[50];
int i, n;
if (s->params[1].type == SEL_PARAM_STR) {
hname = pkg_malloc(sizeof(*hname));
if (!hname) return E_OUT_OF_MEM;
memset(hname, 0, sizeof(*hname));
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]='-';
}
i = snprintf(buf, sizeof(buf)-1, "%.*s: X\n", s->params[1].v.s.len, s->params[1].v.s.s);
buf[i] = 0;
hname->hname = s->params[1].v.s;
parse_hname2(buf, buf+i, &hdr);
if (hdr.type == HDR_ERROR_T) return E_CFG;
hname->htype = hdr.type;
s->params[1].v.p = hname;
s->params[1].type = SEL_PARAM_PTR;
}
else {
hname = s->params[1].v.p;
}
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) {
hname->idx = s->params[2].v.i;
hname->flags |= HNF_IDX;
if (hname->idx < -MAX_HF_VALUE_STACK) {
LOG(L_ERR, "ERROR: textops: index cannot be lower than %d\n", -MAX_HF_VALUE_STACK);
return E_CFG;
}
if (hname->idx == 0)
hname->idx = 1;
i = 3;
}
else {
i = 2;
hname->idx = 1;
}
if (n > i && s->params[i].type == SEL_PARAM_STR) {
hname->param = s->params[i].v.s;
for (i=hname->param.len-1; i>0; i--) {
if (hname->param.s[i]=='_')
hname->param.s[i]='-';
}
}
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;
switch (hname->oper) {
case hnoGetValueUri:
if (hname->flags & HNF_ALL || (hname->flags & HNF_IDX) == 0) {
char *buf = NULL;
int buf_len = 0;
hf = 0;
do {
r = find_next_hf(msg, hname, &hf);
if (r < 0) break;
if (hf) {
char *p;
str huri;
hval2.len = 0;
p = hf->body.s;
do {
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 == '<') {
huri.s++;
huri.len -= 2;
}
}
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;
res->s = pkg_realloc(buf, buf_len);
if (!res->s) {
pkg_free(buf);
LOG(L_ERR, "ERROR: textops: cannot realloc buffer\n");
res->len = 0;
return E_OUT_OF_MEM;
}
buf = res->s;
}
}
else {
/* 2nd value */
buf_len = res->len+huri.len+1+_ALLOC_INC_SIZE;
buf = pkg_malloc(buf_len);
if (!buf) {
LOG(L_ERR, "ERROR: testops: out of memory\n");
res->len = 0;
return E_OUT_OF_MEM;
}
/* copy 1st value */
memcpy(buf, res->s, res->len);
res->s = buf;
}
res->s[res->len] = ',';
res->len++;
if (huri.len) {
memcpy(res->s+res->len, huri.s, huri.len);
res->len += huri.len;
}
}
} while (r);
}
} while (hf);
if (buf) {
res->s = get_static_buffer(res->len);
if (!res->s) {
pkg_free(buf);
res->len = 0;
LOG(L_ERR, "ERROR: testops: cannot allocate static buffer\n");
return E_OUT_OF_MEM;
}
memcpy(res->s, buf, res->len);
pkg_free(buf);
}
}
else {
r = find_hf_value_idx(msg, hname, &hf, &hval1, &hval2);
if (r > 0) {
get_uri_and_skip_until_params(&hval1, &dummy_name, res);
if (res->len && *res->s == '<') {
res->s++; /* strip < & > */
res->len-=2;
}
}
}
break;
case hnoGetValueName:
if ((hname->flags & HNF_ALL) == 0) {
r = find_hf_value_idx(msg, hname, &hf, &hval1, &hval2);
if (r > 0) {
get_uri_and_skip_until_params(&hval1, res, &dummy_name);
if (res->len >= 2 && res->s[0] == '\"' && res->s[res->len-1]=='\"' ) {
res->s++; /* strip quotes */
res->len-=2;
}
}
}
break;
case hnoGetValue:
if (hname->flags & HNF_ALL || (hname->flags & HNF_IDX) == 0) {
char *buf = NULL;
int buf_len = 0;
hf = 0;
do {
r = find_next_hf(msg, hname, &hf);
if (r < 0) break;
if (hf) {
char *p;
hval2.len = 0;
p = hf->body.s;
do {
r = find_next_value(&p, hf->body.s+hf->body.len, &hval1, &hval2);
if (res->len == 0) {
*res = hval1; /* first value, if is also last value then we don't need any buffer */
}
else {
if (buf) {
if (res->len+hval1.len+1 > buf_len) {
buf_len = res->len+hval1.len+1+_ALLOC_INC_SIZE;
res->s = pkg_realloc(buf, buf_len);
if (!res->s) {
pkg_free(buf);
LOG(L_ERR, "ERROR: textops: cannot realloc buffer\n");
res->len = 0;
return E_OUT_OF_MEM;
}
buf = res->s;
}
}
else {
/* 2nd value */
buf_len = res->len+hval1.len+1+_ALLOC_INC_SIZE;
buf = pkg_malloc(buf_len);
if (!buf) {
LOG(L_ERR, "ERROR: testops: out of memory\n");
res->len = 0;
return E_OUT_OF_MEM;
}
/* copy 1st value */
memcpy(buf, res->s, res->len);
res->s = buf;
}
res->s[res->len] = ',';
res->len++;
if (hval1.len) {
memcpy(res->s+res->len, hval1.s, hval1.len);
res->len += hval1.len;
}
}
} while (r);
}
} while (hf);
if (buf) {
res->s = get_static_buffer(res->len);
if (!res->s) {
pkg_free(buf);
res->len = 0;
LOG(L_ERR, "ERROR: testops: cannot allocate static buffer\n");
return E_OUT_OF_MEM;
}
memcpy(res->s, buf, res->len);
pkg_free(buf);
}
}
else {
r = find_hf_value_idx(msg, hname, &hf, &hval1, &hval2);
if (r > 0) {
if (hname->param.len) {
str d1, d2;
get_uri_and_skip_until_params(&hval1, &dummy_name, &huri);
if (find_hf_value_param(hname, &hval1, &val, &d1, &d2)) {
*res = val;
}
}
else {
*res = hval1;
}
}
}
break;
case hnoGetValue2:
r = find_hf_value_idx(msg, hname, &hf, 0, 0);
if (r > 0) {
if (hname->param.len) {
str d1, d2;
char c;
if (find_hf_value2_param(hname, &hf->body, &val, &d1, &d2, &c)) {
*res = val;
}
}
else {
*res = hf->body;
}
}
break;
default:
break;
}
return 0;
}
static int sel_hf_value_name_param_name(str* res, select_t* s, struct sip_msg* msg) {
return sel_hf_value_name(res, s, msg);
}
static int sel_hf_value_name_param_name2(str* res, select_t* s, struct sip_msg* msg) {
if (!msg) { /* eliminate "param" level */
int n;
n = s->param_offset[select_level+1] - s->param_offset[select_level];
s->params[n-2] = s->params[n-1];
}
return sel_hf_value_name(res, s, msg);
}
static int sel_hf_value_name_uri(str* res, select_t* s, struct sip_msg* msg) {
int r;
r = sel_hf_value_name(res, s, msg);
if (!msg && r==0) {
((struct hname_data*) s->params[1].v.p)->oper = hnoGetValueUri;
}
return r;
}
static int sel_hf_value_name_name(str* res, select_t* s, struct sip_msg* msg) {
int r;
r = sel_hf_value_name(res, s, msg);
if (!msg && r==0) {
((struct hname_data*) s->params[1].v.p)->oper = hnoGetValueName;
}
return r;
}
static int sel_hf_value_exists(str* res, select_t* s, struct sip_msg* msg) { /* dummy */
return 0;
}
static int sel_hf_value_exists_param(str* res, select_t* s, struct sip_msg* msg) {
static char ret_val[] = "01";
int r;
if (!msg) {
r = sel_hf_value_name(res, s, msg);
if (r == 0)
((struct hname_data*) s->params[1].v.p)->oper = hnoIsIncluded;
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;
}
static int sel_hf_value2(str* res, select_t* s, struct sip_msg* msg) { /* dummy */
return 0;
}
static int sel_hf_value2_name(str* res, select_t* s, struct sip_msg* msg) {
int r;
r = sel_hf_value_name(res, s, msg);
if (!msg && r==0) {
((struct hname_data*) s->params[1].v.p)->oper = hnoGetValue2;
}
return r;
}
static int sel_hf_value2_name_param_name(str* res, select_t* s, struct sip_msg* msg) {
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[] = {
{ 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}
};