asterisk/pbx/pbx_config.c

1697 lines
42 KiB
C
Raw Normal View History

/*
* Asterisk -- A telephony toolkit for Linux.
*
* Populate and remember extensions from static config file
*
* Copyright (C) 1999, Mark Spencer
*
* Mark Spencer <markster@linux-support.net>
*
* This program is free software, distributed under the terms of
* the GNU General Public License
*/
#include <sys/types.h>
#include <asterisk/pbx.h>
#include <asterisk/config.h>
#include <asterisk/module.h>
#include <asterisk/logger.h>
#include <asterisk/cli.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
/* For where to put dynamic tables */
#include "../asterisk.h"
#include "../astconf.h"
#ifdef __AST_DEBUG_MALLOC
static void FREE(void *ptr)
{
free(ptr);
}
#else
#define FREE free
#endif
static char *dtext = "Text Extension Configuration";
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 = AST_MUTEX_INITIALIZER;
static struct ast_context *local_contexts = NULL;
/*
* 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 <exten>,<priority>,<app>,<app-data> into <context>\n"
" [replace]\n\n"
" This command will add new extension into <context>. 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 <pattern> into <context>\n"
" This command add new ignore pattern into context <context>\n"
"\n"
"Example: add ignorepat _3XX into local\n";
static char context_remove_ignorepat_help[] =
"Usage: remove ignorepat <pattern> from <context>\n"
" This command remove ignore pattern from context <context>\n"
"\n"
"Example: remove ignorepat _3XX from local\n";
static char reload_extensions_help[] =
"Usage: reload extensions.conf without reloading any other modules\n"
" This command does not delete global variables\n"
"\n"
"Example: reload extensions\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
*/
if (strcmp("hint", c)) {
while (*c != '\0') {
if (!isdigit(*c++)) {
ast_cli(fd, "Invalid priority '%s'\n", argv[3]);
return RESULT_FAILURE;
}
}
removing_priority = atoi(argv[3]);
} else
removing_priority = PRIORITY_HINT;
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
* <tab>, 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 ENOENT:
case EINVAL:
ast_cli(fd, "There is no existence of context '%s'\n",
errno == ENOENT ? 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",
(char *)ast_config_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;
}
if (ast_get_extension_priority(p)!=PRIORITY_HINT) {
char *tempdata = NULL, *startdata;
tempdata = strdup((char *)ast_get_extension_app_data(p));
if (tempdata) {
startdata = tempdata;
while (*tempdata) {
if (*tempdata == '|')
*tempdata = ',';
tempdata++;
}
tempdata = startdata;
}
fprintf(output, "exten => %s,%d,%s(%s)\n",
ast_get_extension_name(p),
ast_get_extension_priority(p),
ast_get_extension_app(p),
tempdata);
if (tempdata)
free(tempdata);
} else
fprintf(output, "exten => %s,hint,%s\n",
ast_get_extension_name(p),
ast_get_extension_app(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;
int iprior = -2;
char *cidmatch, *app, *app_data;
char *start, *end;
/* 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,",");
if (prior) {
if (!strcmp(prior, "hint")) {
iprior = PRIORITY_HINT;
} else {
iprior = atoi(prior);
}
}
app = strsep(&whole_exten,",");
if (app && (start = strchr(app, '(')) && (end = strrchr(app, ')'))) {
*start = *end = '\0';
app_data = start + 1;
for (start = app_data; *start; start++)
if (*start == ',')
*start = '|';
} else
app_data = whole_exten;
if (!exten || !prior || !app || (!app_data && iprior != PRIORITY_HINT)) return RESULT_SHOWUSAGE;
if (!app_data)
app_data="";
if (ast_add_extension(argv[4], argc == 6 ? 1 : 0, exten, iprior, 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 ENOENT:
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 ENOENT:
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 ENOENT:
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 int pbx_load_module(void);
static int handle_reload_extensions(int fd, int argc, char *argv[])
{
if (argc!=2) return RESULT_SHOWUSAGE;
pbx_load_module();
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 };
static struct ast_cli_entry reload_extensions_cli =
{ { "extensions", "reload", NULL}, handle_reload_extensions,
"Reload extensions and *only* extensions", reload_extensions_help };
/*
* 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_cli_unregister(&reload_extensions_cli);
ast_context_destroy(NULL, registrar);
return 0;
}
static int pbx_load_module(void)
{
struct ast_config *cfg;
struct ast_variable *v;
char *cxt, *ext, *pri, *appl, *data, *tc, *cidmatch;
struct ast_context *con;
char *start, *end;
char realvalue[256] = "";
cfg = ast_load(config);
if (cfg) {
/* 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"));
v = ast_variable_browse(cfg, "globals");
while(v) {
pbx_substitute_variables_helper(NULL, v->value, realvalue, sizeof(realvalue) - 1);
pbx_builtin_setvar_helper(NULL, v->name, realvalue);
v = v->next;
}
cxt = ast_category_browse(cfg, NULL);
while(cxt) {
/* All categories but "general" are considered contexts */
if (!strcasecmp(cxt, "general") || !strcasecmp(cxt, "globals")) {
cxt = ast_category_browse(cfg, cxt);
continue;
}
if ((con=ast_context_create(&local_contexts,cxt, registrar))) {
v = ast_variable_browse(cfg, cxt);
while(v) {
if (!strcasecmp(v->name, "exten")) {
char *stringp=NULL;
int ipri = -2;
char realext[256]="";
tc = strdup(v->value);
if(tc!=NULL){
stringp=tc;
ext = strsep(&stringp, ",");
if (!ext)
ext="";
pri = strsep(&stringp, ",");
if (!pri)
pri="";
if (!strcmp(pri,"hint"))
ipri=PRIORITY_HINT;
else
ipri=atoi(pri);
appl = stringp;
if (!appl)
appl="";
if (!(start = strchr(appl, '('))) {
if (stringp)
appl = strsep(&stringp, ",");
else
appl = "";
}
if (start && (end = strrchr(appl, ')'))) {
*start = *end = '\0';
data = start + 1;
for (start = data; *start; start++)
if (*start == ',')
*start = '|';
} else if (stringp!=NULL && *stringp=='"') {
stringp++;
data = strsep(&stringp, "\"");
stringp++;
} else {
if (stringp)
data = strsep(&stringp, ",");
else
data = "";
}
cidmatch = strchr(ext, '/');
if (cidmatch) {
*cidmatch = '\0';
cidmatch++;
}
stringp=ext;
strsep(&stringp, "/");
if (!data)
data="";
pbx_substitute_variables_helper(NULL, ext, realext, sizeof(realext) - 1);
if (ast_add_extension2(con, 0, realext, ipri, cidmatch, appl, strdup(data), FREE, registrar)) {
ast_log(LOG_WARNING, "Unable to register extension at line %d\n", v->lineno);
}
free(tc);
} else fprintf(stderr,"Error strdup returned NULL in %s\n",__PRETTY_FUNCTION__);
} 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")) {
char *stringp=NULL;
pbx_substitute_variables_helper(NULL, v->value, realvalue, sizeof(realvalue) - 1);
tc = realvalue;
stringp=tc;
appl = strsep(&stringp, "/");
data = strsep(&stringp, "");
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;
}
}
cxt = ast_category_browse(cfg, cxt);
}
ast_destroy(cfg);
}
ast_merge_contexts_and_delete(&local_contexts,registrar);
return 0;
}
int load_module(void)
{
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);
ast_cli_register(&reload_extensions_cli);
return 0;
}
int reload(void)
{
ast_context_destroy(NULL, registrar);
/* For martin's global variables, don't clear them on reload */
#if 0
pbx_builtin_clear_globals();
#endif
pbx_load_module();
return 0;
}
int usecount(void)
{
return 0;
}
char *description(void)
{
return dtext;
}
char *key(void)
{
return ASTERISK_GPL_KEY;
}