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