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