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