Merge hint patch, add new variables, and misc. PBX cleanups
[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 (!strcmp(prior, "hint")) {
1088             iprior = PRIORITY_HINT;
1089         } else {
1090             iprior = atoi(prior);
1091         }
1092         app         = strsep(&whole_exten,",");
1093         if ((start = strchr(app, '(')) && (end = strrchr(app, ')'))) {
1094                 *start = *end = '\0';
1095                 app_data = start + 1;
1096                 for (start = app_data; *start; start++)
1097                         if (*start == ',')
1098                                 *start = '|';
1099         } else
1100                 app_data    = whole_exten;
1101
1102         if (!exten || !prior || !app || (!app_data && iprior != PRIORITY_HINT)) return RESULT_SHOWUSAGE;
1103
1104         if (!app_data)
1105             app_data="";
1106         if (ast_add_extension(argv[4], argc == 6 ? 1 : 0, exten, iprior, cidmatch, app,
1107                 (void *)strdup(app_data), free, registrar)) {
1108                 switch (errno) {
1109                         case ENOMEM:
1110                                 ast_cli(fd, "Out of free memory\n"); break;
1111
1112                         case EBUSY:
1113                                 ast_cli(fd, "Failed to lock context(s) list, please try again later\n"); break;
1114
1115                         case ENODATA:
1116                                 ast_cli(fd, "No existence of '%s' context\n", argv[4]); break;
1117
1118                         case EEXIST:
1119                                 ast_cli(fd, "Extension %s@%s with priority %s already exists\n",
1120                                         exten, argv[4], prior); break;
1121
1122                         default:
1123                                 ast_cli(fd, "Failed to add '%s,%s,%s,%s' extension into '%s' context\n",
1124                                         exten, prior, app, app_data, argv[4]); break;
1125                 }
1126                 return RESULT_FAILURE;
1127         }
1128
1129         if (argc == 6) 
1130                 ast_cli(fd, "Extension %s@%s (%s) replace by '%s,%s,%s,%s'\n",
1131                         exten, argv[4], prior, exten, prior, app, app_data);
1132         else
1133                 ast_cli(fd, "Extension '%s,%s,%s,%s' added into '%s' context\n",
1134                         exten, prior, app, app_data, argv[4]);
1135
1136         return RESULT_SUCCESS;
1137 }
1138
1139 /* add extension 6123,1,Dial,IAX/212.71.138.13/6123 into local */
1140 static char *complete_context_add_extension(char *line, char *word,
1141         int pos, int state)
1142 {
1143         int which = 0;
1144
1145         /* complete 'into' word ... */
1146         if (pos == 3) {
1147                 if (state == 0) return strdup("into");
1148                 return NULL;
1149         }
1150
1151         /* complete context */
1152         if (pos == 4) {
1153                 struct ast_context *c;
1154
1155                 /* try to lock contexts list ... */
1156                 if (ast_lock_contexts()) {
1157                         ast_log(LOG_WARNING, "Failed to lock contexts list\n");
1158                         return NULL;
1159                 }
1160
1161                 /* walk through all contexts */
1162                 c = ast_walk_contexts(NULL);
1163                 while (c) {
1164                         /* matching context? */
1165                         if (!strncmp(ast_get_context_name(c), word, strlen(word))) {
1166                                 if (++which > state) {
1167                                         char *res = strdup(ast_get_context_name(c));
1168                                         ast_unlock_contexts();
1169                                         return res;
1170                                 }
1171                         }
1172                         c = ast_walk_contexts(c);
1173                 }
1174
1175                 ast_unlock_contexts();
1176                 return NULL;
1177         }
1178
1179         if (pos == 5) return state == 0 ? strdup("replace") : NULL;
1180
1181         return NULL;
1182 }
1183
1184 /*
1185  * IGNOREPAT CLI stuff
1186  */
1187 static int handle_context_add_ignorepat(int fd, int argc, char *argv[])
1188 {
1189         if (argc != 5) return RESULT_SHOWUSAGE;
1190         if (strcmp(argv[3], "into")) return RESULT_SHOWUSAGE;
1191
1192         if (ast_context_add_ignorepat(argv[4], argv[2], registrar)) {
1193                 switch (errno) {
1194                         case ENOMEM:
1195                                 ast_cli(fd, "Out of free memory\n"); break;
1196
1197                         case ENODATA:
1198                                 ast_cli(fd, "There is no existence of '%s' context\n", argv[4]);
1199                                 break;
1200
1201                         case EEXIST:
1202                                 ast_cli(fd, "Ignore pattern '%s' already included in '%s' context\n",
1203                                         argv[2], argv[4]);
1204                                 break;
1205
1206                         case EBUSY:
1207                                 ast_cli(fd, "Failed to lock context(s) list, please, try again later\n");
1208                                 break;
1209
1210                         default:
1211                                 ast_cli(fd, "Failed to add ingore pattern '%s' into '%s' context\n",
1212                                         argv[2], argv[4]);
1213                                 break;
1214                 }
1215                 return RESULT_FAILURE;
1216         }
1217
1218         ast_cli(fd, "Ignore pattern '%s' added into '%s' context\n",
1219                 argv[2], argv[4]);
1220         return RESULT_SUCCESS;
1221 }
1222
1223 static char *complete_context_add_ignorepat(char *line, char *word,
1224         int pos, int state)
1225 {
1226         if (pos == 3) return state == 0 ? strdup("into") : NULL;
1227
1228         if (pos == 4) {
1229                 struct ast_context *c;
1230                 int which = 0;
1231                 char *dupline, *duplinet, *ignorepat = NULL;
1232
1233                 dupline = strdup(line);
1234                 duplinet = dupline;
1235
1236                 if (duplinet) {
1237                         strsep(&duplinet, " "); /* skip 'add' */
1238                         strsep(&duplinet, " "); /* skip 'ignorepat' */
1239                         ignorepat = strsep(&duplinet, " ");
1240                 }
1241
1242                 if (ast_lock_contexts()) {
1243                         ast_log(LOG_ERROR, "Failed to lock contexts list\n");
1244                         return NULL;
1245                 }
1246
1247                 c = ast_walk_contexts(NULL);
1248                 while (c) {
1249                         if (!strncmp(ast_get_context_name(c), word, strlen(word))) {
1250                                 int serve_context = 1;
1251                                 if (ignorepat) {
1252                                         if (!ast_lock_context(c)) {
1253                                                 struct ast_ignorepat *ip;
1254                                                 ip = ast_walk_context_ignorepats(c, NULL);
1255                                                 while (ip && serve_context) {
1256                                                         if (!strcmp(ast_get_ignorepat_name(ip), ignorepat))
1257                                                                 serve_context = 0;
1258                                                         ip = ast_walk_context_ignorepats(c, ip);
1259                                                 }
1260                                                 ast_unlock_context(c);
1261                                         }
1262                                 }
1263                                 if (serve_context) {
1264                                         if (++which > state) {
1265                                                 char *context = strdup(ast_get_context_name(c));
1266                                                 if (dupline) free(dupline);
1267                                                 ast_unlock_contexts();
1268                                                 return context;
1269                                         }
1270                                 }
1271                         }
1272                         c = ast_walk_contexts(c);
1273                 }
1274
1275                 if (dupline) free(dupline);
1276                 ast_unlock_contexts();
1277                 return NULL;
1278         }
1279
1280         return NULL;
1281 }
1282
1283 static int handle_context_remove_ignorepat(int fd, int argc, char *argv[])
1284 {
1285         if (argc != 5) return RESULT_SHOWUSAGE;
1286         if (strcmp(argv[3], "from")) return RESULT_SHOWUSAGE;
1287
1288         if (ast_context_remove_ignorepat(argv[4], argv[2], registrar)) {
1289                 switch (errno) {
1290                         case EBUSY:
1291                                 ast_cli(fd, "Failed to lock context(s) list, please try again later\n");
1292                                 break;
1293
1294                         case ENODATA:
1295                                 ast_cli(fd, "There is no existence of '%s' context\n", argv[4]);
1296                                 break;
1297
1298                         case EINVAL:
1299                                 ast_cli(fd, "There is no existence of '%s' ignore pattern in '%s' context\n",
1300                                         argv[2], argv[4]);
1301                                 break;
1302
1303                         default:
1304                                 ast_cli(fd, "Failed to remove ignore pattern '%s' from '%s' context\n");
1305                                 break;
1306                 }
1307                 return RESULT_FAILURE;
1308         }
1309
1310         ast_cli(fd, "Ignore pattern '%s' removed from '%s' context\n",
1311                 argv[2], argv[4]);
1312         return RESULT_SUCCESS;
1313 }
1314
1315 static char *complete_context_remove_ignorepat(char *line, char *word,
1316         int pos, int state)
1317 {
1318         struct ast_context *c;
1319         int which = 0;
1320
1321         if (pos == 2) {
1322                 if (ast_lock_contexts()) {
1323                         ast_log(LOG_WARNING, "Failed to lock contexts list\n");
1324                         return NULL;
1325                 }
1326
1327                 c = ast_walk_contexts(NULL);
1328                 while (c) {
1329                         if (!ast_lock_context(c)) {
1330                                 struct ast_ignorepat *ip;
1331                         
1332                                 ip = ast_walk_context_ignorepats(c, NULL);
1333                                 while (ip) {
1334                                         if (!strncmp(ast_get_ignorepat_name(ip), word, strlen(word))) {
1335                                                 if (which + 1 > state) {
1336                                                         struct ast_context *cw;
1337                                                         int already_served = 0;
1338                                                         cw = ast_walk_contexts(NULL);
1339                                                         while (cw && cw != c && !already_served) {
1340                                                                 if (!ast_lock_context(cw)) {
1341                                                                         struct ast_ignorepat *ipw;
1342                                                                         ipw = ast_walk_context_ignorepats(cw, NULL);
1343                                                                         while (ipw) {
1344                                                                                 if (!strcmp(ast_get_ignorepat_name(ipw),
1345                                                                                         ast_get_ignorepat_name(ip))) already_served = 1;
1346                                                                                 ipw = ast_walk_context_ignorepats(cw, ipw);
1347                                                                         }
1348                                                                         ast_unlock_context(cw);
1349                                                                 }
1350                                                                 cw = ast_walk_contexts(cw);
1351                                                         }
1352                                                         if (!already_served) {
1353                                                                 char *ret = strdup(ast_get_ignorepat_name(ip));
1354                                                                 ast_unlock_context(c);
1355                                                                 ast_unlock_contexts();
1356                                                                 return ret;
1357                                                         }
1358                                                 } else
1359                                                         which++;
1360                                         }
1361                                         ip = ast_walk_context_ignorepats(c, ip);
1362                                 }
1363
1364                                 ast_unlock_context(c);
1365                         }
1366                         c = ast_walk_contexts(c);
1367                 }
1368
1369                 ast_unlock_contexts();
1370                 return NULL;
1371         }
1372  
1373         if (pos == 3) return state == 0 ? strdup("from") : NULL;
1374
1375         if (pos == 4) {
1376                 char *dupline, *duplinet, *ignorepat;
1377
1378                 dupline = strdup(line);
1379                 if (!dupline) {
1380                         ast_log(LOG_WARNING, "Out of free memory\n");
1381                         return NULL;
1382                 }
1383
1384                 duplinet = dupline;
1385                 strsep(&duplinet, " ");
1386                 strsep(&duplinet, " ");
1387                 ignorepat = strsep(&duplinet, " ");
1388
1389                 if (!ignorepat) {
1390                         free(dupline);
1391                         return NULL;
1392                 }
1393
1394                 if (ast_lock_contexts()) {
1395                         ast_log(LOG_WARNING, "Failed to lock contexts list\n");
1396                         free(dupline);
1397                         return NULL;
1398                 }
1399
1400                 c = ast_walk_contexts(NULL);
1401                 while (c) {
1402                         if (!ast_lock_context(c)) {
1403                                 struct ast_ignorepat *ip;
1404                                 ip = ast_walk_context_ignorepats(c, NULL);
1405                                 while (ip) {
1406                                         if (!strcmp(ast_get_ignorepat_name(ip), ignorepat)) {
1407                                                 if (!strncmp(ast_get_context_name(c), word, strlen(word))) {
1408                                                         if (++which > state) {
1409                                                                 char *ret = strdup(ast_get_context_name(c));
1410                                                                 free(dupline);
1411                                                                 ast_unlock_context(c);
1412                                                                 ast_unlock_contexts();
1413                                                                 return ret;
1414                                                         }
1415                                                 }
1416                                         }
1417                                         ip = ast_walk_context_ignorepats(c, ip);
1418                                 }
1419
1420                                 ast_unlock_context(c);
1421                         }
1422                         c = ast_walk_contexts(c);
1423                 }
1424
1425                 free(dupline);
1426                 ast_unlock_contexts();
1427                 return NULL;
1428         }
1429
1430         return NULL;
1431 }
1432
1433 /*
1434  * CLI entries for commands provided by this module
1435  */
1436 static struct ast_cli_entry context_dont_include_cli =
1437         { { "dont", "include", NULL }, handle_context_dont_include,
1438                 "Remove a specified include from context", context_dont_include_help,
1439                 complete_context_dont_include };
1440
1441 static struct ast_cli_entry context_remove_extension_cli =
1442         { { "remove", "extension", NULL }, handle_context_remove_extension,
1443                 "Remove a specified extension", context_remove_extension_help,
1444                 complete_context_remove_extension };
1445
1446 static struct ast_cli_entry context_add_include_cli =
1447         { { "include", "context", NULL }, handle_context_add_include,
1448                 "Include context in other context", context_add_include_help,
1449                 complete_context_add_include };
1450
1451 static struct ast_cli_entry save_dialplan_cli =
1452         { { "save", "dialplan", NULL }, handle_save_dialplan,
1453                 "Save dialplan", save_dialplan_help };
1454
1455 static struct ast_cli_entry context_add_extension_cli =
1456         { { "add", "extension", NULL }, handle_context_add_extension,
1457                 "Add new extension into context", context_add_extension_help,
1458                 complete_context_add_extension };
1459
1460 static struct ast_cli_entry context_add_ignorepat_cli =
1461         { { "add", "ignorepat", NULL }, handle_context_add_ignorepat,
1462                 "Add new ignore pattern", context_add_ignorepat_help,
1463                 complete_context_add_ignorepat };
1464
1465 static struct ast_cli_entry context_remove_ignorepat_cli =
1466         { { "remove", "ignorepat", NULL }, handle_context_remove_ignorepat,
1467                 "Remove ignore pattern from context", context_remove_ignorepat_help,
1468                 complete_context_remove_ignorepat };
1469
1470 /*
1471  * Standard module functions ...
1472  */
1473 int unload_module(void)
1474 {
1475         ast_cli_unregister(&context_add_extension_cli);
1476         if (static_config && !write_protect_config)
1477                 ast_cli_unregister(&save_dialplan_cli);
1478         ast_cli_unregister(&context_add_include_cli);
1479         ast_cli_unregister(&context_dont_include_cli);
1480         ast_cli_unregister(&context_remove_extension_cli);
1481         ast_cli_unregister(&context_remove_ignorepat_cli);
1482         ast_cli_unregister(&context_add_ignorepat_cli);
1483         ast_context_destroy(NULL, registrar);
1484         return 0;
1485 }
1486
1487 static int pbx_load_module(void)
1488 {
1489         struct ast_config *cfg;
1490         struct ast_variable *v;
1491         char *cxt, *ext, *pri, *appl, *data, *tc, *cidmatch;
1492         struct ast_context *con;
1493         char *start, *end;
1494
1495         cfg = ast_load(config);
1496         if (cfg) {
1497                 /* Use existing config to populate the PBX table */
1498                 static_config = ast_true(ast_variable_retrieve(cfg, "general",
1499                         "static"));
1500                 write_protect_config = ast_true(ast_variable_retrieve(cfg, "general",
1501                         "writeprotect"));
1502                 v = ast_variable_browse(cfg, "globals");
1503                 while(v) {
1504                         pbx_builtin_setvar_helper(NULL, v->name, v->value);
1505                         v = v->next;
1506                 }
1507                 cxt = ast_category_browse(cfg, NULL);
1508                 while(cxt) {
1509                         /* All categories but "general" are considered contexts */
1510                         if (!strcasecmp(cxt, "general") || !strcasecmp(cxt, "globals")) {
1511                                 cxt = ast_category_browse(cfg, cxt);
1512                                 continue;
1513                         }
1514                         if ((con=ast_context_create(cxt, registrar))) {
1515                                 v = ast_variable_browse(cfg, cxt);
1516                                 while(v) {
1517                                         if (!strcasecmp(v->name, "exten")) {
1518                                                 char *stringp=NULL;
1519                                                 int ipri = -2;
1520                                                 tc = strdup(v->value);
1521                                                 if(tc!=NULL){
1522                                                         stringp=tc;
1523                                                         ext = strsep(&stringp, ",");
1524                                                         if (!ext)
1525                                                                 ext="";
1526                                                         pri = strsep(&stringp, ",");
1527                                                         if (!pri)
1528                                                                 pri="";
1529                                                         if (!strcmp(pri,"hint"))
1530                                                                 ipri=PRIORITY_HINT;
1531                                                         else
1532                                                                 ipri=atoi(pri);
1533                                                         appl = stringp;
1534                                                         if (!appl)
1535                                                                 appl="";
1536                                                         if (!(start = strchr(appl, '('))) {
1537                                                                 if (stringp)
1538                                                                         appl = strsep(&stringp, ",");
1539                                                                 else
1540                                                                         appl = "";
1541                                                         }
1542                                                         if (start && (end = strrchr(appl, ')'))) {
1543                                                                 *start = *end = '\0';
1544                                                                 data = start + 1;
1545                                                                 for (start = data; *start; start++)
1546                                                                         if (*start == ',')
1547                                                                                 *start = '|';
1548                                                         } else if (stringp!=NULL && *stringp=='"') {
1549                                                                 stringp++;
1550                                                                 data = strsep(&stringp, "\"");
1551                                                                 stringp++;
1552                                                         } else {
1553                                                                 if (stringp)
1554                                                                         data = strsep(&stringp, ",");
1555                                                                 else
1556                                                                         data = "";
1557                                                         }
1558                                                         cidmatch = strchr(ext, '/');
1559                                                         if (cidmatch) {
1560                                                                 *cidmatch = '\0';
1561                                                                 cidmatch++;
1562                                                         }
1563                                                         stringp=ext;
1564                                                         strsep(&stringp, "/");
1565
1566                                                         if (!data)
1567                                                                 data="";
1568                                                         if (ast_add_extension2(con, 0, ext, ipri, cidmatch, appl, strdup(data), free, registrar)) {
1569                                                                 ast_log(LOG_WARNING, "Unable to register extension at line %d\n", v->lineno);
1570                                                         }
1571                                                         free(tc);
1572                                                 } else fprintf(stderr,"Error strdup returned NULL in %s\n",__PRETTY_FUNCTION__);
1573                                         } else if(!strcasecmp(v->name, "include")) {
1574                                                 if (ast_context_add_include2(con, v->value, registrar))
1575                                                         ast_log(LOG_WARNING, "Unable to include context '%s' in context '%s'\n", v->value, cxt);
1576                                         } else if(!strcasecmp(v->name, "ignorepat")) {
1577                                                 if (ast_context_add_ignorepat2(con, v->value, registrar))
1578                                                         ast_log(LOG_WARNING, "Unable to include ignorepat '%s' in context '%s'\n", v->value, cxt);
1579                                         } else if (!strcasecmp(v->name, "switch")) {
1580                                                 char *stringp=NULL;
1581                                                 tc = strdup(v->value);
1582                                                 stringp=tc;
1583                                                 appl = strsep(&stringp, "/");
1584                                                 data = strsep(&stringp, "");
1585                                                 if (!data)
1586                                                         data = "";
1587                                                 if (ast_context_add_switch2(con, appl, data, registrar))
1588                                                         ast_log(LOG_WARNING, "Unable to include switch '%s' in context '%s'\n", v->value, cxt);
1589                                         }
1590                                         v = v->next;
1591                                 }
1592                         }
1593                         cxt = ast_category_browse(cfg, cxt);
1594                 }
1595                 ast_destroy(cfg);
1596         }
1597         return 0;
1598 }
1599
1600 int load_module(void)
1601 {
1602         if (pbx_load_module()) return -1;
1603  
1604         ast_cli_register(&context_remove_extension_cli);
1605         ast_cli_register(&context_dont_include_cli);
1606         ast_cli_register(&context_add_include_cli);
1607         if (static_config && !write_protect_config)
1608                 ast_cli_register(&save_dialplan_cli);
1609         ast_cli_register(&context_add_extension_cli);
1610         ast_cli_register(&context_add_ignorepat_cli);
1611         ast_cli_register(&context_remove_ignorepat_cli);
1612
1613         return 0;
1614 }
1615
1616 int reload(void)
1617 {
1618         ast_context_destroy(NULL, registrar);
1619         /* For martin's global variables, don't clear them on reload */
1620 #if 0
1621         pbx_builtin_clear_globals();
1622 #endif  
1623         pbx_load_module();
1624         return 0;
1625 }
1626
1627 int usecount(void)
1628 {
1629         return 0;
1630 }
1631
1632 char *description(void)
1633 {
1634         return dtext;
1635 }
1636
1637 char *key(void)
1638 {
1639         return ASTERISK_GPL_KEY;
1640 }