69b66d5c2cf3ee6cccb56f86877526c26f816bf8
[asterisk/asterisk.git] / pbx / pbx_config.c
1 /*
2  * Asterisk -- A telephony toolkit for Linux.
3  *
4  * Populate and remember extensions from static config file
5  * 
6  * Copyright (C) 1999 - 2005, Digium, Inc.
7  *
8  * Mark Spencer <markster@digium.com>
9  *
10  * This program is free software, distributed under the terms of
11  * the GNU General Public License
12  */
13
14 #include <sys/types.h>
15 #include <stdlib.h>
16 #include <stdio.h>
17 #include <string.h>
18 #include <ctype.h>
19 #include <errno.h>
20
21 #include "asterisk.h"
22
23 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
24
25 #include "asterisk/pbx.h"
26 #include "asterisk/config.h"
27 #include "asterisk/options.h"
28 #include "asterisk/module.h"
29 #include "asterisk/logger.h"
30 #include "asterisk/cli.h"
31 #include "asterisk/callerid.h"
32
33 #ifdef __AST_DEBUG_MALLOC
34 static void FREE(void *ptr)
35 {
36         free(ptr);
37 }
38 #else
39 #define FREE free
40 #endif
41
42 static char *dtext = "Text Extension Configuration";
43 static char *config = "extensions.conf";
44 static char *registrar = "pbx_config";
45
46 static int static_config = 0;
47 static int write_protect_config = 1;
48 static int autofallthrough_config = 0;
49 static int clearglobalvars_config = 0;
50
51 AST_MUTEX_DEFINE_STATIC(save_dialplan_lock);
52
53 static struct ast_context *local_contexts = NULL;
54
55 /*
56  * Help for commands provided by this module ...
57  */
58 static char context_dont_include_help[] =
59 "Usage: dont include <context> in <context>\n"
60 "       Remove an included context from another context.\n";
61
62 static char context_remove_extension_help[] =
63 "Usage: remove extension exten@context [priority]\n"
64 "       Remove an extension from a given context. If a priority\n"
65 "       is given, only that specific priority from the given extension\n"
66 "       will be removed.\n";
67
68 static char context_add_include_help[] =
69 "Usage: include <context> in <context>\n"
70 "       Include a context in another context.\n";
71
72 static char save_dialplan_help[] =
73 "Usage: save dialplan [/path/to/extension/file]\n"
74 "       Save dialplan created by pbx_config module.\n"
75 "\n"
76 "Example: save dialplan                 (/etc/asterisk/extensions.conf)\n"
77 "         save dialplan /home/markster  (/home/markster/extensions.conf)\n";
78
79 static char context_add_extension_help[] =
80 "Usage: add extension <exten>,<priority>,<app>,<app-data> into <context>\n"
81 "       [replace]\n\n"
82 "       This command will add new extension into <context>. If there is an\n"
83 "       existence of extension with the same priority and last 'replace'\n"
84 "       arguments is given here we simply replace this extension.\n"
85 "\n"
86 "Example: add extension 6123,1,Dial,IAX/216.207.245.56/6123 into local\n"
87 "         Now, you can dial 6123 and talk to Markster :)\n";
88
89 static char context_add_ignorepat_help[] =
90 "Usage: add ignorepat <pattern> into <context>\n"
91 "       This command adds a new ignore pattern into context <context>\n"
92 "\n"
93 "Example: add ignorepat _3XX into local\n";
94
95 static char context_remove_ignorepat_help[] =
96 "Usage: remove ignorepat <pattern> from <context>\n"
97 "       This command removes an ignore pattern from context <context>\n"
98 "\n"
99 "Example: remove ignorepat _3XX from local\n";
100
101 static char reload_extensions_help[] =
102 "Usage: reload extensions.conf without reloading any other modules\n"
103 "       This command does not delete global variables unless\n"
104 "       clearglobalvars is set to yes in extensions.conf\n"
105 "\n"
106 "Example: extensions reload\n";
107
108 /*
109  * Static code
110  */
111 static char *process_quotes_and_slashes(char *start, char find, char replace_with)
112 {
113         char *dataPut = start;
114         int inEscape = 0;
115         int inQuotes = 0;
116
117         for (; *start; start++) {
118                 if (inEscape) {
119                         *dataPut++ = *start;       /* Always goes verbatim */
120                         inEscape = 0;
121                 } else {
122                         if (*start == '\\') {
123                                 inEscape = 1;      /* Do not copy \ into the data */
124                         } else if (*start == '\'') {
125                                 inQuotes = 1-inQuotes;   /* Do not copy ' into the data */
126                         } else {
127                                 /* Replace , with |, unless in quotes */
128                                 *dataPut++ = inQuotes ? *start : ((*start==find) ? replace_with : *start);
129                         }
130                 }
131         }
132         *dataPut = 0;
133         return dataPut;
134 }
135
136 /*
137  * Implementation of functions provided by this module
138  */
139
140 /*
141  * REMOVE INCLUDE command stuff
142  */
143 static int handle_context_dont_include(int fd, int argc, char *argv[])
144 {
145         if (argc != 5) return RESULT_SHOWUSAGE;
146
147         if (strcmp(argv[3], "in")) return RESULT_SHOWUSAGE;
148
149         if (!ast_context_remove_include(argv[4], argv[2], registrar)) {
150                 ast_cli(fd, "We are not including '%s' in '%s' now\n",
151                         argv[2], argv[4]);
152                 return RESULT_SUCCESS;
153         }
154
155         ast_cli(fd, "Failed to remove '%s' include from '%s' context\n",
156                 argv[2], argv[4]);
157         return RESULT_FAILURE;
158 }
159
160 static char *complete_context_dont_include(char *line, char *word,
161         int pos, int state)
162 {
163         int which = 0;
164
165         /*
166          * Context completion ...
167          */
168         if (pos == 2) {
169                 struct ast_context *c;
170
171                 if (ast_lock_contexts()) {
172                         ast_log(LOG_ERROR, "Failed to lock context list\n");
173                         return NULL;
174                 }
175
176                 /* walk pbx_get_contexts ... */
177                 c = ast_walk_contexts(NULL); 
178                 while (c) {
179                         struct ast_include *i;
180
181                         if (ast_lock_context(c)) {
182                                 c = ast_walk_contexts(c);
183                                 continue;
184                         }
185
186                         i = ast_walk_context_includes(c, NULL);
187                         while (i) {
188                                 if (!strlen(word) ||
189                                         !strncmp(ast_get_include_name(i), word, strlen(word))) {
190                                         struct ast_context *nc;
191                                         int already_served = 0;
192
193                                         /* check if this include is already served or not */
194
195                                         /* go through all contexts again till we reach actuall
196                                          * context or already_served = 1
197                                          */
198                                         nc = ast_walk_contexts(NULL);
199                                         while (nc && nc != c && !already_served) {
200                                                 if (!ast_lock_context(nc)) {
201                                                         struct ast_include *ni;
202
203                                                         ni = ast_walk_context_includes(nc, NULL);
204                                                         while (ni && !already_served) {
205                                                                 if (!strcmp(ast_get_include_name(i),
206                                                                         ast_get_include_name(ni)))
207                                                                         already_served = 1;
208                                                                 ni = ast_walk_context_includes(nc, ni);
209                                                         }       
210                                                         
211                                                         ast_unlock_context(nc);
212                                                 }
213                                                 nc = ast_walk_contexts(nc);
214                                         }
215
216                                         if (!already_served) {
217                                                 if (++which > state) {
218                                                         char *res =
219                                                                 strdup(ast_get_include_name(i));
220                                                         ast_unlock_context(c);
221                                                         ast_unlock_contexts();
222                                                         return res;
223                                                 }
224                                         }
225                                 }
226                                 i = ast_walk_context_includes(c, i);
227                         }
228
229                         ast_unlock_context(c);
230                         c = ast_walk_contexts(c);
231                 }
232
233                 ast_unlock_contexts();
234                 return NULL;
235         }
236
237         /*
238          * 'in' completion ... (complete only if previous context is really
239          * included somewhere)
240          */
241         if (pos == 3) {
242                 struct ast_context *c;
243                 char *context, *dupline, *duplinet;
244
245                 if (state > 0) return NULL;
246
247                 /* take 'context' from line ... */
248                 if (!(dupline = strdup(line))) {
249                         ast_log(LOG_ERROR, "Out of free memory\n");
250                         return NULL;
251                 }
252
253                 duplinet = dupline;
254                 strsep(&duplinet, " "); /* skip 'dont' */
255                 strsep(&duplinet, " "); /* skip 'include' */
256                 context = strsep(&duplinet, " ");
257
258                 if (!context) {
259                         free(dupline);
260                         return NULL;
261                 }
262
263                 if (ast_lock_contexts()) {
264                         ast_log(LOG_WARNING, "Failed to lock contexts list\n");
265                         free(dupline);
266                         return NULL;
267                 }
268
269                 /* go through all contexts and check if is included ... */
270                 c = ast_walk_contexts(NULL);
271                 while (c) {
272                         struct ast_include *i;
273                         if (ast_lock_context(c)) {
274                                 free(dupline);
275                                 ast_unlock_contexts();
276                                 return NULL;
277                         }
278
279                         i = ast_walk_context_includes(c, NULL);
280                         while (i) {
281                                 /* is it our context? */
282                                 if (!strcmp(ast_get_include_name(i), context)) {
283                                         /* yes, it is, context is really included, so
284                                          * complete "in" command
285                                          */
286                                         free(dupline);
287                                         ast_unlock_context(c);
288                                         ast_unlock_contexts();
289                                         return strdup("in");
290                                 }
291                                 i = ast_walk_context_includes(c, i);
292                         }
293                         ast_unlock_context(c);
294                         c = ast_walk_contexts(c);
295                 }
296                 free(dupline);
297                 ast_unlock_contexts();
298                 return NULL;
299         }
300
301         /*
302          * Context from which we removing include ... 
303          */
304         if (pos == 4) {
305                 struct ast_context *c;
306                 char *context, *dupline, *duplinet, *in;
307
308                 if (!(dupline = strdup(line))) {
309                         ast_log(LOG_ERROR, "Out of free memory\n");
310                         return NULL;
311                 }
312
313                 duplinet = dupline;
314
315                 strsep(&duplinet, " "); /* skip 'dont' */
316                 strsep(&duplinet, " "); /* skip 'include' */
317
318                 if (!(context = strsep(&duplinet, " "))) {
319                         free(dupline);
320                         return NULL;
321                 }
322
323                 /* third word must be in */
324                 in = strsep(&duplinet, " ");
325                 if (!in ||
326                         strcmp(in, "in")) {
327                         free(dupline);
328                         return NULL;
329                 }
330
331                 if (ast_lock_contexts()) {
332                         ast_log(LOG_ERROR, "Failed to lock context list\n");
333                         free(dupline);
334                         return NULL;
335                 }
336
337                 /* walk through all contexts ... */
338                 c = ast_walk_contexts(NULL);
339                 while (c) {
340                         struct ast_include *i;
341                         if (ast_lock_context(c)) {
342                                 free(dupline);
343                                 return NULL;
344                         }
345         
346                         /* walk through all includes and check if it is our context */  
347                         i = ast_walk_context_includes(c, NULL);
348                         while (i) {
349                                 /* is in this context included another on which we want to
350                                  * remove?
351                                  */
352                                 if (!strcmp(context, ast_get_include_name(i))) {
353                                         /* yes, it's included, is matching our word too? */
354                                         if (!strncmp(ast_get_context_name(c),
355                                                         word, strlen(word))) {
356                                                 /* check state for completion */
357                                                 if (++which > state) {
358                                                         char *res = strdup(ast_get_context_name(c));
359                                                         free(dupline);
360                                                         ast_unlock_context(c);
361                                                         ast_unlock_contexts();
362                                                         return res;
363                                                 }
364                                         }
365                                         break;
366                                 }
367                                 i = ast_walk_context_includes(c, i);
368                         }       
369                         ast_unlock_context(c);
370                         c = ast_walk_contexts(c);
371                 }
372
373                 free(dupline);
374                 ast_unlock_contexts();
375                 return NULL;
376         }
377
378         return NULL;
379 }
380
381 /*
382  * REMOVE EXTENSION command stuff
383  */
384 static int handle_context_remove_extension(int fd, int argc, char *argv[])
385 {
386         int removing_priority = 0;
387         char *exten, *context;
388
389         if (argc != 4 && argc != 3) return RESULT_SHOWUSAGE;
390
391         /*
392          * Priority input checking ...
393          */
394         if (argc == 4) {
395                 char *c = argv[3];
396
397                 /* check for digits in whole parameter for right priority ...
398                  * why? because atoi (strtol) returns 0 if any characters in
399                  * string and whole extension will be removed, it's not good
400                  */
401                 if (strcmp("hint", c)) {
402                     while (*c != '\0') {
403                         if (!isdigit(*c++)) {
404                                 ast_cli(fd, "Invalid priority '%s'\n", argv[3]);
405                                 return RESULT_FAILURE;
406                         }
407                     }
408                     removing_priority = atoi(argv[3]);
409                 } else
410                     removing_priority = PRIORITY_HINT;
411
412                 if (removing_priority == 0) {
413                         ast_cli(fd, "If you want to remove whole extension, please " \
414                                 "omit priority argument\n");
415                         return RESULT_FAILURE;
416                 }
417         }
418
419         /*
420          * Format exten@context checking ...
421          */
422         if (!(context = strchr(argv[2], (int)'@'))) {
423                 ast_cli(fd, "First argument must be in exten@context format\n");
424                 return RESULT_FAILURE;
425         }
426
427         *context++ = '\0';
428         exten = argv[2];
429         if ((!strlen(exten)) || (!(strlen(context)))) {
430                 ast_cli(fd, "Missing extension or context name in second argument '%s@%s'\n",
431                         exten == NULL ? "?" : exten, context == NULL ? "?" : context);
432                 return RESULT_FAILURE;
433         }
434
435         if (!ast_context_remove_extension(context, exten, removing_priority, registrar)) {
436                 if (!removing_priority)
437                         ast_cli(fd, "Whole extension %s@%s removed\n",
438                                 exten, context);
439                 else
440                         ast_cli(fd, "Extension %s@%s with priority %d removed\n",
441                                 exten, context, removing_priority);
442                         
443                 return RESULT_SUCCESS;
444         }
445
446         ast_cli(fd, "Failed to remove extension %s@%s\n", exten, context);
447
448         return RESULT_FAILURE;
449 }
450
451 #define BROKEN_READLINE 1
452
453 #ifdef BROKEN_READLINE
454 /*
455  * There is one funny thing, when you have word like 300@ and you hit
456  * <tab>, you arguments will like as your word is '300 ', so it '@'
457  * characters acts sometimes as word delimiter and sometimes as a part
458  * of word
459  *
460  * This fix function, allocates new word variable and store here every
461  * time xxx@yyy always as one word and correct pos is set too
462  *
463  * It's ugly, I know, but I'm waiting for Mark suggestion if upper is
464  * bug or feature ...
465  */
466 static int fix_complete_args(char *line, char **word, int *pos)
467 {
468         char *_line, *_strsep_line, *_previous_word = NULL, *_word = NULL;
469         int words = 0;
470
471         _line = strdup(line);
472
473         _strsep_line = _line;
474         while (_strsep_line) {
475                 _previous_word = _word;
476                 _word = strsep(&_strsep_line, " ");
477
478                 if (_word && strlen(_word)) words++;
479         }
480
481
482         if (_word || _previous_word) {
483                 if (_word) {
484                         if (!strlen(_word)) words++;
485                         *word = strdup(_word);
486                 } else
487                         *word = strdup(_previous_word);
488                 *pos = words - 1;
489                 free(_line);
490                 return 0;
491         }
492
493         free(_line);
494         return -1;
495 }
496 #endif /* BROKEN_READLINE */
497
498 static char *complete_context_remove_extension(char *line, char *word, int pos,
499         int state)
500 {
501         char *ret = NULL;
502         int which = 0;
503
504 #ifdef BROKEN_READLINE
505         /*
506          * Fix arguments, *word is a new allocated structure, REMEMBER to
507          * free *word when you want to return from this function ...
508          */
509         if (fix_complete_args(line, &word, &pos)) {
510                 ast_log(LOG_ERROR, "Out of free memory\n");
511                 return NULL;
512         }
513 #endif
514
515         /*
516          * exten@context completion ... 
517          */
518         if (pos == 2) {
519                 struct ast_context *c;
520                 struct ast_exten *e;
521                 char *context = NULL, *exten = NULL, *delim = NULL;
522
523                 /* now, parse values from word = exten@context */
524                 if ((delim = strchr(word, (int)'@'))) {
525                         /* check for duplicity ... */
526                         if (delim != strrchr(word, (int)'@')) {
527 #ifdef BROKEN_READLINE
528                                 free(word);
529 #endif
530                                 return NULL;
531                         }
532
533                         *delim = '\0';
534                         exten = strdup(word);
535                         context = strdup(delim + 1);
536                         *delim = '@';
537                 } else {
538                         exten = strdup(word);
539                 }
540 #ifdef BROKEN_READLINE
541                 free(word);
542 #endif
543
544                 if (ast_lock_contexts()) {
545                         ast_log(LOG_ERROR, "Failed to lock context list\n");
546                         free(context); free(exten);
547                         return NULL;
548                 }
549
550                 /* find our context ... */
551                 c = ast_walk_contexts(NULL); 
552                 while (c) {
553                         /* our context? */
554                         if ( (!context || !strlen(context)) ||                            /* if no input, all contexts ... */
555                                  (context && !strncmp(ast_get_context_name(c),
556                                               context, strlen(context))) ) {                  /* if input, compare ... */
557                                 /* try to complete extensions ... */
558                                 e = ast_walk_context_extensions(c, NULL);
559                                 while (e) {
560                                         /* our extension? */
561                                         if ( (!exten || !strlen(exten)) ||                           /* if not input, all extensions ... */
562                                                  (exten && !strncmp(ast_get_extension_name(e), exten,
563                                                                     strlen(exten))) ) { /* if input, compare ... */
564                                                 if (++which > state) {
565                                                         /* If there is an extension then return
566                                                          * exten@context.
567                                                          */
568                                                         if (exten) {
569                                                                 ret = malloc(strlen(ast_get_extension_name(e)) +
570                                                                         strlen(ast_get_context_name(c)) + 2);
571                                                                 if (ret)
572                                                                         sprintf(ret, "%s@%s", ast_get_extension_name(e),
573                                                                                 ast_get_context_name(c));
574                                                         }
575                                                         free(exten); free(context);
576
577                                                         ast_unlock_contexts();
578         
579                                                         return ret;
580                                                 }
581                                         }
582                                         e = ast_walk_context_extensions(c, e);
583                                 }
584                         }
585                         c = ast_walk_contexts(c);
586                 }
587
588                 ast_unlock_contexts();
589
590                 free(exten); free(context);
591
592                 return NULL;
593         }
594
595         /*
596          * Complete priority ...
597          */
598         if (pos == 3) {
599                 char *delim, *exten, *context, *dupline, *duplinet, *ec;
600                 struct ast_context *c;
601
602                 dupline = strdup(line);
603                 if (!dupline) {
604 #ifdef BROKEN_READLINE
605                         free(word);
606 #endif
607                         return NULL;
608                 }
609                 duplinet = dupline;
610
611                 strsep(&duplinet, " "); /* skip 'remove' */
612                 strsep(&duplinet, " "); /* skip 'extension */
613
614                 if (!(ec = strsep(&duplinet, " "))) {
615                         free(dupline);
616 #ifdef BROKEN_READLINE
617                         free(word);
618 #endif
619                         return NULL;
620                 }
621
622                 /* wrong exten@context format? */
623                 if (!(delim = strchr(ec, (int)'@')) ||
624                         (strchr(ec, (int)'@') != strrchr(ec, (int)'@'))) {
625 #ifdef BROKEN_READLINE
626                         free(word);
627 #endif
628                         free(dupline);
629                         return NULL;
630                 }
631
632                 /* check if there is exten and context too ... */
633                 *delim = '\0';
634                 if ((!strlen(ec)) || (!strlen(delim + 1))) {
635 #ifdef BROKEN_READLINE
636                         free(word);
637 #endif
638                         free(dupline);
639                         return NULL;
640                 }
641
642                 exten = strdup(ec);
643                 context = strdup(delim + 1);
644                 free(dupline);
645
646                 if (ast_lock_contexts()) {
647                         ast_log(LOG_ERROR, "Failed to lock context list\n");
648 #ifdef BROKEN_READLINE
649                         free(word);
650 #endif
651                         free(exten); free(context);
652                         return NULL;
653                 }
654
655                 /* walk contexts */
656                 c = ast_walk_contexts(NULL); 
657                 while (c) {
658                         if (!strcmp(ast_get_context_name(c), context)) {
659                                 struct ast_exten *e;
660
661                                 /* walk extensions */
662                                 free(context);
663                                 e = ast_walk_context_extensions(c, NULL); 
664                                 while (e) {
665                                         if (!strcmp(ast_get_extension_name(e), exten)) {
666                                                 struct ast_exten *priority;
667                                                 char buffer[10];
668                                         
669                                                 free(exten);
670                                                 priority = ast_walk_extension_priorities(e, NULL);
671                                                 /* serve priorities */
672                                                 do {
673                                                         snprintf(buffer, 10, "%u",
674                                                                 ast_get_extension_priority(priority));
675                                                         if (!strncmp(word, buffer, strlen(word))) {
676                                                                 if (++which > state) {
677 #ifdef BROKEN_READLINE
678                                                                         free(word);
679 #endif
680                                                                         ast_unlock_contexts();
681                                                                         return strdup(buffer);
682                                                                 }
683                                                         }
684                                                         priority = ast_walk_extension_priorities(e,
685                                                                 priority);
686                                                 } while (priority);
687
688 #ifdef BROKEN_READLINE
689                                                 free(word);
690 #endif
691                                                 ast_unlock_contexts();
692                                                 return NULL;                    
693                                         }
694                                         e = ast_walk_context_extensions(c, e);
695                                 }
696 #ifdef BROKEN_READLINE
697                                 free(word);
698 #endif
699                                 free(exten);
700                                 ast_unlock_contexts();
701                                 return NULL;
702                         }
703                         c = ast_walk_contexts(c);
704                 }
705
706 #ifdef BROKEN_READLINE
707                 free(word);
708 #endif
709                 free(exten); free(context);
710
711                 ast_unlock_contexts();
712                 return NULL;
713         }
714
715 #ifdef BROKEN_READLINE
716         free(word);
717 #endif
718         return NULL; 
719 }
720
721 /*
722  * Include context ...
723  */
724 static int handle_context_add_include(int fd, int argc, char *argv[])
725 {
726         if (argc != 5) return RESULT_SHOWUSAGE;
727
728         /* third arg must be 'in' ... */
729         if (strcmp(argv[3], "in")) return RESULT_SHOWUSAGE;
730
731         if (ast_context_add_include(argv[4], argv[2], registrar)) {
732                 switch (errno) {
733                         case ENOMEM:
734                                 ast_cli(fd, "Out of memory for context addition\n"); break;
735
736                         case EBUSY:
737                                 ast_cli(fd, "Failed to lock context(s) list, please try again later\n"); break;
738
739                         case EEXIST:
740                                 ast_cli(fd, "Context '%s' already included in '%s' context\n",
741                                         argv[1], argv[3]); break;
742
743                         case ENOENT:
744                         case EINVAL:
745                                 ast_cli(fd, "There is no existence of context '%s'\n",
746                                         errno == ENOENT ? argv[4] : argv[2]); break;
747
748                         default:
749                                 ast_cli(fd, "Failed to include '%s' in '%s' context\n",
750                                         argv[1], argv[3]); break;
751                 }
752                 return RESULT_FAILURE;
753         }
754
755         /* show some info ... */
756         ast_cli(fd, "Context '%s' included in '%s' context\n",
757                 argv[2], argv[3]);
758
759         return RESULT_SUCCESS;
760 }
761
762 static char *complete_context_add_include(char *line, char *word, int pos,
763     int state)
764 {
765         struct ast_context *c;
766         int which = 0;
767
768         /* server context for inclusion ... */
769         if (pos == 1)
770         {
771                 if (ast_lock_contexts()) {
772                         ast_log(LOG_ERROR, "Failed to lock context list\n");
773                         return NULL;
774                 }
775
776                 /* server all contexts */ 
777                 c = ast_walk_contexts(NULL); 
778                 while (c) {
779                         if ((!strlen(word) || 
780                                  !strncmp(ast_get_context_name(c), word, strlen(word))) &&
781                                 ++which > state)
782                         {
783                                 char *context = strdup(ast_get_context_name(c));
784                                 ast_unlock_contexts();
785                                 return context;
786                         }
787                         c = ast_walk_contexts(c);
788                 }
789
790                 ast_unlock_contexts();
791         }
792
793         /* complete 'in' only if context exist ... */
794         if (pos == 2)
795         {
796                 char *context, *dupline, *duplinet;
797
798                 if (state != 0) return NULL;
799
800                 /* parse context from line ... */
801                 if (!(dupline = strdup(line))) {
802                         ast_log(LOG_ERROR, "Out of free memory\n");
803                         if (state == 0) return strdup("in");
804                         return NULL;
805                 }
806
807                 duplinet = dupline;
808
809                 strsep(&duplinet, " ");
810                 context = strsep(&duplinet, " ");
811                 if (context) {
812                         struct ast_context *c;
813                         int context_existence = 0;
814
815                         /* check for context existence ... */
816                         if (ast_lock_contexts()) {
817                                 ast_log(LOG_ERROR, "Failed to lock context list\n");
818                                 free(dupline);
819                                 /* our fault, we can't check, so complete 'in' ... */
820                                 return strdup("in");
821                         }
822
823                         c = ast_walk_contexts(NULL);
824                         while (c && !context_existence) {
825                                 if (!strcmp(context, ast_get_context_name(c))) {
826                                         context_existence = 1;
827                                         continue;
828                                 }
829                                 c = ast_walk_contexts(c);
830                         }
831
832                         /* if context exists, return 'into' ... */
833                         if (context_existence) {
834                                 free(dupline);
835                                 ast_unlock_contexts();
836                                 return strdup("into");
837                         }
838
839                         ast_unlock_contexts();
840                 }       
841
842                 free(dupline);
843                 return NULL;
844         }
845
846         /* serve context into which we include another context */
847         if (pos == 3)
848         {
849                 char *context, *dupline, *duplinet, *in;
850                 int context_existence = 0;
851
852                 if (!(dupline = strdup(line))) {
853                         ast_log(LOG_ERROR, "Out of free memory\n");
854                         return NULL;
855                 }
856
857                 duplinet = dupline;
858
859                 strsep(&duplinet, " "); /* skip 'include' */
860                 context = strsep(&duplinet, " ");
861                 in = strsep(&duplinet, " ");
862
863                 /* given some context and third word is in? */
864                 if (!strlen(context) || strcmp(in, "in")) {
865                         free(dupline);
866                         return NULL;
867                 }
868
869                 if (ast_lock_contexts()) {
870                         ast_log(LOG_ERROR, "Failed to lock context list\n");
871                         free(dupline);
872                         return NULL;
873                 }
874
875                 /* check for context existence ... */
876                 c = ast_walk_contexts(NULL);
877                 while (c && !context_existence) {
878                         if (!strcmp(context, ast_get_context_name(c))) {
879                                 context_existence = 1;
880                                 continue;
881                         }
882                         c = ast_walk_contexts(c);
883                 }
884
885                 if (!context_existence) {
886                         free(dupline);
887                         ast_unlock_contexts();
888                         return NULL;
889                 }
890
891                 /* go through all contexts ... */
892                 c = ast_walk_contexts(NULL);
893                 while (c) {
894                         /* must be different contexts ... */
895                         if (strcmp(context, ast_get_context_name(c))) {
896                                 if (!ast_lock_context(c)) {
897                                         struct ast_include *i;
898                                         int included = 0;
899
900                                         /* check for duplicity inclusion ... */
901                                         i = ast_walk_context_includes(c, NULL);
902                                         while (i && !included) {
903                                                 if (!strcmp(ast_get_include_name(i), context))
904                                                         included = 1;
905                                                 i = ast_walk_context_includes(c, i);
906                                         }
907                                         ast_unlock_context(c);
908
909                                         /* not included yet, so show possibility ... */
910                                         if (!included &&
911                                                 !strncmp(ast_get_context_name(c), word, strlen(word))){
912                                                 
913                                                 if (++which > state) {
914                                                         char *res = strdup(ast_get_context_name(c));
915                                                         free(dupline);
916                                                         ast_unlock_contexts();
917                                                         return res;
918                                                 }
919                                         }       
920                                 }
921                         }
922                         c = ast_walk_contexts(c);
923                 }
924
925                 ast_unlock_contexts();
926                 free(dupline);
927                 return NULL;
928         }
929
930         return NULL;
931 }
932
933 /*
934  * 'save dialplan' CLI command implementation functions ...
935  */
936 static int handle_save_dialplan(int fd, int argc, char *argv[])
937 {
938         char filename[256];
939         struct ast_context *c;
940         struct ast_config *cfg;
941         struct ast_variable *v;
942         int context_header_written;
943         int incomplete = 0; /* incomplete config write? */
944         FILE *output;
945
946         if (! (static_config && !write_protect_config)) {
947                 ast_cli(fd,
948                         "I can't save dialplan now, see '%s' example file.\n",
949                         config);
950                 return RESULT_FAILURE;
951         }
952
953         if (argc != 2 && argc != 3) return RESULT_SHOWUSAGE;
954
955         if (ast_mutex_lock(&save_dialplan_lock)) {
956                 ast_cli(fd,
957                         "Failed to lock dialplan saving (another proccess saving?)\n");
958                 return RESULT_FAILURE;
959         }
960
961         /* have config path? */
962         if (argc == 3) {
963                 /* is there extension.conf too? */
964                 if (!strstr(argv[2], ".conf")) {
965                         /* no, only directory path, check for last '/' occurence */
966                         if (*(argv[2] + strlen(argv[2]) -1) == '/')
967                                 snprintf(filename, sizeof(filename), "%s%s",
968                                         argv[2], config);
969                         else
970                                 /* without config extensions.conf, add it */
971                                 snprintf(filename, sizeof(filename), "%s/%s",
972                                         argv[2], config);
973                 } else
974                         /* there is an .conf */
975                         snprintf(filename, sizeof(filename), argv[2]);
976         } else
977                 /* no config file, default one */
978                 snprintf(filename, sizeof(filename), "%s/%s",
979                         (char *)ast_config_AST_CONFIG_DIR, config);
980
981         cfg = ast_config_load("extensions.conf");
982
983         /* try to lock contexts list */
984         if (ast_lock_contexts()) {
985                 ast_cli(fd, "Failed to lock contexts list\n");
986                 ast_mutex_unlock(&save_dialplan_lock);
987                 ast_config_destroy(cfg);
988                 return RESULT_FAILURE;
989         }
990
991         /* create new file ... */
992         if (!(output = fopen(filename, "wt"))) {
993                 ast_cli(fd, "Failed to create file '%s'\n",
994                         filename);
995                 ast_unlock_contexts();
996                 ast_mutex_unlock(&save_dialplan_lock);
997                 ast_config_destroy(cfg);
998                 return RESULT_FAILURE;
999         }
1000
1001         /* fireout general info */
1002         fprintf(output, "[general]\nstatic=%s\nwriteprotect=%s\n\n",
1003                 static_config ? "yes" : "no",
1004                 write_protect_config ? "yes" : "no");
1005
1006         if ((v = ast_variable_browse(cfg, "globals"))) {
1007                 fprintf(output, "[globals]\n");
1008                 while(v) {
1009                         fprintf(output, "%s => %s\n", v->name, v->value);
1010                         v = v->next;
1011                 }
1012                 fprintf(output, "\n");
1013         }
1014
1015         ast_config_destroy(cfg);
1016         
1017         /* walk all contexts */
1018         c = ast_walk_contexts(NULL);
1019         while (c) {
1020                 context_header_written = 0;
1021         
1022                 /* try to lock context and fireout all info */  
1023                 if (!ast_lock_context(c)) {
1024                         struct ast_exten *e, *last_written_e = NULL;
1025                         struct ast_include *i;
1026                         struct ast_ignorepat *ip;
1027                         struct ast_sw *sw;
1028
1029                         /* registered by this module? */
1030                         if (!strcmp(ast_get_context_registrar(c), registrar)) {
1031                                 fprintf(output, "[%s]\n", ast_get_context_name(c));
1032                                 context_header_written = 1;
1033                         }
1034
1035                         /* walk extensions ... */
1036                         e = ast_walk_context_extensions(c, NULL);
1037                         while (e) {
1038                                 struct ast_exten *p;
1039
1040                                 /* fireout priorities */
1041                                 p = ast_walk_extension_priorities(e, NULL);
1042                                 while (p) {
1043                                         if (!strcmp(ast_get_extension_registrar(p),
1044                                                 registrar)) {
1045                         
1046                                                 /* make empty line between different extensions */      
1047                                                 if (last_written_e != NULL &&
1048                                                         strcmp(ast_get_extension_name(last_written_e),
1049                                                                 ast_get_extension_name(p)))
1050                                                         fprintf(output, "\n");
1051                                                 last_written_e = p;
1052                                 
1053                                                 if (!context_header_written) {
1054                                                         fprintf(output, "[%s]\n", ast_get_context_name(c));
1055                                                         context_header_written = 1;
1056                                                 }
1057
1058                                                 if (ast_get_extension_priority(p)!=PRIORITY_HINT) {
1059                                                         char *tempdata = NULL, *startdata;
1060                                                         tempdata = strdup((char *)ast_get_extension_app_data(p));
1061                                                         if (tempdata) {
1062                                                                 startdata = tempdata;
1063                                                                 while (*tempdata) {
1064                                                                         if (*tempdata == '|')
1065                                                                                 *tempdata = ',';
1066                                                                         tempdata++;
1067                                                                 }
1068                                                                 tempdata = startdata;
1069                                                         }
1070                                                         if (ast_get_extension_matchcid(p))
1071                                                                 fprintf(output, "exten => %s/%s,%d,%s(%s)\n",
1072                                                                     ast_get_extension_name(p),
1073                                                                     ast_get_extension_cidmatch(p),
1074                                                                     ast_get_extension_priority(p),
1075                                                                     ast_get_extension_app(p),
1076                                                                     tempdata);
1077                                                         else
1078                                                                 fprintf(output, "exten => %s,%d,%s(%s)\n",
1079                                                                     ast_get_extension_name(p),
1080                                                                     ast_get_extension_priority(p),
1081                                                                     ast_get_extension_app(p),
1082                                                                     tempdata);
1083                                                         if (tempdata)
1084                                                                 free(tempdata);
1085                                                 } else
1086                                                         fprintf(output, "exten => %s,hint,%s\n",
1087                                                             ast_get_extension_name(p),
1088                                                             ast_get_extension_app(p));
1089                                                 
1090                                         }
1091                                         p = ast_walk_extension_priorities(e, p);
1092                                 }
1093
1094                                 e = ast_walk_context_extensions(c, e);
1095                         }
1096
1097                         /* written any extensions? ok, write space between exten & inc */
1098                         if (last_written_e) fprintf(output, "\n");
1099
1100                         /* walk through includes */
1101                         i = ast_walk_context_includes(c, NULL);
1102                         while (i) {
1103                                 if (!strcmp(ast_get_include_registrar(i), registrar)) {
1104                                         if (!context_header_written) {
1105                                                 fprintf(output, "[%s]\n", ast_get_context_name(c));
1106                                                 context_header_written = 1;
1107                                         }
1108                                         fprintf(output, "include => %s\n",
1109                                                 ast_get_include_name(i));
1110                                 }
1111                                 i = ast_walk_context_includes(c, i);
1112                         }
1113
1114                         if (ast_walk_context_includes(c, NULL))
1115                                 fprintf(output, "\n");
1116
1117                         /* walk through switches */
1118                         sw = ast_walk_context_switches(c, NULL);
1119                         while (sw) {
1120                                 if (!strcmp(ast_get_switch_registrar(sw), registrar)) {
1121                                         if (!context_header_written) {
1122                                                 fprintf(output, "[%s]\n", ast_get_context_name(c));
1123                                                 context_header_written = 1;
1124                                         }
1125                                         fprintf(output, "switch => %s/%s\n",
1126                                                 ast_get_switch_name(sw),
1127                                                 ast_get_switch_data(sw));
1128                                 }
1129                                 sw = ast_walk_context_switches(c, sw);
1130                         }
1131
1132                         if (ast_walk_context_switches(c, NULL))
1133                                 fprintf(output, "\n");
1134
1135                         /* fireout ignorepats ... */
1136                         ip = ast_walk_context_ignorepats(c, NULL);
1137                         while (ip) {
1138                                 if (!strcmp(ast_get_ignorepat_registrar(ip), registrar)) {
1139                                         if (!context_header_written) {
1140                                                 fprintf(output, "[%s]\n", ast_get_context_name(c));
1141                                                 context_header_written = 1;
1142                                         }
1143
1144                                         fprintf(output, "ignorepat => %s\n",
1145                                                 ast_get_ignorepat_name(ip));
1146                                 }
1147                                 ip = ast_walk_context_ignorepats(c, ip);
1148                         }
1149
1150                         ast_unlock_context(c);
1151                 } else
1152                         incomplete = 1;
1153
1154                 c = ast_walk_contexts(c);
1155         }       
1156
1157         ast_unlock_contexts();
1158         ast_mutex_unlock(&save_dialplan_lock);
1159         fclose(output);
1160
1161         if (incomplete) {
1162                 ast_cli(fd, "Saved dialplan is incomplete\n");
1163                 return RESULT_FAILURE;
1164         }
1165
1166         ast_cli(fd, "Dialplan successfully saved into '%s'\n",
1167                 filename);
1168         return RESULT_SUCCESS;
1169 }
1170
1171 /*
1172  * ADD EXTENSION command stuff
1173  */
1174 static int handle_context_add_extension(int fd, int argc, char *argv[])
1175 {
1176         char *whole_exten;
1177         char *exten, *prior;
1178         int iprior = -2;
1179         char *cidmatch, *app, *app_data;
1180         char *start, *end;
1181
1182         /* check for arguments at first */
1183         if (argc != 5 && argc != 6) return RESULT_SHOWUSAGE;
1184         if (strcmp(argv[3], "into")) return RESULT_SHOWUSAGE;
1185         if (argc == 6) if (strcmp(argv[5], "replace")) return RESULT_SHOWUSAGE;
1186
1187         whole_exten = argv[2];
1188         exten           = strsep(&whole_exten,",");
1189         if (strchr(exten, '/')) {
1190                 cidmatch = exten;
1191                 strsep(&cidmatch,"/");
1192         } else {
1193                 cidmatch = NULL;
1194         }
1195         prior       = strsep(&whole_exten,",");
1196         if (prior) {
1197         if (!strcmp(prior, "hint")) {
1198                         iprior = PRIORITY_HINT;
1199                 } else {
1200                         if (sscanf(prior, "%d", &iprior) != 1) {
1201                                 ast_cli(fd, "'%s' is not a valid priority\n", prior);
1202                                 prior = NULL;
1203                         }
1204                 }
1205         }
1206         app = whole_exten;
1207         if (app && (start = strchr(app, '(')) && (end = strrchr(app, ')'))) {
1208                 *start = *end = '\0';
1209                 app_data = start + 1;
1210                 process_quotes_and_slashes(app_data, ',', '|');
1211         } else {
1212                 if (app) {
1213                         app_data = strchr(app, ',');
1214                         if (app_data) {
1215                                 *app_data = '\0';
1216                                 app_data++;
1217                         }
1218                 } else  
1219                         app_data = NULL;
1220         }
1221
1222         if (!exten || !prior || !app || (!app_data && iprior != PRIORITY_HINT)) return RESULT_SHOWUSAGE;
1223
1224         if (!app_data)
1225                 app_data="";
1226         if (ast_add_extension(argv[4], argc == 6 ? 1 : 0, exten, iprior, NULL, cidmatch, app,
1227                 (void *)strdup(app_data), free, registrar)) {
1228                 switch (errno) {
1229                         case ENOMEM:
1230                                 ast_cli(fd, "Out of free memory\n"); break;
1231
1232                         case EBUSY:
1233                                 ast_cli(fd, "Failed to lock context(s) list, please try again later\n"); break;
1234
1235                         case ENOENT:
1236                                 ast_cli(fd, "No existence of '%s' context\n", argv[4]); break;
1237
1238                         case EEXIST:
1239                                 ast_cli(fd, "Extension %s@%s with priority %s already exists\n",
1240                                         exten, argv[4], prior); break;
1241
1242                         default:
1243                                 ast_cli(fd, "Failed to add '%s,%s,%s,%s' extension into '%s' context\n",
1244                                         exten, prior, app, app_data, argv[4]); break;
1245                 }
1246                 return RESULT_FAILURE;
1247         }
1248
1249         if (argc == 6) 
1250                 ast_cli(fd, "Extension %s@%s (%s) replace by '%s,%s,%s,%s'\n",
1251                         exten, argv[4], prior, exten, prior, app, app_data);
1252         else
1253                 ast_cli(fd, "Extension '%s,%s,%s,%s' added into '%s' context\n",
1254                         exten, prior, app, app_data, argv[4]);
1255
1256         return RESULT_SUCCESS;
1257 }
1258
1259 /* add extension 6123,1,Dial,IAX/212.71.138.13/6123 into local */
1260 static char *complete_context_add_extension(char *line, char *word,
1261         int pos, int state)
1262 {
1263         int which = 0;
1264
1265         /* complete 'into' word ... */
1266         if (pos == 3) {
1267                 if (state == 0) return strdup("into");
1268                 return NULL;
1269         }
1270
1271         /* complete context */
1272         if (pos == 4) {
1273                 struct ast_context *c;
1274
1275                 /* try to lock contexts list ... */
1276                 if (ast_lock_contexts()) {
1277                         ast_log(LOG_WARNING, "Failed to lock contexts list\n");
1278                         return NULL;
1279                 }
1280
1281                 /* walk through all contexts */
1282                 c = ast_walk_contexts(NULL);
1283                 while (c) {
1284                         /* matching context? */
1285                         if (!strncmp(ast_get_context_name(c), word, strlen(word))) {
1286                                 if (++which > state) {
1287                                         char *res = strdup(ast_get_context_name(c));
1288                                         ast_unlock_contexts();
1289                                         return res;
1290                                 }
1291                         }
1292                         c = ast_walk_contexts(c);
1293                 }
1294
1295                 ast_unlock_contexts();
1296                 return NULL;
1297         }
1298
1299         if (pos == 5) return state == 0 ? strdup("replace") : NULL;
1300
1301         return NULL;
1302 }
1303
1304 /*
1305  * IGNOREPAT CLI stuff
1306  */
1307 static int handle_context_add_ignorepat(int fd, int argc, char *argv[])
1308 {
1309         if (argc != 5) return RESULT_SHOWUSAGE;
1310         if (strcmp(argv[3], "into")) return RESULT_SHOWUSAGE;
1311
1312         if (ast_context_add_ignorepat(argv[4], argv[2], registrar)) {
1313                 switch (errno) {
1314                         case ENOMEM:
1315                                 ast_cli(fd, "Out of free memory\n"); break;
1316
1317                         case ENOENT:
1318                                 ast_cli(fd, "There is no existence of '%s' context\n", argv[4]);
1319                                 break;
1320
1321                         case EEXIST:
1322                                 ast_cli(fd, "Ignore pattern '%s' already included in '%s' context\n",
1323                                         argv[2], argv[4]);
1324                                 break;
1325
1326                         case EBUSY:
1327                                 ast_cli(fd, "Failed to lock context(s) list, please, try again later\n");
1328                                 break;
1329
1330                         default:
1331                                 ast_cli(fd, "Failed to add ingore pattern '%s' into '%s' context\n",
1332                                         argv[2], argv[4]);
1333                                 break;
1334                 }
1335                 return RESULT_FAILURE;
1336         }
1337
1338         ast_cli(fd, "Ignore pattern '%s' added into '%s' context\n",
1339                 argv[2], argv[4]);
1340         return RESULT_SUCCESS;
1341 }
1342
1343 static char *complete_context_add_ignorepat(char *line, char *word,
1344         int pos, int state)
1345 {
1346         if (pos == 3) return state == 0 ? strdup("into") : NULL;
1347
1348         if (pos == 4) {
1349                 struct ast_context *c;
1350                 int which = 0;
1351                 char *dupline, *duplinet, *ignorepat = NULL;
1352
1353                 dupline = strdup(line);
1354                 duplinet = dupline;
1355
1356                 if (duplinet) {
1357                         strsep(&duplinet, " "); /* skip 'add' */
1358                         strsep(&duplinet, " "); /* skip 'ignorepat' */
1359                         ignorepat = strsep(&duplinet, " ");
1360                 }
1361
1362                 if (ast_lock_contexts()) {
1363                         ast_log(LOG_ERROR, "Failed to lock contexts list\n");
1364                         return NULL;
1365                 }
1366
1367                 c = ast_walk_contexts(NULL);
1368                 while (c) {
1369                         if (!strncmp(ast_get_context_name(c), word, strlen(word))) {
1370                                 int serve_context = 1;
1371                                 if (ignorepat) {
1372                                         if (!ast_lock_context(c)) {
1373                                                 struct ast_ignorepat *ip;
1374                                                 ip = ast_walk_context_ignorepats(c, NULL);
1375                                                 while (ip && serve_context) {
1376                                                         if (!strcmp(ast_get_ignorepat_name(ip), ignorepat))
1377                                                                 serve_context = 0;
1378                                                         ip = ast_walk_context_ignorepats(c, ip);
1379                                                 }
1380                                                 ast_unlock_context(c);
1381                                         }
1382                                 }
1383                                 if (serve_context) {
1384                                         if (++which > state) {
1385                                                 char *context = strdup(ast_get_context_name(c));
1386                                                 if (dupline) free(dupline);
1387                                                 ast_unlock_contexts();
1388                                                 return context;
1389                                         }
1390                                 }
1391                         }
1392                         c = ast_walk_contexts(c);
1393                 }
1394
1395                 if (dupline) free(dupline);
1396                 ast_unlock_contexts();
1397                 return NULL;
1398         }
1399
1400         return NULL;
1401 }
1402
1403 static int handle_context_remove_ignorepat(int fd, int argc, char *argv[])
1404 {
1405         if (argc != 5) return RESULT_SHOWUSAGE;
1406         if (strcmp(argv[3], "from")) return RESULT_SHOWUSAGE;
1407
1408         if (ast_context_remove_ignorepat(argv[4], argv[2], registrar)) {
1409                 switch (errno) {
1410                         case EBUSY:
1411                                 ast_cli(fd, "Failed to lock context(s) list, please try again later\n");
1412                                 break;
1413
1414                         case ENOENT:
1415                                 ast_cli(fd, "There is no existence of '%s' context\n", argv[4]);
1416                                 break;
1417
1418                         case EINVAL:
1419                                 ast_cli(fd, "There is no existence of '%s' ignore pattern in '%s' context\n",
1420                                         argv[2], argv[4]);
1421                                 break;
1422
1423                         default:
1424                                 ast_cli(fd, "Failed to remove ignore pattern '%s' from '%s' context\n", argv[2], argv[4]);
1425                                 break;
1426                 }
1427                 return RESULT_FAILURE;
1428         }
1429
1430         ast_cli(fd, "Ignore pattern '%s' removed from '%s' context\n",
1431                 argv[2], argv[4]);
1432         return RESULT_SUCCESS;
1433 }
1434
1435 static int pbx_load_module(void);
1436
1437 static int handle_reload_extensions(int fd, int argc, char *argv[])
1438 {
1439         if (argc!=2) return RESULT_SHOWUSAGE;
1440         pbx_load_module();
1441         return RESULT_SUCCESS;
1442 }
1443
1444 static char *complete_context_remove_ignorepat(char *line, char *word,
1445         int pos, int state)
1446 {
1447         struct ast_context *c;
1448         int which = 0;
1449
1450         if (pos == 2) {
1451                 if (ast_lock_contexts()) {
1452                         ast_log(LOG_WARNING, "Failed to lock contexts list\n");
1453                         return NULL;
1454                 }
1455
1456                 c = ast_walk_contexts(NULL);
1457                 while (c) {
1458                         if (!ast_lock_context(c)) {
1459                                 struct ast_ignorepat *ip;
1460                         
1461                                 ip = ast_walk_context_ignorepats(c, NULL);
1462                                 while (ip) {
1463                                         if (!strncmp(ast_get_ignorepat_name(ip), word, strlen(word))) {
1464                                                 if (which + 1 > state) {
1465                                                         struct ast_context *cw;
1466                                                         int already_served = 0;
1467                                                         cw = ast_walk_contexts(NULL);
1468                                                         while (cw && cw != c && !already_served) {
1469                                                                 if (!ast_lock_context(cw)) {
1470                                                                         struct ast_ignorepat *ipw;
1471                                                                         ipw = ast_walk_context_ignorepats(cw, NULL);
1472                                                                         while (ipw) {
1473                                                                                 if (!strcmp(ast_get_ignorepat_name(ipw),
1474                                                                                         ast_get_ignorepat_name(ip))) already_served = 1;
1475                                                                                 ipw = ast_walk_context_ignorepats(cw, ipw);
1476                                                                         }
1477                                                                         ast_unlock_context(cw);
1478                                                                 }
1479                                                                 cw = ast_walk_contexts(cw);
1480                                                         }
1481                                                         if (!already_served) {
1482                                                                 char *ret = strdup(ast_get_ignorepat_name(ip));
1483                                                                 ast_unlock_context(c);
1484                                                                 ast_unlock_contexts();
1485                                                                 return ret;
1486                                                         }
1487                                                 } else
1488                                                         which++;
1489                                         }
1490                                         ip = ast_walk_context_ignorepats(c, ip);
1491                                 }
1492
1493                                 ast_unlock_context(c);
1494                         }
1495                         c = ast_walk_contexts(c);
1496                 }
1497
1498                 ast_unlock_contexts();
1499                 return NULL;
1500         }
1501  
1502         if (pos == 3) return state == 0 ? strdup("from") : NULL;
1503
1504         if (pos == 4) {
1505                 char *dupline, *duplinet, *ignorepat;
1506
1507                 dupline = strdup(line);
1508                 if (!dupline) {
1509                         ast_log(LOG_WARNING, "Out of free memory\n");
1510                         return NULL;
1511                 }
1512
1513                 duplinet = dupline;
1514                 strsep(&duplinet, " ");
1515                 strsep(&duplinet, " ");
1516                 ignorepat = strsep(&duplinet, " ");
1517
1518                 if (!ignorepat) {
1519                         free(dupline);
1520                         return NULL;
1521                 }
1522
1523                 if (ast_lock_contexts()) {
1524                         ast_log(LOG_WARNING, "Failed to lock contexts list\n");
1525                         free(dupline);
1526                         return NULL;
1527                 }
1528
1529                 c = ast_walk_contexts(NULL);
1530                 while (c) {
1531                         if (!ast_lock_context(c)) {
1532                                 struct ast_ignorepat *ip;
1533                                 ip = ast_walk_context_ignorepats(c, NULL);
1534                                 while (ip) {
1535                                         if (!strcmp(ast_get_ignorepat_name(ip), ignorepat)) {
1536                                                 if (!strncmp(ast_get_context_name(c), word, strlen(word))) {
1537                                                         if (++which > state) {
1538                                                                 char *ret = strdup(ast_get_context_name(c));
1539                                                                 free(dupline);
1540                                                                 ast_unlock_context(c);
1541                                                                 ast_unlock_contexts();
1542                                                                 return ret;
1543                                                         }
1544                                                 }
1545                                         }
1546                                         ip = ast_walk_context_ignorepats(c, ip);
1547                                 }
1548
1549                                 ast_unlock_context(c);
1550                         }
1551                         c = ast_walk_contexts(c);
1552                 }
1553
1554                 free(dupline);
1555                 ast_unlock_contexts();
1556                 return NULL;
1557         }
1558
1559         return NULL;
1560 }
1561
1562 /*
1563  * CLI entries for commands provided by this module
1564  */
1565 static struct ast_cli_entry context_dont_include_cli =
1566         { { "dont", "include", NULL }, handle_context_dont_include,
1567                 "Remove a specified include from context", context_dont_include_help,
1568                 complete_context_dont_include };
1569
1570 static struct ast_cli_entry context_remove_extension_cli =
1571         { { "remove", "extension", NULL }, handle_context_remove_extension,
1572                 "Remove a specified extension", context_remove_extension_help,
1573                 complete_context_remove_extension };
1574
1575 static struct ast_cli_entry context_add_include_cli =
1576         { { "include", "context", NULL }, handle_context_add_include,
1577                 "Include context in other context", context_add_include_help,
1578                 complete_context_add_include };
1579
1580 static struct ast_cli_entry save_dialplan_cli =
1581         { { "save", "dialplan", NULL }, handle_save_dialplan,
1582                 "Save dialplan", save_dialplan_help };
1583
1584 static struct ast_cli_entry context_add_extension_cli =
1585         { { "add", "extension", NULL }, handle_context_add_extension,
1586                 "Add new extension into context", context_add_extension_help,
1587                 complete_context_add_extension };
1588
1589 static struct ast_cli_entry context_add_ignorepat_cli =
1590         { { "add", "ignorepat", NULL }, handle_context_add_ignorepat,
1591                 "Add new ignore pattern", context_add_ignorepat_help,
1592                 complete_context_add_ignorepat };
1593
1594 static struct ast_cli_entry context_remove_ignorepat_cli =
1595         { { "remove", "ignorepat", NULL }, handle_context_remove_ignorepat,
1596                 "Remove ignore pattern from context", context_remove_ignorepat_help,
1597                 complete_context_remove_ignorepat };
1598
1599 static struct ast_cli_entry reload_extensions_cli = 
1600         { { "extensions", "reload", NULL}, handle_reload_extensions,
1601                 "Reload extensions and *only* extensions", reload_extensions_help };
1602
1603 /*
1604  * Standard module functions ...
1605  */
1606 int unload_module(void)
1607 {
1608         ast_cli_unregister(&context_add_extension_cli);
1609         if (static_config && !write_protect_config)
1610                 ast_cli_unregister(&save_dialplan_cli);
1611         ast_cli_unregister(&context_add_include_cli);
1612         ast_cli_unregister(&context_dont_include_cli);
1613         ast_cli_unregister(&context_remove_extension_cli);
1614         ast_cli_unregister(&context_remove_ignorepat_cli);
1615         ast_cli_unregister(&context_add_ignorepat_cli);
1616         ast_cli_unregister(&reload_extensions_cli);
1617         ast_context_destroy(NULL, registrar);
1618         return 0;
1619 }
1620
1621 static int pbx_load_module(void)
1622 {
1623         struct ast_config *cfg;
1624         struct ast_variable *v;
1625         char *cxt, *ext, *pri, *appl, *data, *tc, *cidmatch;
1626         struct ast_context *con;
1627         char *end;
1628         char *label;
1629         char realvalue[256];
1630         int lastpri = -2;
1631
1632         cfg = ast_config_load(config);
1633         if (cfg) {
1634                 /* Use existing config to populate the PBX table */
1635                 static_config = ast_true(ast_variable_retrieve(cfg, "general",
1636                         "static"));
1637                 write_protect_config = ast_true(ast_variable_retrieve(cfg, "general",
1638                         "writeprotect"));
1639                 
1640                 autofallthrough_config = ast_true(ast_variable_retrieve(cfg, "general",
1641                         "autofallthrough"));
1642
1643                 clearglobalvars_config = ast_true(ast_variable_retrieve(cfg, "general", 
1644                         "clearglobalvars"));
1645
1646                 v = ast_variable_browse(cfg, "globals");
1647                 while(v) {
1648                         memset(realvalue, 0, sizeof(realvalue));
1649                         pbx_substitute_variables_helper(NULL, v->value, realvalue, sizeof(realvalue) - 1);
1650                         pbx_builtin_setvar_helper(NULL, v->name, realvalue);
1651                         v = v->next;
1652                 }
1653                 cxt = ast_category_browse(cfg, NULL);
1654                 while(cxt) {
1655                         /* All categories but "general" or "globals" are considered contexts */
1656                         if (!strcasecmp(cxt, "general") || !strcasecmp(cxt, "globals")) {
1657                                 cxt = ast_category_browse(cfg, cxt);
1658                                 continue;
1659                         }
1660                         if ((con=ast_context_create(&local_contexts,cxt, registrar))) {
1661                                 v = ast_variable_browse(cfg, cxt);
1662                                 while(v) {
1663                                         if (!strcasecmp(v->name, "exten")) {
1664                                                 char *stringp=NULL;
1665                                                 int ipri = -2;
1666                                                 char realext[256]="";
1667                                                 char *plus, *firstp, *firstc;
1668                                                 tc = strdup(v->value);
1669                                                 if(tc!=NULL){
1670                                                         stringp=tc;
1671                                                         ext = strsep(&stringp, ",");
1672                                                         if (!ext)
1673                                                                 ext="";
1674                                                         pbx_substitute_variables_helper(NULL, ext, realext, sizeof(realext) - 1);
1675                                                         cidmatch = strchr(realext, '/');
1676                                                         if (cidmatch) {
1677                                                                 *cidmatch = '\0';
1678                                                                 cidmatch++;
1679                                                                 ast_shrink_phone_number(cidmatch);
1680                                                         }
1681                                                         pri = strsep(&stringp, ",");
1682                                                         if (!pri)
1683                                                                 pri="";
1684                                                         label = strchr(pri, '(');
1685                                                         if (label) {
1686                                                                 *label = '\0';
1687                                                                 label++;
1688                                                                 end = strchr(label, ')');
1689                                                                 if (end)
1690                                                                         *end = '\0';
1691                                                                 else
1692                                                                         ast_log(LOG_WARNING, "Label missing trailing ')' at line %d\n", v->lineno);
1693                                                         }
1694                                                         plus = strchr(pri, '+');
1695                                                         if (plus) {
1696                                                                 *plus = '\0';
1697                                                                 plus++;
1698                                                         }
1699                                                         if (!strcmp(pri,"hint"))
1700                                                                 ipri=PRIORITY_HINT;
1701                                                         else if (!strcmp(pri, "next") || !strcmp(pri, "n")) {
1702                                                                 if (lastpri > -2)
1703                                                                         ipri = lastpri + 1;
1704                                                                 else
1705                                                                         ast_log(LOG_WARNING, "Can't use 'next' priority on the first entry!\n");
1706                                                         } else if (!strcmp(pri, "same") || !strcmp(pri, "s")) {
1707                                                                 if (lastpri > -2)
1708                                                                         ipri = lastpri;
1709                                                                 else
1710                                                                         ast_log(LOG_WARNING, "Can't use 'same' priority on the first entry!\n");
1711                                                         } else  {
1712                                                                 if (sscanf(pri, "%d", &ipri) != 1) {
1713                                                                         if ((ipri = ast_findlabel_extension2(NULL, con, realext, pri, cidmatch)) < 1) {
1714                                                                                 ast_log(LOG_WARNING, "Invalid priority/label '%s' at line %d\n", pri, v->lineno);
1715                                                                                 ipri = 0;
1716                                                                         }
1717                                                                 }
1718                                                         }
1719                                                         appl = stringp;
1720                                                         if (!appl)
1721                                                                 appl="";
1722                                                         /* Find the first occurrence of either '(' or ',' */
1723                                                         firstc = strchr(appl, ',');
1724                                                         firstp = strchr(appl, '(');
1725                                                         if (firstc && ((!firstp) || (firstc < firstp))) {
1726                                                                 /* comma found, no parenthesis */
1727                                                                 /* or both found, but comma found first */
1728                                                                 appl = strsep(&stringp, ",");
1729                                                                 data = stringp;
1730                                                         } else if ((!firstc) && (!firstp)) {
1731                                                                 /* Neither found */
1732                                                                 data = "";
1733                                                         } else {
1734                                                                 /* Final remaining case is parenthesis found first */
1735                                                                 appl = strsep(&stringp, "(");
1736                                                                 data = stringp;
1737                                                                 end = strrchr(data, ')');
1738                                                                 if ((end = strrchr(data, ')'))) {
1739                                                                         *end = '\0';
1740                                                                 } else {
1741                                                                         ast_log(LOG_WARNING, "No closing parenthesis found? '%s(%s'\n", appl, data);
1742                                                                 }
1743                                                                 process_quotes_and_slashes(data, ',', '|');
1744                                                         }
1745
1746                                                         if (!data)
1747                                                                 data="";
1748                                                         while(*appl && (*appl < 33)) appl++;
1749                                                         if (ipri) {
1750                                                                 if (plus)
1751                                                                         ipri += atoi(plus);
1752                                                                 lastpri = ipri;
1753                                                                 if(!option_dontwarn) {
1754                                                                         if (!strcmp(realext, "_."))
1755                                                                                 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);
1756                                                                 }
1757                                                                 if (ast_add_extension2(con, 0, realext, ipri, label, cidmatch, appl, strdup(data), FREE, registrar)) {
1758                                                                         ast_log(LOG_WARNING, "Unable to register extension at line %d\n", v->lineno);
1759                                                                 }
1760                                                         }
1761                                                         free(tc);
1762                                                 } else fprintf(stderr,"Error strdup returned NULL in %s\n",__PRETTY_FUNCTION__);
1763                                         } else if(!strcasecmp(v->name, "include")) {
1764                                                 memset(realvalue, 0, sizeof(realvalue));
1765                                                 pbx_substitute_variables_helper(NULL, v->value, realvalue, sizeof(realvalue) - 1);
1766                                                 if (ast_context_add_include2(con, realvalue, registrar))
1767                                                         ast_log(LOG_WARNING, "Unable to include context '%s' in context '%s'\n", v->value, cxt);
1768                                         } else if(!strcasecmp(v->name, "ignorepat")) {
1769                                                 memset(realvalue, 0, sizeof(realvalue));
1770                                                 pbx_substitute_variables_helper(NULL, v->value, realvalue, sizeof(realvalue) - 1);
1771                                                 if (ast_context_add_ignorepat2(con, realvalue, registrar))
1772                                                         ast_log(LOG_WARNING, "Unable to include ignorepat '%s' in context '%s'\n", v->value, cxt);
1773                                         } else if (!strcasecmp(v->name, "switch") || !strcasecmp(v->name, "lswitch") || !strcasecmp(v->name, "eswitch")) {
1774                                                 char *stringp=NULL;
1775                                                 memset(realvalue, 0, sizeof(realvalue));
1776                                                 if (!strcasecmp(v->name, "switch"))
1777                                                         pbx_substitute_variables_helper(NULL, v->value, realvalue, sizeof(realvalue) - 1);
1778                                                 else
1779                                                         strncpy(realvalue, v->value, sizeof(realvalue) - 1);
1780                                                 tc = realvalue;
1781                                                 stringp=tc;
1782                                                 appl = strsep(&stringp, "/");
1783                                                 data = strsep(&stringp, "");
1784                                                 if (!data)
1785                                                         data = "";
1786                                                 if (ast_context_add_switch2(con, appl, data, !strcasecmp(v->name, "eswitch"), registrar))
1787                                                         ast_log(LOG_WARNING, "Unable to include switch '%s' in context '%s'\n", v->value, cxt);
1788                                         }
1789                                         v = v->next;
1790                                 }
1791                         }
1792                         cxt = ast_category_browse(cfg, cxt);
1793                 }
1794                 ast_config_destroy(cfg);
1795         }
1796         ast_merge_contexts_and_delete(&local_contexts,registrar);
1797
1798         for (con = ast_walk_contexts(NULL); con; con = ast_walk_contexts(con))
1799                 ast_context_verify_includes(con);
1800
1801         pbx_set_autofallthrough(autofallthrough_config);
1802
1803         return 0;
1804 }
1805
1806 int load_module(void)
1807 {
1808         if (pbx_load_module()) return -1;
1809  
1810         ast_cli_register(&context_remove_extension_cli);
1811         ast_cli_register(&context_dont_include_cli);
1812         ast_cli_register(&context_add_include_cli);
1813         if (static_config && !write_protect_config)
1814                 ast_cli_register(&save_dialplan_cli);
1815         ast_cli_register(&context_add_extension_cli);
1816         ast_cli_register(&context_add_ignorepat_cli);
1817         ast_cli_register(&context_remove_ignorepat_cli);
1818         ast_cli_register(&reload_extensions_cli);
1819
1820         return 0;
1821 }
1822
1823 int reload(void)
1824 {
1825         ast_context_destroy(NULL, registrar);
1826         if (clearglobalvars_config)
1827                 pbx_builtin_clear_globals();
1828         pbx_load_module();
1829         return 0;
1830 }
1831
1832 int usecount(void)
1833 {
1834         return 0;
1835 }
1836
1837 char *description(void)
1838 {
1839         return dtext;
1840 }
1841
1842 char *key(void)
1843 {
1844         return ASTERISK_GPL_KEY;
1845 }