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