carrierroute: Remove dependency on libconfuse, based on the patch by Lucian Balaceanu...
authorCarsten Bock <carsten@ng-voice.com>
Thu, 13 Mar 2014 13:52:49 +0000 (14:52 +0100)
committerCarsten Bock <carsten@ng-voice.com>
Fri, 14 Mar 2014 13:47:32 +0000 (14:47 +0100)
INSTALL
modules/carrierroute/Makefile
modules/carrierroute/cr_config.c
modules/carrierroute/doc/carrierroute_admin.xml
modules/carrierroute/parser_carrierroute.c [new file with mode: 0644]
modules/carrierroute/parser_carrierroute.h [new file with mode: 0644]

diff --git a/INSTALL b/INSTALL
index 053b88a..b69b409 100644 (file)
--- a/INSTALL
+++ b/INSTALL
@@ -126,8 +126,6 @@ Requirements:
 - libsnmp9 - libs and devel headers - if you want SNMP client functionality 
   (SNMP AgentX subagent) for Kamailio
 - libldap libs and devel headers v2.1 or greater - if you want LDAP support
-- libconfuse and devel headers - if you want to compile the carrierroute
-  module
 - libpcre libs and devel headers - if you want to compile the lcr and dialplan
   modules
 - libsctp devel headers - if you want to compile the SCTP transport in the core
index b06441a..4117bbb 100644 (file)
@@ -9,23 +9,6 @@ include ../../Makefile.defs
 auto_gen=
 NAME=carrierroute.so
 
-ifeq ($(CROSS_COMPILE),)
-CONFUSE_BUILDER = $(shell \
-       if which confuse-config &>/dev/null;then \
-               echo 'confuse-config'; \
-       elif pkg-config --exists libconfuse; then \
-               echo 'pkg-config libconfuse'; \
-       fi)
-endif
-
-ifeq ($(CONFUSE_BUILDER),)
-       DEFS += -I$(LOCALBASE)/include
-       LIBS += -L$(LOCALBASE)/lib -lconfuse
-else
-       DEFS += $(shell $(CONFUSE_BUILDER) --cflags)
-       LIBS += $(shell $(CONFUSE_BUILDER) --libs)
-endif
-
 DEFS+=-DKAMAILIO_MOD_INTERFACE
 
 SERLIBPATH=../../lib
index 92f96a9..d07c5c0 100644 (file)
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 
-/**
+/*!
  * \file cr_config.c
  * \brief Functions for load and save routing data from a config file.
  * \ingroup carrierroute
  * - Module; \ref carrierroute
  */
 
-#include <confuse.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <unistd.h>
 #include "cr_rule.h"
 #include "cr_domain.h"
 #include "cr_carrier.h"
+#include "parser_carrierroute.h"
 
+enum target_opt_ids { TO_ID_COMMENT = 0, TO_ID_STRIP, TO_ID_REWR_PREFIX, TO_ID_PROB, TO_ID_HASH_INDEX,
+                                         TO_ID_REWR_SUFFIX, TO_ID_STATUS, TO_ID_BACKED_UP, TO_ID_BACKUP, TO_MAX_IDS };
+enum prefix_opt_ids { PO_MAX_TARGETS = 0, PO_MAX_IDS };
 
-/**
- * reports errors during config file parsing using LOG macro
- *
- * @param cfg points to the current config data structure
- * @param fmt a format string
- * @param ap format arguments
- */
-static void conf_error(cfg_t *cfg, const char * fmt, va_list ap) {
-       int ret;
-       static char buf[1024];
-
-       ret = vsnprintf(buf, sizeof(buf), fmt, ap);
-       if (ret < 0 || ret >= sizeof(buf)) {
-               LM_ERR("could not print error message\n");
-       } else {
-               // FIXME this don't seems to work reliable in all cases, charset 
-               // problems
-               LM_GEN1(L_ERR, "%s", buf);
+option_description target_options[TO_MAX_IDS];
+option_description prefix_options[PO_MAX_IDS];
+
+static void reset_opts(option_description * opts, int size){
+       int i;
+       if ( NULL == opts){
+               LM_ERR("Trying to init a NULL pointer location \n");
+               return;
        }
+       for (i=0; i < size; i++){
+               memset(&(opts[i].value),'\0', sizeof(union opt_data));
+               opts[i].visited = 0;
+               opts[i].no_elems = 0;
+               if ( CFG_STR == opts[i].type ){
+                       opts[i].value.string_data.s = opts[i].str_buf;
+                       strcpy(opts[i].str_buf,"");
+                       opts[i].value.string_data.len = 0;
+               }
+       }
+       return;
 }
 
-
-/**
- * Parses the config file
- *
- * @return a pointer to the configuration data structure, NULL on failure
- */
-static cfg_t * parse_config(void) {
-       cfg_t * cfg = NULL;
-
-       cfg_opt_t target_opts[] = {
-                                     CFG_STR("comment", 0, CFGF_NONE),
-                                     CFG_INT("strip", 0, CFGF_NONE),
-                                     CFG_STR("rewrite_prefix", 0, CFGF_NONE),
-                                     CFG_FLOAT("prob", 0, CFGF_NONE),
-                                     CFG_INT("hash_index", 0, CFGF_NONE),
-                                     CFG_STR("rewrite_suffix", 0, CFGF_NONE),
-                                     CFG_INT("status", 1, CFGF_NONE),
-                                     CFG_INT_LIST("backed_up", NULL, CFGF_NONE),
-                                     CFG_INT("backup", -1, CFGF_NONE),
-                                     CFG_END()
-                                 };
-
-       cfg_opt_t prefix_opts[] = {
-                                     CFG_SEC("target", target_opts, CFGF_MULTI | CFGF_TITLE),
-                                     CFG_INT("max_targets", -1, CFGF_NONE),
-                                     CFG_END()
-                                 };
-
-       cfg_opt_t domain_opts[] = {
-                                     CFG_SEC("prefix", prefix_opts, CFGF_MULTI | CFGF_TITLE),
-                                     CFG_END()
-                                 };
-
-       cfg_opt_t opts[] = {
-                              CFG_SEC("domain", domain_opts, CFGF_MULTI | CFGF_TITLE),
-                              CFG_END()
-                          };
-
-       cfg = cfg_init(opts, CFGF_NONE);
-       if (cfg == NULL) {
-               LM_ERR("could not initialize configuration\n");
-               return NULL;
+static int init_target_opts(option_description * opts){
+       if ( NULL == opts){
+               LM_DBG("Trying to init a NULL pointer location \n");
+               return -1;
        }
+       memset(opts, '\0', sizeof(option_description) * TO_MAX_IDS);
+
+       strcpy((char*)(opts[TO_ID_COMMENT].name),    "comment");
+       strcpy((char*)(opts[TO_ID_STRIP].name),      "strip");
+       strcpy((char*)(opts[TO_ID_REWR_PREFIX].name),"rewrite_prefix");
+       strcpy((char*)(opts[TO_ID_PROB].name),       "prob");
+       strcpy((char*)(opts[TO_ID_HASH_INDEX].name), "hash_index");
+       strcpy((char*)(opts[TO_ID_REWR_SUFFIX].name),"rewrite_suffix");
+       strcpy((char*)(opts[TO_ID_STATUS].name),     "status");
+       strcpy((char*)(opts[TO_ID_BACKED_UP].name),  "backed_up");
+       strcpy((char*)(opts[TO_ID_BACKUP].name),     "backup");
+
+       opts[TO_ID_COMMENT    ].type=CFG_STR;
+       opts[TO_ID_STRIP      ].type=CFG_INT;
+       opts[TO_ID_REWR_PREFIX].type=CFG_STR;
+       opts[TO_ID_PROB       ].type=CFG_FLOAT;
+       opts[TO_ID_HASH_INDEX ].type=CFG_INT;
+       opts[TO_ID_REWR_SUFFIX].type=CFG_STR;
+       opts[TO_ID_STATUS     ].type=CFG_INT;
+       opts[TO_ID_BACKED_UP  ].type=CFG_INT_LIST;
+       opts[TO_ID_BACKUP     ].type=CFG_INT;
+
+       reset_opts(opts, TO_MAX_IDS);
+       return 0;
+}
 
-       cfg_set_error_function(cfg, conf_error);
-
-       switch (cfg_parse(cfg, config_file)) {
-               case CFG_FILE_ERROR: LM_ERR("file not found: %s\n", config_file);
-                       return NULL;
-               case CFG_PARSE_ERROR: LM_ERR("error while parsing %s in line %i, section %s\n",
-                                                 cfg->filename, cfg->line, cfg->name);
-                       return NULL;
-               case CFG_SUCCESS: break;
+static int init_prefix_opts(option_description * opts){
+       if ( NULL == opts){
+               LM_DBG("Trying to init a NULL pointer location \n");
+               return -1;
        }
-       return cfg;
+       memset(opts, '\0', sizeof(option_description) * PO_MAX_IDS);
+       strcpy((char*)(opts[PO_MAX_TARGETS].name), "max_targets");
+       opts[PO_MAX_TARGETS].type=CFG_INT;
+       return 0;
 }
 
-
 static int backup_config(void) {
        FILE * from, * to;
        char * backup_file, ch;
@@ -188,10 +176,11 @@ errout:
        return -1;
 }
 
-
 /**
  * Loads the routing data from the config file given in global
  * variable config_data and stores it in routing tree rd.
+ * The function mixes code parsing calls with rd structure
+ * completion.
  *
  * @param rd Pointer to the route data tree where the routing data
  * shall be loaded into
@@ -200,173 +189,277 @@ errout:
  *
  */
 int load_config(struct route_data_t * rd) {
-       cfg_t * cfg = NULL;
-       int m, o, i, j, k,l, status, hash_index, max_targets, strip;
-       cfg_t * d, * p, * t;
+       FILE * file;
+
+       int ret_domain, ret_prefix, ret_target, ret_prefix_opts, ret_target_opts;
+       int domain_id, allocated_domain_num = DEFAULT_DOMAIN_NUM;
+       str domain_name, prefix_name, rewrite_host;
+       char domain_buf[CR_MAX_LINE_SIZE], prefix_buf[CR_MAX_LINE_SIZE],  rewrite_buf[CR_MAX_LINE_SIZE];
+
+       str rewrite_prefix, rewrite_suffix, comment;
+       struct domain_data_t *domain_data = NULL;
        struct carrier_data_t * tmp_carrier_data;
-       int domain_id;
-       str domain, prefix, rewrite_prefix, rewrite_suffix, rewrite_host, comment;
+       int hash_index, max_targets = 0, strip;
        double prob;
        int * backed_up = NULL;
-       int backed_up_size, backup;
-       backed_up_size = backup = 0;
+       int backed_up_size = 0, backup = 0, status;
+       void* p_realloc;
+       int i=0, l, k;
+
+       domain_name.s = domain_buf; domain_name.len = CR_MAX_LINE_SIZE;
+       prefix_name.s = prefix_buf; prefix_name.len = CR_MAX_LINE_SIZE;
+       rewrite_host.s = rewrite_buf; rewrite_host.len = CR_MAX_LINE_SIZE;
 
-       if ((cfg = parse_config()) == NULL) {
+       /* open configuration file */
+       if ((file = fopen(config_file, "rb"))==NULL) {
+               LM_ERR("Cannot open source file.\n");
                return -1;
        }
 
        rd->carrier_num = 1;
        rd->first_empty_carrier = 0;
-       rd->domain_num = cfg_size(cfg, "domain");
+       rd->domain_num = 0;
 
        if ((rd->carriers = shm_malloc(sizeof(struct carrier_data_t *))) == NULL) {
                SHM_MEM_ERROR;
-               return -1;
+               goto errclose;
        }
        memset(rd->carriers, 0, sizeof(struct carrier_data_t *));
 
        /* Create carrier map */
        if ((rd->carrier_map = shm_malloc(sizeof(struct name_map_t))) == NULL) {
                SHM_MEM_ERROR;
-               return -1;
+               goto errclose;
        }
+
        memset(rd->carrier_map, 0, sizeof(struct name_map_t));
        rd->carrier_map[0].id = 1;
        rd->carrier_map[0].name.len = default_tree.len;
        rd->carrier_map[0].name.s = shm_malloc(rd->carrier_map[0].name.len);
+
        if (rd->carrier_map[0].name.s == NULL) {
                SHM_MEM_ERROR;
-               return -1;
+               goto errclose;
        }
        memcpy(rd->carrier_map[0].name.s, default_tree.s, rd->carrier_map[0].name.len);
 
        /* Create domain map */
-       if ((rd->domain_map = shm_malloc(sizeof(struct name_map_t) * rd->domain_num)) == NULL) {
+       if ((rd->domain_map = shm_malloc(sizeof(struct name_map_t) * allocated_domain_num)) == NULL) {
                SHM_MEM_ERROR;
-               return -1;
+               goto errclose;
        }
-       memset(rd->domain_map, 0, sizeof(struct name_map_t) * rd->domain_num);
-       for (i=0; i<rd->domain_num; i++) {
-               d = cfg_getnsec(cfg, "domain", i);
-               domain.s = (char *)cfg_title(d);
-               if (domain.s==NULL) domain.s="";
-               domain.len = strlen(domain.s);
-               rd->domain_map[i].id = i+1;
-               rd->domain_map[i].name.len = domain.len;
-               rd->domain_map[i].name.s = shm_malloc(rd->domain_map[i].name.len);
-               if (rd->domain_map[i].name.s == NULL) {
-                       SHM_MEM_ERROR;
-                       return -1;
-               }
-               memcpy(rd->domain_map[i].name.s, domain.s, rd->domain_map[i].name.len);
-       }
-       /* sort domain map by id for faster access */
-       qsort(rd->domain_map, rd->domain_num, sizeof(rd->domain_map[0]), compare_name_map);
+       memset(rd->domain_map, 0, sizeof(struct name_map_t) * allocated_domain_num);
 
        /* Create and insert carrier data structure */
-       tmp_carrier_data = create_carrier_data(1, &rd->carrier_map[0].name, rd->domain_num);
+       tmp_carrier_data = create_carrier_data(1, &rd->carrier_map[0].name, allocated_domain_num);
+       tmp_carrier_data->domain_num = 0;
+       tmp_carrier_data->id = 1;
+       tmp_carrier_data->name = &(rd->carrier_map[0].name);
+
        if (tmp_carrier_data == NULL) {
                LM_ERR("can't create new carrier\n");
-               return -1;
+               goto errclose;
        }
        if (add_carrier_data(rd, tmp_carrier_data) < 0) {
                LM_ERR("couldn't add carrier data\n");
                destroy_carrier_data(tmp_carrier_data);
-               return -1;
+               goto errclose;
        }
 
-       /* add all routes */
-       for (i = 0; i < rd->domain_num; i++) {
-               d = cfg_getnsec(cfg, "domain", i);
-               domain.s = (char *)cfg_title(d);
-               if (domain.s==NULL) domain.s="";
-               domain.len = strlen(domain.s);
-               m = cfg_size(d, "prefix");
-
-               LM_INFO("loading domain %.*s\n", domain.len, domain.s);
-               for (j = 0; j < m; j++) {
-                       p = cfg_getnsec(d, "prefix", j);
-                       prefix.s = (char *)cfg_title(p);
-                       if (prefix.s==NULL) prefix.s="";
-                       prefix.len = strlen(prefix.s);
-                       if (str_strcasecmp(&prefix, &CR_EMPTY_PREFIX) == 0) {
-                               prefix.s = "";
-                               prefix.len = 0;
+       init_prefix_opts(prefix_options);
+       init_target_opts(target_options);
+
+       /* add all routes by parsing the route conf file */
+       /* while there are domain structures, get name and parse the structure*/
+       while ((ret_domain = parse_struct_header(file, "domain", &domain_name))
+                       == SUCCESSFUL_PARSING) {
+
+               domain_id = ++rd->domain_num;
+               tmp_carrier_data->domain_num++;
+
+               /* (re)allocate memory for a maximum of MAX_DOMAIN_NUM domains
+                rd is not fully allocated from the start as this would require the preparsing
+                of the entire route file */
+               if ( rd->domain_num > allocated_domain_num){
+
+                       if (MAX_DOMAIN_NUM <= allocated_domain_num){
+                               LM_ERR("Maximum number of domains reached");
+                               break;
                        }
 
-                       LM_INFO("loading prefix %.*s\n", prefix.len, prefix.s);
-                       max_targets = cfg_getint(p, "max_targets");
-                       o = cfg_size(p, "target");
-                       for (k = 0; k < o; k++) {
-                               t = cfg_getnsec(p, "target", k);
-                               rewrite_host.s = (char *)cfg_title(t);
-                               if (rewrite_host.s==NULL) rewrite_host.s="";
-                               rewrite_host.len = strlen(rewrite_host.s);
+                       LM_INFO("crt_alloc_size=%d must be increased \n", allocated_domain_num);
+                       allocated_domain_num *= 2;
+
+                       if ( ( p_realloc = shm_realloc(rd->domain_map,
+                                       sizeof(struct name_map_t) * allocated_domain_num) ) == NULL)
+                       {
+                               SHM_MEM_ERROR;
+                               goto errclose;
+                       }
+
+                       rd->domain_map = (struct name_map_t *)p_realloc;
+
+                       if (( p_realloc = shm_realloc( rd->carriers[0]->domains,
+                                       sizeof(struct domain_data_t *) * allocated_domain_num)) == NULL) {
+                               SHM_MEM_ERROR;
+                               goto errclose;
+                       }
+                       rd->carriers[0]->domains = (struct domain_data_t **)p_realloc;
+
+                       for (i=0; i<rd->domain_num-1; i++){
+                               rd->carriers[0]->domains[i]->name = &(rd->domain_map[i].name);
+                       }
+               }// end of mem (re)allocation for domains
+
+               /*insert domain in domain map*/
+               rd->domain_map[domain_id-1].id = domain_id;
+               rd->domain_map[domain_id-1].name.len = domain_name.len;
+               rd->domain_map[domain_id-1].name.s = shm_malloc(rd->domain_map[domain_id-1].name.len);
+               if (rd->domain_map[domain_id-1].name.s == NULL) {
+                       SHM_MEM_ERROR;
+                       goto errclose;
+               }
+               memcpy(rd->domain_map[domain_id-1].name.s, domain_name.s, rd->domain_map[domain_id-1].name.len);
+
+               /* create new domain data */
+               if ((domain_data = create_domain_data(domain_id,&(rd->domain_map[domain_id-1].name))) == NULL) {
+                       LM_ERR("could not create new domain data\n");
+                       goto errclose;
+               }
+
+               if (add_domain_data(tmp_carrier_data, domain_data, domain_id-1) < 0) {
+                       LM_ERR("could not add domain data\n");
+                       destroy_domain_data(domain_data);
+                       goto errclose;
+               }
+               LM_DBG("added domain %d '%.*s' to carrier %d '%.*s'\n",
+                               domain_id, domain_name.len, domain_name.s,
+                               tmp_carrier_data->id, tmp_carrier_data->name->len, tmp_carrier_data->name->s);
+
+               /* while there are prefix structures, get name and parse the structure */
+               while ((ret_prefix = parse_struct_header(file, "prefix", &prefix_name))
+                               == SUCCESSFUL_PARSING) {
+
+                       reset_opts(prefix_options, PO_MAX_IDS);
+                       if (str_strcasecmp(&prefix_name, &CR_EMPTY_PREFIX) == 0) {
+                               prefix_name.s[0] = '\0';
+                               prefix_name.len = 0;
+                       }
+
+                       /* look for max_targets = value which is described in prefix_options */
+                       if ((ret_prefix_opts = parse_options(file, prefix_options,
+                                       PO_MAX_IDS, "target")) != SUCCESSFUL_PARSING) {
+                               LM_ERR("Error in parsing \n");
+                               goto errclose;
+                       }
+
+                       max_targets = prefix_options[PO_MAX_TARGETS].value.int_data;
+                       /* look for max_targets target structures */
+                       for ( k = 0; k < max_targets; k++) {
+                               /* parse the target header, get name and continue*/
+                               ret_target = parse_struct_header(file, "target", &rewrite_host);
+                               if (ret_target != SUCCESSFUL_PARSING) {
+                                       LM_ERR("Error in parsing \n");
+                                       goto errclose;
+                               }
+
+                               reset_opts(target_options, TO_MAX_IDS);
+                               /* look for the target options: prob, hash index, status, etc*/
+                               ret_target_opts = parse_options(file, target_options, TO_MAX_IDS, "}");
+                               if ( SUCCESSFUL_PARSING == ret_target_opts ){
+                                       /* parsing target structure closing bracket*/
+                                       parse_struct_stop(file);
+                               }else{
+                                       LM_ERR("Error in parsing in target options \n");
+                                       goto errclose;
+                               }
+                               /* intermediary variables for more lisibility */
                                if (str_strcasecmp(&rewrite_host, &CR_EMPTY_PREFIX) == 0) {
-                                       rewrite_host.s = "";
+                                       rewrite_host.s[0] = '\0';
                                        rewrite_host.len = 0;
                                }
-
-                               LM_INFO("loading target %.*s\n", rewrite_host.len, rewrite_host.s);
-                               prob = cfg_getfloat(t, "prob");
-                               strip = cfg_getint(t, "strip");
-                               rewrite_prefix.s = (char *)cfg_getstr(t, "rewrite_prefix");
-                               if (rewrite_prefix.s==NULL) rewrite_prefix.s="";
-                               rewrite_prefix.len = strlen(rewrite_prefix.s);
-                               rewrite_suffix.s = (char *)cfg_getstr(t, "rewrite_suffix");
-                               if (rewrite_suffix.s==NULL) rewrite_suffix.s="";
-                               rewrite_suffix.len = strlen(rewrite_suffix.s);
-                               hash_index = cfg_getint(t, "hash_index");
-                               comment.s = (char *)cfg_getstr(t, "comment");
-                               if (comment.s==NULL) comment.s="";
-                               comment.len = strlen(comment.s);
-                               status = cfg_getint(t, "status");
-
-                               if ((backed_up_size = cfg_size(t, "backed_up")) > 0) {
+                               LM_DBG("loading target %.*s\n", rewrite_host.len, rewrite_host.s);
+                               prob = target_options[TO_ID_PROB].value.float_data;
+                               strip = target_options[TO_ID_STRIP].value.int_data;
+                               rewrite_prefix.s = target_options[TO_ID_REWR_PREFIX].value.string_data.s;
+                               rewrite_prefix.len = target_options[TO_ID_REWR_PREFIX].value.string_data.len;
+                               rewrite_suffix.s = target_options[TO_ID_REWR_SUFFIX].value.string_data.s;
+                               rewrite_suffix.len = target_options[TO_ID_REWR_SUFFIX].value.string_data.len;
+                               hash_index = target_options[TO_ID_HASH_INDEX].value.int_data;
+                               comment.s = target_options[TO_ID_COMMENT].value.string_data.s;
+                               comment.len = target_options[TO_ID_COMMENT].value.string_data.len;
+                               status = target_options[TO_ID_STATUS].value.int_data;
+
+                               if ( (backed_up_size = target_options[TO_ID_BACKED_UP].no_elems) > 0){
                                        if ((backed_up = pkg_malloc(sizeof(int) * (backed_up_size + 1))) == NULL) {
                                                PKG_MEM_ERROR;
-                                               return -1;
+                                               goto errclose;
                                        }
                                        for (l = 0; l < backed_up_size; l++) {
-                                               backed_up[l] = cfg_getnint(t, "backed_up", l);
+                                               backed_up[l] = target_options[TO_ID_BACKED_UP].value.int_list[l];
                                        }
                                        backed_up[backed_up_size] = -1;
                                }
-                               backup = cfg_getint(t, "backup");
-
-                               domain_id = map_name2id(rd->domain_map, rd->domain_num, &domain);
-                               if (domain_id < 0) {
-                                       LM_ERR("cannot find id for domain '%.*s'", domain.len, domain.s);
-                                       if (backed_up) {
-                                               pkg_free(backed_up);
-                                       }
-                                       return -1;
-                               }
-
-                               LM_INFO("adding route for prefix %.*s, to host %.*s, prob %f, backed up: %i, backup: %i\n",
-                                   prefix.len, prefix.s, rewrite_host.len, rewrite_host.s, prob, backed_up_size, backup);
-                               if (add_route(rd, 1, domain_id, &prefix, 0, 0, max_targets, prob, &rewrite_host,
-                                             strip, &rewrite_prefix, &rewrite_suffix, status,
-                                             hash_index, backup, backed_up, &comment) < 0) {
+                               backup = target_options[TO_ID_BACKUP].value.int_data;
+
+                               LM_ERR("\n Adding route to tree <'%.*s'>: prefix_name:%s\n,"
+                                               " max_targets =%d\n, prob=%f\n, rewr_host=%s\n,"
+                                               " strip=%i\n, rwr_prefix=%s\n, rwr_suff=%s\n,"
+                                               " status=%i\n, hash_index=%i\n, comment=%s \n",
+                                               domain_data->name->len, domain_data->name->s, prefix_name.s,
+                                               max_targets, prob, rewrite_host.s, strip, rewrite_prefix.s,
+                                               rewrite_suffix.s, status, hash_index, comment.s);
+
+                               if (add_route_to_tree(domain_data->tree, &prefix_name, 0, 0,
+                                               &prefix_name, max_targets, prob, &rewrite_host,
+                                               strip, &rewrite_prefix, &rewrite_suffix, status,
+                                               hash_index, backup, backed_up, &comment) < 0) {
                                        LM_INFO("Error while adding route\n");
                                        if (backed_up) {
                                                pkg_free(backed_up);
                                        }
-                                       return -1;
+                                       goto errclose;
                                }
+
                                if (backed_up) {
                                        pkg_free(backed_up);
                                }
                                backed_up = NULL;
                        }
+
+                       if (k != prefix_options[0].value.int_data ) {
+                               LM_ERR("Error in parsing: max_targets =%i, actual targets =%i \n",
+                                               prefix_options[0].value.int_data, i);
+                               goto errclose;
+                       }
+                       /* parsing prefix structure closing bracket */
+                       if (parse_struct_stop(file) != SUCCESSFUL_PARSING) {
+                               LM_ERR("Error in parsing targets, expecting } \n");
+                               goto errclose;
+
+                       }
+               } // END OF PREFIX part
+
+               /* parsing domain structure closing bracket */
+               if (parse_struct_stop(file) != SUCCESSFUL_PARSING) {
+                       LM_ERR("Error in parsing targets, expecting } \n");
+                       goto errclose;
                }
+       }
 
+       if (EOF_REACHED != ret_domain){
+               LM_ERR("Error appeared while parsing domain header \n");
+               goto errclose;
        }
-       cfg_free(cfg);
+
+       LM_ERR("File parsed successfully \n");
+       fclose(file);
        return 0;
+errclose:
+       fclose(file);
+       return -1;
 }
 
-
 /**
  * Does the work for save_config, traverses the routing data tree
  * and writes each rule to file.
index 11c1b6a..2780114 100644 (file)
                </itemizedlist>
            </para>
        </section>
-       <section>
-           <title>External Libraries or Applications</title>
-           <para>
-               The following libraries or applications must be installed before running
-               &kamailio; with this module loaded:
-               <itemizedlist>
-                   <listitem>
-                       <para>
-                               <emphasis>libconfuse</emphasis>, a configuration file parser library.
-                               ( http://www.nongnu.org/confuse/ )
-                       </para>
-                   </listitem>
-               </itemizedlist>
-           </para>
-       </section>
     </section>
     <section>
        <title>Parameters</title>
diff --git a/modules/carrierroute/parser_carrierroute.c b/modules/carrierroute/parser_carrierroute.c
new file mode 100644 (file)
index 0000000..d49050a
--- /dev/null
@@ -0,0 +1,411 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 1&1 Internet AG
+ *
+ * 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
+ */
+
+/*!
+ * \file parser_carrierroute.c
+ * \brief Functions for parsing the config file of cr when using file mode.
+ * \ingroup carrierroute
+ * - Module; \ref carrierroute
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <float.h>
+#include <math.h>
+#include <errno.h>
+#include "parser_carrierroute.h"
+
+#include "../../ut.h"
+#include "../../trim.h"
+
+#define assert_not_null(s) do { \
+       if ( NULL == (s) ){ \
+               LM_INFO("Unexpected null option \n"); \
+               return ERROR_IN_PARSING;\
+       } \
+} while (0)
+
+
+/**
+ * Gets index of given option inside option list
+ *
+ * @param opt_name    name of option
+ * @param opt_list    points to the option list
+ * @param no_options  size of option list
+ *
+ * @return index of option inside option list, -1 not found
+ */
+int get_option_position(char* opt_name, option_description* opt_list, int no_options){
+       int i;
+       for (i = 0; i<no_options; i++){
+               if (strcmp(opt_name, opt_list[i].name) == 0){
+                       return i;
+               }
+       }
+       return -1;
+}
+
+
+/**
+ * Fills special structure with params from src string
+ *
+ * @param int_list    string of type {INT_VAL [, INT_VAL]*}
+ * @param opts        destination option_description variable
+ *
+ * @return number of integers in int_list, on success
+ *         ERROR_IN_PARSING, on error
+ */
+int parse_int_list(char *int_list, option_description* opts){
+       char *pch, *endptr;
+       long val;
+
+       pch = strtok (int_list,", \t");
+
+       while ( pch != NULL )
+       {
+               LM_DBG("Parsing [%s] \n", pch);
+               if ( INT_LIST_MAX_SIZE == opts->no_elems){
+                       LM_ERR("INT LIST exceeds max allowed size of: %d \n", INT_LIST_MAX_SIZE);
+                       return ERROR_IN_PARSING;
+               }
+
+               endptr = NULL;
+               val = strtol(pch, &endptr, 10);
+
+               if ( val < 0 || val> INT_MAX){
+                       LM_ERR("Parsed value is out of bounds \n");
+                       return ERROR_IN_PARSING;
+               }
+
+               if (*endptr != '\0'){
+                       LM_ERR("Parsed value is not integer \n");
+                       return ERROR_IN_PARSING;
+               }
+
+               opts->value.int_list[opts->no_elems] = (int) val;
+               opts->no_elems ++;
+               pch = strtok (NULL, ", \t");
+       }
+
+       if ( 0 == opts->no_elems ){
+               LM_ERR("The number of int elements cannot be 0 \n");
+               return ERROR_IN_PARSING;
+       }
+       return opts->no_elems;
+}
+
+/**
+ * Tries to parse right value string into an option
+ *
+ * @param src   str source
+ * @param opt   the option to be filled
+ *
+ * @return SUCCESSFUL_PARSING, ERROR_IN_PARSING
+ */
+int parse_rv_option(str src, option_description* opt){
+       long l_val;
+       double d_val;
+       char* endptr;
+       int i, ret = ERROR_IN_PARSING;
+
+       switch (opt->type)
+       {
+               case CFG_INT:
+                       l_val = strtol(src.s, &endptr, 10);
+
+                       if (errno == ERANGE && (l_val == LONG_MAX || l_val == LONG_MIN)) {
+                                       LM_ERR("Conversion error for %s\n", src.s);
+                       }else
+                       if (*endptr != '\0'){
+                               LM_ERR("Value is not integer \n");
+                       }else
+                       if ( l_val < 0 || l_val> INT_MAX){
+                               LM_ERR("The number is out of bounds \n");
+                       }
+                       else{// successful rv conversion
+                               opt->value.int_data = l_val;
+                               LM_DBG("Key=<%s>, value=<%i> \n", opt->name, opt->value.int_data);
+                               ret = SUCCESSFUL_PARSING;
+                       }
+                       break;
+               case CFG_FLOAT:
+                       d_val = strtod(src.s, &endptr);
+
+                       if (errno == ERANGE && (d_val == -HUGE_VAL || d_val == HUGE_VAL)) {
+                               LM_ERR("Conversion error for %s\n", src.s);
+                       }else
+                       if (*endptr != '\0'){
+                               LM_ERR("Value is not float \n");
+                       }else
+                       if ( d_val < 0.0 || d_val> FLT_MAX){
+                               LM_ERR("The number is out of bounds \n");
+                       }else{
+                               opt->value.float_data = d_val;
+                               LM_DBG("Key=<%s>, value=<%f> \n", opt->name, opt->value.float_data);
+                               ret = SUCCESSFUL_PARSING;
+                       }
+                       break;
+               case CFG_STR:
+                       if ((src.s[0] != '"') && (src.s[src.len-1] != '"')){
+                               LM_ERR("Not a string \n");
+                       }
+                       else{
+                               // we now expect a string with NO " inside
+                               for (i=1; i< src.len-2; i++)
+                                       if (src.s[i] == '"') {
+                                               LM_ERR("Not a string \n");
+                                               return ERROR_IN_PARSING;
+                                       }
+                               strcpy( opt->value.string_data.s, src.s);
+                               opt->value.string_data.len = src.len;
+                               LM_DBG("String Key=<%s>, value=<%s> \n", opt->name, opt->value.string_data.s);
+                               ret = SUCCESSFUL_PARSING;
+                       }
+                       break;
+               case CFG_INT_LIST:
+                       // int list looks like: lv = {NO1 [,NO]*}
+                       if ((src.len == 2) || ((src.s[0] != '{') && (src.s[src.len-1] != '}'))){
+                               LM_ERR("Not an int list \n");
+                       }
+                       else{
+                               src.s[src.len-1]='\0';src.s++; src.len = src.len-2;
+                               //parse a list of comma separated integers
+                               if ( parse_int_list(src.s, opt) != ERROR_IN_PARSING ){
+                                       // dbg printing of parsed values
+                                       LM_DBG("The number of elements in int_list: %d \n", opt->no_elems);
+                                       for ( i =0; i< opt->no_elems; i++ ){
+                                               LM_DBG("        value=<%d> \n", opt->value.int_list[i]);
+                                       }
+                                       ret = SUCCESSFUL_PARSING;
+                               }
+                       }
+                       break;
+               default:
+                       break;
+       }
+
+       opt->visited = VISITED;
+       return ret;
+}
+
+/**
+ * Parses the options part in a file and populates a destination structure.
+ * The end of the options part should be signaled by end_str string.
+ *
+ * @param file pointer to source file
+ * @param opts destiation option structure to be populated
+ * @param no_options expected number of options
+ * @param end_str the delimiter that signals end of options zone
+ *
+ * @return  SUCCESSFUL_PARSING on success, ERROR_IN_PARSING on error
+ */
+int parse_options(FILE* file, option_description* opts, int no_options, char* end_str){
+       str  data, lv_str, rv_str;
+       char *pch, buf[CR_MAX_LINE_SIZE], lv[CR_MAX_LINE_SIZE], rv[CR_MAX_LINE_SIZE];
+       int opt_pos, full_line_len, ret;
+
+       ret = ERROR_IN_PARSING;
+       data.s = buf;
+
+       /* refactor data.s = buf using get_non_blank_line */
+       while ( get_non_blank_line( &data, CR_MAX_LINE_SIZE, file, &full_line_len) == 0  ) /* read a line */
+       {
+               LM_DBG("Parsing line: %.*s \n", data.len, data.s);
+
+               /* parsing stops when end_str is reached: e.g: }, target */
+               if ( strncmp(data.s, end_str, strlen(end_str)) == 0){
+                       LM_DBG("End of options list received \n");
+                       fseek(file, -full_line_len, SEEK_CUR);
+                       ret = SUCCESSFUL_PARSING;
+                       break;
+               }
+
+               /* Line must be of type lv = rv */
+               if ( (pch =  strchr(data.s,'=')) == NULL){
+                       LM_ERR("Parsed line does not contain = delimiter \n");
+                       break;
+               }
+               else{ /* parse lv, rv */
+                       strncpy(lv, data.s, pch-data.s); lv[pch-data.s]='\0';
+                       strncpy(rv, pch+1, CR_MAX_LINE_SIZE-1); rv[CR_MAX_LINE_SIZE-1]='\0';
+                       LM_DBG("lv=%s, rv=%s\n", lv, rv);
+                       lv_str.s=lv; lv_str.len=(int)strlen(lv); trim(&lv_str); lv_str.s[lv_str.len] = '\0';
+                       rv_str.s=rv; rv_str.len=(int)strlen(rv); trim(&rv_str); rv_str.s[rv_str.len] = '\0';
+
+                       if ( (lv_str.len == 0) || (rv_str.len == 0)){
+                               LM_ERR("String is not lv = rv \n");
+                               break;
+                       }
+
+                       /* Parsing lv */
+                       if ( (opt_pos = get_option_position(lv_str.s, opts, no_options )) < 0){
+                               LM_ERR("Unexpected option received: %s \n", lv);
+                               break;
+                       }
+
+                       if ( VISITED == opts[opt_pos].visited ){
+                               LM_ERR("Duplicate option definition %s \n", opts[opt_pos].name);
+                               break;
+                       }
+
+                       /* Parsing rv: this is the only case the options parsing continues */
+                       if ( (rv_str.len != 0 ) && (parse_rv_option(rv_str, &opts[opt_pos]) == ERROR_IN_PARSING ))
+                       {
+                               LM_ERR("Error in parsing rv value \n");
+                               break;
+                       }
+               }
+       }
+       return ret;
+}
+
+
+/**
+ * Searches for next content line in the src file
+ *
+ * @param data the destination trimmed non blank line
+ * @param size maximum accepted line length
+ * @param file source file
+ * @param p_full_len initial lenght of contents line
+ *
+ * @return  0 on success, -1 on error, 1 on EOF
+ */
+int get_non_blank_line(str* line, int size, FILE* file, int* pFull_len ){
+       char* buf = line->s;
+
+       while ( line->s = buf, fgets( line->s, size, file) != NULL ) /* read a line */
+       {
+               *pFull_len = line->len = strlen(line->s);
+               LM_DBG("line is %s ", line->s);
+               /*  missing newline indicates the line length was too big */
+               if ( line->s[line->len-1] != '\n' ){
+                       LM_ERR("Unaccepted line length \n");
+                       return -1;
+               }
+               trim(line);
+               if( line->len != 0 ){ /* we have a non blank line */
+                       line->s[line->len] = '\0'; /* just mark end of string*/
+                       return 0;
+               }
+       }
+       //EOF
+       return 1;
+}
+
+
+/**
+ * Parses the header of structure part in the source file and retrieves name.
+ *
+ * @param file pointer to source file
+ * @param expected_struct_type name of expected structure
+ * @param struct_name the parsed name of the structure.
+ *
+ * @return SUCCESSFUL_PARSING, EOF_REACHED, ERROR_IN_PARSING
+ */
+int parse_struct_header(FILE* file, char* expected_struct_type, str* struct_name){
+       str data;
+       char buf[CR_MAX_LINE_SIZE], name[CR_MAX_LINE_SIZE];
+       char str2[CR_MAX_LINE_SIZE], format[CR_MAX_LINE_SIZE];
+       int no_toks, full_line_len, ret;
+
+       ret = ERROR_IN_PARSING;
+
+       data.s = buf;
+       if( get_non_blank_line( &data, CR_MAX_LINE_SIZE, file, &full_line_len) == 1 ) {/* read a line */
+               LM_DBG("Graceful exit out of parse struct \n");
+               return EOF_REACHED;
+       }
+
+       snprintf(format, CR_MAX_LINE_SIZE-1, " %s %%s %%s %%*s", expected_struct_type);
+       no_toks = sscanf(data.s, format, name, str2);
+       LM_DBG("no_tok=<%d>, name=<%s> , delim=<%s>\n", no_toks, name, str2);
+
+       switch (no_toks) {
+       /* With 1 token parsed, correct is: "domain_name" OR "domain_name{" */
+       case 1:
+               if ( name[strlen(name)-1] == '{' ) {
+                       if (strlen(name) > 1) {
+                               name[strlen(name)-1]='\0';
+                               ret = SUCCESSFUL_PARSING;
+                       }
+                       else {
+                               LM_ERR("Domain name seems to be empty \n");
+                       }
+               }
+               else{ /* is the following non blank line a "{" ? */
+                       str new_line;
+                       char new_line_buf[CR_MAX_LINE_SIZE];
+                       new_line.s = new_line_buf;
+
+                       if ( get_non_blank_line(&new_line, CR_MAX_LINE_SIZE, file, &full_line_len) != 0 ) {
+                               LM_ERR("Unexpected end of file while waiting for { \n");
+                       } else
+                       if ( strncmp(new_line.s, "{", 1) != 0) {
+                               LM_ERR("Unexpected token while waiting for { \n");
+                       }
+                       else
+                               ret = SUCCESSFUL_PARSING;
+               }
+               break;
+       /* with 2 tokens parsed, the second must be "{" */
+       case 2:
+               if (( strncmp(str2, "{", 1) != 0))
+                       LM_ERR("Wrongly formatted line: %s\n", data.s);
+               else
+                       ret = SUCCESSFUL_PARSING;
+               break;
+       default:
+               LM_ERR("Wrong number of tokens in line: %s\n", data.s);
+       }
+
+       if ( SUCCESSFUL_PARSING == ret ){
+               LM_DBG( "Sucessfully parsed struct %s - <%s> header\n", expected_struct_type, name);
+               struct_name->len = strlen(name);
+               memcpy(struct_name->s, name, struct_name->len);
+               struct_name->s[struct_name->len]='\0';
+       }
+       else
+               fseek(file, -full_line_len, SEEK_CUR);
+
+       return ret;
+}
+
+int parse_struct_stop(FILE* file){
+       char buf[CR_MAX_LINE_SIZE];
+       str data;
+       int full_line_len;
+       data.s = buf;
+
+       if ( get_non_blank_line(&data, CR_MAX_LINE_SIZE, file, &full_line_len) == -1 )  {
+               LM_INFO("EOF received \n");
+               return ERROR_IN_PARSING;
+       }
+
+       if (strcmp(data.s, "}") != 0){
+               LM_INFO("Unexpected <%s> while waiting for } \n", data.s);
+               return ERROR_IN_PARSING;
+       }
+       return SUCCESSFUL_PARSING;
+}
diff --git a/modules/carrierroute/parser_carrierroute.h b/modules/carrierroute/parser_carrierroute.h
new file mode 100644 (file)
index 0000000..9af0b1d
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 1&1 Internet AG
+ *
+ * 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
+ */
+
+/*!
+ * \file parser_carrierroute.h
+ * \brief Functions for parsing the config file of cr when using file mode.
+ * \ingroup carrierroute
+ * - Module; \ref carrierroute
+ */
+
+#ifndef PARSER_CARRIERROUTE_H_
+#define PARSER_CARRIERROUTE_H_
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <float.h>
+#include <math.h>
+#include <errno.h>
+#include "../../str.h"
+#include "../../mem/shm_mem.h"
+#include "../../mem/mem.h"
+
+#define CR_MAX_LINE_SIZE 256
+#define MAX_FIELD_NAME 60
+#define INT_LIST_MAX_SIZE 10
+
+#define NOT_VISITED 0
+#define VISITED 1
+
+#define EXPECTED_END_OF_OPTS 0
+#define ERROR_IN_PARSING  -1
+#define SUCCESSFUL_PARSING 1
+#define EOF_REACHED 2
+
+#define DEFAULT_DOMAIN_NUM 16
+#define MAX_DOMAIN_NUM 64 // must be a power of 2
+
+enum opt_type { CFG_STR=0, CFG_INT, CFG_FLOAT, CFG_INT_LIST, MAX_OPTS};
+
+union opt_data{
+       int int_data;
+       float float_data;
+       str string_data;
+       int  int_list[INT_LIST_MAX_SIZE];
+};
+
+typedef struct {
+       char            name[MAX_FIELD_NAME];
+       enum opt_type   type;
+       union opt_data  value;
+       int             visited;
+       int             no_elems; // TBD: name should suggest int_list_no_elems
+       char            str_buf[CR_MAX_LINE_SIZE];
+} option_description;
+
+int get_non_blank_line(str* data, int size, FILE* file, int* full_line_len);
+
+int get_option_position(const char* opt_name, const option_description* opt_list, int no_options);
+
+int parse_options(FILE* file, option_description* opts, int no_options, char* end_str);
+
+int parse_struct_header(FILE* file, char* expected_struct_type, str* struct_name);
+
+int next_token_is_eof(FILE* file);
+
+int next_token_is_struct_stop(FILE* file);
+
+int parse_struct_stop(FILE* file);
+
+#endif /* PARSER_CARRIERROUTE_H_ */