pbx: Create pbx_ignorepat.c for management of 'struct ast_ignorepat'.
[asterisk/asterisk.git] / pbx / pbx_config.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2006, Digium, Inc.
5  *
6  * Mark Spencer <markster@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19 /*! \file
20  *
21  * \brief Populate and remember extensions from static config file
22  *
23  * 
24  */
25
26 /*** MODULEINFO
27         <support_level>core</support_level>
28  ***/
29
30 /*** DOCUMENTATION
31         <manager name="DialplanExtensionAdd" language="en_US">
32                 <synopsis>
33                         Add an extension to the dialplan
34                 </synopsis>
35                 <syntax>
36                         <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
37                         <parameter name="Context" required="true">
38                                 <para>Context where the extension will be created. The context will
39                                 be created if it does not already exist.</para>
40                         </parameter>
41                         <parameter name="Extension" required="true">
42                                 <para>Name of the extension that will be created (may include callerid match by separating
43                                 with '/')</para>
44                         </parameter>
45                         <parameter name="Priority" required="true">
46                                 <para>Priority being added to this extension. Must be either <literal>hint</literal> or a
47                                 numerical value.</para>
48                         </parameter>
49                         <parameter name="Application" required="true">
50                                 <para>The application to use for this extension at the requested priority</para>
51                         </parameter>
52                         <parameter name="ApplicationData" required="false">
53                                 <para>Arguments to the application.</para>
54                         </parameter>
55                         <parameter name="Replace" required="false">
56                                 <para>If set to 'yes', '1', 'true' or any of the other values we evaluate as true, then
57                                 if an extension already exists at the requested context, extension, and priority it will
58                                 be overwritten. Otherwise, the existing extension will remain and the action will fail.
59                                 </para>
60                         </parameter>
61                 </syntax>
62         </manager>
63         <manager name="DialplanExtensionRemove" language="en_US">
64                 <synopsis>
65                         Remove an extension from the dialplan
66                 </synopsis>
67                 <syntax>
68                         <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
69                         <parameter name="Context" required="true">
70                                 <para>Context of the extension being removed</para>
71                         </parameter>
72                         <parameter name="Extension" required="true">
73                                 <para>Name of the extension being removed (may include callerid match by separating with '/')</para>
74                         </parameter>
75                         <parameter name="Priority" required="false">
76                                 <para>If provided, only remove this priority from the extension instead of all
77                                 priorities in the extension.</para>
78                         </parameter>
79                 </syntax>
80         </manager>
81  ***/
82
83 #include "asterisk.h"
84
85 ASTERISK_REGISTER_FILE()
86
87 #include <ctype.h>
88
89 #include "asterisk/paths.h"     /* ast_config_AST_CONFIG_DIR */
90 #include "asterisk/pbx.h"
91 #include "asterisk/config.h"
92 #include "asterisk/module.h"
93 #include "asterisk/logger.h"
94 #include "asterisk/cli.h"
95 #include "asterisk/channel.h"   /* AST_MAX_EXTENSION */
96 #include "asterisk/callerid.h"
97
98 static const char config[] = "extensions.conf";
99 static const char registrar[] = "pbx_config";
100 static char userscontext[AST_MAX_EXTENSION] = "default";
101
102 static int static_config = 0;
103 static int write_protect_config = 1;
104 static int autofallthrough_config = 1;
105 static int clearglobalvars_config = 0;
106 static int extenpatternmatchnew_config = 0;
107 static char *overrideswitch_config = NULL;
108
109 AST_MUTEX_DEFINE_STATIC(save_dialplan_lock);
110
111 AST_MUTEX_DEFINE_STATIC(reload_lock);
112
113 static struct ast_context *local_contexts = NULL;
114 static struct ast_hashtab *local_table = NULL;
115 /*
116  * Prototypes for our completion functions
117  */
118 static char *complete_dialplan_remove_include(struct ast_cli_args *);
119 static char *complete_dialplan_add_include(struct ast_cli_args *);
120 static char *complete_dialplan_remove_ignorepat(struct ast_cli_args *);
121 static char *complete_dialplan_add_ignorepat(struct ast_cli_args *);
122 static char *complete_dialplan_remove_extension(struct ast_cli_args *);
123 static char *complete_dialplan_add_extension(struct ast_cli_args *);
124 static char *complete_dialplan_remove_context(struct ast_cli_args *);
125
126 /*
127  * Implementation of functions provided by this module
128  */
129
130 /*!
131  * * REMOVE context command stuff
132  */
133
134 static char *handle_cli_dialplan_remove_context(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
135 {
136         switch (cmd) {
137         case CLI_INIT:
138                 e->command = "dialplan remove context";
139                 e->usage =
140                         "Usage: dialplan remove context <context>\n"
141                         "       Removes all extensions from a specified context.\n";
142                 return NULL;
143         case CLI_GENERATE:
144                 return complete_dialplan_remove_context(a);
145         }
146
147         if (a->argc != 4) {
148                 return CLI_SHOWUSAGE;
149         }
150
151         if (ast_context_destroy_by_name(a->argv[3], NULL)) {
152                 ast_cli(a->fd, "There is no such context as '%s'\n", a->argv[3]);
153                 return CLI_SUCCESS;
154         } else {
155                 ast_cli(a->fd, "Removed context '%s'\n", a->argv[3]);
156                 return CLI_SUCCESS;
157         }
158 }
159 /*!
160  * REMOVE INCLUDE command stuff
161  */
162 static char *handle_cli_dialplan_remove_include(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
163 {
164         switch (cmd) {
165         case CLI_INIT:
166                 e->command = "dialplan remove include";
167                 e->usage =
168                         "Usage: dialplan remove include <context> from <context>\n"
169                         "       Remove an included context from another context.\n";
170                 return NULL;
171         case CLI_GENERATE:
172                 return complete_dialplan_remove_include(a);
173         }
174
175         if (a->argc != 6 || strcmp(a->argv[4], "from"))
176                 return CLI_SHOWUSAGE;
177
178         if (!ast_context_remove_include(a->argv[5], a->argv[3], registrar)) {
179                 ast_cli(a->fd, "We are not including '%s' into '%s' now\n",
180                         a->argv[3], a->argv[5]);
181                 return CLI_SUCCESS;
182         }
183
184         ast_cli(a->fd, "Failed to remove '%s' include from '%s' context\n",
185                 a->argv[3], a->argv[5]);
186         return CLI_FAILURE;
187 }
188
189 /*! \brief return true if 'name' is included by context c */
190 static int lookup_ci(struct ast_context *c, const char *name)
191 {
192         int idx;
193         int ret = 0;
194
195         if (ast_rdlock_context(c)) {
196                 /* error, skip */
197                 return 0;
198         }
199
200         for (idx = 0; idx < ast_context_includes_count(c); idx++) {
201                 const struct ast_include *i = ast_context_includes_get(c, idx);
202
203                 if (!strcmp(name, ast_get_include_name(i))) {
204                         ret = -1;
205                         break;
206                 }
207         }
208         ast_unlock_context(c);
209
210         return ret;
211 }
212
213 /*! \brief return true if 'name' is in the ignorepats for context c */
214 static int lookup_c_ip(struct ast_context *c, const char *name)
215 {
216         int idx;
217         int ret = 0;
218
219         if (ast_rdlock_context(c)) {
220                 /* error, skip */
221                 return 0;
222         }
223
224         for (idx = 0; idx < ast_context_ignorepats_count(c); idx++) {
225                 const struct ast_ignorepat *ip = ast_context_ignorepats_get(c, idx);
226
227                 if (!strcmp(name, ast_get_ignorepat_name(ip))) {
228                         ret = -1;
229                         break;
230                 }
231         }
232         ast_unlock_context(c);
233
234         return ret;
235 }
236
237 /*! \brief moves to the n-th word in the string, or empty string if none */
238 static const char *skip_words(const char *p, int n)
239 {
240         int in_blank = 0;
241         for (;n && *p; p++) {
242                 if (isblank(*p) /* XXX order is important */ && !in_blank) {
243                         n--;    /* one word is gone */
244                         in_blank = 1;
245                 } else if (/* !is_blank(*p), we know already, && */ in_blank) {
246                         in_blank = 0;
247                 }
248         }
249         return p;
250 }
251
252 /*! \brief match the first 'len' chars of word. len==0 always succeeds */
253 static int partial_match(const char *s, const char *word, int len)
254 {
255         return (len == 0 || !strncmp(s, word, len));
256 }
257
258 /*! \brief split extension\@context in two parts, return -1 on error.
259  * The return string is malloc'ed and pointed by *ext
260  */
261 static int split_ec(const char *src, char **ext, char ** const ctx, char ** const cid)
262 {
263         char *i, *c, *e = ast_strdup(src); /* now src is not used anymore */
264
265         if (e == NULL)
266                 return -1;      /* malloc error */
267         /* now, parse values from 'exten@context' */
268         *ext = e;
269         c = strchr(e, '@');
270         if (c == NULL)  /* no context part */
271                 *ctx = "";      /* it is not overwritten, anyways */
272         else {  /* found context, check for duplicity ... */
273                 *c++ = '\0';
274                 *ctx = c;
275                 if (strchr(c, '@')) { /* two @, not allowed */
276                         ast_free(e);
277                         return -1;
278                 }
279         }
280         if (cid && (i = strchr(e, '/'))) {
281                 *i++ = '\0';
282                 *cid = i;
283         } else if (cid) {
284                 /* Signal none detected */
285                 *cid = NULL;
286         }
287         return 0;
288 }
289
290 /* _X_ is the string we need to complete */
291 static char *complete_dialplan_remove_include(struct ast_cli_args *a)
292 {
293         int which = 0;
294         char *res = NULL;
295         int len = strlen(a->word); /* how many bytes to match */
296         struct ast_context *c = NULL;
297
298         if (a->pos == 3) {              /* "dialplan remove include _X_" */
299                 if (ast_wrlock_contexts()) {
300                         ast_log(LOG_ERROR, "Failed to lock context list\n");
301                         return NULL;
302                 }
303                 /* walk contexts and their includes, return the n-th match */
304                 while (!res && (c = ast_walk_contexts(c))) {
305                         int idx;
306
307                         if (ast_rdlock_context(c))      /* error ? skip this one */
308                                 continue;
309
310                         for (idx = 0; idx < ast_context_includes_count(c); idx++) {
311                                 const struct ast_include *i = ast_context_includes_get(c, idx);
312                                 const char *i_name = ast_get_include_name(i);
313                                 struct ast_context *nc = NULL;
314                                 int already_served = 0;
315
316                                 if (!partial_match(i_name, a->word, len))
317                                         continue;       /* not matched */
318
319                                 /* check if this include is already served or not */
320
321                                 /* go through all contexts again till we reach actual
322                                  * context or already_served = 1
323                                  */
324                                 while ( (nc = ast_walk_contexts(nc)) && nc != c && !already_served)
325                                         already_served = lookup_ci(nc, i_name);
326
327                                 if (!already_served && ++which > a->n)
328                                         res = ast_strdup(i_name);
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                 struct ast_sw *sw;
932
933                 /* try to lock context and fireout all info */  
934                 if (ast_rdlock_context(c)) { /* lock failure */
935                         incomplete = 1;
936                         continue;
937                 }
938                 /* registered by this module? */
939                 /* XXX do we need this ? */
940                 if (!strcmp(ast_get_context_registrar(c), registrar)) {
941                         fprintf(output, "[%s]\n", ast_get_context_name(c));
942                         context_header_written = 1;
943                 }
944
945                 /* walk extensions ... */
946                 for (ext = NULL; (ext = ast_walk_context_extensions(c, ext)); ) {
947                         struct ast_exten *p = NULL;
948
949                         /* fireout priorities */
950                         while ( (p = ast_walk_extension_priorities(ext, p)) ) {
951                                 if (strcmp(ast_get_extension_registrar(p), registrar) != 0) /* not this source */
952                                         continue;
953                 
954                                 /* make empty line between different extensions */      
955                                 if (last_written_e != NULL &&
956                                             strcmp(ast_get_extension_name(last_written_e),
957                                                     ast_get_extension_name(p)))
958                                         fprintf(output, "\n");
959                                 last_written_e = p;
960                         
961                                 PUT_CTX_HDR;
962
963                                 if (ast_get_extension_priority(p) == PRIORITY_HINT) { /* easy */
964                                         fprintf(output, "exten => %s,hint,%s\n",
965                                                     ast_get_extension_name(p),
966                                                     ast_get_extension_app(p));
967                                 } else {
968                                         const char *sep, *cid;
969                                         const char *el = ast_get_extension_label(p);
970                                         char label[128] = "";
971                                         char *appdata = ast_get_extension_app_data(p);
972
973                                         int escaped_len = (!ast_strlen_zero(appdata)) ? 2 * strlen(appdata) + 1 : 1;
974                                         char escaped[escaped_len];
975
976                                         if (ast_get_extension_matchcid(p)) {
977                                                 sep = "/";
978                                                 cid = ast_get_extension_cidmatch(p);
979                                         } else {
980                                                 sep = cid = "";
981                                         }
982
983                                         if (el && (snprintf(label, sizeof(label), "(%s)", el) != (strlen(el) + 2))) {
984                                                 incomplete = 1; /* error encountered or label > 125 chars */
985                                         }
986
987                                         if (!ast_strlen_zero(appdata)) {
988                                                 ast_escape_semicolons(appdata, escaped, escaped_len);
989                                         } else {
990                                                 escaped[0] = '\0';
991                                         }
992
993                                         fprintf(output, "exten => %s%s%s,%d%s,%s(%s)\n",
994                                             ast_get_extension_name(p), (ast_strlen_zero(sep) ? "" : sep), (ast_strlen_zero(cid) ? "" : cid),
995                                             ast_get_extension_priority(p), label,
996                                             ast_get_extension_app(p), escaped);
997                                 }
998                         }
999                 }
1000
1001                 /* written any extensions? ok, write space between exten & inc */
1002                 if (last_written_e)
1003                         fprintf(output, "\n");
1004
1005                 /* walk through includes */
1006                 for (idx = 0; idx < ast_context_includes_count(c); idx++) {
1007                         const struct ast_include *i = ast_context_includes_get(c, idx);
1008
1009                         if (strcmp(ast_get_include_registrar(i), registrar) != 0)
1010                                 continue; /* not mine */
1011                         PUT_CTX_HDR;
1012                         fprintf(output, "include => %s\n", ast_get_include_name(i));
1013                 }
1014                 if (ast_context_includes_count(c)) {
1015                         fprintf(output, "\n");
1016                 }
1017
1018                 /* walk through switches */
1019                 for (sw = NULL; (sw = ast_walk_context_switches(c, sw)) ; ) {
1020                         if (strcmp(ast_get_switch_registrar(sw), registrar) != 0)
1021                                 continue; /* not mine */
1022                         PUT_CTX_HDR;
1023                         fprintf(output, "switch => %s/%s\n",
1024                                     ast_get_switch_name(sw), ast_get_switch_data(sw));
1025                 }
1026
1027                 if (ast_walk_context_switches(c, NULL))
1028                         fprintf(output, "\n");
1029
1030                 /* fireout ignorepats ... */
1031                 for (idx = 0; idx < ast_context_ignorepats_count(c); idx++) {
1032                         const struct ast_ignorepat *ip = ast_context_ignorepats_get(c, idx);
1033
1034                         if (strcmp(ast_get_ignorepat_registrar(ip), registrar) != 0)
1035                                 continue; /* not mine */
1036                         PUT_CTX_HDR;
1037                         fprintf(output, "ignorepat => %s\n",
1038                                                 ast_get_ignorepat_name(ip));
1039                 }
1040
1041                 ast_unlock_context(c);
1042         }       
1043
1044         ast_unlock_contexts();
1045         ast_mutex_unlock(&save_dialplan_lock);
1046         fclose(output);
1047
1048         if (incomplete) {
1049                 ast_cli(a->fd, "Saved dialplan is incomplete\n");
1050                 return CLI_FAILURE;
1051         }
1052
1053         ast_cli(a->fd, "Dialplan successfully saved into '%s'\n",
1054                 filename);
1055         return CLI_SUCCESS;
1056 }
1057
1058 /*!
1059  * \brief ADD EXTENSION command stuff
1060  */
1061 static char *handle_cli_dialplan_add_extension(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1062 {
1063         char *whole_exten;
1064         char *exten, *prior;
1065         int iprior = -2;
1066         char *cidmatch, *app, *app_data;
1067         char *start, *end;
1068         const char *into_context;
1069
1070         switch (cmd) {
1071         case CLI_INIT:
1072                 e->command = "dialplan add extension";
1073                 e->usage =
1074                         "Usage: dialplan add extension <exten>,<priority>,<app> into <context> [replace]\n"
1075                         "\n"
1076                         "       app can be either:\n"
1077                         "         app-name\n"
1078                         "         app-name(app-data)\n"
1079                         "         app-name,<app-data>\n"
1080                         "\n"
1081                         "       This command will add the new extension into <context>.  If\n"
1082                         "       an extension with the same priority already exists and the\n"
1083                         "       'replace' option is given we will replace the extension.\n"
1084                         "\n"
1085                         "Example: dialplan add extension 6123,1,Dial,IAX/216.207.245.56/6123 into local\n"
1086                         "         Now, you can dial 6123 and talk to Markster :)\n";
1087                 return NULL;
1088         case CLI_GENERATE:
1089                 return complete_dialplan_add_extension(a);
1090         }
1091
1092         /* check for arguments at first */
1093         if (a->argc != 6 && a->argc != 7)
1094                 return CLI_SHOWUSAGE;
1095         if (strcmp(a->argv[4], "into"))
1096                 return CLI_SHOWUSAGE;
1097         if (a->argc == 7)
1098                 if (strcmp(a->argv[6], "replace"))
1099                         return CLI_SHOWUSAGE;
1100
1101         whole_exten = ast_strdupa(a->argv[3]);
1102         exten = strsep(&whole_exten,",");
1103         if (strchr(exten, '/')) {
1104                 cidmatch = exten;
1105                 strsep(&cidmatch,"/");
1106         } else {
1107                 cidmatch = NULL;
1108         }
1109         prior = strsep(&whole_exten,",");
1110         if (prior) {
1111                 if (!strcmp(prior, "hint")) {
1112                         iprior = PRIORITY_HINT;
1113                 } else {
1114                         if (sscanf(prior, "%30d", &iprior) != 1) {
1115                                 ast_cli(a->fd, "'%s' is not a valid priority\n", prior);
1116                                 prior = NULL;
1117                         }
1118                 }
1119         }
1120         app = whole_exten;
1121         if (app) {
1122                 if ((start = strchr(app, '(')) && (end = strrchr(app, ')'))) {
1123                         *start = *end = '\0';
1124                         app_data = start + 1;
1125                 } else {
1126                         app_data = strchr(app, ',');
1127                         if (app_data) {
1128                                 *app_data++ = '\0';
1129                         }
1130                 }
1131         } else {
1132                 app_data = NULL;
1133         }
1134
1135         if (!exten || !prior || !app) {
1136                 return CLI_SHOWUSAGE;
1137         }
1138
1139         if (!app_data) {
1140                 app_data = "";
1141         }
1142         into_context = a->argv[5];
1143
1144         if (!ast_context_find(into_context)) {
1145                 ast_cli(a->fd, "Context '%s' did not exist prior to add extension - the context will be created.\n", into_context);
1146         }
1147
1148         if (!ast_context_find_or_create(NULL, NULL, into_context, registrar)) {
1149                 ast_cli(a->fd, "Failed to add '%s,%s,%s(%s)' extension into '%s' context\n",
1150                         exten, prior, app, app_data, into_context);
1151                 return CLI_FAILURE;
1152         }
1153
1154         if (ast_add_extension(into_context, a->argc == 7 ? 1 : 0, exten, iprior, NULL, cidmatch, app,
1155                 ast_strdup(app_data), ast_free_ptr, registrar)) {
1156                 switch (errno) {
1157                 case ENOMEM:
1158                         ast_cli(a->fd, "Out of free memory\n");
1159                         break;
1160
1161                 case EBUSY:
1162                         ast_cli(a->fd, "Failed to lock context(s) list, please try again later\n");
1163                         break;
1164
1165                 case ENOENT:
1166                         ast_cli(a->fd, "No existence of '%s' context\n", into_context);
1167                         break;
1168
1169                 case EEXIST:
1170                         ast_cli(a->fd, "Extension %s@%s with priority %s already exists\n",
1171                                 exten, into_context, prior);
1172                         break;
1173
1174                 default:
1175                         ast_cli(a->fd, "Failed to add '%s,%s,%s(%s)' extension into '%s' context\n",
1176                                         exten, prior, app, app_data, into_context);
1177                         break;
1178                 }
1179                 return CLI_FAILURE;
1180         }
1181
1182         if (a->argc == 7) {
1183                 ast_cli(a->fd, "Extension %s@%s (%s) replace by '%s,%s,%s(%s)'\n",
1184                         exten, into_context, prior, exten, prior, app, app_data);
1185         } else {
1186                 ast_cli(a->fd, "Extension '%s,%s,%s(%s)' added into '%s' context\n",
1187                         exten, prior, app, app_data, into_context);
1188         }
1189
1190         return CLI_SUCCESS;
1191 }
1192
1193 static int manager_dialplan_extension_add(struct mansession *s, const struct message *m)
1194 {
1195         const char *context = astman_get_header(m, "Context");
1196         const char *extension = astman_get_header(m, "Extension");
1197         const char *priority = astman_get_header(m, "Priority");
1198         const char *application = astman_get_header(m, "Application");
1199         const char *application_data = astman_get_header(m, "ApplicationData");
1200         int replace = ast_true(astman_get_header(m, "Replace"));
1201         int ipriority;
1202         char *exten;
1203         char *cidmatch = NULL;
1204         struct ast_context *add_context;
1205
1206         if (ast_strlen_zero(context) || ast_strlen_zero(extension) ||
1207                         ast_strlen_zero(priority) || ast_strlen_zero(application)) {
1208                 astman_send_error(s, m, "Context, Extension, Priority, and "
1209                         "Application must be defined for DialplanExtensionAdd.");
1210                 return 0;
1211         }
1212
1213         /* Priority conversion/validation */
1214         if (!strcmp(priority, "hint")) {
1215                 ipriority = PRIORITY_HINT;
1216         } else if ((sscanf(priority, "%30d", &ipriority) != 1) || (ipriority < 0)) {
1217                 astman_send_error(s, m, "The priority specified was invalid.");
1218                 return 0;
1219         }
1220
1221         /* Split extension from cidmatch */
1222         exten = ast_strdupa(extension);
1223
1224         if (strchr(exten, '/')) {
1225                 cidmatch = exten;
1226                 strsep(&cidmatch, "/");
1227         }
1228
1229         if (ast_wrlock_contexts()) {
1230                 astman_send_error(s, m, "Failed to lock contexts list. Try again later.");
1231                 return 0;
1232         }
1233
1234         add_context = ast_context_find_or_create(NULL, NULL, context, registrar);
1235         if (!add_context) {
1236                 astman_send_error(s, m, "Could not find or create context specified "
1237                         "for the extension.");
1238                 ast_unlock_contexts();
1239                 return 0;
1240         }
1241
1242         if (ast_add_extension2(add_context, replace, exten, ipriority, NULL, cidmatch,
1243                         application, ast_strdup(application_data), ast_free_ptr, registrar)) {
1244                 ast_unlock_contexts();
1245                 switch (errno) {
1246                 case ENOMEM:
1247                         astman_send_error(s, m, "Out of Memory");
1248                         break;
1249
1250                 case EBUSY:
1251                         astman_send_error(s, m, "Failed to lock context(s) list");
1252                         break;
1253
1254                 case ENOENT:
1255                         astman_send_error(s, m, "Context does not exist");
1256                         break;
1257
1258                 case EEXIST:
1259                         astman_send_error(s, m, "That extension and priority already exist at that context");
1260                         break;
1261
1262                 default:
1263                         astman_send_error(s, m, "Failed to add extension");
1264                         break;
1265                 }
1266                 return 0;
1267         }
1268         ast_unlock_contexts();
1269
1270         astman_send_ack(s, m, "Added requested extension");
1271
1272         return 0;
1273 }
1274
1275 static char *complete_dialplan_remove_context(struct ast_cli_args *a)
1276 {
1277         struct ast_context *c = NULL;
1278         int len = strlen(a->word);
1279         char *res = NULL;
1280         int which = 0;
1281
1282         if (a->pos != 3) {
1283                 return NULL;
1284         }
1285
1286
1287         /* try to lock contexts list ... */
1288         if (ast_rdlock_contexts()) {
1289                 ast_log(LOG_WARNING, "Failed to lock contexts list\n");
1290                 return NULL;
1291         }
1292
1293         /* walk through all contexts */
1294         while ( !res && (c = ast_walk_contexts(c)) ) {
1295                 if (partial_match(ast_get_context_name(c), a->word, len) && ++which > a->n) {
1296                         res = ast_strdup(ast_get_context_name(c));
1297                 }
1298         }
1299         ast_unlock_contexts();
1300         return res;
1301 }
1302
1303 /*! dialplan add extension 6123,1,Dial,IAX/212.71.138.13/6123 into local */
1304 static char *complete_dialplan_add_extension(struct ast_cli_args *a)
1305 {
1306         int which = 0;
1307
1308         if (a->pos == 4) {              /* complete 'into' word ... */
1309                 return (a->n == 0) ? ast_strdup("into") : NULL;
1310         } else if (a->pos == 5) { /* complete context */
1311                 struct ast_context *c = NULL;
1312                 int len = strlen(a->word);
1313                 char *res = NULL;
1314
1315                 /* try to lock contexts list ... */
1316                 if (ast_rdlock_contexts()) {
1317                         ast_log(LOG_WARNING, "Failed to lock contexts list\n");
1318                         return NULL;
1319                 }
1320
1321                 /* walk through all contexts */
1322                 while ( !res && (c = ast_walk_contexts(c)) )
1323                         if (partial_match(ast_get_context_name(c), a->word, len) && ++which > a->n)
1324                                 res = ast_strdup(ast_get_context_name(c));
1325                 ast_unlock_contexts();
1326                 return res;
1327         } else if (a->pos == 6) {
1328                 return a->n == 0 ? ast_strdup("replace") : NULL;
1329         }
1330         return NULL;
1331 }
1332
1333 /*!
1334  * IGNOREPAT CLI stuff
1335  */
1336 static char *handle_cli_dialplan_add_ignorepat(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1337 {
1338         switch (cmd) {
1339         case CLI_INIT:
1340                 e->command = "dialplan add ignorepat";
1341                 e->usage =
1342                         "Usage: dialplan add ignorepat <pattern> into <context>\n"
1343                         "       This command adds a new ignore pattern into context <context>\n"
1344                         "\n"
1345                         "Example: dialplan add ignorepat _3XX into local\n";
1346                 return NULL;
1347         case CLI_GENERATE:
1348                 return complete_dialplan_add_ignorepat(a);
1349         }
1350
1351         if (a->argc != 6)
1352                 return CLI_SHOWUSAGE;
1353
1354         if (strcmp(a->argv[4], "into"))
1355                 return CLI_SHOWUSAGE;
1356
1357         if (ast_context_add_ignorepat(a->argv[5], a->argv[3], registrar)) {
1358                 switch (errno) {
1359                 case ENOMEM:
1360                         ast_cli(a->fd, "Out of free memory\n");
1361                         break;
1362
1363                 case ENOENT:
1364                         ast_cli(a->fd, "There is no existence of '%s' context\n", a->argv[5]);
1365                         break;
1366
1367                 case EEXIST:
1368                         ast_cli(a->fd, "Ignore pattern '%s' already included in '%s' context\n",
1369                                 a->argv[3], a->argv[5]);
1370                         break;
1371
1372                 case EBUSY:
1373                         ast_cli(a->fd, "Failed to lock context(s) list, please, try again later\n");
1374                         break;
1375
1376                 default:
1377                         ast_cli(a->fd, "Failed to add ingore pattern '%s' into '%s' context\n",
1378                                 a->argv[3], a->argv[5]);
1379                         break;
1380                 }
1381                 return CLI_FAILURE;
1382         }
1383
1384         ast_cli(a->fd, "Ignore pattern '%s' added into '%s' context\n",
1385                 a->argv[3], a->argv[5]);
1386
1387         return CLI_SUCCESS;
1388 }
1389
1390 static char *complete_dialplan_add_ignorepat(struct ast_cli_args *a)
1391 {
1392         if (a->pos == 4)
1393                 return a->n == 0 ? ast_strdup("into") : NULL;
1394         else if (a->pos == 5) {
1395                 struct ast_context *c;
1396                 int which = 0;
1397                 char *dupline, *ignorepat = NULL;
1398                 const char *s;
1399                 char *ret = NULL;
1400                 int len = strlen(a->word);
1401
1402                 /* XXX skip first three words 'dialplan' 'add' 'ignorepat' */
1403                 s = skip_words(a->line, 3);
1404                 if (s == NULL)
1405                         return NULL;
1406                 dupline = ast_strdup(s);
1407                 if (!dupline) {
1408                         ast_log(LOG_ERROR, "Malloc failure\n");
1409                         return NULL;
1410                 }
1411                 ignorepat = strsep(&dupline, " ");
1412
1413                 if (ast_rdlock_contexts()) {
1414                         ast_log(LOG_ERROR, "Failed to lock contexts list\n");
1415                         return NULL;
1416                 }
1417
1418                 for (c = NULL; !ret && (c = ast_walk_contexts(c));) {
1419                         int found = 0;
1420
1421                         if (!partial_match(ast_get_context_name(c), a->word, len))
1422                                 continue; /* not mine */
1423                         if (ignorepat) /* there must be one, right ? */
1424                                 found = lookup_c_ip(c, ignorepat);
1425                         if (!found && ++which > a->n)
1426                                 ret = ast_strdup(ast_get_context_name(c));
1427                 }
1428
1429                 ast_free(ignorepat);
1430                 ast_unlock_contexts();
1431                 return ret;
1432         }
1433
1434         return NULL;
1435 }
1436
1437 static char *handle_cli_dialplan_remove_ignorepat(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1438 {
1439         switch (cmd) {
1440         case CLI_INIT:
1441                 e->command = "dialplan remove ignorepat";
1442                 e->usage =
1443                         "Usage: dialplan remove ignorepat <pattern> from <context>\n"
1444                         "       This command removes an ignore pattern from context <context>\n"
1445                         "\n"
1446                         "Example: dialplan remove ignorepat _3XX from local\n";
1447                 return NULL;
1448         case CLI_GENERATE:
1449                 return complete_dialplan_remove_ignorepat(a);
1450         }
1451
1452         if (a->argc != 6)
1453                 return CLI_SHOWUSAGE;
1454
1455         if (strcmp(a->argv[4], "from"))
1456                 return CLI_SHOWUSAGE;
1457
1458         if (ast_context_remove_ignorepat(a->argv[5], a->argv[3], registrar)) {
1459                 switch (errno) {
1460                 case EBUSY:
1461                         ast_cli(a->fd, "Failed to lock context(s) list, please try again later\n");
1462                         break;
1463
1464                 case ENOENT:
1465                         ast_cli(a->fd, "There is no existence of '%s' context\n", a->argv[5]);
1466                         break;
1467
1468                 case EINVAL:
1469                         ast_cli(a->fd, "There is no existence of '%s' ignore pattern in '%s' context\n",
1470                                         a->argv[3], a->argv[5]);
1471                         break;
1472
1473                 default:
1474                         ast_cli(a->fd, "Failed to remove ignore pattern '%s' from '%s' context\n",
1475                                         a->argv[3], a->argv[5]);
1476                         break;
1477                 }
1478                 return CLI_FAILURE;
1479         }
1480
1481         ast_cli(a->fd, "Ignore pattern '%s' removed from '%s' context\n",
1482                 a->argv[3], a->argv[5]);
1483         return CLI_SUCCESS;
1484 }
1485
1486 static char *complete_dialplan_remove_ignorepat(struct ast_cli_args *a)
1487 {
1488         struct ast_context *c;
1489         int which = 0;
1490         char *ret = NULL;
1491
1492         if (a->pos == 3) {
1493                 int len = strlen(a->word);
1494                 if (ast_rdlock_contexts()) {
1495                         ast_log(LOG_WARNING, "Failed to lock contexts list\n");
1496                         return NULL;
1497                 }
1498
1499                 for (c = NULL; !ret && (c = ast_walk_contexts(c));) {
1500                         int idx;
1501
1502                         if (ast_rdlock_context(c))      /* error, skip it */
1503                                 continue;
1504                         for (idx = 0; idx < ast_context_ignorepats_count(c); idx++) {
1505                                 const struct ast_ignorepat *ip = ast_context_ignorepats_get(c, idx);
1506
1507                                 if (partial_match(ast_get_ignorepat_name(ip), a->word, len) && ++which > a->n) {
1508                                         /* n-th match */
1509                                         struct ast_context *cw = NULL;
1510                                         int found = 0;
1511                                         while ( (cw = ast_walk_contexts(cw)) && cw != c && !found) {
1512                                                 /* XXX do i stop on c, or skip it ? */
1513                                                 found = lookup_c_ip(cw, ast_get_ignorepat_name(ip));
1514                                         }
1515                                         if (!found)
1516                                                 ret = ast_strdup(ast_get_ignorepat_name(ip));
1517                                 }
1518                         }
1519                         ast_unlock_context(c);
1520                 }
1521                 ast_unlock_contexts();
1522                 return ret;
1523         } else if (a->pos == 4) {
1524                  return a->n == 0 ? ast_strdup("from") : NULL;
1525         } else if (a->pos == 5) { /* XXX check this */
1526                 char *dupline, *duplinet, *ignorepat;
1527                 int len = strlen(a->word);
1528
1529                 dupline = ast_strdup(a->line);
1530                 if (!dupline) {
1531                         ast_log(LOG_WARNING, "Out of free memory\n");
1532                         return NULL;
1533                 }
1534
1535                 duplinet = dupline;
1536                 strsep(&duplinet, " ");
1537                 strsep(&duplinet, " ");
1538                 ignorepat = strsep(&duplinet, " ");
1539
1540                 if (!ignorepat) {
1541                         ast_free(dupline);
1542                         return NULL;
1543                 }
1544
1545                 if (ast_rdlock_contexts()) {
1546                         ast_log(LOG_WARNING, "Failed to lock contexts list\n");
1547                         ast_free(dupline);
1548                         return NULL;
1549                 }
1550
1551                 for (c = NULL; !ret && (c = ast_walk_contexts(c)); ) {
1552                         if (ast_rdlock_context(c))      /* fail, skip it */
1553                                 continue;
1554                         if (!partial_match(ast_get_context_name(c), a->word, len))
1555                                 continue;
1556                         if (lookup_c_ip(c, ignorepat) && ++which > a->n)
1557                                 ret = ast_strdup(ast_get_context_name(c));
1558                         ast_unlock_context(c);
1559                 }
1560                 ast_unlock_contexts();
1561                 ast_free(dupline);
1562                 return NULL;
1563         }
1564
1565         return NULL;
1566 }
1567
1568 static int pbx_load_module(void);
1569
1570 static char *handle_cli_dialplan_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1571 {
1572         switch (cmd) {
1573         case CLI_INIT:
1574                 e->command = "dialplan reload";
1575                 e->usage =
1576                         "Usage: dialplan reload\n"
1577                         "       Reload extensions.conf without reloading any other\n"
1578                         "       modules.  This command does not delete global variables\n"
1579                         "       unless clearglobalvars is set to yes in extensions.conf\n";
1580                 return NULL;
1581         case CLI_GENERATE:
1582                 return NULL;
1583         }
1584
1585         if (a->argc != 2)
1586                 return CLI_SHOWUSAGE;
1587
1588         if (clearglobalvars_config)
1589                 pbx_builtin_clear_globals();
1590
1591         pbx_load_module();
1592         ast_cli(a->fd, "Dialplan reloaded.\n");
1593         return CLI_SUCCESS;
1594 }
1595
1596 /*!
1597  * CLI entries for commands provided by this module
1598  */
1599 static struct ast_cli_entry cli_pbx_config[] = {
1600         AST_CLI_DEFINE(handle_cli_dialplan_add_extension,    "Add new extension into context"),
1601         AST_CLI_DEFINE(handle_cli_dialplan_remove_extension, "Remove a specified extension"),
1602         AST_CLI_DEFINE(handle_cli_dialplan_remove_context,   "Remove a specified context"),
1603         AST_CLI_DEFINE(handle_cli_dialplan_add_ignorepat,    "Add new ignore pattern"),
1604         AST_CLI_DEFINE(handle_cli_dialplan_remove_ignorepat, "Remove ignore pattern from context"),
1605         AST_CLI_DEFINE(handle_cli_dialplan_add_include,      "Include context in other context"),
1606         AST_CLI_DEFINE(handle_cli_dialplan_remove_include,   "Remove a specified include from context"),
1607         AST_CLI_DEFINE(handle_cli_dialplan_reload,           "Reload extensions and *only* extensions"),
1608         AST_CLI_DEFINE(handle_cli_dialplan_save,             "Save current dialplan into a file")
1609 };
1610
1611 static struct ast_cli_entry cli_dialplan_save =
1612         AST_CLI_DEFINE(handle_cli_dialplan_save, "Save dialplan");
1613
1614 #define AMI_EXTENSION_ADD "DialplanExtensionAdd"
1615 #define AMI_EXTENSION_REMOVE "DialplanExtensionRemove"
1616
1617 /*!
1618  * Standard module functions ...
1619  */
1620 static int unload_module(void)
1621 {
1622         if (static_config && !write_protect_config)
1623                 ast_cli_unregister(&cli_dialplan_save);
1624         if (overrideswitch_config) {
1625                 ast_free(overrideswitch_config);
1626         }
1627         ast_cli_unregister_multiple(cli_pbx_config, ARRAY_LEN(cli_pbx_config));
1628         ast_manager_unregister(AMI_EXTENSION_ADD);
1629         ast_manager_unregister(AMI_EXTENSION_REMOVE);
1630         ast_context_destroy(NULL, registrar);
1631         return 0;
1632 }
1633
1634 /*!\note Protect against misparsing based upon commas in the middle of fields
1635  * like character classes.  We've taken steps to permit pretty much every other
1636  * printable character in a character class, so properly handling a comma at
1637  * this level is a natural extension.  This is almost like the standard
1638  * application parser in app.c, except that it handles square brackets. */
1639 static char *pbx_strsep(char **destructible, const char *delim)
1640 {
1641         int square = 0;
1642         char *res;
1643
1644         if (!destructible || !*destructible) {
1645                 return NULL;
1646         }
1647         res = *destructible;
1648         for (; **destructible; (*destructible)++) {
1649                 if (**destructible == '[' && !strchr(delim, '[')) {
1650                         square++;
1651                 } else if (**destructible == ']' && !strchr(delim, ']')) {
1652                         if (square) {
1653                                 square--;
1654                         }
1655                 } else if (**destructible == '\\' && !strchr(delim, '\\')) {
1656                         (*destructible)++;
1657                 } else if (strchr(delim, **destructible) && !square) {
1658                         **destructible = '\0';
1659                         (*destructible)++;
1660                         break;
1661                 }
1662         }
1663         if (**destructible == '\0') {
1664                 *destructible = NULL;
1665         }
1666         return res;
1667 }
1668
1669 static int pbx_load_config(const char *config_file)
1670 {
1671         struct ast_config *cfg;
1672         char *end;
1673         char *label;
1674 #ifdef LOW_MEMORY
1675         char realvalue[256];
1676 #else
1677         char realvalue[8192];
1678 #endif
1679         int lastpri = -2;
1680         struct ast_context *con;
1681         struct ast_variable *v;
1682         const char *cxt;
1683         const char *aft;
1684         const char *newpm, *ovsw;
1685         struct ast_flags config_flags = { 0 };
1686         char lastextension[256];
1687         cfg = ast_config_load(config_file, config_flags);
1688         if (!cfg || cfg == CONFIG_STATUS_FILEINVALID)
1689                 return 0;
1690
1691         /* Use existing config to populate the PBX table */
1692         static_config = ast_true(ast_variable_retrieve(cfg, "general", "static"));
1693         write_protect_config = ast_true(ast_variable_retrieve(cfg, "general", "writeprotect"));
1694         if ((aft = ast_variable_retrieve(cfg, "general", "autofallthrough")))
1695                 autofallthrough_config = ast_true(aft);
1696         if ((newpm = ast_variable_retrieve(cfg, "general", "extenpatternmatchnew")))
1697                 extenpatternmatchnew_config = ast_true(newpm);
1698         clearglobalvars_config = ast_true(ast_variable_retrieve(cfg, "general", "clearglobalvars"));
1699         if ((ovsw = ast_variable_retrieve(cfg, "general", "overrideswitch"))) {
1700                 if (overrideswitch_config) {
1701                         ast_free(overrideswitch_config);
1702                 }
1703                 if (!ast_strlen_zero(ovsw)) {
1704                         overrideswitch_config = ast_strdup(ovsw);
1705                 } else {
1706                         overrideswitch_config = NULL;
1707                 }
1708         }
1709
1710         ast_copy_string(userscontext, ast_variable_retrieve(cfg, "general", "userscontext") ?: "default", sizeof(userscontext));
1711                                                                     
1712         for (v = ast_variable_browse(cfg, "globals"); v; v = v->next) {
1713                 pbx_substitute_variables_helper(NULL, v->value, realvalue, sizeof(realvalue) - 1);
1714                 pbx_builtin_setvar_helper(NULL, v->name, realvalue);
1715         }
1716         for (cxt = ast_category_browse(cfg, NULL);
1717              cxt;
1718              cxt = ast_category_browse(cfg, cxt)) {
1719                 /* All categories but "general" or "globals" are considered contexts */
1720                 if (!strcasecmp(cxt, "general") || !strcasecmp(cxt, "globals")) {
1721                         continue;
1722                 }
1723                 if (!(con = ast_context_find_or_create(&local_contexts, local_table, cxt, registrar))) {
1724                         continue;
1725                 }
1726
1727                 /* Reset continuation items at the beginning of each context */
1728                 lastextension[0] = '\0';
1729                 lastpri = -2;
1730
1731                 for (v = ast_variable_browse(cfg, cxt); v; v = v->next) {
1732                         char *tc = NULL;
1733                         char realext[256] = "";
1734                         char *stringp, *ext;
1735                         const char *vfile;
1736
1737                         /* get filename for error reporting from top level or an #include */
1738                         vfile = !*v->file ? config_file : v->file;
1739
1740                         if (!strncasecmp(v->name, "same", 4)) {
1741                                 if (ast_strlen_zero(lastextension)) {
1742                                         ast_log(LOG_ERROR,
1743                                                 "No previous pattern in the first entry of context '%s' to match '%s' at line %d of %s!\n",
1744                                                 cxt, v->name, v->lineno, vfile);
1745                                         continue;
1746                                 }
1747                                 if ((stringp = tc = ast_strdup(v->value))) {
1748                                         ast_copy_string(realext, lastextension, sizeof(realext));
1749                                         goto process_extension;
1750                                 }
1751                         } else if (!strcasecmp(v->name, "exten")) {
1752                                 int ipri;
1753                                 char *plus;
1754                                 char *pri, *appl, *data, *cidmatch;
1755
1756                                 if (!(stringp = tc = ast_strdup(v->value))) {
1757                                         continue;
1758                                 }
1759
1760                                 ext = S_OR(pbx_strsep(&stringp, ","), "");
1761                                 pbx_substitute_variables_helper(NULL, ext, realext, sizeof(realext) - 1);
1762                                 ast_copy_string(lastextension, realext, sizeof(lastextension));
1763 process_extension:
1764                                 ipri = -2;
1765                                 if ((cidmatch = strchr(realext, '/'))) {
1766                                         *cidmatch++ = '\0';
1767                                         ast_shrink_phone_number(cidmatch);
1768                                 }
1769                                 pri = ast_strip(S_OR(strsep(&stringp, ","), ""));
1770                                 if ((label = strchr(pri, '('))) {
1771                                         *label++ = '\0';
1772                                         if ((end = strchr(label, ')'))) {
1773                                                 *end = '\0';
1774                                         } else {
1775                                                 ast_log(LOG_WARNING,
1776                                                         "Label missing trailing ')' at line %d of %s\n",
1777                                                         v->lineno, vfile);
1778                                                 ast_free(tc);
1779                                                 continue;
1780                                         }
1781                                 }
1782                                 if ((plus = strchr(pri, '+'))) {
1783                                         *plus++ = '\0';
1784                                 }
1785                                 if (!strcmp(pri,"hint")) {
1786                                         ipri = PRIORITY_HINT;
1787                                 } else if (!strcmp(pri, "next") || !strcmp(pri, "n")) {
1788                                         if (lastpri > -2) {
1789                                                 ipri = lastpri + 1;
1790                                         } else {
1791                                                 ast_log(LOG_WARNING,
1792                                                         "Can't use 'next' priority on the first entry at line %d of %s!\n",
1793                                                         v->lineno, vfile);
1794                                                 ast_free(tc);
1795                                                 continue;
1796                                         }
1797                                 } else if (!strcmp(pri, "same") || !strcmp(pri, "s")) {
1798                                         if (lastpri > -2) {
1799                                                 ipri = lastpri;
1800                                         } else {
1801                                                 ast_log(LOG_WARNING,
1802                                                         "Can't use 'same' priority on the first entry at line %d of %s!\n",
1803                                                         v->lineno, vfile);
1804                                                 ast_free(tc);
1805                                                 continue;
1806                                         }
1807                                 } else if (sscanf(pri, "%30d", &ipri) != 1 &&
1808                                            (ipri = ast_findlabel_extension2(NULL, con, realext, pri, cidmatch)) < 1) {
1809                                         ast_log(LOG_WARNING,
1810                                                 "Invalid priority/label '%s' at line %d of %s\n",
1811                                                 pri, v->lineno, vfile);
1812                                         ipri = 0;
1813                                         ast_free(tc);
1814                                         continue;
1815                                 } else if (ipri < 1) {
1816                                         ast_log(LOG_WARNING, "Invalid priority '%s' at line %d of %s\n",
1817                                                 pri, v->lineno, vfile);
1818                                         ast_free(tc);
1819                                         continue;
1820                                 }
1821                                 appl = S_OR(stringp, "");
1822                                 /* Find the first occurrence of '(' */
1823                                 if (!strchr(appl, '(')) {
1824                                         /* No arguments */
1825                                         data = "";
1826                                 } else {
1827                                         char *orig_appl = ast_strdup(appl);
1828
1829                                         if (!orig_appl) {
1830                                                 ast_free(tc);
1831                                                 continue;
1832                                         }
1833
1834                                         appl = strsep(&stringp, "(");
1835
1836                                         /* check if there are variables or expressions without an application, like: exten => 100,hint,DAHDI/g0/${GLOBAL(var)}  */
1837                                         if (strstr(appl, "${") || strstr(appl, "$[")){
1838                                                 /* set appl to original one */
1839                                                 strcpy(appl, orig_appl);
1840                                                 /* set no data */
1841                                                 data = "";
1842                                         /* no variable before application found -> go ahead */
1843                                         } else {
1844                                                 data = S_OR(stringp, "");
1845                                                 if ((end = strrchr(data, ')'))) {
1846                                                         *end = '\0';
1847                                                 } else {
1848                                                         ast_log(LOG_WARNING,
1849                                                                 "No closing parenthesis found? '%s(%s' at line %d of %s\n",
1850                                                                 appl, data, v->lineno, vfile);
1851                                                 }
1852                                         }
1853                                         ast_free(orig_appl);
1854                                 }
1855
1856                                 appl = ast_skip_blanks(appl);
1857                                 if (ipri) {
1858                                         if (plus) {
1859                                                 ipri += atoi(plus);
1860                                         }
1861                                         lastpri = ipri;
1862                                         if (!ast_opt_dont_warn && (!strcmp(realext, "_.") || !strcmp(realext, "_!"))) {
1863                                                 ast_log(LOG_WARNING,
1864                                                         "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",
1865                                                         realext, realext[1], v->lineno, vfile);
1866                                         }
1867                                         if (ast_add_extension2(con, 0, realext, ipri, label, cidmatch, appl, ast_strdup(data), ast_free_ptr, registrar)) {
1868                                                 ast_log(LOG_WARNING,
1869                                                         "Unable to register extension at line %d of %s\n",
1870                                                         v->lineno, vfile);
1871                                         }
1872                                 }
1873                                 ast_free(tc);
1874                         } else if (!strcasecmp(v->name, "include")) {
1875                                 pbx_substitute_variables_helper(NULL, v->value, realvalue, sizeof(realvalue) - 1);
1876                                 if (ast_context_add_include2(con, realvalue, registrar)) {
1877                                         switch (errno) {
1878                                         case ENOMEM:
1879                                                 ast_log(LOG_WARNING, "Out of memory for context addition\n");
1880                                                 break;
1881
1882                                         case EBUSY:
1883                                                 ast_log(LOG_WARNING, "Failed to lock context(s) list, please try again later\n");
1884                                                 break;
1885
1886                                         case EEXIST:
1887                                                 ast_log(LOG_WARNING,
1888                                                         "Context '%s' already included in '%s' context on include at line %d of %s\n",
1889                                                         v->value, cxt, v->lineno, vfile);
1890                                                 break;
1891
1892                                         case ENOENT:
1893                                         case EINVAL:
1894                                                 ast_log(LOG_WARNING,
1895                                                         "There is no existence of context '%s' included at line %d of %s\n",
1896                                                         errno == ENOENT ? v->value : cxt, v->lineno, vfile);
1897                                                 break;
1898
1899                                         default:
1900                                                 ast_log(LOG_WARNING,
1901                                                         "Failed to include '%s' in '%s' context at line %d of %s\n",
1902                                                         v->value, cxt, v->lineno, vfile);
1903                                                 break;
1904                                         }
1905                                 }
1906                         } else if (!strcasecmp(v->name, "ignorepat")) {
1907                                 pbx_substitute_variables_helper(NULL, v->value, realvalue, sizeof(realvalue) - 1);
1908                                 if (ast_context_add_ignorepat2(con, realvalue, registrar)) {
1909                                         ast_log(LOG_WARNING,
1910                                                 "Unable to include ignorepat '%s' in context '%s' at line %d of %s\n",
1911                                                 v->value, cxt, v->lineno, vfile);
1912                                 }
1913                         } else if (!strcasecmp(v->name, "switch") || !strcasecmp(v->name, "lswitch") || !strcasecmp(v->name, "eswitch")) {
1914                                 char *appl, *data;
1915                                 stringp = realvalue;
1916                                 
1917                                 if (!strcasecmp(v->name, "switch")) {
1918                                         pbx_substitute_variables_helper(NULL, v->value, realvalue, sizeof(realvalue) - 1);
1919                                 } else {
1920                                         ast_copy_string(realvalue, v->value, sizeof(realvalue));
1921                                 }
1922                                 appl = strsep(&stringp, "/");
1923                                 data = S_OR(stringp, "");
1924                                 if (ast_context_add_switch2(con, appl, data, !strcasecmp(v->name, "eswitch"), registrar)) {
1925                                         ast_log(LOG_WARNING,
1926                                                 "Unable to include switch '%s' in context '%s' at line %d of %s\n",
1927                                                 v->value, cxt, v->lineno, vfile);
1928                                 }
1929                         } else if (!strcasecmp(v->name, "autohints")) {
1930                                 ast_context_set_autohints(con, ast_true(v->value));
1931                         } else {
1932                                 ast_log(LOG_WARNING,
1933                                         "==!!== Unknown directive: %s at line %d of %s -- IGNORING!!!\n",
1934                                         v->name, v->lineno, vfile);
1935                         }
1936                 }
1937         }
1938         ast_config_destroy(cfg);
1939         return 1;
1940 }
1941
1942 static void append_interface(char *iface, int maxlen, char *add)
1943 {
1944         int len = strlen(iface);
1945         if (strlen(add) + len < maxlen - 2) {
1946                 if (strlen(iface)) {
1947                         iface[len] = '&';
1948                         strcpy(iface + len + 1, add);
1949                 } else
1950                         strcpy(iface, add);
1951         }
1952 }
1953
1954 static void pbx_load_users(void)
1955 {
1956         struct ast_config *cfg;
1957         char *cat, *chan;
1958         const char *dahdichan;
1959         const char *hasexten, *altexts;
1960         char tmp[256];
1961         char iface[256];
1962         char dahdicopy[256];
1963         char *ext, altcopy[256];
1964         char *c;
1965         int hasvoicemail;
1966         int start, finish, x;
1967         struct ast_context *con = NULL;
1968         struct ast_flags config_flags = { 0 };
1969         
1970         cfg = ast_config_load("users.conf", config_flags);
1971         if (!cfg)
1972                 return;
1973
1974         for (cat = ast_category_browse(cfg, NULL); cat ; cat = ast_category_browse(cfg, cat)) {
1975                 if (!strcasecmp(cat, "general"))
1976                         continue;
1977                 iface[0] = '\0';
1978                 if (ast_true(ast_config_option(cfg, cat, "hassip"))) {
1979                         snprintf(tmp, sizeof(tmp), "SIP/%s", cat);
1980                         append_interface(iface, sizeof(iface), tmp);
1981                 }
1982                 if (ast_true(ast_config_option(cfg, cat, "hasiax"))) {
1983                         snprintf(tmp, sizeof(tmp), "IAX2/%s", cat);
1984                         append_interface(iface, sizeof(iface), tmp);
1985                 }
1986                 if (ast_true(ast_config_option(cfg, cat, "hash323"))) {
1987                         snprintf(tmp, sizeof(tmp), "H323/%s", cat);
1988                         append_interface(iface, sizeof(iface), tmp);
1989                 }
1990                 hasexten = ast_config_option(cfg, cat, "hasexten");
1991                 if (hasexten && !ast_true(hasexten))
1992                         continue;
1993                 hasvoicemail = ast_true(ast_config_option(cfg, cat, "hasvoicemail"));
1994                 dahdichan = ast_variable_retrieve(cfg, cat, "dahdichan");
1995                 if (!dahdichan)
1996                         dahdichan = ast_variable_retrieve(cfg, "general", "dahdichan");
1997                 if (!ast_strlen_zero(dahdichan)) {
1998                         ast_copy_string(dahdicopy, dahdichan, sizeof(dahdicopy));
1999                         c = dahdicopy;
2000                         chan = strsep(&c, ",");
2001                         while (chan) {
2002                                 if (sscanf(chan, "%30d-%30d", &start, &finish) == 2) {
2003                                         /* Range */
2004                                 } else if (sscanf(chan, "%30d", &start)) {
2005                                         /* Just one */
2006                                         finish = start;
2007                                 } else {
2008                                         start = 0; finish = 0;
2009                                 }
2010                                 if (finish < start) {
2011                                         x = finish;
2012                                         finish = start;
2013                                         start = x;
2014                                 }
2015                                 for (x = start; x <= finish; x++) {
2016                                         snprintf(tmp, sizeof(tmp), "DAHDI/%d", x);
2017                                         append_interface(iface, sizeof(iface), tmp);
2018                                 }
2019                                 chan = strsep(&c, ",");
2020                         }
2021                 }
2022                 if (!ast_strlen_zero(iface)) {
2023                         /* Only create a context here when it is really needed. Otherwise default empty context
2024                         created by pbx_config may conflict with the one explicitly created by pbx_ael */
2025                         if (!con)
2026                                 con = ast_context_find_or_create(&local_contexts, local_table, userscontext, registrar);
2027
2028                         if (!con) {
2029                                 ast_log(LOG_ERROR, "Can't find/create user context '%s'\n", userscontext);
2030                                 return;
2031                         }
2032
2033                         /* Add hint */
2034                         ast_add_extension2(con, 0, cat, -1, NULL, NULL, iface, NULL, NULL, registrar);
2035                         /* If voicemail, use "stdexten" else use plain old dial */
2036                         if (hasvoicemail) {
2037                                 if (ast_opt_stdexten_macro) {
2038                                         /* Use legacy stdexten macro method. */
2039                                         snprintf(tmp, sizeof(tmp), "stdexten,%s,${HINT}", cat);
2040                                         ast_add_extension2(con, 0, cat, 1, NULL, NULL, "Macro", ast_strdup(tmp), ast_free_ptr, registrar);
2041                                 } else {
2042                                         snprintf(tmp, sizeof(tmp), "%s,stdexten(${HINT})", cat);
2043                                         ast_add_extension2(con, 0, cat, 1, NULL, NULL, "Gosub", ast_strdup(tmp), ast_free_ptr, registrar);
2044                                 }
2045                         } else {
2046                                 ast_add_extension2(con, 0, cat, 1, NULL, NULL, "Dial", ast_strdup("${HINT}"), ast_free_ptr, registrar);
2047                         }
2048                         altexts = ast_variable_retrieve(cfg, cat, "alternateexts");
2049                         if (!ast_strlen_zero(altexts)) {
2050                                 snprintf(tmp, sizeof(tmp), "%s,1", cat);
2051                                 ast_copy_string(altcopy, altexts, sizeof(altcopy));
2052                                 c = altcopy;
2053                                 ext = strsep(&c, ",");
2054                                 while (ext) {
2055                                         ast_add_extension2(con, 0, ext, 1, NULL, NULL, "Goto", ast_strdup(tmp), ast_free_ptr, registrar);
2056                                         ext = strsep(&c, ",");
2057                                 }
2058                         }
2059                 }
2060         }
2061         ast_config_destroy(cfg);
2062 }
2063
2064 static int pbx_load_module(void)
2065 {
2066         struct ast_context *con;
2067
2068         ast_mutex_lock(&reload_lock);
2069
2070         if (!local_table)
2071                 local_table = ast_hashtab_create(17, ast_hashtab_compare_contexts, ast_hashtab_resize_java, ast_hashtab_newsize_java, ast_hashtab_hash_contexts, 0);
2072
2073         if (!pbx_load_config(config)) {
2074                 ast_mutex_unlock(&reload_lock);
2075                 return AST_MODULE_LOAD_DECLINE;
2076         }
2077         
2078         pbx_load_users();
2079
2080         ast_merge_contexts_and_delete(&local_contexts, local_table, registrar);
2081         local_table = NULL; /* the local table has been moved into the global one. */
2082         local_contexts = NULL;
2083
2084         ast_mutex_unlock(&reload_lock);
2085
2086         for (con = NULL; (con = ast_walk_contexts(con));)
2087                 ast_context_verify_includes(con);
2088
2089         pbx_set_overrideswitch(overrideswitch_config);
2090         pbx_set_autofallthrough(autofallthrough_config);
2091         pbx_set_extenpatternmatchnew(extenpatternmatchnew_config);
2092
2093         return AST_MODULE_LOAD_SUCCESS;
2094 }
2095
2096 static int load_module(void)
2097 {
2098         int res;
2099
2100         if (static_config && !write_protect_config)
2101                 ast_cli_register(&cli_dialplan_save);
2102         ast_cli_register_multiple(cli_pbx_config, ARRAY_LEN(cli_pbx_config));
2103
2104         res = ast_manager_register_xml(AMI_EXTENSION_ADD,
2105                 EVENT_FLAG_SYSTEM, manager_dialplan_extension_add);
2106         res |= ast_manager_register_xml(AMI_EXTENSION_REMOVE,
2107                 EVENT_FLAG_SYSTEM, manager_dialplan_extension_remove);
2108
2109         if (res) {
2110                 unload_module();
2111                 return AST_MODULE_LOAD_DECLINE;
2112         }
2113
2114         if (pbx_load_module())
2115                 return AST_MODULE_LOAD_DECLINE;
2116
2117         return AST_MODULE_LOAD_SUCCESS;
2118 }
2119
2120 static int reload(void)
2121 {
2122         if (clearglobalvars_config)
2123                 pbx_builtin_clear_globals();
2124         return pbx_load_module();
2125 }
2126
2127 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Text Extension Configuration",
2128         .support_level = AST_MODULE_SUPPORT_CORE,
2129         .load = load_module,
2130         .unload = unload_module,
2131         .reload = reload,
2132 );