Save CID and switches in "save dialplan" command (bug #2279)
[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 = strsep(&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                 app_data = whole_exten;
1205
1206         if (!exten || !prior || !app || (!app_data && iprior != PRIORITY_HINT)) return RESULT_SHOWUSAGE;
1207
1208         if (!app_data)
1209                 app_data="";
1210         if (ast_add_extension(argv[4], argc == 6 ? 1 : 0, exten, iprior, cidmatch, app,
1211                 (void *)strdup(app_data), free, registrar)) {
1212                 switch (errno) {
1213                         case ENOMEM:
1214                                 ast_cli(fd, "Out of free memory\n"); break;
1215
1216                         case EBUSY:
1217                                 ast_cli(fd, "Failed to lock context(s) list, please try again later\n"); break;
1218
1219                         case ENOENT:
1220                                 ast_cli(fd, "No existence of '%s' context\n", argv[4]); break;
1221
1222                         case EEXIST:
1223                                 ast_cli(fd, "Extension %s@%s with priority %s already exists\n",
1224                                         exten, argv[4], prior); break;
1225
1226                         default:
1227                                 ast_cli(fd, "Failed to add '%s,%s,%s,%s' extension into '%s' context\n",
1228                                         exten, prior, app, app_data, argv[4]); break;
1229                 }
1230                 return RESULT_FAILURE;
1231         }
1232
1233         if (argc == 6) 
1234                 ast_cli(fd, "Extension %s@%s (%s) replace by '%s,%s,%s,%s'\n",
1235                         exten, argv[4], prior, exten, prior, app, app_data);
1236         else
1237                 ast_cli(fd, "Extension '%s,%s,%s,%s' added into '%s' context\n",
1238                         exten, prior, app, app_data, argv[4]);
1239
1240         return RESULT_SUCCESS;
1241 }
1242
1243 /* add extension 6123,1,Dial,IAX/212.71.138.13/6123 into local */
1244 static char *complete_context_add_extension(char *line, char *word,
1245         int pos, int state)
1246 {
1247         int which = 0;
1248
1249         /* complete 'into' word ... */
1250         if (pos == 3) {
1251                 if (state == 0) return strdup("into");
1252                 return NULL;
1253         }
1254
1255         /* complete context */
1256         if (pos == 4) {
1257                 struct ast_context *c;
1258
1259                 /* try to lock contexts list ... */
1260                 if (ast_lock_contexts()) {
1261                         ast_log(LOG_WARNING, "Failed to lock contexts list\n");
1262                         return NULL;
1263                 }
1264
1265                 /* walk through all contexts */
1266                 c = ast_walk_contexts(NULL);
1267                 while (c) {
1268                         /* matching context? */
1269                         if (!strncmp(ast_get_context_name(c), word, strlen(word))) {
1270                                 if (++which > state) {
1271                                         char *res = strdup(ast_get_context_name(c));
1272                                         ast_unlock_contexts();
1273                                         return res;
1274                                 }
1275                         }
1276                         c = ast_walk_contexts(c);
1277                 }
1278
1279                 ast_unlock_contexts();
1280                 return NULL;
1281         }
1282
1283         if (pos == 5) return state == 0 ? strdup("replace") : NULL;
1284
1285         return NULL;
1286 }
1287
1288 /*
1289  * IGNOREPAT CLI stuff
1290  */
1291 static int handle_context_add_ignorepat(int fd, int argc, char *argv[])
1292 {
1293         if (argc != 5) return RESULT_SHOWUSAGE;
1294         if (strcmp(argv[3], "into")) return RESULT_SHOWUSAGE;
1295
1296         if (ast_context_add_ignorepat(argv[4], argv[2], registrar)) {
1297                 switch (errno) {
1298                         case ENOMEM:
1299                                 ast_cli(fd, "Out of free memory\n"); break;
1300
1301                         case ENOENT:
1302                                 ast_cli(fd, "There is no existence of '%s' context\n", argv[4]);
1303                                 break;
1304
1305                         case EEXIST:
1306                                 ast_cli(fd, "Ignore pattern '%s' already included in '%s' context\n",
1307                                         argv[2], argv[4]);
1308                                 break;
1309
1310                         case EBUSY:
1311                                 ast_cli(fd, "Failed to lock context(s) list, please, try again later\n");
1312                                 break;
1313
1314                         default:
1315                                 ast_cli(fd, "Failed to add ingore pattern '%s' into '%s' context\n",
1316                                         argv[2], argv[4]);
1317                                 break;
1318                 }
1319                 return RESULT_FAILURE;
1320         }
1321
1322         ast_cli(fd, "Ignore pattern '%s' added into '%s' context\n",
1323                 argv[2], argv[4]);
1324         return RESULT_SUCCESS;
1325 }
1326
1327 static char *complete_context_add_ignorepat(char *line, char *word,
1328         int pos, int state)
1329 {
1330         if (pos == 3) return state == 0 ? strdup("into") : NULL;
1331
1332         if (pos == 4) {
1333                 struct ast_context *c;
1334                 int which = 0;
1335                 char *dupline, *duplinet, *ignorepat = NULL;
1336
1337                 dupline = strdup(line);
1338                 duplinet = dupline;
1339
1340                 if (duplinet) {
1341                         strsep(&duplinet, " "); /* skip 'add' */
1342                         strsep(&duplinet, " "); /* skip 'ignorepat' */
1343                         ignorepat = strsep(&duplinet, " ");
1344                 }
1345
1346                 if (ast_lock_contexts()) {
1347                         ast_log(LOG_ERROR, "Failed to lock contexts list\n");
1348                         return NULL;
1349                 }
1350
1351                 c = ast_walk_contexts(NULL);
1352                 while (c) {
1353                         if (!strncmp(ast_get_context_name(c), word, strlen(word))) {
1354                                 int serve_context = 1;
1355                                 if (ignorepat) {
1356                                         if (!ast_lock_context(c)) {
1357                                                 struct ast_ignorepat *ip;
1358                                                 ip = ast_walk_context_ignorepats(c, NULL);
1359                                                 while (ip && serve_context) {
1360                                                         if (!strcmp(ast_get_ignorepat_name(ip), ignorepat))
1361                                                                 serve_context = 0;
1362                                                         ip = ast_walk_context_ignorepats(c, ip);
1363                                                 }
1364                                                 ast_unlock_context(c);
1365                                         }
1366                                 }
1367                                 if (serve_context) {
1368                                         if (++which > state) {
1369                                                 char *context = strdup(ast_get_context_name(c));
1370                                                 if (dupline) free(dupline);
1371                                                 ast_unlock_contexts();
1372                                                 return context;
1373                                         }
1374                                 }
1375                         }
1376                         c = ast_walk_contexts(c);
1377                 }
1378
1379                 if (dupline) free(dupline);
1380                 ast_unlock_contexts();
1381                 return NULL;
1382         }
1383
1384         return NULL;
1385 }
1386
1387 static int handle_context_remove_ignorepat(int fd, int argc, char *argv[])
1388 {
1389         if (argc != 5) return RESULT_SHOWUSAGE;
1390         if (strcmp(argv[3], "from")) return RESULT_SHOWUSAGE;
1391
1392         if (ast_context_remove_ignorepat(argv[4], argv[2], registrar)) {
1393                 switch (errno) {
1394                         case EBUSY:
1395                                 ast_cli(fd, "Failed to lock context(s) list, please try again later\n");
1396                                 break;
1397
1398                         case ENOENT:
1399                                 ast_cli(fd, "There is no existence of '%s' context\n", argv[4]);
1400                                 break;
1401
1402                         case EINVAL:
1403                                 ast_cli(fd, "There is no existence of '%s' ignore pattern in '%s' context\n",
1404                                         argv[2], argv[4]);
1405                                 break;
1406
1407                         default:
1408                                 ast_cli(fd, "Failed to remove ignore pattern '%s' from '%s' context\n", argv[2], argv[4]);
1409                                 break;
1410                 }
1411                 return RESULT_FAILURE;
1412         }
1413
1414         ast_cli(fd, "Ignore pattern '%s' removed from '%s' context\n",
1415                 argv[2], argv[4]);
1416         return RESULT_SUCCESS;
1417 }
1418
1419 static int pbx_load_module(void);
1420
1421 static int handle_reload_extensions(int fd, int argc, char *argv[])
1422 {
1423         if (argc!=2) return RESULT_SHOWUSAGE;
1424         pbx_load_module();
1425         return RESULT_SUCCESS;
1426 }
1427
1428 static char *complete_context_remove_ignorepat(char *line, char *word,
1429         int pos, int state)
1430 {
1431         struct ast_context *c;
1432         int which = 0;
1433
1434         if (pos == 2) {
1435                 if (ast_lock_contexts()) {
1436                         ast_log(LOG_WARNING, "Failed to lock contexts list\n");
1437                         return NULL;
1438                 }
1439
1440                 c = ast_walk_contexts(NULL);
1441                 while (c) {
1442                         if (!ast_lock_context(c)) {
1443                                 struct ast_ignorepat *ip;
1444                         
1445                                 ip = ast_walk_context_ignorepats(c, NULL);
1446                                 while (ip) {
1447                                         if (!strncmp(ast_get_ignorepat_name(ip), word, strlen(word))) {
1448                                                 if (which + 1 > state) {
1449                                                         struct ast_context *cw;
1450                                                         int already_served = 0;
1451                                                         cw = ast_walk_contexts(NULL);
1452                                                         while (cw && cw != c && !already_served) {
1453                                                                 if (!ast_lock_context(cw)) {
1454                                                                         struct ast_ignorepat *ipw;
1455                                                                         ipw = ast_walk_context_ignorepats(cw, NULL);
1456                                                                         while (ipw) {
1457                                                                                 if (!strcmp(ast_get_ignorepat_name(ipw),
1458                                                                                         ast_get_ignorepat_name(ip))) already_served = 1;
1459                                                                                 ipw = ast_walk_context_ignorepats(cw, ipw);
1460                                                                         }
1461                                                                         ast_unlock_context(cw);
1462                                                                 }
1463                                                                 cw = ast_walk_contexts(cw);
1464                                                         }
1465                                                         if (!already_served) {
1466                                                                 char *ret = strdup(ast_get_ignorepat_name(ip));
1467                                                                 ast_unlock_context(c);
1468                                                                 ast_unlock_contexts();
1469                                                                 return ret;
1470                                                         }
1471                                                 } else
1472                                                         which++;
1473                                         }
1474                                         ip = ast_walk_context_ignorepats(c, ip);
1475                                 }
1476
1477                                 ast_unlock_context(c);
1478                         }
1479                         c = ast_walk_contexts(c);
1480                 }
1481
1482                 ast_unlock_contexts();
1483                 return NULL;
1484         }
1485  
1486         if (pos == 3) return state == 0 ? strdup("from") : NULL;
1487
1488         if (pos == 4) {
1489                 char *dupline, *duplinet, *ignorepat;
1490
1491                 dupline = strdup(line);
1492                 if (!dupline) {
1493                         ast_log(LOG_WARNING, "Out of free memory\n");
1494                         return NULL;
1495                 }
1496
1497                 duplinet = dupline;
1498                 strsep(&duplinet, " ");
1499                 strsep(&duplinet, " ");
1500                 ignorepat = strsep(&duplinet, " ");
1501
1502                 if (!ignorepat) {
1503                         free(dupline);
1504                         return NULL;
1505                 }
1506
1507                 if (ast_lock_contexts()) {
1508                         ast_log(LOG_WARNING, "Failed to lock contexts list\n");
1509                         free(dupline);
1510                         return NULL;
1511                 }
1512
1513                 c = ast_walk_contexts(NULL);
1514                 while (c) {
1515                         if (!ast_lock_context(c)) {
1516                                 struct ast_ignorepat *ip;
1517                                 ip = ast_walk_context_ignorepats(c, NULL);
1518                                 while (ip) {
1519                                         if (!strcmp(ast_get_ignorepat_name(ip), ignorepat)) {
1520                                                 if (!strncmp(ast_get_context_name(c), word, strlen(word))) {
1521                                                         if (++which > state) {
1522                                                                 char *ret = strdup(ast_get_context_name(c));
1523                                                                 free(dupline);
1524                                                                 ast_unlock_context(c);
1525                                                                 ast_unlock_contexts();
1526                                                                 return ret;
1527                                                         }
1528                                                 }
1529                                         }
1530                                         ip = ast_walk_context_ignorepats(c, ip);
1531                                 }
1532
1533                                 ast_unlock_context(c);
1534                         }
1535                         c = ast_walk_contexts(c);
1536                 }
1537
1538                 free(dupline);
1539                 ast_unlock_contexts();
1540                 return NULL;
1541         }
1542
1543         return NULL;
1544 }
1545
1546 /*
1547  * CLI entries for commands provided by this module
1548  */
1549 static struct ast_cli_entry context_dont_include_cli =
1550         { { "dont", "include", NULL }, handle_context_dont_include,
1551                 "Remove a specified include from context", context_dont_include_help,
1552                 complete_context_dont_include };
1553
1554 static struct ast_cli_entry context_remove_extension_cli =
1555         { { "remove", "extension", NULL }, handle_context_remove_extension,
1556                 "Remove a specified extension", context_remove_extension_help,
1557                 complete_context_remove_extension };
1558
1559 static struct ast_cli_entry context_add_include_cli =
1560         { { "include", "context", NULL }, handle_context_add_include,
1561                 "Include context in other context", context_add_include_help,
1562                 complete_context_add_include };
1563
1564 static struct ast_cli_entry save_dialplan_cli =
1565         { { "save", "dialplan", NULL }, handle_save_dialplan,
1566                 "Save dialplan", save_dialplan_help };
1567
1568 static struct ast_cli_entry context_add_extension_cli =
1569         { { "add", "extension", NULL }, handle_context_add_extension,
1570                 "Add new extension into context", context_add_extension_help,
1571                 complete_context_add_extension };
1572
1573 static struct ast_cli_entry context_add_ignorepat_cli =
1574         { { "add", "ignorepat", NULL }, handle_context_add_ignorepat,
1575                 "Add new ignore pattern", context_add_ignorepat_help,
1576                 complete_context_add_ignorepat };
1577
1578 static struct ast_cli_entry context_remove_ignorepat_cli =
1579         { { "remove", "ignorepat", NULL }, handle_context_remove_ignorepat,
1580                 "Remove ignore pattern from context", context_remove_ignorepat_help,
1581                 complete_context_remove_ignorepat };
1582
1583 static struct ast_cli_entry reload_extensions_cli = 
1584         { { "extensions", "reload", NULL}, handle_reload_extensions,
1585                 "Reload extensions and *only* extensions", reload_extensions_help };
1586
1587 /*
1588  * Standard module functions ...
1589  */
1590 int unload_module(void)
1591 {
1592         ast_cli_unregister(&context_add_extension_cli);
1593         if (static_config && !write_protect_config)
1594                 ast_cli_unregister(&save_dialplan_cli);
1595         ast_cli_unregister(&context_add_include_cli);
1596         ast_cli_unregister(&context_dont_include_cli);
1597         ast_cli_unregister(&context_remove_extension_cli);
1598         ast_cli_unregister(&context_remove_ignorepat_cli);
1599         ast_cli_unregister(&context_add_ignorepat_cli);
1600         ast_cli_unregister(&reload_extensions_cli);
1601         ast_context_destroy(NULL, registrar);
1602         return 0;
1603 }
1604
1605 static int pbx_load_module(void)
1606 {
1607         struct ast_config *cfg;
1608         struct ast_variable *v;
1609         char *cxt, *ext, *pri, *appl, *data, *tc, *cidmatch;
1610         struct ast_context *con;
1611         char *start, *end;
1612         char realvalue[256];
1613
1614         cfg = ast_load(config);
1615         if (cfg) {
1616                 /* Use existing config to populate the PBX table */
1617                 static_config = ast_true(ast_variable_retrieve(cfg, "general",
1618                         "static"));
1619                 write_protect_config = ast_true(ast_variable_retrieve(cfg, "general",
1620                         "writeprotect"));
1621                 v = ast_variable_browse(cfg, "globals");
1622                 while(v) {
1623                         memset(realvalue, 0, sizeof(realvalue));
1624                         pbx_substitute_variables_helper(NULL, v->value, realvalue, sizeof(realvalue) - 1);
1625                         pbx_builtin_setvar_helper(NULL, v->name, realvalue);
1626                         v = v->next;
1627                 }
1628                 cxt = ast_category_browse(cfg, NULL);
1629                 while(cxt) {
1630                         /* All categories but "general" or "globals" are considered contexts */
1631                         if (!strcasecmp(cxt, "general") || !strcasecmp(cxt, "globals")) {
1632                                 cxt = ast_category_browse(cfg, cxt);
1633                                 continue;
1634                         }
1635                         if ((con=ast_context_create(&local_contexts,cxt, registrar))) {
1636                                 v = ast_variable_browse(cfg, cxt);
1637                                 while(v) {
1638                                         if (!strcasecmp(v->name, "exten")) {
1639                                                 char *stringp=NULL;
1640                                                 int ipri = -2;
1641                                                 char realext[256]="";
1642                                                 tc = strdup(v->value);
1643                                                 if(tc!=NULL){
1644                                                         stringp=tc;
1645                                                         ext = strsep(&stringp, ",");
1646                                                         if (!ext)
1647                                                                 ext="";
1648                                                         pri = strsep(&stringp, ",");
1649                                                         if (!pri)
1650                                                                 pri="";
1651                                                         if (!strcmp(pri,"hint"))
1652                                                                 ipri=PRIORITY_HINT;
1653                                                         else {
1654                                                                 if (sscanf(pri, "%i", &ipri) != 1) {
1655                                                                         ast_log(LOG_WARNING, "Invalid priority '%s' at line %d\n", pri, v->lineno);
1656                                                                         ipri = 0;
1657                                                                 }
1658                                                         }
1659                                                         appl = stringp;
1660                                                         if (!appl)
1661                                                                 appl="";
1662                                                         if (!(start = strchr(appl, '('))) {
1663                                                                 if (stringp)
1664                                                                         appl = strsep(&stringp, ",");
1665                                                                 else
1666                                                                         appl = "";
1667                                                         }
1668                                                         if (start && (end = strrchr(appl, ')'))) {
1669                                                                 *start = *end = '\0';
1670                                                                 data = start + 1;
1671                                                                 process_quotes_and_slashes(data, ',', '|');
1672                                                         } else if (stringp!=NULL && *stringp=='"') {
1673                                                                 stringp++;
1674                                                                 data = strsep(&stringp, "\"");
1675                                                                 stringp++;
1676                                                         } else {
1677                                                                 if (stringp)
1678                                                                         data = strsep(&stringp, ",");
1679                                                                 else
1680                                                                         data = "";
1681                                                         }
1682                                                         cidmatch = strchr(ext, '/');
1683                                                         if (cidmatch) {
1684                                                                 *cidmatch = '\0';
1685                                                                 cidmatch++;
1686                                                         }
1687                                                         stringp=ext;
1688                                                         strsep(&stringp, "/");
1689
1690                                                         if (!data)
1691                                                                 data="";
1692                                                         while(*appl && (*appl < 33)) appl++;
1693                                                         pbx_substitute_variables_helper(NULL, ext, realext, sizeof(realext) - 1);
1694                                                         if (ipri) {
1695                                                                 if (ast_add_extension2(con, 0, realext, ipri, cidmatch, appl, strdup(data), FREE, registrar)) {
1696                                                                         ast_log(LOG_WARNING, "Unable to register extension at line %d\n", v->lineno);
1697                                                                 }
1698                                                         }
1699                                                         free(tc);
1700                                                 } else fprintf(stderr,"Error strdup returned NULL in %s\n",__PRETTY_FUNCTION__);
1701                                         } else if(!strcasecmp(v->name, "include")) {
1702                                                 memset(realvalue, 0, sizeof(realvalue));
1703                                                 pbx_substitute_variables_helper(NULL, v->value, realvalue, sizeof(realvalue) - 1);
1704                                                 if (ast_context_add_include2(con, realvalue, registrar))
1705                                                         ast_log(LOG_WARNING, "Unable to include context '%s' in context '%s'\n", v->value, cxt);
1706                                         } else if(!strcasecmp(v->name, "ignorepat")) {
1707                                                 memset(realvalue, 0, sizeof(realvalue));
1708                                                 pbx_substitute_variables_helper(NULL, v->value, realvalue, sizeof(realvalue) - 1);
1709                                                 if (ast_context_add_ignorepat2(con, realvalue, registrar))
1710                                                         ast_log(LOG_WARNING, "Unable to include ignorepat '%s' in context '%s'\n", v->value, cxt);
1711                                         } else if (!strcasecmp(v->name, "switch")) {
1712                                                 char *stringp=NULL;
1713                                                 memset(realvalue, 0, sizeof(realvalue));
1714                                                 pbx_substitute_variables_helper(NULL, v->value, realvalue, sizeof(realvalue) - 1);
1715                                                 tc = realvalue;
1716                                                 stringp=tc;
1717                                                 appl = strsep(&stringp, "/");
1718                                                 data = strsep(&stringp, "");
1719                                                 if (!data)
1720                                                         data = "";
1721                                                 if (ast_context_add_switch2(con, appl, data, registrar))
1722                                                         ast_log(LOG_WARNING, "Unable to include switch '%s' in context '%s'\n", v->value, cxt);
1723                                         }
1724                                         v = v->next;
1725                                 }
1726                         }
1727                         cxt = ast_category_browse(cfg, cxt);
1728                 }
1729                 ast_destroy(cfg);
1730         }
1731         ast_merge_contexts_and_delete(&local_contexts,registrar);
1732
1733         for (con = ast_walk_contexts(NULL); con; con = ast_walk_contexts(con))
1734                 ast_context_verify_includes(con);
1735
1736         return 0;
1737 }
1738
1739 int load_module(void)
1740 {
1741         if (pbx_load_module()) return -1;
1742  
1743         ast_cli_register(&context_remove_extension_cli);
1744         ast_cli_register(&context_dont_include_cli);
1745         ast_cli_register(&context_add_include_cli);
1746         if (static_config && !write_protect_config)
1747                 ast_cli_register(&save_dialplan_cli);
1748         ast_cli_register(&context_add_extension_cli);
1749         ast_cli_register(&context_add_ignorepat_cli);
1750         ast_cli_register(&context_remove_ignorepat_cli);
1751         ast_cli_register(&reload_extensions_cli);
1752
1753         return 0;
1754 }
1755
1756 int reload(void)
1757 {
1758         ast_context_destroy(NULL, registrar);
1759         /* For martin's global variables, don't clear them on reload */
1760 #if 0
1761         pbx_builtin_clear_globals();
1762 #endif  
1763         pbx_load_module();
1764         return 0;
1765 }
1766
1767 int usecount(void)
1768 {
1769         return 0;
1770 }
1771
1772 char *description(void)
1773 {
1774         return dtext;
1775 }
1776
1777 char *key(void)
1778 {
1779         return ASTERISK_GPL_KEY;
1780 }