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