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