2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2005, 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
26 #include <sys/types.h>
35 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
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 #ifdef __AST_DEBUG_MALLOC
46 static void FREE(void *ptr)
54 static char *dtext = "Text Extension Configuration";
55 static char *config = "extensions.conf";
56 static char *registrar = "pbx_config";
58 static int static_config = 0;
59 static int write_protect_config = 1;
60 static int autofallthrough_config = 0;
61 static int clearglobalvars_config = 0;
63 AST_MUTEX_DEFINE_STATIC(save_dialplan_lock);
65 static struct ast_context *local_contexts = NULL;
68 * Help for commands provided by this module ...
70 static char context_dont_include_help[] =
71 "Usage: dont include <context> in <context>\n"
72 " Remove an included context from another context.\n";
74 static char context_remove_extension_help[] =
75 "Usage: remove extension exten@context [priority]\n"
76 " Remove an extension from a given context. If a priority\n"
77 " is given, only that specific priority from the given extension\n"
78 " will be removed.\n";
80 static char context_add_include_help[] =
81 "Usage: include <context> in <context>\n"
82 " Include a context in another context.\n";
84 static char save_dialplan_help[] =
85 "Usage: save dialplan [/path/to/extension/file]\n"
86 " Save dialplan created by pbx_config module.\n"
88 "Example: save dialplan (/etc/asterisk/extensions.conf)\n"
89 " save dialplan /home/markster (/home/markster/extensions.conf)\n";
91 static char context_add_extension_help[] =
92 "Usage: add extension <exten>,<priority>,<app>,<app-data> into <context>\n"
94 " This command will add new extension into <context>. If there is an\n"
95 " existence of extension with the same priority and last 'replace'\n"
96 " arguments is given here we simply replace this extension.\n"
98 "Example: add extension 6123,1,Dial,IAX/216.207.245.56/6123 into local\n"
99 " Now, you can dial 6123 and talk to Markster :)\n";
101 static char context_add_ignorepat_help[] =
102 "Usage: add ignorepat <pattern> into <context>\n"
103 " This command adds a new ignore pattern into context <context>\n"
105 "Example: add ignorepat _3XX into local\n";
107 static char context_remove_ignorepat_help[] =
108 "Usage: remove ignorepat <pattern> from <context>\n"
109 " This command removes an ignore pattern from context <context>\n"
111 "Example: remove ignorepat _3XX from local\n";
113 static char reload_extensions_help[] =
114 "Usage: reload extensions.conf without reloading any other modules\n"
115 " This command does not delete global variables unless\n"
116 " clearglobalvars is set to yes in extensions.conf\n"
118 "Example: extensions reload\n";
121 * Implementation of functions provided by this module
125 * REMOVE INCLUDE command stuff
127 static int handle_context_dont_include(int fd, int argc, char *argv[])
130 return RESULT_SHOWUSAGE;
132 if (strcmp(argv[3], "in"))
133 return RESULT_SHOWUSAGE;
135 if (!ast_context_remove_include(argv[4], argv[2], registrar)) {
136 ast_cli(fd, "We are not including '%s' in '%s' now\n",
138 return RESULT_SUCCESS;
141 ast_cli(fd, "Failed to remove '%s' include from '%s' context\n",
143 return RESULT_FAILURE;
146 /*! \brief return true if 'name' is included by context c */
147 static int lookup_ci(struct ast_context *c, const char *name)
149 struct ast_include *i = NULL;
151 if (ast_lock_context(c)) /* error, skip */
153 while ( (i = ast_walk_context_includes(c, i)) )
154 if (!strcmp(name, ast_get_include_name(i)))
156 ast_unlock_context(c);
157 return i ? -1 /* success */ : 0;
160 /*! \brief return true if 'name' is in the ignorepats for context c */
161 static int lookup_c_ip(struct ast_context *c, const char *name)
163 struct ast_ignorepat *ip = NULL;
165 if (ast_lock_context(c)) /* error, skip */
167 while ( (ip = ast_walk_context_ignorepats(c, ip)) )
168 if (!strcmp(name, ast_get_ignorepat_name(ip)))
170 ast_unlock_context(c);
171 return ip ? -1 /* success */ : 0;
174 /*! \brief moves to the n-th word in the string, or empty string if none */
175 static const char *skip_words(const char *p, int n)
178 for (;n && *p; p++) {
179 if (isblank(*p) /* XXX order is important */ && !in_blank) {
180 n--; /* one word is gone */
182 } else if (/* !is_blank(*p), we know already, && */ in_blank) {
189 /*! \brief match the first 'len' chars of word. len==0 always succeeds */
190 static int partial_match(const char *s, const char *word, int len)
192 return (len == 0 || !strncmp(s, word, len));
195 /*! \brief split extension@context in two parts, return -1 on error.
196 * The return string is malloc'ed and pointed by *ext
198 static int split_ec(const char *src, char **ext, char ** const ctx)
200 char *c, *e = ast_strdup(src); /* now src is not used anymore */
203 return -1; /* malloc error */
204 /* now, parse values from 'exten@context' */
207 if (c == NULL) /* no context part */
208 *ctx = ""; /* it is not overwritten, anyways */
209 else { /* found context, check for duplicity ... */
212 if (strchr(c, '@')) { /* two @, not allowed */
220 /* _X_ is the string we need to complete */
221 static char *complete_context_dont_include(const char *line, const char *word,
226 int len = strlen(word); /* how many bytes to match */
227 struct ast_context *c = NULL;
229 if (pos == 2) { /* "dont include _X_" */
230 if (ast_lock_contexts()) {
231 ast_log(LOG_ERROR, "Failed to lock context list\n");
234 /* walk contexts and their includes, return the n-th match */
235 while (!res && (c = ast_walk_contexts(c))) {
236 struct ast_include *i = NULL;
238 if (ast_lock_context(c)) /* error ? skip this one */
241 while ( !res && (i = ast_walk_context_includes(c, i)) ) {
242 const char *i_name = ast_get_include_name(i);
243 struct ast_context *nc = NULL;
244 int already_served = 0;
246 if (!partial_match(i_name, word, len))
247 continue; /* not matched */
249 /* check if this include is already served or not */
251 /* go through all contexts again till we reach actual
252 * context or already_served = 1
254 while ( (nc = ast_walk_contexts(nc)) && nc != c && !already_served)
255 already_served = lookup_ci(nc, i_name);
257 if (!already_served && ++which > state)
258 res = strdup(i_name);
260 ast_unlock_context(c);
263 ast_unlock_contexts();
265 } else if (pos == 3) { /* "dont include CTX _X_" */
267 * complete as 'in', but only if previous context is really
270 char *context, *dupline;
271 const char *s = skip_words(line, 2); /* skip 'dont' 'include' */
275 context = dupline = strdup(s);
277 ast_log(LOG_ERROR, "Out of free memory\n");
280 strsep(&dupline, " ");
282 if (ast_lock_contexts()) {
283 ast_log(LOG_ERROR, "Failed to lock contexts list\n");
288 /* go through all contexts and check if is included ... */
289 while (!res && (c = ast_walk_contexts(c)))
290 if (lookup_ci(c, context)) /* context is really included, complete "in" command */
292 ast_unlock_contexts();
294 ast_log(LOG_WARNING, "%s not included anywhere\n", context);
297 } else if (pos == 4) { /* "dont include CTX in _X_" */
299 * Context from which we removing include ...
301 char *context, *dupline, *in;
302 const char *s = skip_words(line, 2); /* skip 'dont' 'include' */
303 context = dupline = strdup(s);
305 ast_log(LOG_ERROR, "Out of free memory\n");
309 strsep(&dupline, " "); /* skip context */
311 /* third word must be 'in' */
312 in = strsep(&dupline, " ");
313 if (!in || strcmp(in, "in")) {
318 if (ast_lock_contexts()) {
319 ast_log(LOG_ERROR, "Failed to lock context list\n");
324 /* walk through all contexts ... */
326 while ( !res && (c = ast_walk_contexts(c))) {
327 const char *c_name = ast_get_context_name(c);
328 if (!partial_match(c_name, word, len)) /* not a good target */
330 /* walk through all includes and check if it is our context */
331 if (lookup_ci(c, context) && ++which > state)
332 res = strdup(c_name);
334 ast_unlock_contexts();
343 * REMOVE EXTENSION command stuff
345 static int handle_context_remove_extension(int fd, int argc, char *argv[])
347 int removing_priority = 0;
348 char *exten, *context;
349 int ret = RESULT_FAILURE;
351 if (argc != 4 && argc != 3) return RESULT_SHOWUSAGE;
354 * Priority input checking ...
359 /* check for digits in whole parameter for right priority ...
360 * why? because atoi (strtol) returns 0 if any characters in
361 * string and whole extension will be removed, it's not good
363 if (!strcmp("hint", c))
364 removing_priority = PRIORITY_HINT;
366 while (*c && isdigit(*c))
368 if (*c) { /* non-digit in string */
369 ast_cli(fd, "Invalid priority '%s'\n", argv[3]);
370 return RESULT_FAILURE;
372 removing_priority = atoi(argv[3]);
375 if (removing_priority == 0) {
376 ast_cli(fd, "If you want to remove whole extension, please " \
377 "omit priority argument\n");
378 return RESULT_FAILURE;
382 /* XXX original overwrote argv[2] */
384 * Format exten@context checking ...
386 if (split_ec(argv[2], &exten, &context))
387 return RESULT_FAILURE; /* XXX malloc failure */
388 if ((!strlen(exten)) || (!(strlen(context)))) {
389 ast_cli(fd, "Missing extension or context name in second argument '%s'\n",
392 return RESULT_FAILURE;
395 if (!ast_context_remove_extension(context, exten, removing_priority, registrar)) {
396 if (!removing_priority)
397 ast_cli(fd, "Whole extension %s@%s removed\n",
400 ast_cli(fd, "Extension %s@%s with priority %d removed\n",
401 exten, context, removing_priority);
403 ret = RESULT_SUCCESS;
405 ast_cli(fd, "Failed to remove extension %s@%s\n", exten, context);
406 ret = RESULT_FAILURE;
412 #define BROKEN_READLINE 1
414 #ifdef BROKEN_READLINE
416 * There is one funny thing, when you have word like 300@ and you hit
417 * <tab>, you arguments will like as your word is '300 ', so it '@'
418 * characters acts sometimes as word delimiter and sometimes as a part
421 * This fix function, allocates new word variable and store here every
422 * time xxx@yyy always as one word and correct pos is set too
424 * It's ugly, I know, but I'm waiting for Mark suggestion if upper is
427 static int fix_complete_args(const char *line, char **word, int *pos)
429 char *_line, *_strsep_line, *_previous_word = NULL, *_word = NULL;
432 _line = strdup(line);
434 _strsep_line = _line;
435 while (_strsep_line) {
436 _previous_word = _word;
437 _word = strsep(&_strsep_line, " ");
439 if (_word && strlen(_word)) words++;
443 if (_word || _previous_word) {
445 if (!strlen(_word)) words++;
446 *word = strdup(_word);
448 *word = strdup(_previous_word);
457 #endif /* BROKEN_READLINE */
459 static char *complete_context_remove_extension(const char *line, const char *word, int pos,
465 #ifdef BROKEN_READLINE
468 * Fix arguments, *word is a new allocated structure, REMEMBER to
469 * free *word when you want to return from this function ...
471 if (fix_complete_args(line, &word2, &pos)) {
472 ast_log(LOG_ERROR, "Out of free memory\n");
478 if (pos == 2) { /* 'remove extension _X_' (exten@context ... */
479 struct ast_context *c = NULL;
480 char *context = NULL, *exten = NULL;
481 int le = 0; /* length of extension */
482 int lc = 0; /* length of context */
484 lc = split_ec(word, &exten, &context);
485 #ifdef BROKEN_READLINE
491 lc = strlen(context);
493 if (ast_lock_contexts()) {
494 ast_log(LOG_ERROR, "Failed to lock context list\n");
498 /* find our context ... */
499 while ( (c = ast_walk_contexts(c)) ) { /* match our context if any */
500 struct ast_exten *e = NULL;
502 if (!partial_match(ast_get_context_name(c), context, lc))
503 continue; /* context not matched */
504 while ( (e = ast_walk_context_extensions(c, e)) ) { /* try to complete extensions ... */
505 if ( partial_match(ast_get_extension_name(e), exten, le) && ++which > state) { /* n-th match */
506 /* If there is an extension then return exten@context. XXX otherwise ? */
508 asprintf(&ret, "%s@%s", ast_get_extension_name(e), ast_get_context_name(c));
512 if (e) /* got a match */
516 ast_unlock_contexts();
520 } else if (pos == 3) { /* 'remove extension EXT _X_' (priority) */
521 char *exten = NULL, *context, *p;
522 struct ast_context *c;
524 const char *s = skip_words(line, 2); /* skip 'remove' 'extension' */
525 int i = split_ec(s, &exten, &context); /* parse ext@context */
529 if ( (p = strchr(exten, ' ')) ) /* remove space after extension */
531 if ( (p = strchr(context, ' ')) ) /* remove space after context */
534 lc = strlen(context);
536 if (le == 0 || lc == 0)
539 if (ast_lock_contexts()) {
540 ast_log(LOG_ERROR, "Failed to lock context list\n");
546 while ( (c = ast_walk_contexts(c)) ) {
547 /* XXX locking on c ? */
549 if (strcmp(ast_get_context_name(c), context) != 0)
551 /* got it, we must match here */
553 while ( (e = ast_walk_context_extensions(c, e)) ) {
554 struct ast_exten *priority;
557 if (strcmp(ast_get_extension_name(e), exten) != 0)
561 while ( !ret && (priority = ast_walk_extension_priorities(e, priority)) ) {
562 snprintf(buffer, sizeof(buffer), "%u", ast_get_extension_priority(priority));
563 if (partial_match(buffer, word, len) && ++which > state) /* n-th match */
564 ret = strdup(buffer);
570 ast_unlock_contexts();
574 #ifdef BROKEN_READLINE
582 * Include context ...
584 static int handle_context_add_include(int fd, int argc, char *argv[])
586 if (argc != 5) /* include context CTX in CTX */
587 return RESULT_SHOWUSAGE;
589 /* third arg must be 'in' ... */
590 if (strcmp(argv[3], "in") && strcmp(argv[3], "into")) /* XXX why both ? */
591 return RESULT_SHOWUSAGE;
593 if (ast_context_add_include(argv[4], argv[2], registrar)) {
596 ast_cli(fd, "Out of memory for context addition\n");
600 ast_cli(fd, "Failed to lock context(s) list, please try again later\n");
604 ast_cli(fd, "Context '%s' already included in '%s' context\n",
610 ast_cli(fd, "There is no existence of context '%s'\n",
611 errno == ENOENT ? argv[4] : argv[2]);
615 ast_cli(fd, "Failed to include '%s' in '%s' context\n",
619 return RESULT_FAILURE;
622 /* show some info ... */
623 ast_cli(fd, "Context '%s' included in '%s' context\n",
626 return RESULT_SUCCESS;
629 static char *complete_context_add_include(const char *line, const char *word, int pos,
632 struct ast_context *c;
635 int len = strlen(word);
637 if (pos == 2) { /* 'include context _X_' (context) ... */
638 if (ast_lock_contexts()) {
639 ast_log(LOG_ERROR, "Failed to lock context list\n");
642 for (c = NULL; !ret && (c = ast_walk_contexts(c)); )
643 if (partial_match(ast_get_context_name(c), word, len) && ++which > state)
644 ret = strdup(ast_get_context_name(c));
645 ast_unlock_contexts();
647 } else if (pos == 3) { /* include context CTX _X_ */
648 /* complete as 'in' if context exists or we are unable to check */
649 char *context, *dupline;
650 struct ast_context *c;
651 const char *s = skip_words(line, 2); /* should not fail */
653 if (state != 0) /* only once */
656 /* parse context from line ... */
657 context = dupline = strdup(s);
659 ast_log(LOG_ERROR, "Out of free memory\n");
662 strsep(&dupline, " ");
664 /* check for context existence ... */
665 if (ast_lock_contexts()) {
666 ast_log(LOG_ERROR, "Failed to lock context list\n");
667 /* our fault, we can't check, so complete 'in' ... */
670 for (c = NULL; !ret && (c = ast_walk_contexts(c)); )
671 if (!strcmp(context, ast_get_context_name(c)))
672 ret = strdup("in"); /* found */
673 ast_unlock_contexts();
677 } else if (pos == 4) { /* 'include context CTX in _X_' (dst context) */
678 char *context, *dupline, *in;
679 const char *s = skip_words(line, 2); /* should not fail */
680 context = dupline = strdup(s);
682 ast_log(LOG_ERROR, "Out of free memory\n");
685 strsep(&dupline, " "); /* skip context */
686 in = strsep(&dupline, " ");
687 /* error if missing context or third word is not 'in' */
688 if (!strlen(context) || strcmp(in, "in")) {
689 ast_log(LOG_ERROR, "bad context %s or missing in %s\n",
694 if (ast_lock_contexts()) {
695 ast_log(LOG_ERROR, "Failed to lock context list\n");
699 for (c = NULL; (c = ast_walk_contexts(c)); )
700 if (!strcmp(context, ast_get_context_name(c)))
702 if (c) { /* first context exists, go on... */
703 /* go through all contexts ... */
704 for (c = NULL; !ret && (c = ast_walk_contexts(c)); ) {
705 if (!strcmp(context, ast_get_context_name(c)))
706 continue; /* skip ourselves */
707 if (partial_match(ast_get_context_name(c), word, len) &&
708 !lookup_ci(c, context) /* not included yet */ &&
710 ret = strdup(ast_get_context_name(c));
713 ast_log(LOG_ERROR, "context %s not found\n", context);
715 ast_unlock_contexts();
725 * \brief 'save dialplan' CLI command implementation functions ...
727 static int handle_save_dialplan(int fd, int argc, char *argv[])
730 struct ast_context *c;
731 struct ast_config *cfg;
732 struct ast_variable *v;
733 int incomplete = 0; /* incomplete config write? */
736 const char *base, *slash, *file;
738 if (! (static_config && !write_protect_config)) {
740 "I can't save dialplan now, see '%s' example file.\n",
742 return RESULT_FAILURE;
745 if (argc != 2 && argc != 3)
746 return RESULT_SHOWUSAGE;
748 if (ast_mutex_lock(&save_dialplan_lock)) {
750 "Failed to lock dialplan saving (another proccess saving?)\n");
751 return RESULT_FAILURE;
753 /* XXX the code here is quite loose, a pathname with .conf in it
754 * is assumed to be a complete pathname
756 if (argc == 3) { /* have config path. Look for *.conf */
758 if (!strstr(argv[2], ".conf")) { /*no, this is assumed to be a pathname */
759 /* if filename ends with '/', do not add one */
760 slash = (*(argv[2] + strlen(argv[2]) -1) == '/') ? "/" : "";
761 file = config; /* default: 'extensions.conf' */
762 } else { /* yes, complete file name */
767 /* no config file, default one */
768 base = ast_config_AST_CONFIG_DIR;
772 snprintf(filename, sizeof(filename), "%s%s%s", base, slash, config);
774 cfg = ast_config_load("extensions.conf");
776 /* try to lock contexts list */
777 if (ast_lock_contexts()) {
778 ast_cli(fd, "Failed to lock contexts list\n");
779 ast_mutex_unlock(&save_dialplan_lock);
780 ast_config_destroy(cfg);
781 return RESULT_FAILURE;
784 /* create new file ... */
785 if (!(output = fopen(filename, "wt"))) {
786 ast_cli(fd, "Failed to create file '%s'\n",
788 ast_unlock_contexts();
789 ast_mutex_unlock(&save_dialplan_lock);
790 ast_config_destroy(cfg);
791 return RESULT_FAILURE;
794 /* fireout general info */
795 fprintf(output, "[general]\nstatic=%s\nwriteprotect=%s\n\n",
796 static_config ? "yes" : "no",
797 write_protect_config ? "yes" : "no");
799 if ((v = ast_variable_browse(cfg, "globals"))) {
800 fprintf(output, "[globals]\n");
802 fprintf(output, "%s => %s\n", v->name, v->value);
805 fprintf(output, "\n");
808 ast_config_destroy(cfg);
810 #define PUT_CTX_HDR do { \
811 if (!context_header_written) { \
812 fprintf(output, "[%s]\n", ast_get_context_name(c)); \
813 context_header_written = 1; \
817 /* walk all contexts */
818 for (c = NULL; (c = ast_walk_contexts(c)); ) {
819 int context_header_written = 0;
820 struct ast_exten *e, *last_written_e = NULL;
821 struct ast_include *i;
822 struct ast_ignorepat *ip;
825 /* try to lock context and fireout all info */
826 if (ast_lock_context(c)) { /* lock failure */
830 /* registered by this module? */
831 /* XXX do we need this ? */
832 if (!strcmp(ast_get_context_registrar(c), registrar)) {
833 fprintf(output, "[%s]\n", ast_get_context_name(c));
834 context_header_written = 1;
837 /* walk extensions ... */
838 for (e = NULL; (e = ast_walk_context_extensions(c, e)); ) {
839 struct ast_exten *p = NULL;
841 /* fireout priorities */
842 while ( (p = ast_walk_extension_priorities(e, p)) ) {
843 if (strcmp(ast_get_extension_registrar(p), registrar) != 0) /* not this source */
846 /* make empty line between different extensions */
847 if (last_written_e != NULL &&
848 strcmp(ast_get_extension_name(last_written_e),
849 ast_get_extension_name(p)))
850 fprintf(output, "\n");
855 if (ast_get_extension_priority(p)==PRIORITY_HINT) { /* easy */
856 fprintf(output, "exten => %s,hint,%s\n",
857 ast_get_extension_name(p),
858 ast_get_extension_app(p));
859 } else { /* copy and replace '|' with ',' */
860 const char *sep, *cid;
861 char *tempdata = strdup(ast_get_extension_app_data(p));
864 if (!tempdata) { /* XXX error duplicating string ? */
868 for (s = tempdata; *s; s++)
871 if (ast_get_extension_matchcid(p)) {
873 cid = ast_get_extension_cidmatch(p);
877 fprintf(output, "exten => %s%s%s,%d,%s(%s)\n",
878 ast_get_extension_name(p), sep, cid,
879 ast_get_extension_priority(p),
880 ast_get_extension_app(p), tempdata);
886 /* written any extensions? ok, write space between exten & inc */
888 fprintf(output, "\n");
890 /* walk through includes */
891 for (i = NULL; (i = ast_walk_context_includes(c, i)) ; ) {
892 if (strcmp(ast_get_include_registrar(i), registrar) != 0)
893 continue; /* not mine */
895 fprintf(output, "include => %s\n", ast_get_include_name(i));
897 if (ast_walk_context_includes(c, NULL))
898 fprintf(output, "\n");
900 /* walk through switches */
901 for (sw = NULL; (sw = ast_walk_context_switches(c, sw)) ; ) {
902 if (strcmp(ast_get_switch_registrar(sw), registrar) != 0)
903 continue; /* not mine */
905 fprintf(output, "switch => %s/%s\n",
906 ast_get_switch_name(sw), ast_get_switch_data(sw));
909 if (ast_walk_context_switches(c, NULL))
910 fprintf(output, "\n");
912 /* fireout ignorepats ... */
913 for (ip = NULL; (ip = ast_walk_context_ignorepats(c, ip)); ) {
914 if (strcmp(ast_get_ignorepat_registrar(ip), registrar) != 0)
915 continue; /* not mine */
917 fprintf(output, "ignorepat => %s\n",
918 ast_get_ignorepat_name(ip));
921 ast_unlock_context(c);
924 ast_unlock_contexts();
925 ast_mutex_unlock(&save_dialplan_lock);
929 ast_cli(fd, "Saved dialplan is incomplete\n");
930 return RESULT_FAILURE;
933 ast_cli(fd, "Dialplan successfully saved into '%s'\n",
935 return RESULT_SUCCESS;
939 * \brief ADD EXTENSION command stuff
941 static int handle_context_add_extension(int fd, int argc, char *argv[])
946 char *cidmatch, *app, *app_data;
949 /* check for arguments at first */
950 if (argc != 5 && argc != 6)
951 return RESULT_SHOWUSAGE;
952 if (strcmp(argv[3], "into"))
953 return RESULT_SHOWUSAGE;
954 if (argc == 6) if (strcmp(argv[5], "replace")) return RESULT_SHOWUSAGE;
956 /* XXX overwrite argv[2] */
957 whole_exten = argv[2];
958 exten = strsep(&whole_exten,",");
959 if (strchr(exten, '/')) {
961 strsep(&cidmatch,"/");
965 prior = strsep(&whole_exten,",");
967 if (!strcmp(prior, "hint")) {
968 iprior = PRIORITY_HINT;
970 if (sscanf(prior, "%d", &iprior) != 1) {
971 ast_cli(fd, "'%s' is not a valid priority\n", prior);
977 if (app && (start = strchr(app, '(')) && (end = strrchr(app, ')'))) {
978 *start = *end = '\0';
979 app_data = start + 1;
980 ast_process_quotes_and_slashes(app_data, ',', '|');
983 app_data = strchr(app, ',');
992 if (!exten || !prior || !app || (!app_data && iprior != PRIORITY_HINT))
993 return RESULT_SHOWUSAGE;
997 if (ast_add_extension(argv[4], argc == 6 ? 1 : 0, exten, iprior, NULL, cidmatch, app,
998 (void *)strdup(app_data), free, registrar)) {
1001 ast_cli(fd, "Out of free memory\n");
1005 ast_cli(fd, "Failed to lock context(s) list, please try again later\n");
1009 ast_cli(fd, "No existence of '%s' context\n", argv[4]);
1013 ast_cli(fd, "Extension %s@%s with priority %s already exists\n",
1014 exten, argv[4], prior);
1018 ast_cli(fd, "Failed to add '%s,%s,%s,%s' extension into '%s' context\n",
1019 exten, prior, app, app_data, argv[4]);
1022 return RESULT_FAILURE;
1026 ast_cli(fd, "Extension %s@%s (%s) replace by '%s,%s,%s,%s'\n",
1027 exten, argv[4], prior, exten, prior, app, app_data);
1029 ast_cli(fd, "Extension '%s,%s,%s,%s' added into '%s' context\n",
1030 exten, prior, app, app_data, argv[4]);
1032 return RESULT_SUCCESS;
1035 /*! add extension 6123,1,Dial,IAX/212.71.138.13/6123 into local */
1036 static char *complete_context_add_extension(const char *line, const char *word,
1041 if (pos == 3) { /* complete 'into' word ... */
1042 return (state == 0) ? strdup("into") : NULL;
1043 } else if (pos == 4) { /* complete context */
1044 struct ast_context *c = NULL;
1045 int len = strlen(word);
1048 /* try to lock contexts list ... */
1049 if (ast_lock_contexts()) {
1050 ast_log(LOG_WARNING, "Failed to lock contexts list\n");
1054 /* walk through all contexts */
1055 while ( !res && (c = ast_walk_contexts(c)) )
1056 if (partial_match(ast_get_context_name(c), word, len) && ++which > state)
1057 res = strdup(ast_get_context_name(c));
1058 ast_unlock_contexts();
1060 } else if (pos == 5) {
1061 return state == 0 ? strdup("replace") : NULL;
1067 * IGNOREPAT CLI stuff
1069 static int handle_context_add_ignorepat(int fd, int argc, char *argv[])
1072 return RESULT_SHOWUSAGE;
1073 if (strcmp(argv[3], "into"))
1074 return RESULT_SHOWUSAGE;
1076 if (ast_context_add_ignorepat(argv[4], argv[2], registrar)) {
1079 ast_cli(fd, "Out of free memory\n");
1083 ast_cli(fd, "There is no existence of '%s' context\n", argv[4]);
1087 ast_cli(fd, "Ignore pattern '%s' already included in '%s' context\n",
1092 ast_cli(fd, "Failed to lock context(s) list, please, try again later\n");
1096 ast_cli(fd, "Failed to add ingore pattern '%s' into '%s' context\n",
1100 return RESULT_FAILURE;
1103 ast_cli(fd, "Ignore pattern '%s' added into '%s' context\n",
1105 return RESULT_SUCCESS;
1108 static char *complete_context_add_ignorepat(const char *line, const char *word,
1112 return state == 0 ? strdup("into") : NULL;
1113 else if (pos == 4) {
1114 struct ast_context *c;
1116 char *dupline, *ignorepat = NULL;
1119 int len = strlen(word);
1121 /* XXX skip first two words 'add' 'ignorepat' */
1122 s = skip_words(line, 2);
1125 dupline = strdup(s);
1127 ast_log(LOG_ERROR, "Malloc failure\n");
1130 ignorepat = strsep(&dupline, " ");
1132 if (ast_lock_contexts()) {
1133 ast_log(LOG_ERROR, "Failed to lock contexts list\n");
1137 for (c = NULL; !ret && (c = ast_walk_contexts(c));) {
1140 if (!partial_match(ast_get_context_name(c), word, len))
1141 continue; /* not mine */
1142 if (ignorepat) /* there must be one, right ? */
1143 found = lookup_c_ip(c, ignorepat);
1144 if (!found && ++which > state)
1145 ret = strdup(ast_get_context_name(c));
1150 ast_unlock_contexts();
1157 static int handle_context_remove_ignorepat(int fd, int argc, char *argv[])
1160 return RESULT_SHOWUSAGE;
1161 if (strcmp(argv[3], "from"))
1162 return RESULT_SHOWUSAGE;
1164 if (ast_context_remove_ignorepat(argv[4], argv[2], registrar)) {
1167 ast_cli(fd, "Failed to lock context(s) list, please try again later\n");
1171 ast_cli(fd, "There is no existence of '%s' context\n", argv[4]);
1175 ast_cli(fd, "There is no existence of '%s' ignore pattern in '%s' context\n",
1180 ast_cli(fd, "Failed to remove ignore pattern '%s' from '%s' context\n", argv[2], argv[4]);
1183 return RESULT_FAILURE;
1186 ast_cli(fd, "Ignore pattern '%s' removed from '%s' context\n",
1188 return RESULT_SUCCESS;
1191 static int pbx_load_module(void);
1193 static int handle_reload_extensions(int fd, int argc, char *argv[])
1196 return RESULT_SHOWUSAGE;
1198 return RESULT_SUCCESS;
1201 static char *complete_context_remove_ignorepat(const char *line, const char *word,
1204 struct ast_context *c;
1209 int len = strlen(word);
1210 if (ast_lock_contexts()) {
1211 ast_log(LOG_WARNING, "Failed to lock contexts list\n");
1215 for (c = NULL; !ret && (c = ast_walk_contexts(c));) {
1216 struct ast_ignorepat *ip;
1218 if (ast_lock_context(c)) /* error, skip it */
1221 for (ip = NULL; !ret && (ip = ast_walk_context_ignorepats(c, ip));) {
1222 if (partial_match(ast_get_ignorepat_name(ip), word, len) && ++which > state) {
1224 struct ast_context *cw = NULL;
1226 while ( (cw = ast_walk_contexts(cw)) && cw != c && !found) {
1227 /* XXX do i stop on c, or skip it ? */
1228 found = lookup_c_ip(cw, ast_get_ignorepat_name(ip));
1231 ret = strdup(ast_get_ignorepat_name(ip));
1234 ast_unlock_context(c);
1236 ast_unlock_contexts();
1238 } else if (pos == 3) {
1239 return state == 0 ? strdup("from") : NULL;
1240 } else if (pos == 4) { /* XXX check this */
1241 char *dupline, *duplinet, *ignorepat;
1242 int len = strlen(word);
1244 dupline = strdup(line);
1246 ast_log(LOG_WARNING, "Out of free memory\n");
1251 strsep(&duplinet, " ");
1252 strsep(&duplinet, " ");
1253 ignorepat = strsep(&duplinet, " ");
1260 if (ast_lock_contexts()) {
1261 ast_log(LOG_WARNING, "Failed to lock contexts list\n");
1266 for (c = NULL; !ret && (c = ast_walk_contexts(c)); ) {
1267 if (ast_lock_context(c)) /* fail, skip it */
1269 if (!partial_match(ast_get_context_name(c), word, len))
1271 if (lookup_c_ip(c, ignorepat) && ++which > state)
1272 ret = strdup(ast_get_context_name(c));
1273 ast_unlock_context(c);
1275 ast_unlock_contexts();
1284 * CLI entries for commands provided by this module
1286 static struct ast_cli_entry context_dont_include_cli =
1287 { { "dont", "include", NULL }, handle_context_dont_include,
1288 "Remove a specified include from context", context_dont_include_help,
1289 complete_context_dont_include };
1291 static struct ast_cli_entry context_remove_extension_cli =
1292 { { "remove", "extension", NULL }, handle_context_remove_extension,
1293 "Remove a specified extension", context_remove_extension_help,
1294 complete_context_remove_extension };
1296 static struct ast_cli_entry context_add_include_cli =
1297 { { "include", "context", NULL }, handle_context_add_include,
1298 "Include context in other context", context_add_include_help,
1299 complete_context_add_include };
1301 static struct ast_cli_entry save_dialplan_cli =
1302 { { "save", "dialplan", NULL }, handle_save_dialplan,
1303 "Save dialplan", save_dialplan_help };
1305 static struct ast_cli_entry context_add_extension_cli =
1306 { { "add", "extension", NULL }, handle_context_add_extension,
1307 "Add new extension into context", context_add_extension_help,
1308 complete_context_add_extension };
1310 static struct ast_cli_entry context_add_ignorepat_cli =
1311 { { "add", "ignorepat", NULL }, handle_context_add_ignorepat,
1312 "Add new ignore pattern", context_add_ignorepat_help,
1313 complete_context_add_ignorepat };
1315 static struct ast_cli_entry context_remove_ignorepat_cli =
1316 { { "remove", "ignorepat", NULL }, handle_context_remove_ignorepat,
1317 "Remove ignore pattern from context", context_remove_ignorepat_help,
1318 complete_context_remove_ignorepat };
1320 static struct ast_cli_entry reload_extensions_cli =
1321 { { "extensions", "reload", NULL}, handle_reload_extensions,
1322 "Reload extensions and *only* extensions", reload_extensions_help };
1325 * Standard module functions ...
1327 int unload_module(void)
1329 ast_cli_unregister(&context_add_extension_cli);
1330 if (static_config && !write_protect_config)
1331 ast_cli_unregister(&save_dialplan_cli);
1332 ast_cli_unregister(&context_add_include_cli);
1333 ast_cli_unregister(&context_dont_include_cli);
1334 ast_cli_unregister(&context_remove_extension_cli);
1335 ast_cli_unregister(&context_remove_ignorepat_cli);
1336 ast_cli_unregister(&context_add_ignorepat_cli);
1337 ast_cli_unregister(&reload_extensions_cli);
1338 ast_context_destroy(NULL, registrar);
1342 static int pbx_load_module(void)
1344 struct ast_config *cfg;
1347 char realvalue[256];
1349 struct ast_context *con;
1351 cfg = ast_config_load(config);
1353 struct ast_variable *v;
1356 /* Use existing config to populate the PBX table */
1357 static_config = ast_true(ast_variable_retrieve(cfg, "general", "static"));
1358 write_protect_config = ast_true(ast_variable_retrieve(cfg, "general", "writeprotect"));
1359 autofallthrough_config = ast_true(ast_variable_retrieve(cfg, "general", "autofallthrough"));
1360 clearglobalvars_config = ast_true(ast_variable_retrieve(cfg, "general", "clearglobalvars"));
1361 ast_set2_flag(&ast_options, !ast_false(ast_variable_retrieve(cfg, "general", "priorityjumping")), AST_OPT_FLAG_PRIORITY_JUMPING);
1363 for (v = ast_variable_browse(cfg, "globals"); v; v = v->next) {
1364 memset(realvalue, 0, sizeof(realvalue));
1365 pbx_substitute_variables_helper(NULL, v->value, realvalue, sizeof(realvalue) - 1);
1366 pbx_builtin_setvar_helper(NULL, v->name, realvalue);
1368 for (cxt = NULL; (cxt = ast_category_browse(cfg, cxt)); ) {
1370 /* All categories but "general" or "globals" are considered contexts */
1371 if (!strcasecmp(cxt, "general") || !strcasecmp(cxt, "globals"))
1373 con=ast_context_create(&local_contexts,cxt, registrar);
1377 /* XXX indentation should be fixed for this block */
1378 for (v = ast_variable_browse(cfg, cxt); v; v = v->next) {
1379 if (!strcasecmp(v->name, "exten")) {
1380 char *ext, *pri, *appl, *data, *cidmatch;
1383 char realext[256]="";
1384 char *plus, *firstp, *firstc;
1385 char *tc = strdup(v->value);
1387 fprintf(stderr,"Error strdup returned NULL in %s\n",__PRETTY_FUNCTION__);
1390 ext = strsep(&stringp, ",");
1393 pbx_substitute_variables_helper(NULL, ext, realext, sizeof(realext) - 1);
1394 cidmatch = strchr(realext, '/');
1397 ast_shrink_phone_number(cidmatch);
1399 pri = strsep(&stringp, ",");
1402 label = strchr(pri, '(');
1405 end = strchr(label, ')');
1409 ast_log(LOG_WARNING, "Label missing trailing ')' at line %d\n", v->lineno);
1411 plus = strchr(pri, '+');
1414 if (!strcmp(pri,"hint"))
1416 else if (!strcmp(pri, "next") || !strcmp(pri, "n")) {
1420 ast_log(LOG_WARNING, "Can't use 'next' priority on the first entry!\n");
1421 } else if (!strcmp(pri, "same") || !strcmp(pri, "s")) {
1425 ast_log(LOG_WARNING, "Can't use 'same' priority on the first entry!\n");
1427 if (sscanf(pri, "%d", &ipri) != 1) {
1428 if ((ipri = ast_findlabel_extension2(NULL, con, realext, pri, cidmatch)) < 1) {
1429 ast_log(LOG_WARNING, "Invalid priority/label '%s' at line %d\n", pri, v->lineno);
1437 /* Find the first occurrence of either '(' or ',' */
1438 firstc = strchr(appl, ',');
1439 firstp = strchr(appl, '(');
1440 if (firstc && ((!firstp) || (firstc < firstp))) {
1441 /* comma found, no parenthesis */
1442 /* or both found, but comma found first */
1443 appl = strsep(&stringp, ",");
1445 } else if ((!firstc) && (!firstp)) {
1449 /* Final remaining case is parenthesis found first */
1450 appl = strsep(&stringp, "(");
1452 end = strrchr(data, ')');
1453 if ((end = strrchr(data, ')'))) {
1456 ast_log(LOG_WARNING, "No closing parenthesis found? '%s(%s'\n", appl, data);
1458 ast_process_quotes_and_slashes(data, ',', '|');
1463 appl = ast_skip_blanks(appl);
1468 if (!ast_opt_dont_warn) {
1469 if (!strcmp(realext, "_."))
1470 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);
1472 if (ast_add_extension2(con, 0, realext, ipri, label, cidmatch, appl, strdup(data), free, registrar)) {
1473 ast_log(LOG_WARNING, "Unable to register extension at line %d\n", v->lineno);
1478 } else if(!strcasecmp(v->name, "include")) {
1479 memset(realvalue, 0, sizeof(realvalue));
1480 pbx_substitute_variables_helper(NULL, v->value, realvalue, sizeof(realvalue) - 1);
1481 if (ast_context_add_include2(con, realvalue, registrar))
1482 ast_log(LOG_WARNING, "Unable to include context '%s' in context '%s'\n", v->value, cxt);
1483 } else if(!strcasecmp(v->name, "ignorepat")) {
1484 memset(realvalue, 0, sizeof(realvalue));
1485 pbx_substitute_variables_helper(NULL, v->value, realvalue, sizeof(realvalue) - 1);
1486 if (ast_context_add_ignorepat2(con, realvalue, registrar))
1487 ast_log(LOG_WARNING, "Unable to include ignorepat '%s' in context '%s'\n", v->value, cxt);
1488 } else if (!strcasecmp(v->name, "switch") || !strcasecmp(v->name, "lswitch") || !strcasecmp(v->name, "eswitch")) {
1489 char *stringp= realvalue;
1492 memset(realvalue, 0, sizeof(realvalue));
1493 if (!strcasecmp(v->name, "switch"))
1494 pbx_substitute_variables_helper(NULL, v->value, realvalue, sizeof(realvalue) - 1);
1496 ast_copy_string(realvalue, v->value, sizeof(realvalue));
1497 appl = strsep(&stringp, "/");
1498 data = strsep(&stringp, ""); /* XXX what for ? */
1501 if (ast_context_add_switch2(con, appl, data, !strcasecmp(v->name, "eswitch"), registrar))
1502 ast_log(LOG_WARNING, "Unable to include switch '%s' in context '%s'\n", v->value, cxt);
1506 ast_config_destroy(cfg);
1508 ast_merge_contexts_and_delete(&local_contexts,registrar);
1510 for (con = NULL; (con = ast_walk_contexts(con));)
1511 ast_context_verify_includes(con);
1513 pbx_set_autofallthrough(autofallthrough_config);
1518 int load_module(void)
1520 if (pbx_load_module()) return -1;
1522 ast_cli_register(&context_remove_extension_cli);
1523 ast_cli_register(&context_dont_include_cli);
1524 ast_cli_register(&context_add_include_cli);
1525 if (static_config && !write_protect_config)
1526 ast_cli_register(&save_dialplan_cli);
1527 ast_cli_register(&context_add_extension_cli);
1528 ast_cli_register(&context_add_ignorepat_cli);
1529 ast_cli_register(&context_remove_ignorepat_cli);
1530 ast_cli_register(&reload_extensions_cli);
1537 ast_context_destroy(NULL, registrar);
1538 if (clearglobalvars_config)
1539 pbx_builtin_clear_globals();
1549 char *description(void)
1556 return ASTERISK_GPL_KEY;