Newer
Older
* Asterisk -- An open source telephony toolkit.
* Copyright (C) 1999 - 2006, Digium, Inc.
* Mark Spencer <markster@digium.com>
* 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.
*/
*
* \brief Populate and remember extensions from static config file
*
/*** MODULEINFO
<support_level>core</support_level>
***/
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
/*** DOCUMENTATION
<manager name="DialplanExtensionAdd" language="en_US">
<synopsis>
Add an extension to the dialplan
</synopsis>
<syntax>
<xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
<parameter name="Context" required="true">
<para>Context where the extension will be created. The context will
be created if it does not already exist.</para>
</parameter>
<parameter name="Extension" required="true">
<para>Name of the extension that will be created (may include callerid match by separating
with '/')</para>
</parameter>
<parameter name="Priority" required="true">
<para>Priority being added to this extension. Must be either <literal>hint</literal> or a
numerical value.</para>
</parameter>
<parameter name="Application" required="true">
<para>The application to use for this extension at the requested priority</para>
</parameter>
<parameter name="ApplicationData" required="false">
<para>Arguments to the application.</para>
</parameter>
<parameter name="Replace" required="false">
<para>If set to 'yes', '1', 'true' or any of the other values we evaluate as true, then
if an extension already exists at the requested context, extension, and priority it will
be overwritten. Otherwise, the existing extension will remain and the action will fail.
</para>
</parameter>
</syntax>
</manager>
<manager name="DialplanExtensionRemove" language="en_US">
<synopsis>
Remove an extension from the dialplan
</synopsis>
<syntax>
<xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
<parameter name="Context" required="true">
<para>Context of the extension being removed</para>
</parameter>
<parameter name="Extension" required="true">
<para>Name of the extension being removed (may include callerid match by separating with '/')</para>
</parameter>
<parameter name="Priority" required="false">
<para>If provided, only remove this priority from the extension instead of all
priorities in the extension.</para>
</parameter>
</syntax>
</manager>
***/
Kevin P. Fleming
committed
#include "asterisk.h"
ASTERISK_REGISTER_FILE()
Kevin P. Fleming
committed
#include "asterisk/paths.h" /* ast_config_AST_CONFIG_DIR */
Kevin P. Fleming
committed
#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 */
Kevin P. Fleming
committed
#include "asterisk/callerid.h"
static const char config[] = "extensions.conf";
static const char registrar[] = "pbx_config";
static char userscontext[AST_MAX_EXTENSION] = "default";
static int autofallthrough_config = 1;
Kevin P. Fleming
committed
static int clearglobalvars_config = 0;
Steve Murphy
committed
static int extenpatternmatchnew_config = 0;
AST_MUTEX_DEFINE_STATIC(save_dialplan_lock);
Joshua Colp
committed
AST_MUTEX_DEFINE_STATIC(reload_lock);
Martin Pycko
committed
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 *);
static char *complete_dialplan_remove_context(struct ast_cli_args *);
Martin Pycko
committed
/*
* Implementation of functions provided by this module
*/
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
/*!
* * REMOVE context command stuff
*/
static char *handle_cli_dialplan_remove_context(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
struct ast_context *con;
switch (cmd) {
case CLI_INIT:
e->command = "dialplan remove context";
e->usage =
"Usage: dialplan remove context <context>\n"
" Removes all extensions from a specified context.\n";
return NULL;
case CLI_GENERATE:
return complete_dialplan_remove_context(a);
}
if (a->argc != 4) {
return CLI_SHOWUSAGE;
}
con = ast_context_find(a->argv[3]);
if (!con) {
ast_cli(a->fd, "There is no such context as '%s'\n",
a->argv[3]);
return CLI_SUCCESS;
} else {
ast_context_destroy(con, registrar);
ast_cli(a->fd, "Removing context '%s'\n",
a->argv[3]);
return CLI_SUCCESS;
}
}
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 <context> from <context>\n"
" Remove an included context from another context.\n";
return NULL;
case CLI_GENERATE:
return complete_dialplan_remove_include(a);
}
if (a->argc != 6 || 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;
Joshua Colp
committed
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;
Joshua Colp
committed
if (ast_rdlock_context(c)) /* error, skip */
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
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 ** const cid)
char *i, *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 */
ast_free(e);
}
if (cid && (i = strchr(e, '/'))) {
*i++ = '\0';
*cid = i;
} else if (cid) {
/* Signal none detected */
*cid = NULL;
}
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_" */
Joshua Colp
committed
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;
Joshua Colp
committed
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 = ast_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)
context = dupline = ast_strdup(s);
if (!dupline) {
ast_log(LOG_ERROR, "Out of free memory\n");
return NULL;
}
strsep(&dupline, " ");
Joshua Colp
committed
if (ast_rdlock_contexts()) {
ast_log(LOG_ERROR, "Failed to lock contexts list\n");
ast_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 = ast_strdup("from");
ast_unlock_contexts();
if (!res)
ast_log(LOG_WARNING, "%s not included anywhere\n", context);
ast_free(context);
} 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 = ast_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")) {
ast_free(context);
Joshua Colp
committed
if (ast_rdlock_contexts()) {
ast_log(LOG_ERROR, "Failed to lock context list\n");
ast_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 = ast_strdup(c_name);
ast_free(context);
return res;
}
return NULL;
}
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, *cid;
char *ret = CLI_FAILURE;
switch (cmd) {
case CLI_INIT:
e->command = "dialplan remove extension";
e->usage =
"Usage: dialplan remove extension exten[/cid]@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) {
const 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 " \
return CLI_FAILURE;
}
}
/* XXX original overwrote argv[3] */
/*
* Format exten@context checking ...
*/
if (split_ec(a->argv[3], &exten, &context, &cid))
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]);
ast_free(exten);
return CLI_FAILURE;
if (!ast_context_remove_extension_callerid(context, exten, removing_priority,
/* Do NOT substitute S_OR; it is NOT the same thing */
cid ? cid : (removing_priority ? "" : NULL), cid ? 1 : 0, registrar)) {
ast_cli(a->fd, "Whole extension %s@%s removed\n",
ast_cli(a->fd, "Extension %s@%s with priority %d removed\n",
exten, context, removing_priority);
ret = CLI_SUCCESS;
if (cid) {
ast_cli(a->fd, "Failed to remove extension %s/%s@%s\n", exten, cid, context);
} else {
ast_cli(a->fd, "Failed to remove extension %s@%s\n", exten, context);
}
ret = CLI_FAILURE;
ast_free(exten);
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
static int manager_dialplan_extension_remove(struct mansession *s, const struct message *m)
{
const char *context = astman_get_header(m, "Context");
const char *extension = astman_get_header(m, "Extension");
const char *priority = astman_get_header(m, "Priority");
int ipriority;
char *exten;
char *cidmatch = NULL;
if (ast_strlen_zero(context) || ast_strlen_zero(extension)) {
astman_send_error(s, m, "Context and Extension must be provided "
"for DialplanExtensionRemove");
return 0;
}
exten = ast_strdupa(extension);
if (strchr(exten, '/')) {
cidmatch = exten;
strsep(&cidmatch, "/");
}
if (ast_strlen_zero(priority)) {
ipriority = 0;
} else if (!strcmp("hint", priority)) {
ipriority = PRIORITY_HINT;
} else if ((sscanf(priority, "%30d", &ipriority) != 1) || ipriority <= 0) {
astman_send_error(s, m, "The priority specified was invalid.");
return 0;
}
if (!ast_context_remove_extension_callerid(context, exten, ipriority,
/* Do not substitute S_OR; it is not the same thing */
!ast_strlen_zero(cidmatch) ? cidmatch : (ipriority ? "" : NULL),
!ast_strlen_zero(cidmatch) ? 1 : 0, registrar)) {
if (ipriority) {
astman_send_ack(s, m, "Removed the requested priority from the extension");
} else {
astman_send_ack(s, m, "Removed the requested extension");
}
} else {
astman_send_error(s, m, "Failed to remove requested extension");
}
return 0;
}
static char *complete_dialplan_remove_extension(struct ast_cli_args *a)
char *ret = NULL;
if (a->pos == 3) { /* 'dialplan remove extension _X_' (exten@context ... */
struct ast_context *c = NULL;
char *context = NULL, *exten = NULL, *cid = NULL;
int le = 0; /* length of extension */
int lc = 0; /* length of context */
int lcid = 0; /* length of cid */
lc = split_ec(a->word, &exten, &context, &cid);
if (lc) { /* error */
le = strlen(exten);
lc = strlen(context);
lcid = cid ? strlen(cid) : -1;
Joshua Colp
committed
if (ast_rdlock_contexts()) {
ast_log(LOG_ERROR, "Failed to lock context list\n");
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 ( !strchr(a->word, '/') ||
(!strchr(a->word, '@') && partial_match(ast_get_extension_cidmatch(e), cid, lcid)) ||
(strchr(a->word, '@') && !strcmp(ast_get_extension_cidmatch(e), cid))) {
if ( ((strchr(a->word, '/') || strchr(a->word, '@')) && !strcmp(ast_get_extension_name(e), exten)) ||
(!strchr(a->word, '/') && !strchr(a->word, '@') && partial_match(ast_get_extension_name(e), exten, le))) { /* n-th match */
if (++which > a->n) {
/* If there is an extension then return exten@context. */
if (ast_get_extension_matchcid(e) && (!strchr(a->word, '@') || strchr(a->word, '/'))) {
if (ast_asprintf(&ret, "%s/%s@%s", ast_get_extension_name(e), ast_get_extension_cidmatch(e), ast_get_context_name(c)) < 0) {
Kevin P. Fleming
committed
ret = NULL;
}
break;
} else if (!ast_get_extension_matchcid(e) && !strchr(a->word, '/')) {
if (ast_asprintf(&ret, "%s@%s", ast_get_extension_name(e), ast_get_context_name(c)) < 0) {
Kevin P. Fleming
committed
ret = NULL;
}
if (e) /* got a match */
break;
ast_free(exten);
} else if (a->pos == 4) { /* 'dialplan remove extension EXT _X_' (priority) */
char *exten = NULL, *context, *cid, *p;
David Vossel
committed
int le, lc, len;
const char *s = skip_words(a->line, 3); /* skip 'dialplan' 'remove' 'extension' */
int i = split_ec(s, &exten, &context, &cid); /* 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;
Joshua Colp
committed
if (ast_rdlock_contexts()) {
ast_log(LOG_ERROR, "Failed to lock context list\n");
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 (cid && strcmp(ast_get_extension_cidmatch(e), cid) != 0) {
continue;
}
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), "%d", ast_get_extension_priority(priority));
if (partial_match(buffer, a->word, len) && ++which > a->n) /* n-th match */
ret = ast_strdup(buffer);
ast_free(exten);
/*!
* Include context ...
*/
static char *handle_cli_dialplan_add_include(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
const char *into_context;
switch (cmd) {
case CLI_INIT:
e->command = "dialplan add include";
e->usage =
"Usage: dialplan add include <context> into <context>\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;
into_context = a->argv[5];
if (!ast_context_find(into_context)) {
ast_cli(a->fd, "Context '%s' did not exist prior to add include - the context will be created.\n", into_context);
}
if (!ast_context_find_or_create(NULL, NULL, into_context, registrar)) {
ast_cli(a->fd, "ast_context_find_or_create() failed\n");
ast_cli(a->fd, "Failed to include '%s' in '%s' context\n",a->argv[3], a->argv[5]);
return CLI_FAILURE;
}
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");
ast_cli(a->fd, "Failed to lock context(s) list, please try again later\n");
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]);
ast_cli(a->fd, "Failed to include '%s' in '%s' context\n",
a->argv[3], a->argv[5]);
return CLI_FAILURE;
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) ... */
Joshua Colp
committed
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 = ast_strdup(ast_get_context_name(c));
ast_unlock_contexts();
return ret;
} else if (a->pos == 4) { /* dialplan add include CTX _X_ */
/* always complete as 'into' */
return (a->n == 0) ? ast_strdup("into") : NULL;
} else if (a->pos == 5) { /* 'dialplan add include CTX into _X_' (dst context) */
const char *s = skip_words(a->line, 3); /* should not fail */
context = dupline = ast_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;
}
Joshua Colp
committed
if (ast_rdlock_contexts()) {
ast_log(LOG_ERROR, "Failed to lock context list\n");
goto error3;
}
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 = ast_strdup(ast_get_context_name(c));
}
}
ast_unlock_contexts();
error3:
ast_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)
struct ast_config *cfg;
struct ast_variable *v;
int incomplete = 0; /* incomplete config write? */
FILE *output;
struct ast_flags config_flags = { 0 };
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;
}
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) == '/') ? "/" : "";
} else { /* yes, complete file name */
slash = "";
}
} else {
base = ast_config_AST_CONFIG_DIR;
slash = "/";
}
snprintf(filename, sizeof(filename), "%s%s%s", base, slash, config);
cfg = ast_config_load("extensions.conf", config_flags);
if (!cfg) {
ast_cli(a->fd, "Failed to load extensions.conf\n");
ast_mutex_unlock(&save_dialplan_lock);
return CLI_FAILURE;
}
Joshua Colp
committed
if (ast_rdlock_contexts()) {
ast_cli(a->fd, "Failed to lock contexts list\n");
ast_mutex_unlock(&save_dialplan_lock);
return CLI_FAILURE;
}
/* create new file ... */
if (!(output = fopen(filename, "wt"))) {
ast_cli(a->fd, "Failed to create file '%s'\n",
ast_mutex_unlock(&save_dialplan_lock);
return CLI_FAILURE;
if (overrideswitch_config) {
snprintf(overrideswitch, sizeof(overrideswitch), "overrideswitch=%s\n", overrideswitch_config);
}
fprintf(output, "[general]\nstatic=%s\nwriteprotect=%s\nautofallthrough=%s\nclearglobalvars=%s\n%sextenpatternmatchnew=%s\n\n",
write_protect_config ? "yes" : "no",
autofallthrough_config ? "yes" : "no",
Steve Murphy
committed
clearglobalvars_config ? "yes" : "no",
Steve Murphy
committed
extenpatternmatchnew_config ? "yes" : "no");
if ((v = ast_variable_browse(cfg, "globals"))) {
fprintf(output, "[globals]\n");
while(v) {
George Joseph
committed
int escaped_len = 2 * strlen(v->value) + 1;
char escaped[escaped_len];
ast_escape_semicolons(v->value, escaped, escaped_len);
fprintf(output, "%s => %s\n", v->name, escaped);
v = v->next;
}
fprintf(output, "\n");
}
#define PUT_CTX_HDR do { \
if (!context_header_written) { \
fprintf(output, "[%s]\n", ast_get_context_name(c)); \
context_header_written = 1; \
} \
} while (0)
for (c = NULL; (c = ast_walk_contexts(c)); ) {
int context_header_written = 0;
struct ast_exten *ext, *last_written_e = NULL;
struct ast_include *i;
struct ast_ignorepat *ip;
struct ast_sw *sw;
/* try to lock context and fireout all info */
Joshua Colp
committed
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 (ext = NULL; (ext = ast_walk_context_extensions(c, ext)); ) {
struct ast_exten *p = NULL;
/* fireout priorities */
while ( (p = ast_walk_extension_priorities(ext, 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;
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);
George Joseph
committed
char *appdata = ast_get_extension_app_data(p);
int escaped_len = (!ast_strlen_zero(appdata)) ? 2 * strlen(appdata) + 1 : 1;
char escaped[escaped_len];
George Joseph
committed
if (ast_get_extension_matchcid(p)) {
sep = "/";
cid = ast_get_extension_cidmatch(p);
George Joseph
committed
} else {
George Joseph
committed
}
if (el && (snprintf(label, sizeof(label), "(%s)", el) != (strlen(el) + 2))) {
incomplete = 1; /* error encountered or label > 125 chars */
George Joseph
committed
}
if (!ast_strlen_zero(appdata)) {
ast_escape_semicolons(appdata, escaped, escaped_len);
} else {
escaped[0] = '\0';
George Joseph
committed
}
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,
George Joseph
committed
ast_get_extension_app(p), escaped);
/* 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");