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