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