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