mirror of https://github.com/sipwise/kamailio.git
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.
674 lines
14 KiB
674 lines
14 KiB
/*
|
|
* $Id$
|
|
*
|
|
* Copyright (C) 2007-2008 Voice Sistem SRL
|
|
*
|
|
* This file is part of SIP-router, a free SIP server.
|
|
*
|
|
* SIP-router 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
|
|
*
|
|
* SIP-router 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*
|
|
* History:
|
|
* --------
|
|
* 2007-08-01 initial version (ancuta onofrei)
|
|
* 2008-10-09 module is now using pcre regexp lib (juha heinanen)
|
|
*/
|
|
|
|
/*!
|
|
* \file
|
|
* \brief SIP-router dialplan :: database interface
|
|
* \ingroup dialplan
|
|
* Module: \ref dialplan
|
|
*/
|
|
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "../../dprint.h"
|
|
#include "../../ut.h"
|
|
#include "../../lib/srdb1/db.h"
|
|
#include "../../re.h"
|
|
#include "dp_db.h"
|
|
#include "dialplan.h"
|
|
|
|
str dp_db_url = {DEFAULT_RODB_URL, DEFAULT_RODB_URL_LEN};
|
|
str dp_table_name = str_init(DP_TABLE_NAME);
|
|
str dpid_column = str_init(DPID_COL);
|
|
str pr_column = str_init(PR_COL);
|
|
str match_op_column = str_init(MATCH_OP_COL);
|
|
str match_exp_column= str_init(MATCH_EXP_COL);
|
|
str match_len_column= str_init(MATCH_LEN_COL);
|
|
str subst_exp_column= str_init(SUBST_EXP_COL);
|
|
str repl_exp_column = str_init(REPL_EXP_COL);
|
|
str attrs_column = str_init(ATTRS_COL);
|
|
|
|
extern int dp_fetch_rows;
|
|
|
|
static db1_con_t* dp_db_handle = 0; /* database connection handle */
|
|
static db_func_t dp_dbf;
|
|
|
|
#define GET_STR_VALUE(_res, _values, _index)\
|
|
do{\
|
|
if ( VAL_NULL((_values)+ (_index)) ) { \
|
|
LM_ERR(" values %d is NULL - not allowed\n",_index);\
|
|
(_res).s = 0; (_res).len = 0;\
|
|
goto err;\
|
|
} \
|
|
(_res).s = VAL_STR((_values)+ (_index)).s;\
|
|
(_res).len = strlen(VAL_STR((_values)+ (_index)).s);\
|
|
}while(0);
|
|
|
|
void destroy_rule(dpl_node_t * rule);
|
|
void destroy_hash(int);
|
|
|
|
dpl_node_t * build_rule(db_val_t * values);
|
|
int add_rule2hash(dpl_node_t *, int);
|
|
|
|
void list_rule(dpl_node_t * );
|
|
void list_hash(int h_index);
|
|
|
|
|
|
dpl_id_p* rules_hash = NULL;
|
|
int * crt_idx, *next_idx;
|
|
|
|
|
|
|
|
int init_db_data(void)
|
|
{
|
|
if(dp_table_name.s == 0){
|
|
LM_ERR("invalid database table name\n");
|
|
return -1;
|
|
}
|
|
|
|
/* Find a database module */
|
|
if (db_bind_mod(&dp_db_url, &dp_dbf) < 0){
|
|
LM_ERR("unable to bind to a database driver\n");
|
|
return -1;
|
|
}
|
|
|
|
if(dp_connect_db() !=0)
|
|
return -1;
|
|
|
|
if(db_check_table_version(&dp_dbf, dp_db_handle, &dp_table_name,
|
|
DP_TABLE_VERSION) < 0) {
|
|
LM_ERR("error during table version check.\n");
|
|
goto error;
|
|
}
|
|
|
|
if(dp_load_db() != 0){
|
|
LM_ERR("failed to load database data\n");
|
|
goto error;
|
|
}
|
|
|
|
dp_disconnect_db();
|
|
|
|
return 0;
|
|
error:
|
|
|
|
dp_disconnect_db();
|
|
return -1;
|
|
}
|
|
|
|
|
|
int dp_connect_db(void)
|
|
{
|
|
if (dp_dbf.init==0){
|
|
LM_CRIT("null dp_dbf\n");
|
|
return -1;
|
|
}
|
|
|
|
if(dp_db_handle){
|
|
LM_CRIT("BUG: connection to database already open\n");
|
|
return -1;
|
|
}
|
|
|
|
if ((dp_db_handle = dp_dbf.init(&dp_db_url)) == 0){
|
|
LM_ERR("unable to connect to the database\n");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
void dp_disconnect_db(void)
|
|
{
|
|
if(dp_db_handle){
|
|
dp_dbf.close(dp_db_handle);
|
|
dp_db_handle = 0;
|
|
}
|
|
}
|
|
|
|
|
|
int init_data(void)
|
|
{
|
|
int *p;
|
|
|
|
rules_hash = (dpl_id_p *)shm_malloc(2*sizeof(dpl_id_p));
|
|
if(!rules_hash) {
|
|
LM_ERR("out of shm memory\n");
|
|
return -1;
|
|
}
|
|
rules_hash[0] = rules_hash[1] = 0;
|
|
|
|
p = (int *)shm_malloc(2*sizeof(int));
|
|
if(!p){
|
|
LM_ERR("out of shm memory\n");
|
|
return -1;
|
|
}
|
|
crt_idx = p;
|
|
next_idx = p+1;
|
|
*crt_idx = *next_idx = 0;
|
|
|
|
LM_DBG("trying to initialize data from db\n");
|
|
if(init_db_data() != 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
void destroy_data(void)
|
|
{
|
|
if(rules_hash){
|
|
destroy_hash(0);
|
|
destroy_hash(1);
|
|
shm_free(rules_hash);
|
|
rules_hash = 0;
|
|
}
|
|
|
|
if(crt_idx)
|
|
shm_free(crt_idx);
|
|
}
|
|
|
|
|
|
/*load rules from DB*/
|
|
int dp_load_db(void)
|
|
{
|
|
int i, nr_rows;
|
|
db1_res_t * res = 0;
|
|
db_val_t * values;
|
|
db_row_t * rows;
|
|
db_key_t query_cols[DP_TABLE_COL_NO] = {
|
|
&dpid_column, &pr_column,
|
|
&match_op_column, &match_exp_column, &match_len_column,
|
|
&subst_exp_column, &repl_exp_column, &attrs_column };
|
|
|
|
db_key_t order = &pr_column;
|
|
|
|
dpl_node_t *rule;
|
|
|
|
LM_DBG("init\n");
|
|
if( (*crt_idx) != (*next_idx)){
|
|
LM_WARN("a load command already generated, aborting reload...\n");
|
|
return 0;
|
|
}
|
|
|
|
if (dp_dbf.use_table(dp_db_handle, &dp_table_name) < 0){
|
|
LM_ERR("error in use_table %.*s\n", dp_table_name.len, dp_table_name.s);
|
|
return -1;
|
|
}
|
|
|
|
if (DB_CAPABILITY(dp_dbf, DB_CAP_FETCH)) {
|
|
if(dp_dbf.query(dp_db_handle,0,0,0,query_cols, 0,
|
|
DP_TABLE_COL_NO, order, 0) < 0){
|
|
LM_ERR("failed to query database!\n");
|
|
return -1;
|
|
}
|
|
if(dp_dbf.fetch_result(dp_db_handle, &res, dp_fetch_rows)<0) {
|
|
LM_ERR("failed to fetch\n");
|
|
if (res)
|
|
dp_dbf.free_result(dp_db_handle, res);
|
|
return -1;
|
|
}
|
|
} else {
|
|
/*select the whole table and all the columns*/
|
|
if(dp_dbf.query(dp_db_handle,0,0,0,query_cols, 0,
|
|
DP_TABLE_COL_NO, order, &res) < 0){
|
|
LM_ERR("failed to query database\n");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
nr_rows = RES_ROW_N(res);
|
|
|
|
*next_idx = ((*crt_idx) == 0)? 1:0;
|
|
destroy_hash(*next_idx);
|
|
|
|
if(nr_rows == 0){
|
|
LM_WARN("no data in the db\n");
|
|
goto end;
|
|
}
|
|
|
|
do {
|
|
for(i=0; i<RES_ROW_N(res); i++){
|
|
rows = RES_ROWS(res);
|
|
|
|
values = ROW_VALUES(rows+i);
|
|
|
|
if((rule = build_rule(values)) ==0 )
|
|
goto err2;
|
|
|
|
if(add_rule2hash(rule , *next_idx) != 0)
|
|
goto err2;
|
|
|
|
}
|
|
if (DB_CAPABILITY(dp_dbf, DB_CAP_FETCH)) {
|
|
if(dp_dbf.fetch_result(dp_db_handle, &res, dp_fetch_rows)<0) {
|
|
LM_ERR("failure while fetching!\n");
|
|
if (res)
|
|
dp_dbf.free_result(dp_db_handle, res);
|
|
return -1;
|
|
}
|
|
} else {
|
|
break;
|
|
}
|
|
} while(RES_ROW_N(res)>0);
|
|
|
|
|
|
end:
|
|
/*update data*/
|
|
*crt_idx = *next_idx;
|
|
list_hash(*crt_idx);
|
|
dp_dbf.free_result(dp_db_handle, res);
|
|
return 0;
|
|
|
|
err2:
|
|
if(rule) destroy_rule(rule);
|
|
destroy_hash(*next_idx);
|
|
dp_dbf.free_result(dp_db_handle, res);
|
|
*next_idx = *crt_idx;
|
|
return -1;
|
|
}
|
|
|
|
|
|
int str_to_shm(str src, str * dest)
|
|
{
|
|
if(src.len ==0 || src.s ==0)
|
|
return 0;
|
|
|
|
dest->s = (char*)shm_malloc((src.len+1) * sizeof(char));
|
|
if(!dest->s){
|
|
LM_ERR("out of shm memory\n");
|
|
return -1;
|
|
}
|
|
|
|
memcpy(dest->s, src.s, src.len);
|
|
dest->s[src.len] = '\0';
|
|
dest->len = src.len;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* Compile pcre pattern and return pointer to shm copy of result */
|
|
static pcre *reg_ex_comp(const char *pattern, int *cap_cnt)
|
|
{
|
|
pcre *re, *result;
|
|
const char *error;
|
|
int rc, err_offset;
|
|
size_t size;
|
|
|
|
re = pcre_compile(pattern, 0, &error, &err_offset, NULL);
|
|
if (re == NULL) {
|
|
LM_ERR("PCRE compilation of '%s' failed at offset %d: %s\n",
|
|
pattern, err_offset, error);
|
|
return (pcre *)0;
|
|
}
|
|
rc = pcre_fullinfo(re, NULL, PCRE_INFO_SIZE, &size);
|
|
if (rc != 0) {
|
|
pcre_free(re);
|
|
LM_ERR("pcre_fullinfo on compiled pattern '%s' yielded error: %d\n",
|
|
pattern, rc);
|
|
return (pcre *)0;
|
|
}
|
|
rc = pcre_fullinfo(re, NULL, PCRE_INFO_CAPTURECOUNT, cap_cnt);
|
|
if (rc != 0) {
|
|
pcre_free(re);
|
|
LM_ERR("pcre_fullinfo on compiled pattern '%s' yielded error: %d\n",
|
|
pattern, rc);
|
|
return (pcre *)0;
|
|
}
|
|
result = (pcre *)shm_malloc(size);
|
|
if (result == NULL) {
|
|
pcre_free(re);
|
|
LM_ERR("not enough shared memory for compiled PCRE pattern\n");
|
|
return (pcre *)0;
|
|
}
|
|
memcpy(result, re, size);
|
|
pcre_free(re);
|
|
return result;
|
|
}
|
|
|
|
|
|
/*compile the expressions, and if ok, build the rule */
|
|
dpl_node_t * build_rule(db_val_t * values)
|
|
{
|
|
pcre *match_comp, *subst_comp;
|
|
struct subst_expr *repl_comp;
|
|
const char *error;
|
|
dpl_node_t * new_rule;
|
|
str match_exp, subst_exp, repl_exp, attrs;
|
|
int matchop, cap_cnt;
|
|
|
|
matchop = VAL_INT(values+2);
|
|
|
|
if((matchop != REGEX_OP) && (matchop!=EQUAL_OP)){
|
|
LM_ERR("invalid value for match operator\n");
|
|
return NULL;
|
|
}
|
|
|
|
match_comp = subst_comp =0;
|
|
repl_comp = 0;
|
|
new_rule = 0;
|
|
error = NULL;
|
|
|
|
GET_STR_VALUE(match_exp, values, 3);
|
|
if(matchop == REGEX_OP){
|
|
match_comp = reg_ex_comp(match_exp.s, &cap_cnt);
|
|
if(!match_comp){
|
|
LM_ERR("failed to compile match expression %.*s\n",
|
|
match_exp.len, match_exp.s);
|
|
goto err;
|
|
}
|
|
}
|
|
|
|
LM_DBG("build_rule\n");
|
|
GET_STR_VALUE(repl_exp, values, 6);
|
|
if(repl_exp.len && repl_exp.s){
|
|
repl_comp = repl_exp_parse(repl_exp);
|
|
if(!repl_comp){
|
|
LM_ERR("failed to compile replacing expression %.*s\n",
|
|
repl_exp.len, repl_exp.s);
|
|
goto err;
|
|
}
|
|
}
|
|
|
|
GET_STR_VALUE(subst_exp, values, 5);
|
|
if(subst_exp.s && subst_exp.len){
|
|
subst_comp = reg_ex_comp(subst_exp.s, &cap_cnt);
|
|
if(!subst_comp){
|
|
LM_ERR("failed to compile subst expression %.*s\n",
|
|
subst_exp.len, subst_exp.s);
|
|
goto err;
|
|
}
|
|
if (cap_cnt > MAX_REPLACE_WITH) {
|
|
LM_ERR("subst expression %.*s has too many sub-expressions\n",
|
|
subst_exp.len, subst_exp.s);
|
|
goto err;
|
|
}
|
|
}
|
|
|
|
if (repl_comp && (cap_cnt < repl_comp->max_pmatch) &&
|
|
(repl_comp->max_pmatch != 0)) {
|
|
LM_ERR("repl_exp %.*s refers to %d sub-expressions, but "
|
|
"subst_exp %.*s has only %d\n",
|
|
repl_exp.len, repl_exp.s, repl_comp->max_pmatch,
|
|
subst_exp.len, subst_exp.s, cap_cnt);
|
|
goto err;
|
|
}
|
|
|
|
new_rule = (dpl_node_t *)shm_malloc(sizeof(dpl_node_t));
|
|
if(!new_rule){
|
|
LM_ERR("out of shm memory(new_rule)\n");
|
|
goto err;
|
|
}
|
|
memset(new_rule, 0, sizeof(dpl_node_t));
|
|
|
|
if(str_to_shm(match_exp, &new_rule->match_exp)!=0)
|
|
goto err;
|
|
|
|
if(str_to_shm(subst_exp, &new_rule->subst_exp)!=0)
|
|
goto err;
|
|
|
|
if(str_to_shm(repl_exp, &new_rule->repl_exp)!=0)
|
|
goto err;
|
|
|
|
/*set the rest of the rule fields*/
|
|
new_rule->dpid = VAL_INT(values);
|
|
new_rule->pr = VAL_INT(values+1);
|
|
new_rule->matchlen = VAL_INT(values+4);
|
|
new_rule->matchop = matchop;
|
|
GET_STR_VALUE(attrs, values, 7);
|
|
if(str_to_shm(attrs, &new_rule->attrs)!=0)
|
|
goto err;
|
|
|
|
LM_DBG("attrs are %.*s\n", new_rule->attrs.len, new_rule->attrs.s);
|
|
|
|
new_rule->match_comp = match_comp;
|
|
new_rule->subst_comp = subst_comp;
|
|
new_rule->repl_comp = repl_comp;
|
|
|
|
return new_rule;
|
|
|
|
err:
|
|
if(match_comp) shm_free(match_comp);
|
|
if(subst_comp) shm_free(subst_comp);
|
|
if(repl_comp) repl_expr_free(repl_comp);
|
|
if(new_rule) destroy_rule(new_rule);
|
|
return NULL;
|
|
}
|
|
|
|
|
|
int add_rule2hash(dpl_node_t * rule, int h_index)
|
|
{
|
|
dpl_id_p crt_idp, last_idp;
|
|
dpl_index_p indexp, last_indexp, new_indexp;
|
|
int new_id;
|
|
|
|
if(!rules_hash){
|
|
LM_ERR("data not allocated\n");
|
|
return -1;
|
|
}
|
|
|
|
new_id = 0;
|
|
|
|
/*search for the corresponding dpl_id*/
|
|
for(crt_idp = last_idp =rules_hash[h_index]; crt_idp!= NULL;
|
|
last_idp = crt_idp, crt_idp = crt_idp->next)
|
|
if(crt_idp->dp_id == rule->dpid)
|
|
break;
|
|
|
|
/*didn't find a dpl_id*/
|
|
if(!crt_idp){
|
|
crt_idp = (dpl_id_t*)shm_malloc(sizeof(dpl_id_t));
|
|
if(!crt_idp){
|
|
LM_ERR("out of shm memory (crt_idp)\n");
|
|
return -1;
|
|
}
|
|
memset(crt_idp, 0, sizeof(dpl_id_t));
|
|
crt_idp->dp_id = rule->dpid;
|
|
new_id = 1;
|
|
LM_DBG("new dpl_id %i\n", rule->dpid);
|
|
}
|
|
|
|
/*search for the corresponding dpl_index*/
|
|
for(indexp = last_indexp =crt_idp->first_index; indexp!=NULL;
|
|
last_indexp = indexp, indexp = indexp->next){
|
|
if(indexp->len == rule->matchlen)
|
|
goto add_rule;
|
|
if((rule->matchlen!=0)&&((indexp->len)?(indexp->len>rule->matchlen):1))
|
|
goto add_index;
|
|
}
|
|
|
|
add_index:
|
|
LM_DBG("new index , len %i\n", rule->matchlen);
|
|
|
|
new_indexp = (dpl_index_t *)shm_malloc(sizeof(dpl_index_t));
|
|
if(!new_indexp){
|
|
LM_ERR("out of shm memory\n");
|
|
goto err;
|
|
}
|
|
memset(new_indexp , 0, sizeof(dpl_index_t));
|
|
new_indexp->next = indexp;
|
|
new_indexp->len = rule->matchlen;
|
|
|
|
/*add as first index*/
|
|
if(last_indexp == indexp){
|
|
crt_idp->first_index = new_indexp;
|
|
}else{
|
|
last_indexp->next = new_indexp;
|
|
}
|
|
|
|
indexp = new_indexp;
|
|
|
|
add_rule:
|
|
rule->next = 0;
|
|
if(!indexp->first_rule)
|
|
indexp->first_rule = rule;
|
|
|
|
if(indexp->last_rule)
|
|
indexp->last_rule->next = rule;
|
|
|
|
indexp->last_rule = rule;
|
|
|
|
if(new_id){
|
|
crt_idp->next = rules_hash[h_index];
|
|
rules_hash[h_index] = crt_idp;
|
|
}
|
|
LM_DBG("added the rule id %i index %i pr %i next %p to the "
|
|
"index with %i len\n", rule->dpid, rule->matchlen,
|
|
rule->pr, rule->next, indexp->len);
|
|
|
|
return 0;
|
|
|
|
err:
|
|
if(new_id)
|
|
shm_free(crt_idp);
|
|
return -1;
|
|
}
|
|
|
|
|
|
void destroy_hash(int index)
|
|
{
|
|
dpl_id_p crt_idp;
|
|
dpl_index_p indexp;
|
|
dpl_node_p rulep;
|
|
|
|
if(!rules_hash[index])
|
|
return;
|
|
|
|
for(crt_idp = rules_hash[index]; crt_idp != NULL;){
|
|
|
|
for(indexp = crt_idp->first_index; indexp != NULL;){
|
|
|
|
for(rulep = indexp->first_rule; rulep!= NULL;){
|
|
|
|
destroy_rule(rulep);
|
|
|
|
indexp->first_rule = rulep->next;
|
|
shm_free(rulep);
|
|
rulep=0;
|
|
rulep= indexp->first_rule;
|
|
}
|
|
crt_idp->first_index= indexp->next;
|
|
shm_free(indexp);
|
|
indexp=0;
|
|
indexp = crt_idp->first_index;
|
|
|
|
}
|
|
|
|
rules_hash[index] = crt_idp->next;
|
|
shm_free(crt_idp);
|
|
crt_idp = 0;
|
|
crt_idp = rules_hash[index];
|
|
}
|
|
|
|
rules_hash[index] = 0;
|
|
}
|
|
|
|
|
|
void destroy_rule(dpl_node_t * rule){
|
|
|
|
if(!rule)
|
|
return;
|
|
|
|
LM_DBG("destroying rule with priority %i\n",
|
|
rule->pr);
|
|
|
|
if(rule->match_comp)
|
|
shm_free(rule->match_comp);
|
|
|
|
if(rule->subst_comp)
|
|
shm_free(rule->subst_comp);
|
|
|
|
/*destroy repl_exp*/
|
|
if(rule->repl_comp)
|
|
repl_expr_free(rule->repl_comp);
|
|
|
|
if(rule->match_exp.s)
|
|
shm_free(rule->match_exp.s);
|
|
|
|
if(rule->subst_exp.s)
|
|
shm_free(rule->subst_exp.s);
|
|
|
|
if(rule->repl_exp.s)
|
|
shm_free(rule->repl_exp.s);
|
|
|
|
if(rule->attrs.s)
|
|
shm_free(rule->attrs.s);
|
|
}
|
|
|
|
|
|
dpl_id_p select_dpid(int id)
|
|
{
|
|
dpl_id_p idp;
|
|
|
|
if(!rules_hash || !crt_idx)
|
|
return NULL;
|
|
|
|
for(idp = rules_hash[*crt_idx]; idp!=NULL; idp = idp->next)
|
|
if(idp->dp_id == id)
|
|
return idp;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/*FOR DEBUG PURPOSE*/
|
|
void list_hash(int h_index)
|
|
{
|
|
dpl_id_p crt_idp;
|
|
dpl_index_p indexp;
|
|
dpl_node_p rulep;
|
|
|
|
|
|
if(!rules_hash[h_index])
|
|
return;
|
|
|
|
for(crt_idp=rules_hash[h_index]; crt_idp!=NULL; crt_idp = crt_idp->next){
|
|
LM_DBG("DPID: %i, pointer %p\n", crt_idp->dp_id, crt_idp);
|
|
for(indexp=crt_idp->first_index; indexp!=NULL;indexp= indexp->next){
|
|
LM_DBG("INDEX LEN: %i\n", indexp->len);
|
|
for(rulep = indexp->first_rule; rulep!= NULL;rulep = rulep->next){
|
|
list_rule(rulep);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void list_rule(dpl_node_t * rule)
|
|
{
|
|
LM_DBG("RULE %p: pr %i next %p match_exp %.*s, "
|
|
"subst_exp %.*s, repl_exp %.*s and attrs %.*s\n", rule,
|
|
rule->pr, rule->next,
|
|
rule->match_exp.len, rule->match_exp.s,
|
|
rule->subst_exp.len, rule->subst_exp.s,
|
|
rule->repl_exp.len, rule->repl_exp.s,
|
|
rule->attrs.len, rule->attrs.s);
|
|
|
|
}
|