a0fb4e8b |
/*
* $Id$
*
* Copyright (C) 2005-2006 iptelorg GmbH
*
* 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.
*
|
2dd048ea |
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
|
a0fb4e8b |
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* History:
* --------
* 2005-12-19 select framework (mma)
|
43f88e4f |
* 2006-01-19 multiple nested calls, IS_ALIAS -> NESTED flag renamed (mma)
* DIVERSION flag checked
|
5c11480e |
* 2006-02-26 don't free str when changing type STR -> DIVERSION (mma)
* it can't be freeable sometimes (e.g. xlog's select)
|
6f1faf76 |
* 2006-05-30 parse_select (mma)
* 2006-06-02 shm_parse_select (mma)
|
5c11480e |
*
|
a0fb4e8b |
*/
|
6f1faf76 |
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>
|
a0fb4e8b |
|
3fb428ed |
#include "select.h"
#include "dprint.h"
#include "select_core.h"
#include "mem/mem.h"
|
6f1faf76 |
#include "mem/shm_mem.h"
|
3fb428ed |
/*
* The main parser table list placeholder
* at startup use core table, modules can
* add their own via register_select_table call
*/
static select_table_t *select_list = &select_core_table;
|
559cb457 |
/* the level of the select call that is beeing evaluated
* by the child process
*/
int select_level = 0;
|
8321667b |
/* pointer to the SIP uri beeing processed.
* Nested function calls can pass information to each
* other using this pointer. Only for performace reasons.
* (Miklos)
*/
struct sip_uri *select_uri_p = NULL;
|
d22deb6c |
/** parse a select identifier (internal version)
|
6f1faf76 |
* Parse select string into select structure s
|
d22deb6c |
* moves pointer p to the first unused char.
*
* The select identifier must be of the form:
* [@] <sel_id> [ '.' <sel_id> ...]
*
* Where
* <sel_id> = <id> |
* <id> '[' <idx> ']'
* <id> = [a-zA-Z0-9_]+
* <idx> = <number> | <string>
* <string> = '"' <ascii> '"' |
* '\"' <ascii> '\"'
*
* Examples:
* @to.tag
* @hf_value["contact"]
* @msg.header["SER-Server-ID"]
* @eval.pop[-1]
* contact.uri.params.maddr
* cfg_get.rtp_proxy.enabled
|
6f1faf76 |
*
|
d22deb6c |
* @return -1 error
|
6f1faf76 |
* p points to the first unconsumed char
* 0 success
* p points to the first unconsumed char
* s points to the select structure
*/
int w_parse_select(char**p, select_t* sel)
{
str name;
|
8aa67e4d |
char* select_name;
|
6f1faf76 |
if (**p=='@') (*p)++;
|
8aa67e4d |
select_name=*p;
|
6f1faf76 |
sel->n=0;
|
f8f678c3 |
while (isalpha((unsigned char)*(*p))) {
|
6f1faf76 |
if (sel->n > MAX_SELECT_PARAMS -2) {
ERR("parse_select: select depth exceeds max\n");
goto error;
}
name.s=(*p);
|
f8f678c3 |
while (isalpha((unsigned char)*(*p)) ||
isdigit((unsigned char)*(*p)) || (*(*p)=='_')) (*p)++;
|
6f1faf76 |
name.len=(*p)-name.s;
sel->params[sel->n].type=SEL_PARAM_STR;
sel->params[sel->n].v.s=name;
DBG("parse_select: part %d: %.*s\n", sel->n, sel->params[sel->n].v.s.len, sel->params[sel->n].v.s.s);
sel->n++;
if (*(*p)=='[') {
(*p)++;
|
b3d26ead |
if (*(*p)=='\\') (*p)++;
|
1ee35cdc |
if (*(*p)=='"') {
(*p)++;
name.s=(*p);
|
b3d26ead |
while ((*(*p)!='\0') && (*(*p)!='"')) (*p)++;
if (*(*p)!='"') {
ERR("parse_select: end of string is missing\n");
goto error;
}
|
1ee35cdc |
name.len=(*p)-name.s;
|
b3d26ead |
if (*((*p)-1)=='\\') name.len--;
|
1ee35cdc |
(*p)++;
if (*(*p)!=']') {
ERR("parse_select: invalid string index, no closing ]\n");
goto error;
};
(*p)++;
sel->params[sel->n].type=SEL_PARAM_STR;
sel->params[sel->n].v.s=name;
DBG("parse_select: part %d: [\"%.*s\"]\n", sel->n, sel->params[sel->n].v.s.len, sel->params[sel->n].v.s.s);
} else {
name.s=(*p);
if (*(*p)=='-') (*p)++;
|
f8f678c3 |
while (isdigit((unsigned char)*(*p))) (*p)++;
|
1ee35cdc |
name.len=(*p)-name.s;
if (*(*p)!=']') {
ERR("parse_select: invalid index, no closing ]\n");
goto error;
};
(*p)++;
sel->params[sel->n].type=SEL_PARAM_INT;
sel->params[sel->n].v.i=atoi(name.s);
DBG("parse_select: part %d: [%d]\n", sel->n, sel->params[sel->n].v.i);
}
|
6f1faf76 |
sel->n++;
}
if (*(*p)!='.') break;
(*p)++;
};
if (sel->n==0) {
|
03d7d3b9 |
ERR("parse_select: invalid select '%.*s'\n", (int)(*p - select_name), select_name);
|
6f1faf76 |
goto error;
};
DBG("parse_select: end, total elements: %d, calling resolve_select\n", sel->n);
if (resolve_select(sel)<0) {
|
03d7d3b9 |
ERR("parse_select: error while resolve_select '%.*s'\n", (int)(*p - select_name), select_name);
|
6f1faf76 |
goto error;
}
return 0;
error:
return -1;
}
|
d22deb6c |
/** parse a select identifier.
* Parse select string into select structure s and
* moves pointer p to the first unused char.
*
* The select identifier must be of the form:
* [@] <sel_id> [ '.' <sel_id> ...]
*
* Where
* <sel_id> = <id> |
* <id> '[' <idx> ']'
* <id> = [a-zA-Z0-9_]+
* <idx> = <number> | '-' <number> | <string>
* <string> = '"' <ascii> '"' |
* '\"' <ascii> '\"'
*
* Examples:
* @to.tag
* @hf_value["contact"]
* @msg.header["SER-Server-ID"]
* @eval.pop[-1]
* contact.uri.params.maddr
* cfg_get.rtp_proxy.enabled
*
* @param p - double string (asciiz) pointer, *p is moved to the first char
* after the select identifier
* @param s - the result will be stored here
* @return < 0 on error, 0 on success
*/
|
6f1faf76 |
int parse_select (char** p, select_t** s)
{
select_t* sel;
sel=(select_t*)pkg_malloc(sizeof(select_t));
if (!sel) {
ERR("parse_select: no free memory\n");
return -1;
}
if (w_parse_select(p, sel)<0) {
|
f5b61aa0 |
pkg_free(sel);
|
6f1faf76 |
return -2;
}
*s=sel;
return 0;
}
|
2a062f98 |
void free_select(select_t *s)
{
if (s)
pkg_free(s);
}
|
6f1faf76 |
int shm_parse_select (char** p, select_t** s)
{
select_t* sel;
sel=(select_t*)shm_malloc(sizeof(select_t));
if (!sel) {
ERR("parse_select: no free shared memory\n");
return -1;
}
if (w_parse_select(p, sel)<0) {
|
f5b61aa0 |
shm_free(sel);
|
6f1faf76 |
return -2;
}
*s=sel;
return 0;
}
|
3fb428ed |
int resolve_select(select_t* s)
{
|
43f88e4f |
select_f f;
int nested;
|
3fb428ed |
int param_idx = 0;
int table_idx = 0;
|
f8f678c3 |
select_table_t* t = NULL;
|
3fb428ed |
int accept = 0;
|
2dd048ea |
|
43f88e4f |
f = NULL;
nested = 0;
|
97aa4411 |
memset (s->f, 0, sizeof(s->f));
|
3fb428ed |
while (param_idx<s->n) {
accept = 0;
|
b9e4bf3f |
switch (s->params[param_idx].type) {
case SEL_PARAM_STR:
DBG("resolve_select: '%.*s'\n", s->params[param_idx].v.s.len, s->params[param_idx].v.s.s);
break;
case SEL_PARAM_INT:
DBG("resolve_select: [%d]\n", s->params[param_idx].v.i);
break;
|
8446b4e0 |
default:
|
5823b2f7 |
/* just to avoid the warning */
break;
|
b9e4bf3f |
}
|
3fb428ed |
for (t=select_list; t; t=t->next) {
|
2dd048ea |
table_idx = 0;
|
3fb428ed |
if (!t->table) continue;
while (t->table[table_idx].curr_f || t->table[table_idx].new_f) {
if (t->table[table_idx].curr_f == f) {
|
d0cf3d0b |
if ((t->table[table_idx].flags & (NESTED | CONSUME_NEXT_INT | CONSUME_NEXT_STR)) == NESTED) {
|
43f88e4f |
accept = 1;
} else if (t->table[table_idx].type == s->params[param_idx].type) {
|
3fb428ed |
switch (t->table[table_idx].type) {
|
ec2b7f55 |
case SEL_PARAM_INT:
|
3fb428ed |
accept = 1;
break;
|
43f88e4f |
case SEL_PARAM_STR:
|
3fb428ed |
accept = (((t->table[table_idx].name.len == s->params[param_idx].v.s.len) || !t->table[table_idx].name.len)
&& (!t->table[table_idx].name.s || !strncasecmp(t->table[table_idx].name.s, s->params[param_idx].v.s.s, s->params[param_idx].v.s.len)));
break;
default:
break;
}
};
}
if (accept) goto accepted;
table_idx++;
}
}
|
2dd048ea |
switch (s->params[param_idx].type) {
case SEL_PARAM_STR:
LOG(L_ERR, "Unable to resolve select '%.*s' at level %d\n", s->params[param_idx].v.s.len, s->params[param_idx].v.s.s, param_idx);
break;
case SEL_PARAM_INT:
LOG(L_ERR, "Unable to resolve select [%d] at level %d\n", s->params[param_idx].v.i, param_idx);
break;
default:
BUG ("Unable to resolve select at level %d\n", param_idx);
break;
break;
}
|
3fb428ed |
goto not_found;
accepted:
|
43f88e4f |
if (t->table[table_idx].flags & DIVERSION) {
|
5c11480e |
/* if (s->params[param_idx].type == SEL_PARAM_STR) pkg_free(s->params[param_idx].v.s.s); */
/* don't free it (the mem can leak only once at startup)
* the parsed string can live inside larger string block
* e.g. when xlog's select is parsed
*/
|
43f88e4f |
s->params[param_idx].type = SEL_PARAM_DIV;
s->params[param_idx].v.i = t->table[table_idx].flags & DIVERSION_MASK;
|
2dd048ea |
|
43f88e4f |
}
|
3fb428ed |
if (t->table[table_idx].flags & CONSUME_NEXT_STR) {
|
ec2b7f55 |
if ((param_idx<s->n-1) && (s->params[param_idx+1].type == SEL_PARAM_STR)) {
|
3fb428ed |
param_idx++;
} else if (!(t->table[table_idx].flags & OPTIONAL)) {
|
43f88e4f |
BUG ("Mandatory STR parameter not found\n");
|
3fb428ed |
goto not_found;
}
}
if (t->table[table_idx].flags & CONSUME_NEXT_INT) {
|
ec2b7f55 |
if ((param_idx<s->n-1) && (s->params[param_idx+1].type == SEL_PARAM_INT)) {
|
3fb428ed |
param_idx++;
} else if (!(t->table[table_idx].flags & OPTIONAL)) {
|
43f88e4f |
BUG ("Mandatory INT parameter not found\n");
|
3fb428ed |
goto not_found;
}
}
|
2dd048ea |
|
43f88e4f |
if (t->table[table_idx].flags & NESTED) {
if (nested < MAX_NESTED_CALLS-1) { /* need space for final function */
s->f[nested++] = f;
|
84b87bd3 |
s->param_offset[nested] = param_idx;
|
43f88e4f |
} else {
BUG("MAX_NESTED_CALLS too small to resolve select\n");
goto not_found;
}
|
3fb428ed |
} else {
param_idx++;
}
|
6fcb46be |
if (t->table[table_idx].flags & FIXUP_CALL) {
|
559cb457 |
select_level = nested;
|
1ee35cdc |
s->param_offset[nested+1] = param_idx;
|
6fcb46be |
if (t->table[table_idx].new_f(NULL, s, NULL)<0) goto not_found;
}
|
3fb428ed |
f = t->table[table_idx].new_f;
|
c935a4a3 |
if (t->table[table_idx].flags & CONSUME_ALL) {
/* sanity checks */
if (t->table[table_idx].flags & NESTED)
WARN("resolve_select: CONSUME_ALL should not be set "
"together with NESTED flag!\n");
if ((t->table[table_idx].flags & FIXUP_CALL) == 0)
WARN("resolve_select: FIXUP_CALL should be defined "
"if CONSUME_ALL flag is set!\n");
break;
}
|
3fb428ed |
}
|
43f88e4f |
if (t->table[table_idx].flags & SEL_PARAM_EXPECTED) {
BUG ("final node has SEL_PARAM_EXPECTED set (no more parameters available)\n");
goto not_found;
}
if (nested >= MAX_NESTED_CALLS) {
BUG("MAX_NESTED_CALLS too small, no space for finally resolved function\n");
goto not_found;
}
if ((nested>0) && (s->f[nested-1] == f)) {
BUG("Topmost nested function equals to final function, won't call it twice\n");
} else {
|
8c5e4afe |
s->f[nested++] = f;
|
43f88e4f |
}
|
8c5e4afe |
s->param_offset[nested] = s->n;
|
2dd048ea |
|
3fb428ed |
return 0;
|
2dd048ea |
|
3fb428ed |
not_found:
return -1;
}
int run_select(str* res, select_t* s, struct sip_msg* msg)
{
|
559cb457 |
int ret, orig_level;
|
2dd048ea |
|
3fb428ed |
if (res == NULL) {
BUG("Select unprepared result space\n");
return -1;
}
if (s == 0) {
BUG("Select structure is NULL\n");
return -1;
}
|
43f88e4f |
if (s->f[0] == 0) {
|
3fb428ed |
BUG("Select structure has not been resolved\n");
return -1;
}
|
43f88e4f |
DBG("Calling SELECT %p \n", s->f);
|
8321667b |
/* reset the uri pointer */
select_uri_p = NULL;
|
34873f53 |
/* save and restore the original select_level
* because of the nested selects */
|
559cb457 |
orig_level = select_level;
|
43f88e4f |
ret = 0;
|
559cb457 |
for ( select_level=0;
(ret == 0) && (s->f[select_level] !=0 ) && (select_level<MAX_NESTED_CALLS);
select_level++
) {
ret = s->f[select_level](res, s, msg);
|
43f88e4f |
}
|
559cb457 |
select_level = orig_level;
|
43f88e4f |
return ret;
|
3fb428ed |
}
void print_select(select_t* s)
{
int i;
DBG("select(");
for(i = 0; i < s->n; i++) {
|
ec2b7f55 |
if (s->params[i].type == SEL_PARAM_INT) {
|
3fb428ed |
DBG("%d,", s->params[i].v.i);
} else {
DBG("%.*s,", s->params[i].v.s.len, s->params[i].v.s.s);
}
}
DBG(")\n");
}
int register_select_table(select_row_t* mod_tab)
{
select_table_t* t;
t=(select_table_t*)pkg_malloc(sizeof(select_table_t));
if (!t) {
ERR("No memory for new select_table structure\n");
return -1;
}
|
2dd048ea |
|
3fb428ed |
t->table=mod_tab;
t->next=select_list;
select_list=t;
return 0;
}
|