2 * Asterisk -- A telephony toolkit for Linux.
4 * Populate and remember extensions from static config file
6 * Copyright (C) 1999, Mark Spencer
8 * Mark Spencer <markster@linux-support.net>
10 * This program is free software, distributed under the terms of
11 * the GNU General Public License
14 #include <sys/types.h>
15 #include <asterisk/pbx.h>
16 #include <asterisk/config.h>
17 #include <asterisk/module.h>
18 #include <asterisk/logger.h>
19 #include <asterisk/cli.h>
25 /* For where to put dynamic tables */
26 #include "../asterisk.h"
27 #include "../astconf.h"
29 #ifdef __AST_DEBUG_MALLOC
30 static void FREE(void *ptr)
38 static char *dtext = "Text Extension Configuration";
39 static char *config = "extensions.conf";
40 static char *registrar = "pbx_config";
42 static int static_config = 0;
43 static int write_protect_config = 1;
45 AST_MUTEX_DEFINE_STATIC(save_dialplan_lock);
47 static struct ast_context *local_contexts = NULL;
50 * Help for commands provided by this module ...
52 static char context_dont_include_help[] =
53 "Usage: dont include context in include\n"
54 " Remove include from context.\n";
56 static char context_remove_extension_help[] =
57 "Usage: remove extension exten@context [priority]\n"
58 " Remove whole extension from context. If priority is set, we are only\n"
59 " removing extension with given priority.\n";
61 static char context_add_include_help[] =
62 "Usage: include context in context\n"
63 " Include context in other context.\n";
65 static char save_dialplan_help[] =
66 "Usage: save dialplan [/path/to/extension/file]\n"
67 " Save dialplan created by pbx_config module.\n"
69 "Example: save dialplan (/etc/asterisk/extensions.conf)\n"
70 " save dialplan /home/markster (/home/markster/extensions.conf)\n";
72 static char context_add_extension_help[] =
73 "Usage: add extension <exten>,<priority>,<app>,<app-data> into <context>\n"
75 " This command will add new extension into <context>. If there is an\n"
76 " existence of extension with the same priority and last 'replace'\n"
77 " arguments is given here we simply replace this extension.\n"
79 "Example: add extension 6123,1,Dial,IAX/216.207.245.56/6123 into local\n"
80 " Now, you can dial 6123 and talk to Markster :)\n";
82 static char context_add_ignorepat_help[] =
83 "Usage: add ignorepat <pattern> into <context>\n"
84 " This command add new ignore pattern into context <context>\n"
86 "Example: add ignorepat _3XX into local\n";
88 static char context_remove_ignorepat_help[] =
89 "Usage: remove ignorepat <pattern> from <context>\n"
90 " This command remove ignore pattern from context <context>\n"
92 "Example: remove ignorepat _3XX from local\n";
94 static char reload_extensions_help[] =
95 "Usage: reload extensions.conf without reloading any other modules\n"
96 " This command does not delete global variables\n"
98 "Example: extensions reload\n";
103 static char *process_quotes_and_slashes(char *start, char find, char replace_with)
105 char *dataPut = start;
109 for (; *start; start++) {
111 *dataPut++ = *start; /* Always goes verbatim */
114 if (*start == '\\') {
115 inEscape = 1; /* Do not copy \ into the data */
116 } else if (*start == '\'') {
117 inQuotes = 1-inQuotes; /* Do not copy ' into the data */
119 /* Replace , with |, unless in quotes */
120 *dataPut++ = inQuotes ? *start : ((*start==find) ? replace_with : *start);
129 * Implementation of functions provided by this module
133 * REMOVE INCLUDE command stuff
135 static int handle_context_dont_include(int fd, int argc, char *argv[])
137 if (argc != 5) return RESULT_SHOWUSAGE;
139 if (strcmp(argv[3], "in")) return RESULT_SHOWUSAGE;
141 if (!ast_context_remove_include(argv[4], argv[2], registrar)) {
142 ast_cli(fd, "We are not including '%s' in '%s' now\n",
144 return RESULT_SUCCESS;
147 ast_cli(fd, "Failed to remove '%s' include from '%s' context\n",
149 return RESULT_FAILURE;
152 static char *complete_context_dont_include(char *line, char *word,
158 * Context completion ...
161 struct ast_context *c;
163 if (ast_lock_contexts()) {
164 ast_log(LOG_ERROR, "Failed to lock context list\n");
168 /* walk pbx_get_contexts ... */
169 c = ast_walk_contexts(NULL);
171 struct ast_include *i;
173 if (ast_lock_context(c)) {
174 c = ast_walk_contexts(c);
178 i = ast_walk_context_includes(c, NULL);
181 !strncmp(ast_get_include_name(i), word, strlen(word))) {
182 struct ast_context *nc;
183 int already_served = 0;
185 /* check if this include is already served or not */
187 /* go through all contexts again till we reach actuall
188 * context or already_served = 1
190 nc = ast_walk_contexts(NULL);
191 while (nc && nc != c && !already_served) {
192 if (!ast_lock_context(nc)) {
193 struct ast_include *ni;
195 ni = ast_walk_context_includes(nc, NULL);
196 while (ni && !already_served) {
197 if (!strcmp(ast_get_include_name(i),
198 ast_get_include_name(ni)))
200 ni = ast_walk_context_includes(nc, ni);
203 ast_unlock_context(nc);
205 nc = ast_walk_contexts(nc);
208 if (!already_served) {
209 if (++which > state) {
211 strdup(ast_get_include_name(i));
212 ast_unlock_context(c);
213 ast_unlock_contexts();
218 i = ast_walk_context_includes(c, i);
221 ast_unlock_context(c);
222 c = ast_walk_contexts(c);
225 ast_unlock_contexts();
230 * 'in' completion ... (complete only if previous context is really
231 * included somewhere)
234 struct ast_context *c;
235 char *context, *dupline, *duplinet;
237 if (state > 0) return NULL;
239 /* take 'context' from line ... */
240 if (!(dupline = strdup(line))) {
241 ast_log(LOG_ERROR, "Out of free memory\n");
246 strsep(&duplinet, " "); /* skip 'dont' */
247 strsep(&duplinet, " "); /* skip 'include' */
248 context = strsep(&duplinet, " ");
255 if (ast_lock_contexts()) {
256 ast_log(LOG_WARNING, "Failed to lock contexts list\n");
261 /* go through all contexts and check if is included ... */
262 c = ast_walk_contexts(NULL);
264 struct ast_include *i;
265 if (ast_lock_context(c)) {
267 ast_unlock_contexts();
271 i = ast_walk_context_includes(c, NULL);
273 /* is it our context? */
274 if (!strcmp(ast_get_include_name(i), context)) {
275 /* yes, it is, context is really included, so
276 * complete "in" command
279 ast_unlock_context(c);
280 ast_unlock_contexts();
283 i = ast_walk_context_includes(c, i);
285 ast_unlock_context(c);
286 c = ast_walk_contexts(c);
289 ast_unlock_contexts();
294 * Context from which we removing include ...
297 struct ast_context *c;
298 char *context, *dupline, *duplinet, *in;
300 if (!(dupline = strdup(line))) {
301 ast_log(LOG_ERROR, "Out of free memory\n");
307 strsep(&duplinet, " "); /* skip 'dont' */
308 strsep(&duplinet, " "); /* skip 'include' */
310 if (!(context = strsep(&duplinet, " "))) {
315 /* third word must be in */
316 in = strsep(&duplinet, " ");
323 if (ast_lock_contexts()) {
324 ast_log(LOG_ERROR, "Failed to lock context list\n");
329 /* walk through all contexts ... */
330 c = ast_walk_contexts(NULL);
332 struct ast_include *i;
333 if (ast_lock_context(c)) {
338 /* walk through all includes and check if it is our context */
339 i = ast_walk_context_includes(c, NULL);
341 /* is in this context included another on which we want to
344 if (!strcmp(context, ast_get_include_name(i))) {
345 /* yes, it's included, is matching our word too? */
346 if (!strncmp(ast_get_context_name(c),
347 word, strlen(word))) {
348 /* check state for completion */
349 if (++which > state) {
350 char *res = strdup(ast_get_context_name(c));
352 ast_unlock_context(c);
353 ast_unlock_contexts();
359 i = ast_walk_context_includes(c, i);
361 ast_unlock_context(c);
362 c = ast_walk_contexts(c);
366 ast_unlock_contexts();
374 * REMOVE EXTENSION command stuff
376 static int handle_context_remove_extension(int fd, int argc, char *argv[])
378 int removing_priority = 0;
379 char *exten, *context;
381 if (argc != 4 && argc != 3) return RESULT_SHOWUSAGE;
384 * Priority input checking ...
389 /* check for digits in whole parameter for right priority ...
390 * why? because atoi (strtol) returns 0 if any characters in
391 * string and whole extension will be removed, it's not good
393 if (strcmp("hint", c)) {
395 if (!isdigit(*c++)) {
396 ast_cli(fd, "Invalid priority '%s'\n", argv[3]);
397 return RESULT_FAILURE;
400 removing_priority = atoi(argv[3]);
402 removing_priority = PRIORITY_HINT;
404 if (removing_priority == 0) {
405 ast_cli(fd, "If you want to remove whole extension, please " \
406 "omit priority argument\n");
407 return RESULT_FAILURE;
412 * Format exten@context checking ...
414 if (!(context = strchr(argv[2], (int)'@'))) {
415 ast_cli(fd, "First argument must be in exten@context format\n");
416 return RESULT_FAILURE;
421 if ((!strlen(exten)) || (!(strlen(context)))) {
422 ast_cli(fd, "Missing extension or context name in second argument '%s@%s'\n",
423 exten == NULL ? "?" : exten, context == NULL ? "?" : context);
424 return RESULT_FAILURE;
427 if (!ast_context_remove_extension(context, exten, removing_priority, registrar)) {
428 if (!removing_priority)
429 ast_cli(fd, "Whole extension %s@%s removed\n",
432 ast_cli(fd, "Extension %s@%s with priority %d removed\n",
433 exten, context, removing_priority);
435 return RESULT_SUCCESS;
438 ast_cli(fd, "Failed to remove extension %s@%s\n", exten, context);
440 return RESULT_FAILURE;
443 #define BROKEN_READLINE 1
445 #ifdef BROKEN_READLINE
447 * There is one funny thing, when you have word like 300@ and you hit
448 * <tab>, you arguments will like as your word is '300 ', so it '@'
449 * characters acts sometimes as word delimiter and sometimes as a part
452 * This fix function, allocates new word variable and store here every
453 * time xxx@yyy always as one word and correct pos is set too
455 * It's ugly, I know, but I'm waiting for Mark suggestion if upper is
458 static int fix_complete_args(char *line, char **word, int *pos)
460 char *_line, *_strsep_line, *_previous_word = NULL, *_word = NULL;
463 _line = strdup(line);
465 _strsep_line = _line;
466 while (_strsep_line) {
467 _previous_word = _word;
468 _word = strsep(&_strsep_line, " ");
470 if (_word && strlen(_word)) words++;
474 if (_word || _previous_word) {
476 if (!strlen(_word)) words++;
477 *word = strdup(_word);
479 *word = strdup(_previous_word);
488 #endif /* BROKEN_READLINE */
490 static char *complete_context_remove_extension(char *line, char *word, int pos,
496 #ifdef BROKEN_READLINE
498 * Fix arguments, *word is a new allocated structure, REMEMBER to
499 * free *word when you want to return from this function ...
501 if (fix_complete_args(line, &word, &pos)) {
502 ast_log(LOG_ERROR, "Out of free memory\n");
508 * exten@context completion ...
511 struct ast_context *c;
513 char *context = NULL, *exten = NULL, *delim = NULL;
515 /* now, parse values from word = exten@context */
516 if ((delim = strchr(word, (int)'@'))) {
517 /* check for duplicity ... */
518 if (delim != strrchr(word, (int)'@')) {
519 #ifdef BROKEN_READLINE
526 exten = strdup(word);
527 context = strdup(delim + 1);
530 exten = strdup(word);
532 #ifdef BROKEN_READLINE
536 if (ast_lock_contexts()) {
537 ast_log(LOG_ERROR, "Failed to lock context list\n");
538 free(context); free(exten);
542 /* find our context ... */
543 c = ast_walk_contexts(NULL);
546 if ( (!context || !strlen(context)) || /* if no input, all contexts ... */
547 (context && !strncmp(ast_get_context_name(c),
548 context, strlen(context))) ) { /* if input, compare ... */
549 /* try to complete extensions ... */
550 e = ast_walk_context_extensions(c, NULL);
553 if ( (!exten || !strlen(exten)) || /* if not input, all extensions ... */
554 (exten && !strncmp(ast_get_extension_name(e), exten,
555 strlen(exten))) ) { /* if input, compare ... */
556 if (++which > state) {
557 /* If there is an extension then return
561 ret = malloc(strlen(ast_get_extension_name(e)) +
562 strlen(ast_get_context_name(c)) + 2);
564 sprintf(ret, "%s@%s", ast_get_extension_name(e),
565 ast_get_context_name(c));
567 free(exten); free(context);
569 ast_unlock_contexts();
574 e = ast_walk_context_extensions(c, e);
577 c = ast_walk_contexts(c);
580 ast_unlock_contexts();
582 free(exten); free(context);
588 * Complete priority ...
591 char *delim, *exten, *context, *dupline, *duplinet, *ec;
592 struct ast_context *c;
594 dupline = strdup(line);
596 #ifdef BROKEN_READLINE
603 strsep(&duplinet, " "); /* skip 'remove' */
604 strsep(&duplinet, " "); /* skip 'extension */
606 if (!(ec = strsep(&duplinet, " "))) {
608 #ifdef BROKEN_READLINE
614 /* wrong exten@context format? */
615 if (!(delim = strchr(ec, (int)'@')) ||
616 (strchr(ec, (int)'@') != strrchr(ec, (int)'@'))) {
617 #ifdef BROKEN_READLINE
624 /* check if there is exten and context too ... */
626 if ((!strlen(ec)) || (!strlen(delim + 1))) {
627 #ifdef BROKEN_READLINE
635 context = strdup(delim + 1);
638 if (ast_lock_contexts()) {
639 ast_log(LOG_ERROR, "Failed to lock context list\n");
640 #ifdef BROKEN_READLINE
643 free(exten); free(context);
648 c = ast_walk_contexts(NULL);
650 if (!strcmp(ast_get_context_name(c), context)) {
653 /* walk extensions */
655 e = ast_walk_context_extensions(c, NULL);
657 if (!strcmp(ast_get_extension_name(e), exten)) {
658 struct ast_exten *priority;
662 priority = ast_walk_extension_priorities(e, NULL);
663 /* serve priorities */
665 snprintf(buffer, 10, "%u",
666 ast_get_extension_priority(priority));
667 if (!strncmp(word, buffer, strlen(word))) {
668 if (++which > state) {
669 #ifdef BROKEN_READLINE
672 ast_unlock_contexts();
673 return strdup(buffer);
676 priority = ast_walk_extension_priorities(e,
680 #ifdef BROKEN_READLINE
683 ast_unlock_contexts();
686 e = ast_walk_context_extensions(c, e);
688 #ifdef BROKEN_READLINE
692 ast_unlock_contexts();
695 c = ast_walk_contexts(c);
698 #ifdef BROKEN_READLINE
701 free(exten); free(context);
703 ast_unlock_contexts();
707 #ifdef BROKEN_READLINE
714 * Include context ...
716 static int handle_context_add_include(int fd, int argc, char *argv[])
718 if (argc != 5) return RESULT_SHOWUSAGE;
720 /* third arg must be 'in' ... */
721 if (strcmp(argv[3], "in")) return RESULT_SHOWUSAGE;
723 if (ast_context_add_include(argv[4], argv[2], registrar)) {
726 ast_cli(fd, "Out of memory for context addition\n"); break;
729 ast_cli(fd, "Failed to lock context(s) list, please try again later\n"); break;
732 ast_cli(fd, "Context '%s' already included in '%s' context\n",
733 argv[1], argv[3]); break;
737 ast_cli(fd, "There is no existence of context '%s'\n",
738 errno == ENOENT ? argv[4] : argv[2]); break;
741 ast_cli(fd, "Failed to include '%s' in '%s' context\n",
742 argv[1], argv[3]); break;
744 return RESULT_FAILURE;
747 /* show some info ... */
748 ast_cli(fd, "Context '%s' included in '%s' context\n",
751 return RESULT_SUCCESS;
754 static char *complete_context_add_include(char *line, char *word, int pos,
757 struct ast_context *c;
760 /* server context for inclusion ... */
763 if (ast_lock_contexts()) {
764 ast_log(LOG_ERROR, "Failed to lock context list\n");
768 /* server all contexts */
769 c = ast_walk_contexts(NULL);
771 if ((!strlen(word) ||
772 !strncmp(ast_get_context_name(c), word, strlen(word))) &&
775 char *context = strdup(ast_get_context_name(c));
776 ast_unlock_contexts();
779 c = ast_walk_contexts(c);
782 ast_unlock_contexts();
785 /* complete 'in' only if context exist ... */
788 char *context, *dupline, *duplinet;
790 if (state != 0) return NULL;
792 /* parse context from line ... */
793 if (!(dupline = strdup(line))) {
794 ast_log(LOG_ERROR, "Out of free memory\n");
795 if (state == 0) return strdup("in");
801 strsep(&duplinet, " ");
802 context = strsep(&duplinet, " ");
804 struct ast_context *c;
805 int context_existence = 0;
807 /* check for context existence ... */
808 if (ast_lock_contexts()) {
809 ast_log(LOG_ERROR, "Failed to lock context list\n");
811 /* our fault, we can't check, so complete 'in' ... */
815 c = ast_walk_contexts(NULL);
816 while (c && !context_existence) {
817 if (!strcmp(context, ast_get_context_name(c))) {
818 context_existence = 1;
821 c = ast_walk_contexts(c);
824 /* if context exists, return 'into' ... */
825 if (context_existence) {
827 ast_unlock_contexts();
828 return strdup("into");
831 ast_unlock_contexts();
838 /* serve context into which we include another context */
841 char *context, *dupline, *duplinet, *in;
842 int context_existence = 0;
844 if (!(dupline = strdup(line))) {
845 ast_log(LOG_ERROR, "Out of free memory\n");
851 strsep(&duplinet, " "); /* skip 'include' */
852 context = strsep(&duplinet, " ");
853 in = strsep(&duplinet, " ");
855 /* given some context and third word is in? */
856 if (!strlen(context) || strcmp(in, "in")) {
861 if (ast_lock_contexts()) {
862 ast_log(LOG_ERROR, "Failed to lock context list\n");
867 /* check for context existence ... */
868 c = ast_walk_contexts(NULL);
869 while (c && !context_existence) {
870 if (!strcmp(context, ast_get_context_name(c))) {
871 context_existence = 1;
874 c = ast_walk_contexts(c);
877 if (!context_existence) {
879 ast_unlock_contexts();
883 /* go through all contexts ... */
884 c = ast_walk_contexts(NULL);
886 /* must be different contexts ... */
887 if (strcmp(context, ast_get_context_name(c))) {
888 if (!ast_lock_context(c)) {
889 struct ast_include *i;
892 /* check for duplicity inclusion ... */
893 i = ast_walk_context_includes(c, NULL);
894 while (i && !included) {
895 if (!strcmp(ast_get_include_name(i), context))
897 i = ast_walk_context_includes(c, i);
899 ast_unlock_context(c);
901 /* not included yet, so show possibility ... */
903 !strncmp(ast_get_context_name(c), word, strlen(word))){
905 if (++which > state) {
906 char *res = strdup(ast_get_context_name(c));
908 ast_unlock_contexts();
914 c = ast_walk_contexts(c);
917 ast_unlock_contexts();
926 * 'save dialplan' CLI command implementation functions ...
928 static int handle_save_dialplan(int fd, int argc, char *argv[])
931 struct ast_context *c;
932 struct ast_config *cfg;
933 struct ast_variable *v;
934 int context_header_written;
935 int incomplete = 0; /* incomplete config write? */
938 if (! (static_config && !write_protect_config)) {
940 "I can't save dialplan now, see '%s' example file.\n",
942 return RESULT_FAILURE;
945 if (argc != 2 && argc != 3) return RESULT_SHOWUSAGE;
947 if (ast_mutex_lock(&save_dialplan_lock)) {
949 "Failed to lock dialplan saving (another proccess saving?)\n");
950 return RESULT_FAILURE;
953 /* have config path? */
955 /* is there extension.conf too? */
956 if (!strstr(argv[2], ".conf")) {
957 /* no, only directory path, check for last '/' occurence */
958 if (*(argv[2] + strlen(argv[2]) -1) == '/')
959 snprintf(filename, sizeof(filename), "%s%s",
962 /* without config extensions.conf, add it */
963 snprintf(filename, sizeof(filename), "%s/%s",
966 /* there is an .conf */
967 snprintf(filename, sizeof(filename), argv[2]);
969 /* no config file, default one */
970 snprintf(filename, sizeof(filename), "%s/%s",
971 (char *)ast_config_AST_CONFIG_DIR, config);
973 cfg = ast_load("extensions.conf");
975 /* try to lock contexts list */
976 if (ast_lock_contexts()) {
977 ast_cli(fd, "Failed to lock contexts list\n");
978 ast_mutex_unlock(&save_dialplan_lock);
980 return RESULT_FAILURE;
983 /* create new file ... */
984 if (!(output = fopen(filename, "wt"))) {
985 ast_cli(fd, "Failed to create file '%s'\n",
987 ast_unlock_contexts();
988 ast_mutex_unlock(&save_dialplan_lock);
990 return RESULT_FAILURE;
993 /* fireout general info */
994 fprintf(output, "[general]\nstatic=%s\nwriteprotect=%s\n\n",
995 static_config ? "yes" : "no",
996 write_protect_config ? "yes" : "no");
998 if ((v = ast_variable_browse(cfg, "globals"))) {
999 fprintf(output, "[globals]\n");
1001 fprintf(output, "%s => %s\n", v->name, v->value);
1004 fprintf(output, "\n");
1009 /* walk all contexts */
1010 c = ast_walk_contexts(NULL);
1012 context_header_written = 0;
1014 /* try to lock context and fireout all info */
1015 if (!ast_lock_context(c)) {
1016 struct ast_exten *e, *last_written_e = NULL;
1017 struct ast_include *i;
1018 struct ast_ignorepat *ip;
1020 /* registered by this module? */
1021 if (!strcmp(ast_get_context_registrar(c), registrar)) {
1022 fprintf(output, "[%s]\n", ast_get_context_name(c));
1023 context_header_written = 1;
1026 /* walk extensions ... */
1027 e = ast_walk_context_extensions(c, NULL);
1029 struct ast_exten *p;
1031 /* fireout priorities */
1032 p = ast_walk_extension_priorities(e, NULL);
1034 if (!strcmp(ast_get_extension_registrar(p),
1037 /* make empty line between different extensions */
1038 if (last_written_e != NULL &&
1039 strcmp(ast_get_extension_name(last_written_e),
1040 ast_get_extension_name(p)))
1041 fprintf(output, "\n");
1044 if (!context_header_written) {
1045 fprintf(output, "[%s]\n", ast_get_context_name(c));
1046 context_header_written = 1;
1049 if (ast_get_extension_priority(p)!=PRIORITY_HINT) {
1050 char *tempdata = NULL, *startdata;
1051 tempdata = strdup((char *)ast_get_extension_app_data(p));
1053 startdata = tempdata;
1055 if (*tempdata == '|')
1059 tempdata = startdata;
1061 fprintf(output, "exten => %s,%d,%s(%s)\n",
1062 ast_get_extension_name(p),
1063 ast_get_extension_priority(p),
1064 ast_get_extension_app(p),
1069 fprintf(output, "exten => %s,hint,%s\n",
1070 ast_get_extension_name(p),
1071 ast_get_extension_app(p));
1074 p = ast_walk_extension_priorities(e, p);
1077 e = ast_walk_context_extensions(c, e);
1080 /* written any extensions? ok, write space between exten & inc */
1081 if (last_written_e) fprintf(output, "\n");
1083 /* walk through includes */
1084 i = ast_walk_context_includes(c, NULL);
1086 if (!strcmp(ast_get_include_registrar(i), registrar)) {
1087 if (!context_header_written) {
1088 fprintf(output, "[%s]\n", ast_get_context_name(c));
1089 context_header_written = 1;
1091 fprintf(output, "include => %s\n",
1092 ast_get_include_name(i));
1094 i = ast_walk_context_includes(c, i);
1097 if (ast_walk_context_includes(c, NULL))
1098 fprintf(output, "\n");
1100 /* fireout ignorepats ... */
1101 ip = ast_walk_context_ignorepats(c, NULL);
1103 if (!strcmp(ast_get_ignorepat_registrar(ip), registrar)) {
1104 if (!context_header_written) {
1105 fprintf(output, "[%s]\n", ast_get_context_name(c));
1106 context_header_written = 1;
1109 fprintf(output, "ignorepat => %s\n",
1110 ast_get_ignorepat_name(ip));
1112 ip = ast_walk_context_ignorepats(c, ip);
1115 ast_unlock_context(c);
1119 c = ast_walk_contexts(c);
1122 ast_unlock_contexts();
1123 ast_mutex_unlock(&save_dialplan_lock);
1127 ast_cli(fd, "Saved dialplan is incomplete\n");
1128 return RESULT_FAILURE;
1131 ast_cli(fd, "Dialplane successfully saved into '%s'\n",
1133 return RESULT_SUCCESS;
1137 * ADD EXTENSION command stuff
1139 static int handle_context_add_extension(int fd, int argc, char *argv[])
1142 char *exten, *prior;
1144 char *cidmatch, *app, *app_data;
1147 /* check for arguments at first */
1148 if (argc != 5 && argc != 6) return RESULT_SHOWUSAGE;
1149 if (strcmp(argv[3], "into")) return RESULT_SHOWUSAGE;
1150 if (argc == 6) if (strcmp(argv[5], "replace")) return RESULT_SHOWUSAGE;
1152 whole_exten = argv[2];
1153 exten = strsep(&whole_exten,",");
1154 if (strchr(exten, '/')) {
1156 strsep(&cidmatch,"/");
1160 prior = strsep(&whole_exten,",");
1162 if (!strcmp(prior, "hint")) {
1163 iprior = PRIORITY_HINT;
1165 if (sscanf(prior, "%i", &iprior) != 1) {
1166 ast_cli(fd, "'%s' is not a valid priority\n", prior);
1171 app = strsep(&whole_exten, ",");
1172 if (app && (start = strchr(app, '(')) && (end = strrchr(app, ')'))) {
1173 *start = *end = '\0';
1174 app_data = start + 1;
1175 process_quotes_and_slashes(app_data, ',', '|');
1177 app_data = whole_exten;
1179 if (!exten || !prior || !app || (!app_data && iprior != PRIORITY_HINT)) return RESULT_SHOWUSAGE;
1183 if (ast_add_extension(argv[4], argc == 6 ? 1 : 0, exten, iprior, cidmatch, app,
1184 (void *)strdup(app_data), free, registrar)) {
1187 ast_cli(fd, "Out of free memory\n"); break;
1190 ast_cli(fd, "Failed to lock context(s) list, please try again later\n"); break;
1193 ast_cli(fd, "No existence of '%s' context\n", argv[4]); break;
1196 ast_cli(fd, "Extension %s@%s with priority %s already exists\n",
1197 exten, argv[4], prior); break;
1200 ast_cli(fd, "Failed to add '%s,%s,%s,%s' extension into '%s' context\n",
1201 exten, prior, app, app_data, argv[4]); break;
1203 return RESULT_FAILURE;
1207 ast_cli(fd, "Extension %s@%s (%s) replace by '%s,%s,%s,%s'\n",
1208 exten, argv[4], prior, exten, prior, app, app_data);
1210 ast_cli(fd, "Extension '%s,%s,%s,%s' added into '%s' context\n",
1211 exten, prior, app, app_data, argv[4]);
1213 return RESULT_SUCCESS;
1216 /* add extension 6123,1,Dial,IAX/212.71.138.13/6123 into local */
1217 static char *complete_context_add_extension(char *line, char *word,
1222 /* complete 'into' word ... */
1224 if (state == 0) return strdup("into");
1228 /* complete context */
1230 struct ast_context *c;
1232 /* try to lock contexts list ... */
1233 if (ast_lock_contexts()) {
1234 ast_log(LOG_WARNING, "Failed to lock contexts list\n");
1238 /* walk through all contexts */
1239 c = ast_walk_contexts(NULL);
1241 /* matching context? */
1242 if (!strncmp(ast_get_context_name(c), word, strlen(word))) {
1243 if (++which > state) {
1244 char *res = strdup(ast_get_context_name(c));
1245 ast_unlock_contexts();
1249 c = ast_walk_contexts(c);
1252 ast_unlock_contexts();
1256 if (pos == 5) return state == 0 ? strdup("replace") : NULL;
1262 * IGNOREPAT CLI stuff
1264 static int handle_context_add_ignorepat(int fd, int argc, char *argv[])
1266 if (argc != 5) return RESULT_SHOWUSAGE;
1267 if (strcmp(argv[3], "into")) return RESULT_SHOWUSAGE;
1269 if (ast_context_add_ignorepat(argv[4], argv[2], registrar)) {
1272 ast_cli(fd, "Out of free memory\n"); break;
1275 ast_cli(fd, "There is no existence of '%s' context\n", argv[4]);
1279 ast_cli(fd, "Ignore pattern '%s' already included in '%s' context\n",
1284 ast_cli(fd, "Failed to lock context(s) list, please, try again later\n");
1288 ast_cli(fd, "Failed to add ingore pattern '%s' into '%s' context\n",
1292 return RESULT_FAILURE;
1295 ast_cli(fd, "Ignore pattern '%s' added into '%s' context\n",
1297 return RESULT_SUCCESS;
1300 static char *complete_context_add_ignorepat(char *line, char *word,
1303 if (pos == 3) return state == 0 ? strdup("into") : NULL;
1306 struct ast_context *c;
1308 char *dupline, *duplinet, *ignorepat = NULL;
1310 dupline = strdup(line);
1314 strsep(&duplinet, " "); /* skip 'add' */
1315 strsep(&duplinet, " "); /* skip 'ignorepat' */
1316 ignorepat = strsep(&duplinet, " ");
1319 if (ast_lock_contexts()) {
1320 ast_log(LOG_ERROR, "Failed to lock contexts list\n");
1324 c = ast_walk_contexts(NULL);
1326 if (!strncmp(ast_get_context_name(c), word, strlen(word))) {
1327 int serve_context = 1;
1329 if (!ast_lock_context(c)) {
1330 struct ast_ignorepat *ip;
1331 ip = ast_walk_context_ignorepats(c, NULL);
1332 while (ip && serve_context) {
1333 if (!strcmp(ast_get_ignorepat_name(ip), ignorepat))
1335 ip = ast_walk_context_ignorepats(c, ip);
1337 ast_unlock_context(c);
1340 if (serve_context) {
1341 if (++which > state) {
1342 char *context = strdup(ast_get_context_name(c));
1343 if (dupline) free(dupline);
1344 ast_unlock_contexts();
1349 c = ast_walk_contexts(c);
1352 if (dupline) free(dupline);
1353 ast_unlock_contexts();
1360 static int handle_context_remove_ignorepat(int fd, int argc, char *argv[])
1362 if (argc != 5) return RESULT_SHOWUSAGE;
1363 if (strcmp(argv[3], "from")) return RESULT_SHOWUSAGE;
1365 if (ast_context_remove_ignorepat(argv[4], argv[2], registrar)) {
1368 ast_cli(fd, "Failed to lock context(s) list, please try again later\n");
1372 ast_cli(fd, "There is no existence of '%s' context\n", argv[4]);
1376 ast_cli(fd, "There is no existence of '%s' ignore pattern in '%s' context\n",
1381 ast_cli(fd, "Failed to remove ignore pattern '%s' from '%s' context\n", argv[2], argv[4]);
1384 return RESULT_FAILURE;
1387 ast_cli(fd, "Ignore pattern '%s' removed from '%s' context\n",
1389 return RESULT_SUCCESS;
1392 static int pbx_load_module(void);
1394 static int handle_reload_extensions(int fd, int argc, char *argv[])
1396 if (argc!=2) return RESULT_SHOWUSAGE;
1398 return RESULT_SUCCESS;
1401 static char *complete_context_remove_ignorepat(char *line, char *word,
1404 struct ast_context *c;
1408 if (ast_lock_contexts()) {
1409 ast_log(LOG_WARNING, "Failed to lock contexts list\n");
1413 c = ast_walk_contexts(NULL);
1415 if (!ast_lock_context(c)) {
1416 struct ast_ignorepat *ip;
1418 ip = ast_walk_context_ignorepats(c, NULL);
1420 if (!strncmp(ast_get_ignorepat_name(ip), word, strlen(word))) {
1421 if (which + 1 > state) {
1422 struct ast_context *cw;
1423 int already_served = 0;
1424 cw = ast_walk_contexts(NULL);
1425 while (cw && cw != c && !already_served) {
1426 if (!ast_lock_context(cw)) {
1427 struct ast_ignorepat *ipw;
1428 ipw = ast_walk_context_ignorepats(cw, NULL);
1430 if (!strcmp(ast_get_ignorepat_name(ipw),
1431 ast_get_ignorepat_name(ip))) already_served = 1;
1432 ipw = ast_walk_context_ignorepats(cw, ipw);
1434 ast_unlock_context(cw);
1436 cw = ast_walk_contexts(cw);
1438 if (!already_served) {
1439 char *ret = strdup(ast_get_ignorepat_name(ip));
1440 ast_unlock_context(c);
1441 ast_unlock_contexts();
1447 ip = ast_walk_context_ignorepats(c, ip);
1450 ast_unlock_context(c);
1452 c = ast_walk_contexts(c);
1455 ast_unlock_contexts();
1459 if (pos == 3) return state == 0 ? strdup("from") : NULL;
1462 char *dupline, *duplinet, *ignorepat;
1464 dupline = strdup(line);
1466 ast_log(LOG_WARNING, "Out of free memory\n");
1471 strsep(&duplinet, " ");
1472 strsep(&duplinet, " ");
1473 ignorepat = strsep(&duplinet, " ");
1480 if (ast_lock_contexts()) {
1481 ast_log(LOG_WARNING, "Failed to lock contexts list\n");
1486 c = ast_walk_contexts(NULL);
1488 if (!ast_lock_context(c)) {
1489 struct ast_ignorepat *ip;
1490 ip = ast_walk_context_ignorepats(c, NULL);
1492 if (!strcmp(ast_get_ignorepat_name(ip), ignorepat)) {
1493 if (!strncmp(ast_get_context_name(c), word, strlen(word))) {
1494 if (++which > state) {
1495 char *ret = strdup(ast_get_context_name(c));
1497 ast_unlock_context(c);
1498 ast_unlock_contexts();
1503 ip = ast_walk_context_ignorepats(c, ip);
1506 ast_unlock_context(c);
1508 c = ast_walk_contexts(c);
1512 ast_unlock_contexts();
1520 * CLI entries for commands provided by this module
1522 static struct ast_cli_entry context_dont_include_cli =
1523 { { "dont", "include", NULL }, handle_context_dont_include,
1524 "Remove a specified include from context", context_dont_include_help,
1525 complete_context_dont_include };
1527 static struct ast_cli_entry context_remove_extension_cli =
1528 { { "remove", "extension", NULL }, handle_context_remove_extension,
1529 "Remove a specified extension", context_remove_extension_help,
1530 complete_context_remove_extension };
1532 static struct ast_cli_entry context_add_include_cli =
1533 { { "include", "context", NULL }, handle_context_add_include,
1534 "Include context in other context", context_add_include_help,
1535 complete_context_add_include };
1537 static struct ast_cli_entry save_dialplan_cli =
1538 { { "save", "dialplan", NULL }, handle_save_dialplan,
1539 "Save dialplan", save_dialplan_help };
1541 static struct ast_cli_entry context_add_extension_cli =
1542 { { "add", "extension", NULL }, handle_context_add_extension,
1543 "Add new extension into context", context_add_extension_help,
1544 complete_context_add_extension };
1546 static struct ast_cli_entry context_add_ignorepat_cli =
1547 { { "add", "ignorepat", NULL }, handle_context_add_ignorepat,
1548 "Add new ignore pattern", context_add_ignorepat_help,
1549 complete_context_add_ignorepat };
1551 static struct ast_cli_entry context_remove_ignorepat_cli =
1552 { { "remove", "ignorepat", NULL }, handle_context_remove_ignorepat,
1553 "Remove ignore pattern from context", context_remove_ignorepat_help,
1554 complete_context_remove_ignorepat };
1556 static struct ast_cli_entry reload_extensions_cli =
1557 { { "extensions", "reload", NULL}, handle_reload_extensions,
1558 "Reload extensions and *only* extensions", reload_extensions_help };
1561 * Standard module functions ...
1563 int unload_module(void)
1565 ast_cli_unregister(&context_add_extension_cli);
1566 if (static_config && !write_protect_config)
1567 ast_cli_unregister(&save_dialplan_cli);
1568 ast_cli_unregister(&context_add_include_cli);
1569 ast_cli_unregister(&context_dont_include_cli);
1570 ast_cli_unregister(&context_remove_extension_cli);
1571 ast_cli_unregister(&context_remove_ignorepat_cli);
1572 ast_cli_unregister(&context_add_ignorepat_cli);
1573 ast_cli_unregister(&reload_extensions_cli);
1574 ast_context_destroy(NULL, registrar);
1578 static int pbx_load_module(void)
1580 struct ast_config *cfg;
1581 struct ast_variable *v;
1582 char *cxt, *ext, *pri, *appl, *data, *tc, *cidmatch;
1583 struct ast_context *con;
1585 char realvalue[256];
1587 cfg = ast_load(config);
1589 /* Use existing config to populate the PBX table */
1590 static_config = ast_true(ast_variable_retrieve(cfg, "general",
1592 write_protect_config = ast_true(ast_variable_retrieve(cfg, "general",
1594 v = ast_variable_browse(cfg, "globals");
1596 memset(realvalue, 0, sizeof(realvalue));
1597 pbx_substitute_variables_helper(NULL, v->value, realvalue, sizeof(realvalue) - 1);
1598 pbx_builtin_setvar_helper(NULL, v->name, realvalue);
1601 cxt = ast_category_browse(cfg, NULL);
1603 /* All categories but "general" or "globals" are considered contexts */
1604 if (!strcasecmp(cxt, "general") || !strcasecmp(cxt, "globals")) {
1605 cxt = ast_category_browse(cfg, cxt);
1608 if ((con=ast_context_create(&local_contexts,cxt, registrar))) {
1609 v = ast_variable_browse(cfg, cxt);
1611 if (!strcasecmp(v->name, "exten")) {
1614 char realext[256]="";
1615 tc = strdup(v->value);
1618 ext = strsep(&stringp, ",");
1621 pri = strsep(&stringp, ",");
1624 if (!strcmp(pri,"hint"))
1627 if (sscanf(pri, "%i", &ipri) != 1) {
1628 ast_log(LOG_WARNING, "Invalid priority '%s' at line %d\n", pri, v->lineno);
1635 if (!(start = strchr(appl, '('))) {
1637 appl = strsep(&stringp, ",");
1641 if (start && (end = strrchr(appl, ')'))) {
1642 *start = *end = '\0';
1644 process_quotes_and_slashes(data, ',', '|');
1645 } else if (stringp!=NULL && *stringp=='"') {
1647 data = strsep(&stringp, "\"");
1651 data = strsep(&stringp, ",");
1655 cidmatch = strchr(ext, '/');
1661 strsep(&stringp, "/");
1665 while(*appl && (*appl < 33)) appl++;
1666 pbx_substitute_variables_helper(NULL, ext, realext, sizeof(realext) - 1);
1668 if (ast_add_extension2(con, 0, realext, ipri, cidmatch, appl, strdup(data), FREE, registrar)) {
1669 ast_log(LOG_WARNING, "Unable to register extension at line %d\n", v->lineno);
1673 } else fprintf(stderr,"Error strdup returned NULL in %s\n",__PRETTY_FUNCTION__);
1674 } else if(!strcasecmp(v->name, "include")) {
1675 memset(realvalue, 0, sizeof(realvalue));
1676 pbx_substitute_variables_helper(NULL, v->value, realvalue, sizeof(realvalue) - 1);
1677 if (ast_context_add_include2(con, realvalue, registrar))
1678 ast_log(LOG_WARNING, "Unable to include context '%s' in context '%s'\n", v->value, cxt);
1679 } else if(!strcasecmp(v->name, "ignorepat")) {
1680 memset(realvalue, 0, sizeof(realvalue));
1681 pbx_substitute_variables_helper(NULL, v->value, realvalue, sizeof(realvalue) - 1);
1682 if (ast_context_add_ignorepat2(con, realvalue, registrar))
1683 ast_log(LOG_WARNING, "Unable to include ignorepat '%s' in context '%s'\n", v->value, cxt);
1684 } else if (!strcasecmp(v->name, "switch")) {
1686 memset(realvalue, 0, sizeof(realvalue));
1687 pbx_substitute_variables_helper(NULL, v->value, realvalue, sizeof(realvalue) - 1);
1690 appl = strsep(&stringp, "/");
1691 data = strsep(&stringp, "");
1694 if (ast_context_add_switch2(con, appl, data, registrar))
1695 ast_log(LOG_WARNING, "Unable to include switch '%s' in context '%s'\n", v->value, cxt);
1700 cxt = ast_category_browse(cfg, cxt);
1704 ast_merge_contexts_and_delete(&local_contexts,registrar);
1706 for (con = ast_walk_contexts(NULL); con; con = ast_walk_contexts(con))
1707 ast_context_verify_includes(con);
1712 int load_module(void)
1714 if (pbx_load_module()) return -1;
1716 ast_cli_register(&context_remove_extension_cli);
1717 ast_cli_register(&context_dont_include_cli);
1718 ast_cli_register(&context_add_include_cli);
1719 if (static_config && !write_protect_config)
1720 ast_cli_register(&save_dialplan_cli);
1721 ast_cli_register(&context_add_extension_cli);
1722 ast_cli_register(&context_add_ignorepat_cli);
1723 ast_cli_register(&context_remove_ignorepat_cli);
1724 ast_cli_register(&reload_extensions_cli);
1731 ast_context_destroy(NULL, registrar);
1732 /* For martin's global variables, don't clear them on reload */
1734 pbx_builtin_clear_globals();
1745 char *description(void)
1752 return ASTERISK_GPL_KEY;