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