merge qwell's CLI verbification work
[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 #include "asterisk.h"
27
28 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
29
30 #include <sys/types.h>
31 #include <stdlib.h>
32 #include <stdio.h>
33 #include <string.h>
34 #include <ctype.h>
35 #include <errno.h>
36
37 #include "asterisk/pbx.h"
38 #include "asterisk/config.h"
39 #include "asterisk/options.h"
40 #include "asterisk/module.h"
41 #include "asterisk/logger.h"
42 #include "asterisk/cli.h"
43 #include "asterisk/callerid.h"
44
45 static char *config = "extensions.conf";
46 static 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 = 0;
52 static int clearglobalvars_config = 0;
53
54 AST_MUTEX_DEFINE_STATIC(save_dialplan_lock);
55
56 static struct ast_context *local_contexts = NULL;
57
58 /*
59  * Help for commands provided by this module ...
60  */
61 static char context_add_extension_help[] =
62 "Usage: dialplan add extension <exten>,<priority>,<app>,<app-data>\n"
63 "       into <context> [replace]\n\n"
64 "       This command will add new extension into <context>. If there is an\n"
65 "       existence of extension with the same priority and last 'replace'\n"
66 "       arguments is given here we simply replace this extension.\n"
67 "\n"
68 "Example: dialplan add extension 6123,1,Dial,IAX/216.207.245.56/6123 into local\n"
69 "         Now, you can dial 6123 and talk to Markster :)\n";
70
71 static char context_remove_extension_help[] =
72 "Usage: dialplan remove extension exten@context [priority]\n"
73 "       Remove an extension from a given context. If a priority\n"
74 "       is given, only that specific priority from the given extension\n"
75 "       will be removed.\n";
76
77 static char context_add_ignorepat_help[] =
78 "Usage: dialplan add ignorepat <pattern> into <context>\n"
79 "       This command adds a new ignore pattern into context <context>\n"
80 "\n"
81 "Example: dialplan add ignorepat _3XX into local\n";
82
83 static char context_remove_ignorepat_help[] =
84 "Usage: dialplan remove ignorepat <pattern> from <context>\n"
85 "       This command removes an ignore pattern from context <context>\n"
86 "\n"
87 "Example: dialplan remove ignorepat _3XX from local\n";
88
89 static char context_add_include_help[] =
90 "Usage: dialplan add include <context> into <context>\n"
91 "       Include a context in another context.\n";
92
93 static char context_remove_include_help[] =
94 "Usage: dialplan remove include <context> from <context>\n"
95 "       Remove an included context from another context.\n";
96
97 static char save_dialplan_help[] =
98 "Usage: dialplan save [/path/to/extension/file]\n"
99 "       Save dialplan created by pbx_config module.\n"
100 "\n"
101 "Example: dialplan save                 (/etc/asterisk/extensions.conf)\n"
102 "         dialplan save /home/markster  (/home/markster/extensions.conf)\n";
103
104 static char reload_extensions_help[] =
105 "Usage: dialplan reload\n"
106 "       reload extensions.conf without reloading any other modules\n"
107 "       This command does not delete global variables unless\n"
108 "       clearglobalvars is set to yes in extensions.conf\n";
109
110 /*
111  * Implementation of functions provided by this module
112  */
113
114 /*!
115  * REMOVE INCLUDE command stuff
116  */
117 static int handle_context_dont_include_deprecated(int fd, int argc, char *argv[])
118 {
119         if (argc != 5)
120                 return RESULT_SHOWUSAGE;
121
122         if (strcmp(argv[3], "into"))
123                 return RESULT_SHOWUSAGE;
124
125         if (!ast_context_remove_include(argv[4], argv[2], registrar)) {
126                 ast_cli(fd, "We are not including '%s' into '%s' now\n",
127                         argv[2], argv[4]);
128                 return RESULT_SUCCESS;
129         }
130
131         ast_cli(fd, "Failed to remove '%s' include from '%s' context\n",
132                 argv[2], argv[4]);
133         return RESULT_FAILURE;
134 }
135
136 static int handle_context_remove_include(int fd, int argc, char *argv[])
137 {
138         if (argc != 6)
139                 return RESULT_SHOWUSAGE;
140
141         if (strcmp(argv[4], "into"))
142                 return RESULT_SHOWUSAGE;
143
144         if (!ast_context_remove_include(argv[5], argv[3], registrar)) {
145                 ast_cli(fd, "We are not including '%s' into '%s' now\n",
146                         argv[3], argv[5]);
147                 return RESULT_SUCCESS;
148         }
149
150         ast_cli(fd, "Failed to remove '%s' include from '%s' context\n",
151                 argv[3], argv[5]);
152         return RESULT_FAILURE;
153 }
154
155 /*! \brief return true if 'name' is included by context c */
156 static int lookup_ci(struct ast_context *c, const char *name)
157 {
158         struct ast_include *i = NULL;
159
160         if (ast_lock_context(c))        /* error, skip */
161                 return 0;
162         while ( (i = ast_walk_context_includes(c, i)) )
163                 if (!strcmp(name, ast_get_include_name(i)))
164                         break;
165         ast_unlock_context(c);
166         return i ? -1 /* success */ : 0;
167 }
168
169 /*! \brief return true if 'name' is in the ignorepats for context c */
170 static int lookup_c_ip(struct ast_context *c, const char *name)
171 {
172         struct ast_ignorepat *ip = NULL;
173
174         if (ast_lock_context(c))        /* error, skip */
175                 return 0;
176         while ( (ip = ast_walk_context_ignorepats(c, ip)) )
177                 if (!strcmp(name, ast_get_ignorepat_name(ip)))
178                         break;
179         ast_unlock_context(c);
180         return ip ? -1 /* success */ : 0;
181 }
182
183 /*! \brief moves to the n-th word in the string, or empty string if none */
184 static const char *skip_words(const char *p, int n)
185 {
186         int in_blank = 0;
187         for (;n && *p; p++) {
188                 if (isblank(*p) /* XXX order is important */ && !in_blank) {
189                         n--;    /* one word is gone */
190                         in_blank = 1;
191                 } else if (/* !is_blank(*p), we know already, && */ in_blank) {
192                         in_blank = 0;
193                 }
194         }
195         return p;
196 }
197
198 /*! \brief match the first 'len' chars of word. len==0 always succeeds */
199 static int partial_match(const char *s, const char *word, int len)
200 {
201         return (len == 0 || !strncmp(s, word, len));
202 }
203
204 /*! \brief split extension\@context in two parts, return -1 on error.
205  * The return string is malloc'ed and pointed by *ext
206  */
207 static int split_ec(const char *src, char **ext, char ** const ctx)
208 {
209         char *c, *e = ast_strdup(src); /* now src is not used anymore */
210
211         if (e == NULL)
212                 return -1;      /* malloc error */
213         /* now, parse values from 'exten@context' */
214         *ext = e;
215         c = strchr(e, '@');
216         if (c == NULL)  /* no context part */
217                 *ctx = "";      /* it is not overwritten, anyways */
218         else {  /* found context, check for duplicity ... */
219                 *c++ = '\0';
220                 *ctx = c;
221                 if (strchr(c, '@')) { /* two @, not allowed */
222                         free(e);
223                         return -1;
224                 }
225         } 
226         return 0;
227 }
228
229 /* _X_ is the string we need to complete */
230 static char *complete_context_dont_include_deprecated(const char *line, const char *word,
231         int pos, int state)
232 {
233         int which = 0;
234         char *res = NULL;
235         int len = strlen(word); /* how many bytes to match */
236         struct ast_context *c = NULL;
237
238         if (pos == 2) {         /* "dont include _X_" */
239                 if (ast_lock_contexts()) {
240                         ast_log(LOG_ERROR, "Failed to lock context list\n");
241                         return NULL;
242                 }
243                 /* walk contexts and their includes, return the n-th match */
244                 while (!res && (c = ast_walk_contexts(c))) {
245                         struct ast_include *i = NULL;
246
247                         if (ast_lock_context(c))        /* error ? skip this one */
248                                 continue;
249
250                         while ( !res && (i = ast_walk_context_includes(c, i)) ) {
251                                 const char *i_name = ast_get_include_name(i);
252                                 struct ast_context *nc = NULL;
253                                 int already_served = 0;
254
255                                 if (!partial_match(i_name, word, len))
256                                         continue;       /* not matched */
257
258                                 /* check if this include is already served or not */
259
260                                 /* go through all contexts again till we reach actual
261                                  * context or already_served = 1
262                                  */
263                                 while ( (nc = ast_walk_contexts(nc)) && nc != c && !already_served)
264                                         already_served = lookup_ci(nc, i_name);
265
266                                 if (!already_served && ++which > state)
267                                         res = strdup(i_name);
268                         }
269                         ast_unlock_context(c);
270                 }
271
272                 ast_unlock_contexts();
273                 return res;
274         } else if (pos == 3) { /* "dont include CTX _X_" */
275                 /*
276                  * complete as 'in', but only if previous context is really
277                  * included somewhere
278                  */
279                 char *context, *dupline;
280                 const char *s = skip_words(line, 2); /* skip 'dont' 'include' */
281
282                 if (state > 0)
283                         return NULL;
284                 context = dupline = strdup(s);
285                 if (!dupline) {
286                         ast_log(LOG_ERROR, "Out of free memory\n");
287                         return NULL;
288                 }
289                 strsep(&dupline, " ");
290
291                 if (ast_lock_contexts()) {
292                         ast_log(LOG_ERROR, "Failed to lock contexts list\n");
293                         free(context);
294                         return NULL;
295                 }
296
297                 /* go through all contexts and check if is included ... */
298                 while (!res && (c = ast_walk_contexts(c)))
299                         if (lookup_ci(c, context)) /* context is really included, complete "in" command */
300                                 res = strdup("in");
301                 ast_unlock_contexts();
302                 if (!res)
303                         ast_log(LOG_WARNING, "%s not included anywhere\n", context);
304                 free(context);
305                 return res;
306         } else if (pos == 4) { /* "dont include CTX in _X_" */
307                 /*
308                  * Context from which we removing include ... 
309                  */
310                 char *context, *dupline, *in;
311                 const char *s = skip_words(line, 2); /* skip 'dont' 'include' */
312                 context = dupline = strdup(s);
313                 if (!dupline) {
314                         ast_log(LOG_ERROR, "Out of free memory\n");
315                         return NULL;
316                 }
317
318                 strsep(&dupline, " "); /* skip context */
319
320                 /* third word must be 'in' */
321                 in = strsep(&dupline, " ");
322                 if (!in || strcmp(in, "in")) {
323                         free(context);
324                         return NULL;
325                 }
326
327                 if (ast_lock_contexts()) {
328                         ast_log(LOG_ERROR, "Failed to lock context list\n");
329                         free(context);
330                         return NULL;
331                 }
332
333                 /* walk through all contexts ... */
334                 c = NULL;
335                 while ( !res && (c = ast_walk_contexts(c))) {
336                         const char *c_name = ast_get_context_name(c);
337                         if (!partial_match(c_name, word, len))  /* not a good target */
338                                 continue;
339                         /* walk through all includes and check if it is our context */  
340                         if (lookup_ci(c, context) && ++which > state)
341                                 res = strdup(c_name);
342                 }
343                 ast_unlock_contexts();
344                 free(context);
345                 return res;
346         }
347
348         return NULL;
349 }
350
351 static char *complete_context_remove_include(const char *line, const char *word,
352         int pos, int state)
353 {
354         int which = 0;
355         char *res = NULL;
356         int len = strlen(word); /* how many bytes to match */
357         struct ast_context *c = NULL;
358
359         if (pos == 3) {         /* "dialplan remove include _X_" */
360                 if (ast_lock_contexts()) {
361                         ast_log(LOG_ERROR, "Failed to lock context list\n");
362                         return NULL;
363                 }
364                 /* walk contexts and their includes, return the n-th match */
365                 while (!res && (c = ast_walk_contexts(c))) {
366                         struct ast_include *i = NULL;
367
368                         if (ast_lock_context(c))        /* error ? skip this one */
369                                 continue;
370
371                         while ( !res && (i = ast_walk_context_includes(c, i)) ) {
372                                 const char *i_name = ast_get_include_name(i);
373                                 struct ast_context *nc = NULL;
374                                 int already_served = 0;
375
376                                 if (!partial_match(i_name, word, len))
377                                         continue;       /* not matched */
378
379                                 /* check if this include is already served or not */
380
381                                 /* go through all contexts again till we reach actual
382                                  * context or already_served = 1
383                                  */
384                                 while ( (nc = ast_walk_contexts(nc)) && nc != c && !already_served)
385                                         already_served = lookup_ci(nc, i_name);
386
387                                 if (!already_served && ++which > state)
388                                         res = strdup(i_name);
389                         }
390                         ast_unlock_context(c);
391                 }
392
393                 ast_unlock_contexts();
394                 return res;
395         } else if (pos == 4) { /* "dialplan remove include CTX _X_" */
396                 /*
397                  * complete as 'from', but only if previous context is really
398                  * included somewhere
399                  */
400                 char *context, *dupline;
401                 const char *s = skip_words(line, 3); /* skip 'dialplan' 'remove' 'include' */
402
403                 if (state > 0)
404                         return NULL;
405                 context = dupline = strdup(s);
406                 if (!dupline) {
407                         ast_log(LOG_ERROR, "Out of free memory\n");
408                         return NULL;
409                 }
410                 strsep(&dupline, " ");
411
412                 if (ast_lock_contexts()) {
413                         ast_log(LOG_ERROR, "Failed to lock contexts list\n");
414                         free(context);
415                         return NULL;
416                 }
417
418                 /* go through all contexts and check if is included ... */
419                 while (!res && (c = ast_walk_contexts(c)))
420                         if (lookup_ci(c, context)) /* context is really included, complete "from" command */
421                                 res = strdup("from");
422                 ast_unlock_contexts();
423                 if (!res)
424                         ast_log(LOG_WARNING, "%s not included anywhere\n", context);
425                 free(context);
426                 return res;
427         } else if (pos == 5) { /* "dialplan remove include CTX from _X_" */
428                 /*
429                  * Context from which we removing include ... 
430                  */
431                 char *context, *dupline, *from;
432                 const char *s = skip_words(line, 3); /* skip 'dialplan' 'remove' 'include' */
433                 context = dupline = strdup(s);
434                 if (!dupline) {
435                         ast_log(LOG_ERROR, "Out of free memory\n");
436                         return NULL;
437                 }
438
439                 strsep(&dupline, " "); /* skip context */
440
441                 /* fourth word must be 'from' */
442                 from = strsep(&dupline, " ");
443                 if (!from || strcmp(from, "from")) {
444                         free(context);
445                         return NULL;
446                 }
447
448                 if (ast_lock_contexts()) {
449                         ast_log(LOG_ERROR, "Failed to lock context list\n");
450                         free(context);
451                         return NULL;
452                 }
453
454                 /* walk through all contexts ... */
455                 c = NULL;
456                 while ( !res && (c = ast_walk_contexts(c))) {
457                         const char *c_name = ast_get_context_name(c);
458                         if (!partial_match(c_name, word, len))  /* not a good target */
459                                 continue;
460                         /* walk through all includes and check if it is our context */  
461                         if (lookup_ci(c, context) && ++which > state)
462                                 res = strdup(c_name);
463                 }
464                 ast_unlock_contexts();
465                 free(context);
466                 return res;
467         }
468
469         return NULL;
470 }
471
472 /*!
473  * REMOVE EXTENSION command stuff
474  */
475 static int handle_context_remove_extension_deprecated(int fd, int argc, char *argv[])
476 {
477         int removing_priority = 0;
478         char *exten, *context;
479         int ret = RESULT_FAILURE;
480
481         if (argc != 4 && argc != 3) return RESULT_SHOWUSAGE;
482
483         /*
484          * Priority input checking ...
485          */
486         if (argc == 4) {
487                 char *c = argv[3];
488
489                 /* check for digits in whole parameter for right priority ...
490                  * why? because atoi (strtol) returns 0 if any characters in
491                  * string and whole extension will be removed, it's not good
492                  */
493                 if (!strcmp("hint", c))
494                         removing_priority = PRIORITY_HINT;
495                 else {
496                         while (*c && isdigit(*c))
497                                 c++;
498                         if (*c) { /* non-digit in string */
499                                 ast_cli(fd, "Invalid priority '%s'\n", argv[3]);
500                                 return RESULT_FAILURE;
501                         }
502                         removing_priority = atoi(argv[3]);
503                 }
504
505                 if (removing_priority == 0) {
506                         ast_cli(fd, "If you want to remove whole extension, please " \
507                                 "omit priority argument\n");
508                         return RESULT_FAILURE;
509                 }
510         }
511
512         /* XXX original overwrote argv[2] */
513         /*
514          * Format exten@context checking ...
515          */
516         if (split_ec(argv[2], &exten, &context))
517                 return RESULT_FAILURE; /* XXX malloc failure */
518         if ((!strlen(exten)) || (!(strlen(context)))) {
519                 ast_cli(fd, "Missing extension or context name in second argument '%s'\n",
520                         argv[2]);
521                 free(exten);
522                 return RESULT_FAILURE;
523         }
524
525         if (!ast_context_remove_extension(context, exten, removing_priority, registrar)) {
526                 if (!removing_priority)
527                         ast_cli(fd, "Whole extension %s@%s removed\n",
528                                 exten, context);
529                 else
530                         ast_cli(fd, "Extension %s@%s with priority %d removed\n",
531                                 exten, context, removing_priority);
532                         
533                 ret = RESULT_SUCCESS;
534         } else {
535                 ast_cli(fd, "Failed to remove extension %s@%s\n", exten, context);
536                 ret = RESULT_FAILURE;
537         }
538         free(exten);
539         return ret;
540 }
541
542 static int handle_context_remove_extension(int fd, int argc, char *argv[])
543 {
544         int removing_priority = 0;
545         char *exten, *context;
546         int ret = RESULT_FAILURE;
547
548         if (argc != 5 && argc != 4) return RESULT_SHOWUSAGE;
549
550         /*
551          * Priority input checking ...
552          */
553         if (argc == 5) {
554                 char *c = argv[4];
555
556                 /* check for digits in whole parameter for right priority ...
557                  * why? because atoi (strtol) returns 0 if any characters in
558                  * string and whole extension will be removed, it's not good
559                  */
560                 if (!strcmp("hint", c))
561                         removing_priority = PRIORITY_HINT;
562                 else {
563                         while (*c && isdigit(*c))
564                                 c++;
565                         if (*c) { /* non-digit in string */
566                                 ast_cli(fd, "Invalid priority '%s'\n", argv[4]);
567                                 return RESULT_FAILURE;
568                         }
569                         removing_priority = atoi(argv[4]);
570                 }
571
572                 if (removing_priority == 0) {
573                         ast_cli(fd, "If you want to remove whole extension, please " \
574                                 "omit priority argument\n");
575                         return RESULT_FAILURE;
576                 }
577         }
578
579         /* XXX original overwrote argv[3] */
580         /*
581          * Format exten@context checking ...
582          */
583         if (split_ec(argv[3], &exten, &context))
584                 return RESULT_FAILURE; /* XXX malloc failure */
585         if ((!strlen(exten)) || (!(strlen(context)))) {
586                 ast_cli(fd, "Missing extension or context name in third argument '%s'\n",
587                         argv[3]);
588                 free(exten);
589                 return RESULT_FAILURE;
590         }
591
592         if (!ast_context_remove_extension(context, exten, removing_priority, registrar)) {
593                 if (!removing_priority)
594                         ast_cli(fd, "Whole extension %s@%s removed\n",
595                                 exten, context);
596                 else
597                         ast_cli(fd, "Extension %s@%s with priority %d removed\n",
598                                 exten, context, removing_priority);
599                         
600                 ret = RESULT_SUCCESS;
601         } else {
602                 ast_cli(fd, "Failed to remove extension %s@%s\n", exten, context);
603                 ret = RESULT_FAILURE;
604         }
605         free(exten);
606         return ret;
607 }
608
609 #define BROKEN_READLINE 1
610
611 #ifdef BROKEN_READLINE
612 /*
613  * There is one funny thing, when you have word like 300@ and you hit
614  * <tab>, you arguments will like as your word is '300 ', so it '@'
615  * characters acts sometimes as word delimiter and sometimes as a part
616  * of word
617  *
618  * This fix function, allocates new word variable and store here every
619  * time xxx@yyy always as one word and correct pos is set too
620  *
621  * It's ugly, I know, but I'm waiting for Mark suggestion if upper is
622  * bug or feature ...
623  */
624 static int fix_complete_args(const char *line, char **word, int *pos)
625 {
626         char *_line, *_strsep_line, *_previous_word = NULL, *_word = NULL;
627         int words = 0;
628
629         _line = strdup(line);
630
631         _strsep_line = _line;
632         while (_strsep_line) {
633                 _previous_word = _word;
634                 _word = strsep(&_strsep_line, " ");
635
636                 if (_word && strlen(_word)) words++;
637         }
638
639
640         if (_word || _previous_word) {
641                 if (_word) {
642                         if (!strlen(_word)) words++;
643                         *word = strdup(_word);
644                 } else
645                         *word = strdup(_previous_word);
646                 *pos = words - 1;
647                 free(_line);
648                 return 0;
649         }
650
651         free(_line);
652         return -1;
653 }
654 #endif /* BROKEN_READLINE */
655
656 static char *complete_context_remove_extension_deprecated(const char *line, const char *word, int pos,
657         int state)
658 {
659         char *ret = NULL;
660         int which = 0;
661
662 #ifdef BROKEN_READLINE
663         char *word2;
664         /*
665          * Fix arguments, *word is a new allocated structure, REMEMBER to
666          * free *word when you want to return from this function ...
667          */
668         if (fix_complete_args(line, &word2, &pos)) {
669                 ast_log(LOG_ERROR, "Out of free memory\n");
670                 return NULL;
671         }
672         word = word2;
673 #endif
674
675         if (pos == 2) { /* 'remove extension _X_' (exten@context ... */
676                 struct ast_context *c = NULL;
677                 char *context = NULL, *exten = NULL;
678                 int le = 0;     /* length of extension */
679                 int lc = 0;     /* length of context */
680
681                 lc = split_ec(word, &exten, &context);
682 #ifdef BROKEN_READLINE
683                 free(word2);
684 #endif
685                 if (lc) /* error */
686                         return NULL;
687                 le = strlen(exten);
688                 lc = strlen(context);
689
690                 if (ast_lock_contexts()) {
691                         ast_log(LOG_ERROR, "Failed to lock context list\n");
692                         goto error2;
693                 }
694
695                 /* find our context ... */
696                 while ( (c = ast_walk_contexts(c)) ) {  /* match our context if any */
697                         struct ast_exten *e = NULL;
698                         /* XXX locking ? */
699                         if (!partial_match(ast_get_context_name(c), context, lc))
700                                 continue;       /* context not matched */
701                         while ( (e = ast_walk_context_extensions(c, e)) ) { /* try to complete extensions ... */
702                                 if ( partial_match(ast_get_extension_name(e), exten, le) && ++which > state) { /* n-th match */
703                                         /* If there is an extension then return exten@context. XXX otherwise ? */
704                                         if (exten)
705                                                 asprintf(&ret, "%s@%s", ast_get_extension_name(e), ast_get_context_name(c));
706                                         break;
707                                 }
708                         }
709                         if (e)  /* got a match */
710                                 break;
711                 }
712
713                 ast_unlock_contexts();
714         error2:
715                 if (exten)
716                         free(exten);
717         } else if (pos == 3) { /* 'remove extension EXT _X_' (priority) */
718                 char *exten = NULL, *context, *p;
719                 struct ast_context *c;
720                 int le, lc, len;
721                 const char *s = skip_words(line, 2); /* skip 'remove' 'extension' */
722                 int i = split_ec(s, &exten, &context);  /* parse ext@context */
723
724                 if (i)  /* error */
725                         goto error3;
726                 if ( (p = strchr(exten, ' ')) ) /* remove space after extension */
727                         *p = '\0';
728                 if ( (p = strchr(context, ' ')) ) /* remove space after context */
729                         *p = '\0';
730                 le = strlen(exten);
731                 lc = strlen(context);
732                 len = strlen(word);
733                 if (le == 0 || lc == 0)
734                         goto error3;
735
736                 if (ast_lock_contexts()) {
737                         ast_log(LOG_ERROR, "Failed to lock context list\n");
738                         goto error3;
739                 }
740
741                 /* walk contexts */
742                 c = NULL;
743                 while ( (c = ast_walk_contexts(c)) ) {
744                         /* XXX locking on c ? */
745                         struct ast_exten *e;
746                         if (strcmp(ast_get_context_name(c), context) != 0)
747                                 continue;
748                         /* got it, we must match here */
749                         e = NULL;
750                         while ( (e = ast_walk_context_extensions(c, e)) ) {
751                                 struct ast_exten *priority;
752                                 char buffer[10];
753
754                                 if (strcmp(ast_get_extension_name(e), exten) != 0)
755                                         continue;
756                                 /* XXX lock e ? */
757                                 priority = NULL;
758                                 while ( !ret && (priority = ast_walk_extension_priorities(e, priority)) ) {
759                                         snprintf(buffer, sizeof(buffer), "%u", ast_get_extension_priority(priority));
760                                         if (partial_match(buffer, word, len) && ++which > state) /* n-th match */
761                                                 ret = strdup(buffer);
762                                 }
763                                 break;
764                         }
765                         break;
766                 }
767                 ast_unlock_contexts();
768         error3:
769                 if (exten)
770                         free(exten);
771 #ifdef BROKEN_READLINE
772                 free(word2);
773 #endif
774         }
775         return ret; 
776 }
777
778 static char *complete_context_remove_extension(const char *line, const char *word, int pos,
779         int state)
780 {
781         char *ret = NULL;
782         int which = 0;
783
784 #ifdef BROKEN_READLINE
785         char *word2;
786         /*
787          * Fix arguments, *word is a new allocated structure, REMEMBER to
788          * free *word when you want to return from this function ...
789          */
790         if (fix_complete_args(line, &word2, &pos)) {
791                 ast_log(LOG_ERROR, "Out of free memory\n");
792                 return NULL;
793         }
794         word = word2;
795 #endif
796
797         if (pos == 3) { /* 'dialplan remove extension _X_' (exten@context ... */
798                 struct ast_context *c = NULL;
799                 char *context = NULL, *exten = NULL;
800                 int le = 0;     /* length of extension */
801                 int lc = 0;     /* length of context */
802
803                 lc = split_ec(word, &exten, &context);
804 #ifdef BROKEN_READLINE
805                 free(word2);
806 #endif
807                 if (lc) /* error */
808                         return NULL;
809                 le = strlen(exten);
810                 lc = strlen(context);
811
812                 if (ast_lock_contexts()) {
813                         ast_log(LOG_ERROR, "Failed to lock context list\n");
814                         goto error2;
815                 }
816
817                 /* find our context ... */
818                 while ( (c = ast_walk_contexts(c)) ) {  /* match our context if any */
819                         struct ast_exten *e = NULL;
820                         /* XXX locking ? */
821                         if (!partial_match(ast_get_context_name(c), context, lc))
822                                 continue;       /* context not matched */
823                         while ( (e = ast_walk_context_extensions(c, e)) ) { /* try to complete extensions ... */
824                                 if ( partial_match(ast_get_extension_name(e), exten, le) && ++which > state) { /* n-th match */
825                                         /* If there is an extension then return exten@context. XXX otherwise ? */
826                                         if (exten)
827                                                 asprintf(&ret, "%s@%s", ast_get_extension_name(e), ast_get_context_name(c));
828                                         break;
829                                 }
830                         }
831                         if (e)  /* got a match */
832                                 break;
833                 }
834
835                 ast_unlock_contexts();
836         error2:
837                 if (exten)
838                         free(exten);
839         } else if (pos == 4) { /* 'dialplan remove extension EXT _X_' (priority) */
840                 char *exten = NULL, *context, *p;
841                 struct ast_context *c;
842                 int le, lc, len;
843                 const char *s = skip_words(line, 3); /* skip 'dialplan' 'remove' 'extension' */
844                 int i = split_ec(s, &exten, &context);  /* parse ext@context */
845
846                 if (i)  /* error */
847                         goto error3;
848                 if ( (p = strchr(exten, ' ')) ) /* remove space after extension */
849                         *p = '\0';
850                 if ( (p = strchr(context, ' ')) ) /* remove space after context */
851                         *p = '\0';
852                 le = strlen(exten);
853                 lc = strlen(context);
854                 len = strlen(word);
855                 if (le == 0 || lc == 0)
856                         goto error3;
857
858                 if (ast_lock_contexts()) {
859                         ast_log(LOG_ERROR, "Failed to lock context list\n");
860                         goto error3;
861                 }
862
863                 /* walk contexts */
864                 c = NULL;
865                 while ( (c = ast_walk_contexts(c)) ) {
866                         /* XXX locking on c ? */
867                         struct ast_exten *e;
868                         if (strcmp(ast_get_context_name(c), context) != 0)
869                                 continue;
870                         /* got it, we must match here */
871                         e = NULL;
872                         while ( (e = ast_walk_context_extensions(c, e)) ) {
873                                 struct ast_exten *priority;
874                                 char buffer[10];
875
876                                 if (strcmp(ast_get_extension_name(e), exten) != 0)
877                                         continue;
878                                 /* XXX lock e ? */
879                                 priority = NULL;
880                                 while ( !ret && (priority = ast_walk_extension_priorities(e, priority)) ) {
881                                         snprintf(buffer, sizeof(buffer), "%u", ast_get_extension_priority(priority));
882                                         if (partial_match(buffer, word, len) && ++which > state) /* n-th match */
883                                                 ret = strdup(buffer);
884                                 }
885                                 break;
886                         }
887                         break;
888                 }
889                 ast_unlock_contexts();
890         error3:
891                 if (exten)
892                         free(exten);
893 #ifdef BROKEN_READLINE
894                 free(word2);
895 #endif
896         }
897         return ret; 
898 }
899
900 /*!
901  * Include context ...
902  */
903 static int handle_context_add_include_deprecated(int fd, int argc, char *argv[])
904 {
905         if (argc != 5) /* include context CTX in CTX */
906                 return RESULT_SHOWUSAGE;
907
908         /* third arg must be 'in' ... */
909         if (strcmp(argv[3], "in") && strcmp(argv[3], "into")) /* XXX why both ? */
910                 return RESULT_SHOWUSAGE;
911
912         if (ast_context_add_include(argv[4], argv[2], registrar)) {
913                 switch (errno) {
914                 case ENOMEM:
915                         ast_cli(fd, "Out of memory for context addition\n");
916                         break;
917
918                 case EBUSY:
919                         ast_cli(fd, "Failed to lock context(s) list, please try again later\n");
920                         break;
921
922                 case EEXIST:
923                         ast_cli(fd, "Context '%s' already included in '%s' context\n",
924                                 argv[2], argv[4]);
925                         break;
926
927                 case ENOENT:
928                 case EINVAL:
929                         ast_cli(fd, "There is no existence of context '%s'\n",
930                                 errno == ENOENT ? argv[4] : argv[2]);
931                         break;
932
933                 default:
934                         ast_cli(fd, "Failed to include '%s' in '%s' context\n",
935                                 argv[2], argv[4]);
936                         break;
937                 }
938                 return RESULT_FAILURE;
939         }
940
941         /* show some info ... */
942         ast_cli(fd, "Context '%s' included in '%s' context\n",
943                 argv[2], argv[4]);
944
945         return RESULT_SUCCESS;
946 }
947
948 static int handle_context_add_include(int fd, int argc, char *argv[])
949 {
950         if (argc != 6) /* dialplan add include CTX in CTX */
951                 return RESULT_SHOWUSAGE;
952
953         /* fifth arg must be 'into' ... */
954         if (strcmp(argv[4], "into"))
955                 return RESULT_SHOWUSAGE;
956
957         if (ast_context_add_include(argv[5], argv[3], registrar)) {
958                 switch (errno) {
959                 case ENOMEM:
960                         ast_cli(fd, "Out of memory for context addition\n");
961                         break;
962
963                 case EBUSY:
964                         ast_cli(fd, "Failed to lock context(s) list, please try again later\n");
965                         break;
966
967                 case EEXIST:
968                         ast_cli(fd, "Context '%s' already included in '%s' context\n",
969                                 argv[3], argv[5]);
970                         break;
971
972                 case ENOENT:
973                 case EINVAL:
974                         ast_cli(fd, "There is no existence of context '%s'\n",
975                                 errno == ENOENT ? argv[5] : argv[3]);
976                         break;
977
978                 default:
979                         ast_cli(fd, "Failed to include '%s' in '%s' context\n",
980                                 argv[3], argv[5]);
981                         break;
982                 }
983                 return RESULT_FAILURE;
984         }
985
986         /* show some info ... */
987         ast_cli(fd, "Context '%s' included in '%s' context\n",
988                 argv[3], argv[5]);
989
990         return RESULT_SUCCESS;
991 }
992
993 static char *complete_context_add_include_deprecated(const char *line, const char *word, int pos,
994     int state)
995 {
996         struct ast_context *c;
997         int which = 0;
998         char *ret = NULL;
999         int len = strlen(word);
1000
1001         if (pos == 2) {         /* 'include context _X_' (context) ... */
1002                 if (ast_lock_contexts()) {
1003                         ast_log(LOG_ERROR, "Failed to lock context list\n");
1004                         return NULL;
1005                 }
1006                 for (c = NULL; !ret && (c = ast_walk_contexts(c)); )
1007                         if (partial_match(ast_get_context_name(c), word, len) && ++which > state)
1008                                 ret = strdup(ast_get_context_name(c));
1009                 ast_unlock_contexts();
1010                 return ret;
1011         } else if (pos == 3) { /* include context CTX _X_ */
1012                 /* complete  as 'in' if context exists or we are unable to check */
1013                 char *context, *dupline;
1014                 struct ast_context *c;
1015                 const char *s = skip_words(line, 2);    /* should not fail */
1016
1017                 if (state != 0) /* only once */
1018                         return NULL;
1019
1020                 /* parse context from line ... */
1021                 context = dupline = strdup(s);
1022                 if (!context) {
1023                         ast_log(LOG_ERROR, "Out of free memory\n");
1024                         return strdup("in");
1025                 }
1026                 strsep(&dupline, " ");
1027
1028                 /* check for context existence ... */
1029                 if (ast_lock_contexts()) {
1030                         ast_log(LOG_ERROR, "Failed to lock context list\n");
1031                         /* our fault, we can't check, so complete 'in' ... */
1032                         ret = strdup("in");
1033                 } else {
1034                         for (c = NULL; !ret && (c = ast_walk_contexts(c)); )
1035                                 if (!strcmp(context, ast_get_context_name(c)))
1036                                         ret = strdup("in"); /* found */
1037                         ast_unlock_contexts();
1038                 }
1039                 free(context);
1040                 return ret;
1041         } else if (pos == 4) { /* 'include context CTX in _X_' (dst context) */
1042                 char *context, *dupline, *in;
1043                 const char *s = skip_words(line, 2); /* should not fail */
1044                 context = dupline = strdup(s);
1045                 if (!dupline) {
1046                         ast_log(LOG_ERROR, "Out of free memory\n");
1047                         return NULL;
1048                 }
1049                 strsep(&dupline, " "); /* skip context */
1050                 in = strsep(&dupline, " ");
1051                 /* error if missing context or third word is not 'in' */
1052                 if (!strlen(context) || strcmp(in, "in")) {
1053                         ast_log(LOG_ERROR, "bad context %s or missing in %s\n",
1054                                 context, in);
1055                         goto error3;
1056                 }
1057
1058                 if (ast_lock_contexts()) {
1059                         ast_log(LOG_ERROR, "Failed to lock context list\n");
1060                         goto error3;
1061                 }
1062
1063                 for (c = NULL; (c = ast_walk_contexts(c)); )
1064                         if (!strcmp(context, ast_get_context_name(c)))
1065                                 break;
1066                 if (c) { /* first context exists, go on... */
1067                         /* go through all contexts ... */
1068                         for (c = NULL; !ret && (c = ast_walk_contexts(c)); ) {
1069                                 if (!strcmp(context, ast_get_context_name(c)))
1070                                         continue; /* skip ourselves */
1071                                 if (partial_match(ast_get_context_name(c), word, len) &&
1072                                                 !lookup_ci(c, context) /* not included yet */ &&
1073                                                 ++which > state)
1074                                         ret = strdup(ast_get_context_name(c));
1075                         }
1076                 } else {
1077                         ast_log(LOG_ERROR, "context %s not found\n", context);
1078                 }
1079                 ast_unlock_contexts();
1080         error3:
1081                 free(context);
1082                 return ret;
1083         }
1084
1085         return NULL;
1086 }
1087
1088 static char *complete_context_add_include(const char *line, const char *word, int pos,
1089     int state)
1090 {
1091         struct ast_context *c;
1092         int which = 0;
1093         char *ret = NULL;
1094         int len = strlen(word);
1095
1096         if (pos == 3) {         /* 'dialplan add include _X_' (context) ... */
1097                 if (ast_lock_contexts()) {
1098                         ast_log(LOG_ERROR, "Failed to lock context list\n");
1099                         return NULL;
1100                 }
1101                 for (c = NULL; !ret && (c = ast_walk_contexts(c)); )
1102                         if (partial_match(ast_get_context_name(c), word, len) && ++which > state)
1103                                 ret = strdup(ast_get_context_name(c));
1104                 ast_unlock_contexts();
1105                 return ret;
1106         } else if (pos == 4) { /* dialplan add include CTX _X_ */
1107                 /* complete  as 'into' if context exists or we are unable to check */
1108                 char *context, *dupline;
1109                 struct ast_context *c;
1110                 const char *s = skip_words(line, 3); /* should not fail */
1111
1112                 if (state != 0) /* only once */
1113                         return NULL;
1114
1115                 /* parse context from line ... */
1116                 context = dupline = strdup(s);
1117                 if (!context) {
1118                         ast_log(LOG_ERROR, "Out of free memory\n");
1119                         return strdup("into");
1120                 }
1121                 strsep(&dupline, " ");
1122
1123                 /* check for context existence ... */
1124                 if (ast_lock_contexts()) {
1125                         ast_log(LOG_ERROR, "Failed to lock context list\n");
1126                         /* our fault, we can't check, so complete 'into' ... */
1127                         ret = strdup("into");
1128                 } else {
1129                         for (c = NULL; !ret && (c = ast_walk_contexts(c)); )
1130                                 if (!strcmp(context, ast_get_context_name(c)))
1131                                         ret = strdup("into"); /* found */
1132                         ast_unlock_contexts();
1133                 }
1134                 free(context);
1135                 return ret;
1136         } else if (pos == 5) { /* 'dialplan add include CTX into _X_' (dst context) */
1137                 char *context, *dupline, *into;
1138                 const char *s = skip_words(line, 3); /* should not fail */
1139                 context = dupline = strdup(s);
1140                 if (!dupline) {
1141                         ast_log(LOG_ERROR, "Out of free memory\n");
1142                         return NULL;
1143                 }
1144                 strsep(&dupline, " "); /* skip context */
1145                 into = strsep(&dupline, " ");
1146                 /* error if missing context or fifth word is not 'into' */
1147                 if (!strlen(context) || strcmp(into, "into")) {
1148                         ast_log(LOG_ERROR, "bad context %s or missing into %s\n",
1149                                 context, into);
1150                         goto error3;
1151                 }
1152
1153                 if (ast_lock_contexts()) {
1154                         ast_log(LOG_ERROR, "Failed to lock context list\n");
1155                         goto error3;
1156                 }
1157
1158                 for (c = NULL; (c = ast_walk_contexts(c)); )
1159                         if (!strcmp(context, ast_get_context_name(c)))
1160                                 break;
1161                 if (c) { /* first context exists, go on... */
1162                         /* go through all contexts ... */
1163                         for (c = NULL; !ret && (c = ast_walk_contexts(c)); ) {
1164                                 if (!strcmp(context, ast_get_context_name(c)))
1165                                         continue; /* skip ourselves */
1166                                 if (partial_match(ast_get_context_name(c), word, len) &&
1167                                                 !lookup_ci(c, context) /* not included yet */ &&
1168                                                 ++which > state)
1169                                         ret = strdup(ast_get_context_name(c));
1170                         }
1171                 } else {
1172                         ast_log(LOG_ERROR, "context %s not found\n", context);
1173                 }
1174                 ast_unlock_contexts();
1175         error3:
1176                 free(context);
1177                 return ret;
1178         }
1179
1180         return NULL;
1181 }
1182
1183 /*!
1184  * \brief 'save dialplan' CLI command implementation functions ...
1185  */
1186 static int handle_save_dialplan(int fd, int argc, char *argv[])
1187 {
1188         char filename[256];
1189         struct ast_context *c;
1190         struct ast_config *cfg;
1191         struct ast_variable *v;
1192         int incomplete = 0; /* incomplete config write? */
1193         FILE *output;
1194
1195         const char *base, *slash, *file;
1196
1197         if (! (static_config && !write_protect_config)) {
1198                 ast_cli(fd,
1199                         "I can't save dialplan now, see '%s' example file.\n",
1200                         config);
1201                 return RESULT_FAILURE;
1202         }
1203
1204         if (argc != 2 && argc != 3)
1205                 return RESULT_SHOWUSAGE;
1206
1207         if (ast_mutex_lock(&save_dialplan_lock)) {
1208                 ast_cli(fd,
1209                         "Failed to lock dialplan saving (another proccess saving?)\n");
1210                 return RESULT_FAILURE;
1211         }
1212         /* XXX the code here is quite loose, a pathname with .conf in it
1213          * is assumed to be a complete pathname
1214          */
1215         if (argc == 3) {        /* have config path. Look for *.conf */
1216                 base = argv[2];
1217                 if (!strstr(argv[2], ".conf")) { /*no, this is assumed to be a pathname */
1218                         /* if filename ends with '/', do not add one */
1219                         slash = (*(argv[2] + strlen(argv[2]) -1) == '/') ? "/" : "";
1220                         file = config;  /* default: 'extensions.conf' */
1221                 } else {        /* yes, complete file name */
1222                         slash = "";
1223                         file = "";
1224                 }
1225         } else {
1226                 /* no config file, default one */
1227                 base = ast_config_AST_CONFIG_DIR;
1228                 slash = "/";
1229                 file = config;
1230         }
1231         snprintf(filename, sizeof(filename), "%s%s%s", base, slash, config);
1232
1233         cfg = ast_config_load("extensions.conf");
1234
1235         /* try to lock contexts list */
1236         if (ast_lock_contexts()) {
1237                 ast_cli(fd, "Failed to lock contexts list\n");
1238                 ast_mutex_unlock(&save_dialplan_lock);
1239                 ast_config_destroy(cfg);
1240                 return RESULT_FAILURE;
1241         }
1242
1243         /* create new file ... */
1244         if (!(output = fopen(filename, "wt"))) {
1245                 ast_cli(fd, "Failed to create file '%s'\n",
1246                         filename);
1247                 ast_unlock_contexts();
1248                 ast_mutex_unlock(&save_dialplan_lock);
1249                 ast_config_destroy(cfg);
1250                 return RESULT_FAILURE;
1251         }
1252
1253         /* fireout general info */
1254         fprintf(output, "[general]\nstatic=%s\nwriteprotect=%s\nautofallthrough=%s\nclearglobalvars=%s\npriorityjumping=%s\n\n",
1255                 static_config ? "yes" : "no",
1256                 write_protect_config ? "yes" : "no",
1257                 autofallthrough_config ? "yes" : "no",
1258                 clearglobalvars_config ? "yes" : "no",
1259                 ast_true(ast_variable_retrieve(cfg, "general", "priorityjumping")) ? "yes" : "no");
1260
1261         if ((v = ast_variable_browse(cfg, "globals"))) {
1262                 fprintf(output, "[globals]\n");
1263                 while(v) {
1264                         fprintf(output, "%s => %s\n", v->name, v->value);
1265                         v = v->next;
1266                 }
1267                 fprintf(output, "\n");
1268         }
1269
1270         ast_config_destroy(cfg);
1271         
1272 #define PUT_CTX_HDR     do { \
1273         if (!context_header_written) {  \
1274                 fprintf(output, "[%s]\n", ast_get_context_name(c));     \
1275                 context_header_written = 1;     \
1276         }       \
1277         } while (0)
1278
1279         /* walk all contexts */
1280         for (c = NULL; (c = ast_walk_contexts(c)); ) {
1281                 int context_header_written = 0;
1282                 struct ast_exten *e, *last_written_e = NULL;
1283                 struct ast_include *i;
1284                 struct ast_ignorepat *ip;
1285                 struct ast_sw *sw;
1286
1287                 /* try to lock context and fireout all info */  
1288                 if (ast_lock_context(c)) { /* lock failure */
1289                         incomplete = 1;
1290                         continue;
1291                 }
1292                 /* registered by this module? */
1293                 /* XXX do we need this ? */
1294                 if (!strcmp(ast_get_context_registrar(c), registrar)) {
1295                         fprintf(output, "[%s]\n", ast_get_context_name(c));
1296                         context_header_written = 1;
1297                 }
1298
1299                 /* walk extensions ... */
1300                 for (e = NULL; (e = ast_walk_context_extensions(c, e)); ) {
1301                         struct ast_exten *p = NULL;
1302
1303                         /* fireout priorities */
1304                         while ( (p = ast_walk_extension_priorities(e, p)) ) {
1305                                 if (strcmp(ast_get_extension_registrar(p), registrar) != 0) /* not this source */
1306                                         continue;
1307                 
1308                                 /* make empty line between different extensions */      
1309                                 if (last_written_e != NULL &&
1310                                             strcmp(ast_get_extension_name(last_written_e),
1311                                                     ast_get_extension_name(p)))
1312                                         fprintf(output, "\n");
1313                                 last_written_e = p;
1314                         
1315                                 PUT_CTX_HDR;
1316
1317                                 if (ast_get_extension_priority(p)==PRIORITY_HINT) { /* easy */
1318                                         fprintf(output, "exten => %s,hint,%s\n",
1319                                                     ast_get_extension_name(p),
1320                                                     ast_get_extension_app(p));
1321                                 } else { /* copy and replace '|' with ',' */
1322                                         const char *sep, *cid;
1323                                         char *tempdata;
1324                                         char *s;
1325                                         const char *el = ast_get_extension_label(p);
1326                                         char label[128];
1327  
1328                                         tempdata = ast_strdupa(ast_get_extension_app_data(p));
1329
1330                                         for (s = tempdata; *s; s++) {
1331                                                 if (*s == '|')
1332                                                         *s = ',';
1333                                         }
1334
1335                                         if (ast_get_extension_matchcid(p)) {
1336                                                 sep = "/";
1337                                                 cid = ast_get_extension_cidmatch(p);
1338                                         } else
1339                                                 sep = cid = "";
1340                                 
1341                                         if (el && (snprintf(label, 127, "(%s)", el) != (strlen(el) + 2)))
1342                                                 incomplete = 1; /* error encountered or label > 125 chars */
1343                                         
1344                                         fprintf(output, "exten => %s%s%s,%d%s,%s(%s)\n",
1345                                             ast_get_extension_name(p), sep, cid,
1346                                             ast_get_extension_priority(p), label,
1347                                             ast_get_extension_app(p), tempdata);
1348                                 }
1349                         }
1350                 }
1351
1352                 /* written any extensions? ok, write space between exten & inc */
1353                 if (last_written_e)
1354                         fprintf(output, "\n");
1355
1356                 /* walk through includes */
1357                 for (i = NULL; (i = ast_walk_context_includes(c, i)) ; ) {
1358                         if (strcmp(ast_get_include_registrar(i), registrar) != 0)
1359                                 continue; /* not mine */
1360                         PUT_CTX_HDR;
1361                         fprintf(output, "include => %s\n", ast_get_include_name(i));
1362                 }
1363                 if (ast_walk_context_includes(c, NULL))
1364                         fprintf(output, "\n");
1365
1366                 /* walk through switches */
1367                 for (sw = NULL; (sw = ast_walk_context_switches(c, sw)) ; ) {
1368                         if (strcmp(ast_get_switch_registrar(sw), registrar) != 0)
1369                                 continue; /* not mine */
1370                         PUT_CTX_HDR;
1371                         fprintf(output, "switch => %s/%s\n",
1372                                     ast_get_switch_name(sw), ast_get_switch_data(sw));
1373                 }
1374
1375                 if (ast_walk_context_switches(c, NULL))
1376                         fprintf(output, "\n");
1377
1378                 /* fireout ignorepats ... */
1379                 for (ip = NULL; (ip = ast_walk_context_ignorepats(c, ip)); ) {
1380                         if (strcmp(ast_get_ignorepat_registrar(ip), registrar) != 0)
1381                                 continue; /* not mine */
1382                         PUT_CTX_HDR;
1383                         fprintf(output, "ignorepat => %s\n",
1384                                                 ast_get_ignorepat_name(ip));
1385                 }
1386
1387                 ast_unlock_context(c);
1388         }       
1389
1390         ast_unlock_contexts();
1391         ast_mutex_unlock(&save_dialplan_lock);
1392         fclose(output);
1393
1394         if (incomplete) {
1395                 ast_cli(fd, "Saved dialplan is incomplete\n");
1396                 return RESULT_FAILURE;
1397         }
1398
1399         ast_cli(fd, "Dialplan successfully saved into '%s'\n",
1400                 filename);
1401         return RESULT_SUCCESS;
1402 }
1403
1404 /*!
1405  * \brief ADD EXTENSION command stuff
1406  */
1407 static int handle_context_add_extension_deprecated(int fd, int argc, char *argv[])
1408 {
1409         char *whole_exten;
1410         char *exten, *prior;
1411         int iprior = -2;
1412         char *cidmatch, *app, *app_data;
1413         char *start, *end;
1414
1415         /* check for arguments at first */
1416         if (argc != 5 && argc != 6)
1417                 return RESULT_SHOWUSAGE;
1418         if (strcmp(argv[3], "into"))
1419                 return RESULT_SHOWUSAGE;
1420         if (argc == 6) if (strcmp(argv[5], "replace")) return RESULT_SHOWUSAGE;
1421
1422         /* XXX overwrite argv[2] */
1423         whole_exten = argv[2];
1424         exten   = strsep(&whole_exten,",");
1425         if (strchr(exten, '/')) {
1426                 cidmatch = exten;
1427                 strsep(&cidmatch,"/");
1428         } else {
1429                 cidmatch = NULL;
1430         }
1431         prior       = strsep(&whole_exten,",");
1432         if (prior) {
1433                 if (!strcmp(prior, "hint")) {
1434                         iprior = PRIORITY_HINT;
1435                 } else {
1436                         if (sscanf(prior, "%d", &iprior) != 1) {
1437                                 ast_cli(fd, "'%s' is not a valid priority\n", prior);
1438                                 prior = NULL;
1439                         }
1440                 }
1441         }
1442         app = whole_exten;
1443         if (app && (start = strchr(app, '(')) && (end = strrchr(app, ')'))) {
1444                 *start = *end = '\0';
1445                 app_data = start + 1;
1446                 ast_process_quotes_and_slashes(app_data, ',', '|');
1447         } else {
1448                 if (app) {
1449                         app_data = strchr(app, ',');
1450                         if (app_data) {
1451                                 *app_data = '\0';
1452                                 app_data++;
1453                         }
1454                 } else  
1455                         app_data = NULL;
1456         }
1457
1458         if (!exten || !prior || !app || (!app_data && iprior != PRIORITY_HINT))
1459                 return RESULT_SHOWUSAGE;
1460
1461         if (!app_data)
1462                 app_data="";
1463         if (ast_add_extension(argv[4], argc == 6 ? 1 : 0, exten, iprior, NULL, cidmatch, app,
1464                 (void *)strdup(app_data), free, registrar)) {
1465                 switch (errno) {
1466                 case ENOMEM:
1467                         ast_cli(fd, "Out of free memory\n");
1468                         break;
1469
1470                 case EBUSY:
1471                         ast_cli(fd, "Failed to lock context(s) list, please try again later\n");
1472                         break;
1473
1474                 case ENOENT:
1475                         ast_cli(fd, "No existence of '%s' context\n", argv[4]);
1476                         break;
1477
1478                 case EEXIST:
1479                         ast_cli(fd, "Extension %s@%s with priority %s already exists\n",
1480                                 exten, argv[4], prior);
1481                         break;
1482
1483                 default:
1484                         ast_cli(fd, "Failed to add '%s,%s,%s,%s' extension into '%s' context\n",
1485                                         exten, prior, app, app_data, argv[4]);
1486                         break;
1487                 }
1488                 return RESULT_FAILURE;
1489         }
1490
1491         if (argc == 6) 
1492                 ast_cli(fd, "Extension %s@%s (%s) replace by '%s,%s,%s,%s'\n",
1493                         exten, argv[4], prior, exten, prior, app, app_data);
1494         else
1495                 ast_cli(fd, "Extension '%s,%s,%s,%s' added into '%s' context\n",
1496                         exten, prior, app, app_data, argv[4]);
1497
1498         return RESULT_SUCCESS;
1499 }
1500 static int handle_context_add_extension(int fd, int argc, char *argv[])
1501 {
1502         char *whole_exten;
1503         char *exten, *prior;
1504         int iprior = -2;
1505         char *cidmatch, *app, *app_data;
1506         char *start, *end;
1507
1508         /* check for arguments at first */
1509         if (argc != 6 && argc != 7)
1510                 return RESULT_SHOWUSAGE;
1511         if (strcmp(argv[3], "into"))
1512                 return RESULT_SHOWUSAGE;
1513         if (argc == 7) if (strcmp(argv[6], "replace")) return RESULT_SHOWUSAGE;
1514
1515         /* XXX overwrite argv[3] */
1516         whole_exten = argv[3];
1517         exten   = strsep(&whole_exten,",");
1518         if (strchr(exten, '/')) {
1519                 cidmatch = exten;
1520                 strsep(&cidmatch,"/");
1521         } else {
1522                 cidmatch = NULL;
1523         }
1524         prior = strsep(&whole_exten,",");
1525         if (prior) {
1526                 if (!strcmp(prior, "hint")) {
1527                         iprior = PRIORITY_HINT;
1528                 } else {
1529                         if (sscanf(prior, "%d", &iprior) != 1) {
1530                                 ast_cli(fd, "'%s' is not a valid priority\n", prior);
1531                                 prior = NULL;
1532                         }
1533                 }
1534         }
1535         app = whole_exten;
1536         if (app && (start = strchr(app, '(')) && (end = strrchr(app, ')'))) {
1537                 *start = *end = '\0';
1538                 app_data = start + 1;
1539                 ast_process_quotes_and_slashes(app_data, ',', '|');
1540         } else {
1541                 if (app) {
1542                         app_data = strchr(app, ',');
1543                         if (app_data) {
1544                                 *app_data = '\0';
1545                                 app_data++;
1546                         }
1547                 } else  
1548                         app_data = NULL;
1549         }
1550
1551         if (!exten || !prior || !app || (!app_data && iprior != PRIORITY_HINT))
1552                 return RESULT_SHOWUSAGE;
1553
1554         if (!app_data)
1555                 app_data="";
1556         if (ast_add_extension(argv[5], argc == 6 ? 1 : 0, exten, iprior, NULL, cidmatch, app,
1557                 (void *)strdup(app_data), free, registrar)) {
1558                 switch (errno) {
1559                 case ENOMEM:
1560                         ast_cli(fd, "Out of free memory\n");
1561                         break;
1562
1563                 case EBUSY:
1564                         ast_cli(fd, "Failed to lock context(s) list, please try again later\n");
1565                         break;
1566
1567                 case ENOENT:
1568                         ast_cli(fd, "No existence of '%s' context\n", argv[5]);
1569                         break;
1570
1571                 case EEXIST:
1572                         ast_cli(fd, "Extension %s@%s with priority %s already exists\n",
1573                                 exten, argv[5], prior);
1574                         break;
1575
1576                 default:
1577                         ast_cli(fd, "Failed to add '%s,%s,%s,%s' extension into '%s' context\n",
1578                                         exten, prior, app, app_data, argv[5]);
1579                         break;
1580                 }
1581                 return RESULT_FAILURE;
1582         }
1583
1584         if (argc == 7)
1585                 ast_cli(fd, "Extension %s@%s (%s) replace by '%s,%s,%s,%s'\n",
1586                         exten, argv[5], prior, exten, prior, app, app_data);
1587         else
1588                 ast_cli(fd, "Extension '%s,%s,%s,%s' added into '%s' context\n",
1589                         exten, prior, app, app_data, argv[5]);
1590
1591         return RESULT_SUCCESS;
1592 }
1593
1594 /*! dialplan add extension 6123,1,Dial,IAX/212.71.138.13/6123 into local */
1595 static char *complete_context_add_extension_deprecated(const char *line, const char *word, int pos, int state)
1596 {
1597         int which = 0;
1598
1599         if (pos == 3) {         /* complete 'into' word ... */
1600                 return (state == 0) ? strdup("into") : NULL;
1601         } else if (pos == 4) { /* complete context */
1602                 struct ast_context *c = NULL;
1603                 int len = strlen(word);
1604                 char *res = NULL;
1605
1606                 /* try to lock contexts list ... */
1607                 if (ast_lock_contexts()) {
1608                         ast_log(LOG_WARNING, "Failed to lock contexts list\n");
1609                         return NULL;
1610                 }
1611
1612                 /* walk through all contexts */
1613                 while ( !res && (c = ast_walk_contexts(c)) )
1614                         if (partial_match(ast_get_context_name(c), word, len) && ++which > state)
1615                                 res = strdup(ast_get_context_name(c));
1616                 ast_unlock_contexts();
1617                 return res;
1618         } else if (pos == 5) {
1619                 return state == 0 ? strdup("replace") : NULL;
1620         }
1621         return NULL;
1622 }
1623
1624 static char *complete_context_add_extension(const char *line, const char *word, int pos, int state)
1625 {
1626         int which = 0;
1627
1628         if (pos == 4) {         /* complete 'into' word ... */
1629                 return (state == 0) ? strdup("into") : NULL;
1630         } else if (pos == 5) { /* complete context */
1631                 struct ast_context *c = NULL;
1632                 int len = strlen(word);
1633                 char *res = NULL;
1634
1635                 /* try to lock contexts list ... */
1636                 if (ast_lock_contexts()) {
1637                         ast_log(LOG_WARNING, "Failed to lock contexts list\n");
1638                         return NULL;
1639                 }
1640
1641                 /* walk through all contexts */
1642                 while ( !res && (c = ast_walk_contexts(c)) )
1643                         if (partial_match(ast_get_context_name(c), word, len) && ++which > state)
1644                                 res = strdup(ast_get_context_name(c));
1645                 ast_unlock_contexts();
1646                 return res;
1647         } else if (pos == 6) {
1648                 return state == 0 ? strdup("replace") : NULL;
1649         }
1650         return NULL;
1651 }
1652
1653 /*!
1654  * IGNOREPAT CLI stuff
1655  */
1656 static int handle_context_add_ignorepat_deprecated(int fd, int argc, char *argv[])
1657 {
1658         if (argc != 5)
1659                 return RESULT_SHOWUSAGE;
1660         if (strcmp(argv[3], "into"))
1661                 return RESULT_SHOWUSAGE;
1662
1663         if (ast_context_add_ignorepat(argv[4], argv[2], registrar)) {
1664                 switch (errno) {
1665                 case ENOMEM:
1666                         ast_cli(fd, "Out of free memory\n");
1667                         break;
1668
1669                 case ENOENT:
1670                         ast_cli(fd, "There is no existence of '%s' context\n", argv[4]);
1671                         break;
1672
1673                 case EEXIST:
1674                         ast_cli(fd, "Ignore pattern '%s' already included in '%s' context\n",
1675                                 argv[2], argv[4]);
1676                         break;
1677
1678                 case EBUSY:
1679                         ast_cli(fd, "Failed to lock context(s) list, please, try again later\n");
1680                         break;
1681
1682                 default:
1683                         ast_cli(fd, "Failed to add ingore pattern '%s' into '%s' context\n",
1684                                 argv[2], argv[4]);
1685                         break;
1686                 }
1687                 return RESULT_FAILURE;
1688         }
1689
1690         ast_cli(fd, "Ignore pattern '%s' added into '%s' context\n",
1691                 argv[2], argv[4]);
1692         return RESULT_SUCCESS;
1693 }
1694
1695 static int handle_context_add_ignorepat(int fd, int argc, char *argv[])
1696 {
1697         if (argc != 6)
1698                 return RESULT_SHOWUSAGE;
1699         if (strcmp(argv[4], "into"))
1700                 return RESULT_SHOWUSAGE;
1701
1702         if (ast_context_add_ignorepat(argv[5], argv[3], registrar)) {
1703                 switch (errno) {
1704                 case ENOMEM:
1705                         ast_cli(fd, "Out of free memory\n");
1706                         break;
1707
1708                 case ENOENT:
1709                         ast_cli(fd, "There is no existence of '%s' context\n", argv[5]);
1710                         break;
1711
1712                 case EEXIST:
1713                         ast_cli(fd, "Ignore pattern '%s' already included in '%s' context\n",
1714                                 argv[3], argv[5]);
1715                         break;
1716
1717                 case EBUSY:
1718                         ast_cli(fd, "Failed to lock context(s) list, please, try again later\n");
1719                         break;
1720
1721                 default:
1722                         ast_cli(fd, "Failed to add ingore pattern '%s' into '%s' context\n",
1723                                 argv[3], argv[5]);
1724                         break;
1725                 }
1726                 return RESULT_FAILURE;
1727         }
1728
1729         ast_cli(fd, "Ignore pattern '%s' added into '%s' context\n",
1730                 argv[3], argv[5]);
1731         return RESULT_SUCCESS;
1732 }
1733
1734 static char *complete_context_add_ignorepat_deprecated(const char *line, const char *word,
1735         int pos, int state)
1736 {
1737         if (pos == 3)
1738                 return state == 0 ? strdup("into") : NULL;
1739         else if (pos == 4) {
1740                 struct ast_context *c;
1741                 int which = 0;
1742                 char *dupline, *ignorepat = NULL;
1743                 const char *s;
1744                 char *ret = NULL;
1745                 int len = strlen(word);
1746
1747                 /* XXX skip first two words 'add' 'ignorepat' */
1748                 s = skip_words(line, 2);
1749                 if (s == NULL)
1750                         return NULL;
1751                 dupline = strdup(s);
1752                 if (!dupline) {
1753                         ast_log(LOG_ERROR, "Malloc failure\n");
1754                         return NULL;
1755                 }
1756                 ignorepat = strsep(&dupline, " ");
1757
1758                 if (ast_lock_contexts()) {
1759                         ast_log(LOG_ERROR, "Failed to lock contexts list\n");
1760                         return NULL;
1761                 }
1762
1763                 for (c = NULL; !ret && (c = ast_walk_contexts(c));) {
1764                         int found = 0;
1765
1766                         if (!partial_match(ast_get_context_name(c), word, len))
1767                                 continue; /* not mine */
1768                         if (ignorepat) /* there must be one, right ? */
1769                                 found = lookup_c_ip(c, ignorepat);
1770                         if (!found && ++which > state)
1771                                 ret = strdup(ast_get_context_name(c));
1772                 }
1773
1774                 if (ignorepat)
1775                         free(ignorepat);
1776                 ast_unlock_contexts();
1777                 return ret;
1778         }
1779
1780         return NULL;
1781 }
1782
1783 static char *complete_context_add_ignorepat(const char *line, const char *word,
1784         int pos, int state)
1785 {
1786         if (pos == 4)
1787                 return state == 0 ? strdup("into") : NULL;
1788         else if (pos == 5) {
1789                 struct ast_context *c;
1790                 int which = 0;
1791                 char *dupline, *ignorepat = NULL;
1792                 const char *s;
1793                 char *ret = NULL;
1794                 int len = strlen(word);
1795
1796                 /* XXX skip first three words 'dialplan' 'add' 'ignorepat' */
1797                 s = skip_words(line, 3);
1798                 if (s == NULL)
1799                         return NULL;
1800                 dupline = strdup(s);
1801                 if (!dupline) {
1802                         ast_log(LOG_ERROR, "Malloc failure\n");
1803                         return NULL;
1804                 }
1805                 ignorepat = strsep(&dupline, " ");
1806
1807                 if (ast_lock_contexts()) {
1808                         ast_log(LOG_ERROR, "Failed to lock contexts list\n");
1809                         return NULL;
1810                 }
1811
1812                 for (c = NULL; !ret && (c = ast_walk_contexts(c));) {
1813                         int found = 0;
1814
1815                         if (!partial_match(ast_get_context_name(c), word, len))
1816                                 continue; /* not mine */
1817                         if (ignorepat) /* there must be one, right ? */
1818                                 found = lookup_c_ip(c, ignorepat);
1819                         if (!found && ++which > state)
1820                                 ret = strdup(ast_get_context_name(c));
1821                 }
1822
1823                 if (ignorepat)
1824                         free(ignorepat);
1825                 ast_unlock_contexts();
1826                 return ret;
1827         }
1828
1829         return NULL;
1830 }
1831
1832 static int handle_context_remove_ignorepat_deprecated(int fd, int argc, char *argv[])
1833 {
1834         if (argc != 5)
1835                 return RESULT_SHOWUSAGE;
1836         if (strcmp(argv[3], "from"))
1837                 return RESULT_SHOWUSAGE;
1838
1839         if (ast_context_remove_ignorepat(argv[4], argv[2], registrar)) {
1840                 switch (errno) {
1841                 case EBUSY:
1842                         ast_cli(fd, "Failed to lock context(s) list, please try again later\n");
1843                         break;
1844
1845                 case ENOENT:
1846                         ast_cli(fd, "There is no existence of '%s' context\n", argv[4]);
1847                         break;
1848
1849                 case EINVAL:
1850                         ast_cli(fd, "There is no existence of '%s' ignore pattern in '%s' context\n",
1851                                         argv[2], argv[4]);
1852                         break;
1853
1854                 default:
1855                         ast_cli(fd, "Failed to remove ignore pattern '%s' from '%s' context\n", argv[2], argv[4]);
1856                         break;
1857                 }
1858                 return RESULT_FAILURE;
1859         }
1860
1861         ast_cli(fd, "Ignore pattern '%s' removed from '%s' context\n",
1862                 argv[2], argv[4]);
1863         return RESULT_SUCCESS;
1864 }
1865
1866 static int handle_context_remove_ignorepat(int fd, int argc, char *argv[])
1867 {
1868         if (argc != 6)
1869                 return RESULT_SHOWUSAGE;
1870         if (strcmp(argv[4], "from"))
1871                 return RESULT_SHOWUSAGE;
1872
1873         if (ast_context_remove_ignorepat(argv[5], argv[3], registrar)) {
1874                 switch (errno) {
1875                 case EBUSY:
1876                         ast_cli(fd, "Failed to lock context(s) list, please try again later\n");
1877                         break;
1878
1879                 case ENOENT:
1880                         ast_cli(fd, "There is no existence of '%s' context\n", argv[5]);
1881                         break;
1882
1883                 case EINVAL:
1884                         ast_cli(fd, "There is no existence of '%s' ignore pattern in '%s' context\n",
1885                                         argv[3], argv[5]);
1886                         break;
1887
1888                 default:
1889                         ast_cli(fd, "Failed to remove ignore pattern '%s' from '%s' context\n", argv[3], argv[5]);
1890                         break;
1891                 }
1892                 return RESULT_FAILURE;
1893         }
1894
1895         ast_cli(fd, "Ignore pattern '%s' removed from '%s' context\n",
1896                 argv[3], argv[5]);
1897         return RESULT_SUCCESS;
1898 }
1899
1900 static char *complete_context_remove_ignorepat_deprecated(const char *line, const char *word,
1901         int pos, int state)
1902 {
1903         struct ast_context *c;
1904         int which = 0;
1905         char *ret = NULL;
1906
1907         if (pos == 2) {
1908                 int len = strlen(word);
1909                 if (ast_lock_contexts()) {
1910                         ast_log(LOG_WARNING, "Failed to lock contexts list\n");
1911                         return NULL;
1912                 }
1913
1914                 for (c = NULL; !ret && (c = ast_walk_contexts(c));) {
1915                         struct ast_ignorepat *ip;
1916
1917                         if (ast_lock_context(c))        /* error, skip it */
1918                                 continue;
1919                         
1920                         for (ip = NULL; !ret && (ip = ast_walk_context_ignorepats(c, ip));) {
1921                                 if (partial_match(ast_get_ignorepat_name(ip), word, len) && ++which > state) {
1922                                         /* n-th match */
1923                                         struct ast_context *cw = NULL;
1924                                         int found = 0;
1925                                         while ( (cw = ast_walk_contexts(cw)) && cw != c && !found) {
1926                                                 /* XXX do i stop on c, or skip it ? */
1927                                                 found = lookup_c_ip(cw, ast_get_ignorepat_name(ip));
1928                                         }
1929                                         if (!found)
1930                                                 ret = strdup(ast_get_ignorepat_name(ip));
1931                                 }
1932                         }
1933                         ast_unlock_context(c);
1934                 }
1935                 ast_unlock_contexts();
1936                 return ret;
1937         } else if (pos == 3) {
1938                  return state == 0 ? strdup("from") : NULL;
1939         } else if (pos == 4) { /* XXX check this */
1940                 char *dupline, *duplinet, *ignorepat;
1941                 int len = strlen(word);
1942
1943                 dupline = strdup(line);
1944                 if (!dupline) {
1945                         ast_log(LOG_WARNING, "Out of free memory\n");
1946                         return NULL;
1947                 }
1948
1949                 duplinet = dupline;
1950                 strsep(&duplinet, " ");
1951                 strsep(&duplinet, " ");
1952                 ignorepat = strsep(&duplinet, " ");
1953
1954                 if (!ignorepat) {
1955                         free(dupline);
1956                         return NULL;
1957                 }
1958
1959                 if (ast_lock_contexts()) {
1960                         ast_log(LOG_WARNING, "Failed to lock contexts list\n");
1961                         free(dupline);
1962                         return NULL;
1963                 }
1964
1965                 for (c = NULL; !ret && (c = ast_walk_contexts(c)); ) {
1966                         if (ast_lock_context(c))        /* fail, skip it */
1967                                 continue;
1968                         if (!partial_match(ast_get_context_name(c), word, len))
1969                                 continue;
1970                         if (lookup_c_ip(c, ignorepat) && ++which > state)
1971                                 ret = strdup(ast_get_context_name(c));
1972                         ast_unlock_context(c);
1973                 }
1974                 ast_unlock_contexts();
1975                 free(dupline);
1976                 return NULL;
1977         }
1978
1979         return NULL;
1980 }
1981
1982 static char *complete_context_remove_ignorepat(const char *line, const char *word,
1983         int pos, int state)
1984 {
1985         struct ast_context *c;
1986         int which = 0;
1987         char *ret = NULL;
1988
1989         if (pos == 3) {
1990                 int len = strlen(word);
1991                 if (ast_lock_contexts()) {
1992                         ast_log(LOG_WARNING, "Failed to lock contexts list\n");
1993                         return NULL;
1994                 }
1995
1996                 for (c = NULL; !ret && (c = ast_walk_contexts(c));) {
1997                         struct ast_ignorepat *ip;
1998
1999                         if (ast_lock_context(c))        /* error, skip it */
2000                                 continue;
2001                         
2002                         for (ip = NULL; !ret && (ip = ast_walk_context_ignorepats(c, ip));) {
2003                                 if (partial_match(ast_get_ignorepat_name(ip), word, len) && ++which > state) {
2004                                         /* n-th match */
2005                                         struct ast_context *cw = NULL;
2006                                         int found = 0;
2007                                         while ( (cw = ast_walk_contexts(cw)) && cw != c && !found) {
2008                                                 /* XXX do i stop on c, or skip it ? */
2009                                                 found = lookup_c_ip(cw, ast_get_ignorepat_name(ip));
2010                                         }
2011                                         if (!found)
2012                                                 ret = strdup(ast_get_ignorepat_name(ip));
2013                                 }
2014                         }
2015                         ast_unlock_context(c);
2016                 }
2017                 ast_unlock_contexts();
2018                 return ret;
2019         } else if (pos == 4) {
2020                  return state == 0 ? strdup("from") : NULL;
2021         } else if (pos == 5) { /* XXX check this */
2022                 char *dupline, *duplinet, *ignorepat;
2023                 int len = strlen(word);
2024
2025                 dupline = strdup(line);
2026                 if (!dupline) {
2027                         ast_log(LOG_WARNING, "Out of free memory\n");
2028                         return NULL;
2029                 }
2030
2031                 duplinet = dupline;
2032                 strsep(&duplinet, " ");
2033                 strsep(&duplinet, " ");
2034                 ignorepat = strsep(&duplinet, " ");
2035
2036                 if (!ignorepat) {
2037                         free(dupline);
2038                         return NULL;
2039                 }
2040
2041                 if (ast_lock_contexts()) {
2042                         ast_log(LOG_WARNING, "Failed to lock contexts list\n");
2043                         free(dupline);
2044                         return NULL;
2045                 }
2046
2047                 for (c = NULL; !ret && (c = ast_walk_contexts(c)); ) {
2048                         if (ast_lock_context(c))        /* fail, skip it */
2049                                 continue;
2050                         if (!partial_match(ast_get_context_name(c), word, len))
2051                                 continue;
2052                         if (lookup_c_ip(c, ignorepat) && ++which > state)
2053                                 ret = strdup(ast_get_context_name(c));
2054                         ast_unlock_context(c);
2055                 }
2056                 ast_unlock_contexts();
2057                 free(dupline);
2058                 return NULL;
2059         }
2060
2061         return NULL;
2062 }
2063
2064 static int pbx_load_module(void);
2065
2066 static int handle_reload_extensions(int fd, int argc, char *argv[])
2067 {
2068         if (argc != 2)
2069                 return RESULT_SHOWUSAGE;
2070         pbx_load_module();
2071         return RESULT_SUCCESS;
2072 }
2073
2074 /*!
2075  * CLI entries for commands provided by this module
2076  */
2077 static struct ast_cli_entry cli_dont_include_deprecated = {
2078         { "dont", "include", NULL },
2079         handle_context_dont_include_deprecated, NULL,
2080         NULL, complete_context_dont_include_deprecated };
2081
2082 static struct ast_cli_entry cli_remove_extension_deprecated = {
2083         { "remove", "extension", NULL },
2084         handle_context_remove_extension_deprecated, NULL,
2085         NULL, complete_context_remove_extension_deprecated };
2086
2087 static struct ast_cli_entry cli_include_context_deprecated = {
2088         { "include", "context", NULL },
2089         handle_context_add_include_deprecated, NULL,
2090         NULL, complete_context_add_include_deprecated };
2091
2092 static struct ast_cli_entry cli_add_extension_deprecated = {
2093         { "add", "extension", NULL },
2094         handle_context_add_extension_deprecated, NULL,
2095         NULL, complete_context_add_extension_deprecated };
2096
2097 static struct ast_cli_entry cli_add_ignorepat_deprecated = {
2098         { "add", "ignorepat", NULL },
2099         handle_context_add_ignorepat_deprecated, NULL,
2100         NULL, complete_context_add_ignorepat_deprecated };
2101
2102 static struct ast_cli_entry cli_remove_ignorepat_deprecated = {
2103         { "remove", "ignorepat", NULL },
2104         handle_context_remove_ignorepat_deprecated, NULL,
2105         NULL, complete_context_remove_ignorepat_deprecated };
2106
2107 static struct ast_cli_entry cli_extensions_reload_deprecated = {
2108         { "extensions", "reload", NULL },
2109         handle_reload_extensions, NULL,
2110         NULL };
2111
2112 static struct ast_cli_entry cli_save_dialplan_deprecated = {
2113         { "save", "dialplan", NULL },
2114         handle_save_dialplan, NULL,
2115         NULL };
2116
2117 static struct ast_cli_entry cli_pbx_config[] = {
2118         { { "dialplan", "add", "extension", NULL },
2119         handle_context_add_extension, "Add new extension into context",
2120         context_add_extension_help, complete_context_add_extension, &cli_add_extension_deprecated },
2121
2122         { { "dialplan", "remove", "extension", NULL },
2123         handle_context_remove_extension, "Remove a specified extension",
2124         context_remove_extension_help, complete_context_remove_extension, &cli_remove_extension_deprecated },
2125
2126         { { "dialplan", "add", "ignorepat", NULL },
2127         handle_context_add_ignorepat, "Add new ignore pattern",
2128         context_add_ignorepat_help, complete_context_add_ignorepat, &cli_add_ignorepat_deprecated },
2129
2130         { { "dialplan", "remove", "ignorepat", NULL },
2131         handle_context_remove_ignorepat, "Remove ignore pattern from context",
2132         context_remove_ignorepat_help, complete_context_remove_ignorepat, &cli_remove_ignorepat_deprecated },
2133
2134         { { "dialplan", "add", "include", NULL },
2135         handle_context_add_include, "Include context in other context",
2136         context_add_include_help, complete_context_add_include, &cli_include_context_deprecated },
2137
2138         { { "dialplan", "remove", "include", NULL },
2139         handle_context_remove_include, "Remove a specified include from context",
2140         context_remove_include_help, complete_context_remove_include, &cli_dont_include_deprecated },
2141
2142         { { "dialplan", "reload", NULL },
2143         handle_reload_extensions, "Reload extensions and *only* extensions",
2144         reload_extensions_help, NULL, &cli_extensions_reload_deprecated },
2145 };
2146
2147
2148 static struct ast_cli_entry cli_dialplan_save = {
2149         { "dialplan", "save", NULL },
2150         handle_save_dialplan, "Save dialplan",
2151         save_dialplan_help, NULL, &cli_save_dialplan_deprecated };
2152
2153 /*!
2154  * Standard module functions ...
2155  */
2156 static int unload_module(void)
2157 {
2158         if (static_config && !write_protect_config)
2159                 ast_cli_unregister(&cli_dialplan_save);
2160         ast_cli_unregister_multiple(cli_pbx_config, sizeof(cli_pbx_config) / sizeof(struct ast_cli_entry));
2161         ast_context_destroy(NULL, registrar);
2162         return 0;
2163 }
2164
2165 static int pbx_load_config(const char *config_file)
2166 {
2167         struct ast_config *cfg;
2168         char *end;
2169         char *label;
2170         char realvalue[256];
2171         int lastpri = -2;
2172         struct ast_context *con;
2173         struct ast_variable *v;
2174         char *cxt;
2175
2176         cfg = ast_config_load(config_file);
2177         if (!cfg)
2178                 return 0;
2179
2180         /* Use existing config to populate the PBX table */
2181         static_config = ast_true(ast_variable_retrieve(cfg, "general", "static"));
2182         write_protect_config = ast_true(ast_variable_retrieve(cfg, "general", "writeprotect"));
2183         autofallthrough_config = ast_true(ast_variable_retrieve(cfg, "general", "autofallthrough"));
2184         clearglobalvars_config = ast_true(ast_variable_retrieve(cfg, "general", "clearglobalvars"));
2185         ast_set2_flag(&ast_options, ast_true(ast_variable_retrieve(cfg, "general", "priorityjumping")), AST_OPT_FLAG_PRIORITY_JUMPING);
2186
2187         if ((cxt = ast_variable_retrieve(cfg, "general", "userscontext"))) 
2188                 ast_copy_string(userscontext, cxt, sizeof(userscontext));
2189         else
2190                 ast_copy_string(userscontext, "default", sizeof(userscontext));
2191                                                                     
2192         for (v = ast_variable_browse(cfg, "globals"); v; v = v->next) {
2193                 memset(realvalue, 0, sizeof(realvalue));
2194                 pbx_substitute_variables_helper(NULL, v->value, realvalue, sizeof(realvalue) - 1);
2195                 pbx_builtin_setvar_helper(NULL, v->name, realvalue);
2196         }
2197         for (cxt = NULL; (cxt = ast_category_browse(cfg, cxt)); ) {
2198                 /* All categories but "general" or "globals" are considered contexts */
2199                 if (!strcasecmp(cxt, "general") || !strcasecmp(cxt, "globals"))
2200                         continue;
2201                 con=ast_context_find_or_create(&local_contexts,cxt, registrar);
2202                 if (con == NULL)
2203                         continue;
2204
2205                 for (v = ast_variable_browse(cfg, cxt); v; v = v->next) {
2206                         if (!strcasecmp(v->name, "exten")) {
2207                                 char *tc = ast_strdup(v->value);
2208                                 if (tc) {
2209                                         int ipri = -2;
2210                                         char realext[256]="";
2211                                         char *plus, *firstp, *firstc;
2212                                         char *pri, *appl, *data, *cidmatch;
2213                                         char *stringp = tc;
2214                                         char *ext = strsep(&stringp, ",");
2215                                         if (!ext)
2216                                                 ext="";
2217                                         pbx_substitute_variables_helper(NULL, ext, realext, sizeof(realext) - 1);
2218                                         cidmatch = strchr(realext, '/');
2219                                         if (cidmatch) {
2220                                                 *cidmatch++ = '\0';
2221                                                 ast_shrink_phone_number(cidmatch);
2222                                         }
2223                                         pri = strsep(&stringp, ",");
2224                                         if (!pri)
2225                                                 pri="";
2226                                         label = strchr(pri, '(');
2227                                         if (label) {
2228                                                 *label++ = '\0';
2229                                                 end = strchr(label, ')');
2230                                                 if (end)
2231                                                         *end = '\0';
2232                                                 else
2233                                                         ast_log(LOG_WARNING, "Label missing trailing ')' at line %d\n", v->lineno);
2234                                         }
2235                                         plus = strchr(pri, '+');
2236                                         if (plus)
2237                                                 *plus++ = '\0';
2238                                         if (!strcmp(pri,"hint"))
2239                                                 ipri=PRIORITY_HINT;
2240                                         else if (!strcmp(pri, "next") || !strcmp(pri, "n")) {
2241                                                 if (lastpri > -2)
2242                                                         ipri = lastpri + 1;
2243                                                 else
2244                                                         ast_log(LOG_WARNING, "Can't use 'next' priority on the first entry!\n");
2245                                         } else if (!strcmp(pri, "same") || !strcmp(pri, "s")) {
2246                                                 if (lastpri > -2)
2247                                                         ipri = lastpri;
2248                                                 else
2249                                                         ast_log(LOG_WARNING, "Can't use 'same' priority on the first entry!\n");
2250                                         } else if (sscanf(pri, "%d", &ipri) != 1 &&
2251                                             (ipri = ast_findlabel_extension2(NULL, con, realext, pri, cidmatch)) < 1) {
2252                                                 ast_log(LOG_WARNING, "Invalid priority/label '%s' at line %d\n", pri, v->lineno);
2253                                                 ipri = 0;
2254                                         }
2255                                         appl = S_OR(stringp, "");
2256                                         /* Find the first occurrence of either '(' or ',' */
2257                                         firstc = strchr(appl, ',');
2258                                         firstp = strchr(appl, '(');
2259                                         if (firstc && (!firstp || firstc < firstp)) {
2260                                                 /* comma found, no parenthesis */
2261                                                 /* or both found, but comma found first */
2262                                                 appl = strsep(&stringp, ",");
2263                                                 data = stringp;
2264                                         } else if (!firstc && !firstp) {
2265                                                 /* Neither found */
2266                                                 data = "";
2267                                         } else {
2268                                                 /* Final remaining case is parenthesis found first */
2269                                                 appl = strsep(&stringp, "(");
2270                                                 data = stringp;
2271                                                 end = strrchr(data, ')');
2272                                                 if ((end = strrchr(data, ')'))) {
2273                                                         *end = '\0';
2274                                                 } else {
2275                                                         ast_log(LOG_WARNING, "No closing parenthesis found? '%s(%s'\n", appl, data);
2276                                                 }
2277                                                 ast_process_quotes_and_slashes(data, ',', '|');
2278                                         }
2279
2280                                         if (!data)
2281                                                 data="";
2282                                         appl = ast_skip_blanks(appl);
2283                                         if (ipri) {
2284                                                 if (plus)
2285                                                         ipri += atoi(plus);
2286                                                 lastpri = ipri;
2287                                                 if (!ast_opt_dont_warn && !strcmp(realext, "_."))
2288                                                         ast_log(LOG_WARNING, "The use of '_.' for an extension is strongly discouraged and can have unexpected behavior.  Please use '_X.' instead at line %d\n", v->lineno);
2289                                                 if (ast_add_extension2(con, 0, realext, ipri, label, cidmatch, appl, strdup(data), ast_free, registrar)) {
2290                                                         ast_log(LOG_WARNING, "Unable to register extension at line %d\n", v->lineno);
2291                                                 }
2292                                         }
2293                                         free(tc);
2294                                 }
2295                         } else if (!strcasecmp(v->name, "include")) {
2296                                 memset(realvalue, 0, sizeof(realvalue));
2297                                 pbx_substitute_variables_helper(NULL, v->value, realvalue, sizeof(realvalue) - 1);
2298                                 if (ast_context_add_include2(con, realvalue, registrar))
2299                                         ast_log(LOG_WARNING, "Unable to include context '%s' in context '%s'\n", v->value, cxt);
2300                         } else if (!strcasecmp(v->name, "ignorepat")) {
2301                                 memset(realvalue, 0, sizeof(realvalue));
2302                                 pbx_substitute_variables_helper(NULL, v->value, realvalue, sizeof(realvalue) - 1);
2303                                 if (ast_context_add_ignorepat2(con, realvalue, registrar))
2304                                         ast_log(LOG_WARNING, "Unable to include ignorepat '%s' in context '%s'\n", v->value, cxt);
2305                         } else if (!strcasecmp(v->name, "switch") || !strcasecmp(v->name, "lswitch") || !strcasecmp(v->name, "eswitch")) {
2306                                 char *stringp= realvalue;
2307                                 char *appl, *data;
2308
2309                                 memset(realvalue, 0, sizeof(realvalue));
2310                                 if (!strcasecmp(v->name, "switch"))
2311                                         pbx_substitute_variables_helper(NULL, v->value, realvalue, sizeof(realvalue) - 1);
2312                                 else
2313                                         ast_copy_string(realvalue, v->value, sizeof(realvalue));
2314                                 appl = strsep(&stringp, "/");
2315                                 data = strsep(&stringp, ""); /* XXX what for ? */
2316                                 if (!data)
2317                                         data = "";
2318                                 if (ast_context_add_switch2(con, appl, data, !strcasecmp(v->name, "eswitch"), registrar))
2319                                         ast_log(LOG_WARNING, "Unable to include switch '%s' in context '%s'\n", v->value, cxt);
2320                         }
2321                 }
2322         }
2323         ast_config_destroy(cfg);
2324         return 1;
2325 }
2326
2327 static void append_interface(char *iface, int maxlen, char *add)
2328 {
2329         int len = strlen(iface);
2330         if (strlen(add) + len < maxlen - 2) {
2331                 if (strlen(iface)) {
2332                         iface[len] = '&';
2333                         strcpy(iface + len + 1, add);
2334                 } else
2335                         strcpy(iface, add);
2336         }
2337 }
2338
2339 static void pbx_load_users(void)
2340 {
2341         struct ast_config *cfg;
2342         char *cat, *chan;
2343         char *zapchan;
2344         char *hasexten;
2345         char tmp[256];
2346         char iface[256];
2347         char zapcopy[256];
2348         char *c;
2349         int len;
2350         int hasvoicemail;
2351         int start, finish, x;
2352         struct ast_context *con;
2353         
2354         cfg = ast_config_load("users.conf");
2355         if (!cfg)
2356                 return;
2357         con = ast_context_find_or_create(&local_contexts, userscontext, registrar);
2358         if (!con)
2359                 return;
2360
2361         for (cat = ast_category_browse(cfg, NULL); cat ; cat = ast_category_browse(cfg, cat)) {
2362                 if (!strcasecmp(cat, "general"))
2363                         continue;
2364                 iface[0] = '\0';
2365                 len = sizeof(iface);
2366                 if (ast_true(ast_config_option(cfg, cat, "hassip"))) {
2367                         snprintf(tmp, sizeof(tmp), "SIP/%s", cat);
2368                         append_interface(iface, sizeof(iface), tmp);
2369                 }
2370                 if (ast_true(ast_config_option(cfg, cat, "hasiax"))) {
2371                         snprintf(tmp, sizeof(tmp), "IAX/%s", cat);
2372                         append_interface(iface, sizeof(iface), tmp);
2373                 }
2374                 hasexten = ast_config_option(cfg, cat, "hasexten");
2375                 if (hasexten && !ast_true(hasexten))
2376                         continue;
2377                 hasvoicemail = ast_true(ast_config_option(cfg, cat, "hasvoicemail"));
2378                 zapchan = ast_variable_retrieve(cfg, cat, "zapchan");
2379                 if (!zapchan)
2380                         zapchan = ast_variable_retrieve(cfg, "general", "zapchan");
2381                 if (!ast_strlen_zero(zapchan)) {
2382                         ast_copy_string(zapcopy, zapchan, sizeof(zapcopy));
2383                         c = zapcopy;
2384                         chan = strsep(&c, ",");
2385                         while (chan) {
2386                                 if (sscanf(chan, "%d-%d", &start, &finish) == 2) {
2387                                         /* Range */
2388                                 } else if (sscanf(chan, "%d", &start)) {
2389                                         /* Just one */
2390                                         finish = start;
2391                                 } else {
2392                                         start = 0; finish = 0;
2393                                 }
2394                                 if (finish < start) {
2395                                         x = finish;
2396                                         finish = start;
2397                                         start = x;
2398                                 }
2399                                 for (x = start; x <= finish; x++) {
2400                                         snprintf(tmp, sizeof(tmp), "Zap/%d", x);
2401                                         append_interface(iface, sizeof(iface), tmp);
2402                                 }
2403                                 chan = strsep(&c, ",");
2404                         }
2405                 }
2406                 if (!ast_strlen_zero(iface)) {
2407                         /* Add hint */
2408                         ast_add_extension2(con, 0, cat, -1, NULL, NULL, iface, strdup(""), ast_free, registrar);
2409                         /* If voicemail, use "stdexten" else use plain old dial */
2410                         if (hasvoicemail) {
2411                                 snprintf(tmp, sizeof(tmp), "stdexten|%s|${HINT}", cat);
2412                                 ast_add_extension2(con, 0, cat, 1, NULL, NULL, "Macro", strdup(tmp), ast_free, registrar);
2413                         } else {
2414                                 ast_add_extension2(con, 0, cat, 1, NULL, NULL, "Dial", strdup("${HINT}"), ast_free, registrar);
2415                         }
2416                 }
2417         }
2418         ast_config_destroy(cfg);
2419 }
2420
2421 static int pbx_load_module(void)
2422 {
2423         struct ast_context *con;
2424
2425         if(!pbx_load_config(config))
2426                 return AST_MODULE_LOAD_DECLINE;
2427         
2428         pbx_load_users();
2429
2430         ast_merge_contexts_and_delete(&local_contexts, registrar);
2431
2432         for (con = NULL; (con = ast_walk_contexts(con));)
2433                 ast_context_verify_includes(con);
2434
2435         pbx_set_autofallthrough(autofallthrough_config);
2436
2437         return 0;
2438 }
2439
2440 static int load_module(void)
2441 {
2442         if (pbx_load_module())
2443                 return AST_MODULE_LOAD_DECLINE;
2444  
2445         if (static_config && !write_protect_config)
2446                 ast_cli_register(&cli_dialplan_save);
2447         ast_cli_register_multiple(cli_pbx_config, sizeof(cli_pbx_config) / sizeof(struct ast_cli_entry));
2448
2449         return 0;
2450 }
2451
2452 static int reload(void)
2453 {
2454         if (clearglobalvars_config)
2455                 pbx_builtin_clear_globals();
2456         pbx_load_module();
2457         return 0;
2458 }
2459
2460 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Text Extension Configuration",
2461                 .load = load_module,
2462                 .unload = unload_module,
2463                 .reload = reload,
2464                );