diff -Nru a/config.c b/config.c --- a/config.c 2005-01-29 16:52:12 -07:00 +++ b/config.c 2005-01-29 16:52:12 -07:00 @@ -62,6 +62,7 @@ struct ast_category { char name[80]; + int ignored; /* do not let user of the config see this category */ struct ast_variable *root; struct ast_variable *last; struct ast_category *next; @@ -123,13 +124,59 @@ return variable; } +static struct ast_variable *variable_get(const struct ast_category *category, const char *name) +{ + struct ast_variable *variable; + + for (variable = category->root; variable; variable = variable->next) + if (!strcasecmp(variable->name, name)) + return variable; + + return NULL; +} + +static void variable_remove(struct ast_category *category, const struct ast_variable *variable) +{ + struct ast_variable *prev = category->root; + + if (!prev) + return; + + if (prev == variable) { + category->root = prev->next; + if (category->last == variable) + category->last = NULL; + } else { + while (prev->next && (prev->next != variable)) prev = prev->next; + if (prev->next) { + prev->next = variable->next; + if (category->last == variable) + category->last = prev; + } + } +} + void ast_variable_append(struct ast_category *category, struct ast_variable *variable) { - if (category->last) - category->last->next = variable; - else - category->root = variable; - category->last = variable; + /* Note: this function also implements "variable replacement"... if the + new variable's value is empty, then existing variables of the same + name in the category are removed (and the new variable is destroyed) + */ + if (variable->value && !ast_strlen_zero(variable->value)) { + if (category->last) + category->last->next = variable; + else + category->root = variable; + category->last = variable; + } else { + struct ast_variable *v; + + while ((v = variable_get(category, variable->name))) { + variable_remove(category, v); + ast_variables_destroy(v); + } + ast_variables_destroy(variable); + } } void ast_variables_destroy(struct ast_variable *v) @@ -181,6 +228,34 @@ return NULL; } +static struct ast_variable *variable_clone(const struct ast_variable *old) +{ + struct ast_variable *new = ast_variable_new(old->name, old->value); + + if (new) { + new->lineno = old->lineno; + new->object = old->object; + new->blanklines = old->blanklines; + /* TODO: clone comments? */ + } + + return new; +} + +static void move_variables(struct ast_category *old, struct ast_category *new) +{ + struct ast_variable *var; + struct ast_variable *next; + + next = old->root; + old->root = NULL; + for (var = next; var; var = next) { + next = var->next; + var->next = NULL; + ast_variable_append(new, var); + } +} + struct ast_category *ast_category_new(const char *name) { struct ast_category *category; @@ -194,23 +269,28 @@ return category; } -struct ast_category *ast_category_get(const struct ast_config *config, const char *category_name) +static struct ast_category *category_get(const struct ast_config *config, const char *category_name, int ignored) { struct ast_category *cat; for (cat = config->root; cat; cat = cat->next) { - if (cat->name == category_name) + if (cat->name == category_name && (ignored || !cat->ignored)) return cat; } for (cat = config->root; cat; cat = cat->next) { - if (!strcasecmp(cat->name, category_name)) + if (!strcasecmp(cat->name, category_name) && (ignored || !cat->ignored)) return cat; } return NULL; } +struct ast_category *ast_category_get(const struct ast_config *config, const char *category_name) +{ + return category_get(config, category_name, 0); +} + int ast_category_exist(const struct ast_config *config, const char *category_name) { return !!ast_category_get(config, category_name); @@ -232,6 +312,13 @@ free(cat); } +static struct ast_category *next_available_category(struct ast_category *cat) +{ + for (; cat && cat->ignored; cat = cat->next); + + return cat; +} + char *ast_category_browse(struct ast_config *config, const char *prev) { struct ast_category *cat = NULL; @@ -256,6 +343,9 @@ } } } + + if (cat) + cat = next_available_category(cat); config->last_browse = cat; if (cat) @@ -279,6 +369,19 @@ strncpy(cat->name, name, sizeof(cat->name) - 1); } +static void inherit_category(struct ast_category *new, const struct ast_category *base) +{ + struct ast_variable *var; + + for (var = base->root; var; var = var->next) { + struct ast_variable *v; + + v = variable_clone(var); + if (v) + ast_variable_append(new, v); + } +} + struct ast_config *ast_config_new(void) { struct ast_config *config; @@ -323,30 +426,69 @@ static int process_text_line(struct ast_config *cfg, struct ast_category **cat, char *buf, int lineno, const char *configfile) { char *c; - char *cur; + char *cur = buf; struct ast_variable *v; int object; - cur = ast_strip(buf); - if (ast_strlen_zero(cur)) - return 0; - /* Actually parse the entry */ if (cur[0] == '[') { + struct ast_category *newcat = NULL; + char *catname; + /* A category header */ c = strchr(cur, ']'); if (!c) { ast_log(LOG_WARNING, "parse error: no closing ']', line %d of %s\n", lineno, configfile); return -1; } - *c = '\0'; + *c++ = '\0'; cur++; - *cat = ast_category_new(cur); - if (!*cat) { + if (*c++ != '(') + c = NULL; + catname = cur; + *cat = newcat = ast_category_new(catname); + if (!newcat) { ast_log(LOG_WARNING, "Out of memory, line %d of %s\n", lineno, configfile); return -1; } - ast_category_append(cfg, *cat); + /* If there are options or categories to inherit from, process them now */ + if (c) { + if (!(cur = strchr(c, ')'))) { + ast_log(LOG_WARNING, "parse error: no closing ')', line %d of %s\n", lineno, configfile); + return -1; + } + *cur = '\0'; + while ((cur = strsep(&c, ","))) { + if (!strcasecmp(cur, "!")) { + (*cat)->ignored = 1; + } else if (!strcasecmp(cur, "+")) { + *cat = category_get(cfg, catname, 1); + if (!*cat) { + ast_destroy(cfg); + if (newcat) + ast_category_destroy(newcat); + ast_log(LOG_WARNING, "Category addition requested, but category '%s' does not exist, line %d of %s\n", catname, lineno, configfile); + return -1; + } + if (newcat) { + move_variables(newcat, *cat); + ast_category_destroy(newcat); + newcat = NULL; + } + } else { + struct ast_category *base; + + base = category_get(cfg, cur, 1); + if (!base) { + ast_log(LOG_WARNING, "Inheritance requested, but category '%s' does not exist, line %d of %s\n", cur, lineno, configfile); + return -1; + } + inherit_category(*cat, base); + } + } + } + if (newcat) + ast_category_append(cfg, *cat); } else if (cur[0] == '#') { /* A directive */ cur++; @@ -518,9 +660,13 @@ new_buf = comment_p + 1; } } - if (process_buf && process_text_line(cfg, &cat, process_buf, lineno, filename)) { - cfg = NULL; - break; + if (process_buf) { + char *buf = ast_strip(process_buf); + if (!ast_strlen_zero(buf)) + if (process_text_line(cfg, &cat, buf, lineno, filename)) { + cfg = NULL; + break; + } } } } diff -Nru a/configs/extensions.conf.sample b/configs/extensions.conf.sample --- a/configs/extensions.conf.sample 2005-01-29 16:52:12 -07:00 +++ b/configs/extensions.conf.sample 2005-01-29 16:52:12 -07:00 @@ -140,6 +140,11 @@ ; include => dundi-e164-canonical include => dundi-e164-customers + +; Contexts can be added together, even if they are located in multiple +; files (via #include statements). This works across all Asterisk +; configuration files. +[dundi-e164-local](+) include => dundi-e164-via-pstn [dundi-e164-switch] diff -Nru a/configs/sip.conf.sample b/configs/sip.conf.sample --- a/configs/sip.conf.sample 2005-01-29 16:52:12 -07:00 +++ b/configs/sip.conf.sample 2005-01-29 16:52:12 -07:00 @@ -231,9 +231,15 @@ ; you will need to configure nat option for those phones. ; Also, turn on qualify=yes to keep the nat session open -;[grandstream1] -;type=friend -;context=from-sip ; Where to start in the dialplan when this phone calls +; Defaults can be specified to later be inherited by entries. +; If your "defaults" category is not a complete peer, or if you +; just wish to hide it from the SIP module, you can use a ! flag +; to do so. +[defaults](!) +;type=friend ; Friends place calls and receive calls +;context=from-sip ; Context for incoming calls from this user + +;[grandstream1](defaults) ;callerid=John Doe <1234> ; Full caller ID, to override the phones config ;host=192.168.0.23 ; we have a static but private IP address ; No registration allowed @@ -251,24 +257,28 @@ ;allow=g729 ; Pass-thru only unless g729 license obtained -;[xlite1] +;[xlite1](defaults) ; Turn off silence suppression in X-Lite ("Transmit Silence"=YES)! ; Note that Xlite sends NAT keep-alive packets, so qualify=yes is not needed -;type=friend ;regexten=1234 ; When they register, create extension 1234 ;callerid="Jane Smith" <5678> ;host=dynamic ; This device needs to register ;nat=yes ; X-Lite is behind a NAT router ;canreinvite=no ; Typically set to NO if behind NAT +; If you want to override a value supplied in an inherited category, +; you can either just supply a new value (and the module using the config +; will use the last-specified value), or you can assign an "empty" +; value, which will forget any previously specified values for that +; variable. +context= +context=default ;disallow=all ;allow=gsm ; GSM consumes far less bandwidth than ulaw ;allow=ulaw ;allow=alaw -;[snom] -;type=friend ; Friends place calls and receive calls -;context=from-sip ; Context for incoming calls from this user +;[snom](defaults) ;secret=blah ;language=de ; Use German prompts for this user ;host=dynamic ; This peer register with us @@ -281,9 +291,7 @@ ;allow=ulaw ; dtmfmode=inband only works with ulaw or alaw! -;[polycom] -;type=friend ; Friends place calls and receive calls -;context=from-sip ; Context for incoming calls from this user +;[polycom](defaults) ;secret=blahpoly ;host=dynamic ; This peer register with us ;dtmfmode=rfc2833 ; Choices are inband, rfc2833, or info @@ -293,8 +301,7 @@ ;progressinband=no ; Polycom phones don't work properly with "never" -;[pingtel] -;type=friend +;[pingtel](defaults) ;username=pingtel ;secret=blah ;host=dynamic @@ -307,8 +314,7 @@ ;pickupgroup=1,3-5 ; We can do call pick-p for call group 1,3,4,5 ;defaultip=192.168.0.60 ; IP address to use if peer has not registred -;[cisco1] -;type=friend +;[cisco1](defaults) ;username=cisco1 ;secret=blah ;qualify=200 ; Qualify peer is no more than 200ms away