From 71355b3fe06a3ff18dc9d09b1fe0d38ce2fd0c4c Mon Sep 17 00:00:00 2001 From: Mark Spencer Date: Wed, 12 Sep 2001 21:29:54 +0000 Subject: Version 0.1.9 from FTP git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@356 65c4cc65-6c06-0410-ace0-fbb531ad65f3 --- pbx/pbx_config.c | 1465 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 1460 insertions(+), 5 deletions(-) (limited to 'pbx') diff --git a/pbx/pbx_config.c b/pbx/pbx_config.c index 24b0bbed2..fc5bc2586 100755 --- a/pbx/pbx_config.c +++ b/pbx/pbx_config.c @@ -15,9 +15,12 @@ #include #include #include +#include #include #include #include +#include +#include /* For where to put dynamic tables */ #include "../asterisk.h" @@ -26,9 +29,1432 @@ static char *config = "extensions.conf"; static char *registrar = "pbx_config"; static int static_config = 0; +static int write_protect_config = 1; +static pthread_mutex_t save_dialplan_lock = PTHREAD_MUTEX_INITIALIZER; + +/* + * Help for commands provided by this module ... + */ +static char context_dont_include_help[] = +"Usage: dont include context in include\n" +" Remove include from context.\n"; + +static char context_remove_extension_help[] = +"Usage: remove extension exten@context [priority]\n" +" Remove whole extension from context. If priority is set, we are only\n" +" removing extension with given priority.\n"; + +static char context_add_include_help[] = +"Usage: include context in context\n" +" Include context in other context.\n"; + +static char save_dialplan_help[] = +"Usage: save dialplan [/path/to/extension/file]\n" +" Save dialplan created by pbx_config module.\n" +"\n" +"Example: save dialplan (/etc/asterisk/extensions.conf)\n" +" save dialplan /home/markster (/home/markster/extensions.conf)\n"; + +static char context_add_extension_help[] = +"Usage: add extension ,,, into \n" +" [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: add extension 6123,1,Dial,IAX/216.207.245.56/6123 into local\n" +" Now, you can dial 6123 and talk to Markster :)\n"; + +static char context_add_ignorepat_help[] = +"Usage: add ignorepat into \n" +" This command add new ignore pattern into context \n" +"\n" +"Example: add ignorepat _3XX into local\n"; + +static char context_remove_ignorepat_help[] = +"Usage: remove ignorepat from \n" +" This command remove ignore pattern from context \n" +"\n" +"Example: remove ignorepat _3XX from local\n"; + +/* + * Implementation of functions provided by this module + */ + +/* + * REMOVE INCLUDE command stuff + */ +static int handle_context_dont_include(int fd, int argc, char *argv[]) +{ + if (argc != 5) return RESULT_SHOWUSAGE; + + if (strcmp(argv[3], "in")) return RESULT_SHOWUSAGE; + + if (!ast_context_remove_include(argv[4], argv[2], registrar)) { + ast_cli(fd, "We are not including '%s' in '%s' now\n", + argv[2], argv[4]); + return RESULT_SUCCESS; + } + + ast_cli(fd, "Failed to remove '%s' include from '%s' context\n", + argv[2], argv[4]); + return RESULT_FAILURE; +} + +static char *complete_context_dont_include(char *line, char *word, + int pos, int state) +{ + int which = 0; + + /* + * Context completion ... + */ + if (pos == 2) { + struct ast_context *c; + + if (ast_lock_contexts()) { + ast_log(LOG_ERROR, "Failed to lock context list\n"); + return NULL; + } + + /* walk pbx_get_contexts ... */ + c = ast_walk_contexts(NULL); + while (c) { + struct ast_include *i; + + if (ast_lock_context(c)) { + c = ast_walk_contexts(c); + continue; + } + + i = ast_walk_context_includes(c, NULL); + while (i) { + if (!strlen(word) || + !strncmp(ast_get_include_name(i), word, strlen(word))) { + struct ast_context *nc; + int already_served = 0; + + /* check if this include is already served or not */ + + /* go through all contexts again till we reach actuall + * context or already_served = 1 + */ + nc = ast_walk_contexts(NULL); + while (nc && nc != c && !already_served) { + if (!ast_lock_context(nc)) { + struct ast_include *ni; + + ni = ast_walk_context_includes(nc, NULL); + while (ni && !already_served) { + if (!strcmp(ast_get_include_name(i), + ast_get_include_name(ni))) + already_served = 1; + ni = ast_walk_context_includes(nc, ni); + } + + ast_unlock_context(nc); + } + nc = ast_walk_contexts(nc); + } + + if (!already_served) { + if (++which > state) { + char *res = + strdup(ast_get_include_name(i)); + ast_unlock_context(c); + ast_unlock_contexts(); + return res; + } + } + } + i = ast_walk_context_includes(c, i); + } + + ast_unlock_context(c); + c = ast_walk_contexts(c); + } + + ast_unlock_contexts(); + return NULL; + } + + /* + * 'in' completion ... (complete only if previous context is really + * included somewhere) + */ + if (pos == 3) { + struct ast_context *c; + char *context, *dupline, *duplinet; + + if (state > 0) return NULL; + + /* take 'context' from line ... */ + if (!(dupline = strdup(line))) { + ast_log(LOG_ERROR, "Out of free memory\n"); + return NULL; + } + + duplinet = dupline; + strsep(&duplinet, " "); /* skip 'dont' */ + strsep(&duplinet, " "); /* skip 'include' */ + context = strsep(&duplinet, " "); + + if (!context) { + free(dupline); + return NULL; + } + + if (ast_lock_contexts()) { + ast_log(LOG_WARNING, "Failed to lock contexts list\n"); + free(dupline); + return NULL; + } + + /* go through all contexts and check if is included ... */ + c = ast_walk_contexts(NULL); + while (c) { + struct ast_include *i; + if (ast_lock_context(c)) { + free(dupline); + ast_unlock_contexts(); + return NULL; + } + + i = ast_walk_context_includes(c, NULL); + while (i) { + /* is it our context? */ + if (!strcmp(ast_get_include_name(i), context)) { + /* yes, it is, context is really included, so + * complete "in" command + */ + free(dupline); + ast_unlock_context(c); + ast_unlock_contexts(); + return strdup("in"); + } + i = ast_walk_context_includes(c, i); + } + ast_unlock_context(c); + c = ast_walk_contexts(c); + } + free(dupline); + ast_unlock_contexts(); + return NULL; + } + + /* + * Context from which we removing include ... + */ + if (pos == 4) { + struct ast_context *c; + char *context, *dupline, *duplinet, *in; + + if (!(dupline = strdup(line))) { + ast_log(LOG_ERROR, "Out of free memory\n"); + return NULL; + } + + duplinet = dupline; + + strsep(&duplinet, " "); /* skip 'dont' */ + strsep(&duplinet, " "); /* skip 'include' */ + + if (!(context = strsep(&duplinet, " "))) { + free(dupline); + return NULL; + } + + /* third word must be in */ + in = strsep(&duplinet, " "); + if (!in || + strcmp(in, "in")) { + free(dupline); + return NULL; + } + + if (ast_lock_contexts()) { + ast_log(LOG_ERROR, "Failed to lock context list\n"); + free(dupline); + return NULL; + } + + /* walk through all contexts ... */ + c = ast_walk_contexts(NULL); + while (c) { + struct ast_include *i; + if (ast_lock_context(c)) { + free(dupline); + return NULL; + } + + /* walk through all includes and check if it is our context */ + i = ast_walk_context_includes(c, NULL); + while (i) { + /* is in this context included another on which we want to + * remove? + */ + if (!strcmp(context, ast_get_include_name(i))) { + /* yes, it's included, is matching our word too? */ + if (!strncmp(ast_get_context_name(c), + word, strlen(word))) { + /* check state for completion */ + if (++which > state) { + char *res = strdup(ast_get_context_name(c)); + free(dupline); + ast_unlock_context(c); + ast_unlock_contexts(); + return res; + } + } + break; + } + i = ast_walk_context_includes(c, i); + } + ast_unlock_context(c); + c = ast_walk_contexts(c); + } + + free(dupline); + ast_unlock_contexts(); + return NULL; + } + + return NULL; +} + +/* + * REMOVE EXTENSION command stuff + */ +static int handle_context_remove_extension(int fd, int argc, char *argv[]) +{ + int removing_priority = 0; + char *exten, *context; + + if (argc != 4 && argc != 3) return RESULT_SHOWUSAGE; + + /* + * Priority input checking ... + */ + if (argc == 4) { + char *c = argv[3]; + + /* 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 + */ + while (*c != '\0') { + if (!isdigit(*c++)) { + ast_cli(fd, "Invalid priority '%s'\n", argv[3]); + return RESULT_FAILURE; + } + } + + removing_priority = atoi(argv[3]); + + if (removing_priority == 0) { + ast_cli(fd, "If you want to remove whole extension, please " \ + "omit priority argument\n"); + return RESULT_FAILURE; + } + } + + /* + * Format exten@context checking ... + */ + if (!(context = strchr(argv[2], (int)'@'))) { + ast_cli(fd, "First argument must be in exten@context format\n"); + return RESULT_FAILURE; + } + + *context++ = '\0'; + exten = argv[2]; + if ((!strlen(exten)) || (!(strlen(context)))) { + ast_cli(fd, "Missing extension or context name in second argument '%s@%s'\n", + exten == NULL ? "?" : exten, context == NULL ? "?" : context); + return RESULT_FAILURE; + } + + if (!ast_context_remove_extension(context, exten, removing_priority, registrar)) { + if (!removing_priority) + ast_cli(fd, "Whole extension %s@%s removed\n", + exten, context); + else + ast_cli(fd, "Extension %s@%s with priority %d removed\n", + exten, context, removing_priority); + + return RESULT_SUCCESS; + } + + ast_cli(fd, "Failed to remove extension %s@%s\n", exten, context); + + return RESULT_FAILURE; +} + +#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(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_context_remove_extension(char *line, char *word, int pos, + int state) +{ + char *ret; + int which = 0; + +#ifdef BROKEN_READLINE + /* + * Fix arguments, *word is a new allocated structure, REMEMBER to + * free *word when you want to return from this function ... + */ + if (fix_complete_args(line, &word, &pos)) { + ast_log(LOG_ERROR, "Out of free memory\n"); + return NULL; + } +#endif + + /* + * exten@context completion ... + */ + if (pos == 2) { + struct ast_context *c; + struct ast_exten *e; + char *context = NULL, *exten = NULL, *delim = NULL; + + /* now, parse values from word = exten@context */ + if ((delim = strchr(word, (int)'@'))) { + /* check for duplicity ... */ + if (delim != strrchr(word, (int)'@')) { +#ifdef BROKEN_READLINE + free(word); +#endif + return NULL; + } + + *delim = '\0'; + exten = strdup(word); + context = strdup(delim + 1); + *delim = '@'; + } else { + exten = strdup(word); + } +#ifdef BROKEN_READLINE + free(word); +#endif + + if (ast_lock_contexts()) { + ast_log(LOG_ERROR, "Failed to lock context list\n"); + free(context); free(exten); + return NULL; + } + + /* find our context ... */ + c = ast_walk_contexts(NULL); + while (c) { + /* our context? */ + if ( (!context || !strlen(context)) || /* if no input, all contexts ... */ + (context && !strncmp(ast_get_context_name(c), + context, strlen(context))) ) { /* if input, compare ... */ + /* try to complete extensions ... */ + e = ast_walk_context_extensions(c, NULL); + while (e) { + /* our extension? */ + if ( (!exten || !strlen(exten)) || /* if not input, all extensions ... */ + (exten && !strncmp(ast_get_extension_name(e), exten, + strlen(exten))) ) { /* if input, compare ... */ + if (++which > state) { + /* is there some context input? if not, throw back + * exten@context, if yes throw back only context ... + */ + if (!context) { + ret = malloc(strlen(ast_get_extension_name(e)) + + strlen(ast_get_context_name(c)) + 2); + if (ret) + sprintf(ret, "%s@%s", ast_get_extension_name(e), + ast_get_context_name(c)); + } else { + ret = strdup(ast_get_context_name(c)); + } + free(exten); free(context); + + ast_unlock_contexts(); + + return ret; + } + } + e = ast_walk_context_extensions(c, e); + } + } + c = ast_walk_contexts(c); + } + + ast_unlock_contexts(); + + free(exten); free(context); + + return NULL; + } + + /* + * Complete priority ... + */ + if (pos == 3) { + char *delim, *exten, *context, *dupline, *duplinet, *ec; + struct ast_context *c; + + dupline = strdup(line); + if (!dupline) { +#ifdef BROKEN_READLINE + free(word); +#endif + return NULL; + } + duplinet = dupline; + + strsep(&duplinet, " "); /* skip 'remove' */ + strsep(&duplinet, " "); /* skip 'extension */ + + if (!(ec = strsep(&duplinet, " "))) { + free(dupline); +#ifdef BROKEN_READLINE + free(word); +#endif + return NULL; + } + + /* wrong exten@context format? */ + if (!(delim = strchr(ec, (int)'@')) || + (strchr(ec, (int)'@') != strrchr(ec, (int)'@'))) { +#ifdef BROKEN_READLINE + free(word); +#endif + free(dupline); + return NULL; + } + + /* check if there is exten and context too ... */ + *delim = '\0'; + if ((!strlen(ec)) || (!strlen(delim + 1))) { +#ifdef BROKEN_READLINE + free(word); +#endif + free(dupline); + return NULL; + } + + exten = strdup(ec); + context = strdup(delim + 1); + free(dupline); + + if (ast_lock_contexts()) { + ast_log(LOG_ERROR, "Failed to lock context list\n"); +#ifdef BROKEN_READLINE + free(word); +#endif + free(exten); free(context); + return NULL; + } + + /* walk contexts */ + c = ast_walk_contexts(NULL); + while (c) { + if (!strcmp(ast_get_context_name(c), context)) { + struct ast_exten *e; + + /* walk extensions */ + free(context); + e = ast_walk_context_extensions(c, NULL); + while (e) { + if (!strcmp(ast_get_extension_name(e), exten)) { + struct ast_exten *priority; + char buffer[10]; + + free(exten); + priority = ast_walk_extension_priorities(e, NULL); + /* serve priorities */ + do { + snprintf(buffer, 10, "%u", + ast_get_extension_priority(priority)); + if (!strncmp(word, buffer, strlen(word))) { + if (++which > state) { +#ifdef BROKEN_READLINE + free(word); +#endif + ast_unlock_contexts(); + return strdup(buffer); + } + } + priority = ast_walk_extension_priorities(e, + priority); + } while (priority); + +#ifdef BROKEN_READLINE + free(word); +#endif + ast_unlock_contexts(); + return NULL; + } + e = ast_walk_context_extensions(c, e); + } +#ifdef BROKEN_READLINE + free(word); +#endif + free(exten); + ast_unlock_contexts(); + return NULL; + } + c = ast_walk_contexts(c); + } + +#ifdef BROKEN_READLINE + free(word); +#endif + free(exten); free(context); + + ast_unlock_contexts(); + return NULL; + } + +#ifdef BROKEN_READLINE + free(word); +#endif + return NULL; +} + +/* + * Include context ... + */ +static int handle_context_add_include(int fd, int argc, char *argv[]) +{ + if (argc != 4) return RESULT_SHOWUSAGE; + + /* third arg must be 'in' ... */ + if (strcmp(argv[2], "in")) return RESULT_SHOWUSAGE; + + if (ast_context_add_include(argv[3], argv[1], registrar)) { + switch (errno) { + case ENOMEM: + ast_cli(fd, "Out of memory for context addition\n"); break; + + case EBUSY: + ast_cli(fd, "Failed to lock context(s) list, please try again later\n"); break; + + case EEXIST: + ast_cli(fd, "Context '%s' already included in '%s' context\n", + argv[1], argv[3]); break; + + case ENODATA: + case EINVAL: + ast_cli(fd, "There is no existence of context '%s'\n", + errno == ENODATA ? argv[3] : argv[1]); break; + + default: + ast_cli(fd, "Failed to include '%s' in '%s' context\n", + argv[1], argv[3]); break; + } + return RESULT_FAILURE; + } + + /* show some info ... */ + ast_cli(fd, "Context '%s' included in '%s' context\n", + argv[1], argv[3]); + + return RESULT_SUCCESS; +} + +static char *complete_context_add_include(char *line, char *word, int pos, + int state) +{ + struct ast_context *c; + int which = 0; + + /* server context for inclusion ... */ + if (pos == 1) + { + if (ast_lock_contexts()) { + ast_log(LOG_ERROR, "Failed to lock context list\n"); + return NULL; + } + + /* server all contexts */ + c = ast_walk_contexts(NULL); + while (c) { + if ((!strlen(word) || + !strncmp(ast_get_context_name(c), word, strlen(word))) && + ++which > state) + { + char *context = strdup(ast_get_context_name(c)); + ast_unlock_contexts(); + return context; + } + c = ast_walk_contexts(c); + } + + ast_unlock_contexts(); + } + + /* complete 'in' only if context exist ... */ + if (pos == 2) + { + char *context, *dupline, *duplinet; + + if (state != 0) return NULL; + + /* parse context from line ... */ + if (!(dupline = strdup(line))) { + ast_log(LOG_ERROR, "Out of free memory\n"); + if (state == 0) return strdup("in"); + return NULL; + } + + duplinet = dupline; + + strsep(&duplinet, " "); + context = strsep(&duplinet, " "); + if (context) { + struct ast_context *c; + int context_existence = 0; + + /* check for context existence ... */ + if (ast_lock_contexts()) { + ast_log(LOG_ERROR, "Failed to lock context list\n"); + free(dupline); + /* our fault, we can't check, so complete 'in' ... */ + return strdup("in"); + } + + c = ast_walk_contexts(NULL); + while (c && !context_existence) { + if (!strcmp(context, ast_get_context_name(c))) { + context_existence = 1; + continue; + } + c = ast_walk_contexts(c); + } + + /* if context exists, return 'into' ... */ + if (context_existence) { + free(dupline); + ast_unlock_contexts(); + return strdup("into"); + } + + ast_unlock_contexts(); + } + + free(dupline); + return NULL; + } + + /* serve context into which we include another context */ + if (pos == 3) + { + char *context, *dupline, *duplinet, *in; + int context_existence = 0; + + if (!(dupline = strdup(line))) { + ast_log(LOG_ERROR, "Out of free memory\n"); + return NULL; + } + + duplinet = dupline; + + strsep(&duplinet, " "); /* skip 'include' */ + context = strsep(&duplinet, " "); + in = strsep(&duplinet, " "); + + /* given some context and third word is in? */ + if (!strlen(context) || strcmp(in, "in")) { + free(dupline); + return NULL; + } + + if (ast_lock_contexts()) { + ast_log(LOG_ERROR, "Failed to lock context list\n"); + free(dupline); + return NULL; + } + + /* check for context existence ... */ + c = ast_walk_contexts(NULL); + while (c && !context_existence) { + if (!strcmp(context, ast_get_context_name(c))) { + context_existence = 1; + continue; + } + c = ast_walk_contexts(c); + } + + if (!context_existence) { + free(dupline); + ast_unlock_contexts(); + return NULL; + } + + /* go through all contexts ... */ + c = ast_walk_contexts(NULL); + while (c) { + /* must be different contexts ... */ + if (strcmp(context, ast_get_context_name(c))) { + if (!ast_lock_context(c)) { + struct ast_include *i; + int included = 0; + + /* check for duplicity inclusion ... */ + i = ast_walk_context_includes(c, NULL); + while (i && !included) { + if (!strcmp(ast_get_include_name(i), context)) + included = 1; + i = ast_walk_context_includes(c, i); + } + ast_unlock_context(c); + + /* not included yet, so show possibility ... */ + if (!included && + !strncmp(ast_get_context_name(c), word, strlen(word))){ + + if (++which > state) { + char *res = strdup(ast_get_context_name(c)); + free(dupline); + ast_unlock_contexts(); + return res; + } + } + } + } + c = ast_walk_contexts(c); + } + + ast_unlock_contexts(); + free(dupline); + return NULL; + } + + return NULL; +} + +/* + * 'save dialplan' CLI command implementation functions ... + */ +static int handle_save_dialplan(int fd, int argc, char *argv[]) +{ + char filename[256]; + struct ast_context *c; + int context_header_written; + int incomplete = 0; /* incomplete config write? */ + FILE *output; + + if (! (static_config && !write_protect_config)) { + ast_cli(fd, + "I can't save dialplan now, see '%s' example file.\n", + config); + return RESULT_FAILURE; + } + + if (argc != 2 && argc != 3) return RESULT_SHOWUSAGE; + + if (ast_pthread_mutex_lock(&save_dialplan_lock)) { + ast_cli(fd, + "Failed to lock dialplan saving (another proccess saving?)\n"); + return RESULT_FAILURE; + } + + /* have config path? */ + if (argc == 3) { + /* is there extension.conf too? */ + if (!strstr(argv[2], ".conf")) { + /* no, only directory path, check for last '/' occurence */ + if (*(argv[2] + strlen(argv[2]) -1) == '/') + snprintf(filename, sizeof(filename), "%s%s", + argv[2], config); + else + /* without config extensions.conf, add it */ + snprintf(filename, sizeof(filename), "%s/%s", + argv[2], config); + } else + /* there is an .conf */ + snprintf(filename, sizeof(filename), argv[2]); + } else + /* no config file, default one */ + snprintf(filename, sizeof(filename), "%s/%s", + AST_CONFIG_DIR, config); + + /* try to lock contexts list */ + if (ast_lock_contexts()) { + ast_cli(fd, "Failed to lock contexts list\n"); + ast_pthread_mutex_unlock(&save_dialplan_lock); + return RESULT_FAILURE; + } + + /* create new file ... */ + if (!(output = fopen(filename, "wt"))) { + ast_cli(fd, "Failed to create file '%s'\n", + filename); + ast_unlock_contexts(); + ast_pthread_mutex_unlock(&save_dialplan_lock); + return RESULT_FAILURE; + } + + /* fireout general info */ + fprintf(output, "[general]\nstatic=%s\nwriteprotect=%s\n\n", + static_config ? "yes" : "no", + write_protect_config ? "yes" : "no"); + + /* walk all contexts */ + c = ast_walk_contexts(NULL); + while (c) { + context_header_written = 0; + + /* try to lock context and fireout all info */ + if (!ast_lock_context(c)) { + struct ast_exten *e, *last_written_e = NULL; + struct ast_include *i; + struct ast_ignorepat *ip; + + /* registered by this module? */ + if (!strcmp(ast_get_context_registrar(c), registrar)) { + fprintf(output, "[%s]\n", ast_get_context_name(c)); + context_header_written = 1; + } + + /* walk extensions ... */ + e = ast_walk_context_extensions(c, NULL); + while (e) { + struct ast_exten *p; + + /* fireout priorities */ + p = ast_walk_extension_priorities(e, NULL); + while (p) { + if (!strcmp(ast_get_extension_registrar(p), + registrar)) { + + /* 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; + + if (!context_header_written) { + fprintf(output, "[%s]\n", ast_get_context_name(c)); + context_header_written = 1; + } + + fprintf(output, "exten => %s,%d,%s,%s\n", + ast_get_extension_name(p), + ast_get_extension_priority(p), + ast_get_extension_app(p), + (char *)ast_get_extension_app_data(p)); + } + p = ast_walk_extension_priorities(e, p); + } + + e = ast_walk_context_extensions(c, e); + } + + /* written any extensions? ok, write space between exten & inc */ + if (last_written_e) fprintf(output, "\n"); + + /* walk through includes */ + i = ast_walk_context_includes(c, NULL); + while (i) { + if (!strcmp(ast_get_include_registrar(i), registrar)) { + if (!context_header_written) { + fprintf(output, "[%s]\n", ast_get_context_name(c)); + context_header_written = 1; + } + fprintf(output, "include => %s\n", + ast_get_include_name(i)); + } + i = ast_walk_context_includes(c, i); + } + + if (ast_walk_context_includes(c, NULL)) + fprintf(output, "\n"); + + /* fireout ignorepats ... */ + ip = ast_walk_context_ignorepats(c, NULL); + while (ip) { + if (!strcmp(ast_get_ignorepat_registrar(ip), registrar)) { + if (!context_header_written) { + fprintf(output, "[%s]\n", ast_get_context_name(c)); + context_header_written = 1; + } + + fprintf(output, "ignorepat => %s\n", + ast_get_ignorepat_name(ip)); + } + ip = ast_walk_context_ignorepats(c, ip); + } + + ast_unlock_context(c); + } else + incomplete = 1; + + c = ast_walk_contexts(c); + } + + ast_unlock_contexts(); + ast_pthread_mutex_unlock(&save_dialplan_lock); + fclose(output); + + if (incomplete) { + ast_cli(fd, "Saved dialplan is incomplete\n"); + return RESULT_FAILURE; + } + + ast_cli(fd, "Dialplane successfully saved into '%s'\n", + filename); + return RESULT_SUCCESS; +} + +/* + * ADD EXTENSION command stuff + */ +static int handle_context_add_extension(int fd, int argc, char *argv[]) +{ + char *whole_exten; + char *exten, *prior; + char *cidmatch, *app, *app_data; + + /* check for arguments at first */ + if (argc != 5 && argc != 6) return RESULT_SHOWUSAGE; + if (strcmp(argv[3], "into")) return RESULT_SHOWUSAGE; + if (argc == 6) if (strcmp(argv[5], "replace")) return RESULT_SHOWUSAGE; + + whole_exten = argv[2]; + exten = strsep(&whole_exten,","); + if (strchr(exten, '/')) { + cidmatch = exten; + strsep(&cidmatch,"/"); + } else { + cidmatch = NULL; + } + prior = strsep(&whole_exten,","); + app = strsep(&whole_exten,","); + app_data = whole_exten; + + if (!exten || !prior || !app || !app_data) return RESULT_SHOWUSAGE; + + if (ast_add_extension(argv[4], argc == 6 ? 1 : 0, exten, atoi(prior), cidmatch, app, + (void *)strdup(app_data), free, registrar)) { + switch (errno) { + case ENOMEM: + ast_cli(fd, "Out of free memory\n"); break; + + case EBUSY: + ast_cli(fd, "Failed to lock context(s) list, please try again later\n"); break; + + case ENODATA: + ast_cli(fd, "No existence of '%s' context\n", argv[4]); break; + + case EEXIST: + ast_cli(fd, "Extension %s@%s with priority %s already exists\n", + exten, argv[4], prior); break; + + default: + ast_cli(fd, "Failed to add '%s,%s,%s,%s' extension into '%s' context\n", + exten, prior, app, app_data, argv[4]); break; + } + return RESULT_FAILURE; + } + + if (argc == 6) + ast_cli(fd, "Extension %s@%s (%s) replace by '%s,%s,%s,%s'\n", + exten, argv[4], prior, exten, prior, app, app_data); + else + ast_cli(fd, "Extension '%s,%s,%s,%s' added into '%s' context\n", + exten, prior, app, app_data, argv[4]); + + return RESULT_SUCCESS; +} + +/* add extension 6123,1,Dial,IAX/212.71.138.13/6123 into local */ +static char *complete_context_add_extension(char *line, char *word, + int pos, int state) +{ + int which = 0; + + /* complete 'into' word ... */ + if (pos == 3) { + if (state == 0) return strdup("into"); + return NULL; + } + + /* complete context */ + if (pos == 4) { + struct ast_context *c; + + /* try to lock contexts list ... */ + if (ast_lock_contexts()) { + ast_log(LOG_WARNING, "Failed to lock contexts list\n"); + return NULL; + } + + /* walk through all contexts */ + c = ast_walk_contexts(NULL); + while (c) { + /* matching context? */ + if (!strncmp(ast_get_context_name(c), word, strlen(word))) { + if (++which > state) { + char *res = strdup(ast_get_context_name(c)); + ast_unlock_contexts(); + return res; + } + } + c = ast_walk_contexts(c); + } + + ast_unlock_contexts(); + return NULL; + } + + if (pos == 5) return state == 0 ? strdup("replace") : NULL; + + return NULL; +} + +/* + * IGNOREPAT CLI stuff + */ +static int handle_context_add_ignorepat(int fd, int argc, char *argv[]) +{ + if (argc != 5) return RESULT_SHOWUSAGE; + if (strcmp(argv[3], "into")) return RESULT_SHOWUSAGE; + + if (ast_context_add_ignorepat(argv[4], argv[2], registrar)) { + switch (errno) { + case ENOMEM: + ast_cli(fd, "Out of free memory\n"); break; + + case ENODATA: + ast_cli(fd, "There is no existence of '%s' context\n", argv[4]); + break; + + case EEXIST: + ast_cli(fd, "Ignore pattern '%s' already included in '%s' context\n", + argv[2], argv[4]); + break; + + case EBUSY: + ast_cli(fd, "Failed to lock context(s) list, please, try again later\n"); + break; + + default: + ast_cli(fd, "Failed to add ingore pattern '%s' into '%s' context\n", + argv[2], argv[4]); + break; + } + return RESULT_FAILURE; + } + + ast_cli(fd, "Ignore pattern '%s' added into '%s' context\n", + argv[2], argv[4]); + return RESULT_SUCCESS; +} + +static char *complete_context_add_ignorepat(char *line, char *word, + int pos, int state) +{ + if (pos == 3) return state == 0 ? strdup("into") : NULL; + + if (pos == 4) { + struct ast_context *c; + int which = 0; + char *dupline, *duplinet, *ignorepat = NULL; + + dupline = strdup(line); + duplinet = dupline; + + if (duplinet) { + strsep(&duplinet, " "); /* skip 'add' */ + strsep(&duplinet, " "); /* skip 'ignorepat' */ + ignorepat = strsep(&duplinet, " "); + } + + if (ast_lock_contexts()) { + ast_log(LOG_ERROR, "Failed to lock contexts list\n"); + return NULL; + } + + c = ast_walk_contexts(NULL); + while (c) { + if (!strncmp(ast_get_context_name(c), word, strlen(word))) { + int serve_context = 1; + if (ignorepat) { + if (!ast_lock_context(c)) { + struct ast_ignorepat *ip; + ip = ast_walk_context_ignorepats(c, NULL); + while (ip && serve_context) { + if (!strcmp(ast_get_ignorepat_name(ip), ignorepat)) + serve_context = 0; + ip = ast_walk_context_ignorepats(c, ip); + } + ast_unlock_context(c); + } + } + if (serve_context) { + if (++which > state) { + char *context = strdup(ast_get_context_name(c)); + if (dupline) free(dupline); + ast_unlock_contexts(); + return context; + } + } + } + c = ast_walk_contexts(c); + } + + if (dupline) free(dupline); + ast_unlock_contexts(); + return NULL; + } + + return NULL; +} + +static int handle_context_remove_ignorepat(int fd, int argc, char *argv[]) +{ + if (argc != 5) return RESULT_SHOWUSAGE; + if (strcmp(argv[3], "from")) return RESULT_SHOWUSAGE; + + if (ast_context_remove_ignorepat(argv[4], argv[2], registrar)) { + switch (errno) { + case EBUSY: + ast_cli(fd, "Failed to lock context(s) list, please try again later\n"); + break; + + case ENODATA: + ast_cli(fd, "There is no existence of '%s' context\n", argv[4]); + break; + + case EINVAL: + ast_cli(fd, "There is no existence of '%s' ignore pattern in '%s' context\n", + argv[2], argv[4]); + break; + + default: + ast_cli(fd, "Failed to remove ignore pattern '%s' from '%s' context\n"); + break; + } + return RESULT_FAILURE; + } + + ast_cli(fd, "Ignore pattern '%s' removed from '%s' context\n", + argv[2], argv[4]); + return RESULT_SUCCESS; +} + +static char *complete_context_remove_ignorepat(char *line, char *word, + int pos, int state) +{ + struct ast_context *c; + int which = 0; + + if (pos == 2) { + if (ast_lock_contexts()) { + ast_log(LOG_WARNING, "Failed to lock contexts list\n"); + return NULL; + } + + c = ast_walk_contexts(NULL); + while (c) { + if (!ast_lock_context(c)) { + struct ast_ignorepat *ip; + + ip = ast_walk_context_ignorepats(c, NULL); + while (ip) { + if (!strncmp(ast_get_ignorepat_name(ip), word, strlen(word))) { + if (which + 1 > state) { + struct ast_context *cw; + int already_served = 0; + cw = ast_walk_contexts(NULL); + while (cw && cw != c && !already_served) { + if (!ast_lock_context(cw)) { + struct ast_ignorepat *ipw; + ipw = ast_walk_context_ignorepats(cw, NULL); + while (ipw) { + if (!strcmp(ast_get_ignorepat_name(ipw), + ast_get_ignorepat_name(ip))) already_served = 1; + ipw = ast_walk_context_ignorepats(cw, ipw); + } + ast_unlock_context(cw); + } + cw = ast_walk_contexts(cw); + } + if (!already_served) { + char *ret = strdup(ast_get_ignorepat_name(ip)); + ast_unlock_context(c); + ast_unlock_contexts(); + return ret; + } + } else + which++; + } + ip = ast_walk_context_ignorepats(c, ip); + } + + ast_unlock_context(c); + } + c = ast_walk_contexts(c); + } + + ast_unlock_contexts(); + return NULL; + } + + if (pos == 3) return state == 0 ? strdup("from") : NULL; + + if (pos == 4) { + char *dupline, *duplinet, *ignorepat; + + dupline = strdup(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_lock_contexts()) { + ast_log(LOG_WARNING, "Failed to lock contexts list\n"); + free(dupline); + return NULL; + } + + c = ast_walk_contexts(NULL); + while (c) { + if (!ast_lock_context(c)) { + struct ast_ignorepat *ip; + ip = ast_walk_context_ignorepats(c, NULL); + while (ip) { + if (!strcmp(ast_get_ignorepat_name(ip), ignorepat)) { + if (!strncmp(ast_get_context_name(c), word, strlen(word))) { + if (++which > state) { + char *ret = strdup(ast_get_context_name(c)); + free(dupline); + ast_unlock_context(c); + ast_unlock_contexts(); + return ret; + } + } + } + ip = ast_walk_context_ignorepats(c, ip); + } + + ast_unlock_context(c); + } + c = ast_walk_contexts(c); + } + + free(dupline); + ast_unlock_contexts(); + return NULL; + } + + return NULL; +} + +/* + * CLI entries for commands provided by this module + */ +static struct ast_cli_entry context_dont_include_cli = + { { "dont", "include", NULL }, handle_context_dont_include, + "Remove a specified include from context", context_dont_include_help, + complete_context_dont_include }; + +static struct ast_cli_entry context_remove_extension_cli = + { { "remove", "extension", NULL }, handle_context_remove_extension, + "Remove a specified extension", context_remove_extension_help, + complete_context_remove_extension }; + +static struct ast_cli_entry context_add_include_cli = + { { "include", "context", NULL }, handle_context_add_include, + "Include context in other context", context_add_include_help, + complete_context_add_include }; + +static struct ast_cli_entry save_dialplan_cli = + { { "save", "dialplan", NULL }, handle_save_dialplan, + "Save dialplan", save_dialplan_help }; + +static struct ast_cli_entry context_add_extension_cli = + { { "add", "extension", NULL }, handle_context_add_extension, + "Add new extension into context", context_add_extension_help, + complete_context_add_extension }; + +static struct ast_cli_entry context_add_ignorepat_cli = + { { "add", "ignorepat", NULL }, handle_context_add_ignorepat, + "Add new ignore pattern", context_add_ignorepat_help, + complete_context_add_ignorepat }; + +static struct ast_cli_entry context_remove_ignorepat_cli = + { { "remove", "ignorepat", NULL }, handle_context_remove_ignorepat, + "Remove ignore pattern from context", context_remove_ignorepat_help, + complete_context_remove_ignorepat }; + +/* + * Standard module functions ... + */ int unload_module(void) { + ast_cli_unregister(&context_add_extension_cli); + if (static_config && !write_protect_config) + ast_cli_unregister(&save_dialplan_cli); + ast_cli_unregister(&context_add_include_cli); + ast_cli_unregister(&context_dont_include_cli); + ast_cli_unregister(&context_remove_extension_cli); + ast_cli_unregister(&context_remove_ignorepat_cli); + ast_cli_unregister(&context_add_ignorepat_cli); ast_context_destroy(NULL, registrar); return 0; } @@ -37,13 +1463,16 @@ static int pbx_load_module(void) { struct ast_config *cfg; struct ast_variable *v; - char *cxt, *ext, *pri, *appl, *data, *tc; + char *cxt, *ext, *pri, *appl, *data, *tc, *cidmatch; struct ast_context *con; cfg = ast_load(config); if (cfg) { /* Use existing config to populate the PBX table */ - static_config = ast_true(ast_variable_retrieve(cfg, "general", "static")); + static_config = ast_true(ast_variable_retrieve(cfg, "general", + "static")); + write_protect_config = ast_true(ast_variable_retrieve(cfg, "general", + "writeprotect")); cxt = ast_category_browse(cfg, NULL); while(cxt) { /* All categories but "general" are considered contexts */ @@ -66,15 +1495,30 @@ static int pbx_load_module(void) if (!appl) appl=""; data = strtok(NULL, ","); + + strtok(ext, "/"); + cidmatch = strtok(NULL, "/"); + if (!data) data=""; - if (ast_add_extension2(con, 0, ext, atoi(pri), appl, strdup(data), free, registrar)) { + if (ast_add_extension2(con, 0, ext, atoi(pri), cidmatch, appl, strdup(data), free, registrar)) { ast_log(LOG_WARNING, "Unable to register extension at line %d\n", v->lineno); } free(tc); } else if(!strcasecmp(v->name, "include")) { if (ast_context_add_include2(con, v->value, registrar)) ast_log(LOG_WARNING, "Unable to include context '%s' in context '%s'\n", v->value, cxt); + } else if(!strcasecmp(v->name, "ignorepat")) { + if (ast_context_add_ignorepat2(con, v->value, registrar)) + ast_log(LOG_WARNING, "Unable to include ignorepat '%s' in context '%s'\n", v->value, cxt); + } else if (!strcasecmp(v->name, "switch")) { + tc = strdup(v->value); + appl = strtok(tc, "/"); + data = strtok(NULL, ""); + if (!data) + data = ""; + if (ast_context_add_switch2(con, appl, data, registrar)) + ast_log(LOG_WARNING, "Unable to include switch '%s' in context '%s'\n", v->value, cxt); } v = v->next; } @@ -88,13 +1532,24 @@ static int pbx_load_module(void) int load_module(void) { - return pbx_load_module(); + if (pbx_load_module()) return -1; + + ast_cli_register(&context_remove_extension_cli); + ast_cli_register(&context_dont_include_cli); + ast_cli_register(&context_add_include_cli); + if (static_config && !write_protect_config) + ast_cli_register(&save_dialplan_cli); + ast_cli_register(&context_add_extension_cli); + ast_cli_register(&context_add_ignorepat_cli); + ast_cli_register(&context_remove_ignorepat_cli); + + return 0; } int reload(void) { ast_context_destroy(NULL, registrar); - load_module(); + pbx_load_module(); return 0; } -- cgit v1.2.3