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