From f8247040e6231c4b3b5099ea3a526348b7941566 Mon Sep 17 00:00:00 2001 From: russell Date: Sat, 19 Jan 2008 00:19:29 +0000 Subject: Creating tag for the release of asterisk-1.6.0-beta1 git-svn-id: http://svn.digium.com/svn/asterisk/tags/1.6.0-beta1@99163 f38db490-d61c-443f-a65b-d21fe96a405b --- trunk/pbx/pbx_config.c | 1668 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1668 insertions(+) create mode 100644 trunk/pbx/pbx_config.c (limited to 'trunk/pbx/pbx_config.c') diff --git a/trunk/pbx/pbx_config.c b/trunk/pbx/pbx_config.c new file mode 100644 index 000000000..4a7a3d368 --- /dev/null +++ b/trunk/pbx/pbx_config.c @@ -0,0 +1,1668 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 1999 - 2006, Digium, Inc. + * + * Mark Spencer + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! \file + * + * \brief Populate and remember extensions from static config file + * + * + */ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + +#include + +#include "asterisk/paths.h" /* ast_config_AST_CONFIG_DIR */ +#include "asterisk/pbx.h" +#include "asterisk/config.h" +#include "asterisk/module.h" +#include "asterisk/logger.h" +#include "asterisk/cli.h" +#include "asterisk/channel.h" /* AST_MAX_EXTENSION */ +#include "asterisk/callerid.h" + +static char *config = "extensions.conf"; +static char *registrar = "pbx_config"; +static char userscontext[AST_MAX_EXTENSION] = "default"; + +static int static_config = 0; +static int write_protect_config = 1; +static int autofallthrough_config = 1; +static int clearglobalvars_config = 0; +static int extenpatternmatchnew_config = 0; + +AST_MUTEX_DEFINE_STATIC(save_dialplan_lock); + +static struct ast_context *local_contexts = NULL; + +/* + * Prototypes for our completion functions + */ +static char *complete_dialplan_remove_include(struct ast_cli_args *); +static char *complete_dialplan_add_include(struct ast_cli_args *); +static char *complete_dialplan_remove_ignorepat(struct ast_cli_args *); +static char *complete_dialplan_add_ignorepat(struct ast_cli_args *); +static char *complete_dialplan_remove_extension(struct ast_cli_args *); +static char *complete_dialplan_add_extension(struct ast_cli_args *); + +/* + * Implementation of functions provided by this module + */ + +/*! + * REMOVE INCLUDE command stuff + */ +static char *handle_cli_dialplan_remove_include(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +{ + switch (cmd) { + case CLI_INIT: + e->command = "dialplan remove include"; + e->usage = + "Usage: dialplan remove include from \n" + " Remove an included context from another context.\n"; + return NULL; + case CLI_GENERATE: + return complete_dialplan_remove_include(a); + } + + if (strcmp(a->argv[4], "from")) + return CLI_SHOWUSAGE; + + if (!ast_context_remove_include(a->argv[5], a->argv[3], registrar)) { + ast_cli(a->fd, "We are not including '%s' into '%s' now\n", + a->argv[3], a->argv[5]); + return CLI_SUCCESS; + } + + ast_cli(a->fd, "Failed to remove '%s' include from '%s' context\n", + a->argv[3], a->argv[5]); + return CLI_FAILURE; +} + +/*! \brief return true if 'name' is included by context c */ +static int lookup_ci(struct ast_context *c, const char *name) +{ + struct ast_include *i = NULL; + + if (ast_rdlock_context(c)) /* error, skip */ + return 0; + while ( (i = ast_walk_context_includes(c, i)) ) + if (!strcmp(name, ast_get_include_name(i))) + break; + ast_unlock_context(c); + return i ? -1 /* success */ : 0; +} + +/*! \brief return true if 'name' is in the ignorepats for context c */ +static int lookup_c_ip(struct ast_context *c, const char *name) +{ + struct ast_ignorepat *ip = NULL; + + if (ast_rdlock_context(c)) /* error, skip */ + return 0; + while ( (ip = ast_walk_context_ignorepats(c, ip)) ) + if (!strcmp(name, ast_get_ignorepat_name(ip))) + break; + ast_unlock_context(c); + return ip ? -1 /* success */ : 0; +} + +/*! \brief moves to the n-th word in the string, or empty string if none */ +static const char *skip_words(const char *p, int n) +{ + int in_blank = 0; + for (;n && *p; p++) { + if (isblank(*p) /* XXX order is important */ && !in_blank) { + n--; /* one word is gone */ + in_blank = 1; + } else if (/* !is_blank(*p), we know already, && */ in_blank) { + in_blank = 0; + } + } + return p; +} + +/*! \brief match the first 'len' chars of word. len==0 always succeeds */ +static int partial_match(const char *s, const char *word, int len) +{ + return (len == 0 || !strncmp(s, word, len)); +} + +/*! \brief split extension\@context in two parts, return -1 on error. + * The return string is malloc'ed and pointed by *ext + */ +static int split_ec(const char *src, char **ext, char ** const ctx) +{ + char *c, *e = ast_strdup(src); /* now src is not used anymore */ + + if (e == NULL) + return -1; /* malloc error */ + /* now, parse values from 'exten@context' */ + *ext = e; + c = strchr(e, '@'); + if (c == NULL) /* no context part */ + *ctx = ""; /* it is not overwritten, anyways */ + else { /* found context, check for duplicity ... */ + *c++ = '\0'; + *ctx = c; + if (strchr(c, '@')) { /* two @, not allowed */ + free(e); + return -1; + } + } + return 0; +} + +/* _X_ is the string we need to complete */ +static char *complete_dialplan_remove_include(struct ast_cli_args *a) +{ + int which = 0; + char *res = NULL; + int len = strlen(a->word); /* how many bytes to match */ + struct ast_context *c = NULL; + + if (a->pos == 3) { /* "dialplan remove include _X_" */ + if (ast_wrlock_contexts()) { + ast_log(LOG_ERROR, "Failed to lock context list\n"); + return NULL; + } + /* walk contexts and their includes, return the n-th match */ + while (!res && (c = ast_walk_contexts(c))) { + struct ast_include *i = NULL; + + if (ast_rdlock_context(c)) /* error ? skip this one */ + continue; + + while ( !res && (i = ast_walk_context_includes(c, i)) ) { + const char *i_name = ast_get_include_name(i); + struct ast_context *nc = NULL; + int already_served = 0; + + if (!partial_match(i_name, a->word, len)) + continue; /* not matched */ + + /* check if this include is already served or not */ + + /* go through all contexts again till we reach actual + * context or already_served = 1 + */ + while ( (nc = ast_walk_contexts(nc)) && nc != c && !already_served) + already_served = lookup_ci(nc, i_name); + + if (!already_served && ++which > a->n) + res = strdup(i_name); + } + ast_unlock_context(c); + } + + ast_unlock_contexts(); + return res; + } else if (a->pos == 4) { /* "dialplan remove include CTX _X_" */ + /* + * complete as 'from', but only if previous context is really + * included somewhere + */ + char *context, *dupline; + const char *s = skip_words(a->line, 3); /* skip 'dialplan' 'remove' 'include' */ + + if (a->n > 0) + return NULL; + context = dupline = strdup(s); + if (!dupline) { + ast_log(LOG_ERROR, "Out of free memory\n"); + return NULL; + } + strsep(&dupline, " "); + + if (ast_rdlock_contexts()) { + ast_log(LOG_ERROR, "Failed to lock contexts list\n"); + free(context); + return NULL; + } + + /* go through all contexts and check if is included ... */ + while (!res && (c = ast_walk_contexts(c))) + if (lookup_ci(c, context)) /* context is really included, complete "from" command */ + res = strdup("from"); + ast_unlock_contexts(); + if (!res) + ast_log(LOG_WARNING, "%s not included anywhere\n", context); + free(context); + return res; + } else if (a->pos == 5) { /* "dialplan remove include CTX from _X_" */ + /* + * Context from which we removing include ... + */ + char *context, *dupline, *from; + const char *s = skip_words(a->line, 3); /* skip 'dialplan' 'remove' 'include' */ + context = dupline = strdup(s); + if (!dupline) { + ast_log(LOG_ERROR, "Out of free memory\n"); + return NULL; + } + + strsep(&dupline, " "); /* skip context */ + + /* fourth word must be 'from' */ + from = strsep(&dupline, " "); + if (!from || strcmp(from, "from")) { + free(context); + return NULL; + } + + if (ast_rdlock_contexts()) { + ast_log(LOG_ERROR, "Failed to lock context list\n"); + free(context); + return NULL; + } + + /* walk through all contexts ... */ + c = NULL; + while ( !res && (c = ast_walk_contexts(c))) { + const char *c_name = ast_get_context_name(c); + if (!partial_match(c_name, a->word, len)) /* not a good target */ + continue; + /* walk through all includes and check if it is our context */ + if (lookup_ci(c, context) && ++which > a->n) + res = strdup(c_name); + } + ast_unlock_contexts(); + free(context); + return res; + } + + return NULL; +} + +/*! + * REMOVE EXTENSION command stuff + */ +static char *handle_cli_dialplan_remove_extension(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +{ + int removing_priority = 0; + char *exten, *context; + char *ret = CLI_FAILURE; + + switch (cmd) { + case CLI_INIT: + e->command = "dialplan remove extension"; + e->usage = + "Usage: dialplan remove extension exten@context [priority]\n" + " Remove an extension from a given context. If a priority\n" + " is given, only that specific priority from the given extension\n" + " will be removed.\n"; + return NULL; + case CLI_GENERATE: + return complete_dialplan_remove_extension(a); + } + + if (a->argc != 5 && a->argc != 4) + return CLI_SHOWUSAGE; + + /* + * Priority input checking ... + */ + if (a->argc == 5) { + char *c = a->argv[4]; + + /* check for digits in whole parameter for right priority ... + * why? because atoi (strtol) returns 0 if any characters in + * string and whole extension will be removed, it's not good + */ + if (!strcmp("hint", c)) + removing_priority = PRIORITY_HINT; + else { + while (*c && isdigit(*c)) + c++; + if (*c) { /* non-digit in string */ + ast_cli(a->fd, "Invalid priority '%s'\n", a->argv[4]); + return CLI_FAILURE; + } + removing_priority = atoi(a->argv[4]); + } + + if (removing_priority == 0) { + ast_cli(a->fd, "If you want to remove whole extension, please " \ + "omit priority argument\n"); + return CLI_FAILURE; + } + } + + /* XXX original overwrote argv[3] */ + /* + * Format exten@context checking ... + */ + if (split_ec(a->argv[3], &exten, &context)) + return CLI_FAILURE; /* XXX malloc failure */ + if ((!strlen(exten)) || (!(strlen(context)))) { + ast_cli(a->fd, "Missing extension or context name in third argument '%s'\n", + a->argv[3]); + free(exten); + return CLI_FAILURE; + } + + if (!ast_context_remove_extension(context, exten, removing_priority, registrar)) { + if (!removing_priority) + ast_cli(a->fd, "Whole extension %s@%s removed\n", + exten, context); + else + ast_cli(a->fd, "Extension %s@%s with priority %d removed\n", + exten, context, removing_priority); + + ret = CLI_SUCCESS; + } else { + ast_cli(a->fd, "Failed to remove extension %s@%s\n", exten, context); + ret = CLI_FAILURE; + } + free(exten); + return ret; +} + +#define BROKEN_READLINE 1 + +#ifdef BROKEN_READLINE +/* + * There is one funny thing, when you have word like 300@ and you hit + * , you arguments will like as your word is '300 ', so it '@' + * characters acts sometimes as word delimiter and sometimes as a part + * of word + * + * This fix function, allocates new word variable and store here every + * time xxx@yyy always as one word and correct pos is set too + * + * It's ugly, I know, but I'm waiting for Mark suggestion if upper is + * bug or feature ... + */ +static int fix_complete_args(const char *line, char **word, int *pos) +{ + char *_line, *_strsep_line, *_previous_word = NULL, *_word = NULL; + int words = 0; + + _line = strdup(line); + + _strsep_line = _line; + while (_strsep_line) { + _previous_word = _word; + _word = strsep(&_strsep_line, " "); + + if (_word && strlen(_word)) words++; + } + + + if (_word || _previous_word) { + if (_word) { + if (!strlen(_word)) words++; + *word = strdup(_word); + } else + *word = strdup(_previous_word); + *pos = words - 1; + free(_line); + return 0; + } + + free(_line); + return -1; +} +#endif /* BROKEN_READLINE */ + +static char *complete_dialplan_remove_extension(struct ast_cli_args *a) +{ + char *ret = NULL; + int which = 0; + +#ifdef BROKEN_READLINE + char *word2; + /* + * Fix arguments, *word is a new allocated structure, REMEMBER to + * free *word when you want to return from this function ... + */ + if (fix_complete_args(a->line, &word2, &a->pos)) { + ast_log(LOG_ERROR, "Out of free memory\n"); + return NULL; + } + a->word = word2; +#endif + + if (a->pos == 3) { /* 'dialplan remove extension _X_' (exten@context ... */ + struct ast_context *c = NULL; + char *context = NULL, *exten = NULL; + int le = 0; /* length of extension */ + int lc = 0; /* length of context */ + + lc = split_ec(a->word, &exten, &context); +#ifdef BROKEN_READLINE + free(word2); +#endif + if (lc) /* error */ + return NULL; + le = strlen(exten); + lc = strlen(context); + + if (ast_rdlock_contexts()) { + ast_log(LOG_ERROR, "Failed to lock context list\n"); + goto error2; + } + + /* find our context ... */ + while ( (c = ast_walk_contexts(c)) ) { /* match our context if any */ + struct ast_exten *e = NULL; + /* XXX locking ? */ + if (!partial_match(ast_get_context_name(c), context, lc)) + continue; /* context not matched */ + while ( (e = ast_walk_context_extensions(c, e)) ) { /* try to complete extensions ... */ + if ( partial_match(ast_get_extension_name(e), exten, le) && ++which > a->n) { /* n-th match */ + /* If there is an extension then return exten@context. XXX otherwise ? */ + if (exten) + asprintf(&ret, "%s@%s", ast_get_extension_name(e), ast_get_context_name(c)); + break; + } + } + if (e) /* got a match */ + break; + } + + ast_unlock_contexts(); + error2: + if (exten) + free(exten); + } else if (a->pos == 4) { /* 'dialplan remove extension EXT _X_' (priority) */ + char *exten = NULL, *context, *p; + struct ast_context *c; + int le, lc, len; + const char *s = skip_words(a->line, 3); /* skip 'dialplan' 'remove' 'extension' */ + int i = split_ec(s, &exten, &context); /* parse ext@context */ + + if (i) /* error */ + goto error3; + if ( (p = strchr(exten, ' ')) ) /* remove space after extension */ + *p = '\0'; + if ( (p = strchr(context, ' ')) ) /* remove space after context */ + *p = '\0'; + le = strlen(exten); + lc = strlen(context); + len = strlen(a->word); + if (le == 0 || lc == 0) + goto error3; + + if (ast_rdlock_contexts()) { + ast_log(LOG_ERROR, "Failed to lock context list\n"); + goto error3; + } + + /* walk contexts */ + c = NULL; + while ( (c = ast_walk_contexts(c)) ) { + /* XXX locking on c ? */ + struct ast_exten *e; + if (strcmp(ast_get_context_name(c), context) != 0) + continue; + /* got it, we must match here */ + e = NULL; + while ( (e = ast_walk_context_extensions(c, e)) ) { + struct ast_exten *priority; + char buffer[10]; + + if (strcmp(ast_get_extension_name(e), exten) != 0) + continue; + /* XXX lock e ? */ + priority = NULL; + while ( !ret && (priority = ast_walk_extension_priorities(e, priority)) ) { + snprintf(buffer, sizeof(buffer), "%u", ast_get_extension_priority(priority)); + if (partial_match(buffer, a->word, len) && ++which > a->n) /* n-th match */ + ret = strdup(buffer); + } + break; + } + break; + } + ast_unlock_contexts(); + error3: + if (exten) + free(exten); +#ifdef BROKEN_READLINE + free(word2); +#endif + } + return ret; +} + +/*! + * Include context ... + */ +static char *handle_cli_dialplan_add_include(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +{ + switch (cmd) { + case CLI_INIT: + e->command = "dialplan add include"; + e->usage = + "Usage: dialplan add include into \n" + " Include a context in another context.\n"; + return NULL; + case CLI_GENERATE: + return complete_dialplan_add_include(a); + } + + if (a->argc != 6) /* dialplan add include CTX in CTX */ + return CLI_SHOWUSAGE; + + /* fifth arg must be 'into' ... */ + if (strcmp(a->argv[4], "into")) + return CLI_SHOWUSAGE; + + if (ast_context_add_include(a->argv[5], a->argv[3], registrar)) { + switch (errno) { + case ENOMEM: + ast_cli(a->fd, "Out of memory for context addition\n"); + break; + + case EBUSY: + ast_cli(a->fd, "Failed to lock context(s) list, please try again later\n"); + break; + + case EEXIST: + ast_cli(a->fd, "Context '%s' already included in '%s' context\n", + a->argv[3], a->argv[5]); + break; + + case ENOENT: + case EINVAL: + ast_cli(a->fd, "There is no existence of context '%s'\n", + errno == ENOENT ? a->argv[5] : a->argv[3]); + break; + + default: + ast_cli(a->fd, "Failed to include '%s' in '%s' context\n", + a->argv[3], a->argv[5]); + break; + } + return CLI_FAILURE; + } + + /* show some info ... */ + ast_cli(a->fd, "Context '%s' included in '%s' context\n", + a->argv[3], a->argv[5]); + + return CLI_SUCCESS; +} + +static char *complete_dialplan_add_include(struct ast_cli_args *a) +{ + struct ast_context *c; + int which = 0; + char *ret = NULL; + int len = strlen(a->word); + + if (a->pos == 3) { /* 'dialplan add include _X_' (context) ... */ + if (ast_rdlock_contexts()) { + ast_log(LOG_ERROR, "Failed to lock context list\n"); + return NULL; + } + for (c = NULL; !ret && (c = ast_walk_contexts(c)); ) + if (partial_match(ast_get_context_name(c), a->word, len) && ++which > a->n) + ret = strdup(ast_get_context_name(c)); + ast_unlock_contexts(); + return ret; + } else if (a->pos == 4) { /* dialplan add include CTX _X_ */ + /* complete as 'into' if context exists or we are unable to check */ + char *context, *dupline; + struct ast_context *c; + const char *s = skip_words(a->line, 3); /* should not fail */ + + if (a->n != 0) /* only once */ + return NULL; + + /* parse context from line ... */ + context = dupline = strdup(s); + if (!context) { + ast_log(LOG_ERROR, "Out of free memory\n"); + return strdup("into"); + } + strsep(&dupline, " "); + + /* check for context existence ... */ + if (ast_rdlock_contexts()) { + ast_log(LOG_ERROR, "Failed to lock context list\n"); + /* our fault, we can't check, so complete 'into' ... */ + ret = strdup("into"); + } else { + for (c = NULL; !ret && (c = ast_walk_contexts(c)); ) + if (!strcmp(context, ast_get_context_name(c))) + ret = strdup("into"); /* found */ + ast_unlock_contexts(); + } + free(context); + return ret; + } else if (a->pos == 5) { /* 'dialplan add include CTX into _X_' (dst context) */ + char *context, *dupline, *into; + const char *s = skip_words(a->line, 3); /* should not fail */ + context = dupline = strdup(s); + if (!dupline) { + ast_log(LOG_ERROR, "Out of free memory\n"); + return NULL; + } + strsep(&dupline, " "); /* skip context */ + into = strsep(&dupline, " "); + /* error if missing context or fifth word is not 'into' */ + if (!strlen(context) || strcmp(into, "into")) { + ast_log(LOG_ERROR, "bad context %s or missing into %s\n", + context, into); + goto error3; + } + + if (ast_rdlock_contexts()) { + ast_log(LOG_ERROR, "Failed to lock context list\n"); + goto error3; + } + + for (c = NULL; (c = ast_walk_contexts(c)); ) + if (!strcmp(context, ast_get_context_name(c))) + break; + if (c) { /* first context exists, go on... */ + /* go through all contexts ... */ + for (c = NULL; !ret && (c = ast_walk_contexts(c)); ) { + if (!strcmp(context, ast_get_context_name(c))) + continue; /* skip ourselves */ + if (partial_match(ast_get_context_name(c), a->word, len) && + !lookup_ci(c, context) /* not included yet */ && + ++which > a->n) + ret = strdup(ast_get_context_name(c)); + } + } else { + ast_log(LOG_ERROR, "context %s not found\n", context); + } + ast_unlock_contexts(); + error3: + free(context); + return ret; + } + + return NULL; +} + +/*! + * \brief 'save dialplan' CLI command implementation functions ... + */ +static char *handle_cli_dialplan_save(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +{ + char filename[256]; + struct ast_context *c; + struct ast_config *cfg; + struct ast_variable *v; + int incomplete = 0; /* incomplete config write? */ + FILE *output; + struct ast_flags config_flags = { 0 }; + const char *base, *slash, *file; + + switch (cmd) { + case CLI_INIT: + e->command = "dialplan save"; + e->usage = + "Usage: dialplan save [/path/to/extension/file]\n" + " Save dialplan created by pbx_config module.\n" + "\n" + "Example: dialplan save (/etc/asterisk/extensions.conf)\n" + " dialplan save /home/markster (/home/markster/extensions.conf)\n"; + return NULL; + case CLI_GENERATE: + return NULL; + } + + if (! (static_config && !write_protect_config)) { + ast_cli(a->fd, + "I can't save dialplan now, see '%s' example file.\n", + config); + return CLI_FAILURE; + } + + if (a->argc != 2 && a->argc != 3) + return CLI_SHOWUSAGE; + + if (ast_mutex_lock(&save_dialplan_lock)) { + ast_cli(a->fd, + "Failed to lock dialplan saving (another proccess saving?)\n"); + return CLI_FAILURE; + } + /* XXX the code here is quite loose, a pathname with .conf in it + * is assumed to be a complete pathname + */ + if (a->argc == 3) { /* have config path. Look for *.conf */ + base = a->argv[2]; + if (!strstr(a->argv[2], ".conf")) { /*no, this is assumed to be a pathname */ + /* if filename ends with '/', do not add one */ + slash = (*(a->argv[2] + strlen(a->argv[2]) -1) == '/') ? "/" : ""; + file = config; /* default: 'extensions.conf' */ + } else { /* yes, complete file name */ + slash = ""; + file = ""; + } + } else { + /* no config file, default one */ + base = ast_config_AST_CONFIG_DIR; + slash = "/"; + file = config; + } + snprintf(filename, sizeof(filename), "%s%s%s", base, slash, config); + + cfg = ast_config_load("extensions.conf", config_flags); + + /* try to lock contexts list */ + if (ast_rdlock_contexts()) { + ast_cli(a->fd, "Failed to lock contexts list\n"); + ast_mutex_unlock(&save_dialplan_lock); + ast_config_destroy(cfg); + return CLI_FAILURE; + } + + /* create new file ... */ + if (!(output = fopen(filename, "wt"))) { + ast_cli(a->fd, "Failed to create file '%s'\n", + filename); + ast_unlock_contexts(); + ast_mutex_unlock(&save_dialplan_lock); + ast_config_destroy(cfg); + return CLI_FAILURE; + } + + /* fireout general info */ + fprintf(output, "[general]\nstatic=%s\nwriteprotect=%s\nautofallthrough=%s\nclearglobalvars=%s\nextenpatternmatchnew=%s\n\n", + static_config ? "yes" : "no", + write_protect_config ? "yes" : "no", + autofallthrough_config ? "yes" : "no", + clearglobalvars_config ? "yes" : "no", + extenpatternmatchnew_config ? "yes" : "no"); + + if ((v = ast_variable_browse(cfg, "globals"))) { + fprintf(output, "[globals]\n"); + while(v) { + fprintf(output, "%s => %s\n", v->name, v->value); + v = v->next; + } + fprintf(output, "\n"); + } + + ast_config_destroy(cfg); + +#define PUT_CTX_HDR do { \ + if (!context_header_written) { \ + fprintf(output, "[%s]\n", ast_get_context_name(c)); \ + context_header_written = 1; \ + } \ + } while (0) + + /* walk all contexts */ + for (c = NULL; (c = ast_walk_contexts(c)); ) { + int context_header_written = 0; + struct ast_exten *e, *last_written_e = NULL; + struct ast_include *i; + struct ast_ignorepat *ip; + struct ast_sw *sw; + + /* try to lock context and fireout all info */ + if (ast_rdlock_context(c)) { /* lock failure */ + incomplete = 1; + continue; + } + /* registered by this module? */ + /* XXX do we need this ? */ + if (!strcmp(ast_get_context_registrar(c), registrar)) { + fprintf(output, "[%s]\n", ast_get_context_name(c)); + context_header_written = 1; + } + + /* walk extensions ... */ + for (e = NULL; (e = ast_walk_context_extensions(c, e)); ) { + struct ast_exten *p = NULL; + + /* fireout priorities */ + while ( (p = ast_walk_extension_priorities(e, p)) ) { + if (strcmp(ast_get_extension_registrar(p), registrar) != 0) /* not this source */ + continue; + + /* make empty line between different extensions */ + if (last_written_e != NULL && + strcmp(ast_get_extension_name(last_written_e), + ast_get_extension_name(p))) + fprintf(output, "\n"); + last_written_e = p; + + PUT_CTX_HDR; + + if (ast_get_extension_priority(p) == PRIORITY_HINT) { /* easy */ + fprintf(output, "exten => %s,hint,%s\n", + ast_get_extension_name(p), + ast_get_extension_app(p)); + } else { + const char *sep, *cid; + const char *el = ast_get_extension_label(p); + char label[128] = ""; + + if (ast_get_extension_matchcid(p)) { + sep = "/"; + cid = ast_get_extension_cidmatch(p); + } else + sep = cid = ""; + + if (el && (snprintf(label, sizeof(label), "(%s)", el) != (strlen(el) + 2))) + incomplete = 1; /* error encountered or label > 125 chars */ + + fprintf(output, "exten => %s%s%s,%d%s,%s(%s)\n", + ast_get_extension_name(p), (ast_strlen_zero(sep) ? "" : sep), (ast_strlen_zero(cid) ? "" : cid), + ast_get_extension_priority(p), label, + ast_get_extension_app(p), (ast_strlen_zero(ast_get_extension_app_data(p)) ? "" : (const char *)ast_get_extension_app_data(p))); + } + } + } + + /* written any extensions? ok, write space between exten & inc */ + if (last_written_e) + fprintf(output, "\n"); + + /* walk through includes */ + for (i = NULL; (i = ast_walk_context_includes(c, i)) ; ) { + if (strcmp(ast_get_include_registrar(i), registrar) != 0) + continue; /* not mine */ + PUT_CTX_HDR; + fprintf(output, "include => %s\n", ast_get_include_name(i)); + } + if (ast_walk_context_includes(c, NULL)) + fprintf(output, "\n"); + + /* walk through switches */ + for (sw = NULL; (sw = ast_walk_context_switches(c, sw)) ; ) { + if (strcmp(ast_get_switch_registrar(sw), registrar) != 0) + continue; /* not mine */ + PUT_CTX_HDR; + fprintf(output, "switch => %s/%s\n", + ast_get_switch_name(sw), ast_get_switch_data(sw)); + } + + if (ast_walk_context_switches(c, NULL)) + fprintf(output, "\n"); + + /* fireout ignorepats ... */ + for (ip = NULL; (ip = ast_walk_context_ignorepats(c, ip)); ) { + if (strcmp(ast_get_ignorepat_registrar(ip), registrar) != 0) + continue; /* not mine */ + PUT_CTX_HDR; + fprintf(output, "ignorepat => %s\n", + ast_get_ignorepat_name(ip)); + } + + ast_unlock_context(c); + } + + ast_unlock_contexts(); + ast_mutex_unlock(&save_dialplan_lock); + fclose(output); + + if (incomplete) { + ast_cli(a->fd, "Saved dialplan is incomplete\n"); + return CLI_FAILURE; + } + + ast_cli(a->fd, "Dialplan successfully saved into '%s'\n", + filename); + return CLI_SUCCESS; +} + +/*! + * \brief ADD EXTENSION command stuff + */ +static char *handle_cli_dialplan_add_extension(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +{ + char *whole_exten; + char *exten, *prior; + int iprior = -2; + char *cidmatch, *app, *app_data; + char *start, *end; + + switch (cmd) { + case CLI_INIT: + e->command = "dialplan add extension"; + e->usage = + "Usage: dialplan add extension ,,,\n" + " into [replace]\n\n" + " This command will add new extension into . If there is an\n" + " existence of extension with the same priority and last 'replace'\n" + " arguments is given here we simply replace this extension.\n" + "\n" + "Example: dialplan add extension 6123,1,Dial,IAX/216.207.245.56/6123 into local\n" + " Now, you can dial 6123 and talk to Markster :)\n"; + return NULL; + case CLI_GENERATE: + return complete_dialplan_add_extension(a); + } + + /* check for arguments at first */ + if (a->argc != 6 && a->argc != 7) + return CLI_SHOWUSAGE; + if (strcmp(a->argv[4], "into")) + return CLI_SHOWUSAGE; + if (a->argc == 7) + if (strcmp(a->argv[6], "replace")) + return CLI_SHOWUSAGE; + + /* XXX overwrite argv[3] */ + whole_exten = a->argv[3]; + exten = strsep(&whole_exten,","); + if (strchr(exten, '/')) { + cidmatch = exten; + strsep(&cidmatch,"/"); + } else { + cidmatch = NULL; + } + prior = strsep(&whole_exten,","); + if (prior) { + if (!strcmp(prior, "hint")) { + iprior = PRIORITY_HINT; + } else { + if (sscanf(prior, "%d", &iprior) != 1) { + ast_cli(a->fd, "'%s' is not a valid priority\n", prior); + prior = NULL; + } + } + } + app = whole_exten; + if (app && (start = strchr(app, '(')) && (end = strrchr(app, ')'))) { + *start = *end = '\0'; + app_data = start + 1; + } else { + if (app) { + app_data = strchr(app, ','); + if (app_data) { + *app_data = '\0'; + app_data++; + } + } else + app_data = NULL; + } + + if (!exten || !prior || !app || (!app_data && iprior != PRIORITY_HINT)) + return CLI_SHOWUSAGE; + + if (!app_data) + app_data=""; + if (ast_add_extension(a->argv[5], a->argc == 7 ? 1 : 0, exten, iprior, NULL, cidmatch, app, + (void *)strdup(app_data), ast_free_ptr, registrar)) { + switch (errno) { + case ENOMEM: + ast_cli(a->fd, "Out of free memory\n"); + break; + + case EBUSY: + ast_cli(a->fd, "Failed to lock context(s) list, please try again later\n"); + break; + + case ENOENT: + ast_cli(a->fd, "No existence of '%s' context\n", a->argv[5]); + break; + + case EEXIST: + ast_cli(a->fd, "Extension %s@%s with priority %s already exists\n", + exten, a->argv[5], prior); + break; + + default: + ast_cli(a->fd, "Failed to add '%s,%s,%s,%s' extension into '%s' context\n", + exten, prior, app, app_data, a->argv[5]); + break; + } + return CLI_FAILURE; + } + + if (a->argc == 7) + ast_cli(a->fd, "Extension %s@%s (%s) replace by '%s,%s,%s,%s'\n", + exten, a->argv[5], prior, exten, prior, app, app_data); + else + ast_cli(a->fd, "Extension '%s,%s,%s,%s' added into '%s' context\n", + exten, prior, app, app_data, a->argv[5]); + + return CLI_SUCCESS; +} + +/*! dialplan add extension 6123,1,Dial,IAX/212.71.138.13/6123 into local */ +static char *complete_dialplan_add_extension(struct ast_cli_args *a) +{ + int which = 0; + + if (a->pos == 4) { /* complete 'into' word ... */ + return (a->n == 0) ? strdup("into") : NULL; + } else if (a->pos == 5) { /* complete context */ + struct ast_context *c = NULL; + int len = strlen(a->word); + char *res = NULL; + + /* try to lock contexts list ... */ + if (ast_rdlock_contexts()) { + ast_log(LOG_WARNING, "Failed to lock contexts list\n"); + return NULL; + } + + /* walk through all contexts */ + while ( !res && (c = ast_walk_contexts(c)) ) + if (partial_match(ast_get_context_name(c), a->word, len) && ++which > a->n) + res = strdup(ast_get_context_name(c)); + ast_unlock_contexts(); + return res; + } else if (a->pos == 6) { + return a->n == 0 ? strdup("replace") : NULL; + } + return NULL; +} + +/*! + * IGNOREPAT CLI stuff + */ +static char *handle_cli_dialplan_add_ignorepat(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +{ + switch (cmd) { + case CLI_INIT: + e->command = "dialplan add ignorepat"; + e->usage = + "Usage: dialplan add ignorepat into \n" + " This command adds a new ignore pattern into context \n" + "\n" + "Example: dialplan add ignorepat _3XX into local\n"; + return NULL; + case CLI_GENERATE: + return complete_dialplan_add_ignorepat(a); + } + + if (a->argc != 6) + return CLI_SHOWUSAGE; + + if (strcmp(a->argv[4], "into")) + return CLI_SHOWUSAGE; + + if (ast_context_add_ignorepat(a->argv[5], a->argv[3], registrar)) { + switch (errno) { + case ENOMEM: + ast_cli(a->fd, "Out of free memory\n"); + break; + + case ENOENT: + ast_cli(a->fd, "There is no existence of '%s' context\n", a->argv[5]); + break; + + case EEXIST: + ast_cli(a->fd, "Ignore pattern '%s' already included in '%s' context\n", + a->argv[3], a->argv[5]); + break; + + case EBUSY: + ast_cli(a->fd, "Failed to lock context(s) list, please, try again later\n"); + break; + + default: + ast_cli(a->fd, "Failed to add ingore pattern '%s' into '%s' context\n", + a->argv[3], a->argv[5]); + break; + } + return CLI_FAILURE; + } + + ast_cli(a->fd, "Ignore pattern '%s' added into '%s' context\n", + a->argv[3], a->argv[5]); + + return CLI_SUCCESS; +} + +static char *complete_dialplan_add_ignorepat(struct ast_cli_args *a) +{ + if (a->pos == 4) + return a->n == 0 ? strdup("into") : NULL; + else if (a->pos == 5) { + struct ast_context *c; + int which = 0; + char *dupline, *ignorepat = NULL; + const char *s; + char *ret = NULL; + int len = strlen(a->word); + + /* XXX skip first three words 'dialplan' 'add' 'ignorepat' */ + s = skip_words(a->line, 3); + if (s == NULL) + return NULL; + dupline = strdup(s); + if (!dupline) { + ast_log(LOG_ERROR, "Malloc failure\n"); + return NULL; + } + ignorepat = strsep(&dupline, " "); + + if (ast_rdlock_contexts()) { + ast_log(LOG_ERROR, "Failed to lock contexts list\n"); + return NULL; + } + + for (c = NULL; !ret && (c = ast_walk_contexts(c));) { + int found = 0; + + if (!partial_match(ast_get_context_name(c), a->word, len)) + continue; /* not mine */ + if (ignorepat) /* there must be one, right ? */ + found = lookup_c_ip(c, ignorepat); + if (!found && ++which > a->n) + ret = strdup(ast_get_context_name(c)); + } + + if (ignorepat) + free(ignorepat); + ast_unlock_contexts(); + return ret; + } + + return NULL; +} + +static char *handle_cli_dialplan_remove_ignorepat(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +{ + switch (cmd) { + case CLI_INIT: + e->command = "dialplan remove ignorepat"; + e->usage = + "Usage: dialplan remove ignorepat from \n" + " This command removes an ignore pattern from context \n" + "\n" + "Example: dialplan remove ignorepat _3XX from local\n"; + return NULL; + case CLI_GENERATE: + return complete_dialplan_remove_ignorepat(a); + } + + if (a->argc != 6) + return CLI_SHOWUSAGE; + + if (strcmp(a->argv[4], "from")) + return CLI_SHOWUSAGE; + + if (ast_context_remove_ignorepat(a->argv[5], a->argv[3], registrar)) { + switch (errno) { + case EBUSY: + ast_cli(a->fd, "Failed to lock context(s) list, please try again later\n"); + break; + + case ENOENT: + ast_cli(a->fd, "There is no existence of '%s' context\n", a->argv[5]); + break; + + case EINVAL: + ast_cli(a->fd, "There is no existence of '%s' ignore pattern in '%s' context\n", + a->argv[3], a->argv[5]); + break; + + default: + ast_cli(a->fd, "Failed to remove ignore pattern '%s' from '%s' context\n", + a->argv[3], a->argv[5]); + break; + } + return CLI_FAILURE; + } + + ast_cli(a->fd, "Ignore pattern '%s' removed from '%s' context\n", + a->argv[3], a->argv[5]); + return CLI_SUCCESS; +} + +static char *complete_dialplan_remove_ignorepat(struct ast_cli_args *a) +{ + struct ast_context *c; + int which = 0; + char *ret = NULL; + + if (a->pos == 3) { + int len = strlen(a->word); + if (ast_rdlock_contexts()) { + ast_log(LOG_WARNING, "Failed to lock contexts list\n"); + return NULL; + } + + for (c = NULL; !ret && (c = ast_walk_contexts(c));) { + struct ast_ignorepat *ip; + + if (ast_rdlock_context(c)) /* error, skip it */ + continue; + + for (ip = NULL; !ret && (ip = ast_walk_context_ignorepats(c, ip));) { + if (partial_match(ast_get_ignorepat_name(ip), a->word, len) && ++which > a->n) { + /* n-th match */ + struct ast_context *cw = NULL; + int found = 0; + while ( (cw = ast_walk_contexts(cw)) && cw != c && !found) { + /* XXX do i stop on c, or skip it ? */ + found = lookup_c_ip(cw, ast_get_ignorepat_name(ip)); + } + if (!found) + ret = strdup(ast_get_ignorepat_name(ip)); + } + } + ast_unlock_context(c); + } + ast_unlock_contexts(); + return ret; + } else if (a->pos == 4) { + return a->n == 0 ? strdup("from") : NULL; + } else if (a->pos == 5) { /* XXX check this */ + char *dupline, *duplinet, *ignorepat; + int len = strlen(a->word); + + dupline = strdup(a->line); + if (!dupline) { + ast_log(LOG_WARNING, "Out of free memory\n"); + return NULL; + } + + duplinet = dupline; + strsep(&duplinet, " "); + strsep(&duplinet, " "); + ignorepat = strsep(&duplinet, " "); + + if (!ignorepat) { + free(dupline); + return NULL; + } + + if (ast_rdlock_contexts()) { + ast_log(LOG_WARNING, "Failed to lock contexts list\n"); + free(dupline); + return NULL; + } + + for (c = NULL; !ret && (c = ast_walk_contexts(c)); ) { + if (ast_rdlock_context(c)) /* fail, skip it */ + continue; + if (!partial_match(ast_get_context_name(c), a->word, len)) + continue; + if (lookup_c_ip(c, ignorepat) && ++which > a->n) + ret = strdup(ast_get_context_name(c)); + ast_unlock_context(c); + } + ast_unlock_contexts(); + free(dupline); + return NULL; + } + + return NULL; +} + +static int pbx_load_module(void); + +static char *handle_cli_dialplan_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +{ + switch (cmd) { + case CLI_INIT: + e->command = "dialplan reload"; + e->usage = + "Usage: dialplan reload\n" + " Reload extensions.conf without reloading any other\n" + " modules. This command does not delete global variables\n" + " unless clearglobalvars is set to yes in extensions.conf\n"; + return NULL; + case CLI_GENERATE: + return NULL; + } + + if (a->argc != 2) + return CLI_SHOWUSAGE; + + if (clearglobalvars_config) + pbx_builtin_clear_globals(); + + pbx_load_module(); + + return CLI_SUCCESS; +} + +/*! + * CLI entries for commands provided by this module + */ +static struct ast_cli_entry cli_pbx_config[] = { + AST_CLI_DEFINE(handle_cli_dialplan_add_extension, "Add new extension into context"), + AST_CLI_DEFINE(handle_cli_dialplan_remove_extension, "Remove a specified extension"), + AST_CLI_DEFINE(handle_cli_dialplan_add_ignorepat, "Add new ignore pattern"), + AST_CLI_DEFINE(handle_cli_dialplan_remove_ignorepat, "Remove ignore pattern from context"), + AST_CLI_DEFINE(handle_cli_dialplan_add_include, "Include context in other context"), + AST_CLI_DEFINE(handle_cli_dialplan_remove_include, "Remove a specified include from context"), + AST_CLI_DEFINE(handle_cli_dialplan_reload, "Reload extensions and *only* extensions") +}; + +static struct ast_cli_entry cli_dialplan_save = + AST_CLI_DEFINE(handle_cli_dialplan_save, "Save dialplan"); + +/*! + * Standard module functions ... + */ +static int unload_module(void) +{ + if (static_config && !write_protect_config) + ast_cli_unregister(&cli_dialplan_save); + ast_cli_unregister_multiple(cli_pbx_config, sizeof(cli_pbx_config) / sizeof(struct ast_cli_entry)); + ast_context_destroy(NULL, registrar); + return 0; +} + +static int pbx_load_config(const char *config_file) +{ + struct ast_config *cfg; + char *end; + char *label; + char realvalue[256]; + int lastpri = -2; + struct ast_context *con; + struct ast_variable *v; + const char *cxt; + const char *aft; + const char *newpm; + struct ast_flags config_flags = { 0 }; + + cfg = ast_config_load(config_file, config_flags); + if (!cfg) + return 0; + + /* Use existing config to populate the PBX table */ + static_config = ast_true(ast_variable_retrieve(cfg, "general", "static")); + write_protect_config = ast_true(ast_variable_retrieve(cfg, "general", "writeprotect")); + if ((aft = ast_variable_retrieve(cfg, "general", "autofallthrough"))) + autofallthrough_config = ast_true(aft); + if ((newpm = ast_variable_retrieve(cfg, "general", "extenpatternmatchnew"))) + extenpatternmatchnew_config = ast_true(newpm); + clearglobalvars_config = ast_true(ast_variable_retrieve(cfg, "general", "clearglobalvars")); + + + if ((cxt = ast_variable_retrieve(cfg, "general", "userscontext"))) + ast_copy_string(userscontext, cxt, sizeof(userscontext)); + else + ast_copy_string(userscontext, "default", sizeof(userscontext)); + + for (v = ast_variable_browse(cfg, "globals"); v; v = v->next) { + pbx_substitute_variables_helper(NULL, v->value, realvalue, sizeof(realvalue) - 1); + pbx_builtin_setvar_helper(NULL, v->name, realvalue); + } + for (cxt = NULL; (cxt = ast_category_browse(cfg, cxt)); ) { + /* All categories but "general" or "globals" are considered contexts */ + if (!strcasecmp(cxt, "general") || !strcasecmp(cxt, "globals")) + continue; + con=ast_context_find_or_create(&local_contexts,cxt, registrar); + if (con == NULL) + continue; + + for (v = ast_variable_browse(cfg, cxt); v; v = v->next) { + if (!strcasecmp(v->name, "exten")) { + char *tc = ast_strdup(v->value); + if (tc) { + int ipri = -2; + char realext[256]=""; + char *plus, *firstp; + char *pri, *appl, *data, *cidmatch; + char *stringp = tc; + char *ext = strsep(&stringp, ","); + if (!ext) + ext=""; + pbx_substitute_variables_helper(NULL, ext, realext, sizeof(realext) - 1); + cidmatch = strchr(realext, '/'); + if (cidmatch) { + *cidmatch++ = '\0'; + ast_shrink_phone_number(cidmatch); + } + pri = strsep(&stringp, ","); + if (!pri) + pri=""; + pri = ast_skip_blanks(pri); + pri = ast_trim_blanks(pri); + label = strchr(pri, '('); + if (label) { + *label++ = '\0'; + end = strchr(label, ')'); + if (end) + *end = '\0'; + else + ast_log(LOG_WARNING, "Label missing trailing ')' at line %d\n", v->lineno); + } + plus = strchr(pri, '+'); + if (plus) + *plus++ = '\0'; + if (!strcmp(pri,"hint")) + ipri=PRIORITY_HINT; + else if (!strcmp(pri, "next") || !strcmp(pri, "n")) { + if (lastpri > -2) + ipri = lastpri + 1; + else + ast_log(LOG_WARNING, "Can't use 'next' priority on the first entry!\n"); + } else if (!strcmp(pri, "same") || !strcmp(pri, "s")) { + if (lastpri > -2) + ipri = lastpri; + else + ast_log(LOG_WARNING, "Can't use 'same' priority on the first entry!\n"); + } else if (sscanf(pri, "%d", &ipri) != 1 && + (ipri = ast_findlabel_extension2(NULL, con, realext, pri, cidmatch)) < 1) { + ast_log(LOG_WARNING, "Invalid priority/label '%s' at line %d\n", pri, v->lineno); + ipri = 0; + } + appl = S_OR(stringp, ""); + /* Find the first occurrence of '(' */ + firstp = strchr(appl, '('); + if (!firstp) { + /* No arguments */ + data = ""; + } else { + appl = strsep(&stringp, "("); + data = stringp; + end = strrchr(data, ')'); + if ((end = strrchr(data, ')'))) { + *end = '\0'; + } else { + ast_log(LOG_WARNING, "No closing parenthesis found? '%s(%s'\n", appl, data); + } + } + + if (!data) + data = ""; + appl = ast_skip_blanks(appl); + if (ipri) { + if (plus) + ipri += atoi(plus); + lastpri = ipri; + if (!ast_opt_dont_warn && !strcmp(realext, "_.")) + ast_log(LOG_WARNING, "The use of '_.' for an extension is strongly discouraged and can have unexpected behavior. Please use '_X.' instead at line %d\n", v->lineno); + if (ast_add_extension2(con, 0, realext, ipri, label, cidmatch, appl, strdup(data), ast_free_ptr, registrar)) { + ast_log(LOG_WARNING, "Unable to register extension at line %d\n", v->lineno); + } + } + free(tc); + } + } else if (!strcasecmp(v->name, "include")) { + pbx_substitute_variables_helper(NULL, v->value, realvalue, sizeof(realvalue) - 1); + if (ast_context_add_include2(con, realvalue, registrar)) + ast_log(LOG_WARNING, "Unable to include context '%s' in context '%s'\n", v->value, cxt); + } else if (!strcasecmp(v->name, "ignorepat")) { + pbx_substitute_variables_helper(NULL, v->value, realvalue, sizeof(realvalue) - 1); + if (ast_context_add_ignorepat2(con, realvalue, registrar)) + ast_log(LOG_WARNING, "Unable to include ignorepat '%s' in context '%s'\n", v->value, cxt); + } else if (!strcasecmp(v->name, "switch") || !strcasecmp(v->name, "lswitch") || !strcasecmp(v->name, "eswitch")) { + char *stringp = realvalue; + char *appl, *data; + + if (!strcasecmp(v->name, "switch")) + pbx_substitute_variables_helper(NULL, v->value, realvalue, sizeof(realvalue) - 1); + else + ast_copy_string(realvalue, v->value, sizeof(realvalue)); + appl = strsep(&stringp, "/"); + data = S_OR(stringp, ""); + if (ast_context_add_switch2(con, appl, data, !strcasecmp(v->name, "eswitch"), registrar)) + ast_log(LOG_WARNING, "Unable to include switch '%s' in context '%s'\n", v->value, cxt); + } else { + ast_log(LOG_WARNING, "==!!== Unknown directive: %s at line %d -- IGNORING!!!\n", v->name, v->lineno); + } + } + } + ast_config_destroy(cfg); + return 1; +} + +static void append_interface(char *iface, int maxlen, char *add) +{ + int len = strlen(iface); + if (strlen(add) + len < maxlen - 2) { + if (strlen(iface)) { + iface[len] = '&'; + strcpy(iface + len + 1, add); + } else + strcpy(iface, add); + } +} + +static void pbx_load_users(void) +{ + struct ast_config *cfg; + char *cat, *chan; + const char *zapchan; + const char *hasexten; + char tmp[256]; + char iface[256]; + char zapcopy[256]; + char *c; + int len; + int hasvoicemail; + int start, finish, x; + struct ast_context *con = NULL; + struct ast_flags config_flags = { 0 }; + + cfg = ast_config_load("users.conf", config_flags); + if (!cfg) + return; + + for (cat = ast_category_browse(cfg, NULL); cat ; cat = ast_category_browse(cfg, cat)) { + if (!strcasecmp(cat, "general")) + continue; + iface[0] = '\0'; + len = sizeof(iface); + if (ast_true(ast_config_option(cfg, cat, "hassip"))) { + snprintf(tmp, sizeof(tmp), "SIP/%s", cat); + append_interface(iface, sizeof(iface), tmp); + } + if (ast_true(ast_config_option(cfg, cat, "hasiax"))) { + snprintf(tmp, sizeof(tmp), "IAX2/%s", cat); + append_interface(iface, sizeof(iface), tmp); + } + if (ast_true(ast_config_option(cfg, cat, "hash323"))) { + snprintf(tmp, sizeof(tmp), "H323/%s", cat); + append_interface(iface, sizeof(iface), tmp); + } + hasexten = ast_config_option(cfg, cat, "hasexten"); + if (hasexten && !ast_true(hasexten)) + continue; + hasvoicemail = ast_true(ast_config_option(cfg, cat, "hasvoicemail")); + zapchan = ast_variable_retrieve(cfg, cat, "zapchan"); + if (!zapchan) + zapchan = ast_variable_retrieve(cfg, "general", "zapchan"); + if (!ast_strlen_zero(zapchan)) { + ast_copy_string(zapcopy, zapchan, sizeof(zapcopy)); + c = zapcopy; + chan = strsep(&c, ","); + while (chan) { + if (sscanf(chan, "%d-%d", &start, &finish) == 2) { + /* Range */ + } else if (sscanf(chan, "%d", &start)) { + /* Just one */ + finish = start; + } else { + start = 0; finish = 0; + } + if (finish < start) { + x = finish; + finish = start; + start = x; + } + for (x = start; x <= finish; x++) { + snprintf(tmp, sizeof(tmp), "Zap/%d", x); + append_interface(iface, sizeof(iface), tmp); + } + chan = strsep(&c, ","); + } + } + if (!ast_strlen_zero(iface)) { + /* Only create a context here when it is really needed. Otherwise default empty context + created by pbx_config may conflict with the one explicitly created by pbx_ael */ + if (!con) + con = ast_context_find_or_create(&local_contexts, userscontext, registrar); + + if (!con) { + ast_log(LOG_ERROR, "Can't find/create user context '%s'\n", userscontext); + return; + } + + /* Add hint */ + ast_add_extension2(con, 0, cat, -1, NULL, NULL, iface, NULL, NULL, registrar); + /* If voicemail, use "stdexten" else use plain old dial */ + if (hasvoicemail) { + snprintf(tmp, sizeof(tmp), "stdexten,%s,${HINT}", cat); + ast_add_extension2(con, 0, cat, 1, NULL, NULL, "Macro", strdup(tmp), ast_free_ptr, registrar); + } else { + ast_add_extension2(con, 0, cat, 1, NULL, NULL, "Dial", strdup("${HINT}"), ast_free_ptr, registrar); + } + } + } + ast_config_destroy(cfg); +} + +static int pbx_load_module(void) +{ + struct ast_context *con; + + if(!pbx_load_config(config)) + return AST_MODULE_LOAD_DECLINE; + + pbx_load_users(); + + ast_merge_contexts_and_delete(&local_contexts, registrar); + + for (con = NULL; (con = ast_walk_contexts(con));) + ast_context_verify_includes(con); + + pbx_set_autofallthrough(autofallthrough_config); + pbx_set_extenpatternmatchnew(extenpatternmatchnew_config); + + return AST_MODULE_LOAD_SUCCESS; +} + +static int load_module(void) +{ + if (pbx_load_module()) + return AST_MODULE_LOAD_DECLINE; + + if (static_config && !write_protect_config) + ast_cli_register(&cli_dialplan_save); + ast_cli_register_multiple(cli_pbx_config, sizeof(cli_pbx_config) / sizeof(struct ast_cli_entry)); + + return AST_MODULE_LOAD_SUCCESS; +} + +static int reload(void) +{ + if (clearglobalvars_config) + pbx_builtin_clear_globals(); + return pbx_load_module(); +} + +AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Text Extension Configuration", + .load = load_module, + .unload = unload_module, + .reload = reload, + ); -- cgit v1.2.3