2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2006, Digium, Inc.
6 * Mark Spencer <markster@digium.com>
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
21 * \brief Populate and remember extensions from static config file
28 ASTERISK_FILE_VERSION(__FILE__
, "$Revision$")
30 #include <sys/types.h>
37 #include "asterisk/pbx.h"
38 #include "asterisk/config.h"
39 #include "asterisk/options.h"
40 #include "asterisk/module.h"
41 #include "asterisk/logger.h"
42 #include "asterisk/cli.h"
43 #include "asterisk/callerid.h"
45 static char *config
= "extensions.conf";
46 static char *registrar
= "pbx_config";
47 static char userscontext
[AST_MAX_EXTENSION
] = "default";
49 static int static_config
= 0;
50 static int write_protect_config
= 1;
51 static int autofallthrough_config
= 1;
52 static int clearglobalvars_config
= 0;
54 AST_MUTEX_DEFINE_STATIC(save_dialplan_lock
);
56 static struct ast_context
*local_contexts
= NULL
;
59 * Help for commands provided by this module ...
61 static char context_add_extension_help
[] =
62 "Usage: dialplan add extension <exten>,<priority>,<app>,<app-data>\n"
63 " into <context> [replace]\n\n"
64 " This command will add new extension into <context>. If there is an\n"
65 " existence of extension with the same priority and last 'replace'\n"
66 " arguments is given here we simply replace this extension.\n"
68 "Example: dialplan add extension 6123,1,Dial,IAX/216.207.245.56/6123 into local\n"
69 " Now, you can dial 6123 and talk to Markster :)\n";
71 static char context_remove_extension_help
[] =
72 "Usage: dialplan remove extension exten@context [priority]\n"
73 " Remove an extension from a given context. If a priority\n"
74 " is given, only that specific priority from the given extension\n"
75 " will be removed.\n";
77 static char context_add_ignorepat_help
[] =
78 "Usage: dialplan add ignorepat <pattern> into <context>\n"
79 " This command adds a new ignore pattern into context <context>\n"
81 "Example: dialplan add ignorepat _3XX into local\n";
83 static char context_remove_ignorepat_help
[] =
84 "Usage: dialplan remove ignorepat <pattern> from <context>\n"
85 " This command removes an ignore pattern from context <context>\n"
87 "Example: dialplan remove ignorepat _3XX from local\n";
89 static char context_add_include_help
[] =
90 "Usage: dialplan add include <context> into <context>\n"
91 " Include a context in another context.\n";
93 static char context_remove_include_help
[] =
94 "Usage: dialplan remove include <context> from <context>\n"
95 " Remove an included context from another context.\n";
97 static char save_dialplan_help
[] =
98 "Usage: dialplan save [/path/to/extension/file]\n"
99 " Save dialplan created by pbx_config module.\n"
101 "Example: dialplan save (/etc/asterisk/extensions.conf)\n"
102 " dialplan save /home/markster (/home/markster/extensions.conf)\n";
104 static char reload_extensions_help
[] =
105 "Usage: dialplan reload\n"
106 " reload extensions.conf without reloading any other modules\n"
107 " This command does not delete global variables unless\n"
108 " clearglobalvars is set to yes in extensions.conf\n";
111 * Implementation of functions provided by this module
115 * REMOVE INCLUDE command stuff
117 static int handle_context_dont_include_deprecated(int fd
, int argc
, char *argv
[])
120 return RESULT_SHOWUSAGE
;
122 if (strcmp(argv
[3], "into"))
123 return RESULT_SHOWUSAGE
;
125 if (!ast_context_remove_include(argv
[4], argv
[2], registrar
)) {
126 ast_cli(fd
, "We are not including '%s' into '%s' now\n",
128 return RESULT_SUCCESS
;
131 ast_cli(fd
, "Failed to remove '%s' include from '%s' context\n",
133 return RESULT_FAILURE
;
136 static int handle_context_remove_include(int fd
, int argc
, char *argv
[])
139 return RESULT_SHOWUSAGE
;
141 if (strcmp(argv
[4], "into"))
142 return RESULT_SHOWUSAGE
;
144 if (!ast_context_remove_include(argv
[5], argv
[3], registrar
)) {
145 ast_cli(fd
, "We are not including '%s' into '%s' now\n",
147 return RESULT_SUCCESS
;
150 ast_cli(fd
, "Failed to remove '%s' include from '%s' context\n",
152 return RESULT_FAILURE
;
155 /*! \brief return true if 'name' is included by context c */
156 static int lookup_ci(struct ast_context
*c
, const char *name
)
158 struct ast_include
*i
= NULL
;
160 if (ast_lock_context(c
)) /* error, skip */
162 while ( (i
= ast_walk_context_includes(c
, i
)) )
163 if (!strcmp(name
, ast_get_include_name(i
)))
165 ast_unlock_context(c
);
166 return i
? -1 /* success */ : 0;
169 /*! \brief return true if 'name' is in the ignorepats for context c */
170 static int lookup_c_ip(struct ast_context
*c
, const char *name
)
172 struct ast_ignorepat
*ip
= NULL
;
174 if (ast_lock_context(c
)) /* error, skip */
176 while ( (ip
= ast_walk_context_ignorepats(c
, ip
)) )
177 if (!strcmp(name
, ast_get_ignorepat_name(ip
)))
179 ast_unlock_context(c
);
180 return ip
? -1 /* success */ : 0;
183 /*! \brief moves to the n-th word in the string, or empty string if none */
184 static const char *skip_words(const char *p
, int n
)
187 for (;n
&& *p
; p
++) {
188 if (isblank(*p
) /* XXX order is important */ && !in_blank
) {
189 n
--; /* one word is gone */
191 } else if (/* !is_blank(*p), we know already, && */ in_blank
) {
198 /*! \brief match the first 'len' chars of word. len==0 always succeeds */
199 static int partial_match(const char *s
, const char *word
, int len
)
201 return (len
== 0 || !strncmp(s
, word
, len
));
204 /*! \brief split extension\@context in two parts, return -1 on error.
205 * The return string is malloc'ed and pointed by *ext
207 static int split_ec(const char *src
, char **ext
, char ** const ctx
)
209 char *c
, *e
= ast_strdup(src
); /* now src is not used anymore */
212 return -1; /* malloc error */
213 /* now, parse values from 'exten@context' */
216 if (c
== NULL
) /* no context part */
217 *ctx
= ""; /* it is not overwritten, anyways */
218 else { /* found context, check for duplicity ... */
221 if (strchr(c
, '@')) { /* two @, not allowed */
229 /* _X_ is the string we need to complete */
230 static char *complete_context_dont_include_deprecated(const char *line
, const char *word
,
235 int len
= strlen(word
); /* how many bytes to match */
236 struct ast_context
*c
= NULL
;
238 if (pos
== 2) { /* "dont include _X_" */
239 if (ast_wrlock_contexts()) {
240 ast_log(LOG_ERROR
, "Failed to lock context list\n");
243 /* walk contexts and their includes, return the n-th match */
244 while (!res
&& (c
= ast_walk_contexts(c
))) {
245 struct ast_include
*i
= NULL
;
247 if (ast_lock_context(c
)) /* error ? skip this one */
250 while ( !res
&& (i
= ast_walk_context_includes(c
, i
)) ) {
251 const char *i_name
= ast_get_include_name(i
);
252 struct ast_context
*nc
= NULL
;
253 int already_served
= 0;
255 if (!partial_match(i_name
, word
, len
))
256 continue; /* not matched */
258 /* check if this include is already served or not */
260 /* go through all contexts again till we reach actual
261 * context or already_served = 1
263 while ( (nc
= ast_walk_contexts(nc
)) && nc
!= c
&& !already_served
)
264 already_served
= lookup_ci(nc
, i_name
);
266 if (!already_served
&& ++which
> state
)
267 res
= strdup(i_name
);
269 ast_unlock_context(c
);
272 ast_unlock_contexts();
274 } else if (pos
== 3) { /* "dont include CTX _X_" */
276 * complete as 'in', but only if previous context is really
279 char *context
, *dupline
;
280 const char *s
= skip_words(line
, 2); /* skip 'dont' 'include' */
284 context
= dupline
= strdup(s
);
286 ast_log(LOG_ERROR
, "Out of free memory\n");
289 strsep(&dupline
, " ");
291 if (ast_rdlock_contexts()) {
292 ast_log(LOG_ERROR
, "Failed to lock contexts list\n");
297 /* go through all contexts and check if is included ... */
298 while (!res
&& (c
= ast_walk_contexts(c
)))
299 if (lookup_ci(c
, context
)) /* context is really included, complete "in" command */
301 ast_unlock_contexts();
303 ast_log(LOG_WARNING
, "%s not included anywhere\n", context
);
306 } else if (pos
== 4) { /* "dont include CTX in _X_" */
308 * Context from which we removing include ...
310 char *context
, *dupline
, *in
;
311 const char *s
= skip_words(line
, 2); /* skip 'dont' 'include' */
312 context
= dupline
= strdup(s
);
314 ast_log(LOG_ERROR
, "Out of free memory\n");
318 strsep(&dupline
, " "); /* skip context */
320 /* third word must be 'in' */
321 in
= strsep(&dupline
, " ");
322 if (!in
|| strcmp(in
, "in")) {
327 if (ast_rdlock_contexts()) {
328 ast_log(LOG_ERROR
, "Failed to lock context list\n");
333 /* walk through all contexts ... */
335 while ( !res
&& (c
= ast_walk_contexts(c
))) {
336 const char *c_name
= ast_get_context_name(c
);
337 if (!partial_match(c_name
, word
, len
)) /* not a good target */
339 /* walk through all includes and check if it is our context */
340 if (lookup_ci(c
, context
) && ++which
> state
)
341 res
= strdup(c_name
);
343 ast_unlock_contexts();
351 static char *complete_context_remove_include(const char *line
, const char *word
,
356 int len
= strlen(word
); /* how many bytes to match */
357 struct ast_context
*c
= NULL
;
359 if (pos
== 3) { /* "dialplan remove include _X_" */
360 if (ast_rdlock_contexts()) {
361 ast_log(LOG_ERROR
, "Failed to lock context list\n");
364 /* walk contexts and their includes, return the n-th match */
365 while (!res
&& (c
= ast_walk_contexts(c
))) {
366 struct ast_include
*i
= NULL
;
368 if (ast_lock_context(c
)) /* error ? skip this one */
371 while ( !res
&& (i
= ast_walk_context_includes(c
, i
)) ) {
372 const char *i_name
= ast_get_include_name(i
);
373 struct ast_context
*nc
= NULL
;
374 int already_served
= 0;
376 if (!partial_match(i_name
, word
, len
))
377 continue; /* not matched */
379 /* check if this include is already served or not */
381 /* go through all contexts again till we reach actual
382 * context or already_served = 1
384 while ( (nc
= ast_walk_contexts(nc
)) && nc
!= c
&& !already_served
)
385 already_served
= lookup_ci(nc
, i_name
);
387 if (!already_served
&& ++which
> state
)
388 res
= strdup(i_name
);
390 ast_unlock_context(c
);
393 ast_unlock_contexts();
395 } else if (pos
== 4) { /* "dialplan remove include CTX _X_" */
397 * complete as 'from', but only if previous context is really
400 char *context
, *dupline
;
401 const char *s
= skip_words(line
, 3); /* skip 'dialplan' 'remove' 'include' */
405 context
= dupline
= strdup(s
);
407 ast_log(LOG_ERROR
, "Out of free memory\n");
410 strsep(&dupline
, " ");
412 if (ast_rdlock_contexts()) {
413 ast_log(LOG_ERROR
, "Failed to lock contexts list\n");
418 /* go through all contexts and check if is included ... */
419 while (!res
&& (c
= ast_walk_contexts(c
)))
420 if (lookup_ci(c
, context
)) /* context is really included, complete "from" command */
421 res
= strdup("from");
422 ast_unlock_contexts();
424 ast_log(LOG_WARNING
, "%s not included anywhere\n", context
);
427 } else if (pos
== 5) { /* "dialplan remove include CTX from _X_" */
429 * Context from which we removing include ...
431 char *context
, *dupline
, *from
;
432 const char *s
= skip_words(line
, 3); /* skip 'dialplan' 'remove' 'include' */
433 context
= dupline
= strdup(s
);
435 ast_log(LOG_ERROR
, "Out of free memory\n");
439 strsep(&dupline
, " "); /* skip context */
441 /* fourth word must be 'from' */
442 from
= strsep(&dupline
, " ");
443 if (!from
|| strcmp(from
, "from")) {
448 if (ast_rdlock_contexts()) {
449 ast_log(LOG_ERROR
, "Failed to lock context list\n");
454 /* walk through all contexts ... */
456 while ( !res
&& (c
= ast_walk_contexts(c
))) {
457 const char *c_name
= ast_get_context_name(c
);
458 if (!partial_match(c_name
, word
, len
)) /* not a good target */
460 /* walk through all includes and check if it is our context */
461 if (lookup_ci(c
, context
) && ++which
> state
)
462 res
= strdup(c_name
);
464 ast_unlock_contexts();
473 * REMOVE EXTENSION command stuff
475 static int handle_context_remove_extension_deprecated(int fd
, int argc
, char *argv
[])
477 int removing_priority
= 0;
478 char *exten
, *context
;
479 int ret
= RESULT_FAILURE
;
481 if (argc
!= 4 && argc
!= 3) return RESULT_SHOWUSAGE
;
484 * Priority input checking ...
489 /* check for digits in whole parameter for right priority ...
490 * why? because atoi (strtol) returns 0 if any characters in
491 * string and whole extension will be removed, it's not good
493 if (!strcmp("hint", c
))
494 removing_priority
= PRIORITY_HINT
;
496 while (*c
&& isdigit(*c
))
498 if (*c
) { /* non-digit in string */
499 ast_cli(fd
, "Invalid priority '%s'\n", argv
[3]);
500 return RESULT_FAILURE
;
502 removing_priority
= atoi(argv
[3]);
505 if (removing_priority
== 0) {
506 ast_cli(fd
, "If you want to remove whole extension, please " \
507 "omit priority argument\n");
508 return RESULT_FAILURE
;
512 /* XXX original overwrote argv[2] */
514 * Format exten@context checking ...
516 if (split_ec(argv
[2], &exten
, &context
))
517 return RESULT_FAILURE
; /* XXX malloc failure */
518 if ((!strlen(exten
)) || (!(strlen(context
)))) {
519 ast_cli(fd
, "Missing extension or context name in second argument '%s'\n",
522 return RESULT_FAILURE
;
525 if (!ast_context_remove_extension(context
, exten
, removing_priority
, registrar
)) {
526 if (!removing_priority
)
527 ast_cli(fd
, "Whole extension %s@%s removed\n",
530 ast_cli(fd
, "Extension %s@%s with priority %d removed\n",
531 exten
, context
, removing_priority
);
533 ret
= RESULT_SUCCESS
;
535 ast_cli(fd
, "Failed to remove extension %s@%s\n", exten
, context
);
536 ret
= RESULT_FAILURE
;
542 static int handle_context_remove_extension(int fd
, int argc
, char *argv
[])
544 int removing_priority
= 0;
545 char *exten
, *context
;
546 int ret
= RESULT_FAILURE
;
548 if (argc
!= 5 && argc
!= 4) return RESULT_SHOWUSAGE
;
551 * Priority input checking ...
556 /* check for digits in whole parameter for right priority ...
557 * why? because atoi (strtol) returns 0 if any characters in
558 * string and whole extension will be removed, it's not good
560 if (!strcmp("hint", c
))
561 removing_priority
= PRIORITY_HINT
;
563 while (*c
&& isdigit(*c
))
565 if (*c
) { /* non-digit in string */
566 ast_cli(fd
, "Invalid priority '%s'\n", argv
[4]);
567 return RESULT_FAILURE
;
569 removing_priority
= atoi(argv
[4]);
572 if (removing_priority
== 0) {
573 ast_cli(fd
, "If you want to remove whole extension, please " \
574 "omit priority argument\n");
575 return RESULT_FAILURE
;
579 /* XXX original overwrote argv[3] */
581 * Format exten@context checking ...
583 if (split_ec(argv
[3], &exten
, &context
))
584 return RESULT_FAILURE
; /* XXX malloc failure */
585 if ((!strlen(exten
)) || (!(strlen(context
)))) {
586 ast_cli(fd
, "Missing extension or context name in third argument '%s'\n",
589 return RESULT_FAILURE
;
592 if (!ast_context_remove_extension(context
, exten
, removing_priority
, registrar
)) {
593 if (!removing_priority
)
594 ast_cli(fd
, "Whole extension %s@%s removed\n",
597 ast_cli(fd
, "Extension %s@%s with priority %d removed\n",
598 exten
, context
, removing_priority
);
600 ret
= RESULT_SUCCESS
;
602 ast_cli(fd
, "Failed to remove extension %s@%s\n", exten
, context
);
603 ret
= RESULT_FAILURE
;
609 #define BROKEN_READLINE 1
611 #ifdef BROKEN_READLINE
613 * There is one funny thing, when you have word like 300@ and you hit
614 * <tab>, you arguments will like as your word is '300 ', so it '@'
615 * characters acts sometimes as word delimiter and sometimes as a part
618 * This fix function, allocates new word variable and store here every
619 * time xxx@yyy always as one word and correct pos is set too
621 * It's ugly, I know, but I'm waiting for Mark suggestion if upper is
624 static int fix_complete_args(const char *line
, char **word
, int *pos
)
626 char *_line
, *_strsep_line
, *_previous_word
= NULL
, *_word
= NULL
;
629 _line
= strdup(line
);
631 _strsep_line
= _line
;
632 while (_strsep_line
) {
633 _previous_word
= _word
;
634 _word
= strsep(&_strsep_line
, " ");
636 if (_word
&& strlen(_word
)) words
++;
640 if (_word
|| _previous_word
) {
642 if (!strlen(_word
)) words
++;
643 *word
= strdup(_word
);
645 *word
= strdup(_previous_word
);
654 #endif /* BROKEN_READLINE */
656 static char *complete_context_remove_extension_deprecated(const char *line
, const char *word
, int pos
,
662 #ifdef BROKEN_READLINE
665 * Fix arguments, *word is a new allocated structure, REMEMBER to
666 * free *word when you want to return from this function ...
668 if (fix_complete_args(line
, &word2
, &pos
)) {
669 ast_log(LOG_ERROR
, "Out of free memory\n");
675 if (pos
== 2) { /* 'remove extension _X_' (exten@context ... */
676 struct ast_context
*c
= NULL
;
677 char *context
= NULL
, *exten
= NULL
;
678 int le
= 0; /* length of extension */
679 int lc
= 0; /* length of context */
681 lc
= split_ec(word
, &exten
, &context
);
682 #ifdef BROKEN_READLINE
688 lc
= strlen(context
);
690 if (ast_rdlock_contexts()) {
691 ast_log(LOG_ERROR
, "Failed to lock context list\n");
695 /* find our context ... */
696 while ( (c
= ast_walk_contexts(c
)) ) { /* match our context if any */
697 struct ast_exten
*e
= NULL
;
699 if (!partial_match(ast_get_context_name(c
), context
, lc
))
700 continue; /* context not matched */
701 while ( (e
= ast_walk_context_extensions(c
, e
)) ) { /* try to complete extensions ... */
702 if ( partial_match(ast_get_extension_name(e
), exten
, le
) && ++which
> state
) { /* n-th match */
703 /* If there is an extension then return exten@context. XXX otherwise ? */
705 asprintf(&ret
, "%s@%s", ast_get_extension_name(e
), ast_get_context_name(c
));
709 if (e
) /* got a match */
713 ast_unlock_contexts();
717 } else if (pos
== 3) { /* 'remove extension EXT _X_' (priority) */
718 char *exten
= NULL
, *context
, *p
;
719 struct ast_context
*c
;
721 const char *s
= skip_words(line
, 2); /* skip 'remove' 'extension' */
722 int i
= split_ec(s
, &exten
, &context
); /* parse ext@context */
726 if ( (p
= strchr(exten
, ' ')) ) /* remove space after extension */
728 if ( (p
= strchr(context
, ' ')) ) /* remove space after context */
731 lc
= strlen(context
);
733 if (le
== 0 || lc
== 0)
736 if (ast_rdlock_contexts()) {
737 ast_log(LOG_ERROR
, "Failed to lock context list\n");
743 while ( (c
= ast_walk_contexts(c
)) ) {
744 /* XXX locking on c ? */
746 if (strcmp(ast_get_context_name(c
), context
) != 0)
748 /* got it, we must match here */
750 while ( (e
= ast_walk_context_extensions(c
, e
)) ) {
751 struct ast_exten
*priority
;
754 if (strcmp(ast_get_extension_name(e
), exten
) != 0)
758 while ( !ret
&& (priority
= ast_walk_extension_priorities(e
, priority
)) ) {
759 snprintf(buffer
, sizeof(buffer
), "%u", ast_get_extension_priority(priority
));
760 if (partial_match(buffer
, word
, len
) && ++which
> state
) /* n-th match */
761 ret
= strdup(buffer
);
767 ast_unlock_contexts();
771 #ifdef BROKEN_READLINE
778 static char *complete_context_remove_extension(const char *line
, const char *word
, int pos
,
784 #ifdef BROKEN_READLINE
787 * Fix arguments, *word is a new allocated structure, REMEMBER to
788 * free *word when you want to return from this function ...
790 if (fix_complete_args(line
, &word2
, &pos
)) {
791 ast_log(LOG_ERROR
, "Out of free memory\n");
797 if (pos
== 3) { /* 'dialplan remove extension _X_' (exten@context ... */
798 struct ast_context
*c
= NULL
;
799 char *context
= NULL
, *exten
= NULL
;
800 int le
= 0; /* length of extension */
801 int lc
= 0; /* length of context */
803 lc
= split_ec(word
, &exten
, &context
);
804 #ifdef BROKEN_READLINE
810 lc
= strlen(context
);
812 if (ast_rdlock_contexts()) {
813 ast_log(LOG_ERROR
, "Failed to lock context list\n");
817 /* find our context ... */
818 while ( (c
= ast_walk_contexts(c
)) ) { /* match our context if any */
819 struct ast_exten
*e
= NULL
;
821 if (!partial_match(ast_get_context_name(c
), context
, lc
))
822 continue; /* context not matched */
823 while ( (e
= ast_walk_context_extensions(c
, e
)) ) { /* try to complete extensions ... */
824 if ( partial_match(ast_get_extension_name(e
), exten
, le
) && ++which
> state
) { /* n-th match */
825 /* If there is an extension then return exten@context. XXX otherwise ? */
827 asprintf(&ret
, "%s@%s", ast_get_extension_name(e
), ast_get_context_name(c
));
831 if (e
) /* got a match */
835 ast_unlock_contexts();
839 } else if (pos
== 4) { /* 'dialplan remove extension EXT _X_' (priority) */
840 char *exten
= NULL
, *context
, *p
;
841 struct ast_context
*c
;
843 const char *s
= skip_words(line
, 3); /* skip 'dialplan' 'remove' 'extension' */
844 int i
= split_ec(s
, &exten
, &context
); /* parse ext@context */
848 if ( (p
= strchr(exten
, ' ')) ) /* remove space after extension */
850 if ( (p
= strchr(context
, ' ')) ) /* remove space after context */
853 lc
= strlen(context
);
855 if (le
== 0 || lc
== 0)
858 if (ast_rdlock_contexts()) {
859 ast_log(LOG_ERROR
, "Failed to lock context list\n");
865 while ( (c
= ast_walk_contexts(c
)) ) {
866 /* XXX locking on c ? */
868 if (strcmp(ast_get_context_name(c
), context
) != 0)
870 /* got it, we must match here */
872 while ( (e
= ast_walk_context_extensions(c
, e
)) ) {
873 struct ast_exten
*priority
;
876 if (strcmp(ast_get_extension_name(e
), exten
) != 0)
880 while ( !ret
&& (priority
= ast_walk_extension_priorities(e
, priority
)) ) {
881 snprintf(buffer
, sizeof(buffer
), "%u", ast_get_extension_priority(priority
));
882 if (partial_match(buffer
, word
, len
) && ++which
> state
) /* n-th match */
883 ret
= strdup(buffer
);
889 ast_unlock_contexts();
893 #ifdef BROKEN_READLINE
901 * Include context ...
903 static int handle_context_add_include_deprecated(int fd
, int argc
, char *argv
[])
905 if (argc
!= 5) /* include context CTX in CTX */
906 return RESULT_SHOWUSAGE
;
908 /* third arg must be 'in' ... */
909 if (strcmp(argv
[3], "in") && strcmp(argv
[3], "into")) /* XXX why both ? */
910 return RESULT_SHOWUSAGE
;
912 if (ast_context_add_include(argv
[4], argv
[2], registrar
)) {
915 ast_cli(fd
, "Out of memory for context addition\n");
919 ast_cli(fd
, "Failed to lock context(s) list, please try again later\n");
923 ast_cli(fd
, "Context '%s' already included in '%s' context\n",
929 ast_cli(fd
, "There is no existence of context '%s'\n",
930 errno
== ENOENT
? argv
[4] : argv
[2]);
934 ast_cli(fd
, "Failed to include '%s' in '%s' context\n",
938 return RESULT_FAILURE
;
941 /* show some info ... */
942 ast_cli(fd
, "Context '%s' included in '%s' context\n",
945 return RESULT_SUCCESS
;
948 static int handle_context_add_include(int fd
, int argc
, char *argv
[])
950 if (argc
!= 6) /* dialplan add include CTX in CTX */
951 return RESULT_SHOWUSAGE
;
953 /* fifth arg must be 'into' ... */
954 if (strcmp(argv
[4], "into"))
955 return RESULT_SHOWUSAGE
;
957 if (ast_context_add_include(argv
[5], argv
[3], registrar
)) {
960 ast_cli(fd
, "Out of memory for context addition\n");
964 ast_cli(fd
, "Failed to lock context(s) list, please try again later\n");
968 ast_cli(fd
, "Context '%s' already included in '%s' context\n",
974 ast_cli(fd
, "There is no existence of context '%s'\n",
975 errno
== ENOENT
? argv
[5] : argv
[3]);
979 ast_cli(fd
, "Failed to include '%s' in '%s' context\n",
983 return RESULT_FAILURE
;
986 /* show some info ... */
987 ast_cli(fd
, "Context '%s' included in '%s' context\n",
990 return RESULT_SUCCESS
;
993 static char *complete_context_add_include_deprecated(const char *line
, const char *word
, int pos
,
996 struct ast_context
*c
;
999 int len
= strlen(word
);
1001 if (pos
== 2) { /* 'include context _X_' (context) ... */
1002 if (ast_rdlock_contexts()) {
1003 ast_log(LOG_ERROR
, "Failed to lock context list\n");
1006 for (c
= NULL
; !ret
&& (c
= ast_walk_contexts(c
)); )
1007 if (partial_match(ast_get_context_name(c
), word
, len
) && ++which
> state
)
1008 ret
= strdup(ast_get_context_name(c
));
1009 ast_unlock_contexts();
1011 } else if (pos
== 3) { /* include context CTX _X_ */
1012 /* complete as 'in' if context exists or we are unable to check */
1013 char *context
, *dupline
;
1014 struct ast_context
*c
;
1015 const char *s
= skip_words(line
, 2); /* should not fail */
1017 if (state
!= 0) /* only once */
1020 /* parse context from line ... */
1021 context
= dupline
= strdup(s
);
1023 ast_log(LOG_ERROR
, "Out of free memory\n");
1024 return strdup("in");
1026 strsep(&dupline
, " ");
1028 /* check for context existence ... */
1029 if (ast_rdlock_contexts()) {
1030 ast_log(LOG_ERROR
, "Failed to lock context list\n");
1031 /* our fault, we can't check, so complete 'in' ... */
1034 for (c
= NULL
; !ret
&& (c
= ast_walk_contexts(c
)); )
1035 if (!strcmp(context
, ast_get_context_name(c
)))
1036 ret
= strdup("in"); /* found */
1037 ast_unlock_contexts();
1041 } else if (pos
== 4) { /* 'include context CTX in _X_' (dst context) */
1042 char *context
, *dupline
, *in
;
1043 const char *s
= skip_words(line
, 2); /* should not fail */
1044 context
= dupline
= strdup(s
);
1046 ast_log(LOG_ERROR
, "Out of free memory\n");
1049 strsep(&dupline
, " "); /* skip context */
1050 in
= strsep(&dupline
, " ");
1051 /* error if missing context or third word is not 'in' */
1052 if (!strlen(context
) || strcmp(in
, "in")) {
1053 ast_log(LOG_ERROR
, "bad context %s or missing in %s\n",
1058 if (ast_rdlock_contexts()) {
1059 ast_log(LOG_ERROR
, "Failed to lock context list\n");
1063 for (c
= NULL
; (c
= ast_walk_contexts(c
)); )
1064 if (!strcmp(context
, ast_get_context_name(c
)))
1066 if (c
) { /* first context exists, go on... */
1067 /* go through all contexts ... */
1068 for (c
= NULL
; !ret
&& (c
= ast_walk_contexts(c
)); ) {
1069 if (!strcmp(context
, ast_get_context_name(c
)))
1070 continue; /* skip ourselves */
1071 if (partial_match(ast_get_context_name(c
), word
, len
) &&
1072 !lookup_ci(c
, context
) /* not included yet */ &&
1074 ret
= strdup(ast_get_context_name(c
));
1077 ast_log(LOG_ERROR
, "context %s not found\n", context
);
1079 ast_unlock_contexts();
1088 static char *complete_context_add_include(const char *line
, const char *word
, int pos
,
1091 struct ast_context
*c
;
1094 int len
= strlen(word
);
1096 if (pos
== 3) { /* 'dialplan add include _X_' (context) ... */
1097 if (ast_rdlock_contexts()) {
1098 ast_log(LOG_ERROR
, "Failed to lock context list\n");
1101 for (c
= NULL
; !ret
&& (c
= ast_walk_contexts(c
)); )
1102 if (partial_match(ast_get_context_name(c
), word
, len
) && ++which
> state
)
1103 ret
= strdup(ast_get_context_name(c
));
1104 ast_unlock_contexts();
1106 } else if (pos
== 4) { /* dialplan add include CTX _X_ */
1107 /* complete as 'into' if context exists or we are unable to check */
1108 char *context
, *dupline
;
1109 struct ast_context
*c
;
1110 const char *s
= skip_words(line
, 3); /* should not fail */
1112 if (state
!= 0) /* only once */
1115 /* parse context from line ... */
1116 context
= dupline
= strdup(s
);
1118 ast_log(LOG_ERROR
, "Out of free memory\n");
1119 return strdup("into");
1121 strsep(&dupline
, " ");
1123 /* check for context existence ... */
1124 if (ast_rdlock_contexts()) {
1125 ast_log(LOG_ERROR
, "Failed to lock context list\n");
1126 /* our fault, we can't check, so complete 'into' ... */
1127 ret
= strdup("into");
1129 for (c
= NULL
; !ret
&& (c
= ast_walk_contexts(c
)); )
1130 if (!strcmp(context
, ast_get_context_name(c
)))
1131 ret
= strdup("into"); /* found */
1132 ast_unlock_contexts();
1136 } else if (pos
== 5) { /* 'dialplan add include CTX into _X_' (dst context) */
1137 char *context
, *dupline
, *into
;
1138 const char *s
= skip_words(line
, 3); /* should not fail */
1139 context
= dupline
= strdup(s
);
1141 ast_log(LOG_ERROR
, "Out of free memory\n");
1144 strsep(&dupline
, " "); /* skip context */
1145 into
= strsep(&dupline
, " ");
1146 /* error if missing context or fifth word is not 'into' */
1147 if (!strlen(context
) || strcmp(into
, "into")) {
1148 ast_log(LOG_ERROR
, "bad context %s or missing into %s\n",
1153 if (ast_rdlock_contexts()) {
1154 ast_log(LOG_ERROR
, "Failed to lock context list\n");
1158 for (c
= NULL
; (c
= ast_walk_contexts(c
)); )
1159 if (!strcmp(context
, ast_get_context_name(c
)))
1161 if (c
) { /* first context exists, go on... */
1162 /* go through all contexts ... */
1163 for (c
= NULL
; !ret
&& (c
= ast_walk_contexts(c
)); ) {
1164 if (!strcmp(context
, ast_get_context_name(c
)))
1165 continue; /* skip ourselves */
1166 if (partial_match(ast_get_context_name(c
), word
, len
) &&
1167 !lookup_ci(c
, context
) /* not included yet */ &&
1169 ret
= strdup(ast_get_context_name(c
));
1172 ast_log(LOG_ERROR
, "context %s not found\n", context
);
1174 ast_unlock_contexts();
1184 * \brief 'save dialplan' CLI command implementation functions ...
1186 static int handle_save_dialplan(int fd
, int argc
, char *argv
[])
1189 struct ast_context
*c
;
1190 struct ast_config
*cfg
;
1191 struct ast_variable
*v
;
1192 int incomplete
= 0; /* incomplete config write? */
1195 const char *base
, *slash
, *file
;
1197 if (! (static_config
&& !write_protect_config
)) {
1199 "I can't save dialplan now, see '%s' example file.\n",
1201 return RESULT_FAILURE
;
1204 if (argc
!= 2 && argc
!= 3)
1205 return RESULT_SHOWUSAGE
;
1207 if (ast_mutex_lock(&save_dialplan_lock
)) {
1209 "Failed to lock dialplan saving (another proccess saving?)\n");
1210 return RESULT_FAILURE
;
1212 /* XXX the code here is quite loose, a pathname with .conf in it
1213 * is assumed to be a complete pathname
1215 if (argc
== 3) { /* have config path. Look for *.conf */
1217 if (!strstr(argv
[2], ".conf")) { /*no, this is assumed to be a pathname */
1218 /* if filename ends with '/', do not add one */
1219 slash
= (*(argv
[2] + strlen(argv
[2]) -1) == '/') ? "/" : "";
1220 file
= config
; /* default: 'extensions.conf' */
1221 } else { /* yes, complete file name */
1226 /* no config file, default one */
1227 base
= ast_config_AST_CONFIG_DIR
;
1231 snprintf(filename
, sizeof(filename
), "%s%s%s", base
, slash
, config
);
1233 cfg
= ast_config_load("extensions.conf");
1235 /* try to lock contexts list */
1236 if (ast_rdlock_contexts()) {
1237 ast_cli(fd
, "Failed to lock contexts list\n");
1238 ast_mutex_unlock(&save_dialplan_lock
);
1239 ast_config_destroy(cfg
);
1240 return RESULT_FAILURE
;
1243 /* create new file ... */
1244 if (!(output
= fopen(filename
, "wt"))) {
1245 ast_cli(fd
, "Failed to create file '%s'\n",
1247 ast_unlock_contexts();
1248 ast_mutex_unlock(&save_dialplan_lock
);
1249 ast_config_destroy(cfg
);
1250 return RESULT_FAILURE
;
1253 /* fireout general info */
1254 fprintf(output
, "[general]\nstatic=%s\nwriteprotect=%s\nautofallthrough=%s\nclearglobalvars=%s\npriorityjumping=%s\n\n",
1255 static_config
? "yes" : "no",
1256 write_protect_config
? "yes" : "no",
1257 autofallthrough_config
? "yes" : "no",
1258 clearglobalvars_config
? "yes" : "no",
1259 ast_true(ast_variable_retrieve(cfg
, "general", "priorityjumping")) ? "yes" : "no");
1261 if ((v
= ast_variable_browse(cfg
, "globals"))) {
1262 fprintf(output
, "[globals]\n");
1264 fprintf(output
, "%s => %s\n", v
->name
, v
->value
);
1267 fprintf(output
, "\n");
1270 ast_config_destroy(cfg
);
1272 #define PUT_CTX_HDR do { \
1273 if (!context_header_written) { \
1274 fprintf(output, "[%s]\n", ast_get_context_name(c)); \
1275 context_header_written = 1; \
1279 /* walk all contexts */
1280 for (c
= NULL
; (c
= ast_walk_contexts(c
)); ) {
1281 int context_header_written
= 0;
1282 struct ast_exten
*e
, *last_written_e
= NULL
;
1283 struct ast_include
*i
;
1284 struct ast_ignorepat
*ip
;
1287 /* try to lock context and fireout all info */
1288 if (ast_lock_context(c
)) { /* lock failure */
1292 /* registered by this module? */
1293 /* XXX do we need this ? */
1294 if (!strcmp(ast_get_context_registrar(c
), registrar
)) {
1295 fprintf(output
, "[%s]\n", ast_get_context_name(c
));
1296 context_header_written
= 1;
1299 /* walk extensions ... */
1300 for (e
= NULL
; (e
= ast_walk_context_extensions(c
, e
)); ) {
1301 struct ast_exten
*p
= NULL
;
1303 /* fireout priorities */
1304 while ( (p
= ast_walk_extension_priorities(e
, p
)) ) {
1305 if (strcmp(ast_get_extension_registrar(p
), registrar
) != 0) /* not this source */
1308 /* make empty line between different extensions */
1309 if (last_written_e
!= NULL
&&
1310 strcmp(ast_get_extension_name(last_written_e
),
1311 ast_get_extension_name(p
)))
1312 fprintf(output
, "\n");
1317 if (ast_get_extension_priority(p
)==PRIORITY_HINT
) { /* easy */
1318 fprintf(output
, "exten => %s,hint,%s\n",
1319 ast_get_extension_name(p
),
1320 ast_get_extension_app(p
));
1321 } else { /* copy and replace '|' with ',' */
1322 const char *sep
, *cid
;
1323 char *tempdata
= "";
1325 const char *el
= ast_get_extension_label(p
);
1326 char label
[128] = "";
1328 s
= ast_get_extension_app_data(p
);
1331 tempdata
= alloca(strlen(tempdata
) * 2 + 1);
1333 for (t
= tempdata
; *s
; s
++, t
++) {
1337 if (*s
== ',' || *s
== ';')
1342 /* Terminating NULL */
1346 if (ast_get_extension_matchcid(p
)) {
1348 cid
= ast_get_extension_cidmatch(p
);
1352 if (el
&& (snprintf(label
, sizeof(label
), "(%s)", el
) != (strlen(el
) + 2)))
1353 incomplete
= 1; /* error encountered or label > 125 chars */
1355 fprintf(output
, "exten => %s%s%s,%d%s,%s(%s)\n",
1356 ast_get_extension_name(p
), (ast_strlen_zero(sep
) ? "" : sep
), (ast_strlen_zero(cid
) ? "" : cid
),
1357 ast_get_extension_priority(p
), label
,
1358 ast_get_extension_app(p
), (ast_strlen_zero(tempdata
) ? "" : tempdata
));
1363 /* written any extensions? ok, write space between exten & inc */
1365 fprintf(output
, "\n");
1367 /* walk through includes */
1368 for (i
= NULL
; (i
= ast_walk_context_includes(c
, i
)) ; ) {
1369 if (strcmp(ast_get_include_registrar(i
), registrar
) != 0)
1370 continue; /* not mine */
1372 fprintf(output
, "include => %s\n", ast_get_include_name(i
));
1374 if (ast_walk_context_includes(c
, NULL
))
1375 fprintf(output
, "\n");
1377 /* walk through switches */
1378 for (sw
= NULL
; (sw
= ast_walk_context_switches(c
, sw
)) ; ) {
1379 if (strcmp(ast_get_switch_registrar(sw
), registrar
) != 0)
1380 continue; /* not mine */
1382 fprintf(output
, "switch => %s/%s\n",
1383 ast_get_switch_name(sw
), ast_get_switch_data(sw
));
1386 if (ast_walk_context_switches(c
, NULL
))
1387 fprintf(output
, "\n");
1389 /* fireout ignorepats ... */
1390 for (ip
= NULL
; (ip
= ast_walk_context_ignorepats(c
, ip
)); ) {
1391 if (strcmp(ast_get_ignorepat_registrar(ip
), registrar
) != 0)
1392 continue; /* not mine */
1394 fprintf(output
, "ignorepat => %s\n",
1395 ast_get_ignorepat_name(ip
));
1398 ast_unlock_context(c
);
1401 ast_unlock_contexts();
1402 ast_mutex_unlock(&save_dialplan_lock
);
1406 ast_cli(fd
, "Saved dialplan is incomplete\n");
1407 return RESULT_FAILURE
;
1410 ast_cli(fd
, "Dialplan successfully saved into '%s'\n",
1412 return RESULT_SUCCESS
;
1416 * \brief ADD EXTENSION command stuff
1418 static int handle_context_add_extension_deprecated(int fd
, int argc
, char *argv
[])
1421 char *exten
, *prior
;
1423 char *cidmatch
, *app
, *app_data
;
1426 /* check for arguments at first */
1427 if (argc
!= 5 && argc
!= 6)
1428 return RESULT_SHOWUSAGE
;
1429 if (strcmp(argv
[3], "into"))
1430 return RESULT_SHOWUSAGE
;
1431 if (argc
== 6) if (strcmp(argv
[5], "replace")) return RESULT_SHOWUSAGE
;
1433 /* XXX overwrite argv[2] */
1434 whole_exten
= argv
[2];
1435 exten
= strsep(&whole_exten
,",");
1436 if (strchr(exten
, '/')) {
1438 strsep(&cidmatch
,"/");
1442 prior
= strsep(&whole_exten
,",");
1444 if (!strcmp(prior
, "hint")) {
1445 iprior
= PRIORITY_HINT
;
1447 if (sscanf(prior
, "%d", &iprior
) != 1) {
1448 ast_cli(fd
, "'%s' is not a valid priority\n", prior
);
1454 if (app
&& (start
= strchr(app
, '(')) && (end
= strrchr(app
, ')'))) {
1455 *start
= *end
= '\0';
1456 app_data
= start
+ 1;
1457 ast_process_quotes_and_slashes(app_data
, ',', '|');
1460 app_data
= strchr(app
, ',');
1469 if (!exten
|| !prior
|| !app
|| (!app_data
&& iprior
!= PRIORITY_HINT
))
1470 return RESULT_SHOWUSAGE
;
1474 if (ast_add_extension(argv
[4], argc
== 6 ? 1 : 0, exten
, iprior
, NULL
, cidmatch
, app
,
1475 (void *)strdup(app_data
), ast_free
, registrar
)) {
1478 ast_cli(fd
, "Out of free memory\n");
1482 ast_cli(fd
, "Failed to lock context(s) list, please try again later\n");
1486 ast_cli(fd
, "No existence of '%s' context\n", argv
[4]);
1490 ast_cli(fd
, "Extension %s@%s with priority %s already exists\n",
1491 exten
, argv
[4], prior
);
1495 ast_cli(fd
, "Failed to add '%s,%s,%s,%s' extension into '%s' context\n",
1496 exten
, prior
, app
, app_data
, argv
[4]);
1499 return RESULT_FAILURE
;
1503 ast_cli(fd
, "Extension %s@%s (%s) replace by '%s,%s,%s,%s'\n",
1504 exten
, argv
[4], prior
, exten
, prior
, app
, app_data
);
1506 ast_cli(fd
, "Extension '%s,%s,%s,%s' added into '%s' context\n",
1507 exten
, prior
, app
, app_data
, argv
[4]);
1509 return RESULT_SUCCESS
;
1511 static int handle_context_add_extension(int fd
, int argc
, char *argv
[])
1514 char *exten
, *prior
;
1516 char *cidmatch
, *app
, *app_data
;
1519 /* check for arguments at first */
1520 if (argc
!= 6 && argc
!= 7)
1521 return RESULT_SHOWUSAGE
;
1522 if (strcmp(argv
[4], "into"))
1523 return RESULT_SHOWUSAGE
;
1524 if (argc
== 7) if (strcmp(argv
[6], "replace")) return RESULT_SHOWUSAGE
;
1526 /* XXX overwrite argv[3] */
1527 whole_exten
= argv
[3];
1528 exten
= strsep(&whole_exten
,",");
1529 if (strchr(exten
, '/')) {
1531 strsep(&cidmatch
,"/");
1535 prior
= strsep(&whole_exten
,",");
1537 if (!strcmp(prior
, "hint")) {
1538 iprior
= PRIORITY_HINT
;
1540 if (sscanf(prior
, "%d", &iprior
) != 1) {
1541 ast_cli(fd
, "'%s' is not a valid priority\n", prior
);
1547 if (app
&& (start
= strchr(app
, '(')) && (end
= strrchr(app
, ')'))) {
1548 *start
= *end
= '\0';
1549 app_data
= start
+ 1;
1550 ast_process_quotes_and_slashes(app_data
, ',', '|');
1553 app_data
= strchr(app
, ',');
1562 if (!exten
|| !prior
|| !app
|| (!app_data
&& iprior
!= PRIORITY_HINT
))
1563 return RESULT_SHOWUSAGE
;
1567 if (ast_add_extension(argv
[5], argc
== 7 ? 1 : 0, exten
, iprior
, NULL
, cidmatch
, app
,
1568 (void *)strdup(app_data
), ast_free
, registrar
)) {
1571 ast_cli(fd
, "Out of free memory\n");
1575 ast_cli(fd
, "Failed to lock context(s) list, please try again later\n");
1579 ast_cli(fd
, "No existence of '%s' context\n", argv
[5]);
1583 ast_cli(fd
, "Extension %s@%s with priority %s already exists\n",
1584 exten
, argv
[5], prior
);
1588 ast_cli(fd
, "Failed to add '%s,%s,%s,%s' extension into '%s' context\n",
1589 exten
, prior
, app
, app_data
, argv
[5]);
1592 return RESULT_FAILURE
;
1596 ast_cli(fd
, "Extension %s@%s (%s) replace by '%s,%s,%s,%s'\n",
1597 exten
, argv
[5], prior
, exten
, prior
, app
, app_data
);
1599 ast_cli(fd
, "Extension '%s,%s,%s,%s' added into '%s' context\n",
1600 exten
, prior
, app
, app_data
, argv
[5]);
1602 return RESULT_SUCCESS
;
1605 /*! dialplan add extension 6123,1,Dial,IAX/212.71.138.13/6123 into local */
1606 static char *complete_context_add_extension_deprecated(const char *line
, const char *word
, int pos
, int state
)
1610 if (pos
== 3) { /* complete 'into' word ... */
1611 return (state
== 0) ? strdup("into") : NULL
;
1612 } else if (pos
== 4) { /* complete context */
1613 struct ast_context
*c
= NULL
;
1614 int len
= strlen(word
);
1617 /* try to lock contexts list ... */
1618 if (ast_rdlock_contexts()) {
1619 ast_log(LOG_WARNING
, "Failed to lock contexts list\n");
1623 /* walk through all contexts */
1624 while ( !res
&& (c
= ast_walk_contexts(c
)) )
1625 if (partial_match(ast_get_context_name(c
), word
, len
) && ++which
> state
)
1626 res
= strdup(ast_get_context_name(c
));
1627 ast_unlock_contexts();
1629 } else if (pos
== 5) {
1630 return state
== 0 ? strdup("replace") : NULL
;
1635 static char *complete_context_add_extension(const char *line
, const char *word
, int pos
, int state
)
1639 if (pos
== 4) { /* complete 'into' word ... */
1640 return (state
== 0) ? strdup("into") : NULL
;
1641 } else if (pos
== 5) { /* complete context */
1642 struct ast_context
*c
= NULL
;
1643 int len
= strlen(word
);
1646 /* try to lock contexts list ... */
1647 if (ast_rdlock_contexts()) {
1648 ast_log(LOG_WARNING
, "Failed to lock contexts list\n");
1652 /* walk through all contexts */
1653 while ( !res
&& (c
= ast_walk_contexts(c
)) )
1654 if (partial_match(ast_get_context_name(c
), word
, len
) && ++which
> state
)
1655 res
= strdup(ast_get_context_name(c
));
1656 ast_unlock_contexts();
1658 } else if (pos
== 6) {
1659 return state
== 0 ? strdup("replace") : NULL
;
1665 * IGNOREPAT CLI stuff
1667 static int handle_context_add_ignorepat_deprecated(int fd
, int argc
, char *argv
[])
1670 return RESULT_SHOWUSAGE
;
1671 if (strcmp(argv
[3], "into"))
1672 return RESULT_SHOWUSAGE
;
1674 if (ast_context_add_ignorepat(argv
[4], argv
[2], registrar
)) {
1677 ast_cli(fd
, "Out of free memory\n");
1681 ast_cli(fd
, "There is no existence of '%s' context\n", argv
[4]);
1685 ast_cli(fd
, "Ignore pattern '%s' already included in '%s' context\n",
1690 ast_cli(fd
, "Failed to lock context(s) list, please, try again later\n");
1694 ast_cli(fd
, "Failed to add ingore pattern '%s' into '%s' context\n",
1698 return RESULT_FAILURE
;
1701 ast_cli(fd
, "Ignore pattern '%s' added into '%s' context\n",
1703 return RESULT_SUCCESS
;
1706 static int handle_context_add_ignorepat(int fd
, int argc
, char *argv
[])
1709 return RESULT_SHOWUSAGE
;
1710 if (strcmp(argv
[4], "into"))
1711 return RESULT_SHOWUSAGE
;
1713 if (ast_context_add_ignorepat(argv
[5], argv
[3], registrar
)) {
1716 ast_cli(fd
, "Out of free memory\n");
1720 ast_cli(fd
, "There is no existence of '%s' context\n", argv
[5]);
1724 ast_cli(fd
, "Ignore pattern '%s' already included in '%s' context\n",
1729 ast_cli(fd
, "Failed to lock context(s) list, please, try again later\n");
1733 ast_cli(fd
, "Failed to add ingore pattern '%s' into '%s' context\n",
1737 return RESULT_FAILURE
;
1740 ast_cli(fd
, "Ignore pattern '%s' added into '%s' context\n",
1742 return RESULT_SUCCESS
;
1745 static char *complete_context_add_ignorepat_deprecated(const char *line
, const char *word
,
1749 return state
== 0 ? strdup("into") : NULL
;
1750 else if (pos
== 4) {
1751 struct ast_context
*c
;
1753 char *dupline
, *ignorepat
= NULL
;
1756 int len
= strlen(word
);
1758 /* XXX skip first two words 'add' 'ignorepat' */
1759 s
= skip_words(line
, 2);
1762 dupline
= strdup(s
);
1764 ast_log(LOG_ERROR
, "Malloc failure\n");
1767 ignorepat
= strsep(&dupline
, " ");
1769 if (ast_rdlock_contexts()) {
1770 ast_log(LOG_ERROR
, "Failed to lock contexts list\n");
1774 for (c
= NULL
; !ret
&& (c
= ast_walk_contexts(c
));) {
1777 if (!partial_match(ast_get_context_name(c
), word
, len
))
1778 continue; /* not mine */
1779 if (ignorepat
) /* there must be one, right ? */
1780 found
= lookup_c_ip(c
, ignorepat
);
1781 if (!found
&& ++which
> state
)
1782 ret
= strdup(ast_get_context_name(c
));
1787 ast_unlock_contexts();
1794 static char *complete_context_add_ignorepat(const char *line
, const char *word
,
1798 return state
== 0 ? strdup("into") : NULL
;
1799 else if (pos
== 5) {
1800 struct ast_context
*c
;
1802 char *dupline
, *ignorepat
= NULL
;
1805 int len
= strlen(word
);
1807 /* XXX skip first three words 'dialplan' 'add' 'ignorepat' */
1808 s
= skip_words(line
, 3);
1811 dupline
= strdup(s
);
1813 ast_log(LOG_ERROR
, "Malloc failure\n");
1816 ignorepat
= strsep(&dupline
, " ");
1818 if (ast_rdlock_contexts()) {
1819 ast_log(LOG_ERROR
, "Failed to lock contexts list\n");
1823 for (c
= NULL
; !ret
&& (c
= ast_walk_contexts(c
));) {
1826 if (!partial_match(ast_get_context_name(c
), word
, len
))
1827 continue; /* not mine */
1828 if (ignorepat
) /* there must be one, right ? */
1829 found
= lookup_c_ip(c
, ignorepat
);
1830 if (!found
&& ++which
> state
)
1831 ret
= strdup(ast_get_context_name(c
));
1836 ast_unlock_contexts();
1843 static int handle_context_remove_ignorepat_deprecated(int fd
, int argc
, char *argv
[])
1846 return RESULT_SHOWUSAGE
;
1847 if (strcmp(argv
[3], "from"))
1848 return RESULT_SHOWUSAGE
;
1850 if (ast_context_remove_ignorepat(argv
[4], argv
[2], registrar
)) {
1853 ast_cli(fd
, "Failed to lock context(s) list, please try again later\n");
1857 ast_cli(fd
, "There is no existence of '%s' context\n", argv
[4]);
1861 ast_cli(fd
, "There is no existence of '%s' ignore pattern in '%s' context\n",
1866 ast_cli(fd
, "Failed to remove ignore pattern '%s' from '%s' context\n", argv
[2], argv
[4]);
1869 return RESULT_FAILURE
;
1872 ast_cli(fd
, "Ignore pattern '%s' removed from '%s' context\n",
1874 return RESULT_SUCCESS
;
1877 static int handle_context_remove_ignorepat(int fd
, int argc
, char *argv
[])
1880 return RESULT_SHOWUSAGE
;
1881 if (strcmp(argv
[4], "from"))
1882 return RESULT_SHOWUSAGE
;
1884 if (ast_context_remove_ignorepat(argv
[5], argv
[3], registrar
)) {
1887 ast_cli(fd
, "Failed to lock context(s) list, please try again later\n");
1891 ast_cli(fd
, "There is no existence of '%s' context\n", argv
[5]);
1895 ast_cli(fd
, "There is no existence of '%s' ignore pattern in '%s' context\n",
1900 ast_cli(fd
, "Failed to remove ignore pattern '%s' from '%s' context\n", argv
[3], argv
[5]);
1903 return RESULT_FAILURE
;
1906 ast_cli(fd
, "Ignore pattern '%s' removed from '%s' context\n",
1908 return RESULT_SUCCESS
;
1911 static char *complete_context_remove_ignorepat_deprecated(const char *line
, const char *word
,
1914 struct ast_context
*c
;
1919 int len
= strlen(word
);
1920 if (ast_rdlock_contexts()) {
1921 ast_log(LOG_WARNING
, "Failed to lock contexts list\n");
1925 for (c
= NULL
; !ret
&& (c
= ast_walk_contexts(c
));) {
1926 struct ast_ignorepat
*ip
;
1928 if (ast_lock_context(c
)) /* error, skip it */
1931 for (ip
= NULL
; !ret
&& (ip
= ast_walk_context_ignorepats(c
, ip
));) {
1932 if (partial_match(ast_get_ignorepat_name(ip
), word
, len
) && ++which
> state
) {
1934 struct ast_context
*cw
= NULL
;
1936 while ( (cw
= ast_walk_contexts(cw
)) && cw
!= c
&& !found
) {
1937 /* XXX do i stop on c, or skip it ? */
1938 found
= lookup_c_ip(cw
, ast_get_ignorepat_name(ip
));
1941 ret
= strdup(ast_get_ignorepat_name(ip
));
1944 ast_unlock_context(c
);
1946 ast_unlock_contexts();
1948 } else if (pos
== 3) {
1949 return state
== 0 ? strdup("from") : NULL
;
1950 } else if (pos
== 4) { /* XXX check this */
1951 char *dupline
, *duplinet
, *ignorepat
;
1952 int len
= strlen(word
);
1954 dupline
= strdup(line
);
1956 ast_log(LOG_WARNING
, "Out of free memory\n");
1961 strsep(&duplinet
, " ");
1962 strsep(&duplinet
, " ");
1963 ignorepat
= strsep(&duplinet
, " ");
1970 if (ast_rdlock_contexts()) {
1971 ast_log(LOG_WARNING
, "Failed to lock contexts list\n");
1976 for (c
= NULL
; !ret
&& (c
= ast_walk_contexts(c
)); ) {
1977 if (ast_lock_context(c
)) /* fail, skip it */
1979 if (!partial_match(ast_get_context_name(c
), word
, len
))
1981 if (lookup_c_ip(c
, ignorepat
) && ++which
> state
)
1982 ret
= strdup(ast_get_context_name(c
));
1983 ast_unlock_context(c
);
1985 ast_unlock_contexts();
1993 static char *complete_context_remove_ignorepat(const char *line
, const char *word
,
1996 struct ast_context
*c
;
2001 int len
= strlen(word
);
2002 if (ast_rdlock_contexts()) {
2003 ast_log(LOG_WARNING
, "Failed to lock contexts list\n");
2007 for (c
= NULL
; !ret
&& (c
= ast_walk_contexts(c
));) {
2008 struct ast_ignorepat
*ip
;
2010 if (ast_lock_context(c
)) /* error, skip it */
2013 for (ip
= NULL
; !ret
&& (ip
= ast_walk_context_ignorepats(c
, ip
));) {
2014 if (partial_match(ast_get_ignorepat_name(ip
), word
, len
) && ++which
> state
) {
2016 struct ast_context
*cw
= NULL
;
2018 while ( (cw
= ast_walk_contexts(cw
)) && cw
!= c
&& !found
) {
2019 /* XXX do i stop on c, or skip it ? */
2020 found
= lookup_c_ip(cw
, ast_get_ignorepat_name(ip
));
2023 ret
= strdup(ast_get_ignorepat_name(ip
));
2026 ast_unlock_context(c
);
2028 ast_unlock_contexts();
2030 } else if (pos
== 4) {
2031 return state
== 0 ? strdup("from") : NULL
;
2032 } else if (pos
== 5) { /* XXX check this */
2033 char *dupline
, *duplinet
, *ignorepat
;
2034 int len
= strlen(word
);
2036 dupline
= strdup(line
);
2038 ast_log(LOG_WARNING
, "Out of free memory\n");
2043 strsep(&duplinet
, " ");
2044 strsep(&duplinet
, " ");
2045 ignorepat
= strsep(&duplinet
, " ");
2052 if (ast_rdlock_contexts()) {
2053 ast_log(LOG_WARNING
, "Failed to lock contexts list\n");
2058 for (c
= NULL
; !ret
&& (c
= ast_walk_contexts(c
)); ) {
2059 if (ast_lock_context(c
)) /* fail, skip it */
2061 if (!partial_match(ast_get_context_name(c
), word
, len
))
2063 if (lookup_c_ip(c
, ignorepat
) && ++which
> state
)
2064 ret
= strdup(ast_get_context_name(c
));
2065 ast_unlock_context(c
);
2067 ast_unlock_contexts();
2075 static int pbx_load_module(void);
2077 static int handle_reload_extensions(int fd
, int argc
, char *argv
[])
2080 return RESULT_SHOWUSAGE
;
2081 if (clearglobalvars_config
)
2082 pbx_builtin_clear_globals();
2084 return RESULT_SUCCESS
;
2088 * CLI entries for commands provided by this module
2090 static struct ast_cli_entry cli_dont_include_deprecated
= {
2091 { "dont", "include", NULL
},
2092 handle_context_dont_include_deprecated
, NULL
,
2093 NULL
, complete_context_dont_include_deprecated
};
2095 static struct ast_cli_entry cli_remove_extension_deprecated
= {
2096 { "remove", "extension", NULL
},
2097 handle_context_remove_extension_deprecated
, NULL
,
2098 NULL
, complete_context_remove_extension_deprecated
};
2100 static struct ast_cli_entry cli_include_context_deprecated
= {
2101 { "include", "context", NULL
},
2102 handle_context_add_include_deprecated
, NULL
,
2103 NULL
, complete_context_add_include_deprecated
};
2105 static struct ast_cli_entry cli_add_extension_deprecated
= {
2106 { "add", "extension", NULL
},
2107 handle_context_add_extension_deprecated
, NULL
,
2108 NULL
, complete_context_add_extension_deprecated
};
2110 static struct ast_cli_entry cli_add_ignorepat_deprecated
= {
2111 { "add", "ignorepat", NULL
},
2112 handle_context_add_ignorepat_deprecated
, NULL
,
2113 NULL
, complete_context_add_ignorepat_deprecated
};
2115 static struct ast_cli_entry cli_remove_ignorepat_deprecated
= {
2116 { "remove", "ignorepat", NULL
},
2117 handle_context_remove_ignorepat_deprecated
, NULL
,
2118 NULL
, complete_context_remove_ignorepat_deprecated
};
2120 static struct ast_cli_entry cli_extensions_reload_deprecated
= {
2121 { "extensions", "reload", NULL
},
2122 handle_reload_extensions
, NULL
,
2125 static struct ast_cli_entry cli_save_dialplan_deprecated
= {
2126 { "save", "dialplan", NULL
},
2127 handle_save_dialplan
, NULL
,
2130 static struct ast_cli_entry cli_pbx_config
[] = {
2131 { { "dialplan", "add", "extension", NULL
},
2132 handle_context_add_extension
, "Add new extension into context",
2133 context_add_extension_help
, complete_context_add_extension
, &cli_add_extension_deprecated
},
2135 { { "dialplan", "remove", "extension", NULL
},
2136 handle_context_remove_extension
, "Remove a specified extension",
2137 context_remove_extension_help
, complete_context_remove_extension
, &cli_remove_extension_deprecated
},
2139 { { "dialplan", "add", "ignorepat", NULL
},
2140 handle_context_add_ignorepat
, "Add new ignore pattern",
2141 context_add_ignorepat_help
, complete_context_add_ignorepat
, &cli_add_ignorepat_deprecated
},
2143 { { "dialplan", "remove", "ignorepat", NULL
},
2144 handle_context_remove_ignorepat
, "Remove ignore pattern from context",
2145 context_remove_ignorepat_help
, complete_context_remove_ignorepat
, &cli_remove_ignorepat_deprecated
},
2147 { { "dialplan", "add", "include", NULL
},
2148 handle_context_add_include
, "Include context in other context",
2149 context_add_include_help
, complete_context_add_include
, &cli_include_context_deprecated
},
2151 { { "dialplan", "remove", "include", NULL
},
2152 handle_context_remove_include
, "Remove a specified include from context",
2153 context_remove_include_help
, complete_context_remove_include
, &cli_dont_include_deprecated
},
2155 { { "dialplan", "reload", NULL
},
2156 handle_reload_extensions
, "Reload extensions and *only* extensions",
2157 reload_extensions_help
, NULL
, &cli_extensions_reload_deprecated
},
2161 static struct ast_cli_entry cli_dialplan_save
= {
2162 { "dialplan", "save", NULL
},
2163 handle_save_dialplan
, "Save dialplan",
2164 save_dialplan_help
, NULL
, &cli_save_dialplan_deprecated
};
2167 * Standard module functions ...
2169 static int unload_module(void)
2171 if (static_config
&& !write_protect_config
)
2172 ast_cli_unregister(&cli_dialplan_save
);
2173 ast_cli_unregister_multiple(cli_pbx_config
, sizeof(cli_pbx_config
) / sizeof(struct ast_cli_entry
));
2174 ast_context_destroy(NULL
, registrar
);
2178 static int pbx_load_config(const char *config_file
)
2180 struct ast_config
*cfg
;
2183 char realvalue
[256];
2185 struct ast_context
*con
;
2186 struct ast_variable
*v
;
2190 cfg
= ast_config_load(config_file
);
2194 /* Use existing config to populate the PBX table */
2195 static_config
= ast_true(ast_variable_retrieve(cfg
, "general", "static"));
2196 write_protect_config
= ast_true(ast_variable_retrieve(cfg
, "general", "writeprotect"));
2197 if ((aft
= ast_variable_retrieve(cfg
, "general", "autofallthrough")))
2198 autofallthrough_config
= ast_true(aft
);
2199 clearglobalvars_config
= ast_true(ast_variable_retrieve(cfg
, "general", "clearglobalvars"));
2200 ast_set2_flag(&ast_options
, ast_true(ast_variable_retrieve(cfg
, "general", "priorityjumping")), AST_OPT_FLAG_PRIORITY_JUMPING
);
2202 if ((cxt
= ast_variable_retrieve(cfg
, "general", "userscontext")))
2203 ast_copy_string(userscontext
, cxt
, sizeof(userscontext
));
2205 ast_copy_string(userscontext
, "default", sizeof(userscontext
));
2207 for (v
= ast_variable_browse(cfg
, "globals"); v
; v
= v
->next
) {
2208 memset(realvalue
, 0, sizeof(realvalue
));
2209 pbx_substitute_variables_helper(NULL
, v
->value
, realvalue
, sizeof(realvalue
) - 1);
2210 pbx_builtin_setvar_helper(NULL
, v
->name
, realvalue
);
2212 for (cxt
= NULL
; (cxt
= ast_category_browse(cfg
, cxt
)); ) {
2213 /* All categories but "general" or "globals" are considered contexts */
2214 if (!strcasecmp(cxt
, "general") || !strcasecmp(cxt
, "globals"))
2216 con
=ast_context_find_or_create(&local_contexts
,cxt
, registrar
);
2220 for (v
= ast_variable_browse(cfg
, cxt
); v
; v
= v
->next
) {
2221 if (!strcasecmp(v
->name
, "exten")) {
2222 char *tc
= ast_strdup(v
->value
);
2225 char realext
[256]="";
2226 char *plus
, *firstp
, *firstc
;
2227 char *pri
, *appl
, *data
, *cidmatch
;
2229 char *ext
= strsep(&stringp
, ",");
2232 pbx_substitute_variables_helper(NULL
, ext
, realext
, sizeof(realext
) - 1);
2233 cidmatch
= strchr(realext
, '/');
2236 ast_shrink_phone_number(cidmatch
);
2238 pri
= strsep(&stringp
, ",");
2241 pri
= ast_skip_blanks(pri
);
2242 pri
= ast_trim_blanks(pri
);
2243 label
= strchr(pri
, '(');
2246 end
= strchr(label
, ')');
2250 ast_log(LOG_WARNING
, "Label missing trailing ')' at line %d\n", v
->lineno
);
2252 plus
= strchr(pri
, '+');
2255 if (!strcmp(pri
,"hint"))
2257 else if (!strcmp(pri
, "next") || !strcmp(pri
, "n")) {
2261 ast_log(LOG_WARNING
, "Can't use 'next' priority on the first entry!\n");
2262 } else if (!strcmp(pri
, "same") || !strcmp(pri
, "s")) {
2266 ast_log(LOG_WARNING
, "Can't use 'same' priority on the first entry!\n");
2267 } else if (sscanf(pri
, "%d", &ipri
) != 1 &&
2268 (ipri
= ast_findlabel_extension2(NULL
, con
, realext
, pri
, cidmatch
)) < 1) {
2269 ast_log(LOG_WARNING
, "Invalid priority/label '%s' at line %d\n", pri
, v
->lineno
);
2272 appl
= S_OR(stringp
, "");
2273 /* Find the first occurrence of either '(' or ',' */
2274 firstc
= strchr(appl
, ',');
2275 firstp
= strchr(appl
, '(');
2276 if (firstc
&& (!firstp
|| firstc
< firstp
)) {
2277 /* comma found, no parenthesis */
2278 /* or both found, but comma found first */
2279 appl
= strsep(&stringp
, ",");
2281 } else if (!firstc
&& !firstp
) {
2285 /* Final remaining case is parenthesis found first */
2286 appl
= strsep(&stringp
, "(");
2288 end
= strrchr(data
, ')');
2289 if ((end
= strrchr(data
, ')'))) {
2292 ast_log(LOG_WARNING
, "No closing parenthesis found? '%s(%s'\n", appl
, data
);
2294 ast_process_quotes_and_slashes(data
, ',', '|');
2299 appl
= ast_skip_blanks(appl
);
2304 if (!ast_opt_dont_warn
&& !strcmp(realext
, "_."))
2305 ast_log(LOG_WARNING
, "The use of '_.' for an extension is strongly discouraged and can have unexpected behavior. Please use '_X.' instead at line %d\n", v
->lineno
);
2306 if (ast_add_extension2(con
, 0, realext
, ipri
, label
, cidmatch
, appl
, strdup(data
), ast_free
, registrar
)) {
2307 ast_log(LOG_WARNING
, "Unable to register extension at line %d\n", v
->lineno
);
2312 } else if (!strcasecmp(v
->name
, "include")) {
2313 memset(realvalue
, 0, sizeof(realvalue
));
2314 pbx_substitute_variables_helper(NULL
, v
->value
, realvalue
, sizeof(realvalue
) - 1);
2315 if (ast_context_add_include2(con
, realvalue
, registrar
))
2316 ast_log(LOG_WARNING
, "Unable to include context '%s' in context '%s'\n", v
->value
, cxt
);
2317 } else if (!strcasecmp(v
->name
, "ignorepat")) {
2318 memset(realvalue
, 0, sizeof(realvalue
));
2319 pbx_substitute_variables_helper(NULL
, v
->value
, realvalue
, sizeof(realvalue
) - 1);
2320 if (ast_context_add_ignorepat2(con
, realvalue
, registrar
))
2321 ast_log(LOG_WARNING
, "Unable to include ignorepat '%s' in context '%s'\n", v
->value
, cxt
);
2322 } else if (!strcasecmp(v
->name
, "switch") || !strcasecmp(v
->name
, "lswitch") || !strcasecmp(v
->name
, "eswitch")) {
2323 char *stringp
= realvalue
;
2326 memset(realvalue
, 0, sizeof(realvalue
));
2327 if (!strcasecmp(v
->name
, "switch"))
2328 pbx_substitute_variables_helper(NULL
, v
->value
, realvalue
, sizeof(realvalue
) - 1);
2330 ast_copy_string(realvalue
, v
->value
, sizeof(realvalue
));
2331 appl
= strsep(&stringp
, "/");
2332 data
= strsep(&stringp
, ""); /* XXX what for ? */
2335 if (ast_context_add_switch2(con
, appl
, data
, !strcasecmp(v
->name
, "eswitch"), registrar
))
2336 ast_log(LOG_WARNING
, "Unable to include switch '%s' in context '%s'\n", v
->value
, cxt
);
2340 ast_config_destroy(cfg
);
2344 static void append_interface(char *iface
, int maxlen
, char *add
)
2346 int len
= strlen(iface
);
2347 if (strlen(add
) + len
< maxlen
- 2) {
2348 if (strlen(iface
)) {
2350 strcpy(iface
+ len
+ 1, add
);
2356 static void pbx_load_users(void)
2358 struct ast_config
*cfg
;
2360 const char *zapchan
;
2361 const char *hasexten
;
2368 int start
, finish
, x
;
2369 struct ast_context
*con
= NULL
;
2371 cfg
= ast_config_load("users.conf");
2375 for (cat
= ast_category_browse(cfg
, NULL
); cat
; cat
= ast_category_browse(cfg
, cat
)) {
2376 if (!strcasecmp(cat
, "general"))
2379 len
= sizeof(iface
);
2380 if (ast_true(ast_config_option(cfg
, cat
, "hassip"))) {
2381 snprintf(tmp
, sizeof(tmp
), "SIP/%s", cat
);
2382 append_interface(iface
, sizeof(iface
), tmp
);
2384 if (ast_true(ast_config_option(cfg
, cat
, "hasiax"))) {
2385 snprintf(tmp
, sizeof(tmp
), "IAX2/%s", cat
);
2386 append_interface(iface
, sizeof(iface
), tmp
);
2388 if (ast_true(ast_config_option(cfg
, cat
, "hash323"))) {
2389 snprintf(tmp
, sizeof(tmp
), "H323/%s", cat
);
2390 append_interface(iface
, sizeof(iface
), tmp
);
2392 hasexten
= ast_config_option(cfg
, cat
, "hasexten");
2393 if (hasexten
&& !ast_true(hasexten
))
2395 hasvoicemail
= ast_true(ast_config_option(cfg
, cat
, "hasvoicemail"));
2396 zapchan
= ast_variable_retrieve(cfg
, cat
, "zapchan");
2398 zapchan
= ast_variable_retrieve(cfg
, "general", "zapchan");
2399 if (!ast_strlen_zero(zapchan
)) {
2400 ast_copy_string(zapcopy
, zapchan
, sizeof(zapcopy
));
2402 chan
= strsep(&c
, ",");
2404 if (sscanf(chan
, "%d-%d", &start
, &finish
) == 2) {
2406 } else if (sscanf(chan
, "%d", &start
)) {
2410 start
= 0; finish
= 0;
2412 if (finish
< start
) {
2417 for (x
= start
; x
<= finish
; x
++) {
2418 snprintf(tmp
, sizeof(tmp
), "Zap/%d", x
);
2419 append_interface(iface
, sizeof(iface
), tmp
);
2421 chan
= strsep(&c
, ",");
2424 if (!ast_strlen_zero(iface
)) {
2425 /* Only create a context here when it is really needed. Otherwise default empty context
2426 created by pbx_config may conflict with the one explicitly created by pbx_ael */
2428 con
= ast_context_find_or_create(&local_contexts
, userscontext
, registrar
);
2431 ast_log(LOG_ERROR
, "Can't find/create user context '%s'\n", userscontext
);
2436 ast_add_extension2(con
, 0, cat
, -1, NULL
, NULL
, iface
, NULL
, NULL
, registrar
);
2437 /* If voicemail, use "stdexten" else use plain old dial */
2439 snprintf(tmp
, sizeof(tmp
), "stdexten|%s|${HINT}", cat
);
2440 ast_add_extension2(con
, 0, cat
, 1, NULL
, NULL
, "Macro", strdup(tmp
), ast_free
, registrar
);
2442 ast_add_extension2(con
, 0, cat
, 1, NULL
, NULL
, "Dial", strdup("${HINT}"), ast_free
, registrar
);
2446 ast_config_destroy(cfg
);
2449 static int pbx_load_module(void)
2451 struct ast_context
*con
;
2453 if(!pbx_load_config(config
))
2454 return AST_MODULE_LOAD_DECLINE
;
2458 ast_merge_contexts_and_delete(&local_contexts
, registrar
);
2460 for (con
= NULL
; (con
= ast_walk_contexts(con
));)
2461 ast_context_verify_includes(con
);
2463 pbx_set_autofallthrough(autofallthrough_config
);
2468 static int load_module(void)
2470 if (pbx_load_module())
2471 return AST_MODULE_LOAD_DECLINE
;
2473 if (static_config
&& !write_protect_config
)
2474 ast_cli_register(&cli_dialplan_save
);
2475 ast_cli_register_multiple(cli_pbx_config
, sizeof(cli_pbx_config
) / sizeof(struct ast_cli_entry
));
2480 static int reload(void)
2482 if (clearglobalvars_config
)
2483 pbx_builtin_clear_globals();
2488 AST_MODULE_INFO(ASTERISK_GPL_KEY
, AST_MODFLAG_DEFAULT
, "Text Extension Configuration",
2489 .load
= load_module
,
2490 .unload
= unload_module
,