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