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