From e71485f9cc0b702f3f56e4b9c5f69c2d481c445f Mon Sep 17 00:00:00 2001 From: Parantido Julius De Rica Date: Mon, 1 Sep 2014 14:24:32 +0200 Subject: [PATCH 1/2] - A new column (named timerec varchar 255 not nullable) in dialplan database schema is needed. - TimeRec value should be compliant to RFC2445 (Internet Calendaring and Scheduling - Core Object Specification) standards in order to match. - A timerec failed match doesn't stops dialplan rule searching. - An empty timerec value is not evaluated in rule matching algorithm. - Attached patch should to be applied to Dialplan Module rev.5895 (actual master). --- modules/dialplan/dialplan.c | 11 +++- modules/dialplan/dialplan.h | 5 +- modules/dialplan/dp_db.c | 96 ++++++++++++++++++++++++----- modules/dialplan/dp_db.h | 10 +-- modules/dialplan/dp_repl.c | 118 +++++++++++++++++++++++++++++++++--- 5 files changed, 208 insertions(+), 32 deletions(-) diff --git a/modules/dialplan/dialplan.c b/modules/dialplan/dialplan.c index 3d5a2a809aa..78e2ce564cf 100644 --- a/modules/dialplan/dialplan.c +++ b/modules/dialplan/dialplan.c @@ -83,13 +83,14 @@ static param_export_t mod_params[]={ { "db_url", STR_PARAM, &default_dp_db_url.s}, { "table_name", STR_PARAM, &default_dp_table.s }, { "dpid_col", STR_PARAM, &dpid_column.s }, - { "pr_col", STR_PARAM, &pr_column.s }, + { "pr_col", STR_PARAM, &pr_column.s }, { "match_op_col", STR_PARAM, &match_op_column.s }, { "match_exp_col", STR_PARAM, &match_exp_column.s }, - { "match_flags_col",STR_PARAM, &match_flags_column.s }, + { "match_flags_col", STR_PARAM, &match_flags_column.s }, { "subst_exp_col", STR_PARAM, &subst_exp_column.s }, { "repl_exp_col", STR_PARAM, &repl_exp_column.s }, { "attrs_col", STR_PARAM, &attrs_column.s }, + { "timerec_col", STR_PARAM, &timerec_column.s }, { "disabled_col", STR_PARAM, &disabled_column.s}, {0,0,0} }; @@ -410,7 +411,7 @@ static int mod_init(void) LM_INFO("initializing module...\n"); - dpid_column.len = strlen( dpid_column.s); + dpid_column.len = strlen(dpid_column.s); pr_column.len = strlen(pr_column.s); match_op_column.len = strlen(match_op_column.s); match_exp_column.len = strlen(match_exp_column.s); @@ -418,6 +419,7 @@ static int mod_init(void) subst_exp_column.len = strlen(subst_exp_column.s); repl_exp_column.len = strlen(repl_exp_column.s); attrs_column.len = strlen(attrs_column.s); + timerec_column.len = strlen(timerec_column.s); disabled_column.len = strlen(disabled_column.s); if (default_dp_db_url.s) { @@ -701,6 +703,9 @@ static int dp_translate_f(struct sip_msg *msg, char *str1, char *str2, goto error; } + LM_DBG("Checking %.*s with dpid %i => output %.*s\n", + input.len, input.s, idp->dp_id, output.len, output.s); + attrs_par = attr_spec ? &attrs : NULL; if (translate(msg, input, &output, idp, attrs_par) != 0) { LM_DBG("could not translate %.*s " diff --git a/modules/dialplan/dialplan.h b/modules/dialplan/dialplan.h index dc81ce010d5..ff34f7aadee 100644 --- a/modules/dialplan/dialplan.h +++ b/modules/dialplan/dialplan.h @@ -29,6 +29,7 @@ #include "../../parser/msg_parser.h" #include "../../rw_locking.h" +#include "../../time_rec.h" #include "../../db/db.h" #include "../../re.h" @@ -40,8 +41,6 @@ #define DP_CASE_INSENSITIVE 1 #define DP_INDEX_HASH_SIZE 16 - - typedef struct dpl_node{ int dpid; int table_id; /*choose between matching regexp/strings with same priority*/ @@ -52,6 +51,8 @@ typedef struct dpl_node{ pcre * match_comp, * subst_comp; /*compiled patterns*/ struct subst_expr * repl_comp; str attrs; + str timerec; + tmrec_t *parsed_timerec; struct dpl_node * next; /*next rule*/ }dpl_node_t, *dpl_node_p; diff --git a/modules/dialplan/dp_db.c b/modules/dialplan/dp_db.c index b92b36a5fbd..e64e1217c23 100644 --- a/modules/dialplan/dp_db.c +++ b/modules/dialplan/dp_db.c @@ -29,6 +29,7 @@ #include "../../dprint.h" #include "../../ut.h" +#include "../../time_rec.h" #include "dp_db.h" @@ -46,6 +47,7 @@ str subst_exp_column = str_init(SUBST_EXP_COL); str repl_exp_column = str_init(REPL_EXP_COL); str disabled_column = str_init(DISABLED_COL); str attrs_column = str_init(ATTRS_COL); +str timerec_column = str_init(TIMEREC_COL); #define GET_STR_VALUE(_res, _values, _index)\ @@ -220,7 +222,7 @@ int dp_load_db(dp_connection_list_p dp_conn) db_key_t query_cols[DP_TABLE_COL_NO] = { &dpid_column, &pr_column, &match_op_column, &match_exp_column, &match_flags_column, - &subst_exp_column, &repl_exp_column, &attrs_column }; + &subst_exp_column, &repl_exp_column, &attrs_column, &timerec_column }; db_key_t order = &pr_column; /* disabled condition */ db_key_t cond_cols[1] = { &disabled_column }; @@ -383,14 +385,55 @@ int str_to_shm(str src, str * dest) return 0; } +static inline tmrec_t* parse_time_def(char *time_str) { + + tmrec_p time_rec; + char *p,*s; + + p = time_str; + time_rec = 0; + + time_rec = tmrec_new(SHM_ALLOC); + if (time_rec==0) { + LM_ERR("no more shm mem\n"); + goto error; + } + + /* empty definition? */ + if ( time_str==0 || *time_str==0 ) + goto done; + + load_TR_value( p, s, time_rec, tr_parse_dtstart, parse_error, done); + load_TR_value( p, s, time_rec, tr_parse_duration, parse_error, done); + load_TR_value( p, s, time_rec, tr_parse_freq, parse_error, done); + load_TR_value( p, s, time_rec, tr_parse_until, parse_error, done); + load_TR_value( p, s, time_rec, tr_parse_interval, parse_error, done); + load_TR_value( p, s, time_rec, tr_parse_byday, parse_error, done); + load_TR_value( p, s, time_rec, tr_parse_bymday, parse_error, done); + load_TR_value( p, s, time_rec, tr_parse_byyday, parse_error, done); + load_TR_value( p, s, time_rec, tr_parse_byweekno, parse_error, done); + load_TR_value( p, s, time_rec, tr_parse_bymonth, parse_error, done); + + /* success */ +done: + return time_rec; +parse_error: + LM_ERR("parse error in <%s> around position %i\n", + time_str, (int)(long)(p-time_str)); +error: + if (time_rec) + tmrec_free( time_rec ); + return 0; +} /*compile the expressions, and if ok, build the rule */ dpl_node_t * build_rule(db_val_t * values) { + tmrec_t *parsed_timerec; pcre * match_comp, *subst_comp; struct subst_expr * repl_comp; dpl_node_t * new_rule; - str match_exp, subst_exp, repl_exp, attrs; + str match_exp, subst_exp, repl_exp, attrs, timerec; int matchop; int namecount; @@ -401,7 +444,8 @@ dpl_node_t * build_rule(db_val_t * values) return NULL; } - match_comp = subst_comp =0; + parsed_timerec = 0; + match_comp = subst_comp = 0; repl_comp = 0; new_rule = 0; @@ -486,6 +530,25 @@ dpl_node_t * build_rule(db_val_t * values) LM_DBG("attrs are %.*s\n", new_rule->attrs.len, new_rule->attrs.s); + /* Retrieve and Parse Timerec Matching Pattern */ + GET_STR_VALUE(timerec, values, 8); + if(timerec.len && timerec.s) { + parsed_timerec = parse_time_def(timerec.s); + if(!parsed_timerec) { + LM_ERR("failed to parse timerec pattern %.*s\n", + timerec.len, timerec.s); + goto err; + } + + if(str_to_shm(timerec, &new_rule->timerec) != 0) + goto err; + + new_rule->parsed_timerec = parsed_timerec; + + LM_DBG("timerecs are %.*s\n", + new_rule->timerec.len, new_rule->timerec.s); + } + if (match_comp) new_rule->match_comp = match_comp; @@ -522,14 +585,12 @@ int add_rule2hash(dpl_node_t * rule, dp_connection_list_t *conn, int index) crt_idp = select_dpid(conn, rule->dpid, index); /*didn't find a dpl_id*/ if(!crt_idp){ - crt_idp = shm_malloc(sizeof(dpl_id_t) + - (DP_INDEX_HASH_SIZE+1) * sizeof(dpl_index_t)); + crt_idp = shm_malloc(sizeof(dpl_id_t) + (DP_INDEX_HASH_SIZE+1) * sizeof(dpl_index_t)); if(!crt_idp){ LM_ERR("out of shm memory (crt_idp)\n"); return -1; } - memset(crt_idp, 0, sizeof(dpl_id_t) + - (DP_INDEX_HASH_SIZE+1) * sizeof(dpl_index_t)); + memset(crt_idp, 0, sizeof(dpl_id_t) + (DP_INDEX_HASH_SIZE+1) * sizeof(dpl_index_t)); crt_idp->dp_id = rule->dpid; crt_idp->rule_hash = (dpl_index_t*)(crt_idp + 1); new_id = 1; @@ -565,13 +626,12 @@ int add_rule2hash(dpl_node_t * rule, dp_connection_list_t *conn, int index) indexp->last_rule = rule; if(new_id){ - crt_idp->next = conn->hash[conn->next_index]; - conn->hash[conn->next_index] = crt_idp; + crt_idp->next = conn->hash[conn->next_index]; + conn->hash[conn->next_index] = crt_idp; } LM_DBG("added the rule id %i pr %i next %p to the " " %i bucket\n", rule->dpid, - rule->pr, rule->next, rule->matchop == REGEX_OP? - DP_INDEX_HASH_SIZE : bucket); + rule->pr, rule->next, rule->matchop == REGEX_OP ? DP_INDEX_HASH_SIZE : bucket); return 0; @@ -646,6 +706,9 @@ void destroy_rule(dpl_node_t * rule){ if(rule->attrs.s) shm_free(rule->attrs.s); + + if(rule->timerec.s) + shm_free(rule->timerec.s); } @@ -699,13 +762,14 @@ void list_hash(dpl_id_t * hash, rw_lock_t * ref_lock) void list_rule(dpl_node_t * rule) { LM_DBG("RULE %p: pr %i next %p match_exp %.*s match_flags %d, " - "subst_exp %.*s, repl_exp %.*s and attrs %.*s\n", rule, + "subst_exp %.*s, repl_exp %.*s and attrs %.*s and timerec %.*s\n", rule, rule->pr, rule->next, - rule->match_exp.len, rule->match_exp.s, + rule->match_exp.len, rule->match_exp.s, rule->match_flags, - rule->subst_exp.len, rule->subst_exp.s, - rule->repl_exp.len, rule->repl_exp.s, - rule->attrs.len, rule->attrs.s); + rule->subst_exp.len, rule->subst_exp.s, + rule->repl_exp.len, rule->repl_exp.s, + rule->attrs.len, rule->attrs.s, + rule->timerec.len, rule->timerec.s); } /* Retrieves the corresponding entry of the given partition name */ diff --git a/modules/dialplan/dp_db.h b/modules/dialplan/dp_db.h index f97dba19d30..c22911d58ec 100644 --- a/modules/dialplan/dp_db.h +++ b/modules/dialplan/dp_db.h @@ -33,19 +33,20 @@ #define DP_PARTITION "default" #define DP_TABLE_NAME "dialplan" -#define DPID_COL "dpid" -#define PR_COL "pr" +#define DPID_COL "dpid" +#define PR_COL "pr" #define MATCH_OP_COL "match_op" #define MATCH_EXP_COL "match_exp" #define MATCH_FLAGS_COL "match_flags" #define SUBST_EXP_COL "subst_exp" #define REPL_EXP_COL "repl_exp" #define DISABLED_COL "disabled" -#define ATTRS_COL "attrs" +#define ATTRS_COL "attrs" +#define TIMEREC_COL "timerec" #define DP_TABLE_VERSION 4 -#define DP_TABLE_COL_NO 8 +#define DP_TABLE_COL_NO 9 typedef struct dp_head{ str partition;/*Attribute that uniquely identifies head*/ @@ -68,6 +69,7 @@ extern str match_flags_column; extern str subst_exp_column; extern str repl_exp_column; extern str attrs_column; +extern str timerec_column; extern str disabled_column; struct dp_param_list; diff --git a/modules/dialplan/dp_repl.c b/modules/dialplan/dp_repl.c index 1a0b53ebc8b..6cc5f8fdac4 100644 --- a/modules/dialplan/dp_repl.c +++ b/modules/dialplan/dp_repl.c @@ -25,6 +25,7 @@ */ #include "../../re.h" +#include "../../time_rec.h" #include "dialplan.h" #define MAX_REPLACE_WITH 10 @@ -275,11 +276,95 @@ int rule_translate(struct sip_msg *msg, str string, dpl_node_t * rule, return -1; } +int timerec_print(tmrec_p _trp) +{ + static char *_wdays[] = {"SU", "MO", "TU", "WE", "TH", "FR", "SA"}; + int i; + + if(!_trp) + { + LM_DBG("\n(null)\n"); + return -1; + } + LM_DBG("Recurrence definition\n-- start time ---\n"); + LM_DBG("Sys time: %d\n", (int)_trp->dtstart); + LM_DBG("Time: %02d:%02d:%02d\n", _trp->ts.tm_hour, + _trp->ts.tm_min, _trp->ts.tm_sec); + LM_DBG("Date: %s, %04d-%02d-%02d\n", _wdays[_trp->ts.tm_wday], + _trp->ts.tm_year+1900, _trp->ts.tm_mon+1, _trp->ts.tm_mday); + LM_DBG("---\n"); + LM_DBG("End time: %d\n", (int)_trp->dtend); + LM_DBG("Duration: %d\n", (int)_trp->duration); + LM_DBG("Until: %d\n", (int)_trp->until); + LM_DBG("Freq: %d\n", (int)_trp->freq); + LM_DBG("Interval: %d\n", (int)_trp->interval); + if(_trp->byday) + { + LM_DBG("Byday: "); + for(i=0; i<_trp->byday->nr; i++) + LM_DBG(" %d%s", _trp->byday->req[i], _wdays[_trp->byday->xxx[i]]); + LM_DBG("\n"); + } + if(_trp->bymday) + { + LM_DBG("Bymday: %d:", _trp->bymday->nr); + for(i=0; i<_trp->bymday->nr; i++) + LM_DBG(" %d", _trp->bymday->xxx[i]*_trp->bymday->req[i]); + LM_DBG("\n"); + } + if(_trp->byyday) + { + LM_DBG("Byyday:"); + for(i=0; i<_trp->byyday->nr; i++) + LM_DBG(" %d", _trp->byyday->xxx[i]*_trp->byyday->req[i]); + LM_DBG("\n"); + } + if(_trp->bymonth) + { + LM_DBG("Bymonth: %d:", _trp->bymonth->nr); + for(i=0; i< _trp->bymonth->nr; i++) + LM_DBG(" %d", _trp->bymonth->xxx[i]*_trp->bymonth->req[i]); + LM_DBG("\n"); + } + if(_trp->byweekno) + { + LM_DBG("Byweekno: "); + for(i=0; i<_trp->byweekno->nr; i++) + LM_DBG(" %d", _trp->byweekno->xxx[i]*_trp->byweekno->req[i]); + LM_DBG("\n"); + } + LM_DBG("Weekstart: %d\n", _trp->wkst); + return 0; +} + +// Validate Passed Time Recurrence Instance +static inline int check_time(tmrec_t *time_rec) { + ac_tm_t att; + + // No TimeRec: Rule is Valid + if(time_rec->dtstart == 0) + return 1; + + // Uncomment to enable Debug + // timerec_print(time_rec); + + // Set Current Time + memset(&att, 0, sizeof(att)); + if(ac_tm_set_time(&att, time(0))) + return -1; + + // Check_Tmrec will return 0 on successfully time recurrence match + if(check_tmrec(time_rec, &att, 0) != 0) + return 0; + + // Recurrence Matched -- Validating Rule + return 1; +} + #define DP_MAX_ATTRS_LEN 32 static char dp_attrs_buf[DP_MAX_ATTRS_LEN+1]; -int translate(struct sip_msg *msg, str input, str * output, dpl_id_p idp, - str * attrs) -{ +int translate(struct sip_msg *msg, str input, str * output, dpl_id_p idp, str * attrs) { + dpl_node_p rulep, rrulep; int string_res = -1, regexp_res = -1, bucket; @@ -298,9 +383,19 @@ int translate(struct sip_msg *msg, str input, str * output, dpl_id_p idp, if(rulep->match_exp.len != input.len) continue; - LM_DBG("Comparing (input %.*s) with (rule %.*s) [%d]\n", + LM_DBG("Comparing (input %.*s) with (rule %.*s) [%d] and timerec %.*s\n", input.len, input.s, rulep->match_exp.len, rulep->match_exp.s, - rulep->match_flags); + rulep->match_flags, rulep->timerec.len, rulep->timerec.s); + + // Check for Time Period if Set + if(rulep->parsed_timerec) { + LM_DBG("Timerec exists for rule checking: %.*s\n", rulep->timerec.len, rulep->timerec.s); + // Doesn't matches time period continue with next rule + if(!check_time(rulep->parsed_timerec)) { + LM_DBG("Time rule doesn't match: skip next!\n"); + continue; + } + } if (rulep->match_flags & DP_CASE_INSENSITIVE) { string_res = strncasecmp(rulep->match_exp.s,input.s,input.len); @@ -314,8 +409,17 @@ int translate(struct sip_msg *msg, str input, str * output, dpl_id_p idp, } /* try to match the input in the regexp bucket */ - for (rrulep = idp->rule_hash[DP_INDEX_HASH_SIZE].first_rule; - rrulep; rrulep=rrulep->next) { + for (rrulep = idp->rule_hash[DP_INDEX_HASH_SIZE].first_rule; rrulep; rrulep=rrulep->next) { + + // Check for Time Period if Set + if(rrulep->parsed_timerec) { + LM_DBG("Timerec exists for rule checking: %.*s\n", rrulep->timerec.len, rrulep->timerec.s); + // Doesn't matches time period continue with next rule + if(!check_time(rrulep->parsed_timerec)) { + LM_DBG("Time rule doesn't match: skip next!\n"); + continue; + } + } regexp_res = (test_match(input, rrulep->match_comp, matches, MAX_MATCHES) >= 0 ? 0 : -1); From 92647beaa791cf8bc34af44b12379634a1455264 Mon Sep 17 00:00:00 2001 From: Parantido Julius De Rica Date: Mon, 1 Sep 2014 15:21:21 +0200 Subject: [PATCH 2/2] Fixed timerec_parsed memory leak problem. --- modules/dialplan/dp_db.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/modules/dialplan/dp_db.c b/modules/dialplan/dp_db.c index e64e1217c23..e12e7d63860 100644 --- a/modules/dialplan/dp_db.c +++ b/modules/dialplan/dp_db.c @@ -561,10 +561,11 @@ dpl_node_t * build_rule(db_val_t * values) return new_rule; err: - if(match_comp) wrap_pcre_free(match_comp); - if(subst_comp) wrap_pcre_free(subst_comp); - if(repl_comp) repl_expr_free(repl_comp); - if(new_rule) destroy_rule(new_rule); + if(parsed_timerec) shm_free(parsed_timerec); + if(match_comp) wrap_pcre_free(match_comp); + if(subst_comp) wrap_pcre_free(subst_comp); + if(repl_comp) repl_expr_free(repl_comp); + if(new_rule) destroy_rule(new_rule); return NULL; }