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