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