Make menu interruptible (bug #2377), Also fix PBX "add extension" CLI (bug #2289)
[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                         struct ast_sw *sw;
1020
1021                         /* registered by this module? */
1022                         if (!strcmp(ast_get_context_registrar(c), registrar)) {
1023                                 fprintf(output, "[%s]\n", ast_get_context_name(c));
1024                                 context_header_written = 1;
1025                         }
1026
1027                         /* walk extensions ... */
1028                         e = ast_walk_context_extensions(c, NULL);
1029                         while (e) {
1030                                 struct ast_exten *p;
1031
1032                                 /* fireout priorities */
1033                                 p = ast_walk_extension_priorities(e, NULL);
1034                                 while (p) {
1035                                         if (!strcmp(ast_get_extension_registrar(p),
1036                                                 registrar)) {
1037                         
1038                                                 /* make empty line between different extensions */      
1039                                                 if (last_written_e != NULL &&
1040                                                         strcmp(ast_get_extension_name(last_written_e),
1041                                                                 ast_get_extension_name(p)))
1042                                                         fprintf(output, "\n");
1043                                                 last_written_e = p;
1044                                 
1045                                                 if (!context_header_written) {
1046                                                         fprintf(output, "[%s]\n", ast_get_context_name(c));
1047                                                         context_header_written = 1;
1048                                                 }
1049
1050                                                 if (ast_get_extension_priority(p)!=PRIORITY_HINT) {
1051                                                         char *tempdata = NULL, *startdata;
1052                                                         tempdata = strdup((char *)ast_get_extension_app_data(p));
1053                                                         if (tempdata) {
1054                                                                 startdata = tempdata;
1055                                                                 while (*tempdata) {
1056                                                                         if (*tempdata == '|')
1057                                                                                 *tempdata = ',';
1058                                                                         tempdata++;
1059                                                                 }
1060                                                                 tempdata = startdata;
1061                                                         }
1062                                                         if (ast_get_extension_matchcid(p))
1063                                                                 fprintf(output, "exten => %s/%s,%d,%s(%s)\n",
1064                                                                     ast_get_extension_name(p),
1065                                                                     ast_get_extension_cidmatch(p),
1066                                                                     ast_get_extension_priority(p),
1067                                                                     ast_get_extension_app(p),
1068                                                                     tempdata);
1069                                                         else
1070                                                                 fprintf(output, "exten => %s,%d,%s(%s)\n",
1071                                                                     ast_get_extension_name(p),
1072                                                                     ast_get_extension_priority(p),
1073                                                                     ast_get_extension_app(p),
1074                                                                     tempdata);
1075                                                         if (tempdata)
1076                                                                 free(tempdata);
1077                                                 } else
1078                                                         fprintf(output, "exten => %s,hint,%s\n",
1079                                                             ast_get_extension_name(p),
1080                                                             ast_get_extension_app(p));
1081                                                 
1082                                         }
1083                                         p = ast_walk_extension_priorities(e, p);
1084                                 }
1085
1086                                 e = ast_walk_context_extensions(c, e);
1087                         }
1088
1089                         /* written any extensions? ok, write space between exten & inc */
1090                         if (last_written_e) fprintf(output, "\n");
1091
1092                         /* walk through includes */
1093                         i = ast_walk_context_includes(c, NULL);
1094                         while (i) {
1095                                 if (!strcmp(ast_get_include_registrar(i), registrar)) {
1096                                         if (!context_header_written) {
1097                                                 fprintf(output, "[%s]\n", ast_get_context_name(c));
1098                                                 context_header_written = 1;
1099                                         }
1100                                         fprintf(output, "include => %s\n",
1101                                                 ast_get_include_name(i));
1102                                 }
1103                                 i = ast_walk_context_includes(c, i);
1104                         }
1105
1106                         if (ast_walk_context_includes(c, NULL))
1107                                 fprintf(output, "\n");
1108
1109                         /* walk through switches */
1110                         sw = ast_walk_context_switches(c, NULL);
1111                         while (sw) {
1112                                 if (!strcmp(ast_get_switch_registrar(sw), registrar)) {
1113                                         if (!context_header_written) {
1114                                                 fprintf(output, "[%s]\n", ast_get_context_name(c));
1115                                                 context_header_written = 1;
1116                                         }
1117                                         fprintf(output, "switch => %s/%s\n",
1118                                                 ast_get_switch_name(sw),
1119                                                 ast_get_switch_data(sw));
1120                                 }
1121                                 sw = ast_walk_context_switches(c, sw);
1122                         }
1123
1124                         if (ast_walk_context_switches(c, NULL))
1125                                 fprintf(output, "\n");
1126
1127                         /* fireout ignorepats ... */
1128                         ip = ast_walk_context_ignorepats(c, NULL);
1129                         while (ip) {
1130                                 if (!strcmp(ast_get_ignorepat_registrar(ip), registrar)) {
1131                                         if (!context_header_written) {
1132                                                 fprintf(output, "[%s]\n", ast_get_context_name(c));
1133                                                 context_header_written = 1;
1134                                         }
1135
1136                                         fprintf(output, "ignorepat => %s\n",
1137                                                 ast_get_ignorepat_name(ip));
1138                                 }
1139                                 ip = ast_walk_context_ignorepats(c, ip);
1140                         }
1141
1142                         ast_unlock_context(c);
1143                 } else
1144                         incomplete = 1;
1145
1146                 c = ast_walk_contexts(c);
1147         }       
1148
1149         ast_unlock_contexts();
1150         ast_mutex_unlock(&save_dialplan_lock);
1151         fclose(output);
1152
1153         if (incomplete) {
1154                 ast_cli(fd, "Saved dialplan is incomplete\n");
1155                 return RESULT_FAILURE;
1156         }
1157
1158         ast_cli(fd, "Dialplane successfully saved into '%s'\n",
1159                 filename);
1160         return RESULT_SUCCESS;
1161 }
1162
1163 /*
1164  * ADD EXTENSION command stuff
1165  */
1166 static int handle_context_add_extension(int fd, int argc, char *argv[])
1167 {
1168         char *whole_exten;
1169         char *exten, *prior;
1170         int iprior = -2;
1171         char *cidmatch, *app, *app_data;
1172         char *start, *end;
1173
1174         /* check for arguments at first */
1175         if (argc != 5 && argc != 6) return RESULT_SHOWUSAGE;
1176         if (strcmp(argv[3], "into")) return RESULT_SHOWUSAGE;
1177         if (argc == 6) if (strcmp(argv[5], "replace")) return RESULT_SHOWUSAGE;
1178
1179         whole_exten = argv[2];
1180         exten           = strsep(&whole_exten,",");
1181         if (strchr(exten, '/')) {
1182                 cidmatch = exten;
1183                 strsep(&cidmatch,"/");
1184         } else {
1185                 cidmatch = NULL;
1186         }
1187         prior       = strsep(&whole_exten,",");
1188         if (prior) {
1189         if (!strcmp(prior, "hint")) {
1190                         iprior = PRIORITY_HINT;
1191                 } else {
1192                         if (sscanf(prior, "%i", &iprior) != 1) {
1193                                 ast_cli(fd, "'%s' is not a valid priority\n", prior);
1194                                 prior = NULL;
1195                         }
1196                 }
1197         }
1198         app = whole_exten;
1199         if (app && (start = strchr(app, '(')) && (end = strrchr(app, ')'))) {
1200                 *start = *end = '\0';
1201                 app_data = start + 1;
1202                 process_quotes_and_slashes(app_data, ',', '|');
1203         } else {
1204                 if (app) {
1205                         app_data = strchr(app, ',');
1206                         if (app_data) {
1207                                 *app_data = '\0';
1208                                 app_data++;
1209                         }
1210                 } else  
1211                         app_data = NULL;
1212         }
1213
1214         if (!exten || !prior || !app || (!app_data && iprior != PRIORITY_HINT)) return RESULT_SHOWUSAGE;
1215
1216         if (!app_data)
1217                 app_data="";
1218         if (ast_add_extension(argv[4], argc == 6 ? 1 : 0, exten, iprior, cidmatch, app,
1219                 (void *)strdup(app_data), free, registrar)) {
1220                 switch (errno) {
1221                         case ENOMEM:
1222                                 ast_cli(fd, "Out of free memory\n"); break;
1223
1224                         case EBUSY:
1225                                 ast_cli(fd, "Failed to lock context(s) list, please try again later\n"); break;
1226
1227                         case ENOENT:
1228                                 ast_cli(fd, "No existence of '%s' context\n", argv[4]); break;
1229
1230                         case EEXIST:
1231                                 ast_cli(fd, "Extension %s@%s with priority %s already exists\n",
1232                                         exten, argv[4], prior); break;
1233
1234                         default:
1235                                 ast_cli(fd, "Failed to add '%s,%s,%s,%s' extension into '%s' context\n",
1236                                         exten, prior, app, app_data, argv[4]); break;
1237                 }
1238                 return RESULT_FAILURE;
1239         }
1240
1241         if (argc == 6) 
1242                 ast_cli(fd, "Extension %s@%s (%s) replace by '%s,%s,%s,%s'\n",
1243                         exten, argv[4], prior, exten, prior, app, app_data);
1244         else
1245                 ast_cli(fd, "Extension '%s,%s,%s,%s' added into '%s' context\n",
1246                         exten, prior, app, app_data, argv[4]);
1247
1248         return RESULT_SUCCESS;
1249 }
1250
1251 /* add extension 6123,1,Dial,IAX/212.71.138.13/6123 into local */
1252 static char *complete_context_add_extension(char *line, char *word,
1253         int pos, int state)
1254 {
1255         int which = 0;
1256
1257         /* complete 'into' word ... */
1258         if (pos == 3) {
1259                 if (state == 0) return strdup("into");
1260                 return NULL;
1261         }
1262
1263         /* complete context */
1264         if (pos == 4) {
1265                 struct ast_context *c;
1266
1267                 /* try to lock contexts list ... */
1268                 if (ast_lock_contexts()) {
1269                         ast_log(LOG_WARNING, "Failed to lock contexts list\n");
1270                         return NULL;
1271                 }
1272
1273                 /* walk through all contexts */
1274                 c = ast_walk_contexts(NULL);
1275                 while (c) {
1276                         /* matching context? */
1277                         if (!strncmp(ast_get_context_name(c), word, strlen(word))) {
1278                                 if (++which > state) {
1279                                         char *res = strdup(ast_get_context_name(c));
1280                                         ast_unlock_contexts();
1281                                         return res;
1282                                 }
1283                         }
1284                         c = ast_walk_contexts(c);
1285                 }
1286
1287                 ast_unlock_contexts();
1288                 return NULL;
1289         }
1290
1291         if (pos == 5) return state == 0 ? strdup("replace") : NULL;
1292
1293         return NULL;
1294 }
1295
1296 /*
1297  * IGNOREPAT CLI stuff
1298  */
1299 static int handle_context_add_ignorepat(int fd, int argc, char *argv[])
1300 {
1301         if (argc != 5) return RESULT_SHOWUSAGE;
1302         if (strcmp(argv[3], "into")) return RESULT_SHOWUSAGE;
1303
1304         if (ast_context_add_ignorepat(argv[4], argv[2], registrar)) {
1305                 switch (errno) {
1306                         case ENOMEM:
1307                                 ast_cli(fd, "Out of free memory\n"); break;
1308
1309                         case ENOENT:
1310                                 ast_cli(fd, "There is no existence of '%s' context\n", argv[4]);
1311                                 break;
1312
1313                         case EEXIST:
1314                                 ast_cli(fd, "Ignore pattern '%s' already included in '%s' context\n",
1315                                         argv[2], argv[4]);
1316                                 break;
1317
1318                         case EBUSY:
1319                                 ast_cli(fd, "Failed to lock context(s) list, please, try again later\n");
1320                                 break;
1321
1322                         default:
1323                                 ast_cli(fd, "Failed to add ingore pattern '%s' into '%s' context\n",
1324                                         argv[2], argv[4]);
1325                                 break;
1326                 }
1327                 return RESULT_FAILURE;
1328         }
1329
1330         ast_cli(fd, "Ignore pattern '%s' added into '%s' context\n",
1331                 argv[2], argv[4]);
1332         return RESULT_SUCCESS;
1333 }
1334
1335 static char *complete_context_add_ignorepat(char *line, char *word,
1336         int pos, int state)
1337 {
1338         if (pos == 3) return state == 0 ? strdup("into") : NULL;
1339
1340         if (pos == 4) {
1341                 struct ast_context *c;
1342                 int which = 0;
1343                 char *dupline, *duplinet, *ignorepat = NULL;
1344
1345                 dupline = strdup(line);
1346                 duplinet = dupline;
1347
1348                 if (duplinet) {
1349                         strsep(&duplinet, " "); /* skip 'add' */
1350                         strsep(&duplinet, " "); /* skip 'ignorepat' */
1351                         ignorepat = strsep(&duplinet, " ");
1352                 }
1353
1354                 if (ast_lock_contexts()) {
1355                         ast_log(LOG_ERROR, "Failed to lock contexts list\n");
1356                         return NULL;
1357                 }
1358
1359                 c = ast_walk_contexts(NULL);
1360                 while (c) {
1361                         if (!strncmp(ast_get_context_name(c), word, strlen(word))) {
1362                                 int serve_context = 1;
1363                                 if (ignorepat) {
1364                                         if (!ast_lock_context(c)) {
1365                                                 struct ast_ignorepat *ip;
1366                                                 ip = ast_walk_context_ignorepats(c, NULL);
1367                                                 while (ip && serve_context) {
1368                                                         if (!strcmp(ast_get_ignorepat_name(ip), ignorepat))
1369                                                                 serve_context = 0;
1370                                                         ip = ast_walk_context_ignorepats(c, ip);
1371                                                 }
1372                                                 ast_unlock_context(c);
1373                                         }
1374                                 }
1375                                 if (serve_context) {
1376                                         if (++which > state) {
1377                                                 char *context = strdup(ast_get_context_name(c));
1378                                                 if (dupline) free(dupline);
1379                                                 ast_unlock_contexts();
1380                                                 return context;
1381                                         }
1382                                 }
1383                         }
1384                         c = ast_walk_contexts(c);
1385                 }
1386
1387                 if (dupline) free(dupline);
1388                 ast_unlock_contexts();
1389                 return NULL;
1390         }
1391
1392         return NULL;
1393 }
1394
1395 static int handle_context_remove_ignorepat(int fd, int argc, char *argv[])
1396 {
1397         if (argc != 5) return RESULT_SHOWUSAGE;
1398         if (strcmp(argv[3], "from")) return RESULT_SHOWUSAGE;
1399
1400         if (ast_context_remove_ignorepat(argv[4], argv[2], registrar)) {
1401                 switch (errno) {
1402                         case EBUSY:
1403                                 ast_cli(fd, "Failed to lock context(s) list, please try again later\n");
1404                                 break;
1405
1406                         case ENOENT:
1407                                 ast_cli(fd, "There is no existence of '%s' context\n", argv[4]);
1408                                 break;
1409
1410                         case EINVAL:
1411                                 ast_cli(fd, "There is no existence of '%s' ignore pattern in '%s' context\n",
1412                                         argv[2], argv[4]);
1413                                 break;
1414
1415                         default:
1416                                 ast_cli(fd, "Failed to remove ignore pattern '%s' from '%s' context\n", argv[2], argv[4]);
1417                                 break;
1418                 }
1419                 return RESULT_FAILURE;
1420         }
1421
1422         ast_cli(fd, "Ignore pattern '%s' removed from '%s' context\n",
1423                 argv[2], argv[4]);
1424         return RESULT_SUCCESS;
1425 }
1426
1427 static int pbx_load_module(void);
1428
1429 static int handle_reload_extensions(int fd, int argc, char *argv[])
1430 {
1431         if (argc!=2) return RESULT_SHOWUSAGE;
1432         pbx_load_module();
1433         return RESULT_SUCCESS;
1434 }
1435
1436 static char *complete_context_remove_ignorepat(char *line, char *word,
1437         int pos, int state)
1438 {
1439         struct ast_context *c;
1440         int which = 0;
1441
1442         if (pos == 2) {
1443                 if (ast_lock_contexts()) {
1444                         ast_log(LOG_WARNING, "Failed to lock contexts list\n");
1445                         return NULL;
1446                 }
1447
1448                 c = ast_walk_contexts(NULL);
1449                 while (c) {
1450                         if (!ast_lock_context(c)) {
1451                                 struct ast_ignorepat *ip;
1452                         
1453                                 ip = ast_walk_context_ignorepats(c, NULL);
1454                                 while (ip) {
1455                                         if (!strncmp(ast_get_ignorepat_name(ip), word, strlen(word))) {
1456                                                 if (which + 1 > state) {
1457                                                         struct ast_context *cw;
1458                                                         int already_served = 0;
1459                                                         cw = ast_walk_contexts(NULL);
1460                                                         while (cw && cw != c && !already_served) {
1461                                                                 if (!ast_lock_context(cw)) {
1462                                                                         struct ast_ignorepat *ipw;
1463                                                                         ipw = ast_walk_context_ignorepats(cw, NULL);
1464                                                                         while (ipw) {
1465                                                                                 if (!strcmp(ast_get_ignorepat_name(ipw),
1466                                                                                         ast_get_ignorepat_name(ip))) already_served = 1;
1467                                                                                 ipw = ast_walk_context_ignorepats(cw, ipw);
1468                                                                         }
1469                                                                         ast_unlock_context(cw);
1470                                                                 }
1471                                                                 cw = ast_walk_contexts(cw);
1472                                                         }
1473                                                         if (!already_served) {
1474                                                                 char *ret = strdup(ast_get_ignorepat_name(ip));
1475                                                                 ast_unlock_context(c);
1476                                                                 ast_unlock_contexts();
1477                                                                 return ret;
1478                                                         }
1479                                                 } else
1480                                                         which++;
1481                                         }
1482                                         ip = ast_walk_context_ignorepats(c, ip);
1483                                 }
1484
1485                                 ast_unlock_context(c);
1486                         }
1487                         c = ast_walk_contexts(c);
1488                 }
1489
1490                 ast_unlock_contexts();
1491                 return NULL;
1492         }
1493  
1494         if (pos == 3) return state == 0 ? strdup("from") : NULL;
1495
1496         if (pos == 4) {
1497                 char *dupline, *duplinet, *ignorepat;
1498
1499                 dupline = strdup(line);
1500                 if (!dupline) {
1501                         ast_log(LOG_WARNING, "Out of free memory\n");
1502                         return NULL;
1503                 }
1504
1505                 duplinet = dupline;
1506                 strsep(&duplinet, " ");
1507                 strsep(&duplinet, " ");
1508                 ignorepat = strsep(&duplinet, " ");
1509
1510                 if (!ignorepat) {
1511                         free(dupline);
1512                         return NULL;
1513                 }
1514
1515                 if (ast_lock_contexts()) {
1516                         ast_log(LOG_WARNING, "Failed to lock contexts list\n");
1517                         free(dupline);
1518                         return NULL;
1519                 }
1520
1521                 c = ast_walk_contexts(NULL);
1522                 while (c) {
1523                         if (!ast_lock_context(c)) {
1524                                 struct ast_ignorepat *ip;
1525                                 ip = ast_walk_context_ignorepats(c, NULL);
1526                                 while (ip) {
1527                                         if (!strcmp(ast_get_ignorepat_name(ip), ignorepat)) {
1528                                                 if (!strncmp(ast_get_context_name(c), word, strlen(word))) {
1529                                                         if (++which > state) {
1530                                                                 char *ret = strdup(ast_get_context_name(c));
1531                                                                 free(dupline);
1532                                                                 ast_unlock_context(c);
1533                                                                 ast_unlock_contexts();
1534                                                                 return ret;
1535                                                         }
1536                                                 }
1537                                         }
1538                                         ip = ast_walk_context_ignorepats(c, ip);
1539                                 }
1540
1541                                 ast_unlock_context(c);
1542                         }
1543                         c = ast_walk_contexts(c);
1544                 }
1545
1546                 free(dupline);
1547                 ast_unlock_contexts();
1548                 return NULL;
1549         }
1550
1551         return NULL;
1552 }
1553
1554 /*
1555  * CLI entries for commands provided by this module
1556  */
1557 static struct ast_cli_entry context_dont_include_cli =
1558         { { "dont", "include", NULL }, handle_context_dont_include,
1559                 "Remove a specified include from context", context_dont_include_help,
1560                 complete_context_dont_include };
1561
1562 static struct ast_cli_entry context_remove_extension_cli =
1563         { { "remove", "extension", NULL }, handle_context_remove_extension,
1564                 "Remove a specified extension", context_remove_extension_help,
1565                 complete_context_remove_extension };
1566
1567 static struct ast_cli_entry context_add_include_cli =
1568         { { "include", "context", NULL }, handle_context_add_include,
1569                 "Include context in other context", context_add_include_help,
1570                 complete_context_add_include };
1571
1572 static struct ast_cli_entry save_dialplan_cli =
1573         { { "save", "dialplan", NULL }, handle_save_dialplan,
1574                 "Save dialplan", save_dialplan_help };
1575
1576 static struct ast_cli_entry context_add_extension_cli =
1577         { { "add", "extension", NULL }, handle_context_add_extension,
1578                 "Add new extension into context", context_add_extension_help,
1579                 complete_context_add_extension };
1580
1581 static struct ast_cli_entry context_add_ignorepat_cli =
1582         { { "add", "ignorepat", NULL }, handle_context_add_ignorepat,
1583                 "Add new ignore pattern", context_add_ignorepat_help,
1584                 complete_context_add_ignorepat };
1585
1586 static struct ast_cli_entry context_remove_ignorepat_cli =
1587         { { "remove", "ignorepat", NULL }, handle_context_remove_ignorepat,
1588                 "Remove ignore pattern from context", context_remove_ignorepat_help,
1589                 complete_context_remove_ignorepat };
1590
1591 static struct ast_cli_entry reload_extensions_cli = 
1592         { { "extensions", "reload", NULL}, handle_reload_extensions,
1593                 "Reload extensions and *only* extensions", reload_extensions_help };
1594
1595 /*
1596  * Standard module functions ...
1597  */
1598 int unload_module(void)
1599 {
1600         ast_cli_unregister(&context_add_extension_cli);
1601         if (static_config && !write_protect_config)
1602                 ast_cli_unregister(&save_dialplan_cli);
1603         ast_cli_unregister(&context_add_include_cli);
1604         ast_cli_unregister(&context_dont_include_cli);
1605         ast_cli_unregister(&context_remove_extension_cli);
1606         ast_cli_unregister(&context_remove_ignorepat_cli);
1607         ast_cli_unregister(&context_add_ignorepat_cli);
1608         ast_cli_unregister(&reload_extensions_cli);
1609         ast_context_destroy(NULL, registrar);
1610         return 0;
1611 }
1612
1613 static int pbx_load_module(void)
1614 {
1615         struct ast_config *cfg;
1616         struct ast_variable *v;
1617         char *cxt, *ext, *pri, *appl, *data, *tc, *cidmatch;
1618         struct ast_context *con;
1619         char *start, *end;
1620         char realvalue[256];
1621
1622         cfg = ast_load(config);
1623         if (cfg) {
1624                 /* Use existing config to populate the PBX table */
1625                 static_config = ast_true(ast_variable_retrieve(cfg, "general",
1626                         "static"));
1627                 write_protect_config = ast_true(ast_variable_retrieve(cfg, "general",
1628                         "writeprotect"));
1629                 v = ast_variable_browse(cfg, "globals");
1630                 while(v) {
1631                         memset(realvalue, 0, sizeof(realvalue));
1632                         pbx_substitute_variables_helper(NULL, v->value, realvalue, sizeof(realvalue) - 1);
1633                         pbx_builtin_setvar_helper(NULL, v->name, realvalue);
1634                         v = v->next;
1635                 }
1636                 cxt = ast_category_browse(cfg, NULL);
1637                 while(cxt) {
1638                         /* All categories but "general" or "globals" are considered contexts */
1639                         if (!strcasecmp(cxt, "general") || !strcasecmp(cxt, "globals")) {
1640                                 cxt = ast_category_browse(cfg, cxt);
1641                                 continue;
1642                         }
1643                         if ((con=ast_context_create(&local_contexts,cxt, registrar))) {
1644                                 v = ast_variable_browse(cfg, cxt);
1645                                 while(v) {
1646                                         if (!strcasecmp(v->name, "exten")) {
1647                                                 char *stringp=NULL;
1648                                                 int ipri = -2;
1649                                                 char realext[256]="";
1650                                                 tc = strdup(v->value);
1651                                                 if(tc!=NULL){
1652                                                         stringp=tc;
1653                                                         ext = strsep(&stringp, ",");
1654                                                         if (!ext)
1655                                                                 ext="";
1656                                                         pri = strsep(&stringp, ",");
1657                                                         if (!pri)
1658                                                                 pri="";
1659                                                         if (!strcmp(pri,"hint"))
1660                                                                 ipri=PRIORITY_HINT;
1661                                                         else {
1662                                                                 if (sscanf(pri, "%i", &ipri) != 1) {
1663                                                                         ast_log(LOG_WARNING, "Invalid priority '%s' at line %d\n", pri, v->lineno);
1664                                                                         ipri = 0;
1665                                                                 }
1666                                                         }
1667                                                         appl = stringp;
1668                                                         if (!appl)
1669                                                                 appl="";
1670                                                         if (!(start = strchr(appl, '('))) {
1671                                                                 if (stringp)
1672                                                                         appl = strsep(&stringp, ",");
1673                                                                 else
1674                                                                         appl = "";
1675                                                         }
1676                                                         if (start && (end = strrchr(appl, ')'))) {
1677                                                                 *start = *end = '\0';
1678                                                                 data = start + 1;
1679                                                                 process_quotes_and_slashes(data, ',', '|');
1680                                                         } else if (stringp!=NULL && *stringp=='"') {
1681                                                                 stringp++;
1682                                                                 data = strsep(&stringp, "\"");
1683                                                                 stringp++;
1684                                                         } else {
1685                                                                 if (stringp)
1686                                                                         data = strsep(&stringp, ",");
1687                                                                 else
1688                                                                         data = "";
1689                                                         }
1690                                                         cidmatch = strchr(ext, '/');
1691                                                         if (cidmatch) {
1692                                                                 *cidmatch = '\0';
1693                                                                 cidmatch++;
1694                                                         }
1695                                                         stringp=ext;
1696                                                         strsep(&stringp, "/");
1697
1698                                                         if (!data)
1699                                                                 data="";
1700                                                         while(*appl && (*appl < 33)) appl++;
1701                                                         pbx_substitute_variables_helper(NULL, ext, realext, sizeof(realext) - 1);
1702                                                         if (ipri) {
1703                                                                 if (ast_add_extension2(con, 0, realext, ipri, cidmatch, appl, strdup(data), FREE, registrar)) {
1704                                                                         ast_log(LOG_WARNING, "Unable to register extension at line %d\n", v->lineno);
1705                                                                 }
1706                                                         }
1707                                                         free(tc);
1708                                                 } else fprintf(stderr,"Error strdup returned NULL in %s\n",__PRETTY_FUNCTION__);
1709                                         } else if(!strcasecmp(v->name, "include")) {
1710                                                 memset(realvalue, 0, sizeof(realvalue));
1711                                                 pbx_substitute_variables_helper(NULL, v->value, realvalue, sizeof(realvalue) - 1);
1712                                                 if (ast_context_add_include2(con, realvalue, registrar))
1713                                                         ast_log(LOG_WARNING, "Unable to include context '%s' in context '%s'\n", v->value, cxt);
1714                                         } else if(!strcasecmp(v->name, "ignorepat")) {
1715                                                 memset(realvalue, 0, sizeof(realvalue));
1716                                                 pbx_substitute_variables_helper(NULL, v->value, realvalue, sizeof(realvalue) - 1);
1717                                                 if (ast_context_add_ignorepat2(con, realvalue, registrar))
1718                                                         ast_log(LOG_WARNING, "Unable to include ignorepat '%s' in context '%s'\n", v->value, cxt);
1719                                         } else if (!strcasecmp(v->name, "switch")) {
1720                                                 char *stringp=NULL;
1721                                                 memset(realvalue, 0, sizeof(realvalue));
1722                                                 pbx_substitute_variables_helper(NULL, v->value, realvalue, sizeof(realvalue) - 1);
1723                                                 tc = realvalue;
1724                                                 stringp=tc;
1725                                                 appl = strsep(&stringp, "/");
1726                                                 data = strsep(&stringp, "");
1727                                                 if (!data)
1728                                                         data = "";
1729                                                 if (ast_context_add_switch2(con, appl, data, registrar))
1730                                                         ast_log(LOG_WARNING, "Unable to include switch '%s' in context '%s'\n", v->value, cxt);
1731                                         }
1732                                         v = v->next;
1733                                 }
1734                         }
1735                         cxt = ast_category_browse(cfg, cxt);
1736                 }
1737                 ast_destroy(cfg);
1738         }
1739         ast_merge_contexts_and_delete(&local_contexts,registrar);
1740
1741         for (con = ast_walk_contexts(NULL); con; con = ast_walk_contexts(con))
1742                 ast_context_verify_includes(con);
1743
1744         return 0;
1745 }
1746
1747 int load_module(void)
1748 {
1749         if (pbx_load_module()) return -1;
1750  
1751         ast_cli_register(&context_remove_extension_cli);
1752         ast_cli_register(&context_dont_include_cli);
1753         ast_cli_register(&context_add_include_cli);
1754         if (static_config && !write_protect_config)
1755                 ast_cli_register(&save_dialplan_cli);
1756         ast_cli_register(&context_add_extension_cli);
1757         ast_cli_register(&context_add_ignorepat_cli);
1758         ast_cli_register(&context_remove_ignorepat_cli);
1759         ast_cli_register(&reload_extensions_cli);
1760
1761         return 0;
1762 }
1763
1764 int reload(void)
1765 {
1766         ast_context_destroy(NULL, registrar);
1767         /* For martin's global variables, don't clear them on reload */
1768 #if 0
1769         pbx_builtin_clear_globals();
1770 #endif  
1771         pbx_load_module();
1772         return 0;
1773 }
1774
1775 int usecount(void)
1776 {
1777         return 0;
1778 }
1779
1780 char *description(void)
1781 {
1782         return dtext;
1783 }
1784
1785 char *key(void)
1786 {
1787         return ASTERISK_GPL_KEY;
1788 }