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 = 0;
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_remove_include(int fd, int argc, char *argv[])
120 return RESULT_SHOWUSAGE;
122 if (strcmp(argv[4], "into"))
123 return RESULT_SHOWUSAGE;
125 if (!ast_context_remove_include(argv[5], argv[3], 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 /*! \brief return true if 'name' is included by context c */
137 static int lookup_ci(struct ast_context *c, const char *name)
139 struct ast_include *i = NULL;
141 if (ast_lock_context(c)) /* error, skip */
143 while ( (i = ast_walk_context_includes(c, i)) )
144 if (!strcmp(name, ast_get_include_name(i)))
146 ast_unlock_context(c);
147 return i ? -1 /* success */ : 0;
150 /*! \brief return true if 'name' is in the ignorepats for context c */
151 static int lookup_c_ip(struct ast_context *c, const char *name)
153 struct ast_ignorepat *ip = NULL;
155 if (ast_lock_context(c)) /* error, skip */
157 while ( (ip = ast_walk_context_ignorepats(c, ip)) )
158 if (!strcmp(name, ast_get_ignorepat_name(ip)))
160 ast_unlock_context(c);
161 return ip ? -1 /* success */ : 0;
164 /*! \brief moves to the n-th word in the string, or empty string if none */
165 static const char *skip_words(const char *p, int n)
168 for (;n && *p; p++) {
169 if (isblank(*p) /* XXX order is important */ && !in_blank) {
170 n--; /* one word is gone */
172 } else if (/* !is_blank(*p), we know already, && */ in_blank) {
179 /*! \brief match the first 'len' chars of word. len==0 always succeeds */
180 static int partial_match(const char *s, const char *word, int len)
182 return (len == 0 || !strncmp(s, word, len));
185 /*! \brief split extension\@context in two parts, return -1 on error.
186 * The return string is malloc'ed and pointed by *ext
188 static int split_ec(const char *src, char **ext, char ** const ctx)
190 char *c, *e = ast_strdup(src); /* now src is not used anymore */
193 return -1; /* malloc error */
194 /* now, parse values from 'exten@context' */
197 if (c == NULL) /* no context part */
198 *ctx = ""; /* it is not overwritten, anyways */
199 else { /* found context, check for duplicity ... */
202 if (strchr(c, '@')) { /* two @, not allowed */
210 /* _X_ is the string we need to complete */
211 static char *complete_context_remove_include(const char *line, const char *word,
216 int len = strlen(word); /* how many bytes to match */
217 struct ast_context *c = NULL;
219 if (pos == 3) { /* "dialplan remove include _X_" */
220 if (ast_lock_contexts()) {
221 ast_log(LOG_ERROR, "Failed to lock context list\n");
224 /* walk contexts and their includes, return the n-th match */
225 while (!res && (c = ast_walk_contexts(c))) {
226 struct ast_include *i = NULL;
228 if (ast_lock_context(c)) /* error ? skip this one */
231 while ( !res && (i = ast_walk_context_includes(c, i)) ) {
232 const char *i_name = ast_get_include_name(i);
233 struct ast_context *nc = NULL;
234 int already_served = 0;
236 if (!partial_match(i_name, word, len))
237 continue; /* not matched */
239 /* check if this include is already served or not */
241 /* go through all contexts again till we reach actual
242 * context or already_served = 1
244 while ( (nc = ast_walk_contexts(nc)) && nc != c && !already_served)
245 already_served = lookup_ci(nc, i_name);
247 if (!already_served && ++which > state)
248 res = strdup(i_name);
250 ast_unlock_context(c);
253 ast_unlock_contexts();
255 } else if (pos == 4) { /* "dialplan remove include CTX _X_" */
257 * complete as 'from', but only if previous context is really
260 char *context, *dupline;
261 const char *s = skip_words(line, 3); /* skip 'dialplan' 'remove' 'include' */
265 context = dupline = strdup(s);
267 ast_log(LOG_ERROR, "Out of free memory\n");
270 strsep(&dupline, " ");
272 if (ast_lock_contexts()) {
273 ast_log(LOG_ERROR, "Failed to lock contexts list\n");
278 /* go through all contexts and check if is included ... */
279 while (!res && (c = ast_walk_contexts(c)))
280 if (lookup_ci(c, context)) /* context is really included, complete "from" command */
281 res = strdup("from");
282 ast_unlock_contexts();
284 ast_log(LOG_WARNING, "%s not included anywhere\n", context);
287 } else if (pos == 5) { /* "dialplan remove include CTX from _X_" */
289 * Context from which we removing include ...
291 char *context, *dupline, *from;
292 const char *s = skip_words(line, 3); /* skip 'dialplan' 'remove' 'include' */
293 context = dupline = strdup(s);
295 ast_log(LOG_ERROR, "Out of free memory\n");
299 strsep(&dupline, " "); /* skip context */
301 /* fourth word must be 'from' */
302 from = strsep(&dupline, " ");
303 if (!from || strcmp(from, "from")) {
308 if (ast_lock_contexts()) {
309 ast_log(LOG_ERROR, "Failed to lock context list\n");
314 /* walk through all contexts ... */
316 while ( !res && (c = ast_walk_contexts(c))) {
317 const char *c_name = ast_get_context_name(c);
318 if (!partial_match(c_name, word, len)) /* not a good target */
320 /* walk through all includes and check if it is our context */
321 if (lookup_ci(c, context) && ++which > state)
322 res = strdup(c_name);
324 ast_unlock_contexts();
333 * REMOVE EXTENSION command stuff
335 static int handle_context_remove_extension(int fd, int argc, char *argv[])
337 int removing_priority = 0;
338 char *exten, *context;
339 int ret = RESULT_FAILURE;
341 if (argc != 5 && argc != 4) return RESULT_SHOWUSAGE;
344 * Priority input checking ...
349 /* check for digits in whole parameter for right priority ...
350 * why? because atoi (strtol) returns 0 if any characters in
351 * string and whole extension will be removed, it's not good
353 if (!strcmp("hint", c))
354 removing_priority = PRIORITY_HINT;
356 while (*c && isdigit(*c))
358 if (*c) { /* non-digit in string */
359 ast_cli(fd, "Invalid priority '%s'\n", argv[4]);
360 return RESULT_FAILURE;
362 removing_priority = atoi(argv[4]);
365 if (removing_priority == 0) {
366 ast_cli(fd, "If you want to remove whole extension, please " \
367 "omit priority argument\n");
368 return RESULT_FAILURE;
372 /* XXX original overwrote argv[3] */
374 * Format exten@context checking ...
376 if (split_ec(argv[3], &exten, &context))
377 return RESULT_FAILURE; /* XXX malloc failure */
378 if ((!strlen(exten)) || (!(strlen(context)))) {
379 ast_cli(fd, "Missing extension or context name in third argument '%s'\n",
382 return RESULT_FAILURE;
385 if (!ast_context_remove_extension(context, exten, removing_priority, registrar)) {
386 if (!removing_priority)
387 ast_cli(fd, "Whole extension %s@%s removed\n",
390 ast_cli(fd, "Extension %s@%s with priority %d removed\n",
391 exten, context, removing_priority);
393 ret = RESULT_SUCCESS;
395 ast_cli(fd, "Failed to remove extension %s@%s\n", exten, context);
396 ret = RESULT_FAILURE;
402 #define BROKEN_READLINE 1
404 #ifdef BROKEN_READLINE
406 * There is one funny thing, when you have word like 300@ and you hit
407 * <tab>, you arguments will like as your word is '300 ', so it '@'
408 * characters acts sometimes as word delimiter and sometimes as a part
411 * This fix function, allocates new word variable and store here every
412 * time xxx@yyy always as one word and correct pos is set too
414 * It's ugly, I know, but I'm waiting for Mark suggestion if upper is
417 static int fix_complete_args(const char *line, char **word, int *pos)
419 char *_line, *_strsep_line, *_previous_word = NULL, *_word = NULL;
422 _line = strdup(line);
424 _strsep_line = _line;
425 while (_strsep_line) {
426 _previous_word = _word;
427 _word = strsep(&_strsep_line, " ");
429 if (_word && strlen(_word)) words++;
433 if (_word || _previous_word) {
435 if (!strlen(_word)) words++;
436 *word = strdup(_word);
438 *word = strdup(_previous_word);
447 #endif /* BROKEN_READLINE */
449 static char *complete_context_remove_extension(const char *line, const char *word, int pos,
455 #ifdef BROKEN_READLINE
458 * Fix arguments, *word is a new allocated structure, REMEMBER to
459 * free *word when you want to return from this function ...
461 if (fix_complete_args(line, &word2, &pos)) {
462 ast_log(LOG_ERROR, "Out of free memory\n");
468 if (pos == 3) { /* 'dialplan remove extension _X_' (exten@context ... */
469 struct ast_context *c = NULL;
470 char *context = NULL, *exten = NULL;
471 int le = 0; /* length of extension */
472 int lc = 0; /* length of context */
474 lc = split_ec(word, &exten, &context);
475 #ifdef BROKEN_READLINE
481 lc = strlen(context);
483 if (ast_lock_contexts()) {
484 ast_log(LOG_ERROR, "Failed to lock context list\n");
488 /* find our context ... */
489 while ( (c = ast_walk_contexts(c)) ) { /* match our context if any */
490 struct ast_exten *e = NULL;
492 if (!partial_match(ast_get_context_name(c), context, lc))
493 continue; /* context not matched */
494 while ( (e = ast_walk_context_extensions(c, e)) ) { /* try to complete extensions ... */
495 if ( partial_match(ast_get_extension_name(e), exten, le) && ++which > state) { /* n-th match */
496 /* If there is an extension then return exten@context. XXX otherwise ? */
498 asprintf(&ret, "%s@%s", ast_get_extension_name(e), ast_get_context_name(c));
502 if (e) /* got a match */
506 ast_unlock_contexts();
510 } else if (pos == 4) { /* 'dialplan remove extension EXT _X_' (priority) */
511 char *exten = NULL, *context, *p;
512 struct ast_context *c;
514 const char *s = skip_words(line, 3); /* skip 'dialplan' 'remove' 'extension' */
515 int i = split_ec(s, &exten, &context); /* parse ext@context */
519 if ( (p = strchr(exten, ' ')) ) /* remove space after extension */
521 if ( (p = strchr(context, ' ')) ) /* remove space after context */
524 lc = strlen(context);
526 if (le == 0 || lc == 0)
529 if (ast_lock_contexts()) {
530 ast_log(LOG_ERROR, "Failed to lock context list\n");
536 while ( (c = ast_walk_contexts(c)) ) {
537 /* XXX locking on c ? */
539 if (strcmp(ast_get_context_name(c), context) != 0)
541 /* got it, we must match here */
543 while ( (e = ast_walk_context_extensions(c, e)) ) {
544 struct ast_exten *priority;
547 if (strcmp(ast_get_extension_name(e), exten) != 0)
551 while ( !ret && (priority = ast_walk_extension_priorities(e, priority)) ) {
552 snprintf(buffer, sizeof(buffer), "%u", ast_get_extension_priority(priority));
553 if (partial_match(buffer, word, len) && ++which > state) /* n-th match */
554 ret = strdup(buffer);
560 ast_unlock_contexts();
564 #ifdef BROKEN_READLINE
572 * Include context ...
574 static int handle_context_add_include(int fd, int argc, char *argv[])
576 if (argc != 6) /* dialplan add include CTX in CTX */
577 return RESULT_SHOWUSAGE;
579 /* fifth arg must be 'into' ... */
580 if (strcmp(argv[4], "into"))
581 return RESULT_SHOWUSAGE;
583 if (ast_context_add_include(argv[5], argv[3], registrar)) {
586 ast_cli(fd, "Out of memory for context addition\n");
590 ast_cli(fd, "Failed to lock context(s) list, please try again later\n");
594 ast_cli(fd, "Context '%s' already included in '%s' context\n",
600 ast_cli(fd, "There is no existence of context '%s'\n",
601 errno == ENOENT ? argv[5] : argv[3]);
605 ast_cli(fd, "Failed to include '%s' in '%s' context\n",
609 return RESULT_FAILURE;
612 /* show some info ... */
613 ast_cli(fd, "Context '%s' included in '%s' context\n",
616 return RESULT_SUCCESS;
619 static char *complete_context_add_include(const char *line, const char *word, int pos,
622 struct ast_context *c;
625 int len = strlen(word);
627 if (pos == 3) { /* 'dialplan add include _X_' (context) ... */
628 if (ast_lock_contexts()) {
629 ast_log(LOG_ERROR, "Failed to lock context list\n");
632 for (c = NULL; !ret && (c = ast_walk_contexts(c)); )
633 if (partial_match(ast_get_context_name(c), word, len) && ++which > state)
634 ret = strdup(ast_get_context_name(c));
635 ast_unlock_contexts();
637 } else if (pos == 4) { /* dialplan add include CTX _X_ */
638 /* complete as 'into' if context exists or we are unable to check */
639 char *context, *dupline;
640 struct ast_context *c;
641 const char *s = skip_words(line, 3); /* should not fail */
643 if (state != 0) /* only once */
646 /* parse context from line ... */
647 context = dupline = strdup(s);
649 ast_log(LOG_ERROR, "Out of free memory\n");
650 return strdup("into");
652 strsep(&dupline, " ");
654 /* check for context existence ... */
655 if (ast_lock_contexts()) {
656 ast_log(LOG_ERROR, "Failed to lock context list\n");
657 /* our fault, we can't check, so complete 'into' ... */
658 ret = strdup("into");
660 for (c = NULL; !ret && (c = ast_walk_contexts(c)); )
661 if (!strcmp(context, ast_get_context_name(c)))
662 ret = strdup("into"); /* found */
663 ast_unlock_contexts();
667 } else if (pos == 5) { /* 'dialplan add include CTX into _X_' (dst context) */
668 char *context, *dupline, *into;
669 const char *s = skip_words(line, 3); /* should not fail */
670 context = dupline = strdup(s);
672 ast_log(LOG_ERROR, "Out of free memory\n");
675 strsep(&dupline, " "); /* skip context */
676 into = strsep(&dupline, " ");
677 /* error if missing context or fifth word is not 'into' */
678 if (!strlen(context) || strcmp(into, "into")) {
679 ast_log(LOG_ERROR, "bad context %s or missing into %s\n",
684 if (ast_lock_contexts()) {
685 ast_log(LOG_ERROR, "Failed to lock context list\n");
689 for (c = NULL; (c = ast_walk_contexts(c)); )
690 if (!strcmp(context, ast_get_context_name(c)))
692 if (c) { /* first context exists, go on... */
693 /* go through all contexts ... */
694 for (c = NULL; !ret && (c = ast_walk_contexts(c)); ) {
695 if (!strcmp(context, ast_get_context_name(c)))
696 continue; /* skip ourselves */
697 if (partial_match(ast_get_context_name(c), word, len) &&
698 !lookup_ci(c, context) /* not included yet */ &&
700 ret = strdup(ast_get_context_name(c));
703 ast_log(LOG_ERROR, "context %s not found\n", context);
705 ast_unlock_contexts();
715 * \brief 'save dialplan' CLI command implementation functions ...
717 static int handle_save_dialplan(int fd, int argc, char *argv[])
720 struct ast_context *c;
721 struct ast_config *cfg;
722 struct ast_variable *v;
723 int incomplete = 0; /* incomplete config write? */
726 const char *base, *slash, *file;
728 if (! (static_config && !write_protect_config)) {
730 "I can't save dialplan now, see '%s' example file.\n",
732 return RESULT_FAILURE;
735 if (argc != 2 && argc != 3)
736 return RESULT_SHOWUSAGE;
738 if (ast_mutex_lock(&save_dialplan_lock)) {
740 "Failed to lock dialplan saving (another proccess saving?)\n");
741 return RESULT_FAILURE;
743 /* XXX the code here is quite loose, a pathname with .conf in it
744 * is assumed to be a complete pathname
746 if (argc == 3) { /* have config path. Look for *.conf */
748 if (!strstr(argv[2], ".conf")) { /*no, this is assumed to be a pathname */
749 /* if filename ends with '/', do not add one */
750 slash = (*(argv[2] + strlen(argv[2]) -1) == '/') ? "/" : "";
751 file = config; /* default: 'extensions.conf' */
752 } else { /* yes, complete file name */
757 /* no config file, default one */
758 base = ast_config_AST_CONFIG_DIR;
762 snprintf(filename, sizeof(filename), "%s%s%s", base, slash, config);
764 cfg = ast_config_load("extensions.conf");
766 /* try to lock contexts list */
767 if (ast_lock_contexts()) {
768 ast_cli(fd, "Failed to lock contexts list\n");
769 ast_mutex_unlock(&save_dialplan_lock);
770 ast_config_destroy(cfg);
771 return RESULT_FAILURE;
774 /* create new file ... */
775 if (!(output = fopen(filename, "wt"))) {
776 ast_cli(fd, "Failed to create file '%s'\n",
778 ast_unlock_contexts();
779 ast_mutex_unlock(&save_dialplan_lock);
780 ast_config_destroy(cfg);
781 return RESULT_FAILURE;
784 /* fireout general info */
785 fprintf(output, "[general]\nstatic=%s\nwriteprotect=%s\nautofallthrough=%s\nclearglobalvars=%s\npriorityjumping=%s\n\n",
786 static_config ? "yes" : "no",
787 write_protect_config ? "yes" : "no",
788 autofallthrough_config ? "yes" : "no",
789 clearglobalvars_config ? "yes" : "no",
790 ast_true(ast_variable_retrieve(cfg, "general", "priorityjumping")) ? "yes" : "no");
792 if ((v = ast_variable_browse(cfg, "globals"))) {
793 fprintf(output, "[globals]\n");
795 fprintf(output, "%s => %s\n", v->name, v->value);
798 fprintf(output, "\n");
801 ast_config_destroy(cfg);
803 #define PUT_CTX_HDR do { \
804 if (!context_header_written) { \
805 fprintf(output, "[%s]\n", ast_get_context_name(c)); \
806 context_header_written = 1; \
810 /* walk all contexts */
811 for (c = NULL; (c = ast_walk_contexts(c)); ) {
812 int context_header_written = 0;
813 struct ast_exten *e, *last_written_e = NULL;
814 struct ast_include *i;
815 struct ast_ignorepat *ip;
818 /* try to lock context and fireout all info */
819 if (ast_lock_context(c)) { /* lock failure */
823 /* registered by this module? */
824 /* XXX do we need this ? */
825 if (!strcmp(ast_get_context_registrar(c), registrar)) {
826 fprintf(output, "[%s]\n", ast_get_context_name(c));
827 context_header_written = 1;
830 /* walk extensions ... */
831 for (e = NULL; (e = ast_walk_context_extensions(c, e)); ) {
832 struct ast_exten *p = NULL;
834 /* fireout priorities */
835 while ( (p = ast_walk_extension_priorities(e, p)) ) {
836 if (strcmp(ast_get_extension_registrar(p), registrar) != 0) /* not this source */
839 /* make empty line between different extensions */
840 if (last_written_e != NULL &&
841 strcmp(ast_get_extension_name(last_written_e),
842 ast_get_extension_name(p)))
843 fprintf(output, "\n");
848 if (ast_get_extension_priority(p)==PRIORITY_HINT) { /* easy */
849 fprintf(output, "exten => %s,hint,%s\n",
850 ast_get_extension_name(p),
851 ast_get_extension_app(p));
852 } else { /* copy and replace '|' with ',' */
853 const char *sep, *cid;
856 const char *el = ast_get_extension_label(p);
859 tempdata = ast_strdupa(ast_get_extension_app_data(p));
861 for (s = tempdata; *s; s++) {
866 if (ast_get_extension_matchcid(p)) {
868 cid = ast_get_extension_cidmatch(p);
872 if (el && (snprintf(label, 127, "(%s)", el) != (strlen(el) + 2)))
873 incomplete = 1; /* error encountered or label > 125 chars */
875 fprintf(output, "exten => %s%s%s,%d%s,%s(%s)\n",
876 ast_get_extension_name(p), sep, cid,
877 ast_get_extension_priority(p), label,
878 ast_get_extension_app(p), tempdata);
883 /* written any extensions? ok, write space between exten & inc */
885 fprintf(output, "\n");
887 /* walk through includes */
888 for (i = NULL; (i = ast_walk_context_includes(c, i)) ; ) {
889 if (strcmp(ast_get_include_registrar(i), registrar) != 0)
890 continue; /* not mine */
892 fprintf(output, "include => %s\n", ast_get_include_name(i));
894 if (ast_walk_context_includes(c, NULL))
895 fprintf(output, "\n");
897 /* walk through switches */
898 for (sw = NULL; (sw = ast_walk_context_switches(c, sw)) ; ) {
899 if (strcmp(ast_get_switch_registrar(sw), registrar) != 0)
900 continue; /* not mine */
902 fprintf(output, "switch => %s/%s\n",
903 ast_get_switch_name(sw), ast_get_switch_data(sw));
906 if (ast_walk_context_switches(c, NULL))
907 fprintf(output, "\n");
909 /* fireout ignorepats ... */
910 for (ip = NULL; (ip = ast_walk_context_ignorepats(c, ip)); ) {
911 if (strcmp(ast_get_ignorepat_registrar(ip), registrar) != 0)
912 continue; /* not mine */
914 fprintf(output, "ignorepat => %s\n",
915 ast_get_ignorepat_name(ip));
918 ast_unlock_context(c);
921 ast_unlock_contexts();
922 ast_mutex_unlock(&save_dialplan_lock);
926 ast_cli(fd, "Saved dialplan is incomplete\n");
927 return RESULT_FAILURE;
930 ast_cli(fd, "Dialplan successfully saved into '%s'\n",
932 return RESULT_SUCCESS;
936 * \brief ADD EXTENSION command stuff
938 static int handle_context_add_extension(int fd, int argc, char *argv[])
943 char *cidmatch, *app, *app_data;
946 /* check for arguments at first */
947 if (argc != 6 && argc != 7)
948 return RESULT_SHOWUSAGE;
949 if (strcmp(argv[3], "into"))
950 return RESULT_SHOWUSAGE;
951 if (argc == 7) if (strcmp(argv[6], "replace")) return RESULT_SHOWUSAGE;
953 /* XXX overwrite argv[3] */
954 whole_exten = argv[3];
955 exten = strsep(&whole_exten,",");
956 if (strchr(exten, '/')) {
958 strsep(&cidmatch,"/");
962 prior = strsep(&whole_exten,",");
964 if (!strcmp(prior, "hint")) {
965 iprior = PRIORITY_HINT;
967 if (sscanf(prior, "%d", &iprior) != 1) {
968 ast_cli(fd, "'%s' is not a valid priority\n", prior);
974 if (app && (start = strchr(app, '(')) && (end = strrchr(app, ')'))) {
975 *start = *end = '\0';
976 app_data = start + 1;
977 ast_process_quotes_and_slashes(app_data, ',', '|');
980 app_data = strchr(app, ',');
989 if (!exten || !prior || !app || (!app_data && iprior != PRIORITY_HINT))
990 return RESULT_SHOWUSAGE;
994 if (ast_add_extension(argv[5], argc == 6 ? 1 : 0, exten, iprior, NULL, cidmatch, app,
995 (void *)strdup(app_data), free, registrar)) {
998 ast_cli(fd, "Out of free memory\n");
1002 ast_cli(fd, "Failed to lock context(s) list, please try again later\n");
1006 ast_cli(fd, "No existence of '%s' context\n", argv[5]);
1010 ast_cli(fd, "Extension %s@%s with priority %s already exists\n",
1011 exten, argv[5], prior);
1015 ast_cli(fd, "Failed to add '%s,%s,%s,%s' extension into '%s' context\n",
1016 exten, prior, app, app_data, argv[5]);
1019 return RESULT_FAILURE;
1023 ast_cli(fd, "Extension %s@%s (%s) replace by '%s,%s,%s,%s'\n",
1024 exten, argv[5], prior, exten, prior, app, app_data);
1026 ast_cli(fd, "Extension '%s,%s,%s,%s' added into '%s' context\n",
1027 exten, prior, app, app_data, argv[5]);
1029 return RESULT_SUCCESS;
1032 /*! dialplan add extension 6123,1,Dial,IAX/212.71.138.13/6123 into local */
1033 static char *complete_context_add_extension(const char *line, const char *word, int pos, int state)
1037 if (pos == 4) { /* complete 'into' word ... */
1038 return (state == 0) ? strdup("into") : NULL;
1039 } else if (pos == 5) { /* complete context */
1040 struct ast_context *c = NULL;
1041 int len = strlen(word);
1044 /* try to lock contexts list ... */
1045 if (ast_lock_contexts()) {
1046 ast_log(LOG_WARNING, "Failed to lock contexts list\n");
1050 /* walk through all contexts */
1051 while ( !res && (c = ast_walk_contexts(c)) )
1052 if (partial_match(ast_get_context_name(c), word, len) && ++which > state)
1053 res = strdup(ast_get_context_name(c));
1054 ast_unlock_contexts();
1056 } else if (pos == 6) {
1057 return state == 0 ? strdup("replace") : NULL;
1063 * IGNOREPAT CLI stuff
1065 static int handle_context_add_ignorepat(int fd, int argc, char *argv[])
1068 return RESULT_SHOWUSAGE;
1069 if (strcmp(argv[4], "into"))
1070 return RESULT_SHOWUSAGE;
1072 if (ast_context_add_ignorepat(argv[5], argv[3], registrar)) {
1075 ast_cli(fd, "Out of free memory\n");
1079 ast_cli(fd, "There is no existence of '%s' context\n", argv[5]);
1083 ast_cli(fd, "Ignore pattern '%s' already included in '%s' context\n",
1088 ast_cli(fd, "Failed to lock context(s) list, please, try again later\n");
1092 ast_cli(fd, "Failed to add ingore pattern '%s' into '%s' context\n",
1096 return RESULT_FAILURE;
1099 ast_cli(fd, "Ignore pattern '%s' added into '%s' context\n",
1101 return RESULT_SUCCESS;
1104 static char *complete_context_add_ignorepat(const char *line, const char *word,
1108 return state == 0 ? strdup("into") : NULL;
1109 else if (pos == 5) {
1110 struct ast_context *c;
1112 char *dupline, *ignorepat = NULL;
1115 int len = strlen(word);
1117 /* XXX skip first three words 'dialplan' 'add' 'ignorepat' */
1118 s = skip_words(line, 3);
1121 dupline = strdup(s);
1123 ast_log(LOG_ERROR, "Malloc failure\n");
1126 ignorepat = strsep(&dupline, " ");
1128 if (ast_lock_contexts()) {
1129 ast_log(LOG_ERROR, "Failed to lock contexts list\n");
1133 for (c = NULL; !ret && (c = ast_walk_contexts(c));) {
1136 if (!partial_match(ast_get_context_name(c), word, len))
1137 continue; /* not mine */
1138 if (ignorepat) /* there must be one, right ? */
1139 found = lookup_c_ip(c, ignorepat);
1140 if (!found && ++which > state)
1141 ret = strdup(ast_get_context_name(c));
1146 ast_unlock_contexts();
1153 static int handle_context_remove_ignorepat(int fd, int argc, char *argv[])
1156 return RESULT_SHOWUSAGE;
1157 if (strcmp(argv[4], "from"))
1158 return RESULT_SHOWUSAGE;
1160 if (ast_context_remove_ignorepat(argv[5], argv[3], registrar)) {
1163 ast_cli(fd, "Failed to lock context(s) list, please try again later\n");
1167 ast_cli(fd, "There is no existence of '%s' context\n", argv[5]);
1171 ast_cli(fd, "There is no existence of '%s' ignore pattern in '%s' context\n",
1176 ast_cli(fd, "Failed to remove ignore pattern '%s' from '%s' context\n", argv[3], argv[5]);
1179 return RESULT_FAILURE;
1182 ast_cli(fd, "Ignore pattern '%s' removed from '%s' context\n",
1184 return RESULT_SUCCESS;
1187 static char *complete_context_remove_ignorepat(const char *line, const char *word,
1190 struct ast_context *c;
1195 int len = strlen(word);
1196 if (ast_lock_contexts()) {
1197 ast_log(LOG_WARNING, "Failed to lock contexts list\n");
1201 for (c = NULL; !ret && (c = ast_walk_contexts(c));) {
1202 struct ast_ignorepat *ip;
1204 if (ast_lock_context(c)) /* error, skip it */
1207 for (ip = NULL; !ret && (ip = ast_walk_context_ignorepats(c, ip));) {
1208 if (partial_match(ast_get_ignorepat_name(ip), word, len) && ++which > state) {
1210 struct ast_context *cw = NULL;
1212 while ( (cw = ast_walk_contexts(cw)) && cw != c && !found) {
1213 /* XXX do i stop on c, or skip it ? */
1214 found = lookup_c_ip(cw, ast_get_ignorepat_name(ip));
1217 ret = strdup(ast_get_ignorepat_name(ip));
1220 ast_unlock_context(c);
1222 ast_unlock_contexts();
1224 } else if (pos == 4) {
1225 return state == 0 ? strdup("from") : NULL;
1226 } else if (pos == 5) { /* XXX check this */
1227 char *dupline, *duplinet, *ignorepat;
1228 int len = strlen(word);
1230 dupline = strdup(line);
1232 ast_log(LOG_WARNING, "Out of free memory\n");
1237 strsep(&duplinet, " ");
1238 strsep(&duplinet, " ");
1239 ignorepat = strsep(&duplinet, " ");
1246 if (ast_lock_contexts()) {
1247 ast_log(LOG_WARNING, "Failed to lock contexts list\n");
1252 for (c = NULL; !ret && (c = ast_walk_contexts(c)); ) {
1253 if (ast_lock_context(c)) /* fail, skip it */
1255 if (!partial_match(ast_get_context_name(c), word, len))
1257 if (lookup_c_ip(c, ignorepat) && ++which > state)
1258 ret = strdup(ast_get_context_name(c));
1259 ast_unlock_context(c);
1261 ast_unlock_contexts();
1269 static int pbx_load_module(void);
1271 static int handle_reload_extensions(int fd, int argc, char *argv[])
1274 return RESULT_SHOWUSAGE;
1276 return RESULT_SUCCESS;
1280 * CLI entries for commands provided by this module
1282 static struct ast_cli_entry cli_pbx_config[] = {
1283 { { "dialplan", "add", "extension", NULL },
1284 handle_context_add_extension, "Add new extension into context",
1285 context_add_extension_help, complete_context_add_extension },
1287 { { "dialplan", "remove", "extension", NULL },
1288 handle_context_remove_extension, "Remove a specified extension",
1289 context_remove_extension_help, complete_context_remove_extension },
1291 { { "dialplan", "add", "ignorepat", NULL },
1292 handle_context_add_ignorepat, "Add new ignore pattern",
1293 context_add_ignorepat_help, complete_context_add_ignorepat },
1295 { { "dialplan", "remove", "ignorepat", NULL },
1296 handle_context_remove_ignorepat, "Remove ignore pattern from context",
1297 context_remove_ignorepat_help, complete_context_remove_ignorepat },
1299 { { "dialplan", "add", "include", NULL },
1300 handle_context_add_include, "Include context in other context",
1301 context_add_include_help, complete_context_add_include },
1303 { { "dialplan", "remove", "include", NULL },
1304 handle_context_remove_include, "Remove a specified include from context",
1305 context_remove_include_help, complete_context_remove_include },
1307 { { "dialplan", "reload", NULL },
1308 handle_reload_extensions, "Reload extensions and *only* extensions",
1309 reload_extensions_help },
1313 static struct ast_cli_entry cli_dialplan_save = {
1314 { "dialplan", "save", NULL },
1315 handle_save_dialplan, "Save dialplan",
1316 save_dialplan_help };
1319 * Standard module functions ...
1321 static int unload_module(void)
1323 if (static_config && !write_protect_config)
1324 ast_cli_unregister(&cli_dialplan_save);
1325 ast_cli_unregister_multiple(cli_pbx_config, sizeof(cli_pbx_config) / sizeof(struct ast_cli_entry));
1326 ast_context_destroy(NULL, registrar);
1330 static int pbx_load_config(const char *config_file)
1332 struct ast_config *cfg;
1335 char realvalue[256];
1337 struct ast_context *con;
1338 struct ast_variable *v;
1341 cfg = ast_config_load(config_file);
1345 /* Use existing config to populate the PBX table */
1346 static_config = ast_true(ast_variable_retrieve(cfg, "general", "static"));
1347 write_protect_config = ast_true(ast_variable_retrieve(cfg, "general", "writeprotect"));
1348 autofallthrough_config = ast_true(ast_variable_retrieve(cfg, "general", "autofallthrough"));
1349 clearglobalvars_config = ast_true(ast_variable_retrieve(cfg, "general", "clearglobalvars"));
1350 ast_set2_flag(&ast_options, ast_true(ast_variable_retrieve(cfg, "general", "priorityjumping")), AST_OPT_FLAG_PRIORITY_JUMPING);
1352 if ((cxt = ast_variable_retrieve(cfg, "general", "userscontext")))
1353 ast_copy_string(userscontext, cxt, sizeof(userscontext));
1355 ast_copy_string(userscontext, "default", sizeof(userscontext));
1357 for (v = ast_variable_browse(cfg, "globals"); v; v = v->next) {
1358 memset(realvalue, 0, sizeof(realvalue));
1359 pbx_substitute_variables_helper(NULL, v->value, realvalue, sizeof(realvalue) - 1);
1360 pbx_builtin_setvar_helper(NULL, v->name, realvalue);
1362 for (cxt = NULL; (cxt = ast_category_browse(cfg, cxt)); ) {
1363 /* All categories but "general" or "globals" are considered contexts */
1364 if (!strcasecmp(cxt, "general") || !strcasecmp(cxt, "globals"))
1366 con=ast_context_find_or_create(&local_contexts,cxt, registrar);
1370 for (v = ast_variable_browse(cfg, cxt); v; v = v->next) {
1371 if (!strcasecmp(v->name, "exten")) {
1372 char *tc = ast_strdup(v->value);
1375 char realext[256]="";
1376 char *plus, *firstp, *firstc;
1377 char *pri, *appl, *data, *cidmatch;
1379 char *ext = strsep(&stringp, ",");
1382 pbx_substitute_variables_helper(NULL, ext, realext, sizeof(realext) - 1);
1383 cidmatch = strchr(realext, '/');
1386 ast_shrink_phone_number(cidmatch);
1388 pri = strsep(&stringp, ",");
1391 label = strchr(pri, '(');
1394 end = strchr(label, ')');
1398 ast_log(LOG_WARNING, "Label missing trailing ')' at line %d\n", v->lineno);
1400 plus = strchr(pri, '+');
1403 if (!strcmp(pri,"hint"))
1405 else if (!strcmp(pri, "next") || !strcmp(pri, "n")) {
1409 ast_log(LOG_WARNING, "Can't use 'next' priority on the first entry!\n");
1410 } else if (!strcmp(pri, "same") || !strcmp(pri, "s")) {
1414 ast_log(LOG_WARNING, "Can't use 'same' priority on the first entry!\n");
1415 } else if (sscanf(pri, "%d", &ipri) != 1 &&
1416 (ipri = ast_findlabel_extension2(NULL, con, realext, pri, cidmatch)) < 1) {
1417 ast_log(LOG_WARNING, "Invalid priority/label '%s' at line %d\n", pri, v->lineno);
1420 appl = S_OR(stringp, "");
1421 /* Find the first occurrence of either '(' or ',' */
1422 firstc = strchr(appl, ',');
1423 firstp = strchr(appl, '(');
1424 if (firstc && (!firstp || firstc < firstp)) {
1425 /* comma found, no parenthesis */
1426 /* or both found, but comma found first */
1427 appl = strsep(&stringp, ",");
1429 } else if (!firstc && !firstp) {
1433 /* Final remaining case is parenthesis found first */
1434 appl = strsep(&stringp, "(");
1436 end = strrchr(data, ')');
1437 if ((end = strrchr(data, ')'))) {
1440 ast_log(LOG_WARNING, "No closing parenthesis found? '%s(%s'\n", appl, data);
1442 ast_process_quotes_and_slashes(data, ',', '|');
1447 appl = ast_skip_blanks(appl);
1452 if (!ast_opt_dont_warn && !strcmp(realext, "_."))
1453 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);
1454 if (ast_add_extension2(con, 0, realext, ipri, label, cidmatch, appl, strdup(data), ast_free, registrar)) {
1455 ast_log(LOG_WARNING, "Unable to register extension at line %d\n", v->lineno);
1460 } else if (!strcasecmp(v->name, "include")) {
1461 memset(realvalue, 0, sizeof(realvalue));
1462 pbx_substitute_variables_helper(NULL, v->value, realvalue, sizeof(realvalue) - 1);
1463 if (ast_context_add_include2(con, realvalue, registrar))
1464 ast_log(LOG_WARNING, "Unable to include context '%s' in context '%s'\n", v->value, cxt);
1465 } else if (!strcasecmp(v->name, "ignorepat")) {
1466 memset(realvalue, 0, sizeof(realvalue));
1467 pbx_substitute_variables_helper(NULL, v->value, realvalue, sizeof(realvalue) - 1);
1468 if (ast_context_add_ignorepat2(con, realvalue, registrar))
1469 ast_log(LOG_WARNING, "Unable to include ignorepat '%s' in context '%s'\n", v->value, cxt);
1470 } else if (!strcasecmp(v->name, "switch") || !strcasecmp(v->name, "lswitch") || !strcasecmp(v->name, "eswitch")) {
1471 char *stringp= realvalue;
1474 memset(realvalue, 0, sizeof(realvalue));
1475 if (!strcasecmp(v->name, "switch"))
1476 pbx_substitute_variables_helper(NULL, v->value, realvalue, sizeof(realvalue) - 1);
1478 ast_copy_string(realvalue, v->value, sizeof(realvalue));
1479 appl = strsep(&stringp, "/");
1480 data = strsep(&stringp, ""); /* XXX what for ? */
1483 if (ast_context_add_switch2(con, appl, data, !strcasecmp(v->name, "eswitch"), registrar))
1484 ast_log(LOG_WARNING, "Unable to include switch '%s' in context '%s'\n", v->value, cxt);
1486 ast_log(LOG_WARNING, "==!!== Unknown directive: %s at line %d -- IGNORING!!!\n", v->name, v->lineno);
1490 ast_config_destroy(cfg);
1494 static void append_interface(char *iface, int maxlen, char *add)
1496 int len = strlen(iface);
1497 if (strlen(add) + len < maxlen - 2) {
1498 if (strlen(iface)) {
1500 strcpy(iface + len + 1, add);
1506 static void pbx_load_users(void)
1508 struct ast_config *cfg;
1510 const char *zapchan;
1511 const char *hasexten;
1518 int start, finish, x;
1519 struct ast_context *con;
1521 cfg = ast_config_load("users.conf");
1524 con = ast_context_find_or_create(&local_contexts, userscontext, registrar);
1528 for (cat = ast_category_browse(cfg, NULL); cat ; cat = ast_category_browse(cfg, cat)) {
1529 if (!strcasecmp(cat, "general"))
1532 len = sizeof(iface);
1533 if (ast_true(ast_config_option(cfg, cat, "hassip"))) {
1534 snprintf(tmp, sizeof(tmp), "SIP/%s", cat);
1535 append_interface(iface, sizeof(iface), tmp);
1537 if (ast_true(ast_config_option(cfg, cat, "hasiax"))) {
1538 snprintf(tmp, sizeof(tmp), "IAX2/%s", cat);
1539 append_interface(iface, sizeof(iface), tmp);
1541 if (ast_true(ast_config_option(cfg, cat, "hash323"))) {
1542 snprintf(tmp, sizeof(tmp), "H323/%s", cat);
1543 append_interface(iface, sizeof(iface), tmp);
1545 hasexten = ast_config_option(cfg, cat, "hasexten");
1546 if (hasexten && !ast_true(hasexten))
1548 hasvoicemail = ast_true(ast_config_option(cfg, cat, "hasvoicemail"));
1549 zapchan = ast_variable_retrieve(cfg, cat, "zapchan");
1551 zapchan = ast_variable_retrieve(cfg, "general", "zapchan");
1552 if (!ast_strlen_zero(zapchan)) {
1553 ast_copy_string(zapcopy, zapchan, sizeof(zapcopy));
1555 chan = strsep(&c, ",");
1557 if (sscanf(chan, "%d-%d", &start, &finish) == 2) {
1559 } else if (sscanf(chan, "%d", &start)) {
1563 start = 0; finish = 0;
1565 if (finish < start) {
1570 for (x = start; x <= finish; x++) {
1571 snprintf(tmp, sizeof(tmp), "Zap/%d", x);
1572 append_interface(iface, sizeof(iface), tmp);
1574 chan = strsep(&c, ",");
1577 if (!ast_strlen_zero(iface)) {
1579 ast_add_extension2(con, 0, cat, -1, NULL, NULL, iface, strdup(""), ast_free, registrar);
1580 /* If voicemail, use "stdexten" else use plain old dial */
1582 snprintf(tmp, sizeof(tmp), "stdexten|%s|${HINT}", cat);
1583 ast_add_extension2(con, 0, cat, 1, NULL, NULL, "Macro", strdup(tmp), ast_free, registrar);
1585 ast_add_extension2(con, 0, cat, 1, NULL, NULL, "Dial", strdup("${HINT}"), ast_free, registrar);
1589 ast_config_destroy(cfg);
1592 static int pbx_load_module(void)
1594 struct ast_context *con;
1596 if(!pbx_load_config(config))
1597 return AST_MODULE_LOAD_DECLINE;
1601 ast_merge_contexts_and_delete(&local_contexts, registrar);
1603 for (con = NULL; (con = ast_walk_contexts(con));)
1604 ast_context_verify_includes(con);
1606 pbx_set_autofallthrough(autofallthrough_config);
1611 static int load_module(void)
1613 if (pbx_load_module())
1614 return AST_MODULE_LOAD_DECLINE;
1616 if (static_config && !write_protect_config)
1617 ast_cli_register(&cli_dialplan_save);
1618 ast_cli_register_multiple(cli_pbx_config, sizeof(cli_pbx_config) / sizeof(struct ast_cli_entry));
1623 static int reload(void)
1625 if (clearglobalvars_config)
1626 pbx_builtin_clear_globals();
1631 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Text Extension Configuration",
1632 .load = load_module,
1633 .unload = unload_module,