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