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